typescript-virtual-container 1.2.4 → 1.2.6

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 (267) hide show
  1. package/README.md +1056 -1239
  2. package/benchmark-results.txt +20 -20
  3. package/dist/SSHMimic/exec.js +2 -2
  4. package/dist/SSHMimic/executor.d.ts +6 -7
  5. package/dist/SSHMimic/executor.d.ts.map +1 -1
  6. package/dist/SSHMimic/executor.js +77 -60
  7. package/dist/SSHMimic/index.d.ts +19 -2
  8. package/dist/SSHMimic/index.d.ts.map +1 -1
  9. package/dist/SSHMimic/index.js +106 -24
  10. package/dist/SSHMimic/sftp.d.ts.map +1 -1
  11. package/dist/SSHMimic/sftp.js +14 -0
  12. package/dist/VirtualFileSystem/index.d.ts +115 -88
  13. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  14. package/dist/VirtualFileSystem/index.js +389 -264
  15. package/dist/VirtualShell/index.d.ts +3 -4
  16. package/dist/VirtualShell/index.d.ts.map +1 -1
  17. package/dist/VirtualShell/index.js +4 -6
  18. package/dist/VirtualShell/shell.d.ts.map +1 -1
  19. package/dist/VirtualShell/shell.js +19 -2
  20. package/dist/VirtualShell/shellParser.d.ts +20 -2
  21. package/dist/VirtualShell/shellParser.d.ts.map +1 -1
  22. package/dist/VirtualShell/shellParser.js +229 -120
  23. package/dist/VirtualUserManager/index.d.ts +25 -0
  24. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  25. package/dist/VirtualUserManager/index.js +33 -0
  26. package/dist/commands/adduser.d.ts.map +1 -1
  27. package/dist/commands/adduser.js +2 -0
  28. package/dist/commands/awk.d.ts +3 -0
  29. package/dist/commands/awk.d.ts.map +1 -0
  30. package/dist/commands/awk.js +29 -0
  31. package/dist/commands/base64.d.ts +3 -0
  32. package/dist/commands/base64.d.ts.map +1 -0
  33. package/dist/commands/base64.js +20 -0
  34. package/dist/commands/cat.d.ts.map +1 -1
  35. package/dist/commands/cat.js +2 -0
  36. package/dist/commands/cd.d.ts.map +1 -1
  37. package/dist/commands/cd.js +2 -0
  38. package/dist/commands/chmod.d.ts +3 -0
  39. package/dist/commands/chmod.d.ts.map +1 -0
  40. package/dist/commands/chmod.js +33 -0
  41. package/dist/commands/clear.d.ts.map +1 -1
  42. package/dist/commands/clear.js +4 -1
  43. package/dist/commands/cp.d.ts +3 -0
  44. package/dist/commands/cp.d.ts.map +1 -0
  45. package/dist/commands/cp.js +70 -0
  46. package/dist/commands/curl.d.ts.map +1 -1
  47. package/dist/commands/curl.js +2 -0
  48. package/dist/commands/cut.d.ts +3 -0
  49. package/dist/commands/cut.d.ts.map +1 -0
  50. package/dist/commands/cut.js +27 -0
  51. package/dist/commands/date.d.ts +3 -0
  52. package/dist/commands/date.d.ts.map +1 -0
  53. package/dist/commands/date.js +22 -0
  54. package/dist/commands/deluser.d.ts.map +1 -1
  55. package/dist/commands/deluser.js +2 -0
  56. package/dist/commands/df.d.ts +3 -0
  57. package/dist/commands/df.d.ts.map +1 -0
  58. package/dist/commands/df.js +16 -0
  59. package/dist/commands/diff.d.ts +3 -0
  60. package/dist/commands/diff.d.ts.map +1 -0
  61. package/dist/commands/diff.js +40 -0
  62. package/dist/commands/du.d.ts +3 -0
  63. package/dist/commands/du.d.ts.map +1 -0
  64. package/dist/commands/du.js +39 -0
  65. package/dist/commands/echo.d.ts.map +1 -1
  66. package/dist/commands/echo.js +2 -0
  67. package/dist/commands/env.d.ts.map +1 -1
  68. package/dist/commands/env.js +6 -14
  69. package/dist/commands/export.d.ts.map +1 -1
  70. package/dist/commands/export.js +11 -21
  71. package/dist/commands/find.d.ts +3 -0
  72. package/dist/commands/find.d.ts.map +1 -0
  73. package/dist/commands/find.js +50 -0
  74. package/dist/commands/grep.d.ts.map +1 -1
  75. package/dist/commands/grep.js +58 -35
  76. package/dist/commands/groups.d.ts +3 -0
  77. package/dist/commands/groups.d.ts.map +1 -0
  78. package/dist/commands/groups.js +12 -0
  79. package/dist/commands/gzip.d.ts +4 -0
  80. package/dist/commands/gzip.d.ts.map +1 -0
  81. package/dist/commands/gzip.js +40 -0
  82. package/dist/commands/head.d.ts +3 -0
  83. package/dist/commands/head.d.ts.map +1 -0
  84. package/dist/commands/head.js +32 -0
  85. package/dist/commands/help.d.ts +1 -1
  86. package/dist/commands/help.d.ts.map +1 -1
  87. package/dist/commands/help.js +75 -3
  88. package/dist/commands/hostname.d.ts.map +1 -1
  89. package/dist/commands/hostname.js +2 -0
  90. package/dist/commands/htop.d.ts.map +1 -1
  91. package/dist/commands/htop.js +2 -0
  92. package/dist/commands/id.d.ts +3 -0
  93. package/dist/commands/id.d.ts.map +1 -0
  94. package/dist/commands/id.js +14 -0
  95. package/dist/commands/index.d.ts +5 -2
  96. package/dist/commands/index.d.ts.map +1 -1
  97. package/dist/commands/index.js +104 -87
  98. package/dist/commands/kill.d.ts +3 -0
  99. package/dist/commands/kill.d.ts.map +1 -0
  100. package/dist/commands/kill.js +13 -0
  101. package/dist/commands/ln.d.ts +3 -0
  102. package/dist/commands/ln.d.ts.map +1 -0
  103. package/dist/commands/ln.js +44 -0
  104. package/dist/commands/ls.d.ts.map +1 -1
  105. package/dist/commands/ls.js +2 -0
  106. package/dist/commands/mkdir.d.ts.map +1 -1
  107. package/dist/commands/mkdir.js +2 -0
  108. package/dist/commands/mv.d.ts +3 -0
  109. package/dist/commands/mv.d.ts.map +1 -0
  110. package/dist/commands/mv.js +37 -0
  111. package/dist/commands/nano.d.ts.map +1 -1
  112. package/dist/commands/nano.js +2 -0
  113. package/dist/commands/neofetch.d.ts.map +1 -1
  114. package/dist/commands/neofetch.js +2 -0
  115. package/dist/commands/passwd.d.ts.map +1 -1
  116. package/dist/commands/passwd.js +2 -0
  117. package/dist/commands/ping.d.ts +3 -0
  118. package/dist/commands/ping.d.ts.map +1 -0
  119. package/dist/commands/ping.js +18 -0
  120. package/dist/commands/ps.d.ts +3 -0
  121. package/dist/commands/ps.d.ts.map +1 -0
  122. package/dist/commands/ps.js +17 -0
  123. package/dist/commands/pwd.d.ts.map +1 -1
  124. package/dist/commands/pwd.js +2 -0
  125. package/dist/commands/rm.d.ts.map +1 -1
  126. package/dist/commands/rm.js +2 -0
  127. package/dist/commands/sed.d.ts +3 -0
  128. package/dist/commands/sed.d.ts.map +1 -0
  129. package/dist/commands/sed.js +47 -0
  130. package/dist/commands/set.d.ts +3 -0
  131. package/dist/commands/set.d.ts.map +1 -1
  132. package/dist/commands/set.js +19 -46
  133. package/dist/commands/sh.d.ts +0 -1
  134. package/dist/commands/sh.d.ts.map +1 -1
  135. package/dist/commands/sh.js +228 -35
  136. package/dist/commands/sleep.d.ts +3 -0
  137. package/dist/commands/sleep.d.ts.map +1 -0
  138. package/dist/commands/sleep.js +13 -0
  139. package/dist/commands/sort.d.ts +3 -0
  140. package/dist/commands/sort.d.ts.map +1 -0
  141. package/dist/commands/sort.js +37 -0
  142. package/dist/commands/su.d.ts.map +1 -1
  143. package/dist/commands/su.js +2 -0
  144. package/dist/commands/sudo.d.ts.map +1 -1
  145. package/dist/commands/sudo.js +2 -0
  146. package/dist/commands/tail.d.ts +3 -0
  147. package/dist/commands/tail.d.ts.map +1 -0
  148. package/dist/commands/tail.js +35 -0
  149. package/dist/commands/tar.d.ts +3 -0
  150. package/dist/commands/tar.d.ts.map +1 -0
  151. package/dist/commands/tar.js +64 -0
  152. package/dist/commands/tee.d.ts +3 -0
  153. package/dist/commands/tee.d.ts.map +1 -0
  154. package/dist/commands/tee.js +29 -0
  155. package/dist/commands/touch.d.ts.map +1 -1
  156. package/dist/commands/touch.js +2 -0
  157. package/dist/commands/tr.d.ts +3 -0
  158. package/dist/commands/tr.d.ts.map +1 -0
  159. package/dist/commands/tr.js +24 -0
  160. package/dist/commands/tree.d.ts.map +1 -1
  161. package/dist/commands/tree.js +2 -0
  162. package/dist/commands/uname.d.ts +3 -0
  163. package/dist/commands/uname.d.ts.map +1 -0
  164. package/dist/commands/uname.js +21 -0
  165. package/dist/commands/uniq.d.ts +3 -0
  166. package/dist/commands/uniq.d.ts.map +1 -0
  167. package/dist/commands/uniq.js +33 -0
  168. package/dist/commands/unset.d.ts.map +1 -1
  169. package/dist/commands/unset.js +6 -10
  170. package/dist/commands/wc.d.ts +3 -0
  171. package/dist/commands/wc.d.ts.map +1 -0
  172. package/dist/commands/wc.js +50 -0
  173. package/dist/commands/wget.d.ts.map +1 -1
  174. package/dist/commands/wget.js +2 -0
  175. package/dist/commands/who.d.ts.map +1 -1
  176. package/dist/commands/who.js +2 -0
  177. package/dist/commands/whoami.d.ts.map +1 -1
  178. package/dist/commands/whoami.js +2 -0
  179. package/dist/commands/xargs.d.ts +3 -0
  180. package/dist/commands/xargs.d.ts.map +1 -0
  181. package/dist/commands/xargs.js +16 -0
  182. package/dist/index.d.ts +1 -0
  183. package/dist/index.d.ts.map +1 -1
  184. package/dist/types/commands.d.ts +13 -0
  185. package/dist/types/commands.d.ts.map +1 -1
  186. package/dist/types/pipeline.d.ts +20 -0
  187. package/dist/types/pipeline.d.ts.map +1 -1
  188. package/package.json +5 -2
  189. package/scripts/publish-package.sh +70 -0
  190. package/src/SSHMimic/exec.ts +2 -2
  191. package/src/SSHMimic/executor.ts +95 -98
  192. package/src/SSHMimic/index.ts +138 -57
  193. package/src/SSHMimic/sftp.ts +15 -0
  194. package/src/VirtualFileSystem/index.ts +464 -292
  195. package/src/VirtualShell/index.ts +4 -6
  196. package/src/VirtualShell/shell.ts +19 -2
  197. package/src/VirtualShell/shellParser.ts +202 -168
  198. package/src/VirtualUserManager/index.ts +36 -0
  199. package/src/commands/adduser.ts +2 -0
  200. package/src/commands/awk.ts +30 -0
  201. package/src/commands/base64.ts +18 -0
  202. package/src/commands/cat.ts +2 -0
  203. package/src/commands/cd.ts +2 -0
  204. package/src/commands/chmod.ts +35 -0
  205. package/src/commands/clear.ts +4 -1
  206. package/src/commands/cp.ts +78 -0
  207. package/src/commands/curl.ts +2 -0
  208. package/src/commands/cut.ts +29 -0
  209. package/src/commands/date.ts +24 -0
  210. package/src/commands/deluser.ts +2 -0
  211. package/src/commands/df.ts +18 -0
  212. package/src/commands/diff.ts +29 -0
  213. package/src/commands/du.ts +39 -0
  214. package/src/commands/echo.ts +2 -0
  215. package/src/commands/env.ts +6 -16
  216. package/src/commands/export.ts +11 -24
  217. package/src/commands/find.ts +63 -0
  218. package/src/commands/grep.ts +51 -38
  219. package/src/commands/groups.ts +14 -0
  220. package/src/commands/gzip.ts +31 -0
  221. package/src/commands/head.ts +37 -0
  222. package/src/commands/help.ts +81 -3
  223. package/src/commands/hostname.ts +2 -0
  224. package/src/commands/htop.ts +2 -0
  225. package/src/commands/id.ts +16 -0
  226. package/src/commands/index.ts +114 -133
  227. package/src/commands/kill.ts +14 -0
  228. package/src/commands/ln.ts +49 -0
  229. package/src/commands/ls.ts +2 -0
  230. package/src/commands/mkdir.ts +2 -0
  231. package/src/commands/mv.ts +45 -0
  232. package/src/commands/nano.ts +2 -0
  233. package/src/commands/neofetch.ts +2 -0
  234. package/src/commands/passwd.ts +2 -0
  235. package/src/commands/ping.ts +20 -0
  236. package/src/commands/ps.ts +19 -0
  237. package/src/commands/pwd.ts +2 -0
  238. package/src/commands/rm.ts +2 -0
  239. package/src/commands/sed.ts +45 -0
  240. package/src/commands/set.ts +19 -50
  241. package/src/commands/sh.ts +192 -43
  242. package/src/commands/sleep.ts +14 -0
  243. package/src/commands/sort.ts +37 -0
  244. package/src/commands/su.ts +2 -0
  245. package/src/commands/sudo.ts +2 -0
  246. package/src/commands/tail.ts +39 -0
  247. package/src/commands/tar.ts +58 -0
  248. package/src/commands/tee.ts +25 -0
  249. package/src/commands/touch.ts +2 -0
  250. package/src/commands/tr.ts +24 -0
  251. package/src/commands/tree.ts +2 -0
  252. package/src/commands/uname.ts +20 -0
  253. package/src/commands/uniq.ts +28 -0
  254. package/src/commands/unset.ts +5 -12
  255. package/src/commands/wc.ts +50 -0
  256. package/src/commands/wget.ts +2 -0
  257. package/src/commands/who.ts +2 -0
  258. package/src/commands/whoami.ts +2 -0
  259. package/src/commands/xargs.ts +17 -0
  260. package/src/index.ts +1 -0
  261. package/src/types/commands.ts +14 -0
  262. package/src/types/pipeline.ts +23 -0
  263. package/standalone.js +93 -55
  264. package/standalone.js.map +4 -4
  265. package/tests/bun-test-shim.ts +1 -0
  266. package/tests/sftp.test.ts +115 -191
  267. package/tests/users.test.ts +42 -88
@@ -1,124 +1,134 @@
1
1
  import { adduserCommand } from "./adduser";
2
+ import { awkCommand } from "./awk";
3
+ import { base64Command } from "./base64";
2
4
  import { catCommand } from "./cat";
3
5
  import { cdCommand } from "./cd";
6
+ import { chmodCommand } from "./chmod";
4
7
  import { clearCommand } from "./clear";
8
+ import { cpCommand } from "./cp";
5
9
  import { curlCommand } from "./curl";
10
+ import { cutCommand } from "./cut";
11
+ import { dateCommand } from "./date";
6
12
  import { deluserCommand } from "./deluser";
13
+ import { dfCommand } from "./df";
14
+ import { diffCommand } from "./diff";
15
+ import { duCommand } from "./du";
7
16
  import { echoCommand } from "./echo";
8
17
  import { envCommand } from "./env";
9
18
  import { exitCommand } from "./exit";
10
19
  import { exportCommand } from "./export";
20
+ import { findCommand } from "./find";
11
21
  import { grepCommand } from "./grep";
22
+ import { groupsCommand } from "./groups";
23
+ import { gunzipCommand, gzipCommand } from "./gzip";
24
+ import { headCommand } from "./head";
12
25
  import { createHelpCommand } from "./help";
13
26
  import { hostnameCommand } from "./hostname";
14
27
  import { htopCommand } from "./htop";
28
+ import { idCommand } from "./id";
29
+ import { killCommand } from "./kill";
30
+ import { lnCommand } from "./ln";
15
31
  import { lsCommand } from "./ls";
16
32
  import { mkdirCommand } from "./mkdir";
33
+ import { mvCommand } from "./mv";
17
34
  import { nanoCommand } from "./nano";
18
35
  import { neofetchCommand } from "./neofetch";
19
36
  import { passwdCommand } from "./passwd";
37
+ import { pingCommand } from "./ping";
38
+ import { psCommand } from "./ps";
20
39
  import { pwdCommand } from "./pwd";
21
40
  import { rmCommand } from "./rm";
41
+ import { sedCommand } from "./sed";
22
42
  import { setCommand } from "./set";
23
43
  import { shCommand } from "./sh";
44
+ import { sleepCommand } from "./sleep";
45
+ import { sortCommand } from "./sort";
24
46
  import { suCommand } from "./su";
25
47
  import { sudoCommand } from "./sudo";
48
+ import { tailCommand } from "./tail";
49
+ import { tarCommand } from "./tar";
50
+ import { teeCommand } from "./tee";
26
51
  import { touchCommand } from "./touch";
52
+ import { trCommand } from "./tr";
27
53
  import { treeCommand } from "./tree";
54
+ import { unameCommand } from "./uname";
55
+ import { uniqCommand } from "./uniq";
28
56
  import { unsetCommand } from "./unset";
57
+ import { wcCommand } from "./wc";
29
58
  import { wgetCommand } from "./wget";
30
59
  import { whoCommand } from "./who";
31
60
  import { whoamiCommand } from "./whoami";
61
+ import { xargsCommand } from "./xargs";
32
62
  const BASE_COMMANDS = [
33
- pwdCommand,
34
- whoamiCommand,
35
- whoCommand,
36
- hostnameCommand,
37
- lsCommand,
38
- cdCommand,
39
- catCommand,
40
- echoCommand,
41
- mkdirCommand,
42
- touchCommand,
43
- rmCommand,
44
- treeCommand,
45
- nanoCommand,
63
+ // Navigation
64
+ pwdCommand, cdCommand, lsCommand, treeCommand,
65
+ // Files
66
+ catCommand, touchCommand, rmCommand, mkdirCommand, cpCommand, mvCommand, lnCommand,
67
+ chmodCommand, findCommand,
68
+ // Text processing
69
+ grepCommand, sedCommand, awkCommand, sortCommand, uniqCommand, wcCommand,
70
+ headCommand, tailCommand, cutCommand, trCommand, teeCommand, xargsCommand,
71
+ diffCommand,
72
+ // Archives
73
+ tarCommand, gzipCommand, gunzipCommand, base64Command,
74
+ // System info
75
+ whoamiCommand, whoCommand, hostnameCommand, idCommand, groupsCommand, unameCommand,
76
+ psCommand, killCommand, dfCommand, duCommand, dateCommand, sleepCommand, pingCommand,
77
+ // Shell
78
+ echoCommand, envCommand, exportCommand, setCommand, unsetCommand, shCommand,
79
+ clearCommand, exitCommand,
80
+ // Editors
81
+ nanoCommand, htopCommand,
82
+ // Network
83
+ curlCommand, wgetCommand,
84
+ // Users
85
+ adduserCommand, passwdCommand, deluserCommand, sudoCommand, suCommand,
86
+ // Misc
46
87
  neofetchCommand,
47
- htopCommand,
48
- adduserCommand,
49
- passwdCommand,
50
- deluserCommand,
51
- sudoCommand,
52
- suCommand,
53
- curlCommand,
54
- envCommand,
55
- wgetCommand,
56
- grepCommand,
57
- exportCommand,
58
- setCommand,
59
- unsetCommand,
60
- shCommand,
61
- clearCommand,
62
- exitCommand,
63
88
  ];
64
89
  const customCommands = [];
65
- const helpCommand = createHelpCommand(() => getCommandModules().map((cmd) => cmd.name));
66
90
  const commandRegistry = new Map();
67
91
  let cachedCommandNames = null;
92
+ const helpCommand = createHelpCommand(() => getCommandModules().map((cmd) => cmd.name));
68
93
  function buildCache() {
94
+ commandRegistry.clear();
69
95
  for (const mod of getCommandModules()) {
70
96
  commandRegistry.set(mod.name, mod);
71
- for (const alias of mod.aliases ?? []) {
97
+ for (const alias of mod.aliases ?? [])
72
98
  commandRegistry.set(alias, mod);
73
- }
74
99
  }
75
100
  cachedCommandNames = Array.from(commandRegistry.keys()).sort();
76
101
  }
77
102
  function getCommandModules() {
78
- // console.log("Loading command modules...");
79
- // console.log(
80
- // `Base commands: ${BASE_COMMANDS.map((cmd) => cmd.name).join(", ")}`,
81
- // );
82
- // console.log(
83
- // `Custom commands: ${customCommands.map((cmd) => cmd.name).join(", ")}`,
84
- // );
85
103
  return [...BASE_COMMANDS, ...customCommands, helpCommand];
86
104
  }
87
105
  export function registerCommand(module) {
88
106
  const normalized = {
89
107
  ...module,
90
108
  name: module.name.trim().toLowerCase(),
91
- aliases: module.aliases?.map((alias) => alias.trim().toLowerCase()),
109
+ aliases: module.aliases?.map((a) => a.trim().toLowerCase()),
92
110
  };
93
111
  const names = [normalized.name, ...(normalized.aliases ?? [])];
94
- if (names.some((name) => name.length === 0 || /\s/.test(name))) {
95
- throw new Error("Command names and aliases must be non-empty and contain no spaces");
96
- }
97
- for (const name of names) {
98
- if (commandRegistry.has(name)) {
99
- throw new Error(`Command '${name}' already exists`);
100
- }
101
- commandRegistry.set(name, normalized);
112
+ if (names.some((n) => n.length === 0 || /\s/.test(n))) {
113
+ throw new Error("Command names must be non-empty and contain no spaces");
102
114
  }
115
+ customCommands.push(normalized);
103
116
  buildCache();
104
117
  }
105
118
  export function createCustomCommand(name, params, run) {
106
- return {
107
- name,
108
- params,
109
- run,
110
- };
119
+ return { name, params, run };
111
120
  }
112
121
  export function getCommandNames() {
113
- if (!cachedCommandNames) {
122
+ if (!cachedCommandNames)
114
123
  buildCache();
115
- }
116
124
  return cachedCommandNames;
117
125
  }
126
+ export function getCommandModulesPublic() {
127
+ return getCommandModules();
128
+ }
118
129
  export function resolveModule(name) {
119
- if (!cachedCommandNames) {
130
+ if (!cachedCommandNames)
120
131
  buildCache();
121
- }
122
132
  return commandRegistry.get(name.toLowerCase());
123
133
  }
124
134
  function splitArgsRespectingQuotes(input) {
@@ -126,7 +136,7 @@ function splitArgsRespectingQuotes(input) {
126
136
  let current = "";
127
137
  let inQuotes = false;
128
138
  let quoteChar = "";
129
- for (let i = 0; i < input.length; i += 1) {
139
+ for (let i = 0; i < input.length; i++) {
130
140
  const ch = input[i] || "";
131
141
  const prev = i > 0 ? input[i - 1] : "";
132
142
  if ((ch === '"' || ch === "'") && prev !== "\\") {
@@ -150,50 +160,57 @@ function splitArgsRespectingQuotes(input) {
150
160
  }
151
161
  current += ch;
152
162
  }
153
- if (current.length > 0) {
163
+ if (current.length > 0)
154
164
  tokens.push(current);
155
- }
156
165
  return tokens;
157
166
  }
158
167
  function parseInput(rawInput) {
159
168
  const parts = splitArgsRespectingQuotes(rawInput.trim());
169
+ return { commandName: parts[0]?.toLowerCase() ?? "", args: parts.slice(1) };
170
+ }
171
+ export function makeDefaultEnv(authUser, hostname) {
160
172
  return {
161
- commandName: parts[0]?.toLowerCase() ?? "",
162
- args: parts.slice(1),
173
+ vars: {
174
+ PATH: "/usr/local/bin:/usr/bin:/bin",
175
+ HOME: `/home/${authUser}`,
176
+ USER: authUser,
177
+ LOGNAME: authUser,
178
+ SHELL: "/bin/sh",
179
+ TERM: "xterm-256color",
180
+ HOSTNAME: hostname,
181
+ PS1: "\\u@\\h:\\w\\$ ",
182
+ },
183
+ lastExitCode: 0,
163
184
  };
164
185
  }
165
- // Internal async function for pipeline execution
166
- export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell, stdin) {
186
+ export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell, stdin, env) {
167
187
  const trimmed = rawInput.trim();
168
- if (trimmed.length === 0) {
188
+ if (trimmed.length === 0)
169
189
  return { exitCode: 0 };
170
- }
171
- if (trimmed.includes("|") || trimmed.includes(">") || trimmed.includes("<")) {
172
- const { parseShellPipeline } = await import("../VirtualShell/shellParser");
173
- const { executePipeline } = await import("../SSHMimic/executor");
174
- const pipeline = parseShellPipeline(trimmed);
175
- if (!pipeline.isValid) {
176
- return {
177
- stderr: pipeline.error || "Syntax error",
178
- exitCode: 1,
179
- };
180
- }
190
+ const shellEnv = env ?? makeDefaultEnv(authUser, hostname);
191
+ // Detect shell operators
192
+ if (/(?<![|&])[|](?![|])/.test(trimmed) ||
193
+ trimmed.includes(">") ||
194
+ trimmed.includes("<") ||
195
+ trimmed.includes("&&") ||
196
+ trimmed.includes("||") ||
197
+ trimmed.includes(";")) {
198
+ const { parseScript } = await import("../VirtualShell/shellParser");
199
+ const { executeStatements } = await import("../SSHMimic/executor");
200
+ const script = parseScript(trimmed);
201
+ if (!script.isValid)
202
+ return { stderr: script.error || "Syntax error", exitCode: 1 };
181
203
  try {
182
- return await executePipeline(pipeline, authUser, hostname, mode, cwd, shell);
204
+ return await executeStatements(script.statements, authUser, hostname, mode, cwd, shell, shellEnv);
183
205
  }
184
206
  catch (error) {
185
- const message = error instanceof Error ? error.message : "Pipeline execution failed";
186
- return { stderr: message, exitCode: 1 };
207
+ return { stderr: error instanceof Error ? error.message : "Execution failed", exitCode: 1 };
187
208
  }
188
209
  }
189
210
  const { commandName, args } = parseInput(trimmed);
190
211
  const mod = resolveModule(commandName);
191
- if (!mod) {
192
- return {
193
- stderr: `Command '${trimmed}' not found`,
194
- exitCode: 127,
195
- };
196
- }
212
+ if (!mod)
213
+ return { stderr: `${commandName}: command not found`, exitCode: 127 };
197
214
  try {
198
215
  return await mod.run({
199
216
  authUser,
@@ -205,10 +222,10 @@ export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell,
205
222
  stdin,
206
223
  cwd,
207
224
  shell,
225
+ env: shellEnv,
208
226
  });
209
227
  }
210
228
  catch (error) {
211
- const message = error instanceof Error ? error.message : "Command failed";
212
- return { stderr: message, exitCode: 1 };
229
+ return { stderr: error instanceof Error ? error.message : "Command failed", exitCode: 1 };
213
230
  }
214
231
  }
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const killCommand: ShellModule;
3
+ //# sourceMappingURL=kill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill.d.ts","sourceRoot":"","sources":["../../src/commands/kill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAWzB,CAAC"}
@@ -0,0 +1,13 @@
1
+ export const killCommand = {
2
+ name: "kill",
3
+ description: "Send signal to process",
4
+ category: "system",
5
+ params: ["[-9] <pid>"],
6
+ run: ({ args }) => {
7
+ const pid = args.find((a) => !a.startsWith("-"));
8
+ if (!pid)
9
+ return { stderr: "kill: no pid specified", exitCode: 1 };
10
+ // In virtual env, we just acknowledge the kill
11
+ return { stdout: ``, exitCode: 0 };
12
+ },
13
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const lnCommand: ShellModule;
3
+ //# sourceMappingURL=ln.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ln.d.ts","sourceRoot":"","sources":["../../src/commands/ln.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,SAAS,EAAE,WA4CvB,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { ifFlag } from "./command-helpers";
2
+ import { assertPathAccess, resolvePath } from "./helpers";
3
+ export const lnCommand = {
4
+ name: "ln",
5
+ description: "Create links",
6
+ category: "files",
7
+ params: ["[-s] <target> <link_name>"],
8
+ run: ({ authUser, shell, cwd, args }) => {
9
+ const symbolic = ifFlag(args, ["-s", "--symbolic"]);
10
+ const positionals = args.filter((a) => !a.startsWith("-"));
11
+ const [targetArg, linkArg] = positionals;
12
+ if (!targetArg || !linkArg) {
13
+ return { stderr: "ln: missing operand", exitCode: 1 };
14
+ }
15
+ const linkPath = resolvePath(cwd, linkArg);
16
+ const targetPath = symbolic
17
+ ? targetArg // keep relative for symlinks
18
+ : resolvePath(cwd, targetArg);
19
+ try {
20
+ assertPathAccess(authUser, linkPath, "ln");
21
+ if (!symbolic) {
22
+ // Hard link — copy file contents
23
+ const srcPath = resolvePath(cwd, targetArg);
24
+ assertPathAccess(authUser, srcPath, "ln");
25
+ if (!shell.vfs.exists(srcPath)) {
26
+ return {
27
+ stderr: `ln: ${targetArg}: No such file or directory`,
28
+ exitCode: 1,
29
+ };
30
+ }
31
+ const content = shell.vfs.readFile(srcPath);
32
+ shell.writeFileAsUser(authUser, linkPath, content);
33
+ }
34
+ else {
35
+ shell.vfs.symlink(targetPath, linkPath);
36
+ }
37
+ return { exitCode: 0 };
38
+ }
39
+ catch (err) {
40
+ const msg = err instanceof Error ? err.message : String(err);
41
+ return { stderr: `ln: ${msg}`, exitCode: 1 };
42
+ }
43
+ },
44
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../src/commands/ls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA4BrD,eAAO,MAAM,SAAS,EAAE,WAuBvB,CAAC"}
1
+ {"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../src/commands/ls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA4BrD,eAAO,MAAM,SAAS,EAAE,WAyBvB,CAAC"}
@@ -23,6 +23,8 @@ function formatDate(date) {
23
23
  }
24
24
  export const lsCommand = {
25
25
  name: "ls",
26
+ description: "List directory contents",
27
+ category: "navigation",
26
28
  params: ["[path]"],
27
29
  run: ({ authUser, shell, cwd, args }) => {
28
30
  const longFormat = ifFlag(args, ["-l", "--long"]);
@@ -1 +1 @@
1
- {"version":3,"file":"mkdir.d.ts","sourceRoot":"","sources":["../../src/commands/mkdir.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,YAAY,EAAE,WAmB1B,CAAC"}
1
+ {"version":3,"file":"mkdir.d.ts","sourceRoot":"","sources":["../../src/commands/mkdir.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,YAAY,EAAE,WAqB1B,CAAC"}
@@ -2,6 +2,8 @@ import { getArg } from "./command-helpers";
2
2
  import { assertPathAccess, resolvePath } from "./helpers";
3
3
  export const mkdirCommand = {
4
4
  name: "mkdir",
5
+ description: "Make directories",
6
+ category: "files",
5
7
  params: ["<dir>"],
6
8
  run: ({ authUser, shell, cwd, args }) => {
7
9
  if (args.length === 0) {
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const mvCommand: ShellModule;
3
+ //# sourceMappingURL=mv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mv.d.ts","sourceRoot":"","sources":["../../src/commands/mv.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,SAAS,EAAE,WAyCvB,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { assertPathAccess, resolvePath } from "./helpers";
2
+ export const mvCommand = {
3
+ name: "mv",
4
+ description: "Move or rename files",
5
+ category: "files",
6
+ params: ["<source> <dest>"],
7
+ run: ({ authUser, shell, cwd, args }) => {
8
+ const positionals = args.filter((a) => !a.startsWith("-"));
9
+ const [srcArg, destArg] = positionals;
10
+ if (!srcArg || !destArg) {
11
+ return { stderr: "mv: missing operand", exitCode: 1 };
12
+ }
13
+ const srcPath = resolvePath(cwd, srcArg);
14
+ const destPath = resolvePath(cwd, destArg);
15
+ try {
16
+ assertPathAccess(authUser, srcPath, "mv");
17
+ assertPathAccess(authUser, destPath, "mv");
18
+ if (!shell.vfs.exists(srcPath)) {
19
+ return {
20
+ stderr: `mv: ${srcArg}: No such file or directory`,
21
+ exitCode: 1,
22
+ };
23
+ }
24
+ // If dest is a directory, move into it
25
+ const finalDest = shell.vfs.exists(destPath) &&
26
+ shell.vfs.stat(destPath).type === "directory"
27
+ ? `${destPath}/${srcArg.split("/").pop()}`
28
+ : destPath;
29
+ shell.vfs.move(srcPath, finalDest);
30
+ return { exitCode: 0 };
31
+ }
32
+ catch (err) {
33
+ const msg = err instanceof Error ? err.message : String(err);
34
+ return { stderr: `mv: ${msg}`, exitCode: 1 };
35
+ }
36
+ },
37
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"nano.d.ts","sourceRoot":"","sources":["../../src/commands/nano.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WA0BzB,CAAC"}
1
+ {"version":3,"file":"nano.d.ts","sourceRoot":"","sources":["../../src/commands/nano.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WA4BzB,CAAC"}
@@ -2,6 +2,8 @@ import * as path from "node:path";
2
2
  import { assertPathAccess, resolvePath } from "./helpers";
3
3
  export const nanoCommand = {
4
4
  name: "nano",
5
+ description: "Text editor",
6
+ category: "shell",
5
7
  params: ["<file>"],
6
8
  run: ({ authUser, shell, cwd, args }) => {
7
9
  const fileArg = args[0];
@@ -1 +1 @@
1
- {"version":3,"file":"neofetch.d.ts","sourceRoot":"","sources":["../../src/commands/neofetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,eAAe,EAAE,WA+B7B,CAAC"}
1
+ {"version":3,"file":"neofetch.d.ts","sourceRoot":"","sources":["../../src/commands/neofetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,eAAe,EAAE,WAiC7B,CAAC"}
@@ -3,6 +3,8 @@ import { ifFlag } from "./command-helpers";
3
3
  import { getAllEnvVars } from "./set";
4
4
  export const neofetchCommand = {
5
5
  name: "neofetch",
6
+ description: "System info display",
7
+ category: "misc",
6
8
  params: ["[--off]"],
7
9
  run: ({ args, authUser, hostname, shell }) => {
8
10
  const env = getAllEnvVars(authUser);
@@ -1 +1 @@
1
- {"version":3,"file":"passwd.d.ts","sourceRoot":"","sources":["../../src/commands/passwd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,aAAa,EAAE,WAsB3B,CAAC"}
1
+ {"version":3,"file":"passwd.d.ts","sourceRoot":"","sources":["../../src/commands/passwd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,aAAa,EAAE,WAwB3B,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export const passwdCommand = {
2
2
  name: "passwd",
3
+ description: "Change user password",
4
+ category: "users",
3
5
  params: ["<username> <password>"],
4
6
  run: async ({ authUser, args, shell }) => {
5
7
  const [username, password] = args;
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const pingCommand: ShellModule;
3
+ //# sourceMappingURL=ping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ping.d.ts","sourceRoot":"","sources":["../../src/commands/ping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAiBzB,CAAC"}
@@ -0,0 +1,18 @@
1
+ export const pingCommand = {
2
+ name: "ping",
3
+ description: "Send ICMP ECHO_REQUEST (mock)",
4
+ category: "network",
5
+ params: ["[-c <count>] <host>"],
6
+ run: ({ args }) => {
7
+ const host = args.find((a) => !a.startsWith("-")) ?? "localhost";
8
+ const count = 4;
9
+ const lines = [`PING ${host}: 56 data bytes`];
10
+ for (let i = 0; i < count; i++) {
11
+ const ms = (Math.random() * 10 + 1).toFixed(3);
12
+ lines.push(`64 bytes from ${host}: icmp_seq=${i} ttl=64 time=${ms} ms`);
13
+ }
14
+ lines.push(`--- ${host} ping statistics ---`);
15
+ lines.push(`${count} packets transmitted, ${count} received, 0% packet loss`);
16
+ return { stdout: lines.join("\n"), exitCode: 0 };
17
+ },
18
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const psCommand: ShellModule;
3
+ //# sourceMappingURL=ps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ps.d.ts","sourceRoot":"","sources":["../../src/commands/ps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,SAAS,EAAE,WAgBvB,CAAC"}
@@ -0,0 +1,17 @@
1
+ export const psCommand = {
2
+ name: "ps",
3
+ description: "Report process status",
4
+ category: "system",
5
+ params: ["[-a] [-u] [-x]"],
6
+ run: ({ authUser, shell }) => {
7
+ const sessions = shell.users.listActiveSessions();
8
+ const lines = [" PID TTY TIME CMD"];
9
+ let pid = 1000;
10
+ for (const s of sessions) {
11
+ lines.push(`${String(pid).padStart(5)} ${s.tty.padEnd(12)} 00:00:00 ${s.username === authUser ? "bash" : `bash (${s.username})`}`);
12
+ pid++;
13
+ }
14
+ lines.push(`${String(pid).padStart(5)} pts/0 00:00:00 ps`);
15
+ return { stdout: lines.join("\n"), exitCode: 0 };
16
+ },
17
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"pwd.d.ts","sourceRoot":"","sources":["../../src/commands/pwd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,UAAU,EAAE,WAIxB,CAAC"}
1
+ {"version":3,"file":"pwd.d.ts","sourceRoot":"","sources":["../../src/commands/pwd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,UAAU,EAAE,WAMxB,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export const pwdCommand = {
2
2
  name: "pwd",
3
+ description: "Print working directory",
4
+ category: "navigation",
3
5
  params: [],
4
6
  run: ({ cwd }) => ({ stdout: cwd, exitCode: 0 }),
5
7
  };
@@ -1 +1 @@
1
- {"version":3,"file":"rm.d.ts","sourceRoot":"","sources":["../../src/commands/rm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,SAAS,EAAE,WA8BvB,CAAC"}
1
+ {"version":3,"file":"rm.d.ts","sourceRoot":"","sources":["../../src/commands/rm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,SAAS,EAAE,WAgCvB,CAAC"}
@@ -2,6 +2,8 @@ import { getArg, ifFlag } from "./command-helpers";
2
2
  import { assertPathAccess, resolvePath } from "./helpers";
3
3
  export const rmCommand = {
4
4
  name: "rm",
5
+ description: "Remove files or directories",
6
+ category: "files",
5
7
  params: ["[-r|-rf] <path>"],
6
8
  run: ({ authUser, shell, cwd, args }) => {
7
9
  if (args.length === 0) {
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const sedCommand: ShellModule;
3
+ //# sourceMappingURL=sed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sed.d.ts","sourceRoot":"","sources":["../../src/commands/sed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,UAAU,EAAE,WAwCxB,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { getFlag, ifFlag } from "./command-helpers";
2
+ import { resolvePath } from "./helpers";
3
+ export const sedCommand = {
4
+ name: "sed",
5
+ description: "Stream editor for filtering and transforming text",
6
+ category: "text",
7
+ params: ["-e <expr> [file]", "s/pattern/replace/[g]"],
8
+ run: ({ authUser, shell, cwd, args, stdin }) => {
9
+ const inPlace = ifFlag(args, ["-i"]);
10
+ const expr = getFlag(args, ["-e"]) ?? args.find((a) => !a.startsWith("-"));
11
+ const fileArg = args.filter((a) => !a.startsWith("-") && a !== expr).pop();
12
+ if (!expr)
13
+ return { stderr: "sed: no expression", exitCode: 1 };
14
+ let content = stdin ?? "";
15
+ if (fileArg) {
16
+ const p = resolvePath(cwd, fileArg);
17
+ try {
18
+ content = shell.vfs.readFile(p);
19
+ }
20
+ catch {
21
+ return { stderr: `sed: ${fileArg}: No such file or directory`, exitCode: 1 };
22
+ }
23
+ }
24
+ // Parse s/from/to/[g]
25
+ const sMatch = expr.match(/^s([^a-zA-Z0-9])(.+?)\1(.*?)\1([gi]*)$/);
26
+ if (!sMatch)
27
+ return { stderr: `sed: unrecognized command: ${expr}`, exitCode: 1 };
28
+ const [, , from, to, flags] = sMatch;
29
+ const regexFlags = (flags ?? "").includes("i") ? "gi" : (flags ?? "").includes("g") ? "g" : "";
30
+ let regex;
31
+ try {
32
+ regex = new RegExp(from, regexFlags || "");
33
+ }
34
+ catch (_e) {
35
+ return { stderr: `sed: invalid regex: ${from}`, exitCode: 1 };
36
+ }
37
+ const result = (flags ?? "").includes("g") || regexFlags.includes("g")
38
+ ? content.replace(regex, to ?? "")
39
+ : content.replace(regex, to ?? "");
40
+ if (inPlace && fileArg) {
41
+ const p = resolvePath(cwd, fileArg);
42
+ shell.writeFileAsUser(authUser, p, result);
43
+ return { exitCode: 0 };
44
+ }
45
+ return { stdout: result, exitCode: 0 };
46
+ },
47
+ };