@mailmodo/cli 0.0.19-beta.pr21.30 → 0.0.19
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.
|
@@ -10,15 +10,9 @@ export default class Deploy extends BaseCommand {
|
|
|
10
10
|
* Fetches current DNS verification status for the deploy flow.
|
|
11
11
|
*
|
|
12
12
|
* @param jsonOutput - When true, spinner uses stderr for stdout-safe JSON runs.
|
|
13
|
-
* @param domain - Sending domain to check. If empty, request will fail and trigger setup flow.
|
|
14
13
|
*/
|
|
15
14
|
private fetchDomainVerifyForDeploy;
|
|
16
15
|
run(): Promise<void>;
|
|
17
|
-
private buildDeployPayload;
|
|
18
|
-
private mapEmailToPayload;
|
|
19
|
-
private buildProjectPayload;
|
|
20
|
-
private confirmDeploy;
|
|
21
|
-
private ensureDomainReady;
|
|
22
16
|
/**
|
|
23
17
|
* Lists emails about to be deployed (skipped when `--json` is set).
|
|
24
18
|
*
|
|
@@ -38,11 +32,9 @@ export default class Deploy extends BaseCommand {
|
|
|
38
32
|
* @returns {Promise<boolean>} true if domain was verified, false if skipped.
|
|
39
33
|
*/
|
|
40
34
|
private runDomainSetup;
|
|
41
|
-
private collectDomainInputs;
|
|
42
|
-
private showDnsRecords;
|
|
43
35
|
/**
|
|
44
36
|
* Calls the domain verification API endpoint and reports pass/fail
|
|
45
|
-
* status for each DNS record (DKIM, DMARC
|
|
37
|
+
* status for each DNS record (SPF, DKIM, DMARC).
|
|
46
38
|
*
|
|
47
39
|
* @returns {Promise<boolean>} true if all records pass.
|
|
48
40
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { confirm, input } from '@inquirer/prompts';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
4
|
-
import { API_ENDPOINTS,
|
|
5
|
-
import {
|
|
4
|
+
import { API_ENDPOINTS, DNS_GUIDE_URL } from '../../lib/constants.js';
|
|
5
|
+
import { saveYaml } from '../../lib/yaml-config.js';
|
|
6
6
|
export default class Deploy extends BaseCommand {
|
|
7
7
|
static description = 'Deploy email sequences and verify sending domain';
|
|
8
8
|
static examples = [
|
|
@@ -16,130 +16,69 @@ export default class Deploy extends BaseCommand {
|
|
|
16
16
|
* Fetches current DNS verification status for the deploy flow.
|
|
17
17
|
*
|
|
18
18
|
* @param jsonOutput - When true, spinner uses stderr for stdout-safe JSON runs.
|
|
19
|
-
* @param domain - Sending domain to check. If empty, request will fail and trigger setup flow.
|
|
20
19
|
*/
|
|
21
|
-
fetchDomainVerifyForDeploy(jsonOutput
|
|
22
|
-
return this.withApiSpinner({ json: jsonOutput, text: ' Checking domain verification...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY
|
|
23
|
-
domain: domain || '',
|
|
24
|
-
}));
|
|
20
|
+
fetchDomainVerifyForDeploy(jsonOutput) {
|
|
21
|
+
return this.withApiSpinner({ json: jsonOutput, text: ' Checking domain verification...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY));
|
|
25
22
|
}
|
|
26
23
|
async run() {
|
|
27
24
|
const { flags } = await this.parse(Deploy);
|
|
28
25
|
await this.ensureAuth();
|
|
29
26
|
const yamlConfig = await this.ensureYaml();
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const domainVerify = await this.fetchDomainVerifyForDeploy(flags.json);
|
|
28
|
+
const domainVerified = domainVerify.ok &&
|
|
29
|
+
domainVerify.data?.spf === 'pass' &&
|
|
30
|
+
domainVerify.data?.dkim === 'pass' &&
|
|
31
|
+
domainVerify.data?.dmarc === 'pass';
|
|
32
|
+
if (!domainVerified) {
|
|
33
|
+
if (!flags.json) {
|
|
34
|
+
this.log(`\n No sending domain verified yet.`);
|
|
35
|
+
this.log(` You need to verify a domain before sending emails.`);
|
|
36
|
+
this.log(` This is a one-time setup. Takes about 5 minutes.\n`);
|
|
37
|
+
}
|
|
38
|
+
if (!flags.yes) {
|
|
39
|
+
const setupNow = await confirm({
|
|
40
|
+
default: true,
|
|
41
|
+
message: 'Set up your sending domain now?',
|
|
42
|
+
});
|
|
43
|
+
if (!setupNow) {
|
|
44
|
+
this.log(`\n Sequences saved but ${chalk.yellow('NOT deployed')}.`);
|
|
45
|
+
this.log(` Emails will not send until your domain is verified.`);
|
|
46
|
+
this.log(` When ready, run: ${chalk.cyan('mailmodo domain')}`);
|
|
47
|
+
this.log(` Then: ${chalk.cyan('mailmodo deploy')}\n`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const completed = await this.runDomainSetup(yamlConfig, flags);
|
|
52
|
+
if (!completed)
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
33
55
|
this.logPreDeploySummary(yamlConfig, flags.json);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
if (!flags.yes) {
|
|
57
|
+
const proceed = await confirm({
|
|
58
|
+
default: true,
|
|
59
|
+
message: `Deploy ${yamlConfig.emails.length} emails?`,
|
|
60
|
+
});
|
|
61
|
+
if (!proceed) {
|
|
62
|
+
this.log('\n Deploy cancelled.\n');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const response = await this.withApiSpinner({ json: flags.json, text: ' Deploying email sequences...' }, () => this.apiClient.post(API_ENDPOINTS.SEQUENCES, {
|
|
67
|
+
emails: yamlConfig.emails,
|
|
68
|
+
project: yamlConfig.project,
|
|
69
|
+
}));
|
|
39
70
|
if (!response.ok) {
|
|
40
71
|
this.handleApiError(response);
|
|
41
72
|
}
|
|
42
73
|
if (flags.json) {
|
|
43
74
|
this.log(JSON.stringify({
|
|
44
|
-
deployed:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
sdkSnippet: response.data.sdkSnippet,
|
|
48
|
-
sequenceId: response.data.sequenceId,
|
|
75
|
+
deployed: true,
|
|
76
|
+
emailsLive: yamlConfig.emails.length,
|
|
77
|
+
sdkSnippet: response.data?.sdkSnippet,
|
|
49
78
|
}, null, 2));
|
|
50
79
|
return;
|
|
51
80
|
}
|
|
52
|
-
this.logDeploySuccessInstructions(
|
|
53
|
-
}
|
|
54
|
-
async buildDeployPayload(yamlConfig) {
|
|
55
|
-
const emailsWithHtml = await Promise.all(yamlConfig.emails.map(async (email) => {
|
|
56
|
-
const html = (await loadTemplate(`${email.id}.html`)) || '';
|
|
57
|
-
return { ...this.mapEmailToPayload(email), html, plainHtml: html };
|
|
58
|
-
}));
|
|
59
|
-
return {
|
|
60
|
-
...this.buildProjectPayload(yamlConfig.project),
|
|
61
|
-
emails: emailsWithHtml,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
mapEmailToPayload(email) {
|
|
65
|
-
return {
|
|
66
|
-
condition: email.condition || null,
|
|
67
|
-
ctaText: '',
|
|
68
|
-
delay: typeof email.delay === 'string'
|
|
69
|
-
? Number.parseInt(email.delay, 10) || 0
|
|
70
|
-
: email.delay,
|
|
71
|
-
goal: email.goal || '',
|
|
72
|
-
id: email.id,
|
|
73
|
-
isReminder: false,
|
|
74
|
-
previewText: email.previewText || '',
|
|
75
|
-
priority: 'medium',
|
|
76
|
-
subject: email.subject,
|
|
77
|
-
trigger: email.trigger,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
buildProjectPayload(project) {
|
|
81
|
-
return {
|
|
82
|
-
brand: {
|
|
83
|
-
colors: [project?.brandColor || DEFAULT_BRAND_COLOR],
|
|
84
|
-
logoUrl: project?.logoUrl || '',
|
|
85
|
-
},
|
|
86
|
-
emailStyle: project?.emailStyle || 'branded',
|
|
87
|
-
monthlyCap: project?.monthlyCap ?? DEFAULT_MONTHLY_CAP,
|
|
88
|
-
product: {
|
|
89
|
-
businessType: project?.type || '',
|
|
90
|
-
description: '',
|
|
91
|
-
pricingModel: '',
|
|
92
|
-
productName: project?.name || '',
|
|
93
|
-
saasModel: '',
|
|
94
|
-
targetUser: '',
|
|
95
|
-
url: project?.url || '',
|
|
96
|
-
},
|
|
97
|
-
senderDetails: {
|
|
98
|
-
address: project?.address || '',
|
|
99
|
-
domain: project?.domain || '',
|
|
100
|
-
fromEmail: project?.fromEmail || '',
|
|
101
|
-
fromName: project?.fromName || '',
|
|
102
|
-
replyTo: project?.replyTo || project?.fromEmail || '',
|
|
103
|
-
},
|
|
104
|
-
webhookUrl: project?.webhookUrl || '',
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
async confirmDeploy(yamlConfig, flags) {
|
|
108
|
-
if (flags.yes)
|
|
109
|
-
return true;
|
|
110
|
-
const proceed = await confirm({
|
|
111
|
-
default: true,
|
|
112
|
-
message: `Deploy ${yamlConfig.emails.length} emails?`,
|
|
113
|
-
});
|
|
114
|
-
if (!proceed) {
|
|
115
|
-
this.log('\n Deploy cancelled.\n');
|
|
116
|
-
}
|
|
117
|
-
return proceed;
|
|
118
|
-
}
|
|
119
|
-
async ensureDomainReady(yamlConfig, flags) {
|
|
120
|
-
const domainVerify = await this.fetchDomainVerifyForDeploy(flags.json, yamlConfig.project?.domain);
|
|
121
|
-
if (domainVerify.ok && domainVerify.data?.domainStatus === 'VERIFIED') {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
if (!flags.json) {
|
|
125
|
-
this.log(`\n No sending domain verified yet.`);
|
|
126
|
-
this.log(` You need to verify a domain before sending emails.`);
|
|
127
|
-
this.log(` This is a one-time setup. Takes about 5 minutes.\n`);
|
|
128
|
-
}
|
|
129
|
-
if (!flags.yes) {
|
|
130
|
-
const setupNow = await confirm({
|
|
131
|
-
default: true,
|
|
132
|
-
message: 'Set up your sending domain now?',
|
|
133
|
-
});
|
|
134
|
-
if (!setupNow) {
|
|
135
|
-
this.log(`\n Sequences saved but ${chalk.yellow('NOT deployed')}.`);
|
|
136
|
-
this.log(` Emails will not send until your domain is verified.`);
|
|
137
|
-
this.log(` When ready, run: ${chalk.cyan('mailmodo domain')}`);
|
|
138
|
-
this.log(` Then: ${chalk.cyan('mailmodo deploy')}\n`);
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return this.runDomainSetup(yamlConfig, flags);
|
|
81
|
+
this.logDeploySuccessInstructions();
|
|
143
82
|
}
|
|
144
83
|
/**
|
|
145
84
|
* Lists emails about to be deployed (skipped when `--json` is set).
|
|
@@ -160,19 +99,19 @@ export default class Deploy extends BaseCommand {
|
|
|
160
99
|
/**
|
|
161
100
|
* Prints the post-deploy success message and SDK install snippet for interactive runs.
|
|
162
101
|
*/
|
|
163
|
-
logDeploySuccessInstructions(
|
|
102
|
+
logDeploySuccessInstructions() {
|
|
164
103
|
this.log(` ${chalk.green('Deployed.')} Emails are live.\n`);
|
|
165
104
|
this.log(` ${'─'.repeat(53)}`);
|
|
166
105
|
this.log(` ${chalk.bold('ADD THIS TO YOUR APP (one-time only):')}`);
|
|
167
106
|
this.log(` ${'─'.repeat(53)}\n`);
|
|
168
|
-
this.log(` ${chalk.cyan(
|
|
107
|
+
this.log(` ${chalk.cyan('npm install @mailmodo/sdk')}\n`);
|
|
169
108
|
this.log(` ${chalk.dim("import { track, identify } from '@mailmodo/sdk'")}\n`);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
this.log('');
|
|
109
|
+
this.log(` ${chalk.dim('// On user signup:')}`);
|
|
110
|
+
this.log(` ${chalk.dim("track('user.signup', { email, first_name, app_url })")}\n`);
|
|
111
|
+
this.log(` ${chalk.dim('// When user creates a project:')}`);
|
|
112
|
+
this.log(` ${chalk.dim('identify(email, { has_created_project: true })')}\n`);
|
|
113
|
+
this.log(` ${chalk.dim('// On trial expiry:')}`);
|
|
114
|
+
this.log(` ${chalk.dim("track('user.trial_expiry', { email, first_name })")}\n`);
|
|
176
115
|
this.log(` Full SDK docs: ${chalk.cyan('mailmodo.com/docs/sdk')}\n`);
|
|
177
116
|
this.log(` ${'─'.repeat(53)}\n`);
|
|
178
117
|
}
|
|
@@ -184,7 +123,31 @@ export default class Deploy extends BaseCommand {
|
|
|
184
123
|
* @returns {Promise<boolean>} true if domain was verified, false if skipped.
|
|
185
124
|
*/
|
|
186
125
|
async runDomainSetup(yamlConfig, flags) {
|
|
187
|
-
|
|
126
|
+
let domain;
|
|
127
|
+
let senderEmail;
|
|
128
|
+
let address;
|
|
129
|
+
if (flags.yes) {
|
|
130
|
+
domain = yamlConfig.project?.domain || '';
|
|
131
|
+
senderEmail = yamlConfig.project?.fromEmail || '';
|
|
132
|
+
address = yamlConfig.project?.address || '';
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.log(`\n ${'─'.repeat(53)}`);
|
|
136
|
+
this.log(` ${chalk.bold('DOMAIN SETUP')}`);
|
|
137
|
+
this.log(` ${'─'.repeat(53)}\n`);
|
|
138
|
+
domain = await input({
|
|
139
|
+
message: 'What domain will you send from?',
|
|
140
|
+
validate: (v) => (v?.trim() ? true : 'Domain is required'),
|
|
141
|
+
});
|
|
142
|
+
senderEmail = await input({
|
|
143
|
+
message: 'Sender email address:',
|
|
144
|
+
validate: (v) => v?.includes('@') ? true : 'Please enter a valid email',
|
|
145
|
+
});
|
|
146
|
+
address = await input({
|
|
147
|
+
message: 'Business address (required by law for email footers):',
|
|
148
|
+
validate: (v) => (v?.trim() ? true : 'Address is required'),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
188
151
|
const domainResponse = await this.withApiSpinner({ json: flags.json, text: ' Configuring domain...' }, () => this.apiClient.post(API_ENDPOINTS.DOMAIN, {
|
|
189
152
|
address,
|
|
190
153
|
domain,
|
|
@@ -197,9 +160,20 @@ export default class Deploy extends BaseCommand {
|
|
|
197
160
|
yamlConfig.project.fromEmail = senderEmail;
|
|
198
161
|
yamlConfig.project.address = address;
|
|
199
162
|
await saveYaml(yamlConfig);
|
|
200
|
-
|
|
163
|
+
const dnsRecords = domainResponse.data?.dnsRecords || [];
|
|
164
|
+
if (!flags.json) {
|
|
165
|
+
this.log(`\n Add these ${dnsRecords.length} DNS records to your domain provider:\n`);
|
|
166
|
+
for (const [i, record] of dnsRecords.entries()) {
|
|
167
|
+
this.log(` ${chalk.bold(`RECORD ${i + 1}`)}`);
|
|
168
|
+
this.log(` Type: ${record.type}`);
|
|
169
|
+
this.log(` Host: ${record.host}`);
|
|
170
|
+
this.log(` Value: ${record.value}\n`);
|
|
171
|
+
}
|
|
172
|
+
this.log(` DNS changes take 5–30 minutes to propagate.`);
|
|
173
|
+
this.log(` Full guide: ${chalk.cyan(DNS_GUIDE_URL)}\n`);
|
|
174
|
+
}
|
|
201
175
|
if (flags.yes) {
|
|
202
|
-
return this.verifyDomain(flags.json
|
|
176
|
+
return this.verifyDomain(flags.json);
|
|
203
177
|
}
|
|
204
178
|
const action = await input({
|
|
205
179
|
default: '',
|
|
@@ -211,65 +185,25 @@ export default class Deploy extends BaseCommand {
|
|
|
211
185
|
this.log(` Then: ${chalk.cyan('mailmodo deploy')}\n`);
|
|
212
186
|
return false;
|
|
213
187
|
}
|
|
214
|
-
return this.verifyDomain(flags.json
|
|
215
|
-
}
|
|
216
|
-
async collectDomainInputs(yamlConfig, flags) {
|
|
217
|
-
if (flags.yes) {
|
|
218
|
-
return {
|
|
219
|
-
address: yamlConfig.project?.address || '',
|
|
220
|
-
domain: yamlConfig.project?.domain || '',
|
|
221
|
-
senderEmail: yamlConfig.project?.fromEmail || '',
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
this.log(`\n ${'─'.repeat(53)}`);
|
|
225
|
-
this.log(` ${chalk.bold('DOMAIN SETUP')}`);
|
|
226
|
-
this.log(` ${'─'.repeat(53)}\n`);
|
|
227
|
-
const domain = await input({
|
|
228
|
-
message: 'What domain will you send from?',
|
|
229
|
-
validate: (v) => (v?.trim() ? true : 'Domain is required'),
|
|
230
|
-
});
|
|
231
|
-
const senderEmail = await input({
|
|
232
|
-
message: 'Sender email address:',
|
|
233
|
-
validate: (v) => (v?.includes('@') ? true : 'Please enter a valid email'),
|
|
234
|
-
});
|
|
235
|
-
const address = await input({
|
|
236
|
-
message: 'Business address (required by law for email footers):',
|
|
237
|
-
validate: (v) => (v?.trim() ? true : 'Address is required'),
|
|
238
|
-
});
|
|
239
|
-
return { address, domain, senderEmail };
|
|
240
|
-
}
|
|
241
|
-
showDnsRecords(dnsRecords, jsonOutput) {
|
|
242
|
-
if (jsonOutput)
|
|
243
|
-
return;
|
|
244
|
-
this.log(`\n Add these ${dnsRecords.length} DNS records to your domain provider:\n`);
|
|
245
|
-
for (const [i, record] of dnsRecords.entries()) {
|
|
246
|
-
this.log(` ${chalk.bold(`RECORD ${i + 1}`)}`);
|
|
247
|
-
this.log(` Type: ${record.type}`);
|
|
248
|
-
this.log(` Host: ${record.host}`);
|
|
249
|
-
this.log(` Value: ${record.value}\n`);
|
|
250
|
-
}
|
|
251
|
-
this.log(` DNS changes take 5–30 minutes to propagate.`);
|
|
252
|
-
this.log(` Full guide: ${chalk.cyan(DNS_GUIDE_URL)}\n`);
|
|
188
|
+
return this.verifyDomain(flags.json);
|
|
253
189
|
}
|
|
254
190
|
/**
|
|
255
191
|
* Calls the domain verification API endpoint and reports pass/fail
|
|
256
|
-
* status for each DNS record (DKIM, DMARC
|
|
192
|
+
* status for each DNS record (SPF, DKIM, DMARC).
|
|
257
193
|
*
|
|
258
194
|
* @returns {Promise<boolean>} true if all records pass.
|
|
259
195
|
*/
|
|
260
|
-
async verifyDomain(jsonOutput
|
|
261
|
-
const verify = await this.withApiSpinner({ json: jsonOutput, text: ' Checking DNS...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY
|
|
262
|
-
domain,
|
|
263
|
-
}));
|
|
196
|
+
async verifyDomain(jsonOutput) {
|
|
197
|
+
const verify = await this.withApiSpinner({ json: jsonOutput, text: ' Checking DNS...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY));
|
|
264
198
|
if (!verify.ok) {
|
|
265
199
|
this.handleApiError(verify);
|
|
266
200
|
}
|
|
267
|
-
const { dkim, dmarc,
|
|
268
|
-
const allPassed =
|
|
201
|
+
const { dkim, dmarc, spf } = verify.data;
|
|
202
|
+
const allPassed = spf === 'pass' && dkim === 'pass' && dmarc === 'pass';
|
|
269
203
|
if (!jsonOutput) {
|
|
270
|
-
this.log(`
|
|
271
|
-
this.log(`
|
|
272
|
-
this.log(`
|
|
204
|
+
this.log(` SPF ${spf === 'pass' ? chalk.green('✓') : chalk.red('✗')}`);
|
|
205
|
+
this.log(` DKIM ${dkim === 'pass' ? chalk.green('✓') : chalk.red('✗')}`);
|
|
206
|
+
this.log(` DMARC ${dmarc === 'pass' ? chalk.green('✓') : chalk.red('✗')}`);
|
|
273
207
|
if (allPassed) {
|
|
274
208
|
this.log(`\n ${chalk.green('Domain verified.')} Continuing deploy...\n`);
|
|
275
209
|
}
|
|
@@ -125,20 +125,19 @@ export default class Preview extends BaseCommand {
|
|
|
125
125
|
*/
|
|
126
126
|
async sendTestEmail(templateId, toAddress, jsonOutput) {
|
|
127
127
|
await this.ensureAuth();
|
|
128
|
-
const response = await this.withApiSpinner({ json: jsonOutput, text: ' Sending test email...' }, () => this.apiClient.post(`${API_ENDPOINTS.PREVIEW}/${templateId}/send`, {
|
|
128
|
+
const response = await this.withApiSpinner({ json: jsonOutput, text: ' Sending test email...' }, () => this.apiClient.post(`${API_ENDPOINTS.PREVIEW}/${templateId}/send`, {
|
|
129
|
+
to: toAddress,
|
|
130
|
+
}));
|
|
129
131
|
if (!response.ok) {
|
|
130
132
|
this.handleApiError(response);
|
|
131
133
|
}
|
|
132
|
-
const { note, sentTo, sentVia, status } = response.data;
|
|
133
134
|
if (jsonOutput) {
|
|
134
|
-
this.log(JSON.stringify({
|
|
135
|
+
this.log(JSON.stringify({ templateId, sentTo: toAddress, status: 'sent' }, null, 2));
|
|
135
136
|
return;
|
|
136
137
|
}
|
|
137
|
-
this.log(`\n
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
this.log('');
|
|
138
|
+
this.log(`\n Sending test to ${chalk.cyan(toAddress)}...`);
|
|
139
|
+
this.log(` ${chalk.dim('Note: If domain is not yet verified, test sends via mailmodo.com domain.')}`);
|
|
140
|
+
this.log(` ${chalk.green('✓')} Sent.\n`);
|
|
142
141
|
}
|
|
143
142
|
/**
|
|
144
143
|
* Starts a local HTTP server on PREVIEW_PORT to serve the rendered email
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -17,8 +17,6 @@ export declare const API_ENDPOINTS: Readonly<{
|
|
|
17
17
|
LOGS: "/logs";
|
|
18
18
|
PREVIEW: "/preview";
|
|
19
19
|
SEQUENCES: "/sequences";
|
|
20
|
-
SEQUENCES_DEPLOY: "/sequences/deploy";
|
|
21
|
-
SEQUENCES_VALIDATE: "/sequences/validate";
|
|
22
20
|
}>;
|
|
23
21
|
export declare const LOGIN_URL = "https://app-vertex-debug.azurewebsites.net/login.html";
|
|
24
22
|
export declare const DOCS_URL = "https://mailmodo.com/docs/cli";
|
package/dist/lib/constants.js
CHANGED
|
@@ -23,8 +23,6 @@ export const API_ENDPOINTS = Object.freeze({
|
|
|
23
23
|
LOGS: '/logs',
|
|
24
24
|
PREVIEW: '/preview',
|
|
25
25
|
SEQUENCES: '/sequences',
|
|
26
|
-
SEQUENCES_DEPLOY: '/sequences/deploy',
|
|
27
|
-
SEQUENCES_VALIDATE: '/sequences/validate',
|
|
28
26
|
});
|
|
29
27
|
const DEV_LOGIN_URL = 'https://app-vertex-debug.azurewebsites.net/login.html';
|
|
30
28
|
// const PRODUCTION_LOGIN_URL = 'https://mailmodo.com/cli';
|
package/oclif.manifest.json
CHANGED
|
@@ -473,14 +473,19 @@
|
|
|
473
473
|
"index.js"
|
|
474
474
|
]
|
|
475
475
|
},
|
|
476
|
-
"
|
|
476
|
+
"preview": {
|
|
477
477
|
"aliases": [],
|
|
478
|
-
"args": {
|
|
479
|
-
|
|
478
|
+
"args": {
|
|
479
|
+
"id": {
|
|
480
|
+
"description": "Email template ID to preview",
|
|
481
|
+
"name": "id"
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
"description": "Preview an email in browser, as text, or send a test",
|
|
480
485
|
"examples": [
|
|
481
|
-
"<%= config.bin %>
|
|
482
|
-
"<%= config.bin %>
|
|
483
|
-
"<%= config.bin %>
|
|
486
|
+
"<%= config.bin %> preview welcome",
|
|
487
|
+
"<%= config.bin %> preview welcome --text",
|
|
488
|
+
"<%= config.bin %> preview welcome --send me@example.com"
|
|
484
489
|
],
|
|
485
490
|
"flags": {
|
|
486
491
|
"json": {
|
|
@@ -496,17 +501,23 @@
|
|
|
496
501
|
"allowNo": false,
|
|
497
502
|
"type": "boolean"
|
|
498
503
|
},
|
|
499
|
-
"
|
|
500
|
-
"description": "
|
|
501
|
-
"name": "
|
|
504
|
+
"send": {
|
|
505
|
+
"description": "Send test email to this address",
|
|
506
|
+
"name": "send",
|
|
502
507
|
"hasDynamicHelp": false,
|
|
503
508
|
"multiple": false,
|
|
504
509
|
"type": "option"
|
|
510
|
+
},
|
|
511
|
+
"text": {
|
|
512
|
+
"description": "Output plain text version (for AI agents)",
|
|
513
|
+
"name": "text",
|
|
514
|
+
"allowNo": false,
|
|
515
|
+
"type": "boolean"
|
|
505
516
|
}
|
|
506
517
|
},
|
|
507
518
|
"hasDynamicHelp": false,
|
|
508
519
|
"hiddenAliases": [],
|
|
509
|
-
"id": "
|
|
520
|
+
"id": "preview",
|
|
510
521
|
"pluginAlias": "@mailmodo/cli",
|
|
511
522
|
"pluginName": "@mailmodo/cli",
|
|
512
523
|
"pluginType": "core",
|
|
@@ -516,23 +527,18 @@
|
|
|
516
527
|
"relativePath": [
|
|
517
528
|
"dist",
|
|
518
529
|
"commands",
|
|
519
|
-
"
|
|
530
|
+
"preview",
|
|
520
531
|
"index.js"
|
|
521
532
|
]
|
|
522
533
|
},
|
|
523
|
-
"
|
|
534
|
+
"settings": {
|
|
524
535
|
"aliases": [],
|
|
525
|
-
"args": {
|
|
526
|
-
|
|
527
|
-
"description": "Email template ID to preview",
|
|
528
|
-
"name": "id"
|
|
529
|
-
}
|
|
530
|
-
},
|
|
531
|
-
"description": "Preview an email in browser, as text, or send a test",
|
|
536
|
+
"args": {},
|
|
537
|
+
"description": "View and update project settings",
|
|
532
538
|
"examples": [
|
|
533
|
-
"<%= config.bin %>
|
|
534
|
-
"<%= config.bin %>
|
|
535
|
-
"<%= config.bin %>
|
|
539
|
+
"<%= config.bin %> settings",
|
|
540
|
+
"<%= config.bin %> settings --set brand_color=#0F3460",
|
|
541
|
+
"<%= config.bin %> settings --json"
|
|
536
542
|
],
|
|
537
543
|
"flags": {
|
|
538
544
|
"json": {
|
|
@@ -548,23 +554,17 @@
|
|
|
548
554
|
"allowNo": false,
|
|
549
555
|
"type": "boolean"
|
|
550
556
|
},
|
|
551
|
-
"
|
|
552
|
-
"description": "
|
|
553
|
-
"name": "
|
|
557
|
+
"set": {
|
|
558
|
+
"description": "Set a setting (format: key=value)",
|
|
559
|
+
"name": "set",
|
|
554
560
|
"hasDynamicHelp": false,
|
|
555
561
|
"multiple": false,
|
|
556
562
|
"type": "option"
|
|
557
|
-
},
|
|
558
|
-
"text": {
|
|
559
|
-
"description": "Output plain text version (for AI agents)",
|
|
560
|
-
"name": "text",
|
|
561
|
-
"allowNo": false,
|
|
562
|
-
"type": "boolean"
|
|
563
563
|
}
|
|
564
564
|
},
|
|
565
565
|
"hasDynamicHelp": false,
|
|
566
566
|
"hiddenAliases": [],
|
|
567
|
-
"id": "
|
|
567
|
+
"id": "settings",
|
|
568
568
|
"pluginAlias": "@mailmodo/cli",
|
|
569
569
|
"pluginName": "@mailmodo/cli",
|
|
570
570
|
"pluginType": "core",
|
|
@@ -574,7 +574,7 @@
|
|
|
574
574
|
"relativePath": [
|
|
575
575
|
"dist",
|
|
576
576
|
"commands",
|
|
577
|
-
"
|
|
577
|
+
"settings",
|
|
578
578
|
"index.js"
|
|
579
579
|
]
|
|
580
580
|
},
|
|
@@ -618,5 +618,5 @@
|
|
|
618
618
|
]
|
|
619
619
|
}
|
|
620
620
|
},
|
|
621
|
-
"version": "0.0.19
|
|
621
|
+
"version": "0.0.19"
|
|
622
622
|
}
|