171 lines
5.0 KiB
JavaScript
171 lines
5.0 KiB
JavaScript
const vscode = require('vscode');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
|
|
function activate(context) {
|
|
context.subscriptions.push(
|
|
vscode.commands.registerCommand('dosh.openRemote', openRemote),
|
|
vscode.commands.registerCommand('dosh.configureHost', configureHostCommand),
|
|
vscode.commands.registerCommand('dosh.showSshConfig', showSshConfig)
|
|
);
|
|
}
|
|
|
|
async function openRemote() {
|
|
const configured = await configureHost();
|
|
if (!configured) {
|
|
return;
|
|
}
|
|
const remotePath = await vscode.window.showInputBox({
|
|
title: 'Remote path',
|
|
prompt: 'Path to open on the remote host',
|
|
value: '~'
|
|
});
|
|
if (remotePath === undefined) {
|
|
return;
|
|
}
|
|
const suffix = remotePath ? `/${remotePath.replace(/^\/+/, '')}` : '';
|
|
await vscode.commands.executeCommand(
|
|
'vscode.openFolder',
|
|
vscode.Uri.parse(`vscode-remote://ssh-remote+${configured.alias}${suffix}`),
|
|
{ forceNewWindow: true }
|
|
);
|
|
}
|
|
|
|
async function configureHostCommand() {
|
|
const configured = await configureHost();
|
|
if (configured) {
|
|
vscode.window.showInformationMessage(`Dosh Remote-SSH host ready: ${configured.alias}`);
|
|
}
|
|
}
|
|
|
|
async function configureHost() {
|
|
const host = await vscode.window.showInputBox({
|
|
title: 'Dosh host',
|
|
prompt: 'Dosh host alias, e.g. palav',
|
|
ignoreFocusOut: true
|
|
});
|
|
if (!host) {
|
|
return undefined;
|
|
}
|
|
const user = await vscode.window.showInputBox({
|
|
title: 'Remote SSH user',
|
|
prompt: 'Optional. Leave blank to let SSH config decide.',
|
|
ignoreFocusOut: true
|
|
});
|
|
const config = vscode.workspace.getConfiguration('dosh');
|
|
const alias = `dosh-${safeAlias(host)}`;
|
|
const block = sshBlock({
|
|
alias,
|
|
host,
|
|
user: user || undefined,
|
|
executable: config.get('executable') || 'dosh',
|
|
targetHost: config.get('targetHost') || '127.0.0.1',
|
|
targetPort: Number(config.get('targetPort') || 22),
|
|
doshPort: Number(config.get('doshPort') || 0)
|
|
});
|
|
const sshDir = path.join(os.homedir(), '.ssh');
|
|
fs.mkdirSync(sshDir, { recursive: true });
|
|
const generatedPath = config.get('generatedSshConfig') || path.join(sshDir, 'config.dosh');
|
|
const mainConfig = path.join(sshDir, 'config');
|
|
ensureInclude(mainConfig, path.basename(generatedPath));
|
|
upsertBlock(generatedPath, alias, block);
|
|
return { alias, generatedPath };
|
|
}
|
|
|
|
function sshBlock(options) {
|
|
let proxy = shellQuote(options.executable);
|
|
if (options.doshPort > 0) {
|
|
proxy += ` --dosh-port ${options.doshPort}`;
|
|
}
|
|
proxy += ` proxy-stdio ${shellQuote(options.host)} %h %p`;
|
|
const lines = [
|
|
`# BEGIN DOSH ${options.alias}`,
|
|
`Host ${options.alias}`,
|
|
` HostName ${options.targetHost}`,
|
|
` Port ${options.targetPort}`,
|
|
` HostKeyAlias ${options.host}`,
|
|
options.user ? ` User ${options.user}` : undefined,
|
|
' ClearAllForwardings yes',
|
|
' ServerAliveInterval 15',
|
|
' ServerAliveCountMax 3',
|
|
` ProxyCommand ${proxy}`,
|
|
`# END DOSH ${options.alias}`,
|
|
''
|
|
].filter(Boolean);
|
|
return lines.join('\n');
|
|
}
|
|
|
|
function ensureInclude(configPath, includeFile) {
|
|
const raw = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf8') : '';
|
|
if (raw.split(/\r?\n/).some((line) => line.trim().toLowerCase() === `include ${includeFile}`.toLowerCase())) {
|
|
return;
|
|
}
|
|
fs.writeFileSync(configPath, `Include ${includeFile}\n${raw ? `\n${raw}` : ''}`);
|
|
}
|
|
|
|
function upsertBlock(configPath, alias, block) {
|
|
const raw = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf8') : '';
|
|
const begin = `# BEGIN DOSH ${alias}`;
|
|
const end = `# END DOSH ${alias}`;
|
|
const lines = raw.split(/\r?\n/);
|
|
const out = [];
|
|
let skipping = false;
|
|
let replaced = false;
|
|
for (const line of lines) {
|
|
if (line.trim() === begin) {
|
|
out.push(block.trimEnd());
|
|
skipping = true;
|
|
replaced = true;
|
|
continue;
|
|
}
|
|
if (skipping) {
|
|
if (line.trim() === end) {
|
|
skipping = false;
|
|
}
|
|
continue;
|
|
}
|
|
if (line.length > 0 || out.length > 0) {
|
|
out.push(line);
|
|
}
|
|
}
|
|
if (!replaced) {
|
|
if (out.length > 0 && out[out.length - 1] !== '') {
|
|
out.push('');
|
|
}
|
|
out.push(block.trimEnd());
|
|
}
|
|
fs.writeFileSync(configPath, `${out.join('\n').trimEnd()}\n`);
|
|
}
|
|
|
|
async function showSshConfig() {
|
|
const config = vscode.workspace.getConfiguration('dosh');
|
|
const sshDir = path.join(os.homedir(), '.ssh');
|
|
const generatedPath = config.get('generatedSshConfig') || path.join(sshDir, 'config.dosh');
|
|
if (!fs.existsSync(generatedPath)) {
|
|
vscode.window.showWarningMessage('No generated Dosh SSH config yet.');
|
|
return;
|
|
}
|
|
const doc = await vscode.workspace.openTextDocument(generatedPath);
|
|
await vscode.window.showTextDocument(doc);
|
|
}
|
|
|
|
function safeAlias(value) {
|
|
const alias = value.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
return alias || 'host';
|
|
}
|
|
|
|
function shellQuote(value) {
|
|
if (/^[a-zA-Z0-9_./:-]+$/.test(value)) {
|
|
return value;
|
|
}
|
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
}
|
|
|
|
function deactivate() {}
|
|
|
|
module.exports = {
|
|
activate,
|
|
deactivate
|
|
};
|