Files
NetMesh/domain/terminalHostResolution.ts
zhaolei 3c72efcb7f
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
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

165 lines
5.3 KiB
TypeScript

import type { GroupConfig, Host, ProxyProfile, TerminalSession } from "./models";
import { applyGroupDefaults, resolveGroupDefaults } from "./groupConfig";
import { materializeHostProxyProfile } from "./proxyProfiles";
import { sanitizePluginConnection } from "./pluginConnection";
type LocalOs = Host["os"];
interface ResolveEffectiveHostOptions {
host: Host;
groupConfigs: GroupConfig[];
proxyProfiles: ProxyProfile[];
validProxyProfileIds?: ReadonlySet<string>;
}
interface ResolveTerminalSessionHostOptions {
session: TerminalSession;
hosts: Host[];
groupConfigs: GroupConfig[];
proxyProfiles: ProxyProfile[];
localOs: LocalOs;
}
interface ResolveTerminalChainHostsOptions {
host: Host | null | undefined;
hosts: Host[];
groupConfigs: GroupConfig[];
proxyProfiles: ProxyProfile[];
validProxyProfileIds?: ReadonlySet<string>;
}
const resolveSerialBackspaceBehavior = (
serialConfig: Host["serialConfig"],
): Host["backspaceBehavior"] | null => {
if (serialConfig?.backspaceBehavior === "ctrl-h") return "ctrl-h";
if (serialConfig?.backspaceBehavior === "default") return undefined;
return null;
};
export function resolveEffectiveTerminalHost({
host,
groupConfigs,
proxyProfiles,
validProxyProfileIds = new Set(proxyProfiles.map((profile) => profile.id)),
}: ResolveEffectiveHostOptions): Host {
const groupDefaults = host.group
? resolveGroupDefaults(host.group, groupConfigs, { validProxyProfileIds })
: {};
return materializeHostProxyProfile(
applyGroupDefaults(host, groupDefaults, { validProxyProfileIds }),
proxyProfiles,
);
}
const suppressDeviceTypeForShellTransport = (host: Host): Host => {
if (!host.moshEnabled && !host.etEnabled) return host;
if (host.deviceType === undefined) return host;
return { ...host, deviceType: undefined };
};
function buildFallbackHostFromSession(
session: TerminalSession,
localOs: LocalOs,
): Host {
const fallbackProtocol = session.protocol ?? "ssh";
return {
id: session.hostId,
label: session.hostLabel || "Local Terminal",
hostname: session.hostname || "localhost",
username: session.username || "local",
port: session.port ?? 22,
os: fallbackProtocol === "local" ? localOs : "linux",
group: "",
tags: [],
protocol: fallbackProtocol,
pluginConnection: sanitizePluginConnection(session.pluginConnection, fallbackProtocol),
moshEnabled: session.moshEnabled,
etEnabled: session.etEnabled,
charset: session.charset,
serialConfig: session.serialConfig,
backspaceBehavior: session.serialConfig?.backspaceBehavior === "ctrl-h" ? "ctrl-h" : undefined,
localShell: session.localShell,
localShellArgs: session.localShellArgs,
localShellName: session.localShellName,
localShellIcon: session.localShellIcon,
localStartDir: session.localStartDir,
};
}
export function resolveTerminalSessionHost({
session,
hosts,
groupConfigs,
proxyProfiles,
localOs,
}: ResolveTerminalSessionHostOptions): Host {
const vaultHost = hosts.find((host) => host.id === session.hostId);
if (!vaultHost) return buildFallbackHostFromSession(session, localOs);
const existingHost = resolveEffectiveTerminalHost({
host: vaultHost,
groupConfigs,
proxyProfiles,
});
const protocol = session.protocol ?? existingHost.protocol;
const pluginConnection = sanitizePluginConnection(session.pluginConnection, protocol)
?? sanitizePluginConnection(existingHost.pluginConnection, protocol);
const port = session.port ?? existingHost.port;
const moshEnabled = session.moshEnabled ?? existingHost.moshEnabled;
const etEnabled = session.etEnabled ?? existingHost.etEnabled;
const sessionSerialBackspace = resolveSerialBackspaceBehavior(session.serialConfig);
const hostSerialBackspace = resolveSerialBackspaceBehavior(existingHost.serialConfig);
const backspaceBehavior = protocol === "serial"
? sessionSerialBackspace !== null
? sessionSerialBackspace
: hostSerialBackspace !== null
? hostSerialBackspace
: existingHost.backspaceBehavior
: existingHost.backspaceBehavior;
if (
protocol === existingHost.protocol &&
port === existingHost.port &&
moshEnabled === existingHost.moshEnabled &&
etEnabled === existingHost.etEnabled &&
JSON.stringify(pluginConnection) === JSON.stringify(existingHost.pluginConnection) &&
backspaceBehavior === existingHost.backspaceBehavior
) {
return suppressDeviceTypeForShellTransport(existingHost);
}
return suppressDeviceTypeForShellTransport({
...existingHost,
protocol,
port,
moshEnabled,
etEnabled,
pluginConnection,
backspaceBehavior,
});
}
export function resolveTerminalChainHosts({
host,
hosts,
groupConfigs,
proxyProfiles,
validProxyProfileIds = new Set(proxyProfiles.map((profile) => profile.id)),
}: ResolveTerminalChainHostsOptions): Host[] {
if (!host?.hostChain?.hostIds?.length) return [];
const hostMap = new Map(hosts.map((candidate) => [candidate.id, candidate]));
return host.hostChain.hostIds
.map((hostId) => {
const chainHost = hostMap.get(hostId);
if (!chainHost) return undefined;
return resolveEffectiveTerminalHost({
host: chainHost,
groupConfigs,
proxyProfiles,
validProxyProfileIds,
});
})
.filter((value): value is Host => Boolean(value));
}