UNPKG

veas

Version:

Veas CLI - Command-line interface for Veas platform

207 lines 6.92 kB
import { EventEmitter } from 'node:events'; import { getBestAuthToken, prepareMCPHeaders } from './auth-wrapper.js'; export class SSEClient extends EventEmitter { url; eventSource = null; options; connectionStatus = false; constructor(url, options) { super(); this.url = url; this.options = options || {}; } connect() { if (this.connectionStatus) return; this.eventSource = new EventSource(this.url, this.options); this.eventSource.onopen = () => { this.connectionStatus = true; this.emit('open'); }; this.eventSource.onmessage = event => { try { const data = JSON.parse(event.data); this.emit('message', data); } catch (_error) { this.emit('error', new Error('Failed to parse message')); } }; this.eventSource.onerror = error => { this.emit('error', error); }; this.eventSource.addEventListener('custom', (event) => { this.emit('custom', event.data); }); } disconnect() { if (this.eventSource) { this.eventSource.close(); this.eventSource = null; this.connectionStatus = false; this.emit('close'); } } async send(data) { const response = await fetch(this.url, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(this.options?.headers || {}), }, body: JSON.stringify(data), }); if (!response.ok) { throw new Error(`Failed to send data: ${response.statusText}`); } return response.json(); } get readyState() { return this.eventSource?.readyState ?? 2; } addEventListener(type, listener) { this.on(type, listener); } removeEventListener(type, listener) { this.off(type, listener); } isConnected() { return this.connectionStatus; } getReadyState() { return this.eventSource?.readyState ?? 2; } } export async function getMCPToolsViaSSE(tokenOrAuthToken) { const apiUrl = process.env.VEAS_API_URL || 'https://veas.app'; let authToken; if (typeof tokenOrAuthToken === 'string') { authToken = { token: tokenOrAuthToken, type: tokenOrAuthToken.includes('_') ? 'pat' : 'cli', }; } else if (tokenOrAuthToken) { authToken = tokenOrAuthToken; } else { authToken = await getBestAuthToken(); } const mcpUrl = `${apiUrl}/api/mcp/mcp`; console.log(`[getMCPToolsViaSSE] Fetching from ${mcpUrl}`); console.log(`[getMCPToolsViaSSE] Using ${authToken.type} token: ${authToken.token.substring(0, 20)}...`); const response = await fetch(mcpUrl, { method: 'POST', headers: prepareMCPHeaders(authToken), body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/list', params: {}, id: 'list-tools', }), }); console.log(`[getMCPToolsViaSSE] Response status: ${response.status} ${response.statusText}`); if (!response.ok) { const body = await response.text(); console.log(`[getMCPToolsViaSSE] Error response body: ${body.substring(0, 500)}`); throw new Error(`Failed to fetch tools: ${response.status} ${response.statusText} - ${body.substring(0, 200)}`); } const contentType = response.headers.get('content-type') || ''; const text = await response.text(); let result; if (contentType.includes('text/event-stream') || text.startsWith('event:')) { const lines = text.split('\n'); const dataLine = lines.find(line => line.startsWith('data: ')); if (dataLine) { const jsonData = dataLine.substring(6); result = JSON.parse(jsonData); } else { throw new Error('Invalid SSE response: no data line found'); } } else { result = JSON.parse(text); } if (result?.error) { throw new Error(`Failed to list tools: ${result.error.message}`); } if (result?.result?.tools) { return result.result.tools; } else if (result?.tools) { return result.tools; } else if (Array.isArray(result)) { return result; } return []; } export async function executeMCPToolViaSSE(name, args, tokenOrAuthToken) { const apiUrl = process.env.VEAS_API_URL || 'https://veas.app'; let authToken; if (typeof tokenOrAuthToken === 'string') { authToken = { token: tokenOrAuthToken, type: tokenOrAuthToken.includes('_') ? 'pat' : 'cli', }; } else if (tokenOrAuthToken) { authToken = tokenOrAuthToken; } else { authToken = await getBestAuthToken(); } const mcpUrl = `${apiUrl}/api/mcp/mcp`; console.log(`[executeMCPToolViaSSE] Calling tool: ${name}`); console.log(`[executeMCPToolViaSSE] URL: ${mcpUrl}`); console.log(`[executeMCPToolViaSSE] Args:`, JSON.stringify(args)); console.log(`[executeMCPToolViaSSE] Using ${authToken.type} token: ${authToken.token.substring(0, 20)}...`); const response = await fetch(mcpUrl, { method: 'POST', headers: prepareMCPHeaders(authToken), body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/call', params: { name, arguments: args, }, id: Date.now().toString(), }), }); console.log(`[executeMCPToolViaSSE] Response status: ${response.status} ${response.statusText}`); if (!response.ok) { const error = await response.text(); console.log(`[executeMCPToolViaSSE] Error response: ${error.substring(0, 500)}`); throw new Error(`Tool execution failed (${response.status}): ${error.substring(0, 200)}`); } const contentType = response.headers.get('content-type') || ''; const text = await response.text(); let result; if (contentType.includes('text/event-stream') || text.startsWith('event:')) { const lines = text.split('\n'); const dataLine = lines.find(line => line.startsWith('data: ')); if (dataLine) { const jsonData = dataLine.substring(6); result = JSON.parse(jsonData); } else { throw new Error('Invalid SSE response: no data line found'); } } else { result = JSON.parse(text); } if (result?.error) { throw new Error(result.error.message); } if (result?.result) { return result.result; } else if (result?.content) { return result.content; } return result; } //# sourceMappingURL=sse-client.js.map