@mailmodo/cli 0.0.31-beta.pr33.55 → 0.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/billing/index.js +2 -3
- package/dist/commands/deploy/index.js +21 -21
- package/dist/commands/domain/index.d.ts +2 -0
- package/dist/commands/domain/index.js +102 -28
- package/dist/commands/login/index.js +2 -3
- package/dist/commands/preview/index.js +2 -3
- package/dist/commands/settings/index.d.ts +7 -0
- package/dist/commands/settings/index.js +51 -8
- package/dist/lib/base-command.d.ts +0 -26
- package/dist/lib/base-command.js +6 -89
- package/dist/lib/config.d.ts +1 -0
- package/oclif.manifest.json +40 -40
- package/package.json +1 -1
- package/dist/lib/messages.d.ts +0 -36
- package/dist/lib/messages.js +0 -39
|
@@ -3,7 +3,6 @@ import chalk from 'chalk';
|
|
|
3
3
|
import open from 'open';
|
|
4
4
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
5
5
|
import { API_ENDPOINTS } from '../../lib/constants.js';
|
|
6
|
-
import { INFO } from '../../lib/messages.js';
|
|
7
6
|
export default class Billing extends BaseCommand {
|
|
8
7
|
static description = 'View billing status, purchase blocks, set cap, or add a payment method';
|
|
9
8
|
static examples = [
|
|
@@ -69,10 +68,10 @@ export default class Billing extends BaseCommand {
|
|
|
69
68
|
this.log(` ${chalk.dim(checkoutUrl)}\n`);
|
|
70
69
|
try {
|
|
71
70
|
await open(checkoutUrl);
|
|
72
|
-
this.log(`
|
|
71
|
+
this.log(` Opening in browser...\n`);
|
|
73
72
|
}
|
|
74
73
|
catch {
|
|
75
|
-
this.log(` ${
|
|
74
|
+
this.log(` ${chalk.dim('Could not open browser. Visit the URL above manually.')}\n`);
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
77
|
async showStatus(jsonOutput) {
|
|
@@ -2,7 +2,6 @@ import { confirm, input } from '@inquirer/prompts';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
4
4
|
import { API_ENDPOINTS, DEFAULT_BRAND_COLOR, DEFAULT_MONTHLY_CAP, } from '../../lib/constants.js';
|
|
5
|
-
import { ERRORS, INFO, PROMPTS, SEPARATOR, VALIDATION, } from '../../lib/messages.js';
|
|
6
5
|
import { loadTemplate, saveYaml, } from '../../lib/yaml-config.js';
|
|
7
6
|
export default class Deploy extends BaseCommand {
|
|
8
7
|
static description = 'Deploy email sequences and verify sending domain';
|
|
@@ -51,10 +50,10 @@ export default class Deploy extends BaseCommand {
|
|
|
51
50
|
const response = await this.withApiSpinner({ json: flags.json, text: ' Validating sequence...' }, () => this.apiClient.post(API_ENDPOINTS.SEQUENCES_VALIDATE, payload));
|
|
52
51
|
if (!response.ok) {
|
|
53
52
|
if (response.data.error === 'senderDomainNotFound') {
|
|
54
|
-
this.error(
|
|
53
|
+
this.error(`Sending domain not registered. Run: ${chalk.cyan('mailmodo domain')}`);
|
|
55
54
|
}
|
|
56
55
|
if (response.data.error === 'senderDomainNotVerified') {
|
|
57
|
-
this.error(
|
|
56
|
+
this.error(`Sending domain not verified. Run: ${chalk.cyan('mailmodo domain --verify')}`);
|
|
58
57
|
}
|
|
59
58
|
this.handleApiError(response);
|
|
60
59
|
}
|
|
@@ -149,9 +148,10 @@ export default class Deploy extends BaseCommand {
|
|
|
149
148
|
message: 'Set up your sending domain now?',
|
|
150
149
|
});
|
|
151
150
|
if (!setupNow) {
|
|
152
|
-
this.log(`\n ${
|
|
151
|
+
this.log(`\n Sequences saved but ${chalk.yellow('NOT deployed')}.`);
|
|
153
152
|
this.log(` Emails will not send until your domain is verified.`);
|
|
154
|
-
this.log(` ${
|
|
153
|
+
this.log(` When ready, run: ${chalk.cyan('mailmodo domain')}`);
|
|
154
|
+
this.log(` Then: ${chalk.cyan('mailmodo deploy')}\n`);
|
|
155
155
|
return false;
|
|
156
156
|
}
|
|
157
157
|
}
|
|
@@ -194,9 +194,9 @@ export default class Deploy extends BaseCommand {
|
|
|
194
194
|
}
|
|
195
195
|
logDeploySuccessInstructions(sdkSnippet) {
|
|
196
196
|
this.log(` ${chalk.green('Deployed.')} Emails are live.\n`);
|
|
197
|
-
this.log(` ${
|
|
197
|
+
this.log(` ${'─'.repeat(53)}`);
|
|
198
198
|
this.log(` ${chalk.bold('ADD THIS TO YOUR APP (one-time only):')}`);
|
|
199
|
-
this.log(` ${
|
|
199
|
+
this.log(` ${'─'.repeat(53)}\n`);
|
|
200
200
|
this.log(` ${chalk.cyan(sdkSnippet.install ?? 'npm install @mailmodo/sdk')}\n`);
|
|
201
201
|
this.log(` ${chalk.dim("import { track, identify } from '@mailmodo/sdk'")}\n`);
|
|
202
202
|
if (sdkSnippet.examples) {
|
|
@@ -217,7 +217,7 @@ export default class Deploy extends BaseCommand {
|
|
|
217
217
|
if (identifyCalls.length > 0)
|
|
218
218
|
this.log('');
|
|
219
219
|
this.log(` Full SDK docs: ${chalk.cyan('mailmodo.com/docs/sdk')}\n`);
|
|
220
|
-
this.log(` ${
|
|
220
|
+
this.log(` ${'─'.repeat(53)}\n`);
|
|
221
221
|
}
|
|
222
222
|
async runDomainSetup(yamlConfig, flags) {
|
|
223
223
|
const { address, domain, senderEmail } = await this.collectDomainInputs(yamlConfig, flags);
|
|
@@ -242,8 +242,9 @@ export default class Deploy extends BaseCommand {
|
|
|
242
242
|
message: "Press Enter once you've added the records, or 'skip' to do this later.",
|
|
243
243
|
});
|
|
244
244
|
if (action.toLowerCase() === 'skip') {
|
|
245
|
-
this.log(`\n ${
|
|
246
|
-
this.log(` ${
|
|
245
|
+
this.log(`\n Sequences saved but ${chalk.yellow('NOT deployed')}.`);
|
|
246
|
+
this.log(` When ready, run: ${chalk.cyan('mailmodo domain')}`);
|
|
247
|
+
this.log(` Then: ${chalk.cyan('mailmodo deploy')}\n`);
|
|
247
248
|
return false;
|
|
248
249
|
}
|
|
249
250
|
return this.verifyDomain(flags.json, domain);
|
|
@@ -256,20 +257,20 @@ export default class Deploy extends BaseCommand {
|
|
|
256
257
|
senderEmail: yamlConfig.project?.fromEmail || '',
|
|
257
258
|
};
|
|
258
259
|
}
|
|
259
|
-
this.log(`\n ${
|
|
260
|
+
this.log(`\n ${'─'.repeat(53)}`);
|
|
260
261
|
this.log(` ${chalk.bold('DOMAIN SETUP')}`);
|
|
261
|
-
this.log(` ${
|
|
262
|
+
this.log(` ${'─'.repeat(53)}\n`);
|
|
262
263
|
const domain = await input({
|
|
263
|
-
message:
|
|
264
|
-
validate: (v) => (v?.trim() ? true :
|
|
264
|
+
message: 'What domain will you send from?',
|
|
265
|
+
validate: (v) => (v?.trim() ? true : 'Domain is required'),
|
|
265
266
|
});
|
|
266
267
|
const senderEmail = await input({
|
|
267
|
-
message:
|
|
268
|
-
validate: (v) => (v?.includes('@') ? true :
|
|
268
|
+
message: 'Sender email address:',
|
|
269
|
+
validate: (v) => (v?.includes('@') ? true : 'Please enter a valid email'),
|
|
269
270
|
});
|
|
270
271
|
const address = await input({
|
|
271
|
-
message:
|
|
272
|
-
validate: (v) => (v?.trim() ? true :
|
|
272
|
+
message: 'Business address (required by law for email footers):',
|
|
273
|
+
validate: (v) => (v?.trim() ? true : 'Address is required'),
|
|
273
274
|
});
|
|
274
275
|
return { address, domain, senderEmail };
|
|
275
276
|
}
|
|
@@ -283,7 +284,7 @@ export default class Deploy extends BaseCommand {
|
|
|
283
284
|
this.log(` Host: ${record.host}`);
|
|
284
285
|
this.log(` Value: ${record.value}\n`);
|
|
285
286
|
}
|
|
286
|
-
this.log(`
|
|
287
|
+
this.log(` DNS changes take 5–30 minutes to propagate.`);
|
|
287
288
|
if (dnsGuideUrl)
|
|
288
289
|
this.log(` Full guide: ${chalk.cyan(dnsGuideUrl)}\n`);
|
|
289
290
|
}
|
|
@@ -304,8 +305,7 @@ export default class Deploy extends BaseCommand {
|
|
|
304
305
|
this.log(`\n ${chalk.green('Domain verified.')} Continuing deploy...\n`);
|
|
305
306
|
}
|
|
306
307
|
else {
|
|
307
|
-
this.log(`\n ${
|
|
308
|
-
this.log(`\n ${INFO.DNS_FIX_AND_VERIFY}`);
|
|
308
|
+
this.log(`\n ${chalk.yellow('Some records failed.')} Fix them and run ${chalk.cyan('mailmodo domain --verify')}.`);
|
|
309
309
|
if (dnsGuideUrl)
|
|
310
310
|
this.log(` Help: ${chalk.cyan(dnsGuideUrl)}\n`);
|
|
311
311
|
}
|
|
@@ -3,7 +3,8 @@ import { input } from '@inquirer/prompts';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
5
5
|
import { API_ENDPOINTS } from '../../lib/constants.js';
|
|
6
|
-
import {
|
|
6
|
+
import { saveConfig } from '../../lib/config.js';
|
|
7
|
+
import { saveYaml } from '../../lib/yaml-config.js';
|
|
7
8
|
export default class Domain extends BaseCommand {
|
|
8
9
|
static description = 'Set up and verify your sending domain';
|
|
9
10
|
static examples = [
|
|
@@ -24,57 +25,83 @@ export default class Domain extends BaseCommand {
|
|
|
24
25
|
};
|
|
25
26
|
async run() {
|
|
26
27
|
const { flags } = await this.parse(Domain);
|
|
27
|
-
await this.ensureAuth();
|
|
28
|
+
const config = await this.ensureAuth();
|
|
28
29
|
if (flags.verify) {
|
|
29
|
-
|
|
30
|
-
const domain = yamlConfig.project?.domain;
|
|
31
|
-
if (!domain) {
|
|
32
|
-
this.error(ERRORS.DOMAIN_NOT_CONFIGURED);
|
|
33
|
-
}
|
|
34
|
-
await this.verifyDomain(flags.json, domain);
|
|
30
|
+
await this.verifyDomain(flags.json, config);
|
|
35
31
|
return;
|
|
36
32
|
}
|
|
37
33
|
if (flags.status) {
|
|
38
|
-
|
|
39
|
-
const domain = yamlConfig.project?.domain;
|
|
40
|
-
if (!domain) {
|
|
41
|
-
this.error(ERRORS.DOMAIN_NOT_CONFIGURED);
|
|
42
|
-
}
|
|
43
|
-
await this.showDomainStatus(flags.json, domain);
|
|
34
|
+
await this.showDomainStatus(flags.json, config);
|
|
44
35
|
return;
|
|
45
36
|
}
|
|
46
|
-
await this.setupDomain(flags);
|
|
37
|
+
await this.setupDomain(flags, config);
|
|
47
38
|
}
|
|
48
39
|
/**
|
|
49
40
|
* Interactive domain setup: collects domain, sender email, and business address,
|
|
50
41
|
* then calls the API to retrieve the required DNS records.
|
|
51
42
|
*/
|
|
52
|
-
async setupDomain(flags) {
|
|
43
|
+
async setupDomain(flags, config) {
|
|
53
44
|
const yamlConfig = await this.ensureYaml();
|
|
54
|
-
this.log(`\n ${
|
|
45
|
+
this.log(`\n ${'─'.repeat(53)}`);
|
|
55
46
|
this.log(` ${chalk.bold('DOMAIN SETUP')}`);
|
|
56
|
-
this.log(` ${
|
|
57
|
-
const
|
|
58
|
-
const
|
|
47
|
+
this.log(` ${'─'.repeat(53)}\n`);
|
|
48
|
+
const { domain, senderEmail, fromName, replyTo, address } = await this.collectDomainInputs(flags.yes, yamlConfig);
|
|
49
|
+
const apiPayload = {
|
|
50
|
+
address,
|
|
51
|
+
domain,
|
|
52
|
+
fromEmail: senderEmail,
|
|
53
|
+
};
|
|
54
|
+
if (fromName)
|
|
55
|
+
apiPayload.fromName = fromName;
|
|
56
|
+
if (replyTo)
|
|
57
|
+
apiPayload.replyTo = replyTo;
|
|
58
|
+
const response = await this.withApiSpinner({ json: flags.json, text: ' Configuring domain...' }, () => this.apiClient.post(API_ENDPOINTS.DOMAIN, apiPayload));
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
this.handleApiError(response);
|
|
61
|
+
}
|
|
62
|
+
yamlConfig.project.domain = domain;
|
|
63
|
+
yamlConfig.project.fromEmail = senderEmail;
|
|
64
|
+
yamlConfig.project.address = address;
|
|
65
|
+
if (fromName)
|
|
66
|
+
yamlConfig.project.fromName = fromName;
|
|
67
|
+
if (replyTo)
|
|
68
|
+
yamlConfig.project.replyTo = replyTo;
|
|
69
|
+
await saveYaml(yamlConfig);
|
|
70
|
+
await saveConfig({ ...config, domain });
|
|
71
|
+
const records = response.data?.dnsRecords || [];
|
|
72
|
+
const guideUrl = response.data?.dnsGuideUrl;
|
|
59
73
|
if (flags.json) {
|
|
60
|
-
this.log(JSON.stringify({ dnsRecords, domain
|
|
74
|
+
this.log(JSON.stringify({ dnsRecords: records, domain }, null, 2));
|
|
61
75
|
return;
|
|
62
76
|
}
|
|
63
|
-
this.
|
|
77
|
+
this.log(`\n Add these ${records.length} DNS records to your domain provider:\n`);
|
|
78
|
+
for (const [i, record] of records.entries()) {
|
|
79
|
+
this.log(` ${chalk.bold(`RECORD ${i + 1} — ${this.recordLabel(i)}`)}`);
|
|
80
|
+
this.log(` Type: ${record.type}`);
|
|
81
|
+
this.log(` Host: ${record.host}`);
|
|
82
|
+
this.log(` Value: ${record.value}\n`);
|
|
83
|
+
}
|
|
84
|
+
this.log(` DNS changes take 5–30 minutes to propagate.`);
|
|
85
|
+
if (guideUrl)
|
|
86
|
+
this.log(` Full guide: ${chalk.cyan(guideUrl)}\n`);
|
|
64
87
|
if (!flags.yes) {
|
|
65
88
|
const action = await input({
|
|
66
89
|
default: '',
|
|
67
|
-
message:
|
|
90
|
+
message: "Press Enter once you've added the records, or 'skip'.",
|
|
68
91
|
});
|
|
69
92
|
if (action.toLowerCase() !== 'skip') {
|
|
70
|
-
await this.verifyDomain(false,
|
|
93
|
+
await this.verifyDomain(false, { ...config, domain });
|
|
71
94
|
}
|
|
72
95
|
}
|
|
73
96
|
}
|
|
74
97
|
/**
|
|
75
98
|
* Calls the domain verification API and displays pass/fail for each DNS record.
|
|
76
99
|
*/
|
|
77
|
-
async verifyDomain(jsonOutput,
|
|
100
|
+
async verifyDomain(jsonOutput, config) {
|
|
101
|
+
if (!config.domain) {
|
|
102
|
+
this.error(`No domain configured. Run ${chalk.cyan('mailmodo domain')} to set up your sending domain.`);
|
|
103
|
+
}
|
|
104
|
+
const domain = config.domain;
|
|
78
105
|
const response = await this.withApiSpinner({ json: jsonOutput, text: ' Checking DNS...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY, {
|
|
79
106
|
domain,
|
|
80
107
|
}));
|
|
@@ -94,7 +121,7 @@ export default class Domain extends BaseCommand {
|
|
|
94
121
|
this.log(`\n ${chalk.green('✓')} Domain verified.\n`);
|
|
95
122
|
}
|
|
96
123
|
else {
|
|
97
|
-
this.log(`\n ${
|
|
124
|
+
this.log(`\n ${chalk.yellow('Some records failed.')}`);
|
|
98
125
|
if (!dkim) {
|
|
99
126
|
this.log(`\n DKIM common mistakes:`);
|
|
100
127
|
this.log(` - Using TXT instead of CNAME record type`);
|
|
@@ -106,7 +133,7 @@ export default class Domain extends BaseCommand {
|
|
|
106
133
|
this.log(` - Missing or incorrect CNAME for mm-bounce subdomain`);
|
|
107
134
|
this.log(` - Cloudflare: proxy must be OFF (grey cloud, not orange)`);
|
|
108
135
|
}
|
|
109
|
-
this.log(`\n ${
|
|
136
|
+
this.log(`\n Fix the records and run ${chalk.cyan('mailmodo domain --verify')} again.`);
|
|
110
137
|
if (dnsGuideUrl)
|
|
111
138
|
this.log(` Help: ${chalk.cyan(dnsGuideUrl)}\n`);
|
|
112
139
|
}
|
|
@@ -115,7 +142,11 @@ export default class Domain extends BaseCommand {
|
|
|
115
142
|
* Displays domain health metrics including verification status,
|
|
116
143
|
* bounce rate, and spam complaint rate.
|
|
117
144
|
*/
|
|
118
|
-
async showDomainStatus(jsonOutput,
|
|
145
|
+
async showDomainStatus(jsonOutput, config) {
|
|
146
|
+
if (!config.domain) {
|
|
147
|
+
this.error(`No domain configured. Run ${chalk.cyan('mailmodo domain')} to set up your sending domain.`);
|
|
148
|
+
}
|
|
149
|
+
const domain = config.domain;
|
|
119
150
|
const response = await this.withApiSpinner({ json: jsonOutput, text: ' Loading domain status...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_STATUS, {
|
|
120
151
|
domain,
|
|
121
152
|
}));
|
|
@@ -132,4 +163,47 @@ export default class Domain extends BaseCommand {
|
|
|
132
163
|
this.log(` Bounce rate: ${data.bounceRate ?? 'N/A'}%`);
|
|
133
164
|
this.log(` Spam rate: ${data.spamRate ?? 'N/A'}%\n`);
|
|
134
165
|
}
|
|
166
|
+
async collectDomainInputs(skipPrompts, yamlConfig) {
|
|
167
|
+
if (skipPrompts) {
|
|
168
|
+
const domain = yamlConfig.project?.domain || '';
|
|
169
|
+
if (!domain) {
|
|
170
|
+
this.error('Domain is required. Set it in mailmodo.yaml or use interactive mode.');
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
address: yamlConfig.project?.address || '',
|
|
174
|
+
domain,
|
|
175
|
+
fromName: yamlConfig.project?.fromName || '',
|
|
176
|
+
replyTo: yamlConfig.project?.replyTo || '',
|
|
177
|
+
senderEmail: yamlConfig.project?.fromEmail || '',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const domain = await input({
|
|
181
|
+
default: yamlConfig.project?.domain,
|
|
182
|
+
message: 'What domain will you send from?',
|
|
183
|
+
validate: (v) => (v?.trim() ? true : 'Domain is required'),
|
|
184
|
+
});
|
|
185
|
+
const senderEmail = await input({
|
|
186
|
+
default: yamlConfig.project?.fromEmail,
|
|
187
|
+
message: 'Sender email address:',
|
|
188
|
+
validate: (v) => (v?.includes('@') ? true : 'Please enter a valid email'),
|
|
189
|
+
});
|
|
190
|
+
const fromName = await input({
|
|
191
|
+
default: yamlConfig.project?.fromName || '',
|
|
192
|
+
message: 'Display name (optional, shown as sender name):',
|
|
193
|
+
});
|
|
194
|
+
const replyTo = await input({
|
|
195
|
+
default: yamlConfig.project?.replyTo || '',
|
|
196
|
+
message: 'Reply-to address (optional, press Enter to use sender email):',
|
|
197
|
+
});
|
|
198
|
+
const address = await input({
|
|
199
|
+
default: yamlConfig.project?.address,
|
|
200
|
+
message: 'Business address (required by law):',
|
|
201
|
+
validate: (v) => (v?.trim() ? true : 'Address is required'),
|
|
202
|
+
});
|
|
203
|
+
return { address, domain, fromName, replyTo, senderEmail };
|
|
204
|
+
}
|
|
205
|
+
recordLabel(index) {
|
|
206
|
+
const labels = ['DKIM', 'DMARC', 'Return Path'];
|
|
207
|
+
return labels[index] || `Record ${index + 1}`;
|
|
208
|
+
}
|
|
135
209
|
}
|
|
@@ -5,7 +5,6 @@ import { BaseCommand } from '../../lib/base-command.js';
|
|
|
5
5
|
import { ApiClient } from '../../lib/api-client.js';
|
|
6
6
|
import { loadConfig, saveConfig } from '../../lib/config.js';
|
|
7
7
|
import { API_ENDPOINTS, LOGIN_URL } from '../../lib/constants.js';
|
|
8
|
-
import { INFO } from '../../lib/messages.js';
|
|
9
8
|
export default class Login extends BaseCommand {
|
|
10
9
|
static description = 'Authenticate with Mailmodo using your API key';
|
|
11
10
|
static examples = [
|
|
@@ -48,10 +47,10 @@ export default class Login extends BaseCommand {
|
|
|
48
47
|
this.log(`\n Get your free API key at: ${chalk.cyan(LOGIN_URL)}\n`);
|
|
49
48
|
try {
|
|
50
49
|
await open(LOGIN_URL);
|
|
51
|
-
this.log(
|
|
50
|
+
this.log(' Opening in browser...\n');
|
|
52
51
|
}
|
|
53
52
|
catch {
|
|
54
|
-
this.log(` ${
|
|
53
|
+
this.log(` ${chalk.dim('Could not open browser. Visit the URL above manually.')}\n`);
|
|
55
54
|
}
|
|
56
55
|
apiKey = await input({
|
|
57
56
|
message: 'Paste your API key:',
|
|
@@ -4,7 +4,6 @@ import chalk from 'chalk';
|
|
|
4
4
|
import open from 'open';
|
|
5
5
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
6
6
|
import { API_ENDPOINTS, PREVIEW_PORT } from '../../lib/constants.js';
|
|
7
|
-
import { INFO } from '../../lib/messages.js';
|
|
8
7
|
import { loadTemplate, getEmailStyle, getTemplateFilename, } from '../../lib/yaml-config.js';
|
|
9
8
|
/* eslint-disable camelcase */
|
|
10
9
|
const SAMPLE_DATA = Object.freeze({
|
|
@@ -239,7 +238,7 @@ export default class Preview extends BaseCommand {
|
|
|
239
238
|
if (!jsonOutput) {
|
|
240
239
|
this.log(`\n Style: ${chalk.cyan(effectiveStyle)}`);
|
|
241
240
|
this.log(` Preview server at ${chalk.cyan(url)}`);
|
|
242
|
-
this.log(`
|
|
241
|
+
this.log(` Opening in browser...\n`);
|
|
243
242
|
this.log(` ${chalk.dim('Press Ctrl+C to stop the preview server.')}\n`);
|
|
244
243
|
}
|
|
245
244
|
try {
|
|
@@ -247,7 +246,7 @@ export default class Preview extends BaseCommand {
|
|
|
247
246
|
}
|
|
248
247
|
catch {
|
|
249
248
|
if (!jsonOutput) {
|
|
250
|
-
this.log(` ${
|
|
249
|
+
this.log(` ${chalk.dim('Could not open browser. Visit the URL above manually.')}`);
|
|
251
250
|
}
|
|
252
251
|
}
|
|
253
252
|
await new Promise((resolve) => {
|
|
@@ -20,7 +20,14 @@ export default class Settings extends BaseCommand {
|
|
|
20
20
|
* Returns true/false for verified/unverified, or null if unavailable.
|
|
21
21
|
*/
|
|
22
22
|
private fetchDomainVerified;
|
|
23
|
+
/**
|
|
24
|
+
* Handles domain change: collects the new domain, sender email, and
|
|
25
|
+
* business address, calls the API to register them, displays the required
|
|
26
|
+
* DNS records, and saves the updated config. Emails won't send until the
|
|
27
|
+
* domain is re-verified.
|
|
28
|
+
*/
|
|
23
29
|
private handleDomainChange;
|
|
30
|
+
private recordLabel;
|
|
24
31
|
/**
|
|
25
32
|
* Handles the logo file upload flow: validates the local file exists,
|
|
26
33
|
* reads it, uploads to Mailmodo CDN via API, and updates both logoFile
|
|
@@ -6,7 +6,6 @@ import { readFile } from 'node:fs/promises';
|
|
|
6
6
|
import { resolve } from 'node:path';
|
|
7
7
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
8
8
|
import { API_ENDPOINTS } from '../../lib/constants.js';
|
|
9
|
-
import { INFO } from '../../lib/messages.js';
|
|
10
9
|
import { saveYaml } from '../../lib/yaml-config.js';
|
|
11
10
|
const SETTINGS_GROUPS = Object.freeze({
|
|
12
11
|
billing: ['monthly_cap'],
|
|
@@ -81,7 +80,7 @@ export default class Settings extends BaseCommand {
|
|
|
81
80
|
return;
|
|
82
81
|
}
|
|
83
82
|
this.log(`\n ${chalk.green('✓')} ${key} updated to ${chalk.cyan(value)}`);
|
|
84
|
-
this.log(` ${
|
|
83
|
+
this.log(` Run ${chalk.cyan("'mailmodo deploy'")} to apply.\n`);
|
|
85
84
|
}
|
|
86
85
|
displaySettingsGroup(group, keys, project, domainVerified) {
|
|
87
86
|
const availableKeys = keys.filter((key) => {
|
|
@@ -170,7 +169,7 @@ export default class Settings extends BaseCommand {
|
|
|
170
169
|
project.emailStyle = style;
|
|
171
170
|
await saveYaml(yamlConfig);
|
|
172
171
|
this.log(`\n ${chalk.green('✓')} email_style updated to ${chalk.cyan(style)}`);
|
|
173
|
-
this.log(` ${
|
|
172
|
+
this.log(` Run ${chalk.cyan("'mailmodo deploy'")} to apply.\n`);
|
|
174
173
|
return;
|
|
175
174
|
}
|
|
176
175
|
const newValue = await input({
|
|
@@ -179,7 +178,7 @@ export default class Settings extends BaseCommand {
|
|
|
179
178
|
project[editPropKey] =
|
|
180
179
|
editPropKey === 'monthlyCap' ? Number(newValue) : newValue;
|
|
181
180
|
await saveYaml(yamlConfig);
|
|
182
|
-
this.log(`\n ${chalk.green('✓')} Updated. ${
|
|
181
|
+
this.log(`\n ${chalk.green('✓')} Updated. Run ${chalk.cyan("'mailmodo deploy'")} to apply.\n`);
|
|
183
182
|
}
|
|
184
183
|
/**
|
|
185
184
|
* Fetches the domain verification status from the API.
|
|
@@ -202,14 +201,58 @@ export default class Settings extends BaseCommand {
|
|
|
202
201
|
return null;
|
|
203
202
|
}
|
|
204
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Handles domain change: collects the new domain, sender email, and
|
|
206
|
+
* business address, calls the API to register them, displays the required
|
|
207
|
+
* DNS records, and saves the updated config. Emails won't send until the
|
|
208
|
+
* domain is re-verified.
|
|
209
|
+
*/
|
|
205
210
|
async handleDomainChange(yamlConfig) {
|
|
211
|
+
const newDomain = await input({
|
|
212
|
+
message: 'New domain:',
|
|
213
|
+
validate: (v) => (v?.trim() ? true : 'Domain is required'),
|
|
214
|
+
});
|
|
215
|
+
const newFromEmail = await input({
|
|
216
|
+
default: yamlConfig.project.fromEmail || '',
|
|
217
|
+
message: 'Sender email (from address):',
|
|
218
|
+
validate: (v) => (v?.includes('@') ? true : 'Please enter a valid email'),
|
|
219
|
+
});
|
|
220
|
+
const newAddress = await input({
|
|
221
|
+
default: yamlConfig.project.address || '',
|
|
222
|
+
message: 'Business address (required by law):',
|
|
223
|
+
validate: (v) => (v?.trim() ? true : 'Address is required'),
|
|
224
|
+
});
|
|
206
225
|
await this.ensureAuth();
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
226
|
+
const response = await this.apiClient.post(API_ENDPOINTS.DOMAIN, {
|
|
227
|
+
address: newAddress,
|
|
228
|
+
domain: newDomain,
|
|
229
|
+
fromEmail: newFromEmail,
|
|
230
|
+
});
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
this.handleApiError(response);
|
|
233
|
+
}
|
|
234
|
+
const records = response.data?.dnsRecords || [];
|
|
235
|
+
const dnsGuideUrl = response.data?.dnsGuideUrl;
|
|
236
|
+
yamlConfig.project.domain = newDomain;
|
|
237
|
+
yamlConfig.project.fromEmail = newFromEmail;
|
|
238
|
+
yamlConfig.project.address = newAddress;
|
|
239
|
+
await saveYaml(yamlConfig);
|
|
240
|
+
this.log(`\n Domain, sender email, and business address updated. You will need to re-verify.`);
|
|
241
|
+
this.log(` New DNS records:\n`);
|
|
242
|
+
for (const [i, record] of records.entries()) {
|
|
243
|
+
this.log(` ${chalk.bold(`RECORD ${i + 1} — ${this.recordLabel(i)}`)}`);
|
|
244
|
+
this.log(` Type: ${record.type}`);
|
|
245
|
+
this.log(` Host: ${record.host}`);
|
|
246
|
+
this.log(` Value: ${record.value}\n`);
|
|
247
|
+
}
|
|
211
248
|
this.log(` Run ${chalk.cyan("'mailmodo domain --verify'")} once records are added.`);
|
|
212
249
|
this.log(` Emails will not send until the new domain is verified.`);
|
|
250
|
+
if (dnsGuideUrl)
|
|
251
|
+
this.log(` Help: ${chalk.cyan(dnsGuideUrl)}\n`);
|
|
252
|
+
}
|
|
253
|
+
recordLabel(index) {
|
|
254
|
+
const labels = ['DKIM', 'DMARC', 'Return Path'];
|
|
255
|
+
return labels[index] || `Record ${index + 1}`;
|
|
213
256
|
}
|
|
214
257
|
/**
|
|
215
258
|
* Handles the logo file upload flow: validates the local file exists,
|
|
@@ -69,31 +69,5 @@ export declare abstract class BaseCommand extends Command {
|
|
|
69
69
|
* @param {{ status: number; debug?: ApiRequestDebugInfo }} response - Failed API response.
|
|
70
70
|
* @returns {string} Message plus indented Request details for troubleshooting.
|
|
71
71
|
*/
|
|
72
|
-
protected collectDomainSetupInputs(yamlConfig: MailmodoYaml, skipPrompts: boolean): Promise<{
|
|
73
|
-
address: string;
|
|
74
|
-
domain: string;
|
|
75
|
-
fromEmail: string;
|
|
76
|
-
fromName: string;
|
|
77
|
-
replyTo: string;
|
|
78
|
-
}>;
|
|
79
|
-
protected registerDomain(yamlConfig: MailmodoYaml, inputs: {
|
|
80
|
-
address: string;
|
|
81
|
-
domain: string;
|
|
82
|
-
fromEmail: string;
|
|
83
|
-
fromName?: string;
|
|
84
|
-
replyTo?: string;
|
|
85
|
-
}, json: boolean): Promise<{
|
|
86
|
-
dnsGuideUrl?: string;
|
|
87
|
-
dnsRecords: Array<{
|
|
88
|
-
host: string;
|
|
89
|
-
type: string;
|
|
90
|
-
value: string;
|
|
91
|
-
}>;
|
|
92
|
-
}>;
|
|
93
|
-
protected logDnsRecords(records: Array<{
|
|
94
|
-
host: string;
|
|
95
|
-
type: string;
|
|
96
|
-
value: string;
|
|
97
|
-
}>, guideUrl: string | undefined, json: boolean): void;
|
|
98
72
|
private formatApiFailure;
|
|
99
73
|
}
|
package/dist/lib/base-command.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import { input } from '@inquirer/prompts';
|
|
2
1
|
import { Command, Flags } from '@oclif/core';
|
|
3
2
|
import chalk from 'chalk';
|
|
4
3
|
import ora from 'ora';
|
|
5
4
|
import { ApiClient } from './api-client.js';
|
|
6
5
|
import { loadConfig } from './config.js';
|
|
7
|
-
import {
|
|
8
|
-
import { ERRORS, INFO, PROMPTS, recordLabel, VALIDATION } from './messages.js';
|
|
9
|
-
import { loadYaml, saveYaml } from './yaml-config.js';
|
|
6
|
+
import { loadYaml } from './yaml-config.js';
|
|
10
7
|
/**
|
|
11
8
|
* Abstract base command providing shared functionality for all Mailmodo CLI commands.
|
|
12
9
|
* Subclasses inherit --json and --yes base flags, authentication enforcement,
|
|
@@ -38,7 +35,7 @@ export class BaseCommand extends Command {
|
|
|
38
35
|
}
|
|
39
36
|
const config = await loadConfig();
|
|
40
37
|
if (!config?.apiKey) {
|
|
41
|
-
this.error(
|
|
38
|
+
this.error(`Not logged in. Run ${chalk.cyan('mailmodo login')} to authenticate.`);
|
|
42
39
|
}
|
|
43
40
|
this.apiClient = new ApiClient(config.apiKey);
|
|
44
41
|
return config;
|
|
@@ -83,7 +80,7 @@ export class BaseCommand extends Command {
|
|
|
83
80
|
async ensureYaml() {
|
|
84
81
|
const config = await loadYaml();
|
|
85
82
|
if (!config) {
|
|
86
|
-
this.error(
|
|
83
|
+
this.error(`No mailmodo.yaml found. Run ${chalk.cyan('mailmodo init')} first.`);
|
|
87
84
|
}
|
|
88
85
|
return config;
|
|
89
86
|
}
|
|
@@ -99,12 +96,12 @@ export class BaseCommand extends Command {
|
|
|
99
96
|
*/
|
|
100
97
|
handleApiError(response) {
|
|
101
98
|
if (response.status === 401) {
|
|
102
|
-
this.error(this.formatApiFailure(
|
|
99
|
+
this.error(this.formatApiFailure(`Invalid API key. Run ${chalk.cyan('mailmodo login')} to re-authenticate.`, response));
|
|
103
100
|
}
|
|
104
101
|
if (response.status === 429) {
|
|
105
|
-
this.error(this.formatApiFailure(
|
|
102
|
+
this.error(this.formatApiFailure('Rate limit exceeded. Please try again later.', response));
|
|
106
103
|
}
|
|
107
|
-
this.error(this.formatApiFailure(response.error ||
|
|
104
|
+
this.error(this.formatApiFailure(response.error || 'An unexpected API error occurred.', response));
|
|
108
105
|
}
|
|
109
106
|
/**
|
|
110
107
|
* Builds the terminal error string for a failed API call, appending request
|
|
@@ -114,86 +111,6 @@ export class BaseCommand extends Command {
|
|
|
114
111
|
* @param {{ status: number; debug?: ApiRequestDebugInfo }} response - Failed API response.
|
|
115
112
|
* @returns {string} Message plus indented Request details for troubleshooting.
|
|
116
113
|
*/
|
|
117
|
-
async collectDomainSetupInputs(yamlConfig, skipPrompts) {
|
|
118
|
-
if (skipPrompts) {
|
|
119
|
-
const domain = yamlConfig.project?.domain || '';
|
|
120
|
-
if (!domain) {
|
|
121
|
-
this.error('Domain is required. Set it in mailmodo.yaml or use interactive mode.');
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
address: yamlConfig.project?.address || '',
|
|
125
|
-
domain,
|
|
126
|
-
fromEmail: yamlConfig.project?.fromEmail || '',
|
|
127
|
-
fromName: yamlConfig.project?.fromName || '',
|
|
128
|
-
replyTo: yamlConfig.project?.replyTo || '',
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
const domain = await input({
|
|
132
|
-
default: yamlConfig.project?.domain,
|
|
133
|
-
message: PROMPTS.DOMAIN,
|
|
134
|
-
validate: (v) => (v?.trim() ? true : VALIDATION.DOMAIN_REQUIRED),
|
|
135
|
-
});
|
|
136
|
-
const fromEmail = await input({
|
|
137
|
-
default: yamlConfig.project?.fromEmail,
|
|
138
|
-
message: PROMPTS.SENDER_EMAIL,
|
|
139
|
-
validate: (v) => (v?.includes('@') ? true : VALIDATION.EMAIL_INVALID),
|
|
140
|
-
});
|
|
141
|
-
const fromName = await input({
|
|
142
|
-
default: yamlConfig.project?.fromName || '',
|
|
143
|
-
message: PROMPTS.FROM_NAME,
|
|
144
|
-
});
|
|
145
|
-
const replyTo = await input({
|
|
146
|
-
default: yamlConfig.project?.replyTo || '',
|
|
147
|
-
message: PROMPTS.REPLY_TO,
|
|
148
|
-
});
|
|
149
|
-
const address = await input({
|
|
150
|
-
default: yamlConfig.project?.address,
|
|
151
|
-
message: PROMPTS.BUSINESS_ADDRESS,
|
|
152
|
-
validate: (v) => (v?.trim() ? true : VALIDATION.ADDRESS_REQUIRED),
|
|
153
|
-
});
|
|
154
|
-
return { address, domain, fromEmail, fromName, replyTo };
|
|
155
|
-
}
|
|
156
|
-
async registerDomain(yamlConfig, inputs, json) {
|
|
157
|
-
const apiPayload = {
|
|
158
|
-
address: inputs.address,
|
|
159
|
-
domain: inputs.domain,
|
|
160
|
-
fromEmail: inputs.fromEmail,
|
|
161
|
-
};
|
|
162
|
-
if (inputs.fromName)
|
|
163
|
-
apiPayload.fromName = inputs.fromName;
|
|
164
|
-
if (inputs.replyTo)
|
|
165
|
-
apiPayload.replyTo = inputs.replyTo;
|
|
166
|
-
const response = await this.withApiSpinner({ json, text: ' Configuring domain...' }, () => this.apiClient.post(API_ENDPOINTS.DOMAIN, apiPayload));
|
|
167
|
-
if (!response.ok) {
|
|
168
|
-
this.handleApiError(response);
|
|
169
|
-
}
|
|
170
|
-
yamlConfig.project.domain = inputs.domain;
|
|
171
|
-
yamlConfig.project.fromEmail = inputs.fromEmail;
|
|
172
|
-
yamlConfig.project.address = inputs.address;
|
|
173
|
-
if (inputs.fromName)
|
|
174
|
-
yamlConfig.project.fromName = inputs.fromName;
|
|
175
|
-
if (inputs.replyTo)
|
|
176
|
-
yamlConfig.project.replyTo = inputs.replyTo;
|
|
177
|
-
await saveYaml(yamlConfig);
|
|
178
|
-
return {
|
|
179
|
-
dnsGuideUrl: response.data?.dnsGuideUrl,
|
|
180
|
-
dnsRecords: response.data?.dnsRecords || [],
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
logDnsRecords(records, guideUrl, json) {
|
|
184
|
-
if (json)
|
|
185
|
-
return;
|
|
186
|
-
this.log(`\n Add these ${records.length} DNS records to your domain provider:\n`);
|
|
187
|
-
for (const [i, record] of records.entries()) {
|
|
188
|
-
this.log(` ${chalk.bold(`RECORD ${i + 1} — ${recordLabel(i)}`)}`);
|
|
189
|
-
this.log(` Type: ${record.type}`);
|
|
190
|
-
this.log(` Host: ${record.host}`);
|
|
191
|
-
this.log(` Value: ${record.value}\n`);
|
|
192
|
-
}
|
|
193
|
-
this.log(` ${INFO.DNS_PROPAGATION}`);
|
|
194
|
-
if (guideUrl)
|
|
195
|
-
this.log(` Full guide: ${chalk.cyan(guideUrl)}\n`);
|
|
196
|
-
}
|
|
197
114
|
formatApiFailure(message, response) {
|
|
198
115
|
const { debug, status } = response;
|
|
199
116
|
if (!debug) {
|
package/dist/lib/config.d.ts
CHANGED
package/oclif.manifest.json
CHANGED
|
@@ -280,45 +280,6 @@
|
|
|
280
280
|
"index.js"
|
|
281
281
|
]
|
|
282
282
|
},
|
|
283
|
-
"emails": {
|
|
284
|
-
"aliases": [],
|
|
285
|
-
"args": {},
|
|
286
|
-
"description": "List and view configured email sequences",
|
|
287
|
-
"examples": [
|
|
288
|
-
"<%= config.bin %> emails",
|
|
289
|
-
"<%= config.bin %> emails --json"
|
|
290
|
-
],
|
|
291
|
-
"flags": {
|
|
292
|
-
"json": {
|
|
293
|
-
"description": "Output as JSON",
|
|
294
|
-
"name": "json",
|
|
295
|
-
"allowNo": false,
|
|
296
|
-
"type": "boolean"
|
|
297
|
-
},
|
|
298
|
-
"yes": {
|
|
299
|
-
"char": "y",
|
|
300
|
-
"description": "Skip confirmation prompts",
|
|
301
|
-
"name": "yes",
|
|
302
|
-
"allowNo": false,
|
|
303
|
-
"type": "boolean"
|
|
304
|
-
}
|
|
305
|
-
},
|
|
306
|
-
"hasDynamicHelp": false,
|
|
307
|
-
"hiddenAliases": [],
|
|
308
|
-
"id": "emails",
|
|
309
|
-
"pluginAlias": "@mailmodo/cli",
|
|
310
|
-
"pluginName": "@mailmodo/cli",
|
|
311
|
-
"pluginType": "core",
|
|
312
|
-
"strict": true,
|
|
313
|
-
"enableJsonFlag": false,
|
|
314
|
-
"isESM": true,
|
|
315
|
-
"relativePath": [
|
|
316
|
-
"dist",
|
|
317
|
-
"commands",
|
|
318
|
-
"emails",
|
|
319
|
-
"index.js"
|
|
320
|
-
]
|
|
321
|
-
},
|
|
322
283
|
"init": {
|
|
323
284
|
"aliases": [],
|
|
324
285
|
"args": {},
|
|
@@ -655,7 +616,46 @@
|
|
|
655
616
|
"status",
|
|
656
617
|
"index.js"
|
|
657
618
|
]
|
|
619
|
+
},
|
|
620
|
+
"emails": {
|
|
621
|
+
"aliases": [],
|
|
622
|
+
"args": {},
|
|
623
|
+
"description": "List and view configured email sequences",
|
|
624
|
+
"examples": [
|
|
625
|
+
"<%= config.bin %> emails",
|
|
626
|
+
"<%= config.bin %> emails --json"
|
|
627
|
+
],
|
|
628
|
+
"flags": {
|
|
629
|
+
"json": {
|
|
630
|
+
"description": "Output as JSON",
|
|
631
|
+
"name": "json",
|
|
632
|
+
"allowNo": false,
|
|
633
|
+
"type": "boolean"
|
|
634
|
+
},
|
|
635
|
+
"yes": {
|
|
636
|
+
"char": "y",
|
|
637
|
+
"description": "Skip confirmation prompts",
|
|
638
|
+
"name": "yes",
|
|
639
|
+
"allowNo": false,
|
|
640
|
+
"type": "boolean"
|
|
641
|
+
}
|
|
642
|
+
},
|
|
643
|
+
"hasDynamicHelp": false,
|
|
644
|
+
"hiddenAliases": [],
|
|
645
|
+
"id": "emails",
|
|
646
|
+
"pluginAlias": "@mailmodo/cli",
|
|
647
|
+
"pluginName": "@mailmodo/cli",
|
|
648
|
+
"pluginType": "core",
|
|
649
|
+
"strict": true,
|
|
650
|
+
"enableJsonFlag": false,
|
|
651
|
+
"isESM": true,
|
|
652
|
+
"relativePath": [
|
|
653
|
+
"dist",
|
|
654
|
+
"commands",
|
|
655
|
+
"emails",
|
|
656
|
+
"index.js"
|
|
657
|
+
]
|
|
658
658
|
}
|
|
659
659
|
},
|
|
660
|
-
"version": "0.0.31
|
|
660
|
+
"version": "0.0.31"
|
|
661
661
|
}
|
package/package.json
CHANGED
package/dist/lib/messages.d.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export declare const SEPARATOR: string;
|
|
2
|
-
export declare const VALIDATION: {
|
|
3
|
-
readonly ADDRESS_REQUIRED: "Address is required";
|
|
4
|
-
readonly DOMAIN_REQUIRED: "Domain is required";
|
|
5
|
-
readonly EMAIL_INVALID: "Please enter a valid email";
|
|
6
|
-
};
|
|
7
|
-
export declare const PROMPTS: {
|
|
8
|
-
readonly BUSINESS_ADDRESS: "Business address (required by law):";
|
|
9
|
-
readonly DOMAIN: "What domain will you send from?";
|
|
10
|
-
readonly ENTER_AFTER_RECORDS: "Press Enter once you've added the records, or 'skip'.";
|
|
11
|
-
readonly FROM_NAME: "Display name (optional, shown as sender name):";
|
|
12
|
-
readonly REPLY_TO: "Reply-to address (optional, press Enter to use sender email):";
|
|
13
|
-
readonly SENDER_EMAIL: "Sender email address:";
|
|
14
|
-
};
|
|
15
|
-
export declare const ERRORS: {
|
|
16
|
-
readonly DOMAIN_NOT_CONFIGURED: `No domain configured. Run ${string} to set up your sending domain.`;
|
|
17
|
-
readonly DOMAIN_NOT_REGISTERED: `Sending domain not registered. Run: ${string}`;
|
|
18
|
-
readonly DOMAIN_NOT_VERIFIED: `Sending domain not verified. Run: ${string}`;
|
|
19
|
-
readonly INVALID_API_KEY: `Invalid API key. Run ${string} to re-authenticate.`;
|
|
20
|
-
readonly NOT_LOGGED_IN: `Not logged in. Run ${string} to authenticate.`;
|
|
21
|
-
readonly NO_YAML: `No mailmodo.yaml found. Run ${string} first.`;
|
|
22
|
-
readonly RATE_LIMIT: "Rate limit exceeded. Please try again later.";
|
|
23
|
-
readonly UNEXPECTED_API: "An unexpected API error occurred.";
|
|
24
|
-
};
|
|
25
|
-
export declare const INFO: {
|
|
26
|
-
readonly BROWSER_OPEN_FAILED: string;
|
|
27
|
-
readonly BROWSER_OPENING: "Opening in browser...";
|
|
28
|
-
readonly DEPLOY_TO_APPLY: `Run ${string} to apply.`;
|
|
29
|
-
readonly DNS_FIX_AND_VERIFY: `Fix the records and run ${string} again.`;
|
|
30
|
-
readonly DNS_PROPAGATION: "DNS changes take 5–30 minutes to propagate.";
|
|
31
|
-
readonly DNS_RECORDS_FAILED: string;
|
|
32
|
-
readonly DOMAIN_NOT_DEPLOYED_HINT: `When ready, run: ${string}
|
|
33
|
-
Then: ${string}`;
|
|
34
|
-
readonly SEQUENCES_NOT_DEPLOYED: `Sequences saved but ${string}.`;
|
|
35
|
-
};
|
|
36
|
-
export declare function recordLabel(index: number): string;
|
package/dist/lib/messages.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
export const SEPARATOR = '─'.repeat(53);
|
|
3
|
-
export const VALIDATION = {
|
|
4
|
-
ADDRESS_REQUIRED: 'Address is required',
|
|
5
|
-
DOMAIN_REQUIRED: 'Domain is required',
|
|
6
|
-
EMAIL_INVALID: 'Please enter a valid email',
|
|
7
|
-
};
|
|
8
|
-
export const PROMPTS = {
|
|
9
|
-
BUSINESS_ADDRESS: 'Business address (required by law):',
|
|
10
|
-
DOMAIN: 'What domain will you send from?',
|
|
11
|
-
ENTER_AFTER_RECORDS: "Press Enter once you've added the records, or 'skip'.",
|
|
12
|
-
FROM_NAME: 'Display name (optional, shown as sender name):',
|
|
13
|
-
REPLY_TO: 'Reply-to address (optional, press Enter to use sender email):',
|
|
14
|
-
SENDER_EMAIL: 'Sender email address:',
|
|
15
|
-
};
|
|
16
|
-
export const ERRORS = {
|
|
17
|
-
DOMAIN_NOT_CONFIGURED: `No domain configured. Run ${chalk.cyan('mailmodo domain')} to set up your sending domain.`,
|
|
18
|
-
DOMAIN_NOT_REGISTERED: `Sending domain not registered. Run: ${chalk.cyan('mailmodo domain')}`,
|
|
19
|
-
DOMAIN_NOT_VERIFIED: `Sending domain not verified. Run: ${chalk.cyan('mailmodo domain --verify')}`,
|
|
20
|
-
INVALID_API_KEY: `Invalid API key. Run ${chalk.cyan('mailmodo login')} to re-authenticate.`,
|
|
21
|
-
NOT_LOGGED_IN: `Not logged in. Run ${chalk.cyan('mailmodo login')} to authenticate.`,
|
|
22
|
-
NO_YAML: `No mailmodo.yaml found. Run ${chalk.cyan('mailmodo init')} first.`,
|
|
23
|
-
RATE_LIMIT: 'Rate limit exceeded. Please try again later.',
|
|
24
|
-
UNEXPECTED_API: 'An unexpected API error occurred.',
|
|
25
|
-
};
|
|
26
|
-
export const INFO = {
|
|
27
|
-
BROWSER_OPEN_FAILED: chalk.dim('Could not open browser. Visit the URL above manually.'),
|
|
28
|
-
BROWSER_OPENING: 'Opening in browser...',
|
|
29
|
-
DEPLOY_TO_APPLY: `Run ${chalk.cyan("'mailmodo deploy'")} to apply.`,
|
|
30
|
-
DNS_FIX_AND_VERIFY: `Fix the records and run ${chalk.cyan('mailmodo domain --verify')} again.`,
|
|
31
|
-
DNS_PROPAGATION: 'DNS changes take 5–30 minutes to propagate.',
|
|
32
|
-
DNS_RECORDS_FAILED: chalk.yellow('Some records failed.'),
|
|
33
|
-
DOMAIN_NOT_DEPLOYED_HINT: `When ready, run: ${chalk.cyan('mailmodo domain')}\n Then: ${chalk.cyan('mailmodo deploy')}`,
|
|
34
|
-
SEQUENCES_NOT_DEPLOYED: `Sequences saved but ${chalk.yellow('NOT deployed')}.`,
|
|
35
|
-
};
|
|
36
|
-
export function recordLabel(index) {
|
|
37
|
-
const labels = ['DKIM', 'DMARC', 'Return Path'];
|
|
38
|
-
return labels[index] || `Record ${index + 1}`;
|
|
39
|
-
}
|