typescript-virtual-container 1.2.5 → 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 (245) hide show
  1. package/README.md +387 -193
  2. package/benchmark-results.txt +21 -21
  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.map +1 -1
  8. package/dist/SSHMimic/index.js +6 -20
  9. package/dist/SSHMimic/sftp.d.ts.map +1 -1
  10. package/dist/SSHMimic/sftp.js +14 -0
  11. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  12. package/dist/VirtualFileSystem/index.js +13 -36
  13. package/dist/VirtualShell/shell.d.ts.map +1 -1
  14. package/dist/VirtualShell/shell.js +19 -2
  15. package/dist/VirtualShell/shellParser.d.ts +20 -2
  16. package/dist/VirtualShell/shellParser.d.ts.map +1 -1
  17. package/dist/VirtualShell/shellParser.js +229 -120
  18. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  19. package/dist/commands/adduser.d.ts.map +1 -1
  20. package/dist/commands/adduser.js +2 -0
  21. package/dist/commands/awk.d.ts +3 -0
  22. package/dist/commands/awk.d.ts.map +1 -0
  23. package/dist/commands/awk.js +29 -0
  24. package/dist/commands/base64.d.ts +3 -0
  25. package/dist/commands/base64.d.ts.map +1 -0
  26. package/dist/commands/base64.js +20 -0
  27. package/dist/commands/cat.d.ts.map +1 -1
  28. package/dist/commands/cat.js +2 -0
  29. package/dist/commands/cd.d.ts.map +1 -1
  30. package/dist/commands/cd.js +2 -0
  31. package/dist/commands/chmod.d.ts.map +1 -1
  32. package/dist/commands/chmod.js +2 -0
  33. package/dist/commands/clear.d.ts.map +1 -1
  34. package/dist/commands/clear.js +4 -1
  35. package/dist/commands/cp.d.ts.map +1 -1
  36. package/dist/commands/cp.js +2 -0
  37. package/dist/commands/curl.d.ts.map +1 -1
  38. package/dist/commands/curl.js +2 -0
  39. package/dist/commands/cut.d.ts +3 -0
  40. package/dist/commands/cut.d.ts.map +1 -0
  41. package/dist/commands/cut.js +27 -0
  42. package/dist/commands/date.d.ts +3 -0
  43. package/dist/commands/date.d.ts.map +1 -0
  44. package/dist/commands/date.js +22 -0
  45. package/dist/commands/deluser.d.ts.map +1 -1
  46. package/dist/commands/deluser.js +2 -0
  47. package/dist/commands/df.d.ts +3 -0
  48. package/dist/commands/df.d.ts.map +1 -0
  49. package/dist/commands/df.js +16 -0
  50. package/dist/commands/diff.d.ts +3 -0
  51. package/dist/commands/diff.d.ts.map +1 -0
  52. package/dist/commands/diff.js +40 -0
  53. package/dist/commands/du.d.ts +3 -0
  54. package/dist/commands/du.d.ts.map +1 -0
  55. package/dist/commands/du.js +39 -0
  56. package/dist/commands/echo.d.ts.map +1 -1
  57. package/dist/commands/echo.js +2 -0
  58. package/dist/commands/env.d.ts.map +1 -1
  59. package/dist/commands/env.js +6 -14
  60. package/dist/commands/export.d.ts.map +1 -1
  61. package/dist/commands/export.js +11 -21
  62. package/dist/commands/find.d.ts.map +1 -1
  63. package/dist/commands/find.js +2 -0
  64. package/dist/commands/grep.d.ts.map +1 -1
  65. package/dist/commands/grep.js +4 -7
  66. package/dist/commands/groups.d.ts +3 -0
  67. package/dist/commands/groups.d.ts.map +1 -0
  68. package/dist/commands/groups.js +12 -0
  69. package/dist/commands/gzip.d.ts +4 -0
  70. package/dist/commands/gzip.d.ts.map +1 -0
  71. package/dist/commands/gzip.js +40 -0
  72. package/dist/commands/head.d.ts.map +1 -1
  73. package/dist/commands/head.js +2 -0
  74. package/dist/commands/help.d.ts +1 -1
  75. package/dist/commands/help.d.ts.map +1 -1
  76. package/dist/commands/help.js +75 -3
  77. package/dist/commands/hostname.d.ts.map +1 -1
  78. package/dist/commands/hostname.js +2 -0
  79. package/dist/commands/htop.d.ts.map +1 -1
  80. package/dist/commands/htop.js +2 -0
  81. package/dist/commands/id.d.ts +3 -0
  82. package/dist/commands/id.d.ts.map +1 -0
  83. package/dist/commands/id.js +14 -0
  84. package/dist/commands/index.d.ts +5 -2
  85. package/dist/commands/index.d.ts.map +1 -1
  86. package/dist/commands/index.js +89 -62
  87. package/dist/commands/kill.d.ts +3 -0
  88. package/dist/commands/kill.d.ts.map +1 -0
  89. package/dist/commands/kill.js +13 -0
  90. package/dist/commands/ln.d.ts.map +1 -1
  91. package/dist/commands/ln.js +2 -0
  92. package/dist/commands/ls.d.ts.map +1 -1
  93. package/dist/commands/ls.js +2 -0
  94. package/dist/commands/mkdir.d.ts.map +1 -1
  95. package/dist/commands/mkdir.js +2 -0
  96. package/dist/commands/mv.d.ts.map +1 -1
  97. package/dist/commands/mv.js +2 -0
  98. package/dist/commands/nano.d.ts.map +1 -1
  99. package/dist/commands/nano.js +2 -0
  100. package/dist/commands/neofetch.d.ts.map +1 -1
  101. package/dist/commands/neofetch.js +2 -0
  102. package/dist/commands/passwd.d.ts.map +1 -1
  103. package/dist/commands/passwd.js +2 -0
  104. package/dist/commands/ping.d.ts +3 -0
  105. package/dist/commands/ping.d.ts.map +1 -0
  106. package/dist/commands/ping.js +18 -0
  107. package/dist/commands/ps.d.ts +3 -0
  108. package/dist/commands/ps.d.ts.map +1 -0
  109. package/dist/commands/ps.js +17 -0
  110. package/dist/commands/pwd.d.ts.map +1 -1
  111. package/dist/commands/pwd.js +2 -0
  112. package/dist/commands/rm.d.ts.map +1 -1
  113. package/dist/commands/rm.js +2 -0
  114. package/dist/commands/sed.d.ts +3 -0
  115. package/dist/commands/sed.d.ts.map +1 -0
  116. package/dist/commands/sed.js +47 -0
  117. package/dist/commands/set.d.ts +3 -0
  118. package/dist/commands/set.d.ts.map +1 -1
  119. package/dist/commands/set.js +19 -46
  120. package/dist/commands/sh.d.ts +0 -1
  121. package/dist/commands/sh.d.ts.map +1 -1
  122. package/dist/commands/sh.js +228 -35
  123. package/dist/commands/sleep.d.ts +3 -0
  124. package/dist/commands/sleep.d.ts.map +1 -0
  125. package/dist/commands/sleep.js +13 -0
  126. package/dist/commands/sort.d.ts +3 -0
  127. package/dist/commands/sort.d.ts.map +1 -0
  128. package/dist/commands/sort.js +37 -0
  129. package/dist/commands/su.d.ts.map +1 -1
  130. package/dist/commands/su.js +2 -0
  131. package/dist/commands/sudo.d.ts.map +1 -1
  132. package/dist/commands/sudo.js +2 -0
  133. package/dist/commands/tail.d.ts.map +1 -1
  134. package/dist/commands/tail.js +2 -0
  135. package/dist/commands/tar.d.ts +3 -0
  136. package/dist/commands/tar.d.ts.map +1 -0
  137. package/dist/commands/tar.js +64 -0
  138. package/dist/commands/tee.d.ts +3 -0
  139. package/dist/commands/tee.d.ts.map +1 -0
  140. package/dist/commands/tee.js +29 -0
  141. package/dist/commands/touch.d.ts.map +1 -1
  142. package/dist/commands/touch.js +2 -0
  143. package/dist/commands/tr.d.ts +3 -0
  144. package/dist/commands/tr.d.ts.map +1 -0
  145. package/dist/commands/tr.js +24 -0
  146. package/dist/commands/tree.d.ts.map +1 -1
  147. package/dist/commands/tree.js +2 -0
  148. package/dist/commands/uname.d.ts +3 -0
  149. package/dist/commands/uname.d.ts.map +1 -0
  150. package/dist/commands/uname.js +21 -0
  151. package/dist/commands/uniq.d.ts +3 -0
  152. package/dist/commands/uniq.d.ts.map +1 -0
  153. package/dist/commands/uniq.js +33 -0
  154. package/dist/commands/unset.d.ts.map +1 -1
  155. package/dist/commands/unset.js +6 -10
  156. package/dist/commands/wc.d.ts.map +1 -1
  157. package/dist/commands/wc.js +2 -0
  158. package/dist/commands/wget.d.ts.map +1 -1
  159. package/dist/commands/wget.js +2 -0
  160. package/dist/commands/who.d.ts.map +1 -1
  161. package/dist/commands/who.js +2 -0
  162. package/dist/commands/whoami.d.ts.map +1 -1
  163. package/dist/commands/whoami.js +2 -0
  164. package/dist/commands/xargs.d.ts +3 -0
  165. package/dist/commands/xargs.d.ts.map +1 -0
  166. package/dist/commands/xargs.js +16 -0
  167. package/dist/types/commands.d.ts +13 -0
  168. package/dist/types/commands.d.ts.map +1 -1
  169. package/dist/types/pipeline.d.ts +20 -0
  170. package/dist/types/pipeline.d.ts.map +1 -1
  171. package/package.json +1 -1
  172. package/src/SSHMimic/exec.ts +2 -2
  173. package/src/SSHMimic/executor.ts +95 -98
  174. package/src/SSHMimic/index.ts +15 -49
  175. package/src/SSHMimic/sftp.ts +15 -0
  176. package/src/VirtualFileSystem/index.ts +27 -75
  177. package/src/VirtualShell/shell.ts +19 -2
  178. package/src/VirtualShell/shellParser.ts +202 -168
  179. package/src/VirtualUserManager/index.ts +2 -7
  180. package/src/commands/adduser.ts +2 -0
  181. package/src/commands/awk.ts +30 -0
  182. package/src/commands/base64.ts +18 -0
  183. package/src/commands/cat.ts +2 -0
  184. package/src/commands/cd.ts +2 -0
  185. package/src/commands/chmod.ts +2 -0
  186. package/src/commands/clear.ts +4 -1
  187. package/src/commands/cp.ts +2 -0
  188. package/src/commands/curl.ts +2 -0
  189. package/src/commands/cut.ts +29 -0
  190. package/src/commands/date.ts +24 -0
  191. package/src/commands/deluser.ts +2 -0
  192. package/src/commands/df.ts +18 -0
  193. package/src/commands/diff.ts +29 -0
  194. package/src/commands/du.ts +39 -0
  195. package/src/commands/echo.ts +2 -0
  196. package/src/commands/env.ts +6 -16
  197. package/src/commands/export.ts +11 -24
  198. package/src/commands/find.ts +2 -0
  199. package/src/commands/grep.ts +4 -7
  200. package/src/commands/groups.ts +14 -0
  201. package/src/commands/gzip.ts +31 -0
  202. package/src/commands/head.ts +2 -0
  203. package/src/commands/help.ts +81 -3
  204. package/src/commands/hostname.ts +2 -0
  205. package/src/commands/htop.ts +2 -0
  206. package/src/commands/id.ts +16 -0
  207. package/src/commands/index.ts +98 -99
  208. package/src/commands/kill.ts +14 -0
  209. package/src/commands/ln.ts +2 -0
  210. package/src/commands/ls.ts +2 -0
  211. package/src/commands/mkdir.ts +2 -0
  212. package/src/commands/mv.ts +2 -0
  213. package/src/commands/nano.ts +2 -0
  214. package/src/commands/neofetch.ts +2 -0
  215. package/src/commands/passwd.ts +2 -0
  216. package/src/commands/ping.ts +20 -0
  217. package/src/commands/ps.ts +19 -0
  218. package/src/commands/pwd.ts +2 -0
  219. package/src/commands/rm.ts +2 -0
  220. package/src/commands/sed.ts +45 -0
  221. package/src/commands/set.ts +19 -50
  222. package/src/commands/sh.ts +192 -43
  223. package/src/commands/sleep.ts +14 -0
  224. package/src/commands/sort.ts +37 -0
  225. package/src/commands/su.ts +2 -0
  226. package/src/commands/sudo.ts +2 -0
  227. package/src/commands/tail.ts +2 -0
  228. package/src/commands/tar.ts +58 -0
  229. package/src/commands/tee.ts +25 -0
  230. package/src/commands/touch.ts +2 -0
  231. package/src/commands/tr.ts +24 -0
  232. package/src/commands/tree.ts +2 -0
  233. package/src/commands/uname.ts +20 -0
  234. package/src/commands/uniq.ts +28 -0
  235. package/src/commands/unset.ts +5 -12
  236. package/src/commands/wc.ts +2 -0
  237. package/src/commands/wget.ts +2 -0
  238. package/src/commands/who.ts +2 -0
  239. package/src/commands/whoami.ts +2 -0
  240. package/src/commands/xargs.ts +17 -0
  241. package/src/types/commands.ts +14 -0
  242. package/src/types/pipeline.ts +23 -0
  243. package/standalone.js +92 -64
  244. package/standalone.js.map +4 -4
  245. package/tests/users.test.ts +5 -34
@@ -1,91 +1,225 @@
1
- /** Parse a shell command line into a structured pipeline */
1
+ // ── Public API ───────────────────────────────────────────────────────────────
2
+ /**
3
+ * Parse a shell input line into a Script (sequence of statements connected
4
+ * by && / || / ;). Each statement contains one Pipeline (commands connected
5
+ * by |).
6
+ */
7
+ export function parseScript(rawInput) {
8
+ const trimmed = rawInput.trim();
9
+ if (!trimmed)
10
+ return { statements: [], isValid: true };
11
+ try {
12
+ const statements = parseStatements(trimmed);
13
+ return { statements, isValid: true };
14
+ }
15
+ catch (e) {
16
+ return { statements: [], isValid: false, error: e.message };
17
+ }
18
+ }
19
+ /** Legacy compat: parse a single pipeline (no &&/||/;) */
2
20
  export function parseShellPipeline(rawInput) {
3
21
  const trimmed = rawInput.trim();
4
- if (!trimmed) {
22
+ if (!trimmed)
5
23
  return { commands: [], isValid: true };
24
+ try {
25
+ const commands = parsePipeline(trimmed);
26
+ return { commands, isValid: true };
6
27
  }
7
- const commands = [];
8
- const tokenized = tokenizePipeline(trimmed);
9
- if (tokenized.error) {
10
- return {
11
- commands: [],
12
- isValid: false,
13
- error: tokenized.error,
14
- };
28
+ catch (e) {
29
+ return { commands: [], isValid: false, error: e.message };
15
30
  }
16
- const pipeTokens = tokenized.tokens;
17
- for (const token of pipeTokens) {
18
- const cmd = parseCommandWithRedirections(token);
19
- if (!cmd.isValid) {
20
- return {
21
- commands: [],
22
- isValid: false,
23
- error: cmd.error,
24
- };
25
- }
26
- if (cmd.command) {
27
- commands.push(cmd.command);
31
+ }
32
+ // ── Variable & tilde expansion ────────────────────────────────────────────────
33
+ /**
34
+ * Expand ~ and $VAR / ${VAR} / ${VAR:-default} / $(cmd placeholder) in a
35
+ * token, given the current env vars and home path.
36
+ * Command substitution $(…) is NOT executed here — it's left as a marker so
37
+ * the executor can handle it.
38
+ */
39
+ export function expandToken(token, env, authUser, lastExitCode = 0) {
40
+ // tilde expansion
41
+ token = token.replace(/^~(\/|$)/, `/home/${authUser}$1`);
42
+ // $? special var
43
+ token = token.replace(/\$\?/g, String(lastExitCode));
44
+ // $$ PID (mock)
45
+ token = token.replace(/\$\$/g, "1");
46
+ // $# argc (0 for interactive)
47
+ token = token.replace(/\$#/g, "0");
48
+ // ${VAR:-default} and ${VAR:+value}
49
+ token = token.replace(/\$\{([^}:]+):-([^}]*)\}/g, (_, name, def) => env[name] ?? def);
50
+ token = token.replace(/\$\{([^}:]+):\+([^}]*)\}/g, (_, name, val) => env[name] ? val : "");
51
+ // ${VAR}
52
+ token = token.replace(/\$\{([^}]+)\}/g, (_, name) => env[name] ?? "");
53
+ // $VAR (greedy: match longest valid identifier)
54
+ token = token.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, name) => env[name] ?? "");
55
+ return token;
56
+ }
57
+ /**
58
+ * Expand glob patterns (*, ?, [abc]) against a list of entries.
59
+ * Returns the original pattern if no match.
60
+ */
61
+ export function expandGlob(pattern, entries) {
62
+ if (!/[*?[]/.test(pattern))
63
+ return [pattern];
64
+ const regex = globToRegex(pattern);
65
+ const matches = entries.filter((e) => regex.test(e));
66
+ return matches.length > 0 ? matches.sort() : [pattern];
67
+ }
68
+ function globToRegex(pattern) {
69
+ let re = "^";
70
+ for (let i = 0; i < pattern.length; i++) {
71
+ const c = pattern[i];
72
+ if (c === "*")
73
+ re += ".*";
74
+ else if (c === "?")
75
+ re += ".";
76
+ else if (c === "[") {
77
+ const close = pattern.indexOf("]", i + 1);
78
+ if (close === -1)
79
+ re += "\\[";
80
+ else {
81
+ re += `[${pattern.slice(i + 1, close)}]`;
82
+ i = close;
83
+ }
28
84
  }
85
+ else
86
+ re += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
29
87
  }
30
- return { commands, isValid: true };
88
+ return new RegExp(`${re}$`);
31
89
  }
32
- /** Tokenize input by pipes, respecting quoted strings */
33
- function tokenizePipeline(input) {
34
- const tokens = [];
90
+ // ── Internal parser ───────────────────────────────────────────────────────────
91
+ function parseStatements(input) {
92
+ // Split by ;, &&, || — respecting quotes and parens
93
+ const segments = splitByLogicalOps(input);
94
+ const statements = [];
95
+ for (const seg of segments) {
96
+ const commands = parsePipeline(seg.text.trim());
97
+ const stmt = { pipeline: { commands, isValid: true } };
98
+ if (seg.op)
99
+ stmt.op = seg.op;
100
+ statements.push(stmt);
101
+ }
102
+ return statements;
103
+ }
104
+ function splitByLogicalOps(input) {
105
+ const segments = [];
35
106
  let current = "";
36
- let inQuotes = false;
37
- let quoteChar = "";
107
+ let depth = 0; // parens/subshell depth
108
+ let inQ = false;
109
+ let qChar = "";
38
110
  let i = 0;
111
+ const flush = (op) => {
112
+ if (current.trim())
113
+ segments.push({ text: current, op });
114
+ current = "";
115
+ };
39
116
  while (i < input.length) {
40
117
  const ch = input[i];
41
- if ((ch === '"' || ch === "'") && (i === 0 || input[i - 1] !== "\\")) {
42
- if (!inQuotes) {
43
- inQuotes = true;
44
- quoteChar = ch;
45
- }
46
- else if (ch === quoteChar) {
47
- inQuotes = false;
48
- }
118
+ const ch2 = input.slice(i, i + 2);
119
+ if ((ch === '"' || ch === "'") && !inQ) {
120
+ inQ = true;
121
+ qChar = ch;
49
122
  current += ch;
50
123
  i++;
124
+ continue;
51
125
  }
52
- else if (ch === "|" && !inQuotes) {
53
- if (!current.trim()) {
54
- return {
55
- tokens: [],
56
- error: "Syntax error near unexpected token '|'",
57
- };
58
- }
59
- tokens.push(current.trim());
60
- current = "";
126
+ if (inQ && ch === qChar) {
127
+ inQ = false;
128
+ current += ch;
61
129
  i++;
130
+ continue;
62
131
  }
63
- else {
132
+ if (inQ) {
64
133
  current += ch;
65
134
  i++;
135
+ continue;
66
136
  }
137
+ if (ch === "(") {
138
+ depth++;
139
+ current += ch;
140
+ i++;
141
+ continue;
142
+ }
143
+ if (ch === ")") {
144
+ depth--;
145
+ current += ch;
146
+ i++;
147
+ continue;
148
+ }
149
+ if (depth > 0) {
150
+ current += ch;
151
+ i++;
152
+ continue;
153
+ }
154
+ if (ch2 === "&&") {
155
+ flush("&&");
156
+ i += 2;
157
+ continue;
158
+ }
159
+ if (ch2 === "||") {
160
+ flush("||");
161
+ i += 2;
162
+ continue;
163
+ }
164
+ if (ch === ";") {
165
+ flush(";");
166
+ i++;
167
+ continue;
168
+ }
169
+ current += ch;
170
+ i++;
67
171
  }
68
- if (inQuotes) {
69
- return {
70
- tokens: [],
71
- error: "Syntax error: unterminated quote",
72
- };
73
- }
74
- if (!current.trim()) {
75
- return {
76
- tokens: [],
77
- error: "Syntax error near unexpected token '|'",
78
- };
172
+ flush();
173
+ return segments;
174
+ }
175
+ function parsePipeline(input) {
176
+ const pipeTokens = splitByPipe(input);
177
+ return pipeTokens.map(parseCommandWithRedirections);
178
+ }
179
+ function splitByPipe(input) {
180
+ const tokens = [];
181
+ let current = "";
182
+ let inQ = false;
183
+ let qChar = "";
184
+ for (let i = 0; i < input.length; i++) {
185
+ const ch = input[i];
186
+ if ((ch === '"' || ch === "'") && !inQ) {
187
+ inQ = true;
188
+ qChar = ch;
189
+ current += ch;
190
+ continue;
191
+ }
192
+ if (inQ && ch === qChar) {
193
+ inQ = false;
194
+ current += ch;
195
+ continue;
196
+ }
197
+ if (inQ) {
198
+ current += ch;
199
+ continue;
200
+ }
201
+ // || was already consumed at statement level, bare | is pipe
202
+ if (ch === "|" && input[i + 1] !== "|") {
203
+ if (!current.trim())
204
+ throw new Error("Syntax error near unexpected token '|'");
205
+ tokens.push(current.trim());
206
+ current = "";
207
+ }
208
+ else {
209
+ current += ch;
210
+ }
79
211
  }
80
- tokens.push(current.trim());
81
- return { tokens };
212
+ const tail = current.trim();
213
+ if (!tail && tokens.length > 0)
214
+ throw new Error("Syntax error near unexpected token '|'");
215
+ if (tail)
216
+ tokens.push(tail);
217
+ return tokens;
82
218
  }
83
- /** Parse a single command with its redirections (>, >>, <) */
84
219
  function parseCommandWithRedirections(token) {
85
220
  const parts = tokenizeCommand(token);
86
- if (parts.length === 0) {
87
- return { isValid: true };
88
- }
221
+ if (parts.length === 0)
222
+ return { name: "", args: [] };
89
223
  const cmdParts = [];
90
224
  let inputFile;
91
225
  let outputFile;
@@ -95,35 +229,23 @@ function parseCommandWithRedirections(token) {
95
229
  const part = parts[i];
96
230
  if (part === "<") {
97
231
  i++;
98
- if (i >= parts.length) {
99
- return {
100
- isValid: false,
101
- error: "Syntax error: expected filename after <",
102
- };
103
- }
232
+ if (i >= parts.length)
233
+ throw new Error("Syntax error: expected filename after <");
104
234
  inputFile = parts[i];
105
235
  i++;
106
236
  }
107
237
  else if (part === ">>") {
108
238
  i++;
109
- if (i >= parts.length) {
110
- return {
111
- isValid: false,
112
- error: "Syntax error: expected filename after >>",
113
- };
114
- }
239
+ if (i >= parts.length)
240
+ throw new Error("Syntax error: expected filename after >>");
115
241
  outputFile = parts[i];
116
242
  appendOutput = true;
117
243
  i++;
118
244
  }
119
245
  else if (part === ">") {
120
246
  i++;
121
- if (i >= parts.length) {
122
- return {
123
- isValid: false,
124
- error: "Syntax error: expected filename after >",
125
- };
126
- }
247
+ if (i >= parts.length)
248
+ throw new Error("Syntax error: expected filename after >");
127
249
  outputFile = parts[i];
128
250
  appendOutput = false;
129
251
  i++;
@@ -133,55 +255,44 @@ function parseCommandWithRedirections(token) {
133
255
  i++;
134
256
  }
135
257
  }
136
- if (cmdParts.length === 0) {
137
- return { isValid: true };
138
- }
139
- const name = cmdParts[0].toLowerCase();
140
- const args = cmdParts.slice(1);
141
- return {
142
- command: {
143
- name,
144
- args,
145
- inputFile,
146
- outputFile,
147
- appendOutput,
148
- },
149
- isValid: true,
150
- };
258
+ const name = (cmdParts[0] ?? "").toLowerCase();
259
+ return { name, args: cmdParts.slice(1), inputFile, outputFile, appendOutput };
151
260
  }
152
- /** Tokenize a command, respecting quotes and handling >> vs > */
153
261
  function tokenizeCommand(input) {
154
262
  const tokens = [];
155
263
  let current = "";
156
- let inQuotes = false;
157
- let quoteChar = "";
264
+ let inQ = false;
265
+ let qChar = "";
158
266
  let i = 0;
159
267
  while (i < input.length) {
160
268
  const ch = input[i];
161
269
  const next = input[i + 1];
162
- // Handle quotes
163
- if ((ch === '"' || ch === "'") && (i === 0 || input[i - 1] !== "\\")) {
164
- if (!inQuotes) {
165
- inQuotes = true;
166
- quoteChar = ch;
167
- }
168
- else if (ch === quoteChar) {
169
- inQuotes = false;
170
- quoteChar = "";
171
- }
172
- else {
173
- current += ch;
174
- }
270
+ if ((ch === '"' || ch === "'") && !inQ) {
271
+ inQ = true;
272
+ qChar = ch;
273
+ i++;
274
+ continue;
275
+ }
276
+ if (inQ && ch === qChar) {
277
+ inQ = false;
278
+ qChar = "";
175
279
  i++;
280
+ continue;
176
281
  }
177
- else if (ch === " " && !inQuotes) {
282
+ if (inQ) {
283
+ current += ch;
284
+ i++;
285
+ continue;
286
+ }
287
+ if (ch === " ") {
178
288
  if (current) {
179
289
  tokens.push(current);
180
290
  current = "";
181
291
  }
182
292
  i++;
293
+ continue;
183
294
  }
184
- else if ((ch === ">" || ch === "<") && !inQuotes) {
295
+ if ((ch === ">" || ch === "<") && !inQ) {
185
296
  if (current) {
186
297
  tokens.push(current);
187
298
  current = "";
@@ -194,14 +305,12 @@ function tokenizeCommand(input) {
194
305
  tokens.push(ch);
195
306
  i++;
196
307
  }
308
+ continue;
197
309
  }
198
- else {
199
- current += ch;
200
- i++;
201
- }
310
+ current += ch;
311
+ i++;
202
312
  }
203
- if (current) {
313
+ if (current)
204
314
  tokens.push(current);
205
- }
206
315
  return tokens;
207
316
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualUserManager/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/VirtualUserManager/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAE1D,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IACjC,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACpC,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;CAClB;AAYD;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAqBlD,OAAO,CAAC,QAAQ,CAAC,GAAG;IAGpB,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAvBrC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAwC;IAC3E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoC;IAC9D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2B;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwC;IAC9D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2C;IAC1E,OAAO,CAAC,OAAO,CAAK;IAEpB;;;;;;OAMG;gBAEe,GAAG,EAAE,iBAAiB,EAGtB,mBAAmB,GAAE,OAAc;IAMrD;;;OAGG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsCxC;;;;;OAKG;IACU,aAAa,CACzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC;IAehB;;;;OAIG;IACU,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxD;;;;;OAKG;IACI,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKrD;;;;;OAKG;IACI,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAU9C;;;;;;;;OAQG;IACI,sBAAsB,CAC5B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,IAAI;IAoCP;;;;;;OAMG;IACI,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAUlE;;;;;OAKG;IACU,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BvE;;;;;OAKG;IACI,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAMvD;;;;;OAKG;IACU,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa3E;;;;OAIG;IACU,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBxD;;;;;OAKG;IACI,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAK1C;;;;OAIG;IACU,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvD;;;;OAIG;IACU,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1D;;;;;;OAMG;IACI,eAAe,CACrB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACnB,oBAAoB;IAkBvB;;;;OAIG;IACI,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI;IAiBpE;;;;;;OAMG;IACI,aAAa,CACnB,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACnB,IAAI;IAkBP;;;;OAIG;IACI,kBAAkB,IAAI,oBAAoB,EAAE;IAOnD,OAAO,CAAC,WAAW;IA4BnB,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,iBAAiB;YAwBX,OAAO;IA0CrB,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,YAAY;IAkBb,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAS7C;;;;;OAKG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAQ7C,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4D;IAE3F;;;;;;OAMG;IACI,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQ3E;;;;OAIG;IACI,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKnD;;;;;OAKG;IACI,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAGjF"}
@@ -1 +1 @@
1
- {"version":3,"file":"adduser.d.ts","sourceRoot":"","sources":["../../src/commands/adduser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,cAAc,EAAE,WAmB5B,CAAC"}
1
+ {"version":3,"file":"adduser.d.ts","sourceRoot":"","sources":["../../src/commands/adduser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,cAAc,EAAE,WAqB5B,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export const adduserCommand = {
2
2
  name: "adduser",
3
+ description: "Add a new user",
4
+ category: "users",
3
5
  params: ["<username> <password>"],
4
6
  run: async ({ authUser, shell, args }) => {
5
7
  if (authUser !== "root") {
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const awkCommand: ShellModule;
3
+ //# sourceMappingURL=awk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"awk.d.ts","sourceRoot":"","sources":["../../src/commands/awk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,UAAU,EAAE,WA0BxB,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { getFlag } from "./command-helpers";
2
+ export const awkCommand = {
3
+ name: "awk",
4
+ description: "Pattern scanning and processing language (minimal)",
5
+ category: "text",
6
+ params: ["[-F <sep>] '<program>' [file]"],
7
+ run: ({ args, stdin }) => {
8
+ const sep = getFlag(args, ["-F"]) ?? " ";
9
+ const prog = args.find((a) => !a.startsWith("-") && a !== sep);
10
+ if (!prog)
11
+ return { stderr: "awk: no program", exitCode: 1 };
12
+ // Only support print $N and {print $N} patterns
13
+ const printMatch = prog.match(/^\{?\s*print\s+([^}]+)\s*\}?$/);
14
+ if (!printMatch)
15
+ return { stderr: `awk: unsupported program: ${prog}`, exitCode: 1 };
16
+ const fields = printMatch[1].split(/\s*,\s*/).map((f) => f.trim());
17
+ const lines = (stdin ?? "").split("\n").filter(Boolean);
18
+ const out = lines.map((line) => {
19
+ const parts = line.split(sep === " " ? /\s+/ : sep);
20
+ return fields.map((f) => {
21
+ if (f === "$0")
22
+ return line;
23
+ const n = parseInt(f.replace("$", ""), 10);
24
+ return Number.isNaN(n) ? f.replace(/"/g, "") : (parts[n - 1] ?? "");
25
+ }).join(sep === " " ? "\t" : sep);
26
+ });
27
+ return { stdout: out.join("\n"), exitCode: 0 };
28
+ },
29
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const base64Command: ShellModule;
3
+ //# sourceMappingURL=base64.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../../src/commands/base64.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,aAAa,EAAE,WAc3B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { ifFlag } from "./command-helpers";
2
+ export const base64Command = {
3
+ name: "base64",
4
+ description: "Encode/decode base64",
5
+ category: "text",
6
+ params: ["[-d] [file]"],
7
+ run: ({ args, stdin }) => {
8
+ const decode = ifFlag(args, ["-d", "--decode"]);
9
+ const input = stdin ?? "";
10
+ if (decode) {
11
+ try {
12
+ return { stdout: Buffer.from(input.trim(), "base64").toString("utf8"), exitCode: 0 };
13
+ }
14
+ catch {
15
+ return { stderr: "base64: invalid input", exitCode: 1 };
16
+ }
17
+ }
18
+ return { stdout: Buffer.from(input).toString("base64"), exitCode: 0 };
19
+ },
20
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"cat.d.ts","sourceRoot":"","sources":["../../src/commands/cat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,UAAU,EAAE,WAaxB,CAAC"}
1
+ {"version":3,"file":"cat.d.ts","sourceRoot":"","sources":["../../src/commands/cat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,UAAU,EAAE,WAexB,CAAC"}
@@ -2,6 +2,8 @@ import { getArg } from "./command-helpers";
2
2
  import { assertPathAccess, resolveReadablePath } from "./helpers";
3
3
  export const catCommand = {
4
4
  name: "cat",
5
+ description: "Concatenate and print files",
6
+ category: "files",
5
7
  params: ["<file>"],
6
8
  run: ({ authUser, shell, cwd, args }) => {
7
9
  const fileArg = getArg(args, 0);
@@ -1 +1 @@
1
- {"version":3,"file":"cd.d.ts","sourceRoot":"","sources":["../../src/commands/cd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,SAAS,EAAE,WAiBvB,CAAC"}
1
+ {"version":3,"file":"cd.d.ts","sourceRoot":"","sources":["../../src/commands/cd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,SAAS,EAAE,WAmBvB,CAAC"}
@@ -1,6 +1,8 @@
1
1
  import { assertPathAccess, resolvePath } from "./helpers";
2
2
  export const cdCommand = {
3
3
  name: "cd",
4
+ description: "Change directory",
5
+ category: "navigation",
4
6
  params: ["[path]"],
5
7
  run: ({ authUser, shell, cwd, args, mode }) => {
6
8
  const target = resolvePath(cwd, args[0] ?? "/virtual-env-js");
@@ -1 +1 @@
1
- {"version":3,"file":"chmod.d.ts","sourceRoot":"","sources":["../../src/commands/chmod.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,YAAY,EAAE,WA6B1B,CAAC"}
1
+ {"version":3,"file":"chmod.d.ts","sourceRoot":"","sources":["../../src/commands/chmod.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,YAAY,EAAE,WA+B1B,CAAC"}
@@ -1,6 +1,8 @@
1
1
  import { assertPathAccess, resolvePath } from "./helpers";
2
2
  export const chmodCommand = {
3
3
  name: "chmod",
4
+ description: "Change file permissions",
5
+ category: "files",
4
6
  params: ["<mode> <file>"],
5
7
  run: ({ authUser, shell, cwd, args }) => {
6
8
  const [modeArg, fileArg] = args;
@@ -1 +1 @@
1
- {"version":3,"file":"clear.d.ts","sourceRoot":"","sources":["../../src/commands/clear.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,YAAY,EAAE,WAI1B,CAAC"}
1
+ {"version":3,"file":"clear.d.ts","sourceRoot":"","sources":["../../src/commands/clear.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,YAAY,EAAE,WAO1B,CAAC"}
@@ -1,5 +1,8 @@
1
1
  export const clearCommand = {
2
2
  name: "clear",
3
+ description: "Clear the terminal screen",
4
+ category: "shell",
3
5
  params: [],
4
- run: () => ({ clearScreen: true, exitCode: 0 }),
6
+ // clearScreen flag triggers \x1b[2J\x1b[H in the shell layer
7
+ run: () => ({ clearScreen: true, stdout: "", exitCode: 0 }),
5
8
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cp.d.ts","sourceRoot":"","sources":["../../src/commands/cp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,SAAS,EAAE,WAuEvB,CAAC"}
1
+ {"version":3,"file":"cp.d.ts","sourceRoot":"","sources":["../../src/commands/cp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,SAAS,EAAE,WAyEvB,CAAC"}
@@ -2,6 +2,8 @@ import { ifFlag } from "./command-helpers";
2
2
  import { assertPathAccess, resolvePath } from "./helpers";
3
3
  export const cpCommand = {
4
4
  name: "cp",
5
+ description: "Copy files or directories",
6
+ category: "files",
5
7
  params: ["[-r] <source> <dest>"],
6
8
  run: ({ authUser, shell, cwd, args }) => {
7
9
  const recursive = ifFlag(args, ["-r", "-R", "--recursive"]);
@@ -1 +1 @@
1
- {"version":3,"file":"curl.d.ts","sourceRoot":"","sources":["../../src/commands/curl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AASrD,eAAO,MAAM,WAAW,EAAE,WAiDzB,CAAC"}
1
+ {"version":3,"file":"curl.d.ts","sourceRoot":"","sources":["../../src/commands/curl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AASrD,eAAO,MAAM,WAAW,EAAE,WAmDzB,CAAC"}
@@ -2,6 +2,8 @@ import { parseArgs } from "./command-helpers";
2
2
  import { assertPathAccess, normalizeTerminalOutput, resolvePath, runHostCommand, } from "./helpers";
3
3
  export const curlCommand = {
4
4
  name: "curl",
5
+ description: "HTTP client",
6
+ category: "network",
5
7
  params: ["[-o file] <url>"],
6
8
  run: async ({ authUser, cwd, args, shell }) => {
7
9
  const { flagsWithValues, positionals } = parseArgs(args, {
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const cutCommand: ShellModule;
3
+ //# sourceMappingURL=cut.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cut.d.ts","sourceRoot":"","sources":["../../src/commands/cut.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,UAAU,EAAE,WAyBxB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { getFlag } from "./command-helpers";
2
+ export const cutCommand = {
3
+ name: "cut",
4
+ description: "Remove sections from lines",
5
+ category: "text",
6
+ params: ["-d <delim> -f <fields> [file]"],
7
+ run: ({ args, stdin }) => {
8
+ const delim = getFlag(args, ["-d"]) ?? "\t";
9
+ const fields = getFlag(args, ["-f"]) ?? "1";
10
+ const cols = fields.split(",").map((f) => {
11
+ const [a, b] = f.split("-").map(Number);
12
+ return b !== undefined ? { from: (a ?? 1) - 1, to: b - 1 } : { from: (a ?? 1) - 1, to: (a ?? 1) - 1 };
13
+ });
14
+ const lines = (stdin ?? "").split("\n");
15
+ const out = lines.map((line) => {
16
+ const parts = line.split(delim);
17
+ const selected = [];
18
+ for (const col of cols) {
19
+ for (let i = col.from; i <= Math.min(col.to, parts.length - 1); i++) {
20
+ selected.push(parts[i] ?? "");
21
+ }
22
+ }
23
+ return selected.join(delim);
24
+ });
25
+ return { stdout: out.join("\n"), exitCode: 0 };
26
+ },
27
+ };