@mailmodo/cli 0.0.54-beta.pr56.87 → 0.0.54-beta.pr56.89

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.
@@ -1,12 +1,14 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { confirm } from '@inquirer/prompts';
3
+ import chalk from 'chalk';
3
4
  import { BaseCommand } from '../../lib/base-command.js';
4
5
  import { API_ENDPOINTS } from '../../lib/constants.js';
6
+ import { MISSING_TEMPLATES } from '../../lib/messages.js';
5
7
  import { buildDeployPayload } from '../../lib/deploy/payload.js';
6
8
  import { logDeploySuccessInstructions, logPreDeploySummary, } from '../../lib/deploy/output.js';
7
9
  import { pauseSequence, resumeSequence, } from '../../lib/deploy/sequence-status.js';
8
10
  import { ensureDomainReady, validateDeploySequence, } from '../../lib/deploy/domain-setup.js';
9
- import { getMissingTemplateIds, handleMissingTemplates, } from '../../lib/deploy/missing-templates.js';
11
+ import { getMissingTemplateIds, handleMissingTemplates, } from '../../lib/templates/missing-templates.js';
10
12
  export default class Deploy extends BaseCommand {
11
13
  static description = 'Deploy, pause, or resume an email sequence';
12
14
  static examples = [
@@ -42,7 +44,9 @@ export default class Deploy extends BaseCommand {
42
44
  const yamlConfig = await this.ensureYaml();
43
45
  const missingIds = getMissingTemplateIds(yamlConfig);
44
46
  if (missingIds.length > 0) {
45
- await handleMissingTemplates(ctx, yamlConfig, missingIds, baseFlags);
47
+ const regenerated = await handleMissingTemplates(ctx, yamlConfig, missingIds, baseFlags);
48
+ if (regenerated)
49
+ ctx.log(`\n ${chalk.green('✓')} ${MISSING_TEMPLATES.REVIEW_HINT}\n`);
46
50
  return;
47
51
  }
48
52
  const domainReady = await ensureDomainReady(ctx, yamlConfig, baseFlags);
@@ -49,7 +49,7 @@ export default class Login extends BaseCommand {
49
49
  else {
50
50
  this.log(` ${chalk.dim('1.')} Run ${chalk.cyan('mailmodo init')} to generate an email sequence.`);
51
51
  }
52
- this.log(` ${chalk.dim('2.')} Run ${chalk.cyan('mailmodo logout')} to log in with another account.\n`);
52
+ this.log(` ${chalk.dim(yamlRestored ? '1.' : '2.')} Run ${chalk.cyan('mailmodo logout')} to log in with another account.\n`);
53
53
  return;
54
54
  }
55
55
  }
@@ -6,6 +6,7 @@ import { BaseCommand } from '../../lib/base-command.js';
6
6
  import { API_ENDPOINTS, PREVIEW_PORT } from '../../lib/constants.js';
7
7
  import { INFO } from '../../lib/messages.js';
8
8
  import { loadTemplate, getEmailStyle, getTemplateFilename, } from '../../lib/yaml-config.js';
9
+ import { handleMissingTemplates } from '../../lib/templates/missing-templates.js';
9
10
  /* eslint-disable camelcase */
10
11
  const SAMPLE_DATA = Object.freeze({
11
12
  app_url: 'https://yourapp.com',
@@ -86,12 +87,28 @@ export default class Preview extends BaseCommand {
86
87
  product_name: yamlConfig.project?.name || 'YourApp', // eslint-disable-line camelcase
87
88
  };
88
89
  const effectiveStyle = getEmailStyle(email.style, yamlConfig.project?.emailStyle);
89
- const templateHtml = await loadTemplate(getTemplateFilename(email.id, email.style, yamlConfig.project?.emailStyle));
90
+ const templateFilename = getTemplateFilename(email.id, email.style, yamlConfig.project?.emailStyle);
91
+ let templateHtml = await loadTemplate(templateFilename);
92
+ if (!templateHtml) {
93
+ await this.ensureAuth();
94
+ const regenCtx = {
95
+ error: (msg) => this.error(msg),
96
+ exit: (code) => this.exit(code),
97
+ log: (msg) => this.log(msg),
98
+ onApiError: (r) => this.handleApiError(r),
99
+ post: (path, body) => this.apiClient.post(path, body),
100
+ spinner: (text, json, work) => this.withApiSpinner({ json, text }, work),
101
+ syncYaml: () => this.syncYamlToServer(),
102
+ };
103
+ const regenerated = await handleMissingTemplates(regenCtx, yamlConfig, [email.id], { json: flags.json, yes: flags.yes });
104
+ if (!regenerated)
105
+ return;
106
+ templateHtml = await loadTemplate(templateFilename);
107
+ if (!templateHtml)
108
+ this.error('Template regeneration failed.');
109
+ }
90
110
  if (flags.send) {
91
- const rendered = templateHtml
92
- ? renderTemplate(templateHtml, sampleData)
93
- : '';
94
- await this.sendTestEmail(email, rendered, {
111
+ await this.sendTestEmail(email, renderTemplate(templateHtml, sampleData), {
95
112
  domain: yamlConfig.project?.domain,
96
113
  jsonOutput: flags.json,
97
114
  toAddress: flags.send,
@@ -112,10 +129,7 @@ export default class Preview extends BaseCommand {
112
129
  * and CI pipelines that cannot open a browser.
113
130
  */
114
131
  async renderText(email, templateHtml, sampleData, jsonOutput) {
115
- const rendered = templateHtml
116
- ? renderTemplate(templateHtml, sampleData)
117
- : '';
118
- const plainText = htmlToText(rendered);
132
+ const plainText = htmlToText(renderTemplate(templateHtml, sampleData));
119
133
  if (jsonOutput) {
120
134
  this.log(JSON.stringify({
121
135
  body: plainText,
@@ -181,9 +195,7 @@ export default class Preview extends BaseCommand {
181
195
  */
182
196
  async startPreviewServer(email, templateHtml, sampleData, opts) {
183
197
  const { effectiveStyle, jsonOutput } = opts;
184
- const rendered = templateHtml
185
- ? renderTemplate(templateHtml, sampleData)
186
- : '<p>No template found.</p>';
198
+ const rendered = renderTemplate(templateHtml, sampleData);
187
199
  const wrapperHtml = `<!DOCTYPE html>
188
200
  <html>
189
201
  <head>
@@ -68,6 +68,7 @@ export declare abstract class BaseCommand extends Command {
68
68
  * settings and all email sequence definitions.
69
69
  */
70
70
  protected ensureYaml(): Promise<MailmodoYaml>;
71
+ private fetchAndWriteYaml;
71
72
  /**
72
73
  * Attempts to fetch mailmodo.yaml from the server and save it locally.
73
74
  * Returns null silently on any failure so callers can fall through to an error.
@@ -95,20 +95,36 @@ export class BaseCommand extends Command {
95
95
  return restored;
96
96
  this.error(ERRORS.NO_YAML);
97
97
  }
98
+ async fetchAndWriteYaml(client) {
99
+ try {
100
+ const response = await client.getRawText(API_ENDPOINTS.ASSETS_YAML);
101
+ if (!response.ok || !response.data)
102
+ return false;
103
+ await writeFile(join(process.cwd(), YAML_FILE), response.data);
104
+ return true;
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
98
110
  /**
99
111
  * Attempts to fetch mailmodo.yaml from the server and save it locally.
100
112
  * Returns null silently on any failure so callers can fall through to an error.
101
113
  */
102
114
  async restoreYamlFromServer() {
103
- if (!this.apiClient)
104
- return null;
105
115
  try {
106
- const response = await this.apiClient.getRawText(API_ENDPOINTS.ASSETS_YAML);
107
- if (!response.ok || !response.data)
116
+ let client = this.apiClient;
117
+ if (!client) {
118
+ const envKey = process.env.MAILMODO_API_KEY;
119
+ const apiKey = envKey ?? (await loadConfig())?.apiKey;
120
+ if (!apiKey)
121
+ return null;
122
+ client = new ApiClient(apiKey);
123
+ }
124
+ const written = await this.fetchAndWriteYaml(client);
125
+ if (!written)
108
126
  return null;
109
- const filePath = join(process.cwd(), YAML_FILE);
110
- await writeFile(filePath, response.data);
111
- this.log(INFO.YAML_RESTORED_FROM_SERVER);
127
+ this.logToStderr(INFO.YAML_RESTORED_FROM_SERVER);
112
128
  return loadYaml();
113
129
  }
114
130
  catch {
@@ -127,16 +143,7 @@ export class BaseCommand extends Command {
127
143
  async recoverYamlAfterLogin(client) {
128
144
  if (existsSync(join(process.cwd(), YAML_FILE)))
129
145
  return false;
130
- try {
131
- const response = await client.getRawText(API_ENDPOINTS.ASSETS_YAML);
132
- if (!response.ok || !response.data)
133
- return false;
134
- await writeFile(join(process.cwd(), YAML_FILE), response.data);
135
- return true;
136
- }
137
- catch {
138
- return false;
139
- }
146
+ return this.fetchAndWriteYaml(client);
140
147
  }
141
148
  /**
142
149
  * Uploads the current local mailmodo.yaml to the server as a backup.
@@ -54,7 +54,9 @@ export async function ensureDomainReady(ctx, yamlConfig, flags) {
54
54
  const check = await ctx.spinner(' Checking domain verification...', flags.json, () => ctx.get(API_ENDPOINTS.DOMAIN_VERIFY, {
55
55
  domain: yamlConfig.project?.domain || '',
56
56
  }));
57
- if (check.ok && check.data?.domainStatus === 'VERIFIED')
57
+ if (!check.ok)
58
+ ctx.onApiError(check);
59
+ if (check.data?.domainStatus === 'VERIFIED')
58
60
  return true;
59
61
  if (yamlConfig.project?.domain) {
60
62
  if (!flags.json)
@@ -1,12 +1,12 @@
1
1
  import chalk from 'chalk';
2
2
  import { SDK_IMPORT_SNIPPET, SDK_INSTALL_COMMAND } from '../constants.js';
3
- import { SEPARATOR } from '../messages.js';
3
+ import { DEPLOY, SEPARATOR } from '../messages.js';
4
4
  export function logDiff(ctx, diff) {
5
5
  if (!diff.hasChanges) {
6
- ctx.log(` No changes from last deployment.`);
6
+ ctx.log(` ${DEPLOY.NO_CHANGES}`);
7
7
  return;
8
8
  }
9
- ctx.log(` Changes vs. last deployment:`);
9
+ ctx.log(` ${DEPLOY.CHANGES_HEADER}`);
10
10
  for (const email of diff.added) {
11
11
  ctx.log(` ${chalk.green('+')} ${email.id.padEnd(24)} ${email.trigger || ''}`);
12
12
  }
@@ -24,7 +24,7 @@ export function logPreDeploySummary(ctx, yamlConfig, validateResult, jsonOutput)
24
24
  return;
25
25
  ctx.log(`\n ${chalk.green('✓')} Domain: ${yamlConfig.project?.domain || 'verified'}\n`);
26
26
  if (!validateResult.existingDeployment || !validateResult.diff) {
27
- ctx.log(` Deploying:`);
27
+ ctx.log(` ${DEPLOY.DEPLOYING_HEADER}`);
28
28
  for (const email of yamlConfig.emails) {
29
29
  ctx.log(` ${chalk.green('+')} ${email.id.padEnd(24)} ${email.trigger}`);
30
30
  }
@@ -35,14 +35,14 @@ export function logPreDeploySummary(ctx, yamlConfig, validateResult, jsonOutput)
35
35
  ctx.log('');
36
36
  }
37
37
  export function logDeploySuccessInstructions(ctx, sdkSnippet) {
38
- ctx.log(` ${chalk.green('Deployed.')} Emails are live.\n`);
38
+ ctx.log(` ${DEPLOY.SUCCESS}\n`);
39
39
  ctx.log(` ${SEPARATOR}`);
40
- ctx.log(` ${chalk.bold('ADD THIS TO YOUR APP (one-time only):')}`);
40
+ ctx.log(` ${DEPLOY.SDK_ONBOARDING_HEADER}`);
41
41
  ctx.log(` ${SEPARATOR}\n`);
42
42
  ctx.log(` ${chalk.cyan(sdkSnippet.install ?? SDK_INSTALL_COMMAND)}\n`);
43
43
  ctx.log(` ${chalk.dim(SDK_IMPORT_SNIPPET)}\n`);
44
44
  if (sdkSnippet.examples) {
45
- ctx.log(` ${chalk.dim('// Example usage:')}`);
45
+ ctx.log(` ${DEPLOY.SDK_EXAMPLE_COMMENT}`);
46
46
  ctx.log(` ${chalk.dim(sdkSnippet.examples.track)}`);
47
47
  ctx.log(` ${chalk.dim(sdkSnippet.examples.identify)}\n`);
48
48
  }
@@ -56,6 +56,6 @@ export function logDeploySuccessInstructions(ctx, sdkSnippet) {
56
56
  ctx.log(` ${chalk.dim(call)}`);
57
57
  if (identifyCalls.length > 0)
58
58
  ctx.log('');
59
- ctx.log(` Full SDK docs: ${chalk.cyan('mailmodo.com/docs/sdk')}\n`);
59
+ ctx.log(` ${DEPLOY.SDK_DOCS_HINT}\n`);
60
60
  ctx.log(` ${SEPARATOR}\n`);
61
61
  }
@@ -1,5 +1,6 @@
1
1
  import type { ApiResponse } from '../api-client.js';
2
2
  import type { MailmodoYaml } from '../yaml-config.js';
3
+ import type { RegenCtx } from '../templates/types.js';
3
4
  export interface EmailDiffEntry {
4
5
  changedFields?: string[];
5
6
  id: string;
@@ -44,7 +45,7 @@ export type DeployFlags = {
44
45
  json: boolean;
45
46
  yes: boolean;
46
47
  };
47
- export type DeployCtx = {
48
+ export type DeployCtx = RegenCtx & {
48
49
  collectDomainInputs(yaml: MailmodoYaml, skip: boolean): Promise<{
49
50
  address: string;
50
51
  domain: string;
@@ -84,3 +85,4 @@ export type DeployCtx = {
84
85
  spinner<T>(text: string, json: boolean, work: () => Promise<T>): Promise<T>;
85
86
  syncYaml(): Promise<void>;
86
87
  };
88
+ export { type RegenCtx } from '../templates/types.js';
@@ -15,8 +15,12 @@ export declare const PROMPTS: {
15
15
  };
16
16
  export declare const MISSING_TEMPLATES: {
17
17
  readonly ABORT_HINT: `Restore the missing files from version control, then run ${string} again.`;
18
+ readonly CHOICE_ABORT: "Abort (restore from version control)";
19
+ readonly CHOICE_REGENERATE: "Re-generate via AI";
18
20
  readonly HEADER: `Some email templates are missing from ${string}:`;
21
+ readonly PROMPT_ACTION: "What would you like to do?";
19
22
  readonly REGENERATE_NOTE: string;
23
+ readonly REGENERATE_SPINNER: "Regenerating email templates...";
20
24
  readonly REVIEW_HINT: `Templates regenerated. Review them with ${string}, then run ${string} again.`;
21
25
  readonly YES_ERROR: `Missing templates cannot be resolved with ${string}. Run ${string} without ${string} to regenerate via AI or restore from version control.`;
22
26
  };
@@ -50,6 +54,15 @@ export declare const INFO: {
50
54
  readonly YAML_RESTORED_FROM_SERVER: string;
51
55
  readonly YAML_RESTORED_ON_LOGIN: ` mailmodo.yaml restored from server. Run ${string} to re-deploy your sequences.`;
52
56
  };
57
+ export declare const DEPLOY: {
58
+ readonly CHANGES_HEADER: "Changes vs. last deployment:";
59
+ readonly DEPLOYING_HEADER: "Deploying:";
60
+ readonly NO_CHANGES: "No changes from last deployment.";
61
+ readonly SDK_DOCS_HINT: `Full SDK docs: ${string}`;
62
+ readonly SDK_EXAMPLE_COMMENT: string;
63
+ readonly SDK_ONBOARDING_HEADER: string;
64
+ readonly SUCCESS: `${string} Emails are live.`;
65
+ };
53
66
  export declare function pauseSuccess(sequenceId: string): string;
54
67
  export declare function pauseAlready(sequenceId: string): string;
55
68
  export declare function resumeSuccess(sequenceId: string): string;
@@ -16,8 +16,12 @@ export const PROMPTS = {
16
16
  };
17
17
  export const MISSING_TEMPLATES = {
18
18
  ABORT_HINT: `Restore the missing files from version control, then run ${chalk.cyan('mailmodo deploy')} again.`,
19
+ CHOICE_ABORT: 'Abort (restore from version control)',
20
+ CHOICE_REGENERATE: 'Re-generate via AI',
19
21
  HEADER: `Some email templates are missing from ${chalk.cyan('./mailmodo/')}:`,
22
+ PROMPT_ACTION: 'What would you like to do?',
20
23
  REGENERATE_NOTE: chalk.yellow(' Note: any previous manual edits to these files will be replaced with a new AI draft.'),
24
+ REGENERATE_SPINNER: 'Regenerating email templates...',
21
25
  REVIEW_HINT: `Templates regenerated. Review them with ${chalk.cyan('mailmodo preview')}, then run ${chalk.cyan('mailmodo deploy')} again.`,
22
26
  YES_ERROR: `Missing templates cannot be resolved with ${chalk.cyan('--yes')}. Run ${chalk.cyan('mailmodo deploy')} without ${chalk.cyan('--yes')} to regenerate via AI or restore from version control.`,
23
27
  };
@@ -50,6 +54,15 @@ export const INFO = {
50
54
  YAML_RESTORED_FROM_SERVER: chalk.dim(' mailmodo.yaml not found locally — restored from server.'),
51
55
  YAML_RESTORED_ON_LOGIN: ` mailmodo.yaml restored from server. Run ${chalk.cyan("'mailmodo deploy'")} to re-deploy your sequences.`,
52
56
  };
57
+ export const DEPLOY = {
58
+ CHANGES_HEADER: 'Changes vs. last deployment:',
59
+ DEPLOYING_HEADER: 'Deploying:',
60
+ NO_CHANGES: 'No changes from last deployment.',
61
+ SDK_DOCS_HINT: `Full SDK docs: ${chalk.cyan('mailmodo.com/docs/sdk')}`,
62
+ SDK_EXAMPLE_COMMENT: chalk.dim('// Example usage:'),
63
+ SDK_ONBOARDING_HEADER: chalk.bold('ADD THIS TO YOUR APP (one-time only):'),
64
+ SUCCESS: `${chalk.green('Deployed.')} Emails are live.`,
65
+ };
53
66
  export function pauseSuccess(sequenceId) {
54
67
  return `Sequence ${chalk.cyan(sequenceId)} paused. Run ${chalk.cyan(`mailmodo deploy --resume ${sequenceId}`)} to resume.`;
55
68
  }
@@ -0,0 +1,5 @@
1
+ import { type MailmodoYaml } from '../yaml-config.js';
2
+ import type { DeployFlags } from '../deploy/types.js';
3
+ import type { RegenCtx } from './types.js';
4
+ export declare function getMissingTemplateIds(yamlConfig: MailmodoYaml): string[];
5
+ export declare function handleMissingTemplates(ctx: RegenCtx, yamlConfig: MailmodoYaml, missingIds: string[], flags: DeployFlags): Promise<boolean>;
@@ -4,19 +4,21 @@ import { select } from '@inquirer/prompts';
4
4
  import chalk from 'chalk';
5
5
  import { API_ENDPOINTS, TEMPLATES_DIR } from '../constants.js';
6
6
  import { MISSING_TEMPLATES } from '../messages.js';
7
- import { saveTemplate } from '../yaml-config.js';
8
- import { buildRegeneratePayload } from './payload.js';
7
+ import { getTemplateFilename, saveTemplate, } from '../yaml-config.js';
8
+ import { buildRegeneratePayload } from '../deploy/payload.js';
9
9
  export function getMissingTemplateIds(yamlConfig) {
10
10
  return yamlConfig.emails
11
- .filter((e) => !existsSync(join(process.cwd(), TEMPLATES_DIR, `${e.id}.html`)))
11
+ .filter((e) => !existsSync(join(process.cwd(), TEMPLATES_DIR, getTemplateFilename(e.id, e.style, yamlConfig.project?.emailStyle))))
12
12
  .map((e) => e.id);
13
13
  }
14
14
  async function regenerateMissingTemplates(ctx, yamlConfig, missingIds, flags) {
15
- const response = await ctx.spinner(' Regenerating email templates...', flags.json, () => ctx.post(API_ENDPOINTS.GENERATE, buildRegeneratePayload(yamlConfig, missingIds)));
15
+ const response = await ctx.spinner(` ${MISSING_TEMPLATES.REGENERATE_SPINNER}`, flags.json, () => ctx.post(API_ENDPOINTS.GENERATE, buildRegeneratePayload(yamlConfig, missingIds)));
16
16
  if (!response.ok)
17
17
  ctx.onApiError(response);
18
18
  const saves = [];
19
19
  for (const email of response.data?.emails ?? []) {
20
+ if (!/^[\w-]+$/.test(email.id))
21
+ continue;
20
22
  if (email.html)
21
23
  saves.push(saveTemplate(`${email.id}.html`, email.html));
22
24
  if (email.plainHtml)
@@ -24,7 +26,6 @@ async function regenerateMissingTemplates(ctx, yamlConfig, missingIds, flags) {
24
26
  }
25
27
  await Promise.all(saves);
26
28
  await ctx.syncYaml();
27
- ctx.log(`\n ${chalk.green('✓')} ${MISSING_TEMPLATES.REVIEW_HINT}\n`);
28
29
  }
29
30
  export async function handleMissingTemplates(ctx, yamlConfig, missingIds, flags) {
30
31
  if (flags.json) {
@@ -34,7 +35,6 @@ export async function handleMissingTemplates(ctx, yamlConfig, missingIds, flags)
34
35
  missingTemplates: missingIds.map((id) => `${id}.html`),
35
36
  }, null, 2));
36
37
  ctx.exit(1);
37
- return;
38
38
  }
39
39
  if (flags.yes)
40
40
  ctx.error(MISSING_TEMPLATES.YES_ERROR);
@@ -44,14 +44,18 @@ export async function handleMissingTemplates(ctx, yamlConfig, missingIds, flags)
44
44
  ctx.log(`\n ${MISSING_TEMPLATES.REGENERATE_NOTE}\n`);
45
45
  const action = await select({
46
46
  choices: [
47
- { name: 'Re-generate via AI', value: 'regenerate' },
48
- { name: 'Abort (restore from version control)', value: 'abort' },
47
+ {
48
+ name: MISSING_TEMPLATES.CHOICE_REGENERATE,
49
+ value: 'regenerate',
50
+ },
51
+ { name: MISSING_TEMPLATES.CHOICE_ABORT, value: 'abort' },
49
52
  ],
50
- message: 'What would you like to do?',
53
+ message: MISSING_TEMPLATES.PROMPT_ACTION,
51
54
  });
52
55
  if (action === 'abort') {
53
56
  ctx.log(`\n ${MISSING_TEMPLATES.ABORT_HINT}\n`);
54
- return;
57
+ return false;
55
58
  }
56
59
  await regenerateMissingTemplates(ctx, yamlConfig, missingIds, flags);
60
+ return true;
57
61
  }
@@ -0,0 +1,13 @@
1
+ import type { ApiResponse } from '../api-client.js';
2
+ export type RegenCtx = {
3
+ error(msg: string): never;
4
+ exit(code?: number): never;
5
+ log(msg?: string): void;
6
+ onApiError(resp: {
7
+ error?: string;
8
+ status: number;
9
+ }): never;
10
+ post<T = Record<string, unknown>>(path: string, body?: Record<string, unknown> | unknown): Promise<ApiResponse<T>>;
11
+ spinner<T>(text: string, json: boolean, work: () => Promise<T>): Promise<T>;
12
+ syncYaml(): Promise<void>;
13
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -76,6 +76,67 @@
76
76
  "index.js"
77
77
  ]
78
78
  },
79
+ "deploy": {
80
+ "aliases": [],
81
+ "args": {},
82
+ "description": "Deploy, pause, or resume an email sequence",
83
+ "examples": [
84
+ "<%= config.bin %> deploy",
85
+ "<%= config.bin %> deploy --yes",
86
+ "<%= config.bin %> deploy --pause seq_abc123",
87
+ "<%= config.bin %> deploy --resume seq_abc123 --json"
88
+ ],
89
+ "flags": {
90
+ "json": {
91
+ "description": "Output as JSON",
92
+ "name": "json",
93
+ "allowNo": false,
94
+ "type": "boolean"
95
+ },
96
+ "yes": {
97
+ "char": "y",
98
+ "description": "Skip confirmation prompts",
99
+ "name": "yes",
100
+ "allowNo": false,
101
+ "type": "boolean"
102
+ },
103
+ "pause": {
104
+ "description": "Pause a deployed sequence by ID (stops scheduled + triggered sends)",
105
+ "exclusive": [
106
+ "resume"
107
+ ],
108
+ "name": "pause",
109
+ "hasDynamicHelp": false,
110
+ "multiple": false,
111
+ "type": "option"
112
+ },
113
+ "resume": {
114
+ "description": "Resume a paused sequence by ID",
115
+ "exclusive": [
116
+ "pause"
117
+ ],
118
+ "name": "resume",
119
+ "hasDynamicHelp": false,
120
+ "multiple": false,
121
+ "type": "option"
122
+ }
123
+ },
124
+ "hasDynamicHelp": false,
125
+ "hiddenAliases": [],
126
+ "id": "deploy",
127
+ "pluginAlias": "@mailmodo/cli",
128
+ "pluginName": "@mailmodo/cli",
129
+ "pluginType": "core",
130
+ "strict": true,
131
+ "enableJsonFlag": false,
132
+ "isESM": true,
133
+ "relativePath": [
134
+ "dist",
135
+ "commands",
136
+ "deploy",
137
+ "index.js"
138
+ ]
139
+ },
79
140
  "contacts": {
80
141
  "aliases": [],
81
142
  "args": {},
@@ -176,15 +237,14 @@
176
237
  "index.js"
177
238
  ]
178
239
  },
179
- "deploy": {
240
+ "domain": {
180
241
  "aliases": [],
181
242
  "args": {},
182
- "description": "Deploy, pause, or resume an email sequence",
243
+ "description": "Set up and verify your sending domain",
183
244
  "examples": [
184
- "<%= config.bin %> deploy",
185
- "<%= config.bin %> deploy --yes",
186
- "<%= config.bin %> deploy --pause seq_abc123",
187
- "<%= config.bin %> deploy --resume seq_abc123 --json"
245
+ "<%= config.bin %> domain",
246
+ "<%= config.bin %> domain --verify",
247
+ "<%= config.bin %> domain --status"
188
248
  ],
189
249
  "flags": {
190
250
  "json": {
@@ -200,30 +260,22 @@
200
260
  "allowNo": false,
201
261
  "type": "boolean"
202
262
  },
203
- "pause": {
204
- "description": "Pause a deployed sequence by ID (stops scheduled + triggered sends)",
205
- "exclusive": [
206
- "resume"
207
- ],
208
- "name": "pause",
209
- "hasDynamicHelp": false,
210
- "multiple": false,
211
- "type": "option"
263
+ "status": {
264
+ "description": "Show domain health status",
265
+ "name": "status",
266
+ "allowNo": false,
267
+ "type": "boolean"
212
268
  },
213
- "resume": {
214
- "description": "Resume a paused sequence by ID",
215
- "exclusive": [
216
- "pause"
217
- ],
218
- "name": "resume",
219
- "hasDynamicHelp": false,
220
- "multiple": false,
221
- "type": "option"
269
+ "verify": {
270
+ "description": "Verify DNS records",
271
+ "name": "verify",
272
+ "allowNo": false,
273
+ "type": "boolean"
222
274
  }
223
275
  },
224
276
  "hasDynamicHelp": false,
225
277
  "hiddenAliases": [],
226
- "id": "deploy",
278
+ "id": "domain",
227
279
  "pluginAlias": "@mailmodo/cli",
228
280
  "pluginName": "@mailmodo/cli",
229
281
  "pluginType": "core",
@@ -233,18 +285,17 @@
233
285
  "relativePath": [
234
286
  "dist",
235
287
  "commands",
236
- "deploy",
288
+ "domain",
237
289
  "index.js"
238
290
  ]
239
291
  },
240
- "domain": {
292
+ "emails": {
241
293
  "aliases": [],
242
294
  "args": {},
243
- "description": "Set up and verify your sending domain",
295
+ "description": "List and view configured email sequences",
244
296
  "examples": [
245
- "<%= config.bin %> domain",
246
- "<%= config.bin %> domain --verify",
247
- "<%= config.bin %> domain --status"
297
+ "<%= config.bin %> emails",
298
+ "<%= config.bin %> emails --json"
248
299
  ],
249
300
  "flags": {
250
301
  "json": {
@@ -259,23 +310,11 @@
259
310
  "name": "yes",
260
311
  "allowNo": false,
261
312
  "type": "boolean"
262
- },
263
- "status": {
264
- "description": "Show domain health status",
265
- "name": "status",
266
- "allowNo": false,
267
- "type": "boolean"
268
- },
269
- "verify": {
270
- "description": "Verify DNS records",
271
- "name": "verify",
272
- "allowNo": false,
273
- "type": "boolean"
274
313
  }
275
314
  },
276
315
  "hasDynamicHelp": false,
277
316
  "hiddenAliases": [],
278
- "id": "domain",
317
+ "id": "emails",
279
318
  "pluginAlias": "@mailmodo/cli",
280
319
  "pluginName": "@mailmodo/cli",
281
320
  "pluginType": "core",
@@ -285,7 +324,7 @@
285
324
  "relativePath": [
286
325
  "dist",
287
326
  "commands",
288
- "domain",
327
+ "emails",
289
328
  "index.js"
290
329
  ]
291
330
  },
@@ -341,13 +380,13 @@
341
380
  "index.js"
342
381
  ]
343
382
  },
344
- "emails": {
383
+ "init": {
345
384
  "aliases": [],
346
385
  "args": {},
347
- "description": "List and view configured email sequences",
386
+ "description": "Analyze your product and generate email sequences",
348
387
  "examples": [
349
- "<%= config.bin %> emails",
350
- "<%= config.bin %> emails --json"
388
+ "<%= config.bin %> init",
389
+ "<%= config.bin %> init --url https://myapp.com --yes"
351
390
  ],
352
391
  "flags": {
353
392
  "json": {
@@ -362,11 +401,18 @@
362
401
  "name": "yes",
363
402
  "allowNo": false,
364
403
  "type": "boolean"
404
+ },
405
+ "url": {
406
+ "description": "Product URL to analyze",
407
+ "name": "url",
408
+ "hasDynamicHelp": false,
409
+ "multiple": false,
410
+ "type": "option"
365
411
  }
366
412
  },
367
413
  "hasDynamicHelp": false,
368
414
  "hiddenAliases": [],
369
- "id": "emails",
415
+ "id": "init",
370
416
  "pluginAlias": "@mailmodo/cli",
371
417
  "pluginName": "@mailmodo/cli",
372
418
  "pluginType": "core",
@@ -376,7 +422,7 @@
376
422
  "relativePath": [
377
423
  "dist",
378
424
  "commands",
379
- "emails",
425
+ "init",
380
426
  "index.js"
381
427
  ]
382
428
  },
@@ -717,53 +763,7 @@
717
763
  "status",
718
764
  "index.js"
719
765
  ]
720
- },
721
- "init": {
722
- "aliases": [],
723
- "args": {},
724
- "description": "Analyze your product and generate email sequences",
725
- "examples": [
726
- "<%= config.bin %> init",
727
- "<%= config.bin %> init --url https://myapp.com --yes"
728
- ],
729
- "flags": {
730
- "json": {
731
- "description": "Output as JSON",
732
- "name": "json",
733
- "allowNo": false,
734
- "type": "boolean"
735
- },
736
- "yes": {
737
- "char": "y",
738
- "description": "Skip confirmation prompts",
739
- "name": "yes",
740
- "allowNo": false,
741
- "type": "boolean"
742
- },
743
- "url": {
744
- "description": "Product URL to analyze",
745
- "name": "url",
746
- "hasDynamicHelp": false,
747
- "multiple": false,
748
- "type": "option"
749
- }
750
- },
751
- "hasDynamicHelp": false,
752
- "hiddenAliases": [],
753
- "id": "init",
754
- "pluginAlias": "@mailmodo/cli",
755
- "pluginName": "@mailmodo/cli",
756
- "pluginType": "core",
757
- "strict": true,
758
- "enableJsonFlag": false,
759
- "isESM": true,
760
- "relativePath": [
761
- "dist",
762
- "commands",
763
- "init",
764
- "index.js"
765
- ]
766
766
  }
767
767
  },
768
- "version": "0.0.54-beta.pr56.87"
768
+ "version": "0.0.54-beta.pr56.89"
769
769
  }
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.54-beta.pr56.87",
4
+ "version": "0.0.54-beta.pr56.89",
5
5
  "author": "provishalk",
6
6
  "bin": {
7
7
  "mailmodo": "bin/run.js"
@@ -1,4 +0,0 @@
1
- import { type MailmodoYaml } from '../yaml-config.js';
2
- import type { DeployCtx, DeployFlags } from './types.js';
3
- export declare function getMissingTemplateIds(yamlConfig: MailmodoYaml): string[];
4
- export declare function handleMissingTemplates(ctx: DeployCtx, yamlConfig: MailmodoYaml, missingIds: string[], flags: DeployFlags): Promise<void>;