unreal-engine-mcp-server 0.5.4 → 0.5.5
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/dist/automation/bridge.d.ts.map +1 -0
- package/dist/automation/bridge.js.map +1 -0
- package/dist/automation/connection-manager.d.ts.map +1 -0
- package/dist/automation/connection-manager.js.map +1 -0
- package/dist/automation/handshake.d.ts.map +1 -0
- package/dist/automation/handshake.js.map +1 -0
- package/dist/automation/index.d.ts.map +1 -0
- package/dist/automation/index.js.map +1 -0
- package/dist/automation/message-handler.d.ts.map +1 -0
- package/dist/automation/message-handler.js.map +1 -0
- package/dist/automation/request-tracker.d.ts.map +1 -0
- package/dist/automation/request-tracker.js.map +1 -0
- package/dist/automation/types.d.ts.map +1 -0
- package/dist/automation/types.js.map +1 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +4 -3
- package/dist/cli.js.map +1 -0
- package/dist/config/class-aliases.d.ts.map +1 -0
- package/dist/config/class-aliases.js.map +1 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js.map +1 -0
- package/dist/graphql/loaders.d.ts.map +1 -0
- package/dist/graphql/loaders.js.map +1 -0
- package/dist/graphql/resolvers.d.ts.map +1 -0
- package/dist/graphql/resolvers.js +29 -29
- package/dist/graphql/resolvers.js.map +1 -0
- package/dist/graphql/schema.d.ts.map +1 -0
- package/dist/graphql/schema.js.map +1 -0
- package/dist/graphql/server.d.ts.map +1 -0
- package/dist/graphql/server.js.map +1 -0
- package/dist/graphql/types.d.ts.map +1 -0
- package/dist/graphql/types.js.map +1 -0
- package/dist/handlers/resource-handlers.d.ts.map +1 -0
- package/dist/handlers/resource-handlers.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -7
- package/dist/index.js.map +1 -0
- package/dist/resources/actors.d.ts.map +1 -0
- package/dist/resources/actors.js.map +1 -0
- package/dist/resources/assets.d.ts.map +1 -0
- package/dist/resources/assets.js +6 -4
- package/dist/resources/assets.js.map +1 -0
- package/dist/resources/levels.d.ts.map +1 -0
- package/dist/resources/levels.js.map +1 -0
- package/dist/server/resource-registry.d.ts.map +1 -0
- package/dist/server/resource-registry.js.map +1 -0
- package/dist/server/tool-registry.d.ts.map +1 -0
- package/dist/server/tool-registry.js.map +1 -0
- package/dist/server-setup.d.ts.map +1 -0
- package/dist/server-setup.js.map +1 -0
- package/dist/services/health-monitor.d.ts.map +1 -0
- package/dist/services/health-monitor.js.map +1 -0
- package/dist/services/metrics-server.d.ts.map +1 -0
- package/dist/services/metrics-server.js.map +1 -0
- package/dist/tools/actors.d.ts.map +1 -0
- package/dist/tools/actors.js +3 -1
- package/dist/tools/actors.js.map +1 -0
- package/dist/tools/animation.d.ts.map +1 -0
- package/dist/tools/animation.js +2 -2
- package/dist/tools/animation.js.map +1 -0
- package/dist/tools/assets.d.ts.map +1 -0
- package/dist/tools/assets.js.map +1 -0
- package/dist/tools/audio.d.ts.map +1 -0
- package/dist/tools/audio.js.map +1 -0
- package/dist/tools/base-tool.d.ts.map +1 -0
- package/dist/tools/base-tool.js.map +1 -0
- package/dist/tools/behavior-tree.d.ts.map +1 -0
- package/dist/tools/behavior-tree.js.map +1 -0
- package/dist/tools/blueprint.d.ts.map +1 -0
- package/dist/tools/blueprint.js +4 -2
- package/dist/tools/blueprint.js.map +1 -0
- package/dist/tools/consolidated-tool-definitions.d.ts.map +1 -0
- package/dist/tools/consolidated-tool-definitions.js.map +1 -0
- package/dist/tools/consolidated-tool-handlers.d.ts.map +1 -0
- package/dist/tools/consolidated-tool-handlers.js.map +1 -0
- package/dist/tools/debug.d.ts.map +1 -0
- package/dist/tools/debug.js +3 -1
- package/dist/tools/debug.js.map +1 -0
- package/dist/tools/dynamic-handler-registry.d.ts.map +1 -0
- package/dist/tools/dynamic-handler-registry.js +3 -1
- package/dist/tools/dynamic-handler-registry.js.map +1 -0
- package/dist/tools/editor.d.ts.map +1 -0
- package/dist/tools/editor.js +1 -1
- package/dist/tools/editor.js.map +1 -0
- package/dist/tools/engine.d.ts.map +1 -0
- package/dist/tools/engine.js.map +1 -0
- package/dist/tools/environment.d.ts.map +1 -0
- package/dist/tools/environment.js +2 -2
- package/dist/tools/environment.js.map +1 -0
- package/dist/tools/foliage.d.ts.map +1 -0
- package/dist/tools/foliage.js.map +1 -0
- package/dist/tools/handlers/actor-handlers.d.ts +1 -1
- package/dist/tools/handlers/actor-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/actor-handlers.js +6 -5
- package/dist/tools/handlers/actor-handlers.js.map +1 -0
- package/dist/tools/handlers/animation-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/animation-handlers.js.map +1 -0
- package/dist/tools/handlers/argument-helper.d.ts.map +1 -0
- package/dist/tools/handlers/argument-helper.js.map +1 -0
- package/dist/tools/handlers/asset-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/asset-handlers.js +5 -1
- package/dist/tools/handlers/asset-handlers.js.map +1 -0
- package/dist/tools/handlers/audio-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/audio-handlers.js.map +1 -0
- package/dist/tools/handlers/blueprint-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/blueprint-handlers.js +2 -1
- package/dist/tools/handlers/blueprint-handlers.js.map +1 -0
- package/dist/tools/handlers/common-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/common-handlers.js.map +1 -0
- package/dist/tools/handlers/editor-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/editor-handlers.js +12 -2
- package/dist/tools/handlers/editor-handlers.js.map +1 -0
- package/dist/tools/handlers/effect-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/effect-handlers.js.map +1 -0
- package/dist/tools/handlers/environment-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/environment-handlers.js.map +1 -0
- package/dist/tools/handlers/graph-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/graph-handlers.js +61 -1
- package/dist/tools/handlers/graph-handlers.js.map +1 -0
- package/dist/tools/handlers/input-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/input-handlers.js.map +1 -0
- package/dist/tools/handlers/inspect-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/inspect-handlers.js.map +1 -0
- package/dist/tools/handlers/level-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/level-handlers.js.map +1 -0
- package/dist/tools/handlers/lighting-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/lighting-handlers.js +23 -1
- package/dist/tools/handlers/lighting-handlers.js.map +1 -0
- package/dist/tools/handlers/performance-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/performance-handlers.js +15 -2
- package/dist/tools/handlers/performance-handlers.js.map +1 -0
- package/dist/tools/handlers/pipeline-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/pipeline-handlers.js.map +1 -0
- package/dist/tools/handlers/sequence-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/sequence-handlers.js.map +1 -0
- package/dist/tools/handlers/system-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/system-handlers.js +16 -1
- package/dist/tools/handlers/system-handlers.js.map +1 -0
- package/dist/tools/input.d.ts.map +1 -0
- package/dist/tools/input.js +3 -1
- package/dist/tools/input.js.map +1 -0
- package/dist/tools/introspection.d.ts.map +1 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/tools/landscape.d.ts.map +1 -0
- package/dist/tools/landscape.js +3 -1
- package/dist/tools/landscape.js.map +1 -0
- package/dist/tools/level.d.ts.map +1 -0
- package/dist/tools/level.js.map +1 -0
- package/dist/tools/lighting.d.ts.map +1 -0
- package/dist/tools/lighting.js +3 -1
- package/dist/tools/lighting.js.map +1 -0
- package/dist/tools/logs.d.ts.map +1 -0
- package/dist/tools/logs.js.map +1 -0
- package/dist/tools/materials.d.ts.map +1 -0
- package/dist/tools/materials.js +3 -1
- package/dist/tools/materials.js.map +1 -0
- package/dist/tools/niagara.d.ts.map +1 -0
- package/dist/tools/niagara.js +7 -5
- package/dist/tools/niagara.js.map +1 -0
- package/dist/tools/performance.d.ts.map +1 -0
- package/dist/tools/performance.js.map +1 -0
- package/dist/tools/physics.d.ts.map +1 -0
- package/dist/tools/physics.js +9 -7
- package/dist/tools/physics.js.map +1 -0
- package/dist/tools/property-dictionary.d.ts.map +1 -0
- package/dist/tools/property-dictionary.js.map +1 -0
- package/dist/tools/sequence.d.ts.map +1 -0
- package/dist/tools/sequence.js +3 -1
- package/dist/tools/sequence.js.map +1 -0
- package/dist/tools/tool-definition-utils.d.ts.map +1 -0
- package/dist/tools/tool-definition-utils.js.map +1 -0
- package/dist/tools/ui.d.ts.map +1 -0
- package/dist/tools/ui.js +3 -1
- package/dist/tools/ui.js.map +1 -0
- package/dist/types/automation-responses.d.ts.map +1 -0
- package/dist/types/automation-responses.js.map +1 -0
- package/dist/types/env.d.ts.map +1 -0
- package/dist/types/env.js.map +1 -0
- package/dist/types/handler-types.d.ts.map +1 -0
- package/dist/types/handler-types.js.map +1 -0
- package/dist/types/tool-interfaces.d.ts.map +1 -0
- package/dist/types/tool-interfaces.js.map +1 -0
- package/dist/types/tool-types.d.ts.map +1 -0
- package/dist/types/tool-types.js.map +1 -0
- package/dist/unreal-bridge.d.ts +1 -0
- package/dist/unreal-bridge.d.ts.map +1 -0
- package/dist/unreal-bridge.js +8 -0
- package/dist/unreal-bridge.js.map +1 -0
- package/dist/utils/command-validator.d.ts.map +1 -0
- package/dist/utils/command-validator.js.map +1 -0
- package/dist/utils/elicitation.d.ts.map +1 -0
- package/dist/utils/elicitation.js.map +1 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/ini-reader.d.ts.map +1 -0
- package/dist/utils/ini-reader.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/normalize.d.ts.map +1 -0
- package/dist/utils/normalize.js.map +1 -0
- package/dist/utils/path-security.d.ts.map +1 -0
- package/dist/utils/path-security.js.map +1 -0
- package/dist/utils/response-factory.d.ts.map +1 -0
- package/dist/utils/response-factory.js +3 -1
- package/dist/utils/response-factory.js.map +1 -0
- package/dist/utils/response-validator.d.ts.map +1 -0
- package/dist/utils/response-validator.js.map +1 -0
- package/dist/utils/result-helpers.d.ts.map +1 -0
- package/dist/utils/result-helpers.js.map +1 -0
- package/dist/utils/safe-json.d.ts.map +1 -0
- package/dist/utils/safe-json.js.map +1 -0
- package/dist/utils/unreal-command-queue.d.ts.map +1 -0
- package/dist/utils/unreal-command-queue.js.map +1 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/wasm/index.d.ts.map +1 -0
- package/dist/wasm/index.js.map +1 -0
- package/package.json +12 -34
- package/server.json +2 -2
- package/.dockerignore +0 -57
- package/.env.example +0 -26
- package/.env.production +0 -61
- package/.eslintrc.json +0 -0
- package/.eslintrc.override.json +0 -8
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -94
- package/.github/ISSUE_TEMPLATE/config.yml +0 -8
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -56
- package/.github/copilot-instructions.md +0 -478
- package/.github/dependabot.yml +0 -19
- package/.github/labeler.yml +0 -24
- package/.github/labels.yml +0 -70
- package/.github/pull_request_template.md +0 -42
- package/.github/release-drafter-config.yml +0 -51
- package/.github/workflows/auto-merge.yml +0 -38
- package/.github/workflows/ci.yml +0 -38
- package/.github/workflows/dependency-review.yml +0 -17
- package/.github/workflows/gemini-issue-triage.yml +0 -172
- package/.github/workflows/greetings.yml +0 -27
- package/.github/workflows/labeler.yml +0 -17
- package/.github/workflows/links.yml +0 -80
- package/.github/workflows/pr-size-labeler.yml +0 -137
- package/.github/workflows/publish-mcp.yml +0 -79
- package/.github/workflows/release-drafter.yml +0 -24
- package/.github/workflows/release.yml +0 -112
- package/.github/workflows/semantic-pull-request.yml +0 -35
- package/.github/workflows/smoke-test.yml +0 -36
- package/.github/workflows/stale.yml +0 -28
- package/CONTRIBUTING.md +0 -140
- package/Dockerfile +0 -37
- package/GEMINI.md +0 -115
- package/Public/Plugin_setup_guide.mp4 +0 -0
- package/Public/icon.png +0 -0
- package/claude_desktop_config_example.json +0 -15
- package/dist/types/responses.d.ts +0 -249
- package/dist/types/responses.js +0 -2
- package/docs/GraphQL-API.md +0 -888
- package/docs/Migration-Guide-v0.5.0.md +0 -684
- package/docs/Roadmap.md +0 -53
- package/docs/WebAssembly-Integration.md +0 -628
- package/docs/editor-plugin-extension.md +0 -370
- package/docs/handler-mapping.md +0 -249
- package/docs/native-automation-progress.md +0 -128
- package/docs/testing-guide.md +0 -423
- package/eslint.config.mjs +0 -68
- package/mcp-config-example.json +0 -14
- package/plugins/McpAutomationBridge/Config/FilterPlugin.ini +0 -8
- package/plugins/McpAutomationBridge/McpAutomationBridge.uplugin +0 -64
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/McpAutomationBridge.Build.cs +0 -189
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.cpp +0 -22
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeGlobals.h +0 -30
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeHelpers.h +0 -1983
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeModule.cpp +0 -72
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSettings.cpp +0 -46
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +0 -846
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +0 -2393
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetQueryHandlers.cpp +0 -300
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AssetWorkflowHandlers.cpp +0 -2807
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AudioHandlers.cpp +0 -1087
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BehaviorTreeHandlers.cpp +0 -488
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.cpp +0 -643
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintCreationHandlers.h +0 -31
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +0 -1094
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +0 -5750
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers_List.cpp +0 -152
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ControlHandlers.cpp +0 -2614
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_DebugHandlers.cpp +0 -42
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EditorFunctionHandlers.cpp +0 -1237
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +0 -1725
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +0 -2265
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_FoliageHandlers.cpp +0 -954
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InputHandlers.cpp +0 -209
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_InsightsHandlers.cpp +0 -41
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LandscapeHandlers.cpp +0 -1164
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LevelHandlers.cpp +0 -762
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +0 -663
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LogHandlers.cpp +0 -136
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_MaterialGraphHandlers.cpp +0 -494
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraGraphHandlers.cpp +0 -278
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_NiagaraHandlers.cpp +0 -625
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PerformanceHandlers.cpp +0 -401
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PipelineHandlers.cpp +0 -67
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +0 -472
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_PropertyHandlers.cpp +0 -2634
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_RenderHandlers.cpp +0 -189
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.cpp +0 -917
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SCSHandlers.h +0 -39
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +0 -2706
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequencerHandlers.cpp +0 -519
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_TestHandlers.cpp +0 -38
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_UiHandlers.cpp +0 -668
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_WorldPartitionHandlers.cpp +0 -346
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.cpp +0 -1345
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpBridgeWebSocket.h +0 -149
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -782
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSettings.h +0 -115
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpAutomationBridgeSubsystem.h +0 -796
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Public/McpConnectionManager.h +0 -117
- package/scripts/check-unreal-connection.mjs +0 -19
- package/scripts/clean-tmp.js +0 -23
- package/scripts/patch-wasm.js +0 -26
- package/scripts/run-all-tests.mjs +0 -136
- package/scripts/smoke-test.ts +0 -94
- package/scripts/sync-mcp-plugin.js +0 -143
- package/scripts/test-no-plugin-alternates.mjs +0 -113
- package/scripts/validate-server.js +0 -46
- package/scripts/verify-automation-bridge.js +0 -200
- package/src/automation/bridge.ts +0 -630
- package/src/automation/connection-manager.ts +0 -148
- package/src/automation/handshake.ts +0 -99
- package/src/automation/index.ts +0 -2
- package/src/automation/message-handler.ts +0 -192
- package/src/automation/request-tracker.ts +0 -155
- package/src/automation/types.ts +0 -108
- package/src/cli.ts +0 -34
- package/src/config/class-aliases.ts +0 -65
- package/src/config.ts +0 -73
- package/src/constants.ts +0 -29
- package/src/graphql/loaders.ts +0 -244
- package/src/graphql/resolvers.ts +0 -1008
- package/src/graphql/schema.ts +0 -452
- package/src/graphql/server.ts +0 -156
- package/src/graphql/types.ts +0 -10
- package/src/handlers/resource-handlers.ts +0 -186
- package/src/index.ts +0 -243
- package/src/resources/actors.ts +0 -127
- package/src/resources/assets.ts +0 -286
- package/src/resources/levels.ts +0 -68
- package/src/server/resource-registry.ts +0 -47
- package/src/server/tool-registry.ts +0 -354
- package/src/server-setup.ts +0 -114
- package/src/services/health-monitor.ts +0 -132
- package/src/services/metrics-server.ts +0 -176
- package/src/tools/actors.ts +0 -564
- package/src/tools/animation.ts +0 -941
- package/src/tools/assets.ts +0 -394
- package/src/tools/audio.ts +0 -499
- package/src/tools/base-tool.ts +0 -52
- package/src/tools/behavior-tree.ts +0 -45
- package/src/tools/blueprint.ts +0 -940
- package/src/tools/consolidated-tool-definitions.ts +0 -1256
- package/src/tools/consolidated-tool-handlers.ts +0 -302
- package/src/tools/debug.ts +0 -622
- package/src/tools/dynamic-handler-registry.ts +0 -33
- package/src/tools/editor.ts +0 -435
- package/src/tools/engine.ts +0 -43
- package/src/tools/environment.ts +0 -281
- package/src/tools/foliage.ts +0 -596
- package/src/tools/handlers/actor-handlers.ts +0 -244
- package/src/tools/handlers/animation-handlers.ts +0 -237
- package/src/tools/handlers/argument-helper.ts +0 -142
- package/src/tools/handlers/asset-handlers.ts +0 -550
- package/src/tools/handlers/audio-handlers.ts +0 -194
- package/src/tools/handlers/blueprint-handlers.ts +0 -380
- package/src/tools/handlers/common-handlers.ts +0 -108
- package/src/tools/handlers/editor-handlers.ts +0 -124
- package/src/tools/handlers/effect-handlers.ts +0 -224
- package/src/tools/handlers/environment-handlers.ts +0 -183
- package/src/tools/handlers/graph-handlers.ts +0 -117
- package/src/tools/handlers/input-handlers.ts +0 -28
- package/src/tools/handlers/inspect-handlers.ts +0 -450
- package/src/tools/handlers/level-handlers.ts +0 -253
- package/src/tools/handlers/lighting-handlers.ts +0 -151
- package/src/tools/handlers/performance-handlers.ts +0 -132
- package/src/tools/handlers/pipeline-handlers.ts +0 -194
- package/src/tools/handlers/sequence-handlers.ts +0 -438
- package/src/tools/handlers/system-handlers.ts +0 -564
- package/src/tools/input.ts +0 -160
- package/src/tools/introspection.ts +0 -689
- package/src/tools/landscape.ts +0 -649
- package/src/tools/level.ts +0 -989
- package/src/tools/lighting.ts +0 -1052
- package/src/tools/logs.ts +0 -219
- package/src/tools/materials.ts +0 -295
- package/src/tools/niagara.ts +0 -485
- package/src/tools/performance.ts +0 -661
- package/src/tools/physics.ts +0 -679
- package/src/tools/property-dictionary.ts +0 -98
- package/src/tools/sequence.ts +0 -385
- package/src/tools/tool-definition-utils.ts +0 -35
- package/src/tools/ui.ts +0 -452
- package/src/types/automation-responses.ts +0 -119
- package/src/types/env.ts +0 -17
- package/src/types/handler-types.ts +0 -442
- package/src/types/responses.ts +0 -355
- package/src/types/tool-interfaces.ts +0 -250
- package/src/types/tool-types.ts +0 -575
- package/src/unreal-bridge.ts +0 -693
- package/src/utils/command-validator.ts +0 -139
- package/src/utils/elicitation.ts +0 -132
- package/src/utils/error-handler.ts +0 -287
- package/src/utils/ini-reader.ts +0 -86
- package/src/utils/logger.ts +0 -35
- package/src/utils/normalize.test.ts +0 -162
- package/src/utils/normalize.ts +0 -146
- package/src/utils/path-security.ts +0 -43
- package/src/utils/response-factory.ts +0 -44
- package/src/utils/response-validator.ts +0 -395
- package/src/utils/result-helpers.ts +0 -195
- package/src/utils/safe-json.test.ts +0 -90
- package/src/utils/safe-json.ts +0 -70
- package/src/utils/unreal-command-queue.ts +0 -166
- package/src/utils/validation.test.ts +0 -184
- package/src/utils/validation.ts +0 -312
- package/src/wasm/index.ts +0 -838
- package/test-server.mjs +0 -100
- package/tests/test-animation.mjs +0 -369
- package/tests/test-asset-advanced.mjs +0 -82
- package/tests/test-asset-graph.mjs +0 -311
- package/tests/test-audio.mjs +0 -417
- package/tests/test-automation-timeouts.mjs +0 -98
- package/tests/test-behavior-tree.mjs +0 -444
- package/tests/test-blueprint-graph.mjs +0 -410
- package/tests/test-blueprint.mjs +0 -577
- package/tests/test-client-mode.mjs +0 -86
- package/tests/test-console-command.mjs +0 -56
- package/tests/test-control-actor.mjs +0 -425
- package/tests/test-control-editor.mjs +0 -112
- package/tests/test-graphql.mjs +0 -372
- package/tests/test-input.mjs +0 -349
- package/tests/test-inspect.mjs +0 -302
- package/tests/test-landscape.mjs +0 -316
- package/tests/test-lighting.mjs +0 -428
- package/tests/test-manage-asset.mjs +0 -438
- package/tests/test-manage-level.mjs +0 -89
- package/tests/test-materials.mjs +0 -356
- package/tests/test-niagara.mjs +0 -185
- package/tests/test-no-inline-python.mjs +0 -122
- package/tests/test-performance.mjs +0 -539
- package/tests/test-plugin-handshake.mjs +0 -82
- package/tests/test-runner.mjs +0 -993
- package/tests/test-sequence.mjs +0 -104
- package/tests/test-system.mjs +0 -96
- package/tests/test-wasm.mjs +0 -283
- package/tests/test-world-partition.mjs +0 -215
- package/tsconfig.json +0 -56
- package/vitest.config.ts +0 -35
- package/wasm/Cargo.lock +0 -363
- package/wasm/Cargo.toml +0 -42
- package/wasm/LICENSE +0 -21
- package/wasm/README.md +0 -253
- package/wasm/src/dependency_resolver.rs +0 -377
- package/wasm/src/lib.rs +0 -153
- package/wasm/src/property_parser.rs +0 -271
- package/wasm/src/transform_math.rs +0 -396
- package/wasm/tests/integration.rs +0 -109
package/src/automation/bridge.ts
DELETED
|
@@ -1,630 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
|
-
import { WebSocket } from 'ws';
|
|
3
|
-
import { Logger } from '../utils/logger.js';
|
|
4
|
-
import {
|
|
5
|
-
DEFAULT_AUTOMATION_HOST,
|
|
6
|
-
DEFAULT_AUTOMATION_PORT,
|
|
7
|
-
DEFAULT_NEGOTIATED_PROTOCOLS,
|
|
8
|
-
DEFAULT_HEARTBEAT_INTERVAL_MS,
|
|
9
|
-
DEFAULT_MAX_PENDING_REQUESTS,
|
|
10
|
-
DEFAULT_MAX_QUEUED_REQUESTS,
|
|
11
|
-
MAX_WS_MESSAGE_SIZE_BYTES
|
|
12
|
-
} from '../constants.js';
|
|
13
|
-
import { createRequire } from 'node:module';
|
|
14
|
-
import {
|
|
15
|
-
AutomationBridgeOptions,
|
|
16
|
-
AutomationBridgeStatus,
|
|
17
|
-
AutomationBridgeMessage,
|
|
18
|
-
AutomationBridgeResponseMessage,
|
|
19
|
-
AutomationBridgeEvents
|
|
20
|
-
} from './types.js';
|
|
21
|
-
import { ConnectionManager } from './connection-manager.js';
|
|
22
|
-
import { RequestTracker } from './request-tracker.js';
|
|
23
|
-
import { HandshakeHandler } from './handshake.js';
|
|
24
|
-
import { MessageHandler } from './message-handler.js';
|
|
25
|
-
|
|
26
|
-
const require = createRequire(import.meta.url);
|
|
27
|
-
const packageInfo: { name?: string; version?: string } = (() => {
|
|
28
|
-
try {
|
|
29
|
-
return require('../../package.json');
|
|
30
|
-
} catch (error) {
|
|
31
|
-
const log = new Logger('AutomationBridge');
|
|
32
|
-
log.debug('Unable to read package.json for version info', error);
|
|
33
|
-
return {};
|
|
34
|
-
}
|
|
35
|
-
})();
|
|
36
|
-
|
|
37
|
-
export class AutomationBridge extends EventEmitter {
|
|
38
|
-
private readonly host: string;
|
|
39
|
-
private readonly port: number;
|
|
40
|
-
private readonly ports: number[];
|
|
41
|
-
private readonly negotiatedProtocols: string[];
|
|
42
|
-
private readonly capabilityToken?: string;
|
|
43
|
-
private readonly enabled: boolean;
|
|
44
|
-
private readonly serverName: string;
|
|
45
|
-
private readonly serverVersion: string;
|
|
46
|
-
private readonly clientHost: string;
|
|
47
|
-
private readonly clientPort: number;
|
|
48
|
-
private readonly serverLegacyEnabled: boolean;
|
|
49
|
-
private readonly maxConcurrentConnections: number;
|
|
50
|
-
private readonly maxQueuedRequests: number;
|
|
51
|
-
|
|
52
|
-
private connectionManager: ConnectionManager;
|
|
53
|
-
private requestTracker: RequestTracker;
|
|
54
|
-
private handshakeHandler: HandshakeHandler;
|
|
55
|
-
private messageHandler: MessageHandler;
|
|
56
|
-
private log = new Logger('AutomationBridge');
|
|
57
|
-
|
|
58
|
-
private lastHandshakeAt?: Date;
|
|
59
|
-
private lastHandshakeMetadata?: Record<string, unknown>;
|
|
60
|
-
private lastHandshakeAck?: AutomationBridgeMessage;
|
|
61
|
-
private lastHandshakeFailure?: { reason: string; at: Date };
|
|
62
|
-
private lastDisconnect?: { code: number; reason: string; at: Date };
|
|
63
|
-
private lastError?: { message: string; at: Date };
|
|
64
|
-
private queuedRequestItems: Array<{ resolve: (v: any) => void; reject: (e: any) => void; action: string; payload: any; options: any }> = [];
|
|
65
|
-
private connectionPromise?: Promise<void>;
|
|
66
|
-
private connectionLock = false;
|
|
67
|
-
|
|
68
|
-
constructor(options: AutomationBridgeOptions = {}) {
|
|
69
|
-
super();
|
|
70
|
-
this.host = options.host ?? process.env.MCP_AUTOMATION_WS_HOST ?? DEFAULT_AUTOMATION_HOST;
|
|
71
|
-
|
|
72
|
-
const sanitizePort = (value: unknown): number | null => {
|
|
73
|
-
if (typeof value === 'number' && Number.isInteger(value)) {
|
|
74
|
-
return value > 0 && value <= 65535 ? value : null;
|
|
75
|
-
}
|
|
76
|
-
if (typeof value === 'string' && value.trim().length > 0) {
|
|
77
|
-
const parsed = Number.parseInt(value.trim(), 10);
|
|
78
|
-
return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : null;
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const defaultPort = sanitizePort(options.port ?? process.env.MCP_AUTOMATION_WS_PORT) ?? DEFAULT_AUTOMATION_PORT;
|
|
84
|
-
const configuredPortValues: Array<number | string> | undefined = options.ports
|
|
85
|
-
? options.ports
|
|
86
|
-
: process.env.MCP_AUTOMATION_WS_PORTS
|
|
87
|
-
?.split(',')
|
|
88
|
-
.map((token) => token.trim())
|
|
89
|
-
.filter((token) => token.length > 0);
|
|
90
|
-
|
|
91
|
-
const sanitizedPorts = Array.isArray(configuredPortValues)
|
|
92
|
-
? configuredPortValues
|
|
93
|
-
.map((value) => sanitizePort(value))
|
|
94
|
-
.filter((port): port is number => port !== null)
|
|
95
|
-
: [];
|
|
96
|
-
|
|
97
|
-
if (!sanitizedPorts.includes(defaultPort)) {
|
|
98
|
-
sanitizedPorts.unshift(defaultPort);
|
|
99
|
-
}
|
|
100
|
-
if (sanitizedPorts.length === 0) {
|
|
101
|
-
sanitizedPorts.push(DEFAULT_AUTOMATION_PORT);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
this.ports = Array.from(new Set(sanitizedPorts));
|
|
105
|
-
const defaultProtocols = DEFAULT_NEGOTIATED_PROTOCOLS;
|
|
106
|
-
const userProtocols = Array.isArray(options.protocols)
|
|
107
|
-
? options.protocols.filter((proto) => typeof proto === 'string' && proto.trim().length > 0)
|
|
108
|
-
: [];
|
|
109
|
-
const envProtocols = process.env.MCP_AUTOMATION_WS_PROTOCOLS
|
|
110
|
-
? process.env.MCP_AUTOMATION_WS_PROTOCOLS.split(',')
|
|
111
|
-
.map((token) => token.trim())
|
|
112
|
-
.filter((token) => token.length > 0)
|
|
113
|
-
: [];
|
|
114
|
-
this.negotiatedProtocols = Array.from(new Set([...userProtocols, ...envProtocols, ...defaultProtocols]));
|
|
115
|
-
this.port = this.ports[0];
|
|
116
|
-
this.serverLegacyEnabled =
|
|
117
|
-
options.serverLegacyEnabled ?? process.env.MCP_AUTOMATION_SERVER_LEGACY !== 'false';
|
|
118
|
-
this.capabilityToken =
|
|
119
|
-
options.capabilityToken ?? process.env.MCP_AUTOMATION_CAPABILITY_TOKEN ?? undefined;
|
|
120
|
-
this.enabled = options.enabled ?? process.env.MCP_AUTOMATION_BRIDGE_ENABLED !== 'false';
|
|
121
|
-
this.serverName = options.serverName
|
|
122
|
-
?? process.env.MCP_SERVER_NAME
|
|
123
|
-
?? packageInfo.name
|
|
124
|
-
?? 'unreal-engine-mcp';
|
|
125
|
-
this.serverVersion = options.serverVersion
|
|
126
|
-
?? process.env.MCP_SERVER_VERSION
|
|
127
|
-
?? packageInfo.version
|
|
128
|
-
?? process.env.npm_package_version
|
|
129
|
-
?? '0.0.0';
|
|
130
|
-
|
|
131
|
-
const heartbeatIntervalMs = (options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS) > 0
|
|
132
|
-
? (options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS)
|
|
133
|
-
: 0;
|
|
134
|
-
|
|
135
|
-
const maxPendingRequests = Math.max(1, options.maxPendingRequests ?? DEFAULT_MAX_PENDING_REQUESTS);
|
|
136
|
-
const maxConcurrentConnections = Math.max(1, options.maxConcurrentConnections ?? 10);
|
|
137
|
-
this.maxQueuedRequests = Math.max(0, options.maxQueuedRequests ?? DEFAULT_MAX_QUEUED_REQUESTS);
|
|
138
|
-
|
|
139
|
-
this.clientHost = options.clientHost ?? process.env.MCP_AUTOMATION_CLIENT_HOST ?? DEFAULT_AUTOMATION_HOST;
|
|
140
|
-
this.clientPort = options.clientPort ?? sanitizePort(process.env.MCP_AUTOMATION_CLIENT_PORT) ?? DEFAULT_AUTOMATION_PORT;
|
|
141
|
-
this.maxConcurrentConnections = maxConcurrentConnections;
|
|
142
|
-
|
|
143
|
-
// Initialize components
|
|
144
|
-
this.connectionManager = new ConnectionManager(heartbeatIntervalMs);
|
|
145
|
-
this.requestTracker = new RequestTracker(maxPendingRequests);
|
|
146
|
-
this.handshakeHandler = new HandshakeHandler(this.capabilityToken);
|
|
147
|
-
this.messageHandler = new MessageHandler(this.requestTracker);
|
|
148
|
-
|
|
149
|
-
// Forward events from connection manager
|
|
150
|
-
// Note: ConnectionManager doesn't emit 'connected'/'disconnected' directly in the same way,
|
|
151
|
-
// we handle socket events here and use ConnectionManager to track state.
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
override on<K extends keyof AutomationBridgeEvents>(
|
|
155
|
-
event: K,
|
|
156
|
-
listener: AutomationBridgeEvents[K]
|
|
157
|
-
): this {
|
|
158
|
-
return super.on(event, listener as (...args: unknown[]) => void);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
override once<K extends keyof AutomationBridgeEvents>(
|
|
162
|
-
event: K,
|
|
163
|
-
listener: AutomationBridgeEvents[K]
|
|
164
|
-
): this {
|
|
165
|
-
return super.once(event, listener as (...args: unknown[]) => void);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
override off<K extends keyof AutomationBridgeEvents>(
|
|
169
|
-
event: K,
|
|
170
|
-
listener: AutomationBridgeEvents[K]
|
|
171
|
-
): this {
|
|
172
|
-
return super.off(event, listener as (...args: unknown[]) => void);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
start(): void {
|
|
176
|
-
if (!this.enabled) {
|
|
177
|
-
this.log.info('Automation bridge disabled by configuration.');
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
this.log.info(`Automation bridge connecting to Unreal server at ws://${this.clientHost}:${this.clientPort}`);
|
|
182
|
-
this.startClient();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private startClient(): void {
|
|
186
|
-
try {
|
|
187
|
-
const url = `ws://${this.clientHost}:${this.clientPort}`;
|
|
188
|
-
this.log.info(`Connecting to Unreal Engine automation server at ${url}`);
|
|
189
|
-
|
|
190
|
-
this.log.debug(`Negotiated protocols: ${JSON.stringify(this.negotiatedProtocols)}`);
|
|
191
|
-
|
|
192
|
-
const protocols = this.negotiatedProtocols.length === 1
|
|
193
|
-
? this.negotiatedProtocols[0]
|
|
194
|
-
: this.negotiatedProtocols;
|
|
195
|
-
|
|
196
|
-
this.log.debug(`Using WebSocket protocols arg: ${JSON.stringify(protocols)}`);
|
|
197
|
-
|
|
198
|
-
const headers: Record<string, string> | undefined = this.capabilityToken
|
|
199
|
-
? {
|
|
200
|
-
'X-MCP-Capability': this.capabilityToken,
|
|
201
|
-
'X-MCP-Capability-Token': this.capabilityToken
|
|
202
|
-
}
|
|
203
|
-
: undefined;
|
|
204
|
-
|
|
205
|
-
const socket = new WebSocket(url, protocols, {
|
|
206
|
-
headers,
|
|
207
|
-
perMessageDeflate: false
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
this.handleClientConnection(socket);
|
|
211
|
-
} catch (error) {
|
|
212
|
-
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
213
|
-
this.lastError = { message: errorObj.message, at: new Date() };
|
|
214
|
-
this.log.error('Failed to create WebSocket client connection', errorObj);
|
|
215
|
-
const errorWithPort = Object.assign(errorObj, { port: this.clientPort });
|
|
216
|
-
this.emitAutomation('error', errorWithPort);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
private async handleClientConnection(socket: WebSocket): Promise<void> {
|
|
221
|
-
socket.on('open', async () => {
|
|
222
|
-
this.log.info('Automation bridge client connected, starting handshake');
|
|
223
|
-
try {
|
|
224
|
-
const metadata = await this.handshakeHandler.initiateHandshake(socket);
|
|
225
|
-
|
|
226
|
-
this.lastHandshakeAt = new Date();
|
|
227
|
-
this.lastHandshakeMetadata = metadata;
|
|
228
|
-
this.lastHandshakeFailure = undefined;
|
|
229
|
-
this.connectionManager.updateLastMessageTime();
|
|
230
|
-
|
|
231
|
-
// Extract remote address/port from underlying TCP socket
|
|
232
|
-
// Note: WebSocket types don't expose _socket, but it exists at runtime
|
|
233
|
-
const socketWithInternal = socket as unknown as { _socket?: { remoteAddress?: string; remotePort?: number }; socket?: { remoteAddress?: string; remotePort?: number } };
|
|
234
|
-
const underlying = socketWithInternal._socket || socketWithInternal.socket;
|
|
235
|
-
const remoteAddr = underlying?.remoteAddress ?? undefined;
|
|
236
|
-
const remotePort = underlying?.remotePort ?? undefined;
|
|
237
|
-
|
|
238
|
-
this.connectionManager.registerSocket(socket, this.clientPort, metadata, remoteAddr, remotePort);
|
|
239
|
-
this.connectionManager.startHeartbeat();
|
|
240
|
-
|
|
241
|
-
this.emitAutomation('connected', {
|
|
242
|
-
socket,
|
|
243
|
-
metadata,
|
|
244
|
-
port: this.clientPort,
|
|
245
|
-
protocol: socket.protocol || null
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
const getRawDataByteLength = (data: unknown): number => {
|
|
249
|
-
if (typeof data === 'string') {
|
|
250
|
-
return Buffer.byteLength(data, 'utf8');
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (Buffer.isBuffer(data)) {
|
|
254
|
-
return data.length;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (Array.isArray(data)) {
|
|
258
|
-
return data.reduce((total, item) => total + (Buffer.isBuffer(item) ? item.length : 0), 0);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (data instanceof ArrayBuffer) {
|
|
262
|
-
return data.byteLength;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (ArrayBuffer.isView(data)) {
|
|
266
|
-
return data.byteLength;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return 0;
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const rawDataToUtf8String = (data: unknown, byteLengthHint?: number): string => {
|
|
273
|
-
if (typeof data === 'string') {
|
|
274
|
-
return data;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (Buffer.isBuffer(data)) {
|
|
278
|
-
return data.toString('utf8');
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (Array.isArray(data)) {
|
|
282
|
-
const buffers = data.filter((item): item is Buffer => Buffer.isBuffer(item));
|
|
283
|
-
const totalLength = typeof byteLengthHint === 'number'
|
|
284
|
-
? byteLengthHint
|
|
285
|
-
: buffers.reduce((total, item) => total + item.length, 0);
|
|
286
|
-
return Buffer.concat(buffers, totalLength).toString('utf8');
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (data instanceof ArrayBuffer) {
|
|
290
|
-
return Buffer.from(data).toString('utf8');
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (ArrayBuffer.isView(data)) {
|
|
294
|
-
return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('utf8');
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return '';
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
socket.on('message', (data) => {
|
|
301
|
-
try {
|
|
302
|
-
const byteLength = getRawDataByteLength(data);
|
|
303
|
-
if (byteLength > MAX_WS_MESSAGE_SIZE_BYTES) {
|
|
304
|
-
this.log.error(
|
|
305
|
-
`Received oversized message (${byteLength} bytes, max: ${MAX_WS_MESSAGE_SIZE_BYTES}). Dropping.`
|
|
306
|
-
);
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const text = rawDataToUtf8String(data, byteLength);
|
|
311
|
-
this.log.debug(`[AutomationBridge Client] Received message: ${text.substring(0, 1000)}`);
|
|
312
|
-
const parsed = JSON.parse(text) as AutomationBridgeMessage;
|
|
313
|
-
this.connectionManager.updateLastMessageTime();
|
|
314
|
-
this.messageHandler.handleMessage(parsed);
|
|
315
|
-
this.emitAutomation('message', parsed);
|
|
316
|
-
} catch (error) {
|
|
317
|
-
this.log.error('Error handling message', error);
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
} catch (error) {
|
|
322
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
323
|
-
this.lastHandshakeFailure = { reason: err.message, at: new Date() };
|
|
324
|
-
this.emitAutomation('handshakeFailed', { reason: err.message, port: this.clientPort });
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
socket.on('error', (error) => {
|
|
329
|
-
this.log.error('Automation bridge client socket error', error);
|
|
330
|
-
const errObj = error instanceof Error ? error : new Error(String(error));
|
|
331
|
-
this.lastError = { message: errObj.message, at: new Date() };
|
|
332
|
-
const errWithPort = Object.assign(errObj, { port: this.clientPort });
|
|
333
|
-
this.emitAutomation('error', errWithPort);
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
socket.on('close', (code, reasonBuffer) => {
|
|
337
|
-
const reason = reasonBuffer.toString('utf8');
|
|
338
|
-
const socketInfo = this.connectionManager.removeSocket(socket);
|
|
339
|
-
|
|
340
|
-
if (socketInfo) {
|
|
341
|
-
this.lastDisconnect = { code, reason, at: new Date() };
|
|
342
|
-
this.emitAutomation('disconnected', {
|
|
343
|
-
code,
|
|
344
|
-
reason,
|
|
345
|
-
port: socketInfo.port,
|
|
346
|
-
protocol: socketInfo.protocol || null
|
|
347
|
-
});
|
|
348
|
-
this.log.info(`Automation bridge client socket closed (code=${code}, reason=${reason})`);
|
|
349
|
-
|
|
350
|
-
if (!this.connectionManager.isConnected()) {
|
|
351
|
-
this.requestTracker.rejectAll(new Error(reason || 'Connection lost'));
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
stop(): void {
|
|
358
|
-
if (this.isConnected()) {
|
|
359
|
-
this.broadcast({
|
|
360
|
-
type: 'bridge_shutdown',
|
|
361
|
-
timestamp: new Date().toISOString(),
|
|
362
|
-
reason: 'Server shutting down'
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
this.connectionManager.closeAll(1001, 'Server shutdown');
|
|
366
|
-
this.lastHandshakeAck = undefined;
|
|
367
|
-
this.requestTracker.rejectAll(new Error('Automation bridge server stopped'));
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
isConnected(): boolean {
|
|
371
|
-
return this.connectionManager.isConnected();
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
getStatus(): AutomationBridgeStatus {
|
|
375
|
-
|
|
376
|
-
const connectionInfos = Array.from(this.connectionManager.getActiveSockets().entries()).map(([socket, info]) => ({
|
|
377
|
-
connectionId: info.connectionId,
|
|
378
|
-
sessionId: info.sessionId ?? null,
|
|
379
|
-
remoteAddress: info.remoteAddress ?? null,
|
|
380
|
-
remotePort: info.remotePort ?? null,
|
|
381
|
-
port: info.port,
|
|
382
|
-
connectedAt: info.connectedAt.toISOString(),
|
|
383
|
-
protocol: info.protocol || null,
|
|
384
|
-
readyState: socket.readyState,
|
|
385
|
-
isPrimary: socket === this.connectionManager.getPrimarySocket()
|
|
386
|
-
}));
|
|
387
|
-
|
|
388
|
-
return {
|
|
389
|
-
enabled: this.enabled,
|
|
390
|
-
host: this.host,
|
|
391
|
-
port: this.port,
|
|
392
|
-
configuredPorts: [...this.ports],
|
|
393
|
-
listeningPorts: [], // We are client-only now
|
|
394
|
-
connected: this.isConnected(),
|
|
395
|
-
connectedAt: connectionInfos.length > 0 ? connectionInfos[0].connectedAt : null,
|
|
396
|
-
activePort: connectionInfos.length > 0 ? connectionInfos[0].port : null,
|
|
397
|
-
negotiatedProtocol: connectionInfos.length > 0 ? connectionInfos[0].protocol : null,
|
|
398
|
-
supportedProtocols: [...this.negotiatedProtocols],
|
|
399
|
-
supportedOpcodes: ['automation_request'],
|
|
400
|
-
expectedResponseOpcodes: ['automation_response'],
|
|
401
|
-
capabilityTokenRequired: Boolean(this.capabilityToken),
|
|
402
|
-
lastHandshakeAt: this.lastHandshakeAt?.toISOString() ?? null,
|
|
403
|
-
lastHandshakeMetadata: this.lastHandshakeMetadata ?? null,
|
|
404
|
-
lastHandshakeAck: this.lastHandshakeAck ?? null,
|
|
405
|
-
lastHandshakeFailure: this.lastHandshakeFailure
|
|
406
|
-
? { reason: this.lastHandshakeFailure.reason, at: this.lastHandshakeFailure.at.toISOString() }
|
|
407
|
-
: null,
|
|
408
|
-
lastDisconnect: this.lastDisconnect
|
|
409
|
-
? { code: this.lastDisconnect.code, reason: this.lastDisconnect.reason, at: this.lastDisconnect.at.toISOString() }
|
|
410
|
-
: null,
|
|
411
|
-
lastError: this.lastError
|
|
412
|
-
? { message: this.lastError.message, at: this.lastError.at.toISOString() }
|
|
413
|
-
: null,
|
|
414
|
-
lastMessageAt: this.connectionManager.getLastMessageTime()?.toISOString() ?? null,
|
|
415
|
-
lastRequestSentAt: this.requestTracker.getLastRequestSentAt()?.toISOString() ?? null,
|
|
416
|
-
pendingRequests: this.requestTracker.getPendingCount(),
|
|
417
|
-
pendingRequestDetails: this.requestTracker.getPendingDetails(),
|
|
418
|
-
connections: connectionInfos,
|
|
419
|
-
webSocketListening: false,
|
|
420
|
-
serverLegacyEnabled: this.serverLegacyEnabled,
|
|
421
|
-
serverName: this.serverName,
|
|
422
|
-
serverVersion: this.serverVersion,
|
|
423
|
-
maxConcurrentConnections: this.maxConcurrentConnections,
|
|
424
|
-
maxPendingRequests: this.requestTracker.getMaxPendingRequests(),
|
|
425
|
-
heartbeatIntervalMs: this.connectionManager.getHeartbeatIntervalMs()
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
async sendAutomationRequest<T = AutomationBridgeResponseMessage>(
|
|
430
|
-
action: string,
|
|
431
|
-
payload: Record<string, unknown> = {},
|
|
432
|
-
options: { timeoutMs?: number } = {}
|
|
433
|
-
): Promise<T> {
|
|
434
|
-
if (!this.isConnected()) {
|
|
435
|
-
if (this.enabled) {
|
|
436
|
-
this.log.info('Automation bridge not connected, attempting lazy connection...');
|
|
437
|
-
|
|
438
|
-
// Avoid multiple simultaneous connection attempts using lock
|
|
439
|
-
if (!this.connectionPromise && !this.connectionLock) {
|
|
440
|
-
this.connectionLock = true;
|
|
441
|
-
this.connectionPromise = new Promise<void>((resolve, reject) => {
|
|
442
|
-
const onConnect = () => {
|
|
443
|
-
cleanup(); resolve();
|
|
444
|
-
};
|
|
445
|
-
// We map errors to rejects, but we should be careful about which errors.
|
|
446
|
-
// A socket error might happen during connection.
|
|
447
|
-
const onError = (err: any) => {
|
|
448
|
-
cleanup(); reject(err);
|
|
449
|
-
};
|
|
450
|
-
// Also listen for handshake failure
|
|
451
|
-
const onHandshakeFail = (err: any) => {
|
|
452
|
-
cleanup(); reject(new Error(`Handshake failed: ${err.reason}`));
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
const cleanup = () => {
|
|
456
|
-
this.off('connected', onConnect);
|
|
457
|
-
this.off('error', onError);
|
|
458
|
-
this.off('handshakeFailed', onHandshakeFail);
|
|
459
|
-
// Clear lock and promise so next attempt can try again
|
|
460
|
-
this.connectionLock = false;
|
|
461
|
-
this.connectionPromise = undefined;
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
this.once('connected', onConnect);
|
|
465
|
-
this.once('error', onError);
|
|
466
|
-
this.once('handshakeFailed', onHandshakeFail);
|
|
467
|
-
|
|
468
|
-
try {
|
|
469
|
-
this.startClient();
|
|
470
|
-
} catch (e) {
|
|
471
|
-
onError(e);
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
try {
|
|
477
|
-
// Wait for connection with a short timeout for the connection itself
|
|
478
|
-
const connectTimeout = 5000;
|
|
479
|
-
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
480
|
-
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
481
|
-
timeoutId = setTimeout(() => reject(new Error('Lazy connection timeout')), connectTimeout);
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
try {
|
|
485
|
-
await Promise.race([this.connectionPromise, timeoutPromise]);
|
|
486
|
-
} finally {
|
|
487
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
488
|
-
}
|
|
489
|
-
} catch (err: any) {
|
|
490
|
-
this.log.error('Lazy connection failed', err);
|
|
491
|
-
// We don't throw here immediately, we let the isConnected check fail below
|
|
492
|
-
// or throw a specific error.
|
|
493
|
-
// Actually, if connection failed, we should probably fail the request.
|
|
494
|
-
throw new Error(`Failed to establish connection to Unreal Engine: ${err.message}`);
|
|
495
|
-
}
|
|
496
|
-
} else {
|
|
497
|
-
throw new Error('Automation bridge disabled');
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
if (!this.isConnected()) {
|
|
502
|
-
throw new Error('Automation bridge not connected');
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
if (this.requestTracker.getPendingCount() >= this.requestTracker.getMaxPendingRequests()) {
|
|
506
|
-
if (this.queuedRequestItems.length >= this.maxQueuedRequests) {
|
|
507
|
-
throw new Error(`Automation bridge request queue is full (max: ${this.maxQueuedRequests}). Please retry later.`);
|
|
508
|
-
}
|
|
509
|
-
return new Promise<T>((resolve, reject) => {
|
|
510
|
-
this.queuedRequestItems.push({
|
|
511
|
-
resolve,
|
|
512
|
-
reject,
|
|
513
|
-
action,
|
|
514
|
-
payload,
|
|
515
|
-
options
|
|
516
|
-
});
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
return this.sendRequestInternal<T>(action, payload, options);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
private async sendRequestInternal<T>(
|
|
524
|
-
action: string,
|
|
525
|
-
payload: Record<string, unknown>,
|
|
526
|
-
options: { timeoutMs?: number }
|
|
527
|
-
): Promise<T> {
|
|
528
|
-
const timeoutMs = options.timeoutMs ?? 60000; // Increased default timeout to 60s
|
|
529
|
-
|
|
530
|
-
// Check for coalescing
|
|
531
|
-
const coalesceKey = this.requestTracker.createCoalesceKey(action, payload);
|
|
532
|
-
if (coalesceKey) {
|
|
533
|
-
const existing = this.requestTracker.getCoalescedRequest(coalesceKey);
|
|
534
|
-
if (existing) {
|
|
535
|
-
return existing as unknown as T;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
const { requestId, promise } = this.requestTracker.createRequest(action, payload, timeoutMs);
|
|
540
|
-
|
|
541
|
-
if (coalesceKey) {
|
|
542
|
-
this.requestTracker.setCoalescedRequest(coalesceKey, promise);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
const message: AutomationBridgeMessage = {
|
|
546
|
-
type: 'automation_request',
|
|
547
|
-
requestId,
|
|
548
|
-
action,
|
|
549
|
-
payload
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
const resultPromise = promise as unknown as Promise<T>;
|
|
553
|
-
|
|
554
|
-
// Ensure we process the queue when this request finishes
|
|
555
|
-
resultPromise.finally(() => {
|
|
556
|
-
this.processRequestQueue();
|
|
557
|
-
}).catch(() => { }); // catch to prevent unhandled rejection during finally chain? no, finally returns new promise
|
|
558
|
-
|
|
559
|
-
if (this.send(message)) {
|
|
560
|
-
this.requestTracker.updateLastRequestSentAt();
|
|
561
|
-
return resultPromise;
|
|
562
|
-
} else {
|
|
563
|
-
this.requestTracker.rejectRequest(requestId, new Error('Failed to send request'));
|
|
564
|
-
throw new Error('Failed to send request');
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
private processRequestQueue() {
|
|
569
|
-
if (this.queuedRequestItems.length === 0) return;
|
|
570
|
-
|
|
571
|
-
// while we have capacity and items
|
|
572
|
-
while (
|
|
573
|
-
this.queuedRequestItems.length > 0 &&
|
|
574
|
-
this.requestTracker.getPendingCount() < this.requestTracker.getMaxPendingRequests()
|
|
575
|
-
) {
|
|
576
|
-
const item = this.queuedRequestItems.shift();
|
|
577
|
-
if (item) {
|
|
578
|
-
this.sendRequestInternal(item.action, item.payload, item.options)
|
|
579
|
-
.then(item.resolve)
|
|
580
|
-
.catch(item.reject);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
send(payload: AutomationBridgeMessage): boolean {
|
|
586
|
-
const primarySocket = this.connectionManager.getPrimarySocket();
|
|
587
|
-
if (!primarySocket || primarySocket.readyState !== WebSocket.OPEN) {
|
|
588
|
-
this.log.warn('Attempted to send automation message without an active primary connection');
|
|
589
|
-
return false;
|
|
590
|
-
}
|
|
591
|
-
try {
|
|
592
|
-
primarySocket.send(JSON.stringify(payload));
|
|
593
|
-
return true;
|
|
594
|
-
} catch (error) {
|
|
595
|
-
this.log.error('Failed to send automation message', error);
|
|
596
|
-
const errObj = error instanceof Error ? error : new Error(String(error));
|
|
597
|
-
const primaryInfo = this.connectionManager.getActiveSockets().get(primarySocket);
|
|
598
|
-
const errorWithPort = Object.assign(errObj, { port: primaryInfo?.port });
|
|
599
|
-
this.emitAutomation('error', errorWithPort);
|
|
600
|
-
return false;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
private broadcast(payload: AutomationBridgeMessage): boolean {
|
|
605
|
-
const sockets = this.connectionManager.getActiveSockets();
|
|
606
|
-
if (sockets.size === 0) {
|
|
607
|
-
this.log.warn('Attempted to broadcast automation message without any active connections');
|
|
608
|
-
return false;
|
|
609
|
-
}
|
|
610
|
-
let sentCount = 0;
|
|
611
|
-
for (const [socket] of sockets) {
|
|
612
|
-
if (socket.readyState === WebSocket.OPEN) {
|
|
613
|
-
try {
|
|
614
|
-
socket.send(JSON.stringify(payload));
|
|
615
|
-
sentCount++;
|
|
616
|
-
} catch (error) {
|
|
617
|
-
this.log.error('Failed to broadcast automation message to socket', error);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
return sentCount > 0;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
private emitAutomation<K extends keyof AutomationBridgeEvents>(
|
|
625
|
-
event: K,
|
|
626
|
-
...args: Parameters<AutomationBridgeEvents[K]>
|
|
627
|
-
): void {
|
|
628
|
-
this.emit(event, ...args);
|
|
629
|
-
}
|
|
630
|
-
}
|