@mailmodo/cli 0.0.15 → 0.0.17-beta.pr19.28
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/init/index.js +1 -2
- 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 +1 -1
- package/dist/lib/fetch-file.d.ts +14 -0
- package/dist/lib/fetch-file.js +51 -0
- package/oclif.manifest.json +22 -22
- package/package.json +3 -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
|
|
@@ -110,8 +110,7 @@ export default class Init extends BaseCommand {
|
|
|
110
110
|
json: flags.json,
|
|
111
111
|
text: ' Generating email templates...',
|
|
112
112
|
}, () => this.apiClient.post(API_ENDPOINTS.GENERATE, {
|
|
113
|
-
|
|
114
|
-
productUrl,
|
|
113
|
+
...analysisPayload,
|
|
115
114
|
}));
|
|
116
115
|
if (!generateResponse.ok) {
|
|
117
116
|
this.handleApiError(generateResponse);
|
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
package/dist/lib/constants.js
CHANGED
|
@@ -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"
|
|
@@ -257,13 +257,13 @@
|
|
|
257
257
|
"index.js"
|
|
258
258
|
]
|
|
259
259
|
},
|
|
260
|
-
"
|
|
260
|
+
"emails": {
|
|
261
261
|
"aliases": [],
|
|
262
262
|
"args": {},
|
|
263
|
-
"description": "
|
|
263
|
+
"description": "List and view configured email sequences",
|
|
264
264
|
"examples": [
|
|
265
|
-
"<%= config.bin %>
|
|
266
|
-
"<%= config.bin %>
|
|
265
|
+
"<%= config.bin %> emails",
|
|
266
|
+
"<%= config.bin %> emails --json"
|
|
267
267
|
],
|
|
268
268
|
"flags": {
|
|
269
269
|
"json": {
|
|
@@ -278,18 +278,11 @@
|
|
|
278
278
|
"name": "yes",
|
|
279
279
|
"allowNo": false,
|
|
280
280
|
"type": "boolean"
|
|
281
|
-
},
|
|
282
|
-
"url": {
|
|
283
|
-
"description": "Product URL to analyze",
|
|
284
|
-
"name": "url",
|
|
285
|
-
"hasDynamicHelp": false,
|
|
286
|
-
"multiple": false,
|
|
287
|
-
"type": "option"
|
|
288
281
|
}
|
|
289
282
|
},
|
|
290
283
|
"hasDynamicHelp": false,
|
|
291
284
|
"hiddenAliases": [],
|
|
292
|
-
"id": "
|
|
285
|
+
"id": "emails",
|
|
293
286
|
"pluginAlias": "@mailmodo/cli",
|
|
294
287
|
"pluginName": "@mailmodo/cli",
|
|
295
288
|
"pluginType": "core",
|
|
@@ -299,17 +292,17 @@
|
|
|
299
292
|
"relativePath": [
|
|
300
293
|
"dist",
|
|
301
294
|
"commands",
|
|
302
|
-
"
|
|
295
|
+
"emails",
|
|
303
296
|
"index.js"
|
|
304
297
|
]
|
|
305
298
|
},
|
|
306
|
-
"
|
|
299
|
+
"init": {
|
|
307
300
|
"aliases": [],
|
|
308
301
|
"args": {},
|
|
309
|
-
"description": "
|
|
302
|
+
"description": "Analyze your product and generate email sequences",
|
|
310
303
|
"examples": [
|
|
311
|
-
"<%= config.bin %>
|
|
312
|
-
"<%= config.bin %>
|
|
304
|
+
"<%= config.bin %> init",
|
|
305
|
+
"<%= config.bin %> init --url https://myapp.com --yes"
|
|
313
306
|
],
|
|
314
307
|
"flags": {
|
|
315
308
|
"json": {
|
|
@@ -324,11 +317,18 @@
|
|
|
324
317
|
"name": "yes",
|
|
325
318
|
"allowNo": false,
|
|
326
319
|
"type": "boolean"
|
|
320
|
+
},
|
|
321
|
+
"url": {
|
|
322
|
+
"description": "Product URL to analyze",
|
|
323
|
+
"name": "url",
|
|
324
|
+
"hasDynamicHelp": false,
|
|
325
|
+
"multiple": false,
|
|
326
|
+
"type": "option"
|
|
327
327
|
}
|
|
328
328
|
},
|
|
329
329
|
"hasDynamicHelp": false,
|
|
330
330
|
"hiddenAliases": [],
|
|
331
|
-
"id": "
|
|
331
|
+
"id": "init",
|
|
332
332
|
"pluginAlias": "@mailmodo/cli",
|
|
333
333
|
"pluginName": "@mailmodo/cli",
|
|
334
334
|
"pluginType": "core",
|
|
@@ -338,7 +338,7 @@
|
|
|
338
338
|
"relativePath": [
|
|
339
339
|
"dist",
|
|
340
340
|
"commands",
|
|
341
|
-
"
|
|
341
|
+
"init",
|
|
342
342
|
"index.js"
|
|
343
343
|
]
|
|
344
344
|
},
|
|
@@ -618,5 +618,5 @@
|
|
|
618
618
|
]
|
|
619
619
|
}
|
|
620
620
|
},
|
|
621
|
-
"version": "0.0.
|
|
621
|
+
"version": "0.0.17-beta.pr19.28"
|
|
622
622
|
}
|
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.
|
|
4
|
+
"version": "0.0.17-beta.pr19.28",
|
|
5
5
|
"author": "provishalk",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mailmodo": "bin/run.js"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"@inquirer/prompts": "^8.3.2",
|
|
12
12
|
"@oclif/core": "^4",
|
|
13
13
|
"@oclif/plugin-help": "^6",
|
|
14
|
+
"@oclif/plugin-not-found": "^3",
|
|
14
15
|
"@oclif/plugin-plugins": "^5",
|
|
15
16
|
"chalk": "^5.6.2",
|
|
16
17
|
"js-yaml": "^4.1.1",
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"commands": "./dist/commands",
|
|
62
63
|
"plugins": [
|
|
63
64
|
"@oclif/plugin-help",
|
|
65
|
+
"@oclif/plugin-not-found",
|
|
64
66
|
"@oclif/plugin-plugins"
|
|
65
67
|
],
|
|
66
68
|
"topicSeparator": " "
|