typescript-virtual-container 1.1.1 → 1.1.2

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 (185) hide show
  1. package/README.md +1 -1
  2. package/dist/SSHClient/index.d.ts +138 -0
  3. package/dist/SSHClient/index.d.ts.map +1 -0
  4. package/dist/SSHClient/index.js +216 -0
  5. package/dist/SSHMimic/exec.d.ts +4 -0
  6. package/dist/SSHMimic/exec.d.ts.map +1 -0
  7. package/dist/SSHMimic/exec.js +21 -0
  8. package/dist/SSHMimic/executor.d.ts +9 -0
  9. package/dist/SSHMimic/executor.d.ts.map +1 -0
  10. package/dist/SSHMimic/executor.js +131 -0
  11. package/dist/SSHMimic/hostKey.d.ts +2 -0
  12. package/dist/SSHMimic/hostKey.d.ts.map +1 -0
  13. package/dist/SSHMimic/hostKey.js +17 -0
  14. package/dist/SSHMimic/index.d.ts +39 -0
  15. package/dist/SSHMimic/index.d.ts.map +1 -0
  16. package/dist/SSHMimic/index.js +113 -0
  17. package/dist/SSHMimic/loginFormat.d.ts +2 -0
  18. package/dist/SSHMimic/loginFormat.d.ts.map +1 -0
  19. package/dist/SSHMimic/loginFormat.js +10 -0
  20. package/dist/SSHMimic/prompt.d.ts +2 -0
  21. package/dist/SSHMimic/prompt.d.ts.map +1 -0
  22. package/dist/SSHMimic/prompt.js +9 -0
  23. package/dist/VirtualFileSystem/archive.d.ts +5 -0
  24. package/dist/VirtualFileSystem/archive.d.ts.map +1 -0
  25. package/dist/VirtualFileSystem/archive.js +56 -0
  26. package/dist/VirtualFileSystem/index.d.ts +131 -0
  27. package/dist/VirtualFileSystem/index.d.ts.map +1 -0
  28. package/dist/VirtualFileSystem/index.js +355 -0
  29. package/dist/VirtualFileSystem/internalTypes.d.ts +18 -0
  30. package/dist/VirtualFileSystem/internalTypes.d.ts.map +1 -0
  31. package/dist/VirtualFileSystem/internalTypes.js +0 -0
  32. package/dist/VirtualFileSystem/path.d.ts +9 -0
  33. package/dist/VirtualFileSystem/path.d.ts.map +1 -0
  34. package/dist/VirtualFileSystem/path.js +49 -0
  35. package/dist/VirtualFileSystem/snapshot.d.ts +5 -0
  36. package/dist/VirtualFileSystem/snapshot.d.ts.map +1 -0
  37. package/dist/VirtualFileSystem/snapshot.js +59 -0
  38. package/dist/VirtualFileSystem/tree.d.ts +3 -0
  39. package/dist/VirtualFileSystem/tree.d.ts.map +1 -0
  40. package/dist/VirtualFileSystem/tree.js +19 -0
  41. package/dist/VirtualShell/index.d.ts +86 -0
  42. package/dist/VirtualShell/index.d.ts.map +1 -0
  43. package/dist/VirtualShell/index.js +129 -0
  44. package/dist/VirtualShell/shell.d.ts +5 -0
  45. package/dist/VirtualShell/shell.d.ts.map +1 -0
  46. package/dist/VirtualShell/shell.js +473 -0
  47. package/dist/VirtualShell/shellParser.d.ts +4 -0
  48. package/dist/VirtualShell/shellParser.d.ts.map +1 -0
  49. package/dist/VirtualShell/shellParser.js +207 -0
  50. package/dist/VirtualUserManager/index.d.ts +168 -0
  51. package/dist/VirtualUserManager/index.d.ts.map +1 -0
  52. package/dist/VirtualUserManager/index.js +375 -0
  53. package/dist/commands/adduser.d.ts +3 -0
  54. package/dist/commands/adduser.d.ts.map +1 -0
  55. package/dist/commands/adduser.js +18 -0
  56. package/dist/commands/cat.d.ts +3 -0
  57. package/dist/commands/cat.d.ts.map +1 -0
  58. package/dist/commands/cat.js +15 -0
  59. package/dist/commands/cd.d.ts +3 -0
  60. package/dist/commands/cd.d.ts.map +1 -0
  61. package/dist/commands/cd.js +17 -0
  62. package/dist/commands/clear.d.ts +3 -0
  63. package/dist/commands/clear.d.ts.map +1 -0
  64. package/dist/commands/clear.js +5 -0
  65. package/dist/commands/command-helpers.d.ts +23 -0
  66. package/dist/commands/command-helpers.d.ts.map +1 -0
  67. package/dist/commands/command-helpers.js +139 -0
  68. package/dist/commands/curl.d.ts +3 -0
  69. package/dist/commands/curl.d.ts.map +1 -0
  70. package/dist/commands/curl.js +44 -0
  71. package/dist/commands/deluser.d.ts +3 -0
  72. package/dist/commands/deluser.d.ts.map +1 -0
  73. package/dist/commands/deluser.js +15 -0
  74. package/dist/commands/echo.d.ts +3 -0
  75. package/dist/commands/echo.d.ts.map +1 -0
  76. package/dist/commands/echo.js +22 -0
  77. package/dist/commands/env.d.ts +3 -0
  78. package/dist/commands/env.d.ts.map +1 -0
  79. package/dist/commands/env.js +18 -0
  80. package/dist/commands/exit.d.ts +3 -0
  81. package/dist/commands/exit.d.ts.map +1 -0
  82. package/dist/commands/exit.js +5 -0
  83. package/dist/commands/export.d.ts +3 -0
  84. package/dist/commands/export.d.ts.map +1 -0
  85. package/dist/commands/export.js +34 -0
  86. package/dist/commands/grep.d.ts +3 -0
  87. package/dist/commands/grep.d.ts.map +1 -0
  88. package/dist/commands/grep.js +69 -0
  89. package/dist/commands/help.d.ts +3 -0
  90. package/dist/commands/help.d.ts.map +1 -0
  91. package/dist/commands/help.js +7 -0
  92. package/dist/commands/helpers.d.ts +26 -0
  93. package/dist/commands/helpers.d.ts.map +1 -0
  94. package/dist/commands/helpers.js +160 -0
  95. package/dist/commands/hostname.d.ts +3 -0
  96. package/dist/commands/hostname.d.ts.map +1 -0
  97. package/dist/commands/hostname.js +5 -0
  98. package/dist/commands/htop.d.ts +3 -0
  99. package/dist/commands/htop.d.ts.map +1 -0
  100. package/dist/commands/htop.js +10 -0
  101. package/dist/commands/index.d.ts +8 -0
  102. package/dist/commands/index.d.ts.map +1 -0
  103. package/dist/commands/index.js +212 -0
  104. package/dist/commands/ls.d.ts +3 -0
  105. package/dist/commands/ls.d.ts.map +1 -0
  106. package/dist/commands/ls.js +47 -0
  107. package/dist/commands/mkdir.d.ts +3 -0
  108. package/dist/commands/mkdir.d.ts.map +1 -0
  109. package/dist/commands/mkdir.js +21 -0
  110. package/dist/commands/nano.d.ts +3 -0
  111. package/dist/commands/nano.d.ts.map +1 -0
  112. package/dist/commands/nano.js +27 -0
  113. package/dist/commands/neofetch.d.ts +3 -0
  114. package/dist/commands/neofetch.d.ts.map +1 -0
  115. package/dist/commands/neofetch.js +32 -0
  116. package/dist/commands/pwd.d.ts +3 -0
  117. package/dist/commands/pwd.d.ts.map +1 -0
  118. package/dist/commands/pwd.js +5 -0
  119. package/dist/commands/rm.d.ts +3 -0
  120. package/dist/commands/rm.d.ts.map +1 -0
  121. package/dist/commands/rm.js +29 -0
  122. package/dist/commands/set.d.ts +7 -0
  123. package/dist/commands/set.d.ts.map +1 -0
  124. package/dist/commands/set.js +64 -0
  125. package/dist/commands/sh.d.ts +4 -0
  126. package/dist/commands/sh.d.ts.map +1 -0
  127. package/dist/commands/sh.js +45 -0
  128. package/dist/commands/su.d.ts +3 -0
  129. package/dist/commands/su.d.ts.map +1 -0
  130. package/dist/commands/su.js +24 -0
  131. package/dist/commands/sudo.d.ts +3 -0
  132. package/dist/commands/sudo.d.ts.map +1 -0
  133. package/dist/commands/sudo.js +47 -0
  134. package/dist/commands/touch.d.ts +3 -0
  135. package/dist/commands/touch.d.ts.map +1 -0
  136. package/dist/commands/touch.js +18 -0
  137. package/dist/commands/tree.d.ts +3 -0
  138. package/dist/commands/tree.d.ts.map +1 -0
  139. package/dist/commands/tree.js +11 -0
  140. package/dist/commands/unset.d.ts +3 -0
  141. package/dist/commands/unset.d.ts.map +1 -0
  142. package/dist/commands/unset.js +15 -0
  143. package/dist/commands/wget.d.ts +3 -0
  144. package/dist/commands/wget.d.ts.map +1 -0
  145. package/dist/commands/wget.js +113 -0
  146. package/dist/commands/who.d.ts +3 -0
  147. package/dist/commands/who.d.ts.map +1 -0
  148. package/dist/commands/who.js +15 -0
  149. package/dist/commands/whoami.d.ts +3 -0
  150. package/dist/commands/whoami.d.ts.map +1 -0
  151. package/dist/commands/whoami.js +5 -0
  152. package/dist/index.d.ts +11 -0
  153. package/dist/index.d.ts.map +1 -0
  154. package/dist/index.js +7 -0
  155. package/dist/modules/neofetch.d.ts +19 -0
  156. package/dist/modules/neofetch.d.ts.map +1 -0
  157. package/dist/modules/neofetch.js +284 -0
  158. package/dist/modules/shellInteractive.d.ts +6 -0
  159. package/dist/modules/shellInteractive.d.ts.map +1 -0
  160. package/dist/modules/shellInteractive.js +26 -0
  161. package/dist/modules/shellRuntime.d.ts +11 -0
  162. package/dist/modules/shellRuntime.d.ts.map +1 -0
  163. package/dist/modules/shellRuntime.js +52 -0
  164. package/dist/standalone.d.ts +2 -0
  165. package/dist/standalone.d.ts.map +1 -0
  166. package/dist/standalone.js +25 -0
  167. package/dist/types/commands.d.ts +89 -0
  168. package/dist/types/commands.d.ts.map +1 -0
  169. package/dist/types/commands.js +0 -0
  170. package/dist/types/pipeline.d.ts +23 -0
  171. package/dist/types/pipeline.d.ts.map +1 -0
  172. package/dist/types/pipeline.js +0 -0
  173. package/dist/types/streams.d.ts +32 -0
  174. package/dist/types/streams.d.ts.map +1 -0
  175. package/dist/types/streams.js +0 -0
  176. package/dist/types/vfs.d.ts +71 -0
  177. package/dist/types/vfs.d.ts.map +1 -0
  178. package/dist/types/vfs.js +0 -0
  179. package/package.json +4 -2
  180. package/src/VirtualShell/shell.ts +3 -3
  181. package/src/commands/neofetch.ts +1 -1
  182. package/{modules → src/modules}/neofetch.ts +56 -51
  183. package/{modules → src/modules}/shellInteractive.ts +16 -4
  184. package/tsconfig.json +20 -8
  185. /package/{modules → src/modules}/shellRuntime.ts +0 -0
@@ -0,0 +1,52 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ export function shellQuote(value) {
4
+ return `'${value.replace(/'/g, `'\\''`)}'`;
5
+ }
6
+ export function toTtyLines(text) {
7
+ return text
8
+ .replace(/\r\n/g, "\n")
9
+ .replace(/\r/g, "\n")
10
+ .replace(/\n/g, "\r\n");
11
+ }
12
+ export function withTerminalSize(command, terminalSize) {
13
+ const cols = Number.isFinite(terminalSize.cols) && terminalSize.cols > 0
14
+ ? Math.floor(terminalSize.cols)
15
+ : 80;
16
+ const rows = Number.isFinite(terminalSize.rows) && terminalSize.rows > 0
17
+ ? Math.floor(terminalSize.rows)
18
+ : 24;
19
+ return `stty cols ${cols} rows ${rows} 2>/dev/null; ${command}`;
20
+ }
21
+ export function resolvePath(base, inputPath) {
22
+ if (!inputPath || inputPath.trim() === "" || inputPath === ".") {
23
+ return base;
24
+ }
25
+ return inputPath.startsWith("/")
26
+ ? path.posix.normalize(inputPath)
27
+ : path.posix.normalize(path.posix.join(base, inputPath));
28
+ }
29
+ export async function collectChildPids(parentPid) {
30
+ try {
31
+ const childrenRaw = await readFile(`/proc/${parentPid}/task/${parentPid}/children`, "utf8");
32
+ const directChildren = childrenRaw
33
+ .trim()
34
+ .split(/\s+/)
35
+ .filter(Boolean)
36
+ .map((value) => Number.parseInt(value, 10))
37
+ .filter((pid) => Number.isInteger(pid) && pid > 0);
38
+ const nested = await Promise.all(directChildren.map((pid) => collectChildPids(pid)));
39
+ return [...directChildren, ...nested.flat()];
40
+ }
41
+ catch {
42
+ return [];
43
+ }
44
+ }
45
+ export async function getVisibleHtopPidList(rootPid = process.pid) {
46
+ const descendants = await collectChildPids(rootPid);
47
+ const unique = Array.from(new Set(descendants)).sort((a, b) => a - b);
48
+ if (unique.length === 0) {
49
+ return null;
50
+ }
51
+ return unique.join(",");
52
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=standalone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import { VirtualShell, VirtualSshServer } from ".";
2
+ const hostname = process.env.SSH_MIMIC_HOSTNAME ?? "typescript-vm";
3
+ const virtualShell = new VirtualShell(hostname);
4
+ virtualShell.addCommand("demo", [], () => {
5
+ return {
6
+ stdout: "This is a demo command. It does nothing useful.",
7
+ exitCode: 0,
8
+ };
9
+ });
10
+ new VirtualSshServer({
11
+ port: 2222,
12
+ hostname,
13
+ shell: virtualShell,
14
+ })
15
+ .start()
16
+ .then((port) => {
17
+ // if (!sshMimic) console.error("Failed to initialize SSH Mimic shell.");
18
+ // else {
19
+ console.log(`SSH Mimic initialized. Listening on port ${port}.`);
20
+ // }
21
+ })
22
+ .catch((error) => {
23
+ console.error("Failed to start SSH Mimic:", error);
24
+ process.exit(1);
25
+ });
@@ -0,0 +1,89 @@
1
+ /** Command invocation mode used by shell runtime. */
2
+ export type CommandMode = "shell" | "exec";
3
+ import type { VirtualShell } from "../VirtualShell";
4
+ import type { VirtualActiveSession } from "../VirtualUserManager";
5
+ /**
6
+ * Normalized command execution output.
7
+ *
8
+ * A command can write text, control session lifecycle, request UI state
9
+ * transitions, and update active identity/cwd.
10
+ */
11
+ export interface CommandResult {
12
+ /** Standard output payload to append in terminal. */
13
+ stdout?: string;
14
+ /** Standard error payload to append in terminal. */
15
+ stderr?: string;
16
+ /** Request full terminal clear before next prompt. */
17
+ clearScreen?: boolean;
18
+ /** Request current shell/exec session close. */
19
+ closeSession?: boolean;
20
+ /** Optional exit code (default behavior handled by caller). */
21
+ exitCode?: number;
22
+ /** Optional cwd to apply for next prompt iteration. */
23
+ nextCwd?: string;
24
+ /** Optional user switch for current session state. */
25
+ switchUser?: string;
26
+ /** Request opening built-in nano editor workflow. */
27
+ openEditor?: NanoEditorSession;
28
+ /** Request opening built-in htop-like screen. */
29
+ openHtop?: boolean;
30
+ /** Request sudo password challenge flow. */
31
+ sudoChallenge?: SudoChallenge;
32
+ }
33
+ /** Deferred sudo challenge metadata returned by sudo command. */
34
+ export interface SudoChallenge {
35
+ /** User currently requesting elevation. */
36
+ username: string;
37
+ /** Target identity for elevated command. */
38
+ targetUser: string;
39
+ /** Command to execute after successful challenge; null for login shell. */
40
+ commandLine: string | null;
41
+ /** True when challenge targets interactive login shell. */
42
+ loginShell: boolean;
43
+ /** Prompt text shown before password input. */
44
+ prompt: string;
45
+ }
46
+ /** State payload used by nano command interactive editor flow. */
47
+ export interface NanoEditorSession {
48
+ /** Final destination path to write when save succeeds. */
49
+ targetPath: string;
50
+ /** Temporary scratch path used while editing. */
51
+ tempPath: string;
52
+ /** Initial editor content shown to user. */
53
+ initialContent: string;
54
+ }
55
+ /** Runtime context object passed to each command module. */
56
+ export interface CommandContext {
57
+ /** Authenticated user currently bound to stream. */
58
+ authUser: string;
59
+ /** Virtual hostname shown in prompt and banners. */
60
+ hostname: string;
61
+ /** Snapshot of currently active user sessions. */
62
+ activeSessions: VirtualActiveSession[];
63
+ /** Original unparsed command line input. */
64
+ rawInput: string;
65
+ /** Invocation mode (interactive shell or direct exec). */
66
+ mode: CommandMode;
67
+ /** Tokenized arguments excluding command name. */
68
+ args: string[];
69
+ /** Virtual shell instance. */
70
+ shell: VirtualShell;
71
+ /** Optional stdin payload (used by pipes/redirections). */
72
+ stdin?: string;
73
+ /** Current working directory for command execution. */
74
+ cwd: string;
75
+ }
76
+ /** Contract implemented by each shell command module. */
77
+ export interface ShellModule {
78
+ /** Primary command name used in CLI. */
79
+ name: string;
80
+ /** Parameter help snippets displayed by help command. */
81
+ params: string[];
82
+ /** Command handler implementation. */
83
+ run: (ctx: CommandContext) => CommandResult | Promise<CommandResult>;
84
+ /** Optional alternative command names. */
85
+ aliases?: string[];
86
+ }
87
+ /** Command return union allowing sync or async handlers. */
88
+ export type CommandOutcome = CommandResult | Promise<CommandResult>;
89
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/types/commands.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC7B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gDAAgD;IAChD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED,iEAAiE;AACjE,MAAM,WAAW,aAAa;IAC7B,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2DAA2D;IAC3D,UAAU,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;CACf;AAED,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IACjC,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,cAAc,EAAE,MAAM,CAAC;CACvB;AAED,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC9B,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,cAAc,EAAE,oBAAoB,EAAE,CAAC;IACvC,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,IAAI,EAAE,WAAW,CAAC;IAClB,kDAAkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,8BAA8B;IAC9B,KAAK,EAAE,YAAY,CAAC;IACpB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC3B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,sCAAsC;IACtC,GAAG,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACrE,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,4DAA4D;AAC5D,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC"}
File without changes
@@ -0,0 +1,23 @@
1
+ /** Represents a single command in a pipeline. */
2
+ export interface PipelineCommand {
3
+ /** Command name */
4
+ name: string;
5
+ /** Command arguments */
6
+ args: string[];
7
+ /** Input redirection file path (< file) */
8
+ inputFile?: string;
9
+ /** Output redirection file path (> file) */
10
+ outputFile?: string;
11
+ /** Append to output file (>> file) */
12
+ appendOutput?: boolean;
13
+ }
14
+ /** Represents a parsed shell pipeline */
15
+ export interface Pipeline {
16
+ /** List of commands in the pipeline */
17
+ commands: PipelineCommand[];
18
+ /** Whether this is a valid pipeline */
19
+ isValid: boolean;
20
+ /** Error message if parsing failed */
21
+ error?: string;
22
+ }
23
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/types/pipeline.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC/B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,yCAAyC;AACzC,MAAM,WAAW,QAAQ;IACxB,uCAAuC;IACvC,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CACf"}
File without changes
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Minimal stream contract used by exec command handlers.
3
+ */
4
+ export interface ExecStream {
5
+ /** Writes text to stdout channel. */
6
+ write(data: string): void;
7
+ /** Signals output completion. */
8
+ end(): void;
9
+ /** Sets process-like exit code for exec response. */
10
+ exit(code: number): void;
11
+ /** Writable stderr channel. */
12
+ stderr: {
13
+ /** Writes text to stderr channel. */
14
+ write(data: string): void;
15
+ };
16
+ }
17
+ /**
18
+ * Minimal interactive stream contract used by shell mode.
19
+ */
20
+ export interface ShellStream {
21
+ /** Writes text to shell output channel. */
22
+ write(data: string): void;
23
+ /** Sets shell exit code on close. */
24
+ exit(code: number): void;
25
+ /** Ends shell stream. */
26
+ end(): void;
27
+ /** Subscribes to incoming user input chunks. */
28
+ on(event: "data", listener: (chunk: Buffer) => void): void;
29
+ /** Subscribes to stream close event. */
30
+ on(event: "close", listener: () => void): void;
31
+ }
32
+ //# sourceMappingURL=streams.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../../src/types/streams.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,qCAAqC;IACrC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iCAAiC;IACjC,GAAG,IAAI,IAAI,CAAC;IACZ,qDAAqD;IACrD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,+BAA+B;IAC/B,MAAM,EAAE;QACP,qCAAqC;QACrC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,2CAA2C;IAC3C,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,qCAAqC;IACrC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yBAAyB;IACzB,GAAG,IAAI,IAAI,CAAC;IACZ,gDAAgD;IAChD,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3D,wCAAwC;IACxC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAC/C"}
File without changes
@@ -0,0 +1,71 @@
1
+ /** Supported virtual node kinds. */
2
+ export type VfsNodeType = "file" | "directory";
3
+ /** Shared metadata fields available on file and directory stats. */
4
+ export interface VfsBaseNode {
5
+ /** Node name without parent path. */
6
+ name: string;
7
+ /** Absolute normalized node path. */
8
+ path: string;
9
+ /** POSIX-like mode bits. */
10
+ mode: number;
11
+ /** Node creation timestamp. */
12
+ createdAt: Date;
13
+ /** Last update timestamp. */
14
+ updatedAt: Date;
15
+ }
16
+ /** Stat shape returned for file nodes. */
17
+ export interface VfsFileNode extends VfsBaseNode {
18
+ type: "file";
19
+ /** True when file content stored as gzip bytes. */
20
+ compressed: boolean;
21
+ /** Stored byte length (compressed when compressed=true). */
22
+ size: number;
23
+ }
24
+ /** Stat shape returned for directory nodes. */
25
+ export interface VfsDirectoryNode extends VfsBaseNode {
26
+ type: "directory";
27
+ /** Number of direct children in directory. */
28
+ childrenCount: number;
29
+ }
30
+ /** Union of file and directory stat responses. */
31
+ export type VfsNodeStats = VfsFileNode | VfsDirectoryNode;
32
+ /** Optional behavior flags for writeFile operations. */
33
+ export interface WriteFileOptions {
34
+ /** POSIX-like mode to apply on create or overwrite. */
35
+ mode?: number;
36
+ /** Store content compressed with gzip. */
37
+ compress?: boolean;
38
+ }
39
+ /** Optional behavior flags for remove operations. */
40
+ export interface RemoveOptions {
41
+ /** Allow deleting non-empty directory trees. */
42
+ recursive?: boolean;
43
+ }
44
+ /** Base snapshot node schema used for archive serialization. */
45
+ export interface VfsSnapshotBaseNode {
46
+ name: string;
47
+ mode: number;
48
+ /** ISO-8601 creation timestamp. */
49
+ createdAt: string;
50
+ /** ISO-8601 update timestamp. */
51
+ updatedAt: string;
52
+ }
53
+ /** Serialized snapshot shape for file nodes. */
54
+ export interface VfsSnapshotFileNode extends VfsSnapshotBaseNode {
55
+ type: "file";
56
+ compressed: boolean;
57
+ /** Base64-encoded raw file bytes. */
58
+ contentBase64: string;
59
+ }
60
+ /** Serialized snapshot shape for directory nodes. */
61
+ export interface VfsSnapshotDirectoryNode extends VfsSnapshotBaseNode {
62
+ type: "directory";
63
+ children: VfsSnapshotNode[];
64
+ }
65
+ /** Union of serialized snapshot node variants. */
66
+ export type VfsSnapshotNode = VfsSnapshotFileNode | VfsSnapshotDirectoryNode;
67
+ /** Top-level serialized filesystem snapshot. */
68
+ export interface VfsSnapshot {
69
+ root: VfsSnapshotDirectoryNode;
70
+ }
71
+ //# sourceMappingURL=vfs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vfs.d.ts","sourceRoot":"","sources":["../../src/types/vfs.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,CAAC;AAE/C,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC3B,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,SAAS,EAAE,IAAI,CAAC;IAChB,6BAA6B;IAC7B,SAAS,EAAE,IAAI,CAAC;CAChB;AAED,0CAA0C;AAC1C,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,UAAU,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;CACb;AAED,+CAA+C;AAC/C,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACpD,IAAI,EAAE,WAAW,CAAC;IAClB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,kDAAkD;AAClD,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAE1D,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAChC,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC7B,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,gDAAgD;AAChD,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,qDAAqD;AACrD,MAAM,WAAW,wBAAyB,SAAQ,mBAAmB;IACpE,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,kDAAkD;AAClD,MAAM,MAAM,eAAe,GAAG,mBAAmB,GAAG,wBAAwB,CAAC;AAE7E,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,wBAAwB,CAAC;CAC/B"}
File without changes
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "typescript-virtual-container",
3
3
  "description": "In-memory SSH server with virtual filesystem and typed programmatic API",
4
- "module": "src/index.ts",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
5
6
  "type": "module",
6
- "version": "1.1.1",
7
+ "version": "1.1.2",
7
8
  "license": "MIT",
8
9
  "keywords": [
9
10
  "ssh",
@@ -20,6 +21,7 @@
20
21
  "lint": "bunx --bun @biomejs/biome lint ./src",
21
22
  "lint:write": "bunx --bun @biomejs/biome lint --write ./src",
22
23
  "test": "bunx --bun @biomejs/biome test ./src",
24
+ "build": "tsc --project tsconfig.json",
23
25
  "deploy:npm": "npm publish --access public"
24
26
  },
25
27
  "devDependencies": {
@@ -2,17 +2,17 @@ import type { ChildProcessWithoutNullStreams } from "node:child_process";
2
2
  import { readFile, unlink, writeFile } from "node:fs/promises";
3
3
  import * as path from "node:path";
4
4
  import type { ShellProperties, VirtualShell } from ".";
5
+ import { getCommandNames, runCommand } from "../commands";
5
6
  import {
6
7
  spawnHtopProcess,
7
8
  spawnNanoEditorProcess,
8
- } from "../../modules/shellInteractive";
9
+ } from "../modules/shellInteractive";
9
10
  import {
10
11
  getVisibleHtopPidList,
11
12
  resolvePath,
12
13
  type TerminalSize,
13
14
  toTtyLines,
14
- } from "../../modules/shellRuntime";
15
- import { getCommandNames, runCommand } from "../commands";
15
+ } from "../modules/shellRuntime";
16
16
  import { formatLoginDate } from "../SSHMimic/loginFormat";
17
17
  import { buildPrompt } from "../SSHMimic/prompt";
18
18
  import type { ShellStream } from "../types/streams";
@@ -1,4 +1,4 @@
1
- import { buildNeofetchOutput } from "../../modules/neofetch";
1
+ import { buildNeofetchOutput } from "../modules/neofetch";
2
2
  import type { ShellModule } from "../types/commands";
3
3
  import { ifFlag } from "./command-helpers";
4
4
  import { getAllEnvVars } from "./set";
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readdirSync, readFileSync } from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
- import type { ShellProperties } from "../src/VirtualShell";
4
+ import type { ShellProperties } from "../VirtualShell";
5
5
 
6
6
  function formatUptime(seconds: number): string {
7
7
  const totalMinutes = Math.max(1, Math.floor(seconds / 60));
@@ -56,11 +56,11 @@ function colorizeDetailLine(line: string): string {
56
56
  return line;
57
57
  }
58
58
 
59
- const colonIndex = line.indexOf(':');
59
+ const colonIndex = line.indexOf(":");
60
60
 
61
61
  if (colonIndex === -1) {
62
62
  // Pas de ':', chercher '@' pour identifier user@host
63
- if (line.includes('@')) {
63
+ if (line.includes("@")) {
64
64
  // C'est user@host, appliquer dégradé horizontal
65
65
  return applyHorizontalGradient(line);
66
66
  }
@@ -79,8 +79,8 @@ function colorizeDetailLine(line: string): string {
79
79
 
80
80
  function applyHorizontalGradient(text: string): string {
81
81
  // Nettoyer les codes ANSI existants
82
- const ansiRegex = new RegExp(`${String.fromCharCode(27)}\\[[\\d;]*m`, 'g');
83
- const cleaned = text.replace(ansiRegex, '');
82
+ const ansiRegex = new RegExp(`${String.fromCharCode(27)}\\[[\\d;]*m`, "g");
83
+ const cleaned = text.replace(ansiRegex, "");
84
84
 
85
85
  if (cleaned.trim().length === 0) {
86
86
  return text;
@@ -88,7 +88,7 @@ function applyHorizontalGradient(text: string): string {
88
88
 
89
89
  const start = { r: 255, g: 255, b: 255 };
90
90
  const end = { r: 168, g: 85, b: 247 };
91
- let result = '';
91
+ let result = "";
92
92
 
93
93
  for (let i = 0; i < cleaned.length; i += 1) {
94
94
  const ratio = cleaned.length <= 1 ? 0 : i / (cleaned.length - 1);
@@ -111,7 +111,7 @@ export interface NeofetchInfo {
111
111
  uptimeSeconds?: number;
112
112
  packages?: string;
113
113
  shell?: string;
114
- shellProps?: ShellProperties;
114
+ shellProps?: ShellProperties;
115
115
  resolution?: string;
116
116
  terminal?: string;
117
117
  cpu?: string;
@@ -180,8 +180,7 @@ function countDpkgPackages(): number | undefined {
180
180
  const data = readFileSync(filePath, "utf8");
181
181
  const matches = data.match(/^Package:\s+/gm);
182
182
  return matches?.length ?? 0;
183
- } catch {
184
- }
183
+ } catch {}
185
184
  }
186
185
 
187
186
  return undefined;
@@ -199,8 +198,7 @@ function countSnapPackages(): number | undefined {
199
198
  const entries = readdirSync(dirPath, { withFileTypes: true });
200
199
  const count = entries.filter((entry) => entry.isDirectory()).length;
201
200
  return count;
202
- } catch {
203
- }
201
+ } catch {}
204
202
  }
205
203
 
206
204
  return undefined;
@@ -250,28 +248,31 @@ function resolveDefaults(info: NeofetchInfo): Required<NeofetchInfo> {
250
248
  const totalMem = os.totalmem();
251
249
  const freeMem = os.freemem();
252
250
  const usedMem = Math.max(0, totalMem - freeMem);
253
- const shellProps = info.shellProps;
251
+ const shellProps = info.shellProps;
254
252
 
255
- const processUptime = process.uptime();
256
- if (info.uptimeSeconds === undefined) {
257
- info.uptimeSeconds = Math.round(processUptime);
258
- }
253
+ const processUptime = process.uptime();
254
+ if (info.uptimeSeconds === undefined) {
255
+ info.uptimeSeconds = Math.round(processUptime);
256
+ }
259
257
 
260
- console.log("Resolving neofetch info with shellProps:", shellProps);
258
+ console.log("Resolving neofetch info with shellProps:", shellProps);
261
259
 
262
260
  return {
263
261
  user: info.user,
264
262
  host: info.host,
265
- osName: shellProps?.os ?? info.osName ?? `${readOsPrettyName() ?? os.type()} ${os.arch()}`,
263
+ osName:
264
+ shellProps?.os ??
265
+ info.osName ??
266
+ `${readOsPrettyName() ?? os.type()} ${os.arch()}`,
266
267
  kernel: shellProps?.kernel ?? info.kernel ?? os.release(),
267
268
  uptimeSeconds: info.uptimeSeconds ?? os.uptime(),
268
269
  packages: info.packages ?? resolvePackagesLabel(),
269
270
  shell: resolveShellLabel(info.shell),
270
- shellProps: info.shellProps as ShellProperties ?? {
271
- kernel: info.kernel ?? os.release(),
272
- os: info.osName ?? `${readOsPrettyName() ?? os.type()} ${os.arch()}`,
273
- arch: os.arch(),
274
- },
271
+ shellProps: (info.shellProps as ShellProperties) ?? {
272
+ kernel: info.kernel ?? os.release(),
273
+ os: info.osName ?? `${readOsPrettyName() ?? os.type()} ${os.arch()}`,
274
+ arch: os.arch(),
275
+ },
275
276
  resolution: info.resolution ?? "n/a (ssh)",
276
277
  terminal: info.terminal ?? "unknown",
277
278
  cpu: info.cpu ?? resolveCpuLabel(),
@@ -287,32 +288,32 @@ export function buildNeofetchOutput(info: NeofetchInfo): string {
287
288
  const colorBars = buildColorBars();
288
289
 
289
290
  const distroLogo = [
290
- " .. .:. ",
291
- " .::.. .. .. ",
292
- ". .... ... .. ",
293
- ": .... .:. .. ",
294
- ": .:.:........:. .. ",
295
- ": .. ",
296
- ". : ",
297
- ". : ",
298
- ".. : ",
299
- " :. .. ",
300
- " .. .. ",
301
- " :-. :: ",
302
- " .:. :. ",
303
- " ..: ... ",
304
- " ..: :.. ",
305
- " :... :....",
306
- " .. ....",
307
- " . .. ",
308
- " .:. .: ",
309
- " :. .. ",
310
- " ::. .. ",
311
- "..... ..:... ",
312
- "...:. .. ",
313
- ".:...:. ::. .. ",
314
- " ... ..:::::.. ..:::::::.. ",
315
- ]
291
+ " .. .:. ",
292
+ " .::.. .. .. ",
293
+ ". .... ... .. ",
294
+ ": .... .:. .. ",
295
+ ": .:.:........:. .. ",
296
+ ": .. ",
297
+ ". : ",
298
+ ". : ",
299
+ ".. : ",
300
+ " :. .. ",
301
+ " .. .. ",
302
+ " :-. :: ",
303
+ " .:. :. ",
304
+ " ..: ... ",
305
+ " ..: :.. ",
306
+ " :... :....",
307
+ " .. ....",
308
+ " . .. ",
309
+ " .:. .: ",
310
+ " :. .. ",
311
+ " ::. .. ",
312
+ "..... ..:... ",
313
+ "...:. .. ",
314
+ ".:...:. ::. .. ",
315
+ " ... ..:::::.. ..:::::::.. ",
316
+ ];
316
317
 
317
318
  const details = [
318
319
  `${fields.user}@${fields.host}`,
@@ -341,7 +342,11 @@ export function buildNeofetchOutput(info: NeofetchInfo): string {
341
342
  const rawLeft = distroLogo[i] ?? "";
342
343
  const right = details[i] ?? "";
343
344
  if (right.length > 0) {
344
- const left = colorizeLogoLine(rawLeft.padEnd(31, " "), i, distroLogo.length);
345
+ const left = colorizeLogoLine(
346
+ rawLeft.padEnd(31, " "),
347
+ i,
348
+ distroLogo.length,
349
+ );
345
350
  const coloredRight = colorizeDetailLine(right);
346
351
  lines.push(`${left} ${coloredRight}`);
347
352
  continue;
@@ -351,4 +356,4 @@ export function buildNeofetchOutput(info: NeofetchInfo): string {
351
356
  }
352
357
 
353
358
  return lines.join("\n");
354
- }
359
+ }
@@ -1,6 +1,10 @@
1
1
  import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
2
- import type { ShellStream } from "../src/types/streams";
3
- import { shellQuote, type TerminalSize, withTerminalSize } from "./shellRuntime";
2
+ import type { ShellStream } from "../types/streams";
3
+ import {
4
+ shellQuote,
5
+ type TerminalSize,
6
+ withTerminalSize,
7
+ } from "./shellRuntime";
4
8
 
5
9
  function spawnScriptProcess(
6
10
  command: string,
@@ -33,7 +37,11 @@ export function spawnNanoEditorProcess(
33
37
  terminalSize: TerminalSize,
34
38
  stream: ShellStream,
35
39
  ): ChildProcessWithoutNullStreams {
36
- return spawnScriptProcess(`nano -- ${shellQuote(tempPath)}`, terminalSize, stream);
40
+ return spawnScriptProcess(
41
+ `nano -- ${shellQuote(tempPath)}`,
42
+ terminalSize,
43
+ stream,
44
+ );
37
45
  }
38
46
 
39
47
  export function spawnHtopProcess(
@@ -41,5 +49,9 @@ export function spawnHtopProcess(
41
49
  terminalSize: TerminalSize,
42
50
  stream: ShellStream,
43
51
  ): ChildProcessWithoutNullStreams {
44
- return spawnScriptProcess(`htop -p ${shellQuote(pidList)}`, terminalSize, stream);
52
+ return spawnScriptProcess(
53
+ `htop -p ${shellQuote(pidList)}`,
54
+ terminalSize,
55
+ stream,
56
+ );
45
57
  }
package/tsconfig.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  // Environment setup & latest features
4
- "lib": ["ESNext"],
4
+ "lib": [
5
+ "ESNext"
6
+ ],
5
7
  "target": "ESNext",
6
8
  "module": "Preserve",
7
9
  "moduleDetection": "force",
8
10
  "jsx": "react-jsx",
9
11
  "allowJs": true,
10
-
12
+
11
13
  // Bundler mode
12
14
  "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
15
  "verbatimModuleSyntax": true,
15
- "noEmit": true,
16
-
16
+ "noEmit": false,
17
+
17
18
  // Best practices
18
19
  "strict": true,
19
20
  "skipLibCheck": true,
@@ -25,7 +26,18 @@
25
26
  "noUnusedLocals": false,
26
27
  "noUnusedParameters": false,
27
28
  "noPropertyAccessFromIndexSignature": false,
29
+ "types": [
30
+ "node",
31
+ "bun"
32
+ ],
28
33
 
29
- "types": ["node", "bun"]
30
- }
31
- }
34
+ "esModuleInterop": true,
35
+ "forceConsistentCasingInFileNames": true,
36
+ "resolveJsonModule": true,
37
+ "declaration": true,
38
+ "declarationMap": true,
39
+ "rootDir": "src",
40
+ "outDir": "dist",
41
+ },
42
+ "include": ["src/**/*"]
43
+ }
File without changes