614 lines
24 KiB
TypeScript
614 lines
24 KiB
TypeScript
import { ToolDefinition, ToolResponse, ToolExecutor } from '../types';
|
|
|
|
export class AssetAdvancedTools implements ToolExecutor {
|
|
getTools(): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: 'save_asset_meta',
|
|
description: 'Save asset meta information',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
urlOrUUID: {
|
|
type: 'string',
|
|
description: 'Asset URL or UUID'
|
|
},
|
|
content: {
|
|
type: 'string',
|
|
description: 'Asset meta serialized content string'
|
|
}
|
|
},
|
|
required: ['urlOrUUID', 'content']
|
|
}
|
|
},
|
|
{
|
|
name: 'generate_available_url',
|
|
description: 'Generate an available URL based on input URL',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
url: {
|
|
type: 'string',
|
|
description: 'Asset URL to generate available URL for'
|
|
}
|
|
},
|
|
required: ['url']
|
|
}
|
|
},
|
|
{
|
|
name: 'query_asset_db_ready',
|
|
description: 'Check if asset database is ready',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {}
|
|
}
|
|
},
|
|
{
|
|
name: 'open_asset_external',
|
|
description: 'Open asset with external program',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
urlOrUUID: {
|
|
type: 'string',
|
|
description: 'Asset URL or UUID to open'
|
|
}
|
|
},
|
|
required: ['urlOrUUID']
|
|
}
|
|
},
|
|
{
|
|
name: 'batch_import_assets',
|
|
description: 'Import multiple assets in batch',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
sourceDirectory: {
|
|
type: 'string',
|
|
description: 'Source directory path'
|
|
},
|
|
targetDirectory: {
|
|
type: 'string',
|
|
description: 'Target directory URL'
|
|
},
|
|
fileFilter: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'File extensions to include (e.g., [".png", ".jpg"])',
|
|
default: []
|
|
},
|
|
recursive: {
|
|
type: 'boolean',
|
|
description: 'Include subdirectories',
|
|
default: false
|
|
},
|
|
overwrite: {
|
|
type: 'boolean',
|
|
description: 'Overwrite existing files',
|
|
default: false
|
|
}
|
|
},
|
|
required: ['sourceDirectory', 'targetDirectory']
|
|
}
|
|
},
|
|
{
|
|
name: 'batch_delete_assets',
|
|
description: 'Delete multiple assets in batch',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
urls: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Array of asset URLs to delete'
|
|
}
|
|
},
|
|
required: ['urls']
|
|
}
|
|
},
|
|
{
|
|
name: 'validate_asset_references',
|
|
description: 'Validate asset references and find broken links',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
directory: {
|
|
type: 'string',
|
|
description: 'Directory to validate (default: entire project)',
|
|
default: 'db://assets'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'get_asset_dependencies',
|
|
description: 'Get asset dependency tree',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
urlOrUUID: {
|
|
type: 'string',
|
|
description: 'Asset URL or UUID'
|
|
},
|
|
direction: {
|
|
type: 'string',
|
|
description: 'Dependency direction',
|
|
enum: ['dependents', 'dependencies', 'both'],
|
|
default: 'dependencies'
|
|
}
|
|
},
|
|
required: ['urlOrUUID']
|
|
}
|
|
},
|
|
{
|
|
name: 'get_unused_assets',
|
|
description: 'Find unused assets in project',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
directory: {
|
|
type: 'string',
|
|
description: 'Directory to scan (default: entire project)',
|
|
default: 'db://assets'
|
|
},
|
|
excludeDirectories: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Directories to exclude from scan',
|
|
default: []
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'compress_textures',
|
|
description: 'Batch compress texture assets',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
directory: {
|
|
type: 'string',
|
|
description: 'Directory containing textures',
|
|
default: 'db://assets'
|
|
},
|
|
format: {
|
|
type: 'string',
|
|
description: 'Compression format',
|
|
enum: ['auto', 'jpg', 'png', 'webp'],
|
|
default: 'auto'
|
|
},
|
|
quality: {
|
|
type: 'number',
|
|
description: 'Compression quality (0.1-1.0)',
|
|
minimum: 0.1,
|
|
maximum: 1.0,
|
|
default: 0.8
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'export_asset_manifest',
|
|
description: 'Export asset manifest/inventory',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
directory: {
|
|
type: 'string',
|
|
description: 'Directory to export manifest for',
|
|
default: 'db://assets'
|
|
},
|
|
format: {
|
|
type: 'string',
|
|
description: 'Export format',
|
|
enum: ['json', 'csv', 'xml'],
|
|
default: 'json'
|
|
},
|
|
includeMetadata: {
|
|
type: 'boolean',
|
|
description: 'Include asset metadata',
|
|
default: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
async execute(toolName: string, args: any): Promise<ToolResponse> {
|
|
switch (toolName) {
|
|
case 'save_asset_meta':
|
|
return await this.saveAssetMeta(args.urlOrUUID, args.content);
|
|
case 'generate_available_url':
|
|
return await this.generateAvailableUrl(args.url);
|
|
case 'query_asset_db_ready':
|
|
return await this.queryAssetDbReady();
|
|
case 'open_asset_external':
|
|
return await this.openAssetExternal(args.urlOrUUID);
|
|
case 'batch_import_assets':
|
|
return await this.batchImportAssets(args);
|
|
case 'batch_delete_assets':
|
|
return await this.batchDeleteAssets(args.urls);
|
|
case 'validate_asset_references':
|
|
return await this.validateAssetReferences(args.directory);
|
|
case 'get_asset_dependencies':
|
|
return await this.getAssetDependencies(args.urlOrUUID, args.direction);
|
|
case 'get_unused_assets':
|
|
return await this.getUnusedAssets(args.directory, args.excludeDirectories);
|
|
case 'compress_textures':
|
|
return await this.compressTextures(args.directory, args.format, args.quality);
|
|
case 'export_asset_manifest':
|
|
return await this.exportAssetManifest(args.directory, args.format, args.includeMetadata);
|
|
default:
|
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
}
|
|
}
|
|
|
|
private async saveAssetMeta(urlOrUUID: string, content: string): Promise<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
Editor.Message.request('asset-db', 'save-asset-meta', urlOrUUID, content).then((result: any) => {
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
uuid: result?.uuid,
|
|
url: result?.url,
|
|
message: 'Asset meta saved successfully'
|
|
}
|
|
});
|
|
}).catch((err: Error) => {
|
|
resolve({ success: false, error: err.message });
|
|
});
|
|
});
|
|
}
|
|
|
|
private async generateAvailableUrl(url: string): Promise<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
Editor.Message.request('asset-db', 'generate-available-url', url).then((availableUrl: string) => {
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
originalUrl: url,
|
|
availableUrl: availableUrl,
|
|
message: availableUrl === url ?
|
|
'URL is available' :
|
|
'Generated new available URL'
|
|
}
|
|
});
|
|
}).catch((err: Error) => {
|
|
resolve({ success: false, error: err.message });
|
|
});
|
|
});
|
|
}
|
|
|
|
private async queryAssetDbReady(): Promise<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
Editor.Message.request('asset-db', 'query-ready').then((ready: boolean) => {
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
ready: ready,
|
|
message: ready ? 'Asset database is ready' : 'Asset database is not ready'
|
|
}
|
|
});
|
|
}).catch((err: Error) => {
|
|
resolve({ success: false, error: err.message });
|
|
});
|
|
});
|
|
}
|
|
|
|
private async openAssetExternal(urlOrUUID: string): Promise<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
Editor.Message.request('asset-db', 'open-asset', urlOrUUID).then(() => {
|
|
resolve({
|
|
success: true,
|
|
message: 'Asset opened with external program'
|
|
});
|
|
}).catch((err: Error) => {
|
|
resolve({ success: false, error: err.message });
|
|
});
|
|
});
|
|
}
|
|
|
|
private async batchImportAssets(args: any): Promise<ToolResponse> {
|
|
return new Promise(async (resolve) => {
|
|
try {
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
if (!fs.existsSync(args.sourceDirectory)) {
|
|
resolve({ success: false, error: 'Source directory does not exist' });
|
|
return;
|
|
}
|
|
|
|
const files = this.getFilesFromDirectory(
|
|
args.sourceDirectory,
|
|
args.fileFilter || [],
|
|
args.recursive || false
|
|
);
|
|
|
|
const importResults: any[] = [];
|
|
let successCount = 0;
|
|
let errorCount = 0;
|
|
|
|
for (const filePath of files) {
|
|
try {
|
|
const fileName = path.basename(filePath);
|
|
const targetPath = `${args.targetDirectory}/${fileName}`;
|
|
|
|
const result = await Editor.Message.request('asset-db', 'import-asset',
|
|
filePath, targetPath, {
|
|
overwrite: args.overwrite || false,
|
|
rename: !(args.overwrite || false)
|
|
});
|
|
|
|
importResults.push({
|
|
source: filePath,
|
|
target: targetPath,
|
|
success: true,
|
|
uuid: result?.uuid
|
|
});
|
|
successCount++;
|
|
} catch (err: any) {
|
|
importResults.push({
|
|
source: filePath,
|
|
success: false,
|
|
error: err.message
|
|
});
|
|
errorCount++;
|
|
}
|
|
}
|
|
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
totalFiles: files.length,
|
|
successCount: successCount,
|
|
errorCount: errorCount,
|
|
results: importResults,
|
|
message: `Batch import completed: ${successCount} success, ${errorCount} errors`
|
|
}
|
|
});
|
|
} catch (err: any) {
|
|
resolve({ success: false, error: err.message });
|
|
}
|
|
});
|
|
}
|
|
|
|
private getFilesFromDirectory(dirPath: string, fileFilter: string[], recursive: boolean): string[] {
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const files: string[] = [];
|
|
|
|
const items = fs.readdirSync(dirPath);
|
|
|
|
for (const item of items) {
|
|
const fullPath = path.join(dirPath, item);
|
|
const stat = fs.statSync(fullPath);
|
|
|
|
if (stat.isFile()) {
|
|
if (fileFilter.length === 0 || fileFilter.some(ext => item.toLowerCase().endsWith(ext.toLowerCase()))) {
|
|
files.push(fullPath);
|
|
}
|
|
} else if (stat.isDirectory() && recursive) {
|
|
files.push(...this.getFilesFromDirectory(fullPath, fileFilter, recursive));
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
private async batchDeleteAssets(urls: string[]): Promise<ToolResponse> {
|
|
return new Promise(async (resolve) => {
|
|
try {
|
|
const deleteResults: any[] = [];
|
|
let successCount = 0;
|
|
let errorCount = 0;
|
|
|
|
for (const url of urls) {
|
|
try {
|
|
await Editor.Message.request('asset-db', 'delete-asset', url);
|
|
deleteResults.push({
|
|
url: url,
|
|
success: true
|
|
});
|
|
successCount++;
|
|
} catch (err: any) {
|
|
deleteResults.push({
|
|
url: url,
|
|
success: false,
|
|
error: err.message
|
|
});
|
|
errorCount++;
|
|
}
|
|
}
|
|
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
totalAssets: urls.length,
|
|
successCount: successCount,
|
|
errorCount: errorCount,
|
|
results: deleteResults,
|
|
message: `Batch delete completed: ${successCount} success, ${errorCount} errors`
|
|
}
|
|
});
|
|
} catch (err: any) {
|
|
resolve({ success: false, error: err.message });
|
|
}
|
|
});
|
|
}
|
|
|
|
private async validateAssetReferences(directory: string = 'db://assets'): Promise<ToolResponse> {
|
|
return new Promise(async (resolve) => {
|
|
try {
|
|
// Get all assets in directory
|
|
const assets = await Editor.Message.request('asset-db', 'query-assets', { pattern: `${directory}/**/*` });
|
|
|
|
const brokenReferences: any[] = [];
|
|
const validReferences: any[] = [];
|
|
|
|
for (const asset of assets) {
|
|
try {
|
|
const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', asset.url);
|
|
if (assetInfo) {
|
|
validReferences.push({
|
|
url: asset.url,
|
|
uuid: asset.uuid,
|
|
name: asset.name
|
|
});
|
|
}
|
|
} catch (err) {
|
|
brokenReferences.push({
|
|
url: asset.url,
|
|
uuid: asset.uuid,
|
|
name: asset.name,
|
|
error: (err as Error).message
|
|
});
|
|
}
|
|
}
|
|
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
directory: directory,
|
|
totalAssets: assets.length,
|
|
validReferences: validReferences.length,
|
|
brokenReferences: brokenReferences.length,
|
|
brokenAssets: brokenReferences,
|
|
message: `Validation completed: ${brokenReferences.length} broken references found`
|
|
}
|
|
});
|
|
} catch (err: any) {
|
|
resolve({ success: false, error: err.message });
|
|
}
|
|
});
|
|
}
|
|
|
|
private async getAssetDependencies(urlOrUUID: string, direction: string = 'dependencies'): Promise<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
// Note: This would require scene analysis or additional APIs not available in current documentation
|
|
resolve({
|
|
success: false,
|
|
error: 'Asset dependency analysis requires additional APIs not available in current Cocos Creator MCP implementation. Consider using the Editor UI for dependency analysis.'
|
|
});
|
|
});
|
|
}
|
|
|
|
private async getUnusedAssets(directory: string = 'db://assets', excludeDirectories: string[] = []): Promise<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
// Note: This would require comprehensive project analysis
|
|
resolve({
|
|
success: false,
|
|
error: 'Unused asset detection requires comprehensive project analysis not available in current Cocos Creator MCP implementation. Consider using the Editor UI or third-party tools for unused asset detection.'
|
|
});
|
|
});
|
|
}
|
|
|
|
private async compressTextures(directory: string = 'db://assets', format: string = 'auto', quality: number = 0.8): Promise<ToolResponse> {
|
|
return new Promise((resolve) => {
|
|
// Note: Texture compression would require image processing APIs
|
|
resolve({
|
|
success: false,
|
|
error: 'Texture compression requires image processing capabilities not available in current Cocos Creator MCP implementation. Use the Editor\'s built-in texture compression settings or external tools.'
|
|
});
|
|
});
|
|
}
|
|
|
|
private async exportAssetManifest(directory: string = 'db://assets', format: string = 'json', includeMetadata: boolean = true): Promise<ToolResponse> {
|
|
return new Promise(async (resolve) => {
|
|
try {
|
|
const assets = await Editor.Message.request('asset-db', 'query-assets', { pattern: `${directory}/**/*` });
|
|
|
|
const manifest: any[] = [];
|
|
|
|
for (const asset of assets) {
|
|
const manifestEntry: any = {
|
|
name: asset.name,
|
|
url: asset.url,
|
|
uuid: asset.uuid,
|
|
type: asset.type,
|
|
size: (asset as any).size || 0,
|
|
isDirectory: asset.isDirectory || false
|
|
};
|
|
|
|
if (includeMetadata) {
|
|
try {
|
|
const assetInfo = await Editor.Message.request('asset-db', 'query-asset-info', asset.url);
|
|
if (assetInfo && assetInfo.meta) {
|
|
manifestEntry.meta = assetInfo.meta;
|
|
}
|
|
} catch (err) {
|
|
// Skip metadata if not available
|
|
}
|
|
}
|
|
|
|
manifest.push(manifestEntry);
|
|
}
|
|
|
|
let exportData: string;
|
|
switch (format) {
|
|
case 'json':
|
|
exportData = JSON.stringify(manifest, null, 2);
|
|
break;
|
|
case 'csv':
|
|
exportData = this.convertToCSV(manifest);
|
|
break;
|
|
case 'xml':
|
|
exportData = this.convertToXML(manifest);
|
|
break;
|
|
default:
|
|
exportData = JSON.stringify(manifest, null, 2);
|
|
}
|
|
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
directory: directory,
|
|
format: format,
|
|
assetCount: manifest.length,
|
|
includeMetadata: includeMetadata,
|
|
manifest: exportData,
|
|
message: `Asset manifest exported with ${manifest.length} assets`
|
|
}
|
|
});
|
|
} catch (err: any) {
|
|
resolve({ success: false, error: err.message });
|
|
}
|
|
});
|
|
}
|
|
|
|
private convertToCSV(data: any[]): string {
|
|
if (data.length === 0) return '';
|
|
|
|
const headers = Object.keys(data[0]);
|
|
const csvRows = [headers.join(',')];
|
|
|
|
for (const row of data) {
|
|
const values = headers.map(header => {
|
|
const value = row[header];
|
|
return typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
});
|
|
csvRows.push(values.join(','));
|
|
}
|
|
|
|
return csvRows.join('\n');
|
|
}
|
|
|
|
private convertToXML(data: any[]): string {
|
|
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<assets>\n';
|
|
|
|
for (const item of data) {
|
|
xml += ' <asset>\n';
|
|
for (const [key, value] of Object.entries(item)) {
|
|
const xmlValue = typeof value === 'object' ?
|
|
JSON.stringify(value) :
|
|
String(value).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
xml += ` <${key}>${xmlValue}</${key}>\n`;
|
|
}
|
|
xml += ' </asset>\n';
|
|
}
|
|
|
|
xml += '</assets>';
|
|
return xml;
|
|
}
|
|
} |