[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,415 @@
const test = require("node:test");
const assert = require("node:assert/strict");
const net = require("node:net");
const { setTimeout: delay } = require("node:timers/promises");
const terminalBridge = require("./terminalBridge.cjs");
const { FLOW_HIGH_WATER_MARK } = require("./terminalFlowAck.cjs");
const { IAC, WILL, WONT, DO, OPT } = require("./telnetProtocol.cjs");
function listen(server) {
return new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", () => {
server.off("error", reject);
resolve(server.address().port);
});
});
}
function waitFor(predicate, timeoutMs = 1000) {
const startedAt = Date.now();
return new Promise((resolve, reject) => {
const tick = () => {
if (predicate()) {
resolve();
return;
}
if (Date.now() - startedAt > timeoutMs) {
reject(new Error("Timed out waiting for telnet echo event"));
return;
}
setTimeout(tick, 10);
};
tick();
});
}
let nextSessionId = 0;
async function runEchoNegotiationTest({ auth = {}, command }) {
const sessionId = `telnet-echo-test-${nextSessionId++}`;
const sockets = new Set();
const server = net.createServer((socket) => {
sockets.add(socket);
socket.on("error", () => {});
socket.on("close", () => sockets.delete(socket));
setImmediate(() => {
socket.write(Buffer.from([IAC, command, OPT.ECHO]));
});
});
const port = await listen(server);
const sessions = new Map();
const sentEvents = [];
terminalBridge.init({
sessions,
electronModule: {
webContents: {
fromId: () => ({
send(channel, payload) {
sentEvents.push({ channel, payload });
},
}),
},
},
});
try {
await terminalBridge.startTelnetSession(
{ sender: { id: 1 } },
{
sessionId,
hostname: "127.0.0.1",
port,
...auth,
},
);
await waitFor(() => sentEvents.some((evt) => evt.channel === "netcatty:telnet:echo-mode"));
return {
sessionId,
payload: sentEvents.find((evt) => evt.channel === "netcatty:telnet:echo-mode").payload,
};
} finally {
terminalBridge.cleanupAllSessions();
for (const socket of sockets) socket.destroy();
await new Promise((resolve) => server.close(resolve));
}
}
test("Telnet WONT ECHO does not force local echo for no-auth sessions", async () => {
const { sessionId, payload } = await runEchoNegotiationTest({ command: WONT });
assert.deepEqual(payload, {
sessionId,
remoteEcho: false,
localEcho: false,
});
});
test("Telnet WILL ECHO disables local echo for no-auth sessions", async () => {
const { sessionId, payload } = await runEchoNegotiationTest({ command: WILL });
assert.deepEqual(payload, {
sessionId,
remoteEcho: true,
localEcho: false,
});
});
test("Telnet WONT ECHO does not force local echo for credential sessions", async () => {
const { sessionId, payload } = await runEchoNegotiationTest({
command: WONT,
auth: { username: "admin", password: "secret" },
});
assert.deepEqual(payload, {
sessionId,
remoteEcho: false,
localEcho: false,
});
});
test("Telnet DO ECHO enables client local echo", async () => {
const { sessionId, payload } = await runEchoNegotiationTest({ command: DO });
assert.deepEqual(payload, {
sessionId,
remoteEcho: true,
localEcho: true,
});
});
test("replaced Telnet sockets close without affecting the replacement session", async () => {
const sessionId = `telnet-echo-test-${nextSessionId++}`;
const sockets = new Set();
let oldSocket = null;
let newSocket = null;
let resolveOldClosed = () => {};
const oldClosed = new Promise((resolve) => {
resolveOldClosed = resolve;
});
const oldServer = net.createServer((socket) => {
oldSocket = socket;
sockets.add(socket);
socket.on("error", () => {});
socket.on("close", () => {
sockets.delete(socket);
resolveOldClosed();
});
});
const newServer = net.createServer((socket) => {
newSocket = socket;
sockets.add(socket);
socket.on("error", () => {});
socket.on("close", () => sockets.delete(socket));
});
const oldPort = await listen(oldServer);
const newPort = await listen(newServer);
const sessions = new Map();
const sentEvents = [];
terminalBridge.init({
sessions,
electronModule: {
webContents: {
fromId: () => ({
send(channel, payload) {
sentEvents.push({ channel, payload });
},
}),
},
},
});
try {
await terminalBridge.startTelnetSession(
{ sender: { id: 1 } },
{ sessionId, hostname: "127.0.0.1", port: oldPort },
);
await waitFor(() => oldSocket);
await terminalBridge.startTelnetSession(
{ sender: { id: 1 } },
{ sessionId, hostname: "127.0.0.1", port: newPort },
);
await waitFor(() => newSocket);
await oldClosed;
assert.equal(sessions.has(sessionId), true);
assert.equal(sentEvents.some((evt) => evt.channel === "netcatty:exit"), false);
assert.equal(sentEvents.some((evt) => evt.channel === "netcatty:telnet:echo-mode"), false);
newSocket.write(Buffer.from([IAC, WONT, OPT.ECHO]));
await waitFor(() => sentEvents.some((evt) => evt.channel === "netcatty:telnet:echo-mode"));
assert.deepEqual(sentEvents.find((evt) => evt.channel === "netcatty:telnet:echo-mode").payload, {
sessionId,
remoteEcho: false,
localEcho: false,
});
} finally {
terminalBridge.cleanupAllSessions();
for (const socket of sockets) socket.destroy();
await new Promise((resolve) => oldServer.close(resolve));
await new Promise((resolve) => newServer.close(resolve));
}
});
test("replacing a Telnet session discards buffered output from the old session", async () => {
const sessionId = `telnet-echo-test-${nextSessionId++}`;
const sockets = new Set();
let newSocket = null;
const newServer = net.createServer((socket) => {
newSocket = socket;
sockets.add(socket);
socket.on("error", () => {});
socket.on("close", () => sockets.delete(socket));
});
const newPort = await listen(newServer);
const sessions = new Map();
const sentEvents = [];
let discarded = 0;
let flushed = 0;
let destroyed = 0;
let cancelled = 0;
let released = 0;
sessions.set(sessionId, {
type: "telnet-native",
socket: {
destroy() {
destroyed += 1;
},
},
zmodemSentry: {
cancel() {
cancelled += 1;
},
},
discardPendingData() {
discarded += 1;
},
flushPendingData() {
flushed += 1;
},
releaseTelnetGeneration() {
released += 1;
},
});
terminalBridge.init({
sessions,
electronModule: {
webContents: {
fromId: () => ({
send(channel, payload) {
sentEvents.push({ channel, payload });
},
}),
},
},
});
try {
await terminalBridge.startTelnetSession(
{ sender: { id: 1 } },
{ sessionId, hostname: "127.0.0.1", port: newPort },
);
await waitFor(() => newSocket);
assert.equal(discarded, 1);
assert.equal(flushed, 0);
assert.equal(destroyed, 1);
assert.equal(cancelled, 1);
assert.equal(released, 1);
assert.equal(sentEvents.some((evt) => evt.channel === "netcatty:data"), false);
assert.equal(sessions.get(sessionId)?.type, "telnet-native");
} finally {
terminalBridge.cleanupAllSessions();
for (const socket of sockets) socket.destroy();
await new Promise((resolve) => newServer.close(resolve));
}
});
test("Telnet paced backlog records buffered pressure while draining", async () => {
const sessionId = `telnet-echo-test-${nextSessionId++}`;
const sockets = new Set();
let serverSocket = null;
const server = net.createServer((socket) => {
serverSocket = socket;
sockets.add(socket);
socket.on("error", () => {});
socket.on("close", () => sockets.delete(socket));
});
const port = await listen(server);
const sessions = new Map();
const sentEvents = [];
terminalBridge.init({
sessions,
electronModule: {
webContents: {
fromId: () => ({
send(channel, payload) {
sentEvents.push({ channel, payload });
},
}),
},
},
});
try {
await terminalBridge.startTelnetSession(
{ sender: { id: 1 } },
{ sessionId, hostname: "127.0.0.1", port },
);
await waitFor(() => serverSocket);
const session = sessions.get(sessionId);
session.flowState = {
rendererPaused: true,
unackedBytes: 0,
bufferedBytes: 0,
appliedPause: false,
outputPaused: true,
};
const flood = "x".repeat(FLOW_HIGH_WATER_MARK + 10);
serverSocket.write(flood);
await waitFor(() => (sessions.get(sessionId)?.flowState?.bufferedBytes || 0) > 0);
let flowState = sessions.get(sessionId)?.flowState;
assert.equal(flowState?.appliedPause, true);
assert.deepEqual(sentEvents.filter((evt) => evt.channel === "netcatty:data"), []);
terminalBridge.setSessionFlowPaused(
{ sender: {} },
{ sessionId, paused: false },
);
await waitFor(() => sentEvents.some((evt) => evt.channel === "netcatty:data"));
flowState = sessions.get(sessionId)?.flowState;
assert.equal(flowState?.rendererPaused, false);
} finally {
terminalBridge.cleanupAllSessions();
for (const socket of sockets) socket.destroy();
await new Promise((resolve) => server.close(resolve));
}
});
test("replacing a Telnet session cancels delayed automated writes from the old socket", async () => {
const sessionId = `telnet-echo-test-${nextSessionId++}`;
const sockets = new Set();
const oldChunks = [];
const newChunks = [];
let oldSocket = null;
let newSocket = null;
const oldServer = net.createServer((socket) => {
oldSocket = socket;
sockets.add(socket);
socket.on("error", () => {});
socket.on("close", () => sockets.delete(socket));
socket.on("data", (buf) => oldChunks.push(buf));
});
const newServer = net.createServer((socket) => {
newSocket = socket;
sockets.add(socket);
socket.on("error", () => {});
socket.on("close", () => sockets.delete(socket));
socket.on("data", (buf) => newChunks.push(buf));
});
const oldPort = await listen(oldServer);
const newPort = await listen(newServer);
const sessions = new Map();
terminalBridge.init({
sessions,
electronModule: {
webContents: {
fromId: () => ({ send() {} }),
},
},
});
try {
await terminalBridge.startTelnetSession(
{ sender: { id: 1 } },
{ sessionId, hostname: "127.0.0.1", port: oldPort },
);
await waitFor(() => oldSocket);
terminalBridge.writeToSession(
{},
{
sessionId,
data: "old-one\rold-two\r",
automated: true,
lineDelayMs: 80,
},
);
await waitFor(() => Buffer.concat(oldChunks).includes(Buffer.from("old-one\r\n")));
await terminalBridge.startTelnetSession(
{ sender: { id: 1 } },
{ sessionId, hostname: "127.0.0.1", port: newPort },
);
await waitFor(() => newSocket);
await delay(140);
assert.equal(Buffer.concat(newChunks).includes(Buffer.from("old-two")), false);
} finally {
terminalBridge.cleanupAllSessions();
for (const socket of sockets) socket.destroy();
await new Promise((resolve) => oldServer.close(resolve));
await new Promise((resolve) => newServer.close(resolve));
}
});