@quenty/studio-bridge 0.21.0 → 0.22.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 +12 -0
- package/README.md +109 -17
- package/build-scripts/transform-add-session-attribute.lua +1 -0
- package/deploy.nevermore.json +10 -0
- package/dist/src/bridge/bridge-connection.d.ts +26 -2
- package/dist/src/bridge/bridge-connection.d.ts.map +1 -1
- package/dist/src/bridge/bridge-connection.js +159 -14
- package/dist/src/bridge/bridge-connection.js.map +1 -1
- package/dist/src/bridge/bridge-connection.test.js +81 -0
- package/dist/src/bridge/bridge-connection.test.js.map +1 -1
- package/dist/src/bridge/bridge-session.d.ts +35 -2
- package/dist/src/bridge/bridge-session.d.ts.map +1 -1
- package/dist/src/bridge/bridge-session.js +185 -28
- package/dist/src/bridge/bridge-session.js.map +1 -1
- package/dist/src/bridge/bridge-session.test.js +6 -4
- package/dist/src/bridge/bridge-session.test.js.map +1 -1
- package/dist/src/bridge/index.d.ts +1 -1
- package/dist/src/bridge/index.d.ts.map +1 -1
- package/dist/src/bridge/index.js.map +1 -1
- package/dist/src/bridge/internal/bridge-client.d.ts +4 -0
- package/dist/src/bridge/internal/bridge-client.d.ts.map +1 -1
- package/dist/src/bridge/internal/bridge-client.js +20 -7
- package/dist/src/bridge/internal/bridge-client.js.map +1 -1
- package/dist/src/bridge/internal/bridge-client.test.js +18 -0
- package/dist/src/bridge/internal/bridge-client.test.js.map +1 -1
- package/dist/src/bridge/internal/bridge-host.d.ts +13 -1
- package/dist/src/bridge/internal/bridge-host.d.ts.map +1 -1
- package/dist/src/bridge/internal/bridge-host.js +35 -0
- package/dist/src/bridge/internal/bridge-host.js.map +1 -1
- package/dist/src/bridge/internal/bridge-host.test.js +31 -0
- package/dist/src/bridge/internal/bridge-host.test.js.map +1 -1
- package/dist/src/bridge/internal/hand-off.d.ts.map +1 -1
- package/dist/src/bridge/internal/hand-off.js +4 -1
- package/dist/src/bridge/internal/hand-off.js.map +1 -1
- package/dist/src/bridge/internal/host-protocol.d.ts +8 -0
- package/dist/src/bridge/internal/host-protocol.d.ts.map +1 -1
- package/dist/src/bridge/internal/host-protocol.js +120 -13
- package/dist/src/bridge/internal/host-protocol.js.map +1 -1
- package/dist/src/bridge/internal/host-protocol.test.js +13 -0
- package/dist/src/bridge/internal/host-protocol.test.js.map +1 -1
- package/dist/src/bridge/internal/instance-state.d.ts +9 -0
- package/dist/src/bridge/internal/instance-state.d.ts.map +1 -0
- package/dist/src/bridge/internal/instance-state.js +28 -0
- package/dist/src/bridge/internal/instance-state.js.map +1 -0
- package/dist/src/bridge/internal/transport-client.d.ts.map +1 -1
- package/dist/src/bridge/internal/transport-client.js +2 -1
- package/dist/src/bridge/internal/transport-client.js.map +1 -1
- package/dist/src/bridge/internal/transport-server.d.ts.map +1 -1
- package/dist/src/bridge/internal/transport-server.js +5 -1
- package/dist/src/bridge/internal/transport-server.js.map +1 -1
- package/dist/src/bridge/types.d.ts +43 -4
- package/dist/src/bridge/types.d.ts.map +1 -1
- package/dist/src/bridge/types.js.map +1 -1
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.js +24 -0
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.js.map +1 -1
- package/dist/src/cli/adapters/cli-command-adapter.d.ts +6 -0
- package/dist/src/cli/adapters/cli-command-adapter.d.ts.map +1 -1
- package/dist/src/cli/adapters/cli-command-adapter.js +107 -11
- package/dist/src/cli/adapters/cli-command-adapter.js.map +1 -1
- package/dist/src/cli/adapters/group-builder.d.ts +3 -0
- package/dist/src/cli/adapters/group-builder.d.ts.map +1 -1
- package/dist/src/cli/adapters/group-builder.js +34 -15
- package/dist/src/cli/adapters/group-builder.js.map +1 -1
- package/dist/src/cli/adapters/group-builder.test.js +26 -2
- package/dist/src/cli/adapters/group-builder.test.js.map +1 -1
- package/dist/src/cli/adapters/help-formatter.d.ts.map +1 -1
- package/dist/src/cli/adapters/help-formatter.js +4 -3
- package/dist/src/cli/adapters/help-formatter.js.map +1 -1
- package/dist/src/cli/adapters/help-formatter.test.js +1 -1
- package/dist/src/cli/adapters/help-formatter.test.js.map +1 -1
- package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts +2 -0
- package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts.map +1 -0
- package/dist/src/cli/adapters/playtest-edit-guard.test.js +28 -0
- package/dist/src/cli/adapters/playtest-edit-guard.test.js.map +1 -0
- package/dist/src/cli/args/global-args.d.ts +1 -1
- package/dist/src/cli/args/global-args.d.ts.map +1 -1
- package/dist/src/cli/cli-output-context.d.ts +14 -0
- package/dist/src/cli/cli-output-context.d.ts.map +1 -0
- package/dist/src/cli/cli-output-context.js +39 -0
- package/dist/src/cli/cli-output-context.js.map +1 -0
- package/dist/src/cli/cli-output-context.test.d.ts +2 -0
- package/dist/src/cli/cli-output-context.test.d.ts.map +1 -0
- package/dist/src/cli/cli-output-context.test.js +38 -0
- package/dist/src/cli/cli-output-context.test.js.map +1 -0
- package/dist/src/cli/cli.js +40 -17
- package/dist/src/cli/cli.js.map +1 -1
- package/dist/src/cli/managed-studio-run.d.ts +21 -0
- package/dist/src/cli/managed-studio-run.d.ts.map +1 -0
- package/dist/src/cli/managed-studio-run.js +75 -0
- package/dist/src/cli/managed-studio-run.js.map +1 -0
- package/dist/src/cli/managed-studio-run.test.d.ts +2 -0
- package/dist/src/cli/managed-studio-run.test.d.ts.map +1 -0
- package/dist/src/cli/managed-studio-run.test.js +52 -0
- package/dist/src/cli/managed-studio-run.test.js.map +1 -0
- package/dist/src/cli/resolve-session.js +1 -1
- package/dist/src/cli/resolve-session.js.map +1 -1
- package/dist/src/cli/resolve-session.test.js +1 -1
- package/dist/src/cli/resolve-session.test.js.map +1 -1
- package/dist/src/cli/script-executor.d.ts +13 -1
- package/dist/src/cli/script-executor.d.ts.map +1 -1
- package/dist/src/cli/script-executor.js +77 -47
- package/dist/src/cli/script-executor.js.map +1 -1
- package/dist/src/commands/console/exec/exec.d.ts +17 -0
- package/dist/src/commands/console/exec/exec.d.ts.map +1 -1
- package/dist/src/commands/console/exec/exec.js +39 -7
- package/dist/src/commands/console/exec/exec.js.map +1 -1
- package/dist/src/commands/console/exec/exec.test.js +62 -3
- package/dist/src/commands/console/exec/exec.test.js.map +1 -1
- package/dist/src/commands/console/logs/logs.d.ts +30 -2
- package/dist/src/commands/console/logs/logs.d.ts.map +1 -1
- package/dist/src/commands/console/logs/logs.js +82 -7
- package/dist/src/commands/console/logs/logs.js.map +1 -1
- package/dist/src/commands/console/logs/logs.test.js +63 -4
- package/dist/src/commands/console/logs/logs.test.js.map +1 -1
- package/dist/src/commands/explorer/export/export.d.ts +19 -0
- package/dist/src/commands/explorer/export/export.d.ts.map +1 -0
- package/dist/src/commands/explorer/export/export.js +37 -0
- package/dist/src/commands/explorer/export/export.js.map +1 -0
- package/dist/src/commands/explorer/export/export.test.d.ts +2 -0
- package/dist/src/commands/explorer/export/export.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/export/export.test.js +25 -0
- package/dist/src/commands/explorer/export/export.test.js.map +1 -0
- package/dist/src/commands/explorer/find/find.d.ts +28 -0
- package/dist/src/commands/explorer/find/find.d.ts.map +1 -0
- package/dist/src/commands/explorer/find/find.js +75 -0
- package/dist/src/commands/explorer/find/find.js.map +1 -0
- package/dist/src/commands/explorer/find/find.test.d.ts +2 -0
- package/dist/src/commands/explorer/find/find.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/find/find.test.js +44 -0
- package/dist/src/commands/explorer/find/find.test.js.map +1 -0
- package/dist/src/commands/explorer/grep/grep.d.ts +24 -0
- package/dist/src/commands/explorer/grep/grep.d.ts.map +1 -0
- package/dist/src/commands/explorer/grep/grep.js +63 -0
- package/dist/src/commands/explorer/grep/grep.js.map +1 -0
- package/dist/src/commands/explorer/grep/grep.test.d.ts +2 -0
- package/dist/src/commands/explorer/grep/grep.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/grep/grep.test.js +42 -0
- package/dist/src/commands/explorer/grep/grep.test.js.map +1 -0
- package/dist/src/commands/explorer/import/import.d.ts +21 -0
- package/dist/src/commands/explorer/import/import.d.ts.map +1 -0
- package/dist/src/commands/explorer/import/import.js +37 -0
- package/dist/src/commands/explorer/import/import.js.map +1 -0
- package/dist/src/commands/explorer/import/import.test.d.ts +2 -0
- package/dist/src/commands/explorer/import/import.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/import/import.test.js +45 -0
- package/dist/src/commands/explorer/import/import.test.js.map +1 -0
- package/dist/src/commands/formatters.test.js +7 -1
- package/dist/src/commands/formatters.test.js.map +1 -1
- package/dist/src/commands/framework/action-loader.d.ts +5 -1
- package/dist/src/commands/framework/action-loader.d.ts.map +1 -1
- package/dist/src/commands/framework/action-loader.js +9 -2
- package/dist/src/commands/framework/action-loader.js.map +1 -1
- package/dist/src/commands/framework/action-loader.test.js +23 -0
- package/dist/src/commands/framework/action-loader.test.js.map +1 -1
- package/dist/src/commands/framework/arg-builder.d.ts +15 -2
- package/dist/src/commands/framework/arg-builder.d.ts.map +1 -1
- package/dist/src/commands/framework/arg-builder.js +22 -2
- package/dist/src/commands/framework/arg-builder.js.map +1 -1
- package/dist/src/commands/framework/define-command.d.ts +19 -3
- package/dist/src/commands/framework/define-command.d.ts.map +1 -1
- package/dist/src/commands/framework/define-command.js.map +1 -1
- package/dist/src/commands/framework/index.d.ts +1 -1
- package/dist/src/commands/framework/index.d.ts.map +1 -1
- package/dist/src/commands/framework/index.js.map +1 -1
- package/dist/src/commands/index.d.ts +11 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +11 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/linux/inject-credentials/inject-credentials.d.ts.map +1 -1
- package/dist/src/commands/linux/inject-credentials/inject-credentials.js +1 -3
- package/dist/src/commands/linux/inject-credentials/inject-credentials.js.map +1 -1
- package/dist/src/commands/linux/setup/setup.d.ts.map +1 -1
- package/dist/src/commands/linux/setup/setup.js +1 -3
- package/dist/src/commands/linux/setup/setup.js.map +1 -1
- package/dist/src/commands/linux/status/status.d.ts +9 -1
- package/dist/src/commands/linux/status/status.d.ts.map +1 -1
- package/dist/src/commands/linux/status/status.js +66 -86
- package/dist/src/commands/linux/status/status.js.map +1 -1
- package/dist/src/commands/playtest/player/add/add.d.ts +17 -0
- package/dist/src/commands/playtest/player/add/add.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/add/add.js +45 -0
- package/dist/src/commands/playtest/player/add/add.js.map +1 -0
- package/dist/src/commands/playtest/player/list/list.d.ts +13 -0
- package/dist/src/commands/playtest/player/list/list.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/list/list.js +33 -0
- package/dist/src/commands/playtest/player/list/list.js.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.d.ts +36 -0
- package/dist/src/commands/playtest/player/playtest-players.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.js +187 -0
- package/dist/src/commands/playtest/player/playtest-players.js.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.test.d.ts +2 -0
- package/dist/src/commands/playtest/player/playtest-players.test.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.test.js +89 -0
- package/dist/src/commands/playtest/player/playtest-players.test.js.map +1 -0
- package/dist/src/commands/playtest/player/remove/remove.d.ts +17 -0
- package/dist/src/commands/playtest/player/remove/remove.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/remove/remove.js +42 -0
- package/dist/src/commands/playtest/player/remove/remove.js.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.d.ts +49 -0
- package/dist/src/commands/playtest/playtest-sessions.d.ts.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.js +283 -0
- package/dist/src/commands/playtest/playtest-sessions.js.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.test.d.ts +2 -0
- package/dist/src/commands/playtest/playtest-sessions.test.d.ts.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.test.js +87 -0
- package/dist/src/commands/playtest/playtest-sessions.test.js.map +1 -0
- package/dist/src/commands/playtest/restart/restart.d.ts +23 -0
- package/dist/src/commands/playtest/restart/restart.d.ts.map +1 -0
- package/dist/src/commands/playtest/restart/restart.js +105 -0
- package/dist/src/commands/playtest/restart/restart.js.map +1 -0
- package/dist/src/commands/playtest/run/run.d.ts +47 -0
- package/dist/src/commands/playtest/run/run.d.ts.map +1 -0
- package/dist/src/commands/playtest/run/run.js +137 -0
- package/dist/src/commands/playtest/run/run.js.map +1 -0
- package/dist/src/commands/playtest/start/start.d.ts +42 -0
- package/dist/src/commands/playtest/start/start.d.ts.map +1 -0
- package/dist/src/commands/playtest/start/start.js +70 -0
- package/dist/src/commands/playtest/start/start.js.map +1 -0
- package/dist/src/commands/playtest/stop/stop.d.ts +15 -0
- package/dist/src/commands/playtest/stop/stop.d.ts.map +1 -0
- package/dist/src/commands/playtest/stop/stop.js +24 -0
- package/dist/src/commands/playtest/stop/stop.js.map +1 -0
- package/dist/src/commands/process/close/close.d.ts +19 -10
- package/dist/src/commands/process/close/close.d.ts.map +1 -1
- package/dist/src/commands/process/close/close.js +118 -15
- package/dist/src/commands/process/close/close.js.map +1 -1
- package/dist/src/commands/process/close/close.test.d.ts +2 -0
- package/dist/src/commands/process/close/close.test.d.ts.map +1 -0
- package/dist/src/commands/process/close/close.test.js +190 -0
- package/dist/src/commands/process/close/close.test.js.map +1 -0
- package/dist/src/commands/process/info/info.d.ts +45 -5
- package/dist/src/commands/process/info/info.d.ts.map +1 -1
- package/dist/src/commands/process/info/info.js +117 -13
- package/dist/src/commands/process/info/info.js.map +1 -1
- package/dist/src/commands/process/info/info.test.js +140 -38
- package/dist/src/commands/process/info/info.test.js.map +1 -1
- package/dist/src/commands/process/launch/launch.d.ts +11 -1
- package/dist/src/commands/process/launch/launch.d.ts.map +1 -1
- package/dist/src/commands/process/launch/launch.js +37 -7
- package/dist/src/commands/process/launch/launch.js.map +1 -1
- package/dist/src/commands/process/launch/launch.test.js +99 -20
- package/dist/src/commands/process/launch/launch.test.js.map +1 -1
- package/dist/src/commands/process/list/list.d.ts +2 -1
- package/dist/src/commands/process/list/list.d.ts.map +1 -1
- package/dist/src/commands/process/list/list.js +9 -6
- package/dist/src/commands/process/list/list.js.map +1 -1
- package/dist/src/commands/process/list/list.test.js +1 -0
- package/dist/src/commands/process/list/list.test.js.map +1 -1
- package/dist/src/commands/process/run/run.d.ts +9 -0
- package/dist/src/commands/process/run/run.d.ts.map +1 -1
- package/dist/src/commands/process/run/run.js +15 -5
- package/dist/src/commands/process/run/run.js.map +1 -1
- package/dist/src/commands/serve/serve.d.ts +2 -0
- package/dist/src/commands/serve/serve.d.ts.map +1 -1
- package/dist/src/commands/serve/serve.js +17 -4
- package/dist/src/commands/serve/serve.js.map +1 -1
- package/dist/src/commands/serve/serve.test.js +9 -2
- package/dist/src/commands/serve/serve.test.js.map +1 -1
- package/dist/src/commands/viewport/screenshot/screenshot.d.ts.map +1 -1
- package/dist/src/commands/viewport/screenshot/screenshot.js +4 -1
- package/dist/src/commands/viewport/screenshot/screenshot.js.map +1 -1
- package/dist/src/commands/viewport/screenshot/screenshot.test.js +5 -9
- package/dist/src/commands/viewport/screenshot/screenshot.test.js.map +1 -1
- package/dist/src/deploy/deploy-places.d.ts +39 -0
- package/dist/src/deploy/deploy-places.d.ts.map +1 -0
- package/dist/src/deploy/deploy-places.js +162 -0
- package/dist/src/deploy/deploy-places.js.map +1 -0
- package/dist/src/deploy/deploy-places.test.d.ts +2 -0
- package/dist/src/deploy/deploy-places.test.d.ts.map +1 -0
- package/dist/src/deploy/deploy-places.test.js +96 -0
- package/dist/src/deploy/deploy-places.test.js.map +1 -0
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/linux/linux-studio-launcher.d.ts.map +1 -1
- package/dist/src/linux/linux-studio-launcher.js +10 -0
- package/dist/src/linux/linux-studio-launcher.js.map +1 -1
- package/dist/src/linux/linux-studio-launcher.test.d.ts +2 -0
- package/dist/src/linux/linux-studio-launcher.test.d.ts.map +1 -0
- package/dist/src/linux/linux-studio-launcher.test.js +68 -0
- package/dist/src/linux/linux-studio-launcher.test.js.map +1 -0
- package/dist/src/plugin/persistent-plugin-installer.d.ts.map +1 -1
- package/dist/src/plugin/persistent-plugin-installer.js +2 -1
- package/dist/src/plugin/persistent-plugin-installer.js.map +1 -1
- package/dist/src/plugin/persistent-plugin-installer.test.js +3 -0
- package/dist/src/plugin/persistent-plugin-installer.test.js.map +1 -1
- package/dist/src/plugin/plugin-build-version.d.ts +7 -0
- package/dist/src/plugin/plugin-build-version.d.ts.map +1 -0
- package/dist/src/plugin/plugin-build-version.js +42 -0
- package/dist/src/plugin/plugin-build-version.js.map +1 -0
- package/dist/src/plugin/plugin-build-version.test.d.ts +2 -0
- package/dist/src/plugin/plugin-build-version.test.d.ts.map +1 -0
- package/dist/src/plugin/plugin-build-version.test.js +41 -0
- package/dist/src/plugin/plugin-build-version.test.js.map +1 -0
- package/dist/src/plugin/plugin-injector.d.ts.map +1 -1
- package/dist/src/plugin/plugin-injector.js +7 -1
- package/dist/src/plugin/plugin-injector.js.map +1 -1
- package/dist/src/process/studio-process-closer.d.ts +35 -0
- package/dist/src/process/studio-process-closer.d.ts.map +1 -0
- package/dist/src/process/studio-process-closer.js +129 -0
- package/dist/src/process/studio-process-closer.js.map +1 -0
- package/dist/src/process/studio-process-closer.test.d.ts +2 -0
- package/dist/src/process/studio-process-closer.test.d.ts.map +1 -0
- package/dist/src/process/studio-process-closer.test.js +80 -0
- package/dist/src/process/studio-process-closer.test.js.map +1 -0
- package/dist/src/process/studio-settings.d.ts +14 -0
- package/dist/src/process/studio-settings.d.ts.map +1 -0
- package/dist/src/process/studio-settings.js +50 -0
- package/dist/src/process/studio-settings.js.map +1 -0
- package/dist/src/process/studio-settings.test.d.ts +2 -0
- package/dist/src/process/studio-settings.test.d.ts.map +1 -0
- package/dist/src/process/studio-settings.test.js +16 -0
- package/dist/src/process/studio-settings.test.js.map +1 -0
- package/dist/src/process/studio-window-state.d.ts +13 -0
- package/dist/src/process/studio-window-state.d.ts.map +1 -0
- package/dist/src/process/studio-window-state.js +48 -0
- package/dist/src/process/studio-window-state.js.map +1 -0
- package/dist/src/server/studio-bridge-server.d.ts +3 -1
- package/dist/src/server/studio-bridge-server.d.ts.map +1 -1
- package/dist/src/server/studio-bridge-server.js +12 -4
- package/dist/src/server/studio-bridge-server.js.map +1 -1
- package/dist/src/server/web-socket-protocol.d.ts +173 -6
- package/dist/src/server/web-socket-protocol.d.ts.map +1 -1
- package/dist/src/server/web-socket-protocol.js +244 -1
- package/dist/src/server/web-socket-protocol.js.map +1 -1
- package/dist/src/server/web-socket-protocol.test.js +48 -0
- package/dist/src/server/web-socket-protocol.test.js.map +1 -1
- package/dist/src/test/e2e/persistent-session.test.js +121 -0
- package/dist/src/test/e2e/persistent-session.test.js.map +1 -1
- package/dist/src/test/e2e/split-server.test.d.ts +3 -5
- package/dist/src/test/e2e/split-server.test.d.ts.map +1 -1
- package/dist/src/test/e2e/split-server.test.js +3 -5
- package/dist/src/test/e2e/split-server.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docker/Dockerfile +3 -0
- package/package.json +8 -6
- package/src/bridge/bridge-connection.test.ts +126 -0
- package/src/bridge/bridge-connection.ts +217 -22
- package/src/bridge/bridge-session.test.ts +6 -4
- package/src/bridge/bridge-session.ts +273 -28
- package/src/bridge/index.ts +3 -0
- package/src/bridge/internal/bridge-client.test.ts +23 -0
- package/src/bridge/internal/bridge-client.ts +25 -7
- package/src/bridge/internal/bridge-host.test.ts +38 -0
- package/src/bridge/internal/bridge-host.ts +47 -0
- package/src/bridge/internal/hand-off.ts +4 -1
- package/src/bridge/internal/host-protocol.test.ts +15 -0
- package/src/bridge/internal/host-protocol.ts +148 -37
- package/src/bridge/internal/instance-state.ts +34 -0
- package/src/bridge/internal/transport-client.ts +2 -1
- package/src/bridge/internal/transport-server.ts +5 -1
- package/src/bridge/types.ts +47 -2
- package/src/cli/adapters/cli-command-adapter-handler.test.ts +28 -0
- package/src/cli/adapters/cli-command-adapter.ts +150 -18
- package/src/cli/adapters/group-builder.test.ts +34 -2
- package/src/cli/adapters/group-builder.ts +52 -20
- package/src/cli/adapters/help-formatter.test.ts +1 -1
- package/src/cli/adapters/help-formatter.ts +4 -3
- package/src/cli/adapters/playtest-edit-guard.test.ts +54 -0
- package/src/cli/args/global-args.ts +1 -1
- package/src/cli/cli-output-context.test.ts +55 -0
- package/src/cli/cli-output-context.ts +50 -0
- package/src/cli/cli.ts +49 -17
- package/src/cli/managed-studio-run.test.ts +71 -0
- package/src/cli/managed-studio-run.ts +135 -0
- package/src/cli/resolve-session.test.ts +1 -1
- package/src/cli/resolve-session.ts +1 -1
- package/src/cli/script-executor.ts +99 -51
- package/src/commands/console/exec/exec.test.ts +96 -3
- package/src/commands/console/exec/exec.ts +59 -6
- package/src/commands/console/exec/execute.lua +356 -240
- package/src/commands/console/exec/execute.spec.lua +286 -0
- package/src/commands/console/logs/logs.test.ts +76 -4
- package/src/commands/console/logs/logs.ts +132 -17
- package/src/commands/console/logs/query-logs.lua +85 -52
- package/src/commands/console/logs/query-logs.spec.lua +132 -0
- package/src/commands/explorer/export/export-instances.lua +108 -0
- package/src/commands/explorer/export/export-instances.spec.lua +87 -0
- package/src/commands/explorer/export/export.test.ts +27 -0
- package/src/commands/explorer/export/export.ts +60 -0
- package/src/commands/explorer/find/find-instances.lua +174 -0
- package/src/commands/explorer/find/find-instances.spec.lua +122 -0
- package/src/commands/explorer/find/find.test.ts +53 -0
- package/src/commands/explorer/find/find.ts +113 -0
- package/src/commands/explorer/grep/grep-scripts.lua +214 -0
- package/src/commands/explorer/grep/grep-scripts.spec.lua +130 -0
- package/src/commands/explorer/grep/grep.test.ts +45 -0
- package/src/commands/explorer/grep/grep.ts +93 -0
- package/src/commands/explorer/import/import-instances.lua +106 -0
- package/src/commands/explorer/import/import-instances.spec.lua +84 -0
- package/src/commands/explorer/import/import.test.ts +60 -0
- package/src/commands/explorer/import/import.ts +66 -0
- package/src/commands/explorer/query/query-data-model.lua +124 -152
- package/src/commands/explorer/query/query-data-model.spec.lua +150 -0
- package/src/commands/formatters.test.ts +7 -1
- package/src/commands/framework/action-loader.test.ts +28 -0
- package/src/commands/framework/action-loader.ts +10 -2
- package/src/commands/framework/arg-builder.ts +26 -3
- package/src/commands/framework/define-command.ts +32 -3
- package/src/commands/framework/index.ts +1 -0
- package/src/commands/index.ts +50 -0
- package/src/commands/linux/inject-credentials/inject-credentials.ts +1 -3
- package/src/commands/linux/setup/setup.ts +1 -3
- package/src/commands/linux/status/status.ts +105 -98
- package/src/commands/playtest/player/add/add.ts +85 -0
- package/src/commands/playtest/player/list/list.ts +57 -0
- package/src/commands/playtest/player/playtest-players.test.ts +146 -0
- package/src/commands/playtest/player/playtest-players.ts +270 -0
- package/src/commands/playtest/player/remove/remove.ts +78 -0
- package/src/commands/playtest/playtest-sessions.test.ts +143 -0
- package/src/commands/playtest/playtest-sessions.ts +417 -0
- package/src/commands/playtest/restart/restart.ts +199 -0
- package/src/commands/playtest/run/playtest-runner.luau +264 -0
- package/src/commands/playtest/run/playtest-runner.spec.lua +71 -0
- package/src/commands/playtest/run/run-playtest-script.lua +139 -0
- package/src/commands/playtest/run/run-playtest-script.spec.lua +43 -0
- package/src/commands/playtest/run/run.ts +239 -0
- package/src/commands/playtest/start/start-playtest.lua +108 -0
- package/src/commands/playtest/start/start.ts +138 -0
- package/src/commands/playtest/stop/end-playtest.lua +47 -0
- package/src/commands/playtest/stop/stop.ts +51 -0
- package/src/commands/process/close/close.test.ts +250 -0
- package/src/commands/process/close/close.ts +183 -17
- package/src/commands/process/info/info.test.ts +198 -44
- package/src/commands/process/info/info.ts +204 -15
- package/src/commands/process/info/query-state.lua +42 -29
- package/src/commands/process/info/query-state.spec.lua +39 -0
- package/src/commands/process/launch/launch.test.ts +138 -21
- package/src/commands/process/launch/launch.ts +69 -9
- package/src/commands/process/list/list.test.ts +1 -0
- package/src/commands/process/list/list.ts +14 -6
- package/src/commands/process/run/run.ts +26 -5
- package/src/commands/serve/serve.test.ts +17 -2
- package/src/commands/serve/serve.ts +25 -6
- package/src/commands/viewport/screenshot/capture-screenshot.lua +175 -182
- package/src/commands/viewport/screenshot/capture-screenshot.spec.lua +129 -0
- package/src/commands/viewport/screenshot/screenshot.test.ts +7 -10
- package/src/commands/viewport/screenshot/screenshot.ts +6 -1
- package/src/deploy/deploy-places.test.ts +127 -0
- package/src/deploy/deploy-places.ts +229 -0
- package/src/index.ts +11 -0
- package/src/linux/linux-studio-launcher.test.ts +89 -0
- package/src/linux/linux-studio-launcher.ts +11 -0
- package/src/plugin/persistent-plugin-installer.test.ts +4 -0
- package/src/plugin/persistent-plugin-installer.ts +2 -1
- package/src/plugin/plugin-build-version.test.ts +55 -0
- package/src/plugin/plugin-build-version.ts +53 -0
- package/src/plugin/plugin-injector.ts +7 -1
- package/src/process/studio-process-closer.test.ts +107 -0
- package/src/process/studio-process-closer.ts +187 -0
- package/src/process/studio-settings.test.ts +18 -0
- package/src/process/studio-settings.ts +61 -0
- package/src/process/studio-window-state.ts +65 -0
- package/src/server/studio-bridge-server.ts +15 -4
- package/src/server/web-socket-protocol.test.ts +55 -0
- package/src/server/web-socket-protocol.ts +468 -8
- package/src/test/e2e/persistent-session.test.ts +137 -0
- package/src/test/e2e/split-server.test.ts +3 -5
- package/templates/studio-bridge-plugin/default.project.json +3 -0
- package/templates/studio-bridge-plugin/src/Shared/ActionRouter.lua +208 -159
- package/templates/studio-bridge-plugin/src/Shared/ActionRouter.spec.lua +418 -0
- package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.lua +94 -0
- package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.spec.lua +166 -0
- package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.lua +118 -137
- package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.spec.lua +228 -0
- package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.lua +80 -109
- package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.spec.lua +116 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.lua +233 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.spec.lua +239 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.lua +53 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.spec.lua +27 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.lua +164 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.spec.lua +159 -0
- package/templates/studio-bridge-plugin/src/StudioBridgePlugin.server.lua +266 -368
- package/templates/studio-bridge-plugin/src/Vendor/png.lua +8 -2
- package/test/default.project.json +41 -0
- package/test/jest.config.lua +3 -0
- package/test/scripts/Server/ServerMain.server.lua +10 -0
- package/tsconfig.json +1 -0
- package/templates/studio-bridge-plugin/src/Shared/Protocol.lua +0 -132
- package/templates/studio-bridge-plugin-test/test/actions.test.lua +0 -580
- package/templates/studio-bridge-plugin-test/test/discovery.test.lua +0 -622
- package/templates/studio-bridge-plugin-test/test/execute-handler.test.lua +0 -517
- package/templates/studio-bridge-plugin-test/test/hash-lifecycle.test.lua +0 -814
- package/templates/studio-bridge-plugin-test/test/integration.test.lua +0 -1020
- package/templates/studio-bridge-plugin-test/test/new-actions.test.lua +0 -339
- package/templates/studio-bridge-plugin-test/test/plugin-entry.test.lua +0 -493
- package/templates/studio-bridge-plugin-test/test/protocol.test.lua +0 -506
- package/templates/studio-bridge-plugin-test/test/roblox-mocks.lua +0 -108
- package/templates/studio-bridge-plugin-test/test/test-runner.lua +0 -112
package/docker/Dockerfile
CHANGED
|
@@ -62,6 +62,9 @@ RUN mkdir -p /home/studio/.aftman/bin \
|
|
|
62
62
|
# --- Build studio-bridge from source (via named build context "workspace") ---
|
|
63
63
|
COPY --from=workspace --chown=studio:studio package.json pnpm-workspace.yaml pnpm-lock.yaml tsconfig.json /home/studio/build/
|
|
64
64
|
COPY --from=workspace --chown=studio:studio tools/ /home/studio/build/tools/
|
|
65
|
+
# The Luau test runner studio-bridge's plugin specs depend on
|
|
66
|
+
COPY --from=workspace --chown=studio:studio src/loader/ /home/studio/build/src/loader/
|
|
67
|
+
COPY --from=workspace --chown=studio:studio src/nevermore-test-runner/ /home/studio/build/src/nevermore-test-runner/
|
|
65
68
|
WORKDIR /home/studio/build
|
|
66
69
|
RUN pnpm install --frozen-lockfile --filter "@quenty/studio-bridge..." \
|
|
67
70
|
&& pnpm -r --filter "@quenty/studio-bridge..." run build
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/studio-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "WebSocket-based bridge for running Luau scripts in Roblox Studio",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -15,15 +15,18 @@
|
|
|
15
15
|
"studio-bridge": "./dist/src/cli/cli.js"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@quenty/cli-output-helpers": "1.
|
|
19
|
-
"@quenty/nevermore-cli-helpers": "1.
|
|
20
|
-
"@quenty/nevermore-
|
|
18
|
+
"@quenty/cli-output-helpers": "1.19.0",
|
|
19
|
+
"@quenty/nevermore-cli-helpers": "1.20.0",
|
|
20
|
+
"@quenty/nevermore-deploy": "1.1.1",
|
|
21
|
+
"@quenty/nevermore-template-helpers": "1.21.0",
|
|
21
22
|
"execa": "^9.6.1",
|
|
22
23
|
"ws": "^8.18.0",
|
|
23
24
|
"yargs": "^18.0.0",
|
|
24
25
|
"zod": "^4.3.6"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
28
|
+
"@quenty/nevermore-test-runner": "1.6.3",
|
|
29
|
+
"@quentystudios/jest-lua": "3.10.0-quenty.2",
|
|
27
30
|
"@types/node": "^18.11.4",
|
|
28
31
|
"@types/ws": "^8.5.13",
|
|
29
32
|
"@types/yargs": "^17.0.13",
|
|
@@ -36,8 +39,7 @@
|
|
|
36
39
|
"build:clean": "tsc --build --clean",
|
|
37
40
|
"test": "vitest run",
|
|
38
41
|
"test:watch": "vitest",
|
|
39
|
-
"test:plugin": "lune run templates/studio-bridge-plugin-test/test/test-runner",
|
|
40
42
|
"test:integration": "node dist/test/integration/smoke-test.js"
|
|
41
43
|
},
|
|
42
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "8ccad9d519f633a4af6659664374605cf2df7ecf"
|
|
43
45
|
}
|
|
@@ -25,6 +25,7 @@ async function performRegisterHandshake(
|
|
|
25
25
|
placeName?: string;
|
|
26
26
|
state?: string;
|
|
27
27
|
context?: string;
|
|
28
|
+
playerName?: string;
|
|
28
29
|
capabilities?: string[];
|
|
29
30
|
}
|
|
30
31
|
): Promise<{ ws: WebSocket }> {
|
|
@@ -39,6 +40,8 @@ async function performRegisterHandshake(
|
|
|
39
40
|
instanceId: options?.instanceId ?? 'inst-1',
|
|
40
41
|
placeName: options?.placeName ?? 'TestPlace',
|
|
41
42
|
state: options?.state ?? 'Edit',
|
|
43
|
+
context: options?.context,
|
|
44
|
+
playerName: options?.playerName,
|
|
42
45
|
capabilities: options?.capabilities ?? ['execute', 'queryState'],
|
|
43
46
|
},
|
|
44
47
|
})
|
|
@@ -656,6 +659,129 @@ describe('BridgeConnection', () => {
|
|
|
656
659
|
});
|
|
657
660
|
});
|
|
658
661
|
|
|
662
|
+
describe('target matching', () => {
|
|
663
|
+
async function connectPlaytestAsync(): Promise<BridgeConnection> {
|
|
664
|
+
const conn = await BridgeConnection.connectAsync({
|
|
665
|
+
port: 0,
|
|
666
|
+
keepAlive: true,
|
|
667
|
+
});
|
|
668
|
+
connections.push(conn);
|
|
669
|
+
|
|
670
|
+
const plugins: Array<[string, string, string, string]> = [
|
|
671
|
+
['test-place-3f9a-edit', 'test-place-3f9a', 'edit', 'test-place.rbxl'],
|
|
672
|
+
['test-place-3f9a-server', 'test-place-3f9a', 'server', 'Game'],
|
|
673
|
+
['test-place-3f9a-client', 'test-place-3f9a', 'client', 'Game'],
|
|
674
|
+
['other-map-77c1-edit', 'other-map-77c1', 'edit', 'Other Map'],
|
|
675
|
+
];
|
|
676
|
+
for (const [sessionId, instanceId, context, placeName] of plugins) {
|
|
677
|
+
const { ws } = await performRegisterHandshake(conn.port, sessionId, {
|
|
678
|
+
instanceId,
|
|
679
|
+
context,
|
|
680
|
+
placeName,
|
|
681
|
+
});
|
|
682
|
+
openClients.push(ws);
|
|
683
|
+
}
|
|
684
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
685
|
+
return conn;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
it('resolves an instance id prefix to its edit session', async () => {
|
|
689
|
+
const conn = await connectPlaytestAsync();
|
|
690
|
+
|
|
691
|
+
const session = await conn.resolveSessionAsync('test-place');
|
|
692
|
+
expect(session.info.sessionId).toBe('test-place-3f9a-edit');
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('applies --context within the targeted instance', async () => {
|
|
696
|
+
const conn = await connectPlaytestAsync();
|
|
697
|
+
|
|
698
|
+
const session = await conn.resolveSessionAsync(
|
|
699
|
+
'test-place-3f9a-edit',
|
|
700
|
+
'server'
|
|
701
|
+
);
|
|
702
|
+
expect(session.info.sessionId).toBe('test-place-3f9a-server');
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
it('refuses a prefix that matches several instances', async () => {
|
|
706
|
+
const conn = await connectPlaytestAsync();
|
|
707
|
+
const { ws } = await performRegisterHandshake(
|
|
708
|
+
conn.port,
|
|
709
|
+
'test-place-b2c4-edit',
|
|
710
|
+
{ instanceId: 'test-place-b2c4', context: 'edit' }
|
|
711
|
+
);
|
|
712
|
+
openClients.push(ws);
|
|
713
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
714
|
+
|
|
715
|
+
await expect(conn.resolveSessionAsync('test-place')).rejects.toThrow(
|
|
716
|
+
/matches several Studio instances/
|
|
717
|
+
);
|
|
718
|
+
await expect(conn.resolveSessionAsync('missing')).rejects.toThrow(
|
|
719
|
+
SessionNotFoundError
|
|
720
|
+
);
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
it('resolves a prefix that matches one session to that session', async () => {
|
|
724
|
+
const conn = await connectPlaytestAsync();
|
|
725
|
+
|
|
726
|
+
const session = await conn.resolveSessionAsync('test-place-3f9a-ser');
|
|
727
|
+
expect(session.info.sessionId).toBe('test-place-3f9a-server');
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
it('refuses --context when a multiplayer test has several clients', async () => {
|
|
731
|
+
const conn = await connectPlaytestAsync();
|
|
732
|
+
const { ws } = await performRegisterHandshake(
|
|
733
|
+
conn.port,
|
|
734
|
+
'test-place-3f9a-client-player2',
|
|
735
|
+
{
|
|
736
|
+
instanceId: 'test-place-3f9a',
|
|
737
|
+
context: 'client',
|
|
738
|
+
playerName: 'Player2',
|
|
739
|
+
}
|
|
740
|
+
);
|
|
741
|
+
openClients.push(ws);
|
|
742
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
743
|
+
|
|
744
|
+
await expect(
|
|
745
|
+
conn.resolveSessionAsync('test-place', 'client')
|
|
746
|
+
).rejects.toThrow(/several client sessions/);
|
|
747
|
+
await expect(
|
|
748
|
+
conn.resolveSessionAsync('test-place-3f9a-cl')
|
|
749
|
+
).rejects.toThrow(/'test-place-3f9a-cl' matches several sessions/);
|
|
750
|
+
const session = await conn.resolveSessionAsync(
|
|
751
|
+
'test-place-3f9a-client-player2',
|
|
752
|
+
'client'
|
|
753
|
+
);
|
|
754
|
+
expect(session.info.playerName).toBe('Player2');
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it('filters listed sessions by target and context', async () => {
|
|
758
|
+
const conn = await connectPlaytestAsync();
|
|
759
|
+
|
|
760
|
+
expect(conn.findSessions('test-place').map((s) => s.sessionId)).toEqual([
|
|
761
|
+
'test-place-3f9a-edit',
|
|
762
|
+
'test-place-3f9a-server',
|
|
763
|
+
'test-place-3f9a-client',
|
|
764
|
+
]);
|
|
765
|
+
expect(
|
|
766
|
+
conn.findSessions(undefined, 'edit').map((s) => s.sessionId)
|
|
767
|
+
).toEqual(['test-place-3f9a-edit', 'other-map-77c1-edit']);
|
|
768
|
+
expect(
|
|
769
|
+
conn.findSessions('other-map-77c1-edit').map((s) => s.sessionId)
|
|
770
|
+
).toEqual(['other-map-77c1-edit']);
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it('reports a running playtest as the state of its instance', async () => {
|
|
774
|
+
const conn = await connectPlaytestAsync();
|
|
775
|
+
|
|
776
|
+
const byId = new Map(conn.listSessions().map((s) => [s.sessionId, s]));
|
|
777
|
+
expect(byId.get('test-place-3f9a-edit')?.state).toBe('Play');
|
|
778
|
+
expect(byId.get('test-place-3f9a-server')?.placeName).toBe(
|
|
779
|
+
'test-place.rbxl'
|
|
780
|
+
);
|
|
781
|
+
expect(byId.get('other-map-77c1-edit')?.state).toBe('Edit');
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
|
|
659
785
|
describe('waitForSession', () => {
|
|
660
786
|
it('resolves immediately when sessions already exist', async () => {
|
|
661
787
|
const conn = await BridgeConnection.connectAsync({
|
|
@@ -18,19 +18,25 @@ import {
|
|
|
18
18
|
getDefaultRemoteHost,
|
|
19
19
|
} from './internal/environment-detection.js';
|
|
20
20
|
import { BridgeSession } from './bridge-session.js';
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
import {
|
|
22
|
+
HOST_PROTOCOL_VERSION,
|
|
23
|
+
type HostEnvelope,
|
|
24
|
+
type HostProtocolMessage,
|
|
25
|
+
type ListSessionsRequest,
|
|
26
|
+
type ListInstancesRequest,
|
|
25
27
|
} from './internal/host-protocol.js';
|
|
26
28
|
import type { SessionInfo, SessionContext, InstanceInfo } from './types.js';
|
|
27
29
|
import { SessionNotFoundError, ContextNotFoundError } from './types.js';
|
|
28
|
-
import
|
|
30
|
+
import { annotateInstanceState } from './internal/instance-state.js';
|
|
31
|
+
import type {
|
|
32
|
+
PluginMessage,
|
|
33
|
+
ServerMessage,
|
|
34
|
+
} from '../server/web-socket-protocol.js';
|
|
29
35
|
import { OutputHelper } from '@quenty/cli-output-helpers';
|
|
30
36
|
export interface BridgeConnectionOptions {
|
|
31
37
|
/** Port for the bridge host. Default: 38741. */
|
|
32
38
|
port?: number;
|
|
33
|
-
/** Max time to wait for
|
|
39
|
+
/** Max time to wait for the first Studio session to connect. Default: 5_000ms. */
|
|
34
40
|
timeoutMs?: number;
|
|
35
41
|
/** Keep the host alive even when idle. Default: false. */
|
|
36
42
|
keepAlive?: boolean;
|
|
@@ -49,6 +55,8 @@ export interface BridgeConnectionOptions {
|
|
|
49
55
|
const DEFAULT_PORT = 38741;
|
|
50
56
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
51
57
|
const IDLE_EXIT_GRACE_MS = 5_000;
|
|
58
|
+
/** How long shutdown waits on relayed actions; a started playtest's request lasts until it ends. */
|
|
59
|
+
const RELAY_DRAIN_TIMEOUT_MS = 10_000;
|
|
52
60
|
|
|
53
61
|
/**
|
|
54
62
|
* Transport handle that relays messages to a plugin's WebSocket via BridgeHost.
|
|
@@ -93,9 +101,12 @@ class HostTransportHandle extends EventEmitter {
|
|
|
93
101
|
|
|
94
102
|
export class BridgeConnection extends EventEmitter {
|
|
95
103
|
private _role: 'host' | 'client';
|
|
104
|
+
private _isRemote = false;
|
|
96
105
|
private _isConnected: boolean = false;
|
|
97
106
|
private _keepAlive: boolean;
|
|
98
107
|
private _hasSettled: boolean = false;
|
|
108
|
+
private _firstSessionTimeoutMs: number | undefined;
|
|
109
|
+
private _pendingRelays = new Set<Promise<void>>();
|
|
99
110
|
|
|
100
111
|
// Host mode internals
|
|
101
112
|
private _host: BridgeHost | undefined;
|
|
@@ -105,6 +116,7 @@ export class BridgeConnection extends EventEmitter {
|
|
|
105
116
|
|
|
106
117
|
// Client mode internals
|
|
107
118
|
private _client: BridgeClient | undefined;
|
|
119
|
+
private _clientPort = 0;
|
|
108
120
|
|
|
109
121
|
// Idle exit
|
|
110
122
|
private _idleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
@@ -169,6 +181,7 @@ export class BridgeConnection extends EventEmitter {
|
|
|
169
181
|
),
|
|
170
182
|
]);
|
|
171
183
|
|
|
184
|
+
autoConn._isRemote = true;
|
|
172
185
|
return autoConn;
|
|
173
186
|
} catch {
|
|
174
187
|
console.warn(
|
|
@@ -186,6 +199,8 @@ export class BridgeConnection extends EventEmitter {
|
|
|
186
199
|
});
|
|
187
200
|
|
|
188
201
|
const conn = new BridgeConnection(detection.role, keepAlive);
|
|
202
|
+
conn._isRemote = parsedRemoteHost !== undefined;
|
|
203
|
+
conn._firstSessionTimeoutMs = options?.timeoutMs;
|
|
189
204
|
OutputHelper.verbose(
|
|
190
205
|
`[bridge] Role: ${detection.role}, port: ${detection.port}`
|
|
191
206
|
);
|
|
@@ -225,7 +240,9 @@ export class BridgeConnection extends EventEmitter {
|
|
|
225
240
|
return conn;
|
|
226
241
|
}
|
|
227
242
|
|
|
228
|
-
async disconnectAsync(
|
|
243
|
+
async disconnectAsync(
|
|
244
|
+
options: { relayDrainTimeoutMs?: number } = {}
|
|
245
|
+
): Promise<void> {
|
|
229
246
|
if (!this._isConnected) {
|
|
230
247
|
return;
|
|
231
248
|
}
|
|
@@ -244,6 +261,24 @@ export class BridgeConnection extends EventEmitter {
|
|
|
244
261
|
this._isConnected = false;
|
|
245
262
|
|
|
246
263
|
if (this._role === 'host' && this._host) {
|
|
264
|
+
// Shutting down drops the plugins, so let actions relayed for clients finish
|
|
265
|
+
if (this._pendingRelays.size > 0) {
|
|
266
|
+
OutputHelper.verbose(
|
|
267
|
+
`[bridge] Waiting for ${this._pendingRelays.size} relayed action(s) before shutting down`
|
|
268
|
+
);
|
|
269
|
+
let drainTimer: ReturnType<typeof setTimeout> | undefined;
|
|
270
|
+
await Promise.race([
|
|
271
|
+
Promise.allSettled([...this._pendingRelays]),
|
|
272
|
+
new Promise<void>((resolve) => {
|
|
273
|
+
drainTimer = setTimeout(
|
|
274
|
+
resolve,
|
|
275
|
+
options.relayDrainTimeoutMs ?? RELAY_DRAIN_TIMEOUT_MS
|
|
276
|
+
);
|
|
277
|
+
}),
|
|
278
|
+
]);
|
|
279
|
+
clearTimeout(drainTimer);
|
|
280
|
+
}
|
|
281
|
+
|
|
247
282
|
for (const handle of this._hostHandles.values()) {
|
|
248
283
|
handle.markDisconnected();
|
|
249
284
|
}
|
|
@@ -270,27 +305,64 @@ export class BridgeConnection extends EventEmitter {
|
|
|
270
305
|
return this._isConnected;
|
|
271
306
|
}
|
|
272
307
|
|
|
308
|
+
/**
|
|
309
|
+
* True when this is a client relaying through a host from an older
|
|
310
|
+
* studio-bridge, which may not pass on options or actions it predates.
|
|
311
|
+
*/
|
|
312
|
+
get isHostOutdated(): boolean {
|
|
313
|
+
return (
|
|
314
|
+
this._role === 'client' &&
|
|
315
|
+
(this._client?.hostProtocolVersion ?? 1) < HOST_PROTOCOL_VERSION
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
273
319
|
/** The actual port the bridge is bound to (host) or connected to (client). */
|
|
320
|
+
/** Whether the bridge host, and so Studio, runs on another machine. */
|
|
321
|
+
get isRemote(): boolean {
|
|
322
|
+
return this._isRemote;
|
|
323
|
+
}
|
|
324
|
+
|
|
274
325
|
get port(): number {
|
|
275
|
-
if (this.
|
|
326
|
+
if (this._host) {
|
|
276
327
|
return this._host.port;
|
|
277
328
|
}
|
|
278
|
-
return
|
|
329
|
+
return this._clientPort;
|
|
279
330
|
}
|
|
280
331
|
|
|
281
332
|
/** List all currently connected Studio sessions. */
|
|
282
333
|
listSessions(): SessionInfo[] {
|
|
283
334
|
if (this._role === 'host' && this._tracker) {
|
|
284
|
-
return this._tracker.listSessions();
|
|
335
|
+
return annotateInstanceState(this._tracker.listSessions());
|
|
285
336
|
}
|
|
286
337
|
|
|
287
338
|
if (this._role === 'client' && this._client) {
|
|
288
|
-
return this._client.listSessions();
|
|
339
|
+
return annotateInstanceState(this._client.listSessions());
|
|
289
340
|
}
|
|
290
341
|
|
|
291
342
|
return [];
|
|
292
343
|
}
|
|
293
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Sessions matching `--target` and `--context`. A target matches a
|
|
347
|
+
* session id or an instance id exactly, or else as a prefix.
|
|
348
|
+
*/
|
|
349
|
+
findSessions(target?: string, context?: SessionContext): SessionInfo[] {
|
|
350
|
+
let sessions = this.listSessions();
|
|
351
|
+
if (target) {
|
|
352
|
+
const exact = sessions.find((s) => s.sessionId === target);
|
|
353
|
+
if (exact && !context) return [exact];
|
|
354
|
+
|
|
355
|
+
const instanceIds = exact
|
|
356
|
+
? [exact.instanceId]
|
|
357
|
+
: this._matchInstanceIds(target);
|
|
358
|
+
sessions = sessions.filter((s) => instanceIds.includes(s.instanceId));
|
|
359
|
+
}
|
|
360
|
+
if (context) {
|
|
361
|
+
sessions = sessions.filter((s) => s.context === context);
|
|
362
|
+
}
|
|
363
|
+
return sessions;
|
|
364
|
+
}
|
|
365
|
+
|
|
294
366
|
/**
|
|
295
367
|
* List unique Studio instances. Each instance groups 1-3 context sessions
|
|
296
368
|
* that share the same instanceId.
|
|
@@ -320,6 +392,31 @@ export class BridgeConnection extends EventEmitter {
|
|
|
320
392
|
return undefined;
|
|
321
393
|
}
|
|
322
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Instances whose id, or one of whose session ids, is `target`. Falls
|
|
397
|
+
* back to ids that start with `target` when nothing matches exactly.
|
|
398
|
+
*/
|
|
399
|
+
private _matchInstanceIds(target: string): string[] {
|
|
400
|
+
const sessions = this.listSessions();
|
|
401
|
+
const exact = new Set(
|
|
402
|
+
sessions
|
|
403
|
+
.filter((s) => s.instanceId === target || s.sessionId === target)
|
|
404
|
+
.map((s) => s.instanceId)
|
|
405
|
+
);
|
|
406
|
+
if (exact.size > 0) return [...exact];
|
|
407
|
+
|
|
408
|
+
return [
|
|
409
|
+
...new Set(
|
|
410
|
+
sessions
|
|
411
|
+
.filter(
|
|
412
|
+
(s) =>
|
|
413
|
+
s.instanceId.startsWith(target) || s.sessionId.startsWith(target)
|
|
414
|
+
)
|
|
415
|
+
.map((s) => s.instanceId)
|
|
416
|
+
),
|
|
417
|
+
];
|
|
418
|
+
}
|
|
419
|
+
|
|
323
420
|
/**
|
|
324
421
|
* Resolve a session for command execution. Instance-aware: groups sessions
|
|
325
422
|
* by instanceId and auto-selects context within an instance.
|
|
@@ -345,13 +442,50 @@ export class BridgeConnection extends EventEmitter {
|
|
|
345
442
|
|
|
346
443
|
if (sessionId) {
|
|
347
444
|
const session = this.getSession(sessionId);
|
|
348
|
-
if (session) {
|
|
445
|
+
if (session && (!context || session.info.context === context)) {
|
|
349
446
|
return session;
|
|
350
447
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
448
|
+
|
|
449
|
+
const prefixed = this.listSessions().filter(
|
|
450
|
+
(s) =>
|
|
451
|
+
s.sessionId.startsWith(sessionId) &&
|
|
452
|
+
(!context || s.context === context)
|
|
453
|
+
);
|
|
454
|
+
const onlyPrefixed =
|
|
455
|
+
prefixed.length === 1 ? this.getSession(prefixed[0].sessionId) : null;
|
|
456
|
+
if (onlyPrefixed) {
|
|
457
|
+
return onlyPrefixed;
|
|
458
|
+
}
|
|
459
|
+
const namesInstance = prefixed.some((s) =>
|
|
460
|
+
s.instanceId.startsWith(sessionId)
|
|
354
461
|
);
|
|
462
|
+
if (!session && prefixed.length > 1 && !namesInstance) {
|
|
463
|
+
throw new SessionNotFoundError(
|
|
464
|
+
`'${sessionId}' matches several sessions. Use a longer --target:\n${prefixed
|
|
465
|
+
.map((s) => ` - ${s.sessionId}`)
|
|
466
|
+
.join('\n')}`,
|
|
467
|
+
sessionId
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const instanceIds = session
|
|
472
|
+
? [session.info.instanceId]
|
|
473
|
+
: this._matchInstanceIds(sessionId);
|
|
474
|
+
if (instanceIds.length === 0) {
|
|
475
|
+
throw new SessionNotFoundError(
|
|
476
|
+
`Session '${sessionId}' not found`,
|
|
477
|
+
sessionId
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
if (instanceIds.length > 1) {
|
|
481
|
+
throw new SessionNotFoundError(
|
|
482
|
+
`'${sessionId}' matches several Studio instances: ${instanceIds.join(
|
|
483
|
+
', '
|
|
484
|
+
)}. Use a longer --target.`,
|
|
485
|
+
sessionId
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
return this._resolveByInstance(instanceIds[0], context);
|
|
355
489
|
}
|
|
356
490
|
|
|
357
491
|
if (instanceId) {
|
|
@@ -368,13 +502,13 @@ export class BridgeConnection extends EventEmitter {
|
|
|
368
502
|
return this._resolveByInstance(instances[0].instanceId, context);
|
|
369
503
|
}
|
|
370
504
|
|
|
371
|
-
// Multiple instances: list session IDs so the user can copy one into --
|
|
505
|
+
// Multiple instances: list session IDs so the user can copy one into --target
|
|
372
506
|
const sessionList = this.listSessions()
|
|
373
507
|
.map((s) => ` - ${s.sessionId}: ${s.placeName} (${s.context})`)
|
|
374
508
|
.join('\n');
|
|
375
509
|
|
|
376
510
|
throw new SessionNotFoundError(
|
|
377
|
-
`Multiple Studio instances connected. Use --
|
|
511
|
+
`Multiple Studio instances connected. Use --target <id> to select one.\n${sessionList}`
|
|
378
512
|
);
|
|
379
513
|
}
|
|
380
514
|
|
|
@@ -429,7 +563,9 @@ export class BridgeConnection extends EventEmitter {
|
|
|
429
563
|
OutputHelper.verbose(
|
|
430
564
|
'[bridge] No sessions yet — waiting for plugins to discover us'
|
|
431
565
|
);
|
|
432
|
-
await this.waitForSessionsToSettleAsync(
|
|
566
|
+
await this.waitForSessionsToSettleAsync({
|
|
567
|
+
firstSessionTimeout: this._firstSessionTimeoutMs,
|
|
568
|
+
});
|
|
433
569
|
this._hasSettled = true;
|
|
434
570
|
const settled = this.listSessions();
|
|
435
571
|
OutputHelper.verbose(
|
|
@@ -461,7 +597,7 @@ export class BridgeConnection extends EventEmitter {
|
|
|
461
597
|
|
|
462
598
|
const firstTimeout = options?.firstSessionTimeout ?? 5_000;
|
|
463
599
|
const settleMs = options?.settleMs ?? 4_000;
|
|
464
|
-
const maxMs = options?.maxMs ?? 15_000;
|
|
600
|
+
const maxMs = options?.maxMs ?? Math.max(15_000, firstTimeout + settleMs);
|
|
465
601
|
|
|
466
602
|
// Wait for the first session
|
|
467
603
|
try {
|
|
@@ -538,7 +674,7 @@ export class BridgeConnection extends EventEmitter {
|
|
|
538
674
|
|
|
539
675
|
this._host.on('plugin-connected', (info) => {
|
|
540
676
|
const state = info.state ?? 'Edit';
|
|
541
|
-
const context = BridgeConnection._deriveContext(state);
|
|
677
|
+
const context = info.context ?? BridgeConnection._deriveContext(state);
|
|
542
678
|
|
|
543
679
|
const sessionInfo: SessionInfo = {
|
|
544
680
|
sessionId: info.sessionId,
|
|
@@ -548,7 +684,9 @@ export class BridgeConnection extends EventEmitter {
|
|
|
548
684
|
pluginVersion: info.pluginVersion ?? '',
|
|
549
685
|
capabilities: info.capabilities,
|
|
550
686
|
connectedAt: new Date(),
|
|
551
|
-
origin: 'user',
|
|
687
|
+
origin: info.launchId ? 'managed' : 'user',
|
|
688
|
+
launchId: info.launchId,
|
|
689
|
+
playerName: info.playerName,
|
|
552
690
|
context,
|
|
553
691
|
instanceId: info.instanceId ?? info.sessionId,
|
|
554
692
|
placeId: 0,
|
|
@@ -578,6 +716,7 @@ export class BridgeConnection extends EventEmitter {
|
|
|
578
716
|
type: 'list-sessions-response',
|
|
579
717
|
requestId: req.requestId,
|
|
580
718
|
sessions: this._tracker!.listSessions(),
|
|
719
|
+
protocolVersion: HOST_PROTOCOL_VERSION,
|
|
581
720
|
});
|
|
582
721
|
} else if (msg.type === 'list-instances') {
|
|
583
722
|
const req = msg as ListInstancesRequest;
|
|
@@ -586,6 +725,10 @@ export class BridgeConnection extends EventEmitter {
|
|
|
586
725
|
requestId: req.requestId,
|
|
587
726
|
instances: this._tracker!.listInstances(),
|
|
588
727
|
});
|
|
728
|
+
} else if (msg.type === 'host-envelope') {
|
|
729
|
+
const relay = this._relayEnvelopeAsync(msg as HostEnvelope, reply);
|
|
730
|
+
this._pendingRelays.add(relay);
|
|
731
|
+
void relay.finally(() => this._pendingRelays.delete(relay));
|
|
589
732
|
}
|
|
590
733
|
}
|
|
591
734
|
);
|
|
@@ -597,6 +740,49 @@ export class BridgeConnection extends EventEmitter {
|
|
|
597
740
|
// when sessions are removed (going from >0 to 0).
|
|
598
741
|
}
|
|
599
742
|
|
|
743
|
+
private async _relayEnvelopeAsync(
|
|
744
|
+
envelope: HostEnvelope,
|
|
745
|
+
reply: (m: HostProtocolMessage) => void
|
|
746
|
+
): Promise<void> {
|
|
747
|
+
const sessionId = envelope.targetSessionId;
|
|
748
|
+
const handle = this._hostHandles.get(sessionId);
|
|
749
|
+
|
|
750
|
+
if (envelope.timeoutMs === undefined) {
|
|
751
|
+
handle?.sendMessage(envelope.action);
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
let result: PluginMessage;
|
|
756
|
+
if (!handle) {
|
|
757
|
+
result = {
|
|
758
|
+
type: 'error',
|
|
759
|
+
sessionId,
|
|
760
|
+
payload: {
|
|
761
|
+
code: 'SESSION_MISMATCH',
|
|
762
|
+
message: `Session '${sessionId}' is not connected to this host`,
|
|
763
|
+
},
|
|
764
|
+
};
|
|
765
|
+
} else {
|
|
766
|
+
try {
|
|
767
|
+
result = await handle.sendActionAsync<PluginMessage>(
|
|
768
|
+
envelope.action,
|
|
769
|
+
envelope.timeoutMs
|
|
770
|
+
);
|
|
771
|
+
} catch (err) {
|
|
772
|
+
result = {
|
|
773
|
+
type: 'error',
|
|
774
|
+
sessionId,
|
|
775
|
+
payload: {
|
|
776
|
+
code: 'INTERNAL_ERROR',
|
|
777
|
+
message: err instanceof Error ? err.message : String(err),
|
|
778
|
+
},
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
reply({ type: 'host-response', requestId: envelope.requestId, result });
|
|
784
|
+
}
|
|
785
|
+
|
|
600
786
|
private async _initClientAsync(
|
|
601
787
|
port: number,
|
|
602
788
|
remoteHost?: string
|
|
@@ -621,6 +807,7 @@ export class BridgeConnection extends EventEmitter {
|
|
|
621
807
|
|
|
622
808
|
const host = remoteHost ? remoteHost.split(':')[0] : undefined;
|
|
623
809
|
await this._client.connectAsync(port, host);
|
|
810
|
+
this._clientPort = port;
|
|
624
811
|
this._isConnected = true;
|
|
625
812
|
}
|
|
626
813
|
|
|
@@ -646,7 +833,15 @@ export class BridgeConnection extends EventEmitter {
|
|
|
646
833
|
|
|
647
834
|
// 5a: Context specified
|
|
648
835
|
if (context) {
|
|
649
|
-
const
|
|
836
|
+
const matches = sessions.filter((s) => s.context === context);
|
|
837
|
+
if (matches.length > 1) {
|
|
838
|
+
throw new SessionNotFoundError(
|
|
839
|
+
`Instance '${instanceId}' has several ${context} sessions. Use --target <session id> to pick one:\n${matches
|
|
840
|
+
.map((s) => ` - ${s.sessionId}`)
|
|
841
|
+
.join('\n')}`
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
const match = matches[0];
|
|
650
845
|
if (match) {
|
|
651
846
|
const session = this.getSession(match.sessionId);
|
|
652
847
|
if (session) {
|
|
@@ -140,12 +140,13 @@ describe('BridgeSession', () => {
|
|
|
140
140
|
const [msg, timeout] = handle.sendActionAsync.mock.calls[0];
|
|
141
141
|
expect((msg as ServerMessage).type).toBe('execute');
|
|
142
142
|
expect((msg as any).payload.script).toBe('print("hello")');
|
|
143
|
-
expect(
|
|
143
|
+
expect((msg as any).payload.timeoutMs).toBe(120_000);
|
|
144
|
+
expect(timeout).toBe(125_000);
|
|
144
145
|
|
|
145
146
|
expect(result.success).toBe(true);
|
|
146
147
|
});
|
|
147
148
|
|
|
148
|
-
it('
|
|
149
|
+
it('has the plugin cancel the script at a custom timeout, then waits for its reply', async () => {
|
|
149
150
|
const handle = new MockTransportHandle();
|
|
150
151
|
handle.sendActionAsync.mockResolvedValueOnce({
|
|
151
152
|
type: 'scriptComplete',
|
|
@@ -156,8 +157,9 @@ describe('BridgeSession', () => {
|
|
|
156
157
|
const session = new BridgeSession(createSessionInfo(), handle);
|
|
157
158
|
await session.execAsync('print("hello")', 5_000);
|
|
158
159
|
|
|
159
|
-
const [, timeout] = handle.sendActionAsync.mock.calls[0];
|
|
160
|
-
expect(
|
|
160
|
+
const [msg, timeout] = handle.sendActionAsync.mock.calls[0];
|
|
161
|
+
expect((msg as any).payload.timeoutMs).toBe(5_000);
|
|
162
|
+
expect(timeout).toBe(10_000);
|
|
161
163
|
});
|
|
162
164
|
|
|
163
165
|
it('returns error info from error response', async () => {
|