unreal-engine-mcp-server 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -1
- package/.github/release-drafter-config.yml +51 -0
- package/.github/workflows/greetings.yml +5 -1
- package/.github/workflows/labeler.yml +2 -1
- package/.github/workflows/publish-mcp.yml +1 -0
- package/.github/workflows/release-drafter.yml +1 -1
- package/.github/workflows/release.yml +3 -3
- package/CHANGELOG.md +71 -0
- package/CONTRIBUTING.md +1 -1
- package/GEMINI.md +115 -0
- package/Public/Plugin_setup_guide.mp4 +0 -0
- package/README.md +166 -200
- package/dist/config.d.ts +0 -1
- package/dist/config.js +0 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +4 -0
- package/dist/graphql/loaders.d.ts +64 -0
- package/dist/graphql/loaders.js +117 -0
- package/dist/graphql/resolvers.d.ts +3 -3
- package/dist/graphql/resolvers.js +33 -30
- package/dist/graphql/server.js +3 -1
- package/dist/graphql/types.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +13 -2
- package/dist/server-setup.d.ts +0 -1
- package/dist/server-setup.js +0 -40
- package/dist/tools/actors.d.ts +40 -24
- package/dist/tools/actors.js +8 -2
- package/dist/tools/assets.d.ts +19 -71
- package/dist/tools/assets.js +28 -22
- package/dist/tools/base-tool.d.ts +4 -4
- package/dist/tools/base-tool.js +1 -1
- package/dist/tools/blueprint.d.ts +33 -61
- package/dist/tools/consolidated-tool-handlers.js +96 -110
- package/dist/tools/dynamic-handler-registry.d.ts +11 -9
- package/dist/tools/dynamic-handler-registry.js +17 -95
- package/dist/tools/editor.d.ts +19 -193
- package/dist/tools/editor.js +8 -0
- package/dist/tools/environment.d.ts +8 -14
- package/dist/tools/foliage.d.ts +18 -143
- package/dist/tools/foliage.js +4 -2
- package/dist/tools/handlers/actor-handlers.js +0 -5
- package/dist/tools/handlers/asset-handlers.js +454 -454
- package/dist/tools/landscape.d.ts +16 -116
- package/dist/tools/landscape.js +7 -3
- package/dist/tools/level.d.ts +22 -103
- package/dist/tools/level.js +24 -16
- package/dist/tools/lighting.js +5 -1
- package/dist/tools/materials.js +5 -1
- package/dist/tools/niagara.js +37 -2
- package/dist/tools/performance.d.ts +0 -1
- package/dist/tools/performance.js +0 -1
- package/dist/tools/physics.js +5 -1
- package/dist/tools/sequence.d.ts +24 -24
- package/dist/tools/sequence.js +13 -0
- package/dist/tools/ui.d.ts +0 -2
- package/dist/types/automation-responses.d.ts +115 -0
- package/dist/types/automation-responses.js +2 -0
- package/dist/types/responses.d.ts +249 -0
- package/dist/types/responses.js +2 -0
- package/dist/types/tool-interfaces.d.ts +135 -135
- package/dist/utils/command-validator.js +3 -2
- package/dist/utils/path-security.d.ts +2 -0
- package/dist/utils/path-security.js +24 -0
- package/dist/utils/response-factory.d.ts +4 -4
- package/dist/utils/response-factory.js +15 -21
- package/docs/Migration-Guide-v0.5.0.md +1 -9
- package/docs/testing-guide.md +2 -2
- package/package.json +12 -6
- package/scripts/run-all-tests.mjs +25 -20
- package/server.json +3 -2
- package/src/config.ts +1 -1
- package/src/constants.ts +7 -0
- package/src/graphql/loaders.ts +244 -0
- package/src/graphql/resolvers.ts +47 -49
- package/src/graphql/server.ts +3 -1
- package/src/graphql/types.ts +3 -0
- package/src/index.ts +15 -2
- package/src/resources/assets.ts +5 -4
- package/src/server-setup.ts +3 -37
- package/src/tools/actors.ts +36 -28
- package/src/tools/animation.ts +1 -0
- package/src/tools/assets.ts +74 -63
- package/src/tools/base-tool.ts +3 -3
- package/src/tools/blueprint.ts +59 -59
- package/src/tools/consolidated-tool-handlers.ts +129 -150
- package/src/tools/dynamic-handler-registry.ts +22 -140
- package/src/tools/editor.ts +39 -26
- package/src/tools/environment.ts +21 -27
- package/src/tools/foliage.ts +28 -25
- package/src/tools/handlers/actor-handlers.ts +2 -8
- package/src/tools/handlers/asset-handlers.ts +484 -484
- package/src/tools/handlers/sequence-handlers.ts +1 -1
- package/src/tools/landscape.ts +34 -28
- package/src/tools/level.ts +96 -76
- package/src/tools/lighting.ts +6 -1
- package/src/tools/materials.ts +8 -2
- package/src/tools/niagara.ts +44 -2
- package/src/tools/performance.ts +1 -2
- package/src/tools/physics.ts +7 -1
- package/src/tools/sequence.ts +41 -25
- package/src/tools/ui.ts +0 -2
- package/src/types/automation-responses.ts +119 -0
- package/src/types/responses.ts +355 -0
- package/src/types/tool-interfaces.ts +135 -135
- package/src/utils/command-validator.ts +3 -2
- package/src/utils/normalize.test.ts +162 -0
- package/src/utils/path-security.ts +43 -0
- package/src/utils/response-factory.ts +29 -24
- package/src/utils/safe-json.test.ts +90 -0
- package/src/utils/validation.test.ts +184 -0
- package/tests/test-animation.mjs +358 -33
- package/tests/test-asset-graph.mjs +311 -0
- package/tests/test-audio.mjs +314 -116
- package/tests/test-behavior-tree.mjs +327 -144
- package/tests/test-blueprint-graph.mjs +343 -12
- package/tests/test-control-editor.mjs +85 -53
- package/tests/test-graphql.mjs +58 -8
- package/tests/test-input.mjs +349 -0
- package/tests/test-inspect.mjs +291 -61
- package/tests/test-landscape.mjs +304 -48
- package/tests/test-lighting.mjs +428 -0
- package/tests/test-manage-level.mjs +70 -51
- package/tests/test-performance.mjs +539 -0
- package/tests/test-sequence.mjs +82 -46
- package/tests/test-system.mjs +72 -33
- package/tests/test-wasm.mjs +98 -8
- package/vitest.config.ts +35 -0
- package/.github/release-drafter.yml +0 -148
- package/dist/prompts/index.d.ts +0 -21
- package/dist/prompts/index.js +0 -217
- package/dist/tools/blueprint/helpers.d.ts +0 -29
- package/dist/tools/blueprint/helpers.js +0 -182
- package/src/prompts/index.ts +0 -249
- package/src/tools/blueprint/helpers.ts +0 -189
- package/tests/test-blueprint-events.mjs +0 -35
- package/tests/test-extra-tools.mjs +0 -38
- package/tests/test-render.mjs +0 -33
- package/tests/test-search-assets.mjs +0 -66
|
@@ -2,531 +2,531 @@ import { cleanObject } from '../../utils/safe-json.js';
|
|
|
2
2
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
3
3
|
import { executeAutomationRequest } from './common-handlers.js';
|
|
4
4
|
import { normalizeArgs } from './argument-helper.js';
|
|
5
|
+
import { ResponseFactory } from '../../utils/response-factory.js';
|
|
5
6
|
|
|
6
7
|
export async function handleAssetTools(action: string, args: any, tools: ITools) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
(Array.isArray(response.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
message += `: ${limitedAssets.map((a: any) => a.path || a.package || a.name).join(', ')}`;
|
|
42
|
-
|
|
43
|
-
if (folders.length > 0 && limitedAssets.length < params.limit) {
|
|
44
|
-
const remainingLimit = params.limit - limitedAssets.length;
|
|
45
|
-
if (remainingLimit > 0) {
|
|
46
|
-
const limitedFolders = folders.slice(0, remainingLimit);
|
|
47
|
-
if (limitedAssets.length > 0) message += ', ';
|
|
48
|
-
message += `Folders: [${limitedFolders.join(', ')}]`;
|
|
49
|
-
if (folders.length > remainingLimit) message += '...';
|
|
8
|
+
try {
|
|
9
|
+
switch (action) {
|
|
10
|
+
case 'list': {
|
|
11
|
+
// Route through C++ HandleListAssets for proper asset enumeration
|
|
12
|
+
const params = normalizeArgs(args, [
|
|
13
|
+
{ key: 'path', aliases: ['directory', 'assetPath'], default: '/Game' },
|
|
14
|
+
{ key: 'limit', default: 50 },
|
|
15
|
+
{ key: 'recursive', default: false },
|
|
16
|
+
{ key: 'depth', default: undefined }
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const recursive = params.recursive === true || (params.depth !== undefined && params.depth > 0);
|
|
20
|
+
|
|
21
|
+
const res = await executeAutomationRequest(tools, 'list', {
|
|
22
|
+
path: params.path,
|
|
23
|
+
recursive,
|
|
24
|
+
depth: params.depth
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
const response = res as any;
|
|
29
|
+
const assets = (Array.isArray(response.assets) ? response.assets :
|
|
30
|
+
(Array.isArray(response.result) ? response.result : (response.result?.assets || [])));
|
|
31
|
+
|
|
32
|
+
// New: Handle folders
|
|
33
|
+
const folders = Array.isArray(response.folders) ? response.folders : (response.result?.folders || []);
|
|
34
|
+
|
|
35
|
+
const totalCount = assets.length;
|
|
36
|
+
const limitedAssets = assets.slice(0, params.limit);
|
|
37
|
+
const remaining = Math.max(0, totalCount - params.limit);
|
|
38
|
+
|
|
39
|
+
let message = `Found ${totalCount} assets`;
|
|
40
|
+
if (folders.length > 0) {
|
|
41
|
+
message += ` and ${folders.length} folders`;
|
|
50
42
|
}
|
|
43
|
+
message += `: ${limitedAssets.map((a: any) => a.path || a.package || a.name).join(', ')}`;
|
|
44
|
+
|
|
45
|
+
if (folders.length > 0 && limitedAssets.length < params.limit) {
|
|
46
|
+
const remainingLimit = params.limit - limitedAssets.length;
|
|
47
|
+
if (remainingLimit > 0) {
|
|
48
|
+
const limitedFolders = folders.slice(0, remainingLimit);
|
|
49
|
+
if (limitedAssets.length > 0) message += ', ';
|
|
50
|
+
message += `Folders: [${limitedFolders.join(', ')}]`;
|
|
51
|
+
if (folders.length > remainingLimit) message += '...';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (remaining > 0) {
|
|
56
|
+
message += `... and ${remaining} others`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return ResponseFactory.success({
|
|
60
|
+
assets: limitedAssets,
|
|
61
|
+
folders: folders,
|
|
62
|
+
totalCount: totalCount,
|
|
63
|
+
count: limitedAssets.length
|
|
64
|
+
}, message);
|
|
51
65
|
}
|
|
66
|
+
case 'create_folder': {
|
|
67
|
+
const params = normalizeArgs(args, [
|
|
68
|
+
{ key: 'path', aliases: ['directoryPath'], required: true }
|
|
69
|
+
]);
|
|
70
|
+
const res = await tools.assetTools.createFolder(params.path);
|
|
71
|
+
return ResponseFactory.success(res, 'Folder created successfully');
|
|
72
|
+
}
|
|
73
|
+
case 'import': {
|
|
74
|
+
const params = normalizeArgs(args, [
|
|
75
|
+
{ key: 'sourcePath', required: true },
|
|
76
|
+
{ key: 'destinationPath', required: true },
|
|
77
|
+
{ key: 'overwrite', default: false },
|
|
78
|
+
{ key: 'save', default: true }
|
|
79
|
+
]);
|
|
52
80
|
|
|
53
|
-
|
|
54
|
-
|
|
81
|
+
const res = await tools.assetTools.importAsset({
|
|
82
|
+
sourcePath: params.sourcePath,
|
|
83
|
+
destinationPath: params.destinationPath,
|
|
84
|
+
overwrite: params.overwrite,
|
|
85
|
+
save: params.save
|
|
86
|
+
});
|
|
87
|
+
return ResponseFactory.success(res, 'Asset imported successfully');
|
|
55
88
|
}
|
|
89
|
+
case 'duplicate': {
|
|
90
|
+
const params = normalizeArgs(args, [
|
|
91
|
+
{ key: 'sourcePath', aliases: ['assetPath'], required: true },
|
|
92
|
+
{ key: 'destinationPath' },
|
|
93
|
+
{ key: 'newName' }
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
let destinationPath = params.destinationPath;
|
|
97
|
+
if (params.newName) {
|
|
98
|
+
if (!destinationPath) {
|
|
99
|
+
const lastSlash = params.sourcePath.lastIndexOf('/');
|
|
100
|
+
const parentDir = lastSlash > 0 ? params.sourcePath.substring(0, lastSlash) : '/Game';
|
|
101
|
+
destinationPath = `${parentDir}/${params.newName}`;
|
|
102
|
+
} else if (!destinationPath.endsWith(params.newName)) {
|
|
103
|
+
if (destinationPath.endsWith('/')) {
|
|
104
|
+
destinationPath = `${destinationPath}${params.newName}`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
56
108
|
|
|
57
|
-
return {
|
|
58
|
-
message: message,
|
|
59
|
-
assets: limitedAssets,
|
|
60
|
-
folders: folders,
|
|
61
|
-
totalCount: totalCount,
|
|
62
|
-
count: limitedAssets.length
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
case 'create_folder': {
|
|
66
|
-
const params = normalizeArgs(args, [
|
|
67
|
-
{ key: 'path', aliases: ['directoryPath'], required: true }
|
|
68
|
-
]);
|
|
69
|
-
const res = await tools.assetTools.createFolder(params.path);
|
|
70
|
-
return cleanObject(res);
|
|
71
|
-
}
|
|
72
|
-
case 'import': {
|
|
73
|
-
const params = normalizeArgs(args, [
|
|
74
|
-
{ key: 'sourcePath', required: true },
|
|
75
|
-
{ key: 'destinationPath', required: true },
|
|
76
|
-
{ key: 'overwrite', default: false },
|
|
77
|
-
{ key: 'save', default: true }
|
|
78
|
-
]);
|
|
79
|
-
|
|
80
|
-
const res = await tools.assetTools.importAsset({
|
|
81
|
-
sourcePath: params.sourcePath,
|
|
82
|
-
destinationPath: params.destinationPath,
|
|
83
|
-
overwrite: params.overwrite,
|
|
84
|
-
save: params.save
|
|
85
|
-
});
|
|
86
|
-
return cleanObject(res);
|
|
87
|
-
}
|
|
88
|
-
case 'duplicate': {
|
|
89
|
-
const params = normalizeArgs(args, [
|
|
90
|
-
{ key: 'sourcePath', aliases: ['assetPath'], required: true },
|
|
91
|
-
{ key: 'destinationPath' },
|
|
92
|
-
{ key: 'newName' }
|
|
93
|
-
]);
|
|
94
|
-
|
|
95
|
-
let destinationPath = params.destinationPath;
|
|
96
|
-
if (params.newName) {
|
|
97
109
|
if (!destinationPath) {
|
|
110
|
+
throw new Error('destinationPath or newName is required for duplicate action');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const res = await tools.assetTools.duplicateAsset({
|
|
114
|
+
sourcePath: params.sourcePath,
|
|
115
|
+
destinationPath
|
|
116
|
+
});
|
|
117
|
+
return ResponseFactory.success(res, 'Asset duplicated successfully');
|
|
118
|
+
}
|
|
119
|
+
case 'rename': {
|
|
120
|
+
const params = normalizeArgs(args, [
|
|
121
|
+
{ key: 'sourcePath', aliases: ['assetPath'], required: true },
|
|
122
|
+
{ key: 'destinationPath' },
|
|
123
|
+
{ key: 'newName' }
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
let destinationPath = params.destinationPath;
|
|
127
|
+
if (!destinationPath && params.newName) {
|
|
98
128
|
const lastSlash = params.sourcePath.lastIndexOf('/');
|
|
99
129
|
const parentDir = lastSlash > 0 ? params.sourcePath.substring(0, lastSlash) : '/Game';
|
|
100
130
|
destinationPath = `${parentDir}/${params.newName}`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!destinationPath) throw new Error('Missing destinationPath or newName');
|
|
134
|
+
|
|
135
|
+
const res: any = await tools.assetTools.renameAsset({
|
|
136
|
+
sourcePath: params.sourcePath,
|
|
137
|
+
destinationPath
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (res && res.success === false) {
|
|
141
|
+
const msg = (res.message || '').toLowerCase();
|
|
142
|
+
if (msg.includes('already exists') || msg.includes('exists')) {
|
|
143
|
+
return cleanObject({
|
|
144
|
+
success: false,
|
|
145
|
+
error: 'ASSET_ALREADY_EXISTS',
|
|
146
|
+
message: res.message || 'Asset already exists at destination',
|
|
147
|
+
sourcePath: params.sourcePath,
|
|
148
|
+
destinationPath
|
|
149
|
+
});
|
|
104
150
|
}
|
|
105
151
|
}
|
|
152
|
+
return cleanObject(res);
|
|
106
153
|
}
|
|
154
|
+
case 'move': {
|
|
155
|
+
const params = normalizeArgs(args, [
|
|
156
|
+
{ key: 'sourcePath', aliases: ['assetPath'], required: true },
|
|
157
|
+
{ key: 'destinationPath' }
|
|
158
|
+
]);
|
|
107
159
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
160
|
+
let destinationPath = params.destinationPath;
|
|
161
|
+
const assetName = params.sourcePath.split('/').pop();
|
|
162
|
+
if (assetName && destinationPath && !destinationPath.endsWith(assetName)) {
|
|
163
|
+
destinationPath = `${destinationPath.replace(/\/$/, '')}/${assetName}`;
|
|
164
|
+
}
|
|
111
165
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
case 'rename': {
|
|
119
|
-
const params = normalizeArgs(args, [
|
|
120
|
-
{ key: 'sourcePath', aliases: ['assetPath'], required: true },
|
|
121
|
-
{ key: 'destinationPath' },
|
|
122
|
-
{ key: 'newName' }
|
|
123
|
-
]);
|
|
124
|
-
|
|
125
|
-
let destinationPath = params.destinationPath;
|
|
126
|
-
if (!destinationPath && params.newName) {
|
|
127
|
-
const lastSlash = params.sourcePath.lastIndexOf('/');
|
|
128
|
-
const parentDir = lastSlash > 0 ? params.sourcePath.substring(0, lastSlash) : '/Game';
|
|
129
|
-
destinationPath = `${parentDir}/${params.newName}`;
|
|
166
|
+
const res = await tools.assetTools.moveAsset({
|
|
167
|
+
sourcePath: params.sourcePath,
|
|
168
|
+
destinationPath
|
|
169
|
+
});
|
|
170
|
+
return ResponseFactory.success(res, 'Asset moved successfully');
|
|
130
171
|
}
|
|
172
|
+
case 'delete_assets':
|
|
173
|
+
case 'delete_asset':
|
|
174
|
+
case 'delete': {
|
|
175
|
+
let paths: string[] = [];
|
|
176
|
+
if (Array.isArray(args.paths)) {
|
|
177
|
+
paths = args.paths;
|
|
178
|
+
} else if (Array.isArray(args.assetPaths)) {
|
|
179
|
+
paths = args.assetPaths;
|
|
180
|
+
} else {
|
|
181
|
+
const single = args.assetPath || args.path;
|
|
182
|
+
if (typeof single === 'string' && single.trim()) {
|
|
183
|
+
paths = [single.trim()];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
131
186
|
|
|
132
|
-
|
|
187
|
+
if (paths.length === 0) {
|
|
188
|
+
throw new Error('No paths provided for delete action');
|
|
189
|
+
}
|
|
133
190
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
191
|
+
const res = await tools.assetTools.deleteAssets({ paths });
|
|
192
|
+
return ResponseFactory.success(res, 'Assets deleted successfully');
|
|
193
|
+
}
|
|
194
|
+
case 'generate_lods': {
|
|
195
|
+
const params = normalizeArgs(args, [
|
|
196
|
+
{ key: 'assetPath', required: true },
|
|
197
|
+
{ key: 'lodCount', required: true }
|
|
198
|
+
]);
|
|
199
|
+
const res = await tools.assetTools.generateLODs({
|
|
200
|
+
assetPath: params.assetPath,
|
|
201
|
+
lodCount: params.lodCount
|
|
202
|
+
});
|
|
203
|
+
return ResponseFactory.success(res, 'LODs generated successfully');
|
|
204
|
+
}
|
|
205
|
+
case 'create_thumbnail': {
|
|
206
|
+
const params = normalizeArgs(args, [
|
|
207
|
+
{ key: 'assetPath', required: true },
|
|
208
|
+
{ key: 'width' },
|
|
209
|
+
{ key: 'height' }
|
|
210
|
+
]);
|
|
211
|
+
const res = await tools.assetTools.createThumbnail({
|
|
212
|
+
assetPath: params.assetPath,
|
|
213
|
+
width: params.width,
|
|
214
|
+
height: params.height
|
|
215
|
+
});
|
|
216
|
+
return ResponseFactory.success(res, 'Thumbnail created successfully');
|
|
217
|
+
}
|
|
218
|
+
case 'set_tags': {
|
|
219
|
+
try {
|
|
220
|
+
const params = normalizeArgs(args, [
|
|
221
|
+
{ key: 'assetPath', required: true },
|
|
222
|
+
{ key: 'tags', required: true }
|
|
223
|
+
]);
|
|
224
|
+
const res = await tools.assetTools.setTags({ assetPath: params.assetPath, tags: params.tags });
|
|
225
|
+
return ResponseFactory.success(res, 'Tags set successfully');
|
|
226
|
+
} catch (err: any) {
|
|
227
|
+
const message = String(err?.message || err || '').toLowerCase();
|
|
228
|
+
if (
|
|
229
|
+
message.includes('not_implemented') ||
|
|
230
|
+
message.includes('not implemented') ||
|
|
231
|
+
message.includes('unknown action') ||
|
|
232
|
+
message.includes('unknown subaction')
|
|
233
|
+
) {
|
|
234
|
+
return ResponseFactory.error('NOT_IMPLEMENTED', 'Asset tag writes are not implemented by the automation plugin.');
|
|
235
|
+
}
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
case 'get_metadata': {
|
|
240
|
+
const params = normalizeArgs(args, [
|
|
241
|
+
{ key: 'assetPath', required: true }
|
|
242
|
+
]);
|
|
243
|
+
const res: any = await tools.assetTools.getMetadata({ assetPath: params.assetPath });
|
|
244
|
+
const tags = res.tags || {};
|
|
245
|
+
const metadata = res.metadata || {};
|
|
246
|
+
const merged = { ...tags, ...metadata };
|
|
247
|
+
const tagCount = Object.keys(merged).length;
|
|
248
|
+
|
|
249
|
+
const cleanRes = cleanObject(res);
|
|
250
|
+
cleanRes.message = `Metadata retrieved (${tagCount} items)`;
|
|
251
|
+
cleanRes.tags = tags;
|
|
252
|
+
if (Object.keys(metadata).length > 0) {
|
|
253
|
+
cleanRes.metadata = metadata;
|
|
254
|
+
}
|
|
138
255
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
256
|
+
return ResponseFactory.success(cleanRes, cleanRes.message);
|
|
257
|
+
}
|
|
258
|
+
case 'set_metadata': {
|
|
259
|
+
const res = await executeAutomationRequest(tools, 'set_metadata', args);
|
|
260
|
+
return ResponseFactory.success(res, 'Metadata set successfully');
|
|
261
|
+
}
|
|
262
|
+
case 'validate':
|
|
263
|
+
case 'validate_asset': {
|
|
264
|
+
const params = normalizeArgs(args, [
|
|
265
|
+
{ key: 'assetPath', required: true }
|
|
266
|
+
]);
|
|
267
|
+
const res = await tools.assetTools.validate({ assetPath: params.assetPath });
|
|
268
|
+
return ResponseFactory.success(res, 'Asset validation complete');
|
|
269
|
+
}
|
|
270
|
+
case 'generate_report': {
|
|
271
|
+
const params = normalizeArgs(args, [
|
|
272
|
+
{ key: 'directory' },
|
|
273
|
+
{ key: 'reportType' },
|
|
274
|
+
{ key: 'outputPath' }
|
|
275
|
+
]);
|
|
276
|
+
const res = await tools.assetTools.generateReport({
|
|
277
|
+
directory: params.directory,
|
|
278
|
+
reportType: params.reportType,
|
|
279
|
+
outputPath: params.outputPath
|
|
280
|
+
});
|
|
281
|
+
return ResponseFactory.success(res, 'Report generated successfully');
|
|
282
|
+
}
|
|
283
|
+
case 'create_material_instance': {
|
|
284
|
+
const res: any = await executeAutomationRequest(
|
|
285
|
+
tools,
|
|
286
|
+
'create_material_instance',
|
|
287
|
+
args,
|
|
288
|
+
'Automation bridge not available for create_material_instance'
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
const result = res?.result ?? res ?? {};
|
|
292
|
+
const errorCode = typeof result.error === 'string' ? result.error.toUpperCase() : '';
|
|
293
|
+
const message = typeof result.message === 'string' ? result.message : '';
|
|
294
|
+
|
|
295
|
+
if (errorCode === 'PARENT_NOT_FOUND' || message.toLowerCase().includes('parent material not found')) {
|
|
296
|
+
// Keep specific error structure for this business logic case
|
|
142
297
|
return cleanObject({
|
|
143
298
|
success: false,
|
|
144
|
-
error: '
|
|
145
|
-
message:
|
|
146
|
-
|
|
147
|
-
|
|
299
|
+
error: 'PARENT_NOT_FOUND',
|
|
300
|
+
message: message || 'Parent material not found',
|
|
301
|
+
path: result.path,
|
|
302
|
+
parentMaterial: args.parentMaterial
|
|
148
303
|
});
|
|
149
304
|
}
|
|
305
|
+
|
|
306
|
+
return ResponseFactory.success(res, 'Material instance created successfully');
|
|
150
307
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
308
|
+
case 'search_assets': {
|
|
309
|
+
const params = normalizeArgs(args, [
|
|
310
|
+
{ key: 'classNames' },
|
|
311
|
+
{ key: 'packagePaths' },
|
|
312
|
+
{ key: 'recursivePaths' },
|
|
313
|
+
{ key: 'recursiveClasses' },
|
|
314
|
+
{ key: 'limit' }
|
|
315
|
+
]);
|
|
316
|
+
const res = await tools.assetTools.searchAssets({
|
|
317
|
+
classNames: params.classNames,
|
|
318
|
+
packagePaths: params.packagePaths,
|
|
319
|
+
recursivePaths: params.recursivePaths,
|
|
320
|
+
recursiveClasses: params.recursiveClasses,
|
|
321
|
+
limit: params.limit
|
|
322
|
+
});
|
|
323
|
+
return ResponseFactory.success(res, 'Assets found');
|
|
163
324
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
case 'delete_assets':
|
|
172
|
-
case 'delete_asset':
|
|
173
|
-
case 'delete': {
|
|
174
|
-
let paths: string[] = [];
|
|
175
|
-
if (Array.isArray(args.paths)) {
|
|
176
|
-
paths = args.paths;
|
|
177
|
-
} else if (Array.isArray(args.assetPaths)) {
|
|
178
|
-
paths = args.assetPaths;
|
|
179
|
-
} else {
|
|
180
|
-
const single = args.assetPath || args.path;
|
|
181
|
-
if (typeof single === 'string' && single.trim()) {
|
|
182
|
-
paths = [single.trim()];
|
|
183
|
-
}
|
|
325
|
+
case 'find_by_tag': {
|
|
326
|
+
const params = normalizeArgs(args, [
|
|
327
|
+
{ key: 'tag', required: true },
|
|
328
|
+
{ key: 'value' }
|
|
329
|
+
]);
|
|
330
|
+
const res = await tools.assetTools.findByTag({ tag: params.tag, value: params.value });
|
|
331
|
+
return ResponseFactory.success(res, 'Assets found by tag');
|
|
184
332
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
333
|
+
case 'get_dependencies': {
|
|
334
|
+
const params = normalizeArgs(args, [
|
|
335
|
+
{ key: 'assetPath', required: true },
|
|
336
|
+
{ key: 'recursive' }
|
|
337
|
+
]);
|
|
338
|
+
const res = await tools.assetTools.getDependencies({ assetPath: params.assetPath, recursive: params.recursive });
|
|
339
|
+
return ResponseFactory.success(res, 'Dependencies retrieved');
|
|
188
340
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
]);
|
|
198
|
-
return cleanObject(await tools.assetTools.generateLODs({
|
|
199
|
-
assetPath: params.assetPath,
|
|
200
|
-
lodCount: params.lodCount
|
|
201
|
-
}));
|
|
202
|
-
}
|
|
203
|
-
case 'create_thumbnail': {
|
|
204
|
-
const params = normalizeArgs(args, [
|
|
205
|
-
{ key: 'assetPath', required: true },
|
|
206
|
-
{ key: 'width' },
|
|
207
|
-
{ key: 'height' }
|
|
208
|
-
]);
|
|
209
|
-
const res = await tools.assetTools.createThumbnail({
|
|
210
|
-
assetPath: params.assetPath,
|
|
211
|
-
width: params.width,
|
|
212
|
-
height: params.height
|
|
213
|
-
});
|
|
214
|
-
return cleanObject(res);
|
|
215
|
-
}
|
|
216
|
-
case 'set_tags': {
|
|
217
|
-
try {
|
|
341
|
+
case 'get_source_control_state': {
|
|
342
|
+
const params = normalizeArgs(args, [
|
|
343
|
+
{ key: 'assetPath', required: true }
|
|
344
|
+
]);
|
|
345
|
+
const res = await tools.assetTools.getSourceControlState({ assetPath: params.assetPath });
|
|
346
|
+
return ResponseFactory.success(res, 'Source control state retrieved');
|
|
347
|
+
}
|
|
348
|
+
case 'analyze_graph': {
|
|
218
349
|
const params = normalizeArgs(args, [
|
|
219
350
|
{ key: 'assetPath', required: true },
|
|
220
|
-
{ key: '
|
|
351
|
+
{ key: 'maxDepth' }
|
|
221
352
|
]);
|
|
222
|
-
const res = await tools
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
message.includes('not_implemented') ||
|
|
228
|
-
message.includes('not implemented') ||
|
|
229
|
-
message.includes('unknown action') ||
|
|
230
|
-
message.includes('unknown subaction')
|
|
231
|
-
) {
|
|
232
|
-
return cleanObject({
|
|
233
|
-
success: false,
|
|
234
|
-
error: 'NOT_IMPLEMENTED',
|
|
235
|
-
message: 'Asset tag writes are not implemented by the automation plugin.',
|
|
236
|
-
action: 'set_tags',
|
|
237
|
-
assetPath: args.assetPath,
|
|
238
|
-
tags: args.tags
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
throw err;
|
|
353
|
+
const res = await executeAutomationRequest(tools, 'get_asset_graph', {
|
|
354
|
+
assetPath: params.assetPath,
|
|
355
|
+
maxDepth: params.maxDepth
|
|
356
|
+
});
|
|
357
|
+
return ResponseFactory.success(res, 'Graph analysis complete');
|
|
242
358
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
359
|
+
case 'create_render_target': {
|
|
360
|
+
const params = normalizeArgs(args, [
|
|
361
|
+
{ key: 'name', required: true },
|
|
362
|
+
{ key: 'packagePath', aliases: ['path'], default: '/Game' },
|
|
363
|
+
{ key: 'width' },
|
|
364
|
+
{ key: 'height' },
|
|
365
|
+
{ key: 'format' }
|
|
366
|
+
]);
|
|
367
|
+
const res = await executeAutomationRequest(tools, 'manage_render', {
|
|
368
|
+
subAction: 'create_render_target',
|
|
369
|
+
name: params.name,
|
|
370
|
+
packagePath: params.packagePath,
|
|
371
|
+
width: params.width,
|
|
372
|
+
height: params.height,
|
|
373
|
+
format: params.format,
|
|
374
|
+
save: true
|
|
375
|
+
});
|
|
376
|
+
return ResponseFactory.success(res, 'Render target created successfully');
|
|
259
377
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
case 'validate':
|
|
268
|
-
case 'validate_asset': {
|
|
269
|
-
const params = normalizeArgs(args, [
|
|
270
|
-
{ key: 'assetPath', required: true }
|
|
271
|
-
]);
|
|
272
|
-
const res = await tools.assetTools.validate({ assetPath: params.assetPath });
|
|
273
|
-
return cleanObject(res);
|
|
274
|
-
}
|
|
275
|
-
case 'generate_report': {
|
|
276
|
-
const params = normalizeArgs(args, [
|
|
277
|
-
{ key: 'directory' },
|
|
278
|
-
{ key: 'reportType' },
|
|
279
|
-
{ key: 'outputPath' }
|
|
280
|
-
]);
|
|
281
|
-
const res = await tools.assetTools.generateReport({
|
|
282
|
-
directory: params.directory,
|
|
283
|
-
reportType: params.reportType,
|
|
284
|
-
outputPath: params.outputPath
|
|
285
|
-
});
|
|
286
|
-
return cleanObject(res);
|
|
287
|
-
}
|
|
288
|
-
case 'create_material_instance': {
|
|
289
|
-
const res: any = await executeAutomationRequest(
|
|
290
|
-
tools,
|
|
291
|
-
'create_material_instance',
|
|
292
|
-
args,
|
|
293
|
-
'Automation bridge not available for create_material_instance'
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
const result = res?.result ?? res ?? {};
|
|
297
|
-
const errorCode = typeof result.error === 'string' ? result.error.toUpperCase() : '';
|
|
298
|
-
const message = typeof result.message === 'string' ? result.message : '';
|
|
299
|
-
|
|
300
|
-
if (errorCode === 'PARENT_NOT_FOUND' || message.toLowerCase().includes('parent material not found')) {
|
|
301
|
-
return cleanObject({
|
|
302
|
-
success: false,
|
|
303
|
-
error: 'PARENT_NOT_FOUND',
|
|
304
|
-
message: message || 'Parent material not found',
|
|
305
|
-
path: result.path,
|
|
306
|
-
parentMaterial: args.parentMaterial
|
|
378
|
+
case 'nanite_rebuild_mesh': {
|
|
379
|
+
const params = normalizeArgs(args, [
|
|
380
|
+
{ key: 'assetPath', aliases: ['meshPath'], required: true }
|
|
381
|
+
]);
|
|
382
|
+
const res = await executeAutomationRequest(tools, 'manage_render', {
|
|
383
|
+
subAction: 'nanite_rebuild_mesh',
|
|
384
|
+
assetPath: params.assetPath
|
|
307
385
|
});
|
|
386
|
+
return ResponseFactory.success(res, 'Nanite mesh rebuilt successfully');
|
|
308
387
|
}
|
|
388
|
+
case 'fixup_redirectors': {
|
|
389
|
+
const directoryRaw = typeof args.directory === 'string' && args.directory.trim().length > 0
|
|
390
|
+
? args.directory.trim()
|
|
391
|
+
: (typeof args.directoryPath === 'string' && args.directoryPath.trim().length > 0
|
|
392
|
+
? args.directoryPath.trim()
|
|
393
|
+
: '');
|
|
394
|
+
|
|
395
|
+
const payload: any = {};
|
|
396
|
+
if (directoryRaw) {
|
|
397
|
+
payload.directoryPath = directoryRaw;
|
|
398
|
+
}
|
|
399
|
+
if (typeof args.checkoutFiles === 'boolean') {
|
|
400
|
+
payload.checkoutFiles = args.checkoutFiles;
|
|
401
|
+
}
|
|
309
402
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
case 'search_assets': {
|
|
313
|
-
const params = normalizeArgs(args, [
|
|
314
|
-
{ key: 'classNames' },
|
|
315
|
-
{ key: 'packagePaths' },
|
|
316
|
-
{ key: 'recursivePaths' },
|
|
317
|
-
{ key: 'recursiveClasses' },
|
|
318
|
-
{ key: 'limit' }
|
|
319
|
-
]);
|
|
320
|
-
const res = await tools.assetTools.searchAssets({
|
|
321
|
-
classNames: params.classNames,
|
|
322
|
-
packagePaths: params.packagePaths,
|
|
323
|
-
recursivePaths: params.recursivePaths,
|
|
324
|
-
recursiveClasses: params.recursiveClasses,
|
|
325
|
-
limit: params.limit
|
|
326
|
-
});
|
|
327
|
-
return cleanObject(res);
|
|
328
|
-
}
|
|
329
|
-
case 'find_by_tag': {
|
|
330
|
-
const params = normalizeArgs(args, [
|
|
331
|
-
{ key: 'tag', required: true },
|
|
332
|
-
{ key: 'value' }
|
|
333
|
-
]);
|
|
334
|
-
return tools.assetTools.findByTag({ tag: params.tag, value: params.value });
|
|
335
|
-
}
|
|
336
|
-
case 'get_dependencies': {
|
|
337
|
-
const params = normalizeArgs(args, [
|
|
338
|
-
{ key: 'assetPath', required: true },
|
|
339
|
-
{ key: 'recursive' }
|
|
340
|
-
]);
|
|
341
|
-
const res = await tools.assetTools.getDependencies({ assetPath: params.assetPath, recursive: params.recursive });
|
|
342
|
-
return cleanObject(res);
|
|
343
|
-
}
|
|
344
|
-
case 'get_source_control_state': {
|
|
345
|
-
const params = normalizeArgs(args, [
|
|
346
|
-
{ key: 'assetPath', required: true }
|
|
347
|
-
]);
|
|
348
|
-
const res = await tools.assetTools.getSourceControlState({ assetPath: params.assetPath });
|
|
349
|
-
return cleanObject(res);
|
|
350
|
-
}
|
|
351
|
-
case 'analyze_graph': {
|
|
352
|
-
const params = normalizeArgs(args, [
|
|
353
|
-
{ key: 'assetPath', required: true },
|
|
354
|
-
{ key: 'maxDepth' }
|
|
355
|
-
]);
|
|
356
|
-
const res = await executeAutomationRequest(tools, 'get_asset_graph', {
|
|
357
|
-
assetPath: params.assetPath,
|
|
358
|
-
maxDepth: params.maxDepth
|
|
359
|
-
});
|
|
360
|
-
return cleanObject(res);
|
|
361
|
-
}
|
|
362
|
-
case 'create_render_target': {
|
|
363
|
-
const params = normalizeArgs(args, [
|
|
364
|
-
{ key: 'name', required: true },
|
|
365
|
-
{ key: 'packagePath', aliases: ['path'], default: '/Game' },
|
|
366
|
-
{ key: 'width' },
|
|
367
|
-
{ key: 'height' },
|
|
368
|
-
{ key: 'format' }
|
|
369
|
-
]);
|
|
370
|
-
const res = await executeAutomationRequest(tools, 'manage_render', {
|
|
371
|
-
subAction: 'create_render_target',
|
|
372
|
-
name: params.name,
|
|
373
|
-
packagePath: params.packagePath,
|
|
374
|
-
width: params.width,
|
|
375
|
-
height: params.height,
|
|
376
|
-
format: params.format,
|
|
377
|
-
save: true
|
|
378
|
-
});
|
|
379
|
-
return cleanObject(res);
|
|
380
|
-
}
|
|
381
|
-
case 'nanite_rebuild_mesh': {
|
|
382
|
-
const params = normalizeArgs(args, [
|
|
383
|
-
{ key: 'assetPath', aliases: ['meshPath'], required: true }
|
|
384
|
-
]);
|
|
385
|
-
const res = await executeAutomationRequest(tools, 'manage_render', {
|
|
386
|
-
subAction: 'nanite_rebuild_mesh',
|
|
387
|
-
assetPath: params.assetPath
|
|
388
|
-
});
|
|
389
|
-
return cleanObject(res);
|
|
390
|
-
}
|
|
391
|
-
case 'fixup_redirectors': {
|
|
392
|
-
const directoryRaw = typeof args.directory === 'string' && args.directory.trim().length > 0
|
|
393
|
-
? args.directory.trim()
|
|
394
|
-
: (typeof args.directoryPath === 'string' && args.directoryPath.trim().length > 0
|
|
395
|
-
? args.directoryPath.trim()
|
|
396
|
-
: '');
|
|
397
|
-
|
|
398
|
-
const payload: any = {};
|
|
399
|
-
if (directoryRaw) {
|
|
400
|
-
payload.directoryPath = directoryRaw;
|
|
403
|
+
const res = await executeAutomationRequest(tools, 'fixup_redirectors', payload);
|
|
404
|
+
return ResponseFactory.success(res, 'Redirectors fixed up successfully');
|
|
401
405
|
}
|
|
402
|
-
|
|
403
|
-
|
|
406
|
+
case 'add_material_parameter': {
|
|
407
|
+
const params = normalizeArgs(args, [
|
|
408
|
+
{ key: 'assetPath', required: true },
|
|
409
|
+
{ key: 'parameterName', aliases: ['name'], required: true },
|
|
410
|
+
{ key: 'parameterType', aliases: ['type'] },
|
|
411
|
+
{ key: 'value', aliases: ['defaultValue'] }
|
|
412
|
+
]);
|
|
413
|
+
const res = await executeAutomationRequest(tools, 'add_material_parameter', {
|
|
414
|
+
assetPath: params.assetPath,
|
|
415
|
+
name: params.parameterName,
|
|
416
|
+
type: params.parameterType,
|
|
417
|
+
value: params.value
|
|
418
|
+
});
|
|
419
|
+
return ResponseFactory.success(res, 'Material parameter added successfully');
|
|
420
|
+
}
|
|
421
|
+
case 'list_instances': {
|
|
422
|
+
const params = normalizeArgs(args, [
|
|
423
|
+
{ key: 'assetPath', required: true }
|
|
424
|
+
]);
|
|
425
|
+
const res = await executeAutomationRequest(tools, 'list_instances', {
|
|
426
|
+
assetPath: params.assetPath
|
|
427
|
+
});
|
|
428
|
+
return ResponseFactory.success(res, 'Instances listed successfully');
|
|
429
|
+
}
|
|
430
|
+
case 'reset_instance_parameters': {
|
|
431
|
+
const params = normalizeArgs(args, [
|
|
432
|
+
{ key: 'assetPath', required: true }
|
|
433
|
+
]);
|
|
434
|
+
const res = await executeAutomationRequest(tools, 'reset_instance_parameters', {
|
|
435
|
+
assetPath: params.assetPath
|
|
436
|
+
});
|
|
437
|
+
return ResponseFactory.success(res, 'Instance parameters reset successfully');
|
|
404
438
|
}
|
|
439
|
+
case 'exists': {
|
|
440
|
+
const params = normalizeArgs(args, [
|
|
441
|
+
{ key: 'assetPath', required: true }
|
|
442
|
+
]);
|
|
443
|
+
const res = await executeAutomationRequest(tools, 'exists', {
|
|
444
|
+
assetPath: params.assetPath
|
|
445
|
+
});
|
|
446
|
+
return ResponseFactory.success(res, 'Asset existence check complete');
|
|
447
|
+
}
|
|
448
|
+
case 'get_material_stats': {
|
|
449
|
+
const params = normalizeArgs(args, [
|
|
450
|
+
{ key: 'assetPath', required: true }
|
|
451
|
+
]);
|
|
452
|
+
const res = await executeAutomationRequest(tools, 'get_material_stats', {
|
|
453
|
+
assetPath: params.assetPath
|
|
454
|
+
});
|
|
455
|
+
return ResponseFactory.success(res, 'Material stats retrieved');
|
|
456
|
+
}
|
|
457
|
+
case 'rebuild_material': {
|
|
458
|
+
const params = normalizeArgs(args, [
|
|
459
|
+
{ key: 'assetPath', required: true }
|
|
460
|
+
]);
|
|
461
|
+
const res = await executeAutomationRequest(tools, 'rebuild_material', {
|
|
462
|
+
assetPath: params.assetPath
|
|
463
|
+
});
|
|
464
|
+
return ResponseFactory.success(res, 'Material rebuilt successfully');
|
|
465
|
+
}
|
|
466
|
+
case 'add_material_node': {
|
|
467
|
+
const materialNodeAliases: Record<string, string> = {
|
|
468
|
+
'Multiply': 'MaterialExpressionMultiply',
|
|
469
|
+
'Add': 'MaterialExpressionAdd',
|
|
470
|
+
'Subtract': 'MaterialExpressionSubtract',
|
|
471
|
+
'Divide': 'MaterialExpressionDivide',
|
|
472
|
+
'Power': 'MaterialExpressionPower',
|
|
473
|
+
'Clamp': 'MaterialExpressionClamp',
|
|
474
|
+
'Constant': 'MaterialExpressionConstant',
|
|
475
|
+
'Constant2Vector': 'MaterialExpressionConstant2Vector',
|
|
476
|
+
'Constant3Vector': 'MaterialExpressionConstant3Vector',
|
|
477
|
+
'Constant4Vector': 'MaterialExpressionConstant4Vector',
|
|
478
|
+
'TextureSample': 'MaterialExpressionTextureSample',
|
|
479
|
+
'TextureCoordinate': 'MaterialExpressionTextureCoordinate',
|
|
480
|
+
'Panner': 'MaterialExpressionPanner',
|
|
481
|
+
'Rotator': 'MaterialExpressionRotator',
|
|
482
|
+
'Lerp': 'MaterialExpressionLinearInterpolate',
|
|
483
|
+
'LinearInterpolate': 'MaterialExpressionLinearInterpolate',
|
|
484
|
+
'Sine': 'MaterialExpressionSine',
|
|
485
|
+
'Cosine': 'MaterialExpressionCosine',
|
|
486
|
+
'Append': 'MaterialExpressionAppendVector',
|
|
487
|
+
'AppendVector': 'MaterialExpressionAppendVector',
|
|
488
|
+
'ComponentMask': 'MaterialExpressionComponentMask',
|
|
489
|
+
'Fresnel': 'MaterialExpressionFresnel',
|
|
490
|
+
'Time': 'MaterialExpressionTime',
|
|
491
|
+
'ScalarParameter': 'MaterialExpressionScalarParameter',
|
|
492
|
+
'VectorParameter': 'MaterialExpressionVectorParameter',
|
|
493
|
+
'StaticSwitchParameter': 'MaterialExpressionStaticSwitchParameter'
|
|
494
|
+
};
|
|
405
495
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
name: params.parameterName,
|
|
419
|
-
type: params.parameterType,
|
|
420
|
-
value: params.value
|
|
421
|
-
});
|
|
422
|
-
return cleanObject(res);
|
|
423
|
-
}
|
|
424
|
-
case 'list_instances': {
|
|
425
|
-
const params = normalizeArgs(args, [
|
|
426
|
-
{ key: 'assetPath', required: true }
|
|
427
|
-
]);
|
|
428
|
-
const res = await executeAutomationRequest(tools, 'list_instances', {
|
|
429
|
-
assetPath: params.assetPath
|
|
430
|
-
});
|
|
431
|
-
return cleanObject(res);
|
|
432
|
-
}
|
|
433
|
-
case 'reset_instance_parameters': {
|
|
434
|
-
const params = normalizeArgs(args, [
|
|
435
|
-
{ key: 'assetPath', required: true }
|
|
436
|
-
]);
|
|
437
|
-
const res = await executeAutomationRequest(tools, 'reset_instance_parameters', {
|
|
438
|
-
assetPath: params.assetPath
|
|
439
|
-
});
|
|
440
|
-
return cleanObject(res);
|
|
441
|
-
}
|
|
442
|
-
case 'exists': {
|
|
443
|
-
const params = normalizeArgs(args, [
|
|
444
|
-
{ key: 'assetPath', required: true }
|
|
445
|
-
]);
|
|
446
|
-
const res = await executeAutomationRequest(tools, 'exists', {
|
|
447
|
-
assetPath: params.assetPath
|
|
448
|
-
});
|
|
449
|
-
return cleanObject(res);
|
|
450
|
-
}
|
|
451
|
-
case 'get_material_stats': {
|
|
452
|
-
const params = normalizeArgs(args, [
|
|
453
|
-
{ key: 'assetPath', required: true }
|
|
454
|
-
]);
|
|
455
|
-
const res = await executeAutomationRequest(tools, 'get_material_stats', {
|
|
456
|
-
assetPath: params.assetPath
|
|
457
|
-
});
|
|
458
|
-
return cleanObject(res);
|
|
459
|
-
}
|
|
460
|
-
case 'rebuild_material': {
|
|
461
|
-
const params = normalizeArgs(args, [
|
|
462
|
-
{ key: 'assetPath', required: true }
|
|
463
|
-
]);
|
|
464
|
-
const res = await executeAutomationRequest(tools, 'rebuild_material', {
|
|
465
|
-
assetPath: params.assetPath
|
|
466
|
-
});
|
|
467
|
-
return cleanObject(res);
|
|
468
|
-
}
|
|
469
|
-
case 'add_material_node': {
|
|
470
|
-
const materialNodeAliases: Record<string, string> = {
|
|
471
|
-
'Multiply': 'MaterialExpressionMultiply',
|
|
472
|
-
'Add': 'MaterialExpressionAdd',
|
|
473
|
-
'Subtract': 'MaterialExpressionSubtract',
|
|
474
|
-
'Divide': 'MaterialExpressionDivide',
|
|
475
|
-
'Power': 'MaterialExpressionPower',
|
|
476
|
-
'Clamp': 'MaterialExpressionClamp',
|
|
477
|
-
'Constant': 'MaterialExpressionConstant',
|
|
478
|
-
'Constant2Vector': 'MaterialExpressionConstant2Vector',
|
|
479
|
-
'Constant3Vector': 'MaterialExpressionConstant3Vector',
|
|
480
|
-
'Constant4Vector': 'MaterialExpressionConstant4Vector',
|
|
481
|
-
'TextureSample': 'MaterialExpressionTextureSample',
|
|
482
|
-
'TextureCoordinate': 'MaterialExpressionTextureCoordinate',
|
|
483
|
-
'Panner': 'MaterialExpressionPanner',
|
|
484
|
-
'Rotator': 'MaterialExpressionRotator',
|
|
485
|
-
'Lerp': 'MaterialExpressionLinearInterpolate',
|
|
486
|
-
'LinearInterpolate': 'MaterialExpressionLinearInterpolate',
|
|
487
|
-
'Sine': 'MaterialExpressionSine',
|
|
488
|
-
'Cosine': 'MaterialExpressionCosine',
|
|
489
|
-
'Append': 'MaterialExpressionAppendVector',
|
|
490
|
-
'AppendVector': 'MaterialExpressionAppendVector',
|
|
491
|
-
'ComponentMask': 'MaterialExpressionComponentMask',
|
|
492
|
-
'Fresnel': 'MaterialExpressionFresnel',
|
|
493
|
-
'Time': 'MaterialExpressionTime',
|
|
494
|
-
'ScalarParameter': 'MaterialExpressionScalarParameter',
|
|
495
|
-
'VectorParameter': 'MaterialExpressionVectorParameter',
|
|
496
|
-
'StaticSwitchParameter': 'MaterialExpressionStaticSwitchParameter'
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
const params = normalizeArgs(args, [
|
|
500
|
-
{ key: 'assetPath', required: true },
|
|
501
|
-
{ key: 'nodeType', aliases: ['type'], required: true, map: materialNodeAliases },
|
|
502
|
-
{ key: 'posX' },
|
|
503
|
-
{ key: 'posY' }
|
|
504
|
-
]);
|
|
505
|
-
|
|
506
|
-
const res = await executeAutomationRequest(tools, 'add_material_node', {
|
|
507
|
-
assetPath: params.assetPath,
|
|
508
|
-
nodeType: params.nodeType,
|
|
509
|
-
posX: params.posX,
|
|
510
|
-
posY: params.posY
|
|
511
|
-
});
|
|
512
|
-
return cleanObject(res);
|
|
513
|
-
}
|
|
514
|
-
default:
|
|
515
|
-
const res: any = await executeAutomationRequest(tools, action || 'manage_asset', args);
|
|
516
|
-
const result = res?.result ?? res ?? {};
|
|
517
|
-
const errorCode = typeof result.error === 'string' ? result.error.toUpperCase() : '';
|
|
518
|
-
const message = typeof result.message === 'string' ? result.message : '';
|
|
519
|
-
|
|
520
|
-
if (errorCode === 'INVALID_SUBACTION' || message.toLowerCase().includes('unknown subaction')) {
|
|
521
|
-
return cleanObject({
|
|
522
|
-
success: false,
|
|
523
|
-
error: 'INVALID_SUBACTION',
|
|
524
|
-
message: 'Asset action not recognized by the automation plugin.',
|
|
525
|
-
action: action || 'manage_asset',
|
|
526
|
-
assetPath: args.assetPath ?? args.path
|
|
496
|
+
const params = normalizeArgs(args, [
|
|
497
|
+
{ key: 'assetPath', required: true },
|
|
498
|
+
{ key: 'nodeType', aliases: ['type'], required: true, map: materialNodeAliases },
|
|
499
|
+
{ key: 'posX' },
|
|
500
|
+
{ key: 'posY' }
|
|
501
|
+
]);
|
|
502
|
+
|
|
503
|
+
const res = await executeAutomationRequest(tools, 'add_material_node', {
|
|
504
|
+
assetPath: params.assetPath,
|
|
505
|
+
nodeType: params.nodeType,
|
|
506
|
+
posX: params.posX,
|
|
507
|
+
posY: params.posY
|
|
527
508
|
});
|
|
509
|
+
return ResponseFactory.success(res, 'Material node added successfully');
|
|
528
510
|
}
|
|
511
|
+
default:
|
|
512
|
+
const res: any = await executeAutomationRequest(tools, action || 'manage_asset', args);
|
|
513
|
+
const result = res?.result ?? res ?? {};
|
|
514
|
+
const errorCode = typeof result.error === 'string' ? result.error.toUpperCase() : '';
|
|
515
|
+
const message = typeof result.message === 'string' ? result.message : '';
|
|
516
|
+
|
|
517
|
+
if (errorCode === 'INVALID_SUBACTION' || message.toLowerCase().includes('unknown subaction')) {
|
|
518
|
+
return cleanObject({
|
|
519
|
+
success: false,
|
|
520
|
+
error: 'INVALID_SUBACTION',
|
|
521
|
+
message: 'Asset action not recognized by the automation plugin.',
|
|
522
|
+
action: action || 'manage_asset',
|
|
523
|
+
assetPath: args.assetPath ?? args.path
|
|
524
|
+
});
|
|
525
|
+
}
|
|
529
526
|
|
|
530
|
-
|
|
527
|
+
return ResponseFactory.success(res, 'Asset action executed successfully');
|
|
528
|
+
}
|
|
529
|
+
} catch (error) {
|
|
530
|
+
return ResponseFactory.error(error);
|
|
531
531
|
}
|
|
532
532
|
}
|