mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-12 02:19:43 +00:00
47 lines
2.1 KiB
JavaScript
47 lines
2.1 KiB
JavaScript
// Writes an OpenSSH host entry for the session Codespace, so tools that use the
|
|
// system ssh (the Claude Code desktop app, VS Code Remote-SSH, plain `ssh`) can
|
|
// connect by a fixed name.
|
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { homedir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
|
|
export const HOST_ALIAS = 'n8n-codespace';
|
|
const HOST_FILE = `${HOST_ALIAS}.conf`;
|
|
const INCLUDE_LINE = `Include ~/.ssh/${HOST_FILE}`;
|
|
|
|
/**
|
|
* Replaces the host name in `gh codespace ssh --config` output with a fixed alias.
|
|
* gh names the host `cs.<codespace>.<branch>`, so its name changes when the
|
|
* checkout changes branch. A saved connection must not depend on that.
|
|
*/
|
|
export function aliasHostConfig(ghConfig, alias = HOST_ALIAS) {
|
|
const hosts = ghConfig.match(/^Host .*$/gm) ?? [];
|
|
if (hosts.length !== 1 || !/^\s*ProxyCommand /m.test(ghConfig)) {
|
|
throw new Error(`Expected one host entry from gh codespace ssh --config, got ${hosts.length}`);
|
|
}
|
|
return ghConfig.replace(/^Host .*$/m, `Host ${alias}`).trimEnd() + '\n';
|
|
}
|
|
|
|
/**
|
|
* Returns the ssh config with the Include line at the top, or undefined if the line
|
|
* is already there. An Include after a Host block applies only to that block.
|
|
*/
|
|
export function withInclude(sshConfig, includeLine = INCLUDE_LINE) {
|
|
if (sshConfig.split('\n').some((line) => line.trim() === includeLine)) return undefined;
|
|
return `${includeLine}\n${sshConfig.length ? '\n' : ''}${sshConfig}`;
|
|
}
|
|
|
|
export function writeSshConfig(ghConfig, sshDir = join(homedir(), '.ssh')) {
|
|
mkdirSync(sshDir, { recursive: true, mode: 0o700 });
|
|
|
|
const hostPath = join(sshDir, HOST_FILE);
|
|
writeFileSync(hostPath, aliasHostConfig(ghConfig), { mode: 0o600 });
|
|
|
|
const configPath = join(sshDir, 'config');
|
|
const current = existsSync(configPath) ? readFileSync(configPath, 'utf8') : '';
|
|
const updated = withInclude(current);
|
|
// The mode applies only when the file is new. An existing file keeps its mode.
|
|
if (updated !== undefined) writeFileSync(configPath, updated, { mode: 0o600 });
|
|
return { hostPath, configPath, includeAdded: updated !== undefined };
|
|
}
|