typescript-virtual-container 1.5.11 → 1.6.1

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 (132) hide show
  1. package/README.md +236 -456
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/Honeypot/index.d.ts +9 -0
  4. package/dist/Honeypot/index.js +57 -0
  5. package/dist/SSHMimic/exec.d.ts +4 -0
  6. package/dist/SSHMimic/exec.js +4 -0
  7. package/dist/SSHMimic/executor.d.ts +10 -1
  8. package/dist/SSHMimic/executor.js +18 -8
  9. package/dist/SSHMimic/hostKey.d.ts +5 -0
  10. package/dist/SSHMimic/hostKey.js +5 -0
  11. package/dist/SSHMimic/loginBanner.d.ts +7 -0
  12. package/dist/SSHMimic/loginBanner.js +4 -0
  13. package/dist/SSHMimic/loginFormat.d.ts +4 -0
  14. package/dist/SSHMimic/loginFormat.js +4 -0
  15. package/dist/SSHMimic/prompt.d.ts +9 -0
  16. package/dist/SSHMimic/prompt.js +9 -0
  17. package/dist/SSHMimic/scp.d.ts +18 -0
  18. package/dist/SSHMimic/scp.js +14 -0
  19. package/dist/VirtualFileSystem/binaryPack.d.ts +7 -3
  20. package/dist/VirtualFileSystem/binaryPack.js +32 -10
  21. package/dist/VirtualFileSystem/index.d.ts +29 -0
  22. package/dist/VirtualFileSystem/index.js +126 -5
  23. package/dist/VirtualFileSystem/internalTypes.d.ts +4 -0
  24. package/dist/VirtualFileSystem/journal.d.ts +10 -4
  25. package/dist/VirtualFileSystem/journal.js +12 -2
  26. package/dist/VirtualFileSystem/path.d.ts +23 -1
  27. package/dist/VirtualFileSystem/path.js +23 -3
  28. package/dist/VirtualPackageManager/index.js +1 -1
  29. package/dist/VirtualShell/index.d.ts +3 -0
  30. package/dist/VirtualShell/index.js +12 -3
  31. package/dist/VirtualUserManager/index.d.ts +20 -1
  32. package/dist/VirtualUserManager/index.js +52 -15
  33. package/dist/commands/bc.d.ts +5 -0
  34. package/dist/commands/bc.js +5 -0
  35. package/dist/commands/cat.js +2 -2
  36. package/dist/commands/chgrp.d.ts +7 -0
  37. package/dist/commands/chgrp.js +42 -0
  38. package/dist/commands/chown.d.ts +7 -0
  39. package/dist/commands/chown.js +79 -0
  40. package/dist/commands/cp.js +4 -3
  41. package/dist/commands/dd.d.ts +7 -0
  42. package/dist/commands/dd.js +60 -0
  43. package/dist/commands/declare.js +0 -2
  44. package/dist/commands/expr.d.ts +7 -0
  45. package/dist/commands/expr.js +63 -0
  46. package/dist/commands/fun.d.ts +5 -0
  47. package/dist/commands/fun.js +5 -1
  48. package/dist/commands/help.d.ts +5 -0
  49. package/dist/commands/help.js +5 -0
  50. package/dist/commands/helpers.d.ts +43 -0
  51. package/dist/commands/helpers.js +61 -0
  52. package/dist/commands/id.d.ts +5 -0
  53. package/dist/commands/id.js +5 -0
  54. package/dist/commands/ip.d.ts +1 -0
  55. package/dist/commands/ip.js +50 -23
  56. package/dist/commands/jobs.js +43 -9
  57. package/dist/commands/kill.d.ts +1 -0
  58. package/dist/commands/kill.js +13 -5
  59. package/dist/commands/last.js +1 -1
  60. package/dist/commands/ln.d.ts +5 -0
  61. package/dist/commands/ln.js +5 -0
  62. package/dist/commands/ls.d.ts +5 -0
  63. package/dist/commands/ls.js +19 -4
  64. package/dist/commands/lsb-release.js +1 -1
  65. package/dist/commands/man.d.ts +5 -0
  66. package/dist/commands/man.js +5 -0
  67. package/dist/commands/manuals-bundle.js +242 -0
  68. package/dist/commands/miscutils.d.ts +43 -0
  69. package/dist/commands/miscutils.js +233 -0
  70. package/dist/commands/mkdir.js +3 -2
  71. package/dist/commands/mv.js +4 -3
  72. package/dist/commands/netcat.d.ts +7 -0
  73. package/dist/commands/netcat.js +64 -0
  74. package/dist/commands/nice.d.ts +7 -0
  75. package/dist/commands/nice.js +22 -0
  76. package/dist/commands/nohup.d.ts +7 -0
  77. package/dist/commands/nohup.js +18 -0
  78. package/dist/commands/ping.d.ts +2 -1
  79. package/dist/commands/ping.js +46 -8
  80. package/dist/commands/procUtils.d.ts +13 -0
  81. package/dist/commands/procUtils.js +72 -0
  82. package/dist/commands/pwd.d.ts +5 -0
  83. package/dist/commands/pwd.js +5 -0
  84. package/dist/commands/python.js +0 -4
  85. package/dist/commands/read.js +0 -1
  86. package/dist/commands/registry.d.ts +37 -0
  87. package/dist/commands/registry.js +73 -0
  88. package/dist/commands/rm.js +3 -2
  89. package/dist/commands/runtime.d.ts +47 -1
  90. package/dist/commands/runtime.js +60 -5
  91. package/dist/commands/sh.d.ts +5 -0
  92. package/dist/commands/sh.js +5 -0
  93. package/dist/commands/stat.js +3 -2
  94. package/dist/commands/strace.js +0 -1
  95. package/dist/commands/sysinfo.d.ts +19 -0
  96. package/dist/commands/sysinfo.js +73 -0
  97. package/dist/commands/test.d.ts +5 -0
  98. package/dist/commands/test.js +5 -0
  99. package/dist/commands/textutils.d.ts +25 -0
  100. package/dist/commands/textutils.js +171 -0
  101. package/dist/commands/top.d.ts +7 -0
  102. package/dist/commands/top.js +54 -0
  103. package/dist/commands/touch.js +6 -2
  104. package/dist/commands/tr.d.ts +5 -0
  105. package/dist/commands/tr.js +5 -0
  106. package/dist/commands/w.js +1 -1
  107. package/dist/commands/which.d.ts +5 -0
  108. package/dist/commands/which.js +5 -0
  109. package/dist/index.d.ts +10 -2
  110. package/dist/index.js +4 -0
  111. package/dist/modules/VirtualNetworkManager.d.ts +123 -0
  112. package/dist/modules/VirtualNetworkManager.js +201 -0
  113. package/dist/modules/linuxRootfs.d.ts +4 -3
  114. package/dist/modules/linuxRootfs.js +115 -74
  115. package/dist/modules/neofetch.d.ts +2 -0
  116. package/dist/modules/neofetch.js +3 -2
  117. package/dist/modules/pacmanGame.d.ts +2 -0
  118. package/dist/modules/pacmanGame.js +1 -0
  119. package/dist/modules/shellInteractive.d.ts +2 -0
  120. package/dist/modules/shellInteractive.js +2 -0
  121. package/dist/modules/shellRuntime.d.ts +7 -0
  122. package/dist/modules/shellRuntime.js +6 -0
  123. package/dist/modules/webTermRenderer.js +0 -7
  124. package/dist/types/commands.d.ts +1 -1
  125. package/dist/types/vfs.d.ts +8 -0
  126. package/dist/utils/argv.d.ts +22 -3
  127. package/dist/utils/argv.js +22 -3
  128. package/dist/utils/perfLogger.d.ts +10 -2
  129. package/dist/utils/perfLogger.js +7 -14
  130. package/dist/utils/shellSession.d.ts +35 -0
  131. package/dist/utils/shellSession.js +35 -0
  132. package/package.json +1 -1
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Netcat network utility
3
+ * @category net
4
+ * @params ["[-l] [-p port] [-v]"]
5
+ */
6
+ export const ncCommand = {
7
+ name: "nc",
8
+ description: "Netcat network utility",
9
+ category: "net",
10
+ params: ["[-l] [-p port] [-v]"],
11
+ run: async ({ args }) => {
12
+ let mod;
13
+ try {
14
+ mod = await import("node:net");
15
+ }
16
+ catch {
17
+ return { stderr: "nc: not available in this environment\n", exitCode: 1 };
18
+ }
19
+ const net = mod;
20
+ const isListen = args.includes("-l");
21
+ const pIdx = args.indexOf("-p");
22
+ const port = pIdx !== -1 && args[pIdx + 1] ? parseInt(args[pIdx + 1], 10) : undefined;
23
+ const verbose = args.includes("-v");
24
+ if (isListen && port) {
25
+ return new Promise((resolve) => {
26
+ const server = net.createServer((socket) => {
27
+ let data = "";
28
+ socket.on("data", (chunk) => { data += chunk.toString(); });
29
+ socket.on("end", () => {
30
+ server.close();
31
+ resolve({ stdout: data, exitCode: 0 });
32
+ });
33
+ });
34
+ server.listen(port, () => {
35
+ if (verbose) {
36
+ resolve({ stdout: `Listening on port ${port}...\n`, exitCode: 0 });
37
+ }
38
+ });
39
+ setTimeout(() => { server.close(); resolve({ exitCode: 0 }); }, 5000);
40
+ });
41
+ }
42
+ const nonFlag = args.filter((a) => !a.startsWith("-"));
43
+ const host = nonFlag[0];
44
+ const portNum = nonFlag[1] ? parseInt(nonFlag[1], 10) : NaN;
45
+ if (host && !isNaN(portNum)) {
46
+ return new Promise((resolve) => {
47
+ const socket = net.createConnection({ host, port: portNum }, () => {
48
+ if (verbose) {
49
+ resolve({ stdout: `Connected to ${host}:${portNum}\n`, exitCode: 0 });
50
+ }
51
+ setTimeout(() => { socket.end(); resolve({ exitCode: 0 }); }, 3000);
52
+ });
53
+ socket.on("error", () => {
54
+ resolve({ stderr: `nc: connection to ${host}:${portNum} failed\n`, exitCode: 1 });
55
+ });
56
+ setTimeout(() => { socket.destroy(); resolve({ exitCode: 1 }); }, 5000);
57
+ });
58
+ }
59
+ return {
60
+ stderr: "nc: missing arguments. Usage: nc [-l] [-p port] [-v] [host] [port]\n",
61
+ exitCode: 1,
62
+ };
63
+ },
64
+ };
@@ -0,0 +1,7 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ /**
3
+ * Run command with adjusted niceness
4
+ * @category system
5
+ * @params ["[-n adjustment] <command> [args...]"]
6
+ */
7
+ export declare const niceCommand: ShellModule;
@@ -0,0 +1,22 @@
1
+ import { parseArgs } from "./command-helpers";
2
+ import { runCommand } from "./runtime";
3
+ /**
4
+ * Run command with adjusted niceness
5
+ * @category system
6
+ * @params ["[-n adjustment] <command> [args...]"]
7
+ */
8
+ export const niceCommand = {
9
+ name: "nice",
10
+ description: "Run command with adjusted niceness",
11
+ category: "system",
12
+ params: ["[-n adjustment] <command> [args...]"],
13
+ run: async ({ authUser, hostname, mode, cwd, shell, stdin, env, args }) => {
14
+ const { positionals } = parseArgs(args, {
15
+ flagsWithValue: ["-n"],
16
+ });
17
+ const cmd = positionals.join(" ");
18
+ if (!cmd)
19
+ return { stderr: "nice: missing command\n", exitCode: 1 };
20
+ return runCommand(cmd, authUser, hostname, mode, cwd, shell, stdin, env);
21
+ },
22
+ };
@@ -0,0 +1,7 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ /**
3
+ * Run command immune to hup signals
4
+ * @category system
5
+ * @params ["<command> [args...]"]
6
+ */
7
+ export declare const nohupCommand: ShellModule;
@@ -0,0 +1,18 @@
1
+ import { runCommand } from "./runtime";
2
+ /**
3
+ * Run command immune to hup signals
4
+ * @category system
5
+ * @params ["<command> [args...]"]
6
+ */
7
+ export const nohupCommand = {
8
+ name: "nohup",
9
+ description: "Run command immune to hup signals",
10
+ category: "system",
11
+ params: ["<command> [args...]"],
12
+ run: async ({ authUser, hostname, mode, cwd, shell, stdin, env, args }) => {
13
+ const cmd = args.join(" ");
14
+ if (!cmd)
15
+ return { stderr: "nohup: missing command\n", exitCode: 1 };
16
+ return runCommand(cmd, authUser, hostname, mode, cwd, shell, stdin, env);
17
+ },
18
+ };
@@ -1,6 +1,7 @@
1
1
  import type { ShellModule } from "../types/commands";
2
2
  /**
3
- * Send ICMP ECHO_REQUEST packets (mock implementation).
3
+ * Send ICMP ECHO_REQUEST packets uses the real host ping when available,
4
+ * falls back to VirtualNetworkManager simulation in browser or when system ping is absent.
4
5
  * @category network
5
6
  * @params ["[-c <count>] <host>"]
6
7
  */
@@ -1,28 +1,66 @@
1
1
  import { parseArgs } from "./command-helpers";
2
+ const isBrowser = typeof process === "undefined" || typeof process.versions?.node === "undefined";
3
+ /** Lazily import execSync — avoids static import that breaks browser polyfills. */
4
+ async function execPing(count, host) {
5
+ try {
6
+ const { execSync } = await import("node:child_process");
7
+ const output = execSync(`ping -c ${count} ${host}`, {
8
+ timeout: 30000,
9
+ encoding: "utf8",
10
+ stdio: ["ignore", "pipe", "pipe"],
11
+ });
12
+ return { stdout: output };
13
+ }
14
+ catch (err) {
15
+ const stderrMsg = err instanceof Error ? err.stderr : "";
16
+ if (stderrMsg)
17
+ return { stderr: stderrMsg };
18
+ return null;
19
+ }
20
+ }
2
21
  /**
3
- * Send ICMP ECHO_REQUEST packets (mock implementation).
22
+ * Send ICMP ECHO_REQUEST packets uses the real host ping when available,
23
+ * falls back to VirtualNetworkManager simulation in browser or when system ping is absent.
4
24
  * @category network
5
25
  * @params ["[-c <count>] <host>"]
6
26
  */
7
27
  export const pingCommand = {
8
28
  name: "ping",
9
- description: "Send ICMP ECHO_REQUEST (mock)",
29
+ description: "Send ICMP ECHO_REQUEST to network hosts",
10
30
  category: "network",
11
31
  params: ["[-c <count>] <host>"],
12
- run: ({ args }) => {
32
+ run: async ({ args, shell }) => {
13
33
  const { flagsWithValues, positionals } = parseArgs(args, {
14
34
  flagsWithValue: ["-c", "-i", "-W"],
15
35
  });
16
36
  const host = positionals[0] ?? "localhost";
17
37
  const countArg = flagsWithValues.get("-c");
18
38
  const count = countArg ? Math.max(1, parseInt(countArg, 10) || 4) : 4;
19
- const lines = [`PING ${host}: 56 data bytes`];
39
+ // Try real system ping first (Node.js only)
40
+ if (!isBrowser) {
41
+ const result = await execPing(count, host);
42
+ if (result)
43
+ return { ...result, exitCode: "stdout" in result ? 0 : 1 };
44
+ }
45
+ // Fallback: VirtualNetworkManager simulation
46
+ const lines = [`PING ${host} (${host === "localhost" ? "127.0.0.1" : host}): 56 data bytes`];
47
+ let transmitted = 0;
48
+ let received = 0;
20
49
  for (let i = 0; i < count; i++) {
21
- const ms = (Math.random() * 10 + 1).toFixed(3);
22
- lines.push(`64 bytes from ${host}: icmp_seq=${i} ttl=64 time=${ms} ms`);
50
+ transmitted++;
51
+ const latency = shell.network.ping(host);
52
+ if (latency < 0) {
53
+ lines.push(`From ${host} icmp_seq=${i} Destination Host Unreachable`);
54
+ }
55
+ else {
56
+ received++;
57
+ lines.push(`64 bytes from ${host}: icmp_seq=${i} ttl=64 time=${latency.toFixed(3)} ms`);
58
+ }
23
59
  }
60
+ const lost = transmitted - received;
61
+ const lossPct = ((lost / transmitted) * 100).toFixed(0);
24
62
  lines.push(`--- ${host} ping statistics ---`);
25
- lines.push(`${count} packets transmitted, ${count} received, 0% packet loss`);
26
- return { stdout: lines.join("\n"), exitCode: 0 };
63
+ lines.push(`${transmitted} packets transmitted, ${received} received, ${lossPct}% packet loss`);
64
+ return { stdout: `${lines.join("\n")}\n`, exitCode: 0 };
27
65
  },
28
66
  };
@@ -0,0 +1,13 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ /**
3
+ * List process IDs matching a pattern
4
+ * @category system
5
+ * @params ["[-f] <pattern>"]
6
+ */
7
+ export declare const pgrepCommand: ShellModule;
8
+ /**
9
+ * Kill processes matching a pattern
10
+ * @category system
11
+ * @params ["[-f] <pattern>"]
12
+ */
13
+ export declare const pkillCommand: ShellModule;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * List process IDs matching a pattern
3
+ * @category system
4
+ * @params ["[-f] <pattern>"]
5
+ */
6
+ export const pgrepCommand = {
7
+ name: "pgrep",
8
+ description: "List process IDs matching a pattern",
9
+ category: "system",
10
+ params: ["[-f] <pattern>"],
11
+ run: ({ activeSessions, args }) => {
12
+ const useFull = args.includes("-f");
13
+ const pattern = args.find((a) => !a.startsWith("-"));
14
+ if (!pattern)
15
+ return { stderr: "pgrep: missing pattern\n", exitCode: 1 };
16
+ try {
17
+ const re = new RegExp(pattern);
18
+ const results = [];
19
+ for (let i = 0; i < activeSessions.length; i++) {
20
+ const s = activeSessions[i];
21
+ const target = useFull
22
+ ? `${s.username} ${s.tty} ${s.remoteAddress} ${s.id}`
23
+ : s.username;
24
+ if (re.test(target)) {
25
+ results.push(String(1000 + i));
26
+ }
27
+ }
28
+ if (results.length === 0)
29
+ return { exitCode: 1 };
30
+ return { stdout: results.join("\n") + "\n", exitCode: 0 };
31
+ }
32
+ catch {
33
+ return { stderr: "pgrep: invalid pattern\n", exitCode: 2 };
34
+ }
35
+ },
36
+ };
37
+ /**
38
+ * Kill processes matching a pattern
39
+ * @category system
40
+ * @params ["[-f] <pattern>"]
41
+ */
42
+ export const pkillCommand = {
43
+ name: "pkill",
44
+ description: "Kill processes matching a pattern",
45
+ category: "system",
46
+ params: ["[-f] <pattern>"],
47
+ run: ({ activeSessions, shell, args }) => {
48
+ const useFull = args.includes("-f");
49
+ const pattern = args.find((a) => !a.startsWith("-"));
50
+ if (!pattern)
51
+ return { stderr: "pkill: missing pattern\n", exitCode: 1 };
52
+ try {
53
+ const re = new RegExp(pattern);
54
+ let killed = 0;
55
+ for (const s of activeSessions) {
56
+ const target = useFull
57
+ ? `${s.username} ${s.tty} ${s.remoteAddress} ${s.id}`
58
+ : s.username;
59
+ if (re.test(target)) {
60
+ shell.users.unregisterSession(s.id);
61
+ killed++;
62
+ }
63
+ }
64
+ if (killed === 0)
65
+ return { exitCode: 1 };
66
+ return { stdout: `killed ${killed} process(es)\n`, exitCode: 0 };
67
+ }
68
+ catch {
69
+ return { stderr: "pkill: invalid pattern\n", exitCode: 2 };
70
+ }
71
+ },
72
+ };
@@ -1,2 +1,7 @@
1
1
  import type { ShellModule } from "../types/commands";
2
+ /**
3
+ * Print working directory.
4
+ * @category navigation
5
+ * @params []
6
+ */
2
7
  export declare const pwdCommand: ShellModule;
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Print working directory.
3
+ * @category navigation
4
+ * @params []
5
+ */
1
6
  export const pwdCommand = {
2
7
  name: "pwd",
3
8
  description: "Print working directory",
@@ -1,7 +1,6 @@
1
1
  import { ifFlag } from "./command-helpers";
2
2
  import { resolvePath } from "./helpers";
3
3
  const VERSION = "Python 3.11.2";
4
- const _VERSION_SHORT = "3.11.2";
5
4
  const VERSION_INFO = "3.11.2 (default, Mar 13 2023, 12:18:29) [GCC 12.2.0]";
6
5
  const NONE = { __pytype__: "none" };
7
6
  function pyDict(entries = []) {
@@ -1664,7 +1663,6 @@ class Interpreter {
1664
1663
  if (line.startsWith("if ") && line.endsWith(":")) {
1665
1664
  const cond = line.slice(3, -1).trim();
1666
1665
  const body = this.collectBlock(lines, idx + 1, indent);
1667
- const _skip = body.length + 1;
1668
1666
  if (pyBool(this.pyEval(cond, scope))) {
1669
1667
  this.execBlock(body, new Map(scope).also?.((s) => {
1670
1668
  for (const [k, v] of scope)
@@ -1814,7 +1812,6 @@ class Interpreter {
1814
1812
  else
1815
1813
  break;
1816
1814
  }
1817
- let _caughtErr = null;
1818
1815
  try {
1819
1816
  this.runBlockInScope(tryBody, scope);
1820
1817
  if (elseBody.length)
@@ -1822,7 +1819,6 @@ class Interpreter {
1822
1819
  }
1823
1820
  catch (e) {
1824
1821
  if (e instanceof PyError) {
1825
- _caughtErr = e;
1826
1822
  let handled = false;
1827
1823
  for (const clause of exceptClauses) {
1828
1824
  if (clause.exc === null ||
@@ -10,7 +10,6 @@ export const readCommand = {
10
10
  category: "shell",
11
11
  params: ["[-r] [-p prompt] <var...>"],
12
12
  run: ({ args, stdin, env }) => {
13
- const _promptIdx = args.indexOf("-p");
14
13
  const varNames = args.filter((a, i) => a !== "-r" && a !== "-p" && args[i - 1] !== "-p");
15
14
  // In non-interactive context, read from stdin pipe
16
15
  const input = (stdin ?? "").split("\n")[0] ?? "";
@@ -1,7 +1,44 @@
1
1
  /** biome-ignore-all lint/style/useNamingConvention: ENV VARIABLES */
2
2
  import type { CommandContext, CommandResult, ShellModule } from "../types/commands";
3
+ /**
4
+ * Registers a command module so it can be called from the shell.
5
+ * The module's name and aliases are lowercased and trimmed; names must be
6
+ * non-empty and contain no spaces. Updates the registry incrementally and
7
+ * invalidates the sorted-name cache.
8
+ *
9
+ * @param module - The command module to register
10
+ */
3
11
  export declare function registerCommand(module: ShellModule): void;
12
+ /**
13
+ * Creates a custom user-defined command from a shell script or function.
14
+ * The returned module is not automatically registered; call
15
+ * {@link registerCommand} to add it to the registry.
16
+ *
17
+ * @param name - The command name
18
+ * @param params - Parameter names for the command
19
+ * @param run - The handler invoked when the command is executed
20
+ * @returns A command module that can be passed to registerCommand
21
+ */
4
22
  export declare function createCustomCommand(name: string, params: string[], run: (ctx: CommandContext) => CommandResult | Promise<CommandResult>): ShellModule;
23
+ /**
24
+ * Returns a sorted list of all registered command names (including aliases).
25
+ * The list is cached and rebuilt lazily when commands are added or removed.
26
+ *
27
+ * @returns A sorted array of command names
28
+ */
5
29
  export declare function getCommandNames(): string[];
30
+ /**
31
+ * Returns all public command modules — built-in commands, custom commands,
32
+ * and the dynamically generated help command.
33
+ *
34
+ * @returns An array of all registered command modules
35
+ */
6
36
  export declare function getCommandModulesPublic(): ShellModule[];
37
+ /**
38
+ * Resolves a command module by name or alias. The lookup is case-insensitive.
39
+ * Builds the internal cache first if it has not been populated yet.
40
+ *
41
+ * @param name - The command or alias name to look up
42
+ * @returns The matching ShellModule, or undefined if not found
43
+ */
7
44
  export declare function resolveModule(name: string): ShellModule | undefined;
@@ -13,7 +13,9 @@ import { straceCommand } from "./strace";
13
13
  import { unzipCommand, zipCommand } from "./zip";
14
14
  import { catCommand } from "./cat";
15
15
  import { cdCommand } from "./cd";
16
+ import { chgrpCommand } from "./chgrp";
16
17
  import { chmodCommand } from "./chmod";
18
+ import { chownCommand } from "./chown";
17
19
  import { clearCommand } from "./clear";
18
20
  import { cpCommand } from "./cp";
19
21
  import { curlCommand } from "./curl";
@@ -97,6 +99,16 @@ import { whichCommand } from "./which";
97
99
  import { whoCommand } from "./who";
98
100
  import { whoamiCommand } from "./whoami";
99
101
  import { xargsCommand } from "./xargs";
102
+ import { ddCommand } from "./dd";
103
+ import { exprCommand } from "./expr";
104
+ import { realpathCommand, md5sumCommand, sha256sumCommand, stringsCommand, foldCommand, expandCommand, fmtCommand } from "./miscutils";
105
+ import { ncCommand } from "./netcat";
106
+ import { niceCommand } from "./nice";
107
+ import { nohupCommand } from "./nohup";
108
+ import { pgrepCommand, pkillCommand } from "./procUtils";
109
+ import { lscpuCommand, lsusbCommand, lspciCommand } from "./sysinfo";
110
+ import { joinCommand, commCommand, splitCommand, csplitCommand } from "./textutils";
111
+ import { topCommand } from "./top";
100
112
  const BASE_COMMANDS = [
101
113
  // Navigation
102
114
  pwdCommand,
@@ -113,9 +125,13 @@ const BASE_COMMANDS = [
113
125
  lnCommand,
114
126
  readlinkCommand,
115
127
  chmodCommand,
128
+ chownCommand,
129
+ chgrpCommand,
116
130
  seqCommand,
117
131
  statCommand,
118
132
  findCommand,
133
+ ddCommand,
134
+ realpathCommand,
119
135
  // Text processing
120
136
  grepCommand,
121
137
  sedCommand,
@@ -130,6 +146,16 @@ const BASE_COMMANDS = [
130
146
  teeCommand,
131
147
  xargsCommand,
132
148
  diffCommand,
149
+ foldCommand,
150
+ expandCommand,
151
+ fmtCommand,
152
+ md5sumCommand,
153
+ sha256sumCommand,
154
+ stringsCommand,
155
+ joinCommand,
156
+ commCommand,
157
+ splitCommand,
158
+ csplitCommand,
133
159
  // Archives
134
160
  tarCommand,
135
161
  gzipCommand,
@@ -153,6 +179,14 @@ const BASE_COMMANDS = [
153
179
  dateCommand,
154
180
  sleepCommand,
155
181
  pingCommand,
182
+ lscpuCommand,
183
+ lsusbCommand,
184
+ lspciCommand,
185
+ pgrepCommand,
186
+ pkillCommand,
187
+ topCommand,
188
+ niceCommand,
189
+ nohupCommand,
156
190
  // Shell
157
191
  echoCommand,
158
192
  envCommand,
@@ -184,6 +218,7 @@ const BASE_COMMANDS = [
184
218
  // Network
185
219
  curlCommand,
186
220
  wgetCommand,
221
+ ncCommand,
187
222
  // Users
188
223
  adduserCommand,
189
224
  passwdCommand,
@@ -222,6 +257,7 @@ const BASE_COMMANDS = [
222
257
  npxCommand,
223
258
  nodeCommand,
224
259
  python3Command,
260
+ exprCommand,
225
261
  // System (extended)
226
262
  uptimeCommand,
227
263
  freeCommand,
@@ -257,6 +293,14 @@ function buildCache() {
257
293
  function getCommandModules() {
258
294
  return [...BASE_COMMANDS, ...customCommands, helpCommand];
259
295
  }
296
+ /**
297
+ * Registers a command module so it can be called from the shell.
298
+ * The module's name and aliases are lowercased and trimmed; names must be
299
+ * non-empty and contain no spaces. Updates the registry incrementally and
300
+ * invalidates the sorted-name cache.
301
+ *
302
+ * @param module - The command module to register
303
+ */
260
304
  export function registerCommand(module) {
261
305
  const normalized = {
262
306
  ...module,
@@ -275,17 +319,46 @@ export function registerCommand(module) {
275
319
  // Invalidate sorted names cache; rebuilt lazily on next getCommandNames()
276
320
  cachedCommandNames = null;
277
321
  }
322
+ /**
323
+ * Creates a custom user-defined command from a shell script or function.
324
+ * The returned module is not automatically registered; call
325
+ * {@link registerCommand} to add it to the registry.
326
+ *
327
+ * @param name - The command name
328
+ * @param params - Parameter names for the command
329
+ * @param run - The handler invoked when the command is executed
330
+ * @returns A command module that can be passed to registerCommand
331
+ */
278
332
  export function createCustomCommand(name, params, run) {
279
333
  return { name, params, run };
280
334
  }
335
+ /**
336
+ * Returns a sorted list of all registered command names (including aliases).
337
+ * The list is cached and rebuilt lazily when commands are added or removed.
338
+ *
339
+ * @returns A sorted array of command names
340
+ */
281
341
  export function getCommandNames() {
282
342
  if (!cachedCommandNames)
283
343
  buildCache();
284
344
  return cachedCommandNames;
285
345
  }
346
+ /**
347
+ * Returns all public command modules — built-in commands, custom commands,
348
+ * and the dynamically generated help command.
349
+ *
350
+ * @returns An array of all registered command modules
351
+ */
286
352
  export function getCommandModulesPublic() {
287
353
  return getCommandModules();
288
354
  }
355
+ /**
356
+ * Resolves a command module by name or alias. The lookup is case-insensitive.
357
+ * Builds the internal cache first if it has not been populated yet.
358
+ *
359
+ * @param name - The command or alias name to look up
360
+ * @returns The matching ShellModule, or undefined if not found
361
+ */
289
362
  export function resolveModule(name) {
290
363
  if (!cachedCommandNames)
291
364
  buildCache();
@@ -1,5 +1,6 @@
1
+ import * as path from "node:path";
1
2
  import { getArg, ifFlag } from "./command-helpers";
2
- import { assertPathAccess, resolvePath } from "./helpers";
3
+ import { checkFilePermission, resolvePath } from "./helpers";
3
4
  const FLAG_RECURSIVE = ["-r", "-R", "-rf", "-fr", "-rF", "-Fr"];
4
5
  const FLAG_FORCE = ["-f", "-rf", "-fr", "-rF", "-Fr", "--force"];
5
6
  /**
@@ -31,7 +32,7 @@ export const rmCommand = {
31
32
  }
32
33
  const resolved = targets.map((t) => resolvePath(cwd, t));
33
34
  for (const r of resolved)
34
- assertPathAccess(authUser, r, "rm");
35
+ checkFilePermission(shell.vfs, shell.users, authUser, path.posix.dirname(r), 2);
35
36
  for (const r of resolved) {
36
37
  if (!shell.vfs.exists(r)) {
37
38
  if (force)
@@ -7,6 +7,52 @@ export declare function userHome(authUser: string): string;
7
7
  * the new user's .bashrc. Call this after setting authUser = newUser.
8
8
  */
9
9
  export declare function applyUserSwitch(newUser: string, hostname: string, cwd: string, shellEnv: ShellEnv, shell: VirtualShell): Promise<void>;
10
+ /**
11
+ * Creates the default shell environment variables for a given user and host.
12
+ * Sets PATH, HOME, USER, LOGNAME, SHELL, TERM, HOSTNAME, and a coloured PS1
13
+ * prompt that distinguishes root (red) from ordinary users (magenta).
14
+ *
15
+ * @param authUser - The authenticated username
16
+ * @param hostname - The machine hostname
17
+ * @returns A ShellEnv populated with default variable values
18
+ */
10
19
  export declare function makeDefaultEnv(authUser: string, hostname: string): ShellEnv;
11
- export declare function runCommandDirect(name: string, args: string[], authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, stdin: string | undefined, env: ShellEnv): Promise<CommandResult>;
20
+ /**
21
+ * Runs a command directly by name, bypassing shell parsing and expansion.
22
+ * Handles variable assignments prefixing the command, shell functions
23
+ * (stored as `__func_<name>`), aliases, registered modules, and VFS
24
+ * stub binaries. Includes an anti-loop guard via a module-level call depth
25
+ * counter and registers the process in the user's session table.
26
+ *
27
+ * @param name - The command name to execute
28
+ * @param args - Argument array (does not include the command name)
29
+ * @param authUser - The authenticated user
30
+ * @param hostname - The machine hostname
31
+ * @param mode - The current command mode (shell, pipe, etc.)
32
+ * @param cwd - The current working directory
33
+ * @param shell - The VirtualShell instance
34
+ * @param stdin - Optional stdin string
35
+ * @param env - The current shell environment
36
+ * @param background - Whether the command is run in the background
37
+ * @param abortController - Optional controller to abort a background process
38
+ * @returns The command result
39
+ */
40
+ export declare function runCommandDirect(name: string, args: string[], authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, stdin: string | undefined, env: ShellEnv, background?: boolean, abortController?: AbortController): Promise<CommandResult>;
41
+ /**
42
+ * Parses and runs a command line string through the full execution pipeline:
43
+ * history expansion (`!!` / `!n`), alias expansion, sh-syntax detection
44
+ * (for/while/if/function/arithmetic), pipe/redirect routing via the shell
45
+ * parser, variable expansion, brace expansion, and glob expansion. Falls
46
+ * back to the `sh` interpreter for compound constructs.
47
+ *
48
+ * @param rawInput - The raw command line string to parse and run
49
+ * @param authUser - The authenticated user
50
+ * @param hostname - The machine hostname
51
+ * @param mode - The current command mode (shell, pipe, etc.)
52
+ * @param cwd - The current working directory
53
+ * @param shell - The VirtualShell instance
54
+ * @param stdin - Optional stdin string
55
+ * @param env - Optional shell environment (created from defaults if omitted)
56
+ * @returns The command result
57
+ */
12
58
  export declare function runCommand(rawInput: string, authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, stdin?: string, env?: ShellEnv): Promise<CommandResult>;