buninu-linux 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (621) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES/Bun-LICENSE.md +84 -0
  3. package/LICENSES/GCC-Runtime-Library-Exception-3.1.txt +73 -0
  4. package/LICENSES/GPL-2.0.txt +338 -0
  5. package/LICENSES/GPL-3.0.txt +674 -0
  6. package/LICENSES/LGPL-2.1.txt +501 -0
  7. package/LICENSES/Linux-syscall-note.txt +25 -0
  8. package/LICENSES/musl-COPYRIGHT.txt +193 -0
  9. package/NOTICE.md +184 -0
  10. package/README.md +537 -0
  11. package/build-image.sh +44 -0
  12. package/build-uki.sh +75 -0
  13. package/fetch-alpine.sh +80 -0
  14. package/fetch-bun.sh +41 -0
  15. package/fetch-plus-build.sh +11 -0
  16. package/hello/build-hello.sh +14 -0
  17. package/hello/hello.c +50 -0
  18. package/index.js +235 -0
  19. package/initramfs/bin/sh +3 -0
  20. package/initramfs/buninu/.bashrc +11 -0
  21. package/initramfs/buninu/ARCHITECTURE.md +452 -0
  22. package/initramfs/buninu/CHANGELOG.md +580 -0
  23. package/initramfs/buninu/LICENSE +21 -0
  24. package/initramfs/buninu/README.md +1161 -0
  25. package/initramfs/buninu/apps/bun/make-bunBin.sh +99 -0
  26. package/initramfs/buninu/apps/bunmsh/CHANGELOG.md +493 -0
  27. package/initramfs/buninu/apps/bunmsh/LICENSE +21 -0
  28. package/initramfs/buninu/apps/bunmsh/LICENSE-MICRO +58 -0
  29. package/initramfs/buninu/apps/bunmsh/LICENSE-MKSH +295 -0
  30. package/initramfs/buninu/apps/bunmsh/PORTING.md +192 -0
  31. package/initramfs/buninu/apps/bunmsh/README.md +1042 -0
  32. package/initramfs/buninu/apps/bunmsh/bunmsh +7 -0
  33. package/initramfs/buninu/apps/bunmsh/bunshell.md +61 -0
  34. package/initramfs/buninu/apps/bunmsh/help/README.md +2237 -0
  35. package/initramfs/buninu/apps/bunmsh/help/alias.md +22 -0
  36. package/initramfs/buninu/apps/bunmsh/help/basename.md +38 -0
  37. package/initramfs/buninu/apps/bunmsh/help/builtin.md +25 -0
  38. package/initramfs/buninu/apps/bunmsh/help/bun.md +27 -0
  39. package/initramfs/buninu/apps/bunmsh/help/bunmsh.md +47 -0
  40. package/initramfs/buninu/apps/bunmsh/help/cat.md +31 -0
  41. package/initramfs/buninu/apps/bunmsh/help/catfancy.md +35 -0
  42. package/initramfs/buninu/apps/bunmsh/help/cd.md +22 -0
  43. package/initramfs/buninu/apps/bunmsh/help/chmod.md +27 -0
  44. package/initramfs/buninu/apps/bunmsh/help/clear.md +21 -0
  45. package/initramfs/buninu/apps/bunmsh/help/colon.md +22 -0
  46. package/initramfs/buninu/apps/bunmsh/help/command.md +27 -0
  47. package/initramfs/buninu/apps/bunmsh/help/cp.md +28 -0
  48. package/initramfs/buninu/apps/bunmsh/help/curl.md +181 -0
  49. package/initramfs/buninu/apps/bunmsh/help/cut.md +25 -0
  50. package/initramfs/buninu/apps/bunmsh/help/date.md +25 -0
  51. package/initramfs/buninu/apps/bunmsh/help/dirname.md +25 -0
  52. package/initramfs/buninu/apps/bunmsh/help/echo.md +25 -0
  53. package/initramfs/buninu/apps/bunmsh/help/env.md +27 -0
  54. package/initramfs/buninu/apps/bunmsh/help/eval.md +22 -0
  55. package/initramfs/buninu/apps/bunmsh/help/exec.md +22 -0
  56. package/initramfs/buninu/apps/bunmsh/help/exit.md +21 -0
  57. package/initramfs/buninu/apps/bunmsh/help/export.md +22 -0
  58. package/initramfs/buninu/apps/bunmsh/help/false.md +22 -0
  59. package/initramfs/buninu/apps/bunmsh/help/find.md +33 -0
  60. package/initramfs/buninu/apps/bunmsh/help/getopts.md +28 -0
  61. package/initramfs/buninu/apps/bunmsh/help/grep.md +34 -0
  62. package/initramfs/buninu/apps/bunmsh/help/head.md +27 -0
  63. package/initramfs/buninu/apps/bunmsh/help/kill.md +43 -0
  64. package/initramfs/buninu/apps/bunmsh/help/ln.md +28 -0
  65. package/initramfs/buninu/apps/bunmsh/help/ls.md +37 -0
  66. package/initramfs/buninu/apps/bunmsh/help/lsbun.md +29 -0
  67. package/initramfs/buninu/apps/bunmsh/help/lsfancy.md +37 -0
  68. package/initramfs/buninu/apps/bunmsh/help/md5sum.md +21 -0
  69. package/initramfs/buninu/apps/bunmsh/help/mkdir.md +27 -0
  70. package/initramfs/buninu/apps/bunmsh/help/mktemp.md +26 -0
  71. package/initramfs/buninu/apps/bunmsh/help/mv.md +26 -0
  72. package/initramfs/buninu/apps/bunmsh/help/print.md +30 -0
  73. package/initramfs/buninu/apps/bunmsh/help/printf.md +25 -0
  74. package/initramfs/buninu/apps/bunmsh/help/pspa.md +38 -0
  75. package/initramfs/buninu/apps/bunmsh/help/pspac.md +57 -0
  76. package/initramfs/buninu/apps/bunmsh/help/pwd.md +21 -0
  77. package/initramfs/buninu/apps/bunmsh/help/read.md +27 -0
  78. package/initramfs/buninu/apps/bunmsh/help/readonly.md +27 -0
  79. package/initramfs/buninu/apps/bunmsh/help/realpath.md +21 -0
  80. package/initramfs/buninu/apps/bunmsh/help/rm.md +33 -0
  81. package/initramfs/buninu/apps/bunmsh/help/rmdir.md +27 -0
  82. package/initramfs/buninu/apps/bunmsh/help/sed.md +29 -0
  83. package/initramfs/buninu/apps/bunmsh/help/seq.md +29 -0
  84. package/initramfs/buninu/apps/bunmsh/help/serve.md +50 -0
  85. package/initramfs/buninu/apps/bunmsh/help/set.md +26 -0
  86. package/initramfs/buninu/apps/bunmsh/help/sha256sum.md +21 -0
  87. package/initramfs/buninu/apps/bunmsh/help/shift.md +23 -0
  88. package/initramfs/buninu/apps/bunmsh/help/sleep.md +25 -0
  89. package/initramfs/buninu/apps/bunmsh/help/sort.md +29 -0
  90. package/initramfs/buninu/apps/bunmsh/help/source.md +23 -0
  91. package/initramfs/buninu/apps/bunmsh/help/tab.md +30 -0
  92. package/initramfs/buninu/apps/bunmsh/help/tac.md +29 -0
  93. package/initramfs/buninu/apps/bunmsh/help/tail.md +27 -0
  94. package/initramfs/buninu/apps/bunmsh/help/tee.md +25 -0
  95. package/initramfs/buninu/apps/bunmsh/help/test.md +30 -0
  96. package/initramfs/buninu/apps/bunmsh/help/time.md +29 -0
  97. package/initramfs/buninu/apps/bunmsh/help/touch.md +26 -0
  98. package/initramfs/buninu/apps/bunmsh/help/tr.md +26 -0
  99. package/initramfs/buninu/apps/bunmsh/help/true.md +22 -0
  100. package/initramfs/buninu/apps/bunmsh/help/type.md +21 -0
  101. package/initramfs/buninu/apps/bunmsh/help/umask.md +22 -0
  102. package/initramfs/buninu/apps/bunmsh/help/unalias.md +28 -0
  103. package/initramfs/buninu/apps/bunmsh/help/uname.md +31 -0
  104. package/initramfs/buninu/apps/bunmsh/help/unset.md +28 -0
  105. package/initramfs/buninu/apps/bunmsh/help/wc.md +27 -0
  106. package/initramfs/buninu/apps/bunmsh/help/whence.md +26 -0
  107. package/initramfs/buninu/apps/bunmsh/help/which.md +41 -0
  108. package/initramfs/buninu/apps/bunmsh/help/yes.md +23 -0
  109. package/initramfs/buninu/apps/bunmsh/index.js +7 -0
  110. package/initramfs/buninu/apps/bunmsh/package.json +30 -0
  111. package/initramfs/buninu/apps/bunmsh/serve.js +468 -0
  112. package/initramfs/buninu/apps/bunmsh/single-exe/README.md +402 -0
  113. package/initramfs/buninu/apps/bunmsh/single-exe/assetsHelper.js +371 -0
  114. package/initramfs/buninu/apps/bunmsh/single-exe/assetsLoader.mjs +85 -0
  115. package/initramfs/buninu/apps/bunmsh/single-exe/assetsPacker.js +218 -0
  116. package/initramfs/buninu/apps/bunmsh/single-exe/compiled.js +527 -0
  117. package/initramfs/buninu/apps/bunmsh/single-exe/entry.mjs +17 -0
  118. package/initramfs/buninu/apps/bunmsh/src/cat-fancy.js +117 -0
  119. package/initramfs/buninu/apps/bunmsh/src/cat-operands.js +31 -0
  120. package/initramfs/buninu/apps/bunmsh/src/completion.js +363 -0
  121. package/initramfs/buninu/apps/bunmsh/src/curl.js +1191 -0
  122. package/initramfs/buninu/apps/bunmsh/src/environment.js +61 -0
  123. package/initramfs/buninu/apps/bunmsh/src/fancy-ls.js +186 -0
  124. package/initramfs/buninu/apps/bunmsh/src/find.js +119 -0
  125. package/initramfs/buninu/apps/bunmsh/src/history.js +198 -0
  126. package/initramfs/buninu/apps/bunmsh/src/main.js +850 -0
  127. package/initramfs/buninu/apps/bunmsh/src/mouse.js +103 -0
  128. package/initramfs/buninu/apps/bunmsh/src/shell.js +3932 -0
  129. package/initramfs/buninu/apps/bunmsh/test/catfancy.test.js +102 -0
  130. package/initramfs/buninu/apps/bunmsh/test/curl.test.js +335 -0
  131. package/initramfs/buninu/apps/bunmsh/test/reference-source.sh +1 -0
  132. package/initramfs/buninu/apps/bunmsh/test/reference.test.js +817 -0
  133. package/initramfs/buninu/apps/bunmsh/test/serve/README.md +3 -0
  134. package/initramfs/buninu/apps/bunmsh/test/serve/bad.json +1 -0
  135. package/initramfs/buninu/apps/bunmsh/test/serve/data.json +1 -0
  136. package/initramfs/buninu/apps/bunmsh/test/serve/data.json5 +1 -0
  137. package/initramfs/buninu/apps/bunmsh/test/serve/data.jsonc +2 -0
  138. package/initramfs/buninu/apps/bunmsh/test/serve/data.jsonl +2 -0
  139. package/initramfs/buninu/apps/bunmsh/test/serve/data.ndjson +1 -0
  140. package/initramfs/buninu/apps/bunmsh/test/serve/data.toml +3 -0
  141. package/initramfs/buninu/apps/bunmsh/test/serve/data.ts +15 -0
  142. package/initramfs/buninu/apps/bunmsh/test/serve/data.xml +1 -0
  143. package/initramfs/buninu/apps/bunmsh/test/serve/data.yaml +3 -0
  144. package/initramfs/buninu/apps/bunmsh/test/serve/data.yml +1 -0
  145. package/initramfs/buninu/apps/bunmsh/test/serve.test.js +241 -0
  146. package/initramfs/buninu/apps/bunmsh/test/shell.test.js +2092 -0
  147. package/initramfs/buninu/apps/bunmsh/test/shell1.test.js +1087 -0
  148. package/initramfs/buninu/apps/bunmsh/test/shell2.test.js +457 -0
  149. package/initramfs/buninu/apps/bunmsh/test/shell3.test.js +741 -0
  150. package/initramfs/buninu/apps/bunmsh/test/t.sh +12 -0
  151. package/initramfs/buninu/apps/bunx/bunx.js +172 -0
  152. package/initramfs/buninu/apps/cmdlist +48 -0
  153. package/initramfs/buninu/apps/jsgotty/LICENSE +23 -0
  154. package/initramfs/buninu/apps/jsgotty/LICENSES/Apache-2.0-zmodem.js.txt +201 -0
  155. package/initramfs/buninu/apps/jsgotty/NOTICE +21 -0
  156. package/initramfs/buninu/apps/jsgotty/README.en.md +294 -0
  157. package/initramfs/buninu/apps/jsgotty/README.md +296 -0
  158. package/initramfs/buninu/apps/jsgotty/client.mjs +700 -0
  159. package/initramfs/buninu/apps/jsgotty/env.sh +2 -0
  160. package/initramfs/buninu/apps/jsgotty/gotty.js +3334 -0
  161. package/initramfs/buninu/apps/jsgotty/node_modules/.package-lock.json +35 -0
  162. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/LICENSE.md +9 -0
  163. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/README.md +319 -0
  164. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/common.gypi +20 -0
  165. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/except.gypi +25 -0
  166. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/index.js +12 -0
  167. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
  168. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/napi-inl.h +6607 -0
  169. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/napi.h +3201 -0
  170. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/node_addon_api.gyp +32 -0
  171. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/node_api.gyp +9 -0
  172. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/noexcept.gypi +26 -0
  173. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/nothing.c +0 -0
  174. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/package-support.json +21 -0
  175. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/package.json +480 -0
  176. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/README.md +73 -0
  177. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/check-napi.js +99 -0
  178. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/clang-format.js +71 -0
  179. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/conversion.js +301 -0
  180. package/initramfs/buninu/apps/jsgotty/node_modules/node-addon-api/tools/eslint-format.js +79 -0
  181. package/initramfs/buninu/apps/jsgotty/node_modules/ws/LICENSE +20 -0
  182. package/initramfs/buninu/apps/jsgotty/node_modules/ws/README.md +548 -0
  183. package/initramfs/buninu/apps/jsgotty/node_modules/ws/browser.js +8 -0
  184. package/initramfs/buninu/apps/jsgotty/node_modules/ws/index.js +22 -0
  185. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/buffer-util.js +131 -0
  186. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/constants.js +19 -0
  187. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/event-target.js +292 -0
  188. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/extension.js +203 -0
  189. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/limiter.js +55 -0
  190. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/permessage-deflate.js +528 -0
  191. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/receiver.js +760 -0
  192. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/sender.js +607 -0
  193. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/stream.js +161 -0
  194. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/subprotocol.js +62 -0
  195. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/validation.js +152 -0
  196. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/websocket-server.js +562 -0
  197. package/initramfs/buninu/apps/jsgotty/node_modules/ws/lib/websocket.js +1407 -0
  198. package/initramfs/buninu/apps/jsgotty/node_modules/ws/package.json +70 -0
  199. package/initramfs/buninu/apps/jsgotty/node_modules/ws/wrapper.mjs +21 -0
  200. package/initramfs/buninu/apps/jsgotty/package-lock.json +46 -0
  201. package/initramfs/buninu/apps/jsgotty/package.json +19 -0
  202. package/initramfs/buninu/apps/jsgotty/patches/windowsPtyAgent.js +326 -0
  203. package/initramfs/buninu/apps/jsgotty/patches/windowsTerminal.js +204 -0
  204. package/initramfs/buninu/apps/jsgotty/rz.js +199 -0
  205. package/initramfs/buninu/apps/jsgotty/single-exe/README.md +384 -0
  206. package/initramfs/buninu/apps/jsgotty/single-exe/assetsHelper.js +371 -0
  207. package/initramfs/buninu/apps/jsgotty/single-exe/assetsLoader.mjs +85 -0
  208. package/initramfs/buninu/apps/jsgotty/single-exe/assetsPacker.js +218 -0
  209. package/initramfs/buninu/apps/jsgotty/single-exe/compiled.js +498 -0
  210. package/initramfs/buninu/apps/jsgotty/single-exe/entry.mjs +23 -0
  211. package/initramfs/buninu/apps/jsgotty/single-exe/packAssets.sh +7 -0
  212. package/initramfs/buninu/apps/jsgotty/static/css/index.css +13 -0
  213. package/initramfs/buninu/apps/jsgotty/static/css/kitty.css +61 -0
  214. package/initramfs/buninu/apps/jsgotty/static/css/xterm.css +218 -0
  215. package/initramfs/buninu/apps/jsgotty/static/css/xterm_customize.css +35 -0
  216. package/initramfs/buninu/apps/jsgotty/static/favicon.ico +0 -0
  217. package/initramfs/buninu/apps/jsgotty/static/help.md +125 -0
  218. package/initramfs/buninu/apps/jsgotty/static/icon.svg +66 -0
  219. package/initramfs/buninu/apps/jsgotty/static/icon_192.png +0 -0
  220. package/initramfs/buninu/apps/jsgotty/static/index.html +164 -0
  221. package/initramfs/buninu/apps/jsgotty/static/js/gotty.js +31537 -0
  222. package/initramfs/buninu/apps/jsgotty/static/js/gotty.js.LICENSE.txt +7 -0
  223. package/initramfs/buninu/apps/jsgotty/static/js/gotty.js.map +1 -0
  224. package/initramfs/buninu/apps/jsgotty/static/js/gotty.licenses.txt +650 -0
  225. package/initramfs/buninu/apps/jsgotty/static/js/kitty.js +1101 -0
  226. package/initramfs/buninu/apps/jsgotty/static/js/windows_bridge.js +194 -0
  227. package/initramfs/buninu/apps/jsgotty/static/js/xterm-addon-unicode11.js +2 -0
  228. package/initramfs/buninu/apps/jsgotty/static/js/zmodem.js +1 -0
  229. package/initramfs/buninu/apps/jsgotty/static/manifest.json +15 -0
  230. package/initramfs/buninu/apps/jsgotty/sz.js +273 -0
  231. package/initramfs/buninu/apps/jsgotty/tests/kitty-order.test.js +58 -0
  232. package/initramfs/buninu/apps/jsgotty/tests/websocket-origin.test.js +54 -0
  233. package/initramfs/buninu/apps/jsgotty/viu.mjs +422 -0
  234. package/initramfs/buninu/apps/jsgotty/wgotty +62 -0
  235. package/initramfs/buninu/apps/jsgotty/windows-bridge-node.js +60 -0
  236. package/initramfs/buninu/apps/jsgotty/zmodem-node.js +262 -0
  237. package/initramfs/buninu/apps/jsmdcui/CHANGELOG.md +1118 -0
  238. package/initramfs/buninu/apps/jsmdcui/LICENSE +22 -0
  239. package/initramfs/buninu/apps/jsmdcui/README.md +1283 -0
  240. package/initramfs/buninu/apps/jsmdcui/cdp-maze.js +141 -0
  241. package/initramfs/buninu/apps/jsmdcui/clean.sh +9 -0
  242. package/initramfs/buninu/apps/jsmdcui/demos/basic.jpg +0 -0
  243. package/initramfs/buninu/apps/jsmdcui/demos/chinese.md +32 -0
  244. package/initramfs/buninu/apps/jsmdcui/demos/event.md +170 -0
  245. package/initramfs/buninu/apps/jsmdcui/demos/good.jpg +0 -0
  246. package/initramfs/buninu/apps/jsmdcui/demos/image-processor.md +366 -0
  247. package/initramfs/buninu/apps/jsmdcui/demos/image-processor.zh-TW.md +366 -0
  248. package/initramfs/buninu/apps/jsmdcui/demos/img-change.md +73 -0
  249. package/initramfs/buninu/apps/jsmdcui/demos/imgtool-zh.md +298 -0
  250. package/initramfs/buninu/apps/jsmdcui/demos/imgtool.md +298 -0
  251. package/initramfs/buninu/apps/jsmdcui/demos/maze.md +149 -0
  252. package/initramfs/buninu/apps/jsmdcui/demos/reactive.md +40 -0
  253. package/initramfs/buninu/apps/jsmdcui/demos/reader.md +513 -0
  254. package/initramfs/buninu/apps/jsmdcui/demos/select.md +63 -0
  255. package/initramfs/buninu/apps/jsmdcui/demos/todo-zh.md +190 -0
  256. package/initramfs/buninu/apps/jsmdcui/demos/todo.md +191 -0
  257. package/initramfs/buninu/apps/jsmdcui/edit +9 -0
  258. package/initramfs/buninu/apps/jsmdcui/jsmdcui.sh +16 -0
  259. package/initramfs/buninu/apps/jsmdcui/llm-maze.txt +94 -0
  260. package/initramfs/buninu/apps/jsmdcui/package.json +29 -0
  261. package/initramfs/buninu/apps/jsmdcui/project_structure.txt +35 -0
  262. package/initramfs/buninu/apps/jsmdcui/runmd.mjs +1140 -0
  263. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/atom-dark.micro +33 -0
  264. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/bubblegum.micro +31 -0
  265. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/cmc-16.micro +47 -0
  266. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/cmc-tc.micro +43 -0
  267. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/darcula.micro +34 -0
  268. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/default.micro +1 -0
  269. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/dracula-tc.micro +49 -0
  270. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/dukedark-tc.micro +38 -0
  271. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/dukelight-tc.micro +38 -0
  272. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/dukeubuntu-tc.micro +38 -0
  273. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/geany.micro +29 -0
  274. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/gotham.micro +29 -0
  275. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/gruvbox-tc.micro +29 -0
  276. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/gruvbox.micro +26 -0
  277. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/material-tc.micro +36 -0
  278. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/monokai-dark.micro +28 -0
  279. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/monokai.micro +34 -0
  280. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/one-dark.micro +39 -0
  281. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/railscast.micro +37 -0
  282. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/simple.micro +33 -0
  283. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/solarized-tc.micro +31 -0
  284. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/solarized.micro +30 -0
  285. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/sunny-day.micro +29 -0
  286. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/twilight.micro +40 -0
  287. package/initramfs/buninu/apps/jsmdcui/runtime/colorschemes/zenburn.micro +30 -0
  288. package/initramfs/buninu/apps/jsmdcui/runtime/help/actions.md +161 -0
  289. package/initramfs/buninu/apps/jsmdcui/runtime/help/cdp.md +130 -0
  290. package/initramfs/buninu/apps/jsmdcui/runtime/help/colors.md +421 -0
  291. package/initramfs/buninu/apps/jsmdcui/runtime/help/commands.md +161 -0
  292. package/initramfs/buninu/apps/jsmdcui/runtime/help/copypaste.md +149 -0
  293. package/initramfs/buninu/apps/jsmdcui/runtime/help/defaultkeys.md +148 -0
  294. package/initramfs/buninu/apps/jsmdcui/runtime/help/help.md +1283 -0
  295. package/initramfs/buninu/apps/jsmdcui/runtime/help/keybindings.md +355 -0
  296. package/initramfs/buninu/apps/jsmdcui/runtime/help/linter.md +90 -0
  297. package/initramfs/buninu/apps/jsmdcui/runtime/help/options.md +709 -0
  298. package/initramfs/buninu/apps/jsmdcui/runtime/help/plugins.md +544 -0
  299. package/initramfs/buninu/apps/jsmdcui/runtime/help/tutorial.md +112 -0
  300. package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/cdp/cdp-server.js +1263 -0
  301. package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/cdp/cdp.js +203 -0
  302. package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/chapter/chapter.js +111 -0
  303. package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/diff/diff.js +46 -0
  304. package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/example/example.js +117 -0
  305. package/initramfs/buninu/apps/jsmdcui/runtime/jsplugins/linter/linter.js +281 -0
  306. package/initramfs/buninu/apps/jsmdcui/runtime/plugins/autoclose/autoclose.lua +75 -0
  307. package/initramfs/buninu/apps/jsmdcui/runtime/plugins/ftoptions/ftoptions.lua +17 -0
  308. package/initramfs/buninu/apps/jsmdcui/runtime/plugins/literate/README.md +5 -0
  309. package/initramfs/buninu/apps/jsmdcui/runtime/plugins/literate/literate.lua +55 -0
  310. package/initramfs/buninu/apps/jsmdcui/runtime/plugins/status/help/status.md +21 -0
  311. package/initramfs/buninu/apps/jsmdcui/runtime/plugins/status/status.lua +62 -0
  312. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/LICENSE +22 -0
  313. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/PowerShell.yaml +128 -0
  314. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/README.md +63 -0
  315. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ada.yaml +43 -0
  316. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/apacheconf.yaml +59 -0
  317. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/arduino.yaml +101 -0
  318. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/asciidoc.yaml +51 -0
  319. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/asm.yaml +123 -0
  320. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ats.yaml +99 -0
  321. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/awk.yaml +44 -0
  322. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/b.yaml +87 -0
  323. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/bat.yaml +57 -0
  324. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/c.yaml +60 -0
  325. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/caddyfile.yaml +23 -0
  326. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cake.yaml +7 -0
  327. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/clojure.yaml +38 -0
  328. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cmake.yaml +42 -0
  329. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/coffeescript.yaml +56 -0
  330. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/colortest.yaml +19 -0
  331. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/conky.yaml +17 -0
  332. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cpp.yaml +91 -0
  333. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/crontab.yaml +36 -0
  334. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/crystal.yaml +72 -0
  335. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/csharp.yaml +52 -0
  336. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/css.yaml +44 -0
  337. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/csx.yaml +8 -0
  338. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cuda.yaml +68 -0
  339. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/cython.yaml +52 -0
  340. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/d.yaml +121 -0
  341. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/dart.yaml +46 -0
  342. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/default.yaml +10 -0
  343. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/dockerfile.yaml +36 -0
  344. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/dot.yaml +29 -0
  345. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/elixir.yaml +30 -0
  346. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/elm.yaml +38 -0
  347. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/erb.yaml +42 -0
  348. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/erlang.yaml +45 -0
  349. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/fish.yaml +48 -0
  350. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/forth.yaml +34 -0
  351. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/fortran.yaml +64 -0
  352. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/freebsd-kernel.yaml +14 -0
  353. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/fsharp.yaml +48 -0
  354. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gdscript.yaml +61 -0
  355. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gemini.yaml +19 -0
  356. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gentoo-ebuild.yaml +48 -0
  357. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gentoo-etc-portage.yaml +23 -0
  358. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/git-commit.yaml +35 -0
  359. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/git-config.yaml +14 -0
  360. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/git-rebase-todo.yaml +19 -0
  361. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gleam.yaml +69 -0
  362. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/glsl.yaml +26 -0
  363. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gnuplot.yaml +15 -0
  364. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/go.yaml +62 -0
  365. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/godoc.yaml +17 -0
  366. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/golo.yaml +73 -0
  367. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/gomod.yaml +31 -0
  368. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/graphql.yaml +47 -0
  369. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/groff.yaml +30 -0
  370. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/groovy.yaml +111 -0
  371. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/haml.yaml +16 -0
  372. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/hare.yaml +52 -0
  373. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/haskell.yaml +52 -0
  374. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/hc.yaml +52 -0
  375. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/html.yaml +70 -0
  376. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/html4.yaml +25 -0
  377. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/html5.yaml +25 -0
  378. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ini.yaml +23 -0
  379. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/inputrc.yaml +14 -0
  380. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/java.yaml +37 -0
  381. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/javascript.yaml +124 -0
  382. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/jinja2.yaml +19 -0
  383. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/json.yaml +39 -0
  384. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/jsonnet.yaml +92 -0
  385. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/julia.yaml +57 -0
  386. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/justfile.yaml +40 -0
  387. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/keymap.yaml +27 -0
  388. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/kickstart.yaml +16 -0
  389. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/kotlin.yaml +66 -0
  390. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/kvlang.yaml +67 -0
  391. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ledger.yaml +14 -0
  392. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/lfe.yaml +17 -0
  393. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/lilypond.yaml +26 -0
  394. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/lisp.yaml +17 -0
  395. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/log.yaml +92 -0
  396. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/lua.yaml +111 -0
  397. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/mail.yaml +25 -0
  398. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/makefile.yaml +38 -0
  399. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/man.yaml +12 -0
  400. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/markdown.yaml +65 -0
  401. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/mc.yaml +23 -0
  402. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/meson.yaml +51 -0
  403. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/micro.yaml +34 -0
  404. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/mpdconf.yaml +13 -0
  405. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/msbuild.yaml +6 -0
  406. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nanorc.yaml +16 -0
  407. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nftables.yaml +30 -0
  408. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nginx.yaml +22 -0
  409. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nim.yaml +27 -0
  410. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nix.yaml +32 -0
  411. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/nu.yaml +114 -0
  412. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/objc.yaml +60 -0
  413. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ocaml.yaml +43 -0
  414. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/octave.yaml +83 -0
  415. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/odin.yaml +64 -0
  416. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/pascal.yaml +45 -0
  417. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/patch.yaml +14 -0
  418. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/peg.yaml +16 -0
  419. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/perl.yaml +58 -0
  420. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/php.yaml +60 -0
  421. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/pkg-config.yaml +12 -0
  422. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/po.yaml +12 -0
  423. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/pony.yaml +37 -0
  424. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/pov.yaml +21 -0
  425. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/privoxy-action.yaml +14 -0
  426. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/privoxy-config.yaml +10 -0
  427. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/privoxy-filter.yaml +12 -0
  428. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/proto.yaml +40 -0
  429. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/prql.yaml +84 -0
  430. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/puppet.yaml +22 -0
  431. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/python2.yaml +60 -0
  432. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/python3.yaml +80 -0
  433. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/r.yaml +32 -0
  434. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/raku.yaml +42 -0
  435. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/reST.yaml +18 -0
  436. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/renpy.yaml +15 -0
  437. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/rpmspec.yaml +43 -0
  438. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/ruby.yaml +73 -0
  439. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/rust.yaml +78 -0
  440. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sage.yaml +60 -0
  441. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/scad.yaml +53 -0
  442. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/scala.yaml +33 -0
  443. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sed.yaml +13 -0
  444. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sh.yaml +69 -0
  445. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sls.yaml +15 -0
  446. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/smalltalk.yaml +55 -0
  447. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/solidity.yaml +41 -0
  448. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/sql.yaml +35 -0
  449. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/stata.yaml +67 -0
  450. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/svelte.yaml +27 -0
  451. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/swift.yaml +103 -0
  452. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/systemd.yaml +16 -0
  453. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/tcl.yaml +18 -0
  454. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/terraform.yaml +87 -0
  455. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/tex.yaml +32 -0
  456. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/toml.yaml +56 -0
  457. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/twig.yaml +55 -0
  458. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/typescript.yaml +49 -0
  459. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/v.yaml +80 -0
  460. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/vala.yaml +26 -0
  461. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/verilog.yaml +60 -0
  462. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/vhdl.yaml +37 -0
  463. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/vi.yaml +31 -0
  464. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/vue.yaml +64 -0
  465. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/xml.yaml +37 -0
  466. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/xresources.yaml +14 -0
  467. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/yaml.yaml +34 -0
  468. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/yum.yaml +12 -0
  469. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/zig.yaml +52 -0
  470. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/zscript.yaml +72 -0
  471. package/initramfs/buninu/apps/jsmdcui/runtime/syntax/zsh.yaml +52 -0
  472. package/initramfs/buninu/apps/jsmdcui/single-exe/README.md +396 -0
  473. package/initramfs/buninu/apps/jsmdcui/single-exe/assetsHelper.js +371 -0
  474. package/initramfs/buninu/apps/jsmdcui/single-exe/assetsLoader.mjs +85 -0
  475. package/initramfs/buninu/apps/jsmdcui/single-exe/assetsPacker.js +218 -0
  476. package/initramfs/buninu/apps/jsmdcui/single-exe/compiled.js +498 -0
  477. package/initramfs/buninu/apps/jsmdcui/single-exe/entry.mjs +17 -0
  478. package/initramfs/buninu/apps/jsmdcui/single-exe/packAssets.sh +7 -0
  479. package/initramfs/buninu/apps/jsmdcui/src/MDCUI_DEFAULT_EDIT +0 -0
  480. package/initramfs/buninu/apps/jsmdcui/src/buffer/backup.js +165 -0
  481. package/initramfs/buninu/apps/jsmdcui/src/buffer/buffer.js +126 -0
  482. package/initramfs/buninu/apps/jsmdcui/src/buffer/fixed3-codec.js +140 -0
  483. package/initramfs/buninu/apps/jsmdcui/src/buffer/loc.js +38 -0
  484. package/initramfs/buninu/apps/jsmdcui/src/buffer/message.js +29 -0
  485. package/initramfs/buninu/apps/jsmdcui/src/build-args.js +40 -0
  486. package/initramfs/buninu/apps/jsmdcui/src/config/clean.js +172 -0
  487. package/initramfs/buninu/apps/jsmdcui/src/config/colorscheme.js +99 -0
  488. package/initramfs/buninu/apps/jsmdcui/src/config/config.js +125 -0
  489. package/initramfs/buninu/apps/jsmdcui/src/config/defaults.js +109 -0
  490. package/initramfs/buninu/apps/jsmdcui/src/cui/fence-events.mjs +144 -0
  491. package/initramfs/buninu/apps/jsmdcui/src/cui/heading-ids.mjs +1568 -0
  492. package/initramfs/buninu/apps/jsmdcui/src/cui/id-collision.mjs +111 -0
  493. package/initramfs/buninu/apps/jsmdcui/src/cui/identity.mjs +26 -0
  494. package/initramfs/buninu/apps/jsmdcui/src/cui/kitty-debug.mjs +25 -0
  495. package/initramfs/buninu/apps/jsmdcui/src/cui/kitty-images.mjs +280 -0
  496. package/initramfs/buninu/apps/jsmdcui/src/cui/kitty-mode.mjs +6 -0
  497. package/initramfs/buninu/apps/jsmdcui/src/cui/rerender-queue.mjs +66 -0
  498. package/initramfs/buninu/apps/jsmdcui/src/cui/rpc.mjs +1554 -0
  499. package/initramfs/buninu/apps/jsmdcui/src/cui/server.mjs +128 -0
  500. package/initramfs/buninu/apps/jsmdcui/src/cui/table-render.mjs +286 -0
  501. package/initramfs/buninu/apps/jsmdcui/src/cui/table-row-edit.mjs +226 -0
  502. package/initramfs/buninu/apps/jsmdcui/src/cui/task-checkbox.mjs +60 -0
  503. package/initramfs/buninu/apps/jsmdcui/src/cui/template-components.mjs +253 -0
  504. package/initramfs/buninu/apps/jsmdcui/src/cui/text-block-resize.mjs +177 -0
  505. package/initramfs/buninu/apps/jsmdcui/src/cui/tui-links.mjs +560 -0
  506. package/initramfs/buninu/apps/jsmdcui/src/display/ansi-style.js +60 -0
  507. package/initramfs/buninu/apps/jsmdcui/src/highlight/highlighter.js +243 -0
  508. package/initramfs/buninu/apps/jsmdcui/src/highlight/parser.js +225 -0
  509. package/initramfs/buninu/apps/jsmdcui/src/index.js +9586 -0
  510. package/initramfs/buninu/apps/jsmdcui/src/lua/engine.js +68 -0
  511. package/initramfs/buninu/apps/jsmdcui/src/platform/archive.js +50 -0
  512. package/initramfs/buninu/apps/jsmdcui/src/platform/clipboard.js +278 -0
  513. package/initramfs/buninu/apps/jsmdcui/src/platform/commands.js +221 -0
  514. package/initramfs/buninu/apps/jsmdcui/src/platform/terminal.js +5 -0
  515. package/initramfs/buninu/apps/jsmdcui/src/plugins/js-bridge.js +3377 -0
  516. package/initramfs/buninu/apps/jsmdcui/src/plugins/manager.js +674 -0
  517. package/initramfs/buninu/apps/jsmdcui/src/runtime/encodings.js +10 -0
  518. package/initramfs/buninu/apps/jsmdcui/src/runtime/registry.js +134 -0
  519. package/initramfs/buninu/apps/jsmdcui/src/screen/cell-buffer.js +81 -0
  520. package/initramfs/buninu/apps/jsmdcui/src/screen/events.js +263 -0
  521. package/initramfs/buninu/apps/jsmdcui/src/screen/screen.js +249 -0
  522. package/initramfs/buninu/apps/jsmdcui/src/screen/vt100.js +571 -0
  523. package/initramfs/buninu/apps/jsmdcui/src/shell/shell.js +70 -0
  524. package/initramfs/buninu/apps/jsmdcui/testapp.md +78 -0
  525. package/initramfs/buninu/apps/jsmdcui/tests/assets-helper.test.js +150 -0
  526. package/initramfs/buninu/apps/jsmdcui/tests/backup.test.js +158 -0
  527. package/initramfs/buninu/apps/jsmdcui/tests/cat-markdown.test.js +139 -0
  528. package/initramfs/buninu/apps/jsmdcui/tests/cdp-security.test.js +32 -0
  529. package/initramfs/buninu/apps/jsmdcui/tests/cdp-websocket-test.html +46 -0
  530. package/initramfs/buninu/apps/jsmdcui/tests/cmphex3b64.js +95 -0
  531. package/initramfs/buninu/apps/jsmdcui/tests/compiled-runtime.test.js +139 -0
  532. package/initramfs/buninu/apps/jsmdcui/tests/config-settings.test.js +52 -0
  533. package/initramfs/buninu/apps/jsmdcui/tests/demo.test.js +448 -0
  534. package/initramfs/buninu/apps/jsmdcui/tests/docs-sync.test.js +14 -0
  535. package/initramfs/buninu/apps/jsmdcui/tests/fence-events.test.js +1293 -0
  536. package/initramfs/buninu/apps/jsmdcui/tests/heading-ids.test.js +559 -0
  537. package/initramfs/buninu/apps/jsmdcui/tests/heading-list-selector.test.js +2393 -0
  538. package/initramfs/buninu/apps/jsmdcui/tests/id-collision.test.js +514 -0
  539. package/initramfs/buninu/apps/jsmdcui/tests/jmigt.js +16 -0
  540. package/initramfs/buninu/apps/jsmdcui/tests/js-plugin-prompts.test.js +110 -0
  541. package/initramfs/buninu/apps/jsmdcui/tests/key-event.md +10 -0
  542. package/initramfs/buninu/apps/jsmdcui/tests/kitty-demo.md +184 -0
  543. package/initramfs/buninu/apps/jsmdcui/tests/kitty-images.test.js +288 -0
  544. package/initramfs/buninu/apps/jsmdcui/tests/kitty-mode.test.js +14 -0
  545. package/initramfs/buninu/apps/jsmdcui/tests/platform-commands.test.js +18 -0
  546. package/initramfs/buninu/apps/jsmdcui/tests/platform-terminal.test.js +8 -0
  547. package/initramfs/buninu/apps/jsmdcui/tests/pty-demo.js +492 -0
  548. package/initramfs/buninu/apps/jsmdcui/tests/rerender-queue.test.js +130 -0
  549. package/initramfs/buninu/apps/jsmdcui/tests/table-render.test.js +150 -0
  550. package/initramfs/buninu/apps/jsmdcui/tests/table-row-edit.test.js +138 -0
  551. package/initramfs/buninu/apps/jsmdcui/tests/task-checkbox.test.js +33 -0
  552. package/initramfs/buninu/apps/jsmdcui/tests/template-components.test.js +422 -0
  553. package/initramfs/buninu/apps/jsmdcui/tests/text-block-resize.test.js +222 -0
  554. package/initramfs/buninu/apps/jsmdcui/tests/textarea.md +8 -0
  555. package/initramfs/buninu/apps/jsmdcui/tests/tui-links.test.js +298 -0
  556. package/initramfs/buninu/apps/jsmdcui/tests/wui-responsive-images.test.js +60 -0
  557. package/initramfs/buninu/apps/jsmdcui/tests/wui.test.js +108 -0
  558. package/initramfs/buninu/apps/jsmdcui/tests/wv-client.js +96 -0
  559. package/initramfs/buninu/apps/jsmdcui/tui +7 -0
  560. package/initramfs/buninu/apps/jsmdcui/wui +9 -0
  561. package/initramfs/buninu/apps/musl-la/LICENSES/GPL-3.0.txt +674 -0
  562. package/initramfs/buninu/apps/musl-la/LICENSES/gcc_runtime_exception.txt +73 -0
  563. package/initramfs/buninu/apps/musl-la/LICENSE_musl.txt +193 -0
  564. package/initramfs/buninu/apps/musl-la/NOTICE +41 -0
  565. package/initramfs/buninu/apps/musl-la/ld-musl-aarch64.so.1 +0 -0
  566. package/initramfs/buninu/apps/musl-la/libgcc_s.so.1 +0 -0
  567. package/initramfs/buninu/apps/musl-la/libstdc++.so.6 +0 -0
  568. package/initramfs/buninu/apps/musl-la/musl-la.sh +36 -0
  569. package/initramfs/buninu/apps/native-bridge/native-bridge.js +199 -0
  570. package/initramfs/buninu/apps/native-bridge/package.json +8 -0
  571. package/initramfs/buninu/apps/tts/tts.js +214 -0
  572. package/initramfs/buninu/apps/xclip/xclip.js +121 -0
  573. package/initramfs/buninu/apps/xdg-open/xdg-open.js +151 -0
  574. package/initramfs/buninu/bin/bun.bat +37 -0
  575. package/initramfs/buninu/bin/bun.sh +57 -0
  576. package/initramfs/buninu/bin/buninu-help.bat +4 -0
  577. package/initramfs/buninu/bin/buninu-help.sh +7 -0
  578. package/initramfs/buninu/bin/bunmsh.bat +3 -0
  579. package/initramfs/buninu/bin/bunmsh.sh +5 -0
  580. package/initramfs/buninu/bin/bunx.bat +3 -0
  581. package/initramfs/buninu/bin/glow.bat +3 -0
  582. package/initramfs/buninu/bin/glow.sh +5 -0
  583. package/initramfs/buninu/bin/init.js +779 -0
  584. package/initramfs/buninu/bin/install.js +570 -0
  585. package/initramfs/buninu/bin/jmi.bat +3 -0
  586. package/initramfs/buninu/bin/jmi.sh +5 -0
  587. package/initramfs/buninu/bin/jsgotty.bat +3 -0
  588. package/initramfs/buninu/bin/jsgotty.sh +5 -0
  589. package/initramfs/buninu/bin/jsmdcui.bat +11 -0
  590. package/initramfs/buninu/bin/libsh-loader.c +135 -0
  591. package/initramfs/buninu/bin/libsh-loader.so +0 -0
  592. package/initramfs/buninu/bin/multicall.sh +103 -0
  593. package/initramfs/buninu/bin/native-bridge.bat +3 -0
  594. package/initramfs/buninu/bin/rz.bat +3 -0
  595. package/initramfs/buninu/bin/rz.sh +5 -0
  596. package/initramfs/buninu/bin/shc.js +5 -0
  597. package/initramfs/buninu/bin/showimg.bat +3 -0
  598. package/initramfs/buninu/bin/showimg.sh +5 -0
  599. package/initramfs/buninu/bin/switch_to_android.sh +9 -0
  600. package/initramfs/buninu/bin/switch_to_linux.sh +7 -0
  601. package/initramfs/buninu/bin/sz.bat +3 -0
  602. package/initramfs/buninu/bin/sz.sh +5 -0
  603. package/initramfs/buninu/bin/tts.bat +3 -0
  604. package/initramfs/buninu/bin/xclip.bat +3 -0
  605. package/initramfs/buninu/bin/xdg-open.bat +3 -0
  606. package/initramfs/buninu/demo-server.js +64 -0
  607. package/initramfs/buninu/icon.png +0 -0
  608. package/initramfs/buninu/package.json +59 -0
  609. package/initramfs/etc/resolv.conf +1 -0
  610. package/initramfs/init.js +339 -0
  611. package/initramfs/lib/ld-musl-x86_64.so.1 +0 -0
  612. package/initramfs/lib/libc.musl-x86_64.so.1 +0 -0
  613. package/initramfs/lib/libgcc_s.so.1 +0 -0
  614. package/initramfs/lib/libstdc++.so.6 +0 -0
  615. package/initramfs/usr/bin/env +7 -0
  616. package/package.json +42 -0
  617. package/run-qemu.sh +44 -0
  618. package/scripts/fetch.sh +26 -0
  619. package/scripts/pack-initramfs.sh +41 -0
  620. package/test/build-init-bootstrap.sh +23 -0
  621. package/test/init-bootstrap.c +55 -0
@@ -0,0 +1,3334 @@
1
+ #!/usr/bin/env bun
2
+
3
+ //Object.defineProperty(process,"platform",{value:'linux'})
4
+
5
+ const fs = require("fs");
6
+ const http = require("http");
7
+ const https = require("https");
8
+ const net = require("net");
9
+ const os = require("os");
10
+ const path = require("path");
11
+ const crypto = require("crypto");
12
+ const zlib = require("zlib");
13
+ const { constants: fsConstants } = require("fs");
14
+ const WebSocket = require("ws");
15
+ const { WebSocketServer } = WebSocket;
16
+
17
+ const MSG_INPUT = "1";
18
+ const MSG_PING = "2";
19
+ const MSG_RESIZE_TERMINAL = "3";
20
+ const MSG_SET_ENCODING = "4";
21
+ const MSG_INPUT_BINARY = "8";
22
+
23
+ const MSG_OUTPUT = "1";
24
+ const MSG_PONG = "2";
25
+ const MSG_SET_WINDOW_TITLE = "3";
26
+ const MSG_SET_PREFERENCES = "4";
27
+ const MSG_SET_RECONNECT = "5";
28
+ const MSG_SET_BUFFER_SIZE = "6";
29
+ const MSG_KITTY_GRAPHICS = "7";
30
+ const MSG_WINDOWS_BRIDGE = "9";
31
+ const MSG_SET_ROLE = "a";
32
+ const MSG_CONTROL_RESULT = "b";
33
+ const MSG_ZMODEM_MODE = "z";
34
+ const MSG_ZMODEM_OUTPUT = "y";
35
+ const MSG_ZMODEM_ACK = "Y";
36
+ const MSG_KITTY_DEBUG = "K";
37
+ const KITTY_TRACE = /^(1|true|yes|on)$/i.test(String(process.env.JSGOTTY_KITTY_DEBUG || ""));
38
+ const WINDOWS_BRIDGE_TRACE = process.env.WINDOWS_BRIDGE_TRACE === "1";
39
+ const WINDOWS_BRIDGE_PREFIX = Buffer.from("@@GOTTYCTL:", "ascii");
40
+ const WINDOWS_BRIDGE_SUFFIX = Buffer.from("@@", "ascii");
41
+ const KITTY_PLACEMENT_LOG = path.resolve(__dirname, "kitty-placement.log");
42
+
43
+ function kittyPlacementLog(stage, details = {}) {
44
+ if (!KITTY_TRACE) {
45
+ return;
46
+ }
47
+ try {
48
+ fs.appendFileSync(KITTY_PLACEMENT_LOG, JSON.stringify({
49
+ time: new Date().toISOString(),
50
+ pid: process.pid,
51
+ stage,
52
+ ...details,
53
+ }) + "\n");
54
+ } catch (error) {
55
+ // Placement diagnostics must never affect terminal output.
56
+ }
57
+ }
58
+
59
+ function cursorControls(buffer) {
60
+ const text = Buffer.from(buffer).toString("utf8");
61
+ return Array.from(text.matchAll(/\u001b\[([0-9;?]*)([A-Za-z])/g), (match) => ({
62
+ params: match[1],
63
+ final: match[2],
64
+ })).filter((entry) => "ABCDEFGHfJKhl".includes(entry.final));
65
+ }
66
+
67
+ const DEFAULTS = {
68
+ address: "0.0.0.0",
69
+ port: "8080",
70
+ path: "/",
71
+ permitWrite: false,
72
+ credential: "",
73
+ randomUrl: false,
74
+ randomUrlLength: 16,
75
+ tls: false,
76
+ tlsCrt: "~/.gotty.crt",
77
+ tlsKey: "~/.gotty.key",
78
+ index: "",
79
+ titleFormat: "{{ .command }}@{{ .hostname }}",
80
+ reconnect: false,
81
+ reconnectTime: -1,
82
+ maxConnection: -1,
83
+ once: false,
84
+ timeout: -1,
85
+ permitArguments: false,
86
+ passHeaders: false,
87
+ width: 0,
88
+ height: 0,
89
+ wsOrigin: "",
90
+ wsQueryArgs: "",
91
+ webgl: false,
92
+ quiet: false,
93
+ closeSignal: "SIGINT",
94
+ closeTimeout: -1
95
+ };
96
+
97
+ const compiledHelperPromise = import("./single-exe/compiled.js").catch(() => null);
98
+ const assetsHelperPromise = import("./single-exe/assetsHelper.js").catch(() => null);
99
+
100
+ // Assets are addressed by the package-relative keys package.json declares
101
+ // ("static/index.html", "README.md"). assetsHelper turns a key into bytes,
102
+ // wherever they live: the embedded store, the compiled binary's virtual
103
+ // filesystem, or a file on disk. Nothing here builds an absolute path.
104
+ let assets = null;
105
+
106
+ // Node runs this file without the ESM-only helper. Then there is nothing
107
+ // embedded either, so a key is just a path under the source checkout.
108
+ function assetText(key) {
109
+ return assets
110
+ ? assets.readAssetTextSync(key)
111
+ : fs.readFileSync(path.join(__dirname, key), "utf8");
112
+ }
113
+
114
+ function assetFilePath(key) {
115
+ return assets ? assets.assetDiskPath(key) : path.join(__dirname, key);
116
+ }
117
+
118
+ // URL-relative path -> asset key, or null when it escapes `static/`.
119
+ // Normalizing against "/" is what makes "../../etc/passwd" unreachable.
120
+ function staticAssetKey(relativePath) {
121
+ const rel = path.posix.normalize(`/${String(relativePath ?? "")}`).slice(1);
122
+ if (!rel || rel === ".." || rel.startsWith("../")) return null;
123
+ return `static/${rel}`;
124
+ }
125
+
126
+ let cliBootstrapPromise = null;
127
+ for (const i of ["rz.js", "sz.js", "viu.mjs", "client.mjs"]) {
128
+ let subcmd = i.split(".")[0] ;
129
+ if (process.argv[2] === `--${subcmd}`) {
130
+ process.argv.splice(2, 1);
131
+ process.argv[1] = process.argv[1].replace(/gotty\.js$/, i);
132
+ cliBootstrapPromise = (async () => {
133
+
134
+ if(subcmd=="rz")
135
+ await import("./rz.js");
136
+ else if(subcmd=="sz")
137
+ await import("./sz.js");
138
+ else if(subcmd=="viu")
139
+ await import("./viu.mjs");
140
+ else if(subcmd=="client")
141
+ await import("./client.mjs");
142
+
143
+ })().catch((error) => {
144
+ console.error(String(error && error.stack ? error.stack : error));
145
+ process.exitCode = 1;
146
+ });
147
+ break;
148
+ }
149
+ }
150
+
151
+
152
+ const pkg = require('./package.json');
153
+
154
+
155
+ function printUsage() {
156
+ console.log(`GoTTY JavaScript rewrite
157
+ Linux/macOS: use Bun
158
+ Windows: use Bun/Node
159
+ Version: ${pkg.version}
160
+
161
+ Usage: bun gotty.js [options] <command> [<arguments...>]
162
+
163
+ Built-in tools:
164
+ --rz Start rz.js
165
+ --sz Start sz.js
166
+ --viu Start viu.mjs
167
+ --client Start client.mjs
168
+
169
+ Options:
170
+ -a, --address <value> IP address to listen
171
+ (default: 0.0.0.0)
172
+
173
+ -p, --port <value> Port number to listen
174
+ (default: 8080)
175
+
176
+ -w, --permit-write Permits writing to the TTY
177
+ (default: disabled)
178
+
179
+ -m, --path <value> Base path
180
+ (default: /)
181
+
182
+ -c, --credential <user:pass> Credential for Basic Authentication
183
+
184
+ --once Accept only one client and exit on disconnect
185
+ (default: disabled)
186
+
187
+ -r, --random-url Add a random string to the URL
188
+ --random-url-length <value> Random URL length
189
+ (default: 16)
190
+
191
+ -t, --tls Enable TLS/SSL
192
+ --tls-crt <value> TLS certificate file path
193
+ (default: ~/.gotty.crt)
194
+ --tls-key <value> TLS key file path
195
+ (default: ~/.gotty.key)
196
+
197
+ --index <value> Custom index.html file
198
+ (default: static/index.html)
199
+ --title-format <value> Title format
200
+ (default: {{ .command }}@{{ .hostname }} )
201
+
202
+ --reconnect, --session Enable session mode
203
+ Keep the PTY session resumable after the browser disconnects
204
+ (default: disabled)
205
+ --reconnect-time <value> Time to preserve original TTY (seconds)
206
+ (default: -1 (forever) )
207
+
208
+ --max-connection <value> Maximum concurrent clients
209
+ (default: -1 (no limit) )
210
+
211
+ --timeout <value> Timeout waiting for first/next client (seconds)
212
+ (default: -1)
213
+
214
+ --permit-arguments Permit query args from the client
215
+ ?arg=a&arg=b... from URL => appends to command argv
216
+ (default: disabled)
217
+
218
+ --pass-headers Pass HTTP request headers as environment variables
219
+ Request headers => child env: HTTP_USER_AGENT=...
220
+ (default: disabled)
221
+
222
+ --width <value> Fixed terminal width
223
+ (default: 0 (auto) )
224
+ --height <value> Fixed terminal height
225
+ (default: 0 (auto) )
226
+
227
+ --ws-origin <regex> Allowed WebSocket Origin regex
228
+ (default: disabled)
229
+ --ws-query-args <value> Query arguments appended by the browser client
230
+ Adds a fixed query string to the WebSocket URL
231
+ Format: keyA=valA&keyB=valB
232
+ (default: empty)
233
+
234
+ --webgl Enable xterm WebGL renderer
235
+ (default: disabled)
236
+
237
+ --quiet Disable logging
238
+ (default: log to stderr)
239
+
240
+ --close-signal <value> Signal sent to the child on close
241
+ (default: ${DEFAULTS.closeSignal})
242
+ --close-timeout <value> Seconds before force kill after ${DEFAULTS.closeSignal}
243
+ (default: -1 (disabled) )
244
+
245
+ --help-web Show help for WebUI frontend
246
+ --readme Show README.md and exit
247
+ --readme-en Show README.en.md and exit
248
+ --help, -h Show help for CLI
249
+
250
+ Experimental:
251
+ --build-exe Build a Bun single-file executable and exit
252
+ --build-for <target> Build a Bun single-file executable for target`);
253
+ }
254
+
255
+ function printMarkdownFile(key) {
256
+ const text = assetText(key);
257
+ const markdownAnsi =
258
+ typeof Bun !== "undefined" &&
259
+ Bun &&
260
+ Bun.markdown &&
261
+ typeof Bun.markdown.ansi === "function"
262
+ ? Bun.markdown.ansi
263
+ : null;
264
+ console.log(markdownAnsi ? markdownAnsi(text, { hyperlinks: true }) : text);
265
+ }
266
+
267
+ function fatal(message, code = 1) {
268
+ console.error(message);
269
+ process.exit(code);
270
+ }
271
+
272
+ function expandHome(filePath) {
273
+ if (!filePath || !filePath.startsWith("~")) {
274
+ return filePath;
275
+ }
276
+ return path.join(os.homedir(), filePath.slice(1));
277
+ }
278
+
279
+ function normalizeBasePath(basePath) {
280
+ let value = basePath || "/";
281
+ if (!value.startsWith("/")) {
282
+ value = `/${value}`;
283
+ }
284
+ if (!value.endsWith("/")) {
285
+ value = `${value}/`;
286
+ }
287
+ return value;
288
+ }
289
+
290
+ function forceRawReadableSocket(socket) {
291
+ if (!socket) {
292
+ return;
293
+ }
294
+
295
+ try {
296
+ if (typeof socket.setEncoding === "function") {
297
+ socket.setEncoding(null);
298
+ }
299
+ } catch (error) {
300
+ // ignore
301
+ }
302
+
303
+ if (Object.prototype.hasOwnProperty.call(socket, "_decoder")) {
304
+ delete socket._decoder;
305
+ }
306
+
307
+ const readableState = socket._readableState;
308
+ if (readableState) {
309
+ readableState.decoder = null;
310
+ readableState.encoding = null;
311
+ }
312
+ }
313
+
314
+ function forceWindowsPtyBinaryMode(proc) {
315
+ if (process.platform !== "win32" || !proc) {
316
+ return;
317
+ }
318
+
319
+ forceRawReadableSocket(proc._socket);
320
+ if (proc._agent) {
321
+ forceRawReadableSocket(proc._agent.outSocket);
322
+ }
323
+ }
324
+
325
+ function encodeWindowsBridgeMessage(message) {
326
+ return `${WINDOWS_BRIDGE_PREFIX.toString("ascii")}${Buffer.from(JSON.stringify(message), "utf8").toString("base64")}${WINDOWS_BRIDGE_SUFFIX.toString("ascii")}`;
327
+ }
328
+
329
+ function windowsBridgeSidecarPath(id) {
330
+ return path.join(os.tmpdir(), "js-gotty-bridge", `${id}.json`);
331
+ }
332
+
333
+ function decodeWindowsBridgePayload(payload) {
334
+ const decoded = JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
335
+ if (!decoded || typeof decoded !== "object" || !decoded.$f) {
336
+ return decoded;
337
+ }
338
+
339
+ const filePath = windowsBridgeSidecarPath(String(decoded.$f));
340
+ let text;
341
+ try {
342
+ text = fs.readFileSync(filePath, "utf8");
343
+ } finally {
344
+ try {
345
+ fs.unlinkSync(filePath);
346
+ } catch {}
347
+ }
348
+ return JSON.parse(text);
349
+ }
350
+
351
+ function basenameSafe(name) {
352
+ const base = path.basename(name || "");
353
+ if (!base || base === "." || base === "..") {
354
+ throw new Error(`invalid filename from peer: ${JSON.stringify(name)}`);
355
+ }
356
+ return base;
357
+ }
358
+
359
+ class WindowsBridgeParser {
360
+ constructor(label = "parser") {
361
+ this.label = label;
362
+ this.pending = Buffer.alloc(0);
363
+ }
364
+
365
+ consume(raw) {
366
+ const data = this.pending.length > 0 ? Buffer.concat([this.pending, raw]) : raw;
367
+ bridgeTrace(this.label, "consume", JSON.stringify({
368
+ rawLength: raw.length,
369
+ pendingLength: this.pending.length,
370
+ combinedLength: data.length,
371
+ prefixIndex: data.indexOf(WINDOWS_BRIDGE_PREFIX),
372
+ suffixIndex: data.indexOf(WINDOWS_BRIDGE_SUFFIX)
373
+ }), previewBuffer(data));
374
+ this.pending = Buffer.alloc(0);
375
+ const plain = [];
376
+ const controls = [];
377
+ let offset = 0;
378
+
379
+ for (;;) {
380
+ const start = data.indexOf(WINDOWS_BRIDGE_PREFIX, offset);
381
+ if (start === -1) {
382
+ break;
383
+ }
384
+
385
+ if (start > offset) {
386
+ plain.push(data.subarray(offset, start));
387
+ }
388
+
389
+ const payloadStart = start + WINDOWS_BRIDGE_PREFIX.length;
390
+ const end = data.indexOf(WINDOWS_BRIDGE_SUFFIX, payloadStart);
391
+ if (end === -1) {
392
+ this.pending = data.subarray(start);
393
+ bridgeTrace(this.label, "pending-incomplete", JSON.stringify({
394
+ start,
395
+ pendingLength: this.pending.length
396
+ }), previewBuffer(this.pending));
397
+ return {
398
+ plain: plain.length === 0 ? Buffer.alloc(0) : plain.length === 1 ? Buffer.from(plain[0]) : Buffer.concat(plain),
399
+ controls
400
+ };
401
+ }
402
+
403
+ const payload = data.subarray(payloadStart, end).toString("ascii");
404
+ try {
405
+ controls.push(decodeWindowsBridgePayload(payload));
406
+ bridgeTrace(this.label, "control", JSON.stringify({
407
+ start,
408
+ end,
409
+ controls: controls.length
410
+ }));
411
+ } catch (error) {
412
+ bridgeTrace(this.label, "decode-error", JSON.stringify({
413
+ start,
414
+ end,
415
+ message: error.message
416
+ }), previewBuffer(data.subarray(start, end + WINDOWS_BRIDGE_SUFFIX.length)));
417
+ plain.push(data.subarray(start, end + WINDOWS_BRIDGE_SUFFIX.length));
418
+ }
419
+
420
+ offset = end + WINDOWS_BRIDGE_SUFFIX.length;
421
+ }
422
+
423
+ if (offset < data.length) {
424
+ const tail = data.subarray(offset);
425
+ let partialPrefixLength = 0;
426
+ const maxPrefixLength = Math.min(tail.length, WINDOWS_BRIDGE_PREFIX.length - 1);
427
+ for (let len = maxPrefixLength; len > 0; len -= 1) {
428
+ if (tail.subarray(tail.length - len).equals(WINDOWS_BRIDGE_PREFIX.subarray(0, len))) {
429
+ partialPrefixLength = len;
430
+ break;
431
+ }
432
+ }
433
+
434
+ if (partialPrefixLength > 0) {
435
+ const flushableLength = tail.length - partialPrefixLength;
436
+ if (flushableLength > 0) {
437
+ plain.push(tail.subarray(0, flushableLength));
438
+ }
439
+ this.pending = tail.subarray(flushableLength);
440
+ bridgeTrace(this.label, "pending-partial-prefix", JSON.stringify({
441
+ partialPrefixLength,
442
+ pendingLength: this.pending.length
443
+ }), previewBuffer(this.pending));
444
+ } else {
445
+ plain.push(tail);
446
+ }
447
+ }
448
+
449
+ const plainBuffer = plain.length === 0 ? Buffer.alloc(0) : plain.length === 1 ? Buffer.from(plain[0]) : Buffer.concat(plain);
450
+ bridgeTrace(this.label, "result", JSON.stringify({
451
+ plainLength: plainBuffer.length,
452
+ controls: controls.length,
453
+ pendingLength: this.pending.length
454
+ }), plainBuffer.length > 0 ? previewBuffer(plainBuffer) : "{\"length\":0}");
455
+
456
+ return {
457
+ plain: plainBuffer,
458
+ controls
459
+ };
460
+ }
461
+ }
462
+
463
+ function randomPathSegment(length) {
464
+ const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
465
+ const bytes = crypto.randomBytes(length);
466
+ let output = "";
467
+ for (let i = 0; i < length; i += 1) {
468
+ output += alphabet[bytes[i] % alphabet.length];
469
+ }
470
+ return output;
471
+ }
472
+
473
+ function generateReconnectToken() {
474
+ return crypto.randomBytes(24).toString("base64url");
475
+ }
476
+
477
+ function formatReconnectTokenForLog(token) {
478
+ const value = String(token || "");
479
+ if (!value) {
480
+ return "-";
481
+ }
482
+ return value // .slice(0, 8);
483
+ }
484
+
485
+ function parseInteger(name, value) {
486
+ const parsed = Number.parseInt(value, 10);
487
+ if (Number.isNaN(parsed)) {
488
+ fatal(`Invalid integer for ${name}: ${value}`, 2);
489
+ }
490
+ return parsed;
491
+ }
492
+
493
+ function parseArgs(argv) {
494
+ const options = { ...DEFAULTS };
495
+ const commandArgs = [];
496
+
497
+ for (let i = 0; i < argv.length; i += 1) {
498
+ const arg = argv[i];
499
+ const next = () => {
500
+ i += 1;
501
+ if (i >= argv.length) {
502
+ fatal(`Missing value for ${arg}`, 2);
503
+ }
504
+ return argv[i];
505
+ };
506
+
507
+ switch (arg) {
508
+ case "--help":
509
+ case "-h":
510
+ printUsage();
511
+ process.exit(0);
512
+ break;
513
+ case "--help-web":
514
+ printMarkdownFile("static/help.md");
515
+ process.exit(0);
516
+ break;
517
+ case "--readme":
518
+ printMarkdownFile("README.md");
519
+ process.exit(0);
520
+ break;
521
+ case "--readme-en":
522
+ printMarkdownFile("README.en.md");
523
+ process.exit(0);
524
+ break;
525
+ case "--address":
526
+ case "-a":
527
+ options.address = next();
528
+ break;
529
+ case "--port":
530
+ case "-p":
531
+ options.port = next();
532
+ break;
533
+ case "--path":
534
+ case "-m":
535
+ options.path = next();
536
+ break;
537
+ case "--permit-write":
538
+ case "-w":
539
+ options.permitWrite = true;
540
+ break;
541
+ case "--credential":
542
+ case "-c":
543
+ options.credential = next();
544
+ break;
545
+ case "--random-url":
546
+ case "-r":
547
+ options.randomUrl = true;
548
+ break;
549
+ case "--random-url-length":
550
+ options.randomUrlLength = parseInteger(arg, next());
551
+ break;
552
+ case "--tls":
553
+ case "-t":
554
+ options.tls = true;
555
+ break;
556
+ case "--tls-crt":
557
+ options.tlsCrt = next();
558
+ break;
559
+ case "--tls-key":
560
+ options.tlsKey = next();
561
+ break;
562
+ case "--index":
563
+ options.index = next();
564
+ break;
565
+ case "--title-format":
566
+ options.titleFormat = next();
567
+ break;
568
+ case "--reconnect":
569
+ case "--session":
570
+ options.reconnect = true;
571
+ break;
572
+ case "--reconnect-time":
573
+ options.reconnectTime = parseInteger(arg, next());
574
+ break;
575
+ case "--max-connection":
576
+ options.maxConnection = parseInteger(arg, next());
577
+ break;
578
+ case "--once":
579
+ options.once = true;
580
+ break;
581
+ case "--timeout":
582
+ options.timeout = parseInteger(arg, next());
583
+ break;
584
+ case "--permit-arguments":
585
+ options.permitArguments = true;
586
+ break;
587
+ case "--pass-headers":
588
+ options.passHeaders = true;
589
+ break;
590
+ case "--width":
591
+ options.width = parseInteger(arg, next());
592
+ break;
593
+ case "--height":
594
+ options.height = parseInteger(arg, next());
595
+ break;
596
+ case "--ws-origin":
597
+ options.wsOrigin = next();
598
+ break;
599
+ case "--ws-query-args":
600
+ options.wsQueryArgs = next();
601
+ break;
602
+ case "--webgl":
603
+ options.webgl = true;
604
+ break;
605
+ case "--quiet":
606
+ options.quiet = true;
607
+ break;
608
+ case "--close-signal":
609
+ options.closeSignal = next();
610
+ break;
611
+ case "--close-timeout":
612
+ options.closeTimeout = parseInteger(arg, next());
613
+ break;
614
+ default:
615
+ if (arg.startsWith("-")) {
616
+ console.error(`Unknown option: ${arg}`);
617
+ options[arg.replace(/^-+/,'')] = true ;
618
+ }
619
+ else{
620
+ commandArgs.push(...argv.slice(i));
621
+ i = argv.length;
622
+ }
623
+ break;
624
+ }
625
+ }
626
+
627
+ if (commandArgs.length === 0) {
628
+ printUsage();
629
+ fatal("\nError: No command given.", 1);
630
+ }
631
+
632
+ return {
633
+ options,
634
+ command: commandArgs[0],
635
+ argv: commandArgs.slice(1)
636
+ };
637
+ }
638
+
639
+ function logFactory(quiet) {
640
+ return (...parts) => {
641
+ if (!quiet) {
642
+ console.error(...parts);
643
+ }
644
+ };
645
+ }
646
+
647
+ function traceLog(...parts) {
648
+ if (KITTY_TRACE) {
649
+ console.log("[kitty-trace]", ...parts);
650
+ }
651
+ }
652
+
653
+ function bridgeTrace(...parts) {
654
+ if (WINDOWS_BRIDGE_TRACE) {
655
+ console.log("[windows-bridge]", ...parts);
656
+ }
657
+ }
658
+
659
+ function previewBuffer(buffer, limit = 96) {
660
+ const chunk = buffer.subarray(0, Math.min(limit, buffer.length));
661
+ return JSON.stringify({
662
+ length: buffer.length,
663
+ ascii: chunk.toString("latin1").replace(/[^\x20-\x7e]/g, "."),
664
+ hex: chunk.toString("hex")
665
+ });
666
+ }
667
+
668
+ function contentType(filePath) {
669
+ const ext = path.extname(filePath).toLowerCase();
670
+ switch (ext) {
671
+ case ".html":
672
+ return "text/html; charset=utf-8";
673
+ case ".css":
674
+ return "text/css; charset=utf-8";
675
+ case ".js":
676
+ return "application/javascript; charset=utf-8";
677
+ case ".json":
678
+ return "application/json; charset=utf-8";
679
+ case ".svg":
680
+ return "image/svg+xml";
681
+ case ".ico":
682
+ return "image/x-icon";
683
+ case ".png":
684
+ return "image/png";
685
+ case ".map":
686
+ return "application/json; charset=utf-8";
687
+ case ".txt":
688
+ return "text/plain; charset=utf-8";
689
+ default:
690
+ return "application/octet-stream";
691
+ }
692
+ }
693
+
694
+ function renderTemplate(template, variables) {
695
+ return template.replace(/\{\{\s*\.([^}]+?)\s*\}\}/g, (_, expression) => {
696
+ const value = resolveTemplateValue(variables, expression.trim());
697
+ return value == null ? "" : String(value);
698
+ });
699
+ }
700
+
701
+ function resolveTemplateValue(variables, expression) {
702
+ const parts = expression.split(".");
703
+ let current = variables;
704
+ for (const part of parts) {
705
+ if (part === "") {
706
+ continue;
707
+ }
708
+ if (current == null || !(part in current)) {
709
+ return "";
710
+ }
711
+ current = current[part];
712
+ }
713
+ if (Array.isArray(current)) {
714
+ return current.join(" ");
715
+ }
716
+ return current;
717
+ }
718
+
719
+ function buildTitleVariables(command, argv, hostname, remoteAddr, slaveVars) {
720
+ const merged = {
721
+ command,
722
+ argv,
723
+ hostname,
724
+ remote_addr: remoteAddr,
725
+ ...slaveVars,
726
+ server: {
727
+ command,
728
+ argv,
729
+ hostname
730
+ },
731
+ master: {
732
+ remote_addr: remoteAddr
733
+ },
734
+ slave: slaveVars
735
+ };
736
+ return merged;
737
+ }
738
+
739
+ function parseInitMessage(raw) {
740
+ let parsed;
741
+ try {
742
+ parsed = JSON.parse(raw.toString("utf8"));
743
+ } catch (error) {
744
+ throw new Error("failed to authenticate websocket connection");
745
+ }
746
+
747
+ return {
748
+ Arguments: typeof parsed.Arguments === "string" ? parsed.Arguments : "",
749
+ AuthToken: typeof parsed.AuthToken === "string" ? parsed.AuthToken : "",
750
+ ReconnectToken: typeof parsed.ReconnectToken === "string"
751
+ ? parsed.ReconnectToken.trim()
752
+ : "",
753
+ ReadOnly: parsed.ReadOnly === true,
754
+ DisconnectWriter: parsed.DisconnectWriter === true,
755
+ TerminateSession: parsed.TerminateSession === true
756
+ };
757
+ }
758
+
759
+ function detectWindowsShell(command) {
760
+ const name = path.basename(String(command || "")).toLowerCase();
761
+ if (name === "powershell" || name === "powershell.exe") {
762
+ return "powershell";
763
+ }
764
+ if (name === "pwsh" || name === "pwsh.exe") {
765
+ return "pwsh";
766
+ }
767
+ if (name === "cmd" || name === "cmd.exe") {
768
+ return "cmd";
769
+ }
770
+ return "";
771
+ }
772
+
773
+ function createHeaderEnvironment(headers) {
774
+ const env = {};
775
+ for (const [key, value] of Object.entries(headers)) {
776
+ if (value == null) {
777
+ continue;
778
+ }
779
+ const normalizedKey = `HTTP_${key.toUpperCase().replace(/-/g, "_")}`;
780
+ env[normalizedKey] = Array.isArray(value) ? value.join(",") : String(value);
781
+ }
782
+ return env;
783
+ }
784
+
785
+ function basicAuthAuthorized(req, credential) {
786
+ const header = req.headers.authorization;
787
+ if (!header) {
788
+ return false;
789
+ }
790
+ const [scheme, encoded] = header.split(" ");
791
+ if (!scheme || !encoded || scheme.toLowerCase() !== "basic") {
792
+ return false;
793
+ }
794
+ let payload;
795
+ try {
796
+ payload = Buffer.from(encoded, "base64").toString("utf8");
797
+ } catch (error) {
798
+ return false;
799
+ }
800
+ return payload === credential;
801
+ }
802
+
803
+ // Match gorilla/websocket's default policy used by upstream GoTTY: non-browser
804
+ // clients without Origin are accepted, while browser connections must come
805
+ // from the same host. --ws-origin intentionally replaces this default check.
806
+ function webSocketOriginAllowed(req, originMatcher = null) {
807
+ const origin = req.headers.origin;
808
+
809
+ if (originMatcher) {
810
+ return originMatcher.test(origin || "");
811
+ }
812
+ if (!origin) {
813
+ return true;
814
+ }
815
+
816
+ try {
817
+ const originHost = new URL(origin).host;
818
+ const requestHost = req.headers.host || "";
819
+ return originHost !== "" && originHost.toLowerCase() === requestHost.toLowerCase();
820
+ } catch {
821
+ return false;
822
+ }
823
+ }
824
+
825
+ function sendBasicAuthRequired(res) {
826
+ res.writeHead(401, {
827
+ "WWW-Authenticate": 'Basic realm="GoTTY"',
828
+ "Content-Type": "text/plain; charset=utf-8"
829
+ });
830
+ res.end("authorization failed");
831
+ }
832
+
833
+ class CursorStateTracker {
834
+ constructor() {
835
+ this.row = 1;
836
+ this.col = 1;
837
+ this.cols = 80;
838
+ this._state = "text";
839
+ this._params = "";
840
+ this._wrapPending = false;
841
+ }
842
+
843
+ snapshot() {
844
+ return { row: this.row, col: this.col };
845
+ }
846
+
847
+ setDimensions(cols) {
848
+ if (Number.isFinite(cols) && cols > 0) {
849
+ this.cols = Math.max(1, Math.floor(cols));
850
+ this.col = Math.min(this.col, this.cols);
851
+ }
852
+ }
853
+
854
+ consume(buffer) {
855
+ const text = buffer.toString("utf8");
856
+ for (let i = 0; i < text.length; i += 1) {
857
+ const ch = text[i];
858
+ if (this._state === "text") {
859
+ if (ch === "\u001b") {
860
+ this._state = "esc";
861
+ continue;
862
+ }
863
+ if (ch === "\r") {
864
+ this.col = 1;
865
+ this._wrapPending = false;
866
+ continue;
867
+ }
868
+ if (ch === "\n") {
869
+ if (!this._wrapPending) {
870
+ this.row += 1;
871
+ }
872
+ this._wrapPending = false;
873
+ continue;
874
+ }
875
+ if (ch === "\b") {
876
+ this.col = Math.max(1, this.col - 1);
877
+ this._wrapPending = false;
878
+ continue;
879
+ }
880
+ if (this._wrapPending) {
881
+ this.row += 1;
882
+ this.col = 1;
883
+ this._wrapPending = false;
884
+ }
885
+ if (this.col >= this.cols) {
886
+ this._wrapPending = true;
887
+ } else {
888
+ this.col += 1;
889
+ }
890
+ continue;
891
+ }
892
+
893
+ if (this._state === "esc") {
894
+ if (ch === "[") {
895
+ this._state = "csi";
896
+ this._params = "";
897
+ continue;
898
+ }
899
+ this._state = "text";
900
+ continue;
901
+ }
902
+
903
+ if (this._state === "csi") {
904
+ if ((ch >= "0" && ch <= "9") || ch === ";" || ch === "?" || ch === ">") {
905
+ this._params += ch;
906
+ continue;
907
+ }
908
+ this._applyCsi(ch, this._params);
909
+ this._state = "text";
910
+ this._params = "";
911
+ }
912
+ }
913
+ }
914
+
915
+ _applyCsi(finalByte, params) {
916
+ const raw = params.replace(/[?>]/g, "");
917
+ const numbers = raw === "" ? [] : raw.split(";").map((value) => Number.parseInt(value || "0", 10) || 0);
918
+ switch (finalByte) {
919
+ case "A":
920
+ this.row = Math.max(1, this.row - (numbers[0] || 1));
921
+ this._wrapPending = false;
922
+ break;
923
+ case "B":
924
+ this.row = Math.max(1, this.row + (numbers[0] || 1));
925
+ this._wrapPending = false;
926
+ break;
927
+ case "C":
928
+ this.col = Math.max(1, this.col + (numbers[0] || 1));
929
+ this.col = Math.min(this.col, this.cols);
930
+ this._wrapPending = false;
931
+ break;
932
+ case "D":
933
+ this.col = Math.max(1, this.col - (numbers[0] || 1));
934
+ this._wrapPending = false;
935
+ break;
936
+ case "E":
937
+ this.row = Math.max(1, this.row + (numbers[0] || 1));
938
+ this.col = 1;
939
+ this._wrapPending = false;
940
+ break;
941
+ case "F":
942
+ this.row = Math.max(1, this.row - (numbers[0] || 1));
943
+ this.col = 1;
944
+ this._wrapPending = false;
945
+ break;
946
+ case "G":
947
+ this.col = Math.max(1, numbers[0] || 1);
948
+ this.col = Math.min(this.col, this.cols);
949
+ this._wrapPending = false;
950
+ break;
951
+ case "H":
952
+ case "f":
953
+ this.row = Math.max(1, numbers[0] || 1);
954
+ this.col = Math.max(1, numbers[1] || 1);
955
+ this.col = Math.min(this.col, this.cols);
956
+ this._wrapPending = false;
957
+ break;
958
+ default:
959
+ break;
960
+ }
961
+ }
962
+
963
+ applyKittyPlacement(control) {
964
+ if (!control || control.C === "1") {
965
+ return;
966
+ }
967
+ const cols = Math.max(0, Number.parseInt(control.c || "0", 10) || 0);
968
+ const rows = Math.max(0, Number.parseInt(control.r || "0", 10) || 0);
969
+ if (cols > 0) {
970
+ this.col += cols;
971
+ if (this.col > this.cols) {
972
+ this.row += Math.floor((this.col - 1) / this.cols);
973
+ this.col = ((this.col - 1) % this.cols) + 1;
974
+ }
975
+ }
976
+ if (rows > 0) {
977
+ this.row += rows;
978
+ }
979
+ this._wrapPending = false;
980
+ }
981
+ }
982
+
983
+ class KittyGraphicsParser {
984
+ constructor() {
985
+ this.pending = Buffer.alloc(0);
986
+ this.transfers = new Map();
987
+ this.images = new Map();
988
+ this.implicitTransferKey = null;
989
+ this.tempRoots = Array.from(new Set(
990
+ [os.tmpdir(), "/tmp", "/dev/shm", process.env.TMPDIR]
991
+ .filter(Boolean)
992
+ .map((entry) => path.resolve(entry))
993
+ ));
994
+ }
995
+
996
+ consume(raw) {
997
+ const data = this.pending.length > 0 ? Buffer.concat([this.pending, raw]) : raw;
998
+ this.pending = Buffer.alloc(0);
999
+ const events = [];
1000
+ let offset = 0;
1001
+ let plainStart = 0;
1002
+
1003
+ while (offset < data.length) {
1004
+ if (data[offset] === 0x1b && offset + 2 < data.length && data[offset + 1] === 0x5f && data[offset + 2] === 0x47) {
1005
+ const commandEnd = this._findApcEnd(data, offset + 3);
1006
+ if (commandEnd === -1) {
1007
+ if (plainStart < offset) {
1008
+ events.push({ kind: "plain", data: data.subarray(plainStart, offset) });
1009
+ }
1010
+ this.pending = data.subarray(offset);
1011
+ break;
1012
+ }
1013
+
1014
+ if (plainStart < offset) {
1015
+ events.push({ kind: "plain", data: data.subarray(plainStart, offset) });
1016
+ }
1017
+
1018
+ const packet = data.subarray(offset + 3, commandEnd);
1019
+ events.push({ kind: "kitty", packet });
1020
+
1021
+ offset = commandEnd + 2;
1022
+ plainStart = offset;
1023
+ continue;
1024
+ }
1025
+ offset += 1;
1026
+ }
1027
+
1028
+ if (plainStart < data.length && this.pending.length === 0) {
1029
+ events.push({ kind: "plain", data: data.subarray(plainStart) });
1030
+ }
1031
+
1032
+ return { events };
1033
+ }
1034
+
1035
+ parsePacket(packet, cursor) {
1036
+ return this._parsePacket(packet, cursor);
1037
+ }
1038
+
1039
+ acknowledge(graphic) {
1040
+ if (!graphic || (graphic.kind !== "placement" && graphic.kind !== "query")) {
1041
+ return null;
1042
+ }
1043
+ const control = graphic.control || {};
1044
+ if (graphic.kind === "placement" && control.q !== "1") {
1045
+ return null;
1046
+ }
1047
+ const imageId = control.i || control.I || (graphic.image && graphic.image.id);
1048
+ if (!imageId || control.q === "2") {
1049
+ return null;
1050
+ }
1051
+ const message = graphic.responseMessage || "OK";
1052
+ return `\u001b_Gi=${imageId};${message}\u001b\\`;
1053
+ }
1054
+
1055
+ _findApcEnd(buffer, start) {
1056
+ for (let i = start; i < buffer.length - 1; i += 1) {
1057
+ if (buffer[i] === 0x1b && buffer[i + 1] === 0x5c) {
1058
+ return i;
1059
+ }
1060
+ }
1061
+ return -1;
1062
+ }
1063
+
1064
+ _parsePacket(packet, cursor) {
1065
+ const separator = packet.indexOf(0x3b);
1066
+ const controlText = separator === -1 ? packet.toString("ascii") : packet.subarray(0, separator).toString("ascii");
1067
+ const payloadText = separator === -1 ? "" : packet.subarray(separator + 1).toString("ascii");
1068
+ traceLog("graphics-packet", JSON.stringify({
1069
+ control: controlText,
1070
+ payloadLength: payloadText.length,
1071
+ row: cursor.row,
1072
+ col: cursor.col
1073
+ }));
1074
+ const control = {};
1075
+ for (const part of controlText.split(",")) {
1076
+ if (!part) {
1077
+ continue;
1078
+ }
1079
+ const eq = part.indexOf("=");
1080
+ if (eq === -1) {
1081
+ control[part] = true;
1082
+ } else {
1083
+ control[part.slice(0, eq)] = part.slice(eq + 1);
1084
+ }
1085
+ }
1086
+
1087
+ const action = control.a || "t";
1088
+ if (action === "q") {
1089
+ const imageId = control.i || control.I || null;
1090
+ try {
1091
+ this._resolveImageData(control, payloadText);
1092
+ return {
1093
+ kind: "query",
1094
+ cursor,
1095
+ control,
1096
+ image: {
1097
+ id: imageId
1098
+ },
1099
+ responseMessage: "OK"
1100
+ };
1101
+ } catch (error) {
1102
+ traceLog("graphics-query-error", JSON.stringify({
1103
+ control,
1104
+ message: error.message
1105
+ }));
1106
+ return {
1107
+ kind: "query",
1108
+ cursor,
1109
+ control,
1110
+ image: {
1111
+ id: imageId
1112
+ },
1113
+ responseMessage: `ERROR:${this._sanitizeResponseMessage(error.message)}`
1114
+ };
1115
+ }
1116
+ }
1117
+
1118
+ if (action === "d") {
1119
+ const deleteScope = control.d || "a";
1120
+ const deleteImageId = control.i || control.I || null;
1121
+ if (deleteScope === "A") {
1122
+ this.images.clear();
1123
+ } else if (deleteScope === "I" && deleteImageId) {
1124
+ this.images.delete(deleteImageId);
1125
+ }
1126
+ return {
1127
+ kind: "delete",
1128
+ delete: {
1129
+ scope: deleteScope,
1130
+ imageId: deleteImageId,
1131
+ placementId: control.p || null,
1132
+ cursor
1133
+ }
1134
+ };
1135
+ }
1136
+
1137
+ if (action === "p") {
1138
+ const imageId = control.i || control.I;
1139
+ if (!imageId || !this.images.has(imageId)) {
1140
+ return null;
1141
+ }
1142
+ return {
1143
+ kind: "placement",
1144
+ cursor,
1145
+ control,
1146
+ image: this.images.get(imageId)
1147
+ };
1148
+ }
1149
+
1150
+ if (action !== "T" && action !== "t") {
1151
+ traceLog("graphics-ignored-action", JSON.stringify({
1152
+ action,
1153
+ control
1154
+ }));
1155
+ return null;
1156
+ }
1157
+
1158
+ const key = this._resolveTransferKey(control, action);
1159
+ if (!key) {
1160
+ traceLog("graphics-transfer-error", JSON.stringify({
1161
+ control,
1162
+ message: "missing transfer key"
1163
+ }));
1164
+ return null;
1165
+ }
1166
+ const imageId = key.slice(0, key.lastIndexOf(":"));
1167
+ const existing = this.transfers.get(key) || {
1168
+ control: { ...control },
1169
+ chunks: []
1170
+ };
1171
+ existing.control = { ...existing.control, ...control };
1172
+ existing.chunks.push(payloadText);
1173
+
1174
+ if (control.m === "1") {
1175
+ this.transfers.set(key, existing);
1176
+ if (!control.i && !control.I) {
1177
+ this.implicitTransferKey = key;
1178
+ }
1179
+ return null;
1180
+ }
1181
+
1182
+ this.transfers.delete(key);
1183
+ if (this.implicitTransferKey === key) {
1184
+ this.implicitTransferKey = null;
1185
+ }
1186
+ let binary;
1187
+ try {
1188
+ binary = this._resolveImageData(existing.control, existing.chunks.join(""));
1189
+ } catch (error) {
1190
+ traceLog("graphics-transfer-error", JSON.stringify({
1191
+ control: existing.control,
1192
+ message: error.message
1193
+ }));
1194
+ return null;
1195
+ }
1196
+
1197
+ const image = {
1198
+ id: imageId,
1199
+ format: Number.parseInt(existing.control.f || "32", 10),
1200
+ width: Number.parseInt(existing.control.s || "0", 10),
1201
+ height: Number.parseInt(existing.control.v || "0", 10),
1202
+ mime: existing.control.U || existing.control.mime || null,
1203
+ data: binary.toString("base64")
1204
+ };
1205
+
1206
+ this.images.set(imageId, image);
1207
+
1208
+ return {
1209
+ kind: "placement",
1210
+ cursor,
1211
+ control: existing.control,
1212
+ image
1213
+ };
1214
+ }
1215
+
1216
+ _resolveTransferKey(control, action) {
1217
+ const explicitImageId = control.i || control.I;
1218
+ if (explicitImageId) {
1219
+ return `${explicitImageId}:${control.p || "0"}`;
1220
+ }
1221
+ if (this.implicitTransferKey && (action === "T" || action === "t")) {
1222
+ return this.implicitTransferKey;
1223
+ }
1224
+ return `anon:${Date.now()}:${Math.random()}:${control.p || "0"}`;
1225
+ }
1226
+
1227
+ _resolveImageData(control, payloadText) {
1228
+ const transmission = control.t || "d";
1229
+ if (transmission === "d") {
1230
+ return this._inflateIfNeeded(control, Buffer.from(payloadText, "base64"));
1231
+ }
1232
+ if (transmission === "f" || transmission === "t") {
1233
+ return this._inflateIfNeeded(control, this._readImageFile(control, payloadText, transmission === "t"));
1234
+ }
1235
+ throw new Error(`unsupported transmission ${transmission}`);
1236
+ }
1237
+
1238
+ _inflateIfNeeded(control, binary) {
1239
+ if (control.o === "z") {
1240
+ return zlib.inflateSync(binary);
1241
+ }
1242
+ return binary;
1243
+ }
1244
+
1245
+ _readImageFile(control, payloadText, deleteAfterRead) {
1246
+ const targetPath = Buffer.from(payloadText, "base64").toString("utf8");
1247
+ if (!targetPath) {
1248
+ throw new Error("empty file path");
1249
+ }
1250
+
1251
+ let resolvedPath;
1252
+ try {
1253
+ resolvedPath = fs.realpathSync(targetPath);
1254
+ } catch (error) {
1255
+ throw new Error(`cannot resolve file path: ${error.message}`);
1256
+ }
1257
+
1258
+ let stat;
1259
+ try {
1260
+ stat = fs.statSync(resolvedPath);
1261
+ } catch (error) {
1262
+ throw new Error(`cannot stat file: ${error.message}`);
1263
+ }
1264
+ if (!stat.isFile()) {
1265
+ throw new Error("path is not a regular file");
1266
+ }
1267
+
1268
+ const offset = Math.max(0, Number.parseInt(control.O || "0", 10) || 0);
1269
+ const requestedSize = Math.max(0, Number.parseInt(control.S || "0", 10) || 0);
1270
+ const size = requestedSize > 0 ? requestedSize : Math.max(0, stat.size - offset);
1271
+ const fd = fs.openSync(resolvedPath, fsConstants.O_RDONLY);
1272
+
1273
+ try {
1274
+ const buffer = Buffer.alloc(size);
1275
+ const bytesRead = fs.readSync(fd, buffer, 0, size, offset);
1276
+ return buffer.subarray(0, bytesRead);
1277
+ } finally {
1278
+ fs.closeSync(fd);
1279
+ if (deleteAfterRead && this._isSafeTempGraphicsPath(resolvedPath)) {
1280
+ try {
1281
+ fs.unlinkSync(resolvedPath);
1282
+ } catch (error) {
1283
+ traceLog("graphics-tempfile-unlink-failed", JSON.stringify({
1284
+ path: resolvedPath,
1285
+ message: error.message
1286
+ }));
1287
+ }
1288
+ }
1289
+ }
1290
+ }
1291
+
1292
+ _isSafeTempGraphicsPath(filePath) {
1293
+ if (!filePath.includes("tty-graphics-protocol")) {
1294
+ return false;
1295
+ }
1296
+ return this.tempRoots.some((root) => filePath === root || filePath.startsWith(`${root}${path.sep}`));
1297
+ }
1298
+
1299
+ _sanitizeResponseMessage(message) {
1300
+ return String(message || "ERROR")
1301
+ .replace(/[^\x20-\x7e]/g, " ")
1302
+ .trim()
1303
+ .slice(0, 200) || "ERROR";
1304
+ }
1305
+ }
1306
+
1307
+ class Counter {
1308
+ constructor(timeoutSeconds, onTimeout) {
1309
+ this.timeoutMs = timeoutSeconds > 0 ? timeoutSeconds * 1000 : 0;
1310
+ this.onTimeout = onTimeout;
1311
+ this.connections = 0;
1312
+ this.timer = null;
1313
+ if (this.timeoutMs > 0) {
1314
+ this.arm();
1315
+ }
1316
+ }
1317
+
1318
+ arm() {
1319
+ this.disarm();
1320
+ this.timer = setTimeout(() => this.onTimeout(), this.timeoutMs);
1321
+ }
1322
+
1323
+ disarm() {
1324
+ if (this.timer) {
1325
+ clearTimeout(this.timer);
1326
+ this.timer = null;
1327
+ }
1328
+ }
1329
+
1330
+ add() {
1331
+ this.disarm();
1332
+ this.connections += 1;
1333
+ return this.connections;
1334
+ }
1335
+
1336
+ done() {
1337
+ this.connections = Math.max(0, this.connections - 1);
1338
+ if (this.connections === 0 && this.timeoutMs > 0) {
1339
+ this.arm();
1340
+ }
1341
+ return this.connections;
1342
+ }
1343
+
1344
+ count() {
1345
+ return this.connections;
1346
+ }
1347
+ }
1348
+
1349
+ class NodePtyBackend {
1350
+ constructor(options) {
1351
+ let nodePty;
1352
+ try {
1353
+ // Split so the bundler cannot resolve it. This branch is unreachable
1354
+ // in a compiled binary (Bun >= 1.3.14 always takes BunPtyBackend), so
1355
+ // a static require would only bake ~54 KB of dead JS into the exe —
1356
+ // and node-pty's own `.node` loading is dynamic anyway, so it would
1357
+ // still need the files on disk.
1358
+ let preventSingleExe="./lib/"
1359
+ nodePty = require(preventSingleExe+"node-pty");
1360
+ } catch (error) {
1361
+ const detail = error && error.message ? `: ${error.message}` : "";
1362
+ throw new Error(
1363
+ globalThis.Bun
1364
+ ? "This build falls back to node-pty because Bun " + globalThis.Bun.version
1365
+ + " is older than 1.3.14, which is where the built-in ConPTY backend landed. "
1366
+ + "A compiled binary cannot load node-pty — it is deliberately left out of the "
1367
+ + "bundle — so rebuild with Bun 1.3.14 or newer, or run from a source checkout "
1368
+ + "with node-pty installed" + detail
1369
+ : "node-pty is required to run under Node on Windows, and it could not be loaded "
1370
+ + "from ./lib/node-pty. Run with Bun 1.3.14 or newer to use the built-in ConPTY "
1371
+ + "backend instead" + detail,
1372
+ );
1373
+ }
1374
+
1375
+ const system32 = path.join(process.env.windir, 'System32');
1376
+
1377
+ let ocmd = options.command
1378
+
1379
+ if(ocmd=='cmd' || ocmd=="powershell")
1380
+ ocmd+=".exe";
1381
+ else if(!fs.existsSync(ocmd) &&
1382
+ fs.existsSync(ocmd+'.exe'))
1383
+ ocmd+='.exe';
1384
+ else if(!fs.existsSync(path.join(system32,ocmd)) &&
1385
+ fs.existsSync(path.join(system32,ocmd+'.exe')))
1386
+ ocmd = path.join(system32,ocmd+'.exe');
1387
+
1388
+ options.command = ocmd ;
1389
+
1390
+ this.options = options;
1391
+ this.closed = false;
1392
+ this.exitHandlers = [];
1393
+ this.dataHandlers = [];
1394
+ this.closeTimer = null;
1395
+
1396
+ const env = {
1397
+ ...process.env,
1398
+ TERM: "xterm-256color",
1399
+ ...(options.headerEnv || {})
1400
+ };
1401
+
1402
+ this.proc = nodePty.spawn(options.command, options.argv, {
1403
+ name: "xterm-256color",
1404
+ cols: options.width || 80,
1405
+ rows: options.height || 24,
1406
+ cwd: process.cwd(),
1407
+ env
1408
+ });
1409
+
1410
+ forceWindowsPtyBinaryMode(this.proc);
1411
+
1412
+ this.pid = this.proc.pid;
1413
+ this.proc.onData((data) => {
1414
+ const chunk = Buffer.isBuffer(data) ? Buffer.from(data) : Buffer.from(data, "latin1");
1415
+ for (const handler of this.dataHandlers) {
1416
+ handler(chunk);
1417
+ }
1418
+ });
1419
+ this.proc.onExit(() => {
1420
+ this.emitExit();
1421
+ });
1422
+
1423
+ this.bootstrapWindowsUtf8();
1424
+ }
1425
+
1426
+ onData(handler) {
1427
+ this.dataHandlers.push(handler);
1428
+ }
1429
+
1430
+ onExit(handler) {
1431
+ this.exitHandlers.push(handler);
1432
+ }
1433
+
1434
+ emitExit() {
1435
+ if (this.closed) {
1436
+ return;
1437
+ }
1438
+ this.closed = true;
1439
+ for (const handler of this.exitHandlers) {
1440
+ handler();
1441
+ }
1442
+ }
1443
+
1444
+ write(buffer) {
1445
+ this.proc.write(buffer.toString("utf8"));
1446
+ }
1447
+
1448
+ writeBinary(buffer) {
1449
+ this.proc.write(buffer);
1450
+ }
1451
+
1452
+ resize(columns, rows) {
1453
+ this.proc.resize(columns, rows);
1454
+ }
1455
+
1456
+ kill(signal) {
1457
+ try {
1458
+ this.proc.kill(signal);
1459
+ } catch (error) {
1460
+ // ignore
1461
+ }
1462
+ }
1463
+
1464
+ close() {
1465
+ if (this.closeTimer) {
1466
+ clearTimeout(this.closeTimer);
1467
+ this.closeTimer = null;
1468
+ }
1469
+
1470
+ if (process.platform === "win32") {
1471
+ this.kill();
1472
+ return;
1473
+ }
1474
+
1475
+ this.kill(this.options.closeSignal);
1476
+ if (this.options.closeTimeout >= 0) {
1477
+ this.closeTimer = setTimeout(() => {
1478
+ this.kill("SIGKILL");
1479
+ }, this.options.closeTimeout * 1000);
1480
+ }
1481
+ }
1482
+
1483
+ bootstrapWindowsUtf8() {
1484
+ if (process.platform !== "win32") {
1485
+ return;
1486
+ }
1487
+
1488
+ const shell = detectWindowsShell(this.options.command);
1489
+ if (shell === "powershell" || shell === "pwsh") {
1490
+ setTimeout(() => {
1491
+ try {
1492
+ this.proc.write("[Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)\r");
1493
+ this.proc.write("$OutputEncoding = [System.Text.UTF8Encoding]::new($false)\r");
1494
+ this.proc.write("chcp 65001 > $null\r");
1495
+ } catch (error) {
1496
+ // ignore
1497
+ }
1498
+ }, 30);
1499
+ return;
1500
+ }
1501
+
1502
+ if (shell === "cmd") {
1503
+ setTimeout(() => {
1504
+ try {
1505
+ this.proc.write("chcp 65001>nul\r");
1506
+ } catch (error) {
1507
+ // ignore
1508
+ }
1509
+ }, 30);
1510
+ }
1511
+ }
1512
+ }
1513
+
1514
+ class BunPtyBackend {
1515
+ constructor(options) {
1516
+ if (typeof Bun === "undefined" || typeof Bun.spawn !== "function") {
1517
+ throw new Error("Bun runtime is required on Linux/macOS for PTY support");
1518
+ }
1519
+
1520
+ this.options = options;
1521
+ this.closed = false;
1522
+ this.exitHandlers = [];
1523
+ this.dataHandlers = [];
1524
+ this.closeTimer = null;
1525
+
1526
+ const env = {
1527
+ ...process.env,
1528
+ TERM: "xterm-256color",
1529
+ ...(options.headerEnv || {})
1530
+ };
1531
+
1532
+ this.proc = Bun.spawn([options.command, ...options.argv], {
1533
+ cwd: process.cwd(),
1534
+ env,
1535
+ terminal: {
1536
+ cols: options.width || 80,
1537
+ rows: options.height || 24,
1538
+ name: "xterm-256color",
1539
+ data: (_terminal, data) => {
1540
+ const chunk = typeof data === "string" ? Buffer.from(data, "utf8") : Buffer.from(data);
1541
+ for (const handler of this.dataHandlers) {
1542
+ handler(chunk);
1543
+ }
1544
+ },
1545
+ exit: () => {
1546
+ this.emitExit();
1547
+ }
1548
+ }
1549
+ });
1550
+
1551
+ this.terminal = this.proc.terminal;
1552
+ this.pid = this.proc.pid;
1553
+
1554
+ Promise.resolve(this.proc.exited)
1555
+ .catch(() => {})
1556
+ .finally(() => {
1557
+ this.emitExit();
1558
+ });
1559
+ }
1560
+
1561
+ onData(handler) {
1562
+ this.dataHandlers.push(handler);
1563
+ }
1564
+
1565
+ onExit(handler) {
1566
+ this.exitHandlers.push(handler);
1567
+ }
1568
+
1569
+ emitExit() {
1570
+ if (this.closed) {
1571
+ return;
1572
+ }
1573
+ this.closed = true;
1574
+ for (const handler of this.exitHandlers) {
1575
+ handler();
1576
+ }
1577
+ }
1578
+
1579
+ write(buffer) {
1580
+ this.terminal.write(buffer);
1581
+ }
1582
+
1583
+ writeBinary(buffer) {
1584
+ this.terminal.write(buffer);
1585
+ }
1586
+
1587
+ resize(columns, rows) {
1588
+ this.terminal.resize(columns, rows);
1589
+ }
1590
+
1591
+ kill(signal) {
1592
+ try {
1593
+ if (typeof this.proc.kill === "function") {
1594
+ this.proc.kill(signal);
1595
+ } else {
1596
+ this.terminal.close();
1597
+ }
1598
+ } catch (error) {
1599
+ try {
1600
+ this.terminal.close();
1601
+ } catch (closeError) {
1602
+ // ignore
1603
+ }
1604
+ }
1605
+ }
1606
+
1607
+ close() {
1608
+ if (this.closeTimer) {
1609
+ clearTimeout(this.closeTimer);
1610
+ this.closeTimer = null;
1611
+ }
1612
+
1613
+ this.kill(this.options.closeSignal);
1614
+ if (this.options.closeTimeout >= 0) {
1615
+ this.closeTimer = setTimeout(() => {
1616
+ this.kill("SIGKILL");
1617
+ }, this.options.closeTimeout * 1000);
1618
+ }
1619
+ }
1620
+ }
1621
+
1622
+ function createPtyBackend(options) {
1623
+ if (process.platform === "win32") {
1624
+ if (globalThis.Bun?.semver?.satisfies?.(
1625
+ globalThis.Bun?.version, ">=1.3.14"
1626
+ )) {
1627
+ return new BunPtyBackend(options);
1628
+ }
1629
+ return new NodePtyBackend(options);
1630
+ }
1631
+
1632
+ return new BunPtyBackend(options);
1633
+ }
1634
+
1635
+ class PtySession {
1636
+ constructor(options) {
1637
+ this.options = options;
1638
+ this.clients = new Map();
1639
+ this.writerWs = null;
1640
+ this.log = options.log;
1641
+ this.permitWrite = options.permitWrite;
1642
+ this.columns = options.width || 0;
1643
+ this.rows = options.height || 0;
1644
+ this.bufferSize = 64 * 1024;
1645
+ this.closed = false;
1646
+ this.backendExited = false;
1647
+ this.backendExitPromise = new Promise((resolve) => {
1648
+ this.resolveBackendExit = resolve;
1649
+ });
1650
+ this.backendTerminationPromise = null;
1651
+ this.reconnectTimer = null;
1652
+ this.reconnectToken = options.reconnect ? generateReconnectToken() : "";
1653
+ this.backend = createPtyBackend(options);
1654
+ this.supportsWindowsBridge = process.platform === "win32";
1655
+ this.cursorTracker = new CursorStateTracker();
1656
+ this.cursorTracker.setDimensions(this.columns || 80);
1657
+ this.kittyParser = new KittyGraphicsParser();
1658
+ this.windowsBridgeParser = this.supportsWindowsBridge ? new WindowsBridgeParser("incoming") : null;
1659
+ this.outgoingWindowsBridgeParser = this.supportsWindowsBridge ? new WindowsBridgeParser("outgoing") : null;
1660
+ this.windowsUploadRequests = new Map();
1661
+ this.windowsBridgeSeen = new Set();
1662
+ this.windowsBridgeSeenOrder = [];
1663
+ this.pendingEchoBytes = Buffer.alloc(0);
1664
+ this.zmodemBinaryBypass = false;
1665
+ this.zmodemDetectTail = Buffer.alloc(0);
1666
+ this.zmodemBinaryBytesSent = 0;
1667
+ this.zmodemBinaryBytesLogged = 0;
1668
+ this.zmodemOutputQueue = [];
1669
+ this.zmodemOutputInFlight = new Map();
1670
+ this.zmodemOutputSeq = 0;
1671
+ this.zmodemOutputStreamId = 0;
1672
+ this.zmodemOutputAckedSeq = -1;
1673
+ this.zmodemOutputWindow = 8;
1674
+ this.zmodemRetransmitTimer = null;
1675
+
1676
+ this.slaveVars = {
1677
+ command: options.command,
1678
+ argv: options.argv,
1679
+ pid: this.backend.pid
1680
+ };
1681
+ }
1682
+
1683
+ start() {
1684
+ this.backend.onData((raw) => {
1685
+ if (this.closed) {
1686
+ return;
1687
+ }
1688
+ if (this.shouldBypassOutputParsers(raw)) {
1689
+ this.sendChunk(MSG_OUTPUT, raw);
1690
+ return;
1691
+ }
1692
+ const parsed = this.kittyParser.consume(raw);
1693
+ for (const event of parsed.events) {
1694
+ if (event.kind === "kitty") {
1695
+ const cursorAtApc = this.cursorTracker.snapshot();
1696
+ const graphic = this.kittyParser.parsePacket(event.packet, cursorAtApc);
1697
+ if (!graphic) {
1698
+ continue;
1699
+ }
1700
+ kittyPlacementLog("server-kitty-event", {
1701
+ cursorAtApc,
1702
+ kind: graphic.kind,
1703
+ control: graphic.control || null,
1704
+ delete: graphic.delete || null,
1705
+ imageId: graphic.image?.id || null,
1706
+ });
1707
+ traceLog("graphics", JSON.stringify({
1708
+ kind: graphic.kind,
1709
+ control: graphic.control || null,
1710
+ imageId: graphic.image ? graphic.image.id : null
1711
+ }));
1712
+ if (graphic.kind === "placement") {
1713
+ this.cursorTracker.applyKittyPlacement(graphic.control);
1714
+ }
1715
+ this.send(MSG_KITTY_GRAPHICS, JSON.stringify(graphic));
1716
+ const syntheticCursorMotion = this.buildKittyCursorMotion(graphic);
1717
+ if (syntheticCursorMotion) {
1718
+ this.sendChunk(MSG_OUTPUT, Buffer.from(syntheticCursorMotion, "utf8"));
1719
+ }
1720
+ const ack = this.kittyParser.acknowledge(graphic);
1721
+ if (ack) {
1722
+ traceLog("graphics-ack", JSON.stringify(graphic.control || {}), JSON.stringify(ack));
1723
+ this.backend.write(Buffer.from(ack, "utf8"));
1724
+ }
1725
+ continue;
1726
+ }
1727
+
1728
+ const chunk = event.data;
1729
+ const filteredChunk = this.filterEchoedTerminalReplies(chunk);
1730
+ if (filteredChunk.length === 0) {
1731
+ continue;
1732
+ }
1733
+ if (!this.supportsWindowsBridge) {
1734
+ const cursorBefore = this.cursorTracker.snapshot();
1735
+ this.cursorTracker.consume(filteredChunk);
1736
+ const cursorAfter = this.cursorTracker.snapshot();
1737
+ const controls = cursorControls(filteredChunk);
1738
+ if (controls.length > 0) {
1739
+ kittyPlacementLog("server-plain-cursor-controls", {
1740
+ bytes: filteredChunk.length,
1741
+ cursorBefore,
1742
+ cursorAfter,
1743
+ controls,
1744
+ });
1745
+ }
1746
+ this.sendChunk(MSG_OUTPUT, filteredChunk);
1747
+ continue;
1748
+ }
1749
+ const bridged = this.windowsBridgeParser.consume(filteredChunk);
1750
+ if (bridged.plain.length > 0) {
1751
+ const cursorBefore = this.cursorTracker.snapshot();
1752
+ this.cursorTracker.consume(bridged.plain);
1753
+ const cursorAfter = this.cursorTracker.snapshot();
1754
+ const controls = cursorControls(bridged.plain);
1755
+ if (controls.length > 0) {
1756
+ kittyPlacementLog("server-plain-cursor-controls", {
1757
+ bytes: bridged.plain.length,
1758
+ cursorBefore,
1759
+ cursorAfter,
1760
+ controls,
1761
+ windowsBridge: true,
1762
+ });
1763
+ }
1764
+ this.sendChunk(MSG_OUTPUT, bridged.plain);
1765
+ }
1766
+ for (const control of bridged.controls) {
1767
+ this.handleWindowsBridgeControl(control);
1768
+ }
1769
+ }
1770
+ });
1771
+
1772
+ this.backend.onExit(() => {
1773
+ this.backendExited = true;
1774
+ this.resolveBackendExit();
1775
+ this.terminate(1000, "process exited", { skipBackendClose: true });
1776
+ });
1777
+
1778
+ this.attachWebSocket(
1779
+ this.options.ws,
1780
+ this.options.remoteAddr || "",
1781
+ this.options.readOnly === true
1782
+ );
1783
+ }
1784
+
1785
+ shouldBypassOutputParsers(raw) {
1786
+ if (this.zmodemBinaryBypass) {
1787
+ return true;
1788
+ }
1789
+
1790
+ const data = this.zmodemDetectTail.length > 0
1791
+ ? Buffer.concat([this.zmodemDetectTail, raw])
1792
+ : raw;
1793
+ this.zmodemDetectTail = data.subarray(Math.max(0, data.length - 8));
1794
+
1795
+ const binaryHeaderA = Buffer.from([0x2a, 0x18, 0x41]);
1796
+ const binaryHeaderC = Buffer.from([0x2a, 0x18, 0x43]);
1797
+ const hexHeader = Buffer.from([0x2a, 0x2a, 0x18, 0x42]);
1798
+ if (
1799
+ data.indexOf(hexHeader) !== -1 ||
1800
+ data.indexOf(binaryHeaderA) !== -1 ||
1801
+ data.indexOf(binaryHeaderC) !== -1
1802
+ ) {
1803
+ this.zmodemBinaryBypass = true;
1804
+ this.resetZmodemOutputFlow();
1805
+ this.log("ZMODEM detected: bypassing server-side output parsers for this session");
1806
+ return true;
1807
+ }
1808
+
1809
+ return false;
1810
+ }
1811
+
1812
+ attachWebSocket(ws, remoteAddr = "", readOnly = false) {
1813
+ if (!ws || this.closed) {
1814
+ return false;
1815
+ }
1816
+
1817
+ if (this.reconnectTimer) {
1818
+ clearTimeout(this.reconnectTimer);
1819
+ this.reconnectTimer = null;
1820
+ }
1821
+
1822
+ this.clients.set(ws, {
1823
+ remoteAddr,
1824
+ encoding: "null",
1825
+ readOnly,
1826
+ sendQueue: Promise.resolve()
1827
+ });
1828
+ if (this.permitWrite && !readOnly && !this.writerWs) {
1829
+ this.writerWs = ws;
1830
+ }
1831
+ this.sendSessionMetadata(ws, remoteAddr);
1832
+ this.startWebSocketHandlers(ws);
1833
+ this.notifyRoles();
1834
+ return true;
1835
+ }
1836
+
1837
+ canResume(token) {
1838
+ return Boolean(
1839
+ token &&
1840
+ this.options.reconnect &&
1841
+ !this.closed &&
1842
+ !this.backendExited &&
1843
+ token === this.reconnectToken
1844
+ );
1845
+ }
1846
+
1847
+ sendSessionMetadata(ws, remoteAddr = "") {
1848
+ const title = renderTemplate(
1849
+ this.options.titleFormat,
1850
+ buildTitleVariables(
1851
+ this.options.command,
1852
+ this.options.argv,
1853
+ this.options.hostname,
1854
+ remoteAddr,
1855
+ this.slaveVars
1856
+ )
1857
+ );
1858
+
1859
+ this.send(MSG_SET_WINDOW_TITLE, title, ws);
1860
+ this.send(MSG_SET_BUFFER_SIZE, JSON.stringify(this.bufferSize), ws);
1861
+
1862
+ if (this.options.reconnect) {
1863
+ this.send(MSG_SET_RECONNECT, JSON.stringify({
1864
+ time: this.options.reconnectTime,
1865
+ token: this.reconnectToken,
1866
+ pid: this.backend.pid
1867
+ }), ws);
1868
+ }
1869
+ }
1870
+
1871
+ isWriter(ws) {
1872
+ return this.writerSocket() === ws;
1873
+ }
1874
+
1875
+ writerSocket() {
1876
+ return this.writerWs && this.clients.has(this.writerWs)
1877
+ ? this.writerWs
1878
+ : null;
1879
+ }
1880
+
1881
+ notifyRoles() {
1882
+ for (const ws of this.clients.keys()) {
1883
+ const writable = this.isWriter(ws);
1884
+ this.send(MSG_SET_ROLE, JSON.stringify({
1885
+ role: writable ? "writer" : "viewer",
1886
+ writable
1887
+ }), ws);
1888
+ }
1889
+ }
1890
+
1891
+ armReconnectTimeout() {
1892
+ if (!this.options.reconnect || this.closed || this.backendExited) {
1893
+ this.terminate();
1894
+ return;
1895
+ }
1896
+
1897
+ if (this.options.reconnectTime < 0) {
1898
+ return;
1899
+ }
1900
+
1901
+ const timeoutMs = this.options.reconnectTime * 1000;
1902
+ if (timeoutMs === 0) {
1903
+ this.terminate(1001, "reconnect timeout");
1904
+ return;
1905
+ }
1906
+
1907
+ this.reconnectTimer = setTimeout(() => {
1908
+ this.reconnectTimer = null;
1909
+ this.terminate(1001, "reconnect timeout");
1910
+ }, timeoutMs);
1911
+ }
1912
+
1913
+ handleWindowsBridgeControl(control) {
1914
+ if (!control || typeof control !== "object") {
1915
+ return;
1916
+ }
1917
+
1918
+ if (this.shouldSkipWindowsBridgeControl(control)) {
1919
+ return;
1920
+ }
1921
+
1922
+ if (control.op === "image_file") {
1923
+ this.handleWindowsImageFile(control);
1924
+ return;
1925
+ }
1926
+
1927
+ if (control.op === "upload_request") {
1928
+ const requestId = String(control.requestId || "");
1929
+ if (this.windowsUploadRequests.has(requestId)) {
1930
+ traceLog("windows-upload-duplicate", JSON.stringify({ requestId }));
1931
+ return;
1932
+ }
1933
+ this.windowsUploadRequests.set(requestId, {
1934
+ requestId,
1935
+ targetDir: String(control.targetDir || ""),
1936
+ files: new Map()
1937
+ });
1938
+ this.send(MSG_WINDOWS_BRIDGE, JSON.stringify(control));
1939
+ return;
1940
+ }
1941
+
1942
+ if (control.op === "download_request") {
1943
+ void this.handleWindowsDownloadRequest(control);
1944
+ return;
1945
+ }
1946
+
1947
+ this.send(MSG_WINDOWS_BRIDGE, JSON.stringify(control));
1948
+ }
1949
+
1950
+ shouldSkipWindowsBridgeControl(control) {
1951
+ const key = this.getWindowsBridgeControlKey(control);
1952
+ if (!key) {
1953
+ return false;
1954
+ }
1955
+
1956
+ if (this.windowsBridgeSeen.has(key)) {
1957
+ traceLog("windows-bridge-duplicate", JSON.stringify({
1958
+ key,
1959
+ op: String(control.op || "")
1960
+ }));
1961
+ return true;
1962
+ }
1963
+
1964
+ this.windowsBridgeSeen.add(key);
1965
+ this.windowsBridgeSeenOrder.push(key);
1966
+ while (this.windowsBridgeSeenOrder.length > 2048) {
1967
+ const oldest = this.windowsBridgeSeenOrder.shift();
1968
+ if (oldest) {
1969
+ this.windowsBridgeSeen.delete(oldest);
1970
+ }
1971
+ }
1972
+ return false;
1973
+ }
1974
+
1975
+ getWindowsBridgeControlKey(control) {
1976
+ const op = String(control.op || "");
1977
+ if (!op) {
1978
+ return "";
1979
+ }
1980
+
1981
+ if (op === "upload_request" || op === "download_request") {
1982
+ const requestId = String(control.requestId || "");
1983
+ return requestId ? `${op}:${requestId}` : "";
1984
+ }
1985
+
1986
+ if (op === "image_file") {
1987
+ const imageId = String(control.id || "");
1988
+ const targetPath = String(control.path || "");
1989
+ if (imageId) {
1990
+ return `${op}:${imageId}`;
1991
+ }
1992
+ if (targetPath) {
1993
+ return `${op}:${targetPath}`;
1994
+ }
1995
+ return "";
1996
+ }
1997
+
1998
+ return "";
1999
+ }
2000
+
2001
+ handleWindowsImageFile(control) {
2002
+ const targetPath = String(control.path || "");
2003
+ if (!targetPath) {
2004
+ return;
2005
+ }
2006
+
2007
+ let binary;
2008
+ try {
2009
+ binary = fs.readFileSync(targetPath);
2010
+ } catch (error) {
2011
+ traceLog("windows-image-file-error", JSON.stringify({
2012
+ path: targetPath,
2013
+ message: error.message
2014
+ }));
2015
+ return;
2016
+ } finally {
2017
+ if (control.deleteAfterRead) {
2018
+ try {
2019
+ fs.unlinkSync(targetPath);
2020
+ } catch {}
2021
+ }
2022
+ }
2023
+
2024
+ this.emitWindowsImageGraphic({
2025
+ id: String(control.id || Date.now()),
2026
+ cols: control.cols,
2027
+ rows: control.rows,
2028
+ width: control.width,
2029
+ height: control.height,
2030
+ mime: control.mime,
2031
+ data: binary.toString("base64")
2032
+ });
2033
+ }
2034
+
2035
+ emitWindowsImageGraphic(image) {
2036
+ const imageId = String(image.id || Date.now());
2037
+ const cols = Math.max(1, Number.parseInt(String(image.cols || 0), 10) || 1);
2038
+ const rows = Math.max(1, Number.parseInt(String(image.rows || 0), 10) || 1);
2039
+ const width = Math.max(1, Number.parseInt(String(image.width || 0), 10) || 1);
2040
+ const height = Math.max(1, Number.parseInt(String(image.height || 0), 10) || 1);
2041
+ const graphic = {
2042
+ kind: "placement",
2043
+ cursor: this.cursorTracker.snapshot(),
2044
+ control: {
2045
+ i: imageId,
2046
+ c: String(cols),
2047
+ r: String(rows),
2048
+ U: image.mime || "image/png",
2049
+ q: "2"
2050
+ },
2051
+ image: {
2052
+ id: imageId,
2053
+ format: 100,
2054
+ width,
2055
+ height,
2056
+ data: String(image.data || "")
2057
+ }
2058
+ };
2059
+ this.send(MSG_KITTY_GRAPHICS, JSON.stringify(graphic));
2060
+ const syntheticCursorMotion = this.buildKittyCursorMotion(graphic);
2061
+ if (syntheticCursorMotion) {
2062
+ this.sendChunk(MSG_OUTPUT, Buffer.from(syntheticCursorMotion, "utf8"));
2063
+ }
2064
+ }
2065
+
2066
+ async handleWindowsDownloadRequest(control) {
2067
+ const requestId = String(control.requestId || "");
2068
+ const files = Array.isArray(control.files) ? control.files : [];
2069
+
2070
+ try {
2071
+ for (const file of files) {
2072
+ const fullPath = String(file.fullPath || "");
2073
+ const name = basenameSafe(file.name || path.basename(fullPath));
2074
+ const stats = fs.statSync(fullPath);
2075
+ if (!stats.isFile()) {
2076
+ throw new Error(`not a regular file: ${fullPath}`);
2077
+ }
2078
+
2079
+ this.send(MSG_WINDOWS_BRIDGE, JSON.stringify({
2080
+ op: "download_start",
2081
+ requestId,
2082
+ name,
2083
+ size: stats.size,
2084
+ mtime: stats.mtime.toISOString()
2085
+ }));
2086
+
2087
+ const fd = fs.openSync(fullPath, "r");
2088
+ const buffer = Buffer.allocUnsafe(48 * 1024);
2089
+ try {
2090
+ for (;;) {
2091
+ const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null);
2092
+ if (bytesRead === 0) {
2093
+ break;
2094
+ }
2095
+ this.send(MSG_WINDOWS_BRIDGE, JSON.stringify({
2096
+ op: "download_chunk",
2097
+ requestId,
2098
+ name,
2099
+ data: buffer.subarray(0, bytesRead).toString("base64")
2100
+ }));
2101
+ }
2102
+ } finally {
2103
+ fs.closeSync(fd);
2104
+ }
2105
+
2106
+ this.send(MSG_WINDOWS_BRIDGE, JSON.stringify({
2107
+ op: "download_end",
2108
+ requestId,
2109
+ name
2110
+ }));
2111
+ }
2112
+
2113
+ this.backend.write(Buffer.from(encodeWindowsBridgeMessage({
2114
+ op: "download_finish",
2115
+ requestId
2116
+ }), "utf8"));
2117
+ } catch (error) {
2118
+ this.backend.write(Buffer.from(encodeWindowsBridgeMessage({
2119
+ op: "download_error",
2120
+ requestId,
2121
+ message: error.message
2122
+ }), "utf8"));
2123
+ }
2124
+ }
2125
+
2126
+ handleBrowserWindowsBridgeMessage(message) {
2127
+ const requestId = String(message && message.requestId || "");
2128
+ const request = this.windowsUploadRequests.get(requestId);
2129
+ if (!request) {
2130
+ return false;
2131
+ }
2132
+
2133
+ try {
2134
+ if (message.op === "upload_cancel") {
2135
+ this.windowsUploadRequests.delete(requestId);
2136
+ this.backend.write(Buffer.from(encodeWindowsBridgeMessage({
2137
+ op: "upload_cancel",
2138
+ requestId
2139
+ }), "utf8"));
2140
+ return true;
2141
+ }
2142
+
2143
+ if (message.op === "upload_start") {
2144
+ const name = basenameSafe(message.name);
2145
+ const outputPath = path.join(request.targetDir, name);
2146
+ const fd = fs.openSync(outputPath, "w", 0o644);
2147
+ request.files.set(name, {
2148
+ fd,
2149
+ outputPath,
2150
+ mtime: message.mtime
2151
+ });
2152
+ return true;
2153
+ }
2154
+
2155
+ if (message.op === "upload_chunk") {
2156
+ const name = basenameSafe(message.name);
2157
+ const entry = request.files.get(name);
2158
+ if (!entry) {
2159
+ throw new Error(`missing upload target for ${name}`);
2160
+ }
2161
+ fs.writeSync(entry.fd, Buffer.from(String(message.data || ""), "base64"));
2162
+ return true;
2163
+ }
2164
+
2165
+ if (message.op === "upload_end") {
2166
+ const name = basenameSafe(message.name);
2167
+ const entry = request.files.get(name);
2168
+ if (!entry) {
2169
+ throw new Error(`missing upload target for ${name}`);
2170
+ }
2171
+ fs.closeSync(entry.fd);
2172
+ entry.fd = undefined;
2173
+ if (entry.mtime) {
2174
+ const mtime = new Date(entry.mtime);
2175
+ if (!Number.isNaN(mtime.valueOf())) {
2176
+ fs.utimesSync(entry.outputPath, new Date(), mtime);
2177
+ }
2178
+ }
2179
+ return true;
2180
+ }
2181
+
2182
+ if (message.op === "upload_finish") {
2183
+ this.windowsUploadRequests.delete(requestId);
2184
+ this.backend.write(Buffer.from(encodeWindowsBridgeMessage({
2185
+ op: "upload_finish",
2186
+ requestId
2187
+ }), "utf8"));
2188
+ return true;
2189
+ }
2190
+ } catch (error) {
2191
+ this.windowsUploadRequests.delete(requestId);
2192
+ this.backend.write(Buffer.from(encodeWindowsBridgeMessage({
2193
+ op: "upload_error",
2194
+ requestId,
2195
+ message: error.message
2196
+ }), "utf8"));
2197
+ return true;
2198
+ }
2199
+
2200
+ return false;
2201
+ }
2202
+
2203
+ sendChunk(type, raw) {
2204
+ if (type === MSG_OUTPUT && this.zmodemBinaryBypass) {
2205
+ this.sendZmodemOutput(raw);
2206
+ return;
2207
+ }
2208
+
2209
+ if (type === MSG_OUTPUT && this.supportsWindowsBridge) {
2210
+ const bridged = this.outgoingWindowsBridgeParser.consume(raw);
2211
+ for (const control of bridged.controls) {
2212
+ this.handleWindowsBridgeControl(control);
2213
+ }
2214
+ raw = bridged.plain;
2215
+ if (raw.length === 0) {
2216
+ return;
2217
+ }
2218
+ }
2219
+
2220
+ const maxChunkSize = Math.floor((this.bufferSize - 1) / 4) * 3;
2221
+ for (let offset = 0; offset < raw.length; offset += maxChunkSize) {
2222
+ const chunk = raw.subarray(offset, Math.min(offset + maxChunkSize, raw.length));
2223
+ this.send(type, chunk.toString("base64"));
2224
+ }
2225
+ }
2226
+
2227
+ sendZmodemOutput(raw) {
2228
+ this.zmodemBinaryBytesSent += raw.length;
2229
+ if (this.zmodemBinaryBytesSent - this.zmodemBinaryBytesLogged >= 16 * 1024 * 1024) {
2230
+ this.zmodemBinaryBytesLogged = this.zmodemBinaryBytesSent;
2231
+ this.log(`ZMODEM text websocket output ${this.zmodemBinaryBytesSent.toLocaleString()} bytes`);
2232
+ }
2233
+
2234
+ const maxChunkSize = 3 * 1024;
2235
+ for (let offset = 0; offset < raw.length; offset += maxChunkSize) {
2236
+ const chunk = raw.subarray(offset, Math.min(offset + maxChunkSize, raw.length));
2237
+ this.zmodemOutputQueue.push({
2238
+ seq: this.zmodemOutputSeq,
2239
+ payload: chunk.toString("base64")
2240
+ });
2241
+ this.zmodemOutputSeq += 1;
2242
+ }
2243
+ this.flushZmodemOutput();
2244
+ }
2245
+
2246
+ resetZmodemOutputFlow() {
2247
+ this.zmodemOutputQueue = [];
2248
+ this.zmodemOutputInFlight = new Map();
2249
+ this.zmodemOutputSeq = 0;
2250
+ this.zmodemOutputStreamId += 1;
2251
+ this.zmodemOutputAckedSeq = -1;
2252
+ if (this.zmodemRetransmitTimer) {
2253
+ clearTimeout(this.zmodemRetransmitTimer);
2254
+ this.zmodemRetransmitTimer = null;
2255
+ }
2256
+ }
2257
+
2258
+ flushZmodemOutput() {
2259
+ const targetWs = this.writerWs && this.writerWs.readyState === WebSocket.OPEN
2260
+ ? this.writerWs
2261
+ : this.clients.keys().next().value;
2262
+ if (!targetWs || targetWs.readyState !== WebSocket.OPEN) {
2263
+ return;
2264
+ }
2265
+
2266
+ while (this.zmodemOutputInFlight.size < this.zmodemOutputWindow && this.zmodemOutputQueue.length > 0) {
2267
+ const item = this.zmodemOutputQueue.shift();
2268
+ this.zmodemOutputInFlight.set(item.seq, item);
2269
+ this.sendZmodemOutputItem(item, targetWs);
2270
+ }
2271
+ this.scheduleZmodemRetransmit();
2272
+ }
2273
+
2274
+ sendZmodemOutputItem(item, targetWs) {
2275
+ this.send(MSG_ZMODEM_OUTPUT, `${this.zmodemOutputStreamId}:${item.seq}:${item.payload}`, targetWs);
2276
+ }
2277
+
2278
+ scheduleZmodemRetransmit() {
2279
+ if (this.zmodemRetransmitTimer || this.zmodemOutputInFlight.size === 0) {
2280
+ return;
2281
+ }
2282
+ this.zmodemRetransmitTimer = setTimeout(() => {
2283
+ this.zmodemRetransmitTimer = null;
2284
+ if (!this.zmodemBinaryBypass || this.zmodemOutputInFlight.size === 0) {
2285
+ return;
2286
+ }
2287
+ const targetWs = this.writerWs && this.writerWs.readyState === WebSocket.OPEN
2288
+ ? this.writerWs
2289
+ : this.clients.keys().next().value;
2290
+ if (!targetWs || targetWs.readyState !== WebSocket.OPEN) {
2291
+ return;
2292
+ }
2293
+ for (const item of this.zmodemOutputInFlight.values()) {
2294
+ this.sendZmodemOutputItem(item, targetWs);
2295
+ }
2296
+ this.scheduleZmodemRetransmit();
2297
+ }, 750);
2298
+ }
2299
+
2300
+ filterEchoedTerminalReplies(raw) {
2301
+ const needle = Buffer.from("^[[?1;2c", "ascii");
2302
+ const combined = this.pendingEchoBytes.length > 0 ? Buffer.concat([this.pendingEchoBytes, raw]) : raw;
2303
+ let searchIndex = 0;
2304
+ const segments = [];
2305
+
2306
+ while (searchIndex < combined.length) {
2307
+ const matchIndex = combined.indexOf(needle, searchIndex);
2308
+ if (matchIndex === -1) {
2309
+ break;
2310
+ }
2311
+ if (matchIndex > searchIndex) {
2312
+ segments.push(combined.subarray(searchIndex, matchIndex));
2313
+ }
2314
+ searchIndex = matchIndex + needle.length;
2315
+ }
2316
+
2317
+ const remaining = combined.subarray(searchIndex);
2318
+ let suffixLength = 0;
2319
+ const maxSuffix = Math.min(needle.length - 1, remaining.length);
2320
+ for (let len = maxSuffix; len > 0; len -= 1) {
2321
+ if (needle.subarray(0, len).equals(remaining.subarray(remaining.length - len))) {
2322
+ suffixLength = len;
2323
+ break;
2324
+ }
2325
+ }
2326
+
2327
+ const flushable = suffixLength > 0 ? remaining.subarray(0, remaining.length - suffixLength) : remaining;
2328
+ if (flushable.length > 0) {
2329
+ segments.push(flushable);
2330
+ }
2331
+ this.pendingEchoBytes = suffixLength > 0 ? remaining.subarray(remaining.length - suffixLength) : Buffer.alloc(0);
2332
+
2333
+ if (segments.length === 0) {
2334
+ return Buffer.alloc(0);
2335
+ }
2336
+ if (segments.length === 1) {
2337
+ return Buffer.from(segments[0]);
2338
+ }
2339
+ return Buffer.concat(segments);
2340
+ }
2341
+
2342
+ buildKittyCursorMotion(graphic) {
2343
+ if (!graphic || graphic.kind !== "placement") {
2344
+ return "";
2345
+ }
2346
+ const control = graphic.control || {};
2347
+ const cursor = graphic.cursor || { col: 1 };
2348
+ if (control.C === "1") {
2349
+ return "";
2350
+ }
2351
+ const cols = Math.max(0, Number.parseInt(control.c || "0", 10) || 0);
2352
+ const rows = Math.max(0, Number.parseInt(control.r || "0", 10) || 0);
2353
+ const targetCol = Math.max(1, (Number.parseInt(cursor.col || "1", 10) || 1) + cols);
2354
+
2355
+ if (rows > 0) {
2356
+ return "\r\n".repeat(rows) + `\u001b[${targetCol}G`;
2357
+ }
2358
+
2359
+ if (cols > 0) {
2360
+ return `\u001b[${targetCol}G`;
2361
+ }
2362
+
2363
+ return "";
2364
+ }
2365
+
2366
+ startWebSocketHandlers(ws) {
2367
+ ws.on("message", (message, isBinary) => {
2368
+ if (this.closed || !this.clients.has(ws) || isBinary) {
2369
+ return;
2370
+ }
2371
+ try {
2372
+ this.handleClientMessage(ws, message.toString("utf8"));
2373
+ } catch (error) {
2374
+ this.log(`WS session error: ${error.message}`);
2375
+ try {
2376
+ ws.close(1011, error.message);
2377
+ } catch (closeError) {
2378
+ // The client socket is already gone.
2379
+ }
2380
+ }
2381
+ });
2382
+
2383
+ const handleSocketGone = () => {
2384
+ if (!this.clients.has(ws) || this.closed) {
2385
+ return;
2386
+ }
2387
+ if (this.writerWs === ws) {
2388
+ this.writerWs = null;
2389
+ }
2390
+ this.clients.delete(ws);
2391
+ if (this.clients.size > 0) {
2392
+ this.notifyRoles();
2393
+ return;
2394
+ }
2395
+ if (this.options.reconnect && !this.backendExited) {
2396
+ this.armReconnectTimeout();
2397
+ return;
2398
+ }
2399
+ this.terminate();
2400
+ };
2401
+
2402
+ ws.once("close", handleSocketGone);
2403
+ ws.once("error", handleSocketGone);
2404
+ }
2405
+
2406
+ handleClientMessage(ws, data) {
2407
+ if (!data || data.length === 0) {
2408
+ throw new Error("unexpected zero length read from master");
2409
+ }
2410
+
2411
+ const type = data[0];
2412
+ const payload = data.slice(1);
2413
+ const client = this.clients.get(ws);
2414
+ if (!client) {
2415
+ return;
2416
+ }
2417
+
2418
+ switch (type) {
2419
+ case MSG_INPUT:
2420
+ if (!this.isWriter(ws) || payload.length === 0) {
2421
+ return;
2422
+ }
2423
+ if (client.encoding === "base64") {
2424
+ this.backend.write(Buffer.from(payload, "base64"));
2425
+ } else {
2426
+ this.backend.write(Buffer.from(payload, "utf8"));
2427
+ }
2428
+ break;
2429
+ case MSG_INPUT_BINARY:
2430
+ if (!this.isWriter(ws) || payload.length === 0) {
2431
+ return;
2432
+ }
2433
+ this.backend.writeBinary(Buffer.from(payload, "base64"));
2434
+ break;
2435
+ case MSG_PING:
2436
+ this.send(MSG_PONG, "", ws);
2437
+ break;
2438
+ case MSG_KITTY_DEBUG:
2439
+ if (!KITTY_TRACE) {
2440
+ break;
2441
+ }
2442
+ try {
2443
+ kittyPlacementLog("browser", JSON.parse(payload));
2444
+ } catch (error) {
2445
+ kittyPlacementLog("browser-malformed", { payload });
2446
+ }
2447
+ break;
2448
+ case MSG_WINDOWS_BRIDGE: {
2449
+ if (!this.supportsWindowsBridge || !this.isWriter(ws) || payload.length === 0) {
2450
+ return;
2451
+ }
2452
+ let message;
2453
+ try {
2454
+ message = JSON.parse(payload);
2455
+ } catch (error) {
2456
+ throw new Error("received malformed data for windows bridge");
2457
+ }
2458
+ if (this.handleBrowserWindowsBridgeMessage(message)) {
2459
+ break;
2460
+ }
2461
+ this.backend.write(Buffer.from(encodeWindowsBridgeMessage(message), "utf8"));
2462
+ break;
2463
+ }
2464
+ case MSG_SET_ENCODING:
2465
+ if (payload === "base64" || payload === "null") {
2466
+ client.encoding = payload;
2467
+ }
2468
+ break;
2469
+ case MSG_ZMODEM_MODE:
2470
+ if (!this.isWriter(ws)) {
2471
+ return;
2472
+ }
2473
+ {
2474
+ const enabled = payload === "1";
2475
+ const wasEnabled = this.zmodemBinaryBypass;
2476
+ this.zmodemBinaryBypass = enabled;
2477
+ if (this.zmodemBinaryBypass && !wasEnabled) {
2478
+ this.zmodemBinaryBytesSent = 0;
2479
+ this.zmodemBinaryBytesLogged = 0;
2480
+ this.resetZmodemOutputFlow();
2481
+ } else if (!this.zmodemBinaryBypass) {
2482
+ this.resetZmodemOutputFlow();
2483
+ }
2484
+ this.log(`ZMODEM parser bypass ${this.zmodemBinaryBypass ? "enabled" : "disabled"} by browser`);
2485
+ }
2486
+ break;
2487
+ case MSG_ZMODEM_ACK: {
2488
+ if (!this.isWriter(ws)) {
2489
+ return;
2490
+ }
2491
+ const seq = Number.parseInt(payload, 10);
2492
+ if (!Number.isFinite(seq) || seq <= this.zmodemOutputAckedSeq) {
2493
+ return;
2494
+ }
2495
+ for (const key of this.zmodemOutputInFlight.keys()) {
2496
+ if (key <= seq) {
2497
+ this.zmodemOutputInFlight.delete(key);
2498
+ }
2499
+ }
2500
+ this.zmodemOutputAckedSeq = seq;
2501
+ this.flushZmodemOutput();
2502
+ break;
2503
+ }
2504
+ case MSG_RESIZE_TERMINAL: {
2505
+ if (!this.isWriter(ws) || (this.columns !== 0 && this.rows !== 0)) {
2506
+ return;
2507
+ }
2508
+ let args;
2509
+ try {
2510
+ args = JSON.parse(payload);
2511
+ } catch (error) {
2512
+ throw new Error("received malformed data for terminal resize");
2513
+ }
2514
+ const columns = this.columns || Number.parseInt(String(args.columns), 10);
2515
+ const rows = this.rows || Number.parseInt(String(args.rows), 10);
2516
+ if (Number.isFinite(columns) && Number.isFinite(rows)) {
2517
+ this.cursorTracker.setDimensions(columns);
2518
+ this.backend.resize(columns, rows);
2519
+ }
2520
+ break;
2521
+ }
2522
+ default:
2523
+ throw new Error(`unknown message type ${type}`);
2524
+ }
2525
+ }
2526
+
2527
+ send(type, payload, targetWs = null) {
2528
+ if (targetWs) {
2529
+ this.queueSend(targetWs, type + payload);
2530
+ return;
2531
+ }
2532
+
2533
+ for (const ws of this.clients.keys()) {
2534
+ this.queueSend(ws, type + payload);
2535
+ }
2536
+ }
2537
+
2538
+ queueSend(ws, payload, binary = false) {
2539
+ const client = this.clients.get(ws);
2540
+ if (!client || ws.readyState !== WebSocket.OPEN) {
2541
+ return;
2542
+ }
2543
+
2544
+ const sendOne = async () => {
2545
+ await new Promise((resolve, reject) => {
2546
+ try {
2547
+ if (binary) {
2548
+ ws.send(payload, { binary: true }, (error) => {
2549
+ if (error) {
2550
+ reject(error);
2551
+ return;
2552
+ }
2553
+ resolve();
2554
+ });
2555
+ } else {
2556
+ ws.send(payload, (error) => {
2557
+ if (error) {
2558
+ reject(error);
2559
+ return;
2560
+ }
2561
+ resolve();
2562
+ });
2563
+ }
2564
+ } catch (error) {
2565
+ reject(error);
2566
+ }
2567
+ });
2568
+ };
2569
+
2570
+ client.sendQueue = client.sendQueue.then(sendOne, sendOne);
2571
+ client.sendQueue.catch((error) => {
2572
+ this.log(`WebSocket send failed: ${error && error.message ? error.message : error}`);
2573
+ });
2574
+ }
2575
+
2576
+ terminateBackend(gracePeriodMs = 5000) {
2577
+ if (this.backendTerminationPromise) {
2578
+ return this.backendTerminationPromise;
2579
+ }
2580
+
2581
+ this.backendTerminationPromise = (async () => {
2582
+ if (this.backendExited) {
2583
+ return { forced: false };
2584
+ }
2585
+
2586
+ const waitForExit = async (timeoutMs) => {
2587
+ let timer = null;
2588
+ const timedOut = new Promise((resolve) => {
2589
+ timer = setTimeout(() => resolve(false), timeoutMs);
2590
+ });
2591
+ const exited = this.backendExitPromise.then(() => true);
2592
+ const result = await Promise.race([exited, timedOut]);
2593
+ if (timer) {
2594
+ clearTimeout(timer);
2595
+ }
2596
+ return result;
2597
+ };
2598
+
2599
+ if (process.platform === "win32") {
2600
+ this.backend.kill();
2601
+ if (await waitForExit(gracePeriodMs)) {
2602
+ return { forced: true };
2603
+ }
2604
+ throw new Error("process did not exit after PTY kill");
2605
+ }
2606
+
2607
+ this.backend.kill("SIGTERM");
2608
+ if (await waitForExit(gracePeriodMs)) {
2609
+ return { forced: false };
2610
+ }
2611
+
2612
+ this.backend.kill("SIGKILL");
2613
+ if (await waitForExit(gracePeriodMs)) {
2614
+ return { forced: true };
2615
+ }
2616
+
2617
+ throw new Error("process did not exit after SIGKILL");
2618
+ })();
2619
+
2620
+ return this.backendTerminationPromise;
2621
+ }
2622
+
2623
+ terminate(code, reason, { skipBackendClose = false } = {}) {
2624
+ if (this.closed) {
2625
+ return;
2626
+ }
2627
+ this.closed = true;
2628
+
2629
+ if (this.reconnectTimer) {
2630
+ clearTimeout(this.reconnectTimer);
2631
+ this.reconnectTimer = null;
2632
+ }
2633
+
2634
+ if (this.options.reconnectRegistry && this.reconnectToken) {
2635
+ this.options.reconnectRegistry.delete(this.reconnectToken);
2636
+ }
2637
+
2638
+ if (!skipBackendClose && !this.backendExited) {
2639
+ this.backend.close();
2640
+ }
2641
+
2642
+ const clients = [...this.clients.keys()];
2643
+ this.clients.clear();
2644
+ this.writerWs = null;
2645
+ for (const ws of clients) {
2646
+ try {
2647
+ ws.close(code || 1000, reason);
2648
+ } catch (error) {
2649
+ // ignore
2650
+ }
2651
+ }
2652
+
2653
+ if (typeof this.options.onTerminate === "function") {
2654
+ this.options.onTerminate(this);
2655
+ }
2656
+ }
2657
+
2658
+ close(code, reason) {
2659
+ this.terminate(code, reason);
2660
+ }
2661
+ }
2662
+
2663
+ function createServerRuntime(command, argv, options) {
2664
+ const log = logFactory(options.quiet);
2665
+ const hostname = os.hostname();
2666
+ const basePath = normalizeBasePath(
2667
+ options.randomUrl ? `/${randomPathSegment(options.randomUrlLength)}/` : options.path
2668
+ );
2669
+ const wsPath = `${basePath}ws`;
2670
+ const originMatcher = options.wsOrigin ? new RegExp(options.wsOrigin) : null;
2671
+ const indexTemplate = options.index
2672
+ ? fs.readFileSync(expandHome(options.index), "utf8")
2673
+ : assetText("static/index.html");
2674
+ const manifestTemplate = assetText("static/manifest.json");
2675
+ const activeSessions = new Set();
2676
+ const reconnectRegistry = new Map();
2677
+ let acceptedOnce = false;
2678
+ let shuttingDown = false;
2679
+
2680
+ const findSessionByIdentifier = (identifier) => {
2681
+ const value = String(identifier || "").trim();
2682
+ if (!value) {
2683
+ return null;
2684
+ }
2685
+
2686
+ const byToken = reconnectRegistry.get(value);
2687
+ if (byToken && byToken.canResume(byToken.reconnectToken)) {
2688
+ return byToken;
2689
+ }
2690
+
2691
+ for (const session of activeSessions) {
2692
+ if (!session || !session.backend) {
2693
+ continue;
2694
+ }
2695
+ if (String(session.backend.pid) === value) {
2696
+ return session;
2697
+ }
2698
+ if (session.reconnectToken && session.reconnectToken === value) {
2699
+ return session;
2700
+ }
2701
+ }
2702
+
2703
+ return null;
2704
+ };
2705
+
2706
+ const findReconnectSession = (identifier) => {
2707
+ const session = findSessionByIdentifier(identifier);
2708
+ if (session && session.canResume(session.reconnectToken)) {
2709
+ return session;
2710
+ }
2711
+ return null;
2712
+ };
2713
+
2714
+ const counter = new Counter(options.timeout, () => {
2715
+ log("Timeout reached without active connections, shutting down");
2716
+ shutdown(false);
2717
+ });
2718
+
2719
+ const requestHandler = (req, res) => {
2720
+ const requestPath = new URL(req.url, "http://localhost").pathname;
2721
+ if (requestPath === basePath.slice(0, -1)) {
2722
+ res.writeHead(302, {
2723
+ Location: basePath,
2724
+ "Server": "GoTTY"
2725
+ });
2726
+ res.end();
2727
+ return;
2728
+ }
2729
+
2730
+ if (!requestPath.startsWith(basePath)) {
2731
+ res.writeHead(404);
2732
+ res.end("Not Found");
2733
+ return;
2734
+ }
2735
+
2736
+ if (options.credential && requestPath !== wsPath) {
2737
+ if (!basicAuthAuthorized(req, options.credential)) {
2738
+ sendBasicAuthRequired(res);
2739
+ return;
2740
+ }
2741
+ }
2742
+
2743
+ const relativePath = requestPath.slice(basePath.length);
2744
+ if (relativePath === "") {
2745
+ const title = renderTemplate(
2746
+ options.titleFormat,
2747
+ buildTitleVariables(command, argv, hostname, req.socket.remoteAddress || "", {})
2748
+ );
2749
+ const html = renderTemplate(indexTemplate, { title });
2750
+ res.writeHead(200, {
2751
+ "Content-Type": "text/html; charset=utf-8",
2752
+ "Server": "GoTTY"
2753
+ });
2754
+ res.end(html);
2755
+ return;
2756
+ }
2757
+
2758
+ if (relativePath === "manifest.json") {
2759
+ const title = renderTemplate(
2760
+ options.titleFormat,
2761
+ buildTitleVariables(command, argv, hostname, req.socket.remoteAddress || "", {})
2762
+ );
2763
+ const body = renderTemplate(manifestTemplate, { title });
2764
+ res.writeHead(200, {
2765
+ "Content-Type": "application/json; charset=utf-8",
2766
+ "Server": "GoTTY"
2767
+ });
2768
+ res.end(body);
2769
+ return;
2770
+ }
2771
+
2772
+ if (relativePath === "auth_token.js") {
2773
+ const body = `var gotty_auth_token = ${JSON.stringify(options.credential)};`;
2774
+ res.writeHead(200, {
2775
+ "Content-Type": "application/javascript; charset=utf-8",
2776
+ "Server": "GoTTY"
2777
+ });
2778
+ res.end(body);
2779
+ return;
2780
+ }
2781
+
2782
+ if (relativePath === "config.js") {
2783
+ const lines = [
2784
+ "var gotty_term = 'xterm';",
2785
+ `var gotty_ws_query_args = ${JSON.stringify(options.wsQueryArgs)};`,
2786
+ `var gotty_enable_webgl = ${options.webgl ? "true" : "false"};`,
2787
+ `var gotty_kitty_trace = ${KITTY_TRACE ? "true" : "false"};`
2788
+ ];
2789
+ res.writeHead(200, {
2790
+ "Content-Type": "application/javascript; charset=utf-8",
2791
+ "Server": "GoTTY"
2792
+ });
2793
+ res.end(lines.join("\n"));
2794
+ return;
2795
+ }
2796
+
2797
+ if (relativePath === "help.md" || relativePath === "help") {
2798
+ const markdown = assetText("static/help.md");
2799
+ const markdownToHtml = global.Bun?.markdown?.html;
2800
+ const renderHtml = relativePath === "help" && typeof markdownToHtml === "function";
2801
+ const body = renderHtml
2802
+ ? markdownToHtml(markdown)
2803
+ : markdown;
2804
+ res.writeHead(200, {
2805
+ "Content-Type": renderHtml
2806
+ ? "text/html; charset=utf-8"
2807
+ : "text/markdown; charset=utf-8",
2808
+ "Server": "GoTTY"
2809
+ });
2810
+ res.end(body);
2811
+ return;
2812
+ }
2813
+
2814
+ if (relativePath === "css.md" || relativePath === "css") {
2815
+ const sessions = [];
2816
+ for (const session of activeSessions) {
2817
+ if (!session || !session.backend) {
2818
+ continue;
2819
+ }
2820
+ const pid = session.backend.pid
2821
+ ? String(session.backend.pid)
2822
+ : "無";
2823
+ const command = session.options && session.options.command
2824
+ ? path.basename(String(session.options.command))
2825
+ : "";
2826
+ const name = command.replace(/[\r\n\s]+/g, " ").trim() || "無";
2827
+ const reconnectToken = session.reconnectToken
2828
+ ? String(session.reconnectToken).replace(/[\r\n\s]+/g, "")
2829
+ : "";
2830
+ sessions.push({ pid, name, token: reconnectToken });
2831
+ }
2832
+ sessions.sort((left, right) => Number(left.pid) - Number(right.pid));
2833
+ const scheme = options.tls ? "https" : "http";
2834
+ const requestHost = String(req.headers.host || "").trim();
2835
+ const fallbackAddress = server.address();
2836
+ const fallbackHost = fallbackAddress && typeof fallbackAddress !== "string"
2837
+ ? `${net.isIPv6(fallbackAddress.address) ? `[${fallbackAddress.address}]` : fallbackAddress.address}:${fallbackAddress.port}`
2838
+ : "127.0.0.1";
2839
+ const publicOrigin = `${scheme}://${requestHost || fallbackHost}`;
2840
+
2841
+ const lines = [
2842
+ "# Current Session(s)",
2843
+ ...(options.reconnect
2844
+ ? [
2845
+ "- Click on the links directly or with Ctrl+Click to reconnect to session",
2846
+ "- Or use bun client.js -r pid"
2847
+ ]
2848
+ : [
2849
+ "- Reconnect is disabled, so only PID and command name are shown"
2850
+ ]),
2851
+ "## PID SESSION",
2852
+ ...(sessions.length > 0
2853
+ ? sessions.map((session) => {
2854
+ const name = session.name.replace(/([\\[\]])/g, "\\$1");
2855
+ if (session.token) {
2856
+ const reconnectUrl = `${publicOrigin}${basePath}?reconnect-token=${encodeURIComponent(session.token)}`;
2857
+ return `- ${session.pid} [${name}](${reconnectUrl})`;
2858
+ }
2859
+ return `- ${session.pid} ${name}`;
2860
+ })
2861
+ : ["- 無 無"])
2862
+ ];
2863
+ const markdown = `${lines.join("\n")}\n`;
2864
+ const markdownToHtml = global.Bun?.markdown?.html;
2865
+ const renderHtml = relativePath === "css" && typeof markdownToHtml === "function";
2866
+ const body = renderHtml
2867
+ ? markdownToHtml(markdown)
2868
+ : markdown;
2869
+ res.writeHead(200, {
2870
+ "Content-Type": renderHtml
2871
+ ? "text/html; charset=utf-8"
2872
+ : "text/markdown; charset=utf-8",
2873
+ "Server": "GoTTY"
2874
+ });
2875
+ res.end(body);
2876
+ return;
2877
+ }
2878
+
2879
+ const assetKey = staticAssetKey(relativePath);
2880
+ if (!assetKey) {
2881
+ res.writeHead(403);
2882
+ res.end("Forbidden");
2883
+ return;
2884
+ }
2885
+
2886
+ const bundledAsset = assets?.readInternalAssetBytes(assetKey);
2887
+ if (bundledAsset != null) {
2888
+ const body = bundledAsset instanceof Uint8Array ? bundledAsset : new Uint8Array(bundledAsset);
2889
+ res.writeHead(200, {
2890
+ "Content-Type": contentType(assetKey),
2891
+ "Content-Length": body.length,
2892
+ "Server": "GoTTY"
2893
+ });
2894
+ res.end(body);
2895
+ return;
2896
+ }
2897
+
2898
+ const externalPath = assetFilePath(assetKey);
2899
+ fs.stat(externalPath, (error, stats) => {
2900
+ if (error || !stats.isFile()) {
2901
+ res.writeHead(404, { "Server": "GoTTY" });
2902
+ res.end("Not Found");
2903
+ return;
2904
+ }
2905
+
2906
+ res.writeHead(200, {
2907
+ "Content-Type": contentType(assetKey),
2908
+ "Content-Length": stats.size,
2909
+ "Server": "GoTTY"
2910
+ });
2911
+ fs.createReadStream(externalPath).pipe(res);
2912
+ });
2913
+ };
2914
+
2915
+ let server;
2916
+ if (options.tls) {
2917
+ const certPath = expandHome(options.tlsCrt);
2918
+ const keyPath = expandHome(options.tlsKey);
2919
+
2920
+ try {
2921
+ server = https.createServer(
2922
+ {
2923
+ cert: fs.readFileSync(certPath),
2924
+ key: fs.readFileSync(keyPath)
2925
+ },
2926
+ requestHandler
2927
+ );
2928
+ } catch (error) {
2929
+ console.error("TLS is enabled, but the certificate or key could not be loaded.");
2930
+ console.error(`Certificate: ${certPath}`);
2931
+ console.error(`Private key: ${keyPath}`);
2932
+ console.error(`Reason: ${error.message}`);
2933
+ console.error("");
2934
+ console.error("You can generate a self-signed certificate for testing with:");
2935
+ console.error(`openssl req -x509 -newkey rsa:2048 -sha256 -days 365 -nodes -keyout ${keyPath} -out ${certPath} -subj "/CN=localhost"`);
2936
+ console.error("");
2937
+ console.error("Or use a certificate and private key issued by a trusted certificate authority.");
2938
+ process.exit(1);
2939
+ }
2940
+ } else {
2941
+ server = http.createServer(requestHandler);
2942
+ }
2943
+
2944
+ const wss = new WebSocketServer({
2945
+ noServer: true,
2946
+ clientTracking: false,
2947
+ perMessageDeflate: false,
2948
+ handleProtocols(protocols) {
2949
+ return protocols.has("webtty") ? "webtty" : false;
2950
+ }
2951
+ });
2952
+
2953
+ server.on("upgrade", (req, socket, head) => {
2954
+ const requestPath = new URL(req.url, "http://localhost").pathname;
2955
+ if (requestPath !== wsPath) {
2956
+ socket.destroy();
2957
+ return;
2958
+ }
2959
+
2960
+ if (shuttingDown) {
2961
+ socket.destroy();
2962
+ return;
2963
+ }
2964
+
2965
+ if (!webSocketOriginAllowed(req, originMatcher)) {
2966
+ socket.destroy();
2967
+ return;
2968
+ }
2969
+
2970
+ if (options.once && acceptedOnce && !options.reconnect) {
2971
+ socket.destroy();
2972
+ return;
2973
+ }
2974
+
2975
+ const num = counter.add();
2976
+ if (options.maxConnection > 0 &&
2977
+ num > options.maxConnection)
2978
+ {
2979
+ counter.done();
2980
+ socket.destroy();
2981
+ return;
2982
+ }
2983
+
2984
+ wss.handleUpgrade(req, socket, head, (ws) => {
2985
+ wss.emit("connection", ws, req);
2986
+ });
2987
+ });
2988
+
2989
+ wss.on("connection", (ws, req) => {
2990
+ log(`\nNew client connected: ${req.socket.remoteAddress}`);
2991
+ log(` Connection count: ${counter.count()}`)
2992
+
2993
+ if(options.maxConnection>0)
2994
+ log(` Limit: ${options.maxConnection}`);
2995
+
2996
+ let finalized = false;
2997
+ let session = null;
2998
+
2999
+ const finish = (reason) => {
3000
+ if (finalized) {
3001
+ return;
3002
+ }
3003
+ finalized = true;
3004
+ const num = counter.done();
3005
+ log(`Connection closed by ${reason}: ${req.socket.remoteAddress}`)
3006
+
3007
+ log(` Connection count: ${num}`)
3008
+
3009
+ if(options.maxConnection>0)
3010
+ log(` Limit: ${options.maxConnection}`);
3011
+
3012
+ if (shuttingDown && num === 0) {
3013
+ process.exit(0);
3014
+ }
3015
+ };
3016
+
3017
+ ws.once("close", () => finish("client"));
3018
+ ws.once("error", (error) => finish(`an error: ${error.message}`));
3019
+
3020
+ ws.once("message", async (message, isBinary) => {
3021
+ if (isBinary) {
3022
+ ws.close(1008, "invalid message type");
3023
+ return;
3024
+ }
3025
+
3026
+ let init;
3027
+ try {
3028
+ init = parseInitMessage(message);
3029
+ } catch (error) {
3030
+ ws.close(1008, error.message);
3031
+ return;
3032
+ }
3033
+
3034
+ if (init.AuthToken !== options.credential) {
3035
+ ws.close(1008, "failed to authenticate websocket connection");
3036
+ return;
3037
+ }
3038
+
3039
+ if (init.DisconnectWriter) {
3040
+ const existing = options.reconnect
3041
+ ? findReconnectSession(init.ReconnectToken)
3042
+ : null;
3043
+ const writer = existing?.writerSocket();
3044
+ const result = existing && writer
3045
+ ? {
3046
+ ok: true,
3047
+ message: "writer disconnected",
3048
+ token: existing.reconnectToken,
3049
+ pid: existing.backend.pid
3050
+ }
3051
+ : {
3052
+ ok: false,
3053
+ message: existing
3054
+ ? "session has no active writer"
3055
+ : "reconnect session not found"
3056
+ };
3057
+
3058
+ if (writer) {
3059
+ try {
3060
+ writer.close(4001, "writer disconnected by remote request");
3061
+ } catch (error) {
3062
+ result.ok = false;
3063
+ result.message = `failed to disconnect writer: ${error.message}`;
3064
+ }
3065
+ }
3066
+
3067
+ ws.send(MSG_CONTROL_RESULT + JSON.stringify(result), () => {
3068
+ ws.close(result.ok ? 1000 : 1008, result.message);
3069
+ });
3070
+ return;
3071
+ }
3072
+
3073
+ if (init.TerminateSession) {
3074
+ const existing = findSessionByIdentifier(init.ReconnectToken);
3075
+ const token = existing?.reconnectToken || "";
3076
+ const pid = existing?.backend?.pid || "";
3077
+ const result = existing
3078
+ ? {
3079
+ ok: true,
3080
+ message: "session terminated",
3081
+ token,
3082
+ pid
3083
+ }
3084
+ : {
3085
+ ok: false,
3086
+ message: "session not found"
3087
+ };
3088
+
3089
+ if (existing) {
3090
+ try {
3091
+ const termination = await existing.terminateBackend(5000);
3092
+ if (termination.forced) {
3093
+ result.message = "session force killed";
3094
+ }
3095
+ } catch (error) {
3096
+ result.ok = false;
3097
+ result.message = `failed to terminate session: ${error.message}`;
3098
+ }
3099
+ }
3100
+
3101
+ ws.send(MSG_CONTROL_RESULT + JSON.stringify(result), () => {
3102
+ ws.close(result.ok ? 1000 : 1008, result.message);
3103
+ });
3104
+ return;
3105
+ }
3106
+
3107
+ if (options.reconnect && init.ReconnectToken) {
3108
+ const existing = findReconnectSession(init.ReconnectToken);
3109
+ if (existing) {
3110
+ const reconnectToken = existing.reconnectToken;
3111
+ log(` Reconnect token: (resume)
3112
+ ${formatReconnectTokenForLog(reconnectToken)}
3113
+ `);
3114
+ session = existing;
3115
+ session.attachWebSocket(
3116
+ ws,
3117
+ req.socket.remoteAddress || "",
3118
+ init.ReadOnly
3119
+ );
3120
+ return;
3121
+ }
3122
+ }
3123
+
3124
+ if (options.once && acceptedOnce) {
3125
+ ws.close(1008, "server already accepted a client");
3126
+ return;
3127
+ }
3128
+
3129
+ let clientArgs = [];
3130
+ if (options.permitArguments && init.Arguments) {
3131
+ const url = new URL(init.Arguments, "http://localhost");
3132
+ clientArgs = url.searchParams.getAll("arg");
3133
+ }
3134
+
3135
+ const headerEnv = options.passHeaders ? createHeaderEnvironment(req.headers) : {};
3136
+
3137
+ try {
3138
+ session = new PtySession({
3139
+ ws,
3140
+ log,
3141
+ command,
3142
+ argv: [...argv, ...clientArgs],
3143
+ headerEnv,
3144
+ remoteAddr: req.socket.remoteAddress || "",
3145
+ readOnly: init.ReadOnly,
3146
+ hostname,
3147
+ titleFormat: options.titleFormat,
3148
+ permitWrite: options.permitWrite,
3149
+ reconnect: options.reconnect,
3150
+ reconnectTime: options.reconnectTime,
3151
+ reconnectRegistry,
3152
+ width: options.width,
3153
+ height: options.height,
3154
+ closeSignal: options.closeSignal,
3155
+ closeTimeout: options.closeTimeout,
3156
+ onTerminate: (endedSession) => {
3157
+ activeSessions.delete(endedSession);
3158
+ if (options.once) {
3159
+ shutdown(false);
3160
+ setTimeout(()=>process.exit(0),3000)
3161
+ }
3162
+ }
3163
+ });
3164
+ } catch (error) {
3165
+ ws.close(1011, `failed to create backend: ${error.message}`);
3166
+ return;
3167
+ }
3168
+ acceptedOnce = acceptedOnce || options.once;
3169
+ activeSessions.add(session);
3170
+ if (session.reconnectToken) {
3171
+ reconnectRegistry.set(session.reconnectToken, session);
3172
+ }
3173
+ log(` Reconnect token:
3174
+ ${formatReconnectTokenForLog(session.reconnectToken)}
3175
+ `);
3176
+ session.start();
3177
+ });
3178
+ });
3179
+
3180
+ function shutdown(graceful) {
3181
+ if (shuttingDown) {
3182
+ return;
3183
+ }
3184
+ shuttingDown = true;
3185
+ if (graceful) {
3186
+ server.close();
3187
+ } else {
3188
+ for (const session of activeSessions) {
3189
+ session.close(1001, "server shutting down");
3190
+ }
3191
+ server.close();
3192
+ }
3193
+ }
3194
+
3195
+ function logUrls() {
3196
+ const scheme = options.tls ? "https" : "http";
3197
+ const accessMode = options.permitWrite ? "writable" : "read-only";
3198
+ const address = server.address();
3199
+ if (!address || typeof address === "string") {
3200
+ return;
3201
+ }
3202
+ const host = address.address === "::" ? "127.0.0.1" : address.address;
3203
+ const formattedHost = net.isIPv6(host) ? `[${host}]` : host;
3204
+ log(`HTTP server is listening at:
3205
+ ${scheme}://${formattedHost}:${address.port}${basePath}
3206
+ Access mode: ${accessMode}
3207
+ `);
3208
+ const hasCustomPath = normalizeBasePath(options.path) !== "/";
3209
+ const customPathValue = normalizeBasePath(options.path)
3210
+ .replace(/^\/+|\/+$/g, "");
3211
+ if (
3212
+ hasCustomPath &&
3213
+ !options.randomUrl &&
3214
+ customPathValue.length < 16
3215
+ ) {
3216
+ log(`SECURITY NOTICE:
3217
+ The custom path is only ${customPathValue.length} characters long.
3218
+ Use at least 16 randomly generated characters to make URL guessing harder.
3219
+ A custom path is not a substitute for --credential and TLS.
3220
+ `);
3221
+ }
3222
+ if (
3223
+ options.permitWrite &&
3224
+ !options.credential &&
3225
+ !options.randomUrl &&
3226
+ !hasCustomPath
3227
+ ) {
3228
+ log(`SECURITY WARNING:
3229
+ This writable terminal has no credential, random URL, or custom path.
3230
+ Anyone who can reach this server may execute arbitrary commands (ACE)
3231
+ with the same permissions as the GoTTY process.
3232
+ Enable --credential, --random-url, or at minimum use a non-default --path.
3233
+ `);
3234
+ }
3235
+ if (options.address === "0.0.0.0") {
3236
+ try {
3237
+ const interfaces = os.networkInterfaces();
3238
+ for (const values of Object.values(interfaces)) {
3239
+ for (const value of values || []) {
3240
+ if (value.family === "IPv4" && !value.internal) {
3241
+ log(`Alternative URL:
3242
+ ${scheme}://${value.address}:${address.port}${basePath}
3243
+ `);
3244
+ }
3245
+ }
3246
+ }
3247
+ } catch (error) {
3248
+ log(`Alternative URL:
3249
+ ${scheme}://127.0.0.1:${address.port}${basePath}
3250
+ `);
3251
+ log(`Alternative URL:
3252
+ ${scheme}://localhost:${address.port}${basePath}
3253
+ `);
3254
+ }
3255
+ }
3256
+ }
3257
+
3258
+ function start() {
3259
+ server.listen(Number.parseInt(options.port, 10), options.address, logUrls);
3260
+ }
3261
+
3262
+ process.on("SIGTERM", () => {
3263
+ shutdown(false);
3264
+ });
3265
+
3266
+ let gracefulInterrupt = false;
3267
+ process.on("SIGINT", () => {
3268
+ if (!gracefulInterrupt) {
3269
+ gracefulInterrupt = true;
3270
+ console.log("")
3271
+ console.log("Press Ctrl+C again to force close❌");
3272
+ console.log("再按一次Ctrl+C強制停止❌")
3273
+ shutdown(true);
3274
+ return;
3275
+ }
3276
+ console.log("")
3277
+ console.log("Force closing...");
3278
+ console.log("強制停止...");
3279
+
3280
+ shutdown(false);
3281
+ process.exit(130);
3282
+ });
3283
+
3284
+ server.on("close", () => {
3285
+ if (counter.count() === 0) {
3286
+ process.exit(0);
3287
+ }
3288
+ });
3289
+
3290
+ return { start };
3291
+ }
3292
+
3293
+ function main() {
3294
+ const { options, command, argv } = parseArgs(process.argv.slice(2));
3295
+ const runtime = createServerRuntime(command, argv, options);
3296
+ runtime.start();
3297
+ }
3298
+
3299
+ async function bootstrap() {
3300
+ try {
3301
+ const compiledHelper = await compiledHelperPromise;
3302
+ await compiledHelper?.buildEarlyExit?.(process.argv,'jsgt');
3303
+
3304
+ assets = await assetsHelperPromise;
3305
+ await globalThis.assetsLoaderPromise;
3306
+ } catch (error) {
3307
+ console.error(String(error && error.stack ? error.stack : error));
3308
+ }
3309
+
3310
+ if (!cliBootstrapPromise) {
3311
+ main();
3312
+ }
3313
+ }
3314
+
3315
+ module.exports.CursorStateTracker = CursorStateTracker;
3316
+ module.exports.KittyGraphicsParser = KittyGraphicsParser;
3317
+ module.exports.webSocketOriginAllowed = webSocketOriginAllowed;
3318
+
3319
+ if (require.main === module) {
3320
+ if (KITTY_TRACE) {
3321
+ try {
3322
+ fs.writeFileSync(KITTY_PLACEMENT_LOG, "");
3323
+ } catch {}
3324
+ }
3325
+ bootstrap().catch((error) => {
3326
+ console.error(String(error && error.stack ? error.stack : error));
3327
+ process.exitCode = 1;
3328
+ });
3329
+ }
3330
+
3331
+ // The guard above is inlined to false once this file is bundled, because
3332
+ // the bundle entry point is single-exe/entry.mjs rather than this file.
3333
+ // Export the starter so that entry point can call it.
3334
+ module.exports.bootstrap = bootstrap;