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
package/dist/commands/index.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
/** biome-ignore-all lint/style/useNamingConvention: ENV VARIABLES */
|
|
2
|
+
import { executeStatements } from "../SSHMimic/executor";
|
|
3
|
+
import { parseScript } from "../VirtualShell/shellParser";
|
|
1
4
|
import { adduserCommand } from "./adduser";
|
|
5
|
+
import { aliasCommand, unaliasCommand } from "./alias";
|
|
6
|
+
import { testCommand } from "./test";
|
|
7
|
+
import { sourceCommand } from "./source";
|
|
8
|
+
import { historyCommand } from "./history";
|
|
9
|
+
import { aptCacheCommand, aptCommand } from "./apt";
|
|
2
10
|
import { awkCommand } from "./awk";
|
|
3
11
|
import { base64Command } from "./base64";
|
|
4
12
|
import { catCommand } from "./cat";
|
|
@@ -12,12 +20,14 @@ import { dateCommand } from "./date";
|
|
|
12
20
|
import { deluserCommand } from "./deluser";
|
|
13
21
|
import { dfCommand } from "./df";
|
|
14
22
|
import { diffCommand } from "./diff";
|
|
23
|
+
import { dpkgCommand, dpkgQueryCommand } from "./dpkg";
|
|
15
24
|
import { duCommand } from "./du";
|
|
16
25
|
import { echoCommand } from "./echo";
|
|
17
26
|
import { envCommand } from "./env";
|
|
18
27
|
import { exitCommand } from "./exit";
|
|
19
28
|
import { exportCommand } from "./export";
|
|
20
29
|
import { findCommand } from "./find";
|
|
30
|
+
import { freeCommand } from "./free";
|
|
21
31
|
import { grepCommand } from "./grep";
|
|
22
32
|
import { groupsCommand } from "./groups";
|
|
23
33
|
import { gunzipCommand, gzipCommand } from "./gzip";
|
|
@@ -29,6 +39,8 @@ import { idCommand } from "./id";
|
|
|
29
39
|
import { killCommand } from "./kill";
|
|
30
40
|
import { lnCommand } from "./ln";
|
|
31
41
|
import { lsCommand } from "./ls";
|
|
42
|
+
import { lsbReleaseCommand } from "./lsb-release";
|
|
43
|
+
import { manCommand } from "./man";
|
|
32
44
|
import { mkdirCommand } from "./mkdir";
|
|
33
45
|
import { mvCommand } from "./mv";
|
|
34
46
|
import { nanoCommand } from "./nano";
|
|
@@ -51,11 +63,14 @@ import { teeCommand } from "./tee";
|
|
|
51
63
|
import { touchCommand } from "./touch";
|
|
52
64
|
import { trCommand } from "./tr";
|
|
53
65
|
import { treeCommand } from "./tree";
|
|
66
|
+
import { typeCommand } from "./type";
|
|
54
67
|
import { unameCommand } from "./uname";
|
|
55
68
|
import { uniqCommand } from "./uniq";
|
|
56
69
|
import { unsetCommand } from "./unset";
|
|
70
|
+
import { uptimeCommand } from "./uptime";
|
|
57
71
|
import { wcCommand } from "./wc";
|
|
58
72
|
import { wgetCommand } from "./wget";
|
|
73
|
+
import { whichCommand } from "./which";
|
|
59
74
|
import { whoCommand } from "./who";
|
|
60
75
|
import { whoamiCommand } from "./whoami";
|
|
61
76
|
import { xargsCommand } from "./xargs";
|
|
@@ -85,6 +100,13 @@ const BASE_COMMANDS = [
|
|
|
85
100
|
adduserCommand, passwdCommand, deluserCommand, sudoCommand, suCommand,
|
|
86
101
|
// Misc
|
|
87
102
|
neofetchCommand,
|
|
103
|
+
// Package management
|
|
104
|
+
aptCommand, aptCacheCommand, dpkgCommand, dpkgQueryCommand,
|
|
105
|
+
// Shell (extended)
|
|
106
|
+
whichCommand, typeCommand, manCommand, aliasCommand, unaliasCommand,
|
|
107
|
+
testCommand, sourceCommand, historyCommand,
|
|
108
|
+
// System (extended)
|
|
109
|
+
uptimeCommand, freeCommand, lsbReleaseCommand,
|
|
88
110
|
];
|
|
89
111
|
const customCommands = [];
|
|
90
112
|
const commandRegistry = new Map();
|
|
@@ -183,21 +205,108 @@ export function makeDefaultEnv(authUser, hostname) {
|
|
|
183
205
|
lastExitCode: 0,
|
|
184
206
|
};
|
|
185
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Execute a pre-parsed command directly by name and argument list.
|
|
210
|
+
*
|
|
211
|
+
* Unlike `runCommand`, this function does NOT re-join name+args into a string
|
|
212
|
+
* and re-parse — so arguments that contain special characters (`;`, `|`, `>`,
|
|
213
|
+
* quotes) are passed through verbatim. Use this from the pipeline executor.
|
|
214
|
+
*/
|
|
215
|
+
export async function runCommandDirect(name, args, authUser, hostname, mode, cwd, shell, stdin, env) {
|
|
216
|
+
// Alias expansion on the command name
|
|
217
|
+
const aliasVal = env.vars[`__alias_${name}`];
|
|
218
|
+
if (aliasVal) {
|
|
219
|
+
// Alias may expand to a multi-word command — re-route through runCommand
|
|
220
|
+
return runCommand(`${aliasVal} ${args.join(" ")}`, authUser, hostname, mode, cwd, shell, stdin, env);
|
|
221
|
+
}
|
|
222
|
+
const mod = resolveModule(name);
|
|
223
|
+
if (!mod)
|
|
224
|
+
return { stderr: `${name}: command not found`, exitCode: 127 };
|
|
225
|
+
try {
|
|
226
|
+
return await mod.run({
|
|
227
|
+
authUser,
|
|
228
|
+
hostname,
|
|
229
|
+
activeSessions: shell.users.listActiveSessions(),
|
|
230
|
+
rawInput: [name, ...args].join(" "),
|
|
231
|
+
mode,
|
|
232
|
+
args,
|
|
233
|
+
stdin,
|
|
234
|
+
cwd,
|
|
235
|
+
shell,
|
|
236
|
+
env,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
return { stderr: error instanceof Error ? error.message : "Command failed", exitCode: 1 };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
186
243
|
export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell, stdin, env) {
|
|
187
244
|
const trimmed = rawInput.trim();
|
|
188
245
|
if (trimmed.length === 0)
|
|
189
246
|
return { exitCode: 0 };
|
|
190
247
|
const shellEnv = env ?? makeDefaultEnv(authUser, hostname);
|
|
248
|
+
// ── $(cmd) command substitution ──────────────────────────────────────────
|
|
249
|
+
let expanded = trimmed;
|
|
250
|
+
if (expanded.includes("$(")) {
|
|
251
|
+
// Only substitute $(…) that are NOT inside single quotes
|
|
252
|
+
// Strategy: walk char by char, track single-quote state
|
|
253
|
+
let result = "";
|
|
254
|
+
let inSingle = false;
|
|
255
|
+
let i = 0;
|
|
256
|
+
while (i < expanded.length) {
|
|
257
|
+
const ch = expanded[i];
|
|
258
|
+
if (ch === "'" && !inSingle) {
|
|
259
|
+
inSingle = true;
|
|
260
|
+
result += ch;
|
|
261
|
+
i++;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (ch === "'" && inSingle) {
|
|
265
|
+
inSingle = false;
|
|
266
|
+
result += ch;
|
|
267
|
+
i++;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (!inSingle && ch === "$" && expanded[i + 1] === "(") {
|
|
271
|
+
// Find matching closing )
|
|
272
|
+
let depth = 0;
|
|
273
|
+
let j = i + 1;
|
|
274
|
+
while (j < expanded.length) {
|
|
275
|
+
if (expanded[j] === "(")
|
|
276
|
+
depth++;
|
|
277
|
+
else if (expanded[j] === ")") {
|
|
278
|
+
depth--;
|
|
279
|
+
if (depth === 0)
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
j++;
|
|
283
|
+
}
|
|
284
|
+
const sub = expanded.slice(i + 2, j).trim();
|
|
285
|
+
const subResult = await runCommand(sub, authUser, hostname, mode, cwd, shell, undefined, shellEnv);
|
|
286
|
+
const subOut = (subResult.stdout ?? "").replace(/\n$/, "");
|
|
287
|
+
result += subOut;
|
|
288
|
+
i = j + 1;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
result += ch;
|
|
292
|
+
i++;
|
|
293
|
+
}
|
|
294
|
+
expanded = result;
|
|
295
|
+
}
|
|
296
|
+
// ── alias expansion ───────────────────────────────────────────────────────
|
|
297
|
+
const firstWord = expanded.split(/\s+/)[0] ?? "";
|
|
298
|
+
const aliasVal = shellEnv.vars[`__alias_${firstWord}`];
|
|
299
|
+
if (aliasVal) {
|
|
300
|
+
expanded = expanded.replace(firstWord, aliasVal);
|
|
301
|
+
}
|
|
191
302
|
// Detect shell operators
|
|
192
|
-
if (/(?<![|&])[|](?![|])/.test(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
const { executeStatements } = await import("../SSHMimic/executor");
|
|
200
|
-
const script = parseScript(trimmed);
|
|
303
|
+
if (/(?<![|&])[|](?![|])/.test(expanded) ||
|
|
304
|
+
expanded.includes(">") ||
|
|
305
|
+
expanded.includes("<") ||
|
|
306
|
+
expanded.includes("&&") ||
|
|
307
|
+
expanded.includes("||") ||
|
|
308
|
+
expanded.includes(";")) {
|
|
309
|
+
const script = parseScript(expanded);
|
|
201
310
|
if (!script.isValid)
|
|
202
311
|
return { stderr: script.error || "Syntax error", exitCode: 1 };
|
|
203
312
|
try {
|
|
@@ -207,7 +316,7 @@ export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell,
|
|
|
207
316
|
return { stderr: error instanceof Error ? error.message : "Execution failed", exitCode: 1 };
|
|
208
317
|
}
|
|
209
318
|
}
|
|
210
|
-
const { commandName, args } = parseInput(
|
|
319
|
+
const { commandName, args } = parseInput(expanded);
|
|
211
320
|
const mod = resolveModule(commandName);
|
|
212
321
|
if (!mod)
|
|
213
322
|
return { stderr: `${commandName}: command not found`, exitCode: 127 };
|
|
@@ -216,7 +325,7 @@ export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell,
|
|
|
216
325
|
authUser,
|
|
217
326
|
hostname,
|
|
218
327
|
activeSessions: shell.users.listActiveSessions(),
|
|
219
|
-
rawInput:
|
|
328
|
+
rawInput: expanded,
|
|
220
329
|
mode,
|
|
221
330
|
args,
|
|
222
331
|
stdin,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../src/commands/ls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA4BrD,eAAO,MAAM,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../src/commands/ls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA4BrD,eAAO,MAAM,SAAS,EAAE,WA0BvB,CAAC"}
|
package/dist/commands/ls.js
CHANGED
|
@@ -25,15 +25,16 @@ export const lsCommand = {
|
|
|
25
25
|
name: "ls",
|
|
26
26
|
description: "List directory contents",
|
|
27
27
|
category: "navigation",
|
|
28
|
-
params: ["[path]"],
|
|
28
|
+
params: ["[-la] [path]"],
|
|
29
29
|
run: ({ authUser, shell, cwd, args }) => {
|
|
30
30
|
const longFormat = ifFlag(args, ["-l", "--long"]);
|
|
31
|
-
const
|
|
31
|
+
const showHidden = ifFlag(args, ["-a", "--all"]);
|
|
32
|
+
const targetArg = getArg(args, 0, { flags: ["-l", "--long", "-a", "--all", "-la", "-al"] });
|
|
32
33
|
const target = resolvePath(cwd, targetArg ?? cwd);
|
|
33
34
|
assertPathAccess(authUser, target, "ls");
|
|
34
35
|
const items = shell.vfs
|
|
35
36
|
.list(target)
|
|
36
|
-
.filter((name) => !name.startsWith("."));
|
|
37
|
+
.filter((name) => showHidden || !name.startsWith("."));
|
|
37
38
|
const rendered = longFormat
|
|
38
39
|
? items
|
|
39
40
|
.map((name) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lsb-release.d.ts","sourceRoot":"","sources":["../../src/commands/lsb-release.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,iBAAiB,EAAE,WAgD/B,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ifFlag } from "./command-helpers";
|
|
2
|
+
export const lsbReleaseCommand = {
|
|
3
|
+
name: "lsb_release",
|
|
4
|
+
description: "Print distribution-specific information",
|
|
5
|
+
category: "system",
|
|
6
|
+
params: ["[-a] [-i] [-d] [-r] [-c]"],
|
|
7
|
+
run: ({ args, shell }) => {
|
|
8
|
+
let osName = shell.properties?.os ?? "Fortune GNU/Linux x64";
|
|
9
|
+
let codename = "aurora";
|
|
10
|
+
let version = "1.0";
|
|
11
|
+
try {
|
|
12
|
+
const content = shell.vfs.readFile("/etc/os-release");
|
|
13
|
+
for (const line of content.split("\n")) {
|
|
14
|
+
if (line.startsWith("PRETTY_NAME="))
|
|
15
|
+
osName = line.slice("PRETTY_NAME=".length).replace(/^"|"$/g, "").trim();
|
|
16
|
+
if (line.startsWith("VERSION_CODENAME="))
|
|
17
|
+
codename = line.slice("VERSION_CODENAME=".length).trim();
|
|
18
|
+
if (line.startsWith("VERSION_ID="))
|
|
19
|
+
version = line.slice("VERSION_ID=".length).replace(/^"|"$/g, "").trim();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch { }
|
|
23
|
+
const all = ifFlag(args, ["-a", "--all"]);
|
|
24
|
+
const showId = ifFlag(args, ["-i", "--id"]);
|
|
25
|
+
const showDesc = ifFlag(args, ["-d", "--description"]);
|
|
26
|
+
const showRelease = ifFlag(args, ["-r", "--release"]);
|
|
27
|
+
const showCodename = ifFlag(args, ["-c", "--codename"]);
|
|
28
|
+
if (all || args.length === 0) {
|
|
29
|
+
return {
|
|
30
|
+
stdout: [
|
|
31
|
+
`Distributor ID:\tFortune`,
|
|
32
|
+
`Description:\t${osName}`,
|
|
33
|
+
`Release:\t${version}`,
|
|
34
|
+
`Codename:\t${codename}`,
|
|
35
|
+
].join("\n"),
|
|
36
|
+
exitCode: 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const lines = [];
|
|
40
|
+
if (showId)
|
|
41
|
+
lines.push(`Distributor ID:\tFortune`);
|
|
42
|
+
if (showDesc)
|
|
43
|
+
lines.push(`Description:\t${osName}`);
|
|
44
|
+
if (showRelease)
|
|
45
|
+
lines.push(`Release:\t${version}`);
|
|
46
|
+
if (showCodename)
|
|
47
|
+
lines.push(`Codename:\t${codename}`);
|
|
48
|
+
return { stdout: lines.join("\n"), exitCode: 0 };
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"man.d.ts","sourceRoot":"","sources":["../../src/commands/man.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAiJrD,eAAO,MAAM,UAAU,EAAE,WAoBxB,CAAC"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const MAN_PAGES = {
|
|
2
|
+
ls: `LS(1) User Commands LS(1)
|
|
3
|
+
|
|
4
|
+
NAME
|
|
5
|
+
ls - list directory contents
|
|
6
|
+
|
|
7
|
+
SYNOPSIS
|
|
8
|
+
ls [OPTION]... [FILE]...
|
|
9
|
+
|
|
10
|
+
DESCRIPTION
|
|
11
|
+
List information about the FILEs (the current directory by default).
|
|
12
|
+
|
|
13
|
+
OPTIONS
|
|
14
|
+
-l use a long listing format
|
|
15
|
+
-a do not ignore entries starting with .
|
|
16
|
+
-h with -l, print human readable sizes
|
|
17
|
+
-r reverse order while sorting
|
|
18
|
+
-t sort by modification time
|
|
19
|
+
|
|
20
|
+
AUTHOR
|
|
21
|
+
Written by Richard M. Stallman and David MacKenzie.`,
|
|
22
|
+
cat: `CAT(1) User Commands CAT(1)
|
|
23
|
+
|
|
24
|
+
NAME
|
|
25
|
+
cat - concatenate files and print on the standard output
|
|
26
|
+
|
|
27
|
+
SYNOPSIS
|
|
28
|
+
cat [OPTION]... [FILE]...
|
|
29
|
+
|
|
30
|
+
DESCRIPTION
|
|
31
|
+
Concatenate FILE(s) to standard output.
|
|
32
|
+
|
|
33
|
+
OPTIONS
|
|
34
|
+
-n, --number number all output lines
|
|
35
|
+
-b, --number-nonblank number nonempty output lines`,
|
|
36
|
+
grep: `GREP(1) User Commands GREP(1)
|
|
37
|
+
|
|
38
|
+
NAME
|
|
39
|
+
grep, egrep, fgrep - print lines that match patterns
|
|
40
|
+
|
|
41
|
+
SYNOPSIS
|
|
42
|
+
grep [OPTION]... PATTERNS [FILE]...
|
|
43
|
+
|
|
44
|
+
OPTIONS
|
|
45
|
+
-i, --ignore-case ignore case distinctions in patterns and data
|
|
46
|
+
-v, --invert-match select non-matching lines
|
|
47
|
+
-n, --line-number print line number with output lines
|
|
48
|
+
-r, --recursive read all files under each directory, recursively`,
|
|
49
|
+
apt: `APT(8) APT APT(8)
|
|
50
|
+
|
|
51
|
+
NAME
|
|
52
|
+
apt - command-line interface
|
|
53
|
+
|
|
54
|
+
SYNOPSIS
|
|
55
|
+
apt [options] command
|
|
56
|
+
|
|
57
|
+
DESCRIPTION
|
|
58
|
+
apt provides a high-level commandline interface for the package
|
|
59
|
+
management system.
|
|
60
|
+
|
|
61
|
+
COMMANDS
|
|
62
|
+
install pkg... Install packages
|
|
63
|
+
remove pkg... Remove packages
|
|
64
|
+
update Download package information
|
|
65
|
+
upgrade Upgrade installed packages
|
|
66
|
+
search term Search in package descriptions
|
|
67
|
+
show pkg Show package information
|
|
68
|
+
list List packages`,
|
|
69
|
+
ssh: `SSH(1) OpenSSH SSH(1)
|
|
70
|
+
|
|
71
|
+
NAME
|
|
72
|
+
ssh - OpenSSH remote login client
|
|
73
|
+
|
|
74
|
+
SYNOPSIS
|
|
75
|
+
ssh [-p port] [user@]hostname [command]
|
|
76
|
+
|
|
77
|
+
DESCRIPTION
|
|
78
|
+
ssh (SSH client) is a program for logging into a remote machine and
|
|
79
|
+
for executing commands on a remote machine.`,
|
|
80
|
+
curl: `CURL(1) User Commands CURL(1)
|
|
81
|
+
|
|
82
|
+
NAME
|
|
83
|
+
curl - transfer a URL
|
|
84
|
+
|
|
85
|
+
SYNOPSIS
|
|
86
|
+
curl [options / URLs]
|
|
87
|
+
|
|
88
|
+
DESCRIPTION
|
|
89
|
+
curl is a tool for transferring data with URL syntax.
|
|
90
|
+
|
|
91
|
+
OPTIONS
|
|
92
|
+
-o, --output <file> Write output to <file>
|
|
93
|
+
-X, --request <method> Specify request method
|
|
94
|
+
-d, --data <data> HTTP POST data
|
|
95
|
+
-H, --header <header> Pass custom header
|
|
96
|
+
-s, --silent Silent mode
|
|
97
|
+
-I, --head Show document info only
|
|
98
|
+
-L, --location Follow redirects
|
|
99
|
+
-v, --verbose Make the operation more talkative`,
|
|
100
|
+
chmod: `CHMOD(1) User Commands CHMOD(1)
|
|
101
|
+
|
|
102
|
+
NAME
|
|
103
|
+
chmod - change file mode bits
|
|
104
|
+
|
|
105
|
+
SYNOPSIS
|
|
106
|
+
chmod [OPTION]... MODE[,MODE]... FILE...
|
|
107
|
+
chmod [OPTION]... OCTAL-MODE FILE...
|
|
108
|
+
|
|
109
|
+
DESCRIPTION
|
|
110
|
+
Change the file mode bits of each given file according to MODE.
|
|
111
|
+
|
|
112
|
+
EXAMPLES
|
|
113
|
+
chmod 755 script.sh rwxr-xr-x
|
|
114
|
+
chmod 644 file.txt rw-r--r--
|
|
115
|
+
chmod +x script.sh add execute permission`,
|
|
116
|
+
tar: `TAR(1) GNU tar Manual TAR(1)
|
|
117
|
+
|
|
118
|
+
NAME
|
|
119
|
+
tar - an archiving utility
|
|
120
|
+
|
|
121
|
+
SYNOPSIS
|
|
122
|
+
tar [OPTION...] [FILE]...
|
|
123
|
+
|
|
124
|
+
DESCRIPTION
|
|
125
|
+
tar saves many files together into a single tape or disk archive,
|
|
126
|
+
and can restore individual files from the archive.
|
|
127
|
+
|
|
128
|
+
OPTIONS
|
|
129
|
+
-c, --create create a new archive
|
|
130
|
+
-x, --extract extract files from an archive
|
|
131
|
+
-z, --gzip filter the archive through gzip
|
|
132
|
+
-f, --file=ARCHIVE use archive file or device ARCHIVE
|
|
133
|
+
-v, --verbose verbosely list files processed
|
|
134
|
+
-t, --list list the contents of an archive`,
|
|
135
|
+
};
|
|
136
|
+
export const manCommand = {
|
|
137
|
+
name: "man",
|
|
138
|
+
description: "Interface to the system reference manuals",
|
|
139
|
+
category: "shell",
|
|
140
|
+
params: ["<command>"],
|
|
141
|
+
run: ({ args, shell }) => {
|
|
142
|
+
const name = args[0];
|
|
143
|
+
if (!name)
|
|
144
|
+
return { stderr: "What manual page do you want?", exitCode: 1 };
|
|
145
|
+
// VFS-installed man pages take priority
|
|
146
|
+
const manPath = `/usr/share/man/man1/${name}.1`;
|
|
147
|
+
if (shell.vfs.exists(manPath)) {
|
|
148
|
+
return { stdout: shell.vfs.readFile(manPath), exitCode: 0 };
|
|
149
|
+
}
|
|
150
|
+
const page = MAN_PAGES[name.toLowerCase()];
|
|
151
|
+
if (page)
|
|
152
|
+
return { stdout: page, exitCode: 0 };
|
|
153
|
+
return { stderr: `No manual entry for ${name}`, exitCode: 16 };
|
|
154
|
+
},
|
|
155
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neofetch.d.ts","sourceRoot":"","sources":["../../src/commands/neofetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"neofetch.d.ts","sourceRoot":"","sources":["../../src/commands/neofetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,eAAe,EAAE,WAsC7B,CAAC"}
|
|
@@ -27,6 +27,11 @@ export const neofetchCommand = {
|
|
|
27
27
|
shell: env.SHELL,
|
|
28
28
|
shellProps: shell.properties,
|
|
29
29
|
terminal: env.TERM,
|
|
30
|
+
uptimeSeconds: Math.floor((Date.now() - shell.startTime) / 1000),
|
|
31
|
+
packages: (() => {
|
|
32
|
+
const count = shell.packageManager?.installedCount() ?? 0;
|
|
33
|
+
return `${count} (dpkg)`;
|
|
34
|
+
})(),
|
|
30
35
|
}),
|
|
31
36
|
exitCode: 0,
|
|
32
37
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ping.d.ts","sourceRoot":"","sources":["../../src/commands/ping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"ping.d.ts","sourceRoot":"","sources":["../../src/commands/ping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WAmBzB,CAAC"}
|
package/dist/commands/ping.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { parseArgs } from "./command-helpers";
|
|
1
2
|
export const pingCommand = {
|
|
2
3
|
name: "ping",
|
|
3
4
|
description: "Send ICMP ECHO_REQUEST (mock)",
|
|
4
5
|
category: "network",
|
|
5
6
|
params: ["[-c <count>] <host>"],
|
|
6
7
|
run: ({ args }) => {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
8
|
+
const { flagsWithValues, positionals } = parseArgs(args, { flagsWithValue: ["-c", "-i", "-W"] });
|
|
9
|
+
const host = positionals[0] ?? "localhost";
|
|
10
|
+
const countArg = flagsWithValues.get("-c");
|
|
11
|
+
const count = countArg ? Math.max(1, parseInt(countArg, 10) || 4) : 4;
|
|
9
12
|
const lines = [`PING ${host}: 56 data bytes`];
|
|
10
13
|
for (let i = 0; i < count; i++) {
|
|
11
14
|
const ms = (Math.random() * 10 + 1).toFixed(3);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ps.d.ts","sourceRoot":"","sources":["../../src/commands/ps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"ps.d.ts","sourceRoot":"","sources":["../../src/commands/ps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,SAAS,EAAE,WAqCvB,CAAC"}
|
package/dist/commands/ps.js
CHANGED
|
@@ -1,17 +1,38 @@
|
|
|
1
|
+
import { ifFlag } from "./command-helpers";
|
|
1
2
|
export const psCommand = {
|
|
2
3
|
name: "ps",
|
|
3
4
|
description: "Report process status",
|
|
4
5
|
category: "system",
|
|
5
|
-
params: ["[-a] [-u] [-x]"],
|
|
6
|
-
run: ({ authUser, shell }) => {
|
|
6
|
+
params: ["[-a] [-u] [-x] [aux]"],
|
|
7
|
+
run: ({ authUser, shell, args }) => {
|
|
7
8
|
const sessions = shell.users.listActiveSessions();
|
|
8
|
-
const
|
|
9
|
+
const showUser = ifFlag(args, ["-u"]) || args.includes("u") || args.includes("aux") || args.includes("au");
|
|
10
|
+
const showAll = ifFlag(args, ["-a", "-x"]) || args.includes("a") || args.includes("aux");
|
|
11
|
+
if (showUser) {
|
|
12
|
+
const header = "USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND";
|
|
13
|
+
const rows = [header];
|
|
14
|
+
let pid = 1000;
|
|
15
|
+
for (const s of sessions) {
|
|
16
|
+
const user = s.username.padEnd(10).slice(0, 10);
|
|
17
|
+
const mem = (Math.random() * 0.5).toFixed(1);
|
|
18
|
+
const vsz = Math.floor(Math.random() * 20000 + 5000);
|
|
19
|
+
const rss = Math.floor(Math.random() * 5000 + 1000);
|
|
20
|
+
rows.push(`${user} ${String(pid).padStart(6)} 0.0 ${mem.padStart(4)} ${String(vsz).padStart(6)} ${String(rss).padStart(5)} ${s.tty.padEnd(8)} Ss 00:00 0:00 bash`);
|
|
21
|
+
pid++;
|
|
22
|
+
}
|
|
23
|
+
rows.push(`root ${String(pid).padStart(6)} 0.0 0.0 0 0 ? S 00:00 0:00 ps`);
|
|
24
|
+
return { stdout: rows.join("\n"), exitCode: 0 };
|
|
25
|
+
}
|
|
26
|
+
const header = " PID TTY TIME CMD";
|
|
27
|
+
const rows = [header];
|
|
9
28
|
let pid = 1000;
|
|
10
29
|
for (const s of sessions) {
|
|
11
|
-
|
|
30
|
+
if (!showAll && s.username !== authUser)
|
|
31
|
+
continue;
|
|
32
|
+
rows.push(`${String(pid).padStart(5)} ${s.tty.padEnd(12)} 00:00:00 ${s.username === authUser ? "bash" : `bash (${s.username})`}`);
|
|
12
33
|
pid++;
|
|
13
34
|
}
|
|
14
|
-
|
|
15
|
-
return { stdout:
|
|
35
|
+
rows.push(`${String(pid).padStart(5)} pts/0 00:00:00 ps`);
|
|
36
|
+
return { stdout: rows.join("\n"), exitCode: 0 };
|
|
16
37
|
},
|
|
17
38
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sh.d.ts","sourceRoot":"","sources":["../../src/commands/sh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiC,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"sh.d.ts","sourceRoot":"","sources":["../../src/commands/sh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiC,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAqMpF,eAAO,MAAM,SAAS,EAAE,WA+BvB,CAAC"}
|
package/dist/commands/sh.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ifFlag } from "./command-helpers";
|
|
2
2
|
import { resolvePath } from "./helpers";
|
|
3
3
|
import { runCommand } from "./index";
|
|
4
|
-
/** Expand $VAR and ${VAR:-default} in a line using the current env */
|
|
5
|
-
function
|
|
4
|
+
/** Expand $VAR and ${VAR:-default} in a line using the current env (sync, no $(cmd)) */
|
|
5
|
+
function expandVarsSync(line, env, lastExit) {
|
|
6
6
|
return line
|
|
7
7
|
.replace(/\$\?/g, String(lastExit))
|
|
8
8
|
.replace(/\$\{([^}:]+):-([^}]*)\}/g, (_, n, d) => env[n] ?? d)
|
|
@@ -10,6 +10,24 @@ function expandVars(line, env, lastExit) {
|
|
|
10
10
|
.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, n) => env[n] ?? "")
|
|
11
11
|
.replace(/^~(\/|$)/, `${env.HOME ?? "/home/user"}$1`);
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Expand $VAR, ${VAR:-default}, and $(cmd) substitution asynchronously.
|
|
15
|
+
* Used before executing each line in sh script context.
|
|
16
|
+
*/
|
|
17
|
+
async function expandVars(line, env, lastExit, ctx) {
|
|
18
|
+
// $(cmd) substitution first
|
|
19
|
+
if (line.includes("$(")) {
|
|
20
|
+
const subRe = /\$\(([^)]+)\)/g;
|
|
21
|
+
const matches = [...line.matchAll(subRe)];
|
|
22
|
+
for (const m of matches) {
|
|
23
|
+
const sub = m[1]?.trim() ?? "";
|
|
24
|
+
const subResult = await runCommand(sub, ctx.authUser, ctx.hostname, ctx.mode, ctx.cwd, ctx.shell, undefined, ctx.env);
|
|
25
|
+
const subOut = (subResult.stdout ?? "").replace(/\n$/, "");
|
|
26
|
+
line = line.replace(m[0], subOut);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return expandVarsSync(line, env, lastExit);
|
|
30
|
+
}
|
|
13
31
|
/** Very small shell interpreter: supports if/elif/else/fi, for/do/done, while/do/done */
|
|
14
32
|
function parseBlocks(lines) {
|
|
15
33
|
const blocks = [];
|
|
@@ -57,8 +75,8 @@ function parseBlocks(lines) {
|
|
|
57
75
|
const body = [];
|
|
58
76
|
i++;
|
|
59
77
|
while (i < lines.length && lines[i]?.trim() !== "done") {
|
|
60
|
-
const l = lines[i].trim();
|
|
61
|
-
if (l !== "do")
|
|
78
|
+
const l = lines[i].trim().replace(/^do\s+/, "");
|
|
79
|
+
if (l && l !== "do")
|
|
62
80
|
body.push(l);
|
|
63
81
|
i++;
|
|
64
82
|
}
|
|
@@ -73,8 +91,8 @@ function parseBlocks(lines) {
|
|
|
73
91
|
const body = [];
|
|
74
92
|
i++;
|
|
75
93
|
while (i < lines.length && lines[i]?.trim() !== "done") {
|
|
76
|
-
const l = lines[i].trim();
|
|
77
|
-
if (l !== "do")
|
|
94
|
+
const l = lines[i].trim().replace(/^do\s+/, "");
|
|
95
|
+
if (l && l !== "do")
|
|
78
96
|
body.push(l);
|
|
79
97
|
i++;
|
|
80
98
|
}
|
|
@@ -88,7 +106,7 @@ function parseBlocks(lines) {
|
|
|
88
106
|
return blocks;
|
|
89
107
|
}
|
|
90
108
|
async function evalCondition(cond, ctx) {
|
|
91
|
-
const expanded = expandVars(cond, ctx.env.vars, ctx.env.lastExitCode);
|
|
109
|
+
const expanded = await expandVars(cond, ctx.env.vars, ctx.env.lastExitCode, ctx);
|
|
92
110
|
// test -f / test -d / [ ... ]
|
|
93
111
|
const testMatch = expanded.match(/^\[?\s*(.+?)\s*\]?$/);
|
|
94
112
|
if (testMatch) {
|
|
@@ -146,7 +164,7 @@ async function runBlocks(blocks, ctx) {
|
|
|
146
164
|
let output = "";
|
|
147
165
|
for (const block of blocks) {
|
|
148
166
|
if (block.type === "cmd") {
|
|
149
|
-
const expanded = expandVars(block.line, ctx.env.vars, ctx.env.lastExitCode);
|
|
167
|
+
const expanded = await expandVars(block.line, ctx.env.vars, ctx.env.lastExitCode, ctx);
|
|
150
168
|
const r = await runCommand(expanded, ctx.authUser, ctx.hostname, ctx.mode, ctx.cwd, ctx.shell, undefined, ctx.env);
|
|
151
169
|
ctx.env.lastExitCode = r.exitCode ?? 0;
|
|
152
170
|
if (r.stdout)
|
|
@@ -181,7 +199,7 @@ async function runBlocks(blocks, ctx) {
|
|
|
181
199
|
}
|
|
182
200
|
}
|
|
183
201
|
else if (block.type === "for") {
|
|
184
|
-
const listExpanded = expandVars(block.list, ctx.env.vars, ctx.env.lastExitCode);
|
|
202
|
+
const listExpanded = await expandVars(block.list, ctx.env.vars, ctx.env.lastExitCode, ctx);
|
|
185
203
|
const items = listExpanded.trim().split(/\s+/);
|
|
186
204
|
for (const item of items) {
|
|
187
205
|
ctx.env.vars[block.var] = item;
|
|
@@ -216,7 +234,7 @@ export const shCommand = {
|
|
|
216
234
|
const { args, shell, cwd } = ctx;
|
|
217
235
|
// sh -c "inline script"
|
|
218
236
|
if (ifFlag(args, "-c")) {
|
|
219
|
-
const script =
|
|
237
|
+
const script = args[args.indexOf("-c") + 1] ?? "";
|
|
220
238
|
if (!script)
|
|
221
239
|
return { stderr: "sh: -c requires a script", exitCode: 1 };
|
|
222
240
|
const lines = script.split(/[;\n]/).map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/commands/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,aAAa,EAAE,WA8B3B,CAAC"}
|