unreal-engine-mcp-server 0.4.6 → 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 +269 -22
  29. package/CONTRIBUTING.md +140 -0
  30. package/README.md +166 -72
  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 -604
  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 +5475 -1627
  97. package/dist/tools/consolidated-tool-definitions.js +829 -482
  98. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  99. package/dist/tools/consolidated-tool-handlers.js +211 -1009
  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 +45 -0
  161. package/dist/tools/logs.js +210 -0
  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 +195 -11
  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 -649
  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 -500
  316. package/src/tools/consolidated-tool-handlers.ts +272 -1122
  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 +219 -0
  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 +250 -13
  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 -572
  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,6 +1,7 @@
1
1
  import Ajv from 'ajv';
2
2
  import { Logger } from './logger.js';
3
3
  import { cleanObject } from './safe-json.js';
4
+ import { wasmIntegration } from '../wasm/index.js';
4
5
  const log = new Logger('ResponseValidator');
5
6
  function isRecord(value) {
6
7
  return !!value && typeof value === 'object' && !Array.isArray(value);
@@ -19,71 +20,106 @@ function buildSummaryText(toolName, payload) {
19
20
  if (!isRecord(payload)) {
20
21
  return `${toolName} responded`;
21
22
  }
23
+ const effectivePayload = { ...payload };
24
+ if (isRecord(effectivePayload.data)) {
25
+ Object.assign(effectivePayload, effectivePayload.data);
26
+ }
27
+ if (isRecord(effectivePayload.result)) {
28
+ Object.assign(effectivePayload, effectivePayload.result);
29
+ }
22
30
  const parts = [];
23
- const message = typeof payload.message === 'string' ? normalizeText(payload.message) : '';
24
- const error = typeof payload.error === 'string' ? normalizeText(payload.error) : '';
25
- const success = typeof payload.success === 'boolean' ? (payload.success ? 'success' : 'failed') : '';
26
- const path = typeof payload.path === 'string' ? payload.path : '';
27
- const name = typeof payload.name === 'string' ? payload.name : '';
28
- const warningCount = Array.isArray(payload.warnings) ? payload.warnings.length : 0;
29
- if (message)
30
- parts.push(message);
31
- if (error && (!message || !message.includes(error)))
32
- parts.push(`error: ${error}`);
33
- if (success)
34
- parts.push(success);
35
- if (path)
36
- parts.push(`path: ${path}`);
37
- if (name)
38
- parts.push(`name: ${name}`);
39
- if (warningCount > 0)
40
- parts.push(`warnings: ${warningCount}`);
41
- const summary = isRecord(payload.summary) ? payload.summary : undefined;
42
- if (summary) {
43
- const summaryParts = [];
44
- for (const [key, value] of Object.entries(summary)) {
45
- if (typeof value === 'number' || typeof value === 'string') {
46
- summaryParts.push(`${key}: ${value}`);
31
+ const listKeys = ['actors', 'levels', 'assets', 'folders', 'blueprints', 'components', 'pawnClasses', 'foliageTypes', 'nodes', 'tracks', 'bindings', 'keys'];
32
+ for (const key of listKeys) {
33
+ if (Array.isArray(effectivePayload[key])) {
34
+ const arr = effectivePayload[key];
35
+ const names = arr.map(i => isRecord(i) ? (i.name || i.path || i.id || i.assetName || i.objectPath || i.packageName || i.nodeName || '<?>') : String(i));
36
+ const count = arr.length;
37
+ const preview = names.slice(0, 100).join(', ');
38
+ const suffix = count > 100 ? `, ... (+${count - 100} more)` : '';
39
+ parts.push(`${key}: ${preview}${suffix} (Total: ${count})`);
40
+ }
41
+ }
42
+ if (typeof effectivePayload.actor === 'string' || isRecord(effectivePayload.actor)) {
43
+ const a = effectivePayload.actor;
44
+ const name = isRecord(a) ? (a.name || a.path) : a;
45
+ const loc = isRecord(effectivePayload.location) ? ` at [${effectivePayload.location.x},${effectivePayload.location.y},${effectivePayload.location.z}]` : '';
46
+ parts.push(`Actor: ${name}${loc}`);
47
+ }
48
+ if (typeof effectivePayload.asset === 'string' || isRecord(effectivePayload.asset)) {
49
+ const a = effectivePayload.asset;
50
+ const path = isRecord(a) ? (a.path || a.name) : a;
51
+ parts.push(`Asset: ${path}`);
52
+ }
53
+ if (typeof effectivePayload.blueprint === 'string' || isRecord(effectivePayload.blueprint)) {
54
+ const bp = effectivePayload.blueprint;
55
+ const name = isRecord(bp) ? (bp.name || bp.path || effectivePayload.blueprintPath) : bp;
56
+ parts.push(`Blueprint: ${name}`);
57
+ }
58
+ if (typeof effectivePayload.sequence === 'string' || isRecord(effectivePayload.sequence)) {
59
+ const seq = effectivePayload.sequence;
60
+ const name = isRecord(seq) ? (seq.name || seq.path) : seq;
61
+ parts.push(`Sequence: ${name}`);
62
+ }
63
+ const usefulKeys = [
64
+ 'success', 'error', 'message', 'assets', 'folders', 'count', 'totalCount',
65
+ 'saved', 'valid', 'issues', 'class', 'skeleton', 'parent',
66
+ 'package', 'dependencies', 'graph', 'tags', 'metadata', 'properties'
67
+ ];
68
+ for (const key of usefulKeys) {
69
+ if (effectivePayload[key] !== undefined && effectivePayload[key] !== null) {
70
+ const val = effectivePayload[key];
71
+ if (typeof val === 'object') {
72
+ if (key === 'metadata' || key === 'properties' || key === 'tags') {
73
+ const entries = Object.entries(val);
74
+ const formatted = entries.map(([k, v]) => `${k}=${v}`);
75
+ const limit = 50;
76
+ parts.push(`${key}: { ${formatted.slice(0, limit).join(', ')}${formatted.length > limit ? '...' : ''} }`);
77
+ continue;
78
+ }
79
+ continue;
80
+ }
81
+ const strVal = String(val);
82
+ if (strVal.length > 100)
83
+ continue;
84
+ if (!parts.some(p => p.includes(strVal))) {
85
+ parts.push(`${key}: ${strVal}`);
47
86
  }
48
- if (summaryParts.length >= 3)
49
- break;
50
87
  }
51
- if (summaryParts.length) {
52
- parts.push(`summary(${summaryParts.join(', ')})`);
88
+ }
89
+ const success = typeof payload.success === 'boolean' ? payload.success : undefined;
90
+ const message = typeof payload.message === 'string' ? normalizeText(payload.message) : '';
91
+ const error = typeof payload.error === 'string' ? normalizeText(payload.error) : '';
92
+ if (parts.length > 0) {
93
+ if (message && message.toLowerCase() !== 'success') {
94
+ parts.push(message);
53
95
  }
54
96
  }
55
- if (parts.length === 0) {
56
- const keys = Object.keys(payload).slice(0, 3);
57
- if (keys.length) {
58
- return `${toolName} responded (${keys.join(', ')})`;
97
+ else {
98
+ if (message)
99
+ parts.push(message);
100
+ if (error)
101
+ parts.push(`Error: ${error}`);
102
+ if (parts.length === 0 && success !== undefined) {
103
+ parts.push(success ? 'Success' : 'Failed');
59
104
  }
60
105
  }
61
- return parts.length > 0
62
- ? parts.join(' | ')
63
- : `${toolName} responded`;
106
+ const warnings = Array.isArray(payload.warnings) ? payload.warnings : [];
107
+ if (warnings.length > 0) {
108
+ parts.push(`Warnings: ${warnings.length}`);
109
+ }
110
+ return parts.length > 0 ? parts.join(' | ') : `${toolName} responded`;
64
111
  }
65
- /**
66
- * Response Validator for MCP Tool Outputs
67
- * Validates tool responses against their defined output schemas
68
- */
69
112
  export class ResponseValidator {
70
- // Keep ajv as any to avoid complex interop typing issues with Ajv's ESM/CJS dual export
71
- // shape when using NodeNext module resolution.
72
113
  ajv;
73
114
  validators = new Map();
74
115
  constructor() {
75
- // Cast Ajv to any for construction to avoid errors when TypeScript's NodeNext
76
- // module resolution represents the import as a namespace object.
77
116
  const AjvCtor = Ajv?.default ?? Ajv;
78
117
  this.ajv = new AjvCtor({
79
118
  allErrors: true,
80
119
  verbose: true,
81
- strict: false // Allow additional properties for flexibility
120
+ strict: true
82
121
  });
83
122
  }
84
- /**
85
- * Register a tool's output schema for validation
86
- */
87
123
  registerSchema(toolName, outputSchema) {
88
124
  if (!outputSchema) {
89
125
  log.warn(`No output schema defined for tool: ${toolName}`);
@@ -92,35 +128,34 @@ export class ResponseValidator {
92
128
  try {
93
129
  const validator = this.ajv.compile(outputSchema);
94
130
  this.validators.set(toolName, validator);
95
- // Demote per-tool schema registration to debug to reduce log noise
96
131
  log.debug(`Registered output schema for tool: ${toolName}`);
97
132
  }
98
133
  catch (_error) {
99
134
  log.error(`Failed to compile output schema for ${toolName}:`, _error);
100
135
  }
101
136
  }
102
- /**
103
- * Validate a tool's response against its schema
104
- */
105
- validateResponse(toolName, response) {
137
+ async validateResponse(toolName, response) {
106
138
  const validator = this.validators.get(toolName);
107
139
  if (!validator) {
108
140
  log.debug(`No validator found for tool: ${toolName}`);
109
- return { valid: true }; // Pass through if no schema defined
141
+ return { valid: true };
110
142
  }
111
- // Extract structured content from response
112
143
  let structuredContent = response;
113
- // If response has MCP format with content array
114
144
  if (response.content && Array.isArray(response.content)) {
115
- // Try to extract structured data from text content
116
145
  const textContent = response.content.find((c) => c.type === 'text');
117
146
  if (textContent?.text) {
118
- try {
119
- // Check if text is JSON
120
- structuredContent = JSON.parse(textContent.text);
147
+ const rawText = String(textContent.text);
148
+ const trimmed = rawText.trim();
149
+ const looksLikeJson = trimmed.startsWith('{') || trimmed.startsWith('[');
150
+ if (looksLikeJson) {
151
+ try {
152
+ structuredContent = await wasmIntegration.parseProperties(rawText);
153
+ }
154
+ catch {
155
+ structuredContent = response;
156
+ }
121
157
  }
122
- catch {
123
- // Not JSON, use the full response
158
+ else {
124
159
  structuredContent = response;
125
160
  }
126
161
  }
@@ -140,16 +175,7 @@ export class ResponseValidator {
140
175
  structuredContent
141
176
  };
142
177
  }
143
- /**
144
- * Wrap a tool response with validation and MCP-compliant content shape.
145
- *
146
- * MCP tools/call responses must contain a `content` array. Many internal
147
- * handlers return structured JSON objects (e.g., { success, message, ... }).
148
- * This wrapper serializes such objects into a single text block while keeping
149
- * existing `content` responses intact.
150
- */
151
- wrapResponse(toolName, response) {
152
- // Ensure response is safe to serialize first
178
+ async wrapResponse(toolName, response) {
153
179
  try {
154
180
  if (response && typeof response === 'object') {
155
181
  JSON.stringify(response);
@@ -159,16 +185,12 @@ export class ResponseValidator {
159
185
  log.error(`Response for ${toolName} contains circular references, cleaning...`);
160
186
  response = cleanObject(response);
161
187
  }
162
- // If handler already returned MCP content, keep it as-is (still validate)
163
188
  const alreadyMcpShaped = response && typeof response === 'object' && Array.isArray(response.content);
164
- // Choose the payload to validate: if already MCP-shaped, validate the
165
- // structured content extracted from text; otherwise validate the object directly.
166
- const validation = this.validateResponse(toolName, response);
189
+ const validation = await this.validateResponse(toolName, response);
167
190
  const structuredPayload = validation.structuredContent;
168
191
  if (!validation.valid) {
169
192
  log.warn(`Tool ${toolName} response validation failed:`, validation.errors);
170
193
  }
171
- // If it's already MCP-shaped, return as-is (optionally append validation meta)
172
194
  if (alreadyMcpShaped) {
173
195
  if (structuredPayload !== undefined && response && typeof response === 'object' && response.structuredContent === undefined) {
174
196
  try {
@@ -178,6 +200,14 @@ export class ResponseValidator {
178
200
  }
179
201
  catch { }
180
202
  }
203
+ try {
204
+ const sc = response.structuredContent || structuredPayload || {};
205
+ const hasExplicitFailure = (typeof sc.success === 'boolean' && sc.success === false) || (typeof sc.error === 'string' && sc.error.length > 0);
206
+ if (hasExplicitFailure && response.isError !== true) {
207
+ response.isError = true;
208
+ }
209
+ }
210
+ catch { }
181
211
  if (!validation.valid) {
182
212
  try {
183
213
  response._validation = { valid: false, errors: validation.errors };
@@ -186,7 +216,6 @@ export class ResponseValidator {
186
216
  }
187
217
  return response;
188
218
  }
189
- // Otherwise, wrap structured result into MCP content
190
219
  const summarySource = structuredPayload !== undefined ? structuredPayload : response;
191
220
  let text = buildSummaryText(toolName, summarySource);
192
221
  if (!text || !text.trim()) {
@@ -197,6 +226,15 @@ export class ResponseValidator {
197
226
  { type: 'text', text }
198
227
  ]
199
228
  };
229
+ try {
230
+ if (structuredPayload && typeof structuredPayload.success === 'boolean') {
231
+ wrapped.success = Boolean(structuredPayload.success);
232
+ }
233
+ else if (response && typeof response.success === 'boolean') {
234
+ wrapped.success = Boolean(response.success);
235
+ }
236
+ }
237
+ catch { }
200
238
  if (structuredPayload !== undefined) {
201
239
  try {
202
240
  wrapped.structuredContent = structuredPayload && typeof structuredPayload === 'object'
@@ -215,14 +253,26 @@ export class ResponseValidator {
215
253
  wrapped.structuredContent = response;
216
254
  }
217
255
  }
256
+ try {
257
+ const sc = wrapped.structuredContent || {};
258
+ const hasExplicitFailure = (typeof sc.success === 'boolean' && sc.success === false) || (typeof sc.error === 'string' && sc.error.length > 0);
259
+ if (hasExplicitFailure) {
260
+ wrapped.isError = true;
261
+ }
262
+ }
263
+ catch { }
218
264
  if (!validation.valid) {
219
265
  wrapped._validation = { valid: false, errors: validation.errors };
220
266
  }
267
+ try {
268
+ const s = wrapped.success;
269
+ if (typeof s === 'boolean' && s === false) {
270
+ wrapped.isError = true;
271
+ }
272
+ }
273
+ catch { }
221
274
  return wrapped;
222
275
  }
223
- /**
224
- * Get validation statistics
225
- */
226
276
  getStats() {
227
277
  return {
228
278
  totalSchemas: this.validators.size,
@@ -230,6 +280,5 @@ export class ResponseValidator {
230
280
  };
231
281
  }
232
282
  }
233
- // Singleton instance
234
283
  export const responseValidator = new ResponseValidator();
235
284
  //# sourceMappingURL=response-validator.js.map
@@ -1,11 +1,10 @@
1
- import { StandardResultPayload } from './python-output.js';
2
1
  export interface InterpretedStandardResult {
3
2
  success: boolean;
4
3
  message: string;
5
4
  error?: string;
6
5
  warnings?: string[];
7
6
  details?: string[];
8
- payload: StandardResultPayload & Record<string, unknown>;
7
+ payload: Record<string, unknown>;
9
8
  cleanText?: string;
10
9
  rawText: string;
11
10
  raw: unknown;
@@ -16,12 +15,12 @@ export declare function interpretStandardResult(response: unknown, defaults: {
16
15
  }): InterpretedStandardResult;
17
16
  export declare function cleanResultText(text: string | undefined, options?: {
18
17
  tag?: string;
19
- fallback?: string;
18
+ defaultValue?: string;
20
19
  }): string | undefined;
21
- export declare function bestEffortInterpretedText(interpreted: Pick<InterpretedStandardResult, 'cleanText' | 'rawText'>, fallback?: string): string | undefined;
20
+ export declare function bestEffortInterpretedText(interpreted: Pick<InterpretedStandardResult, 'cleanText' | 'rawText'>, defaultValue?: string): string | undefined;
22
21
  export declare function coerceString(value: unknown): string | undefined;
23
22
  export declare function coerceStringArray(value: unknown): string[] | undefined;
24
- export declare function coerceBoolean(value: unknown, fallback?: boolean): boolean | undefined;
23
+ export declare function coerceBoolean(value: unknown, defaultValue?: boolean): boolean | undefined;
25
24
  export declare function coerceNumber(value: unknown): number | undefined;
26
25
  export declare function coerceVector3(value: unknown): [number, number, number] | undefined;
27
26
  //# sourceMappingURL=result-helpers.d.ts.map
@@ -1,10 +1,9 @@
1
- import { parseStandardResult, stripTaggedResultLines } from './python-output.js';
2
1
  export function interpretStandardResult(response, defaults) {
3
- const parsed = parseStandardResult(response);
4
- const payload = (parsed.data ?? {});
2
+ const payload = (response && typeof response === 'object' ? response : {});
5
3
  const success = payload.success === true;
6
- const rawText = typeof parsed.text === 'string' ? parsed.text : String(parsed.text ?? '');
7
- const cleanedText = cleanResultText(rawText, { fallback: undefined });
4
+ const rawText = typeof payload.message === 'string' ? payload.message :
5
+ typeof payload.output === 'string' ? payload.output :
6
+ String(payload.result ?? '');
8
7
  const messageFromPayload = typeof payload.message === 'string' ? payload.message.trim() : '';
9
8
  const errorFromPayload = typeof payload.error === 'string' ? payload.error.trim() : '';
10
9
  const message = messageFromPayload || (success ? defaults.successMessage : defaults.failureMessage);
@@ -16,32 +15,32 @@ export function interpretStandardResult(response, defaults) {
16
15
  warnings: coerceStringArray(payload.warnings),
17
16
  details: coerceStringArray(payload.details),
18
17
  payload,
19
- cleanText: cleanedText,
18
+ cleanText: rawText || undefined,
20
19
  rawText,
21
- raw: parsed.raw
20
+ raw: response
22
21
  };
23
22
  }
24
23
  export function cleanResultText(text, options = {}) {
25
- const { tag = 'RESULT:', fallback } = options;
24
+ const { defaultValue } = options;
26
25
  if (!text) {
27
- return fallback;
26
+ return defaultValue;
28
27
  }
29
- const cleaned = stripTaggedResultLines(text, tag).trim();
28
+ const cleaned = text.trim();
30
29
  if (cleaned.length > 0) {
31
30
  return cleaned;
32
31
  }
33
- return fallback;
32
+ return defaultValue;
34
33
  }
35
- export function bestEffortInterpretedText(interpreted, fallback) {
34
+ export function bestEffortInterpretedText(interpreted, defaultValue) {
36
35
  const cleaned = interpreted.cleanText?.trim();
37
36
  if (cleaned) {
38
37
  return cleaned;
39
38
  }
40
39
  const raw = interpreted.rawText?.trim?.();
41
- if (raw && !raw.startsWith('RESULT:')) {
40
+ if (raw) {
42
41
  return raw;
43
42
  }
44
- return fallback;
43
+ return defaultValue;
45
44
  }
46
45
  export function coerceString(value) {
47
46
  if (typeof value === 'string') {
@@ -67,7 +66,7 @@ export function coerceStringArray(value) {
67
66
  }
68
67
  return items.length > 0 ? items : undefined;
69
68
  }
70
- export function coerceBoolean(value, fallback) {
69
+ export function coerceBoolean(value, defaultValue) {
71
70
  if (typeof value === 'boolean') {
72
71
  return value;
73
72
  }
@@ -88,7 +87,7 @@ export function coerceBoolean(value, fallback) {
88
87
  return false;
89
88
  }
90
89
  }
91
- return fallback;
90
+ return defaultValue;
92
91
  }
93
92
  export function coerceNumber(value) {
94
93
  if (typeof value === 'number' && Number.isFinite(value)) {
@@ -1,12 +1,11 @@
1
- // Remove circular references and non-serializable properties from an object
1
+ import { Logger } from './logger.js';
2
2
  export function cleanObject(obj, maxDepth = 10) {
3
3
  const seen = new WeakSet();
4
+ const logger = new Logger('safe-json');
4
5
  function clean(value, depth, path = 'root') {
5
- // Prevent infinite recursion
6
6
  if (depth > maxDepth) {
7
7
  return '[Max depth reached]';
8
8
  }
9
- // Handle primitives
10
9
  if (value === null || value === undefined) {
11
10
  return value;
12
11
  }
@@ -19,20 +18,16 @@ export function cleanObject(obj, maxDepth = 10) {
19
18
  }
20
19
  return value;
21
20
  }
22
- // Check for circular reference
23
21
  if (seen.has(value)) {
24
22
  return '[Circular Reference]';
25
23
  }
26
24
  seen.add(value);
27
- // Handle arrays
28
25
  if (Array.isArray(value)) {
29
26
  const result = value.map((item, index) => clean(item, depth + 1, `${path}[${index}]`));
30
- seen.delete(value); // Remove from seen after processing
27
+ seen.delete(value);
31
28
  return result;
32
29
  }
33
- // Handle objects
34
30
  const cleaned = {};
35
- // Use Object.keys to avoid prototype properties
36
31
  const keys = Object.keys(value);
37
32
  for (const key of keys) {
38
33
  try {
@@ -42,11 +37,10 @@ export function cleanObject(obj, maxDepth = 10) {
42
37
  }
43
38
  }
44
39
  catch (e) {
45
- // Skip properties that throw errors when accessed
46
- console.error(`Error cleaning property ${path}.${key}:`, e);
40
+ logger.error(`Error cleaning property ${path}.${key}`, e);
47
41
  }
48
42
  }
49
- seen.delete(value); // Remove from seen after processing
43
+ seen.delete(value);
50
44
  return cleaned;
51
45
  }
52
46
  return clean(obj, 0);
@@ -0,0 +1,24 @@
1
+ export interface CommandQueueItem<T = any> {
2
+ command: () => Promise<T>;
3
+ resolve: (value: T) => void;
4
+ reject: (reason?: any) => void;
5
+ priority: number;
6
+ retryCount?: number;
7
+ }
8
+ export declare class UnrealCommandQueue {
9
+ private log;
10
+ private queue;
11
+ private isProcessing;
12
+ private lastCommandTime;
13
+ private lastStatCommandTime;
14
+ private readonly MIN_COMMAND_DELAY;
15
+ private readonly MAX_COMMAND_DELAY;
16
+ private readonly STAT_COMMAND_DELAY;
17
+ constructor();
18
+ execute<T>(command: () => Promise<T>, priority?: number): Promise<T>;
19
+ private processQueue;
20
+ private calculateDelay;
21
+ private startProcessor;
22
+ private delay;
23
+ }
24
+ //# sourceMappingURL=unreal-command-queue.d.ts.map
@@ -0,0 +1,120 @@
1
+ import { Logger } from './logger.js';
2
+ export class UnrealCommandQueue {
3
+ log = new Logger('UnrealCommandQueue');
4
+ queue = [];
5
+ isProcessing = false;
6
+ lastCommandTime = 0;
7
+ lastStatCommandTime = 0;
8
+ MIN_COMMAND_DELAY = 100;
9
+ MAX_COMMAND_DELAY = 500;
10
+ STAT_COMMAND_DELAY = 300;
11
+ constructor() {
12
+ this.startProcessor();
13
+ }
14
+ async execute(command, priority = 5) {
15
+ return new Promise((resolve, reject) => {
16
+ this.queue.push({
17
+ command,
18
+ resolve,
19
+ reject,
20
+ priority
21
+ });
22
+ this.queue.sort((a, b) => a.priority - b.priority);
23
+ if (!this.isProcessing) {
24
+ this.processQueue();
25
+ }
26
+ });
27
+ }
28
+ async processQueue() {
29
+ if (this.isProcessing || this.queue.length === 0) {
30
+ return;
31
+ }
32
+ this.isProcessing = true;
33
+ while (this.queue.length > 0) {
34
+ const item = this.queue.shift();
35
+ if (!item)
36
+ continue;
37
+ const timeSinceLastCommand = Date.now() - this.lastCommandTime;
38
+ const requiredDelay = this.calculateDelay(item.priority);
39
+ if (timeSinceLastCommand < requiredDelay) {
40
+ await this.delay(requiredDelay - timeSinceLastCommand);
41
+ }
42
+ try {
43
+ const result = await item.command();
44
+ item.resolve(result);
45
+ }
46
+ catch (error) {
47
+ const msgRaw = error?.message ?? String(error);
48
+ const msg = String(msgRaw).toLowerCase();
49
+ if (item.retryCount === undefined)
50
+ item.retryCount = 0;
51
+ const isTransient = (msg.includes('timeout') ||
52
+ msg.includes('timed out') ||
53
+ msg.includes('connect') ||
54
+ msg.includes('econnrefused') ||
55
+ msg.includes('econnreset') ||
56
+ msg.includes('broken pipe') ||
57
+ msg.includes('automation bridge') ||
58
+ msg.includes('not connected'));
59
+ const isDeterministicFailure = (msg.includes('command not executed') ||
60
+ msg.includes('exec_failed') ||
61
+ msg.includes('invalid command') ||
62
+ msg.includes('invalid argument') ||
63
+ msg.includes('unknown_plugin_action') ||
64
+ msg.includes('unknown action'));
65
+ if (isTransient && item.retryCount < 3) {
66
+ item.retryCount++;
67
+ this.log.warn(`Command failed (transient), retrying (${item.retryCount}/3)`);
68
+ this.queue.unshift({
69
+ command: item.command,
70
+ resolve: item.resolve,
71
+ reject: item.reject,
72
+ priority: Math.max(1, item.priority - 1),
73
+ retryCount: item.retryCount
74
+ });
75
+ await this.delay(500);
76
+ }
77
+ else {
78
+ if (isDeterministicFailure) {
79
+ this.log.warn(`Command failed (non-retryable): ${msgRaw}`);
80
+ }
81
+ item.reject(error);
82
+ }
83
+ }
84
+ this.lastCommandTime = Date.now();
85
+ }
86
+ this.isProcessing = false;
87
+ }
88
+ calculateDelay(priority) {
89
+ if (priority <= 3) {
90
+ return this.MAX_COMMAND_DELAY;
91
+ }
92
+ else if (priority <= 6) {
93
+ return 200;
94
+ }
95
+ else if (priority === 8) {
96
+ const timeSinceLastStat = Date.now() - this.lastStatCommandTime;
97
+ if (timeSinceLastStat < this.STAT_COMMAND_DELAY) {
98
+ return this.STAT_COMMAND_DELAY;
99
+ }
100
+ this.lastStatCommandTime = Date.now();
101
+ return 150;
102
+ }
103
+ else {
104
+ const baseDelay = this.MIN_COMMAND_DELAY;
105
+ const jitter = Math.random() * 50;
106
+ return baseDelay + jitter;
107
+ }
108
+ }
109
+ startProcessor() {
110
+ setInterval(() => {
111
+ if (!this.isProcessing && this.queue.length > 0) {
112
+ this.processQueue();
113
+ }
114
+ }, 1000);
115
+ }
116
+ delay(ms) {
117
+ return new Promise(resolve => setTimeout(resolve, ms));
118
+ }
119
+ }
120
+ //# sourceMappingURL=unreal-command-queue.js.map