emailr-cli 1.0.0

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/index.js ADDED
@@ -0,0 +1,1303 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command as Command9 } from "commander";
5
+
6
+ // src/commands/send.ts
7
+ import { Command } from "commander";
8
+ import { Emailr } from "@emailr/sdk";
9
+
10
+ // src/config.ts
11
+ import fs from "fs";
12
+ import os from "os";
13
+ import path from "path";
14
+ var CONFIG_PATHS = [
15
+ path.join(os.homedir(), ".emailrrc"),
16
+ path.join(os.homedir(), ".config", "emailr", "config.json")
17
+ ];
18
+ function loadConfig() {
19
+ if (process.env.EMAILR_API_KEY) {
20
+ return {
21
+ apiKey: process.env.EMAILR_API_KEY,
22
+ baseUrl: process.env.EMAILR_BASE_URL,
23
+ format: process.env.EMAILR_FORMAT || "table"
24
+ };
25
+ }
26
+ for (const configPath of CONFIG_PATHS) {
27
+ if (fs.existsSync(configPath)) {
28
+ try {
29
+ const content = fs.readFileSync(configPath, "utf-8");
30
+ const config = JSON.parse(content);
31
+ if (config.apiKey) {
32
+ return {
33
+ apiKey: config.apiKey,
34
+ baseUrl: config.baseUrl,
35
+ format: config.format || "table"
36
+ };
37
+ }
38
+ } catch {
39
+ }
40
+ }
41
+ }
42
+ throw new Error(
43
+ 'No API key configured.\n\nSet the EMAILR_API_KEY environment variable:\n export EMAILR_API_KEY=your-api-key\n\nOr create a config file at ~/.emailrrc:\n { "apiKey": "your-api-key" }\n\nOr run: emailr config set api-key <your-api-key>'
44
+ );
45
+ }
46
+ function getConfigPath() {
47
+ const configDir = path.join(os.homedir(), ".config", "emailr");
48
+ return path.join(configDir, "config.json");
49
+ }
50
+ function saveConfig(config) {
51
+ const configPath = getConfigPath();
52
+ const configDir = path.dirname(configPath);
53
+ if (!fs.existsSync(configDir)) {
54
+ fs.mkdirSync(configDir, { recursive: true });
55
+ }
56
+ let existingConfig = {};
57
+ if (fs.existsSync(configPath)) {
58
+ try {
59
+ existingConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
60
+ } catch {
61
+ }
62
+ }
63
+ const newConfig = { ...existingConfig, ...config };
64
+ fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n");
65
+ }
66
+ function getConfigValue(key) {
67
+ try {
68
+ const config = loadConfig();
69
+ return config[key]?.toString();
70
+ } catch {
71
+ return void 0;
72
+ }
73
+ }
74
+
75
+ // src/output.ts
76
+ import Table from "cli-table3";
77
+ import chalk from "chalk";
78
+ function output(data, format = "table") {
79
+ if (format === "json") {
80
+ console.log(JSON.stringify(data, null, 2));
81
+ } else {
82
+ printTable(data);
83
+ }
84
+ }
85
+ function printTable(data) {
86
+ if (Array.isArray(data)) {
87
+ printArrayTable(data);
88
+ } else if (typeof data === "object" && data !== null) {
89
+ printObjectTable(data);
90
+ } else {
91
+ console.log(data);
92
+ }
93
+ }
94
+ function printArrayTable(data) {
95
+ if (data.length === 0) {
96
+ console.log(chalk.gray("No results"));
97
+ return;
98
+ }
99
+ const firstItem = data[0];
100
+ if (typeof firstItem !== "object" || firstItem === null) {
101
+ data.forEach((item) => console.log(item));
102
+ return;
103
+ }
104
+ const keys = Object.keys(firstItem);
105
+ const table = new Table({
106
+ head: keys.map((k) => chalk.cyan(k)),
107
+ style: { head: [], border: [] }
108
+ });
109
+ for (const item of data) {
110
+ const row = keys.map((key) => {
111
+ const value = item[key];
112
+ return formatValue(value);
113
+ });
114
+ table.push(row);
115
+ }
116
+ console.log(table.toString());
117
+ }
118
+ function printObjectTable(data) {
119
+ const table = new Table({
120
+ style: { head: [], border: [] }
121
+ });
122
+ for (const [key, value] of Object.entries(data)) {
123
+ table.push([chalk.cyan(key), formatValue(value)]);
124
+ }
125
+ console.log(table.toString());
126
+ }
127
+ function formatValue(value) {
128
+ if (value === null || value === void 0) {
129
+ return chalk.gray("-");
130
+ }
131
+ if (typeof value === "boolean") {
132
+ return value ? chalk.green("\u2713") : chalk.red("\u2717");
133
+ }
134
+ if (typeof value === "object") {
135
+ return JSON.stringify(value);
136
+ }
137
+ return String(value);
138
+ }
139
+ function success(message) {
140
+ console.log(chalk.green("\u2713"), message);
141
+ }
142
+ function error(message) {
143
+ console.error(chalk.red("\u2717"), message);
144
+ }
145
+ function info(message) {
146
+ console.log(chalk.blue("\u2139"), message);
147
+ }
148
+
149
+ // src/commands/send.ts
150
+ function createSendCommand() {
151
+ const cmd = new Command("send").description("Send an email").requiredOption("--to <email>", "Recipient email address (comma-separated for multiple)").option("--from <email>", "Sender email address").option("--subject <subject>", "Email subject").option("--html <html>", "HTML content").option("--text <text>", "Plain text content").option("--template <id>", "Template ID to use").option("--template-data <json>", "Template data as JSON").option("--cc <emails>", "CC recipients (comma-separated)").option("--bcc <emails>", "BCC recipients (comma-separated)").option("--reply-to <email>", "Reply-to email address").option("--schedule <datetime>", "Schedule send time (ISO 8601)").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
152
+ try {
153
+ const config = loadConfig();
154
+ const client = new Emailr({
155
+ apiKey: config.apiKey,
156
+ baseUrl: config.baseUrl
157
+ });
158
+ const to = options.to.split(",").map((e) => e.trim());
159
+ const request = {
160
+ to: to.length === 1 ? to[0] : to
161
+ };
162
+ if (options.from) request.from = options.from;
163
+ if (options.subject) request.subject = options.subject;
164
+ if (options.html) request.html = options.html;
165
+ if (options.text) request.text = options.text;
166
+ if (options.template) request.template_id = options.template;
167
+ if (options.templateData) {
168
+ try {
169
+ request.template_data = JSON.parse(options.templateData);
170
+ } catch {
171
+ error("Invalid JSON for --template-data");
172
+ process.exit(1);
173
+ }
174
+ }
175
+ if (options.cc) {
176
+ const cc = options.cc.split(",").map((e) => e.trim());
177
+ request.cc = cc.length === 1 ? cc[0] : cc;
178
+ }
179
+ if (options.bcc) {
180
+ const bcc = options.bcc.split(",").map((e) => e.trim());
181
+ request.bcc = bcc.length === 1 ? bcc[0] : bcc;
182
+ }
183
+ if (options.replyTo) request.reply_to_email = options.replyTo;
184
+ if (options.schedule) request.scheduled_at = options.schedule;
185
+ const result = await client.emails.send(request);
186
+ if (options.format === "json") {
187
+ output(result, "json");
188
+ } else {
189
+ success(`Email sent successfully!`);
190
+ output({
191
+ "Message ID": result.message_id,
192
+ Recipients: result.recipients,
193
+ Status: result.status,
194
+ ...result.scheduled_at && { "Scheduled At": result.scheduled_at }
195
+ }, "table");
196
+ }
197
+ } catch (err) {
198
+ error(err instanceof Error ? err.message : "Failed to send email");
199
+ process.exit(1);
200
+ }
201
+ });
202
+ return cmd;
203
+ }
204
+
205
+ // src/commands/contacts.ts
206
+ import { Command as Command2 } from "commander";
207
+ import { Emailr as Emailr2 } from "@emailr/sdk";
208
+ function createContactsCommand() {
209
+ const cmd = new Command2("contacts").description("Manage contacts");
210
+ cmd.command("list").description("List all contacts").option("--limit <number>", "Number of contacts to return", "20").option("--offset <number>", "Offset for pagination", "0").option("--subscribed", "Only show subscribed contacts").option("--unsubscribed", "Only show unsubscribed contacts").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
211
+ try {
212
+ const config = loadConfig();
213
+ const client = new Emailr2({
214
+ apiKey: config.apiKey,
215
+ baseUrl: config.baseUrl
216
+ });
217
+ const params = {
218
+ limit: parseInt(options.limit, 10),
219
+ offset: parseInt(options.offset, 10)
220
+ };
221
+ if (options.subscribed) params.subscribed = true;
222
+ if (options.unsubscribed) params.subscribed = false;
223
+ const result = await client.contacts.list(params);
224
+ if (options.format === "json") {
225
+ output(result, "json");
226
+ } else {
227
+ const contacts = result.contacts.map((c) => ({
228
+ ID: c.id,
229
+ Email: c.email,
230
+ Name: [c.first_name, c.last_name].filter(Boolean).join(" ") || "-",
231
+ Subscribed: c.subscribed,
232
+ Created: c.created_at
233
+ }));
234
+ output(contacts, "table");
235
+ console.log(`
236
+ Total: ${result.total}`);
237
+ }
238
+ } catch (err) {
239
+ error(err instanceof Error ? err.message : "Failed to list contacts");
240
+ process.exit(1);
241
+ }
242
+ });
243
+ cmd.command("get <id>").description("Get a contact by ID").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
244
+ try {
245
+ const config = loadConfig();
246
+ const client = new Emailr2({
247
+ apiKey: config.apiKey,
248
+ baseUrl: config.baseUrl
249
+ });
250
+ const contact = await client.contacts.get(id);
251
+ output(contact, options.format);
252
+ } catch (err) {
253
+ error(err instanceof Error ? err.message : "Failed to get contact");
254
+ process.exit(1);
255
+ }
256
+ });
257
+ cmd.command("create").description("Create a new contact").requiredOption("--email <email>", "Contact email address").option("--first-name <name>", "First name").option("--last-name <name>", "Last name").option("--subscribed", "Mark as subscribed (default: true)").option("--metadata <json>", "Metadata as JSON").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
258
+ try {
259
+ const config = loadConfig();
260
+ const client = new Emailr2({
261
+ apiKey: config.apiKey,
262
+ baseUrl: config.baseUrl
263
+ });
264
+ const request = {
265
+ email: options.email
266
+ };
267
+ if (options.firstName) request.first_name = options.firstName;
268
+ if (options.lastName) request.last_name = options.lastName;
269
+ if (options.subscribed !== void 0) request.subscribed = options.subscribed;
270
+ if (options.metadata) {
271
+ try {
272
+ request.metadata = JSON.parse(options.metadata);
273
+ } catch {
274
+ error("Invalid JSON for --metadata");
275
+ process.exit(1);
276
+ }
277
+ }
278
+ const contact = await client.contacts.create(request);
279
+ if (options.format === "json") {
280
+ output(contact, "json");
281
+ } else {
282
+ success(`Contact created: ${contact.id}`);
283
+ output(contact, "table");
284
+ }
285
+ } catch (err) {
286
+ error(err instanceof Error ? err.message : "Failed to create contact");
287
+ process.exit(1);
288
+ }
289
+ });
290
+ cmd.command("update <id>").description("Update a contact").option("--email <email>", "New email address").option("--first-name <name>", "First name").option("--last-name <name>", "Last name").option("--subscribed", "Mark as subscribed").option("--unsubscribed", "Mark as unsubscribed").option("--metadata <json>", "Metadata as JSON").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
291
+ try {
292
+ const config = loadConfig();
293
+ const client = new Emailr2({
294
+ apiKey: config.apiKey,
295
+ baseUrl: config.baseUrl
296
+ });
297
+ const request = {};
298
+ if (options.email) request.email = options.email;
299
+ if (options.firstName) request.first_name = options.firstName;
300
+ if (options.lastName) request.last_name = options.lastName;
301
+ if (options.subscribed) request.subscribed = true;
302
+ if (options.unsubscribed) request.subscribed = false;
303
+ if (options.metadata) {
304
+ try {
305
+ request.metadata = JSON.parse(options.metadata);
306
+ } catch {
307
+ error("Invalid JSON for --metadata");
308
+ process.exit(1);
309
+ }
310
+ }
311
+ const contact = await client.contacts.update(id, request);
312
+ if (options.format === "json") {
313
+ output(contact, "json");
314
+ } else {
315
+ success(`Contact updated: ${contact.id}`);
316
+ output(contact, "table");
317
+ }
318
+ } catch (err) {
319
+ error(err instanceof Error ? err.message : "Failed to update contact");
320
+ process.exit(1);
321
+ }
322
+ });
323
+ cmd.command("delete <id>").description("Delete a contact").action(async (id) => {
324
+ try {
325
+ const config = loadConfig();
326
+ const client = new Emailr2({
327
+ apiKey: config.apiKey,
328
+ baseUrl: config.baseUrl
329
+ });
330
+ await client.contacts.delete(id);
331
+ success(`Contact deleted: ${id}`);
332
+ } catch (err) {
333
+ error(err instanceof Error ? err.message : "Failed to delete contact");
334
+ process.exit(1);
335
+ }
336
+ });
337
+ return cmd;
338
+ }
339
+
340
+ // src/commands/templates.ts
341
+ import { Command as Command3 } from "commander";
342
+ import { Emailr as Emailr3 } from "@emailr/sdk";
343
+ function createTemplatesCommand() {
344
+ const cmd = new Command3("templates").description("Manage email templates");
345
+ cmd.command("list").description("List all templates").option("--limit <number>", "Number of templates to return", "20").option("--page <number>", "Page number", "1").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
346
+ try {
347
+ const config = loadConfig();
348
+ const client = new Emailr3({
349
+ apiKey: config.apiKey,
350
+ baseUrl: config.baseUrl
351
+ });
352
+ const result = await client.templates.list({
353
+ limit: parseInt(options.limit, 10),
354
+ page: parseInt(options.page, 10)
355
+ });
356
+ if (options.format === "json") {
357
+ output(result, "json");
358
+ } else {
359
+ const templates = result.map((t) => ({
360
+ ID: t.id,
361
+ Name: t.name,
362
+ Subject: t.subject,
363
+ Variables: t.variables?.join(", ") || "-",
364
+ Created: t.created_at
365
+ }));
366
+ output(templates, "table");
367
+ console.log(`
368
+ Total: ${result.length}`);
369
+ }
370
+ } catch (err) {
371
+ error(err instanceof Error ? err.message : "Failed to list templates");
372
+ process.exit(1);
373
+ }
374
+ });
375
+ cmd.command("get <id>").description("Get a template by ID").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
376
+ try {
377
+ const config = loadConfig();
378
+ const client = new Emailr3({
379
+ apiKey: config.apiKey,
380
+ baseUrl: config.baseUrl
381
+ });
382
+ const template = await client.templates.get(id);
383
+ output(template, options.format);
384
+ } catch (err) {
385
+ error(err instanceof Error ? err.message : "Failed to get template");
386
+ process.exit(1);
387
+ }
388
+ });
389
+ cmd.command("create").description("Create a new template").requiredOption("--name <name>", "Template name").requiredOption("--subject <subject>", "Email subject").option("--html <html>", "HTML content").option("--text <text>", "Plain text content").option("--html-file <path>", "Read HTML content from file").option("--text-file <path>", "Read text content from file").option("--from <email>", "Default from email").option("--reply-to <email>", "Default reply-to email").option("--preview-text <text>", "Preview text").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
390
+ try {
391
+ const config = loadConfig();
392
+ const client = new Emailr3({
393
+ apiKey: config.apiKey,
394
+ baseUrl: config.baseUrl
395
+ });
396
+ const request = {
397
+ name: options.name,
398
+ subject: options.subject
399
+ };
400
+ if (options.htmlFile) {
401
+ const fs2 = await import("fs");
402
+ request.html_content = fs2.readFileSync(options.htmlFile, "utf-8");
403
+ } else if (options.html) {
404
+ request.html_content = options.html;
405
+ }
406
+ if (options.textFile) {
407
+ const fs2 = await import("fs");
408
+ request.text_content = fs2.readFileSync(options.textFile, "utf-8");
409
+ } else if (options.text) {
410
+ request.text_content = options.text;
411
+ }
412
+ if (options.from) request.from_email = options.from;
413
+ if (options.replyTo) request.reply_to = options.replyTo;
414
+ if (options.previewText) request.preview_text = options.previewText;
415
+ const template = await client.templates.create(request);
416
+ if (options.format === "json") {
417
+ output(template, "json");
418
+ } else {
419
+ success(`Template created: ${template.id}`);
420
+ output({
421
+ ID: template.id,
422
+ Name: template.name,
423
+ Subject: template.subject,
424
+ Variables: template.variables?.join(", ") || "-"
425
+ }, "table");
426
+ }
427
+ } catch (err) {
428
+ error(err instanceof Error ? err.message : "Failed to create template");
429
+ process.exit(1);
430
+ }
431
+ });
432
+ cmd.command("update <id>").description("Update a template").option("--name <name>", "Template name").option("--subject <subject>", "Email subject").option("--html <html>", "HTML content").option("--text <text>", "Plain text content").option("--html-file <path>", "Read HTML content from file").option("--text-file <path>", "Read text content from file").option("--from <email>", "Default from email").option("--reply-to <email>", "Default reply-to email").option("--preview-text <text>", "Preview text").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
433
+ try {
434
+ const config = loadConfig();
435
+ const client = new Emailr3({
436
+ apiKey: config.apiKey,
437
+ baseUrl: config.baseUrl
438
+ });
439
+ const request = {};
440
+ if (options.name) request.name = options.name;
441
+ if (options.subject) request.subject = options.subject;
442
+ if (options.htmlFile) {
443
+ const fs2 = await import("fs");
444
+ request.html_content = fs2.readFileSync(options.htmlFile, "utf-8");
445
+ } else if (options.html) {
446
+ request.html_content = options.html;
447
+ }
448
+ if (options.textFile) {
449
+ const fs2 = await import("fs");
450
+ request.text_content = fs2.readFileSync(options.textFile, "utf-8");
451
+ } else if (options.text) {
452
+ request.text_content = options.text;
453
+ }
454
+ if (options.from) request.from_email = options.from;
455
+ if (options.replyTo) request.reply_to = options.replyTo;
456
+ if (options.previewText) request.preview_text = options.previewText;
457
+ const template = await client.templates.update(id, request);
458
+ if (options.format === "json") {
459
+ output(template, "json");
460
+ } else {
461
+ success(`Template updated: ${template.id}`);
462
+ output(template, "table");
463
+ }
464
+ } catch (err) {
465
+ error(err instanceof Error ? err.message : "Failed to update template");
466
+ process.exit(1);
467
+ }
468
+ });
469
+ cmd.command("delete <id>").description("Delete a template").action(async (id) => {
470
+ try {
471
+ const config = loadConfig();
472
+ const client = new Emailr3({
473
+ apiKey: config.apiKey,
474
+ baseUrl: config.baseUrl
475
+ });
476
+ await client.templates.delete(id);
477
+ success(`Template deleted: ${id}`);
478
+ } catch (err) {
479
+ error(err instanceof Error ? err.message : "Failed to delete template");
480
+ process.exit(1);
481
+ }
482
+ });
483
+ return cmd;
484
+ }
485
+
486
+ // src/commands/domains.ts
487
+ import { Command as Command4 } from "commander";
488
+ import { Emailr as Emailr4 } from "@emailr/sdk";
489
+ function createDomainsCommand() {
490
+ const cmd = new Command4("domains").description("Manage sending domains");
491
+ cmd.command("list").description("List all domains").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
492
+ try {
493
+ const config = loadConfig();
494
+ const client = new Emailr4({
495
+ apiKey: config.apiKey,
496
+ baseUrl: config.baseUrl
497
+ });
498
+ const domains = await client.domains.list();
499
+ if (options.format === "json") {
500
+ output(domains, "json");
501
+ } else {
502
+ const formatted = domains.map((d) => ({
503
+ ID: d.id,
504
+ Domain: d.domain,
505
+ Status: d.status,
506
+ DKIM: d.dkim_verified,
507
+ SPF: d.spf_verified,
508
+ DMARC: d.dmarc_verified,
509
+ Created: d.created_at
510
+ }));
511
+ output(formatted, "table");
512
+ }
513
+ } catch (err) {
514
+ error(err instanceof Error ? err.message : "Failed to list domains");
515
+ process.exit(1);
516
+ }
517
+ });
518
+ cmd.command("get <id>").description("Get a domain by ID").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
519
+ try {
520
+ const config = loadConfig();
521
+ const client = new Emailr4({
522
+ apiKey: config.apiKey,
523
+ baseUrl: config.baseUrl
524
+ });
525
+ const domain = await client.domains.get(id);
526
+ output(domain, options.format);
527
+ } catch (err) {
528
+ error(err instanceof Error ? err.message : "Failed to get domain");
529
+ process.exit(1);
530
+ }
531
+ });
532
+ cmd.command("add <domain>").description("Add a new domain").option("--receiving-subdomain <subdomain>", "Subdomain for receiving emails").option("--format <format>", "Output format (json|table)", "table").action(async (domainName, options) => {
533
+ try {
534
+ const config = loadConfig();
535
+ const client = new Emailr4({
536
+ apiKey: config.apiKey,
537
+ baseUrl: config.baseUrl
538
+ });
539
+ const request = {
540
+ domain: domainName
541
+ };
542
+ if (options.receivingSubdomain) {
543
+ request.receivingSubdomain = options.receivingSubdomain;
544
+ }
545
+ const domain = await client.domains.add(request);
546
+ if (options.format === "json") {
547
+ output(domain, "json");
548
+ } else {
549
+ success(`Domain added: ${domain.domain}`);
550
+ info("Add the following DNS records to verify your domain:");
551
+ console.log("");
552
+ if (domain.dns_records) {
553
+ const records = [
554
+ { Type: "DKIM", ...formatDnsRecord(domain.dns_records.dkim) },
555
+ { Type: "SPF", ...formatDnsRecord(domain.dns_records.spf) },
556
+ { Type: "DMARC", ...formatDnsRecord(domain.dns_records.dmarc) }
557
+ ];
558
+ output(records, "table");
559
+ }
560
+ console.log("");
561
+ info(`Run 'emailr domains verify ${domain.id}' after adding DNS records`);
562
+ }
563
+ } catch (err) {
564
+ error(err instanceof Error ? err.message : "Failed to add domain");
565
+ process.exit(1);
566
+ }
567
+ });
568
+ cmd.command("verify <id>").description("Verify a domain").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
569
+ try {
570
+ const config = loadConfig();
571
+ const client = new Emailr4({
572
+ apiKey: config.apiKey,
573
+ baseUrl: config.baseUrl
574
+ });
575
+ const result = await client.domains.verify(id);
576
+ if (options.format === "json") {
577
+ output(result, "json");
578
+ } else {
579
+ if (result.verified) {
580
+ success("Domain verified successfully!");
581
+ } else {
582
+ info(`Domain status: ${result.status}`);
583
+ if (result.dkim_status) {
584
+ info(`DKIM status: ${result.dkim_status}`);
585
+ }
586
+ }
587
+ }
588
+ } catch (err) {
589
+ error(err instanceof Error ? err.message : "Failed to verify domain");
590
+ process.exit(1);
591
+ }
592
+ });
593
+ cmd.command("check-dns <id>").description("Check DNS records for a domain").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
594
+ try {
595
+ const config = loadConfig();
596
+ const client = new Emailr4({
597
+ apiKey: config.apiKey,
598
+ baseUrl: config.baseUrl
599
+ });
600
+ const result = await client.domains.checkDns(id);
601
+ if (options.format === "json") {
602
+ output(result, "json");
603
+ } else {
604
+ const records = Object.entries(result).map(([name, status]) => ({
605
+ Record: name,
606
+ Verified: status.verified,
607
+ Expected: status.expected || "-",
608
+ Found: status.found?.join(", ") || "-"
609
+ }));
610
+ output(records, "table");
611
+ }
612
+ } catch (err) {
613
+ error(err instanceof Error ? err.message : "Failed to check DNS");
614
+ process.exit(1);
615
+ }
616
+ });
617
+ cmd.command("delete <id>").description("Delete a domain").action(async (id) => {
618
+ try {
619
+ const config = loadConfig();
620
+ const client = new Emailr4({
621
+ apiKey: config.apiKey,
622
+ baseUrl: config.baseUrl
623
+ });
624
+ await client.domains.delete(id);
625
+ success(`Domain deleted: ${id}`);
626
+ } catch (err) {
627
+ error(err instanceof Error ? err.message : "Failed to delete domain");
628
+ process.exit(1);
629
+ }
630
+ });
631
+ return cmd;
632
+ }
633
+ function formatDnsRecord(record) {
634
+ return {
635
+ "Record Type": record.type,
636
+ Name: record.name,
637
+ Value: record.value.length > 50 ? record.value.substring(0, 47) + "..." : record.value,
638
+ ...record.priority !== void 0 && { Priority: record.priority }
639
+ };
640
+ }
641
+
642
+ // src/commands/config.ts
643
+ import { Command as Command5 } from "commander";
644
+ function createConfigCommand() {
645
+ const cmd = new Command5("config").description("Manage CLI configuration");
646
+ cmd.command("set <key> <value>").description("Set a configuration value").action(async (key, value) => {
647
+ try {
648
+ const validKeys = ["api-key", "base-url", "format"];
649
+ const normalizedKey = key.toLowerCase();
650
+ if (!validKeys.includes(normalizedKey)) {
651
+ error(`Invalid config key: ${key}`);
652
+ info(`Valid keys: ${validKeys.join(", ")}`);
653
+ process.exit(1);
654
+ }
655
+ const keyMap = {
656
+ "api-key": "apiKey",
657
+ "base-url": "baseUrl",
658
+ "format": "format"
659
+ };
660
+ const configKey = keyMap[normalizedKey];
661
+ if (normalizedKey === "format" && !["json", "table"].includes(value)) {
662
+ error(`Invalid format value: ${value}`);
663
+ info("Valid formats: json, table");
664
+ process.exit(1);
665
+ }
666
+ saveConfig({ [configKey]: value });
667
+ success(`Configuration saved: ${key} = ${normalizedKey === "api-key" ? "***" : value}`);
668
+ info(`Config file: ${getConfigPath()}`);
669
+ } catch (err) {
670
+ error(err instanceof Error ? err.message : "Failed to save configuration");
671
+ process.exit(1);
672
+ }
673
+ });
674
+ cmd.command("get <key>").description("Get a configuration value").action(async (key) => {
675
+ try {
676
+ const validKeys = ["api-key", "base-url", "format"];
677
+ const normalizedKey = key.toLowerCase();
678
+ if (!validKeys.includes(normalizedKey)) {
679
+ error(`Invalid config key: ${key}`);
680
+ info(`Valid keys: ${validKeys.join(", ")}`);
681
+ process.exit(1);
682
+ }
683
+ const keyMap = {
684
+ "api-key": "apiKey",
685
+ "base-url": "baseUrl",
686
+ "format": "format"
687
+ };
688
+ const configKey = keyMap[normalizedKey];
689
+ const value = getConfigValue(configKey);
690
+ if (value) {
691
+ if (normalizedKey === "api-key") {
692
+ console.log(value.substring(0, 8) + "..." + value.substring(value.length - 4));
693
+ } else {
694
+ console.log(value);
695
+ }
696
+ } else {
697
+ info(`${key} is not set`);
698
+ }
699
+ } catch (err) {
700
+ error(err instanceof Error ? err.message : "Failed to get configuration");
701
+ process.exit(1);
702
+ }
703
+ });
704
+ cmd.command("list").description("List all configuration values").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
705
+ try {
706
+ const config = loadConfig();
707
+ const configData = {
708
+ "api-key": config.apiKey ? config.apiKey.substring(0, 8) + "..." + config.apiKey.substring(config.apiKey.length - 4) : "(not set)",
709
+ "base-url": config.baseUrl || "(default)",
710
+ "format": config.format || "table"
711
+ };
712
+ if (options.format === "json") {
713
+ output(configData, "json");
714
+ } else {
715
+ const rows = Object.entries(configData).map(([key, value]) => ({
716
+ Key: key,
717
+ Value: value
718
+ }));
719
+ output(rows, "table");
720
+ }
721
+ console.log("");
722
+ info(`Config file: ${getConfigPath()}`);
723
+ } catch (err) {
724
+ if (err instanceof Error && err.message.includes("No API key configured")) {
725
+ info("No configuration found.");
726
+ info(`Run 'emailr config set api-key <your-api-key>' to get started.`);
727
+ } else {
728
+ error(err instanceof Error ? err.message : "Failed to list configuration");
729
+ process.exit(1);
730
+ }
731
+ }
732
+ });
733
+ cmd.command("path").description("Show the configuration file path").action(() => {
734
+ console.log(getConfigPath());
735
+ });
736
+ cmd.command("init").description("Initialize configuration interactively").option("--api-key <key>", "API key to use").option("--base-url <url>", "Base URL for API").action(async (options) => {
737
+ try {
738
+ if (options.apiKey) {
739
+ saveConfig({
740
+ apiKey: options.apiKey,
741
+ baseUrl: options.baseUrl
742
+ });
743
+ success("Configuration initialized!");
744
+ info(`Config file: ${getConfigPath()}`);
745
+ } else {
746
+ info("Initialize your Emailr CLI configuration:");
747
+ console.log("");
748
+ info("Run with --api-key flag:");
749
+ console.log(" emailr config init --api-key <your-api-key>");
750
+ console.log("");
751
+ info("Or set environment variable:");
752
+ console.log(" export EMAILR_API_KEY=<your-api-key>");
753
+ }
754
+ } catch (err) {
755
+ error(err instanceof Error ? err.message : "Failed to initialize configuration");
756
+ process.exit(1);
757
+ }
758
+ });
759
+ return cmd;
760
+ }
761
+
762
+ // src/commands/broadcasts.ts
763
+ import { Command as Command6 } from "commander";
764
+ import { Emailr as Emailr5 } from "@emailr/sdk";
765
+ function createBroadcastsCommand() {
766
+ const cmd = new Command6("broadcasts").description("Manage broadcast campaigns");
767
+ cmd.command("list").description("List all broadcasts").option("--status <status>", "Filter by status (draft, scheduled, sending, sent)").option("--limit <number>", "Number of broadcasts to return", "20").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
768
+ try {
769
+ const config = loadConfig();
770
+ const client = new Emailr5({
771
+ apiKey: config.apiKey,
772
+ baseUrl: config.baseUrl
773
+ });
774
+ const broadcasts = await client.broadcasts.list({
775
+ status: options.status,
776
+ limit: parseInt(options.limit)
777
+ });
778
+ if (options.format === "json") {
779
+ output(broadcasts, "json");
780
+ } else {
781
+ if (broadcasts.length === 0) {
782
+ console.log("No broadcasts found.");
783
+ return;
784
+ }
785
+ const tableData = broadcasts.map((b) => ({
786
+ ID: b.id,
787
+ Name: b.name,
788
+ Subject: b.subject.substring(0, 30) + (b.subject.length > 30 ? "..." : ""),
789
+ Status: b.status,
790
+ Recipients: b.total_recipients || 0,
791
+ "Sent": b.sent_count || 0,
792
+ "Created": new Date(b.created_at).toLocaleDateString()
793
+ }));
794
+ output(tableData, "table");
795
+ }
796
+ } catch (err) {
797
+ error(err instanceof Error ? err.message : "Failed to list broadcasts");
798
+ process.exit(1);
799
+ }
800
+ });
801
+ cmd.command("get <id>").description("Get broadcast details").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
802
+ try {
803
+ const config = loadConfig();
804
+ const client = new Emailr5({
805
+ apiKey: config.apiKey,
806
+ baseUrl: config.baseUrl
807
+ });
808
+ const broadcast = await client.broadcasts.get(id);
809
+ if (options.format === "json") {
810
+ output(broadcast, "json");
811
+ } else {
812
+ output({
813
+ ID: broadcast.id,
814
+ Name: broadcast.name,
815
+ Subject: broadcast.subject,
816
+ "From Email": broadcast.from_email,
817
+ Status: broadcast.status,
818
+ "Total Recipients": broadcast.total_recipients || 0,
819
+ "Sent Count": broadcast.sent_count || 0,
820
+ "Delivered": broadcast.delivered_count || 0,
821
+ "Opened": broadcast.opened_count || 0,
822
+ "Clicked": broadcast.clicked_count || 0,
823
+ "Bounced": broadcast.bounced_count || 0,
824
+ "Scheduled At": broadcast.scheduled_at || "N/A",
825
+ "Started At": broadcast.started_at || "N/A",
826
+ "Completed At": broadcast.completed_at || "N/A",
827
+ "Created At": broadcast.created_at
828
+ }, "table");
829
+ }
830
+ } catch (err) {
831
+ error(err instanceof Error ? err.message : "Failed to get broadcast");
832
+ process.exit(1);
833
+ }
834
+ });
835
+ cmd.command("create").description("Create a new broadcast").requiredOption("--name <name>", "Broadcast name").requiredOption("--subject <subject>", "Email subject").requiredOption("--from <email>", "Sender email address").option("--template <id>", "Template ID to use").option("--segment <id>", "Segment ID to target").option("--html <html>", "HTML content").option("--text <text>", "Plain text content").option("--schedule <datetime>", "Schedule time (ISO 8601)").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
836
+ try {
837
+ const config = loadConfig();
838
+ const client = new Emailr5({
839
+ apiKey: config.apiKey,
840
+ baseUrl: config.baseUrl
841
+ });
842
+ const broadcast = await client.broadcasts.create({
843
+ name: options.name,
844
+ subject: options.subject,
845
+ from_email: options.from,
846
+ template_id: options.template,
847
+ segment_id: options.segment,
848
+ html_content: options.html,
849
+ text_content: options.text,
850
+ scheduled_at: options.schedule
851
+ });
852
+ if (options.format === "json") {
853
+ output(broadcast, "json");
854
+ } else {
855
+ success("Broadcast created successfully!");
856
+ output({
857
+ ID: broadcast.id,
858
+ Name: broadcast.name,
859
+ Status: broadcast.status,
860
+ "Scheduled At": broadcast.scheduled_at || "Not scheduled"
861
+ }, "table");
862
+ }
863
+ } catch (err) {
864
+ error(err instanceof Error ? err.message : "Failed to create broadcast");
865
+ process.exit(1);
866
+ }
867
+ });
868
+ cmd.command("send <id>").description("Send a broadcast immediately").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
869
+ try {
870
+ const config = loadConfig();
871
+ const client = new Emailr5({
872
+ apiKey: config.apiKey,
873
+ baseUrl: config.baseUrl
874
+ });
875
+ const result = await client.broadcasts.send(id);
876
+ if (options.format === "json") {
877
+ output(result, "json");
878
+ } else {
879
+ success("Broadcast sent successfully!");
880
+ output({
881
+ Success: result.success,
882
+ Sent: result.sent,
883
+ Total: result.total
884
+ }, "table");
885
+ }
886
+ } catch (err) {
887
+ error(err instanceof Error ? err.message : "Failed to send broadcast");
888
+ process.exit(1);
889
+ }
890
+ });
891
+ cmd.command("schedule <id>").description("Schedule a broadcast").requiredOption("--at <datetime>", "Schedule time (ISO 8601)").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
892
+ try {
893
+ const config = loadConfig();
894
+ const client = new Emailr5({
895
+ apiKey: config.apiKey,
896
+ baseUrl: config.baseUrl
897
+ });
898
+ const broadcast = await client.broadcasts.schedule(id, options.at);
899
+ if (options.format === "json") {
900
+ output(broadcast, "json");
901
+ } else {
902
+ success("Broadcast scheduled successfully!");
903
+ output({
904
+ ID: broadcast.id,
905
+ Name: broadcast.name,
906
+ Status: broadcast.status,
907
+ "Scheduled At": broadcast.scheduled_at
908
+ }, "table");
909
+ }
910
+ } catch (err) {
911
+ error(err instanceof Error ? err.message : "Failed to schedule broadcast");
912
+ process.exit(1);
913
+ }
914
+ });
915
+ cmd.command("cancel <id>").description("Cancel a scheduled broadcast").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
916
+ try {
917
+ const config = loadConfig();
918
+ const client = new Emailr5({
919
+ apiKey: config.apiKey,
920
+ baseUrl: config.baseUrl
921
+ });
922
+ const result = await client.broadcasts.cancel(id);
923
+ if (options.format === "json") {
924
+ output(result, "json");
925
+ } else {
926
+ success("Broadcast cancelled successfully!");
927
+ }
928
+ } catch (err) {
929
+ error(err instanceof Error ? err.message : "Failed to cancel broadcast");
930
+ process.exit(1);
931
+ }
932
+ });
933
+ cmd.command("delete <id>").description("Delete a broadcast").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
934
+ try {
935
+ const config = loadConfig();
936
+ const client = new Emailr5({
937
+ apiKey: config.apiKey,
938
+ baseUrl: config.baseUrl
939
+ });
940
+ const result = await client.broadcasts.delete(id);
941
+ if (options.format === "json") {
942
+ output(result, "json");
943
+ } else {
944
+ success("Broadcast deleted successfully!");
945
+ }
946
+ } catch (err) {
947
+ error(err instanceof Error ? err.message : "Failed to delete broadcast");
948
+ process.exit(1);
949
+ }
950
+ });
951
+ return cmd;
952
+ }
953
+
954
+ // src/commands/webhooks.ts
955
+ import { Command as Command7 } from "commander";
956
+ import { Emailr as Emailr6 } from "@emailr/sdk";
957
+ function createWebhooksCommand() {
958
+ const cmd = new Command7("webhooks").description("Manage webhooks");
959
+ cmd.command("list").description("List all webhooks").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
960
+ try {
961
+ const config = loadConfig();
962
+ const client = new Emailr6({
963
+ apiKey: config.apiKey,
964
+ baseUrl: config.baseUrl
965
+ });
966
+ const webhooks = await client.webhooks.list();
967
+ if (options.format === "json") {
968
+ output(webhooks, "json");
969
+ } else {
970
+ if (webhooks.length === 0) {
971
+ console.log("No webhooks found.");
972
+ return;
973
+ }
974
+ const tableData = webhooks.map((w) => ({
975
+ ID: w.id,
976
+ Name: w.name,
977
+ URL: w.url.substring(0, 40) + (w.url.length > 40 ? "..." : ""),
978
+ Events: w.events.join(", "),
979
+ Active: w.active ? "Yes" : "No"
980
+ }));
981
+ output(tableData, "table");
982
+ }
983
+ } catch (err) {
984
+ error(err instanceof Error ? err.message : "Failed to list webhooks");
985
+ process.exit(1);
986
+ }
987
+ });
988
+ cmd.command("get <id>").description("Get webhook details").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
989
+ try {
990
+ const config = loadConfig();
991
+ const client = new Emailr6({
992
+ apiKey: config.apiKey,
993
+ baseUrl: config.baseUrl
994
+ });
995
+ const webhook = await client.webhooks.get(id);
996
+ if (options.format === "json") {
997
+ output(webhook, "json");
998
+ } else {
999
+ output({
1000
+ ID: webhook.id,
1001
+ Name: webhook.name,
1002
+ URL: webhook.url,
1003
+ Events: webhook.events.join(", "),
1004
+ Active: webhook.active ? "Yes" : "No",
1005
+ Secret: webhook.secret,
1006
+ "Created At": webhook.created_at
1007
+ }, "table");
1008
+ }
1009
+ } catch (err) {
1010
+ error(err instanceof Error ? err.message : "Failed to get webhook");
1011
+ process.exit(1);
1012
+ }
1013
+ });
1014
+ cmd.command("create").description("Create a new webhook").requiredOption("--name <name>", "Webhook name").requiredOption("--url <url>", "Webhook URL").requiredOption("--events <events>", "Events to subscribe to (comma-separated)").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
1015
+ try {
1016
+ const config = loadConfig();
1017
+ const client = new Emailr6({
1018
+ apiKey: config.apiKey,
1019
+ baseUrl: config.baseUrl
1020
+ });
1021
+ const events = options.events.split(",").map((e) => e.trim());
1022
+ const webhook = await client.webhooks.create({
1023
+ name: options.name,
1024
+ url: options.url,
1025
+ events
1026
+ });
1027
+ if (options.format === "json") {
1028
+ output(webhook, "json");
1029
+ } else {
1030
+ success("Webhook created successfully!");
1031
+ output({
1032
+ ID: webhook.id,
1033
+ Name: webhook.name,
1034
+ URL: webhook.url,
1035
+ Secret: webhook.secret
1036
+ }, "table");
1037
+ }
1038
+ } catch (err) {
1039
+ error(err instanceof Error ? err.message : "Failed to create webhook");
1040
+ process.exit(1);
1041
+ }
1042
+ });
1043
+ cmd.command("update <id>").description("Update a webhook").option("--name <name>", "New webhook name").option("--url <url>", "New webhook URL").option("--events <events>", "New events (comma-separated)").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
1044
+ try {
1045
+ const config = loadConfig();
1046
+ const client = new Emailr6({
1047
+ apiKey: config.apiKey,
1048
+ baseUrl: config.baseUrl
1049
+ });
1050
+ const updateData = {};
1051
+ if (options.name) updateData.name = options.name;
1052
+ if (options.url) updateData.url = options.url;
1053
+ if (options.events) {
1054
+ updateData.events = options.events.split(",").map((e) => e.trim());
1055
+ }
1056
+ const webhook = await client.webhooks.update(id, updateData);
1057
+ if (options.format === "json") {
1058
+ output(webhook, "json");
1059
+ } else {
1060
+ success("Webhook updated successfully!");
1061
+ output({
1062
+ ID: webhook.id,
1063
+ Name: webhook.name,
1064
+ URL: webhook.url
1065
+ }, "table");
1066
+ }
1067
+ } catch (err) {
1068
+ error(err instanceof Error ? err.message : "Failed to update webhook");
1069
+ process.exit(1);
1070
+ }
1071
+ });
1072
+ cmd.command("enable <id>").description("Enable a webhook").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
1073
+ try {
1074
+ const config = loadConfig();
1075
+ const client = new Emailr6({
1076
+ apiKey: config.apiKey,
1077
+ baseUrl: config.baseUrl
1078
+ });
1079
+ const result = await client.webhooks.enable(id);
1080
+ if (options.format === "json") {
1081
+ output(result, "json");
1082
+ } else {
1083
+ success("Webhook enabled successfully!");
1084
+ }
1085
+ } catch (err) {
1086
+ error(err instanceof Error ? err.message : "Failed to enable webhook");
1087
+ process.exit(1);
1088
+ }
1089
+ });
1090
+ cmd.command("disable <id>").description("Disable a webhook").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
1091
+ try {
1092
+ const config = loadConfig();
1093
+ const client = new Emailr6({
1094
+ apiKey: config.apiKey,
1095
+ baseUrl: config.baseUrl
1096
+ });
1097
+ const result = await client.webhooks.disable(id);
1098
+ if (options.format === "json") {
1099
+ output(result, "json");
1100
+ } else {
1101
+ success("Webhook disabled successfully!");
1102
+ }
1103
+ } catch (err) {
1104
+ error(err instanceof Error ? err.message : "Failed to disable webhook");
1105
+ process.exit(1);
1106
+ }
1107
+ });
1108
+ cmd.command("delete <id>").description("Delete a webhook").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
1109
+ try {
1110
+ const config = loadConfig();
1111
+ const client = new Emailr6({
1112
+ apiKey: config.apiKey,
1113
+ baseUrl: config.baseUrl
1114
+ });
1115
+ const result = await client.webhooks.delete(id);
1116
+ if (options.format === "json") {
1117
+ output(result, "json");
1118
+ } else {
1119
+ success("Webhook deleted successfully!");
1120
+ }
1121
+ } catch (err) {
1122
+ error(err instanceof Error ? err.message : "Failed to delete webhook");
1123
+ process.exit(1);
1124
+ }
1125
+ });
1126
+ return cmd;
1127
+ }
1128
+
1129
+ // src/commands/segments.ts
1130
+ import { Command as Command8 } from "commander";
1131
+ import { Emailr as Emailr7 } from "@emailr/sdk";
1132
+ function createSegmentsCommand() {
1133
+ const cmd = new Command8("segments").description("Manage contact segments");
1134
+ cmd.command("list").description("List all segments").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
1135
+ try {
1136
+ const config = loadConfig();
1137
+ const client = new Emailr7({
1138
+ apiKey: config.apiKey,
1139
+ baseUrl: config.baseUrl
1140
+ });
1141
+ const segments = await client.segments.list();
1142
+ if (options.format === "json") {
1143
+ output(segments, "json");
1144
+ } else {
1145
+ if (segments.length === 0) {
1146
+ console.log("No segments found.");
1147
+ return;
1148
+ }
1149
+ const tableData = segments.map((s) => ({
1150
+ ID: s.id,
1151
+ Name: s.name,
1152
+ Description: (s.description || "").substring(0, 30) + ((s.description?.length || 0) > 30 ? "..." : ""),
1153
+ "Created At": new Date(s.created_at).toLocaleDateString()
1154
+ }));
1155
+ output(tableData, "table");
1156
+ }
1157
+ } catch (err) {
1158
+ error(err instanceof Error ? err.message : "Failed to list segments");
1159
+ process.exit(1);
1160
+ }
1161
+ });
1162
+ cmd.command("get <id>").description("Get segment details").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
1163
+ try {
1164
+ const config = loadConfig();
1165
+ const client = new Emailr7({
1166
+ apiKey: config.apiKey,
1167
+ baseUrl: config.baseUrl
1168
+ });
1169
+ const segment = await client.segments.get(id);
1170
+ if (options.format === "json") {
1171
+ output(segment, "json");
1172
+ } else {
1173
+ output({
1174
+ ID: segment.id,
1175
+ Name: segment.name,
1176
+ Description: segment.description || "N/A",
1177
+ Conditions: JSON.stringify(segment.conditions),
1178
+ "Created At": segment.created_at,
1179
+ "Updated At": segment.updated_at
1180
+ }, "table");
1181
+ }
1182
+ } catch (err) {
1183
+ error(err instanceof Error ? err.message : "Failed to get segment");
1184
+ process.exit(1);
1185
+ }
1186
+ });
1187
+ cmd.command("create").description("Create a new segment").requiredOption("--name <name>", "Segment name").requiredOption("--conditions <json>", "Segment conditions as JSON").option("--description <description>", "Segment description").option("--format <format>", "Output format (json|table)", "table").action(async (options) => {
1188
+ try {
1189
+ const config = loadConfig();
1190
+ const client = new Emailr7({
1191
+ apiKey: config.apiKey,
1192
+ baseUrl: config.baseUrl
1193
+ });
1194
+ let conditions;
1195
+ try {
1196
+ conditions = JSON.parse(options.conditions);
1197
+ } catch {
1198
+ error("Invalid JSON for --conditions");
1199
+ process.exit(1);
1200
+ }
1201
+ const segment = await client.segments.create({
1202
+ name: options.name,
1203
+ description: options.description,
1204
+ conditions
1205
+ });
1206
+ if (options.format === "json") {
1207
+ output(segment, "json");
1208
+ } else {
1209
+ success("Segment created successfully!");
1210
+ output({
1211
+ ID: segment.id,
1212
+ Name: segment.name
1213
+ }, "table");
1214
+ }
1215
+ } catch (err) {
1216
+ error(err instanceof Error ? err.message : "Failed to create segment");
1217
+ process.exit(1);
1218
+ }
1219
+ });
1220
+ cmd.command("update <id>").description("Update a segment").option("--name <name>", "New segment name").option("--description <description>", "New description").option("--conditions <json>", "New conditions as JSON").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
1221
+ try {
1222
+ const config = loadConfig();
1223
+ const client = new Emailr7({
1224
+ apiKey: config.apiKey,
1225
+ baseUrl: config.baseUrl
1226
+ });
1227
+ const updateData = {};
1228
+ if (options.name) updateData.name = options.name;
1229
+ if (options.description) updateData.description = options.description;
1230
+ if (options.conditions) {
1231
+ try {
1232
+ updateData.conditions = JSON.parse(options.conditions);
1233
+ } catch {
1234
+ error("Invalid JSON for --conditions");
1235
+ process.exit(1);
1236
+ }
1237
+ }
1238
+ const segment = await client.segments.update(id, updateData);
1239
+ if (options.format === "json") {
1240
+ output(segment, "json");
1241
+ } else {
1242
+ success("Segment updated successfully!");
1243
+ output({
1244
+ ID: segment.id,
1245
+ Name: segment.name
1246
+ }, "table");
1247
+ }
1248
+ } catch (err) {
1249
+ error(err instanceof Error ? err.message : "Failed to update segment");
1250
+ process.exit(1);
1251
+ }
1252
+ });
1253
+ cmd.command("delete <id>").description("Delete a segment").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
1254
+ try {
1255
+ const config = loadConfig();
1256
+ const client = new Emailr7({
1257
+ apiKey: config.apiKey,
1258
+ baseUrl: config.baseUrl
1259
+ });
1260
+ const result = await client.segments.delete(id);
1261
+ if (options.format === "json") {
1262
+ output(result, "json");
1263
+ } else {
1264
+ success("Segment deleted successfully!");
1265
+ }
1266
+ } catch (err) {
1267
+ error(err instanceof Error ? err.message : "Failed to delete segment");
1268
+ process.exit(1);
1269
+ }
1270
+ });
1271
+ cmd.command("count <id>").description("Get the number of contacts in a segment").option("--format <format>", "Output format (json|table)", "table").action(async (id, options) => {
1272
+ try {
1273
+ const config = loadConfig();
1274
+ const client = new Emailr7({
1275
+ apiKey: config.apiKey,
1276
+ baseUrl: config.baseUrl
1277
+ });
1278
+ const result = await client.segments.getContactsCount(id);
1279
+ if (options.format === "json") {
1280
+ output(result, "json");
1281
+ } else {
1282
+ console.log(`Segment contains ${result.count} contacts.`);
1283
+ }
1284
+ } catch (err) {
1285
+ error(err instanceof Error ? err.message : "Failed to get segment count");
1286
+ process.exit(1);
1287
+ }
1288
+ });
1289
+ return cmd;
1290
+ }
1291
+
1292
+ // src/index.ts
1293
+ var program = new Command9();
1294
+ program.name("emailr").description("Emailr CLI - Send emails and manage your email infrastructure").version("1.0.0");
1295
+ program.addCommand(createSendCommand());
1296
+ program.addCommand(createContactsCommand());
1297
+ program.addCommand(createTemplatesCommand());
1298
+ program.addCommand(createDomainsCommand());
1299
+ program.addCommand(createBroadcastsCommand());
1300
+ program.addCommand(createWebhooksCommand());
1301
+ program.addCommand(createSegmentsCommand());
1302
+ program.addCommand(createConfigCommand());
1303
+ program.parse();