typescript-virtual-container 1.5.4 → 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -541
- package/dist/.tsbuildinfo +1 -1
- package/dist/VirtualShell/shell.js +158 -11
- package/dist/commands/awk.d.ts +6 -11
- package/dist/commands/awk.js +462 -109
- package/dist/commands/bc.d.ts +2 -0
- package/dist/commands/bc.js +28 -0
- package/dist/commands/bzip2.d.ts +11 -0
- package/dist/commands/bzip2.js +91 -0
- package/dist/commands/find.d.ts +2 -2
- package/dist/commands/find.js +212 -37
- package/dist/commands/{ifconfig.d.ts → ip.d.ts} +1 -1
- package/dist/commands/{ifconfig.js → ip.js} +3 -3
- package/dist/commands/jobs.d.ts +4 -0
- package/dist/commands/jobs.js +27 -0
- package/dist/commands/lsof.d.ts +6 -0
- package/dist/commands/lsof.js +30 -0
- package/dist/commands/perl.d.ts +6 -0
- package/dist/commands/perl.js +76 -0
- package/dist/commands/registry.js +22 -3
- package/dist/commands/runtime.js +2 -1
- package/dist/commands/sed.d.ts +2 -2
- package/dist/commands/sed.js +216 -34
- package/dist/commands/set.js +20 -0
- package/dist/commands/sh.js +111 -1
- package/dist/commands/strace.d.ts +6 -0
- package/dist/commands/strace.js +26 -0
- package/dist/commands/tar.d.ts +2 -1
- package/dist/commands/tar.js +138 -52
- package/dist/commands/zip.d.ts +11 -0
- package/dist/commands/zip.js +232 -0
- package/dist/modules/linuxRootfs.js +1 -1
- package/dist/utils/expand.js +67 -1
- package/package.json +5 -4
- package/dist/self-standalone.d.ts +0 -1
- package/dist/self-standalone.js +0 -444
- package/dist/standalone-wo-sftp.d.ts +0 -1
- package/dist/standalone-wo-sftp.js +0 -30
- package/dist/standalone.d.ts +0 -1
- package/dist/standalone.js +0 -61
|
@@ -12,6 +12,7 @@ export function startShell(properties, stream, authUser, hostname, sessionId, re
|
|
|
12
12
|
let historyIndex = null;
|
|
13
13
|
let historyDraft = "";
|
|
14
14
|
let cwd = userHome(authUser);
|
|
15
|
+
let pendingHeredoc = null;
|
|
15
16
|
const shellEnv = makeDefaultEnv(authUser, hostname);
|
|
16
17
|
let nanoSession = null;
|
|
17
18
|
let pendingSudo = null;
|
|
@@ -22,23 +23,33 @@ export function startShell(properties, stream, authUser, hostname, sessionId, re
|
|
|
22
23
|
};
|
|
23
24
|
const commandNames = Array.from(new Set(getCommandNames())).sort();
|
|
24
25
|
console.log(`[${sessionId}] Shell started for user '${authUser}' at ${remoteAddress}`);
|
|
25
|
-
//
|
|
26
|
+
// Source login/rc files at startup
|
|
26
27
|
void (async () => {
|
|
27
|
-
const
|
|
28
|
-
|
|
28
|
+
const sourceFile = async (filePath, isEnvFile = false) => {
|
|
29
|
+
if (!shell.vfs.exists(filePath))
|
|
30
|
+
return;
|
|
29
31
|
try {
|
|
30
|
-
const
|
|
31
|
-
for (const line of
|
|
32
|
+
const content = shell.vfs.readFile(filePath);
|
|
33
|
+
for (const line of content.split("\n")) {
|
|
32
34
|
const l = line.trim();
|
|
33
35
|
if (!l || l.startsWith("#"))
|
|
34
36
|
continue;
|
|
35
|
-
|
|
37
|
+
if (isEnvFile) {
|
|
38
|
+
// /etc/environment: KEY=VALUE pairs only, no shell syntax
|
|
39
|
+
const m = l.match(/^([A-Za-z_][A-Za-z0-9_]*)=["']?(.+?)["']?\s*$/);
|
|
40
|
+
if (m)
|
|
41
|
+
shellEnv.vars[m[1]] = m[2];
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
await runCommand(l, authUser, hostname, "shell", cwd, shell, undefined, shellEnv);
|
|
45
|
+
}
|
|
36
46
|
}
|
|
37
47
|
}
|
|
38
|
-
catch {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
48
|
+
catch { /* ignore */ }
|
|
49
|
+
};
|
|
50
|
+
await sourceFile("/etc/environment", true);
|
|
51
|
+
await sourceFile(`${userHome(authUser)}/.profile`);
|
|
52
|
+
await sourceFile(`${userHome(authUser)}/.bashrc`);
|
|
42
53
|
})();
|
|
43
54
|
function renderLine() {
|
|
44
55
|
const prompt = buildCurrentPrompt();
|
|
@@ -283,6 +294,53 @@ export function startShell(properties, stream, authUser, hostname, sessionId, re
|
|
|
283
294
|
nanoSession.process.stdin.write(chunk);
|
|
284
295
|
return;
|
|
285
296
|
}
|
|
297
|
+
if (pendingHeredoc) {
|
|
298
|
+
const hd = pendingHeredoc;
|
|
299
|
+
const input = chunk.toString("utf8");
|
|
300
|
+
for (let i = 0; i < input.length; i++) {
|
|
301
|
+
const ch = input[i];
|
|
302
|
+
if (ch === "") {
|
|
303
|
+
pendingHeredoc = null;
|
|
304
|
+
stream.write("^C\r\n");
|
|
305
|
+
renderLine();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (ch === "" || ch === "\b") {
|
|
309
|
+
lineBuffer = lineBuffer.slice(0, -1);
|
|
310
|
+
renderLine();
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (ch === "\r" || ch === "\n") {
|
|
314
|
+
const typedLine = lineBuffer;
|
|
315
|
+
lineBuffer = "";
|
|
316
|
+
cursorPos = 0;
|
|
317
|
+
stream.write("\r\n");
|
|
318
|
+
if (typedLine === hd.delimiter) {
|
|
319
|
+
const stdin = hd.lines.join("\n");
|
|
320
|
+
const cmd = hd.cmdBefore;
|
|
321
|
+
pendingHeredoc = null;
|
|
322
|
+
pushHistory(`${cmd} << ${hd.delimiter}`);
|
|
323
|
+
const result = await Promise.resolve(runCommand(cmd, authUser, hostname, "shell", cwd, shell, stdin, shellEnv));
|
|
324
|
+
if (result.stdout)
|
|
325
|
+
stream.write(`${toTtyLines(result.stdout)}\r\n`);
|
|
326
|
+
if (result.stderr)
|
|
327
|
+
stream.write(`${toTtyLines(result.stderr)}\r\n`);
|
|
328
|
+
if (result.nextCwd)
|
|
329
|
+
cwd = result.nextCwd;
|
|
330
|
+
renderLine();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
hd.lines.push(typedLine);
|
|
334
|
+
stream.write("> ");
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (ch >= " " || ch === "\t") {
|
|
338
|
+
lineBuffer += ch;
|
|
339
|
+
stream.write(ch);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
286
344
|
if (pendingSudo) {
|
|
287
345
|
const input = chunk.toString("utf8");
|
|
288
346
|
for (let i = 0; i < input.length; i += 1) {
|
|
@@ -407,6 +465,47 @@ export function startShell(properties, stream, authUser, hostname, sessionId, re
|
|
|
407
465
|
}
|
|
408
466
|
continue;
|
|
409
467
|
}
|
|
468
|
+
// Home: \x1b[1~ or \x1b[H
|
|
469
|
+
if (third === "1" && fourth === "~") {
|
|
470
|
+
i += 3;
|
|
471
|
+
cursorPos = 0;
|
|
472
|
+
renderLine();
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
if (third === "H") {
|
|
476
|
+
i += 2;
|
|
477
|
+
cursorPos = 0;
|
|
478
|
+
renderLine();
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
// End: \x1b[4~ or \x1b[F
|
|
482
|
+
if (third === "4" && fourth === "~") {
|
|
483
|
+
i += 3;
|
|
484
|
+
cursorPos = lineBuffer.length;
|
|
485
|
+
renderLine();
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (third === "F") {
|
|
489
|
+
i += 2;
|
|
490
|
+
cursorPos = lineBuffer.length;
|
|
491
|
+
renderLine();
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// Home/End via \x1bO sequences (some terminals)
|
|
496
|
+
if (next === "O" && third) {
|
|
497
|
+
if (third === "H") {
|
|
498
|
+
i += 2;
|
|
499
|
+
cursorPos = 0;
|
|
500
|
+
renderLine();
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
if (third === "F") {
|
|
504
|
+
i += 2;
|
|
505
|
+
cursorPos = lineBuffer.length;
|
|
506
|
+
renderLine();
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
410
509
|
}
|
|
411
510
|
}
|
|
412
511
|
if (ch === "\u0003") {
|
|
@@ -418,13 +517,61 @@ export function startShell(properties, stream, authUser, hostname, sessionId, re
|
|
|
418
517
|
renderLine();
|
|
419
518
|
continue;
|
|
420
519
|
}
|
|
520
|
+
if (ch === "\u0001") {
|
|
521
|
+
cursorPos = 0;
|
|
522
|
+
renderLine();
|
|
523
|
+
continue;
|
|
524
|
+
} // Ctrl+A
|
|
525
|
+
if (ch === "\u0005") {
|
|
526
|
+
cursorPos = lineBuffer.length;
|
|
527
|
+
renderLine();
|
|
528
|
+
continue;
|
|
529
|
+
} // Ctrl+E
|
|
530
|
+
if (ch === "\u000b") {
|
|
531
|
+
lineBuffer = lineBuffer.slice(0, cursorPos);
|
|
532
|
+
renderLine();
|
|
533
|
+
continue;
|
|
534
|
+
} // Ctrl+K
|
|
535
|
+
if (ch === "\u0015") {
|
|
536
|
+
lineBuffer = lineBuffer.slice(cursorPos);
|
|
537
|
+
cursorPos = 0;
|
|
538
|
+
renderLine();
|
|
539
|
+
continue;
|
|
540
|
+
} // Ctrl+U
|
|
541
|
+
if (ch === "\u0017") { // Ctrl+W — kill word backward
|
|
542
|
+
let wStart = cursorPos;
|
|
543
|
+
while (wStart > 0 && lineBuffer[wStart - 1] === " ")
|
|
544
|
+
wStart--;
|
|
545
|
+
while (wStart > 0 && lineBuffer[wStart - 1] !== " ")
|
|
546
|
+
wStart--;
|
|
547
|
+
lineBuffer = lineBuffer.slice(0, wStart) + lineBuffer.slice(cursorPos);
|
|
548
|
+
cursorPos = wStart;
|
|
549
|
+
renderLine();
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
421
552
|
if (ch === "\r" || ch === "\n") {
|
|
422
|
-
|
|
553
|
+
let line = lineBuffer.trim();
|
|
423
554
|
lineBuffer = "";
|
|
424
555
|
cursorPos = 0;
|
|
425
556
|
historyIndex = null;
|
|
426
557
|
historyDraft = "";
|
|
427
558
|
stream.write("\r\n");
|
|
559
|
+
// !! history expansion
|
|
560
|
+
if (line === "!!" || line.startsWith("!! ") || /\s!!$/.test(line) || / !! /.test(line)) {
|
|
561
|
+
const lastCmd = history.length > 0 ? history[history.length - 1] : "";
|
|
562
|
+
line = line === "!!" ? lastCmd : line.replace(/!!/g, lastCmd);
|
|
563
|
+
}
|
|
564
|
+
else if (/(?:^|\s)!!/.test(line)) {
|
|
565
|
+
const lastCmd = history.length > 0 ? history[history.length - 1] : "";
|
|
566
|
+
line = line.replace(/!!/g, lastCmd);
|
|
567
|
+
}
|
|
568
|
+
// Heredoc detection: cmd << DELIM
|
|
569
|
+
const heredocMatch = line.match(/^(.*?)\s*<<-?\s*['"`]?([A-Za-z_][A-Za-z0-9_]*)['"`]?\s*$/);
|
|
570
|
+
if (heredocMatch && line.length > 0) {
|
|
571
|
+
pendingHeredoc = { delimiter: heredocMatch[2], lines: [], cmdBefore: heredocMatch[1].trim() || "cat" };
|
|
572
|
+
stream.write("> ");
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
428
575
|
if (line.length > 0) {
|
|
429
576
|
const result = await Promise.resolve(runCommand(line, authUser, hostname, "shell", cwd, shell, undefined, shellEnv));
|
|
430
577
|
pushHistory(line);
|
package/dist/commands/awk.d.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
+
/** biome-ignore-all lint/style/useNamingConvention: AWK vars (NR, NF, FS, OFS, ORS) are spec names */
|
|
1
2
|
import type { ShellModule } from "../types/commands";
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - `/regex/` pattern
|
|
9
|
-
* - `{ print $N, $M, ... }` action
|
|
10
|
-
* - `{ print }` / `{ print $0 }`
|
|
11
|
-
* - `BEGIN { ... }` and `END { ... }` blocks (no side effects)
|
|
12
|
-
* - `$NF` (last field)
|
|
13
|
-
* - `-F sep` field separator
|
|
4
|
+
* AWK pattern scanning — supports BEGIN/END, /regex/, NR/NF conditions, field ops,
|
|
5
|
+
* variable assignment (-v), arithmetic, string functions (sub/gsub/substr/split/length/index/sprintf/tolower/toupper/match),
|
|
6
|
+
* printf, getline (from stdin), next, and multi-statement blocks.
|
|
7
|
+
* @category text
|
|
8
|
+
* @params ["[-F sep] [-v var=val] '<program>' [file]"]
|
|
14
9
|
*/
|
|
15
10
|
export declare const awkCommand: ShellModule;
|