typescript-virtual-container 1.2.5 → 1.2.6

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 (245) hide show
  1. package/README.md +387 -193
  2. package/benchmark-results.txt +21 -21
  3. package/dist/SSHMimic/exec.js +2 -2
  4. package/dist/SSHMimic/executor.d.ts +6 -7
  5. package/dist/SSHMimic/executor.d.ts.map +1 -1
  6. package/dist/SSHMimic/executor.js +77 -60
  7. package/dist/SSHMimic/index.d.ts.map +1 -1
  8. package/dist/SSHMimic/index.js +6 -20
  9. package/dist/SSHMimic/sftp.d.ts.map +1 -1
  10. package/dist/SSHMimic/sftp.js +14 -0
  11. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  12. package/dist/VirtualFileSystem/index.js +13 -36
  13. package/dist/VirtualShell/shell.d.ts.map +1 -1
  14. package/dist/VirtualShell/shell.js +19 -2
  15. package/dist/VirtualShell/shellParser.d.ts +20 -2
  16. package/dist/VirtualShell/shellParser.d.ts.map +1 -1
  17. package/dist/VirtualShell/shellParser.js +229 -120
  18. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  19. package/dist/commands/adduser.d.ts.map +1 -1
  20. package/dist/commands/adduser.js +2 -0
  21. package/dist/commands/awk.d.ts +3 -0
  22. package/dist/commands/awk.d.ts.map +1 -0
  23. package/dist/commands/awk.js +29 -0
  24. package/dist/commands/base64.d.ts +3 -0
  25. package/dist/commands/base64.d.ts.map +1 -0
  26. package/dist/commands/base64.js +20 -0
  27. package/dist/commands/cat.d.ts.map +1 -1
  28. package/dist/commands/cat.js +2 -0
  29. package/dist/commands/cd.d.ts.map +1 -1
  30. package/dist/commands/cd.js +2 -0
  31. package/dist/commands/chmod.d.ts.map +1 -1
  32. package/dist/commands/chmod.js +2 -0
  33. package/dist/commands/clear.d.ts.map +1 -1
  34. package/dist/commands/clear.js +4 -1
  35. package/dist/commands/cp.d.ts.map +1 -1
  36. package/dist/commands/cp.js +2 -0
  37. package/dist/commands/curl.d.ts.map +1 -1
  38. package/dist/commands/curl.js +2 -0
  39. package/dist/commands/cut.d.ts +3 -0
  40. package/dist/commands/cut.d.ts.map +1 -0
  41. package/dist/commands/cut.js +27 -0
  42. package/dist/commands/date.d.ts +3 -0
  43. package/dist/commands/date.d.ts.map +1 -0
  44. package/dist/commands/date.js +22 -0
  45. package/dist/commands/deluser.d.ts.map +1 -1
  46. package/dist/commands/deluser.js +2 -0
  47. package/dist/commands/df.d.ts +3 -0
  48. package/dist/commands/df.d.ts.map +1 -0
  49. package/dist/commands/df.js +16 -0
  50. package/dist/commands/diff.d.ts +3 -0
  51. package/dist/commands/diff.d.ts.map +1 -0
  52. package/dist/commands/diff.js +40 -0
  53. package/dist/commands/du.d.ts +3 -0
  54. package/dist/commands/du.d.ts.map +1 -0
  55. package/dist/commands/du.js +39 -0
  56. package/dist/commands/echo.d.ts.map +1 -1
  57. package/dist/commands/echo.js +2 -0
  58. package/dist/commands/env.d.ts.map +1 -1
  59. package/dist/commands/env.js +6 -14
  60. package/dist/commands/export.d.ts.map +1 -1
  61. package/dist/commands/export.js +11 -21
  62. package/dist/commands/find.d.ts.map +1 -1
  63. package/dist/commands/find.js +2 -0
  64. package/dist/commands/grep.d.ts.map +1 -1
  65. package/dist/commands/grep.js +4 -7
  66. package/dist/commands/groups.d.ts +3 -0
  67. package/dist/commands/groups.d.ts.map +1 -0
  68. package/dist/commands/groups.js +12 -0
  69. package/dist/commands/gzip.d.ts +4 -0
  70. package/dist/commands/gzip.d.ts.map +1 -0
  71. package/dist/commands/gzip.js +40 -0
  72. package/dist/commands/head.d.ts.map +1 -1
  73. package/dist/commands/head.js +2 -0
  74. package/dist/commands/help.d.ts +1 -1
  75. package/dist/commands/help.d.ts.map +1 -1
  76. package/dist/commands/help.js +75 -3
  77. package/dist/commands/hostname.d.ts.map +1 -1
  78. package/dist/commands/hostname.js +2 -0
  79. package/dist/commands/htop.d.ts.map +1 -1
  80. package/dist/commands/htop.js +2 -0
  81. package/dist/commands/id.d.ts +3 -0
  82. package/dist/commands/id.d.ts.map +1 -0
  83. package/dist/commands/id.js +14 -0
  84. package/dist/commands/index.d.ts +5 -2
  85. package/dist/commands/index.d.ts.map +1 -1
  86. package/dist/commands/index.js +89 -62
  87. package/dist/commands/kill.d.ts +3 -0
  88. package/dist/commands/kill.d.ts.map +1 -0
  89. package/dist/commands/kill.js +13 -0
  90. package/dist/commands/ln.d.ts.map +1 -1
  91. package/dist/commands/ln.js +2 -0
  92. package/dist/commands/ls.d.ts.map +1 -1
  93. package/dist/commands/ls.js +2 -0
  94. package/dist/commands/mkdir.d.ts.map +1 -1
  95. package/dist/commands/mkdir.js +2 -0
  96. package/dist/commands/mv.d.ts.map +1 -1
  97. package/dist/commands/mv.js +2 -0
  98. package/dist/commands/nano.d.ts.map +1 -1
  99. package/dist/commands/nano.js +2 -0
  100. package/dist/commands/neofetch.d.ts.map +1 -1
  101. package/dist/commands/neofetch.js +2 -0
  102. package/dist/commands/passwd.d.ts.map +1 -1
  103. package/dist/commands/passwd.js +2 -0
  104. package/dist/commands/ping.d.ts +3 -0
  105. package/dist/commands/ping.d.ts.map +1 -0
  106. package/dist/commands/ping.js +18 -0
  107. package/dist/commands/ps.d.ts +3 -0
  108. package/dist/commands/ps.d.ts.map +1 -0
  109. package/dist/commands/ps.js +17 -0
  110. package/dist/commands/pwd.d.ts.map +1 -1
  111. package/dist/commands/pwd.js +2 -0
  112. package/dist/commands/rm.d.ts.map +1 -1
  113. package/dist/commands/rm.js +2 -0
  114. package/dist/commands/sed.d.ts +3 -0
  115. package/dist/commands/sed.d.ts.map +1 -0
  116. package/dist/commands/sed.js +47 -0
  117. package/dist/commands/set.d.ts +3 -0
  118. package/dist/commands/set.d.ts.map +1 -1
  119. package/dist/commands/set.js +19 -46
  120. package/dist/commands/sh.d.ts +0 -1
  121. package/dist/commands/sh.d.ts.map +1 -1
  122. package/dist/commands/sh.js +228 -35
  123. package/dist/commands/sleep.d.ts +3 -0
  124. package/dist/commands/sleep.d.ts.map +1 -0
  125. package/dist/commands/sleep.js +13 -0
  126. package/dist/commands/sort.d.ts +3 -0
  127. package/dist/commands/sort.d.ts.map +1 -0
  128. package/dist/commands/sort.js +37 -0
  129. package/dist/commands/su.d.ts.map +1 -1
  130. package/dist/commands/su.js +2 -0
  131. package/dist/commands/sudo.d.ts.map +1 -1
  132. package/dist/commands/sudo.js +2 -0
  133. package/dist/commands/tail.d.ts.map +1 -1
  134. package/dist/commands/tail.js +2 -0
  135. package/dist/commands/tar.d.ts +3 -0
  136. package/dist/commands/tar.d.ts.map +1 -0
  137. package/dist/commands/tar.js +64 -0
  138. package/dist/commands/tee.d.ts +3 -0
  139. package/dist/commands/tee.d.ts.map +1 -0
  140. package/dist/commands/tee.js +29 -0
  141. package/dist/commands/touch.d.ts.map +1 -1
  142. package/dist/commands/touch.js +2 -0
  143. package/dist/commands/tr.d.ts +3 -0
  144. package/dist/commands/tr.d.ts.map +1 -0
  145. package/dist/commands/tr.js +24 -0
  146. package/dist/commands/tree.d.ts.map +1 -1
  147. package/dist/commands/tree.js +2 -0
  148. package/dist/commands/uname.d.ts +3 -0
  149. package/dist/commands/uname.d.ts.map +1 -0
  150. package/dist/commands/uname.js +21 -0
  151. package/dist/commands/uniq.d.ts +3 -0
  152. package/dist/commands/uniq.d.ts.map +1 -0
  153. package/dist/commands/uniq.js +33 -0
  154. package/dist/commands/unset.d.ts.map +1 -1
  155. package/dist/commands/unset.js +6 -10
  156. package/dist/commands/wc.d.ts.map +1 -1
  157. package/dist/commands/wc.js +2 -0
  158. package/dist/commands/wget.d.ts.map +1 -1
  159. package/dist/commands/wget.js +2 -0
  160. package/dist/commands/who.d.ts.map +1 -1
  161. package/dist/commands/who.js +2 -0
  162. package/dist/commands/whoami.d.ts.map +1 -1
  163. package/dist/commands/whoami.js +2 -0
  164. package/dist/commands/xargs.d.ts +3 -0
  165. package/dist/commands/xargs.d.ts.map +1 -0
  166. package/dist/commands/xargs.js +16 -0
  167. package/dist/types/commands.d.ts +13 -0
  168. package/dist/types/commands.d.ts.map +1 -1
  169. package/dist/types/pipeline.d.ts +20 -0
  170. package/dist/types/pipeline.d.ts.map +1 -1
  171. package/package.json +1 -1
  172. package/src/SSHMimic/exec.ts +2 -2
  173. package/src/SSHMimic/executor.ts +95 -98
  174. package/src/SSHMimic/index.ts +15 -49
  175. package/src/SSHMimic/sftp.ts +15 -0
  176. package/src/VirtualFileSystem/index.ts +27 -75
  177. package/src/VirtualShell/shell.ts +19 -2
  178. package/src/VirtualShell/shellParser.ts +202 -168
  179. package/src/VirtualUserManager/index.ts +2 -7
  180. package/src/commands/adduser.ts +2 -0
  181. package/src/commands/awk.ts +30 -0
  182. package/src/commands/base64.ts +18 -0
  183. package/src/commands/cat.ts +2 -0
  184. package/src/commands/cd.ts +2 -0
  185. package/src/commands/chmod.ts +2 -0
  186. package/src/commands/clear.ts +4 -1
  187. package/src/commands/cp.ts +2 -0
  188. package/src/commands/curl.ts +2 -0
  189. package/src/commands/cut.ts +29 -0
  190. package/src/commands/date.ts +24 -0
  191. package/src/commands/deluser.ts +2 -0
  192. package/src/commands/df.ts +18 -0
  193. package/src/commands/diff.ts +29 -0
  194. package/src/commands/du.ts +39 -0
  195. package/src/commands/echo.ts +2 -0
  196. package/src/commands/env.ts +6 -16
  197. package/src/commands/export.ts +11 -24
  198. package/src/commands/find.ts +2 -0
  199. package/src/commands/grep.ts +4 -7
  200. package/src/commands/groups.ts +14 -0
  201. package/src/commands/gzip.ts +31 -0
  202. package/src/commands/head.ts +2 -0
  203. package/src/commands/help.ts +81 -3
  204. package/src/commands/hostname.ts +2 -0
  205. package/src/commands/htop.ts +2 -0
  206. package/src/commands/id.ts +16 -0
  207. package/src/commands/index.ts +98 -99
  208. package/src/commands/kill.ts +14 -0
  209. package/src/commands/ln.ts +2 -0
  210. package/src/commands/ls.ts +2 -0
  211. package/src/commands/mkdir.ts +2 -0
  212. package/src/commands/mv.ts +2 -0
  213. package/src/commands/nano.ts +2 -0
  214. package/src/commands/neofetch.ts +2 -0
  215. package/src/commands/passwd.ts +2 -0
  216. package/src/commands/ping.ts +20 -0
  217. package/src/commands/ps.ts +19 -0
  218. package/src/commands/pwd.ts +2 -0
  219. package/src/commands/rm.ts +2 -0
  220. package/src/commands/sed.ts +45 -0
  221. package/src/commands/set.ts +19 -50
  222. package/src/commands/sh.ts +192 -43
  223. package/src/commands/sleep.ts +14 -0
  224. package/src/commands/sort.ts +37 -0
  225. package/src/commands/su.ts +2 -0
  226. package/src/commands/sudo.ts +2 -0
  227. package/src/commands/tail.ts +2 -0
  228. package/src/commands/tar.ts +58 -0
  229. package/src/commands/tee.ts +25 -0
  230. package/src/commands/touch.ts +2 -0
  231. package/src/commands/tr.ts +24 -0
  232. package/src/commands/tree.ts +2 -0
  233. package/src/commands/uname.ts +20 -0
  234. package/src/commands/uniq.ts +28 -0
  235. package/src/commands/unset.ts +5 -12
  236. package/src/commands/wc.ts +2 -0
  237. package/src/commands/wget.ts +2 -0
  238. package/src/commands/who.ts +2 -0
  239. package/src/commands/whoami.ts +2 -0
  240. package/src/commands/xargs.ts +17 -0
  241. package/src/types/commands.ts +14 -0
  242. package/src/types/pipeline.ts +23 -0
  243. package/standalone.js +92 -64
  244. package/standalone.js.map +4 -4
  245. package/tests/users.test.ts +5 -34
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const dateCommand: ShellModule;
3
+ //# sourceMappingURL=date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/commands/date.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAqBzB,CAAC"}
@@ -0,0 +1,22 @@
1
+ export const dateCommand = {
2
+ name: "date",
3
+ description: "Print current date and time",
4
+ category: "system",
5
+ params: ["[+format]"],
6
+ run: ({ args }) => {
7
+ const now = new Date();
8
+ const fmt = args[0];
9
+ if (fmt?.startsWith("+")) {
10
+ const f = fmt.slice(1)
11
+ .replace("%Y", String(now.getFullYear()))
12
+ .replace("%m", String(now.getMonth() + 1).padStart(2, "0"))
13
+ .replace("%d", String(now.getDate()).padStart(2, "0"))
14
+ .replace("%H", String(now.getHours()).padStart(2, "0"))
15
+ .replace("%M", String(now.getMinutes()).padStart(2, "0"))
16
+ .replace("%S", String(now.getSeconds()).padStart(2, "0"))
17
+ .replace("%s", String(Math.floor(now.getTime() / 1000)));
18
+ return { stdout: f, exitCode: 0 };
19
+ }
20
+ return { stdout: now.toString(), exitCode: 0 };
21
+ },
22
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"deluser.d.ts","sourceRoot":"","sources":["../../src/commands/deluser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,cAAc,EAAE,WAgB5B,CAAC"}
1
+ {"version":3,"file":"deluser.d.ts","sourceRoot":"","sources":["../../src/commands/deluser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,cAAc,EAAE,WAkB5B,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export const deluserCommand = {
2
2
  name: "deluser",
3
+ description: "Delete a user",
4
+ category: "users",
3
5
  params: ["<username>"],
4
6
  run: async ({ authUser, args, shell }) => {
5
7
  if (authUser !== "root") {
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const dfCommand: ShellModule;
3
+ //# sourceMappingURL=df.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"df.d.ts","sourceRoot":"","sources":["../../src/commands/df.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,SAAS,EAAE,WAevB,CAAC"}
@@ -0,0 +1,16 @@
1
+ export const dfCommand = {
2
+ name: "df",
3
+ description: "Report filesystem disk space usage",
4
+ category: "system",
5
+ params: ["[-h]"],
6
+ run: ({ shell }) => {
7
+ const bytes = shell.vfs.getUsageBytes();
8
+ const used = (bytes / 1024).toFixed(0);
9
+ const total = "1048576"; // 1GB virtual
10
+ const avail = String(Number(total) - Number(used));
11
+ const pct = Math.round((Number(used) / Number(total)) * 100);
12
+ const hdr = "Filesystem 1K-blocks Used Available Use% Mounted on";
13
+ const row = `virtual-fs ${total.padStart(9)} ${used.padStart(7)} ${avail.padStart(9)} ${pct}% /`;
14
+ return { stdout: `${hdr}\n${row}`, exitCode: 0 };
15
+ },
16
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const diffCommand: ShellModule;
3
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WAyBzB,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { resolvePath } from "./helpers";
2
+ export const diffCommand = {
3
+ name: "diff",
4
+ description: "Compare files line by line",
5
+ category: "text",
6
+ params: ["<file1> <file2>"],
7
+ run: ({ shell, cwd, args }) => {
8
+ const [f1, f2] = args;
9
+ if (!f1 || !f2)
10
+ return { stderr: "diff: missing operand", exitCode: 1 };
11
+ const p1 = resolvePath(cwd, f1);
12
+ const p2 = resolvePath(cwd, f2);
13
+ let a, b;
14
+ try {
15
+ a = shell.vfs.readFile(p1).split("\n");
16
+ }
17
+ catch {
18
+ return { stderr: `diff: ${f1}: No such file or directory`, exitCode: 2 };
19
+ }
20
+ try {
21
+ b = shell.vfs.readFile(p2).split("\n");
22
+ }
23
+ catch {
24
+ return { stderr: `diff: ${f2}: No such file or directory`, exitCode: 2 };
25
+ }
26
+ const out = [];
27
+ const max = Math.max(a.length, b.length);
28
+ for (let i = 0; i < max; i++) {
29
+ const la = a[i];
30
+ const lb = b[i];
31
+ if (la !== lb) {
32
+ if (la !== undefined)
33
+ out.push(`< ${la}`);
34
+ if (lb !== undefined)
35
+ out.push(`> ${lb}`);
36
+ }
37
+ }
38
+ return { stdout: out.join("\n"), exitCode: out.length > 0 ? 1 : 0 };
39
+ },
40
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const duCommand: ShellModule;
3
+ //# sourceMappingURL=du.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"du.d.ts","sourceRoot":"","sources":["../../src/commands/du.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,SAAS,EAAE,WAkCvB,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { ifFlag } from "./command-helpers";
2
+ import { resolvePath } from "./helpers";
3
+ export const duCommand = {
4
+ name: "du",
5
+ description: "Estimate file space usage",
6
+ category: "system",
7
+ params: ["[-h] [-s] [path]"],
8
+ run: ({ shell, cwd, args }) => {
9
+ const human = ifFlag(args, ["-h"]);
10
+ const summary = ifFlag(args, ["-s"]);
11
+ const target = args.find((a) => !a.startsWith("-")) ?? ".";
12
+ const p = resolvePath(cwd, target);
13
+ const fmt = (b) => human ? `${(b / 1024).toFixed(1)}K` : String(Math.ceil(b / 1024));
14
+ if (!shell.vfs.exists(p))
15
+ return { stderr: `du: ${target}: No such file or directory`, exitCode: 1 };
16
+ if (summary || shell.vfs.stat(p).type === "file") {
17
+ return { stdout: `${fmt(shell.vfs.getUsageBytes(p))}\t${target}`, exitCode: 0 };
18
+ }
19
+ const lines = [];
20
+ const walk = (dir, rel) => {
21
+ let total = 0;
22
+ for (const e of shell.vfs.list(dir)) {
23
+ const full = `${dir}/${e}`, r = `${rel}/${e}`;
24
+ const st = shell.vfs.stat(full);
25
+ if (st.type === "directory")
26
+ total += walk(full, r);
27
+ else {
28
+ total += st.size;
29
+ if (!summary)
30
+ lines.push(`${fmt(st.size)}\t${r}`);
31
+ }
32
+ }
33
+ lines.push(`${fmt(total)}\t${rel}`);
34
+ return total;
35
+ };
36
+ walk(p, target);
37
+ return { stdout: lines.join("\n"), exitCode: 0 };
38
+ },
39
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"echo.d.ts","sourceRoot":"","sources":["../../src/commands/echo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUrD,eAAO,MAAM,WAAW,EAAE,WAgBzB,CAAC"}
1
+ {"version":3,"file":"echo.d.ts","sourceRoot":"","sources":["../../src/commands/echo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUrD,eAAO,MAAM,WAAW,EAAE,WAkBzB,CAAC"}
@@ -7,6 +7,8 @@ function expandEnvVars(input, env) {
7
7
  }
8
8
  export const echoCommand = {
9
9
  name: "echo",
10
+ description: "Display text",
11
+ category: "shell",
10
12
  params: ["[options] [text...]"],
11
13
  run: ({ args, authUser, stdin }) => {
12
14
  const { flags, positionals } = parseArgs(args, { flags: ["-n"] });
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,UAAU,EAAE,WAkBxB,CAAC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,UAAU,EAAE,WASxB,CAAC"}
@@ -1,18 +1,10 @@
1
- import { getAllEnvVars } from "./set";
2
1
  export const envCommand = {
3
2
  name: "env",
4
- params: ["[VAR=value...] [command]"],
5
- run: ({ authUser }) => {
6
- // For now, just display all environment variables
7
- // In a full implementation, this would also handle running commands with modified env
8
- const allVars = getAllEnvVars(authUser);
9
- const envVarsOutput = Object.entries(allVars)
10
- .map(([key, value]) => `${key}=${value}`)
11
- .sort()
12
- .join("\n");
13
- return {
14
- stdout: envVarsOutput,
15
- exitCode: 0,
16
- };
3
+ description: "Print environment variables",
4
+ category: "shell",
5
+ params: [],
6
+ run: ({ env, authUser }) => {
7
+ const vars = { ...env.vars, USER: authUser, HOME: `/home/${authUser}` };
8
+ return { stdout: Object.entries(vars).map(([k, v]) => `${k}=${v}`).join("\n"), exitCode: 0 };
17
9
  },
18
10
  };
@@ -1 +1 @@
1
- {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,aAAa,EAAE,WAiC3B,CAAC"}
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,aAAa,EAAE,WAsB3B,CAAC"}
@@ -1,32 +1,22 @@
1
- import { getArg } from "./command-helpers";
2
- import { getEnvVar, setEnvVar } from "./set";
3
1
  export const exportCommand = {
4
2
  name: "export",
3
+ description: "Set shell environment variable",
4
+ category: "shell",
5
5
  params: ["[VAR=value]"],
6
- run: ({ args }) => {
7
- // export VAR=value or export VAR (to make it available to child processes)
6
+ run: ({ args, env }) => {
8
7
  if (args.length === 0) {
9
- // List all exported variables
10
- return {
11
- stdout: "# export command - sets variables for child processes",
12
- exitCode: 0,
13
- };
8
+ const out = Object.entries(env.vars).map(([k, v]) => `declare -x ${k}="${v}"`).join("\n");
9
+ return { stdout: out, exitCode: 0 };
14
10
  }
15
- // Parse VAR=value format
16
- for (let index = 0;; index += 1) {
17
- const arg = getArg(args, index);
18
- if (!arg) {
19
- break;
20
- }
11
+ for (const arg of args) {
21
12
  if (arg.includes("=")) {
22
- const [varName, varValue] = arg.split("=", 2);
23
- if (varName && varValue !== undefined) {
24
- setEnvVar(varName, varValue);
25
- }
13
+ const eq = arg.indexOf("=");
14
+ const name = arg.slice(0, eq);
15
+ const value = arg.slice(eq + 1);
16
+ env.vars[name] = value;
26
17
  }
27
18
  else {
28
- // export VAR_NAME makes it available but we just set it
29
- setEnvVar(arg, getEnvVar(arg) || "");
19
+ // mark existing as exported (already is)
30
20
  }
31
21
  }
32
22
  return { exitCode: 0 };
@@ -1 +1 @@
1
- {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../src/commands/find.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAwDzB,CAAC"}
1
+ {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../src/commands/find.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WA0DzB,CAAC"}
@@ -2,6 +2,8 @@ import { getFlag } from "./command-helpers";
2
2
  import { assertPathAccess, resolvePath } from "./helpers";
3
3
  export const findCommand = {
4
4
  name: "find",
5
+ description: "Search for files",
6
+ category: "files",
5
7
  params: ["[path] [-name <pattern>] [-type f|d]"],
6
8
  run: ({ authUser, shell, cwd, args }) => {
7
9
  const namePattern = getFlag(args, ["-name"]);
@@ -1 +1 @@
1
- {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/commands/grep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WA2FzB,CAAC"}
1
+ {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/commands/grep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAwFzB,CAAC"}
@@ -2,11 +2,11 @@ import { parseArgs } from "./command-helpers";
2
2
  import { assertPathAccess, resolvePath } from "./helpers";
3
3
  export const grepCommand = {
4
4
  name: "grep",
5
+ description: "Search text patterns",
6
+ category: "text",
5
7
  params: ["[-i] [-v] [-n] [-r] <pattern> [file...]"],
6
8
  run: ({ authUser, shell, cwd, args, stdin }) => {
7
- const { flags, positionals } = parseArgs(args, {
8
- flags: ["-i", "-v", "-n", "-r"],
9
- });
9
+ const { flags, positionals } = parseArgs(args, { flags: ["-i", "-v", "-n", "-r"] });
10
10
  const caseInsensitive = flags.has("-i");
11
11
  const invertMatch = flags.has("-v");
12
12
  const showLineNumbers = flags.has("-n");
@@ -80,10 +80,7 @@ export const grepCommand = {
80
80
  results.push(...matchLines(content, prefix));
81
81
  }
82
82
  catch {
83
- return {
84
- stderr: `grep: ${file}: No such file or directory`,
85
- exitCode: 1,
86
- };
83
+ return { stderr: `grep: ${file}: No such file or directory`, exitCode: 1 };
87
84
  }
88
85
  }
89
86
  }
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const groupsCommand: ShellModule;
3
+ //# sourceMappingURL=groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/commands/groups.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,aAAa,EAAE,WAW3B,CAAC"}
@@ -0,0 +1,12 @@
1
+ export const groupsCommand = {
2
+ name: "groups",
3
+ description: "Print group memberships",
4
+ category: "system",
5
+ params: ["[user]"],
6
+ run: ({ authUser, shell, args }) => {
7
+ const target = args[0] ?? authUser;
8
+ const isSudo = shell.users.isSudoer(target);
9
+ const grps = isSudo ? `${target} sudo root` : target;
10
+ return { stdout: grps, exitCode: 0 };
11
+ },
12
+ };
@@ -0,0 +1,4 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const gzipCommand: ShellModule;
3
+ export declare const gunzipCommand: ShellModule;
4
+ //# sourceMappingURL=gzip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gzip.d.ts","sourceRoot":"","sources":["../../src/commands/gzip.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WAYzB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAa3B,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { resolvePath } from "./helpers";
2
+ export const gzipCommand = {
3
+ name: "gzip",
4
+ description: "Compress files",
5
+ category: "archive",
6
+ params: ["<file>"],
7
+ run: ({ shell, cwd, args }) => {
8
+ const file = args[0];
9
+ if (!file)
10
+ return { stderr: "gzip: no file specified", exitCode: 1 };
11
+ const p = resolvePath(cwd, file);
12
+ try {
13
+ shell.vfs.compressFile(p);
14
+ return { exitCode: 0 };
15
+ }
16
+ catch {
17
+ return { stderr: `gzip: ${file}: No such file or directory`, exitCode: 1 };
18
+ }
19
+ },
20
+ };
21
+ export const gunzipCommand = {
22
+ name: "gunzip",
23
+ description: "Decompress files",
24
+ category: "archive",
25
+ params: ["<file>"],
26
+ aliases: ["zcat"],
27
+ run: ({ shell, cwd, args }) => {
28
+ const file = args[0];
29
+ if (!file)
30
+ return { stderr: "gunzip: no file specified", exitCode: 1 };
31
+ const p = resolvePath(cwd, file);
32
+ try {
33
+ shell.vfs.decompressFile(p);
34
+ return { exitCode: 0 };
35
+ }
36
+ catch {
37
+ return { stderr: `gunzip: ${file}: No such file or directory`, exitCode: 1 };
38
+ }
39
+ },
40
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../src/commands/head.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WA8BzB,CAAC"}
1
+ {"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../src/commands/head.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAgCzB,CAAC"}
@@ -2,6 +2,8 @@ import { getFlag } from "./command-helpers";
2
2
  import { assertPathAccess, resolvePath } from "./helpers";
3
3
  export const headCommand = {
4
4
  name: "head",
5
+ description: "Output first lines",
6
+ category: "text",
5
7
  params: ["[-n <lines>] [file...]"],
6
8
  run: ({ authUser, shell, cwd, args, stdin }) => {
7
9
  const nArg = getFlag(args, ["-n"]);
@@ -1,3 +1,3 @@
1
1
  import type { ShellModule } from "../types/commands";
2
- export declare function createHelpCommand(getNames: () => string[]): ShellModule;
2
+ export declare function createHelpCommand(_getNames: () => string[]): ShellModule;
3
3
  //# sourceMappingURL=help.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/commands/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,MAAM,EAAE,GAAG,WAAW,CAMvE"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/commands/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAoBrD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,MAAM,EAAE,GAAG,WAAW,CAkExE"}
@@ -1,7 +1,79 @@
1
- export function createHelpCommand(getNames) {
1
+ import { getCommandModulesPublic } from "./index";
2
+ const CATEGORY_ORDER = ["navigation", "files", "text", "archive", "system", "network", "shell", "users", "misc"];
3
+ const CATEGORY_LABELS = {
4
+ navigation: "Navigation",
5
+ files: "Files & Filesystem",
6
+ text: "Text Processing",
7
+ archive: "Archive & Compression",
8
+ system: "System",
9
+ network: "Network",
10
+ shell: "Shell",
11
+ users: "Users & Permissions",
12
+ misc: "Miscellaneous",
13
+ };
14
+ function padRight(s, n) {
15
+ return s.length >= n ? s : s + " ".repeat(n - s.length);
16
+ }
17
+ export function createHelpCommand(_getNames) {
2
18
  return {
3
19
  name: "help",
4
- params: [],
5
- run: () => ({ stdout: `Builtins: ${getNames().join(" ")}`, exitCode: 0 }),
20
+ description: "Display this help message",
21
+ category: "shell",
22
+ params: ["[command]"],
23
+ run: ({ args }) => {
24
+ const modules = getCommandModulesPublic();
25
+ // help <command>
26
+ if (args[0]) {
27
+ const mod = modules.find((m) => m.name === args[0] || m.aliases?.includes(args[0]));
28
+ if (!mod)
29
+ return { stderr: `help: no help for '${args[0]}'`, exitCode: 1 };
30
+ const aliases = mod.aliases?.length ? ` aliases: ${mod.aliases.join(", ")}\n` : "";
31
+ const params = mod.params.map((p) => ` ${mod.name} ${p}`).join("\n");
32
+ return {
33
+ stdout: [
34
+ `\x1b[1m${mod.name}\x1b[0m — ${mod.description ?? "no description"}`,
35
+ aliases,
36
+ "Usage:",
37
+ params || ` ${mod.name}`,
38
+ ].filter(Boolean).join("\n"),
39
+ exitCode: 0,
40
+ };
41
+ }
42
+ // Full help — grouped by category
43
+ const grouped = {};
44
+ for (const mod of modules) {
45
+ const cat = mod.category ?? "misc";
46
+ if (!grouped[cat])
47
+ grouped[cat] = [];
48
+ grouped[cat].push(mod);
49
+ }
50
+ const lines = [];
51
+ lines.push("\x1b[1mAvailable commands\x1b[0m");
52
+ lines.push("");
53
+ const cats = [
54
+ ...CATEGORY_ORDER.filter((c) => grouped[c]),
55
+ ...Object.keys(grouped).filter((c) => !CATEGORY_ORDER.includes(c)),
56
+ ];
57
+ for (const cat of cats) {
58
+ const mods = grouped[cat];
59
+ if (!mods || mods.length === 0)
60
+ continue;
61
+ lines.push(`\x1b[33m${CATEGORY_LABELS[cat] ?? cat}\x1b[0m`);
62
+ // Two-column layout
63
+ const sorted = [...mods].sort((a, b) => a.name.localeCompare(b.name));
64
+ for (let i = 0; i < sorted.length; i += 2) {
65
+ const left = sorted[i];
66
+ const right = sorted[i + 1];
67
+ const leftStr = ` \x1b[36m${padRight(left.name, 14)}\x1b[0m ${left.description ?? ""}`;
68
+ const rightStr = right
69
+ ? ` \x1b[36m${padRight(right.name, 14)}\x1b[0m ${right.description ?? ""}`
70
+ : "";
71
+ lines.push(rightStr ? `${leftStr.padEnd(44)}${rightStr}` : leftStr);
72
+ }
73
+ lines.push("");
74
+ }
75
+ lines.push("Type \x1b[1mhelp <command>\x1b[0m for usage details.");
76
+ return { stdout: lines.join("\n"), exitCode: 0 };
77
+ },
6
78
  };
7
79
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hostname.d.ts","sourceRoot":"","sources":["../../src/commands/hostname.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,eAAe,EAAE,WAI7B,CAAC"}
1
+ {"version":3,"file":"hostname.d.ts","sourceRoot":"","sources":["../../src/commands/hostname.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,eAAe,EAAE,WAM7B,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export const hostnameCommand = {
2
2
  name: "hostname",
3
+ description: "Print hostname",
4
+ category: "system",
3
5
  params: [],
4
6
  run: ({ hostname }) => ({ stdout: hostname, exitCode: 0 }),
5
7
  };
@@ -1 +1 @@
1
- {"version":3,"file":"htop.d.ts","sourceRoot":"","sources":["../../src/commands/htop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAUzB,CAAC"}
1
+ {"version":3,"file":"htop.d.ts","sourceRoot":"","sources":["../../src/commands/htop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAYzB,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export const htopCommand = {
2
2
  name: "htop",
3
+ description: "System monitor",
4
+ category: "system",
3
5
  params: [],
4
6
  run: ({ mode }) => {
5
7
  if (mode === "exec") {
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const idCommand: ShellModule;
3
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/commands/id.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,SAAS,EAAE,WAavB,CAAC"}
@@ -0,0 +1,14 @@
1
+ export const idCommand = {
2
+ name: "id",
3
+ description: "Print user identity",
4
+ category: "system",
5
+ params: ["[user]"],
6
+ run: ({ authUser, shell, args }) => {
7
+ const target = args[0] ?? authUser;
8
+ const uid = target === "root" ? 0 : 1000;
9
+ const gid = uid;
10
+ const isSudo = shell.users.isSudoer(target);
11
+ const groups = isSudo ? `${gid}(${target}),0(root)` : `${gid}(${target})`;
12
+ return { stdout: `uid=${uid}(${target}) gid=${gid}(${target}) groups=${groups}`, exitCode: 0 };
13
+ },
14
+ };
@@ -1,8 +1,11 @@
1
+ /** biome-ignore-all lint/style/useNamingConvention: ENV VARIABLES */
1
2
  import type { VirtualShell } from "../VirtualShell";
2
- import type { CommandContext, CommandMode, CommandResult, ShellModule } from "../types/commands";
3
+ import type { CommandContext, CommandMode, CommandResult, ShellEnv, ShellModule } from "../types/commands";
3
4
  export declare function registerCommand(module: ShellModule): void;
4
5
  export declare function createCustomCommand(name: string, params: string[], run: (ctx: CommandContext) => CommandResult | Promise<CommandResult>): ShellModule;
5
6
  export declare function getCommandNames(): string[];
7
+ export declare function getCommandModulesPublic(): ShellModule[];
6
8
  export declare function resolveModule(name: string): ShellModule | undefined;
7
- export declare function runCommand(rawInput: string, authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, stdin?: string): Promise<CommandResult>;
9
+ export declare function makeDefaultEnv(authUser: string, hostname: string): ShellEnv;
10
+ export declare function runCommand(rawInput: string, authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, stdin?: string, env?: ShellEnv): Promise<CommandResult>;
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EACX,cAAc,EACd,WAAW,EACX,aAAa,EACb,WAAW,EACX,MAAM,mBAAmB,CAAC;AA0G3B,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAgBzD;AAED,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,GAAG,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GAClE,WAAW,CAEb;AAED,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAG1C;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAGnE;AAgDD,wBAAsB,UAAU,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,CAAC,CAqDxB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EACX,cAAc,EACd,WAAW,EACX,aAAa,EACb,QAAQ,EACR,WAAW,EACX,MAAM,mBAAmB,CAAC;AA8G3B,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAYzD;AAED,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,GAAG,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GAClE,WAAW,CAEb;AAED,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAG1C;AAED,wBAAgB,uBAAuB,IAAI,WAAW,EAAE,CAEvD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAGnE;AA8BD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAc3E;AAED,wBAAsB,UAAU,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,QAAQ,GACZ,OAAO,CAAC,aAAa,CAAC,CA+CxB"}