typescript-virtual-container 1.1.1-b → 1.1.1-c
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/.vscode/settings.json +1 -1
- package/dist/SSHClient/index.d.ts +138 -0
- package/dist/SSHClient/index.d.ts.map +1 -0
- package/dist/SSHClient/index.js +216 -0
- package/dist/SSHMimic/exec.d.ts +4 -0
- package/dist/SSHMimic/exec.d.ts.map +1 -0
- package/dist/SSHMimic/exec.js +21 -0
- package/dist/SSHMimic/executor.d.ts +9 -0
- package/dist/SSHMimic/executor.d.ts.map +1 -0
- package/dist/SSHMimic/executor.js +131 -0
- package/dist/SSHMimic/hostKey.d.ts +2 -0
- package/dist/SSHMimic/hostKey.d.ts.map +1 -0
- package/dist/SSHMimic/hostKey.js +17 -0
- package/dist/SSHMimic/index.d.ts +39 -0
- package/dist/SSHMimic/index.d.ts.map +1 -0
- package/dist/SSHMimic/index.js +113 -0
- package/dist/SSHMimic/loginFormat.d.ts +2 -0
- package/dist/SSHMimic/loginFormat.d.ts.map +1 -0
- package/dist/SSHMimic/loginFormat.js +10 -0
- package/dist/SSHMimic/prompt.d.ts +2 -0
- package/dist/SSHMimic/prompt.d.ts.map +1 -0
- package/dist/SSHMimic/prompt.js +9 -0
- package/dist/VirtualFileSystem/archive.d.ts +5 -0
- package/dist/VirtualFileSystem/archive.d.ts.map +1 -0
- package/dist/VirtualFileSystem/archive.js +56 -0
- package/dist/VirtualFileSystem/index.d.ts +131 -0
- package/dist/VirtualFileSystem/index.d.ts.map +1 -0
- package/dist/VirtualFileSystem/index.js +355 -0
- package/dist/VirtualFileSystem/internalTypes.d.ts +18 -0
- package/dist/VirtualFileSystem/internalTypes.d.ts.map +1 -0
- package/dist/VirtualFileSystem/internalTypes.js +0 -0
- package/dist/VirtualFileSystem/path.d.ts +9 -0
- package/dist/VirtualFileSystem/path.d.ts.map +1 -0
- package/dist/VirtualFileSystem/path.js +49 -0
- package/dist/VirtualFileSystem/snapshot.d.ts +5 -0
- package/dist/VirtualFileSystem/snapshot.d.ts.map +1 -0
- package/dist/VirtualFileSystem/snapshot.js +59 -0
- package/dist/VirtualFileSystem/tree.d.ts +3 -0
- package/dist/VirtualFileSystem/tree.d.ts.map +1 -0
- package/dist/VirtualFileSystem/tree.js +19 -0
- package/dist/VirtualShell/index.d.ts +86 -0
- package/dist/VirtualShell/index.d.ts.map +1 -0
- package/dist/VirtualShell/index.js +129 -0
- package/dist/VirtualShell/shell.d.ts +5 -0
- package/dist/VirtualShell/shell.d.ts.map +1 -0
- package/dist/VirtualShell/shell.js +473 -0
- package/dist/VirtualShell/shellParser.d.ts +4 -0
- package/dist/VirtualShell/shellParser.d.ts.map +1 -0
- package/dist/VirtualShell/shellParser.js +207 -0
- package/dist/VirtualUserManager/index.d.ts +168 -0
- package/dist/VirtualUserManager/index.d.ts.map +1 -0
- package/dist/VirtualUserManager/index.js +375 -0
- package/dist/commands/adduser.d.ts +3 -0
- package/dist/commands/adduser.d.ts.map +1 -0
- package/dist/commands/adduser.js +18 -0
- package/dist/commands/cat.d.ts +3 -0
- package/dist/commands/cat.d.ts.map +1 -0
- package/dist/commands/cat.js +15 -0
- package/dist/commands/cd.d.ts +3 -0
- package/dist/commands/cd.d.ts.map +1 -0
- package/dist/commands/cd.js +17 -0
- package/dist/commands/clear.d.ts +3 -0
- package/dist/commands/clear.d.ts.map +1 -0
- package/dist/commands/clear.js +5 -0
- package/dist/commands/command-helpers.d.ts +23 -0
- package/dist/commands/command-helpers.d.ts.map +1 -0
- package/dist/commands/command-helpers.js +139 -0
- package/dist/commands/curl.d.ts +3 -0
- package/dist/commands/curl.d.ts.map +1 -0
- package/dist/commands/curl.js +44 -0
- package/dist/commands/deluser.d.ts +3 -0
- package/dist/commands/deluser.d.ts.map +1 -0
- package/dist/commands/deluser.js +15 -0
- package/dist/commands/echo.d.ts +3 -0
- package/dist/commands/echo.d.ts.map +1 -0
- package/dist/commands/echo.js +22 -0
- package/dist/commands/env.d.ts +3 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +18 -0
- package/dist/commands/exit.d.ts +3 -0
- package/dist/commands/exit.d.ts.map +1 -0
- package/dist/commands/exit.js +5 -0
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +34 -0
- package/dist/commands/grep.d.ts +3 -0
- package/dist/commands/grep.d.ts.map +1 -0
- package/dist/commands/grep.js +69 -0
- package/dist/commands/help.d.ts +3 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +7 -0
- package/dist/commands/helpers.d.ts +26 -0
- package/dist/commands/helpers.d.ts.map +1 -0
- package/dist/commands/helpers.js +160 -0
- package/dist/commands/hostname.d.ts +3 -0
- package/dist/commands/hostname.d.ts.map +1 -0
- package/dist/commands/hostname.js +5 -0
- package/dist/commands/htop.d.ts +3 -0
- package/dist/commands/htop.d.ts.map +1 -0
- package/dist/commands/htop.js +10 -0
- package/dist/commands/index.d.ts +8 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +212 -0
- package/dist/commands/ls.d.ts +3 -0
- package/dist/commands/ls.d.ts.map +1 -0
- package/dist/commands/ls.js +47 -0
- package/dist/commands/mkdir.d.ts +3 -0
- package/dist/commands/mkdir.d.ts.map +1 -0
- package/dist/commands/mkdir.js +21 -0
- package/dist/commands/nano.d.ts +3 -0
- package/dist/commands/nano.d.ts.map +1 -0
- package/dist/commands/nano.js +27 -0
- package/dist/commands/neofetch.d.ts +3 -0
- package/dist/commands/neofetch.d.ts.map +1 -0
- package/dist/commands/neofetch.js +32 -0
- package/dist/commands/pwd.d.ts +3 -0
- package/dist/commands/pwd.d.ts.map +1 -0
- package/dist/commands/pwd.js +5 -0
- package/dist/commands/rm.d.ts +3 -0
- package/dist/commands/rm.d.ts.map +1 -0
- package/dist/commands/rm.js +29 -0
- package/dist/commands/set.d.ts +7 -0
- package/dist/commands/set.d.ts.map +1 -0
- package/dist/commands/set.js +64 -0
- package/dist/commands/sh.d.ts +4 -0
- package/dist/commands/sh.d.ts.map +1 -0
- package/dist/commands/sh.js +45 -0
- package/dist/commands/su.d.ts +3 -0
- package/dist/commands/su.d.ts.map +1 -0
- package/dist/commands/su.js +24 -0
- package/dist/commands/sudo.d.ts +3 -0
- package/dist/commands/sudo.d.ts.map +1 -0
- package/dist/commands/sudo.js +47 -0
- package/dist/commands/touch.d.ts +3 -0
- package/dist/commands/touch.d.ts.map +1 -0
- package/dist/commands/touch.js +18 -0
- package/dist/commands/tree.d.ts +3 -0
- package/dist/commands/tree.d.ts.map +1 -0
- package/dist/commands/tree.js +11 -0
- package/dist/commands/unset.d.ts +3 -0
- package/dist/commands/unset.d.ts.map +1 -0
- package/dist/commands/unset.js +15 -0
- package/dist/commands/wget.d.ts +3 -0
- package/dist/commands/wget.d.ts.map +1 -0
- package/dist/commands/wget.js +113 -0
- package/dist/commands/who.d.ts +3 -0
- package/dist/commands/who.d.ts.map +1 -0
- package/dist/commands/who.js +15 -0
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +5 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/modules/neofetch.d.ts +19 -0
- package/dist/modules/neofetch.d.ts.map +1 -0
- package/dist/modules/neofetch.js +284 -0
- package/dist/modules/shellInteractive.d.ts +6 -0
- package/dist/modules/shellInteractive.d.ts.map +1 -0
- package/dist/modules/shellInteractive.js +26 -0
- package/dist/modules/shellRuntime.d.ts +11 -0
- package/dist/modules/shellRuntime.d.ts.map +1 -0
- package/dist/modules/shellRuntime.js +52 -0
- package/dist/standalone.d.ts +2 -0
- package/dist/standalone.d.ts.map +1 -0
- package/dist/standalone.js +25 -0
- package/dist/types/commands.d.ts +89 -0
- package/dist/types/commands.d.ts.map +1 -0
- package/dist/types/commands.js +0 -0
- package/dist/types/pipeline.d.ts +23 -0
- package/dist/types/pipeline.d.ts.map +1 -0
- package/dist/types/pipeline.js +0 -0
- package/dist/types/streams.d.ts +32 -0
- package/dist/types/streams.d.ts.map +1 -0
- package/dist/types/streams.js +0 -0
- package/dist/types/vfs.d.ts +71 -0
- package/dist/types/vfs.d.ts.map +1 -0
- package/dist/types/vfs.js +0 -0
- package/package.json +4 -2
- package/src/VirtualShell/shell.ts +3 -3
- package/src/commands/neofetch.ts +1 -1
- package/{modules → src/modules}/neofetch.ts +56 -51
- package/{modules → src/modules}/shellInteractive.ts +16 -4
- package/tsconfig.json +19 -8
- /package/{modules → src/modules}/shellRuntime.ts +0 -0
package/.vscode/settings.json
CHANGED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { CommandResult } from "../types/commands";
|
|
2
|
+
import type { VirtualShell } from "../VirtualShell";
|
|
3
|
+
/**
|
|
4
|
+
* Programmatic client for executing shell commands against a virtual shell.
|
|
5
|
+
*
|
|
6
|
+
* Maintains working-directory state across invocations and runs commands as a
|
|
7
|
+
* single authenticated user without SSH transport overhead.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const shell = new VirtualShell("typescript-vm");
|
|
12
|
+
* const client = new SshClient(shell, "alice");
|
|
13
|
+
* const result = await client.cd("/tmp");
|
|
14
|
+
* const list = await client.ls();
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare class SshClient {
|
|
18
|
+
private shell;
|
|
19
|
+
private username;
|
|
20
|
+
private currentCwd;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a programmatic client bound to a virtual shell and user.
|
|
23
|
+
*
|
|
24
|
+
* @param shell Parent virtual shell instance.
|
|
25
|
+
* @param username Login user for all commands.
|
|
26
|
+
*/
|
|
27
|
+
constructor(shell: VirtualShell, username: string);
|
|
28
|
+
/**
|
|
29
|
+
* Executes raw shell command.
|
|
30
|
+
*
|
|
31
|
+
* @param command Unparsed command line.
|
|
32
|
+
* @returns Command result with stdout/stderr/exitCode.
|
|
33
|
+
*/
|
|
34
|
+
exec(command: string): Promise<CommandResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Lists directory contents.
|
|
37
|
+
*
|
|
38
|
+
* @param path Target directory, defaults to cwd.
|
|
39
|
+
* @returns Result with directory listing in stdout.
|
|
40
|
+
*/
|
|
41
|
+
ls(path?: string): Promise<CommandResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Prints current working directory.
|
|
44
|
+
*
|
|
45
|
+
* @returns Result with cwd path in stdout.
|
|
46
|
+
*/
|
|
47
|
+
pwd(): Promise<CommandResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Changes working directory.
|
|
50
|
+
*
|
|
51
|
+
* @param path Target directory path.
|
|
52
|
+
* @returns Result; updates internal cwd on success.
|
|
53
|
+
*/
|
|
54
|
+
cd(path: string): Promise<CommandResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Reads file content.
|
|
57
|
+
*
|
|
58
|
+
* @param path File path.
|
|
59
|
+
* @returns Result with file content in stdout.
|
|
60
|
+
*/
|
|
61
|
+
cat(path: string): Promise<CommandResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Creates directory.
|
|
64
|
+
*
|
|
65
|
+
* @param path Directory path.
|
|
66
|
+
* @param recursive When true, create parents.
|
|
67
|
+
* @returns Result from mkdir command.
|
|
68
|
+
*/
|
|
69
|
+
mkdir(path: string, recursive?: boolean): Promise<CommandResult>;
|
|
70
|
+
/**
|
|
71
|
+
* Creates file (empty).
|
|
72
|
+
*
|
|
73
|
+
* @param path File path.
|
|
74
|
+
* @returns Result from touch command.
|
|
75
|
+
*/
|
|
76
|
+
touch(path: string): Promise<CommandResult>;
|
|
77
|
+
/**
|
|
78
|
+
* Removes file or directory.
|
|
79
|
+
*
|
|
80
|
+
* @param path Target path.
|
|
81
|
+
* @param recursive When true, delete directory tree.
|
|
82
|
+
* @returns Result from rm command.
|
|
83
|
+
*/
|
|
84
|
+
rm(path: string, recursive?: boolean): Promise<CommandResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Writes file content.
|
|
87
|
+
*
|
|
88
|
+
* @param path Target file path.
|
|
89
|
+
* @param content Text to write.
|
|
90
|
+
* @returns Result from touch/write simulation.
|
|
91
|
+
*/
|
|
92
|
+
writeFile(path: string, content: string): Promise<CommandResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Reads file content programmatically.
|
|
95
|
+
*
|
|
96
|
+
* @param path Target file path.
|
|
97
|
+
* @returns File content as string or error in result.
|
|
98
|
+
*/
|
|
99
|
+
readFile(path: string): Promise<CommandResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Gets current working directory.
|
|
102
|
+
*
|
|
103
|
+
* @returns Normalized cwd path.
|
|
104
|
+
*/
|
|
105
|
+
getCwd(): string;
|
|
106
|
+
/**
|
|
107
|
+
* Gets logged-in username.
|
|
108
|
+
*
|
|
109
|
+
* @returns Associated username.
|
|
110
|
+
*/
|
|
111
|
+
getUsername(): string;
|
|
112
|
+
/**
|
|
113
|
+
* Renders tree view of directory.
|
|
114
|
+
*
|
|
115
|
+
* @param path Target directory, defaults to cwd.
|
|
116
|
+
* @returns Result with ASCII tree in stdout.
|
|
117
|
+
*/
|
|
118
|
+
tree(path?: string): Promise<CommandResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Shows current user.
|
|
121
|
+
*
|
|
122
|
+
* @returns Result from whoami command.
|
|
123
|
+
*/
|
|
124
|
+
whoami(): Promise<CommandResult>;
|
|
125
|
+
/**
|
|
126
|
+
* Shows hostname.
|
|
127
|
+
*
|
|
128
|
+
* @returns Result from hostname command.
|
|
129
|
+
*/
|
|
130
|
+
hostname(): Promise<CommandResult>;
|
|
131
|
+
/**
|
|
132
|
+
* Lists active users/sessions.
|
|
133
|
+
*
|
|
134
|
+
* @returns Result from who command.
|
|
135
|
+
*/
|
|
136
|
+
who(): Promise<CommandResult>;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHClient/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,qBAAa,SAAS;IAUpB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAVjB,OAAO,CAAC,UAAU,CAAO;IAEzB;;;;;OAKG;gBAEM,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM;IAGzB;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA0BnD;;;;;OAKG;IACG,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAK/C;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;IAInC;;;;;OAKG;IACG,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAQ9C;;;;;OAKG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAI/C;;;;;;OAMG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAKpE;;;;;OAKG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAIjD;;;;;;OAMG;IACG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAKjE;;;;;;OAMG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAiBtE;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAiBpD;;;;OAIG;IACH,MAAM,IAAI,MAAM;IAIhB;;;;OAIG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;OAKG;IACG,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAKjD;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;IAItC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC;IAIxC;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;CAGnC"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { runCommand } from "../commands";
|
|
2
|
+
/**
|
|
3
|
+
* Programmatic client for executing shell commands against a virtual shell.
|
|
4
|
+
*
|
|
5
|
+
* Maintains working-directory state across invocations and runs commands as a
|
|
6
|
+
* single authenticated user without SSH transport overhead.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const shell = new VirtualShell("typescript-vm");
|
|
11
|
+
* const client = new SshClient(shell, "alice");
|
|
12
|
+
* const result = await client.cd("/tmp");
|
|
13
|
+
* const list = await client.ls();
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class SshClient {
|
|
17
|
+
shell;
|
|
18
|
+
username;
|
|
19
|
+
currentCwd = "/";
|
|
20
|
+
/**
|
|
21
|
+
* Creates a programmatic client bound to a virtual shell and user.
|
|
22
|
+
*
|
|
23
|
+
* @param shell Parent virtual shell instance.
|
|
24
|
+
* @param username Login user for all commands.
|
|
25
|
+
*/
|
|
26
|
+
constructor(shell, username) {
|
|
27
|
+
this.shell = shell;
|
|
28
|
+
this.username = username;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Executes raw shell command.
|
|
32
|
+
*
|
|
33
|
+
* @param command Unparsed command line.
|
|
34
|
+
* @returns Command result with stdout/stderr/exitCode.
|
|
35
|
+
*/
|
|
36
|
+
async exec(command) {
|
|
37
|
+
const vfs = this.shell.getVfs();
|
|
38
|
+
const users = this.shell.getUsers();
|
|
39
|
+
const hostname = this.shell.getHostname();
|
|
40
|
+
if (!vfs || !users) {
|
|
41
|
+
throw new Error("SSH client not started");
|
|
42
|
+
}
|
|
43
|
+
const result = runCommand(command, this.username, hostname, "exec", this.currentCwd, this.shell);
|
|
44
|
+
// Handle async results
|
|
45
|
+
if (result instanceof Promise) {
|
|
46
|
+
return await result;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Lists directory contents.
|
|
52
|
+
*
|
|
53
|
+
* @param path Target directory, defaults to cwd.
|
|
54
|
+
* @returns Result with directory listing in stdout.
|
|
55
|
+
*/
|
|
56
|
+
async ls(path) {
|
|
57
|
+
const target = path ?? ".";
|
|
58
|
+
return this.exec(`ls ${target}`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Prints current working directory.
|
|
62
|
+
*
|
|
63
|
+
* @returns Result with cwd path in stdout.
|
|
64
|
+
*/
|
|
65
|
+
async pwd() {
|
|
66
|
+
return this.exec("pwd");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Changes working directory.
|
|
70
|
+
*
|
|
71
|
+
* @param path Target directory path.
|
|
72
|
+
* @returns Result; updates internal cwd on success.
|
|
73
|
+
*/
|
|
74
|
+
async cd(path) {
|
|
75
|
+
const result = await this.exec(`cd ${path}`);
|
|
76
|
+
if (result.nextCwd && result.exitCode !== 1) {
|
|
77
|
+
this.currentCwd = result.nextCwd;
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Reads file content.
|
|
83
|
+
*
|
|
84
|
+
* @param path File path.
|
|
85
|
+
* @returns Result with file content in stdout.
|
|
86
|
+
*/
|
|
87
|
+
async cat(path) {
|
|
88
|
+
return this.exec(`cat ${path}`);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Creates directory.
|
|
92
|
+
*
|
|
93
|
+
* @param path Directory path.
|
|
94
|
+
* @param recursive When true, create parents.
|
|
95
|
+
* @returns Result from mkdir command.
|
|
96
|
+
*/
|
|
97
|
+
async mkdir(path, recursive = false) {
|
|
98
|
+
const flag = recursive ? "-p " : "";
|
|
99
|
+
return this.exec(`mkdir ${flag}${path}`);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Creates file (empty).
|
|
103
|
+
*
|
|
104
|
+
* @param path File path.
|
|
105
|
+
* @returns Result from touch command.
|
|
106
|
+
*/
|
|
107
|
+
async touch(path) {
|
|
108
|
+
return this.exec(`touch ${path}`);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Removes file or directory.
|
|
112
|
+
*
|
|
113
|
+
* @param path Target path.
|
|
114
|
+
* @param recursive When true, delete directory tree.
|
|
115
|
+
* @returns Result from rm command.
|
|
116
|
+
*/
|
|
117
|
+
async rm(path, recursive = false) {
|
|
118
|
+
const flag = recursive ? "-r " : "";
|
|
119
|
+
return this.exec(`rm ${flag}${path}`);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Writes file content.
|
|
123
|
+
*
|
|
124
|
+
* @param path Target file path.
|
|
125
|
+
* @param content Text to write.
|
|
126
|
+
* @returns Result from touch/write simulation.
|
|
127
|
+
*/
|
|
128
|
+
async writeFile(path, content) {
|
|
129
|
+
const vfs = this.shell.getVfs();
|
|
130
|
+
if (!vfs) {
|
|
131
|
+
throw new Error("SSH client not started");
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
this.shell.writeFileAsUser(this.username, path, content);
|
|
135
|
+
return { stdout: `File '${path}' written`, exitCode: 0 };
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return {
|
|
139
|
+
stderr: `Failed to write '${path}': ${error instanceof Error ? error.message : String(error)}`,
|
|
140
|
+
exitCode: 1,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Reads file content programmatically.
|
|
146
|
+
*
|
|
147
|
+
* @param path Target file path.
|
|
148
|
+
* @returns File content as string or error in result.
|
|
149
|
+
*/
|
|
150
|
+
async readFile(path) {
|
|
151
|
+
const vfs = this.shell.getVfs();
|
|
152
|
+
if (!vfs) {
|
|
153
|
+
throw new Error("SSH client not started");
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const content = vfs.readFile(path);
|
|
157
|
+
return { stdout: content, exitCode: 0 };
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
return {
|
|
161
|
+
stderr: `Failed to read '${path}': ${error instanceof Error ? error.message : String(error)}`,
|
|
162
|
+
exitCode: 1,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Gets current working directory.
|
|
168
|
+
*
|
|
169
|
+
* @returns Normalized cwd path.
|
|
170
|
+
*/
|
|
171
|
+
getCwd() {
|
|
172
|
+
return this.currentCwd;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Gets logged-in username.
|
|
176
|
+
*
|
|
177
|
+
* @returns Associated username.
|
|
178
|
+
*/
|
|
179
|
+
getUsername() {
|
|
180
|
+
return this.username;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Renders tree view of directory.
|
|
184
|
+
*
|
|
185
|
+
* @param path Target directory, defaults to cwd.
|
|
186
|
+
* @returns Result with ASCII tree in stdout.
|
|
187
|
+
*/
|
|
188
|
+
async tree(path) {
|
|
189
|
+
const target = path ?? ".";
|
|
190
|
+
return this.exec(`tree ${target}`);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Shows current user.
|
|
194
|
+
*
|
|
195
|
+
* @returns Result from whoami command.
|
|
196
|
+
*/
|
|
197
|
+
async whoami() {
|
|
198
|
+
return this.exec("whoami");
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Shows hostname.
|
|
202
|
+
*
|
|
203
|
+
* @returns Result from hostname command.
|
|
204
|
+
*/
|
|
205
|
+
async hostname() {
|
|
206
|
+
return this.exec("hostname");
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Lists active users/sessions.
|
|
210
|
+
*
|
|
211
|
+
* @returns Result from who command.
|
|
212
|
+
*/
|
|
213
|
+
async who() {
|
|
214
|
+
return this.exec("who");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ExecStream } from "../types/streams";
|
|
2
|
+
import type { VirtualShell } from "../VirtualShell";
|
|
3
|
+
export declare function runExec(stream: ExecStream, cmd: string, authUser: string, hostname: string, shell: VirtualShell): void;
|
|
4
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/exec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AASpD,wBAAgB,OAAO,CACtB,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,GACjB,IAAI,CAiBN"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { runCommand } from "../commands";
|
|
2
|
+
function toTtyLines(text) {
|
|
3
|
+
return text
|
|
4
|
+
.replace(/\r\n/g, "\n")
|
|
5
|
+
.replace(/\r/g, "\n")
|
|
6
|
+
.replace(/\n/g, "\r\n");
|
|
7
|
+
}
|
|
8
|
+
export function runExec(stream, cmd, authUser, hostname, shell) {
|
|
9
|
+
Promise.resolve(runCommand(cmd, authUser, hostname, "exec", `/home/${authUser}`, shell)).then((result) => {
|
|
10
|
+
if (result.stdout) {
|
|
11
|
+
stream.write(`${toTtyLines(result.stdout)}\r\n`);
|
|
12
|
+
}
|
|
13
|
+
if (result.stderr) {
|
|
14
|
+
stream.stderr.write(`${toTtyLines(result.stderr)}\r\n`);
|
|
15
|
+
}
|
|
16
|
+
stream.exit(result.exitCode ?? 0);
|
|
17
|
+
console.log(shell.vfs);
|
|
18
|
+
void shell.vfs.flushMirror();
|
|
19
|
+
stream.end();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CommandMode, CommandResult } from "../types/commands";
|
|
2
|
+
import type { Pipeline } from "../types/pipeline";
|
|
3
|
+
import type { VirtualShell } from "../VirtualShell";
|
|
4
|
+
/**
|
|
5
|
+
* Execute a parsed pipeline, chaining commands and handling redirections.
|
|
6
|
+
* Manages stdout/stderr flow between commands and file I/O.
|
|
7
|
+
*/
|
|
8
|
+
export declare function executePipeline(pipeline: Pipeline, authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell): Promise<CommandResult>;
|
|
9
|
+
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/executor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,mBAAmB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;GAGG;AACH,wBAAsB,eAAe,CACpC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,GACjB,OAAO,CAAC,aAAa,CAAC,CA0BxB"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { runCommand as runSingleCommand } from "../commands";
|
|
2
|
+
import { resolvePath } from "../commands/helpers";
|
|
3
|
+
/**
|
|
4
|
+
* Execute a parsed pipeline, chaining commands and handling redirections.
|
|
5
|
+
* Manages stdout/stderr flow between commands and file I/O.
|
|
6
|
+
*/
|
|
7
|
+
export async function executePipeline(pipeline, authUser, hostname, mode, cwd, shell) {
|
|
8
|
+
if (pipeline.commands.length === 0) {
|
|
9
|
+
return { exitCode: 0 };
|
|
10
|
+
}
|
|
11
|
+
if (pipeline.commands.length === 1) {
|
|
12
|
+
// Single command with possible redirections
|
|
13
|
+
return executeSingleCommandWithRedirections(pipeline.commands[0], authUser, hostname, mode, cwd, shell);
|
|
14
|
+
}
|
|
15
|
+
// Multiple commands in a pipeline
|
|
16
|
+
return executePipelineChain(pipeline.commands, authUser, hostname, mode, cwd, shell);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Execute a single command with input/output redirections
|
|
20
|
+
*/
|
|
21
|
+
async function executeSingleCommandWithRedirections(cmd, authUser, hostname, mode, cwd, shell) {
|
|
22
|
+
// Prepare input if input file specified
|
|
23
|
+
let stdin;
|
|
24
|
+
if (cmd.inputFile) {
|
|
25
|
+
const inputPath = resolvePath(cwd, cmd.inputFile);
|
|
26
|
+
try {
|
|
27
|
+
stdin = shell.vfs.readFile(inputPath);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return {
|
|
31
|
+
stderr: `cat: ${cmd.inputFile}: No such file or directory`,
|
|
32
|
+
exitCode: 1,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Build raw input for the command
|
|
37
|
+
const rawInput = [cmd.name, ...cmd.args].join(" ");
|
|
38
|
+
// Run the command with potential input
|
|
39
|
+
const result = await runSingleCommand(rawInput, authUser, hostname, mode, cwd, shell, stdin);
|
|
40
|
+
// Handle output redirection
|
|
41
|
+
if (cmd.outputFile) {
|
|
42
|
+
const outputPath = resolvePath(cwd, cmd.outputFile);
|
|
43
|
+
const output = result.stdout || "";
|
|
44
|
+
try {
|
|
45
|
+
if (cmd.appendOutput) {
|
|
46
|
+
try {
|
|
47
|
+
const existing = shell.vfs.readFile(outputPath);
|
|
48
|
+
shell.writeFileAsUser(authUser, outputPath, existing + output);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
shell.writeFileAsUser(authUser, outputPath, output);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
shell.writeFileAsUser(authUser, outputPath, output);
|
|
56
|
+
}
|
|
57
|
+
return { ...result, stdout: "" };
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return {
|
|
61
|
+
...result,
|
|
62
|
+
stderr: `Failed to write to ${cmd.outputFile}`,
|
|
63
|
+
exitCode: 1,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Execute a chain of commands connected by pipes
|
|
71
|
+
*/
|
|
72
|
+
async function executePipelineChain(commands, authUser, hostname, mode, cwd, shell) {
|
|
73
|
+
let currentOutput = "";
|
|
74
|
+
let exitCode = 0;
|
|
75
|
+
for (let i = 0; i < commands.length; i++) {
|
|
76
|
+
const cmd = commands[i];
|
|
77
|
+
// Handle input file for first command
|
|
78
|
+
if (i === 0 && cmd.inputFile) {
|
|
79
|
+
const inputPath = resolvePath(cwd, cmd.inputFile);
|
|
80
|
+
try {
|
|
81
|
+
currentOutput = shell.vfs.readFile(inputPath);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return {
|
|
85
|
+
stderr: `cat: ${cmd.inputFile}: No such file or directory`,
|
|
86
|
+
exitCode: 1,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Build raw input
|
|
91
|
+
const rawInput = [cmd.name, ...cmd.args].join(" ");
|
|
92
|
+
// Create a modified context that might accept stdin
|
|
93
|
+
// For now, we'll append input as an additional arg for commands that support it
|
|
94
|
+
const result = await runSingleCommand(rawInput, authUser, hostname, mode, cwd, shell, currentOutput);
|
|
95
|
+
exitCode = result.exitCode ?? 0;
|
|
96
|
+
// Handle output redirection (only for last command)
|
|
97
|
+
if (i === commands.length - 1 && cmd.outputFile) {
|
|
98
|
+
const outputPath = resolvePath(cwd, cmd.outputFile);
|
|
99
|
+
const output = result.stdout || "";
|
|
100
|
+
try {
|
|
101
|
+
if (cmd.appendOutput) {
|
|
102
|
+
try {
|
|
103
|
+
const existing = shell.vfs.readFile(outputPath);
|
|
104
|
+
shell.writeFileAsUser(authUser, outputPath, existing + output);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
shell.writeFileAsUser(authUser, outputPath, output);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
shell.writeFileAsUser(authUser, outputPath, output);
|
|
112
|
+
}
|
|
113
|
+
currentOutput = "";
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return {
|
|
117
|
+
stderr: `Failed to write to ${cmd.outputFile}`,
|
|
118
|
+
exitCode: 1,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Pass output to next command
|
|
124
|
+
currentOutput = result.stdout || "";
|
|
125
|
+
}
|
|
126
|
+
if (result.stderr && exitCode !== 0) {
|
|
127
|
+
return { stderr: result.stderr, exitCode };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return { stdout: currentOutput, exitCode };
|
|
131
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostKey.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/hostKey.ts"],"names":[],"mappings":"AAIA,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,MAAsB,GAAG,MAAM,CAgB3E"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { generateKeyPairSync } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
export function loadOrCreateHostKey(baseDir = process.cwd()) {
|
|
5
|
+
const hostKeyPath = resolve(baseDir, ".ssh-mimic", "host_rsa");
|
|
6
|
+
if (existsSync(hostKeyPath)) {
|
|
7
|
+
return readFileSync(hostKeyPath, "utf8");
|
|
8
|
+
}
|
|
9
|
+
const privateKey = generateKeyPairSync("rsa", {
|
|
10
|
+
modulusLength: 2048,
|
|
11
|
+
privateKeyEncoding: { type: "pkcs1", format: "pem" },
|
|
12
|
+
publicKeyEncoding: { type: "pkcs1", format: "pem" },
|
|
13
|
+
}).privateKey;
|
|
14
|
+
mkdirSync(dirname(hostKeyPath), { recursive: true });
|
|
15
|
+
writeFileSync(hostKeyPath, privateKey, { mode: 0o600 });
|
|
16
|
+
return privateKey;
|
|
17
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Server as SshServer } from "ssh2";
|
|
2
|
+
import { VirtualShell } from "../VirtualShell";
|
|
3
|
+
/**
|
|
4
|
+
* SSH server facade that wires the virtual shell runtime into ssh2 sessions.
|
|
5
|
+
*
|
|
6
|
+
* This class is exported as `VirtualSshServer` for public API compatibility.
|
|
7
|
+
* Create an instance, call {@link SshMimic.start}, and stop it with
|
|
8
|
+
* {@link SshMimic.stop} when your process exits.
|
|
9
|
+
*/
|
|
10
|
+
declare class SshMimic {
|
|
11
|
+
port: number;
|
|
12
|
+
server: SshServer | null;
|
|
13
|
+
private shell;
|
|
14
|
+
private shellHostname;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new SSH mimic server instance.
|
|
17
|
+
*
|
|
18
|
+
* @param port TCP port to bind on localhost.
|
|
19
|
+
* @param hostname Virtual hostname used for the SSH ident and default shell label.
|
|
20
|
+
* @param shell Optional preconfigured virtual shell instance to reuse.
|
|
21
|
+
*/
|
|
22
|
+
constructor({ port, hostname, shell, }: {
|
|
23
|
+
port: number;
|
|
24
|
+
hostname?: string;
|
|
25
|
+
shell?: VirtualShell;
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Starts server and initializes virtual filesystem, users, and handlers.
|
|
29
|
+
*
|
|
30
|
+
* @returns Promise resolved with bound listening port.
|
|
31
|
+
*/
|
|
32
|
+
start(): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* Stops server if running.
|
|
35
|
+
*/
|
|
36
|
+
stop(): void;
|
|
37
|
+
}
|
|
38
|
+
export { SshMimic };
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C;;;;;;GAMG;AACH,cAAM,QAAQ;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,aAAa,CAAS;IAE9B;;;;;;OAMG;gBACS,EACX,IAAI,EACJ,QAA0B,EAC1B,KAAkC,GAClC,EAAE;QACF,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,YAAY,CAAC;KACrB;IAOD;;;;OAIG;IACU,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAwGrC;;OAEG;IACI,IAAI,IAAI,IAAI;CAOnB;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|