emailr-cli 1.12.1 → 1.13.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 +142 -100
- package/package.json +11 -12
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {Command}from'commander';import {Emailr}from'emailr';import
|
|
2
|
+
import {Command}from'commander';import {Emailr}from'emailr';import O,{readFileSync}from'fs';import z from'os';import j from'path';import pe from'cli-table3';import T from'chalk';import at from'http';import {URL}from'url';import vt from'crypto';import {spawn,execSync,exec}from'child_process';var We=[j.join(z.homedir(),".emailrrc"),j.join(z.homedir(),".config","emailr","config.json")];function d(){if(process.env.EMAILR_API_KEY)return {apiKey:process.env.EMAILR_API_KEY,baseUrl:process.env.EMAILR_BASE_URL,format:process.env.EMAILR_FORMAT||"table"};for(let r of We)if(O.existsSync(r))try{let a=O.readFileSync(r,"utf-8"),e=JSON.parse(a);if(e.apiKey)return {apiKey:e.apiKey,baseUrl:e.baseUrl,format:e.format||"table"}}catch{}throw new Error(`No API key configured.
|
|
3
3
|
|
|
4
4
|
Set the EMAILR_API_KEY environment variable:
|
|
5
5
|
export EMAILR_API_KEY=your-api-key
|
|
@@ -7,8 +7,8 @@ Set the EMAILR_API_KEY environment variable:
|
|
|
7
7
|
Or create a config file at ~/.emailrrc:
|
|
8
8
|
{ "apiKey": "your-api-key" }
|
|
9
9
|
|
|
10
|
-
Or run: emailr config set api-key <your-api-key>`)}function
|
|
11
|
-
`);}function
|
|
10
|
+
Or run: emailr config set api-key <your-api-key>`)}function C(){let r=j.join(z.homedir(),".config","emailr");return j.join(r,"config.json")}function G(r){let a=C(),e=j.dirname(a);O.existsSync(e)||O.mkdirSync(e,{recursive:true});let i={};if(O.existsSync(a))try{i=JSON.parse(O.readFileSync(a,"utf-8"));}catch{}let s={...i,...r};O.writeFileSync(a,JSON.stringify(s,null,2)+`
|
|
11
|
+
`);}function de(r){try{return d()[r]?.toString()}catch{return}}function m(r,a="table"){a==="json"?console.log(JSON.stringify(r,null,2)):Xe(r);}function Xe(r){Array.isArray(r)?Ve(r):typeof r=="object"&&r!==null?ze(r):console.log(r);}function Ve(r){if(r.length===0){console.log(T.gray("No results"));return}let a=r[0];if(typeof a!="object"||a===null){r.forEach(s=>console.log(s));return}let e=Object.keys(a),i=new pe({head:e.map(s=>T.cyan(s)),style:{head:[],border:[]}});for(let s of r){let t=e.map(o=>{let n=s[o];return ue(n)});i.push(t);}console.log(i.toString());}function ze(r){let a=new pe({style:{head:[],border:[]}});for(let[e,i]of Object.entries(r))a.push([T.cyan(e),ue(i)]);console.log(a.toString());}function ue(r){return r==null?T.gray("-"):typeof r=="boolean"?r?T.green("\u2713"):T.red("\u2717"):typeof r=="object"?JSON.stringify(r):String(r)}function f(r){console.log(T.green("\u2713"),r);}function l(r){console.error(T.red("\u2717"),r);}function N(r){console.warn(T.yellow("\u26A0"),r);}function b(r){console.log(T.blue("\u2139"),r);}function be(){return new Command("send").description(`Send an email
|
|
12
12
|
|
|
13
13
|
USAGE
|
|
14
14
|
emailr send --to <email_address> [options]
|
|
@@ -156,7 +156,7 @@ EXAMPLES
|
|
|
156
156
|
SEE ALSO
|
|
157
157
|
emailr templates Manage email templates
|
|
158
158
|
emailr contacts Manage contacts
|
|
159
|
-
emailr broadcasts Send bulk emails to segments`).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 (inline)").option("--text <text>","Plain text content (inline)").option("--html-file <path>","Read HTML content from file").option("--text-file <path>","Read plain text content from file").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
|
|
159
|
+
emailr broadcasts Send bulk emails to segments`).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 (inline)").option("--text <text>","Plain text content (inline)").option("--html-file <path>","Read HTML content from file").option("--text-file <path>","Read plain text content from file").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 a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s=a.to.split(",").map(n=>n.trim()),t={to:s.length===1?s[0]:s};if(a.from&&(t.from=a.from),a.subject&&(t.subject=a.subject),a.htmlFile)try{t.html=readFileSync(a.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${a.htmlFile}`),process.exit(1);}else a.html&&(t.html=a.html);if(a.textFile)try{t.text=readFileSync(a.textFile,"utf-8");}catch{l(`Failed to read text file: ${a.textFile}`),process.exit(1);}else a.text&&(t.text=a.text);if(a.template&&(t.template_id=a.template),a.templateData)try{t.template_data=JSON.parse(a.templateData);}catch{l("Invalid JSON for --template-data"),process.exit(1);}if(a.cc){let n=a.cc.split(",").map(c=>c.trim());t.cc=n.length===1?n[0]:n;}if(a.bcc){let n=a.bcc.split(",").map(c=>c.trim());t.bcc=n.length===1?n[0]:n;}a.replyTo&&(t.reply_to_email=a.replyTo),a.schedule&&(t.scheduled_at=a.schedule);let o=await i.emails.send(t);a.format==="json"?m(o,"json"):(f("Email sent successfully!"),m({"Message ID":o.message_id,Recipients:o.recipients,Status:o.status,...o.scheduled_at&&{"Scheduled At":o.scheduled_at}},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to send email"),process.exit(1);}})}function he(){let r=new Command("contacts").description(`Manage contacts
|
|
160
160
|
|
|
161
161
|
USAGE
|
|
162
162
|
emailr contacts <subcommand> [options]
|
|
@@ -286,8 +286,8 @@ EXAMPLES
|
|
|
286
286
|
emailr contacts list --format json
|
|
287
287
|
|
|
288
288
|
# Combine filters with pagination
|
|
289
|
-
emailr contacts list --subscribed --limit 100 --offset 0`).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("--search <query>","Search by email, first name, or last name").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async
|
|
290
|
-
Total: ${
|
|
289
|
+
emailr contacts list --subscribed --limit 100 --offset 0`).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("--search <query>","Search by email, first name, or last name").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={limit:parseInt(a.limit,10),offset:parseInt(a.offset,10)};if(a.subscribed&&(s.subscribed=!0),a.unsubscribed&&(s.subscribed=!1),a.search&&(s.search=a.search),a.tags){let o=a.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean);o.length>0&&(s.tags=o.join(","));}let t=await i.contacts.list(s);if(a.format==="json")m(t,"json");else {let o=t.contacts.map(n=>({ID:n.id,Email:n.email,Name:[n.first_name,n.last_name].filter(Boolean).join(" ")||"-",Subscribed:n.subscribed,Tags:n.tags?.join(", ")||"-",Created:n.created_at}));m(o,"table"),console.log(`
|
|
290
|
+
Total: ${t.total}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list contacts"),process.exit(1);}}),r.command("get <contact_id>").description(`Get a contact by ID
|
|
291
291
|
|
|
292
292
|
USAGE
|
|
293
293
|
emailr contacts get <contact_id> [options]
|
|
@@ -314,7 +314,7 @@ EXAMPLES
|
|
|
314
314
|
emailr contacts get con_abc123 --format json
|
|
315
315
|
|
|
316
316
|
# Pipe JSON to jq for processing
|
|
317
|
-
emailr contacts get con_abc123 --format json | jq '.metadata'`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
317
|
+
emailr contacts get con_abc123 --format json | jq '.metadata'`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).contacts.get(a);m(t,e.format);}catch(i){l(i instanceof Error?i.message:"Failed to get contact"),process.exit(1);}}),r.command("create").description(`Create a new contact
|
|
318
318
|
|
|
319
319
|
USAGE
|
|
320
320
|
emailr contacts create --email <email_address> [options]
|
|
@@ -357,7 +357,7 @@ EXAMPLES
|
|
|
357
357
|
emailr contacts create --email "user@example.com" --subscribed false
|
|
358
358
|
|
|
359
359
|
# Get JSON output for scripting
|
|
360
|
-
emailr contacts create --email "user@example.com" --format json`).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("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async
|
|
360
|
+
emailr contacts create --email "user@example.com" --format json`).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("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={email:a.email};if(a.firstName&&(s.first_name=a.firstName),a.lastName&&(s.last_name=a.lastName),a.subscribed!==void 0&&(s.subscribed=a.subscribed),a.metadata)try{s.metadata=JSON.parse(a.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}a.tags&&(s.tags=a.tags.split(",").map(o=>o.trim().toLowerCase()).filter(Boolean));let t=await i.contacts.create(s);a.format==="json"?m(t,"json"):(f(`Contact created: ${t.id}`),m(t,"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create contact"),process.exit(1);}}),r.command("update <contact_id>").description(`Update a contact
|
|
361
361
|
|
|
362
362
|
USAGE
|
|
363
363
|
emailr contacts update <contact_id> [options]
|
|
@@ -406,7 +406,7 @@ EXAMPLES
|
|
|
406
406
|
emailr contacts update con_abc123 --metadata '{"plan": "enterprise", "upgraded": true}'
|
|
407
407
|
|
|
408
408
|
# Get JSON output
|
|
409
|
-
emailr contacts update con_abc123 --first-name "Jane" --format json`).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("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(
|
|
409
|
+
emailr contacts update con_abc123 --first-name "Jane" --format json`).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("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t={};if(e.email&&(t.email=e.email),e.firstName&&(t.first_name=e.firstName),e.lastName&&(t.last_name=e.lastName),e.subscribed&&(t.subscribed=!0),e.unsubscribed&&(t.subscribed=!1),e.metadata)try{t.metadata=JSON.parse(e.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}e.tags&&(t.tags=e.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let o=await s.contacts.update(a,t);e.format==="json"?m(o,"json"):(f(`Contact updated: ${o.id}`),m(o,"table"));}catch(i){l(i instanceof Error?i.message:"Failed to update contact"),process.exit(1);}}),r.command("delete <contact_id>").description(`Delete a contact
|
|
410
410
|
|
|
411
411
|
USAGE
|
|
412
412
|
emailr contacts delete <contact_id>
|
|
@@ -423,7 +423,7 @@ EXAMPLES
|
|
|
423
423
|
emailr contacts delete con_abc123
|
|
424
424
|
|
|
425
425
|
WARNING
|
|
426
|
-
This action is permanent and cannot be undone.`).action(async
|
|
426
|
+
This action is permanent and cannot be undone.`).action(async a=>{try{let e=d();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).contacts.delete(a),f(`Contact deleted: ${a}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete contact"),process.exit(1);}}),r}function ye(){return j.join(z.homedir(),".config","emailr","templates")}function tt(){let r=ye();O.existsSync(r)||O.mkdirSync(r,{recursive:true});}function Y(r){return j.join(ye(),`${r}.html`)}function Z(r,a){tt();let e=Y(r);O.writeFileSync(e,a,"utf-8");}function we(r){let a=Y(r);return O.existsSync(a)?O.readFileSync(a,"utf-8"):null}function Se(r){let a=Y(r);return O.existsSync(a)}var x=null,A=null,H=false;function xe(r){return r.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function it(r){return r===null||r.trim()===""}function ot(r){return `<!DOCTYPE html>
|
|
427
427
|
<html lang="en">
|
|
428
428
|
<head>
|
|
429
429
|
<meta charset="UTF-8">
|
|
@@ -489,14 +489,14 @@ WARNING
|
|
|
489
489
|
<div class="icon">\u{1F4ED}</div>
|
|
490
490
|
<h1>No Content Available</h1>
|
|
491
491
|
<p>This template exists but has no HTML content to display.</p>
|
|
492
|
-
<div class="template-id">${
|
|
492
|
+
<div class="template-id">${xe(r)}</div>
|
|
493
493
|
<div class="hint">
|
|
494
494
|
<p><strong>To add content:</strong></p>
|
|
495
495
|
<p>Update the template using the CLI with the <code>--html</code> option or provide HTML content when creating the template.</p>
|
|
496
496
|
</div>
|
|
497
497
|
</div>
|
|
498
498
|
</body>
|
|
499
|
-
</html>`}function
|
|
499
|
+
</html>`}function ve(r){return `<!DOCTYPE html>
|
|
500
500
|
<html lang="en">
|
|
501
501
|
<head>
|
|
502
502
|
<meta charset="UTF-8">
|
|
@@ -548,11 +548,11 @@ WARNING
|
|
|
548
548
|
<div class="icon">404</div>
|
|
549
549
|
<h1>Template Not Found</h1>
|
|
550
550
|
<p>The requested template could not be found in local storage.</p>
|
|
551
|
-
<div class="template-id">${
|
|
551
|
+
<div class="template-id">${xe(r)}</div>
|
|
552
552
|
<p style="margin-top: 1rem;">Try creating or retrieving the template first using the CLI.</p>
|
|
553
553
|
</div>
|
|
554
554
|
</body>
|
|
555
|
-
</html>`}function rt(r){let
|
|
555
|
+
</html>`}function rt(r){let a=r.match(/^\/preview\/([^/]+)$/);return a?a[1]:null}function nt(){return (r,a)=>{if(r.method!=="GET"){a.writeHead(405,{"Content-Type":"text/plain"}),a.end("Method Not Allowed");return}let e=r.url||"/",i=rt(e);if(!i){a.writeHead(404,{"Content-Type":"text/plain"}),a.end("Not Found");return}if(!Se(i)){a.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),a.end(ve(i));return}let s=we(i);if(s===null){a.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),a.end(ve(i));return}if(it(s)){a.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),a.end(ot(i));return}a.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),a.end(s);}}var Te={async start(){return H&&A!==null?A:new Promise((r,a)=>{x=at.createServer(nt()),x.listen(0,"127.0.0.1",()=>{let e=x.address();e&&typeof e=="object"?(A=e.port,H=true,x.unref(),r(A)):a(new Error("Failed to get server address"));}),x.on("error",e=>{H=false,A=null,x=null,a(new Error(`Failed to start preview server: ${e.message}`));});})},getPort(){return A},isRunning(){return H},async stop(){if(x)return new Promise(r=>{x.close(()=>{x=null,A=null,H=false,r();});})}};function Oe(){return Te}async function Q(r){try{return `http://127.0.0.1:${await Te.start()}/preview/${r}`}catch{return null}}function Ee(){x&&x.ref();}var R=null,ee=null,W=null,k=[],_e=`
|
|
556
556
|
<script>
|
|
557
557
|
(function() {
|
|
558
558
|
const evtSource = new EventSource('/__live-reload');
|
|
@@ -568,9 +568,9 @@ WARNING
|
|
|
568
568
|
</script>
|
|
569
569
|
`;function mt(r){return r.includes("</body>")?r.replace("</body>",`${_e}</body>`):r+_e}function ct(){k.forEach(r=>{try{r.write(`data: reload
|
|
570
570
|
|
|
571
|
-
`);}catch{}});}async function
|
|
571
|
+
`);}catch{}});}async function te(r,a){let e=j.resolve(r);return new Promise((i,s)=>{R=at.createServer((t,o)=>{if(t.url==="/__live-reload"){o.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),o.write(`data: connected
|
|
572
572
|
|
|
573
|
-
`),k.push(o),
|
|
573
|
+
`),k.push(o),t.on("close",()=>{k=k.filter(n=>n!==o);});return}if(t.method==="GET"){try{let n=O.readFileSync(e,"utf-8"),c=mt(n);o.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),o.end(c);}catch(n){o.writeHead(500,{"Content-Type":"text/plain"}),o.end(`Error reading file: ${n instanceof Error?n.message:String(n)}`);}return}o.writeHead(405,{"Content-Type":"text/plain"}),o.end("Method Not Allowed");}),R.listen(0,"127.0.0.1",()=>{let t=R.address();if(t&&typeof t=="object"){ee=t.port;let o=null;W=O.watch(e,n=>{n==="change"&&(o&&clearTimeout(o),o=setTimeout(()=>{ct(),a?.();},100));}),i(ee);}else s(new Error("Failed to get server address"));}),R.on("error",t=>{s(new Error(`Failed to start server: ${t.message}`));});})}async function ae(){if(W&&(W.close(),W=null),k.forEach(r=>{try{r.end();}catch{}}),k=[],R)return new Promise(r=>{R.close(()=>{R=null,ee=null,r();});})}async function Ie(r){try{let a=r.html_content??"";Z(r.id,a);}catch(a){return N(`Could not save template for preview: ${a instanceof Error?a.message:String(a)}`),null}try{let a=await Q(r.id);return a===null?(N("Could not start preview server"),null):a}catch(a){return N(`Could not generate preview URL: ${a instanceof Error?a.message:String(a)}`),null}}function Ce(){let r=new Command("templates").description(`Manage email templates
|
|
574
574
|
|
|
575
575
|
USAGE
|
|
576
576
|
emailr templates <subcommand> [options]
|
|
@@ -671,8 +671,8 @@ EXAMPLES
|
|
|
671
671
|
emailr templates list --page 2 --limit 10
|
|
672
672
|
|
|
673
673
|
# Get JSON output for scripting
|
|
674
|
-
emailr templates list --format json`).option("--limit <count>","Number of templates to return","20").option("--page <page_number>","Page number for pagination","1").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format: json | table","table").action(async
|
|
675
|
-
Total: ${
|
|
674
|
+
emailr templates list --format json`).option("--limit <count>","Number of templates to return","20").option("--page <page_number>","Page number for pagination","1").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format: json | table","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={limit:parseInt(a.limit,10),page:parseInt(a.page,10)};if(a.tags){let o=a.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean);o.length>0&&(s.tags=o.join(","));}let t=await i.templates.list(s);if(a.format==="json")m(t,"json");else {let o=t.map(n=>({ID:n.id,Name:n.name,Subject:n.subject,Variables:n.variables?.join(", ")||"-",Tags:n.tags?.join(", ")||"-",Created:n.created_at}));m(o,"table"),console.log(`
|
|
675
|
+
Total: ${t.length}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list templates"),process.exit(1);}}),r.command("get <template_id>").description(`Get a template by ID
|
|
676
676
|
|
|
677
677
|
USAGE
|
|
678
678
|
emailr templates get <template_id> [options]
|
|
@@ -700,7 +700,7 @@ EXAMPLES
|
|
|
700
700
|
emailr templates get tpl_abc123 --format json
|
|
701
701
|
|
|
702
702
|
# Pipe JSON to jq for processing
|
|
703
|
-
emailr templates get tpl_abc123 --format json | jq '.html_content'`).option("--format <format>","Output format: json | table","table").action(async(
|
|
703
|
+
emailr templates get tpl_abc123 --format json | jq '.html_content'`).option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).templates.get(a),o=await Ie({id:t.id,html_content:t.html_content??void 0}),n=t.preview_html?`${i.baseUrl}/preview/${t.id}`:null;if(e.format==="json")m({...t,preview_url:n??o},"json");else {let c={ID:t.id,Name:t.name,Subject:t.subject,Variables:t.variables?.join(", ")||"-",Created:t.created_at};n?c["Preview URL"]=n:o&&(c["Local Preview"]=o),m(c,"table");}}catch(i){l(i instanceof Error?i.message:"Failed to get template"),process.exit(1);}}),r.command("fetch <template_id>").description(`Download template HTML to file or stdout
|
|
704
704
|
|
|
705
705
|
USAGE
|
|
706
706
|
emailr templates fetch <template_id> [options]
|
|
@@ -734,8 +734,8 @@ AGENTIC WORKFLOW
|
|
|
734
734
|
This is step 1 of the agentic workflow:
|
|
735
735
|
1. Fetch: emailr templates fetch <id> --output template.html
|
|
736
736
|
2. Edit locally or with AI assistance
|
|
737
|
-
3. Push preview: emailr templates push-preview <id> --html-file template.html`).option("--output <file_path>","Write HTML to file (default: stdout)").option("--preview","Fetch preview HTML instead of published HTML").action(async(
|
|
738
|
-
Run 'emailr templates push-preview <id> --html-file <path>' to create a preview.`)):l("Template has no HTML content"),process.exit(1)),e.output){let o=
|
|
737
|
+
3. Push preview: emailr templates push-preview <id> --html-file template.html`).option("--output <file_path>","Write HTML to file (default: stdout)").option("--preview","Fetch preview HTML instead of published HTML").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).templates.fetch(a,{preview:e.preview??!1});if(t||(e.preview?(l("No preview HTML exists for this template"),console.log(`
|
|
738
|
+
Run 'emailr templates push-preview <id> --html-file <path>' to create a preview.`)):l("Template has no HTML content"),process.exit(1)),e.output){let o=j.resolve(e.output);O.writeFileSync(o,t,"utf-8"),f(`HTML saved to: ${o}`);}else console.log(t);}catch(i){l(i instanceof Error?i.message:"Failed to fetch template"),process.exit(1);}}),r.command("push-preview <template_id>").description(`Upload HTML to template's preview for sharing with AI agents
|
|
739
739
|
|
|
740
740
|
USAGE
|
|
741
741
|
emailr templates push-preview <template_id> --html-file <file_path>
|
|
@@ -778,8 +778,8 @@ AGENTIC WORKFLOW
|
|
|
778
778
|
2. Edit locally or with AI assistance
|
|
779
779
|
3. Push preview: emailr templates push-preview <id> --html-file template.html
|
|
780
780
|
4. Share the preview URL with your AI agent for feedback
|
|
781
|
-
5. Repeat steps 2-4 until satisfied`).option("--html-file <file_path>","Read HTML content from file").option("--html <html_content>","Inline HTML content").option("--format <format>","Output format: json | table","table").action(async(
|
|
782
|
-
Usage:`),console.log(" emailr templates push-preview <id> --html-file <path>"),console.log(" emailr templates push-preview <id> --html <content>"),process.exit(1)),e.htmlFile&&e.html&&(l("Cannot use both --html-file and --html. Choose one."),process.exit(1));let i=d(),
|
|
781
|
+
5. Repeat steps 2-4 until satisfied`).option("--html-file <file_path>","Read HTML content from file").option("--html <html_content>","Inline HTML content").option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{!e.htmlFile&&!e.html&&(l("Either --html-file or --html is required"),console.log(`
|
|
782
|
+
Usage:`),console.log(" emailr templates push-preview <id> --html-file <path>"),console.log(" emailr templates push-preview <id> --html <content>"),process.exit(1)),e.htmlFile&&e.html&&(l("Cannot use both --html-file and --html. Choose one."),process.exit(1));let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t;if(e.htmlFile){let c=j.resolve(e.htmlFile);try{t=O.readFileSync(c,"utf-8");}catch{l(`Failed to read file: ${c}`),process.exit(1);}}else t=e.html;let o=await s.templates.pushPreview(a,t),n="updated";e.format==="json"?m({template_id:a,preview_url:o.preview_url,status:n},"json"):(f("Preview uploaded successfully"),m({"Template ID":a,"Preview URL":o.preview_url,Status:n},"table"),console.log(`
|
|
783
783
|
Share this URL with your AI agent for feedback.`));}catch(i){l(i instanceof Error?i.message:"Failed to push preview"),process.exit(1);}}),r.command("create").description(`Create a new email template
|
|
784
784
|
|
|
785
785
|
USAGE
|
|
@@ -828,7 +828,7 @@ EXAMPLES
|
|
|
828
828
|
|
|
829
829
|
TIP
|
|
830
830
|
For live preview while building, use "emailr templates draft" first.
|
|
831
|
-
It creates a local file with hot-reload preview, then use this command to save.`).requiredOption("--name <template_name>","Template name").requiredOption("--subject <subject_line>","Email subject line").option("--html <html_content>","Inline HTML content").option("--text <text_content>","Plain text content").option("--html-file <file_path>","Read HTML content from file").option("--text-file <file_path>","Read text content from file").option("--from <email_address>","Default from email address").option("--from-name <name>","Sender display name").option("--reply-to <email_address>","Default reply-to email address").option("--preview-text <text>","Preview text shown in email clients").option("--inbox-id <inbox_id>","Inbox ID for sender identity defaults").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async
|
|
831
|
+
It creates a local file with hot-reload preview, then use this command to save.`).requiredOption("--name <template_name>","Template name").requiredOption("--subject <subject_line>","Email subject line").option("--html <html_content>","Inline HTML content").option("--text <text_content>","Plain text content").option("--html-file <file_path>","Read HTML content from file").option("--text-file <file_path>","Read text content from file").option("--from <email_address>","Default from email address").option("--from-name <name>","Sender display name").option("--reply-to <email_address>","Default reply-to email address").option("--preview-text <text>","Preview text shown in email clients").option("--inbox-id <inbox_id>","Inbox ID for sender identity defaults").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={name:a.name,subject:a.subject};if(a.htmlFile){let n=await import('fs');s.html_content=n.readFileSync(a.htmlFile,"utf-8");}else a.html&&(s.html_content=a.html);if(a.textFile){let n=await import('fs');s.text_content=n.readFileSync(a.textFile,"utf-8");}else a.text&&(s.text_content=a.text);a.from&&(s.from_email=a.from),a.fromName&&(s.from_name=a.fromName),a.replyTo&&(s.reply_to=a.replyTo),a.previewText&&(s.preview_text=a.previewText),a.inboxId&&(s.inbox_id=a.inboxId),a.tags&&(s.tags=a.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let t=await i.templates.create(s),o=await Ie({id:t.id,html_content:t.html_content??void 0});if(a.format==="json")m({...t,preview_url:o},"json");else {f(`Template created: ${t.id}`);let n={ID:t.id,Name:t.name,Subject:t.subject,Variables:t.variables?.join(", ")||"-",Tags:t.tags?.join(", ")||"-"};o&&(n["Preview URL"]=o),m(n,"table"),o&&console.log(`
|
|
832
832
|
Open the Preview URL in your browser to view the rendered template.`);}}catch(e){l(e instanceof Error?e.message:"Failed to create template"),process.exit(1);}}),r.command("update <template_id>").description(`Update an existing email template
|
|
833
833
|
|
|
834
834
|
USAGE
|
|
@@ -884,7 +884,7 @@ AGENTIC WORKFLOW
|
|
|
884
884
|
2. Edit locally or with AI assistance
|
|
885
885
|
3. Push preview: emailr templates push-preview <id> --html-file template.html
|
|
886
886
|
4. Share URL with AI agent, iterate on feedback
|
|
887
|
-
5. Publish: emailr templates update <id> --html-file template.html`).option("--name <template_name>","New template name").option("--subject <subject_line>","New email subject line").option("--html <html_content>","New inline HTML content").option("--text <text_content>","New plain text content").option("--html-file <file_path>","Read new HTML content from file").option("--text-file <file_path>","Read new text content from file").option("--from <email_address>","New default from email address").option("--from-name <name>","New sender display name").option("--reply-to <email_address>","New default reply-to email address").option("--preview-text <text>","New preview text").option("--inbox-id <inbox_id>",'Inbox ID for sender identity (use "none" to clear)').option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async(
|
|
887
|
+
5. Publish: emailr templates update <id> --html-file template.html`).option("--name <template_name>","New template name").option("--subject <subject_line>","New email subject line").option("--html <html_content>","New inline HTML content").option("--text <text_content>","New plain text content").option("--html-file <file_path>","Read new HTML content from file").option("--text-file <file_path>","Read new text content from file").option("--from <email_address>","New default from email address").option("--from-name <name>","New sender display name").option("--reply-to <email_address>","New default reply-to email address").option("--preview-text <text>","New preview text").option("--inbox-id <inbox_id>",'Inbox ID for sender identity (use "none" to clear)').option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t={};if(e.name&&(t.name=e.name),e.subject&&(t.subject=e.subject),e.htmlFile){let n=await import('fs');t.html_content=n.readFileSync(e.htmlFile,"utf-8");}else e.html&&(t.html_content=e.html);if(e.textFile){let n=await import('fs');t.text_content=n.readFileSync(e.textFile,"utf-8");}else e.text&&(t.text_content=e.text);e.from&&(t.from_email=e.from),e.fromName&&(t.from_name=e.fromName),e.replyTo&&(t.reply_to=e.replyTo),e.previewText&&(t.preview_text=e.previewText),e.inboxId&&(t.inbox_id=e.inboxId==="none"?null:e.inboxId),e.tags&&(t.tags=e.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let o=await s.templates.update(a,t);if(e.format==="json")m(o,"json");else {f(`Template updated: ${o.id}`);let n={ID:o.id,Name:o.name,Subject:o.subject,Variables:o.variables?.join(", ")||"-",Tags:o.tags?.join(", ")||"-",Updated:o.updated_at};m(n,"table");}}catch(i){l(i instanceof Error?i.message:"Failed to update template"),process.exit(1);}}),r.command("delete <template_id>").description(`Delete a template
|
|
888
888
|
|
|
889
889
|
USAGE
|
|
890
890
|
emailr templates delete <template_id>
|
|
@@ -901,7 +901,7 @@ EXAMPLES
|
|
|
901
901
|
emailr templates delete tpl_abc123
|
|
902
902
|
|
|
903
903
|
WARNING
|
|
904
|
-
This action is permanent and cannot be undone.`).action(async
|
|
904
|
+
This action is permanent and cannot be undone.`).action(async a=>{try{let e=d();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).templates.delete(a),f(`Template deleted: ${a}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete template"),process.exit(1);}}),r.command("preview <template_id>").description(`Preview a template in the browser
|
|
905
905
|
|
|
906
906
|
USAGE
|
|
907
907
|
emailr templates preview <template_id> [options]
|
|
@@ -930,17 +930,17 @@ EXAMPLES
|
|
|
930
930
|
|
|
931
931
|
SEE ALSO
|
|
932
932
|
emailr templates edit Live editing with hot-reload
|
|
933
|
-
emailr templates stop-preview Stop background preview server`).option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(
|
|
934
|
-
Template: ${
|
|
933
|
+
emailr templates stop-preview Stop background preview server`).option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl});console.log(`Fetching template ${a}...`);let t=await s.templates.get(a),o=t.html_content??"";if(Z(t.id,o),e.foreground){let w=await Q(t.id);if(w||(l("Failed to start preview server"),process.exit(1)),Ee(),console.log(`
|
|
934
|
+
Template: ${t.name}`),console.log(`Preview URL: ${w}`),e.open!==!1)try{await(await import('open')).default(w),console.log(`
|
|
935
935
|
Browser opened.`);}catch{console.log(`
|
|
936
936
|
Could not open browser automatically. Open the URL above manually.`);}process.on("SIGINT",async()=>{console.log(`
|
|
937
937
|
|
|
938
|
-
Stopping preview server...`),await
|
|
938
|
+
Stopping preview server...`),await Oe().stop(),console.log("Done."),process.exit(0);});return}let n=j.join(process.cwd(),`.template-preview-${t.id}.html`);O.writeFileSync(n,o,"utf-8");let{spawn:c}=await import('child_process'),u=await import('os'),g=j.join(u.tmpdir(),"emailr-preview.pid");try{let w=O.readFileSync(g,"utf-8").trim();process.kill(parseInt(w,10),"SIGTERM");}catch{}let p=`
|
|
939
939
|
const http = require('http');
|
|
940
940
|
const fs = require('fs');
|
|
941
941
|
const path = require('path');
|
|
942
942
|
const os = require('os');
|
|
943
|
-
const filePath = ${JSON.stringify(
|
|
943
|
+
const filePath = ${JSON.stringify(n)};
|
|
944
944
|
const pidFile = path.join(os.tmpdir(), 'emailr-preview.pid');
|
|
945
945
|
const portFile = path.join(os.tmpdir(), 'emailr-preview.port');
|
|
946
946
|
const server = http.createServer((req, res) => {
|
|
@@ -960,8 +960,8 @@ Stopping preview server...`),await Te().stop(),console.log("Done."),process.exit
|
|
|
960
960
|
console.log('PORT:' + port);
|
|
961
961
|
});
|
|
962
962
|
process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); fs.unlinkSync(filePath); } catch {} process.exit(0); });
|
|
963
|
-
`,h=c("node",["-e",
|
|
964
|
-
Template: ${
|
|
963
|
+
`,h=c("node",["-e",p],{detached:!0,stdio:["ignore","pipe","ignore"]}),y="";await new Promise(w=>{h.stdout?.on("data",I=>{let B=I.toString().match(/PORT:(\d+)/);B&&(y=B[1],w());}),setTimeout(w,3e3);}),h.unref();let _=`http://127.0.0.1:${y}/`;if(console.log(`
|
|
964
|
+
Template: ${t.name}`),console.log(`Preview URL: ${_}`),console.log(`
|
|
965
965
|
To stop: emailr templates stop-preview`),e.open!==!1)try{await(await import('open')).default(_);}catch{}process.exit(0);}catch(i){l(i instanceof Error?i.message:"Failed to preview template"),process.exit(1);}}),r.command("edit <template_id>").description(`Start a live editing session for a template with hot-reload
|
|
966
966
|
|
|
967
967
|
USAGE
|
|
@@ -1002,17 +1002,17 @@ EXAMPLES
|
|
|
1002
1002
|
SEE ALSO
|
|
1003
1003
|
emailr templates draft Draft a new template with live preview
|
|
1004
1004
|
emailr templates update Save changes to the template
|
|
1005
|
-
emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for editing","./template.html").option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(
|
|
1006
|
-
Template: ${o.name}`),console.log(`Template ID: ${o.id}`),console.log(`Live Preview: ${
|
|
1007
|
-
Watching for changes... Edit the file and see live updates.`),console.log(`When done, run: emailr templates update ${
|
|
1005
|
+
emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for editing","./template.html").option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t=j.resolve(e.file);console.log(`Fetching template ${a}...`);let o=await s.templates.get(a),n=o.html_content??"";if(O.writeFileSync(t,n,"utf-8"),console.log(`Template saved to: ${t}`),e.foreground){let I=`http://127.0.0.1:${await te(t,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
|
|
1006
|
+
Template: ${o.name}`),console.log(`Template ID: ${o.id}`),console.log(`Live Preview: ${I}`),console.log(`File: ${t}`),console.log(`
|
|
1007
|
+
Watching for changes... Edit the file and see live updates.`),console.log(`When done, run: emailr templates update ${a} --html-file ${e.file}`),e.open!==!1)try{await(await import('open')).default(I);}catch{}process.on("SIGINT",async()=>{console.log(`
|
|
1008
1008
|
|
|
1009
|
-
Stopping live preview server...`),await
|
|
1010
|
-
To save changes: emailr templates update ${
|
|
1009
|
+
Stopping live preview server...`),await ae(),console.log("Done."),console.log(`
|
|
1010
|
+
To save changes: emailr templates update ${a} --html-file ${e.file}`),process.exit(0);});return}let{spawn:c}=await import('child_process'),u=await import('os'),g=j.join(u.tmpdir(),"emailr-preview.pid");try{let w=O.readFileSync(g,"utf-8").trim();process.kill(parseInt(w,10),"SIGTERM");}catch{}let p=`
|
|
1011
1011
|
const http = require('http');
|
|
1012
1012
|
const fs = require('fs');
|
|
1013
1013
|
const path = require('path');
|
|
1014
1014
|
const os = require('os');
|
|
1015
|
-
const filePath = ${JSON.stringify(
|
|
1015
|
+
const filePath = ${JSON.stringify(t)};
|
|
1016
1016
|
const pidFile = path.join(os.tmpdir(), 'emailr-preview.pid');
|
|
1017
1017
|
const portFile = path.join(os.tmpdir(), 'emailr-preview.port');
|
|
1018
1018
|
let clients = [];
|
|
@@ -1047,10 +1047,10 @@ To save changes: emailr templates update ${t} --html-file ${e.file}`),process.ex
|
|
|
1047
1047
|
});
|
|
1048
1048
|
});
|
|
1049
1049
|
process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); } catch {} process.exit(0); });
|
|
1050
|
-
`,h=c("node",["-e",
|
|
1051
|
-
Template: ${o.name}`),console.log(`Template ID: ${o.id}`),console.log(`Live Preview: ${_}`),console.log(`File: ${
|
|
1050
|
+
`,h=c("node",["-e",p],{detached:!0,stdio:["ignore","pipe","ignore"]}),y="";await new Promise(w=>{h.stdout?.on("data",I=>{let ce=I.toString().match(/PORT:(\d+)/);ce&&(y=ce[1],w());}),setTimeout(w,3e3);}),h.unref();let _=`http://127.0.0.1:${y}/`;if(console.log(`
|
|
1051
|
+
Template: ${o.name}`),console.log(`Template ID: ${o.id}`),console.log(`Live Preview: ${_}`),console.log(`File: ${t}`),console.log(`
|
|
1052
1052
|
Edit the file and see live updates in the browser.`),console.log(`
|
|
1053
|
-
When done:`),console.log(` emailr templates update ${
|
|
1053
|
+
When done:`),console.log(` emailr templates update ${a} --html-file ${e.file}`),console.log(" emailr templates stop-preview"),e.open!==!1)try{await(await import('open')).default(_);}catch{}process.exit(0);}catch(i){l(i instanceof Error?i.message:"Failed to edit template"),process.exit(1);}}),r.command("draft").description(`Start a live drafting session for a new template with hot-reload
|
|
1054
1054
|
|
|
1055
1055
|
USAGE
|
|
1056
1056
|
emailr templates draft [options]
|
|
@@ -1092,7 +1092,7 @@ EXAMPLES
|
|
|
1092
1092
|
SEE ALSO
|
|
1093
1093
|
emailr templates edit Edit an existing template with live preview
|
|
1094
1094
|
emailr templates create Create the template from your draft
|
|
1095
|
-
emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for drafting","./new-template.html").option("--no-open","Do not automatically open browser").option("--blank","Start with a blank file instead of starter template").option("--foreground","Keep process running in foreground (blocks terminal)").action(async
|
|
1095
|
+
emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for drafting","./new-template.html").option("--no-open","Do not automatically open browser").option("--blank","Start with a blank file instead of starter template").option("--foreground","Keep process running in foreground (blocks terminal)").action(async a=>{try{let e=j.resolve(a.file),i;if(a.blank?i="":i=`<!DOCTYPE html>
|
|
1096
1096
|
<html>
|
|
1097
1097
|
<head>
|
|
1098
1098
|
<meta charset="UTF-8">
|
|
@@ -1127,13 +1127,13 @@ SEE ALSO
|
|
|
1127
1127
|
<p><a href="{{unsubscribe_link}}">Unsubscribe</a></p>
|
|
1128
1128
|
</div>
|
|
1129
1129
|
</body>
|
|
1130
|
-
</html>`,
|
|
1130
|
+
</html>`,O.writeFileSync(e,i,"utf-8"),console.log(`Template draft created: ${e}`),a.foreground){let h=`http://127.0.0.1:${await te(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
|
|
1131
1131
|
Live Preview: ${h}`),console.log(`File: ${e}`),console.log(`
|
|
1132
1132
|
Watching for changes... Edit the file and see live updates.`),console.log(`
|
|
1133
|
-
When done, create the template:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${
|
|
1133
|
+
When done, create the template:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${a.file}`),a.open!==!1)try{await(await import('open')).default(h);}catch{}process.on("SIGINT",async()=>{console.log(`
|
|
1134
1134
|
|
|
1135
|
-
Stopping live preview server...`),await
|
|
1136
|
-
To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${
|
|
1135
|
+
Stopping live preview server...`),await ae(),console.log("Done."),console.log(`
|
|
1136
|
+
To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${a.file}`),process.exit(0);});return}let{spawn:s}=await import('child_process'),t=await import('os'),o=j.join(t.tmpdir(),"emailr-preview.pid");try{let p=O.readFileSync(o,"utf-8").trim();process.kill(parseInt(p,10),"SIGTERM");}catch{}let n=`
|
|
1137
1137
|
const http = require('http');
|
|
1138
1138
|
const fs = require('fs');
|
|
1139
1139
|
const path = require('path');
|
|
@@ -1173,10 +1173,10 @@ To create template: emailr templates create --name "Template Name" --subject "Em
|
|
|
1173
1173
|
});
|
|
1174
1174
|
});
|
|
1175
1175
|
process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); } catch {} process.exit(0); });
|
|
1176
|
-
`,c=
|
|
1176
|
+
`,c=s("node",["-e",n],{detached:!0,stdio:["ignore","pipe","ignore"]}),u="";await new Promise(p=>{c.stdout?.on("data",h=>{let y=h.toString().match(/PORT:(\d+)/);y&&(u=y[1],p());}),setTimeout(p,3e3);}),c.unref();let g=`http://127.0.0.1:${u}/`;if(console.log(`
|
|
1177
1177
|
Live Preview: ${g}`),console.log(`File: ${e}`),console.log(`
|
|
1178
1178
|
Edit the file and see live updates in the browser.`),console.log(`
|
|
1179
|
-
When done:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${
|
|
1179
|
+
When done:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${a.file}`),console.log(" emailr templates stop-preview"),a.open!==!1)try{await(await import('open')).default(g);}catch{}process.exit(0);}catch(e){l(e instanceof Error?e.message:"Failed to start draft session"),process.exit(1);}}),r.command("stop-preview").description(`Stop any running background preview server
|
|
1180
1180
|
|
|
1181
1181
|
USAGE
|
|
1182
1182
|
emailr templates stop-preview
|
|
@@ -1193,7 +1193,7 @@ EXAMPLES
|
|
|
1193
1193
|
SEE ALSO
|
|
1194
1194
|
emailr templates edit Start live editing session
|
|
1195
1195
|
emailr templates draft Start live drafting session
|
|
1196
|
-
emailr templates preview Preview a template in browser`).action(async()=>{let
|
|
1196
|
+
emailr templates preview Preview a template in browser`).action(async()=>{let a=await import('os'),e=j.join(a.tmpdir(),"emailr-preview.pid");try{let i=O.readFileSync(e,"utf-8").trim();process.kill(parseInt(i,10),"SIGTERM"),O.unlinkSync(e),f("Preview server stopped");}catch{f("No preview server running");}}),r}function Ne(){let r=new Command("domains").description(`Manage sending domains
|
|
1197
1197
|
|
|
1198
1198
|
USAGE
|
|
1199
1199
|
emailr domains <subcommand> [options]
|
|
@@ -1301,7 +1301,7 @@ EXAMPLES
|
|
|
1301
1301
|
emailr domains list --format json | jq '.[] | select(.status == "verified")'
|
|
1302
1302
|
|
|
1303
1303
|
# Count domains
|
|
1304
|
-
emailr domains list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async
|
|
1304
|
+
emailr domains list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),s=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.list();if(a.format==="json")m(s,"json");else {let t=s.map(o=>({ID:o.id,Domain:o.domain,Status:o.status,DKIM:o.dkim_verified,SPF:o.spf_verified,DMARC:o.dmarc_verified,Created:o.created_at}));m(t,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list domains"),process.exit(1);}}),r.command("get <domain_id>").description(`Get domain details
|
|
1305
1305
|
|
|
1306
1306
|
USAGE
|
|
1307
1307
|
emailr domains get <domain_id> [options]
|
|
@@ -1328,7 +1328,7 @@ EXAMPLES
|
|
|
1328
1328
|
emailr domains get dom_abc123 --format json
|
|
1329
1329
|
|
|
1330
1330
|
# Extract DNS records
|
|
1331
|
-
emailr domains get dom_abc123 --format json | jq '.dns_records'`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
1331
|
+
emailr domains get dom_abc123 --format json | jq '.dns_records'`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).domains.get(a);m(t,e.format);}catch(i){l(i instanceof Error?i.message:"Failed to get domain"),process.exit(1);}}),r.command("add <domain_name>").description(`Add a new domain
|
|
1332
1332
|
|
|
1333
1333
|
USAGE
|
|
1334
1334
|
emailr domains add <domain_name> [options]
|
|
@@ -1381,7 +1381,7 @@ NEXT STEPS
|
|
|
1381
1381
|
1. Copy the DNS records shown in the output
|
|
1382
1382
|
2. Add them to your DNS provider (Cloudflare, Route53, etc.)
|
|
1383
1383
|
3. Wait for DNS propagation (up to 48 hours)
|
|
1384
|
-
4. Run: emailr domains verify <domain_id>`).option("--receiving-subdomain <subdomain>","Subdomain for receiving emails").option("--format <format>","Output format (json|table)","table").action(async(
|
|
1384
|
+
4. Run: emailr domains verify <domain_id>`).option("--receiving-subdomain <subdomain>","Subdomain for receiving emails").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t={domain:a};e.receivingSubdomain&&(t.receivingSubdomain=e.receivingSubdomain);let o=await s.domains.add(t);if(e.format==="json")m(o,"json");else {if(f(`Domain added: ${o.domain}`),b("Add the following DNS records to verify your domain:"),console.log(""),o.dns_records){let n=[{Type:"DKIM",...ie(o.dns_records.dkim)},{Type:"SPF",...ie(o.dns_records.spf)},{Type:"DMARC",...ie(o.dns_records.dmarc)}];m(n,"table");}console.log(""),b(`Run 'emailr domains verify ${o.id}' after adding DNS records`);}}catch(i){l(i instanceof Error?i.message:"Failed to add domain"),process.exit(1);}}),r.command("verify <domain_id>").description(`Verify a domain
|
|
1385
1385
|
|
|
1386
1386
|
USAGE
|
|
1387
1387
|
emailr domains verify <domain_id> [options]
|
|
@@ -1419,7 +1419,7 @@ TROUBLESHOOTING
|
|
|
1419
1419
|
1. Run 'check-dns' to see which records are missing
|
|
1420
1420
|
2. Verify records are correctly configured with your DNS provider
|
|
1421
1421
|
3. Wait for DNS propagation (can take up to 48 hours)
|
|
1422
|
-
4. Try verification again`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
1422
|
+
4. Try verification again`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).domains.verify(a);e.format==="json"?m(t,"json"):t.verified?f("Domain verified successfully!"):(b(`Domain status: ${t.status}`),t.dkim_status&&b(`DKIM status: ${t.dkim_status}`));}catch(i){l(i instanceof Error?i.message:"Failed to verify domain"),process.exit(1);}}),r.command("check-dns <domain_id>").description(`Check DNS records for a domain
|
|
1423
1423
|
|
|
1424
1424
|
USAGE
|
|
1425
1425
|
emailr domains check-dns <domain_id> [options]
|
|
@@ -1466,7 +1466,7 @@ COMMON ISSUES
|
|
|
1466
1466
|
|
|
1467
1467
|
Multiple records found:
|
|
1468
1468
|
- Remove duplicate TXT records
|
|
1469
|
-
- Keep only the Emailr-specific record`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
1469
|
+
- Keep only the Emailr-specific record`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).domains.checkDns(a);if(e.format==="json")m(t,"json");else {let o=Object.entries(t).map(([n,c])=>({Record:n,Verified:c.verified,Expected:c.expected||"-",Found:c.found?.join(", ")||"-"}));m(o,"table");}}catch(i){l(i instanceof Error?i.message:"Failed to check DNS"),process.exit(1);}}),r.command("delete <domain_id>").description(`Delete a domain
|
|
1470
1470
|
|
|
1471
1471
|
USAGE
|
|
1472
1472
|
emailr domains delete <domain_id> [options]
|
|
@@ -1496,7 +1496,7 @@ WARNING
|
|
|
1496
1496
|
This action is permanent and cannot be undone.
|
|
1497
1497
|
- Emails from this domain will fail to send
|
|
1498
1498
|
- DNS records can be removed from your DNS provider
|
|
1499
|
-
- Re-add the domain if you need to restore functionality`).option("--format <format>","Output format (json|table)","table").action(async
|
|
1499
|
+
- Re-add the domain if you need to restore functionality`).option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.delete(a),f(`Domain deleted: ${a}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete domain"),process.exit(1);}}),r}function ie(r){return {"Record Type":r.type,Name:r.name,Value:r.value.length>50?r.value.substring(0,47)+"...":r.value,...r.priority!==void 0&&{Priority:r.priority}}}function Ue(){let r=new Command("config").description(`Manage CLI configuration
|
|
1500
1500
|
|
|
1501
1501
|
USAGE
|
|
1502
1502
|
emailr config <subcommand> [options]
|
|
@@ -1589,7 +1589,7 @@ EXAMPLES
|
|
|
1589
1589
|
|
|
1590
1590
|
NOTE
|
|
1591
1591
|
Environment variables (EMAILR_API_KEY, EMAILR_BASE_URL) take precedence
|
|
1592
|
-
over config file values at runtime.`).action(async(
|
|
1592
|
+
over config file values at runtime.`).action(async(a,e)=>{try{let i=["api-key","base-url","format"],s=a.toLowerCase();i.includes(s)||(l(`Invalid config key: ${a}`),b(`Valid keys: ${i.join(", ")}`),process.exit(1));let o={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[s];s==="format"&&!["json","table"].includes(e)&&(l(`Invalid format value: ${e}`),b("Valid formats: json, table"),process.exit(1)),G({[o]:e}),f(`Configuration saved: ${a} = ${s==="api-key"?"***":e}`),b(`Config file: ${C()}`);}catch(i){l(i instanceof Error?i.message:"Failed to save configuration"),process.exit(1);}}),r.command("get <key>").description(`Get a configuration value
|
|
1593
1593
|
|
|
1594
1594
|
USAGE
|
|
1595
1595
|
emailr config get <key>
|
|
@@ -1617,7 +1617,7 @@ EXAMPLES
|
|
|
1617
1617
|
emailr config get format
|
|
1618
1618
|
|
|
1619
1619
|
SEE ALSO
|
|
1620
|
-
emailr config list Show all configuration values`).action(async
|
|
1620
|
+
emailr config list Show all configuration values`).action(async a=>{try{let e=["api-key","base-url","format"],i=a.toLowerCase();e.includes(i)||(l(`Invalid config key: ${a}`),b(`Valid keys: ${e.join(", ")}`),process.exit(1));let t={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[i],o=de(t);o?console.log(i==="api-key"?o.substring(0,8)+"..."+o.substring(o.length-4):o):b(`${a} is not set`);}catch(e){l(e instanceof Error?e.message:"Failed to get configuration"),process.exit(1);}}),r.command("list").description(`List all configuration values
|
|
1621
1621
|
|
|
1622
1622
|
USAGE
|
|
1623
1623
|
emailr config list [options]
|
|
@@ -1645,7 +1645,7 @@ EXAMPLES
|
|
|
1645
1645
|
|
|
1646
1646
|
SEE ALSO
|
|
1647
1647
|
emailr config get Get a single configuration value
|
|
1648
|
-
emailr config path Show config file location`).option("--format <format>","Output format (json|table)","table").action(async
|
|
1648
|
+
emailr config path Show config file location`).option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),i={"api-key":e.apiKey?e.apiKey.substring(0,8)+"..."+e.apiKey.substring(e.apiKey.length-4):"(not set)","base-url":e.baseUrl||"(default)",format:e.format||"table"};if(a.format==="json")m(i,"json");else {let s=Object.entries(i).map(([t,o])=>({Key:t,Value:o}));m(s,"table");}console.log(""),b(`Config file: ${C()}`);}catch(e){e instanceof Error&&e.message.includes("No API key configured")?(b("No configuration found."),b("Run 'emailr config set api-key <your-api-key>' to get started.")):(l(e instanceof Error?e.message:"Failed to list configuration"),process.exit(1));}}),r.command("path").description(`Show the configuration file path
|
|
1649
1649
|
|
|
1650
1650
|
USAGE
|
|
1651
1651
|
emailr config path
|
|
@@ -1672,7 +1672,7 @@ EXAMPLES
|
|
|
1672
1672
|
open $(emailr config path)
|
|
1673
1673
|
|
|
1674
1674
|
# View config file contents
|
|
1675
|
-
cat $(emailr config path)`).action(()=>{console.log(
|
|
1675
|
+
cat $(emailr config path)`).action(()=>{console.log(C());}),r.command("init").description(`Initialize configuration interactively
|
|
1676
1676
|
|
|
1677
1677
|
USAGE
|
|
1678
1678
|
emailr config init [options]
|
|
@@ -1705,7 +1705,7 @@ ALTERNATIVE: ENVIRONMENT VARIABLES
|
|
|
1705
1705
|
|
|
1706
1706
|
SEE ALSO
|
|
1707
1707
|
emailr config set Set individual configuration values
|
|
1708
|
-
emailr config list View current configuration`).option("--api-key <key>","API key to use").option("--base-url <url>","Base URL for API").action(async
|
|
1708
|
+
emailr config list View current configuration`).option("--api-key <key>","API key to use").option("--base-url <url>","Base URL for API").action(async a=>{try{a.apiKey?(G({apiKey:a.apiKey,baseUrl:a.baseUrl}),f("Configuration initialized!"),b(`Config file: ${C()}`)):(b("Initialize your Emailr CLI configuration:"),console.log(""),b("Run with --api-key flag:"),console.log(" emailr config init --api-key <your-api-key>"),console.log(""),b("Or set environment variable:"),console.log(" export EMAILR_API_KEY=<your-api-key>"));}catch(e){l(e instanceof Error?e.message:"Failed to initialize configuration"),process.exit(1);}}),r}function Pe(){let r=new Command("broadcasts").description(`Manage broadcast campaigns
|
|
1709
1709
|
|
|
1710
1710
|
USAGE
|
|
1711
1711
|
emailr broadcasts <subcommand> [options]
|
|
@@ -1855,7 +1855,7 @@ EXAMPLES
|
|
|
1855
1855
|
emailr broadcasts list --format json
|
|
1856
1856
|
|
|
1857
1857
|
# Pipe JSON to jq for processing
|
|
1858
|
-
emailr broadcasts list --format json | jq '.[] | select(.status == "scheduled")'`).option("--status <status>","Filter by status (draft, scheduled, sending, sent)").option("--limit <number>","Number of broadcasts to return","20").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async
|
|
1858
|
+
emailr broadcasts list --format json | jq '.[] | select(.status == "scheduled")'`).option("--status <status>","Filter by status (draft, scheduled, sending, sent)").option("--limit <number>","Number of broadcasts to return","20").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={status:a.status,limit:parseInt(a.limit)};if(a.tags){let o=a.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean);o.length>0&&(s.tags=o.join(","));}let t=await i.broadcasts.list(s);if(a.format==="json")m(t,"json");else {if(t.length===0){console.log("No broadcasts found.");return}let o=t.map(n=>({ID:n.id,Name:n.name,Subject:n.subject.substring(0,30)+(n.subject.length>30?"...":""),Status:n.status,Tags:n.tags?.join(", ")||"-",Recipients:n.total_recipients||0,Sent:n.sent_count||0,Created:new Date(n.created_at).toLocaleDateString()}));m(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list broadcasts"),process.exit(1);}}),r.command("get <broadcast_id>").description(`Get broadcast details
|
|
1859
1859
|
|
|
1860
1860
|
USAGE
|
|
1861
1861
|
emailr broadcasts get <broadcast_id> [options]
|
|
@@ -1890,7 +1890,7 @@ EXAMPLES
|
|
|
1890
1890
|
emailr broadcasts get brd_abc123 --format json
|
|
1891
1891
|
|
|
1892
1892
|
# Pipe JSON to jq for processing
|
|
1893
|
-
emailr broadcasts get brd_abc123 --format json | jq '{sent: .sent_count, opened: .opened_count}'`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
1893
|
+
emailr broadcasts get brd_abc123 --format json | jq '{sent: .sent_count, opened: .opened_count}'`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.get(a);e.format==="json"?m(t,"json"):m({ID:t.id,Name:t.name,Subject:t.subject,"From Email":t.from_email,"From Name":t.from_name||"-",Status:t.status,Tags:t.tags?.join(", ")||"-","Total Recipients":t.total_recipients||0,"Sent Count":t.sent_count||0,Delivered:t.delivered_count||0,Opened:t.opened_count||0,Clicked:t.clicked_count||0,Bounced:t.bounced_count||0,"Scheduled At":t.scheduled_at||"N/A","Started At":t.started_at||"N/A","Completed At":t.completed_at||"N/A","Created At":t.created_at},"table");}catch(i){l(i instanceof Error?i.message:"Failed to get broadcast"),process.exit(1);}}),r.command("create").description(`Create a new broadcast
|
|
1894
1894
|
|
|
1895
1895
|
USAGE
|
|
1896
1896
|
emailr broadcasts create --name <name> --subject <subject> --from <email> [options]
|
|
@@ -1971,7 +1971,7 @@ EXAMPLES
|
|
|
1971
1971
|
|
|
1972
1972
|
SEE ALSO
|
|
1973
1973
|
emailr templates Create and manage email templates
|
|
1974
|
-
emailr segments Create and manage contact segments`).requiredOption("--name <name>","Broadcast name").requiredOption("--subject <subject>","Email subject").requiredOption("--from <email>","Sender email address").option("--from-name <name>","Sender display name").option("--reply-to <email>","Reply-To email address").option("--preview-text <text>","Preview text (preheader)").option("--inbox-id <inbox_id>","Inbox ID for sender identity defaults").option("--inbox-ids <ids>","Comma-separated inbox IDs for inbox rotation").option("--sending-speed <speed>","Sending speed: auto | slow | normal | instant (default: auto)").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("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async
|
|
1974
|
+
emailr segments Create and manage contact segments`).requiredOption("--name <name>","Broadcast name").requiredOption("--subject <subject>","Email subject").requiredOption("--from <email>","Sender email address").option("--from-name <name>","Sender display name").option("--reply-to <email>","Reply-To email address").option("--preview-text <text>","Preview text (preheader)").option("--inbox-id <inbox_id>","Inbox ID for sender identity defaults").option("--inbox-ids <ids>","Comma-separated inbox IDs for inbox rotation").option("--sending-speed <speed>","Sending speed: auto | slow | normal | instant (default: auto)").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("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={name:a.name,subject:a.subject,from_email:a.from,from_name:a.fromName,reply_to:a.replyTo,preview_text:a.previewText,inbox_id:a.inboxId,template_id:a.template,segment_id:a.segment,html_content:a.html,text_content:a.text,scheduled_at:a.schedule};a.inboxIds&&(s.inbox_ids=a.inboxIds.split(",").map(o=>o.trim()).filter(Boolean)),a.sendingSpeed&&(s.sending_speed=a.sendingSpeed),a.tags&&(s.tags=a.tags.split(",").map(o=>o.trim().toLowerCase()).filter(Boolean));let t=await i.broadcasts.create(s);a.format==="json"?m(t,"json"):(f("Broadcast created successfully!"),m({ID:t.id,Name:t.name,Status:t.status,Tags:t.tags?.join(", ")||"-","Scheduled At":t.scheduled_at||"Not scheduled"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create broadcast"),process.exit(1);}}),r.command("send <broadcast_id>").description(`Send a broadcast immediately
|
|
1975
1975
|
|
|
1976
1976
|
USAGE
|
|
1977
1977
|
emailr broadcasts send <broadcast_id> [options]
|
|
@@ -2004,7 +2004,7 @@ EXAMPLES
|
|
|
2004
2004
|
NOTE
|
|
2005
2005
|
Sending is asynchronous. The command returns when sending starts,
|
|
2006
2006
|
not when all emails are delivered. Use 'emailr broadcasts get' to
|
|
2007
|
-
check delivery progress.`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2007
|
+
check delivery progress.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.send(a);e.format==="json"?m(t,"json"):(f("Broadcast sent successfully!"),m({Success:t.success,Sent:t.sent,Total:t.total},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to send broadcast"),process.exit(1);}}),r.command("schedule <broadcast_id>").description(`Schedule a broadcast for future delivery
|
|
2008
2008
|
|
|
2009
2009
|
USAGE
|
|
2010
2010
|
emailr broadcasts schedule <broadcast_id> --at <datetime> [options]
|
|
@@ -2054,7 +2054,7 @@ EXAMPLES
|
|
|
2054
2054
|
emailr broadcasts schedule brd_abc123 --at "2024-12-25T10:00:00Z" --format json
|
|
2055
2055
|
|
|
2056
2056
|
SEE ALSO
|
|
2057
|
-
emailr broadcasts cancel Cancel a scheduled broadcast`).requiredOption("--at <datetime>","Schedule time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async(
|
|
2057
|
+
emailr broadcasts cancel Cancel a scheduled broadcast`).requiredOption("--at <datetime>","Schedule time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.schedule(a,e.at);e.format==="json"?m(t,"json"):(f("Broadcast scheduled successfully!"),m({ID:t.id,Name:t.name,Status:t.status,"Scheduled At":t.scheduled_at},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to schedule broadcast"),process.exit(1);}}),r.command("cancel <broadcast_id>").description(`Cancel a scheduled broadcast
|
|
2058
2058
|
|
|
2059
2059
|
USAGE
|
|
2060
2060
|
emailr broadcasts cancel <broadcast_id> [options]
|
|
@@ -2082,7 +2082,7 @@ EXAMPLES
|
|
|
2082
2082
|
emailr broadcasts cancel brd_abc123 --format json
|
|
2083
2083
|
|
|
2084
2084
|
NOTE
|
|
2085
|
-
Broadcasts that are already 'sending' or 'sent' cannot be cancelled.`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2085
|
+
Broadcasts that are already 'sending' or 'sent' cannot be cancelled.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.cancel(a);e.format==="json"?m(t,"json"):f("Broadcast cancelled successfully!");}catch(i){l(i instanceof Error?i.message:"Failed to cancel broadcast"),process.exit(1);}}),r.command("update <broadcast_id>").description(`Update a broadcast
|
|
2086
2086
|
|
|
2087
2087
|
USAGE
|
|
2088
2088
|
emailr broadcasts update <broadcast_id> [options]
|
|
@@ -2135,7 +2135,7 @@ EXAMPLES
|
|
|
2135
2135
|
emailr broadcasts update brd_abc123 --name "Updated" --format json
|
|
2136
2136
|
|
|
2137
2137
|
NOTE
|
|
2138
|
-
Only broadcasts in 'draft' or 'scheduled' status can be updated.`).option("--name <name>","Broadcast name").option("--subject <subject>","Email subject").option("--from <email>","Sender email address").option("--from-name <name>","Sender display name").option("--reply-to <email>","Reply-To email address").option("--preview-text <text>","Preview text (preheader)").option("--inbox-id <inbox_id>",'Inbox ID for sender identity (use "none" to clear)').option("--inbox-ids <ids>",'Comma-separated inbox IDs for rotation (use "none" to clear)').option("--sending-speed <speed>",'Sending speed: auto | slow | normal | instant (use "none" to clear)').option("--template <id>",'Template ID (use "none" to clear)').option("--segment <id>",'Segment ID (use "none" to clear)').option("--topic <id>",'Topic ID (use "none" to clear)').option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>",'Schedule time ISO 8601 (use "none" to clear)').option("--tags <tags>","Comma-separated tags (replaces existing)").option("--format <format>","Output format (json|table)","table").action(async(
|
|
2138
|
+
Only broadcasts in 'draft' or 'scheduled' status can be updated.`).option("--name <name>","Broadcast name").option("--subject <subject>","Email subject").option("--from <email>","Sender email address").option("--from-name <name>","Sender display name").option("--reply-to <email>","Reply-To email address").option("--preview-text <text>","Preview text (preheader)").option("--inbox-id <inbox_id>",'Inbox ID for sender identity (use "none" to clear)').option("--inbox-ids <ids>",'Comma-separated inbox IDs for rotation (use "none" to clear)').option("--sending-speed <speed>",'Sending speed: auto | slow | normal | instant (use "none" to clear)').option("--template <id>",'Template ID (use "none" to clear)').option("--segment <id>",'Segment ID (use "none" to clear)').option("--topic <id>",'Topic ID (use "none" to clear)').option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>",'Schedule time ISO 8601 (use "none" to clear)').option("--tags <tags>","Comma-separated tags (replaces existing)").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t={};e.name&&(t.name=e.name),e.subject&&(t.subject=e.subject),e.from&&(t.from_email=e.from),e.fromName&&(t.from_name=e.fromName),e.replyTo&&(t.reply_to=e.replyTo),e.previewText&&(t.preview_text=e.previewText),e.html&&(t.html_content=e.html),e.text&&(t.text_content=e.text),e.inboxId&&(t.inbox_id=e.inboxId==="none"?null:e.inboxId),e.inboxIds&&(t.inbox_ids=e.inboxIds==="none"?null:e.inboxIds.split(",").map(n=>n.trim()).filter(Boolean)),e.sendingSpeed&&(t.sending_speed=e.sendingSpeed==="none"?null:e.sendingSpeed),e.template&&(t.template_id=e.template==="none"?null:e.template),e.segment&&(t.segment_id=e.segment==="none"?null:e.segment),e.topic&&(t.topic_id=e.topic==="none"?null:e.topic),e.schedule&&(t.scheduled_at=e.schedule==="none"?null:e.schedule),e.tags&&(t.tags=e.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean)),Object.keys(t).length===0&&(l("No update fields specified. Use --help to see available options."),process.exit(1));let o=await s.broadcasts.update(a,t);e.format==="json"?m(o,"json"):(f("Broadcast updated successfully!"),m({ID:o.id,Name:o.name,Subject:o.subject,Status:o.status,Tags:o.tags?.join(", ")||"-","Scheduled At":o.scheduled_at||"Not scheduled"},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to update broadcast"),process.exit(1);}}),r.command("delete <broadcast_id>").description(`Delete a broadcast
|
|
2139
2139
|
|
|
2140
2140
|
USAGE
|
|
2141
2141
|
emailr broadcasts delete <broadcast_id> [options]
|
|
@@ -2163,7 +2163,7 @@ EXAMPLES
|
|
|
2163
2163
|
|
|
2164
2164
|
WARNING
|
|
2165
2165
|
This action is permanent and cannot be undone.
|
|
2166
|
-
All delivery statistics will be lost.`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2166
|
+
All delivery statistics will be lost.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.delete(a);e.format==="json"?m(t,"json"):f("Broadcast deleted successfully!");}catch(i){l(i instanceof Error?i.message:"Failed to delete broadcast"),process.exit(1);}}),r}function Ae(){let r=new Command("webhooks").description(`Manage webhooks
|
|
2167
2167
|
|
|
2168
2168
|
USAGE
|
|
2169
2169
|
emailr webhooks <subcommand> [options]
|
|
@@ -2209,7 +2209,7 @@ EXAMPLES
|
|
|
2209
2209
|
|
|
2210
2210
|
OUTPUT FORMATS
|
|
2211
2211
|
--format json Machine-readable JSON array of webhook objects
|
|
2212
|
-
--format table Human-readable table with ID, Name, URL, Type, Events, Active`).option("--format <format>","Output format (json|table)","table").action(async
|
|
2212
|
+
--format table Human-readable table with ID, Name, URL, Type, Events, Active`).option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),s=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).webhooks.list();if(a.format==="json")m(s,"json");else {if(s.length===0){console.log("No webhooks found.");return}let t=s.map(o=>({ID:o.id,Name:o.name,URL:o.url.substring(0,40)+(o.url.length>40?"...":""),Type:o.type,Events:o.events.join(", "),Active:o.active?"Yes":"No"}));m(t,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list webhooks"),process.exit(1);}}),r.command("create").description(`Create a new webhook
|
|
2213
2213
|
|
|
2214
2214
|
WEBHOOK TYPES
|
|
2215
2215
|
transactional Subscribes to: email.sent, email.delivered, email.bounced,
|
|
@@ -2220,19 +2220,19 @@ WEBHOOK TYPES
|
|
|
2220
2220
|
|
|
2221
2221
|
OUTPUT FORMATS
|
|
2222
2222
|
--format json Full webhook object including generated secret
|
|
2223
|
-
--format table Summary with ID, Name, URL, Type, and Secret`).requiredOption("--name <name>","Webhook name").requiredOption("--url <url>","Webhook URL (HTTPS)").requiredOption("--type <type>","Webhook type: transactional | domain | receiving").option("--inbox-ids <ids>","Comma-separated inbox UUIDs (only for receiving type)").option("--format <format>","Output format (json|table)","table").action(async
|
|
2223
|
+
--format table Summary with ID, Name, URL, Type, and Secret`).requiredOption("--name <name>","Webhook name").requiredOption("--url <url>","Webhook URL (HTTPS)").requiredOption("--type <type>","Webhook type: transactional | domain | receiving").option("--inbox-ids <ids>","Comma-separated inbox UUIDs (only for receiving type)").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=["transactional","domain","receiving"];e.includes(a.type)||(l(`Invalid type "${a.type}". Must be one of: ${e.join(", ")}`),process.exit(1));let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t={name:a.name,url:a.url,type:a.type};a.inboxIds&&(t.inbox_ids=a.inboxIds.split(",").map(n=>n.trim()));let o=await s.webhooks.create(t);a.format==="json"?m(o,"json"):(f("Webhook created successfully!"),m({ID:o.id,Name:o.name,URL:o.url,Type:o.type,Events:o.events.join(", "),Secret:o.secret},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create webhook"),process.exit(1);}}),r.command("toggle <webhook_id>").description(`Toggle webhook active state (enable/disable)
|
|
2224
2224
|
|
|
2225
2225
|
OUTPUT FORMATS
|
|
2226
2226
|
--format json Returns: { success: boolean, active: boolean }
|
|
2227
|
-
--format table Human-readable success message`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2227
|
+
--format table Human-readable success message`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).webhooks.toggle(a);e.format==="json"?m(t,"json"):f(`Webhook ${t.active?"enabled":"disabled"} successfully!`);}catch(i){l(i instanceof Error?i.message:"Failed to toggle webhook"),process.exit(1);}}),r.command("deliveries <webhook_id>").description(`List recent deliveries for a webhook
|
|
2228
2228
|
|
|
2229
2229
|
OUTPUT FORMATS
|
|
2230
2230
|
--format json Machine-readable JSON array of delivery objects
|
|
2231
|
-
--format table Human-readable table with ID, Event, Status, Attempts, Dates`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2231
|
+
--format table Human-readable table with ID, Event, Status, Attempts, Dates`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).webhooks.listDeliveries(a);if(e.format==="json")m(t,"json");else {if(t.length===0){console.log("No deliveries found.");return}let o=t.map(n=>({ID:n.id,Event:n.event_type,Status:n.response_status??"-",Attempts:n.attempt_count,"Delivered At":n.delivered_at??"-",Created:new Date(n.created_at).toLocaleString()}));m(o,"table");}}catch(i){l(i instanceof Error?i.message:"Failed to list deliveries"),process.exit(1);}}),r.command("delete <webhook_id>").description(`Delete a webhook permanently
|
|
2232
2232
|
|
|
2233
2233
|
OUTPUT FORMATS
|
|
2234
2234
|
--format json Returns: { success: boolean }
|
|
2235
|
-
--format table Human-readable success message`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2235
|
+
--format table Human-readable success message`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).webhooks.delete(a);e.format==="json"?m(t,"json"):f("Webhook deleted successfully!");}catch(i){l(i instanceof Error?i.message:"Failed to delete webhook"),process.exit(1);}}),r}function Re(){let r=new Command("segments").description(`Manage contact segments
|
|
2236
2236
|
|
|
2237
2237
|
USAGE
|
|
2238
2238
|
emailr segments <subcommand> [options]
|
|
@@ -2372,7 +2372,7 @@ EXAMPLES
|
|
|
2372
2372
|
emailr segments list --format json
|
|
2373
2373
|
|
|
2374
2374
|
# Pipe JSON to jq for processing
|
|
2375
|
-
emailr segments list --format json | jq '.[].name'`).option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async
|
|
2375
|
+
emailr segments list --format json | jq '.[].name'`).option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={};if(a.tags){let o=a.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean);o.length>0&&(s.tags=o.join(","));}let t=await i.segments.list(s);if(a.format==="json")m(t,"json");else {if(t.length===0){console.log("No segments found.");return}let o=t.map(n=>({ID:n.id,Name:n.name,Description:(n.description||"").substring(0,30)+((n.description?.length||0)>30?"...":""),Tags:n.tags?.join(", ")||"-","Created At":new Date(n.created_at).toLocaleDateString()}));m(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list segments"),process.exit(1);}}),r.command("get <segment_id>").description(`Get segment details
|
|
2376
2376
|
|
|
2377
2377
|
USAGE
|
|
2378
2378
|
emailr segments get <segment_id> [options]
|
|
@@ -2399,7 +2399,7 @@ EXAMPLES
|
|
|
2399
2399
|
emailr segments get seg_abc123 --format json
|
|
2400
2400
|
|
|
2401
2401
|
# Pipe JSON to jq to view conditions
|
|
2402
|
-
emailr segments get seg_abc123 --format json | jq '.conditions'`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2402
|
+
emailr segments get seg_abc123 --format json | jq '.conditions'`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).segments.get(a);e.format==="json"?m(t,"json"):m({ID:t.id,Name:t.name,Description:t.description||"N/A",Conditions:JSON.stringify(t.conditions),Tags:t.tags?.join(", ")||"-","Created At":t.created_at,"Updated At":t.updated_at},"table");}catch(i){l(i instanceof Error?i.message:"Failed to get segment"),process.exit(1);}}),r.command("create").description(`Create a new segment
|
|
2403
2403
|
|
|
2404
2404
|
USAGE
|
|
2405
2405
|
emailr segments create --name <segment_name> --conditions <json> [options]
|
|
@@ -2449,7 +2449,7 @@ EXAMPLES
|
|
|
2449
2449
|
# Get JSON output for scripting
|
|
2450
2450
|
emailr segments create --name "Test" \\
|
|
2451
2451
|
--conditions '[{"field": "subscribed", "operator": "equals", "value": true}]' \\
|
|
2452
|
-
--format json`).requiredOption("--name <name>","Segment name").requiredOption("--conditions <json>","Segment conditions as JSON").option("--description <description>","Segment description").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async
|
|
2452
|
+
--format json`).requiredOption("--name <name>","Segment name").requiredOption("--conditions <json>","Segment conditions as JSON").option("--description <description>","Segment description").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s;try{s=JSON.parse(a.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let t={name:a.name,description:a.description,conditions:s};a.tags&&(t.tags=a.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let o=await i.segments.create(t);a.format==="json"?m(o,"json"):(f("Segment created successfully!"),m({ID:o.id,Name:o.name,Tags:o.tags?.join(", ")||"-"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create segment"),process.exit(1);}}),r.command("update <segment_id>").description(`Update a segment
|
|
2453
2453
|
|
|
2454
2454
|
USAGE
|
|
2455
2455
|
emailr segments update <segment_id> [options]
|
|
@@ -2495,7 +2495,7 @@ EXAMPLES
|
|
|
2495
2495
|
--conditions '[{"field": "metadata.plan", "operator": "equals", "value": "enterprise"}]'
|
|
2496
2496
|
|
|
2497
2497
|
# Get JSON output
|
|
2498
|
-
emailr segments update seg_abc123 --name "Updated" --format json`).option("--name <name>","New segment name").option("--description <description>","New description").option("--conditions <json>","New conditions as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(
|
|
2498
|
+
emailr segments update seg_abc123 --name "Updated" --format json`).option("--name <name>","New segment name").option("--description <description>","New description").option("--conditions <json>","New conditions as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t={};if(e.name&&(t.name=e.name),e.description&&(t.description=e.description),e.conditions)try{t.conditions=JSON.parse(e.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}e.tags&&(t.tags=e.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let o=await s.segments.update(a,t);e.format==="json"?m(o,"json"):(f("Segment updated successfully!"),m({ID:o.id,Name:o.name,Tags:o.tags?.join(", ")||"-"},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to update segment"),process.exit(1);}}),r.command("delete <segment_id>").description(`Delete a segment
|
|
2499
2499
|
|
|
2500
2500
|
USAGE
|
|
2501
2501
|
emailr segments delete <segment_id>
|
|
@@ -2520,7 +2520,7 @@ EXAMPLES
|
|
|
2520
2520
|
|
|
2521
2521
|
WARNING
|
|
2522
2522
|
This action is permanent and cannot be undone.
|
|
2523
|
-
Any broadcasts targeting this segment will need to be updated.`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2523
|
+
Any broadcasts targeting this segment will need to be updated.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).segments.delete(a);e.format==="json"?m(t,"json"):f("Segment deleted successfully!");}catch(i){l(i instanceof Error?i.message:"Failed to delete segment"),process.exit(1);}}),r.command("count <segment_id>").description(`Get the number of contacts in a segment
|
|
2524
2524
|
|
|
2525
2525
|
USAGE
|
|
2526
2526
|
emailr segments count <segment_id> [options]
|
|
@@ -2551,7 +2551,7 @@ EXAMPLES
|
|
|
2551
2551
|
|
|
2552
2552
|
TIP
|
|
2553
2553
|
Use this before sending broadcasts to verify your segment
|
|
2554
|
-
targets the expected number of contacts.`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2554
|
+
targets the expected number of contacts.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).segments.getContactsCount(a);e.format==="json"?m(t,"json"):console.log(`Segment contains ${t.count} contacts.`);}catch(i){l(i instanceof Error?i.message:"Failed to get segment count"),process.exit(1);}}),r}function wt(r){try{let a=r.startsWith("http")?r:`http://localhost${r}`,e=new URL(a);return {key:e.searchParams.get("key")??void 0,code:e.searchParams.get("code")??void 0,state:e.searchParams.get("state")??void 0,error:e.searchParams.get("error")??void 0,message:e.searchParams.get("message")??void 0}}catch{return {}}}function St(){return `<!DOCTYPE html>
|
|
2555
2555
|
<html lang="en">
|
|
2556
2556
|
<head>
|
|
2557
2557
|
<meta charset="UTF-8">
|
|
@@ -2597,7 +2597,7 @@ TIP
|
|
|
2597
2597
|
<p>You can close this window and return to your terminal.</p>
|
|
2598
2598
|
</div>
|
|
2599
2599
|
</body>
|
|
2600
|
-
</html>`}function
|
|
2600
|
+
</html>`}function oe(r){return `<!DOCTYPE html>
|
|
2601
2601
|
<html lang="en">
|
|
2602
2602
|
<head>
|
|
2603
2603
|
<meta charset="UTF-8">
|
|
@@ -2652,7 +2652,7 @@ TIP
|
|
|
2652
2652
|
<p style="margin-top: 1rem;">Please close this window and try again.</p>
|
|
2653
2653
|
</div>
|
|
2654
2654
|
</body>
|
|
2655
|
-
</html>`}function
|
|
2655
|
+
</html>`}function De(){let r=null,a=0,e=null,i=null,s=null;return {async start(){return new Promise((t,o)=>{r=at.createServer((n,c)=>{if(n.method!=="GET"||!n.url?.startsWith("/callback")){c.writeHead(404,{"Content-Type":"text/plain"}),c.end("Not Found");return}let u=wt(n.url);if(s&&u.state!==s){c.writeHead(400,{"Content-Type":"text/html"}),c.end(oe("Security verification failed. State parameter mismatch.")),e&&e({success:false,error:"State parameter mismatch"});return}if(u.error){let p=u.message||u.error;c.writeHead(200,{"Content-Type":"text/html"}),c.end(oe(p)),e&&e({success:false,error:p});return}let g=u.key||u.code;if(g){c.writeHead(200,{"Content-Type":"text/html"}),c.end(St()),e&&e({success:true,apiKey:g});return}c.writeHead(400,{"Content-Type":"text/html"}),c.end(oe("Invalid callback: missing required parameters.")),e&&e({success:false,error:"Invalid callback: missing required parameters"});}),r.listen(0,"127.0.0.1",()=>{let n=r.address();n&&typeof n=="object"?(a=n.port,t({port:a,url:`http://127.0.0.1:${a}/callback`})):o(new Error("Failed to get server address"));}),r.on("error",n=>{o(new Error(`Failed to start callback server: ${n.message}`));});})},async waitForCallback(t,o){return s=t,new Promise(n=>{e=n,i=setTimeout(()=>{e&&e({success:false,error:"Login timed out. Please try again."});},o);})},async stop(){if(i&&(clearTimeout(i),i=null),r)return new Promise(t=>{r.close(()=>{r=null,t();});})}}}function ke(){return vt.randomBytes(32).toString("hex")}function Me(r){return new Promise(a=>{let e=process.platform,i;switch(e){case "darwin":i=`open "${r}"`;break;case "win32":i=`start "" "${r}"`;break;default:i=`xdg-open "${r}"`;break}exec(i,s=>{a(!s);});})}var X=120,Ot=process.env.EMAILR_WEB_URL||"https://app.emailr.dev";function Et(r,a){let e=`http://127.0.0.1:${a}/callback`,i=new URLSearchParams({state:r,callback_url:e});return `${Ot}/consent/authorize?${i.toString()}`}function Le(){return new Command("login").description("Log in to Emailr via browser authentication").option("-t, --timeout <seconds>","Timeout in seconds",String(X)).option("--no-browser","Don't automatically open the browser").action(async a=>{await jt({timeout:parseInt(a.timeout,10)||X,noBrowser:a.browser===false});})}async function jt(r){let a=De(),e=(r.timeout||X)*1e3;try{b("Starting authentication server...");let{port:i,url:s}=await a.start(),t=ke(),o=Et(t,i);console.log(""),b("Authorization URL:"),console.log(` ${o}`),console.log(""),r.noBrowser?b("Please open the URL above in your browser to continue."):await Me(o)?b("Browser opened. Please complete authentication in your browser."):(N("Could not open browser automatically."),b("Please open the URL above in your browser to continue.")),console.log(""),b(`Waiting for authentication (timeout: ${r.timeout||X}s)...`);let n=await a.waitForCallback(t,e);n.success&&n.apiKey?(G({apiKey:n.apiKey}),console.log(""),f("Login successful!"),b(`API key saved to: ${C()}`),b("You can now use the Emailr CLI.")):(console.log(""),l(n.error||"Authentication failed."),b("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1));}catch(i){console.log(""),l(i instanceof Error?i.message:"An unexpected error occurred."),b("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1);}finally{await a.stop();}}var ne=j.join(z.homedir(),".config","opencode","skills","emailr-cli"),Nt=`---
|
|
2656
2656
|
name: emailr-cli
|
|
2657
2657
|
description: Operate the Emailr CLI to send emails, reply to threads, manage contacts, templates, inboxes, domains, broadcasts, webhooks, and segments. Includes thread reply auto-resolution and live preview editing for templates.
|
|
2658
2658
|
---
|
|
@@ -2768,7 +2768,7 @@ emailr domains add example.com
|
|
|
2768
2768
|
emailr domains verify example.com
|
|
2769
2769
|
emailr domains list
|
|
2770
2770
|
\`\`\`
|
|
2771
|
-
`;function Ut(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function Pt(){console.log("Installing OpenCode AI agent...");try{return execSync("npm install -g opencode-ai@latest",{stdio:"inherit"}),!0}catch{if(process.platform==="darwin")try{return execSync("brew install anomalyco/tap/opencode",{stdio:"inherit"}),!0}catch{return false}return false}}function At(){
|
|
2771
|
+
`;function Ut(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function Pt(){console.log("Installing OpenCode AI agent...");try{return execSync("npm install -g opencode-ai@latest",{stdio:"inherit"}),!0}catch{if(process.platform==="darwin")try{return execSync("brew install anomalyco/tap/opencode",{stdio:"inherit"}),!0}catch{return false}return false}}function At(){O.existsSync(ne)||O.mkdirSync(ne,{recursive:true});let r=j.join(ne,"SKILL.md");O.writeFileSync(r,Nt,"utf-8");}function Ge(){return new Command("agent").description(`Launch an AI agent with Emailr CLI expertise
|
|
2772
2772
|
|
|
2773
2773
|
This opens OpenCode (opencode.ai), an AI coding agent that knows how to use all Emailr CLI commands.
|
|
2774
2774
|
The agent can help you:
|
|
@@ -2776,10 +2776,10 @@ The agent can help you:
|
|
|
2776
2776
|
- Manage contacts, broadcasts, and segments
|
|
2777
2777
|
- Configure domains and webhooks
|
|
2778
2778
|
|
|
2779
|
-
The agent runs in your terminal with full access to the emailr CLI.`).option("--install","Install OpenCode if not already installed").option("--model <model>","Model to use (e.g., anthropic/claude-sonnet-4)").action(async
|
|
2780
|
-
Install it with one of:`),console.log(" emailr agent --install"),console.log(" npm install -g opencode-ai@latest"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)));try{At(),
|
|
2779
|
+
The agent runs in your terminal with full access to the emailr CLI.`).option("--install","Install OpenCode if not already installed").option("--model <model>","Model to use (e.g., anthropic/claude-sonnet-4)").action(async a=>{Ut()||(a.install?Pt()||(l("Failed to install OpenCode. Please install manually:"),console.log(" npm install -g opencode-ai@latest"),console.log(" # or"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)):(l("OpenCode AI agent is not installed."),console.log(`
|
|
2780
|
+
Install it with one of:`),console.log(" emailr agent --install"),console.log(" npm install -g opencode-ai@latest"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)));try{At(),f("Emailr CLI skill loaded");}catch(s){N(`Could not install skill: ${s instanceof Error?s.message:String(s)}`);}let e=[];a.model&&e.push("--model",a.model),console.log(`
|
|
2781
2781
|
Starting Emailr AI Agent...`),console.log("The agent has the emailr-cli skill loaded."),console.log(`Ask it to help with emails, templates, contacts, or broadcasts.
|
|
2782
|
-
`);let i=spawn("opencode",e,{stdio:"inherit",env:process.env});i.on("error",
|
|
2782
|
+
`);let i=spawn("opencode",e,{stdio:"inherit",env:process.env});i.on("error",s=>{l(`Failed to start agent: ${s.message}`),process.exit(1);}),i.on("exit",s=>{process.exit(s??0);});})}function $e(){let r=new Command("inbox").description(`Manage inbound emails (inbox)
|
|
2783
2783
|
|
|
2784
2784
|
USAGE
|
|
2785
2785
|
emailr inbox <subcommand> [options]
|
|
@@ -2859,7 +2859,7 @@ EXAMPLES
|
|
|
2859
2859
|
|
|
2860
2860
|
OUTPUT FORMATS
|
|
2861
2861
|
--format json Machine-readable JSON with data array and pagination
|
|
2862
|
-
--format table Human-readable table with ID, From, Subject, To, Date (default)`).option("--limit <count>","Number of emails to return","20").option("--page <number>","Page number","1").option("--email <address>","Filter by sender email (partial match)").option("--domain <domain>","Filter by sender domain").option("--inbox-id <inbox_id>","Filter by inbox ID").option("--format <format>","Output format (json|table)","table").action(async
|
|
2862
|
+
--format table Human-readable table with ID, From, Subject, To, Date (default)`).option("--limit <count>","Number of emails to return","20").option("--page <number>","Page number","1").option("--email <address>","Filter by sender email (partial match)").option("--domain <domain>","Filter by sender domain").option("--inbox-id <inbox_id>","Filter by inbox ID").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={status:"received",page:parseInt(a.page),limit:Math.min(parseInt(a.limit),100),email:a.email,domain:a.domain};a.inboxId&&(s.inbox_id=a.inboxId);let t=await i.emails.list(s);if(a.format==="json")m(t,"json");else {if(!t.data||t.data.length===0){b("No emails in inbox");return}let n=t.data.map(c=>({ID:c.id,From:c.from_email,Subject:He(c.subject||"(no subject)",40),To:c.to_email,Date:le(c.created_at),Attachments:c.attachments?.length||0}));m(n,"table"),b(`Page ${t.pagination.page} of ${t.pagination.pages} (${t.pagination.total} total)`);}}catch(e){l(e instanceof Error?e.message:"Failed to list inbox"),process.exit(1);}}),r.command("get <id>").description(`View a received email
|
|
2863
2863
|
|
|
2864
2864
|
USAGE
|
|
2865
2865
|
emailr inbox get <email-id> [options]
|
|
@@ -2880,7 +2880,7 @@ EXAMPLES
|
|
|
2880
2880
|
|
|
2881
2881
|
OUTPUT FORMATS
|
|
2882
2882
|
--format json Full email object with all fields as JSON
|
|
2883
|
-
--format table Key-value table with email metadata and content preview (default)`).option("--content <type>","Content to display: preview | text | html","preview").option("--format <format>","Output format (json|table)","table").action(async(
|
|
2883
|
+
--format table Key-value table with email metadata and content preview (default)`).option("--content <type>","Content to display: preview | text | html","preview").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).emails.get(a);if(e.format==="json")m(t,"json");else {if(m({ID:t.id,From:t.from_email,To:t.to_email,Subject:t.subject||"(no subject)",Date:le(t.created_at),Status:t.status,"Thread ID":t.thread_id||"-","Parent Email":t.parent_email_id||"-",Attachments:t.attachments?.length||0},"table"),console.log(""),e.content==="html")console.log(t.html_content||"(no HTML content)");else if(e.content==="text")console.log(t.text_content||"(no text content)");else {let n=t.text_content||t.html_content?.replace(/<[^>]*>/g,"")||"(no content)";console.log(n);}if(t.attachments&&t.attachments.length>0){console.log(""),b("Attachments:");let n=t.attachments.map(c=>({Filename:c.filename,Type:c.contentType||c.content_type||"-",Size:Dt(c.size||0)}));m(n,"table");}}}catch(i){l(i instanceof Error?i.message:"Failed to get email"),process.exit(1);}}),r.command("thread <id>").description(`View conversation thread for an email
|
|
2884
2884
|
|
|
2885
2885
|
USAGE
|
|
2886
2886
|
emailr inbox thread <email-id> [options]
|
|
@@ -2899,7 +2899,7 @@ EXAMPLES
|
|
|
2899
2899
|
|
|
2900
2900
|
OUTPUT FORMATS
|
|
2901
2901
|
--format json JSON array of all emails in the thread
|
|
2902
|
-
--format table Chronological conversation view with direction indicators (default)`).option("--format <format>","Output format (json|table)","table").action(async(
|
|
2902
|
+
--format table Chronological conversation view with direction indicators (default)`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t=await s.emails.get(a),o=t.thread_id||t.id,n=[],c=new Set;if(n.push(t),c.add(t.id),t.thread_id&&t.thread_id!==t.id)try{let p=await s.emails.get(t.thread_id);c.has(p.id)||(n.push(p),c.add(p.id));}catch{}let u=await s.emails.list({limit:100});if(u.data)for(let p of u.data)c.has(p.id)||(p.thread_id===o||p.parent_email_id===t.id||p.id===t.parent_email_id)&&(n.push(p),c.add(p.id));if(n.sort((p,h)=>new Date(p.created_at).getTime()-new Date(h.created_at).getTime()),e.format==="json")m(n,"json");else {n.length<=1&&b("No other emails in this conversation"),b(`Conversation (${n.length} email${n.length!==1?"s":""}):`),console.log("");for(let p of n){let h=p.status==="received"?"\u2190 IN ":"\u2192 OUT",y=p.id===a?" \u25C0 (selected)":"";console.log(`${h} ${le(p.created_at)}${y}`),console.log(` From: ${p.from_email}`),console.log(` To: ${p.to_email}`),console.log(` Subject: ${p.subject||"(no subject)"}`);let _=p.text_content||p.html_content?.replace(/<[^>]*>/g,"")||"";_&&console.log(` ${He(_.trim(),120)}`),console.log("");}}await new Promise(p=>process.stdout.write("",()=>p())),process.exit(0);}catch(i){l(i instanceof Error?i.message:"Failed to load thread"),process.exit(1);}}),r.command("reply <id>").description(`Reply to a received email
|
|
2903
2903
|
|
|
2904
2904
|
USAGE
|
|
2905
2905
|
emailr inbox reply <email-id> [options]
|
|
@@ -2935,7 +2935,7 @@ EXAMPLES
|
|
|
2935
2935
|
|
|
2936
2936
|
OUTPUT FORMATS
|
|
2937
2937
|
--format json Machine-readable JSON with message_id and status
|
|
2938
|
-
--format table Human-readable summary with Message ID, To, Subject, Status (default)`).option("--html <content>","HTML content for reply").option("--text <content>","Plain text content for reply").option("--html-file <path>","Read HTML content from file").option("--text-file <path>","Read plain text content from file").option("--from <email>","Override sender address").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--subject <subject>","Override reply subject").option("--format <format>","Output format (json|table)","table").action(async(
|
|
2938
|
+
--format table Human-readable summary with Message ID, To, Subject, Status (default)`).option("--html <content>","HTML content for reply").option("--text <content>","Plain text content for reply").option("--html-file <path>","Read HTML content from file").option("--text-file <path>","Read plain text content from file").option("--from <email>","Override sender address").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--subject <subject>","Override reply subject").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t=await s.emails.get(a),o=e.html,n=e.text;if(e.htmlFile)try{o=readFileSync(e.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${e.htmlFile}`),process.exit(1);}if(e.textFile)try{n=readFileSync(e.textFile,"utf-8");}catch{l(`Failed to read text file: ${e.textFile}`),process.exit(1);}!o&&!n&&(l("Reply content is required. Use --html, --text, --html-file, or --text-file"),process.exit(1));let c=e.subject||(t.subject?.startsWith("Re:")?t.subject:`Re: ${t.subject||""}`),u={to:t.from_email,from:e.from||t.to_email,subject:c,html:o||void 0,text:n||o?.replace(/<[^>]*>/g,"")||void 0,replyTo:{in_reply_to:t.message_id||t.ses_message_id,thread_id:t.thread_id||t.id,parent_email_id:t.id}};if(e.cc){let h=e.cc.split(",").map(y=>y.trim());u.cc=h.length===1?h[0]:h;}if(e.bcc){let h=e.bcc.split(",").map(y=>y.trim());u.bcc=h.length===1?h[0]:h;}let g=await s.emails.send(u);e.format==="json"?m(g,"json"):(f("Reply sent successfully!"),m({"Message ID":g.message_id,To:t.from_email,Subject:c,Status:g.status},"table")),await new Promise(h=>process.stdout.write("",()=>h())),process.exit(0);}catch(i){l(i instanceof Error?i.message:"Failed to send reply"),process.exit(1);}}),r.command("forward <id>").description(`Forward a received email
|
|
2939
2939
|
|
|
2940
2940
|
USAGE
|
|
2941
2941
|
emailr inbox forward <email-id> --to <recipients> [options]
|
|
@@ -2961,7 +2961,7 @@ EXAMPLES
|
|
|
2961
2961
|
|
|
2962
2962
|
OUTPUT FORMATS
|
|
2963
2963
|
--format json Machine-readable JSON with message_id and status
|
|
2964
|
-
--format table Human-readable summary with Message ID, To, Recipients, Status (default)`).requiredOption("--to <emails>","Recipient email addresses (comma-separated)").option("--message <text>","Message to include with forwarded email").option("--format <format>","Output format (json|table)","table").action(async(
|
|
2964
|
+
--format table Human-readable summary with Message ID, To, Recipients, Status (default)`).requiredOption("--to <emails>","Recipient email addresses (comma-separated)").option("--message <text>","Message to include with forwarded email").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t=e.to.split(",").map(c=>c.trim()).filter(Boolean),o=await s.emails.forward({email_id:a,to:t.length===1?t[0]:t,message:e.message});e.format==="json"?m(o,"json"):(f("Email forwarded successfully!"),m({"Message ID":o.message_id,To:t.join(", "),Recipients:o.recipients,Status:o.status},"table")),await new Promise(c=>process.stdout.write("",()=>c())),process.exit(0);}catch(i){l(i instanceof Error?i.message:"Failed to forward email"),process.exit(1);}}),r}function He(r,a){return r.length<=a?r:r.slice(0,a-1)+"\u2026"}function le(r){return new Date(r).toLocaleString()}function Dt(r){return r<1024?r+" B":r<1024*1024?(r/1024).toFixed(1)+" KB":(r/(1024*1024)).toFixed(1)+" MB"}function qe(){let r=new Command("inboxes").description(`Manage inboxes
|
|
2965
2965
|
|
|
2966
2966
|
USAGE
|
|
2967
2967
|
emailr inboxes <subcommand> [options]
|
|
@@ -3014,7 +3014,7 @@ EXAMPLES
|
|
|
3014
3014
|
emailr inboxes create --name "Support" --username support --domain example.com --reply-to replies@example.com
|
|
3015
3015
|
|
|
3016
3016
|
# Create and get JSON output
|
|
3017
|
-
emailr inboxes create --name "Sales" --username sales --domain example.com --format json`).requiredOption("--name <name>","Inbox display name").requiredOption("--username <username>","Username for the email address").requiredOption("--domain <domain>","Domain for the email address").option("--reply-to <email>","Custom reply-to address (defaults to username@mail.domain)").option("--format <format>","Output format: json | table","table").action(async
|
|
3017
|
+
emailr inboxes create --name "Sales" --username sales --domain example.com --format json`).requiredOption("--name <name>","Inbox display name").requiredOption("--username <username>","Username for the email address").requiredOption("--domain <domain>","Domain for the email address").option("--reply-to <email>","Custom reply-to address (defaults to username@mail.domain)").option("--format <format>","Output format: json | table","table").action(async a=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={name:a.name,username:a.username,domain:a.domain};a.replyTo&&(s.reply_to=a.replyTo);let t=await i.inboxes.create(s);a.format==="json"?m(t,"json"):(f(`Inbox created: ${t.id}`),m({ID:t.id,Name:t.name,"From Address":t.from_address,"Reply To":t.reply_to,"Inbound Address":t.inbound_address,Created:t.created_at},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create inbox"),process.exit(1);}}),r.command("list").description(`List all inboxes
|
|
3018
3018
|
|
|
3019
3019
|
USAGE
|
|
3020
3020
|
emailr inboxes list [options]
|
|
@@ -3030,8 +3030,8 @@ EXAMPLES
|
|
|
3030
3030
|
emailr inboxes list
|
|
3031
3031
|
|
|
3032
3032
|
# Get JSON output
|
|
3033
|
-
emailr inboxes list --format json`).option("--format <format>","Output format: json | table","table").action(async
|
|
3034
|
-
Total: ${
|
|
3033
|
+
emailr inboxes list --format json`).option("--format <format>","Output format: json | table","table").action(async a=>{try{let e=d(),s=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).inboxes.list();if(a.format==="json")m(s,"json");else {if(s.length===0){console.log("No inboxes found.");return}let t=s.map(o=>({ID:o.id,Name:o.name,"From Address":o.from_address,"Reply To":o.reply_to,"Inbound Address":o.inbound_address,Created:new Date(o.created_at).toLocaleDateString()}));m(t,"table"),console.log(`
|
|
3034
|
+
Total: ${s.length}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list inboxes"),process.exit(1);}}),r.command("get <inbox_id>").description(`Get an inbox by ID
|
|
3035
3035
|
|
|
3036
3036
|
USAGE
|
|
3037
3037
|
emailr inboxes get <inbox_id> [options]
|
|
@@ -3050,7 +3050,7 @@ EXAMPLES
|
|
|
3050
3050
|
emailr inboxes get inb_abc123
|
|
3051
3051
|
|
|
3052
3052
|
# Get JSON output
|
|
3053
|
-
emailr inboxes get inb_abc123 --format json`).option("--format <format>","Output format: json | table","table").action(async(
|
|
3053
|
+
emailr inboxes get inb_abc123 --format json`).option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).inboxes.get(a);e.format==="json"?m(t,"json"):m({ID:t.id,Name:t.name,Username:t.username,Domain:t.domain,"From Address":t.from_address,"Reply To":t.reply_to,"Inbound Address":t.inbound_address,Created:t.created_at,Updated:t.updated_at},"table");}catch(i){l(i instanceof Error?i.message:"Failed to get inbox"),process.exit(1);}}),r.command("update <inbox_id>").description(`Update an inbox
|
|
3054
3054
|
|
|
3055
3055
|
USAGE
|
|
3056
3056
|
emailr inboxes update <inbox_id> --name <name> [options]
|
|
@@ -3077,7 +3077,7 @@ EXAMPLES
|
|
|
3077
3077
|
emailr inboxes update inb_abc123 --reply-to none
|
|
3078
3078
|
|
|
3079
3079
|
# Get JSON output
|
|
3080
|
-
emailr inboxes update inb_abc123 --name "New Name" --format json`).option("--name <name>","New inbox display name").option("--reply-to <email>",'New reply-to address (use "none" to reset to default)').option("--format <format>","Output format: json | table","table").action(async(
|
|
3080
|
+
emailr inboxes update inb_abc123 --name "New Name" --format json`).option("--name <name>","New inbox display name").option("--reply-to <email>",'New reply-to address (use "none" to reset to default)').option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{!e.name&&!e.replyTo&&(l("No update fields specified. Use --name or --reply-to to update the inbox."),process.exit(1));let i=d(),s=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),t={};e.name&&(t.name=e.name),e.replyTo&&(t.reply_to=e.replyTo==="none"?null:e.replyTo);let o=await s.inboxes.update(a,t);e.format==="json"?m(o,"json"):(f(`Inbox updated: ${o.id}`),m({ID:o.id,Name:o.name,"From Address":o.from_address,"Reply To":o.reply_to,"Inbound Address":o.inbound_address,Updated:o.updated_at},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to update inbox"),process.exit(1);}}),r.command("delete <inbox_id>").description(`Delete an inbox
|
|
3081
3081
|
|
|
3082
3082
|
USAGE
|
|
3083
3083
|
emailr inboxes delete <inbox_id> [options]
|
|
@@ -3100,7 +3100,7 @@ EXAMPLES
|
|
|
3100
3100
|
emailr inboxes delete inb_abc123 --format json
|
|
3101
3101
|
|
|
3102
3102
|
WARNING
|
|
3103
|
-
This action is permanent and cannot be undone.`).option("--format <format>","Output format: json | table","table").action(async(
|
|
3103
|
+
This action is permanent and cannot be undone.`).option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{let i=d(),t=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).inboxes.delete(a);e.format==="json"?m(t,"json"):f(`Inbox deleted: ${a}`);}catch(i){l(i instanceof Error?i.message:"Failed to delete inbox"),process.exit(1);}}),r}function Je(){let r=new Command("threads").description(`Manage email threads, labels, replies, and drafts
|
|
3104
3104
|
|
|
3105
3105
|
USAGE
|
|
3106
3106
|
emailr threads <subcommand> [options]
|
|
@@ -3114,9 +3114,15 @@ SUBCOMMANDS
|
|
|
3114
3114
|
get <id> View a thread with all messages
|
|
3115
3115
|
label <id> Add or remove labels from a thread
|
|
3116
3116
|
reply <id> Reply to a thread (auto-resolves from/to/reply-to)
|
|
3117
|
+
mark-read <id> Mark all unread received emails in a thread as read
|
|
3117
3118
|
draft save <id> Save a draft reply for a thread
|
|
3118
3119
|
draft get <id> Get the saved draft for a thread
|
|
3119
3120
|
draft delete <id> Delete the draft for a thread
|
|
3121
|
+
emails tags add Add tags to an email
|
|
3122
|
+
emails tags remove Remove tags from an email
|
|
3123
|
+
emails comments add Add a comment to an email
|
|
3124
|
+
emails comments list List comments for an email
|
|
3125
|
+
emails comments delete Delete a comment from an email
|
|
3120
3126
|
|
|
3121
3127
|
EXAMPLES
|
|
3122
3128
|
# Reply to a thread (from/to/subject auto-resolved from inbox)
|
|
@@ -3132,9 +3138,27 @@ EXAMPLES
|
|
|
3132
3138
|
emailr threads list
|
|
3133
3139
|
|
|
3134
3140
|
# Star a thread
|
|
3135
|
-
emailr threads label <thread-id> --add starred
|
|
3141
|
+
emailr threads label <thread-id> --add starred
|
|
3142
|
+
|
|
3143
|
+
# Add tags to an email
|
|
3144
|
+
emailr threads emails tags add <email-id> urgent follow-up
|
|
3145
|
+
|
|
3146
|
+
# Mark a thread as read
|
|
3147
|
+
emailr threads mark-read <thread-id>
|
|
3148
|
+
|
|
3149
|
+
# Remove tags from an email
|
|
3150
|
+
emailr threads emails tags remove <email-id> urgent
|
|
3151
|
+
|
|
3152
|
+
# Add a comment to an email
|
|
3153
|
+
emailr threads emails comments add <email-id> "Need to follow up on this"
|
|
3154
|
+
|
|
3155
|
+
# List comments for an email
|
|
3156
|
+
emailr threads emails comments list <email-id>
|
|
3157
|
+
|
|
3158
|
+
# Delete a comment from an email
|
|
3159
|
+
emailr threads emails comments delete <email-id> <comment-id>`);r.command("list").description("List threads filtered by label").option("--label <label>","Filter by label (inbox, sent, starred, spam, trash, archived, all)","inbox").option("--limit <count>","Number of threads to return","20").option("--page <number>","Page number","1").option("--search <query>","Search by subject or sender").option("--inbox-id <id>","Filter by inbox ID").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let o=d(),c=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).threads.list({label:t.label,page:parseInt(t.page),limit:Math.min(parseInt(t.limit),100),search:t.search,inbox_id:t.inboxId});if(t.format==="json")m(c,"json");else {if(!c.data||c.data.length===0){b(`No threads in ${t.label}`);return}let g=c.data.map(p=>({"Thread ID":p.thread_id.slice(0,8)+"\u2026",Subject:V(p.subject||"(no subject)",40),From:p.from_email,Messages:p.message_count,Labels:(p.labels||[]).join(", "),Updated:me(p.updated_at)}));m(g,"table"),b(`Page ${c.pagination.page} of ${c.pagination.pages} (${c.pagination.total} total)`);}}catch(o){l(o instanceof Error?o.message:"Failed to list threads"),process.exit(1);}}),r.command("get <id>").description("View a thread with all messages").option("--format <format>","Output format (json|table)","table").action(async(t,o)=>{try{let n=d(),u=await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.get(t);if(o.format==="json")m(u,"json");else {b(`Thread: ${u.subject||"(no subject)"}`),b(`Labels: ${u.labels.join(", ")||"none"}`),b(`Messages: ${u.messages.length}`),console.log("");for(let p of u.messages){let h=p.status==="received"?"\u2190 IN ":"\u2192 OUT";console.log(`${h} ${me(p.created_at)}`),console.log(` From: ${p.from_email}`),console.log(` To: ${p.to_email}`),p.cc_emails?.length&&console.log(` Cc: ${p.cc_emails.join(", ")}`),console.log(` Labels: ${p.labels.join(", ")||"none"}`);let y=p.text_content||p.html_content?.replace(/<[^>]*>/g,"")||"";y&&console.log(` ${V(y.trim(),120)}`),console.log("");}}await new Promise(p=>process.stdout.write("",()=>p())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to get thread"),process.exit(1);}}),r.command("label <id>").description("Add or remove labels from a thread").option("--add <labels>","Labels to add (comma-separated)").option("--remove <labels>","Labels to remove (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async(t,o)=>{try{!o.add&&!o.remove&&(l("Specify --add and/or --remove labels"),process.exit(1));let n=d(),c=new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}),u=o.add?o.add.split(",").map(y=>y.trim()):void 0,g=o.remove?o.remove.split(",").map(y=>y.trim()):void 0,p=await c.threads.updateLabels(t,{add:u,remove:g});o.format==="json"?m(p,"json"):(f(`Updated ${p.updated} email(s) in thread`),u&&b(`Added: ${u.join(", ")}`),g&&b(`Removed: ${g.join(", ")}`)),await new Promise(y=>process.stdout.write("",()=>y())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to update labels"),process.exit(1);}}),r.command("reply <id>").description("Reply to a thread (from/to/subject auto-resolved from inbox)").option("--html <content>","HTML content for the reply").option("--text <content>","Plain text content for the reply").option("--to <email>","Override recipient (auto-resolved if omitted)").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--from <email>","Override sender (auto-resolved from inbox if omitted)").option("--from-name <name>","Override sender name (auto-resolved from inbox if omitted)").option("--reply-to <email>","Override Reply-To (auto-resolved from inbox if omitted)").option("--format <format>","Output format (json|table)","table").action(async(t,o)=>{try{!o.html&&!o.text&&(l("Provide --html or --text for the reply body"),process.exit(1));let n=d(),c=new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}),u={};o.html&&(u.html=o.html),o.text&&(u.text=o.text),o.to&&(u.to=o.to),o.cc&&(u.cc=o.cc.split(",").map(h=>h.trim())),o.bcc&&(u.bcc=o.bcc.split(",").map(h=>h.trim())),o.from&&(u.from=o.from),o.fromName&&(u.from_name=o.fromName),o.replyTo&&(u.reply_to_email=o.replyTo);let g=await c.threads.reply(t,u);o.format==="json"?m(g,"json"):(f(`Reply sent to ${g.recipients} recipient(s)`),b(`Message ID: ${g.message_id}`)),await new Promise(h=>process.stdout.write("",()=>h())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to send reply"),process.exit(1);}});let a=r.command("draft").description("Manage draft replies for threads");a.command("save <id>").description("Save a draft reply for a thread").option("--html <content>","HTML content").option("--text <content>","Plain text content").option("--to <email>","Recipient").option("--cc <emails>","CC recipients").option("--bcc <emails>","BCC recipients").option("--from <email>","Sender").option("--reply-to <email>","Reply-To address").option("--format <format>","Output format (json|table)","table").action(async(t,o)=>{try{let n=d(),c=new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}),u={};o.html&&(u.html=o.html),o.text&&(u.text=o.text),o.to&&(u.to=o.to),o.cc&&(u.cc=o.cc),o.bcc&&(u.bcc=o.bcc),o.from&&(u.from=o.from),o.replyTo&&(u.reply_to_email=o.replyTo);let g=await c.threads.saveDraft(t,u);o.format==="json"?m(g,"json"):(f(`Draft saved for thread ${t}`),b(`Draft ID: ${g.id}`)),await new Promise(h=>process.stdout.write("",()=>h())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to save draft"),process.exit(1);}}),a.command("get <id>").description("Get the saved draft for a thread").option("--format <format>","Output format (json|table)","table").action(async(t,o)=>{try{let n=d(),u=await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.getDraft(t);o.format==="json"?m(u,"json"):(b(`Draft for thread: ${t}`),b(`To: ${u.to_email||"(auto)"}`),b(`From: ${u.from_email||"(auto)"}`),b(`Subject: ${u.subject||"(auto)"}`),u.html_content&&console.log(`
|
|
3136
3160
|
Content:
|
|
3137
|
-
${
|
|
3161
|
+
${V(u.html_content.replace(/<[^>]*>/g,""),200)}`)),await new Promise(p=>process.stdout.write("",()=>p())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"No draft found"),process.exit(1);}}),a.command("delete <id>").description("Delete the draft for a thread").option("--format <format>","Output format (json|table)","table").action(async(t,o)=>{try{let n=d();await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.deleteDraft(t),o.format==="json"?m({success:!0},"json"):f(`Draft deleted for thread ${t}`),await new Promise(g=>process.stdout.write("",()=>g())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to delete draft"),process.exit(1);}}),r.command("mark-read <threadId>").description("Mark all unread received emails in a thread as read").option("--format <format>","Output format (json|table)","table").action(async(t,o)=>{try{let n=d(),u=await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.markAsRead(t);o.format==="json"?m(u,"json"):f(`Marked ${u.updated} email(s) as read in thread ${t}`),await new Promise(p=>process.stdout.write("",()=>p())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to mark thread as read"),process.exit(1);}});let e=r.command("emails").description("Manage individual emails within threads"),i=e.command("tags").description("Manage tags on individual emails");i.command("add <emailId> <tags...>").description("Add tags to an email").option("--format <format>","Output format (json|table)","table").action(async(t,o,n)=>{try{let c=d(),g=await new Emailr({apiKey:c.apiKey,baseUrl:c.baseUrl}).threads.addEmailTags(t,o);n.format==="json"?m(g,"json"):(f(`Added ${o.length} tag(s) to email ${t}`),b(`Tags: ${g.tags.join(", ")}`)),await new Promise(h=>process.stdout.write("",()=>h())),process.exit(0);}catch(c){l(c instanceof Error?c.message:"Failed to add tags"),process.exit(1);}}),i.command("remove <emailId> <tags...>").description("Remove tags from an email").option("--format <format>","Output format (json|table)","table").action(async(t,o,n)=>{try{let c=d(),g=await new Emailr({apiKey:c.apiKey,baseUrl:c.baseUrl}).threads.removeEmailTags(t,o);n.format==="json"?m(g,"json"):(f(`Removed ${o.length} tag(s) from email ${t}`),b(`Tags: ${g.tags.join(", ")}`)),await new Promise(h=>process.stdout.write("",()=>h())),process.exit(0);}catch(c){l(c instanceof Error?c.message:"Failed to remove tags"),process.exit(1);}});let s=e.command("comments").description("Manage comments on individual emails");return s.command("add <emailId> <content>").description("Add a comment to an email").option("--format <format>","Output format (json|table)","table").action(async(t,o,n)=>{try{let c=d(),g=await new Emailr({apiKey:c.apiKey,baseUrl:c.baseUrl}).threads.addEmailComment(t,o);n.format==="json"?m(g,"json"):(f(`Comment added to email ${t}`),b(`Comment ID: ${g.id}`)),await new Promise(h=>process.stdout.write("",()=>h())),process.exit(0);}catch(c){l(c instanceof Error?c.message:"Failed to add comment"),process.exit(1);}}),s.command("list <emailId>").description("List comments for an email").option("--format <format>","Output format (json|table)","table").action(async(t,o)=>{try{let n=d(),u=await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.listEmailComments(t);if(o.format==="json")m(u,"json");else {if(!u.data||u.data.length===0){b(`No comments for email ${t}`);return}let p=u.data.map(h=>({"Comment ID":h.id.slice(0,8)+"\u2026",Content:V(h.content,60),"Created At":me(h.created_at)}));m(p,"table"),b(`${u.data.length} comment(s)`);}await new Promise(p=>process.stdout.write("",()=>p())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to list comments"),process.exit(1);}}),s.command("delete <emailId> <commentId>").description("Delete a comment from an email").option("--format <format>","Output format (json|table)","table").action(async(t,o,n)=>{try{let c=d();await new Emailr({apiKey:c.apiKey,baseUrl:c.baseUrl}).threads.deleteEmailComment(t,o),n.format==="json"?m({success:!0},"json"):f(`Comment ${o} deleted from email ${t}`),await new Promise(p=>process.stdout.write("",()=>p())),process.exit(0);}catch(c){l(c instanceof Error?c.message:"Failed to delete comment"),process.exit(1);}}),r}function V(r,a){return r.length<=a?r:r.slice(0,a-1)+"\u2026"}function me(r){return new Date(r).toLocaleString()}var Ft=`# Emailr CLI \u2014 Agent Skill Guide
|
|
3138
3162
|
|
|
3139
3163
|
## Setup
|
|
3140
3164
|
|
|
@@ -3188,6 +3212,24 @@ emailr threads draft get <thread-id>
|
|
|
3188
3212
|
emailr threads draft delete <thread-id>
|
|
3189
3213
|
\`\`\`
|
|
3190
3214
|
|
|
3215
|
+
#### Mark Thread as Read
|
|
3216
|
+
\`\`\`bash
|
|
3217
|
+
emailr threads mark-read <thread-id>
|
|
3218
|
+
\`\`\`
|
|
3219
|
+
|
|
3220
|
+
#### Email Tags
|
|
3221
|
+
\`\`\`bash
|
|
3222
|
+
emailr threads emails tags add <email-id> urgent follow-up
|
|
3223
|
+
emailr threads emails tags remove <email-id> urgent
|
|
3224
|
+
\`\`\`
|
|
3225
|
+
|
|
3226
|
+
#### Email Comments
|
|
3227
|
+
\`\`\`bash
|
|
3228
|
+
emailr threads emails comments add <email-id> "Need to follow up"
|
|
3229
|
+
emailr threads emails comments list <email-id>
|
|
3230
|
+
emailr threads emails comments delete <email-id> <comment-id>
|
|
3231
|
+
\`\`\`
|
|
3232
|
+
|
|
3191
3233
|
### inbox \u2014 View and manage inbound emails
|
|
3192
3234
|
\`\`\`bash
|
|
3193
3235
|
emailr inbox list # list received emails
|
|
@@ -3361,7 +3403,7 @@ EXAMPLES
|
|
|
3361
3403
|
emailr skill > emailr-skill.md
|
|
3362
3404
|
|
|
3363
3405
|
# Feed to an AI agent context
|
|
3364
|
-
emailr skill | pbcopy`).action(()=>{process.stdout.write(Ft);})}var
|
|
3406
|
+
emailr skill | pbcopy`).action(()=>{process.stdout.write(Ft);})}var S=new Command;S.name("emailr").description(`Emailr CLI - Send emails and manage your email infrastructure
|
|
3365
3407
|
|
|
3366
3408
|
USAGE
|
|
3367
3409
|
emailr <command> [subcommand] [options]
|
|
@@ -3476,4 +3518,4 @@ AGENTIC WORKFLOW
|
|
|
3476
3518
|
|
|
3477
3519
|
MORE INFORMATION
|
|
3478
3520
|
Run 'emailr <command> --help' for detailed help on any command.
|
|
3479
|
-
Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.12.
|
|
3521
|
+
Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.12.2");S.addCommand(be());S.addCommand($e());S.addCommand(qe());S.addCommand(Je());S.addCommand(he());S.addCommand(Ce());S.addCommand(Ne());S.addCommand(Pe());S.addCommand(Ae());S.addCommand(Re());S.addCommand(Ue());S.addCommand(Le());S.addCommand(Ge());S.addCommand(Be());S.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emailr-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Command-line interface for the Emailr email API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,20 +10,11 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "tsup",
|
|
15
|
-
"build:standalone": "npm run build && node scripts/build-standalone.js",
|
|
16
|
-
"dev": "tsup --watch",
|
|
17
|
-
"typecheck": "tsc --noEmit",
|
|
18
|
-
"test": "vitest run",
|
|
19
|
-
"test:watch": "vitest",
|
|
20
|
-
"prepublishOnly": "npm run build"
|
|
21
|
-
},
|
|
22
13
|
"dependencies": {
|
|
23
14
|
"chalk": "^5.3.0",
|
|
24
15
|
"cli-table3": "^0.6.3",
|
|
25
16
|
"commander": "^12.0.0",
|
|
26
|
-
"emailr": "^1.
|
|
17
|
+
"emailr": "^1.8.0",
|
|
27
18
|
"open": "^11.0.0"
|
|
28
19
|
},
|
|
29
20
|
"devDependencies": {
|
|
@@ -59,5 +50,13 @@
|
|
|
59
50
|
},
|
|
60
51
|
"publishConfig": {
|
|
61
52
|
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"build:standalone": "npm run build && node scripts/build-standalone.js",
|
|
57
|
+
"dev": "tsup --watch",
|
|
58
|
+
"typecheck": "tsc --noEmit",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:watch": "vitest"
|
|
62
61
|
}
|
|
63
|
-
}
|
|
62
|
+
}
|