@miosa/cli 1.3.4 → 2.1.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 (489) hide show
  1. package/README.md +20 -835
  2. package/bin/miosa.js +75 -0
  3. package/lib/archive.js +88 -0
  4. package/lib/checksums.js +29 -0
  5. package/lib/platforms.js +31 -0
  6. package/lib/resolve.js +47 -0
  7. package/package.json +26 -41
  8. package/scripts/postinstall.js +68 -0
  9. package/CHANGELOG.md +0 -110
  10. package/dist/action-authority.d.ts +0 -106
  11. package/dist/action-authority.d.ts.map +0 -1
  12. package/dist/action-authority.js +0 -116
  13. package/dist/action-authority.js.map +0 -1
  14. package/dist/app-advisor.d.ts +0 -80
  15. package/dist/app-advisor.d.ts.map +0 -1
  16. package/dist/app-advisor.js +0 -486
  17. package/dist/app-advisor.js.map +0 -1
  18. package/dist/app-contract.d.ts +0 -92
  19. package/dist/app-contract.d.ts.map +0 -1
  20. package/dist/app-contract.js +0 -218
  21. package/dist/app-contract.js.map +0 -1
  22. package/dist/app-document.d.ts +0 -23
  23. package/dist/app-document.d.ts.map +0 -1
  24. package/dist/app-document.js +0 -180
  25. package/dist/app-document.js.map +0 -1
  26. package/dist/app-manifest.d.ts +0 -140
  27. package/dist/app-manifest.d.ts.map +0 -1
  28. package/dist/app-manifest.js +0 -377
  29. package/dist/app-manifest.js.map +0 -1
  30. package/dist/app-operation.d.ts +0 -23
  31. package/dist/app-operation.d.ts.map +0 -1
  32. package/dist/app-operation.js +0 -49
  33. package/dist/app-operation.js.map +0 -1
  34. package/dist/app-release.d.ts +0 -132
  35. package/dist/app-release.d.ts.map +0 -1
  36. package/dist/app-release.js +0 -294
  37. package/dist/app-release.js.map +0 -1
  38. package/dist/bin/miosa.d.ts +0 -3
  39. package/dist/bin/miosa.d.ts.map +0 -1
  40. package/dist/bin/miosa.js +0 -288
  41. package/dist/bin/miosa.js.map +0 -1
  42. package/dist/cli-env.d.ts +0 -12
  43. package/dist/cli-env.d.ts.map +0 -1
  44. package/dist/cli-env.js +0 -34
  45. package/dist/cli-env.js.map +0 -1
  46. package/dist/client.d.ts +0 -155
  47. package/dist/client.d.ts.map +0 -1
  48. package/dist/client.js +0 -865
  49. package/dist/client.js.map +0 -1
  50. package/dist/commands/actions.d.ts +0 -3
  51. package/dist/commands/actions.d.ts.map +0 -1
  52. package/dist/commands/actions.js +0 -222
  53. package/dist/commands/actions.js.map +0 -1
  54. package/dist/commands/agent-runtime-profiles.d.ts +0 -3
  55. package/dist/commands/agent-runtime-profiles.d.ts.map +0 -1
  56. package/dist/commands/agent-runtime-profiles.js +0 -198
  57. package/dist/commands/agent-runtime-profiles.js.map +0 -1
  58. package/dist/commands/agent.d.ts +0 -18
  59. package/dist/commands/agent.d.ts.map +0 -1
  60. package/dist/commands/agent.js +0 -737
  61. package/dist/commands/agent.js.map +0 -1
  62. package/dist/commands/alerts.d.ts +0 -3
  63. package/dist/commands/alerts.d.ts.map +0 -1
  64. package/dist/commands/alerts.js +0 -41
  65. package/dist/commands/alerts.js.map +0 -1
  66. package/dist/commands/api-keys.d.ts +0 -3
  67. package/dist/commands/api-keys.d.ts.map +0 -1
  68. package/dist/commands/api-keys.js +0 -160
  69. package/dist/commands/api-keys.js.map +0 -1
  70. package/dist/commands/api-resource.d.ts +0 -20
  71. package/dist/commands/api-resource.d.ts.map +0 -1
  72. package/dist/commands/api-resource.js +0 -120
  73. package/dist/commands/api-resource.js.map +0 -1
  74. package/dist/commands/app.d.ts +0 -38
  75. package/dist/commands/app.d.ts.map +0 -1
  76. package/dist/commands/app.js +0 -1182
  77. package/dist/commands/app.js.map +0 -1
  78. package/dist/commands/apps.d.ts +0 -3
  79. package/dist/commands/apps.d.ts.map +0 -1
  80. package/dist/commands/apps.js +0 -218
  81. package/dist/commands/apps.js.map +0 -1
  82. package/dist/commands/audit.d.ts +0 -3
  83. package/dist/commands/audit.d.ts.map +0 -1
  84. package/dist/commands/audit.js +0 -25
  85. package/dist/commands/audit.js.map +0 -1
  86. package/dist/commands/auth.d.ts +0 -3
  87. package/dist/commands/auth.d.ts.map +0 -1
  88. package/dist/commands/auth.js +0 -282
  89. package/dist/commands/auth.js.map +0 -1
  90. package/dist/commands/backups.d.ts +0 -3
  91. package/dist/commands/backups.d.ts.map +0 -1
  92. package/dist/commands/backups.js +0 -23
  93. package/dist/commands/backups.js.map +0 -1
  94. package/dist/commands/billing.d.ts +0 -3
  95. package/dist/commands/billing.d.ts.map +0 -1
  96. package/dist/commands/billing.js +0 -208
  97. package/dist/commands/billing.js.map +0 -1
  98. package/dist/commands/builds.d.ts +0 -3
  99. package/dist/commands/builds.d.ts.map +0 -1
  100. package/dist/commands/builds.js +0 -198
  101. package/dist/commands/builds.js.map +0 -1
  102. package/dist/commands/capabilities.d.ts +0 -3
  103. package/dist/commands/capabilities.d.ts.map +0 -1
  104. package/dist/commands/capabilities.js +0 -1002
  105. package/dist/commands/capabilities.js.map +0 -1
  106. package/dist/commands/checkpoints.d.ts +0 -3
  107. package/dist/commands/checkpoints.d.ts.map +0 -1
  108. package/dist/commands/checkpoints.js +0 -193
  109. package/dist/commands/checkpoints.js.map +0 -1
  110. package/dist/commands/cleanup.d.ts +0 -3
  111. package/dist/commands/cleanup.d.ts.map +0 -1
  112. package/dist/commands/cleanup.js +0 -65
  113. package/dist/commands/cleanup.js.map +0 -1
  114. package/dist/commands/cloud.d.ts +0 -3
  115. package/dist/commands/cloud.d.ts.map +0 -1
  116. package/dist/commands/cloud.js +0 -192
  117. package/dist/commands/cloud.js.map +0 -1
  118. package/dist/commands/command-overview.d.ts +0 -13
  119. package/dist/commands/command-overview.d.ts.map +0 -1
  120. package/dist/commands/command-overview.js +0 -50
  121. package/dist/commands/command-overview.js.map +0 -1
  122. package/dist/commands/completion.d.ts +0 -3
  123. package/dist/commands/completion.d.ts.map +0 -1
  124. package/dist/commands/completion.js +0 -183
  125. package/dist/commands/completion.js.map +0 -1
  126. package/dist/commands/computers.d.ts +0 -3
  127. package/dist/commands/computers.d.ts.map +0 -1
  128. package/dist/commands/computers.js +0 -706
  129. package/dist/commands/computers.js.map +0 -1
  130. package/dist/commands/config.d.ts +0 -3
  131. package/dist/commands/config.d.ts.map +0 -1
  132. package/dist/commands/config.js +0 -114
  133. package/dist/commands/config.js.map +0 -1
  134. package/dist/commands/connect.d.ts +0 -6
  135. package/dist/commands/connect.d.ts.map +0 -1
  136. package/dist/commands/connect.js +0 -127
  137. package/dist/commands/connect.js.map +0 -1
  138. package/dist/commands/connectors.d.ts +0 -3
  139. package/dist/commands/connectors.d.ts.map +0 -1
  140. package/dist/commands/connectors.js +0 -172
  141. package/dist/commands/connectors.js.map +0 -1
  142. package/dist/commands/containers.d.ts +0 -3
  143. package/dist/commands/containers.d.ts.map +0 -1
  144. package/dist/commands/containers.js +0 -20
  145. package/dist/commands/containers.js.map +0 -1
  146. package/dist/commands/context.d.ts +0 -3
  147. package/dist/commands/context.d.ts.map +0 -1
  148. package/dist/commands/context.js +0 -233
  149. package/dist/commands/context.js.map +0 -1
  150. package/dist/commands/cp.d.ts +0 -3
  151. package/dist/commands/cp.d.ts.map +0 -1
  152. package/dist/commands/cp.js +0 -319
  153. package/dist/commands/cp.js.map +0 -1
  154. package/dist/commands/cron.d.ts +0 -3
  155. package/dist/commands/cron.d.ts.map +0 -1
  156. package/dist/commands/cron.js +0 -65
  157. package/dist/commands/cron.js.map +0 -1
  158. package/dist/commands/dashboard.d.ts +0 -13
  159. package/dist/commands/dashboard.d.ts.map +0 -1
  160. package/dist/commands/dashboard.js +0 -54
  161. package/dist/commands/dashboard.js.map +0 -1
  162. package/dist/commands/databases.d.ts +0 -3
  163. package/dist/commands/databases.d.ts.map +0 -1
  164. package/dist/commands/databases.js +0 -714
  165. package/dist/commands/databases.js.map +0 -1
  166. package/dist/commands/db.d.ts +0 -3
  167. package/dist/commands/db.d.ts.map +0 -1
  168. package/dist/commands/db.js +0 -385
  169. package/dist/commands/db.js.map +0 -1
  170. package/dist/commands/deploy.d.ts +0 -3
  171. package/dist/commands/deploy.d.ts.map +0 -1
  172. package/dist/commands/deploy.js +0 -1234
  173. package/dist/commands/deploy.js.map +0 -1
  174. package/dist/commands/desktop.d.ts +0 -3
  175. package/dist/commands/desktop.d.ts.map +0 -1
  176. package/dist/commands/desktop.js +0 -277
  177. package/dist/commands/desktop.js.map +0 -1
  178. package/dist/commands/dev.d.ts +0 -3
  179. package/dist/commands/dev.d.ts.map +0 -1
  180. package/dist/commands/dev.js +0 -245
  181. package/dist/commands/dev.js.map +0 -1
  182. package/dist/commands/devices.d.ts +0 -3
  183. package/dist/commands/devices.d.ts.map +0 -1
  184. package/dist/commands/devices.js +0 -279
  185. package/dist/commands/devices.js.map +0 -1
  186. package/dist/commands/docker-deploy.d.ts +0 -3
  187. package/dist/commands/docker-deploy.d.ts.map +0 -1
  188. package/dist/commands/docker-deploy.js +0 -659
  189. package/dist/commands/docker-deploy.js.map +0 -1
  190. package/dist/commands/doctor.d.ts +0 -69
  191. package/dist/commands/doctor.d.ts.map +0 -1
  192. package/dist/commands/doctor.js +0 -596
  193. package/dist/commands/doctor.js.map +0 -1
  194. package/dist/commands/domains.d.ts +0 -3
  195. package/dist/commands/domains.d.ts.map +0 -1
  196. package/dist/commands/domains.js +0 -307
  197. package/dist/commands/domains.js.map +0 -1
  198. package/dist/commands/enterprise-util.d.ts +0 -39
  199. package/dist/commands/enterprise-util.d.ts.map +0 -1
  200. package/dist/commands/enterprise-util.js +0 -217
  201. package/dist/commands/enterprise-util.js.map +0 -1
  202. package/dist/commands/env-input.d.ts +0 -26
  203. package/dist/commands/env-input.d.ts.map +0 -1
  204. package/dist/commands/env-input.js +0 -68
  205. package/dist/commands/env-input.js.map +0 -1
  206. package/dist/commands/env.d.ts +0 -3
  207. package/dist/commands/env.d.ts.map +0 -1
  208. package/dist/commands/env.js +0 -189
  209. package/dist/commands/env.js.map +0 -1
  210. package/dist/commands/exec.d.ts +0 -3
  211. package/dist/commands/exec.d.ts.map +0 -1
  212. package/dist/commands/exec.js +0 -110
  213. package/dist/commands/exec.js.map +0 -1
  214. package/dist/commands/forge.d.ts +0 -3
  215. package/dist/commands/forge.d.ts.map +0 -1
  216. package/dist/commands/forge.js +0 -254
  217. package/dist/commands/forge.js.map +0 -1
  218. package/dist/commands/functions.d.ts +0 -3
  219. package/dist/commands/functions.d.ts.map +0 -1
  220. package/dist/commands/functions.js +0 -47
  221. package/dist/commands/functions.js.map +0 -1
  222. package/dist/commands/gha-runners.d.ts +0 -3
  223. package/dist/commands/gha-runners.d.ts.map +0 -1
  224. package/dist/commands/gha-runners.js +0 -33
  225. package/dist/commands/gha-runners.js.map +0 -1
  226. package/dist/commands/groups.d.ts +0 -3
  227. package/dist/commands/groups.d.ts.map +0 -1
  228. package/dist/commands/groups.js +0 -38
  229. package/dist/commands/groups.js.map +0 -1
  230. package/dist/commands/host.d.ts +0 -3
  231. package/dist/commands/host.d.ts.map +0 -1
  232. package/dist/commands/host.js +0 -74
  233. package/dist/commands/host.js.map +0 -1
  234. package/dist/commands/hosts.d.ts +0 -7
  235. package/dist/commands/hosts.d.ts.map +0 -1
  236. package/dist/commands/hosts.js +0 -91
  237. package/dist/commands/hosts.js.map +0 -1
  238. package/dist/commands/link.d.ts +0 -8
  239. package/dist/commands/link.d.ts.map +0 -1
  240. package/dist/commands/link.js +0 -124
  241. package/dist/commands/link.js.map +0 -1
  242. package/dist/commands/login.d.ts +0 -10
  243. package/dist/commands/login.d.ts.map +0 -1
  244. package/dist/commands/login.js +0 -263
  245. package/dist/commands/login.js.map +0 -1
  246. package/dist/commands/logout.d.ts +0 -3
  247. package/dist/commands/logout.d.ts.map +0 -1
  248. package/dist/commands/logout.js +0 -43
  249. package/dist/commands/logout.js.map +0 -1
  250. package/dist/commands/logs.d.ts +0 -3
  251. package/dist/commands/logs.d.ts.map +0 -1
  252. package/dist/commands/logs.js +0 -401
  253. package/dist/commands/logs.js.map +0 -1
  254. package/dist/commands/ls.d.ts +0 -3
  255. package/dist/commands/ls.d.ts.map +0 -1
  256. package/dist/commands/ls.js +0 -121
  257. package/dist/commands/ls.js.map +0 -1
  258. package/dist/commands/mcp.d.ts +0 -46
  259. package/dist/commands/mcp.d.ts.map +0 -1
  260. package/dist/commands/mcp.js +0 -3413
  261. package/dist/commands/mcp.js.map +0 -1
  262. package/dist/commands/menu.d.ts +0 -29
  263. package/dist/commands/menu.d.ts.map +0 -1
  264. package/dist/commands/menu.js +0 -147
  265. package/dist/commands/menu.js.map +0 -1
  266. package/dist/commands/meshes.d.ts +0 -3
  267. package/dist/commands/meshes.d.ts.map +0 -1
  268. package/dist/commands/meshes.js +0 -27
  269. package/dist/commands/meshes.js.map +0 -1
  270. package/dist/commands/network-policy.d.ts +0 -3
  271. package/dist/commands/network-policy.d.ts.map +0 -1
  272. package/dist/commands/network-policy.js +0 -155
  273. package/dist/commands/network-policy.js.map +0 -1
  274. package/dist/commands/new.d.ts +0 -3
  275. package/dist/commands/new.d.ts.map +0 -1
  276. package/dist/commands/new.js +0 -296
  277. package/dist/commands/new.js.map +0 -1
  278. package/dist/commands/opencomputers.d.ts +0 -10
  279. package/dist/commands/opencomputers.d.ts.map +0 -1
  280. package/dist/commands/opencomputers.js +0 -25
  281. package/dist/commands/opencomputers.js.map +0 -1
  282. package/dist/commands/operating-contract.d.ts +0 -3
  283. package/dist/commands/operating-contract.d.ts.map +0 -1
  284. package/dist/commands/operating-contract.js +0 -651
  285. package/dist/commands/operating-contract.js.map +0 -1
  286. package/dist/commands/project.d.ts +0 -4
  287. package/dist/commands/project.d.ts.map +0 -1
  288. package/dist/commands/project.js +0 -25
  289. package/dist/commands/project.js.map +0 -1
  290. package/dist/commands/pull.d.ts +0 -14
  291. package/dist/commands/pull.d.ts.map +0 -1
  292. package/dist/commands/pull.js +0 -156
  293. package/dist/commands/pull.js.map +0 -1
  294. package/dist/commands/regions.d.ts +0 -3
  295. package/dist/commands/regions.d.ts.map +0 -1
  296. package/dist/commands/regions.js +0 -103
  297. package/dist/commands/regions.js.map +0 -1
  298. package/dist/commands/releases.d.ts +0 -3
  299. package/dist/commands/releases.d.ts.map +0 -1
  300. package/dist/commands/releases.js +0 -365
  301. package/dist/commands/releases.js.map +0 -1
  302. package/dist/commands/rm.d.ts +0 -3
  303. package/dist/commands/rm.d.ts.map +0 -1
  304. package/dist/commands/rm.js +0 -42
  305. package/dist/commands/rm.js.map +0 -1
  306. package/dist/commands/rollback.d.ts +0 -3
  307. package/dist/commands/rollback.d.ts.map +0 -1
  308. package/dist/commands/rollback.js +0 -62
  309. package/dist/commands/rollback.js.map +0 -1
  310. package/dist/commands/run-groups.d.ts +0 -3
  311. package/dist/commands/run-groups.d.ts.map +0 -1
  312. package/dist/commands/run-groups.js +0 -364
  313. package/dist/commands/run-groups.js.map +0 -1
  314. package/dist/commands/run.d.ts +0 -3
  315. package/dist/commands/run.d.ts.map +0 -1
  316. package/dist/commands/run.js +0 -130
  317. package/dist/commands/run.js.map +0 -1
  318. package/dist/commands/runs.d.ts +0 -3
  319. package/dist/commands/runs.d.ts.map +0 -1
  320. package/dist/commands/runs.js +0 -365
  321. package/dist/commands/runs.js.map +0 -1
  322. package/dist/commands/runtime-env.d.ts +0 -3
  323. package/dist/commands/runtime-env.d.ts.map +0 -1
  324. package/dist/commands/runtime-env.js +0 -114
  325. package/dist/commands/runtime-env.js.map +0 -1
  326. package/dist/commands/sandbox-delete-guard.d.ts +0 -4
  327. package/dist/commands/sandbox-delete-guard.d.ts.map +0 -1
  328. package/dist/commands/sandbox-delete-guard.js +0 -32
  329. package/dist/commands/sandbox-delete-guard.js.map +0 -1
  330. package/dist/commands/sandbox-dev.d.ts +0 -20
  331. package/dist/commands/sandbox-dev.d.ts.map +0 -1
  332. package/dist/commands/sandbox-dev.js +0 -583
  333. package/dist/commands/sandbox-dev.js.map +0 -1
  334. package/dist/commands/sandbox.d.ts +0 -36
  335. package/dist/commands/sandbox.d.ts.map +0 -1
  336. package/dist/commands/sandbox.js +0 -3796
  337. package/dist/commands/sandbox.js.map +0 -1
  338. package/dist/commands/scale.d.ts +0 -3
  339. package/dist/commands/scale.d.ts.map +0 -1
  340. package/dist/commands/scale.js +0 -61
  341. package/dist/commands/scale.js.map +0 -1
  342. package/dist/commands/schedules.d.ts +0 -3
  343. package/dist/commands/schedules.d.ts.map +0 -1
  344. package/dist/commands/schedules.js +0 -37
  345. package/dist/commands/schedules.js.map +0 -1
  346. package/dist/commands/secrets.d.ts +0 -3
  347. package/dist/commands/secrets.d.ts.map +0 -1
  348. package/dist/commands/secrets.js +0 -200
  349. package/dist/commands/secrets.js.map +0 -1
  350. package/dist/commands/services.d.ts +0 -3
  351. package/dist/commands/services.d.ts.map +0 -1
  352. package/dist/commands/services.js +0 -70
  353. package/dist/commands/services.js.map +0 -1
  354. package/dist/commands/shell.d.ts +0 -16
  355. package/dist/commands/shell.d.ts.map +0 -1
  356. package/dist/commands/shell.js +0 -555
  357. package/dist/commands/shell.js.map +0 -1
  358. package/dist/commands/snapshot.d.ts +0 -10
  359. package/dist/commands/snapshot.d.ts.map +0 -1
  360. package/dist/commands/snapshot.js +0 -181
  361. package/dist/commands/snapshot.js.map +0 -1
  362. package/dist/commands/ssh.d.ts +0 -3
  363. package/dist/commands/ssh.d.ts.map +0 -1
  364. package/dist/commands/ssh.js +0 -93
  365. package/dist/commands/ssh.js.map +0 -1
  366. package/dist/commands/status.d.ts +0 -3
  367. package/dist/commands/status.d.ts.map +0 -1
  368. package/dist/commands/status.js +0 -302
  369. package/dist/commands/status.js.map +0 -1
  370. package/dist/commands/storage.d.ts +0 -3
  371. package/dist/commands/storage.d.ts.map +0 -1
  372. package/dist/commands/storage.js +0 -456
  373. package/dist/commands/storage.js.map +0 -1
  374. package/dist/commands/teams.d.ts +0 -3
  375. package/dist/commands/teams.d.ts.map +0 -1
  376. package/dist/commands/teams.js +0 -169
  377. package/dist/commands/teams.js.map +0 -1
  378. package/dist/commands/templates.d.ts +0 -3
  379. package/dist/commands/templates.d.ts.map +0 -1
  380. package/dist/commands/templates.js +0 -690
  381. package/dist/commands/templates.js.map +0 -1
  382. package/dist/commands/tenant.d.ts +0 -3
  383. package/dist/commands/tenant.d.ts.map +0 -1
  384. package/dist/commands/tenant.js +0 -95
  385. package/dist/commands/tenant.js.map +0 -1
  386. package/dist/commands/tunnel.d.ts +0 -3
  387. package/dist/commands/tunnel.d.ts.map +0 -1
  388. package/dist/commands/tunnel.js +0 -418
  389. package/dist/commands/tunnel.js.map +0 -1
  390. package/dist/commands/up.d.ts +0 -14
  391. package/dist/commands/up.d.ts.map +0 -1
  392. package/dist/commands/up.js +0 -720
  393. package/dist/commands/up.js.map +0 -1
  394. package/dist/commands/update.d.ts +0 -3
  395. package/dist/commands/update.d.ts.map +0 -1
  396. package/dist/commands/update.js +0 -166
  397. package/dist/commands/update.js.map +0 -1
  398. package/dist/commands/util.d.ts +0 -25
  399. package/dist/commands/util.d.ts.map +0 -1
  400. package/dist/commands/util.js +0 -199
  401. package/dist/commands/util.js.map +0 -1
  402. package/dist/commands/volumes.d.ts +0 -3
  403. package/dist/commands/volumes.d.ts.map +0 -1
  404. package/dist/commands/volumes.js +0 -196
  405. package/dist/commands/volumes.js.map +0 -1
  406. package/dist/commands/watch.d.ts +0 -6
  407. package/dist/commands/watch.d.ts.map +0 -1
  408. package/dist/commands/watch.js +0 -414
  409. package/dist/commands/watch.js.map +0 -1
  410. package/dist/commands/webhooks.d.ts +0 -3
  411. package/dist/commands/webhooks.d.ts.map +0 -1
  412. package/dist/commands/webhooks.js +0 -23
  413. package/dist/commands/webhooks.js.map +0 -1
  414. package/dist/commands/whoami.d.ts +0 -3
  415. package/dist/commands/whoami.d.ts.map +0 -1
  416. package/dist/commands/whoami.js +0 -156
  417. package/dist/commands/whoami.js.map +0 -1
  418. package/dist/commands/workspaces.d.ts +0 -3
  419. package/dist/commands/workspaces.d.ts.map +0 -1
  420. package/dist/commands/workspaces.js +0 -147
  421. package/dist/commands/workspaces.js.map +0 -1
  422. package/dist/config.d.ts +0 -54
  423. package/dist/config.d.ts.map +0 -1
  424. package/dist/config.js +0 -275
  425. package/dist/config.js.map +0 -1
  426. package/dist/errors.d.ts +0 -30
  427. package/dist/errors.d.ts.map +0 -1
  428. package/dist/errors.js +0 -92
  429. package/dist/errors.js.map +0 -1
  430. package/dist/framework-detector.d.ts +0 -22
  431. package/dist/framework-detector.d.ts.map +0 -1
  432. package/dist/framework-detector.js +0 -373
  433. package/dist/framework-detector.js.map +0 -1
  434. package/dist/generated/action-capabilities.d.ts +0 -826
  435. package/dist/generated/action-capabilities.d.ts.map +0 -1
  436. package/dist/generated/action-capabilities.js +0 -1035
  437. package/dist/generated/action-capabilities.js.map +0 -1
  438. package/dist/net-diagnosis.d.ts +0 -87
  439. package/dist/net-diagnosis.d.ts.map +0 -1
  440. package/dist/net-diagnosis.js +0 -408
  441. package/dist/net-diagnosis.js.map +0 -1
  442. package/dist/pty/raw-mode.d.ts +0 -7
  443. package/dist/pty/raw-mode.d.ts.map +0 -1
  444. package/dist/pty/raw-mode.js +0 -22
  445. package/dist/pty/raw-mode.js.map +0 -1
  446. package/dist/pty/ws-pty-client.d.ts +0 -13
  447. package/dist/pty/ws-pty-client.d.ts.map +0 -1
  448. package/dist/pty/ws-pty-client.js +0 -73
  449. package/dist/pty/ws-pty-client.js.map +0 -1
  450. package/dist/tui/dashboard.d.ts +0 -35
  451. package/dist/tui/dashboard.d.ts.map +0 -1
  452. package/dist/tui/dashboard.js +0 -191
  453. package/dist/tui/dashboard.js.map +0 -1
  454. package/dist/tui/logs.d.ts +0 -26
  455. package/dist/tui/logs.d.ts.map +0 -1
  456. package/dist/tui/logs.js +0 -276
  457. package/dist/tui/logs.js.map +0 -1
  458. package/dist/types.d.ts +0 -362
  459. package/dist/types.d.ts.map +0 -1
  460. package/dist/types.js +0 -16
  461. package/dist/types.js.map +0 -1
  462. package/dist/ui/picker.d.ts +0 -41
  463. package/dist/ui/picker.d.ts.map +0 -1
  464. package/dist/ui/picker.js +0 -77
  465. package/dist/ui/picker.js.map +0 -1
  466. package/dist/ui/progress.d.ts +0 -10
  467. package/dist/ui/progress.d.ts.map +0 -1
  468. package/dist/ui/progress.js +0 -36
  469. package/dist/ui/progress.js.map +0 -1
  470. package/dist/ui/render.d.ts +0 -91
  471. package/dist/ui/render.d.ts.map +0 -1
  472. package/dist/ui/render.js +0 -138
  473. package/dist/ui/render.js.map +0 -1
  474. package/dist/ui/spinner.d.ts +0 -4
  475. package/dist/ui/spinner.d.ts.map +0 -1
  476. package/dist/ui/spinner.js +0 -7
  477. package/dist/ui/spinner.js.map +0 -1
  478. package/dist/ui/table.d.ts +0 -8
  479. package/dist/ui/table.d.ts.map +0 -1
  480. package/dist/ui/table.js +0 -46
  481. package/dist/ui/table.js.map +0 -1
  482. package/dist/update-check.d.ts +0 -13
  483. package/dist/update-check.d.ts.map +0 -1
  484. package/dist/update-check.js +0 -94
  485. package/dist/update-check.js.map +0 -1
  486. package/dist/version.d.ts +0 -3
  487. package/dist/version.d.ts.map +0 -1
  488. package/dist/version.js +0 -5
  489. package/dist/version.js.map +0 -1
@@ -1,3413 +0,0 @@
1
- /**
2
- * miosa mcp serve — expose every major CLI command as an MCP server over stdio.
3
- *
4
- * Protocol: JSON-RPC 2.0 over stdin/stdout (newline-delimited).
5
- * initialize → server info + capabilities
6
- * tools/list → all tool schemas
7
- * tools/call → dispatch to MIOSA API
8
- *
9
- * Add to .claude/mcp.json:
10
- * {
11
- * "mcpServers": {
12
- * "miosa": {
13
- * "command": "miosa",
14
- * "args": ["mcp", "serve"]
15
- * }
16
- * }
17
- * }
18
- */
19
- import { createInterface } from "node:readline";
20
- import { spawn, spawnSync } from "node:child_process";
21
- import { randomUUID } from "node:crypto";
22
- import { request } from "undici";
23
- import chalk from "chalk";
24
- import { loadConfig } from "../config.js";
25
- import { MiosaClient } from "../client.js";
26
- const SANDBOX_SIZES = ["xs", "small", "medium", "large", "xl"];
27
- // ── Tool definitions — mirror the Python MCP server exactly ─────────────────
28
- export const MCP_TOOLS = [
29
- // Lifecycle
30
- {
31
- name: "computer_create",
32
- description: "Create a new MIOSA computer and wait until it is active. Returns the computer ID. The new computer becomes the active computer for subsequent calls.",
33
- inputSchema: {
34
- type: "object",
35
- properties: {
36
- name: {
37
- type: "string",
38
- description: "Human-readable name for the computer",
39
- },
40
- template_type: {
41
- type: "string",
42
- description: "Template to boot from (default: miosa-desktop)",
43
- default: "miosa-desktop",
44
- },
45
- size: {
46
- type: "string",
47
- enum: ["small", "medium", "large", "xl"],
48
- description: "VM size (default: small)",
49
- default: "small",
50
- },
51
- workspace_id: {
52
- type: "string",
53
- description: "Workspace ID to assign the computer to (optional)",
54
- },
55
- external_workspace_id: {
56
- type: "string",
57
- description: "Your internal workspace ID for attribution (optional)",
58
- },
59
- external_project_id: {
60
- type: "string",
61
- description: "Your internal project ID for attribution (optional)",
62
- },
63
- },
64
- required: ["name"],
65
- },
66
- },
67
- {
68
- name: "computer_list",
69
- description: "List all computers in the tenant.",
70
- inputSchema: { type: "object", properties: {} },
71
- },
72
- {
73
- name: "computer_destroy",
74
- description: "Permanently destroy a computer.",
75
- inputSchema: {
76
- type: "object",
77
- properties: {
78
- computer_id: {
79
- type: "string",
80
- description: "ID of the computer to destroy.",
81
- },
82
- },
83
- required: ["computer_id"],
84
- },
85
- },
86
- {
87
- name: "computer_get",
88
- description: "Get details and current status of a computer.",
89
- inputSchema: {
90
- type: "object",
91
- properties: {
92
- computer_id: {
93
- type: "string",
94
- description: "ID of the computer to fetch.",
95
- },
96
- },
97
- required: ["computer_id"],
98
- },
99
- },
100
- {
101
- name: "computer_start",
102
- description: "Start a stopped computer.",
103
- inputSchema: {
104
- type: "object",
105
- properties: {
106
- computer_id: { type: "string", description: "Computer ID." },
107
- },
108
- required: ["computer_id"],
109
- },
110
- },
111
- {
112
- name: "computer_stop",
113
- description: "Stop a running computer.",
114
- inputSchema: {
115
- type: "object",
116
- properties: {
117
- computer_id: { type: "string", description: "Computer ID." },
118
- },
119
- required: ["computer_id"],
120
- },
121
- },
122
- {
123
- name: "computer_restart",
124
- description: "Restart a computer.",
125
- inputSchema: {
126
- type: "object",
127
- properties: {
128
- computer_id: { type: "string", description: "Computer ID." },
129
- },
130
- required: ["computer_id"],
131
- },
132
- },
133
- {
134
- name: "computer_update",
135
- description: "Rename a computer or update its metadata.",
136
- inputSchema: {
137
- type: "object",
138
- properties: {
139
- computer_id: { type: "string", description: "Computer ID." },
140
- name: {
141
- type: "string",
142
- description: "New human-readable name for the computer (optional).",
143
- },
144
- metadata: {
145
- type: "object",
146
- description: "Arbitrary key/value metadata to attach (optional).",
147
- },
148
- },
149
- required: ["computer_id"],
150
- },
151
- },
152
- // Screenshot
153
- {
154
- name: "computer_screenshot",
155
- description: "Capture a PNG screenshot of the computer desktop. Returns the image so you can see the current screen state.",
156
- inputSchema: {
157
- type: "object",
158
- properties: {
159
- computer_id: { type: "string", description: "Computer ID." },
160
- },
161
- required: ["computer_id"],
162
- },
163
- },
164
- // Pointer
165
- {
166
- name: "computer_click",
167
- description: "Click a mouse button at the given screen coordinates.",
168
- inputSchema: {
169
- type: "object",
170
- properties: {
171
- computer_id: { type: "string", description: "Computer ID." },
172
- x: { type: "integer", description: "X coordinate in pixels" },
173
- y: { type: "integer", description: "Y coordinate in pixels" },
174
- button: {
175
- type: "string",
176
- enum: ["left", "right", "middle"],
177
- description: "Mouse button (default: left)",
178
- default: "left",
179
- },
180
- },
181
- required: ["computer_id", "x", "y"],
182
- },
183
- },
184
- {
185
- name: "computer_double_click",
186
- description: "Double-click at the given screen coordinates.",
187
- inputSchema: {
188
- type: "object",
189
- properties: {
190
- computer_id: { type: "string" },
191
- x: { type: "integer" },
192
- y: { type: "integer" },
193
- },
194
- required: ["computer_id", "x", "y"],
195
- },
196
- },
197
- {
198
- name: "computer_move_cursor",
199
- description: "Move the mouse cursor to the given coordinates without clicking.",
200
- inputSchema: {
201
- type: "object",
202
- properties: {
203
- computer_id: { type: "string" },
204
- x: { type: "integer" },
205
- y: { type: "integer" },
206
- },
207
- required: ["computer_id", "x", "y"],
208
- },
209
- },
210
- {
211
- name: "computer_drag",
212
- description: "Click-and-drag from one screen position to another.",
213
- inputSchema: {
214
- type: "object",
215
- properties: {
216
- computer_id: { type: "string" },
217
- from_x: { type: "integer" },
218
- from_y: { type: "integer" },
219
- to_x: { type: "integer" },
220
- to_y: { type: "integer" },
221
- },
222
- required: ["computer_id", "from_x", "from_y", "to_x", "to_y"],
223
- },
224
- },
225
- {
226
- name: "computer_scroll",
227
- description: "Scroll in a direction on the desktop.",
228
- inputSchema: {
229
- type: "object",
230
- properties: {
231
- computer_id: { type: "string" },
232
- direction: {
233
- type: "string",
234
- enum: ["up", "down", "left", "right"],
235
- default: "down",
236
- },
237
- clicks: {
238
- type: "integer",
239
- description: "Number of scroll detents (default: 3)",
240
- default: 3,
241
- },
242
- x: { type: "integer", description: "Optional X position for scroll" },
243
- y: { type: "integer", description: "Optional Y position for scroll" },
244
- },
245
- required: ["computer_id"],
246
- },
247
- },
248
- // Keyboard
249
- {
250
- name: "computer_type",
251
- description: "Type text into the currently focused field.",
252
- inputSchema: {
253
- type: "object",
254
- properties: {
255
- computer_id: { type: "string" },
256
- text: { type: "string", description: "Text to type" },
257
- },
258
- required: ["computer_id", "text"],
259
- },
260
- },
261
- {
262
- name: "computer_key",
263
- description: "Press a single key. Use standard key names: Return, Tab, Escape, BackSpace, Delete, space, F1-F12, ctrl, shift, alt, super.",
264
- inputSchema: {
265
- type: "object",
266
- properties: {
267
- computer_id: { type: "string" },
268
- key: { type: "string", description: "Key name to press" },
269
- },
270
- required: ["computer_id", "key"],
271
- },
272
- },
273
- {
274
- name: "computer_hotkey",
275
- description: "Press a keyboard shortcut (multiple keys simultaneously). Example: ['ctrl', 'c'] for copy.",
276
- inputSchema: {
277
- type: "object",
278
- properties: {
279
- computer_id: { type: "string" },
280
- keys: {
281
- type: "array",
282
- items: { type: "string" },
283
- description: "Keys to press together, e.g. ['ctrl', 'c']",
284
- },
285
- },
286
- required: ["computer_id", "keys"],
287
- },
288
- },
289
- // Display info
290
- {
291
- name: "computer_get_screen_size",
292
- description: "Get the screen resolution (width and height in pixels).",
293
- inputSchema: {
294
- type: "object",
295
- properties: {
296
- computer_id: { type: "string" },
297
- },
298
- required: ["computer_id"],
299
- },
300
- },
301
- {
302
- name: "computer_get_cursor_position",
303
- description: "Get the current mouse cursor position (x, y).",
304
- inputSchema: {
305
- type: "object",
306
- properties: {
307
- computer_id: { type: "string" },
308
- },
309
- required: ["computer_id"],
310
- },
311
- },
312
- // Clipboard
313
- {
314
- name: "computer_get_clipboard",
315
- description: "Read the current clipboard text content.",
316
- inputSchema: {
317
- type: "object",
318
- properties: {
319
- computer_id: { type: "string" },
320
- },
321
- required: ["computer_id"],
322
- },
323
- },
324
- {
325
- name: "computer_set_clipboard",
326
- description: "Set the clipboard text content.",
327
- inputSchema: {
328
- type: "object",
329
- properties: {
330
- computer_id: { type: "string" },
331
- text: { type: "string", description: "Text to put in clipboard" },
332
- },
333
- required: ["computer_id", "text"],
334
- },
335
- },
336
- // Window management
337
- {
338
- name: "computer_windows",
339
- description: "List all open windows on the desktop with their IDs, titles, and positions.",
340
- inputSchema: {
341
- type: "object",
342
- properties: {
343
- computer_id: { type: "string" },
344
- },
345
- required: ["computer_id"],
346
- },
347
- },
348
- {
349
- name: "computer_launch",
350
- description: "Launch an application by name (e.g. 'firefox', 'gedit', 'xterm').",
351
- inputSchema: {
352
- type: "object",
353
- properties: {
354
- computer_id: { type: "string" },
355
- app: { type: "string", description: "Application name to launch" },
356
- },
357
- required: ["computer_id", "app"],
358
- },
359
- },
360
- // Shell & Files
361
- {
362
- name: "computer_bash",
363
- description: "Execute a bash command on the computer and return stdout + stderr. Commands run as the default user inside the VM.",
364
- inputSchema: {
365
- type: "object",
366
- properties: {
367
- computer_id: { type: "string" },
368
- command: { type: "string", description: "Bash command to run" },
369
- timeout: {
370
- type: "integer",
371
- description: "Timeout in seconds (optional)",
372
- },
373
- },
374
- required: ["computer_id", "command"],
375
- },
376
- },
377
- {
378
- name: "computer_write_file",
379
- description: "Write text content to a file path inside the computer.",
380
- inputSchema: {
381
- type: "object",
382
- properties: {
383
- computer_id: { type: "string" },
384
- path: { type: "string", description: "Absolute path inside the VM" },
385
- content: { type: "string", description: "File content to write" },
386
- },
387
- required: ["computer_id", "path", "content"],
388
- },
389
- },
390
- {
391
- name: "computer_read_file",
392
- description: "Read a file from inside the computer and return its content as text.",
393
- inputSchema: {
394
- type: "object",
395
- properties: {
396
- computer_id: { type: "string" },
397
- path: { type: "string", description: "Absolute path inside the VM" },
398
- },
399
- required: ["computer_id", "path"],
400
- },
401
- },
402
- // Desktop — extended pointer / keyboard / window / env
403
- {
404
- name: "computer_right_click",
405
- description: "Right-click at the given screen coordinates.",
406
- inputSchema: {
407
- type: "object",
408
- properties: {
409
- computer_id: { type: "string", description: "Computer ID." },
410
- x: { type: "integer", description: "X coordinate in pixels" },
411
- y: { type: "integer", description: "Y coordinate in pixels" },
412
- },
413
- required: ["computer_id", "x", "y"],
414
- },
415
- },
416
- {
417
- name: "computer_mouse_down",
418
- description: "Press and hold a mouse button at (x, y). Pair with computer_mouse_up to release.",
419
- inputSchema: {
420
- type: "object",
421
- properties: {
422
- computer_id: { type: "string", description: "Computer ID." },
423
- x: { type: "integer" },
424
- y: { type: "integer" },
425
- button: {
426
- type: "string",
427
- enum: ["left", "right", "middle"],
428
- default: "left",
429
- },
430
- },
431
- required: ["computer_id", "x", "y"],
432
- },
433
- },
434
- {
435
- name: "computer_mouse_up",
436
- description: "Release a held mouse button at (x, y).",
437
- inputSchema: {
438
- type: "object",
439
- properties: {
440
- computer_id: { type: "string", description: "Computer ID." },
441
- x: { type: "integer" },
442
- y: { type: "integer" },
443
- button: {
444
- type: "string",
445
- enum: ["left", "right", "middle"],
446
- default: "left",
447
- },
448
- },
449
- required: ["computer_id", "x", "y"],
450
- },
451
- },
452
- {
453
- name: "computer_key_down",
454
- description: "Press and hold a key without releasing it. Pair with computer_key_up.",
455
- inputSchema: {
456
- type: "object",
457
- properties: {
458
- computer_id: { type: "string", description: "Computer ID." },
459
- key: { type: "string", description: "Key name to hold down" },
460
- },
461
- required: ["computer_id", "key"],
462
- },
463
- },
464
- {
465
- name: "computer_key_up",
466
- description: "Release a previously held key.",
467
- inputSchema: {
468
- type: "object",
469
- properties: {
470
- computer_id: { type: "string", description: "Computer ID." },
471
- key: { type: "string", description: "Key name to release" },
472
- },
473
- required: ["computer_id", "key"],
474
- },
475
- },
476
- {
477
- name: "computer_wait",
478
- description: "Pause execution inside the computer for N seconds.",
479
- inputSchema: {
480
- type: "object",
481
- properties: {
482
- computer_id: { type: "string", description: "Computer ID." },
483
- seconds: {
484
- type: "number",
485
- description: "Seconds to wait (may be fractional, e.g. 0.5)",
486
- },
487
- },
488
- required: ["computer_id", "seconds"],
489
- },
490
- },
491
- {
492
- name: "computer_focus_window",
493
- description: "Bring a window to the foreground by its window ID.",
494
- inputSchema: {
495
- type: "object",
496
- properties: {
497
- computer_id: { type: "string", description: "Computer ID." },
498
- window_id: {
499
- type: "string",
500
- description: "Window ID from computer_windows",
501
- },
502
- },
503
- required: ["computer_id", "window_id"],
504
- },
505
- },
506
- {
507
- name: "computer_set_window_size",
508
- description: "Resize a window to the given width and height in pixels.",
509
- inputSchema: {
510
- type: "object",
511
- properties: {
512
- computer_id: { type: "string", description: "Computer ID." },
513
- window_id: {
514
- type: "string",
515
- description: "Window ID from computer_windows",
516
- },
517
- width: { type: "integer" },
518
- height: { type: "integer" },
519
- },
520
- required: ["computer_id", "window_id", "width", "height"],
521
- },
522
- },
523
- {
524
- name: "computer_set_window_position",
525
- description: "Move a window to the given screen coordinates.",
526
- inputSchema: {
527
- type: "object",
528
- properties: {
529
- computer_id: { type: "string", description: "Computer ID." },
530
- window_id: {
531
- type: "string",
532
- description: "Window ID from computer_windows",
533
- },
534
- x: { type: "integer" },
535
- y: { type: "integer" },
536
- },
537
- required: ["computer_id", "window_id", "x", "y"],
538
- },
539
- },
540
- {
541
- name: "computer_maximize_window",
542
- description: "Maximize a window.",
543
- inputSchema: {
544
- type: "object",
545
- properties: {
546
- computer_id: { type: "string", description: "Computer ID." },
547
- window_id: {
548
- type: "string",
549
- description: "Window ID from computer_windows",
550
- },
551
- },
552
- required: ["computer_id", "window_id"],
553
- },
554
- },
555
- {
556
- name: "computer_minimize_window",
557
- description: "Minimize (iconify) a window.",
558
- inputSchema: {
559
- type: "object",
560
- properties: {
561
- computer_id: { type: "string", description: "Computer ID." },
562
- window_id: {
563
- type: "string",
564
- description: "Window ID from computer_windows",
565
- },
566
- },
567
- required: ["computer_id", "window_id"],
568
- },
569
- },
570
- {
571
- name: "computer_close_window",
572
- description: "Close a window.",
573
- inputSchema: {
574
- type: "object",
575
- properties: {
576
- computer_id: { type: "string", description: "Computer ID." },
577
- window_id: {
578
- type: "string",
579
- description: "Window ID from computer_windows",
580
- },
581
- },
582
- required: ["computer_id", "window_id"],
583
- },
584
- },
585
- {
586
- name: "computer_get_desktop_env",
587
- description: "Get desktop environment info (name, resolution, session type).",
588
- inputSchema: {
589
- type: "object",
590
- properties: {
591
- computer_id: { type: "string", description: "Computer ID." },
592
- },
593
- required: ["computer_id"],
594
- },
595
- },
596
- {
597
- name: "computer_set_wallpaper",
598
- description: "Set the desktop wallpaper. Accepts an absolute path inside the VM (e.g. '/home/ubuntu/bg.png') or an https:// URL.",
599
- inputSchema: {
600
- type: "object",
601
- properties: {
602
- computer_id: { type: "string", description: "Computer ID." },
603
- path: {
604
- type: "string",
605
- description: "Absolute VM path or https:// URL for the wallpaper image",
606
- },
607
- },
608
- required: ["computer_id", "path"],
609
- },
610
- },
611
- {
612
- name: "computer_accessibility_tree",
613
- description: "Get the AT-SPI accessibility tree for the current desktop state. Returns a nested JSON structure describing all visible UI elements with roles, names, bounding boxes, and parent/child relationships.",
614
- inputSchema: {
615
- type: "object",
616
- properties: {
617
- computer_id: { type: "string", description: "Computer ID." },
618
- },
619
- required: ["computer_id"],
620
- },
621
- },
622
- // Files — extended
623
- {
624
- name: "computer_list_files",
625
- description: "List directory contents on the computer.",
626
- inputSchema: {
627
- type: "object",
628
- properties: {
629
- computer_id: { type: "string" },
630
- path: {
631
- type: "string",
632
- description: "Directory path to list (default: /)",
633
- },
634
- },
635
- required: ["computer_id"],
636
- },
637
- },
638
- {
639
- name: "computer_stat_file",
640
- description: "Get file/directory metadata (size, type, permissions, mtime) inside the computer.",
641
- inputSchema: {
642
- type: "object",
643
- properties: {
644
- computer_id: { type: "string" },
645
- path: { type: "string", description: "Absolute path inside the VM" },
646
- },
647
- required: ["computer_id", "path"],
648
- },
649
- },
650
- {
651
- name: "computer_mkdir",
652
- description: "Create a directory (and any missing parents) inside the computer.",
653
- inputSchema: {
654
- type: "object",
655
- properties: {
656
- computer_id: { type: "string" },
657
- path: {
658
- type: "string",
659
- description: "Absolute directory path to create",
660
- },
661
- recursive: {
662
- type: "boolean",
663
- description: "Create parent directories if missing (default: true)",
664
- default: true,
665
- },
666
- },
667
- required: ["computer_id", "path"],
668
- },
669
- },
670
- {
671
- name: "computer_rename_file",
672
- description: "Rename or move a file/directory inside the computer.",
673
- inputSchema: {
674
- type: "object",
675
- properties: {
676
- computer_id: { type: "string" },
677
- source: { type: "string", description: "Current absolute path" },
678
- dest: { type: "string", description: "New absolute path" },
679
- },
680
- required: ["computer_id", "source", "dest"],
681
- },
682
- },
683
- {
684
- name: "computer_copy_file",
685
- description: "Copy a file or directory tree inside the computer.",
686
- inputSchema: {
687
- type: "object",
688
- properties: {
689
- computer_id: { type: "string" },
690
- source: { type: "string", description: "Source absolute path" },
691
- dest: { type: "string", description: "Destination absolute path" },
692
- recursive: {
693
- type: "boolean",
694
- description: "Copy directory trees recursively (default: false)",
695
- default: false,
696
- },
697
- },
698
- required: ["computer_id", "source", "dest"],
699
- },
700
- },
701
- {
702
- name: "computer_delete_file",
703
- description: "Delete a file or directory inside the computer.",
704
- inputSchema: {
705
- type: "object",
706
- properties: {
707
- computer_id: { type: "string" },
708
- path: { type: "string", description: "Absolute path to delete" },
709
- },
710
- required: ["computer_id", "path"],
711
- },
712
- },
713
- {
714
- name: "computer_upload_file",
715
- description: "Upload a local file from the host machine into the computer at a given path.",
716
- inputSchema: {
717
- type: "object",
718
- properties: {
719
- computer_id: { type: "string" },
720
- local_path: {
721
- type: "string",
722
- description: "Absolute path on the local host to upload",
723
- },
724
- remote_path: {
725
- type: "string",
726
- description: "Destination absolute path inside the VM",
727
- },
728
- },
729
- required: ["computer_id", "local_path", "remote_path"],
730
- },
731
- },
732
- // Checkpoints
733
- {
734
- name: "computer_checkpoint_create",
735
- description: "Save the current state of the computer as a checkpoint (snapshot).",
736
- inputSchema: {
737
- type: "object",
738
- properties: {
739
- computer_id: { type: "string" },
740
- comment: {
741
- type: "string",
742
- description: "Optional human-readable label for the checkpoint",
743
- },
744
- },
745
- required: ["computer_id"],
746
- },
747
- },
748
- {
749
- name: "computer_checkpoint_list",
750
- description: "List all saved checkpoints for the computer.",
751
- inputSchema: {
752
- type: "object",
753
- properties: {
754
- computer_id: { type: "string" },
755
- },
756
- required: ["computer_id"],
757
- },
758
- },
759
- {
760
- name: "computer_checkpoint_restore",
761
- description: "Restore the computer to a previously saved checkpoint.",
762
- inputSchema: {
763
- type: "object",
764
- properties: {
765
- computer_id: { type: "string" },
766
- checkpoint_id: {
767
- type: "string",
768
- description: "ID of the checkpoint to restore",
769
- },
770
- },
771
- required: ["computer_id", "checkpoint_id"],
772
- },
773
- },
774
- {
775
- name: "computer_checkpoint_delete",
776
- description: "Delete a saved checkpoint.",
777
- inputSchema: {
778
- type: "object",
779
- properties: {
780
- computer_id: { type: "string" },
781
- checkpoint_id: {
782
- type: "string",
783
- description: "ID of the checkpoint to delete",
784
- },
785
- },
786
- required: ["computer_id", "checkpoint_id"],
787
- },
788
- },
789
- // Services
790
- {
791
- name: "computer_service_create",
792
- description: "Create and start a long-running background service on the computer (like systemd — process manager for your VM).",
793
- inputSchema: {
794
- type: "object",
795
- properties: {
796
- computer_id: { type: "string" },
797
- name: { type: "string", description: "Service name (must be unique)" },
798
- command: { type: "string", description: "Shell command to run" },
799
- working_dir: {
800
- type: "string",
801
- description: "Working directory for the service (optional)",
802
- },
803
- port: {
804
- type: "integer",
805
- description: "Port the service listens on (optional)",
806
- },
807
- restart_policy: {
808
- type: "string",
809
- enum: ["always", "on-failure", "never"],
810
- description: "Restart behaviour (default: always)",
811
- },
812
- },
813
- required: ["computer_id", "name", "command"],
814
- },
815
- },
816
- {
817
- name: "computer_service_list",
818
- description: "List all background services registered on the computer.",
819
- inputSchema: {
820
- type: "object",
821
- properties: {
822
- computer_id: { type: "string" },
823
- },
824
- required: ["computer_id"],
825
- },
826
- },
827
- {
828
- name: "computer_service_start",
829
- description: "Start a stopped background service.",
830
- inputSchema: {
831
- type: "object",
832
- properties: {
833
- computer_id: { type: "string" },
834
- service_id: { type: "string", description: "Service ID to start" },
835
- },
836
- required: ["computer_id", "service_id"],
837
- },
838
- },
839
- {
840
- name: "computer_service_stop",
841
- description: "Stop a running background service (sends SIGTERM).",
842
- inputSchema: {
843
- type: "object",
844
- properties: {
845
- computer_id: { type: "string" },
846
- service_id: { type: "string", description: "Service ID to stop" },
847
- },
848
- required: ["computer_id", "service_id"],
849
- },
850
- },
851
- {
852
- name: "computer_service_restart",
853
- description: "Restart a background service (stop then start).",
854
- inputSchema: {
855
- type: "object",
856
- properties: {
857
- computer_id: { type: "string" },
858
- service_id: { type: "string", description: "Service ID to restart" },
859
- },
860
- required: ["computer_id", "service_id"],
861
- },
862
- },
863
- {
864
- name: "computer_service_logs",
865
- description: "Retrieve recent log output from a background service (last 100 lines).",
866
- inputSchema: {
867
- type: "object",
868
- properties: {
869
- computer_id: { type: "string" },
870
- service_id: { type: "string", description: "Service ID" },
871
- },
872
- required: ["computer_id", "service_id"],
873
- },
874
- },
875
- {
876
- name: "computer_service_delete",
877
- description: "Delete a background service (stops it first if running).",
878
- inputSchema: {
879
- type: "object",
880
- properties: {
881
- computer_id: { type: "string" },
882
- service_id: { type: "string", description: "Service ID to delete" },
883
- },
884
- required: ["computer_id", "service_id"],
885
- },
886
- },
887
- // Env vars
888
- {
889
- name: "computer_env_list",
890
- description: "List all environment variables set on the computer.",
891
- inputSchema: {
892
- type: "object",
893
- properties: {
894
- computer_id: { type: "string" },
895
- },
896
- required: ["computer_id"],
897
- },
898
- },
899
- {
900
- name: "computer_env_set",
901
- description: "Set (create or update) an environment variable on the computer.",
902
- inputSchema: {
903
- type: "object",
904
- properties: {
905
- computer_id: { type: "string" },
906
- name: { type: "string", description: "Variable name" },
907
- value: { type: "string", description: "Variable value" },
908
- },
909
- required: ["computer_id", "name", "value"],
910
- },
911
- },
912
- {
913
- name: "computer_env_delete",
914
- description: "Delete an environment variable from the computer.",
915
- inputSchema: {
916
- type: "object",
917
- properties: {
918
- computer_id: { type: "string" },
919
- name: {
920
- type: "string",
921
- description: "Variable name to delete",
922
- },
923
- },
924
- required: ["computer_id", "name"],
925
- },
926
- },
927
- // Logs
928
- {
929
- name: "computer_logs",
930
- description: "Get recent VM-level logs from the computer.",
931
- inputSchema: {
932
- type: "object",
933
- properties: {
934
- computer_id: { type: "string" },
935
- lines: {
936
- type: "integer",
937
- description: "Number of recent log lines to return (optional)",
938
- },
939
- },
940
- required: ["computer_id"],
941
- },
942
- },
943
- // Domains
944
- {
945
- name: "computer_domain_add",
946
- description: "Add a custom domain to the computer. Returns CNAME verification instructions to add to your DNS registrar.",
947
- inputSchema: {
948
- type: "object",
949
- properties: {
950
- computer_id: { type: "string" },
951
- fqdn: {
952
- type: "string",
953
- description: "Fully-qualified domain name (e.g. app.example.com)",
954
- },
955
- },
956
- required: ["computer_id", "fqdn"],
957
- },
958
- },
959
- {
960
- name: "computer_domain_list",
961
- description: "List all custom domains registered for the computer.",
962
- inputSchema: {
963
- type: "object",
964
- properties: {
965
- computer_id: { type: "string" },
966
- },
967
- required: ["computer_id"],
968
- },
969
- },
970
- {
971
- name: "computer_domain_delete",
972
- description: "Delete a custom domain mapping from the computer.",
973
- inputSchema: {
974
- type: "object",
975
- properties: {
976
- computer_id: { type: "string" },
977
- domain_id: {
978
- type: "string",
979
- description: "Domain ID to delete",
980
- },
981
- },
982
- required: ["computer_id", "domain_id"],
983
- },
984
- },
985
- // Sandboxes
986
- {
987
- name: "sandbox_create",
988
- description: "Create a new lightweight code sandbox (Firecracker microVM without desktop).",
989
- inputSchema: {
990
- type: "object",
991
- properties: {
992
- name: {
993
- type: "string",
994
- description: "Human-readable name for the sandbox",
995
- },
996
- template_id: {
997
- type: "string",
998
- description: "Template / image ID (default: miosa-sandbox)",
999
- },
1000
- size: {
1001
- type: "string",
1002
- enum: [...SANDBOX_SIZES],
1003
- description: "Canonical sandbox size (default: small)",
1004
- default: "small",
1005
- },
1006
- timeout_sec: {
1007
- type: "integer",
1008
- description: "Idle timeout in seconds",
1009
- },
1010
- },
1011
- },
1012
- },
1013
- {
1014
- name: "sandbox_exec",
1015
- description: "Run a bash command inside a sandbox and return the output.",
1016
- inputSchema: {
1017
- type: "object",
1018
- properties: {
1019
- sandbox_id: { type: "string", description: "Sandbox ID" },
1020
- command: { type: "string", description: "Command to run" },
1021
- cwd: { type: "string", description: "Working directory (optional)" },
1022
- timeout: {
1023
- type: "integer",
1024
- description: "Timeout in seconds (optional)",
1025
- },
1026
- },
1027
- required: ["sandbox_id", "command"],
1028
- },
1029
- },
1030
- {
1031
- name: "sandbox_destroy",
1032
- description: "Destroy a sandbox permanently.",
1033
- inputSchema: {
1034
- type: "object",
1035
- properties: {
1036
- sandbox_id: { type: "string", description: "Sandbox ID to destroy" },
1037
- },
1038
- required: ["sandbox_id"],
1039
- },
1040
- },
1041
- // Sandbox lifecycle (list/get/pause/resume)
1042
- {
1043
- name: "sandbox_list",
1044
- description: "List all sandboxes in the tenant.",
1045
- inputSchema: {
1046
- type: "object",
1047
- properties: {
1048
- state: {
1049
- type: "string",
1050
- enum: ["provisioning", "running", "paused", "destroyed", "error"],
1051
- description: "Filter by state (optional)",
1052
- },
1053
- },
1054
- },
1055
- },
1056
- {
1057
- name: "sandbox_get",
1058
- description: "Get details of a specific sandbox.",
1059
- inputSchema: {
1060
- type: "object",
1061
- properties: {
1062
- sandbox_id: { type: "string", description: "Sandbox ID" },
1063
- },
1064
- required: ["sandbox_id"],
1065
- },
1066
- },
1067
- {
1068
- name: "sandbox_pause",
1069
- description: "Pause a running sandbox (suspends it to save compute).",
1070
- inputSchema: {
1071
- type: "object",
1072
- properties: {
1073
- sandbox_id: { type: "string", description: "Sandbox ID" },
1074
- },
1075
- required: ["sandbox_id"],
1076
- },
1077
- },
1078
- {
1079
- name: "sandbox_resume",
1080
- description: "Resume a paused sandbox.",
1081
- inputSchema: {
1082
- type: "object",
1083
- properties: {
1084
- sandbox_id: { type: "string", description: "Sandbox ID" },
1085
- },
1086
- required: ["sandbox_id"],
1087
- },
1088
- },
1089
- // Sandbox files
1090
- {
1091
- name: "sandbox_write_file",
1092
- description: "Write text content to a file path inside a sandbox.",
1093
- inputSchema: {
1094
- type: "object",
1095
- properties: {
1096
- sandbox_id: { type: "string", description: "Sandbox ID" },
1097
- path: {
1098
- type: "string",
1099
- description: "Absolute path inside the sandbox",
1100
- },
1101
- content: { type: "string", description: "File content (text)" },
1102
- },
1103
- required: ["sandbox_id", "path", "content"],
1104
- },
1105
- },
1106
- {
1107
- name: "sandbox_read_file",
1108
- description: "Read a file from inside a sandbox and return its content as text.",
1109
- inputSchema: {
1110
- type: "object",
1111
- properties: {
1112
- sandbox_id: { type: "string", description: "Sandbox ID" },
1113
- path: {
1114
- type: "string",
1115
- description: "Absolute path inside the sandbox",
1116
- },
1117
- },
1118
- required: ["sandbox_id", "path"],
1119
- },
1120
- },
1121
- {
1122
- name: "sandbox_list_files",
1123
- description: "List files in a directory inside a sandbox.",
1124
- inputSchema: {
1125
- type: "object",
1126
- properties: {
1127
- sandbox_id: { type: "string", description: "Sandbox ID" },
1128
- path: {
1129
- type: "string",
1130
- description: "Directory path to list (default: /workspace)",
1131
- default: "/workspace",
1132
- },
1133
- depth: { type: "integer", description: "Recursion depth (optional)" },
1134
- },
1135
- required: ["sandbox_id"],
1136
- },
1137
- },
1138
- {
1139
- name: "sandbox_upload",
1140
- description: "Upload a file (UTF-8 text or base64 binary) into a sandbox.",
1141
- inputSchema: {
1142
- type: "object",
1143
- properties: {
1144
- sandbox_id: { type: "string", description: "Sandbox ID" },
1145
- path: {
1146
- type: "string",
1147
- description: "Destination path inside the sandbox",
1148
- },
1149
- content: {
1150
- type: "string",
1151
- description: "File content as UTF-8 text or base64-encoded binary",
1152
- },
1153
- },
1154
- required: ["sandbox_id", "path", "content"],
1155
- },
1156
- },
1157
- // Sandbox exec
1158
- {
1159
- name: "sandbox_python",
1160
- description: "Run Python code inside a sandbox and return stdout, stderr, and exit code.",
1161
- inputSchema: {
1162
- type: "object",
1163
- properties: {
1164
- sandbox_id: { type: "string", description: "Sandbox ID" },
1165
- code: { type: "string", description: "Python code to execute" },
1166
- timeout: {
1167
- type: "integer",
1168
- description: "Timeout in seconds (optional)",
1169
- },
1170
- },
1171
- required: ["sandbox_id", "code"],
1172
- },
1173
- },
1174
- // Sandbox snapshots
1175
- {
1176
- name: "sandbox_snapshot_create",
1177
- description: "Create a snapshot of the current sandbox state.",
1178
- inputSchema: {
1179
- type: "object",
1180
- properties: {
1181
- sandbox_id: { type: "string", description: "Sandbox ID" },
1182
- comment: {
1183
- type: "string",
1184
- description: "Optional comment for the snapshot",
1185
- },
1186
- },
1187
- required: ["sandbox_id"],
1188
- },
1189
- },
1190
- {
1191
- name: "sandbox_snapshot_list",
1192
- description: "List all snapshots for a sandbox.",
1193
- inputSchema: {
1194
- type: "object",
1195
- properties: {
1196
- sandbox_id: { type: "string", description: "Sandbox ID" },
1197
- },
1198
- required: ["sandbox_id"],
1199
- },
1200
- },
1201
- {
1202
- name: "sandbox_snapshot_restore",
1203
- description: "Restore a sandbox from a specific snapshot.",
1204
- inputSchema: {
1205
- type: "object",
1206
- properties: {
1207
- sandbox_id: { type: "string", description: "Sandbox ID" },
1208
- snapshot_id: {
1209
- type: "string",
1210
- description: "Snapshot ID to restore from",
1211
- },
1212
- },
1213
- required: ["sandbox_id", "snapshot_id"],
1214
- },
1215
- },
1216
- // Sandbox logs
1217
- {
1218
- name: "sandbox_logs",
1219
- description: "Get logs from a sandbox.",
1220
- inputSchema: {
1221
- type: "object",
1222
- properties: {
1223
- sandbox_id: { type: "string", description: "Sandbox ID" },
1224
- lines: {
1225
- type: "integer",
1226
- description: "Number of log lines to return (optional)",
1227
- },
1228
- },
1229
- required: ["sandbox_id"],
1230
- },
1231
- },
1232
- // Sandbox preview
1233
- {
1234
- name: "sandbox_expose",
1235
- description: "Expose a port on a sandbox and return a publicly accessible preview URL.",
1236
- inputSchema: {
1237
- type: "object",
1238
- properties: {
1239
- sandbox_id: { type: "string", description: "Sandbox ID" },
1240
- port: {
1241
- type: "integer",
1242
- description: "Port number to expose (optional; exposes default port if omitted)",
1243
- },
1244
- },
1245
- required: ["sandbox_id"],
1246
- },
1247
- },
1248
- // Sandbox deploy
1249
- {
1250
- name: "sandbox_deploy",
1251
- description: "Deploy sandbox contents to production.",
1252
- inputSchema: {
1253
- type: "object",
1254
- properties: {
1255
- sandbox_id: { type: "string", description: "Sandbox ID" },
1256
- name: { type: "string", description: "Deployment name (optional)" },
1257
- output_path: {
1258
- type: "string",
1259
- description: "Path inside the sandbox to deploy (optional)",
1260
- },
1261
- entrypoint: {
1262
- type: "string",
1263
- description: "Entrypoint command or file (optional)",
1264
- },
1265
- domain: { type: "string", description: "Custom domain (optional)" },
1266
- },
1267
- required: ["sandbox_id"],
1268
- },
1269
- },
1270
- // Sandbox templates
1271
- {
1272
- name: "sandbox_template_list",
1273
- description: "List available sandbox templates.",
1274
- inputSchema: { type: "object", properties: {} },
1275
- },
1276
- {
1277
- name: "sandbox_template_create",
1278
- description: "Create a custom sandbox template from a build spec.",
1279
- inputSchema: {
1280
- type: "object",
1281
- properties: {
1282
- name: { type: "string", description: "Template name" },
1283
- build_spec: {
1284
- type: "object",
1285
- description: "Build specification (Dockerfile, packages, etc.)",
1286
- },
1287
- slug: {
1288
- type: "string",
1289
- description: "URL-friendly identifier (optional)",
1290
- },
1291
- description: {
1292
- type: "string",
1293
- description: "Human-readable description (optional)",
1294
- },
1295
- },
1296
- required: ["name", "build_spec"],
1297
- },
1298
- },
1299
- // Deploy
1300
- {
1301
- name: "deploy",
1302
- description: "Trigger a redeploy for an existing MIOSA deployment.",
1303
- inputSchema: {
1304
- type: "object",
1305
- properties: {
1306
- deployment_id: {
1307
- type: "string",
1308
- description: "Deployment ID to redeploy",
1309
- },
1310
- },
1311
- required: ["deployment_id"],
1312
- },
1313
- },
1314
- // Deployments
1315
- {
1316
- name: 'deployment_list',
1317
- description: 'List all deployments in the tenant.',
1318
- inputSchema: { type: 'object', properties: {} },
1319
- },
1320
- {
1321
- name: 'deployment_get',
1322
- description: 'Get details of a specific deployment.',
1323
- inputSchema: {
1324
- type: 'object',
1325
- properties: {
1326
- deployment_id: { type: 'string', description: 'Deployment ID' },
1327
- },
1328
- required: ['deployment_id'],
1329
- },
1330
- },
1331
- {
1332
- name: 'deployment_create',
1333
- description: 'Create a new deployment.',
1334
- inputSchema: {
1335
- type: 'object',
1336
- properties: {
1337
- name: { type: 'string', description: 'Deployment name' },
1338
- type: { type: 'string', description: 'Deployment type (e.g. web, worker)' },
1339
- source: { type: 'object', description: 'Source configuration' },
1340
- env_vars: { type: 'object', description: 'Environment variables as key-value pairs' },
1341
- region: { type: 'string', description: 'Deployment region (optional)' },
1342
- },
1343
- required: ['name'],
1344
- },
1345
- },
1346
- {
1347
- name: 'deployment_delete',
1348
- description: 'Delete a deployment permanently.',
1349
- inputSchema: {
1350
- type: 'object',
1351
- properties: {
1352
- deployment_id: { type: 'string', description: 'Deployment ID to delete' },
1353
- },
1354
- required: ['deployment_id'],
1355
- },
1356
- },
1357
- {
1358
- name: 'deployment_publish',
1359
- description: 'Publish a new version of a deployment.',
1360
- inputSchema: {
1361
- type: 'object',
1362
- properties: {
1363
- deployment_id: { type: 'string', description: 'Deployment ID' },
1364
- source: { type: 'object', description: 'Source configuration for the new version' },
1365
- },
1366
- required: ['deployment_id'],
1367
- },
1368
- },
1369
- {
1370
- name: 'deployment_rollback',
1371
- description: 'Rollback a deployment to a previous version.',
1372
- inputSchema: {
1373
- type: 'object',
1374
- properties: {
1375
- deployment_id: { type: 'string', description: 'Deployment ID' },
1376
- version_id: { type: 'string', description: 'Version ID to roll back to' },
1377
- },
1378
- required: ['deployment_id', 'version_id'],
1379
- },
1380
- },
1381
- {
1382
- name: 'deployment_env_list',
1383
- description: 'List environment variables for a deployment.',
1384
- inputSchema: {
1385
- type: 'object',
1386
- properties: {
1387
- deployment_id: { type: 'string', description: 'Deployment ID' },
1388
- },
1389
- required: ['deployment_id'],
1390
- },
1391
- },
1392
- {
1393
- name: 'deployment_env_set',
1394
- description: 'Set (create or update) an environment variable for a deployment.',
1395
- inputSchema: {
1396
- type: 'object',
1397
- properties: {
1398
- deployment_id: { type: 'string', description: 'Deployment ID' },
1399
- key: { type: 'string', description: 'Environment variable name' },
1400
- value: { type: 'string', description: 'Environment variable value' },
1401
- },
1402
- required: ['deployment_id', 'key', 'value'],
1403
- },
1404
- },
1405
- {
1406
- name: 'deployment_logs',
1407
- description: 'Get logs for a deployment.',
1408
- inputSchema: {
1409
- type: 'object',
1410
- properties: {
1411
- deployment_id: { type: 'string', description: 'Deployment ID' },
1412
- lines: { type: 'integer', description: 'Number of log lines to return (default: 100)', default: 100 },
1413
- since: { type: 'string', description: 'ISO 8601 timestamp to fetch logs from (optional)' },
1414
- },
1415
- required: ['deployment_id'],
1416
- },
1417
- },
1418
- {
1419
- name: 'deployment_version_list',
1420
- description: 'List all versions of a deployment.',
1421
- inputSchema: {
1422
- type: 'object',
1423
- properties: {
1424
- deployment_id: { type: 'string', description: 'Deployment ID' },
1425
- },
1426
- required: ['deployment_id'],
1427
- },
1428
- },
1429
- {
1430
- name: 'deployment_version_promote',
1431
- description: 'Promote a specific version to be the active deployment.',
1432
- inputSchema: {
1433
- type: 'object',
1434
- properties: {
1435
- deployment_id: { type: 'string', description: 'Deployment ID' },
1436
- version_id: { type: 'string', description: 'Version ID to promote' },
1437
- },
1438
- required: ['deployment_id', 'version_id'],
1439
- },
1440
- },
1441
- // Storage
1442
- {
1443
- name: 'storage_bucket_list',
1444
- description: 'List all storage buckets in the tenant.',
1445
- inputSchema: { type: 'object', properties: {} },
1446
- },
1447
- {
1448
- name: 'storage_bucket_create',
1449
- description: 'Create a new storage bucket.',
1450
- inputSchema: {
1451
- type: 'object',
1452
- properties: {
1453
- name: { type: 'string', description: 'Bucket name' },
1454
- region: { type: 'string', description: 'Bucket region (optional)' },
1455
- public: { type: 'boolean', description: 'Whether the bucket is publicly readable (default: false)', default: false },
1456
- },
1457
- required: ['name'],
1458
- },
1459
- },
1460
- {
1461
- name: 'storage_bucket_delete',
1462
- description: 'Delete a storage bucket.',
1463
- inputSchema: {
1464
- type: 'object',
1465
- properties: {
1466
- bucket_id: { type: 'string', description: 'Bucket ID or name to delete' },
1467
- },
1468
- required: ['bucket_id'],
1469
- },
1470
- },
1471
- {
1472
- name: 'storage_object_list',
1473
- description: 'List objects in a storage bucket, optionally filtered by prefix.',
1474
- inputSchema: {
1475
- type: 'object',
1476
- properties: {
1477
- bucket_id: { type: 'string', description: 'Bucket ID or name' },
1478
- prefix: { type: 'string', description: 'Key prefix to filter by (optional)' },
1479
- },
1480
- required: ['bucket_id'],
1481
- },
1482
- },
1483
- {
1484
- name: 'storage_object_upload',
1485
- description: 'Upload an object to a storage bucket.',
1486
- inputSchema: {
1487
- type: 'object',
1488
- properties: {
1489
- bucket_id: { type: 'string', description: 'Bucket ID or name' },
1490
- key: { type: 'string', description: 'Object key (path within bucket)' },
1491
- content: { type: 'string', description: 'Object content (text or base64-encoded binary)' },
1492
- content_type: { type: 'string', description: 'MIME type of the object (optional)' },
1493
- },
1494
- required: ['bucket_id', 'key', 'content'],
1495
- },
1496
- },
1497
- {
1498
- name: 'storage_object_download',
1499
- description: 'Download an object from a storage bucket.',
1500
- inputSchema: {
1501
- type: 'object',
1502
- properties: {
1503
- bucket_id: { type: 'string', description: 'Bucket ID or name' },
1504
- key: { type: 'string', description: 'Object key to download' },
1505
- },
1506
- required: ['bucket_id', 'key'],
1507
- },
1508
- },
1509
- {
1510
- name: 'storage_object_delete',
1511
- description: 'Delete an object from a storage bucket.',
1512
- inputSchema: {
1513
- type: 'object',
1514
- properties: {
1515
- bucket_id: { type: 'string', description: 'Bucket ID or name' },
1516
- key: { type: 'string', description: 'Object key to delete' },
1517
- },
1518
- required: ['bucket_id', 'key'],
1519
- },
1520
- },
1521
- {
1522
- name: 'storage_presign',
1523
- description: 'Get a presigned URL for temporary access to a storage object.',
1524
- inputSchema: {
1525
- type: 'object',
1526
- properties: {
1527
- bucket_id: { type: 'string', description: 'Bucket ID or name' },
1528
- key: { type: 'string', description: 'Object key' },
1529
- expires_in: { type: 'integer', description: 'URL expiry in seconds (default: 3600)', default: 3600 },
1530
- method: { type: 'string', enum: ['GET', 'PUT'], description: 'HTTP method (default: GET)', default: 'GET' },
1531
- },
1532
- required: ['bucket_id', 'key'],
1533
- },
1534
- },
1535
- // Databases
1536
- {
1537
- name: 'database_list',
1538
- description: 'List all managed databases in the tenant.',
1539
- inputSchema: { type: 'object', properties: {} },
1540
- },
1541
- {
1542
- name: 'database_create',
1543
- description: 'Create a new managed database.',
1544
- inputSchema: {
1545
- type: 'object',
1546
- properties: {
1547
- name: { type: 'string', description: 'Database name' },
1548
- engine: { type: 'string', enum: ['postgres', 'postgresql', 'mysql', 'redis'], description: 'Database engine' },
1549
- version: { type: 'string', description: 'Engine version (optional)' },
1550
- size: { type: 'string', description: 'Database size/tier (optional)' },
1551
- region: { type: 'string', description: 'Region (optional)' },
1552
- },
1553
- required: ['name', 'engine'],
1554
- },
1555
- },
1556
- {
1557
- name: 'database_get',
1558
- description: 'Get details of a specific database.',
1559
- inputSchema: {
1560
- type: 'object',
1561
- properties: {
1562
- database_id: { type: 'string', description: 'Database ID' },
1563
- },
1564
- required: ['database_id'],
1565
- },
1566
- },
1567
- {
1568
- name: 'database_delete',
1569
- description: 'Delete a managed database permanently.',
1570
- inputSchema: {
1571
- type: 'object',
1572
- properties: {
1573
- database_id: { type: 'string', description: 'Database ID to delete' },
1574
- },
1575
- required: ['database_id'],
1576
- },
1577
- },
1578
- {
1579
- name: 'database_credentials',
1580
- description: 'Get the connection string and credentials for a database.',
1581
- inputSchema: {
1582
- type: 'object',
1583
- properties: {
1584
- database_id: { type: 'string', description: 'Database ID' },
1585
- },
1586
- required: ['database_id'],
1587
- },
1588
- },
1589
- {
1590
- name: 'database_logs',
1591
- description: 'Get logs for a managed database.',
1592
- inputSchema: {
1593
- type: 'object',
1594
- properties: {
1595
- database_id: { type: 'string', description: 'Database ID' },
1596
- lines: { type: 'integer', description: 'Number of log lines to return (default: 100)', default: 100 },
1597
- since: { type: 'string', description: 'ISO 8601 timestamp to fetch logs from (optional)' },
1598
- },
1599
- required: ['database_id'],
1600
- },
1601
- },
1602
- // Workspaces
1603
- {
1604
- name: 'workspace_list',
1605
- description: 'List all workspaces in the tenant.',
1606
- inputSchema: { type: 'object', properties: {} },
1607
- },
1608
- {
1609
- name: 'workspace_create',
1610
- description: 'Create a new workspace.',
1611
- inputSchema: {
1612
- type: 'object',
1613
- properties: {
1614
- name: { type: 'string', description: 'Workspace name' },
1615
- description: { type: 'string', description: 'Workspace description (optional)' },
1616
- },
1617
- required: ['name'],
1618
- },
1619
- },
1620
- {
1621
- name: 'workspace_get',
1622
- description: 'Get details of a specific workspace.',
1623
- inputSchema: {
1624
- type: 'object',
1625
- properties: {
1626
- workspace_id: { type: 'string', description: 'Workspace ID' },
1627
- },
1628
- required: ['workspace_id'],
1629
- },
1630
- },
1631
- {
1632
- name: 'workspace_update',
1633
- description: "Update a workspace's name or description.",
1634
- inputSchema: {
1635
- type: 'object',
1636
- properties: {
1637
- workspace_id: { type: 'string', description: 'Workspace ID' },
1638
- name: { type: 'string', description: 'New workspace name (optional)' },
1639
- description: { type: 'string', description: 'New description (optional)' },
1640
- },
1641
- required: ['workspace_id'],
1642
- },
1643
- },
1644
- {
1645
- name: 'workspace_stats',
1646
- description: 'Get resource statistics for a workspace (computers, sandboxes, databases, etc.).',
1647
- inputSchema: {
1648
- type: 'object',
1649
- properties: {
1650
- workspace_id: { type: 'string', description: 'Workspace ID' },
1651
- },
1652
- required: ['workspace_id'],
1653
- },
1654
- },
1655
- {
1656
- name: 'workspace_usage',
1657
- description: 'Get usage data (compute hours, storage, bandwidth) for a workspace.',
1658
- inputSchema: {
1659
- type: 'object',
1660
- properties: {
1661
- workspace_id: { type: 'string', description: 'Workspace ID' },
1662
- period: { type: 'string', description: "Billing period (e.g. '2026-05'). Defaults to current month." },
1663
- },
1664
- required: ['workspace_id'],
1665
- },
1666
- },
1667
- // Billing
1668
- {
1669
- name: 'billing_usage',
1670
- description: 'Get current billing period usage for the tenant.',
1671
- inputSchema: { type: 'object', properties: {} },
1672
- },
1673
- {
1674
- name: 'billing_plan',
1675
- description: 'Get the current billing plan details for the tenant.',
1676
- inputSchema: { type: 'object', properties: {} },
1677
- },
1678
- ];
1679
- // ── Wire helpers ─────────────────────────────────────────────────────────────
1680
- function ok(text) {
1681
- return { content: [{ type: "text", text }] };
1682
- }
1683
- function err(msg) {
1684
- return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
1685
- }
1686
- function shellQuote(value) {
1687
- return `'${value.replace(/'/g, "'\\''")}'`;
1688
- }
1689
- function commandInCwd(command, cwd) {
1690
- return cwd ? `cd ${shellQuote(cwd)} && ${command}` : command;
1691
- }
1692
- function normalizeDatabaseEngine(engine) {
1693
- const value = String(engine ?? "postgresql").trim().toLowerCase();
1694
- return value === "postgres" ? "postgresql" : value;
1695
- }
1696
- function image(pngBytes) {
1697
- return {
1698
- content: [
1699
- {
1700
- type: "image",
1701
- data: pngBytes.toString("base64"),
1702
- mimeType: "image/png",
1703
- },
1704
- ],
1705
- };
1706
- }
1707
- function send(response) {
1708
- process.stdout.write(JSON.stringify(response) + "\n");
1709
- }
1710
- // ── Tool dispatch ────────────────────────────────────────────────────────────
1711
- export async function dispatchMcpTool(client, name, args) {
1712
- const cid = typeof args["computer_id"] === "string" ? args["computer_id"] : undefined;
1713
- try {
1714
- // ── Lifecycle ──────────────────────────────────────────────────────────
1715
- if (name === "computer_create") {
1716
- const body = { name: args["name"] };
1717
- if (args["template_type"])
1718
- body["template_type"] = args["template_type"];
1719
- if (args["size"])
1720
- body["size"] = args["size"];
1721
- if (args["workspace_id"])
1722
- body["workspace_id"] = args["workspace_id"];
1723
- if (args["external_workspace_id"])
1724
- body["external_workspace_id"] = args["external_workspace_id"];
1725
- if (args["external_project_id"])
1726
- body["external_project_id"] = args["external_project_id"];
1727
- const computer = await client.apiPost("/api/v1/computers", body);
1728
- const data = unwrapData(computer);
1729
- const id = String(data["id"] ?? "");
1730
- const compName = String(data["name"] ?? args["name"]);
1731
- // Poll until the computer reaches "active" state (mirrors Python MCP behaviour)
1732
- const POLL_INTERVAL_MS = 2_000;
1733
- const POLL_TIMEOUT_MS = 30_000;
1734
- const deadline = Date.now() + POLL_TIMEOUT_MS;
1735
- let finalStatus = String(data["status"] ?? data["state"] ?? "created");
1736
- while (finalStatus !== "active" && Date.now() < deadline) {
1737
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
1738
- try {
1739
- const poll = await client.apiGet(`/api/v1/computers/${encodeURIComponent(id)}`);
1740
- const pollData = unwrapData(poll);
1741
- finalStatus = String(pollData["status"] ?? pollData["state"] ?? finalStatus);
1742
- }
1743
- catch {
1744
- // Transient error during poll — keep waiting
1745
- }
1746
- }
1747
- return ok(`Created computer '${compName}' (id=${id}, status=${finalStatus}).`);
1748
- }
1749
- if (name === "computer_list") {
1750
- const result = await client.apiGet("/api/v1/computers");
1751
- const items = listOf(result);
1752
- if (items.length === 0)
1753
- return ok("No computers found.");
1754
- const lines = ["Available computers:"];
1755
- for (const c of items) {
1756
- const r = c;
1757
- lines.push(` ${r["id"]} ${r["name"]} ${r["status"] ?? r["state"]}`);
1758
- }
1759
- return ok(lines.join("\n"));
1760
- }
1761
- if (name === "computer_destroy") {
1762
- if (!cid)
1763
- return err("computer_id is required");
1764
- await client.apiDelete(`/api/v1/computers/${encodeURIComponent(cid)}`);
1765
- return ok(`Computer ${cid} destroyed.`);
1766
- }
1767
- if (name === "computer_get") {
1768
- if (!cid)
1769
- return err("computer_id is required");
1770
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}`);
1771
- const data = unwrapData(result);
1772
- return ok(`id=${data["id"]} name=${JSON.stringify(data["name"])} status=${data["status"] ?? data["state"]}`);
1773
- }
1774
- if (name === "computer_start") {
1775
- if (!cid)
1776
- return err("computer_id is required");
1777
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/start`, {});
1778
- return ok(`Computer ${cid} start issued.`);
1779
- }
1780
- if (name === "computer_stop") {
1781
- if (!cid)
1782
- return err("computer_id is required");
1783
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/stop`, {});
1784
- return ok(`Computer ${cid} stop issued.`);
1785
- }
1786
- if (name === "computer_restart") {
1787
- if (!cid)
1788
- return err("computer_id is required");
1789
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/restart`, {});
1790
- return ok(`Computer ${cid} restart issued.`);
1791
- }
1792
- if (name === "computer_update") {
1793
- if (!cid)
1794
- return err("computer_id is required");
1795
- const body = {};
1796
- if (typeof args["name"] === "string")
1797
- body["name"] = args["name"];
1798
- if (args["metadata"] !== undefined)
1799
- body["metadata"] = args["metadata"];
1800
- const result = await client.apiPatch(`/api/v1/computers/${encodeURIComponent(cid)}`, body);
1801
- const data = unwrapData(result);
1802
- return ok(`Computer ${cid} updated: name=${JSON.stringify(data["name"] ?? args["name"])}.`);
1803
- }
1804
- // ── Screenshot ────────────────────────────────────────────────────────
1805
- if (name === "computer_screenshot") {
1806
- if (!cid)
1807
- return err("computer_id is required");
1808
- const png = await client.apiGetBinary(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/screenshot`);
1809
- return image(png);
1810
- }
1811
- // ── Pointer ───────────────────────────────────────────────────────────
1812
- if (name === "computer_click") {
1813
- if (!cid)
1814
- return err("computer_id is required");
1815
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/click`, {
1816
- x: args["x"],
1817
- y: args["y"],
1818
- button: args["button"] ?? "left",
1819
- });
1820
- return ok(`Clicked (${args["x"]}, ${args["y"]}) button=${args["button"] ?? "left"}`);
1821
- }
1822
- if (name === "computer_double_click") {
1823
- if (!cid)
1824
- return err("computer_id is required");
1825
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/double-click`, {
1826
- x: args["x"],
1827
- y: args["y"],
1828
- });
1829
- return ok(`Double-clicked (${args["x"]}, ${args["y"]})`);
1830
- }
1831
- if (name === "computer_move_cursor") {
1832
- if (!cid)
1833
- return err("computer_id is required");
1834
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/move`, {
1835
- x: args["x"],
1836
- y: args["y"],
1837
- });
1838
- return ok(`Moved cursor to (${args["x"]}, ${args["y"]})`);
1839
- }
1840
- if (name === "computer_drag") {
1841
- if (!cid)
1842
- return err("computer_id is required");
1843
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/drag`, {
1844
- from_x: args["from_x"],
1845
- from_y: args["from_y"],
1846
- to_x: args["to_x"],
1847
- to_y: args["to_y"],
1848
- });
1849
- return ok(`Dragged from (${args["from_x"]}, ${args["from_y"]}) to (${args["to_x"]}, ${args["to_y"]})`);
1850
- }
1851
- if (name === "computer_scroll") {
1852
- if (!cid)
1853
- return err("computer_id is required");
1854
- const body = {
1855
- direction: args["direction"] ?? "down",
1856
- clicks: args["clicks"] ?? 3,
1857
- };
1858
- if (args["x"] !== undefined)
1859
- body["x"] = args["x"];
1860
- if (args["y"] !== undefined)
1861
- body["y"] = args["y"];
1862
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/scroll`, body);
1863
- return ok(`Scrolled ${body["direction"]} by ${body["clicks"]} clicks`);
1864
- }
1865
- // ── Keyboard ──────────────────────────────────────────────────────────
1866
- if (name === "computer_type") {
1867
- if (!cid)
1868
- return err("computer_id is required");
1869
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/type`, {
1870
- text: args["text"],
1871
- });
1872
- const preview = typeof args["text"] === "string"
1873
- ? args["text"].slice(0, 40) + (args["text"].length > 40 ? "..." : "")
1874
- : "";
1875
- return ok(`Typed: ${JSON.stringify(preview)}`);
1876
- }
1877
- if (name === "computer_key") {
1878
- if (!cid)
1879
- return err("computer_id is required");
1880
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/key`, {
1881
- key: args["key"],
1882
- });
1883
- return ok(`Pressed key: ${args["key"]}`);
1884
- }
1885
- if (name === "computer_hotkey") {
1886
- if (!cid)
1887
- return err("computer_id is required");
1888
- const keys = args["keys"];
1889
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/hotkey`, {
1890
- keys,
1891
- });
1892
- return ok(`Pressed hotkey: ${keys.join("+")}`);
1893
- }
1894
- // ── Display info ──────────────────────────────────────────────────────
1895
- if (name === "computer_get_screen_size") {
1896
- if (!cid)
1897
- return err("computer_id is required");
1898
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/screen-size`);
1899
- const data = unwrapData(result);
1900
- return ok(`Screen size: ${data["width"]}x${data["height"]} px`);
1901
- }
1902
- if (name === "computer_get_cursor_position") {
1903
- if (!cid)
1904
- return err("computer_id is required");
1905
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/cursor`);
1906
- const data = unwrapData(result);
1907
- return ok(`Cursor position: x=${data["x"]}, y=${data["y"]}`);
1908
- }
1909
- // ── Clipboard ─────────────────────────────────────────────────────────
1910
- if (name === "computer_get_clipboard") {
1911
- if (!cid)
1912
- return err("computer_id is required");
1913
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/clipboard`);
1914
- const data = unwrapData(result);
1915
- return ok(`Clipboard content:\n${data["text"] ?? ""}`);
1916
- }
1917
- if (name === "computer_set_clipboard") {
1918
- if (!cid)
1919
- return err("computer_id is required");
1920
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/clipboard`, {
1921
- text: args["text"],
1922
- });
1923
- return ok("Clipboard updated.");
1924
- }
1925
- // ── Window management ─────────────────────────────────────────────────
1926
- if (name === "computer_windows") {
1927
- if (!cid)
1928
- return err("computer_id is required");
1929
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/windows`);
1930
- const windows = listOf(result);
1931
- if (windows.length === 0)
1932
- return ok("No open windows found.");
1933
- const lines = ["Open windows:"];
1934
- for (const w of windows) {
1935
- const r = w;
1936
- const focused = r["focused"] ? " [focused]" : "";
1937
- lines.push(` id=${r["id"]} title=${JSON.stringify(r["title"])} app=${JSON.stringify(r["app"])}` +
1938
- ` pos=(${r["x"]},${r["y"]}) size=${r["width"]}x${r["height"]}${focused}`);
1939
- }
1940
- return ok(lines.join("\n"));
1941
- }
1942
- if (name === "computer_launch") {
1943
- if (!cid)
1944
- return err("computer_id is required");
1945
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/launch`, {
1946
- app: args["app"],
1947
- });
1948
- return ok(`Launched: ${args["app"]}`);
1949
- }
1950
- // ── Shell & Files ─────────────────────────────────────────────────────
1951
- if (name === "computer_bash") {
1952
- if (!cid)
1953
- return err("computer_id is required");
1954
- const body = { command: args["command"] };
1955
- if (args["timeout"] !== undefined)
1956
- body["timeout"] = args["timeout"];
1957
- const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/exec`, body);
1958
- const data = unwrapData(result);
1959
- const parts = [];
1960
- if (data["output"] ?? data["stdout"]) {
1961
- parts.push(`stdout:\n${data["output"] ?? data["stdout"]}`);
1962
- }
1963
- if (data["stderr"])
1964
- parts.push(`stderr:\n${data["stderr"]}`);
1965
- parts.push(`exit_code: ${data["exit_code"] ?? 0}`);
1966
- return ok(parts.join("\n"));
1967
- }
1968
- if (name === "computer_write_file") {
1969
- if (!cid)
1970
- return err("computer_id is required");
1971
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/files/write`, {
1972
- path: args["path"],
1973
- content: args["content"],
1974
- encoding: "utf8",
1975
- });
1976
- const len = typeof args["content"] === "string" ? args["content"].length : 0;
1977
- return ok(`Wrote ${len} bytes to ${args["path"]}`);
1978
- }
1979
- if (name === "computer_read_file") {
1980
- if (!cid)
1981
- return err("computer_id is required");
1982
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/files/download?path=${encodeURIComponent(String(args["path"] ?? ""))}`);
1983
- const data = unwrapData(result);
1984
- const content = typeof data["content"] === "string"
1985
- ? Buffer.from(data["content"], "base64").toString("utf8")
1986
- : typeof data["text"] === "string"
1987
- ? data["text"]
1988
- : JSON.stringify(data);
1989
- return ok(content);
1990
- }
1991
- // ── Extended pointer ──────────────────────────────────────────────────
1992
- if (name === "computer_right_click") {
1993
- if (!cid)
1994
- return err("computer_id is required");
1995
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/click`, { x: args["x"], y: args["y"], button: "right" });
1996
- return ok(`Right-clicked (${args["x"]}, ${args["y"]})`);
1997
- }
1998
- if (name === "computer_mouse_down") {
1999
- if (!cid)
2000
- return err("computer_id is required");
2001
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/mouse-down`, {
2002
- x: args["x"],
2003
- y: args["y"],
2004
- button: args["button"] ?? "left",
2005
- });
2006
- return ok(`Mouse down at (${args["x"]}, ${args["y"]}) button=${args["button"] ?? "left"}`);
2007
- }
2008
- if (name === "computer_mouse_up") {
2009
- if (!cid)
2010
- return err("computer_id is required");
2011
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/mouse-up`, {
2012
- x: args["x"],
2013
- y: args["y"],
2014
- button: args["button"] ?? "left",
2015
- });
2016
- return ok(`Mouse up at (${args["x"]}, ${args["y"]}) button=${args["button"] ?? "left"}`);
2017
- }
2018
- // ── Extended keyboard ─────────────────────────────────────────────────
2019
- if (name === "computer_key_down") {
2020
- if (!cid)
2021
- return err("computer_id is required");
2022
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/key-down`, { key: args["key"] });
2023
- return ok(`Key down: ${args["key"]}`);
2024
- }
2025
- if (name === "computer_key_up") {
2026
- if (!cid)
2027
- return err("computer_id is required");
2028
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/key-up`, { key: args["key"] });
2029
- return ok(`Key up: ${args["key"]}`);
2030
- }
2031
- // ── Wait ──────────────────────────────────────────────────────────────
2032
- if (name === "computer_wait") {
2033
- if (!cid)
2034
- return err("computer_id is required");
2035
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/wait`, { seconds: args["seconds"] });
2036
- return ok(`Waited ${args["seconds"]}s`);
2037
- }
2038
- // ── Window management (extended) ──────────────────────────────────────
2039
- if (name === "computer_focus_window") {
2040
- if (!cid)
2041
- return err("computer_id is required");
2042
- const wid = String(args["window_id"] ?? "");
2043
- if (!wid)
2044
- return err("window_id is required");
2045
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/window/focus`, { window_id: wid });
2046
- return ok(`Focused window ${wid}`);
2047
- }
2048
- if (name === "computer_set_window_size") {
2049
- if (!cid)
2050
- return err("computer_id is required");
2051
- const wid = String(args["window_id"] ?? "");
2052
- if (!wid)
2053
- return err("window_id is required");
2054
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/window/${encodeURIComponent(wid)}/resize`, { width: args["width"], height: args["height"] });
2055
- return ok(`Resized window ${wid} to ${args["width"]}x${args["height"]}`);
2056
- }
2057
- if (name === "computer_set_window_position") {
2058
- if (!cid)
2059
- return err("computer_id is required");
2060
- const wid = String(args["window_id"] ?? "");
2061
- if (!wid)
2062
- return err("window_id is required");
2063
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/window/${encodeURIComponent(wid)}/move`, { x: args["x"], y: args["y"] });
2064
- return ok(`Moved window ${wid} to (${args["x"]}, ${args["y"]})`);
2065
- }
2066
- if (name === "computer_maximize_window") {
2067
- if (!cid)
2068
- return err("computer_id is required");
2069
- const wid = String(args["window_id"] ?? "");
2070
- if (!wid)
2071
- return err("window_id is required");
2072
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/window/${encodeURIComponent(wid)}/maximize`, {});
2073
- return ok(`Maximized window ${wid}`);
2074
- }
2075
- if (name === "computer_minimize_window") {
2076
- if (!cid)
2077
- return err("computer_id is required");
2078
- const wid = String(args["window_id"] ?? "");
2079
- if (!wid)
2080
- return err("window_id is required");
2081
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/window/${encodeURIComponent(wid)}/minimize`, {});
2082
- return ok(`Minimized window ${wid}`);
2083
- }
2084
- if (name === "computer_close_window") {
2085
- if (!cid)
2086
- return err("computer_id is required");
2087
- const wid = String(args["window_id"] ?? "");
2088
- if (!wid)
2089
- return err("window_id is required");
2090
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/window/${encodeURIComponent(wid)}/close`, {});
2091
- return ok(`Closed window ${wid}`);
2092
- }
2093
- // ── Desktop environment ───────────────────────────────────────────────
2094
- if (name === "computer_get_desktop_env") {
2095
- if (!cid)
2096
- return err("computer_id is required");
2097
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/environment`);
2098
- const data = unwrapData(result);
2099
- return ok(`desktop_env: name=${JSON.stringify(data["name"])} resolution=${data["resolution"]} session_type=${JSON.stringify(data["session_type"])}`);
2100
- }
2101
- if (name === "computer_set_wallpaper") {
2102
- if (!cid)
2103
- return err("computer_id is required");
2104
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/wallpaper`, { path: args["path"] });
2105
- return ok(`Wallpaper set to: ${args["path"]}`);
2106
- }
2107
- if (name === "computer_accessibility_tree") {
2108
- if (!cid)
2109
- return err("computer_id is required");
2110
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/desktop/accessibility-tree`);
2111
- const tree = unwrapData(result);
2112
- const json = JSON.stringify(tree, null, 2);
2113
- return ok(json.length > 8000 ? json.slice(0, 8000) + "\n…(truncated)" : json);
2114
- }
2115
- // ── Files — extended ──────────────────────────────────────────────────
2116
- if (name === "computer_list_files") {
2117
- if (!cid)
2118
- return err("computer_id is required");
2119
- const path = typeof args["path"] === "string" ? args["path"] : undefined;
2120
- const qs = path ? `?path=${encodeURIComponent(path)}` : "";
2121
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/files${qs}`);
2122
- const items = listOf(result);
2123
- if (items.length === 0)
2124
- return ok(`No files found at ${path ?? "/"}.`);
2125
- const lines = [`Files at ${path ?? "/"}:`];
2126
- for (const e of items) {
2127
- const r = e;
2128
- lines.push(` ${r["name"]} ${r["type"] ?? r["kind"] ?? ""} ${r["size"] ?? ""}`);
2129
- }
2130
- return ok(lines.join("\n"));
2131
- }
2132
- if (name === "computer_stat_file") {
2133
- if (!cid)
2134
- return err("computer_id is required");
2135
- const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/files/stat`, { path: args["path"] });
2136
- const data = unwrapData(result);
2137
- const lines = [
2138
- `path: ${args["path"]}`,
2139
- `type: ${data["type"] ?? data["kind"] ?? ""}`,
2140
- `size: ${data["size"] ?? ""}`,
2141
- `mode: ${data["mode"] ?? ""}`,
2142
- `mtime: ${data["mtime"] ?? data["modified_at"] ?? ""}`,
2143
- ];
2144
- return ok(lines.join("\n"));
2145
- }
2146
- if (name === "computer_mkdir") {
2147
- if (!cid)
2148
- return err("computer_id is required");
2149
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/files/mkdir`, {
2150
- path: args["path"],
2151
- recursive: args["recursive"] !== false,
2152
- mode: "0755",
2153
- });
2154
- return ok(`Created directory ${args["path"]}`);
2155
- }
2156
- if (name === "computer_rename_file") {
2157
- if (!cid)
2158
- return err("computer_id is required");
2159
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/files/rename`, { from: args["source"], to: args["dest"] });
2160
- return ok(`Renamed ${args["source"]} -> ${args["dest"]}`);
2161
- }
2162
- if (name === "computer_copy_file") {
2163
- if (!cid)
2164
- return err("computer_id is required");
2165
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/files/copy`, {
2166
- from: args["source"],
2167
- to: args["dest"],
2168
- recursive: args["recursive"] === true,
2169
- });
2170
- return ok(`Copied ${args["source"]} -> ${args["dest"]}`);
2171
- }
2172
- if (name === "computer_delete_file") {
2173
- if (!cid)
2174
- return err("computer_id is required");
2175
- await client.apiDelete(`/api/v1/computers/${encodeURIComponent(cid)}/files?path=${encodeURIComponent(String(args["path"] ?? ""))}`);
2176
- return ok(`Deleted ${args["path"]}`);
2177
- }
2178
- if (name === "computer_upload_file") {
2179
- if (!cid)
2180
- return err("computer_id is required");
2181
- // Read the local file and POST it as base64 via the write endpoint
2182
- const fs = await import("node:fs/promises");
2183
- const localBytes = await fs.readFile(String(args["local_path"]));
2184
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/files/write`, {
2185
- path: args["remote_path"],
2186
- content: localBytes.toString("base64"),
2187
- encoding: "base64",
2188
- });
2189
- return ok(`Uploaded ${args["local_path"]} -> ${args["remote_path"]}`);
2190
- }
2191
- // ── Checkpoints ───────────────────────────────────────────────────────
2192
- if (name === "computer_checkpoint_create") {
2193
- if (!cid)
2194
- return err("computer_id is required");
2195
- const body = {};
2196
- if (args["comment"])
2197
- body["comment"] = args["comment"];
2198
- const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/snapshots`, body);
2199
- const data = unwrapData(result);
2200
- const snap_id = String(data["id"] ?? "");
2201
- const snap_status = String(data["status"] ?? "");
2202
- const snap_comment = data["comment"]
2203
- ? ` comment=${JSON.stringify(data["comment"])}`
2204
- : "";
2205
- return ok(`Checkpoint created: id=${snap_id} status=${snap_status}${snap_comment}`);
2206
- }
2207
- if (name === "computer_checkpoint_list") {
2208
- if (!cid)
2209
- return err("computer_id is required");
2210
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/snapshots`);
2211
- const items = listOf(result);
2212
- if (items.length === 0)
2213
- return ok("No checkpoints found.");
2214
- const lines = ["Checkpoints:"];
2215
- for (const s of items) {
2216
- const r = s;
2217
- const c = r["comment"] ? ` ${JSON.stringify(r["comment"])}` : "";
2218
- lines.push(` ${r["id"]} ${r["status"]} ${r["created_at"] ?? ""}${c}`);
2219
- }
2220
- return ok(lines.join("\n"));
2221
- }
2222
- if (name === "computer_checkpoint_restore") {
2223
- if (!cid)
2224
- return err("computer_id is required");
2225
- const snapId = String(args["checkpoint_id"] ?? "");
2226
- if (!snapId)
2227
- return err("checkpoint_id is required");
2228
- const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/restore/${encodeURIComponent(snapId)}`, {});
2229
- const raw = unwrapData(result);
2230
- const compData = raw["computer"] ?? raw;
2231
- const newId = String(compData["id"] ?? "");
2232
- const newStatus = String(compData["status"] ?? compData["state"] ?? "");
2233
- return ok(`Restored checkpoint ${snapId} -> new computer id=${newId} status=${newStatus}.`);
2234
- }
2235
- if (name === "computer_checkpoint_delete") {
2236
- if (!cid)
2237
- return err("computer_id is required");
2238
- const snapId = String(args["checkpoint_id"] ?? "");
2239
- if (!snapId)
2240
- return err("checkpoint_id is required");
2241
- const result = await client.apiDelete(`/api/v1/computers/${encodeURIComponent(cid)}/snapshots/${encodeURIComponent(snapId)}`);
2242
- const data = unwrapData(result);
2243
- return ok(`Checkpoint ${data["id"] ?? snapId} deleted (status=${data["status"] ?? "deleted"}).`);
2244
- }
2245
- // ── Services ──────────────────────────────────────────────────────────
2246
- if (name === "computer_service_create") {
2247
- if (!cid)
2248
- return err("computer_id is required");
2249
- const body = {
2250
- name: args["name"],
2251
- command: args["command"],
2252
- };
2253
- if (args["working_dir"])
2254
- body["working_dir"] = args["working_dir"];
2255
- if (args["port"] !== undefined)
2256
- body["port"] = args["port"];
2257
- if (args["restart_policy"])
2258
- body["restart_policy"] = args["restart_policy"];
2259
- const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/services`, body);
2260
- const data = unwrapData(result);
2261
- return ok(`Service created: id=${data["id"]} name=${JSON.stringify(data["name"])} status=${data["status"] ?? data["state"] ?? ""}`);
2262
- }
2263
- if (name === "computer_service_list") {
2264
- if (!cid)
2265
- return err("computer_id is required");
2266
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/services`);
2267
- const items = listOf(result);
2268
- if (items.length === 0)
2269
- return ok("No services found.");
2270
- const lines = ["Services:"];
2271
- for (const s of items) {
2272
- const r = s;
2273
- lines.push(` ${r["id"]} ${JSON.stringify(r["name"])} ${r["status"] ?? r["state"] ?? ""}`);
2274
- }
2275
- return ok(lines.join("\n"));
2276
- }
2277
- if (name === "computer_service_start") {
2278
- if (!cid)
2279
- return err("computer_id is required");
2280
- const sid = String(args["service_id"] ?? "");
2281
- if (!sid)
2282
- return err("service_id is required");
2283
- const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/services/${encodeURIComponent(sid)}/start`, {});
2284
- const data = unwrapData(result);
2285
- return ok(`Service ${data["id"] ?? sid} started (status=${data["status"] ?? ""}).`);
2286
- }
2287
- if (name === "computer_service_stop") {
2288
- if (!cid)
2289
- return err("computer_id is required");
2290
- const sid = String(args["service_id"] ?? "");
2291
- if (!sid)
2292
- return err("service_id is required");
2293
- const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/services/${encodeURIComponent(sid)}/stop`, {});
2294
- const data = unwrapData(result);
2295
- return ok(`Service ${data["id"] ?? sid} stopped (status=${data["status"] ?? ""}).`);
2296
- }
2297
- if (name === "computer_service_restart") {
2298
- if (!cid)
2299
- return err("computer_id is required");
2300
- const sid = String(args["service_id"] ?? "");
2301
- if (!sid)
2302
- return err("service_id is required");
2303
- const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/services/${encodeURIComponent(sid)}/restart`, {});
2304
- const data = unwrapData(result);
2305
- return ok(`Service ${data["id"] ?? sid} restarted (status=${data["status"] ?? ""}).`);
2306
- }
2307
- if (name === "computer_service_logs") {
2308
- if (!cid)
2309
- return err("computer_id is required");
2310
- const sid = String(args["service_id"] ?? "");
2311
- if (!sid)
2312
- return err("service_id is required");
2313
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/services/${encodeURIComponent(sid)}/logs?follow=false`);
2314
- const data = unwrapData(result);
2315
- const logLines = data["lines"] ?? data["logs"] ?? data["data"];
2316
- if (Array.isArray(logLines) && logLines.length > 0) {
2317
- return ok(logLines.map((l) => String(l)).join("\n"));
2318
- }
2319
- if (typeof logLines === "string")
2320
- return ok(logLines);
2321
- return ok("No log output.");
2322
- }
2323
- if (name === "computer_service_delete") {
2324
- if (!cid)
2325
- return err("computer_id is required");
2326
- const sid = String(args["service_id"] ?? "");
2327
- if (!sid)
2328
- return err("service_id is required");
2329
- await client.apiDelete(`/api/v1/computers/${encodeURIComponent(cid)}/services/${encodeURIComponent(sid)}`);
2330
- return ok(`Service ${sid} deleted.`);
2331
- }
2332
- // ── Env vars ──────────────────────────────────────────────────────────
2333
- if (name === "computer_env_list") {
2334
- if (!cid)
2335
- return err("computer_id is required");
2336
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/env`);
2337
- const items = Array.isArray(result)
2338
- ? result
2339
- : (() => {
2340
- const d = unwrapData(result);
2341
- return ((Array.isArray(d["env"]) ? d["env"] : null) ??
2342
- (Array.isArray(d["items"]) ? d["items"] : null) ??
2343
- (Array.isArray(d["data"]) ? d["data"] : null) ??
2344
- []);
2345
- })();
2346
- if (items.length === 0)
2347
- return ok("No environment variables set.");
2348
- const lines = ["Environment variables:"];
2349
- for (const e of items) {
2350
- const r = e;
2351
- const k = String(r["name"] ?? r["key"] ?? "");
2352
- const v = String(r["value"] ?? "");
2353
- lines.push(` ${k}=${v}`);
2354
- }
2355
- return ok(lines.join("\n"));
2356
- }
2357
- if (name === "computer_env_set") {
2358
- if (!cid)
2359
- return err("computer_id is required");
2360
- await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/env`, {
2361
- name: args["name"],
2362
- value: args["value"],
2363
- });
2364
- return ok(`Set env var ${args["name"]}.`);
2365
- }
2366
- if (name === "computer_env_delete") {
2367
- if (!cid)
2368
- return err("computer_id is required");
2369
- const varName = String(args["name"] ?? "");
2370
- if (!varName)
2371
- return err("name is required");
2372
- await client.apiDelete(`/api/v1/computers/${encodeURIComponent(cid)}/env/${encodeURIComponent(varName)}`);
2373
- return ok(`Deleted env var ${varName}.`);
2374
- }
2375
- // ── Logs ──────────────────────────────────────────────────────────────
2376
- if (name === "computer_logs") {
2377
- if (!cid)
2378
- return err("computer_id is required");
2379
- const qs = args["lines"] !== undefined
2380
- ? `?lines=${encodeURIComponent(String(args["lines"]))}`
2381
- : "";
2382
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/logs${qs}`);
2383
- const data = unwrapData(result);
2384
- const logLines = data["lines"] ?? data["logs"] ?? data["data"];
2385
- if (Array.isArray(logLines) && logLines.length > 0) {
2386
- return ok(logLines.map((l) => String(l)).join("\n"));
2387
- }
2388
- if (typeof logLines === "string")
2389
- return ok(logLines);
2390
- if (typeof result === "string")
2391
- return ok(result);
2392
- return ok("No logs.");
2393
- }
2394
- // ── Domains ───────────────────────────────────────────────────────────
2395
- if (name === "computer_domain_add") {
2396
- if (!cid)
2397
- return err("computer_id is required");
2398
- const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/domains`, { fqdn: args["fqdn"] });
2399
- const data = unwrapData(result);
2400
- const lines = [
2401
- `Domain registered: id=${data["id"]} fqdn=${data["fqdn"]} status=${data["status"] ?? ""}`,
2402
- ];
2403
- if (data["verification_target"])
2404
- lines.push(` CNAME target: ${data["verification_target"]}`);
2405
- if (data["instructions"])
2406
- lines.push(` Instructions: ${data["instructions"]}`);
2407
- return ok(lines.join("\n"));
2408
- }
2409
- if (name === "computer_domain_list") {
2410
- if (!cid)
2411
- return err("computer_id is required");
2412
- const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/domains`);
2413
- const items = listOf(result);
2414
- if (items.length === 0)
2415
- return ok("No custom domains registered.");
2416
- const lines = ["Custom domains:"];
2417
- for (const d of items) {
2418
- const r = d;
2419
- lines.push(` ${r["id"]} ${r["fqdn"]} ${r["status"] ?? ""}`);
2420
- }
2421
- return ok(lines.join("\n"));
2422
- }
2423
- if (name === "computer_domain_delete") {
2424
- if (!cid)
2425
- return err("computer_id is required");
2426
- const domainId = String(args["domain_id"] ?? "");
2427
- if (!domainId)
2428
- return err("domain_id is required");
2429
- await client.apiDelete(`/api/v1/computers/${encodeURIComponent(cid)}/domains/${encodeURIComponent(domainId)}`);
2430
- return ok(`Domain ${domainId} deleted.`);
2431
- }
2432
- // ── Sandboxes ─────────────────────────────────────────────────────────
2433
- if (name === "sandbox_create") {
2434
- const rawResourceKeys = ["cpu_count", "memory_mb", "disk_size_mb"].filter((key) => args[key] !== undefined);
2435
- if (rawResourceKeys.length > 0) {
2436
- return err(`sandbox_create accepts named size only; remove ${rawResourceKeys.join(", ")} and use size=xs|small|medium|large|xl`);
2437
- }
2438
- const body = {};
2439
- if (args["name"])
2440
- body["name"] = args["name"];
2441
- if (args["template_id"])
2442
- body["template_id"] = args["template_id"];
2443
- body["size"] = resolveSandboxSize(args["size"]);
2444
- if (args["timeout_sec"] !== undefined)
2445
- body["timeout_sec"] = args["timeout_sec"];
2446
- // Auto-generate an Idempotency-Key (or honor a caller-supplied one) and
2447
- // retry on 429/5xx with the SAME key, so a retried create dedups instead
2448
- // of billing a duplicate sandbox.
2449
- const idempotencyKey = (args["idempotency_key"] ? String(args["idempotency_key"]) : "") ||
2450
- randomUUID();
2451
- const result = await client.apiPostWithRetry("/api/v1/sandboxes", body, { "Idempotency-Key": idempotencyKey });
2452
- const data = unwrapData(result);
2453
- const sid = String(data["id"] ?? "");
2454
- return ok(`Created sandbox '${data["name"] ?? sid}' (id=${sid}).`);
2455
- }
2456
- if (name === "sandbox_exec") {
2457
- const sid = String(args["sandbox_id"] ?? "");
2458
- if (!sid)
2459
- return err("sandbox_id is required");
2460
- const cwd = args["cwd"] ? String(args["cwd"]) : undefined;
2461
- const body = {
2462
- command: commandInCwd(String(args["command"] ?? ""), cwd),
2463
- };
2464
- if (cwd) {
2465
- body["cwd"] = cwd;
2466
- body["dir"] = cwd;
2467
- }
2468
- if (args["timeout"] !== undefined)
2469
- body["timeout"] = args["timeout"];
2470
- const result = await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/exec`, body);
2471
- const data = unwrapData(result);
2472
- const parts = [];
2473
- if (data["output"] ?? data["stdout"]) {
2474
- parts.push(`stdout:\n${data["output"] ?? data["stdout"]}`);
2475
- }
2476
- if (data["stderr"])
2477
- parts.push(`stderr:\n${data["stderr"]}`);
2478
- parts.push(`exit_code: ${data["exit_code"] ?? 0}`);
2479
- return ok(parts.join("\n"));
2480
- }
2481
- if (name === "sandbox_destroy") {
2482
- const sid = String(args["sandbox_id"] ?? "");
2483
- if (!sid)
2484
- return err("sandbox_id is required");
2485
- await client.apiDelete(`/api/v1/sandboxes/${encodeURIComponent(sid)}`);
2486
- return ok(`Sandbox ${sid} destroyed.`);
2487
- }
2488
- // ── Sandbox list/get/pause/resume ─────────────────────────────────────
2489
- if (name === "sandbox_list") {
2490
- const params = new URLSearchParams();
2491
- if (args["state"])
2492
- params.set("state", String(args["state"]));
2493
- const qs = params.toString() ? `?${params.toString()}` : "";
2494
- const result = await client.apiGet(`/api/v1/sandboxes${qs}`);
2495
- const items = listOf(result);
2496
- if (items.length === 0)
2497
- return ok("No sandboxes found.");
2498
- const lines = ["Sandboxes:"];
2499
- for (const s of items) {
2500
- const r = s;
2501
- lines.push(` ${r["id"]} ${r["name"] ?? ""} ${r["state"] ?? r["status"] ?? ""} template=${r["template_id"] ?? ""}`);
2502
- }
2503
- return ok(lines.join("\n"));
2504
- }
2505
- if (name === "sandbox_get") {
2506
- const sid = String(args["sandbox_id"] ?? "");
2507
- if (!sid)
2508
- return err("sandbox_id is required");
2509
- const result = await client.apiGet(`/api/v1/sandboxes/${encodeURIComponent(sid)}`);
2510
- const data = unwrapData(result);
2511
- const lines = [
2512
- `id: ${data["id"]}`,
2513
- `state: ${data["state"] ?? data["status"] ?? ""}`,
2514
- `template_id: ${data["template_id"] ?? ""}`,
2515
- `ready: ${data["ready"] ?? ""}`,
2516
- ];
2517
- if (data["name"])
2518
- lines.splice(1, 0, `name: ${data["name"]}`);
2519
- if (data["preview_url"])
2520
- lines.push(`preview_url: ${data["preview_url"]}`);
2521
- if (data["boot_ms"] !== undefined)
2522
- lines.push(`boot_ms: ${data["boot_ms"]}`);
2523
- return ok(lines.join("\n"));
2524
- }
2525
- if (name === "sandbox_pause") {
2526
- const sid = String(args["sandbox_id"] ?? "");
2527
- if (!sid)
2528
- return err("sandbox_id is required");
2529
- await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/pause`, {});
2530
- return ok(`Sandbox ${sid} paused.`);
2531
- }
2532
- if (name === "sandbox_resume") {
2533
- const sid = String(args["sandbox_id"] ?? "");
2534
- if (!sid)
2535
- return err("sandbox_id is required");
2536
- await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/resume`, {});
2537
- return ok(`Sandbox ${sid} resumed.`);
2538
- }
2539
- // ── Sandbox files ─────────────────────────────────────────────────────
2540
- if (name === "sandbox_write_file") {
2541
- const sid = String(args["sandbox_id"] ?? "");
2542
- if (!sid)
2543
- return err("sandbox_id is required");
2544
- await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/files`, {
2545
- path: args["path"],
2546
- content: Buffer.from(typeof args["content"] === "string" ? args["content"] : "", "utf8").toString("base64"),
2547
- });
2548
- const len = typeof args["content"] === "string" ? args["content"].length : 0;
2549
- return ok(`Wrote ${len} bytes to ${args["path"]}`);
2550
- }
2551
- if (name === "sandbox_read_file") {
2552
- const sid = String(args["sandbox_id"] ?? "");
2553
- if (!sid)
2554
- return err("sandbox_id is required");
2555
- const path = String(args["path"] ?? "").replace(/^\//, "");
2556
- const result = await client.apiGet(`/api/v1/sandboxes/${encodeURIComponent(sid)}/files/${encodeURIComponent(path)}`);
2557
- const data = unwrapData(result);
2558
- if (typeof data["content"] === "string") {
2559
- try {
2560
- return ok(Buffer.from(data["content"], "base64").toString("utf8"));
2561
- }
2562
- catch {
2563
- return ok(data["content"]);
2564
- }
2565
- }
2566
- if (typeof data["text"] === "string")
2567
- return ok(data["text"]);
2568
- return ok(JSON.stringify(data));
2569
- }
2570
- if (name === "sandbox_list_files") {
2571
- const sid = String(args["sandbox_id"] ?? "");
2572
- if (!sid)
2573
- return err("sandbox_id is required");
2574
- const path = String(args["path"] ?? "/workspace");
2575
- const params = new URLSearchParams({ path });
2576
- if (args["depth"] !== undefined)
2577
- params.set("depth", String(args["depth"]));
2578
- const result = await client.apiGet(`/api/v1/sandboxes/${encodeURIComponent(sid)}/files?${params.toString()}`);
2579
- const data = unwrapData(result);
2580
- return ok(JSON.stringify(data, null, 2));
2581
- }
2582
- if (name === "sandbox_upload") {
2583
- const sid = String(args["sandbox_id"] ?? "");
2584
- if (!sid)
2585
- return err("sandbox_id is required");
2586
- await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/files`, {
2587
- path: args["path"],
2588
- content: Buffer.from(typeof args["content"] === "string" ? args["content"] : "", "utf8").toString("base64"),
2589
- });
2590
- return ok(`Uploaded ${args["path"]} to sandbox ${sid}.`);
2591
- }
2592
- // ── Sandbox python ────────────────────────────────────────────────────
2593
- if (name === "sandbox_python") {
2594
- const sid = String(args["sandbox_id"] ?? "");
2595
- if (!sid)
2596
- return err("sandbox_id is required");
2597
- const code = String(args["code"] ?? "");
2598
- const tmpPath = "/tmp/_mcp_run.py";
2599
- // Write code file
2600
- await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/files`, {
2601
- path: tmpPath,
2602
- content: Buffer.from(code, "utf8").toString("base64"),
2603
- });
2604
- // Execute it
2605
- const execBody = {
2606
- command: `python3 ${tmpPath}`,
2607
- };
2608
- if (args["timeout"] !== undefined)
2609
- execBody["timeout"] = args["timeout"];
2610
- const result = await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/exec`, execBody);
2611
- const data = unwrapData(result);
2612
- const parts = [];
2613
- if (data["stdout"] ?? data["output"])
2614
- parts.push(`stdout:\n${data["stdout"] ?? data["output"]}`);
2615
- if (data["stderr"])
2616
- parts.push(`stderr:\n${data["stderr"]}`);
2617
- parts.push(`exit_code: ${data["exit_code"] ?? 0}`);
2618
- return ok(parts.join("\n"));
2619
- }
2620
- // ── Sandbox snapshots ─────────────────────────────────────────────────
2621
- if (name === "sandbox_snapshot_create") {
2622
- const sid = String(args["sandbox_id"] ?? "");
2623
- if (!sid)
2624
- return err("sandbox_id is required");
2625
- const body = {};
2626
- if (args["comment"])
2627
- body["comment"] = args["comment"];
2628
- const result = await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/snapshots`, body);
2629
- const data = unwrapData(result);
2630
- const snapId = data["id"] ?? data["snapshot_id"] ?? "unknown";
2631
- return ok(`Snapshot created: id=${snapId}`);
2632
- }
2633
- if (name === "sandbox_snapshot_list") {
2634
- const sid = String(args["sandbox_id"] ?? "");
2635
- if (!sid)
2636
- return err("sandbox_id is required");
2637
- const result = await client.apiGet(`/api/v1/sandboxes/${encodeURIComponent(sid)}/snapshots`);
2638
- const items = listOf(result);
2639
- if (items.length === 0)
2640
- return ok("No snapshots found.");
2641
- const lines = ["Snapshots:"];
2642
- for (const s of items) {
2643
- const r = s;
2644
- lines.push(` ${r["id"]} ${r["created_at"] ?? ""} ${r["comment"] ?? ""}`);
2645
- }
2646
- return ok(lines.join("\n"));
2647
- }
2648
- if (name === "sandbox_snapshot_restore") {
2649
- const sid = String(args["sandbox_id"] ?? "");
2650
- if (!sid)
2651
- return err("sandbox_id is required");
2652
- const snapId = String(args["snapshot_id"] ?? "");
2653
- if (!snapId)
2654
- return err("snapshot_id is required");
2655
- const result = await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/restore/${encodeURIComponent(snapId)}`, {});
2656
- const data = unwrapData(result);
2657
- const state = data["state"] ?? data["status"] ?? "unknown";
2658
- return ok(`Sandbox ${sid} restored from snapshot ${snapId} (state=${state}).`);
2659
- }
2660
- // ── Sandbox logs ──────────────────────────────────────────────────────
2661
- if (name === "sandbox_logs") {
2662
- const sid = String(args["sandbox_id"] ?? "");
2663
- if (!sid)
2664
- return err("sandbox_id is required");
2665
- const params = new URLSearchParams();
2666
- if (args["lines"] !== undefined)
2667
- params.set("lines", String(args["lines"]));
2668
- const qs = params.toString() ? `?${params.toString()}` : "";
2669
- const result = await client.apiGet(`/api/v1/sandboxes/${encodeURIComponent(sid)}/logs${qs}`);
2670
- const data = unwrapData(result);
2671
- if (Array.isArray(data)) {
2672
- return ok(data.length > 0 ? data.join("\n") : "No logs.");
2673
- }
2674
- return ok(JSON.stringify(data, null, 2));
2675
- }
2676
- // ── Sandbox expose ────────────────────────────────────────────────────
2677
- if (name === "sandbox_expose") {
2678
- const sid = String(args["sandbox_id"] ?? "");
2679
- if (!sid)
2680
- return err("sandbox_id is required");
2681
- const body = {};
2682
- if (args["port"] !== undefined)
2683
- body["port"] = args["port"];
2684
- const result = await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/expose`, body);
2685
- const data = unwrapData(result);
2686
- const url = data["url"] ?? data["preview_url"] ?? "";
2687
- return ok(`Preview URL: ${url}`);
2688
- }
2689
- // ── Sandbox deploy ────────────────────────────────────────────────────
2690
- if (name === "sandbox_deploy") {
2691
- const sid = String(args["sandbox_id"] ?? "");
2692
- if (!sid)
2693
- return err("sandbox_id is required");
2694
- const body = {};
2695
- if (args["name"])
2696
- body["name"] = args["name"];
2697
- if (args["output_path"])
2698
- body["output_path"] = args["output_path"];
2699
- if (args["entrypoint"])
2700
- body["entrypoint"] = args["entrypoint"];
2701
- if (args["domain"])
2702
- body["domain"] = args["domain"];
2703
- const result = await client.apiPost(`/api/v1/sandboxes/${encodeURIComponent(sid)}/deploy`, body);
2704
- const data = unwrapData(result);
2705
- const deployId = data["id"] ?? data["deployment_id"] ?? "unknown";
2706
- const url = data["url"] ?? data["preview_url"] ?? "";
2707
- return ok(`Deployed sandbox ${sid} (deployment id=${deployId}, url=${url}).`);
2708
- }
2709
- // ── Sandbox templates ─────────────────────────────────────────────────
2710
- if (name === "sandbox_template_list") {
2711
- const result = await client.apiGet("/api/v1/sandbox-templates");
2712
- const data = unwrapData(result);
2713
- const items = Array.isArray(data)
2714
- ? data
2715
- : (() => {
2716
- const r = data;
2717
- for (const key of ["templates", "data", "items"]) {
2718
- if (Array.isArray(r[key]))
2719
- return r[key];
2720
- }
2721
- return Object.values(r);
2722
- })();
2723
- if (items.length === 0)
2724
- return ok("No templates found.");
2725
- const lines = ["Templates:"];
2726
- for (const t of items) {
2727
- const r = t;
2728
- lines.push(` ${r["id"] ?? r["slug"]} ${r["name"]} ${r["description"] ?? ""}`);
2729
- }
2730
- return ok(lines.join("\n"));
2731
- }
2732
- if (name === "sandbox_template_create") {
2733
- const body = {
2734
- name: args["name"],
2735
- build_spec: args["build_spec"],
2736
- };
2737
- if (args["slug"])
2738
- body["slug"] = args["slug"];
2739
- if (args["description"])
2740
- body["description"] = args["description"];
2741
- const result = await client.apiPost("/api/v1/sandbox-templates", body);
2742
- const data = unwrapData(result);
2743
- return ok(`Created template '${data["name"] ?? args["name"]}' (id=${data["id"]}).`);
2744
- }
2745
- // ── Deploy ────────────────────────────────────────────────────────────
2746
- if (name === "deploy") {
2747
- const did = String(args["deployment_id"] ?? "");
2748
- if (!did)
2749
- return err("deployment_id is required");
2750
- const result = await client.apiPost(`/api/v1/deployments/${encodeURIComponent(did)}/redeploy`, {});
2751
- const data = unwrapData(result);
2752
- return ok(`Redeploy queued (build id: ${data["id"] ?? "unknown"})`);
2753
- }
2754
- // ── Deployments ──────────────────────────────────────────────────────
2755
- if (name === "deployment_list") {
2756
- const result = await client.apiGet("/api/v1/deployments");
2757
- const items = listOf(result);
2758
- if (items.length === 0)
2759
- return ok("No deployments found.");
2760
- const lines = ["Deployments:"];
2761
- for (const d of items) {
2762
- const r = d;
2763
- lines.push(" " + r["id"] + " " + r["name"] + " " + (r["status"] ?? r["state"] ?? ""));
2764
- }
2765
- return ok(lines.join("\n"));
2766
- }
2767
- if (name === "deployment_get") {
2768
- const did = String(args["deployment_id"] ?? "");
2769
- if (!did)
2770
- return err("deployment_id is required");
2771
- const result = await client.apiGet("/api/v1/deployments/" + encodeURIComponent(did));
2772
- return ok(JSON.stringify(unwrapData(result), null, 2));
2773
- }
2774
- if (name === "deployment_create") {
2775
- const body = { name: args["name"] };
2776
- if (args["type"])
2777
- body["type"] = args["type"];
2778
- if (args["source"])
2779
- body["source"] = args["source"];
2780
- if (args["env_vars"])
2781
- body["env_vars"] = args["env_vars"];
2782
- if (args["region"])
2783
- body["region"] = args["region"];
2784
- const result = await client.apiPost("/api/v1/deployments", body);
2785
- const data = unwrapData(result);
2786
- return ok("Created deployment '" + String(data["name"] ?? args["name"]) + "' (id=" + data["id"] + ")");
2787
- }
2788
- if (name === "deployment_delete") {
2789
- const did = String(args["deployment_id"] ?? "");
2790
- if (!did)
2791
- return err("deployment_id is required");
2792
- await client.apiDelete("/api/v1/deployments/" + encodeURIComponent(did));
2793
- return ok("Deployment " + did + " deleted.");
2794
- }
2795
- if (name === "deployment_publish") {
2796
- const did = String(args["deployment_id"] ?? "");
2797
- if (!did)
2798
- return err("deployment_id is required");
2799
- const body = {};
2800
- if (args["source"])
2801
- body["source"] = args["source"];
2802
- const result = await client.apiPost("/api/v1/deployments/" + encodeURIComponent(did) + "/publish", body);
2803
- const data = unwrapData(result);
2804
- return ok("Published deployment " + did + " (version id=" + (data["id"] ?? "unknown") + ")");
2805
- }
2806
- if (name === "deployment_rollback") {
2807
- const did = String(args["deployment_id"] ?? "");
2808
- const vid = String(args["version_id"] ?? "");
2809
- if (!did)
2810
- return err("deployment_id is required");
2811
- if (!vid)
2812
- return err("version_id is required");
2813
- await client.apiPost("/api/v1/deployments/" + encodeURIComponent(did) + "/rollback", { version_id: vid });
2814
- return ok("Deployment " + did + " rolled back to version " + vid + ".");
2815
- }
2816
- if (name === "deployment_env_list") {
2817
- const did = String(args["deployment_id"] ?? "");
2818
- if (!did)
2819
- return err("deployment_id is required");
2820
- const result = await client.apiGet("/api/v1/deployments/" + encodeURIComponent(did) + "/env");
2821
- const envVars = unwrapData(result);
2822
- if (!envVars || (Array.isArray(envVars) && envVars.length === 0))
2823
- return ok("No environment variables set.");
2824
- const lines = ["Environment variables:"];
2825
- if (typeof envVars === "object" && !Array.isArray(envVars)) {
2826
- for (const [k, v] of Object.entries(envVars)) {
2827
- lines.push(" " + k + "=" + String(v));
2828
- }
2829
- }
2830
- else if (Array.isArray(envVars)) {
2831
- for (const e of envVars) {
2832
- lines.push(" " + e["key"] + "=" + e["value"]);
2833
- }
2834
- }
2835
- return ok(lines.join("\n"));
2836
- }
2837
- if (name === "deployment_env_set") {
2838
- const did = String(args["deployment_id"] ?? "");
2839
- if (!did)
2840
- return err("deployment_id is required");
2841
- await client.apiPost("/api/v1/deployments/" + encodeURIComponent(did) + "/env", { key: args["key"], value: args["value"] });
2842
- return ok("Set " + String(args["key"]) + " on deployment " + did + ".");
2843
- }
2844
- if (name === "deployment_logs") {
2845
- const did = String(args["deployment_id"] ?? "");
2846
- if (!did)
2847
- return err("deployment_id is required");
2848
- const params = new URLSearchParams({ lines: String(args["lines"] ?? 100) });
2849
- if (args["since"])
2850
- params.set("since", String(args["since"]));
2851
- const result = await client.apiGet("/api/v1/deployments/" + encodeURIComponent(did) + "/logs?" + params.toString());
2852
- const logs = unwrapData(result);
2853
- if (Array.isArray(logs))
2854
- return ok(logs.length ? logs.map(String).join("\n") : "No logs.");
2855
- return ok(String(logs));
2856
- }
2857
- if (name === "deployment_version_list") {
2858
- const did = String(args["deployment_id"] ?? "");
2859
- if (!did)
2860
- return err("deployment_id is required");
2861
- const result = await client.apiGet("/api/v1/deployments/" + encodeURIComponent(did) + "/versions");
2862
- const versions = listOf(result);
2863
- if (versions.length === 0)
2864
- return ok("No versions found.");
2865
- const lines = ["Versions:"];
2866
- for (const v of versions) {
2867
- const r = v;
2868
- lines.push(" " + r["id"] + " " + (r["created_at"] ?? "") + " " + (r["status"] ?? ""));
2869
- }
2870
- return ok(lines.join("\n"));
2871
- }
2872
- if (name === "deployment_version_promote") {
2873
- const did = String(args["deployment_id"] ?? "");
2874
- const vid = String(args["version_id"] ?? "");
2875
- if (!did)
2876
- return err("deployment_id is required");
2877
- if (!vid)
2878
- return err("version_id is required");
2879
- await client.apiPost("/api/v1/deployments/" + encodeURIComponent(did) + "/versions/" + encodeURIComponent(vid) + "/promote", {});
2880
- return ok("Version " + vid + " promoted on deployment " + did + ".");
2881
- }
2882
- // ── Storage ───────────────────────────────────────────────────────────
2883
- if (name === "storage_bucket_list") {
2884
- const result = await client.apiGet("/api/v1/storage/buckets");
2885
- const items = listOf(result);
2886
- if (items.length === 0)
2887
- return ok("No buckets found.");
2888
- const lines = ["Buckets:"];
2889
- for (const b of items) {
2890
- const r = b;
2891
- lines.push(" " + r["id"] + " " + r["name"] + " " + (r["region"] ?? ""));
2892
- }
2893
- return ok(lines.join("\n"));
2894
- }
2895
- if (name === "storage_bucket_create") {
2896
- const body = { name: args["name"] };
2897
- if (args["region"])
2898
- body["region"] = args["region"];
2899
- if (args["public"] !== undefined)
2900
- body["public"] = args["public"];
2901
- const result = await client.apiPost("/api/v1/storage/buckets", body);
2902
- const data = unwrapData(result);
2903
- return ok("Created bucket '" + String(data["name"] ?? args["name"]) + "' (id=" + data["id"] + ")");
2904
- }
2905
- if (name === "storage_bucket_delete") {
2906
- const bid = String(args["bucket_id"] ?? "");
2907
- if (!bid)
2908
- return err("bucket_id is required");
2909
- await client.apiDelete("/api/v1/storage/buckets/" + encodeURIComponent(bid));
2910
- return ok("Bucket " + bid + " deleted.");
2911
- }
2912
- if (name === "storage_object_list") {
2913
- const bid = String(args["bucket_id"] ?? "");
2914
- if (!bid)
2915
- return err("bucket_id is required");
2916
- const params = new URLSearchParams();
2917
- if (args["prefix"])
2918
- params.set("prefix", String(args["prefix"]));
2919
- const qs = params.toString() ? "?" + params.toString() : "";
2920
- const result = await client.apiGet("/api/v1/storage/buckets/" + encodeURIComponent(bid) + "/objects" + qs);
2921
- const items = listOf(result);
2922
- if (items.length === 0)
2923
- return ok("No objects found.");
2924
- const lines = ["Objects:"];
2925
- for (const o of items) {
2926
- const r = o;
2927
- lines.push(" " + r["key"] + " " + (r["size"] ?? "") + " " + (r["last_modified"] ?? ""));
2928
- }
2929
- return ok(lines.join("\n"));
2930
- }
2931
- if (name === "storage_object_upload") {
2932
- const bid = String(args["bucket_id"] ?? "");
2933
- if (!bid)
2934
- return err("bucket_id is required");
2935
- await client.apiPost("/api/v1/storage/buckets/" + encodeURIComponent(bid) + "/objects", {
2936
- key: args["key"],
2937
- content: args["content"],
2938
- content_type: args["content_type"] ?? "application/octet-stream",
2939
- });
2940
- return ok("Uploaded " + String(args["key"]) + " to bucket " + bid + ".");
2941
- }
2942
- if (name === "storage_object_download") {
2943
- const bid = String(args["bucket_id"] ?? "");
2944
- if (!bid)
2945
- return err("bucket_id is required");
2946
- const result = await client.apiGet("/api/v1/storage/buckets/" + encodeURIComponent(bid) + "/objects/" + encodeURIComponent(String(args["key"] ?? "")));
2947
- const data = unwrapData(result);
2948
- const content = typeof data["content"] === "string"
2949
- ? Buffer.from(data["content"], "base64").toString("utf8")
2950
- : typeof data["text"] === "string"
2951
- ? data["text"]
2952
- : JSON.stringify(data);
2953
- return ok(content);
2954
- }
2955
- if (name === "storage_object_delete") {
2956
- const bid = String(args["bucket_id"] ?? "");
2957
- if (!bid)
2958
- return err("bucket_id is required");
2959
- await client.apiDelete("/api/v1/storage/buckets/" + encodeURIComponent(bid) + "/objects/" + encodeURIComponent(String(args["key"] ?? "")));
2960
- return ok("Deleted " + String(args["key"]) + " from bucket " + bid + ".");
2961
- }
2962
- if (name === "storage_presign") {
2963
- const bid = String(args["bucket_id"] ?? "");
2964
- if (!bid)
2965
- return err("bucket_id is required");
2966
- const result = await client.apiPost("/api/v1/storage/buckets/" + encodeURIComponent(bid) + "/presign", {
2967
- key: args["key"],
2968
- expires_in: args["expires_in"] ?? 3600,
2969
- method: args["method"] ?? "GET",
2970
- });
2971
- const data = unwrapData(result);
2972
- return ok("Presigned URL: " + (data["url"] ?? JSON.stringify(data)));
2973
- }
2974
- // ── Databases ─────────────────────────────────────────────────────────
2975
- if (name === "database_list") {
2976
- const result = await client.apiGet("/api/v1/databases");
2977
- const items = listOf(result);
2978
- if (items.length === 0)
2979
- return ok("No databases found.");
2980
- const lines = ["Databases:"];
2981
- for (const d of items) {
2982
- const r = d;
2983
- lines.push(" " + r["id"] + " " + r["name"] + " " + (r["engine"] ?? "") + " " + (r["status"] ?? ""));
2984
- }
2985
- return ok(lines.join("\n"));
2986
- }
2987
- if (name === "database_create") {
2988
- const engine = normalizeDatabaseEngine(args["engine"]);
2989
- const body = { name: args["name"], engine };
2990
- if (engine === "postgresql" && !args["version"])
2991
- body["engine_version"] = "16";
2992
- for (const key of ["size", "region"]) {
2993
- if (args[key])
2994
- body[key] = args[key];
2995
- }
2996
- if (args["version"])
2997
- body["engine_version"] = args["version"];
2998
- const result = await client.apiPost("/api/v1/databases", body);
2999
- const data = unwrapData(result);
3000
- return ok("Created database '" + String(data["name"] ?? args["name"]) + "' (id=" + data["id"] + ")");
3001
- }
3002
- if (name === "database_get") {
3003
- const dbid = String(args["database_id"] ?? "");
3004
- if (!dbid)
3005
- return err("database_id is required");
3006
- const result = await client.apiGet("/api/v1/databases/" + encodeURIComponent(dbid));
3007
- return ok(JSON.stringify(unwrapData(result), null, 2));
3008
- }
3009
- if (name === "database_delete") {
3010
- const dbid = String(args["database_id"] ?? "");
3011
- if (!dbid)
3012
- return err("database_id is required");
3013
- await client.apiDelete("/api/v1/databases/" + encodeURIComponent(dbid));
3014
- return ok("Database " + dbid + " deleted.");
3015
- }
3016
- if (name === "database_credentials") {
3017
- const dbid = String(args["database_id"] ?? "");
3018
- if (!dbid)
3019
- return err("database_id is required");
3020
- const result = await client.apiGet("/api/v1/databases/" + encodeURIComponent(dbid) + "/credentials");
3021
- const data = unwrapData(result);
3022
- const lines = ["Database credentials:"];
3023
- for (const field of ["connection_string", "host", "port", "database", "username", "password"]) {
3024
- if (data[field])
3025
- lines.push(" " + field + ": " + data[field]);
3026
- }
3027
- return ok(lines.join("\n"));
3028
- }
3029
- if (name === "database_logs") {
3030
- const dbid = String(args["database_id"] ?? "");
3031
- if (!dbid)
3032
- return err("database_id is required");
3033
- const params = new URLSearchParams({ lines: String(args["lines"] ?? 100) });
3034
- if (args["since"])
3035
- params.set("since", String(args["since"]));
3036
- const result = await client.apiGet("/api/v1/databases/" + encodeURIComponent(dbid) + "/logs?" + params.toString());
3037
- const logs = unwrapData(result);
3038
- if (Array.isArray(logs))
3039
- return ok(logs.length ? logs.map(String).join("\n") : "No logs.");
3040
- return ok(String(logs));
3041
- }
3042
- // ── Workspaces ────────────────────────────────────────────────────────
3043
- if (name === "workspace_list") {
3044
- const result = await client.apiGet("/api/v1/workspaces");
3045
- const items = listOf(result);
3046
- if (items.length === 0)
3047
- return ok("No workspaces found.");
3048
- const lines = ["Workspaces:"];
3049
- for (const w of items) {
3050
- const r = w;
3051
- lines.push(" " + r["id"] + " " + r["name"]);
3052
- }
3053
- return ok(lines.join("\n"));
3054
- }
3055
- if (name === "workspace_create") {
3056
- const body = { name: args["name"] };
3057
- if (args["description"])
3058
- body["description"] = args["description"];
3059
- const result = await client.apiPost("/api/v1/workspaces", body);
3060
- const data = unwrapData(result);
3061
- return ok("Created workspace '" + String(data["name"] ?? args["name"]) + "' (id=" + data["id"] + ")");
3062
- }
3063
- if (name === "workspace_get") {
3064
- const wid = String(args["workspace_id"] ?? "");
3065
- if (!wid)
3066
- return err("workspace_id is required");
3067
- const result = await client.apiGet("/api/v1/workspaces/" + encodeURIComponent(wid));
3068
- return ok(JSON.stringify(unwrapData(result), null, 2));
3069
- }
3070
- if (name === "workspace_update") {
3071
- const wid = String(args["workspace_id"] ?? "");
3072
- if (!wid)
3073
- return err("workspace_id is required");
3074
- const body = {};
3075
- if (args["name"])
3076
- body["name"] = args["name"];
3077
- if (args["description"])
3078
- body["description"] = args["description"];
3079
- await client.apiPost("/api/v1/workspaces/" + encodeURIComponent(wid), body);
3080
- return ok("Workspace " + wid + " updated.");
3081
- }
3082
- if (name === "workspace_stats") {
3083
- const wid = String(args["workspace_id"] ?? "");
3084
- if (!wid)
3085
- return err("workspace_id is required");
3086
- const result = await client.apiGet("/api/v1/workspaces/" + encodeURIComponent(wid) + "/stats");
3087
- return ok(JSON.stringify(unwrapData(result), null, 2));
3088
- }
3089
- if (name === "workspace_usage") {
3090
- const wid = String(args["workspace_id"] ?? "");
3091
- if (!wid)
3092
- return err("workspace_id is required");
3093
- const params = new URLSearchParams();
3094
- if (args["period"])
3095
- params.set("period", String(args["period"]));
3096
- const qs = params.toString() ? "?" + params.toString() : "";
3097
- const result = await client.apiGet("/api/v1/workspaces/" + encodeURIComponent(wid) + "/usage" + qs);
3098
- return ok(JSON.stringify(unwrapData(result), null, 2));
3099
- }
3100
- // ── Billing ───────────────────────────────────────────────────────────
3101
- if (name === "billing_usage") {
3102
- const result = await client.apiGet("/api/v1/billing/overview");
3103
- return ok(JSON.stringify(unwrapData(result), null, 2));
3104
- }
3105
- if (name === "billing_plan") {
3106
- const result = await client.apiGet("/api/v1/billing/overview");
3107
- return ok(JSON.stringify(unwrapData(result), null, 2));
3108
- }
3109
- return err(`Unknown tool: ${name}`);
3110
- }
3111
- catch (e) {
3112
- const msg = e instanceof Error ? e.message : String(e);
3113
- return err(msg);
3114
- }
3115
- }
3116
- // ── Payload helpers ───────────────────────────────────────────────────────────
3117
- function resolveSandboxSize(value) {
3118
- if (value === undefined || value === null || value === "")
3119
- return "small";
3120
- if (typeof value === "string" &&
3121
- SANDBOX_SIZES.includes(value)) {
3122
- return value;
3123
- }
3124
- throw new Error(`Invalid sandbox size '${String(value)}'; expected xs, small, medium, large, or xl`);
3125
- }
3126
- function unwrapData(payload) {
3127
- if (payload !== null &&
3128
- typeof payload === "object" &&
3129
- !Array.isArray(payload) &&
3130
- "data" in payload) {
3131
- return payload["data"];
3132
- }
3133
- return payload;
3134
- }
3135
- function listOf(payload) {
3136
- if (Array.isArray(payload))
3137
- return payload;
3138
- if (payload !== null &&
3139
- typeof payload === "object" &&
3140
- !Array.isArray(payload)) {
3141
- const r = payload;
3142
- if (Array.isArray(r["data"]))
3143
- return r["data"];
3144
- }
3145
- return [];
3146
- }
3147
- // ── MCP server loop ───────────────────────────────────────────────────────────
3148
- async function runServer() {
3149
- const config = loadConfig();
3150
- if (!config.api_key) {
3151
- process.stderr.write("Error: MIOSA_API_KEY is not set and no saved auth found.\n" +
3152
- "Set MIOSA_API_KEY env var or run: miosa login\n");
3153
- process.exit(1);
3154
- }
3155
- const client = new MiosaClient(config);
3156
- const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
3157
- for await (const line of rl) {
3158
- const trimmed = line.trim();
3159
- if (!trimmed)
3160
- continue;
3161
- let req;
3162
- try {
3163
- req = JSON.parse(trimmed);
3164
- }
3165
- catch {
3166
- send({
3167
- jsonrpc: "2.0",
3168
- id: null,
3169
- error: { code: -32700, message: "Parse error" },
3170
- });
3171
- continue;
3172
- }
3173
- const id = req.id ?? null;
3174
- switch (req.method) {
3175
- case "initialize": {
3176
- send({
3177
- jsonrpc: "2.0",
3178
- id,
3179
- result: {
3180
- protocolVersion: "2024-11-05",
3181
- capabilities: { tools: {} },
3182
- serverInfo: { name: "miosa-mcp", version: "0.1.1" },
3183
- },
3184
- });
3185
- break;
3186
- }
3187
- case "notifications/initialized":
3188
- // No response required for notifications
3189
- break;
3190
- case "tools/list": {
3191
- send({
3192
- jsonrpc: "2.0",
3193
- id,
3194
- result: { tools: MCP_TOOLS },
3195
- });
3196
- break;
3197
- }
3198
- case "tools/call": {
3199
- const params = req.params;
3200
- const toolName = String(params?.["name"] ?? "");
3201
- const toolArgs = (params?.["arguments"] ?? {});
3202
- const toolResult = await dispatchMcpTool(client, toolName, toolArgs);
3203
- send({ jsonrpc: "2.0", id, result: toolResult });
3204
- break;
3205
- }
3206
- case "ping": {
3207
- send({ jsonrpc: "2.0", id, result: {} });
3208
- break;
3209
- }
3210
- default: {
3211
- send({
3212
- jsonrpc: "2.0",
3213
- id,
3214
- error: { code: -32601, message: `Method not found: ${req.method}` },
3215
- });
3216
- }
3217
- }
3218
- }
3219
- }
3220
- const MCP_REMOTE_URL = "https://api.miosa.ai/api/v1/mcp";
3221
- const MCP_SERVER_NAME = "miosa";
3222
- function sleep(ms) {
3223
- return new Promise((resolve) => setTimeout(resolve, ms));
3224
- }
3225
- function openUrl(url) {
3226
- const command = process.platform === "darwin"
3227
- ? "open"
3228
- : process.platform === "win32"
3229
- ? "cmd"
3230
- : "xdg-open";
3231
- const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
3232
- const child = spawn(command, args, { detached: true, stdio: "ignore" });
3233
- child.unref();
3234
- }
3235
- async function postJson(endpoint, path, body) {
3236
- const res = await request(`${endpoint.replace(/\/$/, "")}${path}`, {
3237
- method: "POST",
3238
- headers: { "Content-Type": "application/json", Accept: "application/json" },
3239
- body: JSON.stringify(body),
3240
- });
3241
- const text = await res.body.text();
3242
- const parsed = text ? JSON.parse(text) : {};
3243
- return { status: res.statusCode, body: parsed };
3244
- }
3245
- async function runDeviceFlow(endpoint, clientName) {
3246
- const start = await postJson(endpoint, "/api/v1/auth/cli/start", {
3247
- client_name: clientName,
3248
- });
3249
- if (start.status >= 400) {
3250
- throw new Error(`Failed to start auth flow (HTTP ${start.status})`);
3251
- }
3252
- const flow = start.body;
3253
- console.log();
3254
- console.log(chalk.bold("Authorize MIOSA MCP for this device"));
3255
- console.log();
3256
- console.log(` Open: ${chalk.cyan(flow.verification_uri_complete)}`);
3257
- console.log(` Code: ${chalk.bold(flow.user_code)}`);
3258
- console.log();
3259
- try {
3260
- openUrl(flow.verification_uri_complete);
3261
- console.log(chalk.dim(" Browser opened. Waiting for approval..."));
3262
- }
3263
- catch {
3264
- console.log(chalk.dim(" Could not open a browser automatically."));
3265
- }
3266
- const deadline = Date.now() + flow.expires_in * 1000;
3267
- const intervalMs = Math.max(flow.interval || 3, 1) * 1000;
3268
- while (Date.now() < deadline) {
3269
- await sleep(intervalMs);
3270
- const poll = await postJson(endpoint, "/api/v1/auth/cli/token", { device_code: flow.device_code });
3271
- if (poll.status === 200 && poll.body.api_key) {
3272
- return poll.body.api_key;
3273
- }
3274
- if (poll.status === 428 || poll.body.error === "authorization_pending") {
3275
- continue;
3276
- }
3277
- if (poll.body.error === "access_denied") {
3278
- throw new Error("Login was denied in the browser.");
3279
- }
3280
- if (poll.body.error === "expired_token" || poll.status === 410) {
3281
- throw new Error("Login request expired. Run the command again.");
3282
- }
3283
- throw new Error(`Login failed: ${poll.body.error ?? `HTTP ${poll.status}`}`);
3284
- }
3285
- throw new Error("Login timed out. Run the command again.");
3286
- }
3287
- function wireClaudeCode(apiKey, remoteUrl, scope) {
3288
- // `claude mcp remove` first so re-running install replaces cleanly.
3289
- spawnSync("claude", ["mcp", "remove", MCP_SERVER_NAME, "--scope", scope], {
3290
- stdio: "ignore",
3291
- });
3292
- const result = spawnSync("claude", [
3293
- "mcp",
3294
- "add",
3295
- "--transport",
3296
- "http",
3297
- "--scope",
3298
- scope,
3299
- MCP_SERVER_NAME,
3300
- remoteUrl,
3301
- "--header",
3302
- `Authorization: Bearer ${apiKey}`,
3303
- ], { stdio: "pipe", encoding: "utf8" });
3304
- if (result.error) {
3305
- return {
3306
- ok: false,
3307
- reason: `Could not run \`claude\` CLI: ${result.error.message}`,
3308
- };
3309
- }
3310
- if (typeof result.status === "number" && result.status !== 0) {
3311
- return {
3312
- ok: false,
3313
- reason: result.stderr?.trim() || `claude mcp add exited ${result.status}`,
3314
- };
3315
- }
3316
- return { ok: true };
3317
- }
3318
- function printManualSnippet(client, apiKey, remoteUrl) {
3319
- const masked = apiKey.length > 12
3320
- ? apiKey.slice(0, 6) + "…" + apiKey.slice(-4)
3321
- : "msk_u_…";
3322
- console.log();
3323
- console.log(chalk.bold("Manual install snippet"));
3324
- console.log(chalk.dim(` (your key: ${masked} — saved to ~/.miosa/config.json)`));
3325
- console.log();
3326
- if (client === "cursor") {
3327
- console.log(chalk.dim(" Add to ~/.cursor/mcp.json:"));
3328
- console.log();
3329
- console.log(JSON.stringify({
3330
- mcpServers: {
3331
- miosa: {
3332
- transport: "http",
3333
- url: remoteUrl,
3334
- headers: { Authorization: `Bearer ${apiKey}` },
3335
- },
3336
- },
3337
- }, null, 2));
3338
- return;
3339
- }
3340
- if (client === "gemini") {
3341
- console.log(` ${chalk.cyan(`gemini mcp add ${MCP_SERVER_NAME} ${remoteUrl} \\`)}`);
3342
- console.log(` ${chalk.cyan(`--header "Authorization: Bearer ${apiKey}"`)}`);
3343
- return;
3344
- }
3345
- // claude / manual
3346
- console.log(` ${chalk.cyan(`claude mcp add --transport http --scope user ${MCP_SERVER_NAME} \\`)}`);
3347
- console.log(` ${chalk.cyan(remoteUrl + " \\")}`);
3348
- console.log(` ${chalk.cyan(`--header "Authorization: Bearer ${apiKey}"`)}`);
3349
- }
3350
- async function runInstall(opts) {
3351
- const config = loadConfig();
3352
- const clientName = `MIOSA MCP (${opts.client === "manual" ? "manual" : opts.client})`;
3353
- console.log(chalk.bold("MIOSA MCP installer"), chalk.dim(`— wiring ${opts.client} → ${opts.remoteUrl}`));
3354
- const apiKey = await runDeviceFlow(config.endpoint, clientName);
3355
- if (opts.client === "claude") {
3356
- const wired = wireClaudeCode(apiKey, opts.remoteUrl, opts.scope);
3357
- if (wired.ok) {
3358
- console.log();
3359
- console.log(chalk.green("✓"), `MCP server '${MCP_SERVER_NAME}' added to Claude Code (${opts.scope} scope).`);
3360
- console.log();
3361
- console.log(chalk.dim("Verify:"));
3362
- console.log(` ${chalk.cyan("claude mcp list")}`);
3363
- console.log();
3364
- console.log(chalk.dim("Try in a fresh Claude Code session:"));
3365
- console.log(chalk.dim(` "Create a MIOSA sandbox, run \`python -c 'print(2+2)'\`, then destroy it."`));
3366
- return;
3367
- }
3368
- console.log();
3369
- console.log(chalk.yellow("!"), `Could not auto-wire Claude Code: ${wired.reason}`);
3370
- console.log(chalk.yellow(" Falling back to manual snippet:"));
3371
- printManualSnippet("claude", apiKey, opts.remoteUrl);
3372
- return;
3373
- }
3374
- // cursor / gemini / manual — print the snippet
3375
- printManualSnippet(opts.client, apiKey, opts.remoteUrl);
3376
- }
3377
- // ── Commander registration ────────────────────────────────────────────────────
3378
- export function register(program) {
3379
- const mcp = program
3380
- .command("mcp")
3381
- .description("Model Context Protocol — expose MIOSA tools to AI agents");
3382
- mcp
3383
- .command("serve")
3384
- .description("Start an MCP server over stdio (JSON-RPC 2.0). Add to .claude/mcp.json to use with Claude.")
3385
- .action(() => {
3386
- runServer().catch((e) => {
3387
- const msg = e instanceof Error ? e.message : String(e);
3388
- process.stderr.write(`miosa mcp serve fatal: ${msg}\n`);
3389
- process.exit(1);
3390
- });
3391
- });
3392
- mcp
3393
- .command("install")
3394
- .description("Install the hosted MIOSA MCP server (https://api.miosa.ai/api/v1/mcp) into your AI client. Opens a browser to log in and wire your account.")
3395
- .option("-c, --client <client>", "Which AI client to wire: claude (default), cursor, gemini, manual", "claude")
3396
- .option("-s, --scope <scope>", "Claude Code config scope: local, user (default), or project", "user")
3397
- .option("--url <url>", "Override the hosted MCP URL (default: https://api.miosa.ai/api/v1/mcp)", MCP_REMOTE_URL)
3398
- .action(async (opts) => {
3399
- const client = (["claude", "cursor", "gemini", "manual"].includes(opts.client)
3400
- ? opts.client
3401
- : "claude");
3402
- const scope = (["local", "user", "project"].includes(opts.scope) ? opts.scope : "user");
3403
- try {
3404
- await runInstall({ client, scope, remoteUrl: opts.url });
3405
- }
3406
- catch (e) {
3407
- const msg = e instanceof Error ? e.message : String(e);
3408
- console.error(chalk.red(`Error: ${msg}`));
3409
- process.exit(3);
3410
- }
3411
- });
3412
- }
3413
- //# sourceMappingURL=mcp.js.map