@xynogen/pix-ssh 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +9 -3
- package/src/lib.test.ts +15 -0
- package/src/lib.ts +33 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
MAX_OUTPUT_LINES,
|
|
60
60
|
parseHost,
|
|
61
61
|
probeKeyAuth,
|
|
62
|
+
resolveSshHost,
|
|
62
63
|
runSsh,
|
|
63
64
|
truncate,
|
|
64
65
|
} from "./lib.ts";
|
|
@@ -268,9 +269,14 @@ export default function (pi: ExtensionAPI): void {
|
|
|
268
269
|
isError: true,
|
|
269
270
|
};
|
|
270
271
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const
|
|
272
|
+
// ponytail: let OpenSSH own config parsing; `ssh -G` handles aliases,
|
|
273
|
+
// Include, and Match rules without duplicating its config grammar.
|
|
274
|
+
const effectiveSpec = await resolveSshHost(spec, sig);
|
|
275
|
+
const host = hostTarget(effectiveSpec);
|
|
276
|
+
// Keep original target for execution so alias-specific IdentityFile,
|
|
277
|
+
// ProxyJump, and other SSH config options still apply.
|
|
278
|
+
const controlPath = controlPathFor(effectiveSpec);
|
|
279
|
+
const key = cacheKey(effectiveSpec);
|
|
274
280
|
const creds = credCache.get(key) ?? {};
|
|
275
281
|
|
|
276
282
|
const g = globalThis as { __pixAfk?: boolean; __pixYolo?: boolean };
|
package/src/lib.test.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
hostTarget,
|
|
11
11
|
isUnreachable,
|
|
12
12
|
parseHost,
|
|
13
|
+
parseSshConfig,
|
|
13
14
|
remoteCommand,
|
|
14
15
|
shellQuote,
|
|
15
16
|
truncate,
|
|
@@ -46,6 +47,20 @@ describe("parseHost", () => {
|
|
|
46
47
|
});
|
|
47
48
|
});
|
|
48
49
|
|
|
50
|
+
describe("parseSshConfig", () => {
|
|
51
|
+
it("reads effective user, hostname, and port from ssh -G output", () => {
|
|
52
|
+
expect(parseSshConfig("host orin\nuser jetson\nhostname 10.10.21.251\nport 2222\n")).toEqual({
|
|
53
|
+
user: "jetson",
|
|
54
|
+
host: "10.10.21.251",
|
|
55
|
+
port: 2222,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("rejects incomplete ssh -G output", () => {
|
|
60
|
+
expect(parseSshConfig("hostname 10.10.21.251\n")).toBeUndefined();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
49
64
|
describe("hostTarget", () => {
|
|
50
65
|
it("joins user and host", () => {
|
|
51
66
|
expect(hostTarget({ user: "a", host: "b" })).toBe("a@b");
|
package/src/lib.ts
CHANGED
|
@@ -108,6 +108,39 @@ function parsePort(value: string): number {
|
|
|
108
108
|
return n;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/** Parse effective destination fields emitted by OpenSSH's `ssh -G`. */
|
|
112
|
+
export function parseSshConfig(output: string): HostSpec | undefined {
|
|
113
|
+
const values = new Map(
|
|
114
|
+
output
|
|
115
|
+
.split("\n")
|
|
116
|
+
.map((line) => line.trim().split(/\s+/, 2))
|
|
117
|
+
.filter((parts): parts is [string, string] => parts.length === 2),
|
|
118
|
+
);
|
|
119
|
+
const user = values.get("user");
|
|
120
|
+
const host = values.get("hostname");
|
|
121
|
+
const port = values.get("port");
|
|
122
|
+
if (!user || !host || !port) return undefined;
|
|
123
|
+
return { user, host, port: parsePort(port) };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Resolve aliases through OpenSSH config, including Include and Match rules. */
|
|
127
|
+
export function resolveSshHost(spec: HostSpec, signal?: AbortSignal): Promise<HostSpec> {
|
|
128
|
+
const args = ["-G"];
|
|
129
|
+
if (spec.port !== undefined) args.push("-p", String(spec.port));
|
|
130
|
+
args.push(hostTarget(spec));
|
|
131
|
+
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
let stdout = "";
|
|
134
|
+
const proc = spawn("ssh", args, { stdio: ["ignore", "pipe", "ignore"] });
|
|
135
|
+
proc.stdout.on("data", (chunk: Buffer) => {
|
|
136
|
+
stdout += chunk.toString();
|
|
137
|
+
});
|
|
138
|
+
proc.on("error", () => resolve(spec));
|
|
139
|
+
proc.on("close", (code) => resolve(code === 0 ? (parseSshConfig(stdout) ?? spec) : spec));
|
|
140
|
+
signal?.addEventListener("abort", () => proc.kill("SIGTERM"), { once: true });
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
111
144
|
/** Canonical `[user@]host` target string for ssh argv. */
|
|
112
145
|
export function hostTarget(spec: HostSpec): string {
|
|
113
146
|
return spec.user ? `${spec.user}@${spec.host}` : spec.host;
|