unreal-engine-mcp-server 0.4.7 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/.env.example +26 -0
  2. package/.env.production +38 -7
  3. package/.eslintrc.json +0 -54
  4. package/.eslintrc.override.json +8 -0
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  6. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  8. package/.github/copilot-instructions.md +478 -45
  9. package/.github/dependabot.yml +19 -0
  10. package/.github/labeler.yml +24 -0
  11. package/.github/labels.yml +70 -0
  12. package/.github/pull_request_template.md +42 -0
  13. package/.github/release-drafter-config.yml +51 -0
  14. package/.github/workflows/auto-merge.yml +38 -0
  15. package/.github/workflows/ci.yml +38 -0
  16. package/.github/workflows/dependency-review.yml +17 -0
  17. package/.github/workflows/gemini-issue-triage.yml +172 -0
  18. package/.github/workflows/greetings.yml +27 -0
  19. package/.github/workflows/labeler.yml +17 -0
  20. package/.github/workflows/links.yml +80 -0
  21. package/.github/workflows/pr-size-labeler.yml +137 -0
  22. package/.github/workflows/publish-mcp.yml +13 -7
  23. package/.github/workflows/release-drafter.yml +23 -0
  24. package/.github/workflows/release.yml +112 -0
  25. package/.github/workflows/semantic-pull-request.yml +35 -0
  26. package/.github/workflows/smoke-test.yml +36 -0
  27. package/.github/workflows/stale.yml +28 -0
  28. package/CHANGELOG.md +338 -31
  29. package/CONTRIBUTING.md +140 -0
  30. package/GEMINI.md +115 -0
  31. package/Public/Plugin_setup_guide.mp4 +0 -0
  32. package/README.md +189 -128
  33. package/claude_desktop_config_example.json +7 -6
  34. package/dist/automation/bridge.d.ts +50 -0
  35. package/dist/automation/bridge.js +452 -0
  36. package/dist/automation/connection-manager.d.ts +23 -0
  37. package/dist/automation/connection-manager.js +107 -0
  38. package/dist/automation/handshake.d.ts +11 -0
  39. package/dist/automation/handshake.js +89 -0
  40. package/dist/automation/index.d.ts +3 -0
  41. package/dist/automation/index.js +3 -0
  42. package/dist/automation/message-handler.d.ts +12 -0
  43. package/dist/automation/message-handler.js +149 -0
  44. package/dist/automation/request-tracker.d.ts +25 -0
  45. package/dist/automation/request-tracker.js +98 -0
  46. package/dist/automation/types.d.ts +130 -0
  47. package/dist/automation/types.js +2 -0
  48. package/dist/cli.js +32 -5
  49. package/dist/config.d.ts +26 -0
  50. package/dist/config.js +59 -0
  51. package/dist/constants.d.ts +16 -0
  52. package/dist/constants.js +16 -0
  53. package/dist/graphql/loaders.d.ts +64 -0
  54. package/dist/graphql/loaders.js +117 -0
  55. package/dist/graphql/resolvers.d.ts +268 -0
  56. package/dist/graphql/resolvers.js +746 -0
  57. package/dist/graphql/schema.d.ts +5 -0
  58. package/dist/graphql/schema.js +437 -0
  59. package/dist/graphql/server.d.ts +26 -0
  60. package/dist/graphql/server.js +117 -0
  61. package/dist/graphql/types.d.ts +9 -0
  62. package/dist/graphql/types.js +2 -0
  63. package/dist/handlers/resource-handlers.d.ts +20 -0
  64. package/dist/handlers/resource-handlers.js +180 -0
  65. package/dist/index.d.ts +33 -18
  66. package/dist/index.js +130 -619
  67. package/dist/resources/actors.d.ts +17 -12
  68. package/dist/resources/actors.js +56 -76
  69. package/dist/resources/assets.d.ts +6 -14
  70. package/dist/resources/assets.js +115 -147
  71. package/dist/resources/levels.d.ts +13 -13
  72. package/dist/resources/levels.js +25 -34
  73. package/dist/server/resource-registry.d.ts +20 -0
  74. package/dist/server/resource-registry.js +37 -0
  75. package/dist/server/tool-registry.d.ts +23 -0
  76. package/dist/server/tool-registry.js +322 -0
  77. package/dist/server-setup.d.ts +20 -0
  78. package/dist/server-setup.js +71 -0
  79. package/dist/services/health-monitor.d.ts +34 -0
  80. package/dist/services/health-monitor.js +105 -0
  81. package/dist/services/metrics-server.d.ts +11 -0
  82. package/dist/services/metrics-server.js +105 -0
  83. package/dist/tools/actors.d.ts +163 -9
  84. package/dist/tools/actors.js +356 -311
  85. package/dist/tools/animation.d.ts +135 -4
  86. package/dist/tools/animation.js +510 -411
  87. package/dist/tools/assets.d.ts +75 -29
  88. package/dist/tools/assets.js +265 -284
  89. package/dist/tools/audio.d.ts +102 -42
  90. package/dist/tools/audio.js +272 -685
  91. package/dist/tools/base-tool.d.ts +17 -0
  92. package/dist/tools/base-tool.js +46 -0
  93. package/dist/tools/behavior-tree.d.ts +94 -0
  94. package/dist/tools/behavior-tree.js +39 -0
  95. package/dist/tools/blueprint.d.ts +208 -126
  96. package/dist/tools/blueprint.js +685 -832
  97. package/dist/tools/consolidated-tool-definitions.d.ts +5462 -1781
  98. package/dist/tools/consolidated-tool-definitions.js +829 -496
  99. package/dist/tools/consolidated-tool-handlers.d.ts +2 -1
  100. package/dist/tools/consolidated-tool-handlers.js +198 -1027
  101. package/dist/tools/debug.d.ts +143 -85
  102. package/dist/tools/debug.js +234 -180
  103. package/dist/tools/dynamic-handler-registry.d.ts +13 -0
  104. package/dist/tools/dynamic-handler-registry.js +23 -0
  105. package/dist/tools/editor.d.ts +30 -83
  106. package/dist/tools/editor.js +247 -244
  107. package/dist/tools/engine.d.ts +10 -4
  108. package/dist/tools/engine.js +13 -5
  109. package/dist/tools/environment.d.ts +30 -0
  110. package/dist/tools/environment.js +267 -0
  111. package/dist/tools/foliage.d.ts +65 -99
  112. package/dist/tools/foliage.js +221 -331
  113. package/dist/tools/handlers/actor-handlers.d.ts +3 -0
  114. package/dist/tools/handlers/actor-handlers.js +227 -0
  115. package/dist/tools/handlers/animation-handlers.d.ts +3 -0
  116. package/dist/tools/handlers/animation-handlers.js +185 -0
  117. package/dist/tools/handlers/argument-helper.d.ts +16 -0
  118. package/dist/tools/handlers/argument-helper.js +80 -0
  119. package/dist/tools/handlers/asset-handlers.d.ts +3 -0
  120. package/dist/tools/handlers/asset-handlers.js +496 -0
  121. package/dist/tools/handlers/audio-handlers.d.ts +3 -0
  122. package/dist/tools/handlers/audio-handlers.js +166 -0
  123. package/dist/tools/handlers/blueprint-handlers.d.ts +4 -0
  124. package/dist/tools/handlers/blueprint-handlers.js +358 -0
  125. package/dist/tools/handlers/common-handlers.d.ts +14 -0
  126. package/dist/tools/handlers/common-handlers.js +56 -0
  127. package/dist/tools/handlers/editor-handlers.d.ts +3 -0
  128. package/dist/tools/handlers/editor-handlers.js +119 -0
  129. package/dist/tools/handlers/effect-handlers.d.ts +3 -0
  130. package/dist/tools/handlers/effect-handlers.js +171 -0
  131. package/dist/tools/handlers/environment-handlers.d.ts +3 -0
  132. package/dist/tools/handlers/environment-handlers.js +170 -0
  133. package/dist/tools/handlers/graph-handlers.d.ts +3 -0
  134. package/dist/tools/handlers/graph-handlers.js +90 -0
  135. package/dist/tools/handlers/input-handlers.d.ts +3 -0
  136. package/dist/tools/handlers/input-handlers.js +21 -0
  137. package/dist/tools/handlers/inspect-handlers.d.ts +3 -0
  138. package/dist/tools/handlers/inspect-handlers.js +383 -0
  139. package/dist/tools/handlers/level-handlers.d.ts +3 -0
  140. package/dist/tools/handlers/level-handlers.js +237 -0
  141. package/dist/tools/handlers/lighting-handlers.d.ts +3 -0
  142. package/dist/tools/handlers/lighting-handlers.js +144 -0
  143. package/dist/tools/handlers/performance-handlers.d.ts +3 -0
  144. package/dist/tools/handlers/performance-handlers.js +130 -0
  145. package/dist/tools/handlers/pipeline-handlers.d.ts +3 -0
  146. package/dist/tools/handlers/pipeline-handlers.js +110 -0
  147. package/dist/tools/handlers/sequence-handlers.d.ts +3 -0
  148. package/dist/tools/handlers/sequence-handlers.js +376 -0
  149. package/dist/tools/handlers/system-handlers.d.ts +4 -0
  150. package/dist/tools/handlers/system-handlers.js +506 -0
  151. package/dist/tools/input.d.ts +19 -0
  152. package/dist/tools/input.js +89 -0
  153. package/dist/tools/introspection.d.ts +103 -40
  154. package/dist/tools/introspection.js +425 -568
  155. package/dist/tools/landscape.d.ts +54 -93
  156. package/dist/tools/landscape.js +284 -409
  157. package/dist/tools/level.d.ts +66 -27
  158. package/dist/tools/level.js +647 -675
  159. package/dist/tools/lighting.d.ts +77 -38
  160. package/dist/tools/lighting.js +445 -943
  161. package/dist/tools/logs.d.ts +3 -3
  162. package/dist/tools/logs.js +5 -57
  163. package/dist/tools/materials.d.ts +91 -24
  164. package/dist/tools/materials.js +194 -118
  165. package/dist/tools/niagara.d.ts +149 -39
  166. package/dist/tools/niagara.js +267 -182
  167. package/dist/tools/performance.d.ts +27 -13
  168. package/dist/tools/performance.js +203 -122
  169. package/dist/tools/physics.d.ts +32 -77
  170. package/dist/tools/physics.js +175 -582
  171. package/dist/tools/property-dictionary.d.ts +13 -0
  172. package/dist/tools/property-dictionary.js +82 -0
  173. package/dist/tools/sequence.d.ts +85 -60
  174. package/dist/tools/sequence.js +208 -747
  175. package/dist/tools/tool-definition-utils.d.ts +59 -0
  176. package/dist/tools/tool-definition-utils.js +35 -0
  177. package/dist/tools/ui.d.ts +64 -34
  178. package/dist/tools/ui.js +134 -214
  179. package/dist/types/automation-responses.d.ts +115 -0
  180. package/dist/types/automation-responses.js +2 -0
  181. package/dist/types/env.d.ts +0 -3
  182. package/dist/types/env.js +0 -7
  183. package/dist/types/responses.d.ts +249 -0
  184. package/dist/types/responses.js +2 -0
  185. package/dist/types/tool-interfaces.d.ts +898 -0
  186. package/dist/types/tool-interfaces.js +2 -0
  187. package/dist/types/tool-types.d.ts +183 -19
  188. package/dist/types/tool-types.js +0 -4
  189. package/dist/unreal-bridge.d.ts +24 -131
  190. package/dist/unreal-bridge.js +364 -1506
  191. package/dist/utils/command-validator.d.ts +9 -0
  192. package/dist/utils/command-validator.js +68 -0
  193. package/dist/utils/elicitation.d.ts +1 -1
  194. package/dist/utils/elicitation.js +12 -15
  195. package/dist/utils/error-handler.d.ts +2 -51
  196. package/dist/utils/error-handler.js +11 -87
  197. package/dist/utils/ini-reader.d.ts +3 -0
  198. package/dist/utils/ini-reader.js +69 -0
  199. package/dist/utils/logger.js +9 -6
  200. package/dist/utils/normalize.d.ts +3 -0
  201. package/dist/utils/normalize.js +56 -0
  202. package/dist/utils/path-security.d.ts +2 -0
  203. package/dist/utils/path-security.js +24 -0
  204. package/dist/utils/response-factory.d.ts +7 -0
  205. package/dist/utils/response-factory.js +27 -0
  206. package/dist/utils/response-validator.d.ts +3 -24
  207. package/dist/utils/response-validator.js +130 -81
  208. package/dist/utils/result-helpers.d.ts +4 -5
  209. package/dist/utils/result-helpers.js +15 -16
  210. package/dist/utils/safe-json.js +5 -11
  211. package/dist/utils/unreal-command-queue.d.ts +24 -0
  212. package/dist/utils/unreal-command-queue.js +120 -0
  213. package/dist/utils/validation.d.ts +0 -40
  214. package/dist/utils/validation.js +1 -78
  215. package/dist/wasm/index.d.ts +70 -0
  216. package/dist/wasm/index.js +535 -0
  217. package/docs/GraphQL-API.md +888 -0
  218. package/docs/Migration-Guide-v0.5.0.md +684 -0
  219. package/docs/Roadmap.md +53 -0
  220. package/docs/WebAssembly-Integration.md +628 -0
  221. package/docs/editor-plugin-extension.md +370 -0
  222. package/docs/handler-mapping.md +242 -0
  223. package/docs/native-automation-progress.md +128 -0
  224. package/docs/testing-guide.md +423 -0
  225. package/mcp-config-example.json +6 -6
  226. package/package.json +67 -28
  227. package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +8 -0
  228. package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +64 -0
  229. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +189 -0
  230. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +22 -0
  231. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +30 -0
  232. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +1983 -0
  233. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +72 -0
  234. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +46 -0
  235. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +581 -0
  236. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +2394 -0
  237. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +300 -0
  238. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +2807 -0
  239. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +1087 -0
  240. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +488 -0
  241. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +643 -0
  242. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +31 -0
  243. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +1184 -0
  244. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +5652 -0
  245. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +152 -0
  246. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +2614 -0
  247. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +42 -0
  248. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +1237 -0
  249. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +1701 -0
  250. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +2145 -0
  251. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +954 -0
  252. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +209 -0
  253. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +41 -0
  254. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +1164 -0
  255. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +762 -0
  256. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +634 -0
  257. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +136 -0
  258. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +494 -0
  259. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +278 -0
  260. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +625 -0
  261. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +401 -0
  262. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +67 -0
  263. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +735 -0
  264. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +2634 -0
  265. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +189 -0
  266. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +917 -0
  267. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +39 -0
  268. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +2670 -0
  269. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +519 -0
  270. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +38 -0
  271. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +668 -0
  272. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +346 -0
  273. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +1330 -0
  274. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +149 -0
  275. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +783 -0
  276. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +115 -0
  277. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +796 -0
  278. package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +117 -0
  279. package/scripts/check-unreal-connection.mjs +19 -0
  280. package/scripts/clean-tmp.js +23 -0
  281. package/scripts/patch-wasm.js +26 -0
  282. package/scripts/run-all-tests.mjs +136 -0
  283. package/scripts/smoke-test.ts +94 -0
  284. package/scripts/sync-mcp-plugin.js +143 -0
  285. package/scripts/test-no-plugin-alternates.mjs +113 -0
  286. package/scripts/validate-server.js +46 -0
  287. package/scripts/verify-automation-bridge.js +200 -0
  288. package/server.json +58 -21
  289. package/src/automation/bridge.ts +558 -0
  290. package/src/automation/connection-manager.ts +130 -0
  291. package/src/automation/handshake.ts +99 -0
  292. package/src/automation/index.ts +2 -0
  293. package/src/automation/message-handler.ts +167 -0
  294. package/src/automation/request-tracker.ts +123 -0
  295. package/src/automation/types.ts +107 -0
  296. package/src/cli.ts +33 -6
  297. package/src/config.ts +73 -0
  298. package/src/constants.ts +19 -0
  299. package/src/graphql/loaders.ts +244 -0
  300. package/src/graphql/resolvers.ts +1008 -0
  301. package/src/graphql/schema.ts +452 -0
  302. package/src/graphql/server.ts +156 -0
  303. package/src/graphql/types.ts +10 -0
  304. package/src/handlers/resource-handlers.ts +186 -0
  305. package/src/index.ts +166 -664
  306. package/src/resources/actors.ts +58 -76
  307. package/src/resources/assets.ts +148 -134
  308. package/src/resources/levels.ts +28 -33
  309. package/src/server/resource-registry.ts +47 -0
  310. package/src/server/tool-registry.ts +354 -0
  311. package/src/server-setup.ts +114 -0
  312. package/src/services/health-monitor.ts +132 -0
  313. package/src/services/metrics-server.ts +142 -0
  314. package/src/tools/actors.ts +426 -323
  315. package/src/tools/animation.ts +672 -461
  316. package/src/tools/assets.ts +364 -289
  317. package/src/tools/audio.ts +323 -766
  318. package/src/tools/base-tool.ts +52 -0
  319. package/src/tools/behavior-tree.ts +45 -0
  320. package/src/tools/blueprint.ts +792 -970
  321. package/src/tools/consolidated-tool-definitions.ts +993 -515
  322. package/src/tools/consolidated-tool-handlers.ts +258 -1146
  323. package/src/tools/debug.ts +292 -187
  324. package/src/tools/dynamic-handler-registry.ts +33 -0
  325. package/src/tools/editor.ts +329 -253
  326. package/src/tools/engine.ts +14 -3
  327. package/src/tools/environment.ts +281 -0
  328. package/src/tools/foliage.ts +330 -392
  329. package/src/tools/handlers/actor-handlers.ts +265 -0
  330. package/src/tools/handlers/animation-handlers.ts +237 -0
  331. package/src/tools/handlers/argument-helper.ts +142 -0
  332. package/src/tools/handlers/asset-handlers.ts +532 -0
  333. package/src/tools/handlers/audio-handlers.ts +194 -0
  334. package/src/tools/handlers/blueprint-handlers.ts +380 -0
  335. package/src/tools/handlers/common-handlers.ts +87 -0
  336. package/src/tools/handlers/editor-handlers.ts +123 -0
  337. package/src/tools/handlers/effect-handlers.ts +220 -0
  338. package/src/tools/handlers/environment-handlers.ts +183 -0
  339. package/src/tools/handlers/graph-handlers.ts +116 -0
  340. package/src/tools/handlers/input-handlers.ts +28 -0
  341. package/src/tools/handlers/inspect-handlers.ts +450 -0
  342. package/src/tools/handlers/level-handlers.ts +252 -0
  343. package/src/tools/handlers/lighting-handlers.ts +147 -0
  344. package/src/tools/handlers/performance-handlers.ts +132 -0
  345. package/src/tools/handlers/pipeline-handlers.ts +127 -0
  346. package/src/tools/handlers/sequence-handlers.ts +415 -0
  347. package/src/tools/handlers/system-handlers.ts +564 -0
  348. package/src/tools/input.ts +101 -0
  349. package/src/tools/introspection.ts +493 -584
  350. package/src/tools/landscape.ts +418 -507
  351. package/src/tools/level.ts +786 -708
  352. package/src/tools/lighting.ts +588 -984
  353. package/src/tools/logs.ts +9 -57
  354. package/src/tools/materials.ts +237 -121
  355. package/src/tools/niagara.ts +335 -168
  356. package/src/tools/performance.ts +320 -169
  357. package/src/tools/physics.ts +274 -613
  358. package/src/tools/property-dictionary.ts +98 -0
  359. package/src/tools/sequence.ts +276 -820
  360. package/src/tools/tool-definition-utils.ts +35 -0
  361. package/src/tools/ui.ts +205 -283
  362. package/src/types/automation-responses.ts +119 -0
  363. package/src/types/env.ts +0 -10
  364. package/src/types/responses.ts +355 -0
  365. package/src/types/tool-interfaces.ts +250 -0
  366. package/src/types/tool-types.ts +243 -21
  367. package/src/unreal-bridge.ts +460 -1550
  368. package/src/utils/command-validator.ts +76 -0
  369. package/src/utils/elicitation.ts +10 -7
  370. package/src/utils/error-handler.ts +14 -90
  371. package/src/utils/ini-reader.ts +86 -0
  372. package/src/utils/logger.ts +8 -3
  373. package/src/utils/normalize.test.ts +162 -0
  374. package/src/utils/normalize.ts +60 -0
  375. package/src/utils/path-security.ts +43 -0
  376. package/src/utils/response-factory.ts +44 -0
  377. package/src/utils/response-validator.ts +176 -56
  378. package/src/utils/result-helpers.ts +21 -19
  379. package/src/utils/safe-json.test.ts +90 -0
  380. package/src/utils/safe-json.ts +14 -11
  381. package/src/utils/unreal-command-queue.ts +152 -0
  382. package/src/utils/validation.test.ts +184 -0
  383. package/src/utils/validation.ts +4 -1
  384. package/src/wasm/index.ts +838 -0
  385. package/test-server.mjs +100 -0
  386. package/tests/run-unreal-tool-tests.mjs +242 -14
  387. package/tests/test-animation.mjs +369 -0
  388. package/tests/test-asset-advanced.mjs +82 -0
  389. package/tests/test-asset-errors.mjs +35 -0
  390. package/tests/test-asset-graph.mjs +311 -0
  391. package/tests/test-audio.mjs +417 -0
  392. package/tests/test-automation-timeouts.mjs +98 -0
  393. package/tests/test-behavior-tree.mjs +444 -0
  394. package/tests/test-blueprint-graph.mjs +410 -0
  395. package/tests/test-blueprint.mjs +577 -0
  396. package/tests/test-client-mode.mjs +86 -0
  397. package/tests/test-console-command.mjs +56 -0
  398. package/tests/test-control-actor.mjs +425 -0
  399. package/tests/test-control-editor.mjs +112 -0
  400. package/tests/test-graphql.mjs +372 -0
  401. package/tests/test-input.mjs +349 -0
  402. package/tests/test-inspect.mjs +302 -0
  403. package/tests/test-landscape.mjs +316 -0
  404. package/tests/test-lighting.mjs +428 -0
  405. package/tests/test-manage-asset.mjs +438 -0
  406. package/tests/test-manage-level.mjs +89 -0
  407. package/tests/test-materials.mjs +356 -0
  408. package/tests/test-niagara.mjs +185 -0
  409. package/tests/test-no-inline-python.mjs +122 -0
  410. package/tests/test-performance.mjs +539 -0
  411. package/tests/test-plugin-handshake.mjs +82 -0
  412. package/tests/test-runner.mjs +933 -0
  413. package/tests/test-sequence.mjs +104 -0
  414. package/tests/test-system.mjs +96 -0
  415. package/tests/test-wasm.mjs +283 -0
  416. package/tests/test-world-partition.mjs +215 -0
  417. package/tsconfig.json +3 -3
  418. package/vitest.config.ts +35 -0
  419. package/wasm/Cargo.lock +363 -0
  420. package/wasm/Cargo.toml +42 -0
  421. package/wasm/LICENSE +21 -0
  422. package/wasm/README.md +253 -0
  423. package/wasm/src/dependency_resolver.rs +377 -0
  424. package/wasm/src/lib.rs +153 -0
  425. package/wasm/src/property_parser.rs +271 -0
  426. package/wasm/src/transform_math.rs +396 -0
  427. package/wasm/tests/integration.rs +109 -0
  428. package/.github/workflows/smithery-build.yml +0 -29
  429. package/dist/prompts/index.d.ts +0 -21
  430. package/dist/prompts/index.js +0 -217
  431. package/dist/tools/build_environment_advanced.d.ts +0 -65
  432. package/dist/tools/build_environment_advanced.js +0 -633
  433. package/dist/tools/rc.d.ts +0 -110
  434. package/dist/tools/rc.js +0 -437
  435. package/dist/tools/visual.d.ts +0 -40
  436. package/dist/tools/visual.js +0 -282
  437. package/dist/utils/http.d.ts +0 -6
  438. package/dist/utils/http.js +0 -151
  439. package/dist/utils/python-output.d.ts +0 -18
  440. package/dist/utils/python-output.js +0 -290
  441. package/dist/utils/python.d.ts +0 -2
  442. package/dist/utils/python.js +0 -4
  443. package/dist/utils/stdio-redirect.d.ts +0 -2
  444. package/dist/utils/stdio-redirect.js +0 -20
  445. package/docs/unreal-tool-test-cases.md +0 -574
  446. package/smithery.yaml +0 -29
  447. package/src/prompts/index.ts +0 -249
  448. package/src/tools/build_environment_advanced.ts +0 -732
  449. package/src/tools/rc.ts +0 -515
  450. package/src/tools/visual.ts +0 -281
  451. package/src/utils/http.ts +0 -187
  452. package/src/utils/python-output.ts +0 -351
  453. package/src/utils/python.ts +0 -3
  454. package/src/utils/stdio-redirect.ts +0 -18
@@ -1,16 +1,33 @@
1
1
  import { UnrealBridge } from '../unreal-bridge.js';
2
+ import { AutomationBridge } from '../automation/index.js';
2
3
  import { Logger } from '../utils/logger.js';
3
- import { bestEffortInterpretedText, interpretStandardResult } from '../utils/result-helpers.js';
4
+ import { lookupPropertyMetadata, normalizeDictionaryKey, PropertyDictionaryEntry } from './property-dictionary.js';
5
+
6
+ export interface ObjectSummary {
7
+ name?: string;
8
+ class?: string;
9
+ path?: string;
10
+ parent?: string;
11
+ tags?: string[];
12
+ propertyCount: number;
13
+ curatedPropertyCount: number;
14
+ filteredPropertyCount: number;
15
+ categories?: Record<string, number>;
16
+ }
4
17
 
5
18
  export interface ObjectInfo {
6
- class: string;
7
- name: string;
8
- path: string;
19
+ class?: string;
20
+ name?: string;
21
+ path?: string;
9
22
  properties: PropertyInfo[];
10
23
  functions?: FunctionInfo[];
11
24
  parent?: string;
12
25
  interfaces?: string[];
13
26
  flags?: string[];
27
+ summary?: ObjectSummary;
28
+ filteredProperties?: string[];
29
+ tags?: string[];
30
+ original?: any;
14
31
  }
15
32
 
16
33
  export interface PropertyInfo {
@@ -21,6 +38,10 @@ export interface PropertyInfo {
21
38
  metadata?: Record<string, any>;
22
39
  category?: string;
23
40
  tooltip?: string;
41
+ description?: string;
42
+ displayValue?: string;
43
+ dictionaryEntry?: PropertyDictionaryEntry;
44
+ isReadOnly?: boolean;
24
45
  }
25
46
 
26
47
  export interface FunctionInfo {
@@ -43,8 +64,28 @@ export class IntrospectionTools {
43
64
  private objectCache = new Map<string, ObjectInfo>();
44
65
  private retryAttempts = 3;
45
66
  private retryDelay = 1000;
46
-
47
- constructor(private bridge: UnrealBridge) {}
67
+
68
+ private readonly propertyFilterPatterns: RegExp[] = [
69
+ /internal/i,
70
+ /transient/i,
71
+ /^temp/i,
72
+ /^bhidden/i,
73
+ /renderstate/i,
74
+ /previewonly/i,
75
+ /deprecated/i
76
+ ];
77
+
78
+ private readonly ignoredPropertyKeys = new Set<string>([
79
+ 'assetimportdata',
80
+ 'blueprintcreatedcomponents',
81
+ 'componentreplicator',
82
+ 'componentoverrides',
83
+ 'actorcomponenttags'
84
+ ]);
85
+
86
+ constructor(private bridge: UnrealBridge, private automationBridge?: AutomationBridge) { }
87
+
88
+ setAutomationBridge(automationBridge?: AutomationBridge) { this.automationBridge = automationBridge; }
48
89
 
49
90
  /**
50
91
  * Execute with retry logic for transient failures
@@ -54,57 +95,23 @@ export class IntrospectionTools {
54
95
  operationName: string
55
96
  ): Promise<T> {
56
97
  let lastError: any;
57
-
98
+
58
99
  for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
59
100
  try {
60
101
  return await operation();
61
102
  } catch (error: any) {
62
103
  lastError = error;
63
104
  this.log.warn(`${operationName} attempt ${attempt} failed: ${error.message || error}`);
64
-
105
+
65
106
  if (attempt < this.retryAttempts) {
66
- await new Promise(resolve =>
107
+ await new Promise(resolve =>
67
108
  setTimeout(resolve, this.retryDelay * attempt)
68
109
  );
69
110
  }
70
111
  }
71
112
  }
72
-
73
- throw lastError;
74
- }
75
113
 
76
- /**
77
- * Parse Python execution result with better error handling
78
- */
79
- private parsePythonResult(resp: any, operationName: string): any {
80
- const interpreted = interpretStandardResult(resp, {
81
- successMessage: `${operationName} succeeded`,
82
- failureMessage: `${operationName} failed`
83
- });
84
-
85
- if (interpreted.success) {
86
- return {
87
- ...interpreted.payload,
88
- success: true
89
- };
90
- }
91
-
92
- const output = bestEffortInterpretedText(interpreted) ?? '';
93
- if (output) {
94
- this.log.error(`Failed to parse ${operationName} result: ${output}`);
95
- }
96
-
97
- if (output.includes('ModuleNotFoundError')) {
98
- return { success: false, error: 'Reflection module not available.' };
99
- }
100
- if (output.includes('AttributeError')) {
101
- return { success: false, error: 'Reflection API method not found. Check Unreal Engine version compatibility.' };
102
- }
103
-
104
- return {
105
- success: false,
106
- error: `${interpreted.error ?? `${operationName} did not return a valid result`}: ${output.substring(0, 200)}`
107
- };
114
+ throw lastError;
108
115
  }
109
116
 
110
117
  /**
@@ -134,6 +141,197 @@ export class IntrospectionTools {
134
141
  return value;
135
142
  }
136
143
 
144
+ private isPlainObject(value: any): value is Record<string, any> {
145
+ return !!value && typeof value === 'object' && !Array.isArray(value);
146
+ }
147
+
148
+ private isLikelyPropertyDescriptor(value: any): boolean {
149
+ if (!this.isPlainObject(value)) return false;
150
+ if (typeof value.name === 'string' && ('value' in value || 'type' in value)) return true;
151
+ if ('propertyName' in value && ('currentValue' in value || 'defaultValue' in value)) return true;
152
+ return false;
153
+ }
154
+
155
+ private shouldFilterProperty(name: string, value: any, flags?: string[], detailed = false): boolean {
156
+ if (detailed) return false;
157
+ if (!name) return true;
158
+ const normalized = normalizeDictionaryKey(name);
159
+ if (this.ignoredPropertyKeys.has(normalized)) return true;
160
+ for (const pattern of this.propertyFilterPatterns) {
161
+ if (pattern.test(name)) return true;
162
+ }
163
+ if (Array.isArray(value) && value.length === 0) return true;
164
+ if (this.isPlainObject(value) && Object.keys(value).length === 0) return true;
165
+ if (flags?.some((f) => /deprecated/i.test(f))) return true;
166
+ return false;
167
+ }
168
+
169
+ private formatDisplayValue(value: any, type: string): string {
170
+ if (value === null || value === undefined) return 'None';
171
+ if (typeof value === 'string') return value.length > 120 ? `${value.slice(0, 117)}...` : value;
172
+ if (typeof value === 'number' || typeof value === 'boolean') return `${value}`;
173
+ if (Array.isArray(value)) {
174
+ const preview = value.slice(0, 5).map((entry) => this.formatDisplayValue(entry, typeof entry));
175
+ const suffix = value.length > 5 ? `, … (+${value.length - 5})` : '';
176
+ return `[${preview.join(', ')}${suffix}]`;
177
+ }
178
+ if (this.isPlainObject(value)) {
179
+ const keys = Object.keys(value);
180
+ if (['vector', 'rotator', 'transform'].some((token) => type.toLowerCase().includes(token))) {
181
+ const printable = Object.entries(value)
182
+ .map(([k, v]) => `${k}: ${this.formatDisplayValue(v, typeof v)}`)
183
+ .join(', ');
184
+ return `{ ${printable} }`;
185
+ }
186
+ const preview = keys.slice(0, 5).map((k) => `${k}: ${this.formatDisplayValue(value[k], typeof value[k])}`);
187
+ const suffix = keys.length > 5 ? `, … (+${keys.length - 5} keys)` : '';
188
+ return `{ ${preview.join(', ')}${suffix} }`;
189
+ }
190
+ return String(value);
191
+ }
192
+
193
+ private normalizePropertyEntry(entry: any, detailed = false): PropertyInfo | null {
194
+ if (!entry) return null;
195
+ const name: string = entry.name ?? entry.propertyName ?? entry.key ?? '';
196
+ if (!name) return null;
197
+
198
+ const candidateType = entry.type ?? entry.propertyType ?? (entry.value !== undefined ? typeof entry.value : undefined);
199
+ const type = typeof candidateType === 'string' && candidateType.length > 0 ? candidateType : 'unknown';
200
+ const rawValue = entry.value ?? entry.currentValue ?? entry.defaultValue ?? entry.data ?? entry;
201
+ const value = this.convertPropertyValue(rawValue, type);
202
+ const flags: string[] | undefined = entry.flags ?? entry.attributes;
203
+ const metadata: Record<string, any> | undefined = entry.metadata ?? entry.annotations;
204
+ const filtered = this.shouldFilterProperty(name, value, flags, detailed);
205
+ const dictionaryEntry = lookupPropertyMetadata(name);
206
+ const propertyInfo: PropertyInfo = {
207
+ name,
208
+ type,
209
+ value,
210
+ flags,
211
+ metadata,
212
+ category: dictionaryEntry?.category ?? entry.category,
213
+ tooltip: entry.tooltip ?? entry.helpText,
214
+ description: dictionaryEntry?.description ?? entry.description,
215
+ displayValue: this.formatDisplayValue(value, type),
216
+ dictionaryEntry,
217
+ isReadOnly: Boolean(entry.isReadOnly || entry.readOnly || flags?.some((f) => f.toLowerCase().includes('readonly')))
218
+ };
219
+ (propertyInfo as any).__filtered = filtered;
220
+ return propertyInfo;
221
+ }
222
+
223
+ private flattenPropertyMap(source: Record<string, any>, prefix = '', detailed = false): PropertyInfo[] {
224
+ const properties: PropertyInfo[] = [];
225
+ for (const [rawKey, rawValue] of Object.entries(source)) {
226
+ const name = prefix ? `${prefix}.${rawKey}` : rawKey;
227
+ if (this.isLikelyPropertyDescriptor(rawValue)) {
228
+ const normalized = this.normalizePropertyEntry({ ...rawValue, name }, detailed);
229
+ if (normalized) properties.push(normalized);
230
+ continue;
231
+ }
232
+
233
+ if (this.isPlainObject(rawValue)) {
234
+ const nestedKeys = Object.keys(rawValue);
235
+ const hasPrimitiveChildren = nestedKeys.some((key) => {
236
+ const child = rawValue[key];
237
+ return child === null || typeof child !== 'object' || Array.isArray(child) || this.isLikelyPropertyDescriptor(child);
238
+ });
239
+
240
+ if (hasPrimitiveChildren) {
241
+ const normalized = this.normalizePropertyEntry({ name, value: rawValue }, detailed);
242
+ if (normalized) properties.push(normalized);
243
+ } else {
244
+ properties.push(...this.flattenPropertyMap(rawValue, name, detailed));
245
+ }
246
+ continue;
247
+ }
248
+
249
+ const normalized = this.normalizePropertyEntry({ name, value: rawValue }, detailed);
250
+ if (normalized) properties.push(normalized);
251
+ }
252
+ return properties;
253
+ }
254
+
255
+ private extractRawProperties(rawInfo: any, detailed = false): PropertyInfo[] {
256
+ if (!rawInfo) return [];
257
+ if (Array.isArray(rawInfo.properties)) {
258
+ const entries = rawInfo.properties as Array<Record<string, unknown>>;
259
+ return entries
260
+ .map((entry) => this.normalizePropertyEntry(entry, detailed))
261
+ .filter((entry): entry is PropertyInfo => Boolean(entry));
262
+ }
263
+
264
+ if (this.isPlainObject(rawInfo.properties)) {
265
+ return this.flattenPropertyMap(rawInfo.properties, '', detailed);
266
+ }
267
+
268
+ if (Array.isArray(rawInfo)) {
269
+ const entries = rawInfo as Array<Record<string, unknown>>;
270
+ return entries
271
+ .map((entry) => this.normalizePropertyEntry(entry, detailed))
272
+ .filter((entry): entry is PropertyInfo => Boolean(entry));
273
+ }
274
+
275
+ if (this.isPlainObject(rawInfo)) {
276
+ const shallow = { ...rawInfo };
277
+ delete shallow.properties;
278
+ delete shallow.functions;
279
+ delete shallow.summary;
280
+ return this.flattenPropertyMap(shallow, '', detailed);
281
+ }
282
+
283
+ return [];
284
+ }
285
+
286
+ curateObjectInfo(rawInfo: any, objectPath: string, detailed = false): ObjectInfo {
287
+ const properties = this.extractRawProperties(rawInfo, detailed);
288
+ const filteredProperties: string[] = [];
289
+ const curatedProperties = properties.filter((prop) => {
290
+ const shouldFilter = (prop as any).__filtered;
291
+ delete (prop as any).__filtered;
292
+ if (shouldFilter) {
293
+ filteredProperties.push(prop.name);
294
+ }
295
+ return !shouldFilter;
296
+ });
297
+
298
+ const categories: Record<string, number> = {};
299
+ const finalList = (detailed ? properties : curatedProperties).map((prop) => {
300
+ const category = (prop.category ?? prop.dictionaryEntry?.category ?? 'General');
301
+ categories[category] = (categories[category] ?? 0) + 1;
302
+ return prop;
303
+ });
304
+
305
+ const summary: ObjectSummary = {
306
+ name: rawInfo?.name ?? rawInfo?.objectName ?? rawInfo?.displayName,
307
+ class: rawInfo?.class ?? rawInfo?.className ?? rawInfo?.type ?? rawInfo?.objectClass,
308
+ path: rawInfo?.path ?? rawInfo?.objectPath ?? objectPath,
309
+ parent: rawInfo?.outer ?? rawInfo?.parent,
310
+ tags: Array.isArray(rawInfo?.tags) ? rawInfo.tags : undefined,
311
+ propertyCount: properties.length,
312
+ curatedPropertyCount: curatedProperties.length,
313
+ filteredPropertyCount: filteredProperties.length,
314
+ categories
315
+ };
316
+
317
+ const info: ObjectInfo = {
318
+ class: summary.class,
319
+ name: summary.name,
320
+ path: summary.path,
321
+ parent: summary.parent,
322
+ tags: summary.tags,
323
+ properties: finalList,
324
+ functions: Array.isArray(rawInfo?.functions) ? rawInfo.functions : undefined,
325
+ interfaces: Array.isArray(rawInfo?.interfaces) ? rawInfo.interfaces : undefined,
326
+ flags: Array.isArray(rawInfo?.flags) ? rawInfo.flags : undefined,
327
+ summary,
328
+ filteredProperties: filteredProperties.length ? filteredProperties : undefined,
329
+ original: rawInfo
330
+ };
331
+
332
+ return info;
333
+ }
334
+
137
335
  async inspectObject(params: { objectPath: string; detailed?: boolean }) {
138
336
  // Check cache first if not requesting detailed info
139
337
  if (!params.detailed && this.objectCache.has(params.objectPath)) {
@@ -142,256 +340,60 @@ export class IntrospectionTools {
142
340
  return { success: true, info: cached };
143
341
  }
144
342
  }
145
-
146
- const py = `
147
- import unreal, json, inspect
148
- path = r"${params.objectPath}"
149
- detailed = ${params.detailed ? 'True' : 'False'}
150
-
151
- def get_property_info(prop, obj=None):
152
- """Extract detailed property information"""
153
- try:
154
- info = {
155
- 'name': prop.get_name(),
156
- 'type': prop.get_property_class_name() if hasattr(prop, 'get_property_class_name') else 'Unknown'
157
- }
158
-
159
- # Try to get property flags
160
- flags = []
161
- if hasattr(prop, 'has_any_property_flags'):
162
- if prop.has_any_property_flags(unreal.PropertyFlags.CPF_EDIT_CONST):
163
- flags.append('ReadOnly')
164
- if prop.has_any_property_flags(unreal.PropertyFlags.CPF_BLUEPRINT_READ_ONLY):
165
- flags.append('BlueprintReadOnly')
166
- if prop.has_any_property_flags(unreal.PropertyFlags.CPF_TRANSIENT):
167
- flags.append('Transient')
168
- info['flags'] = flags
169
-
170
- # Try to get metadata
171
- if hasattr(prop, 'get_metadata'):
172
- try:
173
- info['category'] = prop.get_metadata('Category')
174
- info['tooltip'] = prop.get_metadata('ToolTip')
175
- except Exception:
176
- pass
177
-
178
- # Try to get current value if object provided
179
- if obj and detailed:
180
- try:
181
- value = getattr(obj, prop.get_name())
182
- # Convert complex types to serializable format
183
- if hasattr(value, '__dict__'):
184
- value = str(value)
185
- info['value'] = value
186
- except Exception:
187
- pass
188
-
189
- return info
190
- except Exception as e:
191
- return {'name': str(prop) if prop else 'Unknown', 'type': 'Unknown', 'error': str(e)}
192
-
193
- try:
194
- obj = unreal.load_object(None, path)
195
- if not obj:
196
- # Try as class if object load fails
197
- try:
198
- obj = unreal.load_class(None, path)
199
- if not obj:
200
- print('RESULT:' + json.dumps({'success': False, 'error': 'Object or class not found'}))
201
- raise SystemExit(0)
202
- except Exception:
203
- print('RESULT:' + json.dumps({'success': False, 'error': 'Object not found'}))
204
- raise SystemExit(0)
205
-
206
- info = {
207
- 'class': obj.get_class().get_name() if hasattr(obj, 'get_class') else str(type(obj)),
208
- 'name': obj.get_name() if hasattr(obj, 'get_name') else '',
209
- 'path': path,
210
- 'properties': [],
211
- 'functions': [],
212
- 'flags': []
213
- }
214
-
215
- # Get parent class
216
- try:
217
- if hasattr(obj, 'get_class'):
218
- cls = obj.get_class()
219
- if hasattr(cls, 'get_super_class'):
220
- super_cls = cls.get_super_class()
221
- if super_cls:
222
- info['parent'] = super_cls.get_name()
223
- except Exception:
224
- pass
225
-
226
- # Get object flags
227
- try:
228
- if hasattr(obj, 'has_any_flags'):
229
- flags = []
230
- if obj.has_any_flags(unreal.ObjectFlags.RF_PUBLIC):
231
- flags.append('Public')
232
- if obj.has_any_flags(unreal.ObjectFlags.RF_TRANSIENT):
233
- flags.append('Transient')
234
- if obj.has_any_flags(unreal.ObjectFlags.RF_DEFAULT_SUB_OBJECT):
235
- flags.append('DefaultSubObject')
236
- info['flags'] = flags
237
- except Exception:
238
- pass
239
-
240
- # Get properties - AVOID deprecated properties completely
241
- props = []
242
-
243
- # List of deprecated properties to completely skip
244
- deprecated_props = [
245
- 'life_span', 'on_actor_touch', 'on_actor_un_touch',
246
- 'get_touching_actors', 'get_touching_components',
247
- 'controller_class', 'look_up_scale', 'sound_wave_param',
248
- 'material_substitute', 'texture_object'
249
- ]
250
-
251
- try:
252
- cls = obj.get_class() if hasattr(obj, 'get_class') else obj
253
-
254
- # Try UE5 reflection API with safe property access
255
- if hasattr(cls, 'get_properties'):
256
- for prop in cls.get_properties():
257
- prop_name = None
258
- try:
259
- # Safe property name extraction
260
- if hasattr(prop, 'get_name'):
261
- prop_name = prop.get_name()
262
- elif hasattr(prop, 'name'):
263
- prop_name = prop.name
264
-
265
- # Skip if deprecated
266
- if prop_name and prop_name not in deprecated_props:
267
- prop_info = get_property_info(prop, obj if detailed else None)
268
- if prop_info.get('name') not in deprecated_props:
269
- props.append(prop_info)
270
- except Exception:
271
- pass
272
-
273
- # If reflection API didn't work, use a safe property list
274
- if not props:
275
- # Only access known safe properties
276
- safe_properties = [
277
- 'actor_guid', 'actor_instance_guid', 'always_relevant',
278
- 'auto_destroy_when_finished', 'can_be_damaged',
279
- 'content_bundle_guid', 'custom_time_dilation',
280
- 'enable_auto_lod_generation', 'find_camera_component_when_view_target',
281
- 'generate_overlap_events_during_level_streaming', 'hidden',
282
- 'initial_life_span', # Use new name instead of life_span
283
- 'instigator', 'is_spatially_loaded', 'min_net_update_frequency',
284
- 'net_cull_distance_squared', 'net_dormancy', 'net_priority',
285
- 'net_update_frequency', 'net_use_owner_relevancy',
286
- 'only_relevant_to_owner', 'pivot_offset',
287
- 'replicate_using_registered_sub_object_list', 'replicates',
288
- 'root_component', 'runtime_grid', 'spawn_collision_handling_method',
289
- 'sprite_scale', 'tags', 'location', 'rotation', 'scale'
290
- ]
291
-
292
- for prop_name in safe_properties:
293
- try:
294
- # Use get_editor_property for safer access
295
- if hasattr(obj, 'get_editor_property'):
296
- val = obj.get_editor_property(prop_name)
297
- props.append({
298
- 'name': prop_name,
299
- 'type': type(val).__name__ if val is not None else 'None',
300
- 'value': str(val)[:100] if detailed and val is not None else None
301
- })
302
- elif hasattr(obj, prop_name):
303
- # Direct access only for safe properties
304
- val = getattr(obj, prop_name)
305
- if not callable(val):
306
- props.append({
307
- 'name': prop_name,
308
- 'type': type(val).__name__,
309
- 'value': str(val)[:100] if detailed else None
310
- })
311
- except Exception:
312
- pass
313
- except Exception as e:
314
- # Minimal fallback with only essential safe properties
315
- pass
316
-
317
- info['properties'] = props
318
-
319
- # Get functions/methods if detailed
320
- if detailed:
321
- funcs = []
322
- try:
323
- cls = obj.get_class() if hasattr(obj, 'get_class') else obj
324
-
325
- # Try to get UFunctions
326
- if hasattr(cls, 'get_functions'):
327
- for func in cls.get_functions():
328
- func_info = {
329
- 'name': func.get_name(),
330
- 'parameters': [],
331
- 'flags': []
332
- }
333
- # Get parameters if possible
334
- if hasattr(func, 'get_params'):
335
- for param in func.get_params():
336
- func_info['parameters'].append({
337
- 'name': param.get_name() if hasattr(param, 'get_name') else str(param),
338
- 'type': 'Unknown'
339
- })
340
- funcs.append(func_info)
341
- else:
342
- # Fallback: use known safe function names
343
- safe_functions = [
344
- 'get_actor_location', 'set_actor_location',
345
- 'get_actor_rotation', 'set_actor_rotation',
346
- 'get_actor_scale', 'set_actor_scale',
347
- 'destroy_actor', 'destroy_component',
348
- 'get_components', 'get_component_by_class',
349
- 'add_actor_component', 'add_component',
350
- 'get_world', 'get_name', 'get_path_name',
351
- 'is_valid', 'is_a', 'has_authority'
352
- ]
353
- for func_name in safe_functions:
354
- if hasattr(obj, func_name):
355
- try:
356
- attr_value = getattr(obj, func_name)
357
- if callable(attr_value):
358
- funcs.append({
359
- 'name': func_name,
360
- 'parameters': [],
361
- 'flags': []
362
- })
363
- except Exception:
364
- pass
365
- except Exception:
366
- pass
367
-
368
- info['functions'] = funcs
369
-
370
- print('RESULT:' + json.dumps({'success': True, 'info': info}))
371
- except Exception as e:
372
- print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
373
- `.trim();
374
- const resp = await this.executeWithRetry(
375
- () => this.bridge.executePython(py),
376
- 'inspectObject'
377
- );
378
-
379
- const result = this.parsePythonResult(resp, 'inspectObject');
380
-
381
- // Cache the result if successful and not detailed
382
- if (result.success && result.info && !params.detailed) {
383
- this.objectCache.set(params.objectPath, result.info);
343
+
344
+ if (!this.automationBridge) {
345
+ throw new Error('Automation Bridge not available. Introspection operations require plugin support.');
384
346
  }
385
-
386
- return result;
347
+
348
+ const automationBridge = this.automationBridge;
349
+ return this.executeWithRetry(async () => {
350
+ try {
351
+ const response = await automationBridge.sendAutomationRequest('inspect_object', {
352
+ objectPath: params.objectPath,
353
+ detailed: params.detailed ?? false
354
+ }, {
355
+ timeoutMs: 60000
356
+ });
357
+
358
+ if (response.success === false) {
359
+ return {
360
+ success: false,
361
+ error: response.error || response.message || 'Failed to inspect object'
362
+ };
363
+ }
364
+
365
+ const rawInfo = response.info ?? response.result ?? response.data ?? response;
366
+ const curatedInfo = this.curateObjectInfo(rawInfo, params.objectPath, params.detailed ?? false);
367
+
368
+ const result = {
369
+ success: true,
370
+ info: curatedInfo
371
+ };
372
+
373
+ // Cache the result if successful and not detailed
374
+ if (result.success && result.info && !params.detailed) {
375
+ this.objectCache.set(params.objectPath, result.info as ObjectInfo);
376
+ }
377
+
378
+ return result;
379
+ } catch (error) {
380
+ return {
381
+ success: false,
382
+ error: `Failed to inspect object: ${error instanceof Error ? error.message : String(error)}`
383
+ };
384
+ }
385
+ }, 'inspectObject');
387
386
  }
388
387
 
388
+ /**
389
+ * Set property value on an object
390
+ */
389
391
  async setProperty(params: { objectPath: string; propertyName: string; value: any }) {
390
392
  return this.executeWithRetry(async () => {
391
393
  try {
392
394
  // Validate and convert value type if needed
393
395
  let processedValue = params.value;
394
-
396
+
395
397
  // Handle special Unreal types
396
398
  if (typeof params.value === 'object' && params.value !== null) {
397
399
  // Vector conversion
@@ -422,23 +424,23 @@ except Exception as e:
422
424
  'Rotator'
423
425
  ),
424
426
  Scale3D: this.convertPropertyValue(
425
- params.value.scale || params.value.Scale || {x: 1, y: 1, z: 1},
427
+ params.value.scale || params.value.Scale || { x: 1, y: 1, z: 1 },
426
428
  'Vector'
427
429
  )
428
430
  };
429
431
  }
430
432
  }
431
-
432
- const res = await this.bridge.httpCall('/remote/object/property', 'PUT', {
433
+ const res = await this.bridge.setObjectProperty({
433
434
  objectPath: params.objectPath,
434
435
  propertyName: params.propertyName,
435
- propertyValue: processedValue
436
+ value: processedValue
436
437
  });
437
-
438
- // Clear cache for this object
439
- this.objectCache.delete(params.objectPath);
440
-
441
- return { success: true, result: res };
438
+
439
+ if (res.success) {
440
+ this.objectCache.delete(params.objectPath);
441
+ }
442
+
443
+ return res;
442
444
  } catch (err: any) {
443
445
  const errorMsg = err?.message || String(err);
444
446
  if (errorMsg.includes('404')) {
@@ -456,77 +458,17 @@ except Exception as e:
456
458
  * Get property value of an object
457
459
  */
458
460
  async getProperty(params: { objectPath: string; propertyName: string }) {
459
- const py = `
460
- import unreal, json
461
- path = r"${params.objectPath}"
462
- prop_name = r"${params.propertyName}"
463
- try:
464
- obj = unreal.load_object(None, path)
465
- if not obj:
466
- print('RESULT:' + json.dumps({'success': False, 'error': 'Object not found'}))
467
- else:
468
- # Try different methods to get property
469
- value = None
470
- found = False
471
-
472
- # Method 1: Direct attribute access
473
- if hasattr(obj, prop_name):
474
- try:
475
- value = getattr(obj, prop_name)
476
- found = True
477
- except Exception:
478
- pass
479
-
480
- # Method 2: get_editor_property (UE4/5)
481
- if not found and hasattr(obj, 'get_editor_property'):
482
- try:
483
- value = obj.get_editor_property(prop_name)
484
- found = True
485
- except Exception:
486
- pass
487
-
488
- # Method 3: Try with common property name variations
489
- if not found:
490
- # Try common property name variations
491
- variations = [
492
- prop_name,
493
- prop_name.lower(),
494
- prop_name.upper(),
495
- prop_name.capitalize(),
496
- # Convert snake_case to CamelCase
497
- ''.join(word.capitalize() for word in prop_name.split('_')),
498
- # Convert CamelCase to snake_case
499
- ''.join(['_' + c.lower() if c.isupper() else c for c in prop_name]).lstrip('_')
500
- ]
501
- for variant in variations:
502
- if hasattr(obj, variant):
503
- try:
504
- value = getattr(obj, variant)
505
- found = True
506
- break
507
- except Exception:
508
- pass
509
-
510
- if found:
511
- # Convert complex types to string
512
- if hasattr(value, '__dict__'):
513
- value = str(value)
514
- elif isinstance(value, (list, tuple, dict)):
515
- value = json.dumps(value)
516
-
517
- print('RESULT:' + json.dumps({'success': True, 'value': value}))
518
- else:
519
- print('RESULT:' + json.dumps({'success': False, 'error': f'Property {prop_name} not found'}))
520
- except Exception as e:
521
- print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
522
- `.trim();
523
-
524
- const resp = await this.executeWithRetry(
525
- () => this.bridge.executePython(py),
461
+ return this.executeWithRetry(
462
+ async () => {
463
+ const result = await this.bridge.getObjectProperty({
464
+ objectPath: params.objectPath,
465
+ propertyName: params.propertyName
466
+ });
467
+
468
+ return result;
469
+ },
526
470
  'getProperty'
527
471
  );
528
-
529
- return this.parsePythonResult(resp, 'getProperty');
530
472
  }
531
473
 
532
474
  /**
@@ -537,238 +479,205 @@ except Exception as e:
537
479
  functionName: string;
538
480
  parameters?: any[];
539
481
  }) {
540
- const py = `
541
- import unreal, json
542
- path = r"${params.objectPath}"
543
- func_name = r"${params.functionName}"
544
- params = ${JSON.stringify(params.parameters || [])}
545
- try:
546
- obj = unreal.load_object(None, path)
547
- if not obj:
548
- # Try loading as class if object fails
549
- try:
550
- obj = unreal.load_class(None, path)
551
- except:
552
- pass
553
-
554
- if not obj:
555
- print('RESULT:' + json.dumps({'success': False, 'error': 'Object not found'}))
556
- else:
557
- # For KismetMathLibrary or similar utility classes, use static method call
558
- if 'KismetMathLibrary' in path or 'MathLibrary' in path or 'GameplayStatics' in path:
559
- try:
560
- # Use Unreal's MathLibrary (KismetMathLibrary is exposed as MathLibrary in Python)
561
- if func_name.lower() == 'abs':
562
- # Use Unreal's MathLibrary.abs function
563
- result = unreal.MathLibrary.abs(float(params[0])) if params else 0
564
- print('RESULT:' + json.dumps({'success': True, 'result': result}))
565
- elif func_name.lower() == 'sqrt':
566
- # Use Unreal's MathLibrary.sqrt function
567
- result = unreal.MathLibrary.sqrt(float(params[0])) if params else 0
568
- print('RESULT:' + json.dumps({'success': True, 'result': result}))
569
- else:
570
- # Try to call as static method
571
- if hasattr(obj, func_name):
572
- func = getattr(obj, func_name)
573
- if callable(func):
574
- result = func(*params) if params else func()
575
- if hasattr(result, '__dict__'):
576
- result = str(result)
577
- print('RESULT:' + json.dumps({'success': True, 'result': result}))
578
- else:
579
- print('RESULT:' + json.dumps({'success': False, 'error': f'{func_name} is not callable'}))
580
- else:
581
- # Try snake_case version
582
- snake_case_name = ''.join(['_' + c.lower() if c.isupper() else c for c in func_name]).lstrip('_')
583
- if hasattr(obj, snake_case_name):
584
- func = getattr(obj, snake_case_name)
585
- result = func(*params) if params else func()
586
- print('RESULT:' + json.dumps({'success': True, 'result': result}))
587
- else:
588
- print('RESULT:' + json.dumps({'success': False, 'error': f'Function {func_name} not found'}))
589
- except Exception as e:
590
- print('RESULT:' + json.dumps({'success': False, 'error': f'Function call failed: {str(e)}'}))
591
- else:
592
- # Regular object method call
593
- if hasattr(obj, func_name):
594
- func = getattr(obj, func_name)
595
- if callable(func):
596
- try:
597
- result = func(*params) if params else func()
598
- # Convert result to serializable format
599
- if hasattr(result, '__dict__'):
600
- result = str(result)
601
- print('RESULT:' + json.dumps({'success': True, 'result': result}))
602
- except Exception as e:
603
- print('RESULT:' + json.dumps({'success': False, 'error': f'Function call failed: {str(e)}'}))
604
- else:
605
- print('RESULT:' + json.dumps({'success': False, 'error': f'{func_name} is not callable'}))
606
- else:
607
- print('RESULT:' + json.dumps({'success': False, 'error': f'Function {func_name} not found'}))
608
- except Exception as e:
609
- print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
610
- `.trim();
611
-
612
- const resp = await this.executeWithRetry(
613
- () => this.bridge.executePython(py),
614
- 'callFunction'
615
- );
616
-
617
- return this.parsePythonResult(resp, 'callFunction');
482
+ if (!this.automationBridge) {
483
+ throw new Error('Automation Bridge not available. Function call operations require plugin support.');
484
+ }
485
+
486
+ const automationBridge = this.automationBridge;
487
+ return this.executeWithRetry(async () => {
488
+ try {
489
+ const response = await automationBridge.sendAutomationRequest('call_object_function', {
490
+ objectPath: params.objectPath,
491
+ functionName: params.functionName,
492
+ parameters: params.parameters || []
493
+ }, {
494
+ timeoutMs: 60000
495
+ });
496
+
497
+ if (response.success === false) {
498
+ return {
499
+ success: false,
500
+ error: response.error || response.message || 'Failed to call function'
501
+ };
502
+ }
503
+
504
+ return {
505
+ success: true,
506
+ result: response.result
507
+ };
508
+ } catch (error) {
509
+ return {
510
+ success: false,
511
+ error: `Failed to call function: ${error instanceof Error ? error.message : String(error)}`
512
+ };
513
+ }
514
+ }, 'callFunction');
618
515
  }
619
516
 
620
517
  /**
621
518
  * Get Class Default Object (CDO) for a class
622
519
  */
623
520
  async getCDO(className: string) {
624
- const py = `
625
- import unreal, json
626
- class_name = r"${className}"
627
- try:
628
- # Try to find the class
629
- cls = None
630
-
631
- # Method 1: Direct class load
632
- try:
633
- cls = unreal.load_class(None, class_name)
634
- except Exception:
635
- pass
636
-
637
- # Method 2: Find class by name
638
- if not cls:
639
- try:
640
- cls = unreal.find_class(class_name)
641
- except Exception:
642
- pass
643
-
644
- # Method 3: Search in loaded classes
645
- if not cls:
646
- for obj in unreal.ObjectLibrary.get_all_objects():
647
- if hasattr(obj, 'get_class'):
648
- obj_cls = obj.get_class()
649
- if obj_cls.get_name() == class_name:
650
- cls = obj_cls
651
- break
652
-
653
- if not cls:
654
- print('RESULT:' + json.dumps({'success': False, 'error': 'Class not found'}))
655
- else:
656
- # Get CDO
657
- cdo = cls.get_default_object() if hasattr(cls, 'get_default_object') else None
658
-
659
- if cdo:
660
- info = {
661
- 'className': cls.get_name(),
662
- 'cdoPath': cdo.get_path_name() if hasattr(cdo, 'get_path_name') else '',
663
- 'properties': []
664
- }
665
-
666
- # Get default property values using safe property list
667
- safe_cdo_properties = [
668
- 'initial_life_span', 'hidden', 'can_be_damaged', 'replicates',
669
- 'always_relevant', 'net_dormancy', 'net_priority',
670
- 'net_update_frequency', 'replicate_movement',
671
- 'actor_guid', 'tags', 'root_component',
672
- 'auto_destroy_when_finished', 'enable_auto_lod_generation'
673
- ]
674
- for prop_name in safe_cdo_properties:
675
- try:
676
- if hasattr(cdo, 'get_editor_property'):
677
- value = cdo.get_editor_property(prop_name)
678
- info['properties'].append({
679
- 'name': prop_name,
680
- 'defaultValue': str(value)[:100]
681
- })
682
- elif hasattr(cdo, prop_name):
683
- value = getattr(cdo, prop_name)
684
- if not callable(value):
685
- info['properties'].append({
686
- 'name': prop_name,
687
- 'defaultValue': str(value)[:100]
688
- })
689
- except Exception:
690
- pass
691
-
692
- print('RESULT:' + json.dumps({'success': True, 'cdo': info}))
693
- else:
694
- print('RESULT:' + json.dumps({'success': False, 'error': 'Could not get CDO'}))
695
- except Exception as e:
696
- print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
697
- `.trim();
698
-
699
- const resp = await this.executeWithRetry(
700
- () => this.bridge.executePython(py),
701
- 'getCDO'
702
- );
703
-
704
- return this.parsePythonResult(resp, 'getCDO');
521
+ if (!this.automationBridge) {
522
+ throw new Error('Automation Bridge not available. CDO operations require plugin support.');
523
+ }
524
+
525
+ const automationBridge = this.automationBridge;
526
+ return this.executeWithRetry(async () => {
527
+ try {
528
+ const response: any = await automationBridge.sendAutomationRequest('inspect', {
529
+ action: 'inspect_class',
530
+ // C++ plugin expects `classPath` for inspect_class, but accepts both
531
+ // short names and full paths (e.g. "Actor" or "/Script/Engine.Actor").
532
+ classPath: className
533
+ }, {
534
+ timeoutMs: 60000
535
+ });
536
+
537
+ if (response?.success === false) {
538
+ return {
539
+ success: false,
540
+ error: response.error || response.message || 'Failed to get CDO'
541
+ };
542
+ }
543
+
544
+ return {
545
+ success: true,
546
+ // Plugin returns class inspection data under data/result depending on bridge version.
547
+ cdo: response?.data ?? response?.result ?? response
548
+ };
549
+ } catch (error) {
550
+ return {
551
+ success: false,
552
+ error: `Failed to get CDO: ${error instanceof Error ? error.message : String(error)}`
553
+ };
554
+ }
555
+ }, 'getCDO');
705
556
  }
706
557
 
707
558
  /**
708
559
  * Search for objects by class
709
560
  */
710
561
  async findObjectsByClass(className: string, limit: number = 100) {
711
- const py = `
712
- import unreal, json
713
- class_name = r"${className}"
714
- limit = ${limit}
715
- try:
716
- objects = []
717
- count = 0
718
-
719
- # Use EditorAssetLibrary to find assets
720
- try:
721
- all_assets = unreal.EditorAssetLibrary.list_assets("/Game", recursive=True)
722
- for asset_path in all_assets:
723
- if count >= limit:
724
- break
725
- try:
726
- asset = unreal.EditorAssetLibrary.load_asset(asset_path)
727
- if asset:
728
- asset_class = asset.get_class() if hasattr(asset, 'get_class') else None
729
- if asset_class and class_name in asset_class.get_name():
730
- objects.append({
731
- 'path': asset_path,
732
- 'name': asset.get_name() if hasattr(asset, 'get_name') else '',
733
- 'class': asset_class.get_name()
734
- })
735
- count += 1
736
- except Exception:
737
- pass
738
- except Exception as e:
739
- print('RESULT:' + json.dumps({'success': False, 'error': f'Asset search failed: {str(e)}'}))
740
- raise SystemExit(0)
741
-
742
- # Also search in level actors
743
- try:
744
- actor_sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
745
- if actor_sub:
746
- for actor in actor_sub.get_all_level_actors():
747
- if count >= limit:
748
- break
749
- if actor:
750
- actor_class = actor.get_class() if hasattr(actor, 'get_class') else None
751
- if actor_class and class_name in actor_class.get_name():
752
- objects.append({
753
- 'path': actor.get_path_name() if hasattr(actor, 'get_path_name') else '',
754
- 'name': actor.get_actor_label() if hasattr(actor, 'get_actor_label') else '',
755
- 'class': actor_class.get_name()
756
- })
757
- count += 1
758
- except Exception:
759
- pass
760
-
761
- print('RESULT:' + json.dumps({'success': True, 'objects': objects, 'count': len(objects)}))
762
- except Exception as e:
763
- print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
764
- `.trim();
765
-
766
- const resp = await this.executeWithRetry(
767
- () => this.bridge.executePython(py),
768
- 'findObjectsByClass'
769
- );
770
-
771
- return this.parsePythonResult(resp, 'findObjectsByClass');
562
+ if (!this.automationBridge) {
563
+ throw new Error('Automation Bridge not available. Object search operations require plugin support.');
564
+ }
565
+
566
+ const automationBridge = this.automationBridge;
567
+ return this.executeWithRetry(async () => {
568
+ try {
569
+ const response: any = await automationBridge.sendAutomationRequest('inspect', {
570
+ action: 'find_by_class',
571
+ className,
572
+ limit
573
+ }, {
574
+ timeoutMs: 60000
575
+ });
576
+
577
+ if (response?.success === false) {
578
+ return {
579
+ success: false,
580
+ error: response.error || response.message || 'Failed to find objects'
581
+ };
582
+ }
583
+
584
+ const data = response?.data ?? response?.result ?? response;
585
+ const validObjects = Array.isArray(data?.actors)
586
+ ? data.actors
587
+ : (Array.isArray(data?.objects) ? data.objects : (Array.isArray(data) ? data : []));
588
+ return {
589
+ success: true,
590
+ message: `Found ${validObjects.length} objects`,
591
+ objects: validObjects,
592
+ count: validObjects.length
593
+ };
594
+ } catch (error) {
595
+ return {
596
+ success: false,
597
+ error: `Failed to find objects: ${error instanceof Error ? error.message : String(error)}`
598
+ };
599
+ }
600
+ }, 'findObjectsByClass');
601
+ }
602
+
603
+ /**
604
+ * Get property value of a component
605
+ */
606
+ async getComponentProperty(params: { objectPath: string; componentName: string; propertyName: string }) {
607
+ if (!this.automationBridge) {
608
+ throw new Error('Automation Bridge not available. Component property operations require plugin support.');
609
+ }
610
+
611
+ const automationBridge = this.automationBridge;
612
+ return this.executeWithRetry(async () => {
613
+ try {
614
+ const response = await automationBridge.sendAutomationRequest('get_component_property', {
615
+ objectPath: params.objectPath,
616
+ componentName: params.componentName,
617
+ propertyName: params.propertyName
618
+ }, {
619
+ timeoutMs: 15000
620
+ });
621
+
622
+ if (response.success === false) {
623
+ return {
624
+ success: false,
625
+ error: response.error || response.message || 'Failed to get component property'
626
+ };
627
+ }
628
+
629
+ return {
630
+ success: true,
631
+ value: response.value,
632
+ type: response.type
633
+ };
634
+ } catch (error) {
635
+ return {
636
+ success: false,
637
+ error: `Failed to get component property: ${error instanceof Error ? error.message : String(error)}`
638
+ };
639
+ }
640
+ }, 'getComponentProperty');
641
+ }
642
+
643
+ /**
644
+ * Set property value of a component
645
+ */
646
+ async setComponentProperty(params: { objectPath: string; componentName: string; propertyName: string; value: any }) {
647
+ if (!this.automationBridge) {
648
+ throw new Error('Automation Bridge not available. Component property operations require plugin support.');
649
+ }
650
+
651
+ const automationBridge = this.automationBridge;
652
+ return this.executeWithRetry(async () => {
653
+ try {
654
+ const response = await automationBridge.sendAutomationRequest('set_component_property', {
655
+ objectPath: params.objectPath,
656
+ componentName: params.componentName,
657
+ propertyName: params.propertyName,
658
+ value: params.value
659
+ }, {
660
+ timeoutMs: 15000
661
+ });
662
+
663
+ if (response.success === false) {
664
+ return {
665
+ success: false,
666
+ error: response.error || response.message || 'Failed to set component property'
667
+ };
668
+ }
669
+
670
+ return {
671
+ success: true,
672
+ message: response.message || 'Property set successfully'
673
+ };
674
+ } catch (error) {
675
+ return {
676
+ success: false,
677
+ error: `Failed to set component property: ${error instanceof Error ? error.message : String(error)}`
678
+ };
679
+ }
680
+ }, 'setComponentProperty');
772
681
  }
773
682
 
774
683
  /**