@quenty/studio-bridge 0.21.1 → 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.
Files changed (482) hide show
  1. package/CHANGELOG.md +6 -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
@@ -0,0 +1,286 @@
1
+ --!strict
2
+ --[[
3
+ @class execute.spec.lua
4
+ ]]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local ActionRouter = require("ActionRouter")
9
+ local ExecuteAction = require("execute")
10
+ local Jest = require("Jest")
11
+
12
+ local describe = Jest.Globals.describe
13
+ local expect = Jest.Globals.expect
14
+ local it = Jest.Globals.it
15
+
16
+ local function runScript(code: string): any
17
+ return ExecuteAction.handleExecute({ script = code }, "req-1", "session-1")
18
+ end
19
+
20
+ local function setup(useSendMessage: boolean): any
21
+ local router = ActionRouter.new()
22
+ local sent = {}
23
+
24
+ ExecuteAction.register(
25
+ router,
26
+ if useSendMessage
27
+ then function(message)
28
+ table.insert(sent, message)
29
+ end
30
+ else nil
31
+ )
32
+
33
+ local controller = {
34
+ router = router,
35
+ sent = sent,
36
+ execute = function(payload: { [string]: any }, requestId: string?)
37
+ return router:Dispatch({
38
+ type = "execute",
39
+ sessionId = "session-1",
40
+ payload = payload,
41
+ requestId = requestId,
42
+ })
43
+ end,
44
+ Destroy = function(_self)
45
+ ExecuteAction._resetQueue()
46
+ end,
47
+ }
48
+
49
+ return controller
50
+ end
51
+
52
+ describe("ExecuteAction.handleExecute()", function()
53
+ it("should report success with no return values", function()
54
+ local result = runScript("local _ = 1")
55
+
56
+ expect(result).toEqual({ success = true, output = {}, returnValues = {} })
57
+ end)
58
+
59
+ it("should capture print and warn output in order", function()
60
+ local result = runScript("print('hello', 1) warn('careful')")
61
+
62
+ expect(#result.output).toEqual(2)
63
+ expect(result.output[1].level).toEqual("Print")
64
+ expect(result.output[1].body).toEqual("hello 1")
65
+ expect(result.output[2].level).toEqual("Warning")
66
+ expect(result.output[2].body).toEqual("careful")
67
+ end)
68
+
69
+ it("should report the compiler's message for code that does not compile", function()
70
+ local result = runScript("local = ")
71
+
72
+ expect(result.success).toEqual(false)
73
+ expect(result.code).toEqual("SCRIPT_LOAD_ERROR")
74
+ expect(result.error).never.toEqual("Failed to compile script")
75
+ end)
76
+
77
+ it("should report a runtime error along with the output before it", function()
78
+ local result = runScript("print('before') error('boom')")
79
+
80
+ expect(result.success).toEqual(false)
81
+ expect(result.code).toEqual("SCRIPT_RUNTIME_ERROR")
82
+ expect(result.error).toContain("boom")
83
+ expect(result.output[1].body).toEqual("before")
84
+ expect(result.returnValues).toBeNil()
85
+ end)
86
+
87
+ it("should reject a request without code", function()
88
+ local result = ExecuteAction.handleExecute({}, "req-1", "session-1")
89
+
90
+ expect(result).toEqual({
91
+ success = false,
92
+ error = "Missing code in execute request",
93
+ code = "SCRIPT_LOAD_ERROR",
94
+ })
95
+ end)
96
+
97
+ it("should reject code that is not a string", function()
98
+ local result = ExecuteAction.handleExecute({ script = 5 }, "req-1", "session-1")
99
+
100
+ expect(result and result.code).toEqual("SCRIPT_LOAD_ERROR")
101
+ end)
102
+
103
+ it("should accept code under payload.code and prefer payload.script", function()
104
+ local fromCode = ExecuteAction.handleExecute({ code = "return 'code'" }, "req-1", "session-1")
105
+ local fromBoth =
106
+ ExecuteAction.handleExecute({ script = "return 'script'", code = "return 'code'" }, "req-1", "session-1")
107
+
108
+ expect(fromCode and fromCode.returnValues).toEqual({ "code" })
109
+ expect(fromBoth and fromBoth.returnValues).toEqual({ "script" })
110
+ end)
111
+
112
+ it("should capture output from threads the script starts", function()
113
+ local result = runScript("task.defer(print, 'deferred')")
114
+
115
+ expect(result.output[1].body).toEqual("deferred")
116
+ end)
117
+
118
+ it("should capture warnings from threads the script starts", function()
119
+ local result = runScript("task.defer(warn, 'thread warning')")
120
+
121
+ expect(result.output[1].level).toEqual("Warning")
122
+ expect(result.output[1].body).toEqual("thread warning")
123
+ end)
124
+
125
+ it("should report where a runtime error was thrown", function()
126
+ local result = runScript("local bomb = {}\nfunction bomb.explode()\n\terror('boom')\nend\nbomb.explode()")
127
+
128
+ expect(result.code).toEqual("SCRIPT_RUNTIME_ERROR")
129
+ expect(result.error).toContain("exec:3: boom")
130
+ expect(result.traceback).toContain("exec:3")
131
+ expect(result.traceback).toContain("exec:5")
132
+ expect(result.traceback).never.toContain("execute:")
133
+ end)
134
+
135
+ it("should pass args to the script as ...", function()
136
+ local result = ExecuteAction.handleExecute({
137
+ script = "local name, count = ... return name, tonumber(count), select('#', ...)",
138
+ args = { "Player1", "3" },
139
+ }, "req-1", "session-1")
140
+
141
+ expect(result.returnValues).toEqual({ "Player1", 3, 2 } :: { any })
142
+ end)
143
+
144
+ it("should cancel a script that outlives its timeout", function()
145
+ local result = ExecuteAction.handleExecute(
146
+ { script = "task.wait(5) print('too late')", timeoutMs = 50 },
147
+ "req-1",
148
+ "session-1"
149
+ )
150
+
151
+ expect(result.success).toEqual(false)
152
+ expect(result.code).toEqual("SCRIPT_TIMEOUT")
153
+ expect(runScript("return 'next'").returnValues).toEqual({ "next" })
154
+ end)
155
+ end)
156
+
157
+ describe("returnValues", function()
158
+ it("should carry every returned value in order", function()
159
+ expect(runScript("return 1, 'two', true").returnValues).toEqual({ 1, "two", true } :: { any })
160
+ end)
161
+
162
+ it("should keep nested tables and arrays", function()
163
+ expect(runScript("return { a = { b = 1 } }, { 1, 2, 3 }").returnValues).toEqual({
164
+ { a = { b = 1 } },
165
+ { 1, 2, 3 },
166
+ } :: { any })
167
+ end)
168
+
169
+ it("should turn non-string keys into strings", function()
170
+ expect(runScript("return { [1] = 'a', [5] = 'b' }").returnValues).toEqual({ { ["1"] = "a", ["5"] = "b" } })
171
+ end)
172
+
173
+ it("should keep a returned nil in its position", function()
174
+ expect(runScript("return 1, nil, 3").returnValues).toEqual({ 1, { type = "Nil" }, 3 } :: { any })
175
+ end)
176
+
177
+ it("should report a cycle instead of recursing forever", function()
178
+ local result = runScript("local t = {} t.self = t return t")
179
+
180
+ expect(result.returnValues[1].self).toEqual({ type = "Unsupported", typeName = "table", toString = "<cycle>" })
181
+ end)
182
+
183
+ it("should mark non-finite numbers", function()
184
+ local result = runScript("return math.huge, 0/0")
185
+
186
+ expect(result.returnValues[1]).toEqual({ type = "Unsupported", typeName = "number", toString = "inf" })
187
+ expect(result.returnValues[2].type).toEqual("Unsupported")
188
+ end)
189
+
190
+ it("should mark functions unsupported", function()
191
+ local result = runScript("return print")
192
+
193
+ expect(result.returnValues[1].type).toEqual("Unsupported")
194
+ expect(result.returnValues[1].typeName).toEqual("function")
195
+ end)
196
+
197
+ it("should serialize Roblox values", function()
198
+ local result = runScript(
199
+ "return Vector3.new(1, 2, 3), Color3.new(1, 0, 0), Enum.KeyCode.E, UDim2.new(0.5, 10, 1, 20), workspace"
200
+ )
201
+
202
+ expect(result.returnValues).toEqual({
203
+ { type = "Vector3", value = { 1, 2, 3 } },
204
+ { type = "Color3", value = { 1, 0, 0 } },
205
+ { type = "EnumItem", enum = "KeyCode", name = "E", value = Enum.KeyCode.E.Value },
206
+ { type = "UDim2", value = { 0.5, 10, 1, 20 } },
207
+ { type = "Instance", className = "Workspace", path = "Workspace" },
208
+ } :: { any })
209
+ end)
210
+ end)
211
+
212
+ describe("ExecuteAction.register()", function()
213
+ it("should time out a queued script that waited past its timeout without running it", function()
214
+ local controller = setup(true)
215
+
216
+ task.spawn(controller.execute, { script = "task.wait(0.3) return 1" }, "req-1")
217
+ controller.execute({ script = "print('ran') return 2", timeoutMs = 100 }, "req-2")
218
+ while #controller.sent < 2 do
219
+ task.wait()
220
+ end
221
+
222
+ expect(controller.sent[2].requestId).toEqual("req-2")
223
+ expect(controller.sent[2].payload).toEqual({
224
+ success = false,
225
+ error = "Script timed out after 100ms waiting behind other scripts, and never ran",
226
+ code = "SCRIPT_TIMEOUT",
227
+ })
228
+
229
+ controller:Destroy()
230
+ end)
231
+
232
+ it("should reply through the router as scriptComplete without sendMessage", function()
233
+ local controller = setup(false)
234
+
235
+ local response = controller.execute({ script = "return 1" }, "req-1")
236
+
237
+ expect(response and response.type).toEqual("scriptComplete")
238
+ expect(response and response.requestId).toEqual("req-1")
239
+ expect(response and response.payload.returnValues).toEqual({ 1 })
240
+
241
+ controller:Destroy()
242
+ end)
243
+
244
+ it("should reply through sendMessage and nothing through the router", function()
245
+ local controller = setup(true)
246
+
247
+ local response = controller.execute({ script = "return 1" }, "req-1")
248
+
249
+ expect(response).toBeNil()
250
+ expect(controller.sent).toEqual({
251
+ {
252
+ type = "scriptComplete",
253
+ sessionId = "session-1",
254
+ requestId = "req-1",
255
+ payload = { success = true, output = {}, returnValues = { 1 } },
256
+ },
257
+ })
258
+
259
+ controller:Destroy()
260
+ end)
261
+
262
+ it("should leave requestId out when the request had none", function()
263
+ local controller = setup(true)
264
+
265
+ controller.execute({ script = "return 1" }, nil)
266
+
267
+ expect(controller.sent[1].requestId).toBeNil()
268
+
269
+ controller:Destroy()
270
+ end)
271
+
272
+ it("should keep answering in order after a failing request", function()
273
+ local controller = setup(true)
274
+
275
+ controller.execute({ script = "return 1" }, "req-1")
276
+ controller.execute({ script = "error('boom')" }, "req-2")
277
+ controller.execute({ script = "return 3" }, "req-3")
278
+
279
+ expect(#controller.sent).toEqual(3)
280
+ expect(controller.sent[1].requestId).toEqual("req-1")
281
+ expect(controller.sent[2].payload.code).toEqual("SCRIPT_RUNTIME_ERROR")
282
+ expect(controller.sent[3].payload.returnValues).toEqual({ 3 })
283
+
284
+ controller:Destroy()
285
+ end)
286
+ end)
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { describe, it, expect, vi } from 'vitest';
6
- import { queryLogsHandlerAsync } from './logs.js';
6
+ import { formatLogsText, parseSince, queryLogsHandlerAsync } from './logs.js';
7
7
 
8
8
  function createMockSession(logsResult: {
9
9
  entries: Array<{ level: string; body: string; timestamp: number }>;
@@ -31,7 +31,7 @@ describe('queryLogsHandlerAsync', () => {
31
31
  expect(result.entries).toHaveLength(2);
32
32
  expect(result.total).toBe(42);
33
33
  expect(result.bufferCapacity).toBe(1000);
34
- expect(result.summary).toBe('2 entries (42 total in buffer)');
34
+ expect(result.summary).toBe('2 entries (42 in buffer)');
35
35
  });
36
36
 
37
37
  it('passes default options to session', async () => {
@@ -48,6 +48,8 @@ describe('queryLogsHandlerAsync', () => {
48
48
  direction: 'tail',
49
49
  levels: undefined,
50
50
  includeInternal: undefined,
51
+ since: undefined,
52
+ groupRepeats: true,
51
53
  });
52
54
  });
53
55
 
@@ -63,6 +65,7 @@ describe('queryLogsHandlerAsync', () => {
63
65
  direction: 'head',
64
66
  levels: ['Error', 'Warning'],
65
67
  includeInternal: true,
68
+ since: 1793125630,
66
69
  });
67
70
 
68
71
  expect(session.queryLogsAsync).toHaveBeenCalledWith({
@@ -70,6 +73,8 @@ describe('queryLogsHandlerAsync', () => {
70
73
  direction: 'head',
71
74
  levels: ['Error', 'Warning'],
72
75
  includeInternal: true,
76
+ since: 1793125630,
77
+ groupRepeats: true,
73
78
  });
74
79
  });
75
80
 
@@ -84,7 +89,7 @@ describe('queryLogsHandlerAsync', () => {
84
89
 
85
90
  expect(result.entries).toEqual([]);
86
91
  expect(result.total).toBe(0);
87
- expect(result.summary).toBe('0 entries (0 total in buffer)');
92
+ expect(result.summary).toBe('0 entries (0 in buffer)');
88
93
  });
89
94
 
90
95
  it('handles entries with all log levels', async () => {
@@ -133,6 +138,73 @@ describe('queryLogsHandlerAsync', () => {
133
138
  expect(result.entries).toEqual([]);
134
139
  expect(result.total).toBe(0);
135
140
  expect(result.bufferCapacity).toBe(0);
136
- expect(result.summary).toBe('0 entries (0 total in buffer)');
141
+ expect(result.summary).toBe('0 entries (0 in buffer)');
142
+ });
143
+ });
144
+
145
+ describe('parseSince', () => {
146
+ const now = Date.parse('2026-10-27T18:00:00Z');
147
+
148
+ it('reads a duration back from now', () => {
149
+ expect(parseSince('30s', now)).toBe(now / 1000 - 30);
150
+ expect(parseSince('5m', now)).toBe(now / 1000 - 300);
151
+ expect(parseSince('2h', now)).toBe(now / 1000 - 7200);
152
+ });
153
+
154
+ it('reads a date', () => {
155
+ expect(parseSince('2026-10-27T17:00:00Z', now)).toBe(now / 1000 - 3600);
156
+ });
157
+
158
+ it('refuses anything else', () => {
159
+ expect(() => parseSince('yesterday', now)).toThrow(/Can't read --since/);
160
+ });
161
+ });
162
+
163
+ describe('repeat grouping', () => {
164
+ it('is asked of the plugin unless turned off', async () => {
165
+ const session = createMockSession({
166
+ entries: [],
167
+ total: 0,
168
+ bufferCapacity: 1000,
169
+ });
170
+
171
+ await queryLogsHandlerAsync(session);
172
+ await queryLogsHandlerAsync(session, { groupRepeats: false });
173
+
174
+ expect(session.queryLogsAsync.mock.calls[0][0].groupRepeats).toBe(true);
175
+ expect(session.queryLogsAsync.mock.calls[1][0].groupRepeats).toBe(false);
176
+ });
177
+
178
+ it('counts each repeat in the summary', async () => {
179
+ const session = createMockSession({
180
+ entries: [
181
+ { level: 'Print', body: 'tick', timestamp: 1, repeats: 3 },
182
+ { level: 'Print', body: 'done', timestamp: 4 },
183
+ ],
184
+ total: 4,
185
+ bufferCapacity: 1000,
186
+ } as any);
187
+
188
+ expect((await queryLogsHandlerAsync(session)).summary).toBe(
189
+ '4 entries (4 in buffer)'
190
+ );
191
+ });
192
+ });
193
+
194
+ describe('formatLogsText', () => {
195
+ it('shows the repeat count and counts every repeat as shown', () => {
196
+ const text = formatLogsText({
197
+ entries: [
198
+ { level: 'Print', body: 'tick', timestamp: 1793125630, repeats: 3 },
199
+ ],
200
+ total: 3,
201
+ bufferCapacity: 1000,
202
+ summary: '3 entries (3 in buffer)',
203
+ });
204
+
205
+ expect(text).toContain('tick');
206
+ expect(text).toContain('(x3)');
207
+ expect(text).toContain('(3 shown, 3 in buffer)');
208
+ expect(text).toContain(new Date(1793125630 * 1000).toLocaleTimeString());
137
209
  });
138
210
  });
@@ -6,7 +6,7 @@
6
6
  import { defineCommand } from '../../framework/define-command.js';
7
7
  import { arg } from '../../framework/arg-builder.js';
8
8
  import { OutputHelper } from '@quenty/cli-output-helpers';
9
- import type { BridgeSession } from '../../../bridge/index.js';
9
+ import type { BridgeConnection, BridgeSession } from '../../../bridge/index.js';
10
10
  import type {
11
11
  LogEntry,
12
12
  LogsResult as BridgeLogsResult,
@@ -14,9 +14,12 @@ import type {
14
14
  import type { OutputLevel } from '../../../server/web-socket-protocol.js';
15
15
 
16
16
  export interface LogsResult {
17
+ /** With repeats grouped, a run of identical entries is one with `repeats` and `lastTimestamp`. */
17
18
  entries: LogEntry[];
18
19
  total: number;
19
20
  bufferCapacity: number;
21
+ /** The cutoff `--since` or `--since-restart` applied, in epoch seconds. */
22
+ since?: number;
20
23
  summary: string;
21
24
  }
22
25
 
@@ -25,6 +28,89 @@ export interface LogsOptions {
25
28
  direction?: 'head' | 'tail';
26
29
  levels?: OutputLevel[];
27
30
  includeInternal?: boolean;
31
+ /** Only entries logged at or after this time, in epoch seconds. */
32
+ since?: number;
33
+ /** Collapse identical consecutive entries. Defaults to true. */
34
+ groupRepeats?: boolean;
35
+ }
36
+
37
+ interface ConsoleLogsArgs {
38
+ count?: number;
39
+ direction?: 'head' | 'tail';
40
+ levels?: OutputLevel[];
41
+ includeInternal?: boolean;
42
+ since?: string;
43
+ sinceRestart?: boolean;
44
+ groupRepeats?: boolean;
45
+ }
46
+
47
+ const DURATION_UNITS_SEC: Record<string, number> = {
48
+ s: 1,
49
+ m: 60,
50
+ h: 3600,
51
+ d: 86400,
52
+ };
53
+
54
+ /**
55
+ * The time `--since` names, in epoch seconds: a duration back from `nowMs`
56
+ * (`30s`, `5m`, `2h`, `1d`) or a date (`2026-10-27T18:00:00`).
57
+ */
58
+ export function parseSince(value: string, nowMs: number = Date.now()): number {
59
+ const duration = /^(\d+(?:\.\d+)?)([smhd])$/.exec(value.trim());
60
+ if (duration) {
61
+ return nowMs / 1000 - Number(duration[1]) * DURATION_UNITS_SEC[duration[2]];
62
+ }
63
+
64
+ const dateMs = Date.parse(value);
65
+ if (Number.isNaN(dateMs)) {
66
+ throw new Error(
67
+ `Can't read --since '${value}'. Use a duration like 30s, 5m, 2h or 1d, or a date.`
68
+ );
69
+ }
70
+ return dateMs / 1000;
71
+ }
72
+
73
+ const PLAYTEST_START_SCRIPT =
74
+ 'return DateTime.now().UnixTimestampMillis / 1000 - time()';
75
+ /** The server's DataModel starts running a moment after Edit starts the playtest. */
76
+ const PLAYTEST_START_SLACK_SEC = 2;
77
+
78
+ /**
79
+ * The `--since-restart` cutoff for `session`, in epoch seconds: none for a
80
+ * playtest's own session, whose log began with the playtest, and otherwise a
81
+ * little before the running playtest's server started.
82
+ */
83
+ export async function getSinceRestartCutoffAsync(
84
+ connection: BridgeConnection,
85
+ session: BridgeSession
86
+ ): Promise<number | undefined> {
87
+ if (session.info.context !== 'edit') {
88
+ return undefined;
89
+ }
90
+
91
+ const server = connection
92
+ .listSessions()
93
+ .find(
94
+ (info) =>
95
+ info.instanceId === session.info.instanceId && info.context === 'server'
96
+ );
97
+ const serverSession = server && connection.getSession(server.sessionId);
98
+ if (!serverSession) {
99
+ throw new Error(
100
+ `--since-restart needs a running playtest, and '${session.info.instanceId}' has none. Use --since to read back to a time instead.`
101
+ );
102
+ }
103
+
104
+ const result = await serverSession.execAsync(PLAYTEST_START_SCRIPT, 10_000);
105
+ const startedAt = result.returnValues?.[0];
106
+ if (!result.success || typeof startedAt !== 'number') {
107
+ throw new Error(
108
+ `Could not read when the playtest started: ${
109
+ result.error ?? 'no time returned'
110
+ }`
111
+ );
112
+ }
113
+ return startedAt - PLAYTEST_START_SLACK_SEC;
28
114
  }
29
115
 
30
116
  export async function queryLogsHandlerAsync(
@@ -36,15 +122,19 @@ export async function queryLogsHandlerAsync(
36
122
  direction: options.direction ?? 'tail',
37
123
  levels: options.levels,
38
124
  includeInternal: options.includeInternal,
125
+ since: options.since,
126
+ groupRepeats: options.groupRepeats !== false,
39
127
  });
40
128
 
129
+ const entries = result.entries ?? [];
130
+ const shown = entries.reduce((sum, e) => sum + (e.repeats ?? 1), 0);
131
+
41
132
  return {
42
- entries: result.entries ?? [],
133
+ entries,
43
134
  total: result.total ?? 0,
44
135
  bufferCapacity: result.bufferCapacity ?? 0,
45
- summary: `${(result.entries ?? []).length} entries (${
46
- result.total ?? 0
47
- } total in buffer)`,
136
+ since: options.since,
137
+ summary: `${shown} entries (${result.total ?? 0} in buffer)`,
48
138
  };
49
139
  }
50
140
 
@@ -63,20 +153,20 @@ export function formatLogsText(result: LogsResult): string {
63
153
  if (result.entries.length === 0) return result.summary;
64
154
  const lines = result.entries.map((e) => {
65
155
  const ts = OutputHelper.formatDim(
66
- new Date(e.timestamp).toLocaleTimeString()
156
+ new Date(e.timestamp * 1000).toLocaleTimeString()
67
157
  );
68
158
  const level = colorizeLevel(e.level);
69
- return `[${ts}] [${level}] ${e.body}`;
159
+ const repeats = e.repeats ? OutputHelper.formatDim(` (x${e.repeats})`) : '';
160
+ return `[${ts}] [${level}] ${e.body}${repeats}`;
70
161
  });
162
+ const shown = result.entries.reduce((sum, e) => sum + (e.repeats ?? 1), 0);
71
163
  lines.push(
72
- OutputHelper.formatDim(
73
- `(${result.entries.length} of ${result.total} entries)`
74
- )
164
+ OutputHelper.formatDim(`(${shown} shown, ${result.total} in buffer)`)
75
165
  );
76
166
  return lines.join('\n');
77
167
  }
78
168
 
79
- export const logsCommand = defineCommand<LogsOptions, LogsResult>({
169
+ export const logsCommand = defineCommand<ConsoleLogsArgs, LogsResult>({
80
170
  group: 'console',
81
171
  name: 'logs',
82
172
  description: 'Retrieve buffered log history from Studio',
@@ -94,22 +184,47 @@ export const logsCommand = defineCommand<LogsOptions, LogsResult>({
94
184
  choices: ['head', 'tail'] as const,
95
185
  }),
96
186
  levels: arg.option({
97
- description: 'Filter by output level (Print, Warning, Error)',
187
+ description: 'Filter by output level',
98
188
  array: true,
189
+ choices: ['Print', 'Info', 'Warning', 'Error'] as const,
99
190
  }),
100
191
  includeInternal: arg.flag({
101
192
  description: 'Include internal/system log messages',
102
193
  }),
194
+ since: arg.option({
195
+ description:
196
+ 'Only entries logged since a duration ago (30s, 5m, 2h, 1d) or a date',
197
+ }),
198
+ sinceRestart: arg.flag({
199
+ description: 'Only entries logged since the running playtest started',
200
+ }),
201
+ groupRepeats: arg.flag({
202
+ description:
203
+ 'Collapse identical consecutive entries into one with a repeat count (--no-group-repeats to keep them)',
204
+ default: true,
205
+ }),
103
206
  },
104
207
  cli: {
105
208
  format: formatLogsText,
106
209
  },
107
- handler: async (session, args) => {
210
+ handler: async (session, args, connection) => {
211
+ if (args.since && args.sinceRestart) {
212
+ throw new Error('Use either --since or --since-restart, not both');
213
+ }
214
+
215
+ const since = args.sinceRestart
216
+ ? await getSinceRestartCutoffAsync(connection, session)
217
+ : args.since
218
+ ? parseSince(args.since)
219
+ : undefined;
220
+
108
221
  return queryLogsHandlerAsync(session, {
109
- count: args.count as number | undefined,
110
- direction: args.direction as 'head' | 'tail' | undefined,
111
- levels: args.levels as OutputLevel[] | undefined,
112
- includeInternal: args.includeInternal as boolean | undefined,
222
+ count: args.count,
223
+ direction: args.direction,
224
+ levels: args.levels,
225
+ includeInternal: args.includeInternal,
226
+ since,
227
+ groupRepeats: args.groupRepeats,
113
228
  });
114
229
  },
115
230
  });