emailr-cli 1.13.0 → 1.14.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 +19 -19
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ Or create a config file at ~/.emailrrc:
|
|
|
8
8
|
{ "apiKey": "your-api-key" }
|
|
9
9
|
|
|
10
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)):
|
|
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)):Ve(r);}function Ve(r){Array.isArray(r)?Xe(r):typeof r=="object"&&r!==null?ze(r):console.log(r);}function Xe(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 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
|
|
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 ge(){let r=new Command("contacts").description(`Manage contacts
|
|
160
160
|
|
|
161
161
|
USAGE
|
|
162
162
|
emailr contacts <subcommand> [options]
|
|
@@ -935,7 +935,7 @@ Template: ${t.name}`),console.log(`Preview URL: ${w}`),e.open!==!1)try{await(awa
|
|
|
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 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'),
|
|
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'),h=j.join(u.tmpdir(),"emailr-preview.pid");try{let w=O.readFileSync(h,"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');
|
|
@@ -960,7 +960,7 @@ Stopping preview server...`),await Oe().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
|
-
`,
|
|
963
|
+
`,g=c("node",["-e",p],{detached:!0,stdio:["ignore","pipe","ignore"]}),y="";await new Promise(w=>{g.stdout?.on("data",I=>{let B=I.toString().match(/PORT:(\d+)/);B&&(y=B[1],w());}),setTimeout(w,3e3);}),g.unref();let _=`http://127.0.0.1:${y}/`;if(console.log(`
|
|
964
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
|
|
|
@@ -1007,7 +1007,7 @@ Template: ${o.name}`),console.log(`Template ID: ${o.id}`),console.log(`Live Prev
|
|
|
1007
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
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'),
|
|
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'),h=j.join(u.tmpdir(),"emailr-preview.pid");try{let w=O.readFileSync(h,"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');
|
|
@@ -1047,7 +1047,7 @@ To save changes: emailr templates update ${a} --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
|
-
`,
|
|
1050
|
+
`,g=c("node",["-e",p],{detached:!0,stdio:["ignore","pipe","ignore"]}),y="";await new Promise(w=>{g.stdout?.on("data",I=>{let ce=I.toString().match(/PORT:(\d+)/);ce&&(y=ce[1],w());}),setTimeout(w,3e3);}),g.unref();let _=`http://127.0.0.1:${y}/`;if(console.log(`
|
|
1051
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
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
|
|
@@ -1127,10 +1127,10 @@ SEE ALSO
|
|
|
1127
1127
|
<p><a href="{{unsubscribe_link}}">Unsubscribe</a></p>
|
|
1128
1128
|
</div>
|
|
1129
1129
|
</body>
|
|
1130
|
-
</html>`,O.writeFileSync(e,i,"utf-8"),console.log(`Template draft created: ${e}`),a.foreground){let
|
|
1131
|
-
Live Preview: ${
|
|
1130
|
+
</html>`,O.writeFileSync(e,i,"utf-8"),console.log(`Template draft created: ${e}`),a.foreground){let g=`http://127.0.0.1:${await te(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
|
|
1131
|
+
Live Preview: ${g}`),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 ${a.file}`),a.open!==!1)try{await(await import('open')).default(
|
|
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(g);}catch{}process.on("SIGINT",async()=>{console.log(`
|
|
1134
1134
|
|
|
1135
1135
|
Stopping live preview server...`),await ae(),console.log("Done."),console.log(`
|
|
1136
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=`
|
|
@@ -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=s("node",["-e",n],{detached:!0,stdio:["ignore","pipe","ignore"]}),u="";await new Promise(p=>{c.stdout?.on("data",
|
|
1177
|
-
Live Preview: ${
|
|
1176
|
+
`,c=s("node",["-e",n],{detached:!0,stdio:["ignore","pipe","ignore"]}),u="";await new Promise(p=>{c.stdout?.on("data",g=>{let y=g.toString().match(/PORT:(\d+)/);y&&(u=y[1],p());}),setTimeout(p,3e3);}),c.unref();let h=`http://127.0.0.1:${u}/`;if(console.log(`
|
|
1177
|
+
Live Preview: ${h}`),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 ${a.file}`),console.log(" emailr templates stop-preview"),a.open!==!1)try{await(await import('open')).default(
|
|
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(h);}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
|
|
@@ -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("--
|
|
1974
|
+
emailr segments Create and manage contact segments`).requiredOption("--name <name>","Broadcast name").requiredOption("--subject <subject>","Email subject").requiredOption("--from <email>","Sender email address").requiredOption("--template <id>","Template ID to use for email content").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("--segment <id>","Segment ID to target").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,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]
|
|
@@ -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 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
|
|
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 h=u.key||u.code;if(h){c.writeHead(200,{"Content-Type":"text/html"}),c.end(St()),e&&e({success:true,apiKey:h});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 V=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(V)).option("--no-browser","Don't automatically open the browser").action(async a=>{await jt({timeout:parseInt(a.timeout,10)||V,noBrowser:a.browser===false});})}async function jt(r){let a=De(),e=(r.timeout||V)*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||V}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
|
---
|
|
@@ -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(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,
|
|
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,g)=>new Date(p.created_at).getTime()-new Date(g.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 g=p.status==="received"?"\u2190 IN ":"\u2192 OUT",y=p.id===a?" \u25C0 (selected)":"";console.log(`${g} ${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(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
|
|
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 g=e.cc.split(",").map(y=>y.trim());u.cc=g.length===1?g[0]:g;}if(e.bcc){let g=e.bcc.split(",").map(y=>y.trim());u.bcc=g.length===1?g[0]:g;}let h=await s.emails.send(u);e.format==="json"?m(h,"json"):(f("Reply sent successfully!"),m({"Message ID":h.message_id,To:t.from_email,Subject:c,Status:h.status},"table")),await new Promise(g=>process.stdout.write("",()=>g())),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]
|
|
@@ -3156,9 +3156,9 @@ EXAMPLES
|
|
|
3156
3156
|
emailr threads emails comments list <email-id>
|
|
3157
3157
|
|
|
3158
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
|
|
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 h=c.data.map(p=>({"Thread ID":p.thread_id.slice(0,8)+"\u2026",Subject:X(p.subject||"(no subject)",40),From:p.from_email,Messages:p.message_count,Labels:(p.labels||[]).join(", "),Updated:me(p.updated_at)}));m(h,"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 g=p.status==="received"?"\u2190 IN ":"\u2192 OUT";console.log(`${g} ${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(` ${X(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,h=o.remove?o.remove.split(",").map(y=>y.trim()):void 0,p=await c.threads.updateLabels(t,{add:u,remove:h});o.format==="json"?m(p,"json"):(f(`Updated ${p.updated} email(s) in thread`),u&&b(`Added: ${u.join(", ")}`),h&&b(`Removed: ${h.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(g=>g.trim())),o.bcc&&(u.bcc=o.bcc.split(",").map(g=>g.trim())),o.from&&(u.from=o.from),o.fromName&&(u.from_name=o.fromName),o.replyTo&&(u.reply_to_email=o.replyTo);let h=await c.threads.reply(t,u);o.format==="json"?m(h,"json"):(f(`Reply sent to ${h.recipients} recipient(s)`),b(`Message ID: ${h.message_id}`)),await new Promise(g=>process.stdout.write("",()=>g())),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 h=await c.threads.saveDraft(t,u);o.format==="json"?m(h,"json"):(f(`Draft saved for thread ${t}`),b(`Draft ID: ${h.id}`)),await new Promise(g=>process.stdout.write("",()=>g())),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(`
|
|
3160
3160
|
Content:
|
|
3161
|
-
${
|
|
3161
|
+
${X(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(h=>process.stdout.write("",()=>h())),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(),h=await new Emailr({apiKey:c.apiKey,baseUrl:c.baseUrl}).threads.addEmailTags(t,o);n.format==="json"?m(h,"json"):(f(`Added ${o.length} tag(s) to email ${t}`),b(`Tags: ${h.tags.join(", ")}`)),await new Promise(g=>process.stdout.write("",()=>g())),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(),h=await new Emailr({apiKey:c.apiKey,baseUrl:c.baseUrl}).threads.removeEmailTags(t,o);n.format==="json"?m(h,"json"):(f(`Removed ${o.length} tag(s) from email ${t}`),b(`Tags: ${h.tags.join(", ")}`)),await new Promise(g=>process.stdout.write("",()=>g())),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(),h=await new Emailr({apiKey:c.apiKey,baseUrl:c.baseUrl}).threads.addEmailComment(t,o);n.format==="json"?m(h,"json"):(f(`Comment added to email ${t}`),b(`Comment ID: ${h.id}`)),await new Promise(g=>process.stdout.write("",()=>g())),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(g=>({"Comment ID":g.id.slice(0,8)+"\u2026",Content:X(g.content,60),"Created At":me(g.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 X(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
|
|
3162
3162
|
|
|
3163
3163
|
## Setup
|
|
3164
3164
|
|
|
@@ -3518,4 +3518,4 @@ AGENTIC WORKFLOW
|
|
|
3518
3518
|
|
|
3519
3519
|
MORE INFORMATION
|
|
3520
3520
|
Run 'emailr <command> --help' for detailed help on any command.
|
|
3521
|
-
Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.
|
|
3521
|
+
Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.14.0");S.addCommand(be());S.addCommand($e());S.addCommand(qe());S.addCommand(Je());S.addCommand(ge());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.14.0",
|
|
4
4
|
"description": "Command-line interface for the Emailr email API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"chalk": "^5.3.0",
|
|
15
15
|
"cli-table3": "^0.6.3",
|
|
16
16
|
"commander": "^12.0.0",
|
|
17
|
-
"emailr": "^1.
|
|
17
|
+
"emailr": "^1.9.0",
|
|
18
18
|
"open": "^11.0.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|