@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
package/server/routes/agent.js
CHANGED
|
@@ -7,13 +7,31 @@ import crypto from 'crypto';
|
|
|
7
7
|
import { userDb, apiKeysDb, githubTokensDb } from '../database/db.js';
|
|
8
8
|
import { addProjectManually } from '../projects.js';
|
|
9
9
|
import { queryClaudeSDK } from '../claude-sdk.js';
|
|
10
|
-
import { spawnCursor } from '../cursor-cli.js';
|
|
11
10
|
import { queryCodex } from '../openai-codex.js';
|
|
12
11
|
import { queryGemini } from '../gemini-cli.js';
|
|
13
12
|
import { queryOpencode } from '../opencode-cli.js';
|
|
13
|
+
import { SessionEventMirrorWriter } from '../session-core/runtimeWriter.js';
|
|
14
|
+
import { ABORTABLE_AGENT_PROVIDERS, abortAgentSessionWithWriter, isAbortableAgentProvider } from '../session-core/abortSession.js';
|
|
15
|
+
import {
|
|
16
|
+
buildAgentCallbackPayload,
|
|
17
|
+
createAgentCallbackEventId,
|
|
18
|
+
createEmptyTokenSummary,
|
|
19
|
+
normalizeAgentCallbackConfig,
|
|
20
|
+
scheduleAgentCallbackDelivery,
|
|
21
|
+
shouldDeliverAgentCallback
|
|
22
|
+
} from '../utils/agentCallback.js';
|
|
14
23
|
import { Octokit } from '@octokit/rest';
|
|
15
24
|
import { CODEX_MODELS, GEMINI_MODELS, OPENCODE_MODELS } from '../../shared/modelConstants.js';
|
|
16
25
|
import { IS_PLATFORM } from '../constants/config.js';
|
|
26
|
+
import { validateExternalApiKey as validateExternalAgentApiKey } from '../external-agent/auth.js';
|
|
27
|
+
import {
|
|
28
|
+
NoopWriter as ExternalNoopWriter,
|
|
29
|
+
ResponseCollector as ExternalResponseCollector,
|
|
30
|
+
SSEStreamWriter as ExternalSSEStreamWriter,
|
|
31
|
+
normalizeExternalAgentAbortRequest,
|
|
32
|
+
normalizeExternalAgentRunRequest,
|
|
33
|
+
runExternalAgentRequest
|
|
34
|
+
} from '../external-agent/service.js';
|
|
17
35
|
|
|
18
36
|
const router = express.Router();
|
|
19
37
|
|
|
@@ -632,6 +650,182 @@ class ResponseCollector {
|
|
|
632
650
|
}
|
|
633
651
|
}
|
|
634
652
|
|
|
653
|
+
class AgentSessionAbortedError extends Error {
|
|
654
|
+
constructor(message = 'Session aborted') {
|
|
655
|
+
super(message);
|
|
656
|
+
this.name = 'AgentSessionAbortedError';
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function extractTerminalErrorMessage(payload) {
|
|
661
|
+
if (!payload || typeof payload !== 'object') {
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (typeof payload.error === 'string' && payload.error.trim()) {
|
|
666
|
+
return payload.error.trim();
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (payload.error && typeof payload.error.message === 'string' && payload.error.message.trim()) {
|
|
670
|
+
return payload.error.message.trim();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const nestedData = payload.data;
|
|
674
|
+
if (nestedData && typeof nestedData === 'object') {
|
|
675
|
+
if (typeof nestedData.error === 'string' && nestedData.error.trim()) {
|
|
676
|
+
return nestedData.error.trim();
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (nestedData.error && typeof nestedData.error.message === 'string' && nestedData.error.message.trim()) {
|
|
680
|
+
return nestedData.error.message.trim();
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (typeof nestedData.message === 'string' && nestedData.message.trim()) {
|
|
684
|
+
return nestedData.message.trim();
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
class CallbackCaptureWriter {
|
|
692
|
+
constructor() {
|
|
693
|
+
this.sessionId = null;
|
|
694
|
+
this.assistantMessages = [];
|
|
695
|
+
this.tokenSummary = createEmptyTokenSummary();
|
|
696
|
+
this.terminalState = null;
|
|
697
|
+
this.terminalErrorMessage = null;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
send(payload) {
|
|
701
|
+
if (!payload || typeof payload !== 'object') {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (typeof payload.sessionId === 'string' && payload.sessionId.trim()) {
|
|
706
|
+
this.sessionId = payload.sessionId.trim();
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (payload.type === 'session-created' && typeof payload.sessionId === 'string' && payload.sessionId.trim()) {
|
|
710
|
+
this.sessionId = payload.sessionId.trim();
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (payload.type === 'token-budget' && payload.data && typeof payload.data === 'object') {
|
|
714
|
+
this.tokenSummary = {
|
|
715
|
+
inputTokens: Number(payload.data.inputTokens) || 0,
|
|
716
|
+
outputTokens: Number(payload.data.outputTokens) || 0,
|
|
717
|
+
cacheReadTokens: Number(payload.data.cacheReadTokens) || 0,
|
|
718
|
+
cacheCreationTokens: Number(payload.data.cacheCreationTokens) || 0,
|
|
719
|
+
totalTokens: Number(payload.data.totalTokens) || 0
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (payload.type === 'claude-response' && payload.data?.type === 'assistant') {
|
|
724
|
+
this.assistantMessages.push(payload.data);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (
|
|
728
|
+
payload.type === 'codex-response' &&
|
|
729
|
+
payload.data?.type === 'item_done' &&
|
|
730
|
+
payload.data?.itemType === 'agent_message' &&
|
|
731
|
+
typeof payload.data?.content === 'string' &&
|
|
732
|
+
payload.data.content.trim()
|
|
733
|
+
) {
|
|
734
|
+
this.assistantMessages.push({
|
|
735
|
+
type: 'assistant',
|
|
736
|
+
message: {
|
|
737
|
+
role: 'assistant',
|
|
738
|
+
content: payload.data.content
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (payload.type === 'session-aborted') {
|
|
744
|
+
this.terminalState = 'aborted';
|
|
745
|
+
this.terminalErrorMessage = 'Session aborted';
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (payload.type === 'claude-complete' || payload.type === 'codex-complete') {
|
|
750
|
+
this.terminalState = 'completed';
|
|
751
|
+
this.terminalErrorMessage = null;
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if (
|
|
756
|
+
payload.type === 'claude-error' ||
|
|
757
|
+
payload.type === 'codex-error' ||
|
|
758
|
+
payload.type === 'error' ||
|
|
759
|
+
(payload.type === 'codex-response' && payload.data?.type === 'turn_failed')
|
|
760
|
+
) {
|
|
761
|
+
this.terminalState = 'errored';
|
|
762
|
+
this.terminalErrorMessage = extractTerminalErrorMessage(payload) || this.terminalErrorMessage || 'Agent session failed';
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
end() {}
|
|
767
|
+
|
|
768
|
+
setSessionId(sessionId) {
|
|
769
|
+
if (typeof sessionId === 'string' && sessionId.trim()) {
|
|
770
|
+
this.sessionId = sessionId.trim();
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
getSessionId() {
|
|
775
|
+
return this.sessionId;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
getAssistantMessages() {
|
|
779
|
+
return this.assistantMessages;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
getTotalTokens() {
|
|
783
|
+
return this.tokenSummary;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
getTerminalState() {
|
|
787
|
+
return this.terminalState;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
getTerminalErrorMessage() {
|
|
791
|
+
return this.terminalErrorMessage;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
class TeeWriter {
|
|
796
|
+
constructor(primaryWriter, secondaryWriter) {
|
|
797
|
+
this.primaryWriter = primaryWriter;
|
|
798
|
+
this.secondaryWriter = secondaryWriter;
|
|
799
|
+
this.isSSEStreamWriter = !!primaryWriter?.isSSEStreamWriter;
|
|
800
|
+
this.isWebSocketWriter = !!primaryWriter?.isWebSocketWriter;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
send(payload) {
|
|
804
|
+
this.primaryWriter?.send?.(payload);
|
|
805
|
+
this.secondaryWriter?.send?.(payload);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
end() {
|
|
809
|
+
this.primaryWriter?.end?.();
|
|
810
|
+
this.secondaryWriter?.end?.();
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
setSessionId(sessionId) {
|
|
814
|
+
this.primaryWriter?.setSessionId?.(sessionId);
|
|
815
|
+
this.secondaryWriter?.setSessionId?.(sessionId);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
getSessionId() {
|
|
819
|
+
return this.primaryWriter?.getSessionId?.() || this.secondaryWriter?.getSessionId?.() || null;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
class NoopWriter {
|
|
824
|
+
send() {}
|
|
825
|
+
|
|
826
|
+
end() {}
|
|
827
|
+
}
|
|
828
|
+
|
|
635
829
|
// ===============================
|
|
636
830
|
// External API Endpoint
|
|
637
831
|
// ===============================
|
|
@@ -639,25 +833,26 @@ class ResponseCollector {
|
|
|
639
833
|
/**
|
|
640
834
|
* POST /api/agent
|
|
641
835
|
*
|
|
642
|
-
* Trigger an AI agent (Claude,
|
|
836
|
+
* Trigger an AI agent (Claude, Codex, Gemini, or OpenCode) to work on a project.
|
|
643
837
|
* Supports automatic GitHub branch and pull request creation after successful completion.
|
|
644
838
|
*
|
|
645
839
|
* ================================================================================================
|
|
646
840
|
* REQUEST BODY PARAMETERS
|
|
647
841
|
* ================================================================================================
|
|
648
842
|
*
|
|
649
|
-
* @param {string} githubUrl - (
|
|
843
|
+
* @param {string} githubUrl - (Optional) GitHub repository URL to clone.
|
|
650
844
|
* Supported formats:
|
|
651
845
|
* - HTTPS: https://github.com/owner/repo
|
|
652
846
|
* - HTTPS with .git: https://github.com/owner/repo.git
|
|
653
847
|
* - SSH: git@github.com:owner/repo
|
|
654
848
|
* - SSH with .git: git@github.com:owner/repo.git
|
|
655
849
|
*
|
|
656
|
-
* @param {string} projectPath - (
|
|
850
|
+
* @param {string} projectPath - (Optional) Path to existing project OR destination for cloning.
|
|
657
851
|
* Behavior depends on usage:
|
|
658
852
|
* - If used alone: Must point to existing project directory
|
|
659
853
|
* - If used with githubUrl: Target location for cloning
|
|
660
854
|
* - If omitted with githubUrl: Auto-generates temporary path in ~/.claude/external-projects/
|
|
855
|
+
* - If omitted without githubUrl: Falls back to service default working directory, then process cwd
|
|
661
856
|
*
|
|
662
857
|
* @param {string} message - (Required) Task description for the AI agent. Used as:
|
|
663
858
|
* - Instructions for the agent
|
|
@@ -667,7 +862,7 @@ class ResponseCollector {
|
|
|
667
862
|
* @param {string} sessionId - (Optional) Existing session ID to resume.
|
|
668
863
|
* If provided, the request continues that session instead of creating a new one.
|
|
669
864
|
*
|
|
670
|
-
* @param {string} provider - (Optional) AI provider to use. Options: 'claude' | '
|
|
865
|
+
* @param {string} provider - (Optional) AI provider to use. Options: 'claude' | 'codex' | 'gemini' | 'opencode'
|
|
671
866
|
* Default: 'claude'
|
|
672
867
|
*
|
|
673
868
|
* @param {boolean} stream - (Optional) Enable Server-Sent Events (SSE) streaming for real-time updates.
|
|
@@ -678,8 +873,7 @@ class ResponseCollector {
|
|
|
678
873
|
* @param {string} model - (Optional) Model identifier for providers.
|
|
679
874
|
*
|
|
680
875
|
* Claude models: 'sonnet' (default), 'opus', 'haiku', 'opusplan', 'sonnet[1m]'
|
|
681
|
-
*
|
|
682
|
-
* 'gemini-3-pro', 'composer-1', 'auto', 'gpt-5.1', 'gpt-5.1-high',
|
|
876
|
+
* * 'gemini-3-pro', 'composer-1', 'auto', 'gpt-5.1', 'gpt-5.1-high',
|
|
683
877
|
* 'gpt-5.1-codex', 'gpt-5.1-codex-high', 'gpt-5.1-codex-max',
|
|
684
878
|
* 'gpt-5.1-codex-max-high', 'opus-4.1', 'grok', and thinking variants
|
|
685
879
|
* Codex models: 'gpt-5.2' (default), 'gpt-5.1-codex-max', 'o3', 'o4-mini'
|
|
@@ -733,6 +927,16 @@ class ResponseCollector {
|
|
|
733
927
|
* - If PR already exists: GitHub returns error with details
|
|
734
928
|
* - Requires either githubUrl OR projectPath with GitHub remote
|
|
735
929
|
*
|
|
930
|
+
* @param {object} callback - (Optional) Terminal webhook configuration for external systems.
|
|
931
|
+
* Behavior:
|
|
932
|
+
* - Sent once when the run ends as completed, errored, or aborted
|
|
933
|
+
* - Delivery is asynchronous and never blocks the main API response
|
|
934
|
+
* - Not supported with openOnly=true
|
|
935
|
+
* Fields:
|
|
936
|
+
* - url: Required HTTP/HTTPS target URL
|
|
937
|
+
* - events: Optional subset of ["completed", "errored", "aborted"]
|
|
938
|
+
* - secret: Optional shared secret for HMAC signing
|
|
939
|
+
*
|
|
736
940
|
* ================================================================================================
|
|
737
941
|
* PATH HANDLING BEHAVIOR
|
|
738
942
|
* ================================================================================================
|
|
@@ -784,11 +988,14 @@ class ResponseCollector {
|
|
|
784
988
|
* ================================================================================================
|
|
785
989
|
*
|
|
786
990
|
* Input Validations (400 Bad Request):
|
|
787
|
-
* - Either githubUrl OR projectPath must be provided (not neither)
|
|
788
991
|
* - message must be non-empty string
|
|
789
|
-
* - provider must be 'claude', '
|
|
790
|
-
* - createBranch/createPR requires
|
|
992
|
+
* - provider must be 'claude', 'codex', 'gemini', or 'opencode'
|
|
993
|
+
* - createBranch/createPR requires a resolvable working directory or githubUrl
|
|
791
994
|
* - branchName must pass Git naming rules (if provided)
|
|
995
|
+
* - callback must be an object when provided
|
|
996
|
+
* - callback.url must be a valid HTTP/HTTPS URL
|
|
997
|
+
* - callback.events can only include completed/errored/aborted
|
|
998
|
+
* - callback is not supported when openOnly=true
|
|
792
999
|
*
|
|
793
1000
|
* Runtime Validations (500 Internal Server Error or specific error in response):
|
|
794
1001
|
* - projectPath must exist (if used alone)
|
|
@@ -847,6 +1054,14 @@ class ResponseCollector {
|
|
|
847
1054
|
* Content-Type: application/json
|
|
848
1055
|
* { success: false, error: "Error description" }
|
|
849
1056
|
*
|
|
1057
|
+
* Callback Delivery:
|
|
1058
|
+
* Content-Type: application/json
|
|
1059
|
+
* Headers:
|
|
1060
|
+
* - X-Agent-Event
|
|
1061
|
+
* - X-Agent-Event-Id
|
|
1062
|
+
* - X-Agent-Delivery-Attempt
|
|
1063
|
+
* - X-Agent-Signature (only when callback.secret is provided)
|
|
1064
|
+
*
|
|
850
1065
|
* ================================================================================================
|
|
851
1066
|
* EXAMPLES
|
|
852
1067
|
* ================================================================================================
|
|
@@ -874,481 +1089,154 @@ class ResponseCollector {
|
|
|
874
1089
|
* "cleanup": false
|
|
875
1090
|
* }
|
|
876
1091
|
*/
|
|
877
|
-
router.post('/',
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
const createBranch = branchName ? true : (req.body.createBranch === true || req.body.createBranch === 'true');
|
|
886
|
-
const createPR = req.body.createPR === true || req.body.createPR === 'true';
|
|
887
|
-
const normalizedSessionId = typeof sessionId === 'string' && sessionId.trim() ? sessionId.trim() : null;
|
|
888
|
-
const normalizedMessage = typeof message === 'string' ? message.trim() : '';
|
|
889
|
-
const requestedOpenOnly = openOnly === true || openOnly === 'true';
|
|
890
|
-
const isOpenOnly = requestedOpenOnly || (!normalizedMessage && !!normalizedSessionId);
|
|
891
|
-
// Keep openOnly requests backward-compatible with existing docs/examples:
|
|
892
|
-
// cleanup is irrelevant when we only generate session navigation.
|
|
893
|
-
const cleanup = isOpenOnly ? false : requestedCleanup;
|
|
894
|
-
|
|
895
|
-
// Validate inputs
|
|
896
|
-
if (!['claude', 'cursor', 'codex', 'gemini', 'opencode'].includes(provider)) {
|
|
897
|
-
return res.status(400).json({ error: 'provider must be "claude", "cursor", "codex", "gemini", or "opencode"' });
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
if (sessionId !== undefined && (typeof sessionId !== 'string' || !sessionId.trim())) {
|
|
901
|
-
return res.status(400).json({ error: 'sessionId must be a non-empty string when provided' });
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
if (isOpenOnly && !normalizedSessionId) {
|
|
905
|
-
return res.status(400).json({ error: 'sessionId is required when message is empty or openOnly=true' });
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
if (!isOpenOnly && !normalizedMessage) {
|
|
909
|
-
return res.status(400).json({ error: 'message is required unless openOnly=true' });
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
if (!isOpenOnly && !githubUrl && !projectPath) {
|
|
913
|
-
return res.status(400).json({ error: 'Either githubUrl or projectPath is required' });
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
if (isOpenOnly && githubUrl) {
|
|
917
|
-
return res.status(400).json({ error: 'githubUrl is not supported when openOnly=true' });
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
if (isOpenOnly && (createBranch || createPR)) {
|
|
921
|
-
return res.status(400).json({ error: 'createBranch and createPR are not supported when openOnly=true' });
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
// Validate GitHub branch/PR creation requirements
|
|
925
|
-
// Allow branch/PR creation with projectPath as long as it has a GitHub remote
|
|
926
|
-
if ((createBranch || createPR) && !githubUrl && !projectPath) {
|
|
927
|
-
return res.status(400).json({ error: 'createBranch and createPR require either githubUrl or projectPath with a GitHub remote' });
|
|
1092
|
+
router.post('/', validateExternalAgentApiKey, async (req, res) => {
|
|
1093
|
+
let normalized = null;
|
|
1094
|
+
try {
|
|
1095
|
+
normalized = normalizeExternalAgentRunRequest(req.body);
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
return res.status(error.statusCode || 400).json({
|
|
1098
|
+
error: error.message
|
|
1099
|
+
});
|
|
928
1100
|
}
|
|
929
1101
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const response = {
|
|
933
|
-
success: true,
|
|
934
|
-
openOnly: true,
|
|
935
|
-
sessionId: normalizedSessionId,
|
|
936
|
-
sessionPath,
|
|
937
|
-
sessionUrl,
|
|
938
|
-
isResumed: true,
|
|
939
|
-
message: 'Session link generated. No model call triggered.'
|
|
940
|
-
};
|
|
1102
|
+
try {
|
|
1103
|
+
let transportWriter = null;
|
|
941
1104
|
|
|
942
|
-
if (stream) {
|
|
1105
|
+
if (normalized.stream) {
|
|
943
1106
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
944
1107
|
res.setHeader('Cache-Control', 'no-cache');
|
|
945
1108
|
res.setHeader('Connection', 'keep-alive');
|
|
946
1109
|
res.setHeader('X-Accel-Buffering', 'no');
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
type: 'open-only',
|
|
951
|
-
...response
|
|
952
|
-
});
|
|
953
|
-
writer.end();
|
|
954
|
-
return;
|
|
1110
|
+
transportWriter = new ExternalSSEStreamWriter(res);
|
|
1111
|
+
} else {
|
|
1112
|
+
transportWriter = new ExternalResponseCollector();
|
|
955
1113
|
}
|
|
956
1114
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
try {
|
|
964
|
-
// Determine the final project path
|
|
965
|
-
if (githubUrl) {
|
|
966
|
-
// Clone repository (to projectPath if provided, otherwise generate path)
|
|
967
|
-
const tokenToUse = githubToken || githubTokensDb.getActiveGithubToken(req.user.id);
|
|
968
|
-
|
|
969
|
-
let targetPath;
|
|
970
|
-
if (projectPath) {
|
|
971
|
-
targetPath = projectPath;
|
|
972
|
-
} else {
|
|
973
|
-
// Generate a unique path for cloning
|
|
974
|
-
const repoHash = crypto.createHash('md5').update(githubUrl + Date.now()).digest('hex');
|
|
975
|
-
targetPath = path.join(os.homedir(), '.claude', 'external-projects', repoHash);
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
finalProjectPath = await cloneGitHubRepo(githubUrl.trim(), tokenToUse, targetPath);
|
|
979
|
-
} else {
|
|
980
|
-
// Use existing project path
|
|
981
|
-
finalProjectPath = path.resolve(projectPath);
|
|
1115
|
+
const response = await runExternalAgentRequest({
|
|
1116
|
+
user: req.user,
|
|
1117
|
+
request: normalized,
|
|
1118
|
+
transportWriter,
|
|
1119
|
+
endTransportOnFinish: normalized.stream
|
|
1120
|
+
});
|
|
982
1121
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
await fs.access(finalProjectPath);
|
|
986
|
-
} catch (error) {
|
|
987
|
-
throw new Error(`Project path does not exist: ${finalProjectPath}`);
|
|
988
|
-
}
|
|
1122
|
+
if (!normalized.stream) {
|
|
1123
|
+
return res.json(response);
|
|
989
1124
|
}
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
console.error('❌ External session error:', error);
|
|
990
1127
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
// If project already exists, that's fine - continue with the existing registration
|
|
998
|
-
if (error.message && error.message.includes('Project already configured')) {
|
|
999
|
-
console.log('📦 Using existing project registration for:', finalProjectPath);
|
|
1000
|
-
project = { path: finalProjectPath };
|
|
1001
|
-
} else {
|
|
1002
|
-
throw error;
|
|
1128
|
+
if (normalized?.stream) {
|
|
1129
|
+
if (!res.headersSent) {
|
|
1130
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
1131
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
1132
|
+
res.setHeader('Connection', 'keep-alive');
|
|
1133
|
+
res.setHeader('X-Accel-Buffering', 'no');
|
|
1003
1134
|
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
// Set up writer based on streaming mode
|
|
1007
|
-
const statusMessage = normalizedSessionId
|
|
1008
|
-
? (githubUrl ? 'Repository cloned and session resumed' : 'Session resumed')
|
|
1009
|
-
: (githubUrl ? 'Repository cloned and session started' : 'Session started');
|
|
1010
1135
|
|
|
1011
|
-
|
|
1012
|
-
// Set up SSE headers for streaming
|
|
1013
|
-
res.setHeader('Content-Type', 'text/event-stream');
|
|
1014
|
-
res.setHeader('Cache-Control', 'no-cache');
|
|
1015
|
-
res.setHeader('Connection', 'keep-alive');
|
|
1016
|
-
res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering
|
|
1017
|
-
|
|
1018
|
-
writer = new SSEStreamWriter(res);
|
|
1019
|
-
|
|
1020
|
-
// Send initial status
|
|
1136
|
+
const writer = new ExternalSSEStreamWriter(res);
|
|
1021
1137
|
writer.send({
|
|
1022
|
-
type: '
|
|
1023
|
-
|
|
1024
|
-
|
|
1138
|
+
type: 'error',
|
|
1139
|
+
error: error.message,
|
|
1140
|
+
message: `Failed: ${error.message}`
|
|
1025
1141
|
});
|
|
1026
|
-
} else {
|
|
1027
|
-
// Non-streaming mode: collect messages
|
|
1028
|
-
writer = new ResponseCollector();
|
|
1029
|
-
|
|
1030
|
-
// Collect initial status message
|
|
1031
|
-
writer.send({
|
|
1032
|
-
type: 'status',
|
|
1033
|
-
message: statusMessage,
|
|
1034
|
-
projectPath: finalProjectPath
|
|
1035
|
-
});
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
// Start the appropriate session
|
|
1039
|
-
if (provider === 'claude') {
|
|
1040
|
-
console.log('🤖 Starting Claude SDK session');
|
|
1041
|
-
|
|
1042
|
-
await queryClaudeSDK(normalizedMessage, {
|
|
1043
|
-
projectPath: finalProjectPath,
|
|
1044
|
-
cwd: finalProjectPath,
|
|
1045
|
-
sessionId: normalizedSessionId,
|
|
1046
|
-
model: model,
|
|
1047
|
-
permissionMode: 'bypassPermissions' // Bypass all permissions for API calls
|
|
1048
|
-
}, writer);
|
|
1049
|
-
|
|
1050
|
-
} else if (provider === 'cursor') {
|
|
1051
|
-
console.log('🖱️ Starting Cursor CLI session');
|
|
1052
|
-
|
|
1053
|
-
await spawnCursor(normalizedMessage, {
|
|
1054
|
-
projectPath: finalProjectPath,
|
|
1055
|
-
cwd: finalProjectPath,
|
|
1056
|
-
sessionId: normalizedSessionId,
|
|
1057
|
-
resume: !!normalizedSessionId,
|
|
1058
|
-
model: model || undefined,
|
|
1059
|
-
skipPermissions: true // Bypass permissions for Cursor
|
|
1060
|
-
}, writer);
|
|
1061
|
-
} else if (provider === 'codex') {
|
|
1062
|
-
console.log('🤖 Starting Codex SDK session');
|
|
1063
|
-
|
|
1064
|
-
await queryCodex(normalizedMessage, {
|
|
1065
|
-
projectPath: finalProjectPath,
|
|
1066
|
-
cwd: finalProjectPath,
|
|
1067
|
-
sessionId: normalizedSessionId,
|
|
1068
|
-
model: model || CODEX_MODELS.DEFAULT,
|
|
1069
|
-
permissionMode: 'bypassPermissions'
|
|
1070
|
-
}, writer);
|
|
1071
|
-
} else if (provider === 'gemini') {
|
|
1072
|
-
console.log('💎 Starting Gemini CLI session');
|
|
1073
|
-
|
|
1074
|
-
await queryGemini(normalizedMessage, {
|
|
1075
|
-
projectPath: finalProjectPath,
|
|
1076
|
-
cwd: finalProjectPath,
|
|
1077
|
-
sessionId: normalizedSessionId,
|
|
1078
|
-
resume: !!normalizedSessionId,
|
|
1079
|
-
model: model || GEMINI_MODELS.DEFAULT,
|
|
1080
|
-
permissionMode: 'bypassPermissions'
|
|
1081
|
-
}, writer);
|
|
1082
|
-
} else if (provider === 'opencode') {
|
|
1083
|
-
console.log('🧠 Starting OpenCode CLI session');
|
|
1084
|
-
|
|
1085
|
-
await queryOpencode(normalizedMessage, {
|
|
1086
|
-
projectPath: finalProjectPath,
|
|
1087
|
-
cwd: finalProjectPath,
|
|
1088
|
-
sessionId: normalizedSessionId,
|
|
1089
|
-
resume: !!normalizedSessionId,
|
|
1090
|
-
model: model || OPENCODE_MODELS.DEFAULT,
|
|
1091
|
-
permissionMode: 'bypassPermissions'
|
|
1092
|
-
}, writer);
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// Handle GitHub branch and PR creation after successful agent completion
|
|
1096
|
-
let branchInfo = null;
|
|
1097
|
-
let prInfo = null;
|
|
1098
|
-
|
|
1099
|
-
if (createBranch || createPR) {
|
|
1100
|
-
try {
|
|
1101
|
-
console.log('🔄 Starting GitHub branch/PR creation workflow...');
|
|
1102
|
-
|
|
1103
|
-
// Get GitHub token
|
|
1104
|
-
const tokenToUse = githubToken || githubTokensDb.getActiveGithubToken(req.user.id);
|
|
1105
|
-
|
|
1106
|
-
if (!tokenToUse) {
|
|
1107
|
-
throw new Error('GitHub token required for branch/PR creation. Please configure a GitHub token in settings.');
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
// Initialize Octokit
|
|
1111
|
-
const octokit = new Octokit({ auth: tokenToUse });
|
|
1112
|
-
|
|
1113
|
-
// Get GitHub URL - either from parameter or from git remote
|
|
1114
|
-
let repoUrl = githubUrl;
|
|
1115
|
-
if (!repoUrl) {
|
|
1116
|
-
console.log('🔍 Getting GitHub URL from git remote...');
|
|
1117
|
-
try {
|
|
1118
|
-
repoUrl = await getGitRemoteUrl(finalProjectPath);
|
|
1119
|
-
if (!repoUrl.includes('github.com')) {
|
|
1120
|
-
throw new Error('Project does not have a GitHub remote configured');
|
|
1121
|
-
}
|
|
1122
|
-
console.log(`✅ Found GitHub remote: ${repoUrl}`);
|
|
1123
|
-
} catch (error) {
|
|
1124
|
-
throw new Error(`Failed to get GitHub remote URL: ${error.message}`);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
// Parse GitHub URL to get owner and repo
|
|
1129
|
-
const { owner, repo } = parseGitHubUrl(repoUrl);
|
|
1130
|
-
console.log(`📦 Repository: ${owner}/${repo}`);
|
|
1131
|
-
|
|
1132
|
-
// Use provided branch name or auto-generate from message
|
|
1133
|
-
const finalBranchName = branchName || autogenerateBranchName(message);
|
|
1134
|
-
if (branchName) {
|
|
1135
|
-
console.log(`🌿 Using provided branch name: ${finalBranchName}`);
|
|
1136
|
-
|
|
1137
|
-
// Validate custom branch name
|
|
1138
|
-
const validation = validateBranchName(finalBranchName);
|
|
1139
|
-
if (!validation.valid) {
|
|
1140
|
-
throw new Error(`Invalid branch name: ${validation.error}`);
|
|
1141
|
-
}
|
|
1142
|
-
} else {
|
|
1143
|
-
console.log(`🌿 Auto-generated branch name: ${finalBranchName}`);
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
if (createBranch) {
|
|
1147
|
-
// Create and checkout the new branch locally
|
|
1148
|
-
console.log('🔄 Creating local branch...');
|
|
1149
|
-
const checkoutProcess = spawn('git', ['checkout', '-b', finalBranchName], {
|
|
1150
|
-
cwd: finalProjectPath,
|
|
1151
|
-
stdio: 'pipe'
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
await new Promise((resolve, reject) => {
|
|
1155
|
-
let stderr = '';
|
|
1156
|
-
checkoutProcess.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
1157
|
-
checkoutProcess.on('close', (code) => {
|
|
1158
|
-
if (code === 0) {
|
|
1159
|
-
console.log(`✅ Created and checked out local branch '${finalBranchName}'`);
|
|
1160
|
-
resolve();
|
|
1161
|
-
} else {
|
|
1162
|
-
// Branch might already exist locally, try to checkout
|
|
1163
|
-
if (stderr.includes('already exists')) {
|
|
1164
|
-
console.log(`ℹ️ Branch '${finalBranchName}' already exists locally, checking out...`);
|
|
1165
|
-
const checkoutExisting = spawn('git', ['checkout', finalBranchName], {
|
|
1166
|
-
cwd: finalProjectPath,
|
|
1167
|
-
stdio: 'pipe'
|
|
1168
|
-
});
|
|
1169
|
-
checkoutExisting.on('close', (checkoutCode) => {
|
|
1170
|
-
if (checkoutCode === 0) {
|
|
1171
|
-
console.log(`✅ Checked out existing branch '${finalBranchName}'`);
|
|
1172
|
-
resolve();
|
|
1173
|
-
} else {
|
|
1174
|
-
reject(new Error(`Failed to checkout existing branch: ${stderr}`));
|
|
1175
|
-
}
|
|
1176
|
-
});
|
|
1177
|
-
} else {
|
|
1178
|
-
reject(new Error(`Failed to create branch: ${stderr}`));
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
});
|
|
1182
|
-
});
|
|
1183
|
-
|
|
1184
|
-
// Push the branch to remote
|
|
1185
|
-
console.log('🔄 Pushing branch to remote...');
|
|
1186
|
-
const pushProcess = spawn('git', ['push', '-u', 'origin', finalBranchName], {
|
|
1187
|
-
cwd: finalProjectPath,
|
|
1188
|
-
stdio: 'pipe'
|
|
1189
|
-
});
|
|
1190
|
-
|
|
1191
|
-
await new Promise((resolve, reject) => {
|
|
1192
|
-
let stderr = '';
|
|
1193
|
-
let stdout = '';
|
|
1194
|
-
pushProcess.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
1195
|
-
pushProcess.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
1196
|
-
pushProcess.on('close', (code) => {
|
|
1197
|
-
if (code === 0) {
|
|
1198
|
-
console.log(`✅ Pushed branch '${finalBranchName}' to remote`);
|
|
1199
|
-
resolve();
|
|
1200
|
-
} else {
|
|
1201
|
-
// Check if branch exists on remote but has different commits
|
|
1202
|
-
if (stderr.includes('already exists') || stderr.includes('up-to-date')) {
|
|
1203
|
-
console.log(`ℹ️ Branch '${finalBranchName}' already exists on remote, using existing branch`);
|
|
1204
|
-
resolve();
|
|
1205
|
-
} else {
|
|
1206
|
-
reject(new Error(`Failed to push branch: ${stderr}`));
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
});
|
|
1210
|
-
});
|
|
1211
|
-
|
|
1212
|
-
branchInfo = {
|
|
1213
|
-
name: finalBranchName,
|
|
1214
|
-
url: `https://github.com/${owner}/${repo}/tree/${finalBranchName}`
|
|
1215
|
-
};
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
if (createPR) {
|
|
1219
|
-
// Get commit messages to generate PR description
|
|
1220
|
-
console.log('🔄 Generating PR title and description...');
|
|
1221
|
-
const commitMessages = await getCommitMessages(finalProjectPath, 5);
|
|
1222
|
-
|
|
1223
|
-
// Use the first commit message as the PR title, or fallback to the agent message
|
|
1224
|
-
const prTitle = commitMessages.length > 0 ? commitMessages[0] : message;
|
|
1225
|
-
|
|
1226
|
-
// Generate PR body from commit messages
|
|
1227
|
-
let prBody = '## Changes\n\n';
|
|
1228
|
-
if (commitMessages.length > 0) {
|
|
1229
|
-
prBody += commitMessages.map(msg => `- ${msg}`).join('\n');
|
|
1230
|
-
} else {
|
|
1231
|
-
prBody += `Agent task: ${message}`;
|
|
1232
|
-
}
|
|
1233
|
-
prBody += '\n\n---\n*This pull request was automatically created by Axhub Genie Agent.*';
|
|
1234
|
-
|
|
1235
|
-
console.log(`📝 PR Title: ${prTitle}`);
|
|
1236
|
-
|
|
1237
|
-
// Create the pull request
|
|
1238
|
-
console.log('🔄 Creating pull request...');
|
|
1239
|
-
prInfo = await createGitHubPR(octokit, owner, repo, finalBranchName, prTitle, prBody, 'main');
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
// Send branch/PR info in response
|
|
1243
|
-
if (stream) {
|
|
1244
|
-
if (branchInfo) {
|
|
1245
|
-
writer.send({
|
|
1246
|
-
type: 'github-branch',
|
|
1247
|
-
branch: branchInfo
|
|
1248
|
-
});
|
|
1249
|
-
}
|
|
1250
|
-
if (prInfo) {
|
|
1251
|
-
writer.send({
|
|
1252
|
-
type: 'github-pr',
|
|
1253
|
-
pullRequest: prInfo
|
|
1254
|
-
});
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
} catch (error) {
|
|
1259
|
-
console.error('❌ GitHub branch/PR creation error:', error);
|
|
1260
|
-
|
|
1261
|
-
// Send error but don't fail the entire request
|
|
1262
|
-
if (stream) {
|
|
1263
|
-
writer.send({
|
|
1264
|
-
type: 'github-error',
|
|
1265
|
-
error: error.message
|
|
1266
|
-
});
|
|
1267
|
-
}
|
|
1268
|
-
// Store error info for non-streaming response
|
|
1269
|
-
if (!stream) {
|
|
1270
|
-
branchInfo = { error: error.message };
|
|
1271
|
-
prInfo = { error: error.message };
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
// Handle response based on streaming mode
|
|
1277
|
-
if (stream) {
|
|
1278
|
-
// Streaming mode: end the SSE stream
|
|
1279
1142
|
writer.end();
|
|
1280
|
-
|
|
1281
|
-
// Non-streaming mode: send filtered messages and token summary as JSON
|
|
1282
|
-
const assistantMessages = writer.getAssistantMessages();
|
|
1283
|
-
const tokenSummary = writer.getTotalTokens();
|
|
1284
|
-
const resolvedSessionId = writer.getSessionId() || normalizedSessionId;
|
|
1285
|
-
const { sessionPath, sessionUrl } = buildSessionNavigation(resolvedSessionId);
|
|
1286
|
-
|
|
1287
|
-
const response = {
|
|
1288
|
-
success: true,
|
|
1289
|
-
sessionId: resolvedSessionId,
|
|
1290
|
-
sessionPath,
|
|
1291
|
-
sessionUrl,
|
|
1292
|
-
isResumed: !!normalizedSessionId,
|
|
1293
|
-
messages: assistantMessages,
|
|
1294
|
-
tokens: tokenSummary,
|
|
1295
|
-
projectPath: finalProjectPath
|
|
1296
|
-
};
|
|
1297
|
-
|
|
1298
|
-
// Add branch/PR info if created
|
|
1299
|
-
if (branchInfo) {
|
|
1300
|
-
response.branch = branchInfo;
|
|
1301
|
-
}
|
|
1302
|
-
if (prInfo) {
|
|
1303
|
-
response.pullRequest = prInfo;
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
res.json(response);
|
|
1143
|
+
return;
|
|
1307
1144
|
}
|
|
1308
1145
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
}, 5000);
|
|
1316
|
-
}
|
|
1146
|
+
return res.status(error.statusCode || 500).json({
|
|
1147
|
+
success: false,
|
|
1148
|
+
error: error.message
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1317
1152
|
|
|
1153
|
+
/**
|
|
1154
|
+
* POST /api/agent/abort
|
|
1155
|
+
*
|
|
1156
|
+
* Interrupt an active AI agent session.
|
|
1157
|
+
*
|
|
1158
|
+
* ================================================================================================
|
|
1159
|
+
* REQUEST BODY PARAMETERS
|
|
1160
|
+
* ================================================================================================
|
|
1161
|
+
*
|
|
1162
|
+
* @param {string} sessionId - (Required) Session ID to interrupt.
|
|
1163
|
+
* @param {string} provider - (Optional) AI provider for the active session.
|
|
1164
|
+
* Options: 'claude' | 'codex' | 'gemini' | 'opencode'
|
|
1165
|
+
* Default: 'claude'
|
|
1166
|
+
*
|
|
1167
|
+
* ================================================================================================
|
|
1168
|
+
* VALIDATION & ERROR HANDLING
|
|
1169
|
+
* ================================================================================================
|
|
1170
|
+
*
|
|
1171
|
+
* Input Validations (400 Bad Request):
|
|
1172
|
+
* - sessionId must be a non-empty string
|
|
1173
|
+
* - provider must be 'claude', 'codex', 'gemini', or 'opencode' when provided
|
|
1174
|
+
*
|
|
1175
|
+
* Success Response (200 OK):
|
|
1176
|
+
* {
|
|
1177
|
+
* success: true,
|
|
1178
|
+
* aborted: true,
|
|
1179
|
+
* sessionId: "session-123",
|
|
1180
|
+
* provider: "claude",
|
|
1181
|
+
* message: "Session aborted"
|
|
1182
|
+
* }
|
|
1183
|
+
*
|
|
1184
|
+
* Not Found Response (200 OK):
|
|
1185
|
+
* {
|
|
1186
|
+
* success: false,
|
|
1187
|
+
* aborted: false,
|
|
1188
|
+
* sessionId: "session-123",
|
|
1189
|
+
* provider: "claude",
|
|
1190
|
+
* error: "Active session not found"
|
|
1191
|
+
* }
|
|
1192
|
+
*
|
|
1193
|
+
* Error Response:
|
|
1194
|
+
* HTTP Status: 400, 401, 500
|
|
1195
|
+
* Content-Type: application/json
|
|
1196
|
+
* { success: false, error: "Error description" }
|
|
1197
|
+
*/
|
|
1198
|
+
router.post('/abort', validateExternalAgentApiKey, async (req, res) => {
|
|
1199
|
+
let normalized = null;
|
|
1200
|
+
try {
|
|
1201
|
+
normalized = normalizeExternalAgentAbortRequest(req.body);
|
|
1318
1202
|
} catch (error) {
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
cleanupProject(finalProjectPath, sessionIdForCleanup);
|
|
1325
|
-
}
|
|
1203
|
+
return res.status(error.statusCode || 400).json({
|
|
1204
|
+
success: false,
|
|
1205
|
+
error: error.message
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1326
1208
|
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
res.setHeader('X-Accel-Buffering', 'no');
|
|
1335
|
-
writer = new SSEStreamWriter(res);
|
|
1336
|
-
}
|
|
1209
|
+
try {
|
|
1210
|
+
const writer = new SessionEventMirrorWriter(new ExternalNoopWriter(), normalized.provider);
|
|
1211
|
+
const { success } = await abortAgentSessionWithWriter({
|
|
1212
|
+
provider: normalized.provider,
|
|
1213
|
+
sessionId: normalized.sessionId,
|
|
1214
|
+
writer
|
|
1215
|
+
});
|
|
1337
1216
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
type: 'error',
|
|
1341
|
-
error: error.message,
|
|
1342
|
-
message: `Failed: ${error.message}`
|
|
1343
|
-
});
|
|
1344
|
-
writer.end();
|
|
1345
|
-
}
|
|
1346
|
-
} else if (!res.headersSent) {
|
|
1347
|
-
res.status(500).json({
|
|
1217
|
+
if (!success) {
|
|
1218
|
+
return res.json({
|
|
1348
1219
|
success: false,
|
|
1349
|
-
|
|
1220
|
+
aborted: false,
|
|
1221
|
+
sessionId: normalized.sessionId,
|
|
1222
|
+
provider: normalized.provider,
|
|
1223
|
+
error: 'Active session not found'
|
|
1350
1224
|
});
|
|
1351
1225
|
}
|
|
1226
|
+
|
|
1227
|
+
return res.json({
|
|
1228
|
+
success: true,
|
|
1229
|
+
aborted: true,
|
|
1230
|
+
sessionId: normalized.sessionId,
|
|
1231
|
+
provider: normalized.provider,
|
|
1232
|
+
message: 'Session aborted'
|
|
1233
|
+
});
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
console.error('❌ External abort session error:', error);
|
|
1236
|
+
return res.status(500).json({
|
|
1237
|
+
success: false,
|
|
1238
|
+
error: error.message
|
|
1239
|
+
});
|
|
1352
1240
|
}
|
|
1353
1241
|
});
|
|
1354
1242
|
|