unreal-engine-mcp-server 0.4.7 → 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.
Files changed (454) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter-config.yml +51 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +27 -0
  19. package/.github/workflows/labeler.yml +17 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +13 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +338 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/GEMINI.md +115 -0
  31. package/Public/Plugin_setup_guide.mp4 +0 -0
  32. package/README.md +189 -128
  33. package/claude_desktop_config_example.json +7 -6
  34. package/dist/automation/bridge.d.ts +50 -0
  35. package/dist/automation/bridge.js +452 -0
  36. package/dist/automation/connection-manager.d.ts +23 -0
  37. package/dist/automation/connection-manager.js +107 -0
  38. package/dist/automation/handshake.d.ts +11 -0
  39. package/dist/automation/handshake.js +89 -0
  40. package/dist/automation/index.d.ts +3 -0
  41. package/dist/automation/index.js +3 -0
  42. package/dist/automation/message-handler.d.ts +12 -0
  43. package/dist/automation/message-handler.js +149 -0
  44. package/dist/automation/request-tracker.d.ts +25 -0
  45. package/dist/automation/request-tracker.js +98 -0
  46. package/dist/automation/types.d.ts +130 -0
  47. package/dist/automation/types.js +2 -0
  48. package/dist/cli.js +32 -5
  49. package/dist/config.d.ts +26 -0
  50. package/dist/config.js +59 -0
  51. package/dist/constants.d.ts +16 -0
  52. package/dist/constants.js +16 -0
  53. package/dist/graphql/loaders.d.ts +64 -0
  54. package/dist/graphql/loaders.js +117 -0
  55. package/dist/graphql/resolvers.d.ts +268 -0
  56. package/dist/graphql/resolvers.js +746 -0
  57. package/dist/graphql/schema.d.ts +5 -0
  58. package/dist/graphql/schema.js +437 -0
  59. package/dist/graphql/server.d.ts +26 -0
  60. package/dist/graphql/server.js +117 -0
  61. package/dist/graphql/types.d.ts +9 -0
  62. package/dist/graphql/types.js +2 -0
  63. package/dist/handlers/resource-handlers.d.ts +20 -0
  64. package/dist/handlers/resource-handlers.js +180 -0
  65. package/dist/index.d.ts +33 -18
  66. package/dist/index.js +130 -619
  67. package/dist/resources/actors.d.ts +17 -12
  68. package/dist/resources/actors.js +56 -76
  69. package/dist/resources/assets.d.ts +6 -14
  70. package/dist/resources/assets.js +115 -147
  71. package/dist/resources/levels.d.ts +13 -13
  72. package/dist/resources/levels.js +25 -34
  73. package/dist/server/resource-registry.d.ts +20 -0
  74. package/dist/server/resource-registry.js +37 -0
  75. package/dist/server/tool-registry.d.ts +23 -0
  76. package/dist/server/tool-registry.js +322 -0
  77. package/dist/server-setup.d.ts +20 -0
  78. package/dist/server-setup.js +71 -0
  79. package/dist/services/health-monitor.d.ts +34 -0
  80. package/dist/services/health-monitor.js +105 -0
  81. package/dist/services/metrics-server.d.ts +11 -0
  82. package/dist/services/metrics-server.js +105 -0
  83. package/dist/tools/actors.d.ts +163 -9
  84. package/dist/tools/actors.js +356 -311
  85. package/dist/tools/animation.d.ts +135 -4
  86. package/dist/tools/animation.js +510 -411
  87. package/dist/tools/assets.d.ts +75 -29
  88. package/dist/tools/assets.js +265 -284
  89. package/dist/tools/audio.d.ts +102 -42
  90. package/dist/tools/audio.js +272 -685
  91. package/dist/tools/base-tool.d.ts +17 -0
  92. package/dist/tools/base-tool.js +46 -0
  93. package/dist/tools/behavior-tree.d.ts +94 -0
  94. package/dist/tools/behavior-tree.js +39 -0
  95. package/dist/tools/blueprint.d.ts +208 -126
  96. package/dist/tools/blueprint.js +685 -832
  97. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  98. package/dist/tools/consolidated-tool-definitions.js +829 -496
  99. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  100. package/dist/tools/consolidated-tool-handlers.js +198 -1027
  101. package/dist/tools/debug.d.ts +143 -85
  102. package/dist/tools/debug.js +234 -180
  103. package/dist/tools/dynamic-handler-registry.d.ts +13 -0
  104. package/dist/tools/dynamic-handler-registry.js +23 -0
  105. package/dist/tools/editor.d.ts +30 -83
  106. package/dist/tools/editor.js +247 -244
  107. package/dist/tools/engine.d.ts +10 -4
  108. package/dist/tools/engine.js +13 -5
  109. package/dist/tools/environment.d.ts +30 -0
  110. package/dist/tools/environment.js +267 -0
  111. package/dist/tools/foliage.d.ts +65 -99
  112. package/dist/tools/foliage.js +221 -331
  113. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  114. package/dist/tools/handlers/actor-handlers.js +227 -0
  115. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  116. package/dist/tools/handlers/animation-handlers.js +185 -0
  117. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  118. package/dist/tools/handlers/argument-helper.js +80 -0
  119. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  120. package/dist/tools/handlers/asset-handlers.js +496 -0
  121. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  122. package/dist/tools/handlers/audio-handlers.js +166 -0
  123. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  124. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  125. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  126. package/dist/tools/handlers/common-handlers.js +56 -0
  127. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  128. package/dist/tools/handlers/editor-handlers.js +119 -0
  129. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  130. package/dist/tools/handlers/effect-handlers.js +171 -0
  131. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  132. package/dist/tools/handlers/environment-handlers.js +170 -0
  133. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  134. package/dist/tools/handlers/graph-handlers.js +90 -0
  135. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  136. package/dist/tools/handlers/input-handlers.js +21 -0
  137. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  138. package/dist/tools/handlers/inspect-handlers.js +383 -0
  139. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  140. package/dist/tools/handlers/level-handlers.js +237 -0
  141. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  142. package/dist/tools/handlers/lighting-handlers.js +144 -0
  143. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  144. package/dist/tools/handlers/performance-handlers.js +130 -0
  145. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  146. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  147. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  148. package/dist/tools/handlers/sequence-handlers.js +376 -0
  149. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  150. package/dist/tools/handlers/system-handlers.js +506 -0
  151. package/dist/tools/input.d.ts +19 -0
  152. package/dist/tools/input.js +89 -0
  153. package/dist/tools/introspection.d.ts +103 -40
  154. package/dist/tools/introspection.js +425 -568
  155. package/dist/tools/landscape.d.ts +54 -93
  156. package/dist/tools/landscape.js +284 -409
  157. package/dist/tools/level.d.ts +66 -27
  158. package/dist/tools/level.js +647 -675
  159. package/dist/tools/lighting.d.ts +77 -38
  160. package/dist/tools/lighting.js +445 -943
  161. package/dist/tools/logs.d.ts +3 -3
  162. package/dist/tools/logs.js +5 -57
  163. package/dist/tools/materials.d.ts +91 -24
  164. package/dist/tools/materials.js +194 -118
  165. package/dist/tools/niagara.d.ts +149 -39
  166. package/dist/tools/niagara.js +267 -182
  167. package/dist/tools/performance.d.ts +27 -13
  168. package/dist/tools/performance.js +203 -122
  169. package/dist/tools/physics.d.ts +32 -77
  170. package/dist/tools/physics.js +175 -582
  171. package/dist/tools/property-dictionary.d.ts +13 -0
  172. package/dist/tools/property-dictionary.js +82 -0
  173. package/dist/tools/sequence.d.ts +85 -60
  174. package/dist/tools/sequence.js +208 -747
  175. package/dist/tools/tool-definition-utils.d.ts +59 -0
  176. package/dist/tools/tool-definition-utils.js +35 -0
  177. package/dist/tools/ui.d.ts +64 -34
  178. package/dist/tools/ui.js +134 -214
  179. package/dist/types/automation-responses.d.ts +115 -0
  180. package/dist/types/automation-responses.js +2 -0
  181. package/dist/types/env.d.ts +0 -3
  182. package/dist/types/env.js +0 -7
  183. package/dist/types/responses.d.ts +249 -0
  184. package/dist/types/responses.js +2 -0
  185. package/dist/types/tool-interfaces.d.ts +898 -0
  186. package/dist/types/tool-interfaces.js +2 -0
  187. package/dist/types/tool-types.d.ts +183 -19
  188. package/dist/types/tool-types.js +0 -4
  189. package/dist/unreal-bridge.d.ts +24 -131
  190. package/dist/unreal-bridge.js +364 -1506
  191. package/dist/utils/command-validator.d.ts +9 -0
  192. package/dist/utils/command-validator.js +68 -0
  193. package/dist/utils/elicitation.d.ts +1 -1
  194. package/dist/utils/elicitation.js +12 -15
  195. package/dist/utils/error-handler.d.ts +2 -51
  196. package/dist/utils/error-handler.js +11 -87
  197. package/dist/utils/ini-reader.d.ts +3 -0
  198. package/dist/utils/ini-reader.js +69 -0
  199. package/dist/utils/logger.js +9 -6
  200. package/dist/utils/normalize.d.ts +3 -0
  201. package/dist/utils/normalize.js +56 -0
  202. package/dist/utils/path-security.d.ts +2 -0
  203. package/dist/utils/path-security.js +24 -0
  204. package/dist/utils/response-factory.d.ts +7 -0
  205. package/dist/utils/response-factory.js +27 -0
  206. package/dist/utils/response-validator.d.ts +3 -24
  207. package/dist/utils/response-validator.js +130 -81
  208. package/dist/utils/result-helpers.d.ts +4 -5
  209. package/dist/utils/result-helpers.js +15 -16
  210. package/dist/utils/safe-json.js +5 -11
  211. package/dist/utils/unreal-command-queue.d.ts +24 -0
  212. package/dist/utils/unreal-command-queue.js +120 -0
  213. package/dist/utils/validation.d.ts +0 -40
  214. package/dist/utils/validation.js +1 -78
  215. package/dist/wasm/index.d.ts +70 -0
  216. package/dist/wasm/index.js +535 -0
  217. package/docs/GraphQL-API.md +888 -0
  218. package/docs/Migration-Guide-v0.5.0.md +684 -0
  219. package/docs/Roadmap.md +53 -0
  220. package/docs/WebAssembly-Integration.md +628 -0
  221. package/docs/editor-plugin-extension.md +370 -0
  222. package/docs/handler-mapping.md +242 -0
  223. package/docs/native-automation-progress.md +128 -0
  224. package/docs/testing-guide.md +423 -0
  225. package/mcp-config-example.json +6 -6
  226. package/package.json +67 -28
  227. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  228. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  272. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  273. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  274. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  275. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  276. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  277. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  278. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  279. package/scripts/check-unreal-connection.mjs +19 -0
  280. package/scripts/clean-tmp.js +23 -0
  281. package/scripts/patch-wasm.js +26 -0
  282. package/scripts/run-all-tests.mjs +136 -0
  283. package/scripts/smoke-test.ts +94 -0
  284. package/scripts/sync-mcp-plugin.js +143 -0
  285. package/scripts/test-no-plugin-alternates.mjs +113 -0
  286. package/scripts/validate-server.js +46 -0
  287. package/scripts/verify-automation-bridge.js +200 -0
  288. package/server.json +58 -21
  289. package/src/automation/bridge.ts +558 -0
  290. package/src/automation/connection-manager.ts +130 -0
  291. package/src/automation/handshake.ts +99 -0
  292. package/src/automation/index.ts +2 -0
  293. package/src/automation/message-handler.ts +167 -0
  294. package/src/automation/request-tracker.ts +123 -0
  295. package/src/automation/types.ts +107 -0
  296. package/src/cli.ts +33 -6
  297. package/src/config.ts +73 -0
  298. package/src/constants.ts +19 -0
  299. package/src/graphql/loaders.ts +244 -0
  300. package/src/graphql/resolvers.ts +1008 -0
  301. package/src/graphql/schema.ts +452 -0
  302. package/src/graphql/server.ts +156 -0
  303. package/src/graphql/types.ts +10 -0
  304. package/src/handlers/resource-handlers.ts +186 -0
  305. package/src/index.ts +166 -664
  306. package/src/resources/actors.ts +58 -76
  307. package/src/resources/assets.ts +148 -134
  308. package/src/resources/levels.ts +28 -33
  309. package/src/server/resource-registry.ts +47 -0
  310. package/src/server/tool-registry.ts +354 -0
  311. package/src/server-setup.ts +114 -0
  312. package/src/services/health-monitor.ts +132 -0
  313. package/src/services/metrics-server.ts +142 -0
  314. package/src/tools/actors.ts +426 -323
  315. package/src/tools/animation.ts +672 -461
  316. package/src/tools/assets.ts +364 -289
  317. package/src/tools/audio.ts +323 -766
  318. package/src/tools/base-tool.ts +52 -0
  319. package/src/tools/behavior-tree.ts +45 -0
  320. package/src/tools/blueprint.ts +792 -970
  321. package/src/tools/consolidated-tool-definitions.ts +993 -515
  322. package/src/tools/consolidated-tool-handlers.ts +258 -1146
  323. package/src/tools/debug.ts +292 -187
  324. package/src/tools/dynamic-handler-registry.ts +33 -0
  325. package/src/tools/editor.ts +329 -253
  326. package/src/tools/engine.ts +14 -3
  327. package/src/tools/environment.ts +281 -0
  328. package/src/tools/foliage.ts +330 -392
  329. package/src/tools/handlers/actor-handlers.ts +265 -0
  330. package/src/tools/handlers/animation-handlers.ts +237 -0
  331. package/src/tools/handlers/argument-helper.ts +142 -0
  332. package/src/tools/handlers/asset-handlers.ts +532 -0
  333. package/src/tools/handlers/audio-handlers.ts +194 -0
  334. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  335. package/src/tools/handlers/common-handlers.ts +87 -0
  336. package/src/tools/handlers/editor-handlers.ts +123 -0
  337. package/src/tools/handlers/effect-handlers.ts +220 -0
  338. package/src/tools/handlers/environment-handlers.ts +183 -0
  339. package/src/tools/handlers/graph-handlers.ts +116 -0
  340. package/src/tools/handlers/input-handlers.ts +28 -0
  341. package/src/tools/handlers/inspect-handlers.ts +450 -0
  342. package/src/tools/handlers/level-handlers.ts +252 -0
  343. package/src/tools/handlers/lighting-handlers.ts +147 -0
  344. package/src/tools/handlers/performance-handlers.ts +132 -0
  345. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  346. package/src/tools/handlers/sequence-handlers.ts +415 -0
  347. package/src/tools/handlers/system-handlers.ts +564 -0
  348. package/src/tools/input.ts +101 -0
  349. package/src/tools/introspection.ts +493 -584
  350. package/src/tools/landscape.ts +418 -507
  351. package/src/tools/level.ts +786 -708
  352. package/src/tools/lighting.ts +588 -984
  353. package/src/tools/logs.ts +9 -57
  354. package/src/tools/materials.ts +237 -121
  355. package/src/tools/niagara.ts +335 -168
  356. package/src/tools/performance.ts +320 -169
  357. package/src/tools/physics.ts +274 -613
  358. package/src/tools/property-dictionary.ts +98 -0
  359. package/src/tools/sequence.ts +276 -820
  360. package/src/tools/tool-definition-utils.ts +35 -0
  361. package/src/tools/ui.ts +205 -283
  362. package/src/types/automation-responses.ts +119 -0
  363. package/src/types/env.ts +0 -10
  364. package/src/types/responses.ts +355 -0
  365. package/src/types/tool-interfaces.ts +250 -0
  366. package/src/types/tool-types.ts +243 -21
  367. package/src/unreal-bridge.ts +460 -1550
  368. package/src/utils/command-validator.ts +76 -0
  369. package/src/utils/elicitation.ts +10 -7
  370. package/src/utils/error-handler.ts +14 -90
  371. package/src/utils/ini-reader.ts +86 -0
  372. package/src/utils/logger.ts +8 -3
  373. package/src/utils/normalize.test.ts +162 -0
  374. package/src/utils/normalize.ts +60 -0
  375. package/src/utils/path-security.ts +43 -0
  376. package/src/utils/response-factory.ts +44 -0
  377. package/src/utils/response-validator.ts +176 -56
  378. package/src/utils/result-helpers.ts +21 -19
  379. package/src/utils/safe-json.test.ts +90 -0
  380. package/src/utils/safe-json.ts +14 -11
  381. package/src/utils/unreal-command-queue.ts +152 -0
  382. package/src/utils/validation.test.ts +184 -0
  383. package/src/utils/validation.ts +4 -1
  384. package/src/wasm/index.ts +838 -0
  385. package/test-server.mjs +100 -0
  386. package/tests/run-unreal-tool-tests.mjs +242 -14
  387. package/tests/test-animation.mjs +369 -0
  388. package/tests/test-asset-advanced.mjs +82 -0
  389. package/tests/test-asset-errors.mjs +35 -0
  390. package/tests/test-asset-graph.mjs +311 -0
  391. package/tests/test-audio.mjs +417 -0
  392. package/tests/test-automation-timeouts.mjs +98 -0
  393. package/tests/test-behavior-tree.mjs +444 -0
  394. package/tests/test-blueprint-graph.mjs +410 -0
  395. package/tests/test-blueprint.mjs +577 -0
  396. package/tests/test-client-mode.mjs +86 -0
  397. package/tests/test-console-command.mjs +56 -0
  398. package/tests/test-control-actor.mjs +425 -0
  399. package/tests/test-control-editor.mjs +112 -0
  400. package/tests/test-graphql.mjs +372 -0
  401. package/tests/test-input.mjs +349 -0
  402. package/tests/test-inspect.mjs +302 -0
  403. package/tests/test-landscape.mjs +316 -0
  404. package/tests/test-lighting.mjs +428 -0
  405. package/tests/test-manage-asset.mjs +438 -0
  406. package/tests/test-manage-level.mjs +89 -0
  407. package/tests/test-materials.mjs +356 -0
  408. package/tests/test-niagara.mjs +185 -0
  409. package/tests/test-no-inline-python.mjs +122 -0
  410. package/tests/test-performance.mjs +539 -0
  411. package/tests/test-plugin-handshake.mjs +82 -0
  412. package/tests/test-runner.mjs +933 -0
  413. package/tests/test-sequence.mjs +104 -0
  414. package/tests/test-system.mjs +96 -0
  415. package/tests/test-wasm.mjs +283 -0
  416. package/tests/test-world-partition.mjs +215 -0
  417. package/tsconfig.json +3 -3
  418. package/vitest.config.ts +35 -0
  419. package/wasm/Cargo.lock +363 -0
  420. package/wasm/Cargo.toml +42 -0
  421. package/wasm/LICENSE +21 -0
  422. package/wasm/README.md +253 -0
  423. package/wasm/src/dependency_resolver.rs +377 -0
  424. package/wasm/src/lib.rs +153 -0
  425. package/wasm/src/property_parser.rs +271 -0
  426. package/wasm/src/transform_math.rs +396 -0
  427. package/wasm/tests/integration.rs +109 -0
  428. package/.github/workflows/smithery-build.yml +0 -29
  429. package/dist/prompts/index.d.ts +0 -21
  430. package/dist/prompts/index.js +0 -217
  431. package/dist/tools/build_environment_advanced.d.ts +0 -65
  432. package/dist/tools/build_environment_advanced.js +0 -633
  433. package/dist/tools/rc.d.ts +0 -110
  434. package/dist/tools/rc.js +0 -437
  435. package/dist/tools/visual.d.ts +0 -40
  436. package/dist/tools/visual.js +0 -282
  437. package/dist/utils/http.d.ts +0 -6
  438. package/dist/utils/http.js +0 -151
  439. package/dist/utils/python-output.d.ts +0 -18
  440. package/dist/utils/python-output.js +0 -290
  441. package/dist/utils/python.d.ts +0 -2
  442. package/dist/utils/python.js +0 -4
  443. package/dist/utils/stdio-redirect.d.ts +0 -2
  444. package/dist/utils/stdio-redirect.js +0 -20
  445. package/docs/unreal-tool-test-cases.md +0 -574
  446. package/smithery.yaml +0 -29
  447. package/src/prompts/index.ts +0 -249
  448. package/src/tools/build_environment_advanced.ts +0 -732
  449. package/src/tools/rc.ts +0 -515
  450. package/src/tools/visual.ts +0 -281
  451. package/src/utils/http.ts +0 -187
  452. package/src/utils/python-output.ts +0 -351
  453. package/src/utils/python.ts +0 -3
  454. package/src/utils/stdio-redirect.ts +0 -18
@@ -1,9 +1,18 @@
1
+ import { cleanObject } from '../utils/safe-json.js';
1
2
  import { validateAssetParams } from '../utils/validation.js';
2
- import { interpretStandardResult, coerceBoolean, coerceString, coerceStringArray } from '../utils/result-helpers.js';
3
3
  export class AnimationTools {
4
4
  bridge;
5
- constructor(bridge) {
5
+ managedArtifacts = new Map();
6
+ automationBridge;
7
+ constructor(bridge, automationBridge) {
6
8
  this.bridge = bridge;
9
+ this.automationBridge = automationBridge;
10
+ }
11
+ setAutomationBridge(automationBridge) {
12
+ this.automationBridge = automationBridge;
13
+ }
14
+ trackArtifact(key, info) {
15
+ this.managedArtifacts.set(key, { ...info, createdAt: Date.now() });
7
16
  }
8
17
  async createAnimationBlueprint(params) {
9
18
  try {
@@ -16,13 +25,63 @@ export class AnimationTools {
16
25
  const sanitized = validation.sanitized;
17
26
  const assetName = sanitized.name;
18
27
  const assetPath = sanitized.savePath ?? targetPath;
19
- const script = this.buildCreateAnimationBlueprintScript({
20
- name: assetName,
21
- path: assetPath,
22
- skeletonPath: params.skeletonPath
23
- });
24
- const response = await this.bridge.executePython(script);
25
- return this.parseAnimationBlueprintResponse(response, assetName, assetPath);
28
+ const fullPath = `${assetPath}/${assetName}`;
29
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
30
+ try {
31
+ const resp = await this.automationBridge.sendAutomationRequest('create_animation_blueprint', {
32
+ name: assetName,
33
+ skeletonPath: params.skeletonPath,
34
+ savePath: assetPath
35
+ }, { timeoutMs: 60000 });
36
+ const result = resp?.result ?? resp;
37
+ const resultObj = result && typeof result === 'object' ? result : undefined;
38
+ const warnings = Array.isArray(resultObj?.warnings) ? resultObj.warnings : undefined;
39
+ const details = Array.isArray(resultObj?.details) ? resultObj.details : undefined;
40
+ const isSuccess = resp && resp.success !== false && !!resultObj;
41
+ if (isSuccess && resultObj) {
42
+ const blueprintPath = typeof resultObj.blueprintPath === 'string' ? resultObj.blueprintPath : fullPath;
43
+ this.trackArtifact(assetName, { path: blueprintPath, type: 'AnimationBlueprint' });
44
+ return {
45
+ success: true,
46
+ message: resp.message || `Animation Blueprint created at ${blueprintPath}`,
47
+ path: blueprintPath,
48
+ skeleton: params.skeletonPath,
49
+ warnings,
50
+ details
51
+ };
52
+ }
53
+ const message = typeof resp?.message === 'string'
54
+ ? resp.message
55
+ : (typeof resp?.error === 'string' ? resp.error : 'Animation Blueprint creation failed');
56
+ const error = typeof resp?.error === 'string' ? resp.error : message;
57
+ return {
58
+ success: false,
59
+ message,
60
+ error,
61
+ path: fullPath,
62
+ skeleton: params.skeletonPath,
63
+ warnings,
64
+ details
65
+ };
66
+ }
67
+ catch (err) {
68
+ const error = String(err);
69
+ return {
70
+ success: false,
71
+ message: `Failed to create Animation Blueprint: ${error}`,
72
+ error,
73
+ path: fullPath,
74
+ skeleton: params.skeletonPath
75
+ };
76
+ }
77
+ }
78
+ return {
79
+ success: false,
80
+ message: 'Automation bridge not connected for Animation Blueprint creation',
81
+ error: 'AUTOMATION_BRIDGE_UNAVAILABLE',
82
+ path: fullPath,
83
+ skeleton: params.skeletonPath
84
+ };
26
85
  }
27
86
  catch (err) {
28
87
  const error = `Failed to create Animation Blueprint: ${err}`;
@@ -65,6 +124,72 @@ export class AnimationTools {
65
124
  return { success: false, error: `Failed to add state machine: ${err}` };
66
125
  }
67
126
  }
127
+ async createStateMachine(params) {
128
+ try {
129
+ const rawName = typeof params.machineName === 'string' ? params.machineName.trim() : '';
130
+ const machineName = rawName || 'StateMachine';
131
+ const normalizedStates = Array.isArray(params.states)
132
+ ? params.states
133
+ .map((s) => {
134
+ if (typeof s === 'string') {
135
+ const name = s.trim();
136
+ return name ? { name } : undefined;
137
+ }
138
+ if (s && typeof s === 'object' && typeof s.name === 'string') {
139
+ const name = s.name.trim();
140
+ if (!name)
141
+ return undefined;
142
+ return s;
143
+ }
144
+ return undefined;
145
+ })
146
+ .filter((s) => !!s)
147
+ : [];
148
+ const normalizedTransitionsRaw = Array.isArray(params.transitions)
149
+ ? params.transitions
150
+ .map((t) => {
151
+ if (!t || typeof t !== 'object')
152
+ return undefined;
153
+ const src = (t.sourceState || '').trim();
154
+ const dst = (t.targetState || '').trim();
155
+ if (!src || !dst)
156
+ return undefined;
157
+ return { sourceState: src, targetState: dst, condition: t.condition };
158
+ })
159
+ .filter((t) => !!t)
160
+ : [];
161
+ const normalizedTransitions = normalizedTransitionsRaw;
162
+ const blueprintPath = typeof params.blueprintPath === 'string' && params.blueprintPath.trim().length > 0
163
+ ? params.blueprintPath.trim()
164
+ : undefined;
165
+ const key = `StateMachine:${machineName}`;
166
+ this.trackArtifact(key, {
167
+ path: blueprintPath,
168
+ type: 'AnimationStateMachine',
169
+ metadata: {
170
+ machineName,
171
+ states: normalizedStates,
172
+ transitions: normalizedTransitions
173
+ }
174
+ });
175
+ return {
176
+ success: true,
177
+ message: `State machine '${machineName}' specification recorded`,
178
+ machineName,
179
+ blueprintPath,
180
+ states: normalizedStates.length ? normalizedStates : undefined,
181
+ transitions: normalizedTransitions.length ? normalizedTransitions : undefined
182
+ };
183
+ }
184
+ catch (err) {
185
+ const error = String(err);
186
+ return {
187
+ success: false,
188
+ message: `Failed to record state machine specification: ${error}`,
189
+ error
190
+ };
191
+ }
192
+ }
68
193
  async createBlendSpace(params) {
69
194
  try {
70
195
  const targetPath = params.savePath ?? '/Game/Animations';
@@ -76,69 +201,401 @@ export class AnimationTools {
76
201
  const assetName = sanitized.name;
77
202
  const assetPath = sanitized.savePath ?? targetPath;
78
203
  const dimensions = params.dimensions === 2 ? 2 : 1;
79
- const blendSpaceType = dimensions === 2 ? 'BlendSpace' : 'BlendSpace1D';
80
- const commands = [
81
- `CreateAsset ${blendSpaceType} ${assetName} ${assetPath}`,
82
- `echo Creating ${blendSpaceType} ${assetName} at ${assetPath}`
83
- ];
84
- if (params.skeletonPath) {
85
- commands.push(`SetBlendSpaceSkeleton ${assetName} ${params.skeletonPath}`);
204
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
205
+ try {
206
+ const payload = {
207
+ action: 'create_blend_space',
208
+ name: assetName,
209
+ savePath: assetPath,
210
+ skeletonPath: params.skeletonPath,
211
+ dimensions,
212
+ samples: params.samples
213
+ };
214
+ if (params.horizontalAxis) {
215
+ payload.minX = params.horizontalAxis.minValue;
216
+ payload.maxX = params.horizontalAxis.maxValue;
217
+ }
218
+ if (params.verticalAxis) {
219
+ payload.minY = params.verticalAxis.minValue;
220
+ payload.maxY = params.verticalAxis.maxValue;
221
+ }
222
+ const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject(payload));
223
+ const result = resp?.result ?? resp;
224
+ const resultObj = result && typeof result === 'object' ? result : undefined;
225
+ const isSuccess = resp && resp.success !== false && !!resultObj;
226
+ if (isSuccess && resultObj) {
227
+ const path = typeof resultObj.blendSpacePath === 'string'
228
+ ? resultObj.blendSpacePath
229
+ : `${assetPath}/${assetName}`;
230
+ const warnings = Array.isArray(resultObj.warnings) ? resultObj.warnings : undefined;
231
+ const details = resultObj ? resultObj.details : undefined;
232
+ return {
233
+ success: true,
234
+ message: resp.message || `Blend Space ${assetName} created`,
235
+ path,
236
+ skeletonPath: params.skeletonPath,
237
+ details,
238
+ warnings
239
+ };
240
+ }
241
+ const message = typeof resp?.message === 'string'
242
+ ? resp.message
243
+ : (typeof resp?.error === 'string' ? resp.error : 'Blend space creation failed');
244
+ const error = typeof resp?.error === 'string' ? resp.error : message;
245
+ return { success: false, error };
246
+ }
247
+ catch (err) {
248
+ const error = String(err);
249
+ return { success: false, error: `Failed to create blend space: ${error}` };
250
+ }
251
+ }
252
+ return { success: false, error: 'Automation bridge not connected for createBlendSpace' };
253
+ }
254
+ catch (err) {
255
+ return { success: false, error: `Failed to create blend space: ${err}` };
256
+ }
257
+ }
258
+ async setupControlRig(params) {
259
+ try {
260
+ const targetPath = params.savePath ?? '/Game/Animations';
261
+ const validation = validateAssetParams({ name: params.name, savePath: targetPath });
262
+ if (!validation.valid) {
263
+ return { success: false, error: validation.error ?? 'Invalid asset parameters' };
264
+ }
265
+ const sanitized = validation.sanitized;
266
+ const assetName = sanitized.name;
267
+ const assetPath = sanitized.savePath ?? targetPath;
268
+ const fullPath = `${assetPath}/${assetName}`;
269
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
270
+ try {
271
+ const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject({
272
+ action: 'setup_ik',
273
+ name: assetName,
274
+ savePath: assetPath,
275
+ skeletonPath: params.skeletonPath,
276
+ controls: params.controls
277
+ }), { timeoutMs: 60000 });
278
+ const result = resp?.result ?? resp;
279
+ const resultObj = result && typeof result === 'object' ? result : undefined;
280
+ const isSuccess = resp && resp.success !== false && !!resultObj;
281
+ if (isSuccess && resultObj) {
282
+ const controlRigPath = typeof resultObj.controlRigPath === 'string'
283
+ ? resultObj.controlRigPath
284
+ : fullPath;
285
+ const warnings = Array.isArray(resultObj.warnings) ? resultObj.warnings : undefined;
286
+ const details = resultObj ? resultObj.details : undefined;
287
+ this.trackArtifact(assetName, { path: controlRigPath, type: 'ControlRig' });
288
+ return {
289
+ success: true,
290
+ message: resp.message || `Control Rig ${assetName} created`,
291
+ path: controlRigPath,
292
+ warnings,
293
+ details
294
+ };
295
+ }
296
+ const message = typeof resp?.message === 'string'
297
+ ? resp.message
298
+ : (typeof resp?.error === 'string' ? resp.error : 'Control Rig setup failed');
299
+ const error = typeof resp?.error === 'string' ? resp.error : message;
300
+ return { success: false, error };
301
+ }
302
+ catch (err) {
303
+ const error = String(err);
304
+ return { success: false, error: `Failed to setup control rig: ${error}` };
305
+ }
306
+ }
307
+ return { success: false, error: 'Automation bridge not connected for setupControlRig' };
308
+ }
309
+ catch (err) {
310
+ return { success: false, error: `Failed to setup control rig: ${err}` };
311
+ }
312
+ }
313
+ async setupIK(params) {
314
+ try {
315
+ const actorName = (params.actorName || 'Character').trim();
316
+ const ikBones = Array.isArray(params.ikBones)
317
+ ? params.ikBones.map((b) => String(b)).filter((b) => b.trim().length > 0)
318
+ : [];
319
+ const key = `IK:${actorName}`;
320
+ this.trackArtifact(key, {
321
+ type: 'IKSetup',
322
+ metadata: {
323
+ actorName,
324
+ ikBones,
325
+ enableFootPlacement: params.enableFootPlacement === true
326
+ }
327
+ });
328
+ return {
329
+ success: true,
330
+ message: `IK setup specification recorded for actor '${actorName}'`,
331
+ actorName,
332
+ ikBones: ikBones.length ? ikBones : undefined,
333
+ enableFootPlacement: params.enableFootPlacement === true ? true : undefined
334
+ };
335
+ }
336
+ catch (err) {
337
+ const error = String(err);
338
+ return {
339
+ success: false,
340
+ message: `Failed to record IK setup: ${error}`,
341
+ error
342
+ };
343
+ }
344
+ }
345
+ async createProceduralAnim(params) {
346
+ try {
347
+ const baseName = (params.systemName || '').trim()
348
+ || (params.baseAnimation ? params.baseAnimation.split('/').pop() || '' : '');
349
+ const systemName = baseName || 'ProceduralSystem';
350
+ const basePath = (params.savePath || '/Game/Animations').replace(/\/+$/, '');
351
+ const path = `${basePath || '/Game/Animations'}/${systemName}`;
352
+ this.trackArtifact(systemName, {
353
+ path,
354
+ type: 'ProceduralAnimation',
355
+ metadata: {
356
+ baseAnimation: params.baseAnimation,
357
+ modifiers: Array.isArray(params.modifiers) ? params.modifiers : []
358
+ }
359
+ });
360
+ return {
361
+ success: true,
362
+ message: `Procedural animation system '${systemName}' specification recorded at ${path}`,
363
+ path,
364
+ systemName
365
+ };
366
+ }
367
+ catch (err) {
368
+ const error = String(err);
369
+ return {
370
+ success: false,
371
+ message: `Failed to record procedural animation system: ${error}`,
372
+ error
373
+ };
374
+ }
375
+ }
376
+ async createBlendTree(params) {
377
+ try {
378
+ const rawName = (params.treeName || '').trim();
379
+ const treeName = rawName || 'BlendTree';
380
+ const basePath = (params.savePath || '/Game/Animations').replace(/\/+$/, '');
381
+ const path = `${basePath || '/Game/Animations'}/${treeName}`;
382
+ this.trackArtifact(treeName, {
383
+ path,
384
+ type: 'BlendTree',
385
+ metadata: {
386
+ blendType: params.blendType,
387
+ basePose: params.basePose,
388
+ additiveAnimations: Array.isArray(params.additiveAnimations) ? params.additiveAnimations : []
389
+ }
390
+ });
391
+ return {
392
+ success: true,
393
+ message: `Blend tree '${treeName}' specification recorded at ${path}`,
394
+ path,
395
+ treeName
396
+ };
397
+ }
398
+ catch (err) {
399
+ const error = String(err);
400
+ return {
401
+ success: false,
402
+ message: `Failed to record blend tree specification: ${error}`,
403
+ error
404
+ };
405
+ }
406
+ }
407
+ async cleanup(artifacts) {
408
+ try {
409
+ const pathsToDelete = [];
410
+ if (Array.isArray(artifacts) && artifacts.length > 0) {
411
+ pathsToDelete.push(...artifacts.map((a) => String(a).trim()).filter((a) => a.length > 0));
412
+ }
413
+ else {
414
+ for (const [key, val] of this.managedArtifacts.entries()) {
415
+ if (val.path)
416
+ pathsToDelete.push(val.path);
417
+ else
418
+ pathsToDelete.push(key);
419
+ }
420
+ }
421
+ if (pathsToDelete.length === 0) {
422
+ return {
423
+ success: true,
424
+ message: 'No artifacts to cleanup.'
425
+ };
426
+ }
427
+ let bridgeMessage = '';
428
+ if (this.automationBridge) {
429
+ try {
430
+ const response = await this.automationBridge.sendAutomationRequest('animation_physics', {
431
+ action: 'cleanup',
432
+ artifacts: pathsToDelete
433
+ });
434
+ if (!response.success) {
435
+ bridgeMessage = ` (Engine cleanup failed: ${response.message})`;
436
+ }
437
+ else {
438
+ bridgeMessage = ' (Engine assets deleted)';
439
+ }
440
+ }
441
+ catch (e) {
442
+ bridgeMessage = ` (Engine connection failed: ${e})`;
443
+ }
444
+ }
445
+ else {
446
+ bridgeMessage = ' (No automation bridge available)';
86
447
  }
87
- if (params.horizontalAxis) {
88
- commands.push(`SetBlendSpaceAxis ${assetName} Horizontal ${params.horizontalAxis.name} ${params.horizontalAxis.minValue} ${params.horizontalAxis.maxValue}`);
448
+ const removed = [];
449
+ const toRemoveKeys = [];
450
+ for (const [key, val] of this.managedArtifacts.entries()) {
451
+ if (pathsToDelete.includes(key) || (val.path && pathsToDelete.includes(val.path))) {
452
+ toRemoveKeys.push(key);
453
+ }
89
454
  }
90
- if (dimensions === 2 && params.verticalAxis) {
91
- commands.push(`SetBlendSpaceAxis ${assetName} Vertical ${params.verticalAxis.name} ${params.verticalAxis.minValue} ${params.verticalAxis.maxValue}`);
455
+ for (const key of toRemoveKeys) {
456
+ this.managedArtifacts.delete(key);
457
+ removed.push(key);
92
458
  }
93
- if (params.samples) {
94
- for (const sample of params.samples) {
95
- const coords = dimensions === 1 ? `${sample.x}` : `${sample.x} ${sample.y ?? 0}`;
96
- commands.push(`AddBlendSpaceSample ${assetName} ${sample.animation} ${coords}`);
459
+ for (const path of pathsToDelete) {
460
+ if (!removed.includes(path)) {
461
+ removed.push(path);
97
462
  }
98
463
  }
99
- await this.bridge.executeConsoleCommands(commands);
100
464
  return {
101
465
  success: true,
102
- message: `Blend Space ${assetName} created`,
103
- path: `${assetPath}/${assetName}`
466
+ message: `Cleanup attempt processed for ${pathsToDelete.length} artifacts${bridgeMessage}`,
467
+ removed
104
468
  };
105
469
  }
106
470
  catch (err) {
107
- return { success: false, error: `Failed to create blend space: ${err}` };
471
+ const error = String(err);
472
+ return {
473
+ success: false,
474
+ message: `Failed to cleanup animation artifacts: ${error}`,
475
+ error
476
+ };
108
477
  }
109
478
  }
110
- async setupControlRig(params) {
479
+ async createAnimationAsset(params) {
111
480
  try {
112
- const targetPath = params.savePath ?? '/Game/Animations';
481
+ const targetPath = (params.path || params.savePath) ?? '/Game/Animations';
113
482
  const validation = validateAssetParams({ name: params.name, savePath: targetPath });
114
483
  if (!validation.valid) {
115
- return { success: false, error: validation.error ?? 'Invalid asset parameters' };
484
+ const message = validation.error ?? 'Invalid asset parameters';
485
+ return { success: false, message, error: message };
116
486
  }
117
487
  const sanitized = validation.sanitized;
118
488
  const assetName = sanitized.name;
119
489
  const assetPath = sanitized.savePath ?? targetPath;
120
490
  const fullPath = `${assetPath}/${assetName}`;
121
- const commands = [
122
- `CreateAsset ControlRig ${assetName} ${assetPath}`,
123
- `SetControlRigSkeleton ${assetName} ${params.skeletonPath}`
124
- ];
125
- if (params.controls) {
126
- for (const control of params.controls) {
127
- commands.push(`AddControlRigControl ${assetName} ${control.name} ${control.type} ${control.bone ?? ''}`);
128
- if (control.defaultValue !== undefined) {
129
- commands.push(`SetControlRigDefault ${assetName} ${control.name} ${JSON.stringify(control.defaultValue)}`);
491
+ const normalizedType = (params.assetType || 'sequence').toLowerCase();
492
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
493
+ try {
494
+ const payload = {
495
+ action: 'create_animation_asset',
496
+ name: assetName,
497
+ savePath: assetPath,
498
+ skeletonPath: params.skeletonPath,
499
+ assetType: normalizedType
500
+ };
501
+ const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject(payload), { timeoutMs: 60000 });
502
+ const result = resp?.result ?? resp;
503
+ const resultObj = result && typeof result === 'object' ? result : undefined;
504
+ const isSuccess = resp && resp.success !== false && !!resultObj;
505
+ if (isSuccess && resultObj) {
506
+ const assetPathResult = typeof resultObj.assetPath === 'string' ? resultObj.assetPath : fullPath;
507
+ const assetTypeResult = typeof resultObj.assetType === 'string' ? resultObj.assetType : undefined;
508
+ const existingAsset = typeof resultObj.existingAsset === 'boolean' ? Boolean(resultObj.existingAsset) : false;
509
+ this.trackArtifact(assetName, { path: assetPathResult, type: 'AnimationAsset' });
510
+ return {
511
+ success: true,
512
+ message: resp.message || `Animation asset created at ${assetPathResult}`,
513
+ path: assetPathResult,
514
+ assetType: assetTypeResult,
515
+ existingAsset
516
+ };
130
517
  }
518
+ const message = typeof resp?.message === 'string'
519
+ ? resp.message
520
+ : (typeof resp?.error === 'string' ? resp.error : 'Animation asset creation failed');
521
+ const error = typeof resp?.error === 'string' ? resp.error : message;
522
+ return { success: false, message, error };
523
+ }
524
+ catch (err) {
525
+ const error = String(err);
526
+ return {
527
+ success: false,
528
+ message: `Failed to create animation asset: ${error}`,
529
+ error
530
+ };
131
531
  }
132
532
  }
133
- await this.bridge.executeConsoleCommands(commands);
134
533
  return {
135
- success: true,
136
- message: `Control Rig ${assetName} created`,
137
- path: fullPath
534
+ success: false,
535
+ message: 'Automation bridge not connected for createAnimationAsset',
536
+ error: 'AUTOMATION_BRIDGE_UNAVAILABLE'
138
537
  };
139
538
  }
140
539
  catch (err) {
141
- return { success: false, error: `Failed to setup control rig: ${err}` };
540
+ const error = String(err);
541
+ return {
542
+ success: false,
543
+ message: `Failed to create animation asset: ${error}`,
544
+ error
545
+ };
546
+ }
547
+ }
548
+ async addNotify(params) {
549
+ try {
550
+ const rawPath = (params.animationPath || params.assetPath || '').trim();
551
+ if (!rawPath) {
552
+ const error = 'animationPath or assetPath is required for addNotify';
553
+ return { success: false, message: error, error };
554
+ }
555
+ const notifyName = (params.notifyName || 'Notify').trim();
556
+ const time = typeof params.time === 'number' && params.time >= 0 ? params.time : 0;
557
+ if (this.automationBridge && typeof this.automationBridge.sendAutomationRequest === 'function') {
558
+ try {
559
+ const resp = await this.automationBridge.sendAutomationRequest('animation_physics', cleanObject({
560
+ action: 'add_notify',
561
+ assetPath: rawPath,
562
+ notifyName,
563
+ time
564
+ }), { timeoutMs: 60000 });
565
+ const result = resp?.result ?? resp;
566
+ const resultObj = result && typeof result === 'object' ? result : undefined;
567
+ if (resp && resp.success !== false && resultObj) {
568
+ return {
569
+ success: true,
570
+ message: resp.message || `Notify '${notifyName}' added to ${rawPath} at time ${time}`,
571
+ assetPath: typeof resultObj.assetPath === 'string' ? resultObj.assetPath : rawPath,
572
+ notifyName,
573
+ time
574
+ };
575
+ }
576
+ }
577
+ catch (err) {
578
+ const error = String(err);
579
+ return {
580
+ success: false,
581
+ message: `Failed to add notify: ${error}`,
582
+ error
583
+ };
584
+ }
585
+ }
586
+ return {
587
+ success: false,
588
+ message: 'Automation bridge not connected for addNotify',
589
+ error: 'AUTOMATION_BRIDGE_UNAVAILABLE'
590
+ };
591
+ }
592
+ catch (err) {
593
+ const error = String(err);
594
+ return {
595
+ success: false,
596
+ message: `Failed to add notify: ${error}`,
597
+ error
598
+ };
142
599
  }
143
600
  }
144
601
  async createLevelSequence(params) {
@@ -179,380 +636,22 @@ export class AnimationTools {
179
636
  }
180
637
  async playAnimation(params) {
181
638
  try {
182
- const script = this.buildPlayAnimationScript({
639
+ const commands = [
640
+ `PlayAnimation ${params.actorName} ${params.animationType} ${params.animationPath} ${params.playRate ?? 1.0} ${params.loop ?? false} ${params.blendInTime ?? 0.25} ${params.blendOutTime ?? 0.25}`
641
+ ];
642
+ await this.bridge.executeConsoleCommands(commands);
643
+ return {
644
+ success: true,
645
+ message: `Animation ${params.animationType} triggered on ${params.actorName}`,
183
646
  actorName: params.actorName,
184
647
  animationType: params.animationType,
185
- animationPath: params.animationPath,
186
- playRate: params.playRate ?? 1.0,
187
- loop: params.loop ?? false,
188
- blendInTime: params.blendInTime ?? 0.25,
189
- blendOutTime: params.blendOutTime ?? 0.25
190
- });
191
- const response = await this.bridge.executePython(script);
192
- const interpreted = interpretStandardResult(response, {
193
- successMessage: `Animation ${params.animationType} triggered on ${params.actorName}`,
194
- failureMessage: `Failed to play animation on ${params.actorName}`
195
- });
196
- const payload = interpreted.payload ?? {};
197
- const warnings = interpreted.warnings ?? coerceStringArray(payload.warnings) ?? undefined;
198
- const details = interpreted.details ?? coerceStringArray(payload.details) ?? undefined;
199
- const availableActors = coerceStringArray(payload.availableActors);
200
- const actorName = coerceString(payload.actorName) ?? params.actorName;
201
- const animationType = coerceString(payload.animationType) ?? params.animationType;
202
- const assetPath = coerceString(payload.assetPath) ?? params.animationPath;
203
- const errorMessage = coerceString(payload.error) ?? interpreted.error ?? `Animation playback failed for ${params.actorName}`;
204
- if (interpreted.success) {
205
- const result = {
206
- success: true,
207
- message: interpreted.message
208
- };
209
- if (warnings && warnings.length > 0) {
210
- result.warnings = warnings;
211
- }
212
- if (details && details.length > 0) {
213
- result.details = details;
214
- }
215
- if (actorName) {
216
- result.actorName = actorName;
217
- }
218
- if (animationType) {
219
- result.animationType = animationType;
220
- }
221
- if (assetPath) {
222
- result.assetPath = assetPath;
223
- }
224
- return result;
225
- }
226
- const failure = {
227
- success: false,
228
- message: `Failed to play animation: ${errorMessage}`,
229
- error: errorMessage
648
+ assetPath: params.animationPath
230
649
  };
231
- if (warnings && warnings.length > 0) {
232
- failure.warnings = warnings;
233
- }
234
- if (details && details.length > 0) {
235
- failure.details = details;
236
- }
237
- if (availableActors && availableActors.length > 0) {
238
- failure.availableActors = availableActors;
239
- }
240
- if (actorName) {
241
- failure.actorName = actorName;
242
- }
243
- if (animationType) {
244
- failure.animationType = animationType;
245
- }
246
- if (assetPath) {
247
- failure.assetPath = assetPath;
248
- }
249
- return failure;
250
650
  }
251
651
  catch (err) {
252
652
  const error = `Failed to play animation: ${err}`;
253
653
  return { success: false, message: error, error: String(err) };
254
654
  }
255
655
  }
256
- buildCreateAnimationBlueprintScript(args) {
257
- const payload = JSON.stringify(args);
258
- return `
259
- import unreal
260
- import json
261
- import traceback
262
-
263
- params = json.loads(${JSON.stringify(payload)})
264
-
265
- result = {
266
- "success": False,
267
- "message": "",
268
- "error": "",
269
- "warnings": [],
270
- "details": [],
271
- "exists": False,
272
- "skeleton": params.get("skeletonPath") or ""
273
- }
274
-
275
- try:
276
- asset_path = (params.get("path") or "/Game").rstrip('/')
277
- asset_name = params.get("name") or ""
278
- full_path = f"{asset_path}/{asset_name}"
279
- result["path"] = full_path
280
-
281
- editor_lib = unreal.EditorAssetLibrary
282
- asset_subsystem = None
283
- try:
284
- asset_subsystem = unreal.get_editor_subsystem(unreal.EditorAssetSubsystem)
285
- except Exception:
286
- asset_subsystem = None
287
-
288
- skeleton_path = params.get("skeletonPath")
289
- skeleton_asset = None
290
- if skeleton_path:
291
- if editor_lib.does_asset_exist(skeleton_path):
292
- skeleton_asset = editor_lib.load_asset(skeleton_path)
293
- if skeleton_asset and isinstance(skeleton_asset, unreal.Skeleton):
294
- result["details"].append(f"Using skeleton: {skeleton_path}")
295
- result["skeleton"] = skeleton_path
296
- else:
297
- result["error"] = f"Skeleton asset invalid at {skeleton_path}"
298
- result["warnings"].append(result["error"])
299
- skeleton_asset = None
300
- else:
301
- result["error"] = f"Skeleton not found at {skeleton_path}"
302
- result["warnings"].append(result["error"])
303
-
304
- if not skeleton_asset:
305
- raise RuntimeError(result["error"] or f"Skeleton {skeleton_path} unavailable")
306
-
307
- does_exist = False
308
- try:
309
- if asset_subsystem and hasattr(asset_subsystem, 'does_asset_exist'):
310
- does_exist = asset_subsystem.does_asset_exist(full_path)
311
- else:
312
- does_exist = editor_lib.does_asset_exist(full_path)
313
- except Exception:
314
- does_exist = editor_lib.does_asset_exist(full_path)
315
-
316
- if does_exist:
317
- result["exists"] = True
318
- loaded = editor_lib.load_asset(full_path)
319
- if loaded:
320
- result["success"] = True
321
- result["message"] = f"Animation Blueprint already exists at {full_path}"
322
- result["details"].append(result["message"])
323
- else:
324
- result["error"] = f"Asset exists but could not be loaded: {full_path}"
325
- result["warnings"].append(result["error"])
326
- else:
327
- factory = unreal.AnimBlueprintFactory()
328
- if skeleton_asset:
329
- try:
330
- factory.target_skeleton = skeleton_asset
331
- except Exception as assign_error:
332
- result["warnings"].append(f"Unable to assign skeleton {skeleton_path}: {assign_error}")
333
-
334
- asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
335
- created = asset_tools.create_asset(
336
- asset_name=asset_name,
337
- package_path=asset_path,
338
- asset_class=unreal.AnimBlueprint,
339
- factory=factory
340
- )
341
-
342
- if created:
343
- editor_lib.save_asset(full_path, only_if_is_dirty=False)
344
- result["success"] = True
345
- result["message"] = f"Animation Blueprint created at {full_path}"
346
- result["details"].append(result["message"])
347
- else:
348
- result["error"] = f"Failed to create Animation Blueprint {asset_name}"
349
-
350
- except Exception as exc:
351
- result["error"] = str(exc)
352
- result["warnings"].append(result["error"])
353
- tb = traceback.format_exc()
354
- if tb:
355
- result.setdefault("details", []).append(tb)
356
-
357
- if result["success"] and not result.get("message"):
358
- result["message"] = f"Animation Blueprint created at {result.get('path')}"
359
-
360
- if not result["success"] and not result.get("error"):
361
- result["error"] = "Animation Blueprint creation failed"
362
-
363
- if not result.get("warnings"):
364
- result.pop("warnings", None)
365
- if not result.get("details"):
366
- result.pop("details", None)
367
- if not result.get("error"):
368
- result.pop("error", None)
369
-
370
- print('RESULT:' + json.dumps(result))
371
- `.trim();
372
- }
373
- parseAnimationBlueprintResponse(response, assetName, assetPath) {
374
- const interpreted = interpretStandardResult(response, {
375
- successMessage: `Animation Blueprint ${assetName} created`,
376
- failureMessage: `Failed to create Animation Blueprint ${assetName}`
377
- });
378
- const payload = interpreted.payload ?? {};
379
- const path = coerceString(payload.path) ?? `${assetPath}/${assetName}`;
380
- const exists = coerceBoolean(payload.exists);
381
- const skeleton = coerceString(payload.skeleton);
382
- const warnings = interpreted.warnings ?? coerceStringArray(payload.warnings) ?? undefined;
383
- const details = interpreted.details ?? coerceStringArray(payload.details) ?? undefined;
384
- if (interpreted.success) {
385
- const result = {
386
- success: true,
387
- message: interpreted.message,
388
- path
389
- };
390
- if (typeof exists === 'boolean') {
391
- result.exists = exists;
392
- }
393
- if (skeleton) {
394
- result.skeleton = skeleton;
395
- }
396
- if (warnings && warnings.length > 0) {
397
- result.warnings = warnings;
398
- }
399
- if (details && details.length > 0) {
400
- result.details = details;
401
- }
402
- return result;
403
- }
404
- const errorMessage = coerceString(payload.error) ?? interpreted.error ?? interpreted.message;
405
- const failure = {
406
- success: false,
407
- message: `Failed to create Animation Blueprint: ${errorMessage}`,
408
- error: errorMessage,
409
- path
410
- };
411
- if (typeof exists === 'boolean') {
412
- failure.exists = exists;
413
- }
414
- if (skeleton) {
415
- failure.skeleton = skeleton;
416
- }
417
- if (warnings && warnings.length > 0) {
418
- failure.warnings = warnings;
419
- }
420
- if (details && details.length > 0) {
421
- failure.details = details;
422
- }
423
- return failure;
424
- }
425
- buildPlayAnimationScript(args) {
426
- const payload = JSON.stringify(args);
427
- return `
428
- import unreal
429
- import json
430
- import traceback
431
-
432
- params = json.loads(${JSON.stringify(payload)})
433
-
434
- result = {
435
- "success": False,
436
- "message": "",
437
- "error": "",
438
- "warnings": [],
439
- "details": [],
440
- "actorName": params.get("actorName"),
441
- "animationType": params.get("animationType"),
442
- "assetPath": params.get("animationPath"),
443
- "availableActors": []
444
- }
445
-
446
- try:
447
- actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
448
- actors = actor_subsystem.get_all_level_actors() if actor_subsystem else []
449
- target = None
450
- search = params.get("actorName") or ""
451
- search_lower = search.lower()
452
-
453
- for actor in actors:
454
- if not actor:
455
- continue
456
- name = (actor.get_name() or "").lower()
457
- label = (actor.get_actor_label() or "").lower()
458
- if search_lower and (search_lower == name or search_lower == label or search_lower in label):
459
- target = actor
460
- break
461
-
462
- if not target:
463
- result["error"] = f"Actor not found: {search}"
464
- result["warnings"].append("Actor search yielded no results")
465
- suggestions = []
466
- for actor in actors[:20]:
467
- try:
468
- suggestions.append(actor.get_actor_label())
469
- except Exception:
470
- continue
471
- if suggestions:
472
- result["availableActors"] = suggestions
473
- else:
474
- try:
475
- display_name = target.get_actor_label() or target.get_name()
476
- if display_name:
477
- result["actorName"] = display_name
478
- except Exception:
479
- pass
480
-
481
- skeletal_component = target.get_component_by_class(unreal.SkeletalMeshComponent)
482
- if not skeletal_component:
483
- try:
484
- skeletal_component = target.get_editor_property('mesh')
485
- except Exception:
486
- skeletal_component = None
487
-
488
- if not skeletal_component:
489
- result["error"] = "No SkeletalMeshComponent found on actor"
490
- result["warnings"].append("Actor lacks SkeletalMeshComponent")
491
- else:
492
- asset_path = params.get("animationPath")
493
- if not asset_path or not unreal.EditorAssetLibrary.does_asset_exist(asset_path):
494
- result["error"] = f"Animation asset not found: {asset_path}"
495
- result["warnings"].append("Animation asset missing")
496
- else:
497
- asset = unreal.EditorAssetLibrary.load_asset(asset_path)
498
- anim_type = params.get("animationType") or ""
499
- if anim_type == 'Montage':
500
- anim_instance = skeletal_component.get_anim_instance()
501
- if anim_instance:
502
- try:
503
- anim_instance.montage_play(asset, params.get("playRate", 1.0))
504
- result["success"] = True
505
- result["message"] = f"Montage playing on {result.get('actorName') or search}"
506
- result["details"].append(result["message"])
507
- except Exception as play_error:
508
- result["error"] = f"Failed to play montage: {play_error}"
509
- result["warnings"].append(result["error"])
510
- else:
511
- result["error"] = "AnimInstance not found on SkeletalMeshComponent"
512
- result["warnings"].append(result["error"])
513
- elif anim_type == 'Sequence':
514
- try:
515
- skeletal_component.play_animation(asset, bool(params.get("loop")))
516
- try:
517
- anim_instance = skeletal_component.get_anim_instance()
518
- if anim_instance:
519
- anim_instance.set_play_rate(params.get("playRate", 1.0))
520
- except Exception:
521
- pass
522
- result["success"] = True
523
- result["message"] = f"Sequence playing on {result.get('actorName') or search}"
524
- result["details"].append(result["message"])
525
- except Exception as play_error:
526
- result["error"] = f"Failed to play sequence: {play_error}"
527
- result["warnings"].append(result["error"])
528
- else:
529
- result["error"] = "BlendSpace playback requires Animation Blueprint support"
530
- result["warnings"].append("Unsupported animation type for direct play")
531
-
532
- except Exception as exc:
533
- result["error"] = str(exc)
534
- result["warnings"].append(result["error"])
535
- tb = traceback.format_exc()
536
- if tb:
537
- result["details"].append(tb)
538
-
539
- if result["success"] and not result.get("message"):
540
- result["message"] = f"Animation {result.get('animationType')} triggered on {result.get('actorName') or params.get('actorName')}"
541
-
542
- if not result["success"] and not result.get("error"):
543
- result["error"] = "Animation playback failed"
544
-
545
- if not result.get("warnings"):
546
- result.pop("warnings", None)
547
- if not result.get("details"):
548
- result.pop("details", None)
549
- if not result.get("availableActors"):
550
- result.pop("availableActors", None)
551
- if not result.get("error"):
552
- result.pop("error", None)
553
-
554
- print('RESULT:' + json.dumps(result))
555
- `.trim();
556
- }
557
656
  }
558
657
  //# sourceMappingURL=animation.js.map