@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,241 @@
1
+ import path from "path"
2
+ import { spawn } from "child_process"
3
+ import type { WorktreeDescriptor } from "../api-types"
4
+ import { promises as fsp } from "fs"
5
+
6
+ export interface LogLike {
7
+ debug?: (obj: any, msg?: string) => void
8
+ warn?: (obj: any, msg?: string) => void
9
+ }
10
+
11
+ type GitResult = { ok: true; stdout: string } | { ok: false; error: Error; stdout?: string; stderr?: string }
12
+
13
+ function runGit(args: string[], cwd: string): Promise<GitResult> {
14
+ return new Promise((resolve) => {
15
+ const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] })
16
+ let stdout = ""
17
+ let stderr = ""
18
+
19
+ child.stdout?.on("data", (chunk) => {
20
+ stdout += chunk.toString()
21
+ })
22
+ child.stderr?.on("data", (chunk) => {
23
+ stderr += chunk.toString()
24
+ })
25
+ child.once("error", (error) => {
26
+ resolve({ ok: false, error, stdout, stderr })
27
+ })
28
+ child.once("close", (code) => {
29
+ if (code === 0) {
30
+ resolve({ ok: true, stdout })
31
+ } else {
32
+ const error = new Error(stderr.trim() || `git ${args.join(" ")} failed with code ${code}`)
33
+ resolve({ ok: false, error, stdout, stderr })
34
+ }
35
+ })
36
+ })
37
+ }
38
+
39
+ export async function resolveRepoRoot(folder: string, logger?: LogLike): Promise<{ repoRoot: string; isGitRepo: boolean }> {
40
+ const result = await runGit(["rev-parse", "--show-toplevel"], folder)
41
+ if (!result.ok) {
42
+ logger?.debug?.({ folder, err: result.error }, "Folder is not a Git repository; using workspace folder as root")
43
+ return { repoRoot: folder, isGitRepo: false }
44
+ }
45
+ const repoRoot = result.stdout.trim()
46
+ if (!repoRoot) {
47
+ return { repoRoot: folder, isGitRepo: false }
48
+ }
49
+ return { repoRoot, isGitRepo: true }
50
+ }
51
+
52
+ function parseWorktreePorcelain(output: string): Array<{ worktree: string; branch?: string; head?: string; detached?: boolean }> {
53
+ const records: Array<{ worktree: string; branch?: string; head?: string; detached?: boolean }> = []
54
+ const lines = output.split(/\r?\n/)
55
+ let current: { worktree?: string; branch?: string; head?: string; detached?: boolean } = {}
56
+
57
+ const flush = () => {
58
+ if (current.worktree) {
59
+ records.push({ worktree: current.worktree, branch: current.branch })
60
+ }
61
+ current = {}
62
+ }
63
+
64
+ for (const line of lines) {
65
+ const trimmed = line.trim()
66
+ if (!trimmed) {
67
+ flush()
68
+ continue
69
+ }
70
+ const [key, ...rest] = trimmed.split(" ")
71
+ const value = rest.join(" ").trim()
72
+ if (key === "worktree") {
73
+ current.worktree = value
74
+ } else if (key === "branch") {
75
+ // branch is like refs/heads/foo
76
+ current.branch = value.replace(/^refs\/heads\//, "")
77
+ } else if (key === "HEAD") {
78
+ current.head = value
79
+ } else if (key === "detached") {
80
+ current.detached = true
81
+ }
82
+ }
83
+ flush()
84
+ return records
85
+ }
86
+
87
+ export async function listWorktrees(params: {
88
+ repoRoot: string
89
+ workspaceFolder: string
90
+ logger?: LogLike
91
+ }): Promise<WorktreeDescriptor[]> {
92
+ const { repoRoot, workspaceFolder, logger } = params
93
+ const rootDescriptor: WorktreeDescriptor = { slug: "root", directory: repoRoot, kind: "root" }
94
+
95
+ const result = await runGit(["worktree", "list", "--porcelain"], workspaceFolder)
96
+ if (!result.ok) {
97
+ logger?.debug?.({ repoRoot, err: result.error }, "Failed to list git worktrees; returning root only")
98
+ return [rootDescriptor]
99
+ }
100
+
101
+ const records = parseWorktreePorcelain(result.stdout)
102
+
103
+ const worktrees: WorktreeDescriptor[] = [rootDescriptor]
104
+ const seen = new Set<string>(["root"])
105
+
106
+ const normalizeSlug = (record: { branch?: string; head?: string; detached?: boolean; worktree: string }): string => {
107
+ const branch = (record.branch ?? "").trim()
108
+ if (branch) {
109
+ return branch
110
+ }
111
+ const head = (record.head ?? "").trim()
112
+ if (head && /^[0-9a-f]{7,40}$/i.test(head)) {
113
+ return `detached-${head.slice(0, 7)}`
114
+ }
115
+ // Fallback: stable-ish identifier derived from directory basename.
116
+ const base = path.basename(record.worktree || "")
117
+ return base ? `worktree-${base}` : "worktree"
118
+ }
119
+
120
+
121
+ for (const record of records) {
122
+ const abs = record.worktree
123
+ if (!abs || typeof abs !== "string") continue
124
+
125
+ // Skip the root record (we always expose it as slug="root").
126
+ if (path.resolve(abs) === path.resolve(repoRoot)) {
127
+ continue
128
+ }
129
+
130
+ const slug = normalizeSlug(record)
131
+ if (!slug || slug === "root") {
132
+ continue
133
+ }
134
+ if (seen.has(slug)) {
135
+ continue
136
+ }
137
+ seen.add(slug)
138
+ worktrees.push({ slug, directory: abs, kind: "worktree", branch: record.branch })
139
+ }
140
+
141
+ return worktrees
142
+ }
143
+
144
+ export function isValidWorktreeSlug(slug: string): boolean {
145
+ if (!slug) return false
146
+ const trimmed = slug.trim()
147
+ if (!trimmed) return false
148
+ if (trimmed.length > 200) return false
149
+ // Disallow control characters; allow branch-like slugs including '/'.
150
+ if (/[\x00-\x1F\x7F]/.test(trimmed)) return false
151
+ return true
152
+ }
153
+
154
+ export async function createManagedWorktree(params: {
155
+ repoRoot: string
156
+ workspaceFolder: string
157
+ slug: string
158
+ logger?: LogLike
159
+ }): Promise<{ slug: string; directory: string; branch?: string }> {
160
+ const { repoRoot, workspaceFolder, logger } = params
161
+ const branch = params.slug.trim()
162
+
163
+ if (!branch || branch === "root" || !isValidWorktreeSlug(branch)) {
164
+ throw new Error("Invalid worktree slug")
165
+ }
166
+
167
+ const sanitizeDirName = (input: string): string => {
168
+ const normalized = input
169
+ .trim()
170
+ .replace(/[\\/]+/g, "-")
171
+ .replace(/\s+/g, "-")
172
+ .replace(/[^a-zA-Z0-9_.-]+/g, "-")
173
+ .replace(/-{2,}/g, "-")
174
+ .replace(/^-+|-+$/g, "")
175
+ return normalized || "worktree"
176
+ }
177
+
178
+ const worktreesDir = path.join(repoRoot, ".codenomad", "worktrees")
179
+ const targetDir = path.join(worktreesDir, sanitizeDirName(branch))
180
+ await fsp.mkdir(worktreesDir, { recursive: true })
181
+
182
+ try {
183
+ const stat = await fsp.stat(targetDir)
184
+ if (stat.isDirectory()) {
185
+ throw new Error("Worktree directory already exists")
186
+ }
187
+ } catch (error) {
188
+ const code = (error as NodeJS.ErrnoException).code
189
+ if (code !== "ENOENT") {
190
+ throw error
191
+ }
192
+ }
193
+
194
+ logger?.debug?.({ slug: branch, branch, targetDir }, "Creating managed git worktree")
195
+
196
+ // Prefer creating a new branch from HEAD.
197
+ const first = await runGit(["worktree", "add", "-b", branch, targetDir, "HEAD"], workspaceFolder)
198
+ if (first.ok) {
199
+ return { slug: branch, directory: targetDir, branch }
200
+ }
201
+
202
+ const message = first.stderr?.toLowerCase() ?? first.error.message.toLowerCase()
203
+ if (message.includes("already exists")) {
204
+ // If the branch already exists, add worktree for that branch.
205
+ const second = await runGit(["worktree", "add", targetDir, branch], workspaceFolder)
206
+ if (second.ok) {
207
+ return { slug: branch, directory: targetDir, branch }
208
+ }
209
+ throw second.error
210
+ }
211
+
212
+ throw first.error
213
+ }
214
+
215
+ export async function removeWorktree(params: {
216
+ workspaceFolder: string
217
+ directory: string
218
+ force?: boolean
219
+ logger?: LogLike
220
+ }): Promise<void> {
221
+ const { workspaceFolder, logger } = params
222
+ const directory = (params.directory ?? "").trim()
223
+ if (!directory) {
224
+ throw new Error("Invalid worktree directory")
225
+ }
226
+ logger?.debug?.({ directory, force: Boolean(params.force) }, "Removing git worktree")
227
+
228
+ const args = ["worktree", "remove"]
229
+ if (params.force) {
230
+ args.push("--force")
231
+ }
232
+ args.push(directory)
233
+
234
+ const result = await runGit(args, workspaceFolder)
235
+ if (!result.ok) {
236
+ throw result.error
237
+ }
238
+
239
+ // Best-effort cleanup of stale metadata.
240
+ await runGit(["worktree", "prune"], workspaceFolder).catch(() => undefined)
241
+ }
@@ -0,0 +1,226 @@
1
+ import { Agent, fetch } from "undici"
2
+ import { Agent as UndiciAgent } from "undici"
3
+ import { EventBus } from "../events/bus"
4
+ import { Logger } from "../logger"
5
+ import { WorkspaceManager } from "./manager"
6
+ import { InstanceStreamEvent, InstanceStreamStatus } from "../api-types"
7
+
8
+ const INSTANCE_HOST = "127.0.0.1"
9
+ const STREAM_AGENT = new UndiciAgent({ bodyTimeout: 0, headersTimeout: 0 })
10
+ const RECONNECT_DELAY_MS = 1000
11
+
12
+ interface InstanceEventBridgeOptions {
13
+ workspaceManager: WorkspaceManager
14
+ eventBus: EventBus
15
+ logger: Logger
16
+ }
17
+
18
+ interface ActiveStream {
19
+ controller: AbortController
20
+ task: Promise<void>
21
+ }
22
+
23
+ export class InstanceEventBridge {
24
+ private readonly streams = new Map<string, ActiveStream>()
25
+
26
+ constructor(private readonly options: InstanceEventBridgeOptions) {
27
+ const bus = this.options.eventBus
28
+ bus.on("workspace.started", (event) => this.startStream(event.workspace.id))
29
+ bus.on("workspace.stopped", (event) => this.stopStream(event.workspaceId, "workspace stopped"))
30
+ bus.on("workspace.error", (event) => this.stopStream(event.workspace.id, "workspace error"))
31
+ }
32
+
33
+ shutdown() {
34
+ for (const [id, active] of this.streams) {
35
+ active.controller.abort()
36
+ this.publishStatus(id, "disconnected")
37
+ }
38
+ this.streams.clear()
39
+ }
40
+
41
+ private startStream(workspaceId: string) {
42
+ if (this.streams.has(workspaceId)) {
43
+ return
44
+ }
45
+
46
+ const controller = new AbortController()
47
+ const task = this.runStream(workspaceId, controller.signal)
48
+ .catch((error) => {
49
+ if (!controller.signal.aborted) {
50
+ this.options.logger.warn({ workspaceId, err: error }, "Instance event stream failed")
51
+ this.publishStatus(workspaceId, "error", error instanceof Error ? error.message : String(error))
52
+ }
53
+ })
54
+ .finally(() => {
55
+ const active = this.streams.get(workspaceId)
56
+ if (active?.controller === controller) {
57
+ this.streams.delete(workspaceId)
58
+ }
59
+ })
60
+
61
+ this.streams.set(workspaceId, { controller, task })
62
+ }
63
+
64
+ private stopStream(workspaceId: string, reason?: string) {
65
+ const active = this.streams.get(workspaceId)
66
+ if (!active) {
67
+ return
68
+ }
69
+ active.controller.abort()
70
+ this.streams.delete(workspaceId)
71
+ this.publishStatus(workspaceId, "disconnected", reason)
72
+ }
73
+
74
+ private async runStream(workspaceId: string, signal: AbortSignal) {
75
+ while (!signal.aborted) {
76
+ const port = this.options.workspaceManager.getInstancePort(workspaceId)
77
+ if (!port) {
78
+ await this.delay(RECONNECT_DELAY_MS, signal)
79
+ continue
80
+ }
81
+
82
+ this.publishStatus(workspaceId, "connecting")
83
+
84
+ try {
85
+ await this.consumeStream(workspaceId, port, signal)
86
+ } catch (error) {
87
+ if (signal.aborted) {
88
+ break
89
+ }
90
+ this.options.logger.warn({ workspaceId, err: error }, "Instance event stream disconnected")
91
+ this.publishStatus(workspaceId, "error", error instanceof Error ? error.message : String(error))
92
+ await this.delay(RECONNECT_DELAY_MS, signal)
93
+ }
94
+ }
95
+ }
96
+
97
+ private async consumeStream(workspaceId: string, port: number, signal: AbortSignal) {
98
+ const url = `http://${INSTANCE_HOST}:${port}/global/event`
99
+
100
+ const headers: Record<string, string> = { Accept: "text/event-stream" }
101
+ const authHeader = this.options.workspaceManager.getInstanceAuthorizationHeader(workspaceId)
102
+ if (authHeader) {
103
+ headers["Authorization"] = authHeader
104
+ }
105
+
106
+ const response = await fetch(url, {
107
+ headers,
108
+ signal,
109
+ dispatcher: STREAM_AGENT,
110
+ })
111
+
112
+ if (!response.ok || !response.body) {
113
+ throw new Error(`Instance event stream unavailable (${response.status})`)
114
+ }
115
+
116
+ this.publishStatus(workspaceId, "connected")
117
+
118
+ const reader = response.body.getReader()
119
+ const decoder = new TextDecoder()
120
+ let buffer = ""
121
+
122
+ while (!signal.aborted) {
123
+ const { done, value } = await reader.read()
124
+ if (done || !value) {
125
+ break
126
+ }
127
+ buffer += decoder.decode(value, { stream: true })
128
+ buffer = this.flushEvents(buffer, workspaceId)
129
+ }
130
+ }
131
+
132
+ private flushEvents(buffer: string, workspaceId: string) {
133
+ let separatorIndex = buffer.indexOf("\n\n")
134
+
135
+ while (separatorIndex >= 0) {
136
+ const chunk = buffer.slice(0, separatorIndex)
137
+ buffer = buffer.slice(separatorIndex + 2)
138
+ this.processChunk(chunk, workspaceId)
139
+ separatorIndex = buffer.indexOf("\n\n")
140
+ }
141
+
142
+ return buffer
143
+ }
144
+
145
+ private processChunk(chunk: string, workspaceId: string) {
146
+ const lines = chunk.split(/\r?\n/)
147
+ const dataLines: string[] = []
148
+
149
+ for (const line of lines) {
150
+ if (line.startsWith(":")) {
151
+ continue
152
+ }
153
+ if (line.startsWith("data:")) {
154
+ dataLines.push(line.slice(5).trimStart())
155
+ }
156
+ }
157
+
158
+ if (dataLines.length === 0) {
159
+ return
160
+ }
161
+
162
+ const payload = dataLines.join("\n").trim()
163
+ if (!payload) {
164
+ return
165
+ }
166
+
167
+ try {
168
+ const parsed = JSON.parse(payload) as any
169
+ if (!parsed || typeof parsed !== "object") {
170
+ this.options.logger.warn({ workspaceId, chunk: payload }, "Dropped malformed instance event")
171
+ return
172
+ }
173
+
174
+ // OpenCode SSE payload shapes vary across versions.
175
+ // Common variants:
176
+ // - { type, properties, ... }
177
+ // - { payload: { type, properties, ... }, directory: "/abs/path" }
178
+ // - { payload: { type, properties, ... } }
179
+ const base = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : parsed
180
+
181
+ const event: InstanceStreamEvent | null = base && typeof base === "object" ? ({ ...base } as any) : null
182
+
183
+ // Attach directory when available (don't overwrite if already present).
184
+ if (event && !(event as any).directory && typeof (parsed as any).directory === "string") {
185
+ ;(event as any).directory = (parsed as any).directory
186
+ }
187
+
188
+ if (!event || typeof (event as any).type !== "string") {
189
+ this.options.logger.warn({ workspaceId, chunk: payload }, "Dropped malformed instance event")
190
+ return
191
+ }
192
+
193
+ this.options.logger.debug({ workspaceId, eventType: (event as any).type }, "Instance SSE event received")
194
+ if (this.options.logger.isLevelEnabled("trace")) {
195
+ this.options.logger.trace({ workspaceId, event }, "Instance SSE event payload")
196
+ }
197
+ this.options.eventBus.publish({ type: "instance.event", instanceId: workspaceId, event })
198
+ } catch (error) {
199
+ this.options.logger.warn({ workspaceId, chunk: payload, err: error }, "Failed to parse instance SSE payload")
200
+ }
201
+ }
202
+
203
+ private publishStatus(instanceId: string, status: InstanceStreamStatus, reason?: string) {
204
+ this.options.logger.debug({ instanceId, status, reason }, "Instance SSE status updated")
205
+ this.options.eventBus.publish({ type: "instance.eventStatus", instanceId, status, reason })
206
+ }
207
+
208
+ private delay(duration: number, signal: AbortSignal) {
209
+ if (duration <= 0) {
210
+ return Promise.resolve()
211
+ }
212
+ return new Promise<void>((resolve) => {
213
+ const timeout = setTimeout(() => {
214
+ signal.removeEventListener("abort", onAbort)
215
+ resolve()
216
+ }, duration)
217
+
218
+ const onAbort = () => {
219
+ clearTimeout(timeout)
220
+ resolve()
221
+ }
222
+
223
+ signal.addEventListener("abort", onAbort, { once: true })
224
+ })
225
+ }
226
+ }