@axhub/genie 0.1.8 → 0.2.0
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/README.md +1 -1
- package/dist/api-docs.html +6 -6
- package/dist/assets/App-BfaNALgf.js +504 -0
- package/dist/assets/App-qxJ8_QYu.css +32 -0
- package/dist/assets/ReviewApp-DIT2yWk-.js +1 -0
- package/dist/assets/_basePickBy-Dz3NcIVK.js +1 -0
- package/dist/assets/_baseUniq-DON_Sg7x.js +1 -0
- package/dist/assets/abap-BdImnpbu.js +1 -0
- package/dist/assets/actionscript-3-CoDkCxhg.js +1 -0
- package/dist/assets/ada-bCR0ucgS.js +1 -0
- package/dist/assets/andromeeda-C4gqWexZ.js +1 -0
- package/dist/assets/angular-html-CU67Zn6k.js +1 -0
- package/dist/assets/angular-ts-BwZT4LLn.js +1 -0
- package/dist/assets/apache-Pmp26Uib.js +1 -0
- package/dist/assets/apex-D8_7TLub.js +1 -0
- package/dist/assets/apl-dKokRX4l.js +1 -0
- package/dist/assets/applescript-Co6uUVPk.js +1 -0
- package/dist/assets/ara-BRHolxvo.js +1 -0
- package/dist/assets/arc-Y4G80q-l.js +1 -0
- package/dist/assets/architectureDiagram-2XIMDMQ5-D_qR4657.js +36 -0
- package/dist/assets/asciidoc-Ve4PFQV2.js +1 -0
- package/dist/assets/asm-D_Q5rh1f.js +1 -0
- package/dist/assets/astro-CbQHKStN.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-BkioyH1T.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-DPOp833l.js +1 -0
- package/dist/assets/blade-D4QpJJKB.js +1 -0
- package/dist/assets/blockDiagram-WCTKOSBZ-NsmAlV5_.js +132 -0
- package/dist/assets/bsl-BO_Y6i37.js +1 -0
- package/dist/assets/c-BIGW1oBm.js +1 -0
- package/dist/assets/c3-eo99z4R2.js +1 -0
- package/dist/assets/c4Diagram-IC4MRINW-cbOJM4yr.js +10 -0
- package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
- package/dist/assets/cairo-KRGpt6FW.js +1 -0
- package/dist/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- package/dist/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- package/dist/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- package/dist/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- package/dist/assets/channel-C6KNnXlA.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-bLBhl74J.js +1 -0
- package/dist/assets/chunk-55IACEB6-D8kNkDUO.js +1 -0
- package/dist/assets/chunk-FMBD7UC4-BjR6UbXB.js +15 -0
- package/dist/assets/chunk-JSJVCQXG-luNqWn64.js +1 -0
- package/dist/assets/chunk-KX2RTZJC-CNnKm6dK.js +1 -0
- package/dist/assets/chunk-NQ4KR5QH-Cp9gb43u.js +220 -0
- package/dist/assets/chunk-QZHKN3VN-HlVYo2Oq.js +1 -0
- package/dist/assets/chunk-WL4C6EOR-CjSZoOGO.js +189 -0
- package/dist/assets/clarity-D53aC0YG.js +1 -0
- package/dist/assets/classDiagram-VBA2DB6C-BQlzzlH7.js +1 -0
- package/dist/assets/classDiagram-v2-RAHNMMFH-BQlzzlH7.js +1 -0
- package/dist/assets/clojure-P80f7IUj.js +1 -0
- package/dist/assets/clone-DMxS3qWP.js +1 -0
- package/dist/assets/cmake-D1j8_8rp.js +1 -0
- package/dist/assets/cobol-nwyudZeR.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-DkFqJrB1.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-DZWRjeEd.js +1 -0
- package/dist/assets/cpp-CofmeUqb.js +1 -0
- package/dist/assets/crystal-tKQVLTB8.js +1 -0
- package/dist/assets/csharp-COcwbKMJ.js +1 -0
- package/dist/assets/css-DPfMkruS.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/cytoscape.esm-2ZfV8NB5.js +331 -0
- package/dist/assets/d-85-TOEBH.js +1 -0
- package/dist/assets/dagre-KLK3FWXG-yAzUmqI7.js +4 -0
- package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/assets/dart-CF10PKvl.js +1 -0
- package/dist/assets/dax-CEL-wOlO.js +1 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/desktop-BmXAJ9_W.js +1 -0
- package/dist/assets/diagram-E7M64L7V-CvzlIvDJ.js +24 -0
- package/dist/assets/diagram-IFDJBPK2-DFMIJpuM.js +43 -0
- package/dist/assets/diagram-P4PSJMXO-KL-J3gyb.js +24 -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-BkV0erSs.js +1 -0
- package/dist/assets/elixir-CDX3lj18.js +1 -0
- package/dist/assets/elm-DbKCFpqz.js +1 -0
- package/dist/assets/emacs-lisp-C9XAeP06.js +1 -0
- package/dist/assets/erDiagram-INFDFZHY-BXszHbTM.js +70 -0
- package/dist/assets/erb-B12qg9BL.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/flowDiagram-PKNHOUZH-Ba43NVp6.js +162 -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/ganttDiagram-A5KZAMGK-uLHfhCrg.js +292 -0
- package/dist/assets/gdresource-BOOCDP_w.js +1 -0
- package/dist/assets/gdscript-C5YyOfLZ.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/gitGraphDiagram-K3NZZRJ6-BTEuFaiL.js +65 -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-Rg0-pVw9.js +1 -0
- package/dist/assets/glimmer-ts-U6CK756n.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-CxLEBnE3.js +1 -0
- package/dist/assets/graph-h2nuWjx4.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-CaT9iCJl.js +1 -0
- package/dist/assets/haml-B8DHNrY2.js +1 -0
- package/dist/assets/handlebars-BL8al0AC.js +1 -0
- package/dist/assets/haskell-Df6bDoY_.js +1 -0
- package/dist/assets/haxe-CzTSHFRz.js +1 -0
- package/dist/assets/hcl-BWvSN4gD.js +1 -0
- package/dist/assets/highlighted-body-TPN3WLV5-C6BY7XZJ.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-Cn-bp-IR.js +1 -0
- package/dist/assets/houston-DnULxvSX.js +1 -0
- package/dist/assets/html-GMplVEZG.js +1 -0
- package/dist/assets/html-derivative-BFtXZ54Q.js +1 -0
- package/dist/assets/http-jrhK8wxY.js +1 -0
- package/dist/assets/hurl-irOxFIW8.js +1 -0
- package/dist/assets/hxml-Bvhsp5Yf.js +1 -0
- package/dist/assets/hy-DFXneXwc.js +1 -0
- package/dist/assets/imba-DGztddWO.js +1 -0
- package/dist/assets/index-2198VgsK.css +1 -0
- package/dist/assets/index-C6Bb2jGF.js +2 -0
- package/dist/assets/infoDiagram-LFFYTUFH-BOLfvCIq.js +2 -0
- package/dist/assets/ini-BEwlwnbL.js +1 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/ishikawaDiagram-PHBUUO56-BRzQ1ee5.js +70 -0
- package/dist/assets/java-CylS5w8V.js +1 -0
- package/dist/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/assets/jinja-4LBKfQ-Z.js +1 -0
- package/dist/assets/jison-wvAkD_A8.js +1 -0
- package/dist/assets/journeyDiagram-4ABVD52K-DXm_VcMy.js +139 -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-CxzCAyBv.js +1 -0
- package/dist/assets/just-Cw27pwNe.js +1 -0
- package/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- package/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- package/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
- package/dist/assets/kanban-definition-K7BYSVSG-D_oyzopl.js +89 -0
- package/dist/assets/kdl-DV7GczEv.js +1 -0
- package/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/dist/assets/kusto-DZf3V79B.js +1 -0
- package/dist/assets/laserwave-DUszq2jm.js +1 -0
- package/dist/assets/latex-CWtU0Tv5.js +1 -0
- package/dist/assets/layout-Q8YoR_E1.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/linear-B3qNg7di.js +1 -0
- package/dist/assets/liquid-DYVedYrR.js +1 -0
- package/dist/assets/llvm-DjAJT7YJ.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-C-HG3fhB.js +1 -0
- package/dist/assets/make-CHLpvVh8.js +1 -0
- package/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/dist/assets/marko-CnJfTvn9.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-BMNejdWA.js +1 -0
- package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/dist/assets/mermaid-O7DHMXV3-BVZ_4MKo.js +988 -0
- package/dist/assets/mermaid-mWjccvbQ.js +1 -0
- package/dist/assets/min-dark-CafNBF8u.js +1 -0
- package/dist/assets/min-light-CTRr51gU.js +1 -0
- package/dist/assets/mindmap-definition-YRQLILUH-CjulgYdi.js +68 -0
- package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
- package/dist/assets/mojo-rZm6bMo-.js +1 -0
- package/dist/assets/monokai-D4h5O-jR.js +1 -0
- package/dist/assets/moonbit-_H4v1dQx.js +1 -0
- package/dist/assets/move-IF9eRakj.js +1 -0
- package/dist/assets/narrat-DRg8JJMk.js +1 -0
- package/dist/assets/nextflow-Zz6hmt5N.js +1 -0
- package/dist/assets/nextflow-groovy-BeH2EWoN.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-CVrawwO9.js +1 -0
- package/dist/assets/nix-CwoSXNpI.js +1 -0
- package/dist/assets/nord-Ddv68eIx.js +1 -0
- package/dist/assets/nushell-Cz2AlsmD.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/ordinal-Cboi1Yqb.js +1 -0
- package/dist/assets/pascal-D93ZcfNL.js +1 -0
- package/dist/assets/perl-C0TMdlhV.js +1 -0
- package/dist/assets/php-Dhbhpdrm.js +1 -0
- package/dist/assets/pieDiagram-SKSYHLDU-8VzrefxA.js +30 -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-Dpen1YoG.js +1 -0
- package/dist/assets/prisma-Dd19v3D-.js +1 -0
- package/dist/assets/prolog-CbFg5uaA.js +1 -0
- package/dist/assets/proto-C7zT0LnQ.js +1 -0
- package/dist/assets/pug-CGlum2m_.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/quadrantDiagram-337W2JSQ-CFh-ijm2.js +7 -0
- package/dist/assets/r-Dspwwk_N.js +1 -0
- package/dist/assets/racket-BqYA7rlc.js +1 -0
- package/dist/assets/raku-DXvB9xmW.js +1 -0
- package/dist/assets/razor-Uh8Bk_45.js +1 -0
- package/dist/assets/red-bN70gL4F.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/requirementDiagram-Z7DCOOCP-BNPlTs5Q.js +73 -0
- package/dist/assets/riscv-BM1_JUlF.js +1 -0
- package/dist/assets/ron-D8l8udqQ.js +1 -0
- package/dist/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- package/dist/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- package/dist/assets/rose-pine-qdsjHGoJ.js +1 -0
- package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
- package/dist/assets/rst-BrH8l1NY.js +1 -0
- package/dist/assets/ruby-Dw2BHqvy.js +1 -0
- package/dist/assets/rust-B1yitclQ.js +1 -0
- package/dist/assets/sankeyDiagram-WA2Y5GQK-C5l_hYst.js +10 -0
- package/dist/assets/sas-cz2c8ADy.js +1 -0
- package/dist/assets/sass-Cj5Yp3dK.js +1 -0
- package/dist/assets/scala-C151Ov-r.js +1 -0
- package/dist/assets/scheme-C98Dy4si.js +1 -0
- package/dist/assets/scss-OYdSNvt2.js +1 -0
- package/dist/assets/sdbl-DVxCFoDh.js +1 -0
- package/dist/assets/sequenceDiagram-2WXFIKYE-B4a_rQw8.js +145 -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-BERRCDM3.js +1 -0
- package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
- package/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
- package/dist/assets/solarized-light-L9t79GZl.js +1 -0
- package/dist/assets/solidity-rGO070M0.js +1 -0
- package/dist/assets/soy-Brmx7dQM.js +1 -0
- package/dist/assets/sparql-rVzFXLq3.js +1 -0
- package/dist/assets/splunk-BtCnVYZw.js +1 -0
- package/dist/assets/sql-BLtJtn59.js +1 -0
- package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
- package/dist/assets/stata-BH5u7GGu.js +1 -0
- package/dist/assets/stateDiagram-RAJIS63D-Bt4mMmKB.js +1 -0
- package/dist/assets/stateDiagram-v2-FVOUBMTO-6NYMazfq.js +1 -0
- package/dist/assets/stylus-BEDo0Tqx.js +1 -0
- package/dist/assets/surrealql-Bq5Q-fJD.js +1 -0
- package/dist/assets/svelte-C_ipcX3V.js +1 -0
- package/dist/assets/swift-D82vCrfD.js +1 -0
- package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/dist/assets/system-verilog-CnnmHF94.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-P3uqSqPl.js +1 -0
- package/dist/assets/terraform-BETggiCN.js +1 -0
- package/dist/assets/tex-idrVyKtj.js +1 -0
- package/dist/assets/timeline-definition-YZTLITO2-CLYvSw_R.js +61 -0
- package/dist/assets/tokyo-night-hegEt444.js +1 -0
- package/dist/assets/toml-vGWfd6FD.js +1 -0
- package/dist/assets/treemap-KZPCXAKY-ksND0hZK.js +162 -0
- package/dist/assets/ts-tags-zn1MmPIZ.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-DNn4PbVi.js +1 -0
- package/dist/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/assets/typespec-BGHnOYBU.js +1 -0
- package/dist/assets/typst-DHCkPAjA.js +1 -0
- package/dist/assets/v-BcVCzyr7.js +1 -0
- package/dist/assets/vala-CsfeWuGM.js +1 -0
- package/dist/assets/vb-D17OF-Vu.js +1 -0
- package/dist/assets/{vendor-codemirror-B88_OPWf.js → vendor-codemirror-Dz7_EqNA.js} +3 -3
- package/dist/assets/{vendor-react-C3RJLQGO.js → vendor-react-Cpt6D04s.js} +11 -11
- package/dist/assets/vennDiagram-LZ73GAT5-CaQg4oZK.js +34 -0
- package/dist/assets/verilog-BQ8w6xss.js +1 -0
- package/dist/assets/vesper-DU1UobuO.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-DN_0RTcg.js +1 -0
- package/dist/assets/vue-html-AaS7Mt5G.js +1 -0
- package/dist/assets/vue-vine-CQOfvN7w.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/xychartDiagram-JWTSCODW-C8dCbTeM.js +7 -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 +35 -21
- package/dist/manifest.json +1 -1
- package/dist/sw.js +18 -46
- package/package.json +12 -8
- package/server/bin/codex-sdk-wrapper.js +49 -0
- package/server/channels/runtime/AgentRuntimeAdapter.js +2 -5
- package/server/channels/store/ChannelStore.js +73 -107
- package/server/claude-sdk.js +160 -6
- package/server/cli.js +590 -32
- package/server/cli.test.js +76 -0
- package/server/database/db.js +438 -372
- package/server/external-agent/auth.js +88 -0
- package/server/external-agent/service.js +1052 -0
- package/server/external-agent/service.test.js +41 -0
- package/server/external-agent/ws.js +1526 -0
- package/server/external-agent/ws.test.js +289 -0
- package/server/gemini-cli.js +108 -20
- package/server/index.js +115 -121
- package/server/load-env.js +16 -13
- package/server/openai-codex.js +165 -11
- package/server/opencode-cli.js +3 -2
- package/server/projects.js +432 -338
- package/server/routes/agent.js +347 -459
- package/server/routes/auth.js +14 -36
- package/server/routes/cli-auth.js +60 -113
- package/server/routes/commands.js +16 -26
- package/server/routes/git.js +5 -16
- package/server/routes/projects.js +2 -7
- package/server/routes/session-core.js +177 -0
- package/server/session-core/abortSession.js +48 -0
- package/server/session-core/eventStore.js +139 -0
- package/server/session-core/providerAdapters.js +84 -0
- package/server/session-core/providerDiscovery.js +235 -0
- package/server/session-core/runtimeState.js +390 -0
- package/server/session-core/runtimeWriter.js +59 -0
- package/server/utils/agentCallback.js +273 -0
- package/server/utils/agentImages.js +253 -0
- package/server/utils/codexPath.js +47 -0
- package/server/utils/defaultWorkingDirectory.js +34 -0
- package/shared/conversationEvents.js +1071 -0
- package/shared/modelConstants.js +18 -24
- package/dist/assets/index-CVjMty4a.js +0 -902
- package/dist/assets/index-eo5scY_Z.css +0 -32
- package/server/cursor-cli.js +0 -276
- package/server/database/init.sql +0 -98
- package/server/routes/cursor.js +0 -795
|
@@ -0,0 +1,1526 @@
|
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
2
|
+
|
|
3
|
+
import { SessionEventMirrorWriter } from '../session-core/runtimeWriter.js';
|
|
4
|
+
import { abortAgentSessionWithWriter } from '../session-core/abortSession.js';
|
|
5
|
+
import {
|
|
6
|
+
AgentSessionAbortedError,
|
|
7
|
+
buildSessionNavigation,
|
|
8
|
+
normalizeExternalAgentAbortRequest,
|
|
9
|
+
normalizeExternalAgentRunRequest,
|
|
10
|
+
runExternalAgentRequest
|
|
11
|
+
} from './service.js';
|
|
12
|
+
import {
|
|
13
|
+
buildSessionRuntimeSnapshot,
|
|
14
|
+
createSessionRuntimeSubscriptionKey,
|
|
15
|
+
subscribeToSessionRuntimeStateChanges
|
|
16
|
+
} from '../session-core/runtimeState.js';
|
|
17
|
+
import { SUPPORTED_PROVIDERS } from '../../shared/conversationEvents.js';
|
|
18
|
+
import { materializeDataUrlImageToFile, sanitizeFileName } from '../utils/agentImages.js';
|
|
19
|
+
|
|
20
|
+
const INTEGRATION_ROLE_FRONTEND_PAGE = 'frontend-page';
|
|
21
|
+
const INTEGRATION_ROLE_EXTERNAL_CLIENT = 'external-client';
|
|
22
|
+
const INTEGRATION_REQUEST_TTL_MS = 10 * 60 * 1000;
|
|
23
|
+
const INTEGRATION_PING_TIMEOUT_MS = 1500;
|
|
24
|
+
const INTEGRATION_EDITOR_REQUEST_TIMEOUT_MS = 15000;
|
|
25
|
+
const INTEGRATION_EDITOR_TYPE_PREFIX = 'integration.editor.';
|
|
26
|
+
const INTEGRATION_EDITOR_RESULT_SUFFIX = '.result';
|
|
27
|
+
const INTEGRATION_EDITOR_CAPABILITIES = Object.freeze({
|
|
28
|
+
'integration.editor.snapshot.get': 'editor.snapshot',
|
|
29
|
+
'integration.editor.nodes.list': 'editor.nodes.list',
|
|
30
|
+
'integration.editor.node.screenshot.get': 'editor.node.screenshot',
|
|
31
|
+
'integration.editor.context-images.get': 'editor.context-images',
|
|
32
|
+
'integration.editor.editing.set': 'editor.editing.set'
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const integrationConnections = new Map();
|
|
36
|
+
const pendingIntegrationRequests = new Map();
|
|
37
|
+
const pendingIntegrationPings = new Map();
|
|
38
|
+
const sessionStateSubscriptionsByWs = new Map();
|
|
39
|
+
const sessionStateSubscribersByKey = new Map();
|
|
40
|
+
|
|
41
|
+
function sendJson(ws, payload) {
|
|
42
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
43
|
+
ws.send(JSON.stringify(payload));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeNonEmptyString(value) {
|
|
48
|
+
if (typeof value !== 'string') {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const normalized = value.trim();
|
|
53
|
+
return normalized ? normalized : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createRuntimeId(prefix = 'integration') {
|
|
57
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
58
|
+
return `${prefix}_${crypto.randomUUID()}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isPlainObject(value) {
|
|
65
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeOptionalString(value) {
|
|
69
|
+
if (typeof value !== 'string') {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return value.trim();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function normalizePositiveInteger(value) {
|
|
76
|
+
const next = Number(value);
|
|
77
|
+
if (!Number.isInteger(next) || next <= 0) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return next;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isEditorResultMessageType(type) {
|
|
84
|
+
return typeof type === 'string'
|
|
85
|
+
&& type.startsWith(INTEGRATION_EDITOR_TYPE_PREFIX)
|
|
86
|
+
&& type.endsWith(INTEGRATION_EDITOR_RESULT_SUFFIX);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getRequestId(data) {
|
|
90
|
+
return normalizeNonEmptyString(data?.requestId) || null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function sendRequestError(ws, { requestId, provider, sessionId = null, error, statusCode = 400 }) {
|
|
94
|
+
sendJson(ws, {
|
|
95
|
+
type: 'agent.error',
|
|
96
|
+
requestId: requestId || null,
|
|
97
|
+
provider: provider || null,
|
|
98
|
+
sessionId,
|
|
99
|
+
error,
|
|
100
|
+
statusCode
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function sendIntegrationError(ws, { requestId, code, message }) {
|
|
105
|
+
sendJson(ws, {
|
|
106
|
+
type: 'integration.error',
|
|
107
|
+
requestId: requestId || null,
|
|
108
|
+
payload: {
|
|
109
|
+
code,
|
|
110
|
+
message
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function createAgentStateError(ws, { requestId, provider, sessionId, error, statusCode = 400 }) {
|
|
116
|
+
sendRequestError(ws, {
|
|
117
|
+
requestId,
|
|
118
|
+
provider,
|
|
119
|
+
sessionId,
|
|
120
|
+
error,
|
|
121
|
+
statusCode
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function normalizeAgentStatePayload(payload) {
|
|
126
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
127
|
+
throw Object.assign(new Error('payload must be an object'), { statusCode: 400 });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const sessionId = normalizeNonEmptyString(payload.sessionId);
|
|
131
|
+
if (!sessionId) {
|
|
132
|
+
throw Object.assign(new Error('sessionId is required'), { statusCode: 400 });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const provider = normalizeNonEmptyString(payload.provider)?.toLowerCase() || null;
|
|
136
|
+
if (!provider) {
|
|
137
|
+
throw Object.assign(new Error('provider is required'), { statusCode: 400 });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!SUPPORTED_PROVIDERS.includes(provider)) {
|
|
141
|
+
throw Object.assign(new Error(`provider must be one of: ${SUPPORTED_PROVIDERS.join(', ')}`), { statusCode: 400 });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
sessionId,
|
|
146
|
+
provider
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function addSessionStateSubscription(ws, subscriptionKey) {
|
|
151
|
+
if (!subscriptionKey) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!sessionStateSubscriptionsByWs.has(ws)) {
|
|
156
|
+
sessionStateSubscriptionsByWs.set(ws, new Set());
|
|
157
|
+
}
|
|
158
|
+
sessionStateSubscriptionsByWs.get(ws).add(subscriptionKey);
|
|
159
|
+
|
|
160
|
+
if (!sessionStateSubscribersByKey.has(subscriptionKey)) {
|
|
161
|
+
sessionStateSubscribersByKey.set(subscriptionKey, new Set());
|
|
162
|
+
}
|
|
163
|
+
sessionStateSubscribersByKey.get(subscriptionKey).add(ws);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function removeSessionStateSubscriptionsForWs(ws) {
|
|
167
|
+
const subscriptionKeys = sessionStateSubscriptionsByWs.get(ws);
|
|
168
|
+
if (!subscriptionKeys) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
subscriptionKeys.forEach((subscriptionKey) => {
|
|
173
|
+
const subscribers = sessionStateSubscribersByKey.get(subscriptionKey);
|
|
174
|
+
if (!subscribers) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
subscribers.delete(ws);
|
|
179
|
+
if (subscribers.size === 0) {
|
|
180
|
+
sessionStateSubscribersByKey.delete(subscriptionKey);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
sessionStateSubscriptionsByWs.delete(ws);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function handleAgentStateQuery(ws, data) {
|
|
188
|
+
const requestId = getRequestId(data);
|
|
189
|
+
|
|
190
|
+
let normalized = null;
|
|
191
|
+
try {
|
|
192
|
+
normalized = normalizeAgentStatePayload(data?.payload);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
createAgentStateError(ws, {
|
|
195
|
+
requestId,
|
|
196
|
+
provider: data?.payload?.provider || null,
|
|
197
|
+
sessionId: data?.payload?.sessionId || null,
|
|
198
|
+
error: error.message,
|
|
199
|
+
statusCode: error.statusCode || 400
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const snapshot = await buildSessionRuntimeSnapshot(normalized.provider, normalized.sessionId);
|
|
205
|
+
if (!snapshot) {
|
|
206
|
+
createAgentStateError(ws, {
|
|
207
|
+
requestId,
|
|
208
|
+
provider: normalized.provider,
|
|
209
|
+
sessionId: normalized.sessionId,
|
|
210
|
+
error: 'Session not found for provider',
|
|
211
|
+
statusCode: 404
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
sendJson(ws, {
|
|
217
|
+
type: 'agent.state.snapshot',
|
|
218
|
+
requestId,
|
|
219
|
+
payload: {
|
|
220
|
+
sessionId: snapshot.sessionId,
|
|
221
|
+
provider: snapshot.provider,
|
|
222
|
+
phase: snapshot.phase,
|
|
223
|
+
isLoading: snapshot.isLoading,
|
|
224
|
+
canAbortSession: snapshot.canAbortSession,
|
|
225
|
+
hasPendingApproval: snapshot.hasPendingApproval,
|
|
226
|
+
updatedAt: snapshot.updatedAt
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function handleAgentStateSubscribe(ws, data) {
|
|
232
|
+
const requestId = getRequestId(data);
|
|
233
|
+
|
|
234
|
+
let normalized = null;
|
|
235
|
+
try {
|
|
236
|
+
normalized = normalizeAgentStatePayload(data?.payload);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
createAgentStateError(ws, {
|
|
239
|
+
requestId,
|
|
240
|
+
provider: data?.payload?.provider || null,
|
|
241
|
+
sessionId: data?.payload?.sessionId || null,
|
|
242
|
+
error: error.message,
|
|
243
|
+
statusCode: error.statusCode || 400
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const snapshot = await buildSessionRuntimeSnapshot(normalized.provider, normalized.sessionId);
|
|
249
|
+
if (!snapshot) {
|
|
250
|
+
createAgentStateError(ws, {
|
|
251
|
+
requestId,
|
|
252
|
+
provider: normalized.provider,
|
|
253
|
+
sessionId: normalized.sessionId,
|
|
254
|
+
error: 'Session not found for provider',
|
|
255
|
+
statusCode: 404
|
|
256
|
+
});
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const subscriptionKey = createSessionRuntimeSubscriptionKey(normalized.provider, normalized.sessionId);
|
|
261
|
+
addSessionStateSubscription(ws, subscriptionKey);
|
|
262
|
+
|
|
263
|
+
sendJson(ws, {
|
|
264
|
+
type: 'agent.state.subscribed',
|
|
265
|
+
requestId,
|
|
266
|
+
payload: {
|
|
267
|
+
sessionId: snapshot.sessionId,
|
|
268
|
+
provider: snapshot.provider,
|
|
269
|
+
subscriptionKey
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function cleanupExpiredPendingIntegrationRequests() {
|
|
275
|
+
const now = Date.now();
|
|
276
|
+
for (const [requestId, entry] of pendingIntegrationRequests.entries()) {
|
|
277
|
+
if (!entry?.createdAt || now - entry.createdAt > INTEGRATION_REQUEST_TTL_MS) {
|
|
278
|
+
if (entry?.timeoutId) {
|
|
279
|
+
clearTimeout(entry.timeoutId);
|
|
280
|
+
}
|
|
281
|
+
pendingIntegrationRequests.delete(requestId);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function clearPendingIntegrationRequest(requestId) {
|
|
287
|
+
const entry = pendingIntegrationRequests.get(requestId);
|
|
288
|
+
if (entry?.timeoutId) {
|
|
289
|
+
clearTimeout(entry.timeoutId);
|
|
290
|
+
}
|
|
291
|
+
pendingIntegrationRequests.delete(requestId);
|
|
292
|
+
return entry || null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function storePendingIntegrationRequest(requestId, originWs, options = {}) {
|
|
296
|
+
if (!requestId) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
cleanupExpiredPendingIntegrationRequests();
|
|
301
|
+
const timeoutMs = normalizePositiveInteger(options.timeoutMs) || INTEGRATION_EDITOR_REQUEST_TIMEOUT_MS;
|
|
302
|
+
const nextEntry = {
|
|
303
|
+
originWs,
|
|
304
|
+
createdAt: Date.now(),
|
|
305
|
+
timeoutId: null,
|
|
306
|
+
awaitResult: options.awaitResult === true,
|
|
307
|
+
requestType: options.requestType || null,
|
|
308
|
+
requestPayload: isPlainObject(options.requestPayload) ? { ...options.requestPayload } : null
|
|
309
|
+
};
|
|
310
|
+
nextEntry.timeoutId = setTimeout(() => {
|
|
311
|
+
const current = pendingIntegrationRequests.get(requestId);
|
|
312
|
+
if (current !== nextEntry) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
pendingIntegrationRequests.delete(requestId);
|
|
316
|
+
if (!originWs || originWs.readyState !== WebSocket.OPEN) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
sendIntegrationError(originWs, {
|
|
320
|
+
requestId,
|
|
321
|
+
code: 'REQUEST_TIMEOUT',
|
|
322
|
+
message: `Timed out waiting for response to ${options.requestType || 'integration request'}`
|
|
323
|
+
});
|
|
324
|
+
}, timeoutMs);
|
|
325
|
+
pendingIntegrationRequests.set(requestId, nextEntry);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function removePendingIntegrationRequestsForWs(ws) {
|
|
329
|
+
for (const [requestId, entry] of pendingIntegrationRequests.entries()) {
|
|
330
|
+
if (entry?.originWs === ws) {
|
|
331
|
+
clearPendingIntegrationRequest(requestId);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function touchIntegrationConnection(ws) {
|
|
337
|
+
const existing = integrationConnections.get(ws);
|
|
338
|
+
if (!existing) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const updated = {
|
|
343
|
+
...existing,
|
|
344
|
+
lastSeenAt: new Date().toISOString()
|
|
345
|
+
};
|
|
346
|
+
integrationConnections.set(ws, updated);
|
|
347
|
+
return updated;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function listIntegrationConnections({ role = null, channel = null, clientId = null } = {}) {
|
|
351
|
+
return Array.from(integrationConnections.values()).filter((meta) => {
|
|
352
|
+
if (role && meta.role !== role) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (channel && meta.channel !== channel) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (clientId && meta.clientId !== clientId) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return true;
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function buildEditorClientSummary(meta) {
|
|
369
|
+
return {
|
|
370
|
+
channel: meta.channel,
|
|
371
|
+
clientId: meta.clientId,
|
|
372
|
+
sessionId: meta.sessionId || null,
|
|
373
|
+
pageUrl: meta.pageUrl || null,
|
|
374
|
+
lastSeenAt: meta.lastSeenAt || null,
|
|
375
|
+
connectedAt: meta.connectedAt || null,
|
|
376
|
+
connectionId: meta.connectionId || null,
|
|
377
|
+
capabilities: Array.isArray(meta.capabilities) ? meta.capabilities.slice() : []
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function sanitizeFallbackBaseName(value, fallback = 'image') {
|
|
382
|
+
return sanitizeFileName(String(value || '').trim()) || fallback;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function materializeEditorScreenshotPayload(payload, pendingRequest) {
|
|
386
|
+
if (!isPlainObject(payload)) {
|
|
387
|
+
throw Object.assign(new Error('editor screenshot result payload must be an object'), {
|
|
388
|
+
code: 'INVALID_PAYLOAD'
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (typeof payload.absolutePath === 'string' && payload.absolutePath.trim()) {
|
|
393
|
+
return payload;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const imageSource = isPlainObject(payload.image) ? payload.image : payload;
|
|
397
|
+
if (typeof imageSource.data !== 'string' || !imageSource.data.trim()) {
|
|
398
|
+
throw Object.assign(new Error('editor screenshot result must include image.data as a data URL'), {
|
|
399
|
+
code: 'INVALID_PAYLOAD'
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const outputDir = normalizeOptionalString(pendingRequest?.requestPayload?.downloadPath) || process.cwd();
|
|
404
|
+
const elementKey = normalizeOptionalString(payload.elementKey)
|
|
405
|
+
|| normalizeOptionalString(pendingRequest?.requestPayload?.elementKey)
|
|
406
|
+
|| 'node-screenshot';
|
|
407
|
+
const fileMeta = await materializeDataUrlImageToFile(imageSource, outputDir, {
|
|
408
|
+
label: 'editor screenshot',
|
|
409
|
+
fallbackBaseName: sanitizeFallbackBaseName(elementKey, 'node-screenshot')
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
return {
|
|
413
|
+
...payload,
|
|
414
|
+
elementKey,
|
|
415
|
+
absolutePath: fileMeta.absolutePath,
|
|
416
|
+
mimeType: fileMeta.mimeType,
|
|
417
|
+
width: normalizePositiveInteger(payload.width) || normalizePositiveInteger(imageSource.width) || null,
|
|
418
|
+
height: normalizePositiveInteger(payload.height) || normalizePositiveInteger(imageSource.height) || null,
|
|
419
|
+
size: fileMeta.size
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
async function materializeEditorContextImagesPayload(payload, pendingRequest) {
|
|
424
|
+
if (!isPlainObject(payload) || !Array.isArray(payload.items)) {
|
|
425
|
+
throw Object.assign(new Error('editor context-images result payload must include items[]'), {
|
|
426
|
+
code: 'INVALID_PAYLOAD'
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (payload.items.every((item) => typeof item?.absolutePath === 'string' && item.absolutePath.trim())) {
|
|
431
|
+
return payload;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const outputDir = normalizeOptionalString(pendingRequest?.requestPayload?.downloadPath) || process.cwd();
|
|
435
|
+
const nextItems = [];
|
|
436
|
+
|
|
437
|
+
for (const [index, item] of payload.items.entries()) {
|
|
438
|
+
if (!isPlainObject(item)) {
|
|
439
|
+
throw Object.assign(new Error(`editor context image item ${index} must be an object`), {
|
|
440
|
+
code: 'INVALID_PAYLOAD'
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const fileMeta = await materializeDataUrlImageToFile(item, outputDir, {
|
|
445
|
+
label: `editor context image item ${index}`,
|
|
446
|
+
fallbackBaseName: sanitizeFallbackBaseName(item.name || item.id || `context-image-${index + 1}`, `context-image-${index + 1}`)
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
nextItems.push({
|
|
450
|
+
...item,
|
|
451
|
+
id: normalizeOptionalString(item.id) || `context-image-${index + 1}`,
|
|
452
|
+
name: normalizeOptionalString(item.name) || sanitizeFallbackBaseName(item.id || `context-image-${index + 1}`, `context-image-${index + 1}`),
|
|
453
|
+
absolutePath: fileMeta.absolutePath,
|
|
454
|
+
mimeType: fileMeta.mimeType,
|
|
455
|
+
size: fileMeta.size,
|
|
456
|
+
createdAt: Number.isFinite(Number(item.createdAt)) ? Number(item.createdAt) : Date.now(),
|
|
457
|
+
source: normalizeOptionalString(item.source) || 'prompt-context'
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
...payload,
|
|
463
|
+
items: nextItems
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
async function maybeMaterializeEditorResult(data, pendingRequest) {
|
|
468
|
+
if (!pendingRequest?.requestType || !isPlainObject(data)) {
|
|
469
|
+
return data;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (
|
|
473
|
+
pendingRequest.requestType === 'integration.editor.node.screenshot.get'
|
|
474
|
+
&& data.type === 'integration.editor.node.screenshot.result'
|
|
475
|
+
) {
|
|
476
|
+
return {
|
|
477
|
+
...data,
|
|
478
|
+
payload: await materializeEditorScreenshotPayload(data.payload, pendingRequest)
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (
|
|
483
|
+
pendingRequest.requestType === 'integration.editor.context-images.get'
|
|
484
|
+
&& data.type === 'integration.editor.context-images.result'
|
|
485
|
+
) {
|
|
486
|
+
return {
|
|
487
|
+
...data,
|
|
488
|
+
payload: await materializeEditorContextImagesPayload(data.payload, pendingRequest)
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return data;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function buildPresenceEventPayload(meta, event) {
|
|
496
|
+
return {
|
|
497
|
+
channel: meta.channel,
|
|
498
|
+
event,
|
|
499
|
+
clientId: meta.clientId,
|
|
500
|
+
sessionId: meta.sessionId || null,
|
|
501
|
+
pageUrl: meta.pageUrl || null,
|
|
502
|
+
timestamp: new Date().toISOString()
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function broadcastPresenceEvent(meta, event) {
|
|
507
|
+
if (!meta?.channel) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const payload = buildPresenceEventPayload(meta, event);
|
|
512
|
+
for (const clientMeta of listIntegrationConnections({
|
|
513
|
+
role: INTEGRATION_ROLE_EXTERNAL_CLIENT,
|
|
514
|
+
channel: meta.channel
|
|
515
|
+
})) {
|
|
516
|
+
sendJson(clientMeta.ws, {
|
|
517
|
+
type: 'integration.presence',
|
|
518
|
+
requestId: createRuntimeId('presence'),
|
|
519
|
+
payload
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function unregisterIntegrationConnection(ws) {
|
|
525
|
+
const existing = integrationConnections.get(ws);
|
|
526
|
+
removeSessionStateSubscriptionsForWs(ws);
|
|
527
|
+
if (!existing) {
|
|
528
|
+
removePendingIntegrationRequestsForWs(ws);
|
|
529
|
+
removePendingIntegrationPingsForWs(ws);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
integrationConnections.delete(ws);
|
|
534
|
+
removePendingIntegrationRequestsForWs(ws);
|
|
535
|
+
removePendingIntegrationPingsForWs(ws);
|
|
536
|
+
|
|
537
|
+
if (existing.role === INTEGRATION_ROLE_FRONTEND_PAGE) {
|
|
538
|
+
broadcastPresenceEvent(existing, 'frontend-offline');
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function validateIntegrationConnectPayload(payload) {
|
|
543
|
+
if (!payload || typeof payload !== 'object') {
|
|
544
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const role = normalizeNonEmptyString(payload.role);
|
|
548
|
+
if (role !== INTEGRATION_ROLE_FRONTEND_PAGE && role !== INTEGRATION_ROLE_EXTERNAL_CLIENT) {
|
|
549
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'role must be frontend-page or external-client' };
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const channel = normalizeNonEmptyString(payload.channel);
|
|
553
|
+
if (!channel) {
|
|
554
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const clientId = normalizeNonEmptyString(payload.clientId);
|
|
558
|
+
if (!clientId) {
|
|
559
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'clientId is required' };
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return {
|
|
563
|
+
ok: true,
|
|
564
|
+
payload: {
|
|
565
|
+
role,
|
|
566
|
+
channel,
|
|
567
|
+
clientId,
|
|
568
|
+
pageUrl: normalizeNonEmptyString(payload.pageUrl) || null,
|
|
569
|
+
sessionId: normalizeNonEmptyString(payload.sessionId) || null,
|
|
570
|
+
capabilities: Array.isArray(payload.capabilities)
|
|
571
|
+
? payload.capabilities.filter((value) => typeof value === 'string' && value.trim())
|
|
572
|
+
: []
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function handleIntegrationConnect(ws, data) {
|
|
578
|
+
const requestId = getRequestId(data);
|
|
579
|
+
const validation = validateIntegrationConnectPayload(data?.payload);
|
|
580
|
+
if (!validation.ok) {
|
|
581
|
+
sendIntegrationError(ws, {
|
|
582
|
+
requestId,
|
|
583
|
+
code: validation.code,
|
|
584
|
+
message: validation.message
|
|
585
|
+
});
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const now = new Date().toISOString();
|
|
590
|
+
const nextMeta = {
|
|
591
|
+
...validation.payload,
|
|
592
|
+
ws,
|
|
593
|
+
connectionId: integrationConnections.get(ws)?.connectionId || createRuntimeId('conn'),
|
|
594
|
+
connectedAt: integrationConnections.get(ws)?.connectedAt || now,
|
|
595
|
+
lastSeenAt: now
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
const previous = integrationConnections.get(ws) || null;
|
|
599
|
+
integrationConnections.set(ws, nextMeta);
|
|
600
|
+
|
|
601
|
+
sendJson(ws, {
|
|
602
|
+
type: 'integration.connected',
|
|
603
|
+
requestId,
|
|
604
|
+
payload: {
|
|
605
|
+
connectionId: nextMeta.connectionId,
|
|
606
|
+
role: nextMeta.role,
|
|
607
|
+
channel: nextMeta.channel,
|
|
608
|
+
clientId: nextMeta.clientId,
|
|
609
|
+
serverTime: now
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
if (previous?.role === INTEGRATION_ROLE_FRONTEND_PAGE) {
|
|
614
|
+
const previousKey = `${previous.channel}::${previous.clientId}`;
|
|
615
|
+
const nextKey = `${nextMeta.channel}::${nextMeta.clientId}`;
|
|
616
|
+
if (previousKey !== nextKey) {
|
|
617
|
+
broadcastPresenceEvent(previous, 'frontend-offline');
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (nextMeta.role === INTEGRATION_ROLE_FRONTEND_PAGE) {
|
|
622
|
+
const previousKey = previous ? `${previous.channel}::${previous.clientId}::${previous.role}` : null;
|
|
623
|
+
const nextKey = `${nextMeta.channel}::${nextMeta.clientId}::${nextMeta.role}`;
|
|
624
|
+
if (previousKey !== nextKey) {
|
|
625
|
+
broadcastPresenceEvent(nextMeta, 'frontend-online');
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function buildFrontendPresence(channel, targetClientId = null) {
|
|
631
|
+
const clients = listIntegrationConnections({
|
|
632
|
+
role: INTEGRATION_ROLE_FRONTEND_PAGE,
|
|
633
|
+
channel,
|
|
634
|
+
clientId: targetClientId
|
|
635
|
+
}).map((meta) => ({
|
|
636
|
+
clientId: meta.clientId,
|
|
637
|
+
lastSeenAt: meta.lastSeenAt,
|
|
638
|
+
pageUrl: meta.pageUrl || null,
|
|
639
|
+
sessionId: meta.sessionId || null
|
|
640
|
+
}));
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
connected: clients.length > 0,
|
|
644
|
+
count: clients.length,
|
|
645
|
+
clients
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function validateIntegrationPingPayload(payload) {
|
|
650
|
+
if (!payload || typeof payload !== 'object') {
|
|
651
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const channel = normalizeNonEmptyString(payload.channel);
|
|
655
|
+
if (!channel) {
|
|
656
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return {
|
|
660
|
+
ok: true,
|
|
661
|
+
payload: {
|
|
662
|
+
channel,
|
|
663
|
+
targetClientId: normalizeNonEmptyString(payload.targetClientId)
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function buildResponsiveFrontendPresence(responses) {
|
|
669
|
+
const clients = Array.from(responses.values());
|
|
670
|
+
return {
|
|
671
|
+
connected: clients.length > 0,
|
|
672
|
+
count: clients.length,
|
|
673
|
+
clients
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function finalizePendingIntegrationPing(probeRequestId) {
|
|
678
|
+
const pendingPing = pendingIntegrationPings.get(probeRequestId);
|
|
679
|
+
if (!pendingPing) {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
pendingIntegrationPings.delete(probeRequestId);
|
|
684
|
+
if (pendingPing.timeoutId) {
|
|
685
|
+
clearTimeout(pendingPing.timeoutId);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (!pendingPing.originWs || pendingPing.originWs.readyState !== WebSocket.OPEN) {
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
sendJson(pendingPing.originWs, {
|
|
693
|
+
type: 'integration.pong',
|
|
694
|
+
requestId: pendingPing.originRequestId,
|
|
695
|
+
payload: {
|
|
696
|
+
server: {
|
|
697
|
+
status: 'ok',
|
|
698
|
+
started: true
|
|
699
|
+
},
|
|
700
|
+
frontend: buildResponsiveFrontendPresence(pendingPing.responses)
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
return true;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function removePendingIntegrationPingsForWs(ws) {
|
|
708
|
+
for (const [probeRequestId, entry] of pendingIntegrationPings.entries()) {
|
|
709
|
+
if (entry?.originWs === ws) {
|
|
710
|
+
if (entry.timeoutId) {
|
|
711
|
+
clearTimeout(entry.timeoutId);
|
|
712
|
+
}
|
|
713
|
+
pendingIntegrationPings.delete(probeRequestId);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function handleIntegrationPing(ws, data) {
|
|
719
|
+
const requestId = getRequestId(data);
|
|
720
|
+
const validation = validateIntegrationPingPayload(data?.payload);
|
|
721
|
+
if (!validation.ok) {
|
|
722
|
+
sendIntegrationError(ws, {
|
|
723
|
+
requestId,
|
|
724
|
+
code: validation.code,
|
|
725
|
+
message: validation.message
|
|
726
|
+
});
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const { channel, targetClientId } = validation.payload;
|
|
731
|
+
const targets = getTargetFrontendConnections(channel, targetClientId);
|
|
732
|
+
if (targets.length === 0) {
|
|
733
|
+
sendJson(ws, {
|
|
734
|
+
type: 'integration.pong',
|
|
735
|
+
requestId,
|
|
736
|
+
payload: {
|
|
737
|
+
server: {
|
|
738
|
+
status: 'ok',
|
|
739
|
+
started: true
|
|
740
|
+
},
|
|
741
|
+
frontend: buildFrontendPresence(channel, targetClientId)
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const probeRequestId = createRuntimeId('integration_probe');
|
|
748
|
+
const pendingPing = {
|
|
749
|
+
originWs: ws,
|
|
750
|
+
originRequestId: requestId,
|
|
751
|
+
channel,
|
|
752
|
+
targetClientId,
|
|
753
|
+
responses: new Map(),
|
|
754
|
+
timeoutId: null
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
pendingPing.timeoutId = setTimeout(() => {
|
|
758
|
+
finalizePendingIntegrationPing(probeRequestId);
|
|
759
|
+
}, INTEGRATION_PING_TIMEOUT_MS);
|
|
760
|
+
pendingIntegrationPings.set(probeRequestId, pendingPing);
|
|
761
|
+
|
|
762
|
+
for (const target of targets) {
|
|
763
|
+
sendJson(target.ws, {
|
|
764
|
+
type: 'integration.ping',
|
|
765
|
+
requestId: probeRequestId,
|
|
766
|
+
payload: {
|
|
767
|
+
channel,
|
|
768
|
+
targetClientId: targetClientId || null
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function handleIntegrationProbeResponse(ws, data) {
|
|
775
|
+
const probeRequestId = getRequestId(data);
|
|
776
|
+
if (!probeRequestId) {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const pendingPing = pendingIntegrationPings.get(probeRequestId);
|
|
781
|
+
if (!pendingPing) {
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const meta = integrationConnections.get(ws);
|
|
786
|
+
if (!meta || meta.role !== INTEGRATION_ROLE_FRONTEND_PAGE) {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (data?.type === 'integration.ack') {
|
|
791
|
+
pendingPing.responses.set(meta.clientId, {
|
|
792
|
+
clientId: meta.clientId,
|
|
793
|
+
lastSeenAt: new Date().toISOString(),
|
|
794
|
+
pageUrl: normalizeNonEmptyString(data?.payload?.pageUrl) || meta.pageUrl || null,
|
|
795
|
+
sessionId: normalizeNonEmptyString(data?.payload?.sessionId) || meta.sessionId || null
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
if (pendingPing.targetClientId) {
|
|
799
|
+
finalizePendingIntegrationPing(probeRequestId);
|
|
800
|
+
}
|
|
801
|
+
return true;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (data?.type === 'integration.error') {
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function getTargetFrontendConnections(channel, targetClientId) {
|
|
812
|
+
return listIntegrationConnections({
|
|
813
|
+
role: INTEGRATION_ROLE_FRONTEND_PAGE,
|
|
814
|
+
channel,
|
|
815
|
+
clientId: targetClientId
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function validateEditorClientsListPayload(payload) {
|
|
820
|
+
if (payload === undefined || payload === null) {
|
|
821
|
+
return {
|
|
822
|
+
ok: true,
|
|
823
|
+
payload: {
|
|
824
|
+
channel: null,
|
|
825
|
+
clientId: null
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
if (!isPlainObject(payload)) {
|
|
831
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return {
|
|
835
|
+
ok: true,
|
|
836
|
+
payload: {
|
|
837
|
+
channel: normalizeNonEmptyString(payload.channel),
|
|
838
|
+
clientId: normalizeNonEmptyString(payload.clientId)
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function validateEditorTargetPayload(payload, extras = {}) {
|
|
844
|
+
if (!isPlainObject(payload)) {
|
|
845
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const channel = normalizeNonEmptyString(payload.channel);
|
|
849
|
+
if (!channel) {
|
|
850
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
const targetClientId = normalizeNonEmptyString(payload.targetClientId);
|
|
854
|
+
if (!targetClientId) {
|
|
855
|
+
return { ok: false, code: 'INVALID_TARGET', message: 'targetClientId is required' };
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
return {
|
|
859
|
+
ok: true,
|
|
860
|
+
payload: {
|
|
861
|
+
channel,
|
|
862
|
+
targetClientId,
|
|
863
|
+
...extras
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function validateEditorSnapshotPayload(payload) {
|
|
869
|
+
return validateEditorTargetPayload(payload);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function validateEditorNodesListPayload(payload) {
|
|
873
|
+
if (!isPlainObject(payload)) {
|
|
874
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const base = validateEditorTargetPayload(payload, {});
|
|
878
|
+
if (!base.ok) {
|
|
879
|
+
return base;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
let status = [];
|
|
883
|
+
if (Array.isArray(payload.status)) {
|
|
884
|
+
status = payload.status
|
|
885
|
+
.map((value) => normalizeNonEmptyString(value))
|
|
886
|
+
.filter(Boolean);
|
|
887
|
+
} else if (typeof payload.status === 'string') {
|
|
888
|
+
status = payload.status
|
|
889
|
+
.split(',')
|
|
890
|
+
.map((value) => normalizeNonEmptyString(value))
|
|
891
|
+
.filter(Boolean);
|
|
892
|
+
} else if (payload.status !== undefined && payload.status !== null) {
|
|
893
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'status must be a string or string array' };
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
const elementKey = normalizeNonEmptyString(payload.elementKey);
|
|
897
|
+
const limit = payload.limit === undefined ? null : normalizePositiveInteger(payload.limit);
|
|
898
|
+
if (payload.limit !== undefined && !limit) {
|
|
899
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'limit must be a positive integer' };
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
return {
|
|
903
|
+
ok: true,
|
|
904
|
+
payload: {
|
|
905
|
+
...base.payload,
|
|
906
|
+
status,
|
|
907
|
+
elementKey,
|
|
908
|
+
limit
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function validateEditorNodeScreenshotPayload(payload) {
|
|
914
|
+
if (!isPlainObject(payload)) {
|
|
915
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const elementKey = normalizeNonEmptyString(payload.elementKey);
|
|
919
|
+
if (!elementKey) {
|
|
920
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'elementKey is required' };
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
const base = validateEditorTargetPayload(payload, {
|
|
924
|
+
elementKey,
|
|
925
|
+
downloadPath: normalizeOptionalString(payload.downloadPath) || null
|
|
926
|
+
});
|
|
927
|
+
return base;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function validateEditorContextImagesPayload(payload) {
|
|
931
|
+
if (payload !== undefined && payload !== null && !isPlainObject(payload)) {
|
|
932
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
return validateEditorTargetPayload(payload || {}, {
|
|
936
|
+
downloadPath: normalizeOptionalString(payload?.downloadPath) || null
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function validateEditorEditingSetPayload(payload) {
|
|
941
|
+
if (!isPlainObject(payload)) {
|
|
942
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
const elementKey = normalizeNonEmptyString(payload.elementKey);
|
|
946
|
+
if (!elementKey) {
|
|
947
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'elementKey is required' };
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const state = normalizeNonEmptyString(payload.state);
|
|
951
|
+
if (state !== 'editing' && state !== 'idle') {
|
|
952
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'state must be editing or idle' };
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
return validateEditorTargetPayload(payload, {
|
|
956
|
+
elementKey,
|
|
957
|
+
state,
|
|
958
|
+
taskRef: isPlainObject(payload.taskRef) ? payload.taskRef : null
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function handleEditorClientsList(ws, data) {
|
|
963
|
+
const requestId = getRequestId(data);
|
|
964
|
+
const validation = validateEditorClientsListPayload(data?.payload);
|
|
965
|
+
if (!validation.ok) {
|
|
966
|
+
sendIntegrationError(ws, {
|
|
967
|
+
requestId,
|
|
968
|
+
code: validation.code,
|
|
969
|
+
message: validation.message
|
|
970
|
+
});
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
const items = listIntegrationConnections({
|
|
975
|
+
role: INTEGRATION_ROLE_FRONTEND_PAGE,
|
|
976
|
+
channel: validation.payload.channel,
|
|
977
|
+
clientId: validation.payload.clientId
|
|
978
|
+
}).map(buildEditorClientSummary);
|
|
979
|
+
|
|
980
|
+
sendJson(ws, {
|
|
981
|
+
type: 'integration.editor.clients.result',
|
|
982
|
+
requestId,
|
|
983
|
+
payload: {
|
|
984
|
+
items,
|
|
985
|
+
total: items.length,
|
|
986
|
+
filters: validation.payload
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function validateIntegrationContextUpdatePayload(payload) {
|
|
992
|
+
if (!payload || typeof payload !== 'object') {
|
|
993
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
const channel = normalizeNonEmptyString(payload.channel);
|
|
997
|
+
if (!channel) {
|
|
998
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
const targetClientId = normalizeNonEmptyString(payload.targetClientId);
|
|
1002
|
+
if (!targetClientId) {
|
|
1003
|
+
return { ok: false, code: 'INVALID_TARGET', message: 'targetClientId is required' };
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
const mode = normalizeNonEmptyString(payload.mode) || 'replace';
|
|
1007
|
+
if (mode !== 'replace' && mode !== 'append') {
|
|
1008
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'mode must be replace or append' };
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
if (!payload.context || typeof payload.context !== 'object' || Array.isArray(payload.context)) {
|
|
1012
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'context must be an object' };
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
return {
|
|
1016
|
+
ok: true,
|
|
1017
|
+
payload: {
|
|
1018
|
+
channel,
|
|
1019
|
+
targetClientId,
|
|
1020
|
+
mode,
|
|
1021
|
+
context: payload.context
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
function validateIntegrationPromptUpdatePayload(payload) {
|
|
1027
|
+
if (!payload || typeof payload !== 'object') {
|
|
1028
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
const channel = normalizeNonEmptyString(payload.channel);
|
|
1032
|
+
if (!channel) {
|
|
1033
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
const targetClientId = normalizeNonEmptyString(payload.targetClientId);
|
|
1037
|
+
if (!targetClientId) {
|
|
1038
|
+
return { ok: false, code: 'INVALID_TARGET', message: 'targetClientId is required' };
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (typeof payload.prompt !== 'string') {
|
|
1042
|
+
return { ok: false, code: 'INVALID_PAYLOAD', message: 'prompt must be a string' };
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
return {
|
|
1046
|
+
ok: true,
|
|
1047
|
+
payload: {
|
|
1048
|
+
channel,
|
|
1049
|
+
targetClientId,
|
|
1050
|
+
prompt: payload.prompt,
|
|
1051
|
+
autoSend: payload.autoSend === true
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
function forwardIntegrationMessage(ws, data, { validator, type, awaitResult = false }) {
|
|
1057
|
+
const requestId = getRequestId(data);
|
|
1058
|
+
const validation = validator(data?.payload);
|
|
1059
|
+
if (!validation.ok) {
|
|
1060
|
+
sendIntegrationError(ws, {
|
|
1061
|
+
requestId,
|
|
1062
|
+
code: validation.code,
|
|
1063
|
+
message: validation.message
|
|
1064
|
+
});
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
const { channel, targetClientId } = validation.payload;
|
|
1069
|
+
const targets = getTargetFrontendConnections(channel, targetClientId);
|
|
1070
|
+
if (targets.length === 0) {
|
|
1071
|
+
sendIntegrationError(ws, {
|
|
1072
|
+
requestId,
|
|
1073
|
+
code: 'FRONTEND_NOT_ONLINE',
|
|
1074
|
+
message: 'Target frontend page is not connected'
|
|
1075
|
+
});
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
const requiredCapability = INTEGRATION_EDITOR_CAPABILITIES[type] || null;
|
|
1080
|
+
if (requiredCapability) {
|
|
1081
|
+
const unsupportedTarget = targets.find((target) => !target.capabilities?.includes(requiredCapability));
|
|
1082
|
+
if (unsupportedTarget) {
|
|
1083
|
+
sendIntegrationError(ws, {
|
|
1084
|
+
requestId,
|
|
1085
|
+
code: 'UNSUPPORTED_FRONTEND_CAPABILITY',
|
|
1086
|
+
message: `Target frontend page does not advertise capability ${requiredCapability}`
|
|
1087
|
+
});
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
storePendingIntegrationRequest(requestId, ws, {
|
|
1093
|
+
awaitResult,
|
|
1094
|
+
requestType: type,
|
|
1095
|
+
timeoutMs: data?.payload?.timeoutMs,
|
|
1096
|
+
requestPayload: validation.payload
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
const outgoingPayload = {
|
|
1100
|
+
type,
|
|
1101
|
+
requestId,
|
|
1102
|
+
payload: validation.payload
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1105
|
+
for (const target of targets) {
|
|
1106
|
+
sendJson(target.ws, outgoingPayload);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
async function forwardIntegrationResponseToOrigin(data) {
|
|
1111
|
+
const requestId = getRequestId(data);
|
|
1112
|
+
if (!requestId) {
|
|
1113
|
+
return false;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
const pendingRequest = pendingIntegrationRequests.get(requestId);
|
|
1117
|
+
if (!pendingRequest?.originWs || pendingRequest.originWs.readyState !== WebSocket.OPEN) {
|
|
1118
|
+
clearPendingIntegrationRequest(requestId);
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
let outgoingData = data;
|
|
1123
|
+
try {
|
|
1124
|
+
outgoingData = await maybeMaterializeEditorResult(data, pendingRequest);
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
sendIntegrationError(pendingRequest.originWs, {
|
|
1127
|
+
requestId,
|
|
1128
|
+
code: error.code || 'INTERNAL_ERROR',
|
|
1129
|
+
message: error.message || 'Failed to materialize editor result'
|
|
1130
|
+
});
|
|
1131
|
+
clearPendingIntegrationRequest(requestId);
|
|
1132
|
+
return true;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
sendJson(pendingRequest.originWs, outgoingData);
|
|
1136
|
+
const isTerminal =
|
|
1137
|
+
outgoingData?.type === 'integration.error'
|
|
1138
|
+
|| isEditorResultMessageType(outgoingData?.type)
|
|
1139
|
+
|| (outgoingData?.type === 'integration.ack' && pendingRequest.awaitResult !== true);
|
|
1140
|
+
if (isTerminal) {
|
|
1141
|
+
clearPendingIntegrationRequest(requestId);
|
|
1142
|
+
}
|
|
1143
|
+
return true;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
class ExternalAgentWebSocketWriter {
|
|
1147
|
+
constructor(ws, { requestId, provider }) {
|
|
1148
|
+
this.ws = ws;
|
|
1149
|
+
this.requestId = requestId;
|
|
1150
|
+
this.provider = provider;
|
|
1151
|
+
this.sessionId = null;
|
|
1152
|
+
this.isWebSocketWriter = true;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
send(payload) {
|
|
1156
|
+
if (!payload || typeof payload !== 'object') {
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (typeof payload.sessionId === 'string' && payload.sessionId.trim()) {
|
|
1161
|
+
this.sessionId = payload.sessionId.trim();
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
if (payload.type === 'session-created') {
|
|
1165
|
+
const sessionId = typeof payload.sessionId === 'string' ? payload.sessionId.trim() : this.sessionId;
|
|
1166
|
+
if (!sessionId) {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
this.sessionId = sessionId;
|
|
1171
|
+
sendJson(this.ws, {
|
|
1172
|
+
type: 'agent.session.created',
|
|
1173
|
+
requestId: this.requestId,
|
|
1174
|
+
provider: payload.provider || this.provider,
|
|
1175
|
+
sessionId,
|
|
1176
|
+
...buildSessionNavigation(sessionId)
|
|
1177
|
+
});
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
if (payload.type === 'conversation-event' && payload.event) {
|
|
1182
|
+
const sessionId = payload.sessionId || payload.event?.sessionId || this.sessionId || null;
|
|
1183
|
+
if (sessionId) {
|
|
1184
|
+
this.sessionId = sessionId;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
sendJson(this.ws, {
|
|
1188
|
+
type: 'agent.event',
|
|
1189
|
+
requestId: this.requestId,
|
|
1190
|
+
provider: payload.provider || payload.event?.provider || this.provider,
|
|
1191
|
+
sessionId,
|
|
1192
|
+
event: payload.event
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
setSessionId(sessionId) {
|
|
1198
|
+
this.sessionId = sessionId;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
getSessionId() {
|
|
1202
|
+
return this.sessionId;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
subscribeToSessionRuntimeStateChanges(({ subscriptionKey, snapshot }) => {
|
|
1207
|
+
const subscribers = sessionStateSubscribersByKey.get(subscriptionKey);
|
|
1208
|
+
if (!subscribers || subscribers.size === 0) {
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
subscribers.forEach((ws) => {
|
|
1213
|
+
sendJson(ws, {
|
|
1214
|
+
type: 'agent.state.changed',
|
|
1215
|
+
payload: {
|
|
1216
|
+
subscriptionKey,
|
|
1217
|
+
sessionId: snapshot.sessionId,
|
|
1218
|
+
provider: snapshot.provider,
|
|
1219
|
+
phase: snapshot.phase,
|
|
1220
|
+
isLoading: snapshot.isLoading,
|
|
1221
|
+
canAbortSession: snapshot.canAbortSession,
|
|
1222
|
+
hasPendingApproval: snapshot.hasPendingApproval,
|
|
1223
|
+
updatedAt: snapshot.updatedAt
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
});
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
async function handleAgentRun(ws, request, data) {
|
|
1230
|
+
const requestId = getRequestId(data);
|
|
1231
|
+
|
|
1232
|
+
let normalized = null;
|
|
1233
|
+
try {
|
|
1234
|
+
normalized = normalizeExternalAgentRunRequest(data.payload || {});
|
|
1235
|
+
} catch (error) {
|
|
1236
|
+
sendRequestError(ws, {
|
|
1237
|
+
requestId,
|
|
1238
|
+
provider: data?.payload?.provider || null,
|
|
1239
|
+
sessionId: data?.payload?.sessionId || null,
|
|
1240
|
+
error: error.message,
|
|
1241
|
+
statusCode: error.statusCode || 400
|
|
1242
|
+
});
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
sendJson(ws, {
|
|
1247
|
+
type: 'agent.accepted',
|
|
1248
|
+
requestId,
|
|
1249
|
+
provider: normalized.provider,
|
|
1250
|
+
sessionId: normalized.normalizedSessionId
|
|
1251
|
+
});
|
|
1252
|
+
|
|
1253
|
+
const baseWriter = new ExternalAgentWebSocketWriter(ws, {
|
|
1254
|
+
requestId,
|
|
1255
|
+
provider: normalized.provider
|
|
1256
|
+
});
|
|
1257
|
+
const transportWriter = new SessionEventMirrorWriter(baseWriter, normalized.provider);
|
|
1258
|
+
|
|
1259
|
+
try {
|
|
1260
|
+
const result = await runExternalAgentRequest({
|
|
1261
|
+
user: request.user,
|
|
1262
|
+
request: normalized,
|
|
1263
|
+
transportWriter,
|
|
1264
|
+
endTransportOnFinish: false
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
sendJson(ws, {
|
|
1268
|
+
type: 'agent.completed',
|
|
1269
|
+
requestId,
|
|
1270
|
+
provider: normalized.provider,
|
|
1271
|
+
sessionId: result.sessionId || baseWriter.getSessionId() || normalized.normalizedSessionId,
|
|
1272
|
+
result
|
|
1273
|
+
});
|
|
1274
|
+
} catch (error) {
|
|
1275
|
+
const resolvedSessionId = baseWriter.getSessionId() || normalized.normalizedSessionId || null;
|
|
1276
|
+
if (error instanceof AgentSessionAbortedError) {
|
|
1277
|
+
sendJson(ws, {
|
|
1278
|
+
type: 'agent.aborted',
|
|
1279
|
+
requestId,
|
|
1280
|
+
success: true,
|
|
1281
|
+
aborted: true,
|
|
1282
|
+
sessionId: resolvedSessionId,
|
|
1283
|
+
provider: normalized.provider,
|
|
1284
|
+
message: 'Session aborted'
|
|
1285
|
+
});
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
sendRequestError(ws, {
|
|
1290
|
+
requestId,
|
|
1291
|
+
provider: normalized.provider,
|
|
1292
|
+
sessionId: resolvedSessionId,
|
|
1293
|
+
error: error.message,
|
|
1294
|
+
statusCode: error.statusCode || 500
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
async function handleAgentAbort(ws, data) {
|
|
1300
|
+
const requestId = getRequestId(data);
|
|
1301
|
+
|
|
1302
|
+
let normalized = null;
|
|
1303
|
+
try {
|
|
1304
|
+
normalized = normalizeExternalAgentAbortRequest(data.payload || {});
|
|
1305
|
+
} catch (error) {
|
|
1306
|
+
sendRequestError(ws, {
|
|
1307
|
+
requestId,
|
|
1308
|
+
provider: data?.payload?.provider || null,
|
|
1309
|
+
sessionId: data?.payload?.sessionId || null,
|
|
1310
|
+
error: error.message,
|
|
1311
|
+
statusCode: error.statusCode || 400
|
|
1312
|
+
});
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
try {
|
|
1317
|
+
const writer = new SessionEventMirrorWriter(
|
|
1318
|
+
new ExternalAgentWebSocketWriter(ws, { requestId, provider: normalized.provider }),
|
|
1319
|
+
normalized.provider
|
|
1320
|
+
);
|
|
1321
|
+
const { success } = await abortAgentSessionWithWriter({
|
|
1322
|
+
provider: normalized.provider,
|
|
1323
|
+
sessionId: normalized.sessionId,
|
|
1324
|
+
writer
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
if (!success) {
|
|
1328
|
+
sendRequestError(ws, {
|
|
1329
|
+
requestId,
|
|
1330
|
+
provider: normalized.provider,
|
|
1331
|
+
sessionId: normalized.sessionId,
|
|
1332
|
+
error: 'Active session not found',
|
|
1333
|
+
statusCode: 200
|
|
1334
|
+
});
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
sendJson(ws, {
|
|
1339
|
+
type: 'agent.aborted',
|
|
1340
|
+
requestId,
|
|
1341
|
+
success: true,
|
|
1342
|
+
aborted: true,
|
|
1343
|
+
sessionId: normalized.sessionId,
|
|
1344
|
+
provider: normalized.provider,
|
|
1345
|
+
message: 'Session aborted'
|
|
1346
|
+
});
|
|
1347
|
+
} catch (error) {
|
|
1348
|
+
sendRequestError(ws, {
|
|
1349
|
+
requestId,
|
|
1350
|
+
provider: normalized.provider,
|
|
1351
|
+
sessionId: normalized.sessionId,
|
|
1352
|
+
error: error.message,
|
|
1353
|
+
statusCode: 500
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
export function handleExternalAgentWebSocketConnection(ws, request) {
|
|
1359
|
+
ws.on('message', (message) => {
|
|
1360
|
+
let data = null;
|
|
1361
|
+
try {
|
|
1362
|
+
data = JSON.parse(message);
|
|
1363
|
+
} catch {
|
|
1364
|
+
sendRequestError(ws, {
|
|
1365
|
+
requestId: null,
|
|
1366
|
+
provider: null,
|
|
1367
|
+
sessionId: null,
|
|
1368
|
+
error: 'Invalid JSON message',
|
|
1369
|
+
statusCode: 400
|
|
1370
|
+
});
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
touchIntegrationConnection(ws);
|
|
1375
|
+
|
|
1376
|
+
if (data?.type === 'ping') {
|
|
1377
|
+
sendJson(ws, {
|
|
1378
|
+
type: 'pong',
|
|
1379
|
+
requestId: getRequestId(data),
|
|
1380
|
+
timestamp: new Date().toISOString()
|
|
1381
|
+
});
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
if (data?.type === 'integration.connect') {
|
|
1386
|
+
handleIntegrationConnect(ws, data);
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
if (data?.type === 'integration.ping') {
|
|
1391
|
+
handleIntegrationPing(ws, data);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if (data?.type === 'integration.context.update') {
|
|
1396
|
+
forwardIntegrationMessage(ws, data, {
|
|
1397
|
+
validator: validateIntegrationContextUpdatePayload,
|
|
1398
|
+
type: 'integration.context.update'
|
|
1399
|
+
});
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
if (data?.type === 'integration.prompt.update') {
|
|
1404
|
+
forwardIntegrationMessage(ws, data, {
|
|
1405
|
+
validator: validateIntegrationPromptUpdatePayload,
|
|
1406
|
+
type: 'integration.prompt.update'
|
|
1407
|
+
});
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
if (data?.type === 'integration.editor.clients.list') {
|
|
1412
|
+
handleEditorClientsList(ws, data);
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
if (data?.type === 'integration.editor.snapshot.get') {
|
|
1417
|
+
forwardIntegrationMessage(ws, data, {
|
|
1418
|
+
validator: validateEditorSnapshotPayload,
|
|
1419
|
+
type: 'integration.editor.snapshot.get',
|
|
1420
|
+
awaitResult: true
|
|
1421
|
+
});
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
if (data?.type === 'integration.editor.nodes.list') {
|
|
1426
|
+
forwardIntegrationMessage(ws, data, {
|
|
1427
|
+
validator: validateEditorNodesListPayload,
|
|
1428
|
+
type: 'integration.editor.nodes.list',
|
|
1429
|
+
awaitResult: true
|
|
1430
|
+
});
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
if (data?.type === 'integration.editor.node.screenshot.get') {
|
|
1435
|
+
forwardIntegrationMessage(ws, data, {
|
|
1436
|
+
validator: validateEditorNodeScreenshotPayload,
|
|
1437
|
+
type: 'integration.editor.node.screenshot.get',
|
|
1438
|
+
awaitResult: true
|
|
1439
|
+
});
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
if (data?.type === 'integration.editor.context-images.get') {
|
|
1444
|
+
forwardIntegrationMessage(ws, data, {
|
|
1445
|
+
validator: validateEditorContextImagesPayload,
|
|
1446
|
+
type: 'integration.editor.context-images.get',
|
|
1447
|
+
awaitResult: true
|
|
1448
|
+
});
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
if (data?.type === 'integration.editor.editing.set') {
|
|
1453
|
+
forwardIntegrationMessage(ws, data, {
|
|
1454
|
+
validator: validateEditorEditingSetPayload,
|
|
1455
|
+
type: 'integration.editor.editing.set',
|
|
1456
|
+
awaitResult: true
|
|
1457
|
+
});
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
if (data?.type === 'integration.ack' || data?.type === 'integration.error' || isEditorResultMessageType(data?.type)) {
|
|
1462
|
+
const handledProbeResponse = handleIntegrationProbeResponse(ws, data);
|
|
1463
|
+
if (handledProbeResponse) {
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
void forwardIntegrationResponseToOrigin(data).then((forwarded) => {
|
|
1468
|
+
if (!forwarded && data?.type === 'integration.error') {
|
|
1469
|
+
sendIntegrationError(ws, {
|
|
1470
|
+
requestId: getRequestId(data),
|
|
1471
|
+
code: 'UNSUPPORTED_MESSAGE_TYPE',
|
|
1472
|
+
message: 'No pending integration request found for this response'
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
}).catch((error) => {
|
|
1476
|
+
sendIntegrationError(ws, {
|
|
1477
|
+
requestId: getRequestId(data),
|
|
1478
|
+
code: error.code || 'INTERNAL_ERROR',
|
|
1479
|
+
message: error.message || 'Failed to forward integration response'
|
|
1480
|
+
});
|
|
1481
|
+
});
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
if (data?.type === 'agent.run') {
|
|
1486
|
+
void handleAgentRun(ws, request, data);
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
if (data?.type === 'agent.state.query') {
|
|
1491
|
+
void handleAgentStateQuery(ws, data);
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
if (data?.type === 'agent.state.subscribe') {
|
|
1496
|
+
void handleAgentStateSubscribe(ws, data);
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
if (data?.type === 'agent.abort') {
|
|
1501
|
+
void handleAgentAbort(ws, data);
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
if (typeof data?.type === 'string' && data.type.startsWith('integration.')) {
|
|
1506
|
+
sendIntegrationError(ws, {
|
|
1507
|
+
requestId: getRequestId(data),
|
|
1508
|
+
code: 'UNSUPPORTED_MESSAGE_TYPE',
|
|
1509
|
+
message: 'Unsupported integration message type'
|
|
1510
|
+
});
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
sendRequestError(ws, {
|
|
1515
|
+
requestId: getRequestId(data),
|
|
1516
|
+
provider: data?.payload?.provider || null,
|
|
1517
|
+
sessionId: data?.payload?.sessionId || null,
|
|
1518
|
+
error: 'Unsupported message type',
|
|
1519
|
+
statusCode: 400
|
|
1520
|
+
});
|
|
1521
|
+
});
|
|
1522
|
+
|
|
1523
|
+
ws.on('close', () => {
|
|
1524
|
+
unregisterIntegrationConnection(ws);
|
|
1525
|
+
});
|
|
1526
|
+
}
|