@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,493 @@
1
+ import path from "path"
2
+ import { spawnSync } from "child_process"
3
+ import { connect } from "net"
4
+ import { EventBus } from "../events/bus"
5
+ import { ConfigStore } from "../config/store"
6
+ import { BinaryRegistry } from "../config/binaries"
7
+ import { FileSystemBrowser } from "../filesystem/browser"
8
+ import { searchWorkspaceFiles, WorkspaceFileSearchOptions } from "../filesystem/search"
9
+ import { clearWorkspaceSearchCache } from "../filesystem/search-cache"
10
+ import { WorkspaceDescriptor, WorkspaceFileResponse, FileSystemEntry } from "../api-types"
11
+ import { WorkspaceRuntime, ProcessExitInfo } from "./runtime"
12
+ import { Logger } from "../logger"
13
+ import { getOpencodeConfigDir } from "../opencode-config.js"
14
+ import {
15
+ buildOpencodeBasicAuthHeader,
16
+ DEFAULT_OPENCODE_USERNAME,
17
+ generateOpencodeServerPassword,
18
+ OPENCODE_SERVER_PASSWORD_ENV,
19
+ OPENCODE_SERVER_USERNAME_ENV,
20
+ } from "./opencode-auth"
21
+
22
+ const STARTUP_STABILITY_DELAY_MS = 1500
23
+
24
+ interface WorkspaceManagerOptions {
25
+ rootDir: string
26
+ configStore: ConfigStore
27
+ binaryRegistry: BinaryRegistry
28
+ eventBus: EventBus
29
+ logger: Logger
30
+ getServerBaseUrl: () => string
31
+ /** Optional CA bundle path to trust CodeNomad HTTPS certs. */
32
+ nodeExtraCaCertsPath?: string
33
+ }
34
+
35
+ interface WorkspaceRecord extends WorkspaceDescriptor {}
36
+
37
+ export class WorkspaceManager {
38
+ private readonly workspaces = new Map<string, WorkspaceRecord>()
39
+ private readonly runtime: WorkspaceRuntime
40
+ private readonly opencodeConfigDir: string
41
+ private readonly opencodeAuth = new Map<string, { username: string; password: string; authorization: string }>()
42
+
43
+ constructor(private readonly options: WorkspaceManagerOptions) {
44
+ this.runtime = new WorkspaceRuntime(this.options.eventBus, this.options.logger)
45
+ this.opencodeConfigDir = getOpencodeConfigDir()
46
+ }
47
+
48
+ list(): WorkspaceDescriptor[] {
49
+ return Array.from(this.workspaces.values())
50
+ }
51
+
52
+ get(id: string): WorkspaceDescriptor | undefined {
53
+ return this.workspaces.get(id)
54
+ }
55
+
56
+ getInstancePort(id: string): number | undefined {
57
+ return this.workspaces.get(id)?.port
58
+ }
59
+
60
+ getInstanceAuthorizationHeader(id: string): string | undefined {
61
+ return this.opencodeAuth.get(id)?.authorization
62
+ }
63
+
64
+ listFiles(workspaceId: string, relativePath = "."): FileSystemEntry[] {
65
+ const workspace = this.requireWorkspace(workspaceId)
66
+ const browser = new FileSystemBrowser({ rootDir: workspace.path })
67
+ return browser.list(relativePath)
68
+ }
69
+
70
+ searchFiles(workspaceId: string, query: string, options?: WorkspaceFileSearchOptions): FileSystemEntry[] {
71
+ const workspace = this.requireWorkspace(workspaceId)
72
+ return searchWorkspaceFiles(workspace.path, query, options)
73
+ }
74
+
75
+ readFile(workspaceId: string, relativePath: string): WorkspaceFileResponse {
76
+ const workspace = this.requireWorkspace(workspaceId)
77
+ const browser = new FileSystemBrowser({ rootDir: workspace.path })
78
+ const contents = browser.readFile(relativePath)
79
+ return {
80
+ workspaceId,
81
+ relativePath,
82
+ contents,
83
+ }
84
+ }
85
+
86
+ async create(folder: string, name?: string): Promise<WorkspaceDescriptor> {
87
+
88
+ const id = `${Date.now().toString(36)}`
89
+ const binary = this.options.binaryRegistry.resolveDefault()
90
+ const resolvedBinaryPath = this.resolveBinaryPath(binary.path)
91
+ const workspacePath = path.isAbsolute(folder) ? folder : path.resolve(this.options.rootDir, folder)
92
+ clearWorkspaceSearchCache(workspacePath)
93
+
94
+ this.options.logger.info({ workspaceId: id, folder: workspacePath, binary: resolvedBinaryPath }, "Creating workspace")
95
+
96
+ const proxyPath = `/workspaces/${id}/worktrees/root/instance`
97
+
98
+
99
+ const descriptor: WorkspaceRecord = {
100
+ id,
101
+ path: workspacePath,
102
+ name,
103
+ status: "starting",
104
+ proxyPath,
105
+ binaryId: resolvedBinaryPath,
106
+ binaryLabel: binary.label,
107
+ binaryVersion: binary.version,
108
+ createdAt: new Date().toISOString(),
109
+ updatedAt: new Date().toISOString(),
110
+ }
111
+
112
+ if (!descriptor.binaryVersion) {
113
+ descriptor.binaryVersion = this.detectBinaryVersion(resolvedBinaryPath)
114
+ }
115
+
116
+ this.workspaces.set(id, descriptor)
117
+
118
+
119
+ this.options.eventBus.publish({ type: "workspace.created", workspace: descriptor })
120
+
121
+ const preferences = this.options.configStore.get().preferences ?? {}
122
+ const userEnvironment = preferences.environmentVariables ?? {}
123
+
124
+ const opencodeUsername = DEFAULT_OPENCODE_USERNAME
125
+ const opencodePassword = generateOpencodeServerPassword()
126
+ const authorization = buildOpencodeBasicAuthHeader({ username: opencodeUsername, password: opencodePassword })
127
+ if (!authorization) {
128
+ throw new Error("Failed to build OpenCode auth header")
129
+ }
130
+ this.opencodeAuth.set(id, { username: opencodeUsername, password: opencodePassword, authorization })
131
+
132
+ const environment = {
133
+ ...userEnvironment,
134
+ OPENCODE_CONFIG_DIR: this.opencodeConfigDir,
135
+ CODENOMAD_INSTANCE_ID: id,
136
+ CODENOMAD_BASE_URL: this.options.getServerBaseUrl(),
137
+ ...(this.options.nodeExtraCaCertsPath ? { NODE_EXTRA_CA_CERTS: this.options.nodeExtraCaCertsPath } : {}),
138
+ [OPENCODE_SERVER_USERNAME_ENV]: opencodeUsername,
139
+ [OPENCODE_SERVER_PASSWORD_ENV]: opencodePassword,
140
+ }
141
+
142
+ try {
143
+ const { pid, port, exitPromise, getLastOutput } = await this.runtime.launch({
144
+ workspaceId: id,
145
+ folder: workspacePath,
146
+ binaryPath: resolvedBinaryPath,
147
+ environment,
148
+ onExit: (info) => this.handleProcessExit(info.workspaceId, info),
149
+ })
150
+
151
+ await this.waitForWorkspaceReadiness({ workspaceId: id, port, exitPromise, getLastOutput })
152
+
153
+ descriptor.pid = pid
154
+ descriptor.port = port
155
+ descriptor.status = "ready"
156
+ descriptor.updatedAt = new Date().toISOString()
157
+ this.options.eventBus.publish({ type: "workspace.started", workspace: descriptor })
158
+ this.options.logger.info({ workspaceId: id, port }, "Workspace ready")
159
+ return descriptor
160
+ } catch (error) {
161
+ descriptor.status = "error"
162
+ descriptor.error = error instanceof Error ? error.message : String(error)
163
+ descriptor.updatedAt = new Date().toISOString()
164
+ this.options.eventBus.publish({ type: "workspace.error", workspace: descriptor })
165
+ this.options.logger.error({ workspaceId: id, err: error }, "Workspace failed to start")
166
+ throw error
167
+ }
168
+ }
169
+
170
+ async delete(id: string): Promise<WorkspaceDescriptor | undefined> {
171
+ const workspace = this.workspaces.get(id)
172
+ if (!workspace) return undefined
173
+
174
+ this.options.logger.info({ workspaceId: id }, "Stopping workspace")
175
+ const wasRunning = Boolean(workspace.pid)
176
+ if (wasRunning) {
177
+ await this.runtime.stop(id).catch((error) => {
178
+ this.options.logger.warn({ workspaceId: id, err: error }, "Failed to stop workspace process cleanly")
179
+ })
180
+ }
181
+
182
+ this.workspaces.delete(id)
183
+ this.opencodeAuth.delete(id)
184
+ clearWorkspaceSearchCache(workspace.path)
185
+ if (!wasRunning) {
186
+ this.options.eventBus.publish({ type: "workspace.stopped", workspaceId: id })
187
+ }
188
+ return workspace
189
+ }
190
+
191
+ async shutdown() {
192
+ this.options.logger.info("Shutting down all workspaces")
193
+
194
+ const stopTasks: Array<Promise<void>> = []
195
+
196
+ for (const [id, workspace] of this.workspaces) {
197
+ if (!workspace.pid) {
198
+ this.options.logger.debug({ workspaceId: id }, "Workspace already stopped")
199
+ continue
200
+ }
201
+
202
+ this.options.logger.info({ workspaceId: id }, "Stopping workspace during shutdown")
203
+ stopTasks.push(
204
+ this.runtime.stop(id).catch((error) => {
205
+ this.options.logger.error({ workspaceId: id, err: error }, "Failed to stop workspace during shutdown")
206
+ }),
207
+ )
208
+ }
209
+
210
+ if (stopTasks.length > 0) {
211
+ await Promise.allSettled(stopTasks)
212
+ }
213
+
214
+ this.workspaces.clear()
215
+ this.opencodeAuth.clear()
216
+ this.options.logger.info("All workspaces cleared")
217
+ }
218
+
219
+ private requireWorkspace(id: string): WorkspaceRecord {
220
+ const workspace = this.workspaces.get(id)
221
+ if (!workspace) {
222
+ throw new Error("Workspace not found")
223
+ }
224
+ return workspace
225
+ }
226
+
227
+ private resolveBinaryPath(identifier: string): string {
228
+ if (!identifier) {
229
+ return identifier
230
+ }
231
+
232
+ const looksLikePath = identifier.includes("/") || identifier.includes("\\") || identifier.startsWith(".")
233
+ if (path.isAbsolute(identifier) || looksLikePath) {
234
+ return identifier
235
+ }
236
+
237
+ const locator = process.platform === "win32" ? "where" : "which"
238
+
239
+ try {
240
+ const result = spawnSync(locator, [identifier], { encoding: "utf8" })
241
+ if (result.status === 0 && result.stdout) {
242
+ const candidates = result.stdout
243
+ .split(/\r?\n/)
244
+ .map((line) => line.trim())
245
+ .filter((line) => line.length > 0)
246
+ .filter((line) => !/^INFO:/i.test(line))
247
+
248
+ if (candidates.length > 0) {
249
+ const resolved = this.pickBinaryCandidate(candidates)
250
+ this.options.logger.debug({ identifier, resolved, candidates }, "Resolved binary path from system PATH")
251
+ return resolved
252
+ }
253
+ } else if (result.error) {
254
+ this.options.logger.warn({ identifier, err: result.error }, "Failed to resolve binary path via locator command")
255
+ }
256
+ } catch (error) {
257
+ this.options.logger.warn({ identifier, err: error }, "Failed to resolve binary path from system PATH")
258
+ }
259
+
260
+ return identifier
261
+ }
262
+
263
+ private pickBinaryCandidate(candidates: string[]): string {
264
+ if (process.platform !== "win32") {
265
+ return candidates[0] ?? ""
266
+ }
267
+
268
+ const extensionPreference = [".exe", ".cmd", ".bat", ".ps1"]
269
+
270
+ for (const ext of extensionPreference) {
271
+ const match = candidates.find((candidate) => candidate.toLowerCase().endsWith(ext))
272
+ if (match) {
273
+ return match
274
+ }
275
+ }
276
+
277
+ return candidates[0] ?? ""
278
+ }
279
+
280
+ private detectBinaryVersion(resolvedPath: string): string | undefined {
281
+ if (!resolvedPath) {
282
+ return undefined
283
+ }
284
+
285
+ try {
286
+ const result = spawnSync(resolvedPath, ["--version"], { encoding: "utf8" })
287
+ if (result.status === 0 && result.stdout) {
288
+ const line = result.stdout.split(/\r?\n/).find((entry) => entry.trim().length > 0)
289
+ if (line) {
290
+ const normalized = line.trim()
291
+ const versionMatch = normalized.match(/([0-9]+\.[0-9]+\.[0-9A-Za-z.-]+)/)
292
+ if (versionMatch) {
293
+ const version = versionMatch[1]
294
+ this.options.logger.debug({ binary: resolvedPath, version }, "Detected binary version")
295
+ return version
296
+ }
297
+ this.options.logger.debug({ binary: resolvedPath, reported: normalized }, "Binary reported version string")
298
+ return normalized
299
+ }
300
+ } else if (result.error) {
301
+ this.options.logger.warn({ binary: resolvedPath, err: result.error }, "Failed to read binary version")
302
+ }
303
+ } catch (error) {
304
+ this.options.logger.warn({ binary: resolvedPath, err: error }, "Failed to detect binary version")
305
+ }
306
+
307
+ return undefined
308
+ }
309
+
310
+ private async waitForWorkspaceReadiness(params: {
311
+ workspaceId: string
312
+ port: number
313
+ exitPromise: Promise<ProcessExitInfo>
314
+ getLastOutput: () => string
315
+ }) {
316
+
317
+ await Promise.race([
318
+ this.waitForPortAvailability(params.port),
319
+ params.exitPromise.then((info) => {
320
+ throw this.buildStartupError(
321
+ params.workspaceId,
322
+ "exited before becoming ready",
323
+ info,
324
+ params.getLastOutput(),
325
+ )
326
+ }),
327
+ ])
328
+
329
+ await this.waitForInstanceHealth(params)
330
+
331
+ await Promise.race([
332
+ this.delay(STARTUP_STABILITY_DELAY_MS),
333
+ params.exitPromise.then((info) => {
334
+ throw this.buildStartupError(
335
+ params.workspaceId,
336
+ "exited shortly after start",
337
+ info,
338
+ params.getLastOutput(),
339
+ )
340
+ }),
341
+ ])
342
+ }
343
+
344
+ private async waitForInstanceHealth(params: {
345
+ workspaceId: string
346
+ port: number
347
+ exitPromise: Promise<ProcessExitInfo>
348
+ getLastOutput: () => string
349
+ }) {
350
+ const probeResult = await Promise.race([
351
+ this.probeInstance(params.workspaceId, params.port),
352
+ params.exitPromise.then((info) => {
353
+ throw this.buildStartupError(
354
+ params.workspaceId,
355
+ "exited during health checks",
356
+ info,
357
+ params.getLastOutput(),
358
+ )
359
+ }),
360
+ ])
361
+
362
+ if (probeResult.ok) {
363
+ return
364
+ }
365
+
366
+ const latestOutput = params.getLastOutput().trim()
367
+ if (latestOutput) {
368
+ throw new Error(latestOutput)
369
+ }
370
+ const reason = probeResult.reason ?? "Health check failed"
371
+ throw new Error(`Workspace ${params.workspaceId} failed health check: ${reason}.`)
372
+ }
373
+
374
+ private async probeInstance(workspaceId: string, port: number): Promise<{ ok: boolean; reason?: string }> {
375
+ const url = `http://127.0.0.1:${port}/project/current`
376
+
377
+ try {
378
+ const headers: Record<string, string> = {}
379
+ const authHeader = this.opencodeAuth.get(workspaceId)?.authorization
380
+ if (authHeader) {
381
+ headers["Authorization"] = authHeader
382
+ }
383
+
384
+ const response = await fetch(url, { headers })
385
+ if (!response.ok) {
386
+ const reason = `health probe returned HTTP ${response.status}`
387
+ this.options.logger.debug({ workspaceId, status: response.status }, "Health probe returned server error")
388
+ return { ok: false, reason }
389
+ }
390
+ return { ok: true }
391
+ } catch (error) {
392
+ const reason = error instanceof Error ? error.message : String(error)
393
+ this.options.logger.debug({ workspaceId, err: error }, "Health probe failed")
394
+ return { ok: false, reason }
395
+ }
396
+ }
397
+
398
+ private buildStartupError(
399
+ workspaceId: string,
400
+ phase: string,
401
+ exitInfo: ProcessExitInfo,
402
+ lastOutput: string,
403
+ ): Error {
404
+ const exitDetails = this.describeExit(exitInfo)
405
+ const trimmedOutput = lastOutput.trim()
406
+ const outputDetails = trimmedOutput ? ` Last output: ${trimmedOutput}` : ""
407
+ return new Error(`Workspace ${workspaceId} ${phase} (${exitDetails}).${outputDetails}`)
408
+ }
409
+
410
+ private waitForPortAvailability(port: number, timeoutMs = 5000): Promise<void> {
411
+ return new Promise((resolve, reject) => {
412
+ const deadline = Date.now() + timeoutMs
413
+ let settled = false
414
+ let retryTimer: NodeJS.Timeout | null = null
415
+
416
+ const cleanup = () => {
417
+ settled = true
418
+ if (retryTimer) {
419
+ clearTimeout(retryTimer)
420
+ retryTimer = null
421
+ }
422
+ }
423
+
424
+ const tryConnect = () => {
425
+ if (settled) {
426
+ return
427
+ }
428
+ const socket = connect({ port, host: "127.0.0.1" }, () => {
429
+ cleanup()
430
+ socket.end()
431
+ resolve()
432
+ })
433
+ socket.once("error", () => {
434
+ socket.destroy()
435
+ if (settled) {
436
+ return
437
+ }
438
+ if (Date.now() >= deadline) {
439
+ cleanup()
440
+ reject(new Error(`Workspace port ${port} did not become ready within ${timeoutMs}ms`))
441
+ } else {
442
+ retryTimer = setTimeout(() => {
443
+ retryTimer = null
444
+ tryConnect()
445
+ }, 100)
446
+ }
447
+ })
448
+ }
449
+
450
+ tryConnect()
451
+ })
452
+ }
453
+
454
+ private delay(durationMs: number): Promise<void> {
455
+ if (durationMs <= 0) {
456
+ return Promise.resolve()
457
+ }
458
+ return new Promise((resolve) => setTimeout(resolve, durationMs))
459
+ }
460
+
461
+ private describeExit(info: ProcessExitInfo): string {
462
+ if (info.signal) {
463
+ return `signal ${info.signal}`
464
+ }
465
+ if (info.code !== null) {
466
+ return `code ${info.code}`
467
+ }
468
+ return "unknown reason"
469
+ }
470
+
471
+ private handleProcessExit(workspaceId: string, info: { code: number | null; requested: boolean }) {
472
+ const workspace = this.workspaces.get(workspaceId)
473
+ if (!workspace) return
474
+
475
+ this.opencodeAuth.delete(workspaceId)
476
+
477
+ this.options.logger.info({ workspaceId, ...info }, "Workspace process exited")
478
+
479
+ workspace.pid = undefined
480
+ workspace.port = undefined
481
+ workspace.updatedAt = new Date().toISOString()
482
+
483
+ if (info.requested || info.code === 0) {
484
+ workspace.status = "stopped"
485
+ workspace.error = undefined
486
+ this.options.eventBus.publish({ type: "workspace.stopped", workspaceId })
487
+ } else {
488
+ workspace.status = "error"
489
+ workspace.error = `Process exited with code ${info.code}`
490
+ this.options.eventBus.publish({ type: "workspace.error", workspace })
491
+ }
492
+ }
493
+ }
@@ -0,0 +1,22 @@
1
+ import crypto from "node:crypto"
2
+
3
+ export const OPENCODE_SERVER_USERNAME_ENV = "OPENCODE_SERVER_USERNAME" as const
4
+ export const OPENCODE_SERVER_PASSWORD_ENV = "OPENCODE_SERVER_PASSWORD" as const
5
+
6
+ export const DEFAULT_OPENCODE_USERNAME = "codenomad" as const
7
+
8
+ export function generateOpencodeServerPassword(): string {
9
+ return crypto.randomBytes(32).toString("base64url")
10
+ }
11
+
12
+ export function buildOpencodeBasicAuthHeader(params: { username?: string; password?: string }): string | undefined {
13
+ const username = params.username
14
+ const password = params.password
15
+
16
+ if (!username || !password) {
17
+ return undefined
18
+ }
19
+
20
+ const token = Buffer.from(`${username}:${password}`, "utf8").toString("base64")
21
+ return `Basic ${token}`
22
+ }