@cuongtran001/kanna 0.39.2

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 (473) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +355 -0
  3. package/bin/kanna +9 -0
  4. package/dist/client/apple-touch-icon.png +0 -0
  5. package/dist/client/assets/abap-BdImnpbu.js +1 -0
  6. package/dist/client/assets/actionscript-3-CoDkCxhg.js +1 -0
  7. package/dist/client/assets/ada-bCR0ucgS.js +1 -0
  8. package/dist/client/assets/andromeeda-C4gqWexZ.js +1 -0
  9. package/dist/client/assets/angular-html-CU67Zn6k.js +1 -0
  10. package/dist/client/assets/angular-ts-BwZT4LLn.js +1 -0
  11. package/dist/client/assets/apache-Pmp26Uib.js +1 -0
  12. package/dist/client/assets/apex-D8_7TLub.js +1 -0
  13. package/dist/client/assets/apl-dKokRX4l.js +1 -0
  14. package/dist/client/assets/applescript-Co6uUVPk.js +1 -0
  15. package/dist/client/assets/ara-BRHolxvo.js +1 -0
  16. package/dist/client/assets/asciidoc-Ve4PFQV2.js +1 -0
  17. package/dist/client/assets/asm-D_Q5rh1f.js +1 -0
  18. package/dist/client/assets/astro-CbQHKStN.js +1 -0
  19. package/dist/client/assets/aurora-x-D-2ljcwZ.js +1 -0
  20. package/dist/client/assets/awk-DMzUqQB5.js +1 -0
  21. package/dist/client/assets/ayu-dark-DYE7WIF3.js +1 -0
  22. package/dist/client/assets/ayu-light-BA47KaF1.js +1 -0
  23. package/dist/client/assets/ayu-mirage-32ctXXKs.js +1 -0
  24. package/dist/client/assets/ballerina-BFfxhgS-.js +1 -0
  25. package/dist/client/assets/bat-BkioyH1T.js +1 -0
  26. package/dist/client/assets/beancount-k_qm7-4y.js +1 -0
  27. package/dist/client/assets/berry-uYugtg8r.js +1 -0
  28. package/dist/client/assets/bibtex-CHM0blh-.js +1 -0
  29. package/dist/client/assets/bicep-Bmn6On1c.js +1 -0
  30. package/dist/client/assets/bird2-DPOp833l.js +1 -0
  31. package/dist/client/assets/blade-D4QpJJKB.js +1 -0
  32. package/dist/client/assets/bricolage-grotesque-latin-ext-wght-normal-CcLUaPy7.woff2 +0 -0
  33. package/dist/client/assets/bricolage-grotesque-latin-wght-normal-DLoelf7F.woff2 +0 -0
  34. package/dist/client/assets/bricolage-grotesque-vietnamese-wght-normal-BUzh504Q.woff2 +0 -0
  35. package/dist/client/assets/bsl-BO_Y6i37.js +1 -0
  36. package/dist/client/assets/c-BIGW1oBm.js +1 -0
  37. package/dist/client/assets/c3-eo99z4R2.js +1 -0
  38. package/dist/client/assets/cadence-Bv_4Rxtq.js +1 -0
  39. package/dist/client/assets/cairo-KRGpt6FW.js +1 -0
  40. package/dist/client/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  41. package/dist/client/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  42. package/dist/client/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  43. package/dist/client/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  44. package/dist/client/assets/clarity-D53aC0YG.js +1 -0
  45. package/dist/client/assets/clojure-P80f7IUj.js +1 -0
  46. package/dist/client/assets/cmake-D1j8_8rp.js +1 -0
  47. package/dist/client/assets/cobol-nwyudZeR.js +1 -0
  48. package/dist/client/assets/codeowners-Bp6g37R7.js +1 -0
  49. package/dist/client/assets/codeql-DsOJ9woJ.js +1 -0
  50. package/dist/client/assets/coffee-Ch7k5sss.js +1 -0
  51. package/dist/client/assets/common-lisp-Cg-RD9OK.js +1 -0
  52. package/dist/client/assets/coq-DkFqJrB1.js +1 -0
  53. package/dist/client/assets/cpp-CofmeUqb.js +1 -0
  54. package/dist/client/assets/crystal-tKQVLTB8.js +1 -0
  55. package/dist/client/assets/csharp-COcwbKMJ.js +1 -0
  56. package/dist/client/assets/css-DPfMkruS.js +1 -0
  57. package/dist/client/assets/csv-fuZLfV_i.js +1 -0
  58. package/dist/client/assets/cue-D82EKSYY.js +1 -0
  59. package/dist/client/assets/cypher-COkxafJQ.js +1 -0
  60. package/dist/client/assets/d-85-TOEBH.js +1 -0
  61. package/dist/client/assets/dark-plus-C3mMm8J8.js +1 -0
  62. package/dist/client/assets/dart-CF10PKvl.js +1 -0
  63. package/dist/client/assets/dax-CEL-wOlO.js +1 -0
  64. package/dist/client/assets/desktop-BmXAJ9_W.js +1 -0
  65. package/dist/client/assets/diff-D97Zzqfu.js +1 -0
  66. package/dist/client/assets/docker-BcOcwvcX.js +1 -0
  67. package/dist/client/assets/dotenv-Da5cRb03.js +1 -0
  68. package/dist/client/assets/dracula-BzJJZx-M.js +1 -0
  69. package/dist/client/assets/dracula-soft-BXkSAIEj.js +1 -0
  70. package/dist/client/assets/dream-maker-BtqSS_iP.js +1 -0
  71. package/dist/client/assets/edge-BkV0erSs.js +1 -0
  72. package/dist/client/assets/elixir-CDX3lj18.js +1 -0
  73. package/dist/client/assets/elm-DbKCFpqz.js +1 -0
  74. package/dist/client/assets/emacs-lisp-C9XAeP06.js +1 -0
  75. package/dist/client/assets/erb-B12qg9BL.js +1 -0
  76. package/dist/client/assets/erlang-DsQrWhSR.js +1 -0
  77. package/dist/client/assets/everforest-dark-BgDCqdQA.js +1 -0
  78. package/dist/client/assets/everforest-light-C8M2exoo.js +1 -0
  79. package/dist/client/assets/fennel-BYunw83y.js +1 -0
  80. package/dist/client/assets/fish-BvzEVeQv.js +1 -0
  81. package/dist/client/assets/fluent-C4IJs8-o.js +1 -0
  82. package/dist/client/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  83. package/dist/client/assets/fortran-free-form-BxgE0vQu.js +1 -0
  84. package/dist/client/assets/fsharp-CXgrBDvD.js +1 -0
  85. package/dist/client/assets/gdresource-BOOCDP_w.js +1 -0
  86. package/dist/client/assets/gdscript-C5YyOfLZ.js +1 -0
  87. package/dist/client/assets/gdshader-DkwncUOv.js +1 -0
  88. package/dist/client/assets/genie-D0YGMca9.js +1 -0
  89. package/dist/client/assets/gherkin-DyxjwDmM.js +1 -0
  90. package/dist/client/assets/git-commit-F4YmCXRG.js +1 -0
  91. package/dist/client/assets/git-rebase-r7XF79zn.js +1 -0
  92. package/dist/client/assets/github-dark-DHJKELXO.js +1 -0
  93. package/dist/client/assets/github-dark-default-Cuk6v7N8.js +1 -0
  94. package/dist/client/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  95. package/dist/client/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  96. package/dist/client/assets/github-light-DAi9KRSo.js +1 -0
  97. package/dist/client/assets/github-light-default-D7oLnXFd.js +1 -0
  98. package/dist/client/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  99. package/dist/client/assets/gleam-BspZqrRM.js +1 -0
  100. package/dist/client/assets/glimmer-js-Rg0-pVw9.js +1 -0
  101. package/dist/client/assets/glimmer-ts-U6CK756n.js +1 -0
  102. package/dist/client/assets/glsl-DplSGwfg.js +1 -0
  103. package/dist/client/assets/gn-n2N0HUVH.js +1 -0
  104. package/dist/client/assets/gnuplot-DdkO51Og.js +1 -0
  105. package/dist/client/assets/go-CxLEBnE3.js +1 -0
  106. package/dist/client/assets/graphql-ChdNCCLP.js +1 -0
  107. package/dist/client/assets/groovy-gcz8RCvz.js +1 -0
  108. package/dist/client/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  109. package/dist/client/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  110. package/dist/client/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  111. package/dist/client/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  112. package/dist/client/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  113. package/dist/client/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  114. package/dist/client/assets/hack-CaT9iCJl.js +1 -0
  115. package/dist/client/assets/haml-B8DHNrY2.js +1 -0
  116. package/dist/client/assets/handlebars-BL8al0AC.js +1 -0
  117. package/dist/client/assets/haskell-Df6bDoY_.js +1 -0
  118. package/dist/client/assets/haxe-CzTSHFRz.js +1 -0
  119. package/dist/client/assets/hcl-BWvSN4gD.js +1 -0
  120. package/dist/client/assets/hjson-D5-asLiD.js +1 -0
  121. package/dist/client/assets/hlsl-D3lLCCz7.js +1 -0
  122. package/dist/client/assets/horizon-BUw7H-hv.js +1 -0
  123. package/dist/client/assets/horizon-bright-Cn-bp-IR.js +1 -0
  124. package/dist/client/assets/houston-DnULxvSX.js +1 -0
  125. package/dist/client/assets/html-GMplVEZG.js +1 -0
  126. package/dist/client/assets/html-derivative-BFtXZ54Q.js +1 -0
  127. package/dist/client/assets/http-jrhK8wxY.js +1 -0
  128. package/dist/client/assets/hurl-irOxFIW8.js +1 -0
  129. package/dist/client/assets/hxml-Bvhsp5Yf.js +1 -0
  130. package/dist/client/assets/hy-DFXneXwc.js +1 -0
  131. package/dist/client/assets/imba-DGztddWO.js +1 -0
  132. package/dist/client/assets/index-Do7324M0.css +32 -0
  133. package/dist/client/assets/index-ktE9DLCD.js +2620 -0
  134. package/dist/client/assets/ini-BEwlwnbL.js +1 -0
  135. package/dist/client/assets/java-CylS5w8V.js +1 -0
  136. package/dist/client/assets/javascript-wDzz0qaB.js +1 -0
  137. package/dist/client/assets/jinja-4LBKfQ-Z.js +1 -0
  138. package/dist/client/assets/jison-wvAkD_A8.js +1 -0
  139. package/dist/client/assets/json-Cp-IABpG.js +1 -0
  140. package/dist/client/assets/json5-C9tS-k6U.js +1 -0
  141. package/dist/client/assets/jsonc-Des-eS-w.js +1 -0
  142. package/dist/client/assets/jsonl-DcaNXYhu.js +1 -0
  143. package/dist/client/assets/jsonnet-DFQXde-d.js +1 -0
  144. package/dist/client/assets/jssm-C2t-YnRu.js +1 -0
  145. package/dist/client/assets/jsx-g9-lgVsj.js +1 -0
  146. package/dist/client/assets/julia-CxzCAyBv.js +1 -0
  147. package/dist/client/assets/just-Cw27pwNe.js +1 -0
  148. package/dist/client/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  149. package/dist/client/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  150. package/dist/client/assets/kanagawa-wave-DWedfzmr.js +1 -0
  151. package/dist/client/assets/kdl-DV7GczEv.js +1 -0
  152. package/dist/client/assets/kotlin-BdnUsdx6.js +1 -0
  153. package/dist/client/assets/kusto-DZf3V79B.js +1 -0
  154. package/dist/client/assets/laserwave-DUszq2jm.js +1 -0
  155. package/dist/client/assets/latex-CWtU0Tv5.js +1 -0
  156. package/dist/client/assets/lean-BZvkOJ9d.js +1 -0
  157. package/dist/client/assets/less-B1dDrJ26.js +1 -0
  158. package/dist/client/assets/light-plus-B7mTdjB0.js +1 -0
  159. package/dist/client/assets/liquid-DYVedYrR.js +1 -0
  160. package/dist/client/assets/llvm-DjAJT7YJ.js +1 -0
  161. package/dist/client/assets/log-2UxHyX5q.js +1 -0
  162. package/dist/client/assets/logo-BtOb2qkB.js +1 -0
  163. package/dist/client/assets/lua-BaeVxFsk.js +1 -0
  164. package/dist/client/assets/luau-C-HG3fhB.js +1 -0
  165. package/dist/client/assets/make-CHLpvVh8.js +1 -0
  166. package/dist/client/assets/markdown-Cvjx9yec.js +1 -0
  167. package/dist/client/assets/marko-CnJfTvn9.js +1 -0
  168. package/dist/client/assets/material-theme-D5KoaKCx.js +1 -0
  169. package/dist/client/assets/material-theme-darker-BfHTSMKl.js +1 -0
  170. package/dist/client/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  171. package/dist/client/assets/material-theme-ocean-CyktbL80.js +1 -0
  172. package/dist/client/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  173. package/dist/client/assets/matlab-D7o27uSR.js +1 -0
  174. package/dist/client/assets/mdc-BMNejdWA.js +1 -0
  175. package/dist/client/assets/mdx-Cmh6b_Ma.js +1 -0
  176. package/dist/client/assets/mermaid-mWjccvbQ.js +1 -0
  177. package/dist/client/assets/min-dark-CafNBF8u.js +1 -0
  178. package/dist/client/assets/min-light-CTRr51gU.js +1 -0
  179. package/dist/client/assets/mipsasm-CKIfxQSi.js +1 -0
  180. package/dist/client/assets/mojo-rZm6bMo-.js +1 -0
  181. package/dist/client/assets/monokai-D4h5O-jR.js +1 -0
  182. package/dist/client/assets/moonbit-_H4v1dQx.js +1 -0
  183. package/dist/client/assets/move-IF9eRakj.js +1 -0
  184. package/dist/client/assets/narrat-DRg8JJMk.js +1 -0
  185. package/dist/client/assets/nextflow-Zz6hmt5N.js +1 -0
  186. package/dist/client/assets/nextflow-groovy-BeH2EWoN.js +1 -0
  187. package/dist/client/assets/nginx-BpAMiNFr.js +1 -0
  188. package/dist/client/assets/night-owl-C39BiMTA.js +1 -0
  189. package/dist/client/assets/night-owl-light-CMTm3GFP.js +1 -0
  190. package/dist/client/assets/nim-CVrawwO9.js +1 -0
  191. package/dist/client/assets/nix-CwoSXNpI.js +1 -0
  192. package/dist/client/assets/nord-Ddv68eIx.js +1 -0
  193. package/dist/client/assets/nushell-Cz2AlsmD.js +1 -0
  194. package/dist/client/assets/objective-c-DXmwc3jG.js +1 -0
  195. package/dist/client/assets/objective-cpp-CLxacb5B.js +1 -0
  196. package/dist/client/assets/ocaml-C0hk2d4L.js +1 -0
  197. package/dist/client/assets/odin-BBf5iR-q.js +1 -0
  198. package/dist/client/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  199. package/dist/client/assets/one-light-C3Wv6jpd.js +1 -0
  200. package/dist/client/assets/openscad-C4EeE6gA.js +1 -0
  201. package/dist/client/assets/pascal-D93ZcfNL.js +1 -0
  202. package/dist/client/assets/perl-C0TMdlhV.js +1 -0
  203. package/dist/client/assets/php-Dhbhpdrm.js +1 -0
  204. package/dist/client/assets/pierre-dark-DF2SEV7i.js +1 -0
  205. package/dist/client/assets/pierre-light-DOlZxES8.js +1 -0
  206. package/dist/client/assets/pkl-u5AG7uiY.js +1 -0
  207. package/dist/client/assets/plastic-3e1v2bzS.js +1 -0
  208. package/dist/client/assets/plsql-ChMvpjG-.js +1 -0
  209. package/dist/client/assets/po-BTJTHyun.js +1 -0
  210. package/dist/client/assets/poimandres-CS3Unz2-.js +1 -0
  211. package/dist/client/assets/polar-C0HS_06l.js +1 -0
  212. package/dist/client/assets/postcss-CXtECtnM.js +1 -0
  213. package/dist/client/assets/powerquery-CEu0bR-o.js +1 -0
  214. package/dist/client/assets/powershell-Dpen1YoG.js +1 -0
  215. package/dist/client/assets/prisma-Dd19v3D-.js +1 -0
  216. package/dist/client/assets/prolog-CbFg5uaA.js +1 -0
  217. package/dist/client/assets/proto-C7zT0LnQ.js +1 -0
  218. package/dist/client/assets/pug-CGlum2m_.js +1 -0
  219. package/dist/client/assets/puppet-BMWR74SV.js +1 -0
  220. package/dist/client/assets/purescript-CklMAg4u.js +1 -0
  221. package/dist/client/assets/python-B6aJPvgy.js +1 -0
  222. package/dist/client/assets/qml-3beO22l8.js +1 -0
  223. package/dist/client/assets/qmldir-C8lEn-DE.js +1 -0
  224. package/dist/client/assets/qss-IeuSbFQv.js +1 -0
  225. package/dist/client/assets/r-Dspwwk_N.js +1 -0
  226. package/dist/client/assets/racket-BqYA7rlc.js +1 -0
  227. package/dist/client/assets/raku-DXvB9xmW.js +1 -0
  228. package/dist/client/assets/razor-Uh8Bk_45.js +1 -0
  229. package/dist/client/assets/red-bN70gL4F.js +1 -0
  230. package/dist/client/assets/reg-C-SQnVFl.js +1 -0
  231. package/dist/client/assets/regexp-CDVJQ6XC.js +1 -0
  232. package/dist/client/assets/rel-C3B-1QV4.js +1 -0
  233. package/dist/client/assets/riscv-BM1_JUlF.js +1 -0
  234. package/dist/client/assets/ron-D8l8udqQ.js +1 -0
  235. package/dist/client/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  236. package/dist/client/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  237. package/dist/client/assets/rose-pine-qdsjHGoJ.js +1 -0
  238. package/dist/client/assets/rosmsg-BJDFO7_C.js +1 -0
  239. package/dist/client/assets/rst-BrH8l1NY.js +1 -0
  240. package/dist/client/assets/ruby-Dw2BHqvy.js +1 -0
  241. package/dist/client/assets/rust-B1yitclQ.js +1 -0
  242. package/dist/client/assets/sas-cz2c8ADy.js +1 -0
  243. package/dist/client/assets/sass-Cj5Yp3dK.js +1 -0
  244. package/dist/client/assets/scala-C151Ov-r.js +1 -0
  245. package/dist/client/assets/scheme-C98Dy4si.js +1 -0
  246. package/dist/client/assets/scss-OYdSNvt2.js +1 -0
  247. package/dist/client/assets/sdbl-DVxCFoDh.js +1 -0
  248. package/dist/client/assets/shaderlab-Dg9Lc6iA.js +1 -0
  249. package/dist/client/assets/shellscript-Yzrsuije.js +1 -0
  250. package/dist/client/assets/shellsession-BADoaaVG.js +1 -0
  251. package/dist/client/assets/slack-dark-BthQWCQV.js +1 -0
  252. package/dist/client/assets/slack-ochin-DqwNpetd.js +1 -0
  253. package/dist/client/assets/smalltalk-BERRCDM3.js +1 -0
  254. package/dist/client/assets/snazzy-light-Bw305WKR.js +1 -0
  255. package/dist/client/assets/solarized-dark-DXbdFlpD.js +1 -0
  256. package/dist/client/assets/solarized-light-L9t79GZl.js +1 -0
  257. package/dist/client/assets/solidity-rGO070M0.js +1 -0
  258. package/dist/client/assets/soy-Brmx7dQM.js +1 -0
  259. package/dist/client/assets/sparql-rVzFXLq3.js +1 -0
  260. package/dist/client/assets/splunk-BtCnVYZw.js +1 -0
  261. package/dist/client/assets/sql-BLtJtn59.js +1 -0
  262. package/dist/client/assets/ssh-config-_ykCGR6B.js +1 -0
  263. package/dist/client/assets/stata-BH5u7GGu.js +1 -0
  264. package/dist/client/assets/stylus-BEDo0Tqx.js +1 -0
  265. package/dist/client/assets/surrealql-Bq5Q-fJD.js +1 -0
  266. package/dist/client/assets/svelte-C_ipcX3V.js +1 -0
  267. package/dist/client/assets/swift-D82vCrfD.js +1 -0
  268. package/dist/client/assets/synthwave-84-CbfX1IO0.js +1 -0
  269. package/dist/client/assets/system-verilog-CnnmHF94.js +1 -0
  270. package/dist/client/assets/systemd-4A_iFExJ.js +1 -0
  271. package/dist/client/assets/talonscript-CkByrt1z.js +1 -0
  272. package/dist/client/assets/tasl-QIJgUcNo.js +1 -0
  273. package/dist/client/assets/tcl-dwOrl1Do.js +1 -0
  274. package/dist/client/assets/templ-P3uqSqPl.js +1 -0
  275. package/dist/client/assets/terraform-BETggiCN.js +1 -0
  276. package/dist/client/assets/tex-idrVyKtj.js +1 -0
  277. package/dist/client/assets/tokyo-night-hegEt444.js +1 -0
  278. package/dist/client/assets/toml-vGWfd6FD.js +1 -0
  279. package/dist/client/assets/ts-tags-zn1MmPIZ.js +1 -0
  280. package/dist/client/assets/tsv-B_m7g4N7.js +1 -0
  281. package/dist/client/assets/tsx-COt5Ahok.js +1 -0
  282. package/dist/client/assets/turtle-BsS91CYL.js +1 -0
  283. package/dist/client/assets/twig-DNn4PbVi.js +1 -0
  284. package/dist/client/assets/typescript-BPQ3VLAy.js +1 -0
  285. package/dist/client/assets/typespec-BGHnOYBU.js +1 -0
  286. package/dist/client/assets/typst-DHCkPAjA.js +1 -0
  287. package/dist/client/assets/v-BcVCzyr7.js +1 -0
  288. package/dist/client/assets/vala-CsfeWuGM.js +1 -0
  289. package/dist/client/assets/vb-D17OF-Vu.js +1 -0
  290. package/dist/client/assets/verilog-BQ8w6xss.js +1 -0
  291. package/dist/client/assets/vesper-DU1UobuO.js +1 -0
  292. package/dist/client/assets/vhdl-CeAyd5Ju.js +1 -0
  293. package/dist/client/assets/viml-CJc9bBzg.js +1 -0
  294. package/dist/client/assets/vitesse-black-Bkuqu6BP.js +1 -0
  295. package/dist/client/assets/vitesse-dark-D0r3Knsf.js +1 -0
  296. package/dist/client/assets/vitesse-light-CVO1_9PV.js +1 -0
  297. package/dist/client/assets/vue-DN_0RTcg.js +1 -0
  298. package/dist/client/assets/vue-html-AaS7Mt5G.js +1 -0
  299. package/dist/client/assets/vue-vine-CQOfvN7w.js +1 -0
  300. package/dist/client/assets/vyper-CDx5xZoG.js +1 -0
  301. package/dist/client/assets/wasm-CG6Dc4jp.js +1 -0
  302. package/dist/client/assets/wasm-MzD3tlZU.js +1 -0
  303. package/dist/client/assets/wenyan-BV7otONQ.js +1 -0
  304. package/dist/client/assets/wgsl-Dx-B1_4e.js +1 -0
  305. package/dist/client/assets/wikitext-BhOHFoWU.js +1 -0
  306. package/dist/client/assets/wit-5i3qLPDT.js +1 -0
  307. package/dist/client/assets/wolfram-lXgVvXCa.js +1 -0
  308. package/dist/client/assets/xml-sdJ4AIDG.js +1 -0
  309. package/dist/client/assets/xsl-CtQFsRM5.js +1 -0
  310. package/dist/client/assets/yaml-Buea-lGh.js +1 -0
  311. package/dist/client/assets/zenscript-DVFEvuxE.js +1 -0
  312. package/dist/client/assets/zig-VOosw3JB.js +1 -0
  313. package/dist/client/chat-sounds/Blow.mp3 +0 -0
  314. package/dist/client/chat-sounds/Bottle.mp3 +0 -0
  315. package/dist/client/chat-sounds/Frog.mp3 +0 -0
  316. package/dist/client/chat-sounds/Funk.mp3 +0 -0
  317. package/dist/client/chat-sounds/Glass.mp3 +0 -0
  318. package/dist/client/chat-sounds/Ping.mp3 +0 -0
  319. package/dist/client/chat-sounds/Pop.mp3 +0 -0
  320. package/dist/client/chat-sounds/Purr.mp3 +0 -0
  321. package/dist/client/chat-sounds/Tink.mp3 +0 -0
  322. package/dist/client/editor-icons/cursor.png +0 -0
  323. package/dist/client/editor-icons/custom.png +0 -0
  324. package/dist/client/editor-icons/default-app.png +0 -0
  325. package/dist/client/editor-icons/finder.png +0 -0
  326. package/dist/client/editor-icons/preview.png +0 -0
  327. package/dist/client/editor-icons/terminal.png +0 -0
  328. package/dist/client/editor-icons/windsurf.png +0 -0
  329. package/dist/client/editor-icons/xcode.png +0 -0
  330. package/dist/client/favicon.png +0 -0
  331. package/dist/client/fonts/body-medium.woff2 +0 -0
  332. package/dist/client/fonts/body-regular-italic.woff2 +0 -0
  333. package/dist/client/fonts/body-regular.woff2 +0 -0
  334. package/dist/client/fonts/body-semibold.woff2 +0 -0
  335. package/dist/client/icon-192.png +0 -0
  336. package/dist/client/icon-512.png +0 -0
  337. package/dist/client/icon-maskable-512.png +0 -0
  338. package/dist/client/icon.svg +4 -0
  339. package/dist/client/index.html +34 -0
  340. package/dist/client/manifest.webmanifest +46 -0
  341. package/dist/client/screenshot-light.png +0 -0
  342. package/dist/client/screenshot.png +0 -0
  343. package/dist/export-viewer/assets/bricolage-grotesque-latin-ext-wght-normal-CcLUaPy7.woff2 +0 -0
  344. package/dist/export-viewer/assets/bricolage-grotesque-latin-wght-normal-DLoelf7F.woff2 +0 -0
  345. package/dist/export-viewer/assets/bricolage-grotesque-vietnamese-wght-normal-BUzh504Q.woff2 +0 -0
  346. package/dist/export-viewer/assets/index-D1qUumZR.js +410 -0
  347. package/dist/export-viewer/assets/index-gG2nMW51.css +1 -0
  348. package/dist/export-viewer/editor-icons/cursor.png +0 -0
  349. package/dist/export-viewer/editor-icons/custom.png +0 -0
  350. package/dist/export-viewer/editor-icons/default-app.png +0 -0
  351. package/dist/export-viewer/editor-icons/finder.png +0 -0
  352. package/dist/export-viewer/editor-icons/preview.png +0 -0
  353. package/dist/export-viewer/editor-icons/terminal.png +0 -0
  354. package/dist/export-viewer/editor-icons/windsurf.png +0 -0
  355. package/dist/export-viewer/editor-icons/xcode.png +0 -0
  356. package/dist/export-viewer/fonts/body-medium.woff2 +0 -0
  357. package/dist/export-viewer/fonts/body-regular-italic.woff2 +0 -0
  358. package/dist/export-viewer/fonts/body-regular.woff2 +0 -0
  359. package/dist/export-viewer/fonts/body-semibold.woff2 +0 -0
  360. package/dist/export-viewer/index.html +14 -0
  361. package/package.json +99 -0
  362. package/src/server/__fixtures__/claude-session-empty.jsonl +0 -0
  363. package/src/server/__fixtures__/claude-session-malformed.jsonl +3 -0
  364. package/src/server/__fixtures__/claude-session-valid.jsonl +6 -0
  365. package/src/server/agent.test.ts +2369 -0
  366. package/src/server/agent.ts +1927 -0
  367. package/src/server/analytics.test.ts +313 -0
  368. package/src/server/analytics.ts +131 -0
  369. package/src/server/app-settings.test.ts +233 -0
  370. package/src/server/app-settings.ts +548 -0
  371. package/src/server/auth.test.ts +329 -0
  372. package/src/server/auth.ts +204 -0
  373. package/src/server/auto-continue/e2e.test.ts +215 -0
  374. package/src/server/auto-continue/events.test.ts +30 -0
  375. package/src/server/auto-continue/events.ts +35 -0
  376. package/src/server/auto-continue/limit-detector.test.ts +153 -0
  377. package/src/server/auto-continue/limit-detector.ts +159 -0
  378. package/src/server/auto-continue/read-model.test.ts +109 -0
  379. package/src/server/auto-continue/read-model.ts +83 -0
  380. package/src/server/auto-continue/schedule-manager.test.ts +155 -0
  381. package/src/server/auto-continue/schedule-manager.ts +116 -0
  382. package/src/server/claude-session-importer.test.ts +214 -0
  383. package/src/server/claude-session-importer.ts +187 -0
  384. package/src/server/claude-session-mapper.test.ts +88 -0
  385. package/src/server/claude-session-mapper.ts +106 -0
  386. package/src/server/claude-session-parser.test.ts +38 -0
  387. package/src/server/claude-session-parser.ts +67 -0
  388. package/src/server/claude-session-scanner.test.ts +49 -0
  389. package/src/server/claude-session-scanner.ts +24 -0
  390. package/src/server/claude-session-types.ts +61 -0
  391. package/src/server/cli-runtime.test.ts +523 -0
  392. package/src/server/cli-runtime.ts +405 -0
  393. package/src/server/cli-supervisor.ts +102 -0
  394. package/src/server/cli.ts +64 -0
  395. package/src/server/cloudflare-tunnel/agent-integration.test.ts +76 -0
  396. package/src/server/cloudflare-tunnel/agent-integration.ts +55 -0
  397. package/src/server/cloudflare-tunnel/detector.test.ts +72 -0
  398. package/src/server/cloudflare-tunnel/detector.ts +44 -0
  399. package/src/server/cloudflare-tunnel/e2e.test.ts +194 -0
  400. package/src/server/cloudflare-tunnel/events.test.ts +43 -0
  401. package/src/server/cloudflare-tunnel/events.ts +31 -0
  402. package/src/server/cloudflare-tunnel/gateway.ts +143 -0
  403. package/src/server/cloudflare-tunnel/lifecycle.test.ts +48 -0
  404. package/src/server/cloudflare-tunnel/lifecycle.ts +62 -0
  405. package/src/server/cloudflare-tunnel/read-model.test.ts +69 -0
  406. package/src/server/cloudflare-tunnel/read-model.ts +80 -0
  407. package/src/server/cloudflare-tunnel/tunnel-manager.test.ts +116 -0
  408. package/src/server/cloudflare-tunnel/tunnel-manager.ts +165 -0
  409. package/src/server/codex-app-server-protocol.ts +487 -0
  410. package/src/server/codex-app-server.test.ts +1816 -0
  411. package/src/server/codex-app-server.ts +1475 -0
  412. package/src/server/diff-store.test.ts +737 -0
  413. package/src/server/diff-store.ts +2199 -0
  414. package/src/server/discovery.test.ts +211 -0
  415. package/src/server/discovery.ts +301 -0
  416. package/src/server/event-store.test.ts +797 -0
  417. package/src/server/event-store.ts +1421 -0
  418. package/src/server/events.ts +217 -0
  419. package/src/server/external-open.test.ts +112 -0
  420. package/src/server/external-open.ts +345 -0
  421. package/src/server/generate-commit-message.test.ts +79 -0
  422. package/src/server/generate-commit-message.ts +126 -0
  423. package/src/server/generate-title.ts +76 -0
  424. package/src/server/harness-types.ts +19 -0
  425. package/src/server/keybindings.test.ts +144 -0
  426. package/src/server/keybindings.ts +178 -0
  427. package/src/server/llm-provider.test.ts +134 -0
  428. package/src/server/llm-provider.ts +207 -0
  429. package/src/server/machine-name.ts +22 -0
  430. package/src/server/paths-route.test.ts +64 -0
  431. package/src/server/paths.ts +35 -0
  432. package/src/server/process-utils.test.ts +12 -0
  433. package/src/server/process-utils.ts +47 -0
  434. package/src/server/project-paths.test.ts +95 -0
  435. package/src/server/project-paths.ts +191 -0
  436. package/src/server/provider-catalog.test.ts +69 -0
  437. package/src/server/provider-catalog.ts +87 -0
  438. package/src/server/quick-response.test.ts +440 -0
  439. package/src/server/quick-response.ts +300 -0
  440. package/src/server/read-models.test.ts +509 -0
  441. package/src/server/read-models.ts +230 -0
  442. package/src/server/restart.test.ts +27 -0
  443. package/src/server/restart.ts +30 -0
  444. package/src/server/server.ts +616 -0
  445. package/src/server/share.test.ts +180 -0
  446. package/src/server/share.ts +150 -0
  447. package/src/server/standalone-export.test.ts +224 -0
  448. package/src/server/standalone-export.ts +419 -0
  449. package/src/server/terminal-manager.test.ts +315 -0
  450. package/src/server/terminal-manager.ts +350 -0
  451. package/src/server/test-helpers/async-event-queue.ts +52 -0
  452. package/src/server/test-helpers/wait-for.ts +14 -0
  453. package/src/server/title-generation.live.test.ts +44 -0
  454. package/src/server/update-manager.test.ts +158 -0
  455. package/src/server/update-manager.ts +222 -0
  456. package/src/server/update-strategy.test.ts +237 -0
  457. package/src/server/update-strategy.ts +241 -0
  458. package/src/server/uploads.test.ts +292 -0
  459. package/src/server/uploads.ts +131 -0
  460. package/src/server/ws-router.test.ts +2292 -0
  461. package/src/server/ws-router.ts +1465 -0
  462. package/src/shared/analytics.ts +30 -0
  463. package/src/shared/branding.test.ts +31 -0
  464. package/src/shared/branding.ts +77 -0
  465. package/src/shared/dev-ports.test.ts +113 -0
  466. package/src/shared/dev-ports.ts +134 -0
  467. package/src/shared/ports.ts +2 -0
  468. package/src/shared/protocol.ts +257 -0
  469. package/src/shared/share.ts +27 -0
  470. package/src/shared/tools.test.ts +164 -0
  471. package/src/shared/tools.ts +327 -0
  472. package/src/shared/types.test.ts +25 -0
  473. package/src/shared/types.ts +1088 -0
@@ -0,0 +1,405 @@
1
+ import process from "node:process"
2
+ import { spawnSync } from "node:child_process"
3
+ import { hasCommand, spawnDetached } from "./process-utils"
4
+ import { APP_NAME, CLI_COMMAND, getDataDirDisplay, LOG_PREFIX, PACKAGE_NAME } from "../shared/branding"
5
+ import type { ShareMode } from "../shared/share"
6
+ import { assertNoHostOverride, getShareCliFlag, isShareEnabled, isTokenShareMode } from "../shared/share"
7
+ import type { UpdateInstallErrorCode } from "../shared/types"
8
+ import { PROD_SERVER_PORT } from "../shared/ports"
9
+ import { CLI_SUPPRESS_OPEN_ONCE_ENV_VAR } from "./restart"
10
+ import { logShareDetails, renderTerminalQr, startShareTunnel, type StartedShareTunnel } from "./share"
11
+
12
+ export interface CliOptions {
13
+ port: number
14
+ host: string
15
+ openBrowser: boolean
16
+ share: ShareMode
17
+ password: string | null
18
+ strictPort: boolean
19
+ }
20
+
21
+ export interface CliUpdateOptions {
22
+ version: string
23
+ fetchLatestVersion: (packageName: string) => Promise<string>
24
+ installVersion: (packageName: string, version: string) => UpdateInstallAttemptResult
25
+ argv: string[]
26
+ command: string
27
+ }
28
+
29
+ export interface StartedCli {
30
+ kind: "started"
31
+ stop: () => Promise<void>
32
+ }
33
+
34
+ export interface RestartingCli {
35
+ kind: "restarting"
36
+ reason: "startup_update" | "ui_update"
37
+ }
38
+
39
+ export interface ExitedCli {
40
+ kind: "exited"
41
+ code: number
42
+ }
43
+
44
+ export type CliRunResult = StartedCli | RestartingCli | ExitedCli
45
+
46
+ export interface CliRuntimeDeps {
47
+ version: string
48
+ bunVersion: string
49
+ startServer: (options: CliOptions & {
50
+ update: CliUpdateOptions
51
+ onMigrationProgress?: (message: string) => void
52
+ trustProxy?: boolean
53
+ }) => Promise<{ port: number; stop: () => Promise<void> }>
54
+ fetchLatestVersion: (packageName: string) => Promise<string>
55
+ installVersion: (packageName: string, version: string) => UpdateInstallAttemptResult
56
+ openUrl: (url: string) => void
57
+ log: (message: string) => void
58
+ warn: (message: string) => void
59
+ renderShareQr?: (url: string) => Promise<string>
60
+ startShareTunnel?: (localUrl: string, shareMode: Exclude<ShareMode, false>) => Promise<StartedShareTunnel>
61
+ }
62
+
63
+ export interface UpdateInstallAttemptResult {
64
+ ok: boolean
65
+ errorCode: UpdateInstallErrorCode | null
66
+ userTitle: string | null
67
+ userMessage: string | null
68
+ }
69
+
70
+ type ParsedArgs =
71
+ | { kind: "run"; options: CliOptions }
72
+ | { kind: "help" }
73
+ | { kind: "version" }
74
+
75
+ const MINIMUM_BUN_VERSION = "1.3.5"
76
+
77
+ function throwShareConflict(share: Exclude<ShareMode, false>, hostFlag: "--host" | "--remote"): never {
78
+ throw new Error(`${getShareCliFlag(share)} cannot be used with ${hostFlag}`)
79
+ }
80
+
81
+ function printHelp() {
82
+ console.log(`${APP_NAME} — local-only project chat UI
83
+
84
+ Usage:
85
+ ${CLI_COMMAND} [options]
86
+
87
+ Options:
88
+ --port <number> Port to listen on (default: ${PROD_SERVER_PORT})
89
+ --host <host> Bind to a specific host or IP
90
+ --remote Shortcut for --host 0.0.0.0
91
+ --share Create a public Cloudflare quick tunnel with terminal QR
92
+ --cloudflared <token>
93
+ Run a named Cloudflare tunnel from a token
94
+ --password <secret> Require a password before loading the app
95
+ --strict-port Fail instead of trying another port
96
+ --no-open Don't open browser automatically
97
+ --version Print version and exit
98
+ --help Show this help message`)
99
+ }
100
+
101
+ export function parseArgs(argv: string[]): ParsedArgs {
102
+ let port = PROD_SERVER_PORT
103
+ let host = "127.0.0.1"
104
+ let openBrowser = true
105
+ let share: ShareMode = false
106
+ let password: string | null = null
107
+ let sawHost = false
108
+ let sawRemote = false
109
+ let strictPort = false
110
+
111
+ for (let index = 0; index < argv.length; index += 1) {
112
+ const arg = argv[index]
113
+ if (arg === "--version" || arg === "-v") {
114
+ return { kind: "version" }
115
+ }
116
+ if (arg === "--help" || arg === "-h") {
117
+ return { kind: "help" }
118
+ }
119
+ if (arg === "--port") {
120
+ const next = argv[index + 1]
121
+ if (!next) throw new Error("Missing value for --port")
122
+ port = Number(next)
123
+ index += 1
124
+ continue
125
+ }
126
+ if (arg === "--host") {
127
+ const next = argv[index + 1]
128
+ if (!next || next.startsWith("-")) throw new Error("Missing value for --host")
129
+ if (isShareEnabled(share)) {
130
+ throwShareConflict(share, "--host")
131
+ }
132
+ host = next
133
+ sawHost = true
134
+ index += 1
135
+ continue
136
+ }
137
+ if (arg === "--remote") {
138
+ if (isShareEnabled(share)) {
139
+ throwShareConflict(share, "--remote")
140
+ }
141
+ host = "0.0.0.0"
142
+ sawRemote = true
143
+ continue
144
+ }
145
+ if (arg === "--share") {
146
+ assertNoHostOverride("--share", sawHost, sawRemote)
147
+ share = "quick"
148
+ continue
149
+ }
150
+ if (arg === "--cloudflared") {
151
+ assertNoHostOverride("--cloudflared", sawHost, sawRemote)
152
+ const next = argv[index + 1]
153
+ if (!next || next.startsWith("-")) throw new Error("Missing value for --cloudflared")
154
+ share = { kind: "token", token: next }
155
+ index += 1
156
+ continue
157
+ }
158
+ if (arg === "--no-open") {
159
+ openBrowser = false
160
+ continue
161
+ }
162
+ if (arg === "--password") {
163
+ const next = argv[index + 1]
164
+ if (!next || next.startsWith("-")) throw new Error("Missing value for --password")
165
+ password = next
166
+ index += 1
167
+ continue
168
+ }
169
+ if (arg === "--strict-port") {
170
+ strictPort = true
171
+ continue
172
+ }
173
+ if (!arg.startsWith("-")) throw new Error(`Unexpected positional argument: ${arg}`)
174
+ }
175
+
176
+ return {
177
+ kind: "run",
178
+ options: {
179
+ port,
180
+ host,
181
+ openBrowser,
182
+ share,
183
+ password,
184
+ strictPort,
185
+ },
186
+ }
187
+ }
188
+
189
+ export function compareVersions(currentVersion: string, latestVersion: string) {
190
+ const currentParts = normalizeVersion(currentVersion)
191
+ const latestParts = normalizeVersion(latestVersion)
192
+ const length = Math.max(currentParts.length, latestParts.length)
193
+
194
+ for (let index = 0; index < length; index += 1) {
195
+ const current = currentParts[index] ?? 0
196
+ const latest = latestParts[index] ?? 0
197
+ if (current === latest) continue
198
+ return current < latest ? -1 : 1
199
+ }
200
+
201
+ return 0
202
+ }
203
+
204
+ function normalizeVersion(version: string) {
205
+ return version
206
+ .trim()
207
+ .replace(/^v/i, "")
208
+ .split("-")[0]
209
+ .split(".")
210
+ .map((part) => Number.parseInt(part, 10))
211
+ .filter((part) => Number.isFinite(part))
212
+ }
213
+
214
+ async function maybeSelfUpdate(_argv: string[], deps: CliRuntimeDeps) {
215
+ if (process.env.KANNA_DISABLE_SELF_UPDATE === "1") {
216
+ return null
217
+ }
218
+
219
+ deps.log(`${LOG_PREFIX} checking for updates`)
220
+
221
+ let latestVersion: string
222
+ try {
223
+ latestVersion = await deps.fetchLatestVersion(PACKAGE_NAME)
224
+ }
225
+ catch (error) {
226
+ deps.warn(`${LOG_PREFIX} update check failed, continuing current version`)
227
+ if (error instanceof Error && error.message) {
228
+ deps.warn(`${LOG_PREFIX} ${error.message}`)
229
+ }
230
+ return null
231
+ }
232
+
233
+ if (!latestVersion || compareVersions(deps.version, latestVersion) >= 0) {
234
+ return null
235
+ }
236
+
237
+ deps.log(`${LOG_PREFIX} installing ${PACKAGE_NAME}@${latestVersion}`)
238
+ const installResult = deps.installVersion(PACKAGE_NAME, latestVersion)
239
+ if (!installResult.ok) {
240
+ deps.warn(`${LOG_PREFIX} update failed, continuing current version`)
241
+ if (installResult.userMessage) {
242
+ deps.warn(`${LOG_PREFIX} ${installResult.userMessage}`)
243
+ }
244
+ return null
245
+ }
246
+
247
+ deps.log(`${LOG_PREFIX} restarting into updated version`)
248
+ return "startup_update"
249
+ }
250
+
251
+ export async function runCli(argv: string[], deps: CliRuntimeDeps): Promise<CliRunResult> {
252
+ const parsedArgs = parseArgs(argv)
253
+ if (parsedArgs.kind === "version") {
254
+ deps.log(deps.version)
255
+ return { kind: "exited", code: 0 }
256
+ }
257
+ if (parsedArgs.kind === "help") {
258
+ printHelp()
259
+ return { kind: "exited", code: 0 }
260
+ }
261
+
262
+ if (compareVersions(deps.bunVersion, MINIMUM_BUN_VERSION) < 0) {
263
+ deps.warn(`${LOG_PREFIX} Bun ${MINIMUM_BUN_VERSION}+ is required for the embedded terminal. Current Bun: ${deps.bunVersion}`)
264
+ return { kind: "exited", code: 1 }
265
+ }
266
+
267
+ const shouldRestart = await maybeSelfUpdate(argv, deps)
268
+ if (shouldRestart !== null) {
269
+ return { kind: "restarting", reason: shouldRestart }
270
+ }
271
+
272
+ const { port, stop } = await deps.startServer({
273
+ ...parsedArgs.options,
274
+ trustProxy: isShareEnabled(parsedArgs.options.share),
275
+ onMigrationProgress: deps.log,
276
+ update: {
277
+ version: deps.version,
278
+ fetchLatestVersion: deps.fetchLatestVersion,
279
+ installVersion: deps.installVersion,
280
+ argv,
281
+ command: CLI_COMMAND,
282
+ },
283
+ })
284
+ const bindHost = parsedArgs.options.host
285
+ const displayHost = isShareEnabled(parsedArgs.options.share) || bindHost === "127.0.0.1" || bindHost === "0.0.0.0" ? "localhost" : bindHost
286
+ const launchUrl = `http://${displayHost}:${port}`
287
+ let shareTunnelStop: (() => void) | null = null
288
+
289
+ deps.log(`${LOG_PREFIX} listening on http://${bindHost}:${port}`)
290
+ deps.log(`${LOG_PREFIX} data dir: ${getDataDirDisplay()}`)
291
+
292
+ const suppressOpenBrowser = process.env[CLI_SUPPRESS_OPEN_ONCE_ENV_VAR] === "1"
293
+ if (isShareEnabled(parsedArgs.options.share)) {
294
+ try {
295
+ const shareTunnel = await (deps.startShareTunnel ?? ((localUrl, shareMode) => startShareTunnel(localUrl, shareMode, {
296
+ log: (message) => deps.log(`${LOG_PREFIX} ${message}`),
297
+ })))(launchUrl, parsedArgs.options.share)
298
+ shareTunnelStop = shareTunnel.stop
299
+ if (shareTunnel.publicUrl) {
300
+ await logShareDetails(deps.log, shareTunnel.publicUrl, launchUrl, deps.renderShareQr ?? renderTerminalQr)
301
+ } else {
302
+ deps.warn(`${LOG_PREFIX} named tunnel started but no public hostname was detected`)
303
+ if (isTokenShareMode(parsedArgs.options.share)) {
304
+ deps.warn(`${LOG_PREFIX} use the hostname configured for the provided Cloudflare tunnel token`)
305
+ }
306
+ deps.log("Local URL:")
307
+ deps.log(launchUrl)
308
+ }
309
+ } catch (error) {
310
+ await stop()
311
+ deps.warn(`${LOG_PREFIX} failed to start Cloudflare share tunnel`)
312
+ if (error instanceof Error && error.message) {
313
+ deps.warn(`${LOG_PREFIX} ${error.message}`)
314
+ }
315
+ return { kind: "exited", code: 1 }
316
+ }
317
+ }
318
+
319
+ if (parsedArgs.options.openBrowser && !isShareEnabled(parsedArgs.options.share) && !suppressOpenBrowser) {
320
+ deps.openUrl(launchUrl)
321
+ }
322
+
323
+ return {
324
+ kind: "started",
325
+ stop: async () => {
326
+ shareTunnelStop?.()
327
+ await stop()
328
+ },
329
+ }
330
+ }
331
+
332
+ export function openUrl(url: string) {
333
+ const platform = process.platform
334
+ if (platform === "darwin") {
335
+ void spawnDetached("open", [url]).catch(() => {})
336
+ } else if (platform === "win32") {
337
+ void spawnDetached("cmd", ["/c", "start", "", url]).catch(() => {})
338
+ } else {
339
+ void spawnDetached("xdg-open", [url]).catch(() => {})
340
+ }
341
+ console.log(`${LOG_PREFIX} opened in default browser`)
342
+ }
343
+
344
+ export async function fetchLatestPackageVersion(packageName: string) {
345
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`)
346
+ if (!response.ok) {
347
+ throw new Error(`registry returned ${response.status}`)
348
+ }
349
+
350
+ const payload = await response.json() as { version?: unknown }
351
+ if (typeof payload.version !== "string" || !payload.version.trim()) {
352
+ throw new Error("registry response did not include a version")
353
+ }
354
+
355
+ return payload.version
356
+ }
357
+
358
+ export function classifyInstallVersionFailure(output: string): UpdateInstallAttemptResult {
359
+ const normalizedOutput = output.trim()
360
+ if (/No version matching .* found|failed to resolve/i.test(normalizedOutput)) {
361
+ return {
362
+ ok: false,
363
+ errorCode: "version_not_live_yet",
364
+ userTitle: "Update not live yet",
365
+ userMessage: "This update is still propagating. Try again in a few minutes.",
366
+ }
367
+ }
368
+
369
+ return {
370
+ ok: false,
371
+ errorCode: "install_failed",
372
+ userTitle: "Update failed",
373
+ userMessage: "Kanna could not install the update. Try again later.",
374
+ }
375
+ }
376
+
377
+ export function installPackageVersion(packageName: string, version: string) {
378
+ if (!hasCommand("bun")) {
379
+ return {
380
+ ok: false,
381
+ errorCode: "command_missing",
382
+ userTitle: "Bun not found",
383
+ userMessage: "Kanna could not find Bun to install the update.",
384
+ } satisfies UpdateInstallAttemptResult
385
+ }
386
+
387
+ const result = spawnSync("bun", ["install", "-g", `${packageName}@${version}`], {
388
+ stdio: ["ignore", "pipe", "pipe"],
389
+ encoding: "utf8",
390
+ })
391
+ const stdout = result.stdout ?? ""
392
+ const stderr = result.stderr ?? ""
393
+ if (stdout) process.stdout.write(stdout)
394
+ if (stderr) process.stderr.write(stderr)
395
+ if (result.status === 0) {
396
+ return {
397
+ ok: true,
398
+ errorCode: null,
399
+ userTitle: null,
400
+ userMessage: null,
401
+ } satisfies UpdateInstallAttemptResult
402
+ }
403
+
404
+ return classifyInstallVersionFailure(`${stdout}\n${stderr}`)
405
+ }
@@ -0,0 +1,102 @@
1
+ import process from "node:process"
2
+ import { spawn } from "node:child_process"
3
+ import { CLI_COMMAND, LOG_PREFIX } from "../shared/branding"
4
+ import {
5
+ CLI_CHILD_ARGS_ENV_VAR,
6
+ CLI_CHILD_COMMAND_ENV_VAR,
7
+ CLI_CHILD_MODE,
8
+ CLI_CHILD_MODE_ENV_VAR,
9
+ CLI_STARTUP_UPDATE_RESTART_EXIT_CODE,
10
+ CLI_SUPPRESS_OPEN_ONCE_ENV_VAR,
11
+ isUiUpdateRestart,
12
+ parseChildArgsEnv,
13
+ shouldRestartCliProcess,
14
+ } from "./restart"
15
+
16
+ interface ChildExit {
17
+ code: number | null
18
+ signal: NodeJS.Signals | null
19
+ }
20
+
21
+ function getChildProcessSpec() {
22
+ const command = process.env[CLI_CHILD_COMMAND_ENV_VAR] || CLI_COMMAND
23
+ const args = parseChildArgsEnv(process.env[CLI_CHILD_ARGS_ENV_VAR])
24
+ return { command, args }
25
+ }
26
+
27
+ function spawnChild(argv: string[]) {
28
+ const childProcess = getChildProcessSpec()
29
+ const suppressOpenThisChild = suppressOpenOnNextChild
30
+ const skipUpdateThisChild = skipUpdateOnNextChild
31
+ suppressOpenOnNextChild = false
32
+ skipUpdateOnNextChild = false
33
+ return new Promise<ChildExit>((resolve, reject) => {
34
+ const child = spawn(childProcess.command, [...childProcess.args, ...argv], {
35
+ stdio: "inherit",
36
+ env: {
37
+ ...process.env,
38
+ [CLI_CHILD_MODE_ENV_VAR]: CLI_CHILD_MODE,
39
+ ...(suppressOpenThisChild ? { [CLI_SUPPRESS_OPEN_ONCE_ENV_VAR]: "1" } : {}),
40
+ ...(skipUpdateThisChild ? { KANNA_DISABLE_SELF_UPDATE: "1" } : {}),
41
+ },
42
+ })
43
+
44
+ const forwardSignal = (signal: NodeJS.Signals) => {
45
+ if (child.exitCode !== null) return
46
+ child.kill(signal)
47
+ }
48
+
49
+ const onSigint = () => {
50
+ forwardSignal("SIGINT")
51
+ }
52
+ const onSigterm = () => {
53
+ forwardSignal("SIGTERM")
54
+ }
55
+
56
+ process.on("SIGINT", onSigint)
57
+ process.on("SIGTERM", onSigterm)
58
+
59
+ child.once("error", (error) => {
60
+ process.off("SIGINT", onSigint)
61
+ process.off("SIGTERM", onSigterm)
62
+ reject(error)
63
+ })
64
+
65
+ child.once("exit", (code, signal) => {
66
+ process.off("SIGINT", onSigint)
67
+ process.off("SIGTERM", onSigterm)
68
+ resolve({ code, signal })
69
+ })
70
+ })
71
+ }
72
+
73
+ const argv = process.argv.slice(2)
74
+ let suppressOpenOnNextChild = false
75
+ let skipUpdateOnNextChild = false
76
+ let lastStartupUpdateRestart = false
77
+
78
+ while (true) {
79
+ const result = await spawnChild(argv)
80
+ if (shouldRestartCliProcess(result.code, result.signal)) {
81
+ const isStartupUpdate = result.signal === null && result.code === CLI_STARTUP_UPDATE_RESTART_EXIT_CODE
82
+
83
+ // Guard against infinite restart loops: if two consecutive startup-update
84
+ // restarts happen it means the installed update did not change the binary
85
+ // that actually runs (e.g. when launched via `bunx`, which maintains its
86
+ // own package cache). Skip the self-update on the next spawn so the child
87
+ // proceeds normally instead of trying to update again.
88
+ if (isStartupUpdate && lastStartupUpdateRestart) {
89
+ console.log(`${LOG_PREFIX} update installed but the running binary did not change, continuing with current version`)
90
+ skipUpdateOnNextChild = true
91
+ lastStartupUpdateRestart = false
92
+ } else {
93
+ lastStartupUpdateRestart = isStartupUpdate
94
+ }
95
+
96
+ suppressOpenOnNextChild = isUiUpdateRestart(result.code, result.signal)
97
+ console.log(`${LOG_PREFIX} supervisor restarting ${CLI_COMMAND} in the same terminal session`)
98
+ continue
99
+ }
100
+
101
+ process.exit(result.code ?? (result.signal ? 1 : 0))
102
+ }
@@ -0,0 +1,64 @@
1
+ import process from "node:process"
2
+ import { LOG_PREFIX } from "../shared/branding"
3
+ import {
4
+ fetchLatestPackageVersion,
5
+ installPackageVersion,
6
+ openUrl,
7
+ runCli,
8
+ } from "./cli-runtime"
9
+ import { CLI_STARTUP_UPDATE_RESTART_EXIT_CODE, CLI_UI_UPDATE_RESTART_EXIT_CODE } from "./restart"
10
+ import { startKannaServer } from "./server"
11
+
12
+ // Read version from package.json at the package root
13
+ const pkg = await Bun.file(new URL("../../package.json", import.meta.url)).json()
14
+ const VERSION: string = pkg.version ?? "0.0.0"
15
+
16
+ const argv = process.argv.slice(2)
17
+ let resolveExitAction: ((action: "ui_restart" | "exit") => void) | null = null
18
+
19
+ const result = await runCli(argv, {
20
+ version: VERSION,
21
+ bunVersion: Bun.version,
22
+ startServer: async (options) => {
23
+ const started = await startKannaServer(options)
24
+ if (started.updateManager && options.update) {
25
+ started.updateManager.onChange((snapshot) => {
26
+ if (snapshot.status !== "restart_pending") return
27
+ console.log(`${LOG_PREFIX} update installed, shutting down current process for restart`)
28
+ resolveExitAction?.("ui_restart")
29
+ })
30
+ }
31
+
32
+ return started
33
+ },
34
+ fetchLatestVersion: fetchLatestPackageVersion,
35
+ installVersion: installPackageVersion,
36
+ openUrl,
37
+ log: console.log,
38
+ warn: console.warn,
39
+ })
40
+
41
+ if (result.kind === "exited") {
42
+ process.exit(result.code)
43
+ }
44
+
45
+ if (result.kind === "restarting") {
46
+ process.exit(result.reason === "startup_update" ? CLI_STARTUP_UPDATE_RESTART_EXIT_CODE : CLI_UI_UPDATE_RESTART_EXIT_CODE)
47
+ }
48
+
49
+ const exitAction = await new Promise<"ui_restart" | "exit">((resolve) => {
50
+ resolveExitAction = resolve
51
+
52
+ const shutdown = () => {
53
+ resolve("exit")
54
+ }
55
+
56
+ process.once("SIGINT", shutdown)
57
+ process.once("SIGTERM", shutdown)
58
+ })
59
+
60
+ await result.stop()
61
+ if (exitAction === "ui_restart") {
62
+ console.log(`${LOG_PREFIX} current process stopped, handing restart back to supervisor`)
63
+ }
64
+ process.exit(exitAction === "ui_restart" ? CLI_UI_UPDATE_RESTART_EXIT_CODE : 0)
@@ -0,0 +1,76 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { handleBashToolResult } from "./agent-integration"
3
+ import type { CloudflareTunnelEvent } from "./events"
4
+ import type { CloudflareTunnelSettings } from "../../shared/types"
5
+
6
+ const baseSettings: CloudflareTunnelSettings = {
7
+ enabled: true,
8
+ cloudflaredPath: "cloudflared",
9
+ mode: "always-ask",
10
+ }
11
+
12
+ describe("handleBashToolResult", () => {
13
+ test("emits one tunnel_proposed per detected port", async () => {
14
+ const events: CloudflareTunnelEvent[] = []
15
+ let autoCalls = 0
16
+ await handleBashToolResult({
17
+ command: "bun run dev",
18
+ stdout: "Local: http://localhost:5173\nNetwork: http://127.0.0.1:5174",
19
+ chatId: "c1",
20
+ sourcePid: 100,
21
+ settings: baseSettings,
22
+ onEvent: (e: CloudflareTunnelEvent) => events.push(e),
23
+ autoStart: async () => { autoCalls++ },
24
+ })
25
+ const proposed = events.filter((e: CloudflareTunnelEvent) => e.kind === "tunnel_proposed")
26
+ expect(proposed).toHaveLength(2)
27
+ const ports = proposed.map((e) => (e.kind === "tunnel_proposed" ? e.port : 0)).sort((a, b) => a - b)
28
+ expect(ports).toEqual([5173, 5174])
29
+ expect(autoCalls).toBe(0)
30
+ })
31
+
32
+ test("skips when feature disabled", async () => {
33
+ const events: CloudflareTunnelEvent[] = []
34
+ await handleBashToolResult({
35
+ command: "bun run dev",
36
+ stdout: "Local: http://localhost:5173",
37
+ chatId: "c1",
38
+ sourcePid: 100,
39
+ settings: { ...baseSettings, enabled: false },
40
+ onEvent: (e: CloudflareTunnelEvent) => events.push(e),
41
+ autoStart: async () => {},
42
+ })
43
+ expect(events).toEqual([])
44
+ })
45
+
46
+ test("auto-expose mode emits accepted + triggers autoStart per port", async () => {
47
+ const events: CloudflareTunnelEvent[] = []
48
+ let startCalls = 0
49
+ await handleBashToolResult({
50
+ command: "bun run dev",
51
+ stdout: "Local: http://localhost:5173",
52
+ chatId: "c1",
53
+ sourcePid: 100,
54
+ settings: { ...baseSettings, mode: "auto-expose" },
55
+ onEvent: (e: CloudflareTunnelEvent) => events.push(e),
56
+ autoStart: async () => { startCalls++ },
57
+ })
58
+ expect(startCalls).toBe(1)
59
+ expect(events.some((e) => e.kind === "tunnel_proposed")).toBe(true)
60
+ expect(events.some((e) => e.kind === "tunnel_accepted")).toBe(true)
61
+ })
62
+
63
+ test("no events when detector reports no server", async () => {
64
+ const events: CloudflareTunnelEvent[] = []
65
+ await handleBashToolResult({
66
+ command: "ls",
67
+ stdout: "a b c",
68
+ chatId: "c1",
69
+ sourcePid: null,
70
+ settings: baseSettings,
71
+ onEvent: (e: CloudflareTunnelEvent) => events.push(e),
72
+ autoStart: async () => {},
73
+ })
74
+ expect(events).toEqual([])
75
+ })
76
+ })
@@ -0,0 +1,55 @@
1
+ import { randomUUID } from "node:crypto"
2
+ import type { CloudflareTunnelSettings } from "../../shared/types"
3
+ import { evaluateBashOutput } from "./detector"
4
+ import type { CloudflareTunnelEvent } from "./events"
5
+ import { CLOUDFLARE_TUNNEL_EVENT_VERSION } from "./events"
6
+
7
+ export interface HandleBashArgs {
8
+ command: string
9
+ stdout: string
10
+ chatId: string
11
+ sourcePid: number | null
12
+ settings: CloudflareTunnelSettings
13
+ onEvent: (event: CloudflareTunnelEvent) => void
14
+ autoStart: (args: { chatId: string; tunnelId: string; port: number; sourcePid: number | null }) => Promise<void>
15
+ now?: () => number
16
+ }
17
+
18
+ export function handleBashToolResult(args: HandleBashArgs): Promise<void> {
19
+ return runHandleBashToolResult(args)
20
+ }
21
+
22
+ async function runHandleBashToolResult(args: HandleBashArgs): Promise<void> {
23
+ if (!args.settings.enabled) return
24
+ const result = evaluateBashOutput({
25
+ command: args.command,
26
+ stdout: args.stdout,
27
+ })
28
+ if (!result.isServer) return
29
+
30
+ const now = (args.now ?? Date.now)()
31
+ for (const port of result.ports) {
32
+ const tunnelId = randomUUID()
33
+ args.onEvent({
34
+ v: CLOUDFLARE_TUNNEL_EVENT_VERSION,
35
+ kind: "tunnel_proposed",
36
+ timestamp: now,
37
+ chatId: args.chatId,
38
+ tunnelId,
39
+ port,
40
+ sourcePid: args.sourcePid,
41
+ })
42
+
43
+ if (args.settings.mode === "auto-expose") {
44
+ args.onEvent({
45
+ v: CLOUDFLARE_TUNNEL_EVENT_VERSION,
46
+ kind: "tunnel_accepted",
47
+ timestamp: now,
48
+ chatId: args.chatId,
49
+ tunnelId,
50
+ source: "auto_setting",
51
+ })
52
+ await args.autoStart({ chatId: args.chatId, tunnelId, port, sourcePid: args.sourcePid })
53
+ }
54
+ }
55
+ }