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
165 lines
5.3 KiB
TypeScript
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));
|
|
}
|