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,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
- });