1098 lines
42 KiB
TypeScript
1098 lines
42 KiB
TypeScript
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']
|
|
}
|
|
},
|
|
{
|
|
name: 'find_asset_by_name',
|
|
description: 'Find assets by name (supports partial matching and multiple results)',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
name: {
|
|
type: 'string',
|
|
description: 'Asset name to search for (supports partial matching)'
|
|
},
|
|
exactMatch: {
|
|
type: 'boolean',
|
|
description: 'Whether to use exact name matching',
|
|
default: false
|
|
},
|
|
assetType: {
|
|
type: 'string',
|
|
description: 'Filter by asset type',
|
|
enum: ['all', 'scene', 'prefab', 'script', 'texture', 'material', 'mesh', 'audio', 'animation', 'spriteFrame'],
|
|
default: 'all'
|
|
},
|
|
folder: {
|
|
type: 'string',
|
|
description: 'Folder to search in',
|
|
default: 'db://assets'
|
|
},
|
|
maxResults: {
|
|
type: 'number',
|
|
description: 'Maximum number of results to return',
|
|
default: 20,
|
|
minimum: 1,
|
|
maximum: 100
|
|
}
|
|
},
|
|
required: ['name']
|
|
}
|
|
},
|
|
{
|
|
name: 'get_asset_details',
|
|
description: 'Get detailed asset information including spriteFrame sub-assets',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
assetPath: {
|
|
type: 'string',
|
|
description: 'Asset path (db://assets/...)'
|
|
},
|
|
includeSubAssets: {
|
|
type: 'boolean',
|
|
description: 'Include sub-assets like spriteFrame, texture',
|
|
default: true
|
|
}
|
|
},
|
|
required: ['assetPath']
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
async execute(toolName: string, args: any): Promise<ToolResponse> {
|
|
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);
|
|
case 'find_asset_by_name':
|
|
return await this.findAssetByName(args);
|
|
case 'get_asset_details':
|
|
return await this.getAssetDetails(args.assetPath, args.includeSubAssets);
|
|
default:
|
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
}
|
|
}
|
|
|
|
private async runProject(platform: string = 'browser'): Promise<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
const previewConfig = {
|
|
platform: platform,
|
|
scenes: [] // Will use current scene
|
|
};
|
|
|
|
// Note: Preview module is not documented in official API
|
|
// Using fallback approach - open build panel as alternative
|
|
Editor.Message.request('builder', 'open').then(() => {
|
|
resolve({
|
|
success: true,
|
|
message: `Build panel opened. Preview functionality requires manual setup.`
|
|
});
|
|
}).catch((err: Error) => {
|
|
resolve({ success: false, error: err.message });
|
|
});
|
|
});
|
|
}
|
|
|
|
private async buildProject(args: any): Promise<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
const buildOptions = {
|
|
platform: args.platform,
|
|
debug: args.debug !== false,
|
|
sourceMaps: args.debug !== false,
|
|
buildPath: `build/${args.platform}`
|
|
};
|
|
|
|
// Note: Builder module only supports 'open' and 'query-worker-ready'
|
|
// Building requires manual interaction through the build panel
|
|
Editor.Message.request('builder', 'open').then(() => {
|
|
resolve({
|
|
success: true,
|
|
message: `Build panel opened for ${args.platform}. Please configure and start build manually.`,
|
|
data: {
|
|
platform: args.platform,
|
|
instruction: "Use the build panel to configure and start the build process"
|
|
}
|
|
});
|
|
}).catch((err: Error) => {
|
|
resolve({ success: false, error: err.message });
|
|
});
|
|
});
|
|
}
|
|
|
|
private async getProjectInfo(): Promise<ToolResponse> {
|
|
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'
|
|
};
|
|
|
|
// Note: 'query-info' API doesn't exist, using 'query-config' instead
|
|
Editor.Message.request('project', 'query-config', 'project').then((additionalInfo: any) => {
|
|
if (additionalInfo) {
|
|
Object.assign(info, { config: 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<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
// 使用正确的 project API 查询项目配置
|
|
const configMap: Record<string, string> = {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
let pattern = `${folder}/**/*`;
|
|
|
|
// 添加类型过滤
|
|
if (type !== 'all') {
|
|
const typeExtensions: Record<string, string> = {
|
|
'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}`;
|
|
}
|
|
}
|
|
|
|
// Note: query-assets API parameters corrected based on documentation
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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<ToolResponse> {
|
|
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 });
|
|
});
|
|
});
|
|
}
|
|
|
|
private async findAssetByName(args: any): Promise<ToolResponse> {
|
|
const { name, exactMatch = false, assetType = 'all', folder = 'db://assets', maxResults = 20 } = args;
|
|
|
|
return new Promise(async (resolve) => {
|
|
try {
|
|
// Get all assets in the specified folder
|
|
const allAssetsResponse = await this.getAssets(assetType, folder);
|
|
if (!allAssetsResponse.success || !allAssetsResponse.data) {
|
|
resolve({
|
|
success: false,
|
|
error: `Failed to get assets: ${allAssetsResponse.error}`
|
|
});
|
|
return;
|
|
}
|
|
|
|
const allAssets = allAssetsResponse.data.assets as any[];
|
|
let matchedAssets: any[] = [];
|
|
|
|
// Search for matching assets
|
|
for (const asset of allAssets) {
|
|
const assetName = asset.name;
|
|
let matches = false;
|
|
|
|
if (exactMatch) {
|
|
matches = assetName === name;
|
|
} else {
|
|
matches = assetName.toLowerCase().includes(name.toLowerCase());
|
|
}
|
|
|
|
if (matches) {
|
|
// Get detailed asset info if needed
|
|
try {
|
|
const detailResponse = await this.getAssetInfo(asset.path);
|
|
if (detailResponse.success) {
|
|
matchedAssets.push({
|
|
...asset,
|
|
details: detailResponse.data
|
|
});
|
|
} else {
|
|
matchedAssets.push(asset);
|
|
}
|
|
} catch {
|
|
matchedAssets.push(asset);
|
|
}
|
|
|
|
if (matchedAssets.length >= maxResults) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
searchTerm: name,
|
|
exactMatch,
|
|
assetType,
|
|
folder,
|
|
totalFound: matchedAssets.length,
|
|
maxResults,
|
|
assets: matchedAssets,
|
|
message: `Found ${matchedAssets.length} assets matching '${name}'`
|
|
}
|
|
});
|
|
|
|
} catch (error: any) {
|
|
resolve({
|
|
success: false,
|
|
error: `Asset search failed: ${error.message}`
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
private async getAssetDetails(assetPath: string, includeSubAssets: boolean = true): Promise<ToolResponse> {
|
|
return new Promise(async (resolve) => {
|
|
try {
|
|
// Get basic asset info
|
|
const assetInfoResponse = await this.getAssetInfo(assetPath);
|
|
if (!assetInfoResponse.success) {
|
|
resolve(assetInfoResponse);
|
|
return;
|
|
}
|
|
|
|
const assetInfo = assetInfoResponse.data;
|
|
const detailedInfo: any = {
|
|
...assetInfo,
|
|
subAssets: []
|
|
};
|
|
|
|
if (includeSubAssets && assetInfo) {
|
|
// For image assets, try to get spriteFrame and texture sub-assets
|
|
if (assetInfo.type === 'cc.ImageAsset' || assetPath.match(/\.(png|jpg|jpeg|gif|tga|bmp|psd)$/i)) {
|
|
// Generate common sub-asset UUIDs
|
|
const baseUuid = assetInfo.uuid;
|
|
const possibleSubAssets = [
|
|
{ type: 'spriteFrame', uuid: `${baseUuid}@f9941`, suffix: '@f9941' },
|
|
{ type: 'texture', uuid: `${baseUuid}@6c48a`, suffix: '@6c48a' },
|
|
{ type: 'texture2D', uuid: `${baseUuid}@6c48a`, suffix: '@6c48a' }
|
|
];
|
|
|
|
for (const subAsset of possibleSubAssets) {
|
|
try {
|
|
// Try to get URL for the sub-asset to verify it exists
|
|
const subAssetUrl = await Editor.Message.request('asset-db', 'query-url', subAsset.uuid);
|
|
if (subAssetUrl) {
|
|
detailedInfo.subAssets.push({
|
|
type: subAsset.type,
|
|
uuid: subAsset.uuid,
|
|
url: subAssetUrl,
|
|
suffix: subAsset.suffix
|
|
});
|
|
}
|
|
} catch {
|
|
// Sub-asset doesn't exist, skip it
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
assetPath,
|
|
includeSubAssets,
|
|
...detailedInfo,
|
|
message: `Asset details retrieved. Found ${detailedInfo.subAssets.length} sub-assets.`
|
|
}
|
|
});
|
|
|
|
} catch (error: any) {
|
|
resolve({
|
|
success: false,
|
|
error: `Failed to get asset details: ${error.message}`
|
|
});
|
|
}
|
|
});
|
|
}
|
|
} |