typescript-virtual-container 1.2.4 → 1.2.5
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/README.md +868 -1245
- package/benchmark-results.txt +21 -21
- package/dist/SSHMimic/index.d.ts +19 -2
- package/dist/SSHMimic/index.d.ts.map +1 -1
- package/dist/SSHMimic/index.js +116 -20
- package/dist/VirtualFileSystem/index.d.ts +115 -88
- package/dist/VirtualFileSystem/index.d.ts.map +1 -1
- package/dist/VirtualFileSystem/index.js +406 -258
- package/dist/VirtualShell/index.d.ts +3 -4
- package/dist/VirtualShell/index.d.ts.map +1 -1
- package/dist/VirtualShell/index.js +4 -6
- package/dist/VirtualUserManager/index.d.ts +25 -0
- package/dist/VirtualUserManager/index.d.ts.map +1 -1
- package/dist/VirtualUserManager/index.js +33 -0
- package/dist/commands/chmod.d.ts +3 -0
- package/dist/commands/chmod.d.ts.map +1 -0
- package/dist/commands/chmod.js +31 -0
- package/dist/commands/cp.d.ts +3 -0
- package/dist/commands/cp.d.ts.map +1 -0
- package/dist/commands/cp.js +68 -0
- package/dist/commands/find.d.ts +3 -0
- package/dist/commands/find.d.ts.map +1 -0
- package/dist/commands/find.js +48 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +61 -35
- package/dist/commands/head.d.ts +3 -0
- package/dist/commands/head.d.ts.map +1 -0
- package/dist/commands/head.js +30 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +25 -35
- package/dist/commands/ln.d.ts +3 -0
- package/dist/commands/ln.d.ts.map +1 -0
- package/dist/commands/ln.js +42 -0
- package/dist/commands/mv.d.ts +3 -0
- package/dist/commands/mv.d.ts.map +1 -0
- package/dist/commands/mv.js +35 -0
- package/dist/commands/tail.d.ts +3 -0
- package/dist/commands/tail.d.ts.map +1 -0
- package/dist/commands/tail.js +33 -0
- package/dist/commands/wc.d.ts +3 -0
- package/dist/commands/wc.d.ts.map +1 -0
- package/dist/commands/wc.js +48 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/package.json +5 -2
- package/scripts/publish-package.sh +70 -0
- package/src/SSHMimic/index.ts +143 -28
- package/src/VirtualFileSystem/index.ts +500 -280
- package/src/VirtualShell/index.ts +4 -6
- package/src/VirtualUserManager/index.ts +41 -0
- package/src/commands/chmod.ts +33 -0
- package/src/commands/cp.ts +76 -0
- package/src/commands/find.ts +61 -0
- package/src/commands/grep.ts +54 -38
- package/src/commands/head.ts +35 -0
- package/src/commands/index.ts +25 -43
- package/src/commands/ln.ts +47 -0
- package/src/commands/mv.ts +43 -0
- package/src/commands/tail.ts +37 -0
- package/src/commands/wc.ts +48 -0
- package/src/index.ts +1 -0
- package/standalone.js +62 -52
- package/standalone.js.map +4 -4
- package/tests/bun-test-shim.ts +1 -0
- package/tests/sftp.test.ts +115 -191
- package/tests/users.test.ts +66 -83
package/dist/commands/index.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { adduserCommand } from "./adduser";
|
|
2
2
|
import { catCommand } from "./cat";
|
|
3
3
|
import { cdCommand } from "./cd";
|
|
4
|
+
import { chmodCommand } from "./chmod";
|
|
4
5
|
import { clearCommand } from "./clear";
|
|
6
|
+
import { cpCommand } from "./cp";
|
|
5
7
|
import { curlCommand } from "./curl";
|
|
6
8
|
import { deluserCommand } from "./deluser";
|
|
7
9
|
import { echoCommand } from "./echo";
|
|
8
10
|
import { envCommand } from "./env";
|
|
9
11
|
import { exitCommand } from "./exit";
|
|
10
12
|
import { exportCommand } from "./export";
|
|
13
|
+
import { findCommand } from "./find";
|
|
11
14
|
import { grepCommand } from "./grep";
|
|
15
|
+
import { headCommand } from "./head";
|
|
12
16
|
import { createHelpCommand } from "./help";
|
|
13
17
|
import { hostnameCommand } from "./hostname";
|
|
14
18
|
import { htopCommand } from "./htop";
|
|
19
|
+
import { lnCommand } from "./ln";
|
|
15
20
|
import { lsCommand } from "./ls";
|
|
16
21
|
import { mkdirCommand } from "./mkdir";
|
|
22
|
+
import { mvCommand } from "./mv";
|
|
17
23
|
import { nanoCommand } from "./nano";
|
|
18
24
|
import { neofetchCommand } from "./neofetch";
|
|
19
25
|
import { passwdCommand } from "./passwd";
|
|
@@ -23,9 +29,11 @@ import { setCommand } from "./set";
|
|
|
23
29
|
import { shCommand } from "./sh";
|
|
24
30
|
import { suCommand } from "./su";
|
|
25
31
|
import { sudoCommand } from "./sudo";
|
|
32
|
+
import { tailCommand } from "./tail";
|
|
26
33
|
import { touchCommand } from "./touch";
|
|
27
34
|
import { treeCommand } from "./tree";
|
|
28
35
|
import { unsetCommand } from "./unset";
|
|
36
|
+
import { wcCommand } from "./wc";
|
|
29
37
|
import { wgetCommand } from "./wget";
|
|
30
38
|
import { whoCommand } from "./who";
|
|
31
39
|
import { whoamiCommand } from "./whoami";
|
|
@@ -60,12 +68,21 @@ const BASE_COMMANDS = [
|
|
|
60
68
|
shCommand,
|
|
61
69
|
clearCommand,
|
|
62
70
|
exitCommand,
|
|
71
|
+
cpCommand,
|
|
72
|
+
mvCommand,
|
|
73
|
+
lnCommand,
|
|
74
|
+
findCommand,
|
|
75
|
+
wcCommand,
|
|
76
|
+
headCommand,
|
|
77
|
+
tailCommand,
|
|
78
|
+
chmodCommand,
|
|
63
79
|
];
|
|
64
80
|
const customCommands = [];
|
|
65
81
|
const helpCommand = createHelpCommand(() => getCommandModules().map((cmd) => cmd.name));
|
|
66
82
|
const commandRegistry = new Map();
|
|
67
83
|
let cachedCommandNames = null;
|
|
68
84
|
function buildCache() {
|
|
85
|
+
commandRegistry.clear();
|
|
69
86
|
for (const mod of getCommandModules()) {
|
|
70
87
|
commandRegistry.set(mod.name, mod);
|
|
71
88
|
for (const alias of mod.aliases ?? []) {
|
|
@@ -75,13 +92,6 @@ function buildCache() {
|
|
|
75
92
|
cachedCommandNames = Array.from(commandRegistry.keys()).sort();
|
|
76
93
|
}
|
|
77
94
|
function getCommandModules() {
|
|
78
|
-
// console.log("Loading command modules...");
|
|
79
|
-
// console.log(
|
|
80
|
-
// `Base commands: ${BASE_COMMANDS.map((cmd) => cmd.name).join(", ")}`,
|
|
81
|
-
// );
|
|
82
|
-
// console.log(
|
|
83
|
-
// `Custom commands: ${customCommands.map((cmd) => cmd.name).join(", ")}`,
|
|
84
|
-
// );
|
|
85
95
|
return [...BASE_COMMANDS, ...customCommands, helpCommand];
|
|
86
96
|
}
|
|
87
97
|
export function registerCommand(module) {
|
|
@@ -94,31 +104,20 @@ export function registerCommand(module) {
|
|
|
94
104
|
if (names.some((name) => name.length === 0 || /\s/.test(name))) {
|
|
95
105
|
throw new Error("Command names and aliases must be non-empty and contain no spaces");
|
|
96
106
|
}
|
|
97
|
-
|
|
98
|
-
if (commandRegistry.has(name)) {
|
|
99
|
-
throw new Error(`Command '${name}' already exists`);
|
|
100
|
-
}
|
|
101
|
-
commandRegistry.set(name, normalized);
|
|
102
|
-
}
|
|
107
|
+
customCommands.push(normalized);
|
|
103
108
|
buildCache();
|
|
104
109
|
}
|
|
105
110
|
export function createCustomCommand(name, params, run) {
|
|
106
|
-
return {
|
|
107
|
-
name,
|
|
108
|
-
params,
|
|
109
|
-
run,
|
|
110
|
-
};
|
|
111
|
+
return { name, params, run };
|
|
111
112
|
}
|
|
112
113
|
export function getCommandNames() {
|
|
113
|
-
if (!cachedCommandNames)
|
|
114
|
+
if (!cachedCommandNames)
|
|
114
115
|
buildCache();
|
|
115
|
-
}
|
|
116
116
|
return cachedCommandNames;
|
|
117
117
|
}
|
|
118
118
|
export function resolveModule(name) {
|
|
119
|
-
if (!cachedCommandNames)
|
|
119
|
+
if (!cachedCommandNames)
|
|
120
120
|
buildCache();
|
|
121
|
-
}
|
|
122
121
|
return commandRegistry.get(name.toLowerCase());
|
|
123
122
|
}
|
|
124
123
|
function splitArgsRespectingQuotes(input) {
|
|
@@ -150,9 +149,8 @@ function splitArgsRespectingQuotes(input) {
|
|
|
150
149
|
}
|
|
151
150
|
current += ch;
|
|
152
151
|
}
|
|
153
|
-
if (current.length > 0)
|
|
152
|
+
if (current.length > 0)
|
|
154
153
|
tokens.push(current);
|
|
155
|
-
}
|
|
156
154
|
return tokens;
|
|
157
155
|
}
|
|
158
156
|
function parseInput(rawInput) {
|
|
@@ -162,21 +160,16 @@ function parseInput(rawInput) {
|
|
|
162
160
|
args: parts.slice(1),
|
|
163
161
|
};
|
|
164
162
|
}
|
|
165
|
-
// Internal async function for pipeline execution
|
|
166
163
|
export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell, stdin) {
|
|
167
164
|
const trimmed = rawInput.trim();
|
|
168
|
-
if (trimmed.length === 0)
|
|
165
|
+
if (trimmed.length === 0)
|
|
169
166
|
return { exitCode: 0 };
|
|
170
|
-
}
|
|
171
167
|
if (trimmed.includes("|") || trimmed.includes(">") || trimmed.includes("<")) {
|
|
172
168
|
const { parseShellPipeline } = await import("../VirtualShell/shellParser");
|
|
173
169
|
const { executePipeline } = await import("../SSHMimic/executor");
|
|
174
170
|
const pipeline = parseShellPipeline(trimmed);
|
|
175
171
|
if (!pipeline.isValid) {
|
|
176
|
-
return {
|
|
177
|
-
stderr: pipeline.error || "Syntax error",
|
|
178
|
-
exitCode: 1,
|
|
179
|
-
};
|
|
172
|
+
return { stderr: pipeline.error || "Syntax error", exitCode: 1 };
|
|
180
173
|
}
|
|
181
174
|
try {
|
|
182
175
|
return await executePipeline(pipeline, authUser, hostname, mode, cwd, shell);
|
|
@@ -189,10 +182,7 @@ export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell,
|
|
|
189
182
|
const { commandName, args } = parseInput(trimmed);
|
|
190
183
|
const mod = resolveModule(commandName);
|
|
191
184
|
if (!mod) {
|
|
192
|
-
return {
|
|
193
|
-
stderr: `Command '${trimmed}' not found`,
|
|
194
|
-
exitCode: 127,
|
|
195
|
-
};
|
|
185
|
+
return { stderr: `Command '${trimmed}' not found`, exitCode: 127 };
|
|
196
186
|
}
|
|
197
187
|
try {
|
|
198
188
|
return await mod.run({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ln.d.ts","sourceRoot":"","sources":["../../src/commands/ln.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,SAAS,EAAE,WA0CvB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ifFlag } from "./command-helpers";
|
|
2
|
+
import { assertPathAccess, resolvePath } from "./helpers";
|
|
3
|
+
export const lnCommand = {
|
|
4
|
+
name: "ln",
|
|
5
|
+
params: ["[-s] <target> <link_name>"],
|
|
6
|
+
run: ({ authUser, shell, cwd, args }) => {
|
|
7
|
+
const symbolic = ifFlag(args, ["-s", "--symbolic"]);
|
|
8
|
+
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
9
|
+
const [targetArg, linkArg] = positionals;
|
|
10
|
+
if (!targetArg || !linkArg) {
|
|
11
|
+
return { stderr: "ln: missing operand", exitCode: 1 };
|
|
12
|
+
}
|
|
13
|
+
const linkPath = resolvePath(cwd, linkArg);
|
|
14
|
+
const targetPath = symbolic
|
|
15
|
+
? targetArg // keep relative for symlinks
|
|
16
|
+
: resolvePath(cwd, targetArg);
|
|
17
|
+
try {
|
|
18
|
+
assertPathAccess(authUser, linkPath, "ln");
|
|
19
|
+
if (!symbolic) {
|
|
20
|
+
// Hard link — copy file contents
|
|
21
|
+
const srcPath = resolvePath(cwd, targetArg);
|
|
22
|
+
assertPathAccess(authUser, srcPath, "ln");
|
|
23
|
+
if (!shell.vfs.exists(srcPath)) {
|
|
24
|
+
return {
|
|
25
|
+
stderr: `ln: ${targetArg}: No such file or directory`,
|
|
26
|
+
exitCode: 1,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const content = shell.vfs.readFile(srcPath);
|
|
30
|
+
shell.writeFileAsUser(authUser, linkPath, content);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
shell.vfs.symlink(targetPath, linkPath);
|
|
34
|
+
}
|
|
35
|
+
return { exitCode: 0 };
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
39
|
+
return { stderr: `ln: ${msg}`, exitCode: 1 };
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mv.d.ts","sourceRoot":"","sources":["../../src/commands/mv.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,SAAS,EAAE,WAuCvB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { assertPathAccess, resolvePath } from "./helpers";
|
|
2
|
+
export const mvCommand = {
|
|
3
|
+
name: "mv",
|
|
4
|
+
params: ["<source> <dest>"],
|
|
5
|
+
run: ({ authUser, shell, cwd, args }) => {
|
|
6
|
+
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
7
|
+
const [srcArg, destArg] = positionals;
|
|
8
|
+
if (!srcArg || !destArg) {
|
|
9
|
+
return { stderr: "mv: missing operand", exitCode: 1 };
|
|
10
|
+
}
|
|
11
|
+
const srcPath = resolvePath(cwd, srcArg);
|
|
12
|
+
const destPath = resolvePath(cwd, destArg);
|
|
13
|
+
try {
|
|
14
|
+
assertPathAccess(authUser, srcPath, "mv");
|
|
15
|
+
assertPathAccess(authUser, destPath, "mv");
|
|
16
|
+
if (!shell.vfs.exists(srcPath)) {
|
|
17
|
+
return {
|
|
18
|
+
stderr: `mv: ${srcArg}: No such file or directory`,
|
|
19
|
+
exitCode: 1,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// If dest is a directory, move into it
|
|
23
|
+
const finalDest = shell.vfs.exists(destPath) &&
|
|
24
|
+
shell.vfs.stat(destPath).type === "directory"
|
|
25
|
+
? `${destPath}/${srcArg.split("/").pop()}`
|
|
26
|
+
: destPath;
|
|
27
|
+
shell.vfs.move(srcPath, finalDest);
|
|
28
|
+
return { exitCode: 0 };
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
32
|
+
return { stderr: `mv: ${msg}`, exitCode: 1 };
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tail.d.ts","sourceRoot":"","sources":["../../src/commands/tail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAgCzB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getFlag } from "./command-helpers";
|
|
2
|
+
import { assertPathAccess, resolvePath } from "./helpers";
|
|
3
|
+
export const tailCommand = {
|
|
4
|
+
name: "tail",
|
|
5
|
+
params: ["[-n <lines>] [file...]"],
|
|
6
|
+
run: ({ authUser, shell, cwd, args, stdin }) => {
|
|
7
|
+
const nArg = getFlag(args, ["-n"]);
|
|
8
|
+
const n = typeof nArg === "string" ? parseInt(nArg, 10) : 10;
|
|
9
|
+
const positionals = args.filter((a) => !a.startsWith("-") && a !== nArg);
|
|
10
|
+
const take = (content) => {
|
|
11
|
+
const lines = content.split("\n");
|
|
12
|
+
return lines.slice(Math.max(0, lines.length - n)).join("\n");
|
|
13
|
+
};
|
|
14
|
+
if (positionals.length === 0) {
|
|
15
|
+
return { stdout: take(stdin ?? ""), exitCode: 0 };
|
|
16
|
+
}
|
|
17
|
+
const results = [];
|
|
18
|
+
for (const file of positionals) {
|
|
19
|
+
const filePath = resolvePath(cwd, file);
|
|
20
|
+
try {
|
|
21
|
+
assertPathAccess(authUser, filePath, "tail");
|
|
22
|
+
results.push(take(shell.vfs.readFile(filePath)));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return {
|
|
26
|
+
stderr: `tail: ${file}: No such file or directory`,
|
|
27
|
+
exitCode: 1,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { stdout: results.join("\n"), exitCode: 0 };
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wc.d.ts","sourceRoot":"","sources":["../../src/commands/wc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,SAAS,EAAE,WA2CvB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ifFlag } from "./command-helpers";
|
|
2
|
+
import { assertPathAccess, resolvePath } from "./helpers";
|
|
3
|
+
export const wcCommand = {
|
|
4
|
+
name: "wc",
|
|
5
|
+
params: ["[-l] [-w] [-c] [file...]"],
|
|
6
|
+
run: ({ authUser, shell, cwd, args, stdin }) => {
|
|
7
|
+
const lines = ifFlag(args, ["-l"]);
|
|
8
|
+
const words = ifFlag(args, ["-w"]);
|
|
9
|
+
const bytes = ifFlag(args, ["-c"]);
|
|
10
|
+
const showAll = !lines && !words && !bytes;
|
|
11
|
+
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
12
|
+
const count = (content, label) => {
|
|
13
|
+
const l = content.split("\n").length - (content.endsWith("\n") ? 1 : 0);
|
|
14
|
+
const w = content.trim().split(/\s+/).filter(Boolean).length;
|
|
15
|
+
const c = Buffer.byteLength(content, "utf8");
|
|
16
|
+
const parts = [];
|
|
17
|
+
if (showAll || lines)
|
|
18
|
+
parts.push(String(l).padStart(7));
|
|
19
|
+
if (showAll || words)
|
|
20
|
+
parts.push(String(w).padStart(7));
|
|
21
|
+
if (showAll || bytes)
|
|
22
|
+
parts.push(String(c).padStart(7));
|
|
23
|
+
if (label)
|
|
24
|
+
parts.push(` ${label}`);
|
|
25
|
+
return parts.join("");
|
|
26
|
+
};
|
|
27
|
+
if (positionals.length === 0) {
|
|
28
|
+
const content = stdin ?? "";
|
|
29
|
+
return { stdout: count(content, ""), exitCode: 0 };
|
|
30
|
+
}
|
|
31
|
+
const results = [];
|
|
32
|
+
for (const file of positionals) {
|
|
33
|
+
const filePath = resolvePath(cwd, file);
|
|
34
|
+
try {
|
|
35
|
+
assertPathAccess(authUser, filePath, "wc");
|
|
36
|
+
const content = shell.vfs.readFile(filePath);
|
|
37
|
+
results.push(count(content, file));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return {
|
|
41
|
+
stderr: `wc: ${file}: No such file or directory`,
|
|
42
|
+
exitCode: 1,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { stdout: results.join("\n"), exitCode: 0 };
|
|
47
|
+
},
|
|
48
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export type { AuditLogEntry, HoneyPotStats, } from "./Honeypot/index";
|
|
|
8
8
|
export type { CommandContext, CommandMode, CommandOutcome, CommandResult, NanoEditorSession, ShellModule, SudoChallenge, } from "./types/commands";
|
|
9
9
|
export type { ExecStream, ShellStream } from "./types/streams";
|
|
10
10
|
export type { RemoveOptions, VfsBaseNode, VfsDirectoryNode, VfsFileNode, VfsNodeStats, VfsNodeType, VfsSnapshot, VfsSnapshotBaseNode, VfsSnapshotDirectoryNode, VfsSnapshotFileNode, VfsSnapshotNode, WriteFileOptions, } from "./types/vfs";
|
|
11
|
+
export type { VfsOptions, VfsPersistenceMode } from "./VirtualFileSystem/index";
|
|
11
12
|
export { HoneyPot, SshClient, VirtualFileSystem, SftpMimic as VirtualSftpServer, VirtualShell, SshMimic as VirtualSshServer, VirtualUserManager, };
|
|
12
13
|
export { getArg, getFlag, ifFlag, } from "./commands/command-helpers";
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,iBAAiB,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,YAAY,EACX,aAAa,EACb,aAAa,GACb,MAAM,kBAAkB,CAAC;AAC1B,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;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,iBAAiB,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,YAAY,EACX,aAAa,EACb,aAAa,GACb,MAAM,kBAAkB,CAAC;AAC1B,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;AACrB,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEhF,OAAO,EACN,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,SAAS,IAAI,iBAAiB,EAC9B,YAAY,EACZ,QAAQ,IAAI,gBAAgB,EAC5B,kBAAkB,GAClB,CAAC;AAEF,OAAO,EACN,MAAM,EACN,OAAO,EACP,MAAM,GACN,MAAM,4BAA4B,CAAC"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"version": "1.2.
|
|
7
|
+
"version": "1.2.5",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -27,13 +27,16 @@
|
|
|
27
27
|
"test": "bunx --bun @biomejs/biome test ./src",
|
|
28
28
|
"build": "tsc --project tsconfig.json",
|
|
29
29
|
"deploy:npm": "npm publish --access public",
|
|
30
|
+
"bench": "rm -rf .benchmark-shells/ && bun benchmark-virtualshell.ts",
|
|
31
|
+
"publish-package": "bash ./scripts/publish-package.sh",
|
|
30
32
|
"standalone-build": "bunx esbuild src/standalone.ts --bundle --platform=node --target=node18 --outfile=standalone.js --tree-shaking=true --minify --sourcemap"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
35
|
"@biomejs/biome": "^2.4.11",
|
|
34
36
|
"@types/bun": "latest",
|
|
35
37
|
"@types/node": "^25.6.0",
|
|
36
|
-
"@types/ssh2": "^1.15.5"
|
|
38
|
+
"@types/ssh2": "^1.15.5",
|
|
39
|
+
"typescript": "^6.0.3"
|
|
37
40
|
},
|
|
38
41
|
"peerDependencies": {
|
|
39
42
|
"typescript": "^5"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
5
|
+
echo "Error: this command must run inside a git repository."
|
|
6
|
+
exit 1
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
if [[ -n "$(git status --porcelain)" ]]; then
|
|
10
|
+
echo "Error: working tree is not clean. Commit or stash your changes first."
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
VERSION="$(node -p "require('./package.json').version")"
|
|
15
|
+
TAG="v${VERSION}"
|
|
16
|
+
|
|
17
|
+
if [[ -z "${VERSION}" ]]; then
|
|
18
|
+
echo "Error: package.json version is empty."
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
git fetch --tags --quiet
|
|
23
|
+
|
|
24
|
+
TAG_EXISTS_LOCAL="false"
|
|
25
|
+
TAG_EXISTS_REMOTE="false"
|
|
26
|
+
|
|
27
|
+
if git rev-parse --verify --quiet "refs/tags/${TAG}" >/dev/null; then
|
|
28
|
+
TAG_EXISTS_LOCAL="true"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then
|
|
32
|
+
TAG_EXISTS_REMOTE="true"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if [[ "${TAG_EXISTS_LOCAL}" == "true" || "${TAG_EXISTS_REMOTE}" == "true" ]]; then
|
|
36
|
+
echo "Tag ${TAG} already exists. Deleting existing release/tag before republishing."
|
|
37
|
+
|
|
38
|
+
if command -v gh >/dev/null 2>&1; then
|
|
39
|
+
if gh release view "${TAG}" >/dev/null 2>&1; then
|
|
40
|
+
gh release delete "${TAG}" --yes
|
|
41
|
+
echo "GitHub release ${TAG} deleted."
|
|
42
|
+
fi
|
|
43
|
+
else
|
|
44
|
+
echo "gh CLI not found; skipping GitHub release deletion."
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [[ "${TAG_EXISTS_LOCAL}" == "true" ]]; then
|
|
48
|
+
git tag -d "${TAG}" >/dev/null
|
|
49
|
+
echo "Local tag ${TAG} deleted."
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
if [[ "${TAG_EXISTS_REMOTE}" == "true" ]]; then
|
|
53
|
+
git push --delete origin "${TAG}" >/dev/null
|
|
54
|
+
echo "Remote tag ${TAG} deleted."
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
git tag -a "${TAG}" -m "Release ${TAG}"
|
|
59
|
+
git push origin "${TAG}"
|
|
60
|
+
|
|
61
|
+
echo "Tag ${TAG} pushed."
|
|
62
|
+
|
|
63
|
+
if command -v gh >/dev/null 2>&1; then
|
|
64
|
+
gh release create "${TAG}" --title "${TAG}" --generate-notes
|
|
65
|
+
echo "GitHub release ${TAG} created."
|
|
66
|
+
else
|
|
67
|
+
echo "gh CLI not found; skipping GitHub release creation."
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
echo "Done. The publish workflow should run from tag ${TAG}."
|