typescript-virtual-container 1.6.0 → 1.6.2
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.
- package/README.md +21 -21
- package/dist/.tsbuildinfo +1 -1
- package/dist/VirtualShell/index.d.ts +3 -0
- package/dist/VirtualShell/index.js +2 -0
- package/dist/commands/dpkg.js +1 -1
- package/dist/commands/expr.js +1 -1
- package/dist/commands/last.js +1 -1
- package/dist/commands/manuals-bundle.js +1 -1
- package/dist/commands/miscutils.js +3 -3
- package/dist/commands/mousepad.d.ts +2 -0
- package/dist/commands/mousepad.js +21 -0
- package/dist/commands/netcat.js +1 -1
- package/dist/commands/procUtils.js +1 -1
- package/dist/commands/registry.js +7 -0
- package/dist/commands/startxfce4.d.ts +2 -0
- package/dist/commands/startxfce4.js +13 -0
- package/dist/commands/sysinfo.js +3 -3
- package/dist/commands/textutils.js +2 -2
- package/dist/commands/top.js +1 -1
- package/dist/commands/uname.js +1 -1
- package/dist/commands/xfceDesktop.d.ts +2 -0
- package/dist/commands/xfceDesktop.js +13 -0
- package/dist/modules/VirtualNetworkManager.d.ts +74 -5
- package/dist/modules/VirtualNetworkManager.js +63 -5
- package/dist/modules/desktopManager.d.ts +104 -0
- package/dist/modules/desktopManager.js +927 -0
- package/dist/modules/linuxRootfs.js +125 -19
- package/package.json +1 -1
package/dist/commands/last.js
CHANGED
|
@@ -43,7 +43,7 @@ export const dmesgCommand = {
|
|
|
43
43
|
const n = args.includes("-n") ? parseInt(args[args.indexOf("-n") + 1] ?? "20", 10) : 20;
|
|
44
44
|
const msgs = [
|
|
45
45
|
"[ 0.000000] Booting Linux on physical CPU 0x0",
|
|
46
|
-
"[ 0.000000] Linux version 6.1.0-fortune (gcc
|
|
46
|
+
"[ 0.000000] Linux version 6.1.0-fortune (gcc (Fortune 13.3.0-nyx1) 13.3.0)",
|
|
47
47
|
"[ 0.000000] Command line: BOOT_IMAGE=/vmlinuz-6.1.0 root=/dev/sda1 ro quiet",
|
|
48
48
|
"[ 0.000000] BIOS-provided physical RAM map:",
|
|
49
49
|
"[ 0.000000] ACPI: IRQ0 used by override.",
|
|
@@ -23,7 +23,7 @@ export const realpathCommand = {
|
|
|
23
23
|
const resolved = shell.vfs.isSymlink(p)
|
|
24
24
|
? shell.vfs.resolveSymlink(p)
|
|
25
25
|
: p;
|
|
26
|
-
return { stdout: path.posix.normalize(resolved)
|
|
26
|
+
return { stdout: `${path.posix.normalize(resolved)}\n`, exitCode: 0 };
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
29
|
/**
|
|
@@ -106,7 +106,7 @@ export const stringsCommand = {
|
|
|
106
106
|
}
|
|
107
107
|
if (current.length >= 4)
|
|
108
108
|
results.push(current);
|
|
109
|
-
return { stdout: results.join("\n")
|
|
109
|
+
return { stdout: `${results.join("\n")}\n`, exitCode: 0 };
|
|
110
110
|
},
|
|
111
111
|
};
|
|
112
112
|
/**
|
|
@@ -228,6 +228,6 @@ export const fmtCommand = {
|
|
|
228
228
|
}
|
|
229
229
|
if (current)
|
|
230
230
|
lines.push(current);
|
|
231
|
-
return { stdout: lines.join("\n")
|
|
231
|
+
return { stdout: `${lines.join("\n")}\n`, exitCode: 0 };
|
|
232
232
|
},
|
|
233
233
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const mousepadCommand = {
|
|
2
|
+
name: "mousepad",
|
|
3
|
+
aliases: ["gedit", "xed"],
|
|
4
|
+
params: ["[file]"],
|
|
5
|
+
description: "Open a text file in the desktop text editor",
|
|
6
|
+
category: "desktop",
|
|
7
|
+
run(ctx) {
|
|
8
|
+
const dm = ctx.shell.desktopManager;
|
|
9
|
+
if (!dm) {
|
|
10
|
+
return { stderr: "mousepad: desktop is only available in the browser", exitCode: 1 };
|
|
11
|
+
}
|
|
12
|
+
if (!dm.isActive()) {
|
|
13
|
+
return { stderr: "mousepad: no desktop session running (start with startxfce4)", exitCode: 1 };
|
|
14
|
+
}
|
|
15
|
+
const path = ctx.args[0]
|
|
16
|
+
? ctx.args[0].startsWith("/") ? ctx.args[0] : `${ctx.cwd}/${ctx.args[0]}`
|
|
17
|
+
: "/root/untitled.txt";
|
|
18
|
+
dm.createEditorWindow(path);
|
|
19
|
+
return { exitCode: 0 };
|
|
20
|
+
},
|
|
21
|
+
};
|
package/dist/commands/netcat.js
CHANGED
|
@@ -42,7 +42,7 @@ export const ncCommand = {
|
|
|
42
42
|
const nonFlag = args.filter((a) => !a.startsWith("-"));
|
|
43
43
|
const host = nonFlag[0];
|
|
44
44
|
const portNum = nonFlag[1] ? parseInt(nonFlag[1], 10) : NaN;
|
|
45
|
-
if (host && !isNaN(portNum)) {
|
|
45
|
+
if (host && !Number.isNaN(portNum)) {
|
|
46
46
|
return new Promise((resolve) => {
|
|
47
47
|
const socket = net.createConnection({ host, port: portNum }, () => {
|
|
48
48
|
if (verbose) {
|
|
@@ -27,7 +27,7 @@ export const pgrepCommand = {
|
|
|
27
27
|
}
|
|
28
28
|
if (results.length === 0)
|
|
29
29
|
return { exitCode: 1 };
|
|
30
|
-
return { stdout: results.join("\n")
|
|
30
|
+
return { stdout: `${results.join("\n")}\n`, exitCode: 0 };
|
|
31
31
|
}
|
|
32
32
|
catch {
|
|
33
33
|
return { stderr: "pgrep: invalid pattern\n", exitCode: 2 };
|
|
@@ -109,6 +109,9 @@ import { pgrepCommand, pkillCommand } from "./procUtils";
|
|
|
109
109
|
import { lscpuCommand, lsusbCommand, lspciCommand } from "./sysinfo";
|
|
110
110
|
import { joinCommand, commCommand, splitCommand, csplitCommand } from "./textutils";
|
|
111
111
|
import { topCommand } from "./top";
|
|
112
|
+
import { startxfce4Command } from "./startxfce4";
|
|
113
|
+
import { thunarCommand } from "./xfceDesktop";
|
|
114
|
+
import { mousepadCommand } from "./mousepad";
|
|
112
115
|
const BASE_COMMANDS = [
|
|
113
116
|
// Navigation
|
|
114
117
|
pwdCommand,
|
|
@@ -258,6 +261,10 @@ const BASE_COMMANDS = [
|
|
|
258
261
|
nodeCommand,
|
|
259
262
|
python3Command,
|
|
260
263
|
exprCommand,
|
|
264
|
+
// Desktop
|
|
265
|
+
startxfce4Command,
|
|
266
|
+
thunarCommand,
|
|
267
|
+
mousepadCommand,
|
|
261
268
|
// System (extended)
|
|
262
269
|
uptimeCommand,
|
|
263
270
|
freeCommand,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const startxfce4Command = {
|
|
2
|
+
name: "startxfce4",
|
|
3
|
+
aliases: ["xfce4-session"],
|
|
4
|
+
params: [],
|
|
5
|
+
async run(ctx) {
|
|
6
|
+
const dm = ctx.shell.desktopManager;
|
|
7
|
+
if (!dm) {
|
|
8
|
+
return { stderr: "startxfce4: desktop is only available in the browser", exitCode: 1 };
|
|
9
|
+
}
|
|
10
|
+
await dm.start();
|
|
11
|
+
return { exitCode: 0 };
|
|
12
|
+
},
|
|
13
|
+
};
|
package/dist/commands/sysinfo.js
CHANGED
|
@@ -27,7 +27,7 @@ export const lscpuCommand = {
|
|
|
27
27
|
`Socket(s): 1`,
|
|
28
28
|
`Vendor ID: GenuineIntel`,
|
|
29
29
|
];
|
|
30
|
-
return { stdout: lines.join("\n")
|
|
30
|
+
return { stdout: `${lines.join("\n")}\n`, exitCode: 0 };
|
|
31
31
|
},
|
|
32
32
|
};
|
|
33
33
|
/**
|
|
@@ -46,7 +46,7 @@ export const lsusbCommand = {
|
|
|
46
46
|
"Bus 001 Device 002: ID 80ee:0021 VirtualBox USB Tablet",
|
|
47
47
|
"Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub",
|
|
48
48
|
];
|
|
49
|
-
return { stdout: devices.join("\n")
|
|
49
|
+
return { stdout: `${devices.join("\n")}\n`, exitCode: 0 };
|
|
50
50
|
},
|
|
51
51
|
};
|
|
52
52
|
/**
|
|
@@ -68,6 +68,6 @@ export const lspciCommand = {
|
|
|
68
68
|
"00:03.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller",
|
|
69
69
|
"00:04.0 SATA controller: Intel Corporation 82801IR/IO (ICH9R) SATA Controller",
|
|
70
70
|
];
|
|
71
|
-
return { stdout: devices.join("\n")
|
|
71
|
+
return { stdout: `${devices.join("\n")}\n`, exitCode: 0 };
|
|
72
72
|
},
|
|
73
73
|
};
|
|
@@ -49,7 +49,7 @@ export const joinCommand = {
|
|
|
49
49
|
results.push(`${match} ${line}`);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
return { stdout: results.join("\n")
|
|
52
|
+
return { stdout: `${results.join("\n")}\n`, exitCode: 0 };
|
|
53
53
|
},
|
|
54
54
|
};
|
|
55
55
|
/**
|
|
@@ -105,7 +105,7 @@ export const commCommand = {
|
|
|
105
105
|
const col3 = i < both.length ? both[i] : "";
|
|
106
106
|
results.push(`${col1}\t${col2}\t${col3}`);
|
|
107
107
|
}
|
|
108
|
-
return { stdout: results.join("\n")
|
|
108
|
+
return { stdout: `${results.join("\n")}\n`, exitCode: 0 };
|
|
109
109
|
},
|
|
110
110
|
};
|
|
111
111
|
/**
|
package/dist/commands/top.js
CHANGED
|
@@ -49,6 +49,6 @@ export const topCommand = {
|
|
|
49
49
|
const status = p.status === "running" ? "R" : "S";
|
|
50
50
|
lines.push(`${String(p.pid).padStart(5)} ${p.username.padEnd(8).slice(0, 8)} 20 0 ${String(virt).padStart(7)} ${String(res).padStart(6)} ${String(shr).padStart(6)} ${status} ${cpu.padStart(4)} ${mem.padStart(5)} 0:00.00 ${p.command}`);
|
|
51
51
|
});
|
|
52
|
-
return { stdout: lines.join("\n")
|
|
52
|
+
return { stdout: `${lines.join("\n")}\n`, exitCode: 0 };
|
|
53
53
|
},
|
|
54
54
|
};
|
package/dist/commands/uname.js
CHANGED
|
@@ -12,7 +12,7 @@ export const unameCommand = {
|
|
|
12
12
|
run: ({ shell, args }) => {
|
|
13
13
|
const all = ifFlag(args, ["-a"]);
|
|
14
14
|
const sysname = "Linux";
|
|
15
|
-
const release = shell.properties?.kernel ?? "
|
|
15
|
+
const release = shell.properties?.kernel ?? "1.0.0+itsrealfortune+1-amd64";
|
|
16
16
|
const machine = shell.properties?.arch ?? "x86_64";
|
|
17
17
|
const hostname = shell.hostname;
|
|
18
18
|
if (all)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const thunarCommand = {
|
|
2
|
+
name: "thunar",
|
|
3
|
+
params: [],
|
|
4
|
+
run(ctx) {
|
|
5
|
+
const dm = ctx.shell.desktopManager;
|
|
6
|
+
if (!dm?.isActive()) {
|
|
7
|
+
return { stderr: "thunar: desktop is not running (start it with startxfce4)", exitCode: 1 };
|
|
8
|
+
}
|
|
9
|
+
const path = ctx.args[0] || ctx.env.vars.HOME || "/root";
|
|
10
|
+
dm.createThunarWindow(path);
|
|
11
|
+
return { exitCode: 0 };
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
* latency and packet loss. Used by the `ip`, `ping`, and `netstat` commands
|
|
6
6
|
* to produce dynamic, deterministic output instead of hardcoded strings.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* A virtual network interface, either loopback or ethernet,
|
|
10
|
+
* with MAC address, MTU, IPv4/IPv6 addresses, and link state.
|
|
11
|
+
*/
|
|
8
12
|
export interface VirtualInterface {
|
|
9
13
|
name: string;
|
|
10
14
|
type: "loopback" | "ether";
|
|
@@ -15,6 +19,10 @@ export interface VirtualInterface {
|
|
|
15
19
|
ipv4Mask: number;
|
|
16
20
|
ipv6: string;
|
|
17
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* A routing table entry mapping a destination subnet
|
|
24
|
+
* to a gateway and outbound device.
|
|
25
|
+
*/
|
|
18
26
|
export interface VirtualRoute {
|
|
19
27
|
destination: string;
|
|
20
28
|
gateway: string;
|
|
@@ -22,32 +30,93 @@ export interface VirtualRoute {
|
|
|
22
30
|
device: string;
|
|
23
31
|
flags: string;
|
|
24
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* An ARP cache entry mapping an IP address to a MAC address
|
|
35
|
+
* on a specific device, with neighbour reachability state.
|
|
36
|
+
*/
|
|
25
37
|
export interface VirtualArpEntry {
|
|
26
38
|
ip: string;
|
|
27
39
|
mac: string;
|
|
28
40
|
device: string;
|
|
29
41
|
state: "REACHABLE" | "STALE" | "PERMANENT";
|
|
30
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Virtual network stack with routing table, ARP cache, and interface management.
|
|
45
|
+
* Provides dynamic data for `ip`, `ping`, and `/proc/net/*`.
|
|
46
|
+
*/
|
|
31
47
|
export declare class VirtualNetworkManager {
|
|
32
48
|
private interfaces;
|
|
33
49
|
private routes;
|
|
34
50
|
private arpCache;
|
|
51
|
+
/**
|
|
52
|
+
* Returns a copy of all configured interfaces.
|
|
53
|
+
* @returns Array of VirtualInterface objects.
|
|
54
|
+
*/
|
|
35
55
|
getInterfaces(): VirtualInterface[];
|
|
56
|
+
/**
|
|
57
|
+
* Returns a copy of the routing table.
|
|
58
|
+
* @returns Array of VirtualRoute objects.
|
|
59
|
+
*/
|
|
36
60
|
getRoutes(): VirtualRoute[];
|
|
61
|
+
/**
|
|
62
|
+
* Returns a copy of the ARP cache.
|
|
63
|
+
* @returns Array of VirtualArpEntry objects.
|
|
64
|
+
*/
|
|
37
65
|
getArpCache(): VirtualArpEntry[];
|
|
66
|
+
/**
|
|
67
|
+
* Adds a new route to the routing table.
|
|
68
|
+
* @param dest Destination network or "default".
|
|
69
|
+
* @param gateway Gateway IP address.
|
|
70
|
+
* @param netmask Subnet mask (e.g. "255.255.255.0").
|
|
71
|
+
* @param device Outbound device name (e.g. "eth0").
|
|
72
|
+
*/
|
|
38
73
|
addRoute(dest: string, gateway: string, netmask: string, device: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Removes a route by destination network.
|
|
76
|
+
* @param dest Destination network to remove.
|
|
77
|
+
* @returns True if a route was removed, false if no match.
|
|
78
|
+
*/
|
|
39
79
|
delRoute(dest: string): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Sets the administrative state of an interface.
|
|
82
|
+
* @param name Interface name ("lo", "eth0", etc.).
|
|
83
|
+
* @param state Desired state: "UP" or "DOWN".
|
|
84
|
+
* @returns True if the interface was found and updated, false otherwise.
|
|
85
|
+
*/
|
|
40
86
|
setInterfaceState(name: string, state: "UP" | "DOWN"): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Sets the IPv4 address and prefix length on an interface.
|
|
89
|
+
* @param name Interface name.
|
|
90
|
+
* @param ipv4 New IPv4 address.
|
|
91
|
+
* @param mask New subnet mask prefix length (e.g. 24).
|
|
92
|
+
* @returns True if the interface was found and updated, false otherwise.
|
|
93
|
+
*/
|
|
41
94
|
setInterfaceIp(name: string, ipv4: string, mask: number): boolean;
|
|
42
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* Simulates an ICMP ping to the given host.
|
|
97
|
+
* @param host Target IP address or hostname.
|
|
98
|
+
* @returns Latency in milliseconds if reachable, -1 if unreachable.
|
|
99
|
+
*/
|
|
43
100
|
ping(host: string): number;
|
|
44
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* Formats all interfaces as `ip addr` output.
|
|
103
|
+
* @returns Formatted string mimicking `ip addr` command.
|
|
104
|
+
*/
|
|
45
105
|
formatIpAddr(): string;
|
|
46
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* Formats the routing table as `ip route` output.
|
|
108
|
+
* @returns Formatted string mimicking `ip route` command.
|
|
109
|
+
*/
|
|
47
110
|
formatIpRoute(): string;
|
|
48
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* Formats all interfaces as `ip link` output.
|
|
113
|
+
* @returns Formatted string mimicking `ip link` command.
|
|
114
|
+
*/
|
|
49
115
|
formatIpLink(): string;
|
|
50
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* Formats the ARP cache as `ip neigh` output.
|
|
118
|
+
* @returns Formatted string mimicking `ip neigh` command.
|
|
119
|
+
*/
|
|
51
120
|
formatIpNeigh(): string;
|
|
52
121
|
private _maskToCidr;
|
|
53
122
|
private _ipForDevice;
|
|
@@ -5,10 +5,15 @@
|
|
|
5
5
|
* latency and packet loss. Used by the `ip`, `ping`, and `netstat` commands
|
|
6
6
|
* to produce dynamic, deterministic output instead of hardcoded strings.
|
|
7
7
|
*/
|
|
8
|
+
/** Generates a random MAC address in the 02:42:xx:xx:xx:xx range. */
|
|
8
9
|
function randomMac() {
|
|
9
10
|
const hex = () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0");
|
|
10
11
|
return `02:42:${hex()}:${hex()}:${hex()}:${hex()}`;
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Virtual network stack with routing table, ARP cache, and interface management.
|
|
15
|
+
* Provides dynamic data for `ip`, `ping`, and `/proc/net/*`.
|
|
16
|
+
*/
|
|
12
17
|
export class VirtualNetworkManager {
|
|
13
18
|
interfaces = [
|
|
14
19
|
{
|
|
@@ -40,18 +45,42 @@ export class VirtualNetworkManager {
|
|
|
40
45
|
arpCache = [
|
|
41
46
|
{ ip: "10.0.0.1", mac: "02:42:0a:00:00:01", device: "eth0", state: "REACHABLE" },
|
|
42
47
|
];
|
|
48
|
+
/**
|
|
49
|
+
* Returns a copy of all configured interfaces.
|
|
50
|
+
* @returns Array of VirtualInterface objects.
|
|
51
|
+
*/
|
|
43
52
|
getInterfaces() {
|
|
44
53
|
return [...this.interfaces];
|
|
45
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Returns a copy of the routing table.
|
|
57
|
+
* @returns Array of VirtualRoute objects.
|
|
58
|
+
*/
|
|
46
59
|
getRoutes() {
|
|
47
60
|
return [...this.routes];
|
|
48
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns a copy of the ARP cache.
|
|
64
|
+
* @returns Array of VirtualArpEntry objects.
|
|
65
|
+
*/
|
|
49
66
|
getArpCache() {
|
|
50
67
|
return [...this.arpCache];
|
|
51
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Adds a new route to the routing table.
|
|
71
|
+
* @param dest Destination network or "default".
|
|
72
|
+
* @param gateway Gateway IP address.
|
|
73
|
+
* @param netmask Subnet mask (e.g. "255.255.255.0").
|
|
74
|
+
* @param device Outbound device name (e.g. "eth0").
|
|
75
|
+
*/
|
|
52
76
|
addRoute(dest, gateway, netmask, device) {
|
|
53
77
|
this.routes.push({ destination: dest, gateway, netmask, device, flags: "UG" });
|
|
54
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Removes a route by destination network.
|
|
81
|
+
* @param dest Destination network to remove.
|
|
82
|
+
* @returns True if a route was removed, false if no match.
|
|
83
|
+
*/
|
|
55
84
|
delRoute(dest) {
|
|
56
85
|
const idx = this.routes.findIndex((r) => r.destination === dest);
|
|
57
86
|
if (idx === -1)
|
|
@@ -59,6 +88,12 @@ export class VirtualNetworkManager {
|
|
|
59
88
|
this.routes.splice(idx, 1);
|
|
60
89
|
return true;
|
|
61
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Sets the administrative state of an interface.
|
|
93
|
+
* @param name Interface name ("lo", "eth0", etc.).
|
|
94
|
+
* @param state Desired state: "UP" or "DOWN".
|
|
95
|
+
* @returns True if the interface was found and updated, false otherwise.
|
|
96
|
+
*/
|
|
62
97
|
setInterfaceState(name, state) {
|
|
63
98
|
const iface = this.interfaces.find((i) => i.name === name);
|
|
64
99
|
if (!iface)
|
|
@@ -66,6 +101,13 @@ export class VirtualNetworkManager {
|
|
|
66
101
|
iface.state = state;
|
|
67
102
|
return true;
|
|
68
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Sets the IPv4 address and prefix length on an interface.
|
|
106
|
+
* @param name Interface name.
|
|
107
|
+
* @param ipv4 New IPv4 address.
|
|
108
|
+
* @param mask New subnet mask prefix length (e.g. 24).
|
|
109
|
+
* @returns True if the interface was found and updated, false otherwise.
|
|
110
|
+
*/
|
|
69
111
|
setInterfaceIp(name, ipv4, mask) {
|
|
70
112
|
const iface = this.interfaces.find((i) => i.name === name);
|
|
71
113
|
if (!iface)
|
|
@@ -74,7 +116,11 @@ export class VirtualNetworkManager {
|
|
|
74
116
|
iface.ipv4Mask = mask;
|
|
75
117
|
return true;
|
|
76
118
|
}
|
|
77
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* Simulates an ICMP ping to the given host.
|
|
121
|
+
* @param host Target IP address or hostname.
|
|
122
|
+
* @returns Latency in milliseconds if reachable, -1 if unreachable.
|
|
123
|
+
*/
|
|
78
124
|
ping(host) {
|
|
79
125
|
// Loopback always works
|
|
80
126
|
if (host === "127.0.0.1" || host === "localhost" || host === "::1") {
|
|
@@ -90,7 +136,10 @@ export class VirtualNetworkManager {
|
|
|
90
136
|
return -1;
|
|
91
137
|
return 0.8 + Math.random() * 5;
|
|
92
138
|
}
|
|
93
|
-
/**
|
|
139
|
+
/**
|
|
140
|
+
* Formats all interfaces as `ip addr` output.
|
|
141
|
+
* @returns Formatted string mimicking `ip addr` command.
|
|
142
|
+
*/
|
|
94
143
|
formatIpAddr() {
|
|
95
144
|
const lines = [];
|
|
96
145
|
let idx = 1;
|
|
@@ -108,7 +157,10 @@ export class VirtualNetworkManager {
|
|
|
108
157
|
}
|
|
109
158
|
return lines.join("\n");
|
|
110
159
|
}
|
|
111
|
-
/**
|
|
160
|
+
/**
|
|
161
|
+
* Formats the routing table as `ip route` output.
|
|
162
|
+
* @returns Formatted string mimicking `ip route` command.
|
|
163
|
+
*/
|
|
112
164
|
formatIpRoute() {
|
|
113
165
|
return this.routes.map((r) => {
|
|
114
166
|
if (r.destination === "default") {
|
|
@@ -117,7 +169,10 @@ export class VirtualNetworkManager {
|
|
|
117
169
|
return `${r.destination}/${this._maskToCidr(r.netmask)} dev ${r.device} proto kernel scope link src ${this._ipForDevice(r.device)}`;
|
|
118
170
|
}).join("\n");
|
|
119
171
|
}
|
|
120
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* Formats all interfaces as `ip link` output.
|
|
174
|
+
* @returns Formatted string mimicking `ip link` command.
|
|
175
|
+
*/
|
|
121
176
|
formatIpLink() {
|
|
122
177
|
const lines = [];
|
|
123
178
|
let idx = 1;
|
|
@@ -131,7 +186,10 @@ export class VirtualNetworkManager {
|
|
|
131
186
|
}
|
|
132
187
|
return lines.join("\n");
|
|
133
188
|
}
|
|
134
|
-
/**
|
|
189
|
+
/**
|
|
190
|
+
* Formats the ARP cache as `ip neigh` output.
|
|
191
|
+
* @returns Formatted string mimicking `ip neigh` command.
|
|
192
|
+
*/
|
|
135
193
|
formatIpNeigh() {
|
|
136
194
|
return this.arpCache.map((e) => `${e.ip} dev ${e.device} lladdr ${e.mac} ${e.state}`).join("\n");
|
|
137
195
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { VirtualShell } from "../VirtualShell";
|
|
2
|
+
import type { ShellStream } from "../types/streams";
|
|
3
|
+
import { WebTermRenderer } from "./webTermRenderer";
|
|
4
|
+
export interface TerminalContent {
|
|
5
|
+
type: "terminal";
|
|
6
|
+
termRenderer: WebTermRenderer;
|
|
7
|
+
dataListeners: Array<(chunk: Buffer) => void>;
|
|
8
|
+
preEl?: HTMLPreElement;
|
|
9
|
+
stream?: ShellStream;
|
|
10
|
+
}
|
|
11
|
+
export interface ThunarContent {
|
|
12
|
+
type: "thunar";
|
|
13
|
+
path: string;
|
|
14
|
+
}
|
|
15
|
+
export interface AboutContent {
|
|
16
|
+
type: "about";
|
|
17
|
+
}
|
|
18
|
+
export interface EditorContent {
|
|
19
|
+
type: "editor";
|
|
20
|
+
path: string;
|
|
21
|
+
dirty: boolean;
|
|
22
|
+
}
|
|
23
|
+
export type WindowContent = TerminalContent | ThunarContent | AboutContent | EditorContent;
|
|
24
|
+
export interface DesktopWindow {
|
|
25
|
+
id: string;
|
|
26
|
+
title: string;
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
minimized: boolean;
|
|
32
|
+
focused: boolean;
|
|
33
|
+
zIndex: number;
|
|
34
|
+
content: WindowContent;
|
|
35
|
+
}
|
|
36
|
+
export interface DesktopState {
|
|
37
|
+
active: boolean;
|
|
38
|
+
windows: DesktopWindow[];
|
|
39
|
+
menuOpen: boolean;
|
|
40
|
+
clock: string;
|
|
41
|
+
focusedWindowId: string | null;
|
|
42
|
+
}
|
|
43
|
+
export declare class DesktopManager {
|
|
44
|
+
private shell;
|
|
45
|
+
private container;
|
|
46
|
+
private active;
|
|
47
|
+
private windows;
|
|
48
|
+
private zCounter;
|
|
49
|
+
private menuOpen;
|
|
50
|
+
private nextWinId;
|
|
51
|
+
private clockInterval?;
|
|
52
|
+
private onExit;
|
|
53
|
+
private stopResolve;
|
|
54
|
+
private dragState;
|
|
55
|
+
private _renderGuard;
|
|
56
|
+
private readonly trashPath;
|
|
57
|
+
private docListeners;
|
|
58
|
+
private pendingTimeouts;
|
|
59
|
+
constructor(shell: VirtualShell, container: HTMLElement);
|
|
60
|
+
isActive(): boolean;
|
|
61
|
+
setOnExit(cb: () => void): void;
|
|
62
|
+
start(): Promise<void>;
|
|
63
|
+
stop(): void;
|
|
64
|
+
getFocusedTerminal(): {
|
|
65
|
+
stream: ShellStream;
|
|
66
|
+
dataListeners: Array<(chunk: Buffer) => void>;
|
|
67
|
+
preEl: HTMLPreElement;
|
|
68
|
+
} | null;
|
|
69
|
+
handleKeyDown(e: KeyboardEvent): void;
|
|
70
|
+
handlePaste(e: ClipboardEvent): void;
|
|
71
|
+
createTerminalWindow(): string;
|
|
72
|
+
createThunarWindow(path?: string): string;
|
|
73
|
+
createEditorWindow(path?: string): string;
|
|
74
|
+
createAboutWindow(): string;
|
|
75
|
+
closeWindow(id: string): void;
|
|
76
|
+
toggleMinimize(id: string): void;
|
|
77
|
+
focusWindow(id: string): void;
|
|
78
|
+
private createWindow;
|
|
79
|
+
private ensureWindowElement;
|
|
80
|
+
private renderWindowElement;
|
|
81
|
+
private addDocListener;
|
|
82
|
+
private removeAllDocListeners;
|
|
83
|
+
private setupEventDelegation;
|
|
84
|
+
private renderAll;
|
|
85
|
+
private renderPanel;
|
|
86
|
+
private renderDesktopIcons;
|
|
87
|
+
private renderWindows;
|
|
88
|
+
private renderWindowPositions;
|
|
89
|
+
private renderTerminalContentById;
|
|
90
|
+
private renderThunarContent;
|
|
91
|
+
private renderEditorContent;
|
|
92
|
+
private saveEditor;
|
|
93
|
+
private renderAboutContent;
|
|
94
|
+
private updateClock;
|
|
95
|
+
private showContextMenu;
|
|
96
|
+
private closeContextMenu;
|
|
97
|
+
private ensureTrashDir;
|
|
98
|
+
private refreshThunarWindow;
|
|
99
|
+
private moveToTrash;
|
|
100
|
+
private trashRestore;
|
|
101
|
+
private trashDelete;
|
|
102
|
+
private renamePrompt;
|
|
103
|
+
private escapeHtml;
|
|
104
|
+
}
|