pame-core-cli
Version:
PAME.AI Core Operating System CLI - Open Source AI Platform for Agentic Commerce
884 lines ⢠43.2 kB
JavaScript
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import inquirer from 'inquirer';
import * as fs from 'fs';
import * as path from 'path';
import { GoDaddyManager } from '../services/godaddy-manager.js';
import { VercelManager } from '../services/vercel-manager.js';
import { VercelSDKManager } from '../services/vercel-sdk-manager.js';
export const subdomainCommand = new Command('subdomain')
.description('Manage subdomains with integrated Vercel and GoDaddy DNS configuration')
.addCommand(new Command('create')
.description('Create a new subdomain with Vercel project and GoDaddy DNS setup')
.argument('[subdomain]', 'Subdomain name (e.g., "crm" for crm.pame.ai)')
.option('-f, --framework <framework>', 'Project framework', 'nextjs')
.option('-p, --production', 'Deploy to production immediately', false)
.option('--dry-run', 'Show what would be done without making changes', false)
.option('--skip-vercel', 'Skip Vercel project creation', false)
.option('--skip-godaddy', 'Skip GoDaddy DNS configuration', false)
.option('--skip-tests', 'Skip endpoint testing', false)
.option('--use-sdk', 'Use Vercel SDK instead of direct API calls', false)
.action(async (subdomainArg, options) => {
try {
console.log(chalk.blue('š PAME.AI Subdomain Creation Wizard\n'));
if (options.dryRun) {
console.log(chalk.yellow('š DRY RUN MODE - No changes will be made\n'));
}
// Get subdomain name
let subdomain = subdomainArg || options.subdomain;
if (!subdomain) {
const { inputSubdomain } = await inquirer.prompt([{
type: 'input',
name: 'inputSubdomain',
message: 'Enter subdomain name (e.g., "crm" for crm.pame.ai):',
validate: (input) => {
if (!input)
return 'Subdomain name is required';
if (!/^[a-z0-9-]+$/.test(input))
return 'Subdomain can only contain lowercase letters, numbers, and hyphens';
if (input.length > 63)
return 'Subdomain must be 63 characters or less';
return true;
}
}]);
subdomain = inputSubdomain;
}
// Validate subdomain format
if (!subdomain || !/^[a-z0-9-]+$/.test(subdomain)) {
console.error(chalk.red('ā Invalid subdomain format. Use only lowercase letters, numbers, and hyphens.'));
process.exit(1);
}
// Get framework if not specified
let framework = options.framework;
if (!framework) {
const { selectedFramework } = await inquirer.prompt([{
type: 'list',
name: 'selectedFramework',
message: 'Select project framework:',
choices: [
{ name: 'Next.js (Recommended for PAME)', value: 'nextjs' },
{ name: 'React', value: 'react' },
{ name: 'Vue.js', value: 'vue' },
{ name: 'Static Site', value: 'static' }
],
default: 'nextjs'
}]);
framework = selectedFramework;
}
// Confirm creation
if (!options.dryRun) {
const { confirm } = await inquirer.prompt([{
type: 'confirm',
name: 'confirm',
message: `Create ${subdomain}.pame.ai with ${framework} framework?`,
default: true
}]);
if (!confirm) {
console.log(chalk.yellow('Operation cancelled.'));
return;
}
}
// Initialize managers
let godaddyManager = null;
let vercelManager = null;
let vercelSDKManager = null;
if (!options.skipGodaddy) {
try {
godaddyManager = new GoDaddyManager();
if (!options.dryRun) {
await godaddyManager.testConnection();
}
else {
console.log(chalk.green('š [DRY RUN] Would test GoDaddy API connection...'));
}
}
catch (error) {
console.error(chalk.red('ā GoDaddy connection failed:'), error instanceof Error ? error.message : String(error));
if (!options.dryRun)
process.exit(1);
}
}
if (!options.skipVercel) {
try {
if (options.useSdk) {
console.log(chalk.blue('š Using Vercel SDK for enhanced domain management...'));
vercelSDKManager = new VercelSDKManager();
if (!options.dryRun) {
await vercelSDKManager.testConnection();
}
else {
console.log(chalk.green('š [DRY RUN] Would test Vercel SDK connection...'));
}
}
else {
vercelManager = new VercelManager();
if (!options.dryRun) {
await vercelManager.testConnection();
}
else {
console.log(chalk.green('š [DRY RUN] Would test Vercel API connection...'));
}
}
}
catch (error) {
console.error(chalk.red('ā Vercel connection failed:'), error instanceof Error ? error.message : String(error));
if (!options.dryRun)
process.exit(1);
}
}
// Start creation process
const report = {
subdomain,
recommendations: [],
issues: []
};
console.log(chalk.blue(`\nš Creating subdomain: ${subdomain}.pame.ai\n`));
// Step 1: Create Vercel project
if (!options.skipVercel) {
if (options.useSdk && vercelSDKManager) {
// Use SDK for enhanced domain management
await createSubdomainWithSDK(vercelSDKManager, subdomain, framework, options, report);
}
else if (vercelManager) {
// Use traditional HTTP API
await createVercelProject(vercelManager, subdomain, framework, options, report);
}
}
// Step 2: Configure DNS in GoDaddy
if (!options.skipGodaddy && godaddyManager && report.vercelProject) {
await configureDNS(godaddyManager, vercelManager, subdomain, options, report);
}
// Step 3: Verify and test endpoints
if (!options.skipTests) {
await testEndpoints(vercelManager, subdomain, options, report);
}
// Generate final report
await generateReport(report, options);
}
catch (error) {
console.error(chalk.red('\nā Subdomain creation failed:'), error instanceof Error ? error.message : String(error));
process.exit(1);
}
}))
.addCommand(new Command('list')
.description('List all existing subdomains and their status')
.option('--godaddy-only', 'Show only GoDaddy DNS records')
.option('--vercel-only', 'Show only Vercel projects')
.action(async (options) => {
try {
console.log(chalk.blue('š Current Subdomains for pame.ai\n'));
if (!options.vercelOnly) {
const godaddyManager = new GoDaddyManager();
const subdomains = await godaddyManager.listSubdomains();
console.log(chalk.cyan('š GoDaddy DNS Records:'));
if (subdomains.length === 0) {
console.log(chalk.gray(' No subdomains found'));
}
else {
subdomains.forEach(sub => {
console.log(chalk.white(` ${sub.subdomain}.pame.ai`) + chalk.gray(` ā ${sub.target} (TTL: ${sub.ttl}s)`));
});
}
console.log();
}
if (!options.godaddyOnly) {
const vercelManager = new VercelManager();
const projects = await vercelManager.listProjects();
console.log(chalk.cyan('ā² Vercel Projects:'));
if (projects.length === 0) {
console.log(chalk.gray(' No projects found'));
}
else {
projects.forEach(project => {
const domain = project.targets?.production?.domain || 'No domain';
console.log(chalk.white(` ${project.name}`) + chalk.gray(` ā ${domain} (${project.framework})`));
});
}
}
}
catch (error) {
console.error(chalk.red('ā Failed to list subdomains:'), error instanceof Error ? error.message : String(error));
process.exit(1);
}
}))
.addCommand(new Command('status')
.description('Check status of DNS and Vercel configuration')
.option('-s, --subdomain <subdomain>', 'Check specific subdomain')
.action(async (options) => {
try {
console.log(chalk.blue('š Checking DNS and Vercel Status\n'));
const godaddyManager = new GoDaddyManager();
const vercelManager = new VercelManager();
if (options.subdomain) {
// Check specific subdomain
await checkSubdomainStatus(godaddyManager, vercelManager, options.subdomain);
}
else {
// Overall status
const dnsStatus = await godaddyManager.getDNSStatus();
console.log(chalk.cyan('š Overall Status:'));
console.log(chalk.white(` Total DNS Records: ${dnsStatus.totalRecords}`));
console.log(chalk.white(` Active Subdomains: ${dnsStatus.subdomains}`));
console.log(chalk.white(` TXT Records: ${dnsStatus.txtRecords}`));
console.log(chalk.white(` Vercel Records: ${dnsStatus.vercelRecords}`));
const projects = await vercelManager.listProjects();
console.log(chalk.white(` Vercel Projects: ${projects.length}`));
}
}
catch (error) {
console.error(chalk.red('ā Status check failed:'), error instanceof Error ? error.message : String(error));
process.exit(1);
}
}))
.addCommand(new Command('test')
.description('Test subdomain connectivity and performance')
.argument('<subdomain>', 'Subdomain to test')
.action(async (subdomain) => {
try {
console.log(chalk.blue(`š Testing ${subdomain}.pame.ai\n`));
const vercelManager = new VercelManager();
// Test custom domain
const customDomainResult = await vercelManager.testEndpoint(`https://${subdomain}.pame.ai`);
console.log(chalk.cyan('š Custom Domain Test:'));
if (customDomainResult.success) {
console.log(chalk.green(` ā
${subdomain}.pame.ai responding (${customDomainResult.responseTime}ms)`));
}
else {
console.log(chalk.red(` ā ${subdomain}.pame.ai failed: ${customDomainResult.error}`));
}
// Performance recommendations
if (customDomainResult.responseTime > 2000) {
console.log(chalk.yellow('ā ļø Slow response time detected. Consider optimizing your application.'));
}
}
catch (error) {
console.error(chalk.red('ā Test failed:'), error instanceof Error ? error.message : String(error));
process.exit(1);
}
}))
.addCommand(new Command('update')
.description('Update DNS records for an existing subdomain')
.argument('<subdomain>', 'Subdomain to update (e.g., "gcp" for gcp.pame.ai)')
.option('--target <target>', 'New CNAME target (e.g., new-deployment.vercel.app)')
.option('--add-domain', 'Add domain to Vercel project using SDK', false)
.option('--add-txt <value>', 'Add TXT verification record')
.option('--dry-run', 'Show what would be done without making changes', false)
.action(async (subdomain, options) => {
try {
console.log(chalk.blue(`š Updating DNS for ${subdomain}.pame.ai\n`));
if (options.dryRun) {
console.log(chalk.yellow('š DRY RUN MODE - No changes will be made\n'));
}
// Initialize managers
const godaddyManager = new GoDaddyManager();
let vercelSDKManager = null;
if (options.addDomain) {
console.log(chalk.blue('š Using Vercel SDK for domain management...'));
vercelSDKManager = new VercelSDKManager();
await vercelSDKManager.testConnection();
}
// Test GoDaddy connection
console.log(chalk.blue('š Testing GoDaddy API connection...'));
const connected = await godaddyManager.testConnection();
if (!connected) {
console.error(chalk.red('ā Failed to connect to GoDaddy API'));
process.exit(1);
}
// Add domain to Vercel project if requested
if (options.addDomain && vercelSDKManager) {
const projectName = `pame-${subdomain}`;
const domain = `${subdomain}.pame.ai`;
console.log(chalk.blue(`š Adding domain ${domain} to Vercel project ${projectName}...`));
try {
// Check current domains first
const currentDomains = await vercelSDKManager.getProjectDomains(projectName);
const existingDomain = currentDomains.find(d => d.name === domain);
if (existingDomain) {
console.log(chalk.yellow(`ā ļø Domain ${domain} already exists in project`));
console.log(chalk.gray(` Verified: ${existingDomain.verified ? 'Yes' : 'No'}`));
if (!existingDomain.verified) {
console.log(chalk.blue('š” Domain needs verification. Get TXT record from Vercel dashboard.'));
}
}
else {
if (!options.dryRun) {
const domainResult = await vercelSDKManager.addDomainToProject(projectName, domain);
console.log(chalk.green(`ā
Domain added: ${domainResult.name}`));
}
else {
console.log(chalk.blue(`š [DRY RUN] Would add domain ${domain} to project ${projectName}`));
}
}
// Get updated domain status
const updatedDomains = await vercelSDKManager.getProjectDomains(projectName);
const targetDomain = updatedDomains.find(d => d.name === domain);
if (targetDomain) {
console.log(chalk.cyan('\nš Vercel Domain Status:'));
console.log(chalk.white(` Domain: ${targetDomain.name}`));
console.log(chalk.white(` Verified: ${targetDomain.verified ? 'ā
Yes' : 'ā ļø No'}`));
if (!targetDomain.verified) {
console.log(chalk.blue('\nš” To complete verification:'));
console.log(' 1. Go to Vercel dashboard ā pame-gcp ā Domains');
console.log(' 2. Copy the TXT verification record');
console.log(' 3. Add it using: --add-txt "verification-value"');
}
}
}
catch (error) {
if (error instanceof Error && error.message.includes('already exists')) {
console.log(chalk.yellow('ā ļø Domain already exists in Vercel project'));
}
else {
console.error(chalk.red('ā Failed to add domain to Vercel:'), error instanceof Error ? error.message : String(error));
}
}
}
// Add TXT verification record if provided
if (options.addTxt) {
console.log(chalk.blue('š Adding TXT verification record...'));
const txtRecord = [{
type: 'TXT',
name: '_vercel',
data: options.addTxt,
ttl: 3600
}];
if (!options.dryRun) {
await godaddyManager.updateRecords('TXT', '_vercel', txtRecord);
console.log(chalk.green(`ā
TXT record added: _vercel.pame.ai ā "${options.addTxt}"`));
}
else {
console.log(chalk.blue(`š [DRY RUN] Would add TXT record: _vercel.pame.ai ā "${options.addTxt}"`));
}
}
// Check current CNAME record
console.log(chalk.blue('š Checking current CNAME record...'));
const currentCNAME = await godaddyManager.getRecords('CNAME', subdomain);
if (currentCNAME.length === 0) {
console.log(chalk.red(`ā No CNAME record found for ${subdomain}.pame.ai`));
console.log(chalk.yellow('š” Use "pame-core subdomain create" to create a new subdomain'));
process.exit(1);
}
const currentTarget = currentCNAME[0].data;
console.log(chalk.gray(` Current: ${subdomain}.pame.ai ā ${currentTarget}`));
// Update CNAME if target provided
if (options.target) {
const newTarget = options.target;
console.log(chalk.gray(` Target: ${subdomain}.pame.ai ā ${newTarget}`));
// Check if update is needed
if (currentTarget === newTarget) {
console.log(chalk.green('ā
CNAME record already points to the correct target!'));
}
else {
// Confirm update
if (!options.dryRun) {
const { confirm } = await inquirer.prompt([{
type: 'confirm',
name: 'confirm',
message: `Update ${subdomain}.pame.ai from ${currentTarget} to ${newTarget}?`,
default: true
}]);
if (!confirm) {
console.log(chalk.yellow('Update cancelled.'));
return;
}
}
// Update the CNAME record
if (options.dryRun) {
console.log(chalk.blue(`š [DRY RUN] Would update CNAME record:`));
console.log(chalk.gray(` ${subdomain}.pame.ai ā ${newTarget}`));
}
else {
console.log(chalk.blue(`š Updating CNAME record...`));
const newRecord = [{
type: 'CNAME',
name: subdomain,
data: newTarget,
ttl: 3600
}];
await godaddyManager.updateRecords('CNAME', subdomain, newRecord);
// Verify the update
console.log(chalk.blue('š Verifying update...'));
const updatedCNAME = await godaddyManager.getRecords('CNAME', subdomain);
if (updatedCNAME.length > 0 && updatedCNAME[0].data === newTarget) {
console.log(chalk.green('ā
CNAME record updated successfully!'));
console.log(chalk.gray(` ${subdomain}.pame.ai ā ${newTarget} (TTL: ${updatedCNAME[0].ttl}s)`));
}
else {
console.log(chalk.red('ā CNAME record update verification failed'));
process.exit(1);
}
}
}
}
// Check TXT verification record status
console.log(chalk.blue('\nš Checking TXT verification record...'));
const txtRecords = await godaddyManager.getRecords('TXT', '_vercel');
const subdomainTxtRecord = txtRecords.find(record => record.data.includes(`${subdomain}.pame.ai`));
if (subdomainTxtRecord) {
console.log(chalk.green('ā
TXT verification record found'));
console.log(chalk.gray(` _vercel.pame.ai ā "${subdomainTxtRecord.data}"`));
}
else {
console.log(chalk.yellow('ā ļø No TXT verification record found'));
if (!options.addDomain) {
console.log(chalk.blue('\nš” To add domain verification:'));
console.log(` pame-core subdomain update ${subdomain} --add-domain`);
}
}
// Final summary
console.log(chalk.green('\nā
DNS update completed!'));
console.log(chalk.blue('\nš Summary:'));
if (options.target) {
if (options.dryRun) {
console.log(` ⢠Would update: ${subdomain}.pame.ai ā ${options.target}`);
}
else {
console.log(` ⢠Updated: ${subdomain}.pame.ai ā ${options.target} ā
`);
}
}
if (options.addDomain) {
console.log(` ⢠Vercel domain: ${options.dryRun ? 'Would add' : 'Added'} ā
`);
}
if (options.addTxt) {
console.log(` ⢠TXT record: ${options.dryRun ? 'Would add' : 'Added'} ā
`);
}
console.log(` ⢠TXT verification: ${subdomainTxtRecord ? 'ā
Found' : 'ā ļø May be needed'}`);
console.log(' ⢠DNS propagation: up to 24 hours');
console.log('\nš Test URLs:');
const target = options.target || currentTarget;
console.log(` ⢠Direct: https://${target}`);
console.log(` ⢠Custom: https://${subdomain}.pame.ai (after propagation)`);
}
catch (error) {
console.error(chalk.red('\nā Update failed:'), error instanceof Error ? error.message : String(error));
process.exit(1);
}
}))
.addCommand(new Command('verify')
.description('Get verification details for a subdomain')
.argument('<subdomain>', 'Subdomain to check verification for (e.g., "gcp" for gcp.pame.ai)')
.action(async (subdomain) => {
try {
console.log(chalk.blue(`š Getting verification details for ${subdomain}.pame.ai\n`));
// Initialize Vercel SDK Manager
const vercelSDKManager = new VercelSDKManager();
await vercelSDKManager.testConnection();
const projectName = `pame-${subdomain}`;
const domain = `${subdomain}.pame.ai`;
// Get project domains with detailed verification info
console.log(chalk.blue(`š Checking project ${projectName}...`));
const domains = await vercelSDKManager.getProjectDomains(projectName);
const targetDomain = domains.find(d => d.name === domain);
if (!targetDomain) {
console.log(chalk.red(`ā Domain ${domain} not found in project ${projectName}`));
console.log(chalk.yellow('š” Available domains:'));
domains.forEach(d => console.log(chalk.gray(` ${d.name}`)));
process.exit(1);
}
console.log(chalk.cyan('\nš Domain Verification Details:'));
console.log(chalk.white(` Domain: ${targetDomain.name}`));
console.log(chalk.white(` Verified: ${targetDomain.verified ? 'ā
Yes' : 'ā ļø No'}`));
console.log(chalk.white(` Created: ${targetDomain.createdAt}`));
if (targetDomain.verification && targetDomain.verification.length > 0) {
console.log(chalk.cyan('\nš Verification Requirements:'));
targetDomain.verification.forEach((req, i) => {
console.log(chalk.white(` ${i + 1}. Type: ${req.type}`));
console.log(chalk.white(` Domain: ${req.domain}`));
console.log(chalk.white(` Value: ${req.value}`));
console.log(chalk.white(` Reason: ${req.reason || 'Domain verification'}`));
if (req.type === 'TXT') {
console.log(chalk.yellow('\nšÆ TXT Record Required:'));
console.log(chalk.white(` Host: ${req.domain}`));
console.log(chalk.white(` Value: ${req.value}`));
console.log(chalk.green(`\nš Add with this command:`));
console.log(chalk.cyan(` npx pame-core-cli subdomain update ${subdomain} --add-txt "${req.value}"`));
}
});
}
else if (!targetDomain.verified) {
console.log(chalk.yellow('\nā ļø Domain not verified but no verification requirements found'));
console.log(chalk.blue('š” Check the Vercel dashboard for manual verification steps:'));
console.log(chalk.cyan(` https://vercel.com/getaifactory/${projectName}/domains`));
}
else {
console.log(chalk.green('\nā
Domain is fully verified!'));
}
}
catch (error) {
console.error(chalk.red('\nā Verification check failed:'), error instanceof Error ? error.message : String(error));
process.exit(1);
}
}));
// Helper function to create subdomain using Vercel SDK
async function createSubdomainWithSDK(vercelSDKManager, subdomain, framework, options, report) {
const spinner = ora('Creating subdomain with Vercel SDK...').start();
try {
if (options.dryRun) {
spinner.stop();
console.log(chalk.blue(`š [DRY RUN] Would create subdomain: ${subdomain}.pame.ai using SDK`));
console.log(chalk.gray(` - Framework: ${framework}`));
console.log(chalk.gray(` - Enhanced features: Domain verification, status monitoring`));
// Mock report data for dry run
report.vercelProject = {
id: 'mock-sdk-project-id',
name: `pame-${subdomain}`,
url: `https://pame-${subdomain}.vercel.app`,
domain: `${subdomain}.pame.ai`
};
return;
}
const result = await vercelSDKManager.createSubdomainWithSDK({
subdomain,
framework,
envVars: {
NEXT_PUBLIC_SUBDOMAIN: subdomain,
NEXT_PUBLIC_DOMAIN: `${subdomain}.pame.ai`,
}
});
spinner.stop();
if (result.success && result.project) {
console.log(chalk.green(`ā
SDK subdomain creation successful!`));
console.log(chalk.gray(` Project: ${result.project.name} (${result.project.id})`));
console.log(chalk.gray(` Domain: ${result.domain?.name || `${subdomain}.pame.ai`}`));
console.log(chalk.gray(` Domains found: ${result.domains?.length || 0}`));
report.vercelProject = {
id: result.project.id,
name: result.project.name,
url: `https://${result.project.name}.vercel.app`,
domain: `${subdomain}.pame.ai`
};
if (!report.recommendations)
report.recommendations = [];
report.recommendations.push('SDK-based creation provides enhanced domain management features');
}
else {
throw new Error(result.error || 'SDK subdomain creation failed');
}
}
catch (error) {
spinner.stop();
console.error(chalk.red('ā SDK subdomain creation failed:'), error instanceof Error ? error.message : String(error));
if (!report.issues)
report.issues = [];
report.issues.push(`SDK creation failed: ${error instanceof Error ? error.message : String(error)}`);
throw error;
}
}
async function createVercelProject(vercelManager, subdomain, framework, options, report) {
const spinner = ora('Creating Vercel project...').start();
try {
const projectName = `pame-${subdomain}`;
const domain = `${subdomain}.pame.ai`;
// Handle dry-run mode first, before any API calls
if (options.dryRun) {
spinner.stop();
console.log(chalk.blue(`š [DRY RUN] Would create Vercel project: ${projectName}`));
console.log(chalk.gray(` - Framework: ${framework}`));
console.log(chalk.gray(` - Domain: ${domain}`));
console.log(chalk.gray(` - Environment: NEXT_PUBLIC_PLATFORM=${subdomain}`));
// Mock the report data for dry run
report.vercelProject = {
id: 'mock-project-id',
name: projectName,
url: `https://${projectName}.vercel.app`,
domain: domain
};
// Mock DNS configuration for subsequent dry-run steps
report.godaddyDNS = {
cname: `${projectName}.vercel.app`,
txtRecord: 'mock-txt-verification-record',
verified: false
};
spinner.succeed(`ā
[DRY RUN] Would create project: ${projectName}`);
return;
}
// Check if project already exists (only in real mode)
const existingProject = await vercelManager.getProject(projectName);
if (existingProject) {
spinner.stop();
console.log(chalk.yellow(`ā ļø Project ${projectName} already exists. Using existing project.`));
report.vercelProject = {
id: existingProject.id,
name: existingProject.name,
url: existingProject.targets?.production?.url || '',
domain: existingProject.targets?.production?.domain || domain
};
return;
}
// Create project configuration
const projectConfig = {
name: projectName,
subdomain,
framework,
envVars: {
NEXT_PUBLIC_PLATFORM: subdomain,
NEXT_PUBLIC_API_URL: 'https://api.pame.ai'
},
rewrites: [
// Block access to other platform routes
{ source: '/core/(.*)', destination: '/404' },
{ source: '/saas/(.*)', destination: '/404' }
]
};
// Create Vercel project
spinner.text = 'Creating Vercel project...';
const project = await vercelManager.createProject(projectConfig);
// Add custom domain
spinner.text = 'Adding custom domain...';
await vercelManager.addDomain(project.id, domain);
spinner.succeed(`ā
Vercel project created: ${projectName}`);
console.log(chalk.green(`š Domain ${domain} has been added to project!`));
console.log(chalk.blue(`š” Webhook automation will handle DNS verification automatically`));
report.vercelProject = {
id: project.id,
name: project.name,
url: `https://${projectName}.vercel.app`,
domain
};
// Set up DNS configuration for automated webhook processing
// The webhook at api.pame.ai will handle the actual DNS records
report.godaddyDNS = {
cname: `${projectName}.vercel.app`,
txtRecord: `Webhook automation will handle verification`,
verified: false
};
console.log(chalk.gray(` Project ID: ${project.id}`));
console.log(chalk.gray(` Domain: ${domain}`));
console.log(chalk.gray(` Framework: ${framework}`));
console.log(chalk.yellow(` š Note: DNS records will be configured automatically via webhook`));
}
catch (error) {
spinner.fail('ā Failed to create Vercel project');
throw error;
}
}
async function configureDNS(godaddyManager, vercelManager, subdomain, options, report) {
const spinner = ora('Configuring DNS records...').start();
try {
// Check if webhook automation is handling this
if (report.godaddyDNS?.txtRecord === 'Webhook automation will handle verification') {
spinner.stop();
console.log(chalk.blue('š” Webhook automation is handling DNS configuration'));
console.log(chalk.yellow('š DNS records will be created automatically when webhook triggers'));
console.log(chalk.gray(' The webhook at api.pame.ai will process domain events from Vercel'));
console.log(chalk.gray(' and automatically create the required DNS records in GoDaddy'));
report.godaddyDNS.verified = false; // Will be updated by webhook
return;
}
if (!report.godaddyDNS?.cname || !report.godaddyDNS?.txtRecord) {
spinner.fail('ā Missing DNS configuration from Vercel');
throw new Error('DNS configuration not available from Vercel step');
}
if (options.dryRun) {
spinner.stop();
console.log(chalk.blue('š Would configure DNS records:'));
console.log(chalk.gray(` CNAME: ${subdomain} ā ${report.godaddyDNS.cname}`));
console.log(chalk.gray(` TXT: _vercel ā ${report.godaddyDNS.txtRecord}`));
return;
}
const subdomainSetup = {
subdomain,
cname: report.godaddyDNS.cname,
txtRecord: report.godaddyDNS.txtRecord,
vercelDomain: `${subdomain}.pame.ai`
};
spinner.text = 'Adding DNS records to GoDaddy...';
await godaddyManager.createSubdomainSetup(subdomainSetup);
// Wait a bit for DNS propagation
spinner.text = 'Waiting for DNS propagation...';
await new Promise(resolve => setTimeout(resolve, 5000));
// Verify DNS configuration
spinner.text = 'Verifying DNS configuration...';
const verified = await godaddyManager.verifySubdomain(subdomain, report.godaddyDNS.cname, report.godaddyDNS.txtRecord);
if (verified) {
// Try to verify domain in Vercel
spinner.text = 'Verifying domain in Vercel...';
const vercelVerified = await vercelManager.verifyDomain(`${subdomain}.pame.ai`);
report.godaddyDNS.verified = vercelVerified;
}
spinner.succeed('ā
DNS records configured successfully');
console.log(chalk.gray(` CNAME: ${subdomain}.pame.ai ā ${report.godaddyDNS.cname}`));
console.log(chalk.gray(` TXT: _vercel ā ${report.godaddyDNS.txtRecord}`));
console.log(chalk.gray(` Verified: ${report.godaddyDNS.verified ? 'Yes' : 'Pending'}`));
if (!report.godaddyDNS.verified) {
report.recommendations?.push('DNS verification is pending. It may take up to 24 hours for DNS changes to propagate globally.');
}
}
catch (error) {
spinner.fail('ā Failed to configure DNS');
throw error;
}
}
async function testEndpoints(vercelManager, subdomain, options, report) {
const spinner = ora('Testing endpoints...').start();
try {
if (options.dryRun) {
spinner.stop();
console.log(chalk.blue('š Would test endpoints:'));
console.log(chalk.gray(` https://${report.vercelProject?.name}.vercel.app`));
console.log(chalk.gray(` https://${subdomain}.pame.ai`));
return;
}
report.endpoints = {
vercelUrl: { url: '', status: 0, responseTime: 0, success: false },
customDomain: { url: '', status: 0, responseTime: 0, success: false }
};
// Test Vercel URL
if (report.vercelProject?.name) {
spinner.text = 'Testing Vercel URL...';
const vercelUrl = `https://${report.vercelProject.name}.vercel.app`;
const vercelResult = await vercelManager.testEndpoint(vercelUrl);
report.endpoints.vercelUrl = {
url: vercelUrl,
status: vercelResult.status,
responseTime: vercelResult.responseTime,
success: vercelResult.success,
error: vercelResult.error
};
}
// Test custom domain
spinner.text = 'Testing custom domain...';
const customUrl = `https://${subdomain}.pame.ai`;
const customResult = await vercelManager.testEndpoint(customUrl);
report.endpoints.customDomain = {
url: customUrl,
status: customResult.status,
responseTime: customResult.responseTime,
success: customResult.success,
error: customResult.error
};
spinner.succeed('ā
Endpoint testing completed');
// Log results
if (report.endpoints.vercelUrl.success) {
console.log(chalk.green(` ā
Vercel URL: ${report.endpoints.vercelUrl.status} (${report.endpoints.vercelUrl.responseTime}ms)`));
}
else {
console.log(chalk.red(` ā Vercel URL: ${report.endpoints.vercelUrl.error}`));
}
if (report.endpoints.customDomain.success) {
console.log(chalk.green(` ā
Custom Domain: ${report.endpoints.customDomain.status} (${report.endpoints.customDomain.responseTime}ms)`));
}
else {
console.log(chalk.yellow(` ā ļø Custom Domain: ${report.endpoints.customDomain.error} (This is normal for new domains)`));
report.recommendations?.push('Custom domain may take time to become available after DNS propagation.');
}
}
catch (error) {
spinner.fail('ā Endpoint testing failed');
report.issues?.push(`Endpoint testing failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
async function generateReport(report, options) {
console.log(chalk.blue('\nš Subdomain Creation Report\n'));
// Summary
console.log(chalk.cyan('š Summary:'));
console.log(chalk.white(` Subdomain: ${report.subdomain}.pame.ai`));
if (report.vercelProject) {
console.log(chalk.white(` Vercel Project: ${report.vercelProject.name} (${report.vercelProject.id})`));
}
// DNS Status
if (report.godaddyDNS) {
console.log(chalk.cyan('\nš DNS Configuration:'));
console.log(chalk.white(` CNAME Record: ${report.subdomain} ā ${report.godaddyDNS.cname}`));
console.log(chalk.white(` TXT Record: _vercel ā ${report.godaddyDNS.txtRecord}`));
console.log(chalk.white(` Verification: ${report.godaddyDNS.verified ? 'ā
Verified' : 'ā³ Pending'}`));
}
// Endpoint Status
if (report.endpoints) {
console.log(chalk.cyan('\nš Endpoint Status:'));
if (report.endpoints.vercelUrl.url) {
const status = report.endpoints.vercelUrl.success ? 'ā
' : 'ā';
console.log(chalk.white(` Vercel URL: ${status} ${report.endpoints.vercelUrl.url}`));
}
if (report.endpoints.customDomain.url) {
const status = report.endpoints.customDomain.success ? 'ā
' : 'ā ļø';
console.log(chalk.white(` Custom Domain: ${status} ${report.endpoints.customDomain.url}`));
}
}
// Recommendations
if (report.recommendations && report.recommendations.length > 0) {
console.log(chalk.cyan('\nš” Recommendations:'));
report.recommendations.forEach(rec => {
console.log(chalk.yellow(` ⢠${rec}`));
});
}
// Issues
if (report.issues && report.issues.length > 0) {
console.log(chalk.cyan('\nā ļø Issues:'));
report.issues.forEach(issue => {
console.log(chalk.red(` ⢠${issue}`));
});
}
// Next Steps
console.log(chalk.cyan('\nš Next Steps:'));
if (!options.production) {
console.log(chalk.white(' 1. Deploy your application code to the Vercel project'));
console.log(chalk.white(` 2. Run: pame-core deploy ${report.vercelProject?.name} --production`));
}
console.log(chalk.white(' 3. Wait for DNS propagation (up to 24 hours)'));
console.log(chalk.white(` 4. Test your site: https://${report.subdomain}.pame.ai`));
console.log(chalk.white(' 5. Configure SSL certificate (automatic with Vercel)'));
// Save report to file
if (!options.dryRun) {
const reportPath = path.join(process.cwd(), `${report.subdomain}-subdomain-report.json`);
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
console.log(chalk.gray(`\nš Full report saved to: ${reportPath}`));
}
console.log(chalk.green(`\nā
Subdomain ${report.subdomain}.pame.ai setup completed!`));
}
async function checkSubdomainStatus(godaddyManager, vercelManager, subdomain) {
console.log(chalk.cyan(`š Checking status for ${subdomain}.pame.ai\n`));
try {
// Check DNS records
const cnameRecords = await godaddyManager.getRecords('CNAME', subdomain);
const txtRecords = await godaddyManager.getRecords('TXT', '_vercel');
console.log(chalk.cyan('š DNS Records:'));
if (cnameRecords.length > 0) {
cnameRecords.forEach(record => {
console.log(chalk.white(` CNAME: ${record.name} ā ${record.data} (TTL: ${record.ttl}s)`));
});
}
else {
console.log(chalk.red(' ā No CNAME record found'));
}
const relevantTxtRecords = txtRecords.filter(record => record.data.includes(`${subdomain}.pame.ai`));
if (relevantTxtRecords.length > 0) {
relevantTxtRecords.forEach(record => {
console.log(chalk.white(` TXT: ${record.name} ā ${record.data}`));
});
}
else {
console.log(chalk.red(` ā No TXT verification record found for ${subdomain}.pame.ai`));
}
// Check Vercel project
const projectName = `pame-${subdomain}`;
const project = await vercelManager.getProject(projectName);
console.log(chalk.cyan('\nā² Vercel Project:'));
if (project) {
console.log(chalk.white(` Name: ${project.name}`));
console.log(chalk.white(` ID: ${project.id}`));
console.log(chalk.white(` Framework: ${project.framework}`));
if (project.targets?.production) {
console.log(chalk.white(` Production URL: ${project.targets.production.url}`));
console.log(chalk.white(` Production Domain: ${project.targets.production.domain}`));
}
}
else {
console.log(chalk.red(` ā No Vercel project found for ${projectName}`));
}
// Test connectivity
console.log(chalk.cyan('\nš Connectivity Test:'));
const result = await vercelManager.testEndpoint(`https://${subdomain}.pame.ai`);
if (result.success) {
console.log(chalk.green(` ā
${subdomain}.pame.ai is responding (${result.responseTime}ms)`));
}
else {
console.log(chalk.red(` ā ${subdomain}.pame.ai is not responding: ${result.error}`));
}
}
catch (error) {
console.error(chalk.red('ā Status check failed:'), error instanceof Error ? error.message : String(error));
}
}
//# sourceMappingURL=subdomain.js.map