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
|
@@ -8,8 +8,8 @@ export interface StandardActionResponse<T = any> {
|
|
|
8
8
|
success: boolean;
|
|
9
9
|
data?: T;
|
|
10
10
|
warnings?: string[];
|
|
11
|
-
error?: {
|
|
12
|
-
code
|
|
11
|
+
error?: string | {
|
|
12
|
+
code?: string;
|
|
13
13
|
message: string;
|
|
14
14
|
[key: string]: unknown;
|
|
15
15
|
} | null;
|
|
@@ -53,48 +53,48 @@ export interface SourceControlState {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export interface IAssetTools {
|
|
56
|
-
importAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean; save?: boolean }): Promise<
|
|
57
|
-
createFolder(path: string): Promise<
|
|
58
|
-
duplicateAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean }): Promise<
|
|
59
|
-
renameAsset(params: { sourcePath: string; destinationPath: string }): Promise<
|
|
60
|
-
moveAsset(params: { sourcePath: string; destinationPath: string }): Promise<
|
|
61
|
-
deleteAssets(params: { paths: string[]; fixupRedirectors?: boolean; timeoutMs?: number }): Promise<
|
|
62
|
-
searchAssets(params: { classNames?: string[]; packagePaths?: string[]; recursivePaths?: boolean; recursiveClasses?: boolean; limit?: number }): Promise<
|
|
63
|
-
saveAsset(assetPath: string): Promise<
|
|
64
|
-
findByTag(params: { tag: string; value?: string }): Promise<
|
|
65
|
-
getDependencies(params: { assetPath: string; recursive?: boolean }): Promise<
|
|
66
|
-
getMetadata(params: { assetPath: string }): Promise<
|
|
67
|
-
getSourceControlState(params: { assetPath: string }): Promise<SourceControlState |
|
|
68
|
-
analyzeGraph(params: { assetPath: string; maxDepth?: number }): Promise<
|
|
69
|
-
createThumbnail(params: { assetPath: string; width?: number; height?: number }): Promise<
|
|
70
|
-
setTags(params: { assetPath: string; tags: string[] }): Promise<
|
|
71
|
-
generateReport(params: { directory: string; reportType?: string; outputPath?: string }): Promise<
|
|
72
|
-
validate(params: { assetPath: string }): Promise<
|
|
73
|
-
generateLODs(params: { assetPath: string; lodCount: number; reductionSettings?: Record<string, unknown> }): Promise<
|
|
56
|
+
importAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean; save?: boolean }): Promise<StandardActionResponse>;
|
|
57
|
+
createFolder(path: string): Promise<StandardActionResponse>;
|
|
58
|
+
duplicateAsset(params: { sourcePath: string; destinationPath: string; overwrite?: boolean }): Promise<StandardActionResponse>;
|
|
59
|
+
renameAsset(params: { sourcePath: string; destinationPath: string }): Promise<StandardActionResponse>;
|
|
60
|
+
moveAsset(params: { sourcePath: string; destinationPath: string }): Promise<StandardActionResponse>;
|
|
61
|
+
deleteAssets(params: { paths: string[]; fixupRedirectors?: boolean; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
62
|
+
searchAssets(params: { classNames?: string[]; packagePaths?: string[]; recursivePaths?: boolean; recursiveClasses?: boolean; limit?: number }): Promise<StandardActionResponse>;
|
|
63
|
+
saveAsset(assetPath: string): Promise<StandardActionResponse>;
|
|
64
|
+
findByTag(params: { tag: string; value?: string }): Promise<StandardActionResponse>;
|
|
65
|
+
getDependencies(params: { assetPath: string; recursive?: boolean }): Promise<StandardActionResponse>;
|
|
66
|
+
getMetadata(params: { assetPath: string }): Promise<StandardActionResponse>;
|
|
67
|
+
getSourceControlState(params: { assetPath: string }): Promise<SourceControlState | StandardActionResponse>;
|
|
68
|
+
analyzeGraph(params: { assetPath: string; maxDepth?: number }): Promise<StandardActionResponse>;
|
|
69
|
+
createThumbnail(params: { assetPath: string; width?: number; height?: number }): Promise<StandardActionResponse>;
|
|
70
|
+
setTags(params: { assetPath: string; tags: string[] }): Promise<StandardActionResponse>;
|
|
71
|
+
generateReport(params: { directory: string; reportType?: string; outputPath?: string }): Promise<StandardActionResponse>;
|
|
72
|
+
validate(params: { assetPath: string }): Promise<StandardActionResponse>;
|
|
73
|
+
generateLODs(params: { assetPath: string; lodCount: number; reductionSettings?: Record<string, unknown> }): Promise<StandardActionResponse>;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export interface ISequenceTools {
|
|
77
|
-
create(params: { name: string; path?: string; timeoutMs?: number }): Promise<
|
|
78
|
-
open(params: { path: string }): Promise<
|
|
79
|
-
addCamera(params: { spawnable?: boolean; path?: string }): Promise<
|
|
80
|
-
addActor(params: { actorName: string; createBinding?: boolean; path?: string }): Promise<
|
|
81
|
-
addActors(params: { actorNames: string[]; path?: string }): Promise<
|
|
82
|
-
removeActors(params: { actorNames: string[]; path?: string }): Promise<
|
|
83
|
-
getBindings(params: { path?: string }): Promise<
|
|
84
|
-
addSpawnableFromClass(params: { className: string; path?: string }): Promise<
|
|
85
|
-
play(params: { path?: string; startTime?: number; loopMode?: 'once' | 'loop' | 'pingpong' }): Promise<
|
|
86
|
-
pause(params?: { path?: string }): Promise<
|
|
87
|
-
stop(params?: { path?: string }): Promise<
|
|
88
|
-
setSequenceProperties(params: { path?: string; frameRate?: number; lengthInFrames?: number; playbackStart?: number; playbackEnd?: number }): Promise<
|
|
89
|
-
getSequenceProperties(params: { path?: string }): Promise<
|
|
90
|
-
setPlaybackSpeed(params: { speed: number; path?: string }): Promise<
|
|
91
|
-
list(params: { path?: string }): Promise<
|
|
92
|
-
duplicate(params: { path: string; destinationPath: string }): Promise<
|
|
93
|
-
rename(params: { path: string; newName: string }): Promise<
|
|
94
|
-
deleteSequence(params: { path: string }): Promise<
|
|
95
|
-
getMetadata(params: { path: string }): Promise<
|
|
96
|
-
listTracks(params: { path: string }): Promise<
|
|
97
|
-
setWorkRange(params: { path?: string; start: number; end: number }): Promise<
|
|
77
|
+
create(params: { name: string; path?: string; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
78
|
+
open(params: { path: string }): Promise<StandardActionResponse>;
|
|
79
|
+
addCamera(params: { spawnable?: boolean; path?: string }): Promise<StandardActionResponse>;
|
|
80
|
+
addActor(params: { actorName: string; createBinding?: boolean; path?: string }): Promise<StandardActionResponse>;
|
|
81
|
+
addActors(params: { actorNames: string[]; path?: string }): Promise<StandardActionResponse>;
|
|
82
|
+
removeActors(params: { actorNames: string[]; path?: string }): Promise<StandardActionResponse>;
|
|
83
|
+
getBindings(params: { path?: string }): Promise<StandardActionResponse>;
|
|
84
|
+
addSpawnableFromClass(params: { className: string; path?: string }): Promise<StandardActionResponse>;
|
|
85
|
+
play(params: { path?: string; startTime?: number; loopMode?: 'once' | 'loop' | 'pingpong' }): Promise<StandardActionResponse>;
|
|
86
|
+
pause(params?: { path?: string }): Promise<StandardActionResponse>;
|
|
87
|
+
stop(params?: { path?: string }): Promise<StandardActionResponse>;
|
|
88
|
+
setSequenceProperties(params: { path?: string; frameRate?: number; lengthInFrames?: number; playbackStart?: number; playbackEnd?: number }): Promise<StandardActionResponse>;
|
|
89
|
+
getSequenceProperties(params: { path?: string }): Promise<StandardActionResponse>;
|
|
90
|
+
setPlaybackSpeed(params: { speed: number; path?: string }): Promise<StandardActionResponse>;
|
|
91
|
+
list(params: { path?: string }): Promise<StandardActionResponse>;
|
|
92
|
+
duplicate(params: { path: string; destinationPath: string }): Promise<StandardActionResponse>;
|
|
93
|
+
rename(params: { path: string; newName: string }): Promise<StandardActionResponse>;
|
|
94
|
+
deleteSequence(params: { path: string }): Promise<StandardActionResponse>;
|
|
95
|
+
getMetadata(params: { path: string }): Promise<StandardActionResponse>;
|
|
96
|
+
listTracks(params: { path: string }): Promise<StandardActionResponse>;
|
|
97
|
+
setWorkRange(params: { path?: string; start: number; end: number }): Promise<StandardActionResponse>;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
export interface IAssetResources {
|
|
@@ -102,118 +102,118 @@ export interface IAssetResources {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
export interface IBlueprintTools {
|
|
105
|
-
createBlueprint(params: { name: string; blueprintType?: string; savePath?: string; parentClass?: string; properties?: Record<string, unknown>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
106
|
-
modifyConstructionScript(params: { blueprintPath: string; operations: any[]; compile?: boolean; save?: boolean; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
107
|
-
addComponent(params: { blueprintName: string; componentType: string; componentName: string; attachTo?: string; transform?: Record<string, unknown>; properties?: Record<string, unknown>; compile?: boolean; save?: boolean; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
108
|
-
waitForBlueprint(blueprintRef: string | string[], timeoutMs?: number): Promise<
|
|
109
|
-
getBlueprint(params: { blueprintName: string; timeoutMs?: number }): Promise<
|
|
110
|
-
getBlueprintInfo(params: { blueprintPath: string; timeoutMs?: number }): Promise<
|
|
111
|
-
probeSubobjectDataHandle(opts?: { componentClass?: string }): Promise<
|
|
112
|
-
setBlueprintDefault(params: { blueprintName: string; propertyName: string; value: unknown }): Promise<
|
|
113
|
-
addVariable(params: { blueprintName: string; variableName: string; variableType: string; defaultValue?: any; category?: string; isReplicated?: boolean; isPublic?: boolean; variablePinType?: Record<string, unknown>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
114
|
-
removeVariable(params: { blueprintName: string; variableName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
115
|
-
renameVariable(params: { blueprintName: string; oldName: string; newName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
116
|
-
addEvent(params: { blueprintName: string; eventType: string; customEventName?: string; parameters?: Array<{ name: string; type: string }>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
117
|
-
removeEvent(params: { blueprintName: string; eventName: string; customEventName?: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
118
|
-
addFunction(params: { blueprintName: string; functionName: string; inputs?: Array<{ name: string; type: string }>; outputs?: Array<{ name: string; type: string }>; isPublic?: boolean; category?: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
119
|
-
setVariableMetadata(params: { blueprintName: string; variableName: string; metadata: Record<string, unknown>; timeoutMs?: number }): Promise<
|
|
120
|
-
renameVariable(params: { blueprintName: string; oldName: string; newName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
121
|
-
addConstructionScript(params: { blueprintName: string; scriptName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<
|
|
122
|
-
compileBlueprint(params: { blueprintName: string; saveAfterCompile?: boolean }): Promise<
|
|
123
|
-
getBlueprintSCS(params: { blueprintPath: string; timeoutMs?: number }): Promise<
|
|
124
|
-
addSCSComponent(params: { blueprintPath: string; componentClass: string; componentName: string; parentComponent?: string; meshPath?: string; materialPath?: string; timeoutMs?: number }): Promise<
|
|
125
|
-
removeSCSComponent(params: { blueprintPath: string; componentName: string; timeoutMs?: number }): Promise<
|
|
126
|
-
reparentSCSComponent(params: { blueprintPath: string; componentName: string; newParent: string; timeoutMs?: number }): Promise<
|
|
127
|
-
setSCSComponentTransform(params: { blueprintPath: string; componentName: string; location?: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number]; timeoutMs?: number }): Promise<
|
|
128
|
-
setSCSComponentProperty(params: { blueprintPath: string; componentName: string; propertyName: string; propertyValue: any; timeoutMs?: number }): Promise<
|
|
129
|
-
addNode(params: { blueprintName: string; nodeType: string; graphName?: string; functionName?: string; variableName?: string; nodeName?: string; eventName?: string; memberClass?: string; posX?: number; posY?: number; timeoutMs?: number }): Promise<
|
|
130
|
-
connectPins(params: { blueprintName: string; sourceNodeGuid: string; targetNodeGuid: string; sourcePinName?: string; targetPinName?: string; timeoutMs?: number }): Promise<
|
|
105
|
+
createBlueprint(params: { name: string; blueprintType?: string; savePath?: string; parentClass?: string; properties?: Record<string, unknown>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
106
|
+
modifyConstructionScript(params: { blueprintPath: string; operations: any[]; compile?: boolean; save?: boolean; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
107
|
+
addComponent(params: { blueprintName: string; componentType: string; componentName: string; attachTo?: string; transform?: Record<string, unknown>; properties?: Record<string, unknown>; compile?: boolean; save?: boolean; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
108
|
+
waitForBlueprint(blueprintRef: string | string[], timeoutMs?: number): Promise<StandardActionResponse>;
|
|
109
|
+
getBlueprint(params: { blueprintName: string; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
110
|
+
getBlueprintInfo(params: { blueprintPath: string; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
111
|
+
probeSubobjectDataHandle(opts?: { componentClass?: string }): Promise<StandardActionResponse>;
|
|
112
|
+
setBlueprintDefault(params: { blueprintName: string; propertyName: string; value: unknown }): Promise<StandardActionResponse>;
|
|
113
|
+
addVariable(params: { blueprintName: string; variableName: string; variableType: string; defaultValue?: any; category?: string; isReplicated?: boolean; isPublic?: boolean; variablePinType?: Record<string, unknown>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
114
|
+
removeVariable(params: { blueprintName: string; variableName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
115
|
+
renameVariable(params: { blueprintName: string; oldName: string; newName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
116
|
+
addEvent(params: { blueprintName: string; eventType: string; customEventName?: string; parameters?: Array<{ name: string; type: string }>; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
117
|
+
removeEvent(params: { blueprintName: string; eventName: string; customEventName?: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
118
|
+
addFunction(params: { blueprintName: string; functionName: string; inputs?: Array<{ name: string; type: string }>; outputs?: Array<{ name: string; type: string }>; isPublic?: boolean; category?: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
119
|
+
setVariableMetadata(params: { blueprintName: string; variableName: string; metadata: Record<string, unknown>; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
120
|
+
renameVariable(params: { blueprintName: string; oldName: string; newName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
121
|
+
addConstructionScript(params: { blueprintName: string; scriptName: string; timeoutMs?: number; waitForCompletion?: boolean; waitForCompletionTimeoutMs?: number }): Promise<StandardActionResponse>;
|
|
122
|
+
compileBlueprint(params: { blueprintName: string; saveAfterCompile?: boolean }): Promise<StandardActionResponse>;
|
|
123
|
+
getBlueprintSCS(params: { blueprintPath: string; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
124
|
+
addSCSComponent(params: { blueprintPath: string; componentClass: string; componentName: string; parentComponent?: string; meshPath?: string; materialPath?: string; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
125
|
+
removeSCSComponent(params: { blueprintPath: string; componentName: string; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
126
|
+
reparentSCSComponent(params: { blueprintPath: string; componentName: string; newParent: string; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
127
|
+
setSCSComponentTransform(params: { blueprintPath: string; componentName: string; location?: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number]; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
128
|
+
setSCSComponentProperty(params: { blueprintPath: string; componentName: string; propertyName: string; propertyValue: any; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
129
|
+
addNode(params: { blueprintName: string; nodeType: string; graphName?: string; functionName?: string; variableName?: string; nodeName?: string; eventName?: string; memberClass?: string; posX?: number; posY?: number; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
130
|
+
connectPins(params: { blueprintName: string; sourceNodeGuid: string; targetNodeGuid: string; sourcePinName?: string; targetPinName?: string; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export interface ILevelTools {
|
|
134
|
-
listLevels(): Promise<
|
|
135
|
-
getLevelSummary(levelPath?: string): Promise<
|
|
134
|
+
listLevels(): Promise<StandardActionResponse>;
|
|
135
|
+
getLevelSummary(levelPath?: string): Promise<StandardActionResponse>;
|
|
136
136
|
registerLight(levelPath: string | undefined, info: { name: string; type: string; details?: Record<string, unknown> }): void;
|
|
137
|
-
exportLevel(params: { levelPath?: string; exportPath: string; note?: string; timeoutMs?: number }): Promise<
|
|
138
|
-
importLevel(params: { packagePath: string; destinationPath?: string; streaming?: boolean; timeoutMs?: number }): Promise<
|
|
139
|
-
saveLevelAs(params: { sourcePath?: string; targetPath: string }): Promise<
|
|
140
|
-
deleteLevels(params: { levelPaths: string[] }): Promise<
|
|
141
|
-
loadLevel(params: { levelPath: string; streaming?: boolean; position?: [number, number, number] }): Promise<
|
|
142
|
-
saveLevel(params: { levelName?: string; savePath?: string }): Promise<
|
|
143
|
-
createLevel(params: { levelName: string; template?: 'Empty' | 'Default' | 'VR' | 'TimeOfDay'; savePath?: string }): Promise<
|
|
144
|
-
addSubLevel(params: { parentLevel?: string; subLevelPath: string; streamingMethod?: 'Blueprint' | 'AlwaysLoaded' }): Promise<
|
|
145
|
-
streamLevel(params: { levelPath?: string; levelName?: string; shouldBeLoaded: boolean; shouldBeVisible?: boolean; position?: [number, number, number] }): Promise<
|
|
146
|
-
setupWorldComposition(params: { enableComposition: boolean; tileSize?: number; distanceStreaming?: boolean; streamingDistance?: number }): Promise<
|
|
147
|
-
editLevelBlueprint(params: { eventType: 'BeginPlay' | 'EndPlay' | 'Tick' | 'Custom'; customEventName?: string; nodes?: Array<{ nodeType: string; position: [number, number]; connections?: string[] }> }): Promise<
|
|
148
|
-
createSubLevel(params: { name: string; type: 'Persistent' | 'Streaming' | 'Lighting' | 'Gameplay'; parent?: string }): Promise<
|
|
149
|
-
setWorldSettings(params: { gravity?: number; worldScale?: number; gameMode?: string; defaultPawn?: string; killZ?: number }): Promise<
|
|
150
|
-
setLevelBounds(params: { min: [number, number, number]; max: [number, number, number] }): Promise<
|
|
151
|
-
buildNavMesh(params: { rebuildAll?: boolean; selectedOnly?: boolean }): Promise<
|
|
152
|
-
setLevelVisibility(params: { levelName: string; visible: boolean }): Promise<
|
|
153
|
-
setWorldOrigin(params: { location: [number, number, number] }): Promise<
|
|
154
|
-
createStreamingVolume(params: { levelName: string; position: [number, number, number]; size: [number, number, number]; streamingDistance?: number }): Promise<
|
|
155
|
-
setLevelLOD(params: { levelName: string; lodLevel: number; distance: number }): Promise<
|
|
137
|
+
exportLevel(params: { levelPath?: string; exportPath: string; note?: string; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
138
|
+
importLevel(params: { packagePath: string; destinationPath?: string; streaming?: boolean; timeoutMs?: number }): Promise<StandardActionResponse>;
|
|
139
|
+
saveLevelAs(params: { sourcePath?: string; targetPath: string }): Promise<StandardActionResponse>;
|
|
140
|
+
deleteLevels(params: { levelPaths: string[] }): Promise<StandardActionResponse>;
|
|
141
|
+
loadLevel(params: { levelPath: string; streaming?: boolean; position?: [number, number, number] }): Promise<StandardActionResponse>;
|
|
142
|
+
saveLevel(params: { levelName?: string; savePath?: string }): Promise<StandardActionResponse>;
|
|
143
|
+
createLevel(params: { levelName: string; template?: 'Empty' | 'Default' | 'VR' | 'TimeOfDay'; savePath?: string }): Promise<StandardActionResponse>;
|
|
144
|
+
addSubLevel(params: { parentLevel?: string; subLevelPath: string; streamingMethod?: 'Blueprint' | 'AlwaysLoaded' }): Promise<StandardActionResponse>;
|
|
145
|
+
streamLevel(params: { levelPath?: string; levelName?: string; shouldBeLoaded: boolean; shouldBeVisible?: boolean; position?: [number, number, number] }): Promise<StandardActionResponse>;
|
|
146
|
+
setupWorldComposition(params: { enableComposition: boolean; tileSize?: number; distanceStreaming?: boolean; streamingDistance?: number }): Promise<StandardActionResponse>;
|
|
147
|
+
editLevelBlueprint(params: { eventType: 'BeginPlay' | 'EndPlay' | 'Tick' | 'Custom'; customEventName?: string; nodes?: Array<{ nodeType: string; position: [number, number]; connections?: string[] }> }): Promise<StandardActionResponse>;
|
|
148
|
+
createSubLevel(params: { name: string; type: 'Persistent' | 'Streaming' | 'Lighting' | 'Gameplay'; parent?: string }): Promise<StandardActionResponse>;
|
|
149
|
+
setWorldSettings(params: { gravity?: number; worldScale?: number; gameMode?: string; defaultPawn?: string; killZ?: number }): Promise<StandardActionResponse>;
|
|
150
|
+
setLevelBounds(params: { min: [number, number, number]; max: [number, number, number] }): Promise<StandardActionResponse>;
|
|
151
|
+
buildNavMesh(params: { rebuildAll?: boolean; selectedOnly?: boolean }): Promise<StandardActionResponse>;
|
|
152
|
+
setLevelVisibility(params: { levelName: string; visible: boolean }): Promise<StandardActionResponse>;
|
|
153
|
+
setWorldOrigin(params: { location: [number, number, number] }): Promise<StandardActionResponse>;
|
|
154
|
+
createStreamingVolume(params: { levelName: string; position: [number, number, number]; size: [number, number, number]; streamingDistance?: number }): Promise<StandardActionResponse>;
|
|
155
|
+
setLevelLOD(params: { levelName: string; lodLevel: number; distance: number }): Promise<StandardActionResponse>;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
export interface IEditorTools {
|
|
159
159
|
isInPIE(): Promise<boolean>;
|
|
160
160
|
ensureNotInPIE(): Promise<void>;
|
|
161
|
-
playInEditor(timeoutMs?: number): Promise<
|
|
162
|
-
stopPlayInEditor(): Promise<
|
|
163
|
-
pausePlayInEditor(): Promise<
|
|
164
|
-
pauseInEditor(): Promise<
|
|
165
|
-
buildLighting(): Promise<
|
|
166
|
-
setViewportCamera(location?: { x: number; y: number; z: number } | [number, number, number] | null | undefined, rotation?: { pitch: number; yaw: number; roll: number } | [number, number, number] | null | undefined): Promise<
|
|
167
|
-
setCameraSpeed(speed: number): Promise<
|
|
168
|
-
setFOV(fov: number): Promise<
|
|
169
|
-
takeScreenshot(filename?: string, resolution?: string): Promise<
|
|
170
|
-
resumePlayInEditor(): Promise<
|
|
171
|
-
stepPIEFrame(steps?: number): Promise<
|
|
172
|
-
startRecording(options?: { filename?: string; frameRate?: number; durationSeconds?: number; metadata?: Record<string, unknown> }): Promise<
|
|
173
|
-
stopRecording(): Promise<
|
|
174
|
-
createCameraBookmark(name: string): Promise<
|
|
175
|
-
jumpToCameraBookmark(name: string): Promise<
|
|
176
|
-
setEditorPreferences(category: string | undefined, preferences: Record<string, unknown>): Promise<
|
|
177
|
-
setViewportResolution(width: number, height: number): Promise<
|
|
178
|
-
executeConsoleCommand(command: string): Promise<
|
|
161
|
+
playInEditor(timeoutMs?: number): Promise<StandardActionResponse>;
|
|
162
|
+
stopPlayInEditor(): Promise<StandardActionResponse>;
|
|
163
|
+
pausePlayInEditor(): Promise<StandardActionResponse>;
|
|
164
|
+
pauseInEditor(): Promise<StandardActionResponse>;
|
|
165
|
+
buildLighting(): Promise<StandardActionResponse>;
|
|
166
|
+
setViewportCamera(location?: { x: number; y: number; z: number } | [number, number, number] | null | undefined, rotation?: { pitch: number; yaw: number; roll: number } | [number, number, number] | null | undefined): Promise<StandardActionResponse>;
|
|
167
|
+
setCameraSpeed(speed: number): Promise<StandardActionResponse>;
|
|
168
|
+
setFOV(fov: number): Promise<StandardActionResponse>;
|
|
169
|
+
takeScreenshot(filename?: string, resolution?: string): Promise<StandardActionResponse>;
|
|
170
|
+
resumePlayInEditor(): Promise<StandardActionResponse>;
|
|
171
|
+
stepPIEFrame(steps?: number): Promise<StandardActionResponse>;
|
|
172
|
+
startRecording(options?: { filename?: string; frameRate?: number; durationSeconds?: number; metadata?: Record<string, unknown> }): Promise<StandardActionResponse>;
|
|
173
|
+
stopRecording(): Promise<StandardActionResponse>;
|
|
174
|
+
createCameraBookmark(name: string): Promise<StandardActionResponse>;
|
|
175
|
+
jumpToCameraBookmark(name: string): Promise<StandardActionResponse>;
|
|
176
|
+
setEditorPreferences(category: string | undefined, preferences: Record<string, unknown>): Promise<StandardActionResponse>;
|
|
177
|
+
setViewportResolution(width: number, height: number): Promise<StandardActionResponse>;
|
|
178
|
+
executeConsoleCommand(command: string): Promise<StandardActionResponse>;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
export interface IEnvironmentTools {
|
|
182
|
-
setTimeOfDay(hour: unknown): Promise<
|
|
183
|
-
setSunIntensity(intensity: unknown): Promise<
|
|
184
|
-
setSkylightIntensity(intensity: unknown): Promise<
|
|
185
|
-
exportSnapshot(params: { path?: unknown; filename?: unknown }): Promise<
|
|
186
|
-
importSnapshot(params: { path?: unknown; filename?: unknown }): Promise<
|
|
187
|
-
cleanup(params?: { names?: unknown }): Promise<
|
|
182
|
+
setTimeOfDay(hour: unknown): Promise<StandardActionResponse>;
|
|
183
|
+
setSunIntensity(intensity: unknown): Promise<StandardActionResponse>;
|
|
184
|
+
setSkylightIntensity(intensity: unknown): Promise<StandardActionResponse>;
|
|
185
|
+
exportSnapshot(params: { path?: unknown; filename?: unknown }): Promise<StandardActionResponse>;
|
|
186
|
+
importSnapshot(params: { path?: unknown; filename?: unknown }): Promise<StandardActionResponse>;
|
|
187
|
+
cleanup(params?: { names?: unknown }): Promise<StandardActionResponse>;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
export interface ILandscapeTools {
|
|
191
|
-
createLandscape(params: { name: string; location?: [number, number, number]; sizeX?: number; sizeY?: number; quadsPerSection?: number; sectionsPerComponent?: number; componentCount?: number; materialPath?: string; enableWorldPartition?: boolean; runtimeGrid?: string; isSpatiallyLoaded?: boolean; dataLayers?: string[] }): Promise<
|
|
192
|
-
sculptLandscape(params: { landscapeName: string; tool: string; brushSize?: number; brushFalloff?: number; strength?: number; location?: [number, number, number]; radius?: number }): Promise<
|
|
193
|
-
paintLandscape(params: { landscapeName: string; layerName: string; position: [number, number, number]; brushSize?: number; strength?: number; targetValue?: number; radius?: number; density?: number }): Promise<
|
|
194
|
-
createProceduralTerrain(params: { name: string; location?: [number, number, number]; subdivisions?: number; heightFunction?: string; material?: string; settings?: Record<string, unknown> }): Promise<
|
|
195
|
-
createLandscapeGrassType(params: { name: string; meshPath: string; density?: number; minScale?: number; maxScale?: number; path?: string; staticMesh?: string }): Promise<
|
|
196
|
-
setLandscapeMaterial(params: { landscapeName: string; materialPath: string }): Promise<
|
|
197
|
-
modifyHeightmap(params: { landscapeName: string; heightData: number[]; minX: number; minY: number; maxX: number; maxY: number; updateNormals?: boolean }): Promise<
|
|
191
|
+
createLandscape(params: { name: string; location?: [number, number, number]; sizeX?: number; sizeY?: number; quadsPerSection?: number; sectionsPerComponent?: number; componentCount?: number; materialPath?: string; enableWorldPartition?: boolean; runtimeGrid?: string; isSpatiallyLoaded?: boolean; dataLayers?: string[] }): Promise<StandardActionResponse>;
|
|
192
|
+
sculptLandscape(params: { landscapeName: string; tool: string; brushSize?: number; brushFalloff?: number; strength?: number; location?: [number, number, number]; radius?: number }): Promise<StandardActionResponse>;
|
|
193
|
+
paintLandscape(params: { landscapeName: string; layerName: string; position: [number, number, number]; brushSize?: number; strength?: number; targetValue?: number; radius?: number; density?: number }): Promise<StandardActionResponse>;
|
|
194
|
+
createProceduralTerrain(params: { name: string; location?: [number, number, number]; subdivisions?: number; heightFunction?: string; material?: string; settings?: Record<string, unknown> }): Promise<StandardActionResponse>;
|
|
195
|
+
createLandscapeGrassType(params: { name: string; meshPath: string; density?: number; minScale?: number; maxScale?: number; path?: string; staticMesh?: string }): Promise<StandardActionResponse>;
|
|
196
|
+
setLandscapeMaterial(params: { landscapeName: string; materialPath: string }): Promise<StandardActionResponse>;
|
|
197
|
+
modifyHeightmap(params: { landscapeName: string; heightData: number[]; minX: number; minY: number; maxX: number; maxY: number; updateNormals?: boolean }): Promise<StandardActionResponse>;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
export interface IFoliageTools {
|
|
201
|
-
addFoliageType(params: { name: string; meshPath: string; density?: number; radius?: number; minScale?: number; maxScale?: number; alignToNormal?: boolean; randomYaw?: boolean; groundSlope?: number }): Promise<
|
|
202
|
-
addFoliage(params: { foliageType: string; locations: Array<{ x: number; y: number; z: number }> }): Promise<
|
|
203
|
-
paintFoliage(params: { foliageType: string; position: [number, number, number]; brushSize?: number; paintDensity?: number; eraseMode?: boolean }): Promise<
|
|
204
|
-
createProceduralFoliage(params: { name: string; bounds?: { location: { x: number; y: number; z: number }; size: { x: number; y: number; z: number } }; foliageTypes?: Array<{ meshPath: string; density: number; minScale?: number; maxScale?: number; alignToNormal?: boolean; randomYaw?: boolean }>; volumeName?: string; position?: [number, number, number]; size?: [number, number, number]; seed?: number; tileSize?: number }): Promise<
|
|
205
|
-
addFoliageInstances(params: { foliageType: string; transforms: Array<{ location: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number] }> }): Promise<
|
|
206
|
-
getFoliageInstances(params: { foliageType?: string }): Promise<
|
|
207
|
-
removeFoliage(params: { foliageType?: string; removeAll?: boolean }): Promise<
|
|
208
|
-
createInstancedMesh(params: { name: string; meshPath: string; instances: Array<{ position: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number] }>; enableCulling?: boolean; cullDistance?: number }): Promise<
|
|
209
|
-
setFoliageLOD(params: { foliageType: string; lodDistances?: number[]; screenSize?: number[] }): Promise<
|
|
210
|
-
setFoliageCollision(params: { foliageType: string; collisionEnabled?: boolean; collisionProfile?: string; generateOverlapEvents?: boolean }): Promise<
|
|
211
|
-
createGrassSystem(params: { name: string; grassTypes: Array<{ meshPath: string; density: number; minScale?: number; maxScale?: number }>; windStrength?: number; windSpeed?: number }): Promise<
|
|
212
|
-
removeFoliageInstances(params: { foliageType: string; position: [number, number, number]; radius: number }): Promise<
|
|
213
|
-
selectFoliageInstances(params: { foliageType: string; position?: [number, number, number]; radius?: number; selectAll?: boolean }): Promise<
|
|
214
|
-
updateFoliageInstances(params: { foliageType: string; updateTransforms?: boolean; updateMesh?: boolean; newMeshPath?: string }): Promise<
|
|
215
|
-
createFoliageSpawner(params: { name: string; spawnArea: 'Landscape' | 'StaticMesh' | 'BSP' | 'Foliage' | 'All'; excludeAreas?: Array<[number, number, number, number]> }): Promise<
|
|
216
|
-
optimizeFoliage(params: { mergeInstances?: boolean; generateClusters?: boolean; clusterSize?: number; reduceDrawCalls?: boolean }): Promise<
|
|
201
|
+
addFoliageType(params: { name: string; meshPath: string; density?: number; radius?: number; minScale?: number; maxScale?: number; alignToNormal?: boolean; randomYaw?: boolean; groundSlope?: number }): Promise<StandardActionResponse>;
|
|
202
|
+
addFoliage(params: { foliageType: string; locations: Array<{ x: number; y: number; z: number }> }): Promise<StandardActionResponse>;
|
|
203
|
+
paintFoliage(params: { foliageType: string; position: [number, number, number]; brushSize?: number; paintDensity?: number; eraseMode?: boolean }): Promise<StandardActionResponse>;
|
|
204
|
+
createProceduralFoliage(params: { name: string; bounds?: { location: { x: number; y: number; z: number }; size: { x: number; y: number; z: number } }; foliageTypes?: Array<{ meshPath: string; density: number; minScale?: number; maxScale?: number; alignToNormal?: boolean; randomYaw?: boolean }>; volumeName?: string; position?: [number, number, number]; size?: [number, number, number]; seed?: number; tileSize?: number }): Promise<StandardActionResponse>;
|
|
205
|
+
addFoliageInstances(params: { foliageType: string; transforms: Array<{ location: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number] }> }): Promise<StandardActionResponse>;
|
|
206
|
+
getFoliageInstances(params: { foliageType?: string }): Promise<StandardActionResponse>;
|
|
207
|
+
removeFoliage(params: { foliageType?: string; removeAll?: boolean }): Promise<StandardActionResponse>;
|
|
208
|
+
createInstancedMesh(params: { name: string; meshPath: string; instances: Array<{ position: [number, number, number]; rotation?: [number, number, number]; scale?: [number, number, number] }>; enableCulling?: boolean; cullDistance?: number }): Promise<StandardActionResponse>;
|
|
209
|
+
setFoliageLOD(params: { foliageType: string; lodDistances?: number[]; screenSize?: number[] }): Promise<StandardActionResponse>;
|
|
210
|
+
setFoliageCollision(params: { foliageType: string; collisionEnabled?: boolean; collisionProfile?: string; generateOverlapEvents?: boolean }): Promise<StandardActionResponse>;
|
|
211
|
+
createGrassSystem(params: { name: string; grassTypes: Array<{ meshPath: string; density: number; minScale?: number; maxScale?: number }>; windStrength?: number; windSpeed?: number }): Promise<StandardActionResponse>;
|
|
212
|
+
removeFoliageInstances(params: { foliageType: string; position: [number, number, number]; radius: number }): Promise<StandardActionResponse>;
|
|
213
|
+
selectFoliageInstances(params: { foliageType: string; position?: [number, number, number]; radius?: number; selectAll?: boolean }): Promise<StandardActionResponse>;
|
|
214
|
+
updateFoliageInstances(params: { foliageType: string; updateTransforms?: boolean; updateMesh?: boolean; newMeshPath?: string }): Promise<StandardActionResponse>;
|
|
215
|
+
createFoliageSpawner(params: { name: string; spawnArea: 'Landscape' | 'StaticMesh' | 'BSP' | 'Foliage' | 'All'; excludeAreas?: Array<[number, number, number, number]> }): Promise<StandardActionResponse>;
|
|
216
|
+
optimizeFoliage(params: { mergeInstances?: boolean; generateClusters?: boolean; clusterSize?: number; reduceDrawCalls?: boolean }): Promise<StandardActionResponse>;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
export interface ITools {
|
|
@@ -5,7 +5,8 @@ export class CommandValidator {
|
|
|
5
5
|
'viewmode visualizebuffer worldnormal',
|
|
6
6
|
'r.gpucrash',
|
|
7
7
|
'buildpaths', // Can cause access violation if nav system not initialized
|
|
8
|
-
'rebuildnavigation' // Can also crash without proper nav setup
|
|
8
|
+
'rebuildnavigation', // Can also crash without proper nav setup
|
|
9
|
+
'obj garbage', 'obj list', 'memreport' // Heavy debug commands that can stall
|
|
9
10
|
];
|
|
10
11
|
|
|
11
12
|
private static readonly FORBIDDEN_TOKENS = [
|
|
@@ -13,7 +14,7 @@ export class CommandValidator {
|
|
|
13
14
|
'rmdir', 'mklink', 'copy ', 'move ', 'start "', 'system(',
|
|
14
15
|
'import os', 'import subprocess', 'subprocess.', 'os.system',
|
|
15
16
|
'exec(', 'eval(', '__import__', 'import sys', 'import importlib',
|
|
16
|
-
'with open', 'open('
|
|
17
|
+
'with open', 'open(', 'write(', 'read('
|
|
17
18
|
];
|
|
18
19
|
|
|
19
20
|
private static readonly INVALID_PATTERNS = [
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for normalize utility functions
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import {
|
|
6
|
+
toVec3Object,
|
|
7
|
+
toRotObject,
|
|
8
|
+
toVec3Tuple,
|
|
9
|
+
toRotTuple,
|
|
10
|
+
toFiniteNumber,
|
|
11
|
+
normalizePartialVector,
|
|
12
|
+
normalizeTransformInput
|
|
13
|
+
} from './normalize.js';
|
|
14
|
+
|
|
15
|
+
describe('toVec3Object', () => {
|
|
16
|
+
it('converts object input to Vector3', () => {
|
|
17
|
+
const result = toVec3Object({ x: 1, y: 2, z: 3 });
|
|
18
|
+
expect(result).toEqual({ x: 1, y: 2, z: 3 });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('converts array input to Vector3', () => {
|
|
22
|
+
const result = toVec3Object([1, 2, 3]);
|
|
23
|
+
expect(result).toEqual({ x: 1, y: 2, z: 3 });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('returns null for invalid input', () => {
|
|
27
|
+
expect(toVec3Object('invalid')).toBeNull();
|
|
28
|
+
expect(toVec3Object(null)).toBeNull();
|
|
29
|
+
expect(toVec3Object(undefined)).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('returns null for incomplete object', () => {
|
|
33
|
+
expect(toVec3Object({ x: 1, y: 2 })).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('handles zero values', () => {
|
|
37
|
+
const result = toVec3Object({ x: 0, y: 0, z: 0 });
|
|
38
|
+
expect(result).toEqual({ x: 0, y: 0, z: 0 });
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('toRotObject', () => {
|
|
43
|
+
it('converts object input to Rotator', () => {
|
|
44
|
+
const result = toRotObject({ pitch: 10, yaw: 20, roll: 30 });
|
|
45
|
+
expect(result).toEqual({ pitch: 10, yaw: 20, roll: 30 });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('converts array input to Rotator', () => {
|
|
49
|
+
const result = toRotObject([10, 20, 30]);
|
|
50
|
+
expect(result).toEqual({ pitch: 10, yaw: 20, roll: 30 });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns null for invalid input', () => {
|
|
54
|
+
expect(toRotObject('invalid')).toBeNull();
|
|
55
|
+
expect(toRotObject(null)).toBeNull();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('toVec3Tuple', () => {
|
|
60
|
+
it('converts object to tuple', () => {
|
|
61
|
+
const result = toVec3Tuple({ x: 1, y: 2, z: 3 });
|
|
62
|
+
expect(result).toEqual([1, 2, 3]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('passes through valid array', () => {
|
|
66
|
+
const result = toVec3Tuple([1, 2, 3]);
|
|
67
|
+
expect(result).toEqual([1, 2, 3]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('returns null for invalid input', () => {
|
|
71
|
+
expect(toVec3Tuple([1, 2])).toBeNull();
|
|
72
|
+
expect(toVec3Tuple(null)).toBeNull();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('toRotTuple', () => {
|
|
77
|
+
it('converts object to tuple', () => {
|
|
78
|
+
const result = toRotTuple({ pitch: 10, yaw: 20, roll: 30 });
|
|
79
|
+
expect(result).toEqual([10, 20, 30]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('passes through valid array', () => {
|
|
83
|
+
const result = toRotTuple([10, 20, 30]);
|
|
84
|
+
expect(result).toEqual([10, 20, 30]);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('toFiniteNumber', () => {
|
|
89
|
+
it('accepts valid numbers', () => {
|
|
90
|
+
expect(toFiniteNumber(42)).toBe(42);
|
|
91
|
+
expect(toFiniteNumber(0)).toBe(0);
|
|
92
|
+
expect(toFiniteNumber(-5)).toBe(-5);
|
|
93
|
+
expect(toFiniteNumber(3.14)).toBe(3.14);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('parses string numbers', () => {
|
|
97
|
+
expect(toFiniteNumber('42')).toBe(42);
|
|
98
|
+
expect(toFiniteNumber('3.14')).toBe(3.14);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('returns undefined for invalid input', () => {
|
|
102
|
+
expect(toFiniteNumber('not a number')).toBeUndefined();
|
|
103
|
+
expect(toFiniteNumber(NaN)).toBeUndefined();
|
|
104
|
+
expect(toFiniteNumber(Infinity)).toBeUndefined();
|
|
105
|
+
expect(toFiniteNumber(null)).toBeUndefined();
|
|
106
|
+
expect(toFiniteNumber(undefined)).toBeUndefined();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('normalizePartialVector', () => {
|
|
111
|
+
it('normalizes complete vectors', () => {
|
|
112
|
+
const result = normalizePartialVector({ x: 1, y: 2, z: 3 });
|
|
113
|
+
expect(result).toEqual({ x: 1, y: 2, z: 3 });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('handles partial vectors', () => {
|
|
117
|
+
const result = normalizePartialVector({ x: 1 });
|
|
118
|
+
expect(result).toBeDefined();
|
|
119
|
+
expect(result?.x).toBe(1);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('uses alternate keys for reading input', () => {
|
|
123
|
+
const result = normalizePartialVector(
|
|
124
|
+
{ pitch: 10, yaw: 20, roll: 30 },
|
|
125
|
+
['pitch', 'yaw', 'roll']
|
|
126
|
+
);
|
|
127
|
+
// alternateKeys are used to READ from input, but output is still x/y/z
|
|
128
|
+
expect(result).toEqual({ x: 10, y: 20, z: 30 });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('returns undefined for invalid input', () => {
|
|
132
|
+
expect(normalizePartialVector(null)).toBeUndefined();
|
|
133
|
+
expect(normalizePartialVector('string')).toBeUndefined();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('normalizeTransformInput', () => {
|
|
138
|
+
it('normalizes complete transform', () => {
|
|
139
|
+
const result = normalizeTransformInput({
|
|
140
|
+
location: { x: 0, y: 0, z: 100 },
|
|
141
|
+
rotation: { pitch: 0, yaw: 90, roll: 0 },
|
|
142
|
+
scale: { x: 1, y: 1, z: 1 }
|
|
143
|
+
});
|
|
144
|
+
expect(result).toBeDefined();
|
|
145
|
+
expect(result?.location).toBeDefined();
|
|
146
|
+
expect(result?.rotation).toBeDefined();
|
|
147
|
+
expect(result?.scale).toBeDefined();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('handles partial transforms', () => {
|
|
151
|
+
const result = normalizeTransformInput({
|
|
152
|
+
location: { x: 100, y: 200, z: 300 }
|
|
153
|
+
});
|
|
154
|
+
expect(result).toBeDefined();
|
|
155
|
+
expect(result?.location).toBeDefined();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('returns undefined for invalid input', () => {
|
|
159
|
+
expect(normalizeTransformInput(null)).toBeUndefined();
|
|
160
|
+
expect(normalizeTransformInput('invalid')).toBeUndefined();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function sanitizePath(path: string, allowedRoots: string[] = ['/Game', '/Engine']): string {
|
|
2
|
+
if (!path || typeof path !== 'string') {
|
|
3
|
+
throw new Error('Invalid path: must be a non-empty string');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const trimmed = path.trim();
|
|
7
|
+
if (trimmed.length === 0) {
|
|
8
|
+
throw new Error('Invalid path: cannot be empty');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Normalize separators
|
|
12
|
+
const normalized = trimmed.replace(/\\/g, '/');
|
|
13
|
+
|
|
14
|
+
// Prevent directory traversal
|
|
15
|
+
if (normalized.includes('..')) {
|
|
16
|
+
throw new Error('Invalid path: directory traversal (..) is not allowed');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Ensure path starts with a valid root
|
|
20
|
+
// We check case-insensitive for the root prefix to be user-friendly,
|
|
21
|
+
// but Unreal paths are typically case-insensitive anyway.
|
|
22
|
+
const isAllowed = allowedRoots.some(root =>
|
|
23
|
+
normalized.toLowerCase() === root.toLowerCase() ||
|
|
24
|
+
normalized.toLowerCase().startsWith(`${root.toLowerCase()}/`)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (!isAllowed) {
|
|
28
|
+
throw new Error(`Invalid path: must start with one of [${allowedRoots.join(', ')}]`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Basic character validation (Unreal strictness)
|
|
32
|
+
// Blocks: < > : " | ? * (Windows reserved) and control characters
|
|
33
|
+
// allowing spaces, dots, underscores, dashes, slashes
|
|
34
|
+
// Note: Unreal allows spaces in some contexts but it's often safer to restrict them if strict mode is desired.
|
|
35
|
+
// For now, we block the definitely invalid ones.
|
|
36
|
+
// eslint-disable-next-line no-control-regex
|
|
37
|
+
const invalidChars = /[<>:"|?*\x00-\x1f]/;
|
|
38
|
+
if (invalidChars.test(normalized)) {
|
|
39
|
+
throw new Error('Invalid path: contains illegal characters');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return normalized;
|
|
43
|
+
}
|