[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,342 @@
import assert from "node:assert/strict";
import test from "node:test";
import { buildTerminalContextSnapshotText } from "../../domain/terminalContextRead.ts";
import {
applyAuthoritativeHibernateSnapshot,
isTerminalAlternateScreenActive,
nudgeAlternateScreenRedraw,
readTerminalHibernateContext,
refreshTerminalViewport,
resolveHibernateSerializeOptions,
resolveTerminalSnapshotCapture,
serializeTerminalForHibernate,
} from "./terminalHibernateRuntime.ts";
test("authoritative empty snapshots clear every hibernated copy", () => {
const ref = <T>(current: T) => ({ current });
const refs = {
snapshot: ref("old snapshot"),
viewportSnapshot: ref("old viewport"),
scrollbackSnapshot: ref("old scrollback"),
contextSnapshot: ref("old context"),
contextViewportSnapshot: ref("old context viewport"),
contextScrollbackSnapshot: ref("old context scrollback"),
pendingBuffer: ref("old pending"),
alternateScreen: ref(true),
};
applyAuthoritativeHibernateSnapshot(refs, "", {
contextSnapshot: "",
contextViewportSnapshot: "",
contextScrollbackSnapshot: "",
alternateScreen: false,
});
assert.deepEqual(Object.fromEntries(
Object.entries(refs).map(([key, value]) => [key, value.current]),
), {
snapshot: "",
viewportSnapshot: "",
scrollbackSnapshot: "",
contextSnapshot: "",
contextViewportSnapshot: "",
contextScrollbackSnapshot: "",
pendingBuffer: "",
alternateScreen: false,
});
});
test("authoritative non-empty snapshots keep readable hibernated context", () => {
const ref = <T>(current: T) => ({ current });
const refs = {
snapshot: ref(""),
viewportSnapshot: ref(""),
scrollbackSnapshot: ref("old scrollback"),
contextSnapshot: ref("old context"),
contextViewportSnapshot: ref("old context viewport"),
contextScrollbackSnapshot: ref("old context scrollback"),
pendingBuffer: ref("old pending"),
alternateScreen: ref(true),
};
const serialized = "\x1b[31mleft\x1b[4Cright\x1b[0m";
const context = {
contextSnapshot: "history\nleft right",
contextViewportSnapshot: "left right",
contextScrollbackSnapshot: "history",
alternateScreen: true,
};
applyAuthoritativeHibernateSnapshot(refs, serialized, context);
assert.equal(refs.snapshot.current, serialized);
assert.equal(refs.viewportSnapshot.current, serialized);
assert.equal(refs.scrollbackSnapshot.current, "");
assert.equal(refs.contextSnapshot.current, context.contextSnapshot);
assert.equal(refs.contextViewportSnapshot.current, context.contextViewportSnapshot);
assert.equal(refs.contextScrollbackSnapshot.current, context.contextScrollbackSnapshot);
assert.equal(refs.pendingBuffer.current, "");
assert.equal(refs.alternateScreen.current, true);
});
const createFakeTerm = (bufferType: "normal" | "alternate", rows = 24, length = 30) => ({
rows,
cols: 80,
buffer: {
active: {
type: bufferType,
length,
getLine(index: number) {
return {
translateToString: () => `screen-${index}`,
};
},
},
},
});
test("resolveHibernateSerializeOptions keeps alt buffer and modes for full-screen apps", () => {
const term = createFakeTerm("alternate");
assert.equal(isTerminalAlternateScreenActive(term as never), true);
assert.deepEqual(resolveHibernateSerializeOptions(term as never), {
excludeAltBuffer: false,
excludeModes: false,
alternateScreen: true,
});
});
test("resolveHibernateSerializeOptions excludes alt buffer on the normal screen", () => {
const term = createFakeTerm("normal");
assert.equal(isTerminalAlternateScreenActive(term as never), false);
assert.deepEqual(resolveHibernateSerializeOptions(term as never), {
excludeAltBuffer: true,
excludeModes: true,
alternateScreen: false,
});
});
test("readTerminalHibernateContext uses rendered buffer text instead of replay control sequences", () => {
const term = createFakeTerm("normal", 3, 5);
const context = readTerminalHibernateContext(term as never);
assert.equal(context.contextScrollbackSnapshot, "screen-0\nscreen-1");
assert.equal(context.contextViewportSnapshot, "screen-2\nscreen-3\nscreen-4");
assert.equal(context.contextSnapshot, "screen-0\nscreen-1\nscreen-2\nscreen-3\nscreen-4");
});
test("readTerminalHibernateContext preserves a scrolled viewport separately from full context", () => {
const term = createFakeTerm("normal", 3, 7);
Object.assign(term.buffer.active, { viewportY: 2 });
const context = readTerminalHibernateContext(term as never);
assert.equal(context.contextSnapshot, Array.from({ length: 7 }, (_, index) => `screen-${index}`).join("\n"));
assert.equal(context.contextScrollbackSnapshot, "screen-0\nscreen-1");
assert.equal(context.contextViewportSnapshot, "screen-2\nscreen-3\nscreen-4");
assert.equal(context.alternateScreen, false);
});
test("readTerminalHibernateContext marks alternate-screen viewport context", () => {
const term = createFakeTerm("alternate", 3, 3);
assert.deepEqual(readTerminalHibernateContext(term as never), {
contextSnapshot: "screen-0\nscreen-1\nscreen-2",
contextViewportSnapshot: "screen-0\nscreen-1\nscreen-2",
contextScrollbackSnapshot: "",
alternateScreen: true,
});
});
test("readTerminalHibernateContext treats an all-empty buffer as no readable context", () => {
const term = createFakeTerm("normal", 3, 3);
Object.assign(term.buffer.active, {
getLine() {
return { translateToString: () => "" };
},
});
assert.deepEqual(readTerminalHibernateContext(term as never), {
contextSnapshot: "",
contextViewportSnapshot: "",
contextScrollbackSnapshot: "",
alternateScreen: false,
});
});
test("readTerminalHibernateContext preserves blank scrollback line boundaries", () => {
const lines = ["", "", "v1", "v2", "v3"];
const term = createFakeTerm("normal", 3, lines.length);
Object.assign(term.buffer.active, {
viewportY: 2,
getLine(index: number) {
return { translateToString: () => lines[index] ?? "" };
},
});
const context = readTerminalHibernateContext(term as never);
assert.equal(context.contextSnapshot, "\n\nv1\nv2\nv3");
assert.equal(context.contextScrollbackSnapshot, "\n");
assert.equal(context.contextViewportSnapshot, "v1\nv2\nv3");
assert.deepEqual(buildTerminalContextSnapshotText({
scrollbackText: context.contextScrollbackSnapshot,
viewportText: context.contextViewportSnapshot,
pendingText: "",
}), {
fullText: "\n\nv1\nv2\nv3",
viewportStartLine: 2,
viewportEndLine: 4,
});
});
test("readTerminalHibernateContext preserves an empty viewport after readable history", () => {
const lines = ["history", "", "", ""];
const term = createFakeTerm("normal", 3, lines.length);
Object.assign(term.buffer.active, {
viewportY: 1,
getLine(index: number) {
return { translateToString: () => lines[index] ?? "" };
},
});
const context = readTerminalHibernateContext(term as never);
assert.equal(context.contextSnapshot, "history\n\n\n");
assert.equal(context.contextScrollbackSnapshot, "history");
assert.equal(context.contextViewportSnapshot, "\n\n");
assert.equal(buildTerminalContextSnapshotText({
scrollbackText: context.contextScrollbackSnapshot,
viewportText: context.contextViewportSnapshot,
pendingText: "",
}).viewportStartLine, 1);
});
test("resolveTerminalSnapshotCapture falls back for missing serializers and preserves alternate mode", () => {
const context = {
contextSnapshot: "old\nscreen",
contextViewportSnapshot: "screen",
contextScrollbackSnapshot: "old",
alternateScreen: true,
};
assert.deepEqual(resolveTerminalSnapshotCapture(undefined, context), {
snapshot: "\x1b[?1049h\x1b[Hscreen",
context,
});
assert.equal(resolveTerminalSnapshotCapture("", context).snapshot, "");
});
test("refreshTerminalViewport skips refresh when the terminal has no rows", () => {
const term = {
rows: 0,
refresh: () => {
throw new Error("refresh should not be called");
},
};
refreshTerminalViewport(term as never);
});
test("refreshTerminalViewport refreshes the full viewport", () => {
let refreshed: [number, number] | null = null;
const term = {
rows: 24,
refresh: (start: number, end: number) => {
refreshed = [start, end];
},
};
refreshTerminalViewport(term as never);
assert.deepEqual(refreshed, [0, 23]);
});
test("alternate-screen redraw never forces xterm parser flushing through resize", () => {
const refreshed: Array<[number, number]> = [];
const term = {
cols: 80,
rows: 24,
refresh: (start: number, end: number) => refreshed.push([start, end]),
resize: () => {
throw new Error("redraw must not resize while a TUI frame may still be pending");
},
};
nudgeAlternateScreenRedraw(term as never);
assert.deepEqual(refreshed, [[0, 23]]);
});
test("serializeTerminalForHibernate preserves alternate screen when serialize throws", async () => {
const term = createFakeTerm("alternate");
const serializeAddon = {
serialize: () => {
throw new Error("serialize failed");
},
};
const result = await serializeTerminalForHibernate(term as never, serializeAddon as never);
assert.equal(result.snapshot, "");
assert.equal(result.viewportSnapshot, "");
assert.equal(result.scrollbackSnapshot, "");
assert.equal(result.contextViewportSnapshot?.startsWith("screen-0\nscreen-1"), true);
assert.equal(result.alternateScreen, true);
});
test("serializeTerminalForHibernate requests viewport-only range on alternate screen", async () => {
let capturedOptions: Record<string, unknown> | undefined;
const term = createFakeTerm("alternate", 24);
const serializeAddon = {
serialize: (options?: Record<string, unknown>) => {
capturedOptions = options;
return "alt-viewport";
},
};
const result = await serializeTerminalForHibernate(term as never, serializeAddon as never);
assert.equal(result.snapshot, "alt-viewport");
assert.equal(result.contextViewportSnapshot, Array.from({ length: 24 }, (_, index) => `screen-${index}`).join("\n"));
assert.deepEqual(capturedOptions?.range, { start: 0, end: 23 });
});
test("serializeTerminalForHibernate keeps visual capture when context buffer reading fails", async () => {
const term = createFakeTerm("normal", 3, 3);
Object.assign(term.buffer.active, {
getLine() {
throw new Error("buffer unavailable");
},
});
const result = await serializeTerminalForHibernate(term as never, {
serialize: () => "serialized",
} as never);
assert.equal(result.snapshot, "serialized");
assert.equal(result.contextSnapshot, "");
assert.equal(result.contextViewportSnapshot, "");
assert.equal(result.contextScrollbackSnapshot, "");
});
test("serializeTerminalForHibernate preserves plain text context for normal screen", async () => {
const capturedRanges: unknown[] = [];
const term = createFakeTerm("normal", 3, 5);
const serializeAddon = {
serialize: (options?: Record<string, unknown>) => {
capturedRanges.push(options?.range);
return "serialized";
},
};
const result = await serializeTerminalForHibernate(term as never, serializeAddon as never);
assert.equal(result.viewportSnapshot, "serialized");
assert.equal(result.scrollbackSnapshot, "serialized");
assert.equal(result.contextScrollbackSnapshot, "screen-0\nscreen-1");
assert.equal(result.contextViewportSnapshot, "screen-2\nscreen-3\nscreen-4");
assert.equal(result.contextSnapshot, "screen-0\nscreen-1\nscreen-2\nscreen-3\nscreen-4");
assert.deepEqual(capturedRanges, [
{ start: 2, end: 4 },
{ start: 0, end: 1 },
undefined,
]);
});
test("serializeTerminalForHibernate waits for deferred history preparation", async () => {
const term = createFakeTerm("normal", 3, 5);
const events: string[] = [];
const result = await serializeTerminalForHibernate(term as never, {
serialize: () => {
events.push("serialize");
return "serialized";
},
} as never, {
prepare: async () => {
await new Promise((resolve) => setTimeout(resolve, 1));
events.push("prepare");
},
});
assert.equal(result.snapshot, "serialized");
assert.deepEqual(events.slice(0, 2), ["prepare", "serialize"]);
});