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,421 +0,0 @@
1
- /** biome-ignore-all lint/style/useNamingConvention: ENV VARIABLES */
2
- import { executeStatements } from "../SSHMimic/executor";
3
- import type { VirtualShell } from "../VirtualShell";
4
- import { parseScript } from "../VirtualShell/shellParser";
5
- import type {
6
- CommandMode,
7
- CommandResult,
8
- ShellEnv,
9
- } from "../types/commands";
10
- import { expandAsync, expandBraces } from "../utils/expand";
11
- import { tokenizeCommand } from "../utils/tokenize";
12
- import { resolveModule } from "./registry";
13
-
14
- /** Returns the home directory path for a given user. Root lives at /root. */
15
- export function userHome(authUser: string): string {
16
- return authUser === "root" ? "/root" : `/home/${authUser}`;
17
- }
18
-
19
- export function makeDefaultEnv(authUser: string, hostname: string): ShellEnv {
20
- return {
21
- vars: {
22
- PATH: "/usr/local/bin:/usr/bin:/bin",
23
- HOME: userHome(authUser),
24
- USER: authUser,
25
- LOGNAME: authUser,
26
- SHELL: "/bin/sh",
27
- TERM: "xterm-256color",
28
- HOSTNAME: hostname,
29
- PS1: "\\u@\\h:\\w\\$ ",
30
- },
31
- lastExitCode: 0,
32
- };
33
- }
34
-
35
- function resolveVfsBinary(
36
- name: string,
37
- env: ShellEnv,
38
- shell: VirtualShell,
39
- authUser: string,
40
- ): string | null {
41
- if (name.startsWith("/")) {
42
- if (!shell.vfs.exists(name)) return null;
43
- try {
44
- const st = shell.vfs.stat(name);
45
- if (st.type !== "file") return null;
46
- if (!(st.mode & 0o111)) return null;
47
- if (
48
- (name.startsWith("/sbin/") || name.startsWith("/usr/sbin/")) &&
49
- authUser !== "root"
50
- )
51
- return null;
52
- return name;
53
- } catch {
54
- return null;
55
- }
56
- }
57
-
58
- const pathDirs = (env.vars.PATH ?? "/usr/local/bin:/usr/bin:/bin").split(":");
59
- for (const dir of pathDirs) {
60
- if ((dir === "/sbin" || dir === "/usr/sbin") && authUser !== "root")
61
- continue;
62
- const full = `${dir}/${name}`;
63
- if (!shell.vfs.exists(full)) continue;
64
- try {
65
- const st = shell.vfs.stat(full);
66
- if (st.type !== "file") continue;
67
- if (!(st.mode & 0o111)) continue;
68
- return full;
69
- } catch {}
70
- }
71
- return null;
72
- }
73
-
74
- const MAX_CALL_DEPTH = 8;
75
- let _callDepth = 0;
76
-
77
- export async function runCommandDirect(
78
- name: string,
79
- args: string[],
80
- authUser: string,
81
- hostname: string,
82
- mode: CommandMode,
83
- cwd: string,
84
- shell: VirtualShell,
85
- stdin: string | undefined,
86
- env: ShellEnv,
87
- ): Promise<CommandResult> {
88
- // Anti-loop guard: track call depth via env to avoid infinite recursion
89
- _callDepth++;
90
- // console.debug(`[depth=${_callDepth}] runCommandDirect: ${name}`);
91
- if (_callDepth > MAX_CALL_DEPTH) {
92
- _callDepth--;
93
- // console.debug(`[LOOP DETECTED] runCommandDirect blocked: ${name}`);
94
- return { stderr: `${name}: maximum call depth (${MAX_CALL_DEPTH}) exceeded`, exitCode: 126 };
95
- }
96
- try {
97
- return await _runCommandDirectInner(name, args, authUser, hostname, mode, cwd, shell, stdin, env);
98
- } finally {
99
- _callDepth--;
100
- }
101
- }
102
-
103
- async function _runCommandDirectInner(
104
- name: string,
105
- args: string[],
106
- authUser: string,
107
- hostname: string,
108
- mode: CommandMode,
109
- cwd: string,
110
- shell: VirtualShell,
111
- stdin: string | undefined,
112
- env: ShellEnv,
113
- ): Promise<CommandResult> {
114
- const assignRe = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
115
- const invocation = [name, ...args];
116
- let assignCount = 0;
117
- while (assignCount < invocation.length && assignRe.test(invocation[assignCount]!)) {
118
- assignCount += 1;
119
- }
120
- if (assignCount > 0) {
121
- const assignments = invocation.slice(0, assignCount).map((token) => token.match(assignRe)!);
122
- const remaining = invocation.slice(assignCount);
123
- const restored: Array<[string, string | undefined]> = [];
124
- for (const [, key, value] of assignments) {
125
- restored.push([key!, env.vars[key!]]);
126
- env.vars[key!] = value!;
127
- }
128
- if (remaining.length === 0) {
129
- return { exitCode: 0 };
130
- }
131
- try {
132
- const result = await runCommandDirect(
133
- remaining[0]!,
134
- remaining.slice(1),
135
- authUser,
136
- hostname,
137
- mode,
138
- cwd,
139
- shell,
140
- stdin,
141
- env,
142
- );
143
- return result;
144
- } finally {
145
- for (const [key, value] of restored) {
146
- if (value === undefined) delete env.vars[key];
147
- else env.vars[key] = value;
148
- }
149
- }
150
- }
151
-
152
- const aliasVal = env.vars[`__alias_${name}`];
153
- if (aliasVal) {
154
- return runCommand(
155
- `${aliasVal} ${args.join(" ")}`,
156
- authUser,
157
- hostname,
158
- mode,
159
- cwd,
160
- shell,
161
- stdin,
162
- env,
163
- );
164
- }
165
-
166
- const mod = resolveModule(name);
167
- if (!mod) {
168
- const vfsBinary = resolveVfsBinary(name, env, shell, authUser);
169
- if (vfsBinary) {
170
- const stubContent = shell.vfs.readFile(vfsBinary);
171
- const builtinMatch = stubContent.match(/exec\s+builtin\s+(\S+)/);
172
- if (builtinMatch) {
173
- const builtinMod = resolveModule(builtinMatch[1]!);
174
- if (builtinMod) {
175
- return await builtinMod.run({
176
- authUser,
177
- hostname,
178
- activeSessions: shell.users.listActiveSessions(),
179
- rawInput: [name, ...args].join(" "),
180
- mode,
181
- args,
182
- stdin,
183
- cwd,
184
- shell,
185
- env,
186
- });
187
- }
188
- // builtin not found — stop here, don't fall through to sh -c (avoids infinite loop)
189
- return { stderr: `${name}: exec builtin '${builtinMatch[1]}' not found`, exitCode: 127 };
190
- }
191
- const shMod = resolveModule("sh");
192
- if (shMod) {
193
- return await shMod.run({
194
- authUser,
195
- hostname,
196
- activeSessions: shell.users.listActiveSessions(),
197
- rawInput: `sh -c ${JSON.stringify(stubContent)}`,
198
- mode,
199
- args: ["-c", stubContent, "--", ...args],
200
- stdin,
201
- cwd,
202
- shell,
203
- env,
204
- });
205
- }
206
- }
207
- return { stderr: `${name}: command not found`, exitCode: 127 };
208
- }
209
-
210
- try {
211
- return await mod.run({
212
- authUser,
213
- hostname,
214
- activeSessions: shell.users.listActiveSessions(),
215
- rawInput: [name, ...args].join(" "),
216
- mode,
217
- args,
218
- stdin,
219
- cwd,
220
- shell,
221
- env,
222
- });
223
- } catch (error: unknown) {
224
- return {
225
- stderr: error instanceof Error ? error.message : "Command failed",
226
- exitCode: 1,
227
- };
228
- }
229
- }
230
-
231
- export async function runCommand(
232
- rawInput: string,
233
- authUser: string,
234
- hostname: string,
235
- mode: CommandMode,
236
- cwd: string,
237
- shell: VirtualShell,
238
- stdin?: string,
239
- env?: ShellEnv,
240
- ): Promise<CommandResult> {
241
- const trimmed = rawInput.trim();
242
- if (trimmed.length === 0) return { exitCode: 0 };
243
-
244
- const shellEnv: ShellEnv = env ?? makeDefaultEnv(authUser, hostname);
245
-
246
- // Anti-loop guard: check depth here too — catches sh -c recursive calls
247
- _callDepth++;
248
- // console.debug(`[depth=${_callDepth}] runCommand: ${trimmed.slice(0, 60)}`);
249
- if (_callDepth > MAX_CALL_DEPTH) {
250
- _callDepth--;
251
- // console.debug(`[LOOP DETECTED] runCommand blocked: ${trimmed.slice(0, 60)}`);
252
- return { stderr: `${trimmed.split(" ")[0]}: maximum call depth (${MAX_CALL_DEPTH}) exceeded`, exitCode: 126 };
253
- }
254
- try {
255
- const rawTokens = tokenizeCommand(trimmed);
256
- const rawFirstWord = rawTokens[0]?.toLowerCase() ?? "";
257
- const aliasVal = shellEnv.vars[`__alias_${rawFirstWord}`];
258
- const aliasExpanded = aliasVal
259
- ? trimmed.replace(rawFirstWord, aliasVal)
260
- : trimmed;
261
-
262
- // Detect sh-syntax constructs that must be handled by the sh interpreter
263
- const isShScript =
264
- /\bfor\s+\w+\s+in\b/.test(aliasExpanded) ||
265
- /\bwhile\s+/.test(aliasExpanded) ||
266
- /\bif\s+/.test(aliasExpanded) ||
267
- /\w+\s*\(\s*\)\s*\{/.test(aliasExpanded) ||
268
- /\bfunction\s+\w+/.test(aliasExpanded) ||
269
- /\(\(\s*.+\s*\)\)/.test(aliasExpanded);
270
-
271
- const hasOperators =
272
- /(?<![|&])[|](?![|])/.test(aliasExpanded) ||
273
- aliasExpanded.includes(">") ||
274
- aliasExpanded.includes("<") ||
275
- aliasExpanded.includes("&&") ||
276
- aliasExpanded.includes("||") ||
277
- aliasExpanded.includes(";");
278
-
279
- if ((isShScript && rawFirstWord !== "sh" && rawFirstWord !== "bash") || hasOperators) {
280
- // sh-syntax: route through sh interpreter to handle for/while/functions
281
- if (isShScript && rawFirstWord !== "sh" && rawFirstWord !== "bash") {
282
- const shMod = resolveModule("sh");
283
- if (shMod) {
284
- return await shMod.run({
285
- authUser, hostname,
286
- activeSessions: shell.users.listActiveSessions(),
287
- rawInput: aliasExpanded,
288
- mode,
289
- args: ["-c", aliasExpanded],
290
- stdin: undefined,
291
- cwd,
292
- shell,
293
- env: shellEnv,
294
- });
295
- }
296
- }
297
- const script = parseScript(aliasExpanded);
298
- if (!script.isValid)
299
- return { stderr: script.error || "Syntax error", exitCode: 1 };
300
- try {
301
- return await executeStatements(
302
- script.statements,
303
- authUser,
304
- hostname,
305
- mode,
306
- cwd,
307
- shell,
308
- shellEnv,
309
- );
310
- } catch (error: unknown) {
311
- return {
312
- stderr: error instanceof Error ? error.message : "Execution failed",
313
- exitCode: 1,
314
- };
315
- }
316
- }
317
-
318
- const expanded = await expandAsync(
319
- aliasExpanded,
320
- shellEnv.vars,
321
- shellEnv.lastExitCode,
322
- (sub) =>
323
- runCommand(
324
- sub,
325
- authUser,
326
- hostname,
327
- mode,
328
- cwd,
329
- shell,
330
- undefined,
331
- shellEnv,
332
- ).then((r) => r.stdout ?? ""),
333
- );
334
-
335
- const parts = tokenizeCommand(expanded.trim());
336
- if (parts.length === 0) return { exitCode: 0 };
337
- const assignRe = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
338
- if (assignRe.test(parts[0]!)) {
339
- return runCommandDirect(
340
- parts[0]!,
341
- parts.slice(1),
342
- authUser,
343
- hostname,
344
- mode,
345
- cwd,
346
- shell,
347
- stdin,
348
- shellEnv,
349
- );
350
- }
351
- const commandName = parts[0]?.toLowerCase() ?? "";
352
- // Apply brace expansion to each arg token
353
- const args = parts.slice(1).flatMap(expandBraces);
354
- const mod = resolveModule(commandName);
355
-
356
- if (!mod) {
357
- const vfsBinary = resolveVfsBinary(commandName, shellEnv, shell, authUser);
358
- if (vfsBinary) {
359
- const stubContent = shell.vfs.readFile(vfsBinary);
360
- const builtinMatch = stubContent.match(/exec\s+builtin\s+(\S+)/);
361
- if (builtinMatch) {
362
- const builtinName = builtinMatch[1]!;
363
- const builtinMod = resolveModule(builtinName);
364
- if (builtinMod) {
365
- return await builtinMod.run({
366
- authUser,
367
- hostname,
368
- activeSessions: shell.users.listActiveSessions(),
369
- rawInput: [commandName, ...args].join(" "),
370
- mode,
371
- args,
372
- stdin,
373
- cwd,
374
- shell,
375
- env: shellEnv,
376
- });
377
- }
378
- }
379
- const shMod = resolveModule("sh");
380
- if (shMod) {
381
- return await shMod.run({
382
- authUser,
383
- hostname,
384
- activeSessions: shell.users.listActiveSessions(),
385
- rawInput: `sh -c ${JSON.stringify(stubContent)}`,
386
- mode,
387
- args: ["-c", stubContent, "--", ...args],
388
- stdin,
389
- cwd,
390
- shell,
391
- env: shellEnv,
392
- });
393
- }
394
- }
395
-
396
- return { stderr: `${commandName}: command not found`, exitCode: 127 };
397
- }
398
-
399
- try {
400
- return await mod.run({
401
- authUser,
402
- hostname,
403
- activeSessions: shell.users.listActiveSessions(),
404
- rawInput: expanded,
405
- mode,
406
- args,
407
- stdin,
408
- cwd,
409
- shell,
410
- env: shellEnv,
411
- });
412
- } catch (error: unknown) {
413
- return {
414
- stderr: error instanceof Error ? error.message : "Command failed",
415
- exitCode: 1,
416
- };
417
- }
418
- } finally {
419
- _callDepth--;
420
- }
421
- }
@@ -1,68 +0,0 @@
1
- import type { ShellModule } from "../types/commands";
2
- import { getFlag, ifFlag } from "./command-helpers";
3
- import { resolvePath } from "./helpers";
4
-
5
- /**
6
- * Stream editor for filtering and transforming text lines.
7
- * @category text
8
- * @params ["-e <expr> [file]", "s/pattern/replace/[g]"]
9
- */
10
- export const sedCommand: ShellModule = {
11
- name: "sed",
12
- description: "Stream editor for filtering and transforming text",
13
- category: "text",
14
- params: ["-e <expr> [file]", "s/pattern/replace/[g]"],
15
- run: ({ authUser, shell, cwd, args, stdin }) => {
16
- const inPlace = ifFlag(args, ["-i"]);
17
- const expr =
18
- (getFlag(args, ["-e"]) as string | undefined) ??
19
- args.find((a) => !a.startsWith("-"));
20
- const fileArg = args.filter((a) => !a.startsWith("-") && a !== expr).pop();
21
-
22
- if (!expr) return { stderr: "sed: no expression", exitCode: 1 };
23
-
24
- let content = stdin ?? "";
25
- if (fileArg) {
26
- const p = resolvePath(cwd, fileArg);
27
- try {
28
- content = shell.vfs.readFile(p);
29
- } catch {
30
- return {
31
- stderr: `sed: ${fileArg}: No such file or directory`,
32
- exitCode: 1,
33
- };
34
- }
35
- }
36
-
37
- // Parse s/from/to/[g]
38
- const sMatch = expr.match(/^s([^a-zA-Z0-9])(.+?)\1(.*?)\1([gi]*)$/);
39
- if (!sMatch)
40
- return { stderr: `sed: unrecognized command: ${expr}`, exitCode: 1 };
41
-
42
- const [, , from, to, flags] = sMatch;
43
- const regexFlags = (flags ?? "").includes("i")
44
- ? "gi"
45
- : (flags ?? "").includes("g")
46
- ? "g"
47
- : "";
48
- let regex: RegExp;
49
- try {
50
- regex = new RegExp(from!, regexFlags || "");
51
- } catch (_e) {
52
- return { stderr: `sed: invalid regex: ${from}`, exitCode: 1 };
53
- }
54
-
55
- const result =
56
- (flags ?? "").includes("g") || regexFlags.includes("g")
57
- ? content.replace(regex, to ?? "")
58
- : content.replace(regex, to ?? "");
59
-
60
- if (inPlace && fileArg) {
61
- const p = resolvePath(cwd, fileArg);
62
- shell.writeFileAsUser(authUser, p, result);
63
- return { exitCode: 0 };
64
- }
65
-
66
- return { stdout: result, exitCode: 0 };
67
- },
68
- };
@@ -1,43 +0,0 @@
1
- import type { ShellModule } from "../types/commands";
2
-
3
- /** Generate sequences of numbers. seq LAST / seq FIRST LAST / seq FIRST INCREMENT LAST */
4
- export const seqCommand: ShellModule = {
5
- name: "seq",
6
- description: "Print a sequence of numbers",
7
- category: "text",
8
- params: ["[FIRST [INCREMENT]] LAST"],
9
- run: ({ args }) => {
10
- const nums = args.filter((a) => !a.startsWith("-") || /^-[\d.]/.test(a)).map(Number);
11
- const sep = (() => { const i = args.indexOf("-s"); return i !== -1 ? (args[i + 1] ?? "\n") : "\n"; })();
12
- const fmt = (() => { const i = args.indexOf("-f"); return i !== -1 ? (args[i + 1] ?? "%g") : null; })();
13
- const width = args.includes("-w");
14
-
15
- let first = 1, inc = 1, last: number;
16
- if (nums.length === 1) { last = nums[0]!; }
17
- else if (nums.length === 2) { first = nums[0]!; last = nums[1]!; }
18
- else { first = nums[0]!; inc = nums[1]!; last = nums[2]!; }
19
-
20
- if (inc === 0) return { stderr: "seq: zero increment\n", exitCode: 1 };
21
- if ((inc > 0 && first > last) || (inc < 0 && first < last)) return { stdout: "", exitCode: 0 };
22
-
23
- const results: string[] = [];
24
- const maxSteps = 100000;
25
- let steps = 0;
26
- for (let n = first; inc > 0 ? n <= last : n >= last; n = Math.round((n + inc) * 1e10) / 1e10) {
27
- if (++steps > maxSteps) break;
28
- let s: string;
29
- if (fmt) {
30
- s = fmt.replace("%g", String(n)).replace("%f", n.toFixed(6)).replace("%d", String(Math.trunc(n)));
31
- } else {
32
- s = Number.isInteger(n) ? String(n) : n.toPrecision(12).replace(/\.?0+$/, "");
33
- }
34
- if (width) {
35
- const maxLen = String(Math.trunc(last)).length;
36
- s = s.padStart(maxLen, "0");
37
- }
38
- results.push(s);
39
- }
40
-
41
- return { stdout: `${results.join(sep)}\n`, exitCode: 0 };
42
- },
43
- };
@@ -1,29 +0,0 @@
1
- /** biome-ignore-all lint/style/useNamingConvention: env variables */
2
- import type { ShellModule } from "../types/commands";
3
-
4
- /**
5
- * Display or set shell variables and options.
6
- * @category shell
7
- * @params ["[VAR=value]"]
8
- */
9
- export const setCommand: ShellModule = {
10
- name: "set",
11
- description: "Display or set shell variables",
12
- category: "shell",
13
- params: ["[VAR=value]"],
14
- run: ({ args, env }) => {
15
- if (args.length === 0) {
16
- const out = Object.entries(env.vars)
17
- .map(([k, v]) => `${k}=${v}`)
18
- .join("\n");
19
- return { stdout: out, exitCode: 0 };
20
- }
21
- for (const arg of args) {
22
- if (arg.includes("=")) {
23
- const eq = arg.indexOf("=");
24
- env.vars[arg.slice(0, eq)] = arg.slice(eq + 1);
25
- }
26
- }
27
- return { exitCode: 0 };
28
- },
29
- };