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
@@ -505,6 +505,39 @@ export class VirtualUserManager extends EventEmitter {
505
505
  throw new Error("invalid password");
506
506
  }
507
507
  }
508
+ authorizedKeys = new Map();
509
+ /**
510
+ * Adds an SSH public key for a user, enabling public-key authentication.
511
+ *
512
+ * @param username Target user.
513
+ * @param algo Key algorithm (e.g. "ssh-rsa", "ssh-ed25519").
514
+ * @param data Raw key data as a Buffer (the base64-decoded key bytes).
515
+ */
516
+ addAuthorizedKey(username, algo, data) {
517
+ perf.mark("addAuthorizedKey");
518
+ const keys = this.authorizedKeys.get(username) ?? [];
519
+ keys.push({ algo, data });
520
+ this.authorizedKeys.set(username, keys);
521
+ this.emit("key:add", { username, algo });
522
+ }
523
+ /**
524
+ * Removes all authorized keys for a user.
525
+ *
526
+ * @param username Target user.
527
+ */
528
+ removeAuthorizedKeys(username) {
529
+ this.authorizedKeys.delete(username);
530
+ this.emit("key:remove", { username });
531
+ }
532
+ /**
533
+ * Returns the list of authorized keys for a user.
534
+ * Returns an empty array when no keys are registered.
535
+ *
536
+ * @param username Target user.
537
+ */
538
+ getAuthorizedKeys(username) {
539
+ return this.authorizedKeys.get(username) ?? [];
540
+ }
508
541
  }
509
542
  function normalizeVfsPath(targetPath) {
510
543
  const normalized = path.posix.normalize(targetPath);
@@ -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");
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const chmodCommand: ShellModule;
3
+ //# sourceMappingURL=chmod.d.ts.map
@@ -0,0 +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,WA+B1B,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { assertPathAccess, resolvePath } from "./helpers";
2
+ export const chmodCommand = {
3
+ name: "chmod",
4
+ description: "Change file permissions",
5
+ category: "files",
6
+ params: ["<mode> <file>"],
7
+ run: ({ authUser, shell, cwd, args }) => {
8
+ const [modeArg, fileArg] = args;
9
+ if (!modeArg || !fileArg) {
10
+ return { stderr: "chmod: missing operand", exitCode: 1 };
11
+ }
12
+ const filePath = resolvePath(cwd, fileArg);
13
+ try {
14
+ assertPathAccess(authUser, filePath, "chmod");
15
+ if (!shell.vfs.exists(filePath)) {
16
+ return {
17
+ stderr: `chmod: ${fileArg}: No such file or directory`,
18
+ exitCode: 1,
19
+ };
20
+ }
21
+ const mode = parseInt(modeArg, 8);
22
+ if (Number.isNaN(mode)) {
23
+ return { stderr: `chmod: invalid mode: ${modeArg}`, exitCode: 1 };
24
+ }
25
+ shell.vfs.chmod(filePath, mode);
26
+ return { exitCode: 0 };
27
+ }
28
+ catch (err) {
29
+ const msg = err instanceof Error ? err.message : String(err);
30
+ return { stderr: `chmod: ${msg}`, exitCode: 1 };
31
+ }
32
+ },
33
+ };
@@ -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
  };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const cpCommand: ShellModule;
3
+ //# sourceMappingURL=cp.d.ts.map
@@ -0,0 +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,WAyEvB,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { ifFlag } from "./command-helpers";
2
+ import { assertPathAccess, resolvePath } from "./helpers";
3
+ export const cpCommand = {
4
+ name: "cp",
5
+ description: "Copy files or directories",
6
+ category: "files",
7
+ params: ["[-r] <source> <dest>"],
8
+ run: ({ authUser, shell, cwd, args }) => {
9
+ const recursive = ifFlag(args, ["-r", "-R", "--recursive"]);
10
+ const positionals = args.filter((a) => !a.startsWith("-"));
11
+ const [srcArg, destArg] = positionals;
12
+ if (!srcArg || !destArg) {
13
+ return { stderr: "cp: missing operand", exitCode: 1 };
14
+ }
15
+ const srcPath = resolvePath(cwd, srcArg);
16
+ const destPath = resolvePath(cwd, destArg);
17
+ try {
18
+ assertPathAccess(authUser, srcPath, "cp");
19
+ assertPathAccess(authUser, destPath, "cp");
20
+ if (!shell.vfs.exists(srcPath)) {
21
+ return {
22
+ stderr: `cp: ${srcArg}: No such file or directory`,
23
+ exitCode: 1,
24
+ };
25
+ }
26
+ const srcStat = shell.vfs.stat(srcPath);
27
+ if (srcStat.type === "directory") {
28
+ if (!recursive) {
29
+ return {
30
+ stderr: `cp: ${srcArg}: is a directory (use -r)`,
31
+ exitCode: 1,
32
+ };
33
+ }
34
+ const copyDir = (from, to) => {
35
+ shell.vfs.mkdir(to, 0o755);
36
+ for (const entry of shell.vfs.list(from)) {
37
+ const fromEntry = `${from}/${entry}`;
38
+ const toEntry = `${to}/${entry}`;
39
+ const stat = shell.vfs.stat(fromEntry);
40
+ if (stat.type === "directory") {
41
+ copyDir(fromEntry, toEntry);
42
+ }
43
+ else {
44
+ const content = shell.vfs.readFileRaw(fromEntry);
45
+ shell.writeFileAsUser(authUser, toEntry, content);
46
+ }
47
+ }
48
+ };
49
+ const finalDest = shell.vfs.exists(destPath) &&
50
+ shell.vfs.stat(destPath).type === "directory"
51
+ ? `${destPath}/${srcArg.split("/").pop()}`
52
+ : destPath;
53
+ copyDir(srcPath, finalDest);
54
+ }
55
+ else {
56
+ const finalDest = shell.vfs.exists(destPath) &&
57
+ shell.vfs.stat(destPath).type === "directory"
58
+ ? `${destPath}/${srcArg.split("/").pop()}`
59
+ : destPath;
60
+ const content = shell.vfs.readFileRaw(srcPath);
61
+ shell.writeFileAsUser(authUser, finalDest, content);
62
+ }
63
+ return { exitCode: 0 };
64
+ }
65
+ catch (err) {
66
+ const msg = err instanceof Error ? err.message : String(err);
67
+ return { stderr: `cp: ${msg}`, exitCode: 1 };
68
+ }
69
+ },
70
+ };
@@ -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
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const dateCommand: ShellModule;
3
+ //# sourceMappingURL=date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/commands/date.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAqBzB,CAAC"}
@@ -0,0 +1,22 @@
1
+ export const dateCommand = {
2
+ name: "date",
3
+ description: "Print current date and time",
4
+ category: "system",
5
+ params: ["[+format]"],
6
+ run: ({ args }) => {
7
+ const now = new Date();
8
+ const fmt = args[0];
9
+ if (fmt?.startsWith("+")) {
10
+ const f = fmt.slice(1)
11
+ .replace("%Y", String(now.getFullYear()))
12
+ .replace("%m", String(now.getMonth() + 1).padStart(2, "0"))
13
+ .replace("%d", String(now.getDate()).padStart(2, "0"))
14
+ .replace("%H", String(now.getHours()).padStart(2, "0"))
15
+ .replace("%M", String(now.getMinutes()).padStart(2, "0"))
16
+ .replace("%S", String(now.getSeconds()).padStart(2, "0"))
17
+ .replace("%s", String(Math.floor(now.getTime() / 1000)));
18
+ return { stdout: f, exitCode: 0 };
19
+ }
20
+ return { stdout: now.toString(), exitCode: 0 };
21
+ },
22
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"deluser.d.ts","sourceRoot":"","sources":["../../src/commands/deluser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,cAAc,EAAE,WAgB5B,CAAC"}
1
+ {"version":3,"file":"deluser.d.ts","sourceRoot":"","sources":["../../src/commands/deluser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,cAAc,EAAE,WAkB5B,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export const deluserCommand = {
2
2
  name: "deluser",
3
+ description: "Delete a user",
4
+ category: "users",
3
5
  params: ["<username>"],
4
6
  run: async ({ authUser, args, shell }) => {
5
7
  if (authUser !== "root") {
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const dfCommand: ShellModule;
3
+ //# sourceMappingURL=df.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"df.d.ts","sourceRoot":"","sources":["../../src/commands/df.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,SAAS,EAAE,WAevB,CAAC"}
@@ -0,0 +1,16 @@
1
+ export const dfCommand = {
2
+ name: "df",
3
+ description: "Report filesystem disk space usage",
4
+ category: "system",
5
+ params: ["[-h]"],
6
+ run: ({ shell }) => {
7
+ const bytes = shell.vfs.getUsageBytes();
8
+ const used = (bytes / 1024).toFixed(0);
9
+ const total = "1048576"; // 1GB virtual
10
+ const avail = String(Number(total) - Number(used));
11
+ const pct = Math.round((Number(used) / Number(total)) * 100);
12
+ const hdr = "Filesystem 1K-blocks Used Available Use% Mounted on";
13
+ const row = `virtual-fs ${total.padStart(9)} ${used.padStart(7)} ${avail.padStart(9)} ${pct}% /`;
14
+ return { stdout: `${hdr}\n${row}`, exitCode: 0 };
15
+ },
16
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const diffCommand: ShellModule;
3
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WAyBzB,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { resolvePath } from "./helpers";
2
+ export const diffCommand = {
3
+ name: "diff",
4
+ description: "Compare files line by line",
5
+ category: "text",
6
+ params: ["<file1> <file2>"],
7
+ run: ({ shell, cwd, args }) => {
8
+ const [f1, f2] = args;
9
+ if (!f1 || !f2)
10
+ return { stderr: "diff: missing operand", exitCode: 1 };
11
+ const p1 = resolvePath(cwd, f1);
12
+ const p2 = resolvePath(cwd, f2);
13
+ let a, b;
14
+ try {
15
+ a = shell.vfs.readFile(p1).split("\n");
16
+ }
17
+ catch {
18
+ return { stderr: `diff: ${f1}: No such file or directory`, exitCode: 2 };
19
+ }
20
+ try {
21
+ b = shell.vfs.readFile(p2).split("\n");
22
+ }
23
+ catch {
24
+ return { stderr: `diff: ${f2}: No such file or directory`, exitCode: 2 };
25
+ }
26
+ const out = [];
27
+ const max = Math.max(a.length, b.length);
28
+ for (let i = 0; i < max; i++) {
29
+ const la = a[i];
30
+ const lb = b[i];
31
+ if (la !== lb) {
32
+ if (la !== undefined)
33
+ out.push(`< ${la}`);
34
+ if (lb !== undefined)
35
+ out.push(`> ${lb}`);
36
+ }
37
+ }
38
+ return { stdout: out.join("\n"), exitCode: out.length > 0 ? 1 : 0 };
39
+ },
40
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const duCommand: ShellModule;
3
+ //# sourceMappingURL=du.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"du.d.ts","sourceRoot":"","sources":["../../src/commands/du.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,SAAS,EAAE,WAkCvB,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { ifFlag } from "./command-helpers";
2
+ import { resolvePath } from "./helpers";
3
+ export const duCommand = {
4
+ name: "du",
5
+ description: "Estimate file space usage",
6
+ category: "system",
7
+ params: ["[-h] [-s] [path]"],
8
+ run: ({ shell, cwd, args }) => {
9
+ const human = ifFlag(args, ["-h"]);
10
+ const summary = ifFlag(args, ["-s"]);
11
+ const target = args.find((a) => !a.startsWith("-")) ?? ".";
12
+ const p = resolvePath(cwd, target);
13
+ const fmt = (b) => human ? `${(b / 1024).toFixed(1)}K` : String(Math.ceil(b / 1024));
14
+ if (!shell.vfs.exists(p))
15
+ return { stderr: `du: ${target}: No such file or directory`, exitCode: 1 };
16
+ if (summary || shell.vfs.stat(p).type === "file") {
17
+ return { stdout: `${fmt(shell.vfs.getUsageBytes(p))}\t${target}`, exitCode: 0 };
18
+ }
19
+ const lines = [];
20
+ const walk = (dir, rel) => {
21
+ let total = 0;
22
+ for (const e of shell.vfs.list(dir)) {
23
+ const full = `${dir}/${e}`, r = `${rel}/${e}`;
24
+ const st = shell.vfs.stat(full);
25
+ if (st.type === "directory")
26
+ total += walk(full, r);
27
+ else {
28
+ total += st.size;
29
+ if (!summary)
30
+ lines.push(`${fmt(st.size)}\t${r}`);
31
+ }
32
+ }
33
+ lines.push(`${fmt(total)}\t${rel}`);
34
+ return total;
35
+ };
36
+ walk(p, target);
37
+ return { stdout: lines.join("\n"), exitCode: 0 };
38
+ },
39
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"echo.d.ts","sourceRoot":"","sources":["../../src/commands/echo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUrD,eAAO,MAAM,WAAW,EAAE,WAgBzB,CAAC"}
1
+ {"version":3,"file":"echo.d.ts","sourceRoot":"","sources":["../../src/commands/echo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUrD,eAAO,MAAM,WAAW,EAAE,WAkBzB,CAAC"}
@@ -7,6 +7,8 @@ function expandEnvVars(input, env) {
7
7
  }
8
8
  export const echoCommand = {
9
9
  name: "echo",
10
+ description: "Display text",
11
+ category: "shell",
10
12
  params: ["[options] [text...]"],
11
13
  run: ({ args, authUser, stdin }) => {
12
14
  const { flags, positionals } = parseArgs(args, { flags: ["-n"] });
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,UAAU,EAAE,WAkBxB,CAAC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,UAAU,EAAE,WASxB,CAAC"}
@@ -1,18 +1,10 @@
1
- import { getAllEnvVars } from "./set";
2
1
  export const envCommand = {
3
2
  name: "env",
4
- params: ["[VAR=value...] [command]"],
5
- run: ({ authUser }) => {
6
- // For now, just display all environment variables
7
- // In a full implementation, this would also handle running commands with modified env
8
- const allVars = getAllEnvVars(authUser);
9
- const envVarsOutput = Object.entries(allVars)
10
- .map(([key, value]) => `${key}=${value}`)
11
- .sort()
12
- .join("\n");
13
- return {
14
- stdout: envVarsOutput,
15
- exitCode: 0,
16
- };
3
+ description: "Print environment variables",
4
+ category: "shell",
5
+ params: [],
6
+ run: ({ env, authUser }) => {
7
+ const vars = { ...env.vars, USER: authUser, HOME: `/home/${authUser}` };
8
+ return { stdout: Object.entries(vars).map(([k, v]) => `${k}=${v}`).join("\n"), exitCode: 0 };
17
9
  },
18
10
  };
@@ -1 +1 @@
1
- {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,aAAa,EAAE,WAiC3B,CAAC"}
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,aAAa,EAAE,WAsB3B,CAAC"}
@@ -1,32 +1,22 @@
1
- import { getArg } from "./command-helpers";
2
- import { getEnvVar, setEnvVar } from "./set";
3
1
  export const exportCommand = {
4
2
  name: "export",
3
+ description: "Set shell environment variable",
4
+ category: "shell",
5
5
  params: ["[VAR=value]"],
6
- run: ({ args }) => {
7
- // export VAR=value or export VAR (to make it available to child processes)
6
+ run: ({ args, env }) => {
8
7
  if (args.length === 0) {
9
- // List all exported variables
10
- return {
11
- stdout: "# export command - sets variables for child processes",
12
- exitCode: 0,
13
- };
8
+ const out = Object.entries(env.vars).map(([k, v]) => `declare -x ${k}="${v}"`).join("\n");
9
+ return { stdout: out, exitCode: 0 };
14
10
  }
15
- // Parse VAR=value format
16
- for (let index = 0;; index += 1) {
17
- const arg = getArg(args, index);
18
- if (!arg) {
19
- break;
20
- }
11
+ for (const arg of args) {
21
12
  if (arg.includes("=")) {
22
- const [varName, varValue] = arg.split("=", 2);
23
- if (varName && varValue !== undefined) {
24
- setEnvVar(varName, varValue);
25
- }
13
+ const eq = arg.indexOf("=");
14
+ const name = arg.slice(0, eq);
15
+ const value = arg.slice(eq + 1);
16
+ env.vars[name] = value;
26
17
  }
27
18
  else {
28
- // export VAR_NAME makes it available but we just set it
29
- setEnvVar(arg, getEnvVar(arg) || "");
19
+ // mark existing as exported (already is)
30
20
  }
31
21
  }
32
22
  return { exitCode: 0 };
@@ -0,0 +1,3 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const findCommand: ShellModule;
3
+ //# sourceMappingURL=find.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../src/commands/find.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WA0DzB,CAAC"}