typescript-virtual-container 1.2.8 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/.vscode/settings.json +0 -1
  2. package/README.md +462 -44
  3. package/biome.json +7 -0
  4. package/dist/SSHMimic/exec.d.ts.map +1 -1
  5. package/dist/SSHMimic/executor.d.ts.map +1 -1
  6. package/dist/SSHMimic/executor.js +35 -21
  7. package/dist/SSHMimic/index.d.ts.map +1 -1
  8. package/dist/SSHMimic/index.js +20 -6
  9. package/dist/VirtualFileSystem/binaryPack.d.ts.map +1 -1
  10. package/dist/VirtualFileSystem/binaryPack.js +29 -6
  11. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  12. package/dist/VirtualFileSystem/index.js +36 -13
  13. package/dist/VirtualPackageManager/index.d.ts +202 -0
  14. package/dist/VirtualPackageManager/index.d.ts.map +1 -0
  15. package/dist/VirtualPackageManager/index.js +825 -0
  16. package/dist/VirtualShell/index.d.ts +93 -12
  17. package/dist/VirtualShell/index.d.ts.map +1 -1
  18. package/dist/VirtualShell/index.js +95 -13
  19. package/dist/VirtualShell/shell.d.ts.map +1 -1
  20. package/dist/VirtualShell/shell.js +3 -1
  21. package/dist/VirtualShell/shellParser.d.ts.map +1 -1
  22. package/dist/VirtualUserManager/index.d.ts +52 -20
  23. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  24. package/dist/VirtualUserManager/index.js +54 -20
  25. package/dist/commands/adduser.d.ts +6 -0
  26. package/dist/commands/adduser.d.ts.map +1 -1
  27. package/dist/commands/adduser.js +6 -0
  28. package/dist/commands/alias.d.ts +9 -0
  29. package/dist/commands/alias.d.ts.map +1 -0
  30. package/dist/commands/alias.js +63 -0
  31. package/dist/commands/apt.d.ts +9 -0
  32. package/dist/commands/apt.d.ts.map +1 -0
  33. package/dist/commands/apt.js +205 -0
  34. package/dist/commands/awk.d.ts +11 -0
  35. package/dist/commands/awk.d.ts.map +1 -1
  36. package/dist/commands/awk.js +15 -2
  37. package/dist/commands/base64.d.ts +5 -0
  38. package/dist/commands/base64.d.ts.map +1 -1
  39. package/dist/commands/base64.js +9 -1
  40. package/dist/commands/cat.d.ts +5 -0
  41. package/dist/commands/cat.d.ts.map +1 -1
  42. package/dist/commands/cat.js +35 -8
  43. package/dist/commands/cd.d.ts +5 -0
  44. package/dist/commands/cd.d.ts.map +1 -1
  45. package/dist/commands/cd.js +5 -0
  46. package/dist/commands/chmod.d.ts +5 -0
  47. package/dist/commands/chmod.d.ts.map +1 -1
  48. package/dist/commands/chmod.js +57 -3
  49. package/dist/commands/command-helpers.d.ts +78 -4
  50. package/dist/commands/command-helpers.d.ts.map +1 -1
  51. package/dist/commands/command-helpers.js +78 -4
  52. package/dist/commands/cp.d.ts +5 -0
  53. package/dist/commands/cp.d.ts.map +1 -1
  54. package/dist/commands/cp.js +5 -0
  55. package/dist/commands/curl.d.ts +5 -0
  56. package/dist/commands/curl.d.ts.map +1 -1
  57. package/dist/commands/curl.js +106 -26
  58. package/dist/commands/cut.d.ts +5 -0
  59. package/dist/commands/cut.d.ts.map +1 -1
  60. package/dist/commands/cut.js +8 -1
  61. package/dist/commands/date.d.ts +5 -0
  62. package/dist/commands/date.d.ts.map +1 -1
  63. package/dist/commands/date.js +7 -1
  64. package/dist/commands/declare.d.ts +3 -0
  65. package/dist/commands/declare.d.ts.map +1 -0
  66. package/dist/commands/declare.js +39 -0
  67. package/dist/commands/diff.d.ts +5 -0
  68. package/dist/commands/diff.d.ts.map +1 -1
  69. package/dist/commands/diff.js +5 -0
  70. package/dist/commands/dpkg.d.ts +9 -0
  71. package/dist/commands/dpkg.d.ts.map +1 -0
  72. package/dist/commands/dpkg.js +161 -0
  73. package/dist/commands/du.d.ts.map +1 -1
  74. package/dist/commands/du.js +8 -2
  75. package/dist/commands/echo.d.ts +5 -0
  76. package/dist/commands/echo.d.ts.map +1 -1
  77. package/dist/commands/echo.js +33 -12
  78. package/dist/commands/env.d.ts +5 -0
  79. package/dist/commands/env.d.ts.map +1 -1
  80. package/dist/commands/env.js +11 -1
  81. package/dist/commands/exit.d.ts +5 -0
  82. package/dist/commands/exit.d.ts.map +1 -1
  83. package/dist/commands/exit.js +12 -2
  84. package/dist/commands/export.d.ts.map +1 -1
  85. package/dist/commands/export.js +3 -1
  86. package/dist/commands/find.d.ts +5 -0
  87. package/dist/commands/find.d.ts.map +1 -1
  88. package/dist/commands/find.js +5 -0
  89. package/dist/commands/free.d.ts +8 -0
  90. package/dist/commands/free.d.ts.map +1 -0
  91. package/dist/commands/free.js +43 -0
  92. package/dist/commands/grep.d.ts +5 -0
  93. package/dist/commands/grep.d.ts.map +1 -1
  94. package/dist/commands/grep.js +12 -2
  95. package/dist/commands/gzip.d.ts +5 -0
  96. package/dist/commands/gzip.d.ts.map +1 -1
  97. package/dist/commands/gzip.js +18 -2
  98. package/dist/commands/head.d.ts +5 -0
  99. package/dist/commands/head.d.ts.map +1 -1
  100. package/dist/commands/head.js +5 -0
  101. package/dist/commands/help.d.ts.map +1 -1
  102. package/dist/commands/help.js +98 -45
  103. package/dist/commands/helpers.d.ts +3 -0
  104. package/dist/commands/helpers.d.ts.map +1 -1
  105. package/dist/commands/helpers.js +3 -0
  106. package/dist/commands/history.d.ts +8 -0
  107. package/dist/commands/history.d.ts.map +1 -0
  108. package/dist/commands/history.js +26 -0
  109. package/dist/commands/hostname.d.ts +5 -0
  110. package/dist/commands/hostname.d.ts.map +1 -1
  111. package/dist/commands/hostname.js +5 -0
  112. package/dist/commands/id.d.ts.map +1 -1
  113. package/dist/commands/id.js +4 -1
  114. package/dist/commands/index.d.ts +2 -10
  115. package/dist/commands/index.d.ts.map +1 -1
  116. package/dist/commands/index.js +2 -231
  117. package/dist/commands/ls.d.ts.map +1 -1
  118. package/dist/commands/ls.js +6 -3
  119. package/dist/commands/lsb-release.d.ts +3 -0
  120. package/dist/commands/lsb-release.d.ts.map +1 -0
  121. package/dist/commands/lsb-release.js +56 -0
  122. package/dist/commands/man.d.ts +3 -0
  123. package/dist/commands/man.d.ts.map +1 -0
  124. package/dist/commands/man.js +155 -0
  125. package/dist/commands/nano.js +1 -1
  126. package/dist/commands/neofetch.d.ts.map +1 -1
  127. package/dist/commands/neofetch.js +6 -1
  128. package/dist/commands/node.d.ts +9 -0
  129. package/dist/commands/node.d.ts.map +1 -0
  130. package/dist/commands/node.js +316 -0
  131. package/dist/commands/npm.d.ts +19 -0
  132. package/dist/commands/npm.d.ts.map +1 -0
  133. package/dist/commands/npm.js +109 -0
  134. package/dist/commands/ping.d.ts.map +1 -1
  135. package/dist/commands/ping.js +7 -2
  136. package/dist/commands/printf.d.ts +3 -0
  137. package/dist/commands/printf.d.ts.map +1 -0
  138. package/dist/commands/printf.js +113 -0
  139. package/dist/commands/ps.d.ts.map +1 -1
  140. package/dist/commands/ps.js +30 -6
  141. package/dist/commands/python.d.ts +30 -0
  142. package/dist/commands/python.d.ts.map +1 -0
  143. package/dist/commands/python.js +2058 -0
  144. package/dist/commands/read.d.ts +3 -0
  145. package/dist/commands/read.d.ts.map +1 -0
  146. package/dist/commands/read.js +34 -0
  147. package/dist/commands/registry.d.ts +8 -0
  148. package/dist/commands/registry.d.ts.map +1 -0
  149. package/dist/commands/registry.js +229 -0
  150. package/dist/commands/runtime.d.ts +6 -0
  151. package/dist/commands/runtime.d.ts.map +1 -0
  152. package/dist/commands/runtime.js +280 -0
  153. package/dist/commands/sed.d.ts.map +1 -1
  154. package/dist/commands/sed.js +11 -3
  155. package/dist/commands/set.d.ts.map +1 -1
  156. package/dist/commands/set.js +9 -3
  157. package/dist/commands/sh.d.ts.map +1 -1
  158. package/dist/commands/sh.js +69 -30
  159. package/dist/commands/shift.d.ts +5 -0
  160. package/dist/commands/shift.d.ts.map +1 -0
  161. package/dist/commands/shift.js +52 -0
  162. package/dist/commands/sleep.d.ts.map +1 -1
  163. package/dist/commands/sort.d.ts.map +1 -1
  164. package/dist/commands/sort.js +4 -2
  165. package/dist/commands/source.d.ts +3 -0
  166. package/dist/commands/source.d.ts.map +1 -0
  167. package/dist/commands/source.js +34 -0
  168. package/dist/commands/sudo.js +1 -1
  169. package/dist/commands/tar.d.ts.map +1 -1
  170. package/dist/commands/tar.js +11 -3
  171. package/dist/commands/tee.d.ts.map +1 -1
  172. package/dist/commands/tee.js +8 -6
  173. package/dist/commands/test.d.ts +3 -0
  174. package/dist/commands/test.d.ts.map +1 -0
  175. package/dist/commands/test.js +114 -0
  176. package/dist/commands/tr.d.ts.map +1 -1
  177. package/dist/commands/tr.js +3 -1
  178. package/dist/commands/true.d.ts +4 -0
  179. package/dist/commands/true.d.ts.map +1 -0
  180. package/dist/commands/true.js +14 -0
  181. package/dist/commands/type.d.ts +3 -0
  182. package/dist/commands/type.d.ts.map +1 -0
  183. package/dist/commands/type.js +34 -0
  184. package/dist/commands/uname.d.ts.map +1 -1
  185. package/dist/commands/uname.js +4 -1
  186. package/dist/commands/uniq.d.ts.map +1 -1
  187. package/dist/commands/uptime.d.ts +3 -0
  188. package/dist/commands/uptime.d.ts.map +1 -0
  189. package/dist/commands/uptime.js +43 -0
  190. package/dist/commands/wget.d.ts.map +1 -1
  191. package/dist/commands/wget.js +92 -96
  192. package/dist/commands/which.d.ts +3 -0
  193. package/dist/commands/which.d.ts.map +1 -0
  194. package/dist/commands/which.js +32 -0
  195. package/dist/commands/xargs.d.ts.map +1 -1
  196. package/dist/commands/xargs.js +1 -1
  197. package/dist/index.d.ts +15 -11
  198. package/dist/index.d.ts.map +1 -1
  199. package/dist/index.js +9 -8
  200. package/dist/modules/linuxRootfs.d.ts +41 -0
  201. package/dist/modules/linuxRootfs.d.ts.map +1 -0
  202. package/dist/modules/linuxRootfs.js +440 -0
  203. package/dist/modules/neofetch.d.ts.map +1 -1
  204. package/dist/modules/neofetch.js +1 -0
  205. package/dist/standalone-wo-sftp.d.ts +2 -0
  206. package/dist/standalone-wo-sftp.d.ts.map +1 -0
  207. package/dist/standalone-wo-sftp.js +30 -0
  208. package/dist/utils/expand.d.ts +50 -0
  209. package/dist/utils/expand.d.ts.map +1 -0
  210. package/dist/utils/expand.js +183 -0
  211. package/dist/utils/vfsDiff.d.ts +90 -0
  212. package/dist/utils/vfsDiff.d.ts.map +1 -0
  213. package/dist/utils/vfsDiff.js +177 -0
  214. package/package.json +3 -1
  215. package/src/SSHMimic/exec.ts +10 -1
  216. package/src/SSHMimic/executor.ts +105 -21
  217. package/src/SSHMimic/index.ts +49 -15
  218. package/src/VirtualFileSystem/binaryPack.ts +35 -8
  219. package/src/VirtualFileSystem/index.ts +78 -28
  220. package/src/VirtualPackageManager/index.ts +979 -0
  221. package/src/VirtualShell/index.ts +133 -14
  222. package/src/VirtualShell/shell.ts +23 -3
  223. package/src/VirtualShell/shellParser.ts +134 -36
  224. package/src/VirtualUserManager/index.ts +62 -22
  225. package/src/commands/adduser.ts +6 -0
  226. package/src/commands/alias.ts +64 -0
  227. package/src/commands/apt.ts +228 -0
  228. package/src/commands/awk.ts +20 -6
  229. package/src/commands/base64.ts +13 -2
  230. package/src/commands/cat.ts +40 -8
  231. package/src/commands/cd.ts +5 -0
  232. package/src/commands/chmod.ts +53 -3
  233. package/src/commands/command-helpers.ts +78 -4
  234. package/src/commands/cp.ts +5 -0
  235. package/src/commands/curl.ts +118 -33
  236. package/src/commands/cut.ts +8 -1
  237. package/src/commands/date.ts +7 -1
  238. package/src/commands/declare.ts +44 -0
  239. package/src/commands/diff.ts +17 -3
  240. package/src/commands/dpkg.ts +180 -0
  241. package/src/commands/du.ts +17 -5
  242. package/src/commands/echo.ts +41 -12
  243. package/src/commands/env.ts +11 -1
  244. package/src/commands/exit.ts +12 -2
  245. package/src/commands/export.ts +3 -1
  246. package/src/commands/find.ts +5 -0
  247. package/src/commands/free.ts +47 -0
  248. package/src/commands/grep.ts +12 -2
  249. package/src/commands/gzip.ts +28 -4
  250. package/src/commands/head.ts +5 -0
  251. package/src/commands/help.ts +121 -47
  252. package/src/commands/helpers.ts +8 -0
  253. package/src/commands/history.ts +34 -0
  254. package/src/commands/hostname.ts +5 -0
  255. package/src/commands/id.ts +4 -1
  256. package/src/commands/index.ts +9 -255
  257. package/src/commands/ls.ts +6 -3
  258. package/src/commands/lsb-release.ts +58 -0
  259. package/src/commands/man.ts +166 -0
  260. package/src/commands/nano.ts +1 -1
  261. package/src/commands/neofetch.ts +6 -1
  262. package/src/commands/node.ts +341 -0
  263. package/src/commands/npm.ts +132 -0
  264. package/src/commands/ping.ts +10 -3
  265. package/src/commands/printf.ts +112 -0
  266. package/src/commands/ps.ts +40 -6
  267. package/src/commands/python.ts +2229 -0
  268. package/src/commands/read.ts +41 -0
  269. package/src/commands/registry.ts +244 -0
  270. package/src/commands/runtime.ts +353 -0
  271. package/src/commands/sed.ts +27 -9
  272. package/src/commands/set.ts +9 -3
  273. package/src/commands/sh.ts +170 -44
  274. package/src/commands/shift.ts +53 -0
  275. package/src/commands/sleep.ts +2 -1
  276. package/src/commands/sort.ts +10 -6
  277. package/src/commands/source.ts +47 -0
  278. package/src/commands/sudo.ts +1 -1
  279. package/src/commands/tar.ts +28 -7
  280. package/src/commands/tee.ts +7 -1
  281. package/src/commands/test.ts +135 -0
  282. package/src/commands/tr.ts +3 -1
  283. package/src/commands/true.ts +17 -0
  284. package/src/commands/type.ts +43 -0
  285. package/src/commands/uname.ts +5 -1
  286. package/src/commands/uniq.ts +8 -2
  287. package/src/commands/uptime.ts +49 -0
  288. package/src/commands/wget.ts +105 -119
  289. package/src/commands/which.ts +37 -0
  290. package/src/commands/xargs.ts +11 -2
  291. package/src/index.ts +27 -18
  292. package/src/modules/linuxRootfs.ts +642 -0
  293. package/src/modules/neofetch.ts +1 -0
  294. package/src/standalone-wo-sftp.ts +38 -0
  295. package/src/utils/expand.ts +238 -0
  296. package/src/utils/vfsDiff.ts +275 -0
  297. package/standalone-wo-sftp.js +507 -0
  298. package/standalone-wo-sftp.js.map +7 -0
  299. package/standalone.js +486 -109
  300. package/standalone.js.map +4 -4
  301. package/tests/bun-test-shim.ts +9 -1
  302. package/tests/command-helpers.test.ts +1 -5
  303. package/tests/new-features.test.ts +1036 -0
  304. package/tests/parser-executor.test.ts +27 -27
  305. package/tests/sftp.test.ts +122 -42
  306. package/tests/users.test.ts +23 -5
  307. package/CHANGELOG.md +0 -150
@@ -30,15 +30,18 @@ export const lsCommand: ShellModule = {
30
30
  name: "ls",
31
31
  description: "List directory contents",
32
32
  category: "navigation",
33
- params: ["[path]"],
33
+ params: ["[-la] [path]"],
34
34
  run: ({ authUser, shell, cwd, args }) => {
35
35
  const longFormat = ifFlag(args, ["-l", "--long"]);
36
- const targetArg = getArg(args, 0, { flags: ["-l", "--long"] });
36
+ const showHidden = ifFlag(args, ["-a", "--all"]);
37
+ const targetArg = getArg(args, 0, {
38
+ flags: ["-l", "--long", "-a", "--all", "-la", "-al"],
39
+ });
37
40
  const target = resolvePath(cwd, targetArg ?? cwd);
38
41
  assertPathAccess(authUser, target, "ls");
39
42
  const items = shell.vfs
40
43
  .list(target)
41
- .filter((name) => !name.startsWith("."));
44
+ .filter((name) => showHidden || !name.startsWith("."));
42
45
  const rendered = longFormat
43
46
  ? items
44
47
  .map((name) => {
@@ -0,0 +1,58 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ import { ifFlag } from "./command-helpers";
3
+
4
+ export const lsbReleaseCommand: ShellModule = {
5
+ name: "lsb_release",
6
+ description: "Print distribution-specific information",
7
+ category: "system",
8
+ params: ["[-a] [-i] [-d] [-r] [-c]"],
9
+ run: ({ args, shell }) => {
10
+ let osName = shell.properties?.os ?? "Fortune GNU/Linux x64";
11
+ let codename = "aurora";
12
+ let version = "1.0";
13
+
14
+ try {
15
+ const content = shell.vfs.readFile("/etc/os-release");
16
+ for (const line of content.split("\n")) {
17
+ if (line.startsWith("PRETTY_NAME="))
18
+ osName = line
19
+ .slice("PRETTY_NAME=".length)
20
+ .replace(/^"|"$/g, "")
21
+ .trim();
22
+ if (line.startsWith("VERSION_CODENAME="))
23
+ codename = line.slice("VERSION_CODENAME=".length).trim();
24
+ if (line.startsWith("VERSION_ID="))
25
+ version = line
26
+ .slice("VERSION_ID=".length)
27
+ .replace(/^"|"$/g, "")
28
+ .trim();
29
+ }
30
+ } catch {}
31
+
32
+ const all = ifFlag(args, ["-a", "--all"]);
33
+ const showId = ifFlag(args, ["-i", "--id"]);
34
+ const showDesc = ifFlag(args, ["-d", "--description"]);
35
+ const showRelease = ifFlag(args, ["-r", "--release"]);
36
+ const showCodename = ifFlag(args, ["-c", "--codename"]);
37
+
38
+ if (all || args.length === 0) {
39
+ return {
40
+ stdout: [
41
+ `Distributor ID:\tFortune`,
42
+ `Description:\t${osName}`,
43
+ `Release:\t${version}`,
44
+ `Codename:\t${codename}`,
45
+ ].join("\n"),
46
+ exitCode: 0,
47
+ };
48
+ }
49
+
50
+ const lines: string[] = [];
51
+ if (showId) lines.push(`Distributor ID:\tFortune`);
52
+ if (showDesc) lines.push(`Description:\t${osName}`);
53
+ if (showRelease) lines.push(`Release:\t${version}`);
54
+ if (showCodename) lines.push(`Codename:\t${codename}`);
55
+
56
+ return { stdout: lines.join("\n"), exitCode: 0 };
57
+ },
58
+ };
@@ -0,0 +1,166 @@
1
+ import type { ShellModule } from "../types/commands";
2
+
3
+ const MAN_PAGES: Record<string, string> = {
4
+ ls: `LS(1) User Commands LS(1)
5
+
6
+ NAME
7
+ ls - list directory contents
8
+
9
+ SYNOPSIS
10
+ ls [OPTION]... [FILE]...
11
+
12
+ DESCRIPTION
13
+ List information about the FILEs (the current directory by default).
14
+
15
+ OPTIONS
16
+ -l use a long listing format
17
+ -a do not ignore entries starting with .
18
+ -h with -l, print human readable sizes
19
+ -r reverse order while sorting
20
+ -t sort by modification time
21
+
22
+ AUTHOR
23
+ Written by Richard M. Stallman and David MacKenzie.`,
24
+
25
+ cat: `CAT(1) User Commands CAT(1)
26
+
27
+ NAME
28
+ cat - concatenate files and print on the standard output
29
+
30
+ SYNOPSIS
31
+ cat [OPTION]... [FILE]...
32
+
33
+ DESCRIPTION
34
+ Concatenate FILE(s) to standard output.
35
+
36
+ OPTIONS
37
+ -n, --number number all output lines
38
+ -b, --number-nonblank number nonempty output lines`,
39
+
40
+ grep: `GREP(1) User Commands GREP(1)
41
+
42
+ NAME
43
+ grep, egrep, fgrep - print lines that match patterns
44
+
45
+ SYNOPSIS
46
+ grep [OPTION]... PATTERNS [FILE]...
47
+
48
+ OPTIONS
49
+ -i, --ignore-case ignore case distinctions in patterns and data
50
+ -v, --invert-match select non-matching lines
51
+ -n, --line-number print line number with output lines
52
+ -r, --recursive read all files under each directory, recursively`,
53
+
54
+ apt: `APT(8) APT APT(8)
55
+
56
+ NAME
57
+ apt - command-line interface
58
+
59
+ SYNOPSIS
60
+ apt [options] command
61
+
62
+ DESCRIPTION
63
+ apt provides a high-level commandline interface for the package
64
+ management system.
65
+
66
+ COMMANDS
67
+ install pkg... Install packages
68
+ remove pkg... Remove packages
69
+ update Download package information
70
+ upgrade Upgrade installed packages
71
+ search term Search in package descriptions
72
+ show pkg Show package information
73
+ list List packages`,
74
+
75
+ ssh: `SSH(1) OpenSSH SSH(1)
76
+
77
+ NAME
78
+ ssh - OpenSSH remote login client
79
+
80
+ SYNOPSIS
81
+ ssh [-p port] [user@]hostname [command]
82
+
83
+ DESCRIPTION
84
+ ssh (SSH client) is a program for logging into a remote machine and
85
+ for executing commands on a remote machine.`,
86
+
87
+ curl: `CURL(1) User Commands CURL(1)
88
+
89
+ NAME
90
+ curl - transfer a URL
91
+
92
+ SYNOPSIS
93
+ curl [options / URLs]
94
+
95
+ DESCRIPTION
96
+ curl is a tool for transferring data with URL syntax.
97
+
98
+ OPTIONS
99
+ -o, --output <file> Write output to <file>
100
+ -X, --request <method> Specify request method
101
+ -d, --data <data> HTTP POST data
102
+ -H, --header <header> Pass custom header
103
+ -s, --silent Silent mode
104
+ -I, --head Show document info only
105
+ -L, --location Follow redirects
106
+ -v, --verbose Make the operation more talkative`,
107
+
108
+ chmod: `CHMOD(1) User Commands CHMOD(1)
109
+
110
+ NAME
111
+ chmod - change file mode bits
112
+
113
+ SYNOPSIS
114
+ chmod [OPTION]... MODE[,MODE]... FILE...
115
+ chmod [OPTION]... OCTAL-MODE FILE...
116
+
117
+ DESCRIPTION
118
+ Change the file mode bits of each given file according to MODE.
119
+
120
+ EXAMPLES
121
+ chmod 755 script.sh rwxr-xr-x
122
+ chmod 644 file.txt rw-r--r--
123
+ chmod +x script.sh add execute permission`,
124
+
125
+ tar: `TAR(1) GNU tar Manual TAR(1)
126
+
127
+ NAME
128
+ tar - an archiving utility
129
+
130
+ SYNOPSIS
131
+ tar [OPTION...] [FILE]...
132
+
133
+ DESCRIPTION
134
+ tar saves many files together into a single tape or disk archive,
135
+ and can restore individual files from the archive.
136
+
137
+ OPTIONS
138
+ -c, --create create a new archive
139
+ -x, --extract extract files from an archive
140
+ -z, --gzip filter the archive through gzip
141
+ -f, --file=ARCHIVE use archive file or device ARCHIVE
142
+ -v, --verbose verbosely list files processed
143
+ -t, --list list the contents of an archive`,
144
+ };
145
+
146
+ export const manCommand: ShellModule = {
147
+ name: "man",
148
+ description: "Interface to the system reference manuals",
149
+ category: "shell",
150
+ params: ["<command>"],
151
+ run: ({ args, shell }) => {
152
+ const name = args[0];
153
+ if (!name) return { stderr: "What manual page do you want?", exitCode: 1 };
154
+
155
+ // VFS-installed man pages take priority
156
+ const manPath = `/usr/share/man/man1/${name}.1`;
157
+ if (shell.vfs.exists(manPath)) {
158
+ return { stdout: shell.vfs.readFile(manPath), exitCode: 0 };
159
+ }
160
+
161
+ const page = MAN_PAGES[name.toLowerCase()];
162
+ if (page) return { stdout: page, exitCode: 0 };
163
+
164
+ return { stderr: `No manual entry for ${name}`, exitCode: 16 };
165
+ },
166
+ };
@@ -5,7 +5,7 @@ import { assertPathAccess, resolvePath } from "./helpers";
5
5
  export const nanoCommand: ShellModule = {
6
6
  name: "nano",
7
7
  description: "Text editor",
8
- category: "shell",
8
+ category: "files",
9
9
  params: ["<file>"],
10
10
  run: ({ authUser, shell, cwd, args }) => {
11
11
  const fileArg = args[0];
@@ -6,7 +6,7 @@ import { getAllEnvVars } from "./set";
6
6
  export const neofetchCommand: ShellModule = {
7
7
  name: "neofetch",
8
8
  description: "System info display",
9
- category: "misc",
9
+ category: "system",
10
10
  params: ["[--off]"],
11
11
  run: ({ args, authUser, hostname, shell }) => {
12
12
  const env = getAllEnvVars(authUser);
@@ -32,6 +32,11 @@ export const neofetchCommand: ShellModule = {
32
32
  shell: env.SHELL,
33
33
  shellProps: shell.properties,
34
34
  terminal: env.TERM,
35
+ uptimeSeconds: Math.floor((Date.now() - shell.startTime) / 1000),
36
+ packages: (() => {
37
+ const count = shell.packageManager?.installedCount() ?? 0;
38
+ return `${count} (dpkg)`;
39
+ })(),
35
40
  }),
36
41
  exitCode: 0,
37
42
  };
@@ -0,0 +1,341 @@
1
+ /** biome-ignore-all lint/style/useNamingConvention: node globals and ENV VAR KEYS */
2
+ /**
3
+ * node.ts — Virtual Node.js runtime.
4
+ *
5
+ * Uses `node:vm` for sandboxed evaluation with a controlled context that
6
+ * intercepts `process`, `require`, `console`, and all standard globals.
7
+ * No host filesystem access, no network, no child processes.
8
+ */
9
+ import vm from "node:vm";
10
+ import type { ShellModule } from "../types/commands";
11
+ import { ifFlag } from "./command-helpers";
12
+ import { resolvePath } from "./helpers";
13
+
14
+ const VIRTUAL_VERSION = "v18.19.0";
15
+ const VIRTUAL_VERSIONS = {
16
+ node: VIRTUAL_VERSION,
17
+ npm: "9.2.0",
18
+ v8: "10.2.154.26-node.22",
19
+ };
20
+
21
+ // ─── sandbox context ──────────────────────────────────────────────────────────
22
+
23
+ function makeContext(outputLines: string[], stderrLines: string[]) {
24
+ const fakeProcess = {
25
+ version: VIRTUAL_VERSION,
26
+ versions: VIRTUAL_VERSIONS,
27
+ platform: "linux",
28
+ arch: "x64",
29
+ env: {
30
+ NODE_ENV: "production",
31
+ HOME: "/root",
32
+ PATH: "/usr/local/bin:/usr/bin:/bin",
33
+ },
34
+ argv: ["node"],
35
+ stdout: {
36
+ write: (s: string) => {
37
+ outputLines.push(s);
38
+ return true;
39
+ },
40
+ },
41
+ stderr: {
42
+ write: (s: string) => {
43
+ stderrLines.push(s);
44
+ return true;
45
+ },
46
+ },
47
+ exit: (code = 0) => {
48
+ throw new ExitSignal(code);
49
+ },
50
+ cwd: () => "/root",
51
+ hrtime: () => [0, 0],
52
+ };
53
+
54
+ const fakeConsole = {
55
+ log: (...a: unknown[]) => outputLines.push(a.map(formatValue).join(" ")),
56
+ error: (...a: unknown[]) => stderrLines.push(a.map(formatValue).join(" ")),
57
+ warn: (...a: unknown[]) => stderrLines.push(a.map(formatValue).join(" ")),
58
+ info: (...a: unknown[]) => outputLines.push(a.map(formatValue).join(" ")),
59
+ dir: (v: unknown) => outputLines.push(formatValue(v)),
60
+ };
61
+
62
+ const fakeRequire = (mod: string): unknown => {
63
+ // Provide stubs for common modules
64
+ switch (mod) {
65
+ case "path":
66
+ return {
67
+ join: (...parts: string[]) => parts.join("/").replace(/\/+/g, "/"),
68
+ resolve: (...parts: string[]) =>
69
+ `/${parts.join("/").replace(/^\/+/, "")}`,
70
+ dirname: (p: string) => p.split("/").slice(0, -1).join("/") || "/",
71
+ basename: (p: string) => p.split("/").pop() ?? "",
72
+ extname: (p: string) => {
73
+ const b = p.split("/").pop() ?? "";
74
+ const d = b.lastIndexOf(".");
75
+ return d > 0 ? b.slice(d) : "";
76
+ },
77
+ sep: "/",
78
+ delimiter: ":",
79
+ };
80
+ case "os":
81
+ return {
82
+ platform: () => "linux",
83
+ arch: () => "x64",
84
+ type: () => "Linux",
85
+ hostname: () => "fortune-vm",
86
+ homedir: () => "/root",
87
+ tmpdir: () => "/tmp",
88
+ EOL: "\n",
89
+ };
90
+ case "util":
91
+ return {
92
+ format: (...a: unknown[]) => a.map(formatValue).join(" "),
93
+ inspect: (v: unknown) => formatValue(v),
94
+ };
95
+ case "fs":
96
+ case "fs/promises":
97
+ throw new Error(
98
+ `Cannot require '${mod}': filesystem access not available in virtual runtime`,
99
+ );
100
+ case "child_process":
101
+ case "net":
102
+ case "http":
103
+ case "https":
104
+ throw new Error(
105
+ `Cannot require '${mod}': not available in virtual runtime`,
106
+ );
107
+ default:
108
+ throw new Error(`Cannot find module '${mod}'`);
109
+ }
110
+ };
111
+ fakeRequire.resolve = (id: string) => {
112
+ throw new Error(`Cannot resolve '${id}'`);
113
+ };
114
+ fakeRequire.cache = {};
115
+ fakeRequire.extensions = {};
116
+
117
+ return vm.createContext({
118
+ // Core globals
119
+ console: fakeConsole,
120
+ process: fakeProcess,
121
+ require: fakeRequire,
122
+
123
+ // JS built-ins
124
+ Math,
125
+ JSON,
126
+ Object,
127
+ Array,
128
+ String,
129
+ Number,
130
+ Boolean,
131
+ Symbol,
132
+ Date,
133
+ RegExp,
134
+ Error,
135
+ TypeError,
136
+ RangeError,
137
+ SyntaxError,
138
+ Promise,
139
+ Map,
140
+ Set,
141
+ WeakMap,
142
+ WeakSet,
143
+ parseInt,
144
+ parseFloat,
145
+ isNaN,
146
+ isFinite,
147
+ encodeURIComponent,
148
+ decodeURIComponent,
149
+ encodeURI,
150
+ decodeURI,
151
+ setTimeout: () => {},
152
+ clearTimeout: () => {},
153
+ setInterval: () => {},
154
+ clearInterval: () => {},
155
+ queueMicrotask: () => {},
156
+ globalThis: undefined as unknown, // set below
157
+ undefined,
158
+ Infinity,
159
+ NaN,
160
+ });
161
+ }
162
+
163
+ class ExitSignal {
164
+ constructor(public readonly code: number) {}
165
+ }
166
+
167
+ function formatValue(v: unknown): string {
168
+ if (v === null) return "null";
169
+ if (v === undefined) return "undefined";
170
+ if (typeof v === "string") return v;
171
+ if (typeof v === "function") return `[Function: ${v.name || "(anonymous)"}]`;
172
+ if (Array.isArray(v)) return `[ ${v.map(formatValue).join(", ")} ]`;
173
+ if (v instanceof Error) return `${v.name}: ${v.message}`;
174
+ if (typeof v === "object") {
175
+ try {
176
+ const entries = Object.entries(v as Record<string, unknown>)
177
+ .map(([k, val]) => `${k}: ${formatValue(val)}`)
178
+ .join(", ");
179
+ return `{ ${entries} }`;
180
+ } catch {
181
+ return "[Object]";
182
+ }
183
+ }
184
+ return String(v);
185
+ }
186
+
187
+ // ─── execution ────────────────────────────────────────────────────────────────
188
+
189
+ function runJs(code: string): {
190
+ stdout: string;
191
+ stderr: string;
192
+ exitCode: number;
193
+ } {
194
+ const outputLines: string[] = [];
195
+ const stderrLines: string[] = [];
196
+ const ctx = makeContext(outputLines, stderrLines);
197
+
198
+ let exitCode = 0;
199
+
200
+ try {
201
+ const result = vm.runInContext(code, ctx, { timeout: 5000 });
202
+ // If the expression returned a value and nothing was console.log'd, print it
203
+ if (result !== undefined && outputLines.length === 0) {
204
+ outputLines.push(formatValue(result));
205
+ }
206
+ } catch (err) {
207
+ if (err instanceof ExitSignal) {
208
+ exitCode = err.code;
209
+ } else if (err instanceof Error) {
210
+ stderrLines.push(`${err.name}: ${err.message}`);
211
+ exitCode = 1;
212
+ } else {
213
+ stderrLines.push(String(err));
214
+ exitCode = 1;
215
+ }
216
+ }
217
+
218
+ return {
219
+ stdout: outputLines.length ? `${outputLines.join("\n")}\n` : "",
220
+ stderr: stderrLines.length ? `${stderrLines.join("\n")}\n` : "",
221
+ exitCode,
222
+ };
223
+ }
224
+
225
+ function runJsFile(code: string): {
226
+ stdout: string;
227
+ stderr: string;
228
+ exitCode: number;
229
+ } {
230
+ // If the code is a single expression (no semicolons, no newlines, no statements),
231
+ // wrap it to capture the return value like a REPL would
232
+ const trimmed = code.trim();
233
+ const isExpression =
234
+ !trimmed.includes("\n") &&
235
+ !trimmed.startsWith("const ") &&
236
+ !trimmed.startsWith("let ") &&
237
+ !trimmed.startsWith("var ") &&
238
+ !trimmed.startsWith("function ") &&
239
+ !trimmed.startsWith("class ") &&
240
+ !trimmed.startsWith("if ") &&
241
+ !trimmed.startsWith("for ") &&
242
+ !trimmed.startsWith("while ") &&
243
+ !trimmed.startsWith("import ") &&
244
+ !trimmed.startsWith("//");
245
+
246
+ if (isExpression) return runJs(trimmed);
247
+
248
+ // Multi-line: wrap in IIFE
249
+ return runJs(`(async () => { ${code} })()`);
250
+ }
251
+
252
+ // ─── command ──────────────────────────────────────────────────────────────────
253
+ /**
254
+ * `node` virtual runtime command. Executes JS in a safe sandbox with
255
+ * limited globals and no host FS/child process access.
256
+ * @category system
257
+ * @params []
258
+ */
259
+ export const nodeCommand: ShellModule = {
260
+ name: "node",
261
+ description: "JavaScript runtime (virtual)",
262
+ category: "system",
263
+ params: ["[--version] [-e <expr>] [-p <expr>] [file]"],
264
+ run: ({ args, shell, cwd }) => {
265
+ // Require explicit installation via `apt install nodejs`
266
+ if (!shell.packageManager.isInstalled("nodejs")) {
267
+ return {
268
+ stderr:
269
+ "bash: node: command not found\nHint: install it with: apt install nodejs\n",
270
+ exitCode: 127,
271
+ };
272
+ }
273
+ if (ifFlag(args, ["--version", "-v"])) {
274
+ return { stdout: `${VIRTUAL_VERSION}\n`, exitCode: 0 };
275
+ }
276
+
277
+ if (ifFlag(args, ["--versions"])) {
278
+ return {
279
+ stdout: `${JSON.stringify(VIRTUAL_VERSIONS, null, 2)}\n`,
280
+ exitCode: 0,
281
+ };
282
+ }
283
+
284
+ // -e 'expr'
285
+ const eIdx = args.findIndex((a) => a === "-e" || a === "--eval");
286
+ if (eIdx !== -1) {
287
+ const expr = args[eIdx + 1];
288
+ if (!expr)
289
+ return { stderr: "node: -e requires an argument\n", exitCode: 1 };
290
+ const { stdout, stderr, exitCode } = runJs(expr);
291
+ return {
292
+ stdout: stdout || undefined,
293
+ stderr: stderr || undefined,
294
+ exitCode,
295
+ };
296
+ }
297
+
298
+ // -p 'expr' — print mode
299
+ const pIdx = args.findIndex((a) => a === "-p" || a === "--print");
300
+ if (pIdx !== -1) {
301
+ const expr = args[pIdx + 1];
302
+ if (!expr)
303
+ return { stderr: "node: -p requires an argument\n", exitCode: 1 };
304
+ const { stdout, stderr, exitCode } = runJs(expr);
305
+ return {
306
+ stdout: stdout || (exitCode === 0 ? "\n" : undefined),
307
+ stderr: stderr || undefined,
308
+ exitCode,
309
+ };
310
+ }
311
+
312
+ // node <file>
313
+ const file = args.find((a) => !a.startsWith("-"));
314
+ if (file) {
315
+ const filePath = resolvePath(cwd, file);
316
+ if (!shell.vfs.exists(filePath)) {
317
+ return {
318
+ stderr: `node: cannot open file '${file}': No such file or directory\n`,
319
+ exitCode: 1,
320
+ };
321
+ }
322
+ const code = shell.vfs.readFile(filePath);
323
+ const { stdout, stderr, exitCode } = runJsFile(code);
324
+ return {
325
+ stdout: stdout || undefined,
326
+ stderr: stderr || undefined,
327
+ exitCode,
328
+ };
329
+ }
330
+
331
+ // No args — REPL hint
332
+ return {
333
+ stdout: [
334
+ `Welcome to Node.js ${VIRTUAL_VERSION}.`,
335
+ `Type ".exit" to exit the REPL.`,
336
+ `> `,
337
+ ].join("\n"),
338
+ exitCode: 0,
339
+ };
340
+ },
341
+ };