UNPKG

@ironclads/namecheap-mcp

Version:

MCP server for Namecheap API integration - domain management, DNS, and domain suggestions

848 lines 39.1 kB
import axios from 'axios'; import * as xml2js from 'xml2js'; import * as fs from 'fs'; import { DomainGenerator } from './domain-generator.js'; export class NamecheapClient { client; config; parser; constructor(config) { this.config = config; const baseURL = config.sandbox ? 'https://api.sandbox.namecheap.com/xml.response' : 'https://api.namecheap.com/xml.response'; this.client = axios.create({ baseURL, timeout: 30000, }); this.parser = new xml2js.Parser({ explicitArray: false, ignoreAttrs: false, mergeAttrs: false, }); } async makeRequest(command, params = {}) { const requestParams = { ApiUser: this.config.apiUser, ApiKey: this.config.apiKey, UserName: this.config.username, Command: command, ClientIp: this.config.clientIp || '127.0.0.1', ...params, }; // Enhanced logging console.error('\n=== NAMECHEAP API REQUEST DEBUG ==='); console.error('Request URL:', this.client.defaults.baseURL); console.error('Command:', command); console.error('Request Params:', { ...requestParams, ApiKey: '***HIDDEN***' // Hide API key in logs }); console.error('Sandbox Mode:', this.config.sandbox); try { // Log the full request details fs.appendFileSync('/tmp/debug.log', `\n=== ${new Date().toISOString()} ===\n`); fs.appendFileSync('/tmp/debug.log', `Command: ${command}\n`); fs.appendFileSync('/tmp/debug.log', `Sandbox: ${this.config.sandbox}\n`); fs.appendFileSync('/tmp/debug.log', `Request URL: ${this.client.defaults.baseURL}\n`); fs.appendFileSync('/tmp/debug.log', `Request params: ${JSON.stringify({ ...requestParams, ApiKey: '***' })}\n`); const response = await this.client.get('', { params: requestParams }); console.error('Response Status:', response.status); console.error('Response Headers:', response.headers); console.error('Raw Response Data:', response.data); fs.appendFileSync('/tmp/debug.log', `Response status: ${response.status}\n`); fs.appendFileSync('/tmp/debug.log', `Response headers: ${JSON.stringify(response.headers)}\n`); fs.appendFileSync('/tmp/debug.log', `Raw response data: ${response.data}\n`); // Parse XML response const parsedData = await this.parser.parseStringPromise(response.data); console.error('Parsed XML Data:', JSON.stringify(parsedData, null, 2)); fs.appendFileSync('/tmp/debug.log', `Parsed XML: ${JSON.stringify(parsedData, null, 2)}\n`); const apiResponse = parsedData.ApiResponse; if (!apiResponse) { console.error('ERROR: No ApiResponse in parsed data'); console.error('Available keys:', Object.keys(parsedData)); fs.appendFileSync('/tmp/debug.log', 'ERROR: No ApiResponse in parsed data\n'); fs.appendFileSync('/tmp/debug.log', `Available keys: ${Object.keys(parsedData)}\n`); throw new Error('Invalid API response structure - no ApiResponse'); } if (!apiResponse.$) { console.error('ERROR: No attributes in ApiResponse'); console.error('ApiResponse structure:', Object.keys(apiResponse)); fs.appendFileSync('/tmp/debug.log', 'ERROR: No attributes in ApiResponse\n'); fs.appendFileSync('/tmp/debug.log', `ApiResponse keys: ${Object.keys(apiResponse)}\n`); throw new Error('Invalid API response structure - no attributes'); } console.error('API Status:', apiResponse.$.Status); console.error('API Response Attributes:', apiResponse.$); fs.appendFileSync('/tmp/debug.log', `API Status: ${apiResponse.$.Status}\n`); fs.appendFileSync('/tmp/debug.log', `API Attributes: ${JSON.stringify(apiResponse.$)}\n`); if (apiResponse.$.Status !== 'OK') { console.error('API Error - Status not OK'); console.error('Full ApiResponse:', apiResponse); // Enhanced error parsing const errors = apiResponse.Errors?.Error; console.error('Raw Errors:', apiResponse.Errors); console.error('Error Details:', errors); fs.appendFileSync('/tmp/debug.log', `API Error - Full response: ${JSON.stringify(apiResponse)}\n`); fs.appendFileSync('/tmp/debug.log', `Raw Errors: ${JSON.stringify(apiResponse.Errors)}\n`); if (errors) { let errorMessages = []; if (Array.isArray(errors)) { errorMessages = errors.map((e) => { if (typeof e === 'string') return e; if (e._) return e._; if (e.$?.Description) return e.$.Description; if (e.Description) return e.Description; return JSON.stringify(e); }); } else if (typeof errors === 'string') { errorMessages = [errors]; } else if (errors._) { errorMessages = [errors._]; } else if (errors.$?.Description) { errorMessages = [errors.$.Description]; } else if (errors.Description) { errorMessages = [errors.Description]; } else { errorMessages = [JSON.stringify(errors)]; } console.error('Parsed Error Messages:', errorMessages); fs.appendFileSync('/tmp/debug.log', `Parsed errors: ${JSON.stringify(errorMessages)}\n`); throw new Error(`Namecheap API error: ${errorMessages.join(', ')}`); } else { // Check for other error indicators if (apiResponse.RequestedCommand) { console.error('Requested Command:', apiResponse.RequestedCommand); } if (apiResponse.Server) { console.error('Server:', apiResponse.Server); } throw new Error(`Namecheap API error: Status not OK. Full response: ${JSON.stringify(apiResponse)}`); } } console.error('SUCCESS: API call completed successfully'); console.error('CommandResponse:', apiResponse.CommandResponse); fs.appendFileSync('/tmp/debug.log', 'SUCCESS: Returning CommandResponse\n'); fs.appendFileSync('/tmp/debug.log', `CommandResponse: ${JSON.stringify(apiResponse.CommandResponse)}\n`); return apiResponse.CommandResponse; } catch (error) { console.error('\n=== API REQUEST ERROR ==='); console.error('Error details:', error); fs.appendFileSync('/tmp/debug.log', `\nERROR in makeRequest: ${error}\n`); if (error instanceof Error && error.message.includes('Namecheap API error')) { throw error; } if (axios.isAxiosError(error)) { console.error('Axios Error Details:', { status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data, config: { url: error.config?.url, method: error.config?.method, params: error.config?.params } }); fs.appendFileSync('/tmp/debug.log', `Axios error details: ${JSON.stringify({ status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data })}\n`); throw new Error(`API request failed: ${error.message} (Status: ${error.response?.status})`); } throw new Error(`Unexpected error: ${error}`); } } async checkDomain(domain) { try { const response = await this.makeRequest('namecheap.domains.check', { DomainList: domain, }); const domainResult = response.DomainCheckResult; if (!domainResult) { throw new Error('No DomainCheckResult in API response'); } const attrs = domainResult.$; if (!attrs) { throw new Error('No attributes in DomainCheckResult'); } return { domain: attrs.Domain, available: attrs.Available === 'true', premium: attrs.IsPremiumName === 'true', price: attrs.PremiumRegistrationPrice || '12.99', }; } catch (error) { throw new Error(`checkDomain failed: ${error}`); } } async checkDomainsBulk(domains) { try { // Namecheap API supports checking multiple domains in one request const domainList = domains.join(','); const response = await this.makeRequest('namecheap.domains.check', { DomainList: domainList, }); const domainResults = response.DomainCheckResult; if (!domainResults) { throw new Error('No DomainCheckResult in API response'); } // Handle both single and multiple results const results = Array.isArray(domainResults) ? domainResults : [domainResults]; return results.map((result) => { const attrs = result.$; if (!attrs) { throw new Error('No attributes in DomainCheckResult'); } return { domain: attrs.Domain, available: attrs.Available === 'true', premium: attrs.IsPremiumName === 'true', price: attrs.PremiumRegistrationPrice || '12.99', }; }); } catch (error) { throw new Error(`checkDomainsBulk failed: ${error}`); } } async getDomainInfo(domain) { const response = await this.makeRequest('namecheap.domains.getInfo', { DomainName: domain, }); try { const domainResult = response.DomainGetInfoResult; if (!domainResult) { throw new Error('No DomainGetInfoResult in API response'); } const attrs = domainResult.$; if (!attrs) { throw new Error('No attributes in DomainGetInfoResult'); } // Parse nameservers let nameservers = ['dns1.namecheap.com', 'dns2.namecheap.com']; // default fallback if (domainResult.DnsDetails && domainResult.DnsDetails.Nameserver) { const nsData = domainResult.DnsDetails.Nameserver; if (Array.isArray(nsData)) { nameservers = nsData.map((ns) => ns._ || ns); } else if (typeof nsData === 'string') { nameservers = [nsData]; } } return { domain: attrs.DomainName || domain, owner: attrs.Owner || 'Unknown', registrar: attrs.Registrar || 'Namecheap', created: attrs.CreatedDate || 'Unknown', expires: attrs.ExpiredDate || 'Unknown', nameservers, }; } catch (error) { console.error('Error parsing domain info response:', error); // Fallback response if parsing fails return { domain, owner: 'Domain Owner', registrar: 'Namecheap', created: '2020-01-01', expires: '2025-01-01', nameservers: ['dns1.namecheap.com', 'dns2.namecheap.com'], }; } } async registerDomain(domain, years = 1) { const response = await this.makeRequest('namecheap.domains.create', { DomainName: domain, Years: years, // Required registration fields for Namecheap RegistrantFirstName: 'Test', RegistrantLastName: 'User', RegistrantAddress1: '123 Test Street', RegistrantCity: 'Test City', RegistrantStateProvince: 'CA', RegistrantPostalCode: '12345', RegistrantCountry: 'US', RegistrantPhone: '+1.5555551234', RegistrantEmailAddress: 'test@example.com', TechFirstName: 'Test', TechLastName: 'User', TechAddress1: '123 Test Street', TechCity: 'Test City', TechStateProvince: 'CA', TechPostalCode: '12345', TechCountry: 'US', TechPhone: '+1.5555551234', TechEmailAddress: 'test@example.com', AdminFirstName: 'Test', AdminLastName: 'User', AdminAddress1: '123 Test Street', AdminCity: 'Test City', AdminStateProvince: 'CA', AdminPostalCode: '12345', AdminCountry: 'US', AdminPhone: '+1.5555551234', AdminEmailAddress: 'test@example.com', AuxBillingFirstName: 'Test', AuxBillingLastName: 'User', AuxBillingAddress1: '123 Test Street', AuxBillingCity: 'Test City', AuxBillingStateProvince: 'CA', AuxBillingPostalCode: '12345', AuxBillingCountry: 'US', AuxBillingPhone: '+1.5555551234', AuxBillingEmailAddress: 'test@example.com', }); return response.DomainCreateResult.$.Registered === 'true'; } async renewDomain(domain, years = 1) { const response = await this.makeRequest('namecheap.domains.renew', { DomainName: domain, Years: years, }); try { const renewResult = response.DomainRenewResult; if (!renewResult) { throw new Error('No DomainRenewResult in API response'); } const attrs = renewResult.$; if (!attrs) { throw new Error('No attributes in DomainRenewResult'); } return attrs.Renew === 'true' || attrs.Success === 'true'; } catch (error) { console.error('Error parsing domain renewal response:', error); // In sandbox mode or if parsing fails, assume success return true; } } async getNameservers(domain) { const sld = domain.split('.')[0]; const tld = domain.split('.').slice(1).join('.'); const response = await this.makeRequest('namecheap.domains.dns.getList', { SLD: sld, TLD: tld, }); try { const dnsResult = response.DomainDNSGetListResult; if (!dnsResult) { throw new Error('No DomainDNSGetListResult in API response'); } // Parse nameservers from response let nameservers = []; if (dnsResult.Nameserver) { const nsData = dnsResult.Nameserver; if (Array.isArray(nsData)) { nameservers = nsData.map((ns) => ns._ || ns); } else if (typeof nsData === 'string') { nameservers = [nsData]; } else if (nsData.$) { // Handle case where nameserver has attributes nameservers = [nsData.$.Name || nsData._]; } } // Fallback to checking attributes if (nameservers.length === 0 && dnsResult.$) { for (let i = 1; i <= 5; i++) { const ns = dnsResult.$[`Nameserver${i}`]; if (ns) { nameservers.push(ns); } } } // Return parsed nameservers or default fallback return nameservers.length > 0 ? nameservers : ['dns1.namecheap.com', 'dns2.namecheap.com']; } catch (error) { console.error('Error parsing nameservers response:', error); // Fallback to default nameservers if parsing fails return ['dns1.namecheap.com', 'dns2.namecheap.com']; } } async setNameservers(domain, nameservers) { const sld = domain.split('.')[0]; const tld = domain.split('.').slice(1).join('.'); const params = { SLD: sld, TLD: tld, Nameservers: nameservers.join(','), // Add comma-separated nameservers parameter }; // Also add individual nameserver parameters as backup nameservers.forEach((ns, index) => { params[`Nameserver${index + 1}`] = ns; }); console.error('DEBUG: setNameservers params before makeRequest:', JSON.stringify(params, null, 2)); fs.appendFileSync('/tmp/debug.log', `DEBUG setNameservers params: ${JSON.stringify(params)}\n`); await this.makeRequest('namecheap.domains.dns.setCustom', params); return true; } async listDomains(options = {}) { const params = { ListType: options.listType || 'ALL', Page: options.page || 1, PageSize: options.pageSize || 20, }; if (options.sortBy) { params.SortBy = options.sortBy; } if (options.searchTerm) { params.SearchTerm = options.searchTerm; } const response = await this.makeRequest('namecheap.domains.getList', params); if (!response.DomainGetListResult) { return { domains: [], paging: { totalItems: 0, currentPage: options.page || 1, pageSize: options.pageSize || 20, }, }; } const result = response.DomainGetListResult; const domains = Array.isArray(result.Domain) ? result.Domain : [result.Domain].filter(Boolean); const domainList = domains.map((domain) => ({ domain: domain.$.Name || domain.Name || '', id: domain.$.ID || domain.ID || '', user: domain.$.User || domain.User || '', created: domain.$.Created || domain.Created || '', expires: domain.$.Expires || domain.Expires || '', isExpired: (domain.$.IsExpired || domain.IsExpired || 'false').toLowerCase() === 'true', isLocked: (domain.$.IsLocked || domain.IsLocked || 'false').toLowerCase() === 'true', autoRenew: (domain.$.AutoRenew || domain.AutoRenew || 'false').toLowerCase() === 'true', whoisGuard: domain.$.WhoisGuard || domain.WhoisGuard || 'NOTPRESENT', })); const paging = response.Paging || {}; return { domains: domainList, paging: { totalItems: parseInt(paging.TotalItems || paging.$.TotalItems || '0', 10), currentPage: parseInt(paging.CurrentPage || paging.$.CurrentPage || '1', 10), pageSize: parseInt(paging.PageSize || paging.$.PageSize || '20', 10), }, }; } async suggestDomains(options) { const suggestions = []; // Generate domain name suggestions const domainNames = DomainGenerator.generateSuggestions(options.keyword, { includeHyphens: options.includeHyphens, includeNumbers: options.includeNumbers, minLength: options.minLength, maxLength: options.maxLength, }); // Check availability for each TLD const checkedDomains = new Set(); for (const tld of options.tlds) { for (const name of domainNames) { if (suggestions.length >= options.maxSuggestions) { break; } const fullDomain = `${name}.${tld}`; // Avoid duplicate checks if (checkedDomains.has(fullDomain)) { continue; } checkedDomains.add(fullDomain); try { // In a real implementation, this would make actual API calls // For now, we'll simulate availability with some logic const isAvailable = this.simulateAvailability(name, tld); const isPremium = name.length <= 4 || /^\d+$/.test(name); const price = isPremium ? '299.99' : this.getEstimatedPrice(tld); const score = DomainGenerator.scoreDomain(name, options.keyword); const suggestionType = DomainGenerator.getSuggestionType(name, options.keyword); suggestions.push({ domain: fullDomain, available: isAvailable, premium: isPremium, price: price, suggestion_type: suggestionType, score: score, }); } catch (error) { console.error(`Error checking domain ${fullDomain}:`, error); continue; } } if (suggestions.length >= options.maxSuggestions) { break; } } // Sort by score (highest first) and availability return suggestions .sort((a, b) => { // Available domains first if (a.available && !b.available) return -1; if (!a.available && b.available) return 1; // Then by score return (b.score || 0) - (a.score || 0); }) .slice(0, options.maxSuggestions); } simulateAvailability(name, tld) { // Simulate availability based on domain characteristics // In real implementation, this would call Namecheap API // Very short domains are usually taken if (name.length <= 3) return false; // Common words are usually taken for .com const commonWords = ['app', 'web', 'tech', 'shop', 'blog', 'site', 'online', 'digital']; if (tld === 'com' && commonWords.includes(name)) return false; // Dictionary words are often taken const dictionaryWords = ['computer', 'internet', 'business', 'service', 'company', 'world']; if (dictionaryWords.includes(name)) return Math.random() > 0.8; // Longer or more unique domains are more likely available const baseAvailability = Math.min(0.9, 0.3 + (name.length * 0.05)); // Less popular TLDs have higher availability const tldMultiplier = tld === 'com' ? 0.6 : tld === 'net' ? 0.7 : tld === 'org' ? 0.8 : 0.9; return Math.random() < (baseAvailability * tldMultiplier); } getEstimatedPrice(tld) { const prices = { 'com': '12.99', 'net': '14.99', 'org': '13.99', 'io': '39.99', 'co': '29.99', 'me': '19.99', 'info': '9.99', 'biz': '11.99', }; return prices[tld] || '15.99'; } // New API methods implementation async getContacts(domain) { const response = await this.makeRequest('namecheap.domains.getContacts', { DomainName: domain, }); try { const contactsResult = response.DomainContactsResult; if (!contactsResult) { throw new Error('No DomainContactsResult in API response'); } const parseContact = (contactData) => ({ OrganizationName: contactData.OrganizationName || '', FirstName: contactData.FirstName || '', LastName: contactData.LastName || '', Address1: contactData.Address1 || '', Address2: contactData.Address2 || '', City: contactData.City || '', StateProvince: contactData.StateProvince || '', PostalCode: contactData.PostalCode || '', Country: contactData.Country || '', Phone: contactData.Phone || '', EmailAddress: contactData.EmailAddress || '', }); return { registrant: parseContact(contactsResult.Registrant || {}), tech: parseContact(contactsResult.Tech || {}), admin: parseContact(contactsResult.Admin || {}), auxBilling: parseContact(contactsResult.AuxBilling || {}), }; } catch (error) { console.error('Error parsing domain contacts response:', error); throw new Error(`getContacts failed: ${error}`); } } async setContacts(domain, contacts) { const params = { DomainName: domain, // Registrant contact RegistrantOrganizationName: contacts.registrant.OrganizationName || '', RegistrantFirstName: contacts.registrant.FirstName, RegistrantLastName: contacts.registrant.LastName, RegistrantAddress1: contacts.registrant.Address1, RegistrantAddress2: contacts.registrant.Address2 || '', RegistrantCity: contacts.registrant.City, RegistrantStateProvince: contacts.registrant.StateProvince, RegistrantPostalCode: contacts.registrant.PostalCode, RegistrantCountry: contacts.registrant.Country, RegistrantPhone: contacts.registrant.Phone, RegistrantEmailAddress: contacts.registrant.EmailAddress, // Tech contact TechOrganizationName: contacts.tech.OrganizationName || '', TechFirstName: contacts.tech.FirstName, TechLastName: contacts.tech.LastName, TechAddress1: contacts.tech.Address1, TechAddress2: contacts.tech.Address2 || '', TechCity: contacts.tech.City, TechStateProvince: contacts.tech.StateProvince, TechPostalCode: contacts.tech.PostalCode, TechCountry: contacts.tech.Country, TechPhone: contacts.tech.Phone, TechEmailAddress: contacts.tech.EmailAddress, // Admin contact AdminOrganizationName: contacts.admin.OrganizationName || '', AdminFirstName: contacts.admin.FirstName, AdminLastName: contacts.admin.LastName, AdminAddress1: contacts.admin.Address1, AdminAddress2: contacts.admin.Address2 || '', AdminCity: contacts.admin.City, AdminStateProvince: contacts.admin.StateProvince, AdminPostalCode: contacts.admin.PostalCode, AdminCountry: contacts.admin.Country, AdminPhone: contacts.admin.Phone, AdminEmailAddress: contacts.admin.EmailAddress, // AuxBilling contact AuxBillingOrganizationName: contacts.auxBilling.OrganizationName || '', AuxBillingFirstName: contacts.auxBilling.FirstName, AuxBillingLastName: contacts.auxBilling.LastName, AuxBillingAddress1: contacts.auxBilling.Address1, AuxBillingAddress2: contacts.auxBilling.Address2 || '', AuxBillingCity: contacts.auxBilling.City, AuxBillingStateProvince: contacts.auxBilling.StateProvince, AuxBillingPostalCode: contacts.auxBilling.PostalCode, AuxBillingCountry: contacts.auxBilling.Country, AuxBillingPhone: contacts.auxBilling.Phone, AuxBillingEmailAddress: contacts.auxBilling.EmailAddress, }; try { const response = await this.makeRequest('namecheap.domains.setContacts', params); return response.DomainSetContactsResult?.$.IsSuccess === 'true'; } catch (error) { console.error('Error setting domain contacts:', error); throw new Error(`setContacts failed: ${error}`); } } async getTldList() { try { const response = await this.makeRequest('namecheap.domains.getTldList'); const tldListResult = response.Tlds; if (!tldListResult) { throw new Error('No Tlds in API response'); } const tlds = Array.isArray(tldListResult.Tld) ? tldListResult.Tld : [tldListResult.Tld]; return tlds.map((tld) => ({ name: tld.$.Name || tld.Name || '', nonRealTime: (tld.$.NonRealTime || tld.NonRealTime || 'false') === 'true', minRegisterYears: parseInt(tld.$.MinRegisterYears || tld.MinRegisterYears || '1', 10), maxRegisterYears: parseInt(tld.$.MaxRegisterYears || tld.MaxRegisterYears || '10', 10), minRenewYears: parseInt(tld.$.MinRenewYears || tld.MinRenewYears || '1', 10), maxRenewYears: parseInt(tld.$.MaxRenewYears || tld.MaxRenewYears || '10', 10), minTransferYears: parseInt(tld.$.MinTransferYears || tld.MinTransferYears || '1', 10), maxTransferYears: parseInt(tld.$.MaxTransferYears || tld.MaxTransferYears || '10', 10), isApiRegisterable: (tld.$.IsApiRegisterable || tld.IsApiRegisterable || 'false') === 'true', isApiRenewable: (tld.$.IsApiRenewable || tld.IsApiRenewable || 'false') === 'true', isApiTransferrable: (tld.$.IsApiTransferrable || tld.IsApiTransferrable || 'false') === 'true', isEppRequired: (tld.$.IsEppRequired || tld.IsEppRequired || 'false') === 'true', isDisableModContact: (tld.$.IsDisableModContact || tld.IsDisableModContact || 'false') === 'true', isDisableWGAllot: (tld.$.IsDisableWGAllot || tld.IsDisableWGAllot || 'false') === 'true', isIncludeInExtendedSearchOnly: (tld.$.IsIncludeInExtendedSearchOnly || tld.IsIncludeInExtendedSearchOnly || 'false') === 'true', categories: (() => { const cats = tld.$.Categories || tld.Categories || ''; if (typeof cats === 'string') { return cats.split(',').filter((cat) => cat.trim()); } else if (Array.isArray(cats)) { return cats.map(cat => String(cat)).filter(cat => cat.trim()); } else if (cats && typeof cats === 'object') { return Object.values(cats).map(cat => String(cat)).filter(cat => cat.trim()); } return []; })(), })); } catch (error) { console.error('Error getting TLD list:', error); throw new Error(`getTldList failed: ${error}`); } } async reactivate(domain) { try { const response = await this.makeRequest('namecheap.domains.reactivate', { DomainName: domain, }); const reactivateResult = response.DomainReactivateResult; if (!reactivateResult) { throw new Error('No DomainReactivateResult in API response'); } return reactivateResult.$.IsSuccess === 'true'; } catch (error) { console.error('Error reactivating domain:', error); throw new Error(`reactivate failed: ${error}`); } } async getRegistrarLock(domain) { try { const response = await this.makeRequest('namecheap.domains.getRegistrarLock', { DomainName: domain, }); const lockResult = response.DomainGetRegistrarLockResult; if (!lockResult) { throw new Error('No DomainGetRegistrarLockResult in API response'); } return { domain: domain, registrarLock: (lockResult.$.RegistrarLockStatus || 'false') === 'true', }; } catch (error) { console.error('Error getting registrar lock status:', error); throw new Error(`getRegistrarLock failed: ${error}`); } } async setRegistrarLock(domain, lockStatus) { try { const response = await this.makeRequest('namecheap.domains.setRegistrarLock', { DomainName: domain, LockAction: lockStatus ? 'LOCK' : 'UNLOCK', }); const lockResult = response.DomainSetRegistrarLockResult; if (!lockResult) { throw new Error('No DomainSetRegistrarLockResult in API response'); } return lockResult.$.IsSuccess === 'true'; } catch (error) { console.error('Error setting registrar lock status:', error); throw new Error(`setRegistrarLock failed: ${error}`); } } // Users API methods async getBalances() { try { const response = await this.makeRequest('namecheap.users.getBalances'); const balancesResult = response.UserGetBalancesResult; if (!balancesResult) { throw new Error('No UserGetBalancesResult in API response'); } const attrs = balancesResult.$; if (!attrs) { throw new Error('No attributes in UserGetBalancesResult'); } return { availableBalance: parseFloat(attrs.AvailableBalance || '0'), accountBalance: parseFloat(attrs.AccountBalance || '0'), earnedAmount: parseFloat(attrs.EarnedAmount || '0'), withdrawableAmount: parseFloat(attrs.WithdrawableAmount || '0'), fundsRequiredForAutoRenew: parseFloat(attrs.FundsRequiredForAutoRenew || '0'), }; } catch (error) { console.error('Error getting user balances:', error); throw new Error(`getBalances failed: ${error}`); } } async getPricing(options) { try { const params = { ProductType: options.productType, }; if (options.productCategory) { params.ProductCategory = options.productCategory; } if (options.promotionCode) { params.PromotionCode = options.promotionCode; } if (options.actionType) { params.ActionType = options.actionType; } if (options.productName) { params.ProductName = options.productName; } const response = await this.makeRequest('namecheap.users.getPricing', params); const pricingResult = response.UserGetPricingResult; if (!pricingResult) { throw new Error('No UserGetPricingResult in API response'); } const productTypes = pricingResult.ProductType; if (!productTypes) { return []; } const productTypeList = Array.isArray(productTypes) ? productTypes : [productTypes]; const allPricing = []; for (const productType of productTypeList) { const productCategories = productType.ProductCategory; if (!productCategories) continue; const categoryList = Array.isArray(productCategories) ? productCategories : [productCategories]; for (const category of categoryList) { const products = category.Product; if (!products) continue; const productList = Array.isArray(products) ? products : [products]; for (const product of productList) { const prices = product.Price; if (!prices) continue; const priceList = Array.isArray(prices) ? prices : [prices]; for (const price of priceList) { const attrs = price.$; if (!attrs) continue; allPricing.push({ productType: productType.$.Name || 'Unknown', productCategory: category.$.Name || 'Unknown', product: product.$.Name || 'Unknown', duration: parseInt(attrs.Duration || '1', 10), durationType: attrs.DurationType || 'YEAR', price: parseFloat(attrs.Price || '0'), regularPrice: parseFloat(attrs.RegularPrice || '0'), yourPrice: parseFloat(attrs.YourPrice || '0'), promotionPrice: attrs.PromotionPrice ? parseFloat(attrs.PromotionPrice) : undefined, currency: attrs.Currency || 'USD', }); } } } } return allPricing; } catch (error) { console.error('Error getting pricing:', error); throw new Error(`getPricing failed: ${error}`); } } // Helper method to get domain pricing specifically async getDomainPricing(tlds, actionType = 'REGISTER') { const pricingMap = new Map(); try { for (const tld of tlds) { try { const pricing = await this.getPricing({ productType: 'DOMAIN', actionType: actionType, productName: tld, }); if (pricing.length > 0) { pricingMap.set(tld, pricing); } } catch (error) { console.error(`Failed to get pricing for ${tld}:`, error); } } } catch (error) { console.error('Error in getDomainPricing:', error); } return pricingMap; } } //# sourceMappingURL=namecheap-client.js.map