import type { ConfigPath, VyOSAuth } from './schemas/vyos-schemas';
/**
* @fileoverview VyOS HTTPS API client for network operating system automation.
* Provides a comprehensive interface to VyOS routers and firewalls via REST API.
*
* @author VyOS MCP Server
* @version 1.0.0
* @since 2025-01-13
*/
/**
* VyOS HTTPS API client for complete network automation and management.
*
* This client provides access to all VyOS API endpoints including:
* - Configuration management (show, set, delete, commit, save)
* - Operational commands (show, reset, generate)
* - System management (info, reboot, poweroff)
* - Network diagnostics (ping, traceroute)
* - Monitoring and statistics
*
* @example
* ```ts
* // ^?
* const client = new VyOSClient({
* host: "https://vyos.example.com",
* apiKey: "your-api-key",
* timeout: 30000,
* verifySSL: false
* });
*
* // Get system information
* const info = await client.getSystemInfo();
*
* // Configure interface
* await client.setConfig(["interfaces", "ethernet", "eth0", "address"], "192.168.1.1/24");
* await client.commit();
* await client.save();
* ```
*/
export class VyOSClient {
/** VyOS system hostname or IP address */
private host: string;
/** API authentication key */
private apiKey: string;
/** Request timeout in milliseconds */
private timeout: number;
/** Whether to verify SSL certificates (currently unused but kept for future implementation) */
// @ts-ignore - unused for now but kept for future SSL implementation
private _verifySSL: boolean;
/**
* Create a new VyOS API client instance.
*
* @param auth - Authentication and connection parameters
*
* @example
* ```ts
* const client = new VyOSClient({
* host: "https://192.168.1.1",
* apiKey: "my-secure-api-key",
* timeout: 30000,
* verifySSL: false // Common for self-signed certificates
* });
* ```
*/
constructor(auth: VyOSAuth) {
this.host = auth.host;
this.apiKey = auth.apiKey;
this.timeout = auth.timeout || 30000;
this._verifySSL = auth.verifySSL || false;
}
/**
* Internal method to make authenticated requests to VyOS API endpoints.
* Handles form data encoding, authentication, and error processing.
*
* @private
* @param endpoint - API endpoint path (e.g., "/info", "/configure")
* @param data - Request payload data
* @returns Promise resolving to API response data
* @throws {Error} When HTTP request fails or VyOS returns an error
*/
private async makeRequest(endpoint: string, data: unknown): Promise<unknown> {
const url = `${this.host}${endpoint}`;
const formData = new FormData();
formData.append('data', JSON.stringify(data));
formData.append('key', this.apiKey);
try {
const response = await fetch(url, {
method: 'POST',
body: formData,
signal: AbortSignal.timeout(this.timeout),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
if (result.error) {
throw new Error(`VyOS API Error: ${result.error}`);
}
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(`VyOS API request failed: ${error.message}`);
}
throw error;
}
}
/**
* Retrieve VyOS system information including version, hostname, and hardware details.
* This is the only API endpoint that doesn't require authentication.
*
* @returns Promise resolving to system information object
*
* @example
* ```ts
* const info = await client.getSystemInfo();
* console.log(`VyOS Version: ${info.version}`);
* console.log(`Hostname: ${info.hostname}`);
* ```
*/
async getSystemInfo(): Promise<unknown> {
return this.makeRequest('/info', {});
}
async showConfig(
path?: ConfigPath,
format: 'json' | 'commands' = 'json',
): Promise<unknown> {
return this.makeRequest('/retrieve', {
op: 'showConfig',
path: path || [],
configFormat: format,
});
}
async setConfig(path: ConfigPath, value?: string): Promise<unknown> {
return this.makeRequest('/configure', {
op: 'set',
path,
value,
});
}
async deleteConfig(path: ConfigPath): Promise<unknown> {
return this.makeRequest('/configure', {
op: 'delete',
path,
});
}
async configExists(path: ConfigPath): Promise<boolean> {
const result = await this.makeRequest('/retrieve', {
op: 'exists',
path,
});
return (
result as {
exists: boolean;
}
).exists;
}
async returnValues(path: ConfigPath): Promise<string[]> {
const result = await this.makeRequest('/retrieve', {
op: 'returnValues',
path,
});
return (
(
result as {
values?: string[];
}
).values || []
);
}
async commit(comment?: string, confirmTimeout?: number): Promise<unknown> {
const data: Record<string, unknown> = {
op: 'commit',
};
if (comment) data.comment = comment;
if (confirmTimeout) data.confirm = confirmTimeout;
return this.makeRequest('/config-file', data);
}
async save(): Promise<unknown> {
return this.makeRequest('/config-file', {
op: 'save',
});
}
async load(file?: string): Promise<unknown> {
return this.makeRequest('/config-file', {
op: 'load',
file: file || '/config/config.boot',
});
}
async show(
path: ConfigPath,
format: 'json' | 'raw' = 'json',
): Promise<unknown> {
return this.makeRequest('/show', {
path,
format,
});
}
async reset(path: ConfigPath): Promise<unknown> {
return this.makeRequest('/reset', {
path,
});
}
async generate(path: ConfigPath): Promise<unknown> {
return this.makeRequest('/generate', {
path,
});
}
async addImage(name: string): Promise<unknown> {
return this.makeRequest('/image', {
op: 'add',
name,
});
}
async deleteImage(name: string): Promise<unknown> {
return this.makeRequest('/image', {
op: 'delete',
name,
});
}
async reboot(): Promise<unknown> {
return this.makeRequest('/reboot', {});
}
async poweroff(): Promise<unknown> {
return this.makeRequest('/poweroff', {});
}
async ping(
host: string,
options: Record<string, unknown> = {},
): Promise<unknown> {
const pingCmd = [
'ping',
];
if (options.count) pingCmd.push('count', options.count.toString());
if (options.interval) pingCmd.push('interval', options.interval.toString());
if (options.timeout) pingCmd.push('timeout', options.timeout.toString());
if (options.size) pingCmd.push('size', options.size.toString());
if (options.source) pingCmd.push('source', options.source as string);
pingCmd.push(host);
return this.show(pingCmd);
}
async traceroute(
host: string,
options: Record<string, unknown> = {},
): Promise<unknown> {
const traceCmd = [
'traceroute',
];
if (options.maxHops) traceCmd.push('max-ttl', options.maxHops.toString());
if (options.timeout) traceCmd.push('timeout', options.timeout.toString());
if (options.source) traceCmd.push('source', options.source as string);
traceCmd.push(host);
return this.show(traceCmd);
}
async getVersion(): Promise<unknown> {
return this.show([
'version',
]);
}
async getUptime(): Promise<unknown> {
return this.show([
'system',
'uptime',
]);
}
async getInterfaces(): Promise<unknown> {
return this.show([
'interfaces',
]);
}
async getInterfaceStatistics(interfaceName?: string): Promise<unknown> {
const path = [
'interfaces',
];
if (interfaceName) {
path.push('ethernet', interfaceName, 'statistics');
} else {
path.push('statistics');
}
return this.show(path);
}
async getRoutes(): Promise<unknown> {
return this.show([
'ip',
'route',
]);
}
async getBGPSummary(): Promise<unknown> {
return this.show([
'protocols',
'bgp',
'summary',
]);
}
async getBGPNeighbors(): Promise<unknown> {
return this.show([
'protocols',
'bgp',
'neighbors',
]);
}
async getOSPFNeighbors(): Promise<unknown> {
return this.show([
'protocols',
'ospf',
'neighbor',
]);
}
async getVPNStatus(): Promise<unknown> {
return this.show([
'vpn',
'ipsec',
'sa',
]);
}
async getFirewallRules(): Promise<unknown> {
return this.show([
'firewall',
]);
}
async getNATRules(): Promise<unknown> {
return this.show([
'nat',
]);
}
async getDHCPLeases(): Promise<unknown> {
return this.show([
'dhcp',
'server',
'leases',
]);
}
async getConntrack(): Promise<unknown> {
return this.show([
'conntrack',
'table',
]);
}
async getLog(lines: number = 100): Promise<unknown> {
return this.show([
'log',
'tail',
lines.toString(),
]);
}
async getSystemResources(): Promise<unknown> {
return this.show([
'system',
'cpu',
]);
}
async getSystemMemory(): Promise<unknown> {
return this.show([
'system',
'memory',
]);
}
async getSystemStorage(): Promise<unknown> {
return this.show([
'system',
'storage',
]);
}
}