@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-ssh",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Pi tool — ssh_run: run remote commands over SSH with password/key auth and remote sudo",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
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
- const host = hostTarget(spec);
272
- const controlPath = controlPathFor(spec);
273
- const key = cacheKey(spec);
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;