@mailmodo/cli 0.0.32 → 0.0.33-beta.pr35.58

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.
@@ -21,7 +21,5 @@ export default class Deploy extends BaseCommand {
21
21
  private logDiff;
22
22
  private logDeploySuccessInstructions;
23
23
  private runDomainSetup;
24
- private collectDomainInputs;
25
- private showDnsRecords;
26
24
  private verifyDomain;
27
25
  }
@@ -2,8 +2,8 @@ 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
- import { loadTemplate, saveYaml, } from '../../lib/yaml-config.js';
5
+ import { ERRORS, INFO, PROMPTS, SEPARATOR } from '../../lib/messages.js';
6
+ import { loadTemplate, } from '../../lib/yaml-config.js';
7
7
  export default class Deploy extends BaseCommand {
8
8
  static description = 'Deploy email sequences and verify sending domain';
9
9
  static examples = [
@@ -220,72 +220,22 @@ export default class Deploy extends BaseCommand {
220
220
  this.log(` ${SEPARATOR}\n`);
221
221
  }
222
222
  async runDomainSetup(yamlConfig, flags) {
223
- const { address, domain, senderEmail } = await this.collectDomainInputs(yamlConfig, flags);
224
- const domainResponse = await this.withApiSpinner({ json: flags.json, text: ' Configuring domain...' }, () => this.apiClient.post(API_ENDPOINTS.DOMAIN, {
225
- address,
226
- domain,
227
- fromEmail: senderEmail,
228
- }));
229
- if (!domainResponse.ok) {
230
- this.handleApiError(domainResponse);
231
- }
232
- yamlConfig.project.domain = domain;
233
- yamlConfig.project.fromEmail = senderEmail;
234
- yamlConfig.project.address = address;
235
- await saveYaml(yamlConfig);
236
- this.showDnsRecords(domainResponse.data?.dnsRecords || [], flags.json, domainResponse.data?.dnsGuideUrl);
223
+ const inputs = await this.collectDomainSetupInputs(yamlConfig, flags.yes);
224
+ const { dnsRecords, dnsGuideUrl } = await this.registerDomain(yamlConfig, inputs, flags.json);
225
+ this.logDnsRecords(dnsRecords, dnsGuideUrl, flags.json);
237
226
  if (flags.yes) {
238
- return this.verifyDomain(flags.json, domain);
227
+ return this.verifyDomain(flags.json, inputs.domain);
239
228
  }
240
229
  const action = await input({
241
230
  default: '',
242
- message: "Press Enter once you've added the records, or 'skip' to do this later.",
231
+ message: PROMPTS.ENTER_AFTER_RECORDS,
243
232
  });
244
233
  if (action.toLowerCase() === 'skip') {
245
234
  this.log(`\n ${INFO.SEQUENCES_NOT_DEPLOYED}`);
246
235
  this.log(` ${INFO.DOMAIN_NOT_DEPLOYED_HINT}\n`);
247
236
  return false;
248
237
  }
249
- return this.verifyDomain(flags.json, domain);
250
- }
251
- async collectDomainInputs(yamlConfig, flags) {
252
- if (flags.yes) {
253
- return {
254
- address: yamlConfig.project?.address || '',
255
- domain: yamlConfig.project?.domain || '',
256
- senderEmail: yamlConfig.project?.fromEmail || '',
257
- };
258
- }
259
- this.log(`\n ${SEPARATOR}`);
260
- this.log(` ${chalk.bold('DOMAIN SETUP')}`);
261
- this.log(` ${SEPARATOR}\n`);
262
- const domain = await input({
263
- message: PROMPTS.DOMAIN,
264
- validate: (v) => (v?.trim() ? true : VALIDATION.DOMAIN_REQUIRED),
265
- });
266
- const senderEmail = await input({
267
- message: PROMPTS.SENDER_EMAIL,
268
- validate: (v) => (v?.includes('@') ? true : VALIDATION.EMAIL_INVALID),
269
- });
270
- const address = await input({
271
- message: PROMPTS.BUSINESS_ADDRESS,
272
- validate: (v) => (v?.trim() ? true : VALIDATION.ADDRESS_REQUIRED),
273
- });
274
- return { address, domain, senderEmail };
275
- }
276
- showDnsRecords(dnsRecords, jsonOutput, dnsGuideUrl) {
277
- if (jsonOutput)
278
- return;
279
- this.log(`\n Add these ${dnsRecords.length} DNS records to your domain provider:\n`);
280
- for (const [i, record] of dnsRecords.entries()) {
281
- this.log(` ${chalk.bold(`RECORD ${i + 1}`)}`);
282
- this.log(` Type: ${record.type}`);
283
- this.log(` Host: ${record.host}`);
284
- this.log(` Value: ${record.value}\n`);
285
- }
286
- this.log(` ${INFO.DNS_PROPAGATION}`);
287
- if (dnsGuideUrl)
288
- this.log(` Full guide: ${chalk.cyan(dnsGuideUrl)}\n`);
238
+ return this.verifyDomain(flags.json, inputs.domain);
289
239
  }
290
240
  async verifyDomain(jsonOutput, domain) {
291
241
  const verify = await this.withApiSpinner({ json: jsonOutput, text: ' Checking DNS...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY, {
@@ -23,10 +23,9 @@ export default class Login extends BaseCommand {
23
23
  if (existing?.apiKey) {
24
24
  if (flags.json) {
25
25
  this.log(JSON.stringify({
26
- accountName: existing.accountName ?? null,
27
26
  email: existing.email ?? null,
28
- freeRemaining: existing.freeRemaining ?? null,
29
27
  status: 'already_logged_in',
28
+ totalFreeRemaining: existing.totalFreeRemaining ?? null,
30
29
  }, null, 2));
31
30
  return;
32
31
  }
@@ -68,19 +67,20 @@ export default class Login extends BaseCommand {
68
67
  if (!response.ok) {
69
68
  this.handleApiError(response);
70
69
  }
71
- const { accountName, email, freeRemaining } = response.data;
70
+ const { email, totalFreeRemaining } = response.data;
72
71
  await saveConfig({
73
- accountName,
74
72
  apiKey: trimmedKey,
75
73
  email,
76
- freeRemaining,
74
+ totalFreeRemaining,
77
75
  });
78
76
  if (flags.json) {
79
- this.log(JSON.stringify({ accountName, email, freeRemaining, status: 'authenticated' }, null, 2));
77
+ this.log(JSON.stringify({ email, status: 'authenticated', totalFreeRemaining }, null, 2));
80
78
  return;
81
79
  }
82
80
  this.log(`\n Logged in as ${chalk.green(email)}`);
83
- this.log(` Free tier: ${chalk.cyan(String(freeRemaining ?? 1000))} emails remaining`);
81
+ if (totalFreeRemaining !== null && totalFreeRemaining !== undefined) {
82
+ this.log(` Free tier: ${chalk.cyan(String(totalFreeRemaining))} emails remaining`);
83
+ }
84
84
  this.log(' No credit card required.\n');
85
85
  this.log(` Next: Run ${chalk.cyan("'mailmodo init'")} to generate your email sequences.\n`);
86
86
  }
@@ -38,13 +38,18 @@ export default class Status extends BaseCommand {
38
38
  this.log(` ${chalk.dim('No data yet. Deploy emails first.')}`);
39
39
  }
40
40
  this.log('');
41
- this.log(` Emails sent this month: ${chalk.bold(String(monthlySent ?? 0))}`);
41
+ if (monthlySent !== null && monthlySent !== undefined) {
42
+ this.log(` Emails sent this month: ${chalk.bold(String(monthlySent))}`);
43
+ }
42
44
  if (quota) {
43
45
  if (quota.freeRemaining > 0) {
44
46
  this.log(` Free tier remaining: ${chalk.cyan(String(quota.freeRemaining))}`);
45
47
  }
46
48
  else if (quota.blocksUsed !== undefined) {
47
- this.log(` Current paid block: ${chalk.cyan(`${quota.currentBlockRemaining ?? 0} / 10,000 used`)}`);
49
+ if (quota.currentBlockRemaining !== null &&
50
+ quota.currentBlockRemaining !== undefined) {
51
+ this.log(` Current paid block: ${chalk.cyan(`${quota.currentBlockRemaining} / 10,000 used`)}`);
52
+ }
48
53
  this.log(` Blocks purchased: ${quota.blocksUsed}`);
49
54
  }
50
55
  }
@@ -1,15 +1,14 @@
1
1
  export interface MailmodoConfig {
2
- accountName?: string;
3
2
  apiKey: string;
4
3
  email?: string;
5
- freeRemaining?: number;
4
+ totalFreeRemaining?: number;
6
5
  }
7
6
  /**
8
7
  * Loads the Mailmodo CLI configuration from ~/.mailmodo/config.
9
8
  * The config file stores the API key and account metadata as JSON.
10
9
  *
11
10
  * @returns {Promise<MailmodoConfig | null>} The parsed configuration object
12
- * containing apiKey, email, accountName, and freeRemaining quota,
11
+ * containing apiKey, email, and totalFreeRemaining quota,
13
12
  * or null if the config file does not exist or is corrupted.
14
13
  */
15
14
  export declare function loadConfig(): Promise<MailmodoConfig | null>;
@@ -19,7 +18,7 @@ export declare function loadConfig(): Promise<MailmodoConfig | null>;
19
18
  * Overwrites any existing config file.
20
19
  *
21
20
  * @param {MailmodoConfig} config - The configuration to persist, must include
22
- * at minimum an apiKey. Optional fields: email, accountName, freeRemaining.
21
+ * at minimum an apiKey. Optional fields: email, totalFreeRemaining.
23
22
  */
24
23
  export declare function saveConfig(config: MailmodoConfig): Promise<void>;
25
24
  /**
@@ -9,7 +9,7 @@ const CONFIG_FILE = join(CONFIG_DIR, 'config');
9
9
  * The config file stores the API key and account metadata as JSON.
10
10
  *
11
11
  * @returns {Promise<MailmodoConfig | null>} The parsed configuration object
12
- * containing apiKey, email, accountName, and freeRemaining quota,
12
+ * containing apiKey, email, and totalFreeRemaining quota,
13
13
  * or null if the config file does not exist or is corrupted.
14
14
  */
15
15
  export async function loadConfig() {
@@ -29,7 +29,7 @@ export async function loadConfig() {
29
29
  * Overwrites any existing config file.
30
30
  *
31
31
  * @param {MailmodoConfig} config - The configuration to persist, must include
32
- * at minimum an apiKey. Optional fields: email, accountName, freeRemaining.
32
+ * at minimum an apiKey. Optional fields: email, totalFreeRemaining.
33
33
  */
34
34
  export async function saveConfig(config) {
35
35
  if (!existsSync(CONFIG_DIR)) {
@@ -512,14 +512,19 @@
512
512
  "index.js"
513
513
  ]
514
514
  },
515
- "settings": {
515
+ "preview": {
516
516
  "aliases": [],
517
- "args": {},
518
- "description": "View and update project settings",
517
+ "args": {
518
+ "id": {
519
+ "description": "Email template ID to preview",
520
+ "name": "id"
521
+ }
522
+ },
523
+ "description": "Preview an email in browser, as text, or send a test",
519
524
  "examples": [
520
- "<%= config.bin %> settings",
521
- "<%= config.bin %> settings --set brand_color=#0F3460",
522
- "<%= config.bin %> settings --json"
525
+ "<%= config.bin %> preview welcome",
526
+ "<%= config.bin %> preview welcome --text",
527
+ "<%= config.bin %> preview welcome --send me@example.com"
523
528
  ],
524
529
  "flags": {
525
530
  "json": {
@@ -535,17 +540,23 @@
535
540
  "allowNo": false,
536
541
  "type": "boolean"
537
542
  },
538
- "set": {
539
- "description": "Set a setting (format: key=value)",
540
- "name": "set",
543
+ "send": {
544
+ "description": "Send test email to this address",
545
+ "name": "send",
541
546
  "hasDynamicHelp": false,
542
547
  "multiple": false,
543
548
  "type": "option"
549
+ },
550
+ "text": {
551
+ "description": "Output plain text version (for AI agents)",
552
+ "name": "text",
553
+ "allowNo": false,
554
+ "type": "boolean"
544
555
  }
545
556
  },
546
557
  "hasDynamicHelp": false,
547
558
  "hiddenAliases": [],
548
- "id": "settings",
559
+ "id": "preview",
549
560
  "pluginAlias": "@mailmodo/cli",
550
561
  "pluginName": "@mailmodo/cli",
551
562
  "pluginType": "core",
@@ -555,23 +566,18 @@
555
566
  "relativePath": [
556
567
  "dist",
557
568
  "commands",
558
- "settings",
569
+ "preview",
559
570
  "index.js"
560
571
  ]
561
572
  },
562
- "preview": {
573
+ "settings": {
563
574
  "aliases": [],
564
- "args": {
565
- "id": {
566
- "description": "Email template ID to preview",
567
- "name": "id"
568
- }
569
- },
570
- "description": "Preview an email in browser, as text, or send a test",
575
+ "args": {},
576
+ "description": "View and update project settings",
571
577
  "examples": [
572
- "<%= config.bin %> preview welcome",
573
- "<%= config.bin %> preview welcome --text",
574
- "<%= config.bin %> preview welcome --send me@example.com"
578
+ "<%= config.bin %> settings",
579
+ "<%= config.bin %> settings --set brand_color=#0F3460",
580
+ "<%= config.bin %> settings --json"
575
581
  ],
576
582
  "flags": {
577
583
  "json": {
@@ -587,23 +593,17 @@
587
593
  "allowNo": false,
588
594
  "type": "boolean"
589
595
  },
590
- "send": {
591
- "description": "Send test email to this address",
592
- "name": "send",
596
+ "set": {
597
+ "description": "Set a setting (format: key=value)",
598
+ "name": "set",
593
599
  "hasDynamicHelp": false,
594
600
  "multiple": false,
595
601
  "type": "option"
596
- },
597
- "text": {
598
- "description": "Output plain text version (for AI agents)",
599
- "name": "text",
600
- "allowNo": false,
601
- "type": "boolean"
602
602
  }
603
603
  },
604
604
  "hasDynamicHelp": false,
605
605
  "hiddenAliases": [],
606
- "id": "preview",
606
+ "id": "settings",
607
607
  "pluginAlias": "@mailmodo/cli",
608
608
  "pluginName": "@mailmodo/cli",
609
609
  "pluginType": "core",
@@ -613,7 +613,7 @@
613
613
  "relativePath": [
614
614
  "dist",
615
615
  "commands",
616
- "preview",
616
+ "settings",
617
617
  "index.js"
618
618
  ]
619
619
  },
@@ -657,5 +657,5 @@
657
657
  ]
658
658
  }
659
659
  },
660
- "version": "0.0.32"
660
+ "version": "0.0.33-beta.pr35.58"
661
661
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mailmodo/cli",
3
3
  "description": "Email lifecycle automation for the AI-native builder generation.",
4
- "version": "0.0.32",
4
+ "version": "0.0.33-beta.pr35.58",
5
5
  "author": "provishalk",
6
6
  "bin": {
7
7
  "mailmodo": "bin/run.js"