typescript-virtual-container 1.5.2 → 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 (364) 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/commands/basename.d.ts +13 -0
  5. package/dist/commands/basename.js +45 -0
  6. package/dist/commands/file.d.ts +8 -0
  7. package/dist/commands/file.js +57 -0
  8. package/dist/commands/fun.d.ts +32 -0
  9. package/dist/commands/fun.js +172 -0
  10. package/dist/commands/ifconfig.d.ts +7 -0
  11. package/dist/commands/ifconfig.js +52 -0
  12. package/dist/commands/last.d.ts +13 -0
  13. package/dist/commands/last.js +68 -0
  14. package/dist/commands/manuals-bundle.js +598 -6
  15. package/dist/commands/registry.js +24 -2
  16. package/dist/commands/runtime.js +159 -106
  17. package/dist/commands/sh.js +5 -0
  18. package/dist/commands/tput.d.ts +13 -0
  19. package/dist/commands/tput.js +76 -0
  20. package/dist/commands/w.d.ts +7 -0
  21. package/dist/commands/w.js +38 -0
  22. package/dist/utils/expand.d.ts +12 -0
  23. package/dist/utils/expand.js +84 -0
  24. package/package.json +9 -3
  25. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -50
  26. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -31
  27. package/.github/dependabot.yml +0 -27
  28. package/.github/pull_request_template.md +0 -21
  29. package/.github/workflows/create-pull-request.yml +0 -85
  30. package/.github/workflows/publish.yml +0 -25
  31. package/.github/workflows/test-battery.yml +0 -102
  32. package/.vscode/settings.json +0 -20
  33. package/CODE_OF_CONDUCT.md +0 -39
  34. package/CONTRIBUTING.md +0 -59
  35. package/HONEYPOT.md +0 -358
  36. package/SECURITY.md +0 -33
  37. package/benchmark-results.txt +0 -40
  38. package/benchmark-virtualshell.ts +0 -88
  39. package/biome.json +0 -37
  40. package/build.js +0 -22
  41. package/builds/fortune-nyx-v1.5.1-directbash-k6.1.0.mjs +0 -1768
  42. package/builds/fortune-nyx-v1.5.1-ssh-nosftp.js +0 -1768
  43. package/builds/fortune-nyx-v1.5.1-ssh.cjs +0 -1769
  44. package/builds/fortune-nyx-v1.5.1-web.min.js +0 -17022
  45. package/bun.lock +0 -244
  46. package/docs/.nojekyll +0 -1
  47. package/docs/app.js +0 -1755
  48. package/docs/assets/hierarchy.js +0 -1
  49. package/docs/assets/highlight.css +0 -162
  50. package/docs/assets/icons.js +0 -18
  51. package/docs/assets/icons.svg +0 -1
  52. package/docs/assets/main.js +0 -60
  53. package/docs/assets/navigation.js +0 -1
  54. package/docs/assets/search.js +0 -1
  55. package/docs/assets/style.css +0 -1633
  56. package/docs/classes/HoneyPot.html +0 -31
  57. package/docs/classes/IdleManager.html +0 -162
  58. package/docs/classes/SshClient.html +0 -66
  59. package/docs/classes/VirtualFileSystem.html +0 -279
  60. package/docs/classes/VirtualPackageManager.html +0 -63
  61. package/docs/classes/VirtualSftpServer.html +0 -169
  62. package/docs/classes/VirtualShell.html +0 -285
  63. package/docs/classes/VirtualSshServer.html +0 -182
  64. package/docs/classes/VirtualUserManager.html +0 -276
  65. package/docs/demo.html +0 -82
  66. package/docs/functions/assertDiff.html +0 -6
  67. package/docs/functions/diffSnapshots.html +0 -7
  68. package/docs/functions/formatDiff.html +0 -6
  69. package/docs/functions/getArg.html +0 -13
  70. package/docs/functions/getFlag.html +0 -15
  71. package/docs/functions/ifFlag.html +0 -11
  72. package/docs/hierarchy.html +0 -1
  73. package/docs/index.html +0 -1869
  74. package/docs/interfaces/AuditLogEntry.html +0 -6
  75. package/docs/interfaces/CommandContext.html +0 -22
  76. package/docs/interfaces/CommandResult.html +0 -26
  77. package/docs/interfaces/ExecStream.html +0 -11
  78. package/docs/interfaces/HoneyPotStats.html +0 -16
  79. package/docs/interfaces/IdleManagerOptions.html +0 -7
  80. package/docs/interfaces/InstalledPackage.html +0 -20
  81. package/docs/interfaces/NanoEditorSession.html +0 -8
  82. package/docs/interfaces/PackageDefinition.html +0 -30
  83. package/docs/interfaces/PackageFile.html +0 -8
  84. package/docs/interfaces/PasswordChallenge.html +0 -16
  85. package/docs/interfaces/RemoveOptions.html +0 -4
  86. package/docs/interfaces/ShellEnv.html +0 -6
  87. package/docs/interfaces/ShellModule.html +0 -14
  88. package/docs/interfaces/ShellProperties.html +0 -14
  89. package/docs/interfaces/ShellStream.html +0 -11
  90. package/docs/interfaces/SudoChallenge.html +0 -24
  91. package/docs/interfaces/VfsBaseNode.html +0 -12
  92. package/docs/interfaces/VfsDiff.html +0 -10
  93. package/docs/interfaces/VfsDiffEntry.html +0 -6
  94. package/docs/interfaces/VfsDiffModified.html +0 -10
  95. package/docs/interfaces/VfsDirectoryNode.html +0 -15
  96. package/docs/interfaces/VfsFileNode.html +0 -17
  97. package/docs/interfaces/VfsOptions.html +0 -26
  98. package/docs/interfaces/VfsSnapshot.html +0 -3
  99. package/docs/interfaces/VfsSnapshotBaseNode.html +0 -8
  100. package/docs/interfaces/VfsSnapshotDirectoryNode.html +0 -10
  101. package/docs/interfaces/VfsSnapshotFileNode.html +0 -12
  102. package/docs/interfaces/VirtualActiveSession.html +0 -12
  103. package/docs/interfaces/VirtualSftpServerOptions.html +0 -7
  104. package/docs/interfaces/VirtualShellVfsLike.html +0 -15
  105. package/docs/interfaces/VirtualShellVfsOptions.html +0 -3
  106. package/docs/interfaces/WriteFileOptions.html +0 -6
  107. package/docs/media/LICENSE +0 -21
  108. package/docs/modules.html +0 -1
  109. package/docs/types/ArgParseOptions.html +0 -4
  110. package/docs/types/CommandMode.html +0 -2
  111. package/docs/types/CommandOutcome.html +0 -2
  112. package/docs/types/IdleState.html +0 -1
  113. package/docs/types/VfsNodeStats.html +0 -2
  114. package/docs/types/VfsNodeType.html +0 -2
  115. package/docs/types/VfsPersistenceMode.html +0 -5
  116. package/docs/types/VfsSnapshotNode.html +0 -2
  117. package/examples/README.md +0 -288
  118. package/examples/app.js +0 -1755
  119. package/examples/app.ts +0 -299
  120. package/examples/build.js +0 -27
  121. package/examples/demo.html +0 -33
  122. package/examples/honeypot-audit.ts +0 -180
  123. package/examples/honeypot-export.ts +0 -253
  124. package/examples/honeypot-quickstart.ts +0 -110
  125. package/examples/index.html +0 -82
  126. package/examples/server.js +0 -55
  127. package/polyfills/buffer.js +0 -117
  128. package/polyfills/node_child_process/index.js +0 -2
  129. package/polyfills/node_crypto/index.js +0 -167
  130. package/polyfills/node_events/index.js +0 -9
  131. package/polyfills/node_fs/index.js +0 -202
  132. package/polyfills/node_fs/promises.js +0 -4
  133. package/polyfills/node_os/index.js +0 -9
  134. package/polyfills/node_path/index.js +0 -28
  135. package/polyfills/node_vm/index.js +0 -7
  136. package/polyfills/node_zlib/index.js +0 -3
  137. package/polyfills/process.js +0 -14
  138. package/polyfills/ssh2/index.js +0 -75
  139. package/scripts/build-all.mjs +0 -226
  140. package/scripts/build-names.mjs +0 -43
  141. package/scripts/generate-manuals-bundle.mjs +0 -49
  142. package/scripts/postinstall.js +0 -42
  143. package/scripts/publish-package.sh +0 -70
  144. package/src/Honeypot/index.ts +0 -457
  145. package/src/SSHClient/index.ts +0 -270
  146. package/src/SSHMimic/exec.ts +0 -49
  147. package/src/SSHMimic/executor.ts +0 -251
  148. package/src/SSHMimic/hostKey.ts +0 -21
  149. package/src/SSHMimic/index.ts +0 -337
  150. package/src/SSHMimic/loginBanner.ts +0 -36
  151. package/src/SSHMimic/loginFormat.ts +0 -10
  152. package/src/SSHMimic/prompt.ts +0 -14
  153. package/src/SSHMimic/sftp.ts +0 -883
  154. package/src/VirtualFileSystem/binaryPack.ts +0 -258
  155. package/src/VirtualFileSystem/index.ts +0 -1193
  156. package/src/VirtualFileSystem/internalTypes.ts +0 -43
  157. package/src/VirtualFileSystem/journal.ts +0 -171
  158. package/src/VirtualFileSystem/path.ts +0 -74
  159. package/src/VirtualPackageManager/index.ts +0 -1006
  160. package/src/VirtualShell/idleManager.ts +0 -137
  161. package/src/VirtualShell/index.ts +0 -475
  162. package/src/VirtualShell/shell.ts +0 -700
  163. package/src/VirtualShell/shellParser.ts +0 -285
  164. package/src/VirtualUserManager/index.ts +0 -758
  165. package/src/bun.d.ts +0 -1
  166. package/src/commands/adduser.ts +0 -103
  167. package/src/commands/alias.ts +0 -69
  168. package/src/commands/apt.ts +0 -233
  169. package/src/commands/awk.ts +0 -168
  170. package/src/commands/base64.ts +0 -29
  171. package/src/commands/cat.ts +0 -52
  172. package/src/commands/cd.ts +0 -25
  173. package/src/commands/chmod.ts +0 -85
  174. package/src/commands/clear.ts +0 -15
  175. package/src/commands/command-helpers.ts +0 -286
  176. package/src/commands/cp.ts +0 -83
  177. package/src/commands/curl.ts +0 -147
  178. package/src/commands/cut.ts +0 -36
  179. package/src/commands/date.ts +0 -30
  180. package/src/commands/declare.ts +0 -49
  181. package/src/commands/deluser.ts +0 -98
  182. package/src/commands/df.ts +0 -23
  183. package/src/commands/diff.ts +0 -43
  184. package/src/commands/dpkg.ts +0 -180
  185. package/src/commands/du.ts +0 -56
  186. package/src/commands/echo.ts +0 -58
  187. package/src/commands/env.ts +0 -23
  188. package/src/commands/exit.ts +0 -18
  189. package/src/commands/export.ts +0 -34
  190. package/src/commands/find.ts +0 -68
  191. package/src/commands/free.ts +0 -47
  192. package/src/commands/grep.ts +0 -116
  193. package/src/commands/groups.ts +0 -19
  194. package/src/commands/gzip.ts +0 -88
  195. package/src/commands/head.ts +0 -52
  196. package/src/commands/help.ts +0 -152
  197. package/src/commands/helpers.ts +0 -234
  198. package/src/commands/history.ts +0 -34
  199. package/src/commands/hostname.ts +0 -14
  200. package/src/commands/htop.ts +0 -20
  201. package/src/commands/id.ts +0 -19
  202. package/src/commands/index.ts +0 -9
  203. package/src/commands/kill.ts +0 -19
  204. package/src/commands/ln.ts +0 -71
  205. package/src/commands/ls.ts +0 -243
  206. package/src/commands/lsb-release.ts +0 -63
  207. package/src/commands/man.ts +0 -31
  208. package/src/commands/manuals/adduser.txt +0 -11
  209. package/src/commands/manuals/apt-cache.txt +0 -12
  210. package/src/commands/manuals/apt.txt +0 -20
  211. package/src/commands/manuals/awk.txt +0 -13
  212. package/src/commands/manuals/cat.txt +0 -14
  213. package/src/commands/manuals/cd.txt +0 -16
  214. package/src/commands/manuals/chmod.txt +0 -16
  215. package/src/commands/manuals/clear.txt +0 -10
  216. package/src/commands/manuals/cp.txt +0 -10
  217. package/src/commands/manuals/curl.txt +0 -20
  218. package/src/commands/manuals/date.txt +0 -14
  219. package/src/commands/manuals/declare.txt +0 -12
  220. package/src/commands/manuals/deluser.txt +0 -10
  221. package/src/commands/manuals/df.txt +0 -10
  222. package/src/commands/manuals/dpkg-query.txt +0 -11
  223. package/src/commands/manuals/dpkg.txt +0 -14
  224. package/src/commands/manuals/du.txt +0 -11
  225. package/src/commands/manuals/echo.txt +0 -11
  226. package/src/commands/manuals/false.txt +0 -10
  227. package/src/commands/manuals/find.txt +0 -11
  228. package/src/commands/manuals/free.txt +0 -12
  229. package/src/commands/manuals/grep.txt +0 -13
  230. package/src/commands/manuals/groups.txt +0 -10
  231. package/src/commands/manuals/gzip.txt +0 -11
  232. package/src/commands/manuals/head.txt +0 -10
  233. package/src/commands/manuals/help.txt +0 -11
  234. package/src/commands/manuals/history.txt +0 -11
  235. package/src/commands/manuals/hostname.txt +0 -10
  236. package/src/commands/manuals/id.txt +0 -10
  237. package/src/commands/manuals/kill.txt +0 -13
  238. package/src/commands/manuals/ls.txt +0 -20
  239. package/src/commands/manuals/lsb_release.txt +0 -14
  240. package/src/commands/manuals/mkdir.txt +0 -10
  241. package/src/commands/manuals/mv.txt +0 -10
  242. package/src/commands/manuals/nano.txt +0 -11
  243. package/src/commands/manuals/neofetch.txt +0 -10
  244. package/src/commands/manuals/node.txt +0 -13
  245. package/src/commands/manuals/npm.txt +0 -13
  246. package/src/commands/manuals/npx.txt +0 -13
  247. package/src/commands/manuals/passwd.txt +0 -11
  248. package/src/commands/manuals/ping.txt +0 -10
  249. package/src/commands/manuals/printf.txt +0 -11
  250. package/src/commands/manuals/ps.txt +0 -10
  251. package/src/commands/manuals/pwd.txt +0 -10
  252. package/src/commands/manuals/python3.txt +0 -13
  253. package/src/commands/manuals/readlink.txt +0 -10
  254. package/src/commands/manuals/return.txt +0 -10
  255. package/src/commands/manuals/rm.txt +0 -10
  256. package/src/commands/manuals/sed.txt +0 -11
  257. package/src/commands/manuals/set.txt +0 -11
  258. package/src/commands/manuals/shift.txt +0 -10
  259. package/src/commands/manuals/sleep.txt +0 -10
  260. package/src/commands/manuals/sort.txt +0 -12
  261. package/src/commands/manuals/source.txt +0 -11
  262. package/src/commands/manuals/ssh.txt +0 -11
  263. package/src/commands/manuals/stat.txt +0 -10
  264. package/src/commands/manuals/su.txt +0 -13
  265. package/src/commands/manuals/sudo.txt +0 -11
  266. package/src/commands/manuals/tail.txt +0 -10
  267. package/src/commands/manuals/tar.txt +0 -19
  268. package/src/commands/manuals/tee.txt +0 -10
  269. package/src/commands/manuals/test.txt +0 -11
  270. package/src/commands/manuals/touch.txt +0 -11
  271. package/src/commands/manuals/tr.txt +0 -10
  272. package/src/commands/manuals/trap.txt +0 -10
  273. package/src/commands/manuals/true.txt +0 -10
  274. package/src/commands/manuals/type.txt +0 -10
  275. package/src/commands/manuals/uname.txt +0 -12
  276. package/src/commands/manuals/uniq.txt +0 -12
  277. package/src/commands/manuals/unset.txt +0 -10
  278. package/src/commands/manuals/uptime.txt +0 -11
  279. package/src/commands/manuals/wc.txt +0 -12
  280. package/src/commands/manuals/wget.txt +0 -12
  281. package/src/commands/manuals/which.txt +0 -10
  282. package/src/commands/manuals/whoami.txt +0 -10
  283. package/src/commands/manuals/xargs.txt +0 -10
  284. package/src/commands/manuals-bundle.ts +0 -898
  285. package/src/commands/mkdir.ts +0 -31
  286. package/src/commands/mv.ts +0 -50
  287. package/src/commands/nano.ts +0 -38
  288. package/src/commands/neofetch.ts +0 -53
  289. package/src/commands/node.ts +0 -341
  290. package/src/commands/npm.ts +0 -132
  291. package/src/commands/passwd.ts +0 -50
  292. package/src/commands/ping.ts +0 -32
  293. package/src/commands/printf.ts +0 -129
  294. package/src/commands/ps.ts +0 -58
  295. package/src/commands/pwd.ts +0 -9
  296. package/src/commands/python.ts +0 -2229
  297. package/src/commands/read.ts +0 -46
  298. package/src/commands/registry.ts +0 -249
  299. package/src/commands/rm.ts +0 -42
  300. package/src/commands/runtime.ts +0 -378
  301. package/src/commands/sed.ts +0 -68
  302. package/src/commands/seq.ts +0 -43
  303. package/src/commands/set.ts +0 -29
  304. package/src/commands/sh.ts +0 -467
  305. package/src/commands/shift.ts +0 -63
  306. package/src/commands/sleep.ts +0 -20
  307. package/src/commands/sort.ts +0 -46
  308. package/src/commands/source.ts +0 -52
  309. package/src/commands/stat.ts +0 -61
  310. package/src/commands/su.ts +0 -72
  311. package/src/commands/sudo.ts +0 -76
  312. package/src/commands/tail.ts +0 -53
  313. package/src/commands/tar.ts +0 -102
  314. package/src/commands/tee.ts +0 -36
  315. package/src/commands/test.ts +0 -137
  316. package/src/commands/touch.ts +0 -28
  317. package/src/commands/tr.ts +0 -70
  318. package/src/commands/tree.ts +0 -20
  319. package/src/commands/true.ts +0 -27
  320. package/src/commands/type.ts +0 -48
  321. package/src/commands/uname.ts +0 -29
  322. package/src/commands/uniq.ts +0 -39
  323. package/src/commands/unset.ts +0 -17
  324. package/src/commands/uptime.ts +0 -54
  325. package/src/commands/wc.ts +0 -55
  326. package/src/commands/wget.ts +0 -148
  327. package/src/commands/which.ts +0 -37
  328. package/src/commands/who.ts +0 -25
  329. package/src/commands/whoami.ts +0 -14
  330. package/src/commands/xargs.ts +0 -31
  331. package/src/index.ts +0 -67
  332. package/src/modules/linuxRootfs.ts +0 -1961
  333. package/src/modules/neofetch.ts +0 -358
  334. package/src/modules/shellInteractive.ts +0 -57
  335. package/src/modules/shellRuntime.ts +0 -76
  336. package/src/self-standalone.ts +0 -542
  337. package/src/standalone-wo-sftp.ts +0 -38
  338. package/src/standalone.ts +0 -72
  339. package/src/types/commands.ts +0 -146
  340. package/src/types/pipeline.ts +0 -52
  341. package/src/types/streams.ts +0 -32
  342. package/src/types/tar-stream.d.ts +0 -38
  343. package/src/types/vfs.ts +0 -98
  344. package/src/utils/expand.ts +0 -491
  345. package/src/utils/perfLogger.ts +0 -72
  346. package/src/utils/tokenize.ts +0 -98
  347. package/src/utils/vfsDiff.ts +0 -275
  348. package/tests/command-helpers.test.ts +0 -116
  349. package/tests/commands-admin-net.test.ts +0 -441
  350. package/tests/commands-advanced.test.ts +0 -456
  351. package/tests/commands-core.test.ts +0 -562
  352. package/tests/commands-missing.test.ts +0 -570
  353. package/tests/commands-specific-units.test.ts +0 -327
  354. package/tests/commands-text-sys.test.ts +0 -445
  355. package/tests/expand.test.ts +0 -170
  356. package/tests/helpers.test.ts +0 -97
  357. package/tests/new-features.test.ts +0 -1036
  358. package/tests/parser-executor.test.ts +0 -37
  359. package/tests/sftp.test.ts +0 -323
  360. package/tests/ssh-exec.test.ts +0 -45
  361. package/tests/test-helper.ts +0 -79
  362. package/tests/users.test.ts +0 -86
  363. package/tsconfig.json +0 -49
  364. package/typedoc.json +0 -47
@@ -1,570 +0,0 @@
1
- /** biome-ignore-all lint/style/useNamingConvention: ENV */
2
- /** biome-ignore-all lint/suspicious/noTemplateCurlyInString: expand */
3
- import { beforeAll, describe, expect, test } from "bun:test";
4
- import type { SshClient, VirtualShell } from "../src";
5
- import { createTestEnv, createTestFile, pathExists, runCmd } from "./test-helper";
6
-
7
- let shell: VirtualShell;
8
- let client: InstanceType<typeof SshClient>;
9
-
10
- beforeAll(async () => {
11
- const env = await createTestEnv("test-missing");
12
- shell = env.shell;
13
- client = env.client;
14
- });
15
-
16
- // ─── CD command tests ──────────────────────────────────────────────────────
17
-
18
- describe("cd command", () => {
19
- test("cd changes directory", async () => {
20
- const r = await runCmd(client, "cd /tmp && pwd");
21
- expect(r.exitCode).toBe(0);
22
- expect(r.stdout?.trim()).toBe("/tmp");
23
- });
24
-
25
- test("cd to home directory", async () => {
26
- const r = await runCmd(client, "cd ~ && pwd");
27
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
28
- expect(r.stdout?.trim().length).toBeGreaterThan(0);
29
- });
30
-
31
- test("cd to parent directory", async () => {
32
- const r = await runCmd(client, "cd /tmp && cd .. && pwd");
33
- expect(r.exitCode).toBe(0);
34
- expect(r.stdout?.trim()).toBe("/");
35
- });
36
-
37
- test("cd with no args goes to home", async () => {
38
- const r = await runCmd(client, "cd && pwd");
39
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
40
- expect(r.stdout?.trim().length).toBeGreaterThan(0);
41
- });
42
-
43
- test("cd to non-existent directory fails", async () => {
44
- const r = await runCmd(client, "cd /nonexistent 2>&1 || echo 'error'");
45
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
46
- });
47
-
48
- test("cd with absolute path", async () => {
49
- const r = await runCmd(client, "cd /var && pwd");
50
- expect(r.exitCode).toBe(0);
51
- expect(r.stdout?.trim()).toBe("/var");
52
- });
53
-
54
- test("cd with relative path", async () => {
55
- await runCmd(client, "mkdir -p /tmp/cdtest/subdir");
56
- const r = await runCmd(client, "cd /tmp/cdtest && cd subdir && pwd");
57
- expect(r.exitCode).toBe(0);
58
- expect(r.stdout?.trim()).toContain("subdir");
59
- });
60
- });
61
-
62
- // ─── SOURCE/DOT command tests ──────────────────────────────────────────────
63
-
64
- describe("source/dot command", () => {
65
- test("source executes script", async () => {
66
- createTestFile(shell, "/tmp/sourceme.sh", "echo sourced");
67
- const r = await runCmd(client, "source /tmp/sourceme.sh");
68
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
69
- });
70
-
71
- test("source with variable assignment", async () => {
72
- createTestFile(shell, "/tmp/srcvar.sh", "VAR=sourced_value");
73
- const r = await runCmd(client, "source /tmp/srcvar.sh && echo $VAR || echo 'fail'");
74
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
75
- });
76
-
77
- test("dot command (alias for source)", async () => {
78
- createTestFile(shell, "/tmp/dotme.sh", "echo 'dot works'");
79
- const r = await runCmd(client, ". /tmp/dotme.sh");
80
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
81
- });
82
-
83
- test("source non-existent file fails", async () => {
84
- const r = await runCmd(client, "source /nonexistent.sh 2>&1 || echo 'error'");
85
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
86
- });
87
-
88
- test("source with functions", async () => {
89
- createTestFile(shell, "/tmp/func.sh", "myfunc() { echo func_result; }");
90
- const r = await runCmd(client, "source /tmp/func.sh && myfunc || echo 'no func'");
91
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
92
- });
93
- });
94
-
95
- // ─── WHO command tests ────────────────────────────────────────────────────
96
-
97
- describe("who command", () => {
98
- test("who lists logged in users", async () => {
99
- const r = await runCmd(client, "who");
100
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
101
- expect(r.stdout?.length).toBeGreaterThanOrEqual(0);
102
- });
103
-
104
- test("who -b boot time", async () => {
105
- const r = await runCmd(client, "who -b || echo 'boot'");
106
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
107
- });
108
-
109
- test("who -r runlevel", async () => {
110
- const r = await runCmd(client, "who -r || echo 'runlevel'");
111
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
112
- });
113
-
114
- test("who -a all information", async () => {
115
- const r = await runCmd(client, "who -a || echo 'all'");
116
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
117
- });
118
- });
119
-
120
- // ─── EXIT command tests ───────────────────────────────────────────────────
121
-
122
- describe("exit command", () => {
123
- test("exit with code 0", async () => {
124
- const r = await runCmd(client, "exit 0");
125
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
126
- });
127
-
128
- test("exit with code 1", async () => {
129
- const r = await runCmd(client, "exit 1");
130
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
131
- });
132
-
133
- test("exit in subshell", async () => {
134
- const r = await runCmd(client, "sh -c 'exit 42' || echo 'exited'");
135
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
136
- });
137
- });
138
-
139
- // ─── PIPES and REDIRECTION tests ───────────────────────────────────────────
140
-
141
- describe("pipes and redirections", () => {
142
- test("simple pipe", async () => {
143
- const r = await runCmd(client, "echo 'hello world' | wc -w");
144
- expect(r.exitCode).toBe(0);
145
- expect(r.stdout?.trim()).toBe("2");
146
- });
147
-
148
- test("multiple pipes", async () => {
149
- const r = await runCmd(client, "echo -e 'c\\na\\nb' | sort | tr a-z A-Z");
150
- expect(r.exitCode).toBe(0);
151
- expect(r.stdout).toContain("A");
152
- expect(r.stdout).toContain("B");
153
- expect(r.stdout).toContain("C");
154
- });
155
-
156
- test("pipe to file (>)", async () => {
157
- const r = await runCmd(client, "echo 'content' > /tmp/pipe-test.txt");
158
- expect(r.exitCode).toBe(0);
159
- expect(shell.vfs.exists("/tmp/pipe-test.txt")).toBe(true);
160
- expect(shell.vfs.readFile("/tmp/pipe-test.txt")).toContain("content");
161
- });
162
-
163
- test("append to file (>>)", async () => {
164
- createTestFile(shell, "/tmp/append-test.txt", "line1\n");
165
- const r = await runCmd(client, "echo 'line2' >> /tmp/append-test.txt");
166
- expect(r.exitCode).toBe(0);
167
- const content = shell.vfs.readFile("/tmp/append-test.txt");
168
- expect(content).toContain("line1");
169
- expect(content).toContain("line2");
170
- });
171
-
172
- test("redirect stderr (2>)", async () => {
173
- const r = await runCmd(client, "ls /nonexistent 2> /tmp/err.txt 2>&1 || echo done");
174
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
175
- });
176
-
177
- test("redirect both stdout and stderr (&>)", async () => {
178
- const r = await runCmd(client, "echo 'test' &> /tmp/both.txt");
179
- expect(r.exitCode).toBe(0);
180
- expect(pathExists(shell, "/tmp/both.txt")).toBe(true);
181
- });
182
-
183
- test("stdin redirection (<)", async () => {
184
- createTestFile(shell, "/tmp/stdin.txt", "hello world");
185
- const r = await runCmd(client, "cat < /tmp/stdin.txt");
186
- expect(r.exitCode).toBe(0);
187
- expect(r.stdout?.trim()).toBe("hello world");
188
- });
189
-
190
- test("here document (<<)", async () => {
191
- const r = await runCmd(client, "cat << EOF\nhello\nworld\nEOF");
192
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
193
- });
194
- });
195
-
196
- // ─── COMMAND CHAINING tests ───────────────────────────────────────────────
197
-
198
- describe("command chaining and sequencing", () => {
199
- test("AND operator (&&) success", async () => {
200
- const r = await runCmd(client, "echo 'a' && echo 'b'");
201
- expect(r.exitCode).toBe(0);
202
- expect(r.stdout).toContain("a");
203
- expect(r.stdout).toContain("b");
204
- });
205
-
206
- test("AND operator (&&) with failure", async () => {
207
- const r = await runCmd(client, "false && echo 'should not print' || echo 'failed'");
208
- expect(r.exitCode).toBe(0);
209
- expect(r.stdout?.trim()).toBe("failed");
210
- });
211
-
212
- test("OR operator (||) success", async () => {
213
- const r = await runCmd(client, "true || echo 'should not print'");
214
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
215
- expect(r.stdout).toBeUndefined();
216
- });
217
-
218
- test("OR operator (||) with failure", async () => {
219
- const r = await runCmd(client, "false || echo 'printed'");
220
- expect(r.exitCode).toBe(0);
221
- expect(r.stdout?.trim()).toBe("printed");
222
- });
223
-
224
- test("semicolon chaining", async () => {
225
- const r = await runCmd(client, "echo 'a'; echo 'b'; echo 'c'");
226
- expect(r.exitCode).toBe(0);
227
- expect(r.stdout).toContain("a");
228
- expect(r.stdout).toContain("b");
229
- expect(r.stdout).toContain("c");
230
- });
231
-
232
- test("background execution (&)", async () => {
233
- const r = await runCmd(client, "sleep 0.01 & echo 'foreground' || echo 'done'");
234
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
235
- expect(r.stdout).toBeUndefined();
236
- });
237
- });
238
-
239
- // ─── GLOB PATTERNS tests ───────────────────────────────────────────────────
240
-
241
- describe("glob patterns and wildcards", () => {
242
- test("asterisk wildcard (*)", async () => {
243
- createTestFile(shell, "/tmp/glob1.txt", "a");
244
- createTestFile(shell, "/tmp/glob2.txt", "b");
245
- createTestFile(shell, "/tmp/glob3.log", "c");
246
- const r = await runCmd(client, "ls /tmp/glob*.txt 2>&1 || echo 'pattern'");
247
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
248
- });
249
-
250
- test("question mark wildcard (?)", async () => {
251
- createTestFile(shell, "/tmp/file1.txt", "a");
252
- createTestFile(shell, "/tmp/file2.txt", "b");
253
- createTestFile(shell, "/tmp/file10.txt", "c");
254
- const r = await runCmd(client, "ls /tmp/file?.txt 2>&1 || echo 'pattern'");
255
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
256
- });
257
-
258
- test("bracket pattern [abc]", async () => {
259
- createTestFile(shell, "/tmp/a1.txt", "a");
260
- createTestFile(shell, "/tmp/b1.txt", "b");
261
- createTestFile(shell, "/tmp/c1.txt", "c");
262
- createTestFile(shell, "/tmp/d1.txt", "d");
263
- const r = await runCmd(client, "ls /tmp/[abc]1.txt 2>&1 || echo 'pattern'");
264
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
265
- });
266
-
267
- test("range pattern [a-z]", async () => {
268
- createTestFile(shell, "/tmp/a.txt", "");
269
- createTestFile(shell, "/tmp/m.txt", "");
270
- createTestFile(shell, "/tmp/z.txt", "");
271
- const r = await runCmd(client, "ls /tmp/[a-z].txt 2>&1 || echo 'pattern'");
272
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
273
- });
274
-
275
- test("negated pattern [!abc]", async () => {
276
- createTestFile(shell, "/tmp/x.txt", "");
277
- createTestFile(shell, "/tmp/y.txt", "");
278
- const r = await runCmd(client, "ls /tmp/[!abc].txt 2>&1 || echo 'found'");
279
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
280
- });
281
- });
282
-
283
- // ─── COMMAND SUBSTITUTION tests ────────────────────────────────────────────
284
-
285
- describe("command substitution", () => {
286
- test("$() command substitution", async () => {
287
- const r = await runCmd(client, "echo $(echo hello)");
288
- expect(r.exitCode).toBe(0);
289
- expect(r.stdout?.trim()).toBe("hello");
290
- });
291
-
292
- test("backtick command substitution", async () => {
293
- const r = await runCmd(client, "echo $(echo world)");
294
- expect(r.exitCode).toBe(0);
295
- expect(r.stdout?.trim()).toBe("world");
296
- });
297
-
298
- test("nested command substitution", async () => {
299
- const r = await runCmd(client, "echo $(echo $(echo nested))");
300
- expect(r.exitCode).toBe(0);
301
- expect(r.stdout?.trim()).toContain("nested");
302
- });
303
-
304
- test("command substitution in variable", async () => {
305
- const r = await runCmd(client, "VAR=$(echo value) && echo $VAR || echo 'subst'");
306
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
307
- });
308
-
309
- test("command substitution with pipe", async () => {
310
- const r = await runCmd(client, "echo $(echo hello | tr a-z A-Z) || echo 'subst'");
311
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
312
- });
313
- });
314
-
315
- // ─── CONDITIONALS tests ───────────────────────────────────────────────────
316
-
317
- describe("conditionals (if/then/else)", () => {
318
- test("if with true condition", async () => {
319
- const r = await runCmd(client, "if true; then echo yes; fi 2>&1 || echo 'if'");
320
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
321
- });
322
-
323
- test("if with false condition", async () => {
324
- const r = await runCmd(client, "if false; then echo yes; else echo no; fi 2>&1 || echo 'if'");
325
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
326
- });
327
-
328
- test("if with test -f", async () => {
329
- createTestFile(shell, "/tmp/iftest.txt", "content");
330
- const r = await runCmd(client, "if test -f /tmp/iftest.txt; then echo exists; fi 2>&1 || echo 'if'");
331
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
332
- });
333
-
334
- test("if/elif/else chain", async () => {
335
- const r = await runCmd(client, "if [ 1 -eq 2 ]; then echo a; elif [ 2 -eq 2 ]; then echo b; else echo c; fi");
336
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
337
- });
338
- });
339
-
340
- // ─── LOOPS tests ───────────────────────────────────────────────────────────
341
-
342
- describe("loops (for/while)", () => {
343
- test("for loop with range", async () => {
344
- const r = await runCmd(client, "for i in 1 2 3; do echo $i; done");
345
- expect(r.exitCode).toBe(0);
346
- expect(r.stdout).toContain("1");
347
- expect(r.stdout).toContain("2");
348
- expect(r.stdout).toContain("3");
349
- });
350
-
351
- test("for loop with seq", async () => {
352
- const r = await runCmd(client, "for i in $(seq 1 3); do echo $i; done");
353
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
354
- });
355
-
356
- test("for loop with glob", async () => {
357
- createTestFile(shell, "/tmp/loop1.txt", "");
358
- createTestFile(shell, "/tmp/loop2.txt", "");
359
- const r = await runCmd(client, "for f in /tmp/loop*.txt; do echo $f; done 2>&1 || echo 'for'");
360
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
361
- });
362
- });
363
-
364
- // ─── SPECIAL VARIABLES tests ───────────────────────────────────────────────
365
-
366
- describe("special variables", () => {
367
- test("$# number of parameters", async () => {
368
- const r = await runCmd(client, "set a b c && echo $#");
369
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
370
- });
371
-
372
- test("$? exit status", async () => {
373
- const r = await runCmd(client, "true && echo $?");
374
- expect(r.exitCode).toBe(0);
375
- expect(r.stdout?.trim()).toBe("0");
376
- });
377
-
378
- test("$$ process ID", async () => {
379
- const r = await runCmd(client, "echo $$ || echo 'pid'");
380
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
381
- });
382
-
383
- test("$0 script name", async () => {
384
- const r = await runCmd(client, "sh -c 'echo $0'");
385
- expect(r.exitCode).toBe(0);
386
- expect(r.stdout?.length).toBeGreaterThan(0);
387
- });
388
-
389
- test("$1, $2, ... positional args", async () => {
390
- const r = await runCmd(client, "sh -c 'echo $1 $2' script arg1 arg2 || echo 'args'");
391
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
392
- });
393
-
394
- test("$@ all positional args", async () => {
395
- const r = await runCmd(client, "sh -c 'for a in \"$@\"; do echo $a; done' script a b c");
396
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
397
- });
398
- });
399
-
400
- // ─── QUOTING and ESCAPING tests ────────────────────────────────────────────
401
-
402
- describe("quoting and escaping", () => {
403
- test("double quotes with variables", async () => {
404
- const r = await runCmd(client, "V=hello && echo \"$V world\" || echo 'quote'");
405
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
406
- });
407
-
408
- test("single quotes (no substitution)", async () => {
409
- const r = await runCmd(client, "V=hello && echo '$V world' || echo 'quote'");
410
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
411
- });
412
-
413
- test("backslash escaping", async () => {
414
- const r = await runCmd(client, "echo 'a\\tb\\nc'");
415
- expect(r.exitCode).toBe(0);
416
- expect(r.stdout).toContain("a");
417
- expect(r.stdout).toContain("b");
418
- expect(r.stdout).toContain("c");
419
- });
420
-
421
- test("escaped special chars", async () => {
422
- const r = await runCmd(client, "echo '\\$\\*\\?'");
423
- expect(r.exitCode).toBe(0);
424
- expect(r.stdout).toContain("$");
425
- expect(r.stdout).toContain("*");
426
- });
427
- });
428
-
429
- // ─── ARITHMETIC tests ────────────────────────────────────────────────────
430
-
431
- describe("arithmetic expansion", () => {
432
- test("basic arithmetic $((expr))", async () => {
433
- const r = await runCmd(client, "echo $((2+3))");
434
- expect(r.exitCode).toBe(0);
435
- expect(r.stdout?.trim()).toBe("5");
436
- });
437
-
438
- test("subtraction", async () => {
439
- const r = await runCmd(client, "echo $((10-3))");
440
- expect(r.exitCode).toBe(0);
441
- expect(r.stdout?.trim()).toBe("7");
442
- });
443
-
444
- test("multiplication", async () => {
445
- const r = await runCmd(client, "echo $((4*5))");
446
- expect(r.exitCode).toBe(0);
447
- expect(r.stdout?.trim()).toBe("20");
448
- });
449
-
450
- test("division", async () => {
451
- const r = await runCmd(client, "echo $((20/4))");
452
- expect(r.exitCode).toBe(0);
453
- expect(r.stdout?.trim()).toBe("5");
454
- });
455
-
456
- test("modulo", async () => {
457
- const r = await runCmd(client, "echo $((10%3))");
458
- expect(r.exitCode).toBe(0);
459
- expect(r.stdout?.trim()).toBe("1");
460
- });
461
-
462
- test("arithmetic with variables", async () => {
463
- const r = await runCmd(client, "A=10; B=5; echo $((A+B)) || echo 'arith'");
464
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
465
- });
466
-
467
- test("complex arithmetic", async () => {
468
- const r = await runCmd(client, "echo $(((2+3)*4))");
469
- expect(r.exitCode).toBe(0);
470
- expect(r.stdout?.trim()).toBe("20");
471
- });
472
- });
473
-
474
- // ─── PARAMETER EXPANSION tests ─────────────────────────────────────────────
475
-
476
- describe("parameter expansion", () => {
477
- test("${VAR} basic expansion", async () => {
478
- const r = await runCmd(client, "VAR=hello && echo ${VAR}");
479
- expect(r.exitCode).toBe(0);
480
- expect(r.stdout?.trim()).toBe("hello");
481
- });
482
-
483
- test("${VAR:-default} with default", async () => {
484
- const r = await runCmd(client, "echo ${UNDEFINED:-default}");
485
- expect(r.exitCode).toBe(0);
486
- expect(r.stdout?.trim()).toBe("default");
487
- });
488
-
489
- test("${VAR:=default} assign default", async () => {
490
- const r = await runCmd(client, "echo ${NEWVAR:=value} && echo $NEWVAR");
491
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
492
- });
493
-
494
- test("${VAR#pattern} remove prefix", async () => {
495
- const r = await runCmd(client, "VAR=hello world && echo ${VAR#hello }");
496
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
497
- });
498
-
499
- test("${#VAR} string length", async () => {
500
- const r = await runCmd(client, "VAR=hello && echo ${#VAR}");
501
- expect(r.exitCode).toBe(0);
502
- expect(r.stdout?.trim()).toBe("5");
503
- });
504
-
505
- test("${VAR:0:3} substring", async () => {
506
- const r = await runCmd(client, "VAR=hello && echo ${VAR:0:3}");
507
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
508
- });
509
- });
510
-
511
- // ─── PROCESS SUBSTITUTION tests ────────────────────────────────────────────
512
-
513
- describe("process and I/O operations", () => {
514
- test("multiple input/output redirection", async () => {
515
- createTestFile(shell, "/tmp/input.txt", "line1\nline2\nline3");
516
- const r = await runCmd(client, "cat /tmp/input.txt | grep 'line' | sort | uniq");
517
- expect(r.exitCode).toBe(0);
518
- expect(r.stdout).toContain("line");
519
- });
520
-
521
- test("tee to multiple files via pipe", async () => {
522
- const r = await runCmd(client, "echo 'test' | tee /tmp/t1.txt /tmp/t2.txt | cat");
523
- expect(r.exitCode).toBe(0);
524
- expect(shell.vfs.exists("/tmp/t1.txt")).toBe(true);
525
- expect(shell.vfs.exists("/tmp/t2.txt")).toBe(true);
526
- });
527
-
528
- test("wc with multiple file inputs", async () => {
529
- createTestFile(shell, "/tmp/wc1.txt", "a\nb\nc");
530
- createTestFile(shell, "/tmp/wc2.txt", "d\ne");
531
- const r = await runCmd(client, "wc -l /tmp/wc1.txt /tmp/wc2.txt");
532
- expect(r.exitCode).toBe(0);
533
- expect(r.stdout).toContain("3");
534
- expect(r.stdout).toContain("2");
535
- });
536
- });
537
-
538
- // ─── ERROR HANDLING tests ──────────────────────────────────────────────────
539
-
540
- describe("error handling and edge cases", () => {
541
- test("missing command fails", async () => {
542
- const r = await runCmd(client, "nonexistent_cmd 2>&1 || echo error_caught");
543
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
544
- expect(r.stdout?.trim()).toBe("error_caught");
545
- });
546
-
547
- test("division by zero in arithmetic", async () => {
548
- const r = await runCmd(client, "echo $((1/0)) 2>&1 || echo 'math error'");
549
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
550
- });
551
-
552
- test("undefined variable in arithmetic", async () => {
553
- const r = await runCmd(client, "echo $((UNDEFINED_VAR + 5))");
554
- expect(r.exitCode).toBe(0);
555
- expect(r.stdout?.trim()).toBe("5");
556
- });
557
-
558
- test("empty file handling", async () => {
559
- createTestFile(shell, "/tmp/empty.txt", "");
560
- const r = await runCmd(client, "cat /tmp/empty.txt | wc -l");
561
- expect(r.exitCode).toBe(0);
562
- expect(r.stdout?.trim()).toBe("0");
563
- });
564
-
565
- test("binary file handling", async () => {
566
- shell.vfs.writeFile("/tmp/binary.bin", Buffer.from([0x00, 0x01, 0x02]));
567
- const r = await runCmd(client, "file /tmp/binary.bin 2>&1 || echo 'file'");
568
- expect(r.exitCode).toBeGreaterThanOrEqual(0);
569
- });
570
- });