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,676 @@
1
+ export function isAttachment(value) {
2
+ if (!value || typeof value !== "object")
3
+ return false;
4
+ const attachment = value;
5
+ if (typeof attachment.id !== "string" || !validSize(attachment.size))
6
+ return false;
7
+ if (attachment.kind === "paste") {
8
+ return (typeof attachment.text === "string" &&
9
+ typeof attachment.lines === "number" &&
10
+ Number.isInteger(attachment.lines) &&
11
+ attachment.lines >= 1);
12
+ }
13
+ if (attachment.kind === "file") {
14
+ return (typeof attachment.path === "string" &&
15
+ attachment.path.length > 0 &&
16
+ typeof attachment.name === "string");
17
+ }
18
+ if (attachment.kind === "image") {
19
+ return (typeof attachment.path === "string" &&
20
+ attachment.path.length > 0 &&
21
+ typeof attachment.name === "string" &&
22
+ typeof attachment.mime === "string" &&
23
+ attachment.mime.startsWith("image/"));
24
+ }
25
+ return false;
26
+ }
27
+ function validSize(value) {
28
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
29
+ }
30
+ /** Past this, a paste stops reading as typing and becomes an attachment.
31
+ * Long-but-narrow (a stack trace, a log) counts by line, not just chars. */
32
+ export const PASTE_CHARS = 900;
33
+ export const PASTE_LINES = 12;
34
+ export function isLongPaste(text) {
35
+ return text.length >= PASTE_CHARS || countLines(text) >= PASTE_LINES;
36
+ }
37
+ export function countLines(text) {
38
+ return text.split("\n").length;
39
+ }
40
+ function newId() {
41
+ return globalThis.crypto?.randomUUID?.() ?? `a${Math.random().toString(36).slice(2)}`;
42
+ }
43
+ /** The upload id is a server-validated UUID and remains stable across the
44
+ * one recovery attempt. That makes a lost success response safe to retry:
45
+ * the server returns the already committed attachment instead of creating a
46
+ * second file. */
47
+ function newUploadId() {
48
+ if (globalThis.crypto?.randomUUID)
49
+ return globalThis.crypto.randomUUID();
50
+ const bytes = new Uint8Array(16);
51
+ if (globalThis.crypto?.getRandomValues) {
52
+ globalThis.crypto.getRandomValues(bytes);
53
+ }
54
+ else {
55
+ for (let index = 0; index < bytes.length; index += 1) {
56
+ bytes[index] = Math.floor(Math.random() * 256);
57
+ }
58
+ }
59
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
60
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
61
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
62
+ return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
63
+ }
64
+ export function fileAttachment(name, path, size) {
65
+ return { kind: "file", id: newId(), path, name, size };
66
+ }
67
+ /** Matches the server's IMAGE_MAX_BYTES — checked client-side so an
68
+ * oversized paste is refused before the upload starts, not mid-stream. */
69
+ export const IMAGE_MAX_BYTES = 10 * 1024 * 1024;
70
+ export const FILE_MAX_BYTES = 25 * 1024 * 1024;
71
+ const DOCUMENT_MIMES = {
72
+ txt: "text/plain",
73
+ md: "text/markdown",
74
+ markdown: "text/markdown",
75
+ csv: "text/csv",
76
+ tsv: "text/tab-separated-values",
77
+ json: "application/json",
78
+ pdf: "application/pdf",
79
+ rtf: "application/rtf",
80
+ doc: "application/msword",
81
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
82
+ xls: "application/vnd.ms-excel",
83
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
84
+ ppt: "application/vnd.ms-powerpoint",
85
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
86
+ odt: "application/vnd.oasis.opendocument.text",
87
+ ods: "application/vnd.oasis.opendocument.spreadsheet",
88
+ odp: "application/vnd.oasis.opendocument.presentation",
89
+ };
90
+ const ACCEPTED_DOCUMENT_MIMES = new Set(Object.values(DOCUMENT_MIMES));
91
+ export function documentMime(file) {
92
+ const declared = file.type.split(";", 1)[0].trim().toLowerCase();
93
+ if (ACCEPTED_DOCUMENT_MIMES.has(declared))
94
+ return declared;
95
+ const extension = file.name.split(".").at(-1)?.toLowerCase() ?? "";
96
+ return DOCUMENT_MIMES[extension] ?? null;
97
+ }
98
+ /**
99
+ * Checks if a given file or descriptor has a supported image MIME type.
100
+ *
101
+ * @param file - Object with a MIME `type` and optional `size`.
102
+ * @returns `true` if the file is a supported image format (PNG, JPEG, GIF, WebP).
103
+ */
104
+ export function isImageFile(file) {
105
+ return (file.type.startsWith("image/") &&
106
+ ["image/png", "image/jpeg", "image/gif", "image/webp"].includes(file.type.split(";")[0].trim().toLowerCase()));
107
+ }
108
+ /**
109
+ * Extracts valid image files from clipboard data, checking items first because
110
+ * Chromium on macOS exposes screenshots and copied image bitmaps via items
111
+ * while clipboardData.files may remain empty.
112
+ *
113
+ * @param clipboardData - The clipboard DataTransfer object or mock data.
114
+ * @returns Array of valid image File objects found in the clipboard.
115
+ */
116
+ export function clipboardImageFiles(clipboardData) {
117
+ if (!clipboardData)
118
+ return [];
119
+ if (clipboardData.items) {
120
+ const fromItems = [];
121
+ for (const item of clipboardData.items) {
122
+ if (item.kind === "file" && item.type.startsWith("image/")) {
123
+ const file = item.getAsFile();
124
+ if (file && isImageFile(file)) {
125
+ fromItems.push(file);
126
+ }
127
+ }
128
+ }
129
+ if (fromItems.length > 0)
130
+ return fromItems;
131
+ }
132
+ if (clipboardData.files) {
133
+ return Array.from(clipboardData.files).filter(isImageFile);
134
+ }
135
+ return [];
136
+ }
137
+ /**
138
+ * Detects if the clipboard data contains any image items or files.
139
+ *
140
+ * @param clipboardData - The clipboard DataTransfer object or mock data.
141
+ * @returns `true` if any image file or item is present in the clipboard.
142
+ */
143
+ export function clipboardHasImages(clipboardData) {
144
+ if (!clipboardData)
145
+ return false;
146
+ if (clipboardData.items) {
147
+ for (const item of clipboardData.items) {
148
+ if (item.kind === "file" && item.type.startsWith("image/")) {
149
+ return true;
150
+ }
151
+ }
152
+ }
153
+ if (clipboardData.files) {
154
+ for (const file of clipboardData.files) {
155
+ if (isImageFile(file))
156
+ return true;
157
+ }
158
+ }
159
+ return false;
160
+ }
161
+ const IMAGE_EXTENSION_BY_MIME = {
162
+ "image/png": ".png",
163
+ "image/jpeg": ".jpg",
164
+ "image/gif": ".gif",
165
+ "image/webp": ".webp",
166
+ };
167
+ const UPLOAD_ATTEMPTS = 2;
168
+ function retryableUploadStatus(status) {
169
+ return status === 408 || status === 425 || status === 429 || status >= 500;
170
+ }
171
+ /** Retry only failures where the server may already have committed the body.
172
+ * A validation/authentication response is final, while a network failure or
173
+ * transient response gets one replay with the same upload id. */
174
+ async function uploadWithRetry(url, init) {
175
+ for (let attempt = 0; attempt < UPLOAD_ATTEMPTS; attempt += 1) {
176
+ let response;
177
+ try {
178
+ response = await fetch(url, init);
179
+ }
180
+ catch (error) {
181
+ const aborted = typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
182
+ if (aborted || attempt === UPLOAD_ATTEMPTS - 1)
183
+ throw error;
184
+ continue;
185
+ }
186
+ if (!response.ok) {
187
+ if (retryableUploadStatus(response.status) && attempt < UPLOAD_ATTEMPTS - 1) {
188
+ await response.body?.cancel().catch(() => undefined);
189
+ continue;
190
+ }
191
+ const detail = (await response.json().catch(() => ({ error: response.statusText })));
192
+ throw Object.assign(new Error(detail.error ?? "upload failed"), { status: response.status });
193
+ }
194
+ try {
195
+ return await response.json();
196
+ }
197
+ catch (error) {
198
+ if (attempt === UPLOAD_ATTEMPTS - 1) {
199
+ throw new Error("upload returned an invalid response", { cause: error });
200
+ }
201
+ }
202
+ }
203
+ throw new Error("upload failed");
204
+ }
205
+ function canonicalImageName(originalName, path, mime) {
206
+ const normalizedMime = mime.split(";", 1)[0].trim().toLowerCase();
207
+ const extension = IMAGE_EXTENSION_BY_MIME[normalizedMime];
208
+ const pathName = attachmentBasename(path);
209
+ if (!extension || !pathName.toLowerCase().endsWith(extension)) {
210
+ throw new Error("upload returned invalid image metadata");
211
+ }
212
+ const original = attachmentBasename(originalName.trim());
213
+ const dot = original.lastIndexOf(".");
214
+ const stem = (dot > 0 ? original.slice(0, dot) : original).trim() || "pasted image";
215
+ return `${stem}${extension}`;
216
+ }
217
+ /** Persist a pasted image server-side and return the attachment chip data.
218
+ * The server writes ~/.openmausbot/attachments/<uuid>.<ext> and answers
219
+ * with the path; the prompt references that path so every CLI can open it. */
220
+ export function optimisticImageAttachment(file) {
221
+ if (!isImageFile(file))
222
+ return null;
223
+ if (file.size > IMAGE_MAX_BYTES)
224
+ throw Object.assign(new Error(`${file.name} exceeds 10 MB`), { status: 413 });
225
+ const mime = file.type.split(";", 1)[0].trim().toLowerCase();
226
+ const previewUrl = typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : undefined;
227
+ return {
228
+ kind: "image",
229
+ id: newId(),
230
+ path: "",
231
+ name: file.name || "image",
232
+ size: file.size,
233
+ mime,
234
+ ...(previewUrl ? { previewUrl } : {}),
235
+ uploading: true,
236
+ };
237
+ }
238
+ export async function imageAttachmentFromFile(file, optimistic) {
239
+ if (!isImageFile(file))
240
+ return null;
241
+ if (file.size > IMAGE_MAX_BYTES)
242
+ throw Object.assign(new Error(`${file.name} exceeds 10 MB`), { status: 413 });
243
+ const bytes = new Uint8Array(await file.arrayBuffer());
244
+ const uploadId = newUploadId();
245
+ const saved = await uploadWithRetry(`/api/attachments?uploadId=${encodeURIComponent(uploadId)}`, {
246
+ method: "POST",
247
+ headers: { "content-type": file.type },
248
+ body: bytes,
249
+ });
250
+ return {
251
+ kind: "image",
252
+ id: optimistic?.id ?? newId(),
253
+ path: saved.path,
254
+ name: canonicalImageName(file.name, saved.path, saved.mime),
255
+ size: saved.bytes,
256
+ mime: saved.mime,
257
+ ...(optimistic?.previewUrl ? { previewUrl: optimistic.previewUrl } : {}),
258
+ };
259
+ }
260
+ /** Copy a supported document into the private attachment store. The prompt
261
+ * then carries the same durable path for the local agent and paired phones,
262
+ * instead of exposing an arbitrary Finder path to the companion route. */
263
+ export async function fileAttachmentFromFile(file) {
264
+ const mime = documentMime(file);
265
+ if (!mime)
266
+ return null;
267
+ if (file.size > FILE_MAX_BYTES) {
268
+ throw Object.assign(new Error(`${file.name} exceeds 25 MB`), { status: 413 });
269
+ }
270
+ const uploadId = newUploadId();
271
+ const saved = await uploadWithRetry(`/api/files?name=${encodeURIComponent(file.name)}&uploadId=${encodeURIComponent(uploadId)}`, {
272
+ method: "POST",
273
+ headers: { "content-type": mime },
274
+ body: file,
275
+ });
276
+ return fileAttachment(saved.name || file.name, saved.path, saved.bytes);
277
+ }
278
+ export function pasteAttachment(text) {
279
+ const id = newId();
280
+ // measured once, here: a chip re-renders on every keystroke in the
281
+ // composer, and encoding half a megabyte each time would be felt
282
+ return { kind: "paste", id, text, size: byteLength(text), lines: countLines(text) };
283
+ }
284
+ /** Move a pasted attachment into the editable composer draft without
285
+ * running the text back through the paste threshold. */
286
+ export function appendPastedText(text, pasted) {
287
+ if (!text)
288
+ return pasted;
289
+ return `${text}${text.endsWith("\n") ? "" : "\n\n"}${pasted}`;
290
+ }
291
+ export const INLINE_DROP_LIMIT = 512 * 1024;
292
+ /** Turn a browser drop into composer attachments. Electron-backed files
293
+ * keep their disk path; small pathless text drops keep their contents.
294
+ * Promise.all preserves the user's drop order even when text reads finish
295
+ * in a different order. */
296
+ export async function attachmentsFromDroppedFiles(files, getPath) {
297
+ const results = await Promise.all(files.map(async (file) => {
298
+ let path = "";
299
+ try {
300
+ path = getPath(file);
301
+ }
302
+ catch {
303
+ // A browser or older desktop shell has no disk path to expose.
304
+ }
305
+ if (path)
306
+ return { attachment: fileAttachment(file.name, path, file.size) };
307
+ if (isInlineText(file) && file.size <= INLINE_DROP_LIMIT) {
308
+ try {
309
+ return { attachment: pasteAttachment(await file.text()) };
310
+ }
311
+ catch {
312
+ // Treat an unreadable browser drag like any other pathless file.
313
+ }
314
+ }
315
+ return { rejectedName: file.name };
316
+ }));
317
+ return {
318
+ attachments: results.flatMap((result) => "attachment" in result && result.attachment ? [result.attachment] : []),
319
+ rejectedNames: results.flatMap((result) => "rejectedName" in result && result.rejectedName ? [result.rejectedName] : []),
320
+ };
321
+ }
322
+ function isInlineText(file) {
323
+ return file.type.startsWith("text/") || file.type === "application/json";
324
+ }
325
+ /** What the paste actually weighs — String#length counts UTF-16 units, so
326
+ * it reads a third under on accented text and half under on CJK. */
327
+ export function byteLength(text) {
328
+ return new TextEncoder().encode(text).length;
329
+ }
330
+ /** "12 lines, 3.4 KB" — what the chip says under the preview. */
331
+ export function pasteSummary(a) {
332
+ return `${a.lines} lines, ${formatSize(a.size)}`;
333
+ }
334
+ export function formatSize(bytes) {
335
+ if (bytes < 1024)
336
+ return `${bytes} B`;
337
+ if (bytes < 1024 * 1024)
338
+ return `${(bytes / 1024).toFixed(1)} KB`;
339
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
340
+ }
341
+ /** The prompt the bot receives: what was typed, then one block per
342
+ * attachment. Tagged blocks rather than fences — pasted code and markdown
343
+ * carry fences of their own, and nesting them loses the boundary. A file
344
+ * carries its path for the agent and its original name for the transcript. */
345
+ export function composeMessage(text, attachments) {
346
+ const parts = [text.trim()];
347
+ attachments.forEach((a, i) => {
348
+ if (a.kind === "paste") {
349
+ parts.push(`<pasted-text index="${i + 1}">\n${a.text}\n</pasted-text>`);
350
+ }
351
+ else if (a.kind === "image") {
352
+ parts.push(`<attached-image path="${escapeAttribute(a.path)}" name="${escapeAttribute(a.name)}" />`);
353
+ }
354
+ else {
355
+ parts.push(`<attached-file path="${escapeAttribute(a.path)}" name="${escapeAttribute(a.name)}" />`);
356
+ }
357
+ });
358
+ return parts.filter(Boolean).join("\n\n");
359
+ }
360
+ /** File paths are untrusted prompt content. Keep them inside the quoted
361
+ * attribute even when a filename contains XML characters or line breaks. */
362
+ export function escapeAttribute(value) {
363
+ return value
364
+ .replaceAll("&", "&amp;")
365
+ .replaceAll('"', "&quot;")
366
+ .replaceAll("<", "&lt;")
367
+ .replaceAll(">", "&gt;")
368
+ .replaceAll("\t", "&#9;")
369
+ .replaceAll("\r", "&#13;")
370
+ .replaceAll("\n", "&#10;");
371
+ }
372
+ /** Decode only entities emitted by escapeAttribute. A second encoded pass
373
+ * stays encoded, rather than turning attacker-controlled text into markup. */
374
+ function decodeAttachmentAttribute(value) {
375
+ return value.replace(/&(quot|lt|gt|amp);|&#(9|10|13);/g, (entity, named, numeric) => {
376
+ if (numeric === "9")
377
+ return "\t";
378
+ if (numeric === "10")
379
+ return "\n";
380
+ if (numeric === "13")
381
+ return "\r";
382
+ if (named === "quot")
383
+ return '"';
384
+ if (named === "lt")
385
+ return "<";
386
+ if (named === "gt")
387
+ return ">";
388
+ if (named === "amp")
389
+ return "&";
390
+ return entity;
391
+ });
392
+ }
393
+ /** Keep transcript-provided names compact and visually honest. File chips
394
+ * are deliberately not links, but control and bidi characters can still
395
+ * make an untrusted name misleading. */
396
+ function transcriptFileName(path, suppliedName) {
397
+ const decoded = suppliedName ? decodeAttachmentAttribute(suppliedName) : attachmentBasename(path);
398
+ const clean = (value) => Array.from(value, (character) => {
399
+ const code = character.codePointAt(0) ?? 0;
400
+ const control = code <= 31 || (code >= 127 && code <= 159);
401
+ const bidiControl = (code >= 0x202a && code <= 0x202e) || (code >= 0x2066 && code <= 0x2069);
402
+ return control || bidiControl ? " " : character;
403
+ }).join("").replace(/\s+/g, " ").trim();
404
+ const safe = clean(attachmentBasename(decoded));
405
+ const fallback = clean(attachmentBasename(path));
406
+ return Array.from(safe || fallback || "Attached file").slice(0, 180).join("");
407
+ }
408
+ export function isPrivateAttachmentPath(path) {
409
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.[a-z0-9]+$/i
410
+ .test(attachmentBasename(path));
411
+ }
412
+ /** Recognise CommonMark-style fenced code without pulling a Markdown parser
413
+ * into the composer bundle. An unterminated fence deliberately protects the
414
+ * rest of the message: examples must never turn into actionable attachments. */
415
+ function transcriptFenceMarker(line) {
416
+ let index = 0;
417
+ while (index < line.length && index < 4 && line[index] === " ")
418
+ index += 1;
419
+ if (index > 3)
420
+ return null;
421
+ const marker = line[index];
422
+ if (marker !== "`" && marker !== "~")
423
+ return null;
424
+ const start = index;
425
+ while (index < line.length && line[index] === marker)
426
+ index += 1;
427
+ const length = index - start;
428
+ if (length < 3)
429
+ return null;
430
+ return { marker, length, remainder: line.slice(index) };
431
+ }
432
+ const COMMONMARK_BLOCK_TAGS = [
433
+ "address", "article", "aside", "base", "basefont", "blockquote", "body", "caption", "center", "col",
434
+ "colgroup", "dd", "details", "dialog", "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure",
435
+ "footer", "form", "frame", "frameset", "h[1-6]", "head", "header", "hr", "html", "iframe", "legend",
436
+ "li", "link", "main", "menu", "menuitem", "nav", "noframes", "ol", "optgroup", "option", "p",
437
+ "param", "search", "section", "summary", "table", "tbody", "td", "tfoot", "th", "thead", "title",
438
+ "tr", "track", "ul",
439
+ ].join("|");
440
+ const COMMONMARK_TYPE_1 = /^ {0,3}<(script|pre|style|textarea)(?:[\t ]|>|$)/i;
441
+ const COMMONMARK_TYPE_6 = new RegExp(`^ {0,3}</?(?:${COMMONMARK_BLOCK_TAGS})(?:[\\t ]|/?>|$)`, "i");
442
+ const HTML_ATTRIBUTE_NAME = "[A-Za-z_:][A-Za-z0-9_.:-]*";
443
+ const HTML_ATTRIBUTE_VALUE = `(?:[^\\s"'=<>\\x60]+|'[^']*'|"[^"]*")`;
444
+ const HTML_ATTRIBUTE = `(?:[\\t ]+${HTML_ATTRIBUTE_NAME}(?:[\\t ]*=[\\t ]*${HTML_ATTRIBUTE_VALUE})?)`;
445
+ const COMMONMARK_TYPE_7 = new RegExp(`^ {0,3}(?:<[A-Za-z][A-Za-z0-9-]*${HTML_ATTRIBUTE}*[\\t ]*/?>|</[A-Za-z][A-Za-z0-9-]*[\\t ]*>)[\\t ]*$`);
446
+ /** Attachment-looking examples inside CommonMark HTML blocks stay literal.
447
+ * Types 1-5 use their specified terminator; types 6-7 last through the next
448
+ * blank line. The app's pasted-text wrapper is deliberately stronger than a
449
+ * generic custom tag and lasts through its closing tag, including blanks. */
450
+ function transcriptBlockStarting(line) {
451
+ const lower = line.toLowerCase();
452
+ const commentStart = lower.indexOf("<!--");
453
+ if (commentStart >= 0 && lower.indexOf("-->", commentStart + 4) < 0) {
454
+ return { kind: "untilToken", closingToken: "-->" };
455
+ }
456
+ const content = line.match(/^ {0,3}(.*)$/)?.[1];
457
+ if (content === undefined)
458
+ return null;
459
+ const lowerContent = content.toLowerCase();
460
+ const pastedText = /^<pasted-text(?:[\t >]|$)/i.exec(content);
461
+ if (pastedText) {
462
+ return lowerContent.includes("</pasted-text>")
463
+ ? null
464
+ : { kind: "untilToken", closingToken: "</pasted-text>" };
465
+ }
466
+ const typeOne = COMMONMARK_TYPE_1.exec(line);
467
+ if (typeOne) {
468
+ const closingToken = `</${typeOne[1].toLowerCase()}>`;
469
+ return lower.includes(closingToken)
470
+ ? null
471
+ : { kind: "untilToken", closingToken };
472
+ }
473
+ const processing = lowerContent.indexOf("<?");
474
+ if (processing === 0) {
475
+ return lowerContent.indexOf("?>", 2) >= 0
476
+ ? null
477
+ : { kind: "untilToken", closingToken: "?>" };
478
+ }
479
+ const cdata = lowerContent.indexOf("<![cdata[");
480
+ if (cdata === 0) {
481
+ return lowerContent.indexOf("]]>", 9) >= 0
482
+ ? null
483
+ : { kind: "untilToken", closingToken: "]]>" };
484
+ }
485
+ if (/^<![A-Za-z]/.test(content)) {
486
+ return content.indexOf(">", 2) >= 0
487
+ ? null
488
+ : { kind: "untilToken", closingToken: ">" };
489
+ }
490
+ if (COMMONMARK_TYPE_6.test(line) || COMMONMARK_TYPE_7.test(line)) {
491
+ return { kind: "untilBlank" };
492
+ }
493
+ return null;
494
+ }
495
+ const TRANSCRIPT_ATTACHMENT_TAG = /^<attached-(image|file)[\t ]+path="([^"\r\n]*)"(?:[\t ]+name="([^"\r\n]*)")?[\t ]*\/>[\t ]*$/;
496
+ /** Split a stored user message into its display text and attachments for
497
+ * transcript rendering. Prompt-only tags never show in the bubble. */
498
+ export function splitTranscriptAttachments(text) {
499
+ const images = [];
500
+ const files = [];
501
+ let display = "";
502
+ let fence = null;
503
+ let block = null;
504
+ let cursor = 0;
505
+ while (cursor < text.length) {
506
+ const newline = text.indexOf("\n", cursor);
507
+ const lineEnd = newline >= 0 ? newline : text.length;
508
+ const wholeLineEnd = newline >= 0 ? newline + 1 : text.length;
509
+ const rawLine = text.slice(cursor, lineEnd);
510
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
511
+ const marker = transcriptFenceMarker(line);
512
+ let consumed = false;
513
+ if (fence) {
514
+ if (marker &&
515
+ marker.marker === fence.marker &&
516
+ marker.length >= fence.length &&
517
+ /^[\t ]*$/.test(marker.remainder)) {
518
+ fence = null;
519
+ }
520
+ }
521
+ else if (block) {
522
+ if (block.kind === "untilBlank") {
523
+ if (/^[\t ]*$/.test(line))
524
+ block = null;
525
+ }
526
+ else if (line.toLowerCase().includes(block.closingToken)) {
527
+ block = null;
528
+ }
529
+ }
530
+ else if (marker) {
531
+ fence = { marker: marker.marker, length: marker.length };
532
+ }
533
+ else {
534
+ const match = TRANSCRIPT_ATTACHMENT_TAG.exec(line);
535
+ if (match) {
536
+ const kind = match[1];
537
+ const path = decodeAttachmentAttribute(match[2]);
538
+ if (path) {
539
+ const attachment = {
540
+ path,
541
+ name: transcriptFileName(path, match[3]),
542
+ ...(isPrivateAttachmentPath(path) ? { private: true } : {}),
543
+ };
544
+ if (kind === "image")
545
+ images.push(attachment);
546
+ else
547
+ files.push(attachment);
548
+ consumed = true;
549
+ }
550
+ }
551
+ if (!consumed)
552
+ block = transcriptBlockStarting(line);
553
+ }
554
+ if (!consumed)
555
+ display += text.slice(cursor, wholeLineEnd);
556
+ cursor = wholeLineEnd;
557
+ }
558
+ return { display: display.trim(), images, files };
559
+ }
560
+ /** Kept for callers outside the desktop bundle that used the old helper. */
561
+ export function splitAttachedImages(text) {
562
+ const { display, images } = splitTranscriptAttachments(text);
563
+ return { display, images: images.map((image) => image.path) };
564
+ }
565
+ /** The bare filename a saved attachment path ends in — what the serving
566
+ * route expects. Works for POSIX and Windows separators. */
567
+ export function attachmentBasename(path) {
568
+ const parts = path.split(/[\\/]/);
569
+ return parts[parts.length - 1] ?? "";
570
+ }
571
+ const previewHandoffs = new Map();
572
+ const PREVIEW_HANDOFF_MS = 60_000;
573
+ function revokeBlobUrl(url) {
574
+ if (!url?.startsWith("blob:") || typeof URL.revokeObjectURL !== "function")
575
+ return;
576
+ URL.revokeObjectURL(url);
577
+ }
578
+ /** Keep the already-decoded local image visible while the canonical message
579
+ * and its cacheable server URL replace the optimistic row. */
580
+ export function handoffAttachmentImagePreview(path, previewUrl) {
581
+ if (!path || !previewUrl?.startsWith("blob:"))
582
+ return;
583
+ const previous = previewHandoffs.get(path);
584
+ if (previous) {
585
+ clearTimeout(previous.timer);
586
+ if (previous.url !== previewUrl)
587
+ revokeBlobUrl(previous.url);
588
+ }
589
+ const timer = setTimeout(() => {
590
+ if (previewHandoffs.get(path)?.url !== previewUrl)
591
+ return;
592
+ previewHandoffs.delete(path);
593
+ revokeBlobUrl(previewUrl);
594
+ }, PREVIEW_HANDOFF_MS);
595
+ timer.unref?.();
596
+ previewHandoffs.set(path, { url: previewUrl, timer });
597
+ }
598
+ /** Remove a draft image and its local pixels. Sent images deliberately skip
599
+ * this path so their optimistic-to-server handoff can finish. */
600
+ export function releaseAttachmentImagePreview(attachment) {
601
+ const handed = attachment.path ? previewHandoffs.get(attachment.path) : undefined;
602
+ if (handed && handed.url === attachment.previewUrl) {
603
+ clearTimeout(handed.timer);
604
+ previewHandoffs.delete(attachment.path);
605
+ }
606
+ revokeBlobUrl(attachment.previewUrl);
607
+ }
608
+ /** The renderer never loads a transcript-provided URL directly. Only names
609
+ * the attachment server itself can have generated become same-origin image
610
+ * URLs; malformed and executable-image paths render nothing, while a string
611
+ * that looks remote can at most resolve to a local generated filename. */
612
+ export function attachmentImageUrl(path) {
613
+ const local = previewHandoffs.get(path);
614
+ if (local)
615
+ return local.url;
616
+ const name = attachmentBasename(path);
617
+ if (!/^[A-Za-z0-9-]+\.(png|jpg|gif|webp)$/.test(name))
618
+ return null;
619
+ return `/api/attachments/${encodeURIComponent(name)}`;
620
+ }
621
+ /** One intake path for files arriving by drop OR by the composer's attach
622
+ * button, so a picked file and a dropped one can never behave differently.
623
+ * Uploaders are injected for deterministic tests: callers own the network,
624
+ * while this function owns ordering and the sentence shown on failure. */
625
+ export async function intakeFiles(_files, _opts) {
626
+ const files = [..._files];
627
+ const { allowImages, getPath, uploadImage } = _opts;
628
+ const uploadFile = _opts.uploadFile ?? (async (file) => {
629
+ if (!file.arrayBuffer)
630
+ return null;
631
+ return fileAttachmentFromFile(file);
632
+ });
633
+ // Promise.all starts every optimistic image preview immediately while its
634
+ // result array still preserves the order in which files were selected.
635
+ const results = await Promise.all(files.map(async (file) => {
636
+ if (allowImages && isImageFile(file)) {
637
+ try {
638
+ const attachment = await uploadImage(file);
639
+ return { attachments: attachment ? [attachment] : [], rejectedNames: [] };
640
+ }
641
+ catch (err) {
642
+ return {
643
+ attachments: [],
644
+ rejectedNames: [],
645
+ uploadError: `${file.name}: ${err instanceof Error ? err.message : "upload failed"}`,
646
+ };
647
+ }
648
+ }
649
+ try {
650
+ const attachment = await uploadFile(file);
651
+ if (attachment) {
652
+ return { attachments: [attachment], rejectedNames: [] };
653
+ }
654
+ }
655
+ catch (err) {
656
+ return {
657
+ attachments: [],
658
+ rejectedNames: [],
659
+ uploadError: `${file.name}: ${err instanceof Error ? err.message : "upload failed"}`,
660
+ };
661
+ }
662
+ const result = await attachmentsFromDroppedFiles([file], getPath);
663
+ return result;
664
+ }));
665
+ const attachments = results.flatMap((result) => result.attachments);
666
+ const rejectedNames = results.flatMap((result) => result.rejectedNames);
667
+ const uploadErrors = results.flatMap((result) => result.uploadError ? [result.uploadError] : []);
668
+ const pathless = rejectedNames.length
669
+ ? `${rejectedNames.join(", ")} — that file has no path on disk. Save it first, then attach it from Finder.`
670
+ : null;
671
+ const failed = uploadErrors.length ? uploadErrors.join("; ") : null;
672
+ return {
673
+ attachments,
674
+ notice: pathless && failed ? `${pathless} (${failed})` : (pathless ?? failed),
675
+ };
676
+ }