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,304 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
import { parseTeamManifest, TEAM_MANIFEST_FORMAT, TEAM_MANIFEST_VERSION, } from "./team-manifest.js";
|
|
4
|
+
// The scout reads, it never writes — and it reads bounded: a handful of
|
|
5
|
+
// well-known files by name, top-level directory listings, and nothing
|
|
6
|
+
// recursive. A folder full of surprises must cost milliseconds, not minutes.
|
|
7
|
+
const MAX_FILE_BYTES = 256_000;
|
|
8
|
+
const MAX_DIR_ENTRIES = 400;
|
|
9
|
+
function readText(path) {
|
|
10
|
+
try {
|
|
11
|
+
if (statSync(path).size > MAX_FILE_BYTES)
|
|
12
|
+
return null;
|
|
13
|
+
return readFileSync(path, "utf8");
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function listNames(dir) {
|
|
20
|
+
try {
|
|
21
|
+
return readdirSync(dir).slice(0, MAX_DIR_ENTRIES);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function isDir(path) {
|
|
28
|
+
try {
|
|
29
|
+
return statSync(path).isDirectory();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function packageJson(cwd) {
|
|
36
|
+
const raw = readText(join(cwd, "package.json"));
|
|
37
|
+
if (!raw)
|
|
38
|
+
return { deps: new Set() };
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(raw);
|
|
41
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
42
|
+
return { deps: new Set() };
|
|
43
|
+
const pkg = parsed;
|
|
44
|
+
const deps = new Set();
|
|
45
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
46
|
+
const block = pkg[field];
|
|
47
|
+
if (block && typeof block === "object" && !Array.isArray(block)) {
|
|
48
|
+
for (const dep of Object.keys(block))
|
|
49
|
+
deps.add(dep);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
name: typeof pkg.name === "string" ? pkg.name : undefined,
|
|
54
|
+
description: typeof pkg.description === "string" ? pkg.description : undefined,
|
|
55
|
+
deps,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return { deps: new Set() };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** README h1 and the first prose paragraph after it. Badge rows and heading
|
|
63
|
+
* lines are skipped so the summary reads like a sentence, not markup. */
|
|
64
|
+
function readme(cwd) {
|
|
65
|
+
const raw = readText(join(cwd, "README.md")) ?? readText(join(cwd, "readme.md")) ?? readText(join(cwd, "README"));
|
|
66
|
+
if (!raw)
|
|
67
|
+
return {};
|
|
68
|
+
let title;
|
|
69
|
+
let summary;
|
|
70
|
+
for (const line of raw.slice(0, 64_000).split("\n")) {
|
|
71
|
+
const trimmed = line.trim();
|
|
72
|
+
if (!trimmed)
|
|
73
|
+
continue;
|
|
74
|
+
if (trimmed.startsWith("#")) {
|
|
75
|
+
if (!title)
|
|
76
|
+
title = trimmed.replace(/^#+\s*/, "").trim() || undefined;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// badge rows, raw HTML, and blockquote callouts (warnings, notices) are
|
|
80
|
+
// not the sentence that says what the project is
|
|
81
|
+
if (trimmed.startsWith("[![") || trimmed.startsWith("![") || trimmed.startsWith("<") || trimmed.startsWith(">"))
|
|
82
|
+
continue;
|
|
83
|
+
// the summary is rendered as plain text; markdown emphasis would show
|
|
84
|
+
// its asterisks
|
|
85
|
+
summary = trimmed.replace(/[*_`]/g, "").slice(0, 1_000);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
return { title, summary };
|
|
89
|
+
}
|
|
90
|
+
// Order is priority: when a suggestion has to be trimmed, the roles that
|
|
91
|
+
// define the project's shape survive over the ones that polish it.
|
|
92
|
+
const DETECTORS = [
|
|
93
|
+
{
|
|
94
|
+
role: "frontend",
|
|
95
|
+
deps: ["react", "vue", "svelte", "next", "nuxt", "astro", "vite", "@angular/core", "solid-js"],
|
|
96
|
+
paths: ["index.html", "vite.config.ts", "vite.config.js", "next.config.js", "next.config.ts"],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
role: "backend",
|
|
100
|
+
deps: ["express", "fastify", "koa", "hono", "@nestjs/core", "django", "flask", "fastapi"],
|
|
101
|
+
paths: ["server", "api", "go.mod"],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
role: "mobile",
|
|
105
|
+
deps: ["react-native", "expo"],
|
|
106
|
+
paths: ["ios", "android", "pubspec.yaml"],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
role: "data",
|
|
110
|
+
deps: ["prisma", "drizzle-orm", "knex", "sequelize", "typeorm", "mongoose", "pg", "mysql2", "better-sqlite3", "sqlalchemy"],
|
|
111
|
+
paths: ["prisma", "migrations"],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
role: "testing",
|
|
115
|
+
deps: ["vitest", "jest", "mocha", "@playwright/test", "cypress", "pytest"],
|
|
116
|
+
paths: ["test", "tests", "__tests__", "e2e"],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
role: "infra",
|
|
120
|
+
deps: [],
|
|
121
|
+
paths: ["Dockerfile", "docker-compose.yml", "docker-compose.yaml", "compose.yaml", ".github/workflows", "terraform", "helm"],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
role: "docs",
|
|
125
|
+
deps: [],
|
|
126
|
+
paths: ["docs", "mkdocs.yml"],
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
const STACK_MARKERS = [
|
|
130
|
+
{ stack: "TypeScript", deps: ["typescript"], paths: ["tsconfig.json"] },
|
|
131
|
+
{ stack: "React", deps: ["react"] },
|
|
132
|
+
{ stack: "Vue", deps: ["vue"] },
|
|
133
|
+
{ stack: "Svelte", deps: ["svelte"] },
|
|
134
|
+
{ stack: "Next.js", deps: ["next"] },
|
|
135
|
+
{ stack: "Vite", deps: ["vite"] },
|
|
136
|
+
{ stack: "Node", paths: ["package.json"] },
|
|
137
|
+
{ stack: "Python", paths: ["pyproject.toml", "requirements.txt", "setup.py"] },
|
|
138
|
+
{ stack: "Rust", paths: ["Cargo.toml"] },
|
|
139
|
+
{ stack: "Go", paths: ["go.mod"] },
|
|
140
|
+
{ stack: "Ruby", paths: ["Gemfile"] },
|
|
141
|
+
{ stack: "PHP", paths: ["composer.json"] },
|
|
142
|
+
{ stack: "Java", paths: ["pom.xml", "build.gradle", "build.gradle.kts"] },
|
|
143
|
+
{ stack: "Docker", paths: ["Dockerfile", "docker-compose.yml", "compose.yaml"] },
|
|
144
|
+
];
|
|
145
|
+
/** Read a folder and describe the project in it: name, one-line summary,
|
|
146
|
+
* stack chips, and which team roles the contents argue for. Deterministic
|
|
147
|
+
* and offline — the same folder always scouts to the same profile. */
|
|
148
|
+
export function scoutProject(cwd) {
|
|
149
|
+
const pkg = packageJson(cwd);
|
|
150
|
+
const md = readme(cwd);
|
|
151
|
+
const entries = new Set(listNames(cwd));
|
|
152
|
+
// Python deps live in files, not a lockfile-adjacent field; a cheap
|
|
153
|
+
// substring scan of the two conventional files covers the common cases.
|
|
154
|
+
const pythonDeps = `${readText(join(cwd, "requirements.txt")) ?? ""}\n${readText(join(cwd, "pyproject.toml")) ?? ""}`.toLowerCase();
|
|
155
|
+
const hasDep = (dep) => pkg.deps.has(dep) || (pythonDeps.length > 1 && pythonDeps.includes(dep));
|
|
156
|
+
const pathEvidence = (path) => {
|
|
157
|
+
if (path.includes("/"))
|
|
158
|
+
return isDir(join(cwd, path)) && listNames(join(cwd, path)).length > 0 ? `${path}/` : null;
|
|
159
|
+
if (entries.has(path))
|
|
160
|
+
return isDir(join(cwd, path)) ? `${path}/` : path;
|
|
161
|
+
return null;
|
|
162
|
+
};
|
|
163
|
+
const signals = [];
|
|
164
|
+
for (const detector of DETECTORS) {
|
|
165
|
+
const evidence = [];
|
|
166
|
+
for (const dep of detector.deps)
|
|
167
|
+
if (hasDep(dep))
|
|
168
|
+
evidence.push(dep);
|
|
169
|
+
for (const path of detector.paths) {
|
|
170
|
+
const found = pathEvidence(path);
|
|
171
|
+
if (found)
|
|
172
|
+
evidence.push(found);
|
|
173
|
+
}
|
|
174
|
+
// docs need substance: a folder with no markdown in it is not a docs site
|
|
175
|
+
if (detector.role === "docs" && evidence.length > 0) {
|
|
176
|
+
const hasMkdocs = evidence.includes("mkdocs.yml");
|
|
177
|
+
const hasMarkdown = listNames(join(cwd, "docs")).some((name) => name.endsWith(".md"));
|
|
178
|
+
if (!hasMkdocs && !hasMarkdown)
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (evidence.length > 0)
|
|
182
|
+
signals.push({ role: detector.role, evidence: evidence.slice(0, 6) });
|
|
183
|
+
}
|
|
184
|
+
const stacks = [];
|
|
185
|
+
for (const marker of STACK_MARKERS) {
|
|
186
|
+
const byDep = marker.deps?.some((dep) => hasDep(dep)) ?? false;
|
|
187
|
+
const byPath = marker.paths?.some((path) => entries.has(path)) ?? false;
|
|
188
|
+
if (byDep || byPath)
|
|
189
|
+
stacks.push(marker.stack);
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
name: (md.title ?? pkg.name ?? basename(cwd)).slice(0, 100),
|
|
193
|
+
summary: (md.summary ?? pkg.description ?? "").slice(0, 1_000),
|
|
194
|
+
stacks,
|
|
195
|
+
signals,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const stackLine = (profile) => profile.stacks.length > 0 ? ` The stack: ${profile.stacks.join(", ")}.` : "";
|
|
199
|
+
const ROLE_TEMPLATES = {
|
|
200
|
+
frontend: {
|
|
201
|
+
name: "Pixel",
|
|
202
|
+
title: "Frontend Builder",
|
|
203
|
+
color: "pink",
|
|
204
|
+
describe: (profile, evidence) => `You build and refine the user interface of ${profile.name}.${stackLine(profile)} Your turf shows up as ${evidence.join(", ")}. Keep changes small, match the existing component patterns, and check what you built actually renders before calling it done.`,
|
|
205
|
+
},
|
|
206
|
+
backend: {
|
|
207
|
+
name: "Forge",
|
|
208
|
+
title: "Backend Builder",
|
|
209
|
+
color: "blue",
|
|
210
|
+
describe: (profile, evidence) => `You own the server side of ${profile.name}: endpoints, business logic, and the contracts the frontend relies on.${stackLine(profile)} Your turf shows up as ${evidence.join(", ")}. Change behavior only alongside the tests that prove it.`,
|
|
211
|
+
},
|
|
212
|
+
mobile: {
|
|
213
|
+
name: "Pocket",
|
|
214
|
+
title: "Mobile Builder",
|
|
215
|
+
color: "coral",
|
|
216
|
+
describe: (profile, evidence) => `You keep ${profile.name} working on phones: screens, navigation, and platform quirks.${stackLine(profile)} Your turf shows up as ${evidence.join(", ")}. Test on both platforms before declaring victory.`,
|
|
217
|
+
},
|
|
218
|
+
data: {
|
|
219
|
+
name: "Schema",
|
|
220
|
+
title: "Data Engineer",
|
|
221
|
+
color: "teal",
|
|
222
|
+
describe: (profile, evidence) => `You own the data layer of ${profile.name}: models, migrations, and query performance.${stackLine(profile)} Your turf shows up as ${evidence.join(", ")}. Every migration ships with its rollback story.`,
|
|
223
|
+
},
|
|
224
|
+
testing: {
|
|
225
|
+
name: "Probe",
|
|
226
|
+
title: "Test Engineer",
|
|
227
|
+
color: "green",
|
|
228
|
+
describe: (profile, evidence) => `You guard ${profile.name} with tests: you reproduce bugs before they are fixed and extend coverage where changes land.${stackLine(profile)} Your turf shows up as ${evidence.join(", ")}. A red test you wrote is worth more than a green suite nobody trusts.`,
|
|
229
|
+
},
|
|
230
|
+
infra: {
|
|
231
|
+
name: "Anchor",
|
|
232
|
+
title: "Infra & CI",
|
|
233
|
+
color: "orange",
|
|
234
|
+
describe: (profile, evidence) => `You keep ${profile.name} buildable, shippable, and observable: CI, containers, and deploy paths.${stackLine(profile)} Your turf shows up as ${evidence.join(", ")}. Prefer boring, reproducible steps over clever ones.`,
|
|
235
|
+
},
|
|
236
|
+
docs: {
|
|
237
|
+
name: "Quill",
|
|
238
|
+
title: "Docs Writer",
|
|
239
|
+
color: "purple",
|
|
240
|
+
describe: (profile, evidence) => `You keep the documentation of ${profile.name} truthful and current.${stackLine(profile)} Your turf shows up as ${evidence.join(", ")}. When code and docs disagree, you chase down which one is lying.`,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
const LEAD = {
|
|
244
|
+
name: "Compass",
|
|
245
|
+
title: "Project Lead",
|
|
246
|
+
color: "yellow",
|
|
247
|
+
describe: (profile) => `You coordinate work on ${profile.name}: break briefs into tasks for the team, keep the room's bulletin current, and review results before they count as done.${stackLine(profile)}${profile.summary ? ` The project, in its own words: ${profile.summary}` : ""}`,
|
|
248
|
+
};
|
|
249
|
+
/** at most the lead plus this many specialists — a suggestion is a starting
|
|
250
|
+
* lineup, not a payroll */
|
|
251
|
+
const MAX_SUGGESTED_SPECIALISTS = 5;
|
|
252
|
+
/** Turn a scouted profile into an importable team: a lead plus one member
|
|
253
|
+
* per detected role, as a regular v2 manifest. Suggesting is all this does —
|
|
254
|
+
* creating bots and the room stays behind the existing import endpoint and
|
|
255
|
+
* its human click. */
|
|
256
|
+
export function suggestTeam(profile) {
|
|
257
|
+
const reasons = {
|
|
258
|
+
lead: "Every project room needs one member who briefs, splits, and reviews.",
|
|
259
|
+
};
|
|
260
|
+
const members = [
|
|
261
|
+
{
|
|
262
|
+
key: "lead",
|
|
263
|
+
name: LEAD.name,
|
|
264
|
+
title: LEAD.title,
|
|
265
|
+
description: LEAD.describe(profile, []),
|
|
266
|
+
appearance: { color: LEAD.color },
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
for (const signal of profile.signals.slice(0, MAX_SUGGESTED_SPECIALISTS)) {
|
|
270
|
+
const template = ROLE_TEMPLATES[signal.role];
|
|
271
|
+
members.push({
|
|
272
|
+
key: signal.role,
|
|
273
|
+
name: template.name,
|
|
274
|
+
title: template.title,
|
|
275
|
+
description: template.describe(profile, signal.evidence),
|
|
276
|
+
appearance: { color: template.color },
|
|
277
|
+
});
|
|
278
|
+
reasons[signal.role] = `Detected via ${signal.evidence.join(", ")}.`;
|
|
279
|
+
}
|
|
280
|
+
// no signals at all → the room still gets a working pair of hands
|
|
281
|
+
if (members.length === 1) {
|
|
282
|
+
members.push({
|
|
283
|
+
key: "builder",
|
|
284
|
+
name: "Wrench",
|
|
285
|
+
title: "Builder",
|
|
286
|
+
description: `You do the hands-on work in ${profile.name}: read the folder, make the change, show the result.${stackLine(profile)}`,
|
|
287
|
+
appearance: { color: "blue" },
|
|
288
|
+
});
|
|
289
|
+
reasons.builder = "No specific stack detected — a generalist covers the ground.";
|
|
290
|
+
}
|
|
291
|
+
const manifest = {
|
|
292
|
+
format: TEAM_MANIFEST_FORMAT,
|
|
293
|
+
version: TEAM_MANIFEST_VERSION,
|
|
294
|
+
team: {
|
|
295
|
+
name: `${profile.name} team`.slice(0, 100),
|
|
296
|
+
members,
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
if (profile.summary)
|
|
300
|
+
manifest.team.description = profile.summary.slice(0, 2_000);
|
|
301
|
+
// Lockstep with import: a suggestion must be exactly as valid as a file
|
|
302
|
+
// someone shared — same parser, same limits, same normalization.
|
|
303
|
+
return { roomName: profile.name, manifest: parseTeamManifest(manifest), reasons };
|
|
304
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Where the server's own files live at runtime, and the one place a spawned
|
|
2
|
+
// proxy's path is worked out.
|
|
3
|
+
//
|
|
4
|
+
// This must NOT be resolved relative to the module that happens to need it.
|
|
5
|
+
// esbuild inlines modules into an entry bundle (scripts/bundle-server.mjs), so
|
|
6
|
+
// a `".."` written inside drivers/claude.ts stops climbing from drivers/ and
|
|
7
|
+
// starts climbing from the BUNDLE's directory — one level too high, silently.
|
|
8
|
+
//
|
|
9
|
+
// That is not hypothetical: it shipped into the 0.1.24 release candidate.
|
|
10
|
+
// Every proxy resolved outside Resources/server, so permission prompting,
|
|
11
|
+
// computer use and dweb all died on the first turn that needed them, while
|
|
12
|
+
// /api/health stayed perfectly green and the packaging checks passed.
|
|
13
|
+
//
|
|
14
|
+
// This file sits at the server root and is only ever inlined into entries that
|
|
15
|
+
// also sit at the server root (index, computer-proxy, container-mcp) — the
|
|
16
|
+
// nested drivers/* entries import nothing local, which is what keeps the
|
|
17
|
+
// anchor correct in both the dev tree and the bundle. proxy-paths.test.ts pins
|
|
18
|
+
// that invariant. Resolve proxies through SPAWNED_PROXIES and nowhere else.
|
|
19
|
+
import { existsSync } from "node:fs";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
/** The directory holding the server's runtime files: server/ in dev, the
|
|
23
|
+
* extraResources copy of dist-server/ (Resources/server) in the packaged app. */
|
|
24
|
+
export const SERVER_ROOT = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
/** .ts in dev, where node strips types; the compiled sibling once packaged. */
|
|
26
|
+
export function resolveProxy(relative) {
|
|
27
|
+
const source = join(SERVER_ROOT, `${relative}.ts`);
|
|
28
|
+
return existsSync(source) ? source : join(SERVER_ROOT, `${relative}.js`);
|
|
29
|
+
}
|
|
30
|
+
/** Every file the server spawns as its own process. Single source of truth so
|
|
31
|
+
* the smoke test can assert each one actually exists in a packaged layout —
|
|
32
|
+
* the check that would have caught the 0.1.24 breakage. */
|
|
33
|
+
export const SPAWNED_PROXIES = {
|
|
34
|
+
computer: resolveProxy("computer-proxy"),
|
|
35
|
+
permission: resolveProxy("permission-proxy"),
|
|
36
|
+
containerMcp: resolveProxy("container-mcp"),
|
|
37
|
+
vpsContainerMcp: resolveProxy("vps-container-mcp"),
|
|
38
|
+
agents: resolveProxy("drivers/agents-proxy"),
|
|
39
|
+
dweb: resolveProxy("drivers/dweb-proxy"),
|
|
40
|
+
connectors: resolveProxy("connector-proxy"),
|
|
41
|
+
phone: resolveProxy("drivers/phone-proxy"),
|
|
42
|
+
browser: resolveProxy("drivers/browser-proxy"),
|
|
43
|
+
// Loaded by the external `pi` process via `-e`, not by this server — but
|
|
44
|
+
// resolved through the same single source of truth so the packaged layout
|
|
45
|
+
// check can assert it ships.
|
|
46
|
+
piMcpExtension: resolveProxy("drivers/pi-mcp-extension"),
|
|
47
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Keeping secrets out of the native protocol log.
|
|
2
|
+
//
|
|
3
|
+
// The native tee writes every provider message verbatim, which is what makes
|
|
4
|
+
// protocol drift diagnosable — but the messages that set a session up carry
|
|
5
|
+
// the credentials the agent is handed: the box token and the comms token
|
|
6
|
+
// travel inside `session/new`'s mcpServers env, and a Composio consumer key
|
|
7
|
+
// travels in an MCP header. Those logs sit in ~/.openmausbot/native as
|
|
8
|
+
// ordinary files, are read by anyone debugging, and get pasted into issues.
|
|
9
|
+
//
|
|
10
|
+
// So the log keeps the SHAPE and loses the VALUES: a redacted entry still
|
|
11
|
+
// tells you a token was passed, under which name, and how long it was —
|
|
12
|
+
// enough to debug "the proxy got no token" without the token being there.
|
|
13
|
+
/** Key names whose value is a credential. Matched case-insensitively as a
|
|
14
|
+
* substring, so KEY catches ANTHROPIC_API_KEY and x-api-key. */
|
|
15
|
+
const SECRET_KEY_PARTS = ["token", "secret", "password", "passwd", "apikey", "api_key", "authorization", "auth_token"];
|
|
16
|
+
/** `key` alone is too broad — it matches `keyboard`, `keys`, `hotkey`. Only
|
|
17
|
+
* treat it as a credential when it stands alone or is a suffix, which is how
|
|
18
|
+
* every real one is spelled (API_KEY, consumer-key, xai_key). */
|
|
19
|
+
function isSecretName(name) {
|
|
20
|
+
const lower = name.toLowerCase();
|
|
21
|
+
if (SECRET_KEY_PARTS.some((part) => lower.includes(part)))
|
|
22
|
+
return true;
|
|
23
|
+
return /(^|[_.-])keys?$/.test(lower);
|
|
24
|
+
}
|
|
25
|
+
const REDACTION_MARKER = /^«redacted \d+ chars»$/;
|
|
26
|
+
/** Keep repeated redaction byte-for-byte stable. Persisted payloads can pass
|
|
27
|
+
* through both a content scrub and the store-wide scrub; re-masking our own
|
|
28
|
+
* marker would change its reported length (and any hash over the payload). */
|
|
29
|
+
const mask = (value) => (REDACTION_MARKER.test(value) ? value : `«redacted ${value.length} chars»`);
|
|
30
|
+
// ── content-shaped secrets ────────────────────────────────────────────
|
|
31
|
+
// What a bot's own reply, a tool title, or a permission card can carry —
|
|
32
|
+
// and, since the rebuild replays activity into every handed-over context,
|
|
33
|
+
// what would otherwise become permanent. High precision on purpose: a
|
|
34
|
+
// generic "long hex/base64" heuristic would rewrite real code in the
|
|
35
|
+
// transcript, so only shapes that are unmistakably credentials match.
|
|
36
|
+
const KEY_PREFIXES = [
|
|
37
|
+
/\bsk-(?:ant-|proj-|live-|test-)?[A-Za-z0-9_-]{16,}/g, // anthropic / openai / stripe
|
|
38
|
+
/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}/g, // github classic
|
|
39
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}/g, // github fine-grained
|
|
40
|
+
/\bxox[abposr]-[A-Za-z0-9-]{20,}/g, // slack
|
|
41
|
+
/\bAKIA[0-9A-Z]{16}\b/g, // aws access key id
|
|
42
|
+
/\bAIza[0-9A-Za-z_-]{30,}/g, // google api key
|
|
43
|
+
/\bnpm_[A-Za-z0-9]{20,}/g, // npm
|
|
44
|
+
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, // jwt
|
|
45
|
+
];
|
|
46
|
+
const BEARER = /(\bBearer\s+)([A-Za-z0-9._~+/=-]{12,})/g;
|
|
47
|
+
const PEM_BLOCK = /(-----BEGIN [A-Z ]*PRIVATE KEY-----)([\s\S]*?)(-----END [A-Z ]*PRIVATE KEY-----)/g;
|
|
48
|
+
/** key=value / key: value / key="value" where the key is secret-shaped.
|
|
49
|
+
* The value must be a single token of some length; prose after a colon
|
|
50
|
+
* ("password: leave blank…") has spaces and does not match. */
|
|
51
|
+
const KEY_VALUE = /\b((?:[A-Za-z0-9_-]*_)?(?:api[_-]?key|apikey|secret|token|password|passwd|authorization|auth[_-]?token|access[_-]?key|private[_-]?key)s?)(["']?\s*[=:]\s*)(["']?)([A-Za-z0-9._~+/=-]{8,})\3/gi;
|
|
52
|
+
export function redactSecretsInText(text) {
|
|
53
|
+
if (!text || text.length < 8)
|
|
54
|
+
return text;
|
|
55
|
+
let out = text;
|
|
56
|
+
out = out.replace(PEM_BLOCK, (_m, open, body, close) => `${open}\n${mask(body.trim())}\n${close}`);
|
|
57
|
+
for (const re of KEY_PREFIXES)
|
|
58
|
+
out = out.replace(re, (m) => mask(m));
|
|
59
|
+
out = out.replace(BEARER, (_m, lead, tok) => `${lead}${mask(tok)}`);
|
|
60
|
+
out = out.replace(KEY_VALUE, (_m, key, sep, quote, value) => `${key}${sep}${quote}${mask(value)}${quote}`);
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/** Deep copy with credential VALUES replaced. Handles the two shapes that
|
|
64
|
+
* actually carry them: a plain object of env vars ({KEY: "v"}) and the ACP
|
|
65
|
+
* wire shape (env: [{name, value}]). Anything unrecognised is copied as-is. */
|
|
66
|
+
export function redactSecrets(input, depth = 0) {
|
|
67
|
+
if (typeof input === "string")
|
|
68
|
+
return redactSecretsInText(input);
|
|
69
|
+
if (depth > 12 || input === null || typeof input !== "object")
|
|
70
|
+
return input;
|
|
71
|
+
if (Array.isArray(input)) {
|
|
72
|
+
return input.map((item) => {
|
|
73
|
+
// ACP env entries: {name: "OMB_COMMS_TOKEN", value: "…"}
|
|
74
|
+
if (item !== null &&
|
|
75
|
+
typeof item === "object" &&
|
|
76
|
+
!Array.isArray(item) &&
|
|
77
|
+
typeof item.name === "string" &&
|
|
78
|
+
typeof item.value === "string") {
|
|
79
|
+
const entry = item;
|
|
80
|
+
// A non-secret-shaped name (a custom env var, a feature flag) does
|
|
81
|
+
// not clear the value of suspicion — the same content pass every
|
|
82
|
+
// other string in this tree gets is what catches a credential
|
|
83
|
+
// someone stashed under an ordinary-looking name.
|
|
84
|
+
return isSecretName(entry.name)
|
|
85
|
+
? { ...entry, value: mask(entry.value) }
|
|
86
|
+
: { ...entry, value: redactSecretsInText(entry.value) };
|
|
87
|
+
}
|
|
88
|
+
return redactSecrets(item, depth + 1);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const out = {};
|
|
92
|
+
for (const [key, value] of Object.entries(input)) {
|
|
93
|
+
if (typeof value === "string" && isSecretName(key)) {
|
|
94
|
+
out[key] = mask(value);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
// any other string may still CONTAIN a credential (a command line, a
|
|
98
|
+
// header value, a bot's reply) — the content pass catches those
|
|
99
|
+
out[key] = redactSecrets(value, depth + 1);
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Shared provisioning and shell contract for the cloud computer's Cua Driver.
|
|
2
|
+
// The box command API is the transport boundary: the daemon stays loopback-only
|
|
3
|
+
// inside the VM and OpenMausBot never exposes another inbound port.
|
|
4
|
+
export const REMOTE_CUA_VERSION = "0.20.0";
|
|
5
|
+
export const REMOTE_CUA_EXECUTABLE = "/opt/ogb/cua-driver";
|
|
6
|
+
export const REMOTE_CUA_SOCKET = "/opt/ogb/run/cua.sock";
|
|
7
|
+
export const REMOTE_CUA_SESSION = "openmausbot";
|
|
8
|
+
export const REMOTE_CDP_HELPER = "/opt/ogb/openmausbot-cdp.mjs";
|
|
9
|
+
const REMOTE_CUA_WHEELS = {
|
|
10
|
+
x86_64: {
|
|
11
|
+
url: "https://files.pythonhosted.org/packages/fa/d7/a43008a328a40c85e7bc706fc20235b9abedc75e28b413817655153157ff/cua_driver-0.20.0-py3-none-manylinux_2_31_x86_64.whl",
|
|
12
|
+
sha256: "f60c35696a37f37ac954935e478ae4754f220856d022036625c9400d72185961",
|
|
13
|
+
},
|
|
14
|
+
aarch64: {
|
|
15
|
+
url: "https://files.pythonhosted.org/packages/94/9d/1c1838b69067e83266c3d2aae02d74eef353a43dc8644884ccf03fe7f933/cua_driver-0.20.0-py3-none-manylinux_2_31_aarch64.whl",
|
|
16
|
+
sha256: "48833bc5e4c60e701fc9eefb57dbac36ec77ef3990f816fbbe85b4e954af2c77",
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
const CDP_HELPER_SOURCE = String.raw `const [action, encoded = ""] = process.argv.slice(2);
|
|
20
|
+
const input = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8") || "{}");
|
|
21
|
+
const pages = await fetch("http://127.0.0.1:9222/json/list").then((r) => r.json());
|
|
22
|
+
const page = pages.find((item) => item.type === "page" && item.webSocketDebuggerUrl);
|
|
23
|
+
if (!page) throw new Error("no debuggable browser page");
|
|
24
|
+
if (input.url && page.url !== input.url) throw new Error("page changed; take a new browser snapshot");
|
|
25
|
+
const socket = new WebSocket(page.webSocketDebuggerUrl);
|
|
26
|
+
await new Promise((resolve, reject) => {
|
|
27
|
+
socket.addEventListener("open", resolve, { once: true });
|
|
28
|
+
socket.addEventListener("error", () => reject(new Error("DevTools connection failed")), { once: true });
|
|
29
|
+
});
|
|
30
|
+
let nextId = 0;
|
|
31
|
+
const pending = new Map();
|
|
32
|
+
socket.addEventListener("message", (event) => {
|
|
33
|
+
const message = JSON.parse(String(event.data));
|
|
34
|
+
if (!message.id) return;
|
|
35
|
+
const waiter = pending.get(message.id);
|
|
36
|
+
if (!waiter) return;
|
|
37
|
+
pending.delete(message.id);
|
|
38
|
+
if (message.error) waiter.reject(new Error(message.error.message));
|
|
39
|
+
else waiter.resolve(message.result ?? {});
|
|
40
|
+
});
|
|
41
|
+
const send = (method, params = {}) => new Promise((resolve, reject) => {
|
|
42
|
+
const id = ++nextId;
|
|
43
|
+
pending.set(id, { resolve, reject });
|
|
44
|
+
socket.send(JSON.stringify({ id, method, params }));
|
|
45
|
+
});
|
|
46
|
+
const refId = (value) => {
|
|
47
|
+
const match = /^b(\d+)$/.exec(String(value ?? ""));
|
|
48
|
+
if (!match) throw new Error("invalid or stale browser ref; take a new snapshot");
|
|
49
|
+
return Number(match[1]);
|
|
50
|
+
};
|
|
51
|
+
if (action === "snapshot") {
|
|
52
|
+
await send("Accessibility.enable");
|
|
53
|
+
const { nodes = [] } = await send("Accessibility.getFullAXTree", { depth: 14 });
|
|
54
|
+
const useful = new Set(["button", "checkbox", "combobox", "heading", "link", "menuitem", "radio", "searchbox", "slider", "spinbutton", "switch", "tab", "textbox"]);
|
|
55
|
+
const elements = [];
|
|
56
|
+
for (const node of nodes) {
|
|
57
|
+
const role = String(node.role?.value ?? "").toLowerCase();
|
|
58
|
+
const name = String(node.name?.value ?? "").replace(/\s+/g, " ").trim().slice(0, 180);
|
|
59
|
+
const backend = Number(node.backendDOMNodeId ?? 0);
|
|
60
|
+
if (!backend || !useful.has(role) || (!name && role !== "textbox" && role !== "searchbox")) continue;
|
|
61
|
+
const disabled = node.properties?.some((property) => property.name === "disabled" && property.value?.value === true) ?? false;
|
|
62
|
+
elements.push({ ref: "b" + backend, role, name: name || "unnamed", disabled });
|
|
63
|
+
if (elements.length >= 250) break;
|
|
64
|
+
}
|
|
65
|
+
process.stdout.write(JSON.stringify({ title: String(page.title ?? "").slice(0, 200), url: page.url, elements }));
|
|
66
|
+
} else if (action === "click") {
|
|
67
|
+
const backendNodeId = refId(input.ref);
|
|
68
|
+
const { model } = await send("DOM.getBoxModel", { backendNodeId });
|
|
69
|
+
const quad = model?.border ?? model?.content;
|
|
70
|
+
if (!Array.isArray(quad) || quad.length < 8) throw new Error("element is not visible; take a new snapshot");
|
|
71
|
+
const x = (quad[0] + quad[2] + quad[4] + quad[6]) / 4;
|
|
72
|
+
const y = (quad[1] + quad[3] + quad[5] + quad[7]) / 4;
|
|
73
|
+
await send("Input.dispatchMouseEvent", { type: "mousePressed", x, y, button: "left", clickCount: 1 });
|
|
74
|
+
await send("Input.dispatchMouseEvent", { type: "mouseReleased", x, y, button: "left", clickCount: 1 });
|
|
75
|
+
process.stdout.write(JSON.stringify({ ok: true, ref: input.ref }));
|
|
76
|
+
} else if (action === "fill") {
|
|
77
|
+
const backendNodeId = refId(input.ref);
|
|
78
|
+
await send("DOM.focus", { backendNodeId });
|
|
79
|
+
await send("Input.dispatchKeyEvent", { type: "keyDown", key: "a", code: "KeyA", modifiers: 2 });
|
|
80
|
+
await send("Input.dispatchKeyEvent", { type: "keyUp", key: "a", code: "KeyA", modifiers: 2 });
|
|
81
|
+
await send("Input.dispatchKeyEvent", { type: "keyDown", key: "Backspace", code: "Backspace" });
|
|
82
|
+
await send("Input.dispatchKeyEvent", { type: "keyUp", key: "Backspace", code: "Backspace" });
|
|
83
|
+
await send("Input.insertText", { text: String(input.text ?? "") });
|
|
84
|
+
process.stdout.write(JSON.stringify({ ok: true, ref: input.ref }));
|
|
85
|
+
} else {
|
|
86
|
+
throw new Error("unknown browser action");
|
|
87
|
+
}
|
|
88
|
+
socket.close();`;
|
|
89
|
+
const shellQuote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
|
|
90
|
+
export const MAX_REMOTE_COMMAND_LENGTH = 4_000;
|
|
91
|
+
/** Run a user- or model-supplied command without inheriting provider or
|
|
92
|
+
* account credentials from an older box image. Keep this shared by the bot
|
|
93
|
+
* tool and the owner's Computer-panel console so the two boundaries cannot
|
|
94
|
+
* drift apart. */
|
|
95
|
+
export function isolatedRemoteCommand(command) {
|
|
96
|
+
return [
|
|
97
|
+
"exec env -i",
|
|
98
|
+
'HOME="$HOME"',
|
|
99
|
+
'USER="${USER:-$(id -un)}"',
|
|
100
|
+
'LOGNAME="${LOGNAME:-${USER:-$(id -un)}}"',
|
|
101
|
+
'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"',
|
|
102
|
+
'DISPLAY="${DISPLAY:-:0}"',
|
|
103
|
+
'XAUTHORITY="${XAUTHORITY:-$HOME/.Xauthority}"',
|
|
104
|
+
'XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"',
|
|
105
|
+
'DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-}"',
|
|
106
|
+
"/bin/bash -c",
|
|
107
|
+
shellQuote(command),
|
|
108
|
+
].join(" ");
|
|
109
|
+
}
|
|
110
|
+
/** Start the already-installed daemon after a box resume. This is cheap when
|
|
111
|
+
* it is healthy and intentionally does not install anything on the hot path. */
|
|
112
|
+
export function ensureRemoteCuaCommand() {
|
|
113
|
+
return [
|
|
114
|
+
`if [ -x ${REMOTE_CUA_EXECUTABLE} ]; then`,
|
|
115
|
+
` mkdir -p ${REMOTE_CUA_SOCKET.slice(0, REMOTE_CUA_SOCKET.lastIndexOf("/"))}`,
|
|
116
|
+
` if ! ${REMOTE_CUA_EXECUTABLE} status --socket ${REMOTE_CUA_SOCKET} >/dev/null 2>&1; then`,
|
|
117
|
+
` rm -f ${REMOTE_CUA_SOCKET}`,
|
|
118
|
+
' display=${DISPLAY:-$(find /tmp/.X11-unix -maxdepth 1 -name "X*" -printf ":%f\\n" 2>/dev/null | sed "s/:X/:/" | head -1)}',
|
|
119
|
+
' display=${display:-:0}',
|
|
120
|
+
` nohup env HOME="$HOME" DISPLAY="$display" CUA_DRIVER_INSTALL_CHANNEL=python_package CUA_DRIVER_RS_TELEMETRY_ENABLED=0 ${REMOTE_CUA_EXECUTABLE} serve --socket ${REMOTE_CUA_SOCKET} --permission-mode standard > /tmp/ogb-cua-driver.log 2>&1 &`,
|
|
121
|
+
` for i in 1 2 3 4 5 6 7 8 9 10; do ${REMOTE_CUA_EXECUTABLE} status --socket ${REMOTE_CUA_SOCKET} >/dev/null 2>&1 && break; sleep 0.2; done`,
|
|
122
|
+
" fi",
|
|
123
|
+
"fi",
|
|
124
|
+
].join("\n");
|
|
125
|
+
}
|
|
126
|
+
/** Idempotent setup. The exact wheel is verified before its bundled native
|
|
127
|
+
* executable is installed; installation remains asynchronous so first-time
|
|
128
|
+
* provisioning does not block the desktop for several minutes. */
|
|
129
|
+
export function remoteComputerBootstrapCommand(botName) {
|
|
130
|
+
const helper = Buffer.from(CDP_HELPER_SOURCE).toString("base64");
|
|
131
|
+
const installer = [
|
|
132
|
+
"set -eu",
|
|
133
|
+
"trap 'rm -f /tmp/ogb-cua-installing' EXIT",
|
|
134
|
+
"sudo mkdir -p /opt/ogb/run",
|
|
135
|
+
'sudo chown -R "$(id -u):$(id -g)" /opt/ogb',
|
|
136
|
+
'arch="$(uname -m)"',
|
|
137
|
+
`case "$arch" in x86_64) url=${shellQuote(REMOTE_CUA_WHEELS.x86_64.url)}; sha=${REMOTE_CUA_WHEELS.x86_64.sha256} ;; aarch64|arm64) url=${shellQuote(REMOTE_CUA_WHEELS.aarch64.url)}; sha=${REMOTE_CUA_WHEELS.aarch64.sha256} ;; *) echo "unsupported architecture: $arch" >&2; exit 1 ;; esac`,
|
|
138
|
+
'wheel="/tmp/cua-driver-${sha}.whl"',
|
|
139
|
+
'curl -fsSL "$url" -o "$wheel"',
|
|
140
|
+
'echo "$sha $wheel" | sha256sum -c -',
|
|
141
|
+
'python3 - "$wheel" <<\'PY\'\nimport os, sys, zipfile\nwheel = sys.argv[1]\nwith zipfile.ZipFile(wheel) as archive:\n names = [name for name in archive.namelist() if name == "cua_driver/bin/cua-driver" or name.endswith("/cua_driver/bin/cua-driver")]\n if len(names) != 1:\n raise SystemExit("cua-driver executable missing from wheel")\n with archive.open(names[0]) as source, open("/opt/ogb/cua-driver", "wb") as target:\n target.write(source.read())\nos.chmod("/opt/ogb/cua-driver", 0o755)\nPY',
|
|
142
|
+
`test "$(${REMOTE_CUA_EXECUTABLE} --version)" = "cua-driver ${REMOTE_CUA_VERSION}"`,
|
|
143
|
+
`touch /opt/ogb/cua-${REMOTE_CUA_VERSION}-ready`,
|
|
144
|
+
'rm -f "$wheel"',
|
|
145
|
+
].join("\n");
|
|
146
|
+
// The display name is untrusted. Encode the banner before composing the
|
|
147
|
+
// nested tmux shell so substitutions/backticks can never become syntax.
|
|
148
|
+
const banner = Buffer.from(` ▦ ${botName}'s computer — OpenMausBot`).toString("base64");
|
|
149
|
+
const tmuxSessionCommand = [
|
|
150
|
+
"echo",
|
|
151
|
+
`printf %s ${shellQuote(banner)} | base64 -d`,
|
|
152
|
+
"echo",
|
|
153
|
+
"exec bash -i",
|
|
154
|
+
].join("; ");
|
|
155
|
+
return [
|
|
156
|
+
"if ! command -v xdotool >/dev/null || ! command -v convert >/dev/null || ! command -v curl >/dev/null || ! command -v python3 >/dev/null; then sudo apt-get update -qq || true; sudo apt-get install -y -qq ca-certificates curl python3 gnome-screenshot xclip wmctrl xdotool imagemagick scrot >/dev/null 2>&1 || true; fi",
|
|
157
|
+
"sudo mkdir -p /opt/ogb/run",
|
|
158
|
+
`printf %s ${shellQuote(helper)} | base64 -d | sudo tee ${REMOTE_CDP_HELPER} >/dev/null`,
|
|
159
|
+
`sudo chmod 0755 ${REMOTE_CDP_HELPER}`,
|
|
160
|
+
'pkill -f "^/opt/ogb/venv/bin/python -m computer_server( |$)" >/dev/null 2>&1 || true',
|
|
161
|
+
`[ -f /opt/ogb/cua-${REMOTE_CUA_VERSION}-ready ] || [ -f /tmp/ogb-cua-installing ] || { touch /tmp/ogb-cua-installing; nohup bash -c ${shellQuote(installer)} > /tmp/ogb-cua-install.log 2>&1 & }`,
|
|
162
|
+
ensureRemoteCuaCommand(),
|
|
163
|
+
`tmux has-session -t work 2>/dev/null || tmux new-session -d -s work ${shellQuote(tmuxSessionCommand)}`,
|
|
164
|
+
"echo bootstrapped",
|
|
165
|
+
].join("\n");
|
|
166
|
+
}
|
|
167
|
+
export function semanticBrowserCommand(action, input) {
|
|
168
|
+
const encoded = Buffer.from(JSON.stringify(input ?? {})).toString("base64url");
|
|
169
|
+
return `node ${REMOTE_CDP_HELPER} ${action} ${shellQuote(encoded)}`;
|
|
170
|
+
}
|