@miosa/cli 0.2.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 (294) hide show
  1. package/README.md +327 -0
  2. package/dist/bin/miosa.d.ts +3 -0
  3. package/dist/bin/miosa.d.ts.map +1 -0
  4. package/dist/bin/miosa.js +139 -0
  5. package/dist/bin/miosa.js.map +1 -0
  6. package/dist/client.d.ts +74 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +523 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/commands/agent.d.ts +18 -0
  11. package/dist/commands/agent.d.ts.map +1 -0
  12. package/dist/commands/agent.js +468 -0
  13. package/dist/commands/agent.js.map +1 -0
  14. package/dist/commands/alerts.d.ts +3 -0
  15. package/dist/commands/alerts.d.ts.map +1 -0
  16. package/dist/commands/alerts.js +41 -0
  17. package/dist/commands/alerts.js.map +1 -0
  18. package/dist/commands/api-keys.d.ts +3 -0
  19. package/dist/commands/api-keys.d.ts.map +1 -0
  20. package/dist/commands/api-keys.js +119 -0
  21. package/dist/commands/api-keys.js.map +1 -0
  22. package/dist/commands/api-resource.d.ts +20 -0
  23. package/dist/commands/api-resource.d.ts.map +1 -0
  24. package/dist/commands/api-resource.js +120 -0
  25. package/dist/commands/api-resource.js.map +1 -0
  26. package/dist/commands/apps.d.ts +3 -0
  27. package/dist/commands/apps.d.ts.map +1 -0
  28. package/dist/commands/apps.js +218 -0
  29. package/dist/commands/apps.js.map +1 -0
  30. package/dist/commands/audit.d.ts +3 -0
  31. package/dist/commands/audit.d.ts.map +1 -0
  32. package/dist/commands/audit.js +25 -0
  33. package/dist/commands/audit.js.map +1 -0
  34. package/dist/commands/auth.d.ts +3 -0
  35. package/dist/commands/auth.d.ts.map +1 -0
  36. package/dist/commands/auth.js +363 -0
  37. package/dist/commands/auth.js.map +1 -0
  38. package/dist/commands/backups.d.ts +3 -0
  39. package/dist/commands/backups.d.ts.map +1 -0
  40. package/dist/commands/backups.js +23 -0
  41. package/dist/commands/backups.js.map +1 -0
  42. package/dist/commands/checkpoints.d.ts +3 -0
  43. package/dist/commands/checkpoints.d.ts.map +1 -0
  44. package/dist/commands/checkpoints.js +33 -0
  45. package/dist/commands/checkpoints.js.map +1 -0
  46. package/dist/commands/computers.d.ts +3 -0
  47. package/dist/commands/computers.d.ts.map +1 -0
  48. package/dist/commands/computers.js +118 -0
  49. package/dist/commands/computers.js.map +1 -0
  50. package/dist/commands/config.d.ts +3 -0
  51. package/dist/commands/config.d.ts.map +1 -0
  52. package/dist/commands/config.js +114 -0
  53. package/dist/commands/config.js.map +1 -0
  54. package/dist/commands/connect.d.ts +3 -0
  55. package/dist/commands/connect.d.ts.map +1 -0
  56. package/dist/commands/connect.js +96 -0
  57. package/dist/commands/connect.js.map +1 -0
  58. package/dist/commands/containers.d.ts +3 -0
  59. package/dist/commands/containers.d.ts.map +1 -0
  60. package/dist/commands/containers.js +20 -0
  61. package/dist/commands/containers.js.map +1 -0
  62. package/dist/commands/cp.d.ts +3 -0
  63. package/dist/commands/cp.d.ts.map +1 -0
  64. package/dist/commands/cp.js +102 -0
  65. package/dist/commands/cp.js.map +1 -0
  66. package/dist/commands/cron.d.ts +3 -0
  67. package/dist/commands/cron.d.ts.map +1 -0
  68. package/dist/commands/cron.js +65 -0
  69. package/dist/commands/cron.js.map +1 -0
  70. package/dist/commands/databases.d.ts +3 -0
  71. package/dist/commands/databases.d.ts.map +1 -0
  72. package/dist/commands/databases.js +222 -0
  73. package/dist/commands/databases.js.map +1 -0
  74. package/dist/commands/db.d.ts +3 -0
  75. package/dist/commands/db.d.ts.map +1 -0
  76. package/dist/commands/db.js +174 -0
  77. package/dist/commands/db.js.map +1 -0
  78. package/dist/commands/deploy.d.ts +3 -0
  79. package/dist/commands/deploy.d.ts.map +1 -0
  80. package/dist/commands/deploy.js +579 -0
  81. package/dist/commands/deploy.js.map +1 -0
  82. package/dist/commands/desktop.d.ts +3 -0
  83. package/dist/commands/desktop.d.ts.map +1 -0
  84. package/dist/commands/desktop.js +276 -0
  85. package/dist/commands/desktop.js.map +1 -0
  86. package/dist/commands/dev.d.ts +3 -0
  87. package/dist/commands/dev.d.ts.map +1 -0
  88. package/dist/commands/dev.js +246 -0
  89. package/dist/commands/dev.js.map +1 -0
  90. package/dist/commands/doctor.d.ts +3 -0
  91. package/dist/commands/doctor.d.ts.map +1 -0
  92. package/dist/commands/doctor.js +241 -0
  93. package/dist/commands/doctor.js.map +1 -0
  94. package/dist/commands/domains.d.ts +3 -0
  95. package/dist/commands/domains.d.ts.map +1 -0
  96. package/dist/commands/domains.js +31 -0
  97. package/dist/commands/domains.js.map +1 -0
  98. package/dist/commands/enterprise-util.d.ts +37 -0
  99. package/dist/commands/enterprise-util.d.ts.map +1 -0
  100. package/dist/commands/enterprise-util.js +185 -0
  101. package/dist/commands/enterprise-util.js.map +1 -0
  102. package/dist/commands/exec.d.ts +3 -0
  103. package/dist/commands/exec.d.ts.map +1 -0
  104. package/dist/commands/exec.js +68 -0
  105. package/dist/commands/exec.js.map +1 -0
  106. package/dist/commands/functions.d.ts +3 -0
  107. package/dist/commands/functions.d.ts.map +1 -0
  108. package/dist/commands/functions.js +47 -0
  109. package/dist/commands/functions.js.map +1 -0
  110. package/dist/commands/gha-runners.d.ts +3 -0
  111. package/dist/commands/gha-runners.d.ts.map +1 -0
  112. package/dist/commands/gha-runners.js +33 -0
  113. package/dist/commands/gha-runners.js.map +1 -0
  114. package/dist/commands/groups.d.ts +3 -0
  115. package/dist/commands/groups.d.ts.map +1 -0
  116. package/dist/commands/groups.js +38 -0
  117. package/dist/commands/groups.js.map +1 -0
  118. package/dist/commands/host.d.ts +3 -0
  119. package/dist/commands/host.d.ts.map +1 -0
  120. package/dist/commands/host.js +74 -0
  121. package/dist/commands/host.js.map +1 -0
  122. package/dist/commands/hosts.d.ts +3 -0
  123. package/dist/commands/hosts.d.ts.map +1 -0
  124. package/dist/commands/hosts.js +90 -0
  125. package/dist/commands/hosts.js.map +1 -0
  126. package/dist/commands/link.d.ts +8 -0
  127. package/dist/commands/link.d.ts.map +1 -0
  128. package/dist/commands/link.js +124 -0
  129. package/dist/commands/link.js.map +1 -0
  130. package/dist/commands/login.d.ts +3 -0
  131. package/dist/commands/login.d.ts.map +1 -0
  132. package/dist/commands/login.js +172 -0
  133. package/dist/commands/login.js.map +1 -0
  134. package/dist/commands/logout.d.ts +3 -0
  135. package/dist/commands/logout.d.ts.map +1 -0
  136. package/dist/commands/logout.js +17 -0
  137. package/dist/commands/logout.js.map +1 -0
  138. package/dist/commands/logs.d.ts +3 -0
  139. package/dist/commands/logs.d.ts.map +1 -0
  140. package/dist/commands/logs.js +94 -0
  141. package/dist/commands/logs.js.map +1 -0
  142. package/dist/commands/ls.d.ts +3 -0
  143. package/dist/commands/ls.d.ts.map +1 -0
  144. package/dist/commands/ls.js +67 -0
  145. package/dist/commands/ls.js.map +1 -0
  146. package/dist/commands/machines.d.ts +3 -0
  147. package/dist/commands/machines.d.ts.map +1 -0
  148. package/dist/commands/machines.js +29 -0
  149. package/dist/commands/machines.js.map +1 -0
  150. package/dist/commands/mcp.d.ts +21 -0
  151. package/dist/commands/mcp.d.ts.map +1 -0
  152. package/dist/commands/mcp.js +1021 -0
  153. package/dist/commands/mcp.js.map +1 -0
  154. package/dist/commands/meshes.d.ts +3 -0
  155. package/dist/commands/meshes.d.ts.map +1 -0
  156. package/dist/commands/meshes.js +27 -0
  157. package/dist/commands/meshes.js.map +1 -0
  158. package/dist/commands/network-policy.d.ts +3 -0
  159. package/dist/commands/network-policy.d.ts.map +1 -0
  160. package/dist/commands/network-policy.js +40 -0
  161. package/dist/commands/network-policy.js.map +1 -0
  162. package/dist/commands/project.d.ts +4 -0
  163. package/dist/commands/project.d.ts.map +1 -0
  164. package/dist/commands/project.js +25 -0
  165. package/dist/commands/project.js.map +1 -0
  166. package/dist/commands/pull.d.ts +3 -0
  167. package/dist/commands/pull.d.ts.map +1 -0
  168. package/dist/commands/pull.js +155 -0
  169. package/dist/commands/pull.js.map +1 -0
  170. package/dist/commands/regions.d.ts +3 -0
  171. package/dist/commands/regions.d.ts.map +1 -0
  172. package/dist/commands/regions.js +67 -0
  173. package/dist/commands/regions.js.map +1 -0
  174. package/dist/commands/releases.d.ts +3 -0
  175. package/dist/commands/releases.d.ts.map +1 -0
  176. package/dist/commands/releases.js +176 -0
  177. package/dist/commands/releases.js.map +1 -0
  178. package/dist/commands/rm.d.ts +3 -0
  179. package/dist/commands/rm.d.ts.map +1 -0
  180. package/dist/commands/rm.js +42 -0
  181. package/dist/commands/rm.js.map +1 -0
  182. package/dist/commands/run.d.ts +3 -0
  183. package/dist/commands/run.d.ts.map +1 -0
  184. package/dist/commands/run.js +131 -0
  185. package/dist/commands/run.js.map +1 -0
  186. package/dist/commands/sandbox.d.ts +3 -0
  187. package/dist/commands/sandbox.d.ts.map +1 -0
  188. package/dist/commands/sandbox.js +352 -0
  189. package/dist/commands/sandbox.js.map +1 -0
  190. package/dist/commands/schedules.d.ts +3 -0
  191. package/dist/commands/schedules.d.ts.map +1 -0
  192. package/dist/commands/schedules.js +37 -0
  193. package/dist/commands/schedules.js.map +1 -0
  194. package/dist/commands/secrets.d.ts +3 -0
  195. package/dist/commands/secrets.d.ts.map +1 -0
  196. package/dist/commands/secrets.js +194 -0
  197. package/dist/commands/secrets.js.map +1 -0
  198. package/dist/commands/services.d.ts +3 -0
  199. package/dist/commands/services.d.ts.map +1 -0
  200. package/dist/commands/services.js +70 -0
  201. package/dist/commands/services.js.map +1 -0
  202. package/dist/commands/shell.d.ts +16 -0
  203. package/dist/commands/shell.d.ts.map +1 -0
  204. package/dist/commands/shell.js +527 -0
  205. package/dist/commands/shell.js.map +1 -0
  206. package/dist/commands/snapshot.d.ts +10 -0
  207. package/dist/commands/snapshot.d.ts.map +1 -0
  208. package/dist/commands/snapshot.js +181 -0
  209. package/dist/commands/snapshot.js.map +1 -0
  210. package/dist/commands/ssh.d.ts +3 -0
  211. package/dist/commands/ssh.d.ts.map +1 -0
  212. package/dist/commands/ssh.js +37 -0
  213. package/dist/commands/ssh.js.map +1 -0
  214. package/dist/commands/status.d.ts +3 -0
  215. package/dist/commands/status.d.ts.map +1 -0
  216. package/dist/commands/status.js +300 -0
  217. package/dist/commands/status.js.map +1 -0
  218. package/dist/commands/storage.d.ts +3 -0
  219. package/dist/commands/storage.d.ts.map +1 -0
  220. package/dist/commands/storage.js +180 -0
  221. package/dist/commands/storage.js.map +1 -0
  222. package/dist/commands/tenant.d.ts +3 -0
  223. package/dist/commands/tenant.d.ts.map +1 -0
  224. package/dist/commands/tenant.js +87 -0
  225. package/dist/commands/tenant.js.map +1 -0
  226. package/dist/commands/tunnel.d.ts +3 -0
  227. package/dist/commands/tunnel.d.ts.map +1 -0
  228. package/dist/commands/tunnel.js +418 -0
  229. package/dist/commands/tunnel.js.map +1 -0
  230. package/dist/commands/up.d.ts +14 -0
  231. package/dist/commands/up.d.ts.map +1 -0
  232. package/dist/commands/up.js +703 -0
  233. package/dist/commands/up.js.map +1 -0
  234. package/dist/commands/util.d.ts +19 -0
  235. package/dist/commands/util.d.ts.map +1 -0
  236. package/dist/commands/util.js +116 -0
  237. package/dist/commands/util.js.map +1 -0
  238. package/dist/commands/volumes.d.ts +3 -0
  239. package/dist/commands/volumes.d.ts.map +1 -0
  240. package/dist/commands/volumes.js +196 -0
  241. package/dist/commands/volumes.js.map +1 -0
  242. package/dist/commands/watch.d.ts +3 -0
  243. package/dist/commands/watch.d.ts.map +1 -0
  244. package/dist/commands/watch.js +398 -0
  245. package/dist/commands/watch.js.map +1 -0
  246. package/dist/commands/webhooks.d.ts +3 -0
  247. package/dist/commands/webhooks.d.ts.map +1 -0
  248. package/dist/commands/webhooks.js +23 -0
  249. package/dist/commands/webhooks.js.map +1 -0
  250. package/dist/commands/whoami.d.ts +3 -0
  251. package/dist/commands/whoami.d.ts.map +1 -0
  252. package/dist/commands/whoami.js +84 -0
  253. package/dist/commands/whoami.js.map +1 -0
  254. package/dist/commands/workspaces.d.ts +3 -0
  255. package/dist/commands/workspaces.d.ts.map +1 -0
  256. package/dist/commands/workspaces.js +87 -0
  257. package/dist/commands/workspaces.js.map +1 -0
  258. package/dist/config.d.ts +28 -0
  259. package/dist/config.d.ts.map +1 -0
  260. package/dist/config.js +129 -0
  261. package/dist/config.js.map +1 -0
  262. package/dist/errors.d.ts +22 -0
  263. package/dist/errors.d.ts.map +1 -0
  264. package/dist/errors.js +62 -0
  265. package/dist/errors.js.map +1 -0
  266. package/dist/framework-detector.d.ts +22 -0
  267. package/dist/framework-detector.d.ts.map +1 -0
  268. package/dist/framework-detector.js +373 -0
  269. package/dist/framework-detector.js.map +1 -0
  270. package/dist/pty/raw-mode.d.ts +7 -0
  271. package/dist/pty/raw-mode.d.ts.map +1 -0
  272. package/dist/pty/raw-mode.js +22 -0
  273. package/dist/pty/raw-mode.js.map +1 -0
  274. package/dist/pty/ws-pty-client.d.ts +12 -0
  275. package/dist/pty/ws-pty-client.d.ts.map +1 -0
  276. package/dist/pty/ws-pty-client.js +69 -0
  277. package/dist/pty/ws-pty-client.js.map +1 -0
  278. package/dist/types.d.ts +326 -0
  279. package/dist/types.d.ts.map +1 -0
  280. package/dist/types.js +16 -0
  281. package/dist/types.js.map +1 -0
  282. package/dist/ui/progress.d.ts +10 -0
  283. package/dist/ui/progress.d.ts.map +1 -0
  284. package/dist/ui/progress.js +36 -0
  285. package/dist/ui/progress.js.map +1 -0
  286. package/dist/ui/spinner.d.ts +4 -0
  287. package/dist/ui/spinner.d.ts.map +1 -0
  288. package/dist/ui/spinner.js +7 -0
  289. package/dist/ui/spinner.js.map +1 -0
  290. package/dist/ui/table.d.ts +8 -0
  291. package/dist/ui/table.d.ts.map +1 -0
  292. package/dist/ui/table.js +46 -0
  293. package/dist/ui/table.js.map +1 -0
  294. package/package.json +53 -0
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function register(program: Command): void;
3
+ //# sourceMappingURL=services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/commands/services.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYzC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6H/C"}
@@ -0,0 +1,70 @@
1
+ import { addDataOption, deleteAndPrint, enc, getAndPrint, postAndPrint, runAction, } from "./enterprise-util.js";
2
+ export function register(program) {
3
+ const services = program
4
+ .command("services")
5
+ .description("Manage long-running services on Computers");
6
+ // services list <computer-id>
7
+ services
8
+ .command("list <computer-id>")
9
+ .description("List all services on a Computer")
10
+ .option("--json", "Output as JSON")
11
+ .action((id, opts) => runAction(() => getAndPrint(`/computers/${enc(id)}/services`, opts)));
12
+ // services create <computer-id> --name X --command "..."
13
+ addDataOption(services
14
+ .command("create <computer-id>")
15
+ .description("Create a service on a Computer")
16
+ .option("--name <name>", "Service name")
17
+ .option("--command <cmd>", "Command to run")
18
+ .option("--working-dir <dir>", "Working directory")
19
+ .option("--port <port>", "Port the service listens on"))
20
+ .option("--json", "Output as JSON")
21
+ .action((id, opts) => runAction(() => {
22
+ const flagBody = {};
23
+ if (opts.name)
24
+ flagBody["name"] = opts.name;
25
+ if (opts.command)
26
+ flagBody["command"] = opts.command;
27
+ if (opts.workingDir)
28
+ flagBody["working_dir"] = opts.workingDir;
29
+ if (opts.port)
30
+ flagBody["port"] = Number(opts.port);
31
+ return postAndPrint(`/computers/${enc(id)}/services`, opts, flagBody);
32
+ }));
33
+ // services show <computer-id> <service-id>
34
+ services
35
+ .command("show <computer-id> <service-id>")
36
+ .description("Show a service")
37
+ .option("--json", "Output as JSON")
38
+ .action((id, serviceId, opts) => runAction(() => getAndPrint(`/computers/${enc(id)}/services/${enc(serviceId)}`, opts)));
39
+ // services start <computer-id> <service-id>
40
+ services
41
+ .command("start <computer-id> <service-id>")
42
+ .description("Start a stopped service")
43
+ .option("--json", "Output as JSON")
44
+ .action((id, serviceId, opts) => runAction(() => postAndPrint(`/computers/${enc(id)}/services/${enc(serviceId)}/start`, opts)));
45
+ // services stop <computer-id> <service-id>
46
+ services
47
+ .command("stop <computer-id> <service-id>")
48
+ .description("Stop a running service")
49
+ .option("--json", "Output as JSON")
50
+ .action((id, serviceId, opts) => runAction(() => postAndPrint(`/computers/${enc(id)}/services/${enc(serviceId)}/stop`, opts)));
51
+ // services restart <computer-id> <service-id>
52
+ services
53
+ .command("restart <computer-id> <service-id>")
54
+ .description("Restart a service")
55
+ .option("--json", "Output as JSON")
56
+ .action((id, serviceId, opts) => runAction(() => postAndPrint(`/computers/${enc(id)}/services/${enc(serviceId)}/restart`, opts)));
57
+ // services logs <computer-id> <service-id>
58
+ services
59
+ .command("logs <computer-id> <service-id>")
60
+ .description("Show service logs")
61
+ .option("--json", "Output as JSON")
62
+ .action((id, serviceId, opts) => runAction(() => getAndPrint(`/computers/${enc(id)}/services/${enc(serviceId)}/logs`, opts)));
63
+ // services delete <computer-id> <service-id>
64
+ services
65
+ .command("delete <computer-id> <service-id>")
66
+ .description("Delete a service (stops it first if running)")
67
+ .option("--json", "Output as JSON")
68
+ .action((id, serviceId, opts) => runAction(() => deleteAndPrint(`/computers/${enc(id)}/services/${enc(serviceId)}`, opts)));
69
+ }
70
+ //# sourceMappingURL=services.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.js","sourceRoot":"","sources":["../../src/commands/services.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * miosa shell <computer-id>
3
+ *
4
+ * Hybrid interactive session: PTY for shell commands + desktop API for
5
+ * `desktop <action>` lines. Unique to MIOSA — SSH and desktop control in one.
6
+ *
7
+ * PTY lifecycle:
8
+ * POST /api/v1/computers/{id}/terminal → { id, ws_url }
9
+ * WebSocket ws_url → raw terminal I/O
10
+ *
11
+ * Desktop commands are intercepted client-side and dispatched to:
12
+ * /api/v1/computers/{id}/desktop/{action}
13
+ */
14
+ import type { Command } from "commander";
15
+ export declare function register(program: Command): void;
16
+ //# sourceMappingURL=shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/commands/shell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAynBzC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuF/C"}
@@ -0,0 +1,527 @@
1
+ /**
2
+ * miosa shell <computer-id>
3
+ *
4
+ * Hybrid interactive session: PTY for shell commands + desktop API for
5
+ * `desktop <action>` lines. Unique to MIOSA — SSH and desktop control in one.
6
+ *
7
+ * PTY lifecycle:
8
+ * POST /api/v1/computers/{id}/terminal → { id, ws_url }
9
+ * WebSocket ws_url → raw terminal I/O
10
+ *
11
+ * Desktop commands are intercepted client-side and dispatched to:
12
+ * /api/v1/computers/{id}/desktop/{action}
13
+ */
14
+ import { writeFileSync } from "node:fs";
15
+ import { tmpdir } from "node:os";
16
+ import { join } from "node:path";
17
+ import process from "node:process";
18
+ import WebSocket from "ws";
19
+ import chalk from "chalk";
20
+ import { request } from "undici";
21
+ import { loadConfig } from "../config.js";
22
+ import { MiosaClient } from "../client.js";
23
+ import { handleError } from "./util.js";
24
+ import { spin } from "../ui/spinner.js";
25
+ import { getTerminalSize } from "../pty/raw-mode.js";
26
+ import { NetworkError, mapHttpError } from "../errors.js";
27
+ // ---------------------------------------------------------------------------
28
+ // PTY creation
29
+ // ---------------------------------------------------------------------------
30
+ async function createPty(endpoint, apiKey, computerId) {
31
+ let res;
32
+ try {
33
+ res = await request(`${endpoint}/api/v1/computers/${encodeURIComponent(computerId)}/terminal`, {
34
+ method: "POST",
35
+ headers: {
36
+ Authorization: `Bearer ${apiKey}`,
37
+ "Content-Type": "application/json",
38
+ "User-Agent": "@miosa/cli/0.1.0",
39
+ },
40
+ body: JSON.stringify({
41
+ cmd: "/bin/bash",
42
+ env: { TERM: "xterm-256color" },
43
+ }),
44
+ });
45
+ }
46
+ catch (err) {
47
+ throw new NetworkError(`Network error creating PTY: ${err instanceof Error ? err.message : String(err)}`, "Check your connection and endpoint: miosa status");
48
+ }
49
+ if (res.statusCode >= 400) {
50
+ const raw = await res.body.text();
51
+ let body = {};
52
+ try {
53
+ body = JSON.parse(raw);
54
+ }
55
+ catch {
56
+ body = { message: raw || `HTTP ${res.statusCode}` };
57
+ }
58
+ throw mapHttpError(res.statusCode, body, raw);
59
+ }
60
+ const payload = (await res.body.json());
61
+ // Support both wrapped {data: {...}} and flat responses
62
+ const ticket = payload.data ??
63
+ (payload.id && payload.ws_url
64
+ ? { id: payload.id, ws_url: payload.ws_url }
65
+ : (() => {
66
+ throw new Error("Invalid PTY response: missing id or ws_url");
67
+ })());
68
+ return ticket;
69
+ }
70
+ // ---------------------------------------------------------------------------
71
+ // Desktop API dispatch
72
+ // ---------------------------------------------------------------------------
73
+ async function desktopRequest(endpoint, apiKey, computerId, sub, body) {
74
+ const method = body !== undefined ? "POST" : "GET";
75
+ let res;
76
+ try {
77
+ res = await request(`${endpoint}/api/v1/computers/${encodeURIComponent(computerId)}/desktop/${sub}`, {
78
+ method,
79
+ headers: {
80
+ Authorization: `Bearer ${apiKey}`,
81
+ "Content-Type": "application/json",
82
+ "User-Agent": "@miosa/cli/0.1.0",
83
+ },
84
+ body: body !== undefined ? JSON.stringify(body) : undefined,
85
+ });
86
+ }
87
+ catch (err) {
88
+ throw new NetworkError(`Network error calling desktop/${sub}: ${err instanceof Error ? err.message : String(err)}`);
89
+ }
90
+ if (res.statusCode >= 400) {
91
+ const raw = await res.body.text();
92
+ let errBody = {};
93
+ try {
94
+ errBody = JSON.parse(raw);
95
+ }
96
+ catch {
97
+ errBody = { message: raw || `HTTP ${res.statusCode}` };
98
+ }
99
+ throw mapHttpError(res.statusCode, errBody, raw);
100
+ }
101
+ // Screenshot returns binary — we return the raw buffer for that path
102
+ if (sub === "screenshot") {
103
+ const chunks = [];
104
+ for await (const chunk of res.body) {
105
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
106
+ }
107
+ return Buffer.concat(chunks);
108
+ }
109
+ return res.body.json();
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Desktop command parser
113
+ // ---------------------------------------------------------------------------
114
+ function parseDesktopCommand(line) {
115
+ // line is already stripped of leading "desktop "
116
+ const parts = line.trim().split(/\s+/);
117
+ const sub = parts[0] ?? "";
118
+ switch (sub) {
119
+ case "screenshot":
120
+ return { action: "screenshot" };
121
+ case "click": {
122
+ const x = Number(parts[1]);
123
+ const y = Number(parts[2]);
124
+ const button = parts[3] ?? "left";
125
+ if (isNaN(x) || isNaN(y))
126
+ return { action: "unknown", raw: line };
127
+ return { action: "click", x, y, button };
128
+ }
129
+ case "type": {
130
+ // Reconstruct text: everything after "type ", strip optional outer quotes
131
+ const raw = line.slice(sub.length).trim();
132
+ const text = (raw.startsWith('"') && raw.endsWith('"')) ||
133
+ (raw.startsWith("'") && raw.endsWith("'"))
134
+ ? raw.slice(1, -1)
135
+ : raw;
136
+ return { action: "type", text };
137
+ }
138
+ case "open": {
139
+ const app = parts.slice(1).join(" ");
140
+ if (!app)
141
+ return { action: "unknown", raw: line };
142
+ return { action: "open", app };
143
+ }
144
+ case "windows":
145
+ return { action: "windows" };
146
+ default:
147
+ return { action: "unknown", raw: line };
148
+ }
149
+ }
150
+ // ---------------------------------------------------------------------------
151
+ // Desktop command handler — called from the interactive REPL
152
+ // ---------------------------------------------------------------------------
153
+ async function handleDesktopCommand(cmd, endpoint, apiKey, computerId, json) {
154
+ switch (cmd.action) {
155
+ case "screenshot": {
156
+ const png = (await desktopRequest(endpoint, apiKey, computerId, "screenshot"));
157
+ const ts = Date.now();
158
+ const outPath = join(tmpdir(), `miosa-screenshot-${ts}.png`);
159
+ writeFileSync(outPath, png);
160
+ if (json) {
161
+ process.stdout.write(JSON.stringify({
162
+ saved: outPath,
163
+ bytes: png.length,
164
+ timestamp: ts,
165
+ }) + "\n");
166
+ }
167
+ else {
168
+ process.stdout.write(chalk.green(`Screenshot saved to ${outPath}\n`));
169
+ }
170
+ break;
171
+ }
172
+ case "click": {
173
+ const result = await desktopRequest(endpoint, apiKey, computerId, "click", {
174
+ x: cmd.x,
175
+ y: cmd.y,
176
+ button: cmd.button,
177
+ });
178
+ if (json) {
179
+ process.stdout.write(JSON.stringify(result) + "\n");
180
+ }
181
+ else {
182
+ process.stdout.write(chalk.green(`Clicked at (${cmd.x}, ${cmd.y}) [${cmd.button}]\n`));
183
+ }
184
+ break;
185
+ }
186
+ case "type": {
187
+ const result = await desktopRequest(endpoint, apiKey, computerId, "type", {
188
+ text: cmd.text,
189
+ });
190
+ if (json) {
191
+ process.stdout.write(JSON.stringify(result) + "\n");
192
+ }
193
+ else {
194
+ process.stdout.write(chalk.green(`Typed: ${cmd.text}\n`));
195
+ }
196
+ break;
197
+ }
198
+ case "open": {
199
+ const result = await desktopRequest(endpoint, apiKey, computerId, "launch", { app: cmd.app });
200
+ if (json) {
201
+ process.stdout.write(JSON.stringify(result) + "\n");
202
+ }
203
+ else {
204
+ process.stdout.write(chalk.green(`Launched ${cmd.app}\n`));
205
+ }
206
+ break;
207
+ }
208
+ case "windows": {
209
+ const result = await desktopRequest(endpoint, apiKey, computerId, "windows");
210
+ if (json) {
211
+ process.stdout.write(JSON.stringify(result) + "\n");
212
+ }
213
+ else {
214
+ // Pretty-print: result is likely an array of window objects
215
+ if (Array.isArray(result)) {
216
+ if (result.length === 0) {
217
+ process.stdout.write(chalk.dim("No open windows.\n"));
218
+ }
219
+ else {
220
+ for (const w of result) {
221
+ const title = String(w["title"] ?? w["name"] ?? "(untitled)");
222
+ const id = w["id"] !== undefined ? ` [${String(w["id"])}]` : "";
223
+ process.stdout.write(` ${title}${id}\n`);
224
+ }
225
+ }
226
+ }
227
+ else {
228
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
229
+ }
230
+ }
231
+ break;
232
+ }
233
+ case "unknown":
234
+ process.stdout.write(chalk.yellow(`Unknown desktop command: ${cmd.raw}\n` +
235
+ `Available: screenshot | click X Y [button] | type "text" | open APP | windows\n`));
236
+ break;
237
+ }
238
+ }
239
+ // ---------------------------------------------------------------------------
240
+ // Help text for the interactive session
241
+ // ---------------------------------------------------------------------------
242
+ function printSessionHelp(computerName) {
243
+ process.stdout.write(chalk.dim(`Connected to ${computerName}. Type commands normally. Special commands:\n` +
244
+ ` desktop screenshot - take screenshot (saved to /tmp/)\n` +
245
+ ` desktop click X Y - click at coordinates\n` +
246
+ ` desktop click X Y right - right-click at coordinates\n` +
247
+ ` desktop type "text" - type text on desktop\n` +
248
+ ` desktop open APP - launch an application\n` +
249
+ ` desktop windows - list open windows\n` +
250
+ ` exit - disconnect\n\n`));
251
+ }
252
+ // ---------------------------------------------------------------------------
253
+ // Connection banner
254
+ // ---------------------------------------------------------------------------
255
+ function formatBanner(computer) {
256
+ const name = computer.name || computer.id;
257
+ const image = computer.image ?? "unknown";
258
+ const cpuCount = computer.cpu_count ?? computer.vcpu ?? computer.cpu ?? null;
259
+ const ramMb = computer.ram_mb ?? computer.memory_mb ?? null;
260
+ const ram = ramMb !== null ? `${Math.round(ramMb / 1024)}GB RAM` : null;
261
+ const cpu = cpuCount !== null ? `${cpuCount} CPU` : null;
262
+ const specs = [cpu, ram].filter(Boolean).join(", ");
263
+ const specStr = specs ? `, ${specs}` : "";
264
+ return (chalk.bold.green(`Connected to ${name}`) +
265
+ chalk.dim(` (${image}${specStr})`));
266
+ }
267
+ // ---------------------------------------------------------------------------
268
+ // JSON mode (non-interactive) — run a single desktop or shell command
269
+ // ---------------------------------------------------------------------------
270
+ async function runJsonMode(opts) {
271
+ if (opts.desktopCmd) {
272
+ const parsed = parseDesktopCommand(opts.desktopCmd);
273
+ await handleDesktopCommand(parsed, opts.endpoint, opts.apiKey, opts.computerId, true);
274
+ return;
275
+ }
276
+ if (opts.shellCmd) {
277
+ // Use computerExec SSE stream for non-interactive shell command
278
+ const client = new MiosaClient(loadConfig());
279
+ const res = await client.computerExec(opts.computerId, opts.shellCmd);
280
+ const { parseSse } = await import("../client.js");
281
+ for await (const event of parseSse(res.body)) {
282
+ switch (event.type) {
283
+ case "stdout":
284
+ process.stdout.write(event.data);
285
+ break;
286
+ case "stderr":
287
+ process.stderr.write(event.data);
288
+ break;
289
+ case "exit":
290
+ process.exit(event.exit_code);
291
+ break;
292
+ case "error":
293
+ process.stderr.write(`Error: ${event.message}\n`);
294
+ process.exit(1);
295
+ break;
296
+ default:
297
+ break;
298
+ }
299
+ }
300
+ }
301
+ }
302
+ // ---------------------------------------------------------------------------
303
+ // Interactive shell session
304
+ // ---------------------------------------------------------------------------
305
+ async function runInteractiveSession(opts) {
306
+ const { computer, ticket, endpoint, apiKey } = opts;
307
+ const computerId = computer.id;
308
+ const computerName = computer.name || computerId;
309
+ return new Promise((resolve) => {
310
+ const ws = new WebSocket(ticket.ws_url, {
311
+ headers: { Authorization: `Bearer ${apiKey}` },
312
+ });
313
+ // Track whether we're in the middle of a desktop call so we can suppress
314
+ // echoing that line to the PTY.
315
+ let pendingDesktop = false;
316
+ // Buffer for intercepting typed lines — we use readline in "line mode" but
317
+ // the PTY expects raw chars. We switch strategy based on whether the user
318
+ // is typing a desktop command (line-buffered check) vs normal (raw).
319
+ let lineBuffer = "";
320
+ let inDesktopCapture = false;
321
+ let cleanedUp = false;
322
+ function cleanup(code) {
323
+ if (cleanedUp)
324
+ return;
325
+ cleanedUp = true;
326
+ // Restore terminal
327
+ if (process.stdin.isTTY) {
328
+ process.stdin.setRawMode(false);
329
+ process.stdin.pause();
330
+ }
331
+ process.stdin.removeAllListeners("data");
332
+ process.removeAllListeners("SIGWINCH");
333
+ if (ws.readyState === WebSocket.OPEN)
334
+ ws.close();
335
+ resolve(code);
336
+ }
337
+ function sendResize() {
338
+ if (ws.readyState !== WebSocket.OPEN)
339
+ return;
340
+ const { cols, rows } = getTerminalSize();
341
+ ws.send(JSON.stringify({ type: "resize", cols, rows }));
342
+ }
343
+ ws.on("open", () => {
344
+ // Put terminal in raw mode so the PTY gets key-by-key input
345
+ if (process.stdin.isTTY) {
346
+ process.stdin.setRawMode(true);
347
+ process.stdin.resume();
348
+ }
349
+ sendResize();
350
+ // Print the connection banner after the WS is open
351
+ process.stdout.write(formatBanner(computer) + "\n");
352
+ printSessionHelp(computerName);
353
+ process.on("SIGWINCH", sendResize);
354
+ // stdin data handler — intercept "desktop " lines; forward everything
355
+ // else raw to the PTY.
356
+ process.stdin.on("data", (chunk) => {
357
+ const str = chunk.toString("utf8");
358
+ for (const char of str) {
359
+ const code = char.charCodeAt(0);
360
+ // Ctrl+C (0x03) — forward to PTY (interrupt running process), do NOT exit CLI
361
+ if (code === 0x03) {
362
+ if (!pendingDesktop && ws.readyState === WebSocket.OPEN) {
363
+ ws.send(chunk);
364
+ }
365
+ return;
366
+ }
367
+ // Ctrl+D (0x04) — disconnect
368
+ if (code === 0x04) {
369
+ cleanup(0);
370
+ return;
371
+ }
372
+ // Accumulate characters to detect "desktop " prefix
373
+ if (inDesktopCapture) {
374
+ if (char === "\r" || char === "\n") {
375
+ // End of desktop command line — process it
376
+ const captured = lineBuffer;
377
+ lineBuffer = "";
378
+ inDesktopCapture = false;
379
+ pendingDesktop = true;
380
+ // Echo a newline back to user (raw mode won't auto-echo)
381
+ process.stdout.write("\r\n");
382
+ const parsed = parseDesktopCommand(captured);
383
+ void handleDesktopCommand(parsed, endpoint, apiKey, computerId, false).finally(() => {
384
+ pendingDesktop = false;
385
+ // Re-emit the shell prompt hint
386
+ process.stdout.write(chalk.dim(`${computerName}> `));
387
+ });
388
+ return;
389
+ }
390
+ if (char === "\x7f" || char === "\x08") {
391
+ // Backspace
392
+ if (lineBuffer.length > 0) {
393
+ lineBuffer = lineBuffer.slice(0, -1);
394
+ // Check if we've deleted back past "desktop "
395
+ if (!lineBuffer.startsWith("desktop ") &&
396
+ lineBuffer !== "desktop") {
397
+ inDesktopCapture = false;
398
+ // Forward the accumulated buffer so far to the PTY
399
+ if (ws.readyState === WebSocket.OPEN) {
400
+ ws.send(Buffer.from("desktop " + lineBuffer + "\x08"));
401
+ }
402
+ lineBuffer = "";
403
+ }
404
+ else {
405
+ process.stdout.write("\x08 \x08");
406
+ }
407
+ }
408
+ return;
409
+ }
410
+ // Regular character — accumulate and echo
411
+ lineBuffer += char;
412
+ process.stdout.write(char);
413
+ return;
414
+ }
415
+ // Not in desktop capture yet — check if this starts a desktop command
416
+ lineBuffer += char;
417
+ if ("desktop ".startsWith(lineBuffer) &&
418
+ lineBuffer.length <= "desktop ".length) {
419
+ // Could be the start of a desktop command — keep buffering silently
420
+ // but echo to user so they see what they type
421
+ process.stdout.write(char);
422
+ if (lineBuffer === "desktop ") {
423
+ inDesktopCapture = true;
424
+ // Keep lineBuffer empty for the subcommand part
425
+ lineBuffer = "";
426
+ }
427
+ return;
428
+ }
429
+ // Not a desktop command — flush buffer + current char to PTY
430
+ if (lineBuffer.length > 0) {
431
+ const flush = lineBuffer; // includes current char
432
+ lineBuffer = "";
433
+ if (ws.readyState === WebSocket.OPEN) {
434
+ ws.send(Buffer.from(flush));
435
+ }
436
+ }
437
+ }
438
+ });
439
+ });
440
+ // PTY output → local stdout
441
+ ws.on("message", (data) => {
442
+ if (!pendingDesktop) {
443
+ process.stdout.write(typeof data === "string" ? data : data);
444
+ }
445
+ });
446
+ ws.on("close", (code) => {
447
+ cleanup(code === 1000 ? 0 : 1);
448
+ });
449
+ ws.on("error", (err) => {
450
+ process.stderr.write(`\r\nWebSocket error: ${err.message}\r\n`);
451
+ cleanup(2);
452
+ });
453
+ process.stdin.on("end", () => {
454
+ ws.close();
455
+ });
456
+ });
457
+ }
458
+ // ---------------------------------------------------------------------------
459
+ // Commander registration
460
+ // ---------------------------------------------------------------------------
461
+ export function register(program) {
462
+ program
463
+ .command("shell <computer-id>")
464
+ .description("Open an interactive shell + desktop control session on a Computer (SSH and desktop in one)")
465
+ .option("--json", "Non-interactive mode — print structured JSON output")
466
+ .option("--desktop <cmd>", 'Run a single desktop command (e.g. "screenshot")')
467
+ .option("--cmd <command>", "Run a single shell command and exit")
468
+ .action(async (computerIdArg, opts) => {
469
+ try {
470
+ const config = loadConfig();
471
+ if (!config.api_key) {
472
+ process.stderr.write(chalk.red("You are not logged in. Run: miosa auth login\n"));
473
+ process.exit(3);
474
+ }
475
+ const endpoint = config.endpoint.replace(/\/$/, "");
476
+ const apiKey = config.api_key;
477
+ // --- Non-interactive JSON / single-command mode ---
478
+ if (opts.json || opts.desktop || opts.cmd) {
479
+ await runJsonMode({
480
+ computerId: computerIdArg,
481
+ desktopCmd: opts.desktop,
482
+ shellCmd: opts.cmd,
483
+ endpoint,
484
+ apiKey,
485
+ });
486
+ return;
487
+ }
488
+ // --- Interactive mode ---
489
+ const spinner = spin(`Connecting to ${computerIdArg}...`);
490
+ const client = new MiosaClient(config);
491
+ // Fetch computer details for the banner (best-effort — don't block on 404)
492
+ let computer;
493
+ try {
494
+ computer = await client
495
+ .apiGet(`/api/v1/computers/${encodeURIComponent(computerIdArg)}`)
496
+ .then((r) => r.data ?? r);
497
+ }
498
+ catch {
499
+ // Fall back to a minimal stub so we can still connect
500
+ computer = {
501
+ id: computerIdArg,
502
+ name: computerIdArg,
503
+ state: "unknown",
504
+ };
505
+ }
506
+ if (computer.state !== "running" && computer.state !== "unknown") {
507
+ spinner.warn(`Computer "${computer.name || computerIdArg}" is ${computer.state}. Connection may fail.`);
508
+ }
509
+ else {
510
+ spinner.text = `Opening PTY on ${computer.name || computerIdArg}...`;
511
+ }
512
+ const ticket = await createPty(endpoint, apiKey, computerIdArg);
513
+ spinner.stop();
514
+ const exitCode = await runInteractiveSession({
515
+ computer,
516
+ ticket,
517
+ endpoint,
518
+ apiKey,
519
+ });
520
+ process.exit(exitCode);
521
+ }
522
+ catch (err) {
523
+ handleError(err);
524
+ }
525
+ });
526
+ }
527
+ //# sourceMappingURL=shell.js.map