@dyyz1993/codenomad 0.15.0

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 (496) hide show
  1. package/README.md +173 -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 +140 -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 +576 -0
  10. package/dist/bin.js +24 -0
  11. package/dist/cli-upgrade.js +53 -0
  12. package/dist/cli-upgrade.test.js +31 -0
  13. package/dist/clients/connection-manager.js +93 -0
  14. package/dist/config/location.js +57 -0
  15. package/dist/config/schema.js +71 -0
  16. package/dist/events/bus.js +45 -0
  17. package/dist/filesystem/__tests__/browser.test.js +65 -0
  18. package/dist/filesystem/__tests__/search-cache.test.js +40 -0
  19. package/dist/filesystem/browser.js +306 -0
  20. package/dist/filesystem/search-cache.js +43 -0
  21. package/dist/filesystem/search.js +135 -0
  22. package/dist/index.js +477 -0
  23. package/dist/launcher.js +149 -0
  24. package/dist/loader.js +21 -0
  25. package/dist/logger.js +109 -0
  26. package/dist/opencode-config/README.md +32 -0
  27. package/dist/opencode-config/opencode.jsonc +3 -0
  28. package/dist/opencode-config/package-lock.json +380 -0
  29. package/dist/opencode-config/package.json +9 -0
  30. package/dist/opencode-config/plugin/codenomad.ts +62 -0
  31. package/dist/opencode-config/plugin/lib/background-process.ts +265 -0
  32. package/dist/opencode-config/plugin/lib/client.ts +133 -0
  33. package/dist/opencode-config/plugin/lib/request.ts +214 -0
  34. package/dist/opencode-config.js +26 -0
  35. package/dist/plugins/channel.js +40 -0
  36. package/dist/plugins/handlers.js +17 -0
  37. package/dist/plugins/voice-mode.js +78 -0
  38. package/dist/releases/dev-release-monitor.js +75 -0
  39. package/dist/releases/release-monitor.js +107 -0
  40. package/dist/server/__tests__/network-addresses.test.js +68 -0
  41. package/dist/server/__tests__/remote-proxy.test.js +204 -0
  42. package/dist/server/http-server.js +926 -0
  43. package/dist/server/network-addresses.js +114 -0
  44. package/dist/server/remote-proxy.js +466 -0
  45. package/dist/server/routes/auth-pages/login.html +135 -0
  46. package/dist/server/routes/auth-pages/token.html +93 -0
  47. package/dist/server/routes/auth.js +148 -0
  48. package/dist/server/routes/background-processes.js +78 -0
  49. package/dist/server/routes/events.js +66 -0
  50. package/dist/server/routes/filesystem.js +43 -0
  51. package/dist/server/routes/meta.js +44 -0
  52. package/dist/server/routes/plugin.js +70 -0
  53. package/dist/server/routes/remote-proxy.js +42 -0
  54. package/dist/server/routes/remote-servers.js +142 -0
  55. package/dist/server/routes/settings.js +69 -0
  56. package/dist/server/routes/sidecars.js +46 -0
  57. package/dist/server/routes/speech.js +63 -0
  58. package/dist/server/routes/storage.js +52 -0
  59. package/dist/server/routes/workspaces.js +221 -0
  60. package/dist/server/routes/worktrees.js +156 -0
  61. package/dist/server/tls.js +224 -0
  62. package/dist/settings/binaries.js +37 -0
  63. package/dist/settings/merge-patch.js +33 -0
  64. package/dist/settings/migrate.js +238 -0
  65. package/dist/settings/public-config.js +33 -0
  66. package/dist/settings/service.js +101 -0
  67. package/dist/settings/yaml-doc-store.js +96 -0
  68. package/dist/sidecars/manager.js +193 -0
  69. package/dist/speech/providers/openai-compatible.js +189 -0
  70. package/dist/speech/service.js +58 -0
  71. package/dist/storage/instance-store.js +56 -0
  72. package/dist/ui/__tests__/remote-ui.test.js +67 -0
  73. package/dist/ui/remote-ui.js +462 -0
  74. package/dist/workspaces/__tests__/git-worktrees.test.js +43 -0
  75. package/dist/workspaces/__tests__/spawn.test.js +140 -0
  76. package/dist/workspaces/git-mutations.js +98 -0
  77. package/dist/workspaces/git-status.js +323 -0
  78. package/dist/workspaces/git-worktrees.js +216 -0
  79. package/dist/workspaces/instance-events.js +180 -0
  80. package/dist/workspaces/manager.js +368 -0
  81. package/dist/workspaces/opencode-auth.js +34 -0
  82. package/dist/workspaces/opencode-auth.test.js +34 -0
  83. package/dist/workspaces/runtime.js +366 -0
  84. package/dist/workspaces/spawn.js +219 -0
  85. package/dist/workspaces/worktree-directory.js +74 -0
  86. package/dist/workspaces/worktree-map.js +116 -0
  87. package/package.json +50 -0
  88. package/public/apple-touch-icon-180x180.png +0 -0
  89. package/public/assets/ChangesTab-BRNmSjeC.js +2 -0
  90. package/public/assets/CodeNomad-Icon-bmTWNPXy.png +0 -0
  91. package/public/assets/DiffToolbar-DPPgfx42.js +1 -0
  92. package/public/assets/FilesTab-sEeGPOoz.js +2 -0
  93. package/public/assets/GitChangesTab-tJ-TCq0c.js +2 -0
  94. package/public/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  95. package/public/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  96. package/public/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  97. package/public/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  98. package/public/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  99. package/public/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  100. package/public/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  101. package/public/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  102. package/public/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  103. package/public/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  104. package/public/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  105. package/public/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  106. package/public/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  107. package/public/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  108. package/public/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  109. package/public/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  110. package/public/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  111. package/public/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  112. package/public/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  113. package/public/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  114. package/public/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  115. package/public/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  116. package/public/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  117. package/public/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  118. package/public/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  119. package/public/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  120. package/public/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  121. package/public/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  122. package/public/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  123. package/public/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  124. package/public/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  125. package/public/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  126. package/public/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  127. package/public/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  128. package/public/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  129. package/public/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  130. package/public/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  131. package/public/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  132. package/public/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  133. package/public/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  134. package/public/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  135. package/public/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  136. package/public/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  137. package/public/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  138. package/public/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  139. package/public/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  140. package/public/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  141. package/public/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  142. package/public/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  143. package/public/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  144. package/public/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  145. package/public/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  146. package/public/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  147. package/public/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  148. package/public/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  149. package/public/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  150. package/public/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  151. package/public/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  152. package/public/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  153. package/public/assets/SplitFilePanel-CHqNLZvU.js +1 -0
  154. package/public/assets/StatusTab-DMd0ByCA.js +1 -0
  155. package/public/assets/abap-BdImnpbu.js +1 -0
  156. package/public/assets/actionscript-3-CfeIJUat.js +1 -0
  157. package/public/assets/ada-bCR0ucgS.js +1 -0
  158. package/public/assets/align-justify-BWapkGTe.js +1 -0
  159. package/public/assets/andromeeda-C-Jbm3Hp.js +1 -0
  160. package/public/assets/angular-html-CU67Zn6k.js +1 -0
  161. package/public/assets/angular-ts-BwZT4LLn.js +1 -0
  162. package/public/assets/apache-Pmp26Uib.js +1 -0
  163. package/public/assets/apex-DhZLUxFE.js +1 -0
  164. package/public/assets/apl-dKokRX4l.js +1 -0
  165. package/public/assets/applescript-Co6uUVPk.js +1 -0
  166. package/public/assets/ara-BRHolxvo.js +1 -0
  167. package/public/assets/asciidoc-Dv7Oe6Be.js +1 -0
  168. package/public/assets/asm-D_Q5rh1f.js +1 -0
  169. package/public/assets/astro-CbQHKStN.js +1 -0
  170. package/public/assets/aurora-x-D-2ljcwZ.js +1 -0
  171. package/public/assets/awk-DMzUqQB5.js +1 -0
  172. package/public/assets/ayu-dark-Cv9koXgw.js +1 -0
  173. package/public/assets/ballerina-BFfxhgS-.js +1 -0
  174. package/public/assets/bat-BkioyH1T.js +1 -0
  175. package/public/assets/beancount-k_qm7-4y.js +1 -0
  176. package/public/assets/berry-D08WgyRC.js +1 -0
  177. package/public/assets/bibtex-CHM0blh-.js +1 -0
  178. package/public/assets/bicep-Bmn6On1c.js +1 -0
  179. package/public/assets/blade-DVc8C-J4.js +1 -0
  180. package/public/assets/bsl-BO_Y6i37.js +1 -0
  181. package/public/assets/bundle-full-CXlKQh5B.js +13 -0
  182. package/public/assets/c-BIGW1oBm.js +1 -0
  183. package/public/assets/cadence-Bv_4Rxtq.js +1 -0
  184. package/public/assets/cairo-KRGpt6FW.js +1 -0
  185. package/public/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  186. package/public/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  187. package/public/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  188. package/public/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  189. package/public/assets/clarity-D53aC0YG.js +1 -0
  190. package/public/assets/clojure-P80f7IUj.js +1 -0
  191. package/public/assets/cmake-D1j8_8rp.js +1 -0
  192. package/public/assets/cobol-nwyudZeR.js +1 -0
  193. package/public/assets/codeowners-Bp6g37R7.js +1 -0
  194. package/public/assets/codeql-DsOJ9woJ.js +1 -0
  195. package/public/assets/coffee-Ch7k5sss.js +1 -0
  196. package/public/assets/common-lisp-Cg-RD9OK.js +1 -0
  197. package/public/assets/coq-DkFqJrB1.js +1 -0
  198. package/public/assets/cpp-CofmeUqb.js +1 -0
  199. package/public/assets/crystal-tKQVLTB8.js +1 -0
  200. package/public/assets/csharp-CX12Zw3r.js +1 -0
  201. package/public/assets/css-DPfMkruS.js +1 -0
  202. package/public/assets/csv-fuZLfV_i.js +1 -0
  203. package/public/assets/cue-D82EKSYY.js +1 -0
  204. package/public/assets/cypher-COkxafJQ.js +1 -0
  205. package/public/assets/d-85-TOEBH.js +1 -0
  206. package/public/assets/dark-plus-eOWES_5F.js +1 -0
  207. package/public/assets/dart-CF10PKvl.js +1 -0
  208. package/public/assets/dax-CEL-wOlO.js +1 -0
  209. package/public/assets/desktop-BmXAJ9_W.js +1 -0
  210. package/public/assets/diff-D97Zzqfu.js +1 -0
  211. package/public/assets/diff-viewer-BFtW5J8P.js +1 -0
  212. package/public/assets/docker-BcOcwvcX.js +1 -0
  213. package/public/assets/dotenv-Da5cRb03.js +1 -0
  214. package/public/assets/dracula-BzJJZx-M.js +1 -0
  215. package/public/assets/dracula-soft-BXkSAIEj.js +1 -0
  216. package/public/assets/dream-maker-BtqSS_iP.js +1 -0
  217. package/public/assets/edge-BkV0erSs.js +1 -0
  218. package/public/assets/elixir-CDX3lj18.js +1 -0
  219. package/public/assets/elm-DbKCFpqz.js +1 -0
  220. package/public/assets/emacs-lisp-C9XAeP06.js +1 -0
  221. package/public/assets/erb-BOJIQeun.js +1 -0
  222. package/public/assets/erlang-DsQrWhSR.js +1 -0
  223. package/public/assets/everforest-dark-BgDCqdQA.js +1 -0
  224. package/public/assets/everforest-light-C8M2exoo.js +1 -0
  225. package/public/assets/fast-diff-vendor-DgdwVvTQ.js +1 -0
  226. package/public/assets/fennel-BYunw83y.js +1 -0
  227. package/public/assets/fish-BvzEVeQv.js +1 -0
  228. package/public/assets/fluent-C4IJs8-o.js +1 -0
  229. package/public/assets/fortran-fixed-form-BZjJHVRy.js +1 -0
  230. package/public/assets/fortran-free-form-D22FLkUw.js +1 -0
  231. package/public/assets/fsharp-CXgrBDvD.js +1 -0
  232. package/public/assets/gdresource-B7Tvp0Sc.js +1 -0
  233. package/public/assets/gdscript-DTMYz4Jt.js +1 -0
  234. package/public/assets/gdshader-DkwncUOv.js +1 -0
  235. package/public/assets/genie-D0YGMca9.js +1 -0
  236. package/public/assets/gherkin-DyxjwDmM.js +1 -0
  237. package/public/assets/git-commit-F4YmCXRG.js +1 -0
  238. package/public/assets/git-diff-vendor-CSgooKT_.js +52 -0
  239. package/public/assets/git-diff-vendor-HAZkIolJ.css +19 -0
  240. package/public/assets/git-rebase-r7XF79zn.js +1 -0
  241. package/public/assets/github-dark-DHJKELXO.js +1 -0
  242. package/public/assets/github-dark-default-Cuk6v7N8.js +1 -0
  243. package/public/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  244. package/public/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  245. package/public/assets/github-light-DAi9KRSo.js +1 -0
  246. package/public/assets/github-light-default-D7oLnXFd.js +1 -0
  247. package/public/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  248. package/public/assets/gleam-BspZqrRM.js +1 -0
  249. package/public/assets/glimmer-js-Rg0-pVw9.js +1 -0
  250. package/public/assets/glimmer-ts-U6CK756n.js +1 -0
  251. package/public/assets/glsl-DplSGwfg.js +1 -0
  252. package/public/assets/gnuplot-DdkO51Og.js +1 -0
  253. package/public/assets/go-Dn2_MT6a.js +1 -0
  254. package/public/assets/graphql-ChdNCCLP.js +1 -0
  255. package/public/assets/groovy-gcz8RCvz.js +1 -0
  256. package/public/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  257. package/public/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  258. package/public/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  259. package/public/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  260. package/public/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  261. package/public/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  262. package/public/assets/hack-CaT9iCJl.js +1 -0
  263. package/public/assets/haml-B8DHNrY2.js +1 -0
  264. package/public/assets/handlebars-BL8al0AC.js +1 -0
  265. package/public/assets/haskell-Df6bDoY_.js +1 -0
  266. package/public/assets/haxe-CzTSHFRz.js +1 -0
  267. package/public/assets/hcl-BWvSN4gD.js +1 -0
  268. package/public/assets/highlight-vendor-8FKMu9os.js +10 -0
  269. package/public/assets/hjson-D5-asLiD.js +1 -0
  270. package/public/assets/hlsl-D3lLCCz7.js +1 -0
  271. package/public/assets/houston-DnULxvSX.js +1 -0
  272. package/public/assets/html-GMplVEZG.js +1 -0
  273. package/public/assets/html-derivative-BFtXZ54Q.js +1 -0
  274. package/public/assets/http-jrhK8wxY.js +1 -0
  275. package/public/assets/hurl-irOxFIW8.js +1 -0
  276. package/public/assets/hxml-Bvhsp5Yf.js +1 -0
  277. package/public/assets/hy-DFXneXwc.js +1 -0
  278. package/public/assets/imba-DGztddWO.js +1 -0
  279. package/public/assets/index-Bdy3MTIn.js +1 -0
  280. package/public/assets/index-BvRe9GiS.js +1 -0
  281. package/public/assets/index-Ctq8RqRf.js +1 -0
  282. package/public/assets/index-D-NvysdX.js +1 -0
  283. package/public/assets/index-D6RMBXUk.js +2 -0
  284. package/public/assets/index-DXI9DoVB.css +1 -0
  285. package/public/assets/index-DYWkPhKo.js +1 -0
  286. package/public/assets/index-D_FDiI6a.js +1 -0
  287. package/public/assets/index-Dty5bSHf.js +1 -0
  288. package/public/assets/index-H16e4Rqc.js +1 -0
  289. package/public/assets/ini-BEwlwnbL.js +1 -0
  290. package/public/assets/java-CylS5w8V.js +1 -0
  291. package/public/assets/javascript-wDzz0qaB.js +1 -0
  292. package/public/assets/jinja-4LBKfQ-Z.js +1 -0
  293. package/public/assets/jison-wvAkD_A8.js +1 -0
  294. package/public/assets/json-Cp-IABpG.js +1 -0
  295. package/public/assets/json5-C9tS-k6U.js +1 -0
  296. package/public/assets/jsonc-Des-eS-w.js +1 -0
  297. package/public/assets/jsonl-DcaNXYhu.js +1 -0
  298. package/public/assets/jsonnet-DFQXde-d.js +1 -0
  299. package/public/assets/jssm-C2t-YnRu.js +1 -0
  300. package/public/assets/jsx-g9-lgVsj.js +1 -0
  301. package/public/assets/julia-C8NyazO9.js +1 -0
  302. package/public/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  303. package/public/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  304. package/public/assets/kanagawa-wave-DWedfzmr.js +1 -0
  305. package/public/assets/kdl-DV7GczEv.js +1 -0
  306. package/public/assets/kotlin-BdnUsdx6.js +1 -0
  307. package/public/assets/kusto-BvAqAH-y.js +1 -0
  308. package/public/assets/laserwave-DUszq2jm.js +1 -0
  309. package/public/assets/latex-BUKiar2Z.js +1 -0
  310. package/public/assets/lean-DP1Csr6i.js +1 -0
  311. package/public/assets/less-B1dDrJ26.js +1 -0
  312. package/public/assets/light-plus-B7mTdjB0.js +1 -0
  313. package/public/assets/liquid-DYVedYrR.js +1 -0
  314. package/public/assets/llvm-BtvRca6l.js +1 -0
  315. package/public/assets/loading-CmEVQgyj.css +1 -0
  316. package/public/assets/loading-W2Y_wR0P.js +1 -0
  317. package/public/assets/log-2UxHyX5q.js +1 -0
  318. package/public/assets/logo-BtOb2qkB.js +1 -0
  319. package/public/assets/lua-BbnMAYS6.js +1 -0
  320. package/public/assets/luau-CXu1NL6O.js +1 -0
  321. package/public/assets/main-Bala0YJ4.js +57 -0
  322. package/public/assets/make-CHLpvVh8.js +1 -0
  323. package/public/assets/markdown-Cvjx9yec.js +1 -0
  324. package/public/assets/markdown-Db8cvZ4Z.js +315 -0
  325. package/public/assets/marko-CPi9NSCl.js +1 -0
  326. package/public/assets/material-theme-D5KoaKCx.js +1 -0
  327. package/public/assets/material-theme-darker-BfHTSMKl.js +1 -0
  328. package/public/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  329. package/public/assets/material-theme-ocean-CyktbL80.js +1 -0
  330. package/public/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  331. package/public/assets/matlab-D7o27uSR.js +1 -0
  332. package/public/assets/mdc-DUICxH0z.js +1 -0
  333. package/public/assets/mdx-Cmh6b_Ma.js +1 -0
  334. package/public/assets/mermaid-DKYwYmdq.js +1 -0
  335. package/public/assets/min-dark-CafNBF8u.js +1 -0
  336. package/public/assets/min-light-CTRr51gU.js +1 -0
  337. package/public/assets/mipsasm-CKIfxQSi.js +1 -0
  338. package/public/assets/mojo-1DNp92w6.js +1 -0
  339. package/public/assets/monaco-viewer-DWQqXKm3.js +26 -0
  340. package/public/assets/monokai-D4h5O-jR.js +1 -0
  341. package/public/assets/move-Bu9oaDYs.js +1 -0
  342. package/public/assets/narrat-DRg8JJMk.js +1 -0
  343. package/public/assets/nextflow-CUEJCptM.js +1 -0
  344. package/public/assets/nginx-DknmC5AR.js +1 -0
  345. package/public/assets/night-owl-C39BiMTA.js +1 -0
  346. package/public/assets/nim-CVrawwO9.js +1 -0
  347. package/public/assets/nix-BbRYJGeE.js +1 -0
  348. package/public/assets/nord-Ddv68eIx.js +1 -0
  349. package/public/assets/nushell-C-sUppwS.js +1 -0
  350. package/public/assets/objective-c-DXmwc3jG.js +1 -0
  351. package/public/assets/objective-cpp-CLxacb5B.js +1 -0
  352. package/public/assets/ocaml-C0hk2d4L.js +1 -0
  353. package/public/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  354. package/public/assets/one-light-PoHY5YXO.js +1 -0
  355. package/public/assets/pascal-D93ZcfNL.js +1 -0
  356. package/public/assets/perl-C0TMdlhV.js +1 -0
  357. package/public/assets/php-CDn_0X-4.js +1 -0
  358. package/public/assets/pkl-u5AG7uiY.js +1 -0
  359. package/public/assets/plastic-3e1v2bzS.js +1 -0
  360. package/public/assets/plsql-ChMvpjG-.js +1 -0
  361. package/public/assets/po-BTJTHyun.js +1 -0
  362. package/public/assets/poimandres-CS3Unz2-.js +1 -0
  363. package/public/assets/polar-C0HS_06l.js +1 -0
  364. package/public/assets/postcss-CXtECtnM.js +1 -0
  365. package/public/assets/powerquery-CEu0bR-o.js +1 -0
  366. package/public/assets/powershell-Dpen1YoG.js +1 -0
  367. package/public/assets/prisma-Dd19v3D-.js +1 -0
  368. package/public/assets/prolog-CbFg5uaA.js +1 -0
  369. package/public/assets/proto-DyJlTyXw.js +1 -0
  370. package/public/assets/pug-CGlum2m_.js +1 -0
  371. package/public/assets/puppet-BMWR74SV.js +1 -0
  372. package/public/assets/purescript-CklMAg4u.js +1 -0
  373. package/public/assets/python-B6aJPvgy.js +1 -0
  374. package/public/assets/qml-3beO22l8.js +1 -0
  375. package/public/assets/qmldir-C8lEn-DE.js +1 -0
  376. package/public/assets/qss-IeuSbFQv.js +1 -0
  377. package/public/assets/r-DiinP2Uv.js +1 -0
  378. package/public/assets/racket-BqYA7rlc.js +1 -0
  379. package/public/assets/raku-DXvB9xmW.js +1 -0
  380. package/public/assets/razor-WgofotgN.js +1 -0
  381. package/public/assets/red-bN70gL4F.js +1 -0
  382. package/public/assets/reg-C-SQnVFl.js +1 -0
  383. package/public/assets/regexp-CDVJQ6XC.js +1 -0
  384. package/public/assets/rel-C3B-1QV4.js +1 -0
  385. package/public/assets/riscv-BM1_JUlF.js +1 -0
  386. package/public/assets/rose-pine-BHrmToEH.js +1 -0
  387. package/public/assets/rose-pine-dawn-CnK8MTSM.js +1 -0
  388. package/public/assets/rose-pine-moon-NleAzG8P.js +1 -0
  389. package/public/assets/rosmsg-BJDFO7_C.js +1 -0
  390. package/public/assets/rst-B0xPkSld.js +1 -0
  391. package/public/assets/ruby-BvKwtOVI.js +1 -0
  392. package/public/assets/rust-B1yitclQ.js +1 -0
  393. package/public/assets/sas-cz2c8ADy.js +1 -0
  394. package/public/assets/sass-Cj5Yp3dK.js +1 -0
  395. package/public/assets/scala-C151Ov-r.js +1 -0
  396. package/public/assets/scheme-C98Dy4si.js +1 -0
  397. package/public/assets/scss-OYdSNvt2.js +1 -0
  398. package/public/assets/sdbl-DVxCFoDh.js +1 -0
  399. package/public/assets/shaderlab-Dg9Lc6iA.js +1 -0
  400. package/public/assets/shellscript-Yzrsuije.js +1 -0
  401. package/public/assets/shellsession-BADoaaVG.js +1 -0
  402. package/public/assets/slack-dark-BthQWCQV.js +1 -0
  403. package/public/assets/slack-ochin-DqwNpetd.js +1 -0
  404. package/public/assets/smalltalk-BERRCDM3.js +1 -0
  405. package/public/assets/snazzy-light-Bw305WKR.js +1 -0
  406. package/public/assets/solarized-dark-DXbdFlpD.js +1 -0
  407. package/public/assets/solarized-light-L9t79GZl.js +1 -0
  408. package/public/assets/solidity-BbcW6ACK.js +1 -0
  409. package/public/assets/soy-Brmx7dQM.js +1 -0
  410. package/public/assets/sparql-rVzFXLq3.js +1 -0
  411. package/public/assets/splunk-BtCnVYZw.js +1 -0
  412. package/public/assets/sql-BLtJtn59.js +1 -0
  413. package/public/assets/ssh-config-_ykCGR6B.js +1 -0
  414. package/public/assets/stata-BH5u7GGu.js +1 -0
  415. package/public/assets/stylus-BEDo0Tqx.js +1 -0
  416. package/public/assets/svelte-3Dk4HxPD.js +1 -0
  417. package/public/assets/swift-Dg5xB15N.js +1 -0
  418. package/public/assets/synthwave-84-CbfX1IO0.js +1 -0
  419. package/public/assets/system-verilog-CnnmHF94.js +1 -0
  420. package/public/assets/systemd-4A_iFExJ.js +1 -0
  421. package/public/assets/talonscript-CkByrt1z.js +1 -0
  422. package/public/assets/tasl-QIJgUcNo.js +1 -0
  423. package/public/assets/tcl-dwOrl1Do.js +1 -0
  424. package/public/assets/templ-W15q3VgB.js +1 -0
  425. package/public/assets/terraform-BETggiCN.js +1 -0
  426. package/public/assets/tex-Cppo0RY3.js +1 -0
  427. package/public/assets/tokyo-night-hegEt444.js +1 -0
  428. package/public/assets/toml-vGWfd6FD.js +1 -0
  429. package/public/assets/tool-call-D0BYXlFe.js +59 -0
  430. package/public/assets/ts-tags-zn1MmPIZ.js +1 -0
  431. package/public/assets/tsv-B_m7g4N7.js +1 -0
  432. package/public/assets/tsx-COt5Ahok.js +1 -0
  433. package/public/assets/turtle-BsS91CYL.js +1 -0
  434. package/public/assets/twig-CO9l9SDP.js +1 -0
  435. package/public/assets/typescript-BPQ3VLAy.js +1 -0
  436. package/public/assets/typespec-Df68jz8_.js +1 -0
  437. package/public/assets/typst-DHCkPAjA.js +1 -0
  438. package/public/assets/unified-picker-QgONWj_1.js +1 -0
  439. package/public/assets/v-BcVCzyr7.js +1 -0
  440. package/public/assets/vala-CsfeWuGM.js +1 -0
  441. package/public/assets/vb-D17OF-Vu.js +1 -0
  442. package/public/assets/verilog-BQ8w6xss.js +1 -0
  443. package/public/assets/vesper-DU1UobuO.js +1 -0
  444. package/public/assets/vhdl-CeAyd5Ju.js +1 -0
  445. package/public/assets/viml-CJc9bBzg.js +1 -0
  446. package/public/assets/vitesse-black-Bkuqu6BP.js +1 -0
  447. package/public/assets/vitesse-dark-D0r3Knsf.js +1 -0
  448. package/public/assets/vitesse-light-CVO1_9PV.js +1 -0
  449. package/public/assets/vue-CCoi5OLL.js +1 -0
  450. package/public/assets/vue-html-DAAvJJDi.js +1 -0
  451. package/public/assets/vue-vine-_Ih-lPRR.js +1 -0
  452. package/public/assets/vyper-CDx5xZoG.js +1 -0
  453. package/public/assets/wasm-CG6Dc4jp.js +1 -0
  454. package/public/assets/wasm-MzD3tlZU.js +1 -0
  455. package/public/assets/wenyan-BV7otONQ.js +1 -0
  456. package/public/assets/wgsl-Dx-B1_4e.js +1 -0
  457. package/public/assets/wikitext-BhOHFoWU.js +1 -0
  458. package/public/assets/wit-5i3qLPDT.js +1 -0
  459. package/public/assets/wolfram-lXgVvXCa.js +1 -0
  460. package/public/assets/wrap-text-CEH9wOr_.js +1 -0
  461. package/public/assets/xml-sdJ4AIDG.js +1 -0
  462. package/public/assets/xsl-CtQFsRM5.js +1 -0
  463. package/public/assets/yaml-Buea-lGh.js +1 -0
  464. package/public/assets/zenscript-DVFEvuxE.js +1 -0
  465. package/public/assets/zig-VOosw3JB.js +1 -0
  466. package/public/favicon.ico +0 -0
  467. package/public/index.html +42 -0
  468. package/public/loading.html +33 -0
  469. package/public/logo.png +0 -0
  470. package/public/manifest.webmanifest +1 -0
  471. package/public/maskable-icon-512x512.png +0 -0
  472. package/public/monaco/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
  473. package/public/monaco/vs/base/worker/workerMain.js +31 -0
  474. package/public/monaco/vs/basic-languages/cpp/cpp.js +10 -0
  475. package/public/monaco/vs/basic-languages/kotlin/kotlin.js +10 -0
  476. package/public/monaco/vs/basic-languages/markdown/markdown.js +10 -0
  477. package/public/monaco/vs/basic-languages/python/python.js +10 -0
  478. package/public/monaco/vs/editor/editor.main.css +8 -0
  479. package/public/monaco/vs/editor/editor.main.js +798 -0
  480. package/public/monaco/vs/language/css/cssMode.js +13 -0
  481. package/public/monaco/vs/language/css/cssWorker.js +77 -0
  482. package/public/monaco/vs/language/html/htmlMode.js +13 -0
  483. package/public/monaco/vs/language/html/htmlWorker.js +454 -0
  484. package/public/monaco/vs/language/json/jsonMode.js +19 -0
  485. package/public/monaco/vs/language/json/jsonWorker.js +42 -0
  486. package/public/monaco/vs/language/typescript/tsMode.js +20 -0
  487. package/public/monaco/vs/language/typescript/tsWorker.js +51328 -0
  488. package/public/monaco/vs/loader.js +11 -0
  489. package/public/monaco.worker.js +7 -0
  490. package/public/pwa-192x192.png +0 -0
  491. package/public/pwa-512x512.png +0 -0
  492. package/public/pwa-64x64.png +0 -0
  493. package/public/registerSW.js +1 -0
  494. package/public/sw.js +1 -0
  495. package/public/ui-version.json +3 -0
  496. package/public/workbox-60d14903.js +1 -0
@@ -0,0 +1,265 @@
1
+ import path from "path"
2
+ import { tool } from "@opencode-ai/plugin/tool"
3
+ import { createCodeNomadRequester, type CodeNomadConfig } from "./request"
4
+
5
+ type BackgroundProcess = {
6
+ id: string
7
+ title: string
8
+ command: string
9
+ status: "running" | "stopped" | "error"
10
+ startedAt: string
11
+ stoppedAt?: string
12
+ exitCode?: number
13
+ outputSizeBytes?: number
14
+ }
15
+
16
+ type BackgroundProcessNotificationRequest = {
17
+ sessionID: string
18
+ directory: string
19
+ }
20
+
21
+ type BackgroundProcessOptions = {
22
+ baseDir: string
23
+ }
24
+
25
+ type ParsedCommand = {
26
+ head: string
27
+ args: string[]
28
+ }
29
+
30
+ export function createBackgroundProcessTools(config: CodeNomadConfig, options: BackgroundProcessOptions) {
31
+ const requester = createCodeNomadRequester(config)
32
+
33
+ const request = async <T>(path: string, init?: RequestInit): Promise<T> => {
34
+ return requester.requestJson<T>(`/background-processes${path}`, init)
35
+ }
36
+
37
+ return {
38
+ run_background_process: tool({
39
+ description:
40
+ "Run a long-lived background process (dev servers, DBs, watchers) so it keeps running while you do other tasks. Use it for running processes that timeout otherwise or produce a lot of output.",
41
+ args: {
42
+ title: tool.schema.string().describe("Short label for the process (e.g. Dev server, DB server)"),
43
+ command: tool.schema.string().describe("Shell command to run in the workspace"),
44
+ notify: tool.schema.boolean().optional().describe("Notify the current session when the process ends"),
45
+ },
46
+ async execute(args, context) {
47
+ assertCommandWithinBase(args.command, options.baseDir)
48
+ const notification: BackgroundProcessNotificationRequest | undefined = args.notify
49
+ ? {
50
+ sessionID: context.sessionID,
51
+ directory: context.directory,
52
+ }
53
+ : undefined
54
+ const process = await request<BackgroundProcess>("", {
55
+ method: "POST",
56
+ body: JSON.stringify({ title: args.title, command: args.command, notify: args.notify, notification }),
57
+ })
58
+
59
+ return `Started background process ${process.id} (${process.title})\nStatus: ${process.status}\nCommand: ${process.command}`
60
+ },
61
+ }),
62
+ list_background_processes: tool({
63
+ description: "List background processes running for this workspace.",
64
+ args: {},
65
+ async execute() {
66
+ const response = await request<{ processes: BackgroundProcess[] }>("")
67
+ if (response.processes.length === 0) {
68
+ return "No background processes running."
69
+ }
70
+
71
+ return response.processes
72
+ .map((process) => {
73
+ const status = process.status === "running" ? "running" : process.status
74
+ const exit = process.exitCode !== undefined ? ` (exit ${process.exitCode})` : ""
75
+ const size =
76
+ typeof process.outputSizeBytes === "number" ? ` | ${Math.round(process.outputSizeBytes / 1024)}KB` : ""
77
+ return `- ${process.id} | ${process.title} | ${status}${exit}${size}\n ${process.command}`
78
+ })
79
+ .join("\n")
80
+ },
81
+ }),
82
+ read_background_process_output: tool({
83
+ description: "Read output from a background process. Use full, grep, head, or tail.",
84
+ args: {
85
+ id: tool.schema.string().describe("Background process ID"),
86
+ method: tool.schema
87
+ .enum(["full", "grep", "head", "tail"])
88
+ .default("full")
89
+ .describe("Method to read output"),
90
+ pattern: tool.schema.string().optional().describe("Pattern for grep method"),
91
+ lines: tool.schema.number().optional().describe("Number of lines for head/tail methods"),
92
+ },
93
+ async execute(args) {
94
+ if (args.method === "grep" && !args.pattern) {
95
+ return "Pattern is required for grep method."
96
+ }
97
+
98
+ const params = new URLSearchParams({ method: args.method })
99
+ if (args.pattern) {
100
+ params.set("pattern", args.pattern)
101
+ }
102
+ if (args.lines) {
103
+ params.set("lines", String(args.lines))
104
+ }
105
+
106
+ const response = await request<{ id: string; content: string; truncated: boolean; sizeBytes: number }>(
107
+ `/${args.id}/output?${params.toString()}`,
108
+ )
109
+
110
+ const header = response.truncated
111
+ ? `Output (truncated, ${Math.round(response.sizeBytes / 1024)}KB):`
112
+ : `Output (${Math.round(response.sizeBytes / 1024)}KB):`
113
+
114
+ return `${header}\n\n${response.content}`
115
+ },
116
+ }),
117
+ stop_background_process: tool({
118
+ description: "Stop a background process (SIGTERM) but keep its output and entry.",
119
+ args: {
120
+ id: tool.schema.string().describe("Background process ID"),
121
+ },
122
+ async execute(args) {
123
+ const process = await request<BackgroundProcess>(`/${args.id}/stop`, { method: "POST" })
124
+ return `Stopped background process ${process.id} (${process.title}). Status: ${process.status}`
125
+ },
126
+ }),
127
+ terminate_background_process: tool({
128
+ description: "Terminate a background process and delete its output + entry.",
129
+ args: {
130
+ id: tool.schema.string().describe("Background process ID"),
131
+ },
132
+ async execute(args) {
133
+ await request<void>(`/${args.id}/terminate`, { method: "POST" })
134
+ return `Terminated background process ${args.id} and removed its output.`
135
+ },
136
+ }),
137
+ }
138
+ }
139
+
140
+ const FILE_COMMANDS = new Set(["cd", "rm", "cp", "mv", "mkdir", "touch", "chmod", "chown"])
141
+ const EXPANSION_CHARS = /[~*$?\[\]`$]/
142
+
143
+ function assertCommandWithinBase(command: string, baseDir: string) {
144
+ const normalizedBase = path.resolve(baseDir)
145
+ const commands = splitCommands(command)
146
+
147
+ for (const item of commands) {
148
+ if (!FILE_COMMANDS.has(item.head)) {
149
+ continue
150
+ }
151
+
152
+ for (const arg of item.args) {
153
+ if (!arg) continue
154
+ if (arg.startsWith("-") || (item.head === "chmod" && arg.startsWith("+"))) continue
155
+
156
+ const literalArg = unquote(arg)
157
+ if (EXPANSION_CHARS.test(literalArg)) {
158
+ throw new Error(`Background process commands may only reference paths within ${normalizedBase}.`)
159
+ }
160
+
161
+ const resolved = path.isAbsolute(literalArg) ? path.normalize(literalArg) : path.resolve(normalizedBase, literalArg)
162
+ if (!isWithinBase(normalizedBase, resolved)) {
163
+ throw new Error(`Background process commands may only reference paths within ${normalizedBase}.`)
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ function splitCommands(command: string): ParsedCommand[] {
170
+ const tokens = tokenize(command)
171
+ const commands: ParsedCommand[] = []
172
+ let current: string[] = []
173
+
174
+ for (const token of tokens) {
175
+ if (isSeparator(token)) {
176
+ if (current.length > 0) {
177
+ commands.push({ head: current[0], args: current.slice(1) })
178
+ current = []
179
+ }
180
+ continue
181
+ }
182
+ current.push(token)
183
+ }
184
+
185
+ if (current.length > 0) {
186
+ commands.push({ head: current[0], args: current.slice(1) })
187
+ }
188
+
189
+ return commands
190
+ }
191
+
192
+ function tokenize(input: string): string[] {
193
+ const tokens: string[] = []
194
+ let current = ""
195
+ let quote: "'" | '"' | null = null
196
+ let escape = false
197
+
198
+ const flush = () => {
199
+ if (current.length > 0) {
200
+ tokens.push(current)
201
+ current = ""
202
+ }
203
+ }
204
+
205
+ for (let index = 0; index < input.length; index += 1) {
206
+ const char = input[index]
207
+
208
+ if (escape) {
209
+ current += char
210
+ escape = false
211
+ continue
212
+ }
213
+
214
+ if (char === "\\" && quote !== "'") {
215
+ escape = true
216
+ continue
217
+ }
218
+
219
+ if (quote) {
220
+ current += char
221
+ if (char === quote) {
222
+ quote = null
223
+ }
224
+ continue
225
+ }
226
+
227
+ if (char === "'" || char === '"') {
228
+ quote = char
229
+ current += char
230
+ continue
231
+ }
232
+
233
+ if (char === " " || char === "\n" || char === "\t") {
234
+ flush()
235
+ continue
236
+ }
237
+
238
+ if (char === "|" || char === "&" || char === ";") {
239
+ flush()
240
+ tokens.push(char)
241
+ continue
242
+ }
243
+
244
+ current += char
245
+ }
246
+
247
+ flush()
248
+ return tokens
249
+ }
250
+
251
+ function isSeparator(token: string): boolean {
252
+ return token === "|" || token === "&" || token === ";"
253
+ }
254
+
255
+ function unquote(token: string): string {
256
+ if ((token.startsWith('"') && token.endsWith('"')) || (token.startsWith("'") && token.endsWith("'"))) {
257
+ return token.slice(1, -1)
258
+ }
259
+ return token
260
+ }
261
+
262
+ function isWithinBase(base: string, candidate: string): boolean {
263
+ const relative = path.relative(base, candidate)
264
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))
265
+ }
@@ -0,0 +1,133 @@
1
+ import { createCodeNomadRequester, type CodeNomadConfig, type PluginEvent } from "./request"
2
+
3
+ export { getCodeNomadConfig, type CodeNomadConfig, type PluginEvent } from "./request"
4
+
5
+ export function createCodeNomadClient(config: CodeNomadConfig) {
6
+ const requester = createCodeNomadRequester(config)
7
+
8
+ return {
9
+ postEvent: (event: PluginEvent) =>
10
+ requester.requestVoid("/event", {
11
+ method: "POST",
12
+ body: JSON.stringify(event),
13
+ }),
14
+ startEvents: (onEvent: (event: PluginEvent) => void) => startPluginEvents(requester, onEvent),
15
+ }
16
+ }
17
+
18
+ function delay(ms: number) {
19
+ return new Promise<void>((resolve) => setTimeout(resolve, ms))
20
+ }
21
+
22
+ async function startPluginEvents(
23
+ requester: ReturnType<typeof createCodeNomadRequester>,
24
+ onEvent: (event: PluginEvent) => void,
25
+ ) {
26
+ // Fail plugin startup if we cannot establish the initial connection.
27
+ const initialBody = await connectWithRetries(requester, 3)
28
+
29
+ // After startup, keep reconnecting; throw after 3 consecutive failures.
30
+ void consumeWithReconnect(requester, onEvent, initialBody)
31
+ }
32
+
33
+ async function connectWithRetries(requester: ReturnType<typeof createCodeNomadRequester>, maxAttempts: number) {
34
+ let lastError: unknown
35
+
36
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
37
+ try {
38
+ return await requester.requestSseBody("/events")
39
+ } catch (error) {
40
+ lastError = error
41
+ await delay(500 * attempt)
42
+ }
43
+ }
44
+
45
+ const reason = lastError instanceof Error ? lastError.message : String(lastError)
46
+ const url = requester.buildUrl("/events")
47
+ throw new Error(`[CodeNomadPlugin] Failed to connect to CodeNomad at ${url} after ${maxAttempts} retries: ${reason}`)
48
+ }
49
+
50
+ async function consumeWithReconnect(
51
+ requester: ReturnType<typeof createCodeNomadRequester>,
52
+ onEvent: (event: PluginEvent) => void,
53
+ initialBody: ReadableStream<Uint8Array>,
54
+ ) {
55
+ let consecutiveFailures = 0
56
+ let body: ReadableStream<Uint8Array> | null = initialBody
57
+
58
+ while (true) {
59
+ try {
60
+ if (!body) {
61
+ body = await connectWithRetries(requester, 3)
62
+ }
63
+
64
+ await consumeSseBody(body, onEvent)
65
+ body = null
66
+ consecutiveFailures = 0
67
+ } catch (error) {
68
+ body = null
69
+ consecutiveFailures += 1
70
+ if (consecutiveFailures >= 3) {
71
+ const reason = error instanceof Error ? error.message : String(error)
72
+ throw new Error(`[CodeNomadPlugin] Plugin event stream failed after 3 retries: ${reason}`)
73
+ }
74
+ await delay(500 * consecutiveFailures)
75
+ }
76
+ }
77
+ }
78
+
79
+ async function consumeSseBody(body: ReadableStream<Uint8Array>, onEvent: (event: PluginEvent) => void) {
80
+ const reader = body.getReader()
81
+ const decoder = new TextDecoder()
82
+ let buffer = ""
83
+
84
+ while (true) {
85
+ const { done, value } = await reader.read()
86
+ if (done || !value) {
87
+ break
88
+ }
89
+
90
+ buffer += decoder.decode(value, { stream: true })
91
+
92
+ let separatorIndex = buffer.indexOf("\n\n")
93
+ while (separatorIndex >= 0) {
94
+ const chunk = buffer.slice(0, separatorIndex)
95
+ buffer = buffer.slice(separatorIndex + 2)
96
+ separatorIndex = buffer.indexOf("\n\n")
97
+
98
+ const event = parseSseChunk(chunk)
99
+ if (event) {
100
+ onEvent(event)
101
+ }
102
+ }
103
+ }
104
+
105
+ throw new Error("SSE stream ended")
106
+ }
107
+
108
+ function parseSseChunk(chunk: string): PluginEvent | null {
109
+ const lines = chunk.split(/\r?\n/)
110
+ const dataLines: string[] = []
111
+
112
+ for (const line of lines) {
113
+ if (line.startsWith(":")) continue
114
+ if (line.startsWith("data:")) {
115
+ dataLines.push(line.slice(5).trimStart())
116
+ }
117
+ }
118
+
119
+ if (dataLines.length === 0) return null
120
+
121
+ const payload = dataLines.join("\n").trim()
122
+ if (!payload) return null
123
+
124
+ try {
125
+ const parsed = JSON.parse(payload)
126
+ if (!parsed || typeof parsed !== "object" || typeof (parsed as any).type !== "string") {
127
+ return null
128
+ }
129
+ return parsed as PluginEvent
130
+ } catch {
131
+ return null
132
+ }
133
+ }
@@ -0,0 +1,214 @@
1
+ import http from "http"
2
+ import https from "https"
3
+ import { Readable } from "stream"
4
+
5
+ export type PluginEvent = {
6
+ type: string
7
+ properties?: Record<string, unknown>
8
+ }
9
+
10
+ export type CodeNomadConfig = {
11
+ instanceId: string
12
+ baseUrl: string
13
+ }
14
+
15
+ export function getCodeNomadConfig(): CodeNomadConfig {
16
+ return {
17
+ instanceId: requireEnv("CODENOMAD_INSTANCE_ID"),
18
+ baseUrl: requireEnv("CODENOMAD_BASE_URL"),
19
+ }
20
+ }
21
+
22
+ export function createCodeNomadRequester(config: CodeNomadConfig) {
23
+ const rawBaseUrl = (config.baseUrl ?? "").trim()
24
+ const baseUrl = rawBaseUrl.replace(/\/+$/, "")
25
+ const pluginBase = `${baseUrl}/workspaces/${encodeURIComponent(config.instanceId)}/plugin`
26
+ const authorization = buildInstanceAuthorizationHeader()
27
+
28
+ const buildUrl = (path: string) => {
29
+ if (path.startsWith("http://") || path.startsWith("https://")) {
30
+ return path
31
+ }
32
+ const normalized = path.startsWith("/") ? path : `/${path}`
33
+ return `${pluginBase}${normalized}`
34
+ }
35
+
36
+ const buildHeaders = (headers: HeadersInit | undefined, hasBody: boolean): Record<string, string> => {
37
+ const output: Record<string, string> = normalizeHeaders(headers)
38
+ output.Authorization = authorization
39
+ if (hasBody) {
40
+ output["Content-Type"] = output["Content-Type"] ?? "application/json"
41
+ }
42
+ return output
43
+ }
44
+
45
+ const fetchWithAuth = async (path: string, init?: RequestInit): Promise<Response> => {
46
+ const url = buildUrl(path)
47
+ const hasBody = init?.body !== undefined
48
+ const headers = buildHeaders(init?.headers, hasBody)
49
+
50
+ // The CodeNomad plugin only talks to the local CodeNomad server.
51
+ // Use a single request implementation that tolerates custom/self-signed certs
52
+ // without disabling TLS verification for the whole Node process.
53
+ return nodeFetch(url, { ...init, headers }, { rejectUnauthorized: false })
54
+ }
55
+
56
+ const requestJson = async <T>(path: string, init?: RequestInit): Promise<T> => {
57
+ const response = await fetchWithAuth(path, init)
58
+ if (!response.ok) {
59
+ const message = await response.text().catch(() => "")
60
+ throw new Error(message || `Request failed with ${response.status}`)
61
+ }
62
+
63
+ if (response.status === 204) {
64
+ return undefined as T
65
+ }
66
+
67
+ return (await response.json()) as T
68
+ }
69
+
70
+ const requestVoid = async (path: string, init?: RequestInit): Promise<void> => {
71
+ const response = await fetchWithAuth(path, init)
72
+ if (!response.ok) {
73
+ const message = await response.text().catch(() => "")
74
+ throw new Error(message || `Request failed with ${response.status}`)
75
+ }
76
+ }
77
+
78
+ const requestSseBody = async (path: string): Promise<ReadableStream<Uint8Array>> => {
79
+ const response = await fetchWithAuth(path, { headers: { Accept: "text/event-stream" } })
80
+ if (!response.ok || !response.body) {
81
+ throw new Error(`SSE unavailable (${response.status})`)
82
+ }
83
+ return response.body as ReadableStream<Uint8Array>
84
+ }
85
+
86
+ return {
87
+ buildUrl,
88
+ fetch: fetchWithAuth,
89
+ requestJson,
90
+ requestVoid,
91
+ requestSseBody,
92
+ }
93
+ }
94
+
95
+ async function nodeFetch(
96
+ url: string,
97
+ init: RequestInit & { headers?: Record<string, string> },
98
+ tls: { rejectUnauthorized: boolean },
99
+ ): Promise<Response> {
100
+ const parsed = new URL(url)
101
+ const isHttps = parsed.protocol === "https:"
102
+ const requestFn = isHttps ? https.request : http.request
103
+
104
+ const method = (init.method ?? "GET").toUpperCase()
105
+ const headers = init.headers ?? {}
106
+ const body = init.body
107
+
108
+ return await new Promise<Response>((resolve, reject) => {
109
+ const req = requestFn(
110
+ {
111
+ protocol: parsed.protocol,
112
+ hostname: parsed.hostname,
113
+ port: parsed.port ? Number(parsed.port) : undefined,
114
+ path: `${parsed.pathname}${parsed.search}`,
115
+ method,
116
+ headers,
117
+ ...(isHttps ? { rejectUnauthorized: tls.rejectUnauthorized } : {}),
118
+ },
119
+ (res) => {
120
+ const responseHeaders = new Headers()
121
+ for (const [key, value] of Object.entries(res.headers)) {
122
+ if (value === undefined) continue
123
+ if (Array.isArray(value)) {
124
+ responseHeaders.set(key, value.join(", "))
125
+ } else {
126
+ responseHeaders.set(key, String(value))
127
+ }
128
+ }
129
+
130
+ // Convert Node stream -> Web ReadableStream for Response.
131
+ const webBody = Readable.toWeb(res) as unknown as ReadableStream<Uint8Array>
132
+ resolve(new Response(webBody, { status: res.statusCode ?? 0, headers: responseHeaders }))
133
+ },
134
+ )
135
+
136
+ const signal = init.signal
137
+ const abort = () => {
138
+ const err = new Error("Request aborted")
139
+ ;(err as any).name = "AbortError"
140
+ req.destroy(err)
141
+ reject(err)
142
+ }
143
+
144
+ if (signal) {
145
+ if (signal.aborted) {
146
+ abort()
147
+ return
148
+ }
149
+ signal.addEventListener("abort", abort, { once: true })
150
+ req.once("close", () => signal.removeEventListener("abort", abort))
151
+ }
152
+
153
+ req.once("error", reject)
154
+
155
+ if (body === undefined || body === null) {
156
+ req.end()
157
+ return
158
+ }
159
+
160
+ if (typeof body === "string") {
161
+ req.end(body)
162
+ return
163
+ }
164
+
165
+ if (body instanceof Uint8Array) {
166
+ req.end(Buffer.from(body))
167
+ return
168
+ }
169
+
170
+ if (body instanceof ArrayBuffer) {
171
+ req.end(Buffer.from(new Uint8Array(body)))
172
+ return
173
+ }
174
+
175
+ // Fallback for less common BodyInit types.
176
+ req.end(String(body))
177
+ })
178
+ }
179
+
180
+ function requireEnv(key: string): string {
181
+ const value = process.env[key]
182
+ if (!value || !value.trim()) {
183
+ throw new Error(`[CodeNomadPlugin] Missing required env var ${key}`)
184
+ }
185
+ return value
186
+ }
187
+
188
+ function buildInstanceAuthorizationHeader(): string {
189
+ const username = requireEnv("OPENCODE_SERVER_USERNAME")
190
+ const password = requireEnv("OPENCODE_SERVER_PASSWORD")
191
+ const token = Buffer.from(`${username}:${password}`, "utf8").toString("base64")
192
+ return `Basic ${token}`
193
+ }
194
+
195
+ function normalizeHeaders(headers: HeadersInit | undefined): Record<string, string> {
196
+ const output: Record<string, string> = {}
197
+ if (!headers) return output
198
+
199
+ if (headers instanceof Headers) {
200
+ headers.forEach((value, key) => {
201
+ output[key] = value
202
+ })
203
+ return output
204
+ }
205
+
206
+ if (Array.isArray(headers)) {
207
+ for (const [key, value] of headers) {
208
+ output[key] = value
209
+ }
210
+ return output
211
+ }
212
+
213
+ return { ...headers }
214
+ }
@@ -0,0 +1,26 @@
1
+ import { existsSync } from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { createLogger } from "./logger";
5
+ const log = createLogger({ component: "opencode-config" });
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const devTemplateDir = path.resolve(__dirname, "../../opencode-config");
9
+ const resourcesPath = process.resourcesPath;
10
+ const prodTemplateDirs = [
11
+ resourcesPath ? path.resolve(resourcesPath, "opencode-config") : undefined,
12
+ path.resolve(__dirname, "opencode-config"),
13
+ ].filter((dir) => Boolean(dir));
14
+ const isDevBuild = Boolean(process.env.CODENOMAD_DEV ?? process.env.CLI_UI_DEV_SERVER) || existsSync(devTemplateDir);
15
+ const templateDir = isDevBuild
16
+ ? devTemplateDir
17
+ : prodTemplateDirs.find((dir) => existsSync(dir)) ?? prodTemplateDirs[0];
18
+ export function getOpencodeConfigDir() {
19
+ if (!existsSync(templateDir)) {
20
+ throw new Error(`CodeNomad Opencode config template missing at ${templateDir}`);
21
+ }
22
+ if (isDevBuild) {
23
+ log.debug({ templateDir }, "Using Opencode config template directly (dev mode)");
24
+ }
25
+ return templateDir;
26
+ }
@@ -0,0 +1,40 @@
1
+ export class PluginChannelManager {
2
+ constructor(logger) {
3
+ this.logger = logger;
4
+ this.clients = new Set();
5
+ }
6
+ register(workspaceId, reply) {
7
+ const connection = { workspaceId, reply };
8
+ this.clients.add(connection);
9
+ this.logger.debug({ workspaceId }, "Plugin SSE client connected");
10
+ let closed = false;
11
+ const close = () => {
12
+ if (closed)
13
+ return;
14
+ closed = true;
15
+ this.clients.delete(connection);
16
+ this.logger.debug({ workspaceId }, "Plugin SSE client disconnected");
17
+ };
18
+ return { close };
19
+ }
20
+ send(workspaceId, event) {
21
+ for (const client of this.clients) {
22
+ if (client.workspaceId !== workspaceId)
23
+ continue;
24
+ this.write(client.reply, event);
25
+ }
26
+ }
27
+ broadcast(event) {
28
+ for (const client of this.clients) {
29
+ this.write(client.reply, event);
30
+ }
31
+ }
32
+ write(reply, event) {
33
+ try {
34
+ reply.raw.write(`data: ${JSON.stringify(event)}\n\n`);
35
+ }
36
+ catch (error) {
37
+ this.logger.warn({ err: error }, "Failed to write plugin SSE event");
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,17 @@
1
+ export function handlePluginEvent(workspaceId, event, deps) {
2
+ switch (event.type) {
3
+ case "codenomad.pong":
4
+ deps.logger.debug({ workspaceId, properties: event.properties }, "Plugin pong received");
5
+ return;
6
+ default:
7
+ deps.logger.debug({ workspaceId, eventType: event.type }, "Unhandled plugin event");
8
+ }
9
+ }
10
+ export function buildPingEvent() {
11
+ return {
12
+ type: "codenomad.ping",
13
+ properties: {
14
+ ts: Date.now(),
15
+ },
16
+ };
17
+ }