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.
Files changed (2) hide show
  1. package/dist/index.js +19 -19
  2. 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)):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
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 he(){let r=new Command("contacts").description(`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 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'),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=`
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
- `,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(`
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'),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=`
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
- `,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(`
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 h=`http://127.0.0.1:${await te(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1131
- Live Preview: ${h}`),console.log(`File: ${e}`),console.log(`
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(h);}catch{}process.on("SIGINT",async()=>{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(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",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
- Live Preview: ${g}`),console.log(`File: ${e}`),console.log(`
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(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
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("--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
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 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=`---
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,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
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 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
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 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(`
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
- ${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
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.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();
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.13.0",
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.8.0",
17
+ "emailr": "^1.9.0",
18
18
  "open": "^11.0.0"
19
19
  },
20
20
  "devDependencies": {