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,195 @@
1
+ // The spoken register — turning an agent's answer into something worth
2
+ // hearing.
3
+ //
4
+ // Agents write for a screen: fenced code, file paths, tables, link soup,
5
+ // bullet scaffolding. Read aloud verbatim that is unbearable — a 40-line
6
+ // diff becomes four minutes of punctuation, and `server/drivers/acp/core.ts`
7
+ // becomes "server slash drivers slash a c p slash core dot t s".
8
+ //
9
+ // So every string is passed through here before it reaches a voice. The
10
+ // rule is: say the prose, name the artifacts, drop the syntax. A code block
11
+ // becomes "a code block" — the user is looking at the screen if they care,
12
+ // and this is the half of the feature that decides whether it is pleasant.
13
+ //
14
+ // Pure and synchronous on purpose: it is the piece most likely to need
15
+ // tuning against real transcripts, so it stays trivially testable.
16
+ /** A fenced block becomes a mention of itself, with its language if known. */
17
+ function describeCodeBlock(fence) {
18
+ const lang = fence.trim().split(/\s+/)[0]?.replace(/[^a-z0-9+#]/gi, "") ?? "";
19
+ const spoken = {
20
+ ts: "TypeScript",
21
+ tsx: "TypeScript",
22
+ js: "JavaScript",
23
+ jsx: "JavaScript",
24
+ py: "Python",
25
+ sh: "shell",
26
+ bash: "shell",
27
+ zsh: "shell",
28
+ json: "JSON",
29
+ yml: "YAML",
30
+ yaml: "YAML",
31
+ sql: "SQL",
32
+ rs: "Rust",
33
+ go: "Go",
34
+ swift: "Swift",
35
+ diff: "diff",
36
+ };
37
+ const name = spoken[lang.toLowerCase()];
38
+ return name ? `. (a ${name} code block) ` : ". (a code block) ";
39
+ }
40
+ /** `a/b/c/file.ts` → `file.ts`. A path's directories are for the eye. */
41
+ function shortenPaths(text) {
42
+ return text.replace(/(?:[\w.@-]+\/){1,}([\w.-]+\.\w{1,6})\b/g, "$1");
43
+ }
44
+ /** Trailing punctuation that already ends a sentence. */
45
+ const ENDS_SENTENCE = /[.!?:;]\s*$/;
46
+ /**
47
+ * Markdown (or any agent output) → a line a voice can read.
48
+ *
49
+ * Not a markdown parser: a sequence of narrow, ordered replacements, which
50
+ * is both fast enough to run per token-flush and easy to reason about when
51
+ * one of them misfires on a real transcript.
52
+ */
53
+ export function speakable(input) {
54
+ if (!input)
55
+ return "";
56
+ let text = input;
57
+ // fenced code first — everything inside must survive none of the rules below
58
+ text = text.replace(/```([^\n]*)\n[\s\S]*?(?:```|$)/g, (_m, fence) => describeCodeBlock(fence));
59
+ text = text.replace(/~~~([^\n]*)\n[\s\S]*?(?:~~~|$)/g, (_m, fence) => describeCodeBlock(fence));
60
+ // images before links — the syntax differs by one character
61
+ text = text.replace(/!\[([^\]]*)\]\([^)]*\)/g, (_m, alt) => (alt ? `. (image: ${alt}) ` : ". (an image) "));
62
+ // [label](url) → label; a bare url becomes a noun rather than an alphabet soup
63
+ text = text.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
64
+ text = text.replace(/<https?:\/\/[^>\s]+>/g, " a link ");
65
+ text = text.replace(/\bhttps?:\/\/\S+/g, " a link ");
66
+ // tables: a grid is hopeless aloud, and the separator row is pure noise
67
+ text = text.replace(/^\s*\|?[\s:-]*\|[\s|:-]*$/gm, "");
68
+ text = text.replace(/^\s*\|(.+)\|\s*$/gm, (_m, row) => row
69
+ .split("|")
70
+ .map((cell) => cell.trim())
71
+ .filter(Boolean)
72
+ .join(", "));
73
+ // inline code: keep short identifiers (they carry meaning), drop long ones
74
+ text = text.replace(/`([^`\n]+)`/g, (_m, code) => (code.length <= 40 ? code : " that snippet "));
75
+ // headings become sentences so the voice pauses instead of running on
76
+ text = text.replace(/^\s{0,3}#{1,6}\s+(.*)$/gm, (_m, head) => ENDS_SENTENCE.test(head) ? head : `${head.trim()}.`);
77
+ // list scaffolding: the marker is visual, the pause is what matters
78
+ text = text.replace(/^\s*[-*+]\s+/gm, "");
79
+ text = text.replace(/^\s*\d+[.)]\s+/gm, "");
80
+ text = text.replace(/^\s*>\s?/gm, "");
81
+ text = text.replace(/^\s*(?:[-*_]\s*){3,}$/gm, "");
82
+ // emphasis / strikethrough markers
83
+ text = text.replace(/(\*\*|__)(.*?)\1/g, "$2");
84
+ text = text.replace(/(\*|_)(?=\S)(.*?)(?<=\S)\1/g, "$2");
85
+ text = text.replace(/~~(.*?)~~/g, "$1");
86
+ // checkboxes read as literal brackets otherwise
87
+ text = text.replace(/\[[ xX]\]\s*/g, "");
88
+ text = shortenPaths(text);
89
+ // emoji and the pictographic ranges: a voice either ignores them or,
90
+ // worse, announces them by name
91
+ text = text.replace(/[\u{1F000}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE00}-\u{FE0F}\u{2190}-\u{21FF}\u{2B00}-\u{2BFF}]/gu, "");
92
+ // a blank line is a paragraph break — make it an audible one
93
+ text = text.replace(/\n{2,}/g, ". ");
94
+ text = text.replace(/\n/g, ". ");
95
+ // tidy the punctuation the substitutions above inevitably pile up
96
+ text = text.replace(/\s+/g, " ");
97
+ text = text.replace(/\s+([.,!?;:])/g, "$1");
98
+ text = text.replace(/(?:\.\s*){2,}/g, ". ");
99
+ text = text.replace(/,\s*\./g, ".");
100
+ text = text.trim();
101
+ // The paragraph→". " rule turns whitespace-only input into a lone full
102
+ // stop, which a synthesizer happily renders as a click. Nothing to say
103
+ // means nothing to send.
104
+ return /[\p{L}\p{N}]/u.test(text) ? text : "";
105
+ }
106
+ /** Sentence-ish boundary: `.`/`!`/`?` followed by space, but not inside a
107
+ * decimal, an ellipsis, or a common abbreviation. */
108
+ const BOUNDARY = /(?<!\b(?:e\.g|i\.e|etc|vs|Dr|Mr|Mrs|Ms|No|approx))(?<![.\d])([.!?])(["')\]]*)\s+/g;
109
+ /**
110
+ * Split speakable text into utterances a synthesizer can start on.
111
+ *
112
+ * Both tiers want this: cloud TTS charges and buffers per request, and the
113
+ * local model generates per utterance — so the unit of work is a sentence
114
+ * either way. Very short fragments are glued onto their neighbour, because
115
+ * a synthesizer given two words produces two words of flat, contextless
116
+ * prosody.
117
+ */
118
+ export function toUtterances(input, { minChars = 12, maxChars = 320 } = {}) {
119
+ const text = speakable(input);
120
+ if (!text)
121
+ return [];
122
+ // Split on a sentinel, never on the whitespace itself: the boundary
123
+ // match consumes the trailing space, so splitting on " " would split
124
+ // every word in the text rather than every sentence.
125
+ const MARK = "\u0000";
126
+ const rough = text
127
+ .replace(BOUNDARY, `$1$2${MARK}`)
128
+ .split(MARK)
129
+ .map((s) => s.trim())
130
+ .filter(Boolean);
131
+ const out = [];
132
+ for (const piece of rough) {
133
+ // a sentence longer than the cap is broken at a clause, never mid-word
134
+ const parts = piece.length <= maxChars ? [piece] : splitLong(piece, maxChars);
135
+ for (const part of parts) {
136
+ const prev = out[out.length - 1];
137
+ if (prev && (prev.length < minChars || part.length < minChars)) {
138
+ out[out.length - 1] = `${prev} ${part}`;
139
+ }
140
+ else {
141
+ out.push(part);
142
+ }
143
+ }
144
+ }
145
+ return out;
146
+ }
147
+ function splitLong(text, maxChars) {
148
+ const out = [];
149
+ let rest = text;
150
+ while (rest.length > maxChars) {
151
+ const window = rest.slice(0, maxChars);
152
+ // prefer a clause break, then any space; never cut a word in half
153
+ const at = Math.max(window.lastIndexOf(", "), window.lastIndexOf("; "), window.lastIndexOf(" — "));
154
+ const cut = at > maxChars / 2 ? at + 1 : window.lastIndexOf(" ");
155
+ if (cut <= 0)
156
+ break;
157
+ out.push(rest.slice(0, cut).trim());
158
+ rest = rest.slice(cut).trim();
159
+ }
160
+ if (rest)
161
+ out.push(rest);
162
+ return out;
163
+ }
164
+ /** A tool-activity chip as a spoken progress line ("reading core.ts").
165
+ * Returns null for chips not worth interrupting the ear for. */
166
+ export function narrateTool(toolName) {
167
+ const name = toolName.replace(/^mcp__[^_]+__/, "").trim();
168
+ if (!name)
169
+ return null;
170
+ // the harness writes these as chips too; they are already spoken elsewhere
171
+ if (/^(auto-approved|error):/i.test(name))
172
+ return null;
173
+ const bare = name.toLowerCase();
174
+ const verbs = [
175
+ [/^(bash|shell|terminal|run_command|execute|computer_exec)$/, "running a command"],
176
+ [/^(read|read_file|view)$/, "reading a file"],
177
+ [/^(write|create_file)$/, "writing a file"],
178
+ [/^(edit|apply_patch|str_replace|multiedit)$/, "editing a file"],
179
+ [/^(grep|search|glob|find)$/, "searching"],
180
+ [/^(web_?search|websearch)$/, "searching the web"],
181
+ [/^(web_?fetch|fetch)$/, "reading a page"],
182
+ [/^screenshot$/, "looking at the screen"],
183
+ [/^(click|type_text|press_key|scroll|computer_batch)$/, "using the computer"],
184
+ [/^open_url$/, "opening a page"],
185
+ [/^list_bots$/, "checking who's around"],
186
+ [/^ask_bot$/, "asking a teammate"],
187
+ ];
188
+ for (const [pattern, phrase] of verbs) {
189
+ if (pattern.test(bare))
190
+ return phrase;
191
+ }
192
+ // an unrecognized tool still deserves a beat, but not its raw argv
193
+ const short = shortenPaths(name).slice(0, 40);
194
+ return /^[\w .:/-]+$/.test(short) ? `running ${short}` : null;
195
+ }
@@ -0,0 +1,74 @@
1
+ // Built-in macOS speech synthesis — the zero-key voice provider.
2
+ //
3
+ // Calls and spoken replies work with no ElevenLabs account by using the
4
+ // voices already installed on the Mac (`/usr/bin/say`, the same engine the
5
+ // Spoken Content pane uses). Everything about driving `say` lives in this
6
+ // file: listing voices, and turning one utterance into WAV bytes.
7
+ //
8
+ // It runs on the HARNESS for the same reason ElevenLabs does — one place to
9
+ // spawn processes, one place that owns the utterance split — and the
10
+ // renderer keeps playing opaque audio bytes, so it never learns or cares
11
+ // which provider produced them.
12
+ //
13
+ // Platform-gated: `say` is a Darwin binary. Elsewhere the provider simply
14
+ // never appears as an option, and the ElevenLabs path is unchanged.
15
+ import { execFile } from "node:child_process";
16
+ import { mkdtemp, readFile, rm } from "node:fs/promises";
17
+ import { tmpdir } from "node:os";
18
+ import { join } from "node:path";
19
+ import { promisify } from "node:util";
20
+ const SAY = "/usr/bin/say";
21
+ const execFileAsync = promisify(execFile);
22
+ export function systemVoicesAvailable(platform = process.platform) {
23
+ return platform === "darwin";
24
+ }
25
+ const defaultRun = (file, args, timeout) => execFileAsync(file, args, { timeout, maxBuffer: 4 * 1024 * 1024 });
26
+ /** Parse `say -v ?` output. Lines look like:
27
+ * `Albert en_US # Hello! My name is Albert.`
28
+ * The header above the table is localized, so anything without the
29
+ * `name lang # sample` shape is ignored rather than parsed. */
30
+ export function parseVoiceList(stdout) {
31
+ const voices = [];
32
+ for (const line of stdout.split("\n")) {
33
+ const match = /^(.+?)\s{2,}(\S+)\s+#\s*(.+)$/.exec(line.trimEnd());
34
+ if (!match)
35
+ continue;
36
+ const [, name, locale, sample] = match;
37
+ const id = name.trim();
38
+ if (!id)
39
+ continue;
40
+ voices.push({ id, label: id, description: `${locale} — ${sample.trim()}` });
41
+ }
42
+ return voices;
43
+ }
44
+ export async function listSystemVoices(run = defaultRun) {
45
+ try {
46
+ const { stdout } = await run(SAY, ["-v", "?"], 5_000);
47
+ return parseVoiceList(stdout);
48
+ }
49
+ catch {
50
+ return [];
51
+ }
52
+ }
53
+ /** Synthesize one utterance to 22 kHz mono WAV — small, and every browser
54
+ * plays it. `say` writes the container itself; no conversion step needed.
55
+ * The voice id is a system voice name; empty falls back to the system's
56
+ * own default, which is what a fresh Mac already sounds like. */
57
+ export async function synthesizeSystem(text, voiceId, run = defaultRun) {
58
+ const trimmed = text.trim();
59
+ if (!trimmed)
60
+ return { bytes: new Uint8Array(), mime: "audio/wav" };
61
+ const dir = await mkdtemp(join(tmpdir(), "openmausbot-say-"));
62
+ const out = join(dir, "utterance.wav");
63
+ try {
64
+ const args = ["-o", out, "--data-format=LEI16@22050"];
65
+ if (voiceId?.trim())
66
+ args.push("-v", voiceId.trim());
67
+ args.push(trimmed);
68
+ await run(SAY, args, 30_000);
69
+ return { bytes: new Uint8Array(await readFile(out)), mime: "audio/wav" };
70
+ }
71
+ finally {
72
+ await rm(dir, { recursive: true, force: true });
73
+ }
74
+ }
@@ -0,0 +1,202 @@
1
+ // Rung three of the hosting ladder: `openmausbot serve --tunnel` gives a
2
+ // server a public HTTPS address (https://c-<id>.openmausbot.com) with no
3
+ // domain, no proxy and no open port, through the same control plane and
4
+ // Cloudflare tunnel the desktop app already uses. Headless, so:
5
+ //
6
+ // - the account and connector credentials live in a 0600 JSON file under
7
+ // the data dir instead of the OS keychain (a server has none; a key kept
8
+ // next to the ciphertext would be theatre), and its mode is re-tightened
9
+ // on every open;
10
+ // - cloudflared is fetched once into the data dir, by the same pinned
11
+ // version + digest script the release build uses, when nothing usable is
12
+ // on the machine;
13
+ // - the tunnel's loopback gateway forwards to an IPC listener the harness
14
+ // opens for it (OMB_TUNNEL_SOCKET), so to request-auth every public
15
+ // request is "through a proxy" by construction and needs a paired
16
+ // session — the loopback bind and its owner trust are untouched.
17
+ //
18
+ // Everything network- and process-shaped is the desktop's own code
19
+ // (electron/control-plane-client.mjs, companion-account-service.mjs,
20
+ // managed-companion-tunnel.mjs and the connector guardian): one
21
+ // implementation, so a fix lands in both.
22
+ import { spawn } from "node:child_process";
23
+ import { randomUUID } from "node:crypto";
24
+ import { chmodSync, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
25
+ import { hostname } from "node:os";
26
+ import { dirname, join, resolve } from "node:path";
27
+ import { fileURLToPath } from "node:url";
28
+ import { COMPANION_ACCOUNT_EMAIL_FIELD, COMPANION_INSTALLATION_ID_FIELD, createCompanionAccountService, resolveCompanionControlPlaneURL, } from "../electron/companion-account-service.mjs";
29
+ import { cleanupCompanionOriginEndpoint, createCompanionOriginEndpoint, MANAGED_COMPANION_ORIGIN_PORT, } from "../electron/companion-origin-gateway.mjs";
30
+ import { createControlPlaneClient } from "../electron/control-plane-client.mjs";
31
+ import { createManagedCompanionTunnel, managedCompanionTunnelAccess, resolveCloudflaredBinary, } from "../electron/managed-companion-tunnel.mjs";
32
+ import { createSecureCredentialState } from "../electron/secure-credential-state.mjs";
33
+ import { writeFileAtomic } from "./atomic.js";
34
+ const HERE = dirname(fileURLToPath(import.meta.url));
35
+ export const TUNNEL_CREDENTIALS_FILE = "tunnel-account.json";
36
+ export const TUNNEL_RUNTIME_DIR = "tunnel-runtime";
37
+ function plainDocument(value) {
38
+ if (typeof value !== "object" || value === null || Array.isArray(value))
39
+ return null;
40
+ const document = {};
41
+ for (const [key, entry] of Object.entries(value))
42
+ document[key] = entry;
43
+ return document;
44
+ }
45
+ export function openTunnelCredentials(dataDir) {
46
+ const file = join(dataDir, TUNNEL_CREDENTIALS_FILE);
47
+ let initial = {};
48
+ let status = "empty";
49
+ if (existsSync(file)) {
50
+ try {
51
+ // A file someone loosened (or an old umask) is tightened, not trusted as is.
52
+ if (process.platform !== "win32" && (statSync(file).mode & 0o077) !== 0)
53
+ chmodSync(file, 0o600);
54
+ const parsed = plainDocument(JSON.parse(readFileSync(file, "utf8")));
55
+ if (!parsed)
56
+ throw new Error("not a JSON object");
57
+ initial = parsed;
58
+ status = "ok";
59
+ }
60
+ catch {
61
+ status = "unavailable";
62
+ }
63
+ }
64
+ const persist = (next) => {
65
+ mkdirSync(dataDir, { recursive: true, mode: 0o700 });
66
+ writeFileAtomic(file, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
67
+ };
68
+ const state = createSecureCredentialState(initial, persist, { writable: status !== "unavailable" });
69
+ return {
70
+ file,
71
+ status,
72
+ read: () => state.read(),
73
+ update: (derive, afterPersist) => state.update(derive, afterPersist),
74
+ };
75
+ }
76
+ function stringField(document, field) {
77
+ const value = document[field];
78
+ return typeof value === "string" && value ? value : null;
79
+ }
80
+ export function describeTunnelAccount(document) {
81
+ return {
82
+ email: stringField(document, COMPANION_ACCOUNT_EMAIL_FIELD),
83
+ address: managedCompanionTunnelAccess(document)?.endpoint ?? null,
84
+ installationId: stringField(document, COMPANION_INSTALLATION_ID_FIELD),
85
+ };
86
+ }
87
+ export function tunnelAccess(document) {
88
+ return managedCompanionTunnelAccess(document);
89
+ }
90
+ /** The control plane's platform vocabulary. */
91
+ export function platformName(platform = process.platform) {
92
+ return platform === "darwin" ? "darwin" : platform === "win32" ? "windows" : "linux";
93
+ }
94
+ /** The desktop honours OMB_CONTROL_PLANE_URL only in development builds; a
95
+ * server's operator owns its environment, so it is honoured here always and
96
+ * the caller says so in its output. */
97
+ export function createTunnelAccount(options) {
98
+ const env = options.env ?? process.env;
99
+ const credentials = openTunnelCredentials(options.dataDir);
100
+ const controlPlane = resolveCompanionControlPlaneURL({ isPackaged: true, environment: env });
101
+ const client = controlPlane ? createControlPlaneClient({ baseURL: controlPlane, fetchImpl: options.fetchImpl }) : null;
102
+ const service = createCompanionAccountService({
103
+ client,
104
+ readCredentials: () => credentials.read(),
105
+ updateCredentials: (derive, afterPersist) => credentials.update(derive, afterPersist),
106
+ identity: { name: options.machineName ?? hostname(), platform: platformName(), appVersion: options.version },
107
+ newClientInstanceId: () => randomUUID(),
108
+ });
109
+ return { service, credentials, controlPlane };
110
+ }
111
+ // ── cloudflared and the guardian: where they are, or how to get them ──────
112
+ /** OMB_CLOUDFLARED_PATH, then the copy this command downloaded into the data
113
+ * dir, then PATH. */
114
+ export function cloudflaredPath(dataDir, env = process.env) {
115
+ return resolveCloudflaredBinary({ isPackaged: false, appPath: dataDir, environment: env });
116
+ }
117
+ /** The pinned-download script: bundled beside the server in a package or
118
+ * image, the source in a checkout. */
119
+ export function prepareEntry(here = HERE) {
120
+ const bundled = join(here, "prepare-cloudflared.js");
121
+ return existsSync(bundled) ? bundled : resolve(here, "..", "scripts", "prepare-cloudflared.mjs");
122
+ }
123
+ /** The connector guardian (the process that owns the gateway and cloudflared
124
+ * and tears both down if this process dies), same rule. */
125
+ export function guardianEntry(here = HERE) {
126
+ const bundled = join(here, "tunnel-guardian.js");
127
+ if (existsSync(bundled))
128
+ return bundled;
129
+ const source = resolve(here, "..", "electron", "managed-companion-guardian.mjs");
130
+ return existsSync(source) ? source : null;
131
+ }
132
+ export async function ensureCloudflared(options) {
133
+ const env = options.env ?? process.env;
134
+ const found = cloudflaredPath(options.dataDir, env);
135
+ if (found)
136
+ return found;
137
+ options.log(`downloading cloudflared once (pinned version and digest, about 40 MB) into ${options.dataDir}`);
138
+ await new Promise((done, fail) => {
139
+ const child = spawn(process.execPath, [prepareEntry(options.here), "--current", "--root", options.dataDir], {
140
+ env,
141
+ stdio: ["ignore", "inherit", "inherit"],
142
+ });
143
+ child.on("error", fail);
144
+ child.on("exit", (code) => (code === 0 ? done() : fail(new Error(`the cloudflared download failed (exit ${code ?? "signal"})`))));
145
+ });
146
+ const staged = cloudflaredPath(options.dataDir, env);
147
+ if (!staged)
148
+ throw new Error("cloudflared was downloaded but is not where it should be; remove the data dir's dist-native folder and try again");
149
+ return staged;
150
+ }
151
+ // ── the origin: an IPC path the harness listens on for the gateway ────────
152
+ export function createTunnelOrigin() {
153
+ return createCompanionOriginEndpoint({ processId: process.pid });
154
+ }
155
+ export function cleanupTunnelOrigin(origin) {
156
+ cleanupCompanionOriginEndpoint(origin);
157
+ }
158
+ /** The gateway listens on 127.0.0.1:<originPort> (the control plane points
159
+ * the tunnel at 8812; OMB_TUNNEL_ORIGIN_PORT exists for tests) and forwards
160
+ * to the harness's IPC socket. Verification polls the public address until
161
+ * it answers as this app, then retries with backoff forever if it never does. */
162
+ export function startTunnel(options) {
163
+ const env = options.env ?? process.env;
164
+ const originPort = Number(env.OMB_TUNNEL_ORIGIN_PORT || MANAGED_COMPANION_ORIGIN_PORT);
165
+ let current = { status: "stopped", ready: false };
166
+ const tunnel = createManagedCompanionTunnel({
167
+ binaryPath: options.binaryPath,
168
+ guardianEntry: options.guardian,
169
+ runtimeRoot: join(options.dataDir, TUNNEL_RUNTIME_DIR),
170
+ originPort,
171
+ environment: env,
172
+ fetchImpl: options.fetchImpl,
173
+ onChange: (state) => {
174
+ current = state;
175
+ options.onState?.(state);
176
+ },
177
+ });
178
+ const started = tunnel.start({ endpoint: options.access.endpoint, token: options.access.token, originTarget: options.originTarget });
179
+ return {
180
+ address: options.access.endpoint,
181
+ state: () => current,
182
+ started,
183
+ stop: async () => {
184
+ await tunnel.stop();
185
+ },
186
+ };
187
+ }
188
+ /** One line per state change, for a terminal or a journal. */
189
+ export function describeTunnelState(state, address) {
190
+ switch (state.status) {
191
+ case "starting":
192
+ return "tunnel: connecting…";
193
+ case "ready":
194
+ return `tunnel: live at ${address}`;
195
+ case "retrying":
196
+ return `tunnel: ${state.error ?? "not verified yet"} retrying in ${Math.max(1, Math.round((state.retryInMs ?? 0) / 1000))}s`;
197
+ case "stopped":
198
+ return "tunnel: stopped";
199
+ default:
200
+ return `tunnel: ${state.error ?? state.status}`;
201
+ }
202
+ }
@@ -0,0 +1,40 @@
1
+ /** Does this engine need the thread replayed to it? True when a DIFFERENT
2
+ * instance ran the last turn here — a cursor of our own is not enough,
3
+ * because it only proves we once had a session covering some prefix of the
4
+ * thread; every turn another engine took since is missing from it. Tasks
5
+ * from before `lastInstanceId` existed fall back to the cursor map: a lone
6
+ * cursor that is ours means a single-engine thread we can keep resuming;
7
+ * anything else is ambiguous, and replaying is the safe side of ambiguous.
8
+ * Gated on a prior USER turn: a new bot's thread is seeded with its own
9
+ * greeting, and that alone is nothing to join. */
10
+ export function engineIsFresh(input) {
11
+ const { instanceId, lastInstanceId, resumeCursors, transcript } = input;
12
+ if (!transcript.some((m) => m.role === "user"))
13
+ return false;
14
+ if (lastInstanceId !== undefined)
15
+ return lastInstanceId !== instanceId || resumeCursors[instanceId] === undefined;
16
+ const cursorIds = Object.keys(resumeCursors);
17
+ return !(cursorIds.length === 1 && cursorIds[0] === instanceId);
18
+ }
19
+ const REWOUND_PREAMBLE = "[The user rewound this conversation (edited a message or switched to another version). Everything before this point was replaced by the following history:]";
20
+ const FRESH_PREAMBLE = "[You are joining this conversation mid-thread (the user switched this bot over to you). The conversation so far:]";
21
+ const EXTERNAL_UPDATE_PREAMBLE = "[This conversation received an update outside your provider session. The complete current history follows so you can use that update in your next response:]";
22
+ export function buildTurnContext(input) {
23
+ const { text, transcript, rewound, fresh, externallyUpdated, replaysNatively } = input;
24
+ const resume = !rewound && !fresh && !externallyUpdated;
25
+ const replay = !resume && !replaysNatively && transcript.length > 0;
26
+ if (!replay)
27
+ return { turnText: text, resume };
28
+ return {
29
+ turnText: [
30
+ rewound ? REWOUND_PREAMBLE : externallyUpdated ? EXTERNAL_UPDATE_PREAMBLE : FRESH_PREAMBLE,
31
+ "",
32
+ ...transcript.map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${m.text}`),
33
+ "",
34
+ "[Now reply to the user's latest message:]",
35
+ "",
36
+ text,
37
+ ].join("\n"),
38
+ resume,
39
+ };
40
+ }
@@ -0,0 +1,152 @@
1
+ /** Close the Stop-vs-provider-handshake race shared by direct and room turns.
2
+ * An adapter may not publish its active process until sendTurn resolves, so
3
+ * an interrupt during that await can be an honest no-op. Re-check once setup
4
+ * completes and issue a second interrupt only when cancellation won. */
5
+ export async function guardTurnDispatch(started, cancelled, stopAfterSetup) {
6
+ const value = await started;
7
+ if (!cancelled())
8
+ return { value, cancelled: false };
9
+ await stopAfterSetup();
10
+ return { value, cancelled: true };
11
+ }
12
+ /** Bounded tombstones for provider turns cancelled during asynchronous
13
+ * startup. Their late completion/session events must not settle a newer turn
14
+ * that reused the same conversation thread. */
15
+ export class RetiredTurnRegistry {
16
+ #turnIds = new Set();
17
+ #limit;
18
+ constructor(limit = 4_096) {
19
+ this.#limit = limit;
20
+ }
21
+ retire(turnId) {
22
+ this.#turnIds.add(turnId);
23
+ while (this.#turnIds.size > this.#limit) {
24
+ const oldest = this.#turnIds.values().next().value;
25
+ if (oldest === undefined)
26
+ break;
27
+ this.#turnIds.delete(oldest);
28
+ }
29
+ }
30
+ has(turnId) {
31
+ return turnId !== undefined && this.#turnIds.has(turnId);
32
+ }
33
+ }
34
+ /** Correlate an internal capability generation with its provider turn without
35
+ * leaving a bearer alive when a very fast provider completes before
36
+ * sendTurn() returns its id. Completed ids are kept in a bounded tombstone
37
+ * registry, so a late bind fails closed instead of publishing stale ownership. */
38
+ export class ProviderTurnGenerationRegistry {
39
+ #owners = new Map();
40
+ #completed;
41
+ constructor(limit = 4_096) {
42
+ this.#completed = new RetiredTurnRegistry(limit);
43
+ }
44
+ bind(threadId, generation, turnId) {
45
+ if (this.#completed.has(turnId))
46
+ return false;
47
+ this.#owners.set(turnId, { threadId, generation });
48
+ return true;
49
+ }
50
+ complete(threadId, turnId) {
51
+ this.#completed.retire(turnId);
52
+ const owner = this.#owners.get(turnId);
53
+ if (!owner || owner.threadId !== threadId)
54
+ return null;
55
+ this.#owners.delete(turnId);
56
+ return owner;
57
+ }
58
+ deleteGeneration(threadId, generation) {
59
+ for (const [turnId, owner] of this.#owners) {
60
+ if (owner.threadId === threadId && owner.generation === generation) {
61
+ this.#owners.delete(turnId);
62
+ }
63
+ }
64
+ }
65
+ clear() {
66
+ this.#owners.clear();
67
+ }
68
+ }
69
+ /** Thread gate for the narrow interval after Stop wins but before an async
70
+ * adapter returns the provider turn id that can be retired. Multiple queued
71
+ * room operations may share a thread, so ownership is reference-counted.
72
+ *
73
+ * A broken adapter is allowed to leave its sendTurn promise pending forever.
74
+ * The gate therefore expires each owner independently: by then any turn id
75
+ * observed during the vulnerable handshake has been moved to the longer-lived
76
+ * RetiredTurnRegistry, while a replacement turn's new id can flow normally. */
77
+ export class PendingTurnCancellations {
78
+ #ownersByThread = new Map();
79
+ #clearWaitersByThread = new Map();
80
+ #ttlMs;
81
+ constructor(ttlMs = 30_000) {
82
+ this.#ttlMs = Math.max(1, Math.floor(ttlMs));
83
+ }
84
+ mark(threadId, ownerId) {
85
+ const owners = this.#ownersByThread.get(threadId) ?? new Map();
86
+ const previous = owners.get(ownerId);
87
+ if (previous)
88
+ clearTimeout(previous);
89
+ const expiry = setTimeout(() => {
90
+ // A clear + re-mark may have installed a newer timer for the same
91
+ // owner. The stale callback must not clear that renewed quarantine.
92
+ if (this.#ownersByThread.get(threadId)?.get(ownerId) !== expiry)
93
+ return;
94
+ this.clear(threadId, ownerId);
95
+ }, this.#ttlMs);
96
+ expiry.unref?.();
97
+ owners.set(ownerId, expiry);
98
+ this.#ownersByThread.set(threadId, owners);
99
+ }
100
+ clear(threadId, ownerId) {
101
+ const owners = this.#ownersByThread.get(threadId);
102
+ const expiry = owners?.get(ownerId);
103
+ if (expiry)
104
+ clearTimeout(expiry);
105
+ owners?.delete(ownerId);
106
+ if (owners?.size !== 0)
107
+ return;
108
+ this.#ownersByThread.delete(threadId);
109
+ const waiters = this.#clearWaitersByThread.get(threadId);
110
+ this.#clearWaitersByThread.delete(threadId);
111
+ for (const resolve of waiters ?? [])
112
+ resolve();
113
+ }
114
+ /** Do not overlap a replacement dispatch with the ambiguous pre-id window.
115
+ * Re-check after every wake: a cancellation can be renewed in the same
116
+ * microtask turn that clears an older owner. Each owner has its own bounded
117
+ * expiry, so a broken adapter cannot strand this await forever. */
118
+ async waitForClear(threadId) {
119
+ while (this.has(threadId)) {
120
+ await new Promise((resolve) => {
121
+ const waiters = this.#clearWaitersByThread.get(threadId) ?? new Set();
122
+ waiters.add(resolve);
123
+ this.#clearWaitersByThread.set(threadId, waiters);
124
+ // clear() cannot interleave with this synchronous block, but keeping
125
+ // the second check makes the registration safe if the implementation
126
+ // later gains an externally supplied scheduler.
127
+ if (!this.has(threadId)) {
128
+ waiters.delete(resolve);
129
+ if (waiters.size === 0)
130
+ this.#clearWaitersByThread.delete(threadId);
131
+ resolve();
132
+ }
133
+ });
134
+ }
135
+ }
136
+ has(threadId) {
137
+ return this.#ownersByThread.has(threadId);
138
+ }
139
+ }
140
+ /** Apply the two-stage cancellation quarantine. While the unresolved
141
+ * handshake owns the thread, every event is ignored and any stable provider
142
+ * turn id it reveals is retired. After the bounded gate expires, that old id
143
+ * remains ignored but an unrelated replacement id is admitted. */
144
+ export function isTurnEventQuarantined(pending, retired, event) {
145
+ if (retired.has(event.turnId))
146
+ return true;
147
+ if (!pending.has(event.threadId))
148
+ return false;
149
+ if (event.turnId)
150
+ retired.retire(event.turnId);
151
+ return true;
152
+ }