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,1233 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// scripts/mcp-server.ts
|
|
4
|
+
import readline from "node:readline";
|
|
5
|
+
function validateBaseUrl(url) {
|
|
6
|
+
const trimmed = url.replace(/\/+$/, "");
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = new URL(trimmed);
|
|
10
|
+
} catch {
|
|
11
|
+
throw new Error(`Invalid OpenMausBot URL: '${url}'`);
|
|
12
|
+
}
|
|
13
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
14
|
+
throw new Error("OpenMausBot URL must use http:// or https://");
|
|
15
|
+
}
|
|
16
|
+
if (parsed.username || parsed.password) {
|
|
17
|
+
throw new Error("OpenMausBot URL must not contain credentials; use OPENMAUSBOT_TOKEN instead");
|
|
18
|
+
}
|
|
19
|
+
if (parsed.pathname !== "/" && parsed.pathname !== "" || parsed.search || parsed.hash) {
|
|
20
|
+
throw new Error("OpenMausBot URL must be an origin without a path, query, or fragment");
|
|
21
|
+
}
|
|
22
|
+
const hostname = parsed.hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
23
|
+
const isLoopback = hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1";
|
|
24
|
+
if (parsed.protocol === "http:" && !isLoopback && process.env.ALLOW_INSECURE_HTTP !== "true") {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Insecure cleartext HTTP origin '${parsed.origin}' is rejected. Use https:// or set ALLOW_INSECURE_HTTP=true.`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return parsed.origin;
|
|
30
|
+
}
|
|
31
|
+
var configuredUrl = process.env.OPENMAUSBOT_URL || (process.env.OMB_PORT ? `http://127.0.0.1:${process.env.OMB_PORT}` : void 0);
|
|
32
|
+
var OMB_BASE_URL = validateBaseUrl(configuredUrl || "http://127.0.0.1:8799");
|
|
33
|
+
var DISCOVERY_URLS = configuredUrl ? [OMB_BASE_URL] : [8799, 18799, 28799].map((port) => `http://127.0.0.1:${port}`);
|
|
34
|
+
var discoveredBaseUrl;
|
|
35
|
+
function log(msg) {
|
|
36
|
+
process.stderr.write(`[openmausbot-mcp] ${msg}
|
|
37
|
+
`);
|
|
38
|
+
}
|
|
39
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
40
|
+
function requestTimeoutMs() {
|
|
41
|
+
const raw = Number(process.env.OPENMAUSBOT_MCP_TIMEOUT_MS);
|
|
42
|
+
return Number.isFinite(raw) && raw >= 1e3 && raw <= 12e4 ? Math.floor(raw) : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
43
|
+
}
|
|
44
|
+
function requestHeaders(options) {
|
|
45
|
+
const token = process.env.OPENMAUSBOT_TOKEN?.trim();
|
|
46
|
+
const headers = new Headers(options.headers);
|
|
47
|
+
if (!headers.has("Content-Type")) headers.set("Content-Type", "application/json");
|
|
48
|
+
if (token && !headers.has("Authorization")) headers.set("Authorization", `Bearer ${token}`);
|
|
49
|
+
return headers;
|
|
50
|
+
}
|
|
51
|
+
async function fetchJson(url, options = {}) {
|
|
52
|
+
const timeout = AbortSignal.timeout(requestTimeoutMs());
|
|
53
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
54
|
+
const response = await fetch(url, {
|
|
55
|
+
...options,
|
|
56
|
+
signal,
|
|
57
|
+
headers: requestHeaders(options)
|
|
58
|
+
});
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const text = await response.text().catch(() => "");
|
|
61
|
+
if (response.status === 403 && !process.env.OPENMAUSBOT_TOKEN?.trim()) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
"OpenMausBot refused this write because the installed desktop app requires a paired session token. Set OPENMAUSBOT_TOKEN as described in docs/mcp-server.md."
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
throw new Error(`OpenMausBot API error (${response.status}): ${text || response.statusText}`);
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return await response.json();
|
|
70
|
+
} catch {
|
|
71
|
+
throw new Error(`OpenMausBot API returned a non-JSON response from ${url}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function probeBaseUrls(candidates) {
|
|
75
|
+
const failures = [];
|
|
76
|
+
for (const unvalidated of candidates) {
|
|
77
|
+
const candidate = validateBaseUrl(unvalidated);
|
|
78
|
+
try {
|
|
79
|
+
const health = await fetchJson(`${candidate}/api/health`, {
|
|
80
|
+
signal: AbortSignal.timeout(Math.min(requestTimeoutMs(), 2e3))
|
|
81
|
+
});
|
|
82
|
+
if (health?.app !== "openmausbot") {
|
|
83
|
+
failures.push(`${candidate} answered, but it was not OpenMausBot`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
return candidate;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
failures.push(`${candidate}: ${error instanceof Error ? error.message : String(error)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw new Error(`Could not find a running OpenMausBot server. ${failures.join("; ")}`);
|
|
92
|
+
}
|
|
93
|
+
async function resolveBaseUrl() {
|
|
94
|
+
if (discoveredBaseUrl) return discoveredBaseUrl;
|
|
95
|
+
if (process.env.OPENMAUSBOT_TOKEN?.trim() && !configuredUrl) {
|
|
96
|
+
throw new Error("Set OPENMAUSBOT_URL or OMB_PORT when using OPENMAUSBOT_TOKEN so credentials are never sent during port discovery");
|
|
97
|
+
}
|
|
98
|
+
discoveredBaseUrl = await probeBaseUrls(DISCOVERY_URLS);
|
|
99
|
+
return discoveredBaseUrl;
|
|
100
|
+
}
|
|
101
|
+
async function request(path, options = {}, baseUrl) {
|
|
102
|
+
const target = baseUrl ? validateBaseUrl(baseUrl) : await resolveBaseUrl();
|
|
103
|
+
return fetchJson(`${target}${path}`, options);
|
|
104
|
+
}
|
|
105
|
+
var READ_ONLY = { readOnlyHint: true, destructiveHint: false, openWorldHint: false };
|
|
106
|
+
var ADDITIVE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
107
|
+
var MUTATING = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
108
|
+
var DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false };
|
|
109
|
+
var AGENT_ACTION = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true };
|
|
110
|
+
var TOOLS = [
|
|
111
|
+
{
|
|
112
|
+
name: "get_system_health",
|
|
113
|
+
description: "Check whether the OpenMausBot server is reachable.",
|
|
114
|
+
inputSchema: {
|
|
115
|
+
type: "object",
|
|
116
|
+
properties: {},
|
|
117
|
+
additionalProperties: false
|
|
118
|
+
},
|
|
119
|
+
annotations: READ_ONLY
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "list_bots",
|
|
123
|
+
description: "List bots, their current status, active task, and available tasks without loading transcripts.",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {},
|
|
127
|
+
additionalProperties: false
|
|
128
|
+
},
|
|
129
|
+
annotations: READ_ONLY
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "get_bot_messages",
|
|
133
|
+
description: "Retrieve a bounded page of recent messages from one bot task. Images are never returned inline.",
|
|
134
|
+
inputSchema: {
|
|
135
|
+
type: "object",
|
|
136
|
+
properties: {
|
|
137
|
+
bot_id: { type: "string", description: "The ID of the bot." },
|
|
138
|
+
task_id: { type: "string", description: "Optional task/thread ID. Defaults to the bot's active task." },
|
|
139
|
+
limit: { type: "integer", minimum: 1, maximum: 200, description: "Messages to retrieve (default: 30, max: 200)." }
|
|
140
|
+
},
|
|
141
|
+
required: ["bot_id"],
|
|
142
|
+
additionalProperties: false
|
|
143
|
+
},
|
|
144
|
+
annotations: READ_ONLY
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "send_bot_message",
|
|
148
|
+
description: "Send an instruction to a bot's active task. Optionally name the expected task to prevent cross-task races. This may cause the bot to use external tools.",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
bot_id: { type: "string", description: "The ID of the bot to message." },
|
|
153
|
+
task_id: { type: "string", description: "Optional expected active task/thread ID." },
|
|
154
|
+
text: { type: "string", description: "The message content/instruction to send." }
|
|
155
|
+
},
|
|
156
|
+
required: ["bot_id", "text"],
|
|
157
|
+
additionalProperties: false
|
|
158
|
+
},
|
|
159
|
+
annotations: AGENT_ACTION
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "create_bot",
|
|
163
|
+
description: "Create a new bot and optionally configure its profile, section, and exact model selection.",
|
|
164
|
+
inputSchema: {
|
|
165
|
+
type: "object",
|
|
166
|
+
properties: {
|
|
167
|
+
name: { type: "string", description: "Bot display name." },
|
|
168
|
+
title: { type: "string", description: "Optional short role title." },
|
|
169
|
+
description: { type: "string", description: "Optional persona or responsibility description." },
|
|
170
|
+
section: { type: "string", description: "Optional sidebar section." },
|
|
171
|
+
instance_id: { type: "string", description: "Optional provider instance ID; model is required with it." },
|
|
172
|
+
model: { type: "string", description: "Optional exact model ID; instance_id is required with it." },
|
|
173
|
+
effort: { type: "string", enum: ["none", "low", "medium", "high", "xhigh", "max"] }
|
|
174
|
+
},
|
|
175
|
+
required: ["name"],
|
|
176
|
+
additionalProperties: false
|
|
177
|
+
},
|
|
178
|
+
annotations: ADDITIVE
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "update_bot_profile",
|
|
182
|
+
description: "Update safe bot profile fields. This cannot alter permissions, computer access, or approval settings.",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
type: "object",
|
|
185
|
+
properties: {
|
|
186
|
+
bot_id: { type: "string", description: "The ID of the bot." },
|
|
187
|
+
name: { type: "string", description: "Optional bot display name." },
|
|
188
|
+
title: { type: "string", description: "Optional short role title." },
|
|
189
|
+
description: { type: "string", description: "Optional persona or responsibility description." },
|
|
190
|
+
section: { type: ["string", "null"], description: "Optional sidebar section. Null clears it." }
|
|
191
|
+
},
|
|
192
|
+
required: ["bot_id"],
|
|
193
|
+
additionalProperties: false
|
|
194
|
+
},
|
|
195
|
+
annotations: MUTATING
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "list_channels",
|
|
199
|
+
description: "List multi-agent channels, their members, active task, and available tasks without loading transcripts.",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {},
|
|
203
|
+
additionalProperties: false
|
|
204
|
+
},
|
|
205
|
+
annotations: READ_ONLY
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "get_channel_messages",
|
|
209
|
+
description: "Retrieve a bounded page of recent messages from one channel task. Images are never returned inline.",
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
channel_id: { type: "string", description: "The ID of the channel." },
|
|
214
|
+
task_id: { type: "string", description: "Optional task/thread ID. Defaults to the channel's active task." },
|
|
215
|
+
limit: { type: "integer", minimum: 1, maximum: 200, description: "Messages to retrieve (default: 30, max: 200)." }
|
|
216
|
+
},
|
|
217
|
+
required: ["channel_id"],
|
|
218
|
+
additionalProperties: false
|
|
219
|
+
},
|
|
220
|
+
annotations: READ_ONLY
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "send_channel_message",
|
|
224
|
+
description: "Send an instruction to a channel's active task. Optionally name the expected task to prevent cross-task races. This may cause one or more bots to use external tools.",
|
|
225
|
+
inputSchema: {
|
|
226
|
+
type: "object",
|
|
227
|
+
properties: {
|
|
228
|
+
channel_id: { type: "string", description: "The ID of the channel." },
|
|
229
|
+
task_id: { type: "string", description: "Optional expected active task/thread ID." },
|
|
230
|
+
text: { type: "string", description: "The message content to post." }
|
|
231
|
+
},
|
|
232
|
+
required: ["channel_id", "text"],
|
|
233
|
+
additionalProperties: false
|
|
234
|
+
},
|
|
235
|
+
annotations: AGENT_ACTION
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "create_channel",
|
|
239
|
+
description: "Create a multi-agent channel from existing bots.",
|
|
240
|
+
inputSchema: {
|
|
241
|
+
type: "object",
|
|
242
|
+
properties: {
|
|
243
|
+
name: { type: "string", description: "Channel name." },
|
|
244
|
+
member_ids: { type: "array", items: { type: "string" }, minItems: 1, uniqueItems: true },
|
|
245
|
+
section: { type: "string", description: "Optional sidebar section." },
|
|
246
|
+
bulletin: { type: "string", description: "Optional shared instructions for channel members." },
|
|
247
|
+
default_responder: {
|
|
248
|
+
oneOf: [
|
|
249
|
+
{ type: "object", properties: { kind: { const: "everyone" } }, required: ["kind"], additionalProperties: false },
|
|
250
|
+
{ type: "object", properties: { kind: { const: "mentions" } }, required: ["kind"], additionalProperties: false },
|
|
251
|
+
{ type: "object", properties: { kind: { const: "member" }, bot_id: { type: "string" } }, required: ["kind", "bot_id"], additionalProperties: false }
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
required: ["name", "member_ids"],
|
|
256
|
+
additionalProperties: false
|
|
257
|
+
},
|
|
258
|
+
annotations: ADDITIVE
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: "update_channel",
|
|
262
|
+
description: "Update a channel's name, members, section, bulletin, or default responder.",
|
|
263
|
+
inputSchema: {
|
|
264
|
+
type: "object",
|
|
265
|
+
properties: {
|
|
266
|
+
channel_id: { type: "string", description: "The ID of the channel." },
|
|
267
|
+
name: { type: "string" },
|
|
268
|
+
member_ids: { type: "array", items: { type: "string" }, minItems: 1, uniqueItems: true },
|
|
269
|
+
section: { type: ["string", "null"], description: "Null clears the section." },
|
|
270
|
+
bulletin: { type: "string" },
|
|
271
|
+
default_responder: {
|
|
272
|
+
oneOf: [
|
|
273
|
+
{ type: "object", properties: { kind: { const: "everyone" } }, required: ["kind"], additionalProperties: false },
|
|
274
|
+
{ type: "object", properties: { kind: { const: "mentions" } }, required: ["kind"], additionalProperties: false },
|
|
275
|
+
{ type: "object", properties: { kind: { const: "member" }, bot_id: { type: "string" } }, required: ["kind", "bot_id"], additionalProperties: false }
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
required: ["channel_id"],
|
|
280
|
+
additionalProperties: false
|
|
281
|
+
},
|
|
282
|
+
annotations: MUTATING
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: "create_task",
|
|
286
|
+
description: "Create and activate a fresh conversation task for a bot or user-created channel.",
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: "object",
|
|
289
|
+
properties: {
|
|
290
|
+
target_type: { type: "string", enum: ["bot", "channel"] },
|
|
291
|
+
target_id: { type: "string" },
|
|
292
|
+
title: { type: "string", description: "Optional task title." }
|
|
293
|
+
},
|
|
294
|
+
required: ["target_type", "target_id"],
|
|
295
|
+
additionalProperties: false
|
|
296
|
+
},
|
|
297
|
+
annotations: ADDITIVE
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "switch_task",
|
|
301
|
+
description: "Switch a bot or channel to an existing task. Running or approval-blocked conversations are refused.",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
target_type: { type: "string", enum: ["bot", "channel"] },
|
|
306
|
+
target_id: { type: "string" },
|
|
307
|
+
task_id: { type: "string" }
|
|
308
|
+
},
|
|
309
|
+
required: ["target_type", "target_id", "task_id"],
|
|
310
|
+
additionalProperties: false
|
|
311
|
+
},
|
|
312
|
+
annotations: MUTATING
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "rename_task",
|
|
316
|
+
description: "Rename an existing bot or channel task.",
|
|
317
|
+
inputSchema: {
|
|
318
|
+
type: "object",
|
|
319
|
+
properties: {
|
|
320
|
+
target_type: { type: "string", enum: ["bot", "channel"] },
|
|
321
|
+
target_id: { type: "string" },
|
|
322
|
+
task_id: { type: "string" },
|
|
323
|
+
title: { type: "string" }
|
|
324
|
+
},
|
|
325
|
+
required: ["target_type", "target_id", "task_id", "title"],
|
|
326
|
+
additionalProperties: false
|
|
327
|
+
},
|
|
328
|
+
annotations: MUTATING
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: "search_messages",
|
|
332
|
+
description: "Search local transcripts, optionally within one task/thread. Returns at most 100 compact hits.",
|
|
333
|
+
inputSchema: {
|
|
334
|
+
type: "object",
|
|
335
|
+
properties: {
|
|
336
|
+
query: { type: "string" },
|
|
337
|
+
task_id: { type: "string", description: "Optional task/thread ID." },
|
|
338
|
+
limit: { type: "integer", minimum: 1, maximum: 100, description: "Maximum hits (default: 40)." }
|
|
339
|
+
},
|
|
340
|
+
required: ["query"],
|
|
341
|
+
additionalProperties: false
|
|
342
|
+
},
|
|
343
|
+
annotations: READ_ONLY
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
name: "wait_for_conversation",
|
|
347
|
+
description: "Wait for a bot or channel task to finish, stall, fail, or require user input.",
|
|
348
|
+
inputSchema: {
|
|
349
|
+
type: "object",
|
|
350
|
+
properties: {
|
|
351
|
+
target_type: { type: "string", enum: ["bot", "channel"] },
|
|
352
|
+
target_id: { type: "string" },
|
|
353
|
+
task_id: { type: "string", description: "Optional task/thread ID. Defaults to the active task." },
|
|
354
|
+
timeout_seconds: { type: "integer", minimum: 1, maximum: 120, description: "Maximum wait (default: 30 seconds)." }
|
|
355
|
+
},
|
|
356
|
+
required: ["target_type", "target_id"],
|
|
357
|
+
additionalProperties: false
|
|
358
|
+
},
|
|
359
|
+
annotations: READ_ONLY
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: "set_bot_model",
|
|
363
|
+
description: "Change an idle bot to an exact configured provider instance and model.",
|
|
364
|
+
inputSchema: {
|
|
365
|
+
type: "object",
|
|
366
|
+
properties: {
|
|
367
|
+
bot_id: { type: "string", description: "The ID of the bot." },
|
|
368
|
+
instance_id: { type: "string", description: "The configured provider instance ID." },
|
|
369
|
+
model: { type: "string", description: "The exact model ID exposed by that instance." },
|
|
370
|
+
effort: { type: "string", enum: ["none", "low", "medium", "high", "xhigh", "max"] }
|
|
371
|
+
},
|
|
372
|
+
required: ["bot_id", "instance_id", "model"],
|
|
373
|
+
additionalProperties: false
|
|
374
|
+
},
|
|
375
|
+
annotations: MUTATING
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: "list_available_models",
|
|
379
|
+
description: "List configured model instances and capabilities without exposing local executable paths.",
|
|
380
|
+
inputSchema: {
|
|
381
|
+
type: "object",
|
|
382
|
+
properties: {},
|
|
383
|
+
additionalProperties: false
|
|
384
|
+
},
|
|
385
|
+
annotations: READ_ONLY
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
name: "interrupt_conversation",
|
|
389
|
+
description: "Interrupt the active turn in a bot or channel conversation.",
|
|
390
|
+
inputSchema: {
|
|
391
|
+
type: "object",
|
|
392
|
+
properties: {
|
|
393
|
+
target_type: { type: "string", enum: ["bot", "channel"] },
|
|
394
|
+
target_id: { type: "string" }
|
|
395
|
+
},
|
|
396
|
+
required: ["target_type", "target_id"],
|
|
397
|
+
additionalProperties: false
|
|
398
|
+
},
|
|
399
|
+
annotations: DESTRUCTIVE
|
|
400
|
+
}
|
|
401
|
+
];
|
|
402
|
+
function parsePositiveLimit(raw, fallback = 30, maximum = 200) {
|
|
403
|
+
const parsed = Math.floor(Number(raw));
|
|
404
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.min(parsed, maximum) : fallback;
|
|
405
|
+
}
|
|
406
|
+
function isRecord(value) {
|
|
407
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
408
|
+
}
|
|
409
|
+
function valueHasType(value, type) {
|
|
410
|
+
if (type === "null") return value === null;
|
|
411
|
+
if (type === "array") return Array.isArray(value);
|
|
412
|
+
if (type === "object") return isRecord(value);
|
|
413
|
+
if (type === "integer") return typeof value === "number" && Number.isInteger(value);
|
|
414
|
+
if (type === "number") return typeof value === "number" && Number.isFinite(value);
|
|
415
|
+
return typeof value === type;
|
|
416
|
+
}
|
|
417
|
+
function schemaError(schema, value, path) {
|
|
418
|
+
if (schema.const !== void 0 && value !== schema.const) return `${path} must equal ${JSON.stringify(schema.const)}`;
|
|
419
|
+
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) return `${path} must be one of ${schema.enum.join(", ")}`;
|
|
420
|
+
if (Array.isArray(schema.oneOf)) {
|
|
421
|
+
const matches = schema.oneOf.filter((candidate) => !schemaError(candidate, value, path));
|
|
422
|
+
return matches.length === 1 ? null : `${path} does not match exactly one supported shape`;
|
|
423
|
+
}
|
|
424
|
+
if (schema.type !== void 0) {
|
|
425
|
+
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
426
|
+
if (!types.some((type) => valueHasType(value, type))) return `${path} must be ${types.join(" or ")}`;
|
|
427
|
+
}
|
|
428
|
+
if (typeof value === "number") {
|
|
429
|
+
if (typeof schema.minimum === "number" && value < schema.minimum) return `${path} must be at least ${schema.minimum}`;
|
|
430
|
+
if (typeof schema.maximum === "number" && value > schema.maximum) return `${path} must be at most ${schema.maximum}`;
|
|
431
|
+
}
|
|
432
|
+
if (Array.isArray(value)) {
|
|
433
|
+
if (typeof schema.minItems === "number" && value.length < schema.minItems) return `${path} needs at least ${schema.minItems} item(s)`;
|
|
434
|
+
if (schema.uniqueItems && new Set(value.map((item) => JSON.stringify(item))).size !== value.length) {
|
|
435
|
+
return `${path} must not contain duplicates`;
|
|
436
|
+
}
|
|
437
|
+
if (schema.items) {
|
|
438
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
439
|
+
const error = schemaError(schema.items, value[index], `${path}[${index}]`);
|
|
440
|
+
if (error) return error;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (isRecord(value)) {
|
|
445
|
+
const properties = isRecord(schema.properties) ? schema.properties : {};
|
|
446
|
+
for (const key of schema.required ?? []) {
|
|
447
|
+
if (!(key in value)) return `${path}.${key} is required`;
|
|
448
|
+
}
|
|
449
|
+
if (schema.additionalProperties === false) {
|
|
450
|
+
const extra = Object.keys(value).find((key) => !(key in properties));
|
|
451
|
+
if (extra) return `${path}.${extra} is not supported`;
|
|
452
|
+
}
|
|
453
|
+
for (const [key, child] of Object.entries(properties)) {
|
|
454
|
+
if (!(key in value)) continue;
|
|
455
|
+
const error = schemaError(child, value[key], `${path}.${key}`);
|
|
456
|
+
if (error) return error;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
var ToolInputError = class extends Error {
|
|
462
|
+
};
|
|
463
|
+
function validateToolArguments(name, args) {
|
|
464
|
+
if (typeof name !== "string" || !name) throw new ToolInputError("tool name must be a non-empty string");
|
|
465
|
+
const tool = TOOLS.find((candidate) => candidate.name === name);
|
|
466
|
+
if (!tool) throw new ToolInputError(`Unknown tool: ${name}`);
|
|
467
|
+
if (!isRecord(args)) throw new ToolInputError("tool arguments must be an object");
|
|
468
|
+
const error = schemaError(tool.inputSchema, args, "arguments");
|
|
469
|
+
if (error) throw new ToolInputError(error);
|
|
470
|
+
}
|
|
471
|
+
function stringArg(args, key, options = {}) {
|
|
472
|
+
const raw = args[key];
|
|
473
|
+
if (typeof raw !== "string") throw new ToolInputError(`${key} must be a string`);
|
|
474
|
+
const value = options.trim === false ? raw : raw.trim();
|
|
475
|
+
if (!options.allowEmpty && !value) throw new ToolInputError(`${key} must not be empty`);
|
|
476
|
+
if (options.max && value.length > options.max) throw new ToolInputError(`${key} must be at most ${options.max} characters`);
|
|
477
|
+
return value;
|
|
478
|
+
}
|
|
479
|
+
function optionalStringArg(args, key, options = {}) {
|
|
480
|
+
if (!(key in args)) return void 0;
|
|
481
|
+
return stringArg(args, key, options);
|
|
482
|
+
}
|
|
483
|
+
function idArg(args, key) {
|
|
484
|
+
const value = stringArg(args, key);
|
|
485
|
+
if (!/^[\w-]+$/.test(value)) throw new ToolInputError(`${key} is not a valid OpenMausBot ID`);
|
|
486
|
+
return value;
|
|
487
|
+
}
|
|
488
|
+
function stringArrayArg(args, key) {
|
|
489
|
+
const value = args[key];
|
|
490
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((item) => typeof item !== "string" || !item.trim())) {
|
|
491
|
+
throw new ToolInputError(`${key} must be a non-empty list of IDs`);
|
|
492
|
+
}
|
|
493
|
+
return [...new Set(value.map((item) => item.trim()))];
|
|
494
|
+
}
|
|
495
|
+
function records(value) {
|
|
496
|
+
return Array.isArray(value) ? value.filter(isRecord) : [];
|
|
497
|
+
}
|
|
498
|
+
function projectTask(task, activeThreadId) {
|
|
499
|
+
return {
|
|
500
|
+
taskId: task.threadId,
|
|
501
|
+
title: task.title,
|
|
502
|
+
createdAt: task.createdAt,
|
|
503
|
+
...typeof activeThreadId === "string" ? { active: task.threadId === activeThreadId } : {},
|
|
504
|
+
...task.usage ? { usage: task.usage } : {}
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function projectBot(bot) {
|
|
508
|
+
return {
|
|
509
|
+
id: bot.id,
|
|
510
|
+
name: bot.name,
|
|
511
|
+
title: bot.title,
|
|
512
|
+
description: bot.description,
|
|
513
|
+
section: bot.section ?? null,
|
|
514
|
+
chiefOfStaff: Boolean(bot.chiefOfStaff),
|
|
515
|
+
modelSelection: bot.modelSelection,
|
|
516
|
+
busy: Boolean(bot.busy),
|
|
517
|
+
activity: bot.activity,
|
|
518
|
+
unread: Boolean(bot.unread),
|
|
519
|
+
activeTaskId: bot.threadId,
|
|
520
|
+
tasks: records(bot.tasks).map((task) => projectTask(task, bot.threadId))
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
function projectChannel(channel) {
|
|
524
|
+
return {
|
|
525
|
+
id: channel.id,
|
|
526
|
+
name: channel.name,
|
|
527
|
+
memberIds: channel.memberIds,
|
|
528
|
+
bulletin: channel.bulletin,
|
|
529
|
+
defaultResponder: channel.defaultResponder,
|
|
530
|
+
section: channel.section ?? null,
|
|
531
|
+
directMessage: Boolean(channel.dm),
|
|
532
|
+
working: Boolean(channel.working),
|
|
533
|
+
busyBotId: channel.busyBotId ?? null,
|
|
534
|
+
activeTaskId: channel.threadId,
|
|
535
|
+
tasks: records(channel.tasks).map((task) => projectTask(task, channel.threadId))
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
function projectMessage(message) {
|
|
539
|
+
const card = isRecord(message.card) ? {
|
|
540
|
+
title: message.card.title,
|
|
541
|
+
subtitle: message.card.subtitle,
|
|
542
|
+
options: message.card.options,
|
|
543
|
+
answered: message.card.answered,
|
|
544
|
+
dismissed: message.card.dismissed
|
|
545
|
+
} : void 0;
|
|
546
|
+
const tool = isRecord(message.tool) ? { name: message.tool.name, ok: message.tool.ok, spoken: message.tool.spoken, setup: message.tool.setup } : void 0;
|
|
547
|
+
const connector = isRecord(message.connector) ? {
|
|
548
|
+
slug: message.connector.slug,
|
|
549
|
+
label: message.connector.label,
|
|
550
|
+
description: message.connector.description,
|
|
551
|
+
status: message.connector.status,
|
|
552
|
+
dismissed: message.connector.dismissed,
|
|
553
|
+
resumed: message.connector.resumed
|
|
554
|
+
} : void 0;
|
|
555
|
+
const secret = isRecord(message.secret) ? {
|
|
556
|
+
target: message.secret.target,
|
|
557
|
+
label: message.secret.label,
|
|
558
|
+
description: message.secret.description,
|
|
559
|
+
placeholder: message.secret.placeholder,
|
|
560
|
+
helpUrl: message.secret.helpUrl,
|
|
561
|
+
provided: message.secret.provided,
|
|
562
|
+
dismissed: message.secret.dismissed,
|
|
563
|
+
resumed: message.secret.resumed
|
|
564
|
+
} : void 0;
|
|
565
|
+
return {
|
|
566
|
+
id: message.id,
|
|
567
|
+
at: message.at,
|
|
568
|
+
role: message.role,
|
|
569
|
+
kind: message.kind,
|
|
570
|
+
text: message.text,
|
|
571
|
+
from: message.from,
|
|
572
|
+
replyToId: message.replyToId,
|
|
573
|
+
reactions: message.reactions,
|
|
574
|
+
steered: message.steered,
|
|
575
|
+
queued: message.queued,
|
|
576
|
+
...tool ? { tool } : {},
|
|
577
|
+
...card ? { card } : {},
|
|
578
|
+
...connector ? { connector } : {},
|
|
579
|
+
...secret ? { secret } : {},
|
|
580
|
+
...message.kind === "screen" ? { hasImage: Boolean(message.hasImage || message.png) } : {}
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
async function fleet(fetcher) {
|
|
584
|
+
return fetcher("/api/bots?messages=0");
|
|
585
|
+
}
|
|
586
|
+
function taskBelongsTo(owner, taskId) {
|
|
587
|
+
return owner.threadId === taskId || records(owner.tasks).some((task) => task.threadId === taskId);
|
|
588
|
+
}
|
|
589
|
+
function messageNeedsInput(message) {
|
|
590
|
+
const card = isRecord(message.card) && message.card.requestId && !message.card.answered && !message.card.dismissed;
|
|
591
|
+
const connector = isRecord(message.connector) && !message.connector.dismissed && !message.connector.resumed && message.connector.status !== "connected";
|
|
592
|
+
const secret = isRecord(message.secret) && !message.secret.provided && !message.secret.dismissed;
|
|
593
|
+
return Boolean(card || connector || secret);
|
|
594
|
+
}
|
|
595
|
+
function dispatchFailedAfterLatestUser(messages) {
|
|
596
|
+
const lastUser = messages.findLastIndex((message) => message.role === "user");
|
|
597
|
+
const turnMessages = messages.slice(lastUser + 1);
|
|
598
|
+
if (turnMessages.some((message) => message.role === "bot" && message.kind === "text" && message.text?.trim())) {
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
return turnMessages.some(
|
|
602
|
+
(message) => message.kind === "activity" && message.tool?.ok === false && typeof message.tool?.name === "string" && /^error:/i.test(message.tool.name.trim())
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
async function conversationTail(fetcher, taskId, limit = 10) {
|
|
606
|
+
const page = await fetcher(`/api/threads/${encodeURIComponent(taskId)}/messages?limit=${limit}`);
|
|
607
|
+
const raw = records(page.messages);
|
|
608
|
+
return {
|
|
609
|
+
raw,
|
|
610
|
+
messages: raw.map(projectMessage),
|
|
611
|
+
hasMore: Boolean(page.hasMore)
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
function normalizeResponder(value) {
|
|
615
|
+
if (value === void 0) return void 0;
|
|
616
|
+
if (!isRecord(value)) throw new ToolInputError("default_responder must be an object");
|
|
617
|
+
if (value.kind === "everyone" || value.kind === "mentions") return { kind: value.kind };
|
|
618
|
+
if (value.kind === "member" && typeof value.bot_id === "string" && value.bot_id.trim()) {
|
|
619
|
+
return { kind: "member", botId: value.bot_id.trim() };
|
|
620
|
+
}
|
|
621
|
+
throw new ToolInputError("default_responder is invalid");
|
|
622
|
+
}
|
|
623
|
+
async function checkedModelSelection(args, fetcher) {
|
|
624
|
+
const instanceId = stringArg(args, "instance_id");
|
|
625
|
+
const model = stringArg(args, "model");
|
|
626
|
+
const effort = optionalStringArg(args, "effort");
|
|
627
|
+
const described = await fetcher("/api/instances");
|
|
628
|
+
const instance = records(described.instances).find((candidate) => candidate.instanceId === instanceId);
|
|
629
|
+
if (!instance) throw new ToolInputError(`model instance not found: ${instanceId}`);
|
|
630
|
+
if (instance.snapshot?.state !== "available") throw new ToolInputError(`model instance is unavailable: ${instanceId}`);
|
|
631
|
+
const models = isRecord(instance.models) ? instance.models : {};
|
|
632
|
+
const offered = records(models.options).map((option) => option.id).filter((id) => typeof id === "string");
|
|
633
|
+
if (models.default !== model && !offered.includes(model)) {
|
|
634
|
+
throw new ToolInputError(`model '${model}' is not offered by instance '${instanceId}'`);
|
|
635
|
+
}
|
|
636
|
+
const efforts = Array.isArray(instance.capabilities?.effortLevels) ? instance.capabilities.effortLevels : [];
|
|
637
|
+
if (effort && !efforts.includes(effort)) {
|
|
638
|
+
throw new ToolInputError(`effort '${effort}' is not offered by instance '${instanceId}'`);
|
|
639
|
+
}
|
|
640
|
+
return { instanceId, model, ...effort ? { effort } : {} };
|
|
641
|
+
}
|
|
642
|
+
function taskRoute(targetType, targetId) {
|
|
643
|
+
if (targetType === "bot") return `/api/bots/${encodeURIComponent(targetId)}/tasks`;
|
|
644
|
+
if (targetType === "channel") return `/api/groups/${encodeURIComponent(targetId)}/tasks`;
|
|
645
|
+
throw new ToolInputError("target_type must be bot or channel");
|
|
646
|
+
}
|
|
647
|
+
function sleep(ms, signal) {
|
|
648
|
+
return new Promise((resolve, reject) => {
|
|
649
|
+
if (signal?.aborted) return reject(signal.reason ?? new Error("Request cancelled"));
|
|
650
|
+
const onAbort = () => {
|
|
651
|
+
clearTimeout(timer);
|
|
652
|
+
signal?.removeEventListener("abort", onAbort);
|
|
653
|
+
reject(signal?.reason ?? new Error("Request cancelled"));
|
|
654
|
+
};
|
|
655
|
+
const timer = setTimeout(() => {
|
|
656
|
+
signal?.removeEventListener("abort", onAbort);
|
|
657
|
+
resolve();
|
|
658
|
+
}, ms);
|
|
659
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
async function handleToolCall(name, args, baseFetcher = request, signal) {
|
|
663
|
+
const fetcher = signal ? (path, options = {}) => baseFetcher(path, { ...options, signal: options.signal ?? signal }) : baseFetcher;
|
|
664
|
+
validateToolArguments(name, args);
|
|
665
|
+
switch (name) {
|
|
666
|
+
case "get_system_health": {
|
|
667
|
+
const res = await fetcher("/api/health");
|
|
668
|
+
if (res?.app !== "openmausbot") throw new Error("The configured endpoint is not an OpenMausBot server");
|
|
669
|
+
return {
|
|
670
|
+
status: "connected",
|
|
671
|
+
endpoint: discoveredBaseUrl ?? OMB_BASE_URL,
|
|
672
|
+
app: "openmausbot",
|
|
673
|
+
packaged: Boolean(res.static)
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
case "list_bots": {
|
|
677
|
+
const res = await fleet(fetcher);
|
|
678
|
+
return { bots: records(res.bots).map(projectBot) };
|
|
679
|
+
}
|
|
680
|
+
case "get_bot_messages": {
|
|
681
|
+
const botId = idArg(args, "bot_id");
|
|
682
|
+
const res = await fleet(fetcher);
|
|
683
|
+
const bot = records(res.bots).find((candidate) => candidate.id === botId);
|
|
684
|
+
if (!bot) throw new Error(`Bot not found: ${botId}`);
|
|
685
|
+
const taskId = args.task_id === void 0 ? String(bot.threadId) : idArg(args, "task_id");
|
|
686
|
+
if (!taskBelongsTo(bot, taskId)) throw new Error(`Task '${taskId}' does not belong to bot '${botId}'`);
|
|
687
|
+
const limit = parsePositiveLimit(args.limit, 30, 200);
|
|
688
|
+
const page = await fetcher(`/api/threads/${encodeURIComponent(taskId)}/messages?limit=${limit}`);
|
|
689
|
+
return {
|
|
690
|
+
bot: projectBot(bot),
|
|
691
|
+
taskId,
|
|
692
|
+
messages: records(page.messages).map(projectMessage),
|
|
693
|
+
hasMore: Boolean(page.hasMore)
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
case "send_bot_message": {
|
|
697
|
+
const botId = idArg(args, "bot_id");
|
|
698
|
+
const text = stringArg(args, "text", { trim: true, max: 1e5 });
|
|
699
|
+
const state = await fleet(fetcher);
|
|
700
|
+
const bot = records(state.bots).find((candidate) => candidate.id === botId);
|
|
701
|
+
if (!bot) throw new Error(`Bot not found: ${botId}`);
|
|
702
|
+
const taskId = args.task_id === void 0 ? String(bot.threadId) : idArg(args, "task_id");
|
|
703
|
+
if (!taskBelongsTo(bot, taskId)) throw new Error(`Task '${taskId}' does not belong to bot '${botId}'`);
|
|
704
|
+
if (bot.threadId !== taskId) {
|
|
705
|
+
throw new Error(`Task '${taskId}' is not active for bot '${botId}'; switch to it before sending`);
|
|
706
|
+
}
|
|
707
|
+
const busyChannel = records(state.groups).find((channel) => channel.busyBotId === botId);
|
|
708
|
+
if (busyChannel) {
|
|
709
|
+
throw new Error(`Bot '${botId}' is working in channel '${busyChannel.id}'; send to or interrupt that channel instead`);
|
|
710
|
+
}
|
|
711
|
+
await fetcher(`/api/bots/${encodeURIComponent(botId)}/messages`, {
|
|
712
|
+
method: "POST",
|
|
713
|
+
body: JSON.stringify({ text, threadId: taskId })
|
|
714
|
+
});
|
|
715
|
+
return { success: true, botId, taskId };
|
|
716
|
+
}
|
|
717
|
+
case "create_bot": {
|
|
718
|
+
const name2 = stringArg(args, "name", { max: 100 });
|
|
719
|
+
const title = optionalStringArg(args, "title", { trim: false, allowEmpty: true, max: 200 });
|
|
720
|
+
const description = optionalStringArg(args, "description", { trim: false, allowEmpty: true, max: 4e3 });
|
|
721
|
+
const section = optionalStringArg(args, "section", { max: 60 });
|
|
722
|
+
const wantsModel = args.instance_id !== void 0 || args.model !== void 0 || args.effort !== void 0;
|
|
723
|
+
if (wantsModel && (args.instance_id === void 0 || args.model === void 0)) {
|
|
724
|
+
throw new ToolInputError("instance_id and model must be provided together");
|
|
725
|
+
}
|
|
726
|
+
const selection = wantsModel ? await checkedModelSelection(args, fetcher) : void 0;
|
|
727
|
+
const created = await fetcher("/api/bots", {
|
|
728
|
+
method: "POST",
|
|
729
|
+
body: JSON.stringify({
|
|
730
|
+
name: name2,
|
|
731
|
+
...title !== void 0 ? { title } : {},
|
|
732
|
+
...description !== void 0 ? { description } : {},
|
|
733
|
+
...section !== void 0 ? { section } : {},
|
|
734
|
+
...selection ? { modelSelection: selection, requireAvailableModel: true } : {}
|
|
735
|
+
})
|
|
736
|
+
});
|
|
737
|
+
if (!isRecord(created?.bot) || typeof created.bot.id !== "string") {
|
|
738
|
+
throw new Error("OpenMausBot did not return the created bot");
|
|
739
|
+
}
|
|
740
|
+
return { success: true, bot: projectBot(created.bot) };
|
|
741
|
+
}
|
|
742
|
+
case "update_bot_profile": {
|
|
743
|
+
const botId = idArg(args, "bot_id");
|
|
744
|
+
const patch = {};
|
|
745
|
+
if (args.name !== void 0) patch.name = stringArg(args, "name", { max: 100 });
|
|
746
|
+
if (args.title !== void 0) patch.title = stringArg(args, "title", { trim: false, allowEmpty: true, max: 200 });
|
|
747
|
+
if (args.description !== void 0) patch.description = stringArg(args, "description", { trim: false, allowEmpty: true, max: 4e3 });
|
|
748
|
+
if ("section" in args) patch.section = args.section === null ? null : stringArg(args, "section", { max: 60 });
|
|
749
|
+
if (!Object.keys(patch).length) throw new ToolInputError("provide at least one profile field to update");
|
|
750
|
+
const result = await fetcher(`/api/bots/${encodeURIComponent(botId)}`, {
|
|
751
|
+
method: "PATCH",
|
|
752
|
+
body: JSON.stringify(patch)
|
|
753
|
+
});
|
|
754
|
+
if (!isRecord(result?.bot)) {
|
|
755
|
+
throw new Error("OpenMausBot did not return the updated bot");
|
|
756
|
+
}
|
|
757
|
+
return { success: true, bot: projectBot(result.bot) };
|
|
758
|
+
}
|
|
759
|
+
case "list_channels": {
|
|
760
|
+
const res = await fleet(fetcher);
|
|
761
|
+
return { channels: records(res.groups).map(projectChannel) };
|
|
762
|
+
}
|
|
763
|
+
case "get_channel_messages": {
|
|
764
|
+
const channelId = idArg(args, "channel_id");
|
|
765
|
+
const res = await fleet(fetcher);
|
|
766
|
+
const channel = records(res.groups).find((candidate) => candidate.id === channelId);
|
|
767
|
+
if (!channel) throw new Error(`Channel not found: ${channelId}`);
|
|
768
|
+
const taskId = args.task_id === void 0 ? String(channel.threadId) : idArg(args, "task_id");
|
|
769
|
+
if (!taskBelongsTo(channel, taskId)) throw new Error(`Task '${taskId}' does not belong to channel '${channelId}'`);
|
|
770
|
+
const limit = parsePositiveLimit(args.limit, 30, 200);
|
|
771
|
+
const page = await fetcher(`/api/threads/${encodeURIComponent(taskId)}/messages?limit=${limit}`);
|
|
772
|
+
return {
|
|
773
|
+
channel: projectChannel(channel),
|
|
774
|
+
taskId,
|
|
775
|
+
messages: records(page.messages).map(projectMessage),
|
|
776
|
+
hasMore: Boolean(page.hasMore)
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
case "send_channel_message": {
|
|
780
|
+
const channelId = idArg(args, "channel_id");
|
|
781
|
+
const text = stringArg(args, "text", { trim: true, max: 1e5 });
|
|
782
|
+
const state = await fleet(fetcher);
|
|
783
|
+
const channel = records(state.groups).find((candidate) => candidate.id === channelId);
|
|
784
|
+
if (!channel) throw new Error(`Channel not found: ${channelId}`);
|
|
785
|
+
const taskId = args.task_id === void 0 ? String(channel.threadId) : idArg(args, "task_id");
|
|
786
|
+
if (!taskBelongsTo(channel, taskId)) {
|
|
787
|
+
throw new Error(`Task '${taskId}' does not belong to channel '${channelId}'`);
|
|
788
|
+
}
|
|
789
|
+
if (channel.threadId !== taskId) {
|
|
790
|
+
throw new Error(`Task '${taskId}' is not active for channel '${channelId}'; switch to it before sending`);
|
|
791
|
+
}
|
|
792
|
+
await fetcher(`/api/groups/${encodeURIComponent(channelId)}/messages`, {
|
|
793
|
+
method: "POST",
|
|
794
|
+
body: JSON.stringify({ text, threadId: taskId })
|
|
795
|
+
});
|
|
796
|
+
return { success: true, channelId, taskId };
|
|
797
|
+
}
|
|
798
|
+
case "create_channel": {
|
|
799
|
+
const name2 = stringArg(args, "name", { max: 100 });
|
|
800
|
+
const memberIds = stringArrayArg(args, "member_ids");
|
|
801
|
+
const section = optionalStringArg(args, "section", { max: 60 });
|
|
802
|
+
const bulletin = optionalStringArg(args, "bulletin", { trim: false, allowEmpty: true, max: 12e3 }) ?? "";
|
|
803
|
+
const requestedResponder = normalizeResponder(args.default_responder);
|
|
804
|
+
if (requestedResponder?.kind === "member" && !memberIds.includes(requestedResponder.botId)) {
|
|
805
|
+
throw new ToolInputError("default_responder bot must be a channel member");
|
|
806
|
+
}
|
|
807
|
+
const responder = requestedResponder ?? { kind: "member", botId: memberIds[0] };
|
|
808
|
+
const created = await fetcher("/api/groups", {
|
|
809
|
+
method: "POST",
|
|
810
|
+
body: JSON.stringify({
|
|
811
|
+
name: name2,
|
|
812
|
+
memberIds,
|
|
813
|
+
...section ? { section } : {},
|
|
814
|
+
setup: { bulletin, defaultResponder: responder }
|
|
815
|
+
})
|
|
816
|
+
});
|
|
817
|
+
if (!isRecord(created?.group) || typeof created.group.id !== "string") {
|
|
818
|
+
throw new Error("OpenMausBot did not return the created channel");
|
|
819
|
+
}
|
|
820
|
+
return { success: true, channel: projectChannel(created.group) };
|
|
821
|
+
}
|
|
822
|
+
case "update_channel": {
|
|
823
|
+
const channelId = idArg(args, "channel_id");
|
|
824
|
+
const patch = {};
|
|
825
|
+
if (args.name !== void 0) patch.name = stringArg(args, "name", { max: 100 });
|
|
826
|
+
if (args.member_ids !== void 0) patch.memberIds = stringArrayArg(args, "member_ids");
|
|
827
|
+
if (args.section !== void 0) patch.section = args.section === null ? null : stringArg(args, "section", { max: 60 });
|
|
828
|
+
if (args.bulletin !== void 0) patch.bulletin = stringArg(args, "bulletin", { trim: false, allowEmpty: true, max: 12e3 });
|
|
829
|
+
if (args.default_responder !== void 0) patch.defaultResponder = normalizeResponder(args.default_responder);
|
|
830
|
+
if (Object.keys(patch).length === 0) throw new ToolInputError("provide at least one channel field to update");
|
|
831
|
+
const memberIds = patch.memberIds;
|
|
832
|
+
const responder = patch.defaultResponder;
|
|
833
|
+
if (memberIds && responder?.kind === "member" && !memberIds.includes(responder.botId)) {
|
|
834
|
+
throw new ToolInputError("default_responder bot must be a channel member");
|
|
835
|
+
}
|
|
836
|
+
const result = await fetcher(`/api/groups/${encodeURIComponent(channelId)}`, {
|
|
837
|
+
method: "PATCH",
|
|
838
|
+
body: JSON.stringify(patch)
|
|
839
|
+
});
|
|
840
|
+
if (!isRecord(result?.group)) {
|
|
841
|
+
throw new Error("OpenMausBot did not return the updated channel");
|
|
842
|
+
}
|
|
843
|
+
return { success: true, channel: projectChannel(result.group) };
|
|
844
|
+
}
|
|
845
|
+
case "create_task": {
|
|
846
|
+
const targetId = idArg(args, "target_id");
|
|
847
|
+
const title = optionalStringArg(args, "title", { max: 80 });
|
|
848
|
+
const route = taskRoute(args.target_type, targetId);
|
|
849
|
+
const result = await fetcher(route, { method: "POST", body: JSON.stringify(title ? { title } : {}) });
|
|
850
|
+
if (!isRecord(result?.task) || typeof result.task.threadId !== "string") {
|
|
851
|
+
throw new Error("OpenMausBot did not return the created task");
|
|
852
|
+
}
|
|
853
|
+
const activeTaskId = result.bot?.threadId ?? result.group?.threadId ?? result.task?.threadId;
|
|
854
|
+
return {
|
|
855
|
+
success: true,
|
|
856
|
+
targetType: args.target_type,
|
|
857
|
+
targetId,
|
|
858
|
+
task: projectTask(result.task, activeTaskId)
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
case "switch_task": {
|
|
862
|
+
const targetId = idArg(args, "target_id");
|
|
863
|
+
const taskId = idArg(args, "task_id");
|
|
864
|
+
const route = taskRoute(args.target_type, targetId);
|
|
865
|
+
const result = await fetcher(`${route}/${encodeURIComponent(taskId)}?messages=0`, { method: "POST", body: "{}" });
|
|
866
|
+
const target = args.target_type === "bot" ? result.bot : result.group;
|
|
867
|
+
return {
|
|
868
|
+
success: true,
|
|
869
|
+
targetType: args.target_type,
|
|
870
|
+
targetId,
|
|
871
|
+
taskId,
|
|
872
|
+
...isRecord(target) ? { target: args.target_type === "bot" ? projectBot(target) : projectChannel(target) } : {}
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
case "rename_task": {
|
|
876
|
+
const targetId = idArg(args, "target_id");
|
|
877
|
+
const taskId = idArg(args, "task_id");
|
|
878
|
+
const title = stringArg(args, "title", { max: 80 });
|
|
879
|
+
const route = taskRoute(args.target_type, targetId);
|
|
880
|
+
const result = await fetcher(`${route}/${encodeURIComponent(taskId)}`, {
|
|
881
|
+
method: "PATCH",
|
|
882
|
+
body: JSON.stringify({ title })
|
|
883
|
+
});
|
|
884
|
+
if (!isRecord(result?.task)) {
|
|
885
|
+
throw new Error("OpenMausBot did not return the renamed task");
|
|
886
|
+
}
|
|
887
|
+
return {
|
|
888
|
+
success: true,
|
|
889
|
+
targetType: args.target_type,
|
|
890
|
+
targetId,
|
|
891
|
+
task: projectTask(result.task, void 0)
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
case "search_messages": {
|
|
895
|
+
const query = stringArg(args, "query", { max: 500 });
|
|
896
|
+
const limit = parsePositiveLimit(args.limit, 40, 100);
|
|
897
|
+
const params = new URLSearchParams({ q: query, limit: String(limit) });
|
|
898
|
+
if (args.task_id !== void 0) params.set("threadId", idArg(args, "task_id"));
|
|
899
|
+
const result = await fetcher(`/api/search?${params.toString()}`);
|
|
900
|
+
return { hits: records(result.hits) };
|
|
901
|
+
}
|
|
902
|
+
case "wait_for_conversation": {
|
|
903
|
+
const targetType = args.target_type;
|
|
904
|
+
if (targetType !== "bot" && targetType !== "channel") {
|
|
905
|
+
throw new ToolInputError("target_type must be bot or channel");
|
|
906
|
+
}
|
|
907
|
+
const targetId = idArg(args, "target_id");
|
|
908
|
+
const timeoutSeconds = parsePositiveLimit(args.timeout_seconds, 30, 120);
|
|
909
|
+
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
910
|
+
const startupGraceDeadline = Math.min(deadline, Date.now() + 750);
|
|
911
|
+
let state = await fleet(fetcher);
|
|
912
|
+
const collection = targetType === "bot" ? records(state.bots) : records(state.groups);
|
|
913
|
+
let target = collection.find((candidate) => candidate.id === targetId);
|
|
914
|
+
if (!target) throw new Error(`${targetType === "bot" ? "Bot" : "Channel"} not found: ${targetId}`);
|
|
915
|
+
const taskId = args.task_id === void 0 ? String(target.threadId) : idArg(args, "task_id");
|
|
916
|
+
if (!taskBelongsTo(target, taskId)) {
|
|
917
|
+
throw new Error(`Task '${taskId}' does not belong to ${targetType} '${targetId}'`);
|
|
918
|
+
}
|
|
919
|
+
let sawBusy = false;
|
|
920
|
+
while (true) {
|
|
921
|
+
const liveCollection = targetType === "bot" ? records(state.bots) : records(state.groups);
|
|
922
|
+
target = liveCollection.find((candidate) => candidate.id === targetId);
|
|
923
|
+
if (!target) throw new Error(`${targetType === "bot" ? "Bot" : "Channel"} not found: ${targetId}`);
|
|
924
|
+
if (!taskBelongsTo(target, taskId)) {
|
|
925
|
+
throw new Error(`Task '${taskId}' no longer belongs to ${targetType} '${targetId}'`);
|
|
926
|
+
}
|
|
927
|
+
const projectedTarget = targetType === "bot" ? projectBot(target) : projectChannel(target);
|
|
928
|
+
const terminal = async (status, existingTail) => {
|
|
929
|
+
const tail = existingTail ?? await conversationTail(fetcher, taskId);
|
|
930
|
+
const needsInput = tail.raw.some(messageNeedsInput);
|
|
931
|
+
const terminalStatus = status === "settled" && dispatchFailedAfterLatestUser(tail.raw) ? "failed" : status;
|
|
932
|
+
return {
|
|
933
|
+
status: needsInput ? "needs-user" : terminalStatus,
|
|
934
|
+
targetType,
|
|
935
|
+
targetId,
|
|
936
|
+
taskId,
|
|
937
|
+
target: projectedTarget,
|
|
938
|
+
messages: tail.messages,
|
|
939
|
+
hasMore: tail.hasMore
|
|
940
|
+
};
|
|
941
|
+
};
|
|
942
|
+
if (target.threadId !== taskId) return terminal("settled");
|
|
943
|
+
if (targetType === "bot") {
|
|
944
|
+
const busyChannel = records(state.groups).find((channel) => channel.busyBotId === targetId);
|
|
945
|
+
if (busyChannel) {
|
|
946
|
+
throw new Error(`Bot '${targetId}' is working in channel '${busyChannel.id}'; wait on that channel instead`);
|
|
947
|
+
}
|
|
948
|
+
if (target.activity === "waiting-on-you") return terminal("needs-user");
|
|
949
|
+
if (target.activity === "dead") return terminal("failed");
|
|
950
|
+
if (target.activity === "no-signal") return terminal("stalled");
|
|
951
|
+
if (!target.busy) return terminal("settled");
|
|
952
|
+
sawBusy = true;
|
|
953
|
+
} else {
|
|
954
|
+
const tail = await conversationTail(fetcher, taskId);
|
|
955
|
+
if (tail.raw.some(messageNeedsInput)) {
|
|
956
|
+
return terminal("needs-user", tail);
|
|
957
|
+
}
|
|
958
|
+
const channelWorking = target.working === true || Boolean(target.busyBotId);
|
|
959
|
+
if (channelWorking) {
|
|
960
|
+
sawBusy = true;
|
|
961
|
+
const busyBotId = target.busyBotId;
|
|
962
|
+
if (busyBotId) {
|
|
963
|
+
const speaker = records(state.bots).find((bot) => bot.id === busyBotId);
|
|
964
|
+
if (!speaker) return terminal("stalled");
|
|
965
|
+
if (speaker.activity === "waiting-on-you") return terminal("needs-user");
|
|
966
|
+
if (speaker.activity === "dead") return terminal("failed");
|
|
967
|
+
if (speaker.activity === "no-signal") return terminal("stalled");
|
|
968
|
+
}
|
|
969
|
+
} else {
|
|
970
|
+
const latest = tail.raw.at(-1);
|
|
971
|
+
if (sawBusy || target.working === false || latest?.role !== "user") {
|
|
972
|
+
return terminal("settled", tail);
|
|
973
|
+
}
|
|
974
|
+
if (Date.now() >= startupGraceDeadline) {
|
|
975
|
+
return terminal("settled", tail);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
if (Date.now() >= deadline) return terminal("timed-out");
|
|
980
|
+
await sleep(Math.min(500, Math.max(0, deadline - Date.now())), signal);
|
|
981
|
+
state = await fleet(fetcher);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
case "set_bot_model": {
|
|
985
|
+
const botId = idArg(args, "bot_id");
|
|
986
|
+
const current = await fleet(fetcher);
|
|
987
|
+
const bot = records(current.bots).find((candidate) => candidate.id === botId);
|
|
988
|
+
if (!bot) throw new Error(`Bot not found: ${botId}`);
|
|
989
|
+
if (bot.busy) throw new Error("Interrupt the bot or let it finish before changing its model");
|
|
990
|
+
const selection = await checkedModelSelection(args, fetcher);
|
|
991
|
+
const res = await fetcher(`/api/bots/${encodeURIComponent(botId)}`, {
|
|
992
|
+
method: "PATCH",
|
|
993
|
+
body: JSON.stringify({ modelSelection: selection, requireAvailableModel: true })
|
|
994
|
+
});
|
|
995
|
+
return { success: true, bot: projectBot(res.bot) };
|
|
996
|
+
}
|
|
997
|
+
case "list_available_models": {
|
|
998
|
+
const res = await fetcher("/api/instances");
|
|
999
|
+
return {
|
|
1000
|
+
instances: records(res.instances).map((instance) => ({
|
|
1001
|
+
instanceId: instance.instanceId,
|
|
1002
|
+
driverKind: instance.driverKind,
|
|
1003
|
+
displayName: instance.displayName,
|
|
1004
|
+
snapshot: { state: instance.snapshot?.state },
|
|
1005
|
+
models: instance.models,
|
|
1006
|
+
capabilities: instance.capabilities,
|
|
1007
|
+
access: instance.access
|
|
1008
|
+
}))
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
case "interrupt_conversation": {
|
|
1012
|
+
const targetType = args.target_type;
|
|
1013
|
+
if (targetType !== "bot" && targetType !== "channel") {
|
|
1014
|
+
throw new ToolInputError("target_type must be bot or channel");
|
|
1015
|
+
}
|
|
1016
|
+
const targetId = idArg(args, "target_id");
|
|
1017
|
+
const current = await fleet(fetcher);
|
|
1018
|
+
const target = (targetType === "bot" ? records(current.bots) : records(current.groups)).find((candidate) => candidate.id === targetId);
|
|
1019
|
+
if (!target) throw new Error(`${targetType === "bot" ? "Bot" : "Channel"} not found: ${targetId}`);
|
|
1020
|
+
const taskId = String(target.threadId);
|
|
1021
|
+
if (targetType === "bot") {
|
|
1022
|
+
const busyChannel = records(current.groups).find((channel) => channel.busyBotId === targetId);
|
|
1023
|
+
if (busyChannel) {
|
|
1024
|
+
throw new Error(`Bot '${targetId}' is working in channel '${busyChannel.id}'; interrupt that channel instead`);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
const route = targetType === "bot" ? "bots" : "groups";
|
|
1028
|
+
await fetcher(`/api/${route}/${encodeURIComponent(targetId)}/interrupt`, {
|
|
1029
|
+
method: "POST",
|
|
1030
|
+
body: JSON.stringify({ threadId: taskId })
|
|
1031
|
+
});
|
|
1032
|
+
return { success: true, targetType, targetId, taskId };
|
|
1033
|
+
}
|
|
1034
|
+
default:
|
|
1035
|
+
throw new ToolInputError(`Unknown tool: ${name}`);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function formatResponse(id, result, error) {
|
|
1039
|
+
const payload = { jsonrpc: "2.0", id: id ?? null };
|
|
1040
|
+
if (error) {
|
|
1041
|
+
payload.error = {
|
|
1042
|
+
code: error.code ?? -32603,
|
|
1043
|
+
message: error.message ?? "Internal error"
|
|
1044
|
+
};
|
|
1045
|
+
} else {
|
|
1046
|
+
payload.result = result;
|
|
1047
|
+
}
|
|
1048
|
+
return JSON.stringify(payload);
|
|
1049
|
+
}
|
|
1050
|
+
var activeMcpRequests = /* @__PURE__ */ new Map();
|
|
1051
|
+
var activeMcpControllers = /* @__PURE__ */ new Set();
|
|
1052
|
+
async function processMcpMessage(raw, toolHandler = handleToolCall) {
|
|
1053
|
+
const trimmed = raw.trim();
|
|
1054
|
+
if (!trimmed) return null;
|
|
1055
|
+
let message;
|
|
1056
|
+
try {
|
|
1057
|
+
message = JSON.parse(trimmed);
|
|
1058
|
+
} catch {
|
|
1059
|
+
return formatResponse(null, void 0, { code: -32700, message: "Parse error" });
|
|
1060
|
+
}
|
|
1061
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) {
|
|
1062
|
+
return formatResponse(null, void 0, { code: -32600, message: "Invalid Request" });
|
|
1063
|
+
}
|
|
1064
|
+
if (message.jsonrpc !== "2.0") {
|
|
1065
|
+
return formatResponse(null, void 0, { code: -32600, message: "Invalid Request: missing or invalid jsonrpc version" });
|
|
1066
|
+
}
|
|
1067
|
+
const hasId = "id" in message && message.id !== void 0;
|
|
1068
|
+
if (hasId && (typeof message.id !== "string" && typeof message.id !== "number" || typeof message.id === "number" && !Number.isFinite(message.id))) {
|
|
1069
|
+
return formatResponse(null, void 0, { code: -32600, message: "Invalid Request: id must be a string or number" });
|
|
1070
|
+
}
|
|
1071
|
+
const isNotification = !hasId;
|
|
1072
|
+
const id = isNotification ? null : message.id;
|
|
1073
|
+
const { method, params } = message;
|
|
1074
|
+
if (typeof method !== "string") {
|
|
1075
|
+
if (isNotification) return null;
|
|
1076
|
+
return formatResponse(id, void 0, { code: -32600, message: "Invalid Request: method is required" });
|
|
1077
|
+
}
|
|
1078
|
+
try {
|
|
1079
|
+
if (method === "notifications/cancelled") {
|
|
1080
|
+
if (isRecord(params)) {
|
|
1081
|
+
const requestId = params.requestId;
|
|
1082
|
+
if (typeof requestId === "string" || typeof requestId === "number") {
|
|
1083
|
+
activeMcpRequests.get(requestId)?.abort(new DOMException("Request cancelled", "AbortError"));
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return null;
|
|
1087
|
+
}
|
|
1088
|
+
if (method === "initialize") {
|
|
1089
|
+
if (isNotification) return null;
|
|
1090
|
+
if (!isRecord(params) || typeof params.protocolVersion !== "string" || !isRecord(params.capabilities) || !isRecord(params.clientInfo) || typeof params.clientInfo.name !== "string" || typeof params.clientInfo.version !== "string") {
|
|
1091
|
+
return formatResponse(id, void 0, {
|
|
1092
|
+
code: -32602,
|
|
1093
|
+
message: "Invalid params: protocolVersion, capabilities, and clientInfo name/version are required"
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
const supportedVersions = ["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25"];
|
|
1097
|
+
const protocolVersion = supportedVersions.includes(params.protocolVersion) ? params.protocolVersion : supportedVersions[supportedVersions.length - 1];
|
|
1098
|
+
return formatResponse(id, {
|
|
1099
|
+
protocolVersion,
|
|
1100
|
+
capabilities: {
|
|
1101
|
+
tools: {}
|
|
1102
|
+
},
|
|
1103
|
+
serverInfo: {
|
|
1104
|
+
name: "openmausbot-mcp",
|
|
1105
|
+
version: "1.1.0"
|
|
1106
|
+
},
|
|
1107
|
+
instructions: "Use bounded read tools before mutating the OpenMausBot team. Approval grants, deletion, and computer lifecycle are intentionally unavailable."
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
if (method === "notifications/initialized") {
|
|
1111
|
+
log("MCP client initialized session");
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1114
|
+
if (method === "ping") {
|
|
1115
|
+
if (isNotification) return null;
|
|
1116
|
+
return formatResponse(id, {});
|
|
1117
|
+
}
|
|
1118
|
+
if (method === "tools/list") {
|
|
1119
|
+
if (isNotification) return null;
|
|
1120
|
+
return formatResponse(id, { tools: TOOLS });
|
|
1121
|
+
}
|
|
1122
|
+
if (method === "tools/call") {
|
|
1123
|
+
if (!isRecord(params)) {
|
|
1124
|
+
if (isNotification) return null;
|
|
1125
|
+
return formatResponse(id, void 0, { code: -32602, message: "Invalid params: tools/call expects an object" });
|
|
1126
|
+
}
|
|
1127
|
+
const name = params.name;
|
|
1128
|
+
const toolArgs = params.arguments === void 0 ? {} : params.arguments;
|
|
1129
|
+
try {
|
|
1130
|
+
validateToolArguments(name, toolArgs);
|
|
1131
|
+
} catch (error) {
|
|
1132
|
+
if (isNotification) return null;
|
|
1133
|
+
return formatResponse(id, void 0, {
|
|
1134
|
+
code: -32602,
|
|
1135
|
+
message: `Invalid params: ${error instanceof Error ? error.message : String(error)}`
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
const controller = new AbortController();
|
|
1139
|
+
activeMcpControllers.add(controller);
|
|
1140
|
+
if (!isNotification) activeMcpRequests.set(id, controller);
|
|
1141
|
+
let result;
|
|
1142
|
+
try {
|
|
1143
|
+
result = await toolHandler(name, toolArgs, request, controller.signal);
|
|
1144
|
+
} finally {
|
|
1145
|
+
activeMcpControllers.delete(controller);
|
|
1146
|
+
if (!isNotification && activeMcpRequests.get(id) === controller) {
|
|
1147
|
+
activeMcpRequests.delete(id);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
if (isNotification) return null;
|
|
1151
|
+
return formatResponse(id, {
|
|
1152
|
+
content: [
|
|
1153
|
+
{
|
|
1154
|
+
type: "text",
|
|
1155
|
+
text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
1156
|
+
}
|
|
1157
|
+
],
|
|
1158
|
+
...isRecord(result) ? { structuredContent: result } : {}
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
if (isNotification) return null;
|
|
1162
|
+
return formatResponse(id, void 0, { code: -32601, message: `Method not found: ${method}` });
|
|
1163
|
+
} catch (err) {
|
|
1164
|
+
if ((err?.name === "AbortError" || err?.message === "Request cancelled") && !isNotification) {
|
|
1165
|
+
return formatResponse(id, void 0, { code: -32800, message: "Request cancelled" });
|
|
1166
|
+
}
|
|
1167
|
+
log(`Error handling ${method}: ${err?.message || err}`);
|
|
1168
|
+
if (err instanceof ToolInputError && !isNotification) {
|
|
1169
|
+
return formatResponse(id, void 0, { code: -32602, message: `Invalid params: ${err.message}` });
|
|
1170
|
+
}
|
|
1171
|
+
if (!isNotification) {
|
|
1172
|
+
return formatResponse(id, {
|
|
1173
|
+
content: [
|
|
1174
|
+
{
|
|
1175
|
+
type: "text",
|
|
1176
|
+
text: `Error: ${err?.message || String(err)}`
|
|
1177
|
+
}
|
|
1178
|
+
],
|
|
1179
|
+
isError: true
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
return null;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
if (process.argv[1] && (process.argv[1].endsWith("mcp-server.ts") || process.argv[1].endsWith("mcp-server.js"))) {
|
|
1186
|
+
const rl = readline.createInterface({
|
|
1187
|
+
input: process.stdin,
|
|
1188
|
+
output: process.stdout,
|
|
1189
|
+
terminal: false
|
|
1190
|
+
});
|
|
1191
|
+
const activeRequests = /* @__PURE__ */ new Set();
|
|
1192
|
+
rl.on("line", (line) => {
|
|
1193
|
+
const task = (async () => {
|
|
1194
|
+
try {
|
|
1195
|
+
const response = await processMcpMessage(line);
|
|
1196
|
+
if (response) {
|
|
1197
|
+
process.stdout.write(response + "\n");
|
|
1198
|
+
}
|
|
1199
|
+
} catch (err) {
|
|
1200
|
+
log(`Error processing line: ${err}`);
|
|
1201
|
+
}
|
|
1202
|
+
})();
|
|
1203
|
+
activeRequests.add(task);
|
|
1204
|
+
task.finally(() => {
|
|
1205
|
+
activeRequests.delete(task);
|
|
1206
|
+
});
|
|
1207
|
+
});
|
|
1208
|
+
rl.on("close", async () => {
|
|
1209
|
+
for (const controller of activeMcpControllers) {
|
|
1210
|
+
controller.abort(new DOMException("Request cancelled", "AbortError"));
|
|
1211
|
+
}
|
|
1212
|
+
if (activeRequests.size > 0) {
|
|
1213
|
+
await Promise.allSettled(Array.from(activeRequests));
|
|
1214
|
+
}
|
|
1215
|
+
process.exitCode = 0;
|
|
1216
|
+
});
|
|
1217
|
+
log("OpenMausBot MCP server running on stdio");
|
|
1218
|
+
}
|
|
1219
|
+
export {
|
|
1220
|
+
DEFAULT_REQUEST_TIMEOUT_MS,
|
|
1221
|
+
OMB_BASE_URL,
|
|
1222
|
+
TOOLS,
|
|
1223
|
+
ToolInputError,
|
|
1224
|
+
formatResponse,
|
|
1225
|
+
handleToolCall,
|
|
1226
|
+
log,
|
|
1227
|
+
probeBaseUrls,
|
|
1228
|
+
processMcpMessage,
|
|
1229
|
+
request,
|
|
1230
|
+
resolveBaseUrl,
|
|
1231
|
+
validateBaseUrl,
|
|
1232
|
+
validateToolArguments
|
|
1233
|
+
};
|