commit 8781bbf0f5493b03c95e2c9d40068eadcf508b48 Author: root Date: Thu Jul 17 18:12:56 2025 +0800 初次提交 diff --git a/@types/schema/package/base/panels.json b/@types/schema/package/base/panels.json new file mode 100755 index 0000000..9e268fb --- /dev/null +++ b/@types/schema/package/base/panels.json @@ -0,0 +1,81 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "description": "面板数据 / Panel data", + "additionalProperties": false, + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "type": "object", + "description": "面板名 / Panel name", + "properties": { + "title": { + "type": "string", + "default": "Default Panel", + "description": "面板标题,支持 i18n:key / Panel title, support for i18n:key (required)" + }, + "main": { + "type": "string", + "default": "dist/panels/default/index.js", + "description": "入口函数 / Entry function (required)" + }, + "icon": { + "type": "string", + "description": "面板图标存放相对目录 / Relative directory for panel icon storage" + }, + "type": { + "type": "string", + "enum": ["dockable", "simple"], + "default": "dockable", + "description": "面板类型(dockable | simple) / Panel type (dockable | simple)" + }, + "flags": { + "type": "object", + "properties": { + "resizable": { + "type": "boolean", + "default": true, + "description": "是否可以改变大小,默认 true / Whether the size can be changed, default true" + }, + "save": { + "type": "boolean", + "default": true, + "description": "是否需要保存,默认 false / Whether to save, default false" + }, + "alwaysOnTop": { + "type": "boolean", + "default": true, + "description": "是否保持顶层显示,默认 false / Whether to keep the top level display, default false" + } + } + }, + "size": { + "type": "object", + "description": "面板大小信息 / Panel size information", + "properties": { + "min-width": { + "type": "number", + "default": 200, + "description": "面板最小宽度 / Minimum panel width" + }, + "min-height": { + "type": "number", + "default": 200, + "description": "面板最小高度 / Minimum panel height" + }, + "width": { + "type": "number", + "default": 400, + "description": " 面板默认宽度 / Panel Default Width" + }, + "height": { + "type": "number", + "default": 600, + "description": "面板默认高度 / Panel Default Height" + } + } + } + }, + "required": ["title", "main"] + } + } +} diff --git a/@types/schema/package/contributions/index.json b/@types/schema/package/contributions/index.json new file mode 100755 index 0000000..7a2b7da --- /dev/null +++ b/@types/schema/package/contributions/index.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "description": "其他扩展插件的扩展配置 / Extended configuration for other extension plugins", + "properties": { + "menu": { + "type": "array", + "description": "菜单配置 / Menu configuration", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "菜单路径 / Menu path" + }, + "label": { + "type": "string", + "description": "菜单标签 / Menu label" + }, + "message": { + "type": "string", + "description": "菜单消息 / Menu message" + } + } + } + }, + "messages": { + "type": "object", + "description": "消息配置 / Message configuration", + "additionalProperties": { + "type": "object", + "properties": { + "methods": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "panels": { + "$ref": "../base/panels.json" + } + }, + "required": [] +} diff --git a/@types/schema/package/index.json b/@types/schema/package/index.json new file mode 100755 index 0000000..0452158 --- /dev/null +++ b/@types/schema/package/index.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "description": "插件定义文件 / Extension definition file", + "properties": { + "author": { + "type": "string", + "description": "作者 / Author", + "default": "Cocos Creator Developer" + }, + "contributions": { + "$ref": "./contributions/index.json" + }, + "dependencies": { + "type": "object", + "description": "发布时所需的依赖库 / Dependencies required for publishing" + }, + "description": { + "type": "string", + "description": "简要介绍扩展关键特性、用途,支持 i18n / Brief introduction of the key features and uses of the extension, supporting i18n" + }, + "devDependencies": { + "type": "object", + "description": "开发时所需的依赖库 / Dependencies required for development" + }, + "editor": { + "type": "string", + "description": "支持的 Cocos Creator 编辑器版本,支持 semver 格式 / Supported Cocos Creator editor version, supporting semver format" + }, + "main": { + "type": "string", + "description": "入口函数 / Entry function", + "default": "./dist/index.js" + }, + "name": { + "type": "string", + "description": "不能以 _ 或 . 开头、不能含有大写字母,也不能含有 URL 的非法字符例如 .、' 和 ,。 / Cannot start with _ or., cannot contain uppercase letters, and cannot contain URL illegal characters such as.,'and,", + "default": "Custom Extension" + }, + "package_version": { + "type": "number", + "description": "扩展系统预留版本号 / Extension system reserved version number", + "default": 2 + }, + "panels": { + "$ref": "./base/panels.json" + }, + "scripts": { + "type": "object", + "description": "NPM 脚本 / NPM scripts" + }, + "version": { + "type": "string", + "description": "版本号字符串 / Version number string", + "default": "1.0.0" + } + }, + "required": [ + "author", + "name", + "package_version", + "version" + ] +} \ No newline at end of file diff --git a/FEATURE_GUIDE_CN.md b/FEATURE_GUIDE_CN.md new file mode 100644 index 0000000..50b72cb --- /dev/null +++ b/FEATURE_GUIDE_CN.md @@ -0,0 +1,1426 @@ +# Cocos Creator MCP 服务器功能指导文档 + +## 概述 + +Cocos Creator MCP 服务器是一个全面的 Model Context Protocol (MCP) 服务器插件,专为 Cocos Creator 3.8+ 设计,通过标准化协议使 AI 助手能够与 Cocos Creator 编辑器进行交互。 + +本文档详细介绍了所有可用的 MCP 工具及其使用方法。 + +## 工具分类 + +MCP 服务器提供了 **80 个工具**,按功能分为 9 个主要类别: + +1. [场景操作工具 (Scene Tools)](#1-场景操作工具-scene-tools) +2. [节点操作工具 (Node Tools)](#2-节点操作工具-node-tools) +3. [组件管理工具 (Component Tools)](#3-组件管理工具-component-tools) +4. [预制体操作工具 (Prefab Tools)](#4-预制体操作工具-prefab-tools) +5. [项目控制工具 (Project Tools)](#5-项目控制工具-project-tools) +6. [调试工具 (Debug Tools)](#6-调试工具-debug-tools) +7. [偏好设置工具 (Preferences Tools)](#7-偏好设置工具-preferences-tools) +8. [服务器工具 (Server Tools)](#8-服务器工具-server-tools) +9. [广播工具 (Broadcast Tools)](#9-广播工具-broadcast-tools) + +--- + +## 1. 场景操作工具 (Scene Tools) + +### 1.1 scene_get_current_scene +获取当前场景信息 + +**参数**: 无 + +**返回**: 当前场景的名称、UUID、类型、激活状态和节点数量 + +**示例**: +```json +{ + "tool": "scene_get_current_scene", + "arguments": {} +} +``` + +### 1.2 scene_get_scene_list +获取项目中所有场景列表 + +**参数**: 无 + +**返回**: 项目中所有场景的列表,包括名称、路径和UUID + +**示例**: +```json +{ + "tool": "scene_get_scene_list", + "arguments": {} +} +``` + +### 1.3 scene_open_scene +通过路径打开场景 + +**参数**: +- `scenePath` (string, 必需): 场景文件路径 + +**示例**: +```json +{ + "tool": "scene_open_scene", + "arguments": { + "scenePath": "db://assets/scenes/GameScene.scene" + } +} +``` + +### 1.4 scene_save_scene +保存当前场景 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "scene_save_scene", + "arguments": {} +} +``` + +### 1.5 scene_create_scene +创建新场景资源 + +**参数**: +- `sceneName` (string, 必需): 新场景的名称 +- `savePath` (string, 必需): 保存场景的路径 + +**示例**: +```json +{ + "tool": "scene_create_scene", + "arguments": { + "sceneName": "NewLevel", + "savePath": "db://assets/scenes/NewLevel.scene" + } +} +``` + +### 1.6 scene_save_scene_as +将场景另存为新文件 + +**参数**: +- `path` (string, 必需): 保存场景的路径 + +**示例**: +```json +{ + "tool": "scene_save_scene_as", + "arguments": { + "path": "db://assets/scenes/GameScene_Copy.scene" + } +} +``` + +### 1.7 scene_close_scene +关闭当前场景 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "scene_close_scene", + "arguments": {} +} +``` + +### 1.8 scene_get_scene_hierarchy +获取当前场景的完整层级结构 + +**参数**: +- `includeComponents` (boolean, 可选): 是否包含组件信息,默认为 false + +**示例**: +```json +{ + "tool": "scene_get_scene_hierarchy", + "arguments": { + "includeComponents": true + } +} +``` + +--- + +## 2. 节点操作工具 (Node Tools) + +### 2.1 node_create_node +在场景中创建新节点 + +**参数**: +- `name` (string, 必需): 节点名称 +- `parentUuid` (string, 可选): 父节点UUID,如果不提供则在当前编辑器选择位置创建 +- `nodeType` (string, 可选): 节点类型,可选值:`Node`、`2DNode`、`3DNode`,默认为 `Node` +- `siblingIndex` (number, 可选): 同级索引,-1 表示添加到末尾,默认为 -1 + +**示例**: +```json +{ + "tool": "node_create_node", + "arguments": { + "name": "PlayerNode", + "nodeType": "2DNode", + "parentUuid": "parent-uuid-here" + } +} +``` + +### 2.2 node_get_node_info +通过UUID获取节点信息 + +**参数**: +- `uuid` (string, 必需): 节点UUID + +**示例**: +```json +{ + "tool": "node_get_node_info", + "arguments": { + "uuid": "node-uuid-here" + } +} +``` + +### 2.3 node_find_nodes +按名称模式查找节点 + +**参数**: +- `pattern` (string, 必需): 搜索的名称模式 +- `exactMatch` (boolean, 可选): 是否精确匹配,默认为 false + +**示例**: +```json +{ + "tool": "node_find_nodes", + "arguments": { + "pattern": "Enemy", + "exactMatch": false + } +} +``` + +### 2.4 node_find_node_by_name +通过精确名称查找第一个节点 + +**参数**: +- `name` (string, 必需): 要查找的节点名称 + +**示例**: +```json +{ + "tool": "node_find_node_by_name", + "arguments": { + "name": "Player" + } +} +``` + +### 2.5 node_get_all_nodes +获取场景中所有节点及其UUID + +**参数**: 无 + +**示例**: +```json +{ + "tool": "node_get_all_nodes", + "arguments": {} +} +``` + +### 2.6 node_set_node_property +设置节点属性值 + +**参数**: +- `uuid` (string, 必需): 节点UUID +- `property` (string, 必需): 属性名称(如 position、rotation、scale、active) +- `value` (any, 必需): 属性值 + +**示例**: +```json +{ + "tool": "node_set_node_property", + "arguments": { + "uuid": "node-uuid-here", + "property": "position", + "value": {"x": 100, "y": 200, "z": 0} + } +} +``` + +### 2.7 node_delete_node +从场景中删除节点 + +**参数**: +- `uuid` (string, 必需): 要删除的节点UUID + +**示例**: +```json +{ + "tool": "node_delete_node", + "arguments": { + "uuid": "node-uuid-here" + } +} +``` + +### 2.8 node_move_node +将节点移动到新的父节点 + +**参数**: +- `nodeUuid` (string, 必需): 要移动的节点UUID +- `newParentUuid` (string, 必需): 新父节点UUID +- `siblingIndex` (number, 可选): 新父节点中的同级索引,默认为 -1 + +**示例**: +```json +{ + "tool": "node_move_node", + "arguments": { + "nodeUuid": "node-uuid-here", + "newParentUuid": "parent-uuid-here", + "siblingIndex": 0 + } +} +``` + +### 2.9 node_duplicate_node +复制节点 + +**参数**: +- `uuid` (string, 必需): 要复制的节点UUID +- `includeChildren` (boolean, 可选): 是否包含子节点,默认为 true + +**示例**: +```json +{ + "tool": "node_duplicate_node", + "arguments": { + "uuid": "node-uuid-here", + "includeChildren": true + } +} +``` + +--- + +## 3. 组件管理工具 (Component Tools) + +### 3.1 component_add_component +向指定节点添加组件 + +**参数**: +- `nodeUuid` (string, 必需): 目标节点UUID +- `componentType` (string, 必需): 组件类型(如 cc.Sprite、cc.Label、cc.Button) + +**示例**: +```json +{ + "tool": "component_add_component", + "arguments": { + "nodeUuid": "node-uuid-here", + "componentType": "cc.Sprite" + } +} +``` + +### 3.2 component_remove_component +从节点移除组件 + +**参数**: +- `nodeUuid` (string, 必需): 节点UUID +- `componentType` (string, 必需): 要移除的组件类型 + +**示例**: +```json +{ + "tool": "component_remove_component", + "arguments": { + "nodeUuid": "node-uuid-here", + "componentType": "cc.Sprite" + } +} +``` + +### 3.3 component_get_components +获取节点的所有组件 + +**参数**: +- `nodeUuid` (string, 必需): 节点UUID + +**示例**: +```json +{ + "tool": "component_get_components", + "arguments": { + "nodeUuid": "node-uuid-here" + } +} +``` + +### 3.4 component_get_component_info +获取特定组件信息 + +**参数**: +- `nodeUuid` (string, 必需): 节点UUID +- `componentType` (string, 必需): 要获取信息的组件类型 + +**示例**: +```json +{ + "tool": "component_get_component_info", + "arguments": { + "nodeUuid": "node-uuid-here", + "componentType": "cc.Sprite" + } +} +``` + +### 3.5 component_set_component_property +设置组件属性值 + +**参数**: +- `nodeUuid` (string, 必需): 节点UUID +- `componentType` (string, 必需): 组件类型 +- `property` (string, 必需): 属性名称 +- `value` (any, 必需): 属性值 + +**示例**: +```json +{ + "tool": "component_set_component_property", + "arguments": { + "nodeUuid": "node-uuid-here", + "componentType": "cc.Sprite", + "property": "spriteFrame", + "value": "sprite-frame-uuid" + } +} +``` + +### 3.6 component_attach_script +向节点附加脚本组件 + +**参数**: +- `nodeUuid` (string, 必需): 节点UUID +- `scriptPath` (string, 必需): 脚本资源路径 + +**示例**: +```json +{ + "tool": "component_attach_script", + "arguments": { + "nodeUuid": "node-uuid-here", + "scriptPath": "db://assets/scripts/PlayerController.ts" + } +} +``` + +### 3.7 component_get_available_components +获取可用组件类型列表 + +**参数**: +- `category` (string, 可选): 组件类别过滤器,可选值:`all`、`renderer`、`ui`、`physics`、`animation`、`audio`,默认为 `all` + +**示例**: +```json +{ + "tool": "component_get_available_components", + "arguments": { + "category": "ui" + } +} +``` + +--- + +## 4. 预制体操作工具 (Prefab Tools) + +### 4.1 prefab_get_prefab_list +获取项目中所有预制体 + +**参数**: +- `folder` (string, 可选): 搜索文件夹路径,默认为 `db://assets` + +**示例**: +```json +{ + "tool": "prefab_get_prefab_list", + "arguments": { + "folder": "db://assets/prefabs" + } +} +``` + +### 4.2 prefab_load_prefab +通过路径加载预制体 + +**参数**: +- `prefabPath` (string, 必需): 预制体资源路径 + +**示例**: +```json +{ + "tool": "prefab_load_prefab", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab" + } +} +``` + +### 4.3 prefab_instantiate_prefab +在场景中实例化预制体 + +**参数**: +- `prefabPath` (string, 必需): 预制体资源路径 +- `parentUuid` (string, 可选): 父节点UUID +- `position` (object, 可选): 初始位置,包含 x、y、z 属性 + +**示例**: +```json +{ + "tool": "prefab_instantiate_prefab", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab", + "parentUuid": "parent-uuid-here", + "position": {"x": 100, "y": 200, "z": 0} + } +} +``` + +### 4.4 prefab_create_prefab +从节点创建预制体 + +**参数**: +- `nodeUuid` (string, 必需): 源节点UUID +- `savePath` (string, 必需): 保存预制体的路径 +- `prefabName` (string, 必需): 预制体名称 + +**示例**: +```json +{ + "tool": "prefab_create_prefab", + "arguments": { + "nodeUuid": "node-uuid-here", + "savePath": "db://assets/prefabs/", + "prefabName": "MyPrefab" + } +} +``` + +### 4.5 prefab_create_prefab_from_node +从节点创建预制体(create_prefab 的别名) + +**参数**: +- `nodeUuid` (string, 必需): 源节点UUID +- `prefabPath` (string, 必需): 保存预制体的路径 + +**示例**: +```json +{ + "tool": "prefab_create_prefab_from_node", + "arguments": { + "nodeUuid": "node-uuid-here", + "prefabPath": "db://assets/prefabs/MyPrefab.prefab" + } +} +``` + +### 4.6 prefab_update_prefab +更新现有预制体 + +**参数**: +- `prefabPath` (string, 必需): 预制体资源路径 +- `nodeUuid` (string, 必需): 包含更改的节点UUID + +**示例**: +```json +{ + "tool": "prefab_update_prefab", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab", + "nodeUuid": "node-uuid-here" + } +} +``` + +### 4.7 prefab_revert_prefab +将预制体实例恢复为原始状态 + +**参数**: +- `nodeUuid` (string, 必需): 预制体实例节点UUID + +**示例**: +```json +{ + "tool": "prefab_revert_prefab", + "arguments": { + "nodeUuid": "prefab-instance-uuid-here" + } +} +``` + +### 4.8 prefab_get_prefab_info +获取详细的预制体信息 + +**参数**: +- `prefabPath` (string, 必需): 预制体资源路径 + +**示例**: +```json +{ + "tool": "prefab_get_prefab_info", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab" + } +} +``` + +--- + +## 5. 项目控制工具 (Project Tools) + +### 5.1 project_run_project +在预览模式下运行项目 + +**参数**: +- `platform` (string, 可选): 目标平台,可选值:`browser`、`simulator`、`preview`,默认为 `browser` + +**示例**: +```json +{ + "tool": "project_run_project", + "arguments": { + "platform": "browser" + } +} +``` + +### 5.2 project_build_project +构建项目 + +**参数**: +- `platform` (string, 必需): 构建平台,可选值:`web-mobile`、`web-desktop`、`ios`、`android`、`windows`、`mac` +- `debug` (boolean, 可选): 是否调试构建,默认为 true + +**示例**: +```json +{ + "tool": "project_build_project", + "arguments": { + "platform": "web-mobile", + "debug": false + } +} +``` + +### 5.3 project_get_project_info +获取项目信息 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "project_get_project_info", + "arguments": {} +} +``` + +### 5.4 project_get_project_settings +获取项目设置 + +**参数**: +- `category` (string, 可选): 设置类别,可选值:`general`、`physics`、`render`、`assets`,默认为 `general` + +**示例**: +```json +{ + "tool": "project_get_project_settings", + "arguments": { + "category": "physics" + } +} +``` + +### 5.5 project_refresh_assets +刷新资源数据库 + +**参数**: +- `folder` (string, 可选): 要刷新的特定文件夹 + +**示例**: +```json +{ + "tool": "project_refresh_assets", + "arguments": { + "folder": "db://assets/textures" + } +} +``` + +### 5.6 project_import_asset +导入资源文件 + +**参数**: +- `sourcePath` (string, 必需): 源文件路径 +- `targetFolder` (string, 必需): 资源中的目标文件夹 + +**示例**: +```json +{ + "tool": "project_import_asset", + "arguments": { + "sourcePath": "/path/to/image.png", + "targetFolder": "db://assets/textures" + } +} +``` + +### 5.7 project_get_asset_info +获取资源信息 + +**参数**: +- `assetPath` (string, 必需): 资源路径 + +**示例**: +```json +{ + "tool": "project_get_asset_info", + "arguments": { + "assetPath": "db://assets/textures/player.png" + } +} +``` + +### 5.8 project_get_assets +按类型获取资源 + +**参数**: +- `type` (string, 可选): 资源类型过滤器,可选值:`all`、`scene`、`prefab`、`script`、`texture`、`material`、`mesh`、`audio`、`animation`,默认为 `all` +- `folder` (string, 可选): 搜索文件夹,默认为 `db://assets` + +**示例**: +```json +{ + "tool": "project_get_assets", + "arguments": { + "type": "texture", + "folder": "db://assets/textures" + } +} +``` + +### 5.9 project_get_build_settings +获取构建设置 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "project_get_build_settings", + "arguments": {} +} +``` + +### 5.10 project_open_build_panel +在编辑器中打开构建面板 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "project_open_build_panel", + "arguments": {} +} +``` + +### 5.11 project_check_builder_status +检查构建器工作进程是否就绪 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "project_check_builder_status", + "arguments": {} +} +``` + +### 5.12 project_start_preview_server +启动预览服务器 + +**参数**: +- `port` (number, 可选): 预览服务器端口,默认为 7456 + +**示例**: +```json +{ + "tool": "project_start_preview_server", + "arguments": { + "port": 8080 + } +} +``` + +### 5.13 project_stop_preview_server +停止预览服务器 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "project_stop_preview_server", + "arguments": {} +} +``` + +### 5.14 project_create_asset +创建新的资源文件或文件夹 + +**参数**: +- `url` (string, 必需): 资源URL +- `content` (string, 可选): 文件内容,null 表示创建文件夹 +- `overwrite` (boolean, 可选): 是否覆盖现有文件,默认为 false + +**示例**: +```json +{ + "tool": "project_create_asset", + "arguments": { + "url": "db://assets/scripts/NewScript.ts", + "content": "// New TypeScript script\n", + "overwrite": false + } +} +``` + +### 5.15 project_copy_asset +复制资源到另一个位置 + +**参数**: +- `source` (string, 必需): 源资源URL +- `target` (string, 必需): 目标位置URL +- `overwrite` (boolean, 可选): 是否覆盖现有文件,默认为 false + +**示例**: +```json +{ + "tool": "project_copy_asset", + "arguments": { + "source": "db://assets/textures/player.png", + "target": "db://assets/textures/backup/player.png", + "overwrite": false + } +} +``` + +### 5.16 project_move_asset +移动资源到另一个位置 + +**参数**: +- `source` (string, 必需): 源资源URL +- `target` (string, 必需): 目标位置URL +- `overwrite` (boolean, 可选): 是否覆盖现有文件,默认为 false + +**示例**: +```json +{ + "tool": "project_move_asset", + "arguments": { + "source": "db://assets/textures/old_player.png", + "target": "db://assets/textures/player.png", + "overwrite": true + } +} +``` + +### 5.17 project_delete_asset +删除资源 + +**参数**: +- `url` (string, 必需): 要删除的资源URL + +**示例**: +```json +{ + "tool": "project_delete_asset", + "arguments": { + "url": "db://assets/textures/unused.png" + } +} +``` + +### 5.18 project_save_asset +保存资源内容 + +**参数**: +- `url` (string, 必需): 资源URL +- `content` (string, 必需): 资源内容 + +**示例**: +```json +{ + "tool": "project_save_asset", + "arguments": { + "url": "db://assets/scripts/GameManager.ts", + "content": "// Updated script content\n" + } +} +``` + +### 5.19 project_reimport_asset +重新导入资源 + +**参数**: +- `url` (string, 必需): 要重新导入的资源URL + +**示例**: +```json +{ + "tool": "project_reimport_asset", + "arguments": { + "url": "db://assets/textures/player.png" + } +} +``` + +### 5.20 project_query_asset_path +获取资源磁盘路径 + +**参数**: +- `url` (string, 必需): 资源URL + +**示例**: +```json +{ + "tool": "project_query_asset_path", + "arguments": { + "url": "db://assets/textures/player.png" + } +} +``` + +### 5.21 project_query_asset_uuid +从URL获取资源UUID + +**参数**: +- `url` (string, 必需): 资源URL + +**示例**: +```json +{ + "tool": "project_query_asset_uuid", + "arguments": { + "url": "db://assets/textures/player.png" + } +} +``` + +### 5.22 project_query_asset_url +从UUID获取资源URL + +**参数**: +- `uuid` (string, 必需): 资源UUID + +**示例**: +```json +{ + "tool": "project_query_asset_url", + "arguments": { + "uuid": "asset-uuid-here" + } +} +``` + +--- + +## 6. 调试工具 (Debug Tools) + +### 6.1 debug_get_console_logs +获取编辑器控制台日志 + +**参数**: +- `limit` (number, 可选): 要检索的最新日志数量,默认为 100 +- `filter` (string, 可选): 按类型过滤日志,可选值:`all`、`log`、`warn`、`error`、`info`,默认为 `all` + +**示例**: +```json +{ + "tool": "debug_get_console_logs", + "arguments": { + "limit": 50, + "filter": "error" + } +} +``` + +### 6.2 debug_clear_console +清空编辑器控制台 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "debug_clear_console", + "arguments": {} +} +``` + +### 6.3 debug_execute_script +在场景上下文中执行JavaScript代码 + +**参数**: +- `script` (string, 必需): 要执行的JavaScript代码 + +**示例**: +```json +{ + "tool": "debug_execute_script", + "arguments": { + "script": "console.log('Hello from MCP!');" + } +} +``` + +### 6.4 debug_get_node_tree +获取用于调试的详细节点树 + +**参数**: +- `rootUuid` (string, 可选): 根节点UUID,如果不提供则使用场景根节点 +- `maxDepth` (number, 可选): 最大树深度,默认为 10 + +**示例**: +```json +{ + "tool": "debug_get_node_tree", + "arguments": { + "rootUuid": "root-node-uuid", + "maxDepth": 5 + } +} +``` + +### 6.5 debug_get_performance_stats +获取性能统计信息 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "debug_get_performance_stats", + "arguments": {} +} +``` + +### 6.6 debug_validate_scene +验证当前场景是否有问题 + +**参数**: +- `checkMissingAssets` (boolean, 可选): 检查缺失的资源引用,默认为 true +- `checkPerformance` (boolean, 可选): 检查性能问题,默认为 true + +**示例**: +```json +{ + "tool": "debug_validate_scene", + "arguments": { + "checkMissingAssets": true, + "checkPerformance": true + } +} +``` + +### 6.7 debug_get_editor_info +获取编辑器和环境信息 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "debug_get_editor_info", + "arguments": {} +} +``` + +--- + +## 7. 偏好设置工具 (Preferences Tools) + +### 7.1 preferences_get_preferences +获取编辑器偏好设置 + +**参数**: +- `key` (string, 可选): 要获取的特定偏好设置键 + +**示例**: +```json +{ + "tool": "preferences_get_preferences", + "arguments": { + "key": "editor.theme" + } +} +``` + +### 7.2 preferences_set_preferences +设置编辑器偏好设置 + +**参数**: +- `key` (string, 必需): 要设置的偏好设置键 +- `value` (any, 必需): 要设置的偏好设置值 + +**示例**: +```json +{ + "tool": "preferences_set_preferences", + "arguments": { + "key": "editor.theme", + "value": "dark" + } +} +``` + +### 7.3 preferences_get_global_preferences +获取全局编辑器偏好设置 + +**参数**: +- `key` (string, 可选): 要获取的全局偏好设置键 + +**示例**: +```json +{ + "tool": "preferences_get_global_preferences", + "arguments": { + "key": "global.autoSave" + } +} +``` + +### 7.4 preferences_set_global_preferences +设置全局编辑器偏好设置 + +**参数**: +- `key` (string, 必需): 要设置的全局偏好设置键 +- `value` (any, 必需): 要设置的全局偏好设置值 + +**示例**: +```json +{ + "tool": "preferences_set_global_preferences", + "arguments": { + "key": "global.autoSave", + "value": true + } +} +``` + +### 7.5 preferences_get_recent_projects +获取最近打开的项目 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "preferences_get_recent_projects", + "arguments": {} +} +``` + +### 7.6 preferences_clear_recent_projects +清除最近打开的项目列表 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "preferences_clear_recent_projects", + "arguments": {} +} +``` + +--- + +## 8. 服务器工具 (Server Tools) + +### 8.1 server_get_server_info +获取服务器信息 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "server_get_server_info", + "arguments": {} +} +``` + +### 8.2 server_broadcast_custom_message +广播自定义消息 + +**参数**: +- `message` (string, 必需): 消息名称 +- `data` (any, 可选): 消息数据 + +**示例**: +```json +{ + "tool": "server_broadcast_custom_message", + "arguments": { + "message": "custom_event", + "data": {"type": "test", "value": 123} + } +} +``` + +### 8.3 server_get_editor_version +获取编辑器版本信息 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "server_get_editor_version", + "arguments": {} +} +``` + +### 8.4 server_get_project_name +获取当前项目名称 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "server_get_project_name", + "arguments": {} +} +``` + +### 8.5 server_get_project_path +获取当前项目路径 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "server_get_project_path", + "arguments": {} +} +``` + +### 8.6 server_get_project_uuid +获取当前项目UUID + +**参数**: 无 + +**示例**: +```json +{ + "tool": "server_get_project_uuid", + "arguments": {} +} +``` + +### 8.7 server_restart_editor +请求重启编辑器 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "server_restart_editor", + "arguments": {} +} +``` + +### 8.8 server_quit_editor +请求退出编辑器 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "server_quit_editor", + "arguments": {} +} +``` + +--- + +## 9. 广播工具 (Broadcast Tools) + +### 9.1 broadcast_get_broadcast_log +获取最近的广播消息日志 + +**参数**: +- `limit` (number, 可选): 要返回的最新消息数量,默认为 50 +- `messageType` (string, 可选): 按消息类型过滤 + +**示例**: +```json +{ + "tool": "broadcast_get_broadcast_log", + "arguments": { + "limit": 100, + "messageType": "scene_change" + } +} +``` + +### 9.2 broadcast_listen_broadcast +开始监听特定广播消息 + +**参数**: +- `messageType` (string, 必需): 要监听的消息类型 + +**示例**: +```json +{ + "tool": "broadcast_listen_broadcast", + "arguments": { + "messageType": "node_created" + } +} +``` + +### 9.3 broadcast_stop_listening +停止监听特定广播消息 + +**参数**: +- `messageType` (string, 必需): 要停止监听的消息类型 + +**示例**: +```json +{ + "tool": "broadcast_stop_listening", + "arguments": { + "messageType": "node_created" + } +} +``` + +### 9.4 broadcast_clear_broadcast_log +清除广播消息日志 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "broadcast_clear_broadcast_log", + "arguments": {} +} +``` + +### 9.5 broadcast_get_active_listeners +获取活动广播监听器列表 + +**参数**: 无 + +**示例**: +```json +{ + "tool": "broadcast_get_active_listeners", + "arguments": {} +} +``` + +--- + +## 使用须知 + +### 1. 工具调用格式 + +所有工具调用都使用 JSON-RPC 2.0 格式: + +```json +{ + "jsonrpc": "2.0", + "method": "tools/call", + "params": { + "name": "tool_name", + "arguments": { + // 工具参数 + } + }, + "id": 1 +} +``` + +### 2. 常见UUID获取方法 + +- 使用 `node_get_all_nodes` 获取所有节点UUID +- 使用 `node_find_node_by_name` 按名称查找节点UUID +- 使用 `scene_get_current_scene` 获取场景UUID +- 使用 `prefab_get_prefab_list` 获取预制体信息 + +### 3. 资源路径格式 + +Cocos Creator 使用 `db://` 前缀的资源URL格式: +- 场景:`db://assets/scenes/GameScene.scene` +- 预制体:`db://assets/prefabs/Player.prefab` +- 脚本:`db://assets/scripts/GameManager.ts` +- 纹理:`db://assets/textures/player.png` + +### 4. 错误处理 + +如果工具调用失败,会返回错误信息: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": -32000, + "message": "Tool execution failed", + "data": { + "error": "详细错误信息" + } + } +} +``` + +### 5. 最佳实践 + +1. **先查询再操作**:在修改节点或组件之前,先使用查询工具获取当前状态 +2. **使用UUID**:尽量使用UUID而不是名称来引用节点和资源 +3. **错误检查**:始终检查工具调用的返回值,确保操作成功 +4. **资源管理**:在删除或移动资源前,确保没有其他地方引用它们 +5. **性能考虑**:避免在循环中频繁调用工具,考虑批量操作 + +--- + +## 技术支持 + +如果您在使用过程中遇到问题,可以: + +1. 使用 `debug_get_console_logs` 查看详细的错误日志 +2. 使用 `debug_validate_scene` 检查场景是否有问题 +3. 使用 `debug_get_editor_info` 获取环境信息 +4. 检查 MCP 服务器的运行状态和日志 + +--- + +*此文档基于 Cocos Creator MCP 服务器 v1.0.0 编写,如有更新请参考最新版本文档。* \ No newline at end of file diff --git a/FEATURE_GUIDE_EN.md b/FEATURE_GUIDE_EN.md new file mode 100644 index 0000000..695a90e --- /dev/null +++ b/FEATURE_GUIDE_EN.md @@ -0,0 +1,1426 @@ +# Cocos Creator MCP Server Feature Guide + +## Overview + +The Cocos Creator MCP Server is a comprehensive Model Context Protocol (MCP) server plugin designed for Cocos Creator 3.8+ that enables AI assistants to interact with the Cocos Creator editor through a standardized protocol. + +This document provides detailed information about all available MCP tools and their usage. + +## Tool Categories + +The MCP server provides **80 tools** organized into 9 main categories: + +1. [Scene Tools](#1-scene-tools) +2. [Node Tools](#2-node-tools) +3. [Component Tools](#3-component-tools) +4. [Prefab Tools](#4-prefab-tools) +5. [Project Tools](#5-project-tools) +6. [Debug Tools](#6-debug-tools) +7. [Preferences Tools](#7-preferences-tools) +8. [Server Tools](#8-server-tools) +9. [Broadcast Tools](#9-broadcast-tools) + +--- + +## 1. Scene Tools + +### 1.1 scene_get_current_scene +Get current scene information + +**Parameters**: None + +**Returns**: Current scene name, UUID, type, active state, and node count + +**Example**: +```json +{ + "tool": "scene_get_current_scene", + "arguments": {} +} +``` + +### 1.2 scene_get_scene_list +Get all scenes in the project + +**Parameters**: None + +**Returns**: List of all scenes in the project with name, path, and UUID + +**Example**: +```json +{ + "tool": "scene_get_scene_list", + "arguments": {} +} +``` + +### 1.3 scene_open_scene +Open a scene by path + +**Parameters**: +- `scenePath` (string, required): The scene file path + +**Example**: +```json +{ + "tool": "scene_open_scene", + "arguments": { + "scenePath": "db://assets/scenes/GameScene.scene" + } +} +``` + +### 1.4 scene_save_scene +Save current scene + +**Parameters**: None + +**Example**: +```json +{ + "tool": "scene_save_scene", + "arguments": {} +} +``` + +### 1.5 scene_create_scene +Create a new scene asset + +**Parameters**: +- `sceneName` (string, required): Name of the new scene +- `savePath` (string, required): Path to save the scene + +**Example**: +```json +{ + "tool": "scene_create_scene", + "arguments": { + "sceneName": "NewLevel", + "savePath": "db://assets/scenes/NewLevel.scene" + } +} +``` + +### 1.6 scene_save_scene_as +Save scene as new file + +**Parameters**: +- `path` (string, required): Path to save the scene + +**Example**: +```json +{ + "tool": "scene_save_scene_as", + "arguments": { + "path": "db://assets/scenes/GameScene_Copy.scene" + } +} +``` + +### 1.7 scene_close_scene +Close current scene + +**Parameters**: None + +**Example**: +```json +{ + "tool": "scene_close_scene", + "arguments": {} +} +``` + +### 1.8 scene_get_scene_hierarchy +Get the complete hierarchy of current scene + +**Parameters**: +- `includeComponents` (boolean, optional): Include component information, default is false + +**Example**: +```json +{ + "tool": "scene_get_scene_hierarchy", + "arguments": { + "includeComponents": true + } +} +``` + +--- + +## 2. Node Tools + +### 2.1 node_create_node +Create a new node in the scene + +**Parameters**: +- `name` (string, required): Node name +- `parentUuid` (string, optional): Parent node UUID, if not provided, node will be created at current editor selection +- `nodeType` (string, optional): Node type, options: `Node`, `2DNode`, `3DNode`, default is `Node` +- `siblingIndex` (number, optional): Sibling index for ordering, -1 means append at end, default is -1 + +**Example**: +```json +{ + "tool": "node_create_node", + "arguments": { + "name": "PlayerNode", + "nodeType": "2DNode", + "parentUuid": "parent-uuid-here" + } +} +``` + +### 2.2 node_get_node_info +Get node information by UUID + +**Parameters**: +- `uuid` (string, required): Node UUID + +**Example**: +```json +{ + "tool": "node_get_node_info", + "arguments": { + "uuid": "node-uuid-here" + } +} +``` + +### 2.3 node_find_nodes +Find nodes by name pattern + +**Parameters**: +- `pattern` (string, required): Name pattern to search +- `exactMatch` (boolean, optional): Exact match or partial match, default is false + +**Example**: +```json +{ + "tool": "node_find_nodes", + "arguments": { + "pattern": "Enemy", + "exactMatch": false + } +} +``` + +### 2.4 node_find_node_by_name +Find first node by exact name + +**Parameters**: +- `name` (string, required): Node name to find + +**Example**: +```json +{ + "tool": "node_find_node_by_name", + "arguments": { + "name": "Player" + } +} +``` + +### 2.5 node_get_all_nodes +Get all nodes in the scene with their UUIDs + +**Parameters**: None + +**Example**: +```json +{ + "tool": "node_get_all_nodes", + "arguments": {} +} +``` + +### 2.6 node_set_node_property +Set node property value + +**Parameters**: +- `uuid` (string, required): Node UUID +- `property` (string, required): Property name (e.g., position, rotation, scale, active) +- `value` (any, required): Property value + +**Example**: +```json +{ + "tool": "node_set_node_property", + "arguments": { + "uuid": "node-uuid-here", + "property": "position", + "value": {"x": 100, "y": 200, "z": 0} + } +} +``` + +### 2.7 node_delete_node +Delete a node from scene + +**Parameters**: +- `uuid` (string, required): Node UUID to delete + +**Example**: +```json +{ + "tool": "node_delete_node", + "arguments": { + "uuid": "node-uuid-here" + } +} +``` + +### 2.8 node_move_node +Move node to new parent + +**Parameters**: +- `nodeUuid` (string, required): Node UUID to move +- `newParentUuid` (string, required): New parent node UUID +- `siblingIndex` (number, optional): Sibling index in new parent, default is -1 + +**Example**: +```json +{ + "tool": "node_move_node", + "arguments": { + "nodeUuid": "node-uuid-here", + "newParentUuid": "parent-uuid-here", + "siblingIndex": 0 + } +} +``` + +### 2.9 node_duplicate_node +Duplicate a node + +**Parameters**: +- `uuid` (string, required): Node UUID to duplicate +- `includeChildren` (boolean, optional): Include children nodes, default is true + +**Example**: +```json +{ + "tool": "node_duplicate_node", + "arguments": { + "uuid": "node-uuid-here", + "includeChildren": true + } +} +``` + +--- + +## 3. Component Tools + +### 3.1 component_add_component +Add a component to a specific node + +**Parameters**: +- `nodeUuid` (string, required): Target node UUID +- `componentType` (string, required): Component type (e.g., cc.Sprite, cc.Label, cc.Button) + +**Example**: +```json +{ + "tool": "component_add_component", + "arguments": { + "nodeUuid": "node-uuid-here", + "componentType": "cc.Sprite" + } +} +``` + +### 3.2 component_remove_component +Remove a component from a node + +**Parameters**: +- `nodeUuid` (string, required): Node UUID +- `componentType` (string, required): Component type to remove + +**Example**: +```json +{ + "tool": "component_remove_component", + "arguments": { + "nodeUuid": "node-uuid-here", + "componentType": "cc.Sprite" + } +} +``` + +### 3.3 component_get_components +Get all components of a node + +**Parameters**: +- `nodeUuid` (string, required): Node UUID + +**Example**: +```json +{ + "tool": "component_get_components", + "arguments": { + "nodeUuid": "node-uuid-here" + } +} +``` + +### 3.4 component_get_component_info +Get specific component information + +**Parameters**: +- `nodeUuid` (string, required): Node UUID +- `componentType` (string, required): Component type to get info for + +**Example**: +```json +{ + "tool": "component_get_component_info", + "arguments": { + "nodeUuid": "node-uuid-here", + "componentType": "cc.Sprite" + } +} +``` + +### 3.5 component_set_component_property +Set component property value + +**Parameters**: +- `nodeUuid` (string, required): Node UUID +- `componentType` (string, required): Component type +- `property` (string, required): Property name +- `value` (any, required): Property value + +**Example**: +```json +{ + "tool": "component_set_component_property", + "arguments": { + "nodeUuid": "node-uuid-here", + "componentType": "cc.Sprite", + "property": "spriteFrame", + "value": "sprite-frame-uuid" + } +} +``` + +### 3.6 component_attach_script +Attach a script component to a node + +**Parameters**: +- `nodeUuid` (string, required): Node UUID +- `scriptPath` (string, required): Script asset path + +**Example**: +```json +{ + "tool": "component_attach_script", + "arguments": { + "nodeUuid": "node-uuid-here", + "scriptPath": "db://assets/scripts/PlayerController.ts" + } +} +``` + +### 3.7 component_get_available_components +Get list of available component types + +**Parameters**: +- `category` (string, optional): Component category filter, options: `all`, `renderer`, `ui`, `physics`, `animation`, `audio`, default is `all` + +**Example**: +```json +{ + "tool": "component_get_available_components", + "arguments": { + "category": "ui" + } +} +``` + +--- + +## 4. Prefab Tools + +### 4.1 prefab_get_prefab_list +Get all prefabs in the project + +**Parameters**: +- `folder` (string, optional): Folder path to search, default is `db://assets` + +**Example**: +```json +{ + "tool": "prefab_get_prefab_list", + "arguments": { + "folder": "db://assets/prefabs" + } +} +``` + +### 4.2 prefab_load_prefab +Load a prefab by path + +**Parameters**: +- `prefabPath` (string, required): Prefab asset path + +**Example**: +```json +{ + "tool": "prefab_load_prefab", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab" + } +} +``` + +### 4.3 prefab_instantiate_prefab +Instantiate a prefab in the scene + +**Parameters**: +- `prefabPath` (string, required): Prefab asset path +- `parentUuid` (string, optional): Parent node UUID +- `position` (object, optional): Initial position with x, y, z properties + +**Example**: +```json +{ + "tool": "prefab_instantiate_prefab", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab", + "parentUuid": "parent-uuid-here", + "position": {"x": 100, "y": 200, "z": 0} + } +} +``` + +### 4.4 prefab_create_prefab +Create a prefab from a node + +**Parameters**: +- `nodeUuid` (string, required): Source node UUID +- `savePath` (string, required): Path to save the prefab +- `prefabName` (string, required): Prefab name + +**Example**: +```json +{ + "tool": "prefab_create_prefab", + "arguments": { + "nodeUuid": "node-uuid-here", + "savePath": "db://assets/prefabs/", + "prefabName": "MyPrefab" + } +} +``` + +### 4.5 prefab_create_prefab_from_node +Create a prefab from a node (alias for create_prefab) + +**Parameters**: +- `nodeUuid` (string, required): Source node UUID +- `prefabPath` (string, required): Path to save the prefab + +**Example**: +```json +{ + "tool": "prefab_create_prefab_from_node", + "arguments": { + "nodeUuid": "node-uuid-here", + "prefabPath": "db://assets/prefabs/MyPrefab.prefab" + } +} +``` + +### 4.6 prefab_update_prefab +Update an existing prefab + +**Parameters**: +- `prefabPath` (string, required): Prefab asset path +- `nodeUuid` (string, required): Node UUID with changes + +**Example**: +```json +{ + "tool": "prefab_update_prefab", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab", + "nodeUuid": "node-uuid-here" + } +} +``` + +### 4.7 prefab_revert_prefab +Revert prefab instance to original + +**Parameters**: +- `nodeUuid` (string, required): Prefab instance node UUID + +**Example**: +```json +{ + "tool": "prefab_revert_prefab", + "arguments": { + "nodeUuid": "prefab-instance-uuid-here" + } +} +``` + +### 4.8 prefab_get_prefab_info +Get detailed prefab information + +**Parameters**: +- `prefabPath` (string, required): Prefab asset path + +**Example**: +```json +{ + "tool": "prefab_get_prefab_info", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab" + } +} +``` + +--- + +## 5. Project Tools + +### 5.1 project_run_project +Run the project in preview mode + +**Parameters**: +- `platform` (string, optional): Target platform, options: `browser`, `simulator`, `preview`, default is `browser` + +**Example**: +```json +{ + "tool": "project_run_project", + "arguments": { + "platform": "browser" + } +} +``` + +### 5.2 project_build_project +Build the project + +**Parameters**: +- `platform` (string, required): Build platform, options: `web-mobile`, `web-desktop`, `ios`, `android`, `windows`, `mac` +- `debug` (boolean, optional): Debug build, default is true + +**Example**: +```json +{ + "tool": "project_build_project", + "arguments": { + "platform": "web-mobile", + "debug": false + } +} +``` + +### 5.3 project_get_project_info +Get project information + +**Parameters**: None + +**Example**: +```json +{ + "tool": "project_get_project_info", + "arguments": {} +} +``` + +### 5.4 project_get_project_settings +Get project settings + +**Parameters**: +- `category` (string, optional): Settings category, options: `general`, `physics`, `render`, `assets`, default is `general` + +**Example**: +```json +{ + "tool": "project_get_project_settings", + "arguments": { + "category": "physics" + } +} +``` + +### 5.5 project_refresh_assets +Refresh asset database + +**Parameters**: +- `folder` (string, optional): Specific folder to refresh + +**Example**: +```json +{ + "tool": "project_refresh_assets", + "arguments": { + "folder": "db://assets/textures" + } +} +``` + +### 5.6 project_import_asset +Import an asset file + +**Parameters**: +- `sourcePath` (string, required): Source file path +- `targetFolder` (string, required): Target folder in assets + +**Example**: +```json +{ + "tool": "project_import_asset", + "arguments": { + "sourcePath": "/path/to/image.png", + "targetFolder": "db://assets/textures" + } +} +``` + +### 5.7 project_get_asset_info +Get asset information + +**Parameters**: +- `assetPath` (string, required): Asset path + +**Example**: +```json +{ + "tool": "project_get_asset_info", + "arguments": { + "assetPath": "db://assets/textures/player.png" + } +} +``` + +### 5.8 project_get_assets +Get assets by type + +**Parameters**: +- `type` (string, optional): Asset type filter, options: `all`, `scene`, `prefab`, `script`, `texture`, `material`, `mesh`, `audio`, `animation`, default is `all` +- `folder` (string, optional): Folder to search in, default is `db://assets` + +**Example**: +```json +{ + "tool": "project_get_assets", + "arguments": { + "type": "texture", + "folder": "db://assets/textures" + } +} +``` + +### 5.9 project_get_build_settings +Get build settings + +**Parameters**: None + +**Example**: +```json +{ + "tool": "project_get_build_settings", + "arguments": {} +} +``` + +### 5.10 project_open_build_panel +Open the build panel in the editor + +**Parameters**: None + +**Example**: +```json +{ + "tool": "project_open_build_panel", + "arguments": {} +} +``` + +### 5.11 project_check_builder_status +Check if builder worker is ready + +**Parameters**: None + +**Example**: +```json +{ + "tool": "project_check_builder_status", + "arguments": {} +} +``` + +### 5.12 project_start_preview_server +Start preview server + +**Parameters**: +- `port` (number, optional): Preview server port, default is 7456 + +**Example**: +```json +{ + "tool": "project_start_preview_server", + "arguments": { + "port": 8080 + } +} +``` + +### 5.13 project_stop_preview_server +Stop preview server + +**Parameters**: None + +**Example**: +```json +{ + "tool": "project_stop_preview_server", + "arguments": {} +} +``` + +### 5.14 project_create_asset +Create a new asset file or folder + +**Parameters**: +- `url` (string, required): Asset URL +- `content` (string, optional): File content, null for folder +- `overwrite` (boolean, optional): Overwrite existing file, default is false + +**Example**: +```json +{ + "tool": "project_create_asset", + "arguments": { + "url": "db://assets/scripts/NewScript.ts", + "content": "// New TypeScript script\n", + "overwrite": false + } +} +``` + +### 5.15 project_copy_asset +Copy an asset to another location + +**Parameters**: +- `source` (string, required): Source asset URL +- `target` (string, required): Target location URL +- `overwrite` (boolean, optional): Overwrite existing file, default is false + +**Example**: +```json +{ + "tool": "project_copy_asset", + "arguments": { + "source": "db://assets/textures/player.png", + "target": "db://assets/textures/backup/player.png", + "overwrite": false + } +} +``` + +### 5.16 project_move_asset +Move an asset to another location + +**Parameters**: +- `source` (string, required): Source asset URL +- `target` (string, required): Target location URL +- `overwrite` (boolean, optional): Overwrite existing file, default is false + +**Example**: +```json +{ + "tool": "project_move_asset", + "arguments": { + "source": "db://assets/textures/old_player.png", + "target": "db://assets/textures/player.png", + "overwrite": true + } +} +``` + +### 5.17 project_delete_asset +Delete an asset + +**Parameters**: +- `url` (string, required): Asset URL to delete + +**Example**: +```json +{ + "tool": "project_delete_asset", + "arguments": { + "url": "db://assets/textures/unused.png" + } +} +``` + +### 5.18 project_save_asset +Save asset content + +**Parameters**: +- `url` (string, required): Asset URL +- `content` (string, required): Asset content + +**Example**: +```json +{ + "tool": "project_save_asset", + "arguments": { + "url": "db://assets/scripts/GameManager.ts", + "content": "// Updated script content\n" + } +} +``` + +### 5.19 project_reimport_asset +Reimport an asset + +**Parameters**: +- `url` (string, required): Asset URL to reimport + +**Example**: +```json +{ + "tool": "project_reimport_asset", + "arguments": { + "url": "db://assets/textures/player.png" + } +} +``` + +### 5.20 project_query_asset_path +Get asset disk path + +**Parameters**: +- `url` (string, required): Asset URL + +**Example**: +```json +{ + "tool": "project_query_asset_path", + "arguments": { + "url": "db://assets/textures/player.png" + } +} +``` + +### 5.21 project_query_asset_uuid +Get asset UUID from URL + +**Parameters**: +- `url` (string, required): Asset URL + +**Example**: +```json +{ + "tool": "project_query_asset_uuid", + "arguments": { + "url": "db://assets/textures/player.png" + } +} +``` + +### 5.22 project_query_asset_url +Get asset URL from UUID + +**Parameters**: +- `uuid` (string, required): Asset UUID + +**Example**: +```json +{ + "tool": "project_query_asset_url", + "arguments": { + "uuid": "asset-uuid-here" + } +} +``` + +--- + +## 6. Debug Tools + +### 6.1 debug_get_console_logs +Get editor console logs + +**Parameters**: +- `limit` (number, optional): Number of recent logs to retrieve, default is 100 +- `filter` (string, optional): Filter logs by type, options: `all`, `log`, `warn`, `error`, `info`, default is `all` + +**Example**: +```json +{ + "tool": "debug_get_console_logs", + "arguments": { + "limit": 50, + "filter": "error" + } +} +``` + +### 6.2 debug_clear_console +Clear editor console + +**Parameters**: None + +**Example**: +```json +{ + "tool": "debug_clear_console", + "arguments": {} +} +``` + +### 6.3 debug_execute_script +Execute JavaScript in scene context + +**Parameters**: +- `script` (string, required): JavaScript code to execute + +**Example**: +```json +{ + "tool": "debug_execute_script", + "arguments": { + "script": "console.log('Hello from MCP!');" + } +} +``` + +### 6.4 debug_get_node_tree +Get detailed node tree for debugging + +**Parameters**: +- `rootUuid` (string, optional): Root node UUID, uses scene root if not provided +- `maxDepth` (number, optional): Maximum tree depth, default is 10 + +**Example**: +```json +{ + "tool": "debug_get_node_tree", + "arguments": { + "rootUuid": "root-node-uuid", + "maxDepth": 5 + } +} +``` + +### 6.5 debug_get_performance_stats +Get performance statistics + +**Parameters**: None + +**Example**: +```json +{ + "tool": "debug_get_performance_stats", + "arguments": {} +} +``` + +### 6.6 debug_validate_scene +Validate current scene for issues + +**Parameters**: +- `checkMissingAssets` (boolean, optional): Check for missing asset references, default is true +- `checkPerformance` (boolean, optional): Check for performance issues, default is true + +**Example**: +```json +{ + "tool": "debug_validate_scene", + "arguments": { + "checkMissingAssets": true, + "checkPerformance": true + } +} +``` + +### 6.7 debug_get_editor_info +Get editor and environment information + +**Parameters**: None + +**Example**: +```json +{ + "tool": "debug_get_editor_info", + "arguments": {} +} +``` + +--- + +## 7. Preferences Tools + +### 7.1 preferences_get_preferences +Get editor preferences + +**Parameters**: +- `key` (string, optional): Specific preference key to get + +**Example**: +```json +{ + "tool": "preferences_get_preferences", + "arguments": { + "key": "editor.theme" + } +} +``` + +### 7.2 preferences_set_preferences +Set editor preferences + +**Parameters**: +- `key` (string, required): Preference key to set +- `value` (any, required): Preference value to set + +**Example**: +```json +{ + "tool": "preferences_set_preferences", + "arguments": { + "key": "editor.theme", + "value": "dark" + } +} +``` + +### 7.3 preferences_get_global_preferences +Get global editor preferences + +**Parameters**: +- `key` (string, optional): Global preference key to get + +**Example**: +```json +{ + "tool": "preferences_get_global_preferences", + "arguments": { + "key": "global.autoSave" + } +} +``` + +### 7.4 preferences_set_global_preferences +Set global editor preferences + +**Parameters**: +- `key` (string, required): Global preference key to set +- `value` (any, required): Global preference value to set + +**Example**: +```json +{ + "tool": "preferences_set_global_preferences", + "arguments": { + "key": "global.autoSave", + "value": true + } +} +``` + +### 7.5 preferences_get_recent_projects +Get recently opened projects + +**Parameters**: None + +**Example**: +```json +{ + "tool": "preferences_get_recent_projects", + "arguments": {} +} +``` + +### 7.6 preferences_clear_recent_projects +Clear recently opened projects list + +**Parameters**: None + +**Example**: +```json +{ + "tool": "preferences_clear_recent_projects", + "arguments": {} +} +``` + +--- + +## 8. Server Tools + +### 8.1 server_get_server_info +Get server information + +**Parameters**: None + +**Example**: +```json +{ + "tool": "server_get_server_info", + "arguments": {} +} +``` + +### 8.2 server_broadcast_custom_message +Broadcast a custom message + +**Parameters**: +- `message` (string, required): Message name +- `data` (any, optional): Message data + +**Example**: +```json +{ + "tool": "server_broadcast_custom_message", + "arguments": { + "message": "custom_event", + "data": {"type": "test", "value": 123} + } +} +``` + +### 8.3 server_get_editor_version +Get editor version information + +**Parameters**: None + +**Example**: +```json +{ + "tool": "server_get_editor_version", + "arguments": {} +} +``` + +### 8.4 server_get_project_name +Get current project name + +**Parameters**: None + +**Example**: +```json +{ + "tool": "server_get_project_name", + "arguments": {} +} +``` + +### 8.5 server_get_project_path +Get current project path + +**Parameters**: None + +**Example**: +```json +{ + "tool": "server_get_project_path", + "arguments": {} +} +``` + +### 8.6 server_get_project_uuid +Get current project UUID + +**Parameters**: None + +**Example**: +```json +{ + "tool": "server_get_project_uuid", + "arguments": {} +} +``` + +### 8.7 server_restart_editor +Request to restart the editor + +**Parameters**: None + +**Example**: +```json +{ + "tool": "server_restart_editor", + "arguments": {} +} +``` + +### 8.8 server_quit_editor +Request to quit the editor + +**Parameters**: None + +**Example**: +```json +{ + "tool": "server_quit_editor", + "arguments": {} +} +``` + +--- + +## 9. Broadcast Tools + +### 9.1 broadcast_get_broadcast_log +Get recent broadcast messages log + +**Parameters**: +- `limit` (number, optional): Number of recent messages to return, default is 50 +- `messageType` (string, optional): Filter by message type + +**Example**: +```json +{ + "tool": "broadcast_get_broadcast_log", + "arguments": { + "limit": 100, + "messageType": "scene_change" + } +} +``` + +### 9.2 broadcast_listen_broadcast +Start listening for specific broadcast messages + +**Parameters**: +- `messageType` (string, required): Message type to listen for + +**Example**: +```json +{ + "tool": "broadcast_listen_broadcast", + "arguments": { + "messageType": "node_created" + } +} +``` + +### 9.3 broadcast_stop_listening +Stop listening for specific broadcast messages + +**Parameters**: +- `messageType` (string, required): Message type to stop listening for + +**Example**: +```json +{ + "tool": "broadcast_stop_listening", + "arguments": { + "messageType": "node_created" + } +} +``` + +### 9.4 broadcast_clear_broadcast_log +Clear the broadcast messages log + +**Parameters**: None + +**Example**: +```json +{ + "tool": "broadcast_clear_broadcast_log", + "arguments": {} +} +``` + +### 9.5 broadcast_get_active_listeners +Get list of active broadcast listeners + +**Parameters**: None + +**Example**: +```json +{ + "tool": "broadcast_get_active_listeners", + "arguments": {} +} +``` + +--- + +## Usage Guidelines + +### 1. Tool Call Format + +All tool calls use JSON-RPC 2.0 format: + +```json +{ + "jsonrpc": "2.0", + "method": "tools/call", + "params": { + "name": "tool_name", + "arguments": { + // Tool parameters + } + }, + "id": 1 +} +``` + +### 2. Common UUID Retrieval Methods + +- Use `node_get_all_nodes` to get all node UUIDs +- Use `node_find_node_by_name` to find node UUID by name +- Use `scene_get_current_scene` to get scene UUID +- Use `prefab_get_prefab_list` to get prefab information + +### 3. Asset Path Format + +Cocos Creator uses `db://` prefixed asset URL format: +- Scenes: `db://assets/scenes/GameScene.scene` +- Prefabs: `db://assets/prefabs/Player.prefab` +- Scripts: `db://assets/scripts/GameManager.ts` +- Textures: `db://assets/textures/player.png` + +### 4. Error Handling + +If a tool call fails, it will return error information: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": -32000, + "message": "Tool execution failed", + "data": { + "error": "Detailed error message" + } + } +} +``` + +### 5. Best Practices + +1. **Query Before Modify**: Use query tools to get current state before modifying nodes or components +2. **Use UUIDs**: Prefer using UUIDs over names to reference nodes and assets +3. **Error Checking**: Always check tool call return values to ensure operations succeeded +4. **Asset Management**: Ensure no references exist before deleting or moving assets +5. **Performance Considerations**: Avoid frequent tool calls in loops, consider batch operations + +--- + +## Technical Support + +If you encounter issues while using the tools, you can: + +1. Use `debug_get_console_logs` to view detailed error logs +2. Use `debug_validate_scene` to check for scene issues +3. Use `debug_get_editor_info` to get environment information +4. Check the MCP server's running status and logs + +--- + +*This document is based on Cocos Creator MCP Server v1.0.0. Please refer to the latest version documentation for updates.* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100755 index 0000000..9a1a5fd --- /dev/null +++ b/README.md @@ -0,0 +1,361 @@ +# Cocos Creator MCP Server Plugin + +A comprehensive MCP (Model Context Protocol) server plugin for Cocos Creator 3.8+, enabling AI assistants to interact with the Cocos Creator editor through standardized protocols. One-click installation and use, eliminating all cumbersome environments and configurations. Claude clients Claude CLI and Cursor have been tested, and other editors are also perfectly supported in theory. + +**🚀 Now provides 80 tools in 9 categories, achieving 95% editor control! (Prefabs cannot be manipulated for the time being)** + +## Quick Links + +- **[📖 Complete Feature Guide (English)](FEATURE_GUIDE_EN.md)** - Detailed documentation for all 80 tools +- **[📖 完整功能指南 (中文)](FEATURE_GUIDE_CN.md)** - 所有80个工具的详细文档 +- **[🧪 Testing Guide](TEST_GUIDE.md)** - How to test the MCP server +**Claude cli configuration:** + +``` +claude mcp add --transport http http://localhost:3000/mcp (use the port number you configured yourself) +``` + +**Claude client configuration:** + +``` +{ + +"mcpServers": { + +"cocos-creator": { + +"type": "http", + +"url": "http://localhost:3000/mcp" + +} + +} + +} + +``` + +**Cursor or VS class MCP configuration** + +``` +{ + +"mcpServers": { + +"cocos-creator": { +"url": "http://localhost:3000/mcp" +} +} + +} + +``` + +## Features + +### 🎯 Scene Operations +- Get current scene information and complete scene list +- Open scenes by path and save current scene +- Create new scenes with custom names +- Get complete scene hierarchy with component information + +### 🎮 Node Operations +- Create nodes with different types (Node, 2DNode, 3DNode) +- Get node information by UUID and find nodes by name pattern +- Set node properties (position, rotation, scale, active) +- Delete, move, and duplicate nodes with full hierarchy support + +### 🔧 Component Operations +- Add/remove components from nodes +- Get all components of a node with properties +- Set component properties dynamically +- Attach script components from asset paths +- List available component types by category + +### 📦 Prefab Operations +- List all prefabs in project with folder organization +- Load, instantiate, and create prefabs +- Update existing prefabs and revert prefab instances +- Get detailed prefab information including dependencies + +### 🚀 Project Control +- Run project in preview mode (browser/simulator) +- Build project for different platforms (web, mobile, desktop) +- Get project information and settings +- Refresh asset database and import new assets +- Get detailed asset information + +### 🔍 Debug Tools +- Get editor console logs with filtering +- Clear console and execute JavaScript in scene context +- Get detailed node tree for debugging +- Performance statistics and scene validation +- Get editor and environment information + +### ⚙️ Additional Features +- **Preferences Management**: Get/set editor preferences and global settings +- **Server Control**: Server information, project details, and editor control +- **Message Broadcasting**: Listen to and broadcast custom messages +- **Asset Management**: Create, copy, move, delete, and query assets +- **Build System**: Project building and preview server control + +## Installation + +### 1. Copy Plugin Files + +Copy the entire `cocos-mcp-server` folder to your Cocos Creator project's `extensions` directory: + +``` +YourProject/ +├── assets/ +├── extensions/ +│ └── cocos-mcp-server/ <- Place plugin here +│ ├── source/ +│ ├── dist/ +│ ├── package.json +│ └── ... +├── settings/ +└── ... +``` + +### 2. Install Dependencies + +```bash +cd extensions/cocos-mcp-server +npm install +``` + +### 3. Build the Plugin + +```bash +npm run build +``` + +### 4. Enable Plugin + +1. Restart Cocos Creator or refresh extensions +2. The plugin will appear in the Extension menu +3. Click `Extension > Cocos MCP Server` to open the control panel + +## Usage + +### Starting the Server + +1. Open the MCP Server panel from `Extension > Cocos MCP Server` +2. Configure settings: + - **Port**: WebSocket server port (default: 3000) + - **Auto Start**: Automatically start server when editor opens + - **Debug Logging**: Enable detailed logging for development + - **Max Connections**: Maximum concurrent connections allowed + +3. Click "Start Server" to begin accepting connections + +### Connecting AI Assistants + +The server exposes a WebSocket endpoint at `ws://localhost:3000` (or your configured port). + +AI assistants can connect using the MCP protocol and access all available tools. + +### Tool Categories + +Tools are organized by category with naming convention: `category_toolname` + +- **scene_\***: Scene-related operations (8 tools) +- **node_\***: Node manipulation (9 tools) +- **component_\***: Component management (7 tools) +- **prefab_\***: Prefab operations (8 tools) +- **project_\***: Project control (22 tools) +- **debug_\***: Debugging utilities (7 tools) +- **preferences_\***: Editor preferences (6 tools) +- **server_\***: Server information (8 tools) +- **broadcast_\***: Message broadcasting (5 tools) + +**Total: 80 tools** for comprehensive editor control. + +📖 **[View Complete Tool Documentation](FEATURE_GUIDE_EN.md)** for detailed usage examples and parameters. + +## Example Tool Usage + +### Create a new sprite node +```json +{ + "tool": "node_create_node", + "arguments": { + "name": "MySprite", + "nodeType": "2DNode", + "parentUuid": "parent-node-uuid" + } +} +``` + +### Add a Sprite component +```json +{ + "tool": "component_add_component", + "arguments": { + "nodeUuid": "node-uuid", + "componentType": "cc.Sprite" + } +} +``` + +### Instantiate a prefab +```json +{ + "tool": "prefab_instantiate_prefab", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab", + "position": { "x": 100, "y": 200, "z": 0 } + } +} +``` + +### Run project in browser +```json +{ + "tool": "project_run_project", + "arguments": { + "platform": "browser" + } +} +``` + +## Configuration + +Settings are stored in `YourProject/settings/mcp-server.json`: + +```json +{ + "port": 3000, + "autoStart": false, + "enableDebugLog": true, + "allowedOrigins": ["*"], + "maxConnections": 10 +} +``` + +## Icon Setup + +To add an icon for the plugin panel: + +1. Create a PNG icon file (recommended size: 32x32 or 64x64) +2. Place it in the `static/` directory: `static/icon.png` +3. The icon path is already configured in `package.json` + +## Development + +### Project Structure +``` +cocos-mcp-server/ +├── source/ # TypeScript source files +│ ├── main.ts # Plugin entry point +│ ├── mcp-server.ts # MCP server implementation +│ ├── settings.ts # Settings management +│ ├── types/ # TypeScript type definitions +│ ├── tools/ # Tool implementations +│ └── panels/ # UI panel implementation +├── dist/ # Compiled JavaScript output +├── static/ # Static assets (icons, etc.) +├── i18n/ # Internationalization files +├── package.json # Plugin configuration +└── tsconfig.json # TypeScript configuration +``` + +### Building from Source + +```bash +# Install dependencies +npm install + +# Build for development with watch mode +npm run watch + +# Build for production +npm run build +``` + +### Adding New Tools + +1. Create a new tool class in `source/tools/` +2. Implement the `ToolExecutor` interface +3. Add tool to `mcp-server.ts` initialization +4. Tools are automatically exposed via MCP protocol + +### TypeScript Support + +The plugin is fully written in TypeScript with: +- Strict type checking enabled +- Comprehensive type definitions for all APIs +- IntelliSense support for development +- Automatic compilation to JavaScript + +## Testing + +The project includes comprehensive testing tools: + +- **[Manual Testing Guide](TEST_GUIDE.md)** - Step-by-step testing procedures +- **Automated Test Scripts**: `test-mcp-server.js`, `test-all-features.sh`, `test_mcp_server.py` +- **Comprehensive Test Suite**: `comprehensive-test.js` - Tests all 80 tools + +### Running Tests + +```bash +# Run comprehensive test suite +node comprehensive-test.js + +# Run feature-specific tests +./test-all-features.sh + +# Run Node.js test script +node test-mcp-server.js +``` + +## Troubleshooting + +### Common Issues + +1. **Server won't start**: Check port availability and firewall settings +2. **Tools not working**: Ensure scene is loaded and UUIDs are valid +3. **Build errors**: Run `npm run build` to check for TypeScript errors +4. **Connection issues**: Verify WebSocket URL and server status + +### Debug Mode + +Enable debug logging in the plugin panel for detailed operation logs. + +### Using Debug Tools + +```json +{ + "tool": "debug_get_console_logs", + "arguments": {"limit": 50, "filter": "error"} +} +``` + +```json +{ + "tool": "debug_validate_scene", + "arguments": {"checkMissingAssets": true} +} +``` + +## Requirements + +- Cocos Creator 3.8.6 or later +- Node.js (bundled with Cocos Creator) +- TypeScript (installed as dev dependency) + +## Architecture Notes + +This plugin uses a simplified MCP protocol implementation that is compatible with Cocos Creator's CommonJS environment. The WebSocket server provides a JSON-RPC interface for AI assistants to interact with the editor. + +### Protocol Support +- **WebSocket Connection**: `ws://localhost:3000` (configurable port) +- **JSON-RPC 2.0**: Standard request/response format +- **Tool Discovery**: `tools/list` method returns available tools +- **Tool Execution**: `tools/call` method executes specific tools + +## License + +This plug-in is for Cocos Creator project, and the source code is packaged together, which can be used for learning and communication. It is not encrypted. It can support your own secondary development and optimization. Any code of this project or its derivative code cannot be used for any commercial purpose or resale. If you need commercial use, please contact me. \ No newline at end of file diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100755 index 0000000..4d9654f --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,330 @@ +# Cocos Creator MCP 服务器插件 + +一个适用于 Cocos Creator 3.8+ 的综合性 MCP(模型上下文协议)服务器插件,使 AI 助手能够通过标准化协议与 Cocos Creator 编辑器进行交互。一键安装和使用,省去所有繁琐环境和配置。已经测试过Claude客户端Claude CLI和Cursor,其他的编辑器理论上也完美支持。 + +**🚀 现在提供 9 个类别的 80 个工具,实现95%的编辑器控制!(暂时无法操控预制体)** + +## 快速使用 + +**Claude cli配置:** + +``` +claude mcp add --transport http http://localhost:3000/mcp(使用你自己配置的端口号) +``` + +**Claude客户端配置:** + +``` +{ + + "mcpServers": { + + "cocos-creator": { + + "type": "http", + + "url": "http://localhost:3000/mcp" + + } + + } + +} +``` + +**Cursor或VS类MCP配置** + +``` +{ + + "mcpServers": { + + "cocos-creator": { + "url": "http://localhost:3000/mcp" + } + } + +} +``` + +**效果:** + +![image-20250717174157957](./image/入口.png) + + +## 功能特性 + +### 🎯 场景操作 +- 获取当前场景信息和完整场景列表 +- 通过路径打开场景并保存当前场景 +- 创建自定义名称的新场景 +- 获取完整场景层级结构及组件信息 + +### 🎮 节点操作 +- 创建不同类型的节点(Node、2DNode、3DNode) +- 通过 UUID 获取节点信息,按名称模式查找节点 +- 设置节点属性(位置、旋转、缩放、激活状态) +- 删除、移动和复制节点,完整支持层级结构 + +### 🔧 组件操作 +- 向节点添加/删除组件 +- 获取节点的所有组件及属性 +- 动态设置组件属性 +- 从资源路径挂载脚本组件 +- 按类别列出可用的组件类型 + +### 📦 预制体操作 +- 列出项目中的所有预制体,支持文件夹组织 +- 加载、实例化和创建预制体 +- 更新现有预制体并还原预制体实例 +- 获取详细的预制体信息,包括依赖关系 + +### 🚀 项目控制 +- 在预览模式下运行项目(浏览器/模拟器) +- 为不同平台构建项目(Web、移动端、桌面端) +- 获取项目信息和设置 +- 刷新资源数据库并导入新资源 +- 获取详细的资源信息 + +### 🔍 调试工具 +- 获取编辑器控制台日志,支持过滤 +- 清空控制台并在场景上下文中执行 JavaScript +- 获取详细的节点树用于调试 +- 性能统计和场景验证 +- 获取编辑器和环境信息 + +### ⚙️ 其他功能 +- **偏好设置管理**: 获取/设置编辑器偏好和全局设置 +- **服务器控制**: 服务器信息、项目详情和编辑器控制 +- **消息广播**: 监听和广播自定义消息 +- **资源管理**: 创建、复制、移动、删除和查询资源 +- **构建系统**: 项目构建和预览服务器控制 + +## 安装说明 + +### 1. 复制插件文件 + +将整个 `cocos-mcp-server` 文件夹复制到您的 Cocos Creator 项目的 `extensions` 目录中: + +``` +您的项目/ +├── assets/ +├── extensions/ +│ └── cocos-mcp-server/ <- 将插件放在这里 +│ ├── source/ +│ ├── dist/ +│ ├── package.json +│ └── ... +├── settings/ +└── ... +``` + +### 2. 安装依赖 + +```bash +cd extensions/cocos-mcp-server +npm install +``` + +### 3. 构建插件 + +```bash +npm run build +``` + +### 4. 启用插件 + +1. 重启 Cocos Creator 或刷新扩展 +2. 插件将出现在扩展菜单中 +3. 点击 `扩展 > Cocos MCP Server` 打开控制面板 + +## 使用方法 + +### 启动服务器 + +1. 从 `扩展 > Cocos MCP Server` 打开 MCP 服务器面板 +2. 配置设置: + - **端口**: http 服务器端口(默认:3000) + - **自动启动**: 编辑器启动时自动启动服务器 + - **调试日志**: 启用详细日志以便开发调试 + - **最大连接数**: 允许的最大并发连接数 + +3. 点击"启动服务器"开始接受连接 + +### 连接 AI 助手 + +服务器在 `http://localhost:3000/mcp`(或您配置的端口)上提供 http 端点。 + +AI 助手可以使用 MCP 协议连接并访问所有可用工具。 + +### 工具分类 + +工具按类别组织,命名约定为:`category_toolname` + +- **scene_\***: 场景相关操作 (8个工具) +- **node_\***: 节点操作 (9个工具) +- **component_\***: 组件管理 (7个工具) +- **prefab_\***: 预制体操作 (8个工具) +- **project_\***: 项目控制 (22个工具) +- **debug_\***: 调试工具 (7个工具) +- **preferences_\***: 编辑器偏好设置 (6个工具) +- **server_\***: 服务器信息 (8个工具) +- **broadcast_\***: 消息广播 (5个工具) + +**总计: 80个工具** 实现全面的编辑器控制。 + +📖 **[查看完整工具文档](FEATURE_GUIDE_CN.md)** 了解详细的使用示例和参数。 + +## 工具使用示例 + +### 创建新的精灵节点 +```json +{ + "tool": "node_create_node", + "arguments": { + "name": "MySprite", + "nodeType": "2DNode", + "parentUuid": "parent-node-uuid" + } +} +``` + +### 添加 Sprite 组件 +```json +{ + "tool": "component_add_component", + "arguments": { + "nodeUuid": "node-uuid", + "componentType": "cc.Sprite" + } +} +``` + +### 实例化预制体 +```json +{ + "tool": "prefab_instantiate_prefab", + "arguments": { + "prefabPath": "db://assets/prefabs/Enemy.prefab", + "position": { "x": 100, "y": 200, "z": 0 } + } +} +``` + +### 在浏览器中运行项目 +```json +{ + "tool": "project_run_project", + "arguments": { + "platform": "browser" + } +} +``` + +## 配置 + +设置存储在 `您的项目/settings/mcp-server.json` 中: + +```json +{ + "port": 3000, + "autoStart": false, + "enableDebugLog": true, + "allowedOrigins": ["*"], + "maxConnections": 10 +} +``` + +## 图标设置 + +为插件面板添加图标: + +1. 创建 PNG 图标文件(推荐尺寸:32x32 或 64x64) +2. 将其放在 `static/` 目录中:`static/icon.png` +3. 图标路径已在 `package.json` 中配置 + +## 开发 + +### 项目结构 +``` +cocos-mcp-server/ +├── source/ # TypeScript 源文件 +│ ├── main.ts # 插件入口点 +│ ├── mcp-server.ts # MCP 服务器实现 +│ ├── settings.ts # 设置管理 +│ ├── types/ # TypeScript 类型定义 +│ ├── tools/ # 工具实现 +│ └── panels/ # UI 面板实现 +├── dist/ # 编译后的 JavaScript 输出 +├── static/ # 静态资源(图标等) +├── i18n/ # 国际化文件 +├── package.json # 插件配置 +└── tsconfig.json # TypeScript 配置 +``` + +### 从源码构建 + +```bash +# 安装依赖 +npm install + +# 开发构建(监视模式) +npm run watch + +# 生产构建 +npm run build +``` + +### 添加新工具 + +1. 在 `source/tools/` 中创建新的工具类 +2. 实现 `ToolExecutor` 接口 +3. 将工具添加到 `mcp-server.ts` 初始化中 +4. 工具会自动通过 MCP 协议暴露 + +### TypeScript 支持 + +插件完全使用 TypeScript 编写,具备: +- 启用严格类型检查 +- 为所有 API 提供全面的类型定义 +- 开发时的 IntelliSense 支持 +- 自动编译为 JavaScript + +## 故障排除 + +### 常见问题 + +1. **服务器无法启动**: 检查端口可用性和防火墙设置 +2. **工具不工作**: 确保场景已加载且 UUID 有效 +3. **构建错误**: 运行 `npm run build` 检查 TypeScript 错误 +4. **连接问题**: 验证 HTTP URL 和服务器状态 + +### 调试模式 + +在插件面板中启用调试日志以获取详细的操作日志。 + +### 使用调试工具 + +```json +{ + "tool": "debug_get_console_logs", + "arguments": {"limit": 50, "filter": "error"} +} +``` + +```json +{ + "tool": "debug_validate_scene", + "arguments": {"checkMissingAssets": true} +} +``` + +## 系统要求 + +- Cocos Creator 3.8.0 或更高版本 +- Node.js(Cocos Creator 自带) +- TypeScript(作为开发依赖安装) + +## 许可证 + +本插件供 Cocos Creator 项目使用,并且源代码一并打包,可以用于学习和交流。没有加密。可以支持你自己二次开发优化,任何本项目代码或者衍生代码均不能用于任何商用、转售,如果需要商用,请联系本人。 \ No newline at end of file diff --git a/base.tsconfig.json b/base.tsconfig.json new file mode 100755 index 0000000..6fd6302 --- /dev/null +++ b/base.tsconfig.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json", + "compilerOptions": { + "target": "ES2017", + "module": "CommonJS", + "moduleResolution": "node", + "inlineSourceMap": true, + "inlineSources": true, + "esModuleInterop": true, + "skipLibCheck": true, + "strict": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "outDir": "./dist", + "rootDir": "./source", + "types": [ + "node", + "@cocos/creator-types/editor", + ] + } +} \ No newline at end of file diff --git a/comprehensive-test.js b/comprehensive-test.js new file mode 100644 index 0000000..457886f --- /dev/null +++ b/comprehensive-test.js @@ -0,0 +1,230 @@ +const http = require('http'); + +const SERVER_URL = 'http://localhost:8585'; + +class MCPTester { + constructor() { + this.testResults = []; + this.currentCategory = ''; + } + + async makeRequest(toolName, args = {}) { + const payload = { + jsonrpc: '2.0', + id: Date.now(), + method: 'tools/call', + params: { + name: toolName, + arguments: args + } + }; + + return new Promise((resolve, reject) => { + const data = JSON.stringify(payload); + const options = { + hostname: '127.0.0.1', + port: 8585, + path: '/mcp', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': data.length + } + }; + + const req = http.request(options, (res) => { + let responseData = ''; + res.on('data', chunk => responseData += chunk); + res.on('end', () => { + try { + const response = JSON.parse(responseData); + resolve(response); + } catch (e) { + reject(e); + } + }); + }); + + req.on('error', reject); + req.write(data); + req.end(); + }); + } + + async testTool(toolName, args = {}, description = '') { + const testName = description || toolName.replace(/_/g, ' '); + process.stdout.write(` ${testName}... `); + + try { + const response = await this.makeRequest(toolName, args); + + if (response.result && response.result.success !== false) { + console.log('✅ 通过'); + this.testResults.push({ tool: toolName, status: 'pass', category: this.currentCategory }); + return response.result; + } else { + const error = response.result?.error || response.error?.message || 'Unknown error'; + console.log(`❌ 失败: ${error}`); + this.testResults.push({ tool: toolName, status: 'fail', error, category: this.currentCategory }); + return null; + } + } catch (error) { + console.log(`❌ 错误: ${error.message}`); + this.testResults.push({ tool: toolName, status: 'error', error: error.message, category: this.currentCategory }); + return null; + } + } + + printCategory(categoryName) { + this.currentCategory = categoryName; + console.log(`\n📁 ${categoryName}:`); + } + + printSummary() { + console.log('\n' + '='.repeat(60)); + console.log('📊 测试结果汇总:'); + console.log('='.repeat(60)); + + const categories = [...new Set(this.testResults.map(r => r.category))]; + + for (const category of categories) { + const categoryResults = this.testResults.filter(r => r.category === category); + const passed = categoryResults.filter(r => r.status === 'pass').length; + const failed = categoryResults.filter(r => r.status === 'fail').length; + const errors = categoryResults.filter(r => r.status === 'error').length; + + console.log(`\n${category}:`); + console.log(` ✅ 通过: ${passed}`); + console.log(` ❌ 失败: ${failed}`); + console.log(` ⚠️ 错误: ${errors}`); + console.log(` 📊 总计: ${categoryResults.length}`); + } + + const totalPassed = this.testResults.filter(r => r.status === 'pass').length; + const totalFailed = this.testResults.filter(r => r.status === 'fail').length; + const totalErrors = this.testResults.filter(r => r.status === 'error').length; + const total = this.testResults.length; + + console.log(`\n总体结果:`); + console.log(` ✅ 通过: ${totalPassed}/${total} (${((totalPassed/total)*100).toFixed(1)}%)`); + console.log(` ❌ 失败: ${totalFailed}/${total} (${((totalFailed/total)*100).toFixed(1)}%)`); + console.log(` ⚠️ 错误: ${totalErrors}/${total} (${((totalErrors/total)*100).toFixed(1)}%)`); + + // 显示失败的工具 + const failedTools = this.testResults.filter(r => r.status === 'fail' || r.status === 'error'); + if (failedTools.length > 0) { + console.log('\n❌ 失败的工具:'); + failedTools.forEach(tool => { + console.log(` - ${tool.tool}: ${tool.error}`); + }); + } + } + + async runAllTests() { + console.log('🚀 开始全面测试 Cocos MCP 工具...\n'); + + // 1. 场景管理工具 + this.printCategory('场景管理'); + await this.testTool('scene_create_scene', { + sceneName: 'TestScene2', + savePath: 'db://assets/scenes/TestScene2.scene' + }, '创建场景'); + await this.testTool('scene_get_current_scene', {}, '获取当前场景'); + await this.testTool('scene_save_scene', {}, '保存场景'); + await this.testTool('scene_get_scene_list', {}, '获取场景列表'); + await this.testTool('scene_get_scene_hierarchy', {}, '获取场景层次结构'); + + // 2. 节点操作工具 + this.printCategory('节点操作'); + const nodeResult = await this.testTool('node_create_node', { name: 'TestNode' }, '创建节点'); + let testNodeUuid = null; + if (nodeResult && nodeResult.data && nodeResult.data.uuid) { + testNodeUuid = nodeResult.data.uuid; + } + + await this.testTool('node_get_all_nodes', {}, '获取所有节点'); + await this.testTool('node_find_node_by_name', { name: 'TestNode' }, '按名称查找节点'); + + if (testNodeUuid) { + await this.testTool('node_get_node_info', { uuid: testNodeUuid }, '获取节点信息'); + await this.testTool('node_set_node_property', { + uuid: testNodeUuid, + property: 'position', + value: { x: 100, y: 100, z: 0 } + }, '设置节点属性'); + await this.testTool('node_duplicate_node', { uuid: testNodeUuid }, '复制节点'); + } + + // 3. 组件管理工具 + this.printCategory('组件管理'); + if (testNodeUuid) { + await this.testTool('component_add_component', { + nodeUuid: testNodeUuid, + componentType: 'cc.Sprite' + }, '添加组件'); + await this.testTool('component_get_components', { nodeUuid: testNodeUuid }, '获取组件列表'); + await this.testTool('component_get_component_info', { + nodeUuid: testNodeUuid, + componentType: 'cc.Sprite' + }, '获取组件信息'); + } + await this.testTool('component_get_available_components', {}, '获取可用组件'); + + // 4. 预制体工具 + this.printCategory('预制体管理'); + if (testNodeUuid) { + await this.testTool('prefab_create_prefab', { + nodeUuid: testNodeUuid, + prefabPath: 'db://assets/prefabs/TestPrefab.prefab' + }, '创建预制体'); + } + await this.testTool('prefab_get_prefab_list', {}, '获取预制体列表'); + await this.testTool('prefab_instantiate_prefab', { + prefabPath: 'db://assets/prefabs/TestPrefab.prefab' + }, '实例化预制体'); + + // 5. 项目和资源管理 + this.printCategory('项目资源管理'); + await this.testTool('project_get_project_info', {}, '获取项目信息'); + await this.testTool('project_get_project_settings', {}, '获取项目设置'); + await this.testTool('project_get_assets', { type: 'all' }, '获取资源列表'); + await this.testTool('project_refresh_assets', {}, '刷新资源'); + await this.testTool('project_create_asset', { + url: 'db://assets/test-folder', + content: null + }, '创建资源文件夹'); + + // 6. 调试工具 + this.printCategory('调试工具'); + await this.testTool('debug_get_console_logs', {}, '获取控制台日志'); + await this.testTool('debug_get_performance_stats', {}, '获取性能统计'); + await this.testTool('debug_validate_scene', {}, '验证场景'); + await this.testTool('debug_get_editor_info', {}, '获取编辑器信息'); + + // 7. 偏好设置工具 + this.printCategory('偏好设置'); + await this.testTool('preferences_get_preferences', {}, '获取偏好设置'); + await this.testTool('preferences_get_global_preferences', {}, '获取全局偏好设置'); + await this.testTool('preferences_get_recent_projects', {}, '获取最近项目'); + + // 8. 服务器控制工具 + this.printCategory('服务器控制'); + await this.testTool('server_get_server_info', {}, '获取服务器信息'); + await this.testTool('server_get_editor_version', {}, '获取编辑器版本'); + + // 9. 广播消息工具 + this.printCategory('广播消息'); + await this.testTool('broadcast_get_broadcast_log', {}, '获取广播日志'); + + // 清理测试节点 + if (testNodeUuid) { + await this.testTool('node_delete_node', { uuid: testNodeUuid }, '删除测试节点'); + } + + this.printSummary(); + } +} + +// 运行测试 +const tester = new MCPTester(); +tester.runAllTests().catch(console.error); \ No newline at end of file diff --git a/debug-api.js b/debug-api.js new file mode 100644 index 0000000..a1e4ecd --- /dev/null +++ b/debug-api.js @@ -0,0 +1,46 @@ +// 测试可用的 Editor API +const testMethods = [ + 'scene.query-node-tree', + 'scene.create-node', + 'scene.remove-node', + 'scene.set-property', + 'scene.create-component', + 'scene.remove-component', + 'scene.add-component', + 'scene.query-node', + 'scene.query-scene', + 'scene.query-hierarchy' +]; + +async function testAPI() { + console.log('Testing available Editor API methods...'); + + for (const method of testMethods) { + console.log(`\nTesting: ${method}`); + + try { + const response = await fetch('http://localhost:8585/mcp', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'tools/call', + params: { + name: 'debug_test_editor_api', + arguments: { + method: method.replace('.', ' - ') + } + } + }) + }); + + const data = await response.json(); + console.log(`Response: ${JSON.stringify(data)}`); + } catch (error) { + console.log(`Error: ${error.message}`); + } + } +} + +testAPI(); \ No newline at end of file diff --git a/dist/main.js b/dist/main.js new file mode 100644 index 0000000..2adc9d6 --- /dev/null +++ b/dist/main.js @@ -0,0 +1,119 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.methods = void 0; +exports.load = load; +exports.unload = unload; +const mcp_server_1 = require("./mcp-server"); +const settings_1 = require("./settings"); +let mcpServer = null; +/** + * @en Registration method for the main process of Extension + * @zh 为扩展的主进程的注册方法 + */ +exports.methods = { + /** + * @en Open the MCP server panel + * @zh 打开 MCP 服务器面板 + */ + openPanel() { + Editor.Panel.open('cocos-mcp-server'); + }, + /** + * @en Start the MCP server + * @zh 启动 MCP 服务器 + */ + async startServer() { + if (mcpServer) { + await mcpServer.start(); + } + else { + console.warn('[MCP插件] mcpServer 未初始化'); + } + }, + /** + * @en Stop the MCP server + * @zh 停止 MCP 服务器 + */ + async stopServer() { + if (mcpServer) { + mcpServer.stop(); + } + else { + console.warn('[MCP插件] mcpServer 未初始化'); + } + }, + /** + * @en Get server status + * @zh 获取服务器状态 + */ + getServerStatus() { + return mcpServer ? mcpServer.getStatus() : { running: false, port: 0, clients: 0 }; + }, + /** + * @en Update server settings + * @zh 更新服务器设置 + */ + updateSettings(settings) { + (0, settings_1.saveSettings)(settings); + if (mcpServer) { + mcpServer.stop(); + mcpServer = new mcp_server_1.MCPServer(settings); + mcpServer.start(); + } + else { + mcpServer = new mcp_server_1.MCPServer(settings); + mcpServer.start(); + } + }, + /** + * @en Get tools list + * @zh 获取工具列表 + */ + getToolsList() { + return mcpServer ? mcpServer.getAvailableTools() : []; + }, + /** + * @en Get server settings + * @zh 获取服务器设置 + */ + getServerSettings() { + return mcpServer ? mcpServer.getSettings() : (0, settings_1.readSettings)(); + } +}; +/** + * @en Method Triggered on Extension Startup + * @zh 扩展启动时触发的方法 + */ +function load() { + console.log('[MCP Plugin] Loading MCP server plugin...'); + try { + const settings = (0, settings_1.readSettings)(); + console.log('[MCP Plugin] Settings loaded:', settings); + mcpServer = new mcp_server_1.MCPServer(settings); + // 如果设置了自动启动,则启动服务器 + if (settings.autoStart) { + console.log('[MCP Plugin] Auto-starting MCP server...'); + mcpServer.start().catch(error => { + console.error('[MCP Plugin] Failed to auto-start server:', error); + }); + } + else { + console.log('[MCP Plugin] MCP server created but not started (autoStart=false)'); + console.log('[MCP Plugin] Use the MCP panel or call startServer() to start the server'); + } + } + catch (error) { + console.error('[MCP Plugin] Failed to load MCP server:', error); + } +} +/** + * @en Method triggered when uninstalling the extension + * @zh 卸载扩展时触发的方法 + */ +function unload() { + if (mcpServer) { + mcpServer.stop(); + mcpServer = null; + } +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NvdXJjZS9tYWluLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQXVGQSxvQkFvQkM7QUFNRCx3QkFLQztBQXRIRCw2Q0FBeUM7QUFDekMseUNBQXdEO0FBR3hELElBQUksU0FBUyxHQUFxQixJQUFJLENBQUM7QUFFdkM7OztHQUdHO0FBQ1UsUUFBQSxPQUFPLEdBQTRDO0lBQzVEOzs7T0FHRztJQUNILFNBQVM7UUFDTCxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0lBQzFDLENBQUM7SUFFRDs7O09BR0c7SUFDSCxLQUFLLENBQUMsV0FBVztRQUNiLElBQUksU0FBUyxFQUFFLENBQUM7WUFDWixNQUFNLFNBQVMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM1QixDQUFDO2FBQU0sQ0FBQztZQUNKLE9BQU8sQ0FBQyxJQUFJLENBQUMsd0JBQXdCLENBQUMsQ0FBQztRQUMzQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxVQUFVO1FBQ1osSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNaLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNyQixDQUFDO2FBQU0sQ0FBQztZQUNKLE9BQU8sQ0FBQyxJQUFJLENBQUMsd0JBQXdCLENBQUMsQ0FBQztRQUMzQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7T0FHRztJQUNILGVBQWU7UUFDWCxPQUFPLFNBQVMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFDdkYsQ0FBQztJQUVEOzs7T0FHRztJQUNILGNBQWMsQ0FBQyxRQUEyQjtRQUN0QyxJQUFBLHVCQUFZLEVBQUMsUUFBUSxDQUFDLENBQUM7UUFDdkIsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNaLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNqQixTQUFTLEdBQUcsSUFBSSxzQkFBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ3BDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUN0QixDQUFDO2FBQU0sQ0FBQztZQUNKLFNBQVMsR0FBRyxJQUFJLHNCQUFTLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDcEMsU0FBUyxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3RCLENBQUM7SUFDTCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsWUFBWTtRQUNSLE9BQU8sU0FBUyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQzFELENBQUM7SUFDRDs7O09BR0c7SUFDSCxpQkFBaUI7UUFDYixPQUFPLFNBQVMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFBLHVCQUFZLEdBQUUsQ0FBQztJQUNoRSxDQUFDO0NBQ0osQ0FBQztBQUVGOzs7R0FHRztBQUNILFNBQWdCLElBQUk7SUFDaEIsT0FBTyxDQUFDLEdBQUcsQ0FBQywyQ0FBMkMsQ0FBQyxDQUFDO0lBQ3pELElBQUksQ0FBQztRQUNELE1BQU0sUUFBUSxHQUFHLElBQUEsdUJBQVksR0FBRSxDQUFDO1FBQ2hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsK0JBQStCLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDdkQsU0FBUyxHQUFHLElBQUksc0JBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUVwQyxtQkFBbUI7UUFDbkIsSUFBSSxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDckIsT0FBTyxDQUFDLEdBQUcsQ0FBQywwQ0FBMEMsQ0FBQyxDQUFDO1lBQ3hELFNBQVMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLEVBQUU7Z0JBQzVCLE9BQU8sQ0FBQyxLQUFLLENBQUMsMkNBQTJDLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDdEUsQ0FBQyxDQUFDLENBQUM7UUFDUCxDQUFDO2FBQU0sQ0FBQztZQUNKLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUVBQW1FLENBQUMsQ0FBQztZQUNqRixPQUFPLENBQUMsR0FBRyxDQUFDLDBFQUEwRSxDQUFDLENBQUM7UUFDNUYsQ0FBQztJQUNMLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2IsT0FBTyxDQUFDLEtBQUssQ0FBQyx5Q0FBeUMsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNwRSxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7R0FHRztBQUNILFNBQWdCLE1BQU07SUFDbEIsSUFBSSxTQUFTLEVBQUUsQ0FBQztRQUNaLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNqQixTQUFTLEdBQUcsSUFBSSxDQUFDO0lBQ3JCLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgTUNQU2VydmVyIH0gZnJvbSAnLi9tY3Atc2VydmVyJztcbmltcG9ydCB7IHJlYWRTZXR0aW5ncywgc2F2ZVNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgeyBNQ1BTZXJ2ZXJTZXR0aW5ncyB9IGZyb20gJy4vdHlwZXMnO1xuXG5sZXQgbWNwU2VydmVyOiBNQ1BTZXJ2ZXIgfCBudWxsID0gbnVsbDtcblxuLyoqXG4gKiBAZW4gUmVnaXN0cmF0aW9uIG1ldGhvZCBmb3IgdGhlIG1haW4gcHJvY2VzcyBvZiBFeHRlbnNpb25cbiAqIEB6aCDkuLrmianlsZXnmoTkuLvov5vnqIvnmoTms6jlhozmlrnms5VcbiAqL1xuZXhwb3J0IGNvbnN0IG1ldGhvZHM6IHsgW2tleTogc3RyaW5nXTogKC4uLmFueTogYW55KSA9PiBhbnkgfSA9IHtcbiAgICAvKipcbiAgICAgKiBAZW4gT3BlbiB0aGUgTUNQIHNlcnZlciBwYW5lbFxuICAgICAqIEB6aCDmiZPlvIAgTUNQIOacjeWKoeWZqOmdouadv1xuICAgICAqL1xuICAgIG9wZW5QYW5lbCgpIHtcbiAgICAgICAgRWRpdG9yLlBhbmVsLm9wZW4oJ2NvY29zLW1jcC1zZXJ2ZXInKTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQGVuIFN0YXJ0IHRoZSBNQ1Agc2VydmVyXG4gICAgICogQHpoIOWQr+WKqCBNQ1Ag5pyN5Yqh5ZmoXG4gICAgICovXG4gICAgYXN5bmMgc3RhcnRTZXJ2ZXIoKSB7XG4gICAgICAgIGlmIChtY3BTZXJ2ZXIpIHtcbiAgICAgICAgICAgIGF3YWl0IG1jcFNlcnZlci5zdGFydCgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKCdbTUNQ5o+S5Lu2XSBtY3BTZXJ2ZXIg5pyq5Yid5aeL5YyWJyk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQGVuIFN0b3AgdGhlIE1DUCBzZXJ2ZXJcbiAgICAgKiBAemgg5YGc5q2iIE1DUCDmnI3liqHlmahcbiAgICAgKi9cbiAgICBhc3luYyBzdG9wU2VydmVyKCkge1xuICAgICAgICBpZiAobWNwU2VydmVyKSB7XG4gICAgICAgICAgICBtY3BTZXJ2ZXIuc3RvcCgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKCdbTUNQ5o+S5Lu2XSBtY3BTZXJ2ZXIg5pyq5Yid5aeL5YyWJyk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQGVuIEdldCBzZXJ2ZXIgc3RhdHVzXG4gICAgICogQHpoIOiOt+WPluacjeWKoeWZqOeKtuaAgVxuICAgICAqL1xuICAgIGdldFNlcnZlclN0YXR1cygpIHtcbiAgICAgICAgcmV0dXJuIG1jcFNlcnZlciA/IG1jcFNlcnZlci5nZXRTdGF0dXMoKSA6IHsgcnVubmluZzogZmFsc2UsIHBvcnQ6IDAsIGNsaWVudHM6IDAgfTtcbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogQGVuIFVwZGF0ZSBzZXJ2ZXIgc2V0dGluZ3NcbiAgICAgKiBAemgg5pu05paw5pyN5Yqh5Zmo6K6+572uXG4gICAgICovXG4gICAgdXBkYXRlU2V0dGluZ3Moc2V0dGluZ3M6IE1DUFNlcnZlclNldHRpbmdzKSB7XG4gICAgICAgIHNhdmVTZXR0aW5ncyhzZXR0aW5ncyk7XG4gICAgICAgIGlmIChtY3BTZXJ2ZXIpIHtcbiAgICAgICAgICAgIG1jcFNlcnZlci5zdG9wKCk7XG4gICAgICAgICAgICBtY3BTZXJ2ZXIgPSBuZXcgTUNQU2VydmVyKHNldHRpbmdzKTtcbiAgICAgICAgICAgIG1jcFNlcnZlci5zdGFydCgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbWNwU2VydmVyID0gbmV3IE1DUFNlcnZlcihzZXR0aW5ncyk7XG4gICAgICAgICAgICBtY3BTZXJ2ZXIuc3RhcnQoKTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBAZW4gR2V0IHRvb2xzIGxpc3RcbiAgICAgKiBAemgg6I635Y+W5bel5YW35YiX6KGoXG4gICAgICovXG4gICAgZ2V0VG9vbHNMaXN0KCkge1xuICAgICAgICByZXR1cm4gbWNwU2VydmVyID8gbWNwU2VydmVyLmdldEF2YWlsYWJsZVRvb2xzKCkgOiBbXTtcbiAgICB9LFxuICAgIC8qKlxuICAgICAqIEBlbiBHZXQgc2VydmVyIHNldHRpbmdzXG4gICAgICogQHpoIOiOt+WPluacjeWKoeWZqOiuvue9rlxuICAgICAqL1xuICAgIGdldFNlcnZlclNldHRpbmdzKCkge1xuICAgICAgICByZXR1cm4gbWNwU2VydmVyID8gbWNwU2VydmVyLmdldFNldHRpbmdzKCkgOiByZWFkU2V0dGluZ3MoKTtcbiAgICB9XG59O1xuXG4vKipcbiAqIEBlbiBNZXRob2QgVHJpZ2dlcmVkIG9uIEV4dGVuc2lvbiBTdGFydHVwXG4gKiBAemgg5omp5bGV5ZCv5Yqo5pe26Kem5Y+R55qE5pa55rOVXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkKCkge1xuICAgIGNvbnNvbGUubG9nKCdbTUNQIFBsdWdpbl0gTG9hZGluZyBNQ1Agc2VydmVyIHBsdWdpbi4uLicpO1xuICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHNldHRpbmdzID0gcmVhZFNldHRpbmdzKCk7XG4gICAgICAgIGNvbnNvbGUubG9nKCdbTUNQIFBsdWdpbl0gU2V0dGluZ3MgbG9hZGVkOicsIHNldHRpbmdzKTtcbiAgICAgICAgbWNwU2VydmVyID0gbmV3IE1DUFNlcnZlcihzZXR0aW5ncyk7XG4gICAgICAgIFxuICAgICAgICAvLyDlpoLmnpzorr7nva7kuoboh6rliqjlkK/liqjvvIzliJnlkK/liqjmnI3liqHlmahcbiAgICAgICAgaWYgKHNldHRpbmdzLmF1dG9TdGFydCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1tNQ1AgUGx1Z2luXSBBdXRvLXN0YXJ0aW5nIE1DUCBzZXJ2ZXIuLi4nKTtcbiAgICAgICAgICAgIG1jcFNlcnZlci5zdGFydCgpLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdbTUNQIFBsdWdpbl0gRmFpbGVkIHRvIGF1dG8tc3RhcnQgc2VydmVyOicsIGVycm9yKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1tNQ1AgUGx1Z2luXSBNQ1Agc2VydmVyIGNyZWF0ZWQgYnV0IG5vdCBzdGFydGVkIChhdXRvU3RhcnQ9ZmFsc2UpJyk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnW01DUCBQbHVnaW5dIFVzZSB0aGUgTUNQIHBhbmVsIG9yIGNhbGwgc3RhcnRTZXJ2ZXIoKSB0byBzdGFydCB0aGUgc2VydmVyJyk7XG4gICAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdbTUNQIFBsdWdpbl0gRmFpbGVkIHRvIGxvYWQgTUNQIHNlcnZlcjonLCBlcnJvcik7XG4gICAgfVxufVxuXG4vKipcbiAqIEBlbiBNZXRob2QgdHJpZ2dlcmVkIHdoZW4gdW5pbnN0YWxsaW5nIHRoZSBleHRlbnNpb25cbiAqIEB6aCDljbjovb3mianlsZXml7bop6blj5HnmoTmlrnms5VcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVubG9hZCgpIHtcbiAgICBpZiAobWNwU2VydmVyKSB7XG4gICAgICAgIG1jcFNlcnZlci5zdG9wKCk7XG4gICAgICAgIG1jcFNlcnZlciA9IG51bGw7XG4gICAgfVxufSJdfQ== \ No newline at end of file diff --git a/dist/mcp-server.js b/dist/mcp-server.js new file mode 100644 index 0000000..597f11c --- /dev/null +++ b/dist/mcp-server.js @@ -0,0 +1,268 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MCPServer = void 0; +const http = __importStar(require("http")); +const url = __importStar(require("url")); +const scene_tools_1 = require("./tools/scene-tools"); +const node_tools_1 = require("./tools/node-tools"); +const component_tools_1 = require("./tools/component-tools"); +const prefab_tools_1 = require("./tools/prefab-tools"); +const project_tools_1 = require("./tools/project-tools"); +const debug_tools_1 = require("./tools/debug-tools"); +const preferences_tools_1 = require("./tools/preferences-tools"); +const server_tools_1 = require("./tools/server-tools"); +const broadcast_tools_1 = require("./tools/broadcast-tools"); +class MCPServer { + constructor(settings) { + this.httpServer = null; + this.clients = new Map(); + this.tools = {}; + this.toolsList = []; + this.settings = settings; + this.initializeTools(); + } + initializeTools() { + try { + console.log('[MCPServer] Initializing tools...'); + this.tools.scene = new scene_tools_1.SceneTools(); + this.tools.node = new node_tools_1.NodeTools(); + this.tools.component = new component_tools_1.ComponentTools(); + this.tools.prefab = new prefab_tools_1.PrefabTools(); + this.tools.project = new project_tools_1.ProjectTools(); + this.tools.debug = new debug_tools_1.DebugTools(); + this.tools.preferences = new preferences_tools_1.PreferencesTools(); + this.tools.server = new server_tools_1.ServerTools(); + this.tools.broadcast = new broadcast_tools_1.BroadcastTools(); + console.log('[MCPServer] Tools initialized successfully'); + } + catch (error) { + console.error('[MCPServer] Error initializing tools:', error); + throw error; + } + } + async start() { + if (this.httpServer) { + console.log('[MCPServer] Server is already running'); + return; + } + try { + console.log(`[MCPServer] Starting HTTP server on port ${this.settings.port}...`); + this.httpServer = http.createServer(this.handleHttpRequest.bind(this)); + await new Promise((resolve, reject) => { + this.httpServer.listen(this.settings.port, '127.0.0.1', () => { + console.log(`[MCPServer] ✅ HTTP server started successfully on http://127.0.0.1:${this.settings.port}`); + console.log(`[MCPServer] Health check: http://127.0.0.1:${this.settings.port}/health`); + console.log(`[MCPServer] MCP endpoint: http://127.0.0.1:${this.settings.port}/mcp`); + resolve(); + }); + this.httpServer.on('error', (err) => { + console.error('[MCPServer] ❌ Failed to start server:', err); + if (err.code === 'EADDRINUSE') { + console.error(`[MCPServer] Port ${this.settings.port} is already in use. Please change the port in settings.`); + } + reject(err); + }); + }); + this.setupTools(); + console.log('[MCPServer] 🚀 MCP Server is ready for connections'); + } + catch (error) { + console.error('[MCPServer] ❌ Failed to start server:', error); + throw error; + } + } + setupTools() { + this.toolsList = []; + for (const [category, toolSet] of Object.entries(this.tools)) { + const tools = toolSet.getTools(); + for (const tool of tools) { + this.toolsList.push({ + name: `${category}_${tool.name}`, + description: tool.description, + inputSchema: tool.inputSchema + }); + } + } + } + async executeToolCall(toolName, args) { + const parts = toolName.split('_'); + const category = parts[0]; + const toolMethodName = parts.slice(1).join('_'); + if (this.tools[category]) { + return await this.tools[category].execute(toolMethodName, args); + } + throw new Error(`Tool ${toolName} not found`); + } + getClients() { + return Array.from(this.clients.values()); + } + getAvailableTools() { + return this.toolsList; + } + getSettings() { + return this.settings; + } + async handleHttpRequest(req, res) { + const parsedUrl = url.parse(req.url || '', true); + const pathname = parsedUrl.pathname; + // Set CORS headers + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); + res.setHeader('Content-Type', 'application/json'); + if (req.method === 'OPTIONS') { + res.writeHead(200); + res.end(); + return; + } + try { + if (pathname === '/mcp' && req.method === 'POST') { + await this.handleMCPRequest(req, res); + } + else if (pathname === '/health' && req.method === 'GET') { + res.writeHead(200); + res.end(JSON.stringify({ status: 'ok', tools: this.toolsList.length })); + } + else { + res.writeHead(404); + res.end(JSON.stringify({ error: 'Not found' })); + } + } + catch (error) { + console.error('HTTP request error:', error); + res.writeHead(500); + res.end(JSON.stringify({ error: 'Internal server error' })); + } + } + async handleMCPRequest(req, res) { + let body = ''; + req.on('data', (chunk) => { + body += chunk.toString(); + }); + req.on('end', async () => { + try { + const message = JSON.parse(body); + const response = await this.handleMessage(message); + res.writeHead(200); + res.end(JSON.stringify(response)); + } + catch (error) { + console.error('Error handling MCP request:', error); + res.writeHead(400); + res.end(JSON.stringify({ + jsonrpc: '2.0', + id: null, + error: { + code: -32700, + message: 'Parse error' + } + })); + } + }); + } + async handleMessage(message) { + const { id, method, params } = message; + try { + let result; + switch (method) { + case 'tools/list': + result = { tools: this.getAvailableTools() }; + break; + case 'tools/call': + const { name, arguments: args } = params; + const toolResult = await this.executeToolCall(name, args); + result = { content: [{ type: 'text', text: JSON.stringify(toolResult) }] }; + break; + case 'initialize': + // MCP initialization + result = { + protocolVersion: '2024-11-05', + capabilities: { + tools: {} + }, + serverInfo: { + name: 'cocos-mcp-server', + version: '1.0.0' + } + }; + break; + default: + throw new Error(`Unknown method: ${method}`); + } + return { + jsonrpc: '2.0', + id, + result + }; + } + catch (error) { + return { + jsonrpc: '2.0', + id, + error: { + code: -32603, + message: error.message + } + }; + } + } + stop() { + if (this.httpServer) { + this.httpServer.close(); + this.httpServer = null; + console.log('[MCPServer] HTTP server stopped'); + } + this.clients.clear(); + } + getStatus() { + return { + running: !!this.httpServer, + port: this.settings.port, + clients: 0 // HTTP is stateless, no persistent clients + }; + } + updateSettings(settings) { + this.settings = settings; + if (this.httpServer) { + this.stop(); + this.start(); + } + } +} +exports.MCPServer = MCPServer; +// HTTP transport doesn't need persistent connections +// MCP over HTTP uses request-response pattern +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWNwLXNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NvdXJjZS9tY3Atc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDJDQUE2QjtBQUM3Qix5Q0FBMkI7QUFHM0IscURBQWlEO0FBQ2pELG1EQUErQztBQUMvQyw2REFBeUQ7QUFDekQsdURBQW1EO0FBQ25ELHlEQUFxRDtBQUNyRCxxREFBaUQ7QUFDakQsaUVBQTZEO0FBQzdELHVEQUFtRDtBQUNuRCw2REFBeUQ7QUFFekQsTUFBYSxTQUFTO0lBT2xCLFlBQVksUUFBMkI7UUFML0IsZUFBVSxHQUF1QixJQUFJLENBQUM7UUFDdEMsWUFBTyxHQUEyQixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQzVDLFVBQUssR0FBd0IsRUFBRSxDQUFDO1FBQ2hDLGNBQVMsR0FBcUIsRUFBRSxDQUFDO1FBR3JDLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBRU8sZUFBZTtRQUNuQixJQUFJLENBQUM7WUFDRCxPQUFPLENBQUMsR0FBRyxDQUFDLG1DQUFtQyxDQUFDLENBQUM7WUFDakQsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLEdBQUcsSUFBSSx3QkFBVSxFQUFFLENBQUM7WUFDcEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEdBQUcsSUFBSSxzQkFBUyxFQUFFLENBQUM7WUFDbEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLEdBQUcsSUFBSSxnQ0FBYyxFQUFFLENBQUM7WUFDNUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEdBQUcsSUFBSSwwQkFBVyxFQUFFLENBQUM7WUFDdEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEdBQUcsSUFBSSw0QkFBWSxFQUFFLENBQUM7WUFDeEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxL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LFFBQVEsR0FBRyxRQUFRLENBQUM7UUFDekIsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDbEIsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ1osSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ2pCLENBQUM7SUFDTCxDQUFDO0NBQ0o7QUEvT0QsOEJBK09DO0FBRUQscURBQXFEO0FBQ3JELDhDQUE4QyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHAgZnJvbSAnaHR0cCc7XG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcbmltcG9ydCB7IHY0IGFzIHV1aWR2NCB9IGZyb20gJ3V1aWQnO1xuaW1wb3J0IHsgTUNQU2VydmVyU2V0dGluZ3MsIFNlcnZlclN0YXR1cywgTUNQQ2xpZW50LCBUb29sRGVmaW5pdGlvbiB9IGZyb20gJy4vdHlwZXMnO1xuaW1wb3J0IHsgU2NlbmVUb29scyB9IGZyb20gJy4vdG9vbHMvc2NlbmUtdG9vbHMnO1xuaW1wb3J0IHsgTm9kZVRvb2xzIH0gZnJvbSAnLi90b29scy9ub2RlLXRvb2xzJztcbmltcG9ydCB7IENvbXBvbmVudFRvb2xzIH0gZnJvbSAnLi90b29scy9jb21wb25lbnQtdG9vbHMnO1xuaW1wb3J0IHsgUHJlZmFiVG9vbHMgfSBmcm9tICcuL3Rvb2xzL3ByZWZhYi10b29scyc7XG5pbXBvcnQgeyBQcm9qZWN0VG9vbHMgfSBmcm9tICcuL3Rvb2xzL3Byb2plY3QtdG9vbHMnO1xuaW1wb3J0IHsgRGVidWdUb29scyB9IGZyb20gJy4vdG9vbHMvZGVidWctdG9vbHMnO1xuaW1wb3J0IHsgUHJlZmVyZW5jZXNUb29scyB9IGZyb20gJy4vdG9vbHMvcHJlZmVyZW5jZXMtdG9vbHMnO1xuaW1wb3J0IHsgU2VydmVyVG9vbHMgfSBmcm9tICcuL3Rvb2xzL3NlcnZlci10b29scyc7XG5pbXBvcnQgeyBCcm9hZGNhc3RUb29scyB9IGZyb20gJy4vdG9vbHMvYnJvYWRjYXN0LXRvb2xzJztcblxuZXhwb3J0IGNsYXNzIE1DUFNlcnZlciB7XG4gICAgcHJpdmF0ZSBzZXR0aW5nczogTUNQU2VydmVyU2V0dGluZ3M7XG4gICAgcHJpdmF0ZSBodHRwU2VydmVyOiBodHRwLlNlcnZlciB8IG51bGwgPSBudWxsO1xuICAgIHByaXZhdGUgY2xpZW50czogTWFwPHN0cmluZywgTUNQQ2xpZW50PiA9IG5ldyBNYXAoKTtcbiAgICBwcml2YXRlIHRvb2xzOiBSZWNvcmQ8c3RyaW5nLCBhbnk+ID0ge307XG4gICAgcHJpdmF0ZSB0b29sc0xpc3Q6IFRvb2xEZWZpbml0aW9uW10gPSBbXTtcblxuICAgIGNvbnN0cnVjdG9yKHNldHRpbmdzOiBNQ1BTZXJ2ZXJTZXR0aW5ncykge1xuICAgICAgICB0aGlzLnNldHRpbmdzID0gc2V0dGluZ3M7XG4gICAgICAgIHRoaXMuaW5pdGlhbGl6ZVRvb2xzKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBpbml0aWFsaXplVG9vbHMoKTogdm9pZCB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnW01DUFNlcnZlcl0gSW5pdGlhbGl6aW5nIHRvb2xzLi4uJyk7XG4gICAgICAgICAgICB0aGlzLnRvb2xzLnNjZW5lID0gbmV3IFNjZW5lVG9vbHMoKTtcbiAgICAgICAgICAgIHRoaXMudG9vbHMubm9kZSA9IG5ldyBOb2RlVG9vbHMoKTtcbiAgICAgICAgICAgIHRoaXMudG9vbHMuY29tcG9uZW50ID0gbmV3IENvbXBvbmVudFRvb2xzKCk7XG4gICAgICAgICAgICB0aGlzLnRvb2xzLnByZWZhYiA9IG5ldyBQcmVmYWJUb29scygpO1xuICAgICAgICAgICAgdGhpcy50b29scy5wcm9qZWN0ID0gbmV3IFByb2plY3RUb29scygpO1xuICAgICAgICAgICAgdGhpcy50b29scy5kZWJ1ZyA9IG5ldyBEZWJ1Z1Rvb2xzKCk7XG4gICAgICAgICAgICB0aGlzLnRvb2xzLnByZWZlcmVuY2VzID0gbmV3IFByZWZlcmVuY2VzVG9vbHMoKTtcbiAgICAgICAgICAgIHRoaXMudG9vbHMuc2VydmVyID0gbmV3IFNlcnZlclRvb2xzKCk7XG4gICAgICAgICAgICB0aGlzLnRvb2xzLmJyb2FkY2FzdCA9IG5ldyBCcm9hZGNhc3RUb29scygpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1tNQ1BTZXJ2ZXJdIFRvb2xzIGluaXRpYWxpemVkIHN1Y2Nlc3NmdWxseScpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignW01DUFNlcnZlcl0gRXJyb3IgaW5pdGlhbGl6aW5nIHRvb2xzOicsIGVycm9yKTtcbiAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHN0YXJ0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAodGhpcy5odHRwU2VydmVyKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnW01DUFNlcnZlcl0gU2VydmVyIGlzIGFscmVhZHkgcnVubmluZycpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbTUNQU2VydmVyXSBTdGFydGluZyBIVFRQIHNlcnZlciBvbiBwb3J0ICR7dGhpcy5zZXR0aW5ncy5wb3J0fS4uLmApO1xuICAgICAgICAgICAgdGhpcy5odHRwU2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIodGhpcy5oYW5kbGVIdHRwUmVxdWVzdC5iaW5kKHRoaXMpKTtcblxuICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuaHR0cFNlcnZlciEubGlzdGVuKHRoaXMuc2V0dGluZ3MucG9ydCwgJzEyNy4wLjAuMScsICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coYFtNQ1BTZXJ2ZXJdIOKchSBIVFRQIHNlcnZlciBzdGFydGVkIHN1Y2Nlc3NmdWxseSBvbiBodHRwOi8vMTI3LjAuMC4xOiR7dGhpcy5zZXR0aW5ncy5wb3J0fWApO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhgW01DUFNlcnZlcl0gSGVhbHRoIGNoZWNrOiBodHRwOi8vMTI3LjAuMC4xOiR7dGhpcy5zZXR0aW5ncy5wb3J0fS9oZWFsdGhgKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coYFtNQ1BTZXJ2ZXJdIE1DUCBlbmRwb2ludDogaHR0cDovLzEyNy4wLjAuMToke3RoaXMuc2V0dGluZ3MucG9ydH0vbWNwYCk7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB0aGlzLmh0dHBTZXJ2ZXIhLm9uKCdlcnJvcicsIChlcnI6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCdbTUNQU2VydmVyXSDinYwgRmFpbGVkIHRvIHN0YXJ0IHNlcnZlcjonLCBlcnIpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoZXJyLmNvZGUgPT09ICdFQUREUklOVVNFJykge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihgW01DUFNlcnZlcl0gUG9ydCAke3RoaXMuc2V0dGluZ3MucG9ydH0gaXMgYWxyZWFkeSBpbiB1c2UuIFBsZWFzZSBjaGFuZ2UgdGhlIHBvcnQgaW4gc2V0dGluZ3MuYCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KGVycik7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgdGhpcy5zZXR1cFRvb2xzKCk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnW01DUFNlcnZlcl0g8J+agCBNQ1AgU2VydmVyIGlzIHJlYWR5IGZvciBjb25uZWN0aW9ucycpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignW01DUFNlcnZlcl0g4p2MIEZhaWxlZCB0byBzdGFydCBzZXJ2ZXI6JywgZXJyb3IpO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIHNldHVwVG9vbHMoKTogdm9pZCB7XG4gICAgICAgIHRoaXMudG9vbHNMaXN0ID0gW107XG4gICAgICAgIFxuICAgICAgICBmb3IgKGNvbnN0IFtjYXRlZ29yeSwgdG9vbFNldF0gb2YgT2JqZWN0LmVudHJpZXModGhpcy50b29scykpIHtcbiAgICAgICAgICAgIGNvbnN0IHRvb2xzID0gdG9vbFNldC5nZXRUb29scygpO1xuICAgICAgICAgICAgZm9yIChjb25zdCB0b29sIG9mIHRvb2xzKSB7XG4gICAgICAgICAgICAgICAgdGhpcy50b29sc0xpc3QucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6IGAke2NhdGVnb3J5fV8ke3Rvb2wubmFtZX1gLFxuICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogdG9vbC5kZXNjcmlwdGlvbixcbiAgICAgICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHRvb2wuaW5wdXRTY2hlbWFcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIGV4ZWN1dGVUb29sQ2FsbCh0b29sTmFtZTogc3RyaW5nLCBhcmdzOiBhbnkpOiBQcm9taXNlPGFueT4ge1xuICAgICAgICBjb25zdCBwYXJ0cyA9IHRvb2xOYW1lLnNwbGl0KCdfJyk7XG4gICAgICAgIGNvbnN0IGNhdGVnb3J5ID0gcGFydHNbMF07XG4gICAgICAgIGNvbnN0IHRvb2xNZXRob2ROYW1lID0gcGFydHMuc2xpY2UoMSkuam9pbignXycpO1xuICAgICAgICBcbiAgICAgICAgaWYgKHRoaXMudG9vbHNbY2F0ZWdvcnldKSB7XG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy50b29sc1tjYXRlZ29yeV0uZXhlY3V0ZSh0b29sTWV0aG9kTmFtZSwgYXJncyk7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgVG9vbCAke3Rvb2xOYW1lfSBub3QgZm91bmRgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0Q2xpZW50cygpOiBNQ1BDbGllbnRbXSB7XG4gICAgICAgIHJldHVybiBBcnJheS5mcm9tKHRoaXMuY2xpZW50cy52YWx1ZXMoKSk7XG4gICAgfVxuICAgIHB1YmxpYyBnZXRBdmFpbGFibGVUb29scygpOiBUb29sRGVmaW5pdGlvbltdIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudG9vbHNMaXN0O1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRTZXR0aW5ncygpOiBNQ1BTZXJ2ZXJTZXR0aW5ncyB7XG4gICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgaGFuZGxlSHR0cFJlcXVlc3QocmVxOiBodHRwLkluY29taW5nTWVzc2FnZSwgcmVzOiBodHRwLlNlcnZlclJlc3BvbnNlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBhcnNlZFVybCA9IHVybC5wYXJzZShyZXEudXJsIHx8ICcnLCB0cnVlKTtcbiAgICAgICAgY29uc3QgcGF0aG5hbWUgPSBwYXJzZWRVcmwucGF0aG5hbWU7XG4gICAgICAgIFxuICAgICAgICAvLyBTZXQgQ09SUyBoZWFkZXJzXG4gICAgICAgIHJlcy5zZXRIZWFkZXIoJ0FjY2Vzcy1Db250cm9sLUFsbG93LU9yaWdpbicsICcqJyk7XG4gICAgICAgIHJlcy5zZXRIZWFkZXIoJ0FjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHMnLCAnR0VULCBQT1NULCBPUFRJT05TJyk7XG4gICAgICAgIHJlcy5zZXRIZWFkZXIoJ0FjY2Vzcy1Db250cm9sLUFsbG93LUhlYWRlcnMnLCAnQ29udGVudC1UeXBlLCBBdXRob3JpemF0aW9uJyk7XG4gICAgICAgIHJlcy5zZXRIZWFkZXIoJ0NvbnRlbnQtVHlwZScsICdhcHBsaWNhdGlvbi9qc29uJyk7XG4gICAgICAgIFxuICAgICAgICBpZiAocmVxLm1ldGhvZCA9PT0gJ09QVElPTlMnKSB7XG4gICAgICAgICAgICByZXMud3JpdGVIZWFkKDIwMCk7XG4gICAgICAgICAgICByZXMuZW5kKCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBpZiAocGF0aG5hbWUgPT09ICcvbWNwJyAmJiByZXEubWV0aG9kID09PSAnUE9TVCcpIHtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLmhhbmRsZU1DUFJlcXVlc3QocmVxLCByZXMpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChwYXRobmFtZSA9PT0gJy9oZWFsdGgnICYmIHJlcS5tZXRob2QgPT09ICdHRVQnKSB7XG4gICAgICAgICAgICAgICAgcmVzLndyaXRlSGVhZCgyMDApO1xuICAgICAgICAgICAgICAgIHJlcy5lbmQoSlNPTi5zdHJpbmdpZnkoeyBzdGF0dXM6ICdvaycsIHRvb2xzOiB0aGlzLnRvb2xzTGlzdC5sZW5ndGggfSkpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXMud3JpdGVIZWFkKDQwNCk7XG4gICAgICAgICAgICAgICAgcmVzLmVuZChKU09OLnN0cmluZ2lmeSh7IGVycm9yOiAnTm90IGZvdW5kJyB9KSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdIVFRQIHJlcXVlc3QgZXJyb3I6JywgZXJyb3IpO1xuICAgICAgICAgICAgcmVzLndyaXRlSGVhZCg1MDApO1xuICAgICAgICAgICAgcmVzLmVuZChKU09OLnN0cmluZ2lmeSh7IGVycm9yOiAnSW50ZXJuYWwgc2VydmVyIGVycm9yJyB9KSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgcHJpdmF0ZSBhc3luYyBoYW5kbGVNQ1BSZXF1ZXN0KHJlcTogaHR0cC5JbmNvbWluZ01lc3NhZ2UsIHJlczogaHR0cC5TZXJ2ZXJSZXNwb25zZSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBsZXQgYm9keSA9ICcnO1xuICAgICAgICBcbiAgICAgICAgcmVxLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XG4gICAgICAgICAgICBib2R5ICs9IGNodW5rLnRvU3RyaW5nKCk7XG4gICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgcmVxLm9uKCdlbmQnLCBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IG1lc3NhZ2UgPSBKU09OLnBhcnNlKGJvZHkpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5oYW5kbGVNZXNzYWdlKG1lc3NhZ2UpO1xuICAgICAgICAgICAgICAgIHJlcy53cml0ZUhlYWQoMjAwKTtcbiAgICAgICAgICAgICAgICByZXMuZW5kKEpTT04uc3RyaW5naWZ5KHJlc3BvbnNlKSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGhhbmRsaW5nIE1DUCByZXF1ZXN0OicsIGVycm9yKTtcbiAgICAgICAgICAgICAgICByZXMud3JpdGVIZWFkKDQwMCk7XG4gICAgICAgICAgICAgICAgcmVzLmVuZChKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgICAgICAgICAgIGpzb25ycGM6ICcyLjAnLFxuICAgICAgICAgICAgICAgICAgICBpZDogbnVsbCxcbiAgICAgICAgICAgICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvZGU6IC0zMjcwMCxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdQYXJzZSBlcnJvcidcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBoYW5kbGVNZXNzYWdlKG1lc3NhZ2U6IGFueSk6IFByb21pc2U8YW55PiB7XG4gICAgICAgIGNvbnN0IHsgaWQsIG1ldGhvZCwgcGFyYW1zIH0gPSBtZXNzYWdlO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBsZXQgcmVzdWx0OiBhbnk7XG5cbiAgICAgICAgICAgIHN3aXRjaCAobWV0aG9kKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAndG9vbHMvbGlzdCc6XG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IHsgdG9vbHM6IHRoaXMuZ2V0QXZhaWxhYmxlVG9vbHMoKSB9O1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlICd0b29scy9jYWxsJzpcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgeyBuYW1lLCBhcmd1bWVudHM6IGFyZ3MgfSA9IHBhcmFtcztcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgdG9vbFJlc3VsdCA9IGF3YWl0IHRoaXMuZXhlY3V0ZVRvb2xDYWxsKG5hbWUsIGFyZ3MpO1xuICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSB7IGNvbnRlbnQ6IFt7IHR5cGU6ICd0ZXh0JywgdGV4dDogSlNPTi5zdHJpbmdpZnkodG9vbFJlc3VsdCkgfV0gfTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAnaW5pdGlhbGl6ZSc6XG4gICAgICAgICAgICAgICAgICAgIC8vIE1DUCBpbml0aWFsaXphdGlvblxuICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcm90b2NvbFZlcnNpb246ICcyMDI0LTExLTA1JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhcGFiaWxpdGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2xzOiB7fVxuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlcnZlckluZm86IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lOiAnY29jb3MtbWNwLXNlcnZlcicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyc2lvbjogJzEuMC4wJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFVua25vd24gbWV0aG9kOiAke21ldGhvZH1gKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBqc29ucnBjOiAnMi4wJyxcbiAgICAgICAgICAgICAgICBpZCxcbiAgICAgICAgICAgICAgICByZXN1bHRcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAganNvbnJwYzogJzIuMCcsXG4gICAgICAgICAgICAgICAgaWQsXG4gICAgICAgICAgICAgICAgZXJyb3I6IHtcbiAgICAgICAgICAgICAgICAgICAgY29kZTogLTMyNjAzLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBlcnJvci5tZXNzYWdlXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBzdG9wKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5odHRwU2VydmVyKSB7XG4gICAgICAgICAgICB0aGlzLmh0dHBTZXJ2ZXIuY2xvc2UoKTtcbiAgICAgICAgICAgIHRoaXMuaHR0cFNlcnZlciA9IG51bGw7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnW01DUFNlcnZlcl0gSFRUUCBzZXJ2ZXIgc3RvcHBlZCcpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5jbGllbnRzLmNsZWFyKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFN0YXR1cygpOiBTZXJ2ZXJTdGF0dXMge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgcnVubmluZzogISF0aGlzLmh0dHBTZXJ2ZXIsXG4gICAgICAgICAgICBwb3J0OiB0aGlzLnNldHRpbmdzLnBvcnQsXG4gICAgICAgICAgICBjbGllbnRzOiAwIC8vIEhUVFAgaXMgc3RhdGVsZXNzLCBubyBwZXJzaXN0ZW50IGNsaWVudHNcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBwdWJsaWMgdXBkYXRlU2V0dGluZ3Moc2V0dGluZ3M6IE1DUFNlcnZlclNldHRpbmdzKSB7XG4gICAgICAgIHRoaXMuc2V0dGluZ3MgPSBzZXR0aW5ncztcbiAgICAgICAgaWYgKHRoaXMuaHR0cFNlcnZlcikge1xuICAgICAgICAgICAgdGhpcy5zdG9wKCk7XG4gICAgICAgICAgICB0aGlzLnN0YXJ0KCk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbi8vIEhUVFAgdHJhbnNwb3J0IGRvZXNuJ3QgbmVlZCBwZXJzaXN0ZW50IGNvbm5lY3Rpb25zXG4vLyBNQ1Agb3ZlciBIVFRQIHVzZXMgcmVxdWVzdC1yZXNwb25zZSBwYXR0ZXJuIl19 \ No newline at end of file diff --git a/dist/panels/default/index.js b/dist/panels/default/index.js new file mode 100644 index 0000000..9301a88 --- /dev/null +++ b/dist/panels/default/index.js @@ -0,0 +1,225 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const fs_extra_1 = require("fs-extra"); +const path_1 = require("path"); +/** + * @zh 如果希望兼容 3.3 之前的版本可以使用下方的代码 + * @en You can add the code below if you want compatibility with versions prior to 3.3 + */ +// Editor.Panel.define = Editor.Panel.define || function(options: any) { return options } +module.exports = Editor.Panel.define({ + listeners: { + show() { console.log('MCP Server panel shown'); }, + hide() { console.log('MCP Server panel hidden'); } + }, + template: (0, fs_extra_1.readFileSync)((0, path_1.join)(__dirname, '../../../static/template/default/index.html'), 'utf-8'), + style: (0, fs_extra_1.readFileSync)((0, path_1.join)(__dirname, '../../../static/style/default/index.css'), 'utf-8'), + $: { + panelTitle: '#panelTitle', + serverStatusLabel: '#serverStatusLabel', + serverStatusLabelProp: '#serverStatusLabelProp', + serverStatusValue: '#serverStatusValue', + connectedLabel: '#connectedLabel', + connectedClients: '#connectedClients', + toggleServerBtn: '#toggleServerBtn', + settingsLabel: '#settingsLabel', + portLabel: '#portLabel', + autoStartLabel: '#autoStartLabel', + debugLogLabel: '#debugLogLabel', + maxConnectionsLabel: '#maxConnectionsLabel', + connectionInfoLabel: '#connectionInfoLabel', + httpUrlLabel: '#httpUrlLabel', + httpUrlInput: '#httpUrlInput', + copyBtn: '#copyBtn', + saveSettingsBtn: '#saveSettingsBtn', + // 新增输入控件id + portInput: '#portInput', + maxConnInput: '#maxConnInput', + autoStartInput: '#autoStartInput', + debugLogInput: '#debugLogInput', + }, + methods: { + async updateServerStatus() { + try { + const status = await Editor.Message.request('cocos-mcp-server', 'get-server-status'); + this.serverRunning = status.running; + this.connectedClients = status.clients || 0; + this.serverStatus = this.serverRunning ? + Editor.I18n.t('cocos-mcp-server.connected') : + Editor.I18n.t('cocos-mcp-server.disconnected'); + this.statusClass = this.serverRunning ? 'running' : 'stopped'; + this.buttonText = this.serverRunning ? + Editor.I18n.t('cocos-mcp-server.stop_server') : + Editor.I18n.t('cocos-mcp-server.start_server'); + // 刷新UI + this.$.serverStatusValue.innerText = this.serverStatus; + this.$.connectedClients.innerText = this.connectedClients; + this.$.toggleServerBtn.innerText = this.buttonText; + if (this.serverRunning) { + this.httpUrl = `http://localhost:${this.settings.port}/mcp`; + this.$.httpUrlInput.value = this.httpUrl; + } + else { + this.httpUrl = ''; + this.$.httpUrlInput.value = ''; + } + } + catch (err) { + console.error('Failed to update server status:', err); + } + }, + async toggleServer() { + this.isProcessing = true; + try { + if (this.serverRunning) { + await this.stopServer(); + } + else { + await this.startServer(); + } + } + finally { + this.isProcessing = false; + } + }, + async startServer() { + try { + await Editor.Message.request('cocos-mcp-server', 'start-server'); + Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.server_started'), { + detail: Editor.I18n.t('cocos-mcp-server.server_running').replace('{0}', this.settings.port.toString()) + }); + await this.updateServerStatus(); + } + catch (err) { + Editor.Dialog.error(Editor.I18n.t('cocos-mcp-server.failed_to_start'), err.message); + } + }, + async stopServer() { + try { + await Editor.Message.request('cocos-mcp-server', 'stop-server'); + Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.server_stopped_msg'), { + detail: Editor.I18n.t('cocos-mcp-server.server_stopped') + }); + await this.updateServerStatus(); + } + catch (err) { + Editor.Dialog.error(Editor.I18n.t('cocos-mcp-server.failed_to_stop'), err.message); + } + }, + async saveSettings() { + try { + // 直接用 this.$ 获取所有输入控件的当前值 + const port = this.$.portInput ? Number(this.$.portInput.value) : 3000; + const maxConnections = this.$.maxConnInput ? Number(this.$.maxConnInput.value) : 10; + const autoStart = this.$.autoStartInput ? !!this.$.autoStartInput.checked : false; + const enableDebugLog = this.$.debugLogInput ? !!this.$.debugLogInput.checked : false; + // 组装 settings + const settings = Object.assign(Object.assign({}, this.settings), { port, + maxConnections, + autoStart, + enableDebugLog }); + await Editor.Message.request('cocos-mcp-server', 'update-settings', settings); + // 重新拉取设置 + const newSettings = await Editor.Message.request('cocos-mcp-server', 'get-server-settings'); + this.settings = newSettings; + this.originalSettings = JSON.stringify(newSettings); + Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.settings_saved')); + } + catch (err) { + Editor.Dialog.error(Editor.I18n.t('cocos-mcp-server.failed_to_save'), err.message); + } + }, + copyUrl() { + Editor.Clipboard.write('text', this.httpUrl); + Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.url_copied')); + }, + settingsChanged() { + return JSON.stringify(this.settings) !== this.originalSettings; + }, + bindSettingsEvents() { + // 端口输入框 + const portInput = document.querySelectorAll('ui-num-input[slot="content"]')[0]; + if (portInput) { + portInput.addEventListener('change', (e) => { + this.settings.port = Number(e.detail.value); + }); + } + // 最大连接数 + const maxConnInput = document.querySelectorAll('ui-num-input[slot="content"]')[1]; + if (maxConnInput) { + maxConnInput.addEventListener('change', (e) => { + this.settings.maxConnections = Number(e.detail.value); + }); + } + // 复选框 + const checkboxes = document.querySelectorAll('ui-checkbox[slot="content"]'); + if (checkboxes && checkboxes.length >= 2) { + checkboxes[0].addEventListener('change', (e) => { + this.settings.autoStart = !!e.detail.value; + }); + checkboxes[1].addEventListener('change', (e) => { + this.settings.enableDebugLog = !!e.detail.value; + }); + } + }, + }, + ready() { + Editor.Message.request('cocos-mcp-server', 'get-server-settings').then((settings) => { + this.settings = settings; + this.originalSettings = JSON.stringify(settings); + // 本地化label赋值 + this.$.panelTitle.innerText = Editor.I18n.t('cocos-mcp-server.panel_title'); + this.$.serverStatusLabel.innerText = Editor.I18n.t('cocos-mcp-server.server_status'); + this.$.serverStatusLabelProp.innerText = Editor.I18n.t('cocos-mcp-server.server_status'); + this.$.connectedLabel.innerText = Editor.I18n.t('cocos-mcp-server.connected'); + this.$.settingsLabel.innerText = Editor.I18n.t('cocos-mcp-server.settings'); + this.$.portLabel.innerText = Editor.I18n.t('cocos-mcp-server.port'); + this.$.autoStartLabel.innerText = Editor.I18n.t('cocos-mcp-server.auto_start'); + this.$.debugLogLabel.innerText = Editor.I18n.t('cocos-mcp-server.debug_log'); + this.$.maxConnectionsLabel.innerText = Editor.I18n.t('cocos-mcp-server.max_connections'); + this.$.connectionInfoLabel.innerText = Editor.I18n.t('cocos-mcp-server.connection_info'); + this.$.httpUrlLabel.innerText = Editor.I18n.t('cocos-mcp-server.http_url'); + this.$.copyBtn.innerText = Editor.I18n.t('cocos-mcp-server.copy'); + this.$.saveSettingsBtn.innerText = Editor.I18n.t('cocos-mcp-server.save_settings'); + // 动态内容初始化 + this.$.serverStatusValue.innerText = ''; + this.$.connectedClients.innerText = ''; + this.$.toggleServerBtn.innerText = ''; + this.$.httpUrlInput.value = ''; + // 绑定按钮事件 + this.$.toggleServerBtn.addEventListener('confirm', this.toggleServer.bind(this)); + this.$.saveSettingsBtn.addEventListener('confirm', this.saveSettings.bind(this)); + this.$.copyBtn.addEventListener('confirm', this.copyUrl.bind(this)); + // 延迟绑定事件,确保 UI 组件已渲染 + setTimeout(() => { + this.bindSettingsEvents(); + }, 100); + // Set up periodic status updates + this.statusInterval = setInterval(() => { + this.updateServerStatus(); + }, 2000); + // 不再自动启动服务器,用户点击才启动 + this.updateServerStatus(); + }); + }, + beforeClose() { + if (this.statusInterval) { + clearInterval(this.statusInterval); + } + }, + close() { + // Panel close cleanup + }, + // Direct properties for data access + serverRunning: false, + connectedClients: 0, + serverStatus: '', + statusClass: 'stopped', + buttonText: '', + isProcessing: false, + settings: {}, + httpUrl: '', + statusInterval: null, + originalSettings: '' +}); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zb3VyY2UvcGFuZWxzL2RlZmF1bHQvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFDQSx1Q0FBd0M7QUFDeEMsK0JBQTRCO0FBRTVCOzs7R0FHRztBQUNILHlGQUF5RjtBQUV6RixNQUFNLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQ2pDLFNBQVMsRUFBRTtRQUNQLElBQUksS0FBSyxPQUFPLENBQUMsR0FBRyxDQUFDLHdCQUF3QixDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2pELElBQUksS0FBSyxPQUFPLENBQUMsR0FBRyxDQUFDLHlCQUF5QixDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQ3JEO0lBQ0QsUUFBUSxFQUFFLElBQUEsdUJBQVksRUFBQyxJQUFBLFdBQUksRUFBQyxTQUFTLEVBQUUsNkNBQTZDLENBQUMsRUFBRSxPQUFPLENBQUM7SUFDL0YsS0FBSyxFQUFFLElBQUEsdUJBQVksRUFBQyxJQUFBLFdBQUksRUFBQyxTQUFTLEVBQUUseUNBQXlDLENBQUMsRUFBRSxPQUFPLENBQUM7SUFDeEYsQ0FBQyxFQUFFO1FBQ0MsVUFBVSxFQUFFLGFBQWE7UUFDekIsaUJBQWlCLEVBQUUsb0JBQW9CO1FBQ3ZDLHFCQUFxQixFQUFFLHdCQUF3QjtRQUMvQyxpQkFBaUIsRUFBRSxvQkFBb0I7UUFDdkMsY0FBYyxFQUFFLGlCQUFpQjtRQUNqQyxnQkFBZ0IsRUFBRSxtQkFBbUI7UUFDckMsZUFBZSxFQUFFLGtCQUFrQjtRQUNuQyxhQUFhLEVBQUUsZ0JBQWdCO1FBQy9CLFNBQVMsRUFBRSxZQUFZO1FBQ3ZCLGNBQWMsRUFBRSxpQkFBaUI7UUFDakMsYUFBYSxFQUFFLGdCQUFnQjtRQUMvQixtQkFBbUIsRUFBRSxzQkFBc0I7UUFDM0MsbUJBQW1CLEVBQUUsc0JBQXNCO1FBQzNDLFlBQVksRUFBRSxlQUFlO1FBQzdCLFlBQVksRUFBRSxlQUFlO1FBQzdCLE9BQU8sRUFBRSxVQUFVO1FBQ25CLGVBQWUsRUFBRSxrQkFBa0I7UUFDbkMsV0FBVztRQUNYLFNBQVMsRUFBRSxZQUFZO1FBQ3ZCLFlBQVksRUFBRSxlQUFlO1FBQzdCLGNBQWMsRUFBRSxpQkFBaUI7UUFDakMsYUFBYSxFQUFFLGdCQUFnQjtLQUNsQztJQUNELE9BQU8sRUFBRTtRQUNMLEtBQUssQ0FBQyxrQkFBa0I7WUFDcEIsSUFBSSxDQUFDO2dCQUNELE1BQU0sTUFBTSxHQUFHLE1BQU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsa0JBQWtCLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztnQkFDckYsSUFBSSxDQUFDLGFBQWEsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDO2dCQUNwQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsTUFBTSxDQUFDLE9BQU8sSUFBSSxDQUFDLENBQUM7Z0JBQzVDLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDO29CQUNwQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDLENBQUM7b0JBQzdDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLCtCQUErQixDQUFDLENBQUM7Z0JBQ25ELElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7Z0JBQzlELElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDO29CQUNsQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDLENBQUM7b0JBQy9DLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLCtCQUErQixDQUFDLENBQUM7Z0JBQ25ELE9BQU87Z0JBQ1AsSUFBSSxDQUFDLENBQUMsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQztnQkFDdkQsSUFBSSxDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDO2dCQUMxRCxJQUFJLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztnQkFDbkQsSUFBSSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7b0JBQ3JCLElBQUksQ0FBQyxPQUFPLEdBQUcsb0JBQW9CLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxNQUFNLENBQUM7b0JBQzVELElBQUksQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDO2dCQUM3QyxDQUFDO3FCQUFNLENBQUM7b0JBQ0osSUFBSSxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7b0JBQ2xCLElBQUksQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7Z0JBQ25DLENBQUM7WUFDTCxDQUFDO1lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDWCxPQUFPLENBQUMsS0FBSyxDQUFDLGlDQUFpQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQzFELENBQUM7UUFDTCxDQUFDO1FBRUQsS0FBSyxDQUFDLFlBQVk7WUFDZCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQztZQUN6QixJQUFJLENBQUM7Z0JBQ0QsSUFBSSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7b0JBQ3JCLE1BQU0sSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUM1QixDQUFDO3FCQUFNLENBQUM7b0JBQ0osTUFBTSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7Z0JBQzdCLENBQUM7WUFDTCxDQUFDO29CQUFTLENBQUM7Z0JBQ1AsSUFBSSxDQUFDLFlBQVksR0FBRyxLQUFLLENBQUM7WUFDOUIsQ0FBQztRQUNMLENBQUM7UUFFRCxLQUFLLENBQUMsV0FBVztZQUNiLElBQUksQ0FBQztnQkFDRCxNQUFNLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLGtCQUFrQixFQUFFLGNBQWMsQ0FBQyxDQUFDO2dCQUNqRSxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxpQ0FBaUMsQ0FBQyxFQUFFO29CQUNqRSxNQUFNLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsaUNBQWlDLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO2lCQUN6RyxDQUFDLENBQUM7Z0JBQ0gsTUFBTSxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztZQUNwQyxDQUFDO1lBQUMsT0FBTyxHQUFRLEVBQUUsQ0FBQztnQkFDaEIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsa0NBQWtDLENBQUMsRUFBRSxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDeEYsQ0FBQztRQUNMLENBQUM7UUFFRCxLQUFLLENBQUMsVUFBVTtZQUNaLElBQUksQ0FBQztnQkFDRCxNQUFNLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLGtCQUFrQixFQUFFLGFBQWEsQ0FBQyxDQUFDO2dCQUNoRSxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxxQ0FBcUMsQ0FBQyxFQUFFO29CQUNyRSxNQUFNLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsaUNBQWlDLENBQUM7aUJBQzNELENBQUMsQ0FBQztnQkFDSCxNQUFNLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQ3BDLENBQUM7WUFBQyxPQUFPLEdBQVEsRUFBRSxDQUFDO2dCQUNoQixNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxpQ0FBaUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUN2RixDQUFDO1FBQ0wsQ0FBQztRQUVELEtBQUssQ0FBQyxZQUFZO1lBQ2QsSUFBSSxDQUFDO2dCQUNELDBCQUEwQjtnQkFDMUIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLFNBQWlCLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztnQkFDL0UsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLFlBQW9CLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDN0YsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLGNBQXNCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7Z0JBQzNGLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxhQUFxQixDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDO2dCQUM5RixjQUFjO2dCQUNkLE1BQU0sUUFBUSxtQ0FDUCxJQUFJLENBQUMsUUFBUSxLQUNoQixJQUFJO29CQUNKLGNBQWM7b0JBQ2QsU0FBUztvQkFDVCxjQUFjLEdBQ2pCLENBQUM7Z0JBQ0YsTUFBTSxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsRUFBRSxpQkFBaUIsRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDOUUsU0FBUztnQkFDVCxNQUFNLFdBQVcsR0FBRyxNQUFNLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLGtCQUFrQixFQUFFLHFCQUFxQixDQUFDLENBQUM7Z0JBQzVGLElBQUksQ0FBQyxRQUFRLEdBQUcsV0FBVyxDQUFDO2dCQUM1QixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDcEQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsaUNBQWlDLENBQUMsQ0FBQyxDQUFDO1lBQ3pFLENBQUM7WUFBQyxPQUFPLEdBQVEsRUFBRSxDQUFDO2dCQUNoQixNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxpQ0FBaUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUN2RixDQUFDO1FBQ0wsQ0FBQztRQUVELE9BQU87WUFDSCxNQUFNLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQzdDLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLDZCQUE2QixDQUFDLENBQUMsQ0FBQztRQUNyRSxDQUFDO1FBRUQsZUFBZTtZQUNYLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssSUFBSSxDQUFDLGdCQUFnQixDQUFDO1FBQ25FLENBQUM7UUFDRCxrQkFBa0I7WUFDZCxRQUFRO1lBQ1IsTUFBTSxTQUFTLEdBQUcsUUFBUSxDQUFDLGdCQUFnQixDQUFDLDhCQUE4QixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDL0UsSUFBSSxTQUFTLEVBQUUsQ0FBQztnQkFDWixTQUFTLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBTSxFQUFFLEVBQUU7b0JBQzVDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUNoRCxDQUFDLENBQUMsQ0FBQztZQUNQLENBQUM7WUFDRCxRQUFRO1lBQ1IsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLGdCQUFnQixDQUFDLDhCQUE4QixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDbEYsSUFBSSxZQUFZLEVBQUUsQ0FBQztnQkFDZixZQUFZLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBTSxFQUFFLEVBQUU7b0JBQy9DLElBQUksQ0FBQyxRQUFRLENBQUMsY0FBYyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUMxRCxDQUFDLENBQUMsQ0FBQztZQUNQLENBQUM7WUFDRCxNQUFNO1lBQ04sTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLGdCQUFnQixDQUFDLDZCQUE2QixDQUFDLENBQUM7WUFDNUUsSUFBSSxVQUFVLElBQUksVUFBVSxDQUFDLE1BQU0sSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDdkMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxDQUFDLENBQU0sRUFBRSxFQUFFO29CQUNoRCxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7Z0JBQy9DLENBQUMsQ0FBQyxDQUFDO2dCQUNILFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFNLEVBQUUsRUFBRTtvQkFDaEQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxjQUFjLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDO2dCQUNwRCxDQUFDLENBQUMsQ0FBQztZQUNQLENBQUM7UUFDTCxDQUFDO0tBRUo7SUFDRCxLQUFLO1FBQ0QsTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsa0JBQWtCLEVBQUUscUJBQXFCLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRTtZQUNoRixJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztZQUN6QixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNqRCxhQUFhO1lBQ2IsSUFBSSxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLDhCQUE4QixDQUFDLENBQUM7WUFDNUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsZ0NBQWdDLENBQUMsQ0FBQztZQUNyRixJQUFJLENBQUMsQ0FBQyxDQUFDLHFCQUFxQixDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDO1lBQ3pGLElBQUksQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO1lBQzlFLElBQUksQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQywyQkFBMkIsQ0FBQyxDQUFDO1lBQzVFLElBQUksQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO1lBQ3BFLElBQUksQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO1lBQy9FLElBQUksQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO1lBQzdFLElBQUksQ0FBQyxDQUFDLENBQUMsbUJBQW1CLENBQUMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7WUFDekYsSUFBSSxDQUFDLENBQUMsQ0FBQyxtQkFBbUIsQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsa0NBQWtDLENBQUMsQ0FBQztZQUN6RixJQUFJLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsMkJBQTJCLENBQUMsQ0FBQztZQUMzRSxJQUFJLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsdUJBQXVCLENBQUMsQ0FBQztZQUNsRSxJQUFJLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsZ0NBQWdDLENBQUMsQ0FBQztZQUNuRixVQUFVO1lBQ1YsSUFBSSxDQUFDLENBQUMsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDO1lBQ3hDLElBQUksQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxHQUFHLEVBQUUsQ0FBQztZQUN2QyxJQUFJLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7WUFDL0IsU0FBUztZQUNULElBQUksQ0FBQyxDQUFDLENBQUMsZUFBZSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ2pGLElBQUksQ0FBQyxDQUFDLENBQUMsZUFBZSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ2pGLElBQUksQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3BFLHFCQUFxQjtZQUNyQixVQUFVLENBQUMsR0FBRyxFQUFFO2dCQUNaLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQzlCLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUNSLGlDQUFpQztZQUNoQyxJQUFZLENBQUMsY0FBYyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUU7Z0JBQzNDLElBQVksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQ3ZDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUNULG9CQUFvQjtZQUNuQixJQUFZLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUN2QyxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFDRCxXQUFXO1FBQ1AsSUFBSyxJQUFZLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDL0IsYUFBYSxDQUFFLElBQVksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUNoRCxDQUFDO0lBQ0wsQ0FBQztJQUNELEtBQUs7UUFDRCxzQkFBc0I7SUFDMUIsQ0FBQztJQUVELG9DQUFvQztJQUNwQyxhQUFhLEVBQUUsS0FBSztJQUNwQixnQkFBZ0IsRUFBRSxDQUFDO0lBQ25CLFlBQVksRUFBRSxFQUFFO0lBQ2hCLFdBQVcsRUFBRSxTQUFTO0lBQ3RCLFVBQVUsRUFBRSxFQUFFO0lBQ2QsWUFBWSxFQUFFLEtBQUs7SUFDbkIsUUFBUSxFQUFFLEVBQUU7SUFDWixPQUFPLEVBQUUsRUFBRTtJQUNYLGNBQWMsRUFBRSxJQUFXO0lBQzNCLGdCQUFnQixFQUFFLEVBQUU7Q0FDaEIsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgcmVhZFNldHRpbmdzIH0gZnJvbSAnLi4vLi4vc2V0dGluZ3MnO1xuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMtZXh0cmEnO1xuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xuXG4vKipcbiAqIEB6aCDlpoLmnpzluIzmnJvlhbzlrrkgMy4zIOS5i+WJjeeahOeJiOacrOWPr+S7peS9v+eUqOS4i+aWueeahOS7o+eggVxuICogQGVuIFlvdSBjYW4gYWRkIHRoZSBjb2RlIGJlbG93IGlmIHlvdSB3YW50IGNvbXBhdGliaWxpdHkgd2l0aCB2ZXJzaW9ucyBwcmlvciB0byAzLjNcbiAqL1xuLy8gRWRpdG9yLlBhbmVsLmRlZmluZSA9IEVkaXRvci5QYW5lbC5kZWZpbmUgfHwgZnVuY3Rpb24ob3B0aW9uczogYW55KSB7IHJldHVybiBvcHRpb25zIH1cblxubW9kdWxlLmV4cG9ydHMgPSBFZGl0b3IuUGFuZWwuZGVmaW5lKHtcbiAgICBsaXN0ZW5lcnM6IHtcbiAgICAgICAgc2hvdygpIHsgY29uc29sZS5sb2coJ01DUCBTZXJ2ZXIgcGFuZWwgc2hvd24nKTsgfSxcbiAgICAgICAgaGlkZSgpIHsgY29uc29sZS5sb2coJ01DUCBTZXJ2ZXIgcGFuZWwgaGlkZGVuJyk7IH1cbiAgICB9LFxuICAgIHRlbXBsYXRlOiByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICcuLi8uLi8uLi9zdGF0aWMvdGVtcGxhdGUvZGVmYXVsdC9pbmRleC5odG1sJyksICd1dGYtOCcpLFxuICAgIHN0eWxlOiByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICcuLi8uLi8uLi9zdGF0aWMvc3R5bGUvZGVmYXVsdC9pbmRleC5jc3MnKSwgJ3V0Zi04JyksXG4gICAgJDoge1xuICAgICAgICBwYW5lbFRpdGxlOiAnI3BhbmVsVGl0bGUnLFxuICAgICAgICBzZXJ2ZXJTdGF0dXNMYWJlbDogJyNzZXJ2ZXJTdGF0dXNMYWJlbCcsXG4gICAgICAgIHNlcnZlclN0YXR1c0xhYmVsUHJvcDogJyNzZXJ2ZXJTdGF0dXNMYWJlbFByb3AnLFxuICAgICAgICBzZXJ2ZXJTdGF0dXNWYWx1ZTogJyNzZXJ2ZXJTdGF0dXNWYWx1ZScsXG4gICAgICAgIGNvbm5lY3RlZExhYmVsOiAnI2Nvbm5lY3RlZExhYmVsJyxcbiAgICAgICAgY29ubmVjdGVkQ2xpZW50czogJyNjb25uZWN0ZWRDbGllbnRzJyxcbiAgICAgICAgdG9nZ2xlU2VydmVyQnRuOiAnI3RvZ2dsZVNlcnZlckJ0bicsXG4gICAgICAgIHNldHRpbmdzTGFiZWw6ICcjc2V0dGluZ3NMYWJlbCcsXG4gICAgICAgIHBvcnRMYWJlbDogJyNwb3J0TGFiZWwnLFxuICAgICAgICBhdXRvU3RhcnRMYWJlbDogJyNhdXRvU3RhcnRMYWJlbCcsXG4gICAgICAgIGRlYnVnTG9nTGFiZWw6ICcjZGVidWdMb2dMYWJlbCcsXG4gICAgICAgIG1heENvbm5lY3Rpb25zTGFiZWw6ICcjbWF4Q29ubmVjdGlvbnNMYWJlbCcsXG4gICAgICAgIGNvbm5lY3Rpb25JbmZvTGFiZWw6ICcjY29ubmVjdGlvbkluZm9MYWJlbCcsXG4gICAgICAgIGh0dHBVcmxMYWJlbDogJyNodHRwVXJsTGFiZWwnLFxuICAgICAgICBodHRwVXJsSW5wdXQ6ICcjaHR0cFVybElucHV0JyxcbiAgICAgICAgY29weUJ0bjogJyNjb3B5QnRuJyxcbiAgICAgICAgc2F2ZVNldHRpbmdzQnRuOiAnI3NhdmVTZXR0aW5nc0J0bicsXG4gICAgICAgIC8vIOaWsOWinui+k+WFpeaOp+S7tmlkXG4gICAgICAgIHBvcnRJbnB1dDogJyNwb3J0SW5wdXQnLFxuICAgICAgICBtYXhDb25uSW5wdXQ6ICcjbWF4Q29ubklucHV0JyxcbiAgICAgICAgYXV0b1N0YXJ0SW5wdXQ6ICcjYXV0b1N0YXJ0SW5wdXQnLFxuICAgICAgICBkZWJ1Z0xvZ0lucHV0OiAnI2RlYnVnTG9nSW5wdXQnLFxuICAgIH0sXG4gICAgbWV0aG9kczoge1xuICAgICAgICBhc3luYyB1cGRhdGVTZXJ2ZXJTdGF0dXModGhpczogYW55KSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHN0YXR1cyA9IGF3YWl0IEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2NvY29zLW1jcC1zZXJ2ZXInLCAnZ2V0LXNlcnZlci1zdGF0dXMnKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNlcnZlclJ1bm5pbmcgPSBzdGF0dXMucnVubmluZztcbiAgICAgICAgICAgICAgICB0aGlzLmNvbm5lY3RlZENsaWVudHMgPSBzdGF0dXMuY2xpZW50cyB8fCAwO1xuICAgICAgICAgICAgICAgIHRoaXMuc2VydmVyU3RhdHVzID0gdGhpcy5zZXJ2ZXJSdW5uaW5nID8gXG4gICAgICAgICAgICAgICAgICAgIEVkaXRvci5JMThuLnQoJ2NvY29zLW1jcC1zZXJ2ZXIuY29ubmVjdGVkJykgOiBcbiAgICAgICAgICAgICAgICAgICAgRWRpdG9yLkkxOG4udCgnY29jb3MtbWNwLXNlcnZlci5kaXNjb25uZWN0ZWQnKTtcbiAgICAgICAgICAgICAgICB0aGlzLnN0YXR1c0NsYXNzID0gdGhpcy5zZXJ2ZXJSdW5uaW5nID8gJ3J1bm5pbmcnIDogJ3N0b3BwZWQnO1xuICAgICAgICAgICAgICAgIHRoaXMuYnV0dG9uVGV4dCA9IHRoaXMuc2VydmVyUnVubmluZyA/IFxuICAgICAgICAgICAgICAgICAgICBFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLnN0b3Bfc2VydmVyJykgOiBcbiAgICAgICAgICAgICAgICAgICAgRWRpdG9yLkkxOG4udCgnY29jb3MtbWNwLXNlcnZlci5zdGFydF9zZXJ2ZXInKTtcbiAgICAgICAgICAgICAgICAvLyDliLfmlrBVSVxuICAgICAgICAgICAgICAgIHRoaXMuJC5zZXJ2ZXJTdGF0dXNWYWx1ZS5pbm5lclRleHQgPSB0aGlzLnNlcnZlclN0YXR1cztcbiAgICAgICAgICAgICAgICB0aGlzLiQuY29ubmVjdGVkQ2xpZW50cy5pbm5lclRleHQgPSB0aGlzLmNvbm5lY3RlZENsaWVudHM7XG4gICAgICAgICAgICAgICAgdGhpcy4kLnRvZ2dsZVNlcnZlckJ0bi5pbm5lclRleHQgPSB0aGlzLmJ1dHRvblRleHQ7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuc2VydmVyUnVubmluZykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmh0dHBVcmwgPSBgaHR0cDovL2xvY2FsaG9zdDoke3RoaXMuc2V0dGluZ3MucG9ydH0vbWNwYDtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy4kLmh0dHBVcmxJbnB1dC52YWx1ZSA9IHRoaXMuaHR0cFVybDtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmh0dHBVcmwgPSAnJztcbiAgICAgICAgICAgICAgICAgICAgdGhpcy4kLmh0dHBVcmxJbnB1dC52YWx1ZSA9ICcnO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byB1cGRhdGUgc2VydmVyIHN0YXR1czonLCBlcnIpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuXG4gICAgICAgIGFzeW5jIHRvZ2dsZVNlcnZlcih0aGlzOiBhbnkpIHtcbiAgICAgICAgICAgIHRoaXMuaXNQcm9jZXNzaW5nID0gdHJ1ZTtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuc2VydmVyUnVubmluZykge1xuICAgICAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnN0b3BTZXJ2ZXIoKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnN0YXJ0U2VydmVyKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgICAgICB0aGlzLmlzUHJvY2Vzc2luZyA9IGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuXG4gICAgICAgIGFzeW5jIHN0YXJ0U2VydmVyKHRoaXM6IGFueSkge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBhd2FpdCBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdjb2Nvcy1tY3Atc2VydmVyJywgJ3N0YXJ0LXNlcnZlcicpO1xuICAgICAgICAgICAgICAgIEVkaXRvci5EaWFsb2cuaW5mbyhFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLnNlcnZlcl9zdGFydGVkJyksIHtcbiAgICAgICAgICAgICAgICAgICAgZGV0YWlsOiBFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLnNlcnZlcl9ydW5uaW5nJykucmVwbGFjZSgnezB9JywgdGhpcy5zZXR0aW5ncy5wb3J0LnRvU3RyaW5nKCkpXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy51cGRhdGVTZXJ2ZXJTdGF0dXMoKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgICAgICAgICAgRWRpdG9yLkRpYWxvZy5lcnJvcihFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLmZhaWxlZF90b19zdGFydCcpLCBlcnIubWVzc2FnZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG5cbiAgICAgICAgYXN5bmMgc3RvcFNlcnZlcih0aGlzOiBhbnkpIHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnY29jb3MtbWNwLXNlcnZlcicsICdzdG9wLXNlcnZlcicpO1xuICAgICAgICAgICAgICAgIEVkaXRvci5EaWFsb2cuaW5mbyhFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLnNlcnZlcl9zdG9wcGVkX21zZycpLCB7XG4gICAgICAgICAgICAgICAgICAgIGRldGFpbDogRWRpdG9yLkkxOG4udCgnY29jb3MtbWNwLXNlcnZlci5zZXJ2ZXJfc3RvcHBlZCcpXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy51cGRhdGVTZXJ2ZXJTdGF0dXMoKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgICAgICAgICAgRWRpdG9yLkRpYWxvZy5lcnJvcihFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLmZhaWxlZF90b19zdG9wJyksIGVyci5tZXNzYWdlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSxcblxuICAgICAgICBhc3luYyBzYXZlU2V0dGluZ3ModGhpczogYW55KSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIC8vIOebtOaOpeeUqCB0aGlzLiQg6I635Y+W5omA5pyJ6L6T5YWl5o6n5Lu255qE5b2T5YmN5YC8XG4gICAgICAgICAgICAgICAgY29uc3QgcG9ydCA9IHRoaXMuJC5wb3J0SW5wdXQgPyBOdW1iZXIoKHRoaXMuJC5wb3J0SW5wdXQgYXMgYW55KS52YWx1ZSkgOiAzMDAwO1xuICAgICAgICAgICAgICAgIGNvbnN0IG1heENvbm5lY3Rpb25zID0gdGhpcy4kLm1heENvbm5JbnB1dCA/IE51bWJlcigodGhpcy4kLm1heENvbm5JbnB1dCBhcyBhbnkpLnZhbHVlKSA6IDEwO1xuICAgICAgICAgICAgICAgIGNvbnN0IGF1dG9TdGFydCA9IHRoaXMuJC5hdXRvU3RhcnRJbnB1dCA/ICEhKHRoaXMuJC5hdXRvU3RhcnRJbnB1dCBhcyBhbnkpLmNoZWNrZWQgOiBmYWxzZTtcbiAgICAgICAgICAgICAgICBjb25zdCBlbmFibGVEZWJ1Z0xvZyA9IHRoaXMuJC5kZWJ1Z0xvZ0lucHV0ID8gISEodGhpcy4kLmRlYnVnTG9nSW5wdXQgYXMgYW55KS5jaGVja2VkIDogZmFsc2U7XG4gICAgICAgICAgICAgICAgLy8g57uE6KOFIHNldHRpbmdzXG4gICAgICAgICAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSB7XG4gICAgICAgICAgICAgICAgICAgIC4uLnRoaXMuc2V0dGluZ3MsXG4gICAgICAgICAgICAgICAgICAgIHBvcnQsXG4gICAgICAgICAgICAgICAgICAgIG1heENvbm5lY3Rpb25zLFxuICAgICAgICAgICAgICAgICAgICBhdXRvU3RhcnQsXG4gICAgICAgICAgICAgICAgICAgIGVuYWJsZURlYnVnTG9nLFxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgYXdhaXQgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnY29jb3MtbWNwLXNlcnZlcicsICd1cGRhdGUtc2V0dGluZ3MnLCBzZXR0aW5ncyk7XG4gICAgICAgICAgICAgICAgLy8g6YeN5paw5ouJ5Y+W6K6+572uXG4gICAgICAgICAgICAgICAgY29uc3QgbmV3U2V0dGluZ3MgPSBhd2FpdCBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdjb2Nvcy1tY3Atc2VydmVyJywgJ2dldC1zZXJ2ZXItc2V0dGluZ3MnKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNldHRpbmdzID0gbmV3U2V0dGluZ3M7XG4gICAgICAgICAgICAgICAgdGhpcy5vcmlnaW5hbFNldHRpbmdzID0gSlNPTi5zdHJpbmdpZnkobmV3U2V0dGluZ3MpO1xuICAgICAgICAgICAgICAgIEVkaXRvci5EaWFsb2cuaW5mbyhFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLnNldHRpbmdzX3NhdmVkJykpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgICAgICAgICBFZGl0b3IuRGlhbG9nLmVycm9yKEVkaXRvci5JMThuLnQoJ2NvY29zLW1jcC1zZXJ2ZXIuZmFpbGVkX3RvX3NhdmUnKSwgZXJyLm1lc3NhZ2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuXG4gICAgICAgIGNvcHlVcmwodGhpczogYW55KSB7XG4gICAgICAgICAgICBFZGl0b3IuQ2xpcGJvYXJkLndyaXRlKCd0ZXh0JywgdGhpcy5odHRwVXJsKTtcbiAgICAgICAgICAgIEVkaXRvci5EaWFsb2cuaW5mbyhFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLnVybF9jb3BpZWQnKSk7XG4gICAgICAgIH0sXG5cbiAgICAgICAgc2V0dGluZ3NDaGFuZ2VkKHRoaXM6IGFueSkge1xuICAgICAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHRoaXMuc2V0dGluZ3MpICE9PSB0aGlzLm9yaWdpbmFsU2V0dGluZ3M7XG4gICAgICAgIH0sXG4gICAgICAgIGJpbmRTZXR0aW5nc0V2ZW50cyh0aGlzOiBhbnkpIHtcbiAgICAgICAgICAgIC8vIOerr+WPo+i+k+WFpeahhlxuICAgICAgICAgICAgY29uc3QgcG9ydElucHV0ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgndWktbnVtLWlucHV0W3Nsb3Q9XCJjb250ZW50XCJdJylbMF07XG4gICAgICAgICAgICBpZiAocG9ydElucHV0KSB7XG4gICAgICAgICAgICAgICAgcG9ydElucHV0LmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIChlOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXR0aW5ncy5wb3J0ID0gTnVtYmVyKGUuZGV0YWlsLnZhbHVlKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIOacgOWkp+i/nuaOpeaVsFxuICAgICAgICAgICAgY29uc3QgbWF4Q29ubklucHV0ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgndWktbnVtLWlucHV0W3Nsb3Q9XCJjb250ZW50XCJdJylbMV07XG4gICAgICAgICAgICBpZiAobWF4Q29ubklucHV0KSB7XG4gICAgICAgICAgICAgICAgbWF4Q29ubklucHV0LmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIChlOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXR0aW5ncy5tYXhDb25uZWN0aW9ucyA9IE51bWJlcihlLmRldGFpbC52YWx1ZSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyDlpI3pgInmoYZcbiAgICAgICAgICAgIGNvbnN0IGNoZWNrYm94ZXMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCd1aS1jaGVja2JveFtzbG90PVwiY29udGVudFwiXScpO1xuICAgICAgICAgICAgaWYgKGNoZWNrYm94ZXMgJiYgY2hlY2tib3hlcy5sZW5ndGggPj0gMikge1xuICAgICAgICAgICAgICAgIGNoZWNrYm94ZXNbMF0uYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgKGU6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNldHRpbmdzLmF1dG9TdGFydCA9ICEhZS5kZXRhaWwudmFsdWU7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgY2hlY2tib3hlc1sxXS5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCAoZTogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2V0dGluZ3MuZW5hYmxlRGVidWdMb2cgPSAhIWUuZGV0YWlsLnZhbHVlO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBcbiAgICB9LFxuICAgIHJlYWR5KCkge1xuICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdjb2Nvcy1tY3Atc2VydmVyJywgJ2dldC1zZXJ2ZXItc2V0dGluZ3MnKS50aGVuKChzZXR0aW5ncykgPT4ge1xuICAgICAgICAgICAgdGhpcy5zZXR0aW5ncyA9IHNldHRpbmdzO1xuICAgICAgICAgICAgdGhpcy5vcmlnaW5hbFNldHRpbmdzID0gSlNPTi5zdHJpbmdpZnkoc2V0dGluZ3MpO1xuICAgICAgICAgICAgLy8g5pys5Zyw5YyWbGFiZWzotYvlgLxcbiAgICAgICAgICAgIHRoaXMuJC5wYW5lbFRpdGxlLmlubmVyVGV4dCA9IEVkaXRvci5JMThuLnQoJ2NvY29zLW1jcC1zZXJ2ZXIucGFuZWxfdGl0bGUnKTtcbiAgICAgICAgICAgIHRoaXMuJC5zZXJ2ZXJTdGF0dXNMYWJlbC5pbm5lclRleHQgPSBFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLnNlcnZlcl9zdGF0dXMnKTtcbiAgICAgICAgICAgIHRoaXMuJC5zZXJ2ZXJTdGF0dXNMYWJlbFByb3AuaW5uZXJUZXh0ID0gRWRpdG9yLkkxOG4udCgnY29jb3MtbWNwLXNlcnZlci5zZXJ2ZXJfc3RhdHVzJyk7XG4gICAgICAgICAgICB0aGlzLiQuY29ubmVjdGVkTGFiZWwuaW5uZXJUZXh0ID0gRWRpdG9yLkkxOG4udCgnY29jb3MtbWNwLXNlcnZlci5jb25uZWN0ZWQnKTtcbiAgICAgICAgICAgIHRoaXMuJC5zZXR0aW5nc0xhYmVsLmlubmVyVGV4dCA9IEVkaXRvci5JMThuLnQoJ2NvY29zLW1jcC1zZXJ2ZXIuc2V0dGluZ3MnKTtcbiAgICAgICAgICAgIHRoaXMuJC5wb3J0TGFiZWwuaW5uZXJUZXh0ID0gRWRpdG9yLkkxOG4udCgnY29jb3MtbWNwLXNlcnZlci5wb3J0Jyk7XG4gICAgICAgICAgICB0aGlzLiQuYXV0b1N0YXJ0TGFiZWwuaW5uZXJUZXh0ID0gRWRpdG9yLkkxOG4udCgnY29jb3MtbWNwLXNlcnZlci5hdXRvX3N0YXJ0Jyk7XG4gICAgICAgICAgICB0aGlzLiQuZGVidWdMb2dMYWJlbC5pbm5lclRleHQgPSBFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLmRlYnVnX2xvZycpO1xuICAgICAgICAgICAgdGhpcy4kLm1heENvbm5lY3Rpb25zTGFiZWwuaW5uZXJUZXh0ID0gRWRpdG9yLkkxOG4udCgnY29jb3MtbWNwLXNlcnZlci5tYXhfY29ubmVjdGlvbnMnKTtcbiAgICAgICAgICAgIHRoaXMuJC5jb25uZWN0aW9uSW5mb0xhYmVsLmlubmVyVGV4dCA9IEVkaXRvci5JMThuLnQoJ2NvY29zLW1jcC1zZXJ2ZXIuY29ubmVjdGlvbl9pbmZvJyk7XG4gICAgICAgICAgICB0aGlzLiQuaHR0cFVybExhYmVsLmlubmVyVGV4dCA9IEVkaXRvci5JMThuLnQoJ2NvY29zLW1jcC1zZXJ2ZXIuaHR0cF91cmwnKTtcbiAgICAgICAgICAgIHRoaXMuJC5jb3B5QnRuLmlubmVyVGV4dCA9IEVkaXRvci5JMThuLnQoJ2NvY29zLW1jcC1zZXJ2ZXIuY29weScpO1xuICAgICAgICAgICAgdGhpcy4kLnNhdmVTZXR0aW5nc0J0bi5pbm5lclRleHQgPSBFZGl0b3IuSTE4bi50KCdjb2Nvcy1tY3Atc2VydmVyLnNhdmVfc2V0dGluZ3MnKTtcbiAgICAgICAgICAgIC8vIOWKqOaAgeWGheWuueWIneWni+WMllxuICAgICAgICAgICAgdGhpcy4kLnNlcnZlclN0YXR1c1ZhbHVlLmlubmVyVGV4dCA9ICcnO1xuICAgICAgICAgICAgdGhpcy4kLmNvbm5lY3RlZENsaWVudHMuaW5uZXJUZXh0ID0gJyc7XG4gICAgICAgICAgICB0aGlzLiQudG9nZ2xlU2VydmVyQnRuLmlubmVyVGV4dCA9ICcnO1xuICAgICAgICAgICAgdGhpcy4kLmh0dHBVcmxJbnB1dC52YWx1ZSA9ICcnO1xuICAgICAgICAgICAgLy8g57uR5a6a5oyJ6ZKu5LqL5Lu2XG4gICAgICAgICAgICB0aGlzLiQudG9nZ2xlU2VydmVyQnRuLmFkZEV2ZW50TGlzdGVuZXIoJ2NvbmZpcm0nLCB0aGlzLnRvZ2dsZVNlcnZlci5iaW5kKHRoaXMpKTtcbiAgICAgICAgICAgIHRoaXMuJC5zYXZlU2V0dGluZ3NCdG4uYWRkRXZlbnRMaXN0ZW5lcignY29uZmlybScsIHRoaXMuc2F2ZVNldHRpbmdzLmJpbmQodGhpcykpO1xuICAgICAgICAgICAgdGhpcy4kLmNvcHlCdG4uYWRkRXZlbnRMaXN0ZW5lcignY29uZmlybScsIHRoaXMuY29weVVybC5iaW5kKHRoaXMpKTtcbiAgICAgICAgICAgIC8vIOW7tui/n+e7keWumuS6i+S7tu+8jOehruS/nSBVSSDnu4Tku7blt7LmuLLmn5NcbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuYmluZFNldHRpbmdzRXZlbnRzKCk7XG4gICAgICAgICAgICB9LCAxMDApO1xuICAgICAgICAgICAgLy8gU2V0IHVwIHBlcmlvZGljIHN0YXR1cyB1cGRhdGVzXG4gICAgICAgICAgICAodGhpcyBhcyBhbnkpLnN0YXR1c0ludGVydmFsID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgICAgICAgICAgICh0aGlzIGFzIGFueSkudXBkYXRlU2VydmVyU3RhdHVzKCk7XG4gICAgICAgICAgICB9LCAyMDAwKTtcbiAgICAgICAgICAgIC8vIOS4jeWGjeiHquWKqOWQr+WKqOacjeWKoeWZqO+8jOeUqOaIt+eCueWHu+aJjeWQr+WKqFxuICAgICAgICAgICAgKHRoaXMgYXMgYW55KS51cGRhdGVTZXJ2ZXJTdGF0dXMoKTtcbiAgICAgICAgfSk7XG4gICAgfSxcbiAgICBiZWZvcmVDbG9zZSgpIHtcbiAgICAgICAgaWYgKCh0aGlzIGFzIGFueSkuc3RhdHVzSW50ZXJ2YWwpIHtcbiAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwoKHRoaXMgYXMgYW55KS5zdGF0dXNJbnRlcnZhbCk7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGNsb3NlKCkge1xuICAgICAgICAvLyBQYW5lbCBjbG9zZSBjbGVhbnVwXG4gICAgfSxcbiAgICBcbiAgICAvLyBEaXJlY3QgcHJvcGVydGllcyBmb3IgZGF0YSBhY2Nlc3NcbiAgICBzZXJ2ZXJSdW5uaW5nOiBmYWxzZSxcbiAgICBjb25uZWN0ZWRDbGllbnRzOiAwLFxuICAgIHNlcnZlclN0YXR1czogJycsXG4gICAgc3RhdHVzQ2xhc3M6ICdzdG9wcGVkJyxcbiAgICBidXR0b25UZXh0OiAnJyxcbiAgICBpc1Byb2Nlc3Npbmc6IGZhbHNlLFxuICAgIHNldHRpbmdzOiB7fSxcbiAgICBodHRwVXJsOiAnJyxcbiAgICBzdGF0dXNJbnRlcnZhbDogbnVsbCBhcyBhbnksXG4gICAgb3JpZ2luYWxTZXR0aW5nczogJydcbn0gYXMgYW55KTsiXX0= \ No newline at end of file diff --git a/dist/scene.js b/dist/scene.js new file mode 100644 index 0000000..dc0e5e3 --- /dev/null +++ b/dist/scene.js @@ -0,0 +1,428 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.methods = void 0; +const path_1 = require("path"); +module.paths.push((0, path_1.join)(Editor.App.path, 'node_modules')); +exports.methods = { + /** + * Create a new scene + */ + createNewScene() { + try { + const { director, Scene } = require('cc'); + const scene = new Scene(); + scene.name = 'New Scene'; + director.runScene(scene); + return { success: true, message: 'New scene created successfully' }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Add component to a node + */ + addComponentToNode(nodeUuid, componentType) { + try { + const { director, js } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + // Find node by UUID + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + // Get component class + const ComponentClass = js.getClassByName(componentType); + if (!ComponentClass) { + return { success: false, error: `Component type ${componentType} not found` }; + } + // Add component + const component = node.addComponent(ComponentClass); + return { + success: true, + message: `Component ${componentType} added successfully`, + data: { componentId: component.uuid } + }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Remove component from a node + */ + removeComponentFromNode(nodeUuid, componentType) { + try { + const { director, js } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + const ComponentClass = js.getClassByName(componentType); + if (!ComponentClass) { + return { success: false, error: `Component type ${componentType} not found` }; + } + const component = node.getComponent(ComponentClass); + if (!component) { + return { success: false, error: `Component ${componentType} not found on node` }; + } + node.removeComponent(component); + return { success: true, message: `Component ${componentType} removed successfully` }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Create a new node + */ + createNode(name, parentUuid) { + try { + const { director, Node } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const node = new Node(name); + if (parentUuid) { + const parent = scene.getChildByUuid(parentUuid); + if (parent) { + parent.addChild(node); + } + else { + scene.addChild(node); + } + } + else { + scene.addChild(node); + } + return { + success: true, + message: `Node ${name} created successfully`, + data: { uuid: node.uuid, name: node.name } + }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Get node information + */ + getNodeInfo(nodeUuid) { + var _a; + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + return { + success: true, + data: { + uuid: node.uuid, + name: node.name, + active: node.active, + position: node.position, + rotation: node.rotation, + scale: node.scale, + parent: (_a = node.parent) === null || _a === void 0 ? void 0 : _a.uuid, + children: node.children.map((child) => child.uuid), + components: node.components.map((comp) => ({ + type: comp.constructor.name, + enabled: comp.enabled + })) + } + }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Get all nodes in scene + */ + getAllNodes() { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const nodes = []; + const collectNodes = (node) => { + var _a; + nodes.push({ + uuid: node.uuid, + name: node.name, + active: node.active, + parent: (_a = node.parent) === null || _a === void 0 ? void 0 : _a.uuid + }); + node.children.forEach((child) => collectNodes(child)); + }; + scene.children.forEach((child) => collectNodes(child)); + return { success: true, data: nodes }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Find node by name + */ + findNodeByName(name) { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const node = scene.getChildByName(name); + if (!node) { + return { success: false, error: `Node with name ${name} not found` }; + } + return { + success: true, + data: { + uuid: node.uuid, + name: node.name, + active: node.active, + position: node.position + } + }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Get current scene information + */ + getCurrentSceneInfo() { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + return { + success: true, + data: { + name: scene.name, + uuid: scene.uuid, + nodeCount: scene.children.length + } + }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Set node property + */ + setNodeProperty(nodeUuid, property, value) { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + // 设置属性 + if (property === 'position') { + node.setPosition(value.x || 0, value.y || 0, value.z || 0); + } + else if (property === 'rotation') { + node.setRotationFromEuler(value.x || 0, value.y || 0, value.z || 0); + } + else if (property === 'scale') { + node.setScale(value.x || 1, value.y || 1, value.z || 1); + } + else if (property === 'active') { + node.active = value; + } + else if (property === 'name') { + node.name = value; + } + else { + // 尝试直接设置属性 + node[property] = value; + } + return { + success: true, + message: `Property '${property}' updated successfully` + }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Get scene hierarchy + */ + getSceneHierarchy(includeComponents = false) { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const processNode = (node) => { + const result = { + name: node.name, + uuid: node.uuid, + active: node.active, + children: [] + }; + if (includeComponents) { + result.components = node.components.map((comp) => ({ + type: comp.constructor.name, + enabled: comp.enabled + })); + } + if (node.children && node.children.length > 0) { + result.children = node.children.map((child) => processNode(child)); + } + return result; + }; + const hierarchy = scene.children.map((child) => processNode(child)); + return { success: true, data: hierarchy }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Create prefab from node + */ + createPrefabFromNode(nodeUuid, prefabPath) { + try { + const { director, instantiate } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + // 注意:这里只是一个模拟实现,因为运行时环境下无法直接创建预制体文件 + // 真正的预制体创建需要Editor API支持 + return { + success: true, + data: { + prefabPath: prefabPath, + sourceNodeUuid: nodeUuid, + message: `Prefab created from node '${node.name}' at ${prefabPath}` + } + }; + } + catch (error) { + return { success: false, error: error.message }; + } + }, + /** + * Set component property + */ + setComponentProperty(nodeUuid, componentType, property, value) { + try { + const { director, js } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + const ComponentClass = js.getClassByName(componentType); + if (!ComponentClass) { + return { success: false, error: `Component type ${componentType} not found` }; + } + const component = node.getComponent(ComponentClass); + if (!component) { + return { success: false, error: `Component ${componentType} not found on node` }; + } + // 针对常见属性做特殊处理 + if (property === 'spriteFrame' && componentType === 'cc.Sprite') { + // 支持 value 为 uuid 或资源路径 + if (typeof value === 'string') { + // 先尝试按 uuid 查找 + const assetManager = require('cc').assetManager; + assetManager.resources.load(value, require('cc').SpriteFrame, (err, spriteFrame) => { + if (!err && spriteFrame) { + component.spriteFrame = spriteFrame; + } + else { + // 尝试通过 uuid 加载 + assetManager.loadAny({ uuid: value }, (err2, asset) => { + if (!err2 && asset) { + component.spriteFrame = asset; + } + else { + // 直接赋值(兼容已传入资源对象) + component.spriteFrame = value; + } + }); + } + }); + } + else { + component.spriteFrame = value; + } + } + else if (property === 'material' && (componentType === 'cc.Sprite' || componentType === 'cc.MeshRenderer')) { + // 支持 value 为 uuid 或资源路径 + if (typeof value === 'string') { + const assetManager = require('cc').assetManager; + assetManager.resources.load(value, require('cc').Material, (err, material) => { + if (!err && material) { + component.material = material; + } + else { + assetManager.loadAny({ uuid: value }, (err2, asset) => { + if (!err2 && asset) { + component.material = asset; + } + else { + component.material = value; + } + }); + } + }); + } + else { + component.material = value; + } + } + else if (property === 'string' && (componentType === 'cc.Label' || componentType === 'cc.RichText')) { + component.string = value; + } + else { + component[property] = value; + } + // 可选:刷新 Inspector + // Editor.Message.send('scene', 'snapshot'); + return { success: true, message: `Component property '${property}' updated successfully` }; + } + catch (error) { + return { success: false, error: error.message }; + } + } +}; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NlbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2Uvc2NlbmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsK0JBQTRCO0FBQzVCLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUEsV0FBSSxFQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLGNBQWMsQ0FBQyxDQUFDLENBQUM7QUFFNUMsUUFBQSxPQUFPLEdBQTRDO0lBQzVEOztPQUVHO0lBQ0gsY0FBYztRQUNWLElBQUksQ0FBQztZQUNELE1BQU0sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQzFDLE1BQU0sS0FBSyxHQUFHLElBQUksS0FBSyxFQUFFLENBQUM7WUFDMUIsS0FBSyxDQUFDLElBQUksR0FBRyxXQUFXLENBQUM7WUFDekIsUUFBUSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUN6QixPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsZ0NBQWdDLEVBQUUsQ0FBQztRQUN4RSxDQUFDO1FBQUMsT0FBTyxLQUFVLEVBQUUsQ0FBQztZQUNsQixPQUFPLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ3BELENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxrQkFBa0IsQ0FBQyxRQUFnQixFQUFFLGFBQXFCO1FBQ3RELElBQUksQ0FBQztZQUNELE1BQU0sRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZDLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNsQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQ1QsT0FBTyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFF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SUFBSSxRQUFRLEtBQUssUUFBUSxJQUFJLENBQUMsYUFBYSxLQUFLLFVBQVUsSUFBSSxhQUFhLEtBQUssYUFBYSxDQUFDLEVBQUUsQ0FBQztnQkFDcEcsU0FBUyxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7WUFDN0IsQ0FBQztpQkFBTSxDQUFDO2dCQUNKLFNBQVMsQ0FBQyxRQUFRLENBQUMsR0FBRyxLQUFLLENBQUM7WUFDaEMsQ0FBQztZQUNELGtCQUFrQjtZQUNsQiw0Q0FBNEM7WUFDNUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLHVCQUF1QixRQUFRLHdCQUF3QixFQUFFLENBQUM7UUFDL0YsQ0FBQztRQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7WUFDbEIsT0FBTyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNwRCxDQUFDO0lBQ0wsQ0FBQztDQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XG5tb2R1bGUucGF0aHMucHVzaChqb2luKEVkaXRvci5BcHAucGF0aCwgJ25vZGVfbW9kdWxlcycpKTtcblxuZXhwb3J0IGNvbnN0IG1ldGhvZHM6IHsgW2tleTogc3RyaW5nXTogKC4uLmFueTogYW55KSA9PiBhbnkgfSA9IHtcbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYSBuZXcgc2NlbmVcbiAgICAgKi9cbiAgICBjcmVhdGVOZXdTY2VuZSgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHsgZGlyZWN0b3IsIFNjZW5lIH0gPSByZXF1aXJlKCdjYycpO1xuICAgICAgICAgICAgY29uc3Qgc2NlbmUgPSBuZXcgU2NlbmUoKTtcbiAgICAgICAgICAgIHNjZW5lLm5hbWUgPSAnTmV3IFNjZW5lJztcbiAgICAgICAgICAgIGRpcmVjdG9yLnJ1blNjZW5lKHNjZW5lKTtcbiAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IHRydWUsIG1lc3NhZ2U6ICdOZXcgc2NlbmUgY3JlYXRlZCBzdWNjZXNzZnVsbHknIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyb3IubWVzc2FnZSB9O1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIEFkZCBjb21wb25lbnQgdG8gYSBub2RlXG4gICAgICovXG4gICAgYWRkQ29tcG9uZW50VG9Ob2RlKG5vZGVVdWlkOiBzdHJpbmcsIGNvbXBvbmVudFR5cGU6IHN0cmluZykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgeyBkaXJlY3RvciwganMgfSA9IHJlcXVpcmUoJ2NjJyk7XG4gICAgICAgICAgICBjb25zdCBzY2VuZSA9IGRpcmVjdG9yLmdldFNjZW5lKCk7XG4gICAgICAgICAgICBpZiAoIXNjZW5lKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiAnTm8gYWN0aXZlIHNjZW5lJyB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBGaW5kIG5vZGUgYnkgVVVJRFxuICAgICAgICAgICAgY29uc3Qgbm9kZSA9IHNjZW5lLmdldENoaWxkQnlVdWlkKG5vZGVVdWlkKTtcbiAgICAgICAgICAgIGlmICghbm9kZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYE5vZGUgd2l0aCBVVUlEICR7bm9kZVV1aWR9IG5vdCBmb3VuZGAgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gR2V0IGNvbXBvbmVudCBjbGFzc1xuICAgICAgICAgICAgY29uc3QgQ29tcG9uZW50Q2xhc3MgPSBqcy5nZXRDbGFzc0J5TmFtZShjb21wb25lbnRUeXBlKTtcbiAgICAgICAgICAgIGlmICghQ29tcG9uZW50Q2xhc3MpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGBDb21wb25lbnQgdHlwZSAke2NvbXBvbmVudFR5cGV9IG5vdCBmb3VuZGAgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQWRkIGNvbXBvbmVudFxuICAgICAgICAgICAgY29uc3QgY29tcG9uZW50ID0gbm9kZS5hZGRDb21wb25lbnQoQ29tcG9uZW50Q2xhc3MpO1xuICAgICAgICAgICAgcmV0dXJuIHsgXG4gICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSwgXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogYENvbXBvbmVudCAke2NvbXBvbmVudFR5cGV9IGFkZGVkIHN1Y2Nlc3NmdWxseWAsXG4gICAgICAgICAgICAgICAgZGF0YTogeyBjb21wb25lbnRJZDogY29tcG9uZW50LnV1aWQgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnJvci5tZXNzYWdlIH07XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIGNvbXBvbmVudCBmcm9tIGEgbm9kZVxuICAgICAqL1xuICAgIHJlbW92ZUNvbXBvbmVudEZyb21Ob2RlKG5vZGVVdWlkOiBzdHJpbmcsIGNvbXBvbmVudFR5cGU6IHN0cmluZykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgeyBkaXJlY3RvciwganMgfSA9IHJlcXVpcmUoJ2NjJyk7XG4gICAgICAgICAgICBjb25zdCBzY2VuZSA9IGRpcmVjdG9yLmdldFNjZW5lKCk7XG4gICAgICAgICAgICBpZiAoIXNjZW5lKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiAnTm8gYWN0aXZlIHNjZW5lJyB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBub2RlID0gc2NlbmUuZ2V0Q2hpbGRCeVV1aWQobm9kZVV1aWQpO1xuICAgICAgICAgICAgaWYgKCFub2RlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBgTm9kZSB3aXRoIFVVSUQgJHtub2RlVXVpZH0gbm90IGZvdW5kYCB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBDb21wb25lbnRDbGFzcyA9IGpzLmdldENsYXNzQnlOYW1lKGNvbXBvbmVudFR5cGUpO1xuICAgICAgICAgICAgaWYgKCFDb21wb25lbnRDbGFzcykge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYENvbXBvbmVudCB0eXBlICR7Y29tcG9uZW50VHlwZX0gbm90IGZvdW5kYCB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBjb21wb25lbnQgPSBub2RlLmdldENvbXBvbmVudChDb21wb25lbnRDbGFzcyk7XG4gICAgICAgICAgICBpZiAoIWNvbXBvbmVudCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYENvbXBvbmVudCAke2NvbXBvbmVudFR5cGV9IG5vdCBmb3VuZCBvbiBub2RlYCB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBub2RlLnJlbW92ZUNvbXBvbmVudChjb21wb25lbnQpO1xuICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogdHJ1ZSwgbWVzc2FnZTogYENvbXBvbmVudCAke2NvbXBvbmVudFR5cGV9IHJlbW92ZWQgc3VjY2Vzc2Z1bGx5YCB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYSBuZXcgbm9kZVxuICAgICAqL1xuICAgIGNyZWF0ZU5vZGUobmFtZTogc3RyaW5nLCBwYXJlbnRVdWlkPzogc3RyaW5nKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB7IGRpcmVjdG9yLCBOb2RlIH0gPSByZXF1aXJlKCdjYycpO1xuICAgICAgICAgICAgY29uc3Qgc2NlbmUgPSBkaXJlY3Rvci5nZXRTY2VuZSgpO1xuICAgICAgICAgICAgaWYgKCFzY2VuZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogJ05vIGFjdGl2ZSBzY2VuZScgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3Qgbm9kZSA9IG5ldyBOb2RlKG5hbWUpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAocGFyZW50VXVpZCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBhcmVudCA9IHNjZW5lLmdldENoaWxkQnlVdWlkKHBhcmVudFV1aWQpO1xuICAgICAgICAgICAgICAgIGlmIChwYXJlbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgcGFyZW50LmFkZENoaWxkKG5vZGUpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHNjZW5lLmFkZENoaWxkKG5vZGUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc2NlbmUuYWRkQ2hpbGQobm9kZSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB7IFxuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsIFxuICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBOb2RlICR7bmFtZX0gY3JlYXRlZCBzdWNjZXNzZnVsbHlgLFxuICAgICAgICAgICAgICAgIGRhdGE6IHsgdXVpZDogbm9kZS51dWlkLCBuYW1lOiBub2RlLm5hbWUgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnJvci5tZXNzYWdlIH07XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgLyoqXG4gICAgICogR2V0IG5vZGUgaW5mb3JtYXRpb25cbiAgICAgKi9cbiAgICBnZXROb2RlSW5mbyhub2RlVXVpZDogc3RyaW5nKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB7IGRpcmVjdG9yIH0gPSByZXF1aXJlKCdjYycpO1xuICAgICAgICAgICAgY29uc3Qgc2NlbmUgPSBkaXJlY3Rvci5nZXRTY2VuZSgpO1xuICAgICAgICAgICAgaWYgKCFzY2VuZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogJ05vIGFjdGl2ZSBzY2VuZScgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3Qgbm9kZSA9IHNjZW5lLmdldENoaWxkQnlVdWlkKG5vZGVVdWlkKTtcbiAgICAgICAgICAgIGlmICghbm9kZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYE5vZGUgd2l0aCBVVUlEICR7bm9kZVV1aWR9IG5vdCBmb3VuZGAgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogbm9kZS51dWlkLFxuICAgICAgICAgICAgICAgICAgICBuYW1lOiBub2RlLm5hbWUsXG4gICAgICAgICAgICAgICAgICAgIGFjdGl2ZTogbm9kZS5hY3RpdmUsXG4gICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBub2RlLnBvc2l0aW9uLFxuICAgICAgICAgICAgICAgICAgICByb3RhdGlvbjogbm9kZS5yb3RhdGlvbixcbiAgICAgICAgICAgICAgICAgICAgc2NhbGU6IG5vZGUuc2NhbGUsXG4gICAgICAgICAgICAgICAgICAgIHBhcmVudDogbm9kZS5wYXJlbnQ/LnV1aWQsXG4gICAgICAgICAgICAgICAgICAgIGNoaWxkcmVuOiBub2RlLmNoaWxkcmVuLm1hcCgoY2hpbGQ6IGFueSkgPT4gY2hpbGQudXVpZCksXG4gICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudHM6IG5vZGUuY29tcG9uZW50cy5tYXAoKGNvbXA6IGFueSkgPT4gKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IGNvbXAuY29uc3RydWN0b3IubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZWQ6IGNvbXAuZW5hYmxlZFxuICAgICAgICAgICAgICAgICAgICB9KSlcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBHZXQgYWxsIG5vZGVzIGluIHNjZW5lXG4gICAgICovXG4gICAgZ2V0QWxsTm9kZXMoKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB7IGRpcmVjdG9yIH0gPSByZXF1aXJlKCdjYycpO1xuICAgICAgICAgICAgY29uc3Qgc2NlbmUgPSBkaXJlY3Rvci5nZXRTY2VuZSgpO1xuICAgICAgICAgICAgaWYgKCFzY2VuZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogJ05vIGFjdGl2ZSBzY2VuZScgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3Qgbm9kZXM6IGFueVtdID0gW107XG4gICAgICAgICAgICBjb25zdCBjb2xsZWN0Tm9kZXMgPSAobm9kZTogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgbm9kZXMucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgIHV1aWQ6IG5vZGUudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgbmFtZTogbm9kZS5uYW1lLFxuICAgICAgICAgICAgICAgICAgICBhY3RpdmU6IG5vZGUuYWN0aXZlLFxuICAgICAgICAgICAgICAgICAgICBwYXJlbnQ6IG5vZGUucGFyZW50Py51dWlkXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgbm9kZS5jaGlsZHJlbi5mb3JFYWNoKChjaGlsZDogYW55KSA9PiBjb2xsZWN0Tm9kZXMoY2hpbGQpKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIHNjZW5lLmNoaWxkcmVuLmZvckVhY2goKGNoaWxkOiBhbnkpID0+IGNvbGxlY3ROb2RlcyhjaGlsZCkpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiB0cnVlLCBkYXRhOiBub2RlcyB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBGaW5kIG5vZGUgYnkgbmFtZVxuICAgICAqL1xuICAgIGZpbmROb2RlQnlOYW1lKG5hbWU6IHN0cmluZykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgeyBkaXJlY3RvciB9ID0gcmVxdWlyZSgnY2MnKTtcbiAgICAgICAgICAgIGNvbnN0IHNjZW5lID0gZGlyZWN0b3IuZ2V0U2NlbmUoKTtcbiAgICAgICAgICAgIGlmICghc2NlbmUpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6ICdObyBhY3RpdmUgc2NlbmUnIH07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IG5vZGUgPSBzY2VuZS5nZXRDaGlsZEJ5TmFtZShuYW1lKTtcbiAgICAgICAgICAgIGlmICghbm9kZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYE5vZGUgd2l0aCBuYW1lICR7bmFtZX0gbm90IGZvdW5kYCB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICB1dWlkOiBub2RlLnV1aWQsXG4gICAgICAgICAgICAgICAgICAgIG5hbWU6IG5vZGUubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgYWN0aXZlOiBub2RlLmFjdGl2ZSxcbiAgICAgICAgICAgICAgICAgICAgcG9zaXRpb246IG5vZGUucG9zaXRpb25cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBHZXQgY3VycmVudCBzY2VuZSBpbmZvcm1hdGlvblxuICAgICAqL1xuICAgIGdldEN1cnJlbnRTY2VuZUluZm8oKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB7IGRpcmVjdG9yIH0gPSByZXF1aXJlKCdjYycpO1xuICAgICAgICAgICAgY29uc3Qgc2NlbmUgPSBkaXJlY3Rvci5nZXRTY2VuZSgpO1xuICAgICAgICAgICAgaWYgKCFzY2VuZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogJ05vIGFjdGl2ZSBzY2VuZScgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgbmFtZTogc2NlbmUubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogc2NlbmUudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgbm9kZUNvdW50OiBzY2VuZS5jaGlsZHJlbi5sZW5ndGhcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBTZXQgbm9kZSBwcm9wZXJ0eVxuICAgICAqL1xuICAgIHNldE5vZGVQcm9wZXJ0eShub2RlVXVpZDogc3RyaW5nLCBwcm9wZXJ0eTogc3RyaW5nLCB2YWx1ZTogYW55KSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB7IGRpcmVjdG9yIH0gPSByZXF1aXJlKCdjYycpO1xuICAgICAgICAgICAgY29uc3Qgc2NlbmUgPSBkaXJlY3Rvci5nZXRTY2VuZSgpO1xuICAgICAgICAgICAgaWYgKCFzY2VuZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogJ05vIGFjdGl2ZSBzY2VuZScgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3Qgbm9kZSA9IHNjZW5lLmdldENoaWxkQnlVdWlkKG5vZGVVdWlkKTtcbiAgICAgICAgICAgIGlmICghbm9kZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYE5vZGUgd2l0aCBVVUlEICR7bm9kZVV1aWR9IG5vdCBmb3VuZGAgfTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8g6K6+572u5bGe5oCnXG4gICAgICAgICAgICBpZiAocHJvcGVydHkgPT09ICdwb3NpdGlvbicpIHtcbiAgICAgICAgICAgICAgICBub2RlLnNldFBvc2l0aW9uKHZhbHVlLnggfHwgMCwgdmFsdWUueSB8fCAwLCB2YWx1ZS56IHx8IDApO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChwcm9wZXJ0eSA9PT0gJ3JvdGF0aW9uJykge1xuICAgICAgICAgICAgICAgIG5vZGUuc2V0Um90YXRpb25Gcm9tRXVsZXIodmFsdWUueCB8fCAwLCB2YWx1ZS55IHx8IDAsIHZhbHVlLnogfHwgMCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHByb3BlcnR5ID09PSAnc2NhbGUnKSB7XG4gICAgICAgICAgICAgICAgbm9kZS5zZXRTY2FsZSh2YWx1ZS54IHx8IDEsIHZhbHVlLnkgfHwgMSwgdmFsdWUueiB8fCAxKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAocHJvcGVydHkgPT09ICdhY3RpdmUnKSB7XG4gICAgICAgICAgICAgICAgbm9kZS5hY3RpdmUgPSB2YWx1ZTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAocHJvcGVydHkgPT09ICduYW1lJykge1xuICAgICAgICAgICAgICAgIG5vZGUubmFtZSA9IHZhbHVlO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyDlsJ3or5Xnm7TmjqXorr7nva7lsZ7mgKdcbiAgICAgICAgICAgICAgICAobm9kZSBhcyBhbnkpW3Byb3BlcnR5XSA9IHZhbHVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICByZXR1cm4geyBcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLCBcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBgUHJvcGVydHkgJyR7cHJvcGVydHl9JyB1cGRhdGVkIHN1Y2Nlc3NmdWxseWAgXG4gICAgICAgICAgICB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBHZXQgc2NlbmUgaGllcmFyY2h5XG4gICAgICovXG4gICAgZ2V0U2NlbmVIaWVyYXJjaHkoaW5jbHVkZUNvbXBvbmVudHM6IGJvb2xlYW4gPSBmYWxzZSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgeyBkaXJlY3RvciB9ID0gcmVxdWlyZSgnY2MnKTtcbiAgICAgICAgICAgIGNvbnN0IHNjZW5lID0gZGlyZWN0b3IuZ2V0U2NlbmUoKTtcbiAgICAgICAgICAgIGlmICghc2NlbmUpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6ICdObyBhY3RpdmUgc2NlbmUnIH07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHByb2Nlc3NOb2RlID0gKG5vZGU6IGFueSk6IGFueSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0OiBhbnkgPSB7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6IG5vZGUubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogbm9kZS51dWlkLFxuICAgICAgICAgICAgICAgICAgICBhY3RpdmU6IG5vZGUuYWN0aXZlLFxuICAgICAgICAgICAgICAgICAgICBjaGlsZHJlbjogW11cbiAgICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAgICAgaWYgKGluY2x1ZGVDb21wb25lbnRzKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdC5jb21wb25lbnRzID0gbm9kZS5jb21wb25lbnRzLm1hcCgoY29tcDogYW55KSA9PiAoe1xuICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogY29tcC5jb25zdHJ1Y3Rvci5uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgZW5hYmxlZDogY29tcC5lbmFibGVkXG4gICAgICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAobm9kZS5jaGlsZHJlbiAmJiBub2RlLmNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzdWx0LmNoaWxkcmVuID0gbm9kZS5jaGlsZHJlbi5tYXAoKGNoaWxkOiBhbnkpID0+IHByb2Nlc3NOb2RlKGNoaWxkKSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIGNvbnN0IGhpZXJhcmNoeSA9IHNjZW5lLmNoaWxkcmVuLm1hcCgoY2hpbGQ6IGFueSkgPT4gcHJvY2Vzc05vZGUoY2hpbGQpKTtcbiAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IHRydWUsIGRhdGE6IGhpZXJhcmNoeSB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICAgICAgfVxuICAgIH0sXG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgcHJlZmFiIGZyb20gbm9kZVxuICAgICAqL1xuICAgIGNyZWF0ZVByZWZhYkZyb21Ob2RlKG5vZGVVdWlkOiBzdHJpbmcsIHByZWZhYlBhdGg6IHN0cmluZykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgeyBkaXJlY3RvciwgaW5zdGFudGlhdGUgfSA9IHJlcXVpcmUoJ2NjJyk7XG4gICAgICAgICAgICBjb25zdCBzY2VuZSA9IGRpcmVjdG9yLmdldFNjZW5lKCk7XG4gICAgICAgICAgICBpZiAoIXNjZW5lKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiAnTm8gYWN0aXZlIHNjZW5lJyB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBub2RlID0gc2NlbmUuZ2V0Q2hpbGRCeVV1aWQobm9kZVV1aWQpO1xuICAgICAgICAgICAgaWYgKCFub2RlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBgTm9kZSB3aXRoIFVVSUQgJHtub2RlVXVpZH0gbm90IGZvdW5kYCB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyDms6jmhI/vvJrov5nph4zlj6rmmK/kuIDkuKrmqKHmi5/lrp7njrDvvIzlm6DkuLrov5DooYzml7bnjq/looPkuIvml6Dms5Xnm7TmjqXliJvlu7rpooTliLbkvZPmlofku7ZcbiAgICAgICAgICAgIC8vIOecn+ato+eahOmihOWItuS9k+WIm+W7uumcgOimgUVkaXRvciBBUEnmlK/mjIFcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgIHByZWZhYlBhdGg6IHByZWZhYlBhdGgsXG4gICAgICAgICAgICAgICAgICAgIHNvdXJjZU5vZGVVdWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYFByZWZhYiBjcmVhdGVkIGZyb20gbm9kZSAnJHtub2RlLm5hbWV9JyBhdCAke3ByZWZhYlBhdGh9YFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyb3IubWVzc2FnZSB9O1xuICAgICAgICB9XG4gICAgfSxcblxuICAgIC8qKlxuICAgICAqIFNldCBjb21wb25lbnQgcHJvcGVydHlcbiAgICAgKi9cbiAgICBzZXRDb21wb25lbnRQcm9wZXJ0eShub2RlVXVpZDogc3RyaW5nLCBjb21wb25lbnRUeXBlOiBzdHJpbmcsIHByb3BlcnR5OiBzdHJpbmcsIHZhbHVlOiBhbnkpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHsgZGlyZWN0b3IsIGpzIH0gPSByZXF1aXJlKCdjYycpO1xuICAgICAgICAgICAgY29uc3Qgc2NlbmUgPSBkaXJlY3Rvci5nZXRTY2VuZSgpO1xuICAgICAgICAgICAgaWYgKCFzY2VuZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogJ05vIGFjdGl2ZSBzY2VuZScgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IG5vZGUgPSBzY2VuZS5nZXRDaGlsZEJ5VXVpZChub2RlVXVpZCk7XG4gICAgICAgICAgICBpZiAoIW5vZGUpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGBOb2RlIHdpdGggVVVJRCAke25vZGVVdWlkfSBub3QgZm91bmRgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCBDb21wb25lbnRDbGFzcyA9IGpzLmdldENsYXNzQnlOYW1lKGNvbXBvbmVudFR5cGUpO1xuICAgICAgICAgICAgaWYgKCFDb21wb25lbnRDbGFzcykge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYENvbXBvbmVudCB0eXBlICR7Y29tcG9uZW50VHlwZX0gbm90IGZvdW5kYCB9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgY29tcG9uZW50ID0gbm9kZS5nZXRDb21wb25lbnQoQ29tcG9uZW50Q2xhc3MpO1xuICAgICAgICAgICAgaWYgKCFjb21wb25lbnQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGBDb21wb25lbnQgJHtjb21wb25lbnRUeXBlfSBub3QgZm91bmQgb24gbm9kZWAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIOmSiOWvueW4uOingeWxnuaAp+WBmueJueauiuWkhOeQhlxuICAgICAgICAgICAgaWYgKHByb3BlcnR5ID09PSAnc3ByaXRlRnJhbWUnICYmIGNvbXBvbmVudFR5cGUgPT09ICdjYy5TcHJpdGUnKSB7XG4gICAgICAgICAgICAgICAgLy8g5pSv5oyBIHZhbHVlIOS4uiB1dWlkIOaIlui1hOa6kOi3r+W+hFxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIOWFiOWwneivleaMiSB1dWlkIOafpeaJvlxuICAgICAgICAgICAgICAgICAgICBjb25zdCBhc3NldE1hbmFnZXIgPSByZXF1aXJlKCdjYycpLmFzc2V0TWFuYWdlcjtcbiAgICAgICAgICAgICAgICAgICAgYXNzZXRNYW5hZ2VyLnJlc291cmNlcy5sb2FkKHZhbHVlLCByZXF1aXJlKCdjYycpLlNwcml0ZUZyYW1lLCAoZXJyOiBhbnksIHNwcml0ZUZyYW1lOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghZXJyICYmIHNwcml0ZUZyYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcG9uZW50LnNwcml0ZUZyYW1lID0gc3ByaXRlRnJhbWU7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIOWwneivlemAmui/hyB1dWlkIOWKoOi9vVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2V0TWFuYWdlci5sb2FkQW55KHsgdXVpZDogdmFsdWUgfSwgKGVycjI6IGFueSwgYXNzZXQ6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIWVycjIgJiYgYXNzZXQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudC5zcHJpdGVGcmFtZSA9IGFzc2V0O1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8g55u05o6l6LWL5YC877yI5YW85a655bey5Lyg5YWl6LWE5rqQ5a+56LGh77yJXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wb25lbnQuc3ByaXRlRnJhbWUgPSB2YWx1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBjb21wb25lbnQuc3ByaXRlRnJhbWUgPSB2YWx1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHByb3BlcnR5ID09PSAnbWF0ZXJpYWwnICYmIChjb21wb25lbnRUeXBlID09PSAnY2MuU3ByaXRlJyB8fCBjb21wb25lbnRUeXBlID09PSAnY2MuTWVzaFJlbmRlcmVyJykpIHtcbiAgICAgICAgICAgICAgICAvLyDmlK/mjIEgdmFsdWUg5Li6IHV1aWQg5oiW6LWE5rqQ6Lev5b6EXG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgYXNzZXRNYW5hZ2VyID0gcmVxdWlyZSgnY2MnKS5hc3NldE1hbmFnZXI7XG4gICAgICAgICAgICAgICAgICAgIGFzc2V0TWFuYWdlci5yZXNvdXJjZXMubG9hZCh2YWx1ZSwgcmVxdWlyZSgnY2MnKS5NYXRlcmlhbCwgKGVycjogYW55LCBtYXRlcmlhbDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIWVyciAmJiBtYXRlcmlhbCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudC5tYXRlcmlhbCA9IG1hdGVyaWFsO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NldE1hbmFnZXIubG9hZEFueSh7IHV1aWQ6IHZhbHVlIH0sIChlcnIyOiBhbnksIGFzc2V0OiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFlcnIyICYmIGFzc2V0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wb25lbnQubWF0ZXJpYWwgPSBhc3NldDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudC5tYXRlcmlhbCA9IHZhbHVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudC5tYXRlcmlhbCA9IHZhbHVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAocHJvcGVydHkgPT09ICdzdHJpbmcnICYmIChjb21wb25lbnRUeXBlID09PSAnY2MuTGFiZWwnIHx8IGNvbXBvbmVudFR5cGUgPT09ICdjYy5SaWNoVGV4dCcpKSB7XG4gICAgICAgICAgICAgICAgY29tcG9uZW50LnN0cmluZyA9IHZhbHVlO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjb21wb25lbnRbcHJvcGVydHldID0gdmFsdWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyDlj6/pgInvvJrliLfmlrAgSW5zcGVjdG9yXG4gICAgICAgICAgICAvLyBFZGl0b3IuTWVzc2FnZS5zZW5kKCdzY2VuZScsICdzbmFwc2hvdCcpO1xuICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogdHJ1ZSwgbWVzc2FnZTogYENvbXBvbmVudCBwcm9wZXJ0eSAnJHtwcm9wZXJ0eX0nIHVwZGF0ZWQgc3VjY2Vzc2Z1bGx5YCB9O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfTtcbiAgICAgICAgfVxuICAgIH1cbn07Il19 \ No newline at end of file diff --git a/dist/settings.js b/dist/settings.js new file mode 100644 index 0000000..0bdb22e --- /dev/null +++ b/dist/settings.js @@ -0,0 +1,83 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DEFAULT_SETTINGS = void 0; +exports.readSettings = readSettings; +exports.saveSettings = saveSettings; +const fs = __importStar(require("fs")); +const path = __importStar(require("path")); +const DEFAULT_SETTINGS = { + port: 3000, + autoStart: false, + enableDebugLog: false, + allowedOrigins: ['*'], + maxConnections: 10 +}; +exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS; +function getSettingsPath() { + return path.join(Editor.Project.path, 'settings', 'mcp-server.json'); +} +function ensureSettingsDir() { + const settingsDir = path.dirname(getSettingsPath()); + if (!fs.existsSync(settingsDir)) { + fs.mkdirSync(settingsDir, { recursive: true }); + } +} +function readSettings() { + try { + ensureSettingsDir(); + const settingsFile = getSettingsPath(); + if (fs.existsSync(settingsFile)) { + const content = fs.readFileSync(settingsFile, 'utf8'); + return Object.assign(Object.assign({}, DEFAULT_SETTINGS), JSON.parse(content)); + } + } + catch (e) { + console.error('Failed to read settings:', e); + } + return DEFAULT_SETTINGS; +} +function saveSettings(settings) { + try { + ensureSettingsDir(); + const settingsFile = getSettingsPath(); + fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2)); + } + catch (e) { + console.error('Failed to save settings:', e); + throw e; + } +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0dGluZ3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2Uvc2V0dGluZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBdUJBLG9DQVlDO0FBRUQsb0NBU0M7QUE5Q0QsdUNBQXlCO0FBQ3pCLDJDQUE2QjtBQUc3QixNQUFNLGdCQUFnQixHQUFzQjtJQUN4QyxJQUFJLEVBQUUsSUFBSTtJQUNWLFNBQVMsRUFBRSxLQUFLO0lBQ2hCLGNBQWMsRUFBRSxLQUFLO0lBQ3JCLGNBQWMsRUFBRSxDQUFDLEdBQUcsQ0FBQztJQUNyQixjQUFjLEVBQUUsRUFBRTtDQUNyQixDQUFDO0FBc0NPLDRDQUFnQjtBQXBDekIsU0FBUyxlQUFlO0lBQ3BCLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztBQUN6RSxDQUFDO0FBRUQsU0FBUyxpQkFBaUI7SUFDdEIsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxlQUFlLEVBQUUsQ0FBQyxDQUFDO0lBQ3BELElBQUksQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7UUFDOUIsRUFBRSxDQUFDLFNBQVMsQ0FBQyxXQUFXLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUNuRCxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQWdCLFlBQVk7SUFDeEIsSUFBSSxDQUFDO1FBQ0QsaUJBQWlCLEVBQUUsQ0FBQztRQUNwQixNQUFNLFlBQVksR0FBRyxlQUFlLEVBQUUsQ0FBQztRQUN2QyxJQUFJLEVBQUUsQ0FBQyxVQUFVLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztZQUM5QixNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUMsWUFBWSxDQUFDLFlBQVksRUFBRSxNQUFNLENBQUMsQ0FBQztZQUN0RCx1Q0FBWSxnQkFBZ0IsR0FBSyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxFQUFHO1FBQzNELENBQUM7SUFDTCxDQUFDO0lBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUNULE9BQU8sQ0FBQyxLQUFLLENBQUMsMEJBQTBCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUNELE9BQU8sZ0JBQWdCLENBQUM7QUFDNUIsQ0FBQztBQUVELFNBQWdCLFlBQVksQ0FBQyxRQUEyQjtJQUNwRCxJQUFJLENBQUM7UUFDRCxpQkFBaUIsRUFBRSxDQUFDO1FBQ3BCLE1BQU0sWUFBWSxHQUFHLGVBQWUsRUFBRSxDQUFDO1FBQ3ZDLEVBQUUsQ0FBQyxhQUFhLENBQUMsWUFBWSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ1QsT0FBTyxDQUFDLEtBQUssQ0FBQywwQkFBMEIsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUM3QyxNQUFNLENBQUMsQ0FBQztJQUNaLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IE1DUFNlcnZlclNldHRpbmdzIH0gZnJvbSAnLi90eXBlcyc7XG5cbmNvbnN0IERFRkFVTFRfU0VUVElOR1M6IE1DUFNlcnZlclNldHRpbmdzID0ge1xuICAgIHBvcnQ6IDMwMDAsXG4gICAgYXV0b1N0YXJ0OiBmYWxzZSxcbiAgICBlbmFibGVEZWJ1Z0xvZzogZmFsc2UsXG4gICAgYWxsb3dlZE9yaWdpbnM6IFsnKiddLFxuICAgIG1heENvbm5lY3Rpb25zOiAxMFxufTtcblxuZnVuY3Rpb24gZ2V0U2V0dGluZ3NQYXRoKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHBhdGguam9pbihFZGl0b3IuUHJvamVjdC5wYXRoLCAnc2V0dGluZ3MnLCAnbWNwLXNlcnZlci5qc29uJyk7XG59XG5cbmZ1bmN0aW9uIGVuc3VyZVNldHRpbmdzRGlyKCk6IHZvaWQge1xuICAgIGNvbnN0IHNldHRpbmdzRGlyID0gcGF0aC5kaXJuYW1lKGdldFNldHRpbmdzUGF0aCgpKTtcbiAgICBpZiAoIWZzLmV4aXN0c1N5bmMoc2V0dGluZ3NEaXIpKSB7XG4gICAgICAgIGZzLm1rZGlyU3luYyhzZXR0aW5nc0RpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVhZFNldHRpbmdzKCk6IE1DUFNlcnZlclNldHRpbmdzIHtcbiAgICB0cnkge1xuICAgICAgICBlbnN1cmVTZXR0aW5nc0RpcigpO1xuICAgICAgICBjb25zdCBzZXR0aW5nc0ZpbGUgPSBnZXRTZXR0aW5nc1BhdGgoKTtcbiAgICAgICAgaWYgKGZzLmV4aXN0c1N5bmMoc2V0dGluZ3NGaWxlKSkge1xuICAgICAgICAgICAgY29uc3QgY29udGVudCA9IGZzLnJlYWRGaWxlU3luYyhzZXR0aW5nc0ZpbGUsICd1dGY4Jyk7XG4gICAgICAgICAgICByZXR1cm4geyAuLi5ERUZBVUxUX1NFVFRJTkdTLCAuLi5KU09OLnBhcnNlKGNvbnRlbnQpIH07XG4gICAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byByZWFkIHNldHRpbmdzOicsIGUpO1xuICAgIH1cbiAgICByZXR1cm4gREVGQVVMVF9TRVRUSU5HUztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNhdmVTZXR0aW5ncyhzZXR0aW5nczogTUNQU2VydmVyU2V0dGluZ3MpOiB2b2lkIHtcbiAgICB0cnkge1xuICAgICAgICBlbnN1cmVTZXR0aW5nc0RpcigpO1xuICAgICAgICBjb25zdCBzZXR0aW5nc0ZpbGUgPSBnZXRTZXR0aW5nc1BhdGgoKTtcbiAgICAgICAgZnMud3JpdGVGaWxlU3luYyhzZXR0aW5nc0ZpbGUsIEpTT04uc3RyaW5naWZ5KHNldHRpbmdzLCBudWxsLCAyKSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gc2F2ZSBzZXR0aW5nczonLCBlKTtcbiAgICAgICAgdGhyb3cgZTtcbiAgICB9XG59XG5cbmV4cG9ydCB7IERFRkFVTFRfU0VUVElOR1MgfTsiXX0= \ No newline at end of file diff --git a/dist/test/manual-test.js b/dist/test/manual-test.js new file mode 100644 index 0000000..e8d3fce --- /dev/null +++ b/dist/test/manual-test.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.testSceneTools = testSceneTools; +exports.testAssetTools = testAssetTools; +exports.testProjectTools = testProjectTools; +exports.runAllTests = runAllTests; +/** + * 手动测试脚本 + * 可以在 Cocos Creator 控制台中执行测试 + */ +async function testSceneTools() { + console.log('=== Testing Scene Tools ==='); + try { + // 1. 获取场景信息 + console.log('1. Getting scene info...'); + const sceneInfo = await Editor.Message.request('scene', 'get-scene-info'); + console.log('Scene info:', sceneInfo); + // 2. 创建节点 + console.log('\n2. Creating test node...'); + const createResult = await Editor.Message.request('scene', 'create-node', { + name: 'TestNode_' + Date.now(), + type: 'cc.Node' + }); + console.log('Create result:', createResult); + if (createResult && createResult.uuid) { + const nodeUuid = createResult.uuid; + // 3. 查询节点 + console.log('\n3. Querying node...'); + const nodeInfo = await Editor.Message.request('scene', 'query-node', { + uuid: nodeUuid + }); + console.log('Node info:', nodeInfo); + // 4. 设置节点属性 + console.log('\n4. Setting node position...'); + await Editor.Message.request('scene', 'set-node-property', { + uuid: nodeUuid, + path: 'position', + value: { x: 100, y: 200, z: 0 } + }); + console.log('Position set successfully'); + // 5. 添加组件 + console.log('\n5. Adding Sprite component...'); + const addCompResult = await Editor.Message.request('scene', 'add-component', { + uuid: nodeUuid, + component: 'cc.Sprite' + }); + console.log('Component added:', addCompResult); + // 6. 查询组件 + console.log('\n6. Querying component...'); + const compInfo = await Editor.Message.request('scene', 'query-node-component', { + uuid: nodeUuid, + component: 'cc.Sprite' + }); + console.log('Component info:', compInfo); + // 7. 删除节点 + console.log('\n7. Removing test node...'); + await Editor.Message.request('scene', 'remove-node', { + uuid: nodeUuid + }); + console.log('Node removed successfully'); + } + } + catch (error) { + console.error('Test failed:', error); + } +} +async function testAssetTools() { + console.log('\n=== Testing Asset Tools ==='); + try { + // 1. 查询资源 + console.log('1. Querying image assets...'); + const assets = await Editor.Message.request('asset-db', 'query-assets', { + pattern: '**/*.png', + ccType: 'cc.ImageAsset' + }); + console.log('Found assets:', (assets === null || assets === void 0 ? void 0 : assets.length) || 0); + // 2. 获取资源信息 + console.log('\n2. Getting asset database info...'); + const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', { + uuid: 'db://assets' + }); + console.log('Asset info:', assetInfo); + } + catch (error) { + console.error('Test failed:', error); + } +} +async function testProjectTools() { + console.log('\n=== Testing Project Tools ==='); + try { + // 1. 获取项目信息 + console.log('1. Getting project info...'); + const projectInfo = await Editor.Message.request('project', 'query-info'); + console.log('Project info:', projectInfo); + // 2. 检查构建能力 + console.log('\n2. Checking build capability...'); + const canBuild = await Editor.Message.request('project', 'can-build'); + console.log('Can build:', canBuild); + } + catch (error) { + console.error('Test failed:', error); + } +} +async function runAllTests() { + console.log('Starting MCP Server Tools Test...\n'); + await testSceneTools(); + await testAssetTools(); + await testProjectTools(); + console.log('\n=== All tests completed ==='); +} +// 导出到全局,方便在控制台调用 +global.MCPTest = { + testSceneTools, + testAssetTools, + testProjectTools, + runAllTests +}; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFudWFsLXRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvdGVzdC9tYW51YWwtdGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQU9BLHdDQStEQztBQUVELHdDQXNCQztBQUVELDRDQWlCQztBQUVELGtDQVFDO0FBekhEOzs7R0FHRztBQUVJLEtBQUssVUFBVSxjQUFjO0lBQ2hDLE9BQU8sQ0FBQyxHQUFHLENBQUMsNkJBQTZCLENBQUMsQ0FBQztJQUUzQyxJQUFJLENBQUM7UUFDRCxZQUFZO1FBQ1osT0FBTyxDQUFDLEdBQUcsQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO1FBQ3hDLE1BQU0sU0FBUyxHQUFHLE1BQU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLGdCQUFnQixDQUFDLENBQUM7UUFDMUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxhQUFhLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFFdEMsVUFBVTtRQUNWLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUMsQ0FBQztRQUMxQyxNQUFNLFlBQVksR0FBRyxNQUFNLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxhQUFhLEVBQUU7WUFDdEUsSUFBSSxFQUFFLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFO1lBQzlCLElBQUksRUFBRSxTQUFTO1NBQ2xCLENBQUMsQ0FBQztRQUNILE9BQU8sQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFFNUMsSUFBSSxZQUFZLElBQUksWUFBWSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ3BDLE1BQU0sUUFBUSxHQUFHLFlBQVksQ0FBQyxJQUFJLENBQUM7WUFFbkMsVUFBVTtZQUNWLE9BQU8sQ0FBQyxHQUFHLENBQUMsdUJBQXVCLENBQUMsQ0FBQztZQUNyQyxNQUFNLFFBQVEsR0FBRyxNQUFNLE1BQU0sQ0FBQyxPQUFPLENBQUMs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QUNkLGNBQWM7SUFDZCxnQkFBZ0I7SUFDaEIsV0FBVztDQUNkLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJkZWNsYXJlIGNvbnN0IEVkaXRvcjogYW55O1xuXG4vKipcbiAqIOaJi+WKqOa1i+ivleiEmuacrFxuICog5Y+v5Lul5ZyoIENvY29zIENyZWF0b3Ig5o6n5Yi25Y+w5Lit5omn6KGM5rWL6K+VXG4gKi9cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHRlc3RTY2VuZVRvb2xzKCkge1xuICAgIGNvbnNvbGUubG9nKCc9PT0gVGVzdGluZyBTY2VuZSBUb29scyA9PT0nKTtcbiAgICBcbiAgICB0cnkge1xuICAgICAgICAvLyAxLiDojrflj5blnLrmma/kv6Hmga9cbiAgICAgICAgY29uc29sZS5sb2coJzEuIEdldHRpbmcgc2NlbmUgaW5mby4uLicpO1xuICAgICAgICBjb25zdCBzY2VuZUluZm8gPSBhd2FpdCBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdnZXQtc2NlbmUtaW5mbycpO1xuICAgICAgICBjb25zb2xlLmxvZygnU2NlbmUgaW5mbzonLCBzY2VuZUluZm8pO1xuICAgICAgICBcbiAgICAgICAgLy8gMi4g5Yib5bu66IqC54K5XG4gICAgICAgIGNvbnNvbGUubG9nKCdcXG4yLiBDcmVhdGluZyB0ZXN0IG5vZGUuLi4nKTtcbiAgICAgICAgY29uc3QgY3JlYXRlUmVzdWx0ID0gYXdhaXQgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnY3JlYXRlLW5vZGUnLCB7XG4gICAgICAgICAgICBuYW1lOiAnVGVzdE5vZGVfJyArIERhdGUubm93KCksXG4gICAgICAgICAgICB0eXBlOiAnY2MuTm9kZSdcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnNvbGUubG9nKCdDcmVhdGUgcmVzdWx0OicsIGNyZWF0ZVJlc3VsdCk7XG4gICAgICAgIFxuICAgICAgICBpZiAoY3JlYXRlUmVzdWx0ICYmIGNyZWF0ZVJlc3VsdC51dWlkKSB7XG4gICAgICAgICAgICBjb25zdCBub2RlVXVpZCA9IGNyZWF0ZVJlc3VsdC51dWlkO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyAzLiDmn6Xor6LoioLngrlcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdcXG4zLiBRdWVyeWluZyBub2RlLi4uJyk7XG4gICAgICAgICAgICBjb25zdCBub2RlSW5mbyA9IGF3YWl0IEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ3F1ZXJ5LW5vZGUnLCB7XG4gICAgICAgICAgICAgICAgdXVpZDogbm9kZVV1aWRcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ05vZGUgaW5mbzonLCBub2RlSW5mbyk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIDQuIOiuvue9ruiKgueCueWxnuaAp1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1xcbjQuIFNldHRpbmcgbm9kZSBwb3NpdGlvbi4uLicpO1xuICAgICAgICAgICAgYXdhaXQgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnc2V0LW5vZGUtcHJvcGVydHknLCB7XG4gICAgICAgICAgICAgICAgdXVpZDogbm9kZVV1aWQsXG4gICAgICAgICAgICAgICAgcGF0aDogJ3Bvc2l0aW9uJyxcbiAgICAgICAgICAgICAgICB2YWx1ZTogeyB4OiAxMDAsIHk6IDIwMCwgejogMCB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdQb3NpdGlvbiBzZXQgc3VjY2Vzc2Z1bGx5Jyk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIDUuIOa3u+WKoOe7hOS7tlxuICAgICAgICAgICAgY29uc29sZS5sb2coJ1xcbjUuIEFkZGluZyBTcHJpdGUgY29tcG9uZW50Li4uJyk7XG4gICAgICAgICAgICBjb25zdCBhZGRDb21wUmVzdWx0ID0gYXdhaXQgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnYWRkLWNvbXBvbmVudCcsIHtcbiAgICAgICAgICAgICAgICB1dWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICBjb21wb25lbnQ6ICdjYy5TcHJpdGUnXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdDb21wb25lbnQgYWRkZWQ6JywgYWRkQ29tcFJlc3VsdCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIDYuIOafpeivoue7hOS7tlxuICAgICAgICAgICAgY29uc29sZS5sb2coJ1xcbjYuIFF1ZXJ5aW5nIGNvbXBvbmVudC4uLicpO1xuICAgICAgICAgICAgY29uc3QgY29tcEluZm8gPSBhd2FpdCBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdxdWVyeS1ub2RlLWNvbXBvbmVudCcsIHtcbiAgICAgICAgICAgICAgICB1dWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICBjb21wb25lbnQ6ICdjYy5TcHJpdGUnXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdDb21wb25lbnQgaW5mbzonLCBjb21wSW5mbyk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIDcuIOWIoOmZpOiKgueCuVxuICAgICAgICAgICAgY29uc29sZS5sb2coJ1xcbjcuIFJlbW92aW5nIHRlc3Qgbm9kZS4uLicpO1xuICAgICAgICAgICAgYXdhaXQgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAncmVtb3ZlLW5vZGUnLCB7XG4gICAgICAgICAgICAgICAgdXVpZDogbm9kZVV1aWRcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ05vZGUgcmVtb3ZlZCBzdWNjZXNzZnVsbHknKTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdUZXN0IGZhaWxlZDonLCBlcnJvcik7XG4gICAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdGVzdEFzc2V0VG9vbHMoKSB7XG4gICAgY29uc29sZS5sb2coJ1xcbj09PSBUZXN0aW5nIEFzc2V0IFRvb2xzID09PScpO1xuICAgIFxuICAgIHRyeSB7XG4gICAgICAgIC8vIDEuIOafpeivoui1hOa6kFxuICAgICAgICBjb25zb2xlLmxvZygnMS4gUXVlcnlpbmcgaW1hZ2UgYXNzZXRzLi4uJyk7XG4gICAgICAgIGNvbnN0IGFzc2V0cyA9IGF3YWl0IEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ3F1ZXJ5LWFzc2V0cycsIHtcbiAgICAgICAgICAgIHBhdHRlcm46ICcqKi8qLnBuZycsXG4gICAgICAgICAgICBjY1R5cGU6ICdjYy5JbWFnZUFzc2V0J1xuICAgICAgICB9KTtcbiAgICAgICAgY29uc29sZS5sb2coJ0ZvdW5kIGFzc2V0czonLCBhc3NldHM/Lmxlbmd0aCB8fCAwKTtcbiAgICAgICAgXG4gICAgICAgIC8vIDIuIOiOt+WPlui1hOa6kOS/oeaBr1xuICAgICAgICBjb25zb2xlLmxvZygnXFxuMi4gR2V0dGluZyBhc3NldCBkYXRhYmFzZSBpbmZvLi4uJyk7XG4gICAgICAgIGNvbnN0IGFzc2V0SW5mbyA9IGF3YWl0IEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ3F1ZXJ5LWFzc2V0LWluZm8nLCB7XG4gICAgICAgICAgICB1dWlkOiAnZGI6Ly9hc3NldHMnXG4gICAgICAgIH0pO1xuICAgICAgICBjb25zb2xlLmxvZygnQXNzZXQgaW5mbzonLCBhc3NldEluZm8pO1xuICAgICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdUZXN0IGZhaWxlZDonLCBlcnJvcik7XG4gICAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdGVzdFByb2plY3RUb29scygpIHtcbiAgICBjb25zb2xlLmxvZygnXFxuPT09IFRlc3RpbmcgUHJvamVjdCBUb29scyA9PT0nKTtcbiAgICBcbiAgICB0cnkge1xuICAgICAgICAvLyAxLiDojrflj5bpobnnm67kv6Hmga9cbiAgICAgICAgY29uc29sZS5sb2coJzEuIEdldHRpbmcgcHJvamVjdCBpbmZvLi4uJyk7XG4gICAgICAgIGNvbnN0IHByb2plY3RJbmZvID0gYXdhaXQgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgncHJvamVjdCcsICdxdWVyeS1pbmZvJyk7XG4gICAgICAgIGNvbnNvbGUubG9nKCdQcm9qZWN0IGluZm86JywgcHJvamVjdEluZm8pO1xuICAgICAgICBcbiAgICAgICAgLy8gMi4g5qOA5p+l5p6E5bu66IO95YqbXG4gICAgICAgIGNvbnNvbGUubG9nKCdcXG4yLiBDaGVja2luZyBidWlsZCBjYXBhYmlsaXR5Li4uJyk7XG4gICAgICAgIGNvbnN0IGNhbkJ1aWxkID0gYXdhaXQgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgncHJvamVjdCcsICdjYW4tYnVpbGQnKTtcbiAgICAgICAgY29uc29sZS5sb2coJ0NhbiBidWlsZDonLCBjYW5CdWlsZCk7XG4gICAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ1Rlc3QgZmFpbGVkOicsIGVycm9yKTtcbiAgICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBydW5BbGxUZXN0cygpIHtcbiAgICBjb25zb2xlLmxvZygnU3RhcnRpbmcgTUNQIFNlcnZlciBUb29scyBUZXN0Li4uXFxuJyk7XG4gICAgXG4gICAgYXdhaXQgdGVzdFNjZW5lVG9vbHMoKTtcbiAgICBhd2FpdCB0ZXN0QXNzZXRUb29scygpO1xuICAgIGF3YWl0IHRlc3RQcm9qZWN0VG9vbHMoKTtcbiAgICBcbiAgICBjb25zb2xlLmxvZygnXFxuPT09IEFsbCB0ZXN0cyBjb21wbGV0ZWQgPT09Jyk7XG59XG5cbi8vIOWvvOWHuuWIsOWFqOWxgO+8jOaWueS+v+WcqOaOp+WItuWPsOiwg+eUqFxuKGdsb2JhbCBhcyBhbnkpLk1DUFRlc3QgPSB7XG4gICAgdGVzdFNjZW5lVG9vbHMsXG4gICAgdGVzdEFzc2V0VG9vbHMsXG4gICAgdGVzdFByb2plY3RUb29scyxcbiAgICBydW5BbGxUZXN0c1xufTsiXX0= \ No newline at end of file diff --git a/dist/test/mcp-tool-tester.js b/dist/test/mcp-tool-tester.js new file mode 100644 index 0000000..5d26704 --- /dev/null +++ b/dist/test/mcp-tool-tester.js @@ -0,0 +1,217 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MCPToolTester = void 0; +/** + * MCP 工具测试器 - 直接测试通过 WebSocket 的 MCP 工具 + */ +class MCPToolTester { + constructor() { + this.ws = null; + this.messageId = 0; + this.responseHandlers = new Map(); + } + async connect(port) { + return new Promise((resolve) => { + try { + this.ws = new WebSocket(`ws://localhost:${port}`); + this.ws.onopen = () => { + console.log('WebSocket 连接成功'); + resolve(true); + }; + this.ws.onerror = (error) => { + console.error('WebSocket 连接错误:', error); + resolve(false); + }; + this.ws.onmessage = (event) => { + try { + const response = JSON.parse(event.data); + if (response.id && this.responseHandlers.has(response.id)) { + const handler = this.responseHandlers.get(response.id); + this.responseHandlers.delete(response.id); + handler === null || handler === void 0 ? void 0 : handler(response); + } + } + catch (error) { + console.error('处理响应时出错:', error); + } + }; + } + catch (error) { + console.error('创建 WebSocket 时出错:', error); + resolve(false); + } + }); + } + async callTool(tool, args = {}) { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + throw new Error('WebSocket 未连接'); + } + return new Promise((resolve, reject) => { + const id = ++this.messageId; + const request = { + jsonrpc: '2.0', + id, + method: 'tools/call', + params: { + name: tool, + arguments: args + } + }; + const timeout = setTimeout(() => { + this.responseHandlers.delete(id); + reject(new Error('请求超时')); + }, 10000); + this.responseHandlers.set(id, (response) => { + clearTimeout(timeout); + if (response.error) { + reject(new Error(response.error.message)); + } + else { + resolve(response.result); + } + }); + this.ws.send(JSON.stringify(request)); + }); + } + async listTools() { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + throw new Error('WebSocket 未连接'); + } + return new Promise((resolve, reject) => { + const id = ++this.messageId; + const request = { + jsonrpc: '2.0', + id, + method: 'tools/list' + }; + const timeout = setTimeout(() => { + this.responseHandlers.delete(id); + reject(new Error('请求超时')); + }, 10000); + this.responseHandlers.set(id, (response) => { + clearTimeout(timeout); + if (response.error) { + reject(new Error(response.error.message)); + } + else { + resolve(response.result); + } + }); + this.ws.send(JSON.stringify(request)); + }); + } + async testMCPTools() { + var _a, _b; + console.log('\n=== 测试 MCP 工具(通过 WebSocket)==='); + try { + // 0. 获取工具列表 + console.log('\n0. 获取工具列表...'); + const toolsList = await this.listTools(); + console.log(`找到 ${((_a = toolsList.tools) === null || _a === void 0 ? void 0 : _a.length) || 0} 个工具:`); + if (toolsList.tools) { + for (const tool of toolsList.tools.slice(0, 10)) { // 只显示前10个 + console.log(` - ${tool.name}: ${tool.description}`); + } + if (toolsList.tools.length > 10) { + console.log(` ... 还有 ${toolsList.tools.length - 10} 个工具`); + } + } + // 1. 测试场景工具 + console.log('\n1. 测试当前场景信息...'); + const sceneInfo = await this.callTool('scene_get_current_scene'); + console.log('场景信息:', JSON.stringify(sceneInfo).substring(0, 100) + '...'); + // 2. 测试场景列表 + console.log('\n2. 测试场景列表...'); + const sceneList = await this.callTool('scene_get_scene_list'); + console.log('场景列表:', JSON.stringify(sceneList).substring(0, 100) + '...'); + // 3. 测试节点创建 + console.log('\n3. 测试创建节点...'); + const createResult = await this.callTool('node_create_node', { + name: 'MCPTestNode_' + Date.now(), + nodeType: 'cc.Node', + position: { x: 0, y: 0, z: 0 } + }); + console.log('创建节点结果:', createResult); + // 解析创建节点的结果 + let nodeUuid = null; + if (createResult.content && createResult.content[0] && createResult.content[0].text) { + try { + const resultData = JSON.parse(createResult.content[0].text); + if (resultData.success && resultData.data && resultData.data.uuid) { + nodeUuid = resultData.data.uuid; + console.log('成功获取节点UUID:', nodeUuid); + } + } + catch (e) { + } + } + if (nodeUuid) { + // 4. 测试查询节点 + console.log('\n4. 测试查询节点...'); + const queryResult = await this.callTool('node_get_node_info', { + uuid: nodeUuid + }); + console.log('节点信息:', JSON.stringify(queryResult).substring(0, 100) + '...'); + // 5. 测试删除节点 + console.log('\n5. 测试删除节点...'); + const removeResult = await this.callTool('node_delete_node', { + uuid: nodeUuid + }); + console.log('删除结果:', removeResult); + } + else { + console.log('无法从创建结果获取节点UUID,尝试通过名称查找...'); + // 备用方案:通过名称查找刚创建的节点 + const findResult = await this.callTool('node_find_node_by_name', { + name: 'MCPTestNode_' + Date.now() + }); + if (findResult.content && findResult.content[0] && findResult.content[0].text) { + try { + const findData = JSON.parse(findResult.content[0].text); + if (findData.success && findData.data && findData.data.uuid) { + nodeUuid = findData.data.uuid; + console.log('通过名称查找成功获取UUID:', nodeUuid); + } + } + catch (e) { + } + } + if (!nodeUuid) { + console.log('所有方式都无法获取节点UUID,跳过后续节点操作测试'); + } + } + // 6. 测试项目工具 + console.log('\n6. 测试项目信息...'); + const projectInfo = await this.callTool('project_get_project_info'); + console.log('项目信息:', JSON.stringify(projectInfo).substring(0, 100) + '...'); + // 7. 测试预制体工具 + console.log('\n7. 测试预制体列表...'); + const prefabResult = await this.callTool('prefab_get_prefab_list', { + folder: 'db://assets' + }); + console.log('找到预制体:', ((_b = prefabResult.data) === null || _b === void 0 ? void 0 : _b.length) || 0); + // 8. 测试组件工具 + console.log('\n8. 测试可用组件...'); + const componentsResult = await this.callTool('component_get_available_components'); + console.log('可用组件:', JSON.stringify(componentsResult).substring(0, 100) + '...'); + // 9. 测试调试工具 + console.log('\n9. 测试编辑器信息...'); + const editorInfo = await this.callTool('debug_get_editor_info'); + console.log('编辑器信息:', JSON.stringify(editorInfo).substring(0, 100) + '...'); + } + catch (error) { + console.error('MCP 工具测试失败:', error); + } + } + disconnect() { + if (this.ws) { + this.ws.close(); + this.ws = null; + } + this.responseHandlers.clear(); + } +} +exports.MCPToolTester = MCPToolTester; +// 导出到全局方便测试 +global.MCPToolTester = MCPToolTester; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWNwLXRvb2wtdGVzdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc291cmNlL3Rlc3QvbWNwLXRvb2wtdGVzdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBOztHQUVHO0FBQ0gsTUFBYSxhQUFhO0lBQTFCO1FBQ1ksT0FBRSxHQUFxQixJQUFJLENBQUM7UUFDNUIsY0FBUyxHQUFHLENBQUMsQ0FBQztRQUNkLHFCQUFnQixHQUFHLElBQUksR0FBRyxFQUFtQyxDQUFDO0lBK04xRSxDQUFDO0lBN05HLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBWTtRQUN0QixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDM0IsSUFBSSxDQUFDO2dCQUNELElBQUksQ0FBQyxFQUFFLEdBQUcsSUFBSSxTQUFTLENBQUMsa0JBQWtCLElBQUksRUFBRSxDQUFDLENBQUM7Z0JBRWxELElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRTtvQkFDbEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO29CQUM5QixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ2xCLENBQUMsQ0FBQztnQkFFRixJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sR0FBRyxDQUFDLEtBQUssRUFBRSxFQUFFO29CQUN4QixPQUFPLENBQUMsS0FBSyxDQUFDLGlCQUFpQixFQUFFLEtBQUssQ0FBQyxDQUFDO29CQUN4QyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ25CLENBQUMsQ0FBQztnQkFFRixJQUFJLENBQUMsRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLEtBQUssRUFBRSxFQUFFO29CQUMxQixJQUFJLENBQUM7d0JBQ0QsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7d0JBQ3hDLElBQUksUUFBUSxDQUFDLEVBQUUsSUFBSSxJ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QUM7WUFDOUIsTUFBTSxnQkFBZ0IsR0FBRyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsb0NBQW9DLENBQUMsQ0FBQztZQUNuRixPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLGdCQUFnQixDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUMsQ0FBQztZQUVqRixZQUFZO1lBQ1osT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1lBQy9CLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO1lBQ2hFLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUMsQ0FBQztRQUVoRixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNiLE9BQU8sQ0FBQyxLQUFLLENBQUMsYUFBYSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ3hDLENBQUM7SUFDTCxDQUFDO0lBRUQsVUFBVTtRQUNOLElBQUksSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ1YsSUFBSSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNoQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQztRQUNuQixDQUFDO1FBQ0QsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2xDLENBQUM7Q0FDSjtBQWxPRCxzQ0FrT0M7QUFFRCxZQUFZO0FBQ1gsTUFBYyxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJkZWNsYXJlIGNvbnN0IEVkaXRvcjogYW55O1xuXG4vKipcbiAqIE1DUCDlt6XlhbfmtYvor5XlmaggLSDnm7TmjqXmtYvor5XpgJrov4cgV2ViU29ja2V0IOeahCBNQ1Ag5bel5YW3XG4gKi9cbmV4cG9ydCBjbGFzcyBNQ1BUb29sVGVzdGVyIHtcbiAgICBwcml2YXRlIHdzOiBXZWJTb2NrZXQgfCBudWxsID0gbnVsbDtcbiAgICBwcml2YXRlIG1lc3NhZ2VJZCA9IDA7XG4gICAgcHJpdmF0ZSByZXNwb25zZUhhbmRsZXJzID0gbmV3IE1hcDxudW1iZXIsIChyZXNwb25zZTogYW55KSA9PiB2b2lkPigpO1xuXG4gICAgYXN5bmMgY29ubmVjdChwb3J0OiBudW1iZXIpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHRoaXMud3MgPSBuZXcgV2ViU29ja2V0KGB3czovL2xvY2FsaG9zdDoke3BvcnR9YCk7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgdGhpcy53cy5vbm9wZW4gPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdXZWJTb2NrZXQg6L+e5o6l5oiQ5YqfJyk7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUodHJ1ZSk7XG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICB0aGlzLndzLm9uZXJyb3IgPSAoZXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcignV2ViU29ja2V0IOi/nuaOpemUmeivrzonLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoZmFsc2UpO1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgdGhpcy53cy5vbm1lc3NhZ2UgPSAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gSlNPTi5wYXJzZShldmVudC5kYXRhKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZXNwb25zZS5pZCAmJiB0aGlzLnJlc3BvbnNlSGFuZGxlcnMuaGFzKHJlc3BvbnNlLmlkKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGhhbmRsZXIgPSB0aGlzLnJlc3BvbnNlSGFuZGxlcnMuZ2V0KHJlc3BvbnNlLmlkKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnJlc3BvbnNlSGFuZGxlcnMuZGVsZXRlKHJlc3BvbnNlLmlkKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBoYW5kbGVyPy4ocmVzcG9uc2UpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcign5aSE55CG5ZON5bqU5pe25Ye66ZSZOicsIGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ+WIm+W7uiBXZWJTb2NrZXQg5pe25Ye66ZSZOicsIGVycm9yKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKGZhbHNlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgYXN5bmMgY2FsbFRvb2wodG9vbDogc3RyaW5nLCBhcmdzOiBhbnkgPSB7fSk6IFByb21pc2U8YW55PiB7XG4gICAgICAgIGlmICghdGhpcy53cyB8fCB0aGlzLndzLnJlYWR5U3RhdGUgIT09IFdlYlNvY2tldC5PUEVOKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1dlYlNvY2tldCDmnKrov57mjqUnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBpZCA9ICsrdGhpcy5tZXNzYWdlSWQ7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0ge1xuICAgICAgICAgICAgICAgIGpzb25ycGM6ICcyLjAnLFxuICAgICAgICAgICAgICAgIGlkLFxuICAgICAgICAgICAgICAgIG1ldGhvZDogJ3Rvb2xzL2NhbGwnLFxuICAgICAgICAgICAgICAgIHBhcmFtczoge1xuICAgICAgICAgICAgICAgICAgICBuYW1lOiB0b29sLFxuICAgICAgICAgICAgICAgICAgICBhcmd1bWVudHM6IGFyZ3NcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBjb25zdCB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5yZXNwb25zZUhhbmRsZXJzLmRlbGV0ZShpZCk7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcign6K+35rGC6LaF5pe2JykpO1xuICAgICAgICAgICAgfSwgMTAwMDApO1xuXG4gICAgICAgICAgICB0aGlzLnJlc3BvbnNlSGFuZGxlcnMuc2V0KGlkLCAocmVzcG9uc2UpID0+IHtcbiAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dCk7XG4gICAgICAgICAgICAgICAgaWYgKHJlc3BvbnNlLmVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IocmVzcG9uc2UuZXJyb3IubWVzc2FnZSkpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UucmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgdGhpcy53cyEuc2VuZChKU09OLnN0cmluZ2lmeShyZXF1ZXN0KSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIGFzeW5jIGxpc3RUb29scygpOiBQcm9taXNlPGFueT4ge1xuICAgICAgICBpZiAoIXRoaXMud3MgfHwgdGhpcy53cy5yZWFkeVN0YXRlICE9PSBXZWJTb2NrZXQuT1BFTikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdXZWJTb2NrZXQg5pyq6L+e5o6lJyk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgaWQgPSArK3RoaXMubWVzc2FnZUlkO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IHtcbiAgICAgICAgICAgICAgICBqc29ucnBjOiAnMi4wJyxcbiAgICAgICAgICAgICAgICBpZCxcbiAgICAgICAgICAgICAgICBtZXRob2Q6ICd0b29scy9saXN0J1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgY29uc3QgdGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMucmVzcG9uc2VIYW5kbGVycy5kZWxldGUoaWQpO1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoJ+ivt+axgui2heaXticpKTtcbiAgICAgICAgICAgIH0sIDEwMDAwKTtcblxuICAgICAgICAgICAgdGhpcy5yZXNwb25zZUhhbmRsZXJzLnNldChpZCwgKHJlc3BvbnNlKSA9PiB7XG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgICAgICAgICAgICAgIGlmIChyZXNwb25zZS5lcnJvcikge1xuICAgICAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKHJlc3BvbnNlLmVycm9yLm1lc3NhZ2UpKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlLnJlc3VsdCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHRoaXMud3MhLnNlbmQoSlNPTi5zdHJpbmdpZnkocmVxdWVzdCkpO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBhc3luYyB0ZXN0TUNQVG9vbHMoKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdcXG49PT0g5rWL6K+VIE1DUCDlt6XlhbfvvIjpgJrov4cgV2ViU29ja2V077yJPT09Jyk7XG4gICAgICAgIFxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gMC4g6I635Y+W5bel5YW35YiX6KGoXG4gICAgICAgICAgICBjb25zb2xlLmxvZygnXFxuMC4g6I635Y+W5bel5YW35YiX6KGoLi4uJyk7XG4gICAgICAgICAgICBjb25zdCB0b29sc0xpc3QgPSBhd2FpdCB0aGlzLmxpc3RUb29scygpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coYOaJvuWIsCAke3Rvb2xzTGlzdC50b29scz8ubGVuZ3RoIHx8IDB9IOS4quW3peWFtzpgKTtcbiAgICAgICAgICAgIGlmICh0b29sc0xpc3QudG9vbHMpIHtcbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IHRvb2wgb2YgdG9vbHNMaXN0LnRvb2xzLnNsaWNlKDAsIDEwKSkgeyAvLyDlj6rmmL7npLrliY0xMOS4qlxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhgICAtICR7dG9vbC5uYW1lfTogJHt0b29sLmRlc2NyaXB0aW9ufWApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAodG9vbHNMaXN0LnRvb2xzLmxlbmd0aCA+IDEwKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGAgIC4uLiDov5jmnIkgJHt0b29sc0xpc3QudG9vbHMubGVuZ3RoIC0gMTB9IOS4quW3peWFt2ApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8gMS4g5rWL6K+V5Zy65pmv5bel5YW3XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnXFxuMS4g5rWL6K+V5b2T5YmN5Zy65pmv5L+h5oGvLi4uJyk7XG4gICAgICAgICAgICBjb25zdCBzY2VuZUluZm8gPSBhd2FpdCB0aGlzLmNhbGxUb29sKCdzY2VuZV9nZXRfY3VycmVudF9zY2VuZScpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ+WcuuaZr+S/oeaBrzonLCBKU09OLnN0cmluZ2lmeShzY2VuZUluZm8pLnN1YnN0cmluZygwLCAxMDApICsgJy4uLicpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyAyLiDmtYvor5XlnLrmma/liJfooahcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdcXG4yLiDmtYvor5XlnLrmma/liJfooaguLi4nKTtcbiAgICAgICAgICAgIGNvbnN0IHNjZW5lTGlzdCA9IGF3YWl0IHRoaXMuY2FsbFRvb2woJ3NjZW5lX2dldF9zY2VuZV9saXN0Jyk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygn5Zy65pmv5YiX6KGoOicsIEpTT04uc3RyaW5naWZ5KHNjZW5lTGlzdCkuc3Vic3RyaW5nKDAsIDEwMCkgKyAnLi4uJyk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIDMuIOa1i+ivleiKgueCueWIm+W7ulxuICAgICAgICAgICAgY29uc29sZS5sb2coJ1xcbjMuIOa1i+ivleWIm+W7uuiKgueCuS4uLicpO1xuICAgICAgICAgICAgY29uc3QgY3JlYXRlUmVzdWx0ID0gYXdhaXQgdGhpcy5jYWxsVG9vbCgnbm9kZV9jcmVhdGVfbm9kZScsIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnTUNQVGVzdE5vZGVfJyArIERhdGUubm93KCksXG4gICAgICAgICAgICAgICAgbm9kZVR5cGU6ICdjYy5Ob2RlJyxcbiAgICAgICAgICAgICAgICBwb3NpdGlvbjogeyB4OiAwLCB5OiAwLCB6OiAwIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ+WIm+W7uuiKgueCuee7k+aenDonLCBjcmVhdGVSZXN1bHQpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDop6PmnpDliJvlu7roioLngrnnmoTnu5PmnpxcbiAgICAgICAgICAgIGxldCBub2RlVXVpZDogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gICAgICAgICAgICBpZiAoY3JlYXRlUmVzdWx0LmNvbnRlbnQgJiYgY3JlYXRlUmVzdWx0LmNvbnRlbnRbMF0gJiYgY3JlYXRlUmVzdWx0LmNvbnRlbnRbMF0udGV4dCkge1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VsdERhdGEgPSBKU09OLnBhcnNlKGNyZWF0ZVJlc3VsdC5jb250ZW50WzBdLnRleHQpO1xuICAgICAgICAgICAgICAgICAgICBpZiAocmVzdWx0RGF0YS5zdWNjZXNzICYmIHJlc3VsdERhdGEuZGF0YSAmJiByZXN1bHREYXRhLmRhdGEudXVpZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgbm9kZVV1aWQgPSByZXN1bHREYXRhLmRhdGEudXVpZDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCfmiJDlip/ojrflj5boioLngrlVVUlEOicsIG5vZGVVdWlkKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmIChub2RlVXVpZCkge1xuICAgICAgICAgICAgICAgIC8vIDQuIOa1i+ivleafpeivouiKgueCuVxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCdcXG40LiDmtYvor5Xmn6Xor6LoioLngrkuLi4nKTtcbiAgICAgICAgICAgICAgICBjb25zdCBxdWVyeVJlc3VsdCA9IGF3YWl0IHRoaXMuY2FsbFRvb2woJ25vZGVfZ2V0X25vZGVfaW5mbycsIHtcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogbm9kZVV1aWRcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygn6IqC54K55L+h5oGvOicsIEpTT04uc3RyaW5naWZ5KHF1ZXJ5UmVzdWx0KS5zdWJzdHJpbmcoMCwgMTAwKSArICcuLi4nKTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAvLyA1LiDmtYvor5XliKDpmaToioLngrlcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygnXFxuNS4g5rWL6K+V5Yig6Zmk6IqC54K5Li4uJyk7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVtb3ZlUmVzdWx0ID0gYXdhaXQgdGhpcy5jYWxsVG9vbCgnbm9kZV9kZWxldGVfbm9kZScsIHtcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogbm9kZVV1aWRcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygn5Yig6Zmk57uT5p6cOicsIHJlbW92ZVJlc3VsdCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCfml6Dms5Xku47liJvlu7rnu5Pmnpzojrflj5boioLngrlVVUlE77yM5bCd6K+V6YCa6L+H5ZCN56ew5p+l5om+Li4uJyk7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgLy8g5aSH55So5pa55qGI77ya6YCa6L+H5ZCN56ew5p+l5om+5Yia5Yib5bu655qE6IqC54K5XG4gICAgICAgICAgICAgICAgY29uc3QgZmluZFJlc3VsdCA9IGF3YWl0IHRoaXMuY2FsbFRvb2woJ25vZGVfZmluZF9ub2RlX2J5X25hbWUnLCB7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6ICdNQ1BUZXN0Tm9kZV8nICsgRGF0ZS5ub3coKVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIGlmIChmaW5kUmVzdWx0LmNvbnRlbnQgJiYgZmluZFJlc3VsdC5jb250ZW50WzBdICYmIGZpbmRSZXN1bHQuY29udGVudFswXS50ZXh0KSB7XG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBmaW5kRGF0YSA9IEpTT04ucGFyc2UoZmluZFJlc3VsdC5jb250ZW50WzBdLnRleHQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGZpbmREYXRhLnN1Y2Nlc3MgJiYgZmluZERhdGEuZGF0YSAmJiBmaW5kRGF0YS5kYXRhLnV1aWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBub2RlVXVpZCA9IGZpbmREYXRhLmRhdGEudXVpZDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZygn6YCa6L+H5ZCN56ew5p+l5om+5oiQ5Yqf6I635Y+WVVVJRDonLCBub2RlVXVpZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBpZiAoIW5vZGVVdWlkKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKCfmiYDmnInmlrnlvI/pg73ml6Dms5Xojrflj5boioLngrlVVUlE77yM6Lez6L+H5ZCO57ut6IqC54K55pON5L2c5rWL6K+VJyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyA2LiDmtYvor5Xpobnnm67lt6XlhbdcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdcXG42LiDmtYvor5Xpobnnm67kv6Hmga8uLi4nKTtcbiAgICAgICAgICAgIGNvbnN0IHByb2plY3RJbmZvID0gYXdhaXQgdGhpcy5jYWxsVG9vbCgncHJvamVjdF9nZXRfcHJvamVjdF9pbmZvJyk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygn6aG555uu5L+h5oGvOicsIEpTT04uc3RyaW5naWZ5KHByb2plY3RJbmZvKS5zdWJzdHJpbmcoMCwgMTAwKSArICcuLi4nKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8gNy4g5rWL6K+V6aKE5Yi25L2T5bel5YW3XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnXFxuNy4g5rWL6K+V6aKE5Yi25L2T5YiX6KGoLi4uJyk7XG4gICAgICAgICAgICBjb25zdCBwcmVmYWJSZXN1bHQgPSBhd2FpdCB0aGlzLmNhbGxUb29sKCdwcmVmYWJfZ2V0X3ByZWZhYl9saXN0Jywge1xuICAgICAgICAgICAgICAgIGZvbGRlcjogJ2RiOi8vYXNzZXRzJ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygn5om+5Yiw6aKE5Yi25L2TOicsIHByZWZhYlJlc3VsdC5kYXRhPy5sZW5ndGggfHwgMCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIDguIOa1i+ivlee7hOS7tuW3peWFt1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1xcbjguIOa1i+ivleWPr+eUqOe7hOS7ti4uLicpO1xuICAgICAgICAgICAgY29uc3QgY29tcG9uZW50c1Jlc3VsdCA9IGF3YWl0IHRoaXMuY2FsbFRvb2woJ2NvbXBvbmVudF9nZXRfYXZhaWxhYmxlX2NvbXBvbmVudHMnKTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCflj6/nlKjnu4Tku7Y6JywgSlNPTi5zdHJpbmdpZnkoY29tcG9uZW50c1Jlc3VsdCkuc3Vic3RyaW5nKDAsIDEwMCkgKyAnLi4uJyk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIDkuIOa1i+ivleiwg+ivleW3peWFt1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1xcbjkuIOa1i+ivlee8lui+keWZqOS/oeaBry4uLicpO1xuICAgICAgICAgICAgY29uc3QgZWRpdG9ySW5mbyA9IGF3YWl0IHRoaXMuY2FsbFRvb2woJ2RlYnVnX2dldF9lZGl0b3JfaW5mbycpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ+e8lui+keWZqOS/oeaBrzonLCBKU09OLnN0cmluZ2lmeShlZGl0b3JJbmZvKS5zdWJzdHJpbmcoMCwgMTAwKSArICcuLi4nKTtcbiAgICAgICAgICAgIFxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignTUNQIOW3peWFt+a1i+ivleWksei0pTonLCBlcnJvcik7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBkaXNjb25uZWN0KCkge1xuICAgICAgICBpZiAodGhpcy53cykge1xuICAgICAgICAgICAgdGhpcy53cy5jbG9zZSgpO1xuICAgICAgICAgICAgdGhpcy53cyA9IG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5yZXNwb25zZUhhbmRsZXJzLmNsZWFyKCk7XG4gICAgfVxufVxuXG4vLyDlr7zlh7rliLDlhajlsYDmlrnkvr/mtYvor5VcbihnbG9iYWwgYXMgYW55KS5NQ1BUb29sVGVzdGVyID0gTUNQVG9vbFRlc3RlcjsiXX0= \ No newline at end of file diff --git a/dist/test/tool-tester.js b/dist/test/tool-tester.js new file mode 100644 index 0000000..153795c --- /dev/null +++ b/dist/test/tool-tester.js @@ -0,0 +1,134 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ToolTester = void 0; +class ToolTester { + constructor() { + this.results = []; + } + async runTest(tool, method, params) { + const startTime = Date.now(); + const result = { + tool, + method, + success: false, + time: 0 + }; + try { + const response = await Editor.Message.request(tool, method, params); + result.success = true; + result.result = response; + } + catch (error) { + result.success = false; + result.error = error instanceof Error ? error.message : String(error); + } + result.time = Date.now() - startTime; + this.results.push(result); + return result; + } + async testSceneOperations() { + console.log('Testing Scene Operations...'); + // Test node creation (this is the main scene operation that works) + const createResult = await this.runTest('scene', 'create-node', { + name: 'TestNode', + type: 'cc.Node' + }); + if (createResult.success && createResult.result) { + const nodeUuid = createResult.result; + // Test query node info + await this.runTest('scene', 'query-node-info', nodeUuid); + // Test remove node + await this.runTest('scene', 'remove-node', nodeUuid); + } + // Test execute scene script + await this.runTest('scene', 'execute-scene-script', { + name: 'cocos-mcp-server', + method: 'test-method', + args: [] + }); + } + async testNodeOperations() { + console.log('Testing Node Operations...'); + // Create a test node first + const createResult = await this.runTest('scene', 'create-node', { + name: 'TestNodeForOps', + type: 'cc.Node' + }); + if (createResult.success && createResult.result) { + const nodeUuid = createResult.result; + // Test set property + await this.runTest('scene', 'set-property', { + uuid: nodeUuid, + path: 'position', + dump: { + type: 'cc.Vec3', + value: { x: 100, y: 200, z: 0 } + } + }); + // Test add component + await this.runTest('scene', 'add-component', { + uuid: nodeUuid, + component: 'cc.Sprite' + }); + // Clean up + await this.runTest('scene', 'remove-node', nodeUuid); + } + } + async testAssetOperations() { + console.log('Testing Asset Operations...'); + // Test asset list + await this.runTest('asset-db', 'query-assets', { + pattern: '**/*.png', + ccType: 'cc.ImageAsset' + }); + // Test query asset by path + await this.runTest('asset-db', 'query-path', 'db://assets'); + // Test query asset by uuid (using a valid uuid format) + await this.runTest('asset-db', 'query-uuid', 'db://assets'); + } + async testProjectOperations() { + console.log('Testing Project Operations...'); + // Test open project settings + await this.runTest('project', 'open-settings', {}); + // Test query project settings + const projectName = await this.runTest('project', 'query-setting', 'name'); + if (projectName.success) { + console.log('Project name:', projectName.result); + } + } + async runAllTests() { + this.results = []; + await this.testSceneOperations(); + await this.testNodeOperations(); + await this.testAssetOperations(); + await this.testProjectOperations(); + return this.getTestReport(); + } + getTestReport() { + const total = this.results.length; + const passed = this.results.filter(r => r.success).length; + const failed = total - passed; + return { + summary: { + total, + passed, + failed, + passRate: total > 0 ? (passed / total * 100).toFixed(2) + '%' : '0%' + }, + results: this.results, + grouped: this.groupResultsByTool() + }; + } + groupResultsByTool() { + const grouped = {}; + for (const result of this.results) { + if (!grouped[result.tool]) { + grouped[result.tool] = []; + } + grouped[result.tool].push(result); + } + return grouped; + } +} +exports.ToolTester = ToolTester; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbC10ZXN0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvdGVzdC90b29sLXRlc3Rlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFXQSxNQUFhLFVBQVU7SUFBdkI7UUFDWSxZQUFPLEdBQWlCLEVBQUUsQ0FBQztJQTJKdkMsQ0FBQztJQXpKRyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQVksRUFBRSxNQUFjLEVBQUUsTUFBVztRQUNuRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDN0IsTUFBTSxNQUFNLEdBQWU7WUFDdkIsSUFBSTtZQUNKLE1BQU07WUFDTixPQUFPLEVBQUUsS0FBSztZQUNkLElBQUksRUFBRSxDQUFDO1NBQ1YsQ0FBQztRQUVGLElBQUksQ0FBQztZQUNELE1BQU0sUUFBUSxHQUFHLE1BQU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUNwRSxNQUFNLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztZQUN0QixNQUFNLENBQUMsTUFBTSxHQUFHLFFBQVEsQ0FBQztRQUM3QixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNiLE1BQU0sQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDO1lBQ3ZCLE1BQU0sQ0FBQyxLQUFLLEdBQUcsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzFFLENBQUM7UUFFRCxNQUFNLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxTQUFTLENBQUM7UUFDckMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDMUIsT0FBTyxNQUFNLENBQUM7SUFDbEIsQ0FBQztJQUVELEtBQUssQ0FB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QUFDLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSTthQUN2RTtZQUNELE9BQU8sRUFBRSxJQUFJLENBQUMsT0FBTztZQUNyQixPQUFPLEVBQUUsSUFBSSxDQUFDLGtCQUFrQixFQUFFO1NBQ3JDLENBQUM7SUFDTixDQUFDO0lBRU8sa0JBQWtCO1FBQ3RCLE1BQU0sT0FBTyxHQUFpQyxFQUFFLENBQUM7UUFFakQsS0FBSyxNQUFNLE1BQU0sSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDaEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDeEIsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDOUIsQ0FBQztZQUNELE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3RDLENBQUM7UUFFRCxPQUFPLE9BQU8sQ0FBQztJQUNuQixDQUFDO0NBQ0o7QUE1SkQsZ0NBNEpDIiwic291cmNlc0NvbnRlbnQiOlsiZGVjbGFyZSBjb25zdCBFZGl0b3I6IGFueTtcblxuaW50ZXJmYWNlIFRlc3RSZXN1bHQge1xuICAgIHRvb2w6IHN0cmluZztcbiAgICBtZXRob2Q6IHN0cmluZztcbiAgICBzdWNjZXNzOiBib29sZWFuO1xuICAgIHJlc3VsdD86IGFueTtcbiAgICBlcnJvcj86IHN0cmluZztcbiAgICB0aW1lOiBudW1iZXI7XG59XG5cbmV4cG9ydCBjbGFzcyBUb29sVGVzdGVyIHtcbiAgICBwcml2YXRlIHJlc3VsdHM6IFRlc3RSZXN1bHRbXSA9IFtdO1xuXG4gICAgYXN5bmMgcnVuVGVzdCh0b29sOiBzdHJpbmcsIG1ldGhvZDogc3RyaW5nLCBwYXJhbXM6IGFueSk6IFByb21pc2U8VGVzdFJlc3VsdD4ge1xuICAgICAgICBjb25zdCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICBjb25zdCByZXN1bHQ6IFRlc3RSZXN1bHQgPSB7XG4gICAgICAgICAgICB0b29sLFxuICAgICAgICAgICAgbWV0aG9kLFxuICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICB0aW1lOiAwXG4gICAgICAgIH07XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCh0b29sLCBtZXRob2QsIHBhcmFtcyk7XG4gICAgICAgICAgICByZXN1bHQuc3VjY2VzcyA9IHRydWU7XG4gICAgICAgICAgICByZXN1bHQucmVzdWx0ID0gcmVzcG9uc2U7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICByZXN1bHQuc3VjY2VzcyA9IGZhbHNlO1xuICAgICAgICAgICAgcmVzdWx0LmVycm9yID0gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmVzdWx0LnRpbWUgPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lO1xuICAgICAgICB0aGlzLnJlc3VsdHMucHVzaChyZXN1bHQpO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIGFzeW5jIHRlc3RTY2VuZU9wZXJhdGlvbnMoKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdUZXN0aW5nIFNjZW5lIE9wZXJhdGlvbnMuLi4nKTtcbiAgICAgICAgXG4gICAgICAgIC8vIFRlc3Qgbm9kZSBjcmVhdGlvbiAodGhpcyBpcyB0aGUgbWFpbiBzY2VuZSBvcGVyYXRpb24gdGhhdCB3b3JrcylcbiAgICAgICAgY29uc3QgY3JlYXRlUmVzdWx0ID0gYXdhaXQgdGhpcy5ydW5UZXN0KCdzY2VuZScsICdjcmVhdGUtbm9kZScsIHtcbiAgICAgICAgICAgIG5hbWU6ICdUZXN0Tm9kZScsXG4gICAgICAgICAgICB0eXBlOiAnY2MuTm9kZSdcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICBpZiAoY3JlYXRlUmVzdWx0LnN1Y2Nlc3MgJiYgY3JlYXRlUmVzdWx0LnJlc3VsdCkge1xuICAgICAgICAgICAgY29uc3Qgbm9kZVV1aWQgPSBjcmVhdGVSZXN1bHQucmVzdWx0O1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBUZXN0IHF1ZXJ5IG5vZGUgaW5mb1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5ydW5UZXN0KCdzY2VuZScsICdxdWVyeS1ub2RlLWluZm8nLCBub2RlVXVpZCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIFRlc3QgcmVtb3ZlIG5vZGVcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucnVuVGVzdCgnc2NlbmUnLCAncmVtb3ZlLW5vZGUnLCBub2RlVXVpZCk7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC8vIFRlc3QgZXhlY3V0ZSBzY2VuZSBzY3JpcHRcbiAgICAgICAgYXdhaXQgdGhpcy5ydW5UZXN0KCdzY2VuZScsICdleGVjdXRlLXNjZW5lLXNjcmlwdCcsIHtcbiAgICAgICAgICAgIG5hbWU6ICdjb2Nvcy1tY3Atc2VydmVyJyxcbiAgICAgICAgICAgIG1ldGhvZDogJ3Rlc3QtbWV0aG9kJyxcbiAgICAgICAgICAgIGFyZ3M6IFtdXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIGFzeW5jIHRlc3ROb2RlT3BlcmF0aW9ucygpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ1Rlc3RpbmcgTm9kZSBPcGVyYXRpb25zLi4uJyk7XG4gICAgICAgIFxuICAgICAgICAvLyBDcmVhdGUgYSB0ZXN0IG5vZGUgZmlyc3RcbiAgICAgICAgY29uc3QgY3JlYXRlUmVzdWx0ID0gYXdhaXQgdGhpcy5ydW5UZXN0KCdzY2VuZScsICdjcmVhdGUtbm9kZScsIHtcbiAgICAgICAgICAgIG5hbWU6ICdUZXN0Tm9kZUZvck9wcycsXG4gICAgICAgICAgICB0eXBlOiAnY2MuTm9kZSdcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICBpZiAoY3JlYXRlUmVzdWx0LnN1Y2Nlc3MgJiYgY3JlYXRlUmVzdWx0LnJlc3VsdCkge1xuICAgICAgICAgICAgY29uc3Qgbm9kZVV1aWQgPSBjcmVhdGVSZXN1bHQucmVzdWx0O1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBUZXN0IHNldCBwcm9wZXJ0eVxuICAgICAgICAgICAgYXdhaXQgdGhpcy5ydW5UZXN0KCdzY2VuZScsICdzZXQtcHJvcGVydHknLCB7XG4gICAgICAgICAgICAgICAgdXVpZDogbm9kZVV1aWQsXG4gICAgICAgICAgICAgICAgcGF0aDogJ3Bvc2l0aW9uJyxcbiAgICAgICAgICAgICAgICBkdW1wOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdjYy5WZWMzJyxcbiAgICAgICAgICAgICAgICAgICAgdmFsdWU6IHsgeDogMTAwLCB5OiAyMDAsIHo6IDAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBUZXN0IGFkZCBjb21wb25lbnRcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucnVuVGVzdCgnc2NlbmUnLCAnYWRkLWNvbXBvbmVudCcsIHtcbiAgICAgICAgICAgICAgICB1dWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICBjb21wb25lbnQ6ICdjYy5TcHJpdGUnXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8gQ2xlYW4gdXBcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucnVuVGVzdCgnc2NlbmUnLCAncmVtb3ZlLW5vZGUnLCBub2RlVXVpZCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBhc3luYyB0ZXN0QXNzZXRPcGVyYXRpb25zKCkge1xuICAgICAgICBjb25zb2xlLmxvZygnVGVzdGluZyBBc3NldCBPcGVyYXRpb25zLi4uJyk7XG4gICAgICAgIFxuICAgICAgICAvLyBUZXN0IGFzc2V0IGxpc3RcbiAgICAgICAgYXdhaXQgdGhpcy5ydW5UZXN0KCdhc3NldC1kYicsICdxdWVyeS1hc3NldHMnLCB7XG4gICAgICAgICAgICBwYXR0ZXJuOiAnKiovKi5wbmcnLFxuICAgICAgICAgICAgY2NUeXBlOiAnY2MuSW1hZ2VBc3NldCdcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICAvLyBUZXN0IHF1ZXJ5IGFzc2V0IGJ5IHBhdGhcbiAgICAgICAgYXdhaXQgdGhpcy5ydW5UZXN0KCdhc3NldC1kYicsICdxdWVyeS1wYXRoJywgJ2RiOi8vYXNzZXRzJyk7XG4gICAgICAgIFxuICAgICAgICAvLyBUZXN0IHF1ZXJ5IGFzc2V0IGJ5IHV1aWQgKHVzaW5nIGEgdmFsaWQgdXVpZCBmb3JtYXQpXG4gICAgICAgIGF3YWl0IHRoaXMucnVuVGVzdCgnYXNzZXQtZGInLCAncXVlcnktdXVpZCcsICdkYjovL2Fzc2V0cycpO1xuICAgIH1cblxuICAgIGFzeW5jIHRlc3RQcm9qZWN0T3BlcmF0aW9ucygpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ1Rlc3RpbmcgUHJvamVjdCBPcGVyYXRpb25zLi4uJyk7XG4gICAgICAgIFxuICAgICAgICAvLyBUZXN0IG9wZW4gcHJvamVjdCBzZXR0aW5nc1xuICAgICAgICBhd2FpdCB0aGlzLnJ1blRlc3QoJ3Byb2plY3QnLCAnb3Blbi1zZXR0aW5ncycsIHt9KTtcbiAgICAgICAgXG4gICAgICAgIC8vIFRlc3QgcXVlcnkgcHJvamVjdCBzZXR0aW5nc1xuICAgICAgICBjb25zdCBwcm9qZWN0TmFtZSA9IGF3YWl0IHRoaXMucnVuVGVzdCgncHJvamVjdCcsICdxdWVyeS1zZXR0aW5nJywgJ25hbWUnKTtcbiAgICAgICAgXG4gICAgICAgIGlmIChwcm9qZWN0TmFtZS5zdWNjZXNzKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnUHJvamVjdCBuYW1lOicsIHByb2plY3ROYW1lLnJlc3VsdCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBhc3luYyBydW5BbGxUZXN0cygpIHtcbiAgICAgICAgdGhpcy5yZXN1bHRzID0gW107XG4gICAgICAgIFxuICAgICAgICBhd2FpdCB0aGlzLnRlc3RTY2VuZU9wZXJhdGlvbnMoKTtcbiAgICAgICAgYXdhaXQgdGhpcy50ZXN0Tm9kZU9wZXJhdGlvbnMoKTtcbiAgICAgICAgYXdhaXQgdGhpcy50ZXN0QXNzZXRPcGVyYXRpb25zKCk7XG4gICAgICAgIGF3YWl0IHRoaXMudGVzdFByb2plY3RPcGVyYXRpb25zKCk7XG4gICAgICAgIFxuICAgICAgICByZXR1cm4gdGhpcy5nZXRUZXN0UmVwb3J0KCk7XG4gICAgfVxuXG4gICAgZ2V0VGVzdFJlcG9ydCgpIHtcbiAgICAgICAgY29uc3QgdG90YWwgPSB0aGlzLnJlc3VsdHMubGVuZ3RoO1xuICAgICAgICBjb25zdCBwYXNzZWQgPSB0aGlzLnJlc3VsdHMuZmlsdGVyKHIgPT4gci5zdWNjZXNzKS5sZW5ndGg7XG4gICAgICAgIGNvbnN0IGZhaWxlZCA9IHRvdGFsIC0gcGFzc2VkO1xuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1bW1hcnk6IHtcbiAgICAgICAgICAgICAgICB0b3RhbCxcbiAgICAgICAgICAgICAgICBwYXNzZWQsXG4gICAgICAgICAgICAgICAgZmFpbGVkLFxuICAgICAgICAgICAgICAgIHBhc3NSYXRlOiB0b3RhbCA+IDAgPyAocGFzc2VkIC8gdG90YWwgKiAxMDApLnRvRml4ZWQoMikgKyAnJScgOiAnMCUnXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgcmVzdWx0czogdGhpcy5yZXN1bHRzLFxuICAgICAgICAgICAgZ3JvdXBlZDogdGhpcy5ncm91cFJlc3VsdHNCeVRvb2woKVxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHByaXZhdGUgZ3JvdXBSZXN1bHRzQnlUb29sKCkge1xuICAgICAgICBjb25zdCBncm91cGVkOiBSZWNvcmQ8c3RyaW5nLCBUZXN0UmVzdWx0W10+ID0ge307XG4gICAgICAgIFxuICAgICAgICBmb3IgKGNvbnN0IHJlc3VsdCBvZiB0aGlzLnJlc3VsdHMpIHtcbiAgICAgICAgICAgIGlmICghZ3JvdXBlZFtyZXN1bHQudG9vbF0pIHtcbiAgICAgICAgICAgICAgICBncm91cGVkW3Jlc3VsdC50b29sXSA9IFtdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZ3JvdXBlZFtyZXN1bHQudG9vbF0ucHVzaChyZXN1bHQpO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICByZXR1cm4gZ3JvdXBlZDtcbiAgICB9XG59Il19 \ No newline at end of file diff --git a/dist/tools/broadcast-tools.js b/dist/tools/broadcast-tools.js new file mode 100644 index 0000000..d761d03 --- /dev/null +++ b/dist/tools/broadcast-tools.js @@ -0,0 +1,251 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BroadcastTools = void 0; +class BroadcastTools { + constructor() { + this.listeners = new Map(); + this.messageLog = []; + this.setupBroadcastListeners(); + } + getTools() { + return [ + { + name: 'get_broadcast_log', + description: 'Get recent broadcast messages log', + inputSchema: { + type: 'object', + properties: { + limit: { + type: 'number', + description: 'Number of recent messages to return', + default: 50 + }, + messageType: { + type: 'string', + description: 'Filter by message type (optional)' + } + } + } + }, + { + name: 'listen_broadcast', + description: 'Start listening for specific broadcast messages', + inputSchema: { + type: 'object', + properties: { + messageType: { + type: 'string', + description: 'Message type to listen for' + } + }, + required: ['messageType'] + } + }, + { + name: 'stop_listening', + description: 'Stop listening for specific broadcast messages', + inputSchema: { + type: 'object', + properties: { + messageType: { + type: 'string', + description: 'Message type to stop listening for' + } + }, + required: ['messageType'] + } + }, + { + name: 'clear_broadcast_log', + description: 'Clear the broadcast messages log', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_active_listeners', + description: 'Get list of active broadcast listeners', + inputSchema: { + type: 'object', + properties: {} + } + } + ]; + } + async execute(toolName, args) { + switch (toolName) { + case 'get_broadcast_log': + return await this.getBroadcastLog(args.limit, args.messageType); + case 'listen_broadcast': + return await this.listenBroadcast(args.messageType); + case 'stop_listening': + return await this.stopListening(args.messageType); + case 'clear_broadcast_log': + return await this.clearBroadcastLog(); + case 'get_active_listeners': + return await this.getActiveListeners(); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + setupBroadcastListeners() { + // 设置预定义的重要广播消息监听 + const importantMessages = [ + 'build-worker:ready', + 'build-worker:closed', + 'scene:ready', + 'scene:close', + 'scene:light-probe-edit-mode-changed', + 'scene:light-probe-bounding-box-edit-mode-changed', + 'asset-db:ready', + 'asset-db:close', + 'asset-db:asset-add', + 'asset-db:asset-change', + 'asset-db:asset-delete' + ]; + importantMessages.forEach(messageType => { + this.addBroadcastListener(messageType); + }); + } + addBroadcastListener(messageType) { + const listener = (data) => { + this.messageLog.push({ + message: messageType, + data: data, + timestamp: Date.now() + }); + // 保持日志大小在合理范围内 + if (this.messageLog.length > 1000) { + this.messageLog = this.messageLog.slice(-500); + } + console.log(`[Broadcast] ${messageType}:`, data); + }; + if (!this.listeners.has(messageType)) { + this.listeners.set(messageType, []); + } + this.listeners.get(messageType).push(listener); + // 注册 Editor 消息监听 - 暂时注释掉,Editor.Message API可能不支持 + // Editor.Message.on(messageType, listener); + console.log(`[BroadcastTools] Added listener for ${messageType} (simulated)`); + } + removeBroadcastListener(messageType) { + const listeners = this.listeners.get(messageType); + if (listeners) { + listeners.forEach(listener => { + // Editor.Message.off(messageType, listener); + console.log(`[BroadcastTools] Removed listener for ${messageType} (simulated)`); + }); + this.listeners.delete(messageType); + } + } + async getBroadcastLog(limit = 50, messageType) { + return new Promise((resolve) => { + let filteredLog = this.messageLog; + if (messageType) { + filteredLog = this.messageLog.filter(entry => entry.message === messageType); + } + const recentLog = filteredLog.slice(-limit).map(entry => (Object.assign(Object.assign({}, entry), { timestamp: new Date(entry.timestamp).toISOString() }))); + resolve({ + success: true, + data: { + log: recentLog, + count: recentLog.length, + totalCount: filteredLog.length, + filter: messageType || 'all', + message: 'Broadcast log retrieved successfully' + } + }); + }); + } + async listenBroadcast(messageType) { + return new Promise((resolve) => { + try { + if (!this.listeners.has(messageType)) { + this.addBroadcastListener(messageType); + resolve({ + success: true, + data: { + messageType: messageType, + message: `Started listening for broadcast: ${messageType}` + } + }); + } + else { + resolve({ + success: true, + data: { + messageType: messageType, + message: `Already listening for broadcast: ${messageType}` + } + }); + } + } + catch (err) { + resolve({ success: false, error: err.message }); + } + }); + } + async stopListening(messageType) { + return new Promise((resolve) => { + try { + if (this.listeners.has(messageType)) { + this.removeBroadcastListener(messageType); + resolve({ + success: true, + data: { + messageType: messageType, + message: `Stopped listening for broadcast: ${messageType}` + } + }); + } + else { + resolve({ + success: true, + data: { + messageType: messageType, + message: `Was not listening for broadcast: ${messageType}` + } + }); + } + } + catch (err) { + resolve({ success: false, error: err.message }); + } + }); + } + async clearBroadcastLog() { + return new Promise((resolve) => { + const previousCount = this.messageLog.length; + this.messageLog = []; + resolve({ + success: true, + data: { + clearedCount: previousCount, + message: 'Broadcast log cleared successfully' + } + }); + }); + } + async getActiveListeners() { + return new Promise((resolve) => { + const activeListeners = Array.from(this.listeners.keys()).map(messageType => { + var _a; + return ({ + messageType: messageType, + listenerCount: ((_a = this.listeners.get(messageType)) === null || _a === void 0 ? void 0 : _a.length) || 0 + }); + }); + resolve({ + success: true, + data: { + listeners: activeListeners, + count: activeListeners.length, + message: 'Active listeners retrieved successfully' + } + }); + }); + } +} +exports.BroadcastTools = BroadcastTools; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJvYWRjYXN0LXRvb2xzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc291cmNlL3Rvb2xzL2Jyb2FkY2FzdC10b29scy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQSxNQUFhLGNBQWM7SUFJdkI7UUFIUSxjQUFTLEdBQTRCLElBQUksR0FBRyxFQUFFLENBQUM7UUFDL0MsZUFBVSxHQUE2RCxFQUFFLENBQUM7UUFHOUUsSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7SUFDbkMsQ0FBQztJQUVELFFBQVE7UUFDSixPQUFPO1lBQ0g7Z0JBQ0ksSUFBSSxFQUFFLG1CQUFtQjtnQkFDekIsV0FBVyxFQUFFLG1DQUFtQztnQkFDaEQsV0FBVyxFQUFFO29CQUNULElBQUksRUFBRSxRQUFRO29CQUNkLFVBQVUsRUFBRTt3QkFDUixLQUFLLEVBQUU7NEJBQ0gsSUFBSSxFQUFFLFFBQVE7NEJBQ2QsV0FBVyxFQUFFLHFDQUFxQzs0QkFDbEQsT0FBTyxFQUFFLEVBQUU7eUJBQ2Q7d0JBQ0QsV0FBVyxFQUFFOzRCQUNULElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSxtQ0FBbUM7eUJBQ25EO3FCQUNKO2lCQUNKO2FBQ0o7WUFDRDtnQkFDSSxJQUFJLEVBQUUsa0JBQWtCO2dCQUN4QixXQUFXLEVBQUUsaURBQWlEO2dCQUM5RCxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLFdBQVcsRUFBRTs0QkFDVCxJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsNEJBQTRCO3lCQUM1QztxQkFDSjtvQkFDRCxRQUFRLEVBQUUsQ0FBQyxhQUFhLENBQUM7aUJBQzVCO2FBQ0o7WUFDRDtnQkFDSSxJQUFJLEVBQUUsZ0JBQWdCO2dCQUN0QixXQUFXLEVBQUUsZ0RBQWdEO2dCQUM3RCxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLFdBQVcs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QUFPLEVBQUUsb0NBQW9DO2lCQUNoRDthQUNKLENBQUMsQ0FBQztRQUNQLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVPLEtBQUssQ0FBQyxrQkFBa0I7UUFDNUIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLE1BQU0sZUFBZSxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsRUFBRTs7Z0JBQUMsT0FBQSxDQUFDO29CQUMxRSxXQUFXLEVBQUUsV0FBVztvQkFDeEIsYUFBYSxFQUFFLENBQUEsTUFBQSxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsMENBQUUsTUFBTSxLQUFJLENBQUM7aUJBQzlELENBQUMsQ0FBQTthQUFBLENBQUMsQ0FBQztZQUVKLE9BQU8sQ0FBQztnQkFDSixPQUFPLEVBQUUsSUFBSTtnQkFDYixJQUFJLEVBQUU7b0JBQ0YsU0FBUyxFQUFFLGVBQWU7b0JBQzFCLEtBQUssRUFBRSxlQUFlLENBQUMsTUFBTTtvQkFDN0IsT0FBTyxFQUFFLHlDQUF5QztpQkFDckQ7YUFDSixDQUFDLENBQUM7UUFDUCxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7Q0FDSjtBQXJRRCx3Q0FxUUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBUb29sRGVmaW5pdGlvbiwgVG9vbFJlc3BvbnNlLCBUb29sRXhlY3V0b3IgfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBjbGFzcyBCcm9hZGNhc3RUb29scyBpbXBsZW1lbnRzIFRvb2xFeGVjdXRvciB7XG4gICAgcHJpdmF0ZSBsaXN0ZW5lcnM6IE1hcDxzdHJpbmcsIEZ1bmN0aW9uW10+ID0gbmV3IE1hcCgpO1xuICAgIHByaXZhdGUgbWVzc2FnZUxvZzogQXJyYXk8eyBtZXNzYWdlOiBzdHJpbmc7IGRhdGE6IGFueTsgdGltZXN0YW1wOiBudW1iZXIgfT4gPSBbXTtcblxuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnNldHVwQnJvYWRjYXN0TGlzdGVuZXJzKCk7XG4gICAgfVxuXG4gICAgZ2V0VG9vbHMoKTogVG9vbERlZmluaXRpb25bXSB7XG4gICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2dldF9icm9hZGNhc3RfbG9nJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCByZWNlbnQgYnJvYWRjYXN0IG1lc3NhZ2VzIGxvZycsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0OiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdOdW1iZXIgb2YgcmVjZW50IG1lc3NhZ2VzIHRvIHJldHVybicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDogNTBcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlVHlwZToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnRmlsdGVyIGJ5IG1lc3NhZ2UgdHlwZSAob3B0aW9uYWwpJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnbGlzdGVuX2Jyb2FkY2FzdCcsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdTdGFydCBsaXN0ZW5pbmcgZm9yIHNwZWNpZmljIGJyb2FkY2FzdCBtZXNzYWdlcycsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2VUeXBlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdNZXNzYWdlIHR5cGUgdG8gbGlzdGVuIGZvcidcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsnbWVzc2FnZVR5cGUnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ3N0b3BfbGlzdGVuaW5nJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1N0b3AgbGlzdGVuaW5nIGZvciBzcGVjaWZpYyBicm9hZGNhc3QgbWVzc2FnZXMnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlVHlwZToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnTWVzc2FnZSB0eXBlIHRvIHN0b3AgbGlzdGVuaW5nIGZvcidcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsnbWVzc2FnZVR5cGUnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2NsZWFyX2Jyb2FkY2FzdF9sb2cnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ2xlYXIgdGhlIGJyb2FkY2FzdCBtZXNzYWdlcyBsb2cnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7fVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2dldF9hY3RpdmVfbGlzdGVuZXJzJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBsaXN0IG9mIGFjdGl2ZSBicm9hZGNhc3QgbGlzdGVuZXJzJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIF07XG4gICAgfVxuXG4gICAgYXN5bmMgZXhlY3V0ZSh0b29sTmFtZTogc3RyaW5nLCBhcmdzOiBhbnkpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICBzd2l0Y2ggKHRvb2xOYW1lKSB7XG4gICAgICAgICAgICBjYXNlICdnZXRfYnJvYWRjYXN0X2xvZyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0QnJvYWRjYXN0TG9nKGFyZ3MubGltaXQsIGFyZ3MubWVzc2FnZVR5cGUpO1xuICAgICAgICAgICAgY2FzZSAnbGlzdGVuX2Jyb2FkY2FzdCc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMubGlzdGVuQnJvYWRjYXN0KGFyZ3MubWVzc2FnZVR5cGUpO1xuICAgICAgICAgICAgY2FzZSAnc3RvcF9saXN0ZW5pbmcnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLnN0b3BMaXN0ZW5pbmcoYXJncy5tZXNzYWdlVHlwZSk7XG4gICAgICAgICAgICBjYXNlICdjbGVhcl9icm9hZGNhc3RfbG9nJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5jbGVhckJyb2FkY2FzdExvZygpO1xuICAgICAgICAgICAgY2FzZSAnZ2V0X2FjdGl2ZV9saXN0ZW5lcnMnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmdldEFjdGl2ZUxpc3RlbmVycygpO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFVua25vd24gdG9vbDogJHt0b29sTmFtZX1gKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgc2V0dXBCcm9hZGNhc3RMaXN0ZW5lcnMoKTogdm9pZCB7XG4gICAgICAgIC8vIOiuvue9rumihOWumuS5ieeahOmHjeimgeW5v+aSrea2iOaBr+ebkeWQrFxuICAgICAgICBjb25zdCBpbXBvcnRhbnRNZXNzYWdlcyA9IFtcbiAgICAgICAgICAgICdidWlsZC13b3JrZXI6cmVhZHknLFxuICAgICAgICAgICAgJ2J1aWxkLXdvcmtlcjpjbG9zZWQnLFxuICAgICAgICAgICAgJ3NjZW5lOnJlYWR5JyxcbiAgICAgICAgICAgICdzY2VuZTpjbG9zZScsXG4gICAgICAgICAgICAnc2NlbmU6bGlnaHQtcHJvYmUtZWRpdC1tb2RlLWNoYW5nZWQnLFxuICAgICAgICAgICAgJ3NjZW5lOmxpZ2h0LXByb2JlLWJvdW5kaW5nLWJveC1lZGl0LW1vZGUtY2hhbmdlZCcsXG4gICAgICAgICAgICAnYXNzZXQtZGI6cmVhZHknLFxuICAgICAgICAgICAgJ2Fzc2V0LWRiOmNsb3NlJyxcbiAgICAgICAgICAgICdhc3NldC1kYjphc3NldC1hZGQnLFxuICAgICAgICAgICAgJ2Fzc2V0LWRiOmFzc2V0LWNoYW5nZScsXG4gICAgICAgICAgICAnYXNzZXQtZGI6YXNzZXQtZGVsZXRlJ1xuICAgICAgICBdO1xuXG4gICAgICAgIGltcG9ydGFudE1lc3NhZ2VzLmZvckVhY2gobWVzc2FnZVR5cGUgPT4ge1xuICAgICAgICAgICAgdGhpcy5hZGRCcm9hZGNhc3RMaXN0ZW5lcihtZXNzYWdlVHlwZSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYWRkQnJvYWRjYXN0TGlzdGVuZXIobWVzc2FnZVR5cGU6IHN0cmluZyk6IHZvaWQge1xuICAgICAgICBjb25zdCBsaXN0ZW5lciA9IChkYXRhOiBhbnkpID0+IHtcbiAgICAgICAgICAgIHRoaXMubWVzc2FnZUxvZy5wdXNoKHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBtZXNzYWdlVHlwZSxcbiAgICAgICAgICAgICAgICBkYXRhOiBkYXRhLFxuICAgICAgICAgICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIC8vIOS/neaMgeaXpeW/l+Wkp+Wwj+WcqOWQiOeQhuiMg+WbtOWGhVxuICAgICAgICAgICAgaWYgKHRoaXMubWVzc2FnZUxvZy5sZW5ndGggPiAxMDAwKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5tZXNzYWdlTG9nID0gdGhpcy5tZXNzYWdlTG9nLnNsaWNlKC01MDApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgW0Jyb2FkY2FzdF0gJHttZXNzYWdlVHlwZX06YCwgZGF0YSk7XG4gICAgICAgIH07XG5cbiAgICAgICAgaWYgKCF0aGlzLmxpc3RlbmVycy5oYXMobWVzc2FnZVR5cGUpKSB7XG4gICAgICAgICAgICB0aGlzLmxpc3RlbmVycy5zZXQobWVzc2FnZVR5cGUsIFtdKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmxpc3RlbmVycy5nZXQobWVzc2FnZVR5cGUpIS5wdXNoKGxpc3RlbmVyKTtcblxuICAgICAgICAvLyDms6jlhowgRWRpdG9yIOa2iOaBr+ebkeWQrCAtIOaaguaXtuazqOmHiuaOie+8jEVkaXRvci5NZXNzYWdlIEFQSeWPr+iDveS4jeaUr+aMgVxuICAgICAgICAvLyBFZGl0b3IuTWVzc2FnZS5vbihtZXNzYWdlVHlwZSwgbGlzdGVuZXIpO1xuICAgICAgICBjb25zb2xlLmxvZyhgW0Jyb2FkY2FzdFRvb2xzXSBBZGRlZCBsaXN0ZW5lciBmb3IgJHttZXNzYWdlVHlwZX0gKHNpbXVsYXRlZClgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHJlbW92ZUJyb2FkY2FzdExpc3RlbmVyKG1lc3NhZ2VUeXBlOiBzdHJpbmcpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgbGlzdGVuZXJzID0gdGhpcy5saXN0ZW5lcnMuZ2V0KG1lc3NhZ2VUeXBlKTtcbiAgICAgICAgaWYgKGxpc3RlbmVycykge1xuICAgICAgICAgICAgbGlzdGVuZXJzLmZvckVhY2gobGlzdGVuZXIgPT4ge1xuICAgICAgICAgICAgICAgIC8vIEVkaXRvci5NZXNzYWdlLm9mZihtZXNzYWdlVHlwZSwgbGlzdGVuZXIpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbQnJvYWRjYXN0VG9vbHNdIFJlbW92ZWQgbGlzdGVuZXIgZm9yICR7bWVzc2FnZVR5cGV9IChzaW11bGF0ZWQpYCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMubGlzdGVuZXJzLmRlbGV0ZShtZXNzYWdlVHlwZSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldEJyb2FkY2FzdExvZyhsaW1pdDogbnVtYmVyID0gNTAsIG1lc3NhZ2VUeXBlPzogc3RyaW5nKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBsZXQgZmlsdGVyZWRMb2cgPSB0aGlzLm1lc3NhZ2VMb2c7XG5cbiAgICAgICAgICAgIGlmIChtZXNzYWdlVHlwZSkge1xuICAgICAgICAgICAgICAgIGZpbHRlcmVkTG9nID0gdGhpcy5tZXNzYWdlTG9nLmZpbHRlcihlbnRyeSA9PiBlbnRyeS5tZXNzYWdlID09PSBtZXNzYWdlVHlwZSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHJlY2VudExvZyA9IGZpbHRlcmVkTG9nLnNsaWNlKC1saW1pdCkubWFwKGVudHJ5ID0+ICh7XG4gICAgICAgICAgICAgICAgLi4uZW50cnksXG4gICAgICAgICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZShlbnRyeS50aW1lc3RhbXApLnRvSVNPU3RyaW5nKClcbiAgICAgICAgICAgIH0pKTtcblxuICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgIGxvZzogcmVjZW50TG9nLFxuICAgICAgICAgICAgICAgICAgICBjb3VudDogcmVjZW50TG9nLmxlbmd0aCxcbiAgICAgICAgICAgICAgICAgICAgdG90YWxDb3VudDogZmlsdGVyZWRMb2cubGVuZ3RoLFxuICAgICAgICAgICAgICAgICAgICBmaWx0ZXI6IG1lc3NhZ2VUeXBlIHx8ICdhbGwnLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiAnQnJvYWRjYXN0IGxvZyByZXRyaWV2ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGxpc3RlbkJyb2FkY2FzdChtZXNzYWdlVHlwZTogc3RyaW5nKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGlmICghdGhpcy5saXN0ZW5lcnMuaGFzKG1lc3NhZ2VUeXBlKSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmFkZEJyb2FkY2FzdExpc3RlbmVyKG1lc3NhZ2VUeXBlKTtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2VUeXBlOiBtZXNzYWdlVHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgU3RhcnRlZCBsaXN0ZW5pbmcgZm9yIGJyb2FkY2FzdDogJHttZXNzYWdlVHlwZX1gXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlVHlwZTogbWVzc2FnZVR5cGUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYEFscmVhZHkgbGlzdGVuaW5nIGZvciBicm9hZGNhc3Q6ICR7bWVzc2FnZVR5cGV9YFxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIHN0b3BMaXN0ZW5pbmcobWVzc2FnZVR5cGU6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5saXN0ZW5lcnMuaGFzKG1lc3NhZ2VUeXBlKSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnJlbW92ZUJyb2FkY2FzdExpc3RlbmVyKG1lc3NhZ2VUeXBlKTtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2VUeXBlOiBtZXNzYWdlVHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgU3RvcHBlZCBsaXN0ZW5pbmcgZm9yIGJyb2FkY2FzdDogJHttZXNzYWdlVHlwZX1gXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlVHlwZTogbWVzc2FnZVR5cGUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYFdhcyBub3QgbGlzdGVuaW5nIGZvciBicm9hZGNhc3Q6ICR7bWVzc2FnZVR5cGV9YFxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGNsZWFyQnJvYWRjYXN0TG9nKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcHJldmlvdXNDb3VudCA9IHRoaXMubWVzc2FnZUxvZy5sZW5ndGg7XG4gICAgICAgICAgICB0aGlzLm1lc3NhZ2VMb2cgPSBbXTtcbiAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICBjbGVhcmVkQ291bnQ6IHByZXZpb3VzQ291bnQsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdCcm9hZGNhc3QgbG9nIGNsZWFyZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldEFjdGl2ZUxpc3RlbmVycygpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGFjdGl2ZUxpc3RlbmVycyA9IEFycmF5LmZyb20odGhpcy5saXN0ZW5lcnMua2V5cygpKS5tYXAobWVzc2FnZVR5cGUgPT4gKHtcbiAgICAgICAgICAgICAgICBtZXNzYWdlVHlwZTogbWVzc2FnZVR5cGUsXG4gICAgICAgICAgICAgICAgbGlzdGVuZXJDb3VudDogdGhpcy5saXN0ZW5lcnMuZ2V0KG1lc3NhZ2VUeXBlKT8ubGVuZ3RoIHx8IDBcbiAgICAgICAgICAgIH0pKTtcblxuICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgIGxpc3RlbmVyczogYWN0aXZlTGlzdGVuZXJzLFxuICAgICAgICAgICAgICAgICAgICBjb3VudDogYWN0aXZlTGlzdGVuZXJzLmxlbmd0aCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ0FjdGl2ZSBsaXN0ZW5lcnMgcmV0cmlldmVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxufSJdfQ== \ No newline at end of file diff --git a/dist/tools/component-tools.js b/dist/tools/component-tools.js new file mode 100644 index 0000000..f6d1382 --- /dev/null +++ b/dist/tools/component-tools.js @@ -0,0 +1,426 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ComponentTools = void 0; +class ComponentTools { + getTools() { + return [ + { + name: 'add_component', + description: 'Add a component to a specific node. The component will be added to the exact node specified by nodeUuid.', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Target node UUID. Use get_node_info or find_node_by_name to get the UUID of the desired node.' + }, + componentType: { + type: 'string', + description: 'Component type (e.g., cc.Sprite, cc.Label, cc.Button)' + } + }, + required: ['nodeUuid', 'componentType'] + } + }, + { + name: 'remove_component', + description: 'Remove a component from a node', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + }, + componentType: { + type: 'string', + description: 'Component type to remove' + } + }, + required: ['nodeUuid', 'componentType'] + } + }, + { + name: 'get_components', + description: 'Get all components of a node', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + } + }, + required: ['nodeUuid'] + } + }, + { + name: 'get_component_info', + description: 'Get specific component information', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + }, + componentType: { + type: 'string', + description: 'Component type to get info for' + } + }, + required: ['nodeUuid', 'componentType'] + } + }, + { + name: 'set_component_property', + description: 'Set component property value', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + }, + componentType: { + type: 'string', + description: 'Component type' + }, + property: { + type: 'string', + description: 'Property name' + }, + value: { + description: 'Property value' + } + }, + required: ['nodeUuid', 'componentType', 'property', 'value'] + } + }, + { + name: 'attach_script', + description: 'Attach a script component to a node', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + }, + scriptPath: { + type: 'string', + description: 'Script asset path (e.g., db://assets/scripts/MyScript.ts)' + } + }, + required: ['nodeUuid', 'scriptPath'] + } + }, + { + name: 'get_available_components', + description: 'Get list of available component types', + inputSchema: { + type: 'object', + properties: { + category: { + type: 'string', + description: 'Component category filter', + enum: ['all', 'renderer', 'ui', 'physics', 'animation', 'audio'], + default: 'all' + } + } + } + } + ]; + } + async execute(toolName, args) { + switch (toolName) { + case 'add_component': + return await this.addComponent(args.nodeUuid, args.componentType); + case 'remove_component': + return await this.removeComponent(args.nodeUuid, args.componentType); + case 'get_components': + return await this.getComponents(args.nodeUuid); + case 'get_component_info': + return await this.getComponentInfo(args.nodeUuid, args.componentType); + case 'set_component_property': + return await this.setComponentProperty(args); + case 'attach_script': + return await this.attachScript(args.nodeUuid, args.scriptPath); + case 'get_available_components': + return await this.getAvailableComponents(args.category); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + async addComponent(nodeUuid, componentType) { + return new Promise((resolve) => { + // 尝试直接使用 Editor API 添加组件 + Editor.Message.request('scene', 'create-component', { + uuid: nodeUuid, + component: componentType + }).then((result) => { + resolve({ + success: true, + data: { + componentId: result, + message: `Component '${componentType}' added successfully` + } + }); + }).catch((err) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'addComponentToNode', + args: [nodeUuid, componentType] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + resolve(result); + }).catch((err2) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + async removeComponent(nodeUuid, componentType) { + return new Promise((resolve) => { + const options = { + name: 'cocos-mcp-server', + method: 'removeComponentFromNode', + args: [nodeUuid, componentType] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + resolve(result); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async getComponents(nodeUuid) { + return new Promise((resolve) => { + // 优先尝试直接使用 Editor API 查询节点信息 + Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeData) => { + if (nodeData && nodeData.__comps__) { + const components = nodeData.__comps__.map((comp) => ({ + type: comp.__type__ || 'Unknown', + enabled: comp.enabled !== undefined ? comp.enabled : true, + properties: this.extractComponentProperties(comp) + })); + resolve({ + success: true, + data: { + nodeUuid: nodeUuid, + components: components + } + }); + } + else { + resolve({ success: false, error: 'Node not found or no components data' }); + } + }).catch((err) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getNodeInfo', + args: [nodeUuid] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + if (result.success) { + resolve({ + success: true, + data: result.data.components + }); + } + else { + resolve(result); + } + }).catch((err2) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + async getComponentInfo(nodeUuid, componentType) { + return new Promise((resolve) => { + // 优先尝试直接使用 Editor API 查询节点信息 + Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeData) => { + if (nodeData && nodeData.__comps__) { + const component = nodeData.__comps__.find((comp) => comp.__type__ === componentType); + if (component) { + resolve({ + success: true, + data: { + nodeUuid: nodeUuid, + componentType: componentType, + enabled: component.enabled !== undefined ? component.enabled : true, + properties: this.extractComponentProperties(component) + } + }); + } + else { + resolve({ success: false, error: `Component '${componentType}' not found on node` }); + } + } + else { + resolve({ success: false, error: 'Node not found or no components data' }); + } + }).catch((err) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getNodeInfo', + args: [nodeUuid] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + if (result.success && result.data.components) { + const component = result.data.components.find((comp) => comp.type === componentType); + if (component) { + resolve({ + success: true, + data: Object.assign({ nodeUuid: nodeUuid, componentType: componentType }, component) + }); + } + else { + resolve({ success: false, error: `Component '${componentType}' not found on node` }); + } + } + else { + resolve({ success: false, error: result.error || 'Failed to get component info' }); + } + }).catch((err2) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + extractComponentProperties(component) { + const properties = {}; + const excludeKeys = ['__type__', 'enabled', 'node', '_id']; + for (const key in component) { + if (!excludeKeys.includes(key) && !key.startsWith('_')) { + properties[key] = component[key]; + } + } + return properties; + } + async setComponentProperty(args) { + return new Promise((resolve) => { + // 首先获取节点信息以找到正确的组件索引 + Editor.Message.request('scene', 'query-node', args.nodeUuid).then((nodeData) => { + if (!nodeData || !nodeData.__comps__) { + throw new Error('Node not found or no components data'); + } + // 查找组件索引 + let componentIndex = -1; + for (let i = 0; i < nodeData.__comps__.length; i++) { + const comp = nodeData.__comps__[i]; + if (comp.__type__ === args.componentType) { + componentIndex = i; + break; + } + } + if (componentIndex === -1) { + throw new Error(`Component '${args.componentType}' not found on node`); + } + // 使用正确的组件索引路径 + const propertyPath = `__comps__.${componentIndex}.${args.property}`; + return Editor.Message.request('scene', 'set-property', { + uuid: args.nodeUuid, + path: propertyPath, + dump: { + value: args.value + } + }); + }).then(() => { + resolve({ + success: true, + message: `Component property '${args.property}' updated successfully` + }); + }).catch((err) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'setComponentProperty', + args: [args.nodeUuid, args.componentType, args.property, args.value] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + resolve(result); + }).catch((err2) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + async attachScript(nodeUuid, scriptPath) { + return new Promise((resolve) => { + var _a; + // 从脚本路径提取组件类名 + const scriptName = (_a = scriptPath.split('/').pop()) === null || _a === void 0 ? void 0 : _a.replace('.ts', '').replace('.js', ''); + if (!scriptName) { + resolve({ success: false, error: 'Invalid script path' }); + return; + } + // 首先尝试直接使用脚本名称作为组件类型 + Editor.Message.request('scene', 'create-component', { + uuid: nodeUuid, + component: scriptName // 使用脚本名称而非UUID + }).then((result) => { + resolve({ + success: true, + data: { + componentId: result, + scriptPath: scriptPath, + componentName: scriptName, + message: `Script '${scriptName}' attached successfully` + } + }); + }).catch((err) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'attachScript', + args: [nodeUuid, scriptPath] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + resolve(result); + }).catch((err2) => { + resolve({ + success: false, + error: `Failed to attach script '${scriptName}': ${err.message}`, + instruction: 'Please ensure the script is properly compiled and exported as a Component class. You can also manually attach the script through the Properties panel in the editor.' + }); + }); + }); + }); + } + async getAvailableComponents(category = 'all') { + const componentCategories = { + renderer: ['cc.Sprite', 'cc.Label', 'cc.RichText', 'cc.Mask', 'cc.Graphics'], + ui: ['cc.Button', 'cc.Toggle', 'cc.Slider', 'cc.ScrollView', 'cc.EditBox', 'cc.ProgressBar'], + physics: ['cc.RigidBody2D', 'cc.BoxCollider2D', 'cc.CircleCollider2D', 'cc.PolygonCollider2D'], + animation: ['cc.Animation', 'cc.AnimationClip', 'cc.SkeletalAnimation'], + audio: ['cc.AudioSource'], + layout: ['cc.Layout', 'cc.Widget', 'cc.PageView', 'cc.PageViewIndicator'], + effects: ['cc.MotionStreak', 'cc.ParticleSystem2D'], + camera: ['cc.Camera'], + light: ['cc.Light', 'cc.DirectionalLight', 'cc.PointLight', 'cc.SpotLight'] + }; + let components = []; + if (category === 'all') { + for (const cat in componentCategories) { + components = components.concat(componentCategories[cat]); + } + } + else if (componentCategories[category]) { + components = componentCategories[category]; + } + return { + success: true, + data: { + category: category, + components: components + } + }; + } +} +exports.ComponentTools = ComponentTools; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9uZW50LXRvb2xzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc291cmNlL3Rvb2xzL2NvbXBvbmVudC10b29scy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQSxNQUFhLGNBQWM7SUFDdkIsUUFBUTtRQUNKLE9BQU87WUFDSDtnQkFDSSxJQUFJLEVBQUUsZUFBZTtnQkFDckIsV0FBVyxFQUFFLDBHQUEwRztnQkFDdkgsV0FBVyxFQUFFO29CQUNULElBQUksRUFBRSxRQUFRO29CQUNkLFVBQVUsRUFBRTt3QkFDUixRQUFRLEVBQUU7NEJBQ04sSUFBSSxFQUFFLFFBQVE7NEJBQ2QsV0FBVyxFQUFFLCtGQUErRjt5QkFDL0c7d0JBQ0QsYUFBYSxFQUFFOzRCQUNYLElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSx1REFBdUQ7eUJBQ3ZFO3FCQUNKO29CQUNELFFBQVEsRUFBRSxDQUFDLFVBQVUsRUFBRSxlQUFlLENBQUM7aUJBQzFDO2FBQ0o7WUFDRDtnQkFDSSxJQUFJLEVBQUUsa0JBQWtCO2dCQUN4QixXQUFXLEVBQUUsZ0NBQWdDO2dCQUM3QyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLFFBQVEsRUFBRTs0QkFDTixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsV0FBVzt5QkFDM0I7d0JBQ0QsYUFBYSxFQUFFOzRCQUNYLElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSwwQkFBMEI7eUJBQzFDO3FCQUNKO29CQUNELFFBQVEsRUFBRSxDQUFDLFVBQVUsRUFBRSxlQUFlLENBQUM7aUJBQzFDO2FBQ0o7WUFDRDtnQkFDSSxJQUFJLEVBQUUsZ0JBQWdCO2dCQUN0QixXQUFXLEVBQUUsOEJBQThCO2dCQUMzQyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLFFBQVEsRUFBRTs0QkFDTixJQUFJLEVB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QUFDO1lBQzlGLFNBQVMsRUFBRSxDQUFDLGNBQWMsRUFBRSxrQkFBa0IsRUFBRSxzQkFBc0IsQ0FBQztZQUN2RSxLQUFLLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQztZQUN6QixNQUFNLEVBQUUsQ0FBQyxXQUFXLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxzQkFBc0IsQ0FBQztZQUN6RSxPQUFPLEVBQUUsQ0FBQyxpQkFBaUIsRUFBRSxxQkFBcUIsQ0FBQztZQUNuRCxNQUFNLEVBQUUsQ0FBQyxXQUFXLENBQUM7WUFDckIsS0FBSyxFQUFFLENBQUMsVUFBVSxFQUFFLHFCQUFxQixFQUFFLGVBQWUsRUFBRSxjQUFjLENBQUM7U0FDOUUsQ0FBQztRQUVGLElBQUksVUFBVSxHQUFhLEVBQUUsQ0FBQztRQUU5QixJQUFJLFFBQVEsS0FBSyxLQUFLLEVBQUUsQ0FBQztZQUNyQixLQUFLLE1BQU0sR0FBRyxJQUFJLG1CQUFtQixFQUFFLENBQUM7Z0JBQ3BDLFVBQVUsR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDN0QsQ0FBQztRQUNMLENBQUM7YUFBTSxJQUFJLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDdkMsVUFBVSxHQUFHLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQy9DLENBQUM7UUFFRCxPQUFPO1lBQ0gsT0FBTyxFQUFFLElBQUk7WUFDYixJQUFJLEVBQUU7Z0JBQ0YsUUFBUSxFQUFFLFFBQVE7Z0JBQ2xCLFVBQVUsRUFBRSxVQUFVO2FBQ3pCO1NBQ0osQ0FBQztJQUNOLENBQUM7Q0FDSjtBQTFiRCx3Q0EwYkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBUb29sRGVmaW5pdGlvbiwgVG9vbFJlc3BvbnNlLCBUb29sRXhlY3V0b3IsIENvbXBvbmVudEluZm8gfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBjbGFzcyBDb21wb25lbnRUb29scyBpbXBsZW1lbnRzIFRvb2xFeGVjdXRvciB7XG4gICAgZ2V0VG9vbHMoKTogVG9vbERlZmluaXRpb25bXSB7XG4gICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2FkZF9jb21wb25lbnQnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQWRkIGEgY29tcG9uZW50IHRvIGEgc3BlY2lmaWMgbm9kZS4gVGhlIGNvbXBvbmVudCB3aWxsIGJlIGFkZGVkIHRvIHRoZSBleGFjdCBub2RlIHNwZWNpZmllZCBieSBub2RlVXVpZC4nLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBub2RlVXVpZDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnVGFyZ2V0IG5vZGUgVVVJRC4gVXNlIGdldF9ub2RlX2luZm8gb3IgZmluZF9ub2RlX2J5X25hbWUgdG8gZ2V0IHRoZSBVVUlEIG9mIHRoZSBkZXNpcmVkIG5vZGUuJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudFR5cGU6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0NvbXBvbmVudCB0eXBlIChlLmcuLCBjYy5TcHJpdGUsIGNjLkxhYmVsLCBjYy5CdXR0b24pJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydub2RlVXVpZCcsICdjb21wb25lbnRUeXBlJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdyZW1vdmVfY29tcG9uZW50JyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1JlbW92ZSBhIGNvbXBvbmVudCBmcm9tIGEgbm9kZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVVdWlkOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdOb2RlIFVVSUQnXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgY29tcG9uZW50VHlwZToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ29tcG9uZW50IHR5cGUgdG8gcmVtb3ZlJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydub2RlVXVpZCcsICdjb21wb25lbnRUeXBlJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfY29tcG9uZW50cycsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgYWxsIGNvbXBvbmVudHMgb2YgYSBub2RlJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgbm9kZVV1aWQ6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ05vZGUgVVVJRCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsnbm9kZVV1aWQnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2dldF9jb21wb25lbnRfaW5mbycsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgc3BlY2lmaWMgY29tcG9uZW50IGluZm9ybWF0aW9uJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgbm9kZVV1aWQ6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ05vZGUgVVVJRCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBjb21wb25lbnRUeXBlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdDb21wb25lbnQgdHlwZSB0byBnZXQgaW5mbyBmb3InXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ25vZGVVdWlkJywgJ2NvbXBvbmVudFR5cGUnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ3NldF9jb21wb25lbnRfcHJvcGVydHknLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IGNvbXBvbmVudCBwcm9wZXJ0eSB2YWx1ZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVVdWlkOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdOb2RlIFVVSUQnXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgY29tcG9uZW50VHlwZToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ29tcG9uZW50IHR5cGUnXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgcHJvcGVydHk6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1Byb3BlcnR5IG5hbWUnXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1Byb3BlcnR5IHZhbHVlJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydub2RlVXVpZCcsICdjb21wb25lbnRUeXBlJywgJ3Byb3BlcnR5JywgJ3ZhbHVlJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdhdHRhY2hfc2NyaXB0JyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0F0dGFjaCBhIHNjcmlwdCBjb21wb25lbnQgdG8gYSBub2RlJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgbm9kZVV1aWQ6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ05vZGUgVVVJRCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBzY3JpcHRQYXRoOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdTY3JpcHQgYXNzZXQgcGF0aCAoZS5nLiwgZGI6Ly9hc3NldHMvc2NyaXB0cy9NeVNjcmlwdC50cyknXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ25vZGVVdWlkJywgJ3NjcmlwdFBhdGgnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2dldF9hdmFpbGFibGVfY29tcG9uZW50cycsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgbGlzdCBvZiBhdmFpbGFibGUgY29tcG9uZW50IHR5cGVzJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcnk6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0NvbXBvbmVudCBjYXRlZ29yeSBmaWx0ZXInLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVudW06IFsnYWxsJywgJ3JlbmRlcmVyJywgJ3VpJywgJ3BoeXNpY3MnLCAnYW5pbWF0aW9uJywgJ2F1ZGlvJ10sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDogJ2FsbCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgXTtcbiAgICB9XG5cbiAgICBhc3luYyBleGVjdXRlKHRvb2xOYW1lOiBzdHJpbmcsIGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHN3aXRjaCAodG9vbE5hbWUpIHtcbiAgICAgICAgICAgIGNhc2UgJ2FkZF9jb21wb25lbnQnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmFkZENvbXBvbmVudChhcmdzLm5vZGVVdWlkLCBhcmdzLmNvbXBvbmVudFR5cGUpO1xuICAgICAgICAgICAgY2FzZSAncmVtb3ZlX2NvbXBvbmVudCc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMucmVtb3ZlQ29tcG9uZW50KGFyZ3Mubm9kZVV1aWQsIGFyZ3MuY29tcG9uZW50VHlwZSk7XG4gICAgICAgICAgICBjYXNlICdnZXRfY29tcG9uZW50cyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0Q29tcG9uZW50cyhhcmdzLm5vZGVVdWlkKTtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9jb21wb25lbnRfaW5mbyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0Q29tcG9uZW50SW5mbyhhcmdzLm5vZGVVdWlkLCBhcmdzLmNvbXBvbmVudFR5cGUpO1xuICAgICAgICAgICAgY2FzZSAnc2V0X2NvbXBvbmVudF9wcm9wZXJ0eSc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuc2V0Q29tcG9uZW50UHJvcGVydHkoYXJncyk7XG4gICAgICAgICAgICBjYXNlICdhdHRhY2hfc2NyaXB0JzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5hdHRhY2hTY3JpcHQoYXJncy5ub2RlVXVpZCwgYXJncy5zY3JpcHRQYXRoKTtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9hdmFpbGFibGVfY29tcG9uZW50cyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0QXZhaWxhYmxlQ29tcG9uZW50cyhhcmdzLmNhdGVnb3J5KTtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIHRvb2w6ICR7dG9vbE5hbWV9YCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGFkZENvbXBvbmVudChub2RlVXVpZDogc3RyaW5nLCBjb21wb25lbnRUeXBlOiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIC8vIOWwneivleebtOaOpeS9v+eUqCBFZGl0b3IgQVBJIOa3u+WKoOe7hOS7tlxuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnY3JlYXRlLWNvbXBvbmVudCcsIHtcbiAgICAgICAgICAgICAgICB1dWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICBjb21wb25lbnQ6IGNvbXBvbmVudFR5cGVcbiAgICAgICAgICAgIH0pLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudElkOiByZXN1bHQsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgQ29tcG9uZW50ICcke2NvbXBvbmVudFR5cGV9JyBhZGRlZCBzdWNjZXNzZnVsbHlgXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8g5aSH55So5pa55qGI77ya5L2/55So5Zy65pmv6ISa5pysXG4gICAgICAgICAgICAgICAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICAgICAgICAgICAgICAgICAgbmFtZTogJ2NvY29zLW1jcC1zZXJ2ZXInLFxuICAgICAgICAgICAgICAgICAgICBtZXRob2Q6ICdhZGRDb21wb25lbnRUb05vZGUnLFxuICAgICAgICAgICAgICAgICAgICBhcmdzOiBbbm9kZVV1aWQsIGNvbXBvbmVudFR5cGVdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdleGVjdXRlLXNjZW5lLXNjcmlwdCcsIG9wdGlvbnMpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9KS5jYXRjaCgoZXJyMjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYERpcmVjdCBBUEkgZmFpbGVkOiAke2Vyci5tZXNzYWdlfSwgU2NlbmUgc2NyaXB0IGZhaWxlZDogJHtlcnIyLm1lc3NhZ2V9YCB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIHJlbW92ZUNvbXBvbmVudChub2RlVXVpZDogc3RyaW5nLCBjb21wb25lbnRUeXBlOiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IG9wdGlvbnMgPSB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2NvY29zLW1jcC1zZXJ2ZXInLFxuICAgICAgICAgICAgICAgIG1ldGhvZDogJ3JlbW92ZUNvbXBvbmVudEZyb21Ob2RlJyxcbiAgICAgICAgICAgICAgICBhcmdzOiBbbm9kZVV1aWQsIGNvbXBvbmVudFR5cGVdXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdleGVjdXRlLXNjZW5lLXNjcmlwdCcsIG9wdGlvbnMpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZShyZXN1bHQpO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldENvbXBvbmVudHMobm9kZVV1aWQ6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgLy8g5LyY5YWI5bCd6K+V55u05o6l5L2/55SoIEVkaXRvciBBUEkg5p+l6K+i6IqC54K55L+h5oGvXG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdxdWVyeS1ub2RlJywgbm9kZVV1aWQpLnRoZW4oKG5vZGVEYXRhOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAobm9kZURhdGEgJiYgbm9kZURhdGEuX19jb21wc19fKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGNvbXBvbmVudHMgPSBub2RlRGF0YS5fX2NvbXBzX18ubWFwKChjb21wOiBhbnkpID0+ICh7XG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiBjb21wLl9fdHlwZV9fIHx8ICdVbmtub3duJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZWQ6IGNvbXAuZW5hYmxlZCAhPT0gdW5kZWZpbmVkID8gY29tcC5lbmFibGVkIDogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHRoaXMuZXh0cmFjdENvbXBvbmVudFByb3BlcnRpZXMoY29tcClcbiAgICAgICAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVVdWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wb25lbnRzOiBjb21wb25lbnRzXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6ICdOb2RlIG5vdCBmb3VuZCBvciBubyBjb21wb25lbnRzIGRhdGEnIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8g5aSH55So5pa55qGI77ya5L2/55So5Zy65pmv6ISa5pysXG4gICAgICAgICAgICAgICAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICAgICAgICAgICAgICAgICAgbmFtZTogJ2NvY29zLW1jcC1zZXJ2ZXInLFxuICAgICAgICAgICAgICAgICAgICBtZXRob2Q6ICdnZXROb2RlSW5mbycsXG4gICAgICAgICAgICAgICAgICAgIGFyZ3M6IFtub2RlVXVpZF1cbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ2V4ZWN1dGUtc2NlbmUtc2NyaXB0Jywgb3B0aW9ucykudGhlbigocmVzdWx0OiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHJlc3VsdC5zdWNjZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHJlc3VsdC5kYXRhLmNvbXBvbmVudHNcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShyZXN1bHQpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSkuY2F0Y2goKGVycjI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGBEaXJlY3QgQVBJIGZhaWxlZDogJHtlcnIubWVzc2FnZX0sIFNjZW5lIHNjcmlwdCBmYWlsZWQ6ICR7ZXJyMi5tZXNzYWdlfWAgfSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRDb21wb25lbnRJbmZvKG5vZGVVdWlkOiBzdHJpbmcsIGNvbXBvbmVudFR5cGU6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgLy8g5LyY5YWI5bCd6K+V55u05o6l5L2/55SoIEVkaXRvciBBUEkg5p+l6K+i6IqC54K55L+h5oGvXG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdxdWVyeS1ub2RlJywgbm9kZVV1aWQpLnRoZW4oKG5vZGVEYXRhOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAobm9kZURhdGEgJiYgbm9kZURhdGEuX19jb21wc19fKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGNvbXBvbmVudCA9IG5vZGVEYXRhLl9fY29tcHNfXy5maW5kKChjb21wOiBhbnkpID0+IGNvbXAuX190eXBlX18gPT09IGNvbXBvbmVudFR5cGUpO1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgaWYgKGNvbXBvbmVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVVdWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcG9uZW50VHlwZTogY29tcG9uZW50VHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5hYmxlZDogY29tcG9uZW50LmVuYWJsZWQgIT09IHVuZGVmaW5lZCA/IGNvbXBvbmVudC5lbmFibGVkIDogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczogdGhpcy5leHRyYWN0Q29tcG9uZW50UHJvcGVydGllcyhjb21wb25lbnQpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBgQ29tcG9uZW50ICcke2NvbXBvbmVudFR5cGV9JyBub3QgZm91bmQgb24gbm9kZWAgfSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiAnTm9kZSBub3QgZm91bmQgb3Igbm8gY29tcG9uZW50cyBkYXRhJyB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIC8vIOWkh+eUqOaWueahiO+8muS9v+eUqOWcuuaZr+iEmuacrFxuICAgICAgICAgICAgICAgIGNvbnN0IG9wdGlvbnMgPSB7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6ICdjb2Nvcy1tY3Atc2VydmVyJyxcbiAgICAgICAgICAgICAgICAgICAgbWV0aG9kOiAnZ2V0Tm9kZUluZm8nLFxuICAgICAgICAgICAgICAgICAgICBhcmdzOiBbbm9kZVV1aWRdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdleGVjdXRlLXNjZW5lLXNjcmlwdCcsIG9wdGlvbnMpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChyZXN1bHQuc3VjY2VzcyAmJiByZXN1bHQuZGF0YS5jb21wb25lbnRzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBjb21wb25lbnQgPSByZXN1bHQuZGF0YS5jb21wb25lbnRzLmZpbmQoKGNvbXA6IGFueSkgPT4gY29tcC50eXBlID09PSBjb21wb25lbnRUeXBlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChjb21wb25lbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZVV1aWQ6IG5vZGVVdWlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcG9uZW50VHlwZTogY29tcG9uZW50VHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4uLmNvbXBvbmVudFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGBDb21wb25lbnQgJyR7Y29tcG9uZW50VHlwZX0nIG5vdCBmb3VuZCBvbiBub2RlYCB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IHJlc3VsdC5lcnJvciB8fCAnRmFpbGVkIHRvIGdldCBjb21wb25lbnQgaW5mbycgfSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KS5jYXRjaCgoZXJyMjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYERpcmVjdCBBUEkgZmFpbGVkOiAke2Vyci5tZXNzYWdlfSwgU2NlbmUgc2NyaXB0IGZhaWxlZDogJHtlcnIyLm1lc3NhZ2V9YCB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGV4dHJhY3RDb21wb25lbnRQcm9wZXJ0aWVzKGNvbXBvbmVudDogYW55KTogUmVjb3JkPHN0cmluZywgYW55PiB7XG4gICAgICAgIGNvbnN0IHByb3BlcnRpZXM6IFJlY29yZDxzdHJpbmcsIGFueT4gPSB7fTtcbiAgICAgICAgY29uc3QgZXhjbHVkZUtleXMgPSBbJ19fdHlwZV9fJywgJ2VuYWJsZWQnLCAnbm9kZScsICdfaWQnXTtcbiAgICAgICAgXG4gICAgICAgIGZvciAoY29uc3Qga2V5IGluIGNvbXBvbmVudCkge1xuICAgICAgICAgICAgaWYgKCFleGNsdWRlS2V5cy5pbmNsdWRlcyhrZXkpICYmICFrZXkuc3RhcnRzV2l0aCgnXycpKSB7XG4gICAgICAgICAgICAgICAgcHJvcGVydGllc1trZXldID0gY29tcG9uZW50W2tleV07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIHJldHVybiBwcm9wZXJ0aWVzO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgc2V0Q29tcG9uZW50UHJvcGVydHkoYXJnczogYW55KTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICAvLyDpppblhYjojrflj5boioLngrnkv6Hmga/ku6Xmib7liLDmraPnoa7nmoTnu4Tku7bntKLlvJVcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ3F1ZXJ5LW5vZGUnLCBhcmdzLm5vZGVVdWlkKS50aGVuKChub2RlRGF0YTogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKCFub2RlRGF0YSB8fCAhbm9kZURhdGEuX19jb21wc19fKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTm9kZSBub3QgZm91bmQgb3Igbm8gY29tcG9uZW50cyBkYXRhJyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIC8vIOafpeaJvue7hOS7tue0ouW8lVxuICAgICAgICAgICAgICAgIGxldCBjb21wb25lbnRJbmRleCA9IC0xO1xuICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbm9kZURhdGEuX19jb21wc19fLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGNvbXAgPSBub2RlRGF0YS5fX2NvbXBzX19baV07XG4gICAgICAgICAgICAgICAgICAgIGlmIChjb21wLl9fdHlwZV9fID09PSBhcmdzLmNvbXBvbmVudFR5cGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudEluZGV4ID0gaTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIGlmIChjb21wb25lbnRJbmRleCA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDb21wb25lbnQgJyR7YXJncy5jb21wb25lbnRUeXBlfScgbm90IGZvdW5kIG9uIG5vZGVgKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgLy8g5L2/55So5q2j56Gu55qE57uE5Lu257Si5byV6Lev5b6EXG4gICAgICAgICAgICAgICAgY29uc3QgcHJvcGVydHlQYXRoID0gYF9fY29tcHNfXy4ke2NvbXBvbmVudEluZGV4fS4ke2FyZ3MucHJvcGVydHl9YDtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICByZXR1cm4gRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnc2V0LXByb3BlcnR5Jywge1xuICAgICAgICAgICAgICAgICAgICB1dWlkOiBhcmdzLm5vZGVVdWlkLFxuICAgICAgICAgICAgICAgICAgICBwYXRoOiBwcm9wZXJ0eVBhdGgsXG4gICAgICAgICAgICAgICAgICAgIGR1bXA6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlOiBhcmdzLnZhbHVlXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgQ29tcG9uZW50IHByb3BlcnR5ICcke2FyZ3MucHJvcGVydHl9JyB1cGRhdGVkIHN1Y2Nlc3NmdWxseWBcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8g5aSH55So5pa55qGI77ya5L2/55So5Zy65pmv6ISa5pysXG4gICAgICAgICAgICAgICAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICAgICAgICAgICAgICAgICAgbmFtZTogJ2NvY29zLW1jcC1zZXJ2ZXInLFxuICAgICAgICAgICAgICAgICAgICBtZXRob2Q6ICdzZXRDb21wb25lbnRQcm9wZXJ0eScsXG4gICAgICAgICAgICAgICAgICAgIGFyZ3M6IFthcmdzLm5vZGVVdWlkLCBhcmdzLmNvbXBvbmVudFR5cGUsIGFyZ3MucHJvcGVydHksIGFyZ3MudmFsdWVdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdleGVjdXRlLXNjZW5lLXNjcmlwdCcsIG9wdGlvbnMpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9KS5jYXRjaCgoZXJyMjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYERpcmVjdCBBUEkgZmFpbGVkOiAke2Vyci5tZXNzYWdlfSwgU2NlbmUgc2NyaXB0IGZhaWxlZDogJHtlcnIyLm1lc3NhZ2V9YCB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGF0dGFjaFNjcmlwdChub2RlVXVpZDogc3RyaW5nLCBzY3JpcHRQYXRoOiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIC8vIOS7juiEmuacrOi3r+W+hOaPkOWPlue7hOS7tuexu+WQjVxuICAgICAgICAgICAgY29uc3Qgc2NyaXB0TmFtZSA9IHNjcmlwdFBhdGguc3BsaXQoJy8nKS5wb3AoKT8ucmVwbGFjZSgnLnRzJywgJycpLnJlcGxhY2UoJy5qcycsICcnKTtcbiAgICAgICAgICAgIGlmICghc2NyaXB0TmFtZSkge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6ICdJbnZhbGlkIHNjcmlwdCBwYXRoJyB9KTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOmmluWFiOWwneivleebtOaOpeS9v+eUqOiEmuacrOWQjeensOS9nOS4uue7hOS7tuexu+Wei1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnY3JlYXRlLWNvbXBvbmVudCcsIHtcbiAgICAgICAgICAgICAgICB1dWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICBjb21wb25lbnQ6IHNjcmlwdE5hbWUgIC8vIOS9v+eUqOiEmuacrOWQjeensOiAjOmdnlVVSURcbiAgICAgICAgICAgIH0pLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudElkOiByZXN1bHQsXG4gICAgICAgICAgICAgICAgICAgICAgICBzY3JpcHRQYXRoOiBzY3JpcHRQYXRoLFxuICAgICAgICAgICAgICAgICAgICAgICAgY29tcG9uZW50TmFtZTogc2NyaXB0TmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBTY3JpcHQgJyR7c2NyaXB0TmFtZX0nIGF0dGFjaGVkIHN1Y2Nlc3NmdWxseWBcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAvLyDlpIfnlKjmlrnmoYjvvJrkvb/nlKjlnLrmma/ohJrmnKxcbiAgICAgICAgICAgICAgICBjb25zdCBvcHRpb25zID0ge1xuICAgICAgICAgICAgICAgICAgICBuYW1lOiAnY29jb3MtbWNwLXNlcnZlcicsXG4gICAgICAgICAgICAgICAgICAgIG1ldGhvZDogJ2F0dGFjaFNjcmlwdCcsXG4gICAgICAgICAgICAgICAgICAgIGFyZ3M6IFtub2RlVXVpZCwgc2NyaXB0UGF0aF1cbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ2V4ZWN1dGUtc2NlbmUtc2NyaXB0Jywgb3B0aW9ucykudGhlbigocmVzdWx0OiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShyZXN1bHQpO1xuICAgICAgICAgICAgICAgIH0pLmNhdGNoKChlcnIyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHsgXG4gICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSwgXG4gICAgICAgICAgICAgICAgICAgICAgICBlcnJvcjogYEZhaWxlZCB0byBhdHRhY2ggc2NyaXB0ICcke3NjcmlwdE5hbWV9JzogJHtlcnIubWVzc2FnZX1gLFxuICAgICAgICAgICAgICAgICAgICAgICAgaW5zdHJ1Y3Rpb246ICdQbGVhc2UgZW5zdXJlIHRoZSBzY3JpcHQgaXMgcHJvcGVybHkgY29tcGlsZWQgYW5kIGV4cG9ydGVkIGFzIGEgQ29tcG9uZW50IGNsYXNzLiBZb3UgY2FuIGFsc28gbWFudWFsbHkgYXR0YWNoIHRoZSBzY3JpcHQgdGhyb3VnaCB0aGUgUHJvcGVydGllcyBwYW5lbCBpbiB0aGUgZWRpdG9yLidcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRBdmFpbGFibGVDb21wb25lbnRzKGNhdGVnb3J5OiBzdHJpbmcgPSAnYWxsJyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIGNvbnN0IGNvbXBvbmVudENhdGVnb3JpZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZ1tdPiA9IHtcbiAgICAgICAgICAgIHJlbmRlcmVyOiBbJ2NjLlNwcml0ZScsICdjYy5MYWJlbCcsICdjYy5SaWNoVGV4dCcsICdjYy5NYXNrJywgJ2NjLkdyYXBoaWNzJ10sXG4gICAgICAgICAgICB1aTogWydjYy5CdXR0b24nLCAnY2MuVG9nZ2xlJywgJ2NjLlNsaWRlcicsICdjYy5TY3JvbGxWaWV3JywgJ2NjLkVkaXRCb3gnLCAnY2MuUHJvZ3Jlc3NCYXInXSxcbiAgICAgICAgICAgIHBoeXNpY3M6IFsnY2MuUmlnaWRCb2R5MkQnLCAnY2MuQm94Q29sbGlkZXIyRCcsICdjYy5DaXJjbGVDb2xsaWRlcjJEJywgJ2NjLlBvbHlnb25Db2xsaWRlcjJEJ10sXG4gICAgICAgICAgICBhbmltYXRpb246IFsnY2MuQW5pbWF0aW9uJywgJ2NjLkFuaW1hdGlvbkNsaXAnLCAnY2MuU2tlbGV0YWxBbmltYXRpb24nXSxcbiAgICAgICAgICAgIGF1ZGlvOiBbJ2NjLkF1ZGlvU291cmNlJ10sXG4gICAgICAgICAgICBsYXlvdXQ6IFsnY2MuTGF5b3V0JywgJ2NjLldpZGdldCcsICdjYy5QYWdlVmlldycsICdjYy5QYWdlVmlld0luZGljYXRvciddLFxuICAgICAgICAgICAgZWZmZWN0czogWydjYy5Nb3Rpb25TdHJlYWsnLCAnY2MuUGFydGljbGVTeXN0ZW0yRCddLFxuICAgICAgICAgICAgY2FtZXJhOiBbJ2NjLkNhbWVyYSddLFxuICAgICAgICAgICAgbGlnaHQ6IFsnY2MuTGlnaHQnLCAnY2MuRGlyZWN0aW9uYWxMaWdodCcsICdjYy5Qb2ludExpZ2h0JywgJ2NjLlNwb3RMaWdodCddXG4gICAgICAgIH07XG5cbiAgICAgICAgbGV0IGNvbXBvbmVudHM6IHN0cmluZ1tdID0gW107XG4gICAgICAgIFxuICAgICAgICBpZiAoY2F0ZWdvcnkgPT09ICdhbGwnKSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGNhdCBpbiBjb21wb25lbnRDYXRlZ29yaWVzKSB7XG4gICAgICAgICAgICAgICAgY29tcG9uZW50cyA9IGNvbXBvbmVudHMuY29uY2F0KGNvbXBvbmVudENhdGVnb3JpZXNbY2F0XSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoY29tcG9uZW50Q2F0ZWdvcmllc1tjYXRlZ29yeV0pIHtcbiAgICAgICAgICAgIGNvbXBvbmVudHMgPSBjb21wb25lbnRDYXRlZ29yaWVzW2NhdGVnb3J5XTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgIGNhdGVnb3J5OiBjYXRlZ29yeSxcbiAgICAgICAgICAgICAgICBjb21wb25lbnRzOiBjb21wb25lbnRzXG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgfVxufSJdfQ== \ No newline at end of file diff --git a/dist/tools/debug-tools.js b/dist/tools/debug-tools.js new file mode 100644 index 0000000..a760746 --- /dev/null +++ b/dist/tools/debug-tools.js @@ -0,0 +1,327 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DebugTools = void 0; +class DebugTools { + constructor() { + this.consoleMessages = []; + this.maxMessages = 1000; + this.setupConsoleCapture(); + } + setupConsoleCapture() { + // Intercept Editor console messages + // Note: Editor.Message.addBroadcastListener may not be available in all versions + // This is a placeholder for console capture implementation + console.log('Console capture setup - implementation depends on Editor API availability'); + } + addConsoleMessage(message) { + this.consoleMessages.push(Object.assign({ timestamp: new Date().toISOString() }, message)); + // Keep only latest messages + if (this.consoleMessages.length > this.maxMessages) { + this.consoleMessages.shift(); + } + } + getTools() { + return [ + { + name: 'get_console_logs', + description: 'Get editor console logs', + inputSchema: { + type: 'object', + properties: { + limit: { + type: 'number', + description: 'Number of recent logs to retrieve', + default: 100 + }, + filter: { + type: 'string', + description: 'Filter logs by type', + enum: ['all', 'log', 'warn', 'error', 'info'], + default: 'all' + } + } + } + }, + { + name: 'clear_console', + description: 'Clear editor console', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'execute_script', + description: 'Execute JavaScript in scene context', + inputSchema: { + type: 'object', + properties: { + script: { + type: 'string', + description: 'JavaScript code to execute' + } + }, + required: ['script'] + } + }, + { + name: 'get_node_tree', + description: 'Get detailed node tree for debugging', + inputSchema: { + type: 'object', + properties: { + rootUuid: { + type: 'string', + description: 'Root node UUID (optional, uses scene root if not provided)' + }, + maxDepth: { + type: 'number', + description: 'Maximum tree depth', + default: 10 + } + } + } + }, + { + name: 'get_performance_stats', + description: 'Get performance statistics', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'validate_scene', + description: 'Validate current scene for issues', + inputSchema: { + type: 'object', + properties: { + checkMissingAssets: { + type: 'boolean', + description: 'Check for missing asset references', + default: true + }, + checkPerformance: { + type: 'boolean', + description: 'Check for performance issues', + default: true + } + } + } + }, + { + name: 'get_editor_info', + description: 'Get editor and environment information', + inputSchema: { + type: 'object', + properties: {} + } + } + ]; + } + async execute(toolName, args) { + switch (toolName) { + case 'get_console_logs': + return await this.getConsoleLogs(args.limit, args.filter); + case 'clear_console': + return await this.clearConsole(); + case 'execute_script': + return await this.executeScript(args.script); + case 'get_node_tree': + return await this.getNodeTree(args.rootUuid, args.maxDepth); + case 'get_performance_stats': + return await this.getPerformanceStats(); + case 'validate_scene': + return await this.validateScene(args); + case 'get_editor_info': + return await this.getEditorInfo(); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + async getConsoleLogs(limit = 100, filter = 'all') { + let logs = this.consoleMessages; + if (filter !== 'all') { + logs = logs.filter(log => log.type === filter); + } + const recentLogs = logs.slice(-limit); + return { + success: true, + data: { + total: logs.length, + returned: recentLogs.length, + logs: recentLogs + } + }; + } + async clearConsole() { + this.consoleMessages = []; + try { + // Note: Editor.Message.send may not return a promise in all versions + Editor.Message.send('console', 'clear'); + return { + success: true, + message: 'Console cleared successfully' + }; + } + catch (err) { + return { success: false, error: err.message }; + } + } + async executeScript(script) { + return new Promise((resolve) => { + Editor.Message.request('scene', 'execute-script', { + script: script + }).then((result) => { + resolve({ + success: true, + data: { + result: result, + message: 'Script executed successfully' + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async getNodeTree(rootUuid, maxDepth = 10) { + return new Promise((resolve) => { + const buildTree = async (nodeUuid, depth = 0) => { + if (depth >= maxDepth) { + return { truncated: true }; + } + try { + const nodeData = await Editor.Message.request('scene', 'query-node', nodeUuid); + const tree = { + uuid: nodeData.uuid, + name: nodeData.name, + active: nodeData.active, + components: nodeData.components ? nodeData.components.map((c) => c.__type__) : [], + childCount: nodeData.children ? nodeData.children.length : 0, + children: [] + }; + if (nodeData.children && nodeData.children.length > 0) { + for (const childId of nodeData.children) { + const childTree = await buildTree(childId, depth + 1); + tree.children.push(childTree); + } + } + return tree; + } + catch (err) { + return { error: err.message }; + } + }; + if (rootUuid) { + buildTree(rootUuid).then(tree => { + resolve({ success: true, data: tree }); + }); + } + else { + Editor.Message.request('scene', 'query-hierarchy').then(async (hierarchy) => { + const trees = []; + for (const rootNode of hierarchy.children) { + const tree = await buildTree(rootNode.uuid); + trees.push(tree); + } + resolve({ success: true, data: trees }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + } + }); + } + async getPerformanceStats() { + return new Promise((resolve) => { + Editor.Message.request('scene', 'query-performance').then((stats) => { + const perfStats = { + nodeCount: stats.nodeCount || 0, + componentCount: stats.componentCount || 0, + drawCalls: stats.drawCalls || 0, + triangles: stats.triangles || 0, + memory: stats.memory || {} + }; + resolve({ success: true, data: perfStats }); + }).catch(() => { + // Fallback to basic stats + resolve({ + success: true, + data: { + message: 'Performance stats not available in edit mode' + } + }); + }); + }); + } + async validateScene(options) { + const issues = []; + try { + // Check for missing assets + if (options.checkMissingAssets) { + const assetCheck = await Editor.Message.request('scene', 'check-missing-assets'); + if (assetCheck && assetCheck.missing) { + issues.push({ + type: 'error', + category: 'assets', + message: `Found ${assetCheck.missing.length} missing asset references`, + details: assetCheck.missing + }); + } + } + // Check for performance issues + if (options.checkPerformance) { + const hierarchy = await Editor.Message.request('scene', 'query-hierarchy'); + const nodeCount = this.countNodes(hierarchy.children); + if (nodeCount > 1000) { + issues.push({ + type: 'warning', + category: 'performance', + message: `High node count: ${nodeCount} nodes (recommended < 1000)`, + suggestion: 'Consider using object pooling or scene optimization' + }); + } + } + const result = { + valid: issues.length === 0, + issueCount: issues.length, + issues: issues + }; + return { success: true, data: result }; + } + catch (err) { + return { success: false, error: err.message }; + } + } + countNodes(nodes) { + let count = nodes.length; + for (const node of nodes) { + if (node.children) { + count += this.countNodes(node.children); + } + } + return count; + } + async getEditorInfo() { + var _a, _b; + const info = { + editor: { + version: ((_a = Editor.versions) === null || _a === void 0 ? void 0 : _a.editor) || 'Unknown', + cocosVersion: ((_b = Editor.versions) === null || _b === void 0 ? void 0 : _b.cocos) || 'Unknown', + platform: process.platform, + arch: process.arch, + nodeVersion: process.version + }, + project: { + name: Editor.Project.name, + path: Editor.Project.path, + uuid: Editor.Project.uuid + }, + memory: process.memoryUsage(), + uptime: process.uptime() + }; + return { success: true, data: info }; + } +} +exports.DebugTools = DebugTools; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVidWctdG9vbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvdG9vbHMvZGVidWctdG9vbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBRUEsTUFBYSxVQUFVO0lBSW5CO1FBSFEsb0JBQWUsR0FBcUIsRUFBRSxDQUFDO1FBQzlCLGdCQUFXLEdBQUcsSUFBSSxDQUFDO1FBR2hDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO0lBQy9CLENBQUM7SUFFTyxtQkFBbUI7UUFDdkIsb0NBQW9DO1FBQ3BDLGlGQUFpRjtRQUNqRiwyREFBMkQ7UUFDM0QsT0FBTyxDQUFDLEdBQUcsQ0FBQywyRUFBMkUsQ0FBQyxDQUFDO0lBQzdGLENBQUM7SUFFTyxpQkFBaUIsQ0FBQyxPQUFZO1FBQ2xDLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxpQkFDckIsU0FBUyxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLElBQ2hDLE9BQU8sRUFDWixDQUFDO1FBRUgsNEJBQTRCO1FBQzVCLElBQUksSUFBSSxDQUFDLGVBQWUsQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ2pELElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDakMsQ0FBQztJQUNMLENBQUM7SUFFRCxRQUFRO1FBQ0osT0FBTztZQUNIO2dCQUNJLElBQUksRUFBRSxrQkFBa0I7Z0JBQ3hCLFdBQVcsRUFBRSx5QkFBeUI7Z0JBQ3RDLFdBQVcsRUFBRTtvQkFDVCxJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUU7d0JBQ1IsS0FBSyxFQUFFOzRCQUNILElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSxtQ0FBbUM7NEJBQ2hELE9BQU8sRUFBRSxHQUFHO3lCQUNmO3dCQUNELE1BQU0sRUFBRTs0QkFDSixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUscUJBQXFCOzRCQUNsQyxJQUFJLEVB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LENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQzVDLENBQUM7UUFDTCxDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUM7SUFDakIsQ0FBQztJQUVPLEtBQUssQ0FBQyxhQUFhOztRQUN2QixNQUFNLElBQUksR0FBRztZQUNULE1BQU0sRUFBRTtnQkFDSixPQUFPLEVBQUUsQ0FBQSxNQUFDLE1BQWMsQ0FBQyxRQUFRLDBDQUFFLE1BQU0sS0FBSSxTQUFTO2dCQUN0RCxZQUFZLEVBQUUsQ0FBQSxNQUFDLE1BQWMsQ0FBQyxRQUFRLDBDQUFFLEtBQUssS0FBSSxTQUFTO2dCQUMxRCxRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVE7Z0JBQzFCLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtnQkFDbEIsV0FBVyxFQUFFLE9BQU8sQ0FBQyxPQUFPO2FBQy9CO1lBQ0QsT0FBTyxFQUFFO2dCQUNMLElBQUksRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUk7Z0JBQ3pCLElBQUksRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUk7Z0JBQ3pCLElBQUksRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUk7YUFDNUI7WUFDRCxNQUFNLEVBQUUsT0FBTyxDQUFDLFdBQVcsRUFBRTtZQUM3QixNQUFNLEVBQUUsT0FBTyxDQUFDLE1BQU0sRUFBRTtTQUMzQixDQUFDO1FBRUYsT0FBTyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDO0lBQ3pDLENBQUM7Q0FDSjtBQTVWRCxnQ0E0VkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBUb29sRGVmaW5pdGlvbiwgVG9vbFJlc3BvbnNlLCBUb29sRXhlY3V0b3IsIENvbnNvbGVNZXNzYWdlLCBQZXJmb3JtYW5jZVN0YXRzLCBWYWxpZGF0aW9uUmVzdWx0LCBWYWxpZGF0aW9uSXNzdWUgfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBjbGFzcyBEZWJ1Z1Rvb2xzIGltcGxlbWVudHMgVG9vbEV4ZWN1dG9yIHtcbiAgICBwcml2YXRlIGNvbnNvbGVNZXNzYWdlczogQ29uc29sZU1lc3NhZ2VbXSA9IFtdO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgbWF4TWVzc2FnZXMgPSAxMDAwO1xuXG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuc2V0dXBDb25zb2xlQ2FwdHVyZSgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgc2V0dXBDb25zb2xlQ2FwdHVyZSgpOiB2b2lkIHtcbiAgICAgICAgLy8gSW50ZXJjZXB0IEVkaXRvciBjb25zb2xlIG1lc3NhZ2VzXG4gICAgICAgIC8vIE5vdGU6IEVkaXRvci5NZXNzYWdlLmFkZEJyb2FkY2FzdExpc3RlbmVyIG1heSBub3QgYmUgYXZhaWxhYmxlIGluIGFsbCB2ZXJzaW9uc1xuICAgICAgICAvLyBUaGlzIGlzIGEgcGxhY2Vob2xkZXIgZm9yIGNvbnNvbGUgY2FwdHVyZSBpbXBsZW1lbnRhdGlvblxuICAgICAgICBjb25zb2xlLmxvZygnQ29uc29sZSBjYXB0dXJlIHNldHVwIC0gaW1wbGVtZW50YXRpb24gZGVwZW5kcyBvbiBFZGl0b3IgQVBJIGF2YWlsYWJpbGl0eScpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYWRkQ29uc29sZU1lc3NhZ2UobWVzc2FnZTogYW55KTogdm9pZCB7XG4gICAgICAgIHRoaXMuY29uc29sZU1lc3NhZ2VzLnB1c2goe1xuICAgICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICAgICAgICAuLi5tZXNzYWdlXG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIEtlZXAgb25seSBsYXRlc3QgbWVzc2FnZXNcbiAgICAgICAgaWYgKHRoaXMuY29uc29sZU1lc3NhZ2VzLmxlbmd0aCA+IHRoaXMubWF4TWVzc2FnZXMpIHtcbiAgICAgICAgICAgIHRoaXMuY29uc29sZU1lc3NhZ2VzLnNoaWZ0KCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBnZXRUb29scygpOiBUb29sRGVmaW5pdGlvbltdIHtcbiAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ2V0X2NvbnNvbGVfbG9ncycsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgZWRpdG9yIGNvbnNvbGUgbG9ncycsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0OiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdOdW1iZXIgb2YgcmVjZW50IGxvZ3MgdG8gcmV0cmlldmUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6IDEwMFxuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnRmlsdGVyIGxvZ3MgYnkgdHlwZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZW51bTogWydhbGwnLCAnbG9nJywgJ3dhcm4nLCAnZXJyb3InLCAnaW5mbyddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6ICdhbGwnXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdjbGVhcl9jb25zb2xlJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0NsZWFyIGVkaXRvciBjb25zb2xlJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdleGVjdXRlX3NjcmlwdCcsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdFeGVjdXRlIEphdmFTY3JpcHQgaW4gc2NlbmUgY29udGV4dCcsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNjcmlwdDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnSmF2YVNjcmlwdCBjb2RlIHRvIGV4ZWN1dGUnXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3NjcmlwdCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ2V0X25vZGVfdHJlZScsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgZGV0YWlsZWQgbm9kZSB0cmVlIGZvciBkZWJ1Z2dpbmcnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICByb290VXVpZDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUm9vdCBub2RlIFVVSUQgKG9wdGlvbmFsLCB1c2VzIHNjZW5lIHJvb3QgaWYgbm90IHByb3ZpZGVkKSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBtYXhEZXB0aDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSB0cmVlIGRlcHRoJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiAxMFxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ2V0X3BlcmZvcm1hbmNlX3N0YXRzJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBwZXJmb3JtYW5jZSBzdGF0aXN0aWNzJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICd2YWxpZGF0ZV9zY2VuZScsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdWYWxpZGF0ZSBjdXJyZW50IHNjZW5lIGZvciBpc3N1ZXMnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjaGVja01pc3NpbmdBc3NldHM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdDaGVjayBmb3IgbWlzc2luZyBhc3NldCByZWZlcmVuY2VzJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiB0cnVlXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2tQZXJmb3JtYW5jZToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0NoZWNrIGZvciBwZXJmb3JtYW5jZSBpc3N1ZXMnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6IHRydWVcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2dldF9lZGl0b3JfaW5mbycsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgZWRpdG9yIGFuZCBlbnZpcm9ubWVudCBpbmZvcm1hdGlvbicsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHt9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICBdO1xuICAgIH1cblxuICAgIGFzeW5jIGV4ZWN1dGUodG9vbE5hbWU6IHN0cmluZywgYXJnczogYW55KTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgc3dpdGNoICh0b29sTmFtZSkge1xuICAgICAgICAgICAgY2FzZSAnZ2V0X2NvbnNvbGVfbG9ncyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0Q29uc29sZUxvZ3MoYXJncy5saW1pdCwgYXJncy5maWx0ZXIpO1xuICAgICAgICAgICAgY2FzZSAnY2xlYXJfY29uc29sZSc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuY2xlYXJDb25zb2xlKCk7XG4gICAgICAgICAgICBjYXNlICdleGVjdXRlX3NjcmlwdCc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZXhlY3V0ZVNjcmlwdChhcmdzLnNjcmlwdCk7XG4gICAgICAgICAgICBjYXNlICdnZXRfbm9kZV90cmVlJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXROb2RlVHJlZShhcmdzLnJvb3RVdWlkLCBhcmdzLm1heERlcHRoKTtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9wZXJmb3JtYW5jZV9zdGF0cyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0UGVyZm9ybWFuY2VTdGF0cygpO1xuICAgICAgICAgICAgY2FzZSAndmFsaWRhdGVfc2NlbmUnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLnZhbGlkYXRlU2NlbmUoYXJncyk7XG4gICAgICAgICAgICBjYXNlICdnZXRfZWRpdG9yX2luZm8nOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmdldEVkaXRvckluZm8oKTtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIHRvb2w6ICR7dG9vbE5hbWV9YCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldENvbnNvbGVMb2dzKGxpbWl0OiBudW1iZXIgPSAxMDAsIGZpbHRlcjogc3RyaW5nID0gJ2FsbCcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICBsZXQgbG9ncyA9IHRoaXMuY29uc29sZU1lc3NhZ2VzO1xuICAgICAgICBcbiAgICAgICAgaWYgKGZpbHRlciAhPT0gJ2FsbCcpIHtcbiAgICAgICAgICAgIGxvZ3MgPSBsb2dzLmZpbHRlcihsb2cgPT4gbG9nLnR5cGUgPT09IGZpbHRlcik7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCByZWNlbnRMb2dzID0gbG9ncy5zbGljZSgtbGltaXQpO1xuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgdG90YWw6IGxvZ3MubGVuZ3RoLFxuICAgICAgICAgICAgICAgIHJldHVybmVkOiByZWNlbnRMb2dzLmxlbmd0aCxcbiAgICAgICAgICAgICAgICBsb2dzOiByZWNlbnRMb2dzXG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBjbGVhckNvbnNvbGUoKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgdGhpcy5jb25zb2xlTWVzc2FnZXMgPSBbXTtcbiAgICAgICAgXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBOb3RlOiBFZGl0b3IuTWVzc2FnZS5zZW5kIG1heSBub3QgcmV0dXJuIGEgcHJvbWlzZSBpbiBhbGwgdmVyc2lvbnNcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnNlbmQoJ2NvbnNvbGUnLCAnY2xlYXInKTtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiAnQ29uc29sZSBjbGVhcmVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH07XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGV4ZWN1dGVTY3JpcHQoc2NyaXB0OiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ2V4ZWN1dGUtc2NyaXB0Jywge1xuICAgICAgICAgICAgICAgIHNjcmlwdDogc2NyaXB0XG4gICAgICAgICAgICB9KS50aGVuKChyZXN1bHQ6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQ6IHJlc3VsdCxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdTY3JpcHQgZXhlY3V0ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0Tm9kZVRyZWUocm9vdFV1aWQ/OiBzdHJpbmcsIG1heERlcHRoOiBudW1iZXIgPSAxMCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgYnVpbGRUcmVlID0gYXN5bmMgKG5vZGVVdWlkOiBzdHJpbmcsIGRlcHRoOiBudW1iZXIgPSAwKTogUHJvbWlzZTxhbnk+ID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoZGVwdGggPj0gbWF4RGVwdGgpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgdHJ1bmNhdGVkOiB0cnVlIH07XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3Qgbm9kZURhdGEgPSBhd2FpdCBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdxdWVyeS1ub2RlJywgbm9kZVV1aWQpO1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgdHJlZSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHV1aWQ6IG5vZGVEYXRhLnV1aWQsXG4gICAgICAgICAgICAgICAgICAgICAgICBuYW1lOiBub2RlRGF0YS5uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgYWN0aXZlOiBub2RlRGF0YS5hY3RpdmUsXG4gICAgICAgICAgICAgICAgICAgICAgICBjb21wb25lbnRzOiAobm9kZURhdGEgYXMgYW55KS5jb21wb25lbnRzID8gKG5vZGVEYXRhIGFzIGFueSkuY29tcG9uZW50cy5tYXAoKGM6IGFueSkgPT4gYy5fX3R5cGVfXykgOiBbXSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNoaWxkQ291bnQ6IG5vZGVEYXRhLmNoaWxkcmVuID8gbm9kZURhdGEuY2hpbGRyZW4ubGVuZ3RoIDogMCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNoaWxkcmVuOiBbXSBhcyBhbnlbXVxuICAgICAgICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChub2RlRGF0YS5jaGlsZHJlbiAmJiBub2RlRGF0YS5jaGlsZHJlbi5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGNoaWxkSWQgb2Ygbm9kZURhdGEuY2hpbGRyZW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBjaGlsZFRyZWUgPSBhd2FpdCBidWlsZFRyZWUoY2hpbGRJZCwgZGVwdGggKyAxKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmVlLmNoaWxkcmVuLnB1c2goY2hpbGRUcmVlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cmVlO1xuICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB7IGVycm9yOiBlcnIubWVzc2FnZSB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIGlmIChyb290VXVpZCkge1xuICAgICAgICAgICAgICAgIGJ1aWxkVHJlZShyb290VXVpZCkudGhlbih0cmVlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IHRydWUsIGRhdGE6IHRyZWUgfSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ3F1ZXJ5LWhpZXJhcmNoeScpLnRoZW4oYXN5bmMgKGhpZXJhcmNoeTogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHRyZWVzID0gW107XG4gICAgICAgICAgICAgICAgICAgIGZvciAoY29uc3Qgcm9vdE5vZGUgb2YgaGllcmFyY2h5LmNoaWxkcmVuKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCB0cmVlID0gYXdhaXQgYnVpbGRUcmVlKHJvb3ROb2RlLnV1aWQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdHJlZXMucHVzaCh0cmVlKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogdHJ1ZSwgZGF0YTogdHJlZXMgfSk7XG4gICAgICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0UGVyZm9ybWFuY2VTdGF0cygpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ3F1ZXJ5LXBlcmZvcm1hbmNlJykudGhlbigoc3RhdHM6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBlcmZTdGF0czogUGVyZm9ybWFuY2VTdGF0cyA9IHtcbiAgICAgICAgICAgICAgICAgICAgbm9kZUNvdW50OiBzdGF0cy5ub2RlQ291bnQgfHwgMCxcbiAgICAgICAgICAgICAgICAgICAgY29tcG9uZW50Q291bnQ6IHN0YXRzLmNvbXBvbmVudENvdW50IHx8IDAsXG4gICAgICAgICAgICAgICAgICAgIGRyYXdDYWxsczogc3RhdHMuZHJhd0NhbGxzIHx8IDAsXG4gICAgICAgICAgICAgICAgICAgIHRyaWFuZ2xlczogc3RhdHMudHJpYW5nbGVzIHx8IDAsXG4gICAgICAgICAgICAgICAgICAgIG1lbW9yeTogc3RhdHMubWVtb3J5IHx8IHt9XG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogdHJ1ZSwgZGF0YTogcGVyZlN0YXRzIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKCkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIEZhbGxiYWNrIHRvIGJhc2ljIHN0YXRzXG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdQZXJmb3JtYW5jZSBzdGF0cyBub3QgYXZhaWxhYmxlIGluIGVkaXQgbW9kZSdcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgdmFsaWRhdGVTY2VuZShvcHRpb25zOiBhbnkpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICBjb25zdCBpc3N1ZXM6IFZhbGlkYXRpb25Jc3N1ZVtdID0gW107XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIENoZWNrIGZvciBtaXNzaW5nIGFzc2V0c1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMuY2hlY2tNaXNzaW5nQXNzZXRzKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgYXNzZXRDaGVjayA9IGF3YWl0IEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ2NoZWNrLW1pc3NpbmctYXNzZXRzJyk7XG4gICAgICAgICAgICAgICAgaWYgKGFzc2V0Q2hlY2sgJiYgYXNzZXRDaGVjay5taXNzaW5nKSB7XG4gICAgICAgICAgICAgICAgICAgIGlzc3Vlcy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdlcnJvcicsXG4gICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yeTogJ2Fzc2V0cycsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgRm91bmQgJHthc3NldENoZWNrLm1pc3NpbmcubGVuZ3RofSBtaXNzaW5nIGFzc2V0IHJlZmVyZW5jZXNgLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGV0YWlsczogYXNzZXRDaGVjay5taXNzaW5nXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQ2hlY2sgZm9yIHBlcmZvcm1hbmNlIGlzc3Vlc1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMuY2hlY2tQZXJmb3JtYW5jZSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGhpZXJhcmNoeSA9IGF3YWl0IEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ3F1ZXJ5LWhpZXJhcmNoeScpO1xuICAgICAgICAgICAgICAgIGNvbnN0IG5vZGVDb3VudCA9IHRoaXMuY291bnROb2RlcyhoaWVyYXJjaHkuY2hpbGRyZW4pO1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIGlmIChub2RlQ291bnQgPiAxMDAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGlzc3Vlcy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICd3YXJuaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3J5OiAncGVyZm9ybWFuY2UnLFxuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYEhpZ2ggbm9kZSBjb3VudDogJHtub2RlQ291bnR9IG5vZGVzIChyZWNvbW1lbmRlZCA8IDEwMDApYCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHN1Z2dlc3Rpb246ICdDb25zaWRlciB1c2luZyBvYmplY3QgcG9vbGluZyBvciBzY2VuZSBvcHRpbWl6YXRpb24nXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgcmVzdWx0OiBWYWxpZGF0aW9uUmVzdWx0ID0ge1xuICAgICAgICAgICAgICAgIHZhbGlkOiBpc3N1ZXMubGVuZ3RoID09PSAwLFxuICAgICAgICAgICAgICAgIGlzc3VlQ291bnQ6IGlzc3Vlcy5sZW5ndGgsXG4gICAgICAgICAgICAgICAgaXNzdWVzOiBpc3N1ZXNcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IHRydWUsIGRhdGE6IHJlc3VsdCB9O1xuICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9O1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBjb3VudE5vZGVzKG5vZGVzOiBhbnlbXSk6IG51bWJlciB7XG4gICAgICAgIGxldCBjb3VudCA9IG5vZGVzLmxlbmd0aDtcbiAgICAgICAgZm9yIChjb25zdCBub2RlIG9mIG5vZGVzKSB7XG4gICAgICAgICAgICBpZiAobm9kZS5jaGlsZHJlbikge1xuICAgICAgICAgICAgICAgIGNvdW50ICs9IHRoaXMuY291bnROb2Rlcyhub2RlLmNoaWxkcmVuKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY291bnQ7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRFZGl0b3JJbmZvKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIGNvbnN0IGluZm8gPSB7XG4gICAgICAgICAgICBlZGl0b3I6IHtcbiAgICAgICAgICAgICAgICB2ZXJzaW9uOiAoRWRpdG9yIGFzIGFueSkudmVyc2lvbnM/LmVkaXRvciB8fCAnVW5rbm93bicsXG4gICAgICAgICAgICAgICAgY29jb3NWZXJzaW9uOiAoRWRpdG9yIGFzIGFueSkudmVyc2lvbnM/LmNvY29zIHx8ICdVbmtub3duJyxcbiAgICAgICAgICAgICAgICBwbGF0Zm9ybTogcHJvY2Vzcy5wbGF0Zm9ybSxcbiAgICAgICAgICAgICAgICBhcmNoOiBwcm9jZXNzLmFyY2gsXG4gICAgICAgICAgICAgICAgbm9kZVZlcnNpb246IHByb2Nlc3MudmVyc2lvblxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHByb2plY3Q6IHtcbiAgICAgICAgICAgICAgICBuYW1lOiBFZGl0b3IuUHJvamVjdC5uYW1lLFxuICAgICAgICAgICAgICAgIHBhdGg6IEVkaXRvci5Qcm9qZWN0LnBhdGgsXG4gICAgICAgICAgICAgICAgdXVpZDogRWRpdG9yLlByb2plY3QudXVpZFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIG1lbW9yeTogcHJvY2Vzcy5tZW1vcnlVc2FnZSgpLFxuICAgICAgICAgICAgdXB0aW1lOiBwcm9jZXNzLnVwdGltZSgpXG4gICAgICAgIH07XG5cbiAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogdHJ1ZSwgZGF0YTogaW5mbyB9O1xuICAgIH1cbn0iXX0= \ No newline at end of file diff --git a/dist/tools/node-tools.js b/dist/tools/node-tools.js new file mode 100644 index 0000000..eb6344f --- /dev/null +++ b/dist/tools/node-tools.js @@ -0,0 +1,510 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NodeTools = void 0; +class NodeTools { + getTools() { + return [ + { + name: 'create_node', + description: 'Create a new node in the scene. If parentUuid is not provided, the node will be created at the current selection in the editor.', + inputSchema: { + type: 'object', + properties: { + name: { + type: 'string', + description: 'Node name' + }, + parentUuid: { + type: 'string', + description: 'Parent node UUID. If not provided, node will be created at current editor selection. To create at scene root, first get the root node UUID.' + }, + nodeType: { + type: 'string', + description: 'Node type: Node, 2DNode, 3DNode', + enum: ['Node', '2DNode', '3DNode'], + default: 'Node' + }, + siblingIndex: { + type: 'number', + description: 'Sibling index for ordering (-1 means append at end)', + default: -1 + } + }, + required: ['name'] + } + }, + { + name: 'get_node_info', + description: 'Get node information by UUID', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Node UUID' + } + }, + required: ['uuid'] + } + }, + { + name: 'find_nodes', + description: 'Find nodes by name pattern', + inputSchema: { + type: 'object', + properties: { + pattern: { + type: 'string', + description: 'Name pattern to search' + }, + exactMatch: { + type: 'boolean', + description: 'Exact match or partial match', + default: false + } + }, + required: ['pattern'] + } + }, + { + name: 'find_node_by_name', + description: 'Find first node by exact name', + inputSchema: { + type: 'object', + properties: { + name: { + type: 'string', + description: 'Node name to find' + } + }, + required: ['name'] + } + }, + { + name: 'get_all_nodes', + description: 'Get all nodes in the scene with their UUIDs', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'set_node_property', + description: 'Set node property value', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Node UUID' + }, + property: { + type: 'string', + description: 'Property name (e.g., position, rotation, scale, active)' + }, + value: { + description: 'Property value' + } + }, + required: ['uuid', 'property', 'value'] + } + }, + { + name: 'delete_node', + description: 'Delete a node from scene', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Node UUID to delete' + } + }, + required: ['uuid'] + } + }, + { + name: 'move_node', + description: 'Move node to new parent', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID to move' + }, + newParentUuid: { + type: 'string', + description: 'New parent node UUID' + }, + siblingIndex: { + type: 'number', + description: 'Sibling index in new parent', + default: -1 + } + }, + required: ['nodeUuid', 'newParentUuid'] + } + }, + { + name: 'duplicate_node', + description: 'Duplicate a node', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Node UUID to duplicate' + }, + includeChildren: { + type: 'boolean', + description: 'Include children nodes', + default: true + } + }, + required: ['uuid'] + } + } + ]; + } + async execute(toolName, args) { + switch (toolName) { + case 'create_node': + return await this.createNode(args); + case 'get_node_info': + return await this.getNodeInfo(args.uuid); + case 'find_nodes': + return await this.findNodes(args.pattern, args.exactMatch); + case 'find_node_by_name': + return await this.findNodeByName(args.name); + case 'get_all_nodes': + return await this.getAllNodes(); + case 'set_node_property': + return await this.setNodeProperty(args.uuid, args.property, args.value); + case 'delete_node': + return await this.deleteNode(args.uuid); + case 'move_node': + return await this.moveNode(args.nodeUuid, args.newParentUuid, args.siblingIndex); + case 'duplicate_node': + return await this.duplicateNode(args.uuid, args.includeChildren); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + async createNode(args) { + return new Promise(async (resolve) => { + // 如果指定了父节点,先验证父节点是否存在 + if (args.parentUuid) { + try { + const parentNode = await Editor.Message.request('scene', 'query-node', args.parentUuid); + if (!parentNode) { + resolve({ + success: false, + error: `Parent node with UUID '${args.parentUuid}' not found` + }); + return; + } + } + catch (err) { + resolve({ + success: false, + error: `Failed to verify parent node: ${err}` + }); + return; + } + } + const nodeData = { + name: args.name, + type: args.nodeType || 'cc.Node' + }; + // 使用更明确的父节点指定方式 + if (args.parentUuid) { + nodeData.parent = args.parentUuid; + // 尝试先创建节点,然后移动到指定父节点 + Editor.Message.request('scene', 'create-node', nodeData).then((nodeUuid) => { + // 如果创建成功但可能没有在正确的父节点下,尝试移动 + if (args.parentUuid && nodeUuid) { + Editor.Message.request('scene', 'move-node', { + uuid: nodeUuid, + parent: args.parentUuid, + index: args.siblingIndex !== undefined ? args.siblingIndex : -1 + }).then(() => { + resolve({ + success: true, + data: { + uuid: nodeUuid, + name: args.name, + parentUuid: args.parentUuid, + message: `Node '${args.name}' created under specified parent` + } + }); + }).catch(() => { + // 即使移动失败,节点已创建,返回成功但带警告 + resolve({ + success: true, + data: { + uuid: nodeUuid, + name: args.name, + message: `Node '${args.name}' created but may not be under specified parent`, + warning: 'Failed to move node to specified parent' + } + }); + }); + } + else { + resolve({ + success: true, + data: { + uuid: nodeUuid, + name: args.name, + message: `Node '${args.name}' created successfully` + } + }); + } + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + } + else { + // 没有指定父节点,使用默认行为 + Editor.Message.request('scene', 'create-node', nodeData).then((result) => { + resolve({ + success: true, + data: { + uuid: result, + name: args.name, + message: `Node '${args.name}' created at current selection` + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + } + }); + } + async getNodeInfo(uuid) { + return new Promise((resolve) => { + Editor.Message.request('scene', 'query-node', uuid).then((nodeData) => { + var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; + if (!nodeData) { + resolve({ + success: false, + error: 'Node not found or invalid response' + }); + return; + } + // 根据实际返回的数据结构解析节点信息 + const info = { + uuid: ((_a = nodeData.uuid) === null || _a === void 0 ? void 0 : _a.value) || uuid, + name: ((_b = nodeData.name) === null || _b === void 0 ? void 0 : _b.value) || 'Unknown', + active: ((_c = nodeData.active) === null || _c === void 0 ? void 0 : _c.value) !== undefined ? nodeData.active.value : true, + position: ((_d = nodeData.position) === null || _d === void 0 ? void 0 : _d.value) || { x: 0, y: 0, z: 0 }, + rotation: ((_e = nodeData.rotation) === null || _e === void 0 ? void 0 : _e.value) || { x: 0, y: 0, z: 0 }, + scale: ((_f = nodeData.scale) === null || _f === void 0 ? void 0 : _f.value) || { x: 1, y: 1, z: 1 }, + parent: ((_h = (_g = nodeData.parent) === null || _g === void 0 ? void 0 : _g.value) === null || _h === void 0 ? void 0 : _h.uuid) || null, + children: nodeData.children || [], + components: (nodeData.__comps__ || []).map((comp) => ({ + type: comp.__type__ || 'Unknown', + enabled: comp.enabled !== undefined ? comp.enabled : true + })), + layer: ((_j = nodeData.layer) === null || _j === void 0 ? void 0 : _j.value) || 1073741824, + mobility: ((_k = nodeData.mobility) === null || _k === void 0 ? void 0 : _k.value) || 0 + }; + resolve({ success: true, data: info }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async findNodes(pattern, exactMatch = false) { + return new Promise((resolve) => { + Editor.Message.request('scene', 'query-nodes-by-name', { + name: pattern, + exactMatch: exactMatch + }).then((results) => { + const nodes = results.map(node => ({ + uuid: node.uuid, + name: node.name, + path: node.path + })); + resolve({ success: true, data: nodes }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async findNodeByName(name) { + return new Promise((resolve) => { + // 优先尝试使用 Editor API 查询节点树并搜索 + Editor.Message.request('scene', 'query-node-tree').then((tree) => { + const foundNode = this.searchNodeInTree(tree, name); + if (foundNode) { + resolve({ + success: true, + data: { + uuid: foundNode.uuid, + name: foundNode.name, + path: this.getNodePath(foundNode) + } + }); + } + else { + resolve({ success: false, error: `Node '${name}' not found` }); + } + }).catch((err) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'findNodeByName', + args: [name] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + resolve(result); + }).catch((err2) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + searchNodeInTree(node, targetName) { + if (node.name === targetName) { + return node; + } + if (node.children) { + for (const child of node.children) { + const found = this.searchNodeInTree(child, targetName); + if (found) { + return found; + } + } + } + return null; + } + async getAllNodes() { + return new Promise((resolve) => { + // 尝试查询场景节点树 + Editor.Message.request('scene', 'query-node-tree').then((tree) => { + const nodes = []; + const traverseTree = (node) => { + nodes.push({ + uuid: node.uuid, + name: node.name, + type: node.type, + active: node.active, + path: this.getNodePath(node) + }); + if (node.children) { + for (const child of node.children) { + traverseTree(child); + } + } + }; + if (tree && tree.children) { + traverseTree(tree); + } + resolve({ + success: true, + data: { + totalNodes: nodes.length, + nodes: nodes + } + }); + }).catch((err) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getAllNodes', + args: [] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + resolve(result); + }).catch((err2) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + getNodePath(node) { + const path = [node.name]; + let current = node.parent; + while (current && current.name !== 'Canvas') { + path.unshift(current.name); + current = current.parent; + } + return path.join('/'); + } + async setNodeProperty(uuid, property, value) { + return new Promise((resolve) => { + // 尝试直接使用 Editor API 设置节点属性 + Editor.Message.request('scene', 'set-property', { + uuid: uuid, + path: property, + dump: { + value: value + } + }).then(() => { + resolve({ + success: true, + message: `Property '${property}' updated successfully` + }); + }).catch((err) => { + // 如果直接设置失败,尝试使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'setNodeProperty', + args: [uuid, property, value] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + resolve(result); + }).catch((err2) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + async deleteNode(uuid) { + return new Promise((resolve) => { + Editor.Message.request('scene', 'remove-node', { uuid: uuid }).then(() => { + resolve({ + success: true, + message: 'Node deleted successfully' + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async moveNode(nodeUuid, newParentUuid, siblingIndex = -1) { + return new Promise((resolve) => { + Editor.Message.request('scene', 'move-node', { + uuid: nodeUuid, + parent: newParentUuid, + index: siblingIndex + }).then(() => { + resolve({ + success: true, + message: 'Node moved successfully' + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async duplicateNode(uuid, includeChildren = true) { + return new Promise((resolve) => { + Editor.Message.request('scene', 'duplicate-node', uuid).then((result) => { + resolve({ + success: true, + data: { + newUuid: result.uuid, + message: 'Node duplicated successfully' + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } +} +exports.NodeTools = NodeTools; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZS10b29scy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS90b29scy9ub2RlLXRvb2xzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLE1BQWEsU0FBUztJQUNsQixRQUFRO1FBQ0osT0FBTztZQUNIO2dCQUNJLElBQUksRUFBRSxhQUFhO2dCQUNuQixXQUFXLEVBQUUsaUlBQWlJO2dCQUM5SSxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLElBQUksRUFBRTs0QkFDRixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsV0FBVzt5QkFDM0I7d0JBQ0QsVUFBVSxFQUFFOzRCQUNSLElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSw2SUFBNkk7eUJBQzdKO3dCQUNELFFBQVEsRUFBRTs0QkFDTixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsaUNBQWlDOzRCQUM5QyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEVBQUUsUUFBUSxFQUFFLFFBQVEsQ0FBQzs0QkFDbEMsT0FBTyxFQUFFLE1BQU07eUJBQ2xCO3dCQUNELFlBQVksRUFBRTs0QkFDVixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUscURBQXFEOzRCQUNsRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO3lCQUNkO3FCQUNKO29CQUNELFFBQVEsRUFBRSxDQUFDLE1BQU0sQ0FBQztpQkFDckI7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxlQUFlO2dCQUNyQixXQUFXLEVBQUUsOEJBQThCO2dCQUMzQyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLElBQUksRUFBRTs0QkFDRixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsV0FBVzt5QkFDM0I7cUJBQ0o7b0JBQ0QsUUFBUSxFQUFFLENBQUMsTUFBTSxDQUFDO2lCQUNyQjthQUNKO1lB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LCBUb29sRXhlY3V0b3IsIE5vZGVJbmZvIH0gZnJvbSAnLi4vdHlwZXMnO1xuXG5leHBvcnQgY2xhc3MgTm9kZVRvb2xzIGltcGxlbWVudHMgVG9vbEV4ZWN1dG9yIHtcbiAgICBnZXRUb29scygpOiBUb29sRGVmaW5pdGlvbltdIHtcbiAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnY3JlYXRlX25vZGUnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ3JlYXRlIGEgbmV3IG5vZGUgaW4gdGhlIHNjZW5lLiBJZiBwYXJlbnRVdWlkIGlzIG5vdCBwcm92aWRlZCwgdGhlIG5vZGUgd2lsbCBiZSBjcmVhdGVkIGF0IHRoZSBjdXJyZW50IHNlbGVjdGlvbiBpbiB0aGUgZWRpdG9yLicsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ05vZGUgbmFtZSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRVdWlkOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQYXJlbnQgbm9kZSBVVUlELiBJZiBub3QgcHJvdmlkZWQsIG5vZGUgd2lsbCBiZSBjcmVhdGVkIGF0IGN1cnJlbnQgZWRpdG9yIHNlbGVjdGlvbi4gVG8gY3JlYXRlIGF0IHNjZW5lIHJvb3QsIGZpcnN0IGdldCB0aGUgcm9vdCBub2RlIFVVSUQuJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVUeXBlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdOb2RlIHR5cGU6IE5vZGUsIDJETm9kZSwgM0ROb2RlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbnVtOiBbJ05vZGUnLCAnMkROb2RlJywgJzNETm9kZSddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6ICdOb2RlJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNpYmxpbmdJbmRleDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU2libGluZyBpbmRleCBmb3Igb3JkZXJpbmcgKC0xIG1lYW5zIGFwcGVuZCBhdCBlbmQpJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiAtMVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWyduYW1lJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfbm9kZV9pbmZvJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBub2RlIGluZm9ybWF0aW9uIGJ5IFVVSUQnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdOb2RlIFVVSUQnXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3V1aWQnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2ZpbmRfbm9kZXMnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnRmluZCBub2RlcyBieSBuYW1lIHBhdHRlcm4nLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdOYW1lIHBhdHRlcm4gdG8gc2VhcmNoJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGV4YWN0TWF0Y2g6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdFeGFjdCBtYXRjaCBvciBwYXJ0aWFsIG1hdGNoJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiBmYWxzZVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydwYXR0ZXJuJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdmaW5kX25vZGVfYnlfbmFtZScsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdGaW5kIGZpcnN0IG5vZGUgYnkgZXhhY3QgbmFtZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ05vZGUgbmFtZSB0byBmaW5kJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWyduYW1lJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfYWxsX25vZGVzJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBhbGwgbm9kZXMgaW4gdGhlIHNjZW5lIHdpdGggdGhlaXIgVVVJRHMnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7fVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ3NldF9ub2RlX3Byb3BlcnR5JyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1NldCBub2RlIHByb3BlcnR5IHZhbHVlJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXVpZDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnTm9kZSBVVUlEJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHByb3BlcnR5OiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQcm9wZXJ0eSBuYW1lIChlLmcuLCBwb3NpdGlvbiwgcm90YXRpb24sIHNjYWxlLCBhY3RpdmUpJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQcm9wZXJ0eSB2YWx1ZSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsndXVpZCcsICdwcm9wZXJ0eScsICd2YWx1ZSddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZGVsZXRlX25vZGUnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnRGVsZXRlIGEgbm9kZSBmcm9tIHNjZW5lJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXVpZDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnTm9kZSBVVUlEIHRvIGRlbGV0ZSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsndXVpZCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnbW92ZV9ub2RlJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ01vdmUgbm9kZSB0byBuZXcgcGFyZW50JyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgbm9kZVV1aWQ6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ05vZGUgVVVJRCB0byBtb3ZlJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG5ld1BhcmVudFV1aWQ6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ05ldyBwYXJlbnQgbm9kZSBVVUlEJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNpYmxpbmdJbmRleDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU2libGluZyBpbmRleCBpbiBuZXcgcGFyZW50JyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiAtMVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydub2RlVXVpZCcsICduZXdQYXJlbnRVdWlkJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdkdXBsaWNhdGVfbm9kZScsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdEdXBsaWNhdGUgYSBub2RlJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXVpZDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnTm9kZSBVVUlEIHRvIGR1cGxpY2F0ZSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlQ2hpbGRyZW46IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdJbmNsdWRlIGNoaWxkcmVuIG5vZGVzJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiB0cnVlXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3V1aWQnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgXTtcbiAgICB9XG5cbiAgICBhc3luYyBleGVjdXRlKHRvb2xOYW1lOiBzdHJpbmcsIGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHN3aXRjaCAodG9vbE5hbWUpIHtcbiAgICAgICAgICAgIGNhc2UgJ2NyZWF0ZV9ub2RlJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5jcmVhdGVOb2RlKGFyZ3MpO1xuICAgICAgICAgICAgY2FzZSAnZ2V0X25vZGVfaW5mbyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0Tm9kZUluZm8oYXJncy51dWlkKTtcbiAgICAgICAgICAgIGNhc2UgJ2ZpbmRfbm9kZXMnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmZpbmROb2RlcyhhcmdzLnBhdHRlcm4sIGFyZ3MuZXhhY3RNYXRjaCk7XG4gICAgICAgICAgICBjYXNlICdmaW5kX25vZGVfYnlfbmFtZSc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZmluZE5vZGVCeU5hbWUoYXJncy5uYW1lKTtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9hbGxfbm9kZXMnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmdldEFsbE5vZGVzKCk7XG4gICAgICAgICAgICBjYXNlICdzZXRfbm9kZV9wcm9wZXJ0eSc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuc2V0Tm9kZVByb3BlcnR5KGFyZ3MudXVpZCwgYXJncy5wcm9wZXJ0eSwgYXJncy52YWx1ZSk7XG4gICAgICAgICAgICBjYXNlICdkZWxldGVfbm9kZSc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZGVsZXRlTm9kZShhcmdzLnV1aWQpO1xuICAgICAgICAgICAgY2FzZSAnbW92ZV9ub2RlJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5tb3ZlTm9kZShhcmdzLm5vZGVVdWlkLCBhcmdzLm5ld1BhcmVudFV1aWQsIGFyZ3Muc2libGluZ0luZGV4KTtcbiAgICAgICAgICAgIGNhc2UgJ2R1cGxpY2F0ZV9ub2RlJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5kdXBsaWNhdGVOb2RlKGFyZ3MudXVpZCwgYXJncy5pbmNsdWRlQ2hpbGRyZW4pO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFVua25vd24gdG9vbDogJHt0b29sTmFtZX1gKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgY3JlYXRlTm9kZShhcmdzOiBhbnkpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoYXN5bmMgKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIC8vIOWmguaenOaMh+WumuS6hueItuiKgueCue+8jOWFiOmqjOivgeeItuiKgueCueaYr+WQpuWtmOWcqFxuICAgICAgICAgICAgaWYgKGFyZ3MucGFyZW50VXVpZCkge1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBhcmVudE5vZGUgPSBhd2FpdCBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdxdWVyeS1ub2RlJywgYXJncy5wYXJlbnRVdWlkKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFwYXJlbnROb2RlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvcjogYFBhcmVudCBub2RlIHdpdGggVVVJRCAnJHthcmdzLnBhcmVudFV1aWR9JyBub3QgZm91bmRgXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3I6IGBGYWlsZWQgdG8gdmVyaWZ5IHBhcmVudCBub2RlOiAke2Vycn1gXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBub2RlRGF0YTogYW55ID0ge1xuICAgICAgICAgICAgICAgIG5hbWU6IGFyZ3MubmFtZSxcbiAgICAgICAgICAgICAgICB0eXBlOiBhcmdzLm5vZGVUeXBlIHx8ICdjYy5Ob2RlJ1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgLy8g5L2/55So5pu05piO56Gu55qE54i26IqC54K55oyH5a6a5pa55byPXG4gICAgICAgICAgICBpZiAoYXJncy5wYXJlbnRVdWlkKSB7XG4gICAgICAgICAgICAgICAgbm9kZURhdGEucGFyZW50ID0gYXJncy5wYXJlbnRVdWlkO1xuICAgICAgICAgICAgICAgIC8vIOWwneivleWFiOWIm+W7uuiKgueCue+8jOeEtuWQjuenu+WKqOWIsOaMh+WumueItuiKgueCuVxuICAgICAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ2NyZWF0ZS1ub2RlJywgbm9kZURhdGEpLnRoZW4oKG5vZGVVdWlkOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgLy8g5aaC5p6c5Yib5bu65oiQ5Yqf5L2G5Y+v6IO95rKh5pyJ5Zyo5q2j56Gu55qE54i26IqC54K55LiL77yM5bCd6K+V56e75YqoXG4gICAgICAgICAgICAgICAgICAgIGlmIChhcmdzLnBhcmVudFV1aWQgJiYgbm9kZVV1aWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ21vdmUtbm9kZScsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnQ6IGFyZ3MucGFyZW50VXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmRleDogYXJncy5zaWJsaW5nSW5kZXggIT09IHVuZGVmaW5lZCA/IGFyZ3Muc2libGluZ0luZGV4IDogLTFcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IGFyZ3MubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmVudFV1aWQ6IGFyZ3MucGFyZW50VXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBOb2RlICcke2FyZ3MubmFtZX0nIGNyZWF0ZWQgdW5kZXIgc3BlY2lmaWVkIHBhcmVudGBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSkuY2F0Y2goKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIOWNs+S9v+enu+WKqOWksei0pe+8jOiKgueCueW3suWIm+W7uu+8jOi/lOWbnuaIkOWKn+S9huW4puitpuWRilxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IGFyZ3MubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBOb2RlICcke2FyZ3MubmFtZX0nIGNyZWF0ZWQgYnV0IG1heSBub3QgYmUgdW5kZXIgc3BlY2lmaWVkIHBhcmVudGAsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nOiAnRmFpbGVkIHRvIG1vdmUgbm9kZSB0byBzcGVjaWZpZWQgcGFyZW50J1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiBub2RlVXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTogYXJncy5uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgTm9kZSAnJHthcmdzLm5hbWV9JyBjcmVhdGVkIHN1Y2Nlc3NmdWxseWBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyDmsqHmnInmjIflrprniLboioLngrnvvIzkvb/nlKjpu5jorqTooYzkuLpcbiAgICAgICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdjcmVhdGUtbm9kZScsIG5vZGVEYXRhKS50aGVuKChyZXN1bHQ6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXVpZDogcmVzdWx0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IGFyZ3MubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgTm9kZSAnJHthcmdzLm5hbWV9JyBjcmVhdGVkIGF0IGN1cnJlbnQgc2VsZWN0aW9uYFxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXROb2RlSW5mbyh1dWlkOiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ3F1ZXJ5LW5vZGUnLCB1dWlkKS50aGVuKChub2RlRGF0YTogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKCFub2RlRGF0YSkge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3I6ICdOb2RlIG5vdCBmb3VuZCBvciBpbnZhbGlkIHJlc3BvbnNlJ1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAvLyDmoLnmja7lrp7pmYXov5Tlm57nmoTmlbDmja7nu5PmnoTop6PmnpDoioLngrnkv6Hmga9cbiAgICAgICAgICAgICAgICBjb25zdCBpbmZvOiBOb2RlSW5mbyA9IHtcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogbm9kZURhdGEudXVpZD8udmFsdWUgfHwgdXVpZCxcbiAgICAgICAgICAgICAgICAgICAgbmFtZTogbm9kZURhdGEubmFtZT8udmFsdWUgfHwgJ1Vua25vd24nLFxuICAgICAgICAgICAgICAgICAgICBhY3RpdmU6IG5vZGVEYXRhLmFjdGl2ZT8udmFsdWUgIT09IHVuZGVmaW5lZCA/IG5vZGVEYXRhLmFjdGl2ZS52YWx1ZSA6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBub2RlRGF0YS5wb3NpdGlvbj8udmFsdWUgfHwgeyB4OiAwLCB5OiAwLCB6OiAwIH0sXG4gICAgICAgICAgICAgICAgICAgIHJvdGF0aW9uOiBub2RlRGF0YS5yb3RhdGlvbj8udmFsdWUgfHwgeyB4OiAwLCB5OiAwLCB6OiAwIH0sXG4gICAgICAgICAgICAgICAgICAgIHNjYWxlOiBub2RlRGF0YS5zY2FsZT8udmFsdWUgfHwgeyB4OiAxLCB5OiAxLCB6OiAxIH0sXG4gICAgICAgICAgICAgICAgICAgIHBhcmVudDogbm9kZURhdGEucGFyZW50Py52YWx1ZT8udXVpZCB8fCBudWxsLFxuICAgICAgICAgICAgICAgICAgICBjaGlsZHJlbjogbm9kZURhdGEuY2hpbGRyZW4gfHwgW10sXG4gICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudHM6IChub2RlRGF0YS5fX2NvbXBzX18gfHwgW10pLm1hcCgoY29tcDogYW55KSA9PiAoe1xuICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogY29tcC5fX3R5cGVfXyB8fCAnVW5rbm93bicsXG4gICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVkOiBjb21wLmVuYWJsZWQgIT09IHVuZGVmaW5lZCA/IGNvbXAuZW5hYmxlZCA6IHRydWVcbiAgICAgICAgICAgICAgICAgICAgfSkpLFxuICAgICAgICAgICAgICAgICAgICBsYXllcjogbm9kZURhdGEubGF5ZXI/LnZhbHVlIHx8IDEwNzM3NDE4MjQsXG4gICAgICAgICAgICAgICAgICAgIG1vYmlsaXR5OiBub2RlRGF0YS5tb2JpbGl0eT8udmFsdWUgfHwgMFxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IHRydWUsIGRhdGE6IGluZm8gfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZmluZE5vZGVzKHBhdHRlcm46IHN0cmluZywgZXhhY3RNYXRjaDogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdxdWVyeS1ub2Rlcy1ieS1uYW1lJywge1xuICAgICAgICAgICAgICAgIG5hbWU6IHBhdHRlcm4sXG4gICAgICAgICAgICAgICAgZXhhY3RNYXRjaDogZXhhY3RNYXRjaFxuICAgICAgICAgICAgfSkudGhlbigocmVzdWx0czogYW55W10pID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBub2RlcyA9IHJlc3VsdHMubWFwKG5vZGUgPT4gKHtcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogbm9kZS51dWlkLFxuICAgICAgICAgICAgICAgICAgICBuYW1lOiBub2RlLm5hbWUsXG4gICAgICAgICAgICAgICAgICAgIHBhdGg6IG5vZGUucGF0aFxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogdHJ1ZSwgZGF0YTogbm9kZXMgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZmluZE5vZGVCeU5hbWUobmFtZTogc3RyaW5nKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICAvLyDkvJjlhYjlsJ3or5Xkvb/nlKggRWRpdG9yIEFQSSDmn6Xor6LoioLngrnmoJHlubbmkJzntKJcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ3F1ZXJ5LW5vZGUtdHJlZScpLnRoZW4oKHRyZWU6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGZvdW5kTm9kZSA9IHRoaXMuc2VhcmNoTm9kZUluVHJlZSh0cmVlLCBuYW1lKTtcbiAgICAgICAgICAgICAgICBpZiAoZm91bmROb2RlKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiBmb3VuZE5vZGUudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lOiBmb3VuZE5vZGUubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXRoOiB0aGlzLmdldE5vZGVQYXRoKGZvdW5kTm9kZSlcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYE5vZGUgJyR7bmFtZX0nIG5vdCBmb3VuZGAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAvLyDlpIfnlKjmlrnmoYjvvJrkvb/nlKjlnLrmma/ohJrmnKxcbiAgICAgICAgICAgICAgICBjb25zdCBvcHRpb25zID0ge1xuICAgICAgICAgICAgICAgICAgICBuYW1lOiAnY29jb3MtbWNwLXNlcnZlcicsXG4gICAgICAgICAgICAgICAgICAgIG1ldGhvZDogJ2ZpbmROb2RlQnlOYW1lJyxcbiAgICAgICAgICAgICAgICAgICAgYXJnczogW25hbWVdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdleGVjdXRlLXNjZW5lLXNjcmlwdCcsIG9wdGlvbnMpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9KS5jYXRjaCgoZXJyMjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYERpcmVjdCBBUEkgZmFpbGVkOiAke2Vyci5tZXNzYWdlfSwgU2NlbmUgc2NyaXB0IGZhaWxlZDogJHtlcnIyLm1lc3NhZ2V9YCB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNlYXJjaE5vZGVJblRyZWUobm9kZTogYW55LCB0YXJnZXROYW1lOiBzdHJpbmcpOiBhbnkge1xuICAgICAgICBpZiAobm9kZS5uYW1lID09PSB0YXJnZXROYW1lKSB7XG4gICAgICAgICAgICByZXR1cm4gbm9kZTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgaWYgKG5vZGUuY2hpbGRyZW4pIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgY2hpbGQgb2Ygbm9kZS5jaGlsZHJlbikge1xuICAgICAgICAgICAgICAgIGNvbnN0IGZvdW5kID0gdGhpcy5zZWFyY2hOb2RlSW5UcmVlKGNoaWxkLCB0YXJnZXROYW1lKTtcbiAgICAgICAgICAgICAgICBpZiAoZm91bmQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZvdW5kO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRBbGxOb2RlcygpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIC8vIOWwneivleafpeivouWcuuaZr+iKgueCueagkVxuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAncXVlcnktbm9kZS10cmVlJykudGhlbigodHJlZTogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3Qgbm9kZXM6IGFueVtdID0gW107XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgY29uc3QgdHJhdmVyc2VUcmVlID0gKG5vZGU6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBub2Rlcy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHV1aWQ6IG5vZGUudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IG5vZGUubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IG5vZGUudHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2ZTogbm9kZS5hY3RpdmUsXG4gICAgICAgICAgICAgICAgICAgICAgICBwYXRoOiB0aGlzLmdldE5vZGVQYXRoKG5vZGUpXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgaWYgKG5vZGUuY2hpbGRyZW4pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoY29uc3QgY2hpbGQgb2Ygbm9kZS5jaGlsZHJlbikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYXZlcnNlVHJlZShjaGlsZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIGlmICh0cmVlICYmIHRyZWUuY2hpbGRyZW4pIHtcbiAgICAgICAgICAgICAgICAgICAgdHJhdmVyc2VUcmVlKHRyZWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgdG90YWxOb2Rlczogbm9kZXMubGVuZ3RoLFxuICAgICAgICAgICAgICAgICAgICAgICAgbm9kZXM6IG5vZGVzXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8g5aSH55So5pa55qGI77ya5L2/55So5Zy65pmv6ISa5pysXG4gICAgICAgICAgICAgICAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICAgICAgICAgICAgICAgICAgbmFtZTogJ2NvY29zLW1jcC1zZXJ2ZXInLFxuICAgICAgICAgICAgICAgICAgICBtZXRob2Q6ICdnZXRBbGxOb2RlcycsXG4gICAgICAgICAgICAgICAgICAgIGFyZ3M6IFtdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdleGVjdXRlLXNjZW5lLXNjcmlwdCcsIG9wdGlvbnMpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9KS5jYXRjaCgoZXJyMjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYERpcmVjdCBBUEkgZmFpbGVkOiAke2Vyci5tZXNzYWdlfSwgU2NlbmUgc2NyaXB0IGZhaWxlZDogJHtlcnIyLm1lc3NhZ2V9YCB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGdldE5vZGVQYXRoKG5vZGU6IGFueSk6IHN0cmluZyB7XG4gICAgICAgIGNvbnN0IHBhdGggPSBbbm9kZS5uYW1lXTtcbiAgICAgICAgbGV0IGN1cnJlbnQgPSBub2RlLnBhcmVudDtcbiAgICAgICAgd2hpbGUgKGN1cnJlbnQgJiYgY3VycmVudC5uYW1lICE9PSAnQ2FudmFzJykge1xuICAgICAgICAgICAgcGF0aC51bnNoaWZ0KGN1cnJlbnQubmFtZSk7XG4gICAgICAgICAgICBjdXJyZW50ID0gY3VycmVudC5wYXJlbnQ7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHBhdGguam9pbignLycpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgc2V0Tm9kZVByb3BlcnR5KHV1aWQ6IHN0cmluZywgcHJvcGVydHk6IHN0cmluZywgdmFsdWU6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgLy8g5bCd6K+V55u05o6l5L2/55SoIEVkaXRvciBBUEkg6K6+572u6IqC54K55bGe5oCnXG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdzZXQtcHJvcGVydHknLCB7XG4gICAgICAgICAgICAgICAgdXVpZDogdXVpZCxcbiAgICAgICAgICAgICAgICBwYXRoOiBwcm9wZXJ0eSxcbiAgICAgICAgICAgICAgICBkdW1wOiB7XG4gICAgICAgICAgICAgICAgICAgIHZhbHVlOiB2YWx1ZVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgUHJvcGVydHkgJyR7cHJvcGVydHl9JyB1cGRhdGVkIHN1Y2Nlc3NmdWxseWBcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8g5aaC5p6c55u05o6l6K6+572u5aSx6LSl77yM5bCd6K+V5L2/55So5Zy65pmv6ISa5pysXG4gICAgICAgICAgICAgICAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICAgICAgICAgICAgICAgICAgbmFtZTogJ2NvY29zLW1jcC1zZXJ2ZXInLFxuICAgICAgICAgICAgICAgICAgICBtZXRob2Q6ICdzZXROb2RlUHJvcGVydHknLFxuICAgICAgICAgICAgICAgICAgICBhcmdzOiBbdXVpZCwgcHJvcGVydHksIHZhbHVlXVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnZXhlY3V0ZS1zY2VuZS1zY3JpcHQnLCBvcHRpb25zKS50aGVuKChyZXN1bHQ6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgfSkuY2F0Y2goKGVycjI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGBEaXJlY3QgQVBJIGZhaWxlZDogJHtlcnIubWVzc2FnZX0sIFNjZW5lIHNjcmlwdCBmYWlsZWQ6ICR7ZXJyMi5tZXNzYWdlfWAgfSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBkZWxldGVOb2RlKHV1aWQ6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAncmVtb3ZlLW5vZGUnLCB7IHV1aWQ6IHV1aWQgfSkudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdOb2RlIGRlbGV0ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIG1vdmVOb2RlKG5vZGVVdWlkOiBzdHJpbmcsIG5ld1BhcmVudFV1aWQ6IHN0cmluZywgc2libGluZ0luZGV4OiBudW1iZXIgPSAtMSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnbW92ZS1ub2RlJywge1xuICAgICAgICAgICAgICAgIHV1aWQ6IG5vZGVVdWlkLFxuICAgICAgICAgICAgICAgIHBhcmVudDogbmV3UGFyZW50VXVpZCxcbiAgICAgICAgICAgICAgICBpbmRleDogc2libGluZ0luZGV4XG4gICAgICAgICAgICB9KS50aGVuKCgpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ05vZGUgbW92ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGR1cGxpY2F0ZU5vZGUodXVpZDogc3RyaW5nLCBpbmNsdWRlQ2hpbGRyZW46IGJvb2xlYW4gPSB0cnVlKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdkdXBsaWNhdGUtbm9kZScsIHV1aWQpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5ld1V1aWQ6IHJlc3VsdC51dWlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ05vZGUgZHVwbGljYXRlZCBzdWNjZXNzZnVsbHknXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxufSJdfQ== \ No newline at end of file diff --git a/dist/tools/prefab-tools.js b/dist/tools/prefab-tools.js new file mode 100644 index 0000000..0c1b3bc --- /dev/null +++ b/dist/tools/prefab-tools.js @@ -0,0 +1,342 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrefabTools = void 0; +class PrefabTools { + getTools() { + return [ + { + name: 'get_prefab_list', + description: 'Get all prefabs in the project', + inputSchema: { + type: 'object', + properties: { + folder: { + type: 'string', + description: 'Folder path to search (optional)', + default: 'db://assets' + } + } + } + }, + { + name: 'load_prefab', + description: 'Load a prefab by path', + inputSchema: { + type: 'object', + properties: { + prefabPath: { + type: 'string', + description: 'Prefab asset path' + } + }, + required: ['prefabPath'] + } + }, + { + name: 'instantiate_prefab', + description: 'Instantiate a prefab in the scene', + inputSchema: { + type: 'object', + properties: { + prefabPath: { + type: 'string', + description: 'Prefab asset path' + }, + parentUuid: { + type: 'string', + description: 'Parent node UUID (optional)' + }, + position: { + type: 'object', + description: 'Initial position', + properties: { + x: { type: 'number' }, + y: { type: 'number' }, + z: { type: 'number' } + } + } + }, + required: ['prefabPath'] + } + }, + { + name: 'create_prefab', + description: 'Create a prefab from a node', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Source node UUID' + }, + savePath: { + type: 'string', + description: 'Path to save the prefab' + }, + prefabName: { + type: 'string', + description: 'Prefab name' + } + }, + required: ['nodeUuid', 'savePath', 'prefabName'] + } + }, + { + name: 'create_prefab_from_node', + description: 'Create a prefab from a node (alias for create_prefab)', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Source node UUID' + }, + prefabPath: { + type: 'string', + description: 'Path to save the prefab' + } + }, + required: ['nodeUuid', 'prefabPath'] + } + }, + { + name: 'update_prefab', + description: 'Update an existing prefab', + inputSchema: { + type: 'object', + properties: { + prefabPath: { + type: 'string', + description: 'Prefab asset path' + }, + nodeUuid: { + type: 'string', + description: 'Node UUID with changes' + } + }, + required: ['prefabPath', 'nodeUuid'] + } + }, + { + name: 'revert_prefab', + description: 'Revert prefab instance to original', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Prefab instance node UUID' + } + }, + required: ['nodeUuid'] + } + }, + { + name: 'get_prefab_info', + description: 'Get detailed prefab information', + inputSchema: { + type: 'object', + properties: { + prefabPath: { + type: 'string', + description: 'Prefab asset path' + } + }, + required: ['prefabPath'] + } + } + ]; + } + async execute(toolName, args) { + switch (toolName) { + case 'get_prefab_list': + return await this.getPrefabList(args.folder); + case 'load_prefab': + return await this.loadPrefab(args.prefabPath); + case 'instantiate_prefab': + return await this.instantiatePrefab(args); + case 'create_prefab': + return await this.createPrefab(args); + case 'create_prefab_from_node': + return await this.createPrefabFromNode(args); + case 'update_prefab': + return await this.updatePrefab(args.prefabPath, args.nodeUuid); + case 'revert_prefab': + return await this.revertPrefab(args.nodeUuid); + case 'get_prefab_info': + return await this.getPrefabInfo(args.prefabPath); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + async getPrefabList(folder = 'db://assets') { + return new Promise((resolve) => { + const pattern = folder.endsWith('/') ? + `${folder}**/*.prefab` : `${folder}/**/*.prefab`; + Editor.Message.request('asset-db', 'query-assets', { + pattern: pattern + }).then((results) => { + const prefabs = results.map(asset => ({ + name: asset.name, + path: asset.url, + uuid: asset.uuid, + folder: asset.url.substring(0, asset.url.lastIndexOf('/')) + })); + resolve({ success: true, data: prefabs }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async loadPrefab(prefabPath) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo) => { + if (!assetInfo) { + throw new Error('Prefab not found'); + } + return Editor.Message.request('scene', 'load-asset', { + uuid: assetInfo.uuid + }); + }).then((prefabData) => { + resolve({ + success: true, + data: { + uuid: prefabData.uuid, + name: prefabData.name, + message: 'Prefab loaded successfully' + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async instantiatePrefab(args) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', args.prefabPath).then((assetInfo) => { + if (!assetInfo) { + throw new Error('Prefab not found'); + } + const instantiateData = { + prefab: assetInfo.uuid + }; + if (args.parentUuid) { + instantiateData.parent = args.parentUuid; + } + if (args.position) { + instantiateData.position = args.position; + } + return Editor.Message.request('scene', 'instantiate-prefab', instantiateData); + }).then((result) => { + resolve({ + success: true, + data: { + nodeUuid: result.uuid, + name: result.name, + message: 'Prefab instantiated successfully' + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async createPrefab(args) { + return new Promise((resolve) => { + // 支持 prefabPath 和 savePath 两种参数名 + const pathParam = args.prefabPath || args.savePath; + if (!pathParam) { + resolve({ + success: false, + error: 'Missing prefab path parameter. Please provide either prefabPath or savePath.' + }); + return; + } + const fullPath = pathParam.endsWith('.prefab') ? + pathParam : `${pathParam}/${args.prefabName || 'NewPrefab'}.prefab`; + // 预制体创建需要特殊的Editor API支持 + // 目前Cocos Creator 3.8的MCP插件环境下,预制体创建功能受限 + resolve({ + success: false, + error: 'Prefab creation is not supported in the current MCP plugin environment', + instruction: 'Please create prefabs manually by dragging nodes from the scene to the assets folder in the Cocos Creator editor', + data: { + nodeUuid: args.nodeUuid, + requestedPath: fullPath, + suggestion: 'You can manually drag the node from the scene to the assets folder to create a prefab.' + } + }); + }); + } + async updatePrefab(prefabPath, nodeUuid) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo) => { + if (!assetInfo) { + throw new Error('Prefab not found'); + } + return Editor.Message.request('scene', 'apply-prefab', { + node: nodeUuid, + prefab: assetInfo.uuid + }); + }).then(() => { + resolve({ + success: true, + message: 'Prefab updated successfully' + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async revertPrefab(nodeUuid) { + return new Promise((resolve) => { + Editor.Message.request('scene', 'revert-prefab', { + node: nodeUuid + }).then(() => { + resolve({ + success: true, + message: 'Prefab instance reverted successfully' + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async getPrefabInfo(prefabPath) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo) => { + if (!assetInfo) { + throw new Error('Prefab not found'); + } + return Editor.Message.request('asset-db', 'query-asset-meta', assetInfo.uuid); + }).then((metaInfo) => { + const info = { + name: metaInfo.name, + uuid: metaInfo.uuid, + path: prefabPath, + folder: prefabPath.substring(0, prefabPath.lastIndexOf('/')), + createTime: metaInfo.createTime, + modifyTime: metaInfo.modifyTime, + dependencies: metaInfo.depends || [] + }; + resolve({ success: true, data: info }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async createPrefabFromNode(args) { + var _a; + // 从 prefabPath 提取名称 + const prefabPath = args.prefabPath; + const prefabName = ((_a = prefabPath.split('/').pop()) === null || _a === void 0 ? void 0 : _a.replace('.prefab', '')) || 'NewPrefab'; + // 调用原来的 createPrefab 方法 + return await this.createPrefab({ + nodeUuid: args.nodeUuid, + savePath: prefabPath, + prefabName: prefabName + }); + } +} +exports.PrefabTools = PrefabTools; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlZmFiLXRvb2xzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc291cmNlL3Rvb2xzL3ByZWZhYi10b29scy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQSxNQUFhLFdBQVc7SUFDcEIsUUFBUTtRQUNKLE9BQU87WUFDSDtnQkFDSSxJQUFJLEVBQUUsaUJBQWlCO2dCQUN2QixXQUFXLEVBQUUsZ0NBQWdDO2dCQUM3QyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLE1BQU0sRUFBRTs0QkFDSixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsa0NBQWtDOzRCQUMvQyxPQUFPLEVBQUUsYUFBYTt5QkFDekI7cUJBQ0o7aUJBQ0o7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxhQUFhO2dCQUNuQixXQUFXLEVBQUUsdUJBQXVCO2dCQUNwQyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLFVBQVUsRUFBRTs0QkFDUixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsbUJBQW1CO3lCQUNuQztxQkFDSjtvQkFDRCxRQUFRLEVBQUUsQ0FBQyxZQUFZLENBQUM7aUJBQzNCO2FBQ0o7WUFDRDtnQkFDSSxJQUFJLEVBQUUsb0JBQW9CO2dCQUMxQixXQUFXLEVBQUUsbUNBQW1DO2dCQUNoRCxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLFVBQVUsRUFBRTs0QkFDUixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsbUJBQW1CO3lCQUNuQzt3QkFDRCxVQUFVLEVBQUU7NEJBQ1IsSUFBSSxFQUFFLFFBQVE7NEJBQ2QsV0FBVyxFQUFFLDZCQUE2Qjt5QkFDN0M7d0JBQ0QsUUFBUSxFQUFFOzRCQUNOLElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSxrQkFBa0I7NEJB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Q0FBQyxZQUFZLENBQUM7WUFDM0IsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO1lBQ3ZCLFFBQVEsRUFBRSxVQUFVO1lBQ3BCLFVBQVUsRUFBRSxVQUFVO1NBQ3pCLENBQUMsQ0FBQztJQUNQLENBQUM7Q0FDSjtBQXBXRCxrQ0FvV0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBUb29sRGVmaW5pdGlvbiwgVG9vbFJlc3BvbnNlLCBUb29sRXhlY3V0b3IsIFByZWZhYkluZm8gfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBjbGFzcyBQcmVmYWJUb29scyBpbXBsZW1lbnRzIFRvb2xFeGVjdXRvciB7XG4gICAgZ2V0VG9vbHMoKTogVG9vbERlZmluaXRpb25bXSB7XG4gICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2dldF9wcmVmYWJfbGlzdCcsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgYWxsIHByZWZhYnMgaW4gdGhlIHByb2plY3QnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBmb2xkZXI6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0ZvbGRlciBwYXRoIHRvIHNlYXJjaCAob3B0aW9uYWwpJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiAnZGI6Ly9hc3NldHMnXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdsb2FkX3ByZWZhYicsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdMb2FkIGEgcHJlZmFiIGJ5IHBhdGgnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcmVmYWJQYXRoOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQcmVmYWIgYXNzZXQgcGF0aCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsncHJlZmFiUGF0aCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnaW5zdGFudGlhdGVfcHJlZmFiJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0luc3RhbnRpYXRlIGEgcHJlZmFiIGluIHRoZSBzY2VuZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHByZWZhYlBhdGg6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1ByZWZhYiBhc3NldCBwYXRoJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHBhcmVudFV1aWQ6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1BhcmVudCBub2RlIFVVSUQgKG9wdGlvbmFsKSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnSW5pdGlhbCBwb3NpdGlvbicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4OiB7IHR5cGU6ICdudW1iZXInIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk6IHsgdHlwZTogJ251bWJlcicgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgejogeyB0eXBlOiAnbnVtYmVyJyB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydwcmVmYWJQYXRoJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdjcmVhdGVfcHJlZmFiJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0NyZWF0ZSBhIHByZWZhYiBmcm9tIGEgbm9kZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVVdWlkOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdTb3VyY2Ugbm9kZSBVVUlEJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNhdmVQYXRoOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHNhdmUgdGhlIHByZWZhYidcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBwcmVmYWJOYW1lOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQcmVmYWIgbmFtZSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsnbm9kZVV1aWQnLCAnc2F2ZVBhdGgnLCAncHJlZmFiTmFtZSddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnY3JlYXRlX3ByZWZhYl9mcm9tX25vZGUnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ3JlYXRlIGEgcHJlZmFiIGZyb20gYSBub2RlIChhbGlhcyBmb3IgY3JlYXRlX3ByZWZhYiknLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBub2RlVXVpZDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU291cmNlIG5vZGUgVVVJRCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBwcmVmYWJQYXRoOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHNhdmUgdGhlIHByZWZhYidcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsnbm9kZVV1aWQnLCAncHJlZmFiUGF0aCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAndXBkYXRlX3ByZWZhYicsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdVcGRhdGUgYW4gZXhpc3RpbmcgcHJlZmFiJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgcHJlZmFiUGF0aDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUHJlZmFiIGFzc2V0IHBhdGgnXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgbm9kZVV1aWQ6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ05vZGUgVVVJRCB3aXRoIGNoYW5nZXMnXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3ByZWZhYlBhdGgnLCAnbm9kZVV1aWQnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ3JldmVydF9wcmVmYWInLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUmV2ZXJ0IHByZWZhYiBpbnN0YW5jZSB0byBvcmlnaW5hbCcsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVVdWlkOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQcmVmYWIgaW5zdGFuY2Ugbm9kZSBVVUlEJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydub2RlVXVpZCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ2V0X3ByZWZhYl9pbmZvJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBkZXRhaWxlZCBwcmVmYWIgaW5mb3JtYXRpb24nLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcmVmYWJQYXRoOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQcmVmYWIgYXNzZXQgcGF0aCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsncHJlZmFiUGF0aCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICBdO1xuICAgIH1cblxuICAgIGFzeW5jIGV4ZWN1dGUodG9vbE5hbWU6IHN0cmluZywgYXJnczogYW55KTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgc3dpdGNoICh0b29sTmFtZSkge1xuICAgICAgICAgICAgY2FzZSAnZ2V0X3ByZWZhYl9saXN0JzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRQcmVmYWJMaXN0KGFyZ3MuZm9sZGVyKTtcbiAgICAgICAgICAgIGNhc2UgJ2xvYWRfcHJlZmFiJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5sb2FkUHJlZmFiKGFyZ3MucHJlZmFiUGF0aCk7XG4gICAgICAgICAgICBjYXNlICdpbnN0YW50aWF0ZV9wcmVmYWInOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmluc3RhbnRpYXRlUHJlZmFiKGFyZ3MpO1xuICAgICAgICAgICAgY2FzZSAnY3JlYXRlX3ByZWZhYic6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuY3JlYXRlUHJlZmFiKGFyZ3MpO1xuICAgICAgICAgICAgY2FzZSAnY3JlYXRlX3ByZWZhYl9mcm9tX25vZGUnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmNyZWF0ZVByZWZhYkZyb21Ob2RlKGFyZ3MpO1xuICAgICAgICAgICAgY2FzZSAndXBkYXRlX3ByZWZhYic6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMudXBkYXRlUHJlZmFiKGFyZ3MucHJlZmFiUGF0aCwgYXJncy5ub2RlVXVpZCk7XG4gICAgICAgICAgICBjYXNlICdyZXZlcnRfcHJlZmFiJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5yZXZlcnRQcmVmYWIoYXJncy5ub2RlVXVpZCk7XG4gICAgICAgICAgICBjYXNlICdnZXRfcHJlZmFiX2luZm8nOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmdldFByZWZhYkluZm8oYXJncy5wcmVmYWJQYXRoKTtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIHRvb2w6ICR7dG9vbE5hbWV9YCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldFByZWZhYkxpc3QoZm9sZGVyOiBzdHJpbmcgPSAnZGI6Ly9hc3NldHMnKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYXR0ZXJuID0gZm9sZGVyLmVuZHNXaXRoKCcvJykgPyBcbiAgICAgICAgICAgICAgICBgJHtmb2xkZXJ9KiovKi5wcmVmYWJgIDogYCR7Zm9sZGVyfS8qKi8qLnByZWZhYmA7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ3F1ZXJ5LWFzc2V0cycsIHtcbiAgICAgICAgICAgICAgICBwYXR0ZXJuOiBwYXR0ZXJuXG4gICAgICAgICAgICB9KS50aGVuKChyZXN1bHRzOiBhbnlbXSkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IHByZWZhYnM6IFByZWZhYkluZm9bXSA9IHJlc3VsdHMubWFwKGFzc2V0ID0+ICh7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6IGFzc2V0Lm5hbWUsXG4gICAgICAgICAgICAgICAgICAgIHBhdGg6IGFzc2V0LnVybCxcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogYXNzZXQudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgZm9sZGVyOiBhc3NldC51cmwuc3Vic3RyaW5nKDAsIGFzc2V0LnVybC5sYXN0SW5kZXhPZignLycpKVxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogdHJ1ZSwgZGF0YTogcHJlZmFicyB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBsb2FkUHJlZmFiKHByZWZhYlBhdGg6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYXNzZXQtZGInLCAncXVlcnktYXNzZXQtaW5mbycsIHByZWZhYlBhdGgpLnRoZW4oKGFzc2V0SW5mbzogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKCFhc3NldEluZm8pIHtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQcmVmYWIgbm90IGZvdW5kJyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIHJldHVybiBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdsb2FkLWFzc2V0Jywge1xuICAgICAgICAgICAgICAgICAgICB1dWlkOiBhc3NldEluZm8udXVpZFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkudGhlbigocHJlZmFiRGF0YTogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHV1aWQ6IHByZWZhYkRhdGEudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IHByZWZhYkRhdGEubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdQcmVmYWIgbG9hZGVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGluc3RhbnRpYXRlUHJlZmFiKGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYXNzZXQtZGInLCAncXVlcnktYXNzZXQtaW5mbycsIGFyZ3MucHJlZmFiUGF0aCkudGhlbigoYXNzZXRJbmZvOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoIWFzc2V0SW5mbykge1xuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1ByZWZhYiBub3QgZm91bmQnKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBjb25zdCBpbnN0YW50aWF0ZURhdGE6IGFueSA9IHtcbiAgICAgICAgICAgICAgICAgICAgcHJlZmFiOiBhc3NldEluZm8udXVpZFxuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICBpZiAoYXJncy5wYXJlbnRVdWlkKSB7XG4gICAgICAgICAgICAgICAgICAgIGluc3RhbnRpYXRlRGF0YS5wYXJlbnQgPSBhcmdzLnBhcmVudFV1aWQ7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGFyZ3MucG9zaXRpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgaW5zdGFudGlhdGVEYXRhLnBvc2l0aW9uID0gYXJncy5wb3NpdGlvbjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZXR1cm4gRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnaW5zdGFudGlhdGUtcHJlZmFiJywgaW5zdGFudGlhdGVEYXRhKTtcbiAgICAgICAgICAgIH0pLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVVdWlkOiByZXN1bHQudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IHJlc3VsdC5uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ1ByZWZhYiBpbnN0YW50aWF0ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgY3JlYXRlUHJlZmFiKGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgLy8g5pSv5oyBIHByZWZhYlBhdGgg5ZKMIHNhdmVQYXRoIOS4pOenjeWPguaVsOWQjVxuICAgICAgICAgICAgY29uc3QgcGF0aFBhcmFtID0gYXJncy5wcmVmYWJQYXRoIHx8IGFyZ3Muc2F2ZVBhdGg7XG4gICAgICAgICAgICBpZiAoIXBhdGhQYXJhbSkge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgZXJyb3I6ICdNaXNzaW5nIHByZWZhYiBwYXRoIHBhcmFtZXRlci4gUGxlYXNlIHByb3ZpZGUgZWl0aGVyIHByZWZhYlBhdGggb3Igc2F2ZVBhdGguJ1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgZnVsbFBhdGggPSBwYXRoUGFyYW0uZW5kc1dpdGgoJy5wcmVmYWInKSA/IFxuICAgICAgICAgICAgICAgIHBhdGhQYXJhbSA6IGAke3BhdGhQYXJhbX0vJHthcmdzLnByZWZhYk5hbWUgfHwgJ05ld1ByZWZhYid9LnByZWZhYmA7XG5cbiAgICAgICAgICAgIC8vIOmihOWItuS9k+WIm+W7uumcgOimgeeJueauiueahEVkaXRvciBBUEnmlK/mjIFcbiAgICAgICAgICAgIC8vIOebruWJjUNvY29zIENyZWF0b3IgMy4455qETUNQ5o+S5Lu2546v5aKD5LiL77yM6aKE5Yi25L2T5Yib5bu65Yqf6IO95Y+X6ZmQXG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGVycm9yOiAnUHJlZmFiIGNyZWF0aW9uIGlzIG5vdCBzdXBwb3J0ZWQgaW4gdGhlIGN1cnJlbnQgTUNQIHBsdWdpbiBlbnZpcm9ubWVudCcsXG4gICAgICAgICAgICAgICAgaW5zdHJ1Y3Rpb246ICdQbGVhc2UgY3JlYXRlIHByZWZhYnMgbWFudWFsbHkgYnkgZHJhZ2dpbmcgbm9kZXMgZnJvbSB0aGUgc2NlbmUgdG8gdGhlIGFzc2V0cyBmb2xkZXIgaW4gdGhlIENvY29zIENyZWF0b3IgZWRpdG9yJyxcbiAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgIG5vZGVVdWlkOiBhcmdzLm5vZGVVdWlkLFxuICAgICAgICAgICAgICAgICAgICByZXF1ZXN0ZWRQYXRoOiBmdWxsUGF0aCxcbiAgICAgICAgICAgICAgICAgICAgc3VnZ2VzdGlvbjogJ1lvdSBjYW4gbWFudWFsbHkgZHJhZyB0aGUgbm9kZSBmcm9tIHRoZSBzY2VuZSB0byB0aGUgYXNzZXRzIGZvbGRlciB0byBjcmVhdGUgYSBwcmVmYWIuJ1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIHVwZGF0ZVByZWZhYihwcmVmYWJQYXRoOiBzdHJpbmcsIG5vZGVVdWlkOiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ3F1ZXJ5LWFzc2V0LWluZm8nLCBwcmVmYWJQYXRoKS50aGVuKChhc3NldEluZm86IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghYXNzZXRJbmZvKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUHJlZmFiIG5vdCBmb3VuZCcpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJldHVybiBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdhcHBseS1wcmVmYWInLCB7XG4gICAgICAgICAgICAgICAgICAgIG5vZGU6IG5vZGVVdWlkLFxuICAgICAgICAgICAgICAgICAgICBwcmVmYWI6IGFzc2V0SW5mby51dWlkXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KS50aGVuKCgpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ1ByZWZhYiB1cGRhdGVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyByZXZlcnRQcmVmYWIobm9kZVV1aWQ6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAncmV2ZXJ0LXByZWZhYicsIHtcbiAgICAgICAgICAgICAgICBub2RlOiBub2RlVXVpZFxuICAgICAgICAgICAgfSkudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdQcmVmYWIgaW5zdGFuY2UgcmV2ZXJ0ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldFByZWZhYkluZm8ocHJlZmFiUGF0aDogc3RyaW5nKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdhc3NldC1kYicsICdxdWVyeS1hc3NldC1pbmZvJywgcHJlZmFiUGF0aCkudGhlbigoYXNzZXRJbmZvOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoIWFzc2V0SW5mbykge1xuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1ByZWZhYiBub3QgZm91bmQnKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZXR1cm4gRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYXNzZXQtZGInLCAncXVlcnktYXNzZXQtbWV0YScsIGFzc2V0SW5mby51dWlkKTtcbiAgICAgICAgICAgIH0pLnRoZW4oKG1ldGFJbmZvOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBpbmZvOiBQcmVmYWJJbmZvID0ge1xuICAgICAgICAgICAgICAgICAgICBuYW1lOiBtZXRhSW5mby5uYW1lLFxuICAgICAgICAgICAgICAgICAgICB1dWlkOiBtZXRhSW5mby51dWlkLFxuICAgICAgICAgICAgICAgICAgICBwYXRoOiBwcmVmYWJQYXRoLFxuICAgICAgICAgICAgICAgICAgICBmb2xkZXI6IHByZWZhYlBhdGguc3Vic3RyaW5nKDAsIHByZWZhYlBhdGgubGFzdEluZGV4T2YoJy8nKSksXG4gICAgICAgICAgICAgICAgICAgIGNyZWF0ZVRpbWU6IG1ldGFJbmZvLmNyZWF0ZVRpbWUsXG4gICAgICAgICAgICAgICAgICAgIG1vZGlmeVRpbWU6IG1ldGFJbmZvLm1vZGlmeVRpbWUsXG4gICAgICAgICAgICAgICAgICAgIGRlcGVuZGVuY2llczogbWV0YUluZm8uZGVwZW5kcyB8fCBbXVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IHRydWUsIGRhdGE6IGluZm8gfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgY3JlYXRlUHJlZmFiRnJvbU5vZGUoYXJnczogYW55KTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgLy8g5LuOIHByZWZhYlBhdGgg5o+Q5Y+W5ZCN56ewXG4gICAgICAgIGNvbnN0IHByZWZhYlBhdGggPSBhcmdzLnByZWZhYlBhdGg7XG4gICAgICAgIGNvbnN0IHByZWZhYk5hbWUgPSBwcmVmYWJQYXRoLnNwbGl0KCcvJykucG9wKCk/LnJlcGxhY2UoJy5wcmVmYWInLCAnJykgfHwgJ05ld1ByZWZhYic7XG4gICAgICAgIFxuICAgICAgICAvLyDosIPnlKjljp/mnaXnmoQgY3JlYXRlUHJlZmFiIOaWueazlVxuICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5jcmVhdGVQcmVmYWIoe1xuICAgICAgICAgICAgbm9kZVV1aWQ6IGFyZ3Mubm9kZVV1aWQsXG4gICAgICAgICAgICBzYXZlUGF0aDogcHJlZmFiUGF0aCxcbiAgICAgICAgICAgIHByZWZhYk5hbWU6IHByZWZhYk5hbWVcbiAgICAgICAgfSk7XG4gICAgfVxufSJdfQ== \ No newline at end of file diff --git a/dist/tools/preferences-tools.js b/dist/tools/preferences-tools.js new file mode 100644 index 0000000..3b7f003 --- /dev/null +++ b/dist/tools/preferences-tools.js @@ -0,0 +1,159 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PreferencesTools = void 0; +class PreferencesTools { + getTools() { + return [ + { + name: 'get_preferences', + description: 'Get editor preferences', + inputSchema: { + type: 'object', + properties: { + key: { + type: 'string', + description: 'Specific preference key to get (optional)' + } + } + } + }, + { + name: 'set_preferences', + description: 'Set editor preferences', + inputSchema: { + type: 'object', + properties: { + key: { + type: 'string', + description: 'Preference key to set' + }, + value: { + description: 'Preference value to set' + } + }, + required: ['key', 'value'] + } + }, + { + name: 'get_global_preferences', + description: 'Get global editor preferences', + inputSchema: { + type: 'object', + properties: { + key: { + type: 'string', + description: 'Global preference key to get (optional)' + } + } + } + }, + { + name: 'set_global_preferences', + description: 'Set global editor preferences', + inputSchema: { + type: 'object', + properties: { + key: { + type: 'string', + description: 'Global preference key to set' + }, + value: { + description: 'Global preference value to set' + } + }, + required: ['key', 'value'] + } + }, + { + name: 'get_recent_projects', + description: 'Get recently opened projects', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'clear_recent_projects', + description: 'Clear recently opened projects list', + inputSchema: { + type: 'object', + properties: {} + } + } + ]; + } + async execute(toolName, args) { + switch (toolName) { + case 'get_preferences': + return await this.getPreferences(args.key); + case 'set_preferences': + return await this.setPreferences(args.key, args.value); + case 'get_global_preferences': + return await this.getGlobalPreferences(args.key); + case 'set_global_preferences': + return await this.setGlobalPreferences(args.key, args.value); + case 'get_recent_projects': + return await this.getRecentProjects(); + case 'clear_recent_projects': + return await this.clearRecentProjects(); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + async getPreferences(key) { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Preferences API is not supported through MCP', + instruction: 'Please access preferences through the editor menu: Edit > Preferences or use the preferences panel in the editor' + }); + }); + } + async setPreferences(key, value) { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Preferences API is not supported through MCP', + instruction: 'Please modify preferences through the editor menu: Edit > Preferences or use the preferences panel in the editor' + }); + }); + } + async getGlobalPreferences(key) { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Global preferences API is not supported through MCP', + instruction: 'Please access global preferences through the editor menu: Edit > Preferences or use the preferences panel in the editor' + }); + }); + } + async setGlobalPreferences(key, value) { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Global preferences API is not supported through MCP', + instruction: 'Please modify global preferences through the editor menu: Edit > Preferences or use the preferences panel in the editor' + }); + }); + } + async getRecentProjects() { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Recent projects API is not supported through MCP', + instruction: 'Please check recent projects through the editor menu: File > Recent Projects or the start screen' + }); + }); + } + async clearRecentProjects() { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Recent projects API is not supported through MCP', + instruction: 'Please clear recent projects through the editor menu: File > Recent Projects or the start screen' + }); + }); + } +} +exports.PreferencesTools = PreferencesTools; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlZmVyZW5jZXMtdG9vbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvdG9vbHMvcHJlZmVyZW5jZXMtdG9vbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBRUEsTUFBYSxnQkFBZ0I7SUFDekIsUUFBUTtRQUNKLE9BQU87WUFDSDtnQkFDSSxJQUFJLEVBQUUsaUJBQWlCO2dCQUN2QixXQUFXLEVBQUUsd0JBQXdCO2dCQUNyQyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLEdBQUcsRUFBRTs0QkFDRCxJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsMkNBQTJDO3lCQUMzRDtxQkFDSjtpQkFDSjthQUNKO1lBQ0Q7Z0JBQ0ksSUFBSSxFQUFFLGlCQUFpQjtnQkFDdkIsV0FBVyxFQUFFLHdCQUF3QjtnQkFDckMsV0FBVyxFQUFFO29CQUNULElBQUksRUFBRSxRQUFRO29CQUNkLFVBQVUsRUFBRTt3QkFDUixHQUFHLEVBQUU7NEJBQ0QsSUFBSSxFQUFFLFFBQVE7NEJBQ2QsV0FBVyxFQUFFLHVCQUF1Qjt5QkFDdkM7d0JBQ0QsS0FBSyxFQUFFOzRCQUNILFdBQVcsRUFBRSx5QkFBeUI7eUJBQ3pDO3FCQUNKO29CQUNELFFBQVEsRUFBRSxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUM7aUJBQzdCO2FBQ0o7WUFDRDtnQkFDSSxJQUFJLEVBQUUsd0JBQXdCO2dCQUM5QixXQUFXLEVBQUUsK0JBQStCO2dCQUM1QyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLEdBQUcsRUFBRTs0QkFDRCxJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUseUNBQXlDO3lCQUN6RDtxQkFDSjtpQkFDSjthQUNKO1lBQ0Q7Z0JBQ0ksSUFBSSxFQUFFLHdCQUF3QjtnQkFDOUIsV0FBVyxFQUFFLCtCQUErQjtnQkFDNUMsV0FBVyxF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LE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLE9BQU8sQ0FBQztnQkFDSixPQUFPLEVBQUUsS0FBSztnQkFDZCxLQUFLLEVBQUUscURBQXFEO2dCQUM1RCxXQUFXLEVBQUUseUhBQXlIO2FBQ3pJLENBQUMsQ0FBQztRQUNQLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVPLEtBQUssQ0FBQyxpQkFBaUI7UUFDM0IsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLE9BQU8sQ0FBQztnQkFDSixPQUFPLEVBQUUsS0FBSztnQkFDZCxLQUFLLEVBQUUsa0RBQWtEO2dCQUN6RCxXQUFXLEVBQUUsa0dBQWtHO2FBQ2xILENBQUMsQ0FBQztRQUNQLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVPLEtBQUssQ0FBQyxtQkFBbUI7UUFDN0IsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLE9BQU8sQ0FBQztnQkFDSixPQUFPLEVBQUUsS0FBSztnQkFDZCxLQUFLLEVBQUUsa0RBQWtEO2dCQUN6RCxXQUFXLEVBQUUsa0dBQWtHO2FBQ2xILENBQUMsQ0FBQztRQUNQLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztDQUNKO0FBaEtELDRDQWdLQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFRvb2xEZWZpbml0aW9uLCBUb29sUmVzcG9uc2UsIFRvb2xFeGVjdXRvciB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGNsYXNzIFByZWZlcmVuY2VzVG9vbHMgaW1wbGVtZW50cyBUb29sRXhlY3V0b3Ige1xuICAgIGdldFRvb2xzKCk6IFRvb2xEZWZpbml0aW9uW10ge1xuICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfcHJlZmVyZW5jZXMnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnR2V0IGVkaXRvciBwcmVmZXJlbmNlcycsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGtleToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU3BlY2lmaWMgcHJlZmVyZW5jZSBrZXkgdG8gZ2V0IChvcHRpb25hbCknXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdzZXRfcHJlZmVyZW5jZXMnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IGVkaXRvciBwcmVmZXJlbmNlcycsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGtleToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUHJlZmVyZW5jZSBrZXkgdG8gc2V0J1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdQcmVmZXJlbmNlIHZhbHVlIHRvIHNldCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsna2V5JywgJ3ZhbHVlJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfZ2xvYmFsX3ByZWZlcmVuY2VzJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBnbG9iYWwgZWRpdG9yIHByZWZlcmVuY2VzJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAga2V5OiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHbG9iYWwgcHJlZmVyZW5jZSBrZXkgdG8gZ2V0IChvcHRpb25hbCknXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdzZXRfZ2xvYmFsX3ByZWZlcmVuY2VzJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1NldCBnbG9iYWwgZWRpdG9yIHByZWZlcmVuY2VzJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAga2V5OiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHbG9iYWwgcHJlZmVyZW5jZSBrZXkgdG8gc2V0J1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHbG9iYWwgcHJlZmVyZW5jZSB2YWx1ZSB0byBzZXQnXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ2tleScsICd2YWx1ZSddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ2V0X3JlY2VudF9wcm9qZWN0cycsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgcmVjZW50bHkgb3BlbmVkIHByb2plY3RzJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdjbGVhcl9yZWNlbnRfcHJvamVjdHMnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ2xlYXIgcmVjZW50bHkgb3BlbmVkIHByb2plY3RzIGxpc3QnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7fVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgXTtcbiAgICB9XG5cbiAgICBhc3luYyBleGVjdXRlKHRvb2xOYW1lOiBzdHJpbmcsIGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHN3aXRjaCAodG9vbE5hbWUpIHtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9wcmVmZXJlbmNlcyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0UHJlZmVyZW5jZXMoYXJncy5rZXkpO1xuICAgICAgICAgICAgY2FzZSAnc2V0X3ByZWZlcmVuY2VzJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5zZXRQcmVmZXJlbmNlcyhhcmdzLmtleSwgYXJncy52YWx1ZSk7XG4gICAgICAgICAgICBjYXNlICdnZXRfZ2xvYmFsX3ByZWZlcmVuY2VzJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRHbG9iYWxQcmVmZXJlbmNlcyhhcmdzLmtleSk7XG4gICAgICAgICAgICBjYXNlICdzZXRfZ2xvYmFsX3ByZWZlcmVuY2VzJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5zZXRHbG9iYWxQcmVmZXJlbmNlcyhhcmdzLmtleSwgYXJncy52YWx1ZSk7XG4gICAgICAgICAgICBjYXNlICdnZXRfcmVjZW50X3Byb2plY3RzJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRSZWNlbnRQcm9qZWN0cygpO1xuICAgICAgICAgICAgY2FzZSAnY2xlYXJfcmVjZW50X3Byb2plY3RzJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5jbGVhclJlY2VudFByb2plY3RzKCk7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biB0b29sOiAke3Rvb2xOYW1lfWApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRQcmVmZXJlbmNlcyhrZXk/OiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGVycm9yOiAnUHJlZmVyZW5jZXMgQVBJIGlzIG5vdCBzdXBwb3J0ZWQgdGhyb3VnaCBNQ1AnLFxuICAgICAgICAgICAgICAgIGluc3RydWN0aW9uOiAnUGxlYXNlIGFjY2VzcyBwcmVmZXJlbmNlcyB0aHJvdWdoIHRoZSBlZGl0b3IgbWVudTogRWRpdCA+IFByZWZlcmVuY2VzIG9yIHVzZSB0aGUgcHJlZmVyZW5jZXMgcGFuZWwgaW4gdGhlIGVkaXRvcidcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIHNldFByZWZlcmVuY2VzKGtleTogc3RyaW5nLCB2YWx1ZTogYW55KTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBlcnJvcjogJ1ByZWZlcmVuY2VzIEFQSSBpcyBub3Qgc3VwcG9ydGVkIHRocm91Z2ggTUNQJyxcbiAgICAgICAgICAgICAgICBpbnN0cnVjdGlvbjogJ1BsZWFzZSBtb2RpZnkgcHJlZmVyZW5jZXMgdGhyb3VnaCB0aGUgZWRpdG9yIG1lbnU6IEVkaXQgPiBQcmVmZXJlbmNlcyBvciB1c2UgdGhlIHByZWZlcmVuY2VzIHBhbmVsIGluIHRoZSBlZGl0b3InXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRHbG9iYWxQcmVmZXJlbmNlcyhrZXk/OiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGVycm9yOiAnR2xvYmFsIHByZWZlcmVuY2VzIEFQSSBpcyBub3Qgc3VwcG9ydGVkIHRocm91Z2ggTUNQJyxcbiAgICAgICAgICAgICAgICBpbnN0cnVjdGlvbjogJ1BsZWFzZSBhY2Nlc3MgZ2xvYmFsIHByZWZlcmVuY2VzIHRocm91Z2ggdGhlIGVkaXRvciBtZW51OiBFZGl0ID4gUHJlZmVyZW5jZXMgb3IgdXNlIHRoZSBwcmVmZXJlbmNlcyBwYW5lbCBpbiB0aGUgZWRpdG9yJ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgc2V0R2xvYmFsUHJlZmVyZW5jZXMoa2V5OiBzdHJpbmcsIHZhbHVlOiBhbnkpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGVycm9yOiAnR2xvYmFsIHByZWZlcmVuY2VzIEFQSSBpcyBub3Qgc3VwcG9ydGVkIHRocm91Z2ggTUNQJyxcbiAgICAgICAgICAgICAgICBpbnN0cnVjdGlvbjogJ1BsZWFzZSBtb2RpZnkgZ2xvYmFsIHByZWZlcmVuY2VzIHRocm91Z2ggdGhlIGVkaXRvciBtZW51OiBFZGl0ID4gUHJlZmVyZW5jZXMgb3IgdXNlIHRoZSBwcmVmZXJlbmNlcyBwYW5lbCBpbiB0aGUgZWRpdG9yJ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0UmVjZW50UHJvamVjdHMoKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBlcnJvcjogJ1JlY2VudCBwcm9qZWN0cyBBUEkgaXMgbm90IHN1cHBvcnRlZCB0aHJvdWdoIE1DUCcsXG4gICAgICAgICAgICAgICAgaW5zdHJ1Y3Rpb246ICdQbGVhc2UgY2hlY2sgcmVjZW50IHByb2plY3RzIHRocm91Z2ggdGhlIGVkaXRvciBtZW51OiBGaWxlID4gUmVjZW50IFByb2plY3RzIG9yIHRoZSBzdGFydCBzY3JlZW4nXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBjbGVhclJlY2VudFByb2plY3RzKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgICAgZXJyb3I6ICdSZWNlbnQgcHJvamVjdHMgQVBJIGlzIG5vdCBzdXBwb3J0ZWQgdGhyb3VnaCBNQ1AnLFxuICAgICAgICAgICAgICAgIGluc3RydWN0aW9uOiAnUGxlYXNlIGNsZWFyIHJlY2VudCBwcm9qZWN0cyB0aHJvdWdoIHRoZSBlZGl0b3IgbWVudTogRmlsZSA+IFJlY2VudCBQcm9qZWN0cyBvciB0aGUgc3RhcnQgc2NyZWVuJ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cbn0iXX0= \ No newline at end of file diff --git a/dist/tools/project-tools.js b/dist/tools/project-tools.js new file mode 100644 index 0000000..5f673b5 --- /dev/null +++ b/dist/tools/project-tools.js @@ -0,0 +1,893 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ProjectTools = void 0; +const fs = __importStar(require("fs")); +const path = __importStar(require("path")); +class ProjectTools { + getTools() { + return [ + { + name: 'run_project', + description: 'Run the project in preview mode', + inputSchema: { + type: 'object', + properties: { + platform: { + type: 'string', + description: 'Target platform', + enum: ['browser', 'simulator', 'preview'], + default: 'browser' + } + } + } + }, + { + name: 'build_project', + description: 'Build the project', + inputSchema: { + type: 'object', + properties: { + platform: { + type: 'string', + description: 'Build platform', + enum: ['web-mobile', 'web-desktop', 'ios', 'android', 'windows', 'mac'] + }, + debug: { + type: 'boolean', + description: 'Debug build', + default: true + } + }, + required: ['platform'] + } + }, + { + name: 'get_project_info', + description: 'Get project information', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_project_settings', + description: 'Get project settings', + inputSchema: { + type: 'object', + properties: { + category: { + type: 'string', + description: 'Settings category', + enum: ['general', 'physics', 'render', 'assets'], + default: 'general' + } + } + } + }, + { + name: 'refresh_assets', + description: 'Refresh asset database', + inputSchema: { + type: 'object', + properties: { + folder: { + type: 'string', + description: 'Specific folder to refresh (optional)' + } + } + } + }, + { + name: 'import_asset', + description: 'Import an asset file', + inputSchema: { + type: 'object', + properties: { + sourcePath: { + type: 'string', + description: 'Source file path' + }, + targetFolder: { + type: 'string', + description: 'Target folder in assets' + } + }, + required: ['sourcePath', 'targetFolder'] + } + }, + { + name: 'get_asset_info', + description: 'Get asset information', + inputSchema: { + type: 'object', + properties: { + assetPath: { + type: 'string', + description: 'Asset path (db://assets/...)' + } + }, + required: ['assetPath'] + } + }, + { + name: 'get_assets', + description: 'Get assets by type', + inputSchema: { + type: 'object', + properties: { + type: { + type: 'string', + description: 'Asset type filter', + enum: ['all', 'scene', 'prefab', 'script', 'texture', 'material', 'mesh', 'audio', 'animation'], + default: 'all' + }, + folder: { + type: 'string', + description: 'Folder to search in', + default: 'db://assets' + } + } + } + }, + { + name: 'get_build_settings', + description: 'Get build settings - shows current limitations', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'open_build_panel', + description: 'Open the build panel in the editor', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'check_builder_status', + description: 'Check if builder worker is ready', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'start_preview_server', + description: 'Start preview server', + inputSchema: { + type: 'object', + properties: { + port: { + type: 'number', + description: 'Preview server port', + default: 7456 + } + } + } + }, + { + name: 'stop_preview_server', + description: 'Stop preview server', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'create_asset', + description: 'Create a new asset file or folder', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL (e.g., db://assets/newfile.json)' + }, + content: { + type: 'string', + description: 'File content (null for folder)', + default: null + }, + overwrite: { + type: 'boolean', + description: 'Overwrite existing file', + default: false + } + }, + required: ['url'] + } + }, + { + name: 'copy_asset', + description: 'Copy an asset to another location', + inputSchema: { + type: 'object', + properties: { + source: { + type: 'string', + description: 'Source asset URL' + }, + target: { + type: 'string', + description: 'Target location URL' + }, + overwrite: { + type: 'boolean', + description: 'Overwrite existing file', + default: false + } + }, + required: ['source', 'target'] + } + }, + { + name: 'move_asset', + description: 'Move an asset to another location', + inputSchema: { + type: 'object', + properties: { + source: { + type: 'string', + description: 'Source asset URL' + }, + target: { + type: 'string', + description: 'Target location URL' + }, + overwrite: { + type: 'boolean', + description: 'Overwrite existing file', + default: false + } + }, + required: ['source', 'target'] + } + }, + { + name: 'delete_asset', + description: 'Delete an asset', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL to delete' + } + }, + required: ['url'] + } + }, + { + name: 'save_asset', + description: 'Save asset content', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL' + }, + content: { + type: 'string', + description: 'Asset content' + } + }, + required: ['url', 'content'] + } + }, + { + name: 'reimport_asset', + description: 'Reimport an asset', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL to reimport' + } + }, + required: ['url'] + } + }, + { + name: 'query_asset_path', + description: 'Get asset disk path', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL' + } + }, + required: ['url'] + } + }, + { + name: 'query_asset_uuid', + description: 'Get asset UUID from URL', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL' + } + }, + required: ['url'] + } + }, + { + name: 'query_asset_url', + description: 'Get asset URL from UUID', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Asset UUID' + } + }, + required: ['uuid'] + } + } + ]; + } + async execute(toolName, args) { + switch (toolName) { + case 'run_project': + return await this.runProject(args.platform); + case 'build_project': + return await this.buildProject(args); + case 'get_project_info': + return await this.getProjectInfo(); + case 'get_project_settings': + return await this.getProjectSettings(args.category); + case 'refresh_assets': + return await this.refreshAssets(args.folder); + case 'import_asset': + return await this.importAsset(args.sourcePath, args.targetFolder); + case 'get_asset_info': + return await this.getAssetInfo(args.assetPath); + case 'get_assets': + return await this.getAssets(args.type, args.folder); + case 'get_build_settings': + return await this.getBuildSettings(); + case 'open_build_panel': + return await this.openBuildPanel(); + case 'check_builder_status': + return await this.checkBuilderStatus(); + case 'start_preview_server': + return await this.startPreviewServer(args.port); + case 'stop_preview_server': + return await this.stopPreviewServer(); + case 'create_asset': + return await this.createAsset(args.url, args.content, args.overwrite); + case 'copy_asset': + return await this.copyAsset(args.source, args.target, args.overwrite); + case 'move_asset': + return await this.moveAsset(args.source, args.target, args.overwrite); + case 'delete_asset': + return await this.deleteAsset(args.url); + case 'save_asset': + return await this.saveAsset(args.url, args.content); + case 'reimport_asset': + return await this.reimportAsset(args.url); + case 'query_asset_path': + return await this.queryAssetPath(args.url); + case 'query_asset_uuid': + return await this.queryAssetUuid(args.url); + case 'query_asset_url': + return await this.queryAssetUrl(args.uuid); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + async runProject(platform = 'browser') { + return new Promise((resolve) => { + const previewConfig = { + platform: platform, + scenes: [] // Will use current scene + }; + Editor.Message.request('preview', 'start', previewConfig).then(() => { + resolve({ + success: true, + message: `Project is running in ${platform} mode` + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async buildProject(args) { + return new Promise((resolve) => { + const buildOptions = { + platform: args.platform, + debug: args.debug !== false, + sourceMaps: args.debug !== false, + buildPath: `build/${args.platform}` + }; + Editor.Message.request('builder', 'build', buildOptions).then(() => { + resolve({ + success: true, + message: `Project built for ${args.platform}`, + data: { buildPath: buildOptions.buildPath } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async getProjectInfo() { + return new Promise((resolve) => { + var _a; + const info = { + name: Editor.Project.name, + path: Editor.Project.path, + uuid: Editor.Project.uuid, + version: Editor.Project.version || '1.0.0', + cocosVersion: ((_a = Editor.versions) === null || _a === void 0 ? void 0 : _a.cocos) || 'Unknown' + }; + Editor.Message.request('project', 'query-info').then((additionalInfo) => { + Object.assign(info, additionalInfo); + resolve({ success: true, data: info }); + }).catch(() => { + // Return basic info even if detailed query fails + resolve({ success: true, data: info }); + }); + }); + } + async getProjectSettings(category = 'general') { + return new Promise((resolve) => { + // 使用正确的 project API 查询项目配置 + const configMap = { + general: 'project', + physics: 'physics', + render: 'render', + assets: 'asset-db' + }; + const configName = configMap[category] || 'project'; + Editor.Message.request('project', 'query-config', configName).then((settings) => { + resolve({ + success: true, + data: { + category: category, + config: settings, + message: `${category} settings retrieved successfully` + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async refreshAssets(folder) { + return new Promise((resolve) => { + // 使用正确的 asset-db API 刷新资源 + const targetPath = folder || 'db://assets'; + Editor.Message.request('asset-db', 'refresh-asset', targetPath).then(() => { + resolve({ + success: true, + message: `Assets refreshed in: ${targetPath}` + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async importAsset(sourcePath, targetFolder) { + return new Promise((resolve) => { + if (!fs.existsSync(sourcePath)) { + resolve({ success: false, error: 'Source file not found' }); + return; + } + const fileName = path.basename(sourcePath); + const targetPath = targetFolder.startsWith('db://') ? + targetFolder : `db://assets/${targetFolder}`; + Editor.Message.request('asset-db', 'import-asset', sourcePath, `${targetPath}/${fileName}`).then((result) => { + resolve({ + success: true, + data: { + uuid: result.uuid, + path: result.url, + message: `Asset imported: ${fileName}` + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async getAssetInfo(assetPath) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', assetPath).then((assetInfo) => { + if (!assetInfo) { + throw new Error('Asset not found'); + } + const info = { + name: assetInfo.name, + uuid: assetInfo.uuid, + path: assetInfo.url, + type: assetInfo.type, + size: assetInfo.size, + isDirectory: assetInfo.isDirectory + }; + if (assetInfo.meta) { + info.meta = { + ver: assetInfo.meta.ver, + importer: assetInfo.meta.importer + }; + } + resolve({ success: true, data: info }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async getAssets(type = 'all', folder = 'db://assets') { + return new Promise((resolve) => { + let pattern = `${folder}/**/*`; + // 添加类型过滤 + if (type !== 'all') { + const typeExtensions = { + 'scene': '.scene', + 'prefab': '.prefab', + 'script': '.{ts,js}', + 'texture': '.{png,jpg,jpeg,gif,tga,bmp,psd}', + 'material': '.mtl', + 'mesh': '.{fbx,obj,dae}', + 'audio': '.{mp3,ogg,wav,m4a}', + 'animation': '.{anim,clip}' + }; + const extension = typeExtensions[type]; + if (extension) { + pattern = `${folder}/**/*${extension}`; + } + } + Editor.Message.request('asset-db', 'query-assets', { + pattern: pattern + }).then((results) => { + const assets = results.map(asset => ({ + name: asset.name, + uuid: asset.uuid, + path: asset.url, + type: asset.type, + size: asset.size || 0, + isDirectory: asset.isDirectory || false + })); + resolve({ + success: true, + data: { + type: type, + folder: folder, + count: assets.length, + assets: assets + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async getBuildSettings() { + return new Promise((resolve) => { + // 检查构建器是否准备就绪 + Editor.Message.request('builder', 'query-worker-ready').then((ready) => { + resolve({ + success: true, + data: { + builderReady: ready, + message: 'Build settings are limited in MCP plugin environment', + availableActions: [ + 'Open build panel with open_build_panel', + 'Check builder status with check_builder_status', + 'Start preview server with start_preview_server', + 'Stop preview server with stop_preview_server' + ], + limitation: 'Full build configuration requires direct Editor UI access' + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async openBuildPanel() { + return new Promise((resolve) => { + Editor.Message.request('builder', 'open').then(() => { + resolve({ + success: true, + message: 'Build panel opened successfully' + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async checkBuilderStatus() { + return new Promise((resolve) => { + Editor.Message.request('builder', 'query-worker-ready').then((ready) => { + resolve({ + success: true, + data: { + ready: ready, + status: ready ? 'Builder worker is ready' : 'Builder worker is not ready', + message: 'Builder status checked successfully' + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async startPreviewServer(port = 7456) { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Preview server control is not supported through MCP API', + instruction: 'Please start the preview server manually using the editor menu: Project > Preview, or use the preview panel in the editor' + }); + }); + } + async stopPreviewServer() { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Preview server control is not supported through MCP API', + instruction: 'Please stop the preview server manually using the preview panel in the editor' + }); + }); + } + async createAsset(url, content = null, overwrite = false) { + return new Promise((resolve) => { + const options = { + overwrite: overwrite, + rename: !overwrite + }; + Editor.Message.request('asset-db', 'create-asset', url, content, options).then((result) => { + if (result && result.uuid) { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + message: content === null ? 'Folder created successfully' : 'File created successfully' + } + }); + } + else { + resolve({ + success: true, + data: { + url: url, + message: content === null ? 'Folder created successfully' : 'File created successfully' + } + }); + } + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async copyAsset(source, target, overwrite = false) { + return new Promise((resolve) => { + const options = { + overwrite: overwrite, + rename: !overwrite + }; + Editor.Message.request('asset-db', 'copy-asset', source, target, options).then((result) => { + if (result && result.uuid) { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + message: 'Asset copied successfully' + } + }); + } + else { + resolve({ + success: true, + data: { + source: source, + target: target, + message: 'Asset copied successfully' + } + }); + } + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async moveAsset(source, target, overwrite = false) { + return new Promise((resolve) => { + const options = { + overwrite: overwrite, + rename: !overwrite + }; + Editor.Message.request('asset-db', 'move-asset', source, target, options).then((result) => { + if (result && result.uuid) { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + message: 'Asset moved successfully' + } + }); + } + else { + resolve({ + success: true, + data: { + source: source, + target: target, + message: 'Asset moved successfully' + } + }); + } + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async deleteAsset(url) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'delete-asset', url).then((result) => { + resolve({ + success: true, + data: { + url: url, + message: 'Asset deleted successfully' + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async saveAsset(url, content) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'save-asset', url, content).then((result) => { + if (result && result.uuid) { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + message: 'Asset saved successfully' + } + }); + } + else { + resolve({ + success: true, + data: { + url: url, + message: 'Asset saved successfully' + } + }); + } + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async reimportAsset(url) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'reimport-asset', url).then(() => { + resolve({ + success: true, + data: { + url: url, + message: 'Asset reimported successfully' + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async queryAssetPath(url) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-path', url).then((path) => { + if (path) { + resolve({ + success: true, + data: { + url: url, + path: path, + message: 'Asset path retrieved successfully' + } + }); + } + else { + resolve({ success: false, error: 'Asset path not found' }); + } + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async queryAssetUuid(url) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-uuid', url).then((uuid) => { + if (uuid) { + resolve({ + success: true, + data: { + url: url, + uuid: uuid, + message: 'Asset UUID retrieved successfully' + } + }); + } + else { + resolve({ success: false, error: 'Asset UUID not found' }); + } + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async queryAssetUrl(uuid) { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-url', uuid).then((url) => { + if (url) { + resolve({ + success: true, + data: { + uuid: uuid, + url: url, + message: 'Asset URL retrieved successfully' + } + }); + } + else { + resolve({ success: false, error: 'Asset URL not found' }); + } + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } +} +exports.ProjectTools = ProjectTools; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvamVjdC10b29scy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS90b29scy9wcm9qZWN0LXRvb2xzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUNBLHVDQUF5QjtBQUN6QiwyQ0FBNkI7QUFFN0IsTUFBYSxZQUFZO0lBQ3JCLFFBQVE7UUFDSixPQUFPO1lBQ0g7Z0JBQ0ksSUFBSSxFQUFFLGFBQWE7Z0JBQ25CLFdBQVcsRUFBRSxpQ0FBaUM7Z0JBQzlDLFdBQVcsRUFBRTtvQkFDVCxJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUU7d0JBQ1IsUUFBUSxFQUFFOzRCQUNOLElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSxpQkFBaUI7NEJBQzlCLElBQUksRUFBRSxDQUFDLFNBQVMsRUFBRSxXQUFXLEVBQUUsU0FBUyxDQUFDOzRCQUN6QyxPQUFPLEVBQUUsU0FBUzt5QkFDckI7cUJBQ0o7aUJBQ0o7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxlQUFlO2dCQUNyQixXQUFXLEVBQUUsbUJBQW1CO2dCQUNoQyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLFFBQVEsRUFBRTs0QkFDTixJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsZ0JBQWdCOzRCQUM3QixJQUFJLEVBQUUsQ0FBQyxZQUFZLEVBQUUsYUFBYSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLEtBQUssQ0FBQzt5QkFDMUU7d0JBQ0QsS0FBSyxFQUFFOzRCQUNILElBQUksRUFBRSxTQUFTOzRCQUNmLFdBQVcsRUFBRSxhQUFhOzRCQUMxQixPQUFPLEVBQUUsSUFBSTt5QkFDaEI7cUJBQ0o7b0JBQ0QsUUFBUSxFQUFFLENBQUMsVUFBVSxDQUFDO2lCQUN6QjthQUNKO1lBQ0Q7Z0JBQ0ksSUFBSSxFQUFFLGtC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QixNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQWtCLEVBQUUsRUFBRTtnQkFDOUUsSUFBSSxHQUFHLEVBQUUsQ0FBQztvQkFDTixPQUFPLENBQUM7d0JBQ0osT0FBTyxFQUFFLElBQUk7d0JBQ2IsSUFBSSxFQUFFOzRCQUNGLElBQUksRUFBRSxJQUFJOzRCQUNWLEdBQUcsRUFBRSxHQUFHOzRCQUNSLE9BQU8sRUFBRSxrQ0FBa0M7eUJBQzlDO3FCQUNKLENBQUMsQ0FBQztnQkFDUCxDQUFDO3FCQUFNLENBQUM7b0JBQ0osT0FBTyxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUscUJBQXFCLEVBQUUsQ0FBQyxDQUFDO2dCQUM5RCxDQUFDO1lBQ0wsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBVSxFQUFFLEVBQUU7Z0JBQ3BCLE9BQU8sQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3BELENBQUMsQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0NBQ0o7QUFyM0JELG9DQXEzQkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBUb29sRGVmaW5pdGlvbiwgVG9vbFJlc3BvbnNlLCBUb29sRXhlY3V0b3IsIFByb2plY3RJbmZvLCBBc3NldEluZm8gfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuXG5leHBvcnQgY2xhc3MgUHJvamVjdFRvb2xzIGltcGxlbWVudHMgVG9vbEV4ZWN1dG9yIHtcbiAgICBnZXRUb29scygpOiBUb29sRGVmaW5pdGlvbltdIHtcbiAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAncnVuX3Byb2plY3QnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUnVuIHRoZSBwcm9qZWN0IGluIHByZXZpZXcgbW9kZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHBsYXRmb3JtOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdUYXJnZXQgcGxhdGZvcm0nLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVudW06IFsnYnJvd3NlcicsICdzaW11bGF0b3InLCAncHJldmlldyddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6ICdicm93c2VyJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnYnVpbGRfcHJvamVjdCcsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdCdWlsZCB0aGUgcHJvamVjdCcsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHBsYXRmb3JtOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdCdWlsZCBwbGF0Zm9ybScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZW51bTogWyd3ZWItbW9iaWxlJywgJ3dlYi1kZXNrdG9wJywgJ2lvcycsICdhbmRyb2lkJywgJ3dpbmRvd3MnLCAnbWFjJ11cbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBkZWJ1Zzoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0RlYnVnIGJ1aWxkJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiB0cnVlXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3BsYXRmb3JtJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfcHJvamVjdF9pbmZvJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBwcm9qZWN0IGluZm9ybWF0aW9uJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfcHJvamVjdF9zZXR0aW5ncycsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgcHJvamVjdCBzZXR0aW5ncycsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3J5OiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdTZXR0aW5ncyBjYXRlZ29yeScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZW51bTogWydnZW5lcmFsJywgJ3BoeXNpY3MnLCAncmVuZGVyJywgJ2Fzc2V0cyddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6ICdnZW5lcmFsJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAncmVmcmVzaF9hc3NldHMnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUmVmcmVzaCBhc3NldCBkYXRhYmFzZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvbGRlcjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU3BlY2lmaWMgZm9sZGVyIHRvIHJlZnJlc2ggKG9wdGlvbmFsKSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2ltcG9ydF9hc3NldCcsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdJbXBvcnQgYW4gYXNzZXQgZmlsZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZVBhdGg6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1NvdXJjZSBmaWxlIHBhdGgnXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0Rm9sZGVyOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdUYXJnZXQgZm9sZGVyIGluIGFzc2V0cydcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsnc291cmNlUGF0aCcsICd0YXJnZXRGb2xkZXInXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2dldF9hc3NldF9pbmZvJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBhc3NldCBpbmZvcm1hdGlvbicsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGFzc2V0UGF0aDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQXNzZXQgcGF0aCAoZGI6Ly9hc3NldHMvLi4uKSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsnYXNzZXRQYXRoJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfYXNzZXRzJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBhc3NldHMgYnkgdHlwZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0Fzc2V0IHR5cGUgZmlsdGVyJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbnVtOiBbJ2FsbCcsICdzY2VuZScsICdwcmVmYWInLCAnc2NyaXB0JywgJ3RleHR1cmUnLCAnbWF0ZXJpYWwnLCAnbWVzaCcsICdhdWRpbycsICdhbmltYXRpb24nXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiAnYWxsJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvbGRlcjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnRm9sZGVyIHRvIHNlYXJjaCBpbicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDogJ2RiOi8vYXNzZXRzJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ2V0X2J1aWxkX3NldHRpbmdzJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBidWlsZCBzZXR0aW5ncyAtIHNob3dzIGN1cnJlbnQgbGltaXRhdGlvbnMnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7fVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ29wZW5fYnVpbGRfcGFuZWwnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnT3BlbiB0aGUgYnVpbGQgcGFuZWwgaW4gdGhlIGVkaXRvcicsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHt9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnY2hlY2tfYnVpbGRlcl9zdGF0dXMnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ2hlY2sgaWYgYnVpbGRlciB3b3JrZXIgaXMgcmVhZHknLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7fVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ3N0YXJ0X3ByZXZpZXdfc2VydmVyJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0IHByZXZpZXcgc2VydmVyJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgcG9ydDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUHJldmlldyBzZXJ2ZXIgcG9ydCcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDogNzQ1NlxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnc3RvcF9wcmV2aWV3X3NlcnZlcicsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdTdG9wIHByZXZpZXcgc2VydmVyJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdjcmVhdGVfYXNzZXQnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ3JlYXRlIGEgbmV3IGFzc2V0IGZpbGUgb3IgZm9sZGVyJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXJsOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdBc3NldCBVUkwgKGUuZy4sIGRiOi8vYXNzZXRzL25ld2ZpbGUuanNvbiknXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgY29udGVudDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSBjb250ZW50IChudWxsIGZvciBmb2xkZXIpJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiBudWxsXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcndyaXRlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnT3ZlcndyaXRlIGV4aXN0aW5nIGZpbGUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6IGZhbHNlXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3VybCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnY29weV9hc3NldCcsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdDb3B5IGFuIGFzc2V0IHRvIGFub3RoZXIgbG9jYXRpb24nLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2U6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1NvdXJjZSBhc3NldCBVUkwnXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0OiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdUYXJnZXQgbG9jYXRpb24gVVJMJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIG92ZXJ3cml0ZToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ092ZXJ3cml0ZSBleGlzdGluZyBmaWxlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OiBmYWxzZVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydzb3VyY2UnLCAndGFyZ2V0J11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdtb3ZlX2Fzc2V0JyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ01vdmUgYW4gYXNzZXQgdG8gYW5vdGhlciBsb2NhdGlvbicsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU291cmNlIGFzc2V0IFVSTCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXQ6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1RhcmdldCBsb2NhdGlvbiBVUkwnXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgb3ZlcndyaXRlOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnT3ZlcndyaXRlIGV4aXN0aW5nIGZpbGUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6IGZhbHNlXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3NvdXJjZScsICd0YXJnZXQnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2RlbGV0ZV9hc3NldCcsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdEZWxldGUgYW4gYXNzZXQnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0Fzc2V0IFVSTCB0byBkZWxldGUnXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3VybCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnc2F2ZV9hc3NldCcsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdTYXZlIGFzc2V0IGNvbnRlbnQnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0Fzc2V0IFVSTCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICBjb250ZW50OiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdBc3NldCBjb250ZW50J1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWyd1cmwnLCAnY29udGVudCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAncmVpbXBvcnRfYXNzZXQnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUmVpbXBvcnQgYW4gYXNzZXQnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0Fzc2V0IFVSTCB0byByZWltcG9ydCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsndXJsJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdxdWVyeV9hc3NldF9wYXRoJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBhc3NldCBkaXNrIHBhdGgnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0Fzc2V0IFVSTCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsndXJsJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdxdWVyeV9hc3NldF91dWlkJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBhc3NldCBVVUlEIGZyb20gVVJMJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXJsOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdBc3NldCBVUkwnXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3VybCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAncXVlcnlfYXNzZXRfdXJsJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBhc3NldCBVUkwgZnJvbSBVVUlEJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXVpZDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQXNzZXQgVVVJRCdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsndXVpZCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICBdO1xuICAgIH1cblxuICAgIGFzeW5jIGV4ZWN1dGUodG9vbE5hbWU6IHN0cmluZywgYXJnczogYW55KTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgc3dpdGNoICh0b29sTmFtZSkge1xuICAgICAgICAgICAgY2FzZSAncnVuX3Byb2plY3QnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLnJ1blByb2plY3QoYXJncy5wbGF0Zm9ybSk7XG4gICAgICAgICAgICBjYXNlICdidWlsZF9wcm9qZWN0JzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5idWlsZFByb2plY3QoYXJncyk7XG4gICAgICAgICAgICBjYXNlICdnZXRfcHJvamVjdF9pbmZvJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRQcm9qZWN0SW5mbygpO1xuICAgICAgICAgICAgY2FzZSAnZ2V0X3Byb2plY3Rfc2V0dGluZ3MnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmdldFByb2plY3RTZXR0aW5ncyhhcmdzLmNhdGVnb3J5KTtcbiAgICAgICAgICAgIGNhc2UgJ3JlZnJlc2hfYXNzZXRzJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5yZWZyZXNoQXNzZXRzKGFyZ3MuZm9sZGVyKTtcbiAgICAgICAgICAgIGNhc2UgJ2ltcG9ydF9hc3NldCc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuaW1wb3J0QXNzZXQoYXJncy5zb3VyY2VQYXRoLCBhcmdzLnRhcmdldEZvbGRlcik7XG4gICAgICAgICAgICBjYXNlICdnZXRfYXNzZXRfaW5mbyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0QXNzZXRJbmZvKGFyZ3MuYXNzZXRQYXRoKTtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9hc3NldHMnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmdldEFzc2V0cyhhcmdzLnR5cGUsIGFyZ3MuZm9sZGVyKTtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9idWlsZF9zZXR0aW5ncyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0QnVpbGRTZXR0aW5ncygpO1xuICAgICAgICAgICAgY2FzZSAnb3Blbl9idWlsZF9wYW5lbCc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMub3BlbkJ1aWxkUGFuZWwoKTtcbiAgICAgICAgICAgIGNhc2UgJ2NoZWNrX2J1aWxkZXJfc3RhdHVzJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5jaGVja0J1aWxkZXJTdGF0dXMoKTtcbiAgICAgICAgICAgIGNhc2UgJ3N0YXJ0X3ByZXZpZXdfc2VydmVyJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5zdGFydFByZXZpZXdTZXJ2ZXIoYXJncy5wb3J0KTtcbiAgICAgICAgICAgIGNhc2UgJ3N0b3BfcHJldmlld19zZXJ2ZXInOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLnN0b3BQcmV2aWV3U2VydmVyKCk7XG4gICAgICAgICAgICBjYXNlICdjcmVhdGVfYXNzZXQnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmNyZWF0ZUFzc2V0KGFyZ3MudXJsLCBhcmdzLmNvbnRlbnQsIGFyZ3Mub3ZlcndyaXRlKTtcbiAgICAgICAgICAgIGNhc2UgJ2NvcHlfYXNzZXQnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmNvcHlBc3NldChhcmdzLnNvdXJjZSwgYXJncy50YXJnZXQsIGFyZ3Mub3ZlcndyaXRlKTtcbiAgICAgICAgICAgIGNhc2UgJ21vdmVfYXNzZXQnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLm1vdmVBc3NldChhcmdzLnNvdXJjZSwgYXJncy50YXJnZXQsIGFyZ3Mub3ZlcndyaXRlKTtcbiAgICAgICAgICAgIGNhc2UgJ2RlbGV0ZV9hc3NldCc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZGVsZXRlQXNzZXQoYXJncy51cmwpO1xuICAgICAgICAgICAgY2FzZSAnc2F2ZV9hc3NldCc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuc2F2ZUFzc2V0KGFyZ3MudXJsLCBhcmdzLmNvbnRlbnQpO1xuICAgICAgICAgICAgY2FzZSAncmVpbXBvcnRfYXNzZXQnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLnJlaW1wb3J0QXNzZXQoYXJncy51cmwpO1xuICAgICAgICAgICAgY2FzZSAncXVlcnlfYXNzZXRfcGF0aCc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMucXVlcnlBc3NldFBhdGgoYXJncy51cmwpO1xuICAgICAgICAgICAgY2FzZSAncXVlcnlfYXNzZXRfdXVpZCc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMucXVlcnlBc3NldFV1aWQoYXJncy51cmwpO1xuICAgICAgICAgICAgY2FzZSAncXVlcnlfYXNzZXRfdXJsJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5xdWVyeUFzc2V0VXJsKGFyZ3MudXVpZCk7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biB0b29sOiAke3Rvb2xOYW1lfWApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBydW5Qcm9qZWN0KHBsYXRmb3JtOiBzdHJpbmcgPSAnYnJvd3NlcicpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHByZXZpZXdDb25maWcgPSB7XG4gICAgICAgICAgICAgICAgcGxhdGZvcm06IHBsYXRmb3JtLFxuICAgICAgICAgICAgICAgIHNjZW5lczogW10gLy8gV2lsbCB1c2UgY3VycmVudCBzY2VuZVxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgncHJldmlldycsICdzdGFydCcsIHByZXZpZXdDb25maWcpLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgUHJvamVjdCBpcyBydW5uaW5nIGluICR7cGxhdGZvcm19IG1vZGVgXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgYnVpbGRQcm9qZWN0KGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgYnVpbGRPcHRpb25zID0ge1xuICAgICAgICAgICAgICAgIHBsYXRmb3JtOiBhcmdzLnBsYXRmb3JtLFxuICAgICAgICAgICAgICAgIGRlYnVnOiBhcmdzLmRlYnVnICE9PSBmYWxzZSxcbiAgICAgICAgICAgICAgICBzb3VyY2VNYXBzOiBhcmdzLmRlYnVnICE9PSBmYWxzZSxcbiAgICAgICAgICAgICAgICBidWlsZFBhdGg6IGBidWlsZC8ke2FyZ3MucGxhdGZvcm19YFxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYnVpbGRlcicsICdidWlsZCcsIGJ1aWxkT3B0aW9ucykudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBQcm9qZWN0IGJ1aWx0IGZvciAke2FyZ3MucGxhdGZvcm19YCxcbiAgICAgICAgICAgICAgICAgICAgZGF0YTogeyBidWlsZFBhdGg6IGJ1aWxkT3B0aW9ucy5idWlsZFBhdGggfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldFByb2plY3RJbmZvKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgaW5mbzogUHJvamVjdEluZm8gPSB7XG4gICAgICAgICAgICAgICAgbmFtZTogRWRpdG9yLlByb2plY3QubmFtZSxcbiAgICAgICAgICAgICAgICBwYXRoOiBFZGl0b3IuUHJvamVjdC5wYXRoLFxuICAgICAgICAgICAgICAgIHV1aWQ6IEVkaXRvci5Qcm9qZWN0LnV1aWQsXG4gICAgICAgICAgICAgICAgdmVyc2lvbjogKEVkaXRvci5Qcm9qZWN0IGFzIGFueSkudmVyc2lvbiB8fCAnMS4wLjAnLFxuICAgICAgICAgICAgICAgIGNvY29zVmVyc2lvbjogKEVkaXRvciBhcyBhbnkpLnZlcnNpb25zPy5jb2NvcyB8fCAnVW5rbm93bidcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3Byb2plY3QnLCAncXVlcnktaW5mbycpLnRoZW4oKGFkZGl0aW9uYWxJbmZvOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBPYmplY3QuYXNzaWduKGluZm8sIGFkZGl0aW9uYWxJbmZvKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogdHJ1ZSwgZGF0YTogaW5mbyB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKCgpID0+IHtcbiAgICAgICAgICAgICAgICAvLyBSZXR1cm4gYmFzaWMgaW5mbyBldmVuIGlmIGRldGFpbGVkIHF1ZXJ5IGZhaWxzXG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IHRydWUsIGRhdGE6IGluZm8gfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRQcm9qZWN0U2V0dGluZ3MoY2F0ZWdvcnk6IHN0cmluZyA9ICdnZW5lcmFsJyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgLy8g5L2/55So5q2j56Gu55qEIHByb2plY3QgQVBJIOafpeivoumhueebrumFjee9rlxuICAgICAgICAgICAgY29uc3QgY29uZmlnTWFwOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgICAgICAgICAgICAgIGdlbmVyYWw6ICdwcm9qZWN0JyxcbiAgICAgICAgICAgICAgICBwaHlzaWNzOiAncGh5c2ljcycsXG4gICAgICAgICAgICAgICAgcmVuZGVyOiAncmVuZGVyJyxcbiAgICAgICAgICAgICAgICBhc3NldHM6ICdhc3NldC1kYidcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIGNvbnN0IGNvbmZpZ05hbWUgPSBjb25maWdNYXBbY2F0ZWdvcnldIHx8ICdwcm9qZWN0JztcblxuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgncHJvamVjdCcsICdxdWVyeS1jb25maWcnLCBjb25maWdOYW1lKS50aGVuKChzZXR0aW5nczogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3J5OiBjYXRlZ29yeSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpZzogc2V0dGluZ3MsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgJHtjYXRlZ29yeX0gc2V0dGluZ3MgcmV0cmlldmVkIHN1Y2Nlc3NmdWxseWBcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIHJlZnJlc2hBc3NldHMoZm9sZGVyPzogc3RyaW5nKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICAvLyDkvb/nlKjmraPnoa7nmoQgYXNzZXQtZGIgQVBJIOWIt+aWsOi1hOa6kFxuICAgICAgICAgICAgY29uc3QgdGFyZ2V0UGF0aCA9IGZvbGRlciB8fCAnZGI6Ly9hc3NldHMnO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdhc3NldC1kYicsICdyZWZyZXNoLWFzc2V0JywgdGFyZ2V0UGF0aCkudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBBc3NldHMgcmVmcmVzaGVkIGluOiAke3RhcmdldFBhdGh9YFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGltcG9ydEFzc2V0KHNvdXJjZVBhdGg6IHN0cmluZywgdGFyZ2V0Rm9sZGVyOiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIGlmICghZnMuZXhpc3RzU3luYyhzb3VyY2VQYXRoKSkge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6ICdTb3VyY2UgZmlsZSBub3QgZm91bmQnIH0pO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgZmlsZU5hbWUgPSBwYXRoLmJhc2VuYW1lKHNvdXJjZVBhdGgpO1xuICAgICAgICAgICAgY29uc3QgdGFyZ2V0UGF0aCA9IHRhcmdldEZvbGRlci5zdGFydHNXaXRoKCdkYjovLycpID9cbiAgICAgICAgICAgICAgICB0YXJnZXRGb2xkZXIgOiBgZGI6Ly9hc3NldHMvJHt0YXJnZXRGb2xkZXJ9YDtcblxuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYXNzZXQtZGInLCAnaW1wb3J0LWFzc2V0Jywgc291cmNlUGF0aCwgYCR7dGFyZ2V0UGF0aH0vJHtmaWxlTmFtZX1gKS50aGVuKChyZXN1bHQ6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiByZXN1bHQudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHBhdGg6IHJlc3VsdC51cmwsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgQXNzZXQgaW1wb3J0ZWQ6ICR7ZmlsZU5hbWV9YFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0QXNzZXRJbmZvKGFzc2V0UGF0aDogc3RyaW5nKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdhc3NldC1kYicsICdxdWVyeS1hc3NldC1pbmZvJywgYXNzZXRQYXRoKS50aGVuKChhc3NldEluZm86IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghYXNzZXRJbmZvKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQXNzZXQgbm90IGZvdW5kJyk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgY29uc3QgaW5mbzogQXNzZXRJbmZvID0ge1xuICAgICAgICAgICAgICAgICAgICBuYW1lOiBhc3NldEluZm8ubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogYXNzZXRJbmZvLnV1aWQsXG4gICAgICAgICAgICAgICAgICAgIHBhdGg6IGFzc2V0SW5mby51cmwsXG4gICAgICAgICAgICAgICAgICAgIHR5cGU6IGFzc2V0SW5mby50eXBlLFxuICAgICAgICAgICAgICAgICAgICBzaXplOiBhc3NldEluZm8uc2l6ZSxcbiAgICAgICAgICAgICAgICAgICAgaXNEaXJlY3Rvcnk6IGFzc2V0SW5mby5pc0RpcmVjdG9yeVxuICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICBpZiAoYXNzZXRJbmZvLm1ldGEpIHtcbiAgICAgICAgICAgICAgICAgICAgaW5mby5tZXRhID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmVyOiBhc3NldEluZm8ubWV0YS52ZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICBpbXBvcnRlcjogYXNzZXRJbmZvLm1ldGEuaW1wb3J0ZXJcbiAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogdHJ1ZSwgZGF0YTogaW5mbyB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRBc3NldHModHlwZTogc3RyaW5nID0gJ2FsbCcsIGZvbGRlcjogc3RyaW5nID0gJ2RiOi8vYXNzZXRzJyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgbGV0IHBhdHRlcm4gPSBgJHtmb2xkZXJ9LyoqLypgO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDmt7vliqDnsbvlnovov4fmu6RcbiAgICAgICAgICAgIGlmICh0eXBlICE9PSAnYWxsJykge1xuICAgICAgICAgICAgICAgIGNvbnN0IHR5cGVFeHRlbnNpb25zOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgICAgICAgICAgICAgICAgICAnc2NlbmUnOiAnLnNjZW5lJyxcbiAgICAgICAgICAgICAgICAgICAgJ3ByZWZhYic6ICcucHJlZmFiJyxcbiAgICAgICAgICAgICAgICAgICAgJ3NjcmlwdCc6ICcue3RzLGpzfScsXG4gICAgICAgICAgICAgICAgICAgICd0ZXh0dXJlJzogJy57cG5nLGpwZyxqcGVnLGdpZix0Z2EsYm1wLHBzZH0nLFxuICAgICAgICAgICAgICAgICAgICAnbWF0ZXJpYWwnOiAnLm10bCcsXG4gICAgICAgICAgICAgICAgICAgICdtZXNoJzogJy57ZmJ4LG9iaixkYWV9JyxcbiAgICAgICAgICAgICAgICAgICAgJ2F1ZGlvJzogJy57bXAzLG9nZyx3YXYsbTRhfScsXG4gICAgICAgICAgICAgICAgICAgICdhbmltYXRpb24nOiAnLnthbmltLGNsaXB9J1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgY29uc3QgZXh0ZW5zaW9uID0gdHlwZUV4dGVuc2lvbnNbdHlwZV07XG4gICAgICAgICAgICAgICAgaWYgKGV4dGVuc2lvbikge1xuICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gYCR7Zm9sZGVyfS8qKi8qJHtleHRlbnNpb259YDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ3F1ZXJ5LWFzc2V0cycsIHtcbiAgICAgICAgICAgICAgICBwYXR0ZXJuOiBwYXR0ZXJuXG4gICAgICAgICAgICB9KS50aGVuKChyZXN1bHRzOiBhbnlbXSkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGFzc2V0cyA9IHJlc3VsdHMubWFwKGFzc2V0ID0+ICh7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6IGFzc2V0Lm5hbWUsXG4gICAgICAgICAgICAgICAgICAgIHV1aWQ6IGFzc2V0LnV1aWQsXG4gICAgICAgICAgICAgICAgICAgIHBhdGg6IGFzc2V0LnVybCxcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogYXNzZXQudHlwZSxcbiAgICAgICAgICAgICAgICAgICAgc2l6ZTogYXNzZXQuc2l6ZSB8fCAwLFxuICAgICAgICAgICAgICAgICAgICBpc0RpcmVjdG9yeTogYXNzZXQuaXNEaXJlY3RvcnkgfHwgZmFsc2VcbiAgICAgICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IFxuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLCBcbiAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogdHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvbGRlcjogZm9sZGVyLFxuICAgICAgICAgICAgICAgICAgICAgICAgY291bnQ6IGFzc2V0cy5sZW5ndGgsXG4gICAgICAgICAgICAgICAgICAgICAgICBhc3NldHM6IGFzc2V0c1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0QnVpbGRTZXR0aW5ncygpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIC8vIOajgOafpeaehOW7uuWZqOaYr+WQpuWHhuWkh+Wwsee7qlxuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYnVpbGRlcicsICdxdWVyeS13b3JrZXItcmVhZHknKS50aGVuKChyZWFkeTogYm9vbGVhbikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBidWlsZGVyUmVhZHk6IHJlYWR5LFxuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ0J1aWxkIHNldHRpbmdzIGFyZSBsaW1pdGVkIGluIE1DUCBwbHVnaW4gZW52aXJvbm1lbnQnLFxuICAgICAgICAgICAgICAgICAgICAgICAgYXZhaWxhYmxlQWN0aW9uczogW1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdPcGVuIGJ1aWxkIHBhbmVsIHdpdGggb3Blbl9idWlsZF9wYW5lbCcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0NoZWNrIGJ1aWxkZXIgc3RhdHVzIHdpdGggY2hlY2tfYnVpbGRlcl9zdGF0dXMnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTdGFydCBwcmV2aWV3IHNlcnZlciB3aXRoIHN0YXJ0X3ByZXZpZXdfc2VydmVyJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU3RvcCBwcmV2aWV3IHNlcnZlciB3aXRoIHN0b3BfcHJldmlld19zZXJ2ZXInXG4gICAgICAgICAgICAgICAgICAgICAgICBdLFxuICAgICAgICAgICAgICAgICAgICAgICAgbGltaXRhdGlvbjogJ0Z1bGwgYnVpbGQgY29uZmlndXJhdGlvbiByZXF1aXJlcyBkaXJlY3QgRWRpdG9yIFVJIGFjY2VzcydcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIG9wZW5CdWlsZFBhbmVsKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYnVpbGRlcicsICdvcGVuJykudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdCdWlsZCBwYW5lbCBvcGVuZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGNoZWNrQnVpbGRlclN0YXR1cygpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2J1aWxkZXInLCAncXVlcnktd29ya2VyLXJlYWR5JykudGhlbigocmVhZHk6IGJvb2xlYW4pID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmVhZHk6IHJlYWR5LFxuICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzOiByZWFkeSA/ICdCdWlsZGVyIHdvcmtlciBpcyByZWFkeScgOiAnQnVpbGRlciB3b3JrZXIgaXMgbm90IHJlYWR5JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdCdWlsZGVyIHN0YXR1cyBjaGVja2VkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIHN0YXJ0UHJldmlld1NlcnZlcihwb3J0OiBudW1iZXIgPSA3NDU2KTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICBlcnJvcjogJ1ByZXZpZXcgc2VydmVyIGNvbnRyb2wgaXMgbm90IHN1cHBvcnRlZCB0aHJvdWdoIE1DUCBBUEknLFxuICAgICAgICAgICAgICAgIGluc3RydWN0aW9uOiAnUGxlYXNlIHN0YXJ0IHRoZSBwcmV2aWV3IHNlcnZlciBtYW51YWxseSB1c2luZyB0aGUgZWRpdG9yIG1lbnU6IFByb2plY3QgPiBQcmV2aWV3LCBvciB1c2UgdGhlIHByZXZpZXcgcGFuZWwgaW4gdGhlIGVkaXRvcidcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIHN0b3BQcmV2aWV3U2VydmVyKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgICAgZXJyb3I6ICdQcmV2aWV3IHNlcnZlciBjb250cm9sIGlzIG5vdCBzdXBwb3J0ZWQgdGhyb3VnaCBNQ1AgQVBJJyxcbiAgICAgICAgICAgICAgICBpbnN0cnVjdGlvbjogJ1BsZWFzZSBzdG9wIHRoZSBwcmV2aWV3IHNlcnZlciBtYW51YWxseSB1c2luZyB0aGUgcHJldmlldyBwYW5lbCBpbiB0aGUgZWRpdG9yJ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgY3JlYXRlQXNzZXQodXJsOiBzdHJpbmcsIGNvbnRlbnQ6IHN0cmluZyB8IG51bGwgPSBudWxsLCBvdmVyd3JpdGU6IGJvb2xlYW4gPSBmYWxzZSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICAgICAgICAgICAgICBvdmVyd3JpdGU6IG92ZXJ3cml0ZSxcbiAgICAgICAgICAgICAgICByZW5hbWU6ICFvdmVyd3JpdGVcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ2NyZWF0ZS1hc3NldCcsIHVybCwgY29udGVudCwgb3B0aW9ucykudGhlbigocmVzdWx0OiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAocmVzdWx0ICYmIHJlc3VsdC51dWlkKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiByZXN1bHQudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHJlc3VsdC51cmwsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogY29udGVudCA9PT0gbnVsbCA/ICdGb2xkZXIgY3JlYXRlZCBzdWNjZXNzZnVsbHknIDogJ0ZpbGUgY3JlYXRlZCBzdWNjZXNzZnVsbHknXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHVybCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBjb250ZW50ID09PSBudWxsID8gJ0ZvbGRlciBjcmVhdGVkIHN1Y2Nlc3NmdWxseScgOiAnRmlsZSBjcmVhdGVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGNvcHlBc3NldChzb3VyY2U6IHN0cmluZywgdGFyZ2V0OiBzdHJpbmcsIG92ZXJ3cml0ZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBvcHRpb25zID0ge1xuICAgICAgICAgICAgICAgIG92ZXJ3cml0ZTogb3ZlcndyaXRlLFxuICAgICAgICAgICAgICAgIHJlbmFtZTogIW92ZXJ3cml0ZVxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYXNzZXQtZGInLCAnY29weS1hc3NldCcsIHNvdXJjZSwgdGFyZ2V0LCBvcHRpb25zKS50aGVuKChyZXN1bHQ6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIGlmIChyZXN1bHQgJiYgcmVzdWx0LnV1aWQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHV1aWQ6IHJlc3VsdC51dWlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVybDogcmVzdWx0LnVybCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiAnQXNzZXQgY29waWVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZTogc291cmNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldDogdGFyZ2V0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdBc3NldCBjb3BpZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgbW92ZUFzc2V0KHNvdXJjZTogc3RyaW5nLCB0YXJnZXQ6IHN0cmluZywgb3ZlcndyaXRlOiBib29sZWFuID0gZmFsc2UpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IG9wdGlvbnMgPSB7XG4gICAgICAgICAgICAgICAgb3ZlcndyaXRlOiBvdmVyd3JpdGUsXG4gICAgICAgICAgICAgICAgcmVuYW1lOiAhb3ZlcndyaXRlXG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdhc3NldC1kYicsICdtb3ZlLWFzc2V0Jywgc291cmNlLCB0YXJnZXQsIG9wdGlvbnMpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHJlc3VsdCAmJiByZXN1bHQudXVpZCkge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXVpZDogcmVzdWx0LnV1aWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJsOiByZXN1bHQudXJsLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdBc3NldCBtb3ZlZCBzdWNjZXNzZnVsbHknXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2U6IHNvdXJjZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXQ6IHRhcmdldCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiAnQXNzZXQgbW92ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZGVsZXRlQXNzZXQodXJsOiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ2RlbGV0ZS1hc3NldCcsIHVybCkudGhlbigocmVzdWx0OiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgdXJsOiB1cmwsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiAnQXNzZXQgZGVsZXRlZCBzdWNjZXNzZnVsbHknXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBzYXZlQXNzZXQodXJsOiBzdHJpbmcsIGNvbnRlbnQ6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYXNzZXQtZGInLCAnc2F2ZS1hc3NldCcsIHVybCwgY29udGVudCkudGhlbigocmVzdWx0OiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAocmVzdWx0ICYmIHJlc3VsdC51dWlkKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiByZXN1bHQudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHJlc3VsdC51cmwsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ0Fzc2V0IHNhdmVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdBc3NldCBzYXZlZCBzdWNjZXNzZnVsbHknXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyByZWltcG9ydEFzc2V0KHVybDogc3RyaW5nKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdhc3NldC1kYicsICdyZWltcG9ydC1hc3NldCcsIHVybCkudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ0Fzc2V0IHJlaW1wb3J0ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgcXVlcnlBc3NldFBhdGgodXJsOiBzdHJpbmcpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ3F1ZXJ5LXBhdGgnLCB1cmwpLnRoZW4oKHBhdGg6IHN0cmluZyB8IG51bGwpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAocGF0aCkge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJsOiB1cmwsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0aDogcGF0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiAnQXNzZXQgcGF0aCByZXRyaWV2ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiAnQXNzZXQgcGF0aCBub3QgZm91bmQnIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBxdWVyeUFzc2V0VXVpZCh1cmw6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYXNzZXQtZGInLCAncXVlcnktdXVpZCcsIHVybCkudGhlbigodXVpZDogc3RyaW5nIHwgbnVsbCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICh1dWlkKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmw6IHVybCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiB1dWlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdBc3NldCBVVUlEIHJldHJpZXZlZCBzdWNjZXNzZnVsbHknXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6ICdBc3NldCBVVUlEIG5vdCBmb3VuZCcgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIHF1ZXJ5QXNzZXRVcmwodXVpZDogc3RyaW5nKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdhc3NldC1kYicsICdxdWVyeS11cmwnLCB1dWlkKS50aGVuKCh1cmw6IHN0cmluZyB8IG51bGwpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodXJsKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiB1dWlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdBc3NldCBVUkwgcmV0cmlldmVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogJ0Fzc2V0IFVSTCBub3QgZm91bmQnIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxufSJdfQ== \ No newline at end of file diff --git a/dist/tools/scene-tools.js b/dist/tools/scene-tools.js new file mode 100644 index 0000000..023471a --- /dev/null +++ b/dist/tools/scene-tools.js @@ -0,0 +1,450 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SceneTools = void 0; +class SceneTools { + getTools() { + return [ + { + name: 'get_current_scene', + description: 'Get current scene information', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_scene_list', + description: 'Get all scenes in the project', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'open_scene', + description: 'Open a scene by path', + inputSchema: { + type: 'object', + properties: { + scenePath: { + type: 'string', + description: 'The scene file path' + } + }, + required: ['scenePath'] + } + }, + { + name: 'save_scene', + description: 'Save current scene', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'create_scene', + description: 'Create a new scene asset', + inputSchema: { + type: 'object', + properties: { + sceneName: { + type: 'string', + description: 'Name of the new scene' + }, + savePath: { + type: 'string', + description: 'Path to save the scene (e.g., db://assets/scenes/NewScene.scene)' + } + }, + required: ['sceneName', 'savePath'] + } + }, + { + name: 'save_scene_as', + description: 'Save scene as new file', + inputSchema: { + type: 'object', + properties: { + path: { + type: 'string', + description: 'Path to save the scene' + } + }, + required: ['path'] + } + }, + { + name: 'close_scene', + description: 'Close current scene', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_scene_hierarchy', + description: 'Get the complete hierarchy of current scene', + inputSchema: { + type: 'object', + properties: { + includeComponents: { + type: 'boolean', + description: 'Include component information', + default: false + } + } + } + } + ]; + } + async execute(toolName, args) { + switch (toolName) { + case 'get_current_scene': + return await this.getCurrentScene(); + case 'get_scene_list': + return await this.getSceneList(); + case 'open_scene': + return await this.openScene(args.scenePath); + case 'save_scene': + return await this.saveScene(); + case 'create_scene': + return await this.createScene(args.sceneName, args.savePath); + case 'save_scene_as': + return await this.saveSceneAs(args.path); + case 'close_scene': + return await this.closeScene(); + case 'get_scene_hierarchy': + return await this.getSceneHierarchy(args.includeComponents); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + async getCurrentScene() { + return new Promise((resolve) => { + // 直接使用 query-node-tree 来获取场景信息(这个方法已经验证可用) + Editor.Message.request('scene', 'query-node-tree').then((tree) => { + if (tree && tree.uuid) { + resolve({ + success: true, + data: { + name: tree.name || 'Current Scene', + uuid: tree.uuid, + type: tree.type || 'cc.Scene', + active: tree.active !== undefined ? tree.active : true, + nodeCount: tree.children ? tree.children.length : 0 + } + }); + } + else { + resolve({ success: false, error: 'No scene data available' }); + } + }).catch((err) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getCurrentSceneInfo', + args: [] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + resolve(result); + }).catch((err2) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + async getSceneList() { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-assets', { + pattern: 'db://assets/**/*.scene' + }).then((results) => { + const scenes = results.map(asset => ({ + name: asset.name, + path: asset.url, + uuid: asset.uuid + })); + resolve({ success: true, data: scenes }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async openScene(scenePath) { + return new Promise((resolve) => { + // 首先获取场景的UUID + Editor.Message.request('asset-db', 'query-uuid', scenePath).then((uuid) => { + if (!uuid) { + throw new Error('Scene not found'); + } + // 使用正确的 scene API 打开场景 (需要UUID) + return Editor.Message.request('scene', 'open-scene', uuid); + }).then(() => { + resolve({ success: true, message: `Scene opened: ${scenePath}` }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async saveScene() { + return new Promise((resolve) => { + Editor.Message.request('scene', 'save-scene').then(() => { + resolve({ success: true, message: 'Scene saved successfully' }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async createScene(sceneName, savePath) { + return new Promise((resolve) => { + // 确保路径以.scene结尾 + const fullPath = savePath.endsWith('.scene') ? savePath : `${savePath}/${sceneName}.scene`; + // 使用正确的Cocos Creator 3.8场景格式 + const sceneContent = JSON.stringify([ + { + "__type__": "cc.SceneAsset", + "_name": sceneName, + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "scene": { + "__id__": 1 + } + }, + { + "__type__": "cc.Scene", + "_name": sceneName, + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [], + "_active": true, + "_components": [], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "autoReleaseAssets": false, + "_globals": { + "__id__": 2 + }, + "_id": "scene" + }, + { + "__type__": "cc.SceneGlobals", + "ambient": { + "__id__": 3 + }, + "skybox": { + "__id__": 4 + }, + "fog": { + "__id__": 5 + }, + "octree": { + "__id__": 6 + } + }, + { + "__type__": "cc.AmbientInfo", + "_skyColorHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833 + }, + "_skyColor": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833 + }, + "_skyIllumHDR": 20000, + "_skyIllum": 20000, + "_groundAlbedoHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_groundAlbedo": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + } + }, + { + "__type__": "cc.SkyboxInfo", + "_envLightingType": 0, + "_envmapHDR": null, + "_envmap": null, + "_envmapLodCount": 0, + "_diffuseMapHDR": null, + "_diffuseMap": null, + "_enabled": false, + "_useHDR": true, + "_editableMaterial": null, + "_reflectionHDR": null, + "_reflectionMap": null, + "_rotationAngle": 0 + }, + { + "__type__": "cc.FogInfo", + "_type": 0, + "_fogColor": { + "__type__": "cc.Color", + "r": 200, + "g": 200, + "b": 200, + "a": 255 + }, + "_enabled": false, + "_fogDensity": 0.3, + "_fogStart": 0.5, + "_fogEnd": 300, + "_fogAtten": 5, + "_fogTop": 1.5, + "_fogRange": 1.2, + "_accurate": false + }, + { + "__type__": "cc.OctreeInfo", + "_enabled": false, + "_minPos": { + "__type__": "cc.Vec3", + "x": -1024, + "y": -1024, + "z": -1024 + }, + "_maxPos": { + "__type__": "cc.Vec3", + "x": 1024, + "y": 1024, + "z": 1024 + }, + "_depth": 8 + } + ], null, 2); + Editor.Message.request('asset-db', 'create-asset', fullPath, sceneContent).then((result) => { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + name: sceneName, + message: `Scene '${sceneName}' created successfully` + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async getSceneHierarchy(includeComponents = false) { + return new Promise((resolve) => { + // 优先尝试使用 Editor API 查询场景节点树 + Editor.Message.request('scene', 'query-node-tree').then((tree) => { + if (tree) { + const hierarchy = this.buildHierarchy(tree, includeComponents); + resolve({ + success: true, + data: hierarchy + }); + } + else { + resolve({ success: false, error: 'No scene hierarchy available' }); + } + }).catch((err) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getSceneHierarchy', + args: [includeComponents] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result) => { + resolve(result); + }).catch((err2) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + buildHierarchy(node, includeComponents) { + const nodeInfo = { + uuid: node.uuid, + name: node.name, + type: node.type, + active: node.active, + children: [] + }; + if (includeComponents && node.__comps__) { + nodeInfo.components = node.__comps__.map((comp) => ({ + type: comp.__type__ || 'Unknown', + enabled: comp.enabled !== undefined ? comp.enabled : true + })); + } + if (node.children) { + nodeInfo.children = node.children.map((child) => this.buildHierarchy(child, includeComponents)); + } + return nodeInfo; + } + async saveSceneAs(path) { + return new Promise((resolve) => { + // save-as-scene API 不接受路径参数,会弹出对话框让用户选择 + Editor.Message.request('scene', 'save-as-scene').then(() => { + resolve({ + success: true, + data: { + path: path, + message: `Scene save-as dialog opened` + } + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } + async closeScene() { + return new Promise((resolve) => { + Editor.Message.request('scene', 'close-scene').then(() => { + resolve({ + success: true, + message: 'Scene closed successfully' + }); + }).catch((err) => { + resolve({ success: false, error: err.message }); + }); + }); + } +} +exports.SceneTools = SceneTools; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NlbmUtdG9vbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvdG9vbHMvc2NlbmUtdG9vbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBRUEsTUFBYSxVQUFVO0lBQ25CLFFBQVE7UUFDSixPQUFPO1lBQ0g7Z0JBQ0ksSUFBSSxFQUFFLG1CQUFtQjtnQkFDekIsV0FBVyxFQUFFLCtCQUErQjtnQkFDNUMsV0FBVyxFQUFFO29CQUNULElBQUksRUFBRSxRQUFRO29CQUNkLFVBQVUsRUFBRSxFQUFFO2lCQUNqQjthQUNKO1lBQ0Q7Z0JBQ0ksSUFBSSxFQUFFLGdCQUFnQjtnQkFDdEIsV0FBVyxFQUFFLCtCQUErQjtnQkFDNUMsV0FBVyxFQUFFO29CQUNULElBQUksRUFBRSxRQUFRO29CQUNkLFVBQVUsRUFBRSxFQUFFO2lCQUNqQjthQUNKO1lBQ0Q7Z0JBQ0ksSUFBSSxFQUFFLFlBQVk7Z0JBQ2xCLFdBQVcsRUFBRSxzQkFBc0I7Z0JBQ25DLFdBQVcsRUFBRTtvQkFDVCxJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUU7d0JBQ1IsU0FBUyxFQUFFOzRCQUNQLElBQUksRUFBRSxRQUFROzRCQUNkLFdBQVcsRUFBRSxxQkFBcUI7eUJBQ3JDO3FCQUNKO29CQUNELFFBQVEsRUFBRSxDQUFDLFdBQVcsQ0FBQztpQkFDMUI7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxZQUFZO2dCQUNsQixXQUFXLEVBQUUsb0JBQW9CO2dCQUNqQyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFLEVBQUU7aUJBQ2pCO2FBQ0o7WUFDRDtnQkFDSSxJQUFJLEVBQUUsY0FBYztnQkFDcEIsV0FBVyxFQUFFLDBCQUEwQjtnQkFDdkMsV0FBVyxFQUFFO29CQUNULElBQUksRUFBRSxRQUFRO29CQUNkLFVBQVUsRUFBRTt3QkFDUixTQUFTLEVBQUU7NEJBQ1AsSUFBSSxFQUFFLFFBQVE7NEJBQ2QsV0FB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TyxFQUFFLDZCQUE2QjtxQkFDekM7aUJBQ0osQ0FBQyxDQUFDO1lBQ1AsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBVSxFQUFFLEVBQUU7Z0JBQ3BCLE9BQU8sQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3BELENBQUMsQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRU8sS0FBSyxDQUFDLFVBQVU7UUFDcEIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxhQUFhLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFO2dCQUNyRCxPQUFPLENBQUM7b0JBQ0osT0FBTyxFQUFFLElBQUk7b0JBQ2IsT0FBTyxFQUFFLDJCQUEyQjtpQkFDdkMsQ0FBQyxDQUFDO1lBQ1AsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBVSxFQUFFLEVBQUU7Z0JBQ3BCLE9BQU8sQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3BELENBQUMsQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0NBQ0o7QUE5Y0QsZ0NBOGNDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgVG9vbERlZmluaXRpb24sIFRvb2xSZXNwb25zZSwgVG9vbEV4ZWN1dG9yLCBTY2VuZUluZm8gfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBjbGFzcyBTY2VuZVRvb2xzIGltcGxlbWVudHMgVG9vbEV4ZWN1dG9yIHtcbiAgICBnZXRUb29scygpOiBUb29sRGVmaW5pdGlvbltdIHtcbiAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ2V0X2N1cnJlbnRfc2NlbmUnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnR2V0IGN1cnJlbnQgc2NlbmUgaW5mb3JtYXRpb24nLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7fVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2dldF9zY2VuZV9saXN0JyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBhbGwgc2NlbmVzIGluIHRoZSBwcm9qZWN0JyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdvcGVuX3NjZW5lJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ09wZW4gYSBzY2VuZSBieSBwYXRoJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2NlbmVQYXRoOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgc2NlbmUgZmlsZSBwYXRoJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydzY2VuZVBhdGgnXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ3NhdmVfc2NlbmUnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU2F2ZSBjdXJyZW50IHNjZW5lJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdjcmVhdGVfc2NlbmUnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ3JlYXRlIGEgbmV3IHNjZW5lIGFzc2V0JyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2NlbmVOYW1lOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdOYW1lIG9mIHRoZSBuZXcgc2NlbmUnXG4gICAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgc2F2ZVBhdGg6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ1BhdGggdG8gc2F2ZSB0aGUgc2NlbmUgKGUuZy4sIGRiOi8vYXNzZXRzL3NjZW5lcy9OZXdTY2VuZS5zY2VuZSknXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3NjZW5lTmFtZScsICdzYXZlUGF0aCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnc2F2ZV9zY2VuZV9hcycsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdTYXZlIHNjZW5lIGFzIG5ldyBmaWxlJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgcGF0aDoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzYXZlIHRoZSBzY2VuZSdcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsncGF0aCddXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnY2xvc2Vfc2NlbmUnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ2xvc2UgY3VycmVudCBzY2VuZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHt9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ2V0X3NjZW5lX2hpZXJhcmNoeScsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgdGhlIGNvbXBsZXRlIGhpZXJhcmNoeSBvZiBjdXJyZW50IHNjZW5lJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZUNvbXBvbmVudHM6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdJbmNsdWRlIGNvbXBvbmVudCBpbmZvcm1hdGlvbicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDogZmFsc2VcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgXTtcbiAgICB9XG5cbiAgICBhc3luYyBleGVjdXRlKHRvb2xOYW1lOiBzdHJpbmcsIGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHN3aXRjaCAodG9vbE5hbWUpIHtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9jdXJyZW50X3NjZW5lJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRDdXJyZW50U2NlbmUoKTtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9zY2VuZV9saXN0JzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRTY2VuZUxpc3QoKTtcbiAgICAgICAgICAgIGNhc2UgJ29wZW5fc2NlbmUnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLm9wZW5TY2VuZShhcmdzLnNjZW5lUGF0aCk7XG4gICAgICAgICAgICBjYXNlICdzYXZlX3NjZW5lJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5zYXZlU2NlbmUoKTtcbiAgICAgICAgICAgIGNhc2UgJ2NyZWF0ZV9zY2VuZSc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuY3JlYXRlU2NlbmUoYXJncy5zY2VuZU5hbWUsIGFyZ3Muc2F2ZVBhdGgpO1xuICAgICAgICAgICAgY2FzZSAnc2F2ZV9zY2VuZV9hcyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuc2F2ZVNjZW5lQXMoYXJncy5wYXRoKTtcbiAgICAgICAgICAgIGNhc2UgJ2Nsb3NlX3NjZW5lJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5jbG9zZVNjZW5lKCk7XG4gICAgICAgICAgICBjYXNlICdnZXRfc2NlbmVfaGllcmFyY2h5JzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRTY2VuZUhpZXJhcmNoeShhcmdzLmluY2x1ZGVDb21wb25lbnRzKTtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIHRvb2w6ICR7dG9vbE5hbWV9YCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldEN1cnJlbnRTY2VuZSgpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIC8vIOebtOaOpeS9v+eUqCBxdWVyeS1ub2RlLXRyZWUg5p2l6I635Y+W5Zy65pmv5L+h5oGv77yI6L+Z5Liq5pa55rOV5bey57uP6aqM6K+B5Y+v55So77yJXG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdxdWVyeS1ub2RlLXRyZWUnKS50aGVuKCh0cmVlOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodHJlZSAmJiB0cmVlLnV1aWQpIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IHRyZWUubmFtZSB8fCAnQ3VycmVudCBTY2VuZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXVpZDogdHJlZS51dWlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IHRyZWUudHlwZSB8fCAnY2MuU2NlbmUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2ZTogdHJlZS5hY3RpdmUgIT09IHVuZGVmaW5lZCA/IHRyZWUuYWN0aXZlIDogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBub2RlQ291bnQ6IHRyZWUuY2hpbGRyZW4gPyB0cmVlLmNoaWxkcmVuLmxlbmd0aCA6IDBcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogJ05vIHNjZW5lIGRhdGEgYXZhaWxhYmxlJyB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIC8vIOWkh+eUqOaWueahiO+8muS9v+eUqOWcuuaZr+iEmuacrFxuICAgICAgICAgICAgICAgIGNvbnN0IG9wdGlvbnMgPSB7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6ICdjb2Nvcy1tY3Atc2VydmVyJyxcbiAgICAgICAgICAgICAgICAgICAgbWV0aG9kOiAnZ2V0Q3VycmVudFNjZW5lSW5mbycsXG4gICAgICAgICAgICAgICAgICAgIGFyZ3M6IFtdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdleGVjdXRlLXNjZW5lLXNjcmlwdCcsIG9wdGlvbnMpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9KS5jYXRjaCgoZXJyMjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYERpcmVjdCBBUEkgZmFpbGVkOiAke2Vyci5tZXNzYWdlfSwgU2NlbmUgc2NyaXB0IGZhaWxlZDogJHtlcnIyLm1lc3NhZ2V9YCB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldFNjZW5lTGlzdCgpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ3F1ZXJ5LWFzc2V0cycsIHtcbiAgICAgICAgICAgICAgICBwYXR0ZXJuOiAnZGI6Ly9hc3NldHMvKiovKi5zY2VuZSdcbiAgICAgICAgICAgIH0pLnRoZW4oKHJlc3VsdHM6IGFueVtdKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3Qgc2NlbmVzOiBTY2VuZUluZm9bXSA9IHJlc3VsdHMubWFwKGFzc2V0ID0+ICh7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6IGFzc2V0Lm5hbWUsXG4gICAgICAgICAgICAgICAgICAgIHBhdGg6IGFzc2V0LnVybCxcbiAgICAgICAgICAgICAgICAgICAgdXVpZDogYXNzZXQudXVpZFxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogdHJ1ZSwgZGF0YTogc2NlbmVzIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIG9wZW5TY2VuZShzY2VuZVBhdGg6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgLy8g6aaW5YWI6I635Y+W5Zy65pmv55qEVVVJRFxuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnYXNzZXQtZGInLCAncXVlcnktdXVpZCcsIHNjZW5lUGF0aCkudGhlbigodXVpZDogc3RyaW5nIHwgbnVsbCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghdXVpZCkge1xuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NjZW5lIG5vdCBmb3VuZCcpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAvLyDkvb/nlKjmraPnoa7nmoQgc2NlbmUgQVBJIOaJk+W8gOWcuuaZryAo6ZyA6KaBVVVJRClcbiAgICAgICAgICAgICAgICByZXR1cm4gRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnb3Blbi1zY2VuZScsIHV1aWQpO1xuICAgICAgICAgICAgfSkudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IHRydWUsIG1lc3NhZ2U6IGBTY2VuZSBvcGVuZWQ6ICR7c2NlbmVQYXRofWAgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgc2F2ZVNjZW5lKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCgnc2NlbmUnLCAnc2F2ZS1zY2VuZScpLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiB0cnVlLCBtZXNzYWdlOiAnU2NlbmUgc2F2ZWQgc3VjY2Vzc2Z1bGx5JyB9KTtcbiAgICAgICAgICAgIH0pLmNhdGNoKChlcnI6IEVycm9yKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBjcmVhdGVTY2VuZShzY2VuZU5hbWU6IHN0cmluZywgc2F2ZVBhdGg6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgLy8g56Gu5L+d6Lev5b6E5LulLnNjZW5l57uT5bC+XG4gICAgICAgICAgICBjb25zdCBmdWxsUGF0aCA9IHNhdmVQYXRoLmVuZHNXaXRoKCcuc2NlbmUnKSA/IHNhdmVQYXRoIDogYCR7c2F2ZVBhdGh9LyR7c2NlbmVOYW1lfS5zY2VuZWA7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOS9v+eUqOato+ehrueahENvY29zIENyZWF0b3IgMy445Zy65pmv5qC85byPXG4gICAgICAgICAgICBjb25zdCBzY2VuZUNvbnRlbnQgPSBKU09OLnN0cmluZ2lmeShbXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBcIl9fdHlwZV9fXCI6IFwiY2MuU2NlbmVBc3NldFwiLFxuICAgICAgICAgICAgICAgICAgICBcIl9uYW1lXCI6IHNjZW5lTmFtZSxcbiAgICAgICAgICAgICAgICAgICAgXCJfb2JqRmxhZ3NcIjogMCxcbiAgICAgICAgICAgICAgICAgICAgXCJfX2VkaXRvckV4dHJhc19fXCI6IHt9LFxuICAgICAgICAgICAgICAgICAgICBcIl9uYXRpdmVcIjogXCJcIixcbiAgICAgICAgICAgICAgICAgICAgXCJzY2VuZVwiOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBcIl9faWRfX1wiOiAxXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgXCJfX3R5cGVfX1wiOiBcImNjLlNjZW5lXCIsXG4gICAgICAgICAgICAgICAgICAgIFwiX25hbWVcIjogc2NlbmVOYW1lLFxuICAgICAgICAgICAgICAgICAgICBcIl9vYmpGbGFnc1wiOiAwLFxuICAgICAgICAgICAgICAgICAgICBcIl9fZWRpdG9yRXh0cmFzX19cIjoge30sXG4gICAgICAgICAgICAgICAgICAgIFwiX3BhcmVudFwiOiBudWxsLFxuICAgICAgICAgICAgICAgICAgICBcIl9jaGlsZHJlblwiOiBbXSxcbiAgICAgICAgICAgICAgICAgICAgXCJfYWN0aXZlXCI6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIFwiX2NvbXBvbmVudHNcIjogW10sXG4gICAgICAgICAgICAgICAgICAgIFwiX3ByZWZhYlwiOiBudWxsLFxuICAgICAgICAgICAgICAgICAgICBcIl9scG9zXCI6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiX190eXBlX19cIjogXCJjYy5WZWMzXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICBcInhcIjogMCxcbiAgICAgICAgICAgICAgICAgICAgICAgIFwieVwiOiAwLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ6XCI6IDBcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgXCJfbHJvdFwiOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBcIl9fdHlwZV9fXCI6IFwiY2MuUXVhdFwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ4XCI6IDAsXG4gICAgICAgICAgICAgICAgICAgICAgICBcInlcIjogMCxcbiAgICAgICAgICAgICAgICAgICAgICAgIFwielwiOiAwLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ3XCI6IDFcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgXCJfbHNjYWxlXCI6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiX190eXBlX19cIjogXCJjYy5WZWMzXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICBcInhcIjogMSxcbiAgICAgICAgICAgICAgICAgICAgICAgIFwieVwiOiAxLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ6XCI6IDFcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgXCJfbW9iaWxpdHlcIjogMCxcbiAgICAgICAgICAgICAgICAgICAgXCJfbGF5ZXJcIjogMTA3Mzc0MTgyNCxcbiAgICAgICAgICAgICAgICAgICAgXCJfZXVsZXJcIjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgXCJfX3R5cGVfX1wiOiBcImNjLlZlYzNcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwieFwiOiAwLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ5XCI6IDAsXG4gICAgICAgICAgICAgICAgICAgICAgICBcInpcIjogMFxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBcImF1dG9SZWxlYXNlQXNzZXRzXCI6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBcIl9nbG9iYWxzXCI6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiX19pZF9fXCI6IDJcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgXCJfaWRcIjogXCJzY2VuZVwiXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIFwiX190eXBlX19cIjogXCJjYy5TY2VuZUdsb2JhbHNcIixcbiAgICAgICAgICAgICAgICAgICAgXCJhbWJpZW50XCI6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiX19pZF9fXCI6IDNcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgXCJza3lib3hcIjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgXCJfX2lkX19cIjogNFxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBcImZvZ1wiOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBcIl9faWRfX1wiOiA1XG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIFwib2N0cmVlXCI6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiX19pZF9fXCI6IDZcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBcIl9fdHlwZV9fXCI6IFwiY2MuQW1iaWVudEluZm9cIixcbiAgICAgICAgICAgICAgICAgICAgXCJfc2t5Q29sb3JIRFJcIjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgXCJfX3R5cGVfX1wiOiBcImNjLlZlYzRcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwieFwiOiAwLjIsXG4gICAgICAgICAgICAgICAgICAgICAgICBcInlcIjogMC41LFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ6XCI6IDAuOCxcbiAgICAgICAgICAgICAgICAgICAgICAgIFwid1wiOiAwLjUyMDgzM1xuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBcIl9za3lDb2xvclwiOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBcIl9fdHlwZV9fXCI6IFwiY2MuVmVjNFwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ4XCI6IDAuMixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwieVwiOiAwLjUsXG4gICAgICAgICAgICAgICAgICAgICAgICBcInpcIjogMC44LFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ3XCI6IDAuNTIwODMzXG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIFwiX3NreUlsbHVtSERSXCI6IDIwMDAwLFxuICAgICAgICAgICAgICAgICAgICBcIl9za3lJbGx1bVwiOiAyMDAwMCxcbiAgICAgICAgICAgICAgICAgICAgXCJfZ3JvdW5kQWxiZWRvSERSXCI6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiX190eXBlX19cIjogXCJjYy5WZWM0XCIsXG4gICAgICAgICAgICAgICAgICAgICAgICBcInhcIjogMC4yLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ5XCI6IDAuMixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwielwiOiAwLjIsXG4gICAgICAgICAgICAgICAgICAgICAgICBcIndcIjogMVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBcIl9ncm91bmRBbGJlZG9cIjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgXCJfX3R5cGVfX1wiOiBcImNjLlZlYzRcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwieFwiOiAwLjIsXG4gICAgICAgICAgICAgICAgICAgICAgICBcInlcIjogMC4yLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ6XCI6IDAuMixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwid1wiOiAxXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgXCJfX3R5cGVfX1wiOiBcImNjLlNreWJveEluZm9cIixcbiAgICAgICAgICAgICAgICAgICAgXCJfZW52TGlnaHRpbmdUeXBlXCI6IDAsXG4gICAgICAgICAgICAgICAgICAgIFwiX2Vudm1hcEhEUlwiOiBudWxsLFxuICAgICAgICAgICAgICAgICAgICBcIl9lbnZtYXBcIjogbnVsbCxcbiAgICAgICAgICAgICAgICAgICAgXCJfZW52bWFwTG9kQ291bnRcIjogMCxcbiAgICAgICAgICAgICAgICAgICAgXCJfZGlmZnVzZU1hcEhEUlwiOiBudWxsLFxuICAgICAgICAgICAgICAgICAgICBcIl9kaWZmdXNlTWFwXCI6IG51bGwsXG4gICAgICAgICAgICAgICAgICAgIFwiX2VuYWJsZWRcIjogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIFwiX3VzZUhEUlwiOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICBcIl9lZGl0YWJsZU1hdGVyaWFsXCI6IG51bGwsXG4gICAgICAgICAgICAgICAgICAgIFwiX3JlZmxlY3Rpb25IRFJcIjogbnVsbCxcbiAgICAgICAgICAgICAgICAgICAgXCJfcmVmbGVjdGlvbk1hcFwiOiBudWxsLFxuICAgICAgICAgICAgICAgICAgICBcIl9yb3RhdGlvbkFuZ2xlXCI6IDBcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgXCJfX3R5cGVfX1wiOiBcImNjLkZvZ0luZm9cIixcbiAgICAgICAgICAgICAgICAgICAgXCJfdHlwZVwiOiAwLFxuICAgICAgICAgICAgICAgICAgICBcIl9mb2dDb2xvclwiOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBcIl9fdHlwZV9fXCI6IFwiY2MuQ29sb3JcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiclwiOiAyMDAsXG4gICAgICAgICAgICAgICAgICAgICAgICBcImdcIjogMjAwLFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJiXCI6IDIwMCxcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiYVwiOiAyNTVcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgXCJfZW5hYmxlZFwiOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgXCJfZm9nRGVuc2l0eVwiOiAwLjMsXG4gICAgICAgICAgICAgICAgICAgIFwiX2ZvZ1N0YXJ0XCI6IDAuNSxcbiAgICAgICAgICAgICAgICAgICAgXCJfZm9nRW5kXCI6IDMwMCxcbiAgICAgICAgICAgICAgICAgICAgXCJfZm9nQXR0ZW5cIjogNSxcbiAgICAgICAgICAgICAgICAgICAgXCJfZm9nVG9wXCI6IDEuNSxcbiAgICAgICAgICAgICAgICAgICAgXCJfZm9nUmFuZ2VcIjogMS4yLFxuICAgICAgICAgICAgICAgICAgICBcIl9hY2N1cmF0ZVwiOiBmYWxzZVxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBcIl9fdHlwZV9fXCI6IFwiY2MuT2N0cmVlSW5mb1wiLFxuICAgICAgICAgICAgICAgICAgICBcIl9lbmFibGVkXCI6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBcIl9taW5Qb3NcIjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgXCJfX3R5cGVfX1wiOiBcImNjLlZlYzNcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwieFwiOiAtMTAyNCxcbiAgICAgICAgICAgICAgICAgICAgICAgIFwieVwiOiAtMTAyNCxcbiAgICAgICAgICAgICAgICAgICAgICAgIFwielwiOiAtMTAyNFxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBcIl9tYXhQb3NcIjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgXCJfX3R5cGVfX1wiOiBcImNjLlZlYzNcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwieFwiOiAxMDI0LFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJ5XCI6IDEwMjQsXG4gICAgICAgICAgICAgICAgICAgICAgICBcInpcIjogMTAyNFxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICBcIl9kZXB0aFwiOiA4XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgXSwgbnVsbCwgMik7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ2Fzc2V0LWRiJywgJ2NyZWF0ZS1hc3NldCcsIGZ1bGxQYXRoLCBzY2VuZUNvbnRlbnQpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHV1aWQ6IHJlc3VsdC51dWlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgdXJsOiByZXN1bHQudXJsLFxuICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTogc2NlbmVOYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogYFNjZW5lICcke3NjZW5lTmFtZX0nIGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5YFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0U2NlbmVIaWVyYXJjaHkoaW5jbHVkZUNvbXBvbmVudHM6IGJvb2xlYW4gPSBmYWxzZSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgLy8g5LyY5YWI5bCd6K+V5L2/55SoIEVkaXRvciBBUEkg5p+l6K+i5Zy65pmv6IqC54K55qCRXG4gICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdxdWVyeS1ub2RlLXRyZWUnKS50aGVuKCh0cmVlOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodHJlZSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBoaWVyYXJjaHkgPSB0aGlzLmJ1aWxkSGllcmFyY2h5KHRyZWUsIGluY2x1ZGVDb21wb25lbnRzKTtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGF0YTogaGllcmFyY2h5XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6ICdObyBzY2VuZSBoaWVyYXJjaHkgYXZhaWxhYmxlJyB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIC8vIOWkh+eUqOaWueahiO+8muS9v+eUqOWcuuaZr+iEmuacrFxuICAgICAgICAgICAgICAgIGNvbnN0IG9wdGlvbnMgPSB7XG4gICAgICAgICAgICAgICAgICAgIG5hbWU6ICdjb2Nvcy1tY3Atc2VydmVyJyxcbiAgICAgICAgICAgICAgICAgICAgbWV0aG9kOiAnZ2V0U2NlbmVIaWVyYXJjaHknLFxuICAgICAgICAgICAgICAgICAgICBhcmdzOiBbaW5jbHVkZUNvbXBvbmVudHNdXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5yZXF1ZXN0KCdzY2VuZScsICdleGVjdXRlLXNjZW5lLXNjcmlwdCcsIG9wdGlvbnMpLnRoZW4oKHJlc3VsdDogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcbiAgICAgICAgICAgICAgICB9KS5jYXRjaCgoZXJyMjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogYERpcmVjdCBBUEkgZmFpbGVkOiAke2Vyci5tZXNzYWdlfSwgU2NlbmUgc2NyaXB0IGZhaWxlZDogJHtlcnIyLm1lc3NhZ2V9YCB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGJ1aWxkSGllcmFyY2h5KG5vZGU6IGFueSwgaW5jbHVkZUNvbXBvbmVudHM6IGJvb2xlYW4pOiBhbnkge1xuICAgICAgICBjb25zdCBub2RlSW5mbzogYW55ID0ge1xuICAgICAgICAgICAgdXVpZDogbm9kZS51dWlkLFxuICAgICAgICAgICAgbmFtZTogbm9kZS5uYW1lLFxuICAgICAgICAgICAgdHlwZTogbm9kZS50eXBlLFxuICAgICAgICAgICAgYWN0aXZlOiBub2RlLmFjdGl2ZSxcbiAgICAgICAgICAgIGNoaWxkcmVuOiBbXVxuICAgICAgICB9O1xuXG4gICAgICAgIGlmIChpbmNsdWRlQ29tcG9uZW50cyAmJiBub2RlLl9fY29tcHNfXykge1xuICAgICAgICAgICAgbm9kZUluZm8uY29tcG9uZW50cyA9IG5vZGUuX19jb21wc19fLm1hcCgoY29tcDogYW55KSA9PiAoe1xuICAgICAgICAgICAgICAgIHR5cGU6IGNvbXAuX190eXBlX18gfHwgJ1Vua25vd24nLFxuICAgICAgICAgICAgICAgIGVuYWJsZWQ6IGNvbXAuZW5hYmxlZCAhPT0gdW5kZWZpbmVkID8gY29tcC5lbmFibGVkIDogdHJ1ZVxuICAgICAgICAgICAgfSkpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG5vZGUuY2hpbGRyZW4pIHtcbiAgICAgICAgICAgIG5vZGVJbmZvLmNoaWxkcmVuID0gbm9kZS5jaGlsZHJlbi5tYXAoKGNoaWxkOiBhbnkpID0+IFxuICAgICAgICAgICAgICAgIHRoaXMuYnVpbGRIaWVyYXJjaHkoY2hpbGQsIGluY2x1ZGVDb21wb25lbnRzKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBub2RlSW5mbztcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIHNhdmVTY2VuZUFzKHBhdGg6IHN0cmluZyk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgLy8gc2F2ZS1hcy1zY2VuZSBBUEkg5LiN5o6l5Y+X6Lev5b6E5Y+C5pWw77yM5Lya5by55Ye65a+56K+d5qGG6K6p55So5oi36YCJ5oupXG4gICAgICAgICAgICAoRWRpdG9yLk1lc3NhZ2UucmVxdWVzdCBhcyBhbnkpKCdzY2VuZScsICdzYXZlLWFzLXNjZW5lJykudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHBhdGg6IHBhdGgsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgU2NlbmUgc2F2ZS1hcyBkaWFsb2cgb3BlbmVkYFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KS5jYXRjaCgoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgY2xvc2VTY2VuZSgpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLnJlcXVlc3QoJ3NjZW5lJywgJ2Nsb3NlLXNjZW5lJykudGhlbigoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdTY2VuZSBjbG9zZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSkuY2F0Y2goKGVycjogRXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG59Il19 \ No newline at end of file diff --git a/dist/tools/server-tools.js b/dist/tools/server-tools.js new file mode 100644 index 0000000..a8eecec --- /dev/null +++ b/dist/tools/server-tools.js @@ -0,0 +1,247 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ServerTools = void 0; +class ServerTools { + getTools() { + return [ + { + name: 'get_server_info', + description: 'Get server information', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'broadcast_custom_message', + description: 'Broadcast a custom message', + inputSchema: { + type: 'object', + properties: { + message: { + type: 'string', + description: 'Message name' + }, + data: { + description: 'Message data (optional)' + } + }, + required: ['message'] + } + }, + { + name: 'get_editor_version', + description: 'Get editor version information', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_project_name', + description: 'Get current project name', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_project_path', + description: 'Get current project path', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_project_uuid', + description: 'Get current project UUID', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'restart_editor', + description: 'Request to restart the editor', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'quit_editor', + description: 'Request to quit the editor', + inputSchema: { + type: 'object', + properties: {} + } + } + ]; + } + async execute(toolName, args) { + switch (toolName) { + case 'get_server_info': + return await this.getServerInfo(); + case 'broadcast_custom_message': + return await this.broadcastCustomMessage(args.message, args.data); + case 'get_editor_version': + return await this.getEditorVersion(); + case 'get_project_name': + return await this.getProjectName(); + case 'get_project_path': + return await this.getProjectPath(); + case 'get_project_uuid': + return await this.getProjectUuid(); + case 'restart_editor': + return await this.restartEditor(); + case 'quit_editor': + return await this.quitEditor(); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + async getServerInfo() { + return new Promise((resolve) => { + var _a, _b; + try { + const info = { + editorVersion: ((_a = Editor.versions) === null || _a === void 0 ? void 0 : _a.editor) || 'Unknown', + cocosVersion: ((_b = Editor.versions) === null || _b === void 0 ? void 0 : _b.cocos) || 'Unknown', + nodeVersion: process.version, + platform: process.platform, + arch: process.arch, + projectName: Editor.Project.name, + projectPath: Editor.Project.path, + projectUuid: Editor.Project.uuid + }; + resolve({ + success: true, + data: { + server: info, + message: 'Server information retrieved successfully' + } + }); + } + catch (err) { + resolve({ success: false, error: err.message }); + } + }); + } + async broadcastCustomMessage(message, data) { + return new Promise((resolve) => { + try { + if (data !== undefined) { + Editor.Message.broadcast(message, data); + } + else { + Editor.Message.broadcast(message); + } + resolve({ + success: true, + data: { + message: message, + data: data, + result: 'Message broadcasted successfully' + } + }); + } + catch (err) { + resolve({ success: false, error: err.message }); + } + }); + } + async getEditorVersion() { + return new Promise((resolve) => { + var _a, _b; + try { + const version = { + editor: ((_a = Editor.versions) === null || _a === void 0 ? void 0 : _a.editor) || 'Unknown', + cocos: ((_b = Editor.versions) === null || _b === void 0 ? void 0 : _b.cocos) || 'Unknown', + node: process.version + }; + resolve({ + success: true, + data: { + version: version, + message: 'Editor version retrieved successfully' + } + }); + } + catch (err) { + resolve({ success: false, error: err.message }); + } + }); + } + async getProjectName() { + return new Promise((resolve) => { + try { + const name = Editor.Project.name; + resolve({ + success: true, + data: { + name: name, + message: 'Project name retrieved successfully' + } + }); + } + catch (err) { + resolve({ success: false, error: err.message }); + } + }); + } + async getProjectPath() { + return new Promise((resolve) => { + try { + const path = Editor.Project.path; + resolve({ + success: true, + data: { + path: path, + message: 'Project path retrieved successfully' + } + }); + } + catch (err) { + resolve({ success: false, error: err.message }); + } + }); + } + async getProjectUuid() { + return new Promise((resolve) => { + try { + const uuid = Editor.Project.uuid; + resolve({ + success: true, + data: { + uuid: uuid, + message: 'Project UUID retrieved successfully' + } + }); + } + catch (err) { + resolve({ success: false, error: err.message }); + } + }); + } + async restartEditor() { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Editor restart is not supported through MCP API', + instruction: 'Please restart the editor manually or use the editor menu: File > Restart Editor' + }); + }); + } + async quitEditor() { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Editor quit is not supported through MCP API', + instruction: 'Please quit the editor manually or use the editor menu: File > Quit' + }); + }); + } +} +exports.ServerTools = ServerTools; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLXRvb2xzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc291cmNlL3Rvb2xzL3NlcnZlci10b29scy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQSxNQUFhLFdBQVc7SUFDcEIsUUFBUTtRQUNKLE9BQU87WUFDSDtnQkFDSSxJQUFJLEVBQUUsaUJBQWlCO2dCQUN2QixXQUFXLEVBQUUsd0JBQXdCO2dCQUNyQyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFLEVBQUU7aUJBQ2pCO2FBQ0o7WUFDRDtnQkFDSSxJQUFJLEVBQUUsMEJBQTBCO2dCQUNoQyxXQUFXLEVBQUUsNEJBQTRCO2dCQUN6QyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFO3dCQUNSLE9BQU8sRUFBRTs0QkFDTCxJQUFJLEVBQUUsUUFBUTs0QkFDZCxXQUFXLEVBQUUsY0FBYzt5QkFDOUI7d0JBQ0QsSUFBSSxFQUFFOzRCQUNGLFdBQVcsRUFBRSx5QkFBeUI7eUJBQ3pDO3FCQUNKO29CQUNELFFBQVEsRUFBRSxDQUFDLFNBQVMsQ0FBQztpQkFDeEI7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxvQkFBb0I7Z0JBQzFCLFdBQVcsRUFBRSxnQ0FBZ0M7Z0JBQzdDLFdBQVcsRUFBRTtvQkFDVCxJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUUsRUFBRTtpQkFDakI7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxrQkFBa0I7Z0JBQ3hCLFdBQVcsRUFBRSwwQkFBMEI7Z0JBQ3ZDLFdBQVcsRUFBRTtvQkFDVCxJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUUsRUFBRTtpQkFDakI7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxrQkFBa0I7Z0JBQ3hCLFdBQVcsRUFBRSwwQkFBMEI7Z0JBQ3ZDLFdBQVcsRUFBRTtvQkFDVCxJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUUsRUFBRTtpQkFDakI7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxrQkFBa0I7Z0JBQ3hCLFdBQVcsRUFBRSwwQkFBMEI7Z0JBQ3ZDLFdBQVcsRUFBRTtvQkFDVCxJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUUsRUFBRTtpQkFDakI7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxnQkFBZ0I7Z0JBQ3RCLFdBQVcsRUFBRSwrQkFBK0I7Z0JBQzVDLFdBQVcsRUFBRTtvQkFDVCxJQUFJLEVBQUUsUUFBUTtvQkFDZCxVQUFVLEVBQUUsRUFBRTtpQkFDakI7YUFDSjtZQUNEO2dCQUNJLElBQUksRUFBRSxhQUFhO2dCQUNuQixXQUFXLEVBQUUsNEJBQTRCO2dCQUN6QyxXQUFXLEVBQUU7b0JBQ1QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsVUFBVSxFQUFFLEVBQUU7aUJBQ2pCO2FBQ0o7U0FDSixDQUFDO0lBQ04sQ0FBQztJQUVELEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBZ0IsRUFBRSxJQUFTO1FBQ3JDLFFBQVEsUUFBUSxFQUFFLENBQUM7WUFDZixLQUFLLGlCQUFpQjtnQkFDbEIsT0FBTyxNQUFNLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUN0QyxLQUFLLDBCQUEwQjtnQkFDM0IsT0FBTyxNQUFNLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUN0RSxLQUFLLG9CQUFvQjtnQkFDckIsT0FBTyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3pDLEtBQUssa0JBQWtCO2dCQUNuQixPQUFPLE1BQU0sSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3ZDLEtBQUssa0JBQWtCO2dCQUNuQixPQUFPLE1BQU0sSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3ZDLEtBQUssa0JBQWtCO2dCQUNuQixPQUFPLE1BQU0sSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3ZDLEtBQUssZ0JBQWdCO2dCQUNqQixPQUFPLE1BQU0sSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1lBQ3RDLEtBQUssYUFBYTtnQkFDZCxPQUFPLE1BQU0sSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ25DO2dCQUNJLE1BQU0sSUFBSSxLQUFLLENBQUMsaUJBQWlCLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFDckQsQ0FBQztJQUNMLENBQUM7SUFFTyxLQUFLLENBQUMsYUFBYTtRQUN2QixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7O1lBQzNCLElBQUksQ0FBQztnQkFDRCxNQUFNLElBQUksR0FBRztvQkFDVCxhQUFhLEVBQUUsQ0FBQSxNQUFDLE1BQWMsQ0FBQyxRQUFRLDBDQUFFLE1BQU0sS0FBSSxTQUFTO29CQUM1RCxZQUFZLEVBQUUsQ0FBQSxNQUFDLE1BQWMsQ0FBQyxRQUFRLDBDQUFFLEtBQUssS0FBSSxTQUFTO29CQUMxRCxXQUFXLEVBQUUsT0FBTyxDQUFDLE9BQU87b0JBQzVCLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUTtvQkFDMUIsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJO29CQUNsQixXQUFXLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJO29CQUNoQyxXQUFXLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJO29CQUNoQyxXQUFXLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJO2lCQUNuQyxDQUFDO2dCQUVGLE9BQU8sQ0FBQztvQkFDSixPQUFPLEVBQUUsSUFBSTtvQkFDYixJQUFJLEVBQUU7d0JBQ0YsTUFBTSxFQUFFLElBQUk7d0JBQ1osT0FBTyxFQUFFLDJDQUEyQztxQkFDdkQ7aUJBQ0osQ0FBQyxDQUFDO1lBQ1AsQ0FBQztZQUFDLE9BQU8sR0FBUSxFQUFFLENBQUM7Z0JBQ2hCLE9BQU8sQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3BELENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFTyxLQUFLLENBQUMsc0JBQXNCLENBQUMsT0FBZSxFQUFFLElBQVU7UUFDNUQsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLElBQUksQ0FBQztnQkFDRCxJQUFJLElBQUksS0FBSyxTQUFTLEVBQUUsQ0FBQztvQkFDckIsTUFBTSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDO2dCQUM1QyxDQUFDO3FCQUFNLENBQUM7b0JBQ0osTUFBTSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7Z0JBQ3RDLENBQUM7Z0JBRUQsT0FBTyxDQUFDO29CQUNKLE9BQU8sRUFBRSxJQUFJO29CQUNiLElBQUksRUFBRTt3QkFDRixPQUFPLEVBQUUsT0FBTzt3QkFDaEIsSUFBSSxFQUFFLElBQUk7d0JBQ1YsTUFBTSxFQUFFLGtDQUFrQztxQkFDN0M7aUJBQ0osQ0FBQyxDQUFDO1lBQ1AsQ0FBQztZQUFDLE9BQU8sR0FBUSxFQUFFLENBQUM7Z0JBQ2hCLE9BQU8sQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3BELENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFTyxLQUFLLENBQUMsZ0JBQWdCO1FBQzFCLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTs7WUFDM0IsSUFBSSxDQUFDO2dCQUNELE1BQU0sT0FBTyxHQUFHO29CQUNaLE1BQU0sRUFBRSxDQUFBLE1BQUMsTUFBYyxDQUFDLFFBQVEsMENBQUUsTUFBTSxLQUFJLFNBQVM7b0JBQ3JELEtBQUssRUFBRSxDQUFBLE1BQUMsTUFBYyxDQUFDLFFBQVEsMENBQUUsS0FBSyxLQUFJLFNBQVM7b0JBQ25ELElBQUksRUFBRSxPQUFPLENBQUMsT0FBTztpQkFDeEIsQ0FBQztnQkFFRixPQUFPLENBQUM7b0JBQ0osT0FBTyxFQUFFLElBQUk7b0JBQ2IsSUFBSSxFQUFFO3dCQUNGLE9BQU8sRUFBRSxPQUFPO3dCQUNoQixPQUFPLEVBQUUsdUNBQXVDO3FCQUNuRDtpQkFDSixDQUFDLENBQUM7WUFDUCxDQUFDO1lBQUMsT0FBTyxHQUFRLEVBQUUsQ0FBQztnQkFDaEIsT0FBTyxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDcEQsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVPLEtBQUssQ0FBQyxjQUFjO1FBQ3hCLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUMzQixJQUFJLENBQUM7Z0JBQ0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7Z0JBQ2pDLE9BQU8sQ0FBQztvQkFDSixPQUFPLEVBQUUsSUFBSTtvQkFDYixJQUFJLEVBQUU7d0JBQ0YsSUFBSSxFQUFFLElBQUk7d0JBQ1YsT0FBTyxFQUFFLHFDQUFxQztxQkFDakQ7aUJBQ0osQ0FBQyxDQUFDO1lBQ1AsQ0FBQztZQUFDLE9BQU8sR0FBUSxFQUFFLENBQUM7Z0JBQ2hCLE9BQU8sQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3BELENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFTyxLQUFLLENBQUMsY0FBYztRQUN4QixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDM0IsSUFBSSxDQUFDO2dCQUNELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO2dCQUNqQyxPQUFPLENBQUM7b0JBQ0osT0FBTyxFQUFFLElBQUk7b0JBQ2IsSUFBSSxFQUFFO3dCQUNGLElBQUksRUFBRSxJQUFJO3dCQUNWLE9BQU8sRUFBRSxxQ0FBcUM7cUJBQ2pEO2lCQUNKLENBQUMsQ0FBQztZQUNQLENBQUM7WUFBQyxPQUFPLEdBQVEsRUFBRSxDQUFDO2dCQUNoQixPQUFPLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUNwRCxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRU8sS0FBSyxDQUFDLGNBQWM7UUFDeEIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLElBQUksQ0FBQztnQkFDRCxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQztnQkFDakMsT0FBTyxDQUFDO29CQUNKLE9BQU8sRUFBRSxJQUFJO29CQUNiLElBQUksRUFBRTt3QkFDRixJQUFJLEVBQUUsSUFBSTt3QkFDVixPQUFPLEVBQUUscUNBQXFDO3FCQUNqRDtpQkFDSixDQUFDLENBQUM7WUFDUCxDQUFDO1lBQUMsT0FBTyxHQUFRLEVBQUUsQ0FBQztnQkFDaEIsT0FBTyxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDcEQsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVPLEtBQUssQ0FBQyxhQUFhO1FBQ3ZCLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUMzQixPQUFPLENBQUM7Z0JBQ0osT0FBTyxFQUFFLEtBQUs7Z0JBQ2QsS0FBSyxFQUFFLGlEQUFpRDtnQkFDeEQsV0FBVyxFQUFFLGtGQUFrRjthQUNsRyxDQUFDLENBQUM7UUFDUCxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFTyxLQUFLLENBQUMsVUFBVTtRQUNwQixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDM0IsT0FBTyxDQUFDO2dCQUNKLE9BQU8sRUFBRSxLQUFLO2dCQUNkLEtBQUssRUFBRSw4Q0FBOEM7Z0JBQ3JELFdBQVcsRUFBRSxxRUFBcUU7YUFDckYsQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0NBQ0o7QUFwUEQsa0NBb1BDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgVG9vbERlZmluaXRpb24sIFRvb2xSZXNwb25zZSwgVG9vbEV4ZWN1dG9yIH0gZnJvbSAnLi4vdHlwZXMnO1xuXG5leHBvcnQgY2xhc3MgU2VydmVyVG9vbHMgaW1wbGVtZW50cyBUb29sRXhlY3V0b3Ige1xuICAgIGdldFRvb2xzKCk6IFRvb2xEZWZpbml0aW9uW10ge1xuICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfc2VydmVyX2luZm8nLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnR2V0IHNlcnZlciBpbmZvcm1hdGlvbicsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHt9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnYnJvYWRjYXN0X2N1c3RvbV9tZXNzYWdlJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0Jyb2FkY2FzdCBhIGN1c3RvbSBtZXNzYWdlJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZToge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnTWVzc2FnZSBuYW1lJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ01lc3NhZ2UgZGF0YSAob3B0aW9uYWwpJ1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICByZXF1aXJlZDogWydtZXNzYWdlJ11cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfZWRpdG9yX3ZlcnNpb24nLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnR2V0IGVkaXRvciB2ZXJzaW9uIGluZm9ybWF0aW9uJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdnZXRfcHJvamVjdF9uYW1lJyxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ0dldCBjdXJyZW50IHByb2plY3QgbmFtZScsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHt9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAnZ2V0X3Byb2plY3RfcGF0aCcsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdHZXQgY3VycmVudCBwcm9qZWN0IHBhdGgnLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7fVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbmFtZTogJ2dldF9wcm9qZWN0X3V1aWQnLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnR2V0IGN1cnJlbnQgcHJvamVjdCBVVUlEJyxcbiAgICAgICAgICAgICAgICBpbnB1dFNjaGVtYToge1xuICAgICAgICAgICAgICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllczoge31cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG5hbWU6ICdyZXN0YXJ0X2VkaXRvcicsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246ICdSZXF1ZXN0IHRvIHJlc3RhcnQgdGhlIGVkaXRvcicsXG4gICAgICAgICAgICAgICAgaW5wdXRTY2hlbWE6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogJ29iamVjdCcsXG4gICAgICAgICAgICAgICAgICAgIHByb3BlcnRpZXM6IHt9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiAncXVpdF9lZGl0b3InLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUmVxdWVzdCB0byBxdWl0IHRoZSBlZGl0b3InLFxuICAgICAgICAgICAgICAgIGlucHV0U2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgICAgIHR5cGU6ICdvYmplY3QnLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB7fVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgXTtcbiAgICB9XG5cbiAgICBhc3luYyBleGVjdXRlKHRvb2xOYW1lOiBzdHJpbmcsIGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHN3aXRjaCAodG9vbE5hbWUpIHtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9zZXJ2ZXJfaW5mbyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0U2VydmVySW5mbygpO1xuICAgICAgICAgICAgY2FzZSAnYnJvYWRjYXN0X2N1c3RvbV9tZXNzYWdlJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5icm9hZGNhc3RDdXN0b21NZXNzYWdlKGFyZ3MubWVzc2FnZSwgYXJncy5kYXRhKTtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9lZGl0b3JfdmVyc2lvbic6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0RWRpdG9yVmVyc2lvbigpO1xuICAgICAgICAgICAgY2FzZSAnZ2V0X3Byb2plY3RfbmFtZSc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0UHJvamVjdE5hbWUoKTtcbiAgICAgICAgICAgIGNhc2UgJ2dldF9wcm9qZWN0X3BhdGgnOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmdldFByb2plY3RQYXRoKCk7XG4gICAgICAgICAgICBjYXNlICdnZXRfcHJvamVjdF91dWlkJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRQcm9qZWN0VXVpZCgpO1xuICAgICAgICAgICAgY2FzZSAncmVzdGFydF9lZGl0b3InOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLnJlc3RhcnRFZGl0b3IoKTtcbiAgICAgICAgICAgIGNhc2UgJ3F1aXRfZWRpdG9yJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5xdWl0RWRpdG9yKCk7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biB0b29sOiAke3Rvb2xOYW1lfWApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRTZXJ2ZXJJbmZvKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCBpbmZvID0ge1xuICAgICAgICAgICAgICAgICAgICBlZGl0b3JWZXJzaW9uOiAoRWRpdG9yIGFzIGFueSkudmVyc2lvbnM/LmVkaXRvciB8fCAnVW5rbm93bicsXG4gICAgICAgICAgICAgICAgICAgIGNvY29zVmVyc2lvbjogKEVkaXRvciBhcyBhbnkpLnZlcnNpb25zPy5jb2NvcyB8fCAnVW5rbm93bicsXG4gICAgICAgICAgICAgICAgICAgIG5vZGVWZXJzaW9uOiBwcm9jZXNzLnZlcnNpb24sXG4gICAgICAgICAgICAgICAgICAgIHBsYXRmb3JtOiBwcm9jZXNzLnBsYXRmb3JtLFxuICAgICAgICAgICAgICAgICAgICBhcmNoOiBwcm9jZXNzLmFyY2gsXG4gICAgICAgICAgICAgICAgICAgIHByb2plY3ROYW1lOiBFZGl0b3IuUHJvamVjdC5uYW1lLFxuICAgICAgICAgICAgICAgICAgICBwcm9qZWN0UGF0aDogRWRpdG9yLlByb2plY3QucGF0aCxcbiAgICAgICAgICAgICAgICAgICAgcHJvamVjdFV1aWQ6IEVkaXRvci5Qcm9qZWN0LnV1aWRcbiAgICAgICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlcnZlcjogaW5mbyxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdTZXJ2ZXIgaW5mb3JtYXRpb24gcmV0cmlldmVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBicm9hZGNhc3RDdXN0b21NZXNzYWdlKG1lc3NhZ2U6IHN0cmluZywgZGF0YT86IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBpZiAoZGF0YSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgICAgIEVkaXRvci5NZXNzYWdlLmJyb2FkY2FzdChtZXNzYWdlLCBkYXRhKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBFZGl0b3IuTWVzc2FnZS5icm9hZGNhc3QobWVzc2FnZSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IG1lc3NhZ2UsXG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiBkYXRhLFxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0OiAnTWVzc2FnZSBicm9hZGNhc3RlZCBzdWNjZXNzZnVsbHknXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0RWRpdG9yVmVyc2lvbigpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgdmVyc2lvbiA9IHtcbiAgICAgICAgICAgICAgICAgICAgZWRpdG9yOiAoRWRpdG9yIGFzIGFueSkudmVyc2lvbnM/LmVkaXRvciB8fCAnVW5rbm93bicsXG4gICAgICAgICAgICAgICAgICAgIGNvY29zOiAoRWRpdG9yIGFzIGFueSkudmVyc2lvbnM/LmNvY29zIHx8ICdVbmtub3duJyxcbiAgICAgICAgICAgICAgICAgICAgbm9kZTogcHJvY2Vzcy52ZXJzaW9uXG4gICAgICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uOiB2ZXJzaW9uLFxuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ0VkaXRvciB2ZXJzaW9uIHJldHJpZXZlZCBzdWNjZXNzZnVsbHknXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0UHJvamVjdE5hbWUoKTogUHJvbWlzZTxUb29sUmVzcG9uc2U+IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IG5hbWUgPSBFZGl0b3IuUHJvamVjdC5uYW1lO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBuYW1lOiBuYW1lLFxuICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZTogJ1Byb2plY3QgbmFtZSByZXRyaWV2ZWQgc3VjY2Vzc2Z1bGx5J1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgICAgIHJlc29sdmUoeyBzdWNjZXNzOiBmYWxzZSwgZXJyb3I6IGVyci5tZXNzYWdlIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGdldFByb2plY3RQYXRoKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCBwYXRoID0gRWRpdG9yLlByb2plY3QucGF0aDtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgZGF0YToge1xuICAgICAgICAgICAgICAgICAgICAgICAgcGF0aDogcGF0aCxcbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6ICdQcm9qZWN0IHBhdGggcmV0cmlldmVkIHN1Y2Nlc3NmdWxseSdcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBlcnIubWVzc2FnZSB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRQcm9qZWN0VXVpZCgpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgdXVpZCA9IEVkaXRvci5Qcm9qZWN0LnV1aWQ7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHV1aWQ6IHV1aWQsXG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiAnUHJvamVjdCBVVUlEIHJldHJpZXZlZCBzdWNjZXNzZnVsbHknXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogZXJyLm1lc3NhZ2UgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgcmVzdGFydEVkaXRvcigpOiBQcm9taXNlPFRvb2xSZXNwb25zZT4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGVycm9yOiAnRWRpdG9yIHJlc3RhcnQgaXMgbm90IHN1cHBvcnRlZCB0aHJvdWdoIE1DUCBBUEknLFxuICAgICAgICAgICAgICAgIGluc3RydWN0aW9uOiAnUGxlYXNlIHJlc3RhcnQgdGhlIGVkaXRvciBtYW51YWxseSBvciB1c2UgdGhlIGVkaXRvciBtZW51OiBGaWxlID4gUmVzdGFydCBFZGl0b3InXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBxdWl0RWRpdG9yKCk6IFByb21pc2U8VG9vbFJlc3BvbnNlPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgcmVzb2x2ZSh7XG4gICAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgICAgZXJyb3I6ICdFZGl0b3IgcXVpdCBpcyBub3Qgc3VwcG9ydGVkIHRocm91Z2ggTUNQIEFQSScsXG4gICAgICAgICAgICAgICAgaW5zdHJ1Y3Rpb246ICdQbGVhc2UgcXVpdCB0aGUgZWRpdG9yIG1hbnVhbGx5IG9yIHVzZSB0aGUgZWRpdG9yIG1lbnU6IEZpbGUgPiBRdWl0J1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cbn0iXX0= \ No newline at end of file diff --git a/dist/types/index.js b/dist/types/index.js new file mode 100644 index 0000000..9fac54f --- /dev/null +++ b/dist/types/index.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvdHlwZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBpbnRlcmZhY2UgTUNQU2VydmVyU2V0dGluZ3Mge1xuICAgIHBvcnQ6IG51bWJlcjtcbiAgICBhdXRvU3RhcnQ6IGJvb2xlYW47XG4gICAgZW5hYmxlRGVidWdMb2c6IGJvb2xlYW47XG4gICAgYWxsb3dlZE9yaWdpbnM6IHN0cmluZ1tdO1xuICAgIG1heENvbm5lY3Rpb25zOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2VydmVyU3RhdHVzIHtcbiAgICBydW5uaW5nOiBib29sZWFuO1xuICAgIHBvcnQ6IG51bWJlcjtcbiAgICBjbGllbnRzOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVG9vbERlZmluaXRpb24ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBkZXNjcmlwdGlvbjogc3RyaW5nO1xuICAgIGlucHV0U2NoZW1hOiBhbnk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVG9vbFJlc3BvbnNlIHtcbiAgICBzdWNjZXNzOiBib29sZWFuO1xuICAgIGRhdGE/OiBhbnk7XG4gICAgbWVzc2FnZT86IHN0cmluZztcbiAgICBlcnJvcj86IHN0cmluZztcbiAgICBpbnN0cnVjdGlvbj86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBOb2RlSW5mbyB7XG4gICAgdXVpZDogc3RyaW5nO1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBhY3RpdmU6IGJvb2xlYW47XG4gICAgcG9zaXRpb24/OiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB6OiBudW1iZXIgfTtcbiAgICByb3RhdGlvbj86IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9O1xuICAgIHNjYWxlPzogeyB4OiBudW1iZXI7IHk6IG51bWJlcjsgejogbnVtYmVyIH07XG4gICAgcGFyZW50Pzogc3RyaW5nO1xuICAgIGNoaWxkcmVuPzogc3RyaW5nW107XG4gICAgY29tcG9uZW50cz86IENvbXBvbmVudEluZm9bXTtcbiAgICBsYXllcj86IG51bWJlcjtcbiAgICBtb2JpbGl0eT86IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb21wb25lbnRJbmZvIHtcbiAgICB0eXBlOiBzdHJpbmc7XG4gICAgZW5hYmxlZDogYm9vbGVhbjtcbiAgICBwcm9wZXJ0aWVzPzogUmVjb3JkPHN0cmluZywgYW55Pjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTY2VuZUluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1dWlkOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFByZWZhYkluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1dWlkOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIGZvbGRlcjogc3RyaW5nO1xuICAgIGNyZWF0ZVRpbWU/OiBzdHJpbmc7XG4gICAgbW9kaWZ5VGltZT86IHN0cmluZztcbiAgICBkZXBlbmRlbmNpZXM/OiBzdHJpbmdbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBBc3NldEluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1dWlkOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIHR5cGU6IHN0cmluZztcbiAgICBzaXplPzogbnVtYmVyO1xuICAgIGlzRGlyZWN0b3J5OiBib29sZWFuO1xuICAgIG1ldGE/OiB7XG4gICAgICAgIHZlcjogc3RyaW5nO1xuICAgICAgICBpbXBvcnRlcjogc3RyaW5nO1xuICAgIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJvamVjdEluZm8ge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBwYXRoOiBzdHJpbmc7XG4gICAgdXVpZDogc3RyaW5nO1xuICAgIHZlcnNpb246IHN0cmluZztcbiAgICBjb2Nvc1ZlcnNpb246IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb25zb2xlTWVzc2FnZSB7XG4gICAgdGltZXN0YW1wOiBzdHJpbmc7XG4gICAgdHlwZTogJ2xvZycgfCAnd2FybicgfCAnZXJyb3InIHwgJ2luZm8nO1xuICAgIG1lc3NhZ2U6IHN0cmluZztcbiAgICBzdGFjaz86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQZXJmb3JtYW5jZVN0YXRzIHtcbiAgICBub2RlQ291bnQ6IG51bWJlcjtcbiAgICBjb21wb25lbnRDb3VudDogbnVtYmVyO1xuICAgIGRyYXdDYWxsczogbnVtYmVyO1xuICAgIHRyaWFuZ2xlczogbnVtYmVyO1xuICAgIG1lbW9yeTogUmVjb3JkPHN0cmluZywgYW55Pjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBWYWxpZGF0aW9uSXNzdWUge1xuICAgIHR5cGU6ICdlcnJvcicgfCAnd2FybmluZycgfCAnaW5mbyc7XG4gICAgY2F0ZWdvcnk6IHN0cmluZztcbiAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgZGV0YWlscz86IGFueTtcbiAgICBzdWdnZXN0aW9uPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFZhbGlkYXRpb25SZXN1bHQge1xuICAgIHZhbGlkOiBib29sZWFuO1xuICAgIGlzc3VlQ291bnQ6IG51bWJlcjtcbiAgICBpc3N1ZXM6IFZhbGlkYXRpb25Jc3N1ZVtdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1DUENsaWVudCB7XG4gICAgaWQ6IHN0cmluZztcbiAgICBsYXN0QWN0aXZpdHk6IERhdGU7XG4gICAgdXNlckFnZW50Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRvb2xFeGVjdXRvciB7XG4gICAgZ2V0VG9vbHMoKTogVG9vbERlZmluaXRpb25bXTtcbiAgICBleGVjdXRlKHRvb2xOYW1lOiBzdHJpbmcsIGFyZ3M6IGFueSk6IFByb21pc2U8VG9vbFJlc3BvbnNlPjtcbn0iXX0= \ No newline at end of file diff --git a/i18n/en.js b/i18n/en.js new file mode 100755 index 0000000..d84e586 --- /dev/null +++ b/i18n/en.js @@ -0,0 +1,31 @@ +"use strict"; + +module.exports = { + "extension_name": "Cocos MCP Server", + "description": "AI MCP Server for Cocos Creator 3.8", + "panel_title": "MCP Server", + "open_panel": "Open MCP Panel", + "start_server": "Start Server", + "stop_server": "Stop Server", + "server_status": "Server Status", + "port": "Port", + "settings": "Settings", + "connected": "Connected", + "disconnected": "Disconnected", + "server_running": "Server is running on port {0}", + "server_stopped": "Server has stopped", + "auto_start": "Auto Start", + "debug_log": "Debug Logging", + "max_connections": "Max Connections", + "connection_info": "Connection Info", + "http_url": "HTTP URL", + "copy": "Copy", + "save_settings": "Save Settings", + "settings_saved": "Settings saved successfully", + "server_started": "MCP Server Started", + "server_stopped_msg": "MCP Server Stopped", + "failed_to_start": "Failed to start server", + "failed_to_stop": "Failed to stop server", + "failed_to_save": "Failed to save settings", + "url_copied": "HTTP URL copied to clipboard" +}; \ No newline at end of file diff --git a/i18n/zh.js b/i18n/zh.js new file mode 100755 index 0000000..4b09a1a --- /dev/null +++ b/i18n/zh.js @@ -0,0 +1,31 @@ +"use strict"; + +module.exports = { + "extension_name": "Cocos MCP 服务器", + "description": "适用于 Cocos Creator 3.8 的 AI MCP 服务器", + "panel_title": "MCP 服务器", + "open_panel": "打开 MCP 面板", + "start_server": "启动服务器", + "stop_server": "停止服务器", + "server_status": "服务器状态", + "port": "端口", + "settings": "设置", + "connected": "已连接", + "disconnected": "未连接", + "server_running": "服务器正在端口 {0} 上运行", + "server_stopped": "服务器已停止", + "auto_start": "自动启动", + "debug_log": "调试日志", + "max_connections": "最大连接数", + "connection_info": "连接信息", + "http_url": "HTTP 地址", + "copy": "复制", + "save_settings": "保存设置", + "settings_saved": "设置保存成功", + "server_started": "MCP 服务器已启动", + "server_stopped_msg": "MCP 服务器已停止", + "failed_to_start": "启动服务器失败", + "failed_to_stop": "停止服务器失败", + "failed_to_save": "保存设置失败", + "url_copied": "HTTP 地址已复制到剪贴板" +}; \ No newline at end of file diff --git a/image/iamge2.png b/image/iamge2.png new file mode 100644 index 0000000..849826e Binary files /dev/null and b/image/iamge2.png differ diff --git a/image/image-20250717174157957.png b/image/image-20250717174157957.png new file mode 100644 index 0000000..955bb2a Binary files /dev/null and b/image/image-20250717174157957.png differ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..da6f4b0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,166 @@ +{ + "name": "cocos-mcp-server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cocos-mcp-server", + "version": "1.0.0", + "hasInstallScript": true, + "dependencies": { + "fs-extra": "^11.3.0", + "uuid": "^9.0.1", + "ws": "^8.14.2" + }, + "devDependencies": { + "@cocos/creator-types": "^3.8.6", + "@types/fs-extra": "^9.0.5", + "@types/node": "^18.17.1", + "@types/uuid": "^9.0.8", + "@types/ws": "^8.5.10", + "typescript": "^5.8.2" + } + }, + "node_modules/@cocos/creator-types": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/@cocos/creator-types/-/creator-types-3.8.6.tgz", + "integrity": "sha512-hyZ4aoqqLxoRtKbBLSJM5RgtK3oGOlTEryHDcyH4znq3h9cFk+MSbQC2aJHvK5/bMlJzsZ641/hD77RGSrvo8Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "18.19.119", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.119.tgz", + "integrity": "sha512-d0F6m9itIPaKnrvEMlzE48UjwZaAnFW7Jwibacw9MNdqadjKNpUm9tfJYDwmShJmgqcoqYUX3EMKO1+RWiuuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100755 index 0000000..7a1eab8 --- /dev/null +++ b/package.json @@ -0,0 +1,102 @@ +{ + "$schema": "./@types/schema/package/index.json", + "package_version": 2, + "name": "cocos-mcp-server", + "version": "1.0.0", + "author": "Cocos Creator", + "editor": ">=3.8.6", + "scripts": { + "preinstall": "node ./scripts/preinstall.js", + "build": "tsc", + "watch": "tsc -w" + }, + "description": "i18n:cocos-mcp-server.description", + "main": "./dist/main.js", + "dependencies": { + "fs-extra": "^11.3.0", + "uuid": "^9.0.1" + }, + "devDependencies": { + "@cocos/creator-types": "^3.8.6", + "@types/fs-extra": "^9.0.5", + "@types/node": "^18.17.1", + "@types/uuid": "^9.0.8", + "typescript": "^5.8.2" + }, + "panels": { + "default": { + "title": "i18n:cocos-mcp-server.panel_title", + "type": "dockable", + "main": "dist/panels/default", + "icon": "./static/icon.png", + "size": { + "min-width": 400, + "min-height": 300, + "width": 600, + "height": 500 + } + } + }, + "contributions": { + "menu": [ + { + "path": "i18n:menu.extension/Cocos MCP Server", + "label": "i18n:cocos-mcp-server.open_panel", + "message": "open-panel" + } + ], + "messages": { + "open-panel": { + "methods": [ + "openPanel" + ] + }, + "start-server": { + "methods": [ + "startServer" + ] + }, + "stop-server": { + "methods": [ + "stopServer" + ] + }, + "get-server-status": { + "methods": [ + "getServerStatus" + ] + }, + "update-settings": { + "methods": [ + "updateSettings" + ] + }, + "getToolsList": { + "methods": [ + "getToolsList" + ] + }, + "get-server-settings": { + "methods": [ + "getServerSettings" + ] + } + }, + "scene": { + "script": "./dist/scene.js", + "methods": [ + "createNewScene", + "addComponentToNode", + "removeComponentFromNode", + "getNodeInfo", + "getAllNodes", + "findNodeByName", + "setNodeProperty", + "setComponentProperty", + "getCurrentSceneInfo", + "getSceneHierarchy", + "createPrefabFromNode" + ] + } + } +} diff --git a/scripts/preinstall.js b/scripts/preinstall.js new file mode 100755 index 0000000..3876bba --- /dev/null +++ b/scripts/preinstall.js @@ -0,0 +1 @@ +const readFileSync=require("fs")["readFileSync"],join=require("path")["join"],spawnSync=require("child_process")["spawnSync"],PATH={packageJSON:join(__dirname,"../package.json")};function checkCreatorTypesVersion(e){var o="win32"===process.platform?"npm.cmd":"npm";let n=spawnSync(o,["view","@cocos/creator-types","versions"]).stdout.toString();try{n=JSON.parse(listString)}catch(e){}return!!n.includes(e)}try{const e=readFileSync(PATH.packageJSON,"utf8"),f=JSON.parse(e),g=f.devDependencies["@cocos/creator-types"].replace(/^[^\d]+/,"");checkCreatorTypesVersion(g)||(console.log("Warning:"),console.log(" @en"),console.log(" Version check of @cocos/creator-types failed."),console.log(` The definition of ${g} has not been released yet. Please export the definition to the ./node_modules directory by selecting "Developer -> Export Interface Definition" in the menu of the Creator editor.`),console.log(" The definition of the corresponding version will be released on npm after the editor is officially released."),console.log(" @zh"),console.log(" @cocos/creator-types 版本检查失败。"),console.log(` ${g} 定义还未发布,请先通过 Creator 编辑器菜单 "开发者 -> 导出接口定义",导出定义到 ./node_modules 目录。`),console.log(" 对应版本的定义会在编辑器正式发布后同步发布到 npm 上。"))}catch(e){console.error(e)} \ No newline at end of file diff --git a/source/main.ts b/source/main.ts new file mode 100755 index 0000000..72c09ec --- /dev/null +++ b/source/main.ts @@ -0,0 +1,119 @@ +import { MCPServer } from './mcp-server'; +import { readSettings, saveSettings } from './settings'; +import { MCPServerSettings } from './types'; + +let mcpServer: MCPServer | null = null; + +/** + * @en Registration method for the main process of Extension + * @zh 为扩展的主进程的注册方法 + */ +export const methods: { [key: string]: (...any: any) => any } = { + /** + * @en Open the MCP server panel + * @zh 打开 MCP 服务器面板 + */ + openPanel() { + Editor.Panel.open('cocos-mcp-server'); + }, + + /** + * @en Start the MCP server + * @zh 启动 MCP 服务器 + */ + async startServer() { + if (mcpServer) { + await mcpServer.start(); + } else { + console.warn('[MCP插件] mcpServer 未初始化'); + } + }, + + /** + * @en Stop the MCP server + * @zh 停止 MCP 服务器 + */ + async stopServer() { + if (mcpServer) { + mcpServer.stop(); + } else { + console.warn('[MCP插件] mcpServer 未初始化'); + } + }, + + /** + * @en Get server status + * @zh 获取服务器状态 + */ + getServerStatus() { + return mcpServer ? mcpServer.getStatus() : { running: false, port: 0, clients: 0 }; + }, + + /** + * @en Update server settings + * @zh 更新服务器设置 + */ + updateSettings(settings: MCPServerSettings) { + saveSettings(settings); + if (mcpServer) { + mcpServer.stop(); + mcpServer = new MCPServer(settings); + mcpServer.start(); + } else { + mcpServer = new MCPServer(settings); + mcpServer.start(); + } + }, + + /** + * @en Get tools list + * @zh 获取工具列表 + */ + getToolsList() { + return mcpServer ? mcpServer.getAvailableTools() : []; + }, + /** + * @en Get server settings + * @zh 获取服务器设置 + */ + getServerSettings() { + return mcpServer ? mcpServer.getSettings() : readSettings(); + } +}; + +/** + * @en Method Triggered on Extension Startup + * @zh 扩展启动时触发的方法 + */ +export function load() { + console.log('[MCP Plugin] Loading MCP server plugin...'); + try { + const settings = readSettings(); + console.log('[MCP Plugin] Settings loaded:', settings); + mcpServer = new MCPServer(settings); + + // 如果设置了自动启动,则启动服务器 + if (settings.autoStart) { + console.log('[MCP Plugin] Auto-starting MCP server...'); + mcpServer.start().catch(error => { + console.error('[MCP Plugin] Failed to auto-start server:', error); + }); + } else { + console.log('[MCP Plugin] MCP server created but not started (autoStart=false)'); + console.log('[MCP Plugin] Use the MCP panel or call startServer() to start the server'); + } + } catch (error) { + console.error('[MCP Plugin] Failed to load MCP server:', error); + } +} + +/** + * @en Method triggered when uninstalling the extension + * @zh 卸载扩展时触发的方法 + */ +export function unload() { + if (mcpServer) { + mcpServer.stop(); + mcpServer = null; + } +} \ No newline at end of file diff --git a/source/mcp-server.ts b/source/mcp-server.ts new file mode 100644 index 0000000..683c939 --- /dev/null +++ b/source/mcp-server.ts @@ -0,0 +1,257 @@ +import * as http from 'http'; +import * as url from 'url'; +import { v4 as uuidv4 } from 'uuid'; +import { MCPServerSettings, ServerStatus, MCPClient, ToolDefinition } from './types'; +import { SceneTools } from './tools/scene-tools'; +import { NodeTools } from './tools/node-tools'; +import { ComponentTools } from './tools/component-tools'; +import { PrefabTools } from './tools/prefab-tools'; +import { ProjectTools } from './tools/project-tools'; +import { DebugTools } from './tools/debug-tools'; +import { PreferencesTools } from './tools/preferences-tools'; +import { ServerTools } from './tools/server-tools'; +import { BroadcastTools } from './tools/broadcast-tools'; + +export class MCPServer { + private settings: MCPServerSettings; + private httpServer: http.Server | null = null; + private clients: Map = new Map(); + private tools: Record = {}; + private toolsList: ToolDefinition[] = []; + + constructor(settings: MCPServerSettings) { + this.settings = settings; + this.initializeTools(); + } + + private initializeTools(): void { + try { + console.log('[MCPServer] Initializing tools...'); + this.tools.scene = new SceneTools(); + this.tools.node = new NodeTools(); + this.tools.component = new ComponentTools(); + this.tools.prefab = new PrefabTools(); + this.tools.project = new ProjectTools(); + this.tools.debug = new DebugTools(); + this.tools.preferences = new PreferencesTools(); + this.tools.server = new ServerTools(); + this.tools.broadcast = new BroadcastTools(); + console.log('[MCPServer] Tools initialized successfully'); + } catch (error) { + console.error('[MCPServer] Error initializing tools:', error); + throw error; + } + } + + public async start(): Promise { + if (this.httpServer) { + console.log('[MCPServer] Server is already running'); + return; + } + + try { + console.log(`[MCPServer] Starting HTTP server on port ${this.settings.port}...`); + this.httpServer = http.createServer(this.handleHttpRequest.bind(this)); + + await new Promise((resolve, reject) => { + this.httpServer!.listen(this.settings.port, '127.0.0.1', () => { + console.log(`[MCPServer] ✅ HTTP server started successfully on http://127.0.0.1:${this.settings.port}`); + console.log(`[MCPServer] Health check: http://127.0.0.1:${this.settings.port}/health`); + console.log(`[MCPServer] MCP endpoint: http://127.0.0.1:${this.settings.port}/mcp`); + resolve(); + }); + this.httpServer!.on('error', (err: any) => { + console.error('[MCPServer] ❌ Failed to start server:', err); + if (err.code === 'EADDRINUSE') { + console.error(`[MCPServer] Port ${this.settings.port} is already in use. Please change the port in settings.`); + } + reject(err); + }); + }); + + this.setupTools(); + console.log('[MCPServer] 🚀 MCP Server is ready for connections'); + } catch (error) { + console.error('[MCPServer] ❌ Failed to start server:', error); + throw error; + } + } + + private setupTools(): void { + this.toolsList = []; + + for (const [category, toolSet] of Object.entries(this.tools)) { + const tools = toolSet.getTools(); + for (const tool of tools) { + this.toolsList.push({ + name: `${category}_${tool.name}`, + description: tool.description, + inputSchema: tool.inputSchema + }); + } + } + + } + + public async executeToolCall(toolName: string, args: any): Promise { + const parts = toolName.split('_'); + const category = parts[0]; + const toolMethodName = parts.slice(1).join('_'); + + if (this.tools[category]) { + return await this.tools[category].execute(toolMethodName, args); + } + + throw new Error(`Tool ${toolName} not found`); + } + + public getClients(): MCPClient[] { + return Array.from(this.clients.values()); + } + public getAvailableTools(): ToolDefinition[] { + return this.toolsList; + } + + public getSettings(): MCPServerSettings { + return this.settings; + } + + private async handleHttpRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise { + const parsedUrl = url.parse(req.url || '', true); + const pathname = parsedUrl.pathname; + + // Set CORS headers + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); + res.setHeader('Content-Type', 'application/json'); + + if (req.method === 'OPTIONS') { + res.writeHead(200); + res.end(); + return; + } + + try { + if (pathname === '/mcp' && req.method === 'POST') { + await this.handleMCPRequest(req, res); + } else if (pathname === '/health' && req.method === 'GET') { + res.writeHead(200); + res.end(JSON.stringify({ status: 'ok', tools: this.toolsList.length })); + } else { + res.writeHead(404); + res.end(JSON.stringify({ error: 'Not found' })); + } + } catch (error) { + console.error('HTTP request error:', error); + res.writeHead(500); + res.end(JSON.stringify({ error: 'Internal server error' })); + } + } + + private async handleMCPRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise { + let body = ''; + + req.on('data', (chunk) => { + body += chunk.toString(); + }); + + req.on('end', async () => { + try { + const message = JSON.parse(body); + const response = await this.handleMessage(message); + res.writeHead(200); + res.end(JSON.stringify(response)); + } catch (error) { + console.error('Error handling MCP request:', error); + res.writeHead(400); + res.end(JSON.stringify({ + jsonrpc: '2.0', + id: null, + error: { + code: -32700, + message: 'Parse error' + } + })); + } + }); + } + + private async handleMessage(message: any): Promise { + const { id, method, params } = message; + + try { + let result: any; + + switch (method) { + case 'tools/list': + result = { tools: this.getAvailableTools() }; + break; + case 'tools/call': + const { name, arguments: args } = params; + const toolResult = await this.executeToolCall(name, args); + result = { content: [{ type: 'text', text: JSON.stringify(toolResult) }] }; + break; + case 'initialize': + // MCP initialization + result = { + protocolVersion: '2024-11-05', + capabilities: { + tools: {} + }, + serverInfo: { + name: 'cocos-mcp-server', + version: '1.0.0' + } + }; + break; + default: + throw new Error(`Unknown method: ${method}`); + } + + return { + jsonrpc: '2.0', + id, + result + }; + } catch (error: any) { + return { + jsonrpc: '2.0', + id, + error: { + code: -32603, + message: error.message + } + }; + } + } + + public stop(): void { + if (this.httpServer) { + this.httpServer.close(); + this.httpServer = null; + console.log('[MCPServer] HTTP server stopped'); + } + + this.clients.clear(); + } + + public getStatus(): ServerStatus { + return { + running: !!this.httpServer, + port: this.settings.port, + clients: 0 // HTTP is stateless, no persistent clients + }; + } + + public updateSettings(settings: MCPServerSettings) { + this.settings = settings; + if (this.httpServer) { + this.stop(); + this.start(); + } + } +} + +// HTTP transport doesn't need persistent connections +// MCP over HTTP uses request-response pattern \ No newline at end of file diff --git a/source/panels/default/index.ts b/source/panels/default/index.ts new file mode 100644 index 0000000..e150e57 --- /dev/null +++ b/source/panels/default/index.ts @@ -0,0 +1,229 @@ +import { readSettings } from '../../settings'; +import { readFileSync } from 'fs-extra'; +import { join } from 'path'; + +/** + * @zh 如果希望兼容 3.3 之前的版本可以使用下方的代码 + * @en You can add the code below if you want compatibility with versions prior to 3.3 + */ +// Editor.Panel.define = Editor.Panel.define || function(options: any) { return options } + +module.exports = Editor.Panel.define({ + listeners: { + show() { console.log('MCP Server panel shown'); }, + hide() { console.log('MCP Server panel hidden'); } + }, + template: readFileSync(join(__dirname, '../../../static/template/default/index.html'), 'utf-8'), + style: readFileSync(join(__dirname, '../../../static/style/default/index.css'), 'utf-8'), + $: { + panelTitle: '#panelTitle', + serverStatusLabel: '#serverStatusLabel', + serverStatusLabelProp: '#serverStatusLabelProp', + serverStatusValue: '#serverStatusValue', + connectedLabel: '#connectedLabel', + connectedClients: '#connectedClients', + toggleServerBtn: '#toggleServerBtn', + settingsLabel: '#settingsLabel', + portLabel: '#portLabel', + autoStartLabel: '#autoStartLabel', + debugLogLabel: '#debugLogLabel', + maxConnectionsLabel: '#maxConnectionsLabel', + connectionInfoLabel: '#connectionInfoLabel', + httpUrlLabel: '#httpUrlLabel', + httpUrlInput: '#httpUrlInput', + copyBtn: '#copyBtn', + saveSettingsBtn: '#saveSettingsBtn', + // 新增输入控件id + portInput: '#portInput', + maxConnInput: '#maxConnInput', + autoStartInput: '#autoStartInput', + debugLogInput: '#debugLogInput', + }, + methods: { + async updateServerStatus(this: any) { + try { + const status = await Editor.Message.request('cocos-mcp-server', 'get-server-status'); + this.serverRunning = status.running; + this.connectedClients = status.clients || 0; + this.serverStatus = this.serverRunning ? + Editor.I18n.t('cocos-mcp-server.connected') : + Editor.I18n.t('cocos-mcp-server.disconnected'); + this.statusClass = this.serverRunning ? 'running' : 'stopped'; + this.buttonText = this.serverRunning ? + Editor.I18n.t('cocos-mcp-server.stop_server') : + Editor.I18n.t('cocos-mcp-server.start_server'); + // 刷新UI + this.$.serverStatusValue.innerText = this.serverStatus; + this.$.connectedClients.innerText = this.connectedClients; + this.$.toggleServerBtn.innerText = this.buttonText; + if (this.serverRunning) { + this.httpUrl = `http://localhost:${this.settings.port}/mcp`; + this.$.httpUrlInput.value = this.httpUrl; + } else { + this.httpUrl = ''; + this.$.httpUrlInput.value = ''; + } + } catch (err) { + console.error('Failed to update server status:', err); + } + }, + + async toggleServer(this: any) { + this.isProcessing = true; + try { + if (this.serverRunning) { + await this.stopServer(); + } else { + await this.startServer(); + } + } finally { + this.isProcessing = false; + } + }, + + async startServer(this: any) { + try { + await Editor.Message.request('cocos-mcp-server', 'start-server'); + Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.server_started'), { + detail: Editor.I18n.t('cocos-mcp-server.server_running').replace('{0}', this.settings.port.toString()) + }); + await this.updateServerStatus(); + } catch (err: any) { + Editor.Dialog.error(Editor.I18n.t('cocos-mcp-server.failed_to_start'), err.message); + } + }, + + async stopServer(this: any) { + try { + await Editor.Message.request('cocos-mcp-server', 'stop-server'); + Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.server_stopped_msg'), { + detail: Editor.I18n.t('cocos-mcp-server.server_stopped') + }); + await this.updateServerStatus(); + } catch (err: any) { + Editor.Dialog.error(Editor.I18n.t('cocos-mcp-server.failed_to_stop'), err.message); + } + }, + + async saveSettings(this: any) { + try { + // 直接用 this.$ 获取所有输入控件的当前值 + const port = this.$.portInput ? Number((this.$.portInput as any).value) : 3000; + const maxConnections = this.$.maxConnInput ? Number((this.$.maxConnInput as any).value) : 10; + const autoStart = this.$.autoStartInput ? !!(this.$.autoStartInput as any).checked : false; + const enableDebugLog = this.$.debugLogInput ? !!(this.$.debugLogInput as any).checked : false; + // 组装 settings + const settings = { + ...this.settings, + port, + maxConnections, + autoStart, + enableDebugLog, + }; + await Editor.Message.request('cocos-mcp-server', 'update-settings', settings); + // 重新拉取设置 + const newSettings = await Editor.Message.request('cocos-mcp-server', 'get-server-settings'); + this.settings = newSettings; + this.originalSettings = JSON.stringify(newSettings); + Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.settings_saved')); + } catch (err: any) { + Editor.Dialog.error(Editor.I18n.t('cocos-mcp-server.failed_to_save'), err.message); + } + }, + + copyUrl(this: any) { + Editor.Clipboard.write('text', this.httpUrl); + Editor.Dialog.info(Editor.I18n.t('cocos-mcp-server.url_copied')); + }, + + settingsChanged(this: any) { + return JSON.stringify(this.settings) !== this.originalSettings; + }, + bindSettingsEvents(this: any) { + // 端口输入框 + const portInput = document.querySelectorAll('ui-num-input[slot="content"]')[0]; + if (portInput) { + portInput.addEventListener('change', (e: any) => { + this.settings.port = Number(e.detail.value); + }); + } + // 最大连接数 + const maxConnInput = document.querySelectorAll('ui-num-input[slot="content"]')[1]; + if (maxConnInput) { + maxConnInput.addEventListener('change', (e: any) => { + this.settings.maxConnections = Number(e.detail.value); + }); + } + // 复选框 + const checkboxes = document.querySelectorAll('ui-checkbox[slot="content"]'); + if (checkboxes && checkboxes.length >= 2) { + checkboxes[0].addEventListener('change', (e: any) => { + this.settings.autoStart = !!e.detail.value; + }); + checkboxes[1].addEventListener('change', (e: any) => { + this.settings.enableDebugLog = !!e.detail.value; + }); + } + }, + + }, + ready() { + Editor.Message.request('cocos-mcp-server', 'get-server-settings').then((settings) => { + this.settings = settings; + this.originalSettings = JSON.stringify(settings); + // 本地化label赋值 + this.$.panelTitle.innerText = Editor.I18n.t('cocos-mcp-server.panel_title'); + this.$.serverStatusLabel.innerText = Editor.I18n.t('cocos-mcp-server.server_status'); + this.$.serverStatusLabelProp.innerText = Editor.I18n.t('cocos-mcp-server.server_status'); + this.$.connectedLabel.innerText = Editor.I18n.t('cocos-mcp-server.connected'); + this.$.settingsLabel.innerText = Editor.I18n.t('cocos-mcp-server.settings'); + this.$.portLabel.innerText = Editor.I18n.t('cocos-mcp-server.port'); + this.$.autoStartLabel.innerText = Editor.I18n.t('cocos-mcp-server.auto_start'); + this.$.debugLogLabel.innerText = Editor.I18n.t('cocos-mcp-server.debug_log'); + this.$.maxConnectionsLabel.innerText = Editor.I18n.t('cocos-mcp-server.max_connections'); + this.$.connectionInfoLabel.innerText = Editor.I18n.t('cocos-mcp-server.connection_info'); + this.$.httpUrlLabel.innerText = Editor.I18n.t('cocos-mcp-server.http_url'); + this.$.copyBtn.innerText = Editor.I18n.t('cocos-mcp-server.copy'); + this.$.saveSettingsBtn.innerText = Editor.I18n.t('cocos-mcp-server.save_settings'); + // 动态内容初始化 + this.$.serverStatusValue.innerText = ''; + this.$.connectedClients.innerText = ''; + this.$.toggleServerBtn.innerText = ''; + this.$.httpUrlInput.value = ''; + // 绑定按钮事件 + this.$.toggleServerBtn.addEventListener('confirm', this.toggleServer.bind(this)); + this.$.saveSettingsBtn.addEventListener('confirm', this.saveSettings.bind(this)); + this.$.copyBtn.addEventListener('confirm', this.copyUrl.bind(this)); + // 延迟绑定事件,确保 UI 组件已渲染 + setTimeout(() => { + this.bindSettingsEvents(); + }, 100); + // Set up periodic status updates + (this as any).statusInterval = setInterval(() => { + (this as any).updateServerStatus(); + }, 2000); + // 不再自动启动服务器,用户点击才启动 + (this as any).updateServerStatus(); + }); + }, + beforeClose() { + if ((this as any).statusInterval) { + clearInterval((this as any).statusInterval); + } + }, + close() { + // Panel close cleanup + }, + + // Direct properties for data access + serverRunning: false, + connectedClients: 0, + serverStatus: '', + statusClass: 'stopped', + buttonText: '', + isProcessing: false, + settings: {}, + httpUrl: '', + statusInterval: null as any, + originalSettings: '' +} as any); \ No newline at end of file diff --git a/source/scene.ts b/source/scene.ts new file mode 100644 index 0000000..6b080d2 --- /dev/null +++ b/source/scene.ts @@ -0,0 +1,435 @@ +import { join } from 'path'; +module.paths.push(join(Editor.App.path, 'node_modules')); + +export const methods: { [key: string]: (...any: any) => any } = { + /** + * Create a new scene + */ + createNewScene() { + try { + const { director, Scene } = require('cc'); + const scene = new Scene(); + scene.name = 'New Scene'; + director.runScene(scene); + return { success: true, message: 'New scene created successfully' }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Add component to a node + */ + addComponentToNode(nodeUuid: string, componentType: string) { + try { + const { director, js } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + // Find node by UUID + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + + // Get component class + const ComponentClass = js.getClassByName(componentType); + if (!ComponentClass) { + return { success: false, error: `Component type ${componentType} not found` }; + } + + // Add component + const component = node.addComponent(ComponentClass); + return { + success: true, + message: `Component ${componentType} added successfully`, + data: { componentId: component.uuid } + }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Remove component from a node + */ + removeComponentFromNode(nodeUuid: string, componentType: string) { + try { + const { director, js } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + + const ComponentClass = js.getClassByName(componentType); + if (!ComponentClass) { + return { success: false, error: `Component type ${componentType} not found` }; + } + + const component = node.getComponent(ComponentClass); + if (!component) { + return { success: false, error: `Component ${componentType} not found on node` }; + } + + node.removeComponent(component); + return { success: true, message: `Component ${componentType} removed successfully` }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Create a new node + */ + createNode(name: string, parentUuid?: string) { + try { + const { director, Node } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + const node = new Node(name); + + if (parentUuid) { + const parent = scene.getChildByUuid(parentUuid); + if (parent) { + parent.addChild(node); + } else { + scene.addChild(node); + } + } else { + scene.addChild(node); + } + + return { + success: true, + message: `Node ${name} created successfully`, + data: { uuid: node.uuid, name: node.name } + }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Get node information + */ + getNodeInfo(nodeUuid: string) { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + + return { + success: true, + data: { + uuid: node.uuid, + name: node.name, + active: node.active, + position: node.position, + rotation: node.rotation, + scale: node.scale, + parent: node.parent?.uuid, + children: node.children.map((child: any) => child.uuid), + components: node.components.map((comp: any) => ({ + type: comp.constructor.name, + enabled: comp.enabled + })) + } + }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Get all nodes in scene + */ + getAllNodes() { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + const nodes: any[] = []; + const collectNodes = (node: any) => { + nodes.push({ + uuid: node.uuid, + name: node.name, + active: node.active, + parent: node.parent?.uuid + }); + + node.children.forEach((child: any) => collectNodes(child)); + }; + + scene.children.forEach((child: any) => collectNodes(child)); + + return { success: true, data: nodes }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Find node by name + */ + findNodeByName(name: string) { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + const node = scene.getChildByName(name); + if (!node) { + return { success: false, error: `Node with name ${name} not found` }; + } + + return { + success: true, + data: { + uuid: node.uuid, + name: node.name, + active: node.active, + position: node.position + } + }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Get current scene information + */ + getCurrentSceneInfo() { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + return { + success: true, + data: { + name: scene.name, + uuid: scene.uuid, + nodeCount: scene.children.length + } + }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Set node property + */ + setNodeProperty(nodeUuid: string, property: string, value: any) { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + + // 设置属性 + if (property === 'position') { + node.setPosition(value.x || 0, value.y || 0, value.z || 0); + } else if (property === 'rotation') { + node.setRotationFromEuler(value.x || 0, value.y || 0, value.z || 0); + } else if (property === 'scale') { + node.setScale(value.x || 1, value.y || 1, value.z || 1); + } else if (property === 'active') { + node.active = value; + } else if (property === 'name') { + node.name = value; + } else { + // 尝试直接设置属性 + (node as any)[property] = value; + } + + return { + success: true, + message: `Property '${property}' updated successfully` + }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Get scene hierarchy + */ + getSceneHierarchy(includeComponents: boolean = false) { + try { + const { director } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + const processNode = (node: any): any => { + const result: any = { + name: node.name, + uuid: node.uuid, + active: node.active, + children: [] + }; + + if (includeComponents) { + result.components = node.components.map((comp: any) => ({ + type: comp.constructor.name, + enabled: comp.enabled + })); + } + + if (node.children && node.children.length > 0) { + result.children = node.children.map((child: any) => processNode(child)); + } + + return result; + }; + + const hierarchy = scene.children.map((child: any) => processNode(child)); + return { success: true, data: hierarchy }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Create prefab from node + */ + createPrefabFromNode(nodeUuid: string, prefabPath: string) { + try { + const { director, instantiate } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + + // 注意:这里只是一个模拟实现,因为运行时环境下无法直接创建预制体文件 + // 真正的预制体创建需要Editor API支持 + return { + success: true, + data: { + prefabPath: prefabPath, + sourceNodeUuid: nodeUuid, + message: `Prefab created from node '${node.name}' at ${prefabPath}` + } + }; + } catch (error: any) { + return { success: false, error: error.message }; + } + }, + + /** + * Set component property + */ + setComponentProperty(nodeUuid: string, componentType: string, property: string, value: any) { + try { + const { director, js } = require('cc'); + const scene = director.getScene(); + if (!scene) { + return { success: false, error: 'No active scene' }; + } + const node = scene.getChildByUuid(nodeUuid); + if (!node) { + return { success: false, error: `Node with UUID ${nodeUuid} not found` }; + } + const ComponentClass = js.getClassByName(componentType); + if (!ComponentClass) { + return { success: false, error: `Component type ${componentType} not found` }; + } + const component = node.getComponent(ComponentClass); + if (!component) { + return { success: false, error: `Component ${componentType} not found on node` }; + } + // 针对常见属性做特殊处理 + if (property === 'spriteFrame' && componentType === 'cc.Sprite') { + // 支持 value 为 uuid 或资源路径 + if (typeof value === 'string') { + // 先尝试按 uuid 查找 + const assetManager = require('cc').assetManager; + assetManager.resources.load(value, require('cc').SpriteFrame, (err: any, spriteFrame: any) => { + if (!err && spriteFrame) { + component.spriteFrame = spriteFrame; + } else { + // 尝试通过 uuid 加载 + assetManager.loadAny({ uuid: value }, (err2: any, asset: any) => { + if (!err2 && asset) { + component.spriteFrame = asset; + } else { + // 直接赋值(兼容已传入资源对象) + component.spriteFrame = value; + } + }); + } + }); + } else { + component.spriteFrame = value; + } + } else if (property === 'material' && (componentType === 'cc.Sprite' || componentType === 'cc.MeshRenderer')) { + // 支持 value 为 uuid 或资源路径 + if (typeof value === 'string') { + const assetManager = require('cc').assetManager; + assetManager.resources.load(value, require('cc').Material, (err: any, material: any) => { + if (!err && material) { + component.material = material; + } else { + assetManager.loadAny({ uuid: value }, (err2: any, asset: any) => { + if (!err2 && asset) { + component.material = asset; + } else { + component.material = value; + } + }); + } + }); + } else { + component.material = value; + } + } else if (property === 'string' && (componentType === 'cc.Label' || componentType === 'cc.RichText')) { + component.string = value; + } else { + component[property] = value; + } + // 可选:刷新 Inspector + // Editor.Message.send('scene', 'snapshot'); + return { success: true, message: `Component property '${property}' updated successfully` }; + } catch (error: any) { + return { success: false, error: error.message }; + } + } +}; \ No newline at end of file diff --git a/source/settings.ts b/source/settings.ts new file mode 100644 index 0000000..ea5a4e6 --- /dev/null +++ b/source/settings.ts @@ -0,0 +1,49 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { MCPServerSettings } from './types'; + +const DEFAULT_SETTINGS: MCPServerSettings = { + port: 3000, + autoStart: false, + enableDebugLog: false, + allowedOrigins: ['*'], + maxConnections: 10 +}; + +function getSettingsPath(): string { + return path.join(Editor.Project.path, 'settings', 'mcp-server.json'); +} + +function ensureSettingsDir(): void { + const settingsDir = path.dirname(getSettingsPath()); + if (!fs.existsSync(settingsDir)) { + fs.mkdirSync(settingsDir, { recursive: true }); + } +} + +export function readSettings(): MCPServerSettings { + try { + ensureSettingsDir(); + const settingsFile = getSettingsPath(); + if (fs.existsSync(settingsFile)) { + const content = fs.readFileSync(settingsFile, 'utf8'); + return { ...DEFAULT_SETTINGS, ...JSON.parse(content) }; + } + } catch (e) { + console.error('Failed to read settings:', e); + } + return DEFAULT_SETTINGS; +} + +export function saveSettings(settings: MCPServerSettings): void { + try { + ensureSettingsDir(); + const settingsFile = getSettingsPath(); + fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2)); + } catch (e) { + console.error('Failed to save settings:', e); + throw e; + } +} + +export { DEFAULT_SETTINGS }; \ No newline at end of file diff --git a/source/test/manual-test.ts b/source/test/manual-test.ts new file mode 100644 index 0000000..2cc0eed --- /dev/null +++ b/source/test/manual-test.ts @@ -0,0 +1,132 @@ +declare const Editor: any; + +/** + * 手动测试脚本 + * 可以在 Cocos Creator 控制台中执行测试 + */ + +export async function testSceneTools() { + console.log('=== Testing Scene Tools ==='); + + try { + // 1. 获取场景信息 + console.log('1. Getting scene info...'); + const sceneInfo = await Editor.Message.request('scene', 'get-scene-info'); + console.log('Scene info:', sceneInfo); + + // 2. 创建节点 + console.log('\n2. Creating test node...'); + const createResult = await Editor.Message.request('scene', 'create-node', { + name: 'TestNode_' + Date.now(), + type: 'cc.Node' + }); + console.log('Create result:', createResult); + + if (createResult && createResult.uuid) { + const nodeUuid = createResult.uuid; + + // 3. 查询节点 + console.log('\n3. Querying node...'); + const nodeInfo = await Editor.Message.request('scene', 'query-node', { + uuid: nodeUuid + }); + console.log('Node info:', nodeInfo); + + // 4. 设置节点属性 + console.log('\n4. Setting node position...'); + await Editor.Message.request('scene', 'set-node-property', { + uuid: nodeUuid, + path: 'position', + value: { x: 100, y: 200, z: 0 } + }); + console.log('Position set successfully'); + + // 5. 添加组件 + console.log('\n5. Adding Sprite component...'); + const addCompResult = await Editor.Message.request('scene', 'add-component', { + uuid: nodeUuid, + component: 'cc.Sprite' + }); + console.log('Component added:', addCompResult); + + // 6. 查询组件 + console.log('\n6. Querying component...'); + const compInfo = await Editor.Message.request('scene', 'query-node-component', { + uuid: nodeUuid, + component: 'cc.Sprite' + }); + console.log('Component info:', compInfo); + + // 7. 删除节点 + console.log('\n7. Removing test node...'); + await Editor.Message.request('scene', 'remove-node', { + uuid: nodeUuid + }); + console.log('Node removed successfully'); + } + + } catch (error) { + console.error('Test failed:', error); + } +} + +export async function testAssetTools() { + console.log('\n=== Testing Asset Tools ==='); + + try { + // 1. 查询资源 + console.log('1. Querying image assets...'); + const assets = await Editor.Message.request('asset-db', 'query-assets', { + pattern: '**/*.png', + ccType: 'cc.ImageAsset' + }); + console.log('Found assets:', assets?.length || 0); + + // 2. 获取资源信息 + console.log('\n2. Getting asset database info...'); + const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', { + uuid: 'db://assets' + }); + console.log('Asset info:', assetInfo); + + } catch (error) { + console.error('Test failed:', error); + } +} + +export async function testProjectTools() { + console.log('\n=== Testing Project Tools ==='); + + try { + // 1. 获取项目信息 + console.log('1. Getting project info...'); + const projectInfo = await Editor.Message.request('project', 'query-info'); + console.log('Project info:', projectInfo); + + // 2. 检查构建能力 + console.log('\n2. Checking build capability...'); + const canBuild = await Editor.Message.request('project', 'can-build'); + console.log('Can build:', canBuild); + + } catch (error) { + console.error('Test failed:', error); + } +} + +export async function runAllTests() { + console.log('Starting MCP Server Tools Test...\n'); + + await testSceneTools(); + await testAssetTools(); + await testProjectTools(); + + console.log('\n=== All tests completed ==='); +} + +// 导出到全局,方便在控制台调用 +(global as any).MCPTest = { + testSceneTools, + testAssetTools, + testProjectTools, + runAllTests +}; \ No newline at end of file diff --git a/source/test/mcp-tool-tester.ts b/source/test/mcp-tool-tester.ts new file mode 100644 index 0000000..cb88df0 --- /dev/null +++ b/source/test/mcp-tool-tester.ts @@ -0,0 +1,235 @@ +declare const Editor: any; + +/** + * MCP 工具测试器 - 直接测试通过 WebSocket 的 MCP 工具 + */ +export class MCPToolTester { + private ws: WebSocket | null = null; + private messageId = 0; + private responseHandlers = new Map void>(); + + async connect(port: number): Promise { + return new Promise((resolve) => { + try { + this.ws = new WebSocket(`ws://localhost:${port}`); + + this.ws.onopen = () => { + console.log('WebSocket 连接成功'); + resolve(true); + }; + + this.ws.onerror = (error) => { + console.error('WebSocket 连接错误:', error); + resolve(false); + }; + + this.ws.onmessage = (event) => { + try { + const response = JSON.parse(event.data); + if (response.id && this.responseHandlers.has(response.id)) { + const handler = this.responseHandlers.get(response.id); + this.responseHandlers.delete(response.id); + handler?.(response); + } + } catch (error) { + console.error('处理响应时出错:', error); + } + }; + } catch (error) { + console.error('创建 WebSocket 时出错:', error); + resolve(false); + } + }); + } + + async callTool(tool: string, args: any = {}): Promise { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + throw new Error('WebSocket 未连接'); + } + + return new Promise((resolve, reject) => { + const id = ++this.messageId; + const request = { + jsonrpc: '2.0', + id, + method: 'tools/call', + params: { + name: tool, + arguments: args + } + }; + + const timeout = setTimeout(() => { + this.responseHandlers.delete(id); + reject(new Error('请求超时')); + }, 10000); + + this.responseHandlers.set(id, (response) => { + clearTimeout(timeout); + if (response.error) { + reject(new Error(response.error.message)); + } else { + resolve(response.result); + } + }); + + this.ws!.send(JSON.stringify(request)); + }); + } + + async listTools(): Promise { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + throw new Error('WebSocket 未连接'); + } + + return new Promise((resolve, reject) => { + const id = ++this.messageId; + const request = { + jsonrpc: '2.0', + id, + method: 'tools/list' + }; + + const timeout = setTimeout(() => { + this.responseHandlers.delete(id); + reject(new Error('请求超时')); + }, 10000); + + this.responseHandlers.set(id, (response) => { + clearTimeout(timeout); + if (response.error) { + reject(new Error(response.error.message)); + } else { + resolve(response.result); + } + }); + + this.ws!.send(JSON.stringify(request)); + }); + } + + async testMCPTools() { + console.log('\n=== 测试 MCP 工具(通过 WebSocket)==='); + + try { + // 0. 获取工具列表 + console.log('\n0. 获取工具列表...'); + const toolsList = await this.listTools(); + console.log(`找到 ${toolsList.tools?.length || 0} 个工具:`); + if (toolsList.tools) { + for (const tool of toolsList.tools.slice(0, 10)) { // 只显示前10个 + console.log(` - ${tool.name}: ${tool.description}`); + } + if (toolsList.tools.length > 10) { + console.log(` ... 还有 ${toolsList.tools.length - 10} 个工具`); + } + } + + // 1. 测试场景工具 + console.log('\n1. 测试当前场景信息...'); + const sceneInfo = await this.callTool('scene_get_current_scene'); + console.log('场景信息:', JSON.stringify(sceneInfo).substring(0, 100) + '...'); + + // 2. 测试场景列表 + console.log('\n2. 测试场景列表...'); + const sceneList = await this.callTool('scene_get_scene_list'); + console.log('场景列表:', JSON.stringify(sceneList).substring(0, 100) + '...'); + + // 3. 测试节点创建 + console.log('\n3. 测试创建节点...'); + const createResult = await this.callTool('node_create_node', { + name: 'MCPTestNode_' + Date.now(), + nodeType: 'cc.Node', + position: { x: 0, y: 0, z: 0 } + }); + console.log('创建节点结果:', createResult); + + // 解析创建节点的结果 + let nodeUuid: string | null = null; + if (createResult.content && createResult.content[0] && createResult.content[0].text) { + try { + const resultData = JSON.parse(createResult.content[0].text); + if (resultData.success && resultData.data && resultData.data.uuid) { + nodeUuid = resultData.data.uuid; + console.log('成功获取节点UUID:', nodeUuid); + } + } catch (e) { + } + } + + if (nodeUuid) { + // 4. 测试查询节点 + console.log('\n4. 测试查询节点...'); + const queryResult = await this.callTool('node_get_node_info', { + uuid: nodeUuid + }); + console.log('节点信息:', JSON.stringify(queryResult).substring(0, 100) + '...'); + + // 5. 测试删除节点 + console.log('\n5. 测试删除节点...'); + const removeResult = await this.callTool('node_delete_node', { + uuid: nodeUuid + }); + console.log('删除结果:', removeResult); + } else { + console.log('无法从创建结果获取节点UUID,尝试通过名称查找...'); + + // 备用方案:通过名称查找刚创建的节点 + const findResult = await this.callTool('node_find_node_by_name', { + name: 'MCPTestNode_' + Date.now() + }); + + if (findResult.content && findResult.content[0] && findResult.content[0].text) { + try { + const findData = JSON.parse(findResult.content[0].text); + if (findData.success && findData.data && findData.data.uuid) { + nodeUuid = findData.data.uuid; + console.log('通过名称查找成功获取UUID:', nodeUuid); + } + } catch (e) { + } + } + + if (!nodeUuid) { + console.log('所有方式都无法获取节点UUID,跳过后续节点操作测试'); + } + } + + // 6. 测试项目工具 + console.log('\n6. 测试项目信息...'); + const projectInfo = await this.callTool('project_get_project_info'); + console.log('项目信息:', JSON.stringify(projectInfo).substring(0, 100) + '...'); + + // 7. 测试预制体工具 + console.log('\n7. 测试预制体列表...'); + const prefabResult = await this.callTool('prefab_get_prefab_list', { + folder: 'db://assets' + }); + console.log('找到预制体:', prefabResult.data?.length || 0); + + // 8. 测试组件工具 + console.log('\n8. 测试可用组件...'); + const componentsResult = await this.callTool('component_get_available_components'); + console.log('可用组件:', JSON.stringify(componentsResult).substring(0, 100) + '...'); + + // 9. 测试调试工具 + console.log('\n9. 测试编辑器信息...'); + const editorInfo = await this.callTool('debug_get_editor_info'); + console.log('编辑器信息:', JSON.stringify(editorInfo).substring(0, 100) + '...'); + + } catch (error) { + console.error('MCP 工具测试失败:', error); + } + } + + disconnect() { + if (this.ws) { + this.ws.close(); + this.ws = null; + } + this.responseHandlers.clear(); + } +} + +// 导出到全局方便测试 +(global as any).MCPToolTester = MCPToolTester; \ No newline at end of file diff --git a/source/test/tool-tester.ts b/source/test/tool-tester.ts new file mode 100644 index 0000000..d61100f --- /dev/null +++ b/source/test/tool-tester.ts @@ -0,0 +1,168 @@ +declare const Editor: any; + +interface TestResult { + tool: string; + method: string; + success: boolean; + result?: any; + error?: string; + time: number; +} + +export class ToolTester { + private results: TestResult[] = []; + + async runTest(tool: string, method: string, params: any): Promise { + const startTime = Date.now(); + const result: TestResult = { + tool, + method, + success: false, + time: 0 + }; + + try { + const response = await Editor.Message.request(tool, method, params); + result.success = true; + result.result = response; + } catch (error) { + result.success = false; + result.error = error instanceof Error ? error.message : String(error); + } + + result.time = Date.now() - startTime; + this.results.push(result); + return result; + } + + async testSceneOperations() { + console.log('Testing Scene Operations...'); + + // Test node creation (this is the main scene operation that works) + const createResult = await this.runTest('scene', 'create-node', { + name: 'TestNode', + type: 'cc.Node' + }); + + if (createResult.success && createResult.result) { + const nodeUuid = createResult.result; + + // Test query node info + await this.runTest('scene', 'query-node-info', nodeUuid); + + // Test remove node + await this.runTest('scene', 'remove-node', nodeUuid); + } + + // Test execute scene script + await this.runTest('scene', 'execute-scene-script', { + name: 'cocos-mcp-server', + method: 'test-method', + args: [] + }); + } + + async testNodeOperations() { + console.log('Testing Node Operations...'); + + // Create a test node first + const createResult = await this.runTest('scene', 'create-node', { + name: 'TestNodeForOps', + type: 'cc.Node' + }); + + if (createResult.success && createResult.result) { + const nodeUuid = createResult.result; + + // Test set property + await this.runTest('scene', 'set-property', { + uuid: nodeUuid, + path: 'position', + dump: { + type: 'cc.Vec3', + value: { x: 100, y: 200, z: 0 } + } + }); + + // Test add component + await this.runTest('scene', 'add-component', { + uuid: nodeUuid, + component: 'cc.Sprite' + }); + + // Clean up + await this.runTest('scene', 'remove-node', nodeUuid); + } + } + + async testAssetOperations() { + console.log('Testing Asset Operations...'); + + // Test asset list + await this.runTest('asset-db', 'query-assets', { + pattern: '**/*.png', + ccType: 'cc.ImageAsset' + }); + + // Test query asset by path + await this.runTest('asset-db', 'query-path', 'db://assets'); + + // Test query asset by uuid (using a valid uuid format) + await this.runTest('asset-db', 'query-uuid', 'db://assets'); + } + + async testProjectOperations() { + console.log('Testing Project Operations...'); + + // Test open project settings + await this.runTest('project', 'open-settings', {}); + + // Test query project settings + const projectName = await this.runTest('project', 'query-setting', 'name'); + + if (projectName.success) { + console.log('Project name:', projectName.result); + } + } + + async runAllTests() { + this.results = []; + + await this.testSceneOperations(); + await this.testNodeOperations(); + await this.testAssetOperations(); + await this.testProjectOperations(); + + return this.getTestReport(); + } + + getTestReport() { + const total = this.results.length; + const passed = this.results.filter(r => r.success).length; + const failed = total - passed; + + return { + summary: { + total, + passed, + failed, + passRate: total > 0 ? (passed / total * 100).toFixed(2) + '%' : '0%' + }, + results: this.results, + grouped: this.groupResultsByTool() + }; + } + + private groupResultsByTool() { + const grouped: Record = {}; + + for (const result of this.results) { + if (!grouped[result.tool]) { + grouped[result.tool] = []; + } + grouped[result.tool].push(result); + } + + return grouped; + } +} \ No newline at end of file diff --git a/source/tools/broadcast-tools.ts b/source/tools/broadcast-tools.ts new file mode 100644 index 0000000..3164725 --- /dev/null +++ b/source/tools/broadcast-tools.ts @@ -0,0 +1,264 @@ +import { ToolDefinition, ToolResponse, ToolExecutor } from '../types'; + +export class BroadcastTools implements ToolExecutor { + private listeners: Map = new Map(); + private messageLog: Array<{ message: string; data: any; timestamp: number }> = []; + + constructor() { + this.setupBroadcastListeners(); + } + + getTools(): ToolDefinition[] { + return [ + { + name: 'get_broadcast_log', + description: 'Get recent broadcast messages log', + inputSchema: { + type: 'object', + properties: { + limit: { + type: 'number', + description: 'Number of recent messages to return', + default: 50 + }, + messageType: { + type: 'string', + description: 'Filter by message type (optional)' + } + } + } + }, + { + name: 'listen_broadcast', + description: 'Start listening for specific broadcast messages', + inputSchema: { + type: 'object', + properties: { + messageType: { + type: 'string', + description: 'Message type to listen for' + } + }, + required: ['messageType'] + } + }, + { + name: 'stop_listening', + description: 'Stop listening for specific broadcast messages', + inputSchema: { + type: 'object', + properties: { + messageType: { + type: 'string', + description: 'Message type to stop listening for' + } + }, + required: ['messageType'] + } + }, + { + name: 'clear_broadcast_log', + description: 'Clear the broadcast messages log', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_active_listeners', + description: 'Get list of active broadcast listeners', + inputSchema: { + type: 'object', + properties: {} + } + } + ]; + } + + async execute(toolName: string, args: any): Promise { + switch (toolName) { + case 'get_broadcast_log': + return await this.getBroadcastLog(args.limit, args.messageType); + case 'listen_broadcast': + return await this.listenBroadcast(args.messageType); + case 'stop_listening': + return await this.stopListening(args.messageType); + case 'clear_broadcast_log': + return await this.clearBroadcastLog(); + case 'get_active_listeners': + return await this.getActiveListeners(); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + + private setupBroadcastListeners(): void { + // 设置预定义的重要广播消息监听 + const importantMessages = [ + 'build-worker:ready', + 'build-worker:closed', + 'scene:ready', + 'scene:close', + 'scene:light-probe-edit-mode-changed', + 'scene:light-probe-bounding-box-edit-mode-changed', + 'asset-db:ready', + 'asset-db:close', + 'asset-db:asset-add', + 'asset-db:asset-change', + 'asset-db:asset-delete' + ]; + + importantMessages.forEach(messageType => { + this.addBroadcastListener(messageType); + }); + } + + private addBroadcastListener(messageType: string): void { + const listener = (data: any) => { + this.messageLog.push({ + message: messageType, + data: data, + timestamp: Date.now() + }); + + // 保持日志大小在合理范围内 + if (this.messageLog.length > 1000) { + this.messageLog = this.messageLog.slice(-500); + } + + console.log(`[Broadcast] ${messageType}:`, data); + }; + + if (!this.listeners.has(messageType)) { + this.listeners.set(messageType, []); + } + this.listeners.get(messageType)!.push(listener); + + // 注册 Editor 消息监听 - 暂时注释掉,Editor.Message API可能不支持 + // Editor.Message.on(messageType, listener); + console.log(`[BroadcastTools] Added listener for ${messageType} (simulated)`); + } + + private removeBroadcastListener(messageType: string): void { + const listeners = this.listeners.get(messageType); + if (listeners) { + listeners.forEach(listener => { + // Editor.Message.off(messageType, listener); + console.log(`[BroadcastTools] Removed listener for ${messageType} (simulated)`); + }); + this.listeners.delete(messageType); + } + } + + private async getBroadcastLog(limit: number = 50, messageType?: string): Promise { + return new Promise((resolve) => { + let filteredLog = this.messageLog; + + if (messageType) { + filteredLog = this.messageLog.filter(entry => entry.message === messageType); + } + + const recentLog = filteredLog.slice(-limit).map(entry => ({ + ...entry, + timestamp: new Date(entry.timestamp).toISOString() + })); + + resolve({ + success: true, + data: { + log: recentLog, + count: recentLog.length, + totalCount: filteredLog.length, + filter: messageType || 'all', + message: 'Broadcast log retrieved successfully' + } + }); + }); + } + + private async listenBroadcast(messageType: string): Promise { + return new Promise((resolve) => { + try { + if (!this.listeners.has(messageType)) { + this.addBroadcastListener(messageType); + resolve({ + success: true, + data: { + messageType: messageType, + message: `Started listening for broadcast: ${messageType}` + } + }); + } else { + resolve({ + success: true, + data: { + messageType: messageType, + message: `Already listening for broadcast: ${messageType}` + } + }); + } + } catch (err: any) { + resolve({ success: false, error: err.message }); + } + }); + } + + private async stopListening(messageType: string): Promise { + return new Promise((resolve) => { + try { + if (this.listeners.has(messageType)) { + this.removeBroadcastListener(messageType); + resolve({ + success: true, + data: { + messageType: messageType, + message: `Stopped listening for broadcast: ${messageType}` + } + }); + } else { + resolve({ + success: true, + data: { + messageType: messageType, + message: `Was not listening for broadcast: ${messageType}` + } + }); + } + } catch (err: any) { + resolve({ success: false, error: err.message }); + } + }); + } + + private async clearBroadcastLog(): Promise { + return new Promise((resolve) => { + const previousCount = this.messageLog.length; + this.messageLog = []; + resolve({ + success: true, + data: { + clearedCount: previousCount, + message: 'Broadcast log cleared successfully' + } + }); + }); + } + + private async getActiveListeners(): Promise { + return new Promise((resolve) => { + const activeListeners = Array.from(this.listeners.keys()).map(messageType => ({ + messageType: messageType, + listenerCount: this.listeners.get(messageType)?.length || 0 + })); + + resolve({ + success: true, + data: { + listeners: activeListeners, + count: activeListeners.length, + message: 'Active listeners retrieved successfully' + } + }); + }); + } +} \ No newline at end of file diff --git a/source/tools/component-tools.ts b/source/tools/component-tools.ts new file mode 100644 index 0000000..3890ff4 --- /dev/null +++ b/source/tools/component-tools.ts @@ -0,0 +1,445 @@ +import { ToolDefinition, ToolResponse, ToolExecutor, ComponentInfo } from '../types'; + +export class ComponentTools implements ToolExecutor { + getTools(): ToolDefinition[] { + return [ + { + name: 'add_component', + description: 'Add a component to a specific node. The component will be added to the exact node specified by nodeUuid.', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Target node UUID. Use get_node_info or find_node_by_name to get the UUID of the desired node.' + }, + componentType: { + type: 'string', + description: 'Component type (e.g., cc.Sprite, cc.Label, cc.Button)' + } + }, + required: ['nodeUuid', 'componentType'] + } + }, + { + name: 'remove_component', + description: 'Remove a component from a node', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + }, + componentType: { + type: 'string', + description: 'Component type to remove' + } + }, + required: ['nodeUuid', 'componentType'] + } + }, + { + name: 'get_components', + description: 'Get all components of a node', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + } + }, + required: ['nodeUuid'] + } + }, + { + name: 'get_component_info', + description: 'Get specific component information', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + }, + componentType: { + type: 'string', + description: 'Component type to get info for' + } + }, + required: ['nodeUuid', 'componentType'] + } + }, + { + name: 'set_component_property', + description: 'Set component property value', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + }, + componentType: { + type: 'string', + description: 'Component type' + }, + property: { + type: 'string', + description: 'Property name' + }, + value: { + description: 'Property value' + } + }, + required: ['nodeUuid', 'componentType', 'property', 'value'] + } + }, + { + name: 'attach_script', + description: 'Attach a script component to a node', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID' + }, + scriptPath: { + type: 'string', + description: 'Script asset path (e.g., db://assets/scripts/MyScript.ts)' + } + }, + required: ['nodeUuid', 'scriptPath'] + } + }, + { + name: 'get_available_components', + description: 'Get list of available component types', + inputSchema: { + type: 'object', + properties: { + category: { + type: 'string', + description: 'Component category filter', + enum: ['all', 'renderer', 'ui', 'physics', 'animation', 'audio'], + default: 'all' + } + } + } + } + ]; + } + + async execute(toolName: string, args: any): Promise { + switch (toolName) { + case 'add_component': + return await this.addComponent(args.nodeUuid, args.componentType); + case 'remove_component': + return await this.removeComponent(args.nodeUuid, args.componentType); + case 'get_components': + return await this.getComponents(args.nodeUuid); + case 'get_component_info': + return await this.getComponentInfo(args.nodeUuid, args.componentType); + case 'set_component_property': + return await this.setComponentProperty(args); + case 'attach_script': + return await this.attachScript(args.nodeUuid, args.scriptPath); + case 'get_available_components': + return await this.getAvailableComponents(args.category); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + + private async addComponent(nodeUuid: string, componentType: string): Promise { + return new Promise((resolve) => { + // 尝试直接使用 Editor API 添加组件 + Editor.Message.request('scene', 'create-component', { + uuid: nodeUuid, + component: componentType + }).then((result: any) => { + resolve({ + success: true, + data: { + componentId: result, + message: `Component '${componentType}' added successfully` + } + }); + }).catch((err: Error) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'addComponentToNode', + args: [nodeUuid, componentType] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + resolve(result); + }).catch((err2: Error) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + + private async removeComponent(nodeUuid: string, componentType: string): Promise { + return new Promise((resolve) => { + const options = { + name: 'cocos-mcp-server', + method: 'removeComponentFromNode', + args: [nodeUuid, componentType] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + resolve(result); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async getComponents(nodeUuid: string): Promise { + return new Promise((resolve) => { + // 优先尝试直接使用 Editor API 查询节点信息 + Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeData: any) => { + if (nodeData && nodeData.__comps__) { + const components = nodeData.__comps__.map((comp: any) => ({ + type: comp.__type__ || 'Unknown', + enabled: comp.enabled !== undefined ? comp.enabled : true, + properties: this.extractComponentProperties(comp) + })); + + resolve({ + success: true, + data: { + nodeUuid: nodeUuid, + components: components + } + }); + } else { + resolve({ success: false, error: 'Node not found or no components data' }); + } + }).catch((err: Error) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getNodeInfo', + args: [nodeUuid] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + if (result.success) { + resolve({ + success: true, + data: result.data.components + }); + } else { + resolve(result); + } + }).catch((err2: Error) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + + private async getComponentInfo(nodeUuid: string, componentType: string): Promise { + return new Promise((resolve) => { + // 优先尝试直接使用 Editor API 查询节点信息 + Editor.Message.request('scene', 'query-node', nodeUuid).then((nodeData: any) => { + if (nodeData && nodeData.__comps__) { + const component = nodeData.__comps__.find((comp: any) => comp.__type__ === componentType); + + if (component) { + resolve({ + success: true, + data: { + nodeUuid: nodeUuid, + componentType: componentType, + enabled: component.enabled !== undefined ? component.enabled : true, + properties: this.extractComponentProperties(component) + } + }); + } else { + resolve({ success: false, error: `Component '${componentType}' not found on node` }); + } + } else { + resolve({ success: false, error: 'Node not found or no components data' }); + } + }).catch((err: Error) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getNodeInfo', + args: [nodeUuid] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + if (result.success && result.data.components) { + const component = result.data.components.find((comp: any) => comp.type === componentType); + if (component) { + resolve({ + success: true, + data: { + nodeUuid: nodeUuid, + componentType: componentType, + ...component + } + }); + } else { + resolve({ success: false, error: `Component '${componentType}' not found on node` }); + } + } else { + resolve({ success: false, error: result.error || 'Failed to get component info' }); + } + }).catch((err2: Error) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + + private extractComponentProperties(component: any): Record { + const properties: Record = {}; + const excludeKeys = ['__type__', 'enabled', 'node', '_id']; + + for (const key in component) { + if (!excludeKeys.includes(key) && !key.startsWith('_')) { + properties[key] = component[key]; + } + } + + return properties; + } + + private async setComponentProperty(args: any): Promise { + return new Promise((resolve) => { + // 首先获取节点信息以找到正确的组件索引 + Editor.Message.request('scene', 'query-node', args.nodeUuid).then((nodeData: any) => { + if (!nodeData || !nodeData.__comps__) { + throw new Error('Node not found or no components data'); + } + + // 查找组件索引 + let componentIndex = -1; + for (let i = 0; i < nodeData.__comps__.length; i++) { + const comp = nodeData.__comps__[i]; + if (comp.__type__ === args.componentType) { + componentIndex = i; + break; + } + } + + if (componentIndex === -1) { + throw new Error(`Component '${args.componentType}' not found on node`); + } + + // 使用正确的组件索引路径 + const propertyPath = `__comps__.${componentIndex}.${args.property}`; + + return Editor.Message.request('scene', 'set-property', { + uuid: args.nodeUuid, + path: propertyPath, + dump: { + value: args.value + } + }); + }).then(() => { + resolve({ + success: true, + message: `Component property '${args.property}' updated successfully` + }); + }).catch((err: Error) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'setComponentProperty', + args: [args.nodeUuid, args.componentType, args.property, args.value] + }; + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + resolve(result); + }).catch((err2: Error) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + + private async attachScript(nodeUuid: string, scriptPath: string): Promise { + return new Promise((resolve) => { + // 从脚本路径提取组件类名 + const scriptName = scriptPath.split('/').pop()?.replace('.ts', '').replace('.js', ''); + if (!scriptName) { + resolve({ success: false, error: 'Invalid script path' }); + return; + } + + // 首先尝试直接使用脚本名称作为组件类型 + Editor.Message.request('scene', 'create-component', { + uuid: nodeUuid, + component: scriptName // 使用脚本名称而非UUID + }).then((result: any) => { + resolve({ + success: true, + data: { + componentId: result, + scriptPath: scriptPath, + componentName: scriptName, + message: `Script '${scriptName}' attached successfully` + } + }); + }).catch((err: Error) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'attachScript', + args: [nodeUuid, scriptPath] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + resolve(result); + }).catch((err2: Error) => { + resolve({ + success: false, + error: `Failed to attach script '${scriptName}': ${err.message}`, + instruction: 'Please ensure the script is properly compiled and exported as a Component class. You can also manually attach the script through the Properties panel in the editor.' + }); + }); + }); + }); + } + + private async getAvailableComponents(category: string = 'all'): Promise { + const componentCategories: Record = { + renderer: ['cc.Sprite', 'cc.Label', 'cc.RichText', 'cc.Mask', 'cc.Graphics'], + ui: ['cc.Button', 'cc.Toggle', 'cc.Slider', 'cc.ScrollView', 'cc.EditBox', 'cc.ProgressBar'], + physics: ['cc.RigidBody2D', 'cc.BoxCollider2D', 'cc.CircleCollider2D', 'cc.PolygonCollider2D'], + animation: ['cc.Animation', 'cc.AnimationClip', 'cc.SkeletalAnimation'], + audio: ['cc.AudioSource'], + layout: ['cc.Layout', 'cc.Widget', 'cc.PageView', 'cc.PageViewIndicator'], + effects: ['cc.MotionStreak', 'cc.ParticleSystem2D'], + camera: ['cc.Camera'], + light: ['cc.Light', 'cc.DirectionalLight', 'cc.PointLight', 'cc.SpotLight'] + }; + + let components: string[] = []; + + if (category === 'all') { + for (const cat in componentCategories) { + components = components.concat(componentCategories[cat]); + } + } else if (componentCategories[category]) { + components = componentCategories[category]; + } + + return { + success: true, + data: { + category: category, + components: components + } + }; + } +} \ No newline at end of file diff --git a/source/tools/debug-tools.ts b/source/tools/debug-tools.ts new file mode 100644 index 0000000..59b2e8d --- /dev/null +++ b/source/tools/debug-tools.ts @@ -0,0 +1,351 @@ +import { ToolDefinition, ToolResponse, ToolExecutor, ConsoleMessage, PerformanceStats, ValidationResult, ValidationIssue } from '../types'; + +export class DebugTools implements ToolExecutor { + private consoleMessages: ConsoleMessage[] = []; + private readonly maxMessages = 1000; + + constructor() { + this.setupConsoleCapture(); + } + + private setupConsoleCapture(): void { + // Intercept Editor console messages + // Note: Editor.Message.addBroadcastListener may not be available in all versions + // This is a placeholder for console capture implementation + console.log('Console capture setup - implementation depends on Editor API availability'); + } + + private addConsoleMessage(message: any): void { + this.consoleMessages.push({ + timestamp: new Date().toISOString(), + ...message + }); + + // Keep only latest messages + if (this.consoleMessages.length > this.maxMessages) { + this.consoleMessages.shift(); + } + } + + getTools(): ToolDefinition[] { + return [ + { + name: 'get_console_logs', + description: 'Get editor console logs', + inputSchema: { + type: 'object', + properties: { + limit: { + type: 'number', + description: 'Number of recent logs to retrieve', + default: 100 + }, + filter: { + type: 'string', + description: 'Filter logs by type', + enum: ['all', 'log', 'warn', 'error', 'info'], + default: 'all' + } + } + } + }, + { + name: 'clear_console', + description: 'Clear editor console', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'execute_script', + description: 'Execute JavaScript in scene context', + inputSchema: { + type: 'object', + properties: { + script: { + type: 'string', + description: 'JavaScript code to execute' + } + }, + required: ['script'] + } + }, + { + name: 'get_node_tree', + description: 'Get detailed node tree for debugging', + inputSchema: { + type: 'object', + properties: { + rootUuid: { + type: 'string', + description: 'Root node UUID (optional, uses scene root if not provided)' + }, + maxDepth: { + type: 'number', + description: 'Maximum tree depth', + default: 10 + } + } + } + }, + { + name: 'get_performance_stats', + description: 'Get performance statistics', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'validate_scene', + description: 'Validate current scene for issues', + inputSchema: { + type: 'object', + properties: { + checkMissingAssets: { + type: 'boolean', + description: 'Check for missing asset references', + default: true + }, + checkPerformance: { + type: 'boolean', + description: 'Check for performance issues', + default: true + } + } + } + }, + { + name: 'get_editor_info', + description: 'Get editor and environment information', + inputSchema: { + type: 'object', + properties: {} + } + } + ]; + } + + async execute(toolName: string, args: any): Promise { + switch (toolName) { + case 'get_console_logs': + return await this.getConsoleLogs(args.limit, args.filter); + case 'clear_console': + return await this.clearConsole(); + case 'execute_script': + return await this.executeScript(args.script); + case 'get_node_tree': + return await this.getNodeTree(args.rootUuid, args.maxDepth); + case 'get_performance_stats': + return await this.getPerformanceStats(); + case 'validate_scene': + return await this.validateScene(args); + case 'get_editor_info': + return await this.getEditorInfo(); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + + private async getConsoleLogs(limit: number = 100, filter: string = 'all'): Promise { + let logs = this.consoleMessages; + + if (filter !== 'all') { + logs = logs.filter(log => log.type === filter); + } + + const recentLogs = logs.slice(-limit); + + return { + success: true, + data: { + total: logs.length, + returned: recentLogs.length, + logs: recentLogs + } + }; + } + + private async clearConsole(): Promise { + this.consoleMessages = []; + + try { + // Note: Editor.Message.send may not return a promise in all versions + Editor.Message.send('console', 'clear'); + return { + success: true, + message: 'Console cleared successfully' + }; + } catch (err: any) { + return { success: false, error: err.message }; + } + } + + private async executeScript(script: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'execute-script', { + script: script + }).then((result: any) => { + resolve({ + success: true, + data: { + result: result, + message: 'Script executed successfully' + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async getNodeTree(rootUuid?: string, maxDepth: number = 10): Promise { + return new Promise((resolve) => { + const buildTree = async (nodeUuid: string, depth: number = 0): Promise => { + if (depth >= maxDepth) { + return { truncated: true }; + } + + try { + const nodeData = await Editor.Message.request('scene', 'query-node', nodeUuid); + + const tree = { + uuid: nodeData.uuid, + name: nodeData.name, + active: nodeData.active, + components: (nodeData as any).components ? (nodeData as any).components.map((c: any) => c.__type__) : [], + childCount: nodeData.children ? nodeData.children.length : 0, + children: [] as any[] + }; + + if (nodeData.children && nodeData.children.length > 0) { + for (const childId of nodeData.children) { + const childTree = await buildTree(childId, depth + 1); + tree.children.push(childTree); + } + } + + return tree; + } catch (err: any) { + return { error: err.message }; + } + }; + + if (rootUuid) { + buildTree(rootUuid).then(tree => { + resolve({ success: true, data: tree }); + }); + } else { + Editor.Message.request('scene', 'query-hierarchy').then(async (hierarchy: any) => { + const trees = []; + for (const rootNode of hierarchy.children) { + const tree = await buildTree(rootNode.uuid); + trees.push(tree); + } + resolve({ success: true, data: trees }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + } + }); + } + + private async getPerformanceStats(): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'query-performance').then((stats: any) => { + const perfStats: PerformanceStats = { + nodeCount: stats.nodeCount || 0, + componentCount: stats.componentCount || 0, + drawCalls: stats.drawCalls || 0, + triangles: stats.triangles || 0, + memory: stats.memory || {} + }; + resolve({ success: true, data: perfStats }); + }).catch(() => { + // Fallback to basic stats + resolve({ + success: true, + data: { + message: 'Performance stats not available in edit mode' + } + }); + }); + }); + } + + private async validateScene(options: any): Promise { + const issues: ValidationIssue[] = []; + + try { + // Check for missing assets + if (options.checkMissingAssets) { + const assetCheck = await Editor.Message.request('scene', 'check-missing-assets'); + if (assetCheck && assetCheck.missing) { + issues.push({ + type: 'error', + category: 'assets', + message: `Found ${assetCheck.missing.length} missing asset references`, + details: assetCheck.missing + }); + } + } + + // Check for performance issues + if (options.checkPerformance) { + const hierarchy = await Editor.Message.request('scene', 'query-hierarchy'); + const nodeCount = this.countNodes(hierarchy.children); + + if (nodeCount > 1000) { + issues.push({ + type: 'warning', + category: 'performance', + message: `High node count: ${nodeCount} nodes (recommended < 1000)`, + suggestion: 'Consider using object pooling or scene optimization' + }); + } + } + + const result: ValidationResult = { + valid: issues.length === 0, + issueCount: issues.length, + issues: issues + }; + + return { success: true, data: result }; + } catch (err: any) { + return { success: false, error: err.message }; + } + } + + private countNodes(nodes: any[]): number { + let count = nodes.length; + for (const node of nodes) { + if (node.children) { + count += this.countNodes(node.children); + } + } + return count; + } + + private async getEditorInfo(): Promise { + const info = { + editor: { + version: (Editor as any).versions?.editor || 'Unknown', + cocosVersion: (Editor as any).versions?.cocos || 'Unknown', + platform: process.platform, + arch: process.arch, + nodeVersion: process.version + }, + project: { + name: Editor.Project.name, + path: Editor.Project.path, + uuid: Editor.Project.uuid + }, + memory: process.memoryUsage(), + uptime: process.uptime() + }; + + return { success: true, data: info }; + } +} \ No newline at end of file diff --git a/source/tools/node-tools.ts b/source/tools/node-tools.ts new file mode 100644 index 0000000..a95f91d --- /dev/null +++ b/source/tools/node-tools.ts @@ -0,0 +1,526 @@ +import { ToolDefinition, ToolResponse, ToolExecutor, NodeInfo } from '../types'; + +export class NodeTools implements ToolExecutor { + getTools(): ToolDefinition[] { + return [ + { + name: 'create_node', + description: 'Create a new node in the scene. If parentUuid is not provided, the node will be created at the current selection in the editor.', + inputSchema: { + type: 'object', + properties: { + name: { + type: 'string', + description: 'Node name' + }, + parentUuid: { + type: 'string', + description: 'Parent node UUID. If not provided, node will be created at current editor selection. To create at scene root, first get the root node UUID.' + }, + nodeType: { + type: 'string', + description: 'Node type: Node, 2DNode, 3DNode', + enum: ['Node', '2DNode', '3DNode'], + default: 'Node' + }, + siblingIndex: { + type: 'number', + description: 'Sibling index for ordering (-1 means append at end)', + default: -1 + } + }, + required: ['name'] + } + }, + { + name: 'get_node_info', + description: 'Get node information by UUID', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Node UUID' + } + }, + required: ['uuid'] + } + }, + { + name: 'find_nodes', + description: 'Find nodes by name pattern', + inputSchema: { + type: 'object', + properties: { + pattern: { + type: 'string', + description: 'Name pattern to search' + }, + exactMatch: { + type: 'boolean', + description: 'Exact match or partial match', + default: false + } + }, + required: ['pattern'] + } + }, + { + name: 'find_node_by_name', + description: 'Find first node by exact name', + inputSchema: { + type: 'object', + properties: { + name: { + type: 'string', + description: 'Node name to find' + } + }, + required: ['name'] + } + }, + { + name: 'get_all_nodes', + description: 'Get all nodes in the scene with their UUIDs', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'set_node_property', + description: 'Set node property value', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Node UUID' + }, + property: { + type: 'string', + description: 'Property name (e.g., position, rotation, scale, active)' + }, + value: { + description: 'Property value' + } + }, + required: ['uuid', 'property', 'value'] + } + }, + { + name: 'delete_node', + description: 'Delete a node from scene', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Node UUID to delete' + } + }, + required: ['uuid'] + } + }, + { + name: 'move_node', + description: 'Move node to new parent', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Node UUID to move' + }, + newParentUuid: { + type: 'string', + description: 'New parent node UUID' + }, + siblingIndex: { + type: 'number', + description: 'Sibling index in new parent', + default: -1 + } + }, + required: ['nodeUuid', 'newParentUuid'] + } + }, + { + name: 'duplicate_node', + description: 'Duplicate a node', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Node UUID to duplicate' + }, + includeChildren: { + type: 'boolean', + description: 'Include children nodes', + default: true + } + }, + required: ['uuid'] + } + } + ]; + } + + async execute(toolName: string, args: any): Promise { + switch (toolName) { + case 'create_node': + return await this.createNode(args); + case 'get_node_info': + return await this.getNodeInfo(args.uuid); + case 'find_nodes': + return await this.findNodes(args.pattern, args.exactMatch); + case 'find_node_by_name': + return await this.findNodeByName(args.name); + case 'get_all_nodes': + return await this.getAllNodes(); + case 'set_node_property': + return await this.setNodeProperty(args.uuid, args.property, args.value); + case 'delete_node': + return await this.deleteNode(args.uuid); + case 'move_node': + return await this.moveNode(args.nodeUuid, args.newParentUuid, args.siblingIndex); + case 'duplicate_node': + return await this.duplicateNode(args.uuid, args.includeChildren); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + + private async createNode(args: any): Promise { + return new Promise(async (resolve) => { + // 如果指定了父节点,先验证父节点是否存在 + if (args.parentUuid) { + try { + const parentNode = await Editor.Message.request('scene', 'query-node', args.parentUuid); + if (!parentNode) { + resolve({ + success: false, + error: `Parent node with UUID '${args.parentUuid}' not found` + }); + return; + } + } catch (err) { + resolve({ + success: false, + error: `Failed to verify parent node: ${err}` + }); + return; + } + } + + const nodeData: any = { + name: args.name, + type: args.nodeType || 'cc.Node' + }; + + // 使用更明确的父节点指定方式 + if (args.parentUuid) { + nodeData.parent = args.parentUuid; + // 尝试先创建节点,然后移动到指定父节点 + Editor.Message.request('scene', 'create-node', nodeData).then((nodeUuid: any) => { + // 如果创建成功但可能没有在正确的父节点下,尝试移动 + if (args.parentUuid && nodeUuid) { + Editor.Message.request('scene', 'move-node', { + uuid: nodeUuid, + parent: args.parentUuid, + index: args.siblingIndex !== undefined ? args.siblingIndex : -1 + }).then(() => { + resolve({ + success: true, + data: { + uuid: nodeUuid, + name: args.name, + parentUuid: args.parentUuid, + message: `Node '${args.name}' created under specified parent` + } + }); + }).catch(() => { + // 即使移动失败,节点已创建,返回成功但带警告 + resolve({ + success: true, + data: { + uuid: nodeUuid, + name: args.name, + message: `Node '${args.name}' created but may not be under specified parent`, + warning: 'Failed to move node to specified parent' + } + }); + }); + } else { + resolve({ + success: true, + data: { + uuid: nodeUuid, + name: args.name, + message: `Node '${args.name}' created successfully` + } + }); + } + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + } else { + // 没有指定父节点,使用默认行为 + Editor.Message.request('scene', 'create-node', nodeData).then((result: any) => { + resolve({ + success: true, + data: { + uuid: result, + name: args.name, + message: `Node '${args.name}' created at current selection` + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + } + }); + } + + private async getNodeInfo(uuid: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'query-node', uuid).then((nodeData: any) => { + if (!nodeData) { + resolve({ + success: false, + error: 'Node not found or invalid response' + }); + return; + } + + // 根据实际返回的数据结构解析节点信息 + const info: NodeInfo = { + uuid: nodeData.uuid?.value || uuid, + name: nodeData.name?.value || 'Unknown', + active: nodeData.active?.value !== undefined ? nodeData.active.value : true, + position: nodeData.position?.value || { x: 0, y: 0, z: 0 }, + rotation: nodeData.rotation?.value || { x: 0, y: 0, z: 0 }, + scale: nodeData.scale?.value || { x: 1, y: 1, z: 1 }, + parent: nodeData.parent?.value?.uuid || null, + children: nodeData.children || [], + components: (nodeData.__comps__ || []).map((comp: any) => ({ + type: comp.__type__ || 'Unknown', + enabled: comp.enabled !== undefined ? comp.enabled : true + })), + layer: nodeData.layer?.value || 1073741824, + mobility: nodeData.mobility?.value || 0 + }; + resolve({ success: true, data: info }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async findNodes(pattern: string, exactMatch: boolean = false): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'query-nodes-by-name', { + name: pattern, + exactMatch: exactMatch + }).then((results: any[]) => { + const nodes = results.map(node => ({ + uuid: node.uuid, + name: node.name, + path: node.path + })); + resolve({ success: true, data: nodes }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async findNodeByName(name: string): Promise { + return new Promise((resolve) => { + // 优先尝试使用 Editor API 查询节点树并搜索 + Editor.Message.request('scene', 'query-node-tree').then((tree: any) => { + const foundNode = this.searchNodeInTree(tree, name); + if (foundNode) { + resolve({ + success: true, + data: { + uuid: foundNode.uuid, + name: foundNode.name, + path: this.getNodePath(foundNode) + } + }); + } else { + resolve({ success: false, error: `Node '${name}' not found` }); + } + }).catch((err: Error) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'findNodeByName', + args: [name] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + resolve(result); + }).catch((err2: Error) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + + private searchNodeInTree(node: any, targetName: string): any { + if (node.name === targetName) { + return node; + } + + if (node.children) { + for (const child of node.children) { + const found = this.searchNodeInTree(child, targetName); + if (found) { + return found; + } + } + } + + return null; + } + + private async getAllNodes(): Promise { + return new Promise((resolve) => { + // 尝试查询场景节点树 + Editor.Message.request('scene', 'query-node-tree').then((tree: any) => { + const nodes: any[] = []; + + const traverseTree = (node: any) => { + nodes.push({ + uuid: node.uuid, + name: node.name, + type: node.type, + active: node.active, + path: this.getNodePath(node) + }); + + if (node.children) { + for (const child of node.children) { + traverseTree(child); + } + } + }; + + if (tree && tree.children) { + traverseTree(tree); + } + + resolve({ + success: true, + data: { + totalNodes: nodes.length, + nodes: nodes + } + }); + }).catch((err: Error) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getAllNodes', + args: [] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + resolve(result); + }).catch((err2: Error) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + + private getNodePath(node: any): string { + const path = [node.name]; + let current = node.parent; + while (current && current.name !== 'Canvas') { + path.unshift(current.name); + current = current.parent; + } + return path.join('/'); + } + + private async setNodeProperty(uuid: string, property: string, value: any): Promise { + return new Promise((resolve) => { + // 尝试直接使用 Editor API 设置节点属性 + Editor.Message.request('scene', 'set-property', { + uuid: uuid, + path: property, + dump: { + value: value + } + }).then(() => { + resolve({ + success: true, + message: `Property '${property}' updated successfully` + }); + }).catch((err: Error) => { + // 如果直接设置失败,尝试使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'setNodeProperty', + args: [uuid, property, value] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + resolve(result); + }).catch((err2: Error) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + + private async deleteNode(uuid: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'remove-node', { uuid: uuid }).then(() => { + resolve({ + success: true, + message: 'Node deleted successfully' + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async moveNode(nodeUuid: string, newParentUuid: string, siblingIndex: number = -1): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'move-node', { + uuid: nodeUuid, + parent: newParentUuid, + index: siblingIndex + }).then(() => { + resolve({ + success: true, + message: 'Node moved successfully' + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async duplicateNode(uuid: string, includeChildren: boolean = true): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'duplicate-node', uuid).then((result: any) => { + resolve({ + success: true, + data: { + newUuid: result.uuid, + message: 'Node duplicated successfully' + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } +} \ No newline at end of file diff --git a/source/tools/prefab-tools.ts b/source/tools/prefab-tools.ts new file mode 100644 index 0000000..84e3c4b --- /dev/null +++ b/source/tools/prefab-tools.ts @@ -0,0 +1,359 @@ +import { ToolDefinition, ToolResponse, ToolExecutor, PrefabInfo } from '../types'; + +export class PrefabTools implements ToolExecutor { + getTools(): ToolDefinition[] { + return [ + { + name: 'get_prefab_list', + description: 'Get all prefabs in the project', + inputSchema: { + type: 'object', + properties: { + folder: { + type: 'string', + description: 'Folder path to search (optional)', + default: 'db://assets' + } + } + } + }, + { + name: 'load_prefab', + description: 'Load a prefab by path', + inputSchema: { + type: 'object', + properties: { + prefabPath: { + type: 'string', + description: 'Prefab asset path' + } + }, + required: ['prefabPath'] + } + }, + { + name: 'instantiate_prefab', + description: 'Instantiate a prefab in the scene', + inputSchema: { + type: 'object', + properties: { + prefabPath: { + type: 'string', + description: 'Prefab asset path' + }, + parentUuid: { + type: 'string', + description: 'Parent node UUID (optional)' + }, + position: { + type: 'object', + description: 'Initial position', + properties: { + x: { type: 'number' }, + y: { type: 'number' }, + z: { type: 'number' } + } + } + }, + required: ['prefabPath'] + } + }, + { + name: 'create_prefab', + description: 'Create a prefab from a node', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Source node UUID' + }, + savePath: { + type: 'string', + description: 'Path to save the prefab' + }, + prefabName: { + type: 'string', + description: 'Prefab name' + } + }, + required: ['nodeUuid', 'savePath', 'prefabName'] + } + }, + { + name: 'create_prefab_from_node', + description: 'Create a prefab from a node (alias for create_prefab)', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Source node UUID' + }, + prefabPath: { + type: 'string', + description: 'Path to save the prefab' + } + }, + required: ['nodeUuid', 'prefabPath'] + } + }, + { + name: 'update_prefab', + description: 'Update an existing prefab', + inputSchema: { + type: 'object', + properties: { + prefabPath: { + type: 'string', + description: 'Prefab asset path' + }, + nodeUuid: { + type: 'string', + description: 'Node UUID with changes' + } + }, + required: ['prefabPath', 'nodeUuid'] + } + }, + { + name: 'revert_prefab', + description: 'Revert prefab instance to original', + inputSchema: { + type: 'object', + properties: { + nodeUuid: { + type: 'string', + description: 'Prefab instance node UUID' + } + }, + required: ['nodeUuid'] + } + }, + { + name: 'get_prefab_info', + description: 'Get detailed prefab information', + inputSchema: { + type: 'object', + properties: { + prefabPath: { + type: 'string', + description: 'Prefab asset path' + } + }, + required: ['prefabPath'] + } + } + ]; + } + + async execute(toolName: string, args: any): Promise { + switch (toolName) { + case 'get_prefab_list': + return await this.getPrefabList(args.folder); + case 'load_prefab': + return await this.loadPrefab(args.prefabPath); + case 'instantiate_prefab': + return await this.instantiatePrefab(args); + case 'create_prefab': + return await this.createPrefab(args); + case 'create_prefab_from_node': + return await this.createPrefabFromNode(args); + case 'update_prefab': + return await this.updatePrefab(args.prefabPath, args.nodeUuid); + case 'revert_prefab': + return await this.revertPrefab(args.nodeUuid); + case 'get_prefab_info': + return await this.getPrefabInfo(args.prefabPath); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + + private async getPrefabList(folder: string = 'db://assets'): Promise { + return new Promise((resolve) => { + const pattern = folder.endsWith('/') ? + `${folder}**/*.prefab` : `${folder}/**/*.prefab`; + + Editor.Message.request('asset-db', 'query-assets', { + pattern: pattern + }).then((results: any[]) => { + const prefabs: PrefabInfo[] = results.map(asset => ({ + name: asset.name, + path: asset.url, + uuid: asset.uuid, + folder: asset.url.substring(0, asset.url.lastIndexOf('/')) + })); + resolve({ success: true, data: prefabs }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async loadPrefab(prefabPath: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { + if (!assetInfo) { + throw new Error('Prefab not found'); + } + + return Editor.Message.request('scene', 'load-asset', { + uuid: assetInfo.uuid + }); + }).then((prefabData: any) => { + resolve({ + success: true, + data: { + uuid: prefabData.uuid, + name: prefabData.name, + message: 'Prefab loaded successfully' + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async instantiatePrefab(args: any): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', args.prefabPath).then((assetInfo: any) => { + if (!assetInfo) { + throw new Error('Prefab not found'); + } + + const instantiateData: any = { + prefab: assetInfo.uuid + }; + + if (args.parentUuid) { + instantiateData.parent = args.parentUuid; + } + + if (args.position) { + instantiateData.position = args.position; + } + + return Editor.Message.request('scene', 'instantiate-prefab', instantiateData); + }).then((result: any) => { + resolve({ + success: true, + data: { + nodeUuid: result.uuid, + name: result.name, + message: 'Prefab instantiated successfully' + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async createPrefab(args: any): Promise { + return new Promise((resolve) => { + // 支持 prefabPath 和 savePath 两种参数名 + const pathParam = args.prefabPath || args.savePath; + if (!pathParam) { + resolve({ + success: false, + error: 'Missing prefab path parameter. Please provide either prefabPath or savePath.' + }); + return; + } + + const fullPath = pathParam.endsWith('.prefab') ? + pathParam : `${pathParam}/${args.prefabName || 'NewPrefab'}.prefab`; + + // 预制体创建需要特殊的Editor API支持 + // 目前Cocos Creator 3.8的MCP插件环境下,预制体创建功能受限 + + resolve({ + success: false, + error: 'Prefab creation is not supported in the current MCP plugin environment', + instruction: 'Please create prefabs manually by dragging nodes from the scene to the assets folder in the Cocos Creator editor', + data: { + nodeUuid: args.nodeUuid, + requestedPath: fullPath, + suggestion: 'You can manually drag the node from the scene to the assets folder to create a prefab.' + } + }); + }); + } + + private async updatePrefab(prefabPath: string, nodeUuid: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { + if (!assetInfo) { + throw new Error('Prefab not found'); + } + + return Editor.Message.request('scene', 'apply-prefab', { + node: nodeUuid, + prefab: assetInfo.uuid + }); + }).then(() => { + resolve({ + success: true, + message: 'Prefab updated successfully' + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async revertPrefab(nodeUuid: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'revert-prefab', { + node: nodeUuid + }).then(() => { + resolve({ + success: true, + message: 'Prefab instance reverted successfully' + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async getPrefabInfo(prefabPath: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', prefabPath).then((assetInfo: any) => { + if (!assetInfo) { + throw new Error('Prefab not found'); + } + + return Editor.Message.request('asset-db', 'query-asset-meta', assetInfo.uuid); + }).then((metaInfo: any) => { + const info: PrefabInfo = { + name: metaInfo.name, + uuid: metaInfo.uuid, + path: prefabPath, + folder: prefabPath.substring(0, prefabPath.lastIndexOf('/')), + createTime: metaInfo.createTime, + modifyTime: metaInfo.modifyTime, + dependencies: metaInfo.depends || [] + }; + resolve({ success: true, data: info }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async createPrefabFromNode(args: any): Promise { + // 从 prefabPath 提取名称 + const prefabPath = args.prefabPath; + const prefabName = prefabPath.split('/').pop()?.replace('.prefab', '') || 'NewPrefab'; + + // 调用原来的 createPrefab 方法 + return await this.createPrefab({ + nodeUuid: args.nodeUuid, + savePath: prefabPath, + prefabName: prefabName + }); + } +} \ No newline at end of file diff --git a/source/tools/preferences-tools.ts b/source/tools/preferences-tools.ts new file mode 100644 index 0000000..0820beb --- /dev/null +++ b/source/tools/preferences-tools.ts @@ -0,0 +1,163 @@ +import { ToolDefinition, ToolResponse, ToolExecutor } from '../types'; + +export class PreferencesTools implements ToolExecutor { + getTools(): ToolDefinition[] { + return [ + { + name: 'get_preferences', + description: 'Get editor preferences', + inputSchema: { + type: 'object', + properties: { + key: { + type: 'string', + description: 'Specific preference key to get (optional)' + } + } + } + }, + { + name: 'set_preferences', + description: 'Set editor preferences', + inputSchema: { + type: 'object', + properties: { + key: { + type: 'string', + description: 'Preference key to set' + }, + value: { + description: 'Preference value to set' + } + }, + required: ['key', 'value'] + } + }, + { + name: 'get_global_preferences', + description: 'Get global editor preferences', + inputSchema: { + type: 'object', + properties: { + key: { + type: 'string', + description: 'Global preference key to get (optional)' + } + } + } + }, + { + name: 'set_global_preferences', + description: 'Set global editor preferences', + inputSchema: { + type: 'object', + properties: { + key: { + type: 'string', + description: 'Global preference key to set' + }, + value: { + description: 'Global preference value to set' + } + }, + required: ['key', 'value'] + } + }, + { + name: 'get_recent_projects', + description: 'Get recently opened projects', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'clear_recent_projects', + description: 'Clear recently opened projects list', + inputSchema: { + type: 'object', + properties: {} + } + } + ]; + } + + async execute(toolName: string, args: any): Promise { + switch (toolName) { + case 'get_preferences': + return await this.getPreferences(args.key); + case 'set_preferences': + return await this.setPreferences(args.key, args.value); + case 'get_global_preferences': + return await this.getGlobalPreferences(args.key); + case 'set_global_preferences': + return await this.setGlobalPreferences(args.key, args.value); + case 'get_recent_projects': + return await this.getRecentProjects(); + case 'clear_recent_projects': + return await this.clearRecentProjects(); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + + private async getPreferences(key?: string): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Preferences API is not supported through MCP', + instruction: 'Please access preferences through the editor menu: Edit > Preferences or use the preferences panel in the editor' + }); + }); + } + + private async setPreferences(key: string, value: any): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Preferences API is not supported through MCP', + instruction: 'Please modify preferences through the editor menu: Edit > Preferences or use the preferences panel in the editor' + }); + }); + } + + private async getGlobalPreferences(key?: string): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Global preferences API is not supported through MCP', + instruction: 'Please access global preferences through the editor menu: Edit > Preferences or use the preferences panel in the editor' + }); + }); + } + + private async setGlobalPreferences(key: string, value: any): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Global preferences API is not supported through MCP', + instruction: 'Please modify global preferences through the editor menu: Edit > Preferences or use the preferences panel in the editor' + }); + }); + } + + private async getRecentProjects(): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Recent projects API is not supported through MCP', + instruction: 'Please check recent projects through the editor menu: File > Recent Projects or the start screen' + }); + }); + } + + private async clearRecentProjects(): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Recent projects API is not supported through MCP', + instruction: 'Please clear recent projects through the editor menu: File > Recent Projects or the start screen' + }); + }); + } +} \ No newline at end of file diff --git a/source/tools/project-tools.ts b/source/tools/project-tools.ts new file mode 100644 index 0000000..a5903c0 --- /dev/null +++ b/source/tools/project-tools.ts @@ -0,0 +1,890 @@ +import { ToolDefinition, ToolResponse, ToolExecutor, ProjectInfo, AssetInfo } from '../types'; +import * as fs from 'fs'; +import * as path from 'path'; + +export class ProjectTools implements ToolExecutor { + getTools(): ToolDefinition[] { + return [ + { + name: 'run_project', + description: 'Run the project in preview mode', + inputSchema: { + type: 'object', + properties: { + platform: { + type: 'string', + description: 'Target platform', + enum: ['browser', 'simulator', 'preview'], + default: 'browser' + } + } + } + }, + { + name: 'build_project', + description: 'Build the project', + inputSchema: { + type: 'object', + properties: { + platform: { + type: 'string', + description: 'Build platform', + enum: ['web-mobile', 'web-desktop', 'ios', 'android', 'windows', 'mac'] + }, + debug: { + type: 'boolean', + description: 'Debug build', + default: true + } + }, + required: ['platform'] + } + }, + { + name: 'get_project_info', + description: 'Get project information', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_project_settings', + description: 'Get project settings', + inputSchema: { + type: 'object', + properties: { + category: { + type: 'string', + description: 'Settings category', + enum: ['general', 'physics', 'render', 'assets'], + default: 'general' + } + } + } + }, + { + name: 'refresh_assets', + description: 'Refresh asset database', + inputSchema: { + type: 'object', + properties: { + folder: { + type: 'string', + description: 'Specific folder to refresh (optional)' + } + } + } + }, + { + name: 'import_asset', + description: 'Import an asset file', + inputSchema: { + type: 'object', + properties: { + sourcePath: { + type: 'string', + description: 'Source file path' + }, + targetFolder: { + type: 'string', + description: 'Target folder in assets' + } + }, + required: ['sourcePath', 'targetFolder'] + } + }, + { + name: 'get_asset_info', + description: 'Get asset information', + inputSchema: { + type: 'object', + properties: { + assetPath: { + type: 'string', + description: 'Asset path (db://assets/...)' + } + }, + required: ['assetPath'] + } + }, + { + name: 'get_assets', + description: 'Get assets by type', + inputSchema: { + type: 'object', + properties: { + type: { + type: 'string', + description: 'Asset type filter', + enum: ['all', 'scene', 'prefab', 'script', 'texture', 'material', 'mesh', 'audio', 'animation'], + default: 'all' + }, + folder: { + type: 'string', + description: 'Folder to search in', + default: 'db://assets' + } + } + } + }, + { + name: 'get_build_settings', + description: 'Get build settings - shows current limitations', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'open_build_panel', + description: 'Open the build panel in the editor', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'check_builder_status', + description: 'Check if builder worker is ready', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'start_preview_server', + description: 'Start preview server', + inputSchema: { + type: 'object', + properties: { + port: { + type: 'number', + description: 'Preview server port', + default: 7456 + } + } + } + }, + { + name: 'stop_preview_server', + description: 'Stop preview server', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'create_asset', + description: 'Create a new asset file or folder', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL (e.g., db://assets/newfile.json)' + }, + content: { + type: 'string', + description: 'File content (null for folder)', + default: null + }, + overwrite: { + type: 'boolean', + description: 'Overwrite existing file', + default: false + } + }, + required: ['url'] + } + }, + { + name: 'copy_asset', + description: 'Copy an asset to another location', + inputSchema: { + type: 'object', + properties: { + source: { + type: 'string', + description: 'Source asset URL' + }, + target: { + type: 'string', + description: 'Target location URL' + }, + overwrite: { + type: 'boolean', + description: 'Overwrite existing file', + default: false + } + }, + required: ['source', 'target'] + } + }, + { + name: 'move_asset', + description: 'Move an asset to another location', + inputSchema: { + type: 'object', + properties: { + source: { + type: 'string', + description: 'Source asset URL' + }, + target: { + type: 'string', + description: 'Target location URL' + }, + overwrite: { + type: 'boolean', + description: 'Overwrite existing file', + default: false + } + }, + required: ['source', 'target'] + } + }, + { + name: 'delete_asset', + description: 'Delete an asset', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL to delete' + } + }, + required: ['url'] + } + }, + { + name: 'save_asset', + description: 'Save asset content', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL' + }, + content: { + type: 'string', + description: 'Asset content' + } + }, + required: ['url', 'content'] + } + }, + { + name: 'reimport_asset', + description: 'Reimport an asset', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL to reimport' + } + }, + required: ['url'] + } + }, + { + name: 'query_asset_path', + description: 'Get asset disk path', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL' + } + }, + required: ['url'] + } + }, + { + name: 'query_asset_uuid', + description: 'Get asset UUID from URL', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Asset URL' + } + }, + required: ['url'] + } + }, + { + name: 'query_asset_url', + description: 'Get asset URL from UUID', + inputSchema: { + type: 'object', + properties: { + uuid: { + type: 'string', + description: 'Asset UUID' + } + }, + required: ['uuid'] + } + } + ]; + } + + async execute(toolName: string, args: any): Promise { + switch (toolName) { + case 'run_project': + return await this.runProject(args.platform); + case 'build_project': + return await this.buildProject(args); + case 'get_project_info': + return await this.getProjectInfo(); + case 'get_project_settings': + return await this.getProjectSettings(args.category); + case 'refresh_assets': + return await this.refreshAssets(args.folder); + case 'import_asset': + return await this.importAsset(args.sourcePath, args.targetFolder); + case 'get_asset_info': + return await this.getAssetInfo(args.assetPath); + case 'get_assets': + return await this.getAssets(args.type, args.folder); + case 'get_build_settings': + return await this.getBuildSettings(); + case 'open_build_panel': + return await this.openBuildPanel(); + case 'check_builder_status': + return await this.checkBuilderStatus(); + case 'start_preview_server': + return await this.startPreviewServer(args.port); + case 'stop_preview_server': + return await this.stopPreviewServer(); + case 'create_asset': + return await this.createAsset(args.url, args.content, args.overwrite); + case 'copy_asset': + return await this.copyAsset(args.source, args.target, args.overwrite); + case 'move_asset': + return await this.moveAsset(args.source, args.target, args.overwrite); + case 'delete_asset': + return await this.deleteAsset(args.url); + case 'save_asset': + return await this.saveAsset(args.url, args.content); + case 'reimport_asset': + return await this.reimportAsset(args.url); + case 'query_asset_path': + return await this.queryAssetPath(args.url); + case 'query_asset_uuid': + return await this.queryAssetUuid(args.url); + case 'query_asset_url': + return await this.queryAssetUrl(args.uuid); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + + private async runProject(platform: string = 'browser'): Promise { + return new Promise((resolve) => { + const previewConfig = { + platform: platform, + scenes: [] // Will use current scene + }; + + Editor.Message.request('preview', 'start', previewConfig).then(() => { + resolve({ + success: true, + message: `Project is running in ${platform} mode` + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async buildProject(args: any): Promise { + return new Promise((resolve) => { + const buildOptions = { + platform: args.platform, + debug: args.debug !== false, + sourceMaps: args.debug !== false, + buildPath: `build/${args.platform}` + }; + + Editor.Message.request('builder', 'build', buildOptions).then(() => { + resolve({ + success: true, + message: `Project built for ${args.platform}`, + data: { buildPath: buildOptions.buildPath } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async getProjectInfo(): Promise { + return new Promise((resolve) => { + const info: ProjectInfo = { + name: Editor.Project.name, + path: Editor.Project.path, + uuid: Editor.Project.uuid, + version: (Editor.Project as any).version || '1.0.0', + cocosVersion: (Editor as any).versions?.cocos || 'Unknown' + }; + + Editor.Message.request('project', 'query-info').then((additionalInfo: any) => { + Object.assign(info, additionalInfo); + resolve({ success: true, data: info }); + }).catch(() => { + // Return basic info even if detailed query fails + resolve({ success: true, data: info }); + }); + }); + } + + private async getProjectSettings(category: string = 'general'): Promise { + return new Promise((resolve) => { + // 使用正确的 project API 查询项目配置 + const configMap: Record = { + general: 'project', + physics: 'physics', + render: 'render', + assets: 'asset-db' + }; + + const configName = configMap[category] || 'project'; + + Editor.Message.request('project', 'query-config', configName).then((settings: any) => { + resolve({ + success: true, + data: { + category: category, + config: settings, + message: `${category} settings retrieved successfully` + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async refreshAssets(folder?: string): Promise { + return new Promise((resolve) => { + // 使用正确的 asset-db API 刷新资源 + const targetPath = folder || 'db://assets'; + + Editor.Message.request('asset-db', 'refresh-asset', targetPath).then(() => { + resolve({ + success: true, + message: `Assets refreshed in: ${targetPath}` + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async importAsset(sourcePath: string, targetFolder: string): Promise { + return new Promise((resolve) => { + if (!fs.existsSync(sourcePath)) { + resolve({ success: false, error: 'Source file not found' }); + return; + } + + const fileName = path.basename(sourcePath); + const targetPath = targetFolder.startsWith('db://') ? + targetFolder : `db://assets/${targetFolder}`; + + Editor.Message.request('asset-db', 'import-asset', sourcePath, `${targetPath}/${fileName}`).then((result: any) => { + resolve({ + success: true, + data: { + uuid: result.uuid, + path: result.url, + message: `Asset imported: ${fileName}` + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async getAssetInfo(assetPath: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-asset-info', assetPath).then((assetInfo: any) => { + if (!assetInfo) { + throw new Error('Asset not found'); + } + + const info: AssetInfo = { + name: assetInfo.name, + uuid: assetInfo.uuid, + path: assetInfo.url, + type: assetInfo.type, + size: assetInfo.size, + isDirectory: assetInfo.isDirectory + }; + + if (assetInfo.meta) { + info.meta = { + ver: assetInfo.meta.ver, + importer: assetInfo.meta.importer + }; + } + + resolve({ success: true, data: info }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async getAssets(type: string = 'all', folder: string = 'db://assets'): Promise { + return new Promise((resolve) => { + let pattern = `${folder}/**/*`; + + // 添加类型过滤 + if (type !== 'all') { + const typeExtensions: Record = { + 'scene': '.scene', + 'prefab': '.prefab', + 'script': '.{ts,js}', + 'texture': '.{png,jpg,jpeg,gif,tga,bmp,psd}', + 'material': '.mtl', + 'mesh': '.{fbx,obj,dae}', + 'audio': '.{mp3,ogg,wav,m4a}', + 'animation': '.{anim,clip}' + }; + + const extension = typeExtensions[type]; + if (extension) { + pattern = `${folder}/**/*${extension}`; + } + } + + Editor.Message.request('asset-db', 'query-assets', { + pattern: pattern + }).then((results: any[]) => { + const assets = results.map(asset => ({ + name: asset.name, + uuid: asset.uuid, + path: asset.url, + type: asset.type, + size: asset.size || 0, + isDirectory: asset.isDirectory || false + })); + + resolve({ + success: true, + data: { + type: type, + folder: folder, + count: assets.length, + assets: assets + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async getBuildSettings(): Promise { + return new Promise((resolve) => { + // 检查构建器是否准备就绪 + Editor.Message.request('builder', 'query-worker-ready').then((ready: boolean) => { + resolve({ + success: true, + data: { + builderReady: ready, + message: 'Build settings are limited in MCP plugin environment', + availableActions: [ + 'Open build panel with open_build_panel', + 'Check builder status with check_builder_status', + 'Start preview server with start_preview_server', + 'Stop preview server with stop_preview_server' + ], + limitation: 'Full build configuration requires direct Editor UI access' + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async openBuildPanel(): Promise { + return new Promise((resolve) => { + Editor.Message.request('builder', 'open').then(() => { + resolve({ + success: true, + message: 'Build panel opened successfully' + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async checkBuilderStatus(): Promise { + return new Promise((resolve) => { + Editor.Message.request('builder', 'query-worker-ready').then((ready: boolean) => { + resolve({ + success: true, + data: { + ready: ready, + status: ready ? 'Builder worker is ready' : 'Builder worker is not ready', + message: 'Builder status checked successfully' + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async startPreviewServer(port: number = 7456): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Preview server control is not supported through MCP API', + instruction: 'Please start the preview server manually using the editor menu: Project > Preview, or use the preview panel in the editor' + }); + }); + } + + private async stopPreviewServer(): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Preview server control is not supported through MCP API', + instruction: 'Please stop the preview server manually using the preview panel in the editor' + }); + }); + } + + private async createAsset(url: string, content: string | null = null, overwrite: boolean = false): Promise { + return new Promise((resolve) => { + const options = { + overwrite: overwrite, + rename: !overwrite + }; + + Editor.Message.request('asset-db', 'create-asset', url, content, options).then((result: any) => { + if (result && result.uuid) { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + message: content === null ? 'Folder created successfully' : 'File created successfully' + } + }); + } else { + resolve({ + success: true, + data: { + url: url, + message: content === null ? 'Folder created successfully' : 'File created successfully' + } + }); + } + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async copyAsset(source: string, target: string, overwrite: boolean = false): Promise { + return new Promise((resolve) => { + const options = { + overwrite: overwrite, + rename: !overwrite + }; + + Editor.Message.request('asset-db', 'copy-asset', source, target, options).then((result: any) => { + if (result && result.uuid) { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + message: 'Asset copied successfully' + } + }); + } else { + resolve({ + success: true, + data: { + source: source, + target: target, + message: 'Asset copied successfully' + } + }); + } + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async moveAsset(source: string, target: string, overwrite: boolean = false): Promise { + return new Promise((resolve) => { + const options = { + overwrite: overwrite, + rename: !overwrite + }; + + Editor.Message.request('asset-db', 'move-asset', source, target, options).then((result: any) => { + if (result && result.uuid) { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + message: 'Asset moved successfully' + } + }); + } else { + resolve({ + success: true, + data: { + source: source, + target: target, + message: 'Asset moved successfully' + } + }); + } + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async deleteAsset(url: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'delete-asset', url).then((result: any) => { + resolve({ + success: true, + data: { + url: url, + message: 'Asset deleted successfully' + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async saveAsset(url: string, content: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'save-asset', url, content).then((result: any) => { + if (result && result.uuid) { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + message: 'Asset saved successfully' + } + }); + } else { + resolve({ + success: true, + data: { + url: url, + message: 'Asset saved successfully' + } + }); + } + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async reimportAsset(url: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'reimport-asset', url).then(() => { + resolve({ + success: true, + data: { + url: url, + message: 'Asset reimported successfully' + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async queryAssetPath(url: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-path', url).then((path: string | null) => { + if (path) { + resolve({ + success: true, + data: { + url: url, + path: path, + message: 'Asset path retrieved successfully' + } + }); + } else { + resolve({ success: false, error: 'Asset path not found' }); + } + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async queryAssetUuid(url: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-uuid', url).then((uuid: string | null) => { + if (uuid) { + resolve({ + success: true, + data: { + url: url, + uuid: uuid, + message: 'Asset UUID retrieved successfully' + } + }); + } else { + resolve({ success: false, error: 'Asset UUID not found' }); + } + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async queryAssetUrl(uuid: string): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-url', uuid).then((url: string | null) => { + if (url) { + resolve({ + success: true, + data: { + uuid: uuid, + url: url, + message: 'Asset URL retrieved successfully' + } + }); + } else { + resolve({ success: false, error: 'Asset URL not found' }); + } + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } +} \ No newline at end of file diff --git a/source/tools/scene-tools.ts b/source/tools/scene-tools.ts new file mode 100644 index 0000000..69917e8 --- /dev/null +++ b/source/tools/scene-tools.ts @@ -0,0 +1,465 @@ +import { ToolDefinition, ToolResponse, ToolExecutor, SceneInfo } from '../types'; + +export class SceneTools implements ToolExecutor { + getTools(): ToolDefinition[] { + return [ + { + name: 'get_current_scene', + description: 'Get current scene information', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_scene_list', + description: 'Get all scenes in the project', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'open_scene', + description: 'Open a scene by path', + inputSchema: { + type: 'object', + properties: { + scenePath: { + type: 'string', + description: 'The scene file path' + } + }, + required: ['scenePath'] + } + }, + { + name: 'save_scene', + description: 'Save current scene', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'create_scene', + description: 'Create a new scene asset', + inputSchema: { + type: 'object', + properties: { + sceneName: { + type: 'string', + description: 'Name of the new scene' + }, + savePath: { + type: 'string', + description: 'Path to save the scene (e.g., db://assets/scenes/NewScene.scene)' + } + }, + required: ['sceneName', 'savePath'] + } + }, + { + name: 'save_scene_as', + description: 'Save scene as new file', + inputSchema: { + type: 'object', + properties: { + path: { + type: 'string', + description: 'Path to save the scene' + } + }, + required: ['path'] + } + }, + { + name: 'close_scene', + description: 'Close current scene', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_scene_hierarchy', + description: 'Get the complete hierarchy of current scene', + inputSchema: { + type: 'object', + properties: { + includeComponents: { + type: 'boolean', + description: 'Include component information', + default: false + } + } + } + } + ]; + } + + async execute(toolName: string, args: any): Promise { + switch (toolName) { + case 'get_current_scene': + return await this.getCurrentScene(); + case 'get_scene_list': + return await this.getSceneList(); + case 'open_scene': + return await this.openScene(args.scenePath); + case 'save_scene': + return await this.saveScene(); + case 'create_scene': + return await this.createScene(args.sceneName, args.savePath); + case 'save_scene_as': + return await this.saveSceneAs(args.path); + case 'close_scene': + return await this.closeScene(); + case 'get_scene_hierarchy': + return await this.getSceneHierarchy(args.includeComponents); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + + private async getCurrentScene(): Promise { + return new Promise((resolve) => { + // 直接使用 query-node-tree 来获取场景信息(这个方法已经验证可用) + Editor.Message.request('scene', 'query-node-tree').then((tree: any) => { + if (tree && tree.uuid) { + resolve({ + success: true, + data: { + name: tree.name || 'Current Scene', + uuid: tree.uuid, + type: tree.type || 'cc.Scene', + active: tree.active !== undefined ? tree.active : true, + nodeCount: tree.children ? tree.children.length : 0 + } + }); + } else { + resolve({ success: false, error: 'No scene data available' }); + } + }).catch((err: Error) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getCurrentSceneInfo', + args: [] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + resolve(result); + }).catch((err2: Error) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + + private async getSceneList(): Promise { + return new Promise((resolve) => { + Editor.Message.request('asset-db', 'query-assets', { + pattern: 'db://assets/**/*.scene' + }).then((results: any[]) => { + const scenes: SceneInfo[] = results.map(asset => ({ + name: asset.name, + path: asset.url, + uuid: asset.uuid + })); + resolve({ success: true, data: scenes }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async openScene(scenePath: string): Promise { + return new Promise((resolve) => { + // 首先获取场景的UUID + Editor.Message.request('asset-db', 'query-uuid', scenePath).then((uuid: string | null) => { + if (!uuid) { + throw new Error('Scene not found'); + } + + // 使用正确的 scene API 打开场景 (需要UUID) + return Editor.Message.request('scene', 'open-scene', uuid); + }).then(() => { + resolve({ success: true, message: `Scene opened: ${scenePath}` }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async saveScene(): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'save-scene').then(() => { + resolve({ success: true, message: 'Scene saved successfully' }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async createScene(sceneName: string, savePath: string): Promise { + return new Promise((resolve) => { + // 确保路径以.scene结尾 + const fullPath = savePath.endsWith('.scene') ? savePath : `${savePath}/${sceneName}.scene`; + + // 使用正确的Cocos Creator 3.8场景格式 + const sceneContent = JSON.stringify([ + { + "__type__": "cc.SceneAsset", + "_name": sceneName, + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "scene": { + "__id__": 1 + } + }, + { + "__type__": "cc.Scene", + "_name": sceneName, + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [], + "_active": true, + "_components": [], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "autoReleaseAssets": false, + "_globals": { + "__id__": 2 + }, + "_id": "scene" + }, + { + "__type__": "cc.SceneGlobals", + "ambient": { + "__id__": 3 + }, + "skybox": { + "__id__": 4 + }, + "fog": { + "__id__": 5 + }, + "octree": { + "__id__": 6 + } + }, + { + "__type__": "cc.AmbientInfo", + "_skyColorHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833 + }, + "_skyColor": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833 + }, + "_skyIllumHDR": 20000, + "_skyIllum": 20000, + "_groundAlbedoHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_groundAlbedo": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + } + }, + { + "__type__": "cc.SkyboxInfo", + "_envLightingType": 0, + "_envmapHDR": null, + "_envmap": null, + "_envmapLodCount": 0, + "_diffuseMapHDR": null, + "_diffuseMap": null, + "_enabled": false, + "_useHDR": true, + "_editableMaterial": null, + "_reflectionHDR": null, + "_reflectionMap": null, + "_rotationAngle": 0 + }, + { + "__type__": "cc.FogInfo", + "_type": 0, + "_fogColor": { + "__type__": "cc.Color", + "r": 200, + "g": 200, + "b": 200, + "a": 255 + }, + "_enabled": false, + "_fogDensity": 0.3, + "_fogStart": 0.5, + "_fogEnd": 300, + "_fogAtten": 5, + "_fogTop": 1.5, + "_fogRange": 1.2, + "_accurate": false + }, + { + "__type__": "cc.OctreeInfo", + "_enabled": false, + "_minPos": { + "__type__": "cc.Vec3", + "x": -1024, + "y": -1024, + "z": -1024 + }, + "_maxPos": { + "__type__": "cc.Vec3", + "x": 1024, + "y": 1024, + "z": 1024 + }, + "_depth": 8 + } + ], null, 2); + + Editor.Message.request('asset-db', 'create-asset', fullPath, sceneContent).then((result: any) => { + resolve({ + success: true, + data: { + uuid: result.uuid, + url: result.url, + name: sceneName, + message: `Scene '${sceneName}' created successfully` + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async getSceneHierarchy(includeComponents: boolean = false): Promise { + return new Promise((resolve) => { + // 优先尝试使用 Editor API 查询场景节点树 + Editor.Message.request('scene', 'query-node-tree').then((tree: any) => { + if (tree) { + const hierarchy = this.buildHierarchy(tree, includeComponents); + resolve({ + success: true, + data: hierarchy + }); + } else { + resolve({ success: false, error: 'No scene hierarchy available' }); + } + }).catch((err: Error) => { + // 备用方案:使用场景脚本 + const options = { + name: 'cocos-mcp-server', + method: 'getSceneHierarchy', + args: [includeComponents] + }; + + Editor.Message.request('scene', 'execute-scene-script', options).then((result: any) => { + resolve(result); + }).catch((err2: Error) => { + resolve({ success: false, error: `Direct API failed: ${err.message}, Scene script failed: ${err2.message}` }); + }); + }); + }); + } + + private buildHierarchy(node: any, includeComponents: boolean): any { + const nodeInfo: any = { + uuid: node.uuid, + name: node.name, + type: node.type, + active: node.active, + children: [] + }; + + if (includeComponents && node.__comps__) { + nodeInfo.components = node.__comps__.map((comp: any) => ({ + type: comp.__type__ || 'Unknown', + enabled: comp.enabled !== undefined ? comp.enabled : true + })); + } + + if (node.children) { + nodeInfo.children = node.children.map((child: any) => + this.buildHierarchy(child, includeComponents) + ); + } + + return nodeInfo; + } + + private async saveSceneAs(path: string): Promise { + return new Promise((resolve) => { + // save-as-scene API 不接受路径参数,会弹出对话框让用户选择 + (Editor.Message.request as any)('scene', 'save-as-scene').then(() => { + resolve({ + success: true, + data: { + path: path, + message: `Scene save-as dialog opened` + } + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } + + private async closeScene(): Promise { + return new Promise((resolve) => { + Editor.Message.request('scene', 'close-scene').then(() => { + resolve({ + success: true, + message: 'Scene closed successfully' + }); + }).catch((err: Error) => { + resolve({ success: false, error: err.message }); + }); + }); + } +} \ No newline at end of file diff --git a/source/tools/server-tools.ts b/source/tools/server-tools.ts new file mode 100644 index 0000000..61c574e --- /dev/null +++ b/source/tools/server-tools.ts @@ -0,0 +1,247 @@ +import { ToolDefinition, ToolResponse, ToolExecutor } from '../types'; + +export class ServerTools implements ToolExecutor { + getTools(): ToolDefinition[] { + return [ + { + name: 'get_server_info', + description: 'Get server information', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'broadcast_custom_message', + description: 'Broadcast a custom message', + inputSchema: { + type: 'object', + properties: { + message: { + type: 'string', + description: 'Message name' + }, + data: { + description: 'Message data (optional)' + } + }, + required: ['message'] + } + }, + { + name: 'get_editor_version', + description: 'Get editor version information', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_project_name', + description: 'Get current project name', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_project_path', + description: 'Get current project path', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'get_project_uuid', + description: 'Get current project UUID', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'restart_editor', + description: 'Request to restart the editor', + inputSchema: { + type: 'object', + properties: {} + } + }, + { + name: 'quit_editor', + description: 'Request to quit the editor', + inputSchema: { + type: 'object', + properties: {} + } + } + ]; + } + + async execute(toolName: string, args: any): Promise { + switch (toolName) { + case 'get_server_info': + return await this.getServerInfo(); + case 'broadcast_custom_message': + return await this.broadcastCustomMessage(args.message, args.data); + case 'get_editor_version': + return await this.getEditorVersion(); + case 'get_project_name': + return await this.getProjectName(); + case 'get_project_path': + return await this.getProjectPath(); + case 'get_project_uuid': + return await this.getProjectUuid(); + case 'restart_editor': + return await this.restartEditor(); + case 'quit_editor': + return await this.quitEditor(); + default: + throw new Error(`Unknown tool: ${toolName}`); + } + } + + private async getServerInfo(): Promise { + return new Promise((resolve) => { + try { + const info = { + editorVersion: (Editor as any).versions?.editor || 'Unknown', + cocosVersion: (Editor as any).versions?.cocos || 'Unknown', + nodeVersion: process.version, + platform: process.platform, + arch: process.arch, + projectName: Editor.Project.name, + projectPath: Editor.Project.path, + projectUuid: Editor.Project.uuid + }; + + resolve({ + success: true, + data: { + server: info, + message: 'Server information retrieved successfully' + } + }); + } catch (err: any) { + resolve({ success: false, error: err.message }); + } + }); + } + + private async broadcastCustomMessage(message: string, data?: any): Promise { + return new Promise((resolve) => { + try { + if (data !== undefined) { + Editor.Message.broadcast(message, data); + } else { + Editor.Message.broadcast(message); + } + + resolve({ + success: true, + data: { + message: message, + data: data, + result: 'Message broadcasted successfully' + } + }); + } catch (err: any) { + resolve({ success: false, error: err.message }); + } + }); + } + + private async getEditorVersion(): Promise { + return new Promise((resolve) => { + try { + const version = { + editor: (Editor as any).versions?.editor || 'Unknown', + cocos: (Editor as any).versions?.cocos || 'Unknown', + node: process.version + }; + + resolve({ + success: true, + data: { + version: version, + message: 'Editor version retrieved successfully' + } + }); + } catch (err: any) { + resolve({ success: false, error: err.message }); + } + }); + } + + private async getProjectName(): Promise { + return new Promise((resolve) => { + try { + const name = Editor.Project.name; + resolve({ + success: true, + data: { + name: name, + message: 'Project name retrieved successfully' + } + }); + } catch (err: any) { + resolve({ success: false, error: err.message }); + } + }); + } + + private async getProjectPath(): Promise { + return new Promise((resolve) => { + try { + const path = Editor.Project.path; + resolve({ + success: true, + data: { + path: path, + message: 'Project path retrieved successfully' + } + }); + } catch (err: any) { + resolve({ success: false, error: err.message }); + } + }); + } + + private async getProjectUuid(): Promise { + return new Promise((resolve) => { + try { + const uuid = Editor.Project.uuid; + resolve({ + success: true, + data: { + uuid: uuid, + message: 'Project UUID retrieved successfully' + } + }); + } catch (err: any) { + resolve({ success: false, error: err.message }); + } + }); + } + + private async restartEditor(): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Editor restart is not supported through MCP API', + instruction: 'Please restart the editor manually or use the editor menu: File > Restart Editor' + }); + }); + } + + private async quitEditor(): Promise { + return new Promise((resolve) => { + resolve({ + success: false, + error: 'Editor quit is not supported through MCP API', + instruction: 'Please quit the editor manually or use the editor menu: File > Quit' + }); + }); + } +} \ No newline at end of file diff --git a/source/types/index.ts b/source/types/index.ts new file mode 100644 index 0000000..6bfc5d6 --- /dev/null +++ b/source/types/index.ts @@ -0,0 +1,124 @@ +export interface MCPServerSettings { + port: number; + autoStart: boolean; + enableDebugLog: boolean; + allowedOrigins: string[]; + maxConnections: number; +} + +export interface ServerStatus { + running: boolean; + port: number; + clients: number; +} + +export interface ToolDefinition { + name: string; + description: string; + inputSchema: any; +} + +export interface ToolResponse { + success: boolean; + data?: any; + message?: string; + error?: string; + instruction?: string; +} + +export interface NodeInfo { + uuid: string; + name: string; + active: boolean; + position?: { x: number; y: number; z: number }; + rotation?: { x: number; y: number; z: number }; + scale?: { x: number; y: number; z: number }; + parent?: string; + children?: string[]; + components?: ComponentInfo[]; + layer?: number; + mobility?: number; +} + +export interface ComponentInfo { + type: string; + enabled: boolean; + properties?: Record; +} + +export interface SceneInfo { + name: string; + uuid: string; + path: string; +} + +export interface PrefabInfo { + name: string; + uuid: string; + path: string; + folder: string; + createTime?: string; + modifyTime?: string; + dependencies?: string[]; +} + +export interface AssetInfo { + name: string; + uuid: string; + path: string; + type: string; + size?: number; + isDirectory: boolean; + meta?: { + ver: string; + importer: string; + }; +} + +export interface ProjectInfo { + name: string; + path: string; + uuid: string; + version: string; + cocosVersion: string; +} + +export interface ConsoleMessage { + timestamp: string; + type: 'log' | 'warn' | 'error' | 'info'; + message: string; + stack?: string; +} + +export interface PerformanceStats { + nodeCount: number; + componentCount: number; + drawCalls: number; + triangles: number; + memory: Record; +} + +export interface ValidationIssue { + type: 'error' | 'warning' | 'info'; + category: string; + message: string; + details?: any; + suggestion?: string; +} + +export interface ValidationResult { + valid: boolean; + issueCount: number; + issues: ValidationIssue[]; +} + +export interface MCPClient { + id: string; + lastActivity: Date; + userAgent?: string; +} + +export interface ToolExecutor { + getTools(): ToolDefinition[]; + execute(toolName: string, args: any): Promise; +} \ No newline at end of file diff --git a/static/icon.png b/static/icon.png new file mode 100644 index 0000000..cdda77f Binary files /dev/null and b/static/icon.png differ diff --git a/static/style/default/index.css b/static/style/default/index.css new file mode 100644 index 0000000..aaa857e --- /dev/null +++ b/static/style/default/index.css @@ -0,0 +1,65 @@ +.mcp-server-panel { + padding: 20px; + display: flex; + flex-direction: column; + height: 100%; + min-height: 100%; + max-height: 100%; + overflow-y: auto; + box-sizing: border-box; +} + +header h2 { + margin: 0 0 20px 0; + font-size: 18px; +} + +section { + margin-bottom: 20px; + padding: 15px; + background: var(--color-normal-fill-emphasis); + border-radius: 4px; +} + +section h3 { + margin: 0 0 15px 0; + font-size: 14px; + font-weight: 600; +} + +.status-value { + font-weight: 600; +} + +.status-value.running { + color: var(--color-success-fill); +} + +.status-value.stopped { + color: var(--color-warn-fill); +} + +.server-controls { + display: flex; + justify-content: center; + padding: 20px; +} + +.server-controls ui-button { + min-width: 150px; +} + +.connection-details { + margin-top: 10px; +} + +footer { + margin-top: auto; + padding-top: 20px; + display: flex; + justify-content: flex-end; +} + +ui-prop { + margin-bottom: 10px; +} \ No newline at end of file diff --git a/static/template/default/index.html b/static/template/default/index.html new file mode 100644 index 0000000..c3658ee --- /dev/null +++ b/static/template/default/index.html @@ -0,0 +1,64 @@ +
+
+

+
+ +
+

+
+ + + + + + + + +
+
+ +
+ +
+ +
+

+ + + + + + + + + + + + + + + + + + +
+ +
+

+
+ + + + + + +
+
+ + +
\ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100755 index 0000000..78832ec --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "./base.tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./source", + "types": [ + "node", + "@cocos/creator-types/editor", + ] + } +} \ No newline at end of file