typescript-virtual-container 1.0.3 → 1.0.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.
Files changed (56) hide show
  1. package/.github/workflows/test-battery.yml +22 -0
  2. package/CHANGELOG.md +42 -0
  3. package/README.md +56 -28
  4. package/package.json +1 -1
  5. package/src/SSHMimic/client.ts +1 -1
  6. package/src/SSHMimic/exec.ts +1 -1
  7. package/src/SSHMimic/executor.ts +201 -0
  8. package/src/SSHMimic/index.ts +14 -18
  9. package/src/{VirtualFileSystem.ts → VirtualFileSystem/index.ts} +6 -15
  10. package/src/{SSHMimic → VirtualShell}/commands/cat.ts +2 -1
  11. package/src/VirtualShell/commands/command-helpers.ts +135 -0
  12. package/src/{SSHMimic → VirtualShell}/commands/curl.ts +16 -37
  13. package/src/VirtualShell/commands/echo.ts +34 -0
  14. package/src/VirtualShell/commands/env.ts +22 -0
  15. package/src/VirtualShell/commands/export.ts +38 -0
  16. package/src/VirtualShell/commands/grep.ts +88 -0
  17. package/src/{SSHMimic → VirtualShell}/commands/helpers.ts +0 -37
  18. package/src/VirtualShell/commands/index.ts +327 -0
  19. package/src/{SSHMimic → VirtualShell}/commands/ls.ts +3 -2
  20. package/src/{SSHMimic → VirtualShell}/commands/mkdir.ts +6 -1
  21. package/src/{SSHMimic → VirtualShell}/commands/rm.ts +10 -3
  22. package/src/VirtualShell/commands/set.ts +73 -0
  23. package/src/VirtualShell/commands/sh.ts +58 -0
  24. package/src/{SSHMimic → VirtualShell}/commands/su.ts +3 -3
  25. package/src/{SSHMimic → VirtualShell}/commands/sudo.ts +16 -26
  26. package/src/{SSHMimic → VirtualShell}/commands/tree.ts +2 -1
  27. package/src/VirtualShell/commands/unset.ts +19 -0
  28. package/src/{SSHMimic → VirtualShell}/commands/wget.ts +23 -6
  29. package/src/{SSHMimic → VirtualShell}/commands/who.ts +1 -1
  30. package/src/VirtualShell/index.ts +69 -0
  31. package/src/{SSHMimic → VirtualShell}/shell.ts +3 -3
  32. package/src/VirtualShell/shellParser.ts +203 -0
  33. package/src/index.ts +8 -0
  34. package/src/standalone.ts +10 -1
  35. package/src/types/commands.ts +2 -0
  36. package/src/types/pipeline.ts +23 -0
  37. package/tests/command-helpers.test.ts +40 -0
  38. package/tests/helpers.test.ts +1 -1
  39. package/src/SSHMimic/commands/index.ts +0 -120
  40. /package/src/{vfs → VirtualFileSystem}/archive.ts +0 -0
  41. /package/src/{vfs → VirtualFileSystem}/internalTypes.ts +0 -0
  42. /package/src/{vfs → VirtualFileSystem}/path.ts +0 -0
  43. /package/src/{vfs → VirtualFileSystem}/snapshot.ts +0 -0
  44. /package/src/{vfs → VirtualFileSystem}/tree.ts +0 -0
  45. /package/src/{SSHMimic → VirtualShell}/commands/adduser.ts +0 -0
  46. /package/src/{SSHMimic → VirtualShell}/commands/cd.ts +0 -0
  47. /package/src/{SSHMimic → VirtualShell}/commands/clear.ts +0 -0
  48. /package/src/{SSHMimic → VirtualShell}/commands/deluser.ts +0 -0
  49. /package/src/{SSHMimic → VirtualShell}/commands/exit.ts +0 -0
  50. /package/src/{SSHMimic → VirtualShell}/commands/help.ts +0 -0
  51. /package/src/{SSHMimic → VirtualShell}/commands/hostname.ts +0 -0
  52. /package/src/{SSHMimic → VirtualShell}/commands/htop.ts +0 -0
  53. /package/src/{SSHMimic → VirtualShell}/commands/nano.ts +0 -0
  54. /package/src/{SSHMimic → VirtualShell}/commands/pwd.ts +0 -0
  55. /package/src/{SSHMimic → VirtualShell}/commands/touch.ts +0 -0
  56. /package/src/{SSHMimic → VirtualShell}/commands/whoami.ts +0 -0
@@ -0,0 +1,40 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ getArg,
4
+ getFlag,
5
+ ifFlag,
6
+ } from "../src/VirtualShell/commands/command-helpers";
7
+
8
+ describe("command-helpers", () => {
9
+ test("ifFlag detects plain and inline flag forms", () => {
10
+ expect(ifFlag(["-l", "docs"], ["-l", "--long"])).toBe(true);
11
+ expect(ifFlag(["--user=root", "whoami"], "--user")).toBe(true);
12
+ expect(ifFlag(["docs"], ["-l", "--long"])).toBe(false);
13
+ });
14
+
15
+ test("getFlag returns value for adjacent and inline forms", () => {
16
+ expect(getFlag(["-u", "root", "id"], ["-u", "--user"])).toBe("root");
17
+ expect(getFlag(["--user=alice", "id"], ["-u", "--user"])).toBe("alice");
18
+ expect(getFlag(["-i", "whoami"], "-i")).toBe("whoami");
19
+ expect(getFlag(["-o"], "-o")).toBe(true);
20
+ expect(getFlag(["pwd"], ["-u", "--user"])).toBeUndefined();
21
+ });
22
+
23
+ test("getArg skips bool and value flags", () => {
24
+ const args = ["-i", "-u", "root", "sh", "-c", "whoami"];
25
+ const options = { flags: ["-i"], flagsWithValue: ["-u"] };
26
+
27
+ expect(getArg(args, 0, options)).toBe("sh");
28
+ expect(getArg(args, 1, options)).toBe("-c");
29
+ expect(getArg(args, 2, options)).toBe("whoami");
30
+ expect(getArg(args, 3, options)).toBeUndefined();
31
+ });
32
+
33
+ test("getArg keeps tokens after -- as positional", () => {
34
+ const args = ["-n", "--", "-n", "hello"];
35
+ const options = { flags: ["-n"] };
36
+
37
+ expect(getArg(args, 0, options)).toBe("-n");
38
+ expect(getArg(args, 1, options)).toBe("hello");
39
+ });
40
+ });
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { assertPathAccess } from "../src/SSHMimic/commands/helpers";
2
+ import { assertPathAccess } from "../src/VirtualShell/commands/helpers";
3
3
 
4
4
  describe("assertPathAccess", () => {
5
5
  test("blocks non-root access to auth store", () => {
@@ -1,120 +0,0 @@
1
- import type {
2
- CommandMode,
3
- CommandOutcome,
4
- ShellModule,
5
- } from "../../types/commands";
6
- import type VirtualFileSystem from "../../VirtualFileSystem";
7
- import type { VirtualUserManager } from "../users";
8
- import { adduserCommand } from "./adduser";
9
- import { catCommand } from "./cat";
10
- import { cdCommand } from "./cd";
11
- import { clearCommand } from "./clear";
12
- import { curlCommand } from "./curl";
13
- import { deluserCommand } from "./deluser";
14
- import { exitCommand } from "./exit";
15
- import { createHelpCommand } from "./help";
16
- import { hostnameCommand } from "./hostname";
17
- import { htopCommand } from "./htop";
18
- import { lsCommand } from "./ls";
19
- import { mkdirCommand } from "./mkdir";
20
- import { nanoCommand } from "./nano";
21
- import { pwdCommand } from "./pwd";
22
- import { rmCommand } from "./rm";
23
- import { suCommand } from "./su";
24
- import { sudoCommand } from "./sudo";
25
- import { touchCommand } from "./touch";
26
- import { treeCommand } from "./tree";
27
- import { wgetCommand } from "./wget";
28
- import { whoCommand } from "./who";
29
- import { whoamiCommand } from "./whoami";
30
-
31
- const BASE_COMMANDS: ShellModule[] = [
32
- pwdCommand,
33
- whoamiCommand,
34
- whoCommand,
35
- hostnameCommand,
36
- lsCommand,
37
- cdCommand,
38
- catCommand,
39
- mkdirCommand,
40
- touchCommand,
41
- rmCommand,
42
- treeCommand,
43
- nanoCommand,
44
- htopCommand,
45
- adduserCommand,
46
- deluserCommand,
47
- sudoCommand,
48
- suCommand,
49
- curlCommand,
50
- wgetCommand,
51
- clearCommand,
52
- exitCommand,
53
- ];
54
-
55
- const COMMANDS: ShellModule[] = [
56
- ...BASE_COMMANDS,
57
- createHelpCommand(() => COMMANDS.map((cmd) => cmd.name)),
58
- ];
59
-
60
- export function getCommandNames(): string[] {
61
- return COMMANDS.flatMap((cmd) => [cmd.name, ...(cmd.aliases ?? [])]);
62
- }
63
-
64
- function resolveModule(name: string): ShellModule | undefined {
65
- const lowered = name.toLowerCase();
66
- return COMMANDS.find(
67
- (cmd) => cmd.name === lowered || cmd.aliases?.includes(lowered),
68
- );
69
- }
70
-
71
- function parseInput(rawInput: string): { commandName: string; args: string[] } {
72
- const parts = rawInput.trim().split(/\s+/).filter(Boolean);
73
- return {
74
- commandName: parts[0]?.toLowerCase() ?? "",
75
- args: parts.slice(1),
76
- };
77
- }
78
-
79
- export function runCommand(
80
- rawInput: string,
81
- authUser: string,
82
- hostname: string,
83
- users: VirtualUserManager,
84
- mode: CommandMode,
85
- cwd: string,
86
- vfs: VirtualFileSystem,
87
- ): CommandOutcome {
88
- const trimmed = rawInput.trim();
89
-
90
- if (trimmed.length === 0) {
91
- return { exitCode: 0 };
92
- }
93
-
94
- const { commandName, args } = parseInput(trimmed);
95
- const mod = resolveModule(commandName);
96
-
97
- if (!mod) {
98
- return {
99
- stderr: `Command '${trimmed}' not found`,
100
- exitCode: 127,
101
- };
102
- }
103
-
104
- try {
105
- return mod.run({
106
- authUser,
107
- hostname,
108
- users,
109
- activeSessions: users.listActiveSessions(),
110
- rawInput: trimmed,
111
- mode,
112
- args,
113
- cwd,
114
- vfs,
115
- });
116
- } catch (error: unknown) {
117
- const message = error instanceof Error ? error.message : "Command failed";
118
- return { stderr: message, exitCode: 1 };
119
- }
120
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes