@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,428 @@
1
+ import { ChildProcess, spawn, spawnSync } from "child_process"
2
+ import { existsSync, statSync } from "fs"
3
+ import path from "path"
4
+ import { EventBus } from "../events/bus"
5
+ import { LogLevel, WorkspaceLogEntry } from "../api-types"
6
+ import { Logger } from "../logger"
7
+
8
+ export const WINDOWS_CMD_EXTENSIONS = new Set([".cmd", ".bat"])
9
+ export const WINDOWS_POWERSHELL_EXTENSIONS = new Set([".ps1"])
10
+
11
+ export function buildSpawnSpec(binaryPath: string, args: string[]) {
12
+ if (process.platform !== "win32") {
13
+ return { command: binaryPath, args, options: {} as const }
14
+ }
15
+
16
+ const extension = path.extname(binaryPath).toLowerCase()
17
+
18
+ if (WINDOWS_CMD_EXTENSIONS.has(extension)) {
19
+ const comspec = process.env.ComSpec || "cmd.exe"
20
+ // cmd.exe requires the full command as a single string.
21
+ // Using the ""<script> <args>"" pattern ensures paths with spaces are handled.
22
+ const commandLine = `""${binaryPath}" ${args.join(" ")}"`
23
+
24
+ return {
25
+ command: comspec,
26
+ args: ["/d", "/s", "/c", commandLine],
27
+ options: { windowsVerbatimArguments: true } as const,
28
+ }
29
+ }
30
+
31
+ if (WINDOWS_POWERSHELL_EXTENSIONS.has(extension)) {
32
+ // powershell.exe ships with Windows. (pwsh may not.)
33
+ return {
34
+ command: "powershell.exe",
35
+ args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", binaryPath, ...args],
36
+ options: {} as const,
37
+ }
38
+ }
39
+
40
+ return { command: binaryPath, args, options: {} as const }
41
+ }
42
+
43
+ const SENSITIVE_ENV_KEY = /(PASSWORD|TOKEN|SECRET)/i
44
+
45
+ function redactEnvironment(env: Record<string, string | undefined>): Record<string, string | undefined> {
46
+ const redacted: Record<string, string | undefined> = {}
47
+ for (const [key, value] of Object.entries(env)) {
48
+ if (value === undefined) {
49
+ redacted[key] = value
50
+ continue
51
+ }
52
+ redacted[key] = SENSITIVE_ENV_KEY.test(key) ? "[REDACTED]" : value
53
+ }
54
+ return redacted
55
+ }
56
+
57
+ interface LaunchOptions {
58
+ workspaceId: string
59
+ folder: string
60
+ binaryPath: string
61
+ environment?: Record<string, string>
62
+ onExit?: (info: ProcessExitInfo) => void
63
+ }
64
+
65
+ export interface ProcessExitInfo {
66
+ workspaceId: string
67
+ code: number | null
68
+ signal: NodeJS.Signals | null
69
+ requested: boolean
70
+ }
71
+
72
+ interface ManagedProcess {
73
+ child: ChildProcess
74
+ requestedStop: boolean
75
+ }
76
+
77
+ export class WorkspaceRuntime {
78
+ private processes = new Map<string, ManagedProcess>()
79
+
80
+ constructor(private readonly eventBus: EventBus, private readonly logger: Logger) {}
81
+
82
+ async launch(options: LaunchOptions): Promise<{ pid: number; port: number; exitPromise: Promise<ProcessExitInfo>; getLastOutput: () => string }> {
83
+ this.validateFolder(options.folder)
84
+
85
+ const args = ["serve", "--port", "0", "--print-logs", "--log-level", "DEBUG"]
86
+ const env = { ...process.env, ...(options.environment ?? {}) }
87
+
88
+ let exitResolve: ((info: ProcessExitInfo) => void) | null = null
89
+ const exitPromise = new Promise<ProcessExitInfo>((resolveExit) => {
90
+ exitResolve = resolveExit
91
+ })
92
+
93
+ // Store recent output for debugging - keep last 50 lines from each stream
94
+ const MAX_OUTPUT_LINES = 50
95
+ const recentStdout: string[] = []
96
+ const recentStderr: string[] = []
97
+ const getLastOutput = () => {
98
+ const combined: string[] = []
99
+ if (recentStderr.length > 0) {
100
+ combined.push("Error Stream")
101
+ combined.push(...recentStderr.slice(-10))
102
+ }
103
+ if (recentStdout.length > 0) {
104
+ combined.push("Output Stream")
105
+ combined.push(...recentStdout.slice(-10))
106
+ }
107
+ return combined.join("\n")
108
+ }
109
+
110
+ return new Promise((resolve, reject) => {
111
+ const spec = buildSpawnSpec(options.binaryPath, args)
112
+ const commandLine = [spec.command, ...spec.args].join(" ")
113
+ this.logger.info(
114
+ {
115
+ workspaceId: options.workspaceId,
116
+ folder: options.folder,
117
+ binary: options.binaryPath,
118
+ spawnCommand: spec.command,
119
+ commandLine,
120
+ },
121
+ "Launching OpenCode process",
122
+ )
123
+
124
+ this.logger.debug(
125
+ {
126
+ workspaceId: options.workspaceId,
127
+ spawnArgs: spec.args,
128
+ },
129
+ "OpenCode spawn args",
130
+ )
131
+
132
+ this.logger.trace(
133
+ {
134
+ workspaceId: options.workspaceId,
135
+ env: redactEnvironment(env),
136
+ },
137
+ "OpenCode spawn environment",
138
+ )
139
+ const detached = process.platform !== "win32"
140
+ const child = spawn(spec.command, spec.args, {
141
+ cwd: options.folder,
142
+ env,
143
+ stdio: ["ignore", "pipe", "pipe"],
144
+ detached,
145
+ ...spec.options,
146
+ })
147
+
148
+ const managed: ManagedProcess = { child, requestedStop: false }
149
+ this.processes.set(options.workspaceId, managed)
150
+
151
+ let stdoutBuffer = ""
152
+ let stderrBuffer = ""
153
+ let portFound = false
154
+
155
+ let warningTimer: NodeJS.Timeout | null = null
156
+
157
+ const startWarningTimer = () => {
158
+ warningTimer = setInterval(() => {
159
+ this.logger.warn({ workspaceId: options.workspaceId }, "Workspace runtime has not reported a port yet")
160
+ }, 10000)
161
+ }
162
+
163
+ const stopWarningTimer = () => {
164
+ if (warningTimer) {
165
+ clearInterval(warningTimer)
166
+ warningTimer = null
167
+ }
168
+ }
169
+
170
+ startWarningTimer()
171
+
172
+ const cleanupStreams = () => {
173
+ stopWarningTimer()
174
+ child.stdout?.removeAllListeners()
175
+ child.stderr?.removeAllListeners()
176
+ }
177
+
178
+ const handleExit = (code: number | null, signal: NodeJS.Signals | null) => {
179
+ this.logger.info({ workspaceId: options.workspaceId, code, signal }, "OpenCode process exited")
180
+ this.processes.delete(options.workspaceId)
181
+ cleanupStreams()
182
+ child.removeListener("error", handleError)
183
+ child.removeListener("exit", handleExit)
184
+ const exitInfo: ProcessExitInfo = {
185
+ workspaceId: options.workspaceId,
186
+ code,
187
+ signal,
188
+ requested: managed.requestedStop,
189
+ }
190
+ if (exitResolve) {
191
+ exitResolve(exitInfo)
192
+ exitResolve = null
193
+ }
194
+ if (!portFound) {
195
+ const recentOutput = getLastOutput().trim()
196
+ const reason = recentOutput || stderrBuffer || `Process exited with code ${code}`
197
+ reject(new Error(reason))
198
+ } else {
199
+ options.onExit?.(exitInfo)
200
+ }
201
+ }
202
+
203
+ const handleError = (error: Error) => {
204
+ cleanupStreams()
205
+ child.removeListener("exit", handleExit)
206
+ this.processes.delete(options.workspaceId)
207
+ this.logger.error({ workspaceId: options.workspaceId, err: error }, "Workspace runtime error")
208
+ if (exitResolve) {
209
+ exitResolve({ workspaceId: options.workspaceId, code: null, signal: null, requested: managed.requestedStop })
210
+ exitResolve = null
211
+ }
212
+ reject(error)
213
+ }
214
+
215
+ child.on("error", handleError)
216
+ child.on("exit", handleExit)
217
+
218
+ child.stdout?.on("data", (data: Buffer) => {
219
+ const text = data.toString()
220
+ stdoutBuffer += text
221
+ const lines = stdoutBuffer.split("\n")
222
+ stdoutBuffer = lines.pop() ?? ""
223
+
224
+ for (const line of lines) {
225
+ const trimmed = line.trim()
226
+ if (!trimmed) continue
227
+
228
+ recentStdout.push(trimmed)
229
+ if (recentStdout.length > MAX_OUTPUT_LINES) {
230
+ recentStdout.shift()
231
+ }
232
+
233
+ this.emitLog(options.workspaceId, "info", line)
234
+
235
+ if (!portFound) {
236
+ const portMatch = line.match(/opencode server listening on http:\/\/.+:(\d+)/i)
237
+ if (portMatch) {
238
+ portFound = true
239
+ stopWarningTimer()
240
+ child.removeListener("error", handleError)
241
+ const port = parseInt(portMatch[1], 10)
242
+ this.logger.info({ workspaceId: options.workspaceId, port }, "Workspace runtime allocated port")
243
+ resolve({ pid: child.pid!, port, exitPromise, getLastOutput })
244
+ }
245
+ }
246
+ }
247
+ })
248
+
249
+ child.stderr?.on("data", (data: Buffer) => {
250
+ const text = data.toString()
251
+ stderrBuffer += text
252
+ const lines = stderrBuffer.split("\n")
253
+ stderrBuffer = lines.pop() ?? ""
254
+
255
+ for (const line of lines) {
256
+ const trimmed = line.trim()
257
+ if (!trimmed) continue
258
+
259
+ recentStderr.push(trimmed)
260
+ if (recentStderr.length > MAX_OUTPUT_LINES) {
261
+ recentStderr.shift()
262
+ }
263
+
264
+ this.emitLog(options.workspaceId, "error", line)
265
+ }
266
+ })
267
+ })
268
+ }
269
+
270
+ async stop(workspaceId: string): Promise<void> {
271
+ const managed = this.processes.get(workspaceId)
272
+ if (!managed) return
273
+
274
+ managed.requestedStop = true
275
+ const child = managed.child
276
+ this.logger.info({ workspaceId }, "Stopping OpenCode process")
277
+
278
+ const pid = child.pid
279
+ if (!pid) {
280
+ this.logger.warn({ workspaceId }, "Workspace process missing PID; cannot stop")
281
+ return
282
+ }
283
+
284
+ const isAlreadyExited = () => child.exitCode !== null || child.signalCode !== null
285
+
286
+ const tryKillPosixGroup = (signal: NodeJS.Signals) => {
287
+ try {
288
+ // Negative PID targets the process group (POSIX).
289
+ process.kill(-pid, signal)
290
+ return true
291
+ } catch (error) {
292
+ const err = error as NodeJS.ErrnoException
293
+ if (err?.code === "ESRCH") {
294
+ return true
295
+ }
296
+ this.logger.debug({ workspaceId, pid, err }, "Failed to signal POSIX process group")
297
+ return false
298
+ }
299
+ }
300
+
301
+ const tryKillSinglePid = (signal: NodeJS.Signals) => {
302
+ try {
303
+ process.kill(pid, signal)
304
+ return true
305
+ } catch (error) {
306
+ const err = error as NodeJS.ErrnoException
307
+ if (err?.code === "ESRCH") {
308
+ return true
309
+ }
310
+ this.logger.debug({ workspaceId, pid, err }, "Failed to signal workspace PID")
311
+ return false
312
+ }
313
+ }
314
+
315
+ const tryTaskkill = (force: boolean) => {
316
+ const args = ["/PID", String(pid), "/T"]
317
+ if (force) {
318
+ args.push("/F")
319
+ }
320
+
321
+ try {
322
+ const result = spawnSync("taskkill", args, { encoding: "utf8" })
323
+ const exitCode = result.status
324
+ if (exitCode === 0) {
325
+ return true
326
+ }
327
+ // If the PID is already gone, treat it as success.
328
+ const stderr = (result.stderr ?? "").toString().toLowerCase()
329
+ const stdout = (result.stdout ?? "").toString().toLowerCase()
330
+ const combined = `${stdout}\n${stderr}`
331
+ if (combined.includes("not found") || combined.includes("no running instance") || combined.includes("process") && combined.includes("not")) {
332
+ return true
333
+ }
334
+ this.logger.debug({ workspaceId, pid, exitCode, stderr: result.stderr, stdout: result.stdout }, "taskkill failed")
335
+ return false
336
+ } catch (error) {
337
+ this.logger.debug({ workspaceId, pid, err: error }, "taskkill failed to execute")
338
+ return false
339
+ }
340
+ }
341
+
342
+ const sendStopSignal = (signal: NodeJS.Signals) => {
343
+ if (process.platform === "win32") {
344
+ // Best-effort: terminate the whole process tree rooted at pid.
345
+ // Use /F only for escalation.
346
+ tryTaskkill(signal === "SIGKILL")
347
+ return
348
+ }
349
+
350
+ // Prefer process-group signaling so wrapper launchers (bun/node) don't orphan the real server.
351
+ const groupOk = tryKillPosixGroup(signal)
352
+ if (!groupOk) {
353
+ // Fallback to direct PID kill.
354
+ tryKillSinglePid(signal)
355
+ }
356
+ }
357
+
358
+ await new Promise<void>((resolve, reject) => {
359
+ let escalationTimer: NodeJS.Timeout | null = null
360
+
361
+ const cleanup = () => {
362
+ child.removeListener("exit", onExit)
363
+ child.removeListener("error", onError)
364
+ if (escalationTimer) {
365
+ clearTimeout(escalationTimer)
366
+ escalationTimer = null
367
+ }
368
+ }
369
+
370
+ const onExit = () => {
371
+ cleanup()
372
+ resolve()
373
+ }
374
+ const onError = (error: Error) => {
375
+ cleanup()
376
+ reject(error)
377
+ }
378
+
379
+ if (isAlreadyExited()) {
380
+ this.logger.debug({ workspaceId, exitCode: child.exitCode, signal: child.signalCode }, "Process already exited")
381
+ cleanup()
382
+ resolve()
383
+ return
384
+ }
385
+
386
+ child.once("exit", onExit)
387
+ child.once("error", onError)
388
+
389
+ this.logger.debug(
390
+ { workspaceId, pid, detached: process.platform !== "win32" },
391
+ "Sending SIGTERM to workspace process (tree/group)",
392
+ )
393
+ sendStopSignal("SIGTERM")
394
+
395
+ escalationTimer = setTimeout(() => {
396
+ escalationTimer = null
397
+ if (isAlreadyExited()) {
398
+ this.logger.debug({ workspaceId, pid }, "Workspace exited before SIGKILL escalation")
399
+ return
400
+ }
401
+ this.logger.warn({ workspaceId, pid }, "Process did not stop after SIGTERM, escalating")
402
+ sendStopSignal("SIGKILL")
403
+ }, 2000)
404
+ })
405
+ }
406
+
407
+ private emitLog(workspaceId: string, level: LogLevel, message: string) {
408
+ const entry: WorkspaceLogEntry = {
409
+ workspaceId,
410
+ timestamp: new Date().toISOString(),
411
+ level,
412
+ message: message.trim(),
413
+ }
414
+
415
+ this.eventBus.publish({ type: "workspace.log", entry })
416
+ }
417
+
418
+ private validateFolder(folder: string) {
419
+ const resolved = path.resolve(folder)
420
+ if (!existsSync(resolved)) {
421
+ throw new Error(`Folder does not exist: ${resolved}`)
422
+ }
423
+ const stats = statSync(resolved)
424
+ if (!stats.isDirectory()) {
425
+ throw new Error(`Path is not a directory: ${resolved}`)
426
+ }
427
+ }
428
+ }
@@ -0,0 +1,129 @@
1
+ import fs from "fs"
2
+ import { promises as fsp } from "fs"
3
+ import path from "path"
4
+ import type { WorktreeMap } from "../api-types"
5
+ import { resolveRepoRoot } from "./git-worktrees"
6
+ import type { LogLike } from "./git-worktrees"
7
+
8
+ const DEFAULT_MAP: WorktreeMap = {
9
+ version: 1,
10
+ defaultWorktreeSlug: "root",
11
+ parentSessionWorktreeSlug: {},
12
+ }
13
+
14
+ function getMapPath(repoRoot: string): string {
15
+ return path.join(repoRoot, ".codenomad", "worktreeMap.json")
16
+ }
17
+
18
+ function getGitExcludePath(repoRoot: string): string {
19
+ return path.join(repoRoot, ".git", "info", "exclude")
20
+ }
21
+
22
+ async function ensureGitExclude(repoRoot: string, logger?: LogLike): Promise<void> {
23
+ const excludePath = getGitExcludePath(repoRoot)
24
+ try {
25
+ await fsp.mkdir(path.dirname(excludePath), { recursive: true })
26
+ } catch {
27
+ return
28
+ }
29
+
30
+ const entries = [
31
+ ".codenomad/worktrees/",
32
+ ".codenomad/worktreeMap.json",
33
+ ]
34
+
35
+ let existing = ""
36
+ try {
37
+ existing = await fsp.readFile(excludePath, "utf-8")
38
+ } catch (error) {
39
+ const code = (error as NodeJS.ErrnoException).code
40
+ if (code !== "ENOENT") {
41
+ logger?.debug?.({ err: error, excludePath }, "Failed to read .git/info/exclude")
42
+ return
43
+ }
44
+ existing = ""
45
+ }
46
+
47
+ const lines = new Set(existing.split(/\r?\n/).map((l) => l.trim()).filter(Boolean))
48
+ const missing = entries.filter((e) => !lines.has(e))
49
+ if (missing.length === 0) {
50
+ return
51
+ }
52
+
53
+ const header = existing.includes("# codenomad") ? "" : (existing.trim() ? "\n" : "") + "# codenomad\n"
54
+ const suffix = missing.map((e) => `${e}\n`).join("")
55
+ await fsp.writeFile(excludePath, `${existing}${header}${suffix}`, "utf-8")
56
+ }
57
+
58
+ export async function ensureCodenomadGitExclude(workspaceFolder: string, logger?: LogLike): Promise<void> {
59
+ const { repoRoot, isGitRepo } = await resolveRepoRoot(workspaceFolder, logger)
60
+ if (!isGitRepo) {
61
+ return
62
+ }
63
+ await ensureGitExclude(repoRoot, logger)
64
+ }
65
+
66
+ export async function readWorktreeMap(workspaceFolder: string, logger?: LogLike): Promise<WorktreeMap> {
67
+ const { repoRoot, isGitRepo } = await resolveRepoRoot(workspaceFolder, logger)
68
+ const filePath = getMapPath(repoRoot)
69
+ try {
70
+ const raw = await fsp.readFile(filePath, "utf-8")
71
+ const parsed = JSON.parse(raw)
72
+ if (!parsed || typeof parsed !== "object") {
73
+ return DEFAULT_MAP
74
+ }
75
+ const version = (parsed as any).version
76
+ if (version !== 1) {
77
+ return DEFAULT_MAP
78
+ }
79
+ const defaultWorktreeSlug = typeof (parsed as any).defaultWorktreeSlug === "string" ? (parsed as any).defaultWorktreeSlug : "root"
80
+ const parentSessionWorktreeSlug = (parsed as any).parentSessionWorktreeSlug
81
+ const mapping = parentSessionWorktreeSlug && typeof parentSessionWorktreeSlug === "object" ? parentSessionWorktreeSlug : {}
82
+ return {
83
+ version: 1,
84
+ defaultWorktreeSlug,
85
+ parentSessionWorktreeSlug: { ...mapping },
86
+ }
87
+ } catch (error) {
88
+ const code = (error as NodeJS.ErrnoException).code
89
+ if (code === "ENOENT") {
90
+ if (isGitRepo) {
91
+ // Best-effort ignore setup on first use.
92
+ await ensureGitExclude(repoRoot, logger).catch(() => undefined)
93
+ }
94
+ return DEFAULT_MAP
95
+ }
96
+ logger?.warn?.({ err: error, filePath }, "Failed to read worktree map")
97
+ return DEFAULT_MAP
98
+ }
99
+ }
100
+
101
+ export async function writeWorktreeMap(workspaceFolder: string, next: WorktreeMap, logger?: LogLike): Promise<void> {
102
+ const { repoRoot, isGitRepo } = await resolveRepoRoot(workspaceFolder, logger)
103
+ const filePath = getMapPath(repoRoot)
104
+ await fsp.mkdir(path.dirname(filePath), { recursive: true })
105
+
106
+ // Ensure ignore rules are present (local-only).
107
+ if (isGitRepo) {
108
+ await ensureGitExclude(repoRoot, logger).catch(() => undefined)
109
+ }
110
+
111
+ const payload: WorktreeMap = {
112
+ version: 1,
113
+ defaultWorktreeSlug: next.defaultWorktreeSlug || "root",
114
+ parentSessionWorktreeSlug: next.parentSessionWorktreeSlug ?? {},
115
+ }
116
+
117
+ // Write atomically.
118
+ const tmpPath = `${filePath}.${process.pid}.tmp`
119
+ await fsp.writeFile(tmpPath, JSON.stringify(payload, null, 2), "utf-8")
120
+ await fsp.rename(tmpPath, filePath)
121
+ }
122
+
123
+ export function worktreeMapExists(repoRoot: string): boolean {
124
+ try {
125
+ return fs.existsSync(getMapPath(repoRoot))
126
+ } catch {
127
+ return false
128
+ }
129
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "resolveJsonModule": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "outDir": "dist",
12
+ "rootDir": "src",
13
+ "types": ["node"]
14
+ },
15
+ "include": ["src/**/*.ts"],
16
+ "exclude": ["dist", "node_modules"]
17
+ }