typescript-virtual-container 1.5.11 → 1.6.1

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 (132) hide show
  1. package/README.md +236 -456
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/Honeypot/index.d.ts +9 -0
  4. package/dist/Honeypot/index.js +57 -0
  5. package/dist/SSHMimic/exec.d.ts +4 -0
  6. package/dist/SSHMimic/exec.js +4 -0
  7. package/dist/SSHMimic/executor.d.ts +10 -1
  8. package/dist/SSHMimic/executor.js +18 -8
  9. package/dist/SSHMimic/hostKey.d.ts +5 -0
  10. package/dist/SSHMimic/hostKey.js +5 -0
  11. package/dist/SSHMimic/loginBanner.d.ts +7 -0
  12. package/dist/SSHMimic/loginBanner.js +4 -0
  13. package/dist/SSHMimic/loginFormat.d.ts +4 -0
  14. package/dist/SSHMimic/loginFormat.js +4 -0
  15. package/dist/SSHMimic/prompt.d.ts +9 -0
  16. package/dist/SSHMimic/prompt.js +9 -0
  17. package/dist/SSHMimic/scp.d.ts +18 -0
  18. package/dist/SSHMimic/scp.js +14 -0
  19. package/dist/VirtualFileSystem/binaryPack.d.ts +7 -3
  20. package/dist/VirtualFileSystem/binaryPack.js +32 -10
  21. package/dist/VirtualFileSystem/index.d.ts +29 -0
  22. package/dist/VirtualFileSystem/index.js +126 -5
  23. package/dist/VirtualFileSystem/internalTypes.d.ts +4 -0
  24. package/dist/VirtualFileSystem/journal.d.ts +10 -4
  25. package/dist/VirtualFileSystem/journal.js +12 -2
  26. package/dist/VirtualFileSystem/path.d.ts +23 -1
  27. package/dist/VirtualFileSystem/path.js +23 -3
  28. package/dist/VirtualPackageManager/index.js +1 -1
  29. package/dist/VirtualShell/index.d.ts +3 -0
  30. package/dist/VirtualShell/index.js +12 -3
  31. package/dist/VirtualUserManager/index.d.ts +20 -1
  32. package/dist/VirtualUserManager/index.js +52 -15
  33. package/dist/commands/bc.d.ts +5 -0
  34. package/dist/commands/bc.js +5 -0
  35. package/dist/commands/cat.js +2 -2
  36. package/dist/commands/chgrp.d.ts +7 -0
  37. package/dist/commands/chgrp.js +42 -0
  38. package/dist/commands/chown.d.ts +7 -0
  39. package/dist/commands/chown.js +79 -0
  40. package/dist/commands/cp.js +4 -3
  41. package/dist/commands/dd.d.ts +7 -0
  42. package/dist/commands/dd.js +60 -0
  43. package/dist/commands/declare.js +0 -2
  44. package/dist/commands/expr.d.ts +7 -0
  45. package/dist/commands/expr.js +63 -0
  46. package/dist/commands/fun.d.ts +5 -0
  47. package/dist/commands/fun.js +5 -1
  48. package/dist/commands/help.d.ts +5 -0
  49. package/dist/commands/help.js +5 -0
  50. package/dist/commands/helpers.d.ts +43 -0
  51. package/dist/commands/helpers.js +61 -0
  52. package/dist/commands/id.d.ts +5 -0
  53. package/dist/commands/id.js +5 -0
  54. package/dist/commands/ip.d.ts +1 -0
  55. package/dist/commands/ip.js +50 -23
  56. package/dist/commands/jobs.js +43 -9
  57. package/dist/commands/kill.d.ts +1 -0
  58. package/dist/commands/kill.js +13 -5
  59. package/dist/commands/last.js +1 -1
  60. package/dist/commands/ln.d.ts +5 -0
  61. package/dist/commands/ln.js +5 -0
  62. package/dist/commands/ls.d.ts +5 -0
  63. package/dist/commands/ls.js +19 -4
  64. package/dist/commands/lsb-release.js +1 -1
  65. package/dist/commands/man.d.ts +5 -0
  66. package/dist/commands/man.js +5 -0
  67. package/dist/commands/manuals-bundle.js +242 -0
  68. package/dist/commands/miscutils.d.ts +43 -0
  69. package/dist/commands/miscutils.js +233 -0
  70. package/dist/commands/mkdir.js +3 -2
  71. package/dist/commands/mv.js +4 -3
  72. package/dist/commands/netcat.d.ts +7 -0
  73. package/dist/commands/netcat.js +64 -0
  74. package/dist/commands/nice.d.ts +7 -0
  75. package/dist/commands/nice.js +22 -0
  76. package/dist/commands/nohup.d.ts +7 -0
  77. package/dist/commands/nohup.js +18 -0
  78. package/dist/commands/ping.d.ts +2 -1
  79. package/dist/commands/ping.js +46 -8
  80. package/dist/commands/procUtils.d.ts +13 -0
  81. package/dist/commands/procUtils.js +72 -0
  82. package/dist/commands/pwd.d.ts +5 -0
  83. package/dist/commands/pwd.js +5 -0
  84. package/dist/commands/python.js +0 -4
  85. package/dist/commands/read.js +0 -1
  86. package/dist/commands/registry.d.ts +37 -0
  87. package/dist/commands/registry.js +73 -0
  88. package/dist/commands/rm.js +3 -2
  89. package/dist/commands/runtime.d.ts +47 -1
  90. package/dist/commands/runtime.js +60 -5
  91. package/dist/commands/sh.d.ts +5 -0
  92. package/dist/commands/sh.js +5 -0
  93. package/dist/commands/stat.js +3 -2
  94. package/dist/commands/strace.js +0 -1
  95. package/dist/commands/sysinfo.d.ts +19 -0
  96. package/dist/commands/sysinfo.js +73 -0
  97. package/dist/commands/test.d.ts +5 -0
  98. package/dist/commands/test.js +5 -0
  99. package/dist/commands/textutils.d.ts +25 -0
  100. package/dist/commands/textutils.js +171 -0
  101. package/dist/commands/top.d.ts +7 -0
  102. package/dist/commands/top.js +54 -0
  103. package/dist/commands/touch.js +6 -2
  104. package/dist/commands/tr.d.ts +5 -0
  105. package/dist/commands/tr.js +5 -0
  106. package/dist/commands/w.js +1 -1
  107. package/dist/commands/which.d.ts +5 -0
  108. package/dist/commands/which.js +5 -0
  109. package/dist/index.d.ts +10 -2
  110. package/dist/index.js +4 -0
  111. package/dist/modules/VirtualNetworkManager.d.ts +123 -0
  112. package/dist/modules/VirtualNetworkManager.js +201 -0
  113. package/dist/modules/linuxRootfs.d.ts +4 -3
  114. package/dist/modules/linuxRootfs.js +115 -74
  115. package/dist/modules/neofetch.d.ts +2 -0
  116. package/dist/modules/neofetch.js +3 -2
  117. package/dist/modules/pacmanGame.d.ts +2 -0
  118. package/dist/modules/pacmanGame.js +1 -0
  119. package/dist/modules/shellInteractive.d.ts +2 -0
  120. package/dist/modules/shellInteractive.js +2 -0
  121. package/dist/modules/shellRuntime.d.ts +7 -0
  122. package/dist/modules/shellRuntime.js +6 -0
  123. package/dist/modules/webTermRenderer.js +0 -7
  124. package/dist/types/commands.d.ts +1 -1
  125. package/dist/types/vfs.d.ts +8 -0
  126. package/dist/utils/argv.d.ts +22 -3
  127. package/dist/utils/argv.js +22 -3
  128. package/dist/utils/perfLogger.d.ts +10 -2
  129. package/dist/utils/perfLogger.js +7 -14
  130. package/dist/utils/shellSession.d.ts +35 -0
  131. package/dist/utils/shellSession.js +35 -0
  132. package/package.json +1 -1
@@ -1,10 +1,12 @@
1
1
  import type { ShellStream } from "../types/streams";
2
2
  import type { TerminalSize } from "./shellRuntime";
3
+ /** Configuration options for creating a PacmanGame instance. */
3
4
  export interface PacmanGameOptions {
4
5
  stream: ShellStream;
5
6
  terminalSize: TerminalSize;
6
7
  onExit: () => void;
7
8
  }
9
+ /** Classic Pacman game that runs in the terminal with ANSI rendering. */
8
10
  export declare class PacmanGame {
9
11
  private stream;
10
12
  private onExit;
@@ -92,6 +92,7 @@ const DR = [0, 1, 0, -1];
92
92
  const DC = [1, 0, -1, 0];
93
93
  const OPP = [2, 3, 0, 1];
94
94
  // ── PacmanGame ────────────────────────────────────────────────────────────────
95
+ /** Classic Pacman game that runs in the terminal with ANSI rendering. */
95
96
  export class PacmanGame {
96
97
  stream;
97
98
  onExit;
@@ -1,5 +1,7 @@
1
1
  import { type ChildProcessWithoutNullStreams } from "node:child_process";
2
2
  import type { ShellStream } from "../types/streams";
3
3
  import { type TerminalSize } from "./shellRuntime";
4
+ /** Spawns the nano editor as an interactive subprocess. */
4
5
  export declare function spawnNanoEditorProcess(tempPath: string, terminalSize: TerminalSize, stream: ShellStream): ChildProcessWithoutNullStreams;
6
+ /** Spawns htop as an interactive subprocess, filtered to the given PIDs. */
5
7
  export declare function spawnHtopProcess(pidList: string, terminalSize: TerminalSize, stream: ShellStream): ChildProcessWithoutNullStreams;
@@ -18,9 +18,11 @@ function spawnScriptProcess(command, terminalSize, stream) {
18
18
  });
19
19
  return proc;
20
20
  }
21
+ /** Spawns the nano editor as an interactive subprocess. */
21
22
  export function spawnNanoEditorProcess(tempPath, terminalSize, stream) {
22
23
  return spawnScriptProcess(`nano -- ${shellQuote(tempPath)}`, terminalSize, stream);
23
24
  }
25
+ /** Spawns htop as an interactive subprocess, filtered to the given PIDs. */
24
26
  export function spawnHtopProcess(pidList, terminalSize, stream) {
25
27
  return spawnScriptProcess(`htop -p ${shellQuote(pidList)}`, terminalSize, stream);
26
28
  }
@@ -1,10 +1,17 @@
1
+ /** Terminal dimensions (columns × rows). */
1
2
  export interface TerminalSize {
2
3
  cols: number;
3
4
  rows: number;
4
5
  }
6
+ /** Shell-escapes a string for safe use in a command line. */
5
7
  export declare function shellQuote(value: string): string;
8
+ /** Converts line endings to CRLF for TTY output. */
6
9
  export declare function toTtyLines(text: string): string;
10
+ /** Wraps a command to run with a specific terminal size via stty. */
7
11
  export declare function withTerminalSize(command: string, terminalSize: TerminalSize): string;
12
+ /** Resolves a path relative to a base working directory. */
8
13
  export declare function resolvePath(base: string, inputPath: string): string;
14
+ /** Recursively collects all child PIDs of a given parent process. */
9
15
  export declare function collectChildPids(parentPid: number): Promise<number[]>;
16
+ /** Returns a comma-separated PID list visible in htop, or null if none. */
10
17
  export declare function getVisibleHtopPidList(rootPid?: number): Promise<string | null>;
@@ -1,14 +1,17 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import * as path from "node:path";
3
+ /** Shell-escapes a string for safe use in a command line. */
3
4
  export function shellQuote(value) {
4
5
  return `'${value.replace(/'/g, `'\\''`)}'`;
5
6
  }
7
+ /** Converts line endings to CRLF for TTY output. */
6
8
  export function toTtyLines(text) {
7
9
  return text
8
10
  .replace(/\r\n/g, "\n")
9
11
  .replace(/\r/g, "\n")
10
12
  .replace(/\n/g, "\r\n");
11
13
  }
14
+ /** Wraps a command to run with a specific terminal size via stty. */
12
15
  export function withTerminalSize(command, terminalSize) {
13
16
  const cols = Number.isFinite(terminalSize.cols) && terminalSize.cols > 0
14
17
  ? Math.floor(terminalSize.cols)
@@ -18,6 +21,7 @@ export function withTerminalSize(command, terminalSize) {
18
21
  : 24;
19
22
  return `stty cols ${cols} rows ${rows} 2>/dev/null; ${command}`;
20
23
  }
24
+ /** Resolves a path relative to a base working directory. */
21
25
  export function resolvePath(base, inputPath) {
22
26
  if (!inputPath || inputPath.trim() === "" || inputPath === ".") {
23
27
  return base;
@@ -26,6 +30,7 @@ export function resolvePath(base, inputPath) {
26
30
  ? path.posix.normalize(inputPath)
27
31
  : path.posix.normalize(path.posix.join(base, inputPath));
28
32
  }
33
+ /** Recursively collects all child PIDs of a given parent process. */
29
34
  export async function collectChildPids(parentPid) {
30
35
  try {
31
36
  const childrenRaw = await readFile(`/proc/${parentPid}/task/${parentPid}/children`, "utf8");
@@ -42,6 +47,7 @@ export async function collectChildPids(parentPid) {
42
47
  return [];
43
48
  }
44
49
  }
50
+ /** Returns a comma-separated PID list visible in htop, or null if none. */
45
51
  export async function getVisibleHtopPidList(rootPid = process.pid) {
46
52
  const descendants = await collectChildPids(rootPid);
47
53
  const unique = Array.from(new Set(descendants)).sort((a, b) => a - b);
@@ -392,13 +392,6 @@ export class WebTermRenderer {
392
392
  return html;
393
393
  }
394
394
  }
395
- // const ANSI_NORMAL_TO_BRIGHT: Record<string, string> = {
396
- // "#000": "#555", "#c00": "#f55", "#0c0": "#5f5", "#cc0": "#ff5",
397
- // "#00c": "#55f", "#c0c": "#f5f", "#0cc": "#5ff", "#ccc": "#fff",
398
- // };
399
- // function boldBright(fg: string): string {
400
- // return ANSI_NORMAL_TO_BRIGHT[fg] ?? fg;
401
- // }
402
395
  function escHtml(ch) {
403
396
  if (ch === "&")
404
397
  return "&amp;";
@@ -1,5 +1,5 @@
1
1
  /** Command invocation mode used by shell runtime. */
2
- export type CommandMode = "shell" | "exec";
2
+ export type CommandMode = "shell" | "exec" | "background";
3
3
  import type { VirtualShell } from "../VirtualShell";
4
4
  import type { VirtualActiveSession } from "../VirtualUserManager";
5
5
  /**
@@ -8,6 +8,10 @@ export interface VfsBaseNode {
8
8
  path: string;
9
9
  /** POSIX-like mode bits. */
10
10
  mode: number;
11
+ /** Owner user ID (0 = root). */
12
+ uid: number;
13
+ /** Owner group ID (0 = root). */
14
+ gid: number;
11
15
  /** Node creation timestamp. */
12
16
  createdAt: Date;
13
17
  /** Last update timestamp. */
@@ -45,6 +49,10 @@ export interface RemoveOptions {
45
49
  export interface VfsSnapshotBaseNode {
46
50
  name: string;
47
51
  mode: number;
52
+ /** Owner user ID (0 = root). */
53
+ uid: number;
54
+ /** Owner group ID (0 = root). */
55
+ gid: number;
48
56
  /** ISO-8601 creation timestamp. */
49
57
  createdAt: string;
50
58
  /** ISO-8601 update timestamp. */
@@ -1,6 +1,25 @@
1
- /** Returns true if `name` appears in `argv`. */
1
+ /**
2
+ * Returns true if the given flag is present in the argv array.
3
+ * @param argv - The array of command-line arguments.
4
+ * @param name - The exact flag string to look for (e.g. `"--verbose"`).
5
+ * @returns `true` if the flag is present, `false` otherwise.
6
+ */
2
7
  export declare function getFlag(argv: string[], name: string): boolean;
3
- /** Returns the string value for `--name=VALUE` or `--name VALUE`, or `fallback`. */
8
+ /**
9
+ * Gets the string value of an option from argv.
10
+ * Supports both `--name=VALUE` and `--name VALUE` forms.
11
+ * @param argv - The array of command-line arguments.
12
+ * @param name - The option name (e.g. `"--output"`).
13
+ * @param fallback - The default value if the option is not found.
14
+ * @returns The option value, or `fallback` if absent.
15
+ */
4
16
  export declare function getOptionString(argv: string[], name: string, fallback: string): string;
5
- /** Returns the integer value for `--name=VALUE` or `--name VALUE`, or `fallback`. */
17
+ /**
18
+ * Gets the integer value of an option from argv.
19
+ * Supports both `--name=VALUE` and `--name VALUE` forms.
20
+ * @param argv - The array of command-line arguments.
21
+ * @param name - The option name (e.g. `"--port"`).
22
+ * @param fallback - The default value if the option is not found or cannot be parsed.
23
+ * @returns The parsed integer, or `fallback` if absent or unparseable.
24
+ */
6
25
  export declare function getOptionInt(argv: string[], name: string, fallback: number): number;
@@ -1,8 +1,20 @@
1
- /** Returns true if `name` appears in `argv`. */
1
+ /**
2
+ * Returns true if the given flag is present in the argv array.
3
+ * @param argv - The array of command-line arguments.
4
+ * @param name - The exact flag string to look for (e.g. `"--verbose"`).
5
+ * @returns `true` if the flag is present, `false` otherwise.
6
+ */
2
7
  export function getFlag(argv, name) {
3
8
  return argv.includes(name);
4
9
  }
5
- /** Returns the string value for `--name=VALUE` or `--name VALUE`, or `fallback`. */
10
+ /**
11
+ * Gets the string value of an option from argv.
12
+ * Supports both `--name=VALUE` and `--name VALUE` forms.
13
+ * @param argv - The array of command-line arguments.
14
+ * @param name - The option name (e.g. `"--output"`).
15
+ * @param fallback - The default value if the option is not found.
16
+ * @returns The option value, or `fallback` if absent.
17
+ */
6
18
  export function getOptionString(argv, name, fallback) {
7
19
  const prefix = `${name}=`;
8
20
  for (let i = 0; i < argv.length; i++) {
@@ -16,7 +28,14 @@ export function getOptionString(argv, name, fallback) {
16
28
  }
17
29
  return fallback;
18
30
  }
19
- /** Returns the integer value for `--name=VALUE` or `--name VALUE`, or `fallback`. */
31
+ /**
32
+ * Gets the integer value of an option from argv.
33
+ * Supports both `--name=VALUE` and `--name VALUE` forms.
34
+ * @param argv - The array of command-line arguments.
35
+ * @param name - The option name (e.g. `"--port"`).
36
+ * @param fallback - The default value if the option is not found or cannot be parsed.
37
+ * @returns The parsed integer, or `fallback` if absent or unparseable.
38
+ */
20
39
  export function getOptionInt(argv, name, fallback) {
21
40
  const prefix = `${name}=`;
22
41
  for (let i = 0; i < argv.length; i++) {
@@ -1,8 +1,16 @@
1
+ /**
2
+ * Interface for a performance logger instance.
3
+ * When `enabled` is false, `mark` and `done` are no-ops.
4
+ */
1
5
  export type PerfLogger = {
2
6
  enabled: boolean;
3
7
  mark: (label: string) => void;
4
8
  done: (label?: string) => void;
5
9
  };
6
- export declare function isPerfLoggingEnabled(): boolean;
10
+ /**
11
+ * Creates a performance logger that logs elapsed-time marks to the console.
12
+ * Logging is only active when `DEV_MODE` or `RENDER_PERF` env vars are truthy.
13
+ * @param scope - A label prefixed to every log line (e.g. `"render"`).
14
+ * @returns A {@link PerfLogger} instance.
15
+ */
7
16
  export declare function createPerfLogger(scope: string): PerfLogger;
8
- export declare function withPerf<T>(scope: string, label: string, work: () => Promise<T>): Promise<T>;
@@ -8,9 +8,15 @@ function nowMs() {
8
8
  }
9
9
  return Date.now();
10
10
  }
11
- export function isPerfLoggingEnabled() {
11
+ function isPerfLoggingEnabled() {
12
12
  return (isTruthyEnv(process.env.DEV_MODE) || isTruthyEnv(process.env.RENDER_PERF));
13
13
  }
14
+ /**
15
+ * Creates a performance logger that logs elapsed-time marks to the console.
16
+ * Logging is only active when `DEV_MODE` or `RENDER_PERF` env vars are truthy.
17
+ * @param scope - A label prefixed to every log line (e.g. `"render"`).
18
+ * @returns A {@link PerfLogger} instance.
19
+ */
14
20
  export function createPerfLogger(scope) {
15
21
  const enabled = isPerfLoggingEnabled();
16
22
  if (!enabled) {
@@ -34,16 +40,3 @@ export function createPerfLogger(scope) {
34
40
  done,
35
41
  };
36
42
  }
37
- export async function withPerf(scope, label, work) {
38
- const perf = createPerfLogger(scope);
39
- if (!perf.enabled) {
40
- return work();
41
- }
42
- perf.mark(`${label}:start`);
43
- try {
44
- return await work();
45
- }
46
- finally {
47
- perf.done(`${label}:done`);
48
- }
49
- }
@@ -1,10 +1,45 @@
1
1
  import type VirtualFileSystem from "../VirtualFileSystem";
2
+ /**
3
+ * Loads the shell command history for a user from the VFS.
4
+ * Creates an empty history file if none exists.
5
+ * @param vfs - The virtual file system instance.
6
+ * @param authUser - The authenticated username.
7
+ * @returns An array of history lines, with empty lines removed.
8
+ */
2
9
  export declare function loadHistory(vfs: VirtualFileSystem, authUser: string): string[];
10
+ /**
11
+ * Saves shell command history for a user to the VFS.
12
+ * Each entry is written on its own line to `.bash_history`.
13
+ * @param vfs - The virtual file system instance.
14
+ * @param authUser - The authenticated username.
15
+ * @param history - The ordered list of history entries to persist.
16
+ */
3
17
  export declare function saveHistory(vfs: VirtualFileSystem, authUser: string, history: string[]): void;
4
18
  export interface LastLogin {
5
19
  at: string;
6
20
  from: string;
7
21
  }
22
+ /**
23
+ * Reads the last-login timestamp for a user from the VFS.
24
+ * @param vfs - The virtual file system instance.
25
+ * @param authUser - The authenticated username.
26
+ * @returns The `LastLogin` object, or `null` if no record exists or parsing fails.
27
+ */
8
28
  export declare function readLastLogin(vfs: VirtualFileSystem, authUser: string): LastLogin | null;
29
+ /**
30
+ * Writes the current timestamp as the last login for a user to the VFS.
31
+ * @param vfs - The virtual file system instance.
32
+ * @param authUser - The authenticated username.
33
+ * @param from - An identifier for the origin of the login session (e.g. `"web"` or `"ssh"`).
34
+ */
9
35
  export declare function writeLastLogin(vfs: VirtualFileSystem, authUser: string, from: string): void;
36
+ /**
37
+ * Lists path completions for tab-completion in the shell.
38
+ * Filters directory entries that match the given prefix, hides dot-files
39
+ * unless the prefix itself starts with a dot, and appends `"/"` to directories.
40
+ * @param vfs - The virtual file system instance.
41
+ * @param cwd - The current working directory to resolve relative paths against.
42
+ * @param prefix - The partial path to complete (e.g. `"/usr/lo"` or `"./fo"`).
43
+ * @returns A sorted array of matching completion candidates.
44
+ */
10
45
  export declare function listPathCompletions(vfs: VirtualFileSystem, cwd: string, prefix: string): string[];
@@ -2,6 +2,13 @@ import * as path from "node:path";
2
2
  import { resolvePath } from "../commands/helpers";
3
3
  import { userHome } from "../commands/runtime";
4
4
  // ── History ───────────────────────────────────────────────────────────────────
5
+ /**
6
+ * Loads the shell command history for a user from the VFS.
7
+ * Creates an empty history file if none exists.
8
+ * @param vfs - The virtual file system instance.
9
+ * @param authUser - The authenticated username.
10
+ * @returns An array of history lines, with empty lines removed.
11
+ */
5
12
  export function loadHistory(vfs, authUser) {
6
13
  const historyPath = `${userHome(authUser)}/.bash_history`;
7
14
  if (!vfs.exists(historyPath)) {
@@ -13,10 +20,23 @@ export function loadHistory(vfs, authUser) {
13
20
  .map((l) => l.trim())
14
21
  .filter((l) => l.length > 0);
15
22
  }
23
+ /**
24
+ * Saves shell command history for a user to the VFS.
25
+ * Each entry is written on its own line to `.bash_history`.
26
+ * @param vfs - The virtual file system instance.
27
+ * @param authUser - The authenticated username.
28
+ * @param history - The ordered list of history entries to persist.
29
+ */
16
30
  export function saveHistory(vfs, authUser, history) {
17
31
  const data = history.length > 0 ? `${history.join("\n")}\n` : "";
18
32
  vfs.writeFile(`${userHome(authUser)}/.bash_history`, data);
19
33
  }
34
+ /**
35
+ * Reads the last-login timestamp for a user from the VFS.
36
+ * @param vfs - The virtual file system instance.
37
+ * @param authUser - The authenticated username.
38
+ * @returns The `LastLogin` object, or `null` if no record exists or parsing fails.
39
+ */
20
40
  export function readLastLogin(vfs, authUser) {
21
41
  const p = authUser === "root" ? "/root/.lastlog.json" : `/home/${authUser}/.lastlog`;
22
42
  if (!vfs.exists(p))
@@ -28,11 +48,26 @@ export function readLastLogin(vfs, authUser) {
28
48
  return null;
29
49
  }
30
50
  }
51
+ /**
52
+ * Writes the current timestamp as the last login for a user to the VFS.
53
+ * @param vfs - The virtual file system instance.
54
+ * @param authUser - The authenticated username.
55
+ * @param from - An identifier for the origin of the login session (e.g. `"web"` or `"ssh"`).
56
+ */
31
57
  export function writeLastLogin(vfs, authUser, from) {
32
58
  const p = authUser === "root" ? "/root/.lastlog.json" : `/home/${authUser}/.lastlog`;
33
59
  vfs.writeFile(p, JSON.stringify({ at: new Date().toISOString(), from }));
34
60
  }
35
61
  // ── Path completion ───────────────────────────────────────────────────────────
62
+ /**
63
+ * Lists path completions for tab-completion in the shell.
64
+ * Filters directory entries that match the given prefix, hides dot-files
65
+ * unless the prefix itself starts with a dot, and appends `"/"` to directories.
66
+ * @param vfs - The virtual file system instance.
67
+ * @param cwd - The current working directory to resolve relative paths against.
68
+ * @param prefix - The partial path to complete (e.g. `"/usr/lo"` or `"./fo"`).
69
+ * @returns A sorted array of matching completion candidates.
70
+ */
36
71
  export function listPathCompletions(vfs, cwd, prefix) {
37
72
  const slashIndex = prefix.lastIndexOf("/");
38
73
  const dirPart = slashIndex >= 0 ? prefix.slice(0, slashIndex + 1) : "";
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
7
- "version": "1.5.11",
7
+ "version": "1.6.1",
8
8
  "files": [
9
9
  "dist/",
10
10
  "README.md",