miko-code 0.1.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/LICENSE +21 -0
- package/README.md +179 -0
- package/bin/miko +10 -0
- package/dist/client/antigravity.webp +0 -0
- package/dist/client/assets/abap-BdImnpbu.js +1 -0
- package/dist/client/assets/actionscript-3-CoDkCxhg.js +1 -0
- package/dist/client/assets/ada-bCR0ucgS.js +1 -0
- package/dist/client/assets/andromeeda-C4gqWexZ.js +1 -0
- package/dist/client/assets/angular-html-CU67Zn6k.js +1 -0
- package/dist/client/assets/angular-ts-BwZT4LLn.js +1 -0
- package/dist/client/assets/apache-Pmp26Uib.js +1 -0
- package/dist/client/assets/apex-D8_7TLub.js +1 -0
- package/dist/client/assets/apl-dKokRX4l.js +1 -0
- package/dist/client/assets/applescript-Co6uUVPk.js +1 -0
- package/dist/client/assets/ara-BRHolxvo.js +1 -0
- package/dist/client/assets/asciidoc-Ve4PFQV2.js +1 -0
- package/dist/client/assets/asm-D_Q5rh1f.js +1 -0
- package/dist/client/assets/astro-CbQHKStN.js +1 -0
- package/dist/client/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/dist/client/assets/awk-DMzUqQB5.js +1 -0
- package/dist/client/assets/ayu-dark-DYE7WIF3.js +1 -0
- package/dist/client/assets/ayu-light-BA47KaF1.js +1 -0
- package/dist/client/assets/ayu-mirage-32ctXXKs.js +1 -0
- package/dist/client/assets/ballerina-BFfxhgS-.js +1 -0
- package/dist/client/assets/bat-BkioyH1T.js +1 -0
- package/dist/client/assets/beancount-k_qm7-4y.js +1 -0
- package/dist/client/assets/berry-uYugtg8r.js +1 -0
- package/dist/client/assets/bibtex-CHM0blh-.js +1 -0
- package/dist/client/assets/bicep-Bmn6On1c.js +1 -0
- package/dist/client/assets/bird2-DPOp833l.js +1 -0
- package/dist/client/assets/blade-D4QpJJKB.js +1 -0
- package/dist/client/assets/bsl-BO_Y6i37.js +1 -0
- package/dist/client/assets/c-BIGW1oBm.js +1 -0
- package/dist/client/assets/c3-eo99z4R2.js +1 -0
- package/dist/client/assets/cadence-Bv_4Rxtq.js +1 -0
- package/dist/client/assets/cairo-KRGpt6FW.js +1 -0
- package/dist/client/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- package/dist/client/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- package/dist/client/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- package/dist/client/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- package/dist/client/assets/clarity-D53aC0YG.js +1 -0
- package/dist/client/assets/clojure-P80f7IUj.js +1 -0
- package/dist/client/assets/cmake-D1j8_8rp.js +1 -0
- package/dist/client/assets/cobol-nwyudZeR.js +1 -0
- package/dist/client/assets/codeowners-Bp6g37R7.js +1 -0
- package/dist/client/assets/codeql-DsOJ9woJ.js +1 -0
- package/dist/client/assets/coffee-Ch7k5sss.js +1 -0
- package/dist/client/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/dist/client/assets/coq-DkFqJrB1.js +1 -0
- package/dist/client/assets/cpp-CofmeUqb.js +1 -0
- package/dist/client/assets/crystal-tKQVLTB8.js +1 -0
- package/dist/client/assets/csharp-COcwbKMJ.js +1 -0
- package/dist/client/assets/css-DPfMkruS.js +1 -0
- package/dist/client/assets/csv-fuZLfV_i.js +1 -0
- package/dist/client/assets/cue-D82EKSYY.js +1 -0
- package/dist/client/assets/cypher-COkxafJQ.js +1 -0
- package/dist/client/assets/d-85-TOEBH.js +1 -0
- package/dist/client/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/client/assets/dart-CF10PKvl.js +1 -0
- package/dist/client/assets/dax-CEL-wOlO.js +1 -0
- package/dist/client/assets/desktop-BmXAJ9_W.js +1 -0
- package/dist/client/assets/diff-D97Zzqfu.js +1 -0
- package/dist/client/assets/docker-BcOcwvcX.js +1 -0
- package/dist/client/assets/dotenv-Da5cRb03.js +1 -0
- package/dist/client/assets/dracula-BzJJZx-M.js +1 -0
- package/dist/client/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/dist/client/assets/dream-maker-BtqSS_iP.js +1 -0
- package/dist/client/assets/edge-BkV0erSs.js +1 -0
- package/dist/client/assets/elixir-CDX3lj18.js +1 -0
- package/dist/client/assets/elm-DbKCFpqz.js +1 -0
- package/dist/client/assets/emacs-lisp-C9XAeP06.js +1 -0
- package/dist/client/assets/erb-B12qg9BL.js +1 -0
- package/dist/client/assets/erlang-DsQrWhSR.js +1 -0
- package/dist/client/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/dist/client/assets/everforest-light-C8M2exoo.js +1 -0
- package/dist/client/assets/fennel-BYunw83y.js +1 -0
- package/dist/client/assets/fish-BvzEVeQv.js +1 -0
- package/dist/client/assets/fluent-C4IJs8-o.js +1 -0
- package/dist/client/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- package/dist/client/assets/fortran-free-form-BxgE0vQu.js +1 -0
- package/dist/client/assets/fsharp-CXgrBDvD.js +1 -0
- package/dist/client/assets/gdresource-BOOCDP_w.js +1 -0
- package/dist/client/assets/gdscript-C5YyOfLZ.js +1 -0
- package/dist/client/assets/gdshader-DkwncUOv.js +1 -0
- package/dist/client/assets/genie-D0YGMca9.js +1 -0
- package/dist/client/assets/gherkin-DyxjwDmM.js +1 -0
- package/dist/client/assets/git-commit-F4YmCXRG.js +1 -0
- package/dist/client/assets/git-rebase-r7XF79zn.js +1 -0
- package/dist/client/assets/github-dark-DHJKELXO.js +1 -0
- package/dist/client/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/dist/client/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/dist/client/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/dist/client/assets/github-light-DAi9KRSo.js +1 -0
- package/dist/client/assets/github-light-default-D7oLnXFd.js +1 -0
- package/dist/client/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/dist/client/assets/gleam-BspZqrRM.js +1 -0
- package/dist/client/assets/glimmer-js-Rg0-pVw9.js +1 -0
- package/dist/client/assets/glimmer-ts-U6CK756n.js +1 -0
- package/dist/client/assets/glsl-DplSGwfg.js +1 -0
- package/dist/client/assets/gn-n2N0HUVH.js +1 -0
- package/dist/client/assets/gnuplot-DdkO51Og.js +1 -0
- package/dist/client/assets/go-CxLEBnE3.js +1 -0
- package/dist/client/assets/graphql-ChdNCCLP.js +1 -0
- package/dist/client/assets/groovy-gcz8RCvz.js +1 -0
- package/dist/client/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/dist/client/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/dist/client/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/dist/client/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/dist/client/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/dist/client/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/dist/client/assets/hack-CaT9iCJl.js +1 -0
- package/dist/client/assets/haml-B8DHNrY2.js +1 -0
- package/dist/client/assets/handlebars-BL8al0AC.js +1 -0
- package/dist/client/assets/haskell-Df6bDoY_.js +1 -0
- package/dist/client/assets/haxe-CzTSHFRz.js +1 -0
- package/dist/client/assets/hcl-BWvSN4gD.js +1 -0
- package/dist/client/assets/hjson-D5-asLiD.js +1 -0
- package/dist/client/assets/hlsl-D3lLCCz7.js +1 -0
- package/dist/client/assets/horizon-BUw7H-hv.js +1 -0
- package/dist/client/assets/horizon-bright-Cn-bp-IR.js +1 -0
- package/dist/client/assets/houston-DnULxvSX.js +1 -0
- package/dist/client/assets/html-GMplVEZG.js +1 -0
- package/dist/client/assets/html-derivative-BFtXZ54Q.js +1 -0
- package/dist/client/assets/http-jrhK8wxY.js +1 -0
- package/dist/client/assets/hurl-irOxFIW8.js +1 -0
- package/dist/client/assets/hxml-Bvhsp5Yf.js +1 -0
- package/dist/client/assets/hy-DFXneXwc.js +1 -0
- package/dist/client/assets/imba-DGztddWO.js +1 -0
- package/dist/client/assets/index-C07zYq_-.css +32 -0
- package/dist/client/assets/index-Ce3hNHfL.js +2351 -0
- package/dist/client/assets/ini-BEwlwnbL.js +1 -0
- package/dist/client/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/client/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/client/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/client/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/client/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/client/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/client/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/client/assets/java-CylS5w8V.js +1 -0
- package/dist/client/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/client/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/client/assets/jinja-4LBKfQ-Z.js +1 -0
- package/dist/client/assets/jison-wvAkD_A8.js +1 -0
- package/dist/client/assets/json-Cp-IABpG.js +1 -0
- package/dist/client/assets/json5-C9tS-k6U.js +1 -0
- package/dist/client/assets/jsonc-Des-eS-w.js +1 -0
- package/dist/client/assets/jsonl-DcaNXYhu.js +1 -0
- package/dist/client/assets/jsonnet-DFQXde-d.js +1 -0
- package/dist/client/assets/jssm-C2t-YnRu.js +1 -0
- package/dist/client/assets/jsx-g9-lgVsj.js +1 -0
- package/dist/client/assets/julia-CxzCAyBv.js +1 -0
- package/dist/client/assets/just-Cw27pwNe.js +1 -0
- package/dist/client/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- package/dist/client/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- package/dist/client/assets/kanagawa-wave-DWedfzmr.js +1 -0
- package/dist/client/assets/kdl-DV7GczEv.js +1 -0
- package/dist/client/assets/kotlin-BdnUsdx6.js +1 -0
- package/dist/client/assets/kusto-DZf3V79B.js +1 -0
- package/dist/client/assets/laserwave-DUszq2jm.js +1 -0
- package/dist/client/assets/latex-CWtU0Tv5.js +1 -0
- package/dist/client/assets/lean-BZvkOJ9d.js +1 -0
- package/dist/client/assets/less-B1dDrJ26.js +1 -0
- package/dist/client/assets/light-plus-B7mTdjB0.js +1 -0
- package/dist/client/assets/liquid-DYVedYrR.js +1 -0
- package/dist/client/assets/llvm-DjAJT7YJ.js +1 -0
- package/dist/client/assets/log-2UxHyX5q.js +1 -0
- package/dist/client/assets/logo-BtOb2qkB.js +1 -0
- package/dist/client/assets/lua-BaeVxFsk.js +1 -0
- package/dist/client/assets/luau-C-HG3fhB.js +1 -0
- package/dist/client/assets/make-CHLpvVh8.js +1 -0
- package/dist/client/assets/markdown-Cvjx9yec.js +1 -0
- package/dist/client/assets/marko-CnJfTvn9.js +1 -0
- package/dist/client/assets/material-theme-D5KoaKCx.js +1 -0
- package/dist/client/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/dist/client/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/dist/client/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/dist/client/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/dist/client/assets/matlab-D7o27uSR.js +1 -0
- package/dist/client/assets/mdc-BMNejdWA.js +1 -0
- package/dist/client/assets/mdx-Cmh6b_Ma.js +1 -0
- package/dist/client/assets/mermaid-mWjccvbQ.js +1 -0
- package/dist/client/assets/min-dark-CafNBF8u.js +1 -0
- package/dist/client/assets/min-light-CTRr51gU.js +1 -0
- package/dist/client/assets/mipsasm-CKIfxQSi.js +1 -0
- package/dist/client/assets/mojo-rZm6bMo-.js +1 -0
- package/dist/client/assets/monokai-D4h5O-jR.js +1 -0
- package/dist/client/assets/moonbit-_H4v1dQx.js +1 -0
- package/dist/client/assets/move-IF9eRakj.js +1 -0
- package/dist/client/assets/narrat-DRg8JJMk.js +1 -0
- package/dist/client/assets/nextflow-Zz6hmt5N.js +1 -0
- package/dist/client/assets/nextflow-groovy-BeH2EWoN.js +1 -0
- package/dist/client/assets/nginx-BpAMiNFr.js +1 -0
- package/dist/client/assets/night-owl-C39BiMTA.js +1 -0
- package/dist/client/assets/night-owl-light-CMTm3GFP.js +1 -0
- package/dist/client/assets/nim-CVrawwO9.js +1 -0
- package/dist/client/assets/nix-CwoSXNpI.js +1 -0
- package/dist/client/assets/nord-Ddv68eIx.js +1 -0
- package/dist/client/assets/nushell-Cz2AlsmD.js +1 -0
- package/dist/client/assets/objective-c-DXmwc3jG.js +1 -0
- package/dist/client/assets/objective-cpp-CLxacb5B.js +1 -0
- package/dist/client/assets/ocaml-C0hk2d4L.js +1 -0
- package/dist/client/assets/odin-BBf5iR-q.js +1 -0
- package/dist/client/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/dist/client/assets/one-light-C3Wv6jpd.js +1 -0
- package/dist/client/assets/openscad-C4EeE6gA.js +1 -0
- package/dist/client/assets/pascal-D93ZcfNL.js +1 -0
- package/dist/client/assets/perl-C0TMdlhV.js +1 -0
- package/dist/client/assets/php-Dhbhpdrm.js +1 -0
- package/dist/client/assets/pierre-dark-DF2SEV7i.js +1 -0
- package/dist/client/assets/pierre-light-DOlZxES8.js +1 -0
- package/dist/client/assets/pkl-u5AG7uiY.js +1 -0
- package/dist/client/assets/plastic-3e1v2bzS.js +1 -0
- package/dist/client/assets/plsql-ChMvpjG-.js +1 -0
- package/dist/client/assets/po-BTJTHyun.js +1 -0
- package/dist/client/assets/poimandres-CS3Unz2-.js +1 -0
- package/dist/client/assets/polar-C0HS_06l.js +1 -0
- package/dist/client/assets/postcss-CXtECtnM.js +1 -0
- package/dist/client/assets/powerquery-CEu0bR-o.js +1 -0
- package/dist/client/assets/powershell-Dpen1YoG.js +1 -0
- package/dist/client/assets/prisma-Dd19v3D-.js +1 -0
- package/dist/client/assets/prolog-CbFg5uaA.js +1 -0
- package/dist/client/assets/proto-C7zT0LnQ.js +1 -0
- package/dist/client/assets/pug-CGlum2m_.js +1 -0
- package/dist/client/assets/puppet-BMWR74SV.js +1 -0
- package/dist/client/assets/purescript-CklMAg4u.js +1 -0
- package/dist/client/assets/python-B6aJPvgy.js +1 -0
- package/dist/client/assets/qml-3beO22l8.js +1 -0
- package/dist/client/assets/qmldir-C8lEn-DE.js +1 -0
- package/dist/client/assets/qss-IeuSbFQv.js +1 -0
- package/dist/client/assets/r-Dspwwk_N.js +1 -0
- package/dist/client/assets/racket-BqYA7rlc.js +1 -0
- package/dist/client/assets/raku-DXvB9xmW.js +1 -0
- package/dist/client/assets/razor-Uh8Bk_45.js +1 -0
- package/dist/client/assets/red-bN70gL4F.js +1 -0
- package/dist/client/assets/reg-C-SQnVFl.js +1 -0
- package/dist/client/assets/regexp-CDVJQ6XC.js +1 -0
- package/dist/client/assets/rel-C3B-1QV4.js +1 -0
- package/dist/client/assets/riscv-BM1_JUlF.js +1 -0
- package/dist/client/assets/ron-D8l8udqQ.js +1 -0
- package/dist/client/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- package/dist/client/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- package/dist/client/assets/rose-pine-qdsjHGoJ.js +1 -0
- package/dist/client/assets/rosmsg-BJDFO7_C.js +1 -0
- package/dist/client/assets/rst-BrH8l1NY.js +1 -0
- package/dist/client/assets/ruby-Dw2BHqvy.js +1 -0
- package/dist/client/assets/rust-B1yitclQ.js +1 -0
- package/dist/client/assets/sas-cz2c8ADy.js +1 -0
- package/dist/client/assets/sass-Cj5Yp3dK.js +1 -0
- package/dist/client/assets/scala-C151Ov-r.js +1 -0
- package/dist/client/assets/scheme-C98Dy4si.js +1 -0
- package/dist/client/assets/scratchpad-page-DcMny6uZ.js +1 -0
- package/dist/client/assets/scss-OYdSNvt2.js +1 -0
- package/dist/client/assets/sdbl-DVxCFoDh.js +1 -0
- package/dist/client/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/dist/client/assets/shellscript-Yzrsuije.js +1 -0
- package/dist/client/assets/shellsession-BADoaaVG.js +1 -0
- package/dist/client/assets/slack-dark-BthQWCQV.js +1 -0
- package/dist/client/assets/slack-ochin-DqwNpetd.js +1 -0
- package/dist/client/assets/smalltalk-BERRCDM3.js +1 -0
- package/dist/client/assets/snazzy-light-Bw305WKR.js +1 -0
- package/dist/client/assets/solarized-dark-DXbdFlpD.js +1 -0
- package/dist/client/assets/solarized-light-L9t79GZl.js +1 -0
- package/dist/client/assets/solidity-rGO070M0.js +1 -0
- package/dist/client/assets/soy-Brmx7dQM.js +1 -0
- package/dist/client/assets/sparql-rVzFXLq3.js +1 -0
- package/dist/client/assets/splunk-BtCnVYZw.js +1 -0
- package/dist/client/assets/sql-BLtJtn59.js +1 -0
- package/dist/client/assets/ssh-config-_ykCGR6B.js +1 -0
- package/dist/client/assets/stata-BH5u7GGu.js +1 -0
- package/dist/client/assets/stylus-BEDo0Tqx.js +1 -0
- package/dist/client/assets/surrealql-Bq5Q-fJD.js +1 -0
- package/dist/client/assets/svelte-C_ipcX3V.js +1 -0
- package/dist/client/assets/swift-D82vCrfD.js +1 -0
- package/dist/client/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/dist/client/assets/system-verilog-CnnmHF94.js +1 -0
- package/dist/client/assets/systemd-4A_iFExJ.js +1 -0
- package/dist/client/assets/talonscript-CkByrt1z.js +1 -0
- package/dist/client/assets/tasl-QIJgUcNo.js +1 -0
- package/dist/client/assets/tcl-dwOrl1Do.js +1 -0
- package/dist/client/assets/templ-P3uqSqPl.js +1 -0
- package/dist/client/assets/terraform-BETggiCN.js +1 -0
- package/dist/client/assets/tex-idrVyKtj.js +1 -0
- package/dist/client/assets/tokyo-night-hegEt444.js +1 -0
- package/dist/client/assets/toml-vGWfd6FD.js +1 -0
- package/dist/client/assets/ts-tags-zn1MmPIZ.js +1 -0
- package/dist/client/assets/tsv-B_m7g4N7.js +1 -0
- package/dist/client/assets/tsx-COt5Ahok.js +1 -0
- package/dist/client/assets/turtle-BsS91CYL.js +1 -0
- package/dist/client/assets/twig-DNn4PbVi.js +1 -0
- package/dist/client/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/client/assets/typespec-BGHnOYBU.js +1 -0
- package/dist/client/assets/typst-DHCkPAjA.js +1 -0
- package/dist/client/assets/v-BcVCzyr7.js +1 -0
- package/dist/client/assets/vala-CsfeWuGM.js +1 -0
- package/dist/client/assets/vb-D17OF-Vu.js +1 -0
- package/dist/client/assets/verilog-BQ8w6xss.js +1 -0
- package/dist/client/assets/vesper-DU1UobuO.js +1 -0
- package/dist/client/assets/vhdl-CeAyd5Ju.js +1 -0
- package/dist/client/assets/viml-CJc9bBzg.js +1 -0
- package/dist/client/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/dist/client/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/dist/client/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/dist/client/assets/vue-DN_0RTcg.js +1 -0
- package/dist/client/assets/vue-html-AaS7Mt5G.js +1 -0
- package/dist/client/assets/vue-vine-CQOfvN7w.js +1 -0
- package/dist/client/assets/vyper-CDx5xZoG.js +1 -0
- package/dist/client/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/client/assets/wasm-MzD3tlZU.js +1 -0
- package/dist/client/assets/wenyan-BV7otONQ.js +1 -0
- package/dist/client/assets/wgsl-Dx-B1_4e.js +1 -0
- package/dist/client/assets/wikitext-BhOHFoWU.js +1 -0
- package/dist/client/assets/wit-5i3qLPDT.js +1 -0
- package/dist/client/assets/wolfram-lXgVvXCa.js +1 -0
- package/dist/client/assets/xml-sdJ4AIDG.js +1 -0
- package/dist/client/assets/xsl-CtQFsRM5.js +1 -0
- package/dist/client/assets/yaml-Buea-lGh.js +1 -0
- package/dist/client/assets/zenscript-DVFEvuxE.js +1 -0
- package/dist/client/assets/zig-VOosw3JB.js +1 -0
- package/dist/client/cursor.png +0 -0
- package/dist/client/favicon.svg +17 -0
- package/dist/client/finder.png +0 -0
- package/dist/client/icons/claude.svg +1 -0
- package/dist/client/icons/openai.svg +1 -0
- package/dist/client/images/github.png +0 -0
- package/dist/client/index.html +15 -0
- package/dist/client/logo.svg +17 -0
- package/dist/client/terminal.png +0 -0
- package/dist/client/vscode.png +0 -0
- package/dist/client/warp.png +0 -0
- package/package.json +107 -0
- package/src/server/agent-instruction-attachments.ts +458 -0
- package/src/server/agent.ts +1879 -0
- package/src/server/cli-runtime.ts +418 -0
- package/src/server/cli-supervisor.ts +90 -0
- package/src/server/cli.ts +102 -0
- package/src/server/codex-app-server-protocol.ts +478 -0
- package/src/server/codex-app-server.ts +1645 -0
- package/src/server/data-dir-lock.ts +128 -0
- package/src/server/diff-store.ts +1587 -0
- package/src/server/durable-file.ts +74 -0
- package/src/server/event-store.ts +1448 -0
- package/src/server/event.ts +249 -0
- package/src/server/external-file-access.ts +48 -0
- package/src/server/external-open.ts +259 -0
- package/src/server/generate-title.ts +75 -0
- package/src/server/git-refresh-poller.ts +92 -0
- package/src/server/github-rest-client.ts +176 -0
- package/src/server/harness-types.ts +24 -0
- package/src/server/keybindings.ts +203 -0
- package/src/server/machine-name.ts +22 -0
- package/src/server/paths.ts +51 -0
- package/src/server/pr-manager.ts +1204 -0
- package/src/server/pr-refresh-poller.ts +126 -0
- package/src/server/process-utils.ts +18 -0
- package/src/server/provider-catalog.ts +90 -0
- package/src/server/quick-response.ts +274 -0
- package/src/server/read-models.ts +311 -0
- package/src/server/restart.ts +33 -0
- package/src/server/scratchpad-manager.ts +87 -0
- package/src/server/server.ts +759 -0
- package/src/server/share.ts +126 -0
- package/src/server/terminal-manager.ts +371 -0
- package/src/server/update-manager.ts +250 -0
- package/src/server/uploads.ts +191 -0
- package/src/server/workspace-file-search.ts +191 -0
- package/src/server/workspace-manager.ts +627 -0
- package/src/server/workspace-polling.ts +10 -0
- package/src/server/ws-router.ts +1039 -0
- package/src/shared/branding.ts +69 -0
- package/src/shared/dev-ports.ts +100 -0
- package/src/shared/ports.ts +2 -0
- package/src/shared/protocol.ts +217 -0
- package/src/shared/tools.ts +324 -0
- package/src/shared/types.ts +1220 -0
- package/tsconfig.json +35 -0
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { APP_NAME, getRuntimeProfile } from '../shared/branding';
|
|
4
|
+
import type { ChatAttachment } from '../shared/types';
|
|
5
|
+
import { AgentCoordinator } from './agent';
|
|
6
|
+
import {
|
|
7
|
+
cleanupStaleInstructionAttachments,
|
|
8
|
+
getAgentInstructionFilePath,
|
|
9
|
+
} from './agent-instruction-attachments';
|
|
10
|
+
import type { UpdateInstallAttemptResult } from './cli-runtime';
|
|
11
|
+
import { DiffStore } from './diff-store';
|
|
12
|
+
import type { WorkspaceRecord } from './event';
|
|
13
|
+
import { EventStore } from './event-store';
|
|
14
|
+
import { resolveExternalFileAccessToken } from './external-file-access';
|
|
15
|
+
import { GitRefreshPoller } from './git-refresh-poller';
|
|
16
|
+
import { KeybindingsManager } from './keybindings';
|
|
17
|
+
import { getMachineDisplayName } from './machine-name';
|
|
18
|
+
import { getWorkspaceUploadDir } from './paths';
|
|
19
|
+
import { PrManager } from './pr-manager';
|
|
20
|
+
import { PrRefreshPoller } from './pr-refresh-poller';
|
|
21
|
+
import { ScratchpadManager } from './scratchpad-manager';
|
|
22
|
+
import { TerminalManager } from './terminal-manager';
|
|
23
|
+
import { UpdateManager } from './update-manager';
|
|
24
|
+
import {
|
|
25
|
+
deleteWorkspaceUpload,
|
|
26
|
+
inferAttachmentContentType,
|
|
27
|
+
inferWorkspaceFileContentType,
|
|
28
|
+
persistWorkspaceUpload,
|
|
29
|
+
} from './uploads';
|
|
30
|
+
import { WorkspaceManager } from './workspace-manager';
|
|
31
|
+
import { type ClientState, createWsRouter } from './ws-router';
|
|
32
|
+
|
|
33
|
+
const MAX_UPLOAD_FILES = 50;
|
|
34
|
+
const MAX_UPLOAD_SIZE_BYTES = 100 * 1024 * 1024;
|
|
35
|
+
const MAX_WORKSPACE_FILE_CONTENT_BYTES = 2 * 1024 * 1024;
|
|
36
|
+
const MAX_AGENT_INSTRUCTION_CONTENT_BYTES = MAX_WORKSPACE_FILE_CONTENT_BYTES;
|
|
37
|
+
|
|
38
|
+
function safeDecodePathSegment(segment: string): string | null {
|
|
39
|
+
try {
|
|
40
|
+
return decodeURIComponent(segment);
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function shouldRefreshWorkspaceGitOnStartup(workspace: WorkspaceRecord): boolean {
|
|
47
|
+
return workspace.visibilityState === 'active' && workspace.setupState === 'ready';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function shouldRefreshWorkspacePrOnStartup(workspace: WorkspaceRecord): boolean {
|
|
51
|
+
return (
|
|
52
|
+
workspace.visibilityState === 'active' &&
|
|
53
|
+
workspace.reviewState !== 'done' &&
|
|
54
|
+
workspace.reviewState !== 'closed'
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface StartupWorkspaceRefreshDeps {
|
|
59
|
+
listWorkspaces: () => WorkspaceRecord[];
|
|
60
|
+
refreshWorkspaceGitSnapshot: (workspaceId: string, localPath: string) => Promise<boolean>;
|
|
61
|
+
refreshWorkspacePrStage: (
|
|
62
|
+
workspaceId: string,
|
|
63
|
+
options?: { force?: boolean },
|
|
64
|
+
) => Promise<{ refreshed: boolean }>;
|
|
65
|
+
broadcastSnapshots: () => Promise<void>;
|
|
66
|
+
logger?: Pick<Console, 'warn'>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Refreshes git/PR state for active workspaces after startup. Intended to run in the background
|
|
71
|
+
* once the server is reachable so the sidebar can render cached state immediately. Each workspace
|
|
72
|
+
* is isolated in its own try/catch so one bad workspace cannot stop refresh for the rest.
|
|
73
|
+
*/
|
|
74
|
+
export async function refreshStartupWorkspaceState(
|
|
75
|
+
deps: StartupWorkspaceRefreshDeps,
|
|
76
|
+
): Promise<void> {
|
|
77
|
+
const logger = deps.logger ?? console;
|
|
78
|
+
|
|
79
|
+
for (const workspace of deps.listWorkspaces()) {
|
|
80
|
+
if (!shouldRefreshWorkspaceGitOnStartup(workspace)) continue;
|
|
81
|
+
try {
|
|
82
|
+
const changed = await deps.refreshWorkspaceGitSnapshot(workspace.id, workspace.localPath);
|
|
83
|
+
if (changed) await deps.broadcastSnapshots();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
logger.warn('[miko] failed to refresh startup workspace git state', {
|
|
86
|
+
workspaceId: workspace.id,
|
|
87
|
+
error,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const workspace of deps.listWorkspaces()) {
|
|
93
|
+
if (!shouldRefreshWorkspacePrOnStartup(workspace)) continue;
|
|
94
|
+
try {
|
|
95
|
+
const result = await deps.refreshWorkspacePrStage(workspace.id, { force: true });
|
|
96
|
+
if (result.refreshed) await deps.broadcastSnapshots();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
logger.warn('[miko] failed to refresh startup workspace PR state', {
|
|
99
|
+
workspaceId: workspace.id,
|
|
100
|
+
error,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function persistUploadedFiles(args: {
|
|
107
|
+
workspaceId: string;
|
|
108
|
+
dataDir?: string;
|
|
109
|
+
files: File[];
|
|
110
|
+
persistUpload?: typeof persistWorkspaceUpload;
|
|
111
|
+
}): Promise<ChatAttachment[]> {
|
|
112
|
+
const persistUpload = args.persistUpload ?? persistWorkspaceUpload;
|
|
113
|
+
const attachments: ChatAttachment[] = [];
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
for (const file of args.files) {
|
|
117
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
118
|
+
const attachment = await persistUpload({
|
|
119
|
+
workspaceId: args.workspaceId,
|
|
120
|
+
dataDir: args.dataDir,
|
|
121
|
+
fileName: file.name,
|
|
122
|
+
bytes,
|
|
123
|
+
fallbackMimeType: file.type || undefined,
|
|
124
|
+
});
|
|
125
|
+
attachments.push(attachment);
|
|
126
|
+
}
|
|
127
|
+
} catch (error) {
|
|
128
|
+
await Promise.allSettled(
|
|
129
|
+
attachments.map((attachment) =>
|
|
130
|
+
deleteWorkspaceUpload({
|
|
131
|
+
workspaceId: args.workspaceId,
|
|
132
|
+
dataDir: args.dataDir,
|
|
133
|
+
storedName: path.basename(attachment.absolutePath),
|
|
134
|
+
}),
|
|
135
|
+
),
|
|
136
|
+
);
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return attachments;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface StartServerOptions {
|
|
144
|
+
port?: number;
|
|
145
|
+
host?: string;
|
|
146
|
+
strictPort?: boolean;
|
|
147
|
+
onMigrationProgress?: (message: string) => void;
|
|
148
|
+
update?: {
|
|
149
|
+
version: string;
|
|
150
|
+
fetchLatestVersion: (packageName: string) => Promise<string>;
|
|
151
|
+
installVersion: (packageName: string, version: string) => UpdateInstallAttemptResult;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function startServer(options: StartServerOptions = {}) {
|
|
156
|
+
const port = options.port ?? 3210;
|
|
157
|
+
const hostname = options.host ?? '127.0.0.1';
|
|
158
|
+
const strictPort = options.strictPort ?? false;
|
|
159
|
+
|
|
160
|
+
const store = new EventStore(undefined, { lockDataDir: true });
|
|
161
|
+
const diffStore = new DiffStore(store.dataDir);
|
|
162
|
+
const prManager = new PrManager(store);
|
|
163
|
+
const scratchpadManager = new ScratchpadManager(store.dataDir);
|
|
164
|
+
const workspaceManager = new WorkspaceManager(store, {
|
|
165
|
+
diffStore,
|
|
166
|
+
prManager,
|
|
167
|
+
onWorkspaceSetupStateChanged: () => router.broadcastSnapshots(),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const machineDisplayName = getMachineDisplayName();
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
await store.initialize();
|
|
174
|
+
await diffStore.initialize();
|
|
175
|
+
} catch (error) {
|
|
176
|
+
await store.releaseDataDirLock();
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let server: ReturnType<typeof Bun.serve<ClientState>>;
|
|
181
|
+
let router: ReturnType<typeof createWsRouter>;
|
|
182
|
+
|
|
183
|
+
const terminals = new TerminalManager();
|
|
184
|
+
const keybindings = new KeybindingsManager();
|
|
185
|
+
try {
|
|
186
|
+
await keybindings.initialize();
|
|
187
|
+
} catch (error) {
|
|
188
|
+
await store.releaseDataDirLock();
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
const updateManager = options.update
|
|
192
|
+
? new UpdateManager({
|
|
193
|
+
currentVersion: options.update.version,
|
|
194
|
+
fetchLatestVersion: options.update.fetchLatestVersion,
|
|
195
|
+
installVersion: options.update.installVersion,
|
|
196
|
+
devMode: getRuntimeProfile() === 'dev',
|
|
197
|
+
})
|
|
198
|
+
: null;
|
|
199
|
+
|
|
200
|
+
async function refreshWorkspacePrStage(workspaceId: string, options?: { force?: boolean }) {
|
|
201
|
+
return workspaceManager.refreshWorkspacePrStage(workspaceId, options);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function refreshStartupWorkspaceStateInBackground() {
|
|
205
|
+
void refreshStartupWorkspaceState({
|
|
206
|
+
listWorkspaces: () => store.listWorkspaces(),
|
|
207
|
+
refreshWorkspaceGitSnapshot: (workspaceId, localPath) =>
|
|
208
|
+
diffStore.refreshWorkspaceGitSnapshot(workspaceId, localPath),
|
|
209
|
+
refreshWorkspacePrStage: (workspaceId, prOptions) =>
|
|
210
|
+
refreshWorkspacePrStage(workspaceId, prOptions),
|
|
211
|
+
broadcastSnapshots: () => router.broadcastSnapshots(),
|
|
212
|
+
}).catch((error) => {
|
|
213
|
+
console.warn('[miko] failed to refresh startup workspace state', error);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function cleanupStaleInstructionAttachmentsInBackground() {
|
|
218
|
+
void cleanupStaleInstructionAttachments([...store.state.workspacesById.values()]).catch(
|
|
219
|
+
(error) => {
|
|
220
|
+
console.warn('[miko] failed to cleanup instruction attachments', error);
|
|
221
|
+
},
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const prRefreshPoller = new PrRefreshPoller({
|
|
226
|
+
listWorkspaces: () => store.listWorkspaces(),
|
|
227
|
+
getWorkspaceGitHubSnapshot: (workspaceId) => prManager.getWorkspaceGitHubSnapshot(workspaceId),
|
|
228
|
+
refreshWorkspacePrStage: (workspaceId, prOptions) =>
|
|
229
|
+
refreshWorkspacePrStage(workspaceId, prOptions),
|
|
230
|
+
broadcastSnapshots: () => router.broadcastSnapshots(),
|
|
231
|
+
});
|
|
232
|
+
const gitRefreshPoller = new GitRefreshPoller({
|
|
233
|
+
listWorkspaces: () => store.listWorkspaces(),
|
|
234
|
+
refreshWorkspaceGitSnapshot: (workspaceId, localPath) =>
|
|
235
|
+
diffStore.refreshWorkspaceGitSnapshot(workspaceId, localPath),
|
|
236
|
+
broadcastSnapshots: () => router.broadcastSnapshots(),
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const agent = new AgentCoordinator({
|
|
240
|
+
store,
|
|
241
|
+
onStateChange: () => {
|
|
242
|
+
void router.broadcastSnapshots();
|
|
243
|
+
},
|
|
244
|
+
onTurnSettled: async ({ sessionId }) => {
|
|
245
|
+
const result = await workspaceManager.handleWorkspaceTurnSettled({ sessionId });
|
|
246
|
+
if (result.changed) await router.broadcastSnapshots();
|
|
247
|
+
},
|
|
248
|
+
renameWorkspaceBranch: async ({ workspaceId, branchName, expectedCurrentBranchName }) => {
|
|
249
|
+
const currentBranchName = store.requireWorkspace(workspaceId).branchName;
|
|
250
|
+
if (expectedCurrentBranchName && currentBranchName !== expectedCurrentBranchName) {
|
|
251
|
+
return { branchName: currentBranchName, changed: false };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const workspace = await workspaceManager.renameWorkspaceBranch(workspaceId, branchName);
|
|
255
|
+
return {
|
|
256
|
+
branchName: workspace.branchName,
|
|
257
|
+
changed: workspace.branchName !== currentBranchName,
|
|
258
|
+
};
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Release uploaded attachments of queued messages that are dropped before they ever run. The
|
|
263
|
+
// workspaceId is resolved server-side from the session; deleteWorkspaceUpload further confines
|
|
264
|
+
// each storedName to that workspace's upload dir.
|
|
265
|
+
agent.setUploadCleanup((workspaceId, storedNames) => {
|
|
266
|
+
for (const storedName of storedNames) {
|
|
267
|
+
void deleteWorkspaceUpload({ workspaceId, dataDir: store.dataDir, storedName });
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
router = createWsRouter({
|
|
272
|
+
store,
|
|
273
|
+
diffStore,
|
|
274
|
+
workspaceManager,
|
|
275
|
+
prManager,
|
|
276
|
+
scratchpadManager,
|
|
277
|
+
agent,
|
|
278
|
+
terminals,
|
|
279
|
+
keybindings,
|
|
280
|
+
machineDisplayName,
|
|
281
|
+
updateManager,
|
|
282
|
+
refreshWorkspacePrStage,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const distDir = path.join(import.meta.dir, '..', '..', 'dist', 'client');
|
|
286
|
+
|
|
287
|
+
const MAX_PORT_ATTEMPTS = 20;
|
|
288
|
+
let actualPort = port;
|
|
289
|
+
|
|
290
|
+
for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS; attempt++) {
|
|
291
|
+
try {
|
|
292
|
+
server = Bun.serve<ClientState>({
|
|
293
|
+
port: actualPort,
|
|
294
|
+
hostname,
|
|
295
|
+
async fetch(req, serverInstance) {
|
|
296
|
+
const url = new URL(req.url);
|
|
297
|
+
|
|
298
|
+
if (url.pathname === '/ws') {
|
|
299
|
+
const upgraded = serverInstance.upgrade(req, {
|
|
300
|
+
data: {
|
|
301
|
+
subscriptions: new Map(),
|
|
302
|
+
snapshotSignatures: new Map(),
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
return upgraded ? undefined : new Response('WebSocket upgrade failed', { status: 400 });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (url.pathname === '/health') {
|
|
310
|
+
return Response.json({ ok: true, port: actualPort });
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const uploadResponse = await handleWorkspaceUpload(req, url, store);
|
|
314
|
+
if (uploadResponse) {
|
|
315
|
+
return uploadResponse;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const deleteUploadResponse = await handleWorkspaceUploadDelete(req, url, store);
|
|
319
|
+
if (deleteUploadResponse) {
|
|
320
|
+
return deleteUploadResponse;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const attachmentContentResponse = await handleAttachmentContent(req, url, store);
|
|
324
|
+
if (attachmentContentResponse) {
|
|
325
|
+
return attachmentContentResponse;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const agentInstructionContentResponse = await handleAgentInstructionContent(req, url);
|
|
329
|
+
if (agentInstructionContentResponse) {
|
|
330
|
+
return agentInstructionContentResponse;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const workspaceFileContentResponse = await handleWorkspaceFileContent(req, url, store);
|
|
334
|
+
if (workspaceFileContentResponse) {
|
|
335
|
+
return workspaceFileContentResponse;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const externalFileContentResponse = await handleExternalFileContent(req, url);
|
|
339
|
+
if (externalFileContentResponse) {
|
|
340
|
+
return externalFileContentResponse;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return serveStatic(distDir, url.pathname);
|
|
344
|
+
},
|
|
345
|
+
websocket: {
|
|
346
|
+
open(ws) {
|
|
347
|
+
router.handleOpen(ws);
|
|
348
|
+
},
|
|
349
|
+
message(ws, raw) {
|
|
350
|
+
router.handleMessage(ws, raw);
|
|
351
|
+
},
|
|
352
|
+
close(ws) {
|
|
353
|
+
router.handleClose(ws);
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
break;
|
|
358
|
+
} catch (err: unknown) {
|
|
359
|
+
const isAddrInUse =
|
|
360
|
+
err instanceof Error &&
|
|
361
|
+
'code' in err &&
|
|
362
|
+
(err as NodeJS.ErrnoException).code === 'EADDRINUSE';
|
|
363
|
+
|
|
364
|
+
if (!isAddrInUse || strictPort || attempt === MAX_PORT_ATTEMPTS - 1) {
|
|
365
|
+
await store.releaseDataDirLock();
|
|
366
|
+
throw err;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
console.log(`Port ${actualPort} is in use, trying ${actualPort + 1}...`);
|
|
370
|
+
actualPort++;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
void agent.resumeQueuedMessages().catch((error) => {
|
|
375
|
+
console.error('[queue-recovery] Unexpected queue recovery failure:', error);
|
|
376
|
+
});
|
|
377
|
+
cleanupStaleInstructionAttachmentsInBackground();
|
|
378
|
+
refreshStartupWorkspaceStateInBackground();
|
|
379
|
+
prRefreshPoller.start();
|
|
380
|
+
gitRefreshPoller.start();
|
|
381
|
+
|
|
382
|
+
const shutdown = async () => {
|
|
383
|
+
try {
|
|
384
|
+
for (const sessionId of [...agent.activeTurns.keys()]) {
|
|
385
|
+
await agent.cancel(sessionId, { preserveQueue: true });
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
await prRefreshPoller.stop();
|
|
389
|
+
await gitRefreshPoller.stop();
|
|
390
|
+
router.dispose();
|
|
391
|
+
keybindings.dispose();
|
|
392
|
+
terminals.closeAll();
|
|
393
|
+
await store.compact();
|
|
394
|
+
} finally {
|
|
395
|
+
try {
|
|
396
|
+
server.stop(true);
|
|
397
|
+
} finally {
|
|
398
|
+
await store.releaseDataDirLock();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
port: actualPort,
|
|
405
|
+
store,
|
|
406
|
+
diffStore,
|
|
407
|
+
workspaceManager,
|
|
408
|
+
prManager,
|
|
409
|
+
updateManager,
|
|
410
|
+
stop: shutdown,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export async function handleWorkspaceUpload(req: Request, url: URL, store: EventStore) {
|
|
415
|
+
if (req.method !== 'POST') {
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const match = url.pathname.match(/^\/api\/workspaces\/([^/]+)\/uploads$/);
|
|
420
|
+
if (!match) {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const workspace = store.getWorkspace(match[1]);
|
|
425
|
+
if (!workspace) {
|
|
426
|
+
return Response.json({ error: 'Workspace not found' }, { status: 404 });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const formData = await req.formData();
|
|
430
|
+
const files = formData.getAll('files').filter((value): value is File => value instanceof File);
|
|
431
|
+
|
|
432
|
+
if (files.length === 0) {
|
|
433
|
+
return Response.json({ error: 'No files uploaded' }, { status: 400 });
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (files.length > MAX_UPLOAD_FILES) {
|
|
437
|
+
return Response.json(
|
|
438
|
+
{ error: `You can upload up to ${MAX_UPLOAD_FILES} files at a time.` },
|
|
439
|
+
{ status: 400 },
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (const file of files) {
|
|
444
|
+
if (file.size > MAX_UPLOAD_SIZE_BYTES) {
|
|
445
|
+
return Response.json(
|
|
446
|
+
{
|
|
447
|
+
error: `File "${file.name}" exceeds the ${Math.floor(MAX_UPLOAD_SIZE_BYTES / (1024 * 1024))} MB limit.`,
|
|
448
|
+
},
|
|
449
|
+
{ status: 413 },
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
const attachments = await persistUploadedFiles({
|
|
456
|
+
workspaceId: workspace.id,
|
|
457
|
+
dataDir: store.dataDir,
|
|
458
|
+
files,
|
|
459
|
+
});
|
|
460
|
+
return Response.json({ attachments });
|
|
461
|
+
} catch (error) {
|
|
462
|
+
console.error('[uploads] Upload failed:', error);
|
|
463
|
+
return Response.json({ error: 'Upload failed' }, { status: 500 });
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export async function handleAttachmentContent(req: Request, url: URL, store: EventStore) {
|
|
468
|
+
const match = url.pathname.match(/^\/api\/workspaces\/([^/]+)\/uploads\/([^/]+)\/content$/);
|
|
469
|
+
if (!match) {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (req.method !== 'GET') {
|
|
474
|
+
return new Response(null, {
|
|
475
|
+
status: 405,
|
|
476
|
+
headers: {
|
|
477
|
+
Allow: 'GET',
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const workspace = store.getWorkspace(match[1]);
|
|
483
|
+
if (!workspace) {
|
|
484
|
+
return Response.json({ error: 'Workspace not found' }, { status: 404 });
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const storedName = safeDecodePathSegment(match[2]);
|
|
488
|
+
if (
|
|
489
|
+
!storedName ||
|
|
490
|
+
storedName.includes('/') ||
|
|
491
|
+
storedName.includes('\\') ||
|
|
492
|
+
storedName === '.' ||
|
|
493
|
+
storedName === '..'
|
|
494
|
+
) {
|
|
495
|
+
return Response.json({ error: 'Invalid attachment path' }, { status: 400 });
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const filePath = path.join(getWorkspaceUploadDir(workspace.id, store.dataDir), storedName);
|
|
499
|
+
const file = Bun.file(filePath);
|
|
500
|
+
|
|
501
|
+
try {
|
|
502
|
+
const info = await stat(filePath);
|
|
503
|
+
if (!info.isFile()) {
|
|
504
|
+
return Response.json({ error: 'Attachment not found' }, { status: 404 });
|
|
505
|
+
}
|
|
506
|
+
} catch {
|
|
507
|
+
return Response.json({ error: 'Attachment not found' }, { status: 404 });
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return new Response(file, {
|
|
511
|
+
headers: {
|
|
512
|
+
'Content-Type': inferAttachmentContentType(storedName, file.type),
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export async function handleAgentInstructionContent(
|
|
518
|
+
req: Request,
|
|
519
|
+
url: URL,
|
|
520
|
+
options: { getFilePath?: (fileName: string) => string } = {},
|
|
521
|
+
) {
|
|
522
|
+
const match = url.pathname.match(/^\/api\/agent-instructions\/([^/]+)\/content$/);
|
|
523
|
+
if (!match) {
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (req.method !== 'GET') {
|
|
528
|
+
return new Response(null, {
|
|
529
|
+
status: 405,
|
|
530
|
+
headers: {
|
|
531
|
+
Allow: 'GET',
|
|
532
|
+
},
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const fileName = safeDecodePathSegment(match[1]);
|
|
537
|
+
if (!fileName) {
|
|
538
|
+
return Response.json({ error: 'Invalid agent instruction path' }, { status: 400 });
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
let filePath: string;
|
|
542
|
+
try {
|
|
543
|
+
filePath = (options.getFilePath ?? getAgentInstructionFilePath)(fileName);
|
|
544
|
+
} catch {
|
|
545
|
+
return Response.json({ error: 'Invalid agent instruction path' }, { status: 400 });
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const file = Bun.file(filePath);
|
|
549
|
+
try {
|
|
550
|
+
const info = await stat(filePath);
|
|
551
|
+
if (!info.isFile()) {
|
|
552
|
+
return Response.json({ error: 'Agent instruction not found' }, { status: 404 });
|
|
553
|
+
}
|
|
554
|
+
if (info.size > MAX_AGENT_INSTRUCTION_CONTENT_BYTES) {
|
|
555
|
+
return Response.json({ error: 'Agent instruction is too large to preview' }, { status: 413 });
|
|
556
|
+
}
|
|
557
|
+
} catch {
|
|
558
|
+
return Response.json({ error: 'Agent instruction not found' }, { status: 404 });
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return new Response(file, {
|
|
562
|
+
headers: {
|
|
563
|
+
'Content-Type': inferAttachmentContentType(fileName, file.type),
|
|
564
|
+
'Content-Disposition': 'inline',
|
|
565
|
+
'X-Content-Type-Options': 'nosniff',
|
|
566
|
+
},
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
export async function handleWorkspaceFileContent(req: Request, url: URL, store: EventStore) {
|
|
571
|
+
const match = url.pathname.match(/^\/api\/workspaces\/([^/]+)\/files\/([^/]+)\/content$/);
|
|
572
|
+
if (!match) {
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (req.method !== 'GET') {
|
|
577
|
+
return new Response(null, {
|
|
578
|
+
status: 405,
|
|
579
|
+
headers: {
|
|
580
|
+
Allow: 'GET',
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const workspace = store.getWorkspace(match[1]);
|
|
586
|
+
if (!workspace) {
|
|
587
|
+
return Response.json({ error: 'Workspace not found' }, { status: 404 });
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const decodedPathSegment = safeDecodePathSegment(match[2]);
|
|
591
|
+
if (!decodedPathSegment) {
|
|
592
|
+
return Response.json({ error: 'Invalid workspace file path' }, { status: 400 });
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const relativePath = path.posix.normalize(decodedPathSegment.replaceAll('\\', '/'));
|
|
596
|
+
if (
|
|
597
|
+
!relativePath ||
|
|
598
|
+
relativePath === '.' ||
|
|
599
|
+
relativePath.startsWith('../') ||
|
|
600
|
+
relativePath.includes('/../') ||
|
|
601
|
+
path.posix.isAbsolute(relativePath)
|
|
602
|
+
) {
|
|
603
|
+
return Response.json({ error: 'Invalid workspace file path' }, { status: 400 });
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
const filePath = path.resolve(workspace.localPath, relativePath);
|
|
607
|
+
const workspaceRoot = path.resolve(workspace.localPath);
|
|
608
|
+
if (filePath !== workspaceRoot && !filePath.startsWith(`${workspaceRoot}${path.sep}`)) {
|
|
609
|
+
return Response.json({ error: 'Invalid workspace file path' }, { status: 400 });
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
let targetRealPath: string;
|
|
613
|
+
try {
|
|
614
|
+
const [workspaceRootRealPath, fileRealPath] = await Promise.all([
|
|
615
|
+
realpath(workspaceRoot),
|
|
616
|
+
realpath(filePath),
|
|
617
|
+
]);
|
|
618
|
+
if (
|
|
619
|
+
fileRealPath !== workspaceRootRealPath &&
|
|
620
|
+
!fileRealPath.startsWith(`${workspaceRootRealPath}${path.sep}`)
|
|
621
|
+
) {
|
|
622
|
+
return Response.json({ error: 'Invalid workspace file path' }, { status: 400 });
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
targetRealPath = fileRealPath;
|
|
626
|
+
const info = await stat(targetRealPath);
|
|
627
|
+
if (!info.isFile()) {
|
|
628
|
+
return Response.json({ error: 'File not found' }, { status: 404 });
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (info.size > MAX_WORKSPACE_FILE_CONTENT_BYTES) {
|
|
632
|
+
return Response.json({ error: 'File is too large to preview' }, { status: 413 });
|
|
633
|
+
}
|
|
634
|
+
} catch {
|
|
635
|
+
return Response.json({ error: 'File not found' }, { status: 404 });
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const file = Bun.file(targetRealPath);
|
|
639
|
+
return new Response(file, {
|
|
640
|
+
headers: {
|
|
641
|
+
'Content-Type': inferWorkspaceFileContentType(targetRealPath, file.type),
|
|
642
|
+
'Content-Disposition': 'inline',
|
|
643
|
+
'X-Content-Type-Options': 'nosniff',
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
export async function handleExternalFileContent(req: Request, url: URL) {
|
|
649
|
+
if (url.pathname !== '/api/external-files/content') {
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (req.method !== 'GET') {
|
|
654
|
+
return new Response(null, {
|
|
655
|
+
status: 405,
|
|
656
|
+
headers: {
|
|
657
|
+
Allow: 'GET',
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const token = url.searchParams.get('token');
|
|
663
|
+
const grantedPath = token ? resolveExternalFileAccessToken(token) : null;
|
|
664
|
+
if (!grantedPath) {
|
|
665
|
+
return Response.json({ error: 'Invalid external file token' }, { status: 403 });
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
let targetRealPath: string;
|
|
669
|
+
let info: Awaited<ReturnType<typeof stat>>;
|
|
670
|
+
try {
|
|
671
|
+
targetRealPath = await realpath(grantedPath);
|
|
672
|
+
info = await stat(targetRealPath);
|
|
673
|
+
if (!info.isFile()) {
|
|
674
|
+
return Response.json({ error: 'File not found' }, { status: 404 });
|
|
675
|
+
}
|
|
676
|
+
if (info.size > MAX_WORKSPACE_FILE_CONTENT_BYTES) {
|
|
677
|
+
return Response.json({ error: 'File is too large to preview' }, { status: 413 });
|
|
678
|
+
}
|
|
679
|
+
} catch {
|
|
680
|
+
return Response.json({ error: 'File not found' }, { status: 404 });
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const file = Bun.file(targetRealPath);
|
|
684
|
+
const inferredContentType = inferWorkspaceFileContentType(targetRealPath, file.type);
|
|
685
|
+
const contentType =
|
|
686
|
+
inferredContentType.toLowerCase() === 'image/svg+xml'
|
|
687
|
+
? 'text/plain; charset=utf-8'
|
|
688
|
+
: inferredContentType;
|
|
689
|
+
|
|
690
|
+
return new Response(file, {
|
|
691
|
+
headers: {
|
|
692
|
+
'Content-Type': contentType,
|
|
693
|
+
'Content-Disposition': 'inline',
|
|
694
|
+
'X-Content-Type-Options': 'nosniff',
|
|
695
|
+
},
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
export async function handleWorkspaceUploadDelete(req: Request, url: URL, store: EventStore) {
|
|
700
|
+
if (req.method !== 'DELETE') {
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const match = url.pathname.match(/^\/api\/workspaces\/([^/]+)\/uploads\/([^/]+)$/);
|
|
705
|
+
if (!match) {
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const workspace = store.getWorkspace(match[1]);
|
|
710
|
+
if (!workspace) {
|
|
711
|
+
return Response.json({ error: 'Workspace not found' }, { status: 404 });
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const storedName = safeDecodePathSegment(match[2]);
|
|
715
|
+
if (
|
|
716
|
+
!storedName ||
|
|
717
|
+
storedName.includes('/') ||
|
|
718
|
+
storedName.includes('\\') ||
|
|
719
|
+
storedName === '.' ||
|
|
720
|
+
storedName === '..'
|
|
721
|
+
) {
|
|
722
|
+
return Response.json({ error: 'Invalid attachment path' }, { status: 400 });
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const deleted = await deleteWorkspaceUpload({
|
|
726
|
+
workspaceId: workspace.id,
|
|
727
|
+
dataDir: store.dataDir,
|
|
728
|
+
storedName,
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
return Response.json({ ok: deleted });
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
export async function serveStatic(distDir: string, pathname: string) {
|
|
735
|
+
const requestedPath = pathname === '/' ? '/index.html' : pathname;
|
|
736
|
+
const filePath = path.join(distDir, requestedPath);
|
|
737
|
+
const indexPath = path.join(distDir, 'index.html');
|
|
738
|
+
|
|
739
|
+
const file = Bun.file(filePath);
|
|
740
|
+
if (await file.exists()) {
|
|
741
|
+
return new Response(file);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const indexFile = Bun.file(indexPath);
|
|
745
|
+
if (await indexFile.exists()) {
|
|
746
|
+
return new Response(indexFile, {
|
|
747
|
+
headers: {
|
|
748
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return new Response(
|
|
754
|
+
`${APP_NAME} client bundle not found. Run \`bun run build\` inside workbench/ first.`,
|
|
755
|
+
{
|
|
756
|
+
status: 503,
|
|
757
|
+
},
|
|
758
|
+
);
|
|
759
|
+
}
|