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
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { assertPathAccess, resolvePath } from "./helpers";
|
|
2
|
+
export const touchCommand = {
|
|
3
|
+
name: "touch",
|
|
4
|
+
params: ["<file>"],
|
|
5
|
+
run: ({ authUser, shell, cwd, args }) => {
|
|
6
|
+
if (args.length === 0) {
|
|
7
|
+
return { stderr: "touch: missing file operand", exitCode: 1 };
|
|
8
|
+
}
|
|
9
|
+
for (const file of args) {
|
|
10
|
+
const target = resolvePath(cwd, file);
|
|
11
|
+
assertPathAccess(authUser, target, "touch");
|
|
12
|
+
if (!shell.vfs.exists(target)) {
|
|
13
|
+
shell.writeFileAsUser(authUser, target, "");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return { exitCode: 0 };
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/commands/tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAQzB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getArg } from "./command-helpers";
|
|
2
|
+
import { assertPathAccess, resolvePath } from "./helpers";
|
|
3
|
+
export const treeCommand = {
|
|
4
|
+
name: "tree",
|
|
5
|
+
params: ["[path]"],
|
|
6
|
+
run: ({ authUser, shell, cwd, args }) => {
|
|
7
|
+
const target = resolvePath(cwd, getArg(args, 0) ?? cwd);
|
|
8
|
+
assertPathAccess(authUser, target, "tree");
|
|
9
|
+
return { stdout: shell.vfs.tree(target), exitCode: 0 };
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unset.d.ts","sourceRoot":"","sources":["../../src/commands/unset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,YAAY,EAAE,WAe1B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { setEnvVar } from "./set";
|
|
2
|
+
export const unsetCommand = {
|
|
3
|
+
name: "unset",
|
|
4
|
+
params: ["<VAR...>"],
|
|
5
|
+
run: ({ args }) => {
|
|
6
|
+
if (args.length === 0) {
|
|
7
|
+
return { stderr: "unset: missing variable name", exitCode: 1 };
|
|
8
|
+
}
|
|
9
|
+
// Unset (remove) all specified variables
|
|
10
|
+
for (const varName of args) {
|
|
11
|
+
setEnvVar(varName, "");
|
|
12
|
+
}
|
|
13
|
+
return { exitCode: 0 };
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wget.d.ts","sourceRoot":"","sources":["../../src/commands/wget.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA8ErD,eAAO,MAAM,WAAW,EAAE,WA6DzB,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { ifFlag, parseArgs } from "./command-helpers";
|
|
6
|
+
import { assertPathAccess, normalizeTerminalOutput, resolvePath, runHostCommand, stripUrlFilename, } from "./helpers";
|
|
7
|
+
function runHostWget(args) {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
let childProcess;
|
|
10
|
+
try {
|
|
11
|
+
childProcess = spawn("wget", args, {
|
|
12
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
resolve({
|
|
17
|
+
stdout: "",
|
|
18
|
+
stderr: `wget: ${error instanceof Error ? error.message : String(error)}`,
|
|
19
|
+
exitCode: 1,
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
let stdout = "";
|
|
24
|
+
let stderr = "";
|
|
25
|
+
const stdoutStream = childProcess.stdout;
|
|
26
|
+
const stderrStream = childProcess.stderr;
|
|
27
|
+
if (!stdoutStream || !stderrStream) {
|
|
28
|
+
resolve({
|
|
29
|
+
stdout: "",
|
|
30
|
+
stderr: "wget: failed to capture process output",
|
|
31
|
+
exitCode: 1,
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
stdoutStream.setEncoding("utf8");
|
|
36
|
+
stderrStream.setEncoding("utf8");
|
|
37
|
+
stdoutStream.on("data", (chunk) => {
|
|
38
|
+
stdout += chunk;
|
|
39
|
+
});
|
|
40
|
+
stderrStream.on("data", (chunk) => {
|
|
41
|
+
stderr += chunk;
|
|
42
|
+
});
|
|
43
|
+
childProcess.on("error", (error) => {
|
|
44
|
+
const errorCode = error instanceof Error && "code" in error
|
|
45
|
+
? String(error.code ?? "")
|
|
46
|
+
: "";
|
|
47
|
+
resolve({
|
|
48
|
+
stdout: "",
|
|
49
|
+
stderr: `wget: ${error.message}`,
|
|
50
|
+
exitCode: errorCode === "ENOENT" ? 127 : 1,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
childProcess.on("close", (code) => {
|
|
54
|
+
resolve({
|
|
55
|
+
stdout,
|
|
56
|
+
stderr,
|
|
57
|
+
exitCode: code ?? 1,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export const wgetCommand = {
|
|
63
|
+
name: "wget",
|
|
64
|
+
params: ["[url]"],
|
|
65
|
+
run: async ({ authUser, cwd, args, shell }) => {
|
|
66
|
+
const { flagsWithValues, positionals } = parseArgs(args, {
|
|
67
|
+
flagsWithValue: ["-o", "-O", "--output", "--output-document"],
|
|
68
|
+
});
|
|
69
|
+
const outputPath = flagsWithValues.get("-o") ||
|
|
70
|
+
flagsWithValues.get("-O") ||
|
|
71
|
+
flagsWithValues.get("--output") ||
|
|
72
|
+
flagsWithValues.get("--output-document") ||
|
|
73
|
+
null;
|
|
74
|
+
const url = positionals[0];
|
|
75
|
+
if (!url) {
|
|
76
|
+
return { stderr: "wget: missing URL", exitCode: 1 };
|
|
77
|
+
}
|
|
78
|
+
const isHelpLike = ifFlag(args, ["-h", "--help", "-V", "--version"]);
|
|
79
|
+
if (isHelpLike) {
|
|
80
|
+
const result = await runHostWget(args);
|
|
81
|
+
return {
|
|
82
|
+
stdout: normalizeTerminalOutput(result.stdout),
|
|
83
|
+
stderr: result.stderr
|
|
84
|
+
? normalizeTerminalOutput(result.stderr)
|
|
85
|
+
: undefined,
|
|
86
|
+
exitCode: result.exitCode,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const tempDir = await mkdtemp(join(tmpdir(), "virtual-env-js-wget-"));
|
|
90
|
+
const tempFile = join(tempDir, "download");
|
|
91
|
+
try {
|
|
92
|
+
const hostArgs = [...positionals, "-O", tempFile];
|
|
93
|
+
const result = await runHostCommand("wget", hostArgs);
|
|
94
|
+
if (result.exitCode !== 0) {
|
|
95
|
+
return {
|
|
96
|
+
stderr: normalizeTerminalOutput(result.stderr || `wget: exited with code ${result.exitCode}`),
|
|
97
|
+
exitCode: result.exitCode,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const content = await readFile(tempFile, "utf8");
|
|
101
|
+
const target = resolvePath(cwd, outputPath || stripUrlFilename(url));
|
|
102
|
+
assertPathAccess(authUser, target, "wget");
|
|
103
|
+
shell.writeFileAsUser(authUser, target, content);
|
|
104
|
+
return {
|
|
105
|
+
stdout: `saved ${target}`,
|
|
106
|
+
exitCode: 0,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"who.d.ts","sourceRoot":"","sources":["../../src/commands/who.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,UAAU,EAAE,WAcxB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { formatLoginDate } from "../SSHMimic/loginFormat";
|
|
2
|
+
export const whoCommand = {
|
|
3
|
+
name: "who",
|
|
4
|
+
params: [],
|
|
5
|
+
run: ({ shell }) => {
|
|
6
|
+
const lines = shell.users.listActiveSessions().map((session) => {
|
|
7
|
+
const loginAt = new Date(session.startedAt);
|
|
8
|
+
const displayDate = Number.isNaN(loginAt.getTime())
|
|
9
|
+
? session.startedAt
|
|
10
|
+
: formatLoginDate(loginAt);
|
|
11
|
+
return `${session.username} ${session.tty} ${displayDate} (${session.remoteAddress || "unknown"})`;
|
|
12
|
+
});
|
|
13
|
+
return { stdout: lines.join("\n"), exitCode: 0 };
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,aAAa,EAAE,WAI3B,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SshClient } from "./SSHClient";
|
|
2
|
+
import { SshMimic } from "./SSHMimic/index";
|
|
3
|
+
import VirtualFileSystem from "./VirtualFileSystem";
|
|
4
|
+
import { VirtualShell } from "./VirtualShell";
|
|
5
|
+
import { VirtualUserManager } from "./VirtualUserManager";
|
|
6
|
+
export type { CommandContext, CommandMode, CommandOutcome, CommandResult, NanoEditorSession, ShellModule, SudoChallenge, } from "./types/commands";
|
|
7
|
+
export type { ExecStream, ShellStream } from "./types/streams";
|
|
8
|
+
export type { RemoveOptions, VfsBaseNode, VfsDirectoryNode, VfsFileNode, VfsNodeStats, VfsNodeType, VfsSnapshot, VfsSnapshotBaseNode, VfsSnapshotDirectoryNode, VfsSnapshotFileNode, VfsSnapshotNode, WriteFileOptions, } from "./types/vfs";
|
|
9
|
+
export { SshClient, VirtualFileSystem, VirtualShell, SshMimic as VirtualSshServer, VirtualUserManager, };
|
|
10
|
+
export { getArg, getFlag, ifFlag, } from "./commands/command-helpers";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,YAAY,EACX,cAAc,EACd,WAAW,EACX,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,aAAa,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,YAAY,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,QAAQ,IAAI,gBAAgB,EAC5B,kBAAkB,GAClB,CAAC;AAEF,OAAO,EACN,MAAM,EACN,OAAO,EACP,MAAM,GACN,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SshClient } from "./SSHClient";
|
|
2
|
+
import { SshMimic } from "./SSHMimic/index";
|
|
3
|
+
import VirtualFileSystem from "./VirtualFileSystem";
|
|
4
|
+
import { VirtualShell } from "./VirtualShell";
|
|
5
|
+
import { VirtualUserManager } from "./VirtualUserManager";
|
|
6
|
+
export { SshClient, VirtualFileSystem, VirtualShell, SshMimic as VirtualSshServer, VirtualUserManager, };
|
|
7
|
+
export { getArg, getFlag, ifFlag, } from "./commands/command-helpers";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ShellProperties } from "../VirtualShell";
|
|
2
|
+
export interface NeofetchInfo {
|
|
3
|
+
user: string;
|
|
4
|
+
host: string;
|
|
5
|
+
osName?: string;
|
|
6
|
+
kernel?: string;
|
|
7
|
+
uptimeSeconds?: number;
|
|
8
|
+
packages?: string;
|
|
9
|
+
shell?: string;
|
|
10
|
+
shellProps?: ShellProperties;
|
|
11
|
+
resolution?: string;
|
|
12
|
+
terminal?: string;
|
|
13
|
+
cpu?: string;
|
|
14
|
+
gpu?: string;
|
|
15
|
+
memoryUsedMiB?: number;
|
|
16
|
+
memoryTotalMiB?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildNeofetchOutput(info: NeofetchInfo): string;
|
|
19
|
+
//# sourceMappingURL=neofetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neofetch.d.ts","sourceRoot":"","sources":["../../src/modules/neofetch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAsGvD,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAoKD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CA0E9D"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
function formatUptime(seconds) {
|
|
5
|
+
const totalMinutes = Math.max(1, Math.floor(seconds / 60));
|
|
6
|
+
const days = Math.floor(totalMinutes / (24 * 60));
|
|
7
|
+
const hours = Math.floor((totalMinutes % (24 * 60)) / 60);
|
|
8
|
+
const minutes = totalMinutes % 60;
|
|
9
|
+
const parts = [];
|
|
10
|
+
if (days > 0) {
|
|
11
|
+
parts.push(`${days} day${days > 1 ? "s" : ""}`);
|
|
12
|
+
}
|
|
13
|
+
if (hours > 0) {
|
|
14
|
+
parts.push(`${hours} hour${hours > 1 ? "s" : ""}`);
|
|
15
|
+
}
|
|
16
|
+
if (minutes > 0 || parts.length === 0) {
|
|
17
|
+
parts.push(`${minutes} min${minutes > 1 ? "s" : ""}`);
|
|
18
|
+
}
|
|
19
|
+
return parts.join(", ");
|
|
20
|
+
}
|
|
21
|
+
function colorBlock(code) {
|
|
22
|
+
return `\u001b[${code}m \u001b[0m`;
|
|
23
|
+
}
|
|
24
|
+
function buildColorBars() {
|
|
25
|
+
const normal = [40, 41, 42, 43, 44, 45, 46, 47].map(colorBlock).join("");
|
|
26
|
+
const bright = [100, 101, 102, 103, 104, 105, 106, 107]
|
|
27
|
+
.map(colorBlock)
|
|
28
|
+
.join("");
|
|
29
|
+
return [normal, bright];
|
|
30
|
+
}
|
|
31
|
+
function colorizeLogoLine(line, index, total) {
|
|
32
|
+
if (line.trim().length === 0) {
|
|
33
|
+
return line;
|
|
34
|
+
}
|
|
35
|
+
const start = { r: 255, g: 255, b: 255 };
|
|
36
|
+
const end = { r: 168, g: 85, b: 247 };
|
|
37
|
+
const ratio = total <= 1 ? 0 : index / (total - 1);
|
|
38
|
+
const r = Math.round(start.r + (end.r - start.r) * ratio);
|
|
39
|
+
const g = Math.round(start.g + (end.g - start.g) * ratio);
|
|
40
|
+
const b = Math.round(start.b + (end.b - start.b) * ratio);
|
|
41
|
+
return `\u001b[38;2;${r};${g};${b}m${line}\u001b[0m`;
|
|
42
|
+
}
|
|
43
|
+
function colorizeDetailLine(line) {
|
|
44
|
+
if (line.trim().length === 0) {
|
|
45
|
+
return line;
|
|
46
|
+
}
|
|
47
|
+
const colonIndex = line.indexOf(":");
|
|
48
|
+
if (colonIndex === -1) {
|
|
49
|
+
// Pas de ':', chercher '@' pour identifier user@host
|
|
50
|
+
if (line.includes("@")) {
|
|
51
|
+
// C'est user@host, appliquer dégradé horizontal
|
|
52
|
+
return applyHorizontalGradient(line);
|
|
53
|
+
}
|
|
54
|
+
// Sinon c'est un separator ou autre, laisser tel quel
|
|
55
|
+
return line;
|
|
56
|
+
}
|
|
57
|
+
// Il y a un ':', c'est titre: valeur
|
|
58
|
+
const title = line.substring(0, colonIndex + 1);
|
|
59
|
+
const value = line.substring(colonIndex + 1);
|
|
60
|
+
// Appliquer le dégradé seulement au titre
|
|
61
|
+
const colorized = applyHorizontalGradient(title);
|
|
62
|
+
return colorized + value;
|
|
63
|
+
}
|
|
64
|
+
function applyHorizontalGradient(text) {
|
|
65
|
+
// Nettoyer les codes ANSI existants
|
|
66
|
+
const ansiRegex = new RegExp(`${String.fromCharCode(27)}\\[[\\d;]*m`, "g");
|
|
67
|
+
const cleaned = text.replace(ansiRegex, "");
|
|
68
|
+
if (cleaned.trim().length === 0) {
|
|
69
|
+
return text;
|
|
70
|
+
}
|
|
71
|
+
const start = { r: 255, g: 255, b: 255 };
|
|
72
|
+
const end = { r: 168, g: 85, b: 247 };
|
|
73
|
+
let result = "";
|
|
74
|
+
for (let i = 0; i < cleaned.length; i += 1) {
|
|
75
|
+
const ratio = cleaned.length <= 1 ? 0 : i / (cleaned.length - 1);
|
|
76
|
+
const r = Math.round(start.r + (end.r - start.r) * ratio);
|
|
77
|
+
const g = Math.round(start.g + (end.g - start.g) * ratio);
|
|
78
|
+
const b = Math.round(start.b + (end.b - start.b) * ratio);
|
|
79
|
+
result += `\u001b[38;2;${r};${g};${b}m${cleaned[i]}\u001b[0m`;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
function toMiB(bytes) {
|
|
84
|
+
return Math.max(0, Math.round(bytes / (1024 * 1024)));
|
|
85
|
+
}
|
|
86
|
+
function readOsPrettyName() {
|
|
87
|
+
try {
|
|
88
|
+
const data = readFileSync("/etc/os-release", "utf8");
|
|
89
|
+
for (const line of data.split("\n")) {
|
|
90
|
+
if (!line.startsWith("PRETTY_NAME=")) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const value = line.slice("PRETTY_NAME=".length).trim();
|
|
94
|
+
return value.replace(/^"|"$/g, "");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
function readFirstLine(filePath) {
|
|
103
|
+
try {
|
|
104
|
+
const data = readFileSync(filePath, "utf8").split("\n")[0]?.trim();
|
|
105
|
+
if (!data || data.length === 0) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
return data;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function resolveHostLabel(fallback) {
|
|
115
|
+
const vendor = readFirstLine("/sys/devices/virtual/dmi/id/sys_vendor");
|
|
116
|
+
const product = readFirstLine("/sys/devices/virtual/dmi/id/product_name");
|
|
117
|
+
if (vendor && product) {
|
|
118
|
+
return `${vendor} ${product}`;
|
|
119
|
+
}
|
|
120
|
+
if (product) {
|
|
121
|
+
return product;
|
|
122
|
+
}
|
|
123
|
+
return fallback;
|
|
124
|
+
}
|
|
125
|
+
function countDpkgPackages() {
|
|
126
|
+
const candidates = ["/var/lib/dpkg/status", "/usr/local/var/lib/dpkg/status"];
|
|
127
|
+
for (const filePath of candidates) {
|
|
128
|
+
if (!existsSync(filePath)) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const data = readFileSync(filePath, "utf8");
|
|
133
|
+
const matches = data.match(/^Package:\s+/gm);
|
|
134
|
+
return matches?.length ?? 0;
|
|
135
|
+
}
|
|
136
|
+
catch { }
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
function countSnapPackages() {
|
|
141
|
+
const candidates = ["/snap", "/var/lib/snapd/snaps"];
|
|
142
|
+
for (const dirPath of candidates) {
|
|
143
|
+
if (!existsSync(dirPath)) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
148
|
+
const count = entries.filter((entry) => entry.isDirectory()).length;
|
|
149
|
+
return count;
|
|
150
|
+
}
|
|
151
|
+
catch { }
|
|
152
|
+
}
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
function resolvePackagesLabel() {
|
|
156
|
+
const dpkgCount = countDpkgPackages();
|
|
157
|
+
const snapCount = countSnapPackages();
|
|
158
|
+
if (dpkgCount !== undefined && snapCount !== undefined) {
|
|
159
|
+
return `${dpkgCount} (dpkg), ${snapCount} (snap)`;
|
|
160
|
+
}
|
|
161
|
+
if (dpkgCount !== undefined) {
|
|
162
|
+
return `${dpkgCount} (dpkg)`;
|
|
163
|
+
}
|
|
164
|
+
if (snapCount !== undefined) {
|
|
165
|
+
return `${snapCount} (snap)`;
|
|
166
|
+
}
|
|
167
|
+
return "n/a";
|
|
168
|
+
}
|
|
169
|
+
function resolveCpuLabel() {
|
|
170
|
+
const cpus = os.cpus();
|
|
171
|
+
if (cpus.length === 0) {
|
|
172
|
+
return "unknown";
|
|
173
|
+
}
|
|
174
|
+
const first = cpus[0];
|
|
175
|
+
if (!first) {
|
|
176
|
+
return "unknown";
|
|
177
|
+
}
|
|
178
|
+
const ghz = (first.speed / 1000).toFixed(2);
|
|
179
|
+
return `${first.model} (${cpus.length}) @ ${ghz}GHz`;
|
|
180
|
+
}
|
|
181
|
+
function resolveShellLabel(shell) {
|
|
182
|
+
if (!shell || shell.trim().length === 0) {
|
|
183
|
+
return "unknown";
|
|
184
|
+
}
|
|
185
|
+
return path.posix.basename(shell.trim());
|
|
186
|
+
}
|
|
187
|
+
function resolveDefaults(info) {
|
|
188
|
+
const totalMem = os.totalmem();
|
|
189
|
+
const freeMem = os.freemem();
|
|
190
|
+
const usedMem = Math.max(0, totalMem - freeMem);
|
|
191
|
+
const shellProps = info.shellProps;
|
|
192
|
+
const processUptime = process.uptime();
|
|
193
|
+
if (info.uptimeSeconds === undefined) {
|
|
194
|
+
info.uptimeSeconds = Math.round(processUptime);
|
|
195
|
+
}
|
|
196
|
+
console.log("Resolving neofetch info with shellProps:", shellProps);
|
|
197
|
+
return {
|
|
198
|
+
user: info.user,
|
|
199
|
+
host: info.host,
|
|
200
|
+
osName: shellProps?.os ??
|
|
201
|
+
info.osName ??
|
|
202
|
+
`${readOsPrettyName() ?? os.type()} ${os.arch()}`,
|
|
203
|
+
kernel: shellProps?.kernel ?? info.kernel ?? os.release(),
|
|
204
|
+
uptimeSeconds: info.uptimeSeconds ?? os.uptime(),
|
|
205
|
+
packages: info.packages ?? resolvePackagesLabel(),
|
|
206
|
+
shell: resolveShellLabel(info.shell),
|
|
207
|
+
shellProps: info.shellProps ?? {
|
|
208
|
+
kernel: info.kernel ?? os.release(),
|
|
209
|
+
os: info.osName ?? `${readOsPrettyName() ?? os.type()} ${os.arch()}`,
|
|
210
|
+
arch: os.arch(),
|
|
211
|
+
},
|
|
212
|
+
resolution: info.resolution ?? "n/a (ssh)",
|
|
213
|
+
terminal: info.terminal ?? "unknown",
|
|
214
|
+
cpu: info.cpu ?? resolveCpuLabel(),
|
|
215
|
+
gpu: info.gpu ?? "n/a",
|
|
216
|
+
memoryUsedMiB: info.memoryUsedMiB ?? toMiB(usedMem),
|
|
217
|
+
memoryTotalMiB: info.memoryTotalMiB ?? toMiB(totalMem),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
export function buildNeofetchOutput(info) {
|
|
221
|
+
const fields = resolveDefaults(info);
|
|
222
|
+
const uptime = formatUptime(fields.uptimeSeconds);
|
|
223
|
+
const colorBars = buildColorBars();
|
|
224
|
+
const distroLogo = [
|
|
225
|
+
" .. .:. ",
|
|
226
|
+
" .::.. .. .. ",
|
|
227
|
+
". .... ... .. ",
|
|
228
|
+
": .... .:. .. ",
|
|
229
|
+
": .:.:........:. .. ",
|
|
230
|
+
": .. ",
|
|
231
|
+
". : ",
|
|
232
|
+
". : ",
|
|
233
|
+
".. : ",
|
|
234
|
+
" :. .. ",
|
|
235
|
+
" .. .. ",
|
|
236
|
+
" :-. :: ",
|
|
237
|
+
" .:. :. ",
|
|
238
|
+
" ..: ... ",
|
|
239
|
+
" ..: :.. ",
|
|
240
|
+
" :... :....",
|
|
241
|
+
" .. ....",
|
|
242
|
+
" . .. ",
|
|
243
|
+
" .:. .: ",
|
|
244
|
+
" :. .. ",
|
|
245
|
+
" ::. .. ",
|
|
246
|
+
"..... ..:... ",
|
|
247
|
+
"...:. .. ",
|
|
248
|
+
".:...:. ::. .. ",
|
|
249
|
+
" ... ..:::::.. ..:::::::.. ",
|
|
250
|
+
];
|
|
251
|
+
const details = [
|
|
252
|
+
`${fields.user}@${fields.host}`,
|
|
253
|
+
"-------------------------",
|
|
254
|
+
`OS: ${fields.osName}`,
|
|
255
|
+
`Host: ${resolveHostLabel(fields.host)}`,
|
|
256
|
+
`Kernel: ${fields.kernel}`,
|
|
257
|
+
`Uptime: ${uptime}`,
|
|
258
|
+
// `Packages: ${fields.packages}`,
|
|
259
|
+
`Shell: ${fields.shell}`,
|
|
260
|
+
// `Shell Props: ${fields.shellProps}`,
|
|
261
|
+
`Resolution: ${fields.resolution}`,
|
|
262
|
+
`Terminal: ${fields.terminal}`,
|
|
263
|
+
`CPU: ${fields.cpu}`,
|
|
264
|
+
`GPU: ${fields.gpu}`,
|
|
265
|
+
`Memory: ${fields.memoryUsedMiB}MiB / ${fields.memoryTotalMiB}MiB`,
|
|
266
|
+
"",
|
|
267
|
+
colorBars[0],
|
|
268
|
+
colorBars[1],
|
|
269
|
+
];
|
|
270
|
+
const width = Math.max(distroLogo.length, details.length);
|
|
271
|
+
const lines = [];
|
|
272
|
+
for (let i = 0; i < width; i += 1) {
|
|
273
|
+
const rawLeft = distroLogo[i] ?? "";
|
|
274
|
+
const right = details[i] ?? "";
|
|
275
|
+
if (right.length > 0) {
|
|
276
|
+
const left = colorizeLogoLine(rawLeft.padEnd(31, " "), i, distroLogo.length);
|
|
277
|
+
const coloredRight = colorizeDetailLine(right);
|
|
278
|
+
lines.push(`${left} ${coloredRight}`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
lines.push(colorizeLogoLine(rawLeft, i, distroLogo.length));
|
|
282
|
+
}
|
|
283
|
+
return lines.join("\n");
|
|
284
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ChildProcessWithoutNullStreams } from "node:child_process";
|
|
2
|
+
import type { ShellStream } from "../types/streams";
|
|
3
|
+
import { type TerminalSize } from "./shellRuntime";
|
|
4
|
+
export declare function spawnNanoEditorProcess(tempPath: string, terminalSize: TerminalSize, stream: ShellStream): ChildProcessWithoutNullStreams;
|
|
5
|
+
export declare function spawnHtopProcess(pidList: string, terminalSize: TerminalSize, stream: ShellStream): ChildProcessWithoutNullStreams;
|
|
6
|
+
//# sourceMappingURL=shellInteractive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shellInteractive.d.ts","sourceRoot":"","sources":["../../src/modules/shellInteractive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,8BAA8B,EAAS,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAEN,KAAK,YAAY,EAEjB,MAAM,gBAAgB,CAAC;AA4BxB,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,WAAW,GACjB,8BAA8B,CAMhC;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,WAAW,GACjB,8BAA8B,CAMhC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { shellQuote, withTerminalSize, } from "./shellRuntime";
|
|
3
|
+
function spawnScriptProcess(command, terminalSize, stream) {
|
|
4
|
+
const formatted = withTerminalSize(command, terminalSize);
|
|
5
|
+
const proc = spawn("script", ["-qfec", formatted, "/dev/null"], {
|
|
6
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
7
|
+
env: {
|
|
8
|
+
...process.env,
|
|
9
|
+
// biome-ignore lint/style/useNamingConvention: env variable should be uppercase
|
|
10
|
+
TERM: process.env.TERM ?? "xterm-256color",
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
proc.stdout.on("data", (data) => {
|
|
14
|
+
stream.write(data.toString("utf8"));
|
|
15
|
+
});
|
|
16
|
+
proc.stderr.on("data", (data) => {
|
|
17
|
+
stream.write(data.toString("utf8"));
|
|
18
|
+
});
|
|
19
|
+
return proc;
|
|
20
|
+
}
|
|
21
|
+
export function spawnNanoEditorProcess(tempPath, terminalSize, stream) {
|
|
22
|
+
return spawnScriptProcess(`nano -- ${shellQuote(tempPath)}`, terminalSize, stream);
|
|
23
|
+
}
|
|
24
|
+
export function spawnHtopProcess(pidList, terminalSize, stream) {
|
|
25
|
+
return spawnScriptProcess(`htop -p ${shellQuote(pidList)}`, terminalSize, stream);
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TerminalSize {
|
|
2
|
+
cols: number;
|
|
3
|
+
rows: number;
|
|
4
|
+
}
|
|
5
|
+
export declare function shellQuote(value: string): string;
|
|
6
|
+
export declare function toTtyLines(text: string): string;
|
|
7
|
+
export declare function withTerminalSize(command: string, terminalSize: TerminalSize): string;
|
|
8
|
+
export declare function resolvePath(base: string, inputPath: string): string;
|
|
9
|
+
export declare function collectChildPids(parentPid: number): Promise<number[]>;
|
|
10
|
+
export declare function getVisibleHtopPidList(rootPid?: number): Promise<string | null>;
|
|
11
|
+
//# sourceMappingURL=shellRuntime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shellRuntime.d.ts","sourceRoot":"","sources":["../../src/modules/shellRuntime.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,GACxB,MAAM,CAUR;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAOnE;AAED,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoB3E;AAED,wBAAsB,qBAAqB,CAC1C,OAAO,SAAc,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQxB"}
|