Files
my-pi/pi-ssh/src/import.ts
T

66 lines
2.3 KiB
TypeScript

import { execFileSync } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
export type EffectiveSshConfig = Map<string, string[]>;
export function parseSshG(text: string): EffectiveSshConfig {
const values: EffectiveSshConfig = new Map();
for (const line of text.split(/\r?\n/)) {
const separator = line.indexOf(" ");
if (separator < 1) continue;
const key = line.slice(0, separator).trim().toLowerCase();
const value = line.slice(separator + 1).trim();
if (!key || !value) continue;
const current = values.get(key) ?? [];
current.push(value);
values.set(key, current);
}
return values;
}
export function resolveOpenSshAlias(alias: string): EffectiveSshConfig {
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(alias)) throw new Error(`invalid SSH alias: ${alias}`);
const output = execFileSync("ssh", ["-G", "--", alias], {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
return parseSshG(output);
}
export function effectiveValue(config: EffectiveSshConfig, key: string): string | undefined {
return config.get(key.toLowerCase())?.at(-1);
}
export function effectiveValues(config: EffectiveSshConfig, key: string): string[] {
return config.get(key.toLowerCase()) ?? [];
}
export function effectiveIdentityAgent(
config: EffectiveSshConfig,
env: NodeJS.ProcessEnv = process.env,
): string | undefined {
const configured = effectiveValue(config, "identityagent")?.replace(/^"|"$/g, "");
if (!configured || configured.toLowerCase() === "none") return undefined;
if (configured === "SSH_AUTH_SOCK" || configured === "$SSH_AUTH_SOCK" || configured === "${SSH_AUTH_SOCK}") {
return env.SSH_AUTH_SOCK;
}
return configured;
}
export function listDirectSshAliases(configPath = join(homedir(), ".ssh", "config")): string[] {
if (!existsSync(configPath)) return [];
const aliases: string[] = [];
const text = readFileSync(configPath, "utf8");
for (const line of text.split(/\r?\n/)) {
const match = /^\s*Host\s+(.+?)\s*$/i.exec(line);
if (!match) continue;
for (const alias of match[1].split(/\s+/)) {
if (!alias || alias.includes("*") || alias.includes("?") || alias.startsWith("!")) continue;
if (!aliases.includes(alias)) aliases.push(alias);
}
}
return aliases;
}