@neuralnomads/codenomad-dev 0.10.3-dev-20260213-ba418a85

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.
Files changed (462) hide show
  1. package/README.md +126 -0
  2. package/dist/api-types.js +1 -0
  3. package/dist/auth/auth-store.js +134 -0
  4. package/dist/auth/http-auth.js +37 -0
  5. package/dist/auth/manager.js +128 -0
  6. package/dist/auth/password-hash.js +32 -0
  7. package/dist/auth/session-manager.js +17 -0
  8. package/dist/auth/token-manager.js +27 -0
  9. package/dist/background-processes/manager.js +437 -0
  10. package/dist/bin.js +24 -0
  11. package/dist/config/binaries.js +148 -0
  12. package/dist/config/location.js +57 -0
  13. package/dist/config/schema.js +70 -0
  14. package/dist/config/store.js +200 -0
  15. package/dist/events/bus.js +41 -0
  16. package/dist/filesystem/__tests__/search-cache.test.js +40 -0
  17. package/dist/filesystem/browser.js +285 -0
  18. package/dist/filesystem/search-cache.js +43 -0
  19. package/dist/filesystem/search.js +135 -0
  20. package/dist/index.js +410 -0
  21. package/dist/integrations/github/bot-signature.js +11 -0
  22. package/dist/integrations/github/git-ops.js +133 -0
  23. package/dist/integrations/github/github-types.js +1 -0
  24. package/dist/integrations/github/job-runner.js +608 -0
  25. package/dist/integrations/github/octokit.js +58 -0
  26. package/dist/integrations/github/sanitize-webhook.js +42 -0
  27. package/dist/integrations/github/webhook-verify.js +21 -0
  28. package/dist/integrations/github/workspace-context.js +10 -0
  29. package/dist/integrations/github/worktree-context.js +15 -0
  30. package/dist/launcher.js +149 -0
  31. package/dist/loader.js +21 -0
  32. package/dist/logger.js +109 -0
  33. package/dist/opencode/request-context.js +39 -0
  34. package/dist/opencode/worktree-directory.js +42 -0
  35. package/dist/opencode-config/README.md +32 -0
  36. package/dist/opencode-config/opencode.jsonc +3 -0
  37. package/dist/opencode-config/package.json +9 -0
  38. package/dist/opencode-config/plugin/codenomad.ts +32 -0
  39. package/dist/opencode-config/plugin/lib/background-process.ts +253 -0
  40. package/dist/opencode-config/plugin/lib/client.ts +133 -0
  41. package/dist/opencode-config/plugin/lib/request.ts +214 -0
  42. package/dist/opencode-config-template/README.md +32 -0
  43. package/dist/opencode-config-template/opencode.jsonc +3 -0
  44. package/dist/opencode-config-template/plugin/codenomad.ts +40 -0
  45. package/dist/opencode-config-template/plugin/lib/background-process.ts +160 -0
  46. package/dist/opencode-config-template/plugin/lib/client.ts +165 -0
  47. package/dist/opencode-config.js +26 -0
  48. package/dist/plugins/channel.js +40 -0
  49. package/dist/plugins/handlers.js +17 -0
  50. package/dist/releases/dev-release-monitor.js +75 -0
  51. package/dist/releases/release-monitor.js +107 -0
  52. package/dist/server/http-server.js +547 -0
  53. package/dist/server/network-addresses.js +72 -0
  54. package/dist/server/routes/auth-pages/login.html +134 -0
  55. package/dist/server/routes/auth-pages/token.html +93 -0
  56. package/dist/server/routes/auth.js +134 -0
  57. package/dist/server/routes/background-processes.js +60 -0
  58. package/dist/server/routes/config.js +59 -0
  59. package/dist/server/routes/events.js +43 -0
  60. package/dist/server/routes/filesystem.js +43 -0
  61. package/dist/server/routes/github-plugin.js +215 -0
  62. package/dist/server/routes/github-webhook.js +32 -0
  63. package/dist/server/routes/meta.js +47 -0
  64. package/dist/server/routes/plugin.js +52 -0
  65. package/dist/server/routes/storage.js +52 -0
  66. package/dist/server/routes/workspaces.js +89 -0
  67. package/dist/server/routes/worktrees.js +156 -0
  68. package/dist/server/tls.js +224 -0
  69. package/dist/storage/instance-store.js +56 -0
  70. package/dist/ui/__tests__/remote-ui.test.js +46 -0
  71. package/dist/ui/remote-ui.js +462 -0
  72. package/dist/workspaces/git-worktrees.js +199 -0
  73. package/dist/workspaces/instance-events.js +180 -0
  74. package/dist/workspaces/manager.js +375 -0
  75. package/dist/workspaces/opencode-auth.js +16 -0
  76. package/dist/workspaces/runtime.js +346 -0
  77. package/dist/workspaces/worktree-map.js +116 -0
  78. package/package.json +49 -0
  79. package/public/apple-touch-icon-180x180.png +0 -0
  80. package/public/assets/CodeNomad-Icon-bmTWNPXy.png +0 -0
  81. package/public/assets/abap-BdImnpbu.js +1 -0
  82. package/public/assets/actionscript-3-CfeIJUat.js +1 -0
  83. package/public/assets/ada-bCR0ucgS.js +1 -0
  84. package/public/assets/andromeeda-C-Jbm3Hp.js +1 -0
  85. package/public/assets/angular-html-CU67Zn6k.js +1 -0
  86. package/public/assets/angular-ts-BwZT4LLn.js +1 -0
  87. package/public/assets/apache-Pmp26Uib.js +1 -0
  88. package/public/assets/apex-DhZLUxFE.js +1 -0
  89. package/public/assets/apl-dKokRX4l.js +1 -0
  90. package/public/assets/applescript-Co6uUVPk.js +1 -0
  91. package/public/assets/ara-BRHolxvo.js +1 -0
  92. package/public/assets/asciidoc-Dv7Oe6Be.js +1 -0
  93. package/public/assets/asm-D_Q5rh1f.js +1 -0
  94. package/public/assets/astro-CbQHKStN.js +1 -0
  95. package/public/assets/aurora-x-D-2ljcwZ.js +1 -0
  96. package/public/assets/awk-DMzUqQB5.js +1 -0
  97. package/public/assets/ayu-dark-Cv9koXgw.js +1 -0
  98. package/public/assets/ballerina-BFfxhgS-.js +1 -0
  99. package/public/assets/bat-BkioyH1T.js +1 -0
  100. package/public/assets/beancount-k_qm7-4y.js +1 -0
  101. package/public/assets/berry-D08WgyRC.js +1 -0
  102. package/public/assets/bibtex-CHM0blh-.js +1 -0
  103. package/public/assets/bicep-Bmn6On1c.js +1 -0
  104. package/public/assets/blade-DVc8C-J4.js +1 -0
  105. package/public/assets/bsl-BO_Y6i37.js +1 -0
  106. package/public/assets/c-BIGW1oBm.js +1 -0
  107. package/public/assets/cadence-Bv_4Rxtq.js +1 -0
  108. package/public/assets/cairo-KRGpt6FW.js +1 -0
  109. package/public/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  110. package/public/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  111. package/public/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  112. package/public/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  113. package/public/assets/clarity-D53aC0YG.js +1 -0
  114. package/public/assets/clojure-P80f7IUj.js +1 -0
  115. package/public/assets/cmake-D1j8_8rp.js +1 -0
  116. package/public/assets/cobol-nwyudZeR.js +1 -0
  117. package/public/assets/codeowners-Bp6g37R7.js +1 -0
  118. package/public/assets/codeql-DsOJ9woJ.js +1 -0
  119. package/public/assets/coffee-Ch7k5sss.js +1 -0
  120. package/public/assets/common-lisp-Cg-RD9OK.js +1 -0
  121. package/public/assets/coq-DkFqJrB1.js +1 -0
  122. package/public/assets/core-BSTVzpXI.js +1 -0
  123. package/public/assets/cpp-CofmeUqb.js +1 -0
  124. package/public/assets/crystal-tKQVLTB8.js +1 -0
  125. package/public/assets/csharp-CX12Zw3r.js +1 -0
  126. package/public/assets/css-DPfMkruS.js +1 -0
  127. package/public/assets/csv-fuZLfV_i.js +1 -0
  128. package/public/assets/cue-D82EKSYY.js +1 -0
  129. package/public/assets/cypher-COkxafJQ.js +1 -0
  130. package/public/assets/d-85-TOEBH.js +1 -0
  131. package/public/assets/dark-plus-eOWES_5F.js +1 -0
  132. package/public/assets/dart-CF10PKvl.js +1 -0
  133. package/public/assets/dax-CEL-wOlO.js +1 -0
  134. package/public/assets/desktop-BmXAJ9_W.js +1 -0
  135. package/public/assets/diff-D97Zzqfu.js +1 -0
  136. package/public/assets/docker-BcOcwvcX.js +1 -0
  137. package/public/assets/dotenv-Da5cRb03.js +1 -0
  138. package/public/assets/dracula-BzJJZx-M.js +1 -0
  139. package/public/assets/dracula-soft-BXkSAIEj.js +1 -0
  140. package/public/assets/dream-maker-BtqSS_iP.js +1 -0
  141. package/public/assets/edge-BkV0erSs.js +1 -0
  142. package/public/assets/elixir-CDX3lj18.js +1 -0
  143. package/public/assets/elm-DbKCFpqz.js +1 -0
  144. package/public/assets/emacs-lisp-C9XAeP06.js +1 -0
  145. package/public/assets/erb-BOJIQeun.js +1 -0
  146. package/public/assets/erlang-DsQrWhSR.js +1 -0
  147. package/public/assets/everforest-dark-BgDCqdQA.js +1 -0
  148. package/public/assets/everforest-light-C8M2exoo.js +1 -0
  149. package/public/assets/fennel-BYunw83y.js +1 -0
  150. package/public/assets/fish-BvzEVeQv.js +1 -0
  151. package/public/assets/fluent-C4IJs8-o.js +1 -0
  152. package/public/assets/fortran-fixed-form-BZjJHVRy.js +1 -0
  153. package/public/assets/fortran-free-form-D22FLkUw.js +1 -0
  154. package/public/assets/fsharp-CXgrBDvD.js +1 -0
  155. package/public/assets/gdresource-B7Tvp0Sc.js +1 -0
  156. package/public/assets/gdscript-DTMYz4Jt.js +1 -0
  157. package/public/assets/gdshader-DkwncUOv.js +1 -0
  158. package/public/assets/genie-D0YGMca9.js +1 -0
  159. package/public/assets/gherkin-DyxjwDmM.js +1 -0
  160. package/public/assets/git-commit-F4YmCXRG.js +1 -0
  161. package/public/assets/git-rebase-r7XF79zn.js +1 -0
  162. package/public/assets/github-dark-DHJKELXO.js +1 -0
  163. package/public/assets/github-dark-default-Cuk6v7N8.js +1 -0
  164. package/public/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  165. package/public/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  166. package/public/assets/github-light-DAi9KRSo.js +1 -0
  167. package/public/assets/github-light-default-D7oLnXFd.js +1 -0
  168. package/public/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  169. package/public/assets/gleam-BspZqrRM.js +1 -0
  170. package/public/assets/glimmer-js-Rg0-pVw9.js +1 -0
  171. package/public/assets/glimmer-ts-U6CK756n.js +1 -0
  172. package/public/assets/glsl-DplSGwfg.js +1 -0
  173. package/public/assets/gnuplot-DdkO51Og.js +1 -0
  174. package/public/assets/go-Dn2_MT6a.js +1 -0
  175. package/public/assets/graphql-ChdNCCLP.js +1 -0
  176. package/public/assets/groovy-gcz8RCvz.js +1 -0
  177. package/public/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  178. package/public/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  179. package/public/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  180. package/public/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  181. package/public/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  182. package/public/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  183. package/public/assets/hack-CaT9iCJl.js +1 -0
  184. package/public/assets/haml-B8DHNrY2.js +1 -0
  185. package/public/assets/handlebars-BL8al0AC.js +1 -0
  186. package/public/assets/haskell-Df6bDoY_.js +1 -0
  187. package/public/assets/haxe-CzTSHFRz.js +1 -0
  188. package/public/assets/hcl-BWvSN4gD.js +1 -0
  189. package/public/assets/hjson-D5-asLiD.js +1 -0
  190. package/public/assets/hlsl-D3lLCCz7.js +1 -0
  191. package/public/assets/houston-DnULxvSX.js +1 -0
  192. package/public/assets/html-GMplVEZG.js +1 -0
  193. package/public/assets/html-derivative-BFtXZ54Q.js +1 -0
  194. package/public/assets/http-jrhK8wxY.js +1 -0
  195. package/public/assets/hurl-irOxFIW8.js +1 -0
  196. package/public/assets/hxml-Bvhsp5Yf.js +1 -0
  197. package/public/assets/hy-DFXneXwc.js +1 -0
  198. package/public/assets/imba-DGztddWO.js +1 -0
  199. package/public/assets/index-D4PT0yE4.js +1 -0
  200. package/public/assets/index-DN20ggb1.js +1 -0
  201. package/public/assets/index-DdQ7zIzB.js +1 -0
  202. package/public/assets/index-Dl-rJJuP.js +1 -0
  203. package/public/assets/index-Dlo2gDiy.css +1 -0
  204. package/public/assets/ini-BEwlwnbL.js +1 -0
  205. package/public/assets/java-CylS5w8V.js +1 -0
  206. package/public/assets/javascript-wDzz0qaB.js +1 -0
  207. package/public/assets/jinja-4LBKfQ-Z.js +1 -0
  208. package/public/assets/jison-wvAkD_A8.js +1 -0
  209. package/public/assets/json-Cp-IABpG.js +1 -0
  210. package/public/assets/json5-C9tS-k6U.js +1 -0
  211. package/public/assets/jsonc-Des-eS-w.js +1 -0
  212. package/public/assets/jsonl-DcaNXYhu.js +1 -0
  213. package/public/assets/jsonnet-DFQXde-d.js +1 -0
  214. package/public/assets/jssm-C2t-YnRu.js +1 -0
  215. package/public/assets/jsx-g9-lgVsj.js +1 -0
  216. package/public/assets/julia-C8NyazO9.js +1 -0
  217. package/public/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  218. package/public/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  219. package/public/assets/kanagawa-wave-DWedfzmr.js +1 -0
  220. package/public/assets/kdl-DV7GczEv.js +1 -0
  221. package/public/assets/kotlin-BdnUsdx6.js +1 -0
  222. package/public/assets/kusto-BvAqAH-y.js +1 -0
  223. package/public/assets/laserwave-DUszq2jm.js +1 -0
  224. package/public/assets/latex-BUKiar2Z.js +1 -0
  225. package/public/assets/lean-DP1Csr6i.js +1 -0
  226. package/public/assets/less-B1dDrJ26.js +1 -0
  227. package/public/assets/light-plus-B7mTdjB0.js +1 -0
  228. package/public/assets/liquid-DYVedYrR.js +1 -0
  229. package/public/assets/llvm-BtvRca6l.js +1 -0
  230. package/public/assets/loading-CmEVQgyj.css +1 -0
  231. package/public/assets/loading-DgqIiz-T.js +1 -0
  232. package/public/assets/log-2UxHyX5q.js +1 -0
  233. package/public/assets/logo-BtOb2qkB.js +1 -0
  234. package/public/assets/lua-BbnMAYS6.js +1 -0
  235. package/public/assets/luau-CXu1NL6O.js +1 -0
  236. package/public/assets/main-CSlDZj4f.js +188 -0
  237. package/public/assets/main-HAZkIolJ.css +19 -0
  238. package/public/assets/make-CHLpvVh8.js +1 -0
  239. package/public/assets/markdown-Cvjx9yec.js +1 -0
  240. package/public/assets/marko-CPi9NSCl.js +1 -0
  241. package/public/assets/material-theme-D5KoaKCx.js +1 -0
  242. package/public/assets/material-theme-darker-BfHTSMKl.js +1 -0
  243. package/public/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  244. package/public/assets/material-theme-ocean-CyktbL80.js +1 -0
  245. package/public/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  246. package/public/assets/matlab-D7o27uSR.js +1 -0
  247. package/public/assets/mdc-DUICxH0z.js +1 -0
  248. package/public/assets/mdx-Cmh6b_Ma.js +1 -0
  249. package/public/assets/mermaid-DKYwYmdq.js +1 -0
  250. package/public/assets/min-dark-CafNBF8u.js +1 -0
  251. package/public/assets/min-light-CTRr51gU.js +1 -0
  252. package/public/assets/mipsasm-CKIfxQSi.js +1 -0
  253. package/public/assets/mojo-1DNp92w6.js +1 -0
  254. package/public/assets/monokai-D4h5O-jR.js +1 -0
  255. package/public/assets/move-Bu9oaDYs.js +1 -0
  256. package/public/assets/narrat-DRg8JJMk.js +1 -0
  257. package/public/assets/nextflow-CUEJCptM.js +1 -0
  258. package/public/assets/nginx-DknmC5AR.js +1 -0
  259. package/public/assets/night-owl-C39BiMTA.js +1 -0
  260. package/public/assets/nim-CVrawwO9.js +1 -0
  261. package/public/assets/nix-BbRYJGeE.js +1 -0
  262. package/public/assets/nord-Ddv68eIx.js +1 -0
  263. package/public/assets/nushell-C-sUppwS.js +1 -0
  264. package/public/assets/objective-c-DXmwc3jG.js +1 -0
  265. package/public/assets/objective-cpp-CLxacb5B.js +1 -0
  266. package/public/assets/ocaml-C0hk2d4L.js +1 -0
  267. package/public/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  268. package/public/assets/one-light-PoHY5YXO.js +1 -0
  269. package/public/assets/pascal-D93ZcfNL.js +1 -0
  270. package/public/assets/perl-C0TMdlhV.js +1 -0
  271. package/public/assets/php-CDn_0X-4.js +1 -0
  272. package/public/assets/pkl-u5AG7uiY.js +1 -0
  273. package/public/assets/plastic-3e1v2bzS.js +1 -0
  274. package/public/assets/plsql-ChMvpjG-.js +1 -0
  275. package/public/assets/po-BTJTHyun.js +1 -0
  276. package/public/assets/poimandres-CS3Unz2-.js +1 -0
  277. package/public/assets/polar-C0HS_06l.js +1 -0
  278. package/public/assets/postcss-CXtECtnM.js +1 -0
  279. package/public/assets/powerquery-CEu0bR-o.js +1 -0
  280. package/public/assets/powershell-Dpen1YoG.js +1 -0
  281. package/public/assets/prisma-Dd19v3D-.js +1 -0
  282. package/public/assets/prolog-CbFg5uaA.js +1 -0
  283. package/public/assets/proto-DyJlTyXw.js +1 -0
  284. package/public/assets/pug-CGlum2m_.js +1 -0
  285. package/public/assets/puppet-BMWR74SV.js +1 -0
  286. package/public/assets/purescript-CklMAg4u.js +1 -0
  287. package/public/assets/python-B6aJPvgy.js +1 -0
  288. package/public/assets/qml-3beO22l8.js +1 -0
  289. package/public/assets/qmldir-C8lEn-DE.js +1 -0
  290. package/public/assets/qss-IeuSbFQv.js +1 -0
  291. package/public/assets/r-DiinP2Uv.js +1 -0
  292. package/public/assets/racket-BqYA7rlc.js +1 -0
  293. package/public/assets/raku-DXvB9xmW.js +1 -0
  294. package/public/assets/razor-WgofotgN.js +1 -0
  295. package/public/assets/red-bN70gL4F.js +1 -0
  296. package/public/assets/reg-C-SQnVFl.js +1 -0
  297. package/public/assets/regexp-CDVJQ6XC.js +1 -0
  298. package/public/assets/rel-C3B-1QV4.js +1 -0
  299. package/public/assets/riscv-BM1_JUlF.js +1 -0
  300. package/public/assets/rose-pine-BHrmToEH.js +1 -0
  301. package/public/assets/rose-pine-dawn-CnK8MTSM.js +1 -0
  302. package/public/assets/rose-pine-moon-NleAzG8P.js +1 -0
  303. package/public/assets/rosmsg-BJDFO7_C.js +1 -0
  304. package/public/assets/rst-B0xPkSld.js +1 -0
  305. package/public/assets/ruby-BvKwtOVI.js +1 -0
  306. package/public/assets/rust-B1yitclQ.js +1 -0
  307. package/public/assets/sas-cz2c8ADy.js +1 -0
  308. package/public/assets/sass-Cj5Yp3dK.js +1 -0
  309. package/public/assets/scala-C151Ov-r.js +1 -0
  310. package/public/assets/scheme-C98Dy4si.js +1 -0
  311. package/public/assets/scss-OYdSNvt2.js +1 -0
  312. package/public/assets/sdbl-DVxCFoDh.js +1 -0
  313. package/public/assets/shaderlab-Dg9Lc6iA.js +1 -0
  314. package/public/assets/shellscript-Yzrsuije.js +1 -0
  315. package/public/assets/shellsession-BADoaaVG.js +1 -0
  316. package/public/assets/slack-dark-BthQWCQV.js +1 -0
  317. package/public/assets/slack-ochin-DqwNpetd.js +1 -0
  318. package/public/assets/smalltalk-BERRCDM3.js +1 -0
  319. package/public/assets/snazzy-light-Bw305WKR.js +1 -0
  320. package/public/assets/solarized-dark-DXbdFlpD.js +1 -0
  321. package/public/assets/solarized-light-L9t79GZl.js +1 -0
  322. package/public/assets/solidity-BbcW6ACK.js +1 -0
  323. package/public/assets/soy-Brmx7dQM.js +1 -0
  324. package/public/assets/sparql-rVzFXLq3.js +1 -0
  325. package/public/assets/splunk-BtCnVYZw.js +1 -0
  326. package/public/assets/sql-BLtJtn59.js +1 -0
  327. package/public/assets/ssh-config-_ykCGR6B.js +1 -0
  328. package/public/assets/stata-BH5u7GGu.js +1 -0
  329. package/public/assets/stylus-BEDo0Tqx.js +1 -0
  330. package/public/assets/svelte-3Dk4HxPD.js +1 -0
  331. package/public/assets/swift-Dg5xB15N.js +1 -0
  332. package/public/assets/synthwave-84-CbfX1IO0.js +1 -0
  333. package/public/assets/system-verilog-CnnmHF94.js +1 -0
  334. package/public/assets/systemd-4A_iFExJ.js +1 -0
  335. package/public/assets/talonscript-CkByrt1z.js +1 -0
  336. package/public/assets/tasl-QIJgUcNo.js +1 -0
  337. package/public/assets/tcl-dwOrl1Do.js +1 -0
  338. package/public/assets/templ-W15q3VgB.js +1 -0
  339. package/public/assets/terraform-BETggiCN.js +1 -0
  340. package/public/assets/tex-Cppo0RY3.js +1 -0
  341. package/public/assets/tokyo-night-hegEt444.js +1 -0
  342. package/public/assets/toml-vGWfd6FD.js +1 -0
  343. package/public/assets/ts-tags-zn1MmPIZ.js +1 -0
  344. package/public/assets/tsv-B_m7g4N7.js +1 -0
  345. package/public/assets/tsx-COt5Ahok.js +1 -0
  346. package/public/assets/turtle-BsS91CYL.js +1 -0
  347. package/public/assets/twig-CO9l9SDP.js +1 -0
  348. package/public/assets/typescript-BPQ3VLAy.js +1 -0
  349. package/public/assets/typespec-Df68jz8_.js +1 -0
  350. package/public/assets/typst-DHCkPAjA.js +1 -0
  351. package/public/assets/v-BcVCzyr7.js +1 -0
  352. package/public/assets/vala-CsfeWuGM.js +1 -0
  353. package/public/assets/vb-D17OF-Vu.js +1 -0
  354. package/public/assets/verilog-BQ8w6xss.js +1 -0
  355. package/public/assets/vesper-DU1UobuO.js +1 -0
  356. package/public/assets/vhdl-CeAyd5Ju.js +1 -0
  357. package/public/assets/viml-CJc9bBzg.js +1 -0
  358. package/public/assets/vitesse-black-Bkuqu6BP.js +1 -0
  359. package/public/assets/vitesse-dark-D0r3Knsf.js +1 -0
  360. package/public/assets/vitesse-light-CVO1_9PV.js +1 -0
  361. package/public/assets/vue-CCoi5OLL.js +1 -0
  362. package/public/assets/vue-html-DAAvJJDi.js +1 -0
  363. package/public/assets/vue-vine-_Ih-lPRR.js +1 -0
  364. package/public/assets/vyper-CDx5xZoG.js +1 -0
  365. package/public/assets/wasm-CG6Dc4jp.js +1 -0
  366. package/public/assets/wasm-MzD3tlZU.js +1 -0
  367. package/public/assets/wenyan-BV7otONQ.js +1 -0
  368. package/public/assets/wgsl-Dx-B1_4e.js +1 -0
  369. package/public/assets/wikitext-BhOHFoWU.js +1 -0
  370. package/public/assets/wit-5i3qLPDT.js +1 -0
  371. package/public/assets/wolfram-lXgVvXCa.js +1 -0
  372. package/public/assets/xml-sdJ4AIDG.js +1 -0
  373. package/public/assets/xsl-CtQFsRM5.js +1 -0
  374. package/public/assets/yaml-Buea-lGh.js +1 -0
  375. package/public/assets/zenscript-DVFEvuxE.js +1 -0
  376. package/public/assets/zig-VOosw3JB.js +1 -0
  377. package/public/favicon.ico +0 -0
  378. package/public/index.html +38 -0
  379. package/public/loading.html +28 -0
  380. package/public/logo.png +0 -0
  381. package/public/manifest.webmanifest +1 -0
  382. package/public/maskable-icon-512x512.png +0 -0
  383. package/public/monaco/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
  384. package/public/monaco/vs/base/worker/workerMain.js +31 -0
  385. package/public/monaco/vs/basic-languages/cpp/cpp.js +10 -0
  386. package/public/monaco/vs/basic-languages/kotlin/kotlin.js +10 -0
  387. package/public/monaco/vs/basic-languages/markdown/markdown.js +10 -0
  388. package/public/monaco/vs/basic-languages/python/python.js +10 -0
  389. package/public/monaco/vs/editor/editor.main.css +8 -0
  390. package/public/monaco/vs/editor/editor.main.js +798 -0
  391. package/public/monaco/vs/language/css/cssMode.js +13 -0
  392. package/public/monaco/vs/language/css/cssWorker.js +77 -0
  393. package/public/monaco/vs/language/html/htmlMode.js +13 -0
  394. package/public/monaco/vs/language/html/htmlWorker.js +454 -0
  395. package/public/monaco/vs/language/json/jsonMode.js +19 -0
  396. package/public/monaco/vs/language/json/jsonWorker.js +42 -0
  397. package/public/monaco/vs/language/typescript/tsMode.js +20 -0
  398. package/public/monaco/vs/language/typescript/tsWorker.js +51328 -0
  399. package/public/monaco/vs/loader.js +11 -0
  400. package/public/monaco.worker.js +7 -0
  401. package/public/pwa-192x192.png +0 -0
  402. package/public/pwa-512x512.png +0 -0
  403. package/public/pwa-64x64.png +0 -0
  404. package/public/registerSW.js +1 -0
  405. package/public/sw.js +1 -0
  406. package/public/ui-version.json +3 -0
  407. package/public/workbox-60d14903.js +1 -0
  408. package/scripts/copy-auth-pages.mjs +22 -0
  409. package/scripts/copy-opencode-config.mjs +61 -0
  410. package/scripts/copy-ui-dist.mjs +21 -0
  411. package/src/api-types.ts +326 -0
  412. package/src/auth/auth-store.ts +175 -0
  413. package/src/auth/http-auth.ts +38 -0
  414. package/src/auth/manager.ts +163 -0
  415. package/src/auth/password-hash.ts +49 -0
  416. package/src/auth/session-manager.ts +23 -0
  417. package/src/auth/token-manager.ts +32 -0
  418. package/src/background-processes/manager.ts +519 -0
  419. package/src/bin.ts +29 -0
  420. package/src/config/binaries.ts +192 -0
  421. package/src/config/location.ts +78 -0
  422. package/src/config/schema.ts +104 -0
  423. package/src/config/store.ts +244 -0
  424. package/src/events/bus.ts +45 -0
  425. package/src/filesystem/__tests__/search-cache.test.ts +61 -0
  426. package/src/filesystem/browser.ts +353 -0
  427. package/src/filesystem/search-cache.ts +66 -0
  428. package/src/filesystem/search.ts +184 -0
  429. package/src/index.ts +540 -0
  430. package/src/launcher.ts +177 -0
  431. package/src/loader.ts +21 -0
  432. package/src/logger.ts +133 -0
  433. package/src/opencode-config.ts +31 -0
  434. package/src/plugins/channel.ts +55 -0
  435. package/src/plugins/handlers.ts +36 -0
  436. package/src/releases/dev-release-monitor.ts +118 -0
  437. package/src/releases/release-monitor.ts +149 -0
  438. package/src/server/http-server.ts +693 -0
  439. package/src/server/network-addresses.ts +75 -0
  440. package/src/server/routes/auth-pages/login.html +134 -0
  441. package/src/server/routes/auth-pages/token.html +93 -0
  442. package/src/server/routes/auth.ts +164 -0
  443. package/src/server/routes/background-processes.ts +85 -0
  444. package/src/server/routes/config.ts +76 -0
  445. package/src/server/routes/events.ts +61 -0
  446. package/src/server/routes/filesystem.ts +54 -0
  447. package/src/server/routes/meta.ts +58 -0
  448. package/src/server/routes/plugin.ts +75 -0
  449. package/src/server/routes/storage.ts +66 -0
  450. package/src/server/routes/workspaces.ts +113 -0
  451. package/src/server/routes/worktrees.ts +195 -0
  452. package/src/server/tls.ts +283 -0
  453. package/src/storage/instance-store.ts +64 -0
  454. package/src/ui/__tests__/remote-ui.test.ts +58 -0
  455. package/src/ui/remote-ui.ts +571 -0
  456. package/src/workspaces/git-worktrees.ts +241 -0
  457. package/src/workspaces/instance-events.ts +226 -0
  458. package/src/workspaces/manager.ts +493 -0
  459. package/src/workspaces/opencode-auth.ts +22 -0
  460. package/src/workspaces/runtime.ts +428 -0
  461. package/src/workspaces/worktree-map.ts +129 -0
  462. package/tsconfig.json +17 -0
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ const ModelPreferenceSchema = z.object({
3
+ providerId: z.string(),
4
+ modelId: z.string(),
5
+ });
6
+ const AgentModelSelectionSchema = z.record(z.string(), ModelPreferenceSchema);
7
+ const AgentModelSelectionsSchema = z.record(z.string(), AgentModelSelectionSchema);
8
+ const PreferencesSchema = z
9
+ .object({
10
+ showThinkingBlocks: z.boolean().default(false),
11
+ thinkingBlocksExpansion: z.enum(["expanded", "collapsed"]).default("expanded"),
12
+ showTimelineTools: z.boolean().default(true),
13
+ promptSubmitOnEnter: z.boolean().default(false),
14
+ lastUsedBinary: z.string().optional(),
15
+ locale: z.string().optional(),
16
+ environmentVariables: z.record(z.string()).default({}),
17
+ modelRecents: z.array(ModelPreferenceSchema).default([]),
18
+ modelFavorites: z.array(ModelPreferenceSchema).default([]),
19
+ modelThinkingSelections: z.record(z.string(), z.string()).default({}),
20
+ diffViewMode: z.enum(["split", "unified"]).default("split"),
21
+ toolOutputExpansion: z.enum(["expanded", "collapsed"]).default("expanded"),
22
+ diagnosticsExpansion: z.enum(["expanded", "collapsed"]).default("expanded"),
23
+ showUsageMetrics: z.boolean().default(true),
24
+ autoCleanupBlankSessions: z.boolean().default(true),
25
+ listeningMode: z.enum(["local", "all"]).default("local"),
26
+ // OS notifications
27
+ osNotificationsEnabled: z.boolean().default(false),
28
+ osNotificationsAllowWhenVisible: z.boolean().default(false),
29
+ notifyOnNeedsInput: z.boolean().default(true),
30
+ notifyOnIdle: z.boolean().default(true),
31
+ })
32
+ // Preserve unknown preference keys so newer configs survive older binaries.
33
+ .passthrough();
34
+ const RecentFolderSchema = z.object({
35
+ path: z.string(),
36
+ lastAccessed: z.number().nonnegative(),
37
+ });
38
+ const OpenCodeBinarySchema = z.object({
39
+ path: z.string(),
40
+ version: z.string().optional(),
41
+ lastUsed: z.number().nonnegative(),
42
+ label: z.string().optional(),
43
+ });
44
+ const ConfigFileSchema = z
45
+ .object({
46
+ preferences: PreferencesSchema.default({}),
47
+ recentFolders: z.array(RecentFolderSchema).default([]),
48
+ opencodeBinaries: z.array(OpenCodeBinarySchema).default([]),
49
+ theme: z.enum(["light", "dark", "system"]).optional(),
50
+ })
51
+ // Preserve unknown top-level keys so optional future features survive downgrades.
52
+ .passthrough();
53
+ // On-disk config.yaml only stores stable configuration (not volatile state like recent folders).
54
+ const ConfigYamlSchema = z
55
+ .object({
56
+ preferences: PreferencesSchema.default({}),
57
+ opencodeBinaries: z.array(OpenCodeBinarySchema).default([]),
58
+ theme: z.enum(["light", "dark", "system"]).optional(),
59
+ })
60
+ .passthrough();
61
+ // On-disk state.yaml stores server-scoped mutable state (per-server, not per-client).
62
+ const StateFileSchema = z
63
+ .object({
64
+ recentFolders: z.array(RecentFolderSchema).default([]),
65
+ })
66
+ .passthrough();
67
+ const DEFAULT_CONFIG = ConfigFileSchema.parse({});
68
+ const DEFAULT_CONFIG_YAML = ConfigYamlSchema.parse({});
69
+ const DEFAULT_STATE = StateFileSchema.parse({});
70
+ export { ModelPreferenceSchema, AgentModelSelectionSchema, AgentModelSelectionsSchema, PreferencesSchema, RecentFolderSchema, OpenCodeBinarySchema, ConfigFileSchema, ConfigYamlSchema, StateFileSchema, DEFAULT_CONFIG, DEFAULT_CONFIG_YAML, DEFAULT_STATE, };
@@ -0,0 +1,200 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
4
+ import { ConfigFileSchema, ConfigYamlSchema, DEFAULT_CONFIG, DEFAULT_CONFIG_YAML, DEFAULT_STATE, StateFileSchema, } from "./schema";
5
+ export class ConfigStore {
6
+ constructor(location, eventBus, logger) {
7
+ this.location = location;
8
+ this.eventBus = eventBus;
9
+ this.logger = logger;
10
+ this.cache = DEFAULT_CONFIG;
11
+ this.state = DEFAULT_STATE;
12
+ this.loaded = false;
13
+ }
14
+ load() {
15
+ if (this.loaded) {
16
+ return this.cache;
17
+ }
18
+ try {
19
+ const configYamlPath = this.location.configYamlPath;
20
+ const stateYamlPath = this.location.stateYamlPath;
21
+ const legacyJsonPath = this.location.legacyJsonPath;
22
+ if (fs.existsSync(configYamlPath)) {
23
+ const configDoc = this.readYamlFile(configYamlPath, DEFAULT_CONFIG_YAML, ConfigYamlSchema, "config");
24
+ const stateDoc = fs.existsSync(stateYamlPath)
25
+ ? this.readYamlFile(stateYamlPath, DEFAULT_STATE, StateFileSchema, "state")
26
+ : DEFAULT_STATE;
27
+ this.state = stateDoc;
28
+ this.cache = this.mergeDocs(configDoc, stateDoc);
29
+ this.logger.debug({ configYamlPath, stateYamlPath }, "Loaded existing YAML config/state");
30
+ }
31
+ else if (fs.existsSync(legacyJsonPath)) {
32
+ const migrated = this.migrateFromLegacyJson(legacyJsonPath);
33
+ this.state = migrated.state;
34
+ this.cache = migrated.config;
35
+ }
36
+ else {
37
+ // Fresh install: write defaults.
38
+ this.state = DEFAULT_STATE;
39
+ this.cache = this.mergeDocs(DEFAULT_CONFIG_YAML, DEFAULT_STATE);
40
+ this.persist();
41
+ this.logger.debug({ configYamlPath, stateYamlPath }, "No config files found, created default YAML config/state");
42
+ }
43
+ }
44
+ catch (error) {
45
+ this.logger.warn({ err: error }, "Failed to load config/state, using defaults");
46
+ this.state = DEFAULT_STATE;
47
+ this.cache = this.mergeDocs(DEFAULT_CONFIG_YAML, DEFAULT_STATE);
48
+ }
49
+ this.loaded = true;
50
+ return this.cache;
51
+ }
52
+ get() {
53
+ return this.load();
54
+ }
55
+ replace(config) {
56
+ const validated = ConfigFileSchema.parse(config);
57
+ this.commit(validated);
58
+ }
59
+ /**
60
+ * Apply a merge-patch update to the current config.
61
+ * - Missing keys are preserved.
62
+ * - Object values are merged recursively.
63
+ * - Explicit `null` deletes keys.
64
+ * - Arrays are replaced.
65
+ */
66
+ mergePatch(patch) {
67
+ if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
68
+ throw new Error("Config patch must be a JSON object");
69
+ }
70
+ const current = this.get();
71
+ const next = applyMergePatch(current, patch);
72
+ const validated = ConfigFileSchema.parse(next);
73
+ this.commit(validated);
74
+ }
75
+ commit(next) {
76
+ this.cache = next;
77
+ this.loaded = true;
78
+ this.state = {
79
+ ...this.state,
80
+ recentFolders: next.recentFolders,
81
+ };
82
+ this.persist();
83
+ const published = Boolean(this.eventBus);
84
+ this.eventBus?.publish({ type: "config.appChanged", config: this.cache });
85
+ this.logger.debug({ broadcast: published }, "Config SSE event emitted");
86
+ this.logger.trace({ config: this.cache }, "Config payload");
87
+ }
88
+ persist() {
89
+ try {
90
+ const configYamlPath = this.location.configYamlPath;
91
+ const stateYamlPath = this.location.stateYamlPath;
92
+ fs.mkdirSync(this.location.baseDir, { recursive: true });
93
+ fs.mkdirSync(path.dirname(configYamlPath), { recursive: true });
94
+ const configYaml = stringifyYaml(stripRecentFolders(this.cache));
95
+ const stateYaml = stringifyYaml(this.state);
96
+ fs.writeFileSync(configYamlPath, ensureTrailingNewline(configYaml), "utf-8");
97
+ fs.writeFileSync(stateYamlPath, ensureTrailingNewline(stateYaml), "utf-8");
98
+ this.logger.debug({ configYamlPath, stateYamlPath }, "Persisted YAML config/state");
99
+ }
100
+ catch (error) {
101
+ this.logger.warn({ err: error }, "Failed to persist config");
102
+ }
103
+ }
104
+ mergeDocs(configDoc, stateDoc) {
105
+ const merged = {
106
+ ...configDoc,
107
+ // State wins for recent folders.
108
+ recentFolders: stateDoc.recentFolders ?? [],
109
+ };
110
+ return ConfigFileSchema.parse(merged);
111
+ }
112
+ readYamlFile(filePath, fallback, schema, label) {
113
+ try {
114
+ const content = fs.readFileSync(filePath, "utf-8");
115
+ const parsed = parseYaml(content);
116
+ return schema.parse(parsed ?? {});
117
+ }
118
+ catch (error) {
119
+ this.logger.warn({ err: error, filePath, label }, "Failed to read YAML file, using defaults");
120
+ return fallback;
121
+ }
122
+ }
123
+ migrateFromLegacyJson(legacyJsonPath) {
124
+ const configYamlPath = this.location.configYamlPath;
125
+ const stateYamlPath = this.location.stateYamlPath;
126
+ const content = fs.readFileSync(legacyJsonPath, "utf-8");
127
+ const parsed = JSON.parse(content);
128
+ const legacy = ConfigFileSchema.parse(parsed);
129
+ const state = StateFileSchema.parse({
130
+ ...DEFAULT_STATE,
131
+ recentFolders: legacy.recentFolders ?? [],
132
+ });
133
+ const merged = this.mergeDocs(stripRecentFolders(legacy), state);
134
+ // Persist YAML docs first, then move legacy aside.
135
+ try {
136
+ fs.mkdirSync(this.location.baseDir, { recursive: true });
137
+ fs.writeFileSync(configYamlPath, ensureTrailingNewline(stringifyYaml(stripRecentFolders(merged))), "utf-8");
138
+ fs.writeFileSync(stateYamlPath, ensureTrailingNewline(stringifyYaml(state)), "utf-8");
139
+ this.logger.info({ legacyJsonPath, configYamlPath, stateYamlPath }, "Migrated config.json -> YAML");
140
+ }
141
+ catch (error) {
142
+ this.logger.warn({ err: error }, "Failed to persist migrated YAML config/state");
143
+ }
144
+ try {
145
+ const bakPath = pickBackupPath(legacyJsonPath);
146
+ fs.renameSync(legacyJsonPath, bakPath);
147
+ this.logger.info({ legacyJsonPath, bakPath }, "Moved legacy config.json to backup");
148
+ }
149
+ catch (error) {
150
+ this.logger.warn({ err: error, legacyJsonPath }, "Failed to rename legacy config.json to backup");
151
+ }
152
+ return { config: merged, state };
153
+ }
154
+ }
155
+ function ensureTrailingNewline(content) {
156
+ if (!content)
157
+ return "\n";
158
+ return content.endsWith("\n") ? content : `${content}\n`;
159
+ }
160
+ function stripRecentFolders(config) {
161
+ const clone = { ...config };
162
+ delete clone.recentFolders;
163
+ return clone;
164
+ }
165
+ function isPlainObject(value) {
166
+ if (!value || typeof value !== "object")
167
+ return false;
168
+ if (Array.isArray(value))
169
+ return false;
170
+ const proto = Object.getPrototypeOf(value);
171
+ return proto === Object.prototype || proto === null;
172
+ }
173
+ function applyMergePatch(current, patch) {
174
+ // RFC 7396-ish merge patch with explicit null deletes.
175
+ if (!isPlainObject(patch)) {
176
+ return patch;
177
+ }
178
+ const base = isPlainObject(current) ? { ...current } : {};
179
+ for (const [key, value] of Object.entries(patch)) {
180
+ if (value === null) {
181
+ delete base[key];
182
+ continue;
183
+ }
184
+ if (isPlainObject(value) && isPlainObject(base[key])) {
185
+ base[key] = applyMergePatch(base[key], value);
186
+ continue;
187
+ }
188
+ // Arrays and scalars replace.
189
+ base[key] = value;
190
+ }
191
+ return base;
192
+ }
193
+ function pickBackupPath(legacyJsonPath) {
194
+ const base = legacyJsonPath.endsWith(".json") ? legacyJsonPath.slice(0, -".json".length) : legacyJsonPath;
195
+ const preferred = `${base}.json.bak`;
196
+ if (!fs.existsSync(preferred)) {
197
+ return preferred;
198
+ }
199
+ return `${base}.json.bak.${Date.now()}`;
200
+ }
@@ -0,0 +1,41 @@
1
+ import { EventEmitter } from "events";
2
+ export class EventBus extends EventEmitter {
3
+ constructor(logger) {
4
+ super();
5
+ this.logger = logger;
6
+ }
7
+ publish(event) {
8
+ if (event.type !== "instance.event" && event.type !== "instance.eventStatus") {
9
+ this.logger?.debug({ type: event.type }, "Publishing workspace event");
10
+ if (this.logger?.isLevelEnabled("trace")) {
11
+ this.logger.trace({ event }, "Workspace event payload");
12
+ }
13
+ }
14
+ return super.emit(event.type, event);
15
+ }
16
+ onEvent(listener) {
17
+ const handler = (event) => listener(event);
18
+ this.on("workspace.created", handler);
19
+ this.on("workspace.started", handler);
20
+ this.on("workspace.error", handler);
21
+ this.on("workspace.stopped", handler);
22
+ this.on("workspace.log", handler);
23
+ this.on("config.appChanged", handler);
24
+ this.on("config.binariesChanged", handler);
25
+ this.on("instance.dataChanged", handler);
26
+ this.on("instance.event", handler);
27
+ this.on("instance.eventStatus", handler);
28
+ return () => {
29
+ this.off("workspace.created", handler);
30
+ this.off("workspace.started", handler);
31
+ this.off("workspace.error", handler);
32
+ this.off("workspace.stopped", handler);
33
+ this.off("workspace.log", handler);
34
+ this.off("config.appChanged", handler);
35
+ this.off("config.binariesChanged", handler);
36
+ this.off("instance.dataChanged", handler);
37
+ this.off("instance.event", handler);
38
+ this.off("instance.eventStatus", handler);
39
+ };
40
+ }
41
+ }
@@ -0,0 +1,40 @@
1
+ import assert from "node:assert/strict";
2
+ import { beforeEach, describe, it } from "node:test";
3
+ import { clearWorkspaceSearchCache, getWorkspaceCandidates, refreshWorkspaceCandidates, WORKSPACE_CANDIDATE_CACHE_TTL_MS, } from "../search-cache";
4
+ describe("workspace search cache", () => {
5
+ beforeEach(() => {
6
+ clearWorkspaceSearchCache();
7
+ });
8
+ it("expires cached candidates after the TTL", () => {
9
+ const workspacePath = "/tmp/workspace";
10
+ const startTime = 1000;
11
+ refreshWorkspaceCandidates(workspacePath, () => [createEntry("file-a")], startTime);
12
+ const beforeExpiry = getWorkspaceCandidates(workspacePath, startTime + WORKSPACE_CANDIDATE_CACHE_TTL_MS - 1);
13
+ assert.ok(beforeExpiry);
14
+ assert.equal(beforeExpiry.length, 1);
15
+ assert.equal(beforeExpiry[0].name, "file-a");
16
+ const afterExpiry = getWorkspaceCandidates(workspacePath, startTime + WORKSPACE_CANDIDATE_CACHE_TTL_MS + 1);
17
+ assert.equal(afterExpiry, undefined);
18
+ });
19
+ it("replaces cached entries when manually refreshed", () => {
20
+ const workspacePath = "/tmp/workspace";
21
+ refreshWorkspaceCandidates(workspacePath, () => [createEntry("file-a")], 5000);
22
+ const initial = getWorkspaceCandidates(workspacePath);
23
+ assert.ok(initial);
24
+ assert.equal(initial[0].name, "file-a");
25
+ refreshWorkspaceCandidates(workspacePath, () => [createEntry("file-b")], 6000);
26
+ const refreshed = getWorkspaceCandidates(workspacePath);
27
+ assert.ok(refreshed);
28
+ assert.equal(refreshed[0].name, "file-b");
29
+ });
30
+ });
31
+ function createEntry(name) {
32
+ return {
33
+ name,
34
+ path: name,
35
+ absolutePath: `/tmp/${name}`,
36
+ type: "file",
37
+ size: 1,
38
+ modifiedAt: new Date().toISOString(),
39
+ };
40
+ }
@@ -0,0 +1,285 @@
1
+ import fs from "fs";
2
+ import os from "os";
3
+ import path from "path";
4
+ import { WINDOWS_DRIVES_ROOT, } from "../api-types";
5
+ const WINDOWS_DRIVE_LETTERS = Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i));
6
+ export class FileSystemBrowser {
7
+ constructor(options) {
8
+ this.root = path.resolve(options.rootDir);
9
+ this.unrestricted = Boolean(options.unrestricted);
10
+ this.homeDir = os.homedir();
11
+ this.isWindows = process.platform === "win32";
12
+ }
13
+ list(relativePath = ".", options = {}) {
14
+ if (this.unrestricted) {
15
+ throw new Error("Relative listing is unavailable when running with unrestricted root");
16
+ }
17
+ const includeFiles = options.includeFiles ?? true;
18
+ const normalizedPath = this.normalizeRelativePath(relativePath);
19
+ const absolutePath = this.toRestrictedAbsolute(normalizedPath);
20
+ return this.readDirectoryEntries(absolutePath, {
21
+ includeFiles,
22
+ formatPath: (entryName) => this.buildRelativePath(normalizedPath, entryName),
23
+ formatAbsolutePath: (entryName) => this.resolveRestrictedAbsoluteChild(normalizedPath, entryName),
24
+ });
25
+ }
26
+ browse(targetPath, options = {}) {
27
+ const includeFiles = options.includeFiles ?? true;
28
+ if (this.unrestricted) {
29
+ return this.listUnrestricted(targetPath, includeFiles);
30
+ }
31
+ return this.listRestrictedWithMetadata(targetPath, includeFiles);
32
+ }
33
+ createFolder(parentPath, folderName) {
34
+ const name = this.normalizeFolderName(folderName);
35
+ if (this.unrestricted) {
36
+ const resolvedParent = this.resolveUnrestrictedPath(parentPath);
37
+ if (this.isWindows && resolvedParent === WINDOWS_DRIVES_ROOT) {
38
+ throw new Error("Cannot create folders at drive root");
39
+ }
40
+ this.assertDirectoryExists(resolvedParent);
41
+ const absolutePath = this.resolveAbsoluteChild(resolvedParent, name);
42
+ fs.mkdirSync(absolutePath);
43
+ return { path: absolutePath, absolutePath };
44
+ }
45
+ const normalizedParent = this.normalizeRelativePath(parentPath);
46
+ const parentAbsolute = this.toRestrictedAbsolute(normalizedParent);
47
+ this.assertDirectoryExists(parentAbsolute);
48
+ const relativePath = this.buildRelativePath(normalizedParent, name);
49
+ const absolutePath = this.toRestrictedAbsolute(relativePath);
50
+ fs.mkdirSync(absolutePath);
51
+ return { path: relativePath, absolutePath };
52
+ }
53
+ readFile(relativePath) {
54
+ if (this.unrestricted) {
55
+ throw new Error("readFile is not available in unrestricted mode");
56
+ }
57
+ const resolved = this.toRestrictedAbsolute(relativePath);
58
+ return fs.readFileSync(resolved, "utf-8");
59
+ }
60
+ listRestrictedWithMetadata(relativePath, includeFiles) {
61
+ const normalizedPath = this.normalizeRelativePath(relativePath);
62
+ const absolutePath = this.toRestrictedAbsolute(normalizedPath);
63
+ const entries = this.readDirectoryEntries(absolutePath, {
64
+ includeFiles,
65
+ formatPath: (entryName) => this.buildRelativePath(normalizedPath, entryName),
66
+ formatAbsolutePath: (entryName) => this.resolveRestrictedAbsoluteChild(normalizedPath, entryName),
67
+ });
68
+ const metadata = {
69
+ scope: "restricted",
70
+ currentPath: normalizedPath,
71
+ parentPath: normalizedPath === "." ? undefined : this.getRestrictedParent(normalizedPath),
72
+ rootPath: this.root,
73
+ homePath: this.homeDir,
74
+ displayPath: this.resolveRestrictedAbsolute(normalizedPath),
75
+ pathKind: "relative",
76
+ };
77
+ return { entries, metadata };
78
+ }
79
+ listUnrestricted(targetPath, includeFiles) {
80
+ const resolvedPath = this.resolveUnrestrictedPath(targetPath);
81
+ if (this.isWindows && resolvedPath === WINDOWS_DRIVES_ROOT) {
82
+ return this.listWindowsDrives();
83
+ }
84
+ const entries = this.readDirectoryEntries(resolvedPath, {
85
+ includeFiles,
86
+ formatPath: (entryName) => this.resolveAbsoluteChild(resolvedPath, entryName),
87
+ formatAbsolutePath: (entryName) => this.resolveAbsoluteChild(resolvedPath, entryName),
88
+ });
89
+ const parentPath = this.getUnrestrictedParent(resolvedPath);
90
+ const metadata = {
91
+ scope: "unrestricted",
92
+ currentPath: resolvedPath,
93
+ parentPath,
94
+ rootPath: this.homeDir,
95
+ homePath: this.homeDir,
96
+ displayPath: resolvedPath,
97
+ pathKind: "absolute",
98
+ };
99
+ return { entries, metadata };
100
+ }
101
+ listWindowsDrives() {
102
+ if (!this.isWindows) {
103
+ throw new Error("Drive listing is only supported on Windows hosts");
104
+ }
105
+ const entries = [];
106
+ for (const letter of WINDOWS_DRIVE_LETTERS) {
107
+ const drivePath = `${letter}:\\`;
108
+ try {
109
+ if (fs.existsSync(drivePath)) {
110
+ entries.push({
111
+ name: `${letter}:`,
112
+ path: drivePath,
113
+ absolutePath: drivePath,
114
+ type: "directory",
115
+ });
116
+ }
117
+ }
118
+ catch {
119
+ // Ignore inaccessible drives
120
+ }
121
+ }
122
+ // Provide a generic UNC root entry so users can navigate to network shares manually.
123
+ entries.push({
124
+ name: "UNC Network",
125
+ path: "\\\\",
126
+ absolutePath: "\\\\",
127
+ type: "directory",
128
+ });
129
+ const metadata = {
130
+ scope: "unrestricted",
131
+ currentPath: WINDOWS_DRIVES_ROOT,
132
+ parentPath: undefined,
133
+ rootPath: this.homeDir,
134
+ homePath: this.homeDir,
135
+ displayPath: "Drives",
136
+ pathKind: "drives",
137
+ };
138
+ return { entries, metadata };
139
+ }
140
+ normalizeFolderName(input) {
141
+ const name = input.trim();
142
+ if (!name) {
143
+ throw new Error("Folder name is required");
144
+ }
145
+ if (name === "." || name === "..") {
146
+ throw new Error("Invalid folder name");
147
+ }
148
+ if (name.startsWith("~")) {
149
+ throw new Error("Invalid folder name");
150
+ }
151
+ if (name.includes("/") || name.includes("\\")) {
152
+ throw new Error("Folder name must not include path separators");
153
+ }
154
+ if (name.includes("\u0000")) {
155
+ throw new Error("Invalid folder name");
156
+ }
157
+ return name;
158
+ }
159
+ assertDirectoryExists(directory) {
160
+ if (!fs.existsSync(directory)) {
161
+ throw new Error(`Directory does not exist: ${directory}`);
162
+ }
163
+ const stats = fs.statSync(directory);
164
+ if (!stats.isDirectory()) {
165
+ throw new Error(`Path is not a directory: ${directory}`);
166
+ }
167
+ }
168
+ readDirectoryEntries(directory, options) {
169
+ const dirents = fs.readdirSync(directory, { withFileTypes: true });
170
+ const results = [];
171
+ for (const entry of dirents) {
172
+ const absoluteEntryPath = path.join(directory, entry.name);
173
+ let stats;
174
+ try {
175
+ // Use fs.statSync (not Dirent.isDirectory) so symlinks to directories
176
+ // are treated as directories in directory-only listings.
177
+ stats = fs.statSync(absoluteEntryPath);
178
+ }
179
+ catch {
180
+ // Skip entries we cannot stat (insufficient permissions, etc.)
181
+ continue;
182
+ }
183
+ const isDirectory = stats.isDirectory();
184
+ if (!options.includeFiles && !isDirectory) {
185
+ continue;
186
+ }
187
+ results.push({
188
+ name: entry.name,
189
+ path: options.formatPath(entry.name),
190
+ absolutePath: options.formatAbsolutePath(entry.name),
191
+ type: isDirectory ? "directory" : "file",
192
+ size: isDirectory ? undefined : stats.size,
193
+ modifiedAt: stats.mtime.toISOString(),
194
+ });
195
+ }
196
+ return results.sort((a, b) => a.name.localeCompare(b.name));
197
+ }
198
+ normalizeRelativePath(input) {
199
+ if (!input || input === "." || input === "./" || input === "/") {
200
+ return ".";
201
+ }
202
+ let normalized = input.replace(/\\+/g, "/");
203
+ if (normalized.startsWith("./")) {
204
+ normalized = normalized.replace(/^\.\/+/, "");
205
+ }
206
+ if (normalized.startsWith("/")) {
207
+ normalized = normalized.replace(/^\/+/g, "");
208
+ }
209
+ return normalized === "" ? "." : normalized;
210
+ }
211
+ buildRelativePath(parent, child) {
212
+ if (!parent || parent === ".") {
213
+ return this.normalizeRelativePath(child);
214
+ }
215
+ return this.normalizeRelativePath(`${parent}/${child}`);
216
+ }
217
+ resolveRestrictedAbsolute(relativePath) {
218
+ return this.toRestrictedAbsolute(relativePath);
219
+ }
220
+ resolveRestrictedAbsoluteChild(parent, child) {
221
+ const normalized = this.buildRelativePath(parent, child);
222
+ return this.toRestrictedAbsolute(normalized);
223
+ }
224
+ toRestrictedAbsolute(relativePath) {
225
+ const normalized = this.normalizeRelativePath(relativePath);
226
+ const target = path.resolve(this.root, normalized);
227
+ const relativeToRoot = path.relative(this.root, target);
228
+ if (relativeToRoot.startsWith("..") || path.isAbsolute(relativeToRoot) && relativeToRoot !== "") {
229
+ throw new Error("Access outside of root is not allowed");
230
+ }
231
+ return target;
232
+ }
233
+ resolveUnrestrictedPath(input) {
234
+ if (!input || input === "." || input === "./") {
235
+ return this.homeDir;
236
+ }
237
+ if (this.isWindows) {
238
+ if (input === WINDOWS_DRIVES_ROOT) {
239
+ return WINDOWS_DRIVES_ROOT;
240
+ }
241
+ const normalized = path.win32.normalize(input);
242
+ if (/^[a-zA-Z]:/.test(normalized) || normalized.startsWith("\\\\")) {
243
+ return normalized;
244
+ }
245
+ return path.win32.resolve(this.homeDir, normalized);
246
+ }
247
+ if (input.startsWith("/")) {
248
+ return path.posix.normalize(input);
249
+ }
250
+ return path.posix.resolve(this.homeDir, input);
251
+ }
252
+ resolveAbsoluteChild(parent, child) {
253
+ if (this.isWindows) {
254
+ return path.win32.normalize(path.win32.join(parent, child));
255
+ }
256
+ return path.posix.normalize(path.posix.join(parent, child));
257
+ }
258
+ getRestrictedParent(relativePath) {
259
+ const normalized = this.normalizeRelativePath(relativePath);
260
+ if (normalized === ".") {
261
+ return undefined;
262
+ }
263
+ const segments = normalized.split("/");
264
+ segments.pop();
265
+ return segments.length === 0 ? "." : segments.join("/");
266
+ }
267
+ getUnrestrictedParent(currentPath) {
268
+ if (this.isWindows) {
269
+ const normalized = path.win32.normalize(currentPath);
270
+ const parsed = path.win32.parse(normalized);
271
+ if (normalized === WINDOWS_DRIVES_ROOT) {
272
+ return undefined;
273
+ }
274
+ if (normalized === parsed.root) {
275
+ return WINDOWS_DRIVES_ROOT;
276
+ }
277
+ return path.win32.dirname(normalized);
278
+ }
279
+ const normalized = path.posix.normalize(currentPath);
280
+ if (normalized === "/") {
281
+ return undefined;
282
+ }
283
+ return path.posix.dirname(normalized);
284
+ }
285
+ }
@@ -0,0 +1,43 @@
1
+ import path from "path";
2
+ export const WORKSPACE_CANDIDATE_CACHE_TTL_MS = 30000;
3
+ const workspaceCandidateCache = new Map();
4
+ export function getWorkspaceCandidates(rootDir, now = Date.now()) {
5
+ const key = normalizeKey(rootDir);
6
+ const cached = workspaceCandidateCache.get(key);
7
+ if (!cached) {
8
+ return undefined;
9
+ }
10
+ if (cached.expiresAt <= now) {
11
+ workspaceCandidateCache.delete(key);
12
+ return undefined;
13
+ }
14
+ return cloneEntries(cached.candidates);
15
+ }
16
+ export function refreshWorkspaceCandidates(rootDir, builder, now = Date.now()) {
17
+ const key = normalizeKey(rootDir);
18
+ const freshCandidates = builder();
19
+ if (!freshCandidates || freshCandidates.length === 0) {
20
+ workspaceCandidateCache.delete(key);
21
+ return [];
22
+ }
23
+ const storedCandidates = cloneEntries(freshCandidates);
24
+ workspaceCandidateCache.set(key, {
25
+ expiresAt: now + WORKSPACE_CANDIDATE_CACHE_TTL_MS,
26
+ candidates: storedCandidates,
27
+ });
28
+ return cloneEntries(storedCandidates);
29
+ }
30
+ export function clearWorkspaceSearchCache(rootDir) {
31
+ if (typeof rootDir === "undefined") {
32
+ workspaceCandidateCache.clear();
33
+ return;
34
+ }
35
+ const key = normalizeKey(rootDir);
36
+ workspaceCandidateCache.delete(key);
37
+ }
38
+ function cloneEntries(entries) {
39
+ return entries.map((entry) => ({ ...entry }));
40
+ }
41
+ function normalizeKey(rootDir) {
42
+ return path.resolve(rootDir);
43
+ }