quallaa-cli
Version:
Sets up core infrastructure services for AI-assisted development
256 lines ⢠9.56 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupResend = setupResend;
exports.sendTestEmail = sendTestEmail;
const chalk_1 = __importDefault(require("chalk"));
const inquirer_1 = __importDefault(require("inquirer"));
const axios_1 = __importDefault(require("axios"));
const credentials_1 = require("../storage/credentials");
const RESEND_API_BASE = 'https://api.resend.com';
async function setupResend() {
try {
console.log(chalk_1.default.gray('\nResend provides transactional email infrastructure.'));
console.log(chalk_1.default.gray('You\'ll need a Resend account (free tier: 3,000 emails/month).\n'));
const existing = await (0, credentials_1.getCredentials)('resend');
if (existing) {
const { useExisting } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'useExisting',
message: 'Existing Resend credentials found. Use them?',
default: true,
},
]);
if (useExisting) {
return { success: true, service: 'resend', message: 'Using existing credentials' };
}
}
console.log(chalk_1.default.yellow('\nš Resend API Key Setup'));
console.log(chalk_1.default.gray('1. Go to: https://resend.com/api-keys'));
console.log(chalk_1.default.gray('2. Create a new API key with "Full Access" permission'));
console.log(chalk_1.default.gray('3. Copy the key and paste it below\n'));
const { apiKey } = await inquirer_1.default.prompt([
{
type: 'password',
name: 'apiKey',
message: 'Resend API key:',
validate: (input) => {
if (!input.startsWith('re_')) {
return 'Invalid API key format. Should start with "re_"';
}
return true;
},
},
]);
const isValid = await verifyApiKey(apiKey);
if (!isValid) {
throw new Error('Invalid API key');
}
console.log(chalk_1.default.green('ā API key verified'));
const { setupDomain } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'setupDomain',
message: 'Would you like to set up a custom domain for sending?',
default: false,
},
]);
let domain;
if (setupDomain) {
domain = await configureDomain(apiKey);
}
const credentials = { apiKey, domain };
await (0, credentials_1.saveCredentials)('resend', credentials);
await addToEnvFile(apiKey);
return {
success: true,
service: 'resend',
credentials: { resend: credentials },
};
}
catch (error) {
return {
success: false,
service: 'resend',
message: error instanceof Error ? error.message : 'Setup failed',
error: error,
};
}
}
async function verifyApiKey(apiKey) {
try {
const response = await axios_1.default.get(`${RESEND_API_BASE}/api-keys`, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
return response.status === 200;
}
catch {
return false;
}
}
async function configureDomain(apiKey) {
const { domainChoice } = await inquirer_1.default.prompt([
{
type: 'list',
name: 'domainChoice',
message: 'Domain setup:',
choices: [
{ name: 'Add a new domain', value: 'new' },
{ name: 'Use an existing domain', value: 'existing' },
],
},
]);
if (domainChoice === 'new') {
return await addNewDomain(apiKey);
}
else {
return await selectExistingDomain(apiKey);
}
}
async function addNewDomain(apiKey) {
const { domain } = await inquirer_1.default.prompt([
{
type: 'input',
name: 'domain',
message: 'Enter your domain (e.g., example.com):',
validate: (input) => {
const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/;
if (!domainRegex.test(input)) {
return 'Invalid domain format';
}
return true;
},
},
]);
try {
const response = await axios_1.default.post(`${RESEND_API_BASE}/domains`, { name: domain }, {
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
});
console.log(chalk_1.default.yellow('\nš DNS Records to add:'));
console.log(chalk_1.default.gray('Add these records to your domain\'s DNS settings:\n'));
const records = response.data.records;
records.forEach((record) => {
console.log(chalk_1.default.cyan(`Type: ${record.type}`));
console.log(chalk_1.default.gray(`Name: ${record.name}`));
console.log(chalk_1.default.gray(`Value: ${record.value}`));
console.log(chalk_1.default.gray('---'));
});
console.log(chalk_1.default.yellow('\nAfter adding these records, domain verification may take up to 72 hours.'));
return domain;
}
catch (error) {
console.error(chalk_1.default.red('Failed to add domain'));
throw error;
}
}
async function selectExistingDomain(apiKey) {
try {
const response = await axios_1.default.get(`${RESEND_API_BASE}/domains`, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
const domains = response.data.data.filter((d) => d.status === 'verified');
if (domains.length === 0) {
console.log(chalk_1.default.yellow('No verified domains found. Using default resend.dev domain.'));
return 'resend.dev';
}
const { selectedDomain } = await inquirer_1.default.prompt([
{
type: 'list',
name: 'selectedDomain',
message: 'Select a domain:',
choices: domains.map((d) => ({
name: `${d.name} (${d.status})`,
value: d.name,
})),
},
]);
return selectedDomain;
}
catch {
return 'resend.dev';
}
}
async function addToEnvFile(apiKey) {
const envContent = `
# Resend
RESEND_API_KEY=${apiKey}
`;
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
try {
await fs.access('.env.local');
await fs.appendFile('.env.local', envContent);
}
catch {
await fs.writeFile('.env.local', envContent);
}
console.log(chalk_1.default.green('ā Environment variable added to .env.local'));
}
async function sendTestEmail(to, from) {
const credentials = await (0, credentials_1.getCredentials)('resend');
if (!credentials?.apiKey) {
throw new Error('Resend not configured. Run "quallaa setup resend" first.');
}
const fromEmail = from || `onboarding@${credentials.domain || 'resend.dev'}`;
const response = await axios_1.default.post(`${RESEND_API_BASE}/emails`, {
from: fromEmail,
to: [to],
subject: 'Test Email from Quallaa CLI',
html: `
<h1>Welcome to Quallaa!</h1>
<p>Your email service is configured correctly.</p>
<p>You're ready to build AI-native applications!</p>
`,
}, {
headers: {
Authorization: `Bearer ${credentials.apiKey}`,
'Content-Type': 'application/json',
},
});
if (response.data.id) {
console.log(chalk_1.default.green('ā Test email sent successfully'));
}
}
//# sourceMappingURL=resend.js.map