unreal-engine-mcp-server 0.5.1 → 0.5.2
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/.github/workflows/publish-mcp.yml +1 -4
- package/.github/workflows/release-drafter.yml +2 -1
- package/CHANGELOG.md +38 -0
- package/dist/automation/bridge.d.ts +1 -2
- package/dist/automation/bridge.js +24 -23
- package/dist/automation/connection-manager.d.ts +1 -0
- package/dist/automation/connection-manager.js +10 -0
- package/dist/automation/message-handler.js +5 -4
- package/dist/automation/request-tracker.d.ts +4 -0
- package/dist/automation/request-tracker.js +11 -3
- package/dist/tools/actors.d.ts +19 -1
- package/dist/tools/actors.js +15 -5
- package/dist/tools/assets.js +1 -1
- package/dist/tools/blueprint.d.ts +12 -0
- package/dist/tools/blueprint.js +43 -14
- package/dist/tools/consolidated-tool-definitions.js +2 -1
- package/dist/tools/editor.js +3 -2
- package/dist/tools/handlers/actor-handlers.d.ts +1 -1
- package/dist/tools/handlers/actor-handlers.js +14 -8
- package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
- package/dist/tools/handlers/sequence-handlers.js +24 -13
- package/dist/tools/introspection.d.ts +1 -1
- package/dist/tools/introspection.js +1 -1
- package/dist/tools/level.js +3 -3
- package/dist/tools/lighting.d.ts +54 -7
- package/dist/tools/lighting.js +4 -4
- package/dist/tools/materials.d.ts +1 -1
- package/dist/types/tool-types.d.ts +2 -0
- package/dist/unreal-bridge.js +4 -4
- package/dist/utils/command-validator.js +6 -5
- package/dist/utils/error-handler.d.ts +24 -2
- package/dist/utils/error-handler.js +58 -23
- package/dist/utils/normalize.d.ts +7 -4
- package/dist/utils/normalize.js +12 -10
- package/dist/utils/response-validator.js +88 -73
- package/dist/utils/unreal-command-queue.d.ts +2 -0
- package/dist/utils/unreal-command-queue.js +8 -1
- package/docs/handler-mapping.md +4 -2
- package/package.json +1 -1
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +298 -33
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +7 -8
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +229 -319
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +98 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +24 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +96 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +52 -5
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +5 -268
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +57 -2
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -1
- package/server.json +3 -3
- package/src/automation/bridge.ts +27 -25
- package/src/automation/connection-manager.ts +18 -0
- package/src/automation/message-handler.ts +33 -8
- package/src/automation/request-tracker.ts +39 -7
- package/src/server/tool-registry.ts +3 -3
- package/src/tools/actors.ts +44 -19
- package/src/tools/assets.ts +3 -3
- package/src/tools/blueprint.ts +115 -49
- package/src/tools/consolidated-tool-definitions.ts +2 -1
- package/src/tools/editor.ts +4 -3
- package/src/tools/handlers/actor-handlers.ts +14 -9
- package/src/tools/handlers/sequence-handlers.ts +86 -63
- package/src/tools/introspection.ts +7 -7
- package/src/tools/level.ts +6 -6
- package/src/tools/lighting.ts +19 -19
- package/src/tools/materials.ts +1 -1
- package/src/tools/sequence.ts +1 -1
- package/src/tools/ui.ts +1 -1
- package/src/types/tool-types.ts +4 -0
- package/src/unreal-bridge.ts +71 -26
- package/src/utils/command-validator.ts +46 -5
- package/src/utils/error-handler.ts +128 -45
- package/src/utils/normalize.ts +38 -16
- package/src/utils/response-validator.ts +103 -87
- package/src/utils/unreal-command-queue.ts +13 -1
|
@@ -20,7 +20,17 @@ function markSequenceDeleted(path) {
|
|
|
20
20
|
if (!norm)
|
|
21
21
|
return;
|
|
22
22
|
managedSequences.delete(norm);
|
|
23
|
-
deletedSequences.
|
|
23
|
+
deletedSequences.delete(norm);
|
|
24
|
+
}
|
|
25
|
+
function getErrorString(res) {
|
|
26
|
+
if (!res)
|
|
27
|
+
return '';
|
|
28
|
+
return typeof res.error === 'string' ? res.error : '';
|
|
29
|
+
}
|
|
30
|
+
function getMessageString(res) {
|
|
31
|
+
if (!res)
|
|
32
|
+
return '';
|
|
33
|
+
return typeof res.message === 'string' ? res.message : '';
|
|
24
34
|
}
|
|
25
35
|
export async function handleSequenceTools(action, args, tools) {
|
|
26
36
|
const seqAction = String(action || '').trim();
|
|
@@ -39,8 +49,8 @@ export async function handleSequenceTools(action, args, tools) {
|
|
|
39
49
|
if (sequencePath && res && res.success !== false) {
|
|
40
50
|
markSequenceCreated(sequencePath);
|
|
41
51
|
}
|
|
42
|
-
const errorCode =
|
|
43
|
-
const msgLower =
|
|
52
|
+
const errorCode = getErrorString(res).toUpperCase();
|
|
53
|
+
const msgLower = getMessageString(res).toLowerCase();
|
|
44
54
|
if (res && res.success === false && (errorCode === 'FACTORY_NOT_AVAILABLE' || msgLower.includes('ulevelsequencefactorynew not available'))) {
|
|
45
55
|
const path = sequencePath || (typeof args.path === 'string' ? args.path : undefined);
|
|
46
56
|
return cleanObject({
|
|
@@ -75,8 +85,8 @@ export async function handleSequenceTools(action, args, tools) {
|
|
|
75
85
|
subAction: 'add_actor'
|
|
76
86
|
};
|
|
77
87
|
const res = await executeAutomationRequest(tools, 'manage_sequence', payload);
|
|
78
|
-
const errorCode =
|
|
79
|
-
const msgLower =
|
|
88
|
+
const errorCode = getErrorString(res).toUpperCase();
|
|
89
|
+
const msgLower = getMessageString(res).toLowerCase();
|
|
80
90
|
if (res && res.success === false && path) {
|
|
81
91
|
const isInvalidSequence = errorCode === 'INVALID_SEQUENCE' || msgLower.includes('sequence_add_actor requires a sequence path') || msgLower.includes('sequence not found');
|
|
82
92
|
if (isInvalidSequence) {
|
|
@@ -114,8 +124,8 @@ export async function handleSequenceTools(action, args, tools) {
|
|
|
114
124
|
case 'add_actors': {
|
|
115
125
|
const actorNames = Array.isArray(args.actorNames) ? args.actorNames : [];
|
|
116
126
|
const res = await tools.sequenceTools.addActors({ actorNames, path: args.path });
|
|
117
|
-
const errorCode =
|
|
118
|
-
const msgLower =
|
|
127
|
+
const errorCode = getErrorString(res).toUpperCase();
|
|
128
|
+
const msgLower = getMessageString(res).toLowerCase();
|
|
119
129
|
if (actorNames.length === 0 && res && res.success === false && errorCode === 'INVALID_ARGUMENT') {
|
|
120
130
|
return cleanObject({
|
|
121
131
|
success: false,
|
|
@@ -172,8 +182,8 @@ export async function handleSequenceTools(action, args, tools) {
|
|
|
172
182
|
payload.value = { scale: args.value };
|
|
173
183
|
}
|
|
174
184
|
const res = await executeAutomationRequest(tools, 'manage_sequence', payload);
|
|
175
|
-
const errorCode =
|
|
176
|
-
const msgLower =
|
|
185
|
+
const errorCode = getErrorString(res).toUpperCase();
|
|
186
|
+
const msgLower = getMessageString(res).toLowerCase();
|
|
177
187
|
if (errorCode === 'INVALID_ARGUMENT' || msgLower.includes('frame number is required')) {
|
|
178
188
|
return cleanObject(res);
|
|
179
189
|
}
|
|
@@ -237,7 +247,7 @@ export async function handleSequenceTools(action, args, tools) {
|
|
|
237
247
|
throw new Error('Invalid speed: must be a positive number');
|
|
238
248
|
}
|
|
239
249
|
let res = await tools.sequenceTools.setPlaybackSpeed({ speed, path: args.path });
|
|
240
|
-
const errorCode =
|
|
250
|
+
const errorCode = getErrorString(res).toUpperCase();
|
|
241
251
|
if ((!res || res.success === false) && errorCode === 'EDITOR_NOT_OPEN' && args.path) {
|
|
242
252
|
await tools.sequenceTools.open({ path: args.path });
|
|
243
253
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
@@ -252,7 +262,8 @@ export async function handleSequenceTools(action, args, tools) {
|
|
|
252
262
|
case 'duplicate': {
|
|
253
263
|
const path = requireNonEmptyString(args.path, 'path', 'Missing required parameter: path');
|
|
254
264
|
const destDir = requireNonEmptyString(args.destinationPath, 'destinationPath', 'Missing required parameter: destinationPath');
|
|
255
|
-
const
|
|
265
|
+
const defaultNewName = path.split('/').pop() || '';
|
|
266
|
+
const newName = requireNonEmptyString(args.newName || defaultNewName, 'newName', 'Missing required parameter: newName');
|
|
256
267
|
const baseDir = destDir.replace(/\/$/, '');
|
|
257
268
|
const destPath = `${baseDir}/${newName}`;
|
|
258
269
|
const res = await tools.sequenceTools.duplicate({ path, destinationPath: destPath });
|
|
@@ -262,8 +273,8 @@ export async function handleSequenceTools(action, args, tools) {
|
|
|
262
273
|
const path = requireNonEmptyString(args.path, 'path', 'Missing required parameter: path');
|
|
263
274
|
const newName = requireNonEmptyString(args.newName, 'newName', 'Missing required parameter: newName');
|
|
264
275
|
const res = await tools.sequenceTools.rename({ path, newName });
|
|
265
|
-
const errorCode =
|
|
266
|
-
const msgLower =
|
|
276
|
+
const errorCode = getErrorString(res).toUpperCase();
|
|
277
|
+
const msgLower = getMessageString(res).toLowerCase();
|
|
267
278
|
if (res && res.success === false && (errorCode === 'OPERATION_FAILED' || msgLower.includes('failed to rename sequence'))) {
|
|
268
279
|
return cleanObject({
|
|
269
280
|
success: false,
|
|
@@ -339,7 +339,7 @@ export class IntrospectionTools {
|
|
|
339
339
|
return res;
|
|
340
340
|
}
|
|
341
341
|
catch (err) {
|
|
342
|
-
const errorMsg = err
|
|
342
|
+
const errorMsg = (err instanceof Error ? err.message : undefined) || String(err);
|
|
343
343
|
if (errorMsg.includes('404')) {
|
|
344
344
|
return { success: false, error: `Property '${params.propertyName}' not found on object '${params.objectPath}'` };
|
|
345
345
|
}
|
package/dist/tools/level.js
CHANGED
|
@@ -24,7 +24,7 @@ export class LevelTools extends BaseTool {
|
|
|
24
24
|
formatted = sanitizePath(formatted);
|
|
25
25
|
}
|
|
26
26
|
catch (e) {
|
|
27
|
-
throw new Error(`Security validation failed for level path: ${e.message}`);
|
|
27
|
+
throw new Error(`Security validation failed for level path: ${e instanceof Error ? e.message : String(e)}`);
|
|
28
28
|
}
|
|
29
29
|
formatted = formatted.replace(/\.umap$/i, '');
|
|
30
30
|
if (formatted.endsWith('/')) {
|
|
@@ -261,7 +261,7 @@ export class LevelTools extends BaseTool {
|
|
|
261
261
|
};
|
|
262
262
|
}
|
|
263
263
|
catch (e) {
|
|
264
|
-
return { success: false, error: `Export failed: ${e.message}` };
|
|
264
|
+
return { success: false, error: `Export failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
267
|
async importLevel(params) {
|
|
@@ -292,7 +292,7 @@ export class LevelTools extends BaseTool {
|
|
|
292
292
|
};
|
|
293
293
|
}
|
|
294
294
|
catch (e) {
|
|
295
|
-
return { success: false, error: `Import failed: ${e.message}` };
|
|
295
|
+
return { success: false, error: `Import failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
async saveLevelAs(params) {
|
package/dist/tools/lighting.d.ts
CHANGED
|
@@ -19,8 +19,16 @@ export declare class LightingTools {
|
|
|
19
19
|
castShadows?: boolean;
|
|
20
20
|
temperature?: number;
|
|
21
21
|
useAsAtmosphereSunLight?: boolean;
|
|
22
|
-
properties?: Record<string,
|
|
23
|
-
}): Promise<
|
|
22
|
+
properties?: Record<string, unknown>;
|
|
23
|
+
}): Promise<{
|
|
24
|
+
success: boolean;
|
|
25
|
+
message: string;
|
|
26
|
+
error?: undefined;
|
|
27
|
+
} | {
|
|
28
|
+
success: boolean;
|
|
29
|
+
error: string;
|
|
30
|
+
message?: undefined;
|
|
31
|
+
}>;
|
|
24
32
|
createPointLight(params: {
|
|
25
33
|
name: string;
|
|
26
34
|
location?: [number, number, number];
|
|
@@ -34,7 +42,15 @@ export declare class LightingTools {
|
|
|
34
42
|
yaw: number;
|
|
35
43
|
roll: number;
|
|
36
44
|
};
|
|
37
|
-
}): Promise<
|
|
45
|
+
}): Promise<{
|
|
46
|
+
success: boolean;
|
|
47
|
+
message: string;
|
|
48
|
+
error?: undefined;
|
|
49
|
+
} | {
|
|
50
|
+
success: boolean;
|
|
51
|
+
error: string;
|
|
52
|
+
message?: undefined;
|
|
53
|
+
}>;
|
|
38
54
|
createSpotLight(params: {
|
|
39
55
|
name: string;
|
|
40
56
|
location: [number, number, number];
|
|
@@ -49,7 +65,15 @@ export declare class LightingTools {
|
|
|
49
65
|
radius?: number;
|
|
50
66
|
color?: [number, number, number];
|
|
51
67
|
castShadows?: boolean;
|
|
52
|
-
}): Promise<
|
|
68
|
+
}): Promise<{
|
|
69
|
+
success: boolean;
|
|
70
|
+
message: string;
|
|
71
|
+
error?: undefined;
|
|
72
|
+
} | {
|
|
73
|
+
success: boolean;
|
|
74
|
+
error: string;
|
|
75
|
+
message?: undefined;
|
|
76
|
+
}>;
|
|
53
77
|
createRectLight(params: {
|
|
54
78
|
name: string;
|
|
55
79
|
location: [number, number, number];
|
|
@@ -63,7 +87,15 @@ export declare class LightingTools {
|
|
|
63
87
|
intensity?: number;
|
|
64
88
|
color?: [number, number, number];
|
|
65
89
|
castShadows?: boolean;
|
|
66
|
-
}): Promise<
|
|
90
|
+
}): Promise<{
|
|
91
|
+
success: boolean;
|
|
92
|
+
message: string;
|
|
93
|
+
error?: undefined;
|
|
94
|
+
} | {
|
|
95
|
+
success: boolean;
|
|
96
|
+
error: string;
|
|
97
|
+
message?: undefined;
|
|
98
|
+
}>;
|
|
67
99
|
createDynamicLight(params: {
|
|
68
100
|
name?: string;
|
|
69
101
|
lightType?: 'Point' | 'Spot' | 'Directional' | 'Rect' | string;
|
|
@@ -84,7 +116,15 @@ export declare class LightingTools {
|
|
|
84
116
|
enabled?: boolean;
|
|
85
117
|
frequency?: number;
|
|
86
118
|
};
|
|
87
|
-
}): Promise<
|
|
119
|
+
}): Promise<{
|
|
120
|
+
success: boolean;
|
|
121
|
+
message: string;
|
|
122
|
+
error?: undefined;
|
|
123
|
+
} | {
|
|
124
|
+
success: boolean;
|
|
125
|
+
error: string;
|
|
126
|
+
message?: undefined;
|
|
127
|
+
}>;
|
|
88
128
|
createSkyLight(params: {
|
|
89
129
|
name: string;
|
|
90
130
|
sourceType?: 'CapturedScene' | 'SpecifiedCubemap';
|
|
@@ -148,7 +188,14 @@ export declare class LightingTools {
|
|
|
148
188
|
buildOnlySelected?: boolean;
|
|
149
189
|
buildReflectionCaptures?: boolean;
|
|
150
190
|
levelPath?: string;
|
|
151
|
-
}): Promise<
|
|
191
|
+
}): Promise<{
|
|
192
|
+
success: boolean;
|
|
193
|
+
error: string;
|
|
194
|
+
} | {
|
|
195
|
+
success: boolean;
|
|
196
|
+
message: string;
|
|
197
|
+
error?: undefined;
|
|
198
|
+
}>;
|
|
152
199
|
createLightingEnabledLevel(params?: {
|
|
153
200
|
levelName?: string;
|
|
154
201
|
copyActors?: boolean;
|
package/dist/tools/lighting.js
CHANGED
|
@@ -128,7 +128,7 @@ export class LightingTools {
|
|
|
128
128
|
return { success: true, message: `Directional light '${name}' spawned` };
|
|
129
129
|
}
|
|
130
130
|
catch (e) {
|
|
131
|
-
return { success: false, error: `Failed to create directional light: ${e
|
|
131
|
+
return { success: false, error: `Failed to create directional light: ${(e instanceof Error ? e.message : String(e)) ?? e}` };
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
async createPointLight(params) {
|
|
@@ -202,7 +202,7 @@ export class LightingTools {
|
|
|
202
202
|
return { success: true, message: `Point light '${name}' spawned at ${location.join(', ')}` };
|
|
203
203
|
}
|
|
204
204
|
catch (e) {
|
|
205
|
-
return { success: false, error: `Failed to create point light: ${e
|
|
205
|
+
return { success: false, error: `Failed to create point light: ${(e instanceof Error ? e.message : String(e)) ?? e}` };
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
async createSpotLight(params) {
|
|
@@ -302,7 +302,7 @@ export class LightingTools {
|
|
|
302
302
|
return { success: true, message: `Spot light '${name}' spawned at ${params.location.join(', ')}` };
|
|
303
303
|
}
|
|
304
304
|
catch (e) {
|
|
305
|
-
return { success: false, error: `Failed to create spot light: ${e
|
|
305
|
+
return { success: false, error: `Failed to create spot light: ${(e instanceof Error ? e.message : String(e)) ?? e}` };
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
async createRectLight(params) {
|
|
@@ -388,7 +388,7 @@ export class LightingTools {
|
|
|
388
388
|
return { success: true, message: `Rect light '${name}' spawned at ${params.location.join(', ')}` };
|
|
389
389
|
}
|
|
390
390
|
catch (e) {
|
|
391
|
-
return { success: false, error: `Failed to create rect light: ${e
|
|
391
|
+
return { success: false, error: `Failed to create rect light: ${(e instanceof Error ? e.message : String(e)) ?? e}` };
|
|
392
392
|
}
|
|
393
393
|
}
|
|
394
394
|
async createDynamicLight(params) {
|
|
@@ -43,7 +43,7 @@ export declare class MaterialTools {
|
|
|
43
43
|
transport?: undefined;
|
|
44
44
|
warnings?: undefined;
|
|
45
45
|
}>;
|
|
46
|
-
createMaterialInstance(name: string, path: string, parentMaterial: string, parameters?: Record<string,
|
|
46
|
+
createMaterialInstance(name: string, path: string, parentMaterial: string, parameters?: Record<string, unknown>): Promise<any>;
|
|
47
47
|
addNode(params: {
|
|
48
48
|
materialPath: string;
|
|
49
49
|
nodeType: string;
|
package/dist/unreal-bridge.js
CHANGED
|
@@ -206,7 +206,7 @@ export class UnrealBridge {
|
|
|
206
206
|
const success = response.success !== false;
|
|
207
207
|
const rawResult = response.result && typeof response.result === 'object'
|
|
208
208
|
? { ...response.result }
|
|
209
|
-
:
|
|
209
|
+
: undefined;
|
|
210
210
|
const value = rawResult?.value ??
|
|
211
211
|
rawResult?.propertyValue ??
|
|
212
212
|
(success ? rawResult : undefined);
|
|
@@ -295,7 +295,7 @@ export class UnrealBridge {
|
|
|
295
295
|
const success = response.success !== false;
|
|
296
296
|
const rawResult = response.result && typeof response.result === 'object'
|
|
297
297
|
? { ...response.result }
|
|
298
|
-
:
|
|
298
|
+
: undefined;
|
|
299
299
|
if (success) {
|
|
300
300
|
return {
|
|
301
301
|
success: true,
|
|
@@ -423,7 +423,7 @@ export class UnrealBridge {
|
|
|
423
423
|
const resp = await bridge.sendAutomationRequest('system_control', { action: 'get_engine_version' }, { timeoutMs: 15000 });
|
|
424
424
|
const raw = resp && typeof resp.result === 'object'
|
|
425
425
|
? resp.result
|
|
426
|
-
:
|
|
426
|
+
: resp?.result ?? resp ?? {};
|
|
427
427
|
const version = typeof raw.version === 'string' ? raw.version : 'unknown';
|
|
428
428
|
const major = typeof raw.major === 'number' ? raw.major : 0;
|
|
429
429
|
const minor = typeof raw.minor === 'number' ? raw.minor : 0;
|
|
@@ -459,7 +459,7 @@ export class UnrealBridge {
|
|
|
459
459
|
const resp = await bridge.sendAutomationRequest('system_control', { action: 'get_feature_flags' }, { timeoutMs: 15000 });
|
|
460
460
|
const raw = resp && typeof resp.result === 'object'
|
|
461
461
|
? resp.result
|
|
462
|
-
:
|
|
462
|
+
: resp?.result ?? resp ?? {};
|
|
463
463
|
const subs = raw && typeof raw.subsystems === 'object'
|
|
464
464
|
? raw.subsystems
|
|
465
465
|
: {};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export class CommandValidator {
|
|
2
2
|
static DANGEROUS_COMMANDS = [
|
|
3
|
-
'quit', 'exit', '
|
|
3
|
+
'quit', 'exit', 'kill', 'crash',
|
|
4
|
+
'r.gpucrash', 'r.crash', 'debug crash', 'forcecrash', 'debug break',
|
|
5
|
+
'assert false', 'check(false)',
|
|
4
6
|
'viewmode visualizebuffer basecolor',
|
|
5
7
|
'viewmode visualizebuffer worldnormal',
|
|
6
|
-
'
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'obj garbage', 'obj list', 'memreport'
|
|
8
|
+
'buildpaths', 'rebuildnavigation',
|
|
9
|
+
'obj garbage', 'obj list', 'memreport',
|
|
10
|
+
'delete', 'destroy'
|
|
10
11
|
];
|
|
11
12
|
static FORBIDDEN_TOKENS = [
|
|
12
13
|
'rm ', 'rm-', 'del ', 'format ', 'shutdown', 'reboot',
|
|
@@ -8,8 +8,29 @@ export declare enum ErrorType {
|
|
|
8
8
|
TIMEOUT = "TIMEOUT",
|
|
9
9
|
UNKNOWN = "UNKNOWN"
|
|
10
10
|
}
|
|
11
|
+
interface ErrorResponseDebug {
|
|
12
|
+
errorType: ErrorType;
|
|
13
|
+
originalError: string;
|
|
14
|
+
stack?: string;
|
|
15
|
+
context?: Record<string, unknown>;
|
|
16
|
+
retriable: boolean;
|
|
17
|
+
scope: string;
|
|
18
|
+
}
|
|
19
|
+
interface ErrorToolResponse extends BaseToolResponse {
|
|
20
|
+
_debug?: ErrorResponseDebug;
|
|
21
|
+
}
|
|
22
|
+
interface ErrorLike {
|
|
23
|
+
message?: string;
|
|
24
|
+
code?: string;
|
|
25
|
+
type?: string;
|
|
26
|
+
errorType?: string;
|
|
27
|
+
stack?: string;
|
|
28
|
+
response?: {
|
|
29
|
+
status?: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
11
32
|
export declare class ErrorHandler {
|
|
12
|
-
static createErrorResponse(error:
|
|
33
|
+
static createErrorResponse(error: unknown, toolName: string, context?: Record<string, unknown>): ErrorToolResponse;
|
|
13
34
|
private static categorizeError;
|
|
14
35
|
private static getUserFriendlyMessage;
|
|
15
36
|
private static isRetriable;
|
|
@@ -18,7 +39,8 @@ export declare class ErrorHandler {
|
|
|
18
39
|
initialDelay?: number;
|
|
19
40
|
maxDelay?: number;
|
|
20
41
|
backoffMultiplier?: number;
|
|
21
|
-
shouldRetry?: (error:
|
|
42
|
+
shouldRetry?: (error: ErrorLike | Error | unknown) => boolean;
|
|
22
43
|
}): Promise<T>;
|
|
23
44
|
}
|
|
45
|
+
export {};
|
|
24
46
|
//# sourceMappingURL=error-handler.d.ts.map
|
|
@@ -10,43 +10,75 @@ export var ErrorType;
|
|
|
10
10
|
ErrorType["TIMEOUT"] = "TIMEOUT";
|
|
11
11
|
ErrorType["UNKNOWN"] = "UNKNOWN";
|
|
12
12
|
})(ErrorType || (ErrorType = {}));
|
|
13
|
+
function normalizeErrorToLike(error) {
|
|
14
|
+
if (error instanceof Error) {
|
|
15
|
+
return {
|
|
16
|
+
message: error.message,
|
|
17
|
+
stack: error.stack,
|
|
18
|
+
code: error.code
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (typeof error === 'object' && error !== null) {
|
|
22
|
+
const obj = error;
|
|
23
|
+
return {
|
|
24
|
+
message: typeof obj.message === 'string' ? obj.message : undefined,
|
|
25
|
+
code: typeof obj.code === 'string' ? obj.code : undefined,
|
|
26
|
+
type: typeof obj.type === 'string' ? obj.type : undefined,
|
|
27
|
+
errorType: typeof obj.errorType === 'string' ? obj.errorType : undefined,
|
|
28
|
+
stack: typeof obj.stack === 'string' ? obj.stack : undefined,
|
|
29
|
+
response: typeof obj.response === 'object' && obj.response !== null
|
|
30
|
+
? {
|
|
31
|
+
status: typeof obj.response.status === 'number'
|
|
32
|
+
? obj.response.status
|
|
33
|
+
: undefined
|
|
34
|
+
}
|
|
35
|
+
: undefined
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return { message: String(error) };
|
|
39
|
+
}
|
|
13
40
|
export class ErrorHandler {
|
|
14
41
|
static createErrorResponse(error, toolName, context) {
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
42
|
+
const errorObj = normalizeErrorToLike(error);
|
|
43
|
+
const errorType = this.categorizeError(errorObj);
|
|
44
|
+
const userMessage = this.getUserFriendlyMessage(errorType, errorObj);
|
|
45
|
+
const retriable = this.isRetriable(errorObj);
|
|
18
46
|
const scope = context?.scope || `tool-call/${toolName}`;
|
|
47
|
+
const errorMessage = errorObj.message || String(error);
|
|
48
|
+
const errorStack = errorObj.stack;
|
|
19
49
|
log.error(`Tool ${toolName} failed:`, {
|
|
20
50
|
type: errorType,
|
|
21
|
-
message:
|
|
51
|
+
message: errorMessage,
|
|
22
52
|
retriable,
|
|
23
53
|
scope,
|
|
24
54
|
context
|
|
25
55
|
});
|
|
26
|
-
|
|
56
|
+
const response = {
|
|
27
57
|
success: false,
|
|
28
58
|
error: userMessage,
|
|
29
59
|
message: `Failed to execute ${toolName}: ${userMessage}`,
|
|
30
|
-
retriable
|
|
31
|
-
scope
|
|
32
|
-
...(process.env.NODE_ENV === 'development' && {
|
|
33
|
-
_debug: {
|
|
34
|
-
errorType,
|
|
35
|
-
originalError: error.message || String(error),
|
|
36
|
-
stack: error.stack,
|
|
37
|
-
context,
|
|
38
|
-
retriable,
|
|
39
|
-
scope
|
|
40
|
-
}
|
|
41
|
-
})
|
|
60
|
+
retriable,
|
|
61
|
+
scope
|
|
42
62
|
};
|
|
63
|
+
if (process.env.NODE_ENV === 'development') {
|
|
64
|
+
response._debug = {
|
|
65
|
+
errorType,
|
|
66
|
+
originalError: errorMessage,
|
|
67
|
+
stack: errorStack,
|
|
68
|
+
context,
|
|
69
|
+
retriable,
|
|
70
|
+
scope
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return response;
|
|
43
74
|
}
|
|
44
75
|
static categorizeError(error) {
|
|
45
|
-
const
|
|
76
|
+
const errorObj = typeof error === 'object' ? error : null;
|
|
77
|
+
const explicitType = (errorObj?.type || errorObj?.errorType || '').toString().toUpperCase();
|
|
46
78
|
if (explicitType && Object.values(ErrorType).includes(explicitType)) {
|
|
47
79
|
return explicitType;
|
|
48
80
|
}
|
|
49
|
-
const errorMessage =
|
|
81
|
+
const errorMessage = (errorObj?.message || String(error)).toLowerCase();
|
|
50
82
|
if (errorMessage.includes('econnrefused') ||
|
|
51
83
|
errorMessage.includes('timeout') ||
|
|
52
84
|
errorMessage.includes('connection') ||
|
|
@@ -77,7 +109,9 @@ export class ErrorHandler {
|
|
|
77
109
|
return ErrorType.UNKNOWN;
|
|
78
110
|
}
|
|
79
111
|
static getUserFriendlyMessage(type, error) {
|
|
80
|
-
const originalMessage = error
|
|
112
|
+
const originalMessage = (typeof error === 'object' && error !== null && 'message' in error)
|
|
113
|
+
? error.message || String(error)
|
|
114
|
+
: String(error);
|
|
81
115
|
switch (type) {
|
|
82
116
|
case ErrorType.CONNECTION:
|
|
83
117
|
return 'Failed to connect to Unreal Engine. Please ensure the Automation Bridge plugin is active and the editor is running.';
|
|
@@ -97,9 +131,10 @@ export class ErrorHandler {
|
|
|
97
131
|
}
|
|
98
132
|
static isRetriable(error) {
|
|
99
133
|
try {
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
const
|
|
134
|
+
const errorObj = typeof error === 'object' ? error : null;
|
|
135
|
+
const code = (errorObj?.code || '').toString().toUpperCase();
|
|
136
|
+
const msg = (errorObj?.message || String(error) || '').toLowerCase();
|
|
137
|
+
const status = Number(errorObj?.response?.status);
|
|
103
138
|
if (['ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EPIPE'].includes(code))
|
|
104
139
|
return true;
|
|
105
140
|
if (/timeout|timed out|network|connection|closed|unavailable|busy|temporar/.test(msg))
|
|
@@ -10,11 +10,14 @@ export interface Rot3Obj {
|
|
|
10
10
|
}
|
|
11
11
|
export type Vec3Tuple = [number, number, number];
|
|
12
12
|
export type Rot3Tuple = [number, number, number];
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export declare function
|
|
16
|
-
export declare function
|
|
13
|
+
type VectorInput = Vec3Obj | Vec3Tuple | Record<string, unknown> | unknown[];
|
|
14
|
+
type RotationInput = Rot3Obj | Rot3Tuple | Record<string, unknown> | unknown[];
|
|
15
|
+
export declare function toVec3Object(input: VectorInput | unknown): Vec3Obj | null;
|
|
16
|
+
export declare function toRotObject(input: RotationInput | unknown): Rot3Obj | null;
|
|
17
|
+
export declare function toVec3Tuple(input: VectorInput | unknown): Vec3Tuple | null;
|
|
18
|
+
export declare function toRotTuple(input: RotationInput | unknown): Rot3Tuple | null;
|
|
17
19
|
export declare function toFiniteNumber(raw: unknown): number | undefined;
|
|
18
20
|
export declare function normalizePartialVector(value: any, alternateKeys?: string[]): Record<string, number> | undefined;
|
|
19
21
|
export declare function normalizeTransformInput(transform: any): Record<string, unknown> | undefined;
|
|
22
|
+
export {};
|
|
20
23
|
//# sourceMappingURL=normalize.d.ts.map
|
package/dist/utils/normalize.js
CHANGED
|
@@ -3,13 +3,14 @@ export function toVec3Object(input) {
|
|
|
3
3
|
if (Array.isArray(input) && input.length === 3) {
|
|
4
4
|
const [x, y, z] = input;
|
|
5
5
|
if ([x, y, z].every(v => typeof v === 'number' && isFinite(v))) {
|
|
6
|
-
return { x, y, z };
|
|
6
|
+
return { x: x, y: y, z: z };
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
if (input && typeof input === 'object') {
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
9
|
+
if (input && typeof input === 'object' && !Array.isArray(input)) {
|
|
10
|
+
const obj = input;
|
|
11
|
+
const x = Number(obj.x ?? obj.X);
|
|
12
|
+
const y = Number(obj.y ?? obj.Y);
|
|
13
|
+
const z = Number(obj.z ?? obj.Z);
|
|
13
14
|
if ([x, y, z].every(v => typeof v === 'number' && !isNaN(v) && isFinite(v))) {
|
|
14
15
|
return { x, y, z };
|
|
15
16
|
}
|
|
@@ -23,13 +24,14 @@ export function toRotObject(input) {
|
|
|
23
24
|
if (Array.isArray(input) && input.length === 3) {
|
|
24
25
|
const [pitch, yaw, roll] = input;
|
|
25
26
|
if ([pitch, yaw, roll].every(v => typeof v === 'number' && isFinite(v))) {
|
|
26
|
-
return { pitch, yaw, roll };
|
|
27
|
+
return { pitch: pitch, yaw: yaw, roll: roll };
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
if (input && typeof input === 'object') {
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
30
|
+
if (input && typeof input === 'object' && !Array.isArray(input)) {
|
|
31
|
+
const obj = input;
|
|
32
|
+
const pitch = Number(obj.pitch ?? obj.Pitch);
|
|
33
|
+
const yaw = Number(obj.yaw ?? obj.Yaw);
|
|
34
|
+
const roll = Number(obj.roll ?? obj.Roll);
|
|
33
35
|
if ([pitch, yaw, roll].every(v => typeof v === 'number' && !isNaN(v) && isFinite(v))) {
|
|
34
36
|
return { pitch, yaw, roll };
|
|
35
37
|
}
|