typescript-virtual-container 1.2.9 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/.vscode/settings.json +0 -1
  2. package/README.md +141 -50
  3. package/biome.json +7 -0
  4. package/dist/SSHMimic/exec.d.ts.map +1 -1
  5. package/dist/SSHMimic/executor.d.ts.map +1 -1
  6. package/dist/SSHMimic/executor.js +32 -16
  7. package/dist/SSHMimic/index.d.ts.map +1 -1
  8. package/dist/SSHMimic/index.js +20 -6
  9. package/dist/VirtualFileSystem/binaryPack.d.ts.map +1 -1
  10. package/dist/VirtualFileSystem/binaryPack.js +29 -6
  11. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  12. package/dist/VirtualFileSystem/index.js +36 -13
  13. package/dist/VirtualPackageManager/index.d.ts.map +1 -1
  14. package/dist/VirtualPackageManager/index.js +192 -43
  15. package/dist/VirtualShell/index.d.ts +10 -4
  16. package/dist/VirtualShell/index.d.ts.map +1 -1
  17. package/dist/VirtualShell/index.js +18 -7
  18. package/dist/VirtualShell/shell.d.ts.map +1 -1
  19. package/dist/VirtualShell/shell.js +3 -1
  20. package/dist/VirtualShell/shellParser.d.ts.map +1 -1
  21. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  22. package/dist/commands/adduser.d.ts +6 -0
  23. package/dist/commands/adduser.d.ts.map +1 -1
  24. package/dist/commands/adduser.js +6 -0
  25. package/dist/commands/alias.d.ts +5 -0
  26. package/dist/commands/alias.d.ts.map +1 -1
  27. package/dist/commands/alias.js +5 -0
  28. package/dist/commands/apt.d.ts +5 -0
  29. package/dist/commands/apt.d.ts.map +1 -1
  30. package/dist/commands/apt.js +32 -9
  31. package/dist/commands/awk.d.ts +11 -0
  32. package/dist/commands/awk.d.ts.map +1 -1
  33. package/dist/commands/awk.js +15 -2
  34. package/dist/commands/base64.d.ts +5 -0
  35. package/dist/commands/base64.d.ts.map +1 -1
  36. package/dist/commands/base64.js +9 -1
  37. package/dist/commands/cat.d.ts +5 -0
  38. package/dist/commands/cat.d.ts.map +1 -1
  39. package/dist/commands/cat.js +10 -2
  40. package/dist/commands/cd.d.ts +5 -0
  41. package/dist/commands/cd.d.ts.map +1 -1
  42. package/dist/commands/cd.js +5 -0
  43. package/dist/commands/chmod.d.ts +5 -0
  44. package/dist/commands/chmod.d.ts.map +1 -1
  45. package/dist/commands/chmod.js +5 -0
  46. package/dist/commands/cp.d.ts +5 -0
  47. package/dist/commands/cp.d.ts.map +1 -1
  48. package/dist/commands/cp.js +5 -0
  49. package/dist/commands/curl.d.ts +5 -0
  50. package/dist/commands/curl.d.ts.map +1 -1
  51. package/dist/commands/curl.js +34 -6
  52. package/dist/commands/cut.d.ts +5 -0
  53. package/dist/commands/cut.d.ts.map +1 -1
  54. package/dist/commands/cut.js +8 -1
  55. package/dist/commands/date.d.ts +5 -0
  56. package/dist/commands/date.d.ts.map +1 -1
  57. package/dist/commands/date.js +7 -1
  58. package/dist/commands/declare.d.ts +3 -0
  59. package/dist/commands/declare.d.ts.map +1 -0
  60. package/dist/commands/declare.js +39 -0
  61. package/dist/commands/diff.d.ts +5 -0
  62. package/dist/commands/diff.d.ts.map +1 -1
  63. package/dist/commands/diff.js +5 -0
  64. package/dist/commands/dpkg.d.ts +5 -0
  65. package/dist/commands/dpkg.d.ts.map +1 -1
  66. package/dist/commands/dpkg.js +24 -7
  67. package/dist/commands/du.d.ts.map +1 -1
  68. package/dist/commands/du.js +8 -2
  69. package/dist/commands/echo.d.ts +5 -0
  70. package/dist/commands/echo.d.ts.map +1 -1
  71. package/dist/commands/echo.js +13 -4
  72. package/dist/commands/env.d.ts +5 -0
  73. package/dist/commands/env.d.ts.map +1 -1
  74. package/dist/commands/env.js +11 -1
  75. package/dist/commands/exit.d.ts +5 -0
  76. package/dist/commands/exit.d.ts.map +1 -1
  77. package/dist/commands/exit.js +12 -2
  78. package/dist/commands/export.d.ts.map +1 -1
  79. package/dist/commands/export.js +3 -1
  80. package/dist/commands/find.d.ts +5 -0
  81. package/dist/commands/find.d.ts.map +1 -1
  82. package/dist/commands/find.js +5 -0
  83. package/dist/commands/free.d.ts +5 -0
  84. package/dist/commands/free.d.ts.map +1 -1
  85. package/dist/commands/free.js +5 -0
  86. package/dist/commands/grep.d.ts +5 -0
  87. package/dist/commands/grep.d.ts.map +1 -1
  88. package/dist/commands/grep.js +12 -2
  89. package/dist/commands/gzip.d.ts +5 -0
  90. package/dist/commands/gzip.d.ts.map +1 -1
  91. package/dist/commands/gzip.js +18 -2
  92. package/dist/commands/head.d.ts +5 -0
  93. package/dist/commands/head.d.ts.map +1 -1
  94. package/dist/commands/head.js +5 -0
  95. package/dist/commands/help.d.ts.map +1 -1
  96. package/dist/commands/help.js +98 -45
  97. package/dist/commands/history.d.ts +5 -0
  98. package/dist/commands/history.d.ts.map +1 -1
  99. package/dist/commands/history.js +5 -0
  100. package/dist/commands/hostname.d.ts +5 -0
  101. package/dist/commands/hostname.d.ts.map +1 -1
  102. package/dist/commands/hostname.js +5 -0
  103. package/dist/commands/id.d.ts.map +1 -1
  104. package/dist/commands/id.js +4 -1
  105. package/dist/commands/index.d.ts +2 -17
  106. package/dist/commands/index.d.ts.map +1 -1
  107. package/dist/commands/index.js +2 -340
  108. package/dist/commands/ls.d.ts.map +1 -1
  109. package/dist/commands/ls.js +3 -1
  110. package/dist/commands/lsb-release.d.ts.map +1 -1
  111. package/dist/commands/lsb-release.js +8 -2
  112. package/dist/commands/nano.js +1 -1
  113. package/dist/commands/neofetch.js +1 -1
  114. package/dist/commands/node.d.ts +9 -0
  115. package/dist/commands/node.d.ts.map +1 -0
  116. package/dist/commands/node.js +316 -0
  117. package/dist/commands/npm.d.ts +19 -0
  118. package/dist/commands/npm.d.ts.map +1 -0
  119. package/dist/commands/npm.js +109 -0
  120. package/dist/commands/ping.d.ts.map +1 -1
  121. package/dist/commands/ping.js +3 -1
  122. package/dist/commands/printf.d.ts +3 -0
  123. package/dist/commands/printf.d.ts.map +1 -0
  124. package/dist/commands/printf.js +113 -0
  125. package/dist/commands/ps.d.ts.map +1 -1
  126. package/dist/commands/ps.js +4 -1
  127. package/dist/commands/python.d.ts +30 -0
  128. package/dist/commands/python.d.ts.map +1 -0
  129. package/dist/commands/python.js +2058 -0
  130. package/dist/commands/read.d.ts +3 -0
  131. package/dist/commands/read.d.ts.map +1 -0
  132. package/dist/commands/read.js +34 -0
  133. package/dist/commands/registry.d.ts +8 -0
  134. package/dist/commands/registry.d.ts.map +1 -0
  135. package/dist/commands/registry.js +229 -0
  136. package/dist/commands/runtime.d.ts +6 -0
  137. package/dist/commands/runtime.d.ts.map +1 -0
  138. package/dist/commands/runtime.js +280 -0
  139. package/dist/commands/sed.d.ts.map +1 -1
  140. package/dist/commands/sed.js +11 -3
  141. package/dist/commands/set.d.ts.map +1 -1
  142. package/dist/commands/set.js +9 -3
  143. package/dist/commands/sh.d.ts.map +1 -1
  144. package/dist/commands/sh.js +57 -36
  145. package/dist/commands/shift.d.ts +5 -0
  146. package/dist/commands/shift.d.ts.map +1 -0
  147. package/dist/commands/shift.js +52 -0
  148. package/dist/commands/sleep.d.ts.map +1 -1
  149. package/dist/commands/sort.d.ts.map +1 -1
  150. package/dist/commands/sort.js +4 -2
  151. package/dist/commands/source.d.ts.map +1 -1
  152. package/dist/commands/source.js +5 -2
  153. package/dist/commands/sudo.js +1 -1
  154. package/dist/commands/tar.d.ts.map +1 -1
  155. package/dist/commands/tar.js +11 -3
  156. package/dist/commands/tee.d.ts.map +1 -1
  157. package/dist/commands/tee.js +8 -6
  158. package/dist/commands/test.d.ts.map +1 -1
  159. package/dist/commands/test.js +46 -24
  160. package/dist/commands/tr.d.ts.map +1 -1
  161. package/dist/commands/tr.js +3 -1
  162. package/dist/commands/true.d.ts +4 -0
  163. package/dist/commands/true.d.ts.map +1 -0
  164. package/dist/commands/true.js +14 -0
  165. package/dist/commands/type.d.ts.map +1 -1
  166. package/dist/commands/type.js +1 -1
  167. package/dist/commands/uname.d.ts.map +1 -1
  168. package/dist/commands/uname.js +4 -1
  169. package/dist/commands/uniq.d.ts.map +1 -1
  170. package/dist/commands/uptime.d.ts.map +1 -1
  171. package/dist/commands/uptime.js +4 -1
  172. package/dist/commands/wget.d.ts.map +1 -1
  173. package/dist/commands/wget.js +32 -7
  174. package/dist/commands/which.d.ts.map +1 -1
  175. package/dist/commands/xargs.d.ts.map +1 -1
  176. package/dist/commands/xargs.js +1 -1
  177. package/dist/index.d.ts +15 -14
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +9 -9
  180. package/dist/modules/linuxRootfs.d.ts +18 -1
  181. package/dist/modules/linuxRootfs.d.ts.map +1 -1
  182. package/dist/modules/linuxRootfs.js +160 -17
  183. package/dist/standalone-wo-sftp.d.ts +2 -0
  184. package/dist/standalone-wo-sftp.d.ts.map +1 -0
  185. package/dist/standalone-wo-sftp.js +30 -0
  186. package/dist/utils/expand.d.ts +50 -0
  187. package/dist/utils/expand.d.ts.map +1 -0
  188. package/dist/utils/expand.js +183 -0
  189. package/dist/utils/vfsDiff.d.ts +90 -0
  190. package/dist/utils/vfsDiff.d.ts.map +1 -0
  191. package/dist/utils/vfsDiff.js +177 -0
  192. package/package.json +2 -1
  193. package/src/SSHMimic/exec.ts +10 -1
  194. package/src/SSHMimic/executor.ts +104 -18
  195. package/src/SSHMimic/index.ts +49 -15
  196. package/src/VirtualFileSystem/binaryPack.ts +35 -8
  197. package/src/VirtualFileSystem/index.ts +78 -28
  198. package/src/VirtualPackageManager/index.ts +208 -49
  199. package/src/VirtualShell/index.ts +35 -7
  200. package/src/VirtualShell/shell.ts +23 -3
  201. package/src/VirtualShell/shellParser.ts +134 -36
  202. package/src/VirtualUserManager/index.ts +7 -2
  203. package/src/commands/adduser.ts +6 -0
  204. package/src/commands/alias.ts +5 -1
  205. package/src/commands/apt.ts +47 -17
  206. package/src/commands/awk.ts +20 -6
  207. package/src/commands/base64.ts +13 -2
  208. package/src/commands/cat.ts +13 -5
  209. package/src/commands/cd.ts +5 -0
  210. package/src/commands/chmod.ts +5 -0
  211. package/src/commands/cp.ts +5 -0
  212. package/src/commands/curl.ts +56 -12
  213. package/src/commands/cut.ts +8 -1
  214. package/src/commands/date.ts +7 -1
  215. package/src/commands/declare.ts +44 -0
  216. package/src/commands/diff.ts +17 -3
  217. package/src/commands/dpkg.ts +33 -11
  218. package/src/commands/du.ts +17 -5
  219. package/src/commands/echo.ts +22 -9
  220. package/src/commands/env.ts +11 -1
  221. package/src/commands/exit.ts +12 -2
  222. package/src/commands/export.ts +3 -1
  223. package/src/commands/find.ts +5 -0
  224. package/src/commands/free.ts +9 -2
  225. package/src/commands/grep.ts +12 -2
  226. package/src/commands/gzip.ts +28 -4
  227. package/src/commands/head.ts +5 -0
  228. package/src/commands/help.ts +121 -47
  229. package/src/commands/history.ts +7 -2
  230. package/src/commands/hostname.ts +5 -0
  231. package/src/commands/id.ts +4 -1
  232. package/src/commands/index.ts +9 -360
  233. package/src/commands/ls.ts +5 -3
  234. package/src/commands/lsb-release.ts +8 -2
  235. package/src/commands/nano.ts +1 -1
  236. package/src/commands/neofetch.ts +1 -1
  237. package/src/commands/node.ts +341 -0
  238. package/src/commands/npm.ts +132 -0
  239. package/src/commands/ping.ts +6 -2
  240. package/src/commands/printf.ts +112 -0
  241. package/src/commands/ps.ts +21 -9
  242. package/src/commands/python.ts +2229 -0
  243. package/src/commands/read.ts +41 -0
  244. package/src/commands/registry.ts +244 -0
  245. package/src/commands/runtime.ts +353 -0
  246. package/src/commands/sed.ts +27 -9
  247. package/src/commands/set.ts +9 -3
  248. package/src/commands/sh.ts +159 -55
  249. package/src/commands/shift.ts +53 -0
  250. package/src/commands/sleep.ts +2 -1
  251. package/src/commands/sort.ts +10 -6
  252. package/src/commands/source.ts +15 -3
  253. package/src/commands/sudo.ts +1 -1
  254. package/src/commands/tar.ts +28 -7
  255. package/src/commands/tee.ts +7 -1
  256. package/src/commands/test.ts +61 -26
  257. package/src/commands/tr.ts +3 -1
  258. package/src/commands/true.ts +17 -0
  259. package/src/commands/type.ts +6 -3
  260. package/src/commands/uname.ts +5 -1
  261. package/src/commands/uniq.ts +8 -2
  262. package/src/commands/uptime.ts +4 -1
  263. package/src/commands/wget.ts +51 -12
  264. package/src/commands/which.ts +5 -2
  265. package/src/commands/xargs.ts +11 -2
  266. package/src/index.ts +23 -24
  267. package/src/modules/linuxRootfs.ts +233 -30
  268. package/src/standalone-wo-sftp.ts +38 -0
  269. package/src/utils/expand.ts +238 -0
  270. package/src/utils/vfsDiff.ts +275 -0
  271. package/standalone-wo-sftp.js +507 -0
  272. package/standalone-wo-sftp.js.map +7 -0
  273. package/standalone.js +253 -191
  274. package/standalone.js.map +4 -4
  275. package/tests/bun-test-shim.ts +9 -1
  276. package/tests/command-helpers.test.ts +1 -5
  277. package/tests/new-features.test.ts +415 -5
  278. package/tests/parser-executor.test.ts +27 -27
  279. package/tests/sftp.test.ts +122 -42
  280. package/tests/users.test.ts +23 -5
  281. package/CHANGELOG.md +0 -150
@@ -1 +1 @@
1
- {"version":3,"file":"which.d.ts","sourceRoot":"","sources":["../../src/commands/which.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,YAAY,EAAE,WA+B1B,CAAC"}
1
+ {"version":3,"file":"which.d.ts","sourceRoot":"","sources":["../../src/commands/which.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,YAAY,EAAE,WAkC1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"xargs.d.ts","sourceRoot":"","sources":["../../src/commands/xargs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,YAAY,EAAE,WAa1B,CAAC"}
1
+ {"version":3,"file":"xargs.d.ts","sourceRoot":"","sources":["../../src/commands/xargs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,YAAY,EAAE,WAsB1B,CAAC"}
@@ -1,4 +1,4 @@
1
- import { runCommand } from "./index";
1
+ import { runCommand } from "./runtime";
2
2
  export const xargsCommand = {
3
3
  name: "xargs",
4
4
  description: "Build and execute command lines from stdin",
package/dist/index.d.ts CHANGED
@@ -1,17 +1,18 @@
1
- import { HoneyPot } from "./Honeypot/index";
2
- import { SshClient } from "./SSHClient/index";
3
- import { SftpMimic, SshMimic } from "./SSHMimic/index";
4
- import VirtualFileSystem from "./VirtualFileSystem/index";
5
- import { VirtualShell } from "./VirtualShell/index";
6
- import { VirtualUserManager } from "./VirtualUserManager/index";
7
- import { VirtualPackageManager } from "./VirtualPackageManager/index";
8
- export type { AuditLogEntry, HoneyPotStats, } from "./Honeypot/index";
9
- export type { CommandContext, CommandMode, CommandOutcome, CommandResult, NanoEditorSession, ShellEnv, ShellModule, SudoChallenge, } from "./types/commands";
10
- export type { ShellProperties } from "./VirtualShell/index";
1
+ export { HoneyPot } from "./Honeypot/index";
2
+ export { SshClient } from "./SSHClient/index";
3
+ export { SftpMimic as VirtualSftpServer, SshMimic as VirtualSshServer } from "./SSHMimic/index";
4
+ export { default as VirtualFileSystem } from "./VirtualFileSystem/index";
5
+ export { VirtualPackageManager } from "./VirtualPackageManager/index";
6
+ export { VirtualShell } from "./VirtualShell/index";
7
+ export { VirtualUserManager } from "./VirtualUserManager/index";
8
+ export type { AuditLogEntry, HoneyPotStats } from "./Honeypot/index";
9
+ export type { CommandContext, CommandMode, CommandOutcome, CommandResult, NanoEditorSession, ShellEnv, ShellModule, SudoChallenge } from "./types/commands";
11
10
  export type { ExecStream, ShellStream } from "./types/streams";
12
- export type { RemoveOptions, VfsBaseNode, VfsDirectoryNode, VfsFileNode, VfsNodeStats, VfsNodeType, VfsSnapshot, VfsSnapshotBaseNode, VfsSnapshotDirectoryNode, VfsSnapshotFileNode, VfsSnapshotNode, WriteFileOptions, } from "./types/vfs";
11
+ export type { RemoveOptions, VfsBaseNode, VfsDirectoryNode, VfsFileNode, VfsNodeStats, VfsNodeType, VfsSnapshot, VfsSnapshotBaseNode, VfsSnapshotDirectoryNode, VfsSnapshotFileNode, VfsSnapshotNode, WriteFileOptions } from "./types/vfs";
13
12
  export type { VfsOptions, VfsPersistenceMode } from "./VirtualFileSystem/index";
14
- export type { PackageDefinition, PackageFile, InstalledPackage, } from "./VirtualPackageManager/index";
15
- export { HoneyPot, SshClient, VirtualFileSystem, SftpMimic as VirtualSftpServer, VirtualShell, SshMimic as VirtualSshServer, VirtualUserManager, VirtualPackageManager, };
16
- export { getArg, getFlag, ifFlag, } from "./commands/command-helpers";
13
+ export type { ShellProperties } from "./VirtualShell/index";
14
+ export type { InstalledPackage, PackageDefinition, PackageFile } from "./VirtualPackageManager/index";
15
+ export { assertDiff, diffSnapshots, formatDiff } from "./utils/vfsDiff";
16
+ export type { VfsDiff, VfsDiffEntry, VfsDiffModified } from "./utils/vfsDiff";
17
+ export { getArg, getFlag, ifFlag } from "./commands/command-helpers";
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,iBAAiB,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,YAAY,EACX,aAAa,EACb,aAAa,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACX,cAAc,EACd,WAAW,EACX,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,aAAa,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,YAAY,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEhF,YAAY,EACX,iBAAiB,EACjB,WAAW,EACX,gBAAgB,GAChB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACN,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,SAAS,IAAI,iBAAiB,EAC9B,YAAY,EACZ,QAAQ,IAAI,gBAAgB,EAC5B,kBAAkB,EAClB,qBAAqB,GACrB,CAAC;AAEF,OAAO,EACN,MAAM,EACN,OAAO,EACP,MAAM,GACN,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,SAAS,IAAI,iBAAiB,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,YAAY,EACX,aAAa,EACb,aAAa,EACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACX,cAAc,EACd,WAAW,EACX,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,aAAa,EACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,YAAY,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAChF,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,YAAY,EACX,gBAAgB,EAAE,iBAAiB,EACnC,WAAW,EACX,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACN,UAAU,EAAE,aAAa,EACzB,UAAU,EACV,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACX,OAAO,EACP,YAAY,EACZ,eAAe,EACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,MAAM,EACN,OAAO,EACP,MAAM,EACN,MAAM,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { HoneyPot } from "./Honeypot/index";
2
- import { SshClient } from "./SSHClient/index";
3
- import { SftpMimic, SshMimic } from "./SSHMimic/index";
4
- import VirtualFileSystem from "./VirtualFileSystem/index";
5
- import { VirtualShell } from "./VirtualShell/index";
6
- import { VirtualUserManager } from "./VirtualUserManager/index";
7
- import { VirtualPackageManager } from "./VirtualPackageManager/index";
8
- export { HoneyPot, SshClient, VirtualFileSystem, SftpMimic as VirtualSftpServer, VirtualShell, SshMimic as VirtualSshServer, VirtualUserManager, VirtualPackageManager, };
9
- export { getArg, getFlag, ifFlag, } from "./commands/command-helpers";
1
+ export { HoneyPot } from "./Honeypot/index";
2
+ export { SshClient } from "./SSHClient/index";
3
+ export { SftpMimic as VirtualSftpServer, SshMimic as VirtualSshServer } from "./SSHMimic/index";
4
+ export { default as VirtualFileSystem } from "./VirtualFileSystem/index";
5
+ export { VirtualPackageManager } from "./VirtualPackageManager/index";
6
+ export { VirtualShell } from "./VirtualShell/index";
7
+ export { VirtualUserManager } from "./VirtualUserManager/index";
8
+ export { assertDiff, diffSnapshots, formatDiff } from "./utils/vfsDiff";
9
+ export { getArg, getFlag, ifFlag } from "./commands/command-helpers";
@@ -1,3 +1,4 @@
1
+ /** biome-ignore-all lint/style/useNamingConvention: ENV VAR KEYS */
1
2
  /**
2
3
  * linuxRootfs.ts
3
4
  *
@@ -8,8 +9,24 @@
8
9
  import type { ShellProperties } from "../VirtualShell";
9
10
  import type VirtualFileSystem from "../VirtualFileSystem";
10
11
  import type { VirtualUserManager } from "../VirtualUserManager";
12
+ /**
13
+ * Sync `/etc/passwd`, `/etc/group`, and `/etc/shadow` from the
14
+ * VirtualUserManager's current user list into the VFS.
15
+ * @param vfs VirtualFileSystem instance to write files into
16
+ * @param users VirtualUserManager to source users from
17
+ */
11
18
  export declare function syncEtcPasswd(vfs: VirtualFileSystem, users: VirtualUserManager): void;
12
- export declare function refreshProc(vfs: VirtualFileSystem, props: ShellProperties, hostname: string, shellStartTime: number): void;
19
+ /**
20
+ * Populate and refresh `/proc` virtual entries based on host stats and
21
+ * provided active sessions. Rewrites `/proc/uptime`, `/proc/meminfo`,
22
+ * `/proc/cpuinfo`, `/proc/<pid>` entries and `/proc/self` content.
23
+ * @param vfs VirtualFileSystem instance
24
+ * @param props ShellProperties used for version strings
25
+ * @param hostname Hostname to write into /proc/hostname
26
+ * @param shellStartTime Start time used to compute uptime
27
+ * @param sessions Optional active sessions list to populate per-pid entries
28
+ */
29
+ export declare function refreshProc(vfs: VirtualFileSystem, props: ShellProperties, hostname: string, shellStartTime: number, sessions?: import("../VirtualUserManager").VirtualActiveSession[]): void;
13
30
  /**
14
31
  * Bootstraps the full Linux rootfs hierarchy in the VFS.
15
32
  * Safe to call multiple times — idempotent.
@@ -1 +1 @@
1
- {"version":3,"file":"linuxRootfs.d.ts","sourceRoot":"","sources":["../../src/modules/linuxRootfs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAyHhE,wBAAgB,aAAa,CAC5B,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,kBAAkB,GACvB,IAAI,CAsCN;AAID,wBAAgB,WAAW,CAC1B,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACpB,IAAI,CA6DN;AAuKD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,eAAe,EACtB,cAAc,EAAE,MAAM,GACpB,IAAI,CAYN"}
1
+ {"version":3,"file":"linuxRootfs.d.ts","sourceRoot":"","sources":["../../src/modules/linuxRootfs.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAgHhE;;;;;GAKG;AACH,wBAAgB,aAAa,CAC5B,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,kBAAkB,GACvB,IAAI,CAsCN;AAgED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAC1B,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,OAAO,uBAAuB,EAAE,oBAAoB,EAAE,GAC/D,IAAI,CAmJN;AAuND;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,eAAe,EACtB,cAAc,EAAE,MAAM,GACpB,IAAI,CAYN"}
@@ -1,3 +1,4 @@
1
+ /** biome-ignore-all lint/style/useNamingConvention: ENV VAR KEYS */
1
2
  /**
2
3
  * linuxRootfs.ts
3
4
  *
@@ -36,12 +37,7 @@ function bootstrapEtc(vfs, hostname, props) {
36
37
  "export PS1='\\u@\\h:\\w\\$ '",
37
38
  ].join("\n")}\n`);
38
39
  ensureFile(vfs, "/etc/issue", `Fortune GNU/Linux 1.0 \\n \\l\n`);
39
- ensureFile(vfs, "/etc/motd", [
40
- "",
41
- `Welcome to ${props.os}`,
42
- `Kernel: ${props.kernel}`,
43
- "",
44
- ].join("\n"));
40
+ ensureFile(vfs, "/etc/motd", ["", `Welcome to ${props.os}`, `Kernel: ${props.kernel}`, ""].join("\n"));
45
41
  // APT sources
46
42
  ensureDir(vfs, "/etc/apt");
47
43
  ensureDir(vfs, "/etc/apt/sources.list.d");
@@ -71,6 +67,12 @@ function bootstrapEtc(vfs, hostname, props) {
71
67
  ensureDir(vfs, "/etc/systemd/system");
72
68
  }
73
69
  // ─── /etc/passwd + /etc/group + /etc/shadow ─────────────────────────────────
70
+ /**
71
+ * Sync `/etc/passwd`, `/etc/group`, and `/etc/shadow` from the
72
+ * VirtualUserManager's current user list into the VFS.
73
+ * @param vfs VirtualFileSystem instance to write files into
74
+ * @param users VirtualUserManager to source users from
75
+ */
74
76
  export function syncEtcPasswd(vfs, users) {
75
77
  const userList = users.listUsers();
76
78
  const passwdLines = [
@@ -108,7 +110,53 @@ export function syncEtcPasswd(vfs, users) {
108
110
  vfs.writeFile("/etc/shadow", `${shadowLines.join("\n")}\n`, { mode: 0o640 });
109
111
  }
110
112
  // ─── /proc ───────────────────────────────────────────────────────────────────
111
- export function refreshProc(vfs, props, hostname, shellStartTime) {
113
+ /** Derive a stable virtual PID from a tty string like "pts/0" → 1000, "pts/1" → 1001 */
114
+ function ttyToPid(tty) {
115
+ const match = tty.match(/(\d+)$/);
116
+ return 1000 + (match?.[1] ? parseInt(match[1], 10) : 0);
117
+ }
118
+ /** Write /proc/<pid>/ subtree for a single virtual process */
119
+ function writeProcPid(vfs, pid, username, _tty, cmdline, startedAt, env) {
120
+ const dir = `/proc/${pid}`;
121
+ ensureDir(vfs, dir);
122
+ ensureDir(vfs, `${dir}/fd`);
123
+ ensureDir(vfs, `${dir}/fdinfo`);
124
+ const uptimeSec = Math.floor((Date.now() - new Date(startedAt).getTime()) / 1000);
125
+ vfs.writeFile(`${dir}/cmdline`, `${cmdline.replace(/\s+/g, "\0")}\0`);
126
+ vfs.writeFile(`${dir}/comm`, cmdline.split(/\s+/)[0] ?? "bash");
127
+ vfs.writeFile(`${dir}/status`, `${[
128
+ `Name: ${cmdline.split(/\s+/)[0] ?? "bash"}`,
129
+ `State: S (sleeping)`,
130
+ `Pid: ${pid}`,
131
+ `PPid: 1`,
132
+ `Uid: 0\t0\t0\t0`,
133
+ `Gid: 0\t0\t0\t0`,
134
+ `VmRSS: 4096 kB`,
135
+ `VmSize: 16384 kB`,
136
+ `Threads: 1`,
137
+ ].join("\n")}\n`);
138
+ vfs.writeFile(`${dir}/stat`, `${pid} (${cmdline.split(/\s+/)[0] ?? "bash"}) S 1 ${pid} ${pid} 0 -1 4194304 0 0 0 0 ${uptimeSec} 0 0 0 20 0 1 0 0 16384 4096 0\n`);
139
+ vfs.writeFile(`${dir}/environ`, `${Object.entries(env)
140
+ .map(([k, v]) => `${k}=${v}`)
141
+ .join("\0")}\0`);
142
+ vfs.writeFile(`${dir}/cwd`, `/home/${username}\0`);
143
+ vfs.writeFile(`${dir}/exe`, "/bin/bash\0");
144
+ // Standard fd entries
145
+ vfs.writeFile(`${dir}/fd/0`, "");
146
+ vfs.writeFile(`${dir}/fd/1`, "");
147
+ vfs.writeFile(`${dir}/fd/2`, "");
148
+ }
149
+ /**
150
+ * Populate and refresh `/proc` virtual entries based on host stats and
151
+ * provided active sessions. Rewrites `/proc/uptime`, `/proc/meminfo`,
152
+ * `/proc/cpuinfo`, `/proc/<pid>` entries and `/proc/self` content.
153
+ * @param vfs VirtualFileSystem instance
154
+ * @param props ShellProperties used for version strings
155
+ * @param hostname Hostname to write into /proc/hostname
156
+ * @param shellStartTime Start time used to compute uptime
157
+ * @param sessions Optional active sessions list to populate per-pid entries
158
+ */
159
+ export function refreshProc(vfs, props, hostname, shellStartTime, sessions) {
112
160
  ensureDir(vfs, "/proc");
113
161
  const uptimeSec = Math.floor((Date.now() - shellStartTime) / 1000);
114
162
  vfs.writeFile("/proc/uptime", `${uptimeSec}.00 ${Math.floor(uptimeSec * 0.9)}.00\n`);
@@ -130,7 +178,7 @@ export function refreshProc(vfs, props, hostname, shellStartTime) {
130
178
  const c = cpus[i];
131
179
  if (!c)
132
180
  continue;
133
- const mhz = (c.speed).toFixed(3);
181
+ const mhz = c.speed.toFixed(3);
134
182
  cpuLines.push(`processor\t: ${i}`, `model name\t: ${c.model}`, `cpu MHz\t\t: ${mhz}`, `cache size\t: 8192 KB`, "");
135
183
  }
136
184
  vfs.writeFile("/proc/cpuinfo", `${cpuLines.join("\n")}\n`);
@@ -138,7 +186,8 @@ export function refreshProc(vfs, props, hostname, shellStartTime) {
138
186
  vfs.writeFile("/proc/hostname", `${hostname}\n`);
139
187
  // /proc/loadavg
140
188
  const load = (Math.random() * 0.5).toFixed(2);
141
- vfs.writeFile("/proc/loadavg", `${load} ${load} ${load} 1/1 1\n`);
189
+ const numProcs = 1 + (sessions?.length ?? 0);
190
+ vfs.writeFile("/proc/loadavg", `${load} ${load} ${load} ${numProcs}/${numProcs} 1\n`);
142
191
  // /proc/net stubs
143
192
  ensureDir(vfs, "/proc/net");
144
193
  ensureFile(vfs, "/proc/net/dev", `${[
@@ -147,6 +196,61 @@ export function refreshProc(vfs, props, hostname, shellStartTime) {
147
196
  " lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
148
197
  " eth0: 131072 1024 0 0 0 0 0 0 65536 512 0 0 0 0 0 0",
149
198
  ].join("\n")}\n`);
199
+ // ── /proc/1 — init process ────────────────────────────────────────────────
200
+ writeProcPid(vfs, 1, "root", "pts/0", "/sbin/init", new Date(shellStartTime).toISOString(), {});
201
+ // ── /proc/<pid> per session ───────────────────────────────────────────────
202
+ const activeSessions = sessions ?? [];
203
+ for (const session of activeSessions) {
204
+ const pid = ttyToPid(session.tty);
205
+ writeProcPid(vfs, pid, session.username, session.tty, "bash", session.startedAt, {
206
+ USER: session.username,
207
+ HOME: `/home/${session.username}`,
208
+ TERM: "xterm-256color",
209
+ SHELL: "/bin/bash",
210
+ });
211
+ }
212
+ // ── /proc/self — symlink to current session PID or 1 ────────────────────
213
+ // We can't know which session is "current" at populate time,
214
+ // so /proc/self is a directory that mirrors the most recent session,
215
+ // or init if no sessions. Commands that read /proc/self get consistent data.
216
+ const selfPid = activeSessions.length > 0
217
+ ? ttyToPid(activeSessions[activeSessions.length - 1].tty)
218
+ : 1;
219
+ // Remove existing /proc/self and recreate as content copy
220
+ if (vfs.exists("/proc/self")) {
221
+ try {
222
+ vfs.remove("/proc/self");
223
+ }
224
+ catch { }
225
+ }
226
+ // /proc/self is a real directory (not a symlink, which VFS may not support for dirs)
227
+ const selfSrc = `/proc/${selfPid}`;
228
+ if (vfs.exists(selfSrc)) {
229
+ ensureDir(vfs, "/proc/self");
230
+ ensureDir(vfs, "/proc/self/fd");
231
+ for (const entry of vfs.list(selfSrc)) {
232
+ const srcPath = `${selfSrc}/${entry}`;
233
+ const dstPath = `/proc/self/${entry}`;
234
+ try {
235
+ const st = vfs.stat(srcPath);
236
+ if (st.type === "file") {
237
+ vfs.writeFile(dstPath, vfs.readFile(srcPath));
238
+ }
239
+ }
240
+ catch { }
241
+ }
242
+ vfs.writeFile("/proc/self/status", vfs.exists(`${selfSrc}/status`) ? vfs.readFile(`${selfSrc}/status`) : "");
243
+ }
244
+ else {
245
+ // Fallback minimal /proc/self
246
+ ensureDir(vfs, "/proc/self");
247
+ vfs.writeFile("/proc/self/cmdline", "bash\0");
248
+ vfs.writeFile("/proc/self/comm", "bash");
249
+ vfs.writeFile("/proc/self/status", "Name:\tbash\nState:\tS (sleeping)\nPid:\t1\nPPid:\t0\n");
250
+ vfs.writeFile("/proc/self/environ", "");
251
+ vfs.writeFile("/proc/self/cwd", "/root\0");
252
+ vfs.writeFile("/proc/self/exe", "/bin/bash\0");
253
+ }
150
254
  }
151
255
  // ─── /sys ─────────────────────────────────────────────────────────────────────
152
256
  function bootstrapSys(vfs, props) {
@@ -191,18 +295,57 @@ function bootstrapUsr(vfs) {
191
295
  ensureDir(vfs, "/usr/lib");
192
296
  // Stub binaries so `which` can find built-in commands
193
297
  const builtins = [
194
- "sh", "bash", "ls", "cat", "echo", "grep", "find", "sort",
195
- "head", "tail", "cut", "tr", "sed", "awk", "wc", "tee",
196
- "tar", "gzip", "gunzip", "touch", "mkdir", "rm", "mv", "cp",
197
- "chmod", "ln", "pwd", "env", "date", "sleep", "id", "whoami",
198
- "hostname", "uname", "ps", "kill", "df", "du", "curl", "wget",
199
- "nano", "diff", "uniq", "xargs", "base64",
298
+ "sh",
299
+ "bash",
300
+ "ls",
301
+ "cat",
302
+ "echo",
303
+ "grep",
304
+ "find",
305
+ "sort",
306
+ "head",
307
+ "tail",
308
+ "cut",
309
+ "tr",
310
+ "sed",
311
+ "awk",
312
+ "wc",
313
+ "tee",
314
+ "tar",
315
+ "gzip",
316
+ "gunzip",
317
+ "touch",
318
+ "mkdir",
319
+ "rm",
320
+ "mv",
321
+ "cp",
322
+ "chmod",
323
+ "ln",
324
+ "pwd",
325
+ "env",
326
+ "date",
327
+ "sleep",
328
+ "id",
329
+ "whoami",
330
+ "hostname",
331
+ "uname",
332
+ "ps",
333
+ "kill",
334
+ "df",
335
+ "du",
336
+ "curl",
337
+ "wget",
338
+ "nano",
339
+ "diff",
340
+ "uniq",
341
+ "xargs",
342
+ "base64",
200
343
  ];
201
344
  for (const bin of builtins) {
202
345
  ensureFile(vfs, `/usr/bin/${bin}`, `#!/bin/sh\nexec builtin ${bin} "$@"\n`, 0o755);
203
346
  }
204
347
  // lsb_release script
205
- ensureFile(vfs, "/usr/bin/lsb_release", "#!/bin/sh\nexec lsb_release \"$@\"\n", 0o755);
348
+ ensureFile(vfs, "/usr/bin/lsb_release", '#!/bin/sh\nexec lsb_release "$@"\n', 0o755);
206
349
  }
207
350
  // ─── /var ─────────────────────────────────────────────────────────────────────
208
351
  function bootstrapVar(vfs) {
@@ -292,6 +435,6 @@ export function bootstrapLinuxRootfs(vfs, users, hostname, props, shellStartTime
292
435
  bootstrapTmp(vfs);
293
436
  bootstrapRoot(vfs);
294
437
  bootstrapMisc(vfs);
295
- refreshProc(vfs, props, hostname, shellStartTime);
438
+ refreshProc(vfs, props, hostname, shellStartTime, []);
296
439
  syncEtcPasswd(vfs, users);
297
440
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=standalone-wo-sftp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone-wo-sftp.d.ts","sourceRoot":"","sources":["../src/standalone-wo-sftp.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ import { SshMimic } from "./SSHMimic/index";
2
+ import { VirtualShell } from "./VirtualShell";
3
+ const hostname = process.env.SSH_MIMIC_HOSTNAME ?? "typescript-vm";
4
+ const virtualShell = new VirtualShell(hostname, undefined, {
5
+ mode: "fs",
6
+ snapshotPath: ".vfs",
7
+ });
8
+ virtualShell.addCommand("demo", [], () => {
9
+ return {
10
+ stdout: "This is a demo command. It does nothing useful.",
11
+ exitCode: 0,
12
+ };
13
+ });
14
+ new SshMimic({
15
+ port: 2222,
16
+ hostname,
17
+ shell: virtualShell,
18
+ })
19
+ .start()
20
+ .catch((error) => {
21
+ console.error("Failed to start SSH Mimic:", error);
22
+ process.exit(1);
23
+ });
24
+ process.on("uncaughtException", (error) => {
25
+ console.log("Oh my god, something terrible happened: ", error);
26
+ });
27
+ process.on("unhandledRejection", (error, promise) => {
28
+ console.log(" Oh Lord! We forgot to handle a promise rejection here: ", promise);
29
+ console.log(" The error was: ", error);
30
+ });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * expand.ts
3
+ *
4
+ * Centralised shell variable and expression expansion.
5
+ * Used by `runCommand` (index.ts), `echo`, and `sh.ts`.
6
+ *
7
+ * Handles (in order):
8
+ * ~ tilde to $HOME
9
+ * $? last exit code
10
+ * $$ mock PID
11
+ * $# argument count (0 outside scripts)
12
+ * ${#VAR} string length
13
+ * ${VAR:-def} default if unset/empty
14
+ * ${VAR:=def} assign default if unset/empty
15
+ * ${VAR:+val} alternate value if set
16
+ * ${VAR} simple braced reference
17
+ * $VAR simple reference
18
+ * $((expr)) arithmetic (integer)
19
+ */
20
+ /**
21
+ * Evaluate a simple integer arithmetic expression.
22
+ * Supports: + - * / % ** unary- ( )
23
+ * Variables are resolved from `env` before evaluation.
24
+ * Returns NaN on syntax error.
25
+ */
26
+ export declare function evalArith(expr: string, env: Record<string, string>): number;
27
+ /**
28
+ * Expand all shell variable and expression forms synchronously.
29
+ * Does NOT handle `$(cmd)` — that requires async; see `expandAsync`.
30
+ * Content inside single quotes is left verbatim per POSIX sh rules.
31
+ *
32
+ * @param input Raw string possibly containing `$VAR`, `${...}`, `$((...))`.
33
+ * @param env Current session env vars.
34
+ * @param lastExit Last command exit code (for `$?`).
35
+ * @param home Home directory path (for `~`).
36
+ */
37
+ export declare function expandSync(input: string, env: Record<string, string>, lastExit?: number, home?: string): string;
38
+ /**
39
+ * Expand all shell forms including `$(cmd)` command substitution.
40
+ *
41
+ * Processes `$(...)` blocks depth-first, respecting single-quote boundaries.
42
+ * Then delegates to `expandSync` for the remaining forms.
43
+ *
44
+ * @param input Raw string.
45
+ * @param env Current session env vars.
46
+ * @param lastExit Last exit code.
47
+ * @param runCmd Async callback to execute a command and return its stdout.
48
+ */
49
+ export declare function expandAsync(input: string, env: Record<string, string>, lastExit: number, runCmd: (cmd: string) => Promise<string>): Promise<string>;
50
+ //# sourceMappingURL=expand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expand.d.ts","sourceRoot":"","sources":["../../src/utils/expand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAuB3E;AAoCD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACzB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,QAAQ,SAAI,EACZ,IAAI,CAAC,EAAE,MAAM,GACX,MAAM,CA4DR;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAChC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,CAuDjB"}
@@ -0,0 +1,183 @@
1
+ /**
2
+ * expand.ts
3
+ *
4
+ * Centralised shell variable and expression expansion.
5
+ * Used by `runCommand` (index.ts), `echo`, and `sh.ts`.
6
+ *
7
+ * Handles (in order):
8
+ * ~ tilde to $HOME
9
+ * $? last exit code
10
+ * $$ mock PID
11
+ * $# argument count (0 outside scripts)
12
+ * ${#VAR} string length
13
+ * ${VAR:-def} default if unset/empty
14
+ * ${VAR:=def} assign default if unset/empty
15
+ * ${VAR:+val} alternate value if set
16
+ * ${VAR} simple braced reference
17
+ * $VAR simple reference
18
+ * $((expr)) arithmetic (integer)
19
+ */
20
+ // ─── arithmetic evaluator ────────────────────────────────────────────────────
21
+ /**
22
+ * Evaluate a simple integer arithmetic expression.
23
+ * Supports: + - * / % ** unary- ( )
24
+ * Variables are resolved from `env` before evaluation.
25
+ * Returns NaN on syntax error.
26
+ */
27
+ export function evalArith(expr, env) {
28
+ // Substitute variable names before evaluating
29
+ const substituted = expr.replace(/\b([A-Za-z_][A-Za-z0-9_]*)\b/g, (_, name) => {
30
+ const val = env[name];
31
+ return val !== undefined && val !== "" ? val : "0";
32
+ });
33
+ // Whitelist: only digits, operators, spaces, parens
34
+ if (!/^[\d\s+\-*/%()^!&|<>=,. ]+$/.test(substituted))
35
+ return NaN;
36
+ try {
37
+ // Use Function constructor for safe subset (no identifiers remain)
38
+ // eslint-disable-next-line no-new-func
39
+ const result = Function(`"use strict"; return (${substituted.replace(/\*\*/g, "**")});`)();
40
+ return typeof result === "number" ? Math.trunc(result) : NaN;
41
+ }
42
+ catch {
43
+ return NaN;
44
+ }
45
+ }
46
+ // ─── synchronous expansion ───────────────────────────────────────────────────
47
+ /**
48
+ * Apply a replacer only to portions of `input` that are NOT inside single quotes.
49
+ * Single-quoted content is passed through verbatim (POSIX sh behaviour).
50
+ */
51
+ function outsideSingleQuotes(input, replacer) {
52
+ const parts = [];
53
+ let i = 0;
54
+ while (i < input.length) {
55
+ const sqIdx = input.indexOf("'", i);
56
+ if (sqIdx === -1) {
57
+ // No more single quotes — expand the rest
58
+ parts.push(replacer(input.slice(i)));
59
+ break;
60
+ }
61
+ // Expand the part before the single quote
62
+ parts.push(replacer(input.slice(i, sqIdx)));
63
+ // Find closing single quote — everything inside is literal
64
+ const closeIdx = input.indexOf("'", sqIdx + 1);
65
+ if (closeIdx === -1) {
66
+ // Unclosed quote — treat rest as literal
67
+ parts.push(input.slice(sqIdx));
68
+ break;
69
+ }
70
+ parts.push(input.slice(sqIdx, closeIdx + 1)); // include quotes
71
+ i = closeIdx + 1;
72
+ }
73
+ return parts.join("");
74
+ }
75
+ /**
76
+ * Expand all shell variable and expression forms synchronously.
77
+ * Does NOT handle `$(cmd)` — that requires async; see `expandAsync`.
78
+ * Content inside single quotes is left verbatim per POSIX sh rules.
79
+ *
80
+ * @param input Raw string possibly containing `$VAR`, `${...}`, `$((...))`.
81
+ * @param env Current session env vars.
82
+ * @param lastExit Last command exit code (for `$?`).
83
+ * @param home Home directory path (for `~`).
84
+ */
85
+ export function expandSync(input, env, lastExit = 0, home) {
86
+ const homePath = home ?? env.HOME ?? "/home/user";
87
+ return outsideSingleQuotes(input, (chunk) => {
88
+ let s = chunk;
89
+ // Tilde expansion — only at start of token or after `:` or whitespace
90
+ s = s.replace(/(^|[\s:])~(\/|$)/g, (_, pre, post) => `${pre}${homePath}${post}`);
91
+ // $? $$ $#
92
+ s = s.replace(/\$\?/g, String(lastExit));
93
+ s = s.replace(/\$\$/g, "1");
94
+ s = s.replace(/\$#/g, "0");
95
+ // $(( arithmetic )) — must come before ${ and $VAR to avoid conflicts
96
+ s = s.replace(/\$\(\(([^)]+(?:\([^)]*\)[^)]*)*)\)\)/g, (_, expr) => {
97
+ const result = evalArith(expr, env);
98
+ return Number.isNaN(result) ? "0" : String(result);
99
+ });
100
+ // ${#VAR} — string length
101
+ s = s.replace(/\$\{#([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => String((env[name] ?? "").length));
102
+ // ${VAR:-default}
103
+ s = s.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*):-([^}]*)\}/g, (_, name, def) => env[name] !== undefined && env[name] !== "" ? env[name] : def);
104
+ // ${VAR:=default} — also assigns to env
105
+ s = s.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*):=([^}]*)\}/g, (_, name, def) => {
106
+ if (env[name] === undefined || env[name] === "")
107
+ env[name] = def;
108
+ return env[name];
109
+ });
110
+ // ${VAR:+alternate}
111
+ s = s.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*):\+([^}]*)\}/g, (_, name, alt) => env[name] !== undefined && env[name] !== "" ? alt : "");
112
+ // ${VAR}
113
+ s = s.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => env[name] ?? "");
114
+ // $VAR
115
+ s = s.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, name) => env[name] ?? "");
116
+ return s;
117
+ });
118
+ }
119
+ // ─── async expansion (includes $(cmd)) ──────────────────────────────────────
120
+ /**
121
+ * Expand all shell forms including `$(cmd)` command substitution.
122
+ *
123
+ * Processes `$(...)` blocks depth-first, respecting single-quote boundaries.
124
+ * Then delegates to `expandSync` for the remaining forms.
125
+ *
126
+ * @param input Raw string.
127
+ * @param env Current session env vars.
128
+ * @param lastExit Last exit code.
129
+ * @param runCmd Async callback to execute a command and return its stdout.
130
+ */
131
+ export async function expandAsync(input, env, lastExit, runCmd) {
132
+ // $(cmd) substitution — skip content inside single quotes
133
+ if (input.includes("$(")) {
134
+ let result = "";
135
+ let inSingle = false;
136
+ let i = 0;
137
+ while (i < input.length) {
138
+ const ch = input[i];
139
+ if (ch === "'" && !inSingle) {
140
+ inSingle = true;
141
+ result += ch;
142
+ i++;
143
+ continue;
144
+ }
145
+ if (ch === "'" && inSingle) {
146
+ inSingle = false;
147
+ result += ch;
148
+ i++;
149
+ continue;
150
+ }
151
+ if (!inSingle && ch === "$" && input[i + 1] === "(") {
152
+ // $((expr)) arithmetic — NOT a $(cmd) substitution, skip it
153
+ if (input[i + 2] === "(") {
154
+ result += ch;
155
+ i++;
156
+ continue;
157
+ }
158
+ // Find matching ) with depth tracking
159
+ let depth = 0;
160
+ let j = i + 1;
161
+ while (j < input.length) {
162
+ if (input[j] === "(")
163
+ depth++;
164
+ else if (input[j] === ")") {
165
+ depth--;
166
+ if (depth === 0)
167
+ break;
168
+ }
169
+ j++;
170
+ }
171
+ const sub = input.slice(i + 2, j).trim();
172
+ const out = (await runCmd(sub)).replace(/\n$/, "");
173
+ result += out;
174
+ i = j + 1;
175
+ continue;
176
+ }
177
+ result += ch;
178
+ i++;
179
+ }
180
+ input = result;
181
+ }
182
+ return expandSync(input, env, lastExit);
183
+ }