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.
- package/LICENSE +202 -0
- package/README.md +14 -0
- package/cli.js +2 -0
- package/dist/app-icon.svg +29 -0
- package/dist/assets/abap-BdImnpbu.js +1 -0
- package/dist/assets/actionscript-3-B3316cI-.js +1 -0
- package/dist/assets/ada-bCR0ucgS.js +1 -0
- package/dist/assets/ahk-CsyLZFj1.js +1 -0
- package/dist/assets/ahk2-8Zs4aa1G.js +1 -0
- package/dist/assets/andromeeda-C4gqWexZ.js +1 -0
- package/dist/assets/angular-html-DA-rfuFy.js +1 -0
- package/dist/assets/angular-ts-BrjP3tb8.js +1 -0
- package/dist/assets/apache-Pmp26Uib.js +1 -0
- package/dist/assets/apex-DhZFqWV2.js +1 -0
- package/dist/assets/apl-CORt7UWP.js +1 -0
- package/dist/assets/applescript-Co6uUVPk.js +1 -0
- package/dist/assets/ara-BRHolxvo.js +1 -0
- package/dist/assets/asciidoc-CSVQ5wI8.js +1 -0
- package/dist/assets/asm-D_Q5rh1f.js +1 -0
- package/dist/assets/astro-HNnZUWAn.js +1 -0
- package/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/dist/assets/awk-DMzUqQB5.js +1 -0
- package/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
- package/dist/assets/ayu-light-BA47KaF1.js +1 -0
- package/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
- package/dist/assets/ballerina-BFfxhgS-.js +1 -0
- package/dist/assets/bat-CickPsom.js +1 -0
- package/dist/assets/beancount-k_qm7-4y.js +1 -0
- package/dist/assets/berry-uYugtg8r.js +1 -0
- package/dist/assets/bibtex-CHM0blh-.js +1 -0
- package/dist/assets/bicep-Bmn6On1c.js +1 -0
- package/dist/assets/bird2-Bx8U0n9b.js +1 -0
- package/dist/assets/blade-2xfisSek.js +1 -0
- package/dist/assets/bsl-DlhNcFeZ.js +1 -0
- package/dist/assets/c-BIGW1oBm.js +1 -0
- package/dist/assets/c3-Dp5svz6Z.js +1 -0
- package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
- package/dist/assets/cairo-KRGpt6FW.js +1 -0
- package/dist/assets/catppuccin-frappe-CZL1YF0i.js +1 -0
- package/dist/assets/catppuccin-latte-DH-8KZSZ.js +1 -0
- package/dist/assets/catppuccin-macchiato-B7yYVSCf.js +1 -0
- package/dist/assets/catppuccin-mocha-Ct7hS0mc.js +1 -0
- package/dist/assets/chapel-DTp_pixX.js +1 -0
- package/dist/assets/clarity-Dn5IMItf.js +1 -0
- package/dist/assets/clojure-P80f7IUj.js +1 -0
- package/dist/assets/cmake-D1j8_8rp.js +1 -0
- package/dist/assets/cobol-nBiQ_Alo.js +1 -0
- package/dist/assets/codeowners-Bp6g37R7.js +1 -0
- package/dist/assets/codeql-DsOJ9woJ.js +1 -0
- package/dist/assets/coffee-Ch7k5sss.js +1 -0
- package/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/dist/assets/coq-C7JzOVbR.js +1 -0
- package/dist/assets/cpp-BMRokrvK.js +1 -0
- package/dist/assets/crystal-DGywbUpC.js +1 -0
- package/dist/assets/csharp-DSvCPggb.js +1 -0
- package/dist/assets/css-CLj8gQPS.js +1 -0
- package/dist/assets/csv-fuZLfV_i.js +1 -0
- package/dist/assets/cue-D82EKSYY.js +1 -0
- package/dist/assets/cypher-COkxafJQ.js +1 -0
- package/dist/assets/d-85-TOEBH.js +1 -0
- package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/assets/dart-bE4Kk8sk.js +1 -0
- package/dist/assets/dax-CEL-wOlO.js +1 -0
- package/dist/assets/desktop-BmXAJ9_W.js +1 -0
- package/dist/assets/diff-D97Zzqfu.js +1 -0
- package/dist/assets/docker-BcOcwvcX.js +1 -0
- package/dist/assets/dotenv-Da5cRb03.js +1 -0
- package/dist/assets/dracula-BzJJZx-M.js +1 -0
- package/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/dist/assets/dream-maker-BtqSS_iP.js +1 -0
- package/dist/assets/edge-FbVlp4U3.js +1 -0
- package/dist/assets/elixir-CkH2-t6x.js +1 -0
- package/dist/assets/elm-DbKCFpqz.js +1 -0
- package/dist/assets/emacs-lisp-C_m_b--Z.js +1 -0
- package/dist/assets/erb-DXfck5VN.js +1 -0
- package/dist/assets/erlang-DsQrWhSR.js +1 -0
- package/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/dist/assets/everforest-light-C8M2exoo.js +1 -0
- package/dist/assets/fennel-BYunw83y.js +1 -0
- package/dist/assets/fish-BvzEVeQv.js +1 -0
- package/dist/assets/fluent-C4IJs8-o.js +1 -0
- package/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- package/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
- package/dist/assets/fsharp-CXgrBDvD.js +1 -0
- package/dist/assets/gdresource-TyuKm33G.js +1 -0
- package/dist/assets/gdscript-DqcFQ5yU.js +1 -0
- package/dist/assets/gdshader-DkwncUOv.js +1 -0
- package/dist/assets/genie-D0YGMca9.js +1 -0
- package/dist/assets/gherkin-DyxjwDmM.js +1 -0
- package/dist/assets/git-commit-F4YmCXRG.js +1 -0
- package/dist/assets/git-rebase-r7XF79zn.js +1 -0
- package/dist/assets/github-dark-DHJKELXO.js +1 -0
- package/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/dist/assets/github-light-DAi9KRSo.js +1 -0
- package/dist/assets/github-light-default-D7oLnXFd.js +1 -0
- package/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/dist/assets/gleam-BspZqrRM.js +1 -0
- package/dist/assets/glimmer-js-ByusRIyA.js +1 -0
- package/dist/assets/glimmer-ts-BfAWNZQY.js +1 -0
- package/dist/assets/glsl-DplSGwfg.js +1 -0
- package/dist/assets/gn-n2N0HUVH.js +1 -0
- package/dist/assets/gnuplot-DdkO51Og.js +1 -0
- package/dist/assets/go-C27-OAKa.js +1 -0
- package/dist/assets/graphql-ChdNCCLP.js +1 -0
- package/dist/assets/groovy-gcz8RCvz.js +1 -0
- package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/dist/assets/hack-BWmVpMyf.js +1 -0
- package/dist/assets/haml-D5jkg6IW.js +1 -0
- package/dist/assets/handlebars-BpdQsYii.js +1 -0
- package/dist/assets/haskell-Df6bDoY_.js +1 -0
- package/dist/assets/haxe-CfZj7gIn.js +1 -0
- package/dist/assets/hcl-BWvSN4gD.js +1 -0
- package/dist/assets/hjson-D5-asLiD.js +1 -0
- package/dist/assets/hlsl-D3lLCCz7.js +1 -0
- package/dist/assets/horizon-BUw7H-hv.js +1 -0
- package/dist/assets/horizon-bright-CUuTKBJd.js +1 -0
- package/dist/assets/houston-DnULxvSX.js +1 -0
- package/dist/assets/html-derivative-DlHx6ybY.js +1 -0
- package/dist/assets/html-pp8916En.js +1 -0
- package/dist/assets/http-jrhK8wxY.js +1 -0
- package/dist/assets/hurl-irOxFIW8.js +1 -0
- package/dist/assets/hxml-2-FPmUDs.js +1 -0
- package/dist/assets/hy-DFXneXwc.js +1 -0
- package/dist/assets/imba-DGztddWO.js +1 -0
- package/dist/assets/index-B9rLIY81.js +263 -0
- package/dist/assets/index-CJBrtDbC.css +1 -0
- package/dist/assets/index-D-u0LYsV.js +153 -0
- package/dist/assets/ini-BEwlwnbL.js +1 -0
- package/dist/assets/java-CylS5w8V.js +1 -0
- package/dist/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/assets/jinja-f2NsQr07.js +1 -0
- package/dist/assets/jison-wvAkD_A8.js +1 -0
- package/dist/assets/json-Cp-IABpG.js +1 -0
- package/dist/assets/json5-C9tS-k6U.js +1 -0
- package/dist/assets/jsonc-Des-eS-w.js +1 -0
- package/dist/assets/jsonl-DcaNXYhu.js +1 -0
- package/dist/assets/jsonnet-DFQXde-d.js +1 -0
- package/dist/assets/jssm-C2t-YnRu.js +1 -0
- package/dist/assets/jsx-g9-lgVsj.js +1 -0
- package/dist/assets/julia-5Bft2YPA.js +1 -0
- package/dist/assets/just-Cwhn7H3k.js +1 -0
- package/dist/assets/kanagawa-dragon-BuwD2xS4.js +1 -0
- package/dist/assets/kanagawa-lotus-C3DzagqV.js +1 -0
- package/dist/assets/kanagawa-wave-DFGoQZhC.js +1 -0
- package/dist/assets/kdl-DV7GczEv.js +1 -0
- package/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/dist/assets/kusto-wEQ09or8.js +1 -0
- package/dist/assets/laserwave-DUszq2jm.js +1 -0
- package/dist/assets/latex-D5pSuvFb.js +1 -0
- package/dist/assets/lean-BZvkOJ9d.js +1 -0
- package/dist/assets/less-B1dDrJ26.js +1 -0
- package/dist/assets/light-plus-B7mTdjB0.js +1 -0
- package/dist/assets/liquid-C0sCDyMI.js +1 -0
- package/dist/assets/llvm-BZoOZj88.js +1 -0
- package/dist/assets/log-2UxHyX5q.js +1 -0
- package/dist/assets/logo-BtOb2qkB.js +1 -0
- package/dist/assets/lua-BaeVxFsk.js +1 -0
- package/dist/assets/luau-BnpPk5vE.js +1 -0
- package/dist/assets/make-CHLpvVh8.js +1 -0
- package/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/dist/assets/marko-DjSrsDqO.js +1 -0
- package/dist/assets/material-theme-D5KoaKCx.js +1 -0
- package/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/dist/assets/matlab-D7o27uSR.js +1 -0
- package/dist/assets/mdc-D1_yUvq7.js +1 -0
- package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/dist/assets/mermaid-CQcHuHx7.js +1 -0
- package/dist/assets/min-dark-CafNBF8u.js +1 -0
- package/dist/assets/min-light-CTRr51gU.js +1 -0
- package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
- package/dist/assets/mojo-DJz3ZmWd.js +1 -0
- package/dist/assets/monokai-BRVnQi9A.js +1 -0
- package/dist/assets/moonbit-CHtswR0a.js +1 -0
- package/dist/assets/move-el3G9tDJ.js +1 -0
- package/dist/assets/narrat-DRg8JJMk.js +1 -0
- package/dist/assets/nextflow-C-mBbutL.js +1 -0
- package/dist/assets/nextflow-groovy-vE_lwT2v.js +1 -0
- package/dist/assets/nginx-BpAMiNFr.js +1 -0
- package/dist/assets/night-owl-C39BiMTA.js +1 -0
- package/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
- package/dist/assets/nim-BIad80T-.js +1 -0
- package/dist/assets/nix-CwoSXNpI.js +1 -0
- package/dist/assets/nord-Ddv68eIx.js +1 -0
- package/dist/assets/nsis-BlV79W_Q.js +1 -0
- package/dist/assets/nushell-D3jzshHO.js +1 -0
- package/dist/assets/objective-c-DXmwc3jG.js +1 -0
- package/dist/assets/objective-cpp-CLxacb5B.js +1 -0
- package/dist/assets/ocaml-C0hk2d4L.js +1 -0
- package/dist/assets/odin-BBf5iR-q.js +1 -0
- package/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/dist/assets/one-light-C3Wv6jpd.js +1 -0
- package/dist/assets/openscad-C4EeE6gA.js +1 -0
- package/dist/assets/org-DM6o9KBp.js +1 -0
- package/dist/assets/pascal-D93ZcfNL.js +1 -0
- package/dist/assets/perl-B9cMNwum.js +1 -0
- package/dist/assets/php-Csjmro_R.js +1 -0
- package/dist/assets/pkl-u5AG7uiY.js +1 -0
- package/dist/assets/plastic-3e1v2bzS.js +1 -0
- package/dist/assets/plsql-ChMvpjG-.js +1 -0
- package/dist/assets/po-BTJTHyun.js +1 -0
- package/dist/assets/poimandres-CS3Unz2-.js +1 -0
- package/dist/assets/polar-C0HS_06l.js +1 -0
- package/dist/assets/postcss-CXtECtnM.js +1 -0
- package/dist/assets/powerquery-CEu0bR-o.js +1 -0
- package/dist/assets/powershell-BmBUJMz7.js +1 -0
- package/dist/assets/prisma-Vru482bI.js +1 -0
- package/dist/assets/prolog-CbFg5uaA.js +1 -0
- package/dist/assets/proto-C7zT0LnQ.js +1 -0
- package/dist/assets/pug-DKIMFp6K.js +1 -0
- package/dist/assets/puppet-BMWR74SV.js +1 -0
- package/dist/assets/purescript-CklMAg4u.js +1 -0
- package/dist/assets/python-B6aJPvgy.js +1 -0
- package/dist/assets/qml-3beO22l8.js +1 -0
- package/dist/assets/qmldir-C8lEn-DE.js +1 -0
- package/dist/assets/qss-IeuSbFQv.js +1 -0
- package/dist/assets/r-Cf5RLm7j.js +1 -0
- package/dist/assets/racket-BqYA7rlc.js +1 -0
- package/dist/assets/raku-DXvB9xmW.js +1 -0
- package/dist/assets/razor-BjBPvh-w.js +1 -0
- package/dist/assets/rbs-CpoqiR4B.js +1 -0
- package/dist/assets/red-hvxz__6c.js +1 -0
- package/dist/assets/reg-C-SQnVFl.js +1 -0
- package/dist/assets/regexp-CDVJQ6XC.js +1 -0
- package/dist/assets/rel-C3B-1QV4.js +1 -0
- package/dist/assets/riscv-BM1_JUlF.js +1 -0
- package/dist/assets/ron-D8l8udqQ.js +1 -0
- package/dist/assets/rose-pine-5qJOZa0Y.js +1 -0
- package/dist/assets/rose-pine-dawn-zx0QlTCp.js +1 -0
- package/dist/assets/rose-pine-moon-C9pEdX9L.js +1 -0
- package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
- package/dist/assets/rst-bs7f0vWN.js +1 -0
- package/dist/assets/ruby-C0TQ7zu5.js +1 -0
- package/dist/assets/rust-B1yitclQ.js +1 -0
- package/dist/assets/sas-DEy46yEz.js +1 -0
- package/dist/assets/sass-Cj5Yp3dK.js +1 -0
- package/dist/assets/scala-CqE71os6.js +1 -0
- package/dist/assets/scheme-C98Dy4si.js +1 -0
- package/dist/assets/scss-D5BDwBP9.js +1 -0
- package/dist/assets/sdbl-DVxCFoDh.js +1 -0
- package/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/dist/assets/shellscript-Yzrsuije.js +1 -0
- package/dist/assets/shellsession-BADoaaVG.js +1 -0
- package/dist/assets/slack-dark-BthQWCQV.js +1 -0
- package/dist/assets/slack-ochin-DqwNpetd.js +1 -0
- package/dist/assets/smalltalk-BOQMe2GC.js +1 -0
- package/dist/assets/smithy-cds9vsN8.js +1 -0
- package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
- package/dist/assets/solarized-dark-CpvCGNkr.js +1 -0
- package/dist/assets/solarized-light-Dlz6yCKv.js +1 -0
- package/dist/assets/solidity-DijEV5ha.js +1 -0
- package/dist/assets/soy-8wufbnw4.js +1 -0
- package/dist/assets/sparql-rVzFXLq3.js +1 -0
- package/dist/assets/splunk-BtCnVYZw.js +1 -0
- package/dist/assets/sql-CRqJ_cUM.js +1 -0
- package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
- package/dist/assets/stata-DI20mbqo.js +1 -0
- package/dist/assets/stylus-BEDo0Tqx.js +1 -0
- package/dist/assets/surrealql-Cjom0U5J.js +1 -0
- package/dist/assets/svelte-Cy7k_4gC.js +1 -0
- package/dist/assets/swift-C2oV4EkX.js +1 -0
- package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/dist/assets/system-verilog-0hqHdDBg.js +1 -0
- package/dist/assets/systemd-4A_iFExJ.js +1 -0
- package/dist/assets/talonscript-CkByrt1z.js +1 -0
- package/dist/assets/tasl-QIJgUcNo.js +1 -0
- package/dist/assets/tcl-dwOrl1Do.js +1 -0
- package/dist/assets/templ-DhtptRzy.js +1 -0
- package/dist/assets/terraform-BETggiCN.js +1 -0
- package/dist/assets/tex-D96PA37w.js +1 -0
- package/dist/assets/tokyo-night-hegEt444.js +1 -0
- package/dist/assets/toml-vGWfd6FD.js +1 -0
- package/dist/assets/ts-tags-D351s5mN.js +1 -0
- package/dist/assets/tsv-B_m7g4N7.js +1 -0
- package/dist/assets/tsx-COt5Ahok.js +1 -0
- package/dist/assets/turtle-BsS91CYL.js +1 -0
- package/dist/assets/twig-27uCiNez.js +1 -0
- package/dist/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/assets/typespec-CAFt9gP4.js +1 -0
- package/dist/assets/typst-BUadGCkm.js +1 -0
- package/dist/assets/v-BGw2Nkan.js +1 -0
- package/dist/assets/vala-CsfeWuGM.js +1 -0
- package/dist/assets/vb-Cu-pLBUe.js +1 -0
- package/dist/assets/verilog-nZwndyjY.js +1 -0
- package/dist/assets/vesper-DRje8inN.js +1 -0
- package/dist/assets/vhdl-CeAyd5Ju.js +1 -0
- package/dist/assets/viml-CJc9bBzg.js +1 -0
- package/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/dist/assets/vue-BqiEGhQt.js +1 -0
- package/dist/assets/vue-html-AaS7Mt5G.js +1 -0
- package/dist/assets/vue-vine-BoDAl6tE.js +1 -0
- package/dist/assets/vyper-CDx5xZoG.js +1 -0
- package/dist/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/assets/wasm-MzD3tlZU.js +1 -0
- package/dist/assets/wenyan-BV7otONQ.js +1 -0
- package/dist/assets/wgsl-Dx-B1_4e.js +1 -0
- package/dist/assets/wikitext-BhOHFoWU.js +1 -0
- package/dist/assets/wit-5i3qLPDT.js +1 -0
- package/dist/assets/wolfram-lXgVvXCa.js +1 -0
- package/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/dist/assets/xsl-CtQFsRM5.js +1 -0
- package/dist/assets/yaml-Buea-lGh.js +1 -0
- package/dist/assets/zenscript-DVFEvuxE.js +1 -0
- package/dist/assets/zig-VOosw3JB.js +1 -0
- package/dist/index.html +14 -0
- package/dist-server/companion/src/listener.js +187 -0
- package/dist-server/computer-proxy.js +1185 -0
- package/dist-server/connector-proxy.js +186 -0
- package/dist-server/container-mcp.js +15186 -0
- package/dist-server/drivers/agents-proxy.js +887 -0
- package/dist-server/drivers/browser-proxy.js +15037 -0
- package/dist-server/drivers/dweb-proxy.js +175 -0
- package/dist-server/drivers/phone-proxy.js +339 -0
- package/dist-server/drivers/pi-mcp-extension.ts +638 -0
- package/dist-server/index.js +64869 -0
- package/dist-server/local-computer.js +198 -0
- package/dist-server/mcp-server.js +1233 -0
- package/dist-server/openmausbot.js +3671 -0
- package/dist-server/pair-cli.js +3671 -0
- package/dist-server/permission-proxy.js +126 -0
- package/dist-server/prepare-cloudflared.js +253 -0
- package/dist-server/proxy-paths.js +29 -0
- package/dist-server/server/atomic.js +40 -0
- package/dist-server/server/attachments.js +471 -0
- package/dist-server/server/auto-approve.js +162 -0
- package/dist-server/server/auto-review.js +79 -0
- package/dist-server/server/avatar-image.js +144 -0
- package/dist-server/server/bot-cwd.js +29 -0
- package/dist-server/server/bot-directory.js +127 -0
- package/dist-server/server/bot-package.js +268 -0
- package/dist-server/server/bot-profile.js +76 -0
- package/dist-server/server/box-create-idempotency.js +463 -0
- package/dist-server/server/box.js +944 -0
- package/dist-server/server/brand.js +65 -0
- package/dist-server/server/browser-connection.js +187 -0
- package/dist-server/server/browser-lifecycle-cleanup.js +359 -0
- package/dist-server/server/calendar-calls.js +314 -0
- package/dist-server/server/channel-queue.js +70 -0
- package/dist-server/server/checkpoints.js +405 -0
- package/dist-server/server/chief-of-staff.js +49 -0
- package/dist-server/server/claude-update.js +70 -0
- package/dist-server/server/cli.js +524 -0
- package/dist-server/server/cloud-backend.js +30 -0
- package/dist-server/server/comms-visibility.js +109 -0
- package/dist-server/server/composio.js +945 -0
- package/dist-server/server/computer-control.js +149 -0
- package/dist-server/server/computer-observation.js +133 -0
- package/dist-server/server/computer-proxy.js +1061 -0
- package/dist-server/server/config.js +760 -0
- package/dist-server/server/connector-proxy.js +228 -0
- package/dist-server/server/container-computer.js +840 -0
- package/dist-server/server/container-mcp.js +27 -0
- package/dist-server/server/contracts.js +24 -0
- package/dist-server/server/control-client.js +100 -0
- package/dist-server/server/data-dir-lease.js +3 -0
- package/dist-server/server/decision-log.js +106 -0
- package/dist-server/server/delegations.js +628 -0
- package/dist-server/server/drivers/acp/core.js +904 -0
- package/dist-server/server/drivers/acp/cursor.js +365 -0
- package/dist-server/server/drivers/acp/custom.js +51 -0
- package/dist-server/server/drivers/acp/droid.js +225 -0
- package/dist-server/server/drivers/acp/gemini.js +95 -0
- package/dist-server/server/drivers/acp/grok.js +249 -0
- package/dist-server/server/drivers/acp/hermes.js +429 -0
- package/dist-server/server/drivers/acp/kimi.js +559 -0
- package/dist-server/server/drivers/acp/opencode-go.js +370 -0
- package/dist-server/server/drivers/acp/qwen.js +98 -0
- package/dist-server/server/drivers/agents-proxy.js +941 -0
- package/dist-server/server/drivers/antigravity-acp.js +507 -0
- package/dist-server/server/drivers/antigravity-release.js +65 -0
- package/dist-server/server/drivers/antigravity-runtime.js +395 -0
- package/dist-server/server/drivers/antigravity.js +144 -0
- package/dist-server/server/drivers/boxagent.js +245 -0
- package/dist-server/server/drivers/browser-proxy.js +520 -0
- package/dist-server/server/drivers/builtIn.js +36 -0
- package/dist-server/server/drivers/claude.js +1337 -0
- package/dist-server/server/drivers/codex-catalog.js +409 -0
- package/dist-server/server/drivers/codex.js +1116 -0
- package/dist-server/server/drivers/dweb-proxy.js +193 -0
- package/dist-server/server/drivers/grok.js +48 -0
- package/dist-server/server/drivers/grokagent.js +4 -0
- package/dist-server/server/drivers/local-inject.js +388 -0
- package/dist-server/server/drivers/minimax.js +108 -0
- package/dist-server/server/drivers/native.js +21 -0
- package/dist-server/server/drivers/openai-chat.js +240 -0
- package/dist-server/server/drivers/openai-compat.js +143 -0
- package/dist-server/server/drivers/phone-proxy.js +372 -0
- package/dist-server/server/drivers/pi.js +855 -0
- package/dist-server/server/drivers/retry.js +96 -0
- package/dist-server/server/enterprise.js +93 -0
- package/dist-server/server/env-path.js +329 -0
- package/dist-server/server/environment.js +105 -0
- package/dist-server/server/generated-image.js +39 -0
- package/dist-server/server/graceful-shutdown.js +24 -0
- package/dist-server/server/group-goal-run.js +124 -0
- package/dist-server/server/harness/bus.js +86 -0
- package/dist-server/server/harness/registry.js +217 -0
- package/dist-server/server/index.js +11634 -0
- package/dist-server/server/installed-playbooks.js +34 -0
- package/dist-server/server/local-computer.js +278 -0
- package/dist-server/server/local-routing.js +9 -0
- package/dist-server/server/local-vm-idle.js +44 -0
- package/dist-server/server/local-vm-inventory.js +34 -0
- package/dist-server/server/local-vm-lease.js +58 -0
- package/dist-server/server/mcp-bridge.js +251 -0
- package/dist-server/server/mcp-probe.js +153 -0
- package/dist-server/server/mcp-registry.js +130 -0
- package/dist-server/server/member-turn.js +6 -0
- package/dist-server/server/message-db.js +358 -0
- package/dist-server/server/message-file.js +439 -0
- package/dist-server/server/names.js +26 -0
- package/dist-server/server/notify.js +69 -0
- package/dist-server/server/openmaus-status-capsule.js +329 -0
- package/dist-server/server/openmausbot.js +6 -0
- package/dist-server/server/package-export.js +157 -0
- package/dist-server/server/pair-cli.js +7 -0
- package/dist-server/server/peer-approval-key.js +5 -0
- package/dist-server/server/peer-approval.js +182 -0
- package/dist-server/server/peer-provenance.js +44 -0
- package/dist-server/server/peer-roster.js +139 -0
- package/dist-server/server/permission-proxy.js +157 -0
- package/dist-server/server/phone-secret.js +358 -0
- package/dist-server/server/private-screen-capture.js +17 -0
- package/dist-server/server/procs.js +123 -0
- package/dist-server/server/project-scout.js +304 -0
- package/dist-server/server/proxy-paths.js +47 -0
- package/dist-server/server/redact.js +102 -0
- package/dist-server/server/remote-computer.js +170 -0
- package/dist-server/server/repeat-detector.js +48 -0
- package/dist-server/server/replies.js +38 -0
- package/dist-server/server/request-auth.js +327 -0
- package/dist-server/server/room-cwd.js +15 -0
- package/dist-server/server/room-post-budget.js +112 -0
- package/dist-server/server/room-turn-timeout.js +115 -0
- package/dist-server/server/routine-requests.js +871 -0
- package/dist-server/server/routines.js +1111 -0
- package/dist-server/server/schema.js +11 -0
- package/dist-server/server/screen-frame-gate.js +80 -0
- package/dist-server/server/section-context.js +85 -0
- package/dist-server/server/send-idempotency.js +53 -0
- package/dist-server/server/sessions.js +319 -0
- package/dist-server/server/skill-fetch.js +148 -0
- package/dist-server/server/skill-learn.js +61 -0
- package/dist-server/server/skill-library.js +116 -0
- package/dist-server/server/skills.js +1223 -0
- package/dist-server/server/steer-queue.js +114 -0
- package/dist-server/server/store.js +1217 -0
- package/dist-server/server/surface.js +107 -0
- package/dist-server/server/tailscale.js +103 -0
- package/dist-server/server/team-backup.js +183 -0
- package/dist-server/server/team-library.js +205 -0
- package/dist-server/server/team-manifest.js +224 -0
- package/dist-server/server/thread-events.js +226 -0
- package/dist-server/server/tts/elevenlabs.js +92 -0
- package/dist-server/server/tts/index.js +79 -0
- package/dist-server/server/tts/speech-text.js +195 -0
- package/dist-server/server/tts/system-voices.js +74 -0
- package/dist-server/server/tunnel.js +202 -0
- package/dist-server/server/turn-context.js +40 -0
- package/dist-server/server/turn-dispatch-guard.js +152 -0
- package/dist-server/server/turn-images.js +138 -0
- package/dist-server/server/turn-watchdog.js +65 -0
- package/dist-server/server/vps-computer.js +1099 -0
- package/dist-server/server/vps-container-mcp.js +31 -0
- package/dist-server/server/webhook-ingress.js +173 -0
- package/dist-server/server/webhooks.js +491 -0
- package/dist-server/server/workspace.js +178 -0
- package/dist-server/shared/approval-mode.js +39 -0
- package/dist-server/shared/bot-avatar.js +33 -0
- package/dist-server/shared/bot-profile.js +7 -0
- package/dist-server/shared/connector-availability.js +12 -0
- package/dist-server/shared/credential-request.js +82 -0
- package/dist-server/shared/group-goal-run.js +1 -0
- package/dist-server/shared/import-name.js +11 -0
- package/dist-server/shared/mascot-bodies.js +107 -0
- package/dist-server/shared/routine-request.js +10 -0
- package/dist-server/shared/routine-run.js +1 -0
- package/dist-server/shared/skill-request.js +16 -0
- package/dist-server/shared/team-backup.js +148 -0
- package/dist-server/src/lib/composer-attachments.js +676 -0
- package/dist-server/tunnel-guardian.js +390 -0
- package/dist-server/vps-container-mcp.js +15209 -0
- package/package.json +32 -0
- package/skills/create-verification-skill/SKILL.md +65 -0
- package/skills/create-verification-skill/manifest.json +13 -0
- package/skills/phone-harness/SKILL.md +26 -0
- package/skills/phone-harness/agents/openai.yaml +13 -0
- package/skills/phone-harness/manifest.json +17 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
// pi-mcp-extension — the Pi-side half of "hands" for the pi engine.
|
|
2
|
+
//
|
|
3
|
+
// Pi core deliberately ships no MCP client (see pi's docs: "does not include
|
|
4
|
+
// built-in MCP"). This extension is that client: loaded into the per-turn
|
|
5
|
+
// `pi --mode rpc --no-session` process via `-e`, it reads a JSON file whose
|
|
6
|
+
// path is handed in through OMB_MCP_CONFIG and mounts every server described
|
|
7
|
+
// there as first-class pi tools (pi.registerTool). OpenMausBot ships this file
|
|
8
|
+
// and the pi driver spawns it — the pi repo itself is never touched.
|
|
9
|
+
//
|
|
10
|
+
// Protocol: the OpenMausBot proxies speak raw JSON-RPC 2.0 over stdio, one
|
|
11
|
+
// frame per line (no MCP SDK, no Content-Length framing) — see
|
|
12
|
+
// server/mcp-bridge.ts and server/drivers/agents-proxy.ts. This client matches
|
|
13
|
+
// that exactly.
|
|
14
|
+
import { Type, type TObjectOptions, type TSchema, type TSchemaOptions } from "typebox";
|
|
15
|
+
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
|
|
18
|
+
interface McpServerDef {
|
|
19
|
+
command: string;
|
|
20
|
+
args?: string[];
|
|
21
|
+
env?: Record<string, string>;
|
|
22
|
+
/** "local-computer" marks the user's real host desktop: every tool on such
|
|
23
|
+
* a server is gated behind a permission card before it executes. */
|
|
24
|
+
scope?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface McpConfig {
|
|
28
|
+
mcpServers?: Record<string, McpServerDef>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface McpTool {
|
|
32
|
+
name: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
inputSchema?: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface McpTextContent {
|
|
38
|
+
type: "text";
|
|
39
|
+
text: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface McpImageContent {
|
|
43
|
+
type: "image";
|
|
44
|
+
data: unknown;
|
|
45
|
+
mimeType: unknown;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type PiToolContent = { type: "text"; text: string } | { type: "image"; data: string; mimeType: string };
|
|
49
|
+
|
|
50
|
+
interface PiToolContext {
|
|
51
|
+
ui: {
|
|
52
|
+
confirm(title: string, message: string): Promise<boolean>;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface PiToolDefinition {
|
|
57
|
+
name: string;
|
|
58
|
+
label: string;
|
|
59
|
+
description: string;
|
|
60
|
+
parameters: TSchema;
|
|
61
|
+
execute(
|
|
62
|
+
toolCallId: string,
|
|
63
|
+
params: unknown,
|
|
64
|
+
signal: AbortSignal | undefined,
|
|
65
|
+
onUpdate: unknown,
|
|
66
|
+
context: PiToolContext,
|
|
67
|
+
): Promise<{ content: PiToolContent[]; details: Record<string, never> }>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Structural slice of Pi 0.84's ExtensionAPI used by this standalone file.
|
|
71
|
+
* Keeping it local avoids bundling Pi into OpenMausBot; the extension is
|
|
72
|
+
* loaded by the user's installed Pi, which supplies the real implementation. */
|
|
73
|
+
interface PiExtensionApi {
|
|
74
|
+
registerTool(definition: PiToolDefinition): void;
|
|
75
|
+
on(event: "session_shutdown", handler: () => void): void;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const MCP_STARTUP_TIMEOUT_MS = 8_000;
|
|
79
|
+
const MCP_TOOL_TIMEOUT_MS = 10 * 60_000;
|
|
80
|
+
const MCP_MAX_LIST_PAGES = 100;
|
|
81
|
+
const MCP_MAX_FRAME_BYTES = 32 * 1024 * 1024;
|
|
82
|
+
const TOOL_OUTPUT_MAX_BYTES = 50 * 1024;
|
|
83
|
+
const TOOL_OUTPUT_MAX_LINES = 2_000;
|
|
84
|
+
const TOOL_NAME_MAX_LENGTH = 64;
|
|
85
|
+
|
|
86
|
+
/** A minimal stdio JSON-RPC 2.0 MCP client, matched to OpenMausBot's
|
|
87
|
+
* newline-delimited house protocol. */
|
|
88
|
+
export class StdioMcp {
|
|
89
|
+
private child: ChildProcessWithoutNullStreams;
|
|
90
|
+
private buf = "";
|
|
91
|
+
private nextId = 1;
|
|
92
|
+
private disposed = false;
|
|
93
|
+
private pending = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();
|
|
94
|
+
|
|
95
|
+
constructor(def: McpServerDef) {
|
|
96
|
+
this.child = spawn(def.command, def.args ?? [], {
|
|
97
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
98
|
+
env: { ...process.env, ...(def.env ?? {}) },
|
|
99
|
+
});
|
|
100
|
+
this.child.stderr.on("data", () => {
|
|
101
|
+
/* best-effort drain so a chatty server never blocks */
|
|
102
|
+
});
|
|
103
|
+
this.child.stdout.setEncoding("utf8");
|
|
104
|
+
this.child.stdout.on("data", (chunk: string) => this.onData(chunk));
|
|
105
|
+
this.child.on("error", (err) => this.closeWithError(err));
|
|
106
|
+
// A server that starts and then dies emits `exit`, never `error`; without
|
|
107
|
+
// settling on it, an in-flight init/listTools would hang the extension
|
|
108
|
+
// load for the whole turn.
|
|
109
|
+
this.child.on("exit", (code) => this.closeWithError(new Error(`MCP server exited (code ${code ?? "?"})`)));
|
|
110
|
+
// A write to a dead child errors asynchronously on the stream; an
|
|
111
|
+
// unhandled stream error would kill the pi process (the same hazard
|
|
112
|
+
// spawnCli documents for driver-spawned children in procs.ts).
|
|
113
|
+
this.child.stdin.on("error", () => this.closeWithError(new Error("MCP server stdin closed")));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private failAll(err: Error): void {
|
|
117
|
+
for (const [, p] of this.pending) p.reject(err);
|
|
118
|
+
this.pending.clear();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private closeWithError(err: Error): void {
|
|
122
|
+
if (this.disposed) return;
|
|
123
|
+
this.disposed = true;
|
|
124
|
+
this.failAll(err);
|
|
125
|
+
try {
|
|
126
|
+
this.child.kill();
|
|
127
|
+
} catch {
|
|
128
|
+
/* already gone */
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private onData(chunk: string): void {
|
|
133
|
+
this.buf += chunk;
|
|
134
|
+
let nl: number;
|
|
135
|
+
while ((nl = this.buf.indexOf("\n")) !== -1) {
|
|
136
|
+
const line = this.buf.slice(0, nl).trim();
|
|
137
|
+
this.buf = this.buf.slice(nl + 1);
|
|
138
|
+
if (!line) continue;
|
|
139
|
+
if (Buffer.byteLength(line, "utf8") > MCP_MAX_FRAME_BYTES) {
|
|
140
|
+
this.closeWithError(new Error(`MCP frame exceeded ${MCP_MAX_FRAME_BYTES} bytes`));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
let msg: { id?: unknown; method?: unknown; error?: { message?: string }; result?: unknown };
|
|
144
|
+
try {
|
|
145
|
+
msg = JSON.parse(line);
|
|
146
|
+
} catch {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (typeof msg.id === "number" && this.pending.has(msg.id)) {
|
|
150
|
+
const p = this.pending.get(msg.id)!;
|
|
151
|
+
this.pending.delete(msg.id);
|
|
152
|
+
if (msg.error) p.reject(new Error(msg.error.message ?? "MCP error"));
|
|
153
|
+
else p.resolve(msg.result);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
// This minimal client does not implement server→client requests. Reply
|
|
157
|
+
// explicitly instead of leaving a conforming server waiting forever.
|
|
158
|
+
if (msg.id !== undefined && typeof msg.method === "string") {
|
|
159
|
+
try {
|
|
160
|
+
this.write({ jsonrpc: "2.0", id: msg.id, error: { code: -32601, message: "Client method not supported" } });
|
|
161
|
+
} catch (err) {
|
|
162
|
+
this.closeWithError(err instanceof Error ? err : new Error(String(err)));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Notifications (e.g. notifications/tools/list_changed) are intentionally
|
|
167
|
+
// ignored for this single-shot mount.
|
|
168
|
+
}
|
|
169
|
+
if (Buffer.byteLength(this.buf, "utf8") > MCP_MAX_FRAME_BYTES) {
|
|
170
|
+
this.closeWithError(new Error(`MCP frame exceeded ${MCP_MAX_FRAME_BYTES} bytes without a newline`));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private write(frame: unknown): void {
|
|
175
|
+
if (this.disposed || this.child.stdin.destroyed || !this.child.stdin.writable) {
|
|
176
|
+
throw new Error("MCP server stdin is closed");
|
|
177
|
+
}
|
|
178
|
+
this.child.stdin.write(JSON.stringify(frame) + "\n");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private call(method: string, params: unknown, timeoutMs: number, signal?: AbortSignal): Promise<unknown> {
|
|
182
|
+
if (this.disposed) return Promise.reject(new Error("MCP client is closed"));
|
|
183
|
+
if (signal?.aborted) return Promise.reject(new Error(`MCP ${method} aborted`));
|
|
184
|
+
|
|
185
|
+
const id = this.nextId++;
|
|
186
|
+
return new Promise<unknown>((resolve, reject) => {
|
|
187
|
+
let settled = false;
|
|
188
|
+
const cleanup = () => {
|
|
189
|
+
clearTimeout(timer);
|
|
190
|
+
signal?.removeEventListener("abort", onAbort);
|
|
191
|
+
};
|
|
192
|
+
const settle = (fn: () => void) => {
|
|
193
|
+
if (settled) return;
|
|
194
|
+
settled = true;
|
|
195
|
+
cleanup();
|
|
196
|
+
fn();
|
|
197
|
+
};
|
|
198
|
+
const cancel = (reason: string) => {
|
|
199
|
+
this.pending.delete(id);
|
|
200
|
+
try {
|
|
201
|
+
this.notify("notifications/cancelled", { requestId: id, reason });
|
|
202
|
+
} catch {
|
|
203
|
+
/* the transport may already be gone */
|
|
204
|
+
}
|
|
205
|
+
settle(() => reject(new Error(reason)));
|
|
206
|
+
};
|
|
207
|
+
const onAbort = () => cancel(`MCP ${method} aborted`);
|
|
208
|
+
const timer = setTimeout(() => cancel(`MCP ${method} timed out after ${timeoutMs}ms`), timeoutMs);
|
|
209
|
+
timer.unref?.();
|
|
210
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
211
|
+
this.pending.set(id, {
|
|
212
|
+
resolve: (value) => settle(() => resolve(value)),
|
|
213
|
+
reject: (err) => settle(() => reject(err)),
|
|
214
|
+
});
|
|
215
|
+
try {
|
|
216
|
+
this.write({ jsonrpc: "2.0", id, method, params });
|
|
217
|
+
} catch (err) {
|
|
218
|
+
this.pending.delete(id);
|
|
219
|
+
settle(() => reject(err instanceof Error ? err : new Error(String(err))));
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private notify(method: string, params?: unknown): void {
|
|
225
|
+
this.write({ jsonrpc: "2.0", method, params });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async init(): Promise<void> {
|
|
229
|
+
await this.call(
|
|
230
|
+
"initialize",
|
|
231
|
+
{
|
|
232
|
+
protocolVersion: "2024-11-05",
|
|
233
|
+
capabilities: {},
|
|
234
|
+
clientInfo: { name: "openmausbot-pi", version: "1" },
|
|
235
|
+
},
|
|
236
|
+
MCP_STARTUP_TIMEOUT_MS,
|
|
237
|
+
);
|
|
238
|
+
this.notify("notifications/initialized");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async listTools(): Promise<McpTool[]> {
|
|
242
|
+
const tools: McpTool[] = [];
|
|
243
|
+
const seenCursors = new Set<string>();
|
|
244
|
+
const deadline = Date.now() + MCP_STARTUP_TIMEOUT_MS;
|
|
245
|
+
let cursor: string | undefined;
|
|
246
|
+
for (let page = 0; page < MCP_MAX_LIST_PAGES; page += 1) {
|
|
247
|
+
const remainingMs = Math.max(1, deadline - Date.now());
|
|
248
|
+
const res = (await this.call(
|
|
249
|
+
"tools/list",
|
|
250
|
+
cursor ? { cursor } : {},
|
|
251
|
+
remainingMs,
|
|
252
|
+
)) as { tools?: unknown; nextCursor?: unknown } | undefined;
|
|
253
|
+
if (!Array.isArray(res?.tools)) throw new Error("MCP tools/list returned no tools array");
|
|
254
|
+
tools.push(...(res.tools as McpTool[]));
|
|
255
|
+
if (typeof res.nextCursor !== "string" || !res.nextCursor) return tools;
|
|
256
|
+
if (seenCursors.has(res.nextCursor)) throw new Error("MCP tools/list repeated a pagination cursor");
|
|
257
|
+
seenCursors.add(res.nextCursor);
|
|
258
|
+
cursor = res.nextCursor;
|
|
259
|
+
}
|
|
260
|
+
throw new Error(`MCP tools/list exceeded ${MCP_MAX_LIST_PAGES} pages`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** tools/call → Pi content, preserving screenshots and bounding text so a
|
|
264
|
+
* remote server cannot flood the model context. */
|
|
265
|
+
async callTool(name: string, args: unknown, signal?: AbortSignal): Promise<{ content: PiToolContent[]; isError: boolean }> {
|
|
266
|
+
const res = (await this.call(
|
|
267
|
+
"tools/call",
|
|
268
|
+
{ name, arguments: args ?? {} },
|
|
269
|
+
MCP_TOOL_TIMEOUT_MS,
|
|
270
|
+
signal,
|
|
271
|
+
)) as { content?: unknown; isError?: boolean } | undefined;
|
|
272
|
+
const rawContent = Array.isArray(res?.content) ? res.content : [];
|
|
273
|
+
const text = rawContent
|
|
274
|
+
.filter((c): c is McpTextContent => Boolean(c) && (c as { type?: unknown }).type === "text")
|
|
275
|
+
.map((c) => String(c.text ?? ""))
|
|
276
|
+
.join("\n");
|
|
277
|
+
const images = rawContent
|
|
278
|
+
.filter(
|
|
279
|
+
(c): c is McpImageContent =>
|
|
280
|
+
Boolean(c) &&
|
|
281
|
+
(c as { type?: unknown }).type === "image" &&
|
|
282
|
+
typeof (c as McpImageContent).data === "string" &&
|
|
283
|
+
typeof (c as McpImageContent).mimeType === "string",
|
|
284
|
+
)
|
|
285
|
+
.map((c) => ({ type: "image" as const, data: String(c.data), mimeType: String(c.mimeType) }));
|
|
286
|
+
const unsupported = rawContent.length - rawContent.filter((c) => (c as { type?: unknown })?.type === "text").length - images.length;
|
|
287
|
+
let boundedText = truncateToolText(text || (images.length ? "" : "(empty result)"));
|
|
288
|
+
if (unsupported > 0) boundedText += `${boundedText ? "\n" : ""}[${unsupported} unsupported MCP content item(s) omitted]`;
|
|
289
|
+
return {
|
|
290
|
+
content: [...(boundedText ? [{ type: "text" as const, text: boundedText }] : []), ...images],
|
|
291
|
+
isError: Boolean(res?.isError),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
dispose(): void {
|
|
296
|
+
if (this.disposed) return;
|
|
297
|
+
this.disposed = true;
|
|
298
|
+
this.failAll(new Error("MCP client disposed"));
|
|
299
|
+
try {
|
|
300
|
+
this.child.kill();
|
|
301
|
+
} catch {
|
|
302
|
+
/* already gone */
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
interface JsonSchema {
|
|
308
|
+
type?: string | string[];
|
|
309
|
+
title?: unknown;
|
|
310
|
+
description?: unknown;
|
|
311
|
+
default?: unknown;
|
|
312
|
+
const?: unknown;
|
|
313
|
+
enum?: unknown[];
|
|
314
|
+
nullable?: unknown;
|
|
315
|
+
anyOf?: unknown[];
|
|
316
|
+
oneOf?: unknown[];
|
|
317
|
+
allOf?: unknown[];
|
|
318
|
+
items?: unknown | unknown[];
|
|
319
|
+
prefixItems?: unknown[];
|
|
320
|
+
properties?: Record<string, unknown>;
|
|
321
|
+
required?: unknown;
|
|
322
|
+
additionalProperties?: unknown;
|
|
323
|
+
minimum?: unknown;
|
|
324
|
+
maximum?: unknown;
|
|
325
|
+
exclusiveMinimum?: unknown;
|
|
326
|
+
exclusiveMaximum?: unknown;
|
|
327
|
+
multipleOf?: unknown;
|
|
328
|
+
minLength?: unknown;
|
|
329
|
+
maxLength?: unknown;
|
|
330
|
+
pattern?: unknown;
|
|
331
|
+
format?: unknown;
|
|
332
|
+
minItems?: unknown;
|
|
333
|
+
maxItems?: unknown;
|
|
334
|
+
uniqueItems?: unknown;
|
|
335
|
+
minProperties?: unknown;
|
|
336
|
+
maxProperties?: unknown;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const SCHEMA_OPTION_KEYS = [
|
|
340
|
+
"title",
|
|
341
|
+
"description",
|
|
342
|
+
"default",
|
|
343
|
+
"minimum",
|
|
344
|
+
"maximum",
|
|
345
|
+
"exclusiveMinimum",
|
|
346
|
+
"exclusiveMaximum",
|
|
347
|
+
"multipleOf",
|
|
348
|
+
"minLength",
|
|
349
|
+
"maxLength",
|
|
350
|
+
"pattern",
|
|
351
|
+
"format",
|
|
352
|
+
"minItems",
|
|
353
|
+
"maxItems",
|
|
354
|
+
"uniqueItems",
|
|
355
|
+
"minProperties",
|
|
356
|
+
"maxProperties",
|
|
357
|
+
] as const;
|
|
358
|
+
|
|
359
|
+
function schemaOptions(schema: JsonSchema): TSchemaOptions {
|
|
360
|
+
const options: TSchemaOptions = {};
|
|
361
|
+
for (const key of SCHEMA_OPTION_KEYS) {
|
|
362
|
+
const value = schema[key];
|
|
363
|
+
if (value !== undefined) Reflect.set(options, key, value);
|
|
364
|
+
}
|
|
365
|
+
return options;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function isSchemaObject(value: unknown): value is JsonSchema {
|
|
369
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function primitiveLiteral(value: unknown): TSchema | null {
|
|
373
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
374
|
+
return Type.Literal(value);
|
|
375
|
+
}
|
|
376
|
+
if (value === null) return Type.Null();
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function withNullable(schema: TSchema, nullable: unknown): TSchema {
|
|
381
|
+
return nullable === true ? Type.Union([schema, Type.Null()]) : schema;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function collectObjectShape(schema: JsonSchema): { properties: Record<string, unknown>; required: Set<string> } {
|
|
385
|
+
const properties: Record<string, unknown> = {};
|
|
386
|
+
const required = new Set<string>();
|
|
387
|
+
|
|
388
|
+
const visit = (candidate: unknown, mode: "root" | "all" | "choice") => {
|
|
389
|
+
if (!isSchemaObject(candidate)) return;
|
|
390
|
+
for (const branch of candidate.allOf ?? []) visit(branch, mode === "choice" ? "choice" : "all");
|
|
391
|
+
for (const branch of [...(candidate.anyOf ?? []), ...(candidate.oneOf ?? [])]) visit(branch, "choice");
|
|
392
|
+
for (const [key, value] of Object.entries(candidate.properties ?? {})) {
|
|
393
|
+
// The root declaration is authoritative; branch-only properties are
|
|
394
|
+
// still retained so the model knows which arguments exist.
|
|
395
|
+
if (mode === "root" || properties[key] === undefined) properties[key] = value;
|
|
396
|
+
}
|
|
397
|
+
if (mode !== "choice" && Array.isArray(candidate.required)) {
|
|
398
|
+
for (const key of candidate.required) if (typeof key === "string") required.add(key);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
visit(schema, "root");
|
|
403
|
+
return { properties, required };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function objectSchema(schema: JsonSchema, root = false): TSchema {
|
|
407
|
+
const { properties, required } = collectObjectShape(schema);
|
|
408
|
+
const converted: Record<string, TSchema> = {};
|
|
409
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
410
|
+
const nested = nestedTypebox(value);
|
|
411
|
+
converted[key] = required.has(key) ? nested : Type.Optional(nested);
|
|
412
|
+
}
|
|
413
|
+
const options: TObjectOptions = schemaOptions(schema);
|
|
414
|
+
if (typeof schema.additionalProperties === "boolean") {
|
|
415
|
+
options.additionalProperties = schema.additionalProperties;
|
|
416
|
+
} else if (isSchemaObject(schema.additionalProperties)) {
|
|
417
|
+
options.additionalProperties = nestedTypebox(schema.additionalProperties);
|
|
418
|
+
}
|
|
419
|
+
const object = Type.Object(converted, options);
|
|
420
|
+
return root ? object : withNullable(object, schema.nullable);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function nestedTypebox(schema: unknown): TSchema {
|
|
424
|
+
if (!isSchemaObject(schema)) return Type.Any();
|
|
425
|
+
const options = schemaOptions(schema);
|
|
426
|
+
|
|
427
|
+
const literal = primitiveLiteral(schema.const);
|
|
428
|
+
if (literal) return withNullable(literal, schema.nullable);
|
|
429
|
+
|
|
430
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
431
|
+
if (schema.enum.every((value) => typeof value === "string")) {
|
|
432
|
+
// `{type:"string", enum:[...]}` works across Google/OpenAI/Anthropic;
|
|
433
|
+
// Type.Union(Type.Literal(...)) does not work with Google's tool API.
|
|
434
|
+
return withNullable(Type.String({ ...options, enum: schema.enum }), schema.nullable);
|
|
435
|
+
}
|
|
436
|
+
const literals = schema.enum.map(primitiveLiteral).filter((value): value is TSchema => value !== null);
|
|
437
|
+
if (literals.length === schema.enum.length) {
|
|
438
|
+
const union = literals.length === 1 ? literals[0] : Type.Union(literals, options);
|
|
439
|
+
return withNullable(union, schema.nullable);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (Array.isArray(schema.type)) {
|
|
444
|
+
const variants = schema.type.map((type) => nestedTypebox({ ...schema, type, nullable: false }));
|
|
445
|
+
return variants.length === 1 ? variants[0] : Type.Union(variants, options);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (schema.type === "object" || schema.properties) return objectSchema(schema);
|
|
449
|
+
|
|
450
|
+
const choice = schema.anyOf ?? schema.oneOf;
|
|
451
|
+
if (Array.isArray(choice) && choice.length > 0) {
|
|
452
|
+
const variants = choice.map(nestedTypebox);
|
|
453
|
+
return withNullable(variants.length === 1 ? variants[0] : Type.Union(variants, options), schema.nullable);
|
|
454
|
+
}
|
|
455
|
+
if (Array.isArray(schema.allOf) && schema.allOf.length > 0) {
|
|
456
|
+
const variants = schema.allOf.map(nestedTypebox);
|
|
457
|
+
return withNullable(variants.length === 1 ? variants[0] : Type.Intersect(variants, options), schema.nullable);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
switch (schema.type) {
|
|
461
|
+
case "string":
|
|
462
|
+
return withNullable(Type.String(options), schema.nullable);
|
|
463
|
+
case "number":
|
|
464
|
+
return withNullable(Type.Number(options), schema.nullable);
|
|
465
|
+
case "integer":
|
|
466
|
+
return withNullable(Type.Integer(options), schema.nullable);
|
|
467
|
+
case "boolean":
|
|
468
|
+
return withNullable(Type.Boolean(options), schema.nullable);
|
|
469
|
+
case "null":
|
|
470
|
+
return Type.Null();
|
|
471
|
+
case "array": {
|
|
472
|
+
const tupleItems = schema.prefixItems ?? (Array.isArray(schema.items) ? schema.items : undefined);
|
|
473
|
+
const value = tupleItems
|
|
474
|
+
? Type.Tuple(tupleItems.map(nestedTypebox), options)
|
|
475
|
+
: Type.Array(schema.items ? nestedTypebox(schema.items) : Type.Any(), options);
|
|
476
|
+
return withNullable(value, schema.nullable);
|
|
477
|
+
}
|
|
478
|
+
default:
|
|
479
|
+
return Type.Any();
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/** Best-effort JSON Schema → TypeBox. MCP tool parameters must always expose
|
|
484
|
+
* an object at the root; Pi/provider tool serialization rejects a root schema
|
|
485
|
+
* without `type: "object"`. Nested schemas retain unions, nullability and
|
|
486
|
+
* constraints instead of being incorrectly rewritten as objects. */
|
|
487
|
+
export function toTypebox(schema: unknown): TSchema {
|
|
488
|
+
return objectSchema(isSchemaObject(schema) ? schema : {}, true);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/** pi tool names are lowercase snake identifiers; MCP tool names are not
|
|
492
|
+
* (COMPOSIO_SEARCH_TOOLS, browser_navigate, mcp__x…). Normalize and prefix
|
|
493
|
+
* with the server so two servers can never collide. */
|
|
494
|
+
function sanitizeToolName(server: string, tool: string): string {
|
|
495
|
+
const raw = `${server}_${tool}`.toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
|
|
496
|
+
return raw.slice(0, TOOL_NAME_MAX_LENGTH) || "mcp_tool";
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export function allocateToolName(server: string, tool: string, used: Set<string>): string {
|
|
500
|
+
const base = sanitizeToolName(server, tool);
|
|
501
|
+
let candidate = base;
|
|
502
|
+
for (let suffixNumber = 2; used.has(candidate); suffixNumber += 1) {
|
|
503
|
+
const suffix = `_${suffixNumber}`;
|
|
504
|
+
candidate = `${base.slice(0, Math.max(1, TOOL_NAME_MAX_LENGTH - suffix.length))}${suffix}`;
|
|
505
|
+
}
|
|
506
|
+
return candidate;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export function truncateToolText(text: string): string {
|
|
510
|
+
const lines = text.split("\n");
|
|
511
|
+
const lineLimited = lines.slice(0, TOOL_OUTPUT_MAX_LINES).join("\n");
|
|
512
|
+
const bytes = Buffer.from(lineLimited, "utf8");
|
|
513
|
+
let byteEnd = Math.min(bytes.length, TOOL_OUTPUT_MAX_BYTES);
|
|
514
|
+
// Avoid returning a replacement character when the byte limit lands in the
|
|
515
|
+
// middle of a UTF-8 code point.
|
|
516
|
+
while (byteEnd > 0 && byteEnd < bytes.length && (bytes[byteEnd] & 0xc0) === 0x80) byteEnd -= 1;
|
|
517
|
+
const content = bytes.subarray(0, byteEnd).toString("utf8");
|
|
518
|
+
const truncatedByLines = lines.length > TOOL_OUTPUT_MAX_LINES;
|
|
519
|
+
const truncatedByBytes = bytes.length > TOOL_OUTPUT_MAX_BYTES;
|
|
520
|
+
if (!truncatedByLines && !truncatedByBytes) return content;
|
|
521
|
+
const reasons = [
|
|
522
|
+
...(truncatedByLines ? [`${TOOL_OUTPUT_MAX_LINES}-line limit`] : []),
|
|
523
|
+
...(truncatedByBytes ? [`${TOOL_OUTPUT_MAX_BYTES / 1024}KB limit`] : []),
|
|
524
|
+
];
|
|
525
|
+
return `${content}\n\n[MCP output truncated: ${reasons.join(" and ")}. Refine the request for less output.]`;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** A short human-readable line for the permission card's detail. */
|
|
529
|
+
function summarizeParams(params: unknown): string {
|
|
530
|
+
try {
|
|
531
|
+
const s = JSON.stringify(params ?? {});
|
|
532
|
+
return s === "{}" ? "" : s.slice(0, 300);
|
|
533
|
+
} catch {
|
|
534
|
+
return "";
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export default async function (pi: PiExtensionApi): Promise<void> {
|
|
539
|
+
const configPath = process.env.OMB_MCP_CONFIG;
|
|
540
|
+
if (!configPath) return;
|
|
541
|
+
|
|
542
|
+
let config: McpConfig;
|
|
543
|
+
try {
|
|
544
|
+
config = JSON.parse(readFileSync(configPath, "utf8")) as McpConfig;
|
|
545
|
+
} catch {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const used = new Set<string>();
|
|
550
|
+
const clients: StdioMcp[] = [];
|
|
551
|
+
const serverEntries = Object.entries(config.mcpServers ?? {});
|
|
552
|
+
|
|
553
|
+
// Mount independent servers concurrently so one slow integration cannot
|
|
554
|
+
// consume the startup timeout once per server. Registration remains in
|
|
555
|
+
// config order below for deterministic tool names and collision suffixes.
|
|
556
|
+
const mounts = await Promise.all(
|
|
557
|
+
serverEntries.map(async ([serverName, def]) => {
|
|
558
|
+
if (!def || typeof def.command !== "string" || !def.command.trim()) return { serverName };
|
|
559
|
+
let client: StdioMcp | undefined;
|
|
560
|
+
try {
|
|
561
|
+
client = new StdioMcp(def);
|
|
562
|
+
await client.init();
|
|
563
|
+
return { serverName, def, client, tools: await client.listTools() };
|
|
564
|
+
} catch (err) {
|
|
565
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
566
|
+
process.stderr.write(`[openmausbot-pi-mcp] ${serverName}: ${message}\n`);
|
|
567
|
+
client?.dispose();
|
|
568
|
+
return { serverName };
|
|
569
|
+
}
|
|
570
|
+
}),
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
for (const mount of mounts) {
|
|
574
|
+
if (!mount.client || !mount.def || !mount.tools) continue;
|
|
575
|
+
const { serverName, def, client, tools } = mount;
|
|
576
|
+
const gated = def.scope === "local-computer";
|
|
577
|
+
let registered = 0;
|
|
578
|
+
|
|
579
|
+
for (const tool of tools) {
|
|
580
|
+
if (!tool || typeof tool.name !== "string" || !tool.name.trim()) {
|
|
581
|
+
process.stderr.write(`[openmausbot-pi-mcp] ${serverName}: skipped a tool with no valid name\n`);
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const toolName = tool.name;
|
|
585
|
+
const name = allocateToolName(serverName, toolName, used);
|
|
586
|
+
try {
|
|
587
|
+
const parameters = toTypebox(tool.inputSchema);
|
|
588
|
+
pi.registerTool({
|
|
589
|
+
name,
|
|
590
|
+
label: `${serverName}:${toolName}`,
|
|
591
|
+
description: typeof tool.description === "string" ? tool.description : `${toolName} (MCP tool from ${serverName})`,
|
|
592
|
+
parameters,
|
|
593
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
594
|
+
// Host tools ask first, using pi's native permission card
|
|
595
|
+
// (ctx.ui.confirm → extension_ui_request → Allow/Deny card). This
|
|
596
|
+
// mirrors ACP's session/request_permission and Codex's elicitation.
|
|
597
|
+
if (gated) {
|
|
598
|
+
const detail = summarizeParams(params);
|
|
599
|
+
const allowed = await ctx.ui.confirm(
|
|
600
|
+
`Allow ${toolName} on your computer?`,
|
|
601
|
+
detail || `Run ${serverName}:${toolName}`,
|
|
602
|
+
);
|
|
603
|
+
if (!allowed) {
|
|
604
|
+
return { content: [{ type: "text", text: "Blocked by the user." }], details: {} };
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
const res = await client.callTool(toolName, params, signal);
|
|
608
|
+
if (res.isError) {
|
|
609
|
+
const message = res.content
|
|
610
|
+
.filter((item): item is { type: "text"; text: string } => item.type === "text")
|
|
611
|
+
.map((item) => item.text)
|
|
612
|
+
.join("\n");
|
|
613
|
+
// Pi marks a custom tool as failed only when execute throws;
|
|
614
|
+
// returning error-looking text incorrectly produces isError=false.
|
|
615
|
+
throw new Error(message || `MCP tool ${serverName}:${toolName} returned an error`);
|
|
616
|
+
}
|
|
617
|
+
return { content: res.content, details: {} };
|
|
618
|
+
},
|
|
619
|
+
});
|
|
620
|
+
used.add(name);
|
|
621
|
+
registered += 1;
|
|
622
|
+
} catch (err) {
|
|
623
|
+
// One malformed tool must not dispose the client behind tools that
|
|
624
|
+
// were already registered from the same server.
|
|
625
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
626
|
+
process.stderr.write(`[openmausbot-pi-mcp] ${serverName}:${toolName}: ${message}\n`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (registered > 0) clients.push(client);
|
|
631
|
+
else client.dispose();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
pi.on("session_shutdown", () => {
|
|
635
|
+
for (const c of clients) c.dispose();
|
|
636
|
+
clients.length = 0;
|
|
637
|
+
});
|
|
638
|
+
}
|