@mailmodo/cli 0.0.14-beta.pr16.23 → 0.0.15-beta.pr17.24

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,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
- '<%= config.bin %> contacts --export',
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
- if (response.data?.downloadUrl) {
110
- this.log(` Download: ${chalk.cyan(response.data.downloadUrl)}`);
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
- this.log('');
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
@@ -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>>;
@@ -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
  }
@@ -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
+ }
@@ -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"
@@ -114,6 +114,45 @@
114
114
  "index.js"
115
115
  ]
116
116
  },
117
+ "deploy": {
118
+ "aliases": [],
119
+ "args": {},
120
+ "description": "Deploy email sequences and verify sending domain",
121
+ "examples": [
122
+ "<%= config.bin %> deploy",
123
+ "<%= config.bin %> deploy --yes"
124
+ ],
125
+ "flags": {
126
+ "json": {
127
+ "description": "Output as JSON",
128
+ "name": "json",
129
+ "allowNo": false,
130
+ "type": "boolean"
131
+ },
132
+ "yes": {
133
+ "char": "y",
134
+ "description": "Skip confirmation prompts",
135
+ "name": "yes",
136
+ "allowNo": false,
137
+ "type": "boolean"
138
+ }
139
+ },
140
+ "hasDynamicHelp": false,
141
+ "hiddenAliases": [],
142
+ "id": "deploy",
143
+ "pluginAlias": "@mailmodo/cli",
144
+ "pluginName": "@mailmodo/cli",
145
+ "pluginType": "core",
146
+ "strict": true,
147
+ "enableJsonFlag": false,
148
+ "isESM": true,
149
+ "relativePath": [
150
+ "dist",
151
+ "commands",
152
+ "deploy",
153
+ "index.js"
154
+ ]
155
+ },
117
156
  "domain": {
118
157
  "aliases": [],
119
158
  "args": {},
@@ -166,13 +205,13 @@
166
205
  "index.js"
167
206
  ]
168
207
  },
169
- "deploy": {
208
+ "emails": {
170
209
  "aliases": [],
171
210
  "args": {},
172
- "description": "Deploy email sequences and verify sending domain",
211
+ "description": "List and view configured email sequences",
173
212
  "examples": [
174
- "<%= config.bin %> deploy",
175
- "<%= config.bin %> deploy --yes"
213
+ "<%= config.bin %> emails",
214
+ "<%= config.bin %> emails --json"
176
215
  ],
177
216
  "flags": {
178
217
  "json": {
@@ -191,7 +230,7 @@
191
230
  },
192
231
  "hasDynamicHelp": false,
193
232
  "hiddenAliases": [],
194
- "id": "deploy",
233
+ "id": "emails",
195
234
  "pluginAlias": "@mailmodo/cli",
196
235
  "pluginName": "@mailmodo/cli",
197
236
  "pluginType": "core",
@@ -201,7 +240,7 @@
201
240
  "relativePath": [
202
241
  "dist",
203
242
  "commands",
204
- "deploy",
243
+ "emails",
205
244
  "index.js"
206
245
  ]
207
246
  },
@@ -257,45 +296,6 @@
257
296
  "index.js"
258
297
  ]
259
298
  },
260
- "emails": {
261
- "aliases": [],
262
- "args": {},
263
- "description": "List and view configured email sequences",
264
- "examples": [
265
- "<%= config.bin %> emails",
266
- "<%= config.bin %> emails --json"
267
- ],
268
- "flags": {
269
- "json": {
270
- "description": "Output as JSON",
271
- "name": "json",
272
- "allowNo": false,
273
- "type": "boolean"
274
- },
275
- "yes": {
276
- "char": "y",
277
- "description": "Skip confirmation prompts",
278
- "name": "yes",
279
- "allowNo": false,
280
- "type": "boolean"
281
- }
282
- },
283
- "hasDynamicHelp": false,
284
- "hiddenAliases": [],
285
- "id": "emails",
286
- "pluginAlias": "@mailmodo/cli",
287
- "pluginName": "@mailmodo/cli",
288
- "pluginType": "core",
289
- "strict": true,
290
- "enableJsonFlag": false,
291
- "isESM": true,
292
- "relativePath": [
293
- "dist",
294
- "commands",
295
- "emails",
296
- "index.js"
297
- ]
298
- },
299
299
  "init": {
300
300
  "aliases": [],
301
301
  "args": {},
@@ -618,5 +618,5 @@
618
618
  ]
619
619
  }
620
620
  },
621
- "version": "0.0.14-beta.pr16.23"
621
+ "version": "0.0.15-beta.pr17.24"
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.14-beta.pr16.23",
4
+ "version": "0.0.15-beta.pr17.24",
5
5
  "author": "provishalk",
6
6
  "bin": {
7
7
  "mailmodo": "bin/run.js"