unreal-engine-mcp-server 0.5.2 → 0.5.4
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/CHANGELOG.md +195 -0
- package/README.md +9 -6
- package/dist/automation/bridge.d.ts +1 -0
- package/dist/automation/bridge.js +62 -4
- package/dist/automation/types.d.ts +1 -0
- package/dist/config/class-aliases.d.ts +5 -0
- package/dist/config/class-aliases.js +30 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.js +5 -0
- package/dist/graphql/server.d.ts +0 -1
- package/dist/graphql/server.js +15 -16
- package/dist/index.js +1 -1
- package/dist/services/metrics-server.d.ts +2 -1
- package/dist/services/metrics-server.js +29 -4
- package/dist/tools/consolidated-tool-definitions.js +3 -3
- package/dist/tools/debug.d.ts +5 -0
- package/dist/tools/debug.js +7 -0
- package/dist/tools/handlers/actor-handlers.js +4 -27
- package/dist/tools/handlers/asset-handlers.js +13 -1
- package/dist/tools/handlers/blueprint-handlers.d.ts +4 -1
- package/dist/tools/handlers/common-handlers.d.ts +11 -11
- package/dist/tools/handlers/common-handlers.js +6 -4
- package/dist/tools/handlers/editor-handlers.d.ts +2 -1
- package/dist/tools/handlers/editor-handlers.js +6 -6
- package/dist/tools/handlers/effect-handlers.js +3 -0
- package/dist/tools/handlers/graph-handlers.d.ts +2 -1
- package/dist/tools/handlers/graph-handlers.js +1 -1
- package/dist/tools/handlers/input-handlers.d.ts +5 -1
- package/dist/tools/handlers/level-handlers.d.ts +2 -1
- package/dist/tools/handlers/level-handlers.js +3 -3
- package/dist/tools/handlers/lighting-handlers.d.ts +2 -1
- package/dist/tools/handlers/lighting-handlers.js +3 -0
- package/dist/tools/handlers/pipeline-handlers.d.ts +2 -1
- package/dist/tools/handlers/pipeline-handlers.js +64 -10
- package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
- package/dist/tools/handlers/system-handlers.d.ts +1 -1
- package/dist/tools/input.d.ts +5 -1
- package/dist/tools/input.js +37 -1
- package/dist/tools/lighting.d.ts +1 -0
- package/dist/tools/lighting.js +7 -0
- package/dist/tools/physics.d.ts +1 -1
- package/dist/tools/sequence.d.ts +1 -0
- package/dist/tools/sequence.js +7 -0
- package/dist/types/handler-types.d.ts +343 -0
- package/dist/types/handler-types.js +2 -0
- package/dist/unreal-bridge.d.ts +1 -1
- package/dist/unreal-bridge.js +8 -6
- package/dist/utils/command-validator.d.ts +1 -0
- package/dist/utils/command-validator.js +11 -1
- package/dist/utils/error-handler.js +3 -1
- package/dist/utils/response-validator.js +2 -2
- package/dist/utils/safe-json.d.ts +1 -1
- package/dist/utils/safe-json.js +3 -6
- package/dist/utils/unreal-command-queue.js +1 -1
- package/dist/utils/validation.js +6 -2
- package/docs/handler-mapping.md +6 -1
- package/package.json +2 -2
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +25 -1
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +40 -58
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +27 -46
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +16 -1
- package/server.json +2 -2
- package/src/automation/bridge.ts +80 -10
- package/src/automation/types.ts +1 -0
- package/src/config/class-aliases.ts +65 -0
- package/src/constants.ts +10 -0
- package/src/graphql/server.ts +23 -23
- package/src/index.ts +1 -1
- package/src/services/metrics-server.ts +40 -6
- package/src/tools/consolidated-tool-definitions.ts +3 -3
- package/src/tools/debug.ts +8 -0
- package/src/tools/handlers/actor-handlers.ts +5 -31
- package/src/tools/handlers/asset-handlers.ts +19 -1
- package/src/tools/handlers/blueprint-handlers.ts +1 -1
- package/src/tools/handlers/common-handlers.ts +32 -11
- package/src/tools/handlers/editor-handlers.ts +8 -7
- package/src/tools/handlers/effect-handlers.ts +4 -0
- package/src/tools/handlers/graph-handlers.ts +7 -6
- package/src/tools/handlers/level-handlers.ts +5 -4
- package/src/tools/handlers/lighting-handlers.ts +5 -1
- package/src/tools/handlers/pipeline-handlers.ts +83 -16
- package/src/tools/input.ts +60 -1
- package/src/tools/lighting.ts +11 -0
- package/src/tools/physics.ts +1 -1
- package/src/tools/sequence.ts +11 -0
- package/src/types/handler-types.ts +442 -0
- package/src/unreal-bridge.ts +8 -6
- package/src/utils/command-validator.ts +23 -1
- package/src/utils/error-handler.ts +4 -1
- package/src/utils/response-validator.ts +7 -9
- package/src/utils/safe-json.ts +20 -15
- package/src/utils/unreal-command-queue.ts +3 -1
- package/src/utils/validation.test.ts +3 -3
- package/src/utils/validation.ts +36 -26
- package/tests/test-console-command.mjs +1 -1
- package/tests/test-runner.mjs +63 -3
- package/tests/run-unreal-tool-tests.mjs +0 -948
- package/tests/test-asset-errors.mjs +0 -35
|
@@ -1,43 +1,20 @@
|
|
|
1
1
|
import { executeAutomationRequest } from './common-handlers.js';
|
|
2
2
|
import { normalizeArgs } from './argument-helper.js';
|
|
3
3
|
import { ResponseFactory } from '../../utils/response-factory.js';
|
|
4
|
+
import { ACTOR_CLASS_ALIASES, getRequiredComponent } from '../../config/class-aliases.js';
|
|
4
5
|
const handlers = {
|
|
5
6
|
spawn: async (args, tools) => {
|
|
6
|
-
const classAliases = {
|
|
7
|
-
'SplineActor': '/Script/Engine.Actor',
|
|
8
|
-
'Spline': '/Script/Engine.Actor',
|
|
9
|
-
'PointLight': '/Script/Engine.PointLight',
|
|
10
|
-
'SpotLight': '/Script/Engine.SpotLight',
|
|
11
|
-
'DirectionalLight': '/Script/Engine.DirectionalLight',
|
|
12
|
-
'Camera': '/Script/Engine.CameraActor',
|
|
13
|
-
'CameraActor': '/Script/Engine.CameraActor',
|
|
14
|
-
'StaticMeshActor': '/Script/Engine.StaticMeshActor',
|
|
15
|
-
'SkeletalMeshActor': '/Script/Engine.SkeletalMeshActor',
|
|
16
|
-
'PlayerStart': '/Script/Engine.PlayerStart',
|
|
17
|
-
'TriggerBox': '/Script/Engine.TriggerBox',
|
|
18
|
-
'TriggerSphere': '/Script/Engine.TriggerSphere',
|
|
19
|
-
'BlockingVolume': '/Script/Engine.BlockingVolume',
|
|
20
|
-
'Pawn': '/Script/Engine.Pawn',
|
|
21
|
-
'Character': '/Script/Engine.Character',
|
|
22
|
-
'Actor': '/Script/Engine.Actor'
|
|
23
|
-
};
|
|
24
7
|
const params = normalizeArgs(args, [
|
|
25
|
-
{ key: 'classPath', aliases: ['class', 'type', 'actorClass'], required: true, map:
|
|
8
|
+
{ key: 'classPath', aliases: ['class', 'type', 'actorClass'], required: true, map: ACTOR_CLASS_ALIASES },
|
|
26
9
|
{ key: 'actorName', aliases: ['name'] },
|
|
27
10
|
{ key: 'timeoutMs', default: undefined }
|
|
28
11
|
]);
|
|
29
12
|
const timeoutMs = typeof params.timeoutMs === 'number' ? params.timeoutMs : undefined;
|
|
30
13
|
if (typeof timeoutMs === 'number' && timeoutMs > 0 && timeoutMs < 200) {
|
|
31
|
-
|
|
32
|
-
success: false,
|
|
33
|
-
error: `Timeout too small for spawn operation: ${timeoutMs}ms`,
|
|
34
|
-
message: 'Timeout too small for spawn operation'
|
|
35
|
-
};
|
|
14
|
+
throw new Error(`Timeout too small for spawn operation: ${timeoutMs}ms`);
|
|
36
15
|
}
|
|
37
16
|
const originalClass = args.classPath || args.class || args.type || args.actorClass;
|
|
38
|
-
const componentToAdd = (originalClass
|
|
39
|
-
? 'SplineComponent'
|
|
40
|
-
: undefined;
|
|
17
|
+
const componentToAdd = getRequiredComponent(originalClass);
|
|
41
18
|
const result = await tools.actorTools.spawn({
|
|
42
19
|
classPath: params.classPath,
|
|
43
20
|
actorName: params.actorName,
|
|
@@ -168,7 +168,19 @@ export async function handleAssetTools(action, args, tools) {
|
|
|
168
168
|
if (paths.length === 0) {
|
|
169
169
|
throw new Error('No paths provided for delete action');
|
|
170
170
|
}
|
|
171
|
-
const
|
|
171
|
+
const normalizedPaths = paths.map(p => {
|
|
172
|
+
let normalized = p.replace(/\\/g, '/').trim();
|
|
173
|
+
const lastSlash = normalized.lastIndexOf('/');
|
|
174
|
+
if (lastSlash >= 0) {
|
|
175
|
+
const afterSlash = normalized.substring(lastSlash + 1);
|
|
176
|
+
const dotIndex = afterSlash.indexOf('.');
|
|
177
|
+
if (dotIndex > 0) {
|
|
178
|
+
normalized = normalized.substring(0, lastSlash + 1 + dotIndex);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return normalized;
|
|
182
|
+
});
|
|
183
|
+
const res = await tools.assetTools.deleteAssets({ paths: normalizedPaths });
|
|
172
184
|
return ResponseFactory.success(res, 'Assets deleted successfully');
|
|
173
185
|
}
|
|
174
186
|
case 'generate_lods': {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
2
|
export declare function handleBlueprintTools(action: string, args: any, tools: ITools): Promise<any>;
|
|
3
|
-
export declare function handleBlueprintGet(args: any, tools: ITools): Promise<
|
|
3
|
+
export declare function handleBlueprintGet(args: any, tools: ITools): Promise<{
|
|
4
|
+
success?: boolean;
|
|
5
|
+
message?: string;
|
|
6
|
+
} | null>;
|
|
4
7
|
//# sourceMappingURL=blueprint-handlers.d.ts.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
|
-
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
2
|
+
import type { HandlerArgs, Vector3, Rotator } from '../../types/handler-types.js';
|
|
3
|
+
export declare function ensureArgsPresent(args: unknown): asserts args is Record<string, unknown>;
|
|
4
|
+
export declare function requireAction(args: HandlerArgs): string;
|
|
5
|
+
export declare function requireNonEmptyString(value: unknown, field: string, message?: string): string;
|
|
6
|
+
export declare function executeAutomationRequest(tools: ITools, toolName: string, args: HandlerArgs, errorMessage?: string, options?: {
|
|
6
7
|
timeoutMs?: number;
|
|
7
|
-
}): Promise<
|
|
8
|
-
|
|
9
|
-
export declare function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} | undefined;
|
|
8
|
+
}): Promise<unknown>;
|
|
9
|
+
type LocationInput = Vector3 | [number, number, number] | number[] | null | undefined;
|
|
10
|
+
export declare function normalizeLocation(location: LocationInput): [number, number, number] | undefined;
|
|
11
|
+
type RotationInput = Rotator | [number, number, number] | number[] | null | undefined;
|
|
12
|
+
export declare function normalizeRotation(rotation: RotationInput): Rotator | undefined;
|
|
13
|
+
export {};
|
|
14
14
|
//# sourceMappingURL=common-handlers.d.ts.map
|
|
@@ -34,7 +34,8 @@ export function normalizeLocation(location) {
|
|
|
34
34
|
return [Number(location[0]) || 0, Number(location[1]) || 0, Number(location[2]) || 0];
|
|
35
35
|
}
|
|
36
36
|
if (typeof location === 'object' && ('x' in location || 'y' in location || 'z' in location)) {
|
|
37
|
-
|
|
37
|
+
const loc = location;
|
|
38
|
+
return [Number(loc.x) || 0, Number(loc.y) || 0, Number(loc.z) || 0];
|
|
38
39
|
}
|
|
39
40
|
return undefined;
|
|
40
41
|
}
|
|
@@ -45,10 +46,11 @@ export function normalizeRotation(rotation) {
|
|
|
45
46
|
return { pitch: Number(rotation[0]) || 0, yaw: Number(rotation[1]) || 0, roll: Number(rotation[2]) || 0 };
|
|
46
47
|
}
|
|
47
48
|
if (typeof rotation === 'object') {
|
|
49
|
+
const rot = rotation;
|
|
48
50
|
return {
|
|
49
|
-
pitch: Number(
|
|
50
|
-
yaw: Number(
|
|
51
|
-
roll: Number(
|
|
51
|
+
pitch: Number(rot.pitch) || 0,
|
|
52
|
+
yaw: Number(rot.yaw) || 0,
|
|
53
|
+
roll: Number(rot.roll) || 0
|
|
52
54
|
};
|
|
53
55
|
}
|
|
54
56
|
return undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
|
-
|
|
2
|
+
import type { EditorArgs } from '../../types/handler-types.js';
|
|
3
|
+
export declare function handleEditorTools(action: string, args: EditorArgs, tools: ITools): Promise<unknown>;
|
|
3
4
|
//# sourceMappingURL=editor-handlers.d.ts.map
|
|
@@ -14,14 +14,14 @@ export async function handleEditorTools(action, args, tools) {
|
|
|
14
14
|
case 'eject': {
|
|
15
15
|
const inPie = await tools.editorTools.isInPIE();
|
|
16
16
|
if (!inPie) {
|
|
17
|
-
|
|
17
|
+
throw new Error('Cannot eject while not in PIE');
|
|
18
18
|
}
|
|
19
19
|
return await executeAutomationRequest(tools, 'control_editor', { action: 'eject' });
|
|
20
20
|
}
|
|
21
21
|
case 'possess': {
|
|
22
22
|
const inPie = await tools.editorTools.isInPIE();
|
|
23
23
|
if (!inPie) {
|
|
24
|
-
|
|
24
|
+
throw new Error('Cannot possess actor while not in PIE');
|
|
25
25
|
}
|
|
26
26
|
return await executeAutomationRequest(tools, 'control_editor', args);
|
|
27
27
|
}
|
|
@@ -38,7 +38,7 @@ export async function handleEditorTools(action, args, tools) {
|
|
|
38
38
|
return cleanObject(res);
|
|
39
39
|
}
|
|
40
40
|
case 'console_command': {
|
|
41
|
-
const res = await tools.editorTools.executeConsoleCommand(args.command);
|
|
41
|
+
const res = await tools.editorTools.executeConsoleCommand(args.command ?? '');
|
|
42
42
|
return cleanObject(res);
|
|
43
43
|
}
|
|
44
44
|
case 'set_camera': {
|
|
@@ -59,17 +59,17 @@ export async function handleEditorTools(action, args, tools) {
|
|
|
59
59
|
return { success: true, message: 'Stepped frame', action: 'step_frame' };
|
|
60
60
|
}
|
|
61
61
|
case 'create_bookmark': {
|
|
62
|
-
const idx = parseInt(args.bookmarkName) || 0;
|
|
62
|
+
const idx = parseInt(args.bookmarkName ?? '0') || 0;
|
|
63
63
|
await tools.editorTools.executeConsoleCommand(`r.SetBookmark ${idx}`);
|
|
64
64
|
return { success: true, message: `Created bookmark ${idx}`, action: 'create_bookmark' };
|
|
65
65
|
}
|
|
66
66
|
case 'jump_to_bookmark': {
|
|
67
|
-
const idx = parseInt(args.bookmarkName) || 0;
|
|
67
|
+
const idx = parseInt(args.bookmarkName ?? '0') || 0;
|
|
68
68
|
await tools.editorTools.executeConsoleCommand(`r.JumpToBookmark ${idx}`);
|
|
69
69
|
return { success: true, message: `Jumped to bookmark ${idx}`, action: 'jump_to_bookmark' };
|
|
70
70
|
}
|
|
71
71
|
case 'set_preferences': {
|
|
72
|
-
const res = await tools.editorTools.setEditorPreferences(args.category, args.preferences);
|
|
72
|
+
const res = await tools.editorTools.setEditorPreferences(args.category ?? '', args.preferences ?? {});
|
|
73
73
|
return cleanObject(res);
|
|
74
74
|
}
|
|
75
75
|
case 'open_asset': {
|
|
@@ -78,6 +78,9 @@ export async function handleEffectTools(action, args, tools) {
|
|
|
78
78
|
if (action === 'clear_debug_shapes') {
|
|
79
79
|
return executeAutomationRequest(tools, action, args);
|
|
80
80
|
}
|
|
81
|
+
if (action === 'list_debug_shapes') {
|
|
82
|
+
return executeAutomationRequest(tools, 'list_debug_shapes', args);
|
|
83
|
+
}
|
|
81
84
|
if (action === 'cleanup') {
|
|
82
85
|
args.action = 'cleanup';
|
|
83
86
|
args.subAction = 'cleanup';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
|
-
|
|
2
|
+
import type { GraphArgs } from '../../types/handler-types.js';
|
|
3
|
+
export declare function handleGraphTools(toolName: string, action: string, args: GraphArgs, tools: ITools): Promise<any>;
|
|
3
4
|
//# sourceMappingURL=graph-handlers.d.ts.map
|
|
@@ -13,7 +13,7 @@ export async function handleGraphTools(toolName, action, args, tools) {
|
|
|
13
13
|
case 'manage_behavior_tree':
|
|
14
14
|
return handleBehaviorTree(action, args, tools);
|
|
15
15
|
default:
|
|
16
|
-
|
|
16
|
+
throw new Error(`Unknown graph tool: ${toolName}`);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
async function handleBlueprintGraph(action, args, tools) {
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
|
-
export declare function handleInputTools(action: string, args: any, tools: ITools): Promise<any
|
|
2
|
+
export declare function handleInputTools(action: string, args: any, tools: ITools): Promise<import("../../automation/types.js").AutomationBridgeResponseMessage | import("../../types/tool-interfaces.js").StandardActionResponse<any> | {
|
|
3
|
+
success: boolean;
|
|
4
|
+
error: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}>;
|
|
3
7
|
//# sourceMappingURL=input-handlers.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
|
-
|
|
2
|
+
import type { LevelArgs } from '../../types/handler-types.js';
|
|
3
|
+
export declare function handleLevelTools(action: string, args: LevelArgs, tools: ITools): Promise<any>;
|
|
3
4
|
//# sourceMappingURL=level-handlers.d.ts.map
|
|
@@ -111,14 +111,14 @@ export async function handleLevelTools(action, args, tools) {
|
|
|
111
111
|
case 'export_level': {
|
|
112
112
|
const res = await tools.levelTools.exportLevel({
|
|
113
113
|
levelPath: args.levelPath,
|
|
114
|
-
exportPath: args.exportPath
|
|
114
|
+
exportPath: args.exportPath ?? args.destinationPath ?? '',
|
|
115
115
|
timeoutMs: typeof args.timeoutMs === 'number' ? args.timeoutMs : undefined
|
|
116
116
|
});
|
|
117
117
|
return cleanObject(res);
|
|
118
118
|
}
|
|
119
119
|
case 'import_level': {
|
|
120
120
|
const res = await tools.levelTools.importLevel({
|
|
121
|
-
packagePath: args.packagePath
|
|
121
|
+
packagePath: args.packagePath ?? args.sourcePath ?? '',
|
|
122
122
|
destinationPath: args.destinationPath,
|
|
123
123
|
timeoutMs: typeof args.timeoutMs === 'number' ? args.timeoutMs : undefined
|
|
124
124
|
});
|
|
@@ -133,7 +133,7 @@ export async function handleLevelTools(action, args, tools) {
|
|
|
133
133
|
return cleanObject(res);
|
|
134
134
|
}
|
|
135
135
|
case 'delete': {
|
|
136
|
-
const levelPaths = Array.isArray(args.levelPaths) ? args.levelPaths : [args.levelPath];
|
|
136
|
+
const levelPaths = Array.isArray(args.levelPaths) ? args.levelPaths.filter((p) => typeof p === 'string') : (args.levelPath ? [args.levelPath] : []);
|
|
137
137
|
const res = await tools.levelTools.deleteLevels({ levelPaths });
|
|
138
138
|
return cleanObject(res);
|
|
139
139
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
|
-
|
|
2
|
+
import type { LightingArgs } from '../../types/handler-types.js';
|
|
3
|
+
export declare function handleLightingTools(action: string, args: LightingArgs, tools: ITools): Promise<any>;
|
|
3
4
|
//# sourceMappingURL=lighting-handlers.d.ts.map
|
|
@@ -137,6 +137,9 @@ export async function handleLightingTools(action, args, tools) {
|
|
|
137
137
|
useTemplate: args.useTemplate
|
|
138
138
|
}));
|
|
139
139
|
}
|
|
140
|
+
case 'list_light_types': {
|
|
141
|
+
return cleanObject(await tools.lightingTools.listLightTypes());
|
|
142
|
+
}
|
|
140
143
|
default:
|
|
141
144
|
throw new Error(`Unknown lighting action: ${action}`);
|
|
142
145
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
|
-
|
|
2
|
+
import type { PipelineArgs } from '../../types/handler-types.js';
|
|
3
|
+
export declare function handlePipelineTools(action: string, args: PipelineArgs, tools: ITools): Promise<unknown>;
|
|
3
4
|
//# sourceMappingURL=pipeline-handlers.d.ts.map
|
|
@@ -3,6 +3,55 @@ import { executeAutomationRequest } from './common-handlers.js';
|
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import fs from 'fs';
|
|
6
|
+
function validateUbtArgumentsString(extraArgs) {
|
|
7
|
+
if (!extraArgs || typeof extraArgs !== 'string') {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const forbiddenChars = ['\n', '\r', ';', '|', '`', '&&', '||', '>', '<'];
|
|
11
|
+
for (const char of forbiddenChars) {
|
|
12
|
+
if (extraArgs.includes(char)) {
|
|
13
|
+
throw new Error(`UBT arguments contain forbidden character(s) and are blocked for safety. Blocked: ${JSON.stringify(char)}.`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function tokenizeArgs(extraArgs) {
|
|
18
|
+
if (!extraArgs) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
const args = [];
|
|
22
|
+
let current = '';
|
|
23
|
+
let inQuotes = false;
|
|
24
|
+
let escapeNext = false;
|
|
25
|
+
for (let i = 0; i < extraArgs.length; i++) {
|
|
26
|
+
const ch = extraArgs[i];
|
|
27
|
+
if (escapeNext) {
|
|
28
|
+
current += ch;
|
|
29
|
+
escapeNext = false;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (ch === '\\') {
|
|
33
|
+
escapeNext = true;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (ch === '"') {
|
|
37
|
+
inQuotes = !inQuotes;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (!inQuotes && /\s/.test(ch)) {
|
|
41
|
+
if (current.length > 0) {
|
|
42
|
+
args.push(current);
|
|
43
|
+
current = '';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
current += ch;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (current.length > 0) {
|
|
51
|
+
args.push(current);
|
|
52
|
+
}
|
|
53
|
+
return args;
|
|
54
|
+
}
|
|
6
55
|
export async function handlePipelineTools(action, args, tools) {
|
|
7
56
|
switch (action) {
|
|
8
57
|
case 'run_ubt': {
|
|
@@ -11,8 +60,9 @@ export async function handlePipelineTools(action, args, tools) {
|
|
|
11
60
|
const configuration = args.configuration || 'Development';
|
|
12
61
|
const extraArgs = args.arguments || '';
|
|
13
62
|
if (!target) {
|
|
14
|
-
|
|
63
|
+
throw new Error('Target is required for run_ubt');
|
|
15
64
|
}
|
|
65
|
+
validateUbtArgumentsString(extraArgs);
|
|
16
66
|
let ubtPath = 'UnrealBuildTool';
|
|
17
67
|
const enginePath = process.env.UE_ENGINE_PATH || process.env.UNREAL_ENGINE_PATH;
|
|
18
68
|
if (enginePath) {
|
|
@@ -26,7 +76,7 @@ export async function handlePipelineTools(action, args, tools) {
|
|
|
26
76
|
projectPath = args.projectPath;
|
|
27
77
|
}
|
|
28
78
|
if (!projectPath) {
|
|
29
|
-
|
|
79
|
+
throw new Error('UE_PROJECT_PATH environment variable is not set and no projectPath argument was provided.');
|
|
30
80
|
}
|
|
31
81
|
let uprojectFile = projectPath;
|
|
32
82
|
if (!uprojectFile.endsWith('.uproject')) {
|
|
@@ -38,18 +88,20 @@ export async function handlePipelineTools(action, args, tools) {
|
|
|
38
88
|
}
|
|
39
89
|
}
|
|
40
90
|
catch (_e) {
|
|
41
|
-
|
|
91
|
+
throw new Error(`Could not read project directory: ${projectPath}`);
|
|
42
92
|
}
|
|
43
93
|
}
|
|
94
|
+
const projectArg = `-Project="${uprojectFile}"`;
|
|
95
|
+
const extraTokens = tokenizeArgs(extraArgs);
|
|
44
96
|
const cmdArgs = [
|
|
45
97
|
target,
|
|
46
98
|
platform,
|
|
47
99
|
configuration,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
]
|
|
100
|
+
projectArg,
|
|
101
|
+
...extraTokens
|
|
102
|
+
];
|
|
51
103
|
return new Promise((resolve) => {
|
|
52
|
-
const child = spawn(ubtPath, cmdArgs, { shell:
|
|
104
|
+
const child = spawn(ubtPath, cmdArgs, { shell: false });
|
|
53
105
|
const MAX_OUTPUT_SIZE = 20 * 1024;
|
|
54
106
|
let stdout = '';
|
|
55
107
|
let stderr = '';
|
|
@@ -73,12 +125,13 @@ export async function handlePipelineTools(action, args, tools) {
|
|
|
73
125
|
const truncatedNote = (stdout.length >= MAX_OUTPUT_SIZE || stderr.length >= MAX_OUTPUT_SIZE)
|
|
74
126
|
? '\n[Output truncated for response payload]'
|
|
75
127
|
: '';
|
|
128
|
+
const quotedArgs = cmdArgs.map(arg => arg.includes(' ') ? `"${arg}"` : arg);
|
|
76
129
|
if (code === 0) {
|
|
77
130
|
resolve({
|
|
78
131
|
success: true,
|
|
79
132
|
message: 'UnrealBuildTool finished successfully',
|
|
80
133
|
output: stdout + truncatedNote,
|
|
81
|
-
command: `${ubtPath} ${
|
|
134
|
+
command: `${ubtPath} ${quotedArgs.join(' ')}`
|
|
82
135
|
});
|
|
83
136
|
}
|
|
84
137
|
else {
|
|
@@ -88,16 +141,17 @@ export async function handlePipelineTools(action, args, tools) {
|
|
|
88
141
|
message: `UnrealBuildTool failed with code ${code}`,
|
|
89
142
|
output: stdout + truncatedNote,
|
|
90
143
|
errorOutput: stderr + truncatedNote,
|
|
91
|
-
command: `${ubtPath} ${
|
|
144
|
+
command: `${ubtPath} ${quotedArgs.join(' ')}`
|
|
92
145
|
});
|
|
93
146
|
}
|
|
94
147
|
});
|
|
95
148
|
child.on('error', (err) => {
|
|
149
|
+
const quotedArgs = cmdArgs.map(arg => arg.includes(' ') ? `"${arg}"` : arg);
|
|
96
150
|
resolve({
|
|
97
151
|
success: false,
|
|
98
152
|
error: 'SPAWN_FAILED',
|
|
99
153
|
message: `Failed to spawn UnrealBuildTool: ${err.message}`,
|
|
100
|
-
command: `${ubtPath} ${
|
|
154
|
+
command: `${ubtPath} ${quotedArgs.join(' ')}`
|
|
101
155
|
});
|
|
102
156
|
});
|
|
103
157
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
|
-
export declare function handleSequenceTools(action: string, args: Record<string, unknown>, tools: ITools): Promise<
|
|
2
|
+
export declare function handleSequenceTools(action: string, args: Record<string, unknown>, tools: ITools): Promise<unknown>;
|
|
3
3
|
//# sourceMappingURL=sequence-handlers.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ITools } from '../../types/tool-interfaces.js';
|
|
2
2
|
export declare function handleSystemTools(action: string, args: any, tools: ITools): Promise<any>;
|
|
3
|
-
export declare function handleConsoleCommand(args: any, tools: ITools): Promise<
|
|
3
|
+
export declare function handleConsoleCommand(args: any, tools: ITools): Promise<unknown>;
|
|
4
4
|
//# sourceMappingURL=system-handlers.d.ts.map
|
package/dist/tools/input.d.ts
CHANGED
|
@@ -11,7 +11,11 @@ export declare class InputTools {
|
|
|
11
11
|
setAutomationBridge(bridge: AutomationBridge): void;
|
|
12
12
|
createInputAction(name: string, path: string): Promise<import("../automation/types.js").AutomationBridgeResponseMessage>;
|
|
13
13
|
createInputMappingContext(name: string, path: string): Promise<import("../automation/types.js").AutomationBridgeResponseMessage>;
|
|
14
|
-
addMapping(contextPath: string, actionPath: string, key: string): Promise<import("../automation/types.js").AutomationBridgeResponseMessage
|
|
14
|
+
addMapping(contextPath: string, actionPath: string, key: string): Promise<import("../automation/types.js").AutomationBridgeResponseMessage | {
|
|
15
|
+
success: boolean;
|
|
16
|
+
error: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}>;
|
|
15
19
|
removeMapping(contextPath: string, actionPath: string): Promise<import("../automation/types.js").AutomationBridgeResponseMessage>;
|
|
16
20
|
}
|
|
17
21
|
export declare const inputTools: ToolDefinition;
|
package/dist/tools/input.js
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
const VALID_KEY_NAMES = new Set([
|
|
2
|
+
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
|
3
|
+
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
|
4
|
+
'Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine',
|
|
5
|
+
'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12',
|
|
6
|
+
'SpaceBar', 'Enter', 'Escape', 'Tab', 'BackSpace', 'CapsLock',
|
|
7
|
+
'LeftShift', 'RightShift', 'LeftControl', 'RightControl', 'LeftAlt', 'RightAlt',
|
|
8
|
+
'LeftCommand', 'RightCommand', 'Insert', 'Delete', 'Home', 'End', 'PageUp', 'PageDown',
|
|
9
|
+
'Up', 'Down', 'Left', 'Right',
|
|
10
|
+
'Semicolon', 'Equals', 'Comma', 'Hyphen', 'Underscore', 'Period', 'Slash', 'Tilde',
|
|
11
|
+
'LeftBracket', 'Backslash', 'RightBracket', 'Apostrophe', 'Quote',
|
|
12
|
+
'LeftMouseButton', 'RightMouseButton', 'MiddleMouseButton', 'ThumbMouseButton', 'ThumbMouseButton2',
|
|
13
|
+
'MouseX', 'MouseY', 'MouseWheelAxis', 'MouseScrollUp', 'MouseScrollDown',
|
|
14
|
+
'Gamepad_FaceButton_Bottom', 'Gamepad_FaceButton_Right', 'Gamepad_FaceButton_Left', 'Gamepad_FaceButton_Top',
|
|
15
|
+
'Gamepad_LeftShoulder', 'Gamepad_RightShoulder', 'Gamepad_LeftTrigger', 'Gamepad_RightTrigger',
|
|
16
|
+
'Gamepad_LeftTriggerAxis', 'Gamepad_RightTriggerAxis',
|
|
17
|
+
'Gamepad_LeftThumbstick', 'Gamepad_RightThumbstick',
|
|
18
|
+
'Gamepad_LeftStick_Up', 'Gamepad_LeftStick_Down', 'Gamepad_LeftStick_Left', 'Gamepad_LeftStick_Right',
|
|
19
|
+
'Gamepad_RightStick_Up', 'Gamepad_RightStick_Down', 'Gamepad_RightStick_Left', 'Gamepad_RightStick_Right',
|
|
20
|
+
'Gamepad_DPad_Up', 'Gamepad_DPad_Down', 'Gamepad_DPad_Left', 'Gamepad_DPad_Right',
|
|
21
|
+
'Gamepad_Special_Left', 'Gamepad_Special_Right'
|
|
22
|
+
]);
|
|
1
23
|
export class InputTools {
|
|
2
24
|
automationBridge = null;
|
|
3
25
|
constructor() { }
|
|
@@ -25,11 +47,25 @@ export class InputTools {
|
|
|
25
47
|
async addMapping(contextPath, actionPath, key) {
|
|
26
48
|
if (!this.automationBridge)
|
|
27
49
|
throw new Error('Automation bridge not set');
|
|
50
|
+
if (!key || typeof key !== 'string' || key.trim().length === 0) {
|
|
51
|
+
return { success: false, error: 'INVALID_ARGUMENT', message: 'Key name is required.' };
|
|
52
|
+
}
|
|
53
|
+
const trimmedKey = key.trim();
|
|
54
|
+
if (trimmedKey === 'MouseXY2D' || trimmedKey === 'Mouse2D' || trimmedKey === 'MouseXY') {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
error: 'INVALID_ARGUMENT',
|
|
58
|
+
message: `Invalid key name '${trimmedKey}'. For mouse axis input, use separate mappings with 'MouseX' and 'MouseY' keys instead of composite 2D axis names.`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (!VALID_KEY_NAMES.has(trimmedKey)) {
|
|
62
|
+
console.warn(`[InputTools] Key '${trimmedKey}' is not in the standard key list. Attempting mapping anyway.`);
|
|
63
|
+
}
|
|
28
64
|
return this.automationBridge.sendAutomationRequest('manage_input', {
|
|
29
65
|
action: 'add_mapping',
|
|
30
66
|
contextPath,
|
|
31
67
|
actionPath,
|
|
32
|
-
key
|
|
68
|
+
key: trimmedKey
|
|
33
69
|
});
|
|
34
70
|
}
|
|
35
71
|
async removeMapping(contextPath, actionPath) {
|
package/dist/tools/lighting.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare class LightingTools {
|
|
|
6
6
|
constructor(bridge: UnrealBridge, automationBridge?: AutomationBridge | undefined);
|
|
7
7
|
setAutomationBridge(automationBridge?: AutomationBridge): void;
|
|
8
8
|
private normalizeName;
|
|
9
|
+
listLightTypes(): Promise<import("../automation/types.js").AutomationBridgeResponseMessage>;
|
|
9
10
|
private spawnLightViaAutomation;
|
|
10
11
|
createDirectionalLight(params: {
|
|
11
12
|
name: string;
|
package/dist/tools/lighting.js
CHANGED
|
@@ -23,6 +23,13 @@ export class LightingTools {
|
|
|
23
23
|
}
|
|
24
24
|
return `Light_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|
25
25
|
}
|
|
26
|
+
async listLightTypes() {
|
|
27
|
+
if (!this.automationBridge) {
|
|
28
|
+
throw new Error('Automation Bridge required to list light types');
|
|
29
|
+
}
|
|
30
|
+
const response = await this.automationBridge.sendAutomationRequest('list_light_types', {});
|
|
31
|
+
return response;
|
|
32
|
+
}
|
|
26
33
|
async spawnLightViaAutomation(lightClass, params) {
|
|
27
34
|
if (!this.automationBridge) {
|
|
28
35
|
throw new Error('Automation Bridge not available. Cannot spawn lights without plugin support.');
|
package/dist/tools/physics.d.ts
CHANGED
package/dist/tools/sequence.d.ts
CHANGED
|
@@ -129,6 +129,7 @@ export declare class SequenceTools extends BaseTool implements ISequenceTools {
|
|
|
129
129
|
listTracks(params: {
|
|
130
130
|
path: string;
|
|
131
131
|
}): Promise<StandardActionResponse>;
|
|
132
|
+
listTrackTypes(): Promise<StandardActionResponse>;
|
|
132
133
|
setWorkRange(params: {
|
|
133
134
|
path?: string;
|
|
134
135
|
start: number;
|
package/dist/tools/sequence.js
CHANGED
|
@@ -247,6 +247,13 @@ export class SequenceTools extends BaseTool {
|
|
|
247
247
|
}
|
|
248
248
|
return resp;
|
|
249
249
|
}
|
|
250
|
+
async listTrackTypes() {
|
|
251
|
+
const resp = await this.sendAction('list_track_types', {});
|
|
252
|
+
if (!resp.success && this.isUnknownActionResponse(resp)) {
|
|
253
|
+
return { success: false, error: 'UNKNOWN_PLUGIN_ACTION', message: 'Automation plugin does not implement list_track_types' };
|
|
254
|
+
}
|
|
255
|
+
return resp;
|
|
256
|
+
}
|
|
250
257
|
async setWorkRange(params) {
|
|
251
258
|
const resp = await this.sendAction('sequence_set_work_range', { path: params.path, start: params.start, end: params.end });
|
|
252
259
|
if (!resp.success && this.isUnknownActionResponse(resp)) {
|