UNPKG

@vfarcic/dot-ai

Version:

AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance

149 lines (148 loc) 4.49 kB
"use strict"; /** * Plugin Client for dot-ai Plugin System * * HTTP client for communicating with agentic plugins. * Handles describe and invoke hooks via POST /execute endpoint. * * PRD #343: kubectl Plugin Migration */ Object.defineProperty(exports, "__esModule", { value: true }); exports.PluginClient = exports.PluginClientError = void 0; /** * Error thrown when plugin communication fails */ class PluginClientError extends Error { pluginName; pluginUrl; cause; constructor(message, pluginName, pluginUrl, cause) { super(message); this.pluginName = pluginName; this.pluginUrl = pluginUrl; this.cause = cause; this.name = 'PluginClientError'; } } exports.PluginClientError = PluginClientError; /** * HTTP client for a single plugin */ class PluginClient { config; logger; timeout; constructor(config, logger) { this.config = config; this.logger = logger; // PRD #343: Increased default timeout from 30s to 5m for Helm operations // Helm install/upgrade with --wait can take several minutes for complex charts this.timeout = config.timeout ?? 300000; } /** * Get plugin name */ get name() { return this.config.name; } /** * Get plugin URL */ get url() { return this.config.url; } /** * Call the describe hook to get tool definitions */ async describe() { const request = { hook: 'describe', }; this.logger.debug('Calling plugin describe hook', { plugin: this.config.name, url: this.config.url, }); const response = await this.execute(request); this.logger.debug('Plugin describe response', { plugin: this.config.name, version: response.version, toolCount: response.tools.length, }); return response; } /** * Call the invoke hook to execute a tool */ async invoke(tool, args, state = {}, sessionId) { const payload = { tool, args, state, }; const request = { hook: 'invoke', sessionId, payload, }; this.logger.debug('Calling plugin invoke hook', { plugin: this.config.name, tool, sessionId, }); const response = await this.execute(request); this.logger.debug('Plugin invoke response', { plugin: this.config.name, tool, success: response.success, }); return response; } /** * Check if plugin is healthy/reachable */ async healthCheck() { try { await this.describe(); return true; } catch { return false; } } /** * Execute a request to the plugin's /execute endpoint */ async execute(request) { const executeUrl = `${this.config.url}/execute`; try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); const response = await fetch(executeUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(request), signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { const errorText = await response.text().catch(() => 'Unknown error'); throw new PluginClientError(`Plugin returned HTTP ${response.status}: ${errorText}`, this.config.name, this.config.url); } const data = await response.json(); return data; } catch (error) { if (error instanceof PluginClientError) { throw error; } const cause = error instanceof Error ? error : new Error(String(error)); if (cause.name === 'AbortError') { throw new PluginClientError(`Plugin request timed out after ${this.timeout}ms`, this.config.name, this.config.url, cause); } throw new PluginClientError(`Failed to communicate with plugin: ${cause.message}`, this.config.name, this.config.url, cause); } } } exports.PluginClient = PluginClient;