[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,114 @@
const test = require("node:test");
const assert = require("node:assert/strict");
const iconv = require("iconv-lite");
const {
normalizeTerminalEncoding,
isUtf8Encoding,
encodeTerminalInput,
} = require("./terminalEncoding.cjs");
test("normalizeTerminalEncoding maps GB variants onto the gb18030 superset", () => {
assert.equal(normalizeTerminalEncoding("GB18030"), "gb18030");
assert.equal(normalizeTerminalEncoding("gbk"), "gb18030");
assert.equal(normalizeTerminalEncoding("GB2312"), "gb18030");
assert.equal(normalizeTerminalEncoding("gb-18030"), "gb18030");
assert.equal(normalizeTerminalEncoding("zh_CN.GB18030"), "gb18030");
assert.equal(normalizeTerminalEncoding("zh_CN.GBK"), "gb18030");
assert.equal(normalizeTerminalEncoding("zh_CN.cp936"), "gb18030");
assert.equal(normalizeTerminalEncoding("zh_CN.GB18030@custom"), "gb18030");
});
test("normalizeTerminalEncoding normalizes utf-8 spellings and defaults", () => {
assert.equal(normalizeTerminalEncoding("UTF-8"), "utf-8");
assert.equal(normalizeTerminalEncoding("utf8"), "utf-8");
assert.equal(normalizeTerminalEncoding(""), "utf-8");
assert.equal(normalizeTerminalEncoding(undefined), "utf-8");
// Unknown / unsupported charset falls back to utf-8 rather than throwing.
assert.equal(normalizeTerminalEncoding("not-a-real-charset"), "utf-8");
});
test("isUtf8Encoding recognizes utf-8 spellings and treats unset as utf-8", () => {
assert.equal(isUtf8Encoding("utf-8"), true);
assert.equal(isUtf8Encoding("UTF8"), true);
assert.equal(isUtf8Encoding(undefined), true);
assert.equal(isUtf8Encoding(""), true);
assert.equal(isUtf8Encoding("gb18030"), false);
});
test("encodeTerminalInput leaves UTF-8 input as the original string", () => {
// For UTF-8 we hand the string straight to the transport's native
// serialization, so the value must be returned unchanged (no Buffer).
assert.equal(encodeTerminalInput("ls -la 你好\r", "utf-8"), "ls -la 你好\r");
// Unset encoding (mosh / local PTY) behaves like UTF-8.
assert.equal(encodeTerminalInput("你好", undefined), "你好");
});
test("encodeTerminalInput encodes non-UTF-8 input to matching bytes", () => {
const out = encodeTerminalInput("你好", "gb18030");
assert.ok(Buffer.isBuffer(out), "non-UTF-8 input should produce a Buffer");
assert.deepEqual([...out], [...iconv.encode("你好", "gb18030")]);
// Crucially, the GB18030 bytes differ from the UTF-8 bytes — this is the bug:
// the old path always sent UTF-8 bytes regardless of the configured charset.
assert.notDeepEqual([...out], [...Buffer.from("你好", "utf8")]);
});
test("encodeTerminalInput preserves ASCII control bytes under GB18030", () => {
// CSI cursor-up, CR, Ctrl-C — all ASCII, must pass through byte-for-byte so
// escape sequences and control keys keep working on non-UTF-8 sessions.
const input = "\x1b[A\r\x03";
const out = encodeTerminalInput(input, "gb18030");
assert.deepEqual([...out], [0x1b, 0x5b, 0x41, 0x0d, 0x03]);
});
test("encodeTerminalInput round-trips with the GB18030 output decoder", () => {
// Input/output symmetry guarantee (issue #1216): bytes we write for a
// keystroke decode back to the same text the output path would show.
const text = "查看版本 v1.2\tabc\r";
const wireBytes = encodeTerminalInput(text, "gb18030");
const decoder = iconv.getDecoder("gb18030");
assert.equal(decoder.write(wireBytes), text);
});
test("encodeTerminalInput round-trips with the UTF-8 output decoder", () => {
const text = "echo 你好世界\r";
// UTF-8 path returns a string; the transport serializes it natively, which is
// byte-identical to encoding it as UTF-8.
const wire = encodeTerminalInput(text, "utf-8");
const wireBytes = typeof wire === "string" ? Buffer.from(wire, "utf8") : wire;
const decoder = iconv.getDecoder("utf-8");
assert.equal(decoder.write(wireBytes), text);
});
test("encodeTerminalInput falls back to the string for unknown encodings", () => {
// A misconfigured charset must degrade to today's UTF-8 behavior instead of
// throwing on the keystroke hot path.
assert.equal(encodeTerminalInput("你好", "definitely-not-real"), "你好");
});
test("encodeTerminalInput refuses ASCII-incompatible encodings (UTF-16/UCS-2)", () => {
// iconv accepts these, but encoding control bytes would widen "\r" to 0d 00
// and "\x1b[A" to 1b 00 5b 00 ..., breaking Enter / arrows on the remote.
// Fall back to the original string (today's UTF-8 behavior) instead.
for (const enc of ["utf-16le", "utf-16be", "ucs2", "utf-16"]) {
assert.equal(
encodeTerminalInput("ls\r", enc),
"ls\r",
`${enc} should fall back to the original string`,
);
}
});
test("encodeTerminalInput still encodes other ASCII-superset CJK charsets", () => {
// Sanity check that the ASCII-compat guard does not over-reject: legacy
// multi-byte charsets that ARE ASCII supersets keep working for input.
for (const enc of ["big5", "shift_jis", "euc-jp"]) {
const out = encodeTerminalInput("\r", enc);
assert.ok(Buffer.isBuffer(out), `${enc} should encode to a Buffer`);
assert.deepEqual([...out], [0x0d], `${enc} must keep CR single-byte`);
}
});
test("encodeTerminalInput passes non-string payloads through untouched", () => {
const buf = Buffer.from([0x01, 0x02, 0x03]);
assert.equal(encodeTerminalInput(buf, "gb18030"), buf);
});