typescript-virtual-container 1.5.3 → 1.5.5

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 (371) hide show
  1. package/README.md +44 -532
  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/VirtualShell/shell.js +158 -11
  6. package/dist/commands/basename.d.ts +13 -0
  7. package/dist/commands/basename.js +45 -0
  8. package/dist/commands/bc.d.ts +2 -0
  9. package/dist/commands/bc.js +28 -0
  10. package/dist/commands/file.d.ts +8 -0
  11. package/dist/commands/file.js +57 -0
  12. package/dist/commands/fun.d.ts +32 -0
  13. package/dist/commands/fun.js +172 -0
  14. package/dist/commands/ip.d.ts +7 -0
  15. package/dist/commands/ip.js +52 -0
  16. package/dist/commands/jobs.d.ts +4 -0
  17. package/dist/commands/jobs.js +27 -0
  18. package/dist/commands/last.d.ts +13 -0
  19. package/dist/commands/last.js +68 -0
  20. package/dist/commands/manuals-bundle.js +598 -6
  21. package/dist/commands/registry.js +30 -2
  22. package/dist/commands/runtime.js +24 -3
  23. package/dist/commands/set.js +20 -0
  24. package/dist/commands/sh.js +74 -1
  25. package/dist/commands/tput.d.ts +13 -0
  26. package/dist/commands/tput.js +76 -0
  27. package/dist/commands/w.d.ts +7 -0
  28. package/dist/commands/w.js +38 -0
  29. package/dist/utils/expand.d.ts +12 -0
  30. package/dist/utils/expand.js +87 -1
  31. package/package.json +9 -3
  32. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -50
  33. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -31
  34. package/.github/dependabot.yml +0 -27
  35. package/.github/pull_request_template.md +0 -21
  36. package/.github/workflows/create-pull-request.yml +0 -85
  37. package/.github/workflows/publish.yml +0 -25
  38. package/.github/workflows/test-battery.yml +0 -102
  39. package/.vscode/settings.json +0 -20
  40. package/CODE_OF_CONDUCT.md +0 -39
  41. package/CONTRIBUTING.md +0 -59
  42. package/HONEYPOT.md +0 -358
  43. package/SECURITY.md +0 -33
  44. package/benchmark-results.txt +0 -40
  45. package/benchmark-virtualshell.ts +0 -88
  46. package/biome.json +0 -37
  47. package/build.js +0 -22
  48. package/builds/fortune-nyx-v1.5.3-directbash-k6.1.0.mjs +0 -1764
  49. package/builds/fortune-nyx-v1.5.3-ssh-nosftp.js +0 -1764
  50. package/builds/fortune-nyx-v1.5.3-ssh.cjs +0 -1765
  51. package/builds/fortune-nyx-v1.5.3-web.min.js +0 -17036
  52. package/bun.lock +0 -244
  53. package/docs/.nojekyll +0 -1
  54. package/docs/app.js +0 -1751
  55. package/docs/assets/hierarchy.js +0 -1
  56. package/docs/assets/highlight.css +0 -162
  57. package/docs/assets/icons.js +0 -18
  58. package/docs/assets/icons.svg +0 -1
  59. package/docs/assets/main.js +0 -60
  60. package/docs/assets/navigation.js +0 -1
  61. package/docs/assets/search.js +0 -1
  62. package/docs/assets/style.css +0 -1633
  63. package/docs/classes/HoneyPot.html +0 -31
  64. package/docs/classes/IdleManager.html +0 -162
  65. package/docs/classes/SshClient.html +0 -66
  66. package/docs/classes/VirtualFileSystem.html +0 -279
  67. package/docs/classes/VirtualPackageManager.html +0 -63
  68. package/docs/classes/VirtualSftpServer.html +0 -169
  69. package/docs/classes/VirtualShell.html +0 -285
  70. package/docs/classes/VirtualSshServer.html +0 -182
  71. package/docs/classes/VirtualUserManager.html +0 -276
  72. package/docs/demo.html +0 -82
  73. package/docs/functions/assertDiff.html +0 -6
  74. package/docs/functions/diffSnapshots.html +0 -7
  75. package/docs/functions/formatDiff.html +0 -6
  76. package/docs/functions/getArg.html +0 -13
  77. package/docs/functions/getFlag.html +0 -15
  78. package/docs/functions/ifFlag.html +0 -11
  79. package/docs/hierarchy.html +0 -1
  80. package/docs/index.html +0 -1869
  81. package/docs/interfaces/AuditLogEntry.html +0 -6
  82. package/docs/interfaces/CommandContext.html +0 -22
  83. package/docs/interfaces/CommandResult.html +0 -26
  84. package/docs/interfaces/ExecStream.html +0 -11
  85. package/docs/interfaces/HoneyPotStats.html +0 -16
  86. package/docs/interfaces/IdleManagerOptions.html +0 -7
  87. package/docs/interfaces/InstalledPackage.html +0 -20
  88. package/docs/interfaces/NanoEditorSession.html +0 -8
  89. package/docs/interfaces/PackageDefinition.html +0 -30
  90. package/docs/interfaces/PackageFile.html +0 -8
  91. package/docs/interfaces/PasswordChallenge.html +0 -16
  92. package/docs/interfaces/RemoveOptions.html +0 -4
  93. package/docs/interfaces/ShellEnv.html +0 -6
  94. package/docs/interfaces/ShellModule.html +0 -14
  95. package/docs/interfaces/ShellProperties.html +0 -14
  96. package/docs/interfaces/ShellStream.html +0 -11
  97. package/docs/interfaces/SudoChallenge.html +0 -24
  98. package/docs/interfaces/VfsBaseNode.html +0 -12
  99. package/docs/interfaces/VfsDiff.html +0 -10
  100. package/docs/interfaces/VfsDiffEntry.html +0 -6
  101. package/docs/interfaces/VfsDiffModified.html +0 -10
  102. package/docs/interfaces/VfsDirectoryNode.html +0 -15
  103. package/docs/interfaces/VfsFileNode.html +0 -17
  104. package/docs/interfaces/VfsOptions.html +0 -26
  105. package/docs/interfaces/VfsSnapshot.html +0 -3
  106. package/docs/interfaces/VfsSnapshotBaseNode.html +0 -8
  107. package/docs/interfaces/VfsSnapshotDirectoryNode.html +0 -10
  108. package/docs/interfaces/VfsSnapshotFileNode.html +0 -12
  109. package/docs/interfaces/VirtualActiveSession.html +0 -12
  110. package/docs/interfaces/VirtualSftpServerOptions.html +0 -7
  111. package/docs/interfaces/VirtualShellVfsLike.html +0 -15
  112. package/docs/interfaces/VirtualShellVfsOptions.html +0 -3
  113. package/docs/interfaces/WriteFileOptions.html +0 -6
  114. package/docs/media/LICENSE +0 -21
  115. package/docs/modules.html +0 -1
  116. package/docs/types/ArgParseOptions.html +0 -4
  117. package/docs/types/CommandMode.html +0 -2
  118. package/docs/types/CommandOutcome.html +0 -2
  119. package/docs/types/IdleState.html +0 -1
  120. package/docs/types/VfsNodeStats.html +0 -2
  121. package/docs/types/VfsNodeType.html +0 -2
  122. package/docs/types/VfsPersistenceMode.html +0 -5
  123. package/docs/types/VfsSnapshotNode.html +0 -2
  124. package/examples/README.md +0 -288
  125. package/examples/app.js +0 -1751
  126. package/examples/app.ts +0 -299
  127. package/examples/build.js +0 -27
  128. package/examples/demo.html +0 -33
  129. package/examples/honeypot-audit.ts +0 -180
  130. package/examples/honeypot-export.ts +0 -253
  131. package/examples/honeypot-quickstart.ts +0 -110
  132. package/examples/index.html +0 -82
  133. package/examples/server.js +0 -55
  134. package/polyfills/buffer.js +0 -117
  135. package/polyfills/node_child_process/index.js +0 -2
  136. package/polyfills/node_crypto/index.js +0 -167
  137. package/polyfills/node_events/index.js +0 -9
  138. package/polyfills/node_fs/index.js +0 -202
  139. package/polyfills/node_fs/promises.js +0 -4
  140. package/polyfills/node_os/index.js +0 -9
  141. package/polyfills/node_path/index.js +0 -28
  142. package/polyfills/node_vm/index.js +0 -7
  143. package/polyfills/node_zlib/index.js +0 -3
  144. package/polyfills/process.js +0 -14
  145. package/polyfills/ssh2/index.js +0 -75
  146. package/scripts/build-all.mjs +0 -226
  147. package/scripts/build-names.mjs +0 -43
  148. package/scripts/generate-manuals-bundle.mjs +0 -49
  149. package/scripts/postinstall.js +0 -42
  150. package/scripts/publish-package.sh +0 -70
  151. package/src/Honeypot/index.ts +0 -457
  152. package/src/SSHClient/index.ts +0 -270
  153. package/src/SSHMimic/exec.ts +0 -49
  154. package/src/SSHMimic/executor.ts +0 -251
  155. package/src/SSHMimic/hostKey.ts +0 -21
  156. package/src/SSHMimic/index.ts +0 -337
  157. package/src/SSHMimic/loginBanner.ts +0 -36
  158. package/src/SSHMimic/loginFormat.ts +0 -10
  159. package/src/SSHMimic/prompt.ts +0 -14
  160. package/src/SSHMimic/sftp.ts +0 -883
  161. package/src/VirtualFileSystem/binaryPack.ts +0 -258
  162. package/src/VirtualFileSystem/index.ts +0 -1193
  163. package/src/VirtualFileSystem/internalTypes.ts +0 -43
  164. package/src/VirtualFileSystem/journal.ts +0 -171
  165. package/src/VirtualFileSystem/path.ts +0 -74
  166. package/src/VirtualPackageManager/index.ts +0 -996
  167. package/src/VirtualShell/idleManager.ts +0 -137
  168. package/src/VirtualShell/index.ts +0 -475
  169. package/src/VirtualShell/shell.ts +0 -700
  170. package/src/VirtualShell/shellParser.ts +0 -285
  171. package/src/VirtualUserManager/index.ts +0 -758
  172. package/src/bun.d.ts +0 -1
  173. package/src/commands/adduser.ts +0 -103
  174. package/src/commands/alias.ts +0 -69
  175. package/src/commands/apt.ts +0 -233
  176. package/src/commands/awk.ts +0 -168
  177. package/src/commands/base64.ts +0 -29
  178. package/src/commands/cat.ts +0 -52
  179. package/src/commands/cd.ts +0 -25
  180. package/src/commands/chmod.ts +0 -85
  181. package/src/commands/clear.ts +0 -15
  182. package/src/commands/command-helpers.ts +0 -286
  183. package/src/commands/cp.ts +0 -83
  184. package/src/commands/curl.ts +0 -147
  185. package/src/commands/cut.ts +0 -36
  186. package/src/commands/date.ts +0 -30
  187. package/src/commands/declare.ts +0 -49
  188. package/src/commands/deluser.ts +0 -98
  189. package/src/commands/df.ts +0 -23
  190. package/src/commands/diff.ts +0 -43
  191. package/src/commands/dpkg.ts +0 -180
  192. package/src/commands/du.ts +0 -56
  193. package/src/commands/echo.ts +0 -58
  194. package/src/commands/env.ts +0 -23
  195. package/src/commands/exit.ts +0 -18
  196. package/src/commands/export.ts +0 -34
  197. package/src/commands/find.ts +0 -68
  198. package/src/commands/free.ts +0 -47
  199. package/src/commands/grep.ts +0 -116
  200. package/src/commands/groups.ts +0 -19
  201. package/src/commands/gzip.ts +0 -88
  202. package/src/commands/head.ts +0 -52
  203. package/src/commands/help.ts +0 -152
  204. package/src/commands/helpers.ts +0 -234
  205. package/src/commands/history.ts +0 -34
  206. package/src/commands/hostname.ts +0 -14
  207. package/src/commands/htop.ts +0 -20
  208. package/src/commands/id.ts +0 -19
  209. package/src/commands/index.ts +0 -9
  210. package/src/commands/kill.ts +0 -19
  211. package/src/commands/ln.ts +0 -71
  212. package/src/commands/ls.ts +0 -243
  213. package/src/commands/lsb-release.ts +0 -63
  214. package/src/commands/man.ts +0 -31
  215. package/src/commands/manuals/adduser.txt +0 -11
  216. package/src/commands/manuals/apt-cache.txt +0 -12
  217. package/src/commands/manuals/apt.txt +0 -20
  218. package/src/commands/manuals/awk.txt +0 -13
  219. package/src/commands/manuals/cat.txt +0 -14
  220. package/src/commands/manuals/cd.txt +0 -16
  221. package/src/commands/manuals/chmod.txt +0 -16
  222. package/src/commands/manuals/clear.txt +0 -10
  223. package/src/commands/manuals/cp.txt +0 -10
  224. package/src/commands/manuals/curl.txt +0 -20
  225. package/src/commands/manuals/date.txt +0 -14
  226. package/src/commands/manuals/declare.txt +0 -12
  227. package/src/commands/manuals/deluser.txt +0 -10
  228. package/src/commands/manuals/df.txt +0 -10
  229. package/src/commands/manuals/dpkg-query.txt +0 -11
  230. package/src/commands/manuals/dpkg.txt +0 -14
  231. package/src/commands/manuals/du.txt +0 -11
  232. package/src/commands/manuals/echo.txt +0 -11
  233. package/src/commands/manuals/false.txt +0 -10
  234. package/src/commands/manuals/find.txt +0 -11
  235. package/src/commands/manuals/free.txt +0 -12
  236. package/src/commands/manuals/grep.txt +0 -13
  237. package/src/commands/manuals/groups.txt +0 -10
  238. package/src/commands/manuals/gzip.txt +0 -11
  239. package/src/commands/manuals/head.txt +0 -10
  240. package/src/commands/manuals/help.txt +0 -11
  241. package/src/commands/manuals/history.txt +0 -11
  242. package/src/commands/manuals/hostname.txt +0 -10
  243. package/src/commands/manuals/id.txt +0 -10
  244. package/src/commands/manuals/kill.txt +0 -13
  245. package/src/commands/manuals/ls.txt +0 -20
  246. package/src/commands/manuals/lsb_release.txt +0 -14
  247. package/src/commands/manuals/mkdir.txt +0 -10
  248. package/src/commands/manuals/mv.txt +0 -10
  249. package/src/commands/manuals/nano.txt +0 -11
  250. package/src/commands/manuals/neofetch.txt +0 -10
  251. package/src/commands/manuals/node.txt +0 -13
  252. package/src/commands/manuals/npm.txt +0 -13
  253. package/src/commands/manuals/npx.txt +0 -13
  254. package/src/commands/manuals/passwd.txt +0 -11
  255. package/src/commands/manuals/ping.txt +0 -10
  256. package/src/commands/manuals/printf.txt +0 -11
  257. package/src/commands/manuals/ps.txt +0 -10
  258. package/src/commands/manuals/pwd.txt +0 -10
  259. package/src/commands/manuals/python3.txt +0 -13
  260. package/src/commands/manuals/readlink.txt +0 -10
  261. package/src/commands/manuals/return.txt +0 -10
  262. package/src/commands/manuals/rm.txt +0 -10
  263. package/src/commands/manuals/sed.txt +0 -11
  264. package/src/commands/manuals/set.txt +0 -11
  265. package/src/commands/manuals/shift.txt +0 -10
  266. package/src/commands/manuals/sleep.txt +0 -10
  267. package/src/commands/manuals/sort.txt +0 -12
  268. package/src/commands/manuals/source.txt +0 -11
  269. package/src/commands/manuals/ssh.txt +0 -11
  270. package/src/commands/manuals/stat.txt +0 -10
  271. package/src/commands/manuals/su.txt +0 -13
  272. package/src/commands/manuals/sudo.txt +0 -11
  273. package/src/commands/manuals/tail.txt +0 -10
  274. package/src/commands/manuals/tar.txt +0 -19
  275. package/src/commands/manuals/tee.txt +0 -10
  276. package/src/commands/manuals/test.txt +0 -11
  277. package/src/commands/manuals/touch.txt +0 -11
  278. package/src/commands/manuals/tr.txt +0 -10
  279. package/src/commands/manuals/trap.txt +0 -10
  280. package/src/commands/manuals/true.txt +0 -10
  281. package/src/commands/manuals/type.txt +0 -10
  282. package/src/commands/manuals/uname.txt +0 -12
  283. package/src/commands/manuals/uniq.txt +0 -12
  284. package/src/commands/manuals/unset.txt +0 -10
  285. package/src/commands/manuals/uptime.txt +0 -11
  286. package/src/commands/manuals/wc.txt +0 -12
  287. package/src/commands/manuals/wget.txt +0 -12
  288. package/src/commands/manuals/which.txt +0 -10
  289. package/src/commands/manuals/whoami.txt +0 -10
  290. package/src/commands/manuals/xargs.txt +0 -10
  291. package/src/commands/manuals-bundle.ts +0 -898
  292. package/src/commands/mkdir.ts +0 -31
  293. package/src/commands/mv.ts +0 -50
  294. package/src/commands/nano.ts +0 -38
  295. package/src/commands/neofetch.ts +0 -53
  296. package/src/commands/node.ts +0 -341
  297. package/src/commands/npm.ts +0 -132
  298. package/src/commands/passwd.ts +0 -50
  299. package/src/commands/ping.ts +0 -32
  300. package/src/commands/printf.ts +0 -129
  301. package/src/commands/ps.ts +0 -58
  302. package/src/commands/pwd.ts +0 -9
  303. package/src/commands/python.ts +0 -2229
  304. package/src/commands/read.ts +0 -46
  305. package/src/commands/registry.ts +0 -249
  306. package/src/commands/rm.ts +0 -42
  307. package/src/commands/runtime.ts +0 -421
  308. package/src/commands/sed.ts +0 -68
  309. package/src/commands/seq.ts +0 -43
  310. package/src/commands/set.ts +0 -29
  311. package/src/commands/sh.ts +0 -467
  312. package/src/commands/shift.ts +0 -63
  313. package/src/commands/sleep.ts +0 -20
  314. package/src/commands/sort.ts +0 -46
  315. package/src/commands/source.ts +0 -52
  316. package/src/commands/stat.ts +0 -61
  317. package/src/commands/su.ts +0 -72
  318. package/src/commands/sudo.ts +0 -76
  319. package/src/commands/tail.ts +0 -53
  320. package/src/commands/tar.ts +0 -102
  321. package/src/commands/tee.ts +0 -36
  322. package/src/commands/test.ts +0 -137
  323. package/src/commands/touch.ts +0 -28
  324. package/src/commands/tr.ts +0 -70
  325. package/src/commands/tree.ts +0 -20
  326. package/src/commands/true.ts +0 -27
  327. package/src/commands/type.ts +0 -48
  328. package/src/commands/uname.ts +0 -29
  329. package/src/commands/uniq.ts +0 -39
  330. package/src/commands/unset.ts +0 -17
  331. package/src/commands/uptime.ts +0 -54
  332. package/src/commands/wc.ts +0 -55
  333. package/src/commands/wget.ts +0 -148
  334. package/src/commands/which.ts +0 -37
  335. package/src/commands/who.ts +0 -25
  336. package/src/commands/whoami.ts +0 -14
  337. package/src/commands/xargs.ts +0 -31
  338. package/src/index.ts +0 -67
  339. package/src/modules/linuxRootfs.ts +0 -1961
  340. package/src/modules/neofetch.ts +0 -358
  341. package/src/modules/shellInteractive.ts +0 -57
  342. package/src/modules/shellRuntime.ts +0 -76
  343. package/src/self-standalone.ts +0 -542
  344. package/src/standalone-wo-sftp.ts +0 -38
  345. package/src/standalone.ts +0 -72
  346. package/src/types/commands.ts +0 -146
  347. package/src/types/pipeline.ts +0 -52
  348. package/src/types/streams.ts +0 -32
  349. package/src/types/tar-stream.d.ts +0 -38
  350. package/src/types/vfs.ts +0 -98
  351. package/src/utils/expand.ts +0 -491
  352. package/src/utils/perfLogger.ts +0 -72
  353. package/src/utils/tokenize.ts +0 -98
  354. package/src/utils/vfsDiff.ts +0 -275
  355. package/tests/command-helpers.test.ts +0 -116
  356. package/tests/commands-admin-net.test.ts +0 -441
  357. package/tests/commands-advanced.test.ts +0 -456
  358. package/tests/commands-core.test.ts +0 -562
  359. package/tests/commands-missing.test.ts +0 -570
  360. package/tests/commands-specific-units.test.ts +0 -327
  361. package/tests/commands-text-sys.test.ts +0 -445
  362. package/tests/expand.test.ts +0 -170
  363. package/tests/helpers.test.ts +0 -97
  364. package/tests/new-features.test.ts +0 -1036
  365. package/tests/parser-executor.test.ts +0 -37
  366. package/tests/sftp.test.ts +0 -323
  367. package/tests/ssh-exec.test.ts +0 -45
  368. package/tests/test-helper.ts +0 -79
  369. package/tests/users.test.ts +0 -86
  370. package/tsconfig.json +0 -49
  371. 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
- });