veas
Version:
Veas CLI - Command-line interface for Veas platform
207 lines • 6.92 kB
JavaScript
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