@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
@@ -1,111 +1,92 @@
1
- --[[
2
- QueryDataModel action handler for the studio-bridge plugin.
3
-
4
- Resolves a dot-separated path to a Roblox Instance and serializes it
5
- into a DataModelInstance response with optional properties, attributes,
6
- and children up to a specified depth.
7
-
8
- Protocol:
9
- Request: { type: "queryDataModel", path: "Workspace.SpawnLocation", depth: 1, properties: [], includeAttributes: true }
10
- Response: { type: "dataModelResult", payload: { instance: DataModelInstance } }
11
- ]]
1
+ --!strict
2
+ --[=[
3
+ Action that resolves a dot-separated path to an Instance and answers with it
4
+ serialized, along with its properties, attributes, and children down to
5
+ `depth`.
12
6
 
13
- local QueryDataModelAction = {}
7
+ Request: `{ type: "queryDataModel", payload: { path, depth?, properties?, includeAttributes? } }`
8
+ Response: `{ type: "dataModelResult", payload: { instance: DataModelInstance } }`
14
9
 
15
- -- ---------------------------------------------------------------------------
16
- -- Property serialization
17
- -- ---------------------------------------------------------------------------
10
+ @class QueryDataModelAction
11
+ ]=]
18
12
 
13
+ local COMMON_PROPERTIES = { "Name", "ClassName" }
19
14
  local SKIP_PROPERTIES = {
20
15
  Parent = true,
21
16
  }
22
17
 
23
- local function serializeValue(value: any): any
24
- local t = typeof(value)
18
+ local QueryDataModelAction = {}
25
19
 
26
- if t == "string" or t == "number" or t == "boolean" then
27
- return value
28
- elseif t == "nil" then
29
- return nil
30
- elseif t == "Vector3" then
31
- return { type = "Vector3", value = { value.X, value.Y, value.Z } }
32
- elseif t == "Vector2" then
33
- return { type = "Vector2", value = { value.X, value.Y } }
34
- elseif t == "CFrame" then
35
- return { type = "CFrame", value = { value:GetComponents() } }
36
- elseif t == "Color3" then
37
- return { type = "Color3", value = { value.R, value.G, value.B } }
38
- elseif t == "UDim2" then
39
- return { type = "UDim2", value = { value.X.Scale, value.X.Offset, value.Y.Scale, value.Y.Offset } }
40
- elseif t == "UDim" then
41
- return { type = "UDim", value = { value.Scale, value.Offset } }
42
- elseif t == "BrickColor" then
43
- return { type = "BrickColor", name = value.Name, value = value.Number }
44
- elseif t == "EnumItem" then
45
- return { type = "EnumItem", enum = tostring(value.EnumType), name = value.Name, value = value.Value }
46
- elseif t == "Instance" then
47
- return { type = "Instance", className = value.ClassName, path = value:GetFullName() }
48
- else
49
- return { type = "Unsupported", typeName = t, toString = tostring(value) }
50
- end
20
+ --[=[
21
+ Registers the `queryDataModel` handler.
22
+ ]=]
23
+ function QueryDataModelAction.register(router: any)
24
+ router:setResponseType("queryDataModel", "dataModelResult")
25
+ router:register(
26
+ "queryDataModel",
27
+ function(payload: { [string]: any }, _requestId: string, _sessionId: string): { [string]: any }?
28
+ local path = payload.path
29
+ if type(path) ~= "string" then
30
+ return nil
31
+ end
32
+
33
+ local instance = QueryDataModelAction._resolveInstance(path)
34
+ if not instance then
35
+ return {
36
+ success = false,
37
+ code = "INSTANCE_NOT_FOUND",
38
+ message = `Instance not found at path: {path}`,
39
+ }
40
+ end
41
+
42
+ return {
43
+ instance = QueryDataModelAction._serializeInstance(
44
+ instance,
45
+ payload.depth or 0,
46
+ payload.properties,
47
+ payload.includeAttributes == true
48
+ ),
49
+ }
50
+ end
51
+ )
51
52
  end
52
53
 
53
- local function serializeProperties(instance: Instance, propertyFilter: { string }?): { [string]: any }
54
- local result = {}
54
+ --[[
55
+ Walks `path` down from game, falling back to GetService for top-level names
56
+ that are not children by that name. A leading "game" is optional.
57
+ ]]
58
+ function QueryDataModelAction._resolveInstance(path: string): Instance?
59
+ local parts = string.split(path, ".")
60
+ local startIndex = if parts[1] == "game" then 2 else 1
55
61
 
56
- -- If a filter is provided and empty, return no properties
57
- if propertyFilter and #propertyFilter == 0 then
58
- return result
59
- end
62
+ local current: Instance = game
63
+ for index = startIndex, #parts do
64
+ local part = parts[index]
60
65
 
61
- -- Use API dump approach: read common properties via pcall
62
- -- Since Roblox doesn't expose a property list, we read known safe properties
63
- -- and any specifically requested ones
64
- if propertyFilter then
65
- for _, propName in propertyFilter do
66
- if not SKIP_PROPERTIES[propName] then
67
- local ok, value = pcall(function()
68
- return (instance :: any)[propName]
69
- end)
70
- if ok then
71
- result[propName] = serializeValue(value)
72
- end
73
- end
66
+ local ok, child = pcall(current.FindFirstChild, current, part)
67
+ if ok and child then
68
+ current = child
69
+ continue
74
70
  end
75
- else
76
- -- No filter: read common properties
77
- local commonProps = { "Name", "ClassName" }
78
- for _, propName in commonProps do
79
- local ok, value = pcall(function()
80
- return (instance :: any)[propName]
81
- end)
82
- if ok then
83
- result[propName] = serializeValue(value)
84
- end
85
- end
86
- end
87
71
 
88
- return result
89
- end
72
+ if current ~= game then
73
+ return nil
74
+ end
90
75
 
91
- local function serializeAttributes(instance: Instance): { [string]: any }
92
- local result = {}
93
- local ok, attrs = pcall(function()
94
- return instance:GetAttributes()
95
- end)
96
- if ok and attrs then
97
- for key, value in attrs do
98
- result[key] = serializeValue(value)
76
+ local serviceOk, service = pcall(game.GetService, game, part)
77
+ if not (serviceOk and service) then
78
+ return nil
99
79
  end
80
+ current = service
100
81
  end
101
- return result
102
- end
103
82
 
104
- -- ---------------------------------------------------------------------------
105
- -- Instance serialization
106
- -- ---------------------------------------------------------------------------
83
+ return current
84
+ end
107
85
 
108
- local function serializeInstance(
86
+ --[[
87
+ A nil filter reads the common properties, an empty one reads none.
88
+ ]]
89
+ function QueryDataModelAction._serializeInstance(
109
90
  instance: Instance,
110
91
  depth: number,
111
92
  propertyFilter: { string }?,
@@ -116,15 +97,18 @@ local function serializeInstance(
116
97
  name = instance.Name,
117
98
  className = instance.ClassName,
118
99
  path = instance:GetFullName(),
119
- properties = serializeProperties(instance, propertyFilter),
120
- attributes = if includeAttributes then serializeAttributes(instance) else {},
100
+ properties = QueryDataModelAction._serializeProperties(instance, propertyFilter or COMMON_PROPERTIES),
101
+ attributes = if includeAttributes then QueryDataModelAction._serializeAttributes(instance) else {},
121
102
  childCount = #children,
122
103
  }
123
104
 
124
105
  if depth > 0 then
125
106
  local serializedChildren = {}
126
107
  for _, child in children do
127
- table.insert(serializedChildren, serializeInstance(child, depth - 1, propertyFilter, includeAttributes))
108
+ table.insert(
109
+ serializedChildren,
110
+ QueryDataModelAction._serializeInstance(child, depth - 1, propertyFilter, includeAttributes)
111
+ )
128
112
  end
129
113
  node.children = serializedChildren
130
114
  end
@@ -132,75 +116,63 @@ local function serializeInstance(
132
116
  return node
133
117
  end
134
118
 
135
- -- ---------------------------------------------------------------------------
136
- -- Path resolution
137
- -- ---------------------------------------------------------------------------
138
-
139
- local function resolveInstance(path: string): Instance?
140
- -- Split path by dots, resolve from game
141
- local parts = string.split(path, ".")
142
- local startIndex = 1
143
-
144
- -- Skip leading "game" since we start from game
145
- if parts[1] == "game" then
146
- startIndex = 2
147
- end
119
+ function QueryDataModelAction._serializeProperties(instance: Instance, propertyNames: { string }): { [string]: any }
120
+ local result = {}
121
+ for _, propertyName in propertyNames do
122
+ if SKIP_PROPERTIES[propertyName] then
123
+ continue
124
+ end
148
125
 
149
- local current: any = game
150
- for i = startIndex, #parts do
151
- local part = parts[i]
152
- -- Try as a service first (e.g. "Workspace", "ReplicatedStorage")
153
- local ok, child = pcall(function()
154
- return current:FindFirstChild(part)
126
+ -- Roblox exposes no property list, so read each name and skip the ones that throw
127
+ local ok, value = pcall(function()
128
+ return (instance :: any)[propertyName]
155
129
  end)
156
- if not ok or not child then
157
- -- Try GetService for top-level services
158
- if current == game then
159
- local serviceOk, service = pcall(function()
160
- return game:GetService(part)
161
- end)
162
- if serviceOk and service then
163
- current = service
164
- continue
165
- end
166
- end
167
- return nil
130
+ if ok then
131
+ result[propertyName] = QueryDataModelAction._serializeValue(value)
168
132
  end
169
- current = child
170
133
  end
171
-
172
- return current
134
+ return result
173
135
  end
174
136
 
175
- -- ---------------------------------------------------------------------------
176
- -- Public API
177
- -- ---------------------------------------------------------------------------
178
-
179
- function QueryDataModelAction.register(router: any)
180
- router:setResponseType("queryDataModel", "dataModelResult")
181
- router:register("queryDataModel", function(payload: { [string]: any }, _requestId: string, _sessionId: string)
182
- local path = payload.path
183
- if not path or type(path) ~= "string" then
184
- return nil -- ActionRouter will return UNKNOWN_REQUEST for nil
185
- end
137
+ function QueryDataModelAction._serializeAttributes(instance: Instance): { [string]: any }
138
+ local result = {}
139
+ local ok, attributes = pcall(instance.GetAttributes, instance)
140
+ if not ok then
141
+ return result
142
+ end
186
143
 
187
- local depth = payload.depth or 0
188
- local propertyFilter = payload.properties -- nil means common props, {} means none
189
- local includeAttributes = payload.includeAttributes == true
144
+ for key, value in attributes do
145
+ result[key] = QueryDataModelAction._serializeValue(value)
146
+ end
147
+ return result
148
+ end
190
149
 
191
- local instance = resolveInstance(path)
192
- if not instance then
193
- return {
194
- success = false,
195
- code = "INSTANCE_NOT_FOUND",
196
- message = `Instance not found at path: {path}`,
197
- }
198
- end
150
+ function QueryDataModelAction._serializeValue(value: any): any
151
+ local valueType = typeof(value)
199
152
 
200
- return {
201
- instance = serializeInstance(instance, depth, propertyFilter, includeAttributes),
202
- }
203
- end)
153
+ if valueType == "nil" or valueType == "string" or valueType == "number" or valueType == "boolean" then
154
+ return value
155
+ elseif valueType == "Vector3" then
156
+ return { type = "Vector3", value = { value.X, value.Y, value.Z } }
157
+ elseif valueType == "Vector2" then
158
+ return { type = "Vector2", value = { value.X, value.Y } }
159
+ elseif valueType == "CFrame" then
160
+ return { type = "CFrame", value = { value:GetComponents() } }
161
+ elseif valueType == "Color3" then
162
+ return { type = "Color3", value = { value.R, value.G, value.B } }
163
+ elseif valueType == "UDim2" then
164
+ return { type = "UDim2", value = { value.X.Scale, value.X.Offset, value.Y.Scale, value.Y.Offset } }
165
+ elseif valueType == "UDim" then
166
+ return { type = "UDim", value = { value.Scale, value.Offset } }
167
+ elseif valueType == "BrickColor" then
168
+ return { type = "BrickColor", name = value.Name, value = value.Number }
169
+ elseif valueType == "EnumItem" then
170
+ return { type = "EnumItem", enum = tostring(value.EnumType), name = value.Name, value = value.Value }
171
+ elseif valueType == "Instance" then
172
+ return { type = "Instance", className = value.ClassName, path = value:GetFullName() }
173
+ else
174
+ return { type = "Unsupported", typeName = valueType, toString = tostring(value) }
175
+ end
204
176
  end
205
177
 
206
178
  return QueryDataModelAction
@@ -0,0 +1,150 @@
1
+ --!strict
2
+ --[[
3
+ @class query-data-model.spec.lua
4
+ ]]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local HttpService = game:GetService("HttpService")
9
+
10
+ local ActionRouter = require("ActionRouter")
11
+ local Jest = require("Jest")
12
+ local QueryDataModelAction = require("query-data-model")
13
+
14
+ local describe = Jest.Globals.describe
15
+ local expect = Jest.Globals.expect
16
+ local it = Jest.Globals.it
17
+
18
+ local function setup(): any
19
+ local router = ActionRouter.new()
20
+ QueryDataModelAction.register(router)
21
+
22
+ local root = Instance.new("Folder")
23
+ root.Name = `QueryDataModelSpec{HttpService:GenerateGUID(false)}`
24
+ root:SetAttribute("Offset", Vector3.new(1, 2, 3))
25
+
26
+ local value = Instance.new("IntValue")
27
+ value.Name = "Score"
28
+ value.Value = 42
29
+ value.Parent = root
30
+
31
+ local nested = Instance.new("Folder")
32
+ nested.Name = "Nested"
33
+ nested.Parent = value
34
+
35
+ root.Parent = workspace
36
+
37
+ local controller = {
38
+ root = root,
39
+ rootPath = `Workspace.{root.Name}`,
40
+ query = function(payload: { [string]: any }): any
41
+ return router:Dispatch({ type = "queryDataModel", sessionId = "session-1", payload = payload })
42
+ end,
43
+ Destroy = function(_self)
44
+ root:Destroy()
45
+ end,
46
+ }
47
+
48
+ return controller
49
+ end
50
+
51
+ describe("queryDataModel", function()
52
+ it("should answer dataModelResult with the instance's common properties", function()
53
+ local controller = setup()
54
+
55
+ local response = controller.query({ path = controller.rootPath })
56
+
57
+ expect(response.type).toEqual("dataModelResult")
58
+ expect(response.payload.instance).toEqual({
59
+ name = controller.root.Name,
60
+ className = "Folder",
61
+ path = controller.root:GetFullName(),
62
+ properties = { Name = controller.root.Name, ClassName = "Folder" },
63
+ attributes = {},
64
+ childCount = 1,
65
+ })
66
+
67
+ controller:Destroy()
68
+ end)
69
+
70
+ it("should accept a leading game", function()
71
+ local controller = setup()
72
+
73
+ local response = controller.query({ path = `game.{controller.rootPath}` })
74
+
75
+ expect(response.payload.instance.name).toEqual(controller.root.Name)
76
+
77
+ controller:Destroy()
78
+ end)
79
+
80
+ it("should include children down to the requested depth", function()
81
+ local controller = setup()
82
+
83
+ local response = controller.query({ path = controller.rootPath, depth = 1 })
84
+
85
+ local children = response.payload.instance.children
86
+ expect(#children).toEqual(1)
87
+ expect(children[1].name).toEqual("Score")
88
+ expect(children[1].childCount).toEqual(1)
89
+ expect(children[1].children).toBeNil()
90
+
91
+ controller:Destroy()
92
+ end)
93
+
94
+ it("should read only the requested properties, skipping Parent and unknown names", function()
95
+ local controller = setup()
96
+
97
+ local response = controller.query({
98
+ path = `{controller.rootPath}.Score`,
99
+ properties = { "Value", "Parent", "NotAProperty" },
100
+ })
101
+
102
+ expect(response.payload.instance.properties).toEqual({ Value = 42 })
103
+
104
+ controller:Destroy()
105
+ end)
106
+
107
+ it("should read no properties for an empty list", function()
108
+ local controller = setup()
109
+
110
+ local response = controller.query({ path = controller.rootPath, properties = {} })
111
+
112
+ expect(response.payload.instance.properties).toEqual({})
113
+
114
+ controller:Destroy()
115
+ end)
116
+
117
+ it("should serialize attributes when asked", function()
118
+ local controller = setup()
119
+
120
+ local response = controller.query({ path = controller.rootPath, includeAttributes = true })
121
+
122
+ expect(response.payload.instance.attributes).toEqual({
123
+ Offset = { type = "Vector3", value = { 1, 2, 3 } },
124
+ })
125
+
126
+ controller:Destroy()
127
+ end)
128
+
129
+ it("should answer INSTANCE_NOT_FOUND for a missing path", function()
130
+ local controller = setup()
131
+
132
+ local response = controller.query({ path = `{controller.rootPath}.Missing` })
133
+
134
+ expect(response.type).toEqual("error")
135
+ expect(response.payload).toEqual({
136
+ code = "INSTANCE_NOT_FOUND",
137
+ message = `Instance not found at path: {controller.rootPath}.Missing`,
138
+ })
139
+
140
+ controller:Destroy()
141
+ end)
142
+
143
+ it("should send nothing without a path", function()
144
+ local controller = setup()
145
+
146
+ expect(controller.query({})).toBeNil()
147
+
148
+ controller:Destroy()
149
+ end)
150
+ end)
@@ -74,7 +74,7 @@ describe('logs formatter', () => {
74
74
  const text = formatLogsText(result);
75
75
  expect(text).toContain('Hello');
76
76
  expect(text).toContain('Boom');
77
- expect(text).toContain('(2 of 2 entries)');
77
+ expect(text).toContain('(2 shown, 2 in buffer)');
78
78
  });
79
79
 
80
80
  it('is wired into command definition', () => {
@@ -123,10 +123,16 @@ describe('list formatter', () => {
123
123
  describe('info formatter', () => {
124
124
  it('formats key-value pairs', () => {
125
125
  const result = {
126
+ sessionId: 'my-place-1a2b-edit',
127
+ instanceId: 'my-place-1a2b',
128
+ context: 'edit' as const,
129
+ origin: 'user' as const,
126
130
  state: 'Edit' as const,
127
131
  placeName: 'MyPlace',
128
132
  placeId: 123,
129
133
  gameId: 456,
134
+ plugin: { version: 'abc', expectedVersion: 'abc', upToDate: true },
135
+ connectedAt: new Date().toISOString(),
130
136
  summary: 'Mode: Edit, Place: MyPlace (123)',
131
137
  };
132
138
 
@@ -98,6 +98,34 @@ describe('loadActionSourcesAsync', () => {
98
98
  expect(results[0].name).toBe('action');
99
99
  });
100
100
 
101
+ it('skips .spec.lua files', async () => {
102
+ await fs.writeFile(path.join(tmpDir, 'action.lua'), 'included');
103
+ await fs.writeFile(path.join(tmpDir, 'action.spec.lua'), 'not included');
104
+
105
+ const results = await loadActionSourcesAsync(tmpDir);
106
+
107
+ expect(results.map((r) => r.name)).toEqual(['action']);
108
+ });
109
+
110
+ it('finds every shipped action and none of their specs', async () => {
111
+ const results = await loadActionSourcesAsync();
112
+
113
+ expect(results.map((r) => r.name).sort()).toEqual([
114
+ 'capture-screenshot',
115
+ 'end-playtest',
116
+ 'execute',
117
+ 'export-instances',
118
+ 'find-instances',
119
+ 'grep-scripts',
120
+ 'import-instances',
121
+ 'query-data-model',
122
+ 'query-logs',
123
+ 'query-state',
124
+ 'run-playtest-script',
125
+ 'start-playtest',
126
+ ]);
127
+ });
128
+
101
129
  it('returns empty array for empty directory', async () => {
102
130
  const results = await loadActionSourcesAsync(tmpDir);
103
131
  expect(results).toHaveLength(0);
@@ -6,6 +6,9 @@
6
6
  * as its `.ts` file (e.g. `exec.lua` next to `exec.ts`). These files
7
7
  * are Luau modules that register action handlers in the plugin's
8
8
  * ActionRouter at runtime.
9
+ *
10
+ * `.spec.lua` files beside them are Jest specs. They are never pushed,
11
+ * since anything pushed runs inside the user's Studio.
9
12
  */
10
13
 
11
14
  import { createHash } from 'crypto';
@@ -26,7 +29,8 @@ export interface ActionSource {
26
29
 
27
30
  /**
28
31
  * Recursively scan `baseDir` for `.lua` files and return their contents
29
- * as `ActionSource` entries. Only files that end in `.lua` are included.
32
+ * as `ActionSource` entries. Only files that end in `.lua` are included,
33
+ * and `.spec.lua` files are skipped.
30
34
  */
31
35
  export async function loadActionSourcesAsync(
32
36
  baseDir?: string
@@ -54,7 +58,7 @@ async function scanDirAsync(
54
58
  const fullPath = path.join(currentDir, entry.name);
55
59
  if (entry.isDirectory()) {
56
60
  await scanDirAsync(baseDir, fullPath, results);
57
- } else if (entry.name.endsWith('.lua')) {
61
+ } else if (isActionFile(entry.name)) {
58
62
  const source = await fs.readFile(fullPath, 'utf-8');
59
63
  const name = path.basename(entry.name, '.lua');
60
64
  const relativePath = path.relative(baseDir, fullPath);
@@ -63,3 +67,7 @@ async function scanDirAsync(
63
67
  }
64
68
  }
65
69
  }
70
+
71
+ function isActionFile(fileName: string): boolean {
72
+ return fileName.endsWith('.lua') && !fileName.endsWith('.spec.lua');
73
+ }
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Argument definition DSL and schema converters.
3
3
  *
4
- * `arg.positional()`, `arg.option()`, and `arg.flag()` produce `ArgDefinition`
4
+ * `arg.positional()`, `arg.option()`, `arg.flag()`, and `arg.passthrough()` produce `ArgDefinition`
5
5
  * objects that carry enough metadata to generate both yargs options and
6
6
  * JSON Schema properties for MCP tools.
7
7
  */
8
8
 
9
- export type ArgKind = 'positional' | 'option' | 'flag';
9
+ export type ArgKind = 'positional' | 'option' | 'flag' | 'passthrough';
10
10
  export type ArgType = 'string' | 'number' | 'boolean';
11
11
 
12
12
  export interface ArgDefinition {
@@ -33,6 +33,8 @@ export const arg = {
33
33
  type?: 'string' | 'number';
34
34
  required?: boolean;
35
35
  choices?: readonly string[];
36
+ /** Collect every remaining positional into an array (`<name..>`). */
37
+ array?: boolean;
36
38
  }): ArgDefinition {
37
39
  return {
38
40
  kind: 'positional',
@@ -40,6 +42,7 @@ export const arg = {
40
42
  description: config.description,
41
43
  required: config.required ?? true,
42
44
  choices: config.choices,
45
+ array: config.array,
43
46
  };
44
47
  },
45
48
 
@@ -91,6 +94,22 @@ export const arg = {
91
94
  default: config.default ?? false,
92
95
  };
93
96
  },
97
+
98
+ /**
99
+ * Collect everything after `--` as a list of strings, unparsed.
100
+ *
101
+ * ```ts
102
+ * args: { scriptArgs: arg.passthrough({ description: 'Passed to the script' }) }
103
+ * ```
104
+ */
105
+ passthrough(config: { description: string }): ArgDefinition {
106
+ return {
107
+ kind: 'passthrough',
108
+ type: 'string',
109
+ description: config.description,
110
+ array: true,
111
+ };
112
+ },
94
113
  };
95
114
 
96
115
  export interface YargsPositional {
@@ -100,6 +119,7 @@ export interface YargsPositional {
100
119
  type: 'string' | 'number';
101
120
  demandOption?: boolean;
102
121
  choices?: readonly string[];
122
+ array?: boolean;
103
123
  };
104
124
  }
105
125
 
@@ -118,7 +138,9 @@ export function toYargsOptions(
118
138
  const options: Record<string, Record<string, unknown>> = {};
119
139
 
120
140
  for (const [name, def] of Object.entries(args)) {
121
- if (def.kind === 'positional') {
141
+ if (def.kind === 'passthrough') {
142
+ continue;
143
+ } else if (def.kind === 'positional') {
122
144
  positionals.push({
123
145
  name,
124
146
  options: {
@@ -126,6 +148,7 @@ export function toYargsOptions(
126
148
  type: def.type as 'string' | 'number',
127
149
  demandOption: def.required,
128
150
  ...(def.choices ? { choices: def.choices } : {}),
151
+ ...(def.array ? { array: true } : {}),
129
152
  },
130
153
  });
131
154
  } else {