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,3932 @@
1
+ import {
2
+ constants as fsConstants,
3
+ accessSync,
4
+ closeSync,
5
+ chmodSync,
6
+ createReadStream,
7
+ createWriteStream,
8
+ lstatSync,
9
+ linkSync,
10
+ mkdirSync,
11
+ openSync,
12
+ readdirSync,
13
+ rmdirSync,
14
+ readFileSync,
15
+ realpathSync,
16
+ statSync,
17
+ symlinkSync,
18
+ unlinkSync,
19
+ } from "node:fs";
20
+ import { createHash } from "node:crypto";
21
+ import {
22
+ arch as osArch,
23
+ constants as osConstants,
24
+ hostname as osHostname,
25
+ release as osRelease,
26
+ type as osType,
27
+ version as osVersion,
28
+ } from "node:os";
29
+ import {
30
+ basename as pathBasename,
31
+ delimiter as pathDelimiter,
32
+ dirname as pathDirname,
33
+ isAbsolute,
34
+ resolve as resolvePath,
35
+ } from "node:path";
36
+ import { Readable, Writable } from "node:stream";
37
+ import { format as formatValue } from "node:util";
38
+ import { EXECUTABLE_COMMAND, IS_COMPILED } from "../single-exe/compiled.js";
39
+ import { readAssetText } from "../single-exe/assetsHelper.js";
40
+ import { dedupeBunmshHistory, saveBunmshHistory } from "./history.js";
41
+ import { fancyLs } from "./fancy-ls.js";
42
+ import { canonicalEnvironment, environmentValue } from "./environment.js";
43
+ import { findIsRegularBuiltin, runFind } from "./find.js";
44
+ import { main as startFileServer, parseServeArguments, waitForInterrupt } from "../serve.js";
45
+ import { main as catFancy } from "./cat-fancy.js";
46
+ import { parseCatOperands } from "./cat-operands.js";
47
+ import { runCurl } from "./curl.js";
48
+
49
+ const encoder = new TextEncoder();
50
+ const decoder = new TextDecoder();
51
+ const DEFAULT_ALIASES = {
52
+ ls: ["ls", "--color=auto"],
53
+ grep: ["grep", "--color=auto"],
54
+ diff: ["diff", "--color"],
55
+ };
56
+ const DEFAULT_COMMAND_PATH = process.platform === "win32"
57
+ ? (environmentValue(process.env, "PATH") ?? "")
58
+ : ["/bin", "/usr/bin"].join(pathDelimiter);
59
+ const BUN_EXECUTABLE = Bun.which("bun") || process.argv0;
60
+ const BUN_RUNTIME_COMMAND = EXECUTABLE_COMMAND.length > 1
61
+ ? EXECUTABLE_COMMAND
62
+ : [BUN_EXECUTABLE];
63
+ const BUNMSH_ENTRY = resolvePath(import.meta.dirname, "main.js");
64
+ const PIPELINE_STATE_ENV = "BUNMSH_INTERNAL_PIPELINE_STATE";
65
+
66
+ const HELP_ALIASES = {
67
+ ":": "colon",
68
+ "[": "test",
69
+ __builtin: "builtin",
70
+ chdir: "cd",
71
+ };
72
+ const HELP_EXCLUDED = new Set([".", "..", "//", "-", "~"]);
73
+ const HELP_LONG_ONLY = new Set(["ls", "lsfancy"]);
74
+
75
+ function requestsBuiltinHelp(argv) {
76
+ return argv.length === 2 &&
77
+ (argv[1] === "--help" || (argv[1] === "-h" && !HELP_LONG_ONLY.has(argv[0])));
78
+ }
79
+
80
+ function hasBuiltinHelp(name) {
81
+ return !HELP_EXCLUDED.has(name);
82
+ }
83
+
84
+ async function builtinHelp(name) {
85
+ try {
86
+ const source = await readAssetText(`help/${HELP_ALIASES[name] ?? name}.md`);
87
+ return result(0, String(Bun.markdown.ansi(source, { hyperlinks: true })));
88
+ } catch (error) {
89
+ return result(1, "", `bunmsh: ${name}: cannot load help: ${error.message}\n`);
90
+ }
91
+ }
92
+
93
+ function shellPath(value) {
94
+ return process.platform === "win32" ? value.replaceAll("\\", "/") : value;
95
+ }
96
+
97
+ function nativePath(value) {
98
+ return process.platform === "win32" ? value.replaceAll("/", "\\") : value;
99
+ }
100
+
101
+ export class ShellSyntaxError extends Error {
102
+ constructor(message, offset = -1) {
103
+ super(message);
104
+ this.name = "ShellSyntaxError";
105
+ this.offset = offset;
106
+ }
107
+ }
108
+
109
+ function isNameStart(ch) {
110
+ return /[A-Za-z_]/.test(ch ?? "");
111
+ }
112
+
113
+ function isNameChar(ch) {
114
+ return /[A-Za-z0-9_]/.test(ch ?? "");
115
+ }
116
+
117
+ function isSpace(ch) {
118
+ return ch === " " || ch === "\t" || ch === "\r";
119
+ }
120
+
121
+ function lineContinuationLength(source, offset) {
122
+ if (source[offset] !== "\\") return 0;
123
+ if (source[offset + 1] === "\n") return 2;
124
+ if (source[offset + 1] === "\r" && source[offset + 2] === "\n") return 3;
125
+ return 0;
126
+ }
127
+
128
+ function normalizeSource(source) {
129
+ return String(source).replaceAll("\r\n", "\n");
130
+ }
131
+
132
+ function wordToken(offset) {
133
+ return { type: "word", fragments: [], offset };
134
+ }
135
+
136
+ function pushFragment(word, text, quote) {
137
+ if (text === "") {
138
+ if (quote !== "none") word.fragments.push({ text, quote });
139
+ return;
140
+ }
141
+ const previous = word.fragments.at(-1);
142
+ if (previous?.quote === quote) previous.text += text;
143
+ else word.fragments.push({ text, quote });
144
+ }
145
+
146
+ function substitutionEnd(source, start) {
147
+ if (source[start] === "`") {
148
+ for (let i = start + 1; i < source.length; i++) {
149
+ if (source[i] === "\\") i++;
150
+ else if (source[i] === "`") return i + 1;
151
+ }
152
+ throw new ShellSyntaxError("unterminated command substitution", start);
153
+ }
154
+ if (source[start] !== "$" || source[start + 1] !== "(") return start;
155
+ let depth = source[start + 2] === "(" ? 2 : 1;
156
+ let quote = null;
157
+ for (let i = start + 2 + (depth === 2 ? 1 : 0); i < source.length; i++) {
158
+ const ch = source[i];
159
+ if (ch === "\\") { i++; continue; }
160
+ if (quote) {
161
+ if (ch === quote) quote = null;
162
+ continue;
163
+ }
164
+ if (ch === "'" || ch === '"' || ch === "`") { quote = ch; continue; }
165
+ if (ch === "(") depth++;
166
+ else if (ch === ")" && --depth === 0) return i + 1;
167
+ }
168
+ throw new ShellSyntaxError("unterminated substitution", start);
169
+ }
170
+
171
+ function parameterEnd(source, start) {
172
+ let depth = 1;
173
+ for (let i = start + 2; i < source.length; i++) {
174
+ if (source[i] === "\\") { i++; continue; }
175
+ if (source[i] === "$" && source[i + 1] === "{") { depth++; i++; continue; }
176
+ if (source[i] === "}" && --depth === 0) return i + 1;
177
+ }
178
+ throw new ShellSyntaxError("unterminated parameter expansion", start);
179
+ }
180
+
181
+ // Real shells (dash, bash) are lenient when a script ends before a
182
+ // here-document's terminator line ever appears: whatever was read so far
183
+ // becomes the body instead of raising a syntax error. `strict` disables that
184
+ // leniency so a caller (see growHeredocLine) can use the throw as a signal
185
+ // that more input is still needed.
186
+ function consumeHeredocBodies(source, start, pending, strict) {
187
+ let i = start;
188
+ for (const entry of pending) {
189
+ let body = "";
190
+ while (true) {
191
+ const newline = source.indexOf("\n", i);
192
+ const atEnd = newline === -1;
193
+ const lineEnd = atEnd ? source.length : newline;
194
+ const rawLine = source.slice(i, lineEnd);
195
+ const line = entry.strip ? rawLine.replace(/^\t+/, "") : rawLine;
196
+ if (line === entry.delimiterText) {
197
+ i = atEnd ? lineEnd : newline + 1;
198
+ break;
199
+ }
200
+ if (atEnd) {
201
+ if (strict) {
202
+ throw new ShellSyntaxError(
203
+ `unterminated here-document (wanted '${entry.delimiterText}')`,
204
+ entry.token.offset,
205
+ );
206
+ }
207
+ body += line;
208
+ i = lineEnd;
209
+ break;
210
+ }
211
+ body += `${line}\n`;
212
+ i = newline + 1;
213
+ }
214
+ entry.token.heredoc = { body, expand: entry.expand };
215
+ }
216
+ return i;
217
+ }
218
+
219
+ export function tokenize(source, options = {}) {
220
+ source = normalizeSource(source);
221
+ const strict = Boolean(options.strict);
222
+ const tokens = [];
223
+ let i = 0;
224
+ let current = null;
225
+ let atWordBoundary = true;
226
+ let pendingHeredocs = [];
227
+ let awaitingHeredocDelimiter = false;
228
+ let currentHeredocStrip = false;
229
+
230
+ const finishWord = () => {
231
+ if (current) {
232
+ if (awaitingHeredocDelimiter) {
233
+ pendingHeredocs.push({
234
+ token: current,
235
+ strip: currentHeredocStrip,
236
+ expand: current.fragments.every((fragment) =>
237
+ fragment.quote === "none" && !fragment.text.includes("\u0000")),
238
+ delimiterText: current.fragments.map((fragment) =>
239
+ fragment.text.replace(/\u0000(.)/g, "$1")).join(""),
240
+ });
241
+ awaitingHeredocDelimiter = false;
242
+ }
243
+ tokens.push(current);
244
+ }
245
+ current = null;
246
+ };
247
+ const ensureWord = () => {
248
+ current ??= wordToken(i);
249
+ atWordBoundary = false;
250
+ return current;
251
+ };
252
+
253
+ while (i < source.length) {
254
+ const ch = source[i];
255
+
256
+ // A backslash-newline pair is removed before token recognition. Besides
257
+ // joining ordinary words, doing this here avoids creating a phantom empty
258
+ // word for the indented continuation lines commonly used in exec calls.
259
+ const continuation = lineContinuationLength(source, i);
260
+ if (continuation) {
261
+ i += continuation;
262
+ continue;
263
+ }
264
+
265
+ if (ch === "\n") {
266
+ finishWord();
267
+ tokens.push({ type: "op", value: ";", offset: i });
268
+ i++;
269
+ atWordBoundary = true;
270
+ if (pendingHeredocs.length) {
271
+ i = consumeHeredocBodies(source, i, pendingHeredocs, strict);
272
+ pendingHeredocs = [];
273
+ }
274
+ continue;
275
+ }
276
+ if (isSpace(ch)) {
277
+ finishWord();
278
+ i++;
279
+ atWordBoundary = true;
280
+ continue;
281
+ }
282
+ if (ch === "#" && atWordBoundary && !current) {
283
+ while (i < source.length && source[i] !== "\n") i++;
284
+ continue;
285
+ }
286
+
287
+ const two = source.slice(i, i + 2);
288
+ const fdDup = source.slice(i, i + 4);
289
+ const operatorAhead =
290
+ ["|", ";", "<", ">"].includes(ch) ||
291
+ two === "&&" ||
292
+ two === "||" ||
293
+ two === "2>" ||
294
+ fdDup === "1>&2" ||
295
+ fdDup === "2>&1";
296
+ if (current && operatorAhead) {
297
+ finishWord();
298
+ continue;
299
+ }
300
+ // A descriptor duplication begins with the shorter `2>` redirect token,
301
+ // so it must win before ordinary file redirects are considered.
302
+ if (!current && (fdDup === "1>&2" || fdDup === "2>&1")) {
303
+ tokens.push({ type: "op", value: fdDup, offset: i });
304
+ i += 4;
305
+ atWordBoundary = true;
306
+ continue;
307
+ }
308
+ if (!current && ["&&", "||", ">>", "2>", "2>>"].includes(
309
+ source.slice(i, i + 3) === "2>>" ? source.slice(i, i + 3) : two,
310
+ )) {
311
+ const value = source.slice(i, i + 3) === "2>>" ? "2>>" : two;
312
+ tokens.push({ type: "op", value, offset: i });
313
+ i += value.length;
314
+ atWordBoundary = true;
315
+ continue;
316
+ }
317
+ if (!current && two === "<<" && source[i + 2] === "<") {
318
+ tokens.push({ type: "op", value: "<<<", offset: i });
319
+ i += 3;
320
+ atWordBoundary = true;
321
+ continue;
322
+ }
323
+ if (!current && two === "<<") {
324
+ const value = source[i + 2] === "-" ? "<<-" : "<<";
325
+ tokens.push({ type: "op", value, offset: i });
326
+ currentHeredocStrip = value === "<<-";
327
+ awaitingHeredocDelimiter = true;
328
+ i += value.length;
329
+ atWordBoundary = true;
330
+ continue;
331
+ }
332
+ if (!current && ["|", ";", "<", ">"].includes(ch)) {
333
+ tokens.push({ type: "op", value: ch, offset: i++ });
334
+ atWordBoundary = true;
335
+ continue;
336
+ }
337
+
338
+ if (ch === "'") {
339
+ const word = ensureWord();
340
+ const start = ++i;
341
+ while (i < source.length && source[i] !== "'") i++;
342
+ if (i >= source.length)
343
+ throw new ShellSyntaxError("unterminated single quote", start - 1);
344
+ pushFragment(word, source.slice(start, i), "single");
345
+ i++;
346
+ continue;
347
+ }
348
+
349
+ if (ch === '"') {
350
+ const word = ensureWord();
351
+ i++;
352
+ let text = "";
353
+ let closed = false;
354
+ while (i < source.length) {
355
+ if (source[i] === '"') {
356
+ closed = true;
357
+ i++;
358
+ break;
359
+ }
360
+ const quotedContinuation = lineContinuationLength(source, i);
361
+ if (quotedContinuation) {
362
+ i += quotedContinuation;
363
+ continue;
364
+ }
365
+ if (source[i] === "\\" && i + 1 < source.length) {
366
+ const next = source[i + 1];
367
+ if (next === "$" || next === '"' || next === "\\") {
368
+ text += `\u0000${next}`;
369
+ i += 2;
370
+ continue;
371
+ }
372
+ }
373
+ if ((source[i] === "$" && ["(", "{"].includes(source[i + 1])) || source[i] === "`") {
374
+ const end = source[i + 1] === "{" ? parameterEnd(source, i) : substitutionEnd(source, i);
375
+ text += source.slice(i, end);
376
+ i = end;
377
+ continue;
378
+ }
379
+ text += source[i++];
380
+ }
381
+ if (!closed)
382
+ throw new ShellSyntaxError("unterminated double quote", i);
383
+ pushFragment(word, text, "double");
384
+ continue;
385
+ }
386
+
387
+ if (ch === "\\") {
388
+ const word = ensureWord();
389
+ if (i + 1 >= source.length) {
390
+ // A bare trailing backslash at the very end of the source is what an
391
+ // interactive line looks like the instant Enter is pressed, before
392
+ // the next physical line -- which would complete the continuation
393
+ // pair the branch above handles -- has even been typed. In strict
394
+ // mode (needsMoreInput's interactive-continuation check) that must
395
+ // wait for more input like any other unterminated construct, not
396
+ // silently become a literal "\" word character.
397
+ if (strict) throw new ShellSyntaxError("unterminated line continuation", i);
398
+ pushFragment(word, "\\", "none");
399
+ i++;
400
+ } else {
401
+ pushFragment(word, `\u0000${source[i + 1]}`, "none");
402
+ i += 2;
403
+ }
404
+ continue;
405
+ }
406
+
407
+ const word = ensureWord();
408
+ let text = "";
409
+ while (i < source.length) {
410
+ const c = source[i];
411
+ if ((c === "$" && ["(", "{"].includes(source[i + 1])) || c === "`") {
412
+ const end = source[i + 1] === "{" ? parameterEnd(source, i) : substitutionEnd(source, i);
413
+ text += source.slice(i, end);
414
+ i = end;
415
+ continue;
416
+ }
417
+ const maybeOp =
418
+ ["|", ";", "<", ">"].includes(c) ||
419
+ source.slice(i, i + 2) === "&&" ||
420
+ source.slice(i, i + 2) === "||" ||
421
+ source.slice(i, i + 2) === "2>" ||
422
+ source.slice(i, i + 4) === "1>&2" ||
423
+ source.slice(i, i + 4) === "2>&1";
424
+ if (isSpace(c) || c === "\n" || c === "'" || c === '"' || c === "\\" || maybeOp)
425
+ break;
426
+ text += c;
427
+ i++;
428
+ }
429
+ pushFragment(word, text, "none");
430
+ }
431
+
432
+ finishWord();
433
+ if (pendingHeredocs.length) {
434
+ if (strict) {
435
+ const first = pendingHeredocs[0];
436
+ throw new ShellSyntaxError(
437
+ `unterminated here-document (wanted '${first.delimiterText}')`,
438
+ first.token.offset,
439
+ );
440
+ }
441
+ for (const entry of pendingHeredocs) entry.token.heredoc = { body: "", expand: entry.expand };
442
+ }
443
+ return tokens;
444
+ }
445
+
446
+ function emptyCommand() {
447
+ return { words: [], redirects: [] };
448
+ }
449
+
450
+ export function parse(source) {
451
+ const tokens = tokenize(source);
452
+ const jobs = [];
453
+ let i = 0;
454
+ let connector = null;
455
+
456
+ const skipSemicolons = () => {
457
+ while (tokens[i]?.type === "op" && tokens[i].value === ";") i++;
458
+ };
459
+
460
+ skipSemicolons();
461
+ while (i < tokens.length) {
462
+ const pipeline = [];
463
+ while (true) {
464
+ const command = emptyCommand();
465
+ while (i < tokens.length) {
466
+ const token = tokens[i];
467
+ if (token.type === "op" && ["|", ";", "&&", "||"].includes(token.value))
468
+ break;
469
+ if (token.type === "op" && ["1>&2", "2>&1"].includes(token.value)) {
470
+ command.redirects.push({ op: token.value, target: null });
471
+ i++;
472
+ continue;
473
+ }
474
+ if (token.type === "op" && ["<", ">", ">>", "2>", "2>>", "<<", "<<-", "<<<"].includes(token.value)) {
475
+ const target = tokens[i + 1];
476
+ if (!target || target.type !== "word")
477
+ throw new ShellSyntaxError(
478
+ token.value === "<<<"
479
+ ? `redirection ${token.value} requires a word`
480
+ : token.value.startsWith("<<")
481
+ ? `redirection ${token.value} requires a here-document delimiter`
482
+ : `redirection ${token.value} requires a path`,
483
+ token.offset,
484
+ );
485
+ command.redirects.push({ op: token.value, target, heredoc: target.heredoc ?? null });
486
+ i += 2;
487
+ continue;
488
+ }
489
+ if (token.type !== "word")
490
+ throw new ShellSyntaxError(`unexpected operator ${token.value}`, token.offset);
491
+ command.words.push(token);
492
+ i++;
493
+ }
494
+ if (command.words.length === 0 && command.redirects.length === 0)
495
+ throw new ShellSyntaxError("expected a command", tokens[i]?.offset ?? source.length);
496
+ pipeline.push(command);
497
+
498
+ if (tokens[i]?.value === "|") {
499
+ i++;
500
+ if (!tokens[i])
501
+ throw new ShellSyntaxError("pipeline requires a command", source.length);
502
+ continue;
503
+ }
504
+ break;
505
+ }
506
+
507
+ let negate = false;
508
+ const firstWord = pipeline[0]?.words[0];
509
+ if (firstWord?.fragments.length === 1 && firstWord.fragments[0].quote === "none" &&
510
+ firstWord.fragments[0].text === "!") {
511
+ pipeline[0].words.shift();
512
+ if (pipeline[0].words.length === 0)
513
+ throw new ShellSyntaxError("! requires a command", firstWord.offset);
514
+ negate = true;
515
+ }
516
+ jobs.push({ connector, pipeline, negate });
517
+ const separator = tokens[i]?.value;
518
+ if (!separator) break;
519
+ if (![";", "&&", "||"].includes(separator))
520
+ throw new ShellSyntaxError(`unexpected operator ${separator}`, tokens[i].offset);
521
+ connector = separator;
522
+ i++;
523
+ skipSemicolons();
524
+ if (i >= tokens.length) break;
525
+ }
526
+ return jobs;
527
+ }
528
+
529
+ function parameterValue(name, state) {
530
+ if (name === "?") return String(state.lastStatus);
531
+ if (name === "$") return String(process.pid);
532
+ if (name === "#") return String(Math.max(0, state.args.length - 1));
533
+ if (name === "-") return "";
534
+ if (name === "0") return state.args[0] ?? "bunmsh";
535
+ if (name === "@" || name === "*") return state.args.slice(1).join((state.env.IFS ?? " ")[0] ?? " ");
536
+ if (/^[1-9][0-9]*$/.test(name)) return state.args[Number(name)] ?? "";
537
+ return state.env[name] ?? "";
538
+ }
539
+
540
+ function tildeDirectory(name, state) {
541
+ if (name === "") return state.env.HOME ?? "";
542
+ if (name === "+") return state.env.PWD ?? state.cwd;
543
+ if (name === "-") return state.env.OLDPWD ?? "~-";
544
+ try {
545
+ const entry = readFileSync("/etc/passwd", "utf8").split("\n")
546
+ .find((line) => line.split(":", 2)[0] === name);
547
+ return entry?.split(":")[5] ?? `~${name}`;
548
+ } catch { return `~${name}`; }
549
+ }
550
+
551
+ function expandTildes(text, state, assignment) {
552
+ const pattern = assignment ? /(^|[=:])~([A-Za-z0-9_.+-]*)(?=\/|:|$)/g : /^~([A-Za-z0-9_.+-]*)(?=\/|$)/;
553
+ if (assignment)
554
+ return text.replace(pattern, (_match, prefix, name) => `${prefix}${tildeDirectory(name, state)}`);
555
+ return text.replace(pattern, (_match, name) => tildeDirectory(name, state));
556
+ }
557
+
558
+ function arithmeticValue(source, state) {
559
+ const tokens = source.match(/(?:0[xX][0-9a-fA-F]+|\d+|[A-Za-z_][A-Za-z0-9_]*|\|\||&&|==|!=|<=|>=|<<|>>|[()+\-*/%<>&^|!~])/g) ?? [];
560
+ let i = 0;
561
+ const precedence = { "||": 1, "&&": 2, "|": 3, "^": 4, "&": 5, "==": 6, "!=": 6,
562
+ "<": 7, "<=": 7, ">": 7, ">=": 7, "<<": 8, ">>": 8, "+": 9, "-": 9,
563
+ "*": 10, "/": 10, "%": 10 };
564
+ const atom = () => {
565
+ const token = tokens[i++];
566
+ if (["+", "-", "!", "~"].includes(token)) {
567
+ const value = atom();
568
+ return token === "+" ? value : token === "-" ? -value : token === "!" ? Number(!value) : ~value;
569
+ }
570
+ if (token === "(") {
571
+ const value = expression(0);
572
+ if (tokens[i++] !== ")") throw new ShellSyntaxError("bad arithmetic expression");
573
+ return value;
574
+ }
575
+ if (/^[A-Za-z_]/.test(token ?? "")) return Number(state.env[token] ?? 0) || 0;
576
+ if (token === undefined) throw new ShellSyntaxError("bad arithmetic expression");
577
+ return Number(token);
578
+ };
579
+ const expression = (minimum) => {
580
+ let left = atom();
581
+ while ((precedence[tokens[i]] ?? -1) >= minimum) {
582
+ const op = tokens[i++], priority = precedence[op], right = expression(priority + 1);
583
+ left = { "||": Number(Boolean(left || right)), "&&": Number(Boolean(left && right)),
584
+ "|": left | right, "^": left ^ right, "&": left & right, "==": Number(left === right),
585
+ "!=": Number(left !== right), "<": Number(left < right), "<=": Number(left <= right),
586
+ ">": Number(left > right), ">=": Number(left >= right), "<<": left << right,
587
+ ">>": left >> right, "+": left + right, "-": left - right, "*": left * right,
588
+ "/": Math.trunc(left / right), "%": left % right }[op];
589
+ }
590
+ return left;
591
+ };
592
+ const value = expression(0);
593
+ if (i !== tokens.length || !Number.isFinite(value)) throw new ShellSyntaxError("bad arithmetic expression");
594
+ return String(value | 0);
595
+ }
596
+
597
+ function globPatternRegex(pattern, anchoredStart, anchoredEnd, greedy) {
598
+ let source = "";
599
+ for (const ch of pattern) {
600
+ if (ch === "*") source += greedy ? ".*" : ".*?";
601
+ else if (ch === "?") source += ".";
602
+ else source += ch.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
603
+ }
604
+ return new RegExp(`${anchoredStart ? "^" : ""}${source}${anchoredEnd ? "$" : ""}`);
605
+ }
606
+
607
+ async function parameterExpansion(body, state) {
608
+ if (/^[!%][A-Za-z_][A-Za-z0-9_]*$/.test(body)) {
609
+ const value = parameterValue(body.slice(1), state);
610
+ return body[0] === "!" ? parameterValue(value, state) : String([...value].length);
611
+ }
612
+ if (body.startsWith("#") && /^[A-Za-z_][A-Za-z0-9_]*$/.test(body.slice(1)))
613
+ return String(parameterValue(body.slice(1), state).length);
614
+ const trim = /^([A-Za-z_][A-Za-z0-9_]*|[?$#@*]|[0-9]+)(%%|%|##|#)(.*)$/s.exec(body);
615
+ if (trim) {
616
+ const value = parameterValue(trim[1], state);
617
+ const pattern = stripExpansionMarkers(await expandText(trim[3], state));
618
+ const matches = (part) => globPatternRegex(pattern, true, true, true).test(part);
619
+ if (trim[2] === "%") {
620
+ for (let i = value.length; i >= 0; i--) if (matches(value.slice(i))) return value.slice(0, i);
621
+ } else if (trim[2] === "%%") {
622
+ for (let i = 0; i <= value.length; i++) if (matches(value.slice(i))) return value.slice(0, i);
623
+ } else if (trim[2] === "#") {
624
+ for (let i = 0; i <= value.length; i++) if (matches(value.slice(0, i))) return value.slice(i);
625
+ } else {
626
+ for (let i = value.length; i >= 0; i--) if (matches(value.slice(0, i))) return value.slice(i);
627
+ }
628
+ return value;
629
+ }
630
+ const replace = /^([A-Za-z_][A-Za-z0-9_]*|[?$#@*-]|[0-9]+)\/(\/|#|%)?([^/]*)\/(.*)$/s.exec(body);
631
+ if (replace) {
632
+ const value = parameterValue(replace[1], state);
633
+ const pattern = stripExpansionMarkers(await expandText(replace[3], state));
634
+ const replacement = stripExpansionMarkers(await expandText(replace[4], state));
635
+ let source = "";
636
+ for (const ch of pattern) {
637
+ if (ch === "*") source += ".*";
638
+ else if (ch === "?") source += ".";
639
+ else source += ch.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
640
+ }
641
+ const prefix = replace[2] === "#" ? "^" : "";
642
+ const suffix = replace[2] === "%" ? "$" : "";
643
+ return value.replace(new RegExp(`${prefix}${source}${suffix}`, replace[2] === "/" ? "g" : ""), replacement);
644
+ }
645
+ const match = /^([A-Za-z_][A-Za-z0-9_]*|[?$#@*-]|[0-9]+)(?:(:?[-+=?])(.*))?$/s.exec(body);
646
+ if (!match) throw new ShellSyntaxError(`unsupported parameter expansion: \${${body}}`);
647
+ const [, name, operator, operand = ""] = match;
648
+ const value = parameterValue(name, state);
649
+ const unset = !Object.hasOwn(state.env, name) && !/^[?$#@*0-9]$/.test(name);
650
+ const empty = value === "";
651
+ if (!operator) return value;
652
+ const useEmpty = operator.startsWith(":");
653
+ const op = operator.at(-1);
654
+ const missing = unset || (useEmpty && empty);
655
+ const expandedOperand = async () => stripExpansionMarkers(await expandText(operand, state));
656
+ if (op === "-") return missing ? expandedOperand() : value;
657
+ if (op === "+") return missing ? "" : expandedOperand();
658
+ if (op === "=") {
659
+ if (missing) {
660
+ if (!/^[A-Za-z_]/.test(name)) throw new ShellSyntaxError("cannot assign positional parameter");
661
+ if (state.readonly?.has(name)) throw new ShellSyntaxError(`${name}: is read only`);
662
+ const expanded = await expandedOperand();
663
+ state.env[name] = expanded;
664
+ return expanded;
665
+ }
666
+ return value;
667
+ }
668
+ if (op === "?" && missing) throw new ShellSyntaxError(operand ? await expandedOperand() : `${name}: parameter null or not set`);
669
+ return value;
670
+ }
671
+
672
+ function markedExpansion(value) {
673
+ return `\u0001${value}\u0002`;
674
+ }
675
+
676
+ function stripExpansionMarkers(value) {
677
+ return value.replace(/[\u0001\u0002]/g, "");
678
+ }
679
+
680
+ async function commandSubstitution(source, state) {
681
+ const child = {
682
+ ...state,
683
+ env: { ...state.env },
684
+ aliases: { ...state.aliases },
685
+ readonly: new Set(state.readonly),
686
+ directoryHistory: [...state.directoryHistory],
687
+ tabs: [...state.tabs],
688
+ exitRequested: false,
689
+ exitStatus: 0,
690
+ };
691
+ const output = await execute(source, child, { capture: true });
692
+ state.expansionStatus = output.status;
693
+ return decode(output.stdout).replace(/\n+$/, "");
694
+ }
695
+
696
+ async function expandText(text, state) {
697
+ let output = "";
698
+ for (let i = 0; i < text.length; ) {
699
+ if (text[i] === "\u0000") {
700
+ output += `\u0000${text[i + 1] ?? ""}`;
701
+ i += 2;
702
+ continue;
703
+ }
704
+ if (text[i] === "`") {
705
+ const end = substitutionEnd(text, i);
706
+ output += markedExpansion(await commandSubstitution(text.slice(i + 1, end - 1), state));
707
+ i = end;
708
+ continue;
709
+ }
710
+ if (text[i] !== "$") {
711
+ output += text[i++];
712
+ continue;
713
+ }
714
+ if (text.startsWith("$((", i)) {
715
+ const end = substitutionEnd(text, i);
716
+ output += markedExpansion(arithmeticValue(text.slice(i + 3, end - 2), state));
717
+ i = end;
718
+ continue;
719
+ }
720
+ if (text.startsWith("$(", i)) {
721
+ const end = substitutionEnd(text, i);
722
+ output += markedExpansion(await commandSubstitution(text.slice(i + 2, end - 1), state));
723
+ i = end;
724
+ continue;
725
+ }
726
+ if (text[i + 1] === "{") {
727
+ const end = parameterEnd(text, i);
728
+ output += markedExpansion(await parameterExpansion(text.slice(i + 2, end - 1), state));
729
+ i = end;
730
+ continue;
731
+ }
732
+ const next = text[i + 1];
733
+ if (["?", "$", "#", "@", "*", "-"].includes(next) || /[0-9]/.test(next ?? "")) {
734
+ output += markedExpansion(parameterValue(next, state));
735
+ i += 2;
736
+ continue;
737
+ }
738
+ if (isNameStart(next)) {
739
+ let end = i + 2;
740
+ while (isNameChar(text[end])) end++;
741
+ output += markedExpansion(parameterValue(text.slice(i + 1, end), state));
742
+ i = end;
743
+ continue;
744
+ }
745
+ output += "$";
746
+ i++;
747
+ }
748
+ return output;
749
+ }
750
+
751
+ function splitFields(segments, state) {
752
+ const ifs = state.env.IFS === undefined ? " \t\n" : state.env.IFS;
753
+ const fields = [];
754
+ let current = [];
755
+ let forced = false;
756
+ let afterWhitespaceDelimiter = false;
757
+ const finish = (includeEmpty = false) => {
758
+ if (current.length || forced || includeEmpty) fields.push(current);
759
+ current = [];
760
+ forced = false;
761
+ };
762
+ for (const segment of segments) {
763
+ if (segment.quoted && segment.text === "") forced = true;
764
+ for (const ch of segment.text) {
765
+ if (!segment.quoted && segment.splittable && ifs.includes(ch)) {
766
+ const whitespace = " \t\n".includes(ch);
767
+ finish(!whitespace && !afterWhitespaceDelimiter);
768
+ afterWhitespaceDelimiter = whitespace;
769
+ } else {
770
+ current.push({ ch, quoted: segment.quoted });
771
+ afterWhitespaceDelimiter = false;
772
+ }
773
+ }
774
+ }
775
+ finish();
776
+ return fields;
777
+ }
778
+
779
+ async function pathnameFields(field, state) {
780
+ const value = field.map(({ ch }) => ch).join("");
781
+ if (!field.some(({ ch, quoted }) => !quoted && "*?[".includes(ch))) return [value];
782
+ const pattern = field.map(({ ch, quoted }) => {
783
+ if (!quoted) return ch;
784
+ if (ch === "*") return "[*]";
785
+ if (ch === "?") return "[?]";
786
+ if (ch === "[") return "[[]";
787
+ return ch;
788
+ }).join("");
789
+ const glob = new Bun.Glob(pattern);
790
+ const matches = [...glob.scanSync({ cwd: state.cwd, dot: false, onlyFiles: false })].sort();
791
+ return matches.length ? matches : [value];
792
+ }
793
+
794
+ function braceFields(field) {
795
+ let open = -1;
796
+ for (let i = 0; i < field.length; i++) {
797
+ if (!field[i].quoted && field[i].ch === "{") { open = i; break; }
798
+ }
799
+ if (open < 0) return [field];
800
+ let depth = 0, close = -1;
801
+ const commas = [];
802
+ for (let i = open; i < field.length; i++) {
803
+ if (field[i].quoted) continue;
804
+ if (field[i].ch === "{") depth++;
805
+ else if (field[i].ch === "}" && --depth === 0) { close = i; break; }
806
+ else if (field[i].ch === "," && depth === 1) commas.push(i);
807
+ }
808
+ if (close < 0 || commas.length === 0) return [field];
809
+ const boundaries = [open, ...commas, close];
810
+ const output = [];
811
+ for (let i = 0; i < boundaries.length - 1; i++) {
812
+ const replacement = field.slice(boundaries[i] + 1, boundaries[i + 1]);
813
+ output.push(...braceFields([...field.slice(0, open), ...replacement, ...field.slice(close + 1)]));
814
+ }
815
+ return output;
816
+ }
817
+
818
+ export async function expandWord(word, state, options = {}) {
819
+ if (!options.single && word.fragments.length === 1 &&
820
+ word.fragments[0].quote === "double" && word.fragments[0].text === "$@")
821
+ return state.args.slice(1);
822
+ const segments = [];
823
+ for (let index = 0; index < word.fragments.length; index++) {
824
+ const fragment = word.fragments[index];
825
+ const source = fragment.quote === "none" && !options.noTilde
826
+ ? expandTildes(fragment.text, state, Boolean(options.assignment))
827
+ : fragment.text;
828
+ let text = fragment.quote === "single"
829
+ ? source.replaceAll("\u0000", "")
830
+ : await expandText(source, state);
831
+ if (fragment.quote !== "none") {
832
+ segments.push({ text: stripExpansionMarkers(text.replaceAll("\u0000", "")), quoted: true, splittable: false });
833
+ } else {
834
+ let plain = "";
835
+ let splittable = false;
836
+ const flush = () => {
837
+ if (plain) segments.push({ text: plain, quoted: false, splittable });
838
+ plain = "";
839
+ };
840
+ for (let i = 0; i < text.length; i++) {
841
+ if (text[i] === "\u0001") { flush(); splittable = true; continue; }
842
+ if (text[i] === "\u0002") { flush(); splittable = false; continue; }
843
+ if (text[i] !== "\u0000") { plain += text[i]; continue; }
844
+ flush();
845
+ segments.push({ text: text[++i] ?? "", quoted: true, splittable: false });
846
+ }
847
+ flush();
848
+ }
849
+ }
850
+ if (options.single) return [segments.map(({ text }) => text).join("")];
851
+ const fields = splitFields(segments, state);
852
+ const output = [];
853
+ for (const field of fields)
854
+ for (const expanded of braceFields(field)) output.push(...await pathnameFields(expanded, state));
855
+ return output;
856
+ }
857
+
858
+ function splitAssignment(value) {
859
+ const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s.exec(value);
860
+ return match ? { name: match[1], value: match[2] } : null;
861
+ }
862
+
863
+ function bytes(value = "") {
864
+ return value instanceof Uint8Array ? value : encoder.encode(String(value));
865
+ }
866
+
867
+ function concatBytes(parts) {
868
+ const values = parts.map(bytes);
869
+ const length = values.reduce((sum, value) => sum + value.byteLength, 0);
870
+ const result = new Uint8Array(length);
871
+ let offset = 0;
872
+ for (const value of values) {
873
+ result.set(value, offset);
874
+ offset += value.byteLength;
875
+ }
876
+ return result;
877
+ }
878
+
879
+ function result(status = 0, stdout = "", stderr = "") {
880
+ return { status, stdout: bytes(stdout), stderr: bytes(stderr) };
881
+ }
882
+
883
+ function quoteShellWord(value) {
884
+ return `'${String(value).replaceAll("'", `'\\''`)}'`;
885
+ }
886
+
887
+ function findExecutable(name, state, defaultPath = false) {
888
+ if (!state.pathSearch) return null;
889
+ const path = defaultPath ? DEFAULT_COMMAND_PATH : (environmentValue(state.env, "PATH") ?? "");
890
+ const found = Bun.which(name, { PATH: path, cwd: nativePath(state.cwd) });
891
+ return found ? shellPath(found) : null;
892
+ }
893
+
894
+ function aliasWords(value) {
895
+ const tokens = tokenize(value);
896
+ if (tokens.some((token) => token.type !== "word")) return null;
897
+ return tokens.map((token) => token.fragments
898
+ .map((fragment) => fragment.text.replaceAll("\u0000", ""))
899
+ .join(""));
900
+ }
901
+
902
+ function readonlyError(name) {
903
+ return result(1, "", `bunmsh: ${name}: is read only\n`);
904
+ }
905
+
906
+ function writeStream(stream, data) {
907
+ return new Promise((resolve, reject) => {
908
+ stream.write(data, (error) => error ? reject(error) : resolve());
909
+ });
910
+ }
911
+
912
+ function parsePrintEscapes(value) {
913
+ let stop = false;
914
+ const text = value.replace(/\\(c|n|r|t|b|a|v|\\|0[0-7]{1,3})/g, (_, code) => {
915
+ if (code === "c") {
916
+ stop = true;
917
+ return "";
918
+ }
919
+ const map = { n: "\n", r: "\r", t: "\t", b: "\b", a: "\x07", v: "\v", "\\": "\\" };
920
+ if (code[0] === "0") return String.fromCharCode(parseInt(code.slice(1), 8));
921
+ return map[code] ?? code;
922
+ });
923
+ return { text, stop };
924
+ }
925
+
926
+ function parentDirectory(path) {
927
+ if (path === "/") return "/";
928
+ const end = path.endsWith("/") ? path.length - 1 : path.length;
929
+ const separator = path.lastIndexOf("/", end - 1);
930
+ return separator <= 0 ? "/" : path.slice(0, separator);
931
+ }
932
+
933
+ async function changeDirectory(state, target, { print = false } = {}) {
934
+ const path = isAbsolute(nativePath(target))
935
+ ? nativePath(target)
936
+ : resolvePath(nativePath(state.cwd), nativePath(target));
937
+ try {
938
+ const resolved = shellPath(resolvePath(path));
939
+ const stat = await Bun.file(nativePath(resolved)).stat();
940
+ if (!stat.isDirectory())
941
+ return result(1, "", `bunmsh: cd: ${target}: not a directory\n`);
942
+ const previous = state.cwd;
943
+ state.directoryHistory.push(previous);
944
+ state.cwd = resolved;
945
+ state.tabs[state.activeTab] = resolved;
946
+ state.env.OLDPWD = previous;
947
+ state.env.PWD = resolved;
948
+ return result(0, print ? `${resolved}\n` : "");
949
+ } catch (error) {
950
+ return result(1, "", `bunmsh: cd: ${target}: ${error.message}\n`);
951
+ }
952
+ }
953
+
954
+ async function previousChildDirectory(state) {
955
+ for (let i = state.directoryHistory.length - 1; i >= 0; i--) {
956
+ const candidate = state.directoryHistory[i];
957
+ if (parentDirectory(candidate) !== state.cwd) continue;
958
+ try {
959
+ if ((await Bun.file(candidate).stat()).isDirectory())
960
+ return changeDirectory(state, candidate);
961
+ } catch {}
962
+ }
963
+ return result(1, "", "bunmsh: //: no previous child directory\n");
964
+ }
965
+
966
+ async function sourceFile(argv, state) {
967
+ if (!argv[1]) return result(2, "", `bunmsh: ${argv[0]}: missing file operand\n`);
968
+ let path = argv[1];
969
+ if (!path.includes("/")) {
970
+ const found = (environmentValue(state.env, "PATH") ?? "").split(pathDelimiter)
971
+ .map((directory) => resolvePath(directory || state.cwd, path))
972
+ .find((candidate) => {
973
+ try { return statSync(candidate).isFile(); } catch { return false; }
974
+ });
975
+ if (found) path = found;
976
+ } else if (!isAbsolute(path)) path = resolvePath(state.cwd, path);
977
+ try {
978
+ const source = await Bun.file(path).text();
979
+ const previousArgs = state.args;
980
+ if (argv.length > 2) state.args = [previousArgs[0], ...argv.slice(2)];
981
+ try {
982
+ return await execute(source, state, { capture: true });
983
+ } finally {
984
+ state.args = previousArgs;
985
+ }
986
+ } catch (error) {
987
+ return result(1, "", `bunmsh: ${argv[0]}: ${argv[1]}: ${error.message}\n`);
988
+ }
989
+ }
990
+
991
+ function testStat(path, state) {
992
+ try { return statSync(isAbsolute(path) ? path : resolvePath(state.cwd, path)); }
993
+ catch { return null; }
994
+ }
995
+
996
+ function evaluateTest(argv, state) {
997
+ if (argv.length === 0) return false;
998
+ if (argv[0] === "!") return !evaluateTest(argv.slice(1), state);
999
+ const or = argv.indexOf("-o");
1000
+ if (or >= 0) return evaluateTest(argv.slice(0, or), state) || evaluateTest(argv.slice(or + 1), state);
1001
+ const and = argv.indexOf("-a");
1002
+ if (and >= 0) return evaluateTest(argv.slice(0, and), state) && evaluateTest(argv.slice(and + 1), state);
1003
+ if (argv.length === 1) return argv[0].length > 0;
1004
+ if (argv.length === 2) {
1005
+ const [op, value] = argv;
1006
+ if (op === "-n") return value.length > 0;
1007
+ if (op === "-z") return value.length === 0;
1008
+ const stat = testStat(value, state);
1009
+ if (op === "-e") return stat !== null;
1010
+ if (op === "-f") return Boolean(stat?.isFile());
1011
+ if (op === "-d") return Boolean(stat?.isDirectory());
1012
+ if (op === "-b") return Boolean(stat?.isBlockDevice());
1013
+ if (op === "-c") return Boolean(stat?.isCharacterDevice());
1014
+ if (op === "-p") return Boolean(stat?.isFIFO());
1015
+ if (op === "-S") return Boolean(stat?.isSocket());
1016
+ if (op === "-L" || op === "-h") {
1017
+ try { return lstatSync(isAbsolute(value) ? value : resolvePath(state.cwd, value)).isSymbolicLink(); }
1018
+ catch { return false; }
1019
+ }
1020
+ if (op === "-s") return Boolean(stat && stat.size > 0);
1021
+ if (op === "-r" || op === "-w" || op === "-x") {
1022
+ try {
1023
+ const mode = op === "-r" ? fsConstants.R_OK : op === "-w" ? fsConstants.W_OK : fsConstants.X_OK;
1024
+ accessSync(isAbsolute(value) ? value : resolvePath(state.cwd, value), mode);
1025
+ return true;
1026
+ } catch { return false; }
1027
+ }
1028
+ return false;
1029
+ }
1030
+ if (argv.length === 3) {
1031
+ const [left, op, right] = argv;
1032
+ if (op === "=" || op === "==") return left === right;
1033
+ if (op === "!=") return left !== right;
1034
+ if (["-eq", "-ne", "-gt", "-ge", "-lt", "-le"].includes(op)) {
1035
+ const a = Number(left), b = Number(right);
1036
+ if (!Number.isInteger(a) || !Number.isInteger(b)) throw new Error("integer expression expected");
1037
+ return { "-eq": a === b, "-ne": a !== b, "-gt": a > b, "-ge": a >= b,
1038
+ "-lt": a < b, "-le": a <= b }[op];
1039
+ }
1040
+ const a = testStat(left, state), b = testStat(right, state);
1041
+ if (op === "-nt") return Boolean(a && (!b || a.mtimeMs > b.mtimeMs));
1042
+ if (op === "-ot") return Boolean(b && (!a || a.mtimeMs < b.mtimeMs));
1043
+ if (op === "-ef") return Boolean(a && b && a.dev === b.dev && a.ino === b.ino);
1044
+ }
1045
+ throw new Error("unsupported expression");
1046
+ }
1047
+
1048
+ const builtins = {
1049
+ ":": async () => result(),
1050
+ true: async () => result(),
1051
+ false: async () => result(1),
1052
+ // Execution is handled specially by runCommand so command can alter command
1053
+ // lookup without creating another parsed command.
1054
+ command: null,
1055
+ builtin: null,
1056
+ __builtin: null,
1057
+ time: null,
1058
+ // This is handled specially by runCommandArgv because its output is
1059
+ // unbounded and therefore cannot be represented by the normal buffered
1060
+ // builtin result.
1061
+ yes: null,
1062
+ whence: async (argv, state) => {
1063
+ let pathOnly = false, verbose = false, i = 1;
1064
+ for (; i < argv.length; i++) {
1065
+ if (argv[i] === "--") { i++; break; }
1066
+ if (argv[i] === "-p") pathOnly = true;
1067
+ else if (argv[i] === "-v") verbose = true;
1068
+ else if (argv[i].startsWith("-") && argv[i] !== "-")
1069
+ return result(1, "", `bunmsh: whence: ${argv[i]}: unknown option\n`);
1070
+ else break;
1071
+ }
1072
+ let status = 0, output = "";
1073
+ for (const name of argv.slice(i)) {
1074
+ let description;
1075
+ if (pathOnly) {
1076
+ const path = findExecutable(name, state);
1077
+ description = path ? `${path}\n` : null;
1078
+ } else description = describeCommand(name, state, verbose, false);
1079
+ if (description === null || description.endsWith(" not found\n")) status = 1;
1080
+ if (description) output += description;
1081
+ }
1082
+ return result(status, output);
1083
+ },
1084
+ which: async (argv, state) => {
1085
+ let i = 1;
1086
+ if (argv[i] === "--") i++;
1087
+ if (i >= argv.length)
1088
+ return result(1, "", "bunmsh: which: missing command name\n");
1089
+ let status = 0, output = "";
1090
+ for (; i < argv.length; i++) {
1091
+ // `which` is an explicit PATH query, so it remains useful even while
1092
+ // direct command-name lookup is disabled with `tab path off`.
1093
+ const path = findExecutable(argv[i], { ...state, pathSearch: true });
1094
+ if (path) output += `${path}\n`;
1095
+ else status = 1;
1096
+ }
1097
+ return result(status, output);
1098
+ },
1099
+ type: async (argv, state) => {
1100
+ let status = 0, output = "";
1101
+ for (const name of argv.slice(1)) {
1102
+ const description = describeCommand(name, state, true, false);
1103
+ if (description.endsWith(" not found\n")) status = 1;
1104
+ output += description;
1105
+ }
1106
+ return result(status, output);
1107
+ },
1108
+ alias: async (argv, state) => {
1109
+ if (argv.length === 1) {
1110
+ const output = Object.entries(state.aliases)
1111
+ .sort(([a], [b]) => a.localeCompare(b))
1112
+ .map(([name, words]) => `alias ${name}=${quoteShellWord(words.join(" "))}\n`)
1113
+ .join("");
1114
+ return result(0, output);
1115
+ }
1116
+ let status = 0;
1117
+ let output = "";
1118
+ let stderr = "";
1119
+ for (const item of argv.slice(1)) {
1120
+ const equal = item.indexOf("=");
1121
+ if (equal < 0) {
1122
+ const words = state.aliases[item];
1123
+ if (words) output += `alias ${item}=${quoteShellWord(words.join(" "))}\n`;
1124
+ else {
1125
+ status = 1;
1126
+ stderr += `bunmsh: alias: ${item}: not found\n`;
1127
+ }
1128
+ continue;
1129
+ }
1130
+ const name = item.slice(0, equal);
1131
+ const words = aliasWords(item.slice(equal + 1));
1132
+ if (!name || words === null) {
1133
+ status = 1;
1134
+ stderr += `bunmsh: alias: ${name || item}: invalid alias\n`;
1135
+ } else state.aliases[name] = words;
1136
+ }
1137
+ return result(status, output, stderr);
1138
+ },
1139
+ unalias: async (argv, state) => {
1140
+ let i = 1;
1141
+ if (argv[i] === "-a") {
1142
+ state.aliases = {};
1143
+ return result();
1144
+ }
1145
+ if (argv[i] === "--") i++;
1146
+ if (i === argv.length)
1147
+ return result(1, "", "bunmsh: unalias: missing alias name\n");
1148
+ let status = 0;
1149
+ let stderr = "";
1150
+ for (; i < argv.length; i++) {
1151
+ if (Object.hasOwn(state.aliases, argv[i])) delete state.aliases[argv[i]];
1152
+ else {
1153
+ status = 1;
1154
+ stderr += `bunmsh: unalias: ${argv[i]}: not found\n`;
1155
+ }
1156
+ }
1157
+ return result(status, "", stderr);
1158
+ },
1159
+ test: async (argv, state) => {
1160
+ try { return result(evaluateTest(argv.slice(1), state) ? 0 : 1); }
1161
+ catch (error) { return result(2, "", `bunmsh: test: ${error.message}\n`); }
1162
+ },
1163
+ "[": async (argv, state) => {
1164
+ if (argv.at(-1) !== "]") return result(2, "", "bunmsh: [: missing ]\n");
1165
+ try { return result(evaluateTest(argv.slice(1, -1), state) ? 0 : 1); }
1166
+ catch (error) { return result(2, "", `bunmsh: [: ${error.message}\n`); }
1167
+ },
1168
+ echo: async (argv) => {
1169
+ let newline = true;
1170
+ let start = 1;
1171
+ if (argv[1] === "-n") {
1172
+ newline = false;
1173
+ start = 2;
1174
+ }
1175
+ return result(0, argv.slice(start).join(" ") + (newline ? "\n" : ""));
1176
+ },
1177
+ print: async (argv) => {
1178
+ let raw = false;
1179
+ let newline = true;
1180
+ let separator = " ";
1181
+ let terminator = "\n";
1182
+ let fd = 1;
1183
+ let i = 1;
1184
+ for (; i < argv.length; i++) {
1185
+ const arg = argv[i];
1186
+ if (arg === "--") {
1187
+ i++;
1188
+ break;
1189
+ }
1190
+ if (!arg.startsWith("-") || arg === "-") break;
1191
+ if (/^-u[0-9]+$/.test(arg)) {
1192
+ fd = Number(arg.slice(2));
1193
+ continue;
1194
+ }
1195
+ if (arg === "-r" || arg === "-R") raw = true;
1196
+ else if (arg === "-n") newline = false;
1197
+ else if (arg === "-l") separator = "\n";
1198
+ else if (arg === "-N") separator = terminator = "\0";
1199
+ else return result(1, "", `bunmsh: print: ${arg}: unsupported option\n`);
1200
+ }
1201
+ let output = argv.slice(i).join(separator);
1202
+ if (!raw) {
1203
+ const expanded = parsePrintEscapes(output);
1204
+ output = expanded.text;
1205
+ if (expanded.stop) newline = false;
1206
+ }
1207
+ if (newline) output += terminator;
1208
+ if (fd === 2) return result(0, "", output);
1209
+ if (fd !== 1)
1210
+ return result(1, "", `bunmsh: print: file descriptor ${fd} is not supported yet\n`);
1211
+ return result(0, output);
1212
+ },
1213
+ printf: async (argv) => {
1214
+ if (argv.length < 2) return result(2, "", "bunmsh: printf: missing format\n");
1215
+ const format = argv[1];
1216
+ let ai = 2;
1217
+ let output = "";
1218
+ for (let i = 0; i < format.length; i++) {
1219
+ if (format[i] === "\\" && i + 1 < format.length) {
1220
+ const parsed = parsePrintEscapes(format.slice(i, i + 2));
1221
+ output += parsed.text;
1222
+ i++;
1223
+ continue;
1224
+ }
1225
+ if (format[i] !== "%") {
1226
+ output += format[i];
1227
+ continue;
1228
+ }
1229
+ if (format[i + 1] === "%") {
1230
+ output += "%";
1231
+ i++;
1232
+ continue;
1233
+ }
1234
+ const match = /^%([0-9]*)([sdi])/.exec(format.slice(i));
1235
+ if (!match) return result(2, "", `bunmsh: printf: unsupported format near ${format.slice(i)}\n`);
1236
+ const width = Number(match[1] || 0);
1237
+ const kind = match[2];
1238
+ const value = argv[ai++] ?? (kind === "s" ? "" : "0");
1239
+ let rendered = kind === "s" ? value : String(Number.parseInt(value, 10) || 0);
1240
+ if (width > rendered.length)
1241
+ rendered = (match[1]?.startsWith("0") ? "0" : " ").repeat(width - rendered.length) + rendered;
1242
+ output += rendered;
1243
+ i += match[0].length - 1;
1244
+ }
1245
+ return result(0, output);
1246
+ },
1247
+ read: async (argv, state, input) => {
1248
+ let raw = false, i = 1;
1249
+ if (argv[i] === "-r") { raw = true; i++; }
1250
+ if (argv[i] === "--") i++;
1251
+ const names = argv.slice(i);
1252
+ if (!names.length) names.push("REPLY");
1253
+ if (names.some((name) => !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)))
1254
+ return result(2, "", "bunmsh: read: invalid variable name\n");
1255
+ let line, gotInput;
1256
+ if (state.readLines) {
1257
+ gotInput = state.readLines.length > 0;
1258
+ line = state.readLines.shift() ?? "";
1259
+ } else {
1260
+ const reader = fallbackInput(input).getReader();
1261
+ const chunks = [];
1262
+ let foundNewline = false;
1263
+ try {
1264
+ while (!foundNewline) {
1265
+ const { value, done } = await reader.read();
1266
+ if (done) break;
1267
+ const newline = value.indexOf(10);
1268
+ chunks.push(newline < 0 ? value : value.slice(0, newline));
1269
+ foundNewline = newline >= 0;
1270
+ }
1271
+ if (foundNewline) try { await reader.cancel(); } catch {}
1272
+ } finally { try { reader.releaseLock(); } catch {} }
1273
+ line = decoder.decode(concatBytes(chunks));
1274
+ gotInput = foundNewline || chunks.length > 0;
1275
+ }
1276
+ line = line.replace(/\r$/, "");
1277
+ if (!raw) line = line.replace(/\\(.)/gs, "$1");
1278
+ const ifs = state.env.IFS ?? " \t\n";
1279
+ const escaped = ifs.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
1280
+ const fields = ifs ? line.trim().split(new RegExp(`[${escaped}]+`)) : [line];
1281
+ for (let n = 0; n < names.length; n++) {
1282
+ if (state.readonly.has(names[n])) return readonlyError(names[n]);
1283
+ state.env[names[n]] = n === names.length - 1 ? fields.slice(n).join(" ") : (fields[n] ?? "");
1284
+ }
1285
+ return result(gotInput ? 0 : 1);
1286
+ },
1287
+ pwd: async (_argv, state) => result(0, `${state.cwd}\n`),
1288
+ cd: async (argv, state) => {
1289
+ if (argv[1] === "//") return previousChildDirectory(state);
1290
+ if (argv[1] === "-") {
1291
+ if (!state.env.OLDPWD)
1292
+ return result(1, "", "bunmsh: cd: OLDPWD is not set\n");
1293
+ return changeDirectory(state, state.env.OLDPWD, { print: true });
1294
+ }
1295
+ const target = argv[1] ?? state.env.HOME;
1296
+ if (!target) return result(1, "", "bunmsh: cd: HOME is not set\n");
1297
+ return changeDirectory(state, target);
1298
+ },
1299
+ chdir: async (argv, state) => builtins.cd(["cd", ...argv.slice(1)], state),
1300
+ tab: async (argv, state) => {
1301
+ if (argv.length > 3 || (argv.length > 2 && !["mouse", "path", "s", "save"].includes(argv[1])))
1302
+ return result(1, "", "bunmsh: tab: usage: tab [n|x|c|l|r|s|save [d|dedupe]|mouse [on|off|true|false]|path [on|off|true|false]|number]\n");
1303
+ const action = argv[1];
1304
+ const activate = (index) => {
1305
+ state.activeTab = index;
1306
+ state.cwd = state.tabs[index];
1307
+ state.env.PWD = state.cwd;
1308
+ };
1309
+ if (action === "n" || (action === undefined && state.tabs.length === 1)) {
1310
+ state.tabs.push(state.cwd);
1311
+ activate(state.tabs.length - 1);
1312
+ return result();
1313
+ }
1314
+ if (action === undefined || action === "r") {
1315
+ activate((state.activeTab + 1) % state.tabs.length);
1316
+ return result();
1317
+ }
1318
+ if (action === "l") {
1319
+ activate((state.activeTab - 1 + state.tabs.length) % state.tabs.length);
1320
+ return result();
1321
+ }
1322
+ if (action === "x" || action === "c") {
1323
+ if (state.tabs.length === 1)
1324
+ return result(1, "", "bunmsh: tab: cannot close the last tab\n");
1325
+ state.tabs.splice(state.activeTab, 1);
1326
+ activate(Math.min(state.activeTab, state.tabs.length - 1));
1327
+ return result();
1328
+ }
1329
+ if (action === "s" || action === "save") {
1330
+ const modifier = argv[2];
1331
+ if (modifier !== undefined && !["d", "dedupe"].includes(modifier))
1332
+ return result(1, "", "bunmsh: tab: save: expected d or dedupe\n");
1333
+ try {
1334
+ if (modifier === undefined) {
1335
+ const path = await saveBunmshHistory(state, state.env);
1336
+ return result(0, `${shellPath(path)}\n`);
1337
+ }
1338
+ // Rewrites the whole history file to drop duplicates, unlike the
1339
+ // plain save above — see dedupeBunmshHistory for why that is only
1340
+ // safe to do as an explicit, occasional choice.
1341
+ const { path, raced, count } = await dedupeBunmshHistory(state, state.env);
1342
+ const warning = raced
1343
+ ? " (warning: another session wrote to it during cleanup; some of its entries may have been lost — rerun if needed)"
1344
+ : "";
1345
+ return result(0, `${shellPath(path)}: ${count} unique entries${warning}\n`);
1346
+ } catch (error) {
1347
+ return result(1, "", `bunmsh: tab: cannot save history: ${error.message}\n`);
1348
+ }
1349
+ }
1350
+ if (action === "mouse") {
1351
+ const mode = argv[2];
1352
+ if (mode === undefined) state.mouseTracking = !state.mouseTracking;
1353
+ else if (mode === "on" || mode === "true") state.mouseTracking = true;
1354
+ else if (mode === "off" || mode === "false") state.mouseTracking = false;
1355
+ else return result(1, "", "bunmsh: tab: mouse: expected on, off, true, or false\n");
1356
+ return result();
1357
+ }
1358
+ if (action === "path") {
1359
+ const mode = argv[2];
1360
+ if (mode === undefined) state.pathSearch = !state.pathSearch;
1361
+ else if (mode === "on" || mode === "true") state.pathSearch = true;
1362
+ else if (mode === "off" || mode === "false") state.pathSearch = false;
1363
+ else return result(1, "", "bunmsh: tab: path: expected on, off, true, or false\n");
1364
+ return result();
1365
+ }
1366
+ if (/^[1-9][0-9]*$/.test(action ?? "")) {
1367
+ const index = Number(action) - 1;
1368
+ if (index >= state.tabs.length)
1369
+ return result(1, "", `bunmsh: tab: ${action}: no such tab\n`);
1370
+ activate(index);
1371
+ return result();
1372
+ }
1373
+ return result(1, "", "bunmsh: tab: usage: tab [n|x|c|l|r|s|save|mouse [on|off|true|false]|path [on|off|true|false]|number]\n");
1374
+ },
1375
+ "-": async (_argv, state) => builtins.cd(["cd", "-"], state),
1376
+ "~": async (_argv, state) => builtins.cd(["cd"], state),
1377
+ "..": async (_argv, state) => changeDirectory(state, ".."),
1378
+ "//": async (_argv, state) => previousChildDirectory(state),
1379
+ export: async (argv, state) => {
1380
+ if (argv.length === 1) {
1381
+ const listing = Object.entries(state.env)
1382
+ .sort(([a], [b]) => a.localeCompare(b))
1383
+ .map(([name, value]) => `export ${name}=${JSON.stringify(value)}\n`)
1384
+ .join("");
1385
+ return result(0, listing);
1386
+ }
1387
+ for (const item of argv.slice(1)) {
1388
+ const assignment = splitAssignment(item);
1389
+ if (assignment) {
1390
+ if (state.readonly.has(assignment.name)) return readonlyError(assignment.name);
1391
+ state.env[assignment.name] = assignment.value;
1392
+ }
1393
+ else if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(item))
1394
+ state.env[item] ??= "";
1395
+ else return result(1, "", `bunmsh: export: ${item}: invalid name\n`);
1396
+ }
1397
+ return result();
1398
+ },
1399
+ unset: async (argv, state) => {
1400
+ let i = 1;
1401
+ if (argv[i] === "-v" || argv[i] === "--") i++;
1402
+ for (const name of argv.slice(i)) {
1403
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
1404
+ return result(1, "", `bunmsh: unset: ${name}: invalid name\n`);
1405
+ if (state.readonly.has(name)) return readonlyError(name);
1406
+ delete state.env[name];
1407
+ }
1408
+ return result();
1409
+ },
1410
+ readonly: async (argv, state) => {
1411
+ if (argv.length === 1 || (argv.length === 2 && argv[1] === "-p")) {
1412
+ const output = [...state.readonly].sort()
1413
+ .map((name) => `readonly ${name}=${quoteShellWord(state.env[name] ?? "")}\n`)
1414
+ .join("");
1415
+ return result(0, output);
1416
+ }
1417
+ let i = argv[1] === "--" ? 2 : 1;
1418
+ for (; i < argv.length; i++) {
1419
+ const assignment = splitAssignment(argv[i]);
1420
+ const name = assignment?.name ?? argv[i];
1421
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
1422
+ return result(1, "", `bunmsh: readonly: ${name}: invalid name\n`);
1423
+ if (assignment) {
1424
+ if (state.readonly.has(name)) return readonlyError(name);
1425
+ state.env[name] = assignment.value;
1426
+ } else state.env[name] ??= "";
1427
+ state.readonly.add(name);
1428
+ }
1429
+ return result();
1430
+ },
1431
+ env: async (argv, state, input, context = {}) => {
1432
+ let env = { ...state.env };
1433
+ let i = 1;
1434
+ for (; i < argv.length; i++) {
1435
+ const item = argv[i];
1436
+ if (item === "--") { i++; break; }
1437
+ if (item === "-i" || item === "--ignore-environment") { env = {}; continue; }
1438
+ if (item === "-u" || item === "--unset") {
1439
+ const name = argv[++i];
1440
+ if (!name) return result(2, "", "bunmsh: env: option requires a name\n");
1441
+ delete env[name];
1442
+ continue;
1443
+ }
1444
+ if (item.startsWith("--unset=")) { delete env[item.slice(8)]; continue; }
1445
+ const assignment = splitAssignment(item);
1446
+ if (!assignment) break;
1447
+ env[assignment.name] = assignment.value;
1448
+ }
1449
+ if (i >= argv.length)
1450
+ return result(0, Object.entries(env).map(([k, v]) => `${k}=${v}\n`).join(""));
1451
+ const childState = {
1452
+ ...state,
1453
+ env,
1454
+ directoryHistory: [...state.directoryHistory],
1455
+ tabs: [...state.tabs],
1456
+ readonly: new Set(state.readonly),
1457
+ };
1458
+ return runCommandArgv(
1459
+ argv.slice(i),
1460
+ childState,
1461
+ input,
1462
+ context.captureStdout ?? true,
1463
+ context.captureStderr ?? true,
1464
+ context.options ?? {},
1465
+ );
1466
+ },
1467
+ exec: async (argv, state, input, context = {}) => {
1468
+ if (argv.length === 1) return result();
1469
+ const execution = await runCommandArgv(
1470
+ argv.slice(1), state, input,
1471
+ context.captureStdout ?? true,
1472
+ context.captureStderr ?? true,
1473
+ context.options ?? {},
1474
+ );
1475
+ state.exitRequested = true;
1476
+ state.exitStatus = execution.status;
1477
+ return execution;
1478
+ },
1479
+ exit: async (argv, state) => {
1480
+ const status = argv[1] === undefined ? state.lastStatus : Number(argv[1]);
1481
+ if (!Number.isInteger(status))
1482
+ return result(2, "", `bunmsh: exit: ${argv[1]}: numeric argument required\n`);
1483
+ state.exitRequested = true;
1484
+ state.exitStatus = status & 255;
1485
+ return result(state.exitStatus);
1486
+ },
1487
+ shift: async (argv, state) => {
1488
+ const count = argv[1] === undefined ? 1 : Number(argv[1]);
1489
+ if (!Number.isInteger(count) || count < 0)
1490
+ return result(1, "", `bunmsh: shift: ${argv[1]}: bad number\n`);
1491
+ if (count > state.args.length - 1) return result(1);
1492
+ state.args.splice(1, count);
1493
+ return result();
1494
+ },
1495
+ getopts: async (argv, state) => {
1496
+ if (argv.length < 3)
1497
+ return result(2, "", "bunmsh: getopts: usage: getopts optstring name [arg ...]\n");
1498
+ const optstring = argv[1];
1499
+ const variable = argv[2];
1500
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(variable))
1501
+ return result(2, "", `bunmsh: getopts: ${variable}: invalid name\n`);
1502
+ const args = argv.length > 3 ? argv.slice(3) : state.args.slice(1);
1503
+ let index = Number(state.env.OPTIND ?? "1");
1504
+ if (!Number.isInteger(index) || index < 1) index = 1;
1505
+ let offset = state.getoptsOffset ?? 1;
1506
+ const current = args[index - 1];
1507
+ if (!current || current === "--" || !current.startsWith("-") || current === "-") {
1508
+ if (current === "--") state.env.OPTIND = String(index + 1);
1509
+ state.getoptsOffset = 1;
1510
+ return result(1);
1511
+ }
1512
+ const option = current[offset];
1513
+ const position = optstring.indexOf(option);
1514
+ const requiresArgument = position >= 0 && optstring[position + 1] === ":";
1515
+ let stderr = "";
1516
+ delete state.env.OPTARG;
1517
+ if (position < 0 || option === ":") {
1518
+ state.env[variable] = "?";
1519
+ if (optstring[0] === ":") state.env.OPTARG = option;
1520
+ else stderr = `bunmsh: getopts: -${option}: unknown option\n`;
1521
+ } else if (requiresArgument) {
1522
+ if (offset + 1 < current.length) state.env.OPTARG = current.slice(offset + 1);
1523
+ else if (index < args.length) state.env.OPTARG = args[index++];
1524
+ else {
1525
+ state.env[variable] = optstring[0] === ":" ? ":" : "?";
1526
+ if (optstring[0] === ":") state.env.OPTARG = option;
1527
+ else stderr = `bunmsh: getopts: -${option}: argument expected\n`;
1528
+ state.env.OPTIND = String(index + 1);
1529
+ state.getoptsOffset = 1;
1530
+ return result(0, "", stderr);
1531
+ }
1532
+ state.env[variable] = option;
1533
+ offset = current.length;
1534
+ } else state.env[variable] = option;
1535
+ if (offset + 1 >= current.length) {
1536
+ index++;
1537
+ offset = 1;
1538
+ } else offset++;
1539
+ state.env.OPTIND = String(index);
1540
+ state.getoptsOffset = offset;
1541
+ return result(0, "", stderr);
1542
+ },
1543
+ eval: async (argv, state) => execute(argv.slice(1).join(" "), state, { capture: true }),
1544
+ ".": async (argv, state) => sourceFile(argv, state),
1545
+ source: async (argv, state) => sourceFile(argv, state),
1546
+ realpath: async (argv, state) => {
1547
+ let output = "";
1548
+ try {
1549
+ for (const item of argv.slice(1))
1550
+ output += `${realpathSync(isAbsolute(item) ? item : resolvePath(state.cwd, item))}\n`;
1551
+ return result(0, output);
1552
+ } catch (error) {
1553
+ return result(1, "", `bunmsh: realpath: ${error.message}\n`);
1554
+ }
1555
+ },
1556
+ umask: async (argv) => {
1557
+ if (argv.length === 1) return result(0, `${process.umask().toString(8).padStart(4, "0")}\n`);
1558
+ if (argv.length !== 2 || !/^[0-7]{1,4}$/.test(argv[1]))
1559
+ return result(1, "", "bunmsh: umask: bad mask\n");
1560
+ process.umask(Number.parseInt(argv[1], 8));
1561
+ return result();
1562
+ },
1563
+ kill: async (argv) => {
1564
+ let signal = "SIGTERM";
1565
+ let i = 1;
1566
+ if (argv[i] === "-l") {
1567
+ const names = Object.keys(osConstants.signals).map((name) => name.replace(/^SIG/, ""));
1568
+ return result(0, `${names.join(" ")}\n`);
1569
+ }
1570
+ if (argv[i]?.startsWith("-") && argv[i] !== "-") {
1571
+ const value = argv[i++].slice(1).toUpperCase();
1572
+ signal = /^\d+$/.test(value) ? Number(value) : `SIG${value.replace(/^SIG/, "")}`;
1573
+ }
1574
+ if (i >= argv.length) return result(2, "", "bunmsh: kill: missing pid\n");
1575
+ let status = 0, stderr = "";
1576
+ for (; i < argv.length; i++) {
1577
+ const pid = Number(argv[i]);
1578
+ // Signal 0 asks whether a PID can be signalled without touching it,
1579
+ // which taskkill has no way to express — and which process.kill does
1580
+ // answer on Windows. Everything else goes through taskkill there.
1581
+ if (process.platform === "win32" && signal !== 0) {
1582
+ if (!Number.isInteger(pid) || pid <= 0) {
1583
+ status = 1;
1584
+ stderr += `bunmsh: kill: ${argv[i]}: arguments must be process ids\n`;
1585
+ continue;
1586
+ }
1587
+ const failure = await runWindowsKill(pid);
1588
+ if (failure) { status = 1; stderr += `bunmsh: kill: ${argv[i]}: ${failure}\n`; }
1589
+ continue;
1590
+ }
1591
+ try { process.kill(pid, signal); }
1592
+ catch (error) { status = 1; stderr += `bunmsh: kill: ${argv[i]}: ${error.message}\n`; }
1593
+ }
1594
+ return result(status, "", stderr);
1595
+ },
1596
+ set: async (argv, state) => {
1597
+ if (argv.length === 1)
1598
+ return result(0, Object.entries(state.env).sort().map(([k, v]) => `${k}=${JSON.stringify(v)}\n`).join(""));
1599
+ if (argv[1] === "--") {
1600
+ state.args = [state.args[0], ...argv.slice(2)];
1601
+ return result();
1602
+ }
1603
+ return result(2, "", "bunmsh: set: option handling is not implemented yet\n");
1604
+ },
1605
+ };
1606
+
1607
+ const runFindBuiltin = async (argv, state) => {
1608
+ const output = await runFind(argv, state, process.platform, (command) =>
1609
+ runCommandArgv(command, state, null, true, true));
1610
+ return result(output.status, output.stdout, output.stderr);
1611
+ };
1612
+
1613
+ if (findIsRegularBuiltin()) builtins.find = runFindBuiltin;
1614
+
1615
+ // These are used only when PATH does not provide an executable of the same
1616
+ // name. `builtin name` can still select them explicitly.
1617
+ export function bunShellFallbackArgv(argv) {
1618
+ let commandArgv = argv;
1619
+ if (argv[0] === "ls")
1620
+ commandArgv = commandArgv.filter((value, index) => index === 0 || value !== "--color=auto");
1621
+ if (argv[0] === "cp")
1622
+ commandArgv = commandArgv.map((value, index) =>
1623
+ index > 0 && /^-[^-]/.test(value) ? `-${value.slice(1).replaceAll("r", "R")}` : value);
1624
+ return commandArgv;
1625
+ }
1626
+
1627
+ async function runBunShellFallback(argv, state) {
1628
+ const shell = Bun.$`${bunShellFallbackArgv(argv)}`
1629
+ .cwd(nativePath(state.cwd))
1630
+ .env(state.env)
1631
+ .nothrow();
1632
+ if (state.pipelineChild) {
1633
+ const output = await shell;
1634
+ return result(output.exitCode);
1635
+ }
1636
+ const output = await shell.quiet();
1637
+ return result(output.exitCode, output.stdout, output.stderr);
1638
+ }
1639
+
1640
+ async function runFancyLsFallback(argv, state) {
1641
+ const output = fancyLs(argv, state);
1642
+ return result(output.status, output.stdout, output.stderr);
1643
+ }
1644
+
1645
+ async function runBunShellCpFallback(argv, state) {
1646
+ const cmd = IS_COMPILED
1647
+ ? [...EXECUTABLE_COMMAND, "--bun-shell-fallback", ...bunShellFallbackArgv(argv)]
1648
+ : [...BUN_RUNTIME_COMMAND, BUNMSH_ENTRY, "--bun-shell-fallback", ...bunShellFallbackArgv(argv)];
1649
+ const proc = Bun.spawn({
1650
+ cmd,
1651
+ cwd: nativePath(state.cwd),
1652
+ env: {
1653
+ ...state.env,
1654
+ BUN_ENABLE_EXPERIMENTAL_SHELL_BUILTINS: "1",
1655
+ },
1656
+ stdin: "inherit",
1657
+ stdout: state.pipelineChild ? "inherit" : "pipe",
1658
+ stderr: state.pipelineChild ? "inherit" : "pipe",
1659
+ });
1660
+ if (state.pipelineChild) return result(await proc.exited);
1661
+ const [status, stdout, stderr] = await Promise.all([
1662
+ proc.exited,
1663
+ new Response(proc.stdout).arrayBuffer(),
1664
+ new Response(proc.stderr).arrayBuffer(),
1665
+ ]);
1666
+ return result(status, new Uint8Array(stdout), new Uint8Array(stderr));
1667
+ }
1668
+
1669
+ async function runReflectedProcess(cmd, state) {
1670
+ const inherit = state.pipelineChild || Boolean(process.stdout.isTTY);
1671
+ try {
1672
+ const proc = Bun.spawn({
1673
+ cmd,
1674
+ cwd: nativePath(state.cwd),
1675
+ env: state.env,
1676
+ stdin: "inherit",
1677
+ stdout: inherit ? "inherit" : "pipe",
1678
+ stderr: inherit ? "inherit" : "pipe",
1679
+ });
1680
+ if (inherit) return result(await proc.exited);
1681
+ const [status, stdout, stderr] = await Promise.all([
1682
+ proc.exited,
1683
+ new Response(proc.stdout).arrayBuffer(),
1684
+ new Response(proc.stderr).arrayBuffer(),
1685
+ ]);
1686
+ return result(status, new Uint8Array(stdout), new Uint8Array(stderr));
1687
+ } catch (error) {
1688
+ return result(127, "", `bunmsh: ${cmd[0]}: ${error.message}\n`);
1689
+ }
1690
+ }
1691
+
1692
+ function runBunmshFallback(argv, state) {
1693
+ const cmd = IS_COMPILED
1694
+ ? [...EXECUTABLE_COMMAND, ...argv.slice(1)]
1695
+ : [...BUN_RUNTIME_COMMAND, BUNMSH_ENTRY, ...argv.slice(1)];
1696
+ return runReflectedProcess(cmd, state);
1697
+ }
1698
+
1699
+ function runBunFallback(argv, state) {
1700
+ if (!IS_COMPILED)
1701
+ return runReflectedProcess([...BUN_RUNTIME_COMMAND, ...argv.slice(1)], state);
1702
+ const executable = process.argv0.includes("/") || process.argv0.includes("\\")
1703
+ ? process.argv0
1704
+ : (Bun.which(process.argv0) || process.argv0);
1705
+ return runReflectedProcess([executable, ...argv.slice(1)], state);
1706
+ }
1707
+
1708
+ export async function executeBunShellFallback(argv) {
1709
+ const output = await Bun.$`${argv}`.nothrow();
1710
+ return output.exitCode;
1711
+ }
1712
+
1713
+ async function runCatFallback(argv, state, input) {
1714
+ const parsed = parseCatOperands(argv, "cat");
1715
+ if (parsed.error) return result(1, "", parsed.error);
1716
+ const operands = parsed.operands;
1717
+
1718
+ const chunks = [];
1719
+ let status = 0;
1720
+ let stderr = "";
1721
+ const writer = state.pipelineChild ? Bun.stdout.writer() : null;
1722
+ const emit = async (chunk) => {
1723
+ if (writer) {
1724
+ writer.write(chunk);
1725
+ await writer.flush();
1726
+ } else chunks.push(bytes(chunk));
1727
+ };
1728
+
1729
+ try {
1730
+ for (const operand of operands) {
1731
+ try {
1732
+ const stream = operand === "-"
1733
+ ? (input instanceof ReadableStream ? input : Bun.stdin.stream())
1734
+ : Bun.file(nativePath(isAbsolute(nativePath(operand))
1735
+ ? operand
1736
+ : resolvePath(nativePath(state.cwd), nativePath(operand)))).stream();
1737
+ for await (const chunk of stream) await emit(chunk);
1738
+ } catch (error) {
1739
+ status = 1;
1740
+ stderr += `bunmsh: cat: ${operand}: ${error.message}\n`;
1741
+ }
1742
+ }
1743
+ } finally {
1744
+ if (writer) {
1745
+ try { writer.end(); } catch {}
1746
+ }
1747
+ }
1748
+ return result(status, writer ? "" : concatBytes(chunks), stderr);
1749
+ }
1750
+
1751
+ function fallbackInput(input) {
1752
+ return input instanceof ReadableStream ? input : Bun.stdin.stream();
1753
+ }
1754
+
1755
+ async function readFallbackInput(input) {
1756
+ return new Uint8Array(await new Response(fallbackInput(input)).arrayBuffer());
1757
+ }
1758
+
1759
+ async function readFallbackFiles(operands, state, input) {
1760
+ if (operands.length === 0 || (operands.length === 1 && operands[0] === "-"))
1761
+ return readFallbackInput(input);
1762
+ const parts = [];
1763
+ for (const operand of operands) {
1764
+ if (operand === "-") parts.push(await readFallbackInput(input));
1765
+ else {
1766
+ const path = isAbsolute(nativePath(operand))
1767
+ ? nativePath(operand)
1768
+ : resolvePath(nativePath(state.cwd), nativePath(operand));
1769
+ parts.push(new Uint8Array(await Bun.file(path).arrayBuffer()));
1770
+ }
1771
+ }
1772
+ return concatBytes(parts);
1773
+ }
1774
+
1775
+ async function runTacFallback(argv, state, input) {
1776
+ let operands = argv.slice(1);
1777
+ if (operands[0] === "--") operands = operands.slice(1);
1778
+ if (operands.some((value) => value.startsWith("-") && value !== "-"))
1779
+ return result(1, "", "bunmsh: tac: options are not supported\n");
1780
+ if (operands.length === 0) operands = ["-"];
1781
+
1782
+ const output = [];
1783
+ let status = 0;
1784
+ let stderr = "";
1785
+ for (const operand of operands) {
1786
+ try {
1787
+ const data = await readFallbackFiles([operand], state, input);
1788
+ const records = [];
1789
+ let start = 0;
1790
+ for (let i = 0; i < data.byteLength; i++) {
1791
+ if (data[i] !== 10) continue;
1792
+ records.push(data.slice(start, i + 1));
1793
+ start = i + 1;
1794
+ }
1795
+ if (start < data.byteLength) records.push(data.slice(start));
1796
+ records.reverse();
1797
+ output.push(concatBytes(records));
1798
+ } catch (error) {
1799
+ status = 1;
1800
+ stderr += `bunmsh: tac: ${operand}: ${error.message}\n`;
1801
+ }
1802
+ }
1803
+ return result(status, concatBytes(output), stderr);
1804
+ }
1805
+
1806
+ async function runHeadFallback(argv, state, input) {
1807
+ let count = 10, i = 1, bytesMode = false;
1808
+ if (argv[i] === "-n") count = Number(argv[++i]), i++;
1809
+ else if (argv[i] === "-c") bytesMode = true, count = Number(argv[++i]), i++;
1810
+ else if (/^-c\d+$/.test(argv[i] ?? "")) bytesMode = true, count = Number(argv[i++].slice(2));
1811
+ else if (/^-\d+$/.test(argv[i] ?? "")) count = Number(argv[i++].slice(1));
1812
+ if (!Number.isInteger(count) || count < 0)
1813
+ return result(1, "", "bunmsh: head: invalid line count\n");
1814
+ const operands = argv.slice(i);
1815
+ if (operands.length) {
1816
+ try {
1817
+ const data = await readFallbackFiles(operands, state, input);
1818
+ if (bytesMode) return result(0, data.slice(0, count));
1819
+ return result(0, decoder.decode(data).split(/(?<=\n)/).slice(0, count).join(""));
1820
+ } catch (error) { return result(1, "", `bunmsh: head: ${error.message}\n`); }
1821
+ }
1822
+ const reader = fallbackInput(input).getReader();
1823
+ const output = [];
1824
+ let lines = 0;
1825
+ try {
1826
+ let totalBytes = 0;
1827
+ while (bytesMode ? totalBytes < count : lines < count) {
1828
+ const { value, done } = await reader.read();
1829
+ if (done) break;
1830
+ let end = bytesMode ? Math.min(value.byteLength, count - totalBytes) : value.byteLength;
1831
+ if (!bytesMode) for (let j = 0; j < value.byteLength; j++) {
1832
+ if (value[j] === 10 && ++lines >= count) { end = j + 1; break; }
1833
+ }
1834
+ output.push(value.slice(0, end));
1835
+ totalBytes += end;
1836
+ if (end < value.byteLength) break;
1837
+ }
1838
+ } finally {
1839
+ try { await reader.cancel(); } catch {}
1840
+ }
1841
+ return result(0, concatBytes(output));
1842
+ }
1843
+
1844
+ async function runTextFilter(argv, state, input, kind) {
1845
+ try {
1846
+ let args = argv.slice(1), reverse = false, numeric = false, unique = false;
1847
+ let tailCount = 10, tailFromStart = false;
1848
+ if (kind === "tail") {
1849
+ if (args[0] === "-n") {
1850
+ const count = args[1] ?? "";
1851
+ tailFromStart = /^\+\d+$/.test(count);
1852
+ tailCount = Number(count);
1853
+ args = args.slice(2);
1854
+ } else if (/^-n\+\d+$/.test(args[0] ?? "")) {
1855
+ tailFromStart = true;
1856
+ tailCount = Number(args.shift().slice(2));
1857
+ }
1858
+ else if (/^-\d+$/.test(args[0] ?? "")) tailCount = Number(args.shift().slice(1));
1859
+ if (!Number.isInteger(tailCount) || tailCount < 0)
1860
+ return result(1, "", "bunmsh: tail: invalid line count\n");
1861
+ }
1862
+ if (kind === "sort") {
1863
+ while (args[0]?.startsWith("-") && args[0] !== "-") {
1864
+ const option = args.shift();
1865
+ reverse ||= option.includes("r"); numeric ||= option.includes("n"); unique ||= option.includes("u");
1866
+ }
1867
+ }
1868
+ const text = decoder.decode(await readFallbackFiles(args, state, input));
1869
+ let lines = text.split("\n");
1870
+ const trailing = lines.at(-1) === "";
1871
+ if (trailing) lines.pop();
1872
+ if (kind === "tail") {
1873
+ lines = tailFromStart
1874
+ ? lines.slice(Math.max(0, tailCount - 1))
1875
+ : lines.slice(-tailCount);
1876
+ } else {
1877
+ lines.sort((a, b) => numeric ? Number(a) - Number(b) : a.localeCompare(b));
1878
+ if (unique) lines = lines.filter((line, index) => index === 0 || line !== lines[index - 1]);
1879
+ if (reverse) lines.reverse();
1880
+ }
1881
+ return result(0, lines.length ? `${lines.join("\n")}\n` : "");
1882
+ } catch (error) { return result(1, "", `bunmsh: ${kind}: ${error.message}\n`); }
1883
+ }
1884
+
1885
+ async function runWcFallback(argv, state, input) {
1886
+ let flags = "", i = 1;
1887
+ while (/^-[lwc]+$/.test(argv[i] ?? "")) flags += argv[i++].slice(1);
1888
+ if (!flags) flags = "lwc";
1889
+ try {
1890
+ const data = await readFallbackFiles(argv.slice(i), state, input);
1891
+ const text = decoder.decode(data);
1892
+ const values = [];
1893
+ if (flags.includes("l")) values.push((text.match(/\n/g) ?? []).length);
1894
+ if (flags.includes("w")) values.push(text.trim() ? text.trim().split(/\s+/).length : 0);
1895
+ if (flags.includes("c")) values.push(data.byteLength);
1896
+ return result(0, `${values.join(" ")}\n`);
1897
+ } catch (error) { return result(1, "", `bunmsh: wc: ${error.message}\n`); }
1898
+ }
1899
+
1900
+ function expandTrSet(value) {
1901
+ const escapes = { a: "\x07", b: "\b", f: "\f", n: "\n", r: "\r", t: "\t", v: "\v", "\\": "\\" };
1902
+ const characters = [];
1903
+ for (let i = 0; i < value.length; i++) {
1904
+ if (value[i] !== "\\") {
1905
+ characters.push(value[i]);
1906
+ continue;
1907
+ }
1908
+ const octal = value.slice(i + 1).match(/^[0-7]{1,3}/)?.[0];
1909
+ if (octal) {
1910
+ characters.push(String.fromCharCode(Number.parseInt(octal, 8)));
1911
+ i += octal.length;
1912
+ continue;
1913
+ }
1914
+ const escaped = value[++i];
1915
+ characters.push(escapes[escaped] ?? escaped ?? "\\");
1916
+ }
1917
+
1918
+ let output = "";
1919
+ for (let i = 0; i < characters.length; i++) {
1920
+ if (i + 2 < characters.length && characters[i + 1] === "-") {
1921
+ const first = characters[i].charCodeAt(0);
1922
+ const last = characters[i + 2].charCodeAt(0);
1923
+ if (first <= last) {
1924
+ for (let code = first; code <= last; code++) output += String.fromCharCode(code);
1925
+ i += 2;
1926
+ continue;
1927
+ }
1928
+ }
1929
+ output += characters[i];
1930
+ }
1931
+ return output;
1932
+ }
1933
+
1934
+ async function runTrFallback(argv, _state, input) {
1935
+ let del = false, i = 1;
1936
+ if (argv[i] === "-d") del = true, i++;
1937
+ if (!argv[i] || (!del && !argv[i + 1])) return result(1, "", "bunmsh: tr: missing operand\n");
1938
+ const from = expandTrSet(argv[i++]);
1939
+ const to = expandTrSet(argv[i] ?? "");
1940
+ const text = decoder.decode(await readFallbackInput(input));
1941
+ let output = "";
1942
+ for (const ch of text) {
1943
+ const index = from.indexOf(ch);
1944
+ if (index < 0) output += ch;
1945
+ else if (!del) output += to[Math.min(index, Math.max(0, to.length - 1))] ?? "";
1946
+ }
1947
+ return result(0, output);
1948
+ }
1949
+
1950
+ // `curl` only ever runs when the system has none, so it inherits the
1951
+ // streaming rules the other fallbacks use: write straight through when the
1952
+ // bytes are headed for the terminal or a pipeline stage, buffer when the
1953
+ // caller is capturing them.
1954
+ async function runCurlFallback(argv, state, input, context = {}) {
1955
+ const streamStdout = state.pipelineChild ||
1956
+ (!context.captureStdout && !context.options?.stdoutSink);
1957
+ const streamStderr = state.pipelineChild || !context.captureStderr;
1958
+ const stdout = [];
1959
+ const stderr = [];
1960
+ const io = {
1961
+ cwd: nativePath(state.cwd),
1962
+ env: state.env,
1963
+ stdoutIsTTY: streamStdout && Boolean(process.stdout.isTTY),
1964
+ readStdin: () => readFallbackInput(input),
1965
+ async writeStdout(chunk) {
1966
+ const data = bytes(chunk);
1967
+ if (streamStdout) await writeStream(process.stdout, data); else stdout.push(data);
1968
+ },
1969
+ async writeStderr(text) {
1970
+ const data = bytes(text);
1971
+ if (streamStderr) await writeStream(process.stderr, data); else stderr.push(data);
1972
+ },
1973
+ };
1974
+ try {
1975
+ const status = await runCurl(argv, io);
1976
+ return result(status, concatBytes(stdout), concatBytes(stderr));
1977
+ } catch (error) {
1978
+ return result(2, concatBytes(stdout), concatBytes([...stderr, bytes(`curl: ${error.message}\n`)]));
1979
+ }
1980
+ }
1981
+
1982
+ async function runTeeFallback(argv, state, input) {
1983
+ let append = false, i = 1;
1984
+ if (argv[i] === "-a") append = true, i++;
1985
+ const streams = argv.slice(i).map((name) => createWriteStream(
1986
+ isAbsolute(nativePath(name)) ? nativePath(name) : resolvePath(nativePath(state.cwd), nativePath(name)),
1987
+ { flags: append ? "a" : "w" },
1988
+ ));
1989
+ const output = [];
1990
+ try {
1991
+ for await (const chunk of fallbackInput(input)) {
1992
+ const data = bytes(chunk);
1993
+ if (state.pipelineChild) await writeStream(process.stdout, data); else output.push(data);
1994
+ await Promise.all(streams.map((stream) => writeStream(stream, data)));
1995
+ }
1996
+ } finally { for (const stream of streams) stream.end(); }
1997
+ return result(0, state.pipelineChild ? "" : concatBytes(output));
1998
+ }
1999
+
2000
+ async function runHashFallback(argv, state, input, algorithm) {
2001
+ const operands = argv.slice(1);
2002
+ try {
2003
+ const data = await readFallbackFiles(operands, state, input);
2004
+ const digest = createHash(algorithm).update(data).digest("hex");
2005
+ return result(0, `${digest}${operands.length === 1 && operands[0] !== "-" ? ` ${operands[0]}` : ""}\n`);
2006
+ } catch (error) { return result(1, "", `bunmsh: ${algorithm}sum: ${error.message}\n`); }
2007
+ }
2008
+
2009
+ function runMktempFallback(argv, state) {
2010
+ const directory = argv.includes("-d");
2011
+ const template = argv.find((value, index) => index > 0 && !value.startsWith("-")) ?? "tmp.XXXXXX";
2012
+ if (!template.endsWith("XXXXXX")) return result(1, "", "bunmsh: mktemp: template must end in XXXXXX\n");
2013
+ const prefix = template.slice(0, -6);
2014
+ for (let attempt = 0; attempt < 100; attempt++) {
2015
+ const suffix = Math.random().toString(36).slice(2, 8).padEnd(6, "0");
2016
+ const shown = `${prefix}${suffix}`;
2017
+ const path = isAbsolute(nativePath(shown)) ? nativePath(shown) : resolvePath(nativePath(state.cwd), nativePath(shown));
2018
+ try {
2019
+ if (directory) mkdirSync(path); else closeSync(openSync(path, "wx", 0o600));
2020
+ return result(0, `${shellPath(path)}\n`);
2021
+ } catch (error) { if (error.code !== "EEXIST") return result(1, "", `bunmsh: mktemp: ${error.message}\n`); }
2022
+ }
2023
+ return result(1, "", "bunmsh: mktemp: cannot create temporary file\n");
2024
+ }
2025
+
2026
+ async function runSleepFallback(argv) {
2027
+ if (argv.length !== 2) return result(1, "", "bunmsh: sleep: usage: sleep duration\n");
2028
+ const match = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/.exec(argv[1]);
2029
+ if (!match) return result(1, "", `bunmsh: sleep: invalid duration: ${argv[1]}\n`);
2030
+ const scale = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000 }[match[2] ?? "s"];
2031
+ await Bun.sleep(Number(match[1]) * scale);
2032
+ return result();
2033
+ }
2034
+
2035
+ function runRmdirFallback(argv, state) {
2036
+ let parents = false, operands = argv.slice(1);
2037
+ if (operands[0] === "-p" || operands[0] === "--parents") parents = true, operands.shift();
2038
+ if (!operands.length) return result(1, "", "bunmsh: rmdir: missing operand\n");
2039
+ let status = 0, stderr = "";
2040
+ for (const operand of operands) {
2041
+ let path = isAbsolute(nativePath(operand)) ? nativePath(operand) : resolvePath(nativePath(state.cwd), nativePath(operand));
2042
+ try {
2043
+ rmdirSync(path);
2044
+ if (parents) {
2045
+ let parent = pathDirname(path);
2046
+ while (parent !== path) {
2047
+ try { rmdirSync(parent); } catch { break; }
2048
+ path = parent;
2049
+ parent = pathDirname(path);
2050
+ }
2051
+ }
2052
+ } catch (error) { status = 1; stderr += `bunmsh: rmdir: ${operand}: ${error.message}\n`; }
2053
+ }
2054
+ return result(status, "", stderr);
2055
+ }
2056
+
2057
+ function runDateFallback(argv) {
2058
+ const date = new Date();
2059
+ if (argv.length === 1) return result(0, `${date.toString()}\n`);
2060
+ if (argv.length !== 2 || !argv[1].startsWith("+"))
2061
+ return result(1, "", "bunmsh: date: only +FORMAT is supported\n");
2062
+ const pad = (value) => String(value).padStart(2, "0");
2063
+ const values = {
2064
+ "%Y": date.getFullYear(), "%m": pad(date.getMonth() + 1), "%d": pad(date.getDate()),
2065
+ "%H": pad(date.getHours()), "%M": pad(date.getMinutes()), "%S": pad(date.getSeconds()),
2066
+ "%s": Math.floor(date.getTime() / 1000), "%F": `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`,
2067
+ "%T": `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`, "%%": "%",
2068
+ };
2069
+ return result(0, `${argv[1].slice(1).replace(/%[YmdHMSsFT%]/g, (key) => values[key])}\n`);
2070
+ }
2071
+
2072
+ function grepFiles(operands, state, recursive) {
2073
+ const output = [];
2074
+ const visit = (shown) => {
2075
+ const path = isAbsolute(nativePath(shown))
2076
+ ? nativePath(shown)
2077
+ : resolvePath(nativePath(state.cwd), nativePath(shown));
2078
+ const stat = statSync(path);
2079
+ if (!stat.isDirectory()) { output.push({ shown, path }); return; }
2080
+ if (!recursive) throw new Error(`${shown}: is a directory`);
2081
+ for (const entry of readdirSync(path, { withFileTypes: true }))
2082
+ visit(`${shown.replace(/\/$/, "")}/${entry.name}`);
2083
+ };
2084
+ for (const operand of operands) visit(operand);
2085
+ return output;
2086
+ }
2087
+
2088
+ function grepBasicRegex(pattern) {
2089
+ let output = "", inClass = false;
2090
+ for (let i = 0; i < pattern.length; i++) {
2091
+ const ch = pattern[i];
2092
+ if (ch === "[") inClass = true;
2093
+ if (ch === "]" && inClass) inClass = false;
2094
+ if (!inClass && ch === "\\" && i + 1 < pattern.length && "+?|(){}".includes(pattern[i + 1])) {
2095
+ output += pattern[++i];
2096
+ continue;
2097
+ }
2098
+ if (!inClass && "+?|(){}".includes(ch)) output += `\\${ch}`;
2099
+ else output += ch;
2100
+ }
2101
+ return output;
2102
+ }
2103
+
2104
+ function grepPosixClasses(pattern) {
2105
+ const classes = {
2106
+ alnum: "A-Za-z0-9", alpha: "A-Za-z", blank: " \\t", digit: "0-9",
2107
+ lower: "a-z", space: "\\s", upper: "A-Z", word: "A-Za-z0-9_",
2108
+ xdigit: "A-Fa-f0-9",
2109
+ };
2110
+ return pattern.replace(/\[\[:(alnum|alpha|blank|digit|lower|space|upper|word|xdigit):\]\]/g,
2111
+ (_match, name) => `[${classes[name]}]`);
2112
+ }
2113
+
2114
+ async function runGrepFallback(argv, state, input, context = {}) {
2115
+ let flags = "", colorMode = "never", i = 1;
2116
+ while (argv[i]?.startsWith("-") && argv[i] !== "-") {
2117
+ if (argv[i] === "--") { i++; break; }
2118
+ const colorOption = /^--colou?r(?:=(always|auto|never))?$/.exec(argv[i]);
2119
+ if (colorOption) {
2120
+ colorMode = colorOption[1] ?? "always";
2121
+ i++;
2122
+ continue;
2123
+ }
2124
+ if (!/^-[EFiqvnorx]+$/.test(argv[i]))
2125
+ return result(2, "", `bunmsh: grep: unsupported option: ${argv[i]}\n`);
2126
+ flags += argv[i++].slice(1);
2127
+ }
2128
+ const pattern = argv[i++];
2129
+ if (pattern === undefined) return result(2, "", "bunmsh: grep: missing pattern\n");
2130
+ let regex;
2131
+ const regexSource = flags.includes("F")
2132
+ ? pattern.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&")
2133
+ : grepPosixClasses(flags.includes("E") ? pattern : grepBasicRegex(pattern));
2134
+ try { regex = new RegExp(flags.includes("x") ? `^(?:${regexSource})$` : regexSource, `${flags.includes("i") ? "i" : ""}${flags.includes("o") ? "g" : ""}`); }
2135
+ catch (error) { return result(2, "", `bunmsh: grep: ${error.message}\n`); }
2136
+ const invert = flags.includes("v"), quiet = flags.includes("q"), numbers = flags.includes("n"), only = flags.includes("o");
2137
+ const color = colorMode === "always" ||
2138
+ (colorMode === "auto" && !context.captureStdout && Boolean(process.stdout.isTTY));
2139
+ const red = "\x1b[01;31m", reset = "\x1b[m";
2140
+ const colored = (value) => color && value.length ? `${red}${value}${reset}` : value;
2141
+ const highlightRegex = color && !only
2142
+ ? new RegExp(regex.source, regex.flags.includes("g") ? regex.flags : `${regex.flags}g`)
2143
+ : null;
2144
+ let matched = false;
2145
+ const output = [];
2146
+ const processLine = (line, lineNumber, label, showLabel) => {
2147
+ regex.lastIndex = 0;
2148
+ const isMatch = regex.test(line);
2149
+ regex.lastIndex = 0;
2150
+ if (isMatch === invert) return false;
2151
+ matched = true;
2152
+ if (quiet) return true;
2153
+ const prefix = `${showLabel ? `${label}:` : ""}${numbers ? `${lineNumber}:` : ""}`;
2154
+ if (only) {
2155
+ if (invert) return false;
2156
+ for (const match of line.matchAll(regex))
2157
+ if (match[0].length) output.push(`${prefix}${colored(match[0])}\n`);
2158
+ } else {
2159
+ const rendered = !invert && highlightRegex
2160
+ ? line.replace(highlightRegex, (match) => colored(match))
2161
+ : line;
2162
+ output.push(`${prefix}${rendered}\n`);
2163
+ }
2164
+ return false;
2165
+ };
2166
+
2167
+ const operands = argv.slice(i);
2168
+ if (operands.length) {
2169
+ try {
2170
+ const files = grepFiles(operands, state, flags.includes("r"));
2171
+ const showLabel = files.length > 1 || flags.includes("r");
2172
+ for (const file of files) {
2173
+ const lines = (await Bun.file(file.path).text()).split("\n");
2174
+ if (lines.at(-1) === "") lines.pop();
2175
+ for (let line = 0; line < lines.length; line++)
2176
+ if (processLine(lines[line], line + 1, file.shown, showLabel)) return result(0);
2177
+ }
2178
+ } catch (error) { return result(2, "", `bunmsh: grep: ${error.message}\n`); }
2179
+ } else {
2180
+ const reader = fallbackInput(input).getReader();
2181
+ const decode = new TextDecoder();
2182
+ let pending = "", lineNumber = 0;
2183
+ try {
2184
+ while (true) {
2185
+ const { value, done } = await reader.read();
2186
+ pending += decode.decode(value, { stream: !done });
2187
+ const lines = pending.split("\n");
2188
+ pending = done ? "" : lines.pop();
2189
+ if (done && lines.at(-1) === "") lines.pop();
2190
+ for (const line of lines)
2191
+ if (processLine(line, ++lineNumber, "", false)) { await reader.cancel(); return result(0); }
2192
+ if (done) break;
2193
+ }
2194
+ } finally { try { reader.releaseLock(); } catch {} }
2195
+ }
2196
+ return result(matched ? 0 : 1, output.join(""));
2197
+ }
2198
+
2199
+ function splitSedCommands(script) {
2200
+ const commands = [];
2201
+ let start = 0, escaped = false;
2202
+ for (let i = 0; i < script.length; i++) {
2203
+ if (escaped) { escaped = false; continue; }
2204
+ if (script[i] === "\\") { escaped = true; continue; }
2205
+ if (script[i] === ";") {
2206
+ commands.push(script.slice(start, i).trim());
2207
+ start = i + 1;
2208
+ }
2209
+ }
2210
+ commands.push(script.slice(start).trim());
2211
+ return commands.filter(Boolean);
2212
+ }
2213
+
2214
+ function readSedField(command, start, delimiter) {
2215
+ let value = "";
2216
+ for (let i = start; i < command.length; i++) {
2217
+ if (command[i] === "\\" && i + 1 < command.length) {
2218
+ if (command[i + 1] === delimiter) value += command[++i];
2219
+ else value += command[i] + command[++i];
2220
+ } else if (command[i] === delimiter) return [value, i + 1];
2221
+ else value += command[i];
2222
+ }
2223
+ throw new Error("unterminated substitute command");
2224
+ }
2225
+
2226
+ function sedReplacement(value) {
2227
+ let output = "";
2228
+ for (let i = 0; i < value.length; i++) {
2229
+ if (value[i] === "\\" && /[1-9]/.test(value[i + 1] ?? "")) output += `$${value[++i]}`;
2230
+ else if (value[i] === "\\" && i + 1 < value.length) output += value[++i];
2231
+ else if (value[i] === "&") output += "$&";
2232
+ else if (value[i] === "$") output += "$$";
2233
+ else output += value[i];
2234
+ }
2235
+ return output;
2236
+ }
2237
+
2238
+ function compileSedCommand(source, extended) {
2239
+ const numberedPrint = /^(\d+)p$/.exec(source);
2240
+ if (numberedPrint) return { type: "print", line: Number(numberedPrint[1]) };
2241
+ if (!source.startsWith("s") || source.length < 2)
2242
+ throw new Error(`unsupported command: ${source}`);
2243
+ const delimiter = source[1];
2244
+ const [pattern, replacementStart] = readSedField(source, 2, delimiter);
2245
+ const [replacement, flagsStart] = readSedField(source, replacementStart, delimiter);
2246
+ const flags = source.slice(flagsStart);
2247
+ if (!/^[gp]*$/.test(flags)) throw new Error(`unsupported substitute flags: ${flags}`);
2248
+ const regexSource = grepPosixClasses(extended ? pattern : grepBasicRegex(pattern));
2249
+ return {
2250
+ type: "substitute",
2251
+ regex: new RegExp(regexSource, flags.includes("g") ? "g" : ""),
2252
+ replacement: sedReplacement(replacement),
2253
+ print: flags.includes("p"),
2254
+ };
2255
+ }
2256
+
2257
+ function applySed(text, commands, quiet) {
2258
+ const trailingNewline = text.endsWith("\n");
2259
+ const lines = text.split("\n");
2260
+ if (trailingNewline) lines.pop();
2261
+ const output = [];
2262
+ for (let index = 0; index < lines.length; index++) {
2263
+ let line = lines[index];
2264
+ for (const command of commands) {
2265
+ if (command.type === "print") {
2266
+ if (command.line === index + 1) output.push(line);
2267
+ continue;
2268
+ }
2269
+ command.regex.lastIndex = 0;
2270
+ const matched = command.regex.test(line);
2271
+ command.regex.lastIndex = 0;
2272
+ if (matched) {
2273
+ line = line.replace(command.regex, command.replacement);
2274
+ if (command.print) output.push(line);
2275
+ }
2276
+ }
2277
+ if (!quiet) output.push(line);
2278
+ }
2279
+ if (!output.length) return "";
2280
+ return output.join("\n") + (trailingNewline ? "\n" : "");
2281
+ }
2282
+
2283
+ async function runSedFallback(argv, state, input) {
2284
+ let quiet = false, extended = false, inPlace = false, i = 1;
2285
+ const scripts = [];
2286
+ while (i < argv.length && argv[i].startsWith("-") && argv[i] !== "-") {
2287
+ const option = argv[i++];
2288
+ if (option === "--") break;
2289
+ if (option === "-e") {
2290
+ if (i >= argv.length) return result(1, "", "bunmsh: sed: option requires an argument: -e\n");
2291
+ scripts.push(argv[i++]);
2292
+ continue;
2293
+ }
2294
+ if (option.startsWith("-e")) { scripts.push(option.slice(2)); continue; }
2295
+ if (!/^-[nEri]+$/.test(option))
2296
+ return result(1, "", `bunmsh: sed: unsupported option: ${option}\n`);
2297
+ quiet ||= option.includes("n");
2298
+ extended ||= option.includes("E") || option.includes("r");
2299
+ inPlace ||= option.includes("i");
2300
+ }
2301
+ if (!scripts.length && i < argv.length) scripts.push(argv[i++]);
2302
+ if (!scripts.length) return result(1, "", "bunmsh: sed: missing script\n");
2303
+ let commands;
2304
+ try { commands = scripts.flatMap(splitSedCommands).map((script) => compileSedCommand(script, extended)); }
2305
+ catch (error) { return result(1, "", `bunmsh: sed: ${error.message}\n`); }
2306
+ const operands = argv.slice(i);
2307
+ if (inPlace && !operands.length) return result(1, "", "bunmsh: sed: -i requires a file operand\n");
2308
+ try {
2309
+ if (!operands.length) {
2310
+ const text = decoder.decode(await readFallbackInput(input));
2311
+ return result(0, applySed(text, commands, quiet));
2312
+ }
2313
+ let stdout = "";
2314
+ for (const operand of operands) {
2315
+ const file = isAbsolute(nativePath(operand)) ? nativePath(operand) : resolvePath(nativePath(state.cwd), nativePath(operand));
2316
+ const changed = applySed(await Bun.file(file).text(), commands, quiet);
2317
+ if (inPlace) await Bun.write(file, changed);
2318
+ else stdout += changed;
2319
+ }
2320
+ return result(0, stdout);
2321
+ } catch (error) { return result(1, "", `bunmsh: sed: ${error.message}\n`); }
2322
+ }
2323
+
2324
+ function cutCharacterRanges(spec) {
2325
+ const ranges = [];
2326
+ for (const part of spec.split(",")) {
2327
+ const match = /^(\d+)?(?:-(\d+)?)?$/.exec(part);
2328
+ if (!match || (!match[1] && !match[2])) throw new Error(`invalid range: ${part}`);
2329
+ const start = Number(match[1] ?? 1);
2330
+ const end = match[0].includes("-") ? Number(match[2] ?? Number.MAX_SAFE_INTEGER) : start;
2331
+ ranges.push([start, end]);
2332
+ }
2333
+ return ranges;
2334
+ }
2335
+
2336
+ async function runCutFallback(argv, _state, input) {
2337
+ let spec, i = 1;
2338
+ if (argv[i] === "-c") spec = argv[++i], i++;
2339
+ else if (argv[i]?.startsWith("-c")) spec = argv[i++].slice(2);
2340
+ if (!spec || i !== argv.length) return result(1, "", "bunmsh: cut: usage: cut -c LIST\n");
2341
+ try {
2342
+ const ranges = cutCharacterRanges(spec);
2343
+ const text = decoder.decode(await readFallbackInput(input));
2344
+ const trailing = text.endsWith("\n");
2345
+ const lines = text.split("\n");
2346
+ if (trailing) lines.pop();
2347
+ const output = lines.map((line) => [...line].filter((_ch, index) =>
2348
+ ranges.some(([start, end]) => index + 1 >= start && index + 1 <= end)).join("")).join("\n");
2349
+ return result(0, output + (trailing ? "\n" : ""));
2350
+ } catch (error) { return result(1, "", `bunmsh: cut: ${error.message}\n`); }
2351
+ }
2352
+
2353
+ function runLnFallback(argv, state) {
2354
+ let symbolic = false, force = false, noTargetDirectory = false, i = 1;
2355
+ while (/^-[sfT]+$/.test(argv[i] ?? "")) {
2356
+ symbolic ||= argv[i].includes("s");
2357
+ force ||= argv[i].includes("f");
2358
+ noTargetDirectory ||= argv[i].includes("T");
2359
+ i++;
2360
+ }
2361
+ if (argv.length - i !== 2) return result(1, "", "bunmsh: ln: usage: ln [-sf] target link_name\n");
2362
+ const target = nativePath(argv[i]);
2363
+ let link = isAbsolute(nativePath(argv[i + 1]))
2364
+ ? nativePath(argv[i + 1])
2365
+ : resolvePath(nativePath(state.cwd), nativePath(argv[i + 1]));
2366
+ try {
2367
+ if (!noTargetDirectory) {
2368
+ try {
2369
+ if (statSync(link).isDirectory()) link = resolvePath(link, pathBasename(target));
2370
+ } catch {}
2371
+ }
2372
+ if (force) try { unlinkSync(link); } catch {}
2373
+ if (symbolic) symlinkSync(target, link); else {
2374
+ const source = isAbsolute(target) ? target : resolvePath(nativePath(state.cwd), target);
2375
+ linkSync(source, link);
2376
+ }
2377
+ return result();
2378
+ } catch (error) { return result(1, "", `bunmsh: ln: ${error.message}\n`); }
2379
+ }
2380
+
2381
+ function runChmodFallback(argv, state) {
2382
+ if (argv.length < 3) return result(1, "", "bunmsh: chmod: usage: chmod MODE FILE...\n");
2383
+ const mode = argv[1];
2384
+ let status = 0, stderr = "";
2385
+ for (const operand of argv.slice(2)) {
2386
+ const path = isAbsolute(nativePath(operand)) ? nativePath(operand) : resolvePath(nativePath(state.cwd), nativePath(operand));
2387
+ try {
2388
+ if (/^[0-7]{3,4}$/.test(mode)) chmodSync(path, Number.parseInt(mode, 8));
2389
+ else if (mode === "+x" || mode === "a+x") chmodSync(path, statSync(path).mode | 0o111);
2390
+ else throw new Error(`unsupported mode: ${mode}`);
2391
+ } catch (error) { status = 1; stderr += `bunmsh: chmod: ${operand}: ${error.message}\n`; }
2392
+ }
2393
+ return result(status, "", stderr);
2394
+ }
2395
+
2396
+ export function runUnameFallback(argv, system = {}) {
2397
+ const arch = system.arch ?? osArch();
2398
+ const machine = ({ arm64: "aarch64", x64: "x86_64" })[arch] ?? arch;
2399
+ const values = {
2400
+ s: system.type ?? osType(),
2401
+ n: system.hostname ?? osHostname(),
2402
+ r: system.release ?? osRelease(),
2403
+ v: system.version ?? osVersion(),
2404
+ m: machine,
2405
+ p: machine,
2406
+ };
2407
+ if (argv.length === 1) return result(0, `${values.s}\n`);
2408
+ const flags = new Set();
2409
+ for (const arg of argv.slice(1)) {
2410
+ if (arg === "-a") for (const flag of "snrvmp") flags.add(flag);
2411
+ else if (/^-[snrvmp]+$/.test(arg)) for (const flag of arg.slice(1)) flags.add(flag);
2412
+ else return result(1, "", `bunmsh: uname: unsupported option: ${arg}\n`);
2413
+ }
2414
+ return result(0, `${[..."snrvmp"].filter((flag) => flags.has(flag)).map((flag) => values[flag]).join(" ")}\n`);
2415
+ }
2416
+
2417
+ // Windows has no `ps`, so the same two columns come out of Win32_Process.
2418
+ // bun-taskmgr reads `Select-Object ProcessId, CommandLine`, but that is
2419
+ // formatted as a console table, which truncates a long command line with an
2420
+ // ellipsis — exactly the part pspa exists to show. Building the line inside
2421
+ // PowerShell instead keeps it whole, and falls back to the image name for a
2422
+ // system process whose CommandLine is null.
2423
+ const PSPA_WINDOWS_QUERY = "Get-CimInstance Win32_Process | ForEach-Object { " +
2424
+ "$_.ProcessId.ToString() + ' ' + $(if ($_.CommandLine) { $_.CommandLine } else { $_.Name }) }";
2425
+
2426
+ export function parseWindowsProcessList(text) {
2427
+ const rows = [];
2428
+ for (const line of text.split(/\r?\n/)) {
2429
+ const match = /^\s*(\d+) ?(.*)$/.exec(line);
2430
+ // A command line containing a newline arrives as continuation lines with
2431
+ // no PID of their own. There is nothing to attribute them to, so they are
2432
+ // dropped rather than guessed at.
2433
+ if (match) rows.push({ pid: Number(match[1]), args: match[2].trim() });
2434
+ }
2435
+ return rows.sort((first, second) => first.pid - second.pid);
2436
+ }
2437
+
2438
+ // Laid out like `ps -eo pid,args`, so both platforms print the same shape:
2439
+ // a right-aligned PID column at least as wide as procps' own, then the
2440
+ // command line.
2441
+ export function formatProcessTable(rows) {
2442
+ const width = rows.reduce((widest, row) => Math.max(widest, String(row.pid).length), 5);
2443
+ const lines = [`${"PID".padStart(width)} COMMAND`];
2444
+ for (const row of rows)
2445
+ lines.push(`${String(row.pid).padStart(width)} ${row.args}`.trimEnd());
2446
+ return `${lines.join("\n")}\n`;
2447
+ }
2448
+
2449
+ // Windows has no signals to deliver. libuv turns SIGTERM, SIGINT, and
2450
+ // SIGKILL into an unconditional TerminateProcess, refuses every other name
2451
+ // with ENOSYS, and in no case touches the target's children. `taskkill`
2452
+ // reaches all of it, and `/T /F` is the form bun-taskmgr verified on
2453
+ // Windows 11, so that is what a terminating signal becomes here.
2454
+ export function windowsKillCommand(pid) {
2455
+ return ["taskkill", "/PID", String(pid), "/T", "/F"];
2456
+ }
2457
+
2458
+ // taskkill reports its own failures in prose ("ERROR: The process "123" not
2459
+ // found."). Strip its prefix so the line reads as one of ours.
2460
+ export function taskkillFailure(status, stdout, stderr) {
2461
+ if (status === 0) return null;
2462
+ const message = (stderr.trim() || stdout.trim()).replace(/^ERROR:\s*/i, "");
2463
+ return message || `taskkill exited with status ${status}`;
2464
+ }
2465
+
2466
+ async function runWindowsKill(pid) {
2467
+ let proc;
2468
+ try {
2469
+ proc = Bun.spawn({
2470
+ cmd: windowsKillCommand(pid),
2471
+ stdin: "ignore",
2472
+ stdout: "pipe",
2473
+ stderr: "pipe",
2474
+ });
2475
+ } catch (error) { return `taskkill: ${error.message}`; }
2476
+ const [status, stdout, stderr] = await Promise.all([
2477
+ proc.exited,
2478
+ new Response(proc.stdout).text(),
2479
+ new Response(proc.stderr).text(),
2480
+ ]);
2481
+ return taskkillFailure(status, stdout, stderr);
2482
+ }
2483
+
2484
+ // `pspa` answers the question a shell with no process viewer keeps running
2485
+ // into: which PID belongs to which command line. POSIX hands that to
2486
+ // `ps -eo pid,args` and passes the output straight through — reading it over
2487
+ // a pipe is also what stops procps from cutting long command lines at the
2488
+ // terminal width, which it does when its stdout is a terminal.
2489
+ // Rows out of `ps`, for the callers that render the listing themselves
2490
+ // rather than passing it through.
2491
+ export function parsePosixProcessList(text) {
2492
+ const rows = [];
2493
+ for (const line of text.split(/\r?\n/)) {
2494
+ if (/^\s*PID\s+COMMAND\s*$/.test(line)) continue;
2495
+ const match = /^\s*(\d+)\s+(.*)$/.exec(line);
2496
+ if (match) rows.push({ pid: Number(match[1]), args: match[2].trimEnd() });
2497
+ }
2498
+ return rows;
2499
+ }
2500
+
2501
+ async function queryProcessList(name, state) {
2502
+ const windows = process.platform === "win32";
2503
+ const command = windows
2504
+ ? ["powershell", "-NoProfile", "-Command", PSPA_WINDOWS_QUERY]
2505
+ : ["ps", "-eo", "pid,args"];
2506
+ let proc;
2507
+ try {
2508
+ proc = Bun.spawn({
2509
+ cmd: command,
2510
+ cwd: nativePath(state.cwd),
2511
+ env: state.env,
2512
+ stdin: "ignore",
2513
+ stdout: "pipe",
2514
+ stderr: "pipe",
2515
+ });
2516
+ } catch (error) {
2517
+ return { failure: result(127, "", `bunmsh: ${name}: ${command[0]}: ${error.message}\n`) };
2518
+ }
2519
+ const [status, stdout, stderr] = await Promise.all([
2520
+ proc.exited,
2521
+ new Response(proc.stdout).arrayBuffer(),
2522
+ new Response(proc.stderr).text(),
2523
+ ]);
2524
+ if (status !== 0) {
2525
+ return {
2526
+ failure: result(status, "",
2527
+ stderr || `bunmsh: ${name}: ${command[0]}: exited with status ${status}\n`),
2528
+ };
2529
+ }
2530
+ return { windows, output: new Uint8Array(stdout), stderr };
2531
+ }
2532
+
2533
+ async function runPspaFallback(argv, state) {
2534
+ if (argv.length > 1)
2535
+ return result(2, "", `bunmsh: pspa: unexpected operand: ${argv[1]}\n`);
2536
+ const query = await queryProcessList("pspa", state);
2537
+ if (query.failure) return query.failure;
2538
+ return query.windows
2539
+ ? result(0, formatProcessTable(parseWindowsProcessList(decoder.decode(query.output))), query.stderr)
2540
+ : result(0, query.output, query.stderr);
2541
+ }
2542
+
2543
+ // The COMMAND column of a process listing is a shell command line, so it is
2544
+ // highlighted as one. The rules are micro's `syntax/sh.yaml` and the colours
2545
+ // are its `colorschemes/monokai.micro` colour-links, which is also the
2546
+ // palette catfancy already uses — so a command line reads the same here, in
2547
+ // a previewed script, and in the editor.
2548
+ export const SH_COLORS = {
2549
+ comment: Bun.color("#75715E", "ansi-16m"),
2550
+ identifier: Bun.color("#66D9EF", "ansi-16m"),
2551
+ constant: Bun.color("#AE81FF", "ansi-16m"),
2552
+ string: Bun.color("#E6DB74", "ansi-16m"),
2553
+ statement: Bun.color("#F92672", "ansi-16m"),
2554
+ type: Bun.color("#66D9EF", "ansi-16m"),
2555
+ special: Bun.color("#A6E22E", "ansi-16m"),
2556
+ path: "\x1b[2m",
2557
+ header: "\x1b[1m",
2558
+ reset: "\x1b[0m",
2559
+ };
2560
+
2561
+ // sh.yaml's rules, in its order. A later rule paints over an earlier one,
2562
+ // which is how micro resolves them too: that is why the flag rule beats the
2563
+ // command names it overlaps, and why `$var` ends up an identifier rather
2564
+ // than the `special` its `$` matched first.
2565
+ const SH_RULES = [
2566
+ { name: "constant", pattern: /\b[0-9]+\b/g },
2567
+ { name: "statement", pattern: /\b(break|case|continue|do|done|elif|else|esac|exec|exit|fi|for|function|if|in|return|select|then|trap|until|wait|while)\b/g },
2568
+ { name: "special", pattern: /[`$<>!=&~^{}();\][]+/g },
2569
+ { name: "type", pattern: /\b(cd|command|echo|eval|export|getopts|let|local|read|set|shift|time|umask|unset)\b/g },
2570
+ { name: "type", pattern: /\b((g|ig)?awk|bash|dash|find|getopt|\w{0,4}grep|kill|killall|\w{0,4}less|make|pkill|sed|sh|tar)\b/g },
2571
+ { name: "type", pattern: /\b(base64|basename|cat|chcon|chgrp|chmod|chown|chroot|cksum|comm|cp|csplit|cut|date|dd|df|dir|dircolors|dirname|du|env|expand|expr|factor|false|fmt|fold|head|hostid|id|install|join|link|ln|logname|ls|md5sum|mkdir|mkfifo|mknod|mktemp|mv|nice|nl|nohup|nproc|numfmt|od|paste|pathchk|pinky|pr|printenv|printf|ptx|pwd|readlink|realpath|rm|rmdir|runcon|seq|(sha1|sha224|sha256|sha384|sha512)sum|shred|shuf|sleep|sort|split|stat|stdbuf|stty|sum|sync|tac|tail|tee|test|time|timeout|touch|tr|true|truncate|tsort|tty|uname|unexpand|uniq|unlink|users|vdir|wc|who|whoami|yes)\b/g },
2572
+ { name: "statement", pattern: /(\s|^)(--?[A-Za-z0-9][\w-]*)/g, group: 2 },
2573
+ { name: "identifier", pattern: /\$\{[\w:!%&=+#~@*^$?, .\-/[\]]+\}/g },
2574
+ { name: "identifier", pattern: /\$([0-9!#@*$?-]|[A-Za-z_]\w*)/g },
2575
+ ];
2576
+
2577
+ // Quoted strings and comments are regions in sh.yaml, not patterns: they
2578
+ // swallow whatever is inside them. Finding their spans first is what keeps
2579
+ // a `#` inside a quoted argument from turning the rest of the line into a
2580
+ // comment, and a quote inside a comment from opening a string.
2581
+ function shellRegions(text) {
2582
+ const regions = [];
2583
+ for (let i = 0; i < text.length; i++) {
2584
+ const ch = text[i];
2585
+ if (ch === '"' || ch === "'") {
2586
+ let end = i + 1;
2587
+ while (end < text.length) {
2588
+ if (text[end] === "\\") { end += 2; continue; }
2589
+ if (text[end] === ch) { end++; break; }
2590
+ end++;
2591
+ }
2592
+ end = Math.min(end, text.length);
2593
+ regions.push({ start: i, end, name: "string" });
2594
+ i = end - 1;
2595
+ continue;
2596
+ }
2597
+ if (ch === "#" && (i === 0 || /\s/.test(text[i - 1]))) {
2598
+ regions.push({ start: i, end: text.length, name: "comment" });
2599
+ break;
2600
+ }
2601
+ }
2602
+ return regions;
2603
+ }
2604
+
2605
+ // `base` paints runs before any rule runs, so a rule matching inside one
2606
+ // still wins — the same layering micro applies between a scheme's default
2607
+ // and its rules.
2608
+ export function highlightShellCommand(text, base = []) {
2609
+ const painted = new Array(text.length).fill(null);
2610
+ for (const run of base)
2611
+ for (let i = run.start; i < Math.min(run.end, text.length); i++) painted[i] = run.name;
2612
+ const regions = shellRegions(text);
2613
+ const inRegion = new Array(text.length).fill(false);
2614
+ for (const region of regions)
2615
+ for (let i = region.start; i < region.end; i++) inRegion[i] = true;
2616
+ for (const rule of SH_RULES) {
2617
+ for (const match of text.matchAll(rule.pattern)) {
2618
+ const value = rule.group ? match[rule.group] : match[0];
2619
+ if (!value) continue;
2620
+ const start = match.index + (rule.group ? match[0].indexOf(value) : 0);
2621
+ for (let i = start; i < start + value.length; i++)
2622
+ if (!inRegion[i]) painted[i] = rule.name;
2623
+ }
2624
+ }
2625
+ for (const region of regions)
2626
+ for (let i = region.start; i < region.end; i++) painted[i] = region.name;
2627
+
2628
+ let output = "";
2629
+ for (let i = 0; i < text.length;) {
2630
+ const name = painted[i];
2631
+ let end = i;
2632
+ while (end < text.length && painted[end] === name) end++;
2633
+ const chunk = text.slice(i, end);
2634
+ output += name ? `${SH_COLORS[name]}${chunk}${SH_COLORS.reset}` : chunk;
2635
+ i = end;
2636
+ }
2637
+ return output;
2638
+ }
2639
+
2640
+ // Two additions to what sh.yaml describes, because a process listing is not
2641
+ // a script. The directory in front of the program is dimmed: that is the
2642
+ // part an absolute path buries the answer in, and `ps` prints a lot of it.
2643
+ // And the program's own name is painted `type` whether or not it is one of
2644
+ // the names sh.yaml lists — the file has to guess a command from a word
2645
+ // list, while here the first token is known to be one.
2646
+ // Both are base layers, so a rule still paints over them, and a quoted
2647
+ // program keeps the string colour a quoted string gets everywhere else.
2648
+ // A kernel thread — `[kworker/0:1]`, one bracketed token with no program,
2649
+ // path, or arguments in it — recedes whole instead of being taken apart.
2650
+ export function colorProcessCommand(args) {
2651
+ if (!args) return "";
2652
+ if (/^\[.+\]$/.test(args)) return `${SH_COLORS.path}${args}${SH_COLORS.reset}`;
2653
+ const end = args.indexOf(" ");
2654
+ const program = end < 0 ? args.length : end;
2655
+ const separator = Math.max(
2656
+ args.lastIndexOf("/", program - 1),
2657
+ args.lastIndexOf("\\", program - 1),
2658
+ );
2659
+ const base = separator < 0
2660
+ ? [{ start: 0, end: program, name: "type" }]
2661
+ : [
2662
+ { start: 0, end: separator + 1, name: "path" },
2663
+ { start: separator + 1, end: program, name: "type" },
2664
+ ];
2665
+ return highlightShellCommand(args, base);
2666
+ }
2667
+
2668
+ export function colorProcessTable(rows) {
2669
+ // A PID is a number, and sh.yaml paints a number `constant`.
2670
+ const { constant: PID, header: HEADER, reset: RESET } = SH_COLORS;
2671
+ const width = rows.reduce((widest, row) => Math.max(widest, String(row.pid).length), 5);
2672
+ const lines = [`${HEADER}${"PID".padStart(width)} COMMAND${RESET}`];
2673
+ // Nothing but colour separates this from formatProcessTable's output, so
2674
+ // a row with no command line keeps the same trimmed tail that one has.
2675
+ for (const row of rows)
2676
+ lines.push(`${PID}${String(row.pid).padStart(width)}${RESET}` +
2677
+ (row.args ? ` ${colorProcessCommand(row.args)}` : ""));
2678
+ return `${lines.join("\n")}\n`;
2679
+ }
2680
+
2681
+ // `pspac` is `pspa` coloured. Like catfancy it always colours rather than
2682
+ // sniffing for a terminal, so the plain listing stays one command away.
2683
+ async function runPspacFallback(argv, state) {
2684
+ if (argv.length > 1)
2685
+ return result(2, "", `bunmsh: pspac: unexpected operand: ${argv[1]}\n`);
2686
+ const query = await queryProcessList("pspac", state);
2687
+ if (query.failure) return query.failure;
2688
+ const text = decoder.decode(query.output);
2689
+ const rows = query.windows ? parseWindowsProcessList(text) : parsePosixProcessList(text);
2690
+ return result(0, colorProcessTable(rows), query.stderr);
2691
+ }
2692
+
2693
+ const fallbackBuiltins = {
2694
+ basename: async (argv) => {
2695
+ const i = argv[1] === "--" ? 2 : 1;
2696
+ if (i >= argv.length || argv.length - i > 2)
2697
+ return result(1, "", "bunmsh: basename: usage: basename string [suffix]\n");
2698
+ let name = pathBasename(argv[i]);
2699
+ const suffix = argv[i + 1];
2700
+ if (suffix && suffix !== name && name.endsWith(suffix)) name = name.slice(0, -suffix.length);
2701
+ return result(0, `${name}\n`);
2702
+ },
2703
+ dirname: async (argv) => {
2704
+ const i = argv[1] === "--" ? 2 : 1;
2705
+ if (argv.length - i !== 1)
2706
+ return result(1, "", "bunmsh: dirname: usage: dirname string\n");
2707
+ return result(0, `${pathDirname(argv[i])}\n`);
2708
+ },
2709
+ ls: runFancyLsFallback,
2710
+ // Bun Shell's own `ls`, kept reachable under a different name now that
2711
+ // plain `ls` falls back to lsfancy instead. Bun.$ dispatches on the
2712
+ // literal command name, so argv[0] is rewritten back to "ls" for it —
2713
+ // `lsbun` itself is not something Bun Shell knows how to run.
2714
+ lsbun: (argv, state) => runBunShellFallback(["ls", ...argv.slice(1)], state),
2715
+ lsfancy: runFancyLsFallback,
2716
+ mv: runBunShellFallback,
2717
+ rm: runBunShellFallback,
2718
+ mkdir: runBunShellFallback,
2719
+ seq: runBunShellFallback,
2720
+ touch: runBunShellFallback,
2721
+ cat: runCatFallback,
2722
+ tac: runTacFallback,
2723
+ catfancy: async (argv, state, input) => {
2724
+ const output = await catFancy(argv, state.cwd, fallbackInput(input));
2725
+ return result(output.status, output.stdout, output.stderr);
2726
+ },
2727
+ cp: runBunShellCpFallback,
2728
+ curl: runCurlFallback,
2729
+ head: runHeadFallback,
2730
+ tail: (argv, state, input) => runTextFilter(argv, state, input, "tail"),
2731
+ wc: runWcFallback,
2732
+ tr: runTrFallback,
2733
+ sleep: runSleepFallback,
2734
+ tee: runTeeFallback,
2735
+ clear: async () => result(0, "\x1b[2J\x1b[H"),
2736
+ rmdir: async (argv, state) => runRmdirFallback(argv, state),
2737
+ mktemp: async (argv, state) => runMktempFallback(argv, state),
2738
+ sort: (argv, state, input) => runTextFilter(argv, state, input, "sort"),
2739
+ date: async (argv) => runDateFallback(argv),
2740
+ md5sum: (argv, state, input) => runHashFallback(argv, state, input, "md5"),
2741
+ sha256sum: (argv, state, input) => runHashFallback(argv, state, input, "sha256"),
2742
+ grep: runGrepFallback,
2743
+ sed: runSedFallback,
2744
+ cut: runCutFallback,
2745
+ ln: async (argv, state) => runLnFallback(argv, state),
2746
+ chmod: async (argv, state) => runChmodFallback(argv, state),
2747
+ uname: async (argv) => runUnameFallback(argv),
2748
+ pspa: runPspaFallback,
2749
+ pspac: runPspacFallback,
2750
+ bunmsh: runBunmshFallback,
2751
+ bun: runBunFallback,
2752
+ serve: async (argv, state) => {
2753
+ const options = parseServeArguments(argv.slice(1), state.env, state.cwd);
2754
+ if (options.error) return result(2, "", `bunmsh: serve: ${options.error}\n`);
2755
+ try {
2756
+ const server = startFileServer(options.directory, options);
2757
+ const signal = await waitForInterrupt(server, options);
2758
+ return result(signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : 0);
2759
+ } catch (error) {
2760
+ return result(1, "", `bunmsh: serve: ${error.message}\n`);
2761
+ }
2762
+ },
2763
+ };
2764
+
2765
+ if (!findIsRegularBuiltin()) fallbackBuiltins.find = runFindBuiltin;
2766
+
2767
+ export function builtinNames() {
2768
+ return [...new Set([...Object.keys(builtins), ...Object.keys(fallbackBuiltins)])].sort();
2769
+ }
2770
+
2771
+ function formatMilliseconds(milliseconds, color = Boolean(process.stderr.isTTY)) {
2772
+ const rendered = milliseconds.toFixed(6);
2773
+ if (!color) return rendered;
2774
+ const decimal = rendered.indexOf(".");
2775
+ const palette = [46, 82, 118, 154, 190, 226, 220, 214, 208, 202, 196, 198, 201, 165, 129, 93, 57, 21, 27, 33, 39, 45];
2776
+ let output = "";
2777
+ for (let i = 0; i < rendered.length; i++) {
2778
+ if (rendered[i] === ".") {
2779
+ output += "\x1b[2m.\x1b[0m";
2780
+ continue;
2781
+ }
2782
+ const magnitude = i < decimal ? decimal - i - 1 : decimal - i;
2783
+ // Keep each three-digit magnitude region the same colour: units
2784
+ // (000), thousands (000), millions (000), then fractional groups.
2785
+ const group = Math.floor(magnitude / 3);
2786
+ const index = ((group % palette.length) + palette.length) % palette.length;
2787
+ output += `\x1b[38;5;${palette[index]}m${rendered[i]}\x1b[0m`;
2788
+ }
2789
+ return output;
2790
+ }
2791
+
2792
+ export function createState(options = {}) {
2793
+ const env = canonicalEnvironment({ ...process.env, ...(options.env ?? {}) });
2794
+ const cwd = shellPath(options.cwd ?? process.cwd());
2795
+ env.PWD = cwd;
2796
+ return {
2797
+ env,
2798
+ cwd,
2799
+ directoryHistory: [...(options.directoryHistory ?? [])],
2800
+ tabs: [...(options.tabs ?? [cwd])],
2801
+ activeTab: options.activeTab ?? 0,
2802
+ aliases: { ...DEFAULT_ALIASES, ...(options.aliases ?? {}) },
2803
+ functions: { ...(options.functions ?? {}) },
2804
+ history: [...(options.history ?? [])],
2805
+ mouseTracking: options.mouseTracking ?? /^(?:1|true|on|yes)$/i.test(env.BUNMSH_MOUSE ?? ""),
2806
+ pathSearch: options.pathSearch ?? true,
2807
+ readonly: new Set(options.readonly ?? []),
2808
+ getoptsOffset: 1,
2809
+ args: options.args ?? ["bunmsh"],
2810
+ lastStatus: 0,
2811
+ exitRequested: false,
2812
+ exitStatus: 0,
2813
+ pipelineChild: options.pipelineChild ?? false,
2814
+ };
2815
+ }
2816
+
2817
+ function hasCompoundSyntax(source) {
2818
+ if (/(^|\n)\s*(?:\(|[A-Za-z_][A-Za-z0-9_]*\s*\(\s*\)\s*\{)/.test(source)) return true;
2819
+ let commandStart = true;
2820
+ for (const token of tokenize(source)) {
2821
+ if (token.type === "op") {
2822
+ if ([";", "&&", "||", "|"].includes(token.value)) commandStart = true;
2823
+ continue;
2824
+ }
2825
+ const word = token.fragments.length === 1 && token.fragments[0].quote === "none"
2826
+ ? token.fragments[0].text : null;
2827
+ if (commandStart && ["if", "while", "until", "for", "case"].includes(word)) return true;
2828
+ if (!(commandStart && /^[A-Za-z_][A-Za-z0-9_]*=/.test(word ?? ""))) commandStart = false;
2829
+ }
2830
+ return false;
2831
+ }
2832
+
2833
+ function compoundLogicalSource(source) {
2834
+ const tokens = tokenize(source);
2835
+ const breaks = new Set();
2836
+ let commandStart = true;
2837
+ const literal = (token) => token.type === "word" && token.fragments.length === 1 &&
2838
+ token.fragments[0].quote === "none" ? token.fragments[0].text : null;
2839
+ for (let index = 0; index < tokens.length; index++) {
2840
+ const token = tokens[index];
2841
+ if (token.type === "op") {
2842
+ if ([";", "&&", "||", "|"].includes(token.value)) commandStart = true;
2843
+ continue;
2844
+ }
2845
+ const word = literal(token);
2846
+ if (!commandStart) continue;
2847
+ if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(word ?? "")) continue;
2848
+ // Like mksh's KEYWORD lexer mode, these words are special only where a
2849
+ // command may begin. Physical newlines are not required around them.
2850
+ if (["if", "while", "until", "for", "case"].includes(word) && token.offset > 0)
2851
+ breaks.add(token.offset);
2852
+ if (["elif", "else", "fi", "done", "esac"].includes(word))
2853
+ breaks.add(token.offset);
2854
+ if (["then", "do", "else"].includes(word) && tokens[index + 1])
2855
+ breaks.add(tokens[index + 1].offset);
2856
+ commandStart = ["if", "while", "until", "elif", "then", "do", "else"].includes(word);
2857
+ }
2858
+ let logical = source;
2859
+ for (const offset of [...breaks].sort((a, b) => b - a))
2860
+ if (offset > 0 && source[offset - 1] !== "\n")
2861
+ logical = `${logical.slice(0, offset)}\n${logical.slice(offset)}`;
2862
+ return logical;
2863
+ }
2864
+
2865
+ // A leaf command line inside a compound body (if/while/for/case) is scanned
2866
+ // one physical line at a time, but a here-document's body lives on the lines
2867
+ // that follow it. Grow the line by re-tokenizing with more lines appended
2868
+ // until the heredoc(s) it opens are fully resolved, so the folded lines never
2869
+ // get misread as separate commands or as clauses of the surrounding compound.
2870
+ function growHeredocLine(lines, index) {
2871
+ const line = lines[index];
2872
+ if (!line.includes("<<")) return { source: line, next: index + 1 };
2873
+ let end = index;
2874
+ let candidate = line;
2875
+ while (true) {
2876
+ try {
2877
+ tokenize(candidate, { strict: true });
2878
+ return { source: candidate, next: end + 1 };
2879
+ } catch (error) {
2880
+ if (!(error instanceof ShellSyntaxError) ||
2881
+ !error.message.startsWith("unterminated here-document"))
2882
+ throw error;
2883
+ end++;
2884
+ if (end >= lines.length) throw error;
2885
+ candidate += `\n${lines[end]}`;
2886
+ }
2887
+ }
2888
+ }
2889
+
2890
+ function parseCompoundScript(source) {
2891
+ const lines = compoundLogicalSource(source.replace(/\\\r?\n/g, "")).split(/\r?\n/);
2892
+ const parseSequence = (start, stops = []) => {
2893
+ const nodes = [];
2894
+ let i = start;
2895
+ while (i < lines.length) {
2896
+ const line = lines[i];
2897
+ const trimmed = line.trim();
2898
+ if (stops.some((stop) => stop.test(trimmed))) break;
2899
+ if (!trimmed || trimmed.startsWith("#")) { i++; continue; }
2900
+
2901
+ const functionMatch = /^([A-Za-z_][A-Za-z0-9_]*)\s*\(\s*\)\s*\{\s*$/.exec(trimmed);
2902
+ if (functionMatch) {
2903
+ const body = parseSequence(i + 1, [/^}\s*;?$/]);
2904
+ if (body.index >= lines.length) throw new ShellSyntaxError(`unterminated function ${functionMatch[1]}`);
2905
+ nodes.push({ type: "function", name: functionMatch[1], body: body.nodes });
2906
+ i = body.index + 1;
2907
+ continue;
2908
+ }
2909
+
2910
+ const subshellMatch = /^\((.*)\)\s*;?$/.exec(trimmed);
2911
+ if (subshellMatch) {
2912
+ nodes.push({ type: "subshell", source: subshellMatch[1] });
2913
+ i++;
2914
+ continue;
2915
+ }
2916
+
2917
+ if (/^if(?:\s|$)/.test(trimmed)) {
2918
+ const branches = [];
2919
+ let header = trimmed;
2920
+ while (!/;\s*then\s*$/.test(header) && ++i < lines.length)
2921
+ header += ` ${lines[i].trim()}`;
2922
+ if (!/;\s*then\s*$/.test(header)) throw new ShellSyntaxError("if requires then");
2923
+ let condition = header.replace(/^if\s*/, "").replace(/;\s*then\s*$/, "");
2924
+ i++;
2925
+ while (true) {
2926
+ const body = parseSequence(i, [/^elif(?:\s|$)/, /^else\s*;?$/, /^fi\s*;?$/]);
2927
+ branches.push({ condition, body: body.nodes });
2928
+ i = body.index;
2929
+ const stop = lines[i]?.trim() ?? "";
2930
+ if (/^elif(?:\s|$)/.test(stop)) {
2931
+ header = stop;
2932
+ while (!/;\s*then\s*$/.test(header) && ++i < lines.length)
2933
+ header += ` ${lines[i].trim()}`;
2934
+ if (!/;\s*then\s*$/.test(header)) throw new ShellSyntaxError("elif requires then");
2935
+ condition = header.replace(/^elif\s*/, "").replace(/;\s*then\s*$/, "");
2936
+ i++;
2937
+ continue;
2938
+ }
2939
+ let otherwise = [];
2940
+ if (/^else\s*;?$/.test(stop)) {
2941
+ const body = parseSequence(i + 1, [/^fi\s*;?$/]);
2942
+ otherwise = body.nodes;
2943
+ i = body.index;
2944
+ }
2945
+ if (!/^fi\s*;?$/.test(lines[i]?.trim() ?? ""))
2946
+ throw new ShellSyntaxError("unterminated if");
2947
+ nodes.push({ type: "if", branches, otherwise });
2948
+ i++;
2949
+ break;
2950
+ }
2951
+ continue;
2952
+ }
2953
+ if (/^(?:while|until)(?:\s|$)/.test(trimmed)) {
2954
+ const kind = trimmed.startsWith("until") ? "until" : "while";
2955
+ let header = trimmed;
2956
+ while (!/;\s*do\s*$/.test(header) && ++i < lines.length)
2957
+ header += ` ${lines[i].trim()}`;
2958
+ if (!/;\s*do\s*$/.test(header)) throw new ShellSyntaxError(`${kind} requires do`);
2959
+ const condition = header.replace(new RegExp(`^${kind}\\s*`), "").replace(/;\s*do\s*$/, "");
2960
+ const body = parseSequence(i + 1, [/^done(?:\s*<\s*.+)?\s*;?$/]);
2961
+ const done = /^done(?:\s*<\s*(.+?))?\s*;?$/.exec(lines[body.index]?.trim() ?? "");
2962
+ if (!done)
2963
+ throw new ShellSyntaxError(`unterminated ${kind}`);
2964
+ nodes.push({ type: "loop", kind, condition, body: body.nodes, input: done[1] ?? null });
2965
+ i = body.index + 1;
2966
+ continue;
2967
+ }
2968
+ if (/^for(?:\s|$)/.test(trimmed)) {
2969
+ let header = trimmed;
2970
+ while (!/;\s*do\s*$/.test(header) && ++i < lines.length)
2971
+ header += ` ${lines[i].trim()}`;
2972
+ const match = /^for\s+([A-Za-z_][A-Za-z0-9_]*)(?:\s+in\s+(.*?))?\s*;\s*do\s*$/.exec(header);
2973
+ if (!match) throw new ShellSyntaxError("invalid for command");
2974
+ const body = parseSequence(i + 1, [/^done\s*;?$/]);
2975
+ if (!/^done\s*;?$/.test(lines[body.index]?.trim() ?? ""))
2976
+ throw new ShellSyntaxError("unterminated for");
2977
+ nodes.push({ type: "for", name: match[1], words: match[2] ?? null, body: body.nodes });
2978
+ i = body.index + 1;
2979
+ continue;
2980
+ }
2981
+ if (/^case(?:\s|$)/.test(trimmed)) {
2982
+ let header = trimmed;
2983
+ while (!/\s+in\s*$/.test(header) && ++i < lines.length)
2984
+ header += ` ${lines[i].trim()}`;
2985
+ const match = /^case\s+(.+?)\s+in\s*$/.exec(header);
2986
+ if (!match) throw new ShellSyntaxError("case requires in");
2987
+ const arms = [];
2988
+ i++;
2989
+ while (i < lines.length && !/^esac\s*;?$/.test(lines[i].trim())) {
2990
+ if (!lines[i].trim() || lines[i].trim().startsWith("#")) { i++; continue; }
2991
+ const arm = /^(.+?)\)\s*$/.exec(lines[i].trim());
2992
+ if (!arm) throw new ShellSyntaxError("case pattern requires )");
2993
+ const bodyLines = [];
2994
+ i++;
2995
+ while (i < lines.length && !/^esac\s*;?$/.test(lines[i].trim())) {
2996
+ const current = lines[i];
2997
+ const terminator = current.indexOf(";;");
2998
+ if (terminator >= 0) {
2999
+ if (current.slice(0, terminator).trim()) bodyLines.push(current.slice(0, terminator));
3000
+ i++;
3001
+ break;
3002
+ }
3003
+ bodyLines.push(current);
3004
+ i++;
3005
+ }
3006
+ arms.push({ patterns: arm[1].split("|").map((pattern) => pattern.trim()), body: parseCompoundScript(bodyLines.join("\n")) });
3007
+ }
3008
+ if (!/^esac\s*;?$/.test(lines[i]?.trim() ?? "")) throw new ShellSyntaxError("unterminated case");
3009
+ nodes.push({ type: "case", word: match[1], arms });
3010
+ i++;
3011
+ continue;
3012
+ }
3013
+ const grown = growHeredocLine(lines, i);
3014
+ nodes.push({ type: "command", source: grown.source });
3015
+ i = grown.next;
3016
+ }
3017
+ return { nodes, index: i };
3018
+ };
3019
+ const parsed = parseSequence(0);
3020
+ return parsed.nodes;
3021
+ }
3022
+
3023
+ // Every "needs more input before this can run" condition in this parser
3024
+ // (open quotes, an open $()/${}/backtick, an open here-document, or a
3025
+ // compound command still missing its closing keyword) raises a
3026
+ // ShellSyntaxError whose message starts with "unterminated". An interactive
3027
+ // front end can use this, mirroring mksh's PS2 continuation prompt, to tell
3028
+ // "still typing this command" apart from a real syntax error.
3029
+ export function isJavaScriptMode(source) {
3030
+ return source.trimStart().startsWith("Bun.");
3031
+ }
3032
+
3033
+ export function needsMoreInput(source) {
3034
+ source = normalizeSource(source);
3035
+ try {
3036
+ if (hasCompoundSyntax(source)) parseCompoundScript(source);
3037
+ else tokenize(source, { strict: true });
3038
+ } catch (error) {
3039
+ if (!(error instanceof ShellSyntaxError && error.message.startsWith("unterminated")))
3040
+ return false;
3041
+ // JavaScript has no line-continuation backslash outside a string
3042
+ // literal: there it is a syntax error, not a request for another line,
3043
+ // so asking for one only leads the typing somewhere that cannot parse.
3044
+ // An unterminated quote still continues, because that is JavaScript's
3045
+ // own line continuation and the evaluator does accept it.
3046
+ if (isJavaScriptMode(source) && error.message === "unterminated line continuation")
3047
+ return false;
3048
+ return true;
3049
+ }
3050
+ return false;
3051
+ }
3052
+
3053
+ async function executeNodeList(nodes, state, options = {}) {
3054
+ const capture = options.capture ?? true;
3055
+ const stdout = [], stderr = [];
3056
+ let execution = result(state.lastStatus);
3057
+ const append = (value) => {
3058
+ if (!capture) return;
3059
+ if (value.stdout.byteLength) stdout.push(value.stdout);
3060
+ if (value.stderr.byteLength) stderr.push(value.stderr);
3061
+ };
3062
+ for (const node of nodes) {
3063
+ if (state.exitRequested) break;
3064
+ if (node.type === "function") {
3065
+ state.functions[node.name] = node.body;
3066
+ continue;
3067
+ }
3068
+ if (node.type === "command") {
3069
+ execution = await execute(node.source, state, { capture, simple: true });
3070
+ append(execution);
3071
+ continue;
3072
+ }
3073
+ if (node.type === "subshell") {
3074
+ const child = {
3075
+ ...state,
3076
+ env: { ...state.env },
3077
+ aliases: { ...state.aliases },
3078
+ functions: { ...state.functions },
3079
+ readonly: new Set(state.readonly),
3080
+ directoryHistory: [...state.directoryHistory],
3081
+ tabs: [...state.tabs],
3082
+ exitRequested: false,
3083
+ exitStatus: 0,
3084
+ };
3085
+ execution = await execute(node.source, child, { capture });
3086
+ append(execution);
3087
+ state.lastStatus = execution.status;
3088
+ continue;
3089
+ }
3090
+ if (node.type === "loop") {
3091
+ const previousReadLines = state.readLines;
3092
+ let ranBody = false;
3093
+ try {
3094
+ if (node.input !== null) {
3095
+ const parsed = parse(`__input ${node.input}`)[0]?.pipeline[0];
3096
+ const operand = (await expandWord(parsed.words[1], state, { single: true }))[0];
3097
+ const path = isAbsolute(nativePath(operand)) ? nativePath(operand) : resolvePath(nativePath(state.cwd), nativePath(operand));
3098
+ const text = await Bun.file(path).text();
3099
+ state.readLines = text.split("\n");
3100
+ if (state.readLines.at(-1) === "") state.readLines.pop();
3101
+ }
3102
+ while (true) {
3103
+ const checked = await execute(node.condition, state, { capture, simple: true });
3104
+ append(checked);
3105
+ const proceed = node.kind === "until" ? checked.status !== 0 : checked.status === 0;
3106
+ if (!proceed || state.exitRequested) {
3107
+ if (!ranBody) execution = result();
3108
+ break;
3109
+ }
3110
+ execution = await executeNodeList(node.body, state, { capture });
3111
+ ranBody = true;
3112
+ append(execution);
3113
+ if (state.exitRequested) break;
3114
+ }
3115
+ } catch (error) {
3116
+ execution = result(1, "", `bunmsh: ${error.message}\n`);
3117
+ append(execution);
3118
+ } finally {
3119
+ state.readLines = previousReadLines;
3120
+ }
3121
+ state.lastStatus = execution.status;
3122
+ continue;
3123
+ }
3124
+ if (node.type === "for") {
3125
+ let values;
3126
+ if (node.words === null) values = state.args.slice(1);
3127
+ else {
3128
+ const parsed = parse(`__for ${node.words}`)[0]?.pipeline[0];
3129
+ values = [];
3130
+ for (const word of parsed?.words.slice(1) ?? []) values.push(...await expandWord(word, state));
3131
+ }
3132
+ execution = result();
3133
+ for (const value of values) {
3134
+ if (state.readonly.has(node.name)) { execution = readonlyError(node.name); break; }
3135
+ state.env[node.name] = value;
3136
+ execution = await executeNodeList(node.body, state, { capture });
3137
+ append(execution);
3138
+ if (state.exitRequested) break;
3139
+ }
3140
+ state.lastStatus = execution.status;
3141
+ continue;
3142
+ }
3143
+ if (node.type === "case") {
3144
+ const parsed = parse(`__case ${node.word}`)[0]?.pipeline[0];
3145
+ const value = (await expandWord(parsed.words[1], state, { single: true }))[0] ?? "";
3146
+ execution = result();
3147
+ for (const arm of node.arms) {
3148
+ let matches = false;
3149
+ for (let pattern of arm.patterns) {
3150
+ pattern = pattern.replace(/^(['"])(.*)\1$/, "$2");
3151
+ pattern = stripExpansionMarkers(await expandText(pattern, state));
3152
+ if (globPatternRegex(pattern, true, true, true).test(value)) { matches = true; break; }
3153
+ }
3154
+ if (!matches) continue;
3155
+ execution = await executeNodeList(arm.body, state, { capture });
3156
+ append(execution);
3157
+ break;
3158
+ }
3159
+ state.lastStatus = execution.status;
3160
+ continue;
3161
+ }
3162
+ let selected = null;
3163
+ for (const branch of node.branches) {
3164
+ let condition = branch.condition.trim();
3165
+ let negate = false;
3166
+ while (/^!\s+/.test(condition)) { negate = !negate; condition = condition.replace(/^!\s+/, ""); }
3167
+ const checked = await execute(condition, state, { capture, simple: true });
3168
+ append(checked);
3169
+ if ((checked.status === 0) !== negate) { selected = branch.body; break; }
3170
+ }
3171
+ execution = selected
3172
+ ? await executeNodeList(selected, state, { capture })
3173
+ : await executeNodeList(node.otherwise, state, { capture });
3174
+ append(execution);
3175
+ state.lastStatus = execution.status;
3176
+ }
3177
+ return result(execution.status, concatBytes(stdout), concatBytes(stderr));
3178
+ }
3179
+
3180
+ async function executeFunction(name, argv, state) {
3181
+ const previousArgs = state.args;
3182
+ state.args = [previousArgs[0], ...argv.slice(1)];
3183
+ try { return await executeNodeList(state.functions[name], state); }
3184
+ finally { state.args = previousArgs; }
3185
+ }
3186
+
3187
+ async function runExternal(
3188
+ argv,
3189
+ state,
3190
+ input,
3191
+ captureStdout,
3192
+ captureStderr,
3193
+ stdoutSink = null,
3194
+ runtimeOptions = {},
3195
+ ) {
3196
+ // The shell language always exposes forward slashes. Only the executable
3197
+ // pathname needs native separators at the Windows process boundary.
3198
+ const spawnArgv = process.platform === "win32" && argv[0].includes("/")
3199
+ ? [nativePath(argv[0]), ...argv.slice(1)]
3200
+ : argv;
3201
+ const options = {
3202
+ cmd: spawnArgv,
3203
+ cwd: nativePath(state.cwd),
3204
+ env: state.env,
3205
+ stdin: input === null ? "inherit" : input,
3206
+ stdout: captureStdout || stdoutSink ? "pipe" : "inherit",
3207
+ stderr: captureStderr || runtimeOptions.stderrSink ? "pipe" : "inherit",
3208
+ onExit(proc, exitCode, signalCode, error) {
3209
+ runtimeOptions.onExit?.(proc, exitCode, signalCode, error);
3210
+ },
3211
+ };
3212
+ try {
3213
+ const proc = Bun.spawn(options);
3214
+ const pipelineKillSignal = runtimeOptions.pipelineKillSignal ?? "SIGPIPE";
3215
+ // A downstream pipeline stage that closes early (for example `head -n 3`)
3216
+ // leaves nothing reading the other end of `stdoutSink`/`stderrSink` (a
3217
+ // link's readable side, fed as the next stage's stdin). Bun never
3218
+ // releases the reader lock it holds on that side even once the process
3219
+ // reading it has exited, so a write already in flight into it (from the
3220
+ // pipeTo() calls below) backpressures forever instead of settling, and
3221
+ // there is no way to cancel or abort it from out here to unstick it —
3222
+ // every avenue requires a lock only Bun's own, already-exited consumer
3223
+ // holds. So this does not wait for that write to actually finish once
3224
+ // the caller (see onSpawn below) says the downstream stage is done;
3225
+ // it is abandoned in place, harmless since the killed process here
3226
+ // will not produce anything more for it to forward anyway.
3227
+ const abandonStreaming = new Promise((resolve) => {
3228
+ runtimeOptions.onSpawn?.(proc, pipelineKillSignal, resolve);
3229
+ });
3230
+ const outputPromise = captureStdout
3231
+ ? new Response(proc.stdout).arrayBuffer()
3232
+ : Promise.resolve(new ArrayBuffer(0));
3233
+ const streamPromise = stdoutSink
3234
+ ? Promise.race([
3235
+ proc.stdout.pipeTo(stdoutSink).catch(() => {
3236
+ try { proc.kill(pipelineKillSignal); } catch {}
3237
+ }),
3238
+ abandonStreaming,
3239
+ ])
3240
+ : Promise.resolve();
3241
+ const errorStreamPromise = runtimeOptions.stderrSink
3242
+ ? Promise.race([
3243
+ proc.stderr.pipeTo(runtimeOptions.stderrSink).catch(() => {}),
3244
+ abandonStreaming,
3245
+ ])
3246
+ : Promise.resolve();
3247
+ const errorPromise = captureStderr
3248
+ ? new Response(proc.stderr).arrayBuffer()
3249
+ : Promise.resolve(new ArrayBuffer(0));
3250
+ const [status, output, error] = await Promise.all([
3251
+ proc.exited,
3252
+ outputPromise,
3253
+ errorPromise,
3254
+ streamPromise,
3255
+ errorStreamPromise,
3256
+ ]);
3257
+ const execution = result(status, new Uint8Array(output), new Uint8Array(error));
3258
+ execution.stdoutStreamed = Boolean(stdoutSink);
3259
+ execution.stderrStreamed = Boolean(runtimeOptions.stderrSink);
3260
+ return execution;
3261
+ } catch (error) {
3262
+ return result(127, "", `bunmsh: ${argv[0]}: ${error.message}\n`);
3263
+ }
3264
+ }
3265
+
3266
+ function runsInPipelineSubprocess(argv, state) {
3267
+ if (["command", "builtin", "__builtin", "time"].includes(argv[0])) return true;
3268
+ if (Object.hasOwn(builtins, argv[0])) return true;
3269
+ if (Object.hasOwn(state.functions, argv[0])) return true;
3270
+ return !argv[0].includes("/") &&
3271
+ Object.hasOwn(fallbackBuiltins, argv[0]) &&
3272
+ !findExecutable(argv[0], state);
3273
+ }
3274
+
3275
+ function pipelineBuiltinArgv(argv) {
3276
+ return IS_COMPILED
3277
+ ? [...EXECUTABLE_COMMAND, "-cc", ...argv]
3278
+ : [...BUN_RUNTIME_COMMAND, BUNMSH_ENTRY, "-cc", ...argv];
3279
+ }
3280
+
3281
+ function pipelineBuiltinState(state) {
3282
+ return {
3283
+ ...state,
3284
+ env: {
3285
+ ...state.env,
3286
+ [PIPELINE_STATE_ENV]: JSON.stringify({
3287
+ aliases: state.aliases,
3288
+ functions: state.functions,
3289
+ readonly: [...state.readonly],
3290
+ args: state.args,
3291
+ pathSearch: state.pathSearch,
3292
+ }),
3293
+ },
3294
+ };
3295
+ }
3296
+
3297
+ function closedPipe(error) {
3298
+ const code = error?.code ?? error?.cause?.code;
3299
+ return code === "EPIPE" || code === "ECONNRESET" ||
3300
+ /broken pipe|closed pipe|connection reset/i.test(error?.message ?? "");
3301
+ }
3302
+
3303
+ async function runYes(argv) {
3304
+ const line = `${argv.length > 1 ? argv.slice(1).join(" ") : "y"}\n`;
3305
+ const repetitions = Math.max(1, Math.floor(8192 / Math.max(1, bytes(line).byteLength)));
3306
+ const chunk = bytes(line.repeat(repetitions));
3307
+ const writer = Bun.stdout.writer();
3308
+ try {
3309
+ while (true) {
3310
+ writer.write(chunk);
3311
+ await writer.flush();
3312
+ }
3313
+ } catch (error) {
3314
+ if (!closedPipe(error)) throw error;
3315
+ } finally {
3316
+ try { writer.end(); } catch {}
3317
+ }
3318
+ return result();
3319
+ }
3320
+
3321
+ function parseCommandOptions(argv) {
3322
+ let defaultPath = false;
3323
+ let query = null;
3324
+ let i = 1;
3325
+ for (; i < argv.length; i++) {
3326
+ const arg = argv[i];
3327
+ if (arg === "--") {
3328
+ i++;
3329
+ break;
3330
+ }
3331
+ if (arg === "-" || !arg.startsWith("-")) break;
3332
+ for (const option of arg.slice(1)) {
3333
+ if (option === "p") defaultPath = true;
3334
+ else if (option === "v" || option === "V") query = option;
3335
+ else return { error: option };
3336
+ }
3337
+ }
3338
+ return { defaultPath, query, operands: argv.slice(i) };
3339
+ }
3340
+
3341
+ function describeCommand(name, state, verbose, defaultPath) {
3342
+ const alias = state.aliases[name];
3343
+ if (alias) {
3344
+ const definition = quoteShellWord(alias.join(" "));
3345
+ return verbose
3346
+ ? `${name} is an alias for ${definition}\n`
3347
+ : `alias ${name}=${definition}\n`;
3348
+ }
3349
+ if (Object.hasOwn(builtins, name))
3350
+ return verbose ? `${name} is a shell builtin\n` : `${name}\n`;
3351
+ const path = findExecutable(name, state, defaultPath);
3352
+ if (path) return verbose ? `${name} is ${path}\n` : `${path}\n`;
3353
+ if (Object.hasOwn(fallbackBuiltins, name))
3354
+ return verbose ? `${name} is a fallback shell builtin\n` : `${name}\n`;
3355
+ return verbose ? `${name} not found\n` : null;
3356
+ }
3357
+
3358
+ async function runCommandArgv(
3359
+ argv,
3360
+ state,
3361
+ input,
3362
+ captureStdout,
3363
+ captureStderr,
3364
+ options = {},
3365
+ ) {
3366
+ let commandArgv = argv;
3367
+
3368
+ if( process.env.BUNMSH_LEADING_AND ||
3369
+ process.platform === "win32" )
3370
+ {
3371
+ if(argv[0] === "&")
3372
+ commandArgv = argv.slice(1) ;
3373
+ }
3374
+
3375
+ let commandState = state;
3376
+ if (options.pipelineStage && runsInPipelineSubprocess(commandArgv, commandState))
3377
+ return runExternal(
3378
+ pipelineBuiltinArgv(commandArgv),
3379
+ pipelineBuiltinState(commandState),
3380
+ input,
3381
+ captureStdout,
3382
+ captureStderr,
3383
+ options.stdoutSink,
3384
+ { ...options, pipelineKillSignal: "SIGTERM" },
3385
+ );
3386
+ while (["command", "builtin", "__builtin"].includes(commandArgv[0])) {
3387
+ if (requestsBuiltinHelp(commandArgv) && hasBuiltinHelp(commandArgv[0]))
3388
+ return builtinHelp(commandArgv[0]);
3389
+ if (commandArgv[0] !== "command") {
3390
+ const builtinName = commandArgv[0];
3391
+ const start = commandArgv[1] === "--" ? 2 : 1;
3392
+ if (start >= commandArgv.length) {
3393
+ if (builtinName === "builtin")
3394
+ return result(0, `${builtinNames().join("\n")}\n`);
3395
+ return result();
3396
+ }
3397
+ commandArgv = commandArgv.slice(start);
3398
+ if (!Object.hasOwn(builtins, commandArgv[0]) &&
3399
+ !Object.hasOwn(fallbackBuiltins, commandArgv[0]))
3400
+ return result(1, "", `bunmsh: builtin: ${commandArgv[0]}: not found\n`);
3401
+ if (requestsBuiltinHelp(commandArgv) && hasBuiltinHelp(commandArgv[0]))
3402
+ return builtinHelp(commandArgv[0]);
3403
+ if (!["command", "builtin", "__builtin", "time", "yes"].includes(commandArgv[0]))
3404
+ return (builtins[commandArgv[0]] ?? fallbackBuiltins[commandArgv[0]])(
3405
+ commandArgv,
3406
+ commandState,
3407
+ input,
3408
+ { captureStdout, captureStderr, options },
3409
+ );
3410
+ continue;
3411
+ }
3412
+ const parsed = parseCommandOptions(commandArgv);
3413
+ if (parsed.error)
3414
+ return result(1, "", `bunmsh: command: -${parsed.error}: unknown option\n`);
3415
+ if (parsed.query) {
3416
+ let status = 0;
3417
+ let output = "";
3418
+ for (const name of parsed.operands) {
3419
+ const description = describeCommand(
3420
+ name,
3421
+ commandState,
3422
+ parsed.query === "V",
3423
+ parsed.defaultPath,
3424
+ );
3425
+ if (description === null || description.endsWith(" not found\n")) status = 1;
3426
+ if (description !== null) output += description;
3427
+ if (status && parsed.query === "v") break;
3428
+ }
3429
+ return result(status, output);
3430
+ }
3431
+ if (parsed.operands.length === 0) return result();
3432
+ commandArgv = parsed.operands;
3433
+ if (parsed.defaultPath) {
3434
+ commandState = { ...commandState, env: { ...commandState.env, PATH: DEFAULT_COMMAND_PATH } };
3435
+ }
3436
+ }
3437
+ if (requestsBuiltinHelp(commandArgv) && hasBuiltinHelp(commandArgv[0]) &&
3438
+ (Object.hasOwn(builtins, commandArgv[0]) ||
3439
+ (!findExecutable(commandArgv[0], commandState) &&
3440
+ Object.hasOwn(fallbackBuiltins, commandArgv[0]))))
3441
+ return builtinHelp(commandArgv[0]);
3442
+ if (commandArgv[0] === "time") {
3443
+ const started = Bun.nanoseconds();
3444
+ const execution = commandArgv.length === 1
3445
+ ? result()
3446
+ : await runCommandArgv(
3447
+ commandArgv.slice(1),
3448
+ commandState,
3449
+ input,
3450
+ captureStdout,
3451
+ captureStderr,
3452
+ );
3453
+ const milliseconds = (Bun.nanoseconds() - started) / 1_000_000;
3454
+ execution.stderr = concatBytes([
3455
+ execution.stderr,
3456
+ `real ${formatMilliseconds(milliseconds)} ms\n`,
3457
+ ]);
3458
+ return execution;
3459
+ }
3460
+ if (commandArgv[0] === "yes") return runYes(commandArgv);
3461
+ if (Object.hasOwn(commandState.functions, commandArgv[0]))
3462
+ return executeFunction(commandArgv[0], commandArgv, commandState);
3463
+ if (Object.hasOwn(builtins, commandArgv[0]))
3464
+ return builtins[commandArgv[0]](
3465
+ commandArgv,
3466
+ commandState,
3467
+ input,
3468
+ { captureStdout, captureStderr, options },
3469
+ );
3470
+ if (!commandArgv[0].includes("/") && Object.hasOwn(fallbackBuiltins, commandArgv[0]) &&
3471
+ !findExecutable(commandArgv[0], commandState))
3472
+ return fallbackBuiltins[commandArgv[0]](
3473
+ commandArgv,
3474
+ commandState,
3475
+ input,
3476
+ { captureStdout, captureStderr, options },
3477
+ );
3478
+ if (!commandState.pathSearch && !commandArgv[0].includes("/"))
3479
+ return result(127, "", `bunmsh: ${commandArgv[0]}: not found\n`);
3480
+ return runExternal(
3481
+ commandArgv,
3482
+ commandState,
3483
+ input,
3484
+ captureStdout,
3485
+ captureStderr,
3486
+ options.stdoutSink,
3487
+ options,
3488
+ );
3489
+ }
3490
+
3491
+ function redirectInput(path) {
3492
+ const fd = openSync(path, "r");
3493
+ return Readable.toWeb(createReadStream(path, { fd, autoClose: true }));
3494
+ }
3495
+
3496
+ function redirectOutput(path, append) {
3497
+ const fd = openSync(path, append ? "a" : "w");
3498
+ return Writable.toWeb(createWriteStream(path, { fd, autoClose: true }));
3499
+ }
3500
+
3501
+ // Mirrors the double-quote scanning in tokenize(): an unquoted heredoc body
3502
+ // only lets backslash escape $, `, \, and a trailing newline before the rest
3503
+ // goes through the normal $/`` expansion pipeline.
3504
+ function heredocLiteralPass(text) {
3505
+ let output = "";
3506
+ let i = 0;
3507
+ while (i < text.length) {
3508
+ if (text[i] === "\\" && i + 1 < text.length) {
3509
+ const next = text[i + 1];
3510
+ if (next === "$" || next === "\\" || next === "`" || next === "\n") {
3511
+ if (next !== "\n") output += `\u0000${next}`;
3512
+ i += 2;
3513
+ continue;
3514
+ }
3515
+ }
3516
+ if ((text[i] === "$" && ["(", "{"].includes(text[i + 1])) || text[i] === "`") {
3517
+ const end = text[i + 1] === "{" ? parameterEnd(text, i) : substitutionEnd(text, i);
3518
+ output += text.slice(i, end);
3519
+ i = end;
3520
+ continue;
3521
+ }
3522
+ output += text[i++];
3523
+ }
3524
+ return output;
3525
+ }
3526
+
3527
+ function redirectHeredoc(content) {
3528
+ return new Response(bytes(content)).body;
3529
+ }
3530
+
3531
+ async function runCommand(command, state, options = {}) {
3532
+ state.expansionStatus = null;
3533
+ const standaloneTilde =
3534
+ command.words.length === 1 &&
3535
+ command.words[0].fragments.length === 1 &&
3536
+ command.words[0].fragments[0].quote === "none" &&
3537
+ command.words[0].fragments[0].text === "~";
3538
+ let assignmentPrefix = true;
3539
+ let aliasEligible = false;
3540
+ const expanded = standaloneTilde ? ["~"] : [];
3541
+ if (!standaloneTilde) {
3542
+ for (const word of command.words) {
3543
+ const literal = word.fragments.map((fragment) => fragment.text.replaceAll("\u0000", "")).join("");
3544
+ const assignment = assignmentPrefix && /^[A-Za-z_][A-Za-z0-9_]*=/.test(literal);
3545
+ expanded.push(...await expandWord(word, state, { single: assignment, assignment }));
3546
+ if (!assignment) {
3547
+ if (assignmentPrefix)
3548
+ aliasEligible = word.fragments.every((fragment) =>
3549
+ fragment.quote === "none" && !fragment.text.includes("\u0000"));
3550
+ assignmentPrefix = false;
3551
+ }
3552
+ }
3553
+ }
3554
+ const localEnv = {};
3555
+ while (expanded.length) {
3556
+ const assignment = splitAssignment(expanded[0]);
3557
+ if (!assignment) break;
3558
+ localEnv[assignment.name] = assignment.value;
3559
+ expanded.shift();
3560
+ }
3561
+ if (expanded.length === 0) {
3562
+ for (const name of Object.keys(localEnv))
3563
+ if (state.readonly.has(name)) return readonlyError(name);
3564
+ Object.assign(state.env, localEnv);
3565
+ return result(state.expansionStatus ?? 0);
3566
+ }
3567
+ const alias = aliasEligible ? state.aliases[expanded[0]] : null;
3568
+ if (alias) expanded.splice(0, 1, ...alias);
3569
+
3570
+ let input = options.input ?? null;
3571
+ let stdoutPath = null;
3572
+ let stdoutAppend = false;
3573
+ let stderrPath = null;
3574
+ let stderrAppend = false;
3575
+ let stdoutToStderr = false;
3576
+ let stderrToStdout = false;
3577
+ for (const redirect of command.redirects) {
3578
+ if (redirect.op === "1>&2") { stdoutToStderr = true; continue; }
3579
+ if (redirect.op === "2>&1") { stderrToStdout = true; continue; }
3580
+ if (redirect.op === "<<" || redirect.op === "<<-") {
3581
+ const heredoc = redirect.heredoc ?? { body: "", expand: false };
3582
+ const content = heredoc.expand
3583
+ ? stripExpansionMarkers(
3584
+ (await expandText(heredocLiteralPass(heredoc.body), state)).replaceAll("\u0000", ""),
3585
+ )
3586
+ : heredoc.body;
3587
+ if (input instanceof ReadableStream) {
3588
+ try { await input.cancel(); } catch {}
3589
+ }
3590
+ input = redirectHeredoc(content);
3591
+ continue;
3592
+ }
3593
+ if (redirect.op === "<<<") {
3594
+ const [word] = await expandWord(redirect.target, state, { single: true, noTilde: true });
3595
+ if (input instanceof ReadableStream) {
3596
+ try { await input.cancel(); } catch {}
3597
+ }
3598
+ input = redirectHeredoc(`${word}\n`);
3599
+ continue;
3600
+ }
3601
+ const targets = await expandWord(redirect.target, state, { single: true });
3602
+ const target = targets[0];
3603
+ const path = target.startsWith("/") ? target : `${state.cwd}/${target}`;
3604
+ if (redirect.op === "<") {
3605
+ try {
3606
+ if (input instanceof ReadableStream) {
3607
+ try { await input.cancel(); } catch {}
3608
+ }
3609
+ input = redirectInput(path);
3610
+ } catch (error) {
3611
+ return result(1, "", `bunmsh: ${target}: ${error.message}\n`);
3612
+ }
3613
+ } else if (redirect.op === ">" || redirect.op === ">>") {
3614
+ stdoutPath = path;
3615
+ stdoutAppend = redirect.op === ">>";
3616
+ } else {
3617
+ stderrPath = path;
3618
+ stderrAppend = redirect.op === "2>>";
3619
+ }
3620
+ }
3621
+
3622
+
3623
+ let stdoutRedirect = null;
3624
+ let stderrRedirect = null;
3625
+ try {
3626
+ if (stdoutPath !== null) {
3627
+ // A command-level redirect replaces the pipeline's stdout connection.
3628
+ // Close that unused connection now so the next stage observes EOF.
3629
+ if (options.stdoutSink) {
3630
+ try { await options.stdoutSink.getWriter().close(); } catch {}
3631
+ }
3632
+ stdoutRedirect = redirectOutput(stdoutPath, stdoutAppend);
3633
+ }
3634
+ if (stderrPath !== null)
3635
+ stderrRedirect = redirectOutput(stderrPath, stderrAppend);
3636
+ } catch (error) {
3637
+ return result(1, "", `bunmsh: ${stdoutPath ?? stderrPath}: ${error.message}\n`);
3638
+ }
3639
+
3640
+ const commandState = options.subshell
3641
+ ? {
3642
+ ...state,
3643
+ env: { ...state.env, ...localEnv },
3644
+ directoryHistory: [...state.directoryHistory],
3645
+ tabs: [...state.tabs],
3646
+ readonly: new Set(state.readonly),
3647
+ }
3648
+ : state;
3649
+ if (!options.subshell) {
3650
+ for (const name of Object.keys(localEnv))
3651
+ if (commandState.readonly.has(name)) return readonlyError(name);
3652
+ Object.assign(commandState.env, localEnv);
3653
+ }
3654
+
3655
+ const captureStdout = (options.captureStdout || stdoutToStderr) && stdoutRedirect === null;
3656
+ const captureStderr = (options.captureStderr || stderrToStdout) && stderrRedirect === null;
3657
+ const execution = await runCommandArgv(
3658
+ expanded,
3659
+ commandState,
3660
+ input,
3661
+ captureStdout,
3662
+ captureStderr,
3663
+ {
3664
+ pipelineStage: Boolean(options.pipelineStage || stdoutRedirect || stderrRedirect),
3665
+ stdoutSink: stdoutRedirect ?? options.stdoutSink ?? null,
3666
+ stderrSink: stderrRedirect,
3667
+ onSpawn: options.onSpawn,
3668
+ onExit: options.onExit,
3669
+ },
3670
+ );
3671
+
3672
+ if (stdoutToStderr && execution.stdout.byteLength) {
3673
+ execution.stderr = concatBytes([execution.stderr, execution.stdout]);
3674
+ execution.stdout = bytes();
3675
+ }
3676
+ if (stderrToStdout && execution.stderr.byteLength) {
3677
+ execution.stdout = concatBytes([execution.stdout, execution.stderr]);
3678
+ execution.stderr = bytes();
3679
+ }
3680
+
3681
+ return execution;
3682
+ }
3683
+
3684
+ async function runPipeline(pipeline, state, options = {}) {
3685
+ if (pipeline.length === 1)
3686
+ return runCommand(pipeline[0], state, {
3687
+ captureStdout: Boolean(options.capture),
3688
+ captureStderr: Boolean(options.capture),
3689
+ });
3690
+
3691
+ const links = Array.from(
3692
+ { length: pipeline.length - 1 },
3693
+ () => new TransformStream(),
3694
+ );
3695
+ const linksConnected = links.map((_, i) => {
3696
+ const stdoutRedirected = pipeline[i].redirects.some((redirect) =>
3697
+ redirect.op === ">" || redirect.op === ">>");
3698
+ const stdinRedirected = pipeline[i + 1].redirects.some((redirect) =>
3699
+ ["<", "<<", "<<-", "<<<"].includes(redirect.op));
3700
+ return !stdoutRedirected && !stdinRedirected;
3701
+ });
3702
+ const processes = Array(pipeline.length).fill(null);
3703
+ const finished = Array(pipeline.length).fill(false);
3704
+ const stopUpstream = (index) => {
3705
+ if (index < 0 || !linksConnected[index]) return;
3706
+ const processInfo = processes[index];
3707
+ if (processInfo) {
3708
+ try { processInfo.proc.kill(processInfo.signal); } catch {}
3709
+ // Killing the process only stops it from producing more output. Its
3710
+ // own `proc.stdout.pipeTo()` forwarding that output into this link can
3711
+ // still be stuck mid-write, backpressured forever with nobody left to
3712
+ // read the link's other end now that the downstream stage is done —
3713
+ // see runExternal for why that write can't be canceled directly.
3714
+ // This just stops waiting on it instead.
3715
+ try { processInfo.detachStream(); } catch {}
3716
+ }
3717
+ };
3718
+ const stages = pipeline.map((command, i) => {
3719
+ const last = i === pipeline.length - 1;
3720
+ return runCommand(command, state, {
3721
+ input: i === 0 ? null : links[i - 1].readable,
3722
+ captureStdout: last && Boolean(options.capture),
3723
+ captureStderr: Boolean(options.capture),
3724
+ stdoutSink: last ? null : links[i].writable,
3725
+ pipelineStage: true,
3726
+ subshell: true,
3727
+ onSpawn(proc, signal, detachStream) {
3728
+ processes[i] = { proc, signal, detachStream };
3729
+ if (finished[i + 1]) stopUpstream(i);
3730
+ },
3731
+ onExit() {
3732
+ finished[i] = true;
3733
+ stopUpstream(i - 1);
3734
+ },
3735
+ }).then(async (execution) => {
3736
+ // Redirected commands and any future buffered pipeline stages do not
3737
+ // consume stdoutSink themselves. Forward their finite result and close
3738
+ // the link here. Streaming subprocesses already closed it via pipeTo().
3739
+ if (!last && !execution.stdoutStreamed) {
3740
+ const writer = links[i].writable.getWriter();
3741
+ try {
3742
+ if (execution.stdout.byteLength) await writer.write(execution.stdout);
3743
+ await writer.close();
3744
+ } catch {}
3745
+ }
3746
+ return execution;
3747
+ }).finally(() => {
3748
+ finished[i] = true;
3749
+ stopUpstream(i - 1);
3750
+ });
3751
+ });
3752
+ const executions = await Promise.all(stages);
3753
+ const final = executions.at(-1);
3754
+ if (options.capture) {
3755
+ final.stderr = concatBytes(executions.map((execution) => execution.stderr));
3756
+ }
3757
+ return final;
3758
+ }
3759
+
3760
+ export async function execute(source, state = createState(), io = {}) {
3761
+ source = normalizeSource(source);
3762
+ if (source === "?") {
3763
+ const execution = result(0, `${state.lastStatus}\n`);
3764
+ state.lastStatus = 0;
3765
+ if (!io.capture) await writeStream(process.stdout, execution.stdout);
3766
+ return execution;
3767
+ }
3768
+ const rawSource = source.trimStart();
3769
+ if (isJavaScriptMode(rawSource)) {
3770
+ let execution;
3771
+ const previousCwd = process.cwd();
3772
+ try {
3773
+ try {
3774
+ process.chdir(nativePath(state.cwd));
3775
+
3776
+ const $ = state.env
3777
+
3778
+ const value = await eval(rawSource);
3779
+ execution = result(0, value === undefined ? "" : `${formatValue(value)}\n`);
3780
+ } finally {
3781
+ process.chdir(previousCwd);
3782
+ }
3783
+ } catch (error) {
3784
+ execution = result(1, "", `${error?.stack ?? formatValue(error)}\n`);
3785
+ }
3786
+ state.lastStatus = execution.status;
3787
+ if (!io.capture) {
3788
+ if (execution.stdout.byteLength) await writeStream(process.stdout, execution.stdout);
3789
+ if (execution.stderr.byteLength) await writeStream(process.stderr, execution.stderr);
3790
+ }
3791
+ return execution;
3792
+ }
3793
+ const lines = source.match(/.*(?:\n|$)/g)?.filter(Boolean) ?? [];
3794
+ if (lines.some((line) => isJavaScriptMode(line))) {
3795
+ const parts = [];
3796
+ let shellSource = "";
3797
+ for (const line of lines) {
3798
+ if (!isJavaScriptMode(line)) {
3799
+ shellSource += line;
3800
+ continue;
3801
+ }
3802
+ // Only split at a valid top-level shell boundary. This prevents a
3803
+ // Bun.-looking line inside an open quote or $(...) from escaping its
3804
+ // surrounding shell construct.
3805
+ try {
3806
+ if (shellSource.trim()) parse(shellSource);
3807
+ } catch {
3808
+ shellSource += line;
3809
+ continue;
3810
+ }
3811
+ if (shellSource.trim()) parts.push(shellSource);
3812
+ parts.push(line.replace(/\n$/, ""));
3813
+ shellSource = "";
3814
+ }
3815
+ if (shellSource.trim()) parts.push(shellSource);
3816
+ if (parts.length > 1) {
3817
+ let execution = result(state.lastStatus);
3818
+ const stdout = [], stderr = [];
3819
+ for (const part of parts) {
3820
+ execution = await execute(part, state, io);
3821
+ if (io.capture) {
3822
+ if (execution.stdout.byteLength) stdout.push(execution.stdout);
3823
+ if (execution.stderr.byteLength) stderr.push(execution.stderr);
3824
+ }
3825
+ if (state.exitRequested) break;
3826
+ }
3827
+ return io.capture
3828
+ ? result(execution.status, concatBytes(stdout), concatBytes(stderr))
3829
+ : execution;
3830
+ }
3831
+ }
3832
+ let compoundSyntax = false;
3833
+ try { compoundSyntax = !io.simple && hasCompoundSyntax(source); } catch {}
3834
+ if (compoundSyntax) {
3835
+ let execution;
3836
+ try {
3837
+ execution = await executeNodeList(parseCompoundScript(source), state, {
3838
+ capture: Boolean(io.capture),
3839
+ });
3840
+ }
3841
+ catch (error) {
3842
+ const message = error instanceof ShellSyntaxError ? error.message : String(error);
3843
+ execution = result(2, "", `bunmsh: syntax error: ${message}\n`);
3844
+ }
3845
+ state.lastStatus = execution.status;
3846
+ if (!io.capture) {
3847
+ if (execution.stdout.byteLength) await writeStream(process.stdout, execution.stdout);
3848
+ if (execution.stderr.byteLength) await writeStream(process.stderr, execution.stderr);
3849
+ }
3850
+ return execution;
3851
+ }
3852
+
3853
+ let jobs;
3854
+ try {
3855
+ jobs = parse(source);
3856
+ } catch (error) {
3857
+ const message = error instanceof ShellSyntaxError ? error.message : String(error);
3858
+ const output = `bunmsh: syntax error: ${message}\n`;
3859
+ if (io.capture) return result(2, "", output);
3860
+ console.error(output.trimEnd());
3861
+ state.lastStatus = 2;
3862
+ return result(2);
3863
+ }
3864
+
3865
+ let execution = result(state.lastStatus);
3866
+ const capturedStdout = [];
3867
+ const capturedStderr = [];
3868
+ for (const job of jobs) {
3869
+ if (job.connector === "&&" && execution.status !== 0) continue;
3870
+ if (job.connector === "||" && execution.status === 0) continue;
3871
+ execution = await runPipeline(job.pipeline, state, { capture: io.capture });
3872
+ if (job.negate) execution.status = execution.status === 0 ? 1 : 0;
3873
+ state.lastStatus = execution.status;
3874
+ if (execution.stdout.byteLength) {
3875
+ if (io.capture) capturedStdout.push(execution.stdout);
3876
+ else await writeStream(process.stdout, execution.stdout);
3877
+ }
3878
+ if (execution.stderr.byteLength) {
3879
+ if (io.capture) capturedStderr.push(execution.stderr);
3880
+ else await writeStream(process.stderr, execution.stderr);
3881
+ }
3882
+ if (state.exitRequested) break;
3883
+ }
3884
+ return io.capture
3885
+ ? result(execution.status, concatBytes(capturedStdout), concatBytes(capturedStderr))
3886
+ : execution;
3887
+ }
3888
+
3889
+ export async function executeArgv(argv, state = createState(), io = {}) {
3890
+ if (argv.length === 0)
3891
+ return result(2, "", "bunmsh: -cc requires a command\n");
3892
+ const commandArgv = [...argv];
3893
+ const execution = await runCommandArgv(
3894
+ commandArgv,
3895
+ state,
3896
+ null,
3897
+ Boolean(io.capture),
3898
+ Boolean(io.capture),
3899
+ );
3900
+ state.lastStatus = execution.status;
3901
+ if (!io.capture) {
3902
+ if (execution.stdout.byteLength) await writeStream(process.stdout, execution.stdout);
3903
+ if (execution.stderr.byteLength) await writeStream(process.stderr, execution.stderr);
3904
+ }
3905
+ return execution;
3906
+ }
3907
+
3908
+ export function pipelineChildState() {
3909
+ const serialized = process.env[PIPELINE_STATE_ENV];
3910
+ if (!serialized) return createState();
3911
+ try {
3912
+ const inherited = JSON.parse(serialized);
3913
+ const env = { ...process.env };
3914
+ delete env[PIPELINE_STATE_ENV];
3915
+ return createState({
3916
+ env,
3917
+ cwd: process.cwd(),
3918
+ aliases: inherited.aliases,
3919
+ functions: inherited.functions,
3920
+ readonly: inherited.readonly,
3921
+ args: inherited.args,
3922
+ pathSearch: inherited.pathSearch,
3923
+ pipelineChild: true,
3924
+ });
3925
+ } catch {
3926
+ return createState();
3927
+ }
3928
+ }
3929
+
3930
+ export function decode(data) {
3931
+ return decoder.decode(data);
3932
+ }