@quenty/studio-bridge 0.7.1 → 0.9.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/CHANGELOG.md +14 -42
- package/README.md +166 -115
- package/dist/src/bridge/bridge-connection-remote.test.d.ts +7 -0
- package/dist/src/bridge/bridge-connection-remote.test.d.ts.map +1 -0
- package/dist/src/bridge/bridge-connection-remote.test.js +144 -0
- package/dist/src/bridge/bridge-connection-remote.test.js.map +1 -0
- package/dist/src/bridge/bridge-connection.d.ts +112 -0
- package/dist/src/bridge/bridge-connection.d.ts.map +1 -0
- package/dist/src/bridge/bridge-connection.js +528 -0
- package/dist/src/bridge/bridge-connection.js.map +1 -0
- package/dist/src/bridge/bridge-connection.test.d.ts +6 -0
- package/dist/src/bridge/bridge-connection.test.d.ts.map +1 -0
- package/dist/src/bridge/bridge-connection.test.js +773 -0
- package/dist/src/bridge/bridge-connection.test.js.map +1 -0
- package/dist/src/bridge/bridge-session.d.ts +38 -0
- package/dist/src/bridge/bridge-session.d.ts.map +1 -0
- package/dist/src/bridge/bridge-session.js +259 -0
- package/dist/src/bridge/bridge-session.js.map +1 -0
- package/dist/src/bridge/bridge-session.test.d.ts +6 -0
- package/dist/src/bridge/bridge-session.test.d.ts.map +1 -0
- package/dist/src/bridge/bridge-session.test.js +264 -0
- package/dist/src/bridge/bridge-session.test.js.map +1 -0
- package/dist/src/bridge/index.d.ts +13 -0
- package/dist/src/bridge/index.d.ts.map +1 -0
- package/dist/src/bridge/index.js +13 -0
- package/dist/src/bridge/index.js.map +1 -0
- package/dist/src/bridge/internal/__tests__/failover-crash.test.d.ts +9 -0
- package/dist/src/bridge/internal/__tests__/failover-crash.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/__tests__/failover-crash.test.js +159 -0
- package/dist/src/bridge/internal/__tests__/failover-crash.test.js.map +1 -0
- package/dist/src/bridge/internal/__tests__/failover-graceful.test.d.ts +9 -0
- package/dist/src/bridge/internal/__tests__/failover-graceful.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/__tests__/failover-graceful.test.js +218 -0
- package/dist/src/bridge/internal/__tests__/failover-graceful.test.js.map +1 -0
- package/dist/src/bridge/internal/__tests__/failover-inflight.test.d.ts +9 -0
- package/dist/src/bridge/internal/__tests__/failover-inflight.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/__tests__/failover-inflight.test.js +246 -0
- package/dist/src/bridge/internal/__tests__/failover-inflight.test.js.map +1 -0
- package/dist/src/bridge/internal/__tests__/host-client-integration.test.d.ts +10 -0
- package/dist/src/bridge/internal/__tests__/host-client-integration.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/__tests__/host-client-integration.test.js +298 -0
- package/dist/src/bridge/internal/__tests__/host-client-integration.test.js.map +1 -0
- package/dist/src/bridge/internal/bridge-client.d.ts +70 -0
- package/dist/src/bridge/internal/bridge-client.d.ts.map +1 -0
- package/dist/src/bridge/internal/bridge-client.js +344 -0
- package/dist/src/bridge/internal/bridge-client.js.map +1 -0
- package/dist/src/bridge/internal/bridge-client.test.d.ts +7 -0
- package/dist/src/bridge/internal/bridge-client.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/bridge-client.test.js +301 -0
- package/dist/src/bridge/internal/bridge-client.test.js.map +1 -0
- package/dist/src/bridge/internal/bridge-host.d.ts +78 -0
- package/dist/src/bridge/internal/bridge-host.d.ts.map +1 -0
- package/dist/src/bridge/internal/bridge-host.js +383 -0
- package/dist/src/bridge/internal/bridge-host.js.map +1 -0
- package/dist/src/bridge/internal/bridge-host.test.d.ts +7 -0
- package/dist/src/bridge/internal/bridge-host.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/bridge-host.test.js +377 -0
- package/dist/src/bridge/internal/bridge-host.test.js.map +1 -0
- package/dist/src/bridge/internal/environment-detection.d.ts +44 -0
- package/dist/src/bridge/internal/environment-detection.d.ts.map +1 -0
- package/dist/src/bridge/internal/environment-detection.js +91 -0
- package/dist/src/bridge/internal/environment-detection.js.map +1 -0
- package/dist/src/bridge/internal/environment-detection.test.d.ts +7 -0
- package/dist/src/bridge/internal/environment-detection.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/environment-detection.test.js +150 -0
- package/dist/src/bridge/internal/environment-detection.test.js.map +1 -0
- package/dist/src/bridge/internal/hand-off.d.ts +69 -0
- package/dist/src/bridge/internal/hand-off.d.ts.map +1 -0
- package/dist/src/bridge/internal/hand-off.js +178 -0
- package/dist/src/bridge/internal/hand-off.js.map +1 -0
- package/dist/src/bridge/internal/hand-off.test.d.ts +7 -0
- package/dist/src/bridge/internal/hand-off.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/hand-off.test.js +325 -0
- package/dist/src/bridge/internal/hand-off.test.js.map +1 -0
- package/dist/src/bridge/internal/health-endpoint.d.ts +32 -0
- package/dist/src/bridge/internal/health-endpoint.d.ts.map +1 -0
- package/dist/src/bridge/internal/health-endpoint.js +73 -0
- package/dist/src/bridge/internal/health-endpoint.js.map +1 -0
- package/dist/src/bridge/internal/health-endpoint.test.d.ts +6 -0
- package/dist/src/bridge/internal/health-endpoint.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/health-endpoint.test.js +122 -0
- package/dist/src/bridge/internal/health-endpoint.test.js.map +1 -0
- package/dist/src/bridge/internal/host-protocol.d.ts +60 -0
- package/dist/src/bridge/internal/host-protocol.d.ts.map +1 -0
- package/dist/src/bridge/internal/host-protocol.js +184 -0
- package/dist/src/bridge/internal/host-protocol.js.map +1 -0
- package/dist/src/bridge/internal/host-protocol.test.d.ts +6 -0
- package/dist/src/bridge/internal/host-protocol.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/host-protocol.test.js +307 -0
- package/dist/src/bridge/internal/host-protocol.test.js.map +1 -0
- package/dist/src/bridge/internal/session-tracker.d.ts +67 -0
- package/dist/src/bridge/internal/session-tracker.d.ts.map +1 -0
- package/dist/src/bridge/internal/session-tracker.js +138 -0
- package/dist/src/bridge/internal/session-tracker.js.map +1 -0
- package/dist/src/bridge/internal/session-tracker.test.d.ts +6 -0
- package/dist/src/bridge/internal/session-tracker.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/session-tracker.test.js +342 -0
- package/dist/src/bridge/internal/session-tracker.test.js.map +1 -0
- package/dist/src/bridge/internal/transport-client.d.ts +43 -0
- package/dist/src/bridge/internal/transport-client.d.ts.map +1 -0
- package/dist/src/bridge/internal/transport-client.js +146 -0
- package/dist/src/bridge/internal/transport-client.js.map +1 -0
- package/dist/src/bridge/internal/transport-client.test.d.ts +6 -0
- package/dist/src/bridge/internal/transport-client.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/transport-client.test.js +176 -0
- package/dist/src/bridge/internal/transport-client.test.js.map +1 -0
- package/dist/src/bridge/internal/transport-server.d.ts +59 -0
- package/dist/src/bridge/internal/transport-server.d.ts.map +1 -0
- package/dist/src/bridge/internal/transport-server.js +189 -0
- package/dist/src/bridge/internal/transport-server.js.map +1 -0
- package/dist/src/bridge/internal/transport-server.test.d.ts +6 -0
- package/dist/src/bridge/internal/transport-server.test.d.ts.map +1 -0
- package/dist/src/bridge/internal/transport-server.test.js +171 -0
- package/dist/src/bridge/internal/transport-server.test.js.map +1 -0
- package/dist/src/bridge/types.d.ts +117 -0
- package/dist/src/bridge/types.d.ts.map +1 -0
- package/dist/src/bridge/types.js +68 -0
- package/dist/src/bridge/types.js.map +1 -0
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.d.ts +6 -0
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.d.ts.map +1 -0
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.js +260 -0
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.js.map +1 -0
- package/dist/src/cli/adapters/cli-command-adapter.d.ts +39 -0
- package/dist/src/cli/adapters/cli-command-adapter.d.ts.map +1 -0
- package/dist/src/cli/adapters/cli-command-adapter.js +241 -0
- package/dist/src/cli/adapters/cli-command-adapter.js.map +1 -0
- package/dist/src/cli/adapters/cli-command-adapter.test.d.ts +5 -0
- package/dist/src/cli/adapters/cli-command-adapter.test.d.ts.map +1 -0
- package/dist/src/cli/adapters/cli-command-adapter.test.js +188 -0
- package/dist/src/cli/adapters/cli-command-adapter.test.js.map +1 -0
- package/dist/src/cli/adapters/group-builder.d.ts +29 -0
- package/dist/src/cli/adapters/group-builder.d.ts.map +1 -0
- package/dist/src/cli/adapters/group-builder.js +54 -0
- package/dist/src/cli/adapters/group-builder.js.map +1 -0
- package/dist/src/cli/adapters/group-builder.test.d.ts +5 -0
- package/dist/src/cli/adapters/group-builder.test.d.ts.map +1 -0
- package/dist/src/cli/adapters/group-builder.test.js +108 -0
- package/dist/src/cli/adapters/group-builder.test.js.map +1 -0
- package/dist/src/cli/adapters/help-formatter.d.ts +11 -0
- package/dist/src/cli/adapters/help-formatter.d.ts.map +1 -0
- package/dist/src/cli/adapters/help-formatter.js +78 -0
- package/dist/src/cli/adapters/help-formatter.js.map +1 -0
- package/dist/src/cli/adapters/help-formatter.test.d.ts +5 -0
- package/dist/src/cli/adapters/help-formatter.test.d.ts.map +1 -0
- package/dist/src/cli/adapters/help-formatter.test.js +79 -0
- package/dist/src/cli/adapters/help-formatter.test.js.map +1 -0
- package/dist/src/cli/adapters/target-resolver.d.ts +54 -0
- package/dist/src/cli/adapters/target-resolver.d.ts.map +1 -0
- package/dist/src/cli/adapters/target-resolver.js +90 -0
- package/dist/src/cli/adapters/target-resolver.js.map +1 -0
- package/dist/src/cli/adapters/target-resolver.test.d.ts +5 -0
- package/dist/src/cli/adapters/target-resolver.test.d.ts.map +1 -0
- package/dist/src/cli/adapters/target-resolver.test.js +169 -0
- package/dist/src/cli/adapters/target-resolver.test.js.map +1 -0
- package/dist/src/cli/args/global-args.d.ts +2 -0
- package/dist/src/cli/args/global-args.d.ts.map +1 -1
- package/dist/src/cli/cli.d.ts +6 -3
- package/dist/src/cli/cli.d.ts.map +1 -1
- package/dist/src/cli/cli.js +82 -20
- package/dist/src/cli/cli.js.map +1 -1
- package/dist/src/cli/resolve-script-content.d.ts +19 -0
- package/dist/src/cli/resolve-script-content.d.ts.map +1 -0
- package/dist/src/cli/resolve-script-content.js +22 -0
- package/dist/src/cli/resolve-script-content.js.map +1 -0
- package/dist/src/cli/resolve-session.d.ts +21 -0
- package/dist/src/cli/resolve-session.d.ts.map +1 -0
- package/dist/src/cli/resolve-session.js +40 -0
- package/dist/src/cli/resolve-session.js.map +1 -0
- package/dist/src/cli/resolve-session.test.d.ts +5 -0
- package/dist/src/cli/resolve-session.test.d.ts.map +1 -0
- package/dist/src/cli/resolve-session.test.js +112 -0
- package/dist/src/cli/resolve-session.test.js.map +1 -0
- package/dist/src/cli/script-executor.d.ts +1 -0
- package/dist/src/cli/script-executor.d.ts.map +1 -1
- package/dist/src/cli/script-executor.js +7 -1
- package/dist/src/cli/script-executor.js.map +1 -1
- package/dist/src/cli/types.d.ts +11 -0
- package/dist/src/cli/types.d.ts.map +1 -0
- package/dist/src/cli/types.js +5 -0
- package/dist/src/cli/types.js.map +1 -0
- package/dist/src/commands/connect.d.ts +16 -0
- package/dist/src/commands/connect.d.ts.map +1 -0
- package/dist/src/commands/connect.js +15 -0
- package/dist/src/commands/connect.js.map +1 -0
- package/dist/src/commands/connect.test.d.ts +5 -0
- package/dist/src/commands/connect.test.d.ts.map +1 -0
- package/dist/src/commands/connect.test.js +46 -0
- package/dist/src/commands/connect.test.js.map +1 -0
- package/dist/src/commands/console/exec/exec.d.ts +43 -0
- package/dist/src/commands/console/exec/exec.d.ts.map +1 -0
- package/dist/src/commands/console/exec/exec.js +79 -0
- package/dist/src/commands/console/exec/exec.js.map +1 -0
- package/dist/src/commands/console/exec/exec.test.d.ts +5 -0
- package/dist/src/commands/console/exec/exec.test.d.ts.map +1 -0
- package/dist/src/commands/console/exec/exec.test.js +191 -0
- package/dist/src/commands/console/exec/exec.test.js.map +1 -0
- package/dist/src/commands/console/logs/logs.d.ts +23 -0
- package/dist/src/commands/console/logs/logs.d.ts.map +1 -0
- package/dist/src/commands/console/logs/logs.js +80 -0
- package/dist/src/commands/console/logs/logs.js.map +1 -0
- package/dist/src/commands/console/logs/logs.test.d.ts +5 -0
- package/dist/src/commands/console/logs/logs.test.d.ts.map +1 -0
- package/dist/src/commands/console/logs/logs.test.js +111 -0
- package/dist/src/commands/console/logs/logs.test.js.map +1 -0
- package/dist/src/commands/disconnect.d.ts +9 -0
- package/dist/src/commands/disconnect.d.ts.map +1 -0
- package/dist/src/commands/disconnect.js +8 -0
- package/dist/src/commands/disconnect.js.map +1 -0
- package/dist/src/commands/disconnect.test.d.ts +5 -0
- package/dist/src/commands/disconnect.test.d.ts.map +1 -0
- package/dist/src/commands/disconnect.test.js +17 -0
- package/dist/src/commands/disconnect.test.js.map +1 -0
- package/dist/src/commands/explorer/query/query.d.ts +31 -0
- package/dist/src/commands/explorer/query/query.d.ts.map +1 -0
- package/dist/src/commands/explorer/query/query.js +106 -0
- package/dist/src/commands/explorer/query/query.js.map +1 -0
- package/dist/src/commands/explorer/query/query.test.d.ts +5 -0
- package/dist/src/commands/explorer/query/query.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/query/query.test.js +228 -0
- package/dist/src/commands/explorer/query/query.test.js.map +1 -0
- package/dist/src/commands/formatters.test.d.ts +6 -0
- package/dist/src/commands/formatters.test.d.ts.map +1 -0
- package/dist/src/commands/formatters.test.js +211 -0
- package/dist/src/commands/formatters.test.js.map +1 -0
- package/dist/src/commands/framework/action-loader.d.ts +25 -0
- package/dist/src/commands/framework/action-loader.d.ts.map +1 -0
- package/dist/src/commands/framework/action-loader.js +46 -0
- package/dist/src/commands/framework/action-loader.js.map +1 -0
- package/dist/src/commands/framework/action-loader.test.d.ts +5 -0
- package/dist/src/commands/framework/action-loader.test.d.ts.map +1 -0
- package/dist/src/commands/framework/action-loader.test.js +89 -0
- package/dist/src/commands/framework/action-loader.test.js.map +1 -0
- package/dist/src/commands/framework/arg-builder.d.ts +92 -0
- package/dist/src/commands/framework/arg-builder.d.ts.map +1 -0
- package/dist/src/commands/framework/arg-builder.js +137 -0
- package/dist/src/commands/framework/arg-builder.js.map +1 -0
- package/dist/src/commands/framework/arg-builder.test.d.ts +5 -0
- package/dist/src/commands/framework/arg-builder.test.d.ts.map +1 -0
- package/dist/src/commands/framework/arg-builder.test.js +246 -0
- package/dist/src/commands/framework/arg-builder.test.js.map +1 -0
- package/dist/src/commands/framework/command-registry.d.ts +23 -0
- package/dist/src/commands/framework/command-registry.d.ts.map +1 -0
- package/dist/src/commands/framework/command-registry.js +42 -0
- package/dist/src/commands/framework/command-registry.js.map +1 -0
- package/dist/src/commands/framework/command-registry.test.d.ts +5 -0
- package/dist/src/commands/framework/command-registry.test.d.ts.map +1 -0
- package/dist/src/commands/framework/command-registry.test.js +158 -0
- package/dist/src/commands/framework/command-registry.test.js.map +1 -0
- package/dist/src/commands/framework/define-command.d.ts +76 -0
- package/dist/src/commands/framework/define-command.d.ts.map +1 -0
- package/dist/src/commands/framework/define-command.js +28 -0
- package/dist/src/commands/framework/define-command.js.map +1 -0
- package/dist/src/commands/framework/define-command.test.d.ts +5 -0
- package/dist/src/commands/framework/define-command.test.d.ts.map +1 -0
- package/dist/src/commands/framework/define-command.test.js +135 -0
- package/dist/src/commands/framework/define-command.test.js.map +1 -0
- package/dist/src/commands/framework/index.d.ts +8 -0
- package/dist/src/commands/framework/index.d.ts.map +1 -0
- package/dist/src/commands/framework/index.js +8 -0
- package/dist/src/commands/framework/index.js.map +1 -0
- package/dist/src/commands/index.d.ts +20 -0
- package/dist/src/commands/index.d.ts.map +1 -0
- package/dist/src/commands/index.js +20 -0
- package/dist/src/commands/index.js.map +1 -0
- package/dist/src/commands/linux/inject-credentials/inject-credentials.d.ts +15 -0
- package/dist/src/commands/linux/inject-credentials/inject-credentials.d.ts.map +1 -0
- package/dist/src/commands/linux/inject-credentials/inject-credentials.js +88 -0
- package/dist/src/commands/linux/inject-credentials/inject-credentials.js.map +1 -0
- package/dist/src/commands/linux/setup/setup.d.ts +19 -0
- package/dist/src/commands/linux/setup/setup.d.ts.map +1 -0
- package/dist/src/commands/linux/setup/setup.js +107 -0
- package/dist/src/commands/linux/setup/setup.js.map +1 -0
- package/dist/src/commands/linux/status/status.d.ts +34 -0
- package/dist/src/commands/linux/status/status.d.ts.map +1 -0
- package/dist/src/commands/linux/status/status.js +134 -0
- package/dist/src/commands/linux/status/status.js.map +1 -0
- package/dist/src/commands/plugin/install/install.d.ts +10 -0
- package/dist/src/commands/plugin/install/install.d.ts.map +1 -0
- package/dist/src/commands/plugin/install/install.js +23 -0
- package/dist/src/commands/plugin/install/install.js.map +1 -0
- package/dist/src/commands/plugin/uninstall/uninstall.d.ts +9 -0
- package/dist/src/commands/plugin/uninstall/uninstall.d.ts.map +1 -0
- package/dist/src/commands/plugin/uninstall/uninstall.js +41 -0
- package/dist/src/commands/plugin/uninstall/uninstall.js.map +1 -0
- package/dist/src/commands/process/close/close.d.ts +22 -0
- package/dist/src/commands/process/close/close.d.ts.map +1 -0
- package/dist/src/commands/process/close/close.js +34 -0
- package/dist/src/commands/process/close/close.js.map +1 -0
- package/dist/src/commands/process/format-state.d.ts +7 -0
- package/dist/src/commands/process/format-state.d.ts.map +1 -0
- package/dist/src/commands/process/format-state.js +19 -0
- package/dist/src/commands/process/format-state.js.map +1 -0
- package/dist/src/commands/process/info/info.d.ts +17 -0
- package/dist/src/commands/process/info/info.d.ts.map +1 -0
- package/dist/src/commands/process/info/info.js +38 -0
- package/dist/src/commands/process/info/info.js.map +1 -0
- package/dist/src/commands/process/info/info.test.d.ts +5 -0
- package/dist/src/commands/process/info/info.test.d.ts.map +1 -0
- package/dist/src/commands/process/info/info.test.js +58 -0
- package/dist/src/commands/process/info/info.test.js.map +1 -0
- package/dist/src/commands/process/launch/launch.d.ts +17 -0
- package/dist/src/commands/process/launch/launch.d.ts.map +1 -0
- package/dist/src/commands/process/launch/launch.js +26 -0
- package/dist/src/commands/process/launch/launch.js.map +1 -0
- package/dist/src/commands/process/launch/launch.test.d.ts +5 -0
- package/dist/src/commands/process/launch/launch.test.d.ts.map +1 -0
- package/dist/src/commands/process/launch/launch.test.js +49 -0
- package/dist/src/commands/process/launch/launch.test.js.map +1 -0
- package/dist/src/commands/process/list/list.d.ts +16 -0
- package/dist/src/commands/process/list/list.d.ts.map +1 -0
- package/dist/src/commands/process/list/list.js +56 -0
- package/dist/src/commands/process/list/list.js.map +1 -0
- package/dist/src/commands/process/list/list.test.d.ts +5 -0
- package/dist/src/commands/process/list/list.test.d.ts.map +1 -0
- package/dist/src/commands/process/list/list.test.js +71 -0
- package/dist/src/commands/process/list/list.test.js.map +1 -0
- package/dist/src/commands/process/run/run.d.ts +31 -0
- package/dist/src/commands/process/run/run.d.ts.map +1 -0
- package/dist/src/commands/process/run/run.js +63 -0
- package/dist/src/commands/process/run/run.js.map +1 -0
- package/dist/src/commands/rgba-to-png.d.ts +17 -0
- package/dist/src/commands/rgba-to-png.d.ts.map +1 -0
- package/dist/src/commands/rgba-to-png.js +85 -0
- package/dist/src/commands/rgba-to-png.js.map +1 -0
- package/dist/src/commands/serve/serve.d.ts +17 -0
- package/dist/src/commands/serve/serve.d.ts.map +1 -0
- package/dist/src/commands/serve/serve.js +128 -0
- package/dist/src/commands/serve/serve.js.map +1 -0
- package/dist/src/commands/serve/serve.test.d.ts +5 -0
- package/dist/src/commands/serve/serve.test.d.ts.map +1 -0
- package/dist/src/commands/serve/serve.test.js +100 -0
- package/dist/src/commands/serve/serve.test.js.map +1 -0
- package/dist/src/commands/viewport/screenshot/screenshot.d.ts +17 -0
- package/dist/src/commands/viewport/screenshot/screenshot.d.ts.map +1 -0
- package/dist/src/commands/viewport/screenshot/screenshot.js +45 -0
- package/dist/src/commands/viewport/screenshot/screenshot.js.map +1 -0
- package/dist/src/commands/viewport/screenshot/screenshot.test.d.ts +5 -0
- package/dist/src/commands/viewport/screenshot/screenshot.test.d.ts.map +1 -0
- package/dist/src/commands/viewport/screenshot/screenshot.test.js +143 -0
- package/dist/src/commands/viewport/screenshot/screenshot.test.js.map +1 -0
- package/dist/src/docker/docker-delegator.d.ts +30 -0
- package/dist/src/docker/docker-delegator.d.ts.map +1 -0
- package/dist/src/docker/docker-delegator.js +201 -0
- package/dist/src/docker/docker-delegator.js.map +1 -0
- package/dist/src/docker/docker-delegator.test.d.ts +2 -0
- package/dist/src/docker/docker-delegator.test.d.ts.map +1 -0
- package/dist/src/docker/docker-delegator.test.js +132 -0
- package/dist/src/docker/docker-delegator.test.js.map +1 -0
- package/dist/src/index.d.ts +9 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/index.test.d.ts +2 -0
- package/dist/src/index.test.d.ts.map +1 -0
- package/dist/src/index.test.js +51 -0
- package/dist/src/index.test.js.map +1 -0
- package/dist/src/linux/index.d.ts +12 -0
- package/dist/src/linux/index.d.ts.map +1 -0
- package/dist/src/linux/index.js +11 -0
- package/dist/src/linux/index.js.map +1 -0
- package/dist/src/linux/linux-config.d.ts +37 -0
- package/dist/src/linux/linux-config.d.ts.map +1 -0
- package/dist/src/linux/linux-config.js +70 -0
- package/dist/src/linux/linux-config.js.map +1 -0
- package/dist/src/linux/linux-config.test.d.ts +2 -0
- package/dist/src/linux/linux-config.test.d.ts.map +1 -0
- package/dist/src/linux/linux-config.test.js +54 -0
- package/dist/src/linux/linux-config.test.js.map +1 -0
- package/dist/src/linux/linux-credential-writer.d.ts +35 -0
- package/dist/src/linux/linux-credential-writer.d.ts.map +1 -0
- package/dist/src/linux/linux-credential-writer.js +300 -0
- package/dist/src/linux/linux-credential-writer.js.map +1 -0
- package/dist/src/linux/linux-display-manager.d.ts +24 -0
- package/dist/src/linux/linux-display-manager.d.ts.map +1 -0
- package/dist/src/linux/linux-display-manager.js +92 -0
- package/dist/src/linux/linux-display-manager.js.map +1 -0
- package/dist/src/linux/linux-env-guard.d.ts +13 -0
- package/dist/src/linux/linux-env-guard.d.ts.map +1 -0
- package/dist/src/linux/linux-env-guard.js +31 -0
- package/dist/src/linux/linux-env-guard.js.map +1 -0
- package/dist/src/linux/linux-env-guard.test.d.ts +2 -0
- package/dist/src/linux/linux-env-guard.test.d.ts.map +1 -0
- package/dist/src/linux/linux-env-guard.test.js +48 -0
- package/dist/src/linux/linux-env-guard.test.js.map +1 -0
- package/dist/src/linux/linux-fflags.d.ts +17 -0
- package/dist/src/linux/linux-fflags.d.ts.map +1 -0
- package/dist/src/linux/linux-fflags.js +33 -0
- package/dist/src/linux/linux-fflags.js.map +1 -0
- package/dist/src/linux/linux-fflags.test.d.ts +2 -0
- package/dist/src/linux/linux-fflags.test.d.ts.map +1 -0
- package/dist/src/linux/linux-fflags.test.js +55 -0
- package/dist/src/linux/linux-fflags.test.js.map +1 -0
- package/dist/src/linux/linux-prerequisites.d.ts +24 -0
- package/dist/src/linux/linux-prerequisites.d.ts.map +1 -0
- package/dist/src/linux/linux-prerequisites.js +118 -0
- package/dist/src/linux/linux-prerequisites.js.map +1 -0
- package/dist/src/linux/linux-shader-patcher.d.ts +17 -0
- package/dist/src/linux/linux-shader-patcher.d.ts.map +1 -0
- package/dist/src/linux/linux-shader-patcher.js +48 -0
- package/dist/src/linux/linux-shader-patcher.js.map +1 -0
- package/dist/src/linux/linux-shader-patcher.test.d.ts +2 -0
- package/dist/src/linux/linux-shader-patcher.test.d.ts.map +1 -0
- package/dist/src/linux/linux-shader-patcher.test.js +55 -0
- package/dist/src/linux/linux-shader-patcher.test.js.map +1 -0
- package/dist/src/linux/linux-studio-installer.d.ts +10 -0
- package/dist/src/linux/linux-studio-installer.d.ts.map +1 -0
- package/dist/src/linux/linux-studio-installer.js +79 -0
- package/dist/src/linux/linux-studio-installer.js.map +1 -0
- package/dist/src/linux/linux-studio-launcher.d.ts +10 -0
- package/dist/src/linux/linux-studio-launcher.d.ts.map +1 -0
- package/dist/src/linux/linux-studio-launcher.js +73 -0
- package/dist/src/linux/linux-studio-launcher.js.map +1 -0
- package/dist/src/linux/linux-version-resolver.d.ts +18 -0
- package/dist/src/linux/linux-version-resolver.d.ts.map +1 -0
- package/dist/src/linux/linux-version-resolver.js +49 -0
- package/dist/src/linux/linux-version-resolver.js.map +1 -0
- package/dist/src/linux/linux-wine-env.d.ts +11 -0
- package/dist/src/linux/linux-wine-env.d.ts.map +1 -0
- package/dist/src/linux/linux-wine-env.js +33 -0
- package/dist/src/linux/linux-wine-env.js.map +1 -0
- package/dist/src/linux/linux-wine-env.test.d.ts +2 -0
- package/dist/src/linux/linux-wine-env.test.d.ts.map +1 -0
- package/dist/src/linux/linux-wine-env.test.js +47 -0
- package/dist/src/linux/linux-wine-env.test.js.map +1 -0
- package/dist/src/plugin/persistent-plugin-installer.d.ts +21 -0
- package/dist/src/plugin/persistent-plugin-installer.d.ts.map +1 -0
- package/dist/src/plugin/persistent-plugin-installer.js +69 -0
- package/dist/src/plugin/persistent-plugin-installer.js.map +1 -0
- package/dist/src/plugin/persistent-plugin-installer.test.d.ts +5 -0
- package/dist/src/plugin/persistent-plugin-installer.test.d.ts.map +1 -0
- package/dist/src/plugin/persistent-plugin-installer.test.js +119 -0
- package/dist/src/plugin/persistent-plugin-installer.test.js.map +1 -0
- package/dist/src/plugin/plugin-discovery.d.ts +7 -0
- package/dist/src/plugin/plugin-discovery.d.ts.map +1 -0
- package/dist/src/plugin/plugin-discovery.js +18 -0
- package/dist/src/plugin/plugin-discovery.js.map +1 -0
- package/dist/src/plugin/plugin-discovery.test.d.ts +5 -0
- package/dist/src/plugin/plugin-discovery.test.d.ts.map +1 -0
- package/dist/src/plugin/plugin-discovery.test.js +55 -0
- package/dist/src/plugin/plugin-discovery.test.js.map +1 -0
- package/dist/src/plugin/plugin-injector.js +1 -1
- package/dist/src/plugin/plugin-injector.js.map +1 -1
- package/dist/src/process/studio-process-manager.d.ts +7 -3
- package/dist/src/process/studio-process-manager.d.ts.map +1 -1
- package/dist/src/process/studio-process-manager.js +40 -11
- package/dist/src/process/studio-process-manager.js.map +1 -1
- package/dist/src/process/studio-process-manager.test.js +18 -1
- package/dist/src/process/studio-process-manager.test.js.map +1 -1
- package/dist/src/server/action-dispatcher.d.ts +35 -0
- package/dist/src/server/action-dispatcher.d.ts.map +1 -0
- package/dist/src/server/action-dispatcher.js +67 -0
- package/dist/src/server/action-dispatcher.js.map +1 -0
- package/dist/src/server/action-dispatcher.test.d.ts +6 -0
- package/dist/src/server/action-dispatcher.test.d.ts.map +1 -0
- package/dist/src/server/action-dispatcher.test.js +173 -0
- package/dist/src/server/action-dispatcher.test.js.map +1 -0
- package/dist/src/server/index.d.ts +1 -1
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/pending-request-map.d.ts +33 -0
- package/dist/src/server/pending-request-map.d.ts.map +1 -0
- package/dist/src/server/pending-request-map.js +69 -0
- package/dist/src/server/pending-request-map.js.map +1 -0
- package/dist/src/server/pending-request-map.test.d.ts +2 -0
- package/dist/src/server/pending-request-map.test.d.ts.map +1 -0
- package/dist/src/server/pending-request-map.test.js +192 -0
- package/dist/src/server/pending-request-map.test.js.map +1 -0
- package/dist/src/server/plugin-detection.test.d.ts +7 -0
- package/dist/src/server/plugin-detection.test.d.ts.map +1 -0
- package/dist/src/server/plugin-detection.test.js +154 -0
- package/dist/src/server/plugin-detection.test.js.map +1 -0
- package/dist/src/server/studio-bridge-server.d.ts +44 -7
- package/dist/src/server/studio-bridge-server.d.ts.map +1 -1
- package/dist/src/server/studio-bridge-server.js +341 -91
- package/dist/src/server/studio-bridge-server.js.map +1 -1
- package/dist/src/server/studio-bridge-server.test.js +231 -207
- package/dist/src/server/studio-bridge-server.test.js.map +1 -1
- package/dist/src/server/web-socket-protocol-basic.test.d.ts +2 -0
- package/dist/src/server/web-socket-protocol-basic.test.d.ts.map +1 -0
- package/dist/src/server/web-socket-protocol-basic.test.js +98 -0
- package/dist/src/server/web-socket-protocol-basic.test.js.map +1 -0
- package/dist/src/server/web-socket-protocol.d.ts +218 -20
- package/dist/src/server/web-socket-protocol.d.ts.map +1 -1
- package/dist/src/server/web-socket-protocol.js +346 -20
- package/dist/src/server/web-socket-protocol.js.map +1 -1
- package/dist/src/server/web-socket-protocol.test.js +572 -146
- package/dist/src/server/web-socket-protocol.test.js.map +1 -1
- package/dist/src/test/e2e/hand-off.test.d.ts +9 -0
- package/dist/src/test/e2e/hand-off.test.d.ts.map +1 -0
- package/dist/src/test/e2e/hand-off.test.js +274 -0
- package/dist/src/test/e2e/hand-off.test.js.map +1 -0
- package/dist/src/test/e2e/persistent-session.test.d.ts +11 -0
- package/dist/src/test/e2e/persistent-session.test.d.ts.map +1 -0
- package/dist/src/test/e2e/persistent-session.test.js +367 -0
- package/dist/src/test/e2e/persistent-session.test.js.map +1 -0
- package/dist/src/test/e2e/split-server.test.d.ts +14 -0
- package/dist/src/test/e2e/split-server.test.d.ts.map +1 -0
- package/dist/src/test/e2e/split-server.test.js +230 -0
- package/dist/src/test/e2e/split-server.test.js.map +1 -0
- package/dist/src/test/helpers/mock-plugin-client.d.ts +69 -0
- package/dist/src/test/helpers/mock-plugin-client.d.ts.map +1 -0
- package/dist/src/test/helpers/mock-plugin-client.js +213 -0
- package/dist/src/test/helpers/mock-plugin-client.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docker/.dockerignore +2 -0
- package/docker/Dockerfile +113 -0
- package/docker/docker-compose.yml +20 -0
- package/docker/entrypoint.sh +19 -0
- package/package.json +8 -6
- package/src/bridge/bridge-connection-remote.test.ts +189 -0
- package/src/bridge/bridge-connection.test.ts +1076 -0
- package/src/bridge/bridge-connection.ts +718 -0
- package/src/bridge/bridge-session.test.ts +336 -0
- package/src/bridge/bridge-session.ts +365 -0
- package/src/bridge/index.ts +42 -0
- package/src/bridge/internal/__tests__/failover-crash.test.ts +191 -0
- package/src/bridge/internal/__tests__/failover-graceful.test.ts +268 -0
- package/src/bridge/internal/__tests__/failover-inflight.test.ts +329 -0
- package/src/bridge/internal/__tests__/host-client-integration.test.ts +387 -0
- package/src/bridge/internal/bridge-client.test.ts +399 -0
- package/src/bridge/internal/bridge-client.ts +434 -0
- package/src/bridge/internal/bridge-host.test.ts +501 -0
- package/src/bridge/internal/bridge-host.ts +531 -0
- package/src/bridge/internal/environment-detection.test.ts +180 -0
- package/src/bridge/internal/environment-detection.ts +119 -0
- package/src/bridge/internal/hand-off.test.ts +408 -0
- package/src/bridge/internal/hand-off.ts +275 -0
- package/src/bridge/internal/health-endpoint.test.ts +147 -0
- package/src/bridge/internal/health-endpoint.ts +100 -0
- package/src/bridge/internal/host-protocol.test.ts +415 -0
- package/src/bridge/internal/host-protocol.ts +255 -0
- package/src/bridge/internal/session-tracker.test.ts +535 -0
- package/src/bridge/internal/session-tracker.ts +198 -0
- package/src/bridge/internal/transport-client.test.ts +241 -0
- package/src/bridge/internal/transport-client.ts +194 -0
- package/src/bridge/internal/transport-server.test.ts +220 -0
- package/src/bridge/internal/transport-server.ts +242 -0
- package/src/bridge/types.ts +161 -0
- package/src/cli/adapters/cli-command-adapter-handler.test.ts +318 -0
- package/src/cli/adapters/cli-command-adapter.test.ts +221 -0
- package/src/cli/adapters/cli-command-adapter.ts +342 -0
- package/src/cli/adapters/group-builder.test.ts +146 -0
- package/src/cli/adapters/group-builder.ts +81 -0
- package/src/cli/adapters/help-formatter.test.ts +97 -0
- package/src/cli/adapters/help-formatter.ts +86 -0
- package/src/cli/adapters/target-resolver.test.ts +222 -0
- package/src/cli/adapters/target-resolver.ts +144 -0
- package/src/cli/args/global-args.ts +2 -0
- package/src/cli/cli.ts +100 -21
- package/src/cli/resolve-script-content.ts +36 -0
- package/src/cli/resolve-session.test.ts +141 -0
- package/src/cli/resolve-session.ts +66 -0
- package/src/cli/script-executor.ts +22 -4
- package/src/cli/types.ts +11 -0
- package/src/commands/connect.test.ts +61 -0
- package/src/commands/connect.ts +32 -0
- package/src/commands/console/exec/exec.test.ts +258 -0
- package/src/commands/console/exec/exec.ts +130 -0
- package/src/commands/console/exec/execute.luau +202 -0
- package/src/commands/console/logs/logs.test.ts +138 -0
- package/src/commands/console/logs/logs.ts +115 -0
- package/src/commands/console/logs/query-logs.luau +76 -0
- package/src/commands/disconnect.test.ts +21 -0
- package/src/commands/disconnect.ts +12 -0
- package/src/commands/explorer/query/query-data-model.luau +206 -0
- package/src/commands/explorer/query/query.test.ts +297 -0
- package/src/commands/explorer/query/query.ts +157 -0
- package/src/commands/formatters.test.ts +237 -0
- package/src/commands/framework/action-loader.test.ts +118 -0
- package/src/commands/framework/action-loader.ts +65 -0
- package/src/commands/framework/arg-builder.test.ts +298 -0
- package/src/commands/framework/arg-builder.ts +203 -0
- package/src/commands/framework/command-registry.test.ts +204 -0
- package/src/commands/framework/command-registry.ts +51 -0
- package/src/commands/framework/define-command.test.ts +161 -0
- package/src/commands/framework/define-command.ts +114 -0
- package/src/commands/framework/index.ts +33 -0
- package/src/commands/index.ts +72 -0
- package/src/commands/linux/inject-credentials/inject-credentials.ts +118 -0
- package/src/commands/linux/setup/setup.ts +145 -0
- package/src/commands/linux/status/status.ts +180 -0
- package/src/commands/plugin/install/install.ts +31 -0
- package/src/commands/plugin/uninstall/uninstall.ts +54 -0
- package/src/commands/process/close/close.ts +52 -0
- package/src/commands/process/format-state.ts +21 -0
- package/src/commands/process/info/info.test.ts +76 -0
- package/src/commands/process/info/info.ts +54 -0
- package/src/commands/process/info/query-state.luau +48 -0
- package/src/commands/process/launch/launch.test.ts +65 -0
- package/src/commands/process/launch/launch.ts +45 -0
- package/src/commands/process/list/list.test.ts +82 -0
- package/src/commands/process/list/list.ts +75 -0
- package/src/commands/process/run/run.ts +98 -0
- package/src/commands/rgba-to-png.ts +98 -0
- package/src/commands/serve/serve.test.ts +139 -0
- package/src/commands/serve/serve.ts +161 -0
- package/src/commands/viewport/screenshot/capture-screenshot.luau +247 -0
- package/src/commands/viewport/screenshot/screenshot.test.ts +181 -0
- package/src/commands/viewport/screenshot/screenshot.ts +71 -0
- package/src/docker/docker-delegator.test.ts +159 -0
- package/src/docker/docker-delegator.ts +240 -0
- package/src/index.test.ts +58 -0
- package/src/index.ts +104 -3
- package/src/linux/README.md +130 -0
- package/src/linux/index.ts +25 -0
- package/src/linux/linux-config.test.ts +66 -0
- package/src/linux/linux-config.ts +102 -0
- package/src/linux/linux-credential-writer.ts +421 -0
- package/src/linux/linux-display-manager.ts +106 -0
- package/src/linux/linux-env-guard.test.ts +62 -0
- package/src/linux/linux-env-guard.ts +38 -0
- package/src/linux/linux-fflags.test.ts +78 -0
- package/src/linux/linux-fflags.ts +48 -0
- package/src/linux/linux-prerequisites.ts +152 -0
- package/src/linux/linux-shader-patcher.test.ts +76 -0
- package/src/linux/linux-shader-patcher.ts +56 -0
- package/src/linux/linux-studio-installer.ts +113 -0
- package/src/linux/linux-studio-launcher.ts +90 -0
- package/src/linux/linux-version-resolver.ts +74 -0
- package/src/linux/linux-wine-env.test.ts +56 -0
- package/src/linux/linux-wine-env.ts +38 -0
- package/src/linux/write-cred.c +48 -0
- package/src/plugin/persistent-plugin-installer.test.ts +171 -0
- package/src/plugin/persistent-plugin-installer.ts +90 -0
- package/src/plugin/plugin-discovery.test.ts +70 -0
- package/src/plugin/plugin-discovery.ts +21 -0
- package/src/plugin/plugin-injector.ts +1 -1
- package/src/process/studio-process-manager.test.ts +22 -1
- package/src/process/studio-process-manager.ts +59 -18
- package/src/server/action-dispatcher.test.ts +240 -0
- package/src/server/action-dispatcher.ts +89 -0
- package/src/server/index.ts +0 -3
- package/src/server/pending-request-map.test.ts +261 -0
- package/src/server/pending-request-map.ts +87 -0
- package/src/server/plugin-detection.test.ts +195 -0
- package/src/server/studio-bridge-server.test.ts +321 -283
- package/src/server/studio-bridge-server.ts +469 -111
- package/src/server/web-socket-protocol-basic.test.ts +131 -0
- package/src/server/web-socket-protocol.test.ts +658 -159
- package/src/server/web-socket-protocol.ts +668 -58
- package/src/test/e2e/hand-off.test.ts +338 -0
- package/src/test/e2e/persistent-session.test.ts +442 -0
- package/src/test/e2e/split-server.test.ts +290 -0
- package/src/test/helpers/mock-plugin-client.ts +276 -0
- package/templates/studio-bridge-plugin/src/Shared/ActionRouter.luau +245 -0
- package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.luau +203 -0
- package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.luau +144 -0
- package/templates/studio-bridge-plugin/src/Shared/Protocol.luau +132 -0
- package/templates/studio-bridge-plugin/src/StudioBridgePlugin.server.lua +364 -133
- package/templates/studio-bridge-plugin/src/Vendor/png.luau +1247 -0
- package/templates/studio-bridge-plugin-test/test/actions.test.luau +580 -0
- package/templates/studio-bridge-plugin-test/test/discovery.test.luau +622 -0
- package/templates/studio-bridge-plugin-test/test/execute-handler.test.luau +406 -0
- package/templates/studio-bridge-plugin-test/test/hash-lifecycle.test.luau +814 -0
- package/templates/studio-bridge-plugin-test/test/integration.test.luau +1020 -0
- package/templates/studio-bridge-plugin-test/test/new-actions.test.luau +339 -0
- package/templates/studio-bridge-plugin-test/test/plugin-entry.test.luau +493 -0
- package/templates/studio-bridge-plugin-test/test/protocol.test.luau +506 -0
- package/templates/studio-bridge-plugin-test/test/roblox-mocks.luau +108 -0
- package/templates/studio-bridge-plugin-test/test/test-runner.luau +112 -0
- package/dist/src/cli/commands/exec-command.d.ts +0 -15
- package/dist/src/cli/commands/exec-command.d.ts.map +0 -1
- package/dist/src/cli/commands/exec-command.js +0 -35
- package/dist/src/cli/commands/exec-command.js.map +0 -1
- package/dist/src/cli/commands/run-command.d.ts +0 -15
- package/dist/src/cli/commands/run-command.d.ts.map +0 -1
- package/dist/src/cli/commands/run-command.js +0 -47
- package/dist/src/cli/commands/run-command.js.map +0 -1
- package/dist/src/cli/commands/terminal/terminal-command.d.ts +0 -17
- package/dist/src/cli/commands/terminal/terminal-command.d.ts.map +0 -1
- package/dist/src/cli/commands/terminal/terminal-command.js +0 -41
- package/dist/src/cli/commands/terminal/terminal-command.js.map +0 -1
- package/dist/src/cli/commands/terminal/terminal-editor.d.ts +0 -43
- package/dist/src/cli/commands/terminal/terminal-editor.d.ts.map +0 -1
- package/dist/src/cli/commands/terminal/terminal-editor.js +0 -430
- package/dist/src/cli/commands/terminal/terminal-editor.js.map +0 -1
- package/dist/src/cli/commands/terminal/terminal-mode.d.ts +0 -13
- package/dist/src/cli/commands/terminal/terminal-mode.d.ts.map +0 -1
- package/dist/src/cli/commands/terminal/terminal-mode.js +0 -141
- package/dist/src/cli/commands/terminal/terminal-mode.js.map +0 -1
- package/dist/src/server/web-socket-protocol.smoke.test.d.ts +0 -6
- package/dist/src/server/web-socket-protocol.smoke.test.d.ts.map +0 -1
- package/dist/src/server/web-socket-protocol.smoke.test.js +0 -196
- package/dist/src/server/web-socket-protocol.smoke.test.js.map +0 -1
- package/src/cli/commands/exec-command.ts +0 -48
- package/src/cli/commands/run-command.ts +0 -63
- package/src/cli/commands/terminal/terminal-command.ts +0 -54
- package/src/cli/commands/terminal/terminal-editor.ts +0 -500
- package/src/cli/commands/terminal/terminal-mode.ts +0 -184
- package/src/server/web-socket-protocol.smoke.test.ts +0 -255
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge host that manages plugin connections. Creates a TransportServer
|
|
3
|
+
* and registers handlers for the /plugin and /health paths. Tracks
|
|
4
|
+
* connected plugins by sessionId and emits events on connect/disconnect.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { EventEmitter } from 'events';
|
|
8
|
+
import { randomUUID } from 'crypto';
|
|
9
|
+
import type { WebSocket, RawData } from 'ws';
|
|
10
|
+
import type { IncomingMessage } from 'http';
|
|
11
|
+
import { TransportServer } from './transport-server.js';
|
|
12
|
+
import { createHealthHandler } from './health-endpoint.js';
|
|
13
|
+
import {
|
|
14
|
+
decodeHostMessage,
|
|
15
|
+
encodeHostMessage,
|
|
16
|
+
type HostProtocolMessage,
|
|
17
|
+
type HostTransferNotice,
|
|
18
|
+
} from './host-protocol.js';
|
|
19
|
+
import {
|
|
20
|
+
decodePluginMessage,
|
|
21
|
+
encodeMessage,
|
|
22
|
+
type Capability,
|
|
23
|
+
type ServerMessage,
|
|
24
|
+
} from '../../server/web-socket-protocol.js';
|
|
25
|
+
import { PendingRequestMap } from '../../server/pending-request-map.js';
|
|
26
|
+
import { OutputHelper } from '@quenty/cli-output-helpers';
|
|
27
|
+
|
|
28
|
+
export interface BridgeHostOptions {
|
|
29
|
+
/** Port to bind on. Default: 38741. Use 0 for ephemeral (test-friendly). */
|
|
30
|
+
port?: number;
|
|
31
|
+
/** Host to bind on. Default: 'localhost'. */
|
|
32
|
+
host?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface PluginSessionInfo {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
pluginVersion?: string;
|
|
38
|
+
capabilities: Capability[];
|
|
39
|
+
/** Instance ID from register. */
|
|
40
|
+
instanceId?: string;
|
|
41
|
+
/** Place name from register. */
|
|
42
|
+
placeName?: string;
|
|
43
|
+
/** Studio state from register. */
|
|
44
|
+
state?: string;
|
|
45
|
+
/** Place file from register. */
|
|
46
|
+
placeFile?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const SHUTDOWN_TIMEOUT_MS = 2_000;
|
|
50
|
+
const SHUTDOWN_DRAIN_MS = 250;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Per-plugin connection state. The `pendingRequests` map correlates outgoing
|
|
54
|
+
* requests to incoming responses by `requestId`. A single message dispatcher
|
|
55
|
+
* is installed per connection and routes through this map; this avoids the
|
|
56
|
+
* older per-call `ws.on('message', ...)` pattern, which both leaked listeners
|
|
57
|
+
* and could match the wrong response when ≥2 requests were in flight.
|
|
58
|
+
*/
|
|
59
|
+
interface PluginConnectionState {
|
|
60
|
+
ws: WebSocket;
|
|
61
|
+
pendingRequests: PendingRequestMap<unknown>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class BridgeHost extends EventEmitter {
|
|
65
|
+
private _transport: TransportServer;
|
|
66
|
+
private _plugins: Map<string, PluginConnectionState> = new Map();
|
|
67
|
+
private _clients: Set<WebSocket> = new Set();
|
|
68
|
+
private _isRunning = false;
|
|
69
|
+
private _shuttingDown = false;
|
|
70
|
+
private _startTime = 0;
|
|
71
|
+
private _hostStartTime = 0;
|
|
72
|
+
private _lastFailoverAt: string | null = null;
|
|
73
|
+
|
|
74
|
+
constructor() {
|
|
75
|
+
super();
|
|
76
|
+
this._transport = new TransportServer();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Time (ms) since this process became the host. */
|
|
80
|
+
get hostUptime(): number {
|
|
81
|
+
if (this._hostStartTime === 0) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
return Date.now() - this._hostStartTime;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** ISO timestamp of the last failover event, or null if none. */
|
|
88
|
+
get lastFailoverAt(): string | null {
|
|
89
|
+
return this._lastFailoverAt;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
markFailover(): void {
|
|
93
|
+
this._hostStartTime = Date.now();
|
|
94
|
+
this._lastFailoverAt = new Date().toISOString();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Returns the actual bound port. */
|
|
98
|
+
async startAsync(options?: BridgeHostOptions): Promise<number> {
|
|
99
|
+
if (this._isRunning) {
|
|
100
|
+
throw new Error('BridgeHost is already running');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this._startTime = Date.now();
|
|
104
|
+
if (this._hostStartTime === 0) {
|
|
105
|
+
this._hostStartTime = this._startTime;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Register /plugin WebSocket handler — reject during shutdown
|
|
109
|
+
this._transport.onConnection('/plugin', (ws, request) => {
|
|
110
|
+
if (this._shuttingDown) {
|
|
111
|
+
ws.close(1001, 'host shutting down');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this._handlePluginConnection(ws, request);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Register /client WebSocket handler for CLI clients
|
|
118
|
+
this._transport.onConnection('/client', (ws) => {
|
|
119
|
+
this._clients.add(ws);
|
|
120
|
+
|
|
121
|
+
ws.on('message', (raw: RawData) => {
|
|
122
|
+
const data = typeof raw === 'string' ? raw : raw.toString('utf-8');
|
|
123
|
+
const msg = decodeHostMessage(data);
|
|
124
|
+
if (!msg) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
this.emit('client-message', msg, (reply: HostProtocolMessage) => {
|
|
128
|
+
ws.send(encodeHostMessage(reply));
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
ws.on('close', () => {
|
|
133
|
+
this._clients.delete(ws);
|
|
134
|
+
});
|
|
135
|
+
ws.on('error', () => {
|
|
136
|
+
// Errors are handled implicitly via close
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Register /health HTTP handler — returns 503 during shutdown so
|
|
141
|
+
// plugins don't rediscover a host that's about to close
|
|
142
|
+
this._transport.onHttpRequest('/health', (req, res) => {
|
|
143
|
+
if (this._shuttingDown) {
|
|
144
|
+
res.writeHead(503);
|
|
145
|
+
res.end();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
createHealthHandler(() => ({
|
|
149
|
+
port: this._transport.port,
|
|
150
|
+
sessions: this._plugins.size,
|
|
151
|
+
startTime: this._startTime,
|
|
152
|
+
hostStartTime: this._hostStartTime,
|
|
153
|
+
lastFailoverAt: this._lastFailoverAt,
|
|
154
|
+
}))(req, res);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const port = await this._transport.startAsync({
|
|
158
|
+
port: options?.port,
|
|
159
|
+
host: options?.host,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
this._isRunning = true;
|
|
163
|
+
return port;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async stopAsync(): Promise<void> {
|
|
167
|
+
if (!this._isRunning) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this._isRunning = false;
|
|
171
|
+
this._cancelAllPending('host stopped');
|
|
172
|
+
this._plugins.clear();
|
|
173
|
+
this._clients.clear();
|
|
174
|
+
await this._transport.stopAsync();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Graceful shutdown: broadcast HostTransferNotice to all connected clients,
|
|
179
|
+
* wait briefly for them to process it, then close all connections and
|
|
180
|
+
* release the port. Idempotent — calling multiple times is safe.
|
|
181
|
+
*
|
|
182
|
+
* Wrapped in a timeout: if the graceful sequence exceeds 2 seconds, the
|
|
183
|
+
* transport is force-closed to ensure the port is freed.
|
|
184
|
+
*/
|
|
185
|
+
async shutdownAsync(): Promise<void> {
|
|
186
|
+
if (this._shuttingDown) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this._shuttingDown = true;
|
|
190
|
+
OutputHelper.verbose(
|
|
191
|
+
`[host] shutdownAsync called (plugins: ${this._plugins.size}, clients: ${this._clients.size})`
|
|
192
|
+
);
|
|
193
|
+
OutputHelper.verbose(
|
|
194
|
+
`[host] shutdown stack: ${new Error().stack
|
|
195
|
+
?.split('\n')
|
|
196
|
+
.slice(1, 5)
|
|
197
|
+
.map((s) => s.trim())
|
|
198
|
+
.join(' <- ')}`
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
const gracefulShutdown = async () => {
|
|
202
|
+
// Step 1: Broadcast HostTransferNotice to all CLI clients
|
|
203
|
+
const notice: HostTransferNotice = { type: 'host-transfer' };
|
|
204
|
+
const encoded = encodeHostMessage(notice);
|
|
205
|
+
for (const ws of this._clients) {
|
|
206
|
+
try {
|
|
207
|
+
ws.send(encoded);
|
|
208
|
+
} catch {
|
|
209
|
+
// Client may already be disconnected
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Step 1.5: Send shutdown message to all plugins so they disconnect
|
|
214
|
+
// cleanly instead of seeing a WebSocket error
|
|
215
|
+
for (const [sessionId, state] of this._plugins) {
|
|
216
|
+
try {
|
|
217
|
+
state.ws.send(
|
|
218
|
+
encodeMessage({
|
|
219
|
+
type: 'shutdown',
|
|
220
|
+
sessionId,
|
|
221
|
+
payload: {} as Record<string, never>,
|
|
222
|
+
})
|
|
223
|
+
);
|
|
224
|
+
} catch {
|
|
225
|
+
// Plugin may already be disconnected
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Step 2: Wait briefly for plugins/clients to process
|
|
230
|
+
await new Promise<void>((resolve) =>
|
|
231
|
+
setTimeout(resolve, SHUTDOWN_DRAIN_MS)
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Step 3: Send close frames to all plugins and clients
|
|
235
|
+
for (const state of this._plugins.values()) {
|
|
236
|
+
try {
|
|
237
|
+
state.ws.close(1001, 'host shutting down');
|
|
238
|
+
} catch {
|
|
239
|
+
// Ignore
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
for (const ws of this._clients) {
|
|
243
|
+
try {
|
|
244
|
+
ws.close(1001, 'host shutting down');
|
|
245
|
+
} catch {
|
|
246
|
+
// Ignore
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Step 4: Stop the transport
|
|
251
|
+
this._isRunning = false;
|
|
252
|
+
this._cancelAllPending('host shutting down');
|
|
253
|
+
this._plugins.clear();
|
|
254
|
+
this._clients.clear();
|
|
255
|
+
await this._transport.stopAsync();
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// Wrap in timeout — force-close if graceful exceeds limit
|
|
259
|
+
const timeoutPromise = new Promise<'timeout'>((resolve) =>
|
|
260
|
+
setTimeout(() => resolve('timeout'), SHUTDOWN_TIMEOUT_MS)
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const result = await Promise.race([
|
|
264
|
+
gracefulShutdown().then(() => 'done' as const),
|
|
265
|
+
timeoutPromise,
|
|
266
|
+
]);
|
|
267
|
+
|
|
268
|
+
if (result === 'timeout') {
|
|
269
|
+
this._isRunning = false;
|
|
270
|
+
this._cancelAllPending('host force-closed');
|
|
271
|
+
this._plugins.clear();
|
|
272
|
+
this._clients.clear();
|
|
273
|
+
await this._transport.forceCloseAsync();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** The actual port the server is bound to. */
|
|
278
|
+
get port(): number {
|
|
279
|
+
return this._transport.port;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Whether the host is currently running. */
|
|
283
|
+
get isRunning(): boolean {
|
|
284
|
+
return this._isRunning;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Number of connected plugin sessions. */
|
|
288
|
+
get pluginCount(): number {
|
|
289
|
+
return this._plugins.size;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
broadcastToClients(msg: HostProtocolMessage): void {
|
|
293
|
+
const encoded = encodeHostMessage(msg);
|
|
294
|
+
for (const ws of this._clients) {
|
|
295
|
+
try {
|
|
296
|
+
ws.send(encoded);
|
|
297
|
+
} catch {
|
|
298
|
+
// Client may already be disconnected
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async sendToPluginAsync<TResponse>(
|
|
304
|
+
sessionId: string,
|
|
305
|
+
message: ServerMessage,
|
|
306
|
+
timeoutMs: number
|
|
307
|
+
): Promise<TResponse> {
|
|
308
|
+
const state = this._plugins.get(sessionId);
|
|
309
|
+
if (!state) {
|
|
310
|
+
throw new Error(`Plugin session '${sessionId}' not connected`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Every request must carry a requestId so the response can be correlated.
|
|
314
|
+
// All real callers (BridgeSession, BridgeClient) generate one; the fallback
|
|
315
|
+
// here is purely defensive.
|
|
316
|
+
const messageWithId = ensureRequestId(message);
|
|
317
|
+
const requestId = (messageWithId as { requestId: string }).requestId;
|
|
318
|
+
const msgType = messageWithId.type;
|
|
319
|
+
|
|
320
|
+
OutputHelper.verbose(
|
|
321
|
+
`[host] → plugin ${sessionId}: ${msgType} (requestId=${requestId.slice(
|
|
322
|
+
0,
|
|
323
|
+
8
|
|
324
|
+
)}, timeout ${timeoutMs}ms)`
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const responsePromise = state.pendingRequests.addRequestAsync(
|
|
328
|
+
requestId,
|
|
329
|
+
timeoutMs
|
|
330
|
+
) as Promise<TResponse>;
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
state.ws.send(encodeMessage(messageWithId));
|
|
334
|
+
} catch (err) {
|
|
335
|
+
state.pendingRequests.rejectRequest(
|
|
336
|
+
requestId,
|
|
337
|
+
err instanceof Error ? err : new Error(String(err))
|
|
338
|
+
);
|
|
339
|
+
throw err;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return responsePromise;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Send a fire-and-forget message to a specific plugin.
|
|
347
|
+
*/
|
|
348
|
+
sendToPlugin(sessionId: string, message: ServerMessage): void {
|
|
349
|
+
const state = this._plugins.get(sessionId);
|
|
350
|
+
if (!state) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
state.ws.send(encodeMessage(message));
|
|
355
|
+
} catch {
|
|
356
|
+
// Plugin may already be disconnected
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private _handlePluginConnection(
|
|
361
|
+
ws: WebSocket,
|
|
362
|
+
_request: IncomingMessage
|
|
363
|
+
): void {
|
|
364
|
+
const onMessage = (raw: RawData) => {
|
|
365
|
+
const data = typeof raw === 'string' ? raw : raw.toString('utf-8');
|
|
366
|
+
const msg = decodePluginMessage(data);
|
|
367
|
+
if (!msg || msg.type !== 'register') {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const sessionId = msg.sessionId;
|
|
372
|
+
const { pluginVersion, capabilities } = msg.payload;
|
|
373
|
+
|
|
374
|
+
ws.off('message', onMessage);
|
|
375
|
+
this._registerPlugin(ws, {
|
|
376
|
+
sessionId,
|
|
377
|
+
pluginVersion,
|
|
378
|
+
capabilities,
|
|
379
|
+
instanceId: msg.payload.instanceId,
|
|
380
|
+
placeName: msg.payload.placeName,
|
|
381
|
+
state: msg.payload.state,
|
|
382
|
+
placeFile: msg.payload.placeFile,
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
ws.on('message', onMessage);
|
|
387
|
+
|
|
388
|
+
ws.on('error', () => {
|
|
389
|
+
// Errors are handled implicitly via close
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
private _registerPlugin(ws: WebSocket, info: PluginSessionInfo): void {
|
|
394
|
+
// If a plugin with this sessionId is already connected, close the old
|
|
395
|
+
// connection first so the Map stays consistent. This can happen when a
|
|
396
|
+
// plugin reconnects faster than the host detects the old socket's close.
|
|
397
|
+
const existing = this._plugins.get(info.sessionId);
|
|
398
|
+
if (existing && existing.ws !== ws) {
|
|
399
|
+
try {
|
|
400
|
+
existing.ws.close(1001, 'replaced by new connection');
|
|
401
|
+
} catch {
|
|
402
|
+
// Old socket may already be dead
|
|
403
|
+
}
|
|
404
|
+
existing.pendingRequests.cancelAll(
|
|
405
|
+
'plugin connection replaced by new session'
|
|
406
|
+
);
|
|
407
|
+
// Remove immediately so the old close handler doesn't delete the new entry
|
|
408
|
+
this._plugins.delete(info.sessionId);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const state: PluginConnectionState = {
|
|
412
|
+
ws,
|
|
413
|
+
pendingRequests: new PendingRequestMap<unknown>(),
|
|
414
|
+
};
|
|
415
|
+
this._plugins.set(info.sessionId, state);
|
|
416
|
+
|
|
417
|
+
// Install the persistent dispatcher on this connection. All plugin
|
|
418
|
+
// responses arrive through this listener; matched by requestId via the
|
|
419
|
+
// pending-request map.
|
|
420
|
+
ws.on('message', (raw: RawData) =>
|
|
421
|
+
this._dispatchPluginMessage(info.sessionId, state, raw)
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
OutputHelper.verbose(
|
|
425
|
+
`[host] Plugin connected: ${
|
|
426
|
+
info.sessionId
|
|
427
|
+
} (caps: ${info.capabilities.join(', ')})`
|
|
428
|
+
);
|
|
429
|
+
this.emit('plugin-connected', info);
|
|
430
|
+
|
|
431
|
+
ws.on('close', () => {
|
|
432
|
+
// Only remove if this socket is still the registered one — a newer
|
|
433
|
+
// connection with the same sessionId may have already replaced us.
|
|
434
|
+
const current = this._plugins.get(info.sessionId);
|
|
435
|
+
if (current && current.ws === ws) {
|
|
436
|
+
current.pendingRequests.cancelAll('plugin disconnected');
|
|
437
|
+
this._plugins.delete(info.sessionId);
|
|
438
|
+
OutputHelper.verbose(`[host] Plugin disconnected: ${info.sessionId}`);
|
|
439
|
+
this.emit('plugin-disconnected', info.sessionId);
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Persistent per-connection message dispatcher. Routes responses to the
|
|
446
|
+
* pending-request map by `requestId`. Heartbeats and unsolicited messages
|
|
447
|
+
* are logged but otherwise ignored — there are no host-side consumers of
|
|
448
|
+
* push messages today.
|
|
449
|
+
*/
|
|
450
|
+
private _dispatchPluginMessage(
|
|
451
|
+
sessionId: string,
|
|
452
|
+
state: PluginConnectionState,
|
|
453
|
+
raw: RawData
|
|
454
|
+
): void {
|
|
455
|
+
const data = typeof raw === 'string' ? raw : raw.toString('utf-8');
|
|
456
|
+
|
|
457
|
+
// Parse the raw JSON directly — decodePluginMessage's strict schema may
|
|
458
|
+
// reject valid responses we still want to surface to callers.
|
|
459
|
+
let parsed: { type?: unknown; requestId?: unknown; payload?: unknown };
|
|
460
|
+
try {
|
|
461
|
+
parsed = JSON.parse(data);
|
|
462
|
+
} catch {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (parsed.type === 'heartbeat') {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const requestId =
|
|
471
|
+
typeof parsed.requestId === 'string' && parsed.requestId !== ''
|
|
472
|
+
? parsed.requestId
|
|
473
|
+
: undefined;
|
|
474
|
+
|
|
475
|
+
if (requestId !== undefined) {
|
|
476
|
+
if (state.pendingRequests.hasPendingRequest(requestId)) {
|
|
477
|
+
OutputHelper.verbose(
|
|
478
|
+
`[host] ← plugin ${sessionId}: ${String(
|
|
479
|
+
parsed.type
|
|
480
|
+
)} (matched requestId=${requestId.slice(0, 8)})`
|
|
481
|
+
);
|
|
482
|
+
state.pendingRequests.resolveRequest(requestId, parsed);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
OutputHelper.verbose(
|
|
486
|
+
`[host] ← plugin ${sessionId}: dropped ${String(
|
|
487
|
+
parsed.type
|
|
488
|
+
)} for unknown requestId=${requestId.slice(0, 8)}`
|
|
489
|
+
);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// No requestId: nothing to correlate against. Errors without a requestId
|
|
494
|
+
// mean the plugin couldn't even parse the inbound request, so there's no
|
|
495
|
+
// pending entry to fail. Surface as a log so failures aren't silent.
|
|
496
|
+
if (parsed.type === 'error') {
|
|
497
|
+
const payload = parsed.payload as
|
|
498
|
+
| { code?: string; message?: string }
|
|
499
|
+
| undefined;
|
|
500
|
+
OutputHelper.warn(
|
|
501
|
+
`[host] plugin ${sessionId} sent error without requestId (${
|
|
502
|
+
payload?.code ?? 'unknown'
|
|
503
|
+
}): ${payload?.message ?? ''}`
|
|
504
|
+
);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
OutputHelper.verbose(
|
|
509
|
+
`[host] ← plugin ${sessionId}: dropped unsolicited ${String(parsed.type)}`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private _cancelAllPending(reason: string): void {
|
|
514
|
+
for (const state of this._plugins.values()) {
|
|
515
|
+
state.pendingRequests.cancelAll(reason);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Ensure the outgoing message carries a `requestId`. All real callers
|
|
522
|
+
* (BridgeSession, BridgeClient) supply one; this is a defensive fallback
|
|
523
|
+
* so the strict-correlation path always has a key to match against.
|
|
524
|
+
*/
|
|
525
|
+
function ensureRequestId(message: ServerMessage): ServerMessage {
|
|
526
|
+
const existing = (message as { requestId?: string }).requestId;
|
|
527
|
+
if (typeof existing === 'string' && existing !== '') {
|
|
528
|
+
return message;
|
|
529
|
+
}
|
|
530
|
+
return { ...message, requestId: randomUUID() } as ServerMessage;
|
|
531
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for environment-detection -- validates role detection logic
|
|
3
|
+
* including host election on free port, client detection when host exists,
|
|
4
|
+
* remoteHost override, and devcontainer detection.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { describe, it, expect, afterEach, beforeEach } from 'vitest';
|
|
9
|
+
import {
|
|
10
|
+
detectRoleAsync,
|
|
11
|
+
isDevcontainer,
|
|
12
|
+
getDefaultRemoteHost,
|
|
13
|
+
} from './environment-detection.js';
|
|
14
|
+
import { BridgeHost } from './bridge-host.js';
|
|
15
|
+
|
|
16
|
+
describe('detectRoleAsync', () => {
|
|
17
|
+
let bridgeHost: BridgeHost | undefined;
|
|
18
|
+
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
if (bridgeHost) {
|
|
21
|
+
await bridgeHost.stopAsync();
|
|
22
|
+
bridgeHost = undefined;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('returns host role on a free ephemeral port', async () => {
|
|
27
|
+
const result = await detectRoleAsync({ port: 0 });
|
|
28
|
+
|
|
29
|
+
expect(result.role).toBe('host');
|
|
30
|
+
expect(result.port).toBeGreaterThan(0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns client role when remoteHost is specified', async () => {
|
|
34
|
+
const result = await detectRoleAsync({
|
|
35
|
+
port: 38741,
|
|
36
|
+
remoteHost: 'some-remote-host:38741',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(result.role).toBe('client');
|
|
40
|
+
expect(result.port).toBe(38741);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns client role when a bridge host is already running', async () => {
|
|
44
|
+
// Start a real bridge host on an ephemeral port
|
|
45
|
+
bridgeHost = new BridgeHost();
|
|
46
|
+
const port = await bridgeHost.startAsync({ port: 0 });
|
|
47
|
+
|
|
48
|
+
const result = await detectRoleAsync({ port });
|
|
49
|
+
|
|
50
|
+
expect(result.role).toBe('client');
|
|
51
|
+
expect(result.port).toBe(port);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('preserves the port from options in the result', async () => {
|
|
55
|
+
const result = await detectRoleAsync({
|
|
56
|
+
port: 12345,
|
|
57
|
+
remoteHost: 'localhost:12345',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(result.port).toBe(12345);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('returns host role with the bound port when port is 0', async () => {
|
|
64
|
+
const result = await detectRoleAsync({ port: 0 });
|
|
65
|
+
|
|
66
|
+
// Ephemeral port should be assigned by the OS
|
|
67
|
+
expect(result.role).toBe('host');
|
|
68
|
+
expect(result.port).not.toBe(0);
|
|
69
|
+
expect(result.port).toBeGreaterThan(0);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const DEVCONTAINER_ENV_KEYS = [
|
|
74
|
+
'REMOTE_CONTAINERS',
|
|
75
|
+
'CODESPACES',
|
|
76
|
+
'CONTAINER',
|
|
77
|
+
] as const;
|
|
78
|
+
const dockerenvExists = existsSync('/.dockerenv');
|
|
79
|
+
|
|
80
|
+
describe('isDevcontainer', () => {
|
|
81
|
+
const savedEnv: Record<string, string | undefined> = {};
|
|
82
|
+
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
// Save current values so we can restore after each test
|
|
85
|
+
for (const key of DEVCONTAINER_ENV_KEYS) {
|
|
86
|
+
savedEnv[key] = process.env[key];
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
afterEach(() => {
|
|
91
|
+
// Restore original env values
|
|
92
|
+
for (const key of DEVCONTAINER_ENV_KEYS) {
|
|
93
|
+
if (savedEnv[key] === undefined) {
|
|
94
|
+
delete process.env[key];
|
|
95
|
+
} else {
|
|
96
|
+
process.env[key] = savedEnv[key];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
function clearDevcontainerEnv(): void {
|
|
102
|
+
for (const key of DEVCONTAINER_ENV_KEYS) {
|
|
103
|
+
delete process.env[key];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
it('returns true when REMOTE_CONTAINERS is set', () => {
|
|
108
|
+
clearDevcontainerEnv();
|
|
109
|
+
process.env.REMOTE_CONTAINERS = 'true';
|
|
110
|
+
expect(isDevcontainer()).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('returns true when CODESPACES is set', () => {
|
|
114
|
+
clearDevcontainerEnv();
|
|
115
|
+
process.env.CODESPACES = 'true';
|
|
116
|
+
expect(isDevcontainer()).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('returns true when CONTAINER is set', () => {
|
|
120
|
+
clearDevcontainerEnv();
|
|
121
|
+
process.env.CONTAINER = 'true';
|
|
122
|
+
expect(isDevcontainer()).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('returns false when no env vars set and no /.dockerenv', () => {
|
|
126
|
+
clearDevcontainerEnv();
|
|
127
|
+
// If /.dockerenv exists on this machine, the function should still return true
|
|
128
|
+
expect(isDevcontainer()).toBe(dockerenvExists);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('treats empty string as falsy', () => {
|
|
132
|
+
clearDevcontainerEnv();
|
|
133
|
+
process.env.REMOTE_CONTAINERS = '';
|
|
134
|
+
// Empty string is falsy -- result depends only on /.dockerenv
|
|
135
|
+
expect(isDevcontainer()).toBe(dockerenvExists);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('getDefaultRemoteHost', () => {
|
|
140
|
+
const savedEnv: Record<string, string | undefined> = {};
|
|
141
|
+
|
|
142
|
+
beforeEach(() => {
|
|
143
|
+
for (const key of DEVCONTAINER_ENV_KEYS) {
|
|
144
|
+
savedEnv[key] = process.env[key];
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
afterEach(() => {
|
|
149
|
+
for (const key of DEVCONTAINER_ENV_KEYS) {
|
|
150
|
+
if (savedEnv[key] === undefined) {
|
|
151
|
+
delete process.env[key];
|
|
152
|
+
} else {
|
|
153
|
+
process.env[key] = savedEnv[key];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
function clearDevcontainerEnv(): void {
|
|
159
|
+
for (const key of DEVCONTAINER_ENV_KEYS) {
|
|
160
|
+
delete process.env[key];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
it('returns localhost:38741 in devcontainer', () => {
|
|
165
|
+
clearDevcontainerEnv();
|
|
166
|
+
process.env.REMOTE_CONTAINERS = 'true';
|
|
167
|
+
expect(getDefaultRemoteHost()).toBe('localhost:38741');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('returns null outside devcontainer', () => {
|
|
171
|
+
clearDevcontainerEnv();
|
|
172
|
+
// Only null if /.dockerenv doesn't exist
|
|
173
|
+
if (!dockerenvExists) {
|
|
174
|
+
expect(getDefaultRemoteHost()).toBeNull();
|
|
175
|
+
} else {
|
|
176
|
+
// If /.dockerenv exists, we're in a container, so it returns the host
|
|
177
|
+
expect(getDefaultRemoteHost()).toBe('localhost:38741');
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|