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 };