mycode-cli 0.1.0__py3-none-any.whl
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.
- mycode/__init__.py +1 -0
- mycode/cli/__init__.py +1 -0
- mycode/cli/chat.py +650 -0
- mycode/cli/main.py +245 -0
- mycode/cli/render.py +693 -0
- mycode/cli/runtime.py +271 -0
- mycode/cli/theme.py +109 -0
- mycode/core/__init__.py +30 -0
- mycode/core/agent.py +515 -0
- mycode/core/config.py +551 -0
- mycode/core/messages.py +166 -0
- mycode/core/models.py +144 -0
- mycode/core/models_catalog.json +2090 -0
- mycode/core/providers/__init__.py +86 -0
- mycode/core/providers/anthropic_like.py +366 -0
- mycode/core/providers/base.py +351 -0
- mycode/core/providers/gemini.py +321 -0
- mycode/core/providers/openai_chat.py +356 -0
- mycode/core/providers/openai_responses.py +326 -0
- mycode/core/session.py +537 -0
- mycode/core/system_prompt.md +10 -0
- mycode/core/system_prompt.py +319 -0
- mycode/core/tools.py +898 -0
- mycode/server/__init__.py +1 -0
- mycode/server/app.py +52 -0
- mycode/server/deps.py +29 -0
- mycode/server/routers/__init__.py +7 -0
- mycode/server/routers/chat.py +320 -0
- mycode/server/routers/sessions.py +77 -0
- mycode/server/routers/workspaces.py +92 -0
- mycode/server/run_manager.py +195 -0
- mycode/server/schemas.py +66 -0
- mycode/server/static/assets/EditDiff-C1ql7kft.js +12 -0
- mycode/server/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- mycode/server/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- mycode/server/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- mycode/server/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- mycode/server/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- mycode/server/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- mycode/server/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- mycode/server/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- mycode/server/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- mycode/server/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- mycode/server/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- mycode/server/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- mycode/server/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- mycode/server/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- mycode/server/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- mycode/server/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- mycode/server/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- mycode/server/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- mycode/server/static/assets/abap-BdImnpbu.js +1 -0
- mycode/server/static/assets/actionscript-3-CoDkCxhg.js +1 -0
- mycode/server/static/assets/ada-bCR0ucgS.js +1 -0
- mycode/server/static/assets/andromeeda-C4gqWexZ.js +1 -0
- mycode/server/static/assets/angular-html-DA-rfuFy.js +1 -0
- mycode/server/static/assets/angular-ts-BrjP3tb8.js +1 -0
- mycode/server/static/assets/apache-Pmp26Uib.js +1 -0
- mycode/server/static/assets/apex-D8_7TLub.js +1 -0
- mycode/server/static/assets/apl-CORt7UWP.js +1 -0
- mycode/server/static/assets/applescript-Co6uUVPk.js +1 -0
- mycode/server/static/assets/ara-BRHolxvo.js +1 -0
- mycode/server/static/assets/asciidoc-Ve4PFQV2.js +1 -0
- mycode/server/static/assets/asm-D_Q5rh1f.js +1 -0
- mycode/server/static/assets/astro-HNnZUWAn.js +1 -0
- mycode/server/static/assets/aurora-x-D-2ljcwZ.js +1 -0
- mycode/server/static/assets/auto-render-xntwXHOX.js +261 -0
- mycode/server/static/assets/awk-DMzUqQB5.js +1 -0
- mycode/server/static/assets/ayu-dark-DYE7WIF3.js +1 -0
- mycode/server/static/assets/ayu-light-BA47KaF1.js +1 -0
- mycode/server/static/assets/ayu-mirage-32ctXXKs.js +1 -0
- mycode/server/static/assets/ballerina-BFfxhgS-.js +1 -0
- mycode/server/static/assets/bat-BkioyH1T.js +1 -0
- mycode/server/static/assets/beancount-k_qm7-4y.js +1 -0
- mycode/server/static/assets/berry-uYugtg8r.js +1 -0
- mycode/server/static/assets/bibtex-CHM0blh-.js +1 -0
- mycode/server/static/assets/bicep-Bmn6On1c.js +1 -0
- mycode/server/static/assets/bird2-BIv1doCn.js +1 -0
- mycode/server/static/assets/blade-BjGOyj-B.js +1 -0
- mycode/server/static/assets/bsl-BO_Y6i37.js +1 -0
- mycode/server/static/assets/c-BIGW1oBm.js +1 -0
- mycode/server/static/assets/c3-eo99z4R2.js +1 -0
- mycode/server/static/assets/cadence-Bv_4Rxtq.js +1 -0
- mycode/server/static/assets/cairo-KRGpt6FW.js +1 -0
- mycode/server/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- mycode/server/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- mycode/server/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- mycode/server/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- mycode/server/static/assets/clarity-D53aC0YG.js +1 -0
- mycode/server/static/assets/clojure-P80f7IUj.js +1 -0
- mycode/server/static/assets/cmake-D1j8_8rp.js +1 -0
- mycode/server/static/assets/cobol-nBiQ_Alo.js +1 -0
- mycode/server/static/assets/codeowners-Bp6g37R7.js +1 -0
- mycode/server/static/assets/codeql-DsOJ9woJ.js +1 -0
- mycode/server/static/assets/coffee-Ch7k5sss.js +1 -0
- mycode/server/static/assets/common-lisp-Cg-RD9OK.js +1 -0
- mycode/server/static/assets/coq-DkFqJrB1.js +1 -0
- mycode/server/static/assets/cpp-CofmeUqb.js +1 -0
- mycode/server/static/assets/crystal-DNxU26gB.js +1 -0
- mycode/server/static/assets/csharp-COcwbKMJ.js +1 -0
- mycode/server/static/assets/css-CLj8gQPS.js +1 -0
- mycode/server/static/assets/csv-fuZLfV_i.js +1 -0
- mycode/server/static/assets/cue-D82EKSYY.js +1 -0
- mycode/server/static/assets/cypher-COkxafJQ.js +1 -0
- mycode/server/static/assets/d-85-TOEBH.js +1 -0
- mycode/server/static/assets/dark-plus-C3mMm8J8.js +1 -0
- mycode/server/static/assets/dart-bE4Kk8sk.js +1 -0
- mycode/server/static/assets/dax-CEL-wOlO.js +1 -0
- mycode/server/static/assets/desktop-BmXAJ9_W.js +1 -0
- mycode/server/static/assets/diff-D97Zzqfu.js +1 -0
- mycode/server/static/assets/docker-BcOcwvcX.js +1 -0
- mycode/server/static/assets/dotenv-Da5cRb03.js +1 -0
- mycode/server/static/assets/dracula-BzJJZx-M.js +1 -0
- mycode/server/static/assets/dracula-soft-BXkSAIEj.js +1 -0
- mycode/server/static/assets/dream-maker-BtqSS_iP.js +1 -0
- mycode/server/static/assets/edge-FbVlp4U3.js +1 -0
- mycode/server/static/assets/elixir-CkH2-t6x.js +1 -0
- mycode/server/static/assets/elm-DbKCFpqz.js +1 -0
- mycode/server/static/assets/emacs-lisp-CXvaQtF9.js +1 -0
- mycode/server/static/assets/erb-BYCe7drp.js +1 -0
- mycode/server/static/assets/erlang-DsQrWhSR.js +1 -0
- mycode/server/static/assets/everforest-dark-BgDCqdQA.js +1 -0
- mycode/server/static/assets/everforest-light-C8M2exoo.js +1 -0
- mycode/server/static/assets/fennel-BYunw83y.js +1 -0
- mycode/server/static/assets/fish-BvzEVeQv.js +1 -0
- mycode/server/static/assets/fluent-C4IJs8-o.js +1 -0
- mycode/server/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- mycode/server/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
- mycode/server/static/assets/fsharp-CXgrBDvD.js +1 -0
- mycode/server/static/assets/gdresource-BOOCDP_w.js +1 -0
- mycode/server/static/assets/gdscript-C5YyOfLZ.js +1 -0
- mycode/server/static/assets/gdshader-DkwncUOv.js +1 -0
- mycode/server/static/assets/genie-D0YGMca9.js +1 -0
- mycode/server/static/assets/gherkin-DyxjwDmM.js +1 -0
- mycode/server/static/assets/git-commit-F4YmCXRG.js +1 -0
- mycode/server/static/assets/git-rebase-r7XF79zn.js +1 -0
- mycode/server/static/assets/github-dark-DHJKELXO.js +1 -0
- mycode/server/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
- mycode/server/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- mycode/server/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- mycode/server/static/assets/github-light-DAi9KRSo.js +1 -0
- mycode/server/static/assets/github-light-default-D7oLnXFd.js +1 -0
- mycode/server/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- mycode/server/static/assets/gleam-BspZqrRM.js +1 -0
- mycode/server/static/assets/glimmer-js-ByusRIyA.js +1 -0
- mycode/server/static/assets/glimmer-ts-BfAWNZQY.js +1 -0
- mycode/server/static/assets/glsl-DplSGwfg.js +1 -0
- mycode/server/static/assets/gn-n2N0HUVH.js +1 -0
- mycode/server/static/assets/gnuplot-DdkO51Og.js +1 -0
- mycode/server/static/assets/go-C27-OAKa.js +1 -0
- mycode/server/static/assets/graphql-ChdNCCLP.js +1 -0
- mycode/server/static/assets/groovy-gcz8RCvz.js +1 -0
- mycode/server/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- mycode/server/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- mycode/server/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- mycode/server/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- mycode/server/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- mycode/server/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- mycode/server/static/assets/hack-i7_Ulhet.js +1 -0
- mycode/server/static/assets/haml-D5jkg6IW.js +1 -0
- mycode/server/static/assets/handlebars-BpdQsYii.js +1 -0
- mycode/server/static/assets/haskell-Df6bDoY_.js +1 -0
- mycode/server/static/assets/haxe-CzTSHFRz.js +1 -0
- mycode/server/static/assets/hcl-BWvSN4gD.js +1 -0
- mycode/server/static/assets/hjson-D5-asLiD.js +1 -0
- mycode/server/static/assets/hlsl-D3lLCCz7.js +1 -0
- mycode/server/static/assets/horizon-BUw7H-hv.js +1 -0
- mycode/server/static/assets/horizon-bright-CUuTKBJd.js +1 -0
- mycode/server/static/assets/houston-DnULxvSX.js +1 -0
- mycode/server/static/assets/html-derivative-DlHx6ybY.js +1 -0
- mycode/server/static/assets/html-pp8916En.js +1 -0
- mycode/server/static/assets/http-jrhK8wxY.js +1 -0
- mycode/server/static/assets/hurl-irOxFIW8.js +1 -0
- mycode/server/static/assets/hxml-Bvhsp5Yf.js +1 -0
- mycode/server/static/assets/hy-DFXneXwc.js +1 -0
- mycode/server/static/assets/imba-DGztddWO.js +1 -0
- mycode/server/static/assets/index-B4e4WQPq.css +1 -0
- mycode/server/static/assets/index-C2xTNJGd.js +203 -0
- mycode/server/static/assets/ini-BEwlwnbL.js +1 -0
- mycode/server/static/assets/java-CylS5w8V.js +1 -0
- mycode/server/static/assets/javascript-wDzz0qaB.js +1 -0
- mycode/server/static/assets/jinja-f2NsQr07.js +1 -0
- mycode/server/static/assets/jison-wvAkD_A8.js +1 -0
- mycode/server/static/assets/json-Cp-IABpG.js +1 -0
- mycode/server/static/assets/json5-C9tS-k6U.js +1 -0
- mycode/server/static/assets/jsonc-Des-eS-w.js +1 -0
- mycode/server/static/assets/jsonl-DcaNXYhu.js +1 -0
- mycode/server/static/assets/jsonnet-DFQXde-d.js +1 -0
- mycode/server/static/assets/jssm-C2t-YnRu.js +1 -0
- mycode/server/static/assets/jsx-g9-lgVsj.js +1 -0
- mycode/server/static/assets/julia-CxzCAyBv.js +1 -0
- mycode/server/static/assets/just-VxiPbLrw.js +1 -0
- mycode/server/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- mycode/server/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- mycode/server/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
- mycode/server/static/assets/katex-DnJR2-55.css +1 -0
- mycode/server/static/assets/kdl-DV7GczEv.js +1 -0
- mycode/server/static/assets/kotlin-BdnUsdx6.js +1 -0
- mycode/server/static/assets/kusto-wEQ09or8.js +1 -0
- mycode/server/static/assets/laserwave-DUszq2jm.js +1 -0
- mycode/server/static/assets/latex-CWtU0Tv5.js +1 -0
- mycode/server/static/assets/lean-BZvkOJ9d.js +1 -0
- mycode/server/static/assets/less-B1dDrJ26.js +1 -0
- mycode/server/static/assets/light-plus-B7mTdjB0.js +1 -0
- mycode/server/static/assets/liquid-C0sCDyMI.js +1 -0
- mycode/server/static/assets/llvm-DjAJT7YJ.js +1 -0
- mycode/server/static/assets/log-2UxHyX5q.js +1 -0
- mycode/server/static/assets/logo-BtOb2qkB.js +1 -0
- mycode/server/static/assets/lua-BaeVxFsk.js +1 -0
- mycode/server/static/assets/luau-C-HG3fhB.js +1 -0
- mycode/server/static/assets/make-CHLpvVh8.js +1 -0
- mycode/server/static/assets/markdown-Cvjx9yec.js +1 -0
- mycode/server/static/assets/marko-DjSrsDqO.js +1 -0
- mycode/server/static/assets/material-theme-D5KoaKCx.js +1 -0
- mycode/server/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
- mycode/server/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- mycode/server/static/assets/material-theme-ocean-CyktbL80.js +1 -0
- mycode/server/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- mycode/server/static/assets/matlab-D7o27uSR.js +1 -0
- mycode/server/static/assets/mdc-DTYItulj.js +1 -0
- mycode/server/static/assets/mdx-Cmh6b_Ma.js +1 -0
- mycode/server/static/assets/mermaid-mWjccvbQ.js +1 -0
- mycode/server/static/assets/min-dark-CafNBF8u.js +1 -0
- mycode/server/static/assets/min-light-CTRr51gU.js +1 -0
- mycode/server/static/assets/mipsasm-CKIfxQSi.js +1 -0
- mycode/server/static/assets/mojo-rZm6bMo-.js +1 -0
- mycode/server/static/assets/monokai-D4h5O-jR.js +1 -0
- mycode/server/static/assets/moonbit-_H4v1dQx.js +1 -0
- mycode/server/static/assets/move-IF9eRakj.js +1 -0
- mycode/server/static/assets/narrat-DRg8JJMk.js +1 -0
- mycode/server/static/assets/nextflow-C-mBbutL.js +1 -0
- mycode/server/static/assets/nextflow-groovy-vE_lwT2v.js +1 -0
- mycode/server/static/assets/nginx-BpAMiNFr.js +1 -0
- mycode/server/static/assets/night-owl-C39BiMTA.js +1 -0
- mycode/server/static/assets/night-owl-light-CMTm3GFP.js +1 -0
- mycode/server/static/assets/nim-BIad80T-.js +1 -0
- mycode/server/static/assets/nix-CwoSXNpI.js +1 -0
- mycode/server/static/assets/nord-Ddv68eIx.js +1 -0
- mycode/server/static/assets/nushell-Cz2AlsmD.js +1 -0
- mycode/server/static/assets/objective-c-DXmwc3jG.js +1 -0
- mycode/server/static/assets/objective-cpp-CLxacb5B.js +1 -0
- mycode/server/static/assets/ocaml-C0hk2d4L.js +1 -0
- mycode/server/static/assets/odin-BBf5iR-q.js +1 -0
- mycode/server/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- mycode/server/static/assets/one-light-C3Wv6jpd.js +1 -0
- mycode/server/static/assets/openscad-C4EeE6gA.js +1 -0
- mycode/server/static/assets/pascal-D93ZcfNL.js +1 -0
- mycode/server/static/assets/perl-NvoQZIq0.js +1 -0
- mycode/server/static/assets/php-R6g_5hLQ.js +1 -0
- mycode/server/static/assets/pkl-u5AG7uiY.js +1 -0
- mycode/server/static/assets/plastic-3e1v2bzS.js +1 -0
- mycode/server/static/assets/plsql-ChMvpjG-.js +1 -0
- mycode/server/static/assets/po-BTJTHyun.js +1 -0
- mycode/server/static/assets/poimandres-CS3Unz2-.js +1 -0
- mycode/server/static/assets/polar-C0HS_06l.js +1 -0
- mycode/server/static/assets/postcss-CXtECtnM.js +1 -0
- mycode/server/static/assets/powerquery-CEu0bR-o.js +1 -0
- mycode/server/static/assets/powershell-Dpen1YoG.js +1 -0
- mycode/server/static/assets/prisma-Dd19v3D-.js +1 -0
- mycode/server/static/assets/prolog-CbFg5uaA.js +1 -0
- mycode/server/static/assets/proto-C7zT0LnQ.js +1 -0
- mycode/server/static/assets/pug-DKIMFp6K.js +1 -0
- mycode/server/static/assets/puppet-BMWR74SV.js +1 -0
- mycode/server/static/assets/purescript-CklMAg4u.js +1 -0
- mycode/server/static/assets/python-B6aJPvgy.js +1 -0
- mycode/server/static/assets/qml-3beO22l8.js +1 -0
- mycode/server/static/assets/qmldir-C8lEn-DE.js +1 -0
- mycode/server/static/assets/qss-IeuSbFQv.js +1 -0
- mycode/server/static/assets/r-Dspwwk_N.js +1 -0
- mycode/server/static/assets/racket-BqYA7rlc.js +1 -0
- mycode/server/static/assets/raku-DXvB9xmW.js +1 -0
- mycode/server/static/assets/razor-BDqjjVU7.js +1 -0
- mycode/server/static/assets/red-bN70gL4F.js +1 -0
- mycode/server/static/assets/reg-C-SQnVFl.js +1 -0
- mycode/server/static/assets/regexp-CDVJQ6XC.js +1 -0
- mycode/server/static/assets/rel-C3B-1QV4.js +1 -0
- mycode/server/static/assets/riscv-BM1_JUlF.js +1 -0
- mycode/server/static/assets/ron-D8l8udqQ.js +1 -0
- mycode/server/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- mycode/server/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- mycode/server/static/assets/rose-pine-qdsjHGoJ.js +1 -0
- mycode/server/static/assets/rosmsg-BJDFO7_C.js +1 -0
- mycode/server/static/assets/rst-CRjBmOyv.js +1 -0
- mycode/server/static/assets/ruby-Wjq7vjNf.js +1 -0
- mycode/server/static/assets/rust-B1yitclQ.js +1 -0
- mycode/server/static/assets/sas-cz2c8ADy.js +1 -0
- mycode/server/static/assets/sass-Cj5Yp3dK.js +1 -0
- mycode/server/static/assets/scala-C151Ov-r.js +1 -0
- mycode/server/static/assets/scheme-C98Dy4si.js +1 -0
- mycode/server/static/assets/scss-D5BDwBP9.js +1 -0
- mycode/server/static/assets/sdbl-DVxCFoDh.js +1 -0
- mycode/server/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
- mycode/server/static/assets/shellscript-Yzrsuije.js +1 -0
- mycode/server/static/assets/shellsession-BADoaaVG.js +1 -0
- mycode/server/static/assets/slack-dark-BthQWCQV.js +1 -0
- mycode/server/static/assets/slack-ochin-DqwNpetd.js +1 -0
- mycode/server/static/assets/smalltalk-BERRCDM3.js +1 -0
- mycode/server/static/assets/snazzy-light-Bw305WKR.js +1 -0
- mycode/server/static/assets/solarized-dark-DXbdFlpD.js +1 -0
- mycode/server/static/assets/solarized-light-L9t79GZl.js +1 -0
- mycode/server/static/assets/solidity-rGO070M0.js +1 -0
- mycode/server/static/assets/soy-8wufbnw4.js +1 -0
- mycode/server/static/assets/sparql-rVzFXLq3.js +1 -0
- mycode/server/static/assets/splunk-BtCnVYZw.js +1 -0
- mycode/server/static/assets/sql-BLtJtn59.js +1 -0
- mycode/server/static/assets/ssh-config-_ykCGR6B.js +1 -0
- mycode/server/static/assets/stata-BH5u7GGu.js +1 -0
- mycode/server/static/assets/stylus-BEDo0Tqx.js +1 -0
- mycode/server/static/assets/surrealql-Bq5Q-fJD.js +1 -0
- mycode/server/static/assets/svelte-Cy7k_4gC.js +1 -0
- mycode/server/static/assets/swift-D82vCrfD.js +1 -0
- mycode/server/static/assets/synthwave-84-CbfX1IO0.js +1 -0
- mycode/server/static/assets/system-verilog-CnnmHF94.js +1 -0
- mycode/server/static/assets/systemd-4A_iFExJ.js +1 -0
- mycode/server/static/assets/talonscript-CkByrt1z.js +1 -0
- mycode/server/static/assets/tasl-QIJgUcNo.js +1 -0
- mycode/server/static/assets/tcl-dwOrl1Do.js +1 -0
- mycode/server/static/assets/templ-DhtptRzy.js +1 -0
- mycode/server/static/assets/terraform-BETggiCN.js +1 -0
- mycode/server/static/assets/tex-idrVyKtj.js +1 -0
- mycode/server/static/assets/tokyo-night-hegEt444.js +1 -0
- mycode/server/static/assets/toml-vGWfd6FD.js +1 -0
- mycode/server/static/assets/ts-tags-DQrlYJgV.js +1 -0
- mycode/server/static/assets/tsv-B_m7g4N7.js +1 -0
- mycode/server/static/assets/tsx-COt5Ahok.js +1 -0
- mycode/server/static/assets/turtle-BsS91CYL.js +1 -0
- mycode/server/static/assets/twig-xg9kU7Mw.js +1 -0
- mycode/server/static/assets/typescript-BPQ3VLAy.js +1 -0
- mycode/server/static/assets/typespec-CAFt9gP4.js +1 -0
- mycode/server/static/assets/typst-DHCkPAjA.js +1 -0
- mycode/server/static/assets/v-BcVCzyr7.js +1 -0
- mycode/server/static/assets/vala-CsfeWuGM.js +1 -0
- mycode/server/static/assets/vb-D17OF-Vu.js +1 -0
- mycode/server/static/assets/verilog-BQ8w6xss.js +1 -0
- mycode/server/static/assets/vesper-DU1UobuO.js +1 -0
- mycode/server/static/assets/vhdl-CeAyd5Ju.js +1 -0
- mycode/server/static/assets/viml-CJc9bBzg.js +1 -0
- mycode/server/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
- mycode/server/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
- mycode/server/static/assets/vitesse-light-CVO1_9PV.js +1 -0
- mycode/server/static/assets/vue-D2xRrEX4.js +1 -0
- mycode/server/static/assets/vue-html-AaS7Mt5G.js +1 -0
- mycode/server/static/assets/vue-vine-BoDAl6tE.js +1 -0
- mycode/server/static/assets/vyper-CDx5xZoG.js +1 -0
- mycode/server/static/assets/wasm-CG6Dc4jp.js +1 -0
- mycode/server/static/assets/wasm-MzD3tlZU.js +1 -0
- mycode/server/static/assets/wenyan-BV7otONQ.js +1 -0
- mycode/server/static/assets/wgsl-Dx-B1_4e.js +1 -0
- mycode/server/static/assets/wikitext-BhOHFoWU.js +1 -0
- mycode/server/static/assets/wit-5i3qLPDT.js +1 -0
- mycode/server/static/assets/wolfram-lXgVvXCa.js +1 -0
- mycode/server/static/assets/xml-sdJ4AIDG.js +1 -0
- mycode/server/static/assets/xsl-CtQFsRM5.js +1 -0
- mycode/server/static/assets/yaml-Buea-lGh.js +1 -0
- mycode/server/static/assets/zenscript-DVFEvuxE.js +1 -0
- mycode/server/static/assets/zig-VOosw3JB.js +1 -0
- mycode/server/static/favicon_slashes.svg +12 -0
- mycode/server/static/index.html +35 -0
- mycode_cli-0.1.0.dist-info/METADATA +186 -0
- mycode_cli-0.1.0.dist-info/RECORD +404 -0
- mycode_cli-0.1.0.dist-info/WHEEL +4 -0
- mycode_cli-0.1.0.dist-info/entry_points.txt +2 -0
- mycode_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""FastAPI server interface."""
|
mycode/server/app.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""FastAPI application entry point."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from fastapi import FastAPI
|
|
7
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
8
|
+
from fastapi.staticfiles import StaticFiles
|
|
9
|
+
|
|
10
|
+
from mycode.core.config import setup_logging
|
|
11
|
+
from mycode.server.routers import chat_router, sessions_router, workspaces_router
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def frontend_static_path() -> Path:
|
|
17
|
+
"""Return the packaged frontend static directory."""
|
|
18
|
+
|
|
19
|
+
return Path(__file__).resolve().parent / "static"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def create_app(*, serve_frontend: bool = True) -> FastAPI:
|
|
23
|
+
"""Create the FastAPI app."""
|
|
24
|
+
setup_logging()
|
|
25
|
+
application = FastAPI(title="mycode")
|
|
26
|
+
|
|
27
|
+
application.add_middleware(
|
|
28
|
+
CORSMiddleware,
|
|
29
|
+
allow_origins=["*"],
|
|
30
|
+
allow_methods=["*"],
|
|
31
|
+
allow_headers=["*"],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Mount API routers
|
|
35
|
+
application.include_router(chat_router, prefix="/api")
|
|
36
|
+
application.include_router(sessions_router, prefix="/api")
|
|
37
|
+
application.include_router(workspaces_router, prefix="/api")
|
|
38
|
+
|
|
39
|
+
if not serve_frontend:
|
|
40
|
+
logger.info("frontend disabled; starting in API-only mode")
|
|
41
|
+
return application
|
|
42
|
+
|
|
43
|
+
frontend_static = frontend_static_path()
|
|
44
|
+
if frontend_static.is_dir():
|
|
45
|
+
application.mount("/", StaticFiles(directory=str(frontend_static), html=True), name="frontend")
|
|
46
|
+
else:
|
|
47
|
+
logger.warning("frontend assets not found at %s; starting in API-only mode", frontend_static)
|
|
48
|
+
|
|
49
|
+
return application
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
app = create_app()
|
mycode/server/deps.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Shared dependencies for server routers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from functools import lru_cache
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
from fastapi import Depends
|
|
9
|
+
|
|
10
|
+
from mycode.core.session import SessionStore
|
|
11
|
+
from mycode.server.run_manager import RunManager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@lru_cache
|
|
15
|
+
def get_store() -> SessionStore:
|
|
16
|
+
"""Return the shared session store for server requests."""
|
|
17
|
+
|
|
18
|
+
return SessionStore()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@lru_cache
|
|
22
|
+
def get_run_manager() -> RunManager:
|
|
23
|
+
"""Return the shared in-process run manager."""
|
|
24
|
+
|
|
25
|
+
return RunManager()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
StoreDep = Annotated[SessionStore, Depends(get_store)]
|
|
29
|
+
RunManagerDep = Annotated[RunManager, Depends(get_run_manager)]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""API routers package."""
|
|
2
|
+
|
|
3
|
+
from mycode.server.routers.chat import router as chat_router
|
|
4
|
+
from mycode.server.routers.sessions import router as sessions_router
|
|
5
|
+
from mycode.server.routers.workspaces import router as workspaces_router
|
|
6
|
+
|
|
7
|
+
__all__ = ["chat_router", "sessions_router", "workspaces_router"]
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"""Chat and run streaming API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from base64 import b64encode
|
|
9
|
+
from collections.abc import AsyncIterator
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
14
|
+
from fastapi.responses import StreamingResponse
|
|
15
|
+
|
|
16
|
+
from mycode.core.agent import Agent
|
|
17
|
+
from mycode.core.config import (
|
|
18
|
+
get_settings,
|
|
19
|
+
normalize_reasoning_effort,
|
|
20
|
+
resolve_provider,
|
|
21
|
+
resolve_provider_choices,
|
|
22
|
+
)
|
|
23
|
+
from mycode.core.messages import build_message, flatten_message_text, image_block, text_block
|
|
24
|
+
from mycode.core.providers import get_provider_adapter, provider_default_models
|
|
25
|
+
from mycode.core.tools import detect_image_mime_type, resolve_path
|
|
26
|
+
from mycode.server.deps import RunManagerDep, StoreDep
|
|
27
|
+
from mycode.server.run_manager import ActiveRunError, RunState
|
|
28
|
+
from mycode.server.schemas import ChatRequest, StreamEvent
|
|
29
|
+
|
|
30
|
+
router = APIRouter()
|
|
31
|
+
|
|
32
|
+
REASONING_EFFORT_OPTIONS = ("auto", "none", "low", "medium", "high", "xhigh")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _format_sse(event: StreamEvent) -> str:
|
|
36
|
+
return f"data: {json.dumps(event.model_dump(exclude_none=True), ensure_ascii=False)}\n\n"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def _stream_run(req: Request, state: RunState, after: int) -> AsyncIterator[str]:
|
|
40
|
+
last_seq = max(0, after)
|
|
41
|
+
|
|
42
|
+
while True:
|
|
43
|
+
if await req.is_disconnected():
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
async with state.condition:
|
|
47
|
+
pending = [event for event in state.events if int(event.get("seq") or 0) > last_seq]
|
|
48
|
+
finished = state.status != "running"
|
|
49
|
+
|
|
50
|
+
if not pending and not finished:
|
|
51
|
+
try:
|
|
52
|
+
await asyncio.wait_for(state.condition.wait(), timeout=0.5)
|
|
53
|
+
except TimeoutError:
|
|
54
|
+
continue
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
for payload in pending:
|
|
58
|
+
if await req.is_disconnected():
|
|
59
|
+
return
|
|
60
|
+
yield _format_sse(StreamEvent(**payload))
|
|
61
|
+
last_seq = int(payload.get("seq") or last_seq)
|
|
62
|
+
|
|
63
|
+
if finished:
|
|
64
|
+
break
|
|
65
|
+
|
|
66
|
+
yield "data: [DONE]\n\n"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@router.post("/chat")
|
|
70
|
+
async def chat(chat: ChatRequest, store: StoreDep, runs: RunManagerDep):
|
|
71
|
+
cwd = os.path.abspath(chat.cwd or os.getcwd())
|
|
72
|
+
settings = get_settings(cwd)
|
|
73
|
+
resolved = resolve_provider(
|
|
74
|
+
settings,
|
|
75
|
+
provider_name=chat.provider,
|
|
76
|
+
model=chat.model,
|
|
77
|
+
api_key=chat.api_key,
|
|
78
|
+
api_base=chat.api_base,
|
|
79
|
+
)
|
|
80
|
+
request_effort = normalize_reasoning_effort(chat.reasoning_effort)
|
|
81
|
+
reasoning_effort = request_effort if request_effort is not None else resolved.reasoning_effort
|
|
82
|
+
session_id = chat.session_id or "default"
|
|
83
|
+
|
|
84
|
+
if chat.message and chat.input:
|
|
85
|
+
raise HTTPException(status_code=400, detail="message and input are mutually exclusive")
|
|
86
|
+
|
|
87
|
+
if chat.input:
|
|
88
|
+
blocks: list[dict[str, Any]] = []
|
|
89
|
+
for block in chat.input:
|
|
90
|
+
if block.type == "text":
|
|
91
|
+
text = str(block.text or "")
|
|
92
|
+
if text:
|
|
93
|
+
blocks.append(text_block(text))
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
if block.data:
|
|
97
|
+
# Inline base64 from web upload
|
|
98
|
+
if not block.mime_type:
|
|
99
|
+
raise HTTPException(status_code=400, detail="image data requires mime_type")
|
|
100
|
+
blocks.append(image_block(block.data, mime_type=block.mime_type, name=block.name or "image"))
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
if not block.path:
|
|
104
|
+
raise HTTPException(status_code=400, detail="image input requires path or data")
|
|
105
|
+
resolved_path = resolve_path(block.path, cwd=cwd)
|
|
106
|
+
image_path = Path(resolved_path)
|
|
107
|
+
if not image_path.exists() or not image_path.is_file():
|
|
108
|
+
raise HTTPException(status_code=400, detail=f"image file not found: {block.path}")
|
|
109
|
+
mime_type = block.mime_type or detect_image_mime_type(image_path)
|
|
110
|
+
if not mime_type:
|
|
111
|
+
raise HTTPException(status_code=400, detail=f"unsupported image file: {block.path}")
|
|
112
|
+
image_data = b64encode(image_path.read_bytes()).decode("utf-8")
|
|
113
|
+
blocks.append(image_block(image_data, mime_type=mime_type, name=block.name or image_path.name))
|
|
114
|
+
|
|
115
|
+
if not blocks:
|
|
116
|
+
raise HTTPException(status_code=400, detail="input must include at least one non-empty block")
|
|
117
|
+
user_message = build_message("user", blocks)
|
|
118
|
+
else:
|
|
119
|
+
message_text = str(chat.message or "").strip()
|
|
120
|
+
if not message_text:
|
|
121
|
+
raise HTTPException(status_code=400, detail="message or input is required")
|
|
122
|
+
user_message = build_message("user", [text_block(message_text)])
|
|
123
|
+
|
|
124
|
+
if any(isinstance(block, dict) and block.get("type") == "image" for block in user_message.get("content") or []):
|
|
125
|
+
if resolved.supports_image_input is not True:
|
|
126
|
+
raise HTTPException(status_code=400, detail="current model does not support image input")
|
|
127
|
+
|
|
128
|
+
data = await store.load_session(session_id)
|
|
129
|
+
session = (data or {}).get("session")
|
|
130
|
+
messages = (data or {}).get("messages") or []
|
|
131
|
+
|
|
132
|
+
if not session and chat.rewind_to is not None:
|
|
133
|
+
raise HTTPException(status_code=400, detail="rewind_to requires an existing session")
|
|
134
|
+
|
|
135
|
+
if not session:
|
|
136
|
+
title = flatten_message_text(user_message).replace("\n", " ").strip()[:48] or "New chat"
|
|
137
|
+
created = await store.create_session(
|
|
138
|
+
title,
|
|
139
|
+
session_id=session_id,
|
|
140
|
+
provider=resolved.provider,
|
|
141
|
+
model=resolved.model,
|
|
142
|
+
cwd=cwd,
|
|
143
|
+
api_base=resolved.api_base,
|
|
144
|
+
)
|
|
145
|
+
session = created["session"]
|
|
146
|
+
|
|
147
|
+
if chat.rewind_to is not None:
|
|
148
|
+
if not (0 <= chat.rewind_to < len(messages)):
|
|
149
|
+
raise HTTPException(
|
|
150
|
+
status_code=400,
|
|
151
|
+
detail=f"rewind_to must reference a visible message index between 0 and {len(messages) - 1}",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
target = messages[chat.rewind_to]
|
|
155
|
+
blocks = target.get("content")
|
|
156
|
+
has_user_content = isinstance(blocks, list) and any(
|
|
157
|
+
isinstance(block, dict)
|
|
158
|
+
and ((block.get("type") == "text" and block.get("text")) or block.get("type") == "image")
|
|
159
|
+
for block in blocks
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Rewind only makes sense for real user prompts. Synthetic compact
|
|
163
|
+
# summaries, assistant messages, and tool-result-only user messages are
|
|
164
|
+
# not valid targets.
|
|
165
|
+
if target.get("role") != "user" or (target.get("meta") or {}).get("synthetic") or not has_user_content:
|
|
166
|
+
raise HTTPException(
|
|
167
|
+
status_code=400,
|
|
168
|
+
detail="rewind_to must reference a real user message",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
messages = messages[: chat.rewind_to]
|
|
172
|
+
|
|
173
|
+
agent = Agent(
|
|
174
|
+
model=resolved.model,
|
|
175
|
+
provider=resolved.provider,
|
|
176
|
+
cwd=cwd,
|
|
177
|
+
session_dir=store.session_dir(session_id),
|
|
178
|
+
session_id=session_id,
|
|
179
|
+
api_key=resolved.api_key,
|
|
180
|
+
api_base=resolved.api_base,
|
|
181
|
+
messages=messages,
|
|
182
|
+
settings=settings,
|
|
183
|
+
reasoning_effort=reasoning_effort,
|
|
184
|
+
supports_image_input=resolved.supports_image_input,
|
|
185
|
+
max_tokens=resolved.max_tokens,
|
|
186
|
+
context_window=resolved.context_window,
|
|
187
|
+
compact_threshold=settings.compact_threshold,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
rewind_persisted = False
|
|
191
|
+
|
|
192
|
+
async def on_persist(message: dict) -> None:
|
|
193
|
+
nonlocal rewind_persisted
|
|
194
|
+
if chat.rewind_to is not None and not rewind_persisted:
|
|
195
|
+
await store.append_rewind(session_id, chat.rewind_to)
|
|
196
|
+
rewind_persisted = True
|
|
197
|
+
await store.append_message(
|
|
198
|
+
session_id,
|
|
199
|
+
message,
|
|
200
|
+
provider=resolved.provider,
|
|
201
|
+
model=resolved.model,
|
|
202
|
+
cwd=cwd,
|
|
203
|
+
api_base=resolved.api_base,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
run = await runs.start_run(
|
|
208
|
+
session_id=session_id,
|
|
209
|
+
user_message=user_message,
|
|
210
|
+
base_messages=messages,
|
|
211
|
+
agent=agent,
|
|
212
|
+
on_persist=on_persist,
|
|
213
|
+
)
|
|
214
|
+
except ActiveRunError as exc:
|
|
215
|
+
existing = await runs.get_run(exc.run_id)
|
|
216
|
+
detail: dict[str, Any] = {"message": "session already has a running task"}
|
|
217
|
+
if existing:
|
|
218
|
+
detail["run"] = existing.info()
|
|
219
|
+
raise HTTPException(status_code=409, detail=detail) from exc
|
|
220
|
+
|
|
221
|
+
return {"run": run, "session": session}
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@router.get("/runs/{run_id}/stream")
|
|
225
|
+
async def stream_run(run_id: str, req: Request, runs: RunManagerDep, after: int = 0):
|
|
226
|
+
state = await runs.get_run(run_id)
|
|
227
|
+
if not state:
|
|
228
|
+
raise HTTPException(status_code=404, detail="run not found")
|
|
229
|
+
|
|
230
|
+
return StreamingResponse(
|
|
231
|
+
_stream_run(req, state, after),
|
|
232
|
+
media_type="text/event-stream",
|
|
233
|
+
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@router.post("/runs/{run_id}/cancel")
|
|
238
|
+
async def cancel_run(run_id: str, runs: RunManagerDep):
|
|
239
|
+
run = await runs.cancel_run(run_id)
|
|
240
|
+
if not run:
|
|
241
|
+
raise HTTPException(status_code=404, detail="run not found")
|
|
242
|
+
return {"status": "ok", "run": run}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@router.get("/config")
|
|
246
|
+
async def get_config(cwd: str | None = None):
|
|
247
|
+
resolved_cwd = os.path.abspath(cwd or os.getcwd())
|
|
248
|
+
settings = get_settings(resolved_cwd)
|
|
249
|
+
try:
|
|
250
|
+
resolved = resolve_provider(settings)
|
|
251
|
+
except ValueError as exc:
|
|
252
|
+
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
|
253
|
+
|
|
254
|
+
providers_info: dict[str, Any] = {}
|
|
255
|
+
for provider in resolve_provider_choices(settings):
|
|
256
|
+
provider_config = settings.providers.get(provider.provider_name or "")
|
|
257
|
+
models = (
|
|
258
|
+
list(provider_config.models)
|
|
259
|
+
if provider_config and provider_config.models
|
|
260
|
+
else list(provider_default_models(provider.provider))
|
|
261
|
+
)
|
|
262
|
+
if not models:
|
|
263
|
+
models = [provider.model]
|
|
264
|
+
|
|
265
|
+
info: dict[str, Any] = {
|
|
266
|
+
"name": provider.provider_name,
|
|
267
|
+
"provider": provider.provider,
|
|
268
|
+
"type": provider.provider,
|
|
269
|
+
"models": models,
|
|
270
|
+
"base_url": provider.api_base or "",
|
|
271
|
+
"has_api_key": True,
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
image_models: list[str] = []
|
|
275
|
+
adapter = get_provider_adapter(provider.provider)
|
|
276
|
+
if adapter.supports_reasoning_effort:
|
|
277
|
+
reasoning_models: list[str] = []
|
|
278
|
+
for model in models:
|
|
279
|
+
resolved_model = resolve_provider(
|
|
280
|
+
settings,
|
|
281
|
+
provider_name=provider.provider_name or provider.provider,
|
|
282
|
+
model=model,
|
|
283
|
+
api_base=provider.api_base or None,
|
|
284
|
+
)
|
|
285
|
+
if resolved_model.supports_reasoning is True:
|
|
286
|
+
reasoning_models.append(model)
|
|
287
|
+
if resolved_model.supports_image_input is True:
|
|
288
|
+
image_models.append(model)
|
|
289
|
+
|
|
290
|
+
info["supports_reasoning_effort"] = True
|
|
291
|
+
info["reasoning_models"] = reasoning_models
|
|
292
|
+
info["reasoning_effort"] = provider.reasoning_effort
|
|
293
|
+
else:
|
|
294
|
+
for model in models:
|
|
295
|
+
resolved_model = resolve_provider(
|
|
296
|
+
settings,
|
|
297
|
+
provider_name=provider.provider_name or provider.provider,
|
|
298
|
+
model=model,
|
|
299
|
+
api_base=provider.api_base or None,
|
|
300
|
+
)
|
|
301
|
+
if resolved_model.supports_image_input is True:
|
|
302
|
+
image_models.append(model)
|
|
303
|
+
|
|
304
|
+
info["supports_image_input"] = bool(image_models)
|
|
305
|
+
info["image_input_models"] = image_models
|
|
306
|
+
|
|
307
|
+
providers_info[provider.provider_name or provider.provider] = info
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
"providers": providers_info,
|
|
311
|
+
"default": {
|
|
312
|
+
"provider": resolved.provider_name,
|
|
313
|
+
"model": resolved.model,
|
|
314
|
+
},
|
|
315
|
+
"default_reasoning_effort": settings.default_reasoning_effort,
|
|
316
|
+
"reasoning_effort_options": REASONING_EFFORT_OPTIONS,
|
|
317
|
+
"cwd": resolved_cwd,
|
|
318
|
+
"workspace_root": settings.workspace_root,
|
|
319
|
+
"config_paths": settings.config_paths,
|
|
320
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Session management API endpoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, HTTPException
|
|
8
|
+
|
|
9
|
+
from mycode.core.config import get_settings, resolve_provider
|
|
10
|
+
from mycode.server.deps import RunManagerDep, StoreDep
|
|
11
|
+
from mycode.server.schemas import SessionCreateRequest
|
|
12
|
+
|
|
13
|
+
router = APIRouter(prefix="/sessions", tags=["sessions"])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@router.post("")
|
|
17
|
+
async def create_session(req: SessionCreateRequest, store: StoreDep):
|
|
18
|
+
cwd = os.path.abspath(req.cwd or os.getcwd())
|
|
19
|
+
settings = get_settings(cwd)
|
|
20
|
+
resolved = resolve_provider(settings, provider_name=req.provider, model=req.model, api_base=req.api_base)
|
|
21
|
+
return await store.create_session(
|
|
22
|
+
req.title,
|
|
23
|
+
provider=resolved.provider,
|
|
24
|
+
model=resolved.model,
|
|
25
|
+
cwd=cwd,
|
|
26
|
+
api_base=resolved.api_base,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@router.get("")
|
|
31
|
+
async def list_sessions(store: StoreDep, runs: RunManagerDep, cwd: str | None = None):
|
|
32
|
+
sessions = await store.list_sessions(cwd=cwd)
|
|
33
|
+
for session in sessions:
|
|
34
|
+
session["is_running"] = await runs.has_active_run(session.get("id", ""))
|
|
35
|
+
return {"sessions": sessions}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@router.get("/{session_id}")
|
|
39
|
+
async def load_session(session_id: str, store: StoreDep, runs: RunManagerDep):
|
|
40
|
+
"""Load a session, overlaying any active in-memory run state."""
|
|
41
|
+
|
|
42
|
+
data = await store.load_session(session_id)
|
|
43
|
+
session = data.get("session") if data else None
|
|
44
|
+
active = await runs.snapshot_session(session_id)
|
|
45
|
+
if active:
|
|
46
|
+
return {
|
|
47
|
+
"session": session,
|
|
48
|
+
"messages": active["messages"],
|
|
49
|
+
"active_run": active["run"],
|
|
50
|
+
"pending_events": active["pending_events"],
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if not data:
|
|
54
|
+
return {"session": None, "messages": [], "active_run": None, "pending_events": []}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
"session": session,
|
|
58
|
+
"messages": data.get("messages") or [],
|
|
59
|
+
"active_run": None,
|
|
60
|
+
"pending_events": [],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@router.delete("/{session_id}")
|
|
65
|
+
async def delete_session(session_id: str, store: StoreDep, runs: RunManagerDep):
|
|
66
|
+
if await runs.has_active_run(session_id):
|
|
67
|
+
raise HTTPException(status_code=409, detail="session has a running task")
|
|
68
|
+
await store.delete_session(session_id)
|
|
69
|
+
return {"status": "ok"}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@router.post("/{session_id}/clear")
|
|
73
|
+
async def clear_session(session_id: str, store: StoreDep, runs: RunManagerDep):
|
|
74
|
+
if await runs.has_active_run(session_id):
|
|
75
|
+
raise HTTPException(status_code=409, detail="session has a running task")
|
|
76
|
+
await store.clear_session(session_id)
|
|
77
|
+
return {"status": "ok"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Workspace browsing API endpoints."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from fastapi import APIRouter
|
|
7
|
+
|
|
8
|
+
router = APIRouter(prefix="/workspaces", tags=["workspaces"])
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _parse_workspace_roots() -> list[Path]:
|
|
12
|
+
"""Parse allowed workspace roots from environment variables."""
|
|
13
|
+
raw = os.environ.get("MYCODE_WORKSPACE_ROOTS") or os.environ.get("WORKSPACE_ROOTS")
|
|
14
|
+
if raw:
|
|
15
|
+
candidates = [item.strip() for item in raw.split(",") if item.strip()]
|
|
16
|
+
else:
|
|
17
|
+
candidates = [str(Path.home()), os.sep]
|
|
18
|
+
|
|
19
|
+
roots: list[Path] = []
|
|
20
|
+
seen: set[str] = set()
|
|
21
|
+
for value in candidates:
|
|
22
|
+
root = Path(value).expanduser().resolve(strict=False)
|
|
23
|
+
if not root.exists():
|
|
24
|
+
continue
|
|
25
|
+
key = str(root)
|
|
26
|
+
if key not in seen:
|
|
27
|
+
seen.add(key)
|
|
28
|
+
roots.append(root)
|
|
29
|
+
|
|
30
|
+
return roots or [Path(os.getcwd()).resolve(strict=False)]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@router.get("/roots")
|
|
34
|
+
async def list_workspace_roots():
|
|
35
|
+
"""List workspace roots for browsing."""
|
|
36
|
+
return {"roots": [str(root) for root in _parse_workspace_roots()]}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@router.get("/browse")
|
|
40
|
+
async def browse_workspaces(root: str, path: str | None = None):
|
|
41
|
+
"""Browse directories within a workspace root."""
|
|
42
|
+
root_path = None
|
|
43
|
+
requested_root = Path(root).expanduser().resolve(strict=False)
|
|
44
|
+
for allowed_root in _parse_workspace_roots():
|
|
45
|
+
if requested_root == allowed_root:
|
|
46
|
+
root_path = allowed_root
|
|
47
|
+
break
|
|
48
|
+
|
|
49
|
+
if not root_path:
|
|
50
|
+
return {"root": root, "path": "", "current": "", "entries": [], "error": "Invalid root"}
|
|
51
|
+
|
|
52
|
+
rel_path = Path(path) if path else Path()
|
|
53
|
+
target = (root_path / rel_path).resolve(strict=False)
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
target.relative_to(root_path)
|
|
57
|
+
except ValueError:
|
|
58
|
+
return {
|
|
59
|
+
"root": str(root_path),
|
|
60
|
+
"path": "",
|
|
61
|
+
"current": str(root_path),
|
|
62
|
+
"entries": [],
|
|
63
|
+
"error": "Path outside root",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
entries: list[dict[str, str]] = []
|
|
68
|
+
for entry in sorted(target.iterdir(), key=lambda item: item.name.lower()):
|
|
69
|
+
try:
|
|
70
|
+
if entry.name.startswith("."):
|
|
71
|
+
continue
|
|
72
|
+
if entry.is_dir():
|
|
73
|
+
entries.append({"name": entry.name, "path": entry.relative_to(root_path).as_posix()})
|
|
74
|
+
except OSError:
|
|
75
|
+
continue
|
|
76
|
+
except OSError as exc:
|
|
77
|
+
return {
|
|
78
|
+
"root": str(root_path),
|
|
79
|
+
"path": "",
|
|
80
|
+
"current": str(root_path),
|
|
81
|
+
"entries": [],
|
|
82
|
+
"error": str(exc),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
current_path = "" if target == root_path else target.relative_to(root_path).as_posix()
|
|
86
|
+
return {"root": str(root_path), "path": current_path, "current": str(target), "entries": entries, "error": ""}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@router.get("/cwd")
|
|
90
|
+
async def get_cwd():
|
|
91
|
+
"""Get current working directory."""
|
|
92
|
+
return {"cwd": os.getcwd(), "exists": Path(os.getcwd()).exists()}
|