@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
|
@@ -19,13 +19,22 @@ import type {
|
|
|
19
19
|
ScreenshotResult,
|
|
20
20
|
LogsResult,
|
|
21
21
|
DataModelResult,
|
|
22
|
+
ExportInstancesResult,
|
|
23
|
+
ImportInstancesResult,
|
|
22
24
|
LogOptions,
|
|
23
25
|
QueryDataModelOptions,
|
|
26
|
+
PlaytestMode,
|
|
27
|
+
PlaytestEndResult,
|
|
28
|
+
EndPlaytestResult,
|
|
24
29
|
} from './types.js';
|
|
25
30
|
import { SessionDisconnectedError } from './types.js';
|
|
26
31
|
import type {
|
|
27
32
|
PluginMessage,
|
|
28
33
|
OutputLevel,
|
|
34
|
+
FindInstancesMessage,
|
|
35
|
+
FindInstancesResultMessage,
|
|
36
|
+
GrepScriptsMessage,
|
|
37
|
+
GrepScriptsResultMessage,
|
|
29
38
|
} from '../server/web-socket-protocol.js';
|
|
30
39
|
import {
|
|
31
40
|
loadActionSourcesAsync,
|
|
@@ -33,14 +42,64 @@ import {
|
|
|
33
42
|
} from '../commands/framework/action-loader.js';
|
|
34
43
|
import { OutputHelper } from '@quenty/cli-output-helpers';
|
|
35
44
|
|
|
45
|
+
const EXECUTE_REPLY_GRACE_MS = 5_000;
|
|
46
|
+
|
|
36
47
|
const DEFAULT_TIMEOUTS: Record<string, number> = {
|
|
37
48
|
execute: 120_000,
|
|
38
49
|
queryState: 5_000,
|
|
39
50
|
captureScreenshot: 30_000,
|
|
40
51
|
queryDataModel: 10_000,
|
|
52
|
+
exportInstances: 60_000,
|
|
53
|
+
importInstances: 60_000,
|
|
54
|
+
findInstances: 60_000,
|
|
55
|
+
grepScripts: 60_000,
|
|
41
56
|
queryLogs: 10_000,
|
|
57
|
+
endPlaytest: 5_000,
|
|
42
58
|
};
|
|
43
59
|
|
|
60
|
+
/** Time a playtest gets to start and stop on top of its script's own timeout. */
|
|
61
|
+
const PLAYTEST_STARTUP_ALLOWANCE_MS = 60_000;
|
|
62
|
+
|
|
63
|
+
export interface RunPlaytestScriptOptions {
|
|
64
|
+
mode: PlaytestMode;
|
|
65
|
+
script: string;
|
|
66
|
+
runnerSource: string;
|
|
67
|
+
timeoutSec: number;
|
|
68
|
+
/** Passed to the script as `...`. */
|
|
69
|
+
args?: string[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Map an `execute`-style reply onto an ExecResult. */
|
|
73
|
+
function toExecResult(result: PluginMessage): ExecResult {
|
|
74
|
+
if (result.type === 'scriptComplete') {
|
|
75
|
+
const output = (result.payload.output ?? []).map((entry) => ({
|
|
76
|
+
level: entry.level as OutputLevel,
|
|
77
|
+
body: entry.body,
|
|
78
|
+
}));
|
|
79
|
+
return {
|
|
80
|
+
success: result.payload.success,
|
|
81
|
+
output,
|
|
82
|
+
error: result.payload.error,
|
|
83
|
+
traceback: result.payload.traceback,
|
|
84
|
+
returnValues: result.payload.returnValues,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (result.type === 'error') {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
output: [],
|
|
92
|
+
error: result.payload?.message ?? 'Unknown plugin error',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
output: [],
|
|
99
|
+
error: pluginError('scriptComplete', result).message,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
44
103
|
/**
|
|
45
104
|
* Build a descriptive error from a plugin response that didn't match the
|
|
46
105
|
* expected type. Extracts error details from error-typed responses.
|
|
@@ -94,47 +153,65 @@ export class BridgeSession extends EventEmitter {
|
|
|
94
153
|
return this._handle.isConnected;
|
|
95
154
|
}
|
|
96
155
|
|
|
97
|
-
async execAsync(
|
|
156
|
+
async execAsync(
|
|
157
|
+
code: string,
|
|
158
|
+
timeout?: number,
|
|
159
|
+
args?: string[]
|
|
160
|
+
): Promise<ExecResult> {
|
|
98
161
|
this._assertConnected();
|
|
99
162
|
await this._ensureActionsAsync();
|
|
100
163
|
|
|
164
|
+
// The plugin cancels the script at the timeout and replies, so wait a
|
|
165
|
+
// little longer than that for the reply
|
|
101
166
|
const timeoutMs = timeout ?? DEFAULT_TIMEOUTS.execute;
|
|
102
167
|
const result = await this._handle.sendActionAsync<PluginMessage>(
|
|
103
168
|
{
|
|
104
169
|
type: 'execute',
|
|
105
170
|
sessionId: this._info.sessionId,
|
|
106
171
|
requestId: randomUUID(),
|
|
107
|
-
payload: {
|
|
172
|
+
payload: {
|
|
173
|
+
script: code,
|
|
174
|
+
timeoutMs,
|
|
175
|
+
...(args && args.length > 0 ? { args } : {}),
|
|
176
|
+
},
|
|
108
177
|
},
|
|
109
|
-
timeoutMs
|
|
178
|
+
timeoutMs + EXECUTE_REPLY_GRACE_MS
|
|
110
179
|
);
|
|
111
180
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
level: entry.level as OutputLevel,
|
|
115
|
-
body: entry.body,
|
|
116
|
-
}));
|
|
117
|
-
return {
|
|
118
|
-
success: result.payload.success,
|
|
119
|
-
output,
|
|
120
|
-
error: result.payload.error,
|
|
121
|
-
returnValues: result.payload.returnValues,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
181
|
+
return toExecResult(result);
|
|
182
|
+
}
|
|
124
183
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Run a script in a fresh playtest's server, started from this edit
|
|
186
|
+
* session, and resolve with its result once the playtest has ended. Works
|
|
187
|
+
* without the playtest's own sessions connecting: the plugin plants
|
|
188
|
+
* `runnerSource` in the playtest to run the script and end the playtest.
|
|
189
|
+
*/
|
|
190
|
+
async runPlaytestScriptAsync(
|
|
191
|
+
options: RunPlaytestScriptOptions
|
|
192
|
+
): Promise<ExecResult> {
|
|
193
|
+
this._assertConnected();
|
|
194
|
+
await this._ensureActionsAsync();
|
|
132
195
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
196
|
+
const result = await this._handle.sendActionAsync<PluginMessage>(
|
|
197
|
+
{
|
|
198
|
+
type: 'runPlaytestScript',
|
|
199
|
+
sessionId: this._info.sessionId,
|
|
200
|
+
requestId: randomUUID(),
|
|
201
|
+
payload: {
|
|
202
|
+
mode: options.mode,
|
|
203
|
+
script: options.script,
|
|
204
|
+
runnerSource: options.runnerSource,
|
|
205
|
+
timeoutSec: options.timeoutSec,
|
|
206
|
+
...(options.args && options.args.length > 0
|
|
207
|
+
? { args: options.args }
|
|
208
|
+
: {}),
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
options.timeoutSec * 1000 + PLAYTEST_STARTUP_ALLOWANCE_MS
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
return toExecResult(result);
|
|
138
215
|
}
|
|
139
216
|
|
|
140
217
|
async queryStateAsync(): Promise<StateResult> {
|
|
@@ -158,6 +235,8 @@ export class BridgeSession extends EventEmitter {
|
|
|
158
235
|
placeId: result.payload.placeId,
|
|
159
236
|
placeName: result.payload.placeName,
|
|
160
237
|
gameId: result.payload.gameId,
|
|
238
|
+
viewportSize: result.payload.viewportSize,
|
|
239
|
+
studioVersion: result.payload.studioVersion,
|
|
161
240
|
};
|
|
162
241
|
}
|
|
163
242
|
|
|
@@ -206,6 +285,8 @@ export class BridgeSession extends EventEmitter {
|
|
|
206
285
|
direction: options?.direction,
|
|
207
286
|
levels: options?.levels,
|
|
208
287
|
includeInternal: options?.includeInternal,
|
|
288
|
+
...(options?.since !== undefined ? { since: options.since } : {}),
|
|
289
|
+
...(options?.groupRepeats ? { groupRepeats: true } : {}),
|
|
209
290
|
},
|
|
210
291
|
},
|
|
211
292
|
timeoutMs
|
|
@@ -255,6 +336,170 @@ export class BridgeSession extends EventEmitter {
|
|
|
255
336
|
throw pluginError('dataModelResult', result);
|
|
256
337
|
}
|
|
257
338
|
|
|
339
|
+
/** Search the DataModel for instances by name, class and tag. */
|
|
340
|
+
async findInstancesAsync(
|
|
341
|
+
options: FindInstancesMessage['payload']
|
|
342
|
+
): Promise<FindInstancesResultMessage['payload']> {
|
|
343
|
+
this._assertConnected();
|
|
344
|
+
await this._ensureActionsAsync();
|
|
345
|
+
|
|
346
|
+
const result = await this._handle.sendActionAsync<PluginMessage>(
|
|
347
|
+
{
|
|
348
|
+
type: 'findInstances',
|
|
349
|
+
sessionId: this._info.sessionId,
|
|
350
|
+
requestId: randomUUID(),
|
|
351
|
+
payload: options,
|
|
352
|
+
},
|
|
353
|
+
DEFAULT_TIMEOUTS.findInstances
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
if (result.type === 'findInstancesResult') {
|
|
357
|
+
return result.payload;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
throw pluginError('findInstancesResult', result);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Search the source of every script for a pattern. */
|
|
364
|
+
async grepScriptsAsync(
|
|
365
|
+
options: GrepScriptsMessage['payload']
|
|
366
|
+
): Promise<GrepScriptsResultMessage['payload']> {
|
|
367
|
+
this._assertConnected();
|
|
368
|
+
await this._ensureActionsAsync();
|
|
369
|
+
|
|
370
|
+
const result = await this._handle.sendActionAsync<PluginMessage>(
|
|
371
|
+
{
|
|
372
|
+
type: 'grepScripts',
|
|
373
|
+
sessionId: this._info.sessionId,
|
|
374
|
+
requestId: randomUUID(),
|
|
375
|
+
payload: options,
|
|
376
|
+
},
|
|
377
|
+
DEFAULT_TIMEOUTS.grepScripts
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
if (result.type === 'grepScriptsResult') {
|
|
381
|
+
return result.payload;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
throw pluginError('grepScriptsResult', result);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** Serialize the instances at `paths` into one binary model (.rbxm). */
|
|
388
|
+
async exportInstancesAsync(paths: string[]): Promise<ExportInstancesResult> {
|
|
389
|
+
this._assertConnected();
|
|
390
|
+
await this._ensureActionsAsync();
|
|
391
|
+
|
|
392
|
+
const result = await this._handle.sendActionAsync<PluginMessage>(
|
|
393
|
+
{
|
|
394
|
+
type: 'exportInstances',
|
|
395
|
+
sessionId: this._info.sessionId,
|
|
396
|
+
requestId: randomUUID(),
|
|
397
|
+
payload: { paths },
|
|
398
|
+
},
|
|
399
|
+
DEFAULT_TIMEOUTS.exportInstances
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
if (result.type === 'exportInstancesResult') {
|
|
403
|
+
return { data: result.payload.data, paths: result.payload.paths };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
throw pluginError('exportInstancesResult', result);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** Insert a binary model (.rbxm), base64-encoded, under `parentPath`. */
|
|
410
|
+
async importInstancesAsync(
|
|
411
|
+
data: string,
|
|
412
|
+
parentPath: string
|
|
413
|
+
): Promise<ImportInstancesResult> {
|
|
414
|
+
this._assertConnected();
|
|
415
|
+
await this._ensureActionsAsync();
|
|
416
|
+
|
|
417
|
+
const result = await this._handle.sendActionAsync<PluginMessage>(
|
|
418
|
+
{
|
|
419
|
+
type: 'importInstances',
|
|
420
|
+
sessionId: this._info.sessionId,
|
|
421
|
+
requestId: randomUUID(),
|
|
422
|
+
payload: { data, parentPath },
|
|
423
|
+
},
|
|
424
|
+
DEFAULT_TIMEOUTS.importInstances
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
if (result.type === 'importInstancesResult') {
|
|
428
|
+
return { paths: result.payload.paths };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
throw pluginError('importInstancesResult', result);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Start a playtest from this edit session. Resolves when the playtest
|
|
436
|
+
* ends, not when it starts: StudioTestService only reports a refusal to
|
|
437
|
+
* start (a playtest already running) by failing the call that would have
|
|
438
|
+
* waited for the end.
|
|
439
|
+
*/
|
|
440
|
+
async runPlaytestAsync(
|
|
441
|
+
mode: PlaytestMode,
|
|
442
|
+
playtestId: string,
|
|
443
|
+
bridgePort: number,
|
|
444
|
+
timeoutMs: number,
|
|
445
|
+
players?: number
|
|
446
|
+
): Promise<PlaytestEndResult> {
|
|
447
|
+
this._assertConnected();
|
|
448
|
+
await this._ensureActionsAsync();
|
|
449
|
+
|
|
450
|
+
const result = await this._handle.sendActionAsync<PluginMessage>(
|
|
451
|
+
{
|
|
452
|
+
type: 'startPlaytest',
|
|
453
|
+
sessionId: this._info.sessionId,
|
|
454
|
+
requestId: randomUUID(),
|
|
455
|
+
payload: {
|
|
456
|
+
mode,
|
|
457
|
+
playtestId,
|
|
458
|
+
bridgePort,
|
|
459
|
+
instanceId: this._info.instanceId,
|
|
460
|
+
...(players !== undefined ? { players } : {}),
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
timeoutMs
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
if (result.type === 'playtestEnded') {
|
|
467
|
+
return {
|
|
468
|
+
playtestId: result.payload.playtestId,
|
|
469
|
+
success: result.payload.success,
|
|
470
|
+
error: result.payload.error,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
throw pluginError('playtestEnded', result);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/** End the playtest this server session belongs to. */
|
|
478
|
+
async endPlaytestAsync(): Promise<EndPlaytestResult> {
|
|
479
|
+
this._assertConnected();
|
|
480
|
+
await this._ensureActionsAsync();
|
|
481
|
+
|
|
482
|
+
const result = await this._handle.sendActionAsync<PluginMessage>(
|
|
483
|
+
{
|
|
484
|
+
type: 'endPlaytest',
|
|
485
|
+
sessionId: this._info.sessionId,
|
|
486
|
+
requestId: randomUUID(),
|
|
487
|
+
payload: {} as Record<string, never>,
|
|
488
|
+
},
|
|
489
|
+
DEFAULT_TIMEOUTS.endPlaytest
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
if (result.type === 'endPlaytestResult') {
|
|
493
|
+
return {
|
|
494
|
+
playtestId: result.payload.playtestId,
|
|
495
|
+
mode: result.payload.mode,
|
|
496
|
+
players: result.payload.players,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
throw pluginError('endPlaytestResult', result);
|
|
501
|
+
}
|
|
502
|
+
|
|
258
503
|
private _assertConnected(): void {
|
|
259
504
|
if (!this._handle.isConnected) {
|
|
260
505
|
throw new SessionDisconnectedError(this._info.sessionId);
|
|
@@ -300,7 +545,7 @@ export class BridgeSession extends EventEmitter {
|
|
|
300
545
|
type: 'syncActions',
|
|
301
546
|
sessionId: this._info.sessionId,
|
|
302
547
|
requestId: randomUUID(),
|
|
303
|
-
payload: { actions },
|
|
548
|
+
payload: { actions, verbose: OutputHelper.isVerbose() },
|
|
304
549
|
},
|
|
305
550
|
10_000
|
|
306
551
|
);
|
package/src/bridge/index.ts
CHANGED
|
@@ -316,6 +316,29 @@ describe('BridgeClient', () => {
|
|
|
316
316
|
});
|
|
317
317
|
});
|
|
318
318
|
|
|
319
|
+
describe('host loss', () => {
|
|
320
|
+
it('fails a pending request at once when the host drops without replying', async () => {
|
|
321
|
+
host = await createMockHostWithSessions([createSessionInfo()]);
|
|
322
|
+
host.wss.on('connection', (ws) => {
|
|
323
|
+
ws.on('message', (raw) => {
|
|
324
|
+
const msg = decodeHostMessage(raw.toString());
|
|
325
|
+
if (msg?.type === 'host-envelope') {
|
|
326
|
+
ws.terminate();
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
client = new BridgeClient();
|
|
332
|
+
await client.connectAsync(host.port);
|
|
333
|
+
|
|
334
|
+
const started = Date.now();
|
|
335
|
+
await expect(
|
|
336
|
+
client.getSession('session-1')!.queryStateAsync()
|
|
337
|
+
).rejects.toThrow('The bridge host shut down before replying');
|
|
338
|
+
expect(Date.now() - started).toBeLessThan(2_000);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
319
342
|
describe('session events', () => {
|
|
320
343
|
it('handles session-connected event from host', async () => {
|
|
321
344
|
host = await createMockHost();
|
|
@@ -80,9 +80,13 @@ class RelayedTransportHandle extends EventEmitter implements TransportHandle {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/** Lets the host's own timeout error, which says what timed out, arrive first. */
|
|
84
|
+
const HOST_REPLY_GRACE_MS = 2_000;
|
|
85
|
+
|
|
83
86
|
export class BridgeClient extends EventEmitter {
|
|
84
87
|
private _transport = new TransportClient();
|
|
85
88
|
private _sessions = new Map<string, SessionInfo>();
|
|
89
|
+
private _hostProtocolVersion: number | undefined;
|
|
86
90
|
private _sessionHandles = new Map<string, RelayedTransportHandle>();
|
|
87
91
|
private _bridgeSessions = new Map<string, BridgeSession>();
|
|
88
92
|
private _pendingRequests = new Map<
|
|
@@ -111,6 +115,10 @@ export class BridgeClient extends EventEmitter {
|
|
|
111
115
|
|
|
112
116
|
this._transport.on('disconnected', () => {
|
|
113
117
|
this._isConnected = false;
|
|
118
|
+
// Replies to these went to the old host, so none will arrive
|
|
119
|
+
this._rejectPendingRequests(
|
|
120
|
+
'The bridge host shut down before replying. Studio may still have run the request.'
|
|
121
|
+
);
|
|
114
122
|
this.emit('disconnected');
|
|
115
123
|
|
|
116
124
|
// Trigger failover detection
|
|
@@ -134,6 +142,14 @@ export class BridgeClient extends EventEmitter {
|
|
|
134
142
|
await this._fetchSessionsAsync();
|
|
135
143
|
}
|
|
136
144
|
|
|
145
|
+
private _rejectPendingRequests(message: string): void {
|
|
146
|
+
for (const [, entry] of this._pendingRequests) {
|
|
147
|
+
clearTimeout(entry.timer);
|
|
148
|
+
entry.reject(new Error(message));
|
|
149
|
+
}
|
|
150
|
+
this._pendingRequests.clear();
|
|
151
|
+
}
|
|
152
|
+
|
|
137
153
|
/**
|
|
138
154
|
* Disconnect from the bridge host.
|
|
139
155
|
*/
|
|
@@ -144,12 +160,7 @@ export class BridgeClient extends EventEmitter {
|
|
|
144
160
|
this._transport.disconnect();
|
|
145
161
|
this._isConnected = false;
|
|
146
162
|
|
|
147
|
-
|
|
148
|
-
for (const [, entry] of this._pendingRequests) {
|
|
149
|
-
clearTimeout(entry.timer);
|
|
150
|
-
entry.reject(new Error('Client disconnected'));
|
|
151
|
-
}
|
|
152
|
-
this._pendingRequests.clear();
|
|
163
|
+
this._rejectPendingRequests('Client disconnected');
|
|
153
164
|
|
|
154
165
|
// Mark all handles as disconnected
|
|
155
166
|
for (const handle of this._sessionHandles.values()) {
|
|
@@ -163,6 +174,11 @@ export class BridgeClient extends EventEmitter {
|
|
|
163
174
|
/**
|
|
164
175
|
* List all known sessions.
|
|
165
176
|
*/
|
|
177
|
+
/** The host's HOST_PROTOCOL_VERSION, or undefined for hosts older than it. */
|
|
178
|
+
get hostProtocolVersion(): number | undefined {
|
|
179
|
+
return this._hostProtocolVersion;
|
|
180
|
+
}
|
|
181
|
+
|
|
166
182
|
listSessions(): SessionInfo[] {
|
|
167
183
|
return Array.from(this._sessions.values());
|
|
168
184
|
}
|
|
@@ -228,6 +244,7 @@ export class BridgeClient extends EventEmitter {
|
|
|
228
244
|
requestId,
|
|
229
245
|
targetSessionId,
|
|
230
246
|
action,
|
|
247
|
+
timeoutMs,
|
|
231
248
|
};
|
|
232
249
|
|
|
233
250
|
return new Promise<PluginMessage>((resolve, reject) => {
|
|
@@ -236,7 +253,7 @@ export class BridgeClient extends EventEmitter {
|
|
|
236
253
|
reject(
|
|
237
254
|
new Error(`Request "${requestId}" timed out after ${timeoutMs}ms`)
|
|
238
255
|
);
|
|
239
|
-
}, timeoutMs);
|
|
256
|
+
}, timeoutMs + HOST_REPLY_GRACE_MS);
|
|
240
257
|
|
|
241
258
|
this._pendingRequests.set(requestId, { resolve, reject, timer });
|
|
242
259
|
|
|
@@ -306,6 +323,7 @@ export class BridgeClient extends EventEmitter {
|
|
|
306
323
|
}
|
|
307
324
|
|
|
308
325
|
private _handleListSessionsResponse(msg: ListSessionsResponse): void {
|
|
326
|
+
this._hostProtocolVersion = msg.protocolVersion;
|
|
309
327
|
const pending = this._pendingRequests.get(msg.requestId);
|
|
310
328
|
if (pending) {
|
|
311
329
|
clearTimeout(pending.timer);
|
|
@@ -254,6 +254,44 @@ describe('BridgeHost', () => {
|
|
|
254
254
|
});
|
|
255
255
|
});
|
|
256
256
|
|
|
257
|
+
describe('client relay', () => {
|
|
258
|
+
it('answers an envelope it cannot decode instead of dropping it', async () => {
|
|
259
|
+
host = new BridgeHost();
|
|
260
|
+
const port = await host.startAsync({ port: 0 });
|
|
261
|
+
const ws = await new Promise<WebSocket>((resolve, reject) => {
|
|
262
|
+
const client = new WebSocket(`ws://localhost:${port}/client`);
|
|
263
|
+
client.on('open', () => resolve(client));
|
|
264
|
+
client.on('error', reject);
|
|
265
|
+
});
|
|
266
|
+
openClients.push(ws);
|
|
267
|
+
|
|
268
|
+
const reply = new Promise<any>((resolve) => {
|
|
269
|
+
ws.once('message', (raw) => resolve(JSON.parse(raw.toString())));
|
|
270
|
+
});
|
|
271
|
+
ws.send(
|
|
272
|
+
JSON.stringify({
|
|
273
|
+
type: 'host-envelope',
|
|
274
|
+
requestId: 'req-1',
|
|
275
|
+
targetSessionId: 'session-1',
|
|
276
|
+
action: {
|
|
277
|
+
type: 'fromTheFuture',
|
|
278
|
+
sessionId: 'session-1',
|
|
279
|
+
payload: {},
|
|
280
|
+
},
|
|
281
|
+
timeoutMs: 1000,
|
|
282
|
+
})
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const response = await reply;
|
|
286
|
+
expect(response.type).toBe('host-response');
|
|
287
|
+
expect(response.requestId).toBe('req-1');
|
|
288
|
+
expect(response.result.type).toBe('error');
|
|
289
|
+
expect(response.result.payload.message).toContain(
|
|
290
|
+
"could not relay 'fromTheFuture'"
|
|
291
|
+
);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
257
295
|
describe('health endpoint', () => {
|
|
258
296
|
it('responds with valid JSON on /health', async () => {
|
|
259
297
|
host = new BridgeHost();
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
decodePluginMessage,
|
|
21
21
|
encodeMessage,
|
|
22
22
|
type Capability,
|
|
23
|
+
type PluginContext,
|
|
23
24
|
type ServerMessage,
|
|
24
25
|
} from '../../server/web-socket-protocol.js';
|
|
25
26
|
import { PendingRequestMap } from '../../server/pending-request-map.js';
|
|
@@ -42,8 +43,14 @@ export interface PluginSessionInfo {
|
|
|
42
43
|
placeName?: string;
|
|
43
44
|
/** Studio state from register. */
|
|
44
45
|
state?: string;
|
|
46
|
+
/** DataModel context from register. */
|
|
47
|
+
context?: PluginContext;
|
|
45
48
|
/** Place file from register. */
|
|
46
49
|
placeFile?: string;
|
|
50
|
+
/** Launch id from register, for a Studio that `process run` launched. */
|
|
51
|
+
launchId?: string;
|
|
52
|
+
/** Player name from register, for a multiplayer test client. */
|
|
53
|
+
playerName?: string;
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
const SHUTDOWN_TIMEOUT_MS = 2_000;
|
|
@@ -122,6 +129,7 @@ export class BridgeHost extends EventEmitter {
|
|
|
122
129
|
const data = typeof raw === 'string' ? raw : raw.toString('utf-8');
|
|
123
130
|
const msg = decodeHostMessage(data);
|
|
124
131
|
if (!msg) {
|
|
132
|
+
this._replyUndecodable(ws, data);
|
|
125
133
|
return;
|
|
126
134
|
}
|
|
127
135
|
this.emit('client-message', msg, (reply: HostProtocolMessage) => {
|
|
@@ -357,6 +365,42 @@ export class BridgeHost extends EventEmitter {
|
|
|
357
365
|
}
|
|
358
366
|
}
|
|
359
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Answers an envelope this host cannot decode with an error, so the client
|
|
370
|
+
* fails fast instead of waiting out its timeout. That is usually a newer
|
|
371
|
+
* client relaying an action this host does not know.
|
|
372
|
+
*/
|
|
373
|
+
private _replyUndecodable(ws: WebSocket, data: string): void {
|
|
374
|
+
let parsed: any;
|
|
375
|
+
try {
|
|
376
|
+
parsed = JSON.parse(data);
|
|
377
|
+
} catch {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (
|
|
381
|
+
parsed?.type !== 'host-envelope' ||
|
|
382
|
+
typeof parsed.requestId !== 'string'
|
|
383
|
+
) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const actionType = String(parsed.action?.type ?? 'unknown');
|
|
388
|
+
ws.send(
|
|
389
|
+
encodeHostMessage({
|
|
390
|
+
type: 'host-response',
|
|
391
|
+
requestId: parsed.requestId,
|
|
392
|
+
result: {
|
|
393
|
+
type: 'error',
|
|
394
|
+
sessionId: String(parsed.targetSessionId ?? ''),
|
|
395
|
+
payload: {
|
|
396
|
+
code: 'INVALID_PAYLOAD',
|
|
397
|
+
message: `The bridge host could not relay '${actionType}'. It may run an older studio-bridge than this command; restart it (e.g. stop 'studio-bridge serve').`,
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
})
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
360
404
|
private _handlePluginConnection(
|
|
361
405
|
ws: WebSocket,
|
|
362
406
|
_request: IncomingMessage
|
|
@@ -379,7 +423,10 @@ export class BridgeHost extends EventEmitter {
|
|
|
379
423
|
instanceId: msg.payload.instanceId,
|
|
380
424
|
placeName: msg.payload.placeName,
|
|
381
425
|
state: msg.payload.state,
|
|
426
|
+
context: msg.payload.context,
|
|
382
427
|
placeFile: msg.payload.placeFile,
|
|
428
|
+
launchId: msg.payload.launchId,
|
|
429
|
+
playerName: msg.payload.playerName,
|
|
383
430
|
});
|
|
384
431
|
};
|
|
385
432
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { createServer, type Server } from 'net';
|
|
14
14
|
import { WebSocket } from 'ws';
|
|
15
|
+
import { MAX_MESSAGE_BYTES } from '../../server/web-socket-protocol.js';
|
|
15
16
|
import { HostUnreachableError } from '../types.js';
|
|
16
17
|
|
|
17
18
|
export type TakeoverState =
|
|
@@ -79,7 +80,9 @@ function tryConnectAsClientDefaultAsync(port: number): Promise<boolean> {
|
|
|
79
80
|
const CONNECT_TIMEOUT_MS = 2_000;
|
|
80
81
|
|
|
81
82
|
return new Promise<boolean>((resolve) => {
|
|
82
|
-
const ws = new WebSocket(`ws://localhost:${port}/client
|
|
83
|
+
const ws = new WebSocket(`ws://localhost:${port}/client`, {
|
|
84
|
+
maxPayload: MAX_MESSAGE_BYTES,
|
|
85
|
+
});
|
|
83
86
|
|
|
84
87
|
const timer = setTimeout(() => {
|
|
85
88
|
ws.removeAllListeners();
|
|
@@ -41,6 +41,21 @@ function createSessionInfo(overrides: Partial<SessionInfo> = {}): SessionInfo {
|
|
|
41
41
|
|
|
42
42
|
describe('host-protocol', () => {
|
|
43
43
|
describe('HostEnvelope', () => {
|
|
44
|
+
it('keeps payload fields it does not list, for newer clients', () => {
|
|
45
|
+
const decoded = roundTrip({
|
|
46
|
+
type: 'host-envelope',
|
|
47
|
+
requestId: 'req-1',
|
|
48
|
+
targetSessionId: 'session-1',
|
|
49
|
+
action: {
|
|
50
|
+
type: 'execute',
|
|
51
|
+
sessionId: 'session-1',
|
|
52
|
+
payload: { script: 'return ...', futureOption: true },
|
|
53
|
+
},
|
|
54
|
+
} as any) as HostEnvelope;
|
|
55
|
+
|
|
56
|
+
expect((decoded.action.payload as any).futureOption).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
44
59
|
it('round-trips correctly', () => {
|
|
45
60
|
const msg: HostEnvelope = {
|
|
46
61
|
type: 'host-envelope',
|