mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-31 02:42:31 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ca8ff9693 |
No files matched your search
@@ -4,7 +4,7 @@ import test from "node:test";
|
||||
import type { Host } from "../../domain/models";
|
||||
import { prepareSerialConfigForSavedHost } from "../../domain/serialBackspace";
|
||||
import { buildTelnetDeepLinkConnectionHost } from "../../domain/telnetDeepLink";
|
||||
import { resolveEffectiveTerminalHost } from "../../domain/terminalHostResolution";
|
||||
import { resolveEffectiveTerminalHost, resolveTerminalSessionHost } from "../../domain/terminalHostResolution";
|
||||
import {
|
||||
createHostTerminalSession,
|
||||
createSerialTerminalSession,
|
||||
@@ -171,3 +171,16 @@ test("host session factories snapshot plugin connection configuration", () => {
|
||||
assert.deepEqual(workspace.pluginConnection, pluginConnection);
|
||||
assert.notEqual(workspace.pluginConnection, pluginConnection);
|
||||
});
|
||||
|
||||
test("local sessions preserve intentionally absent display metadata when the saved shell changes", () => {
|
||||
const original = host({ protocol: "local", hostname: "localhost", localShell: "/bin/bash" });
|
||||
const source = createHostTerminalSession("original-bash", original);
|
||||
const edited = { ...original, localShell: "/bin/zsh", localShellName: "Zsh", localShellIcon: "zsh" };
|
||||
const copied = resolveTerminalSessionHost({ session: source, hosts: [edited], groupConfigs: [], proxyProfiles: [], localOs: "macos" });
|
||||
assert.equal(copied.localShell, "/bin/bash");
|
||||
assert.equal(copied.localShellName, "");
|
||||
assert.equal(copied.localShellIcon, "");
|
||||
const named = createHostTerminalSession("named-zsh", edited);
|
||||
assert.equal(named.localShellName, "Zsh");
|
||||
assert.equal(named.localShellIcon, "zsh");
|
||||
});
|
||||
@@ -153,8 +153,8 @@ export const createHostTerminalSession = (
|
||||
// Preserve field presence: an edited host must not contribute its args
|
||||
// to a session that was launched with an explicit shell and no args.
|
||||
localShellArgs: host.localShellArgs ?? EMPTY_LOCAL_SHELL_ARGS,
|
||||
localShellName: host.localShellName,
|
||||
localShellIcon: host.localShellIcon,
|
||||
localShellName: host.localShellName ?? "",
|
||||
localShellIcon: host.localShellIcon ?? "",
|
||||
// Effective directory from resolveHostDefaultLocalShell, including a
|
||||
// Settings directory the saved host inherits. Copy Tab overlays this
|
||||
// snapshot, so a later settings or vault edit cannot move the relaunch.
|
||||
|
||||
@@ -86,7 +86,7 @@ const SessionTabIcon: React.FC<{
|
||||
// Local protocol → shell-specific icon if available, else OS-specific icon
|
||||
if (protocol === 'local' || host?.protocol === 'local' || (!protocol && !host)) {
|
||||
// Use shell icon from discovery when available
|
||||
const iconId = shellIcon || host?.localShellIcon;
|
||||
const iconId = shellIcon ?? host?.localShellIcon;
|
||||
if (iconId) {
|
||||
return (
|
||||
<img
|
||||
|
||||
@@ -875,3 +875,21 @@ test("restore retains an explicit backend-default local shell snapshot", () => {
|
||||
assert.deepEqual(restored.sessions[0].localShellArgs, []);
|
||||
assert.equal(restored.sessions[0].localStartDir, "~");
|
||||
});
|
||||
|
||||
test("restore retains absent local display snapshots while legacy fields remain absent", () => {
|
||||
const payload = buildSessionRestorePayload({
|
||||
sessions: [
|
||||
{ ...session("empty-display"), protocol: "local", localShellName: "", localShellIcon: "" },
|
||||
{ ...session("named-display"), protocol: "local", localShellName: "Bash", localShellIcon: "bash" },
|
||||
{ ...session("legacy-display"), protocol: "local" },
|
||||
],
|
||||
workspaces: [], tabOrder: [], activeTabId: "empty-display", now: 123,
|
||||
});
|
||||
const restored = sanitizeSessionRestorePayload(JSON.parse(JSON.stringify(payload)));
|
||||
assert.equal(restored.sessions[0].localShellName, "");
|
||||
assert.equal(restored.sessions[0].localShellIcon, "");
|
||||
assert.equal(restored.sessions[1].localShellName, "Bash");
|
||||
assert.equal(restored.sessions[1].localShellIcon, "bash");
|
||||
assert.equal(restored.sessions[2].localShellName, undefined);
|
||||
assert.equal(restored.sessions[2].localShellIcon, undefined);
|
||||
});
|
||||
@@ -154,8 +154,8 @@ const restoreSession = (session: TerminalSession): RestoredTerminalSession => {
|
||||
...(serialConfig ? { serialConfig } : {}),
|
||||
...(session.localShell !== undefined ? { localShell: session.localShell } : {}),
|
||||
...(session.localShellArgs ? { localShellArgs: [...session.localShellArgs] } : {}),
|
||||
...(session.localShellName ? { localShellName: session.localShellName } : {}),
|
||||
...(session.localShellIcon ? { localShellIcon: session.localShellIcon } : {}),
|
||||
...(session.localShellName !== undefined ? { localShellName: session.localShellName } : {}),
|
||||
...(session.localShellIcon !== undefined ? { localShellIcon: session.localShellIcon } : {}),
|
||||
...(session.localStartDir ? { localStartDir: session.localStartDir } : {}),
|
||||
...(session.fontSize !== undefined ? { fontSize: session.fontSize } : {}),
|
||||
...(session.fontSizeOverride !== undefined ? { fontSizeOverride: session.fontSizeOverride } : {}),
|
||||
@@ -197,8 +197,8 @@ const restoreSessionFromUnknown = (value: unknown): RestoredTerminalSession | nu
|
||||
...(serialConfig ? { serialConfig } : {}),
|
||||
...(readStringAllowEmpty(value, "localShell") !== undefined ? { localShell: readStringAllowEmpty(value, "localShell") } : {}),
|
||||
...(Array.isArray(value.localShellArgs) ? { localShellArgs: value.localShellArgs.filter((arg): arg is string => typeof arg === "string") } : {}),
|
||||
...(readString(value, "localShellName") ? { localShellName: readString(value, "localShellName") } : {}),
|
||||
...(readString(value, "localShellIcon") ? { localShellIcon: readString(value, "localShellIcon") } : {}),
|
||||
...(readStringAllowEmpty(value, "localShellName") !== undefined ? { localShellName: readStringAllowEmpty(value, "localShellName") } : {}),
|
||||
...(readStringAllowEmpty(value, "localShellIcon") !== undefined ? { localShellIcon: readStringAllowEmpty(value, "localShellIcon") } : {}),
|
||||
...(readString(value, "localStartDir") ? { localStartDir: readString(value, "localStartDir") } : {}),
|
||||
...(readNumber(value, "fontSize") !== undefined ? { fontSize: readNumber(value, "fontSize") } : {}),
|
||||
...(readBoolean(value, "fontSizeOverride") !== undefined ? { fontSizeOverride: readBoolean(value, "fontSizeOverride") } : {}),
|
||||
|
||||
@@ -560,7 +560,6 @@ async function runOpenCodeTurn({
|
||||
let opencode = null;
|
||||
let sessionId = resumeSessionId || null;
|
||||
let hasContent = false;
|
||||
let backgroundWarningCount = 0;
|
||||
let failed = false;
|
||||
let abortSent = false;
|
||||
let removeAbortListener = null;
|
||||
@@ -620,13 +619,6 @@ async function runOpenCodeTurn({
|
||||
if (abortController?.signal?.aborted) break;
|
||||
const eventSessionId = getOpenCodeSessionIdFromEvent(event);
|
||||
if (eventSessionId && eventSessionId !== sessionId) continue;
|
||||
if (!eventSessionId && getOpenCodeEventPayload(event)?.type === "session.error") {
|
||||
const message = extractOpenCodeErrorMessage(getOpenCodeEventPayload(event).properties?.error)
|
||||
|| "OpenCode reported a background error";
|
||||
if (emitter.warning) emitter.warning(`opencode-background-error:${++backgroundWarningCount}`, message);
|
||||
else emitter.status(message);
|
||||
continue;
|
||||
}
|
||||
const result = translateOpenCodeEvent(event, emitter, state);
|
||||
if (result.content) hasContent = true;
|
||||
if (result.error) {
|
||||
|
||||
@@ -500,52 +500,6 @@ test("runOpenCodeTurn continues after a tool error part and reaches session.idle
|
||||
assert.equal(events.some((event) => event.k === "error"), false);
|
||||
});
|
||||
|
||||
for (const scoped of [false, true]) {
|
||||
test(`runOpenCodeTurn ${scoped ? "fails on the current session error" : "warns about plugin errors without stopping the session"} (#3173)`, async () => {
|
||||
const { events, emitter } = collector();
|
||||
emitter.warning = (id, message) => events.push({ k: "warning", id, message });
|
||||
const message = scoped ? "bad key" : "Failed to install plugin: invalid package name";
|
||||
const stream = {
|
||||
async *[Symbol.asyncIterator]() {
|
||||
yield {
|
||||
type: "session.error",
|
||||
properties: { ...(scoped ? { sessionID: "sess-1" } : {}), error: { data: { message } } },
|
||||
};
|
||||
if (!scoped) {
|
||||
yield {
|
||||
type: "session.error",
|
||||
properties: { error: { data: { message: "Failed to install another plugin" } } },
|
||||
};
|
||||
}
|
||||
yield {
|
||||
type: "message.part.updated",
|
||||
properties: { part: { type: "text", sessionID: "sess-1", id: "p1", text: "reply" }, delta: "reply" },
|
||||
};
|
||||
yield { type: "session.idle", properties: { sessionID: "sess-1" } };
|
||||
},
|
||||
};
|
||||
await runOpenCodeTurn({
|
||||
prompt: "continue",
|
||||
resumeSessionId: "sess-1",
|
||||
emitter,
|
||||
openCodeFactory: async () => ({
|
||||
client: {
|
||||
global: { event: async () => ({ stream }) },
|
||||
session: { promptAsync: async () => ({ data: true }) },
|
||||
},
|
||||
server: { close() {} },
|
||||
}),
|
||||
});
|
||||
assert.equal(events.some((event) => event.k === "error" && event.m === message), scoped);
|
||||
assert.equal(events.some((event) => event.k === "warning" && event.message === message), !scoped);
|
||||
assert.equal(events.some((event) => event.k === "text" && event.t === "reply"), !scoped);
|
||||
assert.equal(events.some((event) => event.k === "done"), !scoped);
|
||||
const warnings = events.filter((event) => event.k === "warning");
|
||||
assert.equal(warnings.length, scoped ? 0 : 2);
|
||||
assert.equal(new Set(warnings.map((event) => event.id)).size, warnings.length);
|
||||
});
|
||||
}
|
||||
|
||||
test("mapOpenCodeModels flattens providers", () => {
|
||||
assert.deepEqual(mapOpenCodeModels({
|
||||
providers: [
|
||||
|
||||
Reference in new issue
Block a user