typescript-virtual-container 1.5.11 → 1.6.0

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 (132) hide show
  1. package/README.md +236 -456
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/Honeypot/index.d.ts +9 -0
  4. package/dist/Honeypot/index.js +57 -0
  5. package/dist/SSHMimic/exec.d.ts +4 -0
  6. package/dist/SSHMimic/exec.js +4 -0
  7. package/dist/SSHMimic/executor.d.ts +10 -1
  8. package/dist/SSHMimic/executor.js +18 -8
  9. package/dist/SSHMimic/hostKey.d.ts +5 -0
  10. package/dist/SSHMimic/hostKey.js +5 -0
  11. package/dist/SSHMimic/loginBanner.d.ts +7 -0
  12. package/dist/SSHMimic/loginBanner.js +4 -0
  13. package/dist/SSHMimic/loginFormat.d.ts +4 -0
  14. package/dist/SSHMimic/loginFormat.js +4 -0
  15. package/dist/SSHMimic/prompt.d.ts +9 -0
  16. package/dist/SSHMimic/prompt.js +9 -0
  17. package/dist/SSHMimic/scp.d.ts +18 -0
  18. package/dist/SSHMimic/scp.js +14 -0
  19. package/dist/VirtualFileSystem/binaryPack.d.ts +7 -3
  20. package/dist/VirtualFileSystem/binaryPack.js +32 -10
  21. package/dist/VirtualFileSystem/index.d.ts +29 -0
  22. package/dist/VirtualFileSystem/index.js +126 -5
  23. package/dist/VirtualFileSystem/internalTypes.d.ts +4 -0
  24. package/dist/VirtualFileSystem/journal.d.ts +10 -4
  25. package/dist/VirtualFileSystem/journal.js +12 -2
  26. package/dist/VirtualFileSystem/path.d.ts +23 -1
  27. package/dist/VirtualFileSystem/path.js +23 -3
  28. package/dist/VirtualPackageManager/index.js +1 -1
  29. package/dist/VirtualShell/index.d.ts +3 -0
  30. package/dist/VirtualShell/index.js +12 -3
  31. package/dist/VirtualUserManager/index.d.ts +20 -1
  32. package/dist/VirtualUserManager/index.js +52 -15
  33. package/dist/commands/bc.d.ts +5 -0
  34. package/dist/commands/bc.js +5 -0
  35. package/dist/commands/cat.js +2 -2
  36. package/dist/commands/chgrp.d.ts +7 -0
  37. package/dist/commands/chgrp.js +42 -0
  38. package/dist/commands/chown.d.ts +7 -0
  39. package/dist/commands/chown.js +79 -0
  40. package/dist/commands/cp.js +4 -3
  41. package/dist/commands/dd.d.ts +7 -0
  42. package/dist/commands/dd.js +60 -0
  43. package/dist/commands/declare.js +0 -2
  44. package/dist/commands/expr.d.ts +7 -0
  45. package/dist/commands/expr.js +63 -0
  46. package/dist/commands/fun.d.ts +5 -0
  47. package/dist/commands/fun.js +5 -1
  48. package/dist/commands/help.d.ts +5 -0
  49. package/dist/commands/help.js +5 -0
  50. package/dist/commands/helpers.d.ts +43 -0
  51. package/dist/commands/helpers.js +61 -0
  52. package/dist/commands/id.d.ts +5 -0
  53. package/dist/commands/id.js +5 -0
  54. package/dist/commands/ip.d.ts +1 -0
  55. package/dist/commands/ip.js +50 -23
  56. package/dist/commands/jobs.js +43 -9
  57. package/dist/commands/kill.d.ts +1 -0
  58. package/dist/commands/kill.js +13 -5
  59. package/dist/commands/last.js +1 -1
  60. package/dist/commands/ln.d.ts +5 -0
  61. package/dist/commands/ln.js +5 -0
  62. package/dist/commands/ls.d.ts +5 -0
  63. package/dist/commands/ls.js +19 -4
  64. package/dist/commands/lsb-release.js +1 -1
  65. package/dist/commands/man.d.ts +5 -0
  66. package/dist/commands/man.js +5 -0
  67. package/dist/commands/manuals-bundle.js +242 -0
  68. package/dist/commands/miscutils.d.ts +43 -0
  69. package/dist/commands/miscutils.js +233 -0
  70. package/dist/commands/mkdir.js +3 -2
  71. package/dist/commands/mv.js +4 -3
  72. package/dist/commands/netcat.d.ts +7 -0
  73. package/dist/commands/netcat.js +64 -0
  74. package/dist/commands/nice.d.ts +7 -0
  75. package/dist/commands/nice.js +22 -0
  76. package/dist/commands/nohup.d.ts +7 -0
  77. package/dist/commands/nohup.js +18 -0
  78. package/dist/commands/ping.d.ts +2 -1
  79. package/dist/commands/ping.js +46 -8
  80. package/dist/commands/procUtils.d.ts +13 -0
  81. package/dist/commands/procUtils.js +72 -0
  82. package/dist/commands/pwd.d.ts +5 -0
  83. package/dist/commands/pwd.js +5 -0
  84. package/dist/commands/python.js +0 -4
  85. package/dist/commands/read.js +0 -1
  86. package/dist/commands/registry.d.ts +37 -0
  87. package/dist/commands/registry.js +73 -0
  88. package/dist/commands/rm.js +3 -2
  89. package/dist/commands/runtime.d.ts +47 -1
  90. package/dist/commands/runtime.js +60 -5
  91. package/dist/commands/sh.d.ts +5 -0
  92. package/dist/commands/sh.js +5 -0
  93. package/dist/commands/stat.js +3 -2
  94. package/dist/commands/strace.js +0 -1
  95. package/dist/commands/sysinfo.d.ts +19 -0
  96. package/dist/commands/sysinfo.js +73 -0
  97. package/dist/commands/test.d.ts +5 -0
  98. package/dist/commands/test.js +5 -0
  99. package/dist/commands/textutils.d.ts +25 -0
  100. package/dist/commands/textutils.js +171 -0
  101. package/dist/commands/top.d.ts +7 -0
  102. package/dist/commands/top.js +54 -0
  103. package/dist/commands/touch.js +6 -2
  104. package/dist/commands/tr.d.ts +5 -0
  105. package/dist/commands/tr.js +5 -0
  106. package/dist/commands/w.js +1 -1
  107. package/dist/commands/which.d.ts +5 -0
  108. package/dist/commands/which.js +5 -0
  109. package/dist/index.d.ts +10 -2
  110. package/dist/index.js +4 -0
  111. package/dist/modules/VirtualNetworkManager.d.ts +54 -0
  112. package/dist/modules/VirtualNetworkManager.js +144 -0
  113. package/dist/modules/linuxRootfs.d.ts +4 -3
  114. package/dist/modules/linuxRootfs.js +115 -74
  115. package/dist/modules/neofetch.d.ts +2 -0
  116. package/dist/modules/neofetch.js +3 -2
  117. package/dist/modules/pacmanGame.d.ts +2 -0
  118. package/dist/modules/pacmanGame.js +1 -0
  119. package/dist/modules/shellInteractive.d.ts +2 -0
  120. package/dist/modules/shellInteractive.js +2 -0
  121. package/dist/modules/shellRuntime.d.ts +7 -0
  122. package/dist/modules/shellRuntime.js +6 -0
  123. package/dist/modules/webTermRenderer.js +0 -7
  124. package/dist/types/commands.d.ts +1 -1
  125. package/dist/types/vfs.d.ts +8 -0
  126. package/dist/utils/argv.d.ts +22 -3
  127. package/dist/utils/argv.js +22 -3
  128. package/dist/utils/perfLogger.d.ts +10 -2
  129. package/dist/utils/perfLogger.js +7 -14
  130. package/dist/utils/shellSession.d.ts +35 -0
  131. package/dist/utils/shellSession.js +35 -0
  132. package/package.json +1 -1
@@ -1,5 +1,15 @@
1
1
  import * as path from "node:path";
2
2
  const PROTECTED_PREFIXES = ["/.virtual-env-js/.auth", "/etc/htpasswd"];
3
+ /**
4
+ * Resolves a path string against the virtual file system.
5
+ * Supports `~` as shorthand for the home directory. If `inputPath` is
6
+ * absolute it is returned as-is; otherwise it is joined to `cwd`.
7
+ *
8
+ * @param cwd - The current working directory
9
+ * @param inputPath - The path string to resolve
10
+ * @param homeDir - The home directory to use for `~` expansion (defaults to `/root`)
11
+ * @returns The normalized absolute path
12
+ */
3
13
  export function resolvePath(cwd, inputPath, homeDir) {
4
14
  if (!inputPath || inputPath.trim() === "") {
5
15
  return cwd;
@@ -26,6 +36,14 @@ export function assertPathAccess(authUser, targetPath, operation) {
26
36
  throw new Error(`${operation}: permission denied: ${targetPath}`);
27
37
  }
28
38
  }
39
+ /**
40
+ * Strips the filename from a URL path, extracting the last path segment.
41
+ * Removes query strings and fragments first. Returns `"index.html"` when
42
+ * no segment is found.
43
+ *
44
+ * @param url - The URL or path string to process
45
+ * @returns The extracted filename, or "index.html" as a fallback
46
+ */
29
47
  export function stripUrlFilename(url) {
30
48
  const cleaned = url.split("?")[0]?.split("#")[0] ?? url;
31
49
  const lastPart = cleaned.split("/").filter(Boolean).pop();
@@ -47,6 +65,16 @@ function levenshtein(a, b) {
47
65
  }
48
66
  return dp[a.length][b.length];
49
67
  }
68
+ /**
69
+ * Resolves a path with readable error messages by attempting an exact match
70
+ * first, then falling back to case-insensitive matching, and finally to a
71
+ * Levenshtein-distance (≤1) fuzzy match against sibling entries.
72
+ *
73
+ * @param vfs - The virtual file system instance
74
+ * @param cwd - The current working directory
75
+ * @param inputPath - The path string to resolve
76
+ * @returns The best matching path (exact, case-insensitive, or fuzzy)
77
+ */
50
78
  export function resolveReadablePath(vfs, cwd, inputPath) {
51
79
  const exactPath = resolvePath(cwd, inputPath);
52
80
  if (vfs.exists(exactPath)) {
@@ -65,6 +93,39 @@ export function resolveReadablePath(vfs, cwd, inputPath) {
65
93
  }
66
94
  return exactPath;
67
95
  }
96
+ /**
97
+ * Returns the active package manager associated with the given shell.
98
+ *
99
+ * @param shell - The VirtualShell instance
100
+ * @returns The VirtualPackageManager, or undefined if none is configured
101
+ */
68
102
  export function getPackageManager(shell) {
69
103
  return shell.packageManager;
70
104
  }
105
+ /**
106
+ * POSIX-style permission check: does `authUser` have `want` access to `filePath`?
107
+ * `want` is a bitmask: 4=R_OK, 2=W_OK, 1=X_OK, 0=F_OK (check existence).
108
+ * Root bypasses all checks. Throws on EACCES.
109
+ */
110
+ export function checkFilePermission(vfs, users, authUser, filePath, want) {
111
+ if (authUser === "root")
112
+ return;
113
+ if (want === 0)
114
+ return; // F_OK — just existence
115
+ assertPathAccess(authUser, filePath, "access");
116
+ const uid = users.getUid(authUser);
117
+ const gid = users.getGid(authUser);
118
+ if (!vfs.checkAccess(filePath, uid, gid, want)) {
119
+ const mode = vfs.stat(filePath).mode;
120
+ const permStr = (mode & 0o400 ? "r" : "-") +
121
+ (mode & 0o200 ? "w" : "-") +
122
+ (mode & 0o100 ? "x" : "-") +
123
+ (mode & 0o040 ? "r" : "-") +
124
+ (mode & 0o020 ? "w" : "-") +
125
+ (mode & 0o010 ? "x" : "-") +
126
+ (mode & 0o004 ? "r" : "-") +
127
+ (mode & 0o002 ? "w" : "-") +
128
+ (mode & 0o001 ? "x" : "-");
129
+ throw new Error(`access: permission denied (mode=${permStr})`);
130
+ }
131
+ }
@@ -1,2 +1,7 @@
1
1
  import type { ShellModule } from "../types/commands";
2
+ /**
3
+ * Print user identity.
4
+ * @category system
5
+ * @params ["[user]"]
6
+ */
2
7
  export declare const idCommand: ShellModule;
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Print user identity.
3
+ * @category system
4
+ * @params ["[user]"]
5
+ */
1
6
  export const idCommand = {
2
7
  name: "id",
3
8
  description: "Print user identity",
@@ -1,6 +1,7 @@
1
1
  import type { ShellModule } from "../types/commands";
2
2
  /**
3
3
  * Show/manipulate routing, network devices, interfaces and tunnels.
4
+ * Uses the shell's VirtualNetworkManager for dynamic output.
4
5
  * @category network
5
6
  * @params ["<object> <command>"]
6
7
  */
@@ -1,23 +1,6 @@
1
- const ADDR_OUTPUT = `1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
2
- link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3
- inet 127.0.0.1/8 scope host lo
4
- valid_lft forever preferred_lft forever
5
- inet6 ::1/128 scope host
6
- valid_lft forever preferred_lft forever
7
- 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
8
- link/ether 02:42:0a:00:00:02 brd ff:ff:ff:ff:ff:ff
9
- inet 10.0.0.2/24 brd 10.0.0.255 scope global eth0
10
- valid_lft forever preferred_lft forever
11
- inet6 fe80::42:aff:fe00:2/64 scope link
12
- valid_lft forever preferred_lft forever`;
13
- const ROUTE_OUTPUT = `default via 10.0.0.1 dev eth0
14
- 10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.2`;
15
- const LINK_OUTPUT = `1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
16
- link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
17
- 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
18
- link/ether 02:42:0a:00:00:02 brd ff:ff:ff:ff:ff:ff`;
19
1
  /**
20
2
  * Show/manipulate routing, network devices, interfaces and tunnels.
3
+ * Uses the shell's VirtualNetworkManager for dynamic output.
21
4
  * @category network
22
5
  * @params ["<object> <command>"]
23
6
  */
@@ -26,23 +9,67 @@ export const ipCommand = {
26
9
  description: "Show/manipulate routing, network devices, interfaces",
27
10
  category: "network",
28
11
  params: ["<object> <command>"],
29
- run: ({ args }) => {
12
+ run: ({ args, shell }) => {
13
+ const net = shell.network;
30
14
  const obj = args[0]?.toLowerCase();
31
15
  const cmd = args[1]?.toLowerCase() ?? "show";
32
16
  if (!obj) {
33
17
  return { stderr: "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\nOBJECT := { link | addr | route | neigh }", exitCode: 1 };
34
18
  }
35
19
  if (obj === "addr" || obj === "address" || obj === "a") {
36
- return { stdout: ADDR_OUTPUT, exitCode: 0 };
20
+ if (cmd === "add") {
21
+ // ip addr add <ip>/<mask> dev <dev>
22
+ const ipArg = args.find((a) => a.includes("/"));
23
+ const devIdx = args.indexOf("dev");
24
+ const dev = devIdx !== -1 && devIdx + 1 < args.length ? args[devIdx + 1] : undefined;
25
+ if (ipArg && dev) {
26
+ const [ip, maskStr] = ipArg.split("/");
27
+ const mask = parseInt(maskStr ?? "24", 10);
28
+ net.setInterfaceIp(dev, ip ?? "0.0.0.0", mask);
29
+ }
30
+ return { exitCode: 0 };
31
+ }
32
+ if (cmd === "del") {
33
+ const devIdx = args.indexOf("dev");
34
+ const dev = devIdx !== -1 && devIdx + 1 < args.length ? args[devIdx + 1] : undefined;
35
+ if (dev)
36
+ net.setInterfaceIp(dev, "0.0.0.0", 0);
37
+ return { exitCode: 0 };
38
+ }
39
+ return { stdout: `${net.formatIpAddr()}\n`, exitCode: 0 };
37
40
  }
38
41
  if (obj === "route" || obj === "r" || obj === "ro") {
39
- return { stdout: ROUTE_OUTPUT, exitCode: 0 };
42
+ if (cmd === "add") {
43
+ const viaIdx = args.indexOf("via");
44
+ const devIdx = args.indexOf("dev");
45
+ const dest = args[1] !== "add" ? args[1] : args[2]; // ip route add <dest> ...
46
+ const gateway = viaIdx !== -1 ? args[viaIdx + 1] : "0.0.0.0";
47
+ const device = devIdx !== -1 ? args[devIdx + 1] : "eth0";
48
+ if (dest && dest !== "add")
49
+ net.addRoute(dest, gateway ?? "0.0.0.0", "255.255.255.0", device ?? "eth0");
50
+ return { exitCode: 0 };
51
+ }
52
+ if (cmd === "del") {
53
+ const dest = args[1] !== "del" ? args[1] : args[2];
54
+ if (dest && dest !== "del")
55
+ net.delRoute(dest);
56
+ return { exitCode: 0 };
57
+ }
58
+ return { stdout: `${net.formatIpRoute()}\n`, exitCode: 0 };
40
59
  }
41
60
  if (obj === "link" || obj === "l") {
42
- return { stdout: LINK_OUTPUT, exitCode: 0 };
61
+ if (cmd === "set") {
62
+ const dev = args[2]; // ip link set <dev> ...
63
+ if (args.includes("up") && dev)
64
+ net.setInterfaceState(dev, "UP");
65
+ if (args.includes("down") && dev)
66
+ net.setInterfaceState(dev, "DOWN");
67
+ return { exitCode: 0 };
68
+ }
69
+ return { stdout: `${net.formatIpLink()}\n`, exitCode: 0 };
43
70
  }
44
71
  if (obj === "neigh" || obj === "n") {
45
- return { stdout: "10.0.0.1 dev eth0 lladdr 02:42:0a:00:00:01 REACHABLE", exitCode: 0 };
72
+ return { stdout: `${net.formatIpNeigh()}\n`, exitCode: 0 };
46
73
  }
47
74
  if (["set", "add", "del", "flush", "change", "replace"].includes(cmd)) {
48
75
  return { exitCode: 0 }; // silently succeed mutations
@@ -1,27 +1,61 @@
1
+ function parseJobspec(arg, procs) {
2
+ if (!arg) {
3
+ // Default to most recent background job
4
+ return procs.filter((p) => p.status !== "stopped").pop();
5
+ }
6
+ const pid = parseInt(arg.replace(/^%/, ""), 10);
7
+ return procs.find((p) => p.pid === pid);
8
+ }
1
9
  export const jobsCommand = {
2
10
  name: "jobs",
3
11
  description: "List active jobs",
4
12
  category: "shell",
5
13
  params: [],
6
- run: () => ({ stdout: "", exitCode: 0 }),
14
+ run: ({ shell }) => {
15
+ const procs = shell.users.listProcesses();
16
+ if (procs.length === 0)
17
+ return { stdout: "", exitCode: 0 };
18
+ const lines = procs.map((p, i) => {
19
+ const label = `[${i + 1}]`;
20
+ const status = p.status === "running" ? "running" : p.status === "done" ? "done" : "stopped";
21
+ return `${label} ${String(p.pid).padStart(5)} ${status.padEnd(8)} ${p.argv.join(" ")}`;
22
+ });
23
+ return { stdout: `${lines.join("\n")}\n`, exitCode: 0 };
24
+ },
7
25
  };
8
26
  export const bgCommand = {
9
27
  name: "bg",
10
28
  description: "Resume a suspended job in the background",
11
29
  category: "shell",
12
30
  params: ["[%jobspec]"],
13
- run: ({ args }) => ({
14
- stderr: `bg: ${args[0] ?? "%1"}: no such job`,
15
- exitCode: 1,
16
- }),
31
+ run: ({ args, shell }) => {
32
+ const procs = shell.users.listProcesses();
33
+ const proc = parseJobspec(args[0], procs);
34
+ if (!proc) {
35
+ return { stderr: `bg: ${args[0] ?? "%1"}: no such job`, exitCode: 1 };
36
+ }
37
+ if (proc.status === "done") {
38
+ return { stderr: `bg: ${args[0]}: job has finished`, exitCode: 1 };
39
+ }
40
+ proc.status = "running";
41
+ return { stdout: `[${procs.indexOf(proc) + 1}] ${proc.pid} ${proc.argv.join(" ")} &\n`, exitCode: 0 };
42
+ },
17
43
  };
18
44
  export const fgCommand = {
19
45
  name: "fg",
20
46
  description: "Resume a suspended job in the foreground",
21
47
  category: "shell",
22
48
  params: ["[%jobspec]"],
23
- run: ({ args }) => ({
24
- stderr: `fg: ${args[0] ?? "%1"}: no such job`,
25
- exitCode: 1,
26
- }),
49
+ run: ({ args, shell }) => {
50
+ const procs = shell.users.listProcesses();
51
+ const proc = parseJobspec(args[0], procs);
52
+ if (!proc) {
53
+ return { stderr: `fg: ${args[0] ?? "%1"}: no such job`, exitCode: 1 };
54
+ }
55
+ if (proc.status === "done") {
56
+ return { stderr: `fg: ${args[0]}: job has finished`, exitCode: 1 };
57
+ }
58
+ proc.status = "running";
59
+ return { stdout: `${proc.argv.join(" ")}\n`, exitCode: 0 };
60
+ },
27
61
  };
@@ -1,6 +1,7 @@
1
1
  import type { ShellModule } from "../types/commands";
2
2
  /**
3
3
  * Send a signal to a process by PID.
4
+ * Supports SIGTERM (default) and SIGKILL (-9). Aborts background processes.
4
5
  * @category system
5
6
  * @params ["[-9] <pid>"]
6
7
  */
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * Send a signal to a process by PID.
3
+ * Supports SIGTERM (default) and SIGKILL (-9). Aborts background processes.
3
4
  * @category system
4
5
  * @params ["[-9] <pid>"]
5
6
  */
@@ -8,11 +9,18 @@ export const killCommand = {
8
9
  description: "Send signal to process",
9
10
  category: "system",
10
11
  params: ["[-9] <pid>"],
11
- run: ({ args }) => {
12
- const pid = args.find((a) => !a.startsWith("-"));
13
- if (!pid)
12
+ run: ({ args, shell }) => {
13
+ const pidStr = args.find((a) => !a.startsWith("-"));
14
+ if (!pidStr)
14
15
  return { stderr: "kill: no pid specified", exitCode: 1 };
15
- // In virtual env, we just acknowledge the kill
16
- return { stdout: ``, exitCode: 0 };
16
+ const pid = parseInt(pidStr, 10);
17
+ if (Number.isNaN(pid)) {
18
+ return { stderr: `kill: invalid pid: ${pidStr}`, exitCode: 1 };
19
+ }
20
+ const found = shell.users.killProcess(pid);
21
+ if (!found) {
22
+ return { stderr: `kill: (${pid}) - No such process`, exitCode: 1 };
23
+ }
24
+ return { stdout: "", exitCode: 0 };
17
25
  },
18
26
  };
@@ -22,7 +22,7 @@ export const lastCommand = {
22
22
  }).replace(",", "")}`;
23
23
  lines.push(`${target.padEnd(10)} pts/0 ${(log.from ?? "browser").padEnd(16)} ${ds} still logged in`);
24
24
  }
25
- catch { }
25
+ catch { /* log entry may be corrupt */ }
26
26
  }
27
27
  lines.push("");
28
28
  lines.push(`wtmp begins ${new Date().toDateString()}`);
@@ -1,4 +1,9 @@
1
1
  import type { ShellModule } from "../types/commands";
2
+ /**
3
+ * Create links.
4
+ * @category files
5
+ * @params ["[-s] <target> <link_name>"]
6
+ */
2
7
  export declare const lnCommand: ShellModule;
3
8
  /** Shell command: print the value of a symbolic link. */
4
9
  export declare const readlinkCommand: ShellModule;
@@ -1,5 +1,10 @@
1
1
  import { ifFlag } from "./command-helpers";
2
2
  import { assertPathAccess, resolvePath } from "./helpers";
3
+ /**
4
+ * Create links.
5
+ * @category files
6
+ * @params ["[-s] <target> <link_name>"]
7
+ */
3
8
  export const lnCommand = {
4
9
  name: "ln",
5
10
  description: "Create links",
@@ -1,2 +1,7 @@
1
1
  import type { ShellModule } from "../types/commands";
2
+ /**
3
+ * List directory contents.
4
+ * @category navigation
5
+ * @params ["[-la] [path]"]
6
+ */
2
7
  export declare const lsCommand: ShellModule;
@@ -145,13 +145,26 @@ function longListing(vfs, dir, items) {
145
145
  });
146
146
  const wNlink = Math.max(...rows.map((r) => r.nlink.length));
147
147
  const wSize = Math.max(...rows.map((r) => r.size.length));
148
- const owner = "root";
149
- const group = "root";
150
148
  const total = items.length * 8;
151
- const lines = rows.map((r) => `${r.perms} ${r.nlink.padStart(wNlink)} ${owner} ${group} ${r.size.padStart(wSize)} ${r.date} ${r.label}`);
149
+ const lines = rows.map((r, i) => {
150
+ const st = (() => { try {
151
+ return vfs.stat(`${base}/${items[i]}`);
152
+ }
153
+ catch {
154
+ return null;
155
+ } })();
156
+ const uidStr = st && "uid" in st ? String(st.uid) : "0";
157
+ const gidStr = st && "gid" in st ? String(st.gid) : "0";
158
+ return `${r.perms} ${r.nlink.padStart(wNlink)} ${uidStr} ${gidStr} ${r.size.padStart(wSize)} ${r.date} ${r.label}`;
159
+ });
152
160
  return `total ${total}\n${lines.join("\n")}`;
153
161
  }
154
162
  // ─── command ─────────────────────────────────────────────────────────────────
163
+ /**
164
+ * List directory contents.
165
+ * @category navigation
166
+ * @params ["[-la] [path]"]
167
+ */
155
168
  export const lsCommand = {
156
169
  name: "ls",
157
170
  description: "List directory contents",
@@ -181,8 +194,10 @@ export const lsCommand = {
181
194
  const label = isLink
182
195
  ? `${colorize(name, color)} -> ${readlinkTarget(shell.vfs, target)}`
183
196
  : colorize(name, color);
197
+ const uidStr = "uid" in st ? String(st.uid) : "0";
198
+ const gidStr = "gid" in st ? String(st.gid) : "0";
184
199
  return {
185
- stdout: `${perms} 1 root root ${size} ${formatDate(st.updatedAt)} ${label}\n`,
200
+ stdout: `${perms} 1 ${uidStr} ${gidStr} ${size} ${formatDate(st.updatedAt)} ${label}\n`,
186
201
  exitCode: 0,
187
202
  };
188
203
  }
@@ -30,7 +30,7 @@ export const lsbReleaseCommand = {
30
30
  .trim();
31
31
  }
32
32
  }
33
- catch { }
33
+ catch { /* /etc/os-release may not exist */ }
34
34
  const all = ifFlag(args, ["-a", "--all"]);
35
35
  const showId = ifFlag(args, ["-i", "--id"]);
36
36
  const showDesc = ifFlag(args, ["-d", "--description"]);
@@ -1,2 +1,7 @@
1
1
  import type { ShellModule } from "../types/commands";
2
+ /**
3
+ * Interface to the system reference manuals.
4
+ * @category shell
5
+ * @params ["<command>"]
6
+ */
2
7
  export declare const manCommand: ShellModule;
@@ -2,6 +2,11 @@ import { MANUALS } from "./manuals-bundle";
2
2
  const MANUAL_ALIASES = {
3
3
  gunzip: "gzip",
4
4
  };
5
+ /**
6
+ * Interface to the system reference manuals.
7
+ * @category shell
8
+ * @params ["<command>"]
9
+ */
5
10
  export const manCommand = {
6
11
  name: "man",
7
12
  description: "Interface to the system reference manuals",