feat(core): add SSH data access proxy CLI and core managers

This commit is contained in:
2026-05-30 10:02:08 +00:00
commit 47d9846c93
23 changed files with 10399 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { DapCli, MountManager, PortProxy, SshClient } from '../ts/index.js';
tap.test('should build ssh and proxy command args', async () => {
const sshClient = new SshClient();
const sshArgs = sshClient.buildSshArgs('production', {
localForwards: ['5433:127.0.0.1:5432'],
reverseForwards: ['127.0.0.1:45000:127.0.0.1:3000'],
remoteCommand: 'echo ok',
});
expect(sshArgs).toEqual([
'-o',
'ExitOnForwardFailure=yes',
'-L',
'5433:127.0.0.1:5432',
'-R',
'127.0.0.1:45000:127.0.0.1:3000',
'production',
'echo ok',
]);
const portProxy = new PortProxy(sshClient);
expect(portProxy.buildArgs({ host: 'production', localForward: '8080:127.0.0.1:80' })).toEqual([
'-o',
'ExitOnForwardFailure=yes',
'-L',
'8080:127.0.0.1:80',
'-N',
'production',
]);
});
tap.test('should parse mount specs and cli flags', async () => {
const mountManager = new MountManager();
expect(mountManager.parseRemoteSpec('production:/srv/app')).toEqual({
host: 'production',
remotePath: '/srv/app',
});
expect(
mountManager.buildSshfsArgs({ host: 'production', remotePath: '/srv/app', localPath: './mount' })
).toEqual(['production:/srv/app', './mount']);
const cli = new DapCli();
expect(cli.parseArgs(['ssh', 'production', '--no-bridge', '--', '-A']).passthrough).toEqual(['-A']);
expect(cli.parseArgs(['add', 'prod', '--hostname', 'example.com']).flags.hostname).toEqual('example.com');
});
export default tap.start();
+49
View File
@@ -0,0 +1,49 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as fs from 'node:fs/promises';
import * as os from 'node:os';
import * as path from 'node:path';
import { SshConfig } from '../ts/index.js';
tap.test('should read main ssh config and included hosts', async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dap-sshconfig-'));
try {
const sshDir = path.join(tempDir, '.ssh');
const includeDir = path.join(sshDir, 'config.d');
await fs.mkdir(includeDir, { recursive: true });
const mainConfigPath = path.join(sshDir, 'config');
await fs.writeFile(
mainConfigPath,
[
'Include config.d/*',
'',
'Host alpha',
' HostName alpha.example.com',
' User deploy',
'',
'# dap:begin beta',
'Host beta',
' HostName beta.example.com',
'# dap:end beta',
'',
].join('\n')
);
await fs.writeFile(path.join(includeDir, 'hosts'), ['Host gamma', ' HostName gamma.example.com'].join('\n'));
const sshConfig = new SshConfig({ homeDir: tempDir, mainConfigPath });
const result = await sshConfig.read();
const aliases = result.hosts.map((host) => host.patterns[0]);
expect(aliases.includes('alpha')).toEqual(true);
expect(aliases.includes('beta')).toEqual(true);
expect(aliases.includes('gamma')).toEqual(true);
expect(result.hosts.find((host) => host.patterns[0] === 'beta')?.dapManaged).toEqual(true);
expect(result.hosts.find((host) => host.patterns[0] === 'alpha')?.options.hostname?.[0]).toEqual(
'alpha.example.com'
);
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
});
export default tap.start();
+45
View File
@@ -0,0 +1,45 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as fs from 'node:fs/promises';
import * as os from 'node:os';
import * as path from 'node:path';
import { SshConfigWriter } from '../ts/index.js';
tap.test('should add managed host blocks and update existing hosts', async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dap-sshconfigwriter-'));
try {
const sshDir = path.join(tempDir, '.ssh');
await fs.mkdir(sshDir, { recursive: true });
const mainConfigPath = path.join(sshDir, 'config');
await fs.writeFile(mainConfigPath, ['Host old', ' HostName old.example.com', ''].join('\n'));
const writer = new SshConfigWriter({ homeDir: tempDir, mainConfigPath });
const addResult = await writer.addOrReplaceManagedHost({
alias: 'alpha',
hostName: 'alpha.example.com',
user: 'deploy',
port: '22',
identityFile: '~/.ssh/id_ed25519',
});
const addedContent = await fs.readFile(mainConfigPath, 'utf8');
expect(addResult.changed).toEqual(true);
expect(addedContent.includes('# dap:begin alpha')).toEqual(true);
expect(addedContent.includes('HostName alpha.example.com')).toEqual(true);
const preview = await writer.previewUpdateHost('old', { user: 'root', port: '2222' });
expect(preview.dapManaged).toEqual(false);
expect(preview.diff.includes('+ User root')).toEqual(true);
const editResult = await writer.updateHost('old', { user: 'root', port: '2222' });
const editedContent = await fs.readFile(mainConfigPath, 'utf8');
expect(editResult.changed).toEqual(true);
expect(Boolean(editResult.backupPath)).toEqual(true);
expect(editedContent.includes(' User root')).toEqual(true);
expect(editedContent.includes(' Port 2222')).toEqual(true);
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
});
export default tap.start();