typescript-virtual-container 1.2.7 → 1.2.9
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/README.md +457 -42
- package/dist/SSHMimic/executor.js +3 -5
- package/dist/VirtualFileSystem/binaryPack.d.ts +49 -0
- package/dist/VirtualFileSystem/binaryPack.d.ts.map +1 -0
- package/dist/VirtualFileSystem/binaryPack.js +193 -0
- package/dist/VirtualFileSystem/index.d.ts +7 -5
- package/dist/VirtualFileSystem/index.d.ts.map +1 -1
- package/dist/VirtualFileSystem/index.js +20 -9
- package/dist/VirtualPackageManager/index.d.ts +202 -0
- package/dist/VirtualPackageManager/index.d.ts.map +1 -0
- package/dist/VirtualPackageManager/index.js +676 -0
- package/dist/VirtualShell/index.d.ts +87 -12
- package/dist/VirtualShell/index.d.ts.map +1 -1
- package/dist/VirtualShell/index.js +83 -12
- 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/alias.d.ts +4 -0
- package/dist/commands/alias.d.ts.map +1 -0
- package/dist/commands/alias.js +58 -0
- package/dist/commands/apt.d.ts +4 -0
- package/dist/commands/apt.d.ts.map +1 -0
- package/dist/commands/apt.js +182 -0
- package/dist/commands/cat.d.ts.map +1 -1
- package/dist/commands/cat.js +27 -8
- package/dist/commands/chmod.d.ts.map +1 -1
- package/dist/commands/chmod.js +52 -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/curl.d.ts.map +1 -1
- package/dist/commands/curl.js +81 -29
- package/dist/commands/dpkg.d.ts +4 -0
- package/dist/commands/dpkg.d.ts.map +1 -0
- package/dist/commands/dpkg.js +144 -0
- package/dist/commands/echo.d.ts.map +1 -1
- package/dist/commands/echo.js +24 -12
- package/dist/commands/free.d.ts +3 -0
- package/dist/commands/free.d.ts.map +1 -0
- package/dist/commands/free.js +38 -0
- 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 +3 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +21 -0
- package/dist/commands/index.d.ts +8 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +120 -11
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +4 -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 +50 -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/neofetch.d.ts.map +1 -1
- package/dist/commands/neofetch.js +5 -0
- package/dist/commands/ping.d.ts.map +1 -1
- package/dist/commands/ping.js +5 -2
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +27 -6
- package/dist/commands/sh.d.ts.map +1 -1
- package/dist/commands/sh.js +29 -11
- package/dist/commands/source.d.ts +3 -0
- package/dist/commands/source.d.ts.map +1 -0
- package/dist/commands/source.js +31 -0
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +92 -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/uptime.d.ts +3 -0
- package/dist/commands/uptime.d.ts.map +1 -0
- package/dist/commands/uptime.js +40 -0
- package/dist/commands/wget.d.ts.map +1 -1
- package/dist/commands/wget.js +71 -100
- 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/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/modules/linuxRootfs.d.ts +24 -0
- package/dist/modules/linuxRootfs.d.ts.map +1 -0
- package/dist/modules/linuxRootfs.js +297 -0
- package/dist/modules/neofetch.d.ts.map +1 -1
- package/dist/modules/neofetch.js +1 -0
- package/dist/standalone.js +4 -1
- package/package.json +2 -1
- package/src/SSHMimic/executor.ts +3 -5
- package/src/VirtualFileSystem/binaryPack.ts +219 -0
- package/src/VirtualFileSystem/index.ts +21 -11
- package/src/VirtualPackageManager/index.ts +820 -0
- package/src/VirtualShell/index.ts +104 -13
- package/src/VirtualUserManager/index.ts +55 -20
- package/src/commands/alias.ts +60 -0
- package/src/commands/apt.ts +198 -0
- package/src/commands/cat.ts +32 -8
- package/src/commands/chmod.ts +48 -3
- package/src/commands/command-helpers.ts +78 -4
- package/src/commands/curl.ts +78 -37
- package/src/commands/dpkg.ts +158 -0
- package/src/commands/echo.ts +30 -14
- package/src/commands/free.ts +40 -0
- package/src/commands/helpers.ts +8 -0
- package/src/commands/history.ts +29 -0
- package/src/commands/index.ts +116 -11
- package/src/commands/ls.ts +5 -4
- package/src/commands/lsb-release.ts +52 -0
- package/src/commands/man.ts +166 -0
- package/src/commands/neofetch.ts +5 -0
- package/src/commands/ping.ts +5 -2
- package/src/commands/ps.ts +28 -6
- package/src/commands/sh.ts +33 -11
- package/src/commands/source.ts +35 -0
- package/src/commands/test.ts +100 -0
- package/src/commands/type.ts +40 -0
- package/src/commands/uptime.ts +46 -0
- package/src/commands/wget.ts +70 -123
- package/src/commands/which.ts +34 -0
- package/src/index.ts +10 -0
- package/src/modules/linuxRootfs.ts +439 -0
- package/src/modules/neofetch.ts +1 -0
- package/src/standalone.ts +4 -1
- package/standalone.js +418 -103
- package/standalone.js.map +4 -4
- package/tests/new-features.test.ts +626 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { resolvePath } from "./helpers";
|
|
2
|
+
import { runCommand } from ".";
|
|
3
|
+
export const sourceCommand = {
|
|
4
|
+
name: "source",
|
|
5
|
+
aliases: ["."],
|
|
6
|
+
description: "Execute commands from a file in the current shell environment",
|
|
7
|
+
category: "shell",
|
|
8
|
+
params: ["<file> [args...]"],
|
|
9
|
+
run: async ({ args, authUser, hostname, cwd, shell, env }) => {
|
|
10
|
+
const fileArg = args[0];
|
|
11
|
+
if (!fileArg) {
|
|
12
|
+
return { stderr: "source: missing filename", exitCode: 1 };
|
|
13
|
+
}
|
|
14
|
+
const filePath = resolvePath(cwd, fileArg);
|
|
15
|
+
if (!shell.vfs.exists(filePath)) {
|
|
16
|
+
return { stderr: `source: ${fileArg}: No such file or directory`, exitCode: 1 };
|
|
17
|
+
}
|
|
18
|
+
const content = shell.vfs.readFile(filePath);
|
|
19
|
+
let lastExitCode = 0;
|
|
20
|
+
for (const line of content.split("\n")) {
|
|
21
|
+
const l = line.trim();
|
|
22
|
+
if (!l || l.startsWith("#"))
|
|
23
|
+
continue;
|
|
24
|
+
const result = await runCommand(l, authUser, hostname, "shell", cwd, shell, undefined, env);
|
|
25
|
+
lastExitCode = result.exitCode ?? 0;
|
|
26
|
+
if (result.closeSession || result.switchUser)
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
return { exitCode: lastExitCode };
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/commands/test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAqFrD,eAAO,MAAM,WAAW,EAAE,WAczB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evaluate a POSIX test expression.
|
|
3
|
+
* Supports: -f, -d, -e, -r, -w, -x, -s, -z, -n,
|
|
4
|
+
* string =, !=, numeric -eq -ne -lt -le -gt -ge,
|
|
5
|
+
* ! (negate), -a (and), -o (or).
|
|
6
|
+
*/
|
|
7
|
+
function evalTest(tokens, shell, cwd) {
|
|
8
|
+
// When called via [ command, ] is the last arg — strip it
|
|
9
|
+
// When called via test command, no brackets present
|
|
10
|
+
if (tokens[tokens.length - 1] === "]") {
|
|
11
|
+
tokens = tokens.slice(0, -1);
|
|
12
|
+
}
|
|
13
|
+
// Also strip leading [ if present (shouldn't normally happen but be safe)
|
|
14
|
+
if (tokens[0] === "[") {
|
|
15
|
+
tokens = tokens.slice(1);
|
|
16
|
+
}
|
|
17
|
+
if (tokens.length === 0)
|
|
18
|
+
return false;
|
|
19
|
+
// Negation
|
|
20
|
+
if (tokens[0] === "!")
|
|
21
|
+
return !evalTest(tokens.slice(1), shell, cwd);
|
|
22
|
+
// Boolean -a / -o (simple left-right, no precedence)
|
|
23
|
+
const andIdx = tokens.indexOf("-a");
|
|
24
|
+
if (andIdx !== -1) {
|
|
25
|
+
return evalTest(tokens.slice(0, andIdx), shell, cwd) &&
|
|
26
|
+
evalTest(tokens.slice(andIdx + 1), shell, cwd);
|
|
27
|
+
}
|
|
28
|
+
const orIdx = tokens.indexOf("-o");
|
|
29
|
+
if (orIdx !== -1) {
|
|
30
|
+
return evalTest(tokens.slice(0, orIdx), shell, cwd) ||
|
|
31
|
+
evalTest(tokens.slice(orIdx + 1), shell, cwd);
|
|
32
|
+
}
|
|
33
|
+
// Unary file tests
|
|
34
|
+
if (tokens.length === 2) {
|
|
35
|
+
const [flag, operand = ""] = tokens;
|
|
36
|
+
const resolvePath = (p) => p.startsWith("/") ? p : `${cwd}/${p}`.replace(/\/+/g, "/");
|
|
37
|
+
const path = resolvePath(operand);
|
|
38
|
+
switch (flag) {
|
|
39
|
+
case "-e": return shell.vfs.exists(path);
|
|
40
|
+
case "-f": return shell.vfs.exists(path) && shell.vfs.stat(path).type === "file";
|
|
41
|
+
case "-d": return shell.vfs.exists(path) && shell.vfs.stat(path).type === "directory";
|
|
42
|
+
case "-r": return shell.vfs.exists(path); // all readable in virtual env
|
|
43
|
+
case "-w": return shell.vfs.exists(path);
|
|
44
|
+
case "-x": return shell.vfs.exists(path) && !!(shell.vfs.stat(path).mode & 0o111);
|
|
45
|
+
case "-s": return shell.vfs.exists(path) && shell.vfs.stat(path).type === "file" && shell.vfs.stat(path).size > 0;
|
|
46
|
+
case "-z": return operand.length === 0;
|
|
47
|
+
case "-n": return operand.length > 0;
|
|
48
|
+
case "-L": return shell.vfs.isSymlink(path);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Binary comparisons
|
|
52
|
+
if (tokens.length === 3) {
|
|
53
|
+
const [left = "", op, right = ""] = tokens;
|
|
54
|
+
const leftN = Number(left);
|
|
55
|
+
const rightN = Number(right);
|
|
56
|
+
switch (op) {
|
|
57
|
+
// String
|
|
58
|
+
case "=":
|
|
59
|
+
case "==": return left === right;
|
|
60
|
+
case "!=": return left !== right;
|
|
61
|
+
case "<": return left < right;
|
|
62
|
+
case ">": return left > right;
|
|
63
|
+
// Numeric
|
|
64
|
+
case "-eq": return leftN === rightN;
|
|
65
|
+
case "-ne": return leftN !== rightN;
|
|
66
|
+
case "-lt": return leftN < rightN;
|
|
67
|
+
case "-le": return leftN <= rightN;
|
|
68
|
+
case "-gt": return leftN > rightN;
|
|
69
|
+
case "-ge": return leftN >= rightN;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Single string (truthy if non-empty)
|
|
73
|
+
if (tokens.length === 1)
|
|
74
|
+
return (tokens[0] ?? "").length > 0;
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
export const testCommand = {
|
|
78
|
+
name: "test",
|
|
79
|
+
aliases: ["["],
|
|
80
|
+
description: "Evaluate conditional expression",
|
|
81
|
+
category: "shell",
|
|
82
|
+
params: ["<expression>"],
|
|
83
|
+
run: ({ args, shell, cwd }) => {
|
|
84
|
+
try {
|
|
85
|
+
const result = evalTest([...args], shell, cwd);
|
|
86
|
+
return { exitCode: result ? 0 : 1 };
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return { stderr: "test: malformed expression", exitCode: 2 };
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../src/commands/type.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAoCzB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { resolveModule } from ".";
|
|
2
|
+
export const typeCommand = {
|
|
3
|
+
name: "type",
|
|
4
|
+
description: "Describe how a command would be interpreted",
|
|
5
|
+
category: "shell",
|
|
6
|
+
params: ["<command...>"],
|
|
7
|
+
run: ({ args, shell, env }) => {
|
|
8
|
+
if (args.length === 0)
|
|
9
|
+
return { stderr: "type: missing argument", exitCode: 1 };
|
|
10
|
+
const pathDirs = (env?.vars?.PATH ?? "/usr/local/bin:/usr/bin:/bin").split(":");
|
|
11
|
+
const lines = [];
|
|
12
|
+
let exitCode = 0;
|
|
13
|
+
for (const name of args) {
|
|
14
|
+
if (resolveModule(name)) {
|
|
15
|
+
lines.push(`${name} is a shell builtin`);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
let found = false;
|
|
19
|
+
for (const dir of pathDirs) {
|
|
20
|
+
const full = `${dir}/${name}`;
|
|
21
|
+
if (shell.vfs.exists(full)) {
|
|
22
|
+
lines.push(`${name} is ${full}`);
|
|
23
|
+
found = true;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (!found) {
|
|
28
|
+
lines.push(`${name}: not found`);
|
|
29
|
+
exitCode = 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { stdout: lines.join("\n"), exitCode };
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uptime.d.ts","sourceRoot":"","sources":["../../src/commands/uptime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,aAAa,EAAE,WA0C3B,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ifFlag } from "./command-helpers";
|
|
2
|
+
export const uptimeCommand = {
|
|
3
|
+
name: "uptime",
|
|
4
|
+
description: "Tell how long the system has been running",
|
|
5
|
+
category: "system",
|
|
6
|
+
params: ["[-p] [-s]"],
|
|
7
|
+
run: ({ args, shell }) => {
|
|
8
|
+
const pretty = ifFlag(args, ["-p"]);
|
|
9
|
+
const since = ifFlag(args, ["-s"]);
|
|
10
|
+
const uptimeSec = Math.floor((Date.now() - shell.startTime) / 1000);
|
|
11
|
+
const days = Math.floor(uptimeSec / 86400);
|
|
12
|
+
const hours = Math.floor((uptimeSec % 86400) / 3600);
|
|
13
|
+
const mins = Math.floor((uptimeSec % 3600) / 60);
|
|
14
|
+
if (since) {
|
|
15
|
+
return {
|
|
16
|
+
stdout: new Date(shell.startTime).toISOString().slice(0, 19).replace("T", " "),
|
|
17
|
+
exitCode: 0,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (pretty) {
|
|
21
|
+
const parts = [];
|
|
22
|
+
if (days > 0)
|
|
23
|
+
parts.push(`${days} day${days > 1 ? "s" : ""}`);
|
|
24
|
+
if (hours > 0)
|
|
25
|
+
parts.push(`${hours} hour${hours > 1 ? "s" : ""}`);
|
|
26
|
+
parts.push(`${mins} minute${mins !== 1 ? "s" : ""}`);
|
|
27
|
+
return { stdout: `up ${parts.join(", ")}`, exitCode: 0 };
|
|
28
|
+
}
|
|
29
|
+
const timeStr = new Date().toTimeString().slice(0, 8);
|
|
30
|
+
const uptimeStr = days > 0
|
|
31
|
+
? `${days} day${days > 1 ? "s" : ""}, ${String(hours).padStart(2)}:${String(mins).padStart(2, "0")}`
|
|
32
|
+
: `${String(hours).padStart(2)}:${String(mins).padStart(2, "0")}`;
|
|
33
|
+
const sessions = shell.users.listActiveSessions().length;
|
|
34
|
+
const load = (Math.random() * 0.5).toFixed(2);
|
|
35
|
+
return {
|
|
36
|
+
stdout: ` ${timeStr} up ${uptimeStr}, ${sessions} user${sessions !== 1 ? "s" : ""}, load average: ${load}, ${load}, ${load}`,
|
|
37
|
+
exitCode: 0,
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wget.d.ts","sourceRoot":"","sources":["../../src/commands/wget.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wget.d.ts","sourceRoot":"","sources":["../../src/commands/wget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAwFzB,CAAC"}
|
package/dist/commands/wget.js
CHANGED
|
@@ -1,115 +1,86 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
1
|
import { ifFlag, parseArgs } from "./command-helpers";
|
|
6
|
-
import { assertPathAccess,
|
|
7
|
-
function runHostWget(args) {
|
|
8
|
-
return new Promise((resolve) => {
|
|
9
|
-
let childProcess;
|
|
10
|
-
try {
|
|
11
|
-
childProcess = spawn("wget", args, {
|
|
12
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
catch (error) {
|
|
16
|
-
resolve({
|
|
17
|
-
stdout: "",
|
|
18
|
-
stderr: `wget: ${error instanceof Error ? error.message : String(error)}`,
|
|
19
|
-
exitCode: 1,
|
|
20
|
-
});
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
let stdout = "";
|
|
24
|
-
let stderr = "";
|
|
25
|
-
const stdoutStream = childProcess.stdout;
|
|
26
|
-
const stderrStream = childProcess.stderr;
|
|
27
|
-
if (!stdoutStream || !stderrStream) {
|
|
28
|
-
resolve({
|
|
29
|
-
stdout: "",
|
|
30
|
-
stderr: "wget: failed to capture process output",
|
|
31
|
-
exitCode: 1,
|
|
32
|
-
});
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
stdoutStream.setEncoding("utf8");
|
|
36
|
-
stderrStream.setEncoding("utf8");
|
|
37
|
-
stdoutStream.on("data", (chunk) => {
|
|
38
|
-
stdout += chunk;
|
|
39
|
-
});
|
|
40
|
-
stderrStream.on("data", (chunk) => {
|
|
41
|
-
stderr += chunk;
|
|
42
|
-
});
|
|
43
|
-
childProcess.on("error", (error) => {
|
|
44
|
-
const errorCode = error instanceof Error && "code" in error
|
|
45
|
-
? String(error.code ?? "")
|
|
46
|
-
: "";
|
|
47
|
-
resolve({
|
|
48
|
-
stdout: "",
|
|
49
|
-
stderr: `wget: ${error.message}`,
|
|
50
|
-
exitCode: errorCode === "ENOENT" ? 127 : 1,
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
childProcess.on("close", (code) => {
|
|
54
|
-
resolve({
|
|
55
|
-
stdout,
|
|
56
|
-
stderr,
|
|
57
|
-
exitCode: code ?? 1,
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
}
|
|
2
|
+
import { assertPathAccess, resolvePath, stripUrlFilename } from "./helpers";
|
|
62
3
|
export const wgetCommand = {
|
|
63
4
|
name: "wget",
|
|
64
|
-
description: "File downloader",
|
|
5
|
+
description: "File downloader (pure fetch)",
|
|
65
6
|
category: "network",
|
|
66
|
-
params: ["[url
|
|
7
|
+
params: ["[options] <url>"],
|
|
67
8
|
run: async ({ authUser, cwd, args, shell }) => {
|
|
68
9
|
const { flagsWithValues, positionals } = parseArgs(args, {
|
|
69
|
-
flagsWithValue: ["-
|
|
10
|
+
flagsWithValue: ["-O", "--output-document", "-o", "--output-file", "-P", "--directory-prefix", "--tries", "--timeout"],
|
|
70
11
|
});
|
|
71
|
-
|
|
72
|
-
flagsWithValues.get("-O") ||
|
|
73
|
-
flagsWithValues.get("--output") ||
|
|
74
|
-
flagsWithValues.get("--output-document") ||
|
|
75
|
-
null;
|
|
76
|
-
const url = positionals[0];
|
|
77
|
-
if (!url) {
|
|
78
|
-
return { stderr: "wget: missing URL", exitCode: 1 };
|
|
79
|
-
}
|
|
80
|
-
const isHelpLike = ifFlag(args, ["-h", "--help", "-V", "--version"]);
|
|
81
|
-
if (isHelpLike) {
|
|
82
|
-
const result = await runHostWget(args);
|
|
12
|
+
if (ifFlag(args, ["-h", "--help"])) {
|
|
83
13
|
return {
|
|
84
|
-
stdout:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
14
|
+
stdout: [
|
|
15
|
+
"Usage: wget [option]... [URL]...",
|
|
16
|
+
" -O, --output-document=FILE Write to FILE ('-' for stdout)",
|
|
17
|
+
" -P, --directory-prefix=DIR Save files in DIR",
|
|
18
|
+
" -q, --quiet Quiet mode",
|
|
19
|
+
" -v, --verbose Verbose output (default)",
|
|
20
|
+
" -c, --continue Continue partial download",
|
|
21
|
+
" --tries=N Retry N times",
|
|
22
|
+
" --timeout=N Timeout in seconds",
|
|
23
|
+
].join("\n"),
|
|
24
|
+
exitCode: 0,
|
|
89
25
|
};
|
|
90
26
|
}
|
|
91
|
-
|
|
92
|
-
|
|
27
|
+
if (ifFlag(args, ["-V", "--version"])) {
|
|
28
|
+
return { stdout: "GNU Wget 1.21.3 (virtual) built on Fortune GNU/Linux.", exitCode: 0 };
|
|
29
|
+
}
|
|
30
|
+
const url = positionals[0];
|
|
31
|
+
if (!url)
|
|
32
|
+
return { stderr: "wget: missing URL\nUsage: wget [OPTION]... [URL]...", exitCode: 1 };
|
|
33
|
+
const outputArg = flagsWithValues.get("-O") ?? flagsWithValues.get("--output-document") ?? null;
|
|
34
|
+
const dirPrefix = flagsWithValues.get("-P") ?? flagsWithValues.get("--directory-prefix") ?? null;
|
|
35
|
+
const quiet = ifFlag(args, ["-q", "--quiet"]);
|
|
36
|
+
// Derive target filename
|
|
37
|
+
const filename = outputArg === "-" ? null : (outputArg ?? stripUrlFilename(url));
|
|
38
|
+
const targetPath = filename
|
|
39
|
+
? resolvePath(cwd, dirPrefix ? `${dirPrefix}/${filename}` : filename)
|
|
40
|
+
: null;
|
|
41
|
+
if (targetPath)
|
|
42
|
+
assertPathAccess(authUser, targetPath, "wget");
|
|
43
|
+
const stderrLines = [];
|
|
44
|
+
if (!quiet) {
|
|
45
|
+
stderrLines.push(`--${new Date().toISOString()}-- ${url}`);
|
|
46
|
+
stderrLines.push(`Resolving ${new URL(url).host}...`);
|
|
47
|
+
stderrLines.push(`Connecting to ${new URL(url).host}...`);
|
|
48
|
+
}
|
|
49
|
+
let response;
|
|
93
50
|
try {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
51
|
+
response = await fetch(url, { headers: { "User-Agent": "Wget/1.21.3 (Fortune GNU/Linux)" } });
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
55
|
+
stderrLines.push(`wget: unable to resolve host: ${msg}`);
|
|
56
|
+
return { stderr: stderrLines.join("\n"), exitCode: 4 };
|
|
57
|
+
}
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
stderrLines.push(`ERROR ${response.status}: ${response.statusText}`);
|
|
60
|
+
return { stderr: stderrLines.join("\n"), exitCode: 8 };
|
|
61
|
+
}
|
|
62
|
+
let body;
|
|
63
|
+
try {
|
|
64
|
+
body = await response.text();
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return { stderr: "wget: failed to read response", exitCode: 1 };
|
|
68
|
+
}
|
|
69
|
+
if (!quiet) {
|
|
70
|
+
const ct = response.headers.get("content-type") ?? "application/octet-stream";
|
|
71
|
+
stderrLines.push(`HTTP request sent, awaiting response... ${response.status} ${response.statusText}`);
|
|
72
|
+
stderrLines.push(`Length: ${body.length} [${ct}]`);
|
|
73
|
+
}
|
|
74
|
+
// Output to stdout (pipe) or file
|
|
75
|
+
if (outputArg === "-") {
|
|
76
|
+
return { stdout: body, stderr: stderrLines.join("\n") || undefined, exitCode: 0 };
|
|
110
77
|
}
|
|
111
|
-
|
|
112
|
-
|
|
78
|
+
if (targetPath) {
|
|
79
|
+
shell.writeFileAsUser(authUser, targetPath, body);
|
|
80
|
+
if (!quiet)
|
|
81
|
+
stderrLines.push(`Saving to: '${targetPath}'\n${targetPath} 100%[==================>] ${body.length} B`);
|
|
82
|
+
return { stderr: stderrLines.join("\n") || undefined, exitCode: 0 };
|
|
113
83
|
}
|
|
84
|
+
return { stdout: body, exitCode: 0 };
|
|
114
85
|
},
|
|
115
86
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"which.d.ts","sourceRoot":"","sources":["../../src/commands/which.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,YAAY,EAAE,WA+B1B,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const whichCommand = {
|
|
2
|
+
name: "which",
|
|
3
|
+
description: "Locate a command in PATH",
|
|
4
|
+
category: "shell",
|
|
5
|
+
params: ["<command...>"],
|
|
6
|
+
run: ({ args, shell, env }) => {
|
|
7
|
+
if (args.length === 0)
|
|
8
|
+
return { stderr: "which: missing argument", exitCode: 1 };
|
|
9
|
+
const pathDirs = (env?.vars?.PATH ?? "/usr/local/bin:/usr/bin:/bin").split(":");
|
|
10
|
+
const lines = [];
|
|
11
|
+
let anyMissing = false;
|
|
12
|
+
for (const name of args) {
|
|
13
|
+
let found = false;
|
|
14
|
+
for (const dir of pathDirs) {
|
|
15
|
+
const full = `${dir}/${name}`;
|
|
16
|
+
if (shell.vfs.exists(full)) {
|
|
17
|
+
const st = shell.vfs.stat(full);
|
|
18
|
+
if (st.type === "file") {
|
|
19
|
+
lines.push(full);
|
|
20
|
+
found = true;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!found)
|
|
26
|
+
anyMissing = true;
|
|
27
|
+
}
|
|
28
|
+
if (lines.length === 0)
|
|
29
|
+
return { exitCode: 1 };
|
|
30
|
+
return { stdout: lines.join("\n"), exitCode: anyMissing ? 1 : 0 };
|
|
31
|
+
},
|
|
32
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,11 +4,14 @@ import { SftpMimic, SshMimic } from "./SSHMimic/index";
|
|
|
4
4
|
import VirtualFileSystem from "./VirtualFileSystem/index";
|
|
5
5
|
import { VirtualShell } from "./VirtualShell/index";
|
|
6
6
|
import { VirtualUserManager } from "./VirtualUserManager/index";
|
|
7
|
+
import { VirtualPackageManager } from "./VirtualPackageManager/index";
|
|
7
8
|
export type { AuditLogEntry, HoneyPotStats, } from "./Honeypot/index";
|
|
8
|
-
export type { CommandContext, CommandMode, CommandOutcome, CommandResult, NanoEditorSession, ShellModule, SudoChallenge, } from "./types/commands";
|
|
9
|
+
export type { CommandContext, CommandMode, CommandOutcome, CommandResult, NanoEditorSession, ShellEnv, ShellModule, SudoChallenge, } from "./types/commands";
|
|
10
|
+
export type { ShellProperties } from "./VirtualShell/index";
|
|
9
11
|
export type { ExecStream, ShellStream } from "./types/streams";
|
|
10
12
|
export type { RemoveOptions, VfsBaseNode, VfsDirectoryNode, VfsFileNode, VfsNodeStats, VfsNodeType, VfsSnapshot, VfsSnapshotBaseNode, VfsSnapshotDirectoryNode, VfsSnapshotFileNode, VfsSnapshotNode, WriteFileOptions, } from "./types/vfs";
|
|
11
13
|
export type { VfsOptions, VfsPersistenceMode } from "./VirtualFileSystem/index";
|
|
12
|
-
export {
|
|
14
|
+
export type { PackageDefinition, PackageFile, InstalledPackage, } from "./VirtualPackageManager/index";
|
|
15
|
+
export { HoneyPot, SshClient, VirtualFileSystem, SftpMimic as VirtualSftpServer, VirtualShell, SshMimic as VirtualSshServer, VirtualUserManager, VirtualPackageManager, };
|
|
13
16
|
export { getArg, getFlag, ifFlag, } from "./commands/command-helpers";
|
|
14
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,iBAAiB,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,iBAAiB,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,YAAY,EACX,aAAa,EACb,aAAa,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACX,cAAc,EACd,WAAW,EACX,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,aAAa,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,YAAY,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEhF,YAAY,EACX,iBAAiB,EACjB,WAAW,EACX,gBAAgB,GAChB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACN,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,SAAS,IAAI,iBAAiB,EAC9B,YAAY,EACZ,QAAQ,IAAI,gBAAgB,EAC5B,kBAAkB,EAClB,qBAAqB,GACrB,CAAC;AAEF,OAAO,EACN,MAAM,EACN,OAAO,EACP,MAAM,GACN,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,6 @@ import { SftpMimic, SshMimic } from "./SSHMimic/index";
|
|
|
4
4
|
import VirtualFileSystem from "./VirtualFileSystem/index";
|
|
5
5
|
import { VirtualShell } from "./VirtualShell/index";
|
|
6
6
|
import { VirtualUserManager } from "./VirtualUserManager/index";
|
|
7
|
-
|
|
7
|
+
import { VirtualPackageManager } from "./VirtualPackageManager/index";
|
|
8
|
+
export { HoneyPot, SshClient, VirtualFileSystem, SftpMimic as VirtualSftpServer, VirtualShell, SshMimic as VirtualSshServer, VirtualUserManager, VirtualPackageManager, };
|
|
8
9
|
export { getArg, getFlag, ifFlag, } from "./commands/command-helpers";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* linuxRootfs.ts
|
|
3
|
+
*
|
|
4
|
+
* Bootstraps a realistic Linux directory hierarchy in the VFS.
|
|
5
|
+
* Called once during VirtualShell initialization. Idempotent — skips
|
|
6
|
+
* paths that already exist so FS-mode snapshots survive restarts.
|
|
7
|
+
*/
|
|
8
|
+
import type { ShellProperties } from "../VirtualShell";
|
|
9
|
+
import type VirtualFileSystem from "../VirtualFileSystem";
|
|
10
|
+
import type { VirtualUserManager } from "../VirtualUserManager";
|
|
11
|
+
export declare function syncEtcPasswd(vfs: VirtualFileSystem, users: VirtualUserManager): void;
|
|
12
|
+
export declare function refreshProc(vfs: VirtualFileSystem, props: ShellProperties, hostname: string, shellStartTime: number): void;
|
|
13
|
+
/**
|
|
14
|
+
* Bootstraps the full Linux rootfs hierarchy in the VFS.
|
|
15
|
+
* Safe to call multiple times — idempotent.
|
|
16
|
+
*
|
|
17
|
+
* @param vfs Target virtual filesystem.
|
|
18
|
+
* @param users User manager (for /etc/passwd sync).
|
|
19
|
+
* @param hostname Virtual hostname.
|
|
20
|
+
* @param props Shell properties (kernel, os, arch).
|
|
21
|
+
* @param shellStartTime Unix ms of shell creation (for uptime).
|
|
22
|
+
*/
|
|
23
|
+
export declare function bootstrapLinuxRootfs(vfs: VirtualFileSystem, users: VirtualUserManager, hostname: string, props: ShellProperties, shellStartTime: number): void;
|
|
24
|
+
//# sourceMappingURL=linuxRootfs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linuxRootfs.d.ts","sourceRoot":"","sources":["../../src/modules/linuxRootfs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAyHhE,wBAAgB,aAAa,CAC5B,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,kBAAkB,GACvB,IAAI,CAsCN;AAID,wBAAgB,WAAW,CAC1B,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACpB,IAAI,CA6DN;AAuKD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,eAAe,EACtB,cAAc,EAAE,MAAM,GACpB,IAAI,CAYN"}
|