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
@@ -0,0 +1,50 @@
1
+ import { getFlag } from "./command-helpers";
2
+ import { assertPathAccess, resolvePath } from "./helpers";
3
+ export const findCommand = {
4
+ name: "find",
5
+ description: "Search for files",
6
+ category: "files",
7
+ params: ["[path] [-name <pattern>] [-type f|d]"],
8
+ run: ({ authUser, shell, cwd, args }) => {
9
+ const namePattern = getFlag(args, ["-name"]);
10
+ const typeFilter = getFlag(args, ["-type"]);
11
+ const positionals = args.filter((a) => !a.startsWith("-") && a !== namePattern && a !== typeFilter);
12
+ const rootArg = positionals[0] ?? ".";
13
+ const rootPath = resolvePath(cwd, rootArg);
14
+ try {
15
+ assertPathAccess(authUser, rootPath, "find");
16
+ if (!shell.vfs.exists(rootPath)) {
17
+ return {
18
+ stderr: `find: ${rootArg}: No such file or directory`,
19
+ exitCode: 1,
20
+ };
21
+ }
22
+ }
23
+ catch (err) {
24
+ const msg = err instanceof Error ? err.message : String(err);
25
+ return { stderr: `find: ${msg}`, exitCode: 1 };
26
+ }
27
+ const nameRegex = namePattern
28
+ ? new RegExp(`^${namePattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`)
29
+ : null;
30
+ const results = [];
31
+ const walk = (currentPath, display) => {
32
+ const stat = shell.vfs.stat(currentPath);
33
+ const matchesType = !typeFilter ||
34
+ (typeFilter === "f" && stat.type === "file") ||
35
+ (typeFilter === "d" && stat.type === "directory");
36
+ const matchesName = !nameRegex || nameRegex.test(currentPath.split("/").pop() ?? "");
37
+ if (matchesType && matchesName)
38
+ results.push(display);
39
+ if (stat.type === "directory") {
40
+ for (const entry of shell.vfs.list(currentPath)) {
41
+ const full = `${currentPath}/${entry}`;
42
+ const disp = `${display}/${entry}`;
43
+ walk(full, disp);
44
+ }
45
+ }
46
+ };
47
+ walk(rootPath, rootArg);
48
+ return { stdout: results.join("\n"), exitCode: 0 };
49
+ },
50
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/commands/grep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WA2EzB,CAAC"}
1
+ {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/commands/grep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAwFzB,CAAC"}
@@ -2,11 +2,15 @@ import { parseArgs } from "./command-helpers";
2
2
  import { assertPathAccess, resolvePath } from "./helpers";
3
3
  export const grepCommand = {
4
4
  name: "grep",
5
- params: ["[-i] [-v] <pattern> [file...]"],
5
+ description: "Search text patterns",
6
+ category: "text",
7
+ params: ["[-i] [-v] [-n] [-r] <pattern> [file...]"],
6
8
  run: ({ authUser, shell, cwd, args, stdin }) => {
7
- const { flags, positionals } = parseArgs(args, { flags: ["-i", "-v"] });
9
+ const { flags, positionals } = parseArgs(args, { flags: ["-i", "-v", "-n", "-r"] });
8
10
  const caseInsensitive = flags.has("-i");
9
11
  const invertMatch = flags.has("-v");
12
+ const showLineNumbers = flags.has("-n");
13
+ const recursive = flags.has("-r");
10
14
  const pattern = positionals[0];
11
15
  const files = positionals.slice(1);
12
16
  if (!pattern) {
@@ -14,51 +18,70 @@ export const grepCommand = {
14
18
  }
15
19
  let regex;
16
20
  try {
17
- const flags = caseInsensitive ? "gmi" : "gm";
18
- regex = new RegExp(pattern, flags);
21
+ // No "g" flag avoids the stateful lastIndex problem with regex.test()
22
+ const regexFlags = caseInsensitive ? "mi" : "m";
23
+ regex = new RegExp(pattern, regexFlags);
19
24
  }
20
25
  catch {
21
26
  return { stderr: `grep: invalid regex: ${pattern}`, exitCode: 1 };
22
27
  }
23
- const results = [];
24
- if (files.length === 0) {
25
- if (!stdin) {
26
- return { stdout: "", exitCode: 1 };
27
- }
28
- const lines = stdin.split("\n");
29
- for (const line of lines) {
30
- regex.lastIndex = 0;
28
+ const matchLines = (content, prefix = "") => {
29
+ const lines = content.split("\n");
30
+ const out = [];
31
+ for (let i = 0; i < lines.length; i++) {
32
+ const line = lines[i] ?? "";
31
33
  const matches = regex.test(line);
32
34
  const shouldInclude = invertMatch ? !matches : matches;
33
35
  if (shouldInclude) {
34
- results.push(line);
36
+ const lineLabel = showLineNumbers ? `${i + 1}:` : "";
37
+ out.push(`${prefix}${lineLabel}${line}`);
35
38
  }
36
39
  }
37
- return {
38
- stdout: results.length > 0 ? results.join("\n") : "",
39
- exitCode: results.length > 0 ? 0 : 1,
40
+ return out;
41
+ };
42
+ const readPaths = (base) => {
43
+ if (!shell.vfs.exists(base))
44
+ return [];
45
+ const stat = shell.vfs.stat(base);
46
+ if (stat.type === "file")
47
+ return [base];
48
+ if (!recursive)
49
+ return [];
50
+ const paths = [];
51
+ const walk = (dir) => {
52
+ for (const entry of shell.vfs.list(dir)) {
53
+ const full = `${dir}/${entry}`;
54
+ const s = shell.vfs.stat(full);
55
+ if (s.type === "file")
56
+ paths.push(full);
57
+ else
58
+ walk(full);
59
+ }
40
60
  };
61
+ walk(base);
62
+ return paths;
63
+ };
64
+ const results = [];
65
+ if (files.length === 0) {
66
+ if (!stdin)
67
+ return { stdout: "", exitCode: 1 };
68
+ results.push(...matchLines(stdin));
41
69
  }
42
- for (const file of files) {
43
- const target = resolvePath(cwd, file);
44
- try {
45
- assertPathAccess(authUser, target, "grep");
46
- const content = shell.vfs.readFile(target);
47
- const lines = content.split("\n");
48
- for (const line of lines) {
49
- regex.lastIndex = 0;
50
- const matches = regex.test(line);
51
- const shouldInclude = invertMatch ? !matches : matches;
52
- if (shouldInclude) {
53
- results.push(line);
54
- }
70
+ else {
71
+ const resolvedPaths = files.flatMap((f) => {
72
+ const target = resolvePath(cwd, f);
73
+ return readPaths(target).map((p) => ({ file: f, path: p }));
74
+ });
75
+ for (const { file, path: filePath } of resolvedPaths) {
76
+ try {
77
+ assertPathAccess(authUser, filePath, "grep");
78
+ const content = shell.vfs.readFile(filePath);
79
+ const prefix = resolvedPaths.length > 1 ? `${file}:` : "";
80
+ results.push(...matchLines(content, prefix));
81
+ }
82
+ catch {
83
+ return { stderr: `grep: ${file}: No such file or directory`, exitCode: 1 };
55
84
  }
56
- }
57
- catch {
58
- return {
59
- stderr: `grep: ${file}: No such file or directory`,
60
- exitCode: 1,
61
- };
62
85
  }
63
86
  }
64
87
  return {
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const groupsCommand: ShellModule;
3
+ //# sourceMappingURL=groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/commands/groups.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,aAAa,EAAE,WAW3B,CAAC"}
@@ -0,0 +1,12 @@
1
+ export const groupsCommand = {
2
+ name: "groups",
3
+ description: "Print group memberships",
4
+ category: "system",
5
+ params: ["[user]"],
6
+ run: ({ authUser, shell, args }) => {
7
+ const target = args[0] ?? authUser;
8
+ const isSudo = shell.users.isSudoer(target);
9
+ const grps = isSudo ? `${target} sudo root` : target;
10
+ return { stdout: grps, exitCode: 0 };
11
+ },
12
+ };
@@ -0,0 +1,4 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const gzipCommand: ShellModule;
3
+ export declare const gunzipCommand: ShellModule;
4
+ //# sourceMappingURL=gzip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gzip.d.ts","sourceRoot":"","sources":["../../src/commands/gzip.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WAYzB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAa3B,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { resolvePath } from "./helpers";
2
+ export const gzipCommand = {
3
+ name: "gzip",
4
+ description: "Compress files",
5
+ category: "archive",
6
+ params: ["<file>"],
7
+ run: ({ shell, cwd, args }) => {
8
+ const file = args[0];
9
+ if (!file)
10
+ return { stderr: "gzip: no file specified", exitCode: 1 };
11
+ const p = resolvePath(cwd, file);
12
+ try {
13
+ shell.vfs.compressFile(p);
14
+ return { exitCode: 0 };
15
+ }
16
+ catch {
17
+ return { stderr: `gzip: ${file}: No such file or directory`, exitCode: 1 };
18
+ }
19
+ },
20
+ };
21
+ export const gunzipCommand = {
22
+ name: "gunzip",
23
+ description: "Decompress files",
24
+ category: "archive",
25
+ params: ["<file>"],
26
+ aliases: ["zcat"],
27
+ run: ({ shell, cwd, args }) => {
28
+ const file = args[0];
29
+ if (!file)
30
+ return { stderr: "gunzip: no file specified", exitCode: 1 };
31
+ const p = resolvePath(cwd, file);
32
+ try {
33
+ shell.vfs.decompressFile(p);
34
+ return { exitCode: 0 };
35
+ }
36
+ catch {
37
+ return { stderr: `gunzip: ${file}: No such file or directory`, exitCode: 1 };
38
+ }
39
+ },
40
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const headCommand: ShellModule;
3
+ //# sourceMappingURL=head.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../src/commands/head.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAgCzB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { getFlag } from "./command-helpers";
2
+ import { assertPathAccess, resolvePath } from "./helpers";
3
+ export const headCommand = {
4
+ name: "head",
5
+ description: "Output first lines",
6
+ category: "text",
7
+ params: ["[-n <lines>] [file...]"],
8
+ run: ({ authUser, shell, cwd, args, stdin }) => {
9
+ const nArg = getFlag(args, ["-n"]);
10
+ const n = typeof nArg === "string" ? parseInt(nArg, 10) : 10;
11
+ const positionals = args.filter((a) => !a.startsWith("-") && a !== nArg);
12
+ const take = (content) => content.split("\n").slice(0, n).join("\n");
13
+ if (positionals.length === 0) {
14
+ return { stdout: take(stdin ?? ""), exitCode: 0 };
15
+ }
16
+ const results = [];
17
+ for (const file of positionals) {
18
+ const filePath = resolvePath(cwd, file);
19
+ try {
20
+ assertPathAccess(authUser, filePath, "head");
21
+ results.push(take(shell.vfs.readFile(filePath)));
22
+ }
23
+ catch {
24
+ return {
25
+ stderr: `head: ${file}: No such file or directory`,
26
+ exitCode: 1,
27
+ };
28
+ }
29
+ }
30
+ return { stdout: results.join("\n"), exitCode: 0 };
31
+ },
32
+ };
@@ -1,3 +1,3 @@
1
1
  import type { ShellModule } from "../types/commands";
2
- export declare function createHelpCommand(getNames: () => string[]): ShellModule;
2
+ export declare function createHelpCommand(_getNames: () => string[]): ShellModule;
3
3
  //# sourceMappingURL=help.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/commands/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,MAAM,EAAE,GAAG,WAAW,CAMvE"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/commands/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAoBrD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,MAAM,EAAE,GAAG,WAAW,CAkExE"}
@@ -1,7 +1,79 @@
1
- export function createHelpCommand(getNames) {
1
+ import { getCommandModulesPublic } from "./index";
2
+ const CATEGORY_ORDER = ["navigation", "files", "text", "archive", "system", "network", "shell", "users", "misc"];
3
+ const CATEGORY_LABELS = {
4
+ navigation: "Navigation",
5
+ files: "Files & Filesystem",
6
+ text: "Text Processing",
7
+ archive: "Archive & Compression",
8
+ system: "System",
9
+ network: "Network",
10
+ shell: "Shell",
11
+ users: "Users & Permissions",
12
+ misc: "Miscellaneous",
13
+ };
14
+ function padRight(s, n) {
15
+ return s.length >= n ? s : s + " ".repeat(n - s.length);
16
+ }
17
+ export function createHelpCommand(_getNames) {
2
18
  return {
3
19
  name: "help",
4
- params: [],
5
- run: () => ({ stdout: `Builtins: ${getNames().join(" ")}`, exitCode: 0 }),
20
+ description: "Display this help message",
21
+ category: "shell",
22
+ params: ["[command]"],
23
+ run: ({ args }) => {
24
+ const modules = getCommandModulesPublic();
25
+ // help <command>
26
+ if (args[0]) {
27
+ const mod = modules.find((m) => m.name === args[0] || m.aliases?.includes(args[0]));
28
+ if (!mod)
29
+ return { stderr: `help: no help for '${args[0]}'`, exitCode: 1 };
30
+ const aliases = mod.aliases?.length ? ` aliases: ${mod.aliases.join(", ")}\n` : "";
31
+ const params = mod.params.map((p) => ` ${mod.name} ${p}`).join("\n");
32
+ return {
33
+ stdout: [
34
+ `\x1b[1m${mod.name}\x1b[0m — ${mod.description ?? "no description"}`,
35
+ aliases,
36
+ "Usage:",
37
+ params || ` ${mod.name}`,
38
+ ].filter(Boolean).join("\n"),
39
+ exitCode: 0,
40
+ };
41
+ }
42
+ // Full help — grouped by category
43
+ const grouped = {};
44
+ for (const mod of modules) {
45
+ const cat = mod.category ?? "misc";
46
+ if (!grouped[cat])
47
+ grouped[cat] = [];
48
+ grouped[cat].push(mod);
49
+ }
50
+ const lines = [];
51
+ lines.push("\x1b[1mAvailable commands\x1b[0m");
52
+ lines.push("");
53
+ const cats = [
54
+ ...CATEGORY_ORDER.filter((c) => grouped[c]),
55
+ ...Object.keys(grouped).filter((c) => !CATEGORY_ORDER.includes(c)),
56
+ ];
57
+ for (const cat of cats) {
58
+ const mods = grouped[cat];
59
+ if (!mods || mods.length === 0)
60
+ continue;
61
+ lines.push(`\x1b[33m${CATEGORY_LABELS[cat] ?? cat}\x1b[0m`);
62
+ // Two-column layout
63
+ const sorted = [...mods].sort((a, b) => a.name.localeCompare(b.name));
64
+ for (let i = 0; i < sorted.length; i += 2) {
65
+ const left = sorted[i];
66
+ const right = sorted[i + 1];
67
+ const leftStr = ` \x1b[36m${padRight(left.name, 14)}\x1b[0m ${left.description ?? ""}`;
68
+ const rightStr = right
69
+ ? ` \x1b[36m${padRight(right.name, 14)}\x1b[0m ${right.description ?? ""}`
70
+ : "";
71
+ lines.push(rightStr ? `${leftStr.padEnd(44)}${rightStr}` : leftStr);
72
+ }
73
+ lines.push("");
74
+ }
75
+ lines.push("Type \x1b[1mhelp <command>\x1b[0m for usage details.");
76
+ return { stdout: lines.join("\n"), exitCode: 0 };
77
+ },
6
78
  };
7
79
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hostname.d.ts","sourceRoot":"","sources":["../../src/commands/hostname.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,eAAe,EAAE,WAI7B,CAAC"}
1
+ {"version":3,"file":"hostname.d.ts","sourceRoot":"","sources":["../../src/commands/hostname.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,eAAe,EAAE,WAM7B,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export const hostnameCommand = {
2
2
  name: "hostname",
3
+ description: "Print hostname",
4
+ category: "system",
3
5
  params: [],
4
6
  run: ({ hostname }) => ({ stdout: hostname, exitCode: 0 }),
5
7
  };
@@ -1 +1 @@
1
- {"version":3,"file":"htop.d.ts","sourceRoot":"","sources":["../../src/commands/htop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAUzB,CAAC"}
1
+ {"version":3,"file":"htop.d.ts","sourceRoot":"","sources":["../../src/commands/htop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAYzB,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export const htopCommand = {
2
2
  name: "htop",
3
+ description: "System monitor",
4
+ category: "system",
3
5
  params: [],
4
6
  run: ({ mode }) => {
5
7
  if (mode === "exec") {
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const idCommand: ShellModule;
3
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/commands/id.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,SAAS,EAAE,WAavB,CAAC"}
@@ -0,0 +1,14 @@
1
+ export const idCommand = {
2
+ name: "id",
3
+ description: "Print user identity",
4
+ category: "system",
5
+ params: ["[user]"],
6
+ run: ({ authUser, shell, args }) => {
7
+ const target = args[0] ?? authUser;
8
+ const uid = target === "root" ? 0 : 1000;
9
+ const gid = uid;
10
+ const isSudo = shell.users.isSudoer(target);
11
+ const groups = isSudo ? `${gid}(${target}),0(root)` : `${gid}(${target})`;
12
+ return { stdout: `uid=${uid}(${target}) gid=${gid}(${target}) groups=${groups}`, exitCode: 0 };
13
+ },
14
+ };
@@ -1,8 +1,11 @@
1
+ /** biome-ignore-all lint/style/useNamingConvention: ENV VARIABLES */
1
2
  import type { VirtualShell } from "../VirtualShell";
2
- import type { CommandContext, CommandMode, CommandResult, ShellModule } from "../types/commands";
3
+ import type { CommandContext, CommandMode, CommandResult, ShellEnv, ShellModule } from "../types/commands";
3
4
  export declare function registerCommand(module: ShellModule): void;
4
5
  export declare function createCustomCommand(name: string, params: string[], run: (ctx: CommandContext) => CommandResult | Promise<CommandResult>): ShellModule;
5
6
  export declare function getCommandNames(): string[];
7
+ export declare function getCommandModulesPublic(): ShellModule[];
6
8
  export declare function resolveModule(name: string): ShellModule | undefined;
7
- export declare function runCommand(rawInput: string, authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, stdin?: string): Promise<CommandResult>;
9
+ export declare function makeDefaultEnv(authUser: string, hostname: string): ShellEnv;
10
+ export declare function runCommand(rawInput: string, authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, stdin?: string, env?: ShellEnv): Promise<CommandResult>;
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EACX,cAAc,EACd,WAAW,EACX,aAAa,EACb,WAAW,EACX,MAAM,mBAAmB,CAAC;AAgG3B,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAsBzD;AAED,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,GAAG,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GAClE,WAAW,CAMb;AAED,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAK1C;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAKnE;AAsDD,wBAAsB,UAAU,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,CAAC,CA6DxB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EACX,cAAc,EACd,WAAW,EACX,aAAa,EACb,QAAQ,EACR,WAAW,EACX,MAAM,mBAAmB,CAAC;AA8G3B,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAYzD;AAED,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,GAAG,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GAClE,WAAW,CAEb;AAED,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAG1C;AAED,wBAAgB,uBAAuB,IAAI,WAAW,EAAE,CAEvD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAGnE;AA8BD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAc3E;AAED,wBAAsB,UAAU,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,QAAQ,GACZ,OAAO,CAAC,aAAa,CAAC,CA+CxB"}