@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,250 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+
3
+ vi.mock('../../../process/studio-process-closer.js', async (importOriginal) => {
4
+ const actual = await importOriginal<
5
+ typeof import('../../../process/studio-process-closer.js')
6
+ >();
7
+ return {
8
+ ...actual,
9
+ listStudioProcessesAsync: vi.fn(),
10
+ closeStudioProcessAsync: vi.fn(),
11
+ };
12
+ });
13
+
14
+ import * as fs from 'fs/promises';
15
+ import * as os from 'os';
16
+ import * as path from 'path';
17
+ import { closeDeployTargetAsync, processCloseHandlerAsync } from './close.js';
18
+ import {
19
+ closeStudioProcessAsync,
20
+ listStudioProcessesAsync,
21
+ } from '../../../process/studio-process-closer.js';
22
+
23
+ const queryStateAsync = async () => ({ placeId: 0 });
24
+
25
+ const session = {
26
+ info: { sessionId: 'test-place-edit', placeName: 'test-place.rbxl' },
27
+ queryStateAsync,
28
+ } as any;
29
+
30
+ describe('processCloseHandlerAsync', () => {
31
+ beforeEach(() => {
32
+ vi.mocked(listStudioProcessesAsync).mockResolvedValue([
33
+ { pid: 10, windowTitle: 'C:\\places\\test-place.rbxl - Roblox Studio' },
34
+ { pid: 20, windowTitle: 'Other - Roblox Studio' },
35
+ ]);
36
+ vi.mocked(closeStudioProcessAsync).mockReset().mockResolvedValue(true);
37
+ });
38
+
39
+ it('closes the Studio window showing the session place', async () => {
40
+ const result = await processCloseHandlerAsync(session);
41
+
42
+ expect(closeStudioProcessAsync).toHaveBeenCalledWith(10, {
43
+ force: undefined,
44
+ });
45
+ expect(result).toMatchObject({ success: true, pid: 10 });
46
+ });
47
+
48
+ it('closes the Studio a single-process playtest runs in', async () => {
49
+ const server = {
50
+ info: {
51
+ sessionId: 'test-place-server',
52
+ instanceId: 'test-place',
53
+ context: 'server',
54
+ placeName: 'Game',
55
+ },
56
+ queryStateAsync,
57
+ } as any;
58
+ const connection = {
59
+ listSessions: () => [
60
+ {
61
+ sessionId: 'test-place-server',
62
+ instanceId: 'test-place',
63
+ context: 'server',
64
+ placeName: 'test-place.rbxl',
65
+ },
66
+ ],
67
+ } as any;
68
+
69
+ const result = await processCloseHandlerAsync(server, {}, connection);
70
+
71
+ expect(result).toMatchObject({ success: true, pid: 10 });
72
+ });
73
+
74
+ it('refuses a multiplayer test process without --pid', async () => {
75
+ const client = {
76
+ info: {
77
+ sessionId: 'test-place-client-player1',
78
+ instanceId: 'test-place',
79
+ context: 'client',
80
+ placeName: 'Game',
81
+ },
82
+ queryStateAsync,
83
+ } as any;
84
+ const connection = {
85
+ listSessions: () =>
86
+ ['player1', 'player2'].map((player) => ({
87
+ sessionId: `test-place-client-${player}`,
88
+ instanceId: 'test-place',
89
+ context: 'client',
90
+ placeName: 'test-place.rbxl',
91
+ playerName: player,
92
+ })),
93
+ } as any;
94
+
95
+ const result = await processCloseHandlerAsync(client, {}, connection);
96
+
97
+ expect(result.success).toBe(false);
98
+ expect(result.summary).toContain("'studio-bridge playtest stop'");
99
+ expect(closeStudioProcessAsync).not.toHaveBeenCalled();
100
+ });
101
+
102
+ it('finds a cloud place by the place id Studio was launched with', async () => {
103
+ vi.mocked(listStudioProcessesAsync).mockResolvedValue([
104
+ {
105
+ pid: 30,
106
+ windowTitle: 'My Game (abc123) - Roblox Studio',
107
+ commandLine:
108
+ 'RobloxStudioBeta.exe roblox-studio:1+placeId:555+universeId:9',
109
+ },
110
+ ]);
111
+ const cloudSession = {
112
+ info: { sessionId: 'place1-edit', placeName: 'Place1', placeId: 0 },
113
+ queryStateAsync: async () => ({ placeId: 555 }),
114
+ } as any;
115
+
116
+ const result = await processCloseHandlerAsync(cloudSession);
117
+
118
+ expect(result).toMatchObject({ success: true, pid: 30 });
119
+ });
120
+
121
+ it('passes --force through', async () => {
122
+ await processCloseHandlerAsync(session, { force: true });
123
+
124
+ expect(closeStudioProcessAsync).toHaveBeenCalledWith(10, { force: true });
125
+ });
126
+
127
+ it('suggests --force when Studio stays open', async () => {
128
+ vi.mocked(closeStudioProcessAsync).mockResolvedValue(false);
129
+
130
+ const result = await processCloseHandlerAsync(session);
131
+
132
+ expect(result.success).toBe(false);
133
+ expect(result.summary).toContain('--force');
134
+ });
135
+
136
+ it('fails without closing when no window shows the place', async () => {
137
+ vi.mocked(listStudioProcessesAsync).mockResolvedValue([
138
+ { pid: 20, windowTitle: 'Other - Roblox Studio' },
139
+ ]);
140
+
141
+ const result = await processCloseHandlerAsync(session);
142
+
143
+ expect(result.success).toBe(false);
144
+ expect(closeStudioProcessAsync).not.toHaveBeenCalled();
145
+ });
146
+
147
+ it('asks for --pid when several windows show the place', async () => {
148
+ vi.mocked(listStudioProcessesAsync).mockResolvedValue([
149
+ { pid: 10, windowTitle: 'C:\\a\\test-place.rbxl - Roblox Studio' },
150
+ { pid: 11, windowTitle: 'C:\\b\\test-place.rbxl - Roblox Studio' },
151
+ ]);
152
+
153
+ const result = await processCloseHandlerAsync(session);
154
+
155
+ expect(result.success).toBe(false);
156
+ expect(result.summary).toContain('--pid');
157
+ expect(closeStudioProcessAsync).not.toHaveBeenCalled();
158
+ });
159
+
160
+ it('closes the process picked with --pid', async () => {
161
+ const result = await processCloseHandlerAsync(session, { pid: 20 });
162
+
163
+ expect(closeStudioProcessAsync).toHaveBeenCalledWith(20, {
164
+ force: undefined,
165
+ });
166
+ expect(result.success).toBe(true);
167
+ });
168
+ });
169
+
170
+ describe('closeDeployTargetAsync', () => {
171
+ let tempDir: string;
172
+
173
+ beforeEach(async () => {
174
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'close-test-'));
175
+ await fs.writeFile(
176
+ path.join(tempDir, 'deploy.nevermore.json'),
177
+ JSON.stringify({
178
+ targets: {
179
+ test: { universeId: 9, placeId: 555, project: 'test.project.json' },
180
+ },
181
+ })
182
+ );
183
+ vi.mocked(closeStudioProcessAsync).mockReset().mockResolvedValue(true);
184
+ });
185
+
186
+ afterEach(async () => {
187
+ await fs.rm(tempDir, { recursive: true, force: true });
188
+ });
189
+
190
+ it('closes a Studio launched with the target place, connected or not', async () => {
191
+ vi.mocked(listStudioProcessesAsync).mockResolvedValue([
192
+ {
193
+ pid: 40,
194
+ windowTitle: 'My Game (abc123) - Roblox Studio',
195
+ commandLine: 'RobloxStudioBeta.exe roblox-studio:1+placeId:555',
196
+ },
197
+ { pid: 41, windowTitle: 'Other - Roblox Studio' },
198
+ ]);
199
+ const connection = { listInstances: () => [], listSessions: () => [] };
200
+
201
+ const result = await closeDeployTargetAsync(
202
+ connection as any,
203
+ 'test',
204
+ {},
205
+ tempDir
206
+ );
207
+
208
+ expect(closeStudioProcessAsync).toHaveBeenCalledWith(40, {
209
+ force: undefined,
210
+ });
211
+ expect(result).toMatchObject({
212
+ success: true,
213
+ deployTarget: 'test',
214
+ pid: 40,
215
+ });
216
+ });
217
+
218
+ it('closes the connected Studio showing the target place', async () => {
219
+ vi.mocked(listStudioProcessesAsync).mockResolvedValue([
220
+ { pid: 50, windowTitle: 'My Game (abc123) - Roblox Studio' },
221
+ ]);
222
+ const editSession = {
223
+ info: {
224
+ sessionId: 'place1-1a2b-edit',
225
+ instanceId: 'place1-1a2b',
226
+ context: 'edit',
227
+ placeName: 'My Game (abc123)',
228
+ },
229
+ queryStateAsync: async () => ({ placeId: 555 }),
230
+ };
231
+ const connection = {
232
+ listInstances: () => [{ instanceId: 'place1-1a2b', placeId: 555 }],
233
+ listSessions: () => [editSession.info],
234
+ resolveSessionAsync: vi.fn().mockResolvedValue(editSession),
235
+ };
236
+
237
+ const result = await closeDeployTargetAsync(
238
+ connection as any,
239
+ 'test',
240
+ {},
241
+ tempDir
242
+ );
243
+
244
+ expect(connection.resolveSessionAsync).toHaveBeenCalledWith(
245
+ 'place1-1a2b',
246
+ 'edit'
247
+ );
248
+ expect(result).toMatchObject({ success: true, pid: 50 });
249
+ });
250
+ });
@@ -1,34 +1,178 @@
1
1
  /**
2
- * `process close` -- kill a Studio process (not yet implemented).
3
- *
4
- * Closing a Studio process requires matching a bridge session to an OS
5
- * process, which is non-trivial: the Roblox plugin API doesn't expose
6
- * the process ID, so the bridge would need to scan and heuristically
7
- * match OS processes. This command is stubbed until that infrastructure
8
- * exists.
2
+ * `process close` — close the Studio process a session belongs to, or the
3
+ * Studio showing a `deploy.nevermore.json` target's place.
9
4
  */
10
5
 
11
6
  import { defineCommand } from '../../framework/define-command.js';
12
7
  import { arg } from '../../framework/arg-builder.js';
13
- import type { BridgeSession } from '../../../bridge/index.js';
8
+ import type { BridgeConnection, BridgeSession } from '../../../bridge/index.js';
9
+ import {
10
+ closeStudioProcessAsync,
11
+ listStudioProcessesAsync,
12
+ matchStudioProcesses,
13
+ matchStudioProcessesByPlaceId,
14
+ type RunningStudioProcess,
15
+ } from '../../../process/studio-process-closer.js';
16
+ import {
17
+ findInstancesShowingPlaceAsync,
18
+ resolveDeployPlaceAsync,
19
+ } from '../../../deploy/deploy-places.js';
20
+ import { explainMissingPlaytestContextAsync } from '../../playtest/playtest-sessions.js';
21
+ import { describePlaytest } from '../info/info.js';
14
22
 
15
23
  export interface ProcessCloseResult {
16
24
  success: boolean;
17
- sessionId: string;
25
+ sessionId?: string;
26
+ /** The `deploy.nevermore.json` target whose Studio was closed, if one was named. */
27
+ deployTarget?: string;
28
+ pid?: number;
18
29
  summary: string;
19
30
  }
20
31
 
32
+ export interface ProcessCloseOptions {
33
+ force?: boolean;
34
+ pid?: number;
35
+ }
36
+
21
37
  interface ProcessCloseArgs {
38
+ deployTarget?: string;
22
39
  force?: boolean;
40
+ pid?: number;
23
41
  }
24
42
 
25
43
  export async function processCloseHandlerAsync(
26
- session: BridgeSession
44
+ session: BridgeSession,
45
+ options: ProcessCloseOptions = {},
46
+ connection?: BridgeConnection
27
47
  ): Promise<ProcessCloseResult> {
28
- return {
48
+ const { sessionId, instanceId, context } = session.info;
49
+ const sessions = connection?.listSessions() ?? [];
50
+ // Listed sessions carry the edit session's place name for a playtest
51
+ const placeName =
52
+ sessions.find((s) => s.sessionId === sessionId)?.placeName ||
53
+ session.info.placeName;
54
+
55
+ const isMultiplayerProcess =
56
+ context !== 'edit' &&
57
+ describePlaytest(sessions, instanceId)?.playerCount !== undefined;
58
+ if (isMultiplayerProcess && options.pid === undefined) {
59
+ return {
60
+ success: false,
61
+ sessionId,
62
+ summary: `'${sessionId}' runs in its own process of a multiplayer test. End the test with 'studio-bridge playtest stop', remove a player with 'studio-bridge playtest player remove', or pass --pid.`,
63
+ };
64
+ }
65
+
66
+ const processes = await listStudioProcessesAsync();
67
+ let candidates =
68
+ options.pid !== undefined
69
+ ? processes.filter((proc) => proc.pid === options.pid)
70
+ : matchStudioProcesses(processes, placeName);
71
+ if (candidates.length === 0 && options.pid === undefined) {
72
+ // A cloud place loads after the plugin registers, so its registered id can be 0
73
+ const placeId = (await session.queryStateAsync().catch(() => undefined))
74
+ ?.placeId;
75
+ candidates = matchStudioProcesses(processes, placeName, placeId);
76
+ }
77
+
78
+ return closeCandidateAsync(candidates, `'${placeName}'`, options, {
79
+ sessionId,
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Close the Studio showing a target's place from the nearest
85
+ * `deploy.nevermore.json`: a connected one, or else one launched with it.
86
+ */
87
+ export async function closeDeployTargetAsync(
88
+ connection: BridgeConnection,
89
+ deployTarget: string,
90
+ options: ProcessCloseOptions = {},
91
+ cwd = process.cwd()
92
+ ): Promise<ProcessCloseResult> {
93
+ const deployPlace = await resolveDeployPlaceAsync(cwd, deployTarget);
94
+ if (!deployPlace) {
95
+ throw new Error(`Target '${deployTarget}' not found`);
96
+ }
97
+ const { placeId } = deployPlace.place;
98
+ const showing = `'${deployPlace.selector}' (place ${placeId})`;
99
+ const base = { deployTarget: deployPlace.selector };
100
+
101
+ const instanceIds = await findInstancesShowingPlaceAsync(connection, placeId);
102
+ if (instanceIds.length > 1) {
103
+ return {
104
+ ...base,
105
+ success: false,
106
+ summary: `${
107
+ instanceIds.length
108
+ } Studios have ${showing} open (${instanceIds.join(
109
+ ', '
110
+ )}). Pick one with --target.`,
111
+ };
112
+ }
113
+ if (instanceIds.length === 1) {
114
+ const session = await connection.resolveSessionAsync(
115
+ instanceIds[0],
116
+ 'edit'
117
+ );
118
+ return {
119
+ ...(await processCloseHandlerAsync(session, options, connection)),
120
+ ...base,
121
+ };
122
+ }
123
+
124
+ const processes = await listStudioProcessesAsync();
125
+ const candidates =
126
+ options.pid !== undefined
127
+ ? processes.filter((proc) => proc.pid === options.pid)
128
+ : matchStudioProcessesByPlaceId(processes, placeId);
129
+ return closeCandidateAsync(candidates, showing, options, base);
130
+ }
131
+
132
+ async function closeCandidateAsync(
133
+ candidates: RunningStudioProcess[],
134
+ showing: string,
135
+ options: ProcessCloseOptions,
136
+ base: Pick<ProcessCloseResult, 'sessionId' | 'deployTarget'>
137
+ ): Promise<ProcessCloseResult> {
138
+ const fail = (summary: string, pid?: number): ProcessCloseResult => ({
139
+ ...base,
29
140
  success: false,
30
- sessionId: session.info.sessionId,
31
- summary: `process close is not yet implemented. Closing a Studio process requires OS process scanning which has not been built yet.`,
141
+ pid,
142
+ summary,
143
+ });
144
+
145
+ if (candidates.length === 0) {
146
+ return fail(
147
+ options.pid !== undefined
148
+ ? `No Studio process with pid ${options.pid}`
149
+ : `No Studio window is showing ${showing}`
150
+ );
151
+ }
152
+
153
+ if (candidates.length > 1) {
154
+ const pids = candidates.map((proc) => proc.pid).join(', ');
155
+ return fail(
156
+ `${candidates.length} Studio windows are showing ${showing} (pids ${pids}). Pick one with --pid.`
157
+ );
158
+ }
159
+
160
+ const { pid } = candidates[0];
161
+ const exited = await closeStudioProcessAsync(pid, { force: options.force });
162
+ if (!exited) {
163
+ return fail(
164
+ options.force
165
+ ? `Studio (pid ${pid}) did not exit`
166
+ : `Studio (pid ${pid}) is still open, likely asking to save. Use --force to close without saving.`,
167
+ pid
168
+ );
169
+ }
170
+
171
+ return {
172
+ ...base,
173
+ success: true,
174
+ pid,
175
+ summary: `Closed Studio (pid ${pid}) showing ${showing}`,
32
176
  };
33
177
  }
34
178
 
@@ -38,15 +182,37 @@ export const processCloseCommand = defineCommand<
38
182
  >({
39
183
  group: 'process',
40
184
  name: 'close',
41
- description: 'Kill a Studio process (not yet implemented)',
185
+ description:
186
+ "Close a Studio: the one a session belongs to, or the one showing a deploy.nevermore.json target's place",
42
187
  category: 'infrastructure',
43
188
  safety: 'mutate',
44
- scope: 'session',
189
+ scope: 'connection',
45
190
  args: {
191
+ deployTarget: arg.positional({
192
+ description:
193
+ 'deploy.nevermore.json target whose Studio to close, e.g. test or integration.places.hub',
194
+ required: false,
195
+ }),
46
196
  force: arg.flag({
47
- description: 'Force shutdown without confirmation',
197
+ description: 'Kill Studio without letting it prompt to save',
48
198
  alias: 'f',
49
199
  }),
200
+ pid: arg.option({
201
+ description: 'Studio process id, when several windows show the place',
202
+ type: 'number',
203
+ }),
204
+ },
205
+ handler: async (connection, args, targeting) => {
206
+ const options = { force: args.force, pid: args.pid };
207
+ if (args.deployTarget !== undefined) {
208
+ return closeDeployTargetAsync(connection, args.deployTarget, options);
209
+ }
210
+
211
+ const session = await connection
212
+ .resolveSessionAsync(targeting.target, targeting.context)
213
+ .catch(async (err: unknown) => {
214
+ throw await explainMissingPlaytestContextAsync(connection, err);
215
+ });
216
+ return processCloseHandlerAsync(session, options, connection);
50
217
  },
51
- handler: async (session) => processCloseHandlerAsync(session),
52
218
  });