cx-vcc
Version:
Cloudonix Agentic Voice Connector Tool
168 lines (147 loc) • 5.33 kB
JavaScript
const axios = require('axios');
const { logApiRequest, logApiResponse, logApiError, debugLog } = require('../utils/debug');
const { getConfig, saveConfig } = require('../utils/config');
class RetellApiService {
constructor(apiKey, baseUrl = 'https://api.retellai.com') {
this.apiKey = apiKey;
this.baseUrl = baseUrl;
this.client = axios.create({
baseURL: this.baseUrl,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
}
});
this._setupInterceptors();
this._updateConfig();
}
_setupInterceptors() {
this.client.interceptors.request.use(
(config) => {
if (process.argv.includes('--debug')) {
console.log('\n=== Retell API Request ===');
console.log(`${config.method.toUpperCase()} ${config.url}`);
console.log('Headers:', JSON.stringify(config.headers, null, 2));
console.log('Request Data:', JSON.stringify(config.data, null, 2));
console.log('========================\n');
}
return config;
},
(error) => {
if (process.argv.includes('--debug')) {
console.error('\n=== Retell API Request Error ===');
console.error(error);
console.error('================================\n');
}
return Promise.reject(error);
}
);
this.client.interceptors.response.use(
(response) => {
if (process.argv.includes('--debug')) {
console.log('\n=== Retell API Response ===');
console.log(`Status: ${response.status} ${response.statusText}`);
console.log('Headers:', JSON.stringify(response.headers, null, 2));
console.log('Response Data:', JSON.stringify(response.data, null, 2));
console.log('==========================\n');
}
return response;
},
(error) => {
if (process.argv.includes('--debug')) {
console.error('\n=== Retell API Error Response ===');
if (error.response) {
console.error(`Status: ${error.response.status} ${error.response.statusText}`);
console.error('Headers:', JSON.stringify(error.response.headers, null, 2));
console.error('Error Data:', JSON.stringify(error.response.data, null, 2));
} else if (error.request) {
console.error('No response received');
console.error('Request:', error.request);
} else {
console.error('Error:', error.message);
}
console.error('================================\n');
}
return Promise.reject(error);
}
);
}
_updateConfig() {
const config = getConfig();
config.retell = {
...config.retell,
apiKey: this.apiKey,
apiUrl: this.baseUrl
};
saveConfig(config);
}
_getLineNumber() {
return new Error().stack.split('\n')[2].match(/:(\d+):/)[1];
}
async verifyApiKey() {
try {
await this.client.get('/list-agents');
return true;
} catch (error) {
if (error.response && error.response.status === 403) {
throw new Error('Invalid Retell API key: Authentication failed');
}
if (error.response && error.response.status !== 403) {
debugLog('Warning: Retell API returned a non-403 error, assuming API key is valid');
return true;
}
this._handleError(error, 'Failed to verify Retell API key');
}
}
async getPhoneNumbers() {
try {
const response = await this.client.get('/list-phone-numbers');
return response.data;
} catch (error) {
this._handleError(error, 'Failed to retrieve Retell phone numbers');
}
}
async importPhoneNumber(phoneNumber, domainName) {
try {
const config = getConfig();
const domainConfig = config.domains[domainName];
if (!domainConfig?.inboundSipUri) {
throw new Error(`Domain ${domainName} not found or missing inbound SIP URI`);
}
const response = await this.client.post('/import-phone-number', {
phone_number: phoneNumber,
termination_uri: domainConfig.inboundSipUri
});
this._updatePhoneNumberConfig(config, phoneNumber, domainName, response.data.last_modification_timestamp);
return response.data;
} catch (error) {
this._handleError(error, 'Failed to import phone number to ReTell');
}
}
_updatePhoneNumberConfig(config, phoneNumber, domainName, id) {
if (!config.retell) {
config.retell = { phoneNumbers: {} };
} else if (!config.retell.phoneNumbers) {
config.retell.phoneNumbers = {};
}
config.retell.phoneNumbers[phoneNumber] = { id, domainName };
config.retell.apiKey = this.apiKey;
config.retell.apiUrl = this.baseUrl;
saveConfig(config);
}
_handleError(error, message) {
let errorMessage = message;
if (error.response) {
const { status, data } = error.response;
errorMessage += data?.error || data?.message
? `: ${data.error || data.message}`
: `: Status ${status}`;
} else if (error.request) {
errorMessage += ': No response received from server';
} else {
errorMessage += `: ${error.message}`;
}
throw new Error(errorMessage);
}
}
module.exports = RetellApiService;