openmausbot 0.1.55

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 (499) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +14 -0
  3. package/cli.js +2 -0
  4. package/dist/app-icon.svg +29 -0
  5. package/dist/assets/abap-BdImnpbu.js +1 -0
  6. package/dist/assets/actionscript-3-B3316cI-.js +1 -0
  7. package/dist/assets/ada-bCR0ucgS.js +1 -0
  8. package/dist/assets/ahk-CsyLZFj1.js +1 -0
  9. package/dist/assets/ahk2-8Zs4aa1G.js +1 -0
  10. package/dist/assets/andromeeda-C4gqWexZ.js +1 -0
  11. package/dist/assets/angular-html-DA-rfuFy.js +1 -0
  12. package/dist/assets/angular-ts-BrjP3tb8.js +1 -0
  13. package/dist/assets/apache-Pmp26Uib.js +1 -0
  14. package/dist/assets/apex-DhZFqWV2.js +1 -0
  15. package/dist/assets/apl-CORt7UWP.js +1 -0
  16. package/dist/assets/applescript-Co6uUVPk.js +1 -0
  17. package/dist/assets/ara-BRHolxvo.js +1 -0
  18. package/dist/assets/asciidoc-CSVQ5wI8.js +1 -0
  19. package/dist/assets/asm-D_Q5rh1f.js +1 -0
  20. package/dist/assets/astro-HNnZUWAn.js +1 -0
  21. package/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
  22. package/dist/assets/awk-DMzUqQB5.js +1 -0
  23. package/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
  24. package/dist/assets/ayu-light-BA47KaF1.js +1 -0
  25. package/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
  26. package/dist/assets/ballerina-BFfxhgS-.js +1 -0
  27. package/dist/assets/bat-CickPsom.js +1 -0
  28. package/dist/assets/beancount-k_qm7-4y.js +1 -0
  29. package/dist/assets/berry-uYugtg8r.js +1 -0
  30. package/dist/assets/bibtex-CHM0blh-.js +1 -0
  31. package/dist/assets/bicep-Bmn6On1c.js +1 -0
  32. package/dist/assets/bird2-Bx8U0n9b.js +1 -0
  33. package/dist/assets/blade-2xfisSek.js +1 -0
  34. package/dist/assets/bsl-DlhNcFeZ.js +1 -0
  35. package/dist/assets/c-BIGW1oBm.js +1 -0
  36. package/dist/assets/c3-Dp5svz6Z.js +1 -0
  37. package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
  38. package/dist/assets/cairo-KRGpt6FW.js +1 -0
  39. package/dist/assets/catppuccin-frappe-CZL1YF0i.js +1 -0
  40. package/dist/assets/catppuccin-latte-DH-8KZSZ.js +1 -0
  41. package/dist/assets/catppuccin-macchiato-B7yYVSCf.js +1 -0
  42. package/dist/assets/catppuccin-mocha-Ct7hS0mc.js +1 -0
  43. package/dist/assets/chapel-DTp_pixX.js +1 -0
  44. package/dist/assets/clarity-Dn5IMItf.js +1 -0
  45. package/dist/assets/clojure-P80f7IUj.js +1 -0
  46. package/dist/assets/cmake-D1j8_8rp.js +1 -0
  47. package/dist/assets/cobol-nBiQ_Alo.js +1 -0
  48. package/dist/assets/codeowners-Bp6g37R7.js +1 -0
  49. package/dist/assets/codeql-DsOJ9woJ.js +1 -0
  50. package/dist/assets/coffee-Ch7k5sss.js +1 -0
  51. package/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
  52. package/dist/assets/coq-C7JzOVbR.js +1 -0
  53. package/dist/assets/cpp-BMRokrvK.js +1 -0
  54. package/dist/assets/crystal-DGywbUpC.js +1 -0
  55. package/dist/assets/csharp-DSvCPggb.js +1 -0
  56. package/dist/assets/css-CLj8gQPS.js +1 -0
  57. package/dist/assets/csv-fuZLfV_i.js +1 -0
  58. package/dist/assets/cue-D82EKSYY.js +1 -0
  59. package/dist/assets/cypher-COkxafJQ.js +1 -0
  60. package/dist/assets/d-85-TOEBH.js +1 -0
  61. package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
  62. package/dist/assets/dart-bE4Kk8sk.js +1 -0
  63. package/dist/assets/dax-CEL-wOlO.js +1 -0
  64. package/dist/assets/desktop-BmXAJ9_W.js +1 -0
  65. package/dist/assets/diff-D97Zzqfu.js +1 -0
  66. package/dist/assets/docker-BcOcwvcX.js +1 -0
  67. package/dist/assets/dotenv-Da5cRb03.js +1 -0
  68. package/dist/assets/dracula-BzJJZx-M.js +1 -0
  69. package/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
  70. package/dist/assets/dream-maker-BtqSS_iP.js +1 -0
  71. package/dist/assets/edge-FbVlp4U3.js +1 -0
  72. package/dist/assets/elixir-CkH2-t6x.js +1 -0
  73. package/dist/assets/elm-DbKCFpqz.js +1 -0
  74. package/dist/assets/emacs-lisp-C_m_b--Z.js +1 -0
  75. package/dist/assets/erb-DXfck5VN.js +1 -0
  76. package/dist/assets/erlang-DsQrWhSR.js +1 -0
  77. package/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
  78. package/dist/assets/everforest-light-C8M2exoo.js +1 -0
  79. package/dist/assets/fennel-BYunw83y.js +1 -0
  80. package/dist/assets/fish-BvzEVeQv.js +1 -0
  81. package/dist/assets/fluent-C4IJs8-o.js +1 -0
  82. package/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  83. package/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
  84. package/dist/assets/fsharp-CXgrBDvD.js +1 -0
  85. package/dist/assets/gdresource-TyuKm33G.js +1 -0
  86. package/dist/assets/gdscript-DqcFQ5yU.js +1 -0
  87. package/dist/assets/gdshader-DkwncUOv.js +1 -0
  88. package/dist/assets/genie-D0YGMca9.js +1 -0
  89. package/dist/assets/gherkin-DyxjwDmM.js +1 -0
  90. package/dist/assets/git-commit-F4YmCXRG.js +1 -0
  91. package/dist/assets/git-rebase-r7XF79zn.js +1 -0
  92. package/dist/assets/github-dark-DHJKELXO.js +1 -0
  93. package/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
  94. package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  95. package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  96. package/dist/assets/github-light-DAi9KRSo.js +1 -0
  97. package/dist/assets/github-light-default-D7oLnXFd.js +1 -0
  98. package/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  99. package/dist/assets/gleam-BspZqrRM.js +1 -0
  100. package/dist/assets/glimmer-js-ByusRIyA.js +1 -0
  101. package/dist/assets/glimmer-ts-BfAWNZQY.js +1 -0
  102. package/dist/assets/glsl-DplSGwfg.js +1 -0
  103. package/dist/assets/gn-n2N0HUVH.js +1 -0
  104. package/dist/assets/gnuplot-DdkO51Og.js +1 -0
  105. package/dist/assets/go-C27-OAKa.js +1 -0
  106. package/dist/assets/graphql-ChdNCCLP.js +1 -0
  107. package/dist/assets/groovy-gcz8RCvz.js +1 -0
  108. package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  109. package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  110. package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  111. package/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  112. package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  113. package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  114. package/dist/assets/hack-BWmVpMyf.js +1 -0
  115. package/dist/assets/haml-D5jkg6IW.js +1 -0
  116. package/dist/assets/handlebars-BpdQsYii.js +1 -0
  117. package/dist/assets/haskell-Df6bDoY_.js +1 -0
  118. package/dist/assets/haxe-CfZj7gIn.js +1 -0
  119. package/dist/assets/hcl-BWvSN4gD.js +1 -0
  120. package/dist/assets/hjson-D5-asLiD.js +1 -0
  121. package/dist/assets/hlsl-D3lLCCz7.js +1 -0
  122. package/dist/assets/horizon-BUw7H-hv.js +1 -0
  123. package/dist/assets/horizon-bright-CUuTKBJd.js +1 -0
  124. package/dist/assets/houston-DnULxvSX.js +1 -0
  125. package/dist/assets/html-derivative-DlHx6ybY.js +1 -0
  126. package/dist/assets/html-pp8916En.js +1 -0
  127. package/dist/assets/http-jrhK8wxY.js +1 -0
  128. package/dist/assets/hurl-irOxFIW8.js +1 -0
  129. package/dist/assets/hxml-2-FPmUDs.js +1 -0
  130. package/dist/assets/hy-DFXneXwc.js +1 -0
  131. package/dist/assets/imba-DGztddWO.js +1 -0
  132. package/dist/assets/index-B9rLIY81.js +263 -0
  133. package/dist/assets/index-CJBrtDbC.css +1 -0
  134. package/dist/assets/index-D-u0LYsV.js +153 -0
  135. package/dist/assets/ini-BEwlwnbL.js +1 -0
  136. package/dist/assets/java-CylS5w8V.js +1 -0
  137. package/dist/assets/javascript-wDzz0qaB.js +1 -0
  138. package/dist/assets/jinja-f2NsQr07.js +1 -0
  139. package/dist/assets/jison-wvAkD_A8.js +1 -0
  140. package/dist/assets/json-Cp-IABpG.js +1 -0
  141. package/dist/assets/json5-C9tS-k6U.js +1 -0
  142. package/dist/assets/jsonc-Des-eS-w.js +1 -0
  143. package/dist/assets/jsonl-DcaNXYhu.js +1 -0
  144. package/dist/assets/jsonnet-DFQXde-d.js +1 -0
  145. package/dist/assets/jssm-C2t-YnRu.js +1 -0
  146. package/dist/assets/jsx-g9-lgVsj.js +1 -0
  147. package/dist/assets/julia-5Bft2YPA.js +1 -0
  148. package/dist/assets/just-Cwhn7H3k.js +1 -0
  149. package/dist/assets/kanagawa-dragon-BuwD2xS4.js +1 -0
  150. package/dist/assets/kanagawa-lotus-C3DzagqV.js +1 -0
  151. package/dist/assets/kanagawa-wave-DFGoQZhC.js +1 -0
  152. package/dist/assets/kdl-DV7GczEv.js +1 -0
  153. package/dist/assets/kotlin-BdnUsdx6.js +1 -0
  154. package/dist/assets/kusto-wEQ09or8.js +1 -0
  155. package/dist/assets/laserwave-DUszq2jm.js +1 -0
  156. package/dist/assets/latex-D5pSuvFb.js +1 -0
  157. package/dist/assets/lean-BZvkOJ9d.js +1 -0
  158. package/dist/assets/less-B1dDrJ26.js +1 -0
  159. package/dist/assets/light-plus-B7mTdjB0.js +1 -0
  160. package/dist/assets/liquid-C0sCDyMI.js +1 -0
  161. package/dist/assets/llvm-BZoOZj88.js +1 -0
  162. package/dist/assets/log-2UxHyX5q.js +1 -0
  163. package/dist/assets/logo-BtOb2qkB.js +1 -0
  164. package/dist/assets/lua-BaeVxFsk.js +1 -0
  165. package/dist/assets/luau-BnpPk5vE.js +1 -0
  166. package/dist/assets/make-CHLpvVh8.js +1 -0
  167. package/dist/assets/markdown-Cvjx9yec.js +1 -0
  168. package/dist/assets/marko-DjSrsDqO.js +1 -0
  169. package/dist/assets/material-theme-D5KoaKCx.js +1 -0
  170. package/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
  171. package/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  172. package/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
  173. package/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  174. package/dist/assets/matlab-D7o27uSR.js +1 -0
  175. package/dist/assets/mdc-D1_yUvq7.js +1 -0
  176. package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
  177. package/dist/assets/mermaid-CQcHuHx7.js +1 -0
  178. package/dist/assets/min-dark-CafNBF8u.js +1 -0
  179. package/dist/assets/min-light-CTRr51gU.js +1 -0
  180. package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
  181. package/dist/assets/mojo-DJz3ZmWd.js +1 -0
  182. package/dist/assets/monokai-BRVnQi9A.js +1 -0
  183. package/dist/assets/moonbit-CHtswR0a.js +1 -0
  184. package/dist/assets/move-el3G9tDJ.js +1 -0
  185. package/dist/assets/narrat-DRg8JJMk.js +1 -0
  186. package/dist/assets/nextflow-C-mBbutL.js +1 -0
  187. package/dist/assets/nextflow-groovy-vE_lwT2v.js +1 -0
  188. package/dist/assets/nginx-BpAMiNFr.js +1 -0
  189. package/dist/assets/night-owl-C39BiMTA.js +1 -0
  190. package/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
  191. package/dist/assets/nim-BIad80T-.js +1 -0
  192. package/dist/assets/nix-CwoSXNpI.js +1 -0
  193. package/dist/assets/nord-Ddv68eIx.js +1 -0
  194. package/dist/assets/nsis-BlV79W_Q.js +1 -0
  195. package/dist/assets/nushell-D3jzshHO.js +1 -0
  196. package/dist/assets/objective-c-DXmwc3jG.js +1 -0
  197. package/dist/assets/objective-cpp-CLxacb5B.js +1 -0
  198. package/dist/assets/ocaml-C0hk2d4L.js +1 -0
  199. package/dist/assets/odin-BBf5iR-q.js +1 -0
  200. package/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  201. package/dist/assets/one-light-C3Wv6jpd.js +1 -0
  202. package/dist/assets/openscad-C4EeE6gA.js +1 -0
  203. package/dist/assets/org-DM6o9KBp.js +1 -0
  204. package/dist/assets/pascal-D93ZcfNL.js +1 -0
  205. package/dist/assets/perl-B9cMNwum.js +1 -0
  206. package/dist/assets/php-Csjmro_R.js +1 -0
  207. package/dist/assets/pkl-u5AG7uiY.js +1 -0
  208. package/dist/assets/plastic-3e1v2bzS.js +1 -0
  209. package/dist/assets/plsql-ChMvpjG-.js +1 -0
  210. package/dist/assets/po-BTJTHyun.js +1 -0
  211. package/dist/assets/poimandres-CS3Unz2-.js +1 -0
  212. package/dist/assets/polar-C0HS_06l.js +1 -0
  213. package/dist/assets/postcss-CXtECtnM.js +1 -0
  214. package/dist/assets/powerquery-CEu0bR-o.js +1 -0
  215. package/dist/assets/powershell-BmBUJMz7.js +1 -0
  216. package/dist/assets/prisma-Vru482bI.js +1 -0
  217. package/dist/assets/prolog-CbFg5uaA.js +1 -0
  218. package/dist/assets/proto-C7zT0LnQ.js +1 -0
  219. package/dist/assets/pug-DKIMFp6K.js +1 -0
  220. package/dist/assets/puppet-BMWR74SV.js +1 -0
  221. package/dist/assets/purescript-CklMAg4u.js +1 -0
  222. package/dist/assets/python-B6aJPvgy.js +1 -0
  223. package/dist/assets/qml-3beO22l8.js +1 -0
  224. package/dist/assets/qmldir-C8lEn-DE.js +1 -0
  225. package/dist/assets/qss-IeuSbFQv.js +1 -0
  226. package/dist/assets/r-Cf5RLm7j.js +1 -0
  227. package/dist/assets/racket-BqYA7rlc.js +1 -0
  228. package/dist/assets/raku-DXvB9xmW.js +1 -0
  229. package/dist/assets/razor-BjBPvh-w.js +1 -0
  230. package/dist/assets/rbs-CpoqiR4B.js +1 -0
  231. package/dist/assets/red-hvxz__6c.js +1 -0
  232. package/dist/assets/reg-C-SQnVFl.js +1 -0
  233. package/dist/assets/regexp-CDVJQ6XC.js +1 -0
  234. package/dist/assets/rel-C3B-1QV4.js +1 -0
  235. package/dist/assets/riscv-BM1_JUlF.js +1 -0
  236. package/dist/assets/ron-D8l8udqQ.js +1 -0
  237. package/dist/assets/rose-pine-5qJOZa0Y.js +1 -0
  238. package/dist/assets/rose-pine-dawn-zx0QlTCp.js +1 -0
  239. package/dist/assets/rose-pine-moon-C9pEdX9L.js +1 -0
  240. package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
  241. package/dist/assets/rst-bs7f0vWN.js +1 -0
  242. package/dist/assets/ruby-C0TQ7zu5.js +1 -0
  243. package/dist/assets/rust-B1yitclQ.js +1 -0
  244. package/dist/assets/sas-DEy46yEz.js +1 -0
  245. package/dist/assets/sass-Cj5Yp3dK.js +1 -0
  246. package/dist/assets/scala-CqE71os6.js +1 -0
  247. package/dist/assets/scheme-C98Dy4si.js +1 -0
  248. package/dist/assets/scss-D5BDwBP9.js +1 -0
  249. package/dist/assets/sdbl-DVxCFoDh.js +1 -0
  250. package/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
  251. package/dist/assets/shellscript-Yzrsuije.js +1 -0
  252. package/dist/assets/shellsession-BADoaaVG.js +1 -0
  253. package/dist/assets/slack-dark-BthQWCQV.js +1 -0
  254. package/dist/assets/slack-ochin-DqwNpetd.js +1 -0
  255. package/dist/assets/smalltalk-BOQMe2GC.js +1 -0
  256. package/dist/assets/smithy-cds9vsN8.js +1 -0
  257. package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
  258. package/dist/assets/solarized-dark-CpvCGNkr.js +1 -0
  259. package/dist/assets/solarized-light-Dlz6yCKv.js +1 -0
  260. package/dist/assets/solidity-DijEV5ha.js +1 -0
  261. package/dist/assets/soy-8wufbnw4.js +1 -0
  262. package/dist/assets/sparql-rVzFXLq3.js +1 -0
  263. package/dist/assets/splunk-BtCnVYZw.js +1 -0
  264. package/dist/assets/sql-CRqJ_cUM.js +1 -0
  265. package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
  266. package/dist/assets/stata-DI20mbqo.js +1 -0
  267. package/dist/assets/stylus-BEDo0Tqx.js +1 -0
  268. package/dist/assets/surrealql-Cjom0U5J.js +1 -0
  269. package/dist/assets/svelte-Cy7k_4gC.js +1 -0
  270. package/dist/assets/swift-C2oV4EkX.js +1 -0
  271. package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
  272. package/dist/assets/system-verilog-0hqHdDBg.js +1 -0
  273. package/dist/assets/systemd-4A_iFExJ.js +1 -0
  274. package/dist/assets/talonscript-CkByrt1z.js +1 -0
  275. package/dist/assets/tasl-QIJgUcNo.js +1 -0
  276. package/dist/assets/tcl-dwOrl1Do.js +1 -0
  277. package/dist/assets/templ-DhtptRzy.js +1 -0
  278. package/dist/assets/terraform-BETggiCN.js +1 -0
  279. package/dist/assets/tex-D96PA37w.js +1 -0
  280. package/dist/assets/tokyo-night-hegEt444.js +1 -0
  281. package/dist/assets/toml-vGWfd6FD.js +1 -0
  282. package/dist/assets/ts-tags-D351s5mN.js +1 -0
  283. package/dist/assets/tsv-B_m7g4N7.js +1 -0
  284. package/dist/assets/tsx-COt5Ahok.js +1 -0
  285. package/dist/assets/turtle-BsS91CYL.js +1 -0
  286. package/dist/assets/twig-27uCiNez.js +1 -0
  287. package/dist/assets/typescript-BPQ3VLAy.js +1 -0
  288. package/dist/assets/typespec-CAFt9gP4.js +1 -0
  289. package/dist/assets/typst-BUadGCkm.js +1 -0
  290. package/dist/assets/v-BGw2Nkan.js +1 -0
  291. package/dist/assets/vala-CsfeWuGM.js +1 -0
  292. package/dist/assets/vb-Cu-pLBUe.js +1 -0
  293. package/dist/assets/verilog-nZwndyjY.js +1 -0
  294. package/dist/assets/vesper-DRje8inN.js +1 -0
  295. package/dist/assets/vhdl-CeAyd5Ju.js +1 -0
  296. package/dist/assets/viml-CJc9bBzg.js +1 -0
  297. package/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
  298. package/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
  299. package/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
  300. package/dist/assets/vue-BqiEGhQt.js +1 -0
  301. package/dist/assets/vue-html-AaS7Mt5G.js +1 -0
  302. package/dist/assets/vue-vine-BoDAl6tE.js +1 -0
  303. package/dist/assets/vyper-CDx5xZoG.js +1 -0
  304. package/dist/assets/wasm-CG6Dc4jp.js +1 -0
  305. package/dist/assets/wasm-MzD3tlZU.js +1 -0
  306. package/dist/assets/wenyan-BV7otONQ.js +1 -0
  307. package/dist/assets/wgsl-Dx-B1_4e.js +1 -0
  308. package/dist/assets/wikitext-BhOHFoWU.js +1 -0
  309. package/dist/assets/wit-5i3qLPDT.js +1 -0
  310. package/dist/assets/wolfram-lXgVvXCa.js +1 -0
  311. package/dist/assets/xml-sdJ4AIDG.js +1 -0
  312. package/dist/assets/xsl-CtQFsRM5.js +1 -0
  313. package/dist/assets/yaml-Buea-lGh.js +1 -0
  314. package/dist/assets/zenscript-DVFEvuxE.js +1 -0
  315. package/dist/assets/zig-VOosw3JB.js +1 -0
  316. package/dist/index.html +14 -0
  317. package/dist-server/companion/src/listener.js +187 -0
  318. package/dist-server/computer-proxy.js +1185 -0
  319. package/dist-server/connector-proxy.js +186 -0
  320. package/dist-server/container-mcp.js +15186 -0
  321. package/dist-server/drivers/agents-proxy.js +887 -0
  322. package/dist-server/drivers/browser-proxy.js +15037 -0
  323. package/dist-server/drivers/dweb-proxy.js +175 -0
  324. package/dist-server/drivers/phone-proxy.js +339 -0
  325. package/dist-server/drivers/pi-mcp-extension.ts +638 -0
  326. package/dist-server/index.js +64869 -0
  327. package/dist-server/local-computer.js +198 -0
  328. package/dist-server/mcp-server.js +1233 -0
  329. package/dist-server/openmausbot.js +3671 -0
  330. package/dist-server/pair-cli.js +3671 -0
  331. package/dist-server/permission-proxy.js +126 -0
  332. package/dist-server/prepare-cloudflared.js +253 -0
  333. package/dist-server/proxy-paths.js +29 -0
  334. package/dist-server/server/atomic.js +40 -0
  335. package/dist-server/server/attachments.js +471 -0
  336. package/dist-server/server/auto-approve.js +162 -0
  337. package/dist-server/server/auto-review.js +79 -0
  338. package/dist-server/server/avatar-image.js +144 -0
  339. package/dist-server/server/bot-cwd.js +29 -0
  340. package/dist-server/server/bot-directory.js +127 -0
  341. package/dist-server/server/bot-package.js +268 -0
  342. package/dist-server/server/bot-profile.js +76 -0
  343. package/dist-server/server/box-create-idempotency.js +463 -0
  344. package/dist-server/server/box.js +944 -0
  345. package/dist-server/server/brand.js +65 -0
  346. package/dist-server/server/browser-connection.js +187 -0
  347. package/dist-server/server/browser-lifecycle-cleanup.js +359 -0
  348. package/dist-server/server/calendar-calls.js +314 -0
  349. package/dist-server/server/channel-queue.js +70 -0
  350. package/dist-server/server/checkpoints.js +405 -0
  351. package/dist-server/server/chief-of-staff.js +49 -0
  352. package/dist-server/server/claude-update.js +70 -0
  353. package/dist-server/server/cli.js +524 -0
  354. package/dist-server/server/cloud-backend.js +30 -0
  355. package/dist-server/server/comms-visibility.js +109 -0
  356. package/dist-server/server/composio.js +945 -0
  357. package/dist-server/server/computer-control.js +149 -0
  358. package/dist-server/server/computer-observation.js +133 -0
  359. package/dist-server/server/computer-proxy.js +1061 -0
  360. package/dist-server/server/config.js +760 -0
  361. package/dist-server/server/connector-proxy.js +228 -0
  362. package/dist-server/server/container-computer.js +840 -0
  363. package/dist-server/server/container-mcp.js +27 -0
  364. package/dist-server/server/contracts.js +24 -0
  365. package/dist-server/server/control-client.js +100 -0
  366. package/dist-server/server/data-dir-lease.js +3 -0
  367. package/dist-server/server/decision-log.js +106 -0
  368. package/dist-server/server/delegations.js +628 -0
  369. package/dist-server/server/drivers/acp/core.js +904 -0
  370. package/dist-server/server/drivers/acp/cursor.js +365 -0
  371. package/dist-server/server/drivers/acp/custom.js +51 -0
  372. package/dist-server/server/drivers/acp/droid.js +225 -0
  373. package/dist-server/server/drivers/acp/gemini.js +95 -0
  374. package/dist-server/server/drivers/acp/grok.js +249 -0
  375. package/dist-server/server/drivers/acp/hermes.js +429 -0
  376. package/dist-server/server/drivers/acp/kimi.js +559 -0
  377. package/dist-server/server/drivers/acp/opencode-go.js +370 -0
  378. package/dist-server/server/drivers/acp/qwen.js +98 -0
  379. package/dist-server/server/drivers/agents-proxy.js +941 -0
  380. package/dist-server/server/drivers/antigravity-acp.js +507 -0
  381. package/dist-server/server/drivers/antigravity-release.js +65 -0
  382. package/dist-server/server/drivers/antigravity-runtime.js +395 -0
  383. package/dist-server/server/drivers/antigravity.js +144 -0
  384. package/dist-server/server/drivers/boxagent.js +245 -0
  385. package/dist-server/server/drivers/browser-proxy.js +520 -0
  386. package/dist-server/server/drivers/builtIn.js +36 -0
  387. package/dist-server/server/drivers/claude.js +1337 -0
  388. package/dist-server/server/drivers/codex-catalog.js +409 -0
  389. package/dist-server/server/drivers/codex.js +1116 -0
  390. package/dist-server/server/drivers/dweb-proxy.js +193 -0
  391. package/dist-server/server/drivers/grok.js +48 -0
  392. package/dist-server/server/drivers/grokagent.js +4 -0
  393. package/dist-server/server/drivers/local-inject.js +388 -0
  394. package/dist-server/server/drivers/minimax.js +108 -0
  395. package/dist-server/server/drivers/native.js +21 -0
  396. package/dist-server/server/drivers/openai-chat.js +240 -0
  397. package/dist-server/server/drivers/openai-compat.js +143 -0
  398. package/dist-server/server/drivers/phone-proxy.js +372 -0
  399. package/dist-server/server/drivers/pi.js +855 -0
  400. package/dist-server/server/drivers/retry.js +96 -0
  401. package/dist-server/server/enterprise.js +93 -0
  402. package/dist-server/server/env-path.js +329 -0
  403. package/dist-server/server/environment.js +105 -0
  404. package/dist-server/server/generated-image.js +39 -0
  405. package/dist-server/server/graceful-shutdown.js +24 -0
  406. package/dist-server/server/group-goal-run.js +124 -0
  407. package/dist-server/server/harness/bus.js +86 -0
  408. package/dist-server/server/harness/registry.js +217 -0
  409. package/dist-server/server/index.js +11634 -0
  410. package/dist-server/server/installed-playbooks.js +34 -0
  411. package/dist-server/server/local-computer.js +278 -0
  412. package/dist-server/server/local-routing.js +9 -0
  413. package/dist-server/server/local-vm-idle.js +44 -0
  414. package/dist-server/server/local-vm-inventory.js +34 -0
  415. package/dist-server/server/local-vm-lease.js +58 -0
  416. package/dist-server/server/mcp-bridge.js +251 -0
  417. package/dist-server/server/mcp-probe.js +153 -0
  418. package/dist-server/server/mcp-registry.js +130 -0
  419. package/dist-server/server/member-turn.js +6 -0
  420. package/dist-server/server/message-db.js +358 -0
  421. package/dist-server/server/message-file.js +439 -0
  422. package/dist-server/server/names.js +26 -0
  423. package/dist-server/server/notify.js +69 -0
  424. package/dist-server/server/openmaus-status-capsule.js +329 -0
  425. package/dist-server/server/openmausbot.js +6 -0
  426. package/dist-server/server/package-export.js +157 -0
  427. package/dist-server/server/pair-cli.js +7 -0
  428. package/dist-server/server/peer-approval-key.js +5 -0
  429. package/dist-server/server/peer-approval.js +182 -0
  430. package/dist-server/server/peer-provenance.js +44 -0
  431. package/dist-server/server/peer-roster.js +139 -0
  432. package/dist-server/server/permission-proxy.js +157 -0
  433. package/dist-server/server/phone-secret.js +358 -0
  434. package/dist-server/server/private-screen-capture.js +17 -0
  435. package/dist-server/server/procs.js +123 -0
  436. package/dist-server/server/project-scout.js +304 -0
  437. package/dist-server/server/proxy-paths.js +47 -0
  438. package/dist-server/server/redact.js +102 -0
  439. package/dist-server/server/remote-computer.js +170 -0
  440. package/dist-server/server/repeat-detector.js +48 -0
  441. package/dist-server/server/replies.js +38 -0
  442. package/dist-server/server/request-auth.js +327 -0
  443. package/dist-server/server/room-cwd.js +15 -0
  444. package/dist-server/server/room-post-budget.js +112 -0
  445. package/dist-server/server/room-turn-timeout.js +115 -0
  446. package/dist-server/server/routine-requests.js +871 -0
  447. package/dist-server/server/routines.js +1111 -0
  448. package/dist-server/server/schema.js +11 -0
  449. package/dist-server/server/screen-frame-gate.js +80 -0
  450. package/dist-server/server/section-context.js +85 -0
  451. package/dist-server/server/send-idempotency.js +53 -0
  452. package/dist-server/server/sessions.js +319 -0
  453. package/dist-server/server/skill-fetch.js +148 -0
  454. package/dist-server/server/skill-learn.js +61 -0
  455. package/dist-server/server/skill-library.js +116 -0
  456. package/dist-server/server/skills.js +1223 -0
  457. package/dist-server/server/steer-queue.js +114 -0
  458. package/dist-server/server/store.js +1217 -0
  459. package/dist-server/server/surface.js +107 -0
  460. package/dist-server/server/tailscale.js +103 -0
  461. package/dist-server/server/team-backup.js +183 -0
  462. package/dist-server/server/team-library.js +205 -0
  463. package/dist-server/server/team-manifest.js +224 -0
  464. package/dist-server/server/thread-events.js +226 -0
  465. package/dist-server/server/tts/elevenlabs.js +92 -0
  466. package/dist-server/server/tts/index.js +79 -0
  467. package/dist-server/server/tts/speech-text.js +195 -0
  468. package/dist-server/server/tts/system-voices.js +74 -0
  469. package/dist-server/server/tunnel.js +202 -0
  470. package/dist-server/server/turn-context.js +40 -0
  471. package/dist-server/server/turn-dispatch-guard.js +152 -0
  472. package/dist-server/server/turn-images.js +138 -0
  473. package/dist-server/server/turn-watchdog.js +65 -0
  474. package/dist-server/server/vps-computer.js +1099 -0
  475. package/dist-server/server/vps-container-mcp.js +31 -0
  476. package/dist-server/server/webhook-ingress.js +173 -0
  477. package/dist-server/server/webhooks.js +491 -0
  478. package/dist-server/server/workspace.js +178 -0
  479. package/dist-server/shared/approval-mode.js +39 -0
  480. package/dist-server/shared/bot-avatar.js +33 -0
  481. package/dist-server/shared/bot-profile.js +7 -0
  482. package/dist-server/shared/connector-availability.js +12 -0
  483. package/dist-server/shared/credential-request.js +82 -0
  484. package/dist-server/shared/group-goal-run.js +1 -0
  485. package/dist-server/shared/import-name.js +11 -0
  486. package/dist-server/shared/mascot-bodies.js +107 -0
  487. package/dist-server/shared/routine-request.js +10 -0
  488. package/dist-server/shared/routine-run.js +1 -0
  489. package/dist-server/shared/skill-request.js +16 -0
  490. package/dist-server/shared/team-backup.js +148 -0
  491. package/dist-server/src/lib/composer-attachments.js +676 -0
  492. package/dist-server/tunnel-guardian.js +390 -0
  493. package/dist-server/vps-container-mcp.js +15209 -0
  494. package/package.json +32 -0
  495. package/skills/create-verification-skill/SKILL.md +65 -0
  496. package/skills/create-verification-skill/manifest.json +13 -0
  497. package/skills/phone-harness/SKILL.md +26 -0
  498. package/skills/phone-harness/agents/openai.yaml +13 -0
  499. package/skills/phone-harness/manifest.json +17 -0
@@ -0,0 +1,524 @@
1
+ // `openmausbot` on the command line: run the server anywhere and pair devices
2
+ // to it. One implementation for three homes — `npx openmausbot` (the npm
3
+ // package), `node dist-server/cli.js` (the container image) and
4
+ // `pnpm omb` (a checkout) — because scripts/bundle-server.mjs bundles this
5
+ // file next to the server.
6
+ //
7
+ // openmausbot serve [--port 8799] [--data-dir ~/.openmausbot] [--label "cab mini"]
8
+ // [--public-url https://host] [--tailscale | --tunnel] [--no-pair]
9
+ // openmausbot pair [--label "My MacBook"] [--client] [--public-url https://host]
10
+ // openmausbot sessions [revoke <id>]
11
+ // openmausbot status
12
+ // openmausbot login [--email you@example.com]
13
+ // openmausbot logout
14
+ //
15
+ // `serve` starts the server, waits for it, and prints a pairing link with a
16
+ // QR code: scan it with the phone or open it on a laptop. `--tailscale` asks
17
+ // Tailscale to terminate HTTPS for it and uses the MagicDNS name in the link.
18
+ // `--tunnel` (after `login`) serves at a public https://….openmausbot.com
19
+ // address through a Cloudflare tunnel: no domain, no proxy, no open port.
20
+ //
21
+ // This module only exports; openmausbot.ts is the entry that runs main(), so
22
+ // bundling this file into other entries (pair-cli.ts) never runs it twice.
23
+ import { spawn } from "node:child_process";
24
+ import { existsSync, readFileSync } from "node:fs";
25
+ import { homedir } from "node:os";
26
+ import { dirname, join, resolve } from "node:path";
27
+ import { createInterface } from "node:readline/promises";
28
+ import { fileURLToPath } from "node:url";
29
+ import qrcode from "qrcode-terminal";
30
+ import { explainTailscaleFailure, tailscaleServe, tailscaleServeOff, tailscaleStatus } from "./tailscale.js";
31
+ import { cleanupTunnelOrigin, createTunnelAccount, createTunnelOrigin, describeTunnelAccount, describeTunnelState, ensureCloudflared, guardianEntry, startTunnel, tunnelAccess, } from "./tunnel.js";
32
+ const HERE = dirname(fileURLToPath(import.meta.url));
33
+ const COMMANDS = ["serve", "pair", "sessions", "status", "login", "logout", "help", "--help", "-h"];
34
+ export function parseArgs(argv, env = process.env) {
35
+ const [command = "help", ...rest] = argv;
36
+ if (!COMMANDS.includes(command)) {
37
+ return { error: `unknown command "${command}"` };
38
+ }
39
+ const options = {
40
+ command: command === "--help" || command === "-h" ? "help" : command,
41
+ port: Number(env.OMB_PORT || 8799),
42
+ dataDir: env.OMB_DATA_DIR || join(homedir(), ".openmausbot"),
43
+ tailscale: false,
44
+ tunnel: false,
45
+ client: false,
46
+ pair: true,
47
+ json: false,
48
+ };
49
+ for (let i = 0; i < rest.length; i += 1) {
50
+ const arg = rest[i];
51
+ const value = () => {
52
+ const v = rest[i + 1];
53
+ if (v === undefined || v.startsWith("--"))
54
+ throw new Error(`${arg} needs a value`);
55
+ i += 1;
56
+ return v;
57
+ };
58
+ try {
59
+ if (arg === "--port")
60
+ options.port = Number(value());
61
+ else if (arg === "--data-dir")
62
+ options.dataDir = resolve(value());
63
+ else if (arg === "--label")
64
+ options.label = value();
65
+ else if (arg === "--public-url")
66
+ options.publicUrl = value().replace(/\/+$/, "");
67
+ else if (arg === "--tailscale")
68
+ options.tailscale = true;
69
+ else if (arg === "--tunnel")
70
+ options.tunnel = true;
71
+ else if (arg === "--client")
72
+ options.client = true;
73
+ else if (arg === "--no-pair")
74
+ options.pair = false;
75
+ else if (arg === "--json")
76
+ options.json = true;
77
+ else if (arg === "--email")
78
+ options.email = value();
79
+ else if (options.command === "sessions" && arg === "revoke")
80
+ options.revoke = value();
81
+ else
82
+ return { error: `unknown argument "${arg}"` };
83
+ }
84
+ catch (error) {
85
+ return { error: error instanceof Error ? error.message : String(error) };
86
+ }
87
+ }
88
+ if (!Number.isInteger(options.port) || options.port < 1 || options.port > 65_535)
89
+ return { error: "--port must be 1-65535" };
90
+ if (options.publicUrl && !/^https?:\/\//.test(options.publicUrl))
91
+ return { error: "--public-url must start with http:// or https://" };
92
+ if (options.tailscale && options.tunnel)
93
+ return { error: "choose one of --tailscale (your tailnet) and --tunnel (a public address)" };
94
+ return options;
95
+ }
96
+ export const USAGE = `openmausbot — run the server anywhere, pair devices to it
97
+
98
+ openmausbot serve [--port 8799] [--data-dir DIR] [--label NAME]
99
+ [--public-url https://host] [--tailscale | --tunnel] [--no-pair]
100
+ openmausbot pair [--label NAME] [--client] [--public-url https://host]
101
+ openmausbot sessions [revoke ID]
102
+ openmausbot status
103
+ openmausbot login [--email you@example.com]
104
+ openmausbot logout
105
+
106
+ serve starts the server and prints a pairing link + QR code
107
+ pair mints a pairing code against a running server (--client: chat only)
108
+ sessions lists paired devices; "sessions revoke ID" signs one out
109
+ status what the server says about itself
110
+ login signs this machine in to an OpenMausBot account (an emailed code)
111
+ and reserves its public address for --tunnel
112
+ logout releases that address and signs out
113
+
114
+ --tailscale serve over your tailnet: Tailscale terminates HTTPS and the
115
+ link uses this machine's MagicDNS name (needs Tailscale signed in
116
+ and HTTPS certificates enabled for the tailnet)
117
+ --tunnel serve at a public https://….openmausbot.com address through a
118
+ Cloudflare tunnel: no domain, no proxy, no open port. Run
119
+ \`openmausbot login\` once on this machine first.
120
+ `;
121
+ export function defaultIo() {
122
+ return {
123
+ log: (line) => console.log(line),
124
+ error: (line) => console.error(line),
125
+ ask: async (question) => {
126
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
127
+ try {
128
+ return await rl.question(question);
129
+ }
130
+ finally {
131
+ rl.close();
132
+ }
133
+ },
134
+ };
135
+ }
136
+ /** The version this command ships with: package.json is one level up in the
137
+ * npm package (dist-server/), the image and a checkout (server/). */
138
+ export function serverVersion(here = HERE) {
139
+ try {
140
+ const parsed = JSON.parse(readFileSync(resolve(here, "..", "package.json"), "utf8"));
141
+ const version = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "version") : undefined;
142
+ return typeof version === "string" && version ? version : "0.0.0";
143
+ }
144
+ catch {
145
+ return "0.0.0";
146
+ }
147
+ }
148
+ const message = (error) => (error instanceof Error ? error.message : String(error));
149
+ // ── talking to a running server (loopback = owner) ────────────────────
150
+ async function api(port, path, init = {}) {
151
+ const res = await fetch(`http://127.0.0.1:${port}${path}`, { method: init.method, body: init.body, headers: { "content-type": "application/json" } });
152
+ const body = await res.json().catch(() => ({}));
153
+ return { status: res.status, body };
154
+ }
155
+ async function serverUp(port) {
156
+ try {
157
+ const { status } = await api(port, "/api/health");
158
+ return status === 200;
159
+ }
160
+ catch {
161
+ return false;
162
+ }
163
+ }
164
+ /** The pairing link a device opens, rendered as text and a QR code. */
165
+ export function pairingBlock(input) {
166
+ const lines = [`pairing code: ${input.code}`, `expires: ${new Date(input.expiresAt).toLocaleTimeString()} (single use)`];
167
+ if (input.url) {
168
+ lines.push(`open or scan: ${input.url}`);
169
+ lines.push("");
170
+ lines.push(qrToString(input.url));
171
+ }
172
+ else {
173
+ lines.push(`open: /pair on the address you use for this server, and type the code`);
174
+ if (input.hint)
175
+ lines.push(` (${input.hint})`);
176
+ }
177
+ return lines.join("\n");
178
+ }
179
+ export function qrToString(text) {
180
+ let out = "";
181
+ qrcode.generate(text, { small: true }, (rendered) => {
182
+ out = rendered;
183
+ });
184
+ return out;
185
+ }
186
+ async function mintPairing(port, options) {
187
+ const request = {};
188
+ if (options.label)
189
+ request.label = options.label;
190
+ if (options.client)
191
+ request.scopes = ["client"];
192
+ const { status, body } = await api(port, "/api/auth/pairing", { method: "POST", body: JSON.stringify(request) });
193
+ if (status !== 200)
194
+ throw new Error(`server refused to mint a pairing code: ${typeof body?.error === "string" ? body.error : status}`);
195
+ const url = options.publicUrl ? `${options.publicUrl}/pair#code=${body.code}` : typeof body.url === "string" ? body.url : null;
196
+ return pairingBlock({ code: body.code, url, expiresAt: body.expiresAt, hint: typeof body.hint === "string" ? body.hint : null });
197
+ }
198
+ // ── commands ───────────────────────────────────────────────────────────
199
+ export async function runPair(options) {
200
+ if (!(await serverUp(options.port))) {
201
+ console.error(`no OpenMausBot server on http://127.0.0.1:${options.port}; start one with \`openmausbot serve\` or set OMB_PORT`);
202
+ return 1;
203
+ }
204
+ console.log(await mintPairing(options.port, { label: options.label, client: options.client, publicUrl: options.publicUrl }));
205
+ if (options.client)
206
+ console.log("(client scope: chat and approvals only; cannot change settings or pair others)");
207
+ return 0;
208
+ }
209
+ export async function runSessions(options) {
210
+ if (!(await serverUp(options.port))) {
211
+ console.error(`no OpenMausBot server on http://127.0.0.1:${options.port}`);
212
+ return 1;
213
+ }
214
+ if (options.revoke) {
215
+ const { status, body } = await api(options.port, `/api/auth/sessions/${encodeURIComponent(options.revoke)}`, { method: "DELETE" });
216
+ if (status !== 200) {
217
+ console.error(`could not revoke: ${typeof body?.error === "string" ? body.error : status}`);
218
+ return 1;
219
+ }
220
+ console.log(`revoked ${options.revoke}: that device is signed out and its stream is closed`);
221
+ return 0;
222
+ }
223
+ const { body } = await api(options.port, "/api/auth/sessions");
224
+ const sessions = Array.isArray(body?.sessions) ? body.sessions : [];
225
+ if (options.json) {
226
+ console.log(JSON.stringify(sessions, null, 2));
227
+ return 0;
228
+ }
229
+ if (!sessions.length) {
230
+ console.log("no paired devices yet: run `openmausbot pair`");
231
+ return 0;
232
+ }
233
+ console.log(formatSessions(sessions));
234
+ return 0;
235
+ }
236
+ export function formatSessions(sessions, now = Date.now()) {
237
+ const age = (ms) => {
238
+ const m = Math.max(0, Math.floor((now - ms) / 60_000));
239
+ return m < 1 ? "just now" : m < 60 ? `${m} min ago` : m < 1440 ? `${Math.round(m / 60)} h ago` : `${Math.round(m / 1440)} d ago`;
240
+ };
241
+ const rows = sessions.map((s) => [s.id, s.label || "(unnamed)", s.scopes.includes("admin") ? "admin" : "client", age(s.lastSeenAt), new Date(s.expiresAt).toISOString().slice(0, 10)]);
242
+ const head = ["id", "device", "scope", "last seen", "expires"];
243
+ const widths = head.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
244
+ const line = (r) => r.map((c, i) => c.padEnd(widths[i])).join(" ");
245
+ return [line(head), ...rows.map(line), "", "revoke one with: openmausbot sessions revoke <id>"].join("\n");
246
+ }
247
+ export async function runStatus(options, io = defaultIo()) {
248
+ let code = 0;
249
+ try {
250
+ const res = await fetch(`http://127.0.0.1:${options.port}/.well-known/openmausbot/environment`);
251
+ const body = await res.json();
252
+ io.log(options.json ? JSON.stringify(body, null, 2) : `${body.label} · OpenMausBot ${body.version} on ${body.platform} · id ${body.environmentId}`);
253
+ }
254
+ catch {
255
+ io.error(`no OpenMausBot server on http://127.0.0.1:${options.port}`);
256
+ code = 1;
257
+ }
258
+ if (!options.json) {
259
+ const account = describeTunnelAccount(createTunnelAccount({ dataDir: options.dataDir, version: serverVersion() }).credentials.read());
260
+ if (account.address)
261
+ io.log(`public address: ${account.address} (signed in as ${account.email ?? "?"}; serve it with --tunnel)`);
262
+ }
263
+ return code;
264
+ }
265
+ export async function runLogin(options, io = defaultIo()) {
266
+ const account = createTunnelAccount({ dataDir: options.dataDir, version: serverVersion() });
267
+ if (account.credentials.status === "unavailable") {
268
+ io.error(`${account.credentials.file} exists but could not be read; fix or remove it, then try again`);
269
+ return 1;
270
+ }
271
+ if (!account.controlPlane) {
272
+ io.error("OMB_CONTROL_PLANE_URL is set but is not an https address");
273
+ return 1;
274
+ }
275
+ const existing = describeTunnelAccount(account.credentials.read());
276
+ if (existing.address)
277
+ io.log(`already signed in as ${existing.email ?? "?"} (${existing.address}); signing in again refreshes it`);
278
+ const email = (options.email ?? (await io.ask("Email for your OpenMausBot account: "))).trim();
279
+ if (!email) {
280
+ io.error("an email address is needed: openmausbot login --email you@example.com");
281
+ return 1;
282
+ }
283
+ try {
284
+ await account.service.requestCode(email);
285
+ }
286
+ catch (error) {
287
+ io.error(`could not send a sign-in code: ${message(error)}`);
288
+ return 1;
289
+ }
290
+ const code = (await io.ask(`Enter the 8-digit code we emailed to ${email}: `)).trim();
291
+ let state;
292
+ try {
293
+ state = await account.service.verifyCode(email, code);
294
+ }
295
+ catch (error) {
296
+ io.error(`sign-in failed: ${message(error)}`);
297
+ return 1;
298
+ }
299
+ const signedIn = describeTunnelAccount(account.credentials.read());
300
+ if (!signedIn.address) {
301
+ io.error(`signed in, but no public address was issued${state.message ? `: ${state.message}` : ""}`);
302
+ return 1;
303
+ }
304
+ io.log(`Signed in as ${signedIn.email ?? email}.`);
305
+ io.log(`This machine's public address: ${signedIn.address}`);
306
+ io.log("Serve there with: openmausbot serve --tunnel");
307
+ return 0;
308
+ }
309
+ export async function runLogout(options, io = defaultIo()) {
310
+ const account = createTunnelAccount({ dataDir: options.dataDir, version: serverVersion() });
311
+ const before = describeTunnelAccount(account.credentials.read());
312
+ if (!before.email) {
313
+ io.log("this machine is not signed in");
314
+ return 0;
315
+ }
316
+ let state;
317
+ try {
318
+ state = await account.service.signOut();
319
+ }
320
+ catch (error) {
321
+ io.error(`sign-out failed: ${message(error)}`);
322
+ return 1;
323
+ }
324
+ const after = describeTunnelAccount(account.credentials.read());
325
+ if (after.email) {
326
+ io.error(`still signed in${state.message ? `: ${state.message}` : ""}`);
327
+ return 1;
328
+ }
329
+ io.log(`Signed out ${before.email}${before.address ? `; ${before.address} is released` : ""}.`);
330
+ return 0;
331
+ }
332
+ /** Where the server bundle lives relative to this file: next to it in the
333
+ * npm package and the image (dist-server/), or the TypeScript source in a
334
+ * checkout. */
335
+ export function serverEntry(here = HERE) {
336
+ const bundled = join(here, "index.js");
337
+ const root = resolve(here, "..");
338
+ if (existsSync(bundled)) {
339
+ const staticDir = [join(root, "dist"), join(here, "..", "ui")].find((d) => existsSync(join(d, "index.html"))) ?? null;
340
+ const skillsDir = existsSync(join(root, "skills")) ? join(root, "skills") : null;
341
+ return { command: process.execPath, args: [bundled], staticDir, skillsDir };
342
+ }
343
+ const source = join(here, "index.ts");
344
+ const staticDir = existsSync(join(root, "dist", "index.html")) ? join(root, "dist") : null;
345
+ return { command: process.execPath, args: ["--experimental-strip-types", source], staticDir, skillsDir: existsSync(join(root, "skills")) ? join(root, "skills") : null };
346
+ }
347
+ /** Everything `--tunnel` needs before the server starts, or the one reason
348
+ * it cannot have it. Fails closed: no silent fallback to a local-only server. */
349
+ async function planTunnel(options, log) {
350
+ const account = createTunnelAccount({ dataDir: options.dataDir, version: serverVersion() });
351
+ if (account.credentials.status === "unavailable")
352
+ return { error: `${account.credentials.file} exists but could not be read; fix or remove it` };
353
+ if (!describeTunnelAccount(account.credentials.read()).email) {
354
+ return { error: "no account on this machine yet: run `openmausbot login` first, then `openmausbot serve --tunnel`" };
355
+ }
356
+ // A fresh connector token when the control plane answers; the saved one otherwise.
357
+ try {
358
+ const state = await account.service.retry();
359
+ if (state.message && !tunnelAccess(account.credentials.read()))
360
+ log(`tunnel: ${state.message}`);
361
+ }
362
+ catch (error) {
363
+ log(`tunnel: control plane not reachable right now (${message(error)}); using the saved address`);
364
+ }
365
+ const access = tunnelAccess(account.credentials.read());
366
+ if (!access)
367
+ return { error: "this machine has no public address; run `openmausbot login` again" };
368
+ let binary;
369
+ try {
370
+ binary = await ensureCloudflared({ dataDir: options.dataDir, log });
371
+ }
372
+ catch (error) {
373
+ return { error: `--tunnel: ${message(error)}` };
374
+ }
375
+ const guardian = guardianEntry();
376
+ if (!guardian)
377
+ return { error: "--tunnel: the connector guardian is missing from this install" };
378
+ return { access, binary, guardian, origin: createTunnelOrigin() };
379
+ }
380
+ export async function runServe(options, log = console.log) {
381
+ if (await serverUp(options.port)) {
382
+ console.error(`something already answers on http://127.0.0.1:${options.port}; use \`openmausbot pair\` against it, or --port for a second server`);
383
+ return 1;
384
+ }
385
+ let publicUrl = options.publicUrl;
386
+ let tailscale = null;
387
+ if (options.tailscale) {
388
+ const probe = await tailscaleStatus();
389
+ if ("failure" in probe) {
390
+ console.error(`--tailscale: ${explainTailscaleFailure(probe.failure)}`);
391
+ return 1;
392
+ }
393
+ tailscale = probe.status;
394
+ }
395
+ let plan = null;
396
+ if (options.tunnel) {
397
+ const planned = await planTunnel(options, log);
398
+ if ("error" in planned) {
399
+ console.error(planned.error);
400
+ return 1;
401
+ }
402
+ plan = planned;
403
+ if (publicUrl && publicUrl !== plan.access.endpoint)
404
+ log(`note: --public-url is ignored with --tunnel; the address is ${plan.access.endpoint}`);
405
+ publicUrl = plan.access.endpoint;
406
+ }
407
+ const entry = serverEntry();
408
+ if (!entry.staticDir)
409
+ log("note: no built UI found next to the server; the API runs but browsers get no page (build with `pnpm exec vite build`)");
410
+ const env = {
411
+ ...process.env,
412
+ OMB_DATA_DIR: options.dataDir,
413
+ OMB_PORT: String(options.port),
414
+ OMB_WEBHOOK_PORT: process.env.OMB_WEBHOOK_PORT || String(options.port + 1),
415
+ };
416
+ if (entry.staticDir)
417
+ env.OMB_STATIC_DIR = entry.staticDir;
418
+ if (entry.skillsDir && !process.env.OMB_SKILLS_DIR)
419
+ env.OMB_SKILLS_DIR = entry.skillsDir;
420
+ if (options.label && !process.env.OMB_ENVIRONMENT_LABEL)
421
+ env.OMB_ENVIRONMENT_LABEL = options.label;
422
+ if (plan)
423
+ env.OMB_TUNNEL_SOCKET = plan.origin.socketPath;
424
+ if (tailscale) {
425
+ const served = await tailscaleServe(tailscale, options.port);
426
+ if ("failure" in served) {
427
+ console.error(`--tailscale: ${explainTailscaleFailure(served.failure)}`);
428
+ return 1;
429
+ }
430
+ publicUrl = served.origin;
431
+ log(`tailscale: serving https://${tailscale.dnsName} → http://127.0.0.1:${options.port} (only your tailnet can reach it)`);
432
+ }
433
+ if (publicUrl)
434
+ env.OMB_PUBLIC_URL = publicUrl;
435
+ const child = spawn(entry.command, entry.args, { env, stdio: ["ignore", "inherit", "inherit"] });
436
+ let exited = null;
437
+ child.on("exit", (code) => {
438
+ exited = code ?? 1;
439
+ });
440
+ let tunnel = null;
441
+ let stopping = null;
442
+ const stop = () => {
443
+ stopping ??= (async () => {
444
+ // The gateway stops accepting before the server it forwards to goes away.
445
+ if (tunnel)
446
+ await tunnel.stop().catch(() => undefined);
447
+ if (tailscale)
448
+ await tailscaleServeOff(tailscale).catch(() => undefined);
449
+ if (exited === null)
450
+ child.kill("SIGTERM");
451
+ if (plan)
452
+ cleanupTunnelOrigin(plan.origin);
453
+ })();
454
+ return stopping;
455
+ };
456
+ process.on("SIGINT", () => void stop());
457
+ process.on("SIGTERM", () => void stop());
458
+ const deadline = Date.now() + 60_000;
459
+ while (Date.now() < deadline && exited === null) {
460
+ if (await serverUp(options.port))
461
+ break;
462
+ await new Promise((r) => setTimeout(r, 250));
463
+ }
464
+ if (exited !== null) {
465
+ if (plan)
466
+ cleanupTunnelOrigin(plan.origin);
467
+ return exited;
468
+ }
469
+ if (!(await serverUp(options.port))) {
470
+ console.error("the server did not answer within a minute; see its output above");
471
+ await stop();
472
+ return 1;
473
+ }
474
+ if (plan && child.pid) {
475
+ tunnel = startTunnel({
476
+ dataDir: options.dataDir,
477
+ access: plan.access,
478
+ originTarget: { pid: child.pid, socketPath: plan.origin.socketPath },
479
+ binaryPath: plan.binary,
480
+ guardian: plan.guardian,
481
+ onState: (state) => log(describeTunnelState(state, plan.access.endpoint)),
482
+ });
483
+ tunnel.started.catch((error) => log(`tunnel: ${message(error)}`));
484
+ }
485
+ log("");
486
+ log(`OpenMausBot is running on http://127.0.0.1:${options.port}${publicUrl ? `, reachable at ${publicUrl}` : ""}`);
487
+ log(`data: ${options.dataDir}`);
488
+ if (options.pair) {
489
+ log("");
490
+ log(await mintPairing(options.port, { label: options.label ? `${options.label} owner` : undefined, publicUrl: publicUrl ?? undefined }));
491
+ log("");
492
+ log("another device later: openmausbot pair --label \"Kitchen iPad\"");
493
+ }
494
+ log("stop with Ctrl+C");
495
+ return await new Promise((resolveExit) => {
496
+ child.on("exit", (code) => {
497
+ void stop().finally(() => resolveExit(code ?? 0));
498
+ });
499
+ });
500
+ }
501
+ export async function main(argv = process.argv.slice(2)) {
502
+ const options = parseArgs(argv);
503
+ if ("error" in options) {
504
+ console.error(`${options.error}\n\n${USAGE}`);
505
+ return 2;
506
+ }
507
+ switch (options.command) {
508
+ case "serve":
509
+ return runServe(options);
510
+ case "pair":
511
+ return runPair(options);
512
+ case "sessions":
513
+ return runSessions(options);
514
+ case "status":
515
+ return runStatus(options);
516
+ case "login":
517
+ return runLogin(options);
518
+ case "logout":
519
+ return runLogout(options);
520
+ default:
521
+ console.log(USAGE);
522
+ return 0;
523
+ }
524
+ }
@@ -0,0 +1,30 @@
1
+ export const CLOUD_BACKEND_CHANGE_ERROR = "stop the active turn before changing the cloud backend";
2
+ export const VPS_ALIAS_CHANGE_ERROR = "stop the active VPS turn before changing the SSH config alias";
3
+ export const BOX_ACCOUNT_RESOURCES_ERROR = "remove this installation's cloud computers before changing or clearing the Box account";
4
+ export const VPS_ALIAS_RESOURCES_ERROR = "remove this installation's VPS computers before changing or clearing the SSH config alias";
5
+ export function cloudBackendChangeError(botBusy, activeVpsThread) {
6
+ return botBusy || activeVpsThread ? CLOUD_BACKEND_CHANGE_ERROR : null;
7
+ }
8
+ export function vpsAliasChangeError(currentAlias, nextAlias, activeVpsThread) {
9
+ return activeVpsThread && currentAlias !== nextAlias ? VPS_ALIAS_CHANGE_ERROR : null;
10
+ }
11
+ /** An old Box account may be detached only when it owns no local resources.
12
+ * Token rotation is safe when the replacement credential proves access to
13
+ * the exact same provider identities. */
14
+ export function boxAccountResourceChangeError(current, replacement) {
15
+ if (current.length === 0)
16
+ return null;
17
+ if (!replacement || replacement.length !== current.length)
18
+ return BOX_ACCOUNT_RESOURCES_ERROR;
19
+ const identities = (rows) => rows
20
+ .map(({ boxId, name }) => `${boxId}\u0000${name}`)
21
+ .sort();
22
+ const currentIdentities = identities(current);
23
+ const replacementIdentities = identities(replacement);
24
+ return currentIdentities.every((identity, index) => identity === replacementIdentities[index])
25
+ ? null
26
+ : BOX_ACCOUNT_RESOURCES_ERROR;
27
+ }
28
+ export function vpsAliasResourceChangeError(instanceCount) {
29
+ return instanceCount > 0 ? VPS_ALIAS_RESOURCES_ERROR : null;
30
+ }
@@ -0,0 +1,109 @@
1
+ // Bot⇄bot comms visibility: channel creation, message mirroring, and
2
+ // per-thread chips. Extracted from /api/internal/ask-bot so delegations
3
+ // (delegate_bot) and any future peer flow reuse the same UX without a copy.
4
+ import { sectionKey } from "./store.js";
5
+ /** Find or create the channel for a peer exchange. When an originating
6
+ * group is supplied and both bots are members, the exchange is mirrored
7
+ * back into that group. Otherwise the pair's DM is used (creating it if
8
+ * necessary) so 1:1-bot-thread delegations keep their historical fallback. */
9
+ export function getOrCreateChannel(store, from, target, originatingGroup) {
10
+ if (originatingGroup &&
11
+ !originatingGroup.dm &&
12
+ originatingGroup.memberIds.includes(from.id) &&
13
+ originatingGroup.memberIds.includes(target.id)) {
14
+ return originatingGroup;
15
+ }
16
+ const existing = store.dmGroup(from.id, target.id);
17
+ if (existing) {
18
+ if (sectionKey(existing.section) !== sectionKey(from.section)) {
19
+ return store.patchGroup(existing.id, { section: from.section }) ?? existing;
20
+ }
21
+ return existing;
22
+ }
23
+ return store.createGroup(`${from.name} ⇄ ${target.name}`, [from.id, target.id], true, from.section);
24
+ }
25
+ /** Whether this mirror is worth a badge, and the group frame either way.
26
+ *
27
+ * A pair channel is the bots' own coordination: the person asked ONE bot,
28
+ * and the hops behind its answer are that bot's work, not mail addressed to
29
+ * them. Badging every hop turned the sidebar, the dock and the phone into a
30
+ * running commentary on chatter nobody asked to watch. What is NOT withheld
31
+ * is the record — the messages and chips above are already written, and the
32
+ * frame still goes out so the channel keeps its place in every client's
33
+ * list. Hidden bot-to-bot talk is where a prompt injection would propagate
34
+ * unobserved; silence the alert, never the record. A shared room is the
35
+ * other case: people are reading it, so it badges as it always has. */
36
+ function markMirrored(bus, channel, notify) {
37
+ const patch = {};
38
+ if (notify ?? !channel.dm)
39
+ patch.unread = true;
40
+ bus.store.patchGroup(channel.id, patch);
41
+ }
42
+ /** Mirror `from`'s outgoing message into the channel and drop chips into
43
+ * both 1:1 threads linking to the channel. The chips are what make
44
+ * bot-to-bot turns observable — those turns cost the user tokens, and a
45
+ * hidden exchange is exactly the kind of mistake peer coordination is
46
+ * supposed to avoid. `notify` decides only whether the channel also raises
47
+ * a badge; it defaults to off for a bot⇄bot pair channel. */
48
+ export function mirrorExchange(bus, from, target, message, channel, sourceThreadId = from.threadId, notify) {
49
+ const note = (threadId, m) => {
50
+ bus.store.appendMessage(threadId, m);
51
+ return message;
52
+ };
53
+ if (channel) {
54
+ note(channel.threadId, {
55
+ role: "bot",
56
+ kind: "text",
57
+ text: message,
58
+ from: { botId: from.id, name: from.name, color: from.color },
59
+ });
60
+ }
61
+ note(sourceThreadId, {
62
+ role: "bot",
63
+ kind: "activity",
64
+ tool: { name: `Messaged @${target.name}` },
65
+ comm: channel
66
+ ? { groupId: channel.id, withBotId: target.id, withName: target.name, withColor: target.color }
67
+ : undefined,
68
+ });
69
+ note(target.threadId, {
70
+ role: "bot",
71
+ kind: "activity",
72
+ tool: { name: `Message from @${from.name}` },
73
+ comm: channel
74
+ ? { groupId: channel.id, withBotId: from.id, withName: from.name, withColor: from.color }
75
+ : undefined,
76
+ });
77
+ if (channel)
78
+ markMirrored(bus, channel, notify);
79
+ }
80
+ /** Mirror `target`'s reply into the channel so the channel stays the
81
+ * single authoritative record of the exchange. The 1:1 threads already
82
+ * carry their own chips from `mirrorExchange`. */
83
+ export function mirrorReply(bus, target, reply, channel, notify) {
84
+ if (!channel || !reply.trim())
85
+ return;
86
+ bus.store.appendMessage(channel.threadId, {
87
+ role: "bot",
88
+ kind: "text",
89
+ text: reply,
90
+ from: { botId: target.id, name: target.name, color: target.color },
91
+ });
92
+ markMirrored(bus, channel, notify);
93
+ }
94
+ /** Mirror a terminal activity note into the channel — for async handoffs
95
+ * whose terminal state is not a reply (turn failed, was stopped, or never
96
+ * started). Prior art (A2A, MCP Tasks) is unanimous that every terminal
97
+ * state of an async handoff should be visible where the human is looking,
98
+ * and the channel is that place. */
99
+ export function mirrorActivity(bus, from, channel, name, ok, notify) {
100
+ if (!channel)
101
+ return;
102
+ bus.store.appendMessage(channel.threadId, {
103
+ role: "bot",
104
+ kind: "activity",
105
+ tool: { name, ok },
106
+ from: { botId: from.id, name: from.name, color: from.color },
107
+ });
108
+ markMirrored(bus, channel, notify);
109
+ }