typescript-virtual-container 1.5.3 → 1.5.4

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 (365) hide show
  1. package/README.md +43 -23
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/SSHMimic/executor.js +23 -5
  4. package/dist/VirtualPackageManager/index.js +10 -0
  5. package/dist/commands/basename.d.ts +13 -0
  6. package/dist/commands/basename.js +45 -0
  7. package/dist/commands/file.d.ts +8 -0
  8. package/dist/commands/file.js +57 -0
  9. package/dist/commands/fun.d.ts +32 -0
  10. package/dist/commands/fun.js +172 -0
  11. package/dist/commands/ifconfig.d.ts +7 -0
  12. package/dist/commands/ifconfig.js +52 -0
  13. package/dist/commands/last.d.ts +13 -0
  14. package/dist/commands/last.js +68 -0
  15. package/dist/commands/manuals-bundle.js +598 -6
  16. package/dist/commands/registry.js +24 -2
  17. package/dist/commands/runtime.js +22 -2
  18. package/dist/commands/sh.js +5 -0
  19. package/dist/commands/tput.d.ts +13 -0
  20. package/dist/commands/tput.js +76 -0
  21. package/dist/commands/w.d.ts +7 -0
  22. package/dist/commands/w.js +38 -0
  23. package/dist/utils/expand.d.ts +12 -0
  24. package/dist/utils/expand.js +84 -0
  25. package/package.json +9 -3
  26. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -50
  27. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -31
  28. package/.github/dependabot.yml +0 -27
  29. package/.github/pull_request_template.md +0 -21
  30. package/.github/workflows/create-pull-request.yml +0 -85
  31. package/.github/workflows/publish.yml +0 -25
  32. package/.github/workflows/test-battery.yml +0 -102
  33. package/.vscode/settings.json +0 -20
  34. package/CODE_OF_CONDUCT.md +0 -39
  35. package/CONTRIBUTING.md +0 -59
  36. package/HONEYPOT.md +0 -358
  37. package/SECURITY.md +0 -33
  38. package/benchmark-results.txt +0 -40
  39. package/benchmark-virtualshell.ts +0 -88
  40. package/biome.json +0 -37
  41. package/build.js +0 -22
  42. package/builds/fortune-nyx-v1.5.3-directbash-k6.1.0.mjs +0 -1764
  43. package/builds/fortune-nyx-v1.5.3-ssh-nosftp.js +0 -1764
  44. package/builds/fortune-nyx-v1.5.3-ssh.cjs +0 -1765
  45. package/builds/fortune-nyx-v1.5.3-web.min.js +0 -17036
  46. package/bun.lock +0 -244
  47. package/docs/.nojekyll +0 -1
  48. package/docs/app.js +0 -1751
  49. package/docs/assets/hierarchy.js +0 -1
  50. package/docs/assets/highlight.css +0 -162
  51. package/docs/assets/icons.js +0 -18
  52. package/docs/assets/icons.svg +0 -1
  53. package/docs/assets/main.js +0 -60
  54. package/docs/assets/navigation.js +0 -1
  55. package/docs/assets/search.js +0 -1
  56. package/docs/assets/style.css +0 -1633
  57. package/docs/classes/HoneyPot.html +0 -31
  58. package/docs/classes/IdleManager.html +0 -162
  59. package/docs/classes/SshClient.html +0 -66
  60. package/docs/classes/VirtualFileSystem.html +0 -279
  61. package/docs/classes/VirtualPackageManager.html +0 -63
  62. package/docs/classes/VirtualSftpServer.html +0 -169
  63. package/docs/classes/VirtualShell.html +0 -285
  64. package/docs/classes/VirtualSshServer.html +0 -182
  65. package/docs/classes/VirtualUserManager.html +0 -276
  66. package/docs/demo.html +0 -82
  67. package/docs/functions/assertDiff.html +0 -6
  68. package/docs/functions/diffSnapshots.html +0 -7
  69. package/docs/functions/formatDiff.html +0 -6
  70. package/docs/functions/getArg.html +0 -13
  71. package/docs/functions/getFlag.html +0 -15
  72. package/docs/functions/ifFlag.html +0 -11
  73. package/docs/hierarchy.html +0 -1
  74. package/docs/index.html +0 -1869
  75. package/docs/interfaces/AuditLogEntry.html +0 -6
  76. package/docs/interfaces/CommandContext.html +0 -22
  77. package/docs/interfaces/CommandResult.html +0 -26
  78. package/docs/interfaces/ExecStream.html +0 -11
  79. package/docs/interfaces/HoneyPotStats.html +0 -16
  80. package/docs/interfaces/IdleManagerOptions.html +0 -7
  81. package/docs/interfaces/InstalledPackage.html +0 -20
  82. package/docs/interfaces/NanoEditorSession.html +0 -8
  83. package/docs/interfaces/PackageDefinition.html +0 -30
  84. package/docs/interfaces/PackageFile.html +0 -8
  85. package/docs/interfaces/PasswordChallenge.html +0 -16
  86. package/docs/interfaces/RemoveOptions.html +0 -4
  87. package/docs/interfaces/ShellEnv.html +0 -6
  88. package/docs/interfaces/ShellModule.html +0 -14
  89. package/docs/interfaces/ShellProperties.html +0 -14
  90. package/docs/interfaces/ShellStream.html +0 -11
  91. package/docs/interfaces/SudoChallenge.html +0 -24
  92. package/docs/interfaces/VfsBaseNode.html +0 -12
  93. package/docs/interfaces/VfsDiff.html +0 -10
  94. package/docs/interfaces/VfsDiffEntry.html +0 -6
  95. package/docs/interfaces/VfsDiffModified.html +0 -10
  96. package/docs/interfaces/VfsDirectoryNode.html +0 -15
  97. package/docs/interfaces/VfsFileNode.html +0 -17
  98. package/docs/interfaces/VfsOptions.html +0 -26
  99. package/docs/interfaces/VfsSnapshot.html +0 -3
  100. package/docs/interfaces/VfsSnapshotBaseNode.html +0 -8
  101. package/docs/interfaces/VfsSnapshotDirectoryNode.html +0 -10
  102. package/docs/interfaces/VfsSnapshotFileNode.html +0 -12
  103. package/docs/interfaces/VirtualActiveSession.html +0 -12
  104. package/docs/interfaces/VirtualSftpServerOptions.html +0 -7
  105. package/docs/interfaces/VirtualShellVfsLike.html +0 -15
  106. package/docs/interfaces/VirtualShellVfsOptions.html +0 -3
  107. package/docs/interfaces/WriteFileOptions.html +0 -6
  108. package/docs/media/LICENSE +0 -21
  109. package/docs/modules.html +0 -1
  110. package/docs/types/ArgParseOptions.html +0 -4
  111. package/docs/types/CommandMode.html +0 -2
  112. package/docs/types/CommandOutcome.html +0 -2
  113. package/docs/types/IdleState.html +0 -1
  114. package/docs/types/VfsNodeStats.html +0 -2
  115. package/docs/types/VfsNodeType.html +0 -2
  116. package/docs/types/VfsPersistenceMode.html +0 -5
  117. package/docs/types/VfsSnapshotNode.html +0 -2
  118. package/examples/README.md +0 -288
  119. package/examples/app.js +0 -1751
  120. package/examples/app.ts +0 -299
  121. package/examples/build.js +0 -27
  122. package/examples/demo.html +0 -33
  123. package/examples/honeypot-audit.ts +0 -180
  124. package/examples/honeypot-export.ts +0 -253
  125. package/examples/honeypot-quickstart.ts +0 -110
  126. package/examples/index.html +0 -82
  127. package/examples/server.js +0 -55
  128. package/polyfills/buffer.js +0 -117
  129. package/polyfills/node_child_process/index.js +0 -2
  130. package/polyfills/node_crypto/index.js +0 -167
  131. package/polyfills/node_events/index.js +0 -9
  132. package/polyfills/node_fs/index.js +0 -202
  133. package/polyfills/node_fs/promises.js +0 -4
  134. package/polyfills/node_os/index.js +0 -9
  135. package/polyfills/node_path/index.js +0 -28
  136. package/polyfills/node_vm/index.js +0 -7
  137. package/polyfills/node_zlib/index.js +0 -3
  138. package/polyfills/process.js +0 -14
  139. package/polyfills/ssh2/index.js +0 -75
  140. package/scripts/build-all.mjs +0 -226
  141. package/scripts/build-names.mjs +0 -43
  142. package/scripts/generate-manuals-bundle.mjs +0 -49
  143. package/scripts/postinstall.js +0 -42
  144. package/scripts/publish-package.sh +0 -70
  145. package/src/Honeypot/index.ts +0 -457
  146. package/src/SSHClient/index.ts +0 -270
  147. package/src/SSHMimic/exec.ts +0 -49
  148. package/src/SSHMimic/executor.ts +0 -251
  149. package/src/SSHMimic/hostKey.ts +0 -21
  150. package/src/SSHMimic/index.ts +0 -337
  151. package/src/SSHMimic/loginBanner.ts +0 -36
  152. package/src/SSHMimic/loginFormat.ts +0 -10
  153. package/src/SSHMimic/prompt.ts +0 -14
  154. package/src/SSHMimic/sftp.ts +0 -883
  155. package/src/VirtualFileSystem/binaryPack.ts +0 -258
  156. package/src/VirtualFileSystem/index.ts +0 -1193
  157. package/src/VirtualFileSystem/internalTypes.ts +0 -43
  158. package/src/VirtualFileSystem/journal.ts +0 -171
  159. package/src/VirtualFileSystem/path.ts +0 -74
  160. package/src/VirtualPackageManager/index.ts +0 -996
  161. package/src/VirtualShell/idleManager.ts +0 -137
  162. package/src/VirtualShell/index.ts +0 -475
  163. package/src/VirtualShell/shell.ts +0 -700
  164. package/src/VirtualShell/shellParser.ts +0 -285
  165. package/src/VirtualUserManager/index.ts +0 -758
  166. package/src/bun.d.ts +0 -1
  167. package/src/commands/adduser.ts +0 -103
  168. package/src/commands/alias.ts +0 -69
  169. package/src/commands/apt.ts +0 -233
  170. package/src/commands/awk.ts +0 -168
  171. package/src/commands/base64.ts +0 -29
  172. package/src/commands/cat.ts +0 -52
  173. package/src/commands/cd.ts +0 -25
  174. package/src/commands/chmod.ts +0 -85
  175. package/src/commands/clear.ts +0 -15
  176. package/src/commands/command-helpers.ts +0 -286
  177. package/src/commands/cp.ts +0 -83
  178. package/src/commands/curl.ts +0 -147
  179. package/src/commands/cut.ts +0 -36
  180. package/src/commands/date.ts +0 -30
  181. package/src/commands/declare.ts +0 -49
  182. package/src/commands/deluser.ts +0 -98
  183. package/src/commands/df.ts +0 -23
  184. package/src/commands/diff.ts +0 -43
  185. package/src/commands/dpkg.ts +0 -180
  186. package/src/commands/du.ts +0 -56
  187. package/src/commands/echo.ts +0 -58
  188. package/src/commands/env.ts +0 -23
  189. package/src/commands/exit.ts +0 -18
  190. package/src/commands/export.ts +0 -34
  191. package/src/commands/find.ts +0 -68
  192. package/src/commands/free.ts +0 -47
  193. package/src/commands/grep.ts +0 -116
  194. package/src/commands/groups.ts +0 -19
  195. package/src/commands/gzip.ts +0 -88
  196. package/src/commands/head.ts +0 -52
  197. package/src/commands/help.ts +0 -152
  198. package/src/commands/helpers.ts +0 -234
  199. package/src/commands/history.ts +0 -34
  200. package/src/commands/hostname.ts +0 -14
  201. package/src/commands/htop.ts +0 -20
  202. package/src/commands/id.ts +0 -19
  203. package/src/commands/index.ts +0 -9
  204. package/src/commands/kill.ts +0 -19
  205. package/src/commands/ln.ts +0 -71
  206. package/src/commands/ls.ts +0 -243
  207. package/src/commands/lsb-release.ts +0 -63
  208. package/src/commands/man.ts +0 -31
  209. package/src/commands/manuals/adduser.txt +0 -11
  210. package/src/commands/manuals/apt-cache.txt +0 -12
  211. package/src/commands/manuals/apt.txt +0 -20
  212. package/src/commands/manuals/awk.txt +0 -13
  213. package/src/commands/manuals/cat.txt +0 -14
  214. package/src/commands/manuals/cd.txt +0 -16
  215. package/src/commands/manuals/chmod.txt +0 -16
  216. package/src/commands/manuals/clear.txt +0 -10
  217. package/src/commands/manuals/cp.txt +0 -10
  218. package/src/commands/manuals/curl.txt +0 -20
  219. package/src/commands/manuals/date.txt +0 -14
  220. package/src/commands/manuals/declare.txt +0 -12
  221. package/src/commands/manuals/deluser.txt +0 -10
  222. package/src/commands/manuals/df.txt +0 -10
  223. package/src/commands/manuals/dpkg-query.txt +0 -11
  224. package/src/commands/manuals/dpkg.txt +0 -14
  225. package/src/commands/manuals/du.txt +0 -11
  226. package/src/commands/manuals/echo.txt +0 -11
  227. package/src/commands/manuals/false.txt +0 -10
  228. package/src/commands/manuals/find.txt +0 -11
  229. package/src/commands/manuals/free.txt +0 -12
  230. package/src/commands/manuals/grep.txt +0 -13
  231. package/src/commands/manuals/groups.txt +0 -10
  232. package/src/commands/manuals/gzip.txt +0 -11
  233. package/src/commands/manuals/head.txt +0 -10
  234. package/src/commands/manuals/help.txt +0 -11
  235. package/src/commands/manuals/history.txt +0 -11
  236. package/src/commands/manuals/hostname.txt +0 -10
  237. package/src/commands/manuals/id.txt +0 -10
  238. package/src/commands/manuals/kill.txt +0 -13
  239. package/src/commands/manuals/ls.txt +0 -20
  240. package/src/commands/manuals/lsb_release.txt +0 -14
  241. package/src/commands/manuals/mkdir.txt +0 -10
  242. package/src/commands/manuals/mv.txt +0 -10
  243. package/src/commands/manuals/nano.txt +0 -11
  244. package/src/commands/manuals/neofetch.txt +0 -10
  245. package/src/commands/manuals/node.txt +0 -13
  246. package/src/commands/manuals/npm.txt +0 -13
  247. package/src/commands/manuals/npx.txt +0 -13
  248. package/src/commands/manuals/passwd.txt +0 -11
  249. package/src/commands/manuals/ping.txt +0 -10
  250. package/src/commands/manuals/printf.txt +0 -11
  251. package/src/commands/manuals/ps.txt +0 -10
  252. package/src/commands/manuals/pwd.txt +0 -10
  253. package/src/commands/manuals/python3.txt +0 -13
  254. package/src/commands/manuals/readlink.txt +0 -10
  255. package/src/commands/manuals/return.txt +0 -10
  256. package/src/commands/manuals/rm.txt +0 -10
  257. package/src/commands/manuals/sed.txt +0 -11
  258. package/src/commands/manuals/set.txt +0 -11
  259. package/src/commands/manuals/shift.txt +0 -10
  260. package/src/commands/manuals/sleep.txt +0 -10
  261. package/src/commands/manuals/sort.txt +0 -12
  262. package/src/commands/manuals/source.txt +0 -11
  263. package/src/commands/manuals/ssh.txt +0 -11
  264. package/src/commands/manuals/stat.txt +0 -10
  265. package/src/commands/manuals/su.txt +0 -13
  266. package/src/commands/manuals/sudo.txt +0 -11
  267. package/src/commands/manuals/tail.txt +0 -10
  268. package/src/commands/manuals/tar.txt +0 -19
  269. package/src/commands/manuals/tee.txt +0 -10
  270. package/src/commands/manuals/test.txt +0 -11
  271. package/src/commands/manuals/touch.txt +0 -11
  272. package/src/commands/manuals/tr.txt +0 -10
  273. package/src/commands/manuals/trap.txt +0 -10
  274. package/src/commands/manuals/true.txt +0 -10
  275. package/src/commands/manuals/type.txt +0 -10
  276. package/src/commands/manuals/uname.txt +0 -12
  277. package/src/commands/manuals/uniq.txt +0 -12
  278. package/src/commands/manuals/unset.txt +0 -10
  279. package/src/commands/manuals/uptime.txt +0 -11
  280. package/src/commands/manuals/wc.txt +0 -12
  281. package/src/commands/manuals/wget.txt +0 -12
  282. package/src/commands/manuals/which.txt +0 -10
  283. package/src/commands/manuals/whoami.txt +0 -10
  284. package/src/commands/manuals/xargs.txt +0 -10
  285. package/src/commands/manuals-bundle.ts +0 -898
  286. package/src/commands/mkdir.ts +0 -31
  287. package/src/commands/mv.ts +0 -50
  288. package/src/commands/nano.ts +0 -38
  289. package/src/commands/neofetch.ts +0 -53
  290. package/src/commands/node.ts +0 -341
  291. package/src/commands/npm.ts +0 -132
  292. package/src/commands/passwd.ts +0 -50
  293. package/src/commands/ping.ts +0 -32
  294. package/src/commands/printf.ts +0 -129
  295. package/src/commands/ps.ts +0 -58
  296. package/src/commands/pwd.ts +0 -9
  297. package/src/commands/python.ts +0 -2229
  298. package/src/commands/read.ts +0 -46
  299. package/src/commands/registry.ts +0 -249
  300. package/src/commands/rm.ts +0 -42
  301. package/src/commands/runtime.ts +0 -421
  302. package/src/commands/sed.ts +0 -68
  303. package/src/commands/seq.ts +0 -43
  304. package/src/commands/set.ts +0 -29
  305. package/src/commands/sh.ts +0 -467
  306. package/src/commands/shift.ts +0 -63
  307. package/src/commands/sleep.ts +0 -20
  308. package/src/commands/sort.ts +0 -46
  309. package/src/commands/source.ts +0 -52
  310. package/src/commands/stat.ts +0 -61
  311. package/src/commands/su.ts +0 -72
  312. package/src/commands/sudo.ts +0 -76
  313. package/src/commands/tail.ts +0 -53
  314. package/src/commands/tar.ts +0 -102
  315. package/src/commands/tee.ts +0 -36
  316. package/src/commands/test.ts +0 -137
  317. package/src/commands/touch.ts +0 -28
  318. package/src/commands/tr.ts +0 -70
  319. package/src/commands/tree.ts +0 -20
  320. package/src/commands/true.ts +0 -27
  321. package/src/commands/type.ts +0 -48
  322. package/src/commands/uname.ts +0 -29
  323. package/src/commands/uniq.ts +0 -39
  324. package/src/commands/unset.ts +0 -17
  325. package/src/commands/uptime.ts +0 -54
  326. package/src/commands/wc.ts +0 -55
  327. package/src/commands/wget.ts +0 -148
  328. package/src/commands/which.ts +0 -37
  329. package/src/commands/who.ts +0 -25
  330. package/src/commands/whoami.ts +0 -14
  331. package/src/commands/xargs.ts +0 -31
  332. package/src/index.ts +0 -67
  333. package/src/modules/linuxRootfs.ts +0 -1961
  334. package/src/modules/neofetch.ts +0 -358
  335. package/src/modules/shellInteractive.ts +0 -57
  336. package/src/modules/shellRuntime.ts +0 -76
  337. package/src/self-standalone.ts +0 -542
  338. package/src/standalone-wo-sftp.ts +0 -38
  339. package/src/standalone.ts +0 -72
  340. package/src/types/commands.ts +0 -146
  341. package/src/types/pipeline.ts +0 -52
  342. package/src/types/streams.ts +0 -32
  343. package/src/types/tar-stream.d.ts +0 -38
  344. package/src/types/vfs.ts +0 -98
  345. package/src/utils/expand.ts +0 -491
  346. package/src/utils/perfLogger.ts +0 -72
  347. package/src/utils/tokenize.ts +0 -98
  348. package/src/utils/vfsDiff.ts +0 -275
  349. package/tests/command-helpers.test.ts +0 -116
  350. package/tests/commands-admin-net.test.ts +0 -441
  351. package/tests/commands-advanced.test.ts +0 -456
  352. package/tests/commands-core.test.ts +0 -562
  353. package/tests/commands-missing.test.ts +0 -570
  354. package/tests/commands-specific-units.test.ts +0 -327
  355. package/tests/commands-text-sys.test.ts +0 -445
  356. package/tests/expand.test.ts +0 -170
  357. package/tests/helpers.test.ts +0 -97
  358. package/tests/new-features.test.ts +0 -1036
  359. package/tests/parser-executor.test.ts +0 -37
  360. package/tests/sftp.test.ts +0 -323
  361. package/tests/ssh-exec.test.ts +0 -45
  362. package/tests/test-helper.ts +0 -79
  363. package/tests/users.test.ts +0 -86
  364. package/tsconfig.json +0 -49
  365. package/typedoc.json +0 -47
@@ -1,421 +0,0 @@
1
- /** biome-ignore-all lint/style/useNamingConvention: ENV VARIABLES */
2
- import { executeStatements } from "../SSHMimic/executor";
3
- import type { VirtualShell } from "../VirtualShell";
4
- import { parseScript } from "../VirtualShell/shellParser";
5
- import type {
6
- CommandMode,
7
- CommandResult,
8
- ShellEnv,
9
- } from "../types/commands";
10
- import { expandAsync, expandBraces } from "../utils/expand";
11
- import { tokenizeCommand } from "../utils/tokenize";
12
- import { resolveModule } from "./registry";
13
-
14
- /** Returns the home directory path for a given user. Root lives at /root. */
15
- export function userHome(authUser: string): string {
16
- return authUser === "root" ? "/root" : `/home/${authUser}`;
17
- }
18
-
19
- export function makeDefaultEnv(authUser: string, hostname: string): ShellEnv {
20
- return {
21
- vars: {
22
- PATH: "/usr/local/bin:/usr/bin:/bin",
23
- HOME: userHome(authUser),
24
- USER: authUser,
25
- LOGNAME: authUser,
26
- SHELL: "/bin/sh",
27
- TERM: "xterm-256color",
28
- HOSTNAME: hostname,
29
- PS1: "\\u@\\h:\\w\\$ ",
30
- },
31
- lastExitCode: 0,
32
- };
33
- }
34
-
35
- function resolveVfsBinary(
36
- name: string,
37
- env: ShellEnv,
38
- shell: VirtualShell,
39
- authUser: string,
40
- ): string | null {
41
- if (name.startsWith("/")) {
42
- if (!shell.vfs.exists(name)) return null;
43
- try {
44
- const st = shell.vfs.stat(name);
45
- if (st.type !== "file") return null;
46
- if (!(st.mode & 0o111)) return null;
47
- if (
48
- (name.startsWith("/sbin/") || name.startsWith("/usr/sbin/")) &&
49
- authUser !== "root"
50
- )
51
- return null;
52
- return name;
53
- } catch {
54
- return null;
55
- }
56
- }
57
-
58
- const pathDirs = (env.vars.PATH ?? "/usr/local/bin:/usr/bin:/bin").split(":");
59
- for (const dir of pathDirs) {
60
- if ((dir === "/sbin" || dir === "/usr/sbin") && authUser !== "root")
61
- continue;
62
- const full = `${dir}/${name}`;
63
- if (!shell.vfs.exists(full)) continue;
64
- try {
65
- const st = shell.vfs.stat(full);
66
- if (st.type !== "file") continue;
67
- if (!(st.mode & 0o111)) continue;
68
- return full;
69
- } catch {}
70
- }
71
- return null;
72
- }
73
-
74
- const MAX_CALL_DEPTH = 8;
75
- let _callDepth = 0;
76
-
77
- export async function runCommandDirect(
78
- name: string,
79
- args: string[],
80
- authUser: string,
81
- hostname: string,
82
- mode: CommandMode,
83
- cwd: string,
84
- shell: VirtualShell,
85
- stdin: string | undefined,
86
- env: ShellEnv,
87
- ): Promise<CommandResult> {
88
- // Anti-loop guard: track call depth via env to avoid infinite recursion
89
- _callDepth++;
90
- // console.debug(`[depth=${_callDepth}] runCommandDirect: ${name}`);
91
- if (_callDepth > MAX_CALL_DEPTH) {
92
- _callDepth--;
93
- // console.debug(`[LOOP DETECTED] runCommandDirect blocked: ${name}`);
94
- return { stderr: `${name}: maximum call depth (${MAX_CALL_DEPTH}) exceeded`, exitCode: 126 };
95
- }
96
- try {
97
- return await _runCommandDirectInner(name, args, authUser, hostname, mode, cwd, shell, stdin, env);
98
- } finally {
99
- _callDepth--;
100
- }
101
- }
102
-
103
- async function _runCommandDirectInner(
104
- name: string,
105
- args: string[],
106
- authUser: string,
107
- hostname: string,
108
- mode: CommandMode,
109
- cwd: string,
110
- shell: VirtualShell,
111
- stdin: string | undefined,
112
- env: ShellEnv,
113
- ): Promise<CommandResult> {
114
- const assignRe = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
115
- const invocation = [name, ...args];
116
- let assignCount = 0;
117
- while (assignCount < invocation.length && assignRe.test(invocation[assignCount]!)) {
118
- assignCount += 1;
119
- }
120
- if (assignCount > 0) {
121
- const assignments = invocation.slice(0, assignCount).map((token) => token.match(assignRe)!);
122
- const remaining = invocation.slice(assignCount);
123
- const restored: Array<[string, string | undefined]> = [];
124
- for (const [, key, value] of assignments) {
125
- restored.push([key!, env.vars[key!]]);
126
- env.vars[key!] = value!;
127
- }
128
- if (remaining.length === 0) {
129
- return { exitCode: 0 };
130
- }
131
- try {
132
- const result = await runCommandDirect(
133
- remaining[0]!,
134
- remaining.slice(1),
135
- authUser,
136
- hostname,
137
- mode,
138
- cwd,
139
- shell,
140
- stdin,
141
- env,
142
- );
143
- return result;
144
- } finally {
145
- for (const [key, value] of restored) {
146
- if (value === undefined) delete env.vars[key];
147
- else env.vars[key] = value;
148
- }
149
- }
150
- }
151
-
152
- const aliasVal = env.vars[`__alias_${name}`];
153
- if (aliasVal) {
154
- return runCommand(
155
- `${aliasVal} ${args.join(" ")}`,
156
- authUser,
157
- hostname,
158
- mode,
159
- cwd,
160
- shell,
161
- stdin,
162
- env,
163
- );
164
- }
165
-
166
- const mod = resolveModule(name);
167
- if (!mod) {
168
- const vfsBinary = resolveVfsBinary(name, env, shell, authUser);
169
- if (vfsBinary) {
170
- const stubContent = shell.vfs.readFile(vfsBinary);
171
- const builtinMatch = stubContent.match(/exec\s+builtin\s+(\S+)/);
172
- if (builtinMatch) {
173
- const builtinMod = resolveModule(builtinMatch[1]!);
174
- if (builtinMod) {
175
- return await builtinMod.run({
176
- authUser,
177
- hostname,
178
- activeSessions: shell.users.listActiveSessions(),
179
- rawInput: [name, ...args].join(" "),
180
- mode,
181
- args,
182
- stdin,
183
- cwd,
184
- shell,
185
- env,
186
- });
187
- }
188
- // builtin not found — stop here, don't fall through to sh -c (avoids infinite loop)
189
- return { stderr: `${name}: exec builtin '${builtinMatch[1]}' not found`, exitCode: 127 };
190
- }
191
- const shMod = resolveModule("sh");
192
- if (shMod) {
193
- return await shMod.run({
194
- authUser,
195
- hostname,
196
- activeSessions: shell.users.listActiveSessions(),
197
- rawInput: `sh -c ${JSON.stringify(stubContent)}`,
198
- mode,
199
- args: ["-c", stubContent, "--", ...args],
200
- stdin,
201
- cwd,
202
- shell,
203
- env,
204
- });
205
- }
206
- }
207
- return { stderr: `${name}: command not found`, exitCode: 127 };
208
- }
209
-
210
- try {
211
- return await mod.run({
212
- authUser,
213
- hostname,
214
- activeSessions: shell.users.listActiveSessions(),
215
- rawInput: [name, ...args].join(" "),
216
- mode,
217
- args,
218
- stdin,
219
- cwd,
220
- shell,
221
- env,
222
- });
223
- } catch (error: unknown) {
224
- return {
225
- stderr: error instanceof Error ? error.message : "Command failed",
226
- exitCode: 1,
227
- };
228
- }
229
- }
230
-
231
- export async function runCommand(
232
- rawInput: string,
233
- authUser: string,
234
- hostname: string,
235
- mode: CommandMode,
236
- cwd: string,
237
- shell: VirtualShell,
238
- stdin?: string,
239
- env?: ShellEnv,
240
- ): Promise<CommandResult> {
241
- const trimmed = rawInput.trim();
242
- if (trimmed.length === 0) return { exitCode: 0 };
243
-
244
- const shellEnv: ShellEnv = env ?? makeDefaultEnv(authUser, hostname);
245
-
246
- // Anti-loop guard: check depth here too — catches sh -c recursive calls
247
- _callDepth++;
248
- // console.debug(`[depth=${_callDepth}] runCommand: ${trimmed.slice(0, 60)}`);
249
- if (_callDepth > MAX_CALL_DEPTH) {
250
- _callDepth--;
251
- // console.debug(`[LOOP DETECTED] runCommand blocked: ${trimmed.slice(0, 60)}`);
252
- return { stderr: `${trimmed.split(" ")[0]}: maximum call depth (${MAX_CALL_DEPTH}) exceeded`, exitCode: 126 };
253
- }
254
- try {
255
- const rawTokens = tokenizeCommand(trimmed);
256
- const rawFirstWord = rawTokens[0]?.toLowerCase() ?? "";
257
- const aliasVal = shellEnv.vars[`__alias_${rawFirstWord}`];
258
- const aliasExpanded = aliasVal
259
- ? trimmed.replace(rawFirstWord, aliasVal)
260
- : trimmed;
261
-
262
- // Detect sh-syntax constructs that must be handled by the sh interpreter
263
- const isShScript =
264
- /\bfor\s+\w+\s+in\b/.test(aliasExpanded) ||
265
- /\bwhile\s+/.test(aliasExpanded) ||
266
- /\bif\s+/.test(aliasExpanded) ||
267
- /\w+\s*\(\s*\)\s*\{/.test(aliasExpanded) ||
268
- /\bfunction\s+\w+/.test(aliasExpanded) ||
269
- /\(\(\s*.+\s*\)\)/.test(aliasExpanded);
270
-
271
- const hasOperators =
272
- /(?<![|&])[|](?![|])/.test(aliasExpanded) ||
273
- aliasExpanded.includes(">") ||
274
- aliasExpanded.includes("<") ||
275
- aliasExpanded.includes("&&") ||
276
- aliasExpanded.includes("||") ||
277
- aliasExpanded.includes(";");
278
-
279
- if ((isShScript && rawFirstWord !== "sh" && rawFirstWord !== "bash") || hasOperators) {
280
- // sh-syntax: route through sh interpreter to handle for/while/functions
281
- if (isShScript && rawFirstWord !== "sh" && rawFirstWord !== "bash") {
282
- const shMod = resolveModule("sh");
283
- if (shMod) {
284
- return await shMod.run({
285
- authUser, hostname,
286
- activeSessions: shell.users.listActiveSessions(),
287
- rawInput: aliasExpanded,
288
- mode,
289
- args: ["-c", aliasExpanded],
290
- stdin: undefined,
291
- cwd,
292
- shell,
293
- env: shellEnv,
294
- });
295
- }
296
- }
297
- const script = parseScript(aliasExpanded);
298
- if (!script.isValid)
299
- return { stderr: script.error || "Syntax error", exitCode: 1 };
300
- try {
301
- return await executeStatements(
302
- script.statements,
303
- authUser,
304
- hostname,
305
- mode,
306
- cwd,
307
- shell,
308
- shellEnv,
309
- );
310
- } catch (error: unknown) {
311
- return {
312
- stderr: error instanceof Error ? error.message : "Execution failed",
313
- exitCode: 1,
314
- };
315
- }
316
- }
317
-
318
- const expanded = await expandAsync(
319
- aliasExpanded,
320
- shellEnv.vars,
321
- shellEnv.lastExitCode,
322
- (sub) =>
323
- runCommand(
324
- sub,
325
- authUser,
326
- hostname,
327
- mode,
328
- cwd,
329
- shell,
330
- undefined,
331
- shellEnv,
332
- ).then((r) => r.stdout ?? ""),
333
- );
334
-
335
- const parts = tokenizeCommand(expanded.trim());
336
- if (parts.length === 0) return { exitCode: 0 };
337
- const assignRe = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
338
- if (assignRe.test(parts[0]!)) {
339
- return runCommandDirect(
340
- parts[0]!,
341
- parts.slice(1),
342
- authUser,
343
- hostname,
344
- mode,
345
- cwd,
346
- shell,
347
- stdin,
348
- shellEnv,
349
- );
350
- }
351
- const commandName = parts[0]?.toLowerCase() ?? "";
352
- // Apply brace expansion to each arg token
353
- const args = parts.slice(1).flatMap(expandBraces);
354
- const mod = resolveModule(commandName);
355
-
356
- if (!mod) {
357
- const vfsBinary = resolveVfsBinary(commandName, shellEnv, shell, authUser);
358
- if (vfsBinary) {
359
- const stubContent = shell.vfs.readFile(vfsBinary);
360
- const builtinMatch = stubContent.match(/exec\s+builtin\s+(\S+)/);
361
- if (builtinMatch) {
362
- const builtinName = builtinMatch[1]!;
363
- const builtinMod = resolveModule(builtinName);
364
- if (builtinMod) {
365
- return await builtinMod.run({
366
- authUser,
367
- hostname,
368
- activeSessions: shell.users.listActiveSessions(),
369
- rawInput: [commandName, ...args].join(" "),
370
- mode,
371
- args,
372
- stdin,
373
- cwd,
374
- shell,
375
- env: shellEnv,
376
- });
377
- }
378
- }
379
- const shMod = resolveModule("sh");
380
- if (shMod) {
381
- return await shMod.run({
382
- authUser,
383
- hostname,
384
- activeSessions: shell.users.listActiveSessions(),
385
- rawInput: `sh -c ${JSON.stringify(stubContent)}`,
386
- mode,
387
- args: ["-c", stubContent, "--", ...args],
388
- stdin,
389
- cwd,
390
- shell,
391
- env: shellEnv,
392
- });
393
- }
394
- }
395
-
396
- return { stderr: `${commandName}: command not found`, exitCode: 127 };
397
- }
398
-
399
- try {
400
- return await mod.run({
401
- authUser,
402
- hostname,
403
- activeSessions: shell.users.listActiveSessions(),
404
- rawInput: expanded,
405
- mode,
406
- args,
407
- stdin,
408
- cwd,
409
- shell,
410
- env: shellEnv,
411
- });
412
- } catch (error: unknown) {
413
- return {
414
- stderr: error instanceof Error ? error.message : "Command failed",
415
- exitCode: 1,
416
- };
417
- }
418
- } finally {
419
- _callDepth--;
420
- }
421
- }
@@ -1,68 +0,0 @@
1
- import type { ShellModule } from "../types/commands";
2
- import { getFlag, ifFlag } from "./command-helpers";
3
- import { resolvePath } from "./helpers";
4
-
5
- /**
6
- * Stream editor for filtering and transforming text lines.
7
- * @category text
8
- * @params ["-e <expr> [file]", "s/pattern/replace/[g]"]
9
- */
10
- export const sedCommand: ShellModule = {
11
- name: "sed",
12
- description: "Stream editor for filtering and transforming text",
13
- category: "text",
14
- params: ["-e <expr> [file]", "s/pattern/replace/[g]"],
15
- run: ({ authUser, shell, cwd, args, stdin }) => {
16
- const inPlace = ifFlag(args, ["-i"]);
17
- const expr =
18
- (getFlag(args, ["-e"]) as string | undefined) ??
19
- args.find((a) => !a.startsWith("-"));
20
- const fileArg = args.filter((a) => !a.startsWith("-") && a !== expr).pop();
21
-
22
- if (!expr) return { stderr: "sed: no expression", exitCode: 1 };
23
-
24
- let content = stdin ?? "";
25
- if (fileArg) {
26
- const p = resolvePath(cwd, fileArg);
27
- try {
28
- content = shell.vfs.readFile(p);
29
- } catch {
30
- return {
31
- stderr: `sed: ${fileArg}: No such file or directory`,
32
- exitCode: 1,
33
- };
34
- }
35
- }
36
-
37
- // Parse s/from/to/[g]
38
- const sMatch = expr.match(/^s([^a-zA-Z0-9])(.+?)\1(.*?)\1([gi]*)$/);
39
- if (!sMatch)
40
- return { stderr: `sed: unrecognized command: ${expr}`, exitCode: 1 };
41
-
42
- const [, , from, to, flags] = sMatch;
43
- const regexFlags = (flags ?? "").includes("i")
44
- ? "gi"
45
- : (flags ?? "").includes("g")
46
- ? "g"
47
- : "";
48
- let regex: RegExp;
49
- try {
50
- regex = new RegExp(from!, regexFlags || "");
51
- } catch (_e) {
52
- return { stderr: `sed: invalid regex: ${from}`, exitCode: 1 };
53
- }
54
-
55
- const result =
56
- (flags ?? "").includes("g") || regexFlags.includes("g")
57
- ? content.replace(regex, to ?? "")
58
- : content.replace(regex, to ?? "");
59
-
60
- if (inPlace && fileArg) {
61
- const p = resolvePath(cwd, fileArg);
62
- shell.writeFileAsUser(authUser, p, result);
63
- return { exitCode: 0 };
64
- }
65
-
66
- return { stdout: result, exitCode: 0 };
67
- },
68
- };
@@ -1,43 +0,0 @@
1
- import type { ShellModule } from "../types/commands";
2
-
3
- /** Generate sequences of numbers. seq LAST / seq FIRST LAST / seq FIRST INCREMENT LAST */
4
- export const seqCommand: ShellModule = {
5
- name: "seq",
6
- description: "Print a sequence of numbers",
7
- category: "text",
8
- params: ["[FIRST [INCREMENT]] LAST"],
9
- run: ({ args }) => {
10
- const nums = args.filter((a) => !a.startsWith("-") || /^-[\d.]/.test(a)).map(Number);
11
- const sep = (() => { const i = args.indexOf("-s"); return i !== -1 ? (args[i + 1] ?? "\n") : "\n"; })();
12
- const fmt = (() => { const i = args.indexOf("-f"); return i !== -1 ? (args[i + 1] ?? "%g") : null; })();
13
- const width = args.includes("-w");
14
-
15
- let first = 1, inc = 1, last: number;
16
- if (nums.length === 1) { last = nums[0]!; }
17
- else if (nums.length === 2) { first = nums[0]!; last = nums[1]!; }
18
- else { first = nums[0]!; inc = nums[1]!; last = nums[2]!; }
19
-
20
- if (inc === 0) return { stderr: "seq: zero increment\n", exitCode: 1 };
21
- if ((inc > 0 && first > last) || (inc < 0 && first < last)) return { stdout: "", exitCode: 0 };
22
-
23
- const results: string[] = [];
24
- const maxSteps = 100000;
25
- let steps = 0;
26
- for (let n = first; inc > 0 ? n <= last : n >= last; n = Math.round((n + inc) * 1e10) / 1e10) {
27
- if (++steps > maxSteps) break;
28
- let s: string;
29
- if (fmt) {
30
- s = fmt.replace("%g", String(n)).replace("%f", n.toFixed(6)).replace("%d", String(Math.trunc(n)));
31
- } else {
32
- s = Number.isInteger(n) ? String(n) : n.toPrecision(12).replace(/\.?0+$/, "");
33
- }
34
- if (width) {
35
- const maxLen = String(Math.trunc(last)).length;
36
- s = s.padStart(maxLen, "0");
37
- }
38
- results.push(s);
39
- }
40
-
41
- return { stdout: `${results.join(sep)}\n`, exitCode: 0 };
42
- },
43
- };
@@ -1,29 +0,0 @@
1
- /** biome-ignore-all lint/style/useNamingConvention: env variables */
2
- import type { ShellModule } from "../types/commands";
3
-
4
- /**
5
- * Display or set shell variables and options.
6
- * @category shell
7
- * @params ["[VAR=value]"]
8
- */
9
- export const setCommand: ShellModule = {
10
- name: "set",
11
- description: "Display or set shell variables",
12
- category: "shell",
13
- params: ["[VAR=value]"],
14
- run: ({ args, env }) => {
15
- if (args.length === 0) {
16
- const out = Object.entries(env.vars)
17
- .map(([k, v]) => `${k}=${v}`)
18
- .join("\n");
19
- return { stdout: out, exitCode: 0 };
20
- }
21
- for (const arg of args) {
22
- if (arg.includes("=")) {
23
- const eq = arg.indexOf("=");
24
- env.vars[arg.slice(0, eq)] = arg.slice(eq + 1);
25
- }
26
- }
27
- return { exitCode: 0 };
28
- },
29
- };