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