@vividcodeai/embeddedcowork 0.0.22 → 0.0.24
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/dist/api-types.js +1 -0
- package/dist/auth/auth-store.js +134 -0
- package/dist/auth/http-auth.js +37 -0
- package/dist/auth/manager.js +140 -0
- package/dist/auth/password-hash.js +32 -0
- package/dist/auth/session-manager.js +17 -0
- package/dist/auth/token-manager.js +27 -0
- package/dist/background-processes/manager.js +576 -0
- package/dist/bin.js +24 -0
- package/dist/clients/connection-manager.js +93 -0
- package/dist/config/location.js +57 -0
- package/dist/config/schema.js +72 -0
- package/dist/events/bus.js +47 -0
- package/dist/filesystem/__tests__/search-cache.test.js +40 -0
- package/dist/filesystem/browser.js +292 -0
- package/dist/filesystem/search-cache.js +43 -0
- package/dist/filesystem/search.js +135 -0
- package/dist/index.js +496 -0
- package/dist/launcher.js +149 -0
- package/dist/loader.js +21 -0
- package/dist/logger.js +109 -0
- package/dist/opencode-config/README.md +32 -0
- package/dist/opencode-config/opencode.jsonc +35 -0
- package/dist/opencode-config/package.json +9 -0
- package/dist/opencode-config/plugin/embeddedcowork.ts +62 -0
- package/dist/opencode-config/plugin/lib/background-process.ts +265 -0
- package/dist/opencode-config/plugin/lib/client.ts +133 -0
- package/dist/opencode-config/plugin/lib/request.ts +214 -0
- package/dist/opencode-config.js +15 -0
- package/dist/opencode-downloader.js +295 -0
- package/dist/opencode-paths.js +167 -0
- package/dist/plugins/channel.js +40 -0
- package/dist/plugins/handlers.js +17 -0
- package/dist/plugins/voice-mode.js +78 -0
- package/dist/releases/dev-release-monitor.js +75 -0
- package/dist/releases/release-monitor.js +107 -0
- package/dist/releases/rollback.js +54 -0
- package/dist/runtime-paths.js +67 -0
- package/dist/server/__tests__/network-addresses.test.js +68 -0
- package/dist/server/__tests__/remote-proxy.test.js +204 -0
- package/dist/server/http-server.js +998 -0
- package/dist/server/network-addresses.js +114 -0
- package/dist/server/remote-proxy.js +466 -0
- package/dist/server/routes/auth-pages/login.html +135 -0
- package/dist/server/routes/auth-pages/token.html +93 -0
- package/dist/server/routes/auth.js +149 -0
- package/dist/server/routes/background-processes.js +78 -0
- package/dist/server/routes/events.js +66 -0
- package/dist/server/routes/filesystem.js +43 -0
- package/dist/server/routes/meta.js +44 -0
- package/dist/server/routes/opencode-status.js +10 -0
- package/dist/server/routes/plugin.js +70 -0
- package/dist/server/routes/remote-proxy.js +42 -0
- package/dist/server/routes/remote-servers.js +142 -0
- package/dist/server/routes/settings.js +69 -0
- package/dist/server/routes/sidecars.js +46 -0
- package/dist/server/routes/speech.js +63 -0
- package/dist/server/routes/storage.js +52 -0
- package/dist/server/routes/workspaces.js +221 -0
- package/dist/server/routes/worktrees.js +156 -0
- package/dist/server/tls.js +224 -0
- package/dist/settings/binaries.js +37 -0
- package/dist/settings/merge-patch.js +33 -0
- package/dist/settings/migrate.js +238 -0
- package/dist/settings/public-config.js +33 -0
- package/dist/settings/service.js +101 -0
- package/dist/settings/yaml-doc-store.js +96 -0
- package/dist/sidecars/manager.js +193 -0
- package/dist/speech/providers/openai-compatible.js +189 -0
- package/dist/speech/service.js +58 -0
- package/dist/storage/instance-store.js +56 -0
- package/dist/ui/__tests__/remote-ui.test.js +67 -0
- package/dist/ui/remote-ui.js +462 -0
- package/dist/workspaces/__tests__/spawn.test.js +139 -0
- package/dist/workspaces/git-mutations.js +98 -0
- package/dist/workspaces/git-status.js +323 -0
- package/dist/workspaces/git-worktrees.js +216 -0
- package/dist/workspaces/instance-events.js +180 -0
- package/dist/workspaces/manager.js +432 -0
- package/dist/workspaces/opencode-auth.js +16 -0
- package/dist/workspaces/runtime.js +366 -0
- package/dist/workspaces/spawn.js +219 -0
- package/dist/workspaces/worktree-directory.js +74 -0
- package/dist/workspaces/worktree-map.js +116 -0
- package/package.json +3 -12
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/assets/ChangesTab-Uu8GvqIU.js +2 -0
- package/public/assets/DiffToolbar-BgdJy7ap.js +1 -0
- package/public/assets/EmbeddedCowork-Icon-DSw5nKk7.png +0 -0
- package/public/assets/FilesTab-BlKXggYW.js +2 -0
- package/public/assets/GitChangesTab-DRAyGtTx.js +2 -0
- package/public/assets/SplitFilePanel-DR88B4Vy.js +1 -0
- package/public/assets/StatusTab-BS7K80gj.js +1 -0
- package/public/assets/abap-BdImnpbu.js +1 -0
- package/public/assets/actionscript-3-CfeIJUat.js +1 -0
- package/public/assets/ada-bCR0ucgS.js +1 -0
- package/public/assets/andromeeda-C-Jbm3Hp.js +1 -0
- package/public/assets/angular-html-CU67Zn6k.js +1 -0
- package/public/assets/angular-ts-BwZT4LLn.js +1 -0
- package/public/assets/apache-Pmp26Uib.js +1 -0
- package/public/assets/apex-DhZLUxFE.js +1 -0
- package/public/assets/apl-dKokRX4l.js +1 -0
- package/public/assets/applescript-Co6uUVPk.js +1 -0
- package/public/assets/ara-BRHolxvo.js +1 -0
- package/public/assets/asciidoc-Dv7Oe6Be.js +1 -0
- package/public/assets/asm-D_Q5rh1f.js +1 -0
- package/public/assets/astro-CbQHKStN.js +1 -0
- package/public/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/public/assets/awk-DMzUqQB5.js +1 -0
- package/public/assets/ayu-dark-Cv9koXgw.js +1 -0
- package/public/assets/ballerina-BFfxhgS-.js +1 -0
- package/public/assets/bat-BkioyH1T.js +1 -0
- package/public/assets/beancount-k_qm7-4y.js +1 -0
- package/public/assets/berry-D08WgyRC.js +1 -0
- package/public/assets/bibtex-CHM0blh-.js +1 -0
- package/public/assets/bicep-Bmn6On1c.js +1 -0
- package/public/assets/blade-DVc8C-J4.js +1 -0
- package/public/assets/bsl-BO_Y6i37.js +1 -0
- package/public/assets/bundle-full-BusrfhOv.js +13 -0
- package/public/assets/c-BIGW1oBm.js +1 -0
- package/public/assets/cadence-Bv_4Rxtq.js +1 -0
- package/public/assets/cairo-KRGpt6FW.js +1 -0
- package/public/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- package/public/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- package/public/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- package/public/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- package/public/assets/clarity-D53aC0YG.js +1 -0
- package/public/assets/clojure-P80f7IUj.js +1 -0
- package/public/assets/cmake-D1j8_8rp.js +1 -0
- package/public/assets/cobol-nwyudZeR.js +1 -0
- package/public/assets/codeowners-Bp6g37R7.js +1 -0
- package/public/assets/codeql-DsOJ9woJ.js +1 -0
- package/public/assets/coffee-Ch7k5sss.js +1 -0
- package/public/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/public/assets/coq-DkFqJrB1.js +1 -0
- package/public/assets/core-DhEqZVGG.js +1 -0
- package/public/assets/cpp-CofmeUqb.js +1 -0
- package/public/assets/crystal-tKQVLTB8.js +1 -0
- package/public/assets/csharp-CX12Zw3r.js +1 -0
- package/public/assets/css-DPfMkruS.js +1 -0
- package/public/assets/csv-fuZLfV_i.js +1 -0
- package/public/assets/cue-D82EKSYY.js +1 -0
- package/public/assets/cypher-COkxafJQ.js +1 -0
- package/public/assets/d-85-TOEBH.js +1 -0
- package/public/assets/dark-plus-eOWES_5F.js +1 -0
- package/public/assets/dart-CF10PKvl.js +1 -0
- package/public/assets/dax-CEL-wOlO.js +1 -0
- package/public/assets/desktop-BmXAJ9_W.js +1 -0
- package/public/assets/diff-D97Zzqfu.js +1 -0
- package/public/assets/diff-viewer-BDYT5S-Q.js +1 -0
- package/public/assets/docker-BcOcwvcX.js +1 -0
- package/public/assets/dotenv-Da5cRb03.js +1 -0
- package/public/assets/dracula-BzJJZx-M.js +1 -0
- package/public/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/public/assets/dream-maker-BtqSS_iP.js +1 -0
- package/public/assets/edge-BkV0erSs.js +1 -0
- package/public/assets/elixir-CDX3lj18.js +1 -0
- package/public/assets/elm-DbKCFpqz.js +1 -0
- package/public/assets/emacs-lisp-C9XAeP06.js +1 -0
- package/public/assets/erb-BOJIQeun.js +1 -0
- package/public/assets/erlang-DsQrWhSR.js +1 -0
- package/public/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/public/assets/everforest-light-C8M2exoo.js +1 -0
- package/public/assets/fast-diff-vendor-DgdwVvTQ.js +1 -0
- package/public/assets/fennel-BYunw83y.js +1 -0
- package/public/assets/fish-BvzEVeQv.js +1 -0
- package/public/assets/fluent-C4IJs8-o.js +1 -0
- package/public/assets/fortran-fixed-form-BZjJHVRy.js +1 -0
- package/public/assets/fortran-free-form-D22FLkUw.js +1 -0
- package/public/assets/fsharp-CXgrBDvD.js +1 -0
- package/public/assets/gdresource-B7Tvp0Sc.js +1 -0
- package/public/assets/gdscript-DTMYz4Jt.js +1 -0
- package/public/assets/gdshader-DkwncUOv.js +1 -0
- package/public/assets/genie-D0YGMca9.js +1 -0
- package/public/assets/gherkin-DyxjwDmM.js +1 -0
- package/public/assets/git-commit-F4YmCXRG.js +1 -0
- package/public/assets/git-diff-vendor-DaSE45SW.js +52 -0
- package/public/assets/git-diff-vendor-HAZkIolJ.css +19 -0
- package/public/assets/git-rebase-r7XF79zn.js +1 -0
- package/public/assets/github-dark-DHJKELXO.js +1 -0
- package/public/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/public/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/public/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/public/assets/github-light-DAi9KRSo.js +1 -0
- package/public/assets/github-light-default-D7oLnXFd.js +1 -0
- package/public/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/public/assets/gleam-BspZqrRM.js +1 -0
- package/public/assets/glimmer-js-Rg0-pVw9.js +1 -0
- package/public/assets/glimmer-ts-U6CK756n.js +1 -0
- package/public/assets/glsl-DplSGwfg.js +1 -0
- package/public/assets/gnuplot-DdkO51Og.js +1 -0
- package/public/assets/go-Dn2_MT6a.js +1 -0
- package/public/assets/graphql-ChdNCCLP.js +1 -0
- package/public/assets/groovy-gcz8RCvz.js +1 -0
- package/public/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/public/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/public/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/public/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/public/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/public/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/public/assets/hack-CaT9iCJl.js +1 -0
- package/public/assets/haml-B8DHNrY2.js +1 -0
- package/public/assets/handlebars-BL8al0AC.js +1 -0
- package/public/assets/haskell-Df6bDoY_.js +1 -0
- package/public/assets/haxe-CzTSHFRz.js +1 -0
- package/public/assets/hcl-BWvSN4gD.js +1 -0
- package/public/assets/highlight-vendor-8FKMu9os.js +10 -0
- package/public/assets/hjson-D5-asLiD.js +1 -0
- package/public/assets/hlsl-D3lLCCz7.js +1 -0
- package/public/assets/houston-DnULxvSX.js +1 -0
- package/public/assets/html-GMplVEZG.js +1 -0
- package/public/assets/html-derivative-BFtXZ54Q.js +1 -0
- package/public/assets/http-jrhK8wxY.js +1 -0
- package/public/assets/hurl-irOxFIW8.js +1 -0
- package/public/assets/hxml-Bvhsp5Yf.js +1 -0
- package/public/assets/hy-DFXneXwc.js +1 -0
- package/public/assets/imba-DGztddWO.js +1 -0
- package/public/assets/index-BKMyzTSR.js +1 -0
- package/public/assets/index-BWhMWtCP.js +2 -0
- package/public/assets/index-Bsb7z85O.js +1 -0
- package/public/assets/index-By8QJmOa.js +1 -0
- package/public/assets/index-CLSJ4cO9.js +1 -0
- package/public/assets/index-DBPzV8JQ.js +1 -0
- package/public/assets/index-DhOdialG.js +1 -0
- package/public/assets/index-DlobuxMv.js +1 -0
- package/public/assets/index-QdhU2jY3.css +1 -0
- package/public/assets/index-WYKVZxn4.js +1 -0
- package/public/assets/index-gBWCTkMr.js +1 -0
- package/public/assets/ini-BEwlwnbL.js +1 -0
- package/public/assets/java-CylS5w8V.js +1 -0
- package/public/assets/javascript-wDzz0qaB.js +1 -0
- package/public/assets/jinja-4LBKfQ-Z.js +1 -0
- package/public/assets/jison-wvAkD_A8.js +1 -0
- package/public/assets/json-Cp-IABpG.js +1 -0
- package/public/assets/json5-C9tS-k6U.js +1 -0
- package/public/assets/jsonc-Des-eS-w.js +1 -0
- package/public/assets/jsonl-DcaNXYhu.js +1 -0
- package/public/assets/jsonnet-DFQXde-d.js +1 -0
- package/public/assets/jssm-C2t-YnRu.js +1 -0
- package/public/assets/jsx-g9-lgVsj.js +1 -0
- package/public/assets/julia-C8NyazO9.js +1 -0
- package/public/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- package/public/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- package/public/assets/kanagawa-wave-DWedfzmr.js +1 -0
- package/public/assets/kdl-DV7GczEv.js +1 -0
- package/public/assets/kotlin-BdnUsdx6.js +1 -0
- package/public/assets/kusto-BvAqAH-y.js +1 -0
- package/public/assets/laserwave-DUszq2jm.js +1 -0
- package/public/assets/latex-BUKiar2Z.js +1 -0
- package/public/assets/lean-DP1Csr6i.js +1 -0
- package/public/assets/less-B1dDrJ26.js +1 -0
- package/public/assets/light-plus-B7mTdjB0.js +1 -0
- package/public/assets/liquid-DYVedYrR.js +1 -0
- package/public/assets/llvm-BtvRca6l.js +1 -0
- package/public/assets/loading-C4yc63Qi.js +2 -0
- package/public/assets/loading-CugGjKDZ.css +1 -0
- package/public/assets/log-2UxHyX5q.js +1 -0
- package/public/assets/logo-BtOb2qkB.js +1 -0
- package/public/assets/lua-BbnMAYS6.js +1 -0
- package/public/assets/luau-CXu1NL6O.js +1 -0
- package/public/assets/main-y4WbiJBN.js +53 -0
- package/public/assets/make-CHLpvVh8.js +1 -0
- package/public/assets/markdown-Cvjx9yec.js +1 -0
- package/public/assets/markdown-DmuP22dE.js +58 -0
- package/public/assets/marko-CPi9NSCl.js +1 -0
- package/public/assets/material-theme-D5KoaKCx.js +1 -0
- package/public/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/public/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/public/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/public/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/public/assets/matlab-D7o27uSR.js +1 -0
- package/public/assets/mdc-DUICxH0z.js +1 -0
- package/public/assets/mdx-Cmh6b_Ma.js +1 -0
- package/public/assets/mermaid-DKYwYmdq.js +1 -0
- package/public/assets/min-dark-CafNBF8u.js +1 -0
- package/public/assets/min-light-CTRr51gU.js +1 -0
- package/public/assets/mipsasm-CKIfxQSi.js +1 -0
- package/public/assets/mojo-1DNp92w6.js +1 -0
- package/public/assets/monaco-viewer-OXO07Mos.js +26 -0
- package/public/assets/monokai-D4h5O-jR.js +1 -0
- package/public/assets/move-Bu9oaDYs.js +1 -0
- package/public/assets/narrat-DRg8JJMk.js +1 -0
- package/public/assets/nextflow-CUEJCptM.js +1 -0
- package/public/assets/nginx-DknmC5AR.js +1 -0
- package/public/assets/night-owl-C39BiMTA.js +1 -0
- package/public/assets/nim-CVrawwO9.js +1 -0
- package/public/assets/nix-BbRYJGeE.js +1 -0
- package/public/assets/nord-Ddv68eIx.js +1 -0
- package/public/assets/nushell-C-sUppwS.js +1 -0
- package/public/assets/objective-c-DXmwc3jG.js +1 -0
- package/public/assets/objective-cpp-CLxacb5B.js +1 -0
- package/public/assets/ocaml-C0hk2d4L.js +1 -0
- package/public/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/public/assets/one-light-PoHY5YXO.js +1 -0
- package/public/assets/pascal-D93ZcfNL.js +1 -0
- package/public/assets/perl-C0TMdlhV.js +1 -0
- package/public/assets/php-CDn_0X-4.js +1 -0
- package/public/assets/pkl-u5AG7uiY.js +1 -0
- package/public/assets/plastic-3e1v2bzS.js +1 -0
- package/public/assets/plsql-ChMvpjG-.js +1 -0
- package/public/assets/po-BTJTHyun.js +1 -0
- package/public/assets/poimandres-CS3Unz2-.js +1 -0
- package/public/assets/polar-C0HS_06l.js +1 -0
- package/public/assets/postcss-CXtECtnM.js +1 -0
- package/public/assets/powerquery-CEu0bR-o.js +1 -0
- package/public/assets/powershell-Dpen1YoG.js +1 -0
- package/public/assets/prisma-Dd19v3D-.js +1 -0
- package/public/assets/prolog-CbFg5uaA.js +1 -0
- package/public/assets/proto-DyJlTyXw.js +1 -0
- package/public/assets/pug-CGlum2m_.js +1 -0
- package/public/assets/puppet-BMWR74SV.js +1 -0
- package/public/assets/purescript-CklMAg4u.js +1 -0
- package/public/assets/python-B6aJPvgy.js +1 -0
- package/public/assets/qml-3beO22l8.js +1 -0
- package/public/assets/qmldir-C8lEn-DE.js +1 -0
- package/public/assets/qss-IeuSbFQv.js +1 -0
- package/public/assets/r-DiinP2Uv.js +1 -0
- package/public/assets/racket-BqYA7rlc.js +1 -0
- package/public/assets/raku-DXvB9xmW.js +1 -0
- package/public/assets/razor-WgofotgN.js +1 -0
- package/public/assets/red-bN70gL4F.js +1 -0
- package/public/assets/reg-C-SQnVFl.js +1 -0
- package/public/assets/regexp-CDVJQ6XC.js +1 -0
- package/public/assets/rel-C3B-1QV4.js +1 -0
- package/public/assets/riscv-BM1_JUlF.js +1 -0
- package/public/assets/rose-pine-BHrmToEH.js +1 -0
- package/public/assets/rose-pine-dawn-CnK8MTSM.js +1 -0
- package/public/assets/rose-pine-moon-NleAzG8P.js +1 -0
- package/public/assets/rosmsg-BJDFO7_C.js +1 -0
- package/public/assets/rst-B0xPkSld.js +1 -0
- package/public/assets/ruby-BvKwtOVI.js +1 -0
- package/public/assets/rust-B1yitclQ.js +1 -0
- package/public/assets/sas-cz2c8ADy.js +1 -0
- package/public/assets/sass-Cj5Yp3dK.js +1 -0
- package/public/assets/scala-C151Ov-r.js +1 -0
- package/public/assets/scheme-C98Dy4si.js +1 -0
- package/public/assets/scss-OYdSNvt2.js +1 -0
- package/public/assets/sdbl-DVxCFoDh.js +1 -0
- package/public/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/public/assets/shellscript-Yzrsuije.js +1 -0
- package/public/assets/shellsession-BADoaaVG.js +1 -0
- package/public/assets/slack-dark-BthQWCQV.js +1 -0
- package/public/assets/slack-ochin-DqwNpetd.js +1 -0
- package/public/assets/smalltalk-BERRCDM3.js +1 -0
- package/public/assets/snazzy-light-Bw305WKR.js +1 -0
- package/public/assets/solarized-dark-DXbdFlpD.js +1 -0
- package/public/assets/solarized-light-L9t79GZl.js +1 -0
- package/public/assets/solidity-BbcW6ACK.js +1 -0
- package/public/assets/soy-Brmx7dQM.js +1 -0
- package/public/assets/sparql-rVzFXLq3.js +1 -0
- package/public/assets/splunk-BtCnVYZw.js +1 -0
- package/public/assets/sql-BLtJtn59.js +1 -0
- package/public/assets/ssh-config-_ykCGR6B.js +1 -0
- package/public/assets/stata-BH5u7GGu.js +1 -0
- package/public/assets/stylus-BEDo0Tqx.js +1 -0
- package/public/assets/svelte-3Dk4HxPD.js +1 -0
- package/public/assets/swift-Dg5xB15N.js +1 -0
- package/public/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/public/assets/system-verilog-CnnmHF94.js +1 -0
- package/public/assets/systemd-4A_iFExJ.js +1 -0
- package/public/assets/talonscript-CkByrt1z.js +1 -0
- package/public/assets/tasl-QIJgUcNo.js +1 -0
- package/public/assets/tcl-dwOrl1Do.js +1 -0
- package/public/assets/templ-W15q3VgB.js +1 -0
- package/public/assets/terraform-BETggiCN.js +1 -0
- package/public/assets/tex-Cppo0RY3.js +1 -0
- package/public/assets/todo-_6sSKM36.js +1 -0
- package/public/assets/tokyo-night-hegEt444.js +1 -0
- package/public/assets/toml-vGWfd6FD.js +1 -0
- package/public/assets/tool-call-ChcZ5m6V.js +60 -0
- package/public/assets/ts-tags-zn1MmPIZ.js +1 -0
- package/public/assets/tsv-B_m7g4N7.js +1 -0
- package/public/assets/tsx-COt5Ahok.js +1 -0
- package/public/assets/turtle-BsS91CYL.js +1 -0
- package/public/assets/twig-CO9l9SDP.js +1 -0
- package/public/assets/typescript-BPQ3VLAy.js +1 -0
- package/public/assets/typespec-Df68jz8_.js +1 -0
- package/public/assets/typst-DHCkPAjA.js +1 -0
- package/public/assets/unified-picker-BkweRxZy.js +1 -0
- package/public/assets/v-BcVCzyr7.js +1 -0
- package/public/assets/vala-CsfeWuGM.js +1 -0
- package/public/assets/vb-D17OF-Vu.js +1 -0
- package/public/assets/verilog-BQ8w6xss.js +1 -0
- package/public/assets/vesper-DU1UobuO.js +1 -0
- package/public/assets/vhdl-CeAyd5Ju.js +1 -0
- package/public/assets/viml-CJc9bBzg.js +1 -0
- package/public/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/public/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/public/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/public/assets/vue-CCoi5OLL.js +1 -0
- package/public/assets/vue-html-DAAvJJDi.js +1 -0
- package/public/assets/vue-vine-_Ih-lPRR.js +1 -0
- package/public/assets/vyper-CDx5xZoG.js +1 -0
- package/public/assets/wasm-CG6Dc4jp.js +1 -0
- package/public/assets/wasm-MzD3tlZU.js +1 -0
- package/public/assets/wenyan-BV7otONQ.js +1 -0
- package/public/assets/wgsl-Dx-B1_4e.js +1 -0
- package/public/assets/wikitext-BhOHFoWU.js +1 -0
- package/public/assets/wit-5i3qLPDT.js +1 -0
- package/public/assets/wolfram-lXgVvXCa.js +1 -0
- package/public/assets/wrap-text-DDrpiK4b.js +1 -0
- package/public/assets/xml-sdJ4AIDG.js +1 -0
- package/public/assets/xsl-CtQFsRM5.js +1 -0
- package/public/assets/yaml-Buea-lGh.js +1 -0
- package/public/assets/zenscript-DVFEvuxE.js +1 -0
- package/public/assets/zig-VOosw3JB.js +1 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +44 -0
- package/public/loading.html +33 -0
- package/public/logo.png +0 -0
- package/public/manifest.webmanifest +1 -0
- package/public/maskable-icon-512x512.png +0 -0
- package/public/monaco/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
- package/public/monaco/vs/base/worker/workerMain.js +31 -0
- package/public/monaco/vs/basic-languages/cpp/cpp.js +10 -0
- package/public/monaco/vs/basic-languages/kotlin/kotlin.js +10 -0
- package/public/monaco/vs/basic-languages/markdown/markdown.js +10 -0
- package/public/monaco/vs/basic-languages/python/python.js +10 -0
- package/public/monaco/vs/editor/editor.main.css +8 -0
- package/public/monaco/vs/editor/editor.main.js +798 -0
- package/public/monaco/vs/language/css/cssMode.js +13 -0
- package/public/monaco/vs/language/css/cssWorker.js +77 -0
- package/public/monaco/vs/language/html/htmlMode.js +13 -0
- package/public/monaco/vs/language/html/htmlWorker.js +454 -0
- package/public/monaco/vs/language/json/jsonMode.js +19 -0
- package/public/monaco/vs/language/json/jsonWorker.js +42 -0
- package/public/monaco/vs/language/typescript/tsMode.js +20 -0
- package/public/monaco/vs/language/typescript/tsWorker.js +51328 -0
- package/public/monaco/vs/loader.js +11 -0
- package/public/monaco.worker.js +7 -0
- package/public/pwa-192x192.png +0 -0
- package/public/pwa-512x512.png +0 -0
- package/public/pwa-64x64.png +0 -0
- package/public/registerSW.js +1 -0
- package/public/sw.js +1 -0
- package/public/ui-version.json +3 -0
- package/public/workbox-60d14903.js +1 -0
- package/bin/cli.js +0 -56
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import http from "http"
|
|
2
|
+
import https from "https"
|
|
3
|
+
import { Readable } from "stream"
|
|
4
|
+
|
|
5
|
+
export type PluginEvent = {
|
|
6
|
+
type: string
|
|
7
|
+
properties?: Record<string, unknown>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type EmbeddedCoworkConfig = {
|
|
11
|
+
instanceId: string
|
|
12
|
+
baseUrl: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getEmbeddedCoworkConfig(): EmbeddedCoworkConfig {
|
|
16
|
+
return {
|
|
17
|
+
instanceId: requireEnv("EMBEDDEDCOWORK_INSTANCE_ID"),
|
|
18
|
+
baseUrl: requireEnv("EMBEDDEDCOWORK_BASE_URL"),
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createEmbeddedCoworkRequester(config: EmbeddedCoworkConfig) {
|
|
23
|
+
const rawBaseUrl = (config.baseUrl ?? "").trim()
|
|
24
|
+
const baseUrl = rawBaseUrl.replace(/\/+$/, "")
|
|
25
|
+
const pluginBase = `${baseUrl}/workspaces/${encodeURIComponent(config.instanceId)}/plugin`
|
|
26
|
+
const authorization = buildInstanceAuthorizationHeader()
|
|
27
|
+
|
|
28
|
+
const buildUrl = (path: string) => {
|
|
29
|
+
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
30
|
+
return path
|
|
31
|
+
}
|
|
32
|
+
const normalized = path.startsWith("/") ? path : `/${path}`
|
|
33
|
+
return `${pluginBase}${normalized}`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const buildHeaders = (headers: HeadersInit | undefined, hasBody: boolean): Record<string, string> => {
|
|
37
|
+
const output: Record<string, string> = normalizeHeaders(headers)
|
|
38
|
+
output.Authorization = authorization
|
|
39
|
+
if (hasBody) {
|
|
40
|
+
output["Content-Type"] = output["Content-Type"] ?? "application/json"
|
|
41
|
+
}
|
|
42
|
+
return output
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const fetchWithAuth = async (path: string, init?: RequestInit): Promise<Response> => {
|
|
46
|
+
const url = buildUrl(path)
|
|
47
|
+
const hasBody = init?.body !== undefined
|
|
48
|
+
const headers = buildHeaders(init?.headers, hasBody)
|
|
49
|
+
|
|
50
|
+
// The EmbeddedCowork plugin only talks to the local EmbeddedCowork server.
|
|
51
|
+
// Use a single request implementation that tolerates custom/self-signed certs
|
|
52
|
+
// without disabling TLS verification for the whole Node process.
|
|
53
|
+
return nodeFetch(url, { ...init, headers }, { rejectUnauthorized: false })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const requestJson = async <T>(path: string, init?: RequestInit): Promise<T> => {
|
|
57
|
+
const response = await fetchWithAuth(path, init)
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
const message = await response.text().catch(() => "")
|
|
60
|
+
throw new Error(message || `Request failed with ${response.status}`)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (response.status === 204) {
|
|
64
|
+
return undefined as T
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (await response.json()) as T
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const requestVoid = async (path: string, init?: RequestInit): Promise<void> => {
|
|
71
|
+
const response = await fetchWithAuth(path, init)
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const message = await response.text().catch(() => "")
|
|
74
|
+
throw new Error(message || `Request failed with ${response.status}`)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const requestSseBody = async (path: string): Promise<ReadableStream<Uint8Array>> => {
|
|
79
|
+
const response = await fetchWithAuth(path, { headers: { Accept: "text/event-stream" } })
|
|
80
|
+
if (!response.ok || !response.body) {
|
|
81
|
+
throw new Error(`SSE unavailable (${response.status})`)
|
|
82
|
+
}
|
|
83
|
+
return response.body as ReadableStream<Uint8Array>
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
buildUrl,
|
|
88
|
+
fetch: fetchWithAuth,
|
|
89
|
+
requestJson,
|
|
90
|
+
requestVoid,
|
|
91
|
+
requestSseBody,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function nodeFetch(
|
|
96
|
+
url: string,
|
|
97
|
+
init: RequestInit & { headers?: Record<string, string> },
|
|
98
|
+
tls: { rejectUnauthorized: boolean },
|
|
99
|
+
): Promise<Response> {
|
|
100
|
+
const parsed = new URL(url)
|
|
101
|
+
const isHttps = parsed.protocol === "https:"
|
|
102
|
+
const requestFn = isHttps ? https.request : http.request
|
|
103
|
+
|
|
104
|
+
const method = (init.method ?? "GET").toUpperCase()
|
|
105
|
+
const headers = init.headers ?? {}
|
|
106
|
+
const body = init.body
|
|
107
|
+
|
|
108
|
+
return await new Promise<Response>((resolve, reject) => {
|
|
109
|
+
const req = requestFn(
|
|
110
|
+
{
|
|
111
|
+
protocol: parsed.protocol,
|
|
112
|
+
hostname: parsed.hostname,
|
|
113
|
+
port: parsed.port ? Number(parsed.port) : undefined,
|
|
114
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
115
|
+
method,
|
|
116
|
+
headers,
|
|
117
|
+
...(isHttps ? { rejectUnauthorized: tls.rejectUnauthorized } : {}),
|
|
118
|
+
},
|
|
119
|
+
(res) => {
|
|
120
|
+
const responseHeaders = new Headers()
|
|
121
|
+
for (const [key, value] of Object.entries(res.headers)) {
|
|
122
|
+
if (value === undefined) continue
|
|
123
|
+
if (Array.isArray(value)) {
|
|
124
|
+
responseHeaders.set(key, value.join(", "))
|
|
125
|
+
} else {
|
|
126
|
+
responseHeaders.set(key, String(value))
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Convert Node stream -> Web ReadableStream for Response.
|
|
131
|
+
const webBody = Readable.toWeb(res) as unknown as ReadableStream<Uint8Array>
|
|
132
|
+
resolve(new Response(webBody, { status: res.statusCode ?? 0, headers: responseHeaders }))
|
|
133
|
+
},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
const signal = init.signal
|
|
137
|
+
const abort = () => {
|
|
138
|
+
const err = new Error("Request aborted")
|
|
139
|
+
;(err as any).name = "AbortError"
|
|
140
|
+
req.destroy(err)
|
|
141
|
+
reject(err)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (signal) {
|
|
145
|
+
if (signal.aborted) {
|
|
146
|
+
abort()
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
signal.addEventListener("abort", abort, { once: true })
|
|
150
|
+
req.once("close", () => signal.removeEventListener("abort", abort))
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
req.once("error", reject)
|
|
154
|
+
|
|
155
|
+
if (body === undefined || body === null) {
|
|
156
|
+
req.end()
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (typeof body === "string") {
|
|
161
|
+
req.end(body)
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (body instanceof Uint8Array) {
|
|
166
|
+
req.end(Buffer.from(body))
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (body instanceof ArrayBuffer) {
|
|
171
|
+
req.end(Buffer.from(new Uint8Array(body)))
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Fallback for less common BodyInit types.
|
|
176
|
+
req.end(String(body))
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function requireEnv(key: string): string {
|
|
181
|
+
const value = process.env[key]
|
|
182
|
+
if (!value || !value.trim()) {
|
|
183
|
+
throw new Error(`[EmbeddedCoworkPlugin] Missing required env var ${key}`)
|
|
184
|
+
}
|
|
185
|
+
return value
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function buildInstanceAuthorizationHeader(): string {
|
|
189
|
+
const username = requireEnv("OPENCODE_SERVER_USERNAME")
|
|
190
|
+
const password = requireEnv("OPENCODE_SERVER_PASSWORD")
|
|
191
|
+
const token = Buffer.from(`${username}:${password}`, "utf8").toString("base64")
|
|
192
|
+
return `Basic ${token}`
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function normalizeHeaders(headers: HeadersInit | undefined): Record<string, string> {
|
|
196
|
+
const output: Record<string, string> = {}
|
|
197
|
+
if (!headers) return output
|
|
198
|
+
|
|
199
|
+
if (headers instanceof Headers) {
|
|
200
|
+
headers.forEach((value, key) => {
|
|
201
|
+
output[key] = value
|
|
202
|
+
})
|
|
203
|
+
return output
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (Array.isArray(headers)) {
|
|
207
|
+
for (const [key, value] of headers) {
|
|
208
|
+
output[key] = value
|
|
209
|
+
}
|
|
210
|
+
return output
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return { ...headers }
|
|
214
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import { createLogger } from "./logger";
|
|
3
|
+
import { resolveOpencodeTemplateDir } from "./runtime-paths";
|
|
4
|
+
const log = createLogger({ component: "opencode-config" });
|
|
5
|
+
const templateDir = resolveOpencodeTemplateDir(import.meta.url);
|
|
6
|
+
const isDevBuild = Boolean(process.env.EMBEDDEDCOWORK_DEV ?? process.env.CLI_UI_DEV_SERVER);
|
|
7
|
+
export function getOpencodeConfigDir() {
|
|
8
|
+
if (!existsSync(templateDir)) {
|
|
9
|
+
throw new Error(`EmbeddedCowork Opencode config template missing at ${templateDir}`);
|
|
10
|
+
}
|
|
11
|
+
if (isDevBuild) {
|
|
12
|
+
log.debug({ templateDir }, "Using Opencode config template directly (dev mode)");
|
|
13
|
+
}
|
|
14
|
+
return templateDir;
|
|
15
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { createWriteStream, existsSync, mkdirSync, chmodSync, readFileSync, createReadStream, renameSync } from "fs";
|
|
2
|
+
import { stat, mkdir } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { spawnSync } from "child_process";
|
|
6
|
+
import yauzl from "yauzl";
|
|
7
|
+
import tar from "tar";
|
|
8
|
+
import { BIN_DIR, BINARY_NAME } from "./opencode-paths";
|
|
9
|
+
const GITHUB_API = "https://api.github.com/repos/anomalyco/opencode/releases/latest";
|
|
10
|
+
const GITHUB_DL = "https://github.com/anomalyco/opencode/releases/latest/download";
|
|
11
|
+
export class OpencodeDownloader {
|
|
12
|
+
constructor(logger) {
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
}
|
|
15
|
+
getDownloadTarget() {
|
|
16
|
+
const platform = process.platform;
|
|
17
|
+
const arch = process.arch;
|
|
18
|
+
let osName;
|
|
19
|
+
switch (platform) {
|
|
20
|
+
case "win32":
|
|
21
|
+
osName = "windows";
|
|
22
|
+
break;
|
|
23
|
+
case "darwin":
|
|
24
|
+
osName = "darwin";
|
|
25
|
+
break;
|
|
26
|
+
case "linux":
|
|
27
|
+
osName = "linux";
|
|
28
|
+
break;
|
|
29
|
+
default:
|
|
30
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
31
|
+
}
|
|
32
|
+
let archName;
|
|
33
|
+
switch (arch) {
|
|
34
|
+
case "x64":
|
|
35
|
+
archName = "x64";
|
|
36
|
+
break;
|
|
37
|
+
case "arm64":
|
|
38
|
+
archName = "arm64";
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
throw new Error(`Unsupported architecture: ${arch}`);
|
|
42
|
+
}
|
|
43
|
+
let target = `${osName}-${archName}`;
|
|
44
|
+
if (arch === "x64" && !this.hasAvx2()) {
|
|
45
|
+
target += "-baseline";
|
|
46
|
+
}
|
|
47
|
+
if (platform === "linux" && this.isMusl()) {
|
|
48
|
+
target += "-musl";
|
|
49
|
+
}
|
|
50
|
+
const archiveExt = platform === "linux" ? ".tar.gz" : ".zip";
|
|
51
|
+
return { filename: `opencode-${target}${archiveExt}`, archiveExt };
|
|
52
|
+
}
|
|
53
|
+
hasAvx2() {
|
|
54
|
+
if (process.platform === "win32") {
|
|
55
|
+
try {
|
|
56
|
+
const ps = `(Add-Type -MemberDefinition "[DllImport(\"kernel32.dll\")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)`;
|
|
57
|
+
const result = spawnSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", ps], { encoding: "utf8", timeout: 5000 });
|
|
58
|
+
const out = result.stdout?.trim()?.toLowerCase();
|
|
59
|
+
return out === "true" || out === "1";
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (process.platform === "darwin") {
|
|
66
|
+
try {
|
|
67
|
+
const result = spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { encoding: "utf8" });
|
|
68
|
+
return result.status === 0 && result.stdout.trim() === "1";
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (process.platform === "linux") {
|
|
75
|
+
try {
|
|
76
|
+
const cpuinfo = readFileSync("/proc/cpuinfo", "utf8");
|
|
77
|
+
return /avx2/i.test(cpuinfo);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
isMusl() {
|
|
86
|
+
try {
|
|
87
|
+
const result = spawnSync("ldd", ["--version"], { encoding: "utf8" });
|
|
88
|
+
return result.stderr?.toLowerCase().includes("musl") ?? false;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return existsSync("/etc/alpine-release");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async getLatestVersion() {
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
if (OpencodeDownloader.versionCache && now < OpencodeDownloader.versionCache.expiry) {
|
|
97
|
+
return OpencodeDownloader.versionCache.version;
|
|
98
|
+
}
|
|
99
|
+
const res = await fetch(GITHUB_API, {
|
|
100
|
+
headers: { Accept: "application/json", "User-Agent": "embeddedcowork" },
|
|
101
|
+
signal: AbortSignal.timeout(10000),
|
|
102
|
+
});
|
|
103
|
+
if (!res.ok)
|
|
104
|
+
throw new Error(`Failed to fetch latest version: HTTP ${res.status}`);
|
|
105
|
+
const data = (await res.json());
|
|
106
|
+
const version = data.tag_name.replace(/^v/, "");
|
|
107
|
+
OpencodeDownloader.versionCache = { version, expiry: now + 30000 };
|
|
108
|
+
return version;
|
|
109
|
+
}
|
|
110
|
+
async downloadWithRetry(url, dest, progressCb, retries = 5) {
|
|
111
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
112
|
+
let writer = null;
|
|
113
|
+
try {
|
|
114
|
+
const res = await fetch(url);
|
|
115
|
+
if (!res.ok)
|
|
116
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
117
|
+
const total = Number(res.headers.get("content-length") ?? 0);
|
|
118
|
+
let current = 0;
|
|
119
|
+
const reader = res.body?.getReader();
|
|
120
|
+
if (!reader)
|
|
121
|
+
throw new Error("Response body is not readable");
|
|
122
|
+
writer = createWriteStream(dest);
|
|
123
|
+
while (true) {
|
|
124
|
+
const { done, value } = await reader.read();
|
|
125
|
+
if (done)
|
|
126
|
+
break;
|
|
127
|
+
current += value.length;
|
|
128
|
+
progressCb?.(current, total);
|
|
129
|
+
writer.write(value);
|
|
130
|
+
}
|
|
131
|
+
await new Promise((resolve, reject) => {
|
|
132
|
+
writer.end((err) => (err ? reject(err) : resolve()));
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
if (writer) {
|
|
138
|
+
writer.destroy();
|
|
139
|
+
writer = null;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
import("fs/promises").then((fs) => fs.unlink(dest)).catch(() => { });
|
|
143
|
+
}
|
|
144
|
+
catch { }
|
|
145
|
+
if (attempt === retries)
|
|
146
|
+
throw err;
|
|
147
|
+
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 30000);
|
|
148
|
+
this.logger?.warn({ attempt, retries, err }, `Download attempt ${attempt} failed, retrying in ${delay}ms`);
|
|
149
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async extractZip(zipPath, outDir) {
|
|
154
|
+
return new Promise((resolve, reject) => {
|
|
155
|
+
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
156
|
+
if (err || !zipfile)
|
|
157
|
+
return reject(err ?? new Error("Failed to open zip"));
|
|
158
|
+
zipfile.readEntry();
|
|
159
|
+
zipfile.on("entry", (entry) => {
|
|
160
|
+
if (entry.fileName === BINARY_NAME || entry.fileName.endsWith(`/${BINARY_NAME}`)) {
|
|
161
|
+
zipfile.openReadStream(entry, (err2, readStream) => {
|
|
162
|
+
if (err2 || !readStream)
|
|
163
|
+
return reject(err2 ?? new Error("Failed to open read stream"));
|
|
164
|
+
const tmpPath = path.join(outDir, BINARY_NAME + ".tmp");
|
|
165
|
+
const destPath = path.join(outDir, BINARY_NAME);
|
|
166
|
+
const writer = createWriteStream(tmpPath);
|
|
167
|
+
readStream.pipe(writer);
|
|
168
|
+
writer.on("finish", () => {
|
|
169
|
+
chmodSync(tmpPath, 0o755);
|
|
170
|
+
renameSync(tmpPath, destPath);
|
|
171
|
+
resolve();
|
|
172
|
+
});
|
|
173
|
+
writer.on("error", (e) => {
|
|
174
|
+
try {
|
|
175
|
+
import("fs/promises").then((fs) => fs.unlink(tmpPath)).catch(() => { });
|
|
176
|
+
}
|
|
177
|
+
catch { }
|
|
178
|
+
reject(e);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
zipfile.readEntry();
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
zipfile.on("error", reject);
|
|
187
|
+
zipfile.on("end", () => reject(new Error("Binary not found in archive")));
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async extractTarGz(tarPath, outDir) {
|
|
192
|
+
const extractPath = path.join(outDir, "extracted");
|
|
193
|
+
await mkdir(extractPath, { recursive: true });
|
|
194
|
+
await tar.extract({
|
|
195
|
+
file: tarPath,
|
|
196
|
+
cwd: extractPath,
|
|
197
|
+
filter: (filePath) => filePath === BINARY_NAME || filePath.endsWith(`/${BINARY_NAME}`),
|
|
198
|
+
});
|
|
199
|
+
const binarySrc = path.join(extractPath, BINARY_NAME);
|
|
200
|
+
const tmpPath = path.join(outDir, BINARY_NAME + ".tmp");
|
|
201
|
+
const binaryDest = path.join(outDir, BINARY_NAME);
|
|
202
|
+
await new Promise((resolve, reject) => {
|
|
203
|
+
const src = createReadStream(binarySrc);
|
|
204
|
+
const dst = createWriteStream(tmpPath);
|
|
205
|
+
src.pipe(dst);
|
|
206
|
+
dst.on("finish", () => {
|
|
207
|
+
chmodSync(tmpPath, 0o755);
|
|
208
|
+
renameSync(tmpPath, binaryDest);
|
|
209
|
+
resolve();
|
|
210
|
+
});
|
|
211
|
+
dst.on("error", (e) => {
|
|
212
|
+
try {
|
|
213
|
+
import("fs/promises").then((fs) => fs.unlink(tmpPath)).catch(() => { });
|
|
214
|
+
}
|
|
215
|
+
catch { }
|
|
216
|
+
reject(e);
|
|
217
|
+
});
|
|
218
|
+
src.on("error", (e) => {
|
|
219
|
+
try {
|
|
220
|
+
import("fs/promises").then((fs) => fs.unlink(tmpPath)).catch(() => { });
|
|
221
|
+
}
|
|
222
|
+
catch { }
|
|
223
|
+
reject(e);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
getInstalledPath() {
|
|
228
|
+
return path.join(BIN_DIR, BINARY_NAME);
|
|
229
|
+
}
|
|
230
|
+
async verifyBinary(binaryPath) {
|
|
231
|
+
if (!existsSync(binaryPath))
|
|
232
|
+
return null;
|
|
233
|
+
try {
|
|
234
|
+
const result = spawnSync(binaryPath, ["--version"], { encoding: "utf8", timeout: 5000 });
|
|
235
|
+
if (result.status === 0) {
|
|
236
|
+
return (result.stdout ?? result.stderr ?? "").trim();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch { }
|
|
240
|
+
this.logger?.warn({ path: binaryPath }, "Binary exists but --version failed, proceeding anyway");
|
|
241
|
+
return binaryPath;
|
|
242
|
+
}
|
|
243
|
+
async ensureDownloaded(statusCb) {
|
|
244
|
+
const target = this.getDownloadTarget();
|
|
245
|
+
const binaryPath = this.getInstalledPath();
|
|
246
|
+
const existingVersion = await this.verifyBinary(binaryPath);
|
|
247
|
+
try {
|
|
248
|
+
const latestVersion = await this.getLatestVersion();
|
|
249
|
+
if (existingVersion && existingVersion.includes(latestVersion)) {
|
|
250
|
+
this.logger?.info({ version: latestVersion, path: binaryPath }, "OpenCode binary is up to date");
|
|
251
|
+
return binaryPath;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
if (existingVersion) {
|
|
256
|
+
this.logger?.warn({ err }, "Failed to check latest version, using existing binary");
|
|
257
|
+
return binaryPath;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
mkdirSync(BIN_DIR, { recursive: true });
|
|
261
|
+
const tmpDir = path.join(os.tmpdir(), "embeddedcowork-dl");
|
|
262
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
263
|
+
const archivePath = path.join(tmpDir, target.filename);
|
|
264
|
+
const downloadUrl = `${GITHUB_DL}/${target.filename}`;
|
|
265
|
+
try {
|
|
266
|
+
statusCb?.({ type: "downloading", progress: { current: 0, total: 0 } });
|
|
267
|
+
await this.downloadWithRetry(downloadUrl, archivePath, (current, total) => {
|
|
268
|
+
statusCb?.({ type: "downloading", progress: { current, total } });
|
|
269
|
+
});
|
|
270
|
+
statusCb?.({ type: "extracting" });
|
|
271
|
+
if (target.archiveExt === ".zip") {
|
|
272
|
+
await this.extractZip(archivePath, BIN_DIR);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
await this.extractTarGz(archivePath, BIN_DIR);
|
|
276
|
+
}
|
|
277
|
+
statusCb?.({ type: "verifying" });
|
|
278
|
+
const version = await this.verifyBinary(binaryPath);
|
|
279
|
+
if (!version) {
|
|
280
|
+
throw new Error("Downloaded binary failed verification");
|
|
281
|
+
}
|
|
282
|
+
this.logger?.info({ version, path: binaryPath }, "OpenCode binary downloaded and verified");
|
|
283
|
+
statusCb?.({ type: "completed", binaryPath });
|
|
284
|
+
return binaryPath;
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
try {
|
|
288
|
+
await stat(archivePath);
|
|
289
|
+
await import("fs/promises").then((fs) => fs.unlink(archivePath));
|
|
290
|
+
}
|
|
291
|
+
catch { }
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
OpencodeDownloader.versionCache = null;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { spawnSync } from "child_process";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { OpencodeDownloader } from "./opencode-downloader";
|
|
6
|
+
// ── Path constants ──────────────────────────────────────────
|
|
7
|
+
export const BIN_DIR = path.join(os.homedir(), ".embeddedcowork", "bin");
|
|
8
|
+
export const BINARY_NAME = process.platform === "win32" ? "opencode.exe" : "opencode";
|
|
9
|
+
export const INSTALLED_BINARY_PATH = path.join(BIN_DIR, BINARY_NAME);
|
|
10
|
+
// ── Shell helpers ───────────────────────────────────────────
|
|
11
|
+
function defaultShellPath() {
|
|
12
|
+
const configured = process.env.SHELL?.trim();
|
|
13
|
+
if (configured)
|
|
14
|
+
return configured;
|
|
15
|
+
return process.platform === "darwin" ? "/bin/zsh" : "/bin/bash";
|
|
16
|
+
}
|
|
17
|
+
function shellEscape(input) {
|
|
18
|
+
if (!input)
|
|
19
|
+
return "''";
|
|
20
|
+
return `'${input.replace(/'/g, `'\\''`)}'`;
|
|
21
|
+
}
|
|
22
|
+
function wrapCommandForShell(command, shellPath) {
|
|
23
|
+
const shellName = path.basename(shellPath).toLowerCase();
|
|
24
|
+
if (shellName.includes("bash")) {
|
|
25
|
+
return `if [ -f ~/.bashrc ]; then source ~/.bashrc >/dev/null 2>&1; fi; ${command}`;
|
|
26
|
+
}
|
|
27
|
+
if (shellName.includes("zsh")) {
|
|
28
|
+
return `if [ -f ~/.zshrc ]; then source ~/.zshrc >/dev/null 2>&1; fi; ${command}`;
|
|
29
|
+
}
|
|
30
|
+
return command;
|
|
31
|
+
}
|
|
32
|
+
function buildShellArgs(shellPath, command) {
|
|
33
|
+
const shellName = path.basename(shellPath).toLowerCase();
|
|
34
|
+
if (shellName.includes("zsh"))
|
|
35
|
+
return ["-l", "-i", "-c", command];
|
|
36
|
+
return ["-l", "-c", command];
|
|
37
|
+
}
|
|
38
|
+
// ── Binary lookup ───────────────────────────────────────────
|
|
39
|
+
export function resolveBinaryPathFromUserShell(identifier) {
|
|
40
|
+
if (process.platform === "win32")
|
|
41
|
+
return null;
|
|
42
|
+
const shellPath = defaultShellPath();
|
|
43
|
+
const lookupCommand = wrapCommandForShell(`command -v ${shellEscape(identifier)}`, shellPath);
|
|
44
|
+
const result = spawnSync(shellPath, buildShellArgs(shellPath, lookupCommand), {
|
|
45
|
+
encoding: "utf8",
|
|
46
|
+
env: {
|
|
47
|
+
...process.env,
|
|
48
|
+
npm_config_prefix: undefined,
|
|
49
|
+
NPM_CONFIG_PREFIX: undefined,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
if (result.status !== 0)
|
|
53
|
+
return null;
|
|
54
|
+
const resolved = String(result.stdout ?? "")
|
|
55
|
+
.split(/\r?\n/)
|
|
56
|
+
.map((line) => line.trim())
|
|
57
|
+
.find((line) => line.length > 0);
|
|
58
|
+
return resolved ?? null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolve an opencode binary identifier to an absolute path.
|
|
62
|
+
* Order: absolute/relative → which/where → shell command -v → installed path → raw identifier
|
|
63
|
+
*/
|
|
64
|
+
export function resolveBinary(identifier) {
|
|
65
|
+
if (!identifier)
|
|
66
|
+
return identifier;
|
|
67
|
+
if (path.isAbsolute(identifier) || identifier.includes("/") || identifier.includes("\\") || identifier.startsWith(".")) {
|
|
68
|
+
return identifier;
|
|
69
|
+
}
|
|
70
|
+
const locator = process.platform === "win32" ? "where" : "which";
|
|
71
|
+
try {
|
|
72
|
+
const result = spawnSync(locator, [identifier], { encoding: "utf8" });
|
|
73
|
+
if (result.status === 0 && result.stdout) {
|
|
74
|
+
const candidates = result.stdout
|
|
75
|
+
.split(/\r?\n/)
|
|
76
|
+
.map((line) => line.trim())
|
|
77
|
+
.filter((line) => line.length > 0)
|
|
78
|
+
.filter((line) => !/^INFO:/i.test(line));
|
|
79
|
+
if (candidates.length > 0) {
|
|
80
|
+
return candidates[0];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch { }
|
|
85
|
+
const shellResolved = resolveBinaryPathFromUserShell(identifier);
|
|
86
|
+
if (shellResolved)
|
|
87
|
+
return shellResolved;
|
|
88
|
+
if (existsSync(INSTALLED_BINARY_PATH))
|
|
89
|
+
return INSTALLED_BINARY_PATH;
|
|
90
|
+
return identifier;
|
|
91
|
+
}
|
|
92
|
+
let downloadPhase = "idle";
|
|
93
|
+
let downloadProgress = { current: 0, total: 0 };
|
|
94
|
+
let downloadError;
|
|
95
|
+
function setDownloadPhase(phase) {
|
|
96
|
+
downloadPhase = phase;
|
|
97
|
+
if (phase === "idle" || phase === "completed" || phase === "error") {
|
|
98
|
+
downloadProgress = { current: 0, total: 0 };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function getDownloadProgress() {
|
|
102
|
+
return { ...downloadProgress };
|
|
103
|
+
}
|
|
104
|
+
export function getDownloadPhase() {
|
|
105
|
+
return downloadPhase;
|
|
106
|
+
}
|
|
107
|
+
export function getDownloadError() {
|
|
108
|
+
return downloadError;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check whether opencode is available on this system.
|
|
112
|
+
* Uses the same lookup order as resolveBinary("opencode").
|
|
113
|
+
* Returns additional download progress info when not available.
|
|
114
|
+
*/
|
|
115
|
+
export function getOpencodeStatus() {
|
|
116
|
+
const resolved = resolveBinary("opencode");
|
|
117
|
+
const available = resolved !== "opencode" && existsSync(resolved);
|
|
118
|
+
if (available)
|
|
119
|
+
return { available: true };
|
|
120
|
+
return {
|
|
121
|
+
available: false,
|
|
122
|
+
status: downloadPhase,
|
|
123
|
+
progress: downloadPhase === "downloading" ? { ...downloadProgress } : undefined,
|
|
124
|
+
error: downloadError,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export function isBinaryAvailable() {
|
|
128
|
+
const resolved = resolveBinary("opencode");
|
|
129
|
+
return resolved !== "opencode" && existsSync(resolved);
|
|
130
|
+
}
|
|
131
|
+
// ── Download trigger (global singleton) ─────────────────────
|
|
132
|
+
let downloadPromise = null;
|
|
133
|
+
export function triggerBinaryDownload(logger) {
|
|
134
|
+
if (downloadPromise)
|
|
135
|
+
return downloadPromise;
|
|
136
|
+
setDownloadPhase("downloading");
|
|
137
|
+
downloadError = undefined;
|
|
138
|
+
const downloader = new OpencodeDownloader(logger);
|
|
139
|
+
downloadPromise = downloader
|
|
140
|
+
.ensureDownloaded((status) => {
|
|
141
|
+
if (status.type === "downloading") {
|
|
142
|
+
downloadPhase = "downloading";
|
|
143
|
+
downloadProgress = status.progress;
|
|
144
|
+
}
|
|
145
|
+
else if (status.type === "extracting") {
|
|
146
|
+
setDownloadPhase("extracting");
|
|
147
|
+
}
|
|
148
|
+
else if (status.type === "verifying") {
|
|
149
|
+
setDownloadPhase("verifying");
|
|
150
|
+
}
|
|
151
|
+
else if (status.type === "completed") {
|
|
152
|
+
setDownloadPhase("completed");
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
.then(() => {
|
|
156
|
+
logger.info("OpenCode auto-download completed");
|
|
157
|
+
setDownloadPhase("completed");
|
|
158
|
+
downloadPromise = null;
|
|
159
|
+
})
|
|
160
|
+
.catch((err) => {
|
|
161
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
162
|
+
logger.error({ err }, "OpenCode auto-download failed");
|
|
163
|
+
downloadPhase = "error";
|
|
164
|
+
downloadError = message;
|
|
165
|
+
});
|
|
166
|
+
return downloadPromise;
|
|
167
|
+
}
|