@quenty/studio-bridge 0.21.1 → 0.22.1

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.
Files changed (482) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +109 -17
  3. package/build-scripts/transform-add-session-attribute.lua +1 -0
  4. package/deploy.nevermore.json +10 -0
  5. package/dist/src/bridge/bridge-connection.d.ts +26 -2
  6. package/dist/src/bridge/bridge-connection.d.ts.map +1 -1
  7. package/dist/src/bridge/bridge-connection.js +159 -14
  8. package/dist/src/bridge/bridge-connection.js.map +1 -1
  9. package/dist/src/bridge/bridge-connection.test.js +81 -0
  10. package/dist/src/bridge/bridge-connection.test.js.map +1 -1
  11. package/dist/src/bridge/bridge-session.d.ts +35 -2
  12. package/dist/src/bridge/bridge-session.d.ts.map +1 -1
  13. package/dist/src/bridge/bridge-session.js +185 -28
  14. package/dist/src/bridge/bridge-session.js.map +1 -1
  15. package/dist/src/bridge/bridge-session.test.js +6 -4
  16. package/dist/src/bridge/bridge-session.test.js.map +1 -1
  17. package/dist/src/bridge/index.d.ts +1 -1
  18. package/dist/src/bridge/index.d.ts.map +1 -1
  19. package/dist/src/bridge/index.js.map +1 -1
  20. package/dist/src/bridge/internal/bridge-client.d.ts +4 -0
  21. package/dist/src/bridge/internal/bridge-client.d.ts.map +1 -1
  22. package/dist/src/bridge/internal/bridge-client.js +20 -7
  23. package/dist/src/bridge/internal/bridge-client.js.map +1 -1
  24. package/dist/src/bridge/internal/bridge-client.test.js +18 -0
  25. package/dist/src/bridge/internal/bridge-client.test.js.map +1 -1
  26. package/dist/src/bridge/internal/bridge-host.d.ts +13 -1
  27. package/dist/src/bridge/internal/bridge-host.d.ts.map +1 -1
  28. package/dist/src/bridge/internal/bridge-host.js +35 -0
  29. package/dist/src/bridge/internal/bridge-host.js.map +1 -1
  30. package/dist/src/bridge/internal/bridge-host.test.js +31 -0
  31. package/dist/src/bridge/internal/bridge-host.test.js.map +1 -1
  32. package/dist/src/bridge/internal/hand-off.d.ts.map +1 -1
  33. package/dist/src/bridge/internal/hand-off.js +4 -1
  34. package/dist/src/bridge/internal/hand-off.js.map +1 -1
  35. package/dist/src/bridge/internal/host-protocol.d.ts +8 -0
  36. package/dist/src/bridge/internal/host-protocol.d.ts.map +1 -1
  37. package/dist/src/bridge/internal/host-protocol.js +120 -13
  38. package/dist/src/bridge/internal/host-protocol.js.map +1 -1
  39. package/dist/src/bridge/internal/host-protocol.test.js +13 -0
  40. package/dist/src/bridge/internal/host-protocol.test.js.map +1 -1
  41. package/dist/src/bridge/internal/instance-state.d.ts +9 -0
  42. package/dist/src/bridge/internal/instance-state.d.ts.map +1 -0
  43. package/dist/src/bridge/internal/instance-state.js +28 -0
  44. package/dist/src/bridge/internal/instance-state.js.map +1 -0
  45. package/dist/src/bridge/internal/transport-client.d.ts.map +1 -1
  46. package/dist/src/bridge/internal/transport-client.js +2 -1
  47. package/dist/src/bridge/internal/transport-client.js.map +1 -1
  48. package/dist/src/bridge/internal/transport-server.d.ts.map +1 -1
  49. package/dist/src/bridge/internal/transport-server.js +5 -1
  50. package/dist/src/bridge/internal/transport-server.js.map +1 -1
  51. package/dist/src/bridge/types.d.ts +43 -4
  52. package/dist/src/bridge/types.d.ts.map +1 -1
  53. package/dist/src/bridge/types.js.map +1 -1
  54. package/dist/src/cli/adapters/cli-command-adapter-handler.test.js +24 -0
  55. package/dist/src/cli/adapters/cli-command-adapter-handler.test.js.map +1 -1
  56. package/dist/src/cli/adapters/cli-command-adapter.d.ts +6 -0
  57. package/dist/src/cli/adapters/cli-command-adapter.d.ts.map +1 -1
  58. package/dist/src/cli/adapters/cli-command-adapter.js +107 -11
  59. package/dist/src/cli/adapters/cli-command-adapter.js.map +1 -1
  60. package/dist/src/cli/adapters/group-builder.d.ts +3 -0
  61. package/dist/src/cli/adapters/group-builder.d.ts.map +1 -1
  62. package/dist/src/cli/adapters/group-builder.js +34 -15
  63. package/dist/src/cli/adapters/group-builder.js.map +1 -1
  64. package/dist/src/cli/adapters/group-builder.test.js +26 -2
  65. package/dist/src/cli/adapters/group-builder.test.js.map +1 -1
  66. package/dist/src/cli/adapters/help-formatter.d.ts.map +1 -1
  67. package/dist/src/cli/adapters/help-formatter.js +4 -3
  68. package/dist/src/cli/adapters/help-formatter.js.map +1 -1
  69. package/dist/src/cli/adapters/help-formatter.test.js +1 -1
  70. package/dist/src/cli/adapters/help-formatter.test.js.map +1 -1
  71. package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts +2 -0
  72. package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts.map +1 -0
  73. package/dist/src/cli/adapters/playtest-edit-guard.test.js +28 -0
  74. package/dist/src/cli/adapters/playtest-edit-guard.test.js.map +1 -0
  75. package/dist/src/cli/args/global-args.d.ts +1 -1
  76. package/dist/src/cli/args/global-args.d.ts.map +1 -1
  77. package/dist/src/cli/cli-output-context.d.ts +14 -0
  78. package/dist/src/cli/cli-output-context.d.ts.map +1 -0
  79. package/dist/src/cli/cli-output-context.js +39 -0
  80. package/dist/src/cli/cli-output-context.js.map +1 -0
  81. package/dist/src/cli/cli-output-context.test.d.ts +2 -0
  82. package/dist/src/cli/cli-output-context.test.d.ts.map +1 -0
  83. package/dist/src/cli/cli-output-context.test.js +38 -0
  84. package/dist/src/cli/cli-output-context.test.js.map +1 -0
  85. package/dist/src/cli/cli.js +40 -17
  86. package/dist/src/cli/cli.js.map +1 -1
  87. package/dist/src/cli/managed-studio-run.d.ts +21 -0
  88. package/dist/src/cli/managed-studio-run.d.ts.map +1 -0
  89. package/dist/src/cli/managed-studio-run.js +75 -0
  90. package/dist/src/cli/managed-studio-run.js.map +1 -0
  91. package/dist/src/cli/managed-studio-run.test.d.ts +2 -0
  92. package/dist/src/cli/managed-studio-run.test.d.ts.map +1 -0
  93. package/dist/src/cli/managed-studio-run.test.js +52 -0
  94. package/dist/src/cli/managed-studio-run.test.js.map +1 -0
  95. package/dist/src/cli/resolve-session.js +1 -1
  96. package/dist/src/cli/resolve-session.js.map +1 -1
  97. package/dist/src/cli/resolve-session.test.js +1 -1
  98. package/dist/src/cli/resolve-session.test.js.map +1 -1
  99. package/dist/src/cli/script-executor.d.ts +13 -1
  100. package/dist/src/cli/script-executor.d.ts.map +1 -1
  101. package/dist/src/cli/script-executor.js +77 -47
  102. package/dist/src/cli/script-executor.js.map +1 -1
  103. package/dist/src/commands/console/exec/exec.d.ts +17 -0
  104. package/dist/src/commands/console/exec/exec.d.ts.map +1 -1
  105. package/dist/src/commands/console/exec/exec.js +39 -7
  106. package/dist/src/commands/console/exec/exec.js.map +1 -1
  107. package/dist/src/commands/console/exec/exec.test.js +62 -3
  108. package/dist/src/commands/console/exec/exec.test.js.map +1 -1
  109. package/dist/src/commands/console/logs/logs.d.ts +30 -2
  110. package/dist/src/commands/console/logs/logs.d.ts.map +1 -1
  111. package/dist/src/commands/console/logs/logs.js +82 -7
  112. package/dist/src/commands/console/logs/logs.js.map +1 -1
  113. package/dist/src/commands/console/logs/logs.test.js +63 -4
  114. package/dist/src/commands/console/logs/logs.test.js.map +1 -1
  115. package/dist/src/commands/explorer/export/export.d.ts +19 -0
  116. package/dist/src/commands/explorer/export/export.d.ts.map +1 -0
  117. package/dist/src/commands/explorer/export/export.js +37 -0
  118. package/dist/src/commands/explorer/export/export.js.map +1 -0
  119. package/dist/src/commands/explorer/export/export.test.d.ts +2 -0
  120. package/dist/src/commands/explorer/export/export.test.d.ts.map +1 -0
  121. package/dist/src/commands/explorer/export/export.test.js +25 -0
  122. package/dist/src/commands/explorer/export/export.test.js.map +1 -0
  123. package/dist/src/commands/explorer/find/find.d.ts +28 -0
  124. package/dist/src/commands/explorer/find/find.d.ts.map +1 -0
  125. package/dist/src/commands/explorer/find/find.js +75 -0
  126. package/dist/src/commands/explorer/find/find.js.map +1 -0
  127. package/dist/src/commands/explorer/find/find.test.d.ts +2 -0
  128. package/dist/src/commands/explorer/find/find.test.d.ts.map +1 -0
  129. package/dist/src/commands/explorer/find/find.test.js +44 -0
  130. package/dist/src/commands/explorer/find/find.test.js.map +1 -0
  131. package/dist/src/commands/explorer/grep/grep.d.ts +24 -0
  132. package/dist/src/commands/explorer/grep/grep.d.ts.map +1 -0
  133. package/dist/src/commands/explorer/grep/grep.js +63 -0
  134. package/dist/src/commands/explorer/grep/grep.js.map +1 -0
  135. package/dist/src/commands/explorer/grep/grep.test.d.ts +2 -0
  136. package/dist/src/commands/explorer/grep/grep.test.d.ts.map +1 -0
  137. package/dist/src/commands/explorer/grep/grep.test.js +42 -0
  138. package/dist/src/commands/explorer/grep/grep.test.js.map +1 -0
  139. package/dist/src/commands/explorer/import/import.d.ts +21 -0
  140. package/dist/src/commands/explorer/import/import.d.ts.map +1 -0
  141. package/dist/src/commands/explorer/import/import.js +37 -0
  142. package/dist/src/commands/explorer/import/import.js.map +1 -0
  143. package/dist/src/commands/explorer/import/import.test.d.ts +2 -0
  144. package/dist/src/commands/explorer/import/import.test.d.ts.map +1 -0
  145. package/dist/src/commands/explorer/import/import.test.js +45 -0
  146. package/dist/src/commands/explorer/import/import.test.js.map +1 -0
  147. package/dist/src/commands/formatters.test.js +7 -1
  148. package/dist/src/commands/formatters.test.js.map +1 -1
  149. package/dist/src/commands/framework/action-loader.d.ts +5 -1
  150. package/dist/src/commands/framework/action-loader.d.ts.map +1 -1
  151. package/dist/src/commands/framework/action-loader.js +9 -2
  152. package/dist/src/commands/framework/action-loader.js.map +1 -1
  153. package/dist/src/commands/framework/action-loader.test.js +23 -0
  154. package/dist/src/commands/framework/action-loader.test.js.map +1 -1
  155. package/dist/src/commands/framework/arg-builder.d.ts +15 -2
  156. package/dist/src/commands/framework/arg-builder.d.ts.map +1 -1
  157. package/dist/src/commands/framework/arg-builder.js +22 -2
  158. package/dist/src/commands/framework/arg-builder.js.map +1 -1
  159. package/dist/src/commands/framework/define-command.d.ts +19 -3
  160. package/dist/src/commands/framework/define-command.d.ts.map +1 -1
  161. package/dist/src/commands/framework/define-command.js.map +1 -1
  162. package/dist/src/commands/framework/index.d.ts +1 -1
  163. package/dist/src/commands/framework/index.d.ts.map +1 -1
  164. package/dist/src/commands/framework/index.js.map +1 -1
  165. package/dist/src/commands/index.d.ts +11 -0
  166. package/dist/src/commands/index.d.ts.map +1 -1
  167. package/dist/src/commands/index.js +11 -0
  168. package/dist/src/commands/index.js.map +1 -1
  169. package/dist/src/commands/linux/inject-credentials/inject-credentials.d.ts.map +1 -1
  170. package/dist/src/commands/linux/inject-credentials/inject-credentials.js +1 -3
  171. package/dist/src/commands/linux/inject-credentials/inject-credentials.js.map +1 -1
  172. package/dist/src/commands/linux/setup/setup.d.ts.map +1 -1
  173. package/dist/src/commands/linux/setup/setup.js +1 -3
  174. package/dist/src/commands/linux/setup/setup.js.map +1 -1
  175. package/dist/src/commands/linux/status/status.d.ts +9 -1
  176. package/dist/src/commands/linux/status/status.d.ts.map +1 -1
  177. package/dist/src/commands/linux/status/status.js +66 -86
  178. package/dist/src/commands/linux/status/status.js.map +1 -1
  179. package/dist/src/commands/playtest/player/add/add.d.ts +17 -0
  180. package/dist/src/commands/playtest/player/add/add.d.ts.map +1 -0
  181. package/dist/src/commands/playtest/player/add/add.js +45 -0
  182. package/dist/src/commands/playtest/player/add/add.js.map +1 -0
  183. package/dist/src/commands/playtest/player/list/list.d.ts +13 -0
  184. package/dist/src/commands/playtest/player/list/list.d.ts.map +1 -0
  185. package/dist/src/commands/playtest/player/list/list.js +33 -0
  186. package/dist/src/commands/playtest/player/list/list.js.map +1 -0
  187. package/dist/src/commands/playtest/player/playtest-players.d.ts +36 -0
  188. package/dist/src/commands/playtest/player/playtest-players.d.ts.map +1 -0
  189. package/dist/src/commands/playtest/player/playtest-players.js +187 -0
  190. package/dist/src/commands/playtest/player/playtest-players.js.map +1 -0
  191. package/dist/src/commands/playtest/player/playtest-players.test.d.ts +2 -0
  192. package/dist/src/commands/playtest/player/playtest-players.test.d.ts.map +1 -0
  193. package/dist/src/commands/playtest/player/playtest-players.test.js +89 -0
  194. package/dist/src/commands/playtest/player/playtest-players.test.js.map +1 -0
  195. package/dist/src/commands/playtest/player/remove/remove.d.ts +17 -0
  196. package/dist/src/commands/playtest/player/remove/remove.d.ts.map +1 -0
  197. package/dist/src/commands/playtest/player/remove/remove.js +42 -0
  198. package/dist/src/commands/playtest/player/remove/remove.js.map +1 -0
  199. package/dist/src/commands/playtest/playtest-sessions.d.ts +49 -0
  200. package/dist/src/commands/playtest/playtest-sessions.d.ts.map +1 -0
  201. package/dist/src/commands/playtest/playtest-sessions.js +283 -0
  202. package/dist/src/commands/playtest/playtest-sessions.js.map +1 -0
  203. package/dist/src/commands/playtest/playtest-sessions.test.d.ts +2 -0
  204. package/dist/src/commands/playtest/playtest-sessions.test.d.ts.map +1 -0
  205. package/dist/src/commands/playtest/playtest-sessions.test.js +87 -0
  206. package/dist/src/commands/playtest/playtest-sessions.test.js.map +1 -0
  207. package/dist/src/commands/playtest/restart/restart.d.ts +23 -0
  208. package/dist/src/commands/playtest/restart/restart.d.ts.map +1 -0
  209. package/dist/src/commands/playtest/restart/restart.js +105 -0
  210. package/dist/src/commands/playtest/restart/restart.js.map +1 -0
  211. package/dist/src/commands/playtest/run/run.d.ts +47 -0
  212. package/dist/src/commands/playtest/run/run.d.ts.map +1 -0
  213. package/dist/src/commands/playtest/run/run.js +137 -0
  214. package/dist/src/commands/playtest/run/run.js.map +1 -0
  215. package/dist/src/commands/playtest/start/start.d.ts +42 -0
  216. package/dist/src/commands/playtest/start/start.d.ts.map +1 -0
  217. package/dist/src/commands/playtest/start/start.js +70 -0
  218. package/dist/src/commands/playtest/start/start.js.map +1 -0
  219. package/dist/src/commands/playtest/stop/stop.d.ts +15 -0
  220. package/dist/src/commands/playtest/stop/stop.d.ts.map +1 -0
  221. package/dist/src/commands/playtest/stop/stop.js +24 -0
  222. package/dist/src/commands/playtest/stop/stop.js.map +1 -0
  223. package/dist/src/commands/process/close/close.d.ts +19 -10
  224. package/dist/src/commands/process/close/close.d.ts.map +1 -1
  225. package/dist/src/commands/process/close/close.js +118 -15
  226. package/dist/src/commands/process/close/close.js.map +1 -1
  227. package/dist/src/commands/process/close/close.test.d.ts +2 -0
  228. package/dist/src/commands/process/close/close.test.d.ts.map +1 -0
  229. package/dist/src/commands/process/close/close.test.js +190 -0
  230. package/dist/src/commands/process/close/close.test.js.map +1 -0
  231. package/dist/src/commands/process/info/info.d.ts +45 -5
  232. package/dist/src/commands/process/info/info.d.ts.map +1 -1
  233. package/dist/src/commands/process/info/info.js +117 -13
  234. package/dist/src/commands/process/info/info.js.map +1 -1
  235. package/dist/src/commands/process/info/info.test.js +140 -38
  236. package/dist/src/commands/process/info/info.test.js.map +1 -1
  237. package/dist/src/commands/process/launch/launch.d.ts +11 -1
  238. package/dist/src/commands/process/launch/launch.d.ts.map +1 -1
  239. package/dist/src/commands/process/launch/launch.js +37 -7
  240. package/dist/src/commands/process/launch/launch.js.map +1 -1
  241. package/dist/src/commands/process/launch/launch.test.js +99 -20
  242. package/dist/src/commands/process/launch/launch.test.js.map +1 -1
  243. package/dist/src/commands/process/list/list.d.ts +2 -1
  244. package/dist/src/commands/process/list/list.d.ts.map +1 -1
  245. package/dist/src/commands/process/list/list.js +9 -6
  246. package/dist/src/commands/process/list/list.js.map +1 -1
  247. package/dist/src/commands/process/list/list.test.js +1 -0
  248. package/dist/src/commands/process/list/list.test.js.map +1 -1
  249. package/dist/src/commands/process/run/run.d.ts +9 -0
  250. package/dist/src/commands/process/run/run.d.ts.map +1 -1
  251. package/dist/src/commands/process/run/run.js +15 -5
  252. package/dist/src/commands/process/run/run.js.map +1 -1
  253. package/dist/src/commands/serve/serve.d.ts +2 -0
  254. package/dist/src/commands/serve/serve.d.ts.map +1 -1
  255. package/dist/src/commands/serve/serve.js +17 -4
  256. package/dist/src/commands/serve/serve.js.map +1 -1
  257. package/dist/src/commands/serve/serve.test.js +9 -2
  258. package/dist/src/commands/serve/serve.test.js.map +1 -1
  259. package/dist/src/commands/viewport/screenshot/screenshot.d.ts.map +1 -1
  260. package/dist/src/commands/viewport/screenshot/screenshot.js +4 -1
  261. package/dist/src/commands/viewport/screenshot/screenshot.js.map +1 -1
  262. package/dist/src/commands/viewport/screenshot/screenshot.test.js +5 -9
  263. package/dist/src/commands/viewport/screenshot/screenshot.test.js.map +1 -1
  264. package/dist/src/deploy/deploy-places.d.ts +39 -0
  265. package/dist/src/deploy/deploy-places.d.ts.map +1 -0
  266. package/dist/src/deploy/deploy-places.js +162 -0
  267. package/dist/src/deploy/deploy-places.js.map +1 -0
  268. package/dist/src/deploy/deploy-places.test.d.ts +2 -0
  269. package/dist/src/deploy/deploy-places.test.d.ts.map +1 -0
  270. package/dist/src/deploy/deploy-places.test.js +96 -0
  271. package/dist/src/deploy/deploy-places.test.js.map +1 -0
  272. package/dist/src/index.d.ts +2 -2
  273. package/dist/src/index.d.ts.map +1 -1
  274. package/dist/src/index.js +1 -1
  275. package/dist/src/index.js.map +1 -1
  276. package/dist/src/plugin/persistent-plugin-installer.d.ts.map +1 -1
  277. package/dist/src/plugin/persistent-plugin-installer.js +2 -1
  278. package/dist/src/plugin/persistent-plugin-installer.js.map +1 -1
  279. package/dist/src/plugin/persistent-plugin-installer.test.js +3 -0
  280. package/dist/src/plugin/persistent-plugin-installer.test.js.map +1 -1
  281. package/dist/src/plugin/plugin-build-version.d.ts +7 -0
  282. package/dist/src/plugin/plugin-build-version.d.ts.map +1 -0
  283. package/dist/src/plugin/plugin-build-version.js +42 -0
  284. package/dist/src/plugin/plugin-build-version.js.map +1 -0
  285. package/dist/src/plugin/plugin-build-version.test.d.ts +2 -0
  286. package/dist/src/plugin/plugin-build-version.test.d.ts.map +1 -0
  287. package/dist/src/plugin/plugin-build-version.test.js +41 -0
  288. package/dist/src/plugin/plugin-build-version.test.js.map +1 -0
  289. package/dist/src/plugin/plugin-injector.d.ts.map +1 -1
  290. package/dist/src/plugin/plugin-injector.js +7 -1
  291. package/dist/src/plugin/plugin-injector.js.map +1 -1
  292. package/dist/src/process/studio-process-closer.d.ts +35 -0
  293. package/dist/src/process/studio-process-closer.d.ts.map +1 -0
  294. package/dist/src/process/studio-process-closer.js +129 -0
  295. package/dist/src/process/studio-process-closer.js.map +1 -0
  296. package/dist/src/process/studio-process-closer.test.d.ts +2 -0
  297. package/dist/src/process/studio-process-closer.test.d.ts.map +1 -0
  298. package/dist/src/process/studio-process-closer.test.js +80 -0
  299. package/dist/src/process/studio-process-closer.test.js.map +1 -0
  300. package/dist/src/process/studio-settings.d.ts +14 -0
  301. package/dist/src/process/studio-settings.d.ts.map +1 -0
  302. package/dist/src/process/studio-settings.js +50 -0
  303. package/dist/src/process/studio-settings.js.map +1 -0
  304. package/dist/src/process/studio-settings.test.d.ts +2 -0
  305. package/dist/src/process/studio-settings.test.d.ts.map +1 -0
  306. package/dist/src/process/studio-settings.test.js +16 -0
  307. package/dist/src/process/studio-settings.test.js.map +1 -0
  308. package/dist/src/process/studio-window-state.d.ts +13 -0
  309. package/dist/src/process/studio-window-state.d.ts.map +1 -0
  310. package/dist/src/process/studio-window-state.js +48 -0
  311. package/dist/src/process/studio-window-state.js.map +1 -0
  312. package/dist/src/server/studio-bridge-server.d.ts +3 -1
  313. package/dist/src/server/studio-bridge-server.d.ts.map +1 -1
  314. package/dist/src/server/studio-bridge-server.js +12 -4
  315. package/dist/src/server/studio-bridge-server.js.map +1 -1
  316. package/dist/src/server/web-socket-protocol.d.ts +173 -6
  317. package/dist/src/server/web-socket-protocol.d.ts.map +1 -1
  318. package/dist/src/server/web-socket-protocol.js +244 -1
  319. package/dist/src/server/web-socket-protocol.js.map +1 -1
  320. package/dist/src/server/web-socket-protocol.test.js +48 -0
  321. package/dist/src/server/web-socket-protocol.test.js.map +1 -1
  322. package/dist/src/test/e2e/persistent-session.test.js +121 -0
  323. package/dist/src/test/e2e/persistent-session.test.js.map +1 -1
  324. package/dist/src/test/e2e/split-server.test.d.ts +3 -5
  325. package/dist/src/test/e2e/split-server.test.d.ts.map +1 -1
  326. package/dist/src/test/e2e/split-server.test.js +3 -5
  327. package/dist/src/test/e2e/split-server.test.js.map +1 -1
  328. package/dist/tsconfig.tsbuildinfo +1 -1
  329. package/docker/Dockerfile +3 -0
  330. package/package.json +8 -6
  331. package/src/bridge/bridge-connection.test.ts +126 -0
  332. package/src/bridge/bridge-connection.ts +217 -22
  333. package/src/bridge/bridge-session.test.ts +6 -4
  334. package/src/bridge/bridge-session.ts +273 -28
  335. package/src/bridge/index.ts +3 -0
  336. package/src/bridge/internal/bridge-client.test.ts +23 -0
  337. package/src/bridge/internal/bridge-client.ts +25 -7
  338. package/src/bridge/internal/bridge-host.test.ts +38 -0
  339. package/src/bridge/internal/bridge-host.ts +47 -0
  340. package/src/bridge/internal/hand-off.ts +4 -1
  341. package/src/bridge/internal/host-protocol.test.ts +15 -0
  342. package/src/bridge/internal/host-protocol.ts +148 -37
  343. package/src/bridge/internal/instance-state.ts +34 -0
  344. package/src/bridge/internal/transport-client.ts +2 -1
  345. package/src/bridge/internal/transport-server.ts +5 -1
  346. package/src/bridge/types.ts +47 -2
  347. package/src/cli/adapters/cli-command-adapter-handler.test.ts +28 -0
  348. package/src/cli/adapters/cli-command-adapter.ts +150 -18
  349. package/src/cli/adapters/group-builder.test.ts +34 -2
  350. package/src/cli/adapters/group-builder.ts +52 -20
  351. package/src/cli/adapters/help-formatter.test.ts +1 -1
  352. package/src/cli/adapters/help-formatter.ts +4 -3
  353. package/src/cli/adapters/playtest-edit-guard.test.ts +54 -0
  354. package/src/cli/args/global-args.ts +1 -1
  355. package/src/cli/cli-output-context.test.ts +55 -0
  356. package/src/cli/cli-output-context.ts +50 -0
  357. package/src/cli/cli.ts +49 -17
  358. package/src/cli/managed-studio-run.test.ts +71 -0
  359. package/src/cli/managed-studio-run.ts +135 -0
  360. package/src/cli/resolve-session.test.ts +1 -1
  361. package/src/cli/resolve-session.ts +1 -1
  362. package/src/cli/script-executor.ts +99 -51
  363. package/src/commands/console/exec/exec.test.ts +96 -3
  364. package/src/commands/console/exec/exec.ts +59 -6
  365. package/src/commands/console/exec/execute.lua +356 -240
  366. package/src/commands/console/exec/execute.spec.lua +286 -0
  367. package/src/commands/console/logs/logs.test.ts +76 -4
  368. package/src/commands/console/logs/logs.ts +132 -17
  369. package/src/commands/console/logs/query-logs.lua +85 -52
  370. package/src/commands/console/logs/query-logs.spec.lua +132 -0
  371. package/src/commands/explorer/export/export-instances.lua +108 -0
  372. package/src/commands/explorer/export/export-instances.spec.lua +87 -0
  373. package/src/commands/explorer/export/export.test.ts +27 -0
  374. package/src/commands/explorer/export/export.ts +60 -0
  375. package/src/commands/explorer/find/find-instances.lua +174 -0
  376. package/src/commands/explorer/find/find-instances.spec.lua +122 -0
  377. package/src/commands/explorer/find/find.test.ts +53 -0
  378. package/src/commands/explorer/find/find.ts +113 -0
  379. package/src/commands/explorer/grep/grep-scripts.lua +214 -0
  380. package/src/commands/explorer/grep/grep-scripts.spec.lua +130 -0
  381. package/src/commands/explorer/grep/grep.test.ts +45 -0
  382. package/src/commands/explorer/grep/grep.ts +93 -0
  383. package/src/commands/explorer/import/import-instances.lua +106 -0
  384. package/src/commands/explorer/import/import-instances.spec.lua +84 -0
  385. package/src/commands/explorer/import/import.test.ts +60 -0
  386. package/src/commands/explorer/import/import.ts +66 -0
  387. package/src/commands/explorer/query/query-data-model.lua +124 -152
  388. package/src/commands/explorer/query/query-data-model.spec.lua +150 -0
  389. package/src/commands/formatters.test.ts +7 -1
  390. package/src/commands/framework/action-loader.test.ts +28 -0
  391. package/src/commands/framework/action-loader.ts +10 -2
  392. package/src/commands/framework/arg-builder.ts +26 -3
  393. package/src/commands/framework/define-command.ts +32 -3
  394. package/src/commands/framework/index.ts +1 -0
  395. package/src/commands/index.ts +50 -0
  396. package/src/commands/linux/inject-credentials/inject-credentials.ts +1 -3
  397. package/src/commands/linux/setup/setup.ts +1 -3
  398. package/src/commands/linux/status/status.ts +105 -98
  399. package/src/commands/playtest/player/add/add.ts +85 -0
  400. package/src/commands/playtest/player/list/list.ts +57 -0
  401. package/src/commands/playtest/player/playtest-players.test.ts +146 -0
  402. package/src/commands/playtest/player/playtest-players.ts +270 -0
  403. package/src/commands/playtest/player/remove/remove.ts +78 -0
  404. package/src/commands/playtest/playtest-sessions.test.ts +143 -0
  405. package/src/commands/playtest/playtest-sessions.ts +417 -0
  406. package/src/commands/playtest/restart/restart.ts +199 -0
  407. package/src/commands/playtest/run/playtest-runner.luau +264 -0
  408. package/src/commands/playtest/run/playtest-runner.spec.lua +71 -0
  409. package/src/commands/playtest/run/run-playtest-script.lua +139 -0
  410. package/src/commands/playtest/run/run-playtest-script.spec.lua +43 -0
  411. package/src/commands/playtest/run/run.ts +239 -0
  412. package/src/commands/playtest/start/start-playtest.lua +108 -0
  413. package/src/commands/playtest/start/start.ts +138 -0
  414. package/src/commands/playtest/stop/end-playtest.lua +47 -0
  415. package/src/commands/playtest/stop/stop.ts +51 -0
  416. package/src/commands/process/close/close.test.ts +250 -0
  417. package/src/commands/process/close/close.ts +183 -17
  418. package/src/commands/process/info/info.test.ts +198 -44
  419. package/src/commands/process/info/info.ts +204 -15
  420. package/src/commands/process/info/query-state.lua +42 -29
  421. package/src/commands/process/info/query-state.spec.lua +39 -0
  422. package/src/commands/process/launch/launch.test.ts +138 -21
  423. package/src/commands/process/launch/launch.ts +69 -9
  424. package/src/commands/process/list/list.test.ts +1 -0
  425. package/src/commands/process/list/list.ts +14 -6
  426. package/src/commands/process/run/run.ts +26 -5
  427. package/src/commands/serve/serve.test.ts +17 -2
  428. package/src/commands/serve/serve.ts +25 -6
  429. package/src/commands/viewport/screenshot/capture-screenshot.lua +175 -182
  430. package/src/commands/viewport/screenshot/capture-screenshot.spec.lua +129 -0
  431. package/src/commands/viewport/screenshot/screenshot.test.ts +7 -10
  432. package/src/commands/viewport/screenshot/screenshot.ts +6 -1
  433. package/src/deploy/deploy-places.test.ts +127 -0
  434. package/src/deploy/deploy-places.ts +229 -0
  435. package/src/index.ts +11 -0
  436. package/src/plugin/persistent-plugin-installer.test.ts +4 -0
  437. package/src/plugin/persistent-plugin-installer.ts +2 -1
  438. package/src/plugin/plugin-build-version.test.ts +55 -0
  439. package/src/plugin/plugin-build-version.ts +53 -0
  440. package/src/plugin/plugin-injector.ts +7 -1
  441. package/src/process/studio-process-closer.test.ts +107 -0
  442. package/src/process/studio-process-closer.ts +187 -0
  443. package/src/process/studio-settings.test.ts +18 -0
  444. package/src/process/studio-settings.ts +61 -0
  445. package/src/process/studio-window-state.ts +65 -0
  446. package/src/server/studio-bridge-server.ts +15 -4
  447. package/src/server/web-socket-protocol.test.ts +55 -0
  448. package/src/server/web-socket-protocol.ts +468 -8
  449. package/src/test/e2e/persistent-session.test.ts +137 -0
  450. package/src/test/e2e/split-server.test.ts +3 -5
  451. package/templates/studio-bridge-plugin/default.project.json +3 -0
  452. package/templates/studio-bridge-plugin/src/Shared/ActionRouter.lua +208 -159
  453. package/templates/studio-bridge-plugin/src/Shared/ActionRouter.spec.lua +418 -0
  454. package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.lua +94 -0
  455. package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.spec.lua +166 -0
  456. package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.lua +118 -137
  457. package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.spec.lua +228 -0
  458. package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.lua +80 -109
  459. package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.spec.lua +116 -0
  460. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.lua +233 -0
  461. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.spec.lua +239 -0
  462. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.lua +53 -0
  463. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.spec.lua +27 -0
  464. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.lua +164 -0
  465. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.spec.lua +159 -0
  466. package/templates/studio-bridge-plugin/src/StudioBridgePlugin.server.lua +266 -368
  467. package/templates/studio-bridge-plugin/src/Vendor/png.lua +8 -2
  468. package/test/default.project.json +41 -0
  469. package/test/jest.config.lua +3 -0
  470. package/test/scripts/Server/ServerMain.server.lua +10 -0
  471. package/tsconfig.json +1 -0
  472. package/templates/studio-bridge-plugin/src/Shared/Protocol.lua +0 -132
  473. package/templates/studio-bridge-plugin-test/test/actions.test.lua +0 -580
  474. package/templates/studio-bridge-plugin-test/test/discovery.test.lua +0 -622
  475. package/templates/studio-bridge-plugin-test/test/execute-handler.test.lua +0 -517
  476. package/templates/studio-bridge-plugin-test/test/hash-lifecycle.test.lua +0 -814
  477. package/templates/studio-bridge-plugin-test/test/integration.test.lua +0 -1020
  478. package/templates/studio-bridge-plugin-test/test/new-actions.test.lua +0 -339
  479. package/templates/studio-bridge-plugin-test/test/plugin-entry.test.lua +0 -493
  480. package/templates/studio-bridge-plugin-test/test/protocol.test.lua +0 -506
  481. package/templates/studio-bridge-plugin-test/test/roblox-mocks.lua +0 -108
  482. package/templates/studio-bridge-plugin-test/test/test-runner.lua +0 -112
@@ -3,74 +3,228 @@
3
3
  */
4
4
 
5
5
  import { describe, it, expect, vi } from 'vitest';
6
- import { queryStateHandlerAsync } from './info.js';
7
-
8
- function createMockSession(stateResult: {
9
- state: string;
10
- placeId: number;
11
- placeName: string;
12
- gameId: number;
13
- }) {
6
+
7
+ vi.mock('../../../process/studio-process-closer.js', async (importOriginal) => {
8
+ const actual = await importOriginal<
9
+ typeof import('../../../process/studio-process-closer.js')
10
+ >();
11
+ return {
12
+ ...actual,
13
+ listStudioProcessesAsync: vi.fn(async () => [
14
+ { pid: 42, windowTitle: 'C:/places/TestPlace - Roblox Studio' },
15
+ ]),
16
+ };
17
+ });
18
+
19
+ vi.mock('../../../process/studio-window-state.js', () => ({
20
+ tryGetStudioWindowStateAsync: vi.fn(async () => ({
21
+ focused: true,
22
+ minimized: false,
23
+ })),
24
+ }));
25
+
26
+ vi.mock('../../../plugin/plugin-build-version.js', () => ({
27
+ getPluginBuildVersionAsync: async () => 'abc123',
28
+ }));
29
+
30
+ import {
31
+ describePlaytest,
32
+ formatStateText,
33
+ queryStateHandlerAsync,
34
+ } from './info.js';
35
+
36
+ function sessionInfo(
37
+ sessionId: string,
38
+ context: 'edit' | 'server' | 'client',
39
+ overrides: Record<string, unknown> = {}
40
+ ) {
14
41
  return {
15
- queryStateAsync: vi.fn().mockResolvedValue(stateResult),
42
+ sessionId,
43
+ instanceId: 'test-place-3f9a',
44
+ context,
45
+ origin: 'user',
46
+ placeName: 'TestPlace',
47
+ pluginVersion: 'abc123',
48
+ connectedAt: new Date('2026-10-27T18:00:00Z'),
49
+ ...overrides,
16
50
  } as any;
17
51
  }
18
52
 
19
- describe('queryStateHandlerAsync', () => {
20
- it('returns state result with summary', async () => {
21
- const session = createMockSession({
53
+ function createMockSession(
54
+ stateResult: Record<string, unknown>,
55
+ infoOverrides: Record<string, unknown> = {}
56
+ ) {
57
+ return {
58
+ info: sessionInfo('test-place-3f9a-edit', 'edit', infoOverrides),
59
+ queryStateAsync: vi.fn().mockResolvedValue({
22
60
  state: 'Edit',
23
61
  placeId: 12345,
24
62
  placeName: 'TestPlace',
25
63
  gameId: 67890,
26
- });
64
+ ...stateResult,
65
+ }),
66
+ } as any;
67
+ }
68
+
69
+ function createConnection(sessions: any[]) {
70
+ return { listSessions: () => sessions } as any;
71
+ }
72
+
73
+ describe('queryStateHandlerAsync', () => {
74
+ it('reports the session, place and run mode', async () => {
75
+ const session = createMockSession({ studioVersion: '0.744.0.7440803' });
27
76
 
28
77
  const result = await queryStateHandlerAsync(session);
29
78
 
30
- expect(result.state).toBe('Edit');
31
- expect(result.placeId).toBe(12345);
32
- expect(result.placeName).toBe('TestPlace');
33
- expect(result.gameId).toBe(67890);
34
- expect(result.summary).toContain('Edit');
35
- expect(result.summary).toContain('TestPlace');
36
- expect(result.summary).toContain('12345');
79
+ expect(result).toMatchObject({
80
+ sessionId: 'test-place-3f9a-edit',
81
+ instanceId: 'test-place-3f9a',
82
+ context: 'edit',
83
+ origin: 'user',
84
+ state: 'Edit',
85
+ placeId: 12345,
86
+ placeName: 'TestPlace',
87
+ gameId: 67890,
88
+ studioVersion: '0.744.0.7440803',
89
+ connectedAt: '2026-10-27T18:00:00.000Z',
90
+ });
91
+ expect(result.summary).toBe('Mode: Edit, Place: TestPlace (12345)');
37
92
  });
38
93
 
39
- it('calls session.queryStateAsync', async () => {
40
- const session = createMockSession({
41
- state: 'Play',
42
- placeId: 100,
43
- placeName: 'GamePlace',
44
- gameId: 200,
94
+ it('compares the plugin version with the one the CLI installs', async () => {
95
+ const current = await queryStateHandlerAsync(createMockSession({}));
96
+ const stale = await queryStateHandlerAsync(
97
+ createMockSession({}, { pluginVersion: 'old999' })
98
+ );
99
+
100
+ expect(current.plugin).toEqual({
101
+ version: 'abc123',
102
+ expectedVersion: 'abc123',
103
+ upToDate: true,
45
104
  });
105
+ expect(stale.plugin.upToDate).toBe(false);
106
+ expect(formatStateText(stale)).toContain(
107
+ "out of date, expected abc123; run 'studio-bridge plugin install'"
108
+ );
109
+ });
46
110
 
47
- await queryStateHandlerAsync(session);
111
+ it("reports the playtest running in the session's Studio", async () => {
112
+ const session = createMockSession({});
113
+ const connection = createConnection([
114
+ session.info,
115
+ sessionInfo('test-place-3f9a-server', 'server'),
116
+ sessionInfo('test-place-3f9a-client', 'client', {
117
+ playerName: 'Player1',
118
+ }),
119
+ ]);
48
120
 
49
- expect(session.queryStateAsync).toHaveBeenCalledOnce();
121
+ const result = await queryStateHandlerAsync(session, connection);
122
+
123
+ expect(result.playtest).toEqual({
124
+ mode: 'play',
125
+ playerCount: undefined,
126
+ sessions: [
127
+ {
128
+ sessionId: 'test-place-3f9a-server',
129
+ context: 'server',
130
+ playerName: undefined,
131
+ },
132
+ {
133
+ sessionId: 'test-place-3f9a-client',
134
+ context: 'client',
135
+ playerName: 'Player1',
136
+ },
137
+ ],
138
+ });
139
+ expect(formatStateText(result)).toContain(
140
+ 'Playtest: running (play mode): test-place-3f9a-server, test-place-3f9a-client'
141
+ );
50
142
  });
51
143
 
52
- it('handles different state values correctly', async () => {
53
- for (const state of ['Edit', 'Play', 'Paused', 'Run', 'Server', 'Client']) {
54
- const session = createMockSession({
55
- state,
56
- placeId: 1,
57
- placeName: 'Place',
58
- gameId: 2,
59
- });
60
-
61
- const result = await queryStateHandlerAsync(session);
62
- expect(result.state).toBe(state);
63
- expect(result.summary).toContain(state);
64
- }
144
+ it("finds a playtest's window by its DataModel's name, not the edit window", async () => {
145
+ const session = createMockSession(
146
+ { state: 'Server', placeName: 'TestPlace' },
147
+ { sessionId: 'test-place-3f9a-server', context: 'server' }
148
+ );
149
+ const connection = createConnection([
150
+ sessionInfo('test-place-3f9a-edit', 'edit'),
151
+ session.info,
152
+ sessionInfo('test-place-3f9a-client-player1', 'client'),
153
+ sessionInfo('test-place-3f9a-client-player2', 'client'),
154
+ ]);
155
+
156
+ const result = await queryStateHandlerAsync(session, connection);
157
+
158
+ expect(result.window?.pid).toBe(42);
159
+
160
+ session.queryStateAsync.mockResolvedValue({
161
+ state: 'Server',
162
+ placeId: 0,
163
+ placeName: 'Server',
164
+ gameId: 0,
165
+ });
166
+ const unmatched = await queryStateHandlerAsync(session, connection);
167
+ expect(unmatched.window).toBeUndefined();
65
168
  });
66
169
 
67
170
  it('propagates errors from session', async () => {
68
- const session = {
69
- queryStateAsync: vi.fn().mockRejectedValue(new Error('Connection lost')),
70
- } as any;
171
+ const session = createMockSession({});
172
+ session.queryStateAsync.mockRejectedValue(new Error('Connection lost'));
71
173
 
72
174
  await expect(queryStateHandlerAsync(session)).rejects.toThrow(
73
175
  'Connection lost'
74
176
  );
75
177
  });
178
+
179
+ it('reports the window showing the place', async () => {
180
+ const result = await queryStateHandlerAsync(createMockSession({}));
181
+
182
+ expect(result.window).toEqual({ pid: 42, focused: true, minimized: false });
183
+ expect(formatStateText(result)).toContain(
184
+ 'Window: pid 42, open, focused'
185
+ );
186
+ });
187
+
188
+ it('leaves the window out when no window shows the place', async () => {
189
+ const result = await queryStateHandlerAsync(
190
+ createMockSession({ placeName: 'Elsewhere' })
191
+ );
192
+
193
+ expect(result.window).toBeUndefined();
194
+ });
195
+ });
196
+
197
+ describe('describePlaytest', () => {
198
+ it('is absent without playtest sessions', () => {
199
+ expect(
200
+ describePlaytest(
201
+ [sessionInfo('test-place-3f9a-edit', 'edit')],
202
+ 'test-place-3f9a'
203
+ )
204
+ ).toBeUndefined();
205
+ });
206
+
207
+ it('reports run mode when only the server connected', () => {
208
+ expect(
209
+ describePlaytest(
210
+ [sessionInfo('test-place-3f9a-server', 'server')],
211
+ 'test-place-3f9a'
212
+ )?.mode
213
+ ).toBe('run');
214
+ });
215
+
216
+ it('counts the players of a multiplayer test', () => {
217
+ const playtest = describePlaytest(
218
+ [
219
+ sessionInfo('test-place-3f9a-server', 'server'),
220
+ sessionInfo('test-place-3f9a-client-player1', 'client'),
221
+ sessionInfo('test-place-3f9a-client-player2', 'client'),
222
+ sessionInfo('other-1a2b-server', 'server', { instanceId: 'other' }),
223
+ ],
224
+ 'test-place-3f9a'
225
+ );
226
+
227
+ expect(playtest?.playerCount).toBe(2);
228
+ expect(playtest?.sessions).toHaveLength(3);
229
+ });
76
230
  });
@@ -1,48 +1,236 @@
1
1
  /**
2
- * `process info` — query the current Studio state (mode, place info)
3
- * from a connected session.
2
+ * `process info` — report on one Studio session: its place and run mode, the
3
+ * playtest running in its Studio, its window, and whether its plugin is
4
+ * current.
4
5
  */
5
6
 
7
+ import { OutputHelper } from '@quenty/cli-output-helpers';
6
8
  import { defineCommand } from '../../framework/define-command.js';
7
- import type { BridgeSession } from '../../../bridge/index.js';
8
- import type { StudioState } from '../../../bridge/index.js';
9
+ import type {
10
+ BridgeConnection,
11
+ BridgeSession,
12
+ SessionContext,
13
+ SessionInfo,
14
+ SessionOrigin,
15
+ StudioState,
16
+ } from '../../../bridge/index.js';
9
17
  import { colorizeState } from '../format-state.js';
18
+ import {
19
+ listStudioProcessesAsync,
20
+ matchStudioProcesses,
21
+ } from '../../../process/studio-process-closer.js';
22
+ import { tryGetStudioWindowStateAsync } from '../../../process/studio-window-state.js';
23
+ import { getPluginBuildVersionAsync } from '../../../plugin/plugin-build-version.js';
24
+
25
+ export interface PlaytestInfo {
26
+ mode: 'play' | 'run';
27
+ /** Set for a multiplayer test: how many of its clients are connected. */
28
+ playerCount?: number;
29
+ sessions: Array<{
30
+ sessionId: string;
31
+ context: SessionContext;
32
+ playerName?: string;
33
+ }>;
34
+ }
10
35
 
11
36
  export interface StateResult {
37
+ sessionId: string;
38
+ instanceId: string;
39
+ context: SessionContext;
40
+ origin: SessionOrigin;
41
+ /** The run mode of this session's own DataModel. */
12
42
  state: StudioState;
13
43
  placeId: number;
14
44
  placeName: string;
15
45
  gameId: number;
46
+ /** The camera viewport in pixels. Absent without a camera. */
47
+ viewportSize?: [number, number];
48
+ /** Absent from older plugins. */
49
+ studioVersion?: string;
50
+ plugin: {
51
+ version: string;
52
+ /** The version `studio-bridge plugin install` would install. */
53
+ expectedVersion: string;
54
+ upToDate: boolean;
55
+ };
56
+ /**
57
+ * The playtest running in this session's Studio, as seen through the
58
+ * sessions it opened. Absent when none is running or its sessions are not
59
+ * connected.
60
+ */
61
+ playtest?: PlaytestInfo;
62
+ /** The Studio window showing the place. Absent when it cannot be told apart. */
63
+ window?: {
64
+ pid: number;
65
+ focused: boolean;
66
+ minimized: boolean;
67
+ };
68
+ connectedAt: string;
16
69
  summary: string;
17
70
  }
18
71
 
19
72
  export async function queryStateHandlerAsync(
20
- session: BridgeSession
73
+ session: BridgeSession,
74
+ connection?: BridgeConnection
21
75
  ): Promise<StateResult> {
22
- const result = await session.queryStateAsync();
76
+ // Listed sessions carry the edit session's place name for a playtest
77
+ const listed = connection
78
+ ?.listSessions()
79
+ .find((s) => s.sessionId === session.info.sessionId);
80
+ const playtest = connection
81
+ ? describePlaytest(connection.listSessions(), session.info.instanceId)
82
+ : undefined;
83
+ // Each process of a multiplayer test has its own window; otherwise a
84
+ // playtest shows in the edit window
85
+ const ownWindow =
86
+ session.info.context !== 'edit' && playtest?.playerCount !== undefined;
87
+ const [result, expectedVersion] = await Promise.all([
88
+ session.queryStateAsync(),
89
+ getPluginBuildVersionAsync(),
90
+ ]);
91
+ const window = await tryGetWindowAsync(
92
+ ownWindow ? [result.placeName] : [result.placeName, listed?.placeName],
93
+ ownWindow ? undefined : result.placeId
94
+ );
23
95
 
24
96
  return {
97
+ sessionId: session.info.sessionId,
98
+ instanceId: session.info.instanceId,
99
+ context: session.info.context,
100
+ origin: session.info.origin,
25
101
  state: result.state,
26
102
  placeId: result.placeId,
27
- placeName: result.placeName,
103
+ placeName: listed?.placeName || result.placeName,
28
104
  gameId: result.gameId,
105
+ viewportSize: result.viewportSize,
106
+ studioVersion: result.studioVersion,
107
+ plugin: {
108
+ version: session.info.pluginVersion,
109
+ expectedVersion,
110
+ upToDate: session.info.pluginVersion === expectedVersion,
111
+ },
112
+ playtest,
113
+ window,
114
+ connectedAt: new Date(session.info.connectedAt).toISOString(),
29
115
  summary: `Mode: ${result.state}, Place: ${result.placeName} (${result.placeId})`,
30
116
  };
31
117
  }
32
118
 
119
+ /** The playtest of `instanceId`, from the sessions it opened. */
120
+ export function describePlaytest(
121
+ sessions: SessionInfo[],
122
+ instanceId: string
123
+ ): PlaytestInfo | undefined {
124
+ const playtestSessions = sessions.filter(
125
+ (s) => s.instanceId === instanceId && s.context !== 'edit'
126
+ );
127
+ if (playtestSessions.length === 0) return undefined;
128
+
129
+ const clients = playtestSessions.filter((s) => s.context === 'client');
130
+ const isMultiplayer = clients.some(
131
+ (s) => s.sessionId !== `${instanceId}-client`
132
+ );
133
+
134
+ return {
135
+ mode: clients.length > 0 ? 'play' : 'run',
136
+ playerCount: isMultiplayer ? clients.length : undefined,
137
+ sessions: playtestSessions.map((s) => ({
138
+ sessionId: s.sessionId,
139
+ context: s.context,
140
+ playerName: s.playerName,
141
+ })),
142
+ };
143
+ }
144
+
145
+ /**
146
+ * The Studio window titled with the first of `placeNames` that any window
147
+ * has, when exactly one does. A multiplayer test's processes title their
148
+ * windows with their own DataModel's name, and its clients all share one;
149
+ * a single-process playtest shares the edit window.
150
+ */
151
+ async function tryGetWindowAsync(
152
+ placeNames: Array<string | undefined>,
153
+ placeId?: number
154
+ ): Promise<StateResult['window']> {
155
+ const processes = await listStudioProcessesAsync().catch(() => []);
156
+ for (const placeName of placeNames) {
157
+ if (!placeName) continue;
158
+ const matches = matchStudioProcesses(processes, placeName, placeId);
159
+ if (matches.length === 0) continue;
160
+ if (matches.length > 1) return undefined;
161
+
162
+ const pid = matches[0].pid;
163
+ const state = await tryGetStudioWindowStateAsync(pid);
164
+ return state ? { pid, ...state } : undefined;
165
+ }
166
+ return undefined;
167
+ }
168
+
169
+ function formatAge(isoTime: string): string {
170
+ const seconds = Math.max(
171
+ 0,
172
+ Math.round((Date.now() - Date.parse(isoTime)) / 1000)
173
+ );
174
+ if (seconds < 60) return `${seconds}s ago`;
175
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
176
+ return `${Math.floor(seconds / 3600)}h ${Math.floor(
177
+ (seconds % 3600) / 60
178
+ )}m ago`;
179
+ }
180
+
181
+ function formatPlaytest(playtest: PlaytestInfo | undefined): string {
182
+ if (!playtest) return 'none running';
183
+ const kind =
184
+ playtest.playerCount !== undefined
185
+ ? `multiplayer, ${playtest.playerCount} player${
186
+ playtest.playerCount === 1 ? '' : 's'
187
+ }`
188
+ : `${playtest.mode} mode`;
189
+ return `running (${kind}): ${playtest.sessions
190
+ .map((s) => s.sessionId)
191
+ .join(', ')}`;
192
+ }
193
+
33
194
  export function formatStateText(result: StateResult): string {
34
- return [
35
- `Mode: ${colorizeState(result.state)}`,
36
- `Place: ${result.placeName}`,
37
- `PlaceId: ${result.placeId}`,
38
- `GameId: ${result.gameId}`,
39
- ].join('\n');
195
+ const lines = [
196
+ `Session: ${OutputHelper.formatHint(result.sessionId)} (${
197
+ result.context
198
+ }, ${result.origin}, connected ${formatAge(result.connectedAt)})`,
199
+ `Mode: ${colorizeState(result.state)}`,
200
+ `Playtest: ${formatPlaytest(result.playtest)}`,
201
+ `Place: ${result.placeName} (PlaceId ${result.placeId}, GameId ${result.gameId})`,
202
+ ];
203
+ if (result.viewportSize) {
204
+ lines.push(`Viewport: ${result.viewportSize[0]}x${result.viewportSize[1]}`);
205
+ }
206
+ if (result.window) {
207
+ const { pid, focused, minimized } = result.window;
208
+ lines.push(
209
+ `Window: pid ${pid}, ${minimized ? 'minimized' : 'open'}, ${
210
+ focused ? 'focused' : 'not focused'
211
+ }`
212
+ );
213
+ }
214
+ if (result.studioVersion) {
215
+ lines.push(`Studio: ${result.studioVersion}`);
216
+ }
217
+ lines.push(
218
+ `Plugin: ${result.plugin.version || 'unknown'} ${
219
+ result.plugin.upToDate
220
+ ? '(up to date)'
221
+ : OutputHelper.formatWarning(
222
+ `(out of date, expected ${result.plugin.expectedVersion}; run 'studio-bridge plugin install')`
223
+ )
224
+ }`
225
+ );
226
+ return lines.join('\n');
40
227
  }
41
228
 
42
229
  export const infoCommand = defineCommand({
43
230
  group: 'process',
44
231
  name: 'info',
45
- description: 'Query the current Studio state (mode, place info)',
232
+ description:
233
+ 'Report on a Studio session: mode, playtest, place, window and plugin version',
46
234
  category: 'execution',
47
235
  safety: 'read',
48
236
  scope: 'session',
@@ -50,5 +238,6 @@ export const infoCommand = defineCommand({
50
238
  cli: {
51
239
  format: formatStateText,
52
240
  },
53
- handler: async (session) => queryStateHandlerAsync(session),
241
+ handler: async (session, _args, connection) =>
242
+ queryStateHandlerAsync(session, connection),
54
243
  });
@@ -1,48 +1,61 @@
1
- --[[
2
- QueryState action handler for the studio-bridge plugin.
1
+ --!strict
2
+ --[=[
3
+ Action that answers `queryState` with Studio's run mode, the open place and
4
+ the Studio version.
3
5
 
4
- Returns the current Studio run mode, place name, place ID, and game ID.
6
+ Request: `{ type: "queryState", payload: {} }`
7
+ Response: `{ type: "stateResult", payload: { state, placeName, placeId, gameId, viewportSize?, studioVersion } }`
5
8
 
6
- Protocol:
7
- Request: { type: "queryState", payload: {} }
8
- Response: { type: "stateResult", payload: { state, placeName, placeId, gameId } }
9
- ]]
9
+ @class QueryStateAction
10
+ ]=]
10
11
 
11
12
  local RunService = game:GetService("RunService")
12
13
 
13
14
  local QueryStateAction = {}
14
15
 
15
- -- ---------------------------------------------------------------------------
16
- -- State detection
17
- -- ---------------------------------------------------------------------------
18
-
19
- local function detectState(): string
20
- if RunService:IsRunning() then
21
- if RunService:IsClient() and not RunService:IsServer() then
22
- return "Client"
23
- elseif RunService:IsServer() and not RunService:IsClient() then
24
- return "Server"
25
- else
26
- return "Play"
27
- end
28
- end
29
- return "Edit"
30
- end
31
-
32
- -- ---------------------------------------------------------------------------
33
- -- Public API
34
- -- ---------------------------------------------------------------------------
35
-
16
+ --[=[
17
+ Registers the `queryState` handler.
18
+ ]=]
36
19
  function QueryStateAction.register(router: any)
37
20
  router:setResponseType("queryState", "stateResult")
38
21
  router:register("queryState", function(_payload: { [string]: any }, _requestId: string, _sessionId: string)
39
22
  return {
40
- state = detectState(),
23
+ state = QueryStateAction._getState(),
41
24
  placeName = game.Name or "Unknown",
42
25
  placeId = game.PlaceId,
43
26
  gameId = game.GameId,
27
+ viewportSize = QueryStateAction._getViewportSize(),
28
+ -- selene: allow(undefined_variable)
29
+ studioVersion = version(),
44
30
  }
45
31
  end)
46
32
  end
47
33
 
34
+ --[[
35
+ The camera's viewport in pixels, or nil when the DataModel has no camera.
36
+ ]]
37
+ function QueryStateAction._getViewportSize(): { number }?
38
+ local camera = workspace.CurrentCamera
39
+ if not camera then
40
+ return nil
41
+ end
42
+ return { camera.ViewportSize.X, camera.ViewportSize.Y }
43
+ end
44
+
45
+ function QueryStateAction._getState(): string
46
+ if not RunService:IsRunning() then
47
+ return "Edit"
48
+ end
49
+
50
+ local isClient = RunService:IsClient()
51
+ local isServer = RunService:IsServer()
52
+ if isClient and not isServer then
53
+ return "Client"
54
+ elseif isServer and not isClient then
55
+ return "Server"
56
+ else
57
+ return "Play"
58
+ end
59
+ end
60
+
48
61
  return QueryStateAction
@@ -0,0 +1,39 @@
1
+ --!strict
2
+ --[[
3
+ @class query-state.spec.lua
4
+ ]]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local ActionRouter = require("ActionRouter")
9
+ local Jest = require("Jest")
10
+ local QueryStateAction = require("query-state")
11
+
12
+ local describe = Jest.Globals.describe
13
+ local expect = Jest.Globals.expect
14
+ local it = Jest.Globals.it
15
+
16
+ describe("queryState", function()
17
+ it("should answer stateResult with the run mode and the open place", function()
18
+ local router = ActionRouter.new()
19
+ QueryStateAction.register(router)
20
+
21
+ local response = router:Dispatch({ type = "queryState", sessionId = "session-1", payload = {} })
22
+ expect(type(response.payload.studioVersion)).toEqual("string")
23
+ response.payload.studioVersion = nil
24
+
25
+ expect(response).toEqual({
26
+ type = "stateResult",
27
+ sessionId = "session-1",
28
+ payload = {
29
+ state = "Edit",
30
+ placeName = game.Name,
31
+ placeId = game.PlaceId,
32
+ gameId = game.GameId,
33
+ viewportSize = if workspace.CurrentCamera
34
+ then { workspace.CurrentCamera.ViewportSize.X, workspace.CurrentCamera.ViewportSize.Y }
35
+ else nil,
36
+ },
37
+ })
38
+ end)
39
+ end)