@mailmodo/cli 0.0.50 → 0.0.51-beta.pr53.82
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/deploy/index.d.ts +18 -0
- package/dist/commands/deploy/index.js +79 -5
- package/dist/commands/deployments/index.d.ts +14 -0
- package/dist/commands/deployments/index.js +76 -0
- package/dist/commands/sdk/index.d.ts +14 -0
- package/dist/commands/sdk/index.js +74 -0
- package/dist/lib/constants.d.ts +4 -0
- package/dist/lib/constants.js +4 -0
- package/dist/lib/messages.d.ts +6 -0
- package/dist/lib/messages.js +14 -0
- package/oclif.manifest.json +121 -13
- package/package.json +1 -1
|
@@ -3,12 +3,30 @@ export default class Deploy extends BaseCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
+
pause: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
resume: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
8
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
9
|
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
10
|
};
|
|
9
11
|
private fetchDomainVerifyForDeploy;
|
|
10
12
|
run(): Promise<void>;
|
|
11
13
|
private validateSequence;
|
|
14
|
+
/**
|
|
15
|
+
* Calls `POST /sequences/{id}/status` with `{ status: "paused" }` and prints
|
|
16
|
+
* a confirmation-aware success/no-op message. Skips the prompt entirely when
|
|
17
|
+
* `--yes` is set so the command stays scriptable. `--json` always emits the
|
|
18
|
+
* raw server payload (sequenceId, status, alreadyInStatus).
|
|
19
|
+
*/
|
|
20
|
+
private pauseSequence;
|
|
21
|
+
/**
|
|
22
|
+
* Calls `POST /sequences/{id}/status` with `{ status: "active" }`. No prompt
|
|
23
|
+
* — resuming is the safe direction (it does not start sends that weren't
|
|
24
|
+
* already queued). `--json` always emits the raw server payload (sequenceId,
|
|
25
|
+
* status, alreadyInStatus).
|
|
26
|
+
*/
|
|
27
|
+
private resumeSequence;
|
|
28
|
+
private updateSequenceStatus;
|
|
29
|
+
private sequenceStatusPath;
|
|
12
30
|
private buildDeployPayload;
|
|
13
31
|
private resolveMonthlyCapForDeploy;
|
|
14
32
|
private mapEmailToPayload;
|
|
@@ -1,17 +1,28 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
1
2
|
import { confirm, input } from '@inquirer/prompts';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
4
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
4
|
-
import { API_ENDPOINTS, DEFAULT_BRAND_COLOR } from '../../lib/constants.js';
|
|
5
|
-
import { ERRORS, INFO, PROMPTS, SEPARATOR } from '../../lib/messages.js';
|
|
5
|
+
import { API_ENDPOINTS, DEFAULT_BRAND_COLOR, SDK_IMPORT_SNIPPET, SDK_INSTALL_COMMAND, } from '../../lib/constants.js';
|
|
6
|
+
import { ERRORS, INFO, pauseAlready, pauseSuccess, PROMPTS, resumeAlready, resumeSuccess, SEPARATOR, } from '../../lib/messages.js';
|
|
6
7
|
import { loadTemplate, } from '../../lib/yaml-config.js';
|
|
7
8
|
export default class Deploy extends BaseCommand {
|
|
8
|
-
static description = 'Deploy
|
|
9
|
+
static description = 'Deploy, pause, or resume an email sequence';
|
|
9
10
|
static examples = [
|
|
10
11
|
'<%= config.bin %> deploy',
|
|
11
12
|
'<%= config.bin %> deploy --yes',
|
|
13
|
+
'<%= config.bin %> deploy --pause seq_abc123',
|
|
14
|
+
'<%= config.bin %> deploy --resume seq_abc123 --json',
|
|
12
15
|
];
|
|
13
16
|
static flags = {
|
|
14
17
|
...BaseCommand.baseFlags,
|
|
18
|
+
pause: Flags.string({
|
|
19
|
+
description: 'Pause a deployed sequence by ID (stops scheduled + triggered sends)',
|
|
20
|
+
exclusive: ['resume'],
|
|
21
|
+
}),
|
|
22
|
+
resume: Flags.string({
|
|
23
|
+
description: 'Resume a paused sequence by ID',
|
|
24
|
+
exclusive: ['pause'],
|
|
25
|
+
}),
|
|
15
26
|
};
|
|
16
27
|
fetchDomainVerifyForDeploy(jsonOutput, domain) {
|
|
17
28
|
return this.withApiSpinner({ json: jsonOutput, text: ' Checking domain verification...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY, {
|
|
@@ -21,6 +32,15 @@ export default class Deploy extends BaseCommand {
|
|
|
21
32
|
async run() {
|
|
22
33
|
const { flags } = await this.parse(Deploy);
|
|
23
34
|
await this.ensureAuth();
|
|
35
|
+
const baseFlags = { json: flags.json, yes: flags.yes };
|
|
36
|
+
if (flags.pause) {
|
|
37
|
+
await this.pauseSequence(flags.pause, baseFlags);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (flags.resume) {
|
|
41
|
+
await this.resumeSequence(flags.resume, baseFlags);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
24
44
|
const yamlConfig = await this.ensureYaml();
|
|
25
45
|
const domainReady = await this.ensureDomainReady(yamlConfig, flags);
|
|
26
46
|
if (!domainReady)
|
|
@@ -60,6 +80,60 @@ export default class Deploy extends BaseCommand {
|
|
|
60
80
|
}
|
|
61
81
|
return response.data;
|
|
62
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Calls `POST /sequences/{id}/status` with `{ status: "paused" }` and prints
|
|
85
|
+
* a confirmation-aware success/no-op message. Skips the prompt entirely when
|
|
86
|
+
* `--yes` is set so the command stays scriptable. `--json` always emits the
|
|
87
|
+
* raw server payload (sequenceId, status, alreadyInStatus).
|
|
88
|
+
*/
|
|
89
|
+
async pauseSequence(sequenceId, flags) {
|
|
90
|
+
if (!flags.yes) {
|
|
91
|
+
const confirmed = await confirm({
|
|
92
|
+
default: false,
|
|
93
|
+
message: PROMPTS.PAUSE_CONFIRM,
|
|
94
|
+
});
|
|
95
|
+
if (!confirmed) {
|
|
96
|
+
this.log(`\n ${INFO.PAUSE_CANCELLED}\n`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const data = await this.updateSequenceStatus(sequenceId, 'paused', flags, ' Pausing sequence...');
|
|
101
|
+
if (flags.json) {
|
|
102
|
+
this.log(JSON.stringify(data, null, 2));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const message = data.alreadyInStatus
|
|
106
|
+
? pauseAlready(data.sequenceId || sequenceId)
|
|
107
|
+
: pauseSuccess(data.sequenceId || sequenceId);
|
|
108
|
+
this.log(`\n ${message}\n`);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Calls `POST /sequences/{id}/status` with `{ status: "active" }`. No prompt
|
|
112
|
+
* — resuming is the safe direction (it does not start sends that weren't
|
|
113
|
+
* already queued). `--json` always emits the raw server payload (sequenceId,
|
|
114
|
+
* status, alreadyInStatus).
|
|
115
|
+
*/
|
|
116
|
+
async resumeSequence(sequenceId, flags) {
|
|
117
|
+
const data = await this.updateSequenceStatus(sequenceId, 'active', flags, ' Resuming sequence...');
|
|
118
|
+
if (flags.json) {
|
|
119
|
+
this.log(JSON.stringify(data, null, 2));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const message = data.alreadyInStatus
|
|
123
|
+
? resumeAlready(data.sequenceId || sequenceId)
|
|
124
|
+
: resumeSuccess(data.sequenceId || sequenceId);
|
|
125
|
+
this.log(`\n ${message}\n`);
|
|
126
|
+
}
|
|
127
|
+
async updateSequenceStatus(sequenceId, status, flags, spinnerText) {
|
|
128
|
+
const response = await this.withApiSpinner({ json: flags.json, text: spinnerText }, () => this.apiClient.post(this.sequenceStatusPath(sequenceId), { status }));
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
this.handleApiError(response);
|
|
131
|
+
}
|
|
132
|
+
return response.data;
|
|
133
|
+
}
|
|
134
|
+
sequenceStatusPath(sequenceId) {
|
|
135
|
+
return `${API_ENDPOINTS.SEQUENCES}/${encodeURIComponent(sequenceId)}/status`;
|
|
136
|
+
}
|
|
63
137
|
async buildDeployPayload(yamlConfig) {
|
|
64
138
|
const [emailsWithHtml, monthlyCap] = await Promise.all([
|
|
65
139
|
Promise.all(yamlConfig.emails.map(async (email) => {
|
|
@@ -212,8 +286,8 @@ export default class Deploy extends BaseCommand {
|
|
|
212
286
|
this.log(` ${SEPARATOR}`);
|
|
213
287
|
this.log(` ${chalk.bold('ADD THIS TO YOUR APP (one-time only):')}`);
|
|
214
288
|
this.log(` ${SEPARATOR}\n`);
|
|
215
|
-
this.log(` ${chalk.cyan(sdkSnippet.install ??
|
|
216
|
-
this.log(` ${chalk.dim(
|
|
289
|
+
this.log(` ${chalk.cyan(sdkSnippet.install ?? SDK_INSTALL_COMMAND)}\n`);
|
|
290
|
+
this.log(` ${chalk.dim(SDK_IMPORT_SNIPPET)}\n`);
|
|
217
291
|
if (sdkSnippet.examples) {
|
|
218
292
|
this.log(` ${chalk.dim('// Example usage:')}`);
|
|
219
293
|
this.log(` ${chalk.dim(sdkSnippet.examples.track)}`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseCommand } from '../../lib/base-command.js';
|
|
2
|
+
export default class Deployments extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
private renderTable;
|
|
11
|
+
private statusColor;
|
|
12
|
+
private colWidth;
|
|
13
|
+
private formatDate;
|
|
14
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { BaseCommand } from '../../lib/base-command.js';
|
|
3
|
+
import { API_ENDPOINTS } from '../../lib/constants.js';
|
|
4
|
+
export default class Deployments extends BaseCommand {
|
|
5
|
+
static description = 'List every deployed sequence on this account, with the IDs needed for deploy --pause / --resume';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> deployments',
|
|
8
|
+
'<%= config.bin %> deployments --json',
|
|
9
|
+
];
|
|
10
|
+
static flags = {
|
|
11
|
+
...BaseCommand.baseFlags,
|
|
12
|
+
};
|
|
13
|
+
async run() {
|
|
14
|
+
const { flags } = await this.parse(Deployments);
|
|
15
|
+
await this.ensureAuth();
|
|
16
|
+
const response = await this.withApiSpinner({ json: flags.json, text: ' Loading deployments...' }, () => this.apiClient.get(API_ENDPOINTS.SEQUENCES));
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
this.handleApiError(response);
|
|
19
|
+
}
|
|
20
|
+
if (flags.json) {
|
|
21
|
+
this.log(JSON.stringify(response.data, null, 2));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
this.renderTable(response.data);
|
|
25
|
+
}
|
|
26
|
+
renderTable(data) {
|
|
27
|
+
const sequences = data.sequences ?? [];
|
|
28
|
+
if (sequences.length === 0) {
|
|
29
|
+
this.log(`\n ${chalk.dim('No deployed sequences yet.')}`);
|
|
30
|
+
this.log(` Run ${chalk.cyan('mailmodo deploy')} to deploy one.\n`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const rows = sequences.map((seq) => ({
|
|
34
|
+
emails: String(seq.emailCount ?? 0),
|
|
35
|
+
product: seq.productName ?? '',
|
|
36
|
+
sequenceId: seq.sequenceId ?? '',
|
|
37
|
+
status: seq.status ?? '',
|
|
38
|
+
updated: this.formatDate(seq.updatedAt),
|
|
39
|
+
}));
|
|
40
|
+
const widths = {
|
|
41
|
+
emails: this.colWidth(rows, 'emails', 'Emails'),
|
|
42
|
+
product: this.colWidth(rows, 'product', 'Product'),
|
|
43
|
+
sequenceId: this.colWidth(rows, 'sequenceId', 'Sequence ID'),
|
|
44
|
+
status: this.colWidth(rows, 'status', 'Status'),
|
|
45
|
+
updated: this.colWidth(rows, 'updated', 'Updated'),
|
|
46
|
+
};
|
|
47
|
+
this.log(`\n ${chalk.bold(String(sequences.length))} deployed ${sequences.length === 1 ? 'sequence' : 'sequences'}:\n`);
|
|
48
|
+
this.log(` ${chalk.bold('Product'.padEnd(widths.product))}${chalk.bold('Status'.padEnd(widths.status))}${chalk.bold('Emails'.padEnd(widths.emails))}${chalk.bold('Sequence ID'.padEnd(widths.sequenceId))}${chalk.bold('Updated')}`);
|
|
49
|
+
this.log(` ${'─'.repeat(widths.product + widths.status + widths.emails + widths.sequenceId + widths.updated)}`);
|
|
50
|
+
for (const row of rows) {
|
|
51
|
+
const status = this.statusColor(row.status)(row.status.padEnd(widths.status));
|
|
52
|
+
this.log(` ${row.product.padEnd(widths.product)}${status}${row.emails.padEnd(widths.emails)}${chalk.cyan(row.sequenceId.padEnd(widths.sequenceId))}${chalk.dim(row.updated)}`);
|
|
53
|
+
}
|
|
54
|
+
this.log('');
|
|
55
|
+
this.log(` Pause: ${chalk.cyan('mailmodo deploy --pause <sequence-id>')}`);
|
|
56
|
+
this.log(` Resume: ${chalk.cyan('mailmodo deploy --resume <sequence-id>')}\n`);
|
|
57
|
+
}
|
|
58
|
+
statusColor(status) {
|
|
59
|
+
if (status === 'active')
|
|
60
|
+
return chalk.green;
|
|
61
|
+
if (status === 'paused')
|
|
62
|
+
return chalk.yellow;
|
|
63
|
+
return chalk.white;
|
|
64
|
+
}
|
|
65
|
+
colWidth(rows, key, header) {
|
|
66
|
+
return Math.max(...rows.map((r) => r[key].length), header.length) + 2;
|
|
67
|
+
}
|
|
68
|
+
formatDate(iso) {
|
|
69
|
+
if (!iso)
|
|
70
|
+
return '';
|
|
71
|
+
const parsed = new Date(iso);
|
|
72
|
+
if (Number.isNaN(parsed.getTime()))
|
|
73
|
+
return iso;
|
|
74
|
+
return parsed.toISOString().slice(0, 10);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseCommand } from '../../lib/base-command.js';
|
|
2
|
+
export default class Sdk extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
'sequence-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
private renderSnippets;
|
|
12
|
+
private renderSequenceBlock;
|
|
13
|
+
private renderCallBlock;
|
|
14
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { BaseCommand } from '../../lib/base-command.js';
|
|
4
|
+
import { API_ENDPOINTS, SDK_IMPORT_SNIPPET, SDK_INSTALL_COMMAND, } from '../../lib/constants.js';
|
|
5
|
+
import { SEPARATOR } from '../../lib/messages.js';
|
|
6
|
+
export default class Sdk extends BaseCommand {
|
|
7
|
+
static description = 'Show the SDK track() / identify() reference for deployed sequences';
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> sdk',
|
|
10
|
+
'<%= config.bin %> sdk --sequence-id a1b2c3d4',
|
|
11
|
+
'<%= config.bin %> sdk --json',
|
|
12
|
+
];
|
|
13
|
+
static flags = {
|
|
14
|
+
...BaseCommand.baseFlags,
|
|
15
|
+
'sequence-id': Flags.string({
|
|
16
|
+
description: 'Limit output to a single active sequence by ID (default: all active sequences)',
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const { flags } = await this.parse(Sdk);
|
|
21
|
+
await this.ensureAuth();
|
|
22
|
+
const params = flags['sequence-id']
|
|
23
|
+
? { sequenceId: flags['sequence-id'] }
|
|
24
|
+
: undefined;
|
|
25
|
+
const response = await this.withApiSpinner({ json: flags.json, text: ' Loading SDK reference...' }, () => this.apiClient.get(API_ENDPOINTS.SEQUENCES_SDK, params));
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
this.handleApiError(response);
|
|
28
|
+
}
|
|
29
|
+
if (flags.json) {
|
|
30
|
+
this.log(JSON.stringify(response.data, null, 2));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this.renderSnippets(response.data);
|
|
34
|
+
}
|
|
35
|
+
renderSnippets(data) {
|
|
36
|
+
const snippets = data.sdkSnippets ?? [];
|
|
37
|
+
if (snippets.length === 0) {
|
|
38
|
+
this.log(`\n ${chalk.dim('No active deployed sequences.')}`);
|
|
39
|
+
this.log(` Run ${chalk.cyan('mailmodo deploy')} to deploy one.\n`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
this.log(`\n ${chalk.bold(String(snippets.length))} active ${snippets.length === 1 ? 'sequence' : 'sequences'}:\n`);
|
|
43
|
+
this.log(` ${SEPARATOR}`);
|
|
44
|
+
this.log(` ${chalk.bold('SDK EVENT REFERENCE')}`);
|
|
45
|
+
this.log(` ${SEPARATOR}\n`);
|
|
46
|
+
this.log(` ${chalk.cyan(SDK_INSTALL_COMMAND)}\n`);
|
|
47
|
+
this.log(` ${chalk.dim(SDK_IMPORT_SNIPPET)}\n`);
|
|
48
|
+
for (const [index, snippet] of snippets.entries()) {
|
|
49
|
+
this.renderSequenceBlock(snippet);
|
|
50
|
+
if (index < snippets.length - 1)
|
|
51
|
+
this.log('');
|
|
52
|
+
}
|
|
53
|
+
this.log(` ${SEPARATOR}\n`);
|
|
54
|
+
}
|
|
55
|
+
renderSequenceBlock(snippet) {
|
|
56
|
+
const productName = snippet.productName || 'Unnamed sequence';
|
|
57
|
+
this.log(` ${chalk.bold(productName)} ${chalk.dim(`(${snippet.sequenceId})`)}`);
|
|
58
|
+
const trackCalls = [...new Set(snippet.sdkSnippet?.trackCalls ?? [])];
|
|
59
|
+
const identifyCalls = [...new Set(snippet.sdkSnippet?.identifyCalls ?? [])];
|
|
60
|
+
this.renderCallBlock('// track() calls', trackCalls);
|
|
61
|
+
this.renderCallBlock('// identify() calls', identifyCalls);
|
|
62
|
+
if (trackCalls.length === 0 && identifyCalls.length === 0) {
|
|
63
|
+
this.log(` ${chalk.dim('No track() or identify() calls available.')}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
renderCallBlock(label, calls) {
|
|
67
|
+
if (calls.length === 0)
|
|
68
|
+
return;
|
|
69
|
+
this.log(` ${chalk.dim(label)}`);
|
|
70
|
+
for (const call of calls) {
|
|
71
|
+
this.log(` ${chalk.dim(call)}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export declare const API_ENDPOINTS: Readonly<{
|
|
|
26
26
|
PREVIEW: "/preview";
|
|
27
27
|
SEQUENCES: "/sequences";
|
|
28
28
|
SEQUENCES_DEPLOY: "/sequences/deploy";
|
|
29
|
+
SEQUENCES_SDK: "/sequences/sdk";
|
|
29
30
|
SEQUENCES_VALIDATE: "/sequences/validate";
|
|
30
31
|
}>;
|
|
31
32
|
export declare const LOGIN_URL = "https://app-vertex-debug.azurewebsites.net/signup.html";
|
|
@@ -33,3 +34,6 @@ export declare const PREVIEW_PORT = 3421;
|
|
|
33
34
|
export declare const DEFAULT_BRAND_COLOR = "#1A56DB";
|
|
34
35
|
export declare const TEMPLATES_DIR = "mailmodo";
|
|
35
36
|
export declare const YAML_FILE = "mailmodo.yaml";
|
|
37
|
+
export declare const SDK_PACKAGE_NAME = "@mailmodo/sdk";
|
|
38
|
+
export declare const SDK_INSTALL_COMMAND = "npm install @mailmodo/sdk";
|
|
39
|
+
export declare const SDK_IMPORT_SNIPPET = "import { track, identify } from '@mailmodo/sdk'";
|
package/dist/lib/constants.js
CHANGED
|
@@ -32,6 +32,7 @@ export const API_ENDPOINTS = Object.freeze({
|
|
|
32
32
|
PREVIEW: '/preview',
|
|
33
33
|
SEQUENCES: '/sequences',
|
|
34
34
|
SEQUENCES_DEPLOY: '/sequences/deploy',
|
|
35
|
+
SEQUENCES_SDK: '/sequences/sdk',
|
|
35
36
|
SEQUENCES_VALIDATE: '/sequences/validate',
|
|
36
37
|
});
|
|
37
38
|
const DEV_LOGIN_URL = 'https://app-vertex-debug.azurewebsites.net/signup.html';
|
|
@@ -44,3 +45,6 @@ export const PREVIEW_PORT = 3421;
|
|
|
44
45
|
export const DEFAULT_BRAND_COLOR = '#1A56DB';
|
|
45
46
|
export const TEMPLATES_DIR = 'mailmodo';
|
|
46
47
|
export const YAML_FILE = 'mailmodo.yaml';
|
|
48
|
+
export const SDK_PACKAGE_NAME = '@mailmodo/sdk';
|
|
49
|
+
export const SDK_INSTALL_COMMAND = `npm install ${SDK_PACKAGE_NAME}`;
|
|
50
|
+
export const SDK_IMPORT_SNIPPET = `import { track, identify } from '${SDK_PACKAGE_NAME}'`;
|
package/dist/lib/messages.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare const PROMPTS: {
|
|
|
9
9
|
readonly DOMAIN: "What domain will you send from?";
|
|
10
10
|
readonly ENTER_AFTER_RECORDS: "Press Enter once you've added the records, or 'skip'.";
|
|
11
11
|
readonly FROM_NAME: "Display name (optional, shown as sender name):";
|
|
12
|
+
readonly PAUSE_CONFIRM: "Pause this sequence? All scheduled and triggered sends will stop until you resume.";
|
|
12
13
|
readonly REPLY_TO: "Reply-to address (optional, press Enter to use sender email):";
|
|
13
14
|
readonly SENDER_EMAIL: "Sender email address:";
|
|
14
15
|
};
|
|
@@ -37,8 +38,13 @@ export declare const INFO: {
|
|
|
37
38
|
Then: ${string}`;
|
|
38
39
|
readonly DOMAIN_PENDING_VERIFICATION: `Your domain is not verified yet. Please verify it first. Run ${string} to check the status.`;
|
|
39
40
|
readonly FREE_TIER_CAP_BLOCKED: `Monthly cap is a paid-tier setting and is not available on the free tier. Run ${string} to add a payment method, then set a cap.`;
|
|
41
|
+
readonly PAUSE_CANCELLED: "Pause cancelled. Sequence is still live.";
|
|
40
42
|
readonly SEQUENCES_NOT_DEPLOYED: `Sequences saved but ${string}.`;
|
|
41
43
|
};
|
|
44
|
+
export declare function pauseSuccess(sequenceId: string): string;
|
|
45
|
+
export declare function pauseAlready(sequenceId: string): string;
|
|
46
|
+
export declare function resumeSuccess(sequenceId: string): string;
|
|
47
|
+
export declare function resumeAlready(sequenceId: string): string;
|
|
42
48
|
export declare function yamlParseError(detail: string): string;
|
|
43
49
|
export declare function recordLabel(index: number): string;
|
|
44
50
|
/**
|
package/dist/lib/messages.js
CHANGED
|
@@ -10,6 +10,7 @@ export const PROMPTS = {
|
|
|
10
10
|
DOMAIN: 'What domain will you send from?',
|
|
11
11
|
ENTER_AFTER_RECORDS: "Press Enter once you've added the records, or 'skip'.",
|
|
12
12
|
FROM_NAME: 'Display name (optional, shown as sender name):',
|
|
13
|
+
PAUSE_CONFIRM: 'Pause this sequence? All scheduled and triggered sends will stop until you resume.',
|
|
13
14
|
REPLY_TO: 'Reply-to address (optional, press Enter to use sender email):',
|
|
14
15
|
SENDER_EMAIL: 'Sender email address:',
|
|
15
16
|
};
|
|
@@ -37,8 +38,21 @@ export const INFO = {
|
|
|
37
38
|
DOMAIN_NOT_DEPLOYED_HINT: `When ready, run: ${chalk.cyan('mailmodo domain')}\n Then: ${chalk.cyan('mailmodo deploy')}`,
|
|
38
39
|
DOMAIN_PENDING_VERIFICATION: `Your domain is not verified yet. Please verify it first. Run ${chalk.cyan('mailmodo domain --verify')} to check the status.`,
|
|
39
40
|
FREE_TIER_CAP_BLOCKED: `Monthly cap is a paid-tier setting and is not available on the free tier. Run ${chalk.cyan("'mailmodo billing --checkout'")} to add a payment method, then set a cap.`,
|
|
41
|
+
PAUSE_CANCELLED: 'Pause cancelled. Sequence is still live.',
|
|
40
42
|
SEQUENCES_NOT_DEPLOYED: `Sequences saved but ${chalk.yellow('NOT deployed')}.`,
|
|
41
43
|
};
|
|
44
|
+
export function pauseSuccess(sequenceId) {
|
|
45
|
+
return `Sequence ${chalk.cyan(sequenceId)} paused. Run ${chalk.cyan(`mailmodo deploy --resume ${sequenceId}`)} to resume.`;
|
|
46
|
+
}
|
|
47
|
+
export function pauseAlready(sequenceId) {
|
|
48
|
+
return `Sequence ${chalk.cyan(sequenceId)} is already paused. No change made.`;
|
|
49
|
+
}
|
|
50
|
+
export function resumeSuccess(sequenceId) {
|
|
51
|
+
return `Sequence ${chalk.cyan(sequenceId)} resumed. Scheduled and triggered sends are live again.`;
|
|
52
|
+
}
|
|
53
|
+
export function resumeAlready(sequenceId) {
|
|
54
|
+
return `Sequence ${chalk.cyan(sequenceId)} is already active. No change made.`;
|
|
55
|
+
}
|
|
42
56
|
export function yamlParseError(detail) {
|
|
43
57
|
return `mailmodo.yaml has invalid YAML syntax:\n${detail}`;
|
|
44
58
|
}
|
package/oclif.manifest.json
CHANGED
|
@@ -140,10 +140,12 @@
|
|
|
140
140
|
"deploy": {
|
|
141
141
|
"aliases": [],
|
|
142
142
|
"args": {},
|
|
143
|
-
"description": "Deploy
|
|
143
|
+
"description": "Deploy, pause, or resume an email sequence",
|
|
144
144
|
"examples": [
|
|
145
145
|
"<%= config.bin %> deploy",
|
|
146
|
-
"<%= config.bin %> deploy --yes"
|
|
146
|
+
"<%= config.bin %> deploy --yes",
|
|
147
|
+
"<%= config.bin %> deploy --pause seq_abc123",
|
|
148
|
+
"<%= config.bin %> deploy --resume seq_abc123 --json"
|
|
147
149
|
],
|
|
148
150
|
"flags": {
|
|
149
151
|
"json": {
|
|
@@ -158,6 +160,26 @@
|
|
|
158
160
|
"name": "yes",
|
|
159
161
|
"allowNo": false,
|
|
160
162
|
"type": "boolean"
|
|
163
|
+
},
|
|
164
|
+
"pause": {
|
|
165
|
+
"description": "Pause a deployed sequence by ID (stops scheduled + triggered sends)",
|
|
166
|
+
"exclusive": [
|
|
167
|
+
"resume"
|
|
168
|
+
],
|
|
169
|
+
"name": "pause",
|
|
170
|
+
"hasDynamicHelp": false,
|
|
171
|
+
"multiple": false,
|
|
172
|
+
"type": "option"
|
|
173
|
+
},
|
|
174
|
+
"resume": {
|
|
175
|
+
"description": "Resume a paused sequence by ID",
|
|
176
|
+
"exclusive": [
|
|
177
|
+
"pause"
|
|
178
|
+
],
|
|
179
|
+
"name": "resume",
|
|
180
|
+
"hasDynamicHelp": false,
|
|
181
|
+
"multiple": false,
|
|
182
|
+
"type": "option"
|
|
161
183
|
}
|
|
162
184
|
},
|
|
163
185
|
"hasDynamicHelp": false,
|
|
@@ -176,6 +198,45 @@
|
|
|
176
198
|
"index.js"
|
|
177
199
|
]
|
|
178
200
|
},
|
|
201
|
+
"deployments": {
|
|
202
|
+
"aliases": [],
|
|
203
|
+
"args": {},
|
|
204
|
+
"description": "List every deployed sequence on this account, with the IDs needed for deploy --pause / --resume",
|
|
205
|
+
"examples": [
|
|
206
|
+
"<%= config.bin %> deployments",
|
|
207
|
+
"<%= config.bin %> deployments --json"
|
|
208
|
+
],
|
|
209
|
+
"flags": {
|
|
210
|
+
"json": {
|
|
211
|
+
"description": "Output as JSON",
|
|
212
|
+
"name": "json",
|
|
213
|
+
"allowNo": false,
|
|
214
|
+
"type": "boolean"
|
|
215
|
+
},
|
|
216
|
+
"yes": {
|
|
217
|
+
"char": "y",
|
|
218
|
+
"description": "Skip confirmation prompts",
|
|
219
|
+
"name": "yes",
|
|
220
|
+
"allowNo": false,
|
|
221
|
+
"type": "boolean"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"hasDynamicHelp": false,
|
|
225
|
+
"hiddenAliases": [],
|
|
226
|
+
"id": "deployments",
|
|
227
|
+
"pluginAlias": "@mailmodo/cli",
|
|
228
|
+
"pluginName": "@mailmodo/cli",
|
|
229
|
+
"pluginType": "core",
|
|
230
|
+
"strict": true,
|
|
231
|
+
"enableJsonFlag": false,
|
|
232
|
+
"isESM": true,
|
|
233
|
+
"relativePath": [
|
|
234
|
+
"dist",
|
|
235
|
+
"commands",
|
|
236
|
+
"deployments",
|
|
237
|
+
"index.js"
|
|
238
|
+
]
|
|
239
|
+
},
|
|
179
240
|
"domain": {
|
|
180
241
|
"aliases": [],
|
|
181
242
|
"args": {},
|
|
@@ -570,14 +631,14 @@
|
|
|
570
631
|
"index.js"
|
|
571
632
|
]
|
|
572
633
|
},
|
|
573
|
-
"
|
|
634
|
+
"sdk": {
|
|
574
635
|
"aliases": [],
|
|
575
636
|
"args": {},
|
|
576
|
-
"description": "
|
|
637
|
+
"description": "Show the SDK track() / identify() reference for deployed sequences",
|
|
577
638
|
"examples": [
|
|
578
|
-
"<%= config.bin %>
|
|
579
|
-
"<%= config.bin %>
|
|
580
|
-
"<%= config.bin %>
|
|
639
|
+
"<%= config.bin %> sdk",
|
|
640
|
+
"<%= config.bin %> sdk --sequence-id a1b2c3d4",
|
|
641
|
+
"<%= config.bin %> sdk --json"
|
|
581
642
|
],
|
|
582
643
|
"flags": {
|
|
583
644
|
"json": {
|
|
@@ -593,9 +654,9 @@
|
|
|
593
654
|
"allowNo": false,
|
|
594
655
|
"type": "boolean"
|
|
595
656
|
},
|
|
596
|
-
"
|
|
597
|
-
"description": "
|
|
598
|
-
"name": "
|
|
657
|
+
"sequence-id": {
|
|
658
|
+
"description": "Limit output to a single active sequence by ID (default: all active sequences)",
|
|
659
|
+
"name": "sequence-id",
|
|
599
660
|
"hasDynamicHelp": false,
|
|
600
661
|
"multiple": false,
|
|
601
662
|
"type": "option"
|
|
@@ -603,7 +664,7 @@
|
|
|
603
664
|
},
|
|
604
665
|
"hasDynamicHelp": false,
|
|
605
666
|
"hiddenAliases": [],
|
|
606
|
-
"id": "
|
|
667
|
+
"id": "sdk",
|
|
607
668
|
"pluginAlias": "@mailmodo/cli",
|
|
608
669
|
"pluginName": "@mailmodo/cli",
|
|
609
670
|
"pluginType": "core",
|
|
@@ -613,7 +674,7 @@
|
|
|
613
674
|
"relativePath": [
|
|
614
675
|
"dist",
|
|
615
676
|
"commands",
|
|
616
|
-
"
|
|
677
|
+
"sdk",
|
|
617
678
|
"index.js"
|
|
618
679
|
]
|
|
619
680
|
},
|
|
@@ -655,7 +716,54 @@
|
|
|
655
716
|
"status",
|
|
656
717
|
"index.js"
|
|
657
718
|
]
|
|
719
|
+
},
|
|
720
|
+
"settings": {
|
|
721
|
+
"aliases": [],
|
|
722
|
+
"args": {},
|
|
723
|
+
"description": "View and update project settings",
|
|
724
|
+
"examples": [
|
|
725
|
+
"<%= config.bin %> settings",
|
|
726
|
+
"<%= config.bin %> settings --set brand_color=#0F3460",
|
|
727
|
+
"<%= config.bin %> settings --json"
|
|
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
|
+
"set": {
|
|
744
|
+
"description": "Set a setting (format: key=value)",
|
|
745
|
+
"name": "set",
|
|
746
|
+
"hasDynamicHelp": false,
|
|
747
|
+
"multiple": false,
|
|
748
|
+
"type": "option"
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
"hasDynamicHelp": false,
|
|
752
|
+
"hiddenAliases": [],
|
|
753
|
+
"id": "settings",
|
|
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
|
+
"settings",
|
|
764
|
+
"index.js"
|
|
765
|
+
]
|
|
658
766
|
}
|
|
659
767
|
},
|
|
660
|
-
"version": "0.0.
|
|
768
|
+
"version": "0.0.51-beta.pr53.82"
|
|
661
769
|
}
|