@mailmodo/cli 0.0.14 → 0.0.15-beta.pr17.25
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/contacts/index.js +15 -5
- package/dist/commands/login/index.js +3 -3
- package/dist/lib/api-client.d.ts +6 -0
- package/dist/lib/api-client.js +8 -0
- package/dist/lib/constants.d.ts +1 -1
- package/dist/lib/constants.js +6 -1
- package/dist/lib/fetch-file.d.ts +14 -0
- package/dist/lib/fetch-file.js +51 -0
- package/oclif.manifest.json +50 -50
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { confirm } from '@inquirer/prompts';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
+
import { writeFile } from 'node:fs/promises';
|
|
4
5
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
5
6
|
import { API_ENDPOINTS } from '../../lib/constants.js';
|
|
6
7
|
export default class Contacts extends BaseCommand {
|
|
@@ -8,7 +9,7 @@ export default class Contacts extends BaseCommand {
|
|
|
8
9
|
static examples = [
|
|
9
10
|
'<%= config.bin %> contacts',
|
|
10
11
|
'<%= config.bin %> contacts --search sarah@example.com',
|
|
11
|
-
|
|
12
|
+
"<%= config.bin %> contacts --export # GDPR CSV → contacts.csv",
|
|
12
13
|
'<%= config.bin %> contacts --delete sarah@example.com',
|
|
13
14
|
];
|
|
14
15
|
static flags = {
|
|
@@ -18,7 +19,7 @@ export default class Contacts extends BaseCommand {
|
|
|
18
19
|
}),
|
|
19
20
|
export: Flags.boolean({
|
|
20
21
|
default: false,
|
|
21
|
-
description: 'Export all contacts as CSV',
|
|
22
|
+
description: 'Export all contacts as GDPR-compliant CSV (writes contacts.csv in the current directory)',
|
|
22
23
|
}),
|
|
23
24
|
search: Flags.string({ description: 'Search for a contact by email' }),
|
|
24
25
|
};
|
|
@@ -106,10 +107,19 @@ export default class Contacts extends BaseCommand {
|
|
|
106
107
|
return;
|
|
107
108
|
}
|
|
108
109
|
this.log(`\n ${chalk.green('✓')} Contact export started.`);
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
const { downloadUrl, status } = response.data;
|
|
111
|
+
if (!downloadUrl) {
|
|
112
|
+
this.log(`\n Export status: ${status ?? 'unknown'}. No download URL yet.\n`);
|
|
113
|
+
return;
|
|
111
114
|
}
|
|
112
|
-
|
|
115
|
+
const url = `https://${downloadUrl}`;
|
|
116
|
+
const fileResult = await this.apiClient.getFile(url);
|
|
117
|
+
if (!fileResult.ok) {
|
|
118
|
+
this.error(`Download failed: ${fileResult.status} ${fileResult.error ?? ''}\n` +
|
|
119
|
+
` URL: ${fileResult.debug.fullUrl}`);
|
|
120
|
+
}
|
|
121
|
+
await writeFile('contacts.csv', Buffer.from(fileResult.body));
|
|
122
|
+
this.log(`\n ${chalk.green('✓')} Contact export saved to ${chalk.cyan('contacts.csv')}\n`);
|
|
113
123
|
}
|
|
114
124
|
/**
|
|
115
125
|
* Performs a GDPR-compliant hard delete of a contact and all their
|
|
@@ -4,7 +4,7 @@ import open from 'open';
|
|
|
4
4
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
5
5
|
import { ApiClient } from '../../lib/api-client.js';
|
|
6
6
|
import { loadConfig, saveConfig } from '../../lib/config.js';
|
|
7
|
-
import { API_ENDPOINTS,
|
|
7
|
+
import { API_ENDPOINTS, LOGIN_URL } from '../../lib/constants.js';
|
|
8
8
|
export default class Login extends BaseCommand {
|
|
9
9
|
static description = 'Authenticate with Mailmodo using your API key';
|
|
10
10
|
static examples = [
|
|
@@ -44,9 +44,9 @@ export default class Login extends BaseCommand {
|
|
|
44
44
|
this.log('Detected MAILMODO_API_KEY from environment.');
|
|
45
45
|
}
|
|
46
46
|
else {
|
|
47
|
-
this.log(`\n Get your free API key at: ${chalk.cyan(
|
|
47
|
+
this.log(`\n Get your free API key at: ${chalk.cyan(LOGIN_URL)}\n`);
|
|
48
48
|
try {
|
|
49
|
-
await open(
|
|
49
|
+
await open(LOGIN_URL);
|
|
50
50
|
this.log(' Opening in browser...\n');
|
|
51
51
|
}
|
|
52
52
|
catch {
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FileFetchResult } from './fetch-file.js';
|
|
1
2
|
/**
|
|
2
3
|
* Request context attached to API responses for troubleshooting failed calls.
|
|
3
4
|
* Only the resolved URL is stored; it encodes origin, path, and query string.
|
|
@@ -38,6 +39,11 @@ export declare class ApiClient {
|
|
|
38
39
|
private request;
|
|
39
40
|
delete<T = Record<string, unknown>>(path: string): Promise<ApiResponse<T>>;
|
|
40
41
|
get<T = Record<string, unknown>>(path: string, params?: Record<string, string>): Promise<ApiResponse<T>>;
|
|
42
|
+
/**
|
|
43
|
+
* GET an absolute URL and return the raw body (e.g. CSV export). Uses the same
|
|
44
|
+
* Bearer auth as other requests; does not parse JSON.
|
|
45
|
+
*/
|
|
46
|
+
getFile(url: string): Promise<FileFetchResult>;
|
|
41
47
|
patch<T = Record<string, unknown>>(path: string, body?: Record<string, unknown>): Promise<ApiResponse<T>>;
|
|
42
48
|
post<T = Record<string, unknown>>(path: string, body?: Record<string, unknown> | unknown): Promise<ApiResponse<T>>;
|
|
43
49
|
postFormData<T = Record<string, unknown>>(path: string, formData: FormData): Promise<ApiResponse<T>>;
|
package/dist/lib/api-client.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { API_BASE_URL } from './constants.js';
|
|
2
|
+
import { fetchFileWithBearerAuth, } from './fetch-file.js';
|
|
2
3
|
/**
|
|
3
4
|
* HTTP client for the Mailmodo CLI API.
|
|
4
5
|
* Wraps the native fetch API with Bearer token authentication,
|
|
@@ -115,6 +116,13 @@ export class ApiClient {
|
|
|
115
116
|
async get(path, params) {
|
|
116
117
|
return this.request('GET', path, undefined, params);
|
|
117
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* GET an absolute URL and return the raw body (e.g. CSV export). Uses the same
|
|
121
|
+
* Bearer auth as other requests; does not parse JSON.
|
|
122
|
+
*/
|
|
123
|
+
async getFile(url) {
|
|
124
|
+
return fetchFileWithBearerAuth(url, this.apiKey);
|
|
125
|
+
}
|
|
118
126
|
async patch(path, body) {
|
|
119
127
|
return this.request('PATCH', path, body);
|
|
120
128
|
}
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare const API_ENDPOINTS: Readonly<{
|
|
|
18
18
|
PREVIEW: "/preview";
|
|
19
19
|
SEQUENCES: "/sequences";
|
|
20
20
|
}>;
|
|
21
|
-
export declare const
|
|
21
|
+
export declare const LOGIN_URL = "https://app-vertex-debug.azurewebsites.net/login.html";
|
|
22
22
|
export declare const DOCS_URL = "https://mailmodo.com/docs/cli";
|
|
23
23
|
export declare const DNS_GUIDE_URL = "https://mailmodo.com/docs/dns";
|
|
24
24
|
export declare const PREVIEW_PORT = 3421;
|
package/dist/lib/constants.js
CHANGED
|
@@ -24,7 +24,12 @@ export const API_ENDPOINTS = Object.freeze({
|
|
|
24
24
|
PREVIEW: '/preview',
|
|
25
25
|
SEQUENCES: '/sequences',
|
|
26
26
|
});
|
|
27
|
-
|
|
27
|
+
const DEV_LOGIN_URL = 'https://app-vertex-debug.azurewebsites.net/login.html';
|
|
28
|
+
// const PRODUCTION_LOGIN_URL = 'https://mailmodo.com/cli';
|
|
29
|
+
const PRODUCTION_LOGIN_URL = 'https://app-vertex-debug.azurewebsites.net/login.html';
|
|
30
|
+
export const LOGIN_URL = process.env.MAILMODO_DEV_TSX
|
|
31
|
+
? DEV_LOGIN_URL
|
|
32
|
+
: PRODUCTION_LOGIN_URL;
|
|
28
33
|
export const DOCS_URL = 'https://mailmodo.com/docs/cli';
|
|
29
34
|
export const DNS_GUIDE_URL = 'https://mailmodo.com/docs/dns';
|
|
30
35
|
export const PREVIEW_PORT = 3421;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface FileFetchResult {
|
|
2
|
+
body: ArrayBuffer;
|
|
3
|
+
debug: {
|
|
4
|
+
fullUrl: string;
|
|
5
|
+
};
|
|
6
|
+
error?: string;
|
|
7
|
+
ok: boolean;
|
|
8
|
+
status: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* GET an absolute URL and return the raw body (e.g. CSV). Uses Bearer auth;
|
|
12
|
+
* does not parse JSON.
|
|
13
|
+
*/
|
|
14
|
+
export declare function fetchFileWithBearerAuth(url: string, apiKey: string): Promise<FileFetchResult>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const USER_AGENT = '@mailmodo/cli';
|
|
2
|
+
/**
|
|
3
|
+
* GET an absolute URL and return the raw body (e.g. CSV). Uses Bearer auth;
|
|
4
|
+
* does not parse JSON.
|
|
5
|
+
*/
|
|
6
|
+
export async function fetchFileWithBearerAuth(url, apiKey) {
|
|
7
|
+
let href;
|
|
8
|
+
try {
|
|
9
|
+
href = new URL(url.trim()).toString();
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return {
|
|
13
|
+
body: new ArrayBuffer(0),
|
|
14
|
+
debug: { fullUrl: url.trim() },
|
|
15
|
+
error: 'Invalid URL',
|
|
16
|
+
ok: false,
|
|
17
|
+
status: 0,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const debug = { fullUrl: href };
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch(href, {
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Bearer ${apiKey}`,
|
|
25
|
+
'User-Agent': USER_AGENT,
|
|
26
|
+
},
|
|
27
|
+
method: 'GET',
|
|
28
|
+
});
|
|
29
|
+
const body = await response.arrayBuffer();
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
return {
|
|
32
|
+
body: new ArrayBuffer(0),
|
|
33
|
+
debug,
|
|
34
|
+
error: response.statusText,
|
|
35
|
+
ok: false,
|
|
36
|
+
status: response.status,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return { body, debug, ok: true, status: response.status };
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
const err = error;
|
|
43
|
+
return {
|
|
44
|
+
body: new ArrayBuffer(0),
|
|
45
|
+
debug,
|
|
46
|
+
error: err?.message || 'Request failed.',
|
|
47
|
+
ok: false,
|
|
48
|
+
status: 0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"examples": [
|
|
61
61
|
"<%= config.bin %> contacts",
|
|
62
62
|
"<%= config.bin %> contacts --search sarah@example.com",
|
|
63
|
-
"<%= config.bin %> contacts --export",
|
|
63
|
+
"<%= config.bin %> contacts --export # GDPR CSV → contacts.csv",
|
|
64
64
|
"<%= config.bin %> contacts --delete sarah@example.com"
|
|
65
65
|
],
|
|
66
66
|
"flags": {
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"type": "option"
|
|
86
86
|
},
|
|
87
87
|
"export": {
|
|
88
|
-
"description": "Export all contacts as CSV",
|
|
88
|
+
"description": "Export all contacts as GDPR-compliant CSV (writes contacts.csv in the current directory)",
|
|
89
89
|
"name": "export",
|
|
90
90
|
"allowNo": false,
|
|
91
91
|
"type": "boolean"
|
|
@@ -205,19 +205,13 @@
|
|
|
205
205
|
"index.js"
|
|
206
206
|
]
|
|
207
207
|
},
|
|
208
|
-
"
|
|
208
|
+
"emails": {
|
|
209
209
|
"aliases": [],
|
|
210
|
-
"args": {
|
|
211
|
-
|
|
212
|
-
"description": "Email ID to edit",
|
|
213
|
-
"name": "id",
|
|
214
|
-
"required": true
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
"description": "Edit an email using AI-assisted natural language changes",
|
|
210
|
+
"args": {},
|
|
211
|
+
"description": "List and view configured email sequences",
|
|
218
212
|
"examples": [
|
|
219
|
-
"<%= config.bin %>
|
|
220
|
-
"<%= config.bin %>
|
|
213
|
+
"<%= config.bin %> emails",
|
|
214
|
+
"<%= config.bin %> emails --json"
|
|
221
215
|
],
|
|
222
216
|
"flags": {
|
|
223
217
|
"json": {
|
|
@@ -232,18 +226,11 @@
|
|
|
232
226
|
"name": "yes",
|
|
233
227
|
"allowNo": false,
|
|
234
228
|
"type": "boolean"
|
|
235
|
-
},
|
|
236
|
-
"change": {
|
|
237
|
-
"description": "Natural language description of the change",
|
|
238
|
-
"name": "change",
|
|
239
|
-
"hasDynamicHelp": false,
|
|
240
|
-
"multiple": false,
|
|
241
|
-
"type": "option"
|
|
242
229
|
}
|
|
243
230
|
},
|
|
244
231
|
"hasDynamicHelp": false,
|
|
245
232
|
"hiddenAliases": [],
|
|
246
|
-
"id": "
|
|
233
|
+
"id": "emails",
|
|
247
234
|
"pluginAlias": "@mailmodo/cli",
|
|
248
235
|
"pluginName": "@mailmodo/cli",
|
|
249
236
|
"pluginType": "core",
|
|
@@ -253,17 +240,23 @@
|
|
|
253
240
|
"relativePath": [
|
|
254
241
|
"dist",
|
|
255
242
|
"commands",
|
|
256
|
-
"
|
|
243
|
+
"emails",
|
|
257
244
|
"index.js"
|
|
258
245
|
]
|
|
259
246
|
},
|
|
260
|
-
"
|
|
247
|
+
"edit": {
|
|
261
248
|
"aliases": [],
|
|
262
|
-
"args": {
|
|
263
|
-
|
|
249
|
+
"args": {
|
|
250
|
+
"id": {
|
|
251
|
+
"description": "Email ID to edit",
|
|
252
|
+
"name": "id",
|
|
253
|
+
"required": true
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
"description": "Edit an email using AI-assisted natural language changes",
|
|
264
257
|
"examples": [
|
|
265
|
-
"<%= config.bin %>
|
|
266
|
-
"<%= config.bin %>
|
|
258
|
+
"<%= config.bin %> edit welcome",
|
|
259
|
+
"<%= config.bin %> edit welcome --change \"make subject more urgent\" --yes"
|
|
267
260
|
],
|
|
268
261
|
"flags": {
|
|
269
262
|
"json": {
|
|
@@ -278,11 +271,18 @@
|
|
|
278
271
|
"name": "yes",
|
|
279
272
|
"allowNo": false,
|
|
280
273
|
"type": "boolean"
|
|
274
|
+
},
|
|
275
|
+
"change": {
|
|
276
|
+
"description": "Natural language description of the change",
|
|
277
|
+
"name": "change",
|
|
278
|
+
"hasDynamicHelp": false,
|
|
279
|
+
"multiple": false,
|
|
280
|
+
"type": "option"
|
|
281
281
|
}
|
|
282
282
|
},
|
|
283
283
|
"hasDynamicHelp": false,
|
|
284
284
|
"hiddenAliases": [],
|
|
285
|
-
"id": "
|
|
285
|
+
"id": "edit",
|
|
286
286
|
"pluginAlias": "@mailmodo/cli",
|
|
287
287
|
"pluginName": "@mailmodo/cli",
|
|
288
288
|
"pluginType": "core",
|
|
@@ -292,7 +292,7 @@
|
|
|
292
292
|
"relativePath": [
|
|
293
293
|
"dist",
|
|
294
294
|
"commands",
|
|
295
|
-
"
|
|
295
|
+
"edit",
|
|
296
296
|
"index.js"
|
|
297
297
|
]
|
|
298
298
|
},
|
|
@@ -531,14 +531,13 @@
|
|
|
531
531
|
"index.js"
|
|
532
532
|
]
|
|
533
533
|
},
|
|
534
|
-
"
|
|
534
|
+
"status": {
|
|
535
535
|
"aliases": [],
|
|
536
536
|
"args": {},
|
|
537
|
-
"description": "View and
|
|
537
|
+
"description": "View email performance metrics and quota usage",
|
|
538
538
|
"examples": [
|
|
539
|
-
"<%= config.bin %>
|
|
540
|
-
"<%= config.bin %>
|
|
541
|
-
"<%= config.bin %> settings --json"
|
|
539
|
+
"<%= config.bin %> status",
|
|
540
|
+
"<%= config.bin %> status --json"
|
|
542
541
|
],
|
|
543
542
|
"flags": {
|
|
544
543
|
"json": {
|
|
@@ -553,18 +552,11 @@
|
|
|
553
552
|
"name": "yes",
|
|
554
553
|
"allowNo": false,
|
|
555
554
|
"type": "boolean"
|
|
556
|
-
},
|
|
557
|
-
"set": {
|
|
558
|
-
"description": "Set a setting (format: key=value)",
|
|
559
|
-
"name": "set",
|
|
560
|
-
"hasDynamicHelp": false,
|
|
561
|
-
"multiple": false,
|
|
562
|
-
"type": "option"
|
|
563
555
|
}
|
|
564
556
|
},
|
|
565
557
|
"hasDynamicHelp": false,
|
|
566
558
|
"hiddenAliases": [],
|
|
567
|
-
"id": "
|
|
559
|
+
"id": "status",
|
|
568
560
|
"pluginAlias": "@mailmodo/cli",
|
|
569
561
|
"pluginName": "@mailmodo/cli",
|
|
570
562
|
"pluginType": "core",
|
|
@@ -574,17 +566,18 @@
|
|
|
574
566
|
"relativePath": [
|
|
575
567
|
"dist",
|
|
576
568
|
"commands",
|
|
577
|
-
"
|
|
569
|
+
"status",
|
|
578
570
|
"index.js"
|
|
579
571
|
]
|
|
580
572
|
},
|
|
581
|
-
"
|
|
573
|
+
"settings": {
|
|
582
574
|
"aliases": [],
|
|
583
575
|
"args": {},
|
|
584
|
-
"description": "View
|
|
576
|
+
"description": "View and update project settings",
|
|
585
577
|
"examples": [
|
|
586
|
-
"<%= config.bin %>
|
|
587
|
-
"<%= config.bin %>
|
|
578
|
+
"<%= config.bin %> settings",
|
|
579
|
+
"<%= config.bin %> settings --set brand_color=#0F3460",
|
|
580
|
+
"<%= config.bin %> settings --json"
|
|
588
581
|
],
|
|
589
582
|
"flags": {
|
|
590
583
|
"json": {
|
|
@@ -599,11 +592,18 @@
|
|
|
599
592
|
"name": "yes",
|
|
600
593
|
"allowNo": false,
|
|
601
594
|
"type": "boolean"
|
|
595
|
+
},
|
|
596
|
+
"set": {
|
|
597
|
+
"description": "Set a setting (format: key=value)",
|
|
598
|
+
"name": "set",
|
|
599
|
+
"hasDynamicHelp": false,
|
|
600
|
+
"multiple": false,
|
|
601
|
+
"type": "option"
|
|
602
602
|
}
|
|
603
603
|
},
|
|
604
604
|
"hasDynamicHelp": false,
|
|
605
605
|
"hiddenAliases": [],
|
|
606
|
-
"id": "
|
|
606
|
+
"id": "settings",
|
|
607
607
|
"pluginAlias": "@mailmodo/cli",
|
|
608
608
|
"pluginName": "@mailmodo/cli",
|
|
609
609
|
"pluginType": "core",
|
|
@@ -613,10 +613,10 @@
|
|
|
613
613
|
"relativePath": [
|
|
614
614
|
"dist",
|
|
615
615
|
"commands",
|
|
616
|
-
"
|
|
616
|
+
"settings",
|
|
617
617
|
"index.js"
|
|
618
618
|
]
|
|
619
619
|
}
|
|
620
620
|
},
|
|
621
|
-
"version": "0.0.
|
|
621
|
+
"version": "0.0.15-beta.pr17.25"
|
|
622
622
|
}
|