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.
- package/.vscode/settings.json +0 -1
- package/README.md +462 -44
- package/biome.json +7 -0
- package/dist/SSHMimic/exec.d.ts.map +1 -1
- package/dist/SSHMimic/executor.d.ts.map +1 -1
- package/dist/SSHMimic/executor.js +35 -21
- package/dist/SSHMimic/index.d.ts.map +1 -1
- package/dist/SSHMimic/index.js +20 -6
- package/dist/VirtualFileSystem/binaryPack.d.ts.map +1 -1
- package/dist/VirtualFileSystem/binaryPack.js +29 -6
- package/dist/VirtualFileSystem/index.d.ts.map +1 -1
- package/dist/VirtualFileSystem/index.js +36 -13
- package/dist/VirtualPackageManager/index.d.ts +202 -0
- package/dist/VirtualPackageManager/index.d.ts.map +1 -0
- package/dist/VirtualPackageManager/index.js +825 -0
- package/dist/VirtualShell/index.d.ts +93 -12
- package/dist/VirtualShell/index.d.ts.map +1 -1
- package/dist/VirtualShell/index.js +95 -13
- package/dist/VirtualShell/shell.d.ts.map +1 -1
- package/dist/VirtualShell/shell.js +3 -1
- package/dist/VirtualShell/shellParser.d.ts.map +1 -1
- package/dist/VirtualUserManager/index.d.ts +52 -20
- package/dist/VirtualUserManager/index.d.ts.map +1 -1
- package/dist/VirtualUserManager/index.js +54 -20
- package/dist/commands/adduser.d.ts +6 -0
- package/dist/commands/adduser.d.ts.map +1 -1
- package/dist/commands/adduser.js +6 -0
- package/dist/commands/alias.d.ts +9 -0
- package/dist/commands/alias.d.ts.map +1 -0
- package/dist/commands/alias.js +63 -0
- package/dist/commands/apt.d.ts +9 -0
- package/dist/commands/apt.d.ts.map +1 -0
- package/dist/commands/apt.js +205 -0
- package/dist/commands/awk.d.ts +11 -0
- package/dist/commands/awk.d.ts.map +1 -1
- package/dist/commands/awk.js +15 -2
- package/dist/commands/base64.d.ts +5 -0
- package/dist/commands/base64.d.ts.map +1 -1
- package/dist/commands/base64.js +9 -1
- package/dist/commands/cat.d.ts +5 -0
- package/dist/commands/cat.d.ts.map +1 -1
- package/dist/commands/cat.js +35 -8
- package/dist/commands/cd.d.ts +5 -0
- package/dist/commands/cd.d.ts.map +1 -1
- package/dist/commands/cd.js +5 -0
- package/dist/commands/chmod.d.ts +5 -0
- package/dist/commands/chmod.d.ts.map +1 -1
- package/dist/commands/chmod.js +57 -3
- package/dist/commands/command-helpers.d.ts +78 -4
- package/dist/commands/command-helpers.d.ts.map +1 -1
- package/dist/commands/command-helpers.js +78 -4
- package/dist/commands/cp.d.ts +5 -0
- package/dist/commands/cp.d.ts.map +1 -1
- package/dist/commands/cp.js +5 -0
- package/dist/commands/curl.d.ts +5 -0
- package/dist/commands/curl.d.ts.map +1 -1
- package/dist/commands/curl.js +106 -26
- package/dist/commands/cut.d.ts +5 -0
- package/dist/commands/cut.d.ts.map +1 -1
- package/dist/commands/cut.js +8 -1
- package/dist/commands/date.d.ts +5 -0
- package/dist/commands/date.d.ts.map +1 -1
- package/dist/commands/date.js +7 -1
- package/dist/commands/declare.d.ts +3 -0
- package/dist/commands/declare.d.ts.map +1 -0
- package/dist/commands/declare.js +39 -0
- package/dist/commands/diff.d.ts +5 -0
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +5 -0
- package/dist/commands/dpkg.d.ts +9 -0
- package/dist/commands/dpkg.d.ts.map +1 -0
- package/dist/commands/dpkg.js +161 -0
- package/dist/commands/du.d.ts.map +1 -1
- package/dist/commands/du.js +8 -2
- package/dist/commands/echo.d.ts +5 -0
- package/dist/commands/echo.d.ts.map +1 -1
- package/dist/commands/echo.js +33 -12
- package/dist/commands/env.d.ts +5 -0
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +11 -1
- package/dist/commands/exit.d.ts +5 -0
- package/dist/commands/exit.d.ts.map +1 -1
- package/dist/commands/exit.js +12 -2
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +3 -1
- package/dist/commands/find.d.ts +5 -0
- package/dist/commands/find.d.ts.map +1 -1
- package/dist/commands/find.js +5 -0
- package/dist/commands/free.d.ts +8 -0
- package/dist/commands/free.d.ts.map +1 -0
- package/dist/commands/free.js +43 -0
- package/dist/commands/grep.d.ts +5 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +12 -2
- package/dist/commands/gzip.d.ts +5 -0
- package/dist/commands/gzip.d.ts.map +1 -1
- package/dist/commands/gzip.js +18 -2
- package/dist/commands/head.d.ts +5 -0
- package/dist/commands/head.d.ts.map +1 -1
- package/dist/commands/head.js +5 -0
- package/dist/commands/help.d.ts.map +1 -1
- package/dist/commands/help.js +98 -45
- package/dist/commands/helpers.d.ts +3 -0
- package/dist/commands/helpers.d.ts.map +1 -1
- package/dist/commands/helpers.js +3 -0
- package/dist/commands/history.d.ts +8 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +26 -0
- package/dist/commands/hostname.d.ts +5 -0
- package/dist/commands/hostname.d.ts.map +1 -1
- package/dist/commands/hostname.js +5 -0
- package/dist/commands/id.d.ts.map +1 -1
- package/dist/commands/id.js +4 -1
- package/dist/commands/index.d.ts +2 -10
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +2 -231
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +6 -3
- package/dist/commands/lsb-release.d.ts +3 -0
- package/dist/commands/lsb-release.d.ts.map +1 -0
- package/dist/commands/lsb-release.js +56 -0
- package/dist/commands/man.d.ts +3 -0
- package/dist/commands/man.d.ts.map +1 -0
- package/dist/commands/man.js +155 -0
- package/dist/commands/nano.js +1 -1
- package/dist/commands/neofetch.d.ts.map +1 -1
- package/dist/commands/neofetch.js +6 -1
- package/dist/commands/node.d.ts +9 -0
- package/dist/commands/node.d.ts.map +1 -0
- package/dist/commands/node.js +316 -0
- package/dist/commands/npm.d.ts +19 -0
- package/dist/commands/npm.d.ts.map +1 -0
- package/dist/commands/npm.js +109 -0
- package/dist/commands/ping.d.ts.map +1 -1
- package/dist/commands/ping.js +7 -2
- package/dist/commands/printf.d.ts +3 -0
- package/dist/commands/printf.d.ts.map +1 -0
- package/dist/commands/printf.js +113 -0
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +30 -6
- package/dist/commands/python.d.ts +30 -0
- package/dist/commands/python.d.ts.map +1 -0
- package/dist/commands/python.js +2058 -0
- package/dist/commands/read.d.ts +3 -0
- package/dist/commands/read.d.ts.map +1 -0
- package/dist/commands/read.js +34 -0
- package/dist/commands/registry.d.ts +8 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +229 -0
- package/dist/commands/runtime.d.ts +6 -0
- package/dist/commands/runtime.d.ts.map +1 -0
- package/dist/commands/runtime.js +280 -0
- package/dist/commands/sed.d.ts.map +1 -1
- package/dist/commands/sed.js +11 -3
- package/dist/commands/set.d.ts.map +1 -1
- package/dist/commands/set.js +9 -3
- package/dist/commands/sh.d.ts.map +1 -1
- package/dist/commands/sh.js +69 -30
- package/dist/commands/shift.d.ts +5 -0
- package/dist/commands/shift.d.ts.map +1 -0
- package/dist/commands/shift.js +52 -0
- package/dist/commands/sleep.d.ts.map +1 -1
- package/dist/commands/sort.d.ts.map +1 -1
- package/dist/commands/sort.js +4 -2
- package/dist/commands/source.d.ts +3 -0
- package/dist/commands/source.d.ts.map +1 -0
- package/dist/commands/source.js +34 -0
- package/dist/commands/sudo.js +1 -1
- package/dist/commands/tar.d.ts.map +1 -1
- package/dist/commands/tar.js +11 -3
- package/dist/commands/tee.d.ts.map +1 -1
- package/dist/commands/tee.js +8 -6
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +114 -0
- package/dist/commands/tr.d.ts.map +1 -1
- package/dist/commands/tr.js +3 -1
- package/dist/commands/true.d.ts +4 -0
- package/dist/commands/true.d.ts.map +1 -0
- package/dist/commands/true.js +14 -0
- package/dist/commands/type.d.ts +3 -0
- package/dist/commands/type.d.ts.map +1 -0
- package/dist/commands/type.js +34 -0
- package/dist/commands/uname.d.ts.map +1 -1
- package/dist/commands/uname.js +4 -1
- package/dist/commands/uniq.d.ts.map +1 -1
- package/dist/commands/uptime.d.ts +3 -0
- package/dist/commands/uptime.d.ts.map +1 -0
- package/dist/commands/uptime.js +43 -0
- package/dist/commands/wget.d.ts.map +1 -1
- package/dist/commands/wget.js +92 -96
- package/dist/commands/which.d.ts +3 -0
- package/dist/commands/which.d.ts.map +1 -0
- package/dist/commands/which.js +32 -0
- package/dist/commands/xargs.d.ts.map +1 -1
- package/dist/commands/xargs.js +1 -1
- package/dist/index.d.ts +15 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -8
- package/dist/modules/linuxRootfs.d.ts +41 -0
- package/dist/modules/linuxRootfs.d.ts.map +1 -0
- package/dist/modules/linuxRootfs.js +440 -0
- package/dist/modules/neofetch.d.ts.map +1 -1
- package/dist/modules/neofetch.js +1 -0
- package/dist/standalone-wo-sftp.d.ts +2 -0
- package/dist/standalone-wo-sftp.d.ts.map +1 -0
- package/dist/standalone-wo-sftp.js +30 -0
- package/dist/utils/expand.d.ts +50 -0
- package/dist/utils/expand.d.ts.map +1 -0
- package/dist/utils/expand.js +183 -0
- package/dist/utils/vfsDiff.d.ts +90 -0
- package/dist/utils/vfsDiff.d.ts.map +1 -0
- package/dist/utils/vfsDiff.js +177 -0
- package/package.json +3 -1
- package/src/SSHMimic/exec.ts +10 -1
- package/src/SSHMimic/executor.ts +105 -21
- package/src/SSHMimic/index.ts +49 -15
- package/src/VirtualFileSystem/binaryPack.ts +35 -8
- package/src/VirtualFileSystem/index.ts +78 -28
- package/src/VirtualPackageManager/index.ts +979 -0
- package/src/VirtualShell/index.ts +133 -14
- package/src/VirtualShell/shell.ts +23 -3
- package/src/VirtualShell/shellParser.ts +134 -36
- package/src/VirtualUserManager/index.ts +62 -22
- package/src/commands/adduser.ts +6 -0
- package/src/commands/alias.ts +64 -0
- package/src/commands/apt.ts +228 -0
- package/src/commands/awk.ts +20 -6
- package/src/commands/base64.ts +13 -2
- package/src/commands/cat.ts +40 -8
- package/src/commands/cd.ts +5 -0
- package/src/commands/chmod.ts +53 -3
- package/src/commands/command-helpers.ts +78 -4
- package/src/commands/cp.ts +5 -0
- package/src/commands/curl.ts +118 -33
- package/src/commands/cut.ts +8 -1
- package/src/commands/date.ts +7 -1
- package/src/commands/declare.ts +44 -0
- package/src/commands/diff.ts +17 -3
- package/src/commands/dpkg.ts +180 -0
- package/src/commands/du.ts +17 -5
- package/src/commands/echo.ts +41 -12
- package/src/commands/env.ts +11 -1
- package/src/commands/exit.ts +12 -2
- package/src/commands/export.ts +3 -1
- package/src/commands/find.ts +5 -0
- package/src/commands/free.ts +47 -0
- package/src/commands/grep.ts +12 -2
- package/src/commands/gzip.ts +28 -4
- package/src/commands/head.ts +5 -0
- package/src/commands/help.ts +121 -47
- package/src/commands/helpers.ts +8 -0
- package/src/commands/history.ts +34 -0
- package/src/commands/hostname.ts +5 -0
- package/src/commands/id.ts +4 -1
- package/src/commands/index.ts +9 -255
- package/src/commands/ls.ts +6 -3
- package/src/commands/lsb-release.ts +58 -0
- package/src/commands/man.ts +166 -0
- package/src/commands/nano.ts +1 -1
- package/src/commands/neofetch.ts +6 -1
- package/src/commands/node.ts +341 -0
- package/src/commands/npm.ts +132 -0
- package/src/commands/ping.ts +10 -3
- package/src/commands/printf.ts +112 -0
- package/src/commands/ps.ts +40 -6
- package/src/commands/python.ts +2229 -0
- package/src/commands/read.ts +41 -0
- package/src/commands/registry.ts +244 -0
- package/src/commands/runtime.ts +353 -0
- package/src/commands/sed.ts +27 -9
- package/src/commands/set.ts +9 -3
- package/src/commands/sh.ts +170 -44
- package/src/commands/shift.ts +53 -0
- package/src/commands/sleep.ts +2 -1
- package/src/commands/sort.ts +10 -6
- package/src/commands/source.ts +47 -0
- package/src/commands/sudo.ts +1 -1
- package/src/commands/tar.ts +28 -7
- package/src/commands/tee.ts +7 -1
- package/src/commands/test.ts +135 -0
- package/src/commands/tr.ts +3 -1
- package/src/commands/true.ts +17 -0
- package/src/commands/type.ts +43 -0
- package/src/commands/uname.ts +5 -1
- package/src/commands/uniq.ts +8 -2
- package/src/commands/uptime.ts +49 -0
- package/src/commands/wget.ts +105 -119
- package/src/commands/which.ts +37 -0
- package/src/commands/xargs.ts +11 -2
- package/src/index.ts +27 -18
- package/src/modules/linuxRootfs.ts +642 -0
- package/src/modules/neofetch.ts +1 -0
- package/src/standalone-wo-sftp.ts +38 -0
- package/src/utils/expand.ts +238 -0
- package/src/utils/vfsDiff.ts +275 -0
- package/standalone-wo-sftp.js +507 -0
- package/standalone-wo-sftp.js.map +7 -0
- package/standalone.js +486 -109
- package/standalone.js.map +4 -4
- package/tests/bun-test-shim.ts +9 -1
- package/tests/command-helpers.test.ts +1 -5
- package/tests/new-features.test.ts +1036 -0
- package/tests/parser-executor.test.ts +27 -27
- package/tests/sftp.test.ts +122 -42
- package/tests/users.test.ts +23 -5
- package/CHANGELOG.md +0 -150
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import type { ShellModule } from "../types/commands";
|
|
3
|
+
import { ifFlag } from "./command-helpers";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Display memory usage information (human / MB / GB options).
|
|
7
|
+
* @category system
|
|
8
|
+
* @params ["[-h] [-m] [-g]"]
|
|
9
|
+
*/
|
|
10
|
+
export const freeCommand: ShellModule = {
|
|
11
|
+
name: "free",
|
|
12
|
+
description: "Display amount of free and used memory",
|
|
13
|
+
category: "system",
|
|
14
|
+
params: ["[-h] [-m] [-g]"],
|
|
15
|
+
run: ({ args }) => {
|
|
16
|
+
const human = ifFlag(args, ["-h", "--human"]);
|
|
17
|
+
const mb = ifFlag(args, ["-m"]);
|
|
18
|
+
const gb = ifFlag(args, ["-g"]);
|
|
19
|
+
|
|
20
|
+
const osTotalB = os.totalmem();
|
|
21
|
+
const osFreeB = os.freemem();
|
|
22
|
+
const usedB = osTotalB - osFreeB;
|
|
23
|
+
const sharedB = Math.floor(osTotalB * 0.02);
|
|
24
|
+
const buffersB = Math.floor(osTotalB * 0.05);
|
|
25
|
+
const availableB = Math.floor(osFreeB * 0.95);
|
|
26
|
+
const swapB = Math.floor(osTotalB * 0.5);
|
|
27
|
+
|
|
28
|
+
const fmt = (bytes: number): string => {
|
|
29
|
+
if (human) {
|
|
30
|
+
if (bytes >= 1024 * 1024 * 1024)
|
|
31
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}G`;
|
|
32
|
+
if (bytes >= 1024 * 1024)
|
|
33
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
|
|
34
|
+
return `${(bytes / 1024).toFixed(1)}K`;
|
|
35
|
+
}
|
|
36
|
+
if (gb) return String(Math.floor(bytes / (1024 * 1024 * 1024)));
|
|
37
|
+
if (mb) return String(Math.floor(bytes / (1024 * 1024)));
|
|
38
|
+
return String(Math.floor(bytes / 1024));
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const header = ` total used free shared buff/cache available`;
|
|
42
|
+
const memRow = `Mem: ${fmt(osTotalB).padStart(12)} ${fmt(usedB).padStart(11)} ${fmt(osFreeB).padStart(11)} ${fmt(sharedB).padStart(11)} ${fmt(buffersB).padStart(11)} ${fmt(availableB).padStart(11)}`;
|
|
43
|
+
const swapRow = `Swap: ${fmt(swapB).padStart(12)} ${fmt(0).padStart(11)} ${fmt(swapB).padStart(11)}`;
|
|
44
|
+
|
|
45
|
+
return { stdout: [header, memRow, swapRow].join("\n"), exitCode: 0 };
|
|
46
|
+
},
|
|
47
|
+
};
|
package/src/commands/grep.ts
CHANGED
|
@@ -2,13 +2,20 @@ import type { ShellModule } from "../types/commands";
|
|
|
2
2
|
import { parseArgs } from "./command-helpers";
|
|
3
3
|
import { assertPathAccess, resolvePath } from "./helpers";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Search for a regex pattern in files or stdin with common flags.
|
|
7
|
+
* @category text
|
|
8
|
+
* @params ["[-i] [-v] [-n] [-r] <pattern> [file...]"]
|
|
9
|
+
*/
|
|
5
10
|
export const grepCommand: ShellModule = {
|
|
6
11
|
name: "grep",
|
|
7
12
|
description: "Search text patterns",
|
|
8
13
|
category: "text",
|
|
9
14
|
params: ["[-i] [-v] [-n] [-r] <pattern> [file...]"],
|
|
10
15
|
run: ({ authUser, shell, cwd, args, stdin }) => {
|
|
11
|
-
const { flags, positionals } = parseArgs(args, {
|
|
16
|
+
const { flags, positionals } = parseArgs(args, {
|
|
17
|
+
flags: ["-i", "-v", "-n", "-r"],
|
|
18
|
+
});
|
|
12
19
|
const caseInsensitive = flags.has("-i");
|
|
13
20
|
const invertMatch = flags.has("-v");
|
|
14
21
|
const showLineNumbers = flags.has("-n");
|
|
@@ -80,7 +87,10 @@ export const grepCommand: ShellModule = {
|
|
|
80
87
|
const prefix = resolvedPaths.length > 1 ? `${file}:` : "";
|
|
81
88
|
results.push(...matchLines(content, prefix));
|
|
82
89
|
} catch {
|
|
83
|
-
return {
|
|
90
|
+
return {
|
|
91
|
+
stderr: `grep: ${file}: No such file or directory`,
|
|
92
|
+
exitCode: 1,
|
|
93
|
+
};
|
|
84
94
|
}
|
|
85
95
|
}
|
|
86
96
|
}
|
package/src/commands/gzip.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { ShellModule } from "../types/commands";
|
|
2
2
|
import { resolvePath } from "./helpers";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Compress files using gzip (stores in VFS as compressed content).
|
|
6
|
+
* @category archive
|
|
7
|
+
* @params ["<file>"]
|
|
8
|
+
*/
|
|
4
9
|
export const gzipCommand: ShellModule = {
|
|
5
10
|
name: "gzip",
|
|
6
11
|
description: "Compress files",
|
|
@@ -10,12 +15,24 @@ export const gzipCommand: ShellModule = {
|
|
|
10
15
|
const file = args[0];
|
|
11
16
|
if (!file) return { stderr: "gzip: no file specified", exitCode: 1 };
|
|
12
17
|
const p = resolvePath(cwd, file);
|
|
13
|
-
try {
|
|
14
|
-
|
|
18
|
+
try {
|
|
19
|
+
shell.vfs.compressFile(p);
|
|
20
|
+
return { exitCode: 0 };
|
|
21
|
+
} catch {
|
|
22
|
+
return {
|
|
23
|
+
stderr: `gzip: ${file}: No such file or directory`,
|
|
24
|
+
exitCode: 1,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
15
27
|
},
|
|
16
28
|
};
|
|
17
29
|
|
|
18
30
|
export const gunzipCommand: ShellModule = {
|
|
31
|
+
/**
|
|
32
|
+
* Decompress gzip files (or zcat alias).
|
|
33
|
+
* @category archive
|
|
34
|
+
* @params ["<file>"]
|
|
35
|
+
*/
|
|
19
36
|
name: "gunzip",
|
|
20
37
|
description: "Decompress files",
|
|
21
38
|
category: "archive",
|
|
@@ -25,7 +42,14 @@ export const gunzipCommand: ShellModule = {
|
|
|
25
42
|
const file = args[0];
|
|
26
43
|
if (!file) return { stderr: "gunzip: no file specified", exitCode: 1 };
|
|
27
44
|
const p = resolvePath(cwd, file);
|
|
28
|
-
try {
|
|
29
|
-
|
|
45
|
+
try {
|
|
46
|
+
shell.vfs.decompressFile(p);
|
|
47
|
+
return { exitCode: 0 };
|
|
48
|
+
} catch {
|
|
49
|
+
return {
|
|
50
|
+
stderr: `gunzip: ${file}: No such file or directory`,
|
|
51
|
+
exitCode: 1,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
30
54
|
},
|
|
31
55
|
};
|
package/src/commands/head.ts
CHANGED
|
@@ -2,6 +2,11 @@ import type { ShellModule } from "../types/commands";
|
|
|
2
2
|
import { getFlag } from "./command-helpers";
|
|
3
3
|
import { assertPathAccess, resolvePath } from "./helpers";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Output the first part of files or stdin (head).
|
|
7
|
+
* @category text
|
|
8
|
+
* @params ["[-n <lines>] [file...]"]
|
|
9
|
+
*/
|
|
5
10
|
export const headCommand: ShellModule = {
|
|
6
11
|
name: "head",
|
|
7
12
|
description: "Output first lines",
|
package/src/commands/help.ts
CHANGED
|
@@ -1,78 +1,152 @@
|
|
|
1
1
|
import type { ShellModule } from "../types/commands";
|
|
2
|
-
import { getCommandModulesPublic } from "./
|
|
2
|
+
import { getCommandModulesPublic } from "./registry";
|
|
3
|
+
|
|
4
|
+
// ─── category config ──────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
const CATEGORY_ORDER = [
|
|
7
|
+
"navigation",
|
|
8
|
+
"files",
|
|
9
|
+
"text",
|
|
10
|
+
"archive",
|
|
11
|
+
"system",
|
|
12
|
+
"package",
|
|
13
|
+
"network",
|
|
14
|
+
"shell",
|
|
15
|
+
"users",
|
|
16
|
+
"misc",
|
|
17
|
+
];
|
|
3
18
|
|
|
4
|
-
const CATEGORY_ORDER = ["navigation", "files", "text", "archive", "system", "network", "shell", "users", "misc"];
|
|
5
19
|
const CATEGORY_LABELS: Record<string, string> = {
|
|
6
20
|
navigation: "Navigation",
|
|
7
21
|
files: "Files & Filesystem",
|
|
8
22
|
text: "Text Processing",
|
|
9
23
|
archive: "Archive & Compression",
|
|
10
24
|
system: "System",
|
|
25
|
+
package: "Package Management",
|
|
11
26
|
network: "Network",
|
|
12
|
-
shell: "Shell",
|
|
27
|
+
shell: "Shell & Scripting",
|
|
13
28
|
users: "Users & Permissions",
|
|
14
29
|
misc: "Miscellaneous",
|
|
15
30
|
};
|
|
16
31
|
|
|
17
|
-
|
|
32
|
+
// ─── formatting helpers ───────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
const BOLD = "\x1b[1m";
|
|
35
|
+
const RESET = "\x1b[0m";
|
|
36
|
+
const CYAN = "\x1b[36m";
|
|
37
|
+
const YLW = "\x1b[33m";
|
|
38
|
+
const DIM = "\x1b[2m";
|
|
39
|
+
const GREEN = "\x1b[32m";
|
|
40
|
+
|
|
41
|
+
function pad(s: string, n: number): string {
|
|
18
42
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
19
43
|
}
|
|
20
44
|
|
|
45
|
+
function formatCmdLine(mod: ShellModule): string {
|
|
46
|
+
const aliases = mod.aliases?.length
|
|
47
|
+
? ` ${DIM}(${mod.aliases.join(", ")})${RESET}`
|
|
48
|
+
: "";
|
|
49
|
+
return ` ${CYAN}${pad(mod.name, 16)}${RESET}${aliases}${pad("", mod.aliases?.length ? 0 : 0)} ${mod.description ?? ""}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ─── full grouped listing ─────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
function renderFull(modules: ShellModule[]): string {
|
|
55
|
+
const grouped: Record<string, ShellModule[]> = {};
|
|
56
|
+
for (const mod of modules) {
|
|
57
|
+
const cat = mod.category ?? "misc";
|
|
58
|
+
if (!grouped[cat]) grouped[cat] = [];
|
|
59
|
+
grouped[cat]!.push(mod);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const lines: string[] = [
|
|
63
|
+
`${BOLD}Available commands${RESET}`,
|
|
64
|
+
`${DIM}Type 'help <command>' for detailed usage.${RESET}`,
|
|
65
|
+
"",
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const cats = [
|
|
69
|
+
...CATEGORY_ORDER.filter((c) => grouped[c]),
|
|
70
|
+
...Object.keys(grouped)
|
|
71
|
+
.filter((c) => !CATEGORY_ORDER.includes(c))
|
|
72
|
+
.sort(),
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
for (const cat of cats) {
|
|
76
|
+
const mods = grouped[cat];
|
|
77
|
+
if (!mods?.length) continue;
|
|
78
|
+
|
|
79
|
+
lines.push(`${YLW}${CATEGORY_LABELS[cat] ?? cat}${RESET}`);
|
|
80
|
+
const sorted = [...mods].sort((a, b) => a.name.localeCompare(b.name));
|
|
81
|
+
for (const mod of sorted) {
|
|
82
|
+
lines.push(formatCmdLine(mod));
|
|
83
|
+
}
|
|
84
|
+
lines.push("");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const total = modules.length;
|
|
88
|
+
lines.push(`${DIM}${total} commands available.${RESET}`);
|
|
89
|
+
|
|
90
|
+
return lines.join("\n");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ─── single-command detail ────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
function renderDetail(mod: ShellModule): string {
|
|
96
|
+
const lines: string[] = [];
|
|
97
|
+
|
|
98
|
+
lines.push(
|
|
99
|
+
`${BOLD}${mod.name}${RESET} — ${mod.description ?? "no description"}`,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
if (mod.aliases?.length) {
|
|
103
|
+
lines.push(`${DIM}Aliases: ${mod.aliases.join(", ")}${RESET}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
lines.push("");
|
|
107
|
+
lines.push(`${GREEN}Usage:${RESET}`);
|
|
108
|
+
if (mod.params.length) {
|
|
109
|
+
for (const p of mod.params) {
|
|
110
|
+
lines.push(` ${mod.name} ${p}`);
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
lines.push(` ${mod.name}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const catLabel =
|
|
117
|
+
CATEGORY_LABELS[mod.category ?? "misc"] ?? mod.category ?? "misc";
|
|
118
|
+
lines.push("");
|
|
119
|
+
lines.push(`${DIM}Category: ${catLabel}${RESET}`);
|
|
120
|
+
|
|
121
|
+
return lines.join("\n");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ─── export ───────────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
21
126
|
export function createHelpCommand(_getNames: () => string[]): ShellModule {
|
|
22
127
|
return {
|
|
23
128
|
name: "help",
|
|
24
|
-
description: "
|
|
129
|
+
description: "List all commands, or show usage for a specific command",
|
|
25
130
|
category: "shell",
|
|
26
131
|
params: ["[command]"],
|
|
27
132
|
run: ({ args }) => {
|
|
28
133
|
const modules = getCommandModulesPublic();
|
|
29
134
|
|
|
30
|
-
// help <command>
|
|
31
135
|
if (args[0]) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
params || ` ${mod.name}`,
|
|
42
|
-
].filter(Boolean).join("\n"),
|
|
43
|
-
exitCode: 0,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Full help — grouped by category
|
|
48
|
-
const grouped: Record<string, ShellModule[]> = {};
|
|
49
|
-
for (const mod of modules) {
|
|
50
|
-
const cat = mod.category ?? "misc";
|
|
51
|
-
if (!grouped[cat]) grouped[cat] = [];
|
|
52
|
-
grouped[cat]!.push(mod);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const lines: string[] = [];
|
|
56
|
-
lines.push("\x1b[1mAvailable commands\x1b[0m");
|
|
57
|
-
lines.push("");
|
|
58
|
-
|
|
59
|
-
const cats = [
|
|
60
|
-
...CATEGORY_ORDER.filter((c) => grouped[c]),
|
|
61
|
-
...Object.keys(grouped).filter((c) => !CATEGORY_ORDER.includes(c)),
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
for (const cat of cats) {
|
|
65
|
-
const mods = grouped[cat];
|
|
66
|
-
if (!mods || mods.length === 0) continue;
|
|
67
|
-
lines.push(`\x1b[33m${CATEGORY_LABELS[cat] ?? cat}\x1b[0m`);
|
|
68
|
-
|
|
69
|
-
const sorted = [...mods].sort((a, b) => a.name.localeCompare(b.name));
|
|
70
|
-
for (const mod of sorted) {
|
|
71
|
-
lines.push(` \x1b[36m${padRight(mod.name, 14)}\x1b[0m ${mod.description ?? ""}`);
|
|
136
|
+
const target = args[0].toLowerCase();
|
|
137
|
+
const mod = modules.find(
|
|
138
|
+
(m) => m.name === target || m.aliases?.includes(target),
|
|
139
|
+
);
|
|
140
|
+
if (!mod) {
|
|
141
|
+
return {
|
|
142
|
+
stderr: `help: no help entry for '${args[0]}'`,
|
|
143
|
+
exitCode: 1,
|
|
144
|
+
};
|
|
72
145
|
}
|
|
146
|
+
return { stdout: renderDetail(mod), exitCode: 0 };
|
|
73
147
|
}
|
|
74
148
|
|
|
75
|
-
|
|
149
|
+
return { stdout: renderFull(modules), exitCode: 0 };
|
|
76
150
|
},
|
|
77
151
|
};
|
|
78
152
|
}
|
package/src/commands/helpers.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type VirtualFileSystem from "../VirtualFileSystem";
|
|
4
|
+
import type { VirtualPackageManager } from "../VirtualPackageManager";
|
|
5
|
+
import type { VirtualShell } from "../VirtualShell";
|
|
4
6
|
|
|
5
7
|
const PROTECTED_PREFIXES = ["/virtual-env-js/.auth"] as const;
|
|
6
8
|
|
|
@@ -220,3 +222,9 @@ export function joinListWithType(
|
|
|
220
222
|
})
|
|
221
223
|
.join(" ");
|
|
222
224
|
}
|
|
225
|
+
|
|
226
|
+
export function getPackageManager(
|
|
227
|
+
shell: VirtualShell,
|
|
228
|
+
): VirtualPackageManager | undefined {
|
|
229
|
+
return shell.packageManager;
|
|
230
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ShellModule } from "../types/commands";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Display persisted command history for the session (from VFS).
|
|
5
|
+
* @category shell
|
|
6
|
+
* @params ["[n]"]
|
|
7
|
+
*/
|
|
8
|
+
export const historyCommand: ShellModule = {
|
|
9
|
+
name: "history",
|
|
10
|
+
description: "Display command history",
|
|
11
|
+
category: "shell",
|
|
12
|
+
params: ["[n]"],
|
|
13
|
+
run: ({ args, shell }) => {
|
|
14
|
+
// History is persisted in the VFS by the interactive shell
|
|
15
|
+
const histPath = "/virtual-env-js/.bash_history";
|
|
16
|
+
if (!shell.vfs.exists(histPath)) {
|
|
17
|
+
return { stdout: "", exitCode: 0 };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const raw = shell.vfs.readFile(histPath);
|
|
21
|
+
const lines = raw.split("\n").filter(Boolean);
|
|
22
|
+
|
|
23
|
+
const nArg = args[0];
|
|
24
|
+
const n = nArg ? parseInt(nArg, 10) : null;
|
|
25
|
+
const slice = n && !Number.isNaN(n) ? lines.slice(-n) : lines;
|
|
26
|
+
|
|
27
|
+
const offset = lines.length - slice.length + 1;
|
|
28
|
+
const numbered = slice.map(
|
|
29
|
+
(line, i) => `${String(offset + i).padStart(5)} ${line}`,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return { stdout: numbered.join("\n"), exitCode: 0 };
|
|
33
|
+
},
|
|
34
|
+
};
|
package/src/commands/hostname.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { ShellModule } from "../types/commands";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Print the configured hostname for the virtual shell.
|
|
5
|
+
* @category system
|
|
6
|
+
* @params []
|
|
7
|
+
*/
|
|
3
8
|
export const hostnameCommand: ShellModule = {
|
|
4
9
|
name: "hostname",
|
|
5
10
|
description: "Print hostname",
|
package/src/commands/id.ts
CHANGED
|
@@ -11,6 +11,9 @@ export const idCommand: ShellModule = {
|
|
|
11
11
|
const gid = uid;
|
|
12
12
|
const isSudo = shell.users.isSudoer(target);
|
|
13
13
|
const groups = isSudo ? `${gid}(${target}),0(root)` : `${gid}(${target})`;
|
|
14
|
-
return {
|
|
14
|
+
return {
|
|
15
|
+
stdout: `uid=${uid}(${target}) gid=${gid}(${target}) groups=${groups}`,
|
|
16
|
+
exitCode: 0,
|
|
17
|
+
};
|
|
15
18
|
},
|
|
16
19
|
};
|
package/src/commands/index.ts
CHANGED
|
@@ -1,255 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "
|
|
10
|
-
import { adduserCommand } from "./adduser";
|
|
11
|
-
import { awkCommand } from "./awk";
|
|
12
|
-
import { base64Command } from "./base64";
|
|
13
|
-
import { catCommand } from "./cat";
|
|
14
|
-
import { cdCommand } from "./cd";
|
|
15
|
-
import { chmodCommand } from "./chmod";
|
|
16
|
-
import { clearCommand } from "./clear";
|
|
17
|
-
import { cpCommand } from "./cp";
|
|
18
|
-
import { curlCommand } from "./curl";
|
|
19
|
-
import { cutCommand } from "./cut";
|
|
20
|
-
import { dateCommand } from "./date";
|
|
21
|
-
import { deluserCommand } from "./deluser";
|
|
22
|
-
import { dfCommand } from "./df";
|
|
23
|
-
import { diffCommand } from "./diff";
|
|
24
|
-
import { duCommand } from "./du";
|
|
25
|
-
import { echoCommand } from "./echo";
|
|
26
|
-
import { envCommand } from "./env";
|
|
27
|
-
import { exitCommand } from "./exit";
|
|
28
|
-
import { exportCommand } from "./export";
|
|
29
|
-
import { findCommand } from "./find";
|
|
30
|
-
import { grepCommand } from "./grep";
|
|
31
|
-
import { groupsCommand } from "./groups";
|
|
32
|
-
import { gunzipCommand, gzipCommand } from "./gzip";
|
|
33
|
-
import { headCommand } from "./head";
|
|
34
|
-
import { createHelpCommand } from "./help";
|
|
35
|
-
import { hostnameCommand } from "./hostname";
|
|
36
|
-
import { htopCommand } from "./htop";
|
|
37
|
-
import { idCommand } from "./id";
|
|
38
|
-
import { killCommand } from "./kill";
|
|
39
|
-
import { lnCommand } from "./ln";
|
|
40
|
-
import { lsCommand } from "./ls";
|
|
41
|
-
import { mkdirCommand } from "./mkdir";
|
|
42
|
-
import { mvCommand } from "./mv";
|
|
43
|
-
import { nanoCommand } from "./nano";
|
|
44
|
-
import { neofetchCommand } from "./neofetch";
|
|
45
|
-
import { passwdCommand } from "./passwd";
|
|
46
|
-
import { pingCommand } from "./ping";
|
|
47
|
-
import { psCommand } from "./ps";
|
|
48
|
-
import { pwdCommand } from "./pwd";
|
|
49
|
-
import { rmCommand } from "./rm";
|
|
50
|
-
import { sedCommand } from "./sed";
|
|
51
|
-
import { setCommand } from "./set";
|
|
52
|
-
import { shCommand } from "./sh";
|
|
53
|
-
import { sleepCommand } from "./sleep";
|
|
54
|
-
import { sortCommand } from "./sort";
|
|
55
|
-
import { suCommand } from "./su";
|
|
56
|
-
import { sudoCommand } from "./sudo";
|
|
57
|
-
import { tailCommand } from "./tail";
|
|
58
|
-
import { tarCommand } from "./tar";
|
|
59
|
-
import { teeCommand } from "./tee";
|
|
60
|
-
import { touchCommand } from "./touch";
|
|
61
|
-
import { trCommand } from "./tr";
|
|
62
|
-
import { treeCommand } from "./tree";
|
|
63
|
-
import { unameCommand } from "./uname";
|
|
64
|
-
import { uniqCommand } from "./uniq";
|
|
65
|
-
import { unsetCommand } from "./unset";
|
|
66
|
-
import { wcCommand } from "./wc";
|
|
67
|
-
import { wgetCommand } from "./wget";
|
|
68
|
-
import { whoCommand } from "./who";
|
|
69
|
-
import { whoamiCommand } from "./whoami";
|
|
70
|
-
import { xargsCommand } from "./xargs";
|
|
71
|
-
|
|
72
|
-
const BASE_COMMANDS: ShellModule[] = [
|
|
73
|
-
// Navigation
|
|
74
|
-
pwdCommand, cdCommand, lsCommand, treeCommand,
|
|
75
|
-
// Files
|
|
76
|
-
catCommand, touchCommand, rmCommand, mkdirCommand, cpCommand, mvCommand, lnCommand,
|
|
77
|
-
chmodCommand, findCommand,
|
|
78
|
-
// Text processing
|
|
79
|
-
grepCommand, sedCommand, awkCommand, sortCommand, uniqCommand, wcCommand,
|
|
80
|
-
headCommand, tailCommand, cutCommand, trCommand, teeCommand, xargsCommand,
|
|
81
|
-
diffCommand,
|
|
82
|
-
// Archives
|
|
83
|
-
tarCommand, gzipCommand, gunzipCommand, base64Command,
|
|
84
|
-
// System info
|
|
85
|
-
whoamiCommand, whoCommand, hostnameCommand, idCommand, groupsCommand, unameCommand,
|
|
86
|
-
psCommand, killCommand, dfCommand, duCommand, dateCommand, sleepCommand, pingCommand,
|
|
87
|
-
// Shell
|
|
88
|
-
echoCommand, envCommand, exportCommand, setCommand, unsetCommand, shCommand,
|
|
89
|
-
clearCommand, exitCommand,
|
|
90
|
-
// Editors
|
|
91
|
-
nanoCommand, htopCommand,
|
|
92
|
-
// Network
|
|
93
|
-
curlCommand, wgetCommand,
|
|
94
|
-
// Users
|
|
95
|
-
adduserCommand, passwdCommand, deluserCommand, sudoCommand, suCommand,
|
|
96
|
-
// Misc
|
|
97
|
-
neofetchCommand,
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
const customCommands: ShellModule[] = [];
|
|
101
|
-
const commandRegistry = new Map<string, ShellModule>();
|
|
102
|
-
let cachedCommandNames: string[] | null = null;
|
|
103
|
-
|
|
104
|
-
const helpCommand = createHelpCommand(() => getCommandModules().map((cmd) => cmd.name));
|
|
105
|
-
|
|
106
|
-
function buildCache(): void {
|
|
107
|
-
commandRegistry.clear();
|
|
108
|
-
for (const mod of getCommandModules()) {
|
|
109
|
-
commandRegistry.set(mod.name, mod);
|
|
110
|
-
for (const alias of mod.aliases ?? []) commandRegistry.set(alias, mod);
|
|
111
|
-
}
|
|
112
|
-
cachedCommandNames = Array.from(commandRegistry.keys()).sort();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function getCommandModules(): ShellModule[] {
|
|
116
|
-
return [...BASE_COMMANDS, ...customCommands, helpCommand];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function registerCommand(module: ShellModule): void {
|
|
120
|
-
const normalized: ShellModule = {
|
|
121
|
-
...module,
|
|
122
|
-
name: module.name.trim().toLowerCase(),
|
|
123
|
-
aliases: module.aliases?.map((a) => a.trim().toLowerCase()),
|
|
124
|
-
};
|
|
125
|
-
const names = [normalized.name, ...(normalized.aliases ?? [])];
|
|
126
|
-
if (names.some((n) => n.length === 0 || /\s/.test(n))) {
|
|
127
|
-
throw new Error("Command names must be non-empty and contain no spaces");
|
|
128
|
-
}
|
|
129
|
-
customCommands.push(normalized);
|
|
130
|
-
buildCache();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function createCustomCommand(
|
|
134
|
-
name: string,
|
|
135
|
-
params: string[],
|
|
136
|
-
run: (ctx: CommandContext) => CommandResult | Promise<CommandResult>,
|
|
137
|
-
): ShellModule {
|
|
138
|
-
return { name, params, run };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function getCommandNames(): string[] {
|
|
142
|
-
if (!cachedCommandNames) buildCache();
|
|
143
|
-
return cachedCommandNames!;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function getCommandModulesPublic(): ShellModule[] {
|
|
147
|
-
return getCommandModules();
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function resolveModule(name: string): ShellModule | undefined {
|
|
151
|
-
if (!cachedCommandNames) buildCache();
|
|
152
|
-
return commandRegistry.get(name.toLowerCase());
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function splitArgsRespectingQuotes(input: string): string[] {
|
|
156
|
-
const tokens: string[] = [];
|
|
157
|
-
let current = "";
|
|
158
|
-
let inQuotes = false;
|
|
159
|
-
let quoteChar = "";
|
|
160
|
-
|
|
161
|
-
for (let i = 0; i < input.length; i++) {
|
|
162
|
-
const ch = input[i] || "";
|
|
163
|
-
const prev = i > 0 ? input[i - 1] : "";
|
|
164
|
-
if ((ch === '"' || ch === "'") && prev !== "\\") {
|
|
165
|
-
if (!inQuotes) { inQuotes = true; quoteChar = ch; continue; }
|
|
166
|
-
if (ch === quoteChar) { inQuotes = false; quoteChar = ""; continue; }
|
|
167
|
-
}
|
|
168
|
-
if (/\s/.test(ch) && !inQuotes) {
|
|
169
|
-
if (current.length > 0) { tokens.push(current); current = ""; }
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
current += ch;
|
|
173
|
-
}
|
|
174
|
-
if (current.length > 0) tokens.push(current);
|
|
175
|
-
return tokens;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function parseInput(rawInput: string): { commandName: string; args: string[] } {
|
|
179
|
-
const parts = splitArgsRespectingQuotes(rawInput.trim());
|
|
180
|
-
return { commandName: parts[0]?.toLowerCase() ?? "", args: parts.slice(1) };
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export function makeDefaultEnv(authUser: string, hostname: string): ShellEnv {
|
|
184
|
-
return {
|
|
185
|
-
vars: {
|
|
186
|
-
PATH: "/usr/local/bin:/usr/bin:/bin",
|
|
187
|
-
HOME: `/home/${authUser}`,
|
|
188
|
-
USER: authUser,
|
|
189
|
-
LOGNAME: authUser,
|
|
190
|
-
SHELL: "/bin/sh",
|
|
191
|
-
TERM: "xterm-256color",
|
|
192
|
-
HOSTNAME: hostname,
|
|
193
|
-
PS1: "\\u@\\h:\\w\\$ ",
|
|
194
|
-
},
|
|
195
|
-
lastExitCode: 0,
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export async function runCommand(
|
|
200
|
-
rawInput: string,
|
|
201
|
-
authUser: string,
|
|
202
|
-
hostname: string,
|
|
203
|
-
mode: CommandMode,
|
|
204
|
-
cwd: string,
|
|
205
|
-
shell: VirtualShell,
|
|
206
|
-
stdin?: string,
|
|
207
|
-
env?: ShellEnv,
|
|
208
|
-
): Promise<CommandResult> {
|
|
209
|
-
const trimmed = rawInput.trim();
|
|
210
|
-
if (trimmed.length === 0) return { exitCode: 0 };
|
|
211
|
-
|
|
212
|
-
const shellEnv: ShellEnv = env ?? makeDefaultEnv(authUser, hostname);
|
|
213
|
-
|
|
214
|
-
// Detect shell operators
|
|
215
|
-
if (
|
|
216
|
-
/(?<![|&])[|](?![|])/.test(trimmed) ||
|
|
217
|
-
trimmed.includes(">") ||
|
|
218
|
-
trimmed.includes("<") ||
|
|
219
|
-
trimmed.includes("&&") ||
|
|
220
|
-
trimmed.includes("||") ||
|
|
221
|
-
trimmed.includes(";")
|
|
222
|
-
) {
|
|
223
|
-
const { parseScript } = await import("../VirtualShell/shellParser");
|
|
224
|
-
const { executeStatements } = await import("../SSHMimic/executor");
|
|
225
|
-
const script = parseScript(trimmed);
|
|
226
|
-
if (!script.isValid) return { stderr: script.error || "Syntax error", exitCode: 1 };
|
|
227
|
-
try {
|
|
228
|
-
return await executeStatements(script.statements, authUser, hostname, mode, cwd, shell, shellEnv);
|
|
229
|
-
} catch (error: unknown) {
|
|
230
|
-
return { stderr: error instanceof Error ? error.message : "Execution failed", exitCode: 1 };
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const { commandName, args } = parseInput(trimmed);
|
|
235
|
-
const mod = resolveModule(commandName);
|
|
236
|
-
|
|
237
|
-
if (!mod) return { stderr: `${commandName}: command not found`, exitCode: 127 };
|
|
238
|
-
|
|
239
|
-
try {
|
|
240
|
-
return await mod.run({
|
|
241
|
-
authUser,
|
|
242
|
-
hostname,
|
|
243
|
-
activeSessions: shell.users.listActiveSessions(),
|
|
244
|
-
rawInput: trimmed,
|
|
245
|
-
mode,
|
|
246
|
-
args,
|
|
247
|
-
stdin,
|
|
248
|
-
cwd,
|
|
249
|
-
shell,
|
|
250
|
-
env: shellEnv,
|
|
251
|
-
});
|
|
252
|
-
} catch (error: unknown) {
|
|
253
|
-
return { stderr: error instanceof Error ? error.message : "Command failed", exitCode: 1 };
|
|
254
|
-
}
|
|
255
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
createCustomCommand,
|
|
3
|
+
getCommandModulesPublic,
|
|
4
|
+
getCommandNames,
|
|
5
|
+
registerCommand,
|
|
6
|
+
resolveModule
|
|
7
|
+
} from "./registry";
|
|
8
|
+
|
|
9
|
+
export { makeDefaultEnv, runCommand, runCommandDirect } from "./runtime";
|