typescript-virtual-container 1.5.2 → 1.5.4

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 (364) hide show
  1. package/README.md +43 -23
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/SSHMimic/executor.js +23 -5
  4. package/dist/commands/basename.d.ts +13 -0
  5. package/dist/commands/basename.js +45 -0
  6. package/dist/commands/file.d.ts +8 -0
  7. package/dist/commands/file.js +57 -0
  8. package/dist/commands/fun.d.ts +32 -0
  9. package/dist/commands/fun.js +172 -0
  10. package/dist/commands/ifconfig.d.ts +7 -0
  11. package/dist/commands/ifconfig.js +52 -0
  12. package/dist/commands/last.d.ts +13 -0
  13. package/dist/commands/last.js +68 -0
  14. package/dist/commands/manuals-bundle.js +598 -6
  15. package/dist/commands/registry.js +24 -2
  16. package/dist/commands/runtime.js +159 -106
  17. package/dist/commands/sh.js +5 -0
  18. package/dist/commands/tput.d.ts +13 -0
  19. package/dist/commands/tput.js +76 -0
  20. package/dist/commands/w.d.ts +7 -0
  21. package/dist/commands/w.js +38 -0
  22. package/dist/utils/expand.d.ts +12 -0
  23. package/dist/utils/expand.js +84 -0
  24. package/package.json +9 -3
  25. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -50
  26. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -31
  27. package/.github/dependabot.yml +0 -27
  28. package/.github/pull_request_template.md +0 -21
  29. package/.github/workflows/create-pull-request.yml +0 -85
  30. package/.github/workflows/publish.yml +0 -25
  31. package/.github/workflows/test-battery.yml +0 -102
  32. package/.vscode/settings.json +0 -20
  33. package/CODE_OF_CONDUCT.md +0 -39
  34. package/CONTRIBUTING.md +0 -59
  35. package/HONEYPOT.md +0 -358
  36. package/SECURITY.md +0 -33
  37. package/benchmark-results.txt +0 -40
  38. package/benchmark-virtualshell.ts +0 -88
  39. package/biome.json +0 -37
  40. package/build.js +0 -22
  41. package/builds/fortune-nyx-v1.5.1-directbash-k6.1.0.mjs +0 -1768
  42. package/builds/fortune-nyx-v1.5.1-ssh-nosftp.js +0 -1768
  43. package/builds/fortune-nyx-v1.5.1-ssh.cjs +0 -1769
  44. package/builds/fortune-nyx-v1.5.1-web.min.js +0 -17022
  45. package/bun.lock +0 -244
  46. package/docs/.nojekyll +0 -1
  47. package/docs/app.js +0 -1755
  48. package/docs/assets/hierarchy.js +0 -1
  49. package/docs/assets/highlight.css +0 -162
  50. package/docs/assets/icons.js +0 -18
  51. package/docs/assets/icons.svg +0 -1
  52. package/docs/assets/main.js +0 -60
  53. package/docs/assets/navigation.js +0 -1
  54. package/docs/assets/search.js +0 -1
  55. package/docs/assets/style.css +0 -1633
  56. package/docs/classes/HoneyPot.html +0 -31
  57. package/docs/classes/IdleManager.html +0 -162
  58. package/docs/classes/SshClient.html +0 -66
  59. package/docs/classes/VirtualFileSystem.html +0 -279
  60. package/docs/classes/VirtualPackageManager.html +0 -63
  61. package/docs/classes/VirtualSftpServer.html +0 -169
  62. package/docs/classes/VirtualShell.html +0 -285
  63. package/docs/classes/VirtualSshServer.html +0 -182
  64. package/docs/classes/VirtualUserManager.html +0 -276
  65. package/docs/demo.html +0 -82
  66. package/docs/functions/assertDiff.html +0 -6
  67. package/docs/functions/diffSnapshots.html +0 -7
  68. package/docs/functions/formatDiff.html +0 -6
  69. package/docs/functions/getArg.html +0 -13
  70. package/docs/functions/getFlag.html +0 -15
  71. package/docs/functions/ifFlag.html +0 -11
  72. package/docs/hierarchy.html +0 -1
  73. package/docs/index.html +0 -1869
  74. package/docs/interfaces/AuditLogEntry.html +0 -6
  75. package/docs/interfaces/CommandContext.html +0 -22
  76. package/docs/interfaces/CommandResult.html +0 -26
  77. package/docs/interfaces/ExecStream.html +0 -11
  78. package/docs/interfaces/HoneyPotStats.html +0 -16
  79. package/docs/interfaces/IdleManagerOptions.html +0 -7
  80. package/docs/interfaces/InstalledPackage.html +0 -20
  81. package/docs/interfaces/NanoEditorSession.html +0 -8
  82. package/docs/interfaces/PackageDefinition.html +0 -30
  83. package/docs/interfaces/PackageFile.html +0 -8
  84. package/docs/interfaces/PasswordChallenge.html +0 -16
  85. package/docs/interfaces/RemoveOptions.html +0 -4
  86. package/docs/interfaces/ShellEnv.html +0 -6
  87. package/docs/interfaces/ShellModule.html +0 -14
  88. package/docs/interfaces/ShellProperties.html +0 -14
  89. package/docs/interfaces/ShellStream.html +0 -11
  90. package/docs/interfaces/SudoChallenge.html +0 -24
  91. package/docs/interfaces/VfsBaseNode.html +0 -12
  92. package/docs/interfaces/VfsDiff.html +0 -10
  93. package/docs/interfaces/VfsDiffEntry.html +0 -6
  94. package/docs/interfaces/VfsDiffModified.html +0 -10
  95. package/docs/interfaces/VfsDirectoryNode.html +0 -15
  96. package/docs/interfaces/VfsFileNode.html +0 -17
  97. package/docs/interfaces/VfsOptions.html +0 -26
  98. package/docs/interfaces/VfsSnapshot.html +0 -3
  99. package/docs/interfaces/VfsSnapshotBaseNode.html +0 -8
  100. package/docs/interfaces/VfsSnapshotDirectoryNode.html +0 -10
  101. package/docs/interfaces/VfsSnapshotFileNode.html +0 -12
  102. package/docs/interfaces/VirtualActiveSession.html +0 -12
  103. package/docs/interfaces/VirtualSftpServerOptions.html +0 -7
  104. package/docs/interfaces/VirtualShellVfsLike.html +0 -15
  105. package/docs/interfaces/VirtualShellVfsOptions.html +0 -3
  106. package/docs/interfaces/WriteFileOptions.html +0 -6
  107. package/docs/media/LICENSE +0 -21
  108. package/docs/modules.html +0 -1
  109. package/docs/types/ArgParseOptions.html +0 -4
  110. package/docs/types/CommandMode.html +0 -2
  111. package/docs/types/CommandOutcome.html +0 -2
  112. package/docs/types/IdleState.html +0 -1
  113. package/docs/types/VfsNodeStats.html +0 -2
  114. package/docs/types/VfsNodeType.html +0 -2
  115. package/docs/types/VfsPersistenceMode.html +0 -5
  116. package/docs/types/VfsSnapshotNode.html +0 -2
  117. package/examples/README.md +0 -288
  118. package/examples/app.js +0 -1755
  119. package/examples/app.ts +0 -299
  120. package/examples/build.js +0 -27
  121. package/examples/demo.html +0 -33
  122. package/examples/honeypot-audit.ts +0 -180
  123. package/examples/honeypot-export.ts +0 -253
  124. package/examples/honeypot-quickstart.ts +0 -110
  125. package/examples/index.html +0 -82
  126. package/examples/server.js +0 -55
  127. package/polyfills/buffer.js +0 -117
  128. package/polyfills/node_child_process/index.js +0 -2
  129. package/polyfills/node_crypto/index.js +0 -167
  130. package/polyfills/node_events/index.js +0 -9
  131. package/polyfills/node_fs/index.js +0 -202
  132. package/polyfills/node_fs/promises.js +0 -4
  133. package/polyfills/node_os/index.js +0 -9
  134. package/polyfills/node_path/index.js +0 -28
  135. package/polyfills/node_vm/index.js +0 -7
  136. package/polyfills/node_zlib/index.js +0 -3
  137. package/polyfills/process.js +0 -14
  138. package/polyfills/ssh2/index.js +0 -75
  139. package/scripts/build-all.mjs +0 -226
  140. package/scripts/build-names.mjs +0 -43
  141. package/scripts/generate-manuals-bundle.mjs +0 -49
  142. package/scripts/postinstall.js +0 -42
  143. package/scripts/publish-package.sh +0 -70
  144. package/src/Honeypot/index.ts +0 -457
  145. package/src/SSHClient/index.ts +0 -270
  146. package/src/SSHMimic/exec.ts +0 -49
  147. package/src/SSHMimic/executor.ts +0 -251
  148. package/src/SSHMimic/hostKey.ts +0 -21
  149. package/src/SSHMimic/index.ts +0 -337
  150. package/src/SSHMimic/loginBanner.ts +0 -36
  151. package/src/SSHMimic/loginFormat.ts +0 -10
  152. package/src/SSHMimic/prompt.ts +0 -14
  153. package/src/SSHMimic/sftp.ts +0 -883
  154. package/src/VirtualFileSystem/binaryPack.ts +0 -258
  155. package/src/VirtualFileSystem/index.ts +0 -1193
  156. package/src/VirtualFileSystem/internalTypes.ts +0 -43
  157. package/src/VirtualFileSystem/journal.ts +0 -171
  158. package/src/VirtualFileSystem/path.ts +0 -74
  159. package/src/VirtualPackageManager/index.ts +0 -1006
  160. package/src/VirtualShell/idleManager.ts +0 -137
  161. package/src/VirtualShell/index.ts +0 -475
  162. package/src/VirtualShell/shell.ts +0 -700
  163. package/src/VirtualShell/shellParser.ts +0 -285
  164. package/src/VirtualUserManager/index.ts +0 -758
  165. package/src/bun.d.ts +0 -1
  166. package/src/commands/adduser.ts +0 -103
  167. package/src/commands/alias.ts +0 -69
  168. package/src/commands/apt.ts +0 -233
  169. package/src/commands/awk.ts +0 -168
  170. package/src/commands/base64.ts +0 -29
  171. package/src/commands/cat.ts +0 -52
  172. package/src/commands/cd.ts +0 -25
  173. package/src/commands/chmod.ts +0 -85
  174. package/src/commands/clear.ts +0 -15
  175. package/src/commands/command-helpers.ts +0 -286
  176. package/src/commands/cp.ts +0 -83
  177. package/src/commands/curl.ts +0 -147
  178. package/src/commands/cut.ts +0 -36
  179. package/src/commands/date.ts +0 -30
  180. package/src/commands/declare.ts +0 -49
  181. package/src/commands/deluser.ts +0 -98
  182. package/src/commands/df.ts +0 -23
  183. package/src/commands/diff.ts +0 -43
  184. package/src/commands/dpkg.ts +0 -180
  185. package/src/commands/du.ts +0 -56
  186. package/src/commands/echo.ts +0 -58
  187. package/src/commands/env.ts +0 -23
  188. package/src/commands/exit.ts +0 -18
  189. package/src/commands/export.ts +0 -34
  190. package/src/commands/find.ts +0 -68
  191. package/src/commands/free.ts +0 -47
  192. package/src/commands/grep.ts +0 -116
  193. package/src/commands/groups.ts +0 -19
  194. package/src/commands/gzip.ts +0 -88
  195. package/src/commands/head.ts +0 -52
  196. package/src/commands/help.ts +0 -152
  197. package/src/commands/helpers.ts +0 -234
  198. package/src/commands/history.ts +0 -34
  199. package/src/commands/hostname.ts +0 -14
  200. package/src/commands/htop.ts +0 -20
  201. package/src/commands/id.ts +0 -19
  202. package/src/commands/index.ts +0 -9
  203. package/src/commands/kill.ts +0 -19
  204. package/src/commands/ln.ts +0 -71
  205. package/src/commands/ls.ts +0 -243
  206. package/src/commands/lsb-release.ts +0 -63
  207. package/src/commands/man.ts +0 -31
  208. package/src/commands/manuals/adduser.txt +0 -11
  209. package/src/commands/manuals/apt-cache.txt +0 -12
  210. package/src/commands/manuals/apt.txt +0 -20
  211. package/src/commands/manuals/awk.txt +0 -13
  212. package/src/commands/manuals/cat.txt +0 -14
  213. package/src/commands/manuals/cd.txt +0 -16
  214. package/src/commands/manuals/chmod.txt +0 -16
  215. package/src/commands/manuals/clear.txt +0 -10
  216. package/src/commands/manuals/cp.txt +0 -10
  217. package/src/commands/manuals/curl.txt +0 -20
  218. package/src/commands/manuals/date.txt +0 -14
  219. package/src/commands/manuals/declare.txt +0 -12
  220. package/src/commands/manuals/deluser.txt +0 -10
  221. package/src/commands/manuals/df.txt +0 -10
  222. package/src/commands/manuals/dpkg-query.txt +0 -11
  223. package/src/commands/manuals/dpkg.txt +0 -14
  224. package/src/commands/manuals/du.txt +0 -11
  225. package/src/commands/manuals/echo.txt +0 -11
  226. package/src/commands/manuals/false.txt +0 -10
  227. package/src/commands/manuals/find.txt +0 -11
  228. package/src/commands/manuals/free.txt +0 -12
  229. package/src/commands/manuals/grep.txt +0 -13
  230. package/src/commands/manuals/groups.txt +0 -10
  231. package/src/commands/manuals/gzip.txt +0 -11
  232. package/src/commands/manuals/head.txt +0 -10
  233. package/src/commands/manuals/help.txt +0 -11
  234. package/src/commands/manuals/history.txt +0 -11
  235. package/src/commands/manuals/hostname.txt +0 -10
  236. package/src/commands/manuals/id.txt +0 -10
  237. package/src/commands/manuals/kill.txt +0 -13
  238. package/src/commands/manuals/ls.txt +0 -20
  239. package/src/commands/manuals/lsb_release.txt +0 -14
  240. package/src/commands/manuals/mkdir.txt +0 -10
  241. package/src/commands/manuals/mv.txt +0 -10
  242. package/src/commands/manuals/nano.txt +0 -11
  243. package/src/commands/manuals/neofetch.txt +0 -10
  244. package/src/commands/manuals/node.txt +0 -13
  245. package/src/commands/manuals/npm.txt +0 -13
  246. package/src/commands/manuals/npx.txt +0 -13
  247. package/src/commands/manuals/passwd.txt +0 -11
  248. package/src/commands/manuals/ping.txt +0 -10
  249. package/src/commands/manuals/printf.txt +0 -11
  250. package/src/commands/manuals/ps.txt +0 -10
  251. package/src/commands/manuals/pwd.txt +0 -10
  252. package/src/commands/manuals/python3.txt +0 -13
  253. package/src/commands/manuals/readlink.txt +0 -10
  254. package/src/commands/manuals/return.txt +0 -10
  255. package/src/commands/manuals/rm.txt +0 -10
  256. package/src/commands/manuals/sed.txt +0 -11
  257. package/src/commands/manuals/set.txt +0 -11
  258. package/src/commands/manuals/shift.txt +0 -10
  259. package/src/commands/manuals/sleep.txt +0 -10
  260. package/src/commands/manuals/sort.txt +0 -12
  261. package/src/commands/manuals/source.txt +0 -11
  262. package/src/commands/manuals/ssh.txt +0 -11
  263. package/src/commands/manuals/stat.txt +0 -10
  264. package/src/commands/manuals/su.txt +0 -13
  265. package/src/commands/manuals/sudo.txt +0 -11
  266. package/src/commands/manuals/tail.txt +0 -10
  267. package/src/commands/manuals/tar.txt +0 -19
  268. package/src/commands/manuals/tee.txt +0 -10
  269. package/src/commands/manuals/test.txt +0 -11
  270. package/src/commands/manuals/touch.txt +0 -11
  271. package/src/commands/manuals/tr.txt +0 -10
  272. package/src/commands/manuals/trap.txt +0 -10
  273. package/src/commands/manuals/true.txt +0 -10
  274. package/src/commands/manuals/type.txt +0 -10
  275. package/src/commands/manuals/uname.txt +0 -12
  276. package/src/commands/manuals/uniq.txt +0 -12
  277. package/src/commands/manuals/unset.txt +0 -10
  278. package/src/commands/manuals/uptime.txt +0 -11
  279. package/src/commands/manuals/wc.txt +0 -12
  280. package/src/commands/manuals/wget.txt +0 -12
  281. package/src/commands/manuals/which.txt +0 -10
  282. package/src/commands/manuals/whoami.txt +0 -10
  283. package/src/commands/manuals/xargs.txt +0 -10
  284. package/src/commands/manuals-bundle.ts +0 -898
  285. package/src/commands/mkdir.ts +0 -31
  286. package/src/commands/mv.ts +0 -50
  287. package/src/commands/nano.ts +0 -38
  288. package/src/commands/neofetch.ts +0 -53
  289. package/src/commands/node.ts +0 -341
  290. package/src/commands/npm.ts +0 -132
  291. package/src/commands/passwd.ts +0 -50
  292. package/src/commands/ping.ts +0 -32
  293. package/src/commands/printf.ts +0 -129
  294. package/src/commands/ps.ts +0 -58
  295. package/src/commands/pwd.ts +0 -9
  296. package/src/commands/python.ts +0 -2229
  297. package/src/commands/read.ts +0 -46
  298. package/src/commands/registry.ts +0 -249
  299. package/src/commands/rm.ts +0 -42
  300. package/src/commands/runtime.ts +0 -378
  301. package/src/commands/sed.ts +0 -68
  302. package/src/commands/seq.ts +0 -43
  303. package/src/commands/set.ts +0 -29
  304. package/src/commands/sh.ts +0 -467
  305. package/src/commands/shift.ts +0 -63
  306. package/src/commands/sleep.ts +0 -20
  307. package/src/commands/sort.ts +0 -46
  308. package/src/commands/source.ts +0 -52
  309. package/src/commands/stat.ts +0 -61
  310. package/src/commands/su.ts +0 -72
  311. package/src/commands/sudo.ts +0 -76
  312. package/src/commands/tail.ts +0 -53
  313. package/src/commands/tar.ts +0 -102
  314. package/src/commands/tee.ts +0 -36
  315. package/src/commands/test.ts +0 -137
  316. package/src/commands/touch.ts +0 -28
  317. package/src/commands/tr.ts +0 -70
  318. package/src/commands/tree.ts +0 -20
  319. package/src/commands/true.ts +0 -27
  320. package/src/commands/type.ts +0 -48
  321. package/src/commands/uname.ts +0 -29
  322. package/src/commands/uniq.ts +0 -39
  323. package/src/commands/unset.ts +0 -17
  324. package/src/commands/uptime.ts +0 -54
  325. package/src/commands/wc.ts +0 -55
  326. package/src/commands/wget.ts +0 -148
  327. package/src/commands/which.ts +0 -37
  328. package/src/commands/who.ts +0 -25
  329. package/src/commands/whoami.ts +0 -14
  330. package/src/commands/xargs.ts +0 -31
  331. package/src/index.ts +0 -67
  332. package/src/modules/linuxRootfs.ts +0 -1961
  333. package/src/modules/neofetch.ts +0 -358
  334. package/src/modules/shellInteractive.ts +0 -57
  335. package/src/modules/shellRuntime.ts +0 -76
  336. package/src/self-standalone.ts +0 -542
  337. package/src/standalone-wo-sftp.ts +0 -38
  338. package/src/standalone.ts +0 -72
  339. package/src/types/commands.ts +0 -146
  340. package/src/types/pipeline.ts +0 -52
  341. package/src/types/streams.ts +0 -32
  342. package/src/types/tar-stream.d.ts +0 -38
  343. package/src/types/vfs.ts +0 -98
  344. package/src/utils/expand.ts +0 -491
  345. package/src/utils/perfLogger.ts +0 -72
  346. package/src/utils/tokenize.ts +0 -98
  347. package/src/utils/vfsDiff.ts +0 -275
  348. package/tests/command-helpers.test.ts +0 -116
  349. package/tests/commands-admin-net.test.ts +0 -441
  350. package/tests/commands-advanced.test.ts +0 -456
  351. package/tests/commands-core.test.ts +0 -562
  352. package/tests/commands-missing.test.ts +0 -570
  353. package/tests/commands-specific-units.test.ts +0 -327
  354. package/tests/commands-text-sys.test.ts +0 -445
  355. package/tests/expand.test.ts +0 -170
  356. package/tests/helpers.test.ts +0 -97
  357. package/tests/new-features.test.ts +0 -1036
  358. package/tests/parser-executor.test.ts +0 -37
  359. package/tests/sftp.test.ts +0 -323
  360. package/tests/ssh-exec.test.ts +0 -45
  361. package/tests/test-helper.ts +0 -79
  362. package/tests/users.test.ts +0 -86
  363. package/tsconfig.json +0 -49
  364. package/typedoc.json +0 -47
@@ -1,137 +0,0 @@
1
- /**
2
- * IdleManager — cold-start / freeze-thaw system for VirtualShell.
3
- *
4
- * When a shell has had no activity for `idleThresholdMs`, the VFS tree is
5
- * serialised to a compact binary buffer and the in-memory tree is released.
6
- * On the next command the tree is reconstructed in ~0.1 ms.
7
- *
8
- * Memory freed per idle shell: the full JS object graph (~250+ InternalNodes
9
- * for a typical rootfs + user files). The frozen buffer itself stays in RAM
10
- * but is ~27% smaller than the live tree and GC-friendly (one flat Buffer).
11
- *
12
- * CPU freed: the VFS auto-flush setInterval is suspended while frozen.
13
- *
14
- * Usage:
15
- * ```ts
16
- * const idle = new IdleManager(shell, { idleThresholdMs: 60_000 });
17
- * idle.start();
18
- * // call idle.ping() on every shell activity (exec, keypress, …)
19
- * // call idle.stop() on shell destroy
20
- * ```
21
- */
22
-
23
- import { EventEmitter } from "node:events";
24
- import type VirtualFileSystem from "../VirtualFileSystem";
25
- import { decodeVfs } from "../VirtualFileSystem/binaryPack";
26
-
27
- export interface IdleManagerOptions {
28
- /**
29
- * Milliseconds of inactivity before the shell is frozen.
30
- * Default: 60_000 (1 minute).
31
- */
32
- idleThresholdMs?: number;
33
- /**
34
- * How often the idle check runs.
35
- * Default: 15_000 (15 seconds).
36
- */
37
- checkIntervalMs?: number;
38
- }
39
-
40
- export type IdleState = "active" | "frozen";
41
-
42
- export class IdleManager extends EventEmitter {
43
- private readonly vfs: VirtualFileSystem;
44
- private readonly idleThresholdMs: number;
45
- private readonly checkIntervalMs: number;
46
-
47
- private _state: IdleState = "active";
48
- private _lastActivity = Date.now();
49
- private _frozenBuffer: Buffer | null = null;
50
- private _checkTimer: ReturnType<typeof setInterval> | null = null;
51
-
52
- /** Emitted when the shell is frozen (VFS tree released). */
53
- declare on: ((event: "freeze", listener: () => void) => this) &
54
- ((event: "thaw", listener: () => void) => this) &
55
- ((event: string, listener: (...args: unknown[]) => void) => this);
56
-
57
- constructor(vfs: VirtualFileSystem, options: IdleManagerOptions = {}) {
58
- super();
59
- this.vfs = vfs;
60
- this.idleThresholdMs = options.idleThresholdMs ?? 60_000;
61
- this.checkIntervalMs = options.checkIntervalMs ?? 15_000;
62
- }
63
-
64
- /** Start monitoring for idle. Call once after shell initialisation. */
65
- public start(): void {
66
- if (this._checkTimer) return;
67
- this._lastActivity = Date.now();
68
- this._checkTimer = setInterval(() => this._check(), this.checkIntervalMs);
69
- // Don't block process exit
70
- if (typeof this._checkTimer === "object" && this._checkTimer !== null && "unref" in this._checkTimer) {
71
- (this._checkTimer as NodeJS.Timeout).unref();
72
- }
73
- }
74
-
75
- /** Stop monitoring and thaw if frozen. Call on shell destroy. */
76
- public async stop(): Promise<void> {
77
- if (this._checkTimer) {
78
- clearInterval(this._checkTimer);
79
- this._checkTimer = null;
80
- }
81
- if (this._state === "frozen") this._thaw();
82
- }
83
-
84
- /**
85
- * Signal activity — resets the idle clock and thaws synchronously if frozen.
86
- * Call this before every exec / keypress / session event.
87
- *
88
- * Thaw is intentionally synchronous: decodeVfs() is a pure CPU operation
89
- * (~0.07 ms) with no I/O, so it is safe to block briefly on the hot path.
90
- * This guarantees the VFS tree is fully restored before any command runs.
91
- */
92
- public ping(): void {
93
- this._lastActivity = Date.now();
94
- if (this._state === "frozen") this._thaw();
95
- }
96
-
97
- /** Current idle state. */
98
- public get state(): IdleState {
99
- return this._state;
100
- }
101
-
102
- /** Ms since last activity. */
103
- public get idleMs(): number {
104
- return Date.now() - this._lastActivity;
105
- }
106
-
107
- // ── Internal ──────────────────────────────────────────────────────────────
108
-
109
- private _check(): void {
110
- if (this._state === "frozen") return; // already frozen
111
- if (Date.now() - this._lastActivity >= this.idleThresholdMs) {
112
- void this._freeze();
113
- }
114
- }
115
-
116
- private async _freeze(): Promise<void> {
117
- if (this._state === "frozen") return;
118
- // Flush any pending writes before freezing
119
- await this.vfs.stopAutoFlush();
120
- // Serialise the live tree to a compact binary buffer
121
- this._frozenBuffer = this.vfs.encodeBinary();
122
- // Release the live tree — GC can now collect all InternalNode objects
123
- this.vfs.releaseTree();
124
- this._state = "frozen";
125
- this.emit("freeze");
126
- }
127
-
128
- private _thaw(): void {
129
- if (this._state !== "frozen" || !this._frozenBuffer) return;
130
- // Reconstruct the tree from the frozen buffer (~0.07 ms — pure CPU, no I/O)
131
- const root = decodeVfs(this._frozenBuffer);
132
- this.vfs.importRootTree(root);
133
- this._frozenBuffer = null;
134
- this._state = "active";
135
- this.emit("thaw");
136
- }
137
- }
@@ -1,475 +0,0 @@
1
- import { EventEmitter } from "node:events";
2
- import { createCustomCommand, registerCommand } from "../commands/registry";
3
- import { runCommand } from "../commands/runtime";
4
- import {
5
- bootstrapLinuxRootfs,
6
- refreshProc,
7
- syncEtcPasswd,
8
- } from "../modules/linuxRootfs";
9
- import type { CommandContext, CommandResult } from "../types/commands";
10
- import type { ShellStream } from "../types/streams";
11
- import type { VfsNodeStats } from "../types/vfs";
12
- import type { PerfLogger } from "../utils/perfLogger";
13
- import { createPerfLogger } from "../utils/perfLogger";
14
- import VirtualFileSystem, { type VfsOptions } from "../VirtualFileSystem";
15
- import { VirtualPackageManager } from "../VirtualPackageManager";
16
- import { VirtualUserManager } from "../VirtualUserManager";
17
- import { IdleManager, type IdleManagerOptions } from "./idleManager";
18
- import { startShell } from "./shell";
19
-
20
- /**
21
- * Virtual machine identity strings surfaced by system-info commands
22
- * (`uname`, `neofetch`, `lsb_release`, `/proc/version`, `/etc/os-release`).
23
- *
24
- * Pass this as the second argument to `new VirtualShell()` to customise the
25
- * distro name, kernel version, and CPU architecture reported inside the shell.
26
- *
27
- * @example
28
- * ```ts
29
- * const shell = new VirtualShell("my-vm", {
30
- * kernel: "6.1.0+custom-amd64",
31
- * os: "Acme GNU/Linux x64",
32
- * arch: "x86_64",
33
- * });
34
- * ```
35
- */
36
- export interface ShellProperties {
37
- /** Kernel version string (e.g. `"1.0.0+itsrealfortune+1-amd64"`). */
38
- kernel: string;
39
- /** Full OS description (e.g. `"Fortune GNU/Linux x64"`). */
40
- os: string;
41
- /** CPU architecture label (e.g. `"x86_64"`, `"aarch64"`). */
42
- arch: string;
43
- }
44
-
45
- /**
46
- * Minimal VFS interface accepted by {@link VirtualShell} as a drop-in replacement
47
- * for the built-in {@link VirtualFileSystem}.
48
- */
49
- export interface VirtualShellVfsLike {
50
- restoreMirror(): Promise<void>;
51
- flushMirror(): Promise<void>;
52
- writeFile(targetPath: string, content: string | Uint8Array): void;
53
- readFile(targetPath: string): string;
54
- mkdir(targetPath: string, mode?: number): void;
55
- exists(targetPath: string): boolean;
56
- stat(targetPath: string): VfsNodeStats;
57
- list(targetPath: string): string[];
58
- remove(targetPath: string, options?: { recursive?: boolean }): void;
59
- chmod?(targetPath: string, mode: number): void;
60
- symlink?(targetPath: string, linkPath: string): void;
61
- getUsageBytes?(targetPath?: string): number;
62
- }
63
-
64
- /**
65
- * Constructor options for {@link VirtualShell} when passing an existing VFS instance.
66
- */
67
- export interface VirtualShellVfsOptions {
68
- vfsInstance?: VirtualShellVfsLike;
69
- }
70
-
71
- function hasVfsInstance(obj: unknown): obj is { vfsInstance: VirtualShellVfsLike } {
72
- return (
73
- typeof obj === "object" &&
74
- obj !== null &&
75
- "vfsInstance" in obj &&
76
- isVirtualShellVfsLike((obj as Record<string, unknown>).vfsInstance)
77
- );
78
- }
79
-
80
- function isVirtualShellVfsLike(value: unknown): value is VirtualShellVfsLike {
81
- if (typeof value !== "object" || value === null) {
82
- return false;
83
- }
84
-
85
- const candidate = value as Record<string, unknown>;
86
- return (
87
- typeof candidate.restoreMirror === "function" &&
88
- typeof candidate.flushMirror === "function" &&
89
- typeof candidate.writeFile === "function" &&
90
- typeof candidate.readFile === "function" &&
91
- typeof candidate.mkdir === "function" &&
92
- typeof candidate.exists === "function" &&
93
- typeof candidate.stat === "function" &&
94
- typeof candidate.list === "function" &&
95
- typeof candidate.remove === "function" &&
96
- typeof candidate.copy === "function" &&
97
- typeof candidate.move === "function" &&
98
- typeof candidate.touch === "function"
99
- );
100
- }
101
-
102
- const defaultShellProperties: ShellProperties = {
103
- kernel: "1.0.0+itsrealfortune+1-amd64",
104
- os: "Fortune GNU/Linux x64",
105
- arch: "x86_64",
106
- };
107
-
108
- const perf: PerfLogger = createPerfLogger("VirtualShell");
109
-
110
- function resolveAutoSudoForNewUsers(): boolean {
111
- const configured = process.env.SSH_MIMIC_AUTO_SUDO_NEW_USERS;
112
- if (!configured) {
113
- return true;
114
- }
115
-
116
- return !["0", "false", "no", "off"].includes(configured.toLowerCase());
117
- }
118
-
119
- /**
120
- * Coordinates the virtual filesystem, user manager, package manager, and
121
- * command runtime for a single isolated shell environment.
122
- *
123
- * Each instance owns its own VFS tree, user database, package registry, and
124
- * session state — multiple instances are fully independent.
125
- *
126
- * Instances are consumed both by the SSH/SFTP server facades and directly via
127
- * the programmatic `SshClient` API.
128
- *
129
- * @example
130
- * ```ts
131
- * const shell = new VirtualShell("my-vm");
132
- * await shell.ensureInitialized();
133
- * const client = new SshClient(shell, "root");
134
- * const result = await client.exec("uname -a");
135
- * ```
136
- *
137
- * @fires VirtualShell#initialized Emitted once the VFS and users are ready.
138
- * @fires VirtualShell#command Emitted after every command execution.
139
- * @fires VirtualShell#session:start Emitted when an interactive session opens.
140
- */
141
- class VirtualShell extends EventEmitter {
142
- /** Backing virtual filesystem — use for direct path operations. */
143
- vfs: VirtualFileSystem;
144
- /** Virtual user database — use for auth, quotas, and session tracking. */
145
- users: VirtualUserManager;
146
- /** APT/dpkg package manager backed by the built-in package registry. */
147
- packageManager: VirtualPackageManager;
148
- /** Hostname shown in the shell prompt and SSH ident string. */
149
- hostname: string;
150
- /** Distro identity strings surfaced by `uname`, `neofetch`, etc. */
151
- properties: ShellProperties;
152
- /** Unix ms timestamp of shell creation — used by `uptime` and `/proc/uptime`. */
153
- startTime: number;
154
- /** Idle / cold-start manager — null until `enableIdleManagement()` is called. */
155
- private _idle: IdleManager | null = null;
156
- private initialized: Promise<void>;
157
-
158
- /**
159
- * Creates a new virtual shell instance.
160
- *
161
- * @param hostname Virtual hostname used for prompts and idents.
162
- * @param properties Customizable properties shown in `uname -a` and similar commands.
163
- * @param vfsOptionsOrInstance Optional VFS persistence options (mode, snapshotPath) or an existing VFS instance.
164
- */
165
- constructor(
166
- hostname: string,
167
- properties?: ShellProperties,
168
- vfsOptionsOrInstance?: VfsOptions | VirtualShellVfsLike | VirtualShellVfsOptions,
169
- ) {
170
- super();
171
- perf.mark("constructor");
172
- this.hostname = hostname;
173
- this.properties = properties || defaultShellProperties;
174
- this.startTime = Date.now();
175
-
176
- if (isVirtualShellVfsLike(vfsOptionsOrInstance)) {
177
- this.vfs = vfsOptionsOrInstance as unknown as VirtualFileSystem;
178
- } else if (hasVfsInstance(vfsOptionsOrInstance)) {
179
- this.vfs = vfsOptionsOrInstance.vfsInstance as unknown as VirtualFileSystem;
180
- } else {
181
- this.vfs = new VirtualFileSystem((vfsOptionsOrInstance as VfsOptions) ?? {});
182
- }
183
- this.users = new VirtualUserManager(this.vfs, resolveAutoSudoForNewUsers());
184
- this.packageManager = new VirtualPackageManager(this.vfs, this.users);
185
-
186
- // Store references to avoid TypeScript "used before assigned" errors
187
- const vfs = this.vfs;
188
- const users = this.users;
189
- const pm = this.packageManager;
190
- const shellProps = this.properties;
191
- const shellHostname = this.hostname;
192
- const startTime = this.startTime;
193
-
194
- // Initialize both VFS mirror and users, ensuring all is ready before auth
195
- this.initialized = (async () => {
196
- await vfs.restoreMirror();
197
- await users.initialize();
198
- // Bootstrap Linux rootfs (idempotent)
199
- bootstrapLinuxRootfs(vfs, users, shellHostname, shellProps, startTime);
200
- // Load installed packages from dpkg status
201
- pm.load();
202
- this.emit("initialized");
203
- })();
204
- }
205
-
206
- /**
207
- * Ensures initialization is complete before allowing operations.
208
- * Call this before any authentication or command execution.
209
- */
210
- public async ensureInitialized(): Promise<void> {
211
- perf.mark("ensureInitialized");
212
- await this.initialized;
213
- }
214
-
215
- /**
216
- * Registers a new command in the shell runtime.
217
- *
218
- * @param name Case-insensitive command name (no spaces).
219
- * @param params List of parameter names for help text (no validation).
220
- * @param callback Function invoked with command context on execution.
221
- */
222
- addCommand(
223
- name: string,
224
- params: string[],
225
- callback: (ctx: CommandContext) => CommandResult | Promise<CommandResult>,
226
- ): void {
227
- const normalized = name.trim().toLowerCase();
228
- if (normalized.length === 0 || /\s/.test(normalized)) {
229
- throw new Error("Command name must be non-empty and contain no spaces");
230
- }
231
-
232
- registerCommand(createCustomCommand(normalized, params, callback));
233
- }
234
-
235
- /**
236
- * Executes a raw command line string programmatically.
237
- *
238
- * Supports the full shell operator set (`&&`, `||`, `;`, `|`, `>`, `<`,
239
- * `$(cmd)`) and alias expansion. The result is emitted via the
240
- * `"command"` event but not returned — use `SshClient.exec()` for a
241
- * result-returning wrapper.
242
- *
243
- * @param rawInput Unparsed command line (e.g. `"ls -la /tmp"`).
244
- * @param authUser Username to run the command as.
245
- * @param cwd Current working directory for path resolution.
246
- *
247
- * @returns CommandResult or a Promise that resolves to CommandResult. The result is also emitted via the "command" event.
248
- */
249
- executeCommand(rawInput: string, authUser: string, cwd: string): CommandResult | Promise<CommandResult> {
250
- perf.mark("executeCommand");
251
- this._idle?.ping();
252
- const result = runCommand(rawInput, authUser, this.hostname, "shell", cwd, this);
253
- this.emit("command", { command: rawInput, user: authUser, cwd });
254
- return result;
255
- }
256
-
257
- /**
258
- * Attaches an interactive PTY session to this shell instance.
259
- *
260
- * Called internally by `SshMimic` when a client opens a shell channel.
261
- * The session reads from `stream` (user keystrokes) and writes back ANSI
262
- * output. History, `.bashrc` sourcing, and Ctrl+W/Ctrl+U line editing are
263
- * handled automatically.
264
- *
265
- * @param stream Bidirectional SSH channel stream.
266
- * @param authUser Authenticated username bound to this session.
267
- * @param sessionId Stable session UUID (used for `who` output), or `null`.
268
- * @param remoteAddress IP or hostname of the connecting client.
269
- * @param terminalSize Initial terminal dimensions in columns and rows.
270
- */
271
- startInteractiveSession(
272
- stream: ShellStream,
273
- authUser: string,
274
- sessionId: string | null,
275
- remoteAddress: string,
276
- terminalSize: { cols: number; rows: number },
277
- ): void {
278
- perf.mark("startInteractiveSession");
279
- this._idle?.ping();
280
- // Interactive shell logic
281
- this.emit("session:start", { user: authUser, sessionId, remoteAddress });
282
- startShell(
283
- this.properties,
284
- stream,
285
- authUser,
286
- this.hostname,
287
- sessionId,
288
- remoteAddress,
289
- terminalSize,
290
- this,
291
- );
292
- // Refresh /proc/<pid> and /proc/self after session is registered
293
- this.refreshProcSessions();
294
- }
295
-
296
- /**
297
- * Refreshes the `/proc` virtual filesystem with current system state.
298
- *
299
- * Updates `/proc/uptime`, `/proc/meminfo`, `/proc/cpuinfo`,
300
- * `/proc/version`, `/proc/loadavg`, `/proc/self`, and per-session
301
- * `/proc/<pid>` entries from live session and host data.
302
- *
303
- * Called automatically during `bootstrapLinuxRootfs`. Call again before
304
- * reading `/proc` files for up-to-date values.
305
- */
306
- public refreshProcFs(): void {
307
- refreshProc(
308
- this.vfs,
309
- this.properties,
310
- this.hostname,
311
- this.startTime,
312
- this.users.listActiveSessions(),
313
- );
314
- }
315
-
316
- /**
317
- * Mount a host directory into the VFS at `vPath`.
318
- *
319
- * Delegates file operations inside `vPath` to the host filesystem via
320
- * `node:fs`. Silently ignored in browser environments.
321
- *
322
- * @param vPath Absolute path inside the VM (e.g. `"/app"`).
323
- * @param hostPath Path on the host — relative paths are resolved from `process.cwd()`.
324
- * @param options `{ readOnly?: boolean }` — default `true`.
325
- *
326
- * @example
327
- * ```ts
328
- * const shell = new VirtualShell("dev-vm");
329
- * await shell.ensureInitialized();
330
- * shell.mount("/workspace", "./my-project");
331
- * // shell commands can now read ./my-project files via /workspace
332
- * ```
333
- */
334
- public mount(
335
- vPath: string,
336
- hostPath: string,
337
- options: { readOnly?: boolean } = {},
338
- ): void {
339
- this.vfs.mount(vPath, hostPath, options);
340
- }
341
-
342
- /** Remove a previously mounted host directory. */
343
- public unmount(vPath: string): void {
344
- this.vfs.unmount(vPath);
345
- }
346
-
347
- /** List all active mounts. */
348
- public getMounts(): Array<{ vPath: string; hostPath: string; readOnly: boolean }> {
349
- return this.vfs.getMounts();
350
- }
351
-
352
- /**
353
- * Updates only the session-dependent `/proc` entries (`/proc/<pid>`,
354
- * `/proc/self`). Cheaper than a full `refreshProcFs()` — call this
355
- * whenever a session is registered or unregistered.
356
- */
357
- public refreshProcSessions(): void {
358
- refreshProc(
359
- this.vfs,
360
- this.properties,
361
- this.hostname,
362
- this.startTime,
363
- this.users.listActiveSessions(),
364
- );
365
- }
366
-
367
- /**
368
- * Syncs `/etc/passwd`, `/etc/group`, and `/etc/shadow` from the current
369
- * `VirtualUserManager` state.
370
- *
371
- * Called automatically during `bootstrapLinuxRootfs`. Call again after
372
- * `users.addUser()`, `users.deleteUser()`, or `users.addSudoer()` to keep
373
- * the classic Unix credential files in sync with the user manager.
374
- */
375
- public syncPasswd(): void {
376
- syncEtcPasswd(this.vfs, this.users);
377
- }
378
-
379
- /**
380
- * Returns virtual filesystem instance after server started.
381
- *
382
- * @returns VirtualFileSystem or null when not started.
383
- */
384
- public getVfs(): VirtualFileSystem | null {
385
- return this?.vfs ?? null;
386
- }
387
-
388
- /**
389
- * Returns user manager instance after server started.
390
- *
391
- * @returns VirtualUserManager or null when not started.
392
- */
393
- public getUsers(): VirtualUserManager | null {
394
- return this?.users ?? null;
395
- }
396
-
397
- /**
398
- * Returns hostname shown in prompts and idents.
399
- *
400
- * @returns Configured hostname label.
401
- */
402
- public getHostname(): string {
403
- return this?.hostname;
404
- }
405
-
406
- /**
407
- * Writes a file on behalf of a user with quota enforcement.
408
- *
409
- * @param authUser User performing the write.
410
- * @param targetPath Destination path.
411
- * @param content File content.
412
- */
413
- public writeFileAsUser(
414
- authUser: string,
415
- targetPath: string,
416
- content: string | Buffer,
417
- ): void {
418
- perf.mark("writeFileAsUser");
419
- this.users.assertWriteWithinQuota(authUser, targetPath, content);
420
- this.vfs.writeFile(targetPath, content);
421
- }
422
-
423
- /**
424
- * Enable idle detection and cold-start freeze/thaw for this shell.
425
- *
426
- * After `idleThresholdMs` of inactivity the VFS tree is serialised and
427
- * released from RAM. The next command transparently restores it in ~0.1 ms.
428
- *
429
- * @example
430
- * ```ts
431
- * await shell.ensureInitialized();
432
- * shell.enableIdleManagement({ idleThresholdMs: 60_000 });
433
- * ```
434
- */
435
- public enableIdleManagement(options?: IdleManagerOptions): void {
436
- if (this._idle) return; // already enabled
437
- this._idle = new IdleManager(this.vfs, options);
438
- this._idle.on("freeze", () => this.emit("shell:freeze"));
439
- this._idle.on("thaw", () => this.emit("shell:thaw"));
440
- this._idle.start();
441
- }
442
-
443
- /**
444
- * Disable idle management and thaw the shell if currently frozen.
445
- * Safe to call even if idle management was never enabled.
446
- */
447
- public async disableIdleManagement(): Promise<void> {
448
- if (!this._idle) return;
449
- await this._idle.stop();
450
- this._idle = null;
451
- }
452
-
453
- /**
454
- * Current idle state — `"active"` or `"frozen"`.
455
- * Returns `"active"` when idle management is disabled.
456
- */
457
- public get idleState(): "active" | "frozen" {
458
- return this._idle?.state ?? "active";
459
- }
460
-
461
- /** Milliseconds since last shell activity. 0 when idle management is disabled. */
462
- public get idleMs(): number {
463
- return this._idle?.idleMs ?? 0;
464
- }
465
-
466
- /**
467
- * Ping the idle manager to signal external activity (e.g. SFTP, direct VFS writes).
468
- * No-op when idle management is disabled.
469
- */
470
- public pingIdle(): void {
471
- this._idle?.ping();
472
- }
473
- }
474
-
475
- export { VirtualShell };