@vividcodeai/embeddedcowork-dev 0.0.3-dev-20260507-b76190e8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -0
- 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 +71 -0
- package/dist/events/bus.js +45 -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 +466 -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 +3 -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/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/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 +996 -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/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 +420 -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 +57 -0
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/assets/ChangesTab-C2DJXDf9.js +2 -0
- package/public/assets/DiffToolbar-De-3SRCF.js +1 -0
- package/public/assets/EmbeddedCowork-Icon-DSw5nKk7.png +0 -0
- package/public/assets/FilesTab-BuQ00MEc.js +2 -0
- package/public/assets/GitChangesTab-D9bf2jkM.js +2 -0
- package/public/assets/SplitFilePanel-B-3h60o2.js +1 -0
- package/public/assets/StatusTab-D5s19fRN.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-CdNbUxmo.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-B1l_VZc1.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/event-DjZVAIBO.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-CSgooKT_.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-B2LsA7hD.js +1 -0
- package/public/assets/index-BErmCqgL.js +1 -0
- package/public/assets/index-BKMyzTSR.js +1 -0
- package/public/assets/index-BKvZBimW.js +1 -0
- package/public/assets/index-BQeBs108.js +1 -0
- package/public/assets/index-BqQARTCd.js +1 -0
- package/public/assets/index-C9Tl2tHH.js +2 -0
- package/public/assets/index-CLSJ4cO9.js +1 -0
- package/public/assets/index-DElsPAzQ.css +1 -0
- package/public/assets/index-ixx_g9gD.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-CQjaT4lJ.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-C1yBw4P8.js +53 -0
- package/public/assets/make-CHLpvVh8.js +1 -0
- package/public/assets/markdown-Cvjx9yec.js +1 -0
- package/public/assets/markdown-D5eIdNMf.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-9Byc1Kpy.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-uxdyLWei.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-C_JEoVSV.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-ePaJEYDm.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-S8HH4qqP.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
|
@@ -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,40 @@
|
|
|
1
|
+
export class PluginChannelManager {
|
|
2
|
+
constructor(logger) {
|
|
3
|
+
this.logger = logger;
|
|
4
|
+
this.clients = new Set();
|
|
5
|
+
}
|
|
6
|
+
register(workspaceId, reply) {
|
|
7
|
+
const connection = { workspaceId, reply };
|
|
8
|
+
this.clients.add(connection);
|
|
9
|
+
this.logger.debug({ workspaceId }, "Plugin SSE client connected");
|
|
10
|
+
let closed = false;
|
|
11
|
+
const close = () => {
|
|
12
|
+
if (closed)
|
|
13
|
+
return;
|
|
14
|
+
closed = true;
|
|
15
|
+
this.clients.delete(connection);
|
|
16
|
+
this.logger.debug({ workspaceId }, "Plugin SSE client disconnected");
|
|
17
|
+
};
|
|
18
|
+
return { close };
|
|
19
|
+
}
|
|
20
|
+
send(workspaceId, event) {
|
|
21
|
+
for (const client of this.clients) {
|
|
22
|
+
if (client.workspaceId !== workspaceId)
|
|
23
|
+
continue;
|
|
24
|
+
this.write(client.reply, event);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
broadcast(event) {
|
|
28
|
+
for (const client of this.clients) {
|
|
29
|
+
this.write(client.reply, event);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
write(reply, event) {
|
|
33
|
+
try {
|
|
34
|
+
reply.raw.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
this.logger.warn({ err: error }, "Failed to write plugin SSE event");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function handlePluginEvent(workspaceId, event, deps) {
|
|
2
|
+
switch (event.type) {
|
|
3
|
+
case "embeddedcowork.pong":
|
|
4
|
+
deps.logger.debug({ workspaceId, properties: event.properties }, "Plugin pong received");
|
|
5
|
+
return;
|
|
6
|
+
default:
|
|
7
|
+
deps.logger.debug({ workspaceId, eventType: event.type }, "Unhandled plugin event");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function buildPingEvent() {
|
|
11
|
+
return {
|
|
12
|
+
type: "embeddedcowork.ping",
|
|
13
|
+
properties: {
|
|
14
|
+
ts: Date.now(),
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export class VoiceModeManager {
|
|
2
|
+
constructor(options) {
|
|
3
|
+
this.options = options;
|
|
4
|
+
this.enabledConnectionsByInstance = new Map();
|
|
5
|
+
this.aggregateByInstance = new Map();
|
|
6
|
+
this.options.connections.subscribe((event) => {
|
|
7
|
+
if (event.type !== "disconnected")
|
|
8
|
+
return;
|
|
9
|
+
this.clearConnection(event.connection);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
setEnabled(instanceId, connection, enabled) {
|
|
13
|
+
if (enabled && !this.options.connections.isConnected(connection)) {
|
|
14
|
+
this.options.logger.debug({ instanceId, clientId: connection.clientId, connectionId: connection.connectionId }, "Ignoring voice mode enable for disconnected client connection");
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const key = getConnectionKey(connection);
|
|
18
|
+
const current = this.enabledConnectionsByInstance.get(instanceId) ?? new Set();
|
|
19
|
+
if (enabled) {
|
|
20
|
+
current.add(key);
|
|
21
|
+
this.enabledConnectionsByInstance.set(instanceId, current);
|
|
22
|
+
}
|
|
23
|
+
else if (current.delete(key)) {
|
|
24
|
+
if (current.size === 0) {
|
|
25
|
+
this.enabledConnectionsByInstance.delete(instanceId);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.enabledConnectionsByInstance.set(instanceId, current);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
this.options.logger.debug({ instanceId, clientId: connection.clientId, connectionId: connection.connectionId, enabled }, "Voice mode updated for client connection");
|
|
32
|
+
this.publishIfChanged(instanceId);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
syncInstance(instanceId) {
|
|
36
|
+
this.options.channel.send(instanceId, buildVoiceModeEvent(this.isEnabled(instanceId)));
|
|
37
|
+
}
|
|
38
|
+
isEnabled(instanceId) {
|
|
39
|
+
return this.aggregateByInstance.get(instanceId) === true;
|
|
40
|
+
}
|
|
41
|
+
clearConnection(connection) {
|
|
42
|
+
const key = getConnectionKey(connection);
|
|
43
|
+
for (const [instanceId, enabledConnections] of Array.from(this.enabledConnectionsByInstance.entries())) {
|
|
44
|
+
if (!enabledConnections.delete(key))
|
|
45
|
+
continue;
|
|
46
|
+
if (enabledConnections.size === 0) {
|
|
47
|
+
this.enabledConnectionsByInstance.delete(instanceId);
|
|
48
|
+
}
|
|
49
|
+
this.publishIfChanged(instanceId);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
publishIfChanged(instanceId) {
|
|
53
|
+
const enabled = (this.enabledConnectionsByInstance.get(instanceId)?.size ?? 0) > 0;
|
|
54
|
+
const previous = this.aggregateByInstance.get(instanceId) === true;
|
|
55
|
+
if (enabled === previous)
|
|
56
|
+
return;
|
|
57
|
+
if (enabled) {
|
|
58
|
+
this.aggregateByInstance.set(instanceId, true);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.aggregateByInstance.delete(instanceId);
|
|
62
|
+
}
|
|
63
|
+
this.options.logger.debug({ instanceId, enabled }, "Broadcasting aggregate voice mode");
|
|
64
|
+
this.options.channel.send(instanceId, buildVoiceModeEvent(enabled));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function buildVoiceModeEvent(enabled) {
|
|
68
|
+
return {
|
|
69
|
+
type: "embeddedcowork.voiceMode",
|
|
70
|
+
properties: {
|
|
71
|
+
enabled,
|
|
72
|
+
formatVersion: "v1",
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function getConnectionKey(connection) {
|
|
77
|
+
return `${connection.clientId}:${connection.connectionId}`;
|
|
78
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { fetch } from "undici";
|
|
2
|
+
import { compareVersionStrings, stripTagPrefix } from "./release-monitor";
|
|
3
|
+
const DEFAULT_POLL_INTERVAL_MS = 15 * 60 * 1000;
|
|
4
|
+
export function startDevReleaseMonitor(options) {
|
|
5
|
+
let stopped = false;
|
|
6
|
+
let timer = null;
|
|
7
|
+
const pollIntervalMs = Number.isFinite(options.pollIntervalMs) && (options.pollIntervalMs ?? 0) > 0
|
|
8
|
+
? options.pollIntervalMs
|
|
9
|
+
: DEFAULT_POLL_INTERVAL_MS;
|
|
10
|
+
const refresh = async () => {
|
|
11
|
+
if (stopped)
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
const release = await fetchLatestPrerelease({
|
|
15
|
+
repo: options.repo,
|
|
16
|
+
currentVersion: options.currentVersion,
|
|
17
|
+
});
|
|
18
|
+
options.onUpdate(release);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
options.logger.debug({ err: error }, "Failed to refresh dev prerelease information");
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
void refresh();
|
|
25
|
+
timer = setInterval(() => void refresh(), pollIntervalMs);
|
|
26
|
+
return {
|
|
27
|
+
stop() {
|
|
28
|
+
stopped = true;
|
|
29
|
+
if (timer) {
|
|
30
|
+
clearInterval(timer);
|
|
31
|
+
timer = null;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function fetchLatestPrerelease(args) {
|
|
37
|
+
const normalizedRepo = args.repo.trim();
|
|
38
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(normalizedRepo)) {
|
|
39
|
+
throw new Error(`Invalid GitHub repo: ${args.repo}`);
|
|
40
|
+
}
|
|
41
|
+
const apiUrl = `https://api.github.com/repos/${normalizedRepo}/releases?per_page=20`;
|
|
42
|
+
const response = await fetch(apiUrl, {
|
|
43
|
+
headers: {
|
|
44
|
+
Accept: "application/vnd.github+json",
|
|
45
|
+
"User-Agent": "EmbeddedCowork-CLI",
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new Error(`GitHub releases API responded with ${response.status}`);
|
|
50
|
+
}
|
|
51
|
+
const list = (await response.json());
|
|
52
|
+
const latest = list.find((r) => r && r.prerelease === true && r.draft !== true);
|
|
53
|
+
if (!latest) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const tag = latest.tag_name || latest.name;
|
|
57
|
+
if (!tag) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const normalizedVersion = stripTagPrefix(tag);
|
|
61
|
+
if (!normalizedVersion) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
if (compareVersionStrings(normalizedVersion, args.currentVersion) <= 0) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
version: normalizedVersion,
|
|
69
|
+
tag,
|
|
70
|
+
url: latest.html_url ?? `https://github.com/${normalizedRepo}/releases/tag/${encodeURIComponent(tag)}`,
|
|
71
|
+
channel: "dev",
|
|
72
|
+
publishedAt: latest.published_at ?? latest.created_at,
|
|
73
|
+
notes: latest.body,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { fetch } from "undici";
|
|
2
|
+
const RELEASES_API_URL = "https://api.github.com/repos/vividcode-ai/EmbeddedCowork/releases/latest";
|
|
3
|
+
export function startReleaseMonitor(options) {
|
|
4
|
+
let stopped = false;
|
|
5
|
+
const refreshRelease = async () => {
|
|
6
|
+
if (stopped)
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
const release = await fetchLatestRelease(options);
|
|
10
|
+
options.onUpdate(release);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
options.logger.warn({ err: error }, "Failed to refresh release information");
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
void refreshRelease();
|
|
17
|
+
return {
|
|
18
|
+
stop() {
|
|
19
|
+
stopped = true;
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function compareVersionStrings(a, b) {
|
|
24
|
+
const left = parseVersion(a);
|
|
25
|
+
const right = parseVersion(b);
|
|
26
|
+
return compareVersions(left, right);
|
|
27
|
+
}
|
|
28
|
+
async function fetchLatestRelease(options) {
|
|
29
|
+
const response = await fetch(RELEASES_API_URL, {
|
|
30
|
+
headers: {
|
|
31
|
+
Accept: "application/vnd.github+json",
|
|
32
|
+
"User-Agent": "EmbeddedCowork-CLI",
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error(`Release API responded with ${response.status}`);
|
|
37
|
+
}
|
|
38
|
+
const json = (await response.json());
|
|
39
|
+
const tagFromServer = json.tag_name || json.name;
|
|
40
|
+
if (!tagFromServer) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const normalizedVersion = stripTagPrefix(tagFromServer);
|
|
44
|
+
if (!normalizedVersion) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const current = parseVersion(options.currentVersion);
|
|
48
|
+
const remote = parseVersion(normalizedVersion);
|
|
49
|
+
if (compareVersions(remote, current) <= 0) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
version: normalizedVersion,
|
|
54
|
+
tag: tagFromServer,
|
|
55
|
+
url: json.html_url ?? `https://github.com/vividcode-ai/EmbeddedCowork/releases/tag/${encodeURIComponent(tagFromServer)}`,
|
|
56
|
+
channel: json.prerelease || normalizedVersion.includes("-") ? "dev" : "stable",
|
|
57
|
+
publishedAt: json.published_at ?? json.created_at,
|
|
58
|
+
notes: json.body,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function stripTagPrefix(tag) {
|
|
62
|
+
if (!tag)
|
|
63
|
+
return null;
|
|
64
|
+
const trimmed = tag.trim();
|
|
65
|
+
if (!trimmed)
|
|
66
|
+
return null;
|
|
67
|
+
return trimmed.replace(/^v/i, "");
|
|
68
|
+
}
|
|
69
|
+
function parseVersion(value) {
|
|
70
|
+
const normalized = stripTagPrefix(value) ?? "0.0.0";
|
|
71
|
+
const dashIndex = normalized.indexOf("-");
|
|
72
|
+
const core = dashIndex >= 0 ? normalized.slice(0, dashIndex) : normalized;
|
|
73
|
+
const prerelease = dashIndex >= 0 ? normalized.slice(dashIndex + 1) : null;
|
|
74
|
+
const [major = 0, minor = 0, patch = 0] = core.split(".").map((segment) => {
|
|
75
|
+
const parsed = Number.parseInt(segment, 10);
|
|
76
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
major,
|
|
80
|
+
minor,
|
|
81
|
+
patch,
|
|
82
|
+
prerelease,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function compareVersions(a, b) {
|
|
86
|
+
if (a.major !== b.major) {
|
|
87
|
+
return a.major > b.major ? 1 : -1;
|
|
88
|
+
}
|
|
89
|
+
if (a.minor !== b.minor) {
|
|
90
|
+
return a.minor > b.minor ? 1 : -1;
|
|
91
|
+
}
|
|
92
|
+
if (a.patch !== b.patch) {
|
|
93
|
+
return a.patch > b.patch ? 1 : -1;
|
|
94
|
+
}
|
|
95
|
+
const aPre = a.prerelease && a.prerelease.length > 0 ? a.prerelease : null;
|
|
96
|
+
const bPre = b.prerelease && b.prerelease.length > 0 ? b.prerelease : null;
|
|
97
|
+
if (aPre === bPre) {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
if (!aPre) {
|
|
101
|
+
return 1;
|
|
102
|
+
}
|
|
103
|
+
if (!bPre) {
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
return aPre.localeCompare(bPre);
|
|
107
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
function safeModuleDir(importMetaUrl) {
|
|
5
|
+
try {
|
|
6
|
+
return path.dirname(fileURLToPath(importMetaUrl));
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function firstExistingPath(candidates, predicate) {
|
|
13
|
+
for (const candidate of candidates) {
|
|
14
|
+
if (!candidate)
|
|
15
|
+
continue;
|
|
16
|
+
if (predicate(candidate)) {
|
|
17
|
+
return candidate;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export function getPackagedDistDir() {
|
|
23
|
+
return path.dirname(process.execPath);
|
|
24
|
+
}
|
|
25
|
+
export function resolveServerPackageRoot(importMetaUrl) {
|
|
26
|
+
const moduleDir = safeModuleDir(importMetaUrl);
|
|
27
|
+
const configuredRoot = process.env.EMBEDDEDCOWORK_SERVER_ROOT?.trim();
|
|
28
|
+
const candidates = [
|
|
29
|
+
configuredRoot ? path.resolve(configuredRoot) : null,
|
|
30
|
+
moduleDir ? path.resolve(moduleDir, "..") : null,
|
|
31
|
+
path.resolve(getPackagedDistDir(), ".."),
|
|
32
|
+
];
|
|
33
|
+
return (firstExistingPath(candidates, (value) => fs.existsSync(path.join(value, "package.json"))) ??
|
|
34
|
+
candidates.find((value) => Boolean(value)) ??
|
|
35
|
+
process.cwd());
|
|
36
|
+
}
|
|
37
|
+
export function resolveServerPublicDir(importMetaUrl) {
|
|
38
|
+
const moduleDir = safeModuleDir(importMetaUrl);
|
|
39
|
+
const candidates = [moduleDir ? path.resolve(moduleDir, "../public") : null, path.join(resolveServerPackageRoot(importMetaUrl), "public")];
|
|
40
|
+
return firstExistingPath(candidates, (value) => fs.existsSync(value)) ?? candidates[candidates.length - 1];
|
|
41
|
+
}
|
|
42
|
+
export function resolveAuthTemplatePath(importMetaUrl, fileName) {
|
|
43
|
+
const moduleDir = safeModuleDir(importMetaUrl);
|
|
44
|
+
const distDir = getPackagedDistDir();
|
|
45
|
+
const candidates = [
|
|
46
|
+
moduleDir ? path.join(moduleDir, "auth-pages", fileName) : null,
|
|
47
|
+
path.join(distDir, "auth-pages", fileName),
|
|
48
|
+
path.join(distDir, "server", "routes", "auth-pages", fileName),
|
|
49
|
+
];
|
|
50
|
+
return firstExistingPath(candidates, (value) => fs.existsSync(value)) ?? candidates[0];
|
|
51
|
+
}
|
|
52
|
+
export function resolveOpencodeTemplateDir(importMetaUrl) {
|
|
53
|
+
const moduleDir = safeModuleDir(importMetaUrl);
|
|
54
|
+
const resourcesPath = process.resourcesPath;
|
|
55
|
+
const candidates = [
|
|
56
|
+
moduleDir ? path.resolve(moduleDir, "../../opencode-config") : null,
|
|
57
|
+
resourcesPath ? path.resolve(resourcesPath, "opencode-config") : null,
|
|
58
|
+
moduleDir ? path.resolve(moduleDir, "opencode-config") : null,
|
|
59
|
+
path.join(getPackagedDistDir(), "opencode-config"),
|
|
60
|
+
];
|
|
61
|
+
return firstExistingPath(candidates, (value) => fs.existsSync(value)) ?? candidates[candidates.length - 1];
|
|
62
|
+
}
|
|
63
|
+
export function readServerPackageVersion(importMetaUrl) {
|
|
64
|
+
const packageJsonPath = path.join(resolveServerPackageRoot(importMetaUrl), "package.json");
|
|
65
|
+
const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
66
|
+
return typeof parsed.version === "string" && parsed.version.trim().length > 0 ? parsed.version : "0.0.0";
|
|
67
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { describe, it } from "node:test";
|
|
4
|
+
import { resolveNetworkAddresses, resolveRemoteAddresses } from "../network-addresses";
|
|
5
|
+
describe("resolveNetworkAddresses", () => {
|
|
6
|
+
it("preserves interface order among external addresses", () => {
|
|
7
|
+
const addresses = [
|
|
8
|
+
{ address: "172.24.0.1", family: "IPv4", internal: false },
|
|
9
|
+
{ address: "192.168.1.128", family: "IPv4", internal: false },
|
|
10
|
+
{ address: "10.0.0.8", family: 4, internal: false },
|
|
11
|
+
{ address: "127.0.0.1", family: "IPv4", internal: true },
|
|
12
|
+
{ address: "169.254.10.20", family: "IPv4", internal: false },
|
|
13
|
+
];
|
|
14
|
+
usingMockedNetworkInterfaces(addresses, () => {
|
|
15
|
+
const result = resolveNetworkAddresses({ host: "0.0.0.0", protocol: "https", port: 9898 });
|
|
16
|
+
assert.deepEqual(result.map((entry) => entry.ip), ["172.24.0.1", "192.168.1.128", "10.0.0.8", "169.254.10.20", "127.0.0.1"]);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe("resolveRemoteAddresses", () => {
|
|
21
|
+
it("keeps all external addresses user-visible while preferring non-link-local addresses for the primary URL", () => {
|
|
22
|
+
const addresses = [
|
|
23
|
+
{ address: "169.254.10.20", family: "IPv4", internal: false },
|
|
24
|
+
{ address: "192.168.1.128", family: "IPv4", internal: false },
|
|
25
|
+
{ address: "172.24.0.1", family: "IPv4", internal: false },
|
|
26
|
+
];
|
|
27
|
+
usingMockedNetworkInterfaces(addresses, () => {
|
|
28
|
+
const result = resolveRemoteAddresses({ host: "0.0.0.0", protocol: "https", port: 9898 });
|
|
29
|
+
assert.deepEqual(result.userVisible.map((entry) => entry.ip), ["192.168.1.128", "172.24.0.1", "169.254.10.20"]);
|
|
30
|
+
assert.equal(result.primaryRemoteUrl, "https://192.168.1.128:9898");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
it("prefers private LAN addresses over public addresses", () => {
|
|
34
|
+
const addresses = [
|
|
35
|
+
{ address: "203.0.113.40", family: "IPv4", internal: false },
|
|
36
|
+
{ address: "192.168.1.128", family: "IPv4", internal: false },
|
|
37
|
+
{ address: "8.8.8.8", family: "IPv4", internal: false },
|
|
38
|
+
];
|
|
39
|
+
usingMockedNetworkInterfaces(addresses, () => {
|
|
40
|
+
const result = resolveRemoteAddresses({ host: "0.0.0.0", protocol: "https", port: 9898 });
|
|
41
|
+
assert.deepEqual(result.userVisible.map((entry) => entry.ip), ["192.168.1.128", "203.0.113.40", "8.8.8.8"]);
|
|
42
|
+
assert.equal(result.primaryRemoteUrl, "https://192.168.1.128:9898");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
it("uses a public address when no private LAN address is available", () => {
|
|
46
|
+
const addresses = [
|
|
47
|
+
{ address: "169.254.10.20", family: "IPv4", internal: false },
|
|
48
|
+
{ address: "203.0.113.40", family: "IPv4", internal: false },
|
|
49
|
+
];
|
|
50
|
+
usingMockedNetworkInterfaces(addresses, () => {
|
|
51
|
+
const result = resolveRemoteAddresses({ host: "0.0.0.0", protocol: "https", port: 9898 });
|
|
52
|
+
assert.deepEqual(result.userVisible.map((entry) => entry.ip), ["203.0.113.40", "169.254.10.20"]);
|
|
53
|
+
assert.equal(result.primaryRemoteUrl, "https://203.0.113.40:9898");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
function usingMockedNetworkInterfaces(addresses, callback) {
|
|
58
|
+
const original = os.networkInterfaces;
|
|
59
|
+
os.networkInterfaces = (() => ({
|
|
60
|
+
ethernet0: addresses,
|
|
61
|
+
}));
|
|
62
|
+
try {
|
|
63
|
+
callback();
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
os.networkInterfaces = original;
|
|
67
|
+
}
|
|
68
|
+
}
|