typescript-virtual-container 1.2.8 → 1.3.0
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 +0 -1
- package/README.md +462 -44
- package/biome.json +7 -0
- package/dist/SSHMimic/exec.d.ts.map +1 -1
- package/dist/SSHMimic/executor.d.ts.map +1 -1
- package/dist/SSHMimic/executor.js +35 -21
- package/dist/SSHMimic/index.d.ts.map +1 -1
- package/dist/SSHMimic/index.js +20 -6
- package/dist/VirtualFileSystem/binaryPack.d.ts.map +1 -1
- package/dist/VirtualFileSystem/binaryPack.js +29 -6
- package/dist/VirtualFileSystem/index.d.ts.map +1 -1
- package/dist/VirtualFileSystem/index.js +36 -13
- package/dist/VirtualPackageManager/index.d.ts +202 -0
- package/dist/VirtualPackageManager/index.d.ts.map +1 -0
- package/dist/VirtualPackageManager/index.js +825 -0
- package/dist/VirtualShell/index.d.ts +93 -12
- package/dist/VirtualShell/index.d.ts.map +1 -1
- package/dist/VirtualShell/index.js +95 -13
- package/dist/VirtualShell/shell.d.ts.map +1 -1
- package/dist/VirtualShell/shell.js +3 -1
- package/dist/VirtualShell/shellParser.d.ts.map +1 -1
- package/dist/VirtualUserManager/index.d.ts +52 -20
- package/dist/VirtualUserManager/index.d.ts.map +1 -1
- package/dist/VirtualUserManager/index.js +54 -20
- package/dist/commands/adduser.d.ts +6 -0
- package/dist/commands/adduser.d.ts.map +1 -1
- package/dist/commands/adduser.js +6 -0
- package/dist/commands/alias.d.ts +9 -0
- package/dist/commands/alias.d.ts.map +1 -0
- package/dist/commands/alias.js +63 -0
- package/dist/commands/apt.d.ts +9 -0
- package/dist/commands/apt.d.ts.map +1 -0
- package/dist/commands/apt.js +205 -0
- package/dist/commands/awk.d.ts +11 -0
- package/dist/commands/awk.d.ts.map +1 -1
- package/dist/commands/awk.js +15 -2
- package/dist/commands/base64.d.ts +5 -0
- package/dist/commands/base64.d.ts.map +1 -1
- package/dist/commands/base64.js +9 -1
- package/dist/commands/cat.d.ts +5 -0
- package/dist/commands/cat.d.ts.map +1 -1
- package/dist/commands/cat.js +35 -8
- package/dist/commands/cd.d.ts +5 -0
- package/dist/commands/cd.d.ts.map +1 -1
- package/dist/commands/cd.js +5 -0
- package/dist/commands/chmod.d.ts +5 -0
- package/dist/commands/chmod.d.ts.map +1 -1
- package/dist/commands/chmod.js +57 -3
- package/dist/commands/command-helpers.d.ts +78 -4
- package/dist/commands/command-helpers.d.ts.map +1 -1
- package/dist/commands/command-helpers.js +78 -4
- package/dist/commands/cp.d.ts +5 -0
- package/dist/commands/cp.d.ts.map +1 -1
- package/dist/commands/cp.js +5 -0
- package/dist/commands/curl.d.ts +5 -0
- package/dist/commands/curl.d.ts.map +1 -1
- package/dist/commands/curl.js +106 -26
- package/dist/commands/cut.d.ts +5 -0
- package/dist/commands/cut.d.ts.map +1 -1
- package/dist/commands/cut.js +8 -1
- package/dist/commands/date.d.ts +5 -0
- package/dist/commands/date.d.ts.map +1 -1
- package/dist/commands/date.js +7 -1
- package/dist/commands/declare.d.ts +3 -0
- package/dist/commands/declare.d.ts.map +1 -0
- package/dist/commands/declare.js +39 -0
- package/dist/commands/diff.d.ts +5 -0
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +5 -0
- package/dist/commands/dpkg.d.ts +9 -0
- package/dist/commands/dpkg.d.ts.map +1 -0
- package/dist/commands/dpkg.js +161 -0
- package/dist/commands/du.d.ts.map +1 -1
- package/dist/commands/du.js +8 -2
- package/dist/commands/echo.d.ts +5 -0
- package/dist/commands/echo.d.ts.map +1 -1
- package/dist/commands/echo.js +33 -12
- package/dist/commands/env.d.ts +5 -0
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +11 -1
- package/dist/commands/exit.d.ts +5 -0
- package/dist/commands/exit.d.ts.map +1 -1
- package/dist/commands/exit.js +12 -2
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +3 -1
- package/dist/commands/find.d.ts +5 -0
- package/dist/commands/find.d.ts.map +1 -1
- package/dist/commands/find.js +5 -0
- package/dist/commands/free.d.ts +8 -0
- package/dist/commands/free.d.ts.map +1 -0
- package/dist/commands/free.js +43 -0
- package/dist/commands/grep.d.ts +5 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +12 -2
- package/dist/commands/gzip.d.ts +5 -0
- package/dist/commands/gzip.d.ts.map +1 -1
- package/dist/commands/gzip.js +18 -2
- package/dist/commands/head.d.ts +5 -0
- package/dist/commands/head.d.ts.map +1 -1
- package/dist/commands/head.js +5 -0
- package/dist/commands/help.d.ts.map +1 -1
- package/dist/commands/help.js +98 -45
- package/dist/commands/helpers.d.ts +3 -0
- package/dist/commands/helpers.d.ts.map +1 -1
- package/dist/commands/helpers.js +3 -0
- package/dist/commands/history.d.ts +8 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +26 -0
- package/dist/commands/hostname.d.ts +5 -0
- package/dist/commands/hostname.d.ts.map +1 -1
- package/dist/commands/hostname.js +5 -0
- package/dist/commands/id.d.ts.map +1 -1
- package/dist/commands/id.js +4 -1
- package/dist/commands/index.d.ts +2 -10
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +2 -231
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +6 -3
- package/dist/commands/lsb-release.d.ts +3 -0
- package/dist/commands/lsb-release.d.ts.map +1 -0
- package/dist/commands/lsb-release.js +56 -0
- package/dist/commands/man.d.ts +3 -0
- package/dist/commands/man.d.ts.map +1 -0
- package/dist/commands/man.js +155 -0
- package/dist/commands/nano.js +1 -1
- package/dist/commands/neofetch.d.ts.map +1 -1
- package/dist/commands/neofetch.js +6 -1
- package/dist/commands/node.d.ts +9 -0
- package/dist/commands/node.d.ts.map +1 -0
- package/dist/commands/node.js +316 -0
- package/dist/commands/npm.d.ts +19 -0
- package/dist/commands/npm.d.ts.map +1 -0
- package/dist/commands/npm.js +109 -0
- package/dist/commands/ping.d.ts.map +1 -1
- package/dist/commands/ping.js +7 -2
- package/dist/commands/printf.d.ts +3 -0
- package/dist/commands/printf.d.ts.map +1 -0
- package/dist/commands/printf.js +113 -0
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +30 -6
- package/dist/commands/python.d.ts +30 -0
- package/dist/commands/python.d.ts.map +1 -0
- package/dist/commands/python.js +2058 -0
- package/dist/commands/read.d.ts +3 -0
- package/dist/commands/read.d.ts.map +1 -0
- package/dist/commands/read.js +34 -0
- package/dist/commands/registry.d.ts +8 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +229 -0
- package/dist/commands/runtime.d.ts +6 -0
- package/dist/commands/runtime.d.ts.map +1 -0
- package/dist/commands/runtime.js +280 -0
- package/dist/commands/sed.d.ts.map +1 -1
- package/dist/commands/sed.js +11 -3
- package/dist/commands/set.d.ts.map +1 -1
- package/dist/commands/set.js +9 -3
- package/dist/commands/sh.d.ts.map +1 -1
- package/dist/commands/sh.js +69 -30
- package/dist/commands/shift.d.ts +5 -0
- package/dist/commands/shift.d.ts.map +1 -0
- package/dist/commands/shift.js +52 -0
- package/dist/commands/sleep.d.ts.map +1 -1
- package/dist/commands/sort.d.ts.map +1 -1
- package/dist/commands/sort.js +4 -2
- package/dist/commands/source.d.ts +3 -0
- package/dist/commands/source.d.ts.map +1 -0
- package/dist/commands/source.js +34 -0
- package/dist/commands/sudo.js +1 -1
- package/dist/commands/tar.d.ts.map +1 -1
- package/dist/commands/tar.js +11 -3
- package/dist/commands/tee.d.ts.map +1 -1
- package/dist/commands/tee.js +8 -6
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +114 -0
- package/dist/commands/tr.d.ts.map +1 -1
- package/dist/commands/tr.js +3 -1
- package/dist/commands/true.d.ts +4 -0
- package/dist/commands/true.d.ts.map +1 -0
- package/dist/commands/true.js +14 -0
- package/dist/commands/type.d.ts +3 -0
- package/dist/commands/type.d.ts.map +1 -0
- package/dist/commands/type.js +34 -0
- package/dist/commands/uname.d.ts.map +1 -1
- package/dist/commands/uname.js +4 -1
- package/dist/commands/uniq.d.ts.map +1 -1
- package/dist/commands/uptime.d.ts +3 -0
- package/dist/commands/uptime.d.ts.map +1 -0
- package/dist/commands/uptime.js +43 -0
- package/dist/commands/wget.d.ts.map +1 -1
- package/dist/commands/wget.js +92 -96
- package/dist/commands/which.d.ts +3 -0
- package/dist/commands/which.d.ts.map +1 -0
- package/dist/commands/which.js +32 -0
- package/dist/commands/xargs.d.ts.map +1 -1
- package/dist/commands/xargs.js +1 -1
- package/dist/index.d.ts +15 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -8
- package/dist/modules/linuxRootfs.d.ts +41 -0
- package/dist/modules/linuxRootfs.d.ts.map +1 -0
- package/dist/modules/linuxRootfs.js +440 -0
- package/dist/modules/neofetch.d.ts.map +1 -1
- package/dist/modules/neofetch.js +1 -0
- package/dist/standalone-wo-sftp.d.ts +2 -0
- package/dist/standalone-wo-sftp.d.ts.map +1 -0
- package/dist/standalone-wo-sftp.js +30 -0
- package/dist/utils/expand.d.ts +50 -0
- package/dist/utils/expand.d.ts.map +1 -0
- package/dist/utils/expand.js +183 -0
- package/dist/utils/vfsDiff.d.ts +90 -0
- package/dist/utils/vfsDiff.d.ts.map +1 -0
- package/dist/utils/vfsDiff.js +177 -0
- package/package.json +3 -1
- package/src/SSHMimic/exec.ts +10 -1
- package/src/SSHMimic/executor.ts +105 -21
- package/src/SSHMimic/index.ts +49 -15
- package/src/VirtualFileSystem/binaryPack.ts +35 -8
- package/src/VirtualFileSystem/index.ts +78 -28
- package/src/VirtualPackageManager/index.ts +979 -0
- package/src/VirtualShell/index.ts +133 -14
- package/src/VirtualShell/shell.ts +23 -3
- package/src/VirtualShell/shellParser.ts +134 -36
- package/src/VirtualUserManager/index.ts +62 -22
- package/src/commands/adduser.ts +6 -0
- package/src/commands/alias.ts +64 -0
- package/src/commands/apt.ts +228 -0
- package/src/commands/awk.ts +20 -6
- package/src/commands/base64.ts +13 -2
- package/src/commands/cat.ts +40 -8
- package/src/commands/cd.ts +5 -0
- package/src/commands/chmod.ts +53 -3
- package/src/commands/command-helpers.ts +78 -4
- package/src/commands/cp.ts +5 -0
- package/src/commands/curl.ts +118 -33
- package/src/commands/cut.ts +8 -1
- package/src/commands/date.ts +7 -1
- package/src/commands/declare.ts +44 -0
- package/src/commands/diff.ts +17 -3
- package/src/commands/dpkg.ts +180 -0
- package/src/commands/du.ts +17 -5
- package/src/commands/echo.ts +41 -12
- package/src/commands/env.ts +11 -1
- package/src/commands/exit.ts +12 -2
- package/src/commands/export.ts +3 -1
- package/src/commands/find.ts +5 -0
- package/src/commands/free.ts +47 -0
- package/src/commands/grep.ts +12 -2
- package/src/commands/gzip.ts +28 -4
- package/src/commands/head.ts +5 -0
- package/src/commands/help.ts +121 -47
- package/src/commands/helpers.ts +8 -0
- package/src/commands/history.ts +34 -0
- package/src/commands/hostname.ts +5 -0
- package/src/commands/id.ts +4 -1
- package/src/commands/index.ts +9 -255
- package/src/commands/ls.ts +6 -3
- package/src/commands/lsb-release.ts +58 -0
- package/src/commands/man.ts +166 -0
- package/src/commands/nano.ts +1 -1
- package/src/commands/neofetch.ts +6 -1
- package/src/commands/node.ts +341 -0
- package/src/commands/npm.ts +132 -0
- package/src/commands/ping.ts +10 -3
- package/src/commands/printf.ts +112 -0
- package/src/commands/ps.ts +40 -6
- package/src/commands/python.ts +2229 -0
- package/src/commands/read.ts +41 -0
- package/src/commands/registry.ts +244 -0
- package/src/commands/runtime.ts +353 -0
- package/src/commands/sed.ts +27 -9
- package/src/commands/set.ts +9 -3
- package/src/commands/sh.ts +170 -44
- package/src/commands/shift.ts +53 -0
- package/src/commands/sleep.ts +2 -1
- package/src/commands/sort.ts +10 -6
- package/src/commands/source.ts +47 -0
- package/src/commands/sudo.ts +1 -1
- package/src/commands/tar.ts +28 -7
- package/src/commands/tee.ts +7 -1
- package/src/commands/test.ts +135 -0
- package/src/commands/tr.ts +3 -1
- package/src/commands/true.ts +17 -0
- package/src/commands/type.ts +43 -0
- package/src/commands/uname.ts +5 -1
- package/src/commands/uniq.ts +8 -2
- package/src/commands/uptime.ts +49 -0
- package/src/commands/wget.ts +105 -119
- package/src/commands/which.ts +37 -0
- package/src/commands/xargs.ts +11 -2
- package/src/index.ts +27 -18
- package/src/modules/linuxRootfs.ts +642 -0
- package/src/modules/neofetch.ts +1 -0
- package/src/standalone-wo-sftp.ts +38 -0
- package/src/utils/expand.ts +238 -0
- package/src/utils/vfsDiff.ts +275 -0
- package/standalone-wo-sftp.js +507 -0
- package/standalone-wo-sftp.js.map +7 -0
- package/standalone.js +486 -109
- package/standalone.js.map +4 -4
- package/tests/bun-test-shim.ts +9 -1
- package/tests/command-helpers.test.ts +1 -5
- package/tests/new-features.test.ts +1036 -0
- package/tests/parser-executor.test.ts +27 -27
- package/tests/sftp.test.ts +122 -42
- package/tests/users.test.ts +23 -5
- package/CHANGELOG.md +0 -150
package/.vscode/settings.json
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `typescript-virtual-container`
|
|
2
2
|
|
|
3
|
-
> Pure in-memory SSH/SFTP server with a virtual
|
|
3
|
+
> Pure in-memory SSH/SFTP server with a realistic Linux rootfs, a virtual package manager, a real shell interpreter, and a typed programmatic API for testing, automation, honeypots, and interactive shell scripting in TypeScript/JavaScript.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/typescript-virtual-container)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -23,10 +23,13 @@
|
|
|
23
23
|
- [VirtualFileSystem](#virtualfilesystem)
|
|
24
24
|
- [VFSB Binary Format](#vfsb-binary-format)
|
|
25
25
|
- [VirtualUserManager](#virtualusermanager)
|
|
26
|
+
- [VirtualPackageManager](#virtualpackagemanager)
|
|
26
27
|
- [HoneyPot](#honeypot)
|
|
27
28
|
- [SshClient](#sshclient-programmatic-api)
|
|
28
29
|
- [Key Types](#key-types)
|
|
29
30
|
- [Usage Examples](#usage-examples)
|
|
31
|
+
- [Linux Rootfs](#linux-rootfs)
|
|
32
|
+
- [Package Manager (apt/dpkg)](#package-manager-aptdpkg)
|
|
30
33
|
- [Built-in Commands](#built-in-commands)
|
|
31
34
|
- [Shell Scripting](#shell-scripting)
|
|
32
35
|
- [Configuration](#configuration)
|
|
@@ -57,7 +60,11 @@
|
|
|
57
60
|
- **`.bashrc` support**: Loaded automatically at interactive session start from `/home/<user>/.bashrc`.
|
|
58
61
|
- **Event-Driven Architecture**: All core classes extend `EventEmitter` for lifecycle and operation tracking.
|
|
59
62
|
- **Security Auditing**: Built-in `HoneyPot` utility for comprehensive activity logging, event tracking, statistics collection, and anomaly detection across all components.
|
|
60
|
-
- **
|
|
63
|
+
- **Linux rootfs on boot**: Realistic `/etc`, `/proc`, `/sys`, `/dev`, `/usr`, `/var` hierarchy populated at startup — `os-release`, `passwd`, `hosts`, `resolv.conf`, `/proc/meminfo`, `/proc/cpuinfo`, and more.
|
|
64
|
+
- **Virtual package manager**: `apt install`, `apt remove`, `apt search`, `dpkg -l`, `dpkg -s` — 25 packages in the built-in registry (vim, git, nodejs, python3, curl, openssh, gcc…). Writes files into VFS, tracks state in `/var/lib/dpkg/status`.
|
|
65
|
+
- **87 Built-in Commands**: Full navigation, text processing, archiving, system info, package management, and user management commands — grouped and documented in the interactive `help` system.
|
|
66
|
+
- **`$(cmd)` command substitution**: Nested command execution in any argument position.
|
|
67
|
+
- **Alias support**: `alias`, `unalias` — persisted in session environment.
|
|
61
68
|
- **Full TypeScript Support**: Complete JSDoc coverage, exported types, and first-class async/await for all operations.
|
|
62
69
|
|
|
63
70
|
---
|
|
@@ -74,10 +81,10 @@
|
|
|
74
81
|
### What This Is Not
|
|
75
82
|
|
|
76
83
|
- Not a fully isolated container runtime.
|
|
77
|
-
- Not a security sandbox — the VFS does not sandbox host filesystem access by spawned child processes (e.g. `wget`, `curl` delegate to the host binary).
|
|
78
84
|
- Not a kernel-level isolation boundary (unlike Docker/VM-based isolation).
|
|
85
|
+
- Package stubs (e.g. `node`, `python3`) write files into the VFS and are visible to `which`/`dpkg -L`, but do not execute real binaries — the shell is pure TypeScript with no `execvp`.
|
|
79
86
|
|
|
80
|
-
This project emulates shell behavior for developer workflows. It is designed for realism and
|
|
87
|
+
This project emulates shell behavior for developer workflows. `curl` and `wget` use the native `fetch()` API (no host binary). All other network and execution primitives are simulated. It is designed for realism and deployability, not kernel-level security isolation.
|
|
81
88
|
|
|
82
89
|
---
|
|
83
90
|
|
|
@@ -382,12 +389,14 @@ new VirtualShell(
|
|
|
382
389
|
|
|
383
390
|
```typescript
|
|
384
391
|
interface ShellProperties {
|
|
385
|
-
kernel: string; //
|
|
386
|
-
os: string; //
|
|
387
|
-
arch: string; //
|
|
392
|
+
kernel: string; // Kernel version string — shown by uname, neofetch, /proc/version
|
|
393
|
+
os: string; // Full OS description — shown by neofetch, /etc/os-release, lsb_release
|
|
394
|
+
arch: string; // CPU architecture label — shown by uname -m, neofetch
|
|
388
395
|
}
|
|
389
396
|
```
|
|
390
397
|
|
|
398
|
+
Fields map directly to the values reported by `uname -a`, `neofetch`, `lsb_release`, `/etc/os-release`, and `/proc/version` inside the shell. Changing them after construction has no effect — pass them to the constructor.
|
|
399
|
+
|
|
391
400
|
**Example:**
|
|
392
401
|
|
|
393
402
|
```typescript
|
|
@@ -407,13 +416,26 @@ const shell = new VirtualShell("typescript-vm", {
|
|
|
407
416
|
|--------|-------------|
|
|
408
417
|
| `ensureInitialized(): Promise<void>` | Await this before using the shell programmatically. |
|
|
409
418
|
| `addCommand(name, params, callback)` | Register a custom shell command. |
|
|
410
|
-
| `executeCommand(rawInput, authUser, cwd)` | Run a raw command string. |
|
|
411
|
-
| `startInteractiveSession(stream, authUser, sessionId, remoteAddress, terminalSize)` |
|
|
412
|
-
| `writeFileAsUser(authUser, path, content)` | Write a file with quota enforcement. |
|
|
419
|
+
| `executeCommand(rawInput, authUser, cwd)` | Run a raw command string (supports `&&`, `\|`, `$(cmd)`, aliases). |
|
|
420
|
+
| `startInteractiveSession(stream, authUser, sessionId, remoteAddress, terminalSize)` | Attach an interactive PTY session to this shell. |
|
|
421
|
+
| `writeFileAsUser(authUser, path, content)` | Write a file on behalf of a user with quota enforcement. |
|
|
422
|
+
| `refreshProcFs(): void` | Refresh `/proc/uptime`, `/proc/meminfo`, `/proc/cpuinfo`, etc. from live host data. |
|
|
423
|
+
| `syncPasswd(): void` | Sync `/etc/passwd`, `/etc/group`, `/etc/shadow` from `VirtualUserManager` state. |
|
|
413
424
|
| `getVfs(): VirtualFileSystem \| null` | Access the VFS instance. |
|
|
414
425
|
| `getUsers(): VirtualUserManager \| null` | Access the user manager. |
|
|
415
426
|
| `getHostname(): string` | Returns the configured hostname. |
|
|
416
427
|
|
|
428
|
+
#### Public fields
|
|
429
|
+
|
|
430
|
+
| Field | Type | Description |
|
|
431
|
+
|-------|------|-------------|
|
|
432
|
+
| `vfs` | `VirtualFileSystem` | Backing virtual filesystem — use for direct path operations. |
|
|
433
|
+
| `users` | `VirtualUserManager` | Virtual user database — auth, quotas, and session tracking. |
|
|
434
|
+
| `packageManager` | `VirtualPackageManager` | APT/dpkg package manager backed by the built-in registry. |
|
|
435
|
+
| `hostname` | `string` | Hostname shown in the shell prompt and SSH ident string. |
|
|
436
|
+
| `properties` | `ShellProperties` | Distro identity strings surfaced by `uname`, `neofetch`, etc. |
|
|
437
|
+
| `startTime` | `number` | Unix ms timestamp of shell creation — used by `uptime` and `/proc/uptime`. |
|
|
438
|
+
|
|
417
439
|
**Custom command example:**
|
|
418
440
|
|
|
419
441
|
```typescript
|
|
@@ -665,25 +687,27 @@ new VirtualUserManager(
|
|
|
665
687
|
|--------|-------------|
|
|
666
688
|
| `initialize(): Promise<void>` | Load users/sudoers from VFS, ensure root exists. Call once on startup. |
|
|
667
689
|
| `verifyPassword(username, password): boolean` | Check plaintext password against stored hash. |
|
|
668
|
-
| `hasPassword(username): boolean` | Returns true if a password is set for the user. |
|
|
669
|
-
| `hashPassword(password): string` | Hash a password using
|
|
690
|
+
| `hasPassword(username): boolean` | Returns `true` if a non-empty password is set for the user. |
|
|
691
|
+
| `hashPassword(password): string` | Hash a password using scrypt (or SHA-256 with `SSH_MIMIC_FAST_PASSWORD_HASH=1`). |
|
|
692
|
+
| `getPasswordHash(username): string \| null` | Returns the raw stored hash for a user, or `null` if not found. |
|
|
670
693
|
| `addUser(username, password): Promise<void>` | Create user with home directory. |
|
|
671
|
-
| `deleteUser(username): Promise<void>` | Delete user.
|
|
672
|
-
| `setPassword(username, password): Promise<void>` | Update password for an existing user. |
|
|
673
|
-
| `isSudoer(username): boolean` |
|
|
674
|
-
| `addSudoer(username): Promise<void>` | Grant sudo privileges. |
|
|
675
|
-
| `removeSudoer(username): Promise<void>` | Revoke sudo privileges.
|
|
694
|
+
| `deleteUser(username): Promise<void>` | Delete user. Throws when user is `root` or does not exist. |
|
|
695
|
+
| `setPassword(username, password): Promise<void>` | Update password for an existing user. Throws when user does not exist. |
|
|
696
|
+
| `isSudoer(username): boolean` | Returns `true` when the user has sudo privileges. |
|
|
697
|
+
| `addSudoer(username): Promise<void>` | Grant sudo privileges. Throws when user does not exist. |
|
|
698
|
+
| `removeSudoer(username): Promise<void>` | Revoke sudo privileges. Throws when username is `root`. |
|
|
676
699
|
| `setQuotaBytes(username, maxBytes): Promise<void>` | Set per-user write quota (bytes under `/home/<user>`). |
|
|
677
|
-
| `clearQuota(username): Promise<void>` | Remove quota limit. |
|
|
678
|
-
| `getQuotaBytes(username): number \| null` | Returns quota in bytes, or null if unlimited. |
|
|
700
|
+
| `clearQuota(username): Promise<void>` | Remove quota limit for a user. |
|
|
701
|
+
| `getQuotaBytes(username): number \| null` | Returns quota in bytes, or `null` if unlimited. |
|
|
679
702
|
| `getUsageBytes(username): number` | Returns current usage in bytes under `/home/<user>`. |
|
|
680
703
|
| `assertWriteWithinQuota(username, path, content)` | Throws if the write would exceed the user's quota. |
|
|
704
|
+
| `listUsers(): string[]` | Returns a sorted list of all registered usernames. |
|
|
681
705
|
| `addAuthorizedKey(username, algo, data)` | Register an SSH public key for the user. |
|
|
682
706
|
| `getAuthorizedKeys(username)` | Returns the list of authorized keys for a user. |
|
|
683
707
|
| `removeAuthorizedKeys(username)` | Revoke all authorized keys for a user. |
|
|
684
|
-
| `registerSession(username, remoteAddress): VirtualActiveSession` |
|
|
685
|
-
| `unregisterSession(sessionId): void` |
|
|
686
|
-
| `updateSession(sessionId, username, remoteAddress): void` | Update session metadata
|
|
708
|
+
| `registerSession(username, remoteAddress): VirtualActiveSession` | Register an active session and allocate a virtual TTY. |
|
|
709
|
+
| `unregisterSession(sessionId): void` | Remove a session record on disconnect. Safe to call with `null`. |
|
|
710
|
+
| `updateSession(sessionId, username, remoteAddress): void` | Update session metadata after `su`/`sudo` identity change. |
|
|
687
711
|
| `listActiveSessions(): VirtualActiveSession[]` | Returns all active sessions sorted by start time. |
|
|
688
712
|
|
|
689
713
|
#### Events
|
|
@@ -712,6 +736,125 @@ users.on("session:register", ({ sessionId, username, remoteAddress }) => {
|
|
|
712
736
|
|
|
713
737
|
---
|
|
714
738
|
|
|
739
|
+
|
|
740
|
+
### `VirtualPackageManager`
|
|
741
|
+
|
|
742
|
+
Simulates APT/dpkg package management backed by a built-in registry. Accessed via `shell.packageManager`.
|
|
743
|
+
|
|
744
|
+
#### Constructor
|
|
745
|
+
|
|
746
|
+
Instantiated automatically by `VirtualShell`. Not constructed directly.
|
|
747
|
+
|
|
748
|
+
#### Methods
|
|
749
|
+
|
|
750
|
+
| Method | Description |
|
|
751
|
+
|--------|-------------|
|
|
752
|
+
| `load()` | Load installed packages from `/var/lib/dpkg/status` (called on shell init) |
|
|
753
|
+
| `install(names, opts?)` | Install packages (resolves deps, writes files to VFS). Returns `{ output, exitCode }` |
|
|
754
|
+
| `remove(names, opts?)` | Remove packages. `opts.purge` also removes config files |
|
|
755
|
+
| `search(term)` | Search available packages by name or description |
|
|
756
|
+
| `show(name)` | Show dpkg-style metadata block for a package |
|
|
757
|
+
| `listInstalled()` | List all installed packages as `InstalledPackage[]` |
|
|
758
|
+
| `listAvailable()` | List all packages in the registry |
|
|
759
|
+
| `isInstalled(name)` | Returns `true` if package is installed |
|
|
760
|
+
| `installedCount()` | Count of installed packages (used by `neofetch`) |
|
|
761
|
+
| `findInRegistry(name)` | Look up a `PackageDefinition` by name |
|
|
762
|
+
|
|
763
|
+
#### Types
|
|
764
|
+
|
|
765
|
+
```ts
|
|
766
|
+
interface PackageDefinition {
|
|
767
|
+
name: string;
|
|
768
|
+
version: string;
|
|
769
|
+
description: string;
|
|
770
|
+
files?: PackageFile[]; // written to VFS on install
|
|
771
|
+
depends?: string[]; // resolved recursively
|
|
772
|
+
onInstall?: (vfs, users) => void;
|
|
773
|
+
onRemove?: (vfs) => void;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
interface InstalledPackage {
|
|
777
|
+
name: string;
|
|
778
|
+
version: string;
|
|
779
|
+
architecture: string;
|
|
780
|
+
installedAt: string; // ISO-8601
|
|
781
|
+
files: string[]; // paths written to VFS
|
|
782
|
+
}
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
---
|
|
786
|
+
|
|
787
|
+
### Snapshot Diff Tooling
|
|
788
|
+
|
|
789
|
+
Three utility functions for comparing VFS snapshots in tests and deployment verification.
|
|
790
|
+
|
|
791
|
+
```typescript
|
|
792
|
+
import {
|
|
793
|
+
diffSnapshots,
|
|
794
|
+
formatDiff,
|
|
795
|
+
assertDiff,
|
|
796
|
+
} from "typescript-virtual-container";
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
#### `diffSnapshots(before, after, options?): VfsDiff`
|
|
800
|
+
|
|
801
|
+
Compares two snapshots and returns structured diff results.
|
|
802
|
+
|
|
803
|
+
```ts
|
|
804
|
+
const before = shell.vfs.toSnapshot();
|
|
805
|
+
await client.exec("apt install vim && mkdir -p /app");
|
|
806
|
+
const after = shell.vfs.toSnapshot();
|
|
807
|
+
|
|
808
|
+
const diff = diffSnapshots(before, after, {
|
|
809
|
+
ignore: ["/proc", "/var/log"], // skip volatile paths
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
diff.clean; // false
|
|
813
|
+
diff.added; // [{ path: "/usr/bin/vim", type: "file" }, ...]
|
|
814
|
+
diff.removed; // []
|
|
815
|
+
diff.modified; // [{ path: "/var/lib/dpkg/status", before: "...", after: "..." }]
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
#### `formatDiff(diff, options?): string`
|
|
819
|
+
|
|
820
|
+
Human-readable output similar to `git diff --stat`.
|
|
821
|
+
|
|
822
|
+
```ts
|
|
823
|
+
console.log(formatDiff(diff));
|
|
824
|
+
// + /app [directory]
|
|
825
|
+
// + /usr/bin/vim [file]
|
|
826
|
+
// ~ /var/lib/dpkg/status [modified]
|
|
827
|
+
//
|
|
828
|
+
// 2 added, 1 modified
|
|
829
|
+
|
|
830
|
+
console.log(formatDiff(diff, { showContent: true, maxContentChars: 80 }));
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
#### `assertDiff(diff, expected): void`
|
|
834
|
+
|
|
835
|
+
Throws on mismatch — designed for test suites.
|
|
836
|
+
|
|
837
|
+
```ts
|
|
838
|
+
assertDiff(diff, {
|
|
839
|
+
added: ["/app", "/usr/bin/vim"],
|
|
840
|
+
modified: ["/var/lib/dpkg/status"],
|
|
841
|
+
});
|
|
842
|
+
// throws if any expected path is not in the diff
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
#### Types
|
|
846
|
+
|
|
847
|
+
```ts
|
|
848
|
+
interface VfsDiff {
|
|
849
|
+
added: VfsDiffEntry[]; // { path, type }
|
|
850
|
+
removed: VfsDiffEntry[]; // { path, type }
|
|
851
|
+
modified: VfsDiffModified[]; // { path, type, before, after }
|
|
852
|
+
clean: boolean;
|
|
853
|
+
}
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
---
|
|
857
|
+
|
|
715
858
|
### `HoneyPot`
|
|
716
859
|
|
|
717
860
|
Comprehensive security auditing and event tracking utility. Attaches listeners to all core components to log activity, track statistics, and detect anomalies.
|
|
@@ -966,6 +1109,49 @@ interface VfsSnapshot {
|
|
|
966
1109
|
|
|
967
1110
|
---
|
|
968
1111
|
|
|
1112
|
+
### Command Helpers
|
|
1113
|
+
|
|
1114
|
+
Three utility functions are exported from `typescript-virtual-container` to assist with argument parsing inside custom command handlers.
|
|
1115
|
+
|
|
1116
|
+
#### `ifFlag(args, flags): boolean`
|
|
1117
|
+
|
|
1118
|
+
Returns `true` when any of the given flags appear in the argument array. Matches both standalone tokens (`-s`, `--silent`) and inline forms (`--output=file`).
|
|
1119
|
+
|
|
1120
|
+
```typescript
|
|
1121
|
+
import { ifFlag } from "typescript-virtual-container";
|
|
1122
|
+
|
|
1123
|
+
const recursive = ifFlag(args, ["-r", "--recursive"]);
|
|
1124
|
+
const silent = ifFlag(args, "-s");
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
#### `getFlag(args, flags): string | true | undefined`
|
|
1128
|
+
|
|
1129
|
+
Returns the value of a flag, `true` if the flag has no value, or `undefined` if absent.
|
|
1130
|
+
|
|
1131
|
+
```typescript
|
|
1132
|
+
import { getFlag } from "typescript-virtual-container";
|
|
1133
|
+
|
|
1134
|
+
const output = getFlag(args, ["-o", "--output"]);
|
|
1135
|
+
// args = ["--output", "file.txt"] → "file.txt"
|
|
1136
|
+
// args = ["--output=file.txt"] → "file.txt"
|
|
1137
|
+
// args = ["--verbose"] → true (when "verbose" is in flags list)
|
|
1138
|
+
// args = [] → undefined
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
#### `getArg(args, index, options?): string | undefined`
|
|
1142
|
+
|
|
1143
|
+
Returns the positional argument at a given zero-based index, skipping known flags and their values.
|
|
1144
|
+
|
|
1145
|
+
```typescript
|
|
1146
|
+
import { getArg } from "typescript-virtual-container";
|
|
1147
|
+
|
|
1148
|
+
// args = ["-r", "src", "dest"]
|
|
1149
|
+
const src = getArg(args, 0, { flags: ["-r"] }); // "src"
|
|
1150
|
+
const dest = getArg(args, 1, { flags: ["-r"] }); // "dest"
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
---
|
|
1154
|
+
|
|
969
1155
|
## Usage Examples
|
|
970
1156
|
|
|
971
1157
|
### Example 1: Basic SSH Server
|
|
@@ -1316,6 +1502,148 @@ const [r1, r2] = await Promise.all([
|
|
|
1316
1502
|
]);
|
|
1317
1503
|
```
|
|
1318
1504
|
|
|
1505
|
+
---
|
|
1506
|
+
|
|
1507
|
+
## Linux Rootfs
|
|
1508
|
+
|
|
1509
|
+
On every `VirtualShell` init, a realistic Linux directory hierarchy is bootstrapped into the VFS. All paths are created idempotently — FS-mode snapshots survive restarts without duplication.
|
|
1510
|
+
|
|
1511
|
+
### Directory layout
|
|
1512
|
+
|
|
1513
|
+
```
|
|
1514
|
+
/
|
|
1515
|
+
├── bin -> /usr/bin (symlink, Debian-style)
|
|
1516
|
+
├── dev/ null, zero, random, urandom, pts/, shm/
|
|
1517
|
+
├── etc/
|
|
1518
|
+
│ ├── apt/sources.list Fortune package sources
|
|
1519
|
+
│ ├── debian_version
|
|
1520
|
+
│ ├── group synced from VirtualUserManager
|
|
1521
|
+
│ ├── hostname
|
|
1522
|
+
│ ├── hosts 127.0.0.1 localhost + VM hostname
|
|
1523
|
+
│ ├── motd
|
|
1524
|
+
│ ├── os-release NAME="Fortune GNU/Linux" + ShellProperties
|
|
1525
|
+
│ ├── passwd synced from VirtualUserManager
|
|
1526
|
+
│ ├── resolv.conf 1.1.1.1 + 8.8.8.8
|
|
1527
|
+
│ └── shadow (mode 0o640)
|
|
1528
|
+
├── proc/
|
|
1529
|
+
│ ├── 1/ init process (cmdline, status, comm, environ, fd/)
|
|
1530
|
+
│ ├── <pid>/ one entry per active session (based on pts/* TTY)
|
|
1531
|
+
│ ├── self/ mirrors the most recent session's /proc/<pid>/
|
|
1532
|
+
│ ├── cpuinfo real host CPU info
|
|
1533
|
+
│ ├── loadavg
|
|
1534
|
+
│ ├── meminfo real host memory
|
|
1535
|
+
│ ├── net/dev eth0 + lo
|
|
1536
|
+
│ ├── uptime shell uptime in seconds
|
|
1537
|
+
│ └── version kernel from ShellProperties
|
|
1538
|
+
├── root/ root home + .bashrc
|
|
1539
|
+
├── sys/devices/virtual/dmi/id/
|
|
1540
|
+
│ ├── sys_vendor "Fortune Systems"
|
|
1541
|
+
│ └── product_name "VirtualContainer v1"
|
|
1542
|
+
├── tmp/ (mode 0o1777 sticky)
|
|
1543
|
+
├── usr/bin/ stubs for all built-in commands
|
|
1544
|
+
├── var/
|
|
1545
|
+
│ ├── lib/dpkg/status managed by VirtualPackageManager
|
|
1546
|
+
│ └── log/ syslog, auth.log, dpkg.log, apt/
|
|
1547
|
+
└── ...
|
|
1548
|
+
```
|
|
1549
|
+
|
|
1550
|
+
### API
|
|
1551
|
+
|
|
1552
|
+
```ts
|
|
1553
|
+
shell.refreshProcFs(); // refresh /proc/* with current system state
|
|
1554
|
+
shell.syncPasswd(); // sync /etc/passwd|group|shadow from VirtualUserManager
|
|
1555
|
+
```
|
|
1556
|
+
|
|
1557
|
+
`syncPasswd()` is called automatically on `bootstrapLinuxRootfs`. Call it again after `users.addUser()` / `users.deleteUser()` to keep `/etc/passwd` consistent.
|
|
1558
|
+
|
|
1559
|
+
---
|
|
1560
|
+
|
|
1561
|
+
## Package Manager (apt/dpkg)
|
|
1562
|
+
|
|
1563
|
+
A pure-TypeScript APT/dpkg simulation backed by a built-in package registry. No external process is spawned.
|
|
1564
|
+
|
|
1565
|
+
### Workflow
|
|
1566
|
+
|
|
1567
|
+
```
|
|
1568
|
+
apt install <pkg> → resolves deps → writes files to VFS → updates /var/lib/dpkg/status
|
|
1569
|
+
apt remove <pkg> → removes VFS files → updates status
|
|
1570
|
+
dpkg -l → reads installed packages from VirtualPackageManager
|
|
1571
|
+
```
|
|
1572
|
+
|
|
1573
|
+
### Built-in package registry (25 packages)
|
|
1574
|
+
|
|
1575
|
+
| Package | Version | Section |
|
|
1576
|
+
|---------|---------|---------|
|
|
1577
|
+
| `vim` | 2:9.0.1378-2 | editors |
|
|
1578
|
+
| `git` | 1:2.39.2-1 | vcs |
|
|
1579
|
+
| `python3` | 3.11.2-1 | python |
|
|
1580
|
+
| `nodejs` | 18.19.0 | javascript |
|
|
1581
|
+
| `npm` | 9.2.0 | javascript |
|
|
1582
|
+
| `curl` | 7.88.1-10 | web |
|
|
1583
|
+
| `wget` | 1.21.3-1 | web |
|
|
1584
|
+
| `htop` | 3.2.2-2 | utils |
|
|
1585
|
+
| `openssh-client` | 1:9.2p1-2 | net |
|
|
1586
|
+
| `openssh-server` | 1:9.2p1-2 | net |
|
|
1587
|
+
| `net-tools` | 2.10-0.1 | net |
|
|
1588
|
+
| `iputils-ping` | 3:20221126-1 | net |
|
|
1589
|
+
| `jq` | 1.6-2.1 | utils |
|
|
1590
|
+
| `build-essential` | 12.9 | devel |
|
|
1591
|
+
| `gcc` | 4:12.2.0-3 | devel |
|
|
1592
|
+
| `g++` | 4:12.2.0-3 | devel |
|
|
1593
|
+
| `make` | 4.3-4.1 | devel |
|
|
1594
|
+
| `less` | 590-2 | text |
|
|
1595
|
+
| `unzip` | 6.0-28 | utils |
|
|
1596
|
+
| `rsync` | 3.2.7-1 | net |
|
|
1597
|
+
| `tmux` | 3.3a-3 | utils |
|
|
1598
|
+
| `tree` | 2.1.0-1 | utils |
|
|
1599
|
+
| `ca-certificates` | 20230311 | misc |
|
|
1600
|
+
| `sudo` | 1.9.13p3-1 | admin |
|
|
1601
|
+
| `systemd` | 252.22-1 | admin |
|
|
1602
|
+
|
|
1603
|
+
> **Note on package stubs**: installing `nodejs` or `python3` writes stubs into `/usr/bin/` and makes them discoverable via `which` and `dpkg -L`. The stubs print a version line but do not execute real binaries — the shell runtime is pure TypeScript with no `execvp`. This makes them useful for testing install workflows and `which`/`dpkg -L` queries, not for running actual scripts.
|
|
1604
|
+
|
|
1605
|
+
### `VirtualPackageManager` API
|
|
1606
|
+
|
|
1607
|
+
```ts
|
|
1608
|
+
// Access via shell instance
|
|
1609
|
+
const pm = shell.packageManager;
|
|
1610
|
+
|
|
1611
|
+
pm.install(["vim", "git"], { quiet: false }) // → { output, exitCode }
|
|
1612
|
+
pm.remove(["vim"], { purge: false }) // → { output, exitCode }
|
|
1613
|
+
pm.search("editor") // → PackageDefinition[]
|
|
1614
|
+
pm.show("vim") // → string (dpkg-style block)
|
|
1615
|
+
pm.listInstalled() // → InstalledPackage[]
|
|
1616
|
+
pm.listAvailable() // → PackageDefinition[]
|
|
1617
|
+
pm.isInstalled("vim") // → boolean
|
|
1618
|
+
pm.installedCount() // → number
|
|
1619
|
+
pm.findInRegistry("nodejs") // → PackageDefinition | undefined
|
|
1620
|
+
```
|
|
1621
|
+
|
|
1622
|
+
### Registering custom packages
|
|
1623
|
+
|
|
1624
|
+
```ts
|
|
1625
|
+
import { VirtualPackageManager } from "typescript-virtual-container";
|
|
1626
|
+
|
|
1627
|
+
// Custom packages can be added to the registry before shell init
|
|
1628
|
+
// by extending PACKAGE_REGISTRY or by calling install() with custom defs.
|
|
1629
|
+
|
|
1630
|
+
// Or: register a post-install hook via onInstall
|
|
1631
|
+
const customPkg = {
|
|
1632
|
+
name: "myapp",
|
|
1633
|
+
version: "1.0.0",
|
|
1634
|
+
description: "My application",
|
|
1635
|
+
files: [
|
|
1636
|
+
{ path: "/usr/bin/myapp", content: "#!/bin/sh\necho myapp v1.0.0\n", mode: 0o755 },
|
|
1637
|
+
{ path: "/etc/myapp/config.json", content: JSON.stringify({ port: 3000 }) },
|
|
1638
|
+
],
|
|
1639
|
+
onInstall: (vfs) => {
|
|
1640
|
+
vfs.mkdir("/var/lib/myapp", 0o755);
|
|
1641
|
+
vfs.mkdir("/var/log/myapp", 0o755);
|
|
1642
|
+
},
|
|
1643
|
+
};
|
|
1644
|
+
```
|
|
1645
|
+
|
|
1646
|
+
|
|
1319
1647
|
---
|
|
1320
1648
|
|
|
1321
1649
|
## Built-in Commands
|
|
@@ -1327,7 +1655,7 @@ All commands are available in SSH shell mode and via `SshClient.exec()`. Type `h
|
|
|
1327
1655
|
| Command | Flags | Description |
|
|
1328
1656
|
|---------|-------|-------------|
|
|
1329
1657
|
| `cd <path>` | | Change directory |
|
|
1330
|
-
| `ls [path]` | `-l` | List directory contents |
|
|
1658
|
+
| `ls [path]` | `-l` `-a` | List directory contents (`-a` shows dotfiles) |
|
|
1331
1659
|
| `pwd` | | Print working directory |
|
|
1332
1660
|
| `tree [path]` | | ASCII directory tree |
|
|
1333
1661
|
|
|
@@ -1335,14 +1663,15 @@ All commands are available in SSH shell mode and via `SshClient.exec()`. Type `h
|
|
|
1335
1663
|
|
|
1336
1664
|
| Command | Flags | Description |
|
|
1337
1665
|
|---------|-------|-------------|
|
|
1338
|
-
| `cat <path
|
|
1339
|
-
| `chmod <mode> <file>` | | Change file permissions (
|
|
1666
|
+
| `cat <path...>` | `-n` `-b` | Concatenate and print files; `-n` numbers all lines, `-b` numbers non-blank lines |
|
|
1667
|
+
| `chmod <mode> <file>` | | Change file permissions — octal (`755`) or symbolic (`+x`, `u+x`, `go-w`, `a=rx`) |
|
|
1340
1668
|
| `cp <src> <dest>` | `-r` | Copy file or directory |
|
|
1341
1669
|
| `find [path]` | `-name <pat>` `-type f\|d` | Search for files |
|
|
1342
1670
|
| `ln <target> <link>` | `-s` | Create hard or symbolic link |
|
|
1343
1671
|
| `mkdir <path>` | `-p` | Create directory |
|
|
1344
1672
|
| `mv <src> <dest>` | | Move or rename |
|
|
1345
1673
|
| `rm <path>` | `-r` | Remove file or directory |
|
|
1674
|
+
| `nano <path>` | | Interactive text editor |
|
|
1346
1675
|
| `touch <path>` | | Create or update file |
|
|
1347
1676
|
|
|
1348
1677
|
### Text Processing
|
|
@@ -1384,9 +1713,14 @@ All commands are available in SSH shell mode and via `SshClient.exec()`. Type `h
|
|
|
1384
1713
|
| `htop` | | System monitor (mock) |
|
|
1385
1714
|
| `id [user]` | | Print user identity (uid/gid/groups) |
|
|
1386
1715
|
| `kill [-9] <pid>` | | Send signal to process (mock) |
|
|
1387
|
-
| `
|
|
1716
|
+
| `free` | `-h` `-m` `-g` | Display free and used memory |
|
|
1717
|
+
| `lsb_release` | `-a` `-i` `-d` `-r` `-c` | Print distribution information |
|
|
1718
|
+
| `neofetch` | | System info display (shows real packages/uptime) |
|
|
1719
|
+
| `node` | `--version` `-e` `-p` | JavaScript runtime (virtual REPL — evaluates expressions and VFS `.js` files) |
|
|
1720
|
+
| `python3` | `--version` `-c` `-V` | Python 3 interpreter (virtual REPL — evaluates expressions and VFS `.py` files); alias `python` |
|
|
1721
|
+
| `uptime` | `-p` `-s` | Tell how long the system has been running |
|
|
1388
1722
|
| `ping [-c <n>] <host>` | | Send ICMP ECHO_REQUEST (mock) |
|
|
1389
|
-
| `ps` | `-a` `-u` `-x` | Report process status |
|
|
1723
|
+
| `ps` | `-a` `-u` `-x` `aux` | Report process status; `-u` / `aux` shows USER/PID/%CPU/%MEM columns |
|
|
1390
1724
|
| `sleep <seconds>` | | Delay execution |
|
|
1391
1725
|
| `uname` | `-a` `-r` `-m` | Print system information |
|
|
1392
1726
|
| `who` | | List active sessions |
|
|
@@ -1396,22 +1730,51 @@ All commands are available in SSH shell mode and via `SshClient.exec()`. Type `h
|
|
|
1396
1730
|
|
|
1397
1731
|
| Command | Flags | Description |
|
|
1398
1732
|
|---------|-------|-------------|
|
|
1399
|
-
| `curl <url>` | | HTTP client (
|
|
1400
|
-
| `wget <url>` | | File downloader (
|
|
1733
|
+
| `curl <url>` | `-o` `-X` `-d` `-H` `-s` `-I` `-L` `-v` | HTTP client (pure `fetch()`, no host binary) |
|
|
1734
|
+
| `wget <url>` | `-O` `-P` `-q` | File downloader (pure `fetch()`, no host binary) |
|
|
1401
1735
|
|
|
1402
1736
|
### Shell
|
|
1403
1737
|
|
|
1404
1738
|
| Command | Flags | Description |
|
|
1405
1739
|
|---------|-------|-------------|
|
|
1740
|
+
| `alias [name=value]` | | Define or display aliases |
|
|
1406
1741
|
| `clear` | | Clear terminal screen (full ANSI reset) |
|
|
1407
|
-
| `
|
|
1742
|
+
| `declare [name=value]` | `-i` `-r` `-x` | Declare variables with attributes; aliases `local`, `typeset` |
|
|
1743
|
+
| `echo <text>` | `-n` `-e` | Display text; `-n` suppresses newline, `-e` interprets `\n` `\t` `\r` `\\` |
|
|
1408
1744
|
| `env` | | Print session environment variables |
|
|
1409
|
-
| `exit [code]` | | Exit session |
|
|
1745
|
+
| `exit [code]` | | Exit session with optional exit code |
|
|
1410
1746
|
| `export NAME=VALUE` | | Set shell variable in current session |
|
|
1411
|
-
| `help [command]` | | List commands
|
|
1747
|
+
| `help [command]` | | List commands grouped by category, or show usage for a specific command |
|
|
1748
|
+
| `history [n]` | | Display command history (last N entries) |
|
|
1749
|
+
| `man <command>` | | Display command reference manual |
|
|
1750
|
+
| `printf <fmt> [args...]` | | Format and print data (`%s` `%d` `%f` `%x` `\n` `\t`) |
|
|
1751
|
+
| `read [-r] <var...>` | `-r` `-p` | Read a line from stdin into variable(s) |
|
|
1752
|
+
| `return [n]` | | Return from a shell function with optional exit code |
|
|
1412
1753
|
| `set [VAR=val]` | | Display or set shell variables |
|
|
1413
|
-
| `sh` | `-c <script>` `[file]` | Execute shell script
|
|
1754
|
+
| `sh` | `-c <script>` `[file]` | Execute shell script — supports `if`/`for`/`while`/`case`/functions, bare `VAR=val` assignments, `$((expr))` arithmetic; single-quoted args are never expanded |
|
|
1755
|
+
| `shift [n]` | | Shift positional parameters left by n (default 1) |
|
|
1756
|
+
| `source <file>` | | Execute file in current shell environment; aliases and exports persist |
|
|
1757
|
+
| `. <file>` | | Alias for `source` |
|
|
1758
|
+
| `test <expr>` | | Evaluate POSIX conditional expression |
|
|
1759
|
+
| `[ <expr> ]` | | Alias for `test`; supports `-f` `-d` `-e` `-z` `-n` `-x` `-s` `=` `!=` `-eq` `-lt` `-gt` `-le` `-ge` `!` `-a` `-o` |
|
|
1760
|
+
| `trap [action] [signal]` | | Register handler for shell signals; supports `EXIT` |
|
|
1761
|
+
| `true` | | Return success exit code (0) |
|
|
1762
|
+
| `false` | | Return failure exit code (1) |
|
|
1763
|
+
| `type <command>` | | Describe how a command would be interpreted (builtin vs PATH) |
|
|
1764
|
+
| `unalias <name>` | `-a` | Remove alias definitions |
|
|
1414
1765
|
| `unset <VAR>` | | Remove shell variable |
|
|
1766
|
+
| `which <command>` | | Locate a command in the session PATH |
|
|
1767
|
+
|
|
1768
|
+
### Package Management
|
|
1769
|
+
|
|
1770
|
+
| Command | Flags | Description |
|
|
1771
|
+
|---------|-------|-------------|
|
|
1772
|
+
| `apt <cmd> [pkg...]` | | Package manager (`install`, `remove`, `purge`, `update`, `upgrade`, `search`, `show`, `list`) |
|
|
1773
|
+
| `apt-get <cmd>` | | Alias for `apt` |
|
|
1774
|
+
| `apt-cache <cmd>` | | Query package cache (`search`, `show`, `policy`) |
|
|
1775
|
+
| `dpkg` | `-l` `-s` `-L` `-r` `-P` | Debian package manager low-level tool |
|
|
1776
|
+
| `dpkg-query` | `-W` `-l` | Show information about installed packages |
|
|
1777
|
+
|
|
1415
1778
|
|
|
1416
1779
|
### Users & Permissions
|
|
1417
1780
|
|
|
@@ -1419,7 +1782,6 @@ All commands are available in SSH shell mode and via `SshClient.exec()`. Type `h
|
|
|
1419
1782
|
|---------|-------|-------------|
|
|
1420
1783
|
| `adduser <name> <pass>` | | Create user (root only) |
|
|
1421
1784
|
| `deluser <name>` | | Delete user (root only) |
|
|
1422
|
-
| `nano <path>` | | Interactive text editor |
|
|
1423
1785
|
| `passwd [user]` | | Change password |
|
|
1424
1786
|
| `su [user]` | | Switch user |
|
|
1425
1787
|
| `sudo <cmd>` | `-i` | Run as root |
|
|
@@ -1454,11 +1816,34 @@ cat /tmp/out.txt >> /tmp/log.txt
|
|
|
1454
1816
|
```bash
|
|
1455
1817
|
export NAME=world
|
|
1456
1818
|
echo "Hello $NAME" # Hello world
|
|
1819
|
+
echo "${NAME}" # world (braced form)
|
|
1457
1820
|
echo "${NAME:-fallback}" # world (or fallback if unset)
|
|
1458
|
-
echo "${UNSET:-default}" # default
|
|
1459
|
-
echo "
|
|
1821
|
+
echo "${UNSET:-default}" # default (assign-on-read: use ${UNSET:=default})
|
|
1822
|
+
echo "${NAME:+alternate}" # alternate (only if NAME is set)
|
|
1823
|
+
echo "${#NAME}" # 5 (string length)
|
|
1824
|
+
echo "$?" # last exit code
|
|
1825
|
+
echo "~" # /home/<user> (tilde expansion)
|
|
1826
|
+
```
|
|
1827
|
+
|
|
1828
|
+
### Arithmetic Expansion
|
|
1829
|
+
|
|
1830
|
+
```bash
|
|
1831
|
+
echo $((2 + 3)) # 5
|
|
1832
|
+
echo $((10 % 3)) # 1
|
|
1833
|
+
X=4; echo $((X * 2)) # 8
|
|
1834
|
+
i=0; i=$((i + 1)); echo $i # 1
|
|
1835
|
+
|
|
1836
|
+
# In loops (via sh):
|
|
1837
|
+
sh -c 'i=0
|
|
1838
|
+
while [ $i -lt 5 ]; do
|
|
1839
|
+
echo $i
|
|
1840
|
+
i=$((i + 1))
|
|
1841
|
+
done'
|
|
1460
1842
|
```
|
|
1461
1843
|
|
|
1844
|
+
> **Single-quote isolation** — variable and arithmetic expansion never occurs inside single quotes.
|
|
1845
|
+
> `echo '$NAME'` always outputs the literal string `$NAME`.
|
|
1846
|
+
|
|
1462
1847
|
### Conditionals
|
|
1463
1848
|
|
|
1464
1849
|
```bash
|
|
@@ -1789,12 +2174,45 @@ MIT — see [LICENSE](./LICENSE).
|
|
|
1789
2174
|
- [x] New commands: `sort`, `uniq`, `tee`, `cut`, `tr`, `xargs`, `diff`, `sed`, `awk`, `tar`, `gzip`, `gunzip`, `base64`, `date`, `sleep`, `id`, `groups`, `uname`, `ps`, `kill`, `df`, `du`, `ping`
|
|
1790
2175
|
- [x] Structured event hooks (session open/close, file write, sudo challenge)
|
|
1791
2176
|
- [x] Binary snapshot format (VFSB) — replaces JSON+base64, ~27% smaller, no string parsing overhead, backward-compatible JSON migration
|
|
1792
|
-
- [
|
|
2177
|
+
- [x] Linux rootfs on boot — `/etc`, `/proc`, `/sys`, `/dev`, `/usr`, `/var` populated at init; `os-release`, `passwd`, `hosts`, `/proc/meminfo`, `/proc/cpuinfo`, `/proc/version`, `/proc/uptime`, `/sys/devices/virtual/dmi/`, symlinks `/bin`→`/usr/bin`
|
|
2178
|
+
- [x] Virtual package manager — `apt install/remove/purge/update/upgrade/search/show/list`, `apt-get`, `apt-cache`, `dpkg`, `dpkg-query`; 25 packages in registry; writes files to VFS; `/var/lib/dpkg/status` persistence
|
|
2179
|
+
- [x] `curl` / `wget` reimplemented as pure `fetch()` — no host binary spawned, full isolation
|
|
2180
|
+
- [x] New commands: `which`, `type`, `man`, `uptime`, `free`, `lsb_release`, `alias`, `unalias`
|
|
2181
|
+
- [x] `$(cmd)` command substitution in variable expansion
|
|
2182
|
+
- [x] Alias expansion in command dispatch
|
|
2183
|
+
- [x] `neofetch` shows real package count and shell uptime
|
|
2184
|
+
- [x] `syncPasswd()` / `refreshProcFs()` public API on `VirtualShell`
|
|
2185
|
+
- [x] `test` / `[` — full POSIX conditional expressions (`-f`, `-d`, `-e`, `-z`, `-n`, `-x`, `-s`, `-L`, `=`, `!=`, `-eq`, `-lt`, `-gt`, `-le`, `-ge`, `!`, `-a`, `-o`)
|
|
2186
|
+
- [x] `source` / `.` — execute file in current shell env (aliases and exports persist across commands)
|
|
2187
|
+
- [x] `history [n]` — display command history from VFS `.bash_history`
|
|
2188
|
+
- [x] `echo -e` / `echo -n` — escape sequence interpretation and newline suppression
|
|
2189
|
+
- [x] `ls -a` — show dotfiles
|
|
2190
|
+
- [x] `chmod` symbolic modes — `+x`, `u+x`, `go-w`, `a=rx`, comma-separated
|
|
2191
|
+
- [x] `cat -n` / `-b` — line numbering, multi-file concatenation, stdin support
|
|
2192
|
+
- [x] `ping -c N` — respects packet count flag
|
|
2193
|
+
- [x] `ps -u` / `ps aux` — extended format with USER/PID/%CPU/%MEM/VSZ/RSS columns
|
|
2194
|
+
- [x] `$(cmd)` in single-quoted args preserved — `sh -c 'echo $(whoami)'` now works correctly
|
|
2195
|
+
- [x] Pipeline executor: `runCommandDirect` — args with `;`, `|`, `>` no longer re-parsed
|
|
2196
|
+
- [x] `>>` append redirect fixed — was broken because `echo` lacked terminal newline
|
|
2197
|
+
- [x] `export A=1 && echo $A` — env vars visible to subsequent commands in same pipeline
|
|
2198
|
+
- [x] `echo` uses session `env.vars` (not stale global store)
|
|
2199
|
+
- [x] `printf` — format string with `%s` `%d` `%f` `%x` `\n` `\t`
|
|
2200
|
+
- [x] `read` — read stdin into variables (supports multiple vars, splits on whitespace)
|
|
2201
|
+
- [x] `declare` / `local` / `typeset` — variable declaration with `-i` integer, `-r` readonly, `-x` export
|
|
2202
|
+
- [x] `shift [n]` — shift positional parameters
|
|
2203
|
+
- [x] `trap [action] [signal]` — signal handlers with `EXIT` support
|
|
2204
|
+
- [x] `return [n]` — return from shell functions
|
|
2205
|
+
- [x] `exit [code]` — optional exit code
|
|
2206
|
+
- [x] `help` rewrite — Package Management category, aliases shown inline, `help <cmd>` shows category
|
|
2207
|
+
- [x] Category corrections — `neofetch`→system, `nano`→files, `apt`/`dpkg`→package
|
|
2208
|
+
- [x] `src/utils/expand.ts` — centralised expansion module used by `runCommand`, `echo`, and `sh.ts`
|
|
2209
|
+
- [x] `${#VAR}` string length, `$((expr))` arithmetic, `~` tilde expansion
|
|
2210
|
+
- [x] `${VAR:=assign}` assign-on-read, `${VAR:+alternate}` alternate-if-set
|
|
2211
|
+
- [x] Single-quote isolation in expansion — `$VAR` never expanded inside `'...'`
|
|
2212
|
+
- [x] `true` / `false` builtins
|
|
2213
|
+
- [x] Bare `VAR=val` assignments in `sh` scripts (`i=0`, `i=$((i+1))`)
|
|
2214
|
+
- [x] `$?` reflects last command exit code correctly across `&&` / `;` chains
|
|
2215
|
+
- [x] `node` / `python3` virtual REPL stubs — `node -e`, `node <file>`, `python3 -c`, `python3 <file>`, `--version` flags
|
|
2216
|
+
- [x] `/proc/self` and `/proc/<pid>` per-session process entries — `comm`, `status`, `cmdline`, `environ`, `cwd`, `exe`, `fd/`
|
|
2217
|
+
- [x] Snapshot diff tooling — `diffSnapshots()`, `formatDiff()`, `assertDiff()` exported from `typescript-virtual-container`
|
|
1793
2218
|
- [ ] WebSocket-based remote shell client (experimental)
|
|
1794
|
-
- [ ] `$(cmd)` command substitution in variable expansion
|
|
1795
|
-
|
|
1796
|
-
---
|
|
1797
|
-
|
|
1798
|
-
## Changelog
|
|
1799
|
-
|
|
1800
|
-
See [CHANGELOG.md](./CHANGELOG.md).
|