feat(core): the command line with its local commands after Java, measured against Java
The terminal splits a line as Java's window does and runs ls, cd, pwd, mkdir, touch, cp, mv, rm, cat, test, help, ipconfig and route on the node's files, which are the project's own. Its texts are generated from the bundle Java loads. TerminalCases feeds Java's Terminal without a network (16 cases, de and en); eight cases name Java bugs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -118,6 +118,7 @@ import { EchoServer, SimpleClient } from './apps/echo.js';
|
||||
import { WebBrowser, WebServer, type WebServerPage } from './apps/web.js';
|
||||
import { EmailClient, type EmailClientFolders } from './apps/emailClient.js';
|
||||
import { EmailServer } from './apps/emailServer.js';
|
||||
import { Terminal } from './apps/terminal.js';
|
||||
import { firewallLogPage, forwardingTablePage, ripTablePage, routerIndexPage } from './apps/routerPages.js';
|
||||
import { applyFirewallSettings, firewallSettingsPage } from './apps/firewallSettings.js';
|
||||
import { VirtualFileSystem } from '../model/virtualFileSystem.js';
|
||||
@@ -915,6 +916,7 @@ export class NetworkRuntimeEngine {
|
||||
private readonly webBrowsersByNodeId = new Map<Id, WebBrowser>();
|
||||
private readonly emailServersByNodeId = new Map<Id, EmailServer>();
|
||||
private readonly emailClientsByNodeId = new Map<Id, EmailClient>();
|
||||
private readonly terminalsByNodeId = new Map<Id, Terminal>();
|
||||
/** The time of day a program writes into a mail (Java's new Date()). */
|
||||
private readonly wallClock: () => number;
|
||||
/** Each node's firewall log; it outlives a stop and a new start, as Java's log page does. */
|
||||
@@ -1046,6 +1048,7 @@ export class NetworkRuntimeEngine {
|
||||
this.webBrowsersByNodeId.clear();
|
||||
this.emailServersByNodeId.clear();
|
||||
this.emailClientsByNodeId.clear();
|
||||
this.terminalsByNodeId.clear();
|
||||
this.arpLookups = [];
|
||||
this.protocolThreads.clear();
|
||||
this.dnsQueriesBySocketId.clear();
|
||||
@@ -1136,6 +1139,11 @@ export class NetworkRuntimeEngine {
|
||||
return this.emailServersByNodeId.get(nodeId);
|
||||
}
|
||||
|
||||
/** The node's command line while the simulation runs, if it has one installed. */
|
||||
terminal(nodeId: Id): Terminal | undefined {
|
||||
return this.terminalsByNodeId.get(nodeId);
|
||||
}
|
||||
|
||||
/** The node's e-mail program while the simulation runs, if it has one installed. */
|
||||
emailClient(nodeId: Id): EmailClient | undefined {
|
||||
return this.emailClientsByNodeId.get(nodeId);
|
||||
@@ -1171,6 +1179,7 @@ export class NetworkRuntimeEngine {
|
||||
this.webBrowsersByNodeId.clear();
|
||||
this.emailServersByNodeId.clear();
|
||||
this.emailClientsByNodeId.clear();
|
||||
this.terminalsByNodeId.clear();
|
||||
for (const node of snapshot.nodes) {
|
||||
const echoServer = snapshot.echoServersByNodeId.get(node.id);
|
||||
if (echoServer !== undefined) this.echoServersByNodeId.set(node.id, new EchoServer(this, node.id, echoServer));
|
||||
@@ -1196,6 +1205,17 @@ export class NetworkRuntimeEngine {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (snapshot.terminalNodeIds.has(node.id)) {
|
||||
let files = snapshot.fileSystemsByNodeId.get(node.id);
|
||||
if (files === undefined) {
|
||||
// A node without files gets an empty file system, kept in the snapshot.
|
||||
files = new VirtualFileSystem();
|
||||
this.stateValue.topologySnapshot = withSnapshot(this.stateValue.topologySnapshot, {
|
||||
fileSystemsByNodeId: new Map([...this.stateValue.topologySnapshot.fileSystemsByNodeId, [node.id, files]]),
|
||||
});
|
||||
}
|
||||
this.terminalsByNodeId.set(node.id, new Terminal(this, node.id, files));
|
||||
}
|
||||
const emailClient = snapshot.emailClientsByNodeId.get(node.id);
|
||||
if (emailClient !== undefined) {
|
||||
const nodeId = node.id;
|
||||
|
||||
Reference in New Issue
Block a user