unreal-engine-mcp-server 0.4.7 → 0.5.0

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 (438) 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.yml +148 -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 +23 -0
  19. package/.github/workflows/labeler.yml +16 -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 +12 -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 +267 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/README.md +166 -71
  31. package/claude_desktop_config_example.json +7 -6
  32. package/dist/automation/bridge.d.ts +50 -0
  33. package/dist/automation/bridge.js +452 -0
  34. package/dist/automation/connection-manager.d.ts +23 -0
  35. package/dist/automation/connection-manager.js +107 -0
  36. package/dist/automation/handshake.d.ts +11 -0
  37. package/dist/automation/handshake.js +89 -0
  38. package/dist/automation/index.d.ts +3 -0
  39. package/dist/automation/index.js +3 -0
  40. package/dist/automation/message-handler.d.ts +12 -0
  41. package/dist/automation/message-handler.js +149 -0
  42. package/dist/automation/request-tracker.d.ts +25 -0
  43. package/dist/automation/request-tracker.js +98 -0
  44. package/dist/automation/types.d.ts +130 -0
  45. package/dist/automation/types.js +2 -0
  46. package/dist/cli.js +32 -5
  47. package/dist/config.d.ts +27 -0
  48. package/dist/config.js +60 -0
  49. package/dist/constants.d.ts +12 -0
  50. package/dist/constants.js +12 -0
  51. package/dist/graphql/resolvers.d.ts +268 -0
  52. package/dist/graphql/resolvers.js +743 -0
  53. package/dist/graphql/schema.d.ts +5 -0
  54. package/dist/graphql/schema.js +437 -0
  55. package/dist/graphql/server.d.ts +26 -0
  56. package/dist/graphql/server.js +115 -0
  57. package/dist/graphql/types.d.ts +7 -0
  58. package/dist/graphql/types.js +2 -0
  59. package/dist/handlers/resource-handlers.d.ts +20 -0
  60. package/dist/handlers/resource-handlers.js +180 -0
  61. package/dist/index.d.ts +31 -18
  62. package/dist/index.js +119 -619
  63. package/dist/prompts/index.js +4 -4
  64. package/dist/resources/actors.d.ts +17 -12
  65. package/dist/resources/actors.js +56 -76
  66. package/dist/resources/assets.d.ts +6 -14
  67. package/dist/resources/assets.js +115 -147
  68. package/dist/resources/levels.d.ts +13 -13
  69. package/dist/resources/levels.js +25 -34
  70. package/dist/server/resource-registry.d.ts +20 -0
  71. package/dist/server/resource-registry.js +37 -0
  72. package/dist/server/tool-registry.d.ts +23 -0
  73. package/dist/server/tool-registry.js +322 -0
  74. package/dist/server-setup.d.ts +21 -0
  75. package/dist/server-setup.js +111 -0
  76. package/dist/services/health-monitor.d.ts +34 -0
  77. package/dist/services/health-monitor.js +105 -0
  78. package/dist/services/metrics-server.d.ts +11 -0
  79. package/dist/services/metrics-server.js +105 -0
  80. package/dist/tools/actors.d.ts +147 -9
  81. package/dist/tools/actors.js +350 -311
  82. package/dist/tools/animation.d.ts +135 -4
  83. package/dist/tools/animation.js +510 -411
  84. package/dist/tools/assets.d.ts +117 -19
  85. package/dist/tools/assets.js +259 -284
  86. package/dist/tools/audio.d.ts +102 -42
  87. package/dist/tools/audio.js +272 -685
  88. package/dist/tools/base-tool.d.ts +17 -0
  89. package/dist/tools/base-tool.js +46 -0
  90. package/dist/tools/behavior-tree.d.ts +94 -0
  91. package/dist/tools/behavior-tree.js +39 -0
  92. package/dist/tools/blueprint/helpers.d.ts +29 -0
  93. package/dist/tools/blueprint/helpers.js +182 -0
  94. package/dist/tools/blueprint.d.ts +228 -118
  95. package/dist/tools/blueprint.js +685 -832
  96. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  97. package/dist/tools/consolidated-tool-definitions.js +829 -496
  98. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  99. package/dist/tools/consolidated-tool-handlers.js +211 -1026
  100. package/dist/tools/debug.d.ts +143 -85
  101. package/dist/tools/debug.js +234 -180
  102. package/dist/tools/dynamic-handler-registry.d.ts +11 -0
  103. package/dist/tools/dynamic-handler-registry.js +101 -0
  104. package/dist/tools/editor.d.ts +139 -18
  105. package/dist/tools/editor.js +239 -244
  106. package/dist/tools/engine.d.ts +10 -4
  107. package/dist/tools/engine.js +13 -5
  108. package/dist/tools/environment.d.ts +36 -0
  109. package/dist/tools/environment.js +267 -0
  110. package/dist/tools/foliage.d.ts +105 -14
  111. package/dist/tools/foliage.js +219 -331
  112. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  113. package/dist/tools/handlers/actor-handlers.js +232 -0
  114. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  115. package/dist/tools/handlers/animation-handlers.js +185 -0
  116. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  117. package/dist/tools/handlers/argument-helper.js +80 -0
  118. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  119. package/dist/tools/handlers/asset-handlers.js +496 -0
  120. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  121. package/dist/tools/handlers/audio-handlers.js +166 -0
  122. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  123. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  124. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  125. package/dist/tools/handlers/common-handlers.js +56 -0
  126. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  127. package/dist/tools/handlers/editor-handlers.js +119 -0
  128. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  129. package/dist/tools/handlers/effect-handlers.js +171 -0
  130. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  131. package/dist/tools/handlers/environment-handlers.js +170 -0
  132. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  133. package/dist/tools/handlers/graph-handlers.js +90 -0
  134. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  135. package/dist/tools/handlers/input-handlers.js +21 -0
  136. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  137. package/dist/tools/handlers/inspect-handlers.js +383 -0
  138. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  139. package/dist/tools/handlers/level-handlers.js +237 -0
  140. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  141. package/dist/tools/handlers/lighting-handlers.js +144 -0
  142. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  143. package/dist/tools/handlers/performance-handlers.js +130 -0
  144. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  145. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  146. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  147. package/dist/tools/handlers/sequence-handlers.js +376 -0
  148. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  149. package/dist/tools/handlers/system-handlers.js +506 -0
  150. package/dist/tools/input.d.ts +19 -0
  151. package/dist/tools/input.js +89 -0
  152. package/dist/tools/introspection.d.ts +103 -40
  153. package/dist/tools/introspection.js +425 -568
  154. package/dist/tools/landscape.d.ts +97 -36
  155. package/dist/tools/landscape.js +280 -409
  156. package/dist/tools/level.d.ts +130 -10
  157. package/dist/tools/level.js +639 -675
  158. package/dist/tools/lighting.d.ts +77 -38
  159. package/dist/tools/lighting.js +441 -943
  160. package/dist/tools/logs.d.ts +3 -3
  161. package/dist/tools/logs.js +5 -57
  162. package/dist/tools/materials.d.ts +91 -24
  163. package/dist/tools/materials.js +190 -118
  164. package/dist/tools/niagara.d.ts +149 -39
  165. package/dist/tools/niagara.js +232 -182
  166. package/dist/tools/performance.d.ts +27 -12
  167. package/dist/tools/performance.js +204 -122
  168. package/dist/tools/physics.d.ts +32 -77
  169. package/dist/tools/physics.js +171 -582
  170. package/dist/tools/property-dictionary.d.ts +13 -0
  171. package/dist/tools/property-dictionary.js +82 -0
  172. package/dist/tools/sequence.d.ts +73 -48
  173. package/dist/tools/sequence.js +196 -748
  174. package/dist/tools/tool-definition-utils.d.ts +59 -0
  175. package/dist/tools/tool-definition-utils.js +35 -0
  176. package/dist/tools/ui.d.ts +66 -34
  177. package/dist/tools/ui.js +134 -214
  178. package/dist/types/env.d.ts +0 -3
  179. package/dist/types/env.js +0 -7
  180. package/dist/types/tool-interfaces.d.ts +898 -0
  181. package/dist/types/tool-interfaces.js +2 -0
  182. package/dist/types/tool-types.d.ts +183 -19
  183. package/dist/types/tool-types.js +0 -4
  184. package/dist/unreal-bridge.d.ts +24 -131
  185. package/dist/unreal-bridge.js +364 -1506
  186. package/dist/utils/command-validator.d.ts +9 -0
  187. package/dist/utils/command-validator.js +67 -0
  188. package/dist/utils/elicitation.d.ts +1 -1
  189. package/dist/utils/elicitation.js +12 -15
  190. package/dist/utils/error-handler.d.ts +2 -51
  191. package/dist/utils/error-handler.js +11 -87
  192. package/dist/utils/ini-reader.d.ts +3 -0
  193. package/dist/utils/ini-reader.js +69 -0
  194. package/dist/utils/logger.js +9 -6
  195. package/dist/utils/normalize.d.ts +3 -0
  196. package/dist/utils/normalize.js +56 -0
  197. package/dist/utils/response-factory.d.ts +7 -0
  198. package/dist/utils/response-factory.js +33 -0
  199. package/dist/utils/response-validator.d.ts +3 -24
  200. package/dist/utils/response-validator.js +130 -81
  201. package/dist/utils/result-helpers.d.ts +4 -5
  202. package/dist/utils/result-helpers.js +15 -16
  203. package/dist/utils/safe-json.js +5 -11
  204. package/dist/utils/unreal-command-queue.d.ts +24 -0
  205. package/dist/utils/unreal-command-queue.js +120 -0
  206. package/dist/utils/validation.d.ts +0 -40
  207. package/dist/utils/validation.js +1 -78
  208. package/dist/wasm/index.d.ts +70 -0
  209. package/dist/wasm/index.js +535 -0
  210. package/docs/GraphQL-API.md +888 -0
  211. package/docs/Migration-Guide-v0.5.0.md +692 -0
  212. package/docs/Roadmap.md +53 -0
  213. package/docs/WebAssembly-Integration.md +628 -0
  214. package/docs/editor-plugin-extension.md +370 -0
  215. package/docs/handler-mapping.md +242 -0
  216. package/docs/native-automation-progress.md +128 -0
  217. package/docs/testing-guide.md +423 -0
  218. package/mcp-config-example.json +6 -6
  219. package/package.json +60 -27
  220. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  221. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  222. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  223. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  224. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  225. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  226. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  227. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  228. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  272. package/scripts/check-unreal-connection.mjs +19 -0
  273. package/scripts/clean-tmp.js +23 -0
  274. package/scripts/patch-wasm.js +26 -0
  275. package/scripts/run-all-tests.mjs +131 -0
  276. package/scripts/smoke-test.ts +94 -0
  277. package/scripts/sync-mcp-plugin.js +143 -0
  278. package/scripts/test-no-plugin-alternates.mjs +113 -0
  279. package/scripts/validate-server.js +46 -0
  280. package/scripts/verify-automation-bridge.js +200 -0
  281. package/server.json +57 -21
  282. package/src/automation/bridge.ts +558 -0
  283. package/src/automation/connection-manager.ts +130 -0
  284. package/src/automation/handshake.ts +99 -0
  285. package/src/automation/index.ts +2 -0
  286. package/src/automation/message-handler.ts +167 -0
  287. package/src/automation/request-tracker.ts +123 -0
  288. package/src/automation/types.ts +107 -0
  289. package/src/cli.ts +33 -6
  290. package/src/config.ts +73 -0
  291. package/src/constants.ts +12 -0
  292. package/src/graphql/resolvers.ts +1010 -0
  293. package/src/graphql/schema.ts +452 -0
  294. package/src/graphql/server.ts +154 -0
  295. package/src/graphql/types.ts +7 -0
  296. package/src/handlers/resource-handlers.ts +186 -0
  297. package/src/index.ts +152 -663
  298. package/src/prompts/index.ts +4 -4
  299. package/src/resources/actors.ts +58 -76
  300. package/src/resources/assets.ts +147 -134
  301. package/src/resources/levels.ts +28 -33
  302. package/src/server/resource-registry.ts +47 -0
  303. package/src/server/tool-registry.ts +354 -0
  304. package/src/server-setup.ts +148 -0
  305. package/src/services/health-monitor.ts +132 -0
  306. package/src/services/metrics-server.ts +142 -0
  307. package/src/tools/actors.ts +417 -322
  308. package/src/tools/animation.ts +671 -461
  309. package/src/tools/assets.ts +353 -289
  310. package/src/tools/audio.ts +323 -766
  311. package/src/tools/base-tool.ts +52 -0
  312. package/src/tools/behavior-tree.ts +45 -0
  313. package/src/tools/blueprint/helpers.ts +189 -0
  314. package/src/tools/blueprint.ts +787 -965
  315. package/src/tools/consolidated-tool-definitions.ts +993 -515
  316. package/src/tools/consolidated-tool-handlers.ts +272 -1139
  317. package/src/tools/debug.ts +292 -187
  318. package/src/tools/dynamic-handler-registry.ts +151 -0
  319. package/src/tools/editor.ts +309 -246
  320. package/src/tools/engine.ts +14 -3
  321. package/src/tools/environment.ts +287 -0
  322. package/src/tools/foliage.ts +314 -379
  323. package/src/tools/handlers/actor-handlers.ts +271 -0
  324. package/src/tools/handlers/animation-handlers.ts +237 -0
  325. package/src/tools/handlers/argument-helper.ts +142 -0
  326. package/src/tools/handlers/asset-handlers.ts +532 -0
  327. package/src/tools/handlers/audio-handlers.ts +194 -0
  328. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  329. package/src/tools/handlers/common-handlers.ts +87 -0
  330. package/src/tools/handlers/editor-handlers.ts +123 -0
  331. package/src/tools/handlers/effect-handlers.ts +220 -0
  332. package/src/tools/handlers/environment-handlers.ts +183 -0
  333. package/src/tools/handlers/graph-handlers.ts +116 -0
  334. package/src/tools/handlers/input-handlers.ts +28 -0
  335. package/src/tools/handlers/inspect-handlers.ts +450 -0
  336. package/src/tools/handlers/level-handlers.ts +252 -0
  337. package/src/tools/handlers/lighting-handlers.ts +147 -0
  338. package/src/tools/handlers/performance-handlers.ts +132 -0
  339. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  340. package/src/tools/handlers/sequence-handlers.ts +415 -0
  341. package/src/tools/handlers/system-handlers.ts +564 -0
  342. package/src/tools/input.ts +101 -0
  343. package/src/tools/introspection.ts +493 -584
  344. package/src/tools/landscape.ts +394 -489
  345. package/src/tools/level.ts +752 -694
  346. package/src/tools/lighting.ts +583 -984
  347. package/src/tools/logs.ts +9 -57
  348. package/src/tools/materials.ts +231 -121
  349. package/src/tools/niagara.ts +293 -168
  350. package/src/tools/performance.ts +320 -168
  351. package/src/tools/physics.ts +268 -613
  352. package/src/tools/property-dictionary.ts +98 -0
  353. package/src/tools/sequence.ts +255 -815
  354. package/src/tools/tool-definition-utils.ts +35 -0
  355. package/src/tools/ui.ts +207 -283
  356. package/src/types/env.ts +0 -10
  357. package/src/types/tool-interfaces.ts +250 -0
  358. package/src/types/tool-types.ts +243 -21
  359. package/src/unreal-bridge.ts +460 -1550
  360. package/src/utils/command-validator.ts +75 -0
  361. package/src/utils/elicitation.ts +10 -7
  362. package/src/utils/error-handler.ts +14 -90
  363. package/src/utils/ini-reader.ts +86 -0
  364. package/src/utils/logger.ts +8 -3
  365. package/src/utils/normalize.ts +60 -0
  366. package/src/utils/response-factory.ts +39 -0
  367. package/src/utils/response-validator.ts +176 -56
  368. package/src/utils/result-helpers.ts +21 -19
  369. package/src/utils/safe-json.ts +14 -11
  370. package/src/utils/unreal-command-queue.ts +152 -0
  371. package/src/utils/validation.ts +4 -1
  372. package/src/wasm/index.ts +838 -0
  373. package/test-server.mjs +100 -0
  374. package/tests/run-unreal-tool-tests.mjs +242 -14
  375. package/tests/test-animation.mjs +44 -0
  376. package/tests/test-asset-advanced.mjs +82 -0
  377. package/tests/test-asset-errors.mjs +35 -0
  378. package/tests/test-audio.mjs +219 -0
  379. package/tests/test-automation-timeouts.mjs +98 -0
  380. package/tests/test-behavior-tree.mjs +261 -0
  381. package/tests/test-blueprint-events.mjs +35 -0
  382. package/tests/test-blueprint-graph.mjs +79 -0
  383. package/tests/test-blueprint.mjs +577 -0
  384. package/tests/test-client-mode.mjs +86 -0
  385. package/tests/test-console-command.mjs +56 -0
  386. package/tests/test-control-actor.mjs +425 -0
  387. package/tests/test-control-editor.mjs +80 -0
  388. package/tests/test-extra-tools.mjs +38 -0
  389. package/tests/test-graphql.mjs +322 -0
  390. package/tests/test-inspect.mjs +72 -0
  391. package/tests/test-landscape.mjs +60 -0
  392. package/tests/test-manage-asset.mjs +438 -0
  393. package/tests/test-manage-level.mjs +70 -0
  394. package/tests/test-materials.mjs +356 -0
  395. package/tests/test-niagara.mjs +185 -0
  396. package/tests/test-no-inline-python.mjs +122 -0
  397. package/tests/test-plugin-handshake.mjs +82 -0
  398. package/tests/test-render.mjs +33 -0
  399. package/tests/test-runner.mjs +933 -0
  400. package/tests/test-search-assets.mjs +66 -0
  401. package/tests/test-sequence.mjs +68 -0
  402. package/tests/test-system.mjs +57 -0
  403. package/tests/test-wasm.mjs +193 -0
  404. package/tests/test-world-partition.mjs +215 -0
  405. package/tsconfig.json +3 -3
  406. package/wasm/Cargo.lock +363 -0
  407. package/wasm/Cargo.toml +42 -0
  408. package/wasm/LICENSE +21 -0
  409. package/wasm/README.md +253 -0
  410. package/wasm/src/dependency_resolver.rs +377 -0
  411. package/wasm/src/lib.rs +153 -0
  412. package/wasm/src/property_parser.rs +271 -0
  413. package/wasm/src/transform_math.rs +396 -0
  414. package/wasm/tests/integration.rs +109 -0
  415. package/.github/workflows/smithery-build.yml +0 -29
  416. package/dist/tools/build_environment_advanced.d.ts +0 -65
  417. package/dist/tools/build_environment_advanced.js +0 -633
  418. package/dist/tools/rc.d.ts +0 -110
  419. package/dist/tools/rc.js +0 -437
  420. package/dist/tools/visual.d.ts +0 -40
  421. package/dist/tools/visual.js +0 -282
  422. package/dist/utils/http.d.ts +0 -6
  423. package/dist/utils/http.js +0 -151
  424. package/dist/utils/python-output.d.ts +0 -18
  425. package/dist/utils/python-output.js +0 -290
  426. package/dist/utils/python.d.ts +0 -2
  427. package/dist/utils/python.js +0 -4
  428. package/dist/utils/stdio-redirect.d.ts +0 -2
  429. package/dist/utils/stdio-redirect.js +0 -20
  430. package/docs/unreal-tool-test-cases.md +0 -574
  431. package/smithery.yaml +0 -29
  432. package/src/tools/build_environment_advanced.ts +0 -732
  433. package/src/tools/rc.ts +0 -515
  434. package/src/tools/visual.ts +0 -281
  435. package/src/utils/http.ts +0 -187
  436. package/src/utils/python-output.ts +0 -351
  437. package/src/utils/python.ts +0 -3
  438. package/src/utils/stdio-redirect.ts +0 -18
@@ -1,23 +1,17 @@
1
+ import { BaseTool } from './base-tool.js';
1
2
  import { toVec3Object, toRotObject } from '../utils/normalize.js';
2
- import { bestEffortInterpretedText, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
3
- export class EditorTools {
4
- bridge;
5
- constructor(bridge) {
6
- this.bridge = bridge;
7
- }
3
+ import { DEFAULT_SCREENSHOT_RESOLUTION } from '../constants.js';
4
+ export class EditorTools extends BaseTool {
5
+ cameraBookmarks = new Map();
6
+ editorPreferences = new Map();
7
+ activeRecording;
8
8
  async isInPIE() {
9
9
  try {
10
- const pythonCmd = `
11
- import unreal
12
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
13
- if les:
14
- print("PIE_STATE:" + str(les.is_in_play_in_editor()))
15
- else:
16
- print("PIE_STATE:False")
17
- `.trim();
18
- const resp = await this.bridge.executePython(pythonCmd);
19
- const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
20
- return out.includes('PIE_STATE:True');
10
+ const response = await this.sendAutomationRequest('check_pie_state', {}, { timeoutMs: 5000 });
11
+ if (response && response.success !== false) {
12
+ return response.isInPIE === true || response.result?.isInPIE === true;
13
+ }
14
+ return false;
21
15
  }
22
16
  catch {
23
17
  return false;
@@ -26,70 +20,26 @@ else:
26
20
  async ensureNotInPIE() {
27
21
  if (await this.isInPIE()) {
28
22
  await this.stopPlayInEditor();
29
- // Wait a bit for PIE to fully stop
30
23
  await new Promise(resolve => setTimeout(resolve, 500));
31
24
  }
32
25
  }
33
- async playInEditor() {
26
+ async playInEditor(timeoutMs = 30000) {
34
27
  try {
35
- // Set tick rate to match UI play (60 fps for game mode)
36
- await this.bridge.executeConsoleCommand('t.MaxFPS 60');
37
- // Try Python first using the modern LevelEditorSubsystem
38
28
  try {
39
- // Use LevelEditorSubsystem to play in the selected viewport (modern API)
40
- const pythonCmd = `
41
- import unreal, time, json
42
- # Start PIE using LevelEditorSubsystem (modern approach)
43
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
44
- if les:
45
- # Store initial state
46
- was_playing = les.is_in_play_in_editor()
47
-
48
- # Request PIE in the current viewport
49
- les.editor_play_simulate()
50
-
51
- # Wait for PIE to start with multiple checks
52
- max_attempts = 10
53
- for i in range(max_attempts):
54
- time.sleep(0.2) # Wait 200ms between checks
55
- is_playing = les.is_in_play_in_editor()
56
- if is_playing and not was_playing:
57
- # PIE has started
58
- print('RESULT:' + json.dumps({'success': True, 'method': 'LevelEditorSubsystem'}))
59
- break
60
- else:
61
- # If we've waited 2 seconds total and PIE hasn't started,
62
- # but the command was sent, assume it will start
63
- print('RESULT:' + json.dumps({'success': True, 'method': 'LevelEditorSubsystem'}))
64
- else:
65
- # If subsystem not available, report error
66
- print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
67
- `.trim();
68
- const resp = await this.bridge.executePython(pythonCmd);
69
- const interpreted = interpretStandardResult(resp, {
70
- successMessage: 'PIE started',
71
- failureMessage: 'Failed to start PIE'
72
- });
73
- if (interpreted.success) {
74
- const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
75
- return { success: true, message: `PIE started (via ${method})` };
29
+ const response = await this.sendAutomationRequest('control_editor', { action: 'play' }, { timeoutMs });
30
+ if (response && response.success === true) {
31
+ return { success: true, message: response.message || 'PIE started' };
76
32
  }
77
- // If not verified, fall through to fallback
33
+ return { success: false, error: response?.error || response?.message || 'Failed to start PIE' };
78
34
  }
79
35
  catch (err) {
80
- // Log the error for debugging but continue
81
- console.error('Python PIE start issue:', err);
36
+ if (err.message && /time.*out/i.test(err.message)) {
37
+ return { success: false, error: `Timeout waiting for PIE to start: ${err.message}` };
38
+ }
39
+ await this.bridge.executeConsoleCommand('t.MaxFPS 60');
40
+ await this.bridge.executeConsoleCommand('PlayInViewport');
41
+ return { success: true, message: 'PIE start command sent' };
82
42
  }
83
- // Fallback to console command which is more reliable
84
- await this.bridge.executeConsoleCommand('PlayInViewport');
85
- // Wait a moment and verify PIE started
86
- await new Promise(resolve => setTimeout(resolve, 1000));
87
- // Check if PIE is now active
88
- const isPlaying = await this.isInPIE();
89
- return {
90
- success: true,
91
- message: isPlaying ? 'PIE started successfully' : 'PIE start command sent (may take a moment)'
92
- };
93
43
  }
94
44
  catch (err) {
95
45
  return { success: false, error: `Failed to start PIE: ${err}` };
@@ -97,35 +47,20 @@ else:
97
47
  }
98
48
  async stopPlayInEditor() {
99
49
  try {
100
- // Try Python first using the modern LevelEditorSubsystem
101
50
  try {
102
- const pythonCmd = `
103
- import unreal, time, json
104
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
105
- if les:
106
- # Use correct method name for stopping PIE
107
- les.editor_request_end_play() # Modern API method
108
- print('RESULT:' + json.dumps({'success': True, 'method': 'LevelEditorSubsystem'}))
109
- else:
110
- # If subsystem not available, report error
111
- print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
112
- `.trim();
113
- const resp = await this.bridge.executePython(pythonCmd);
114
- const interpreted = interpretStandardResult(resp, {
115
- successMessage: 'PIE stopped successfully',
116
- failureMessage: 'Failed to stop PIE'
117
- });
118
- if (interpreted.success) {
119
- const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
120
- return { success: true, message: `PIE stopped via ${method}` };
121
- }
122
- if (interpreted.error) {
123
- return { success: false, error: interpreted.error };
51
+ const response = await this.sendAutomationRequest('control_editor', { action: 'stop' }, { timeoutMs: 30000 });
52
+ if (response.success !== false) {
53
+ return {
54
+ success: true,
55
+ message: response.message || 'PIE stopped successfully'
56
+ };
124
57
  }
125
- return { success: false, error: 'Failed to stop PIE' };
58
+ return {
59
+ success: false,
60
+ error: response.error || response.message || 'Failed to stop PIE'
61
+ };
126
62
  }
127
- catch {
128
- // Fallback to console command
63
+ catch (_pluginErr) {
129
64
  await this.bridge.executeConsoleCommand('stop');
130
65
  return { success: true, message: 'PIE stopped via console command' };
131
66
  }
@@ -136,69 +71,44 @@ else:
136
71
  }
137
72
  async pausePlayInEditor() {
138
73
  try {
139
- // Pause/Resume PIE
140
- await this.bridge.httpCall('/remote/object/call', 'PUT', {
141
- objectPath: '/Script/Engine.Default__KismetSystemLibrary',
142
- functionName: 'ExecuteConsoleCommand',
143
- parameters: {
144
- WorldContextObject: null,
145
- Command: 'pause',
146
- SpecificPlayer: null
147
- },
148
- generateTransaction: false
149
- });
74
+ await this.bridge.executeConsoleCommand('pause');
150
75
  return { success: true, message: 'PIE paused/resumed' };
151
76
  }
152
77
  catch (err) {
153
78
  return { success: false, error: `Failed to pause PIE: ${err}` };
154
79
  }
155
80
  }
156
- // Alias for consistency with naming convention
157
81
  async pauseInEditor() {
158
82
  return this.pausePlayInEditor();
159
83
  }
160
84
  async buildLighting() {
161
85
  try {
162
- // Use modern LevelEditorSubsystem to build lighting
163
- const py = `
164
- import unreal
165
- import json
166
- try:
167
- # Use modern LevelEditorSubsystem API
168
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
169
- if les:
170
- # build_light_maps(quality, with_reflection_captures)
171
- les.build_light_maps(unreal.LightingBuildQuality.QUALITY_HIGH, True)
172
- print('RESULT:' + json.dumps({'success': True, 'message': 'Lighting build started via LevelEditorSubsystem'}))
173
- else:
174
- # If subsystem not available, report error
175
- print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
176
- except Exception as e:
177
- print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
178
- `.trim();
179
- const resp = await this.bridge.executePython(py);
180
- const interpreted = interpretStandardResult(resp, {
181
- successMessage: 'Lighting build started',
182
- failureMessage: 'Failed to build lighting'
183
- });
184
- if (interpreted.success) {
185
- return { success: true, message: interpreted.message };
186
- }
187
- return {
188
- success: false,
189
- error: interpreted.error ?? 'Failed to build lighting',
190
- details: bestEffortInterpretedText(interpreted)
191
- };
86
+ await this.bridge.executeConsoleCommand('BuildLighting');
87
+ return { success: true, message: 'Lighting build started' };
192
88
  }
193
89
  catch (err) {
194
90
  return { success: false, error: `Failed to build lighting: ${err}` };
195
91
  }
196
92
  }
93
+ async getViewportCameraInfo() {
94
+ try {
95
+ const resp = await this.sendAutomationRequest('control_editor', { action: 'get_camera' }, { timeoutMs: 3000 });
96
+ const result = resp?.result ?? resp;
97
+ const loc = result?.location ?? result?.camera?.location;
98
+ const rot = result?.rotation ?? result?.camera?.rotation;
99
+ const locArr = Array.isArray(loc) && loc.length === 3 ? [Number(loc[0]) || 0, Number(loc[1]) || 0, Number(loc[2]) || 0] : undefined;
100
+ const rotArr = Array.isArray(rot) && rot.length === 3 ? [Number(rot[0]) || 0, Number(rot[1]) || 0, Number(rot[2]) || 0] : undefined;
101
+ if (resp && resp.success !== false && locArr && rotArr) {
102
+ return { success: true, location: locArr, rotation: rotArr };
103
+ }
104
+ return { success: false, error: 'Failed to get camera information' };
105
+ }
106
+ catch (err) {
107
+ return { success: false, error: `Camera query failed: ${err}` };
108
+ }
109
+ }
197
110
  async setViewportCamera(location, rotation) {
198
- // Special handling for when both location and rotation are missing/invalid
199
- // Allow rotation-only updates
200
111
  if (location === null) {
201
- // Explicit null is not allowed for location
202
112
  throw new Error('Invalid location: null is not allowed');
203
113
  }
204
114
  if (location !== undefined && location !== null) {
@@ -206,14 +116,12 @@ except Exception as e:
206
116
  if (!locObj) {
207
117
  throw new Error('Invalid location: must be {x,y,z} or [x,y,z]');
208
118
  }
209
- // Clamp extreme values to reasonable limits for Unreal Engine
210
- const MAX_COORD = 1000000; // 1 million units is a reasonable max for UE
119
+ const MAX_COORD = 1000000;
211
120
  locObj.x = Math.max(-MAX_COORD, Math.min(MAX_COORD, locObj.x));
212
121
  locObj.y = Math.max(-MAX_COORD, Math.min(MAX_COORD, locObj.y));
213
122
  locObj.z = Math.max(-MAX_COORD, Math.min(MAX_COORD, locObj.z));
214
123
  location = locObj;
215
124
  }
216
- // Validate rotation if provided
217
125
  if (rotation !== undefined) {
218
126
  if (rotation === null) {
219
127
  throw new Error('Invalid rotation: null is not allowed');
@@ -222,107 +130,29 @@ except Exception as e:
222
130
  if (!rotObj) {
223
131
  throw new Error('Invalid rotation: must be {pitch,yaw,roll} or [pitch,yaw,roll]');
224
132
  }
225
- // Normalize rotation values to 0-360 range
226
133
  rotObj.pitch = ((rotObj.pitch % 360) + 360) % 360;
227
134
  rotObj.yaw = ((rotObj.yaw % 360) + 360) % 360;
228
135
  rotObj.roll = ((rotObj.roll % 360) + 360) % 360;
229
136
  rotation = rotObj;
230
137
  }
231
138
  try {
232
- // Try Python for actual viewport camera positioning
233
- // Only proceed if we have a valid location
234
- if (location) {
235
- try {
236
- const rot = rotation || { pitch: 0, yaw: 0, roll: 0 };
237
- const pythonCmd = `
238
- import unreal
239
- # Use UnrealEditorSubsystem instead of deprecated EditorLevelLibrary
240
- ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
241
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
242
- location = unreal.Vector(${location.x}, ${location.y}, ${location.z})
243
- rotation = unreal.Rotator(${rot.pitch}, ${rot.yaw}, ${rot.roll})
244
- if ues:
245
- ues.set_level_viewport_camera_info(location, rotation)
246
- # Invalidate viewports to ensure visual update
247
- try:
248
- if les:
249
- les.editor_invalidate_viewports()
250
- except Exception:
251
- pass
252
- `.trim();
253
- await this.bridge.executePython(pythonCmd);
254
- return {
255
- success: true,
256
- message: 'Viewport camera positioned via UnrealEditorSubsystem'
257
- };
258
- }
259
- catch {
260
- // Fallback to camera speed control
261
- await this.bridge.executeConsoleCommand('camspeed 4');
262
- return {
263
- success: true,
264
- message: 'Camera speed set. Use debug camera (toggledebugcamera) for manual positioning'
265
- };
266
- }
267
- }
268
- else if (rotation) {
269
- // Only rotation provided, try to set just rotation
270
- try {
271
- const pythonCmd = `
272
- import unreal
273
- # Use UnrealEditorSubsystem to read/write viewport camera
274
- ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
275
- les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
276
- rotation = unreal.Rotator(${rotation.pitch}, ${rotation.yaw}, ${rotation.roll})
277
- if ues:
278
- info = ues.get_level_viewport_camera_info()
279
- if info is not None:
280
- current_location, _ = info
281
- ues.set_level_viewport_camera_info(current_location, rotation)
282
- try:
283
- if les:
284
- les.editor_invalidate_viewports()
285
- except Exception:
286
- pass
287
- `.trim();
288
- await this.bridge.executePython(pythonCmd);
289
- return {
290
- success: true,
291
- message: 'Viewport camera rotation set via UnrealEditorSubsystem'
292
- };
293
- }
294
- catch {
295
- // Fallback
296
- return {
297
- success: true,
298
- message: 'Camera rotation update attempted'
299
- };
300
- }
301
- }
302
- else {
303
- // Neither location nor rotation provided - this is valid, just no-op
304
- return {
305
- success: true,
306
- message: 'No camera changes requested'
307
- };
139
+ const resp = await this.sendAutomationRequest('control_editor', {
140
+ action: 'set_camera',
141
+ location: location,
142
+ rotation: rotation
143
+ }, { timeoutMs: 10000 });
144
+ if (resp && resp.success === true) {
145
+ return { success: true, message: resp.message || 'Camera set', location, rotation };
308
146
  }
147
+ return { success: false, error: resp?.error || resp?.message || 'Failed to set camera' };
309
148
  }
310
149
  catch (err) {
311
- return { success: false, error: `Failed to set camera: ${err}` };
150
+ return { success: false, error: `Camera control failed: ${err}` };
312
151
  }
313
152
  }
314
153
  async setCameraSpeed(speed) {
315
154
  try {
316
- await this.bridge.httpCall('/remote/object/call', 'PUT', {
317
- objectPath: '/Script/Engine.Default__KismetSystemLibrary',
318
- functionName: 'ExecuteConsoleCommand',
319
- parameters: {
320
- WorldContextObject: null,
321
- Command: `camspeed ${speed}`,
322
- SpecificPlayer: null
323
- },
324
- generateTransaction: false
325
- });
155
+ await this.bridge.executeConsoleCommand(`camspeed ${speed}`);
326
156
  return { success: true, message: `Camera speed set to ${speed}` };
327
157
  }
328
158
  catch (err) {
@@ -331,21 +161,186 @@ if ues:
331
161
  }
332
162
  async setFOV(fov) {
333
163
  try {
334
- await this.bridge.httpCall('/remote/object/call', 'PUT', {
335
- objectPath: '/Script/Engine.Default__KismetSystemLibrary',
336
- functionName: 'ExecuteConsoleCommand',
337
- parameters: {
338
- WorldContextObject: null,
339
- Command: `fov ${fov}`,
340
- SpecificPlayer: null
341
- },
342
- generateTransaction: false
343
- });
164
+ await this.bridge.executeConsoleCommand(`fov ${fov}`);
344
165
  return { success: true, message: `FOV set to ${fov}` };
345
166
  }
346
167
  catch (err) {
347
168
  return { success: false, error: `Failed to set FOV: ${err}` };
348
169
  }
349
170
  }
171
+ async takeScreenshot(filename, resolution) {
172
+ try {
173
+ if (resolution && !/^\d+x\d+$/.test(resolution)) {
174
+ return { success: false, error: 'Invalid resolution format. Use WxH (e.g. 1920x1080)' };
175
+ }
176
+ const sanitizedFilename = filename ? filename.replace(/[<>:*?"|]/g, '_') : `Screenshot_${Date.now()}`;
177
+ const resString = resolution || DEFAULT_SCREENSHOT_RESOLUTION;
178
+ const command = filename ? `highresshot ${resString} filename="${sanitizedFilename}"` : 'shot';
179
+ await this.bridge.executeConsoleCommand(command);
180
+ return {
181
+ success: true,
182
+ message: `Screenshot captured: ${sanitizedFilename}`,
183
+ filename: sanitizedFilename,
184
+ command
185
+ };
186
+ }
187
+ catch (err) {
188
+ return { success: false, error: `Failed to take screenshot: ${err}` };
189
+ }
190
+ }
191
+ async resumePlayInEditor() {
192
+ try {
193
+ await this.bridge.executeConsoleCommand('pause');
194
+ return {
195
+ success: true,
196
+ message: 'PIE resume toggled via pause command'
197
+ };
198
+ }
199
+ catch (err) {
200
+ return { success: false, error: `Failed to resume PIE: ${err}` };
201
+ }
202
+ }
203
+ async stepPIEFrame(steps = 1) {
204
+ const clampedSteps = Number.isFinite(steps) ? Math.max(1, Math.floor(steps)) : 1;
205
+ try {
206
+ for (let index = 0; index < clampedSteps; index += 1) {
207
+ await this.bridge.executeConsoleCommand('Step=1');
208
+ }
209
+ return {
210
+ success: true,
211
+ message: `Advanced PIE by ${clampedSteps} frame(s)`,
212
+ steps: clampedSteps
213
+ };
214
+ }
215
+ catch (err) {
216
+ return { success: false, error: `Failed to step PIE: ${err}` };
217
+ }
218
+ }
219
+ async startRecording(options) {
220
+ const startedAt = Date.now();
221
+ this.activeRecording = {
222
+ name: typeof options?.filename === 'string' ? options.filename.trim() : undefined,
223
+ options: options ? { ...options } : undefined,
224
+ startedAt
225
+ };
226
+ return {
227
+ success: true,
228
+ message: 'Recording session started',
229
+ recording: {
230
+ name: this.activeRecording.name,
231
+ startedAt,
232
+ options: this.activeRecording.options
233
+ }
234
+ };
235
+ }
236
+ async stopRecording() {
237
+ if (!this.activeRecording) {
238
+ return {
239
+ success: true,
240
+ message: 'No active recording session to stop'
241
+ };
242
+ }
243
+ const stoppedRecording = this.activeRecording;
244
+ this.activeRecording = undefined;
245
+ return {
246
+ success: true,
247
+ message: 'Recording session stopped',
248
+ recording: stoppedRecording
249
+ };
250
+ }
251
+ async createCameraBookmark(name) {
252
+ const trimmedName = name.trim();
253
+ if (!trimmedName) {
254
+ return { success: false, error: 'bookmarkName is required' };
255
+ }
256
+ const cameraInfo = await this.getViewportCameraInfo();
257
+ if (!cameraInfo.success || !cameraInfo.location || !cameraInfo.rotation) {
258
+ return {
259
+ success: false,
260
+ error: cameraInfo.error || 'Failed to capture viewport camera'
261
+ };
262
+ }
263
+ this.cameraBookmarks.set(trimmedName, {
264
+ location: cameraInfo.location,
265
+ rotation: cameraInfo.rotation,
266
+ savedAt: Date.now()
267
+ });
268
+ return {
269
+ success: true,
270
+ message: `Bookmark '${trimmedName}' saved`,
271
+ bookmark: {
272
+ name: trimmedName,
273
+ location: cameraInfo.location,
274
+ rotation: cameraInfo.rotation
275
+ }
276
+ };
277
+ }
278
+ async jumpToCameraBookmark(name) {
279
+ const trimmedName = name.trim();
280
+ if (!trimmedName) {
281
+ return { success: false, error: 'bookmarkName is required' };
282
+ }
283
+ const bookmark = this.cameraBookmarks.get(trimmedName);
284
+ if (!bookmark) {
285
+ return {
286
+ success: false,
287
+ error: `Bookmark '${trimmedName}' not found`
288
+ };
289
+ }
290
+ await this.setViewportCamera({ x: bookmark.location[0], y: bookmark.location[1], z: bookmark.location[2] }, { pitch: bookmark.rotation[0], yaw: bookmark.rotation[1], roll: bookmark.rotation[2] });
291
+ return {
292
+ success: true,
293
+ message: `Jumped to bookmark '${trimmedName}'`
294
+ };
295
+ }
296
+ async setEditorPreferences(category, preferences) {
297
+ const resolvedCategory = typeof category === 'string' && category.trim().length > 0 ? category.trim() : 'General';
298
+ const existing = this.editorPreferences.get(resolvedCategory) ?? {};
299
+ this.editorPreferences.set(resolvedCategory, { ...existing, ...preferences });
300
+ return {
301
+ success: true,
302
+ message: `Preferences stored for ${resolvedCategory}`,
303
+ preferences: this.editorPreferences.get(resolvedCategory)
304
+ };
305
+ }
306
+ async setViewportResolution(width, height) {
307
+ try {
308
+ const clampedWidth = Math.max(320, Math.min(7680, width));
309
+ const clampedHeight = Math.max(240, Math.min(4320, height));
310
+ const command = `r.SetRes ${clampedWidth}x${clampedHeight}`;
311
+ await this.bridge.executeConsoleCommand(command);
312
+ return {
313
+ success: true,
314
+ message: `Viewport resolution set to ${clampedWidth}x${clampedHeight}`,
315
+ width: clampedWidth,
316
+ height: clampedHeight
317
+ };
318
+ }
319
+ catch (err) {
320
+ return { success: false, error: `Failed to set viewport resolution: ${err}` };
321
+ }
322
+ }
323
+ async executeConsoleCommand(command) {
324
+ try {
325
+ if (!command || typeof command !== 'string') {
326
+ return { success: false, error: 'Invalid command: must be a non-empty string' };
327
+ }
328
+ if (command.length > 1000) {
329
+ return {
330
+ success: false,
331
+ error: `Command too long (${command.length} chars). Maximum is 1000 characters.`
332
+ };
333
+ }
334
+ const res = await this.bridge.executeConsoleCommand(command);
335
+ return {
336
+ success: true,
337
+ message: `Console command executed: ${command}`,
338
+ output: res
339
+ };
340
+ }
341
+ catch (err) {
342
+ return { success: false, error: `Failed to execute console command: ${err}` };
343
+ }
344
+ }
350
345
  }
351
346
  //# sourceMappingURL=editor.js.map
@@ -1,8 +1,10 @@
1
1
  import { UnrealBridge } from '../unreal-bridge.js';
2
+ import { AutomationBridge } from '../automation/index.js';
2
3
  export declare class EngineTools {
3
- private bridge;
4
+ private automationBridge?;
4
5
  private env;
5
- constructor(bridge: UnrealBridge);
6
+ constructor(_bridge: UnrealBridge, automationBridge?: AutomationBridge | undefined);
7
+ setAutomationBridge(automationBridge?: AutomationBridge): void;
6
8
  launchEditor(params?: {
7
9
  editorExe?: string;
8
10
  projectPath?: string;
@@ -19,12 +21,16 @@ export declare class EngineTools {
19
21
  }>;
20
22
  quitEditor(): Promise<{
21
23
  success: boolean;
24
+ error: string;
22
25
  message: string;
23
- error?: undefined;
24
26
  } | {
25
27
  success: boolean;
26
- error: string;
28
+ error: any;
27
29
  message?: undefined;
30
+ } | {
31
+ success: boolean;
32
+ message: string;
33
+ error?: undefined;
28
34
  }>;
29
35
  }
30
36
  //# sourceMappingURL=engine.d.ts.map
@@ -1,10 +1,13 @@
1
1
  import { loadEnv } from '../types/env.js';
2
2
  import { spawn } from 'child_process';
3
3
  export class EngineTools {
4
- bridge;
4
+ automationBridge;
5
5
  env = loadEnv();
6
- constructor(bridge) {
7
- this.bridge = bridge;
6
+ constructor(_bridge, automationBridge) {
7
+ this.automationBridge = automationBridge;
8
+ }
9
+ setAutomationBridge(automationBridge) {
10
+ this.automationBridge = automationBridge;
8
11
  }
9
12
  async launchEditor(params) {
10
13
  const exe = params?.editorExe || this.env.UE_EDITOR_EXE;
@@ -23,9 +26,14 @@ export class EngineTools {
23
26
  }
24
27
  }
25
28
  async quitEditor() {
29
+ if (!this.automationBridge) {
30
+ return { success: false, error: 'AUTOMATION_BRIDGE_UNAVAILABLE', message: 'Automation bridge is not available for quit_editor' };
31
+ }
26
32
  try {
27
- // Use Python SystemLibrary.quit_editor if available
28
- await this.bridge.executePython('import unreal; unreal.SystemLibrary.quit_editor()');
33
+ const resp = await this.automationBridge.sendAutomationRequest('quit_editor', {});
34
+ if (resp && resp.success === false) {
35
+ return { success: false, error: resp.error || resp.message || 'Quit request failed' };
36
+ }
29
37
  return { success: true, message: 'Quit command sent' };
30
38
  }
31
39
  catch (err) {