typescript-virtual-container 1.5.3 → 1.5.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 (371) hide show
  1. package/README.md +44 -532
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/SSHMimic/executor.js +23 -5
  4. package/dist/VirtualPackageManager/index.js +10 -0
  5. package/dist/VirtualShell/shell.js +158 -11
  6. package/dist/commands/basename.d.ts +13 -0
  7. package/dist/commands/basename.js +45 -0
  8. package/dist/commands/bc.d.ts +2 -0
  9. package/dist/commands/bc.js +28 -0
  10. package/dist/commands/file.d.ts +8 -0
  11. package/dist/commands/file.js +57 -0
  12. package/dist/commands/fun.d.ts +32 -0
  13. package/dist/commands/fun.js +172 -0
  14. package/dist/commands/ip.d.ts +7 -0
  15. package/dist/commands/ip.js +52 -0
  16. package/dist/commands/jobs.d.ts +4 -0
  17. package/dist/commands/jobs.js +27 -0
  18. package/dist/commands/last.d.ts +13 -0
  19. package/dist/commands/last.js +68 -0
  20. package/dist/commands/manuals-bundle.js +598 -6
  21. package/dist/commands/registry.js +30 -2
  22. package/dist/commands/runtime.js +24 -3
  23. package/dist/commands/set.js +20 -0
  24. package/dist/commands/sh.js +74 -1
  25. package/dist/commands/tput.d.ts +13 -0
  26. package/dist/commands/tput.js +76 -0
  27. package/dist/commands/w.d.ts +7 -0
  28. package/dist/commands/w.js +38 -0
  29. package/dist/utils/expand.d.ts +12 -0
  30. package/dist/utils/expand.js +87 -1
  31. package/package.json +9 -3
  32. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -50
  33. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -31
  34. package/.github/dependabot.yml +0 -27
  35. package/.github/pull_request_template.md +0 -21
  36. package/.github/workflows/create-pull-request.yml +0 -85
  37. package/.github/workflows/publish.yml +0 -25
  38. package/.github/workflows/test-battery.yml +0 -102
  39. package/.vscode/settings.json +0 -20
  40. package/CODE_OF_CONDUCT.md +0 -39
  41. package/CONTRIBUTING.md +0 -59
  42. package/HONEYPOT.md +0 -358
  43. package/SECURITY.md +0 -33
  44. package/benchmark-results.txt +0 -40
  45. package/benchmark-virtualshell.ts +0 -88
  46. package/biome.json +0 -37
  47. package/build.js +0 -22
  48. package/builds/fortune-nyx-v1.5.3-directbash-k6.1.0.mjs +0 -1764
  49. package/builds/fortune-nyx-v1.5.3-ssh-nosftp.js +0 -1764
  50. package/builds/fortune-nyx-v1.5.3-ssh.cjs +0 -1765
  51. package/builds/fortune-nyx-v1.5.3-web.min.js +0 -17036
  52. package/bun.lock +0 -244
  53. package/docs/.nojekyll +0 -1
  54. package/docs/app.js +0 -1751
  55. package/docs/assets/hierarchy.js +0 -1
  56. package/docs/assets/highlight.css +0 -162
  57. package/docs/assets/icons.js +0 -18
  58. package/docs/assets/icons.svg +0 -1
  59. package/docs/assets/main.js +0 -60
  60. package/docs/assets/navigation.js +0 -1
  61. package/docs/assets/search.js +0 -1
  62. package/docs/assets/style.css +0 -1633
  63. package/docs/classes/HoneyPot.html +0 -31
  64. package/docs/classes/IdleManager.html +0 -162
  65. package/docs/classes/SshClient.html +0 -66
  66. package/docs/classes/VirtualFileSystem.html +0 -279
  67. package/docs/classes/VirtualPackageManager.html +0 -63
  68. package/docs/classes/VirtualSftpServer.html +0 -169
  69. package/docs/classes/VirtualShell.html +0 -285
  70. package/docs/classes/VirtualSshServer.html +0 -182
  71. package/docs/classes/VirtualUserManager.html +0 -276
  72. package/docs/demo.html +0 -82
  73. package/docs/functions/assertDiff.html +0 -6
  74. package/docs/functions/diffSnapshots.html +0 -7
  75. package/docs/functions/formatDiff.html +0 -6
  76. package/docs/functions/getArg.html +0 -13
  77. package/docs/functions/getFlag.html +0 -15
  78. package/docs/functions/ifFlag.html +0 -11
  79. package/docs/hierarchy.html +0 -1
  80. package/docs/index.html +0 -1869
  81. package/docs/interfaces/AuditLogEntry.html +0 -6
  82. package/docs/interfaces/CommandContext.html +0 -22
  83. package/docs/interfaces/CommandResult.html +0 -26
  84. package/docs/interfaces/ExecStream.html +0 -11
  85. package/docs/interfaces/HoneyPotStats.html +0 -16
  86. package/docs/interfaces/IdleManagerOptions.html +0 -7
  87. package/docs/interfaces/InstalledPackage.html +0 -20
  88. package/docs/interfaces/NanoEditorSession.html +0 -8
  89. package/docs/interfaces/PackageDefinition.html +0 -30
  90. package/docs/interfaces/PackageFile.html +0 -8
  91. package/docs/interfaces/PasswordChallenge.html +0 -16
  92. package/docs/interfaces/RemoveOptions.html +0 -4
  93. package/docs/interfaces/ShellEnv.html +0 -6
  94. package/docs/interfaces/ShellModule.html +0 -14
  95. package/docs/interfaces/ShellProperties.html +0 -14
  96. package/docs/interfaces/ShellStream.html +0 -11
  97. package/docs/interfaces/SudoChallenge.html +0 -24
  98. package/docs/interfaces/VfsBaseNode.html +0 -12
  99. package/docs/interfaces/VfsDiff.html +0 -10
  100. package/docs/interfaces/VfsDiffEntry.html +0 -6
  101. package/docs/interfaces/VfsDiffModified.html +0 -10
  102. package/docs/interfaces/VfsDirectoryNode.html +0 -15
  103. package/docs/interfaces/VfsFileNode.html +0 -17
  104. package/docs/interfaces/VfsOptions.html +0 -26
  105. package/docs/interfaces/VfsSnapshot.html +0 -3
  106. package/docs/interfaces/VfsSnapshotBaseNode.html +0 -8
  107. package/docs/interfaces/VfsSnapshotDirectoryNode.html +0 -10
  108. package/docs/interfaces/VfsSnapshotFileNode.html +0 -12
  109. package/docs/interfaces/VirtualActiveSession.html +0 -12
  110. package/docs/interfaces/VirtualSftpServerOptions.html +0 -7
  111. package/docs/interfaces/VirtualShellVfsLike.html +0 -15
  112. package/docs/interfaces/VirtualShellVfsOptions.html +0 -3
  113. package/docs/interfaces/WriteFileOptions.html +0 -6
  114. package/docs/media/LICENSE +0 -21
  115. package/docs/modules.html +0 -1
  116. package/docs/types/ArgParseOptions.html +0 -4
  117. package/docs/types/CommandMode.html +0 -2
  118. package/docs/types/CommandOutcome.html +0 -2
  119. package/docs/types/IdleState.html +0 -1
  120. package/docs/types/VfsNodeStats.html +0 -2
  121. package/docs/types/VfsNodeType.html +0 -2
  122. package/docs/types/VfsPersistenceMode.html +0 -5
  123. package/docs/types/VfsSnapshotNode.html +0 -2
  124. package/examples/README.md +0 -288
  125. package/examples/app.js +0 -1751
  126. package/examples/app.ts +0 -299
  127. package/examples/build.js +0 -27
  128. package/examples/demo.html +0 -33
  129. package/examples/honeypot-audit.ts +0 -180
  130. package/examples/honeypot-export.ts +0 -253
  131. package/examples/honeypot-quickstart.ts +0 -110
  132. package/examples/index.html +0 -82
  133. package/examples/server.js +0 -55
  134. package/polyfills/buffer.js +0 -117
  135. package/polyfills/node_child_process/index.js +0 -2
  136. package/polyfills/node_crypto/index.js +0 -167
  137. package/polyfills/node_events/index.js +0 -9
  138. package/polyfills/node_fs/index.js +0 -202
  139. package/polyfills/node_fs/promises.js +0 -4
  140. package/polyfills/node_os/index.js +0 -9
  141. package/polyfills/node_path/index.js +0 -28
  142. package/polyfills/node_vm/index.js +0 -7
  143. package/polyfills/node_zlib/index.js +0 -3
  144. package/polyfills/process.js +0 -14
  145. package/polyfills/ssh2/index.js +0 -75
  146. package/scripts/build-all.mjs +0 -226
  147. package/scripts/build-names.mjs +0 -43
  148. package/scripts/generate-manuals-bundle.mjs +0 -49
  149. package/scripts/postinstall.js +0 -42
  150. package/scripts/publish-package.sh +0 -70
  151. package/src/Honeypot/index.ts +0 -457
  152. package/src/SSHClient/index.ts +0 -270
  153. package/src/SSHMimic/exec.ts +0 -49
  154. package/src/SSHMimic/executor.ts +0 -251
  155. package/src/SSHMimic/hostKey.ts +0 -21
  156. package/src/SSHMimic/index.ts +0 -337
  157. package/src/SSHMimic/loginBanner.ts +0 -36
  158. package/src/SSHMimic/loginFormat.ts +0 -10
  159. package/src/SSHMimic/prompt.ts +0 -14
  160. package/src/SSHMimic/sftp.ts +0 -883
  161. package/src/VirtualFileSystem/binaryPack.ts +0 -258
  162. package/src/VirtualFileSystem/index.ts +0 -1193
  163. package/src/VirtualFileSystem/internalTypes.ts +0 -43
  164. package/src/VirtualFileSystem/journal.ts +0 -171
  165. package/src/VirtualFileSystem/path.ts +0 -74
  166. package/src/VirtualPackageManager/index.ts +0 -996
  167. package/src/VirtualShell/idleManager.ts +0 -137
  168. package/src/VirtualShell/index.ts +0 -475
  169. package/src/VirtualShell/shell.ts +0 -700
  170. package/src/VirtualShell/shellParser.ts +0 -285
  171. package/src/VirtualUserManager/index.ts +0 -758
  172. package/src/bun.d.ts +0 -1
  173. package/src/commands/adduser.ts +0 -103
  174. package/src/commands/alias.ts +0 -69
  175. package/src/commands/apt.ts +0 -233
  176. package/src/commands/awk.ts +0 -168
  177. package/src/commands/base64.ts +0 -29
  178. package/src/commands/cat.ts +0 -52
  179. package/src/commands/cd.ts +0 -25
  180. package/src/commands/chmod.ts +0 -85
  181. package/src/commands/clear.ts +0 -15
  182. package/src/commands/command-helpers.ts +0 -286
  183. package/src/commands/cp.ts +0 -83
  184. package/src/commands/curl.ts +0 -147
  185. package/src/commands/cut.ts +0 -36
  186. package/src/commands/date.ts +0 -30
  187. package/src/commands/declare.ts +0 -49
  188. package/src/commands/deluser.ts +0 -98
  189. package/src/commands/df.ts +0 -23
  190. package/src/commands/diff.ts +0 -43
  191. package/src/commands/dpkg.ts +0 -180
  192. package/src/commands/du.ts +0 -56
  193. package/src/commands/echo.ts +0 -58
  194. package/src/commands/env.ts +0 -23
  195. package/src/commands/exit.ts +0 -18
  196. package/src/commands/export.ts +0 -34
  197. package/src/commands/find.ts +0 -68
  198. package/src/commands/free.ts +0 -47
  199. package/src/commands/grep.ts +0 -116
  200. package/src/commands/groups.ts +0 -19
  201. package/src/commands/gzip.ts +0 -88
  202. package/src/commands/head.ts +0 -52
  203. package/src/commands/help.ts +0 -152
  204. package/src/commands/helpers.ts +0 -234
  205. package/src/commands/history.ts +0 -34
  206. package/src/commands/hostname.ts +0 -14
  207. package/src/commands/htop.ts +0 -20
  208. package/src/commands/id.ts +0 -19
  209. package/src/commands/index.ts +0 -9
  210. package/src/commands/kill.ts +0 -19
  211. package/src/commands/ln.ts +0 -71
  212. package/src/commands/ls.ts +0 -243
  213. package/src/commands/lsb-release.ts +0 -63
  214. package/src/commands/man.ts +0 -31
  215. package/src/commands/manuals/adduser.txt +0 -11
  216. package/src/commands/manuals/apt-cache.txt +0 -12
  217. package/src/commands/manuals/apt.txt +0 -20
  218. package/src/commands/manuals/awk.txt +0 -13
  219. package/src/commands/manuals/cat.txt +0 -14
  220. package/src/commands/manuals/cd.txt +0 -16
  221. package/src/commands/manuals/chmod.txt +0 -16
  222. package/src/commands/manuals/clear.txt +0 -10
  223. package/src/commands/manuals/cp.txt +0 -10
  224. package/src/commands/manuals/curl.txt +0 -20
  225. package/src/commands/manuals/date.txt +0 -14
  226. package/src/commands/manuals/declare.txt +0 -12
  227. package/src/commands/manuals/deluser.txt +0 -10
  228. package/src/commands/manuals/df.txt +0 -10
  229. package/src/commands/manuals/dpkg-query.txt +0 -11
  230. package/src/commands/manuals/dpkg.txt +0 -14
  231. package/src/commands/manuals/du.txt +0 -11
  232. package/src/commands/manuals/echo.txt +0 -11
  233. package/src/commands/manuals/false.txt +0 -10
  234. package/src/commands/manuals/find.txt +0 -11
  235. package/src/commands/manuals/free.txt +0 -12
  236. package/src/commands/manuals/grep.txt +0 -13
  237. package/src/commands/manuals/groups.txt +0 -10
  238. package/src/commands/manuals/gzip.txt +0 -11
  239. package/src/commands/manuals/head.txt +0 -10
  240. package/src/commands/manuals/help.txt +0 -11
  241. package/src/commands/manuals/history.txt +0 -11
  242. package/src/commands/manuals/hostname.txt +0 -10
  243. package/src/commands/manuals/id.txt +0 -10
  244. package/src/commands/manuals/kill.txt +0 -13
  245. package/src/commands/manuals/ls.txt +0 -20
  246. package/src/commands/manuals/lsb_release.txt +0 -14
  247. package/src/commands/manuals/mkdir.txt +0 -10
  248. package/src/commands/manuals/mv.txt +0 -10
  249. package/src/commands/manuals/nano.txt +0 -11
  250. package/src/commands/manuals/neofetch.txt +0 -10
  251. package/src/commands/manuals/node.txt +0 -13
  252. package/src/commands/manuals/npm.txt +0 -13
  253. package/src/commands/manuals/npx.txt +0 -13
  254. package/src/commands/manuals/passwd.txt +0 -11
  255. package/src/commands/manuals/ping.txt +0 -10
  256. package/src/commands/manuals/printf.txt +0 -11
  257. package/src/commands/manuals/ps.txt +0 -10
  258. package/src/commands/manuals/pwd.txt +0 -10
  259. package/src/commands/manuals/python3.txt +0 -13
  260. package/src/commands/manuals/readlink.txt +0 -10
  261. package/src/commands/manuals/return.txt +0 -10
  262. package/src/commands/manuals/rm.txt +0 -10
  263. package/src/commands/manuals/sed.txt +0 -11
  264. package/src/commands/manuals/set.txt +0 -11
  265. package/src/commands/manuals/shift.txt +0 -10
  266. package/src/commands/manuals/sleep.txt +0 -10
  267. package/src/commands/manuals/sort.txt +0 -12
  268. package/src/commands/manuals/source.txt +0 -11
  269. package/src/commands/manuals/ssh.txt +0 -11
  270. package/src/commands/manuals/stat.txt +0 -10
  271. package/src/commands/manuals/su.txt +0 -13
  272. package/src/commands/manuals/sudo.txt +0 -11
  273. package/src/commands/manuals/tail.txt +0 -10
  274. package/src/commands/manuals/tar.txt +0 -19
  275. package/src/commands/manuals/tee.txt +0 -10
  276. package/src/commands/manuals/test.txt +0 -11
  277. package/src/commands/manuals/touch.txt +0 -11
  278. package/src/commands/manuals/tr.txt +0 -10
  279. package/src/commands/manuals/trap.txt +0 -10
  280. package/src/commands/manuals/true.txt +0 -10
  281. package/src/commands/manuals/type.txt +0 -10
  282. package/src/commands/manuals/uname.txt +0 -12
  283. package/src/commands/manuals/uniq.txt +0 -12
  284. package/src/commands/manuals/unset.txt +0 -10
  285. package/src/commands/manuals/uptime.txt +0 -11
  286. package/src/commands/manuals/wc.txt +0 -12
  287. package/src/commands/manuals/wget.txt +0 -12
  288. package/src/commands/manuals/which.txt +0 -10
  289. package/src/commands/manuals/whoami.txt +0 -10
  290. package/src/commands/manuals/xargs.txt +0 -10
  291. package/src/commands/manuals-bundle.ts +0 -898
  292. package/src/commands/mkdir.ts +0 -31
  293. package/src/commands/mv.ts +0 -50
  294. package/src/commands/nano.ts +0 -38
  295. package/src/commands/neofetch.ts +0 -53
  296. package/src/commands/node.ts +0 -341
  297. package/src/commands/npm.ts +0 -132
  298. package/src/commands/passwd.ts +0 -50
  299. package/src/commands/ping.ts +0 -32
  300. package/src/commands/printf.ts +0 -129
  301. package/src/commands/ps.ts +0 -58
  302. package/src/commands/pwd.ts +0 -9
  303. package/src/commands/python.ts +0 -2229
  304. package/src/commands/read.ts +0 -46
  305. package/src/commands/registry.ts +0 -249
  306. package/src/commands/rm.ts +0 -42
  307. package/src/commands/runtime.ts +0 -421
  308. package/src/commands/sed.ts +0 -68
  309. package/src/commands/seq.ts +0 -43
  310. package/src/commands/set.ts +0 -29
  311. package/src/commands/sh.ts +0 -467
  312. package/src/commands/shift.ts +0 -63
  313. package/src/commands/sleep.ts +0 -20
  314. package/src/commands/sort.ts +0 -46
  315. package/src/commands/source.ts +0 -52
  316. package/src/commands/stat.ts +0 -61
  317. package/src/commands/su.ts +0 -72
  318. package/src/commands/sudo.ts +0 -76
  319. package/src/commands/tail.ts +0 -53
  320. package/src/commands/tar.ts +0 -102
  321. package/src/commands/tee.ts +0 -36
  322. package/src/commands/test.ts +0 -137
  323. package/src/commands/touch.ts +0 -28
  324. package/src/commands/tr.ts +0 -70
  325. package/src/commands/tree.ts +0 -20
  326. package/src/commands/true.ts +0 -27
  327. package/src/commands/type.ts +0 -48
  328. package/src/commands/uname.ts +0 -29
  329. package/src/commands/uniq.ts +0 -39
  330. package/src/commands/unset.ts +0 -17
  331. package/src/commands/uptime.ts +0 -54
  332. package/src/commands/wc.ts +0 -55
  333. package/src/commands/wget.ts +0 -148
  334. package/src/commands/which.ts +0 -37
  335. package/src/commands/who.ts +0 -25
  336. package/src/commands/whoami.ts +0 -14
  337. package/src/commands/xargs.ts +0 -31
  338. package/src/index.ts +0 -67
  339. package/src/modules/linuxRootfs.ts +0 -1961
  340. package/src/modules/neofetch.ts +0 -358
  341. package/src/modules/shellInteractive.ts +0 -57
  342. package/src/modules/shellRuntime.ts +0 -76
  343. package/src/self-standalone.ts +0 -542
  344. package/src/standalone-wo-sftp.ts +0 -38
  345. package/src/standalone.ts +0 -72
  346. package/src/types/commands.ts +0 -146
  347. package/src/types/pipeline.ts +0 -52
  348. package/src/types/streams.ts +0 -32
  349. package/src/types/tar-stream.d.ts +0 -38
  350. package/src/types/vfs.ts +0 -98
  351. package/src/utils/expand.ts +0 -491
  352. package/src/utils/perfLogger.ts +0 -72
  353. package/src/utils/tokenize.ts +0 -98
  354. package/src/utils/vfsDiff.ts +0 -275
  355. package/tests/command-helpers.test.ts +0 -116
  356. package/tests/commands-admin-net.test.ts +0 -441
  357. package/tests/commands-advanced.test.ts +0 -456
  358. package/tests/commands-core.test.ts +0 -562
  359. package/tests/commands-missing.test.ts +0 -570
  360. package/tests/commands-specific-units.test.ts +0 -327
  361. package/tests/commands-text-sys.test.ts +0 -445
  362. package/tests/expand.test.ts +0 -170
  363. package/tests/helpers.test.ts +0 -97
  364. package/tests/new-features.test.ts +0 -1036
  365. package/tests/parser-executor.test.ts +0 -37
  366. package/tests/sftp.test.ts +0 -323
  367. package/tests/ssh-exec.test.ts +0 -45
  368. package/tests/test-helper.ts +0 -79
  369. package/tests/users.test.ts +0 -86
  370. package/tsconfig.json +0 -49
  371. 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 };