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