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.
Files changed (40) hide show
  1. package/README.md +107 -541
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/VirtualShell/shell.js +158 -11
  4. package/dist/commands/awk.d.ts +6 -11
  5. package/dist/commands/awk.js +462 -109
  6. package/dist/commands/bc.d.ts +2 -0
  7. package/dist/commands/bc.js +28 -0
  8. package/dist/commands/bzip2.d.ts +11 -0
  9. package/dist/commands/bzip2.js +91 -0
  10. package/dist/commands/find.d.ts +2 -2
  11. package/dist/commands/find.js +212 -37
  12. package/dist/commands/{ifconfig.d.ts → ip.d.ts} +1 -1
  13. package/dist/commands/{ifconfig.js → ip.js} +3 -3
  14. package/dist/commands/jobs.d.ts +4 -0
  15. package/dist/commands/jobs.js +27 -0
  16. package/dist/commands/lsof.d.ts +6 -0
  17. package/dist/commands/lsof.js +30 -0
  18. package/dist/commands/perl.d.ts +6 -0
  19. package/dist/commands/perl.js +76 -0
  20. package/dist/commands/registry.js +22 -3
  21. package/dist/commands/runtime.js +2 -1
  22. package/dist/commands/sed.d.ts +2 -2
  23. package/dist/commands/sed.js +216 -34
  24. package/dist/commands/set.js +20 -0
  25. package/dist/commands/sh.js +111 -1
  26. package/dist/commands/strace.d.ts +6 -0
  27. package/dist/commands/strace.js +26 -0
  28. package/dist/commands/tar.d.ts +2 -1
  29. package/dist/commands/tar.js +138 -52
  30. package/dist/commands/zip.d.ts +11 -0
  31. package/dist/commands/zip.js +232 -0
  32. package/dist/modules/linuxRootfs.js +1 -1
  33. package/dist/utils/expand.js +67 -1
  34. package/package.json +5 -4
  35. package/dist/self-standalone.d.ts +0 -1
  36. package/dist/self-standalone.js +0 -444
  37. package/dist/standalone-wo-sftp.d.ts +0 -1
  38. package/dist/standalone-wo-sftp.js +0 -30
  39. package/dist/standalone.d.ts +0 -1
  40. 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
- // Load .bashrc if it exists
26
+ // Source login/rc files at startup
26
27
  void (async () => {
27
- const bashrcPath = `${userHome(authUser)}/.bashrc`;
28
- if (shell.vfs.exists(bashrcPath)) {
28
+ const sourceFile = async (filePath, isEnvFile = false) => {
29
+ if (!shell.vfs.exists(filePath))
30
+ return;
29
31
  try {
30
- const bashrc = shell.vfs.readFile(bashrcPath);
31
- for (const line of bashrc.split("\n")) {
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
- await runCommand(l, authUser, hostname, "shell", cwd, shell, undefined, shellEnv);
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
- /* ignore bashrc errors */
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
- const line = lineBuffer.trim();
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);
@@ -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
- * Minimal awk-like pattern scanner.
4
- *
5
- * Supported:
6
- * - `NR==N` pattern (line number condition)
7
- * - `NF` (number of fields)
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;