@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,437 @@
1
+ import { spawn, spawnSync } from "child_process";
2
+ import { createWriteStream, existsSync, promises as fs } from "fs";
3
+ import path from "path";
4
+ import { randomBytes } from "crypto";
5
+ const ROOT_DIR = ".codenomad/background_processes";
6
+ const INDEX_FILE = "index.json";
7
+ const OUTPUT_FILE = "output.txt";
8
+ const STOP_TIMEOUT_MS = 2000;
9
+ const EXIT_WAIT_TIMEOUT_MS = 5000;
10
+ const MAX_OUTPUT_BYTES = 20 * 1024;
11
+ const OUTPUT_PUBLISH_INTERVAL_MS = 1000;
12
+ export class BackgroundProcessManager {
13
+ constructor(deps) {
14
+ this.deps = deps;
15
+ this.running = new Map();
16
+ this.deps.eventBus.on("workspace.stopped", (event) => this.cleanupWorkspace(event.workspaceId));
17
+ this.deps.eventBus.on("workspace.error", (event) => this.cleanupWorkspace(event.workspace.id));
18
+ }
19
+ async list(workspaceId) {
20
+ const records = await this.readIndex(workspaceId);
21
+ const enriched = await Promise.all(records.map(async (record) => ({
22
+ ...record,
23
+ outputSizeBytes: await this.getOutputSize(workspaceId, record.id),
24
+ })));
25
+ return enriched;
26
+ }
27
+ async start(workspaceId, title, command) {
28
+ const workspace = this.deps.workspaceManager.get(workspaceId);
29
+ if (!workspace) {
30
+ throw new Error("Workspace not found");
31
+ }
32
+ const id = this.generateId();
33
+ const processDir = await this.ensureProcessDir(workspaceId, id);
34
+ const outputPath = path.join(processDir, OUTPUT_FILE);
35
+ const outputStream = createWriteStream(outputPath, { flags: "a" });
36
+ const { shellCommand, shellArgs, spawnOptions } = this.buildShellSpawn(command);
37
+ const child = spawn(shellCommand, shellArgs, {
38
+ cwd: workspace.path,
39
+ stdio: ["ignore", "pipe", "pipe"],
40
+ detached: process.platform !== "win32",
41
+ ...spawnOptions,
42
+ });
43
+ child.on("exit", () => {
44
+ this.killProcessTree(child, "SIGTERM");
45
+ });
46
+ const record = {
47
+ id,
48
+ workspaceId,
49
+ title,
50
+ command,
51
+ cwd: workspace.path,
52
+ status: "running",
53
+ pid: child.pid,
54
+ startedAt: new Date().toISOString(),
55
+ outputSizeBytes: 0,
56
+ };
57
+ const exitPromise = new Promise((resolve) => {
58
+ child.on("close", async (code) => {
59
+ await new Promise((resolve) => outputStream.end(resolve));
60
+ this.running.delete(id);
61
+ record.status = this.statusFromExit(code);
62
+ record.exitCode = code === null ? undefined : code;
63
+ record.stoppedAt = new Date().toISOString();
64
+ await this.upsertIndex(workspaceId, record);
65
+ record.outputSizeBytes = await this.getOutputSize(workspaceId, record.id);
66
+ this.publishUpdate(workspaceId, record);
67
+ resolve();
68
+ });
69
+ });
70
+ this.running.set(id, { id, child, outputPath, exitPromise, workspaceId });
71
+ let lastPublishAt = 0;
72
+ const maybePublishSize = () => {
73
+ const now = Date.now();
74
+ if (now - lastPublishAt < OUTPUT_PUBLISH_INTERVAL_MS) {
75
+ return;
76
+ }
77
+ lastPublishAt = now;
78
+ this.publishUpdate(workspaceId, record);
79
+ };
80
+ child.stdout?.on("data", (data) => {
81
+ outputStream.write(data);
82
+ record.outputSizeBytes = (record.outputSizeBytes ?? 0) + data.length;
83
+ maybePublishSize();
84
+ });
85
+ child.stderr?.on("data", (data) => {
86
+ outputStream.write(data);
87
+ record.outputSizeBytes = (record.outputSizeBytes ?? 0) + data.length;
88
+ maybePublishSize();
89
+ });
90
+ await this.upsertIndex(workspaceId, record);
91
+ record.outputSizeBytes = await this.getOutputSize(workspaceId, record.id);
92
+ this.publishUpdate(workspaceId, record);
93
+ return record;
94
+ }
95
+ async stop(workspaceId, processId) {
96
+ const record = await this.findProcess(workspaceId, processId);
97
+ if (!record) {
98
+ return null;
99
+ }
100
+ const running = this.running.get(processId);
101
+ if (running?.child && !running.child.killed) {
102
+ this.killProcessTree(running.child, "SIGTERM");
103
+ await this.waitForExit(running);
104
+ }
105
+ if (record.status === "running") {
106
+ record.status = "stopped";
107
+ record.stoppedAt = new Date().toISOString();
108
+ await this.upsertIndex(workspaceId, record);
109
+ record.outputSizeBytes = await this.getOutputSize(workspaceId, record.id);
110
+ this.publishUpdate(workspaceId, record);
111
+ }
112
+ return record;
113
+ }
114
+ async terminate(workspaceId, processId) {
115
+ const record = await this.findProcess(workspaceId, processId);
116
+ if (!record)
117
+ return;
118
+ const running = this.running.get(processId);
119
+ if (running?.child && !running.child.killed) {
120
+ this.killProcessTree(running.child, "SIGTERM");
121
+ await this.waitForExit(running);
122
+ }
123
+ await this.removeFromIndex(workspaceId, processId);
124
+ await this.removeProcessDir(workspaceId, processId);
125
+ this.deps.eventBus.publish({
126
+ type: "instance.event",
127
+ instanceId: workspaceId,
128
+ event: { type: "background.process.removed", properties: { processId } },
129
+ });
130
+ }
131
+ async readOutput(workspaceId, processId, options) {
132
+ const outputPath = this.getOutputPath(workspaceId, processId);
133
+ if (!existsSync(outputPath)) {
134
+ return { id: processId, content: "", truncated: false, sizeBytes: 0 };
135
+ }
136
+ const stats = await fs.stat(outputPath);
137
+ const sizeBytes = stats.size;
138
+ const method = options.method ?? "full";
139
+ const lineCount = options.lines ?? 10;
140
+ const raw = await this.readOutputBytes(outputPath, sizeBytes, options.maxBytes);
141
+ let content = raw;
142
+ switch (method) {
143
+ case "head":
144
+ content = this.headLines(raw, lineCount);
145
+ break;
146
+ case "tail":
147
+ content = this.tailLines(raw, lineCount);
148
+ break;
149
+ case "grep":
150
+ if (!options.pattern) {
151
+ throw new Error("Pattern is required for grep output");
152
+ }
153
+ content = this.grepLines(raw, options.pattern);
154
+ break;
155
+ default:
156
+ content = raw;
157
+ }
158
+ const effectiveMaxBytes = options.maxBytes;
159
+ return {
160
+ id: processId,
161
+ content,
162
+ truncated: effectiveMaxBytes !== undefined && sizeBytes > effectiveMaxBytes,
163
+ sizeBytes,
164
+ };
165
+ }
166
+ async streamOutput(workspaceId, processId, reply) {
167
+ const outputPath = this.getOutputPath(workspaceId, processId);
168
+ if (!existsSync(outputPath)) {
169
+ reply.code(404).send({ error: "Output not found" });
170
+ return;
171
+ }
172
+ reply.raw.setHeader("Content-Type", "text/event-stream");
173
+ reply.raw.setHeader("Cache-Control", "no-cache");
174
+ reply.raw.setHeader("Connection", "keep-alive");
175
+ reply.raw.flushHeaders?.();
176
+ reply.hijack();
177
+ const file = await fs.open(outputPath, "r");
178
+ let position = (await file.stat()).size;
179
+ const tick = async () => {
180
+ const stats = await file.stat();
181
+ if (stats.size <= position)
182
+ return;
183
+ const length = stats.size - position;
184
+ const buffer = Buffer.alloc(length);
185
+ await file.read(buffer, 0, length, position);
186
+ position = stats.size;
187
+ const content = buffer.toString("utf-8");
188
+ reply.raw.write(`data: ${JSON.stringify({ type: "chunk", content })}\n\n`);
189
+ };
190
+ const interval = setInterval(() => {
191
+ tick().catch((error) => {
192
+ this.deps.logger.warn({ err: error }, "Failed to stream background process output");
193
+ });
194
+ }, 1000);
195
+ const close = () => {
196
+ clearInterval(interval);
197
+ file.close().catch(() => undefined);
198
+ reply.raw.end?.();
199
+ };
200
+ reply.raw.on("close", close);
201
+ reply.raw.on("error", close);
202
+ }
203
+ async cleanupWorkspace(workspaceId) {
204
+ for (const [, running] of this.running.entries()) {
205
+ if (running.workspaceId !== workspaceId)
206
+ continue;
207
+ this.killProcessTree(running.child, "SIGTERM");
208
+ await this.waitForExit(running);
209
+ }
210
+ await this.removeWorkspaceDir(workspaceId);
211
+ }
212
+ killProcessTree(child, signal) {
213
+ const pid = child.pid;
214
+ if (!pid)
215
+ return;
216
+ if (process.platform === "win32") {
217
+ const args = this.buildWindowsTaskkillArgs(pid, signal);
218
+ try {
219
+ spawnSync("taskkill", args, { stdio: "ignore" });
220
+ return;
221
+ }
222
+ catch {
223
+ // Fall back to killing the direct child.
224
+ }
225
+ }
226
+ else {
227
+ try {
228
+ process.kill(-pid, signal);
229
+ return;
230
+ }
231
+ catch {
232
+ // Fall back to killing the direct child.
233
+ }
234
+ }
235
+ try {
236
+ child.kill(signal);
237
+ }
238
+ catch {
239
+ // ignore
240
+ }
241
+ }
242
+ async waitForExit(running) {
243
+ let exited = false;
244
+ const exitPromise = running.exitPromise.finally(() => {
245
+ exited = true;
246
+ });
247
+ const killTimeout = setTimeout(() => {
248
+ if (!exited) {
249
+ this.killProcessTree(running.child, "SIGKILL");
250
+ }
251
+ }, STOP_TIMEOUT_MS);
252
+ try {
253
+ await Promise.race([
254
+ exitPromise,
255
+ new Promise((resolve) => {
256
+ setTimeout(resolve, EXIT_WAIT_TIMEOUT_MS);
257
+ }),
258
+ ]);
259
+ if (!exited) {
260
+ this.killProcessTree(running.child, "SIGKILL");
261
+ this.running.delete(running.id);
262
+ this.deps.logger.warn({ pid: running.child.pid }, "Timed out waiting for background process to exit");
263
+ }
264
+ }
265
+ finally {
266
+ clearTimeout(killTimeout);
267
+ }
268
+ }
269
+ buildShellSpawn(command) {
270
+ if (process.platform === "win32") {
271
+ const comspec = process.env.ComSpec || "cmd.exe";
272
+ return {
273
+ shellCommand: comspec,
274
+ shellArgs: ["/d", "/s", "/c", command],
275
+ spawnOptions: { windowsVerbatimArguments: true },
276
+ };
277
+ }
278
+ // Keep bash for macOS/Linux.
279
+ return { shellCommand: "bash", shellArgs: ["-c", command] };
280
+ }
281
+ buildWindowsTaskkillArgs(pid, signal) {
282
+ // Default to graceful termination (no /F), then force kill when we escalate.
283
+ const force = signal === "SIGKILL";
284
+ const args = ["/PID", String(pid), "/T"];
285
+ if (force) {
286
+ args.push("/F");
287
+ }
288
+ return args;
289
+ }
290
+ statusFromExit(code) {
291
+ if (code === null)
292
+ return "stopped";
293
+ if (code === 0)
294
+ return "stopped";
295
+ return "error";
296
+ }
297
+ async readOutputBytes(outputPath, sizeBytes, maxBytes) {
298
+ if (maxBytes === undefined || sizeBytes <= maxBytes) {
299
+ return await fs.readFile(outputPath, "utf-8");
300
+ }
301
+ const start = Math.max(0, sizeBytes - maxBytes);
302
+ const file = await fs.open(outputPath, "r");
303
+ const buffer = Buffer.alloc(sizeBytes - start);
304
+ await file.read(buffer, 0, buffer.length, start);
305
+ await file.close();
306
+ return buffer.toString("utf-8");
307
+ }
308
+ headLines(input, lines) {
309
+ const parts = input.split(/\r?\n/);
310
+ return parts.slice(0, Math.max(0, lines)).join("\n");
311
+ }
312
+ tailLines(input, lines) {
313
+ const parts = input.split(/\r?\n/);
314
+ return parts.slice(Math.max(0, parts.length - lines)).join("\n");
315
+ }
316
+ grepLines(input, pattern) {
317
+ let matcher;
318
+ try {
319
+ matcher = new RegExp(pattern);
320
+ }
321
+ catch {
322
+ throw new Error("Invalid grep pattern");
323
+ }
324
+ return input
325
+ .split(/\r?\n/)
326
+ .filter((line) => matcher.test(line))
327
+ .join("\n");
328
+ }
329
+ async ensureProcessDir(workspaceId, processId) {
330
+ const root = await this.ensureWorkspaceDir(workspaceId);
331
+ const processDir = path.join(root, processId);
332
+ await fs.mkdir(processDir, { recursive: true });
333
+ return processDir;
334
+ }
335
+ async ensureWorkspaceDir(workspaceId) {
336
+ const workspace = this.deps.workspaceManager.get(workspaceId);
337
+ if (!workspace) {
338
+ throw new Error("Workspace not found");
339
+ }
340
+ const root = path.join(workspace.path, ROOT_DIR, workspaceId);
341
+ await fs.mkdir(root, { recursive: true });
342
+ return root;
343
+ }
344
+ getOutputPath(workspaceId, processId) {
345
+ const workspace = this.deps.workspaceManager.get(workspaceId);
346
+ if (!workspace) {
347
+ throw new Error("Workspace not found");
348
+ }
349
+ return path.join(workspace.path, ROOT_DIR, workspaceId, processId, OUTPUT_FILE);
350
+ }
351
+ async findProcess(workspaceId, processId) {
352
+ const records = await this.readIndex(workspaceId);
353
+ return records.find((entry) => entry.id === processId) ?? null;
354
+ }
355
+ async readIndex(workspaceId) {
356
+ const indexPath = await this.getIndexPath(workspaceId);
357
+ if (!existsSync(indexPath))
358
+ return [];
359
+ try {
360
+ const raw = await fs.readFile(indexPath, "utf-8");
361
+ const parsed = JSON.parse(raw);
362
+ return Array.isArray(parsed) ? parsed : [];
363
+ }
364
+ catch {
365
+ return [];
366
+ }
367
+ }
368
+ async upsertIndex(workspaceId, record) {
369
+ const records = await this.readIndex(workspaceId);
370
+ const index = records.findIndex((entry) => entry.id === record.id);
371
+ if (index >= 0) {
372
+ records[index] = record;
373
+ }
374
+ else {
375
+ records.push(record);
376
+ }
377
+ await this.writeIndex(workspaceId, records);
378
+ }
379
+ async removeFromIndex(workspaceId, processId) {
380
+ const records = await this.readIndex(workspaceId);
381
+ const next = records.filter((entry) => entry.id !== processId);
382
+ await this.writeIndex(workspaceId, next);
383
+ }
384
+ async writeIndex(workspaceId, records) {
385
+ const indexPath = await this.getIndexPath(workspaceId);
386
+ await fs.mkdir(path.dirname(indexPath), { recursive: true });
387
+ await fs.writeFile(indexPath, JSON.stringify(records, null, 2));
388
+ }
389
+ async getIndexPath(workspaceId) {
390
+ const workspace = this.deps.workspaceManager.get(workspaceId);
391
+ if (!workspace) {
392
+ throw new Error("Workspace not found");
393
+ }
394
+ return path.join(workspace.path, ROOT_DIR, workspaceId, INDEX_FILE);
395
+ }
396
+ async removeProcessDir(workspaceId, processId) {
397
+ const workspace = this.deps.workspaceManager.get(workspaceId);
398
+ if (!workspace) {
399
+ return;
400
+ }
401
+ const processDir = path.join(workspace.path, ROOT_DIR, workspaceId, processId);
402
+ await fs.rm(processDir, { recursive: true, force: true });
403
+ }
404
+ async removeWorkspaceDir(workspaceId) {
405
+ const workspace = this.deps.workspaceManager.get(workspaceId);
406
+ if (!workspace) {
407
+ return;
408
+ }
409
+ const workspaceDir = path.join(workspace.path, ROOT_DIR, workspaceId);
410
+ await fs.rm(workspaceDir, { recursive: true, force: true });
411
+ }
412
+ async getOutputSize(workspaceId, processId) {
413
+ const outputPath = this.getOutputPath(workspaceId, processId);
414
+ if (!existsSync(outputPath)) {
415
+ return 0;
416
+ }
417
+ try {
418
+ const stats = await fs.stat(outputPath);
419
+ return stats.size;
420
+ }
421
+ catch {
422
+ return 0;
423
+ }
424
+ }
425
+ publishUpdate(workspaceId, record) {
426
+ this.deps.eventBus.publish({
427
+ type: "instance.event",
428
+ instanceId: workspaceId,
429
+ event: { type: "background.process.updated", properties: { process: record } },
430
+ });
431
+ }
432
+ generateId() {
433
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "").slice(0, 15);
434
+ const random = randomBytes(3).toString("hex");
435
+ return `proc_${timestamp}_${random}`;
436
+ }
437
+ }
package/dist/bin.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "child_process";
3
+ import path from "path";
4
+ import { fileURLToPath, pathToFileURL } from "url";
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const cliEntry = path.join(__dirname, "index.js");
8
+ const loaderFileUrl = pathToFileURL(path.join(__dirname, "loader.js")).href;
9
+ const registerScript = `import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("${encodeURI(loaderFileUrl)}", pathToFileURL("./"));`;
10
+ const loaderArg = `data:text/javascript,${registerScript}`;
11
+ const child = spawn(process.execPath, ["--import", loaderArg, cliEntry, ...process.argv.slice(2)], {
12
+ stdio: "inherit",
13
+ });
14
+ child.on("exit", (code, signal) => {
15
+ if (signal) {
16
+ process.kill(process.pid, signal);
17
+ return;
18
+ }
19
+ process.exit(code ?? 0);
20
+ });
21
+ child.on("error", (error) => {
22
+ console.error("Failed to launch CLI runtime", error);
23
+ process.exit(1);
24
+ });
@@ -0,0 +1,148 @@
1
+ import { spawnSync } from "child_process";
2
+ import { buildSpawnSpec } from "../workspaces/runtime";
3
+ export class BinaryRegistry {
4
+ constructor(configStore, eventBus, logger) {
5
+ this.configStore = configStore;
6
+ this.eventBus = eventBus;
7
+ this.logger = logger;
8
+ }
9
+ list() {
10
+ return this.mapRecords();
11
+ }
12
+ resolveDefault() {
13
+ const binaries = this.mapRecords();
14
+ if (binaries.length === 0) {
15
+ this.logger.warn("No configured binaries found, falling back to opencode");
16
+ return this.buildFallbackRecord("opencode");
17
+ }
18
+ return binaries.find((binary) => binary.isDefault) ?? binaries[0];
19
+ }
20
+ create(request) {
21
+ this.logger.debug({ path: request.path }, "Registering OpenCode binary");
22
+ const entry = {
23
+ path: request.path,
24
+ version: undefined,
25
+ lastUsed: Date.now(),
26
+ label: request.label,
27
+ };
28
+ const config = this.configStore.get();
29
+ const nextConfig = this.cloneConfig(config);
30
+ const deduped = nextConfig.opencodeBinaries.filter((binary) => binary.path !== request.path);
31
+ nextConfig.opencodeBinaries = [entry, ...deduped];
32
+ if (request.makeDefault) {
33
+ nextConfig.preferences.lastUsedBinary = request.path;
34
+ }
35
+ this.configStore.replace(nextConfig);
36
+ const record = this.getById(request.path);
37
+ this.emitChange();
38
+ return record;
39
+ }
40
+ update(id, updates) {
41
+ this.logger.debug({ id }, "Updating OpenCode binary");
42
+ const config = this.configStore.get();
43
+ const nextConfig = this.cloneConfig(config);
44
+ nextConfig.opencodeBinaries = nextConfig.opencodeBinaries.map((binary) => binary.path === id ? { ...binary, label: updates.label ?? binary.label } : binary);
45
+ if (updates.makeDefault) {
46
+ nextConfig.preferences.lastUsedBinary = id;
47
+ }
48
+ this.configStore.replace(nextConfig);
49
+ const record = this.getById(id);
50
+ this.emitChange();
51
+ return record;
52
+ }
53
+ remove(id) {
54
+ this.logger.debug({ id }, "Removing OpenCode binary");
55
+ const config = this.configStore.get();
56
+ const nextConfig = this.cloneConfig(config);
57
+ const remaining = nextConfig.opencodeBinaries.filter((binary) => binary.path !== id);
58
+ nextConfig.opencodeBinaries = remaining;
59
+ if (nextConfig.preferences.lastUsedBinary === id) {
60
+ nextConfig.preferences.lastUsedBinary = remaining[0]?.path;
61
+ }
62
+ this.configStore.replace(nextConfig);
63
+ this.emitChange();
64
+ }
65
+ validatePath(path) {
66
+ this.logger.debug({ path }, "Validating OpenCode binary path");
67
+ return this.validateRecord({
68
+ id: path,
69
+ path,
70
+ label: this.prettyLabel(path),
71
+ isDefault: false,
72
+ });
73
+ }
74
+ cloneConfig(config) {
75
+ return JSON.parse(JSON.stringify(config));
76
+ }
77
+ mapRecords() {
78
+ const config = this.configStore.get();
79
+ const configuredBinaries = config.opencodeBinaries.map((binary) => ({
80
+ id: binary.path,
81
+ path: binary.path,
82
+ label: binary.label ?? this.prettyLabel(binary.path),
83
+ version: binary.version,
84
+ isDefault: false,
85
+ }));
86
+ const defaultPath = config.preferences.lastUsedBinary ?? configuredBinaries[0]?.path ?? "opencode";
87
+ const annotated = configuredBinaries.map((binary) => ({
88
+ ...binary,
89
+ isDefault: binary.path === defaultPath,
90
+ }));
91
+ if (!annotated.some((binary) => binary.path === defaultPath)) {
92
+ annotated.unshift(this.buildFallbackRecord(defaultPath));
93
+ }
94
+ return annotated;
95
+ }
96
+ getById(id) {
97
+ return this.mapRecords().find((binary) => binary.id === id) ?? this.buildFallbackRecord(id);
98
+ }
99
+ emitChange() {
100
+ this.logger.debug("Emitting binaries changed event");
101
+ this.eventBus?.publish({ type: "config.binariesChanged", binaries: this.mapRecords() });
102
+ }
103
+ validateRecord(record) {
104
+ const inputPath = record.path;
105
+ if (!inputPath) {
106
+ return { valid: false, error: "Missing binary path" };
107
+ }
108
+ const spec = buildSpawnSpec(inputPath, ["--version"]);
109
+ try {
110
+ const result = spawnSync(spec.command, spec.args, {
111
+ encoding: "utf8",
112
+ windowsVerbatimArguments: Boolean(spec.options.windowsVerbatimArguments),
113
+ });
114
+ if (result.error) {
115
+ return { valid: false, error: result.error.message };
116
+ }
117
+ if (result.status !== 0) {
118
+ const stderr = result.stderr?.trim();
119
+ const stdout = result.stdout?.trim();
120
+ const combined = stderr || stdout;
121
+ const error = combined ? `Exited with code ${result.status}: ${combined}` : `Exited with code ${result.status}`;
122
+ return { valid: false, error };
123
+ }
124
+ const stdout = (result.stdout ?? "").trim();
125
+ const firstLine = stdout.split(/\r?\n/).find((line) => line.trim().length > 0);
126
+ const normalized = firstLine?.trim();
127
+ const versionMatch = normalized?.match(/([0-9]+\.[0-9]+\.[0-9A-Za-z.-]+)/);
128
+ const version = versionMatch?.[1];
129
+ return { valid: true, version };
130
+ }
131
+ catch (error) {
132
+ return { valid: false, error: error instanceof Error ? error.message : String(error) };
133
+ }
134
+ }
135
+ buildFallbackRecord(path) {
136
+ return {
137
+ id: path,
138
+ path,
139
+ label: this.prettyLabel(path),
140
+ isDefault: true,
141
+ };
142
+ }
143
+ prettyLabel(path) {
144
+ const parts = path.split(/[\\/]/);
145
+ const last = parts[parts.length - 1] || path;
146
+ return last || path;
147
+ }
148
+ }
@@ -0,0 +1,57 @@
1
+ import os from "os";
2
+ import path from "path";
3
+ function resolvePath(inputPath) {
4
+ if (inputPath.startsWith("~/")) {
5
+ return path.join(os.homedir(), inputPath.slice(2));
6
+ }
7
+ return path.resolve(inputPath);
8
+ }
9
+ function isYamlPath(filePath) {
10
+ const lower = filePath.toLowerCase();
11
+ return lower.endsWith(".yaml") || lower.endsWith(".yml");
12
+ }
13
+ function isJsonPath(filePath) {
14
+ return filePath.toLowerCase().endsWith(".json");
15
+ }
16
+ /**
17
+ * Resolve CodeNomad's config location into a stable base directory + derived file paths.
18
+ *
19
+ * Supported inputs:
20
+ * - Directory: "~/.config/codenomad"
21
+ * - YAML file: "~/.config/codenomad/config.yaml" (or any *.yml/*.yaml)
22
+ * - Legacy JSON file: "~/.config/codenomad/config.json"
23
+ */
24
+ export function resolveConfigLocation(raw) {
25
+ const trimmed = (raw ?? "").trim();
26
+ const fallback = "~/.config/codenomad/config.json";
27
+ const input = trimmed.length > 0 ? trimmed : fallback;
28
+ const resolvedInput = resolvePath(input);
29
+ if (isYamlPath(resolvedInput)) {
30
+ const baseDir = path.dirname(resolvedInput);
31
+ return {
32
+ baseDir,
33
+ configYamlPath: resolvedInput,
34
+ stateYamlPath: path.join(baseDir, "state.yaml"),
35
+ legacyJsonPath: path.join(baseDir, "config.json"),
36
+ instancesDir: path.join(baseDir, "instances"),
37
+ };
38
+ }
39
+ if (isJsonPath(resolvedInput)) {
40
+ const baseDir = path.dirname(resolvedInput);
41
+ return {
42
+ baseDir,
43
+ configYamlPath: path.join(baseDir, "config.yaml"),
44
+ stateYamlPath: path.join(baseDir, "state.yaml"),
45
+ legacyJsonPath: resolvedInput,
46
+ instancesDir: path.join(baseDir, "instances"),
47
+ };
48
+ }
49
+ const baseDir = resolvedInput;
50
+ return {
51
+ baseDir,
52
+ configYamlPath: path.join(baseDir, "config.yaml"),
53
+ stateYamlPath: path.join(baseDir, "state.yaml"),
54
+ legacyJsonPath: path.join(baseDir, "config.json"),
55
+ instancesDir: path.join(baseDir, "instances"),
56
+ };
57
+ }