emailr-cli 1.8.0 → 1.9.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 +263 -79
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import {Command}from'commander';import {Emailr}from'emailr';import O,{readFileSync}from'fs';import V from'os';import _ from'path';import le from'cli-table3';import T from'chalk';import Xe from'http';import {URL}from'url';import ft from'crypto';import {spawn,execSync,exec}from'child_process';var Ge=[_.join(V.homedir(),".emailrrc"),_.join(V.homedir(),".config","emailr","config.json")];function c(){if(process.env.EMAILR_API_KEY)return {apiKey:process.env.EMAILR_API_KEY,baseUrl:process.env.EMAILR_BASE_URL,format:process.env.EMAILR_FORMAT||"table"};for(let i of Ge)if(O.existsSync(i))try{let t=O.readFileSync(i,"utf-8"),e=JSON.parse(t);if(e.apiKey)return {apiKey:e.apiKey,baseUrl:e.baseUrl,format:e.format||"table"}}catch{}throw new Error(`No API key configured.
2
+ import {Command}from'commander';import {Emailr}from'emailr';import O,{readFileSync}from'fs';import X from'os';import E from'path';import ce from'cli-table3';import T from'chalk';import tt from'http';import {URL}from'url';import St from'crypto';import {spawn,execSync,exec}from'child_process';var Be=[E.join(X.homedir(),".emailrrc"),E.join(X.homedir(),".config","emailr","config.json")];function c(){if(process.env.EMAILR_API_KEY)return {apiKey:process.env.EMAILR_API_KEY,baseUrl:process.env.EMAILR_BASE_URL,format:process.env.EMAILR_FORMAT||"table"};for(let i of Be)if(O.existsSync(i))try{let t=O.readFileSync(i,"utf-8"),e=JSON.parse(t);if(e.apiKey)return {apiKey:e.apiKey,baseUrl:e.baseUrl,format:e.format||"table"}}catch{}throw new Error(`No API key configured.
3
3
 
4
4
  Set the EMAILR_API_KEY environment variable:
5
5
  export EMAILR_API_KEY=your-api-key
@@ -7,8 +7,8 @@ Set the EMAILR_API_KEY environment variable:
7
7
  Or create a config file at ~/.emailrrc:
8
8
  { "apiKey": "your-api-key" }
9
9
 
10
- Or run: emailr config set api-key <your-api-key>`)}function N(){let i=_.join(V.homedir(),".config","emailr");return _.join(i,"config.json")}function G(i){let t=N(),e=_.dirname(t);O.existsSync(e)||O.mkdirSync(e,{recursive:true});let o={};if(O.existsSync(t))try{o=JSON.parse(O.readFileSync(t,"utf-8"));}catch{}let s={...o,...i};O.writeFileSync(t,JSON.stringify(s,null,2)+`
11
- `);}function se(i){try{return c()[i]?.toString()}catch{return}}function m(i,t="table"){t==="json"?console.log(JSON.stringify(i,null,2)):Ke(i);}function Ke(i){Array.isArray(i)?He(i):typeof i=="object"&&i!==null?qe(i):console.log(i);}function He(i){if(i.length===0){console.log(T.gray("No results"));return}let t=i[0];if(typeof t!="object"||t===null){i.forEach(s=>console.log(s));return}let e=Object.keys(t),o=new le({head:e.map(s=>T.cyan(s)),style:{head:[],border:[]}});for(let s of i){let a=e.map(r=>{let n=s[r];return me(n)});o.push(a);}console.log(o.toString());}function qe(i){let t=new le({style:{head:[],border:[]}});for(let[e,o]of Object.entries(i))t.push([T.cyan(e),me(o)]);console.log(t.toString());}function me(i){return i==null?T.gray("-"):typeof i=="boolean"?i?T.green("\u2713"):T.red("\u2717"):typeof i=="object"?JSON.stringify(i):String(i)}function p(i){console.log(T.green("\u2713"),i);}function l(i){console.error(T.red("\u2717"),i);}function I(i){console.warn(T.yellow("\u26A0"),i);}function f(i){console.log(T.blue("\u2139"),i);}function de(){return new Command("send").description(`Send an email
10
+ Or run: emailr config set api-key <your-api-key>`)}function I(){let i=E.join(X.homedir(),".config","emailr");return E.join(i,"config.json")}function G(i){let t=I(),e=E.dirname(t);O.existsSync(e)||O.mkdirSync(e,{recursive:true});let o={};if(O.existsSync(t))try{o=JSON.parse(O.readFileSync(t,"utf-8"));}catch{}let r={...o,...i};O.writeFileSync(t,JSON.stringify(r,null,2)+`
11
+ `);}function me(i){try{return c()[i]?.toString()}catch{return}}function m(i,t="table"){t==="json"?console.log(JSON.stringify(i,null,2)):Ve(i);}function Ve(i){Array.isArray(i)?Xe(i):typeof i=="object"&&i!==null?We(i):console.log(i);}function Xe(i){if(i.length===0){console.log(T.gray("No results"));return}let t=i[0];if(typeof t!="object"||t===null){i.forEach(r=>console.log(r));return}let e=Object.keys(t),o=new ce({head:e.map(r=>T.cyan(r)),style:{head:[],border:[]}});for(let r of i){let a=e.map(n=>{let s=r[n];return de(s)});o.push(a);}console.log(o.toString());}function We(i){let t=new ce({style:{head:[],border:[]}});for(let[e,o]of Object.entries(i))t.push([T.cyan(e),de(o)]);console.log(t.toString());}function de(i){return i==null?T.gray("-"):typeof i=="boolean"?i?T.green("\u2713"):T.red("\u2717"):typeof i=="object"?JSON.stringify(i):String(i)}function p(i){console.log(T.green("\u2713"),i);}function l(i){console.error(T.red("\u2717"),i);}function N(i){console.warn(T.yellow("\u26A0"),i);}function u(i){console.log(T.blue("\u2139"),i);}function ue(){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 t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s=t.to.split(",").map(n=>n.trim()),a={to:s.length===1?s[0]:s};if(t.from&&(a.from=t.from),t.subject&&(a.subject=t.subject),t.htmlFile)try{a.html=readFileSync(t.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${t.htmlFile}`),process.exit(1);}else t.html&&(a.html=t.html);if(t.textFile)try{a.text=readFileSync(t.textFile,"utf-8");}catch{l(`Failed to read text file: ${t.textFile}`),process.exit(1);}else t.text&&(a.text=t.text);if(t.template&&(a.template_id=t.template),t.templateData)try{a.template_data=JSON.parse(t.templateData);}catch{l("Invalid JSON for --template-data"),process.exit(1);}if(t.cc){let n=t.cc.split(",").map(d=>d.trim());a.cc=n.length===1?n[0]:n;}if(t.bcc){let n=t.bcc.split(",").map(d=>d.trim());a.bcc=n.length===1?n[0]:n;}t.replyTo&&(a.reply_to_email=t.replyTo),t.schedule&&(a.scheduled_at=t.schedule);let r=await o.emails.send(a);t.format==="json"?m(r,"json"):(p("Email sent successfully!"),m({"Message ID":r.message_id,Recipients:r.recipients,Status:r.status,...r.scheduled_at&&{"Scheduled At":r.scheduled_at}},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to send email"),process.exit(1);}})}function pe(){let i=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 t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r=t.to.split(",").map(s=>s.trim()),a={to:r.length===1?r[0]:r};if(t.from&&(a.from=t.from),t.subject&&(a.subject=t.subject),t.htmlFile)try{a.html=readFileSync(t.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${t.htmlFile}`),process.exit(1);}else t.html&&(a.html=t.html);if(t.textFile)try{a.text=readFileSync(t.textFile,"utf-8");}catch{l(`Failed to read text file: ${t.textFile}`),process.exit(1);}else t.text&&(a.text=t.text);if(t.template&&(a.template_id=t.template),t.templateData)try{a.template_data=JSON.parse(t.templateData);}catch{l("Invalid JSON for --template-data"),process.exit(1);}if(t.cc){let s=t.cc.split(",").map(d=>d.trim());a.cc=s.length===1?s[0]:s;}if(t.bcc){let s=t.bcc.split(",").map(d=>d.trim());a.bcc=s.length===1?s[0]:s;}t.replyTo&&(a.reply_to_email=t.replyTo),t.schedule&&(a.scheduled_at=t.schedule);let n=await o.emails.send(a);t.format==="json"?m(n,"json"):(p("Email sent successfully!"),m({"Message ID":n.message_id,Recipients:n.recipients,Status:n.status,...n.scheduled_at&&{"Scheduled At":n.scheduled_at}},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to send email"),process.exit(1);}})}function fe(){let i=new Command("contacts").description(`Manage contacts
160
160
 
161
161
  USAGE
162
162
  emailr contacts <subcommand> [options]
@@ -286,7 +286,7 @@ EXAMPLES
286
286
  emailr contacts list --format json
287
287
 
288
288
  # Combine filters with pagination
289
- emailr contacts list --subscribed --limit 100 --offset 0`).option("--limit <number>","Number of contacts to return","20").option("--offset <number>","Offset for pagination","0").option("--subscribed","Only show subscribed contacts").option("--unsubscribed","Only show unsubscribed contacts").option("--search <query>","Search by email, first name, or last name").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={limit:parseInt(t.limit,10),offset:parseInt(t.offset,10)};if(t.subscribed&&(s.subscribed=!0),t.unsubscribed&&(s.subscribed=!1),t.search&&(s.search=t.search),t.tags){let r=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean);r.length>0&&(s.tags=r.join(","));}let a=await o.contacts.list(s);if(t.format==="json")m(a,"json");else {let r=a.contacts.map(n=>({ID:n.id,Email:n.email,Name:[n.first_name,n.last_name].filter(Boolean).join(" ")||"-",Subscribed:n.subscribed,Tags:n.tags?.join(", ")||"-",Created:n.created_at}));m(r,"table"),console.log(`
289
+ emailr contacts list --subscribed --limit 100 --offset 0`).option("--limit <number>","Number of contacts to return","20").option("--offset <number>","Offset for pagination","0").option("--subscribed","Only show subscribed contacts").option("--unsubscribed","Only show unsubscribed contacts").option("--search <query>","Search by email, first name, or last name").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={limit:parseInt(t.limit,10),offset:parseInt(t.offset,10)};if(t.subscribed&&(r.subscribed=!0),t.unsubscribed&&(r.subscribed=!1),t.search&&(r.search=t.search),t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let a=await o.contacts.list(r);if(t.format==="json")m(a,"json");else {let n=a.contacts.map(s=>({ID:s.id,Email:s.email,Name:[s.first_name,s.last_name].filter(Boolean).join(" ")||"-",Subscribed:s.subscribed,Tags:s.tags?.join(", ")||"-",Created:s.created_at}));m(n,"table"),console.log(`
290
290
  Total: ${a.total}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list contacts"),process.exit(1);}}),i.command("get <contact_id>").description(`Get a contact by ID
291
291
 
292
292
  USAGE
@@ -357,7 +357,7 @@ EXAMPLES
357
357
  emailr contacts create --email "user@example.com" --subscribed false
358
358
 
359
359
  # Get JSON output for scripting
360
- emailr contacts create --email "user@example.com" --format json`).requiredOption("--email <email>","Contact email address").option("--first-name <name>","First name").option("--last-name <name>","Last name").option("--subscribed","Mark as subscribed (default: true)").option("--metadata <json>","Metadata as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={email:t.email};if(t.firstName&&(s.first_name=t.firstName),t.lastName&&(s.last_name=t.lastName),t.subscribed!==void 0&&(s.subscribed=t.subscribed),t.metadata)try{s.metadata=JSON.parse(t.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}t.tags&&(s.tags=t.tags.split(",").map(r=>r.trim().toLowerCase()).filter(Boolean));let a=await o.contacts.create(s);t.format==="json"?m(a,"json"):(p(`Contact created: ${a.id}`),m(a,"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create contact"),process.exit(1);}}),i.command("update <contact_id>").description(`Update a contact
360
+ emailr contacts create --email "user@example.com" --format json`).requiredOption("--email <email>","Contact email address").option("--first-name <name>","First name").option("--last-name <name>","Last name").option("--subscribed","Mark as subscribed (default: true)").option("--metadata <json>","Metadata as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={email:t.email};if(t.firstName&&(r.first_name=t.firstName),t.lastName&&(r.last_name=t.lastName),t.subscribed!==void 0&&(r.subscribed=t.subscribed),t.metadata)try{r.metadata=JSON.parse(t.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}t.tags&&(r.tags=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let a=await o.contacts.create(r);t.format==="json"?m(a,"json"):(p(`Contact created: ${a.id}`),m(a,"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create contact"),process.exit(1);}}),i.command("update <contact_id>").description(`Update a contact
361
361
 
362
362
  USAGE
363
363
  emailr contacts update <contact_id> [options]
@@ -406,7 +406,7 @@ EXAMPLES
406
406
  emailr contacts update con_abc123 --metadata '{"plan": "enterprise", "upgraded": true}'
407
407
 
408
408
  # Get JSON output
409
- emailr contacts update con_abc123 --first-name "Jane" --format json`).option("--email <email>","New email address").option("--first-name <name>","First name").option("--last-name <name>","Last name").option("--subscribed","Mark as subscribed").option("--unsubscribed","Mark as unsubscribed").option("--metadata <json>","Metadata as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.email&&(a.email=e.email),e.firstName&&(a.first_name=e.firstName),e.lastName&&(a.last_name=e.lastName),e.subscribed&&(a.subscribed=!0),e.unsubscribed&&(a.subscribed=!1),e.metadata)try{a.metadata=JSON.parse(e.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}e.tags&&(a.tags=e.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let r=await s.contacts.update(t,a);e.format==="json"?m(r,"json"):(p(`Contact updated: ${r.id}`),m(r,"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update contact"),process.exit(1);}}),i.command("delete <contact_id>").description(`Delete a contact
409
+ emailr contacts update con_abc123 --first-name "Jane" --format json`).option("--email <email>","New email address").option("--first-name <name>","First name").option("--last-name <name>","Last name").option("--subscribed","Mark as subscribed").option("--unsubscribed","Mark as unsubscribed").option("--metadata <json>","Metadata as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.email&&(a.email=e.email),e.firstName&&(a.first_name=e.firstName),e.lastName&&(a.last_name=e.lastName),e.subscribed&&(a.subscribed=!0),e.unsubscribed&&(a.subscribed=!1),e.metadata)try{a.metadata=JSON.parse(e.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}e.tags&&(a.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.contacts.update(t,a);e.format==="json"?m(n,"json"):(p(`Contact updated: ${n.id}`),m(n,"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update contact"),process.exit(1);}}),i.command("delete <contact_id>").description(`Delete a contact
410
410
 
411
411
  USAGE
412
412
  emailr contacts delete <contact_id>
@@ -423,7 +423,7 @@ EXAMPLES
423
423
  emailr contacts delete con_abc123
424
424
 
425
425
  WARNING
426
- This action is permanent and cannot be undone.`).action(async t=>{try{let e=c();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).contacts.delete(t),p(`Contact deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete contact"),process.exit(1);}}),i}function fe(){return _.join(V.homedir(),".config","emailr","templates")}function We(){let i=fe();O.existsSync(i)||O.mkdirSync(i,{recursive:true});}function W(i){return _.join(fe(),`${i}.html`)}function X(i,t){We();let e=W(i);O.writeFileSync(e,t,"utf-8");}function be(i){let t=W(i);return O.existsSync(t)?O.readFileSync(t,"utf-8"):null}function he(i){let t=W(i);return O.existsSync(t)}var v=null,P=null,H=false;function we(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function ze(i){return i===null||i.trim()===""}function Ye(i){return `<!DOCTYPE html>
426
+ This action is permanent and cannot be undone.`).action(async t=>{try{let e=c();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).contacts.delete(t),p(`Contact deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete contact"),process.exit(1);}}),i}function he(){return E.join(X.homedir(),".config","emailr","templates")}function et(){let i=he();O.existsSync(i)||O.mkdirSync(i,{recursive:true});}function W(i){return E.join(he(),`${i}.html`)}function z(i,t){et();let e=W(i);O.writeFileSync(e,t,"utf-8");}function ge(i){let t=W(i);return O.existsSync(t)?O.readFileSync(t,"utf-8"):null}function we(i){let t=W(i);return O.existsSync(t)}var v=null,P=null,q=false;function Se(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function at(i){return i===null||i.trim()===""}function ot(i){return `<!DOCTYPE html>
427
427
  <html lang="en">
428
428
  <head>
429
429
  <meta charset="UTF-8">
@@ -489,14 +489,14 @@ WARNING
489
489
  <div class="icon">\u{1F4ED}</div>
490
490
  <h1>No Content Available</h1>
491
491
  <p>This template exists but has no HTML content to display.</p>
492
- <div class="template-id">${we(i)}</div>
492
+ <div class="template-id">${Se(i)}</div>
493
493
  <div class="hint">
494
494
  <p><strong>To add content:</strong></p>
495
495
  <p>Update the template using the CLI with the <code>--html</code> option or provide HTML content when creating the template.</p>
496
496
  </div>
497
497
  </div>
498
498
  </body>
499
- </html>`}function ge(i){return `<!DOCTYPE html>
499
+ </html>`}function ye(i){return `<!DOCTYPE html>
500
500
  <html lang="en">
501
501
  <head>
502
502
  <meta charset="UTF-8">
@@ -548,11 +548,11 @@ WARNING
548
548
  <div class="icon">404</div>
549
549
  <h1>Template Not Found</h1>
550
550
  <p>The requested template could not be found in local storage.</p>
551
- <div class="template-id">${we(i)}</div>
551
+ <div class="template-id">${Se(i)}</div>
552
552
  <p style="margin-top: 1rem;">Try creating or retrieving the template first using the CLI.</p>
553
553
  </div>
554
554
  </body>
555
- </html>`}function Ze(i){let t=i.match(/^\/preview\/([^/]+)$/);return t?t[1]:null}function Qe(){return (i,t)=>{if(i.method!=="GET"){t.writeHead(405,{"Content-Type":"text/plain"}),t.end("Method Not Allowed");return}let e=i.url||"/",o=Ze(e);if(!o){t.writeHead(404,{"Content-Type":"text/plain"}),t.end("Not Found");return}if(!he(o)){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(ge(o));return}let s=be(o);if(s===null){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(ge(o));return}if(ze(s)){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(Ye(o));return}t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(s);}}var ye={async start(){return H&&P!==null?P:new Promise((i,t)=>{v=Xe.createServer(Qe()),v.listen(0,"127.0.0.1",()=>{let e=v.address();e&&typeof e=="object"?(P=e.port,H=true,v.unref(),i(P)):t(new Error("Failed to get server address"));}),v.on("error",e=>{H=false,P=null,v=null,t(new Error(`Failed to start preview server: ${e.message}`));});})},getPort(){return P},isRunning(){return H},async stop(){if(v)return new Promise(i=>{v.close(()=>{v=null,P=null,H=false,i();});})}};function Se(){return ye}async function z(i){try{return `http://127.0.0.1:${await ye.start()}/preview/${i}`}catch{return null}}function ve(){v&&v.ref();}var k=null,Y=null,$=null,M=[],Oe=`
555
+ </html>`}function it(i){let t=i.match(/^\/preview\/([^/]+)$/);return t?t[1]:null}function nt(){return (i,t)=>{if(i.method!=="GET"){t.writeHead(405,{"Content-Type":"text/plain"}),t.end("Method Not Allowed");return}let e=i.url||"/",o=it(e);if(!o){t.writeHead(404,{"Content-Type":"text/plain"}),t.end("Not Found");return}if(!we(o)){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(ye(o));return}let r=ge(o);if(r===null){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(ye(o));return}if(at(r)){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(ot(o));return}t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(r);}}var ve={async start(){return q&&P!==null?P:new Promise((i,t)=>{v=tt.createServer(nt()),v.listen(0,"127.0.0.1",()=>{let e=v.address();e&&typeof e=="object"?(P=e.port,q=true,v.unref(),i(P)):t(new Error("Failed to get server address"));}),v.on("error",e=>{q=false,P=null,v=null,t(new Error(`Failed to start preview server: ${e.message}`));});})},getPort(){return P},isRunning(){return q},async stop(){if(v)return new Promise(i=>{v.close(()=>{v=null,P=null,q=false,i();});})}};function Te(){return ve}async function Y(i){try{return `http://127.0.0.1:${await ve.start()}/preview/${i}`}catch{return null}}function Oe(){v&&v.ref();}var A=null,Z=null,B=null,M=[],Ee=`
556
556
  <script>
557
557
  (function() {
558
558
  const evtSource = new EventSource('/__live-reload');
@@ -566,11 +566,11 @@ WARNING
566
566
  };
567
567
  })();
568
568
  </script>
569
- `;function at(i){return i.includes("</body>")?i.replace("</body>",`${Oe}</body>`):i+Oe}function ot(){M.forEach(i=>{try{i.write(`data: reload
569
+ `;function lt(i){return i.includes("</body>")?i.replace("</body>",`${Ee}</body>`):i+Ee}function mt(){M.forEach(i=>{try{i.write(`data: reload
570
570
 
571
- `);}catch{}});}async function Z(i,t){let e=_.resolve(i);return new Promise((o,s)=>{k=Xe.createServer((a,r)=>{if(a.url==="/__live-reload"){r.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),r.write(`data: connected
571
+ `);}catch{}});}async function Q(i,t){let e=E.resolve(i);return new Promise((o,r)=>{A=tt.createServer((a,n)=>{if(a.url==="/__live-reload"){n.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),n.write(`data: connected
572
572
 
573
- `),M.push(r),a.on("close",()=>{M=M.filter(n=>n!==r);});return}if(a.method==="GET"){try{let n=O.readFileSync(e,"utf-8"),d=at(n);r.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),r.end(d);}catch(n){r.writeHead(500,{"Content-Type":"text/plain"}),r.end(`Error reading file: ${n instanceof Error?n.message:String(n)}`);}return}r.writeHead(405,{"Content-Type":"text/plain"}),r.end("Method Not Allowed");}),k.listen(0,"127.0.0.1",()=>{let a=k.address();if(a&&typeof a=="object"){Y=a.port;let r=null;$=O.watch(e,n=>{n==="change"&&(r&&clearTimeout(r),r=setTimeout(()=>{ot(),t?.();},100));}),o(Y);}else s(new Error("Failed to get server address"));}),k.on("error",a=>{s(new Error(`Failed to start server: ${a.message}`));});})}async function Q(){if($&&($.close(),$=null),M.forEach(i=>{try{i.end();}catch{}}),M=[],k)return new Promise(i=>{k.close(()=>{k=null,Y=null,i();});})}async function Ee(i){try{let t=i.html_content??"";X(i.id,t);}catch(t){return I(`Could not save template for preview: ${t instanceof Error?t.message:String(t)}`),null}try{let t=await z(i.id);return t===null?(I("Could not start preview server"),null):t}catch(t){return I(`Could not generate preview URL: ${t instanceof Error?t.message:String(t)}`),null}}function _e(){let i=new Command("templates").description(`Manage email templates
573
+ `),M.push(n),a.on("close",()=>{M=M.filter(s=>s!==n);});return}if(a.method==="GET"){try{let s=O.readFileSync(e,"utf-8"),d=lt(s);n.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),n.end(d);}catch(s){n.writeHead(500,{"Content-Type":"text/plain"}),n.end(`Error reading file: ${s instanceof Error?s.message:String(s)}`);}return}n.writeHead(405,{"Content-Type":"text/plain"}),n.end("Method Not Allowed");}),A.listen(0,"127.0.0.1",()=>{let a=A.address();if(a&&typeof a=="object"){Z=a.port;let n=null;B=O.watch(e,s=>{s==="change"&&(n&&clearTimeout(n),n=setTimeout(()=>{mt(),t?.();},100));}),o(Z);}else r(new Error("Failed to get server address"));}),A.on("error",a=>{r(new Error(`Failed to start server: ${a.message}`));});})}async function ee(){if(B&&(B.close(),B=null),M.forEach(i=>{try{i.end();}catch{}}),M=[],A)return new Promise(i=>{A.close(()=>{A=null,Z=null,i();});})}async function _e(i){try{let t=i.html_content??"";z(i.id,t);}catch(t){return N(`Could not save template for preview: ${t instanceof Error?t.message:String(t)}`),null}try{let t=await Y(i.id);return t===null?(N("Could not start preview server"),null):t}catch(t){return N(`Could not generate preview URL: ${t instanceof Error?t.message:String(t)}`),null}}function je(){let i=new Command("templates").description(`Manage email templates
574
574
 
575
575
  USAGE
576
576
  emailr templates <subcommand> [options]
@@ -671,7 +671,7 @@ EXAMPLES
671
671
  emailr templates list --page 2 --limit 10
672
672
 
673
673
  # Get JSON output for scripting
674
- emailr templates list --format json`).option("--limit <count>","Number of templates to return","20").option("--page <page_number>","Page number for pagination","1").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={limit:parseInt(t.limit,10),page:parseInt(t.page,10)};if(t.tags){let r=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean);r.length>0&&(s.tags=r.join(","));}let a=await o.templates.list(s);if(t.format==="json")m(a,"json");else {let r=a.map(n=>({ID:n.id,Name:n.name,Subject:n.subject,Variables:n.variables?.join(", ")||"-",Tags:n.tags?.join(", ")||"-",Created:n.created_at}));m(r,"table"),console.log(`
674
+ emailr templates list --format json`).option("--limit <count>","Number of templates to return","20").option("--page <page_number>","Page number for pagination","1").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={limit:parseInt(t.limit,10),page:parseInt(t.page,10)};if(t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let a=await o.templates.list(r);if(t.format==="json")m(a,"json");else {let n=a.map(s=>({ID:s.id,Name:s.name,Subject:s.subject,Variables:s.variables?.join(", ")||"-",Tags:s.tags?.join(", ")||"-",Created:s.created_at}));m(n,"table"),console.log(`
675
675
  Total: ${a.length}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list templates"),process.exit(1);}}),i.command("get <template_id>").description(`Get a template by ID
676
676
 
677
677
  USAGE
@@ -700,7 +700,7 @@ EXAMPLES
700
700
  emailr templates get tpl_abc123 --format json
701
701
 
702
702
  # Pipe JSON to jq for processing
703
- emailr templates get tpl_abc123 --format json | jq '.html_content'`).option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).templates.get(t),r=await Ee({id:a.id,html_content:a.html_content??void 0}),n=a.preview_html?`${o.baseUrl}/preview/${a.id}`:null;if(e.format==="json")m({...a,preview_url:n??r},"json");else {let d={ID:a.id,Name:a.name,Subject:a.subject,Variables:a.variables?.join(", ")||"-",Created:a.created_at};n?d["Preview URL"]=n:r&&(d["Local Preview"]=r),m(d,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to get template"),process.exit(1);}}),i.command("fetch <template_id>").description(`Download template HTML to file or stdout
703
+ emailr templates get tpl_abc123 --format json | jq '.html_content'`).option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).templates.get(t),n=await _e({id:a.id,html_content:a.html_content??void 0}),s=a.preview_html?`${o.baseUrl}/preview/${a.id}`:null;if(e.format==="json")m({...a,preview_url:s??n},"json");else {let d={ID:a.id,Name:a.name,Subject:a.subject,Variables:a.variables?.join(", ")||"-",Created:a.created_at};s?d["Preview URL"]=s:n&&(d["Local Preview"]=n),m(d,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to get template"),process.exit(1);}}),i.command("fetch <template_id>").description(`Download template HTML to file or stdout
704
704
 
705
705
  USAGE
706
706
  emailr templates fetch <template_id> [options]
@@ -735,7 +735,7 @@ AGENTIC WORKFLOW
735
735
  1. Fetch: emailr templates fetch <id> --output template.html
736
736
  2. Edit locally or with AI assistance
737
737
  3. Push preview: emailr templates push-preview <id> --html-file template.html`).option("--output <file_path>","Write HTML to file (default: stdout)").option("--preview","Fetch preview HTML instead of published HTML").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).templates.fetch(t,{preview:e.preview??!1});if(a||(e.preview?(l("No preview HTML exists for this template"),console.log(`
738
- Run 'emailr templates push-preview <id> --html-file <path>' to create a preview.`)):l("Template has no HTML content"),process.exit(1)),e.output){let r=_.resolve(e.output);O.writeFileSync(r,a,"utf-8"),p(`HTML saved to: ${r}`);}else console.log(a);}catch(o){l(o instanceof Error?o.message:"Failed to fetch template"),process.exit(1);}}),i.command("push-preview <template_id>").description(`Upload HTML to template's preview for sharing with AI agents
738
+ Run 'emailr templates push-preview <id> --html-file <path>' to create a preview.`)):l("Template has no HTML content"),process.exit(1)),e.output){let n=E.resolve(e.output);O.writeFileSync(n,a,"utf-8"),p(`HTML saved to: ${n}`);}else console.log(a);}catch(o){l(o instanceof Error?o.message:"Failed to fetch template"),process.exit(1);}}),i.command("push-preview <template_id>").description(`Upload HTML to template's preview for sharing with AI agents
739
739
 
740
740
  USAGE
741
741
  emailr templates push-preview <template_id> --html-file <file_path>
@@ -779,7 +779,7 @@ AGENTIC WORKFLOW
779
779
  3. Push preview: emailr templates push-preview <id> --html-file template.html
780
780
  4. Share the preview URL with your AI agent for feedback
781
781
  5. Repeat steps 2-4 until satisfied`).option("--html-file <file_path>","Read HTML content from file").option("--html <html_content>","Inline HTML content").option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{!e.htmlFile&&!e.html&&(l("Either --html-file or --html is required"),console.log(`
782
- Usage:`),console.log(" emailr templates push-preview <id> --html-file <path>"),console.log(" emailr templates push-preview <id> --html <content>"),process.exit(1)),e.htmlFile&&e.html&&(l("Cannot use both --html-file and --html. Choose one."),process.exit(1));let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a;if(e.htmlFile){let d=_.resolve(e.htmlFile);try{a=O.readFileSync(d,"utf-8");}catch{l(`Failed to read file: ${d}`),process.exit(1);}}else a=e.html;let r=await s.templates.pushPreview(t,a),n="updated";e.format==="json"?m({template_id:t,preview_url:r.preview_url,status:n},"json"):(p("Preview uploaded successfully"),m({"Template ID":t,"Preview URL":r.preview_url,Status:n},"table"),console.log(`
782
+ Usage:`),console.log(" emailr templates push-preview <id> --html-file <path>"),console.log(" emailr templates push-preview <id> --html <content>"),process.exit(1)),e.htmlFile&&e.html&&(l("Cannot use both --html-file and --html. Choose one."),process.exit(1));let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a;if(e.htmlFile){let d=E.resolve(e.htmlFile);try{a=O.readFileSync(d,"utf-8");}catch{l(`Failed to read file: ${d}`),process.exit(1);}}else a=e.html;let n=await r.templates.pushPreview(t,a),s="updated";e.format==="json"?m({template_id:t,preview_url:n.preview_url,status:s},"json"):(p("Preview uploaded successfully"),m({"Template ID":t,"Preview URL":n.preview_url,Status:s},"table"),console.log(`
783
783
  Share this URL with your AI agent for feedback.`));}catch(o){l(o instanceof Error?o.message:"Failed to push preview"),process.exit(1);}}),i.command("create").description(`Create a new email template
784
784
 
785
785
  USAGE
@@ -801,6 +801,7 @@ OPTIONS
801
801
  --from-name <name> Sender display name (e.g. "Acme Inc")
802
802
  --reply-to <email_address> Default reply-to email address
803
803
  --preview-text <text> Preview text shown in email clients
804
+ --inbox-id <inbox_id> Inbox ID for sender identity defaults
804
805
  --tags <tags> Comma-separated tags
805
806
  --format <format> Output format: json | table (default: table)
806
807
 
@@ -827,7 +828,7 @@ EXAMPLES
827
828
 
828
829
  TIP
829
830
  For live preview while building, use "emailr templates draft" first.
830
- It creates a local file with hot-reload preview, then use this command to save.`).requiredOption("--name <template_name>","Template name").requiredOption("--subject <subject_line>","Email subject line").option("--html <html_content>","Inline HTML content").option("--text <text_content>","Plain text content").option("--html-file <file_path>","Read HTML content from file").option("--text-file <file_path>","Read text content from file").option("--from <email_address>","Default from email address").option("--from-name <name>","Sender display name").option("--reply-to <email_address>","Default reply-to email address").option("--preview-text <text>","Preview text shown in email clients").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={name:t.name,subject:t.subject};if(t.htmlFile){let n=await import('fs');s.html_content=n.readFileSync(t.htmlFile,"utf-8");}else t.html&&(s.html_content=t.html);if(t.textFile){let n=await import('fs');s.text_content=n.readFileSync(t.textFile,"utf-8");}else t.text&&(s.text_content=t.text);t.from&&(s.from_email=t.from),t.fromName&&(s.from_name=t.fromName),t.replyTo&&(s.reply_to=t.replyTo),t.previewText&&(s.preview_text=t.previewText),t.tags&&(s.tags=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let a=await o.templates.create(s),r=await Ee({id:a.id,html_content:a.html_content??void 0});if(t.format==="json")m({...a,preview_url:r},"json");else {p(`Template created: ${a.id}`);let n={ID:a.id,Name:a.name,Subject:a.subject,Variables:a.variables?.join(", ")||"-",Tags:a.tags?.join(", ")||"-"};r&&(n["Preview URL"]=r),m(n,"table"),r&&console.log(`
831
+ It creates a local file with hot-reload preview, then use this command to save.`).requiredOption("--name <template_name>","Template name").requiredOption("--subject <subject_line>","Email subject line").option("--html <html_content>","Inline HTML content").option("--text <text_content>","Plain text content").option("--html-file <file_path>","Read HTML content from file").option("--text-file <file_path>","Read text content from file").option("--from <email_address>","Default from email address").option("--from-name <name>","Sender display name").option("--reply-to <email_address>","Default reply-to email address").option("--preview-text <text>","Preview text shown in email clients").option("--inbox-id <inbox_id>","Inbox ID for sender identity defaults").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={name:t.name,subject:t.subject};if(t.htmlFile){let s=await import('fs');r.html_content=s.readFileSync(t.htmlFile,"utf-8");}else t.html&&(r.html_content=t.html);if(t.textFile){let s=await import('fs');r.text_content=s.readFileSync(t.textFile,"utf-8");}else t.text&&(r.text_content=t.text);t.from&&(r.from_email=t.from),t.fromName&&(r.from_name=t.fromName),t.replyTo&&(r.reply_to=t.replyTo),t.previewText&&(r.preview_text=t.previewText),t.inboxId&&(r.inbox_id=t.inboxId),t.tags&&(r.tags=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let a=await o.templates.create(r),n=await _e({id:a.id,html_content:a.html_content??void 0});if(t.format==="json")m({...a,preview_url:n},"json");else {p(`Template created: ${a.id}`);let s={ID:a.id,Name:a.name,Subject:a.subject,Variables:a.variables?.join(", ")||"-",Tags:a.tags?.join(", ")||"-"};n&&(s["Preview URL"]=n),m(s,"table"),n&&console.log(`
831
832
  Open the Preview URL in your browser to view the rendered template.`);}}catch(e){l(e instanceof Error?e.message:"Failed to create template"),process.exit(1);}}),i.command("update <template_id>").description(`Update an existing email template
832
833
 
833
834
  USAGE
@@ -852,6 +853,7 @@ OPTIONS
852
853
  --from-name <name> New sender display name
853
854
  --reply-to <email_address> New default reply-to email address
854
855
  --preview-text <text> New preview text
856
+ --inbox-id <inbox_id> Inbox ID for sender identity defaults (use "none" to clear)
855
857
  --tags <tags> Comma-separated tags (replaces existing)
856
858
  --format <format> Output format: json | table (default: table)
857
859
 
@@ -882,7 +884,7 @@ AGENTIC WORKFLOW
882
884
  2. Edit locally or with AI assistance
883
885
  3. Push preview: emailr templates push-preview <id> --html-file template.html
884
886
  4. Share URL with AI agent, iterate on feedback
885
- 5. Publish: emailr templates update <id> --html-file template.html`).option("--name <template_name>","New template name").option("--subject <subject_line>","New email subject line").option("--html <html_content>","New inline HTML content").option("--text <text_content>","New plain text content").option("--html-file <file_path>","Read new HTML content from file").option("--text-file <file_path>","Read new text content from file").option("--from <email_address>","New default from email address").option("--from-name <name>","New sender display name").option("--reply-to <email_address>","New default reply-to email address").option("--preview-text <text>","New preview text").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.name&&(a.name=e.name),e.subject&&(a.subject=e.subject),e.htmlFile){let n=await import('fs');a.html_content=n.readFileSync(e.htmlFile,"utf-8");}else e.html&&(a.html_content=e.html);if(e.textFile){let n=await import('fs');a.text_content=n.readFileSync(e.textFile,"utf-8");}else e.text&&(a.text_content=e.text);e.from&&(a.from_email=e.from),e.fromName&&(a.from_name=e.fromName),e.replyTo&&(a.reply_to=e.replyTo),e.previewText&&(a.preview_text=e.previewText),e.tags&&(a.tags=e.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let r=await s.templates.update(t,a);if(e.format==="json")m(r,"json");else {p(`Template updated: ${r.id}`);let n={ID:r.id,Name:r.name,Subject:r.subject,Variables:r.variables?.join(", ")||"-",Tags:r.tags?.join(", ")||"-",Updated:r.updated_at};m(n,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to update template"),process.exit(1);}}),i.command("delete <template_id>").description(`Delete a template
887
+ 5. Publish: emailr templates update <id> --html-file template.html`).option("--name <template_name>","New template name").option("--subject <subject_line>","New email subject line").option("--html <html_content>","New inline HTML content").option("--text <text_content>","New plain text content").option("--html-file <file_path>","Read new HTML content from file").option("--text-file <file_path>","Read new text content from file").option("--from <email_address>","New default from email address").option("--from-name <name>","New sender display name").option("--reply-to <email_address>","New default reply-to email address").option("--preview-text <text>","New preview text").option("--inbox-id <inbox_id>",'Inbox ID for sender identity (use "none" to clear)').option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.name&&(a.name=e.name),e.subject&&(a.subject=e.subject),e.htmlFile){let s=await import('fs');a.html_content=s.readFileSync(e.htmlFile,"utf-8");}else e.html&&(a.html_content=e.html);if(e.textFile){let s=await import('fs');a.text_content=s.readFileSync(e.textFile,"utf-8");}else e.text&&(a.text_content=e.text);e.from&&(a.from_email=e.from),e.fromName&&(a.from_name=e.fromName),e.replyTo&&(a.reply_to=e.replyTo),e.previewText&&(a.preview_text=e.previewText),e.inboxId&&(a.inbox_id=e.inboxId==="none"?null:e.inboxId),e.tags&&(a.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.templates.update(t,a);if(e.format==="json")m(n,"json");else {p(`Template updated: ${n.id}`);let s={ID:n.id,Name:n.name,Subject:n.subject,Variables:n.variables?.join(", ")||"-",Tags:n.tags?.join(", ")||"-",Updated:n.updated_at};m(s,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to update template"),process.exit(1);}}),i.command("delete <template_id>").description(`Delete a template
886
888
 
887
889
  USAGE
888
890
  emailr templates delete <template_id>
@@ -928,17 +930,17 @@ EXAMPLES
928
930
 
929
931
  SEE ALSO
930
932
  emailr templates edit Live editing with hot-reload
931
- emailr templates stop-preview Stop background preview server`).option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl});console.log(`Fetching template ${t}...`);let a=await s.templates.get(t),r=a.html_content??"";if(X(a.id,r),e.foreground){let g=await z(a.id);if(g||(l("Failed to start preview server"),process.exit(1)),ve(),console.log(`
933
+ emailr templates stop-preview Stop background preview server`).option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl});console.log(`Fetching template ${t}...`);let a=await r.templates.get(t),n=a.html_content??"";if(z(a.id,n),e.foreground){let g=await Y(a.id);if(g||(l("Failed to start preview server"),process.exit(1)),Oe(),console.log(`
932
934
  Template: ${a.name}`),console.log(`Preview URL: ${g}`),e.open!==!1)try{await(await import('open')).default(g),console.log(`
933
935
  Browser opened.`);}catch{console.log(`
934
936
  Could not open browser automatically. Open the URL above manually.`);}process.on("SIGINT",async()=>{console.log(`
935
937
 
936
- Stopping preview server...`),await Se().stop(),console.log("Done."),process.exit(0);});return}let n=_.join(process.cwd(),`.template-preview-${a.id}.html`);O.writeFileSync(n,r,"utf-8");let{spawn:d}=await import('child_process'),h=await import('os'),y=_.join(h.tmpdir(),"emailr-preview.pid");try{let g=O.readFileSync(y,"utf-8").trim();process.kill(parseInt(g,10),"SIGTERM");}catch{}let u=`
938
+ Stopping preview server...`),await Te().stop(),console.log("Done."),process.exit(0);});return}let s=E.join(process.cwd(),`.template-preview-${a.id}.html`);O.writeFileSync(s,n,"utf-8");let{spawn:d}=await import('child_process'),b=await import('os'),S=E.join(b.tmpdir(),"emailr-preview.pid");try{let g=O.readFileSync(S,"utf-8").trim();process.kill(parseInt(g,10),"SIGTERM");}catch{}let f=`
937
939
  const http = require('http');
938
940
  const fs = require('fs');
939
941
  const path = require('path');
940
942
  const os = require('os');
941
- const filePath = ${JSON.stringify(n)};
943
+ const filePath = ${JSON.stringify(s)};
942
944
  const pidFile = path.join(os.tmpdir(), 'emailr-preview.pid');
943
945
  const portFile = path.join(os.tmpdir(), 'emailr-preview.port');
944
946
  const server = http.createServer((req, res) => {
@@ -958,9 +960,9 @@ Stopping preview server...`),await Se().stop(),console.log("Done."),process.exit
958
960
  console.log('PORT:' + port);
959
961
  });
960
962
  process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); fs.unlinkSync(filePath); } catch {} process.exit(0); });
961
- `,b=d("node",["-e",u],{detached:!0,stdio:["ignore","pipe","ignore"]}),w="";await new Promise(g=>{b.stdout?.on("data",x=>{let J=x.toString().match(/PORT:(\d+)/);J&&(w=J[1],g());}),setTimeout(g,3e3);}),b.unref();let j=`http://127.0.0.1:${w}/`;if(console.log(`
962
- Template: ${a.name}`),console.log(`Preview URL: ${j}`),console.log(`
963
- To stop: emailr templates stop-preview`),e.open!==!1)try{await(await import('open')).default(j);}catch{}process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to preview template"),process.exit(1);}}),i.command("edit <template_id>").description(`Start a live editing session for a template with hot-reload
963
+ `,h=d("node",["-e",f],{detached:!0,stdio:["ignore","pipe","ignore"]}),w="";await new Promise(g=>{h.stdout?.on("data",j=>{let $=j.toString().match(/PORT:(\d+)/);$&&(w=$[1],g());}),setTimeout(g,3e3);}),h.unref();let _=`http://127.0.0.1:${w}/`;if(console.log(`
964
+ Template: ${a.name}`),console.log(`Preview URL: ${_}`),console.log(`
965
+ To stop: emailr templates stop-preview`),e.open!==!1)try{await(await import('open')).default(_);}catch{}process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to preview template"),process.exit(1);}}),i.command("edit <template_id>").description(`Start a live editing session for a template with hot-reload
964
966
 
965
967
  USAGE
966
968
  emailr templates edit <template_id> [options]
@@ -1000,12 +1002,12 @@ EXAMPLES
1000
1002
  SEE ALSO
1001
1003
  emailr templates draft Draft a new template with live preview
1002
1004
  emailr templates update Save changes to the template
1003
- emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for editing","./template.html").option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=_.resolve(e.file);console.log(`Fetching template ${t}...`);let r=await s.templates.get(t),n=r.html_content??"";if(O.writeFileSync(a,n,"utf-8"),console.log(`Template saved to: ${a}`),e.foreground){let x=`http://127.0.0.1:${await Z(a,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1004
- Template: ${r.name}`),console.log(`Template ID: ${r.id}`),console.log(`Live Preview: ${x}`),console.log(`File: ${a}`),console.log(`
1005
- Watching for changes... Edit the file and see live updates.`),console.log(`When done, run: emailr templates update ${t} --html-file ${e.file}`),e.open!==!1)try{await(await import('open')).default(x);}catch{}process.on("SIGINT",async()=>{console.log(`
1005
+ emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for editing","./template.html").option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=E.resolve(e.file);console.log(`Fetching template ${t}...`);let n=await r.templates.get(t),s=n.html_content??"";if(O.writeFileSync(a,s,"utf-8"),console.log(`Template saved to: ${a}`),e.foreground){let j=`http://127.0.0.1:${await Q(a,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1006
+ Template: ${n.name}`),console.log(`Template ID: ${n.id}`),console.log(`Live Preview: ${j}`),console.log(`File: ${a}`),console.log(`
1007
+ Watching for changes... Edit the file and see live updates.`),console.log(`When done, run: emailr templates update ${t} --html-file ${e.file}`),e.open!==!1)try{await(await import('open')).default(j);}catch{}process.on("SIGINT",async()=>{console.log(`
1006
1008
 
1007
- Stopping live preview server...`),await Q(),console.log("Done."),console.log(`
1008
- To save changes: emailr templates update ${t} --html-file ${e.file}`),process.exit(0);});return}let{spawn:d}=await import('child_process'),h=await import('os'),y=_.join(h.tmpdir(),"emailr-preview.pid");try{let g=O.readFileSync(y,"utf-8").trim();process.kill(parseInt(g,10),"SIGTERM");}catch{}let u=`
1009
+ Stopping live preview server...`),await ee(),console.log("Done."),console.log(`
1010
+ To save changes: emailr templates update ${t} --html-file ${e.file}`),process.exit(0);});return}let{spawn:d}=await import('child_process'),b=await import('os'),S=E.join(b.tmpdir(),"emailr-preview.pid");try{let g=O.readFileSync(S,"utf-8").trim();process.kill(parseInt(g,10),"SIGTERM");}catch{}let f=`
1009
1011
  const http = require('http');
1010
1012
  const fs = require('fs');
1011
1013
  const path = require('path');
@@ -1045,10 +1047,10 @@ To save changes: emailr templates update ${t} --html-file ${e.file}`),process.ex
1045
1047
  });
1046
1048
  });
1047
1049
  process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); } catch {} process.exit(0); });
1048
- `,b=d("node",["-e",u],{detached:!0,stdio:["ignore","pipe","ignore"]}),w="";await new Promise(g=>{b.stdout?.on("data",x=>{let ne=x.toString().match(/PORT:(\d+)/);ne&&(w=ne[1],g());}),setTimeout(g,3e3);}),b.unref();let j=`http://127.0.0.1:${w}/`;if(console.log(`
1049
- Template: ${r.name}`),console.log(`Template ID: ${r.id}`),console.log(`Live Preview: ${j}`),console.log(`File: ${a}`),console.log(`
1050
+ `,h=d("node",["-e",f],{detached:!0,stdio:["ignore","pipe","ignore"]}),w="";await new Promise(g=>{h.stdout?.on("data",j=>{let le=j.toString().match(/PORT:(\d+)/);le&&(w=le[1],g());}),setTimeout(g,3e3);}),h.unref();let _=`http://127.0.0.1:${w}/`;if(console.log(`
1051
+ Template: ${n.name}`),console.log(`Template ID: ${n.id}`),console.log(`Live Preview: ${_}`),console.log(`File: ${a}`),console.log(`
1050
1052
  Edit the file and see live updates in the browser.`),console.log(`
1051
- When done:`),console.log(` emailr templates update ${t} --html-file ${e.file}`),console.log(" emailr templates stop-preview"),e.open!==!1)try{await(await import('open')).default(j);}catch{}process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to edit template"),process.exit(1);}}),i.command("draft").description(`Start a live drafting session for a new template with hot-reload
1053
+ When done:`),console.log(` emailr templates update ${t} --html-file ${e.file}`),console.log(" emailr templates stop-preview"),e.open!==!1)try{await(await import('open')).default(_);}catch{}process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to edit template"),process.exit(1);}}),i.command("draft").description(`Start a live drafting session for a new template with hot-reload
1052
1054
 
1053
1055
  USAGE
1054
1056
  emailr templates draft [options]
@@ -1090,7 +1092,7 @@ EXAMPLES
1090
1092
  SEE ALSO
1091
1093
  emailr templates edit Edit an existing template with live preview
1092
1094
  emailr templates create Create the template from your draft
1093
- emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for drafting","./new-template.html").option("--no-open","Do not automatically open browser").option("--blank","Start with a blank file instead of starter template").option("--foreground","Keep process running in foreground (blocks terminal)").action(async t=>{try{let e=_.resolve(t.file),o;if(t.blank?o="":o=`<!DOCTYPE html>
1095
+ emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for drafting","./new-template.html").option("--no-open","Do not automatically open browser").option("--blank","Start with a blank file instead of starter template").option("--foreground","Keep process running in foreground (blocks terminal)").action(async t=>{try{let e=E.resolve(t.file),o;if(t.blank?o="":o=`<!DOCTYPE html>
1094
1096
  <html>
1095
1097
  <head>
1096
1098
  <meta charset="UTF-8">
@@ -1125,13 +1127,13 @@ SEE ALSO
1125
1127
  <p><a href="{{unsubscribe_link}}">Unsubscribe</a></p>
1126
1128
  </div>
1127
1129
  </body>
1128
- </html>`,O.writeFileSync(e,o,"utf-8"),console.log(`Template draft created: ${e}`),t.foreground){let b=`http://127.0.0.1:${await Z(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1129
- Live Preview: ${b}`),console.log(`File: ${e}`),console.log(`
1130
+ </html>`,O.writeFileSync(e,o,"utf-8"),console.log(`Template draft created: ${e}`),t.foreground){let h=`http://127.0.0.1:${await Q(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1131
+ Live Preview: ${h}`),console.log(`File: ${e}`),console.log(`
1130
1132
  Watching for changes... Edit the file and see live updates.`),console.log(`
1131
- When done, create the template:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),t.open!==!1)try{await(await import('open')).default(b);}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 ${t.file}`),t.open!==!1)try{await(await import('open')).default(h);}catch{}process.on("SIGINT",async()=>{console.log(`
1132
1134
 
1133
- Stopping live preview server...`),await Q(),console.log("Done."),console.log(`
1134
- To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),process.exit(0);});return}let{spawn:s}=await import('child_process'),a=await import('os'),r=_.join(a.tmpdir(),"emailr-preview.pid");try{let u=O.readFileSync(r,"utf-8").trim();process.kill(parseInt(u,10),"SIGTERM");}catch{}let n=`
1135
+ Stopping live preview server...`),await ee(),console.log("Done."),console.log(`
1136
+ To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),process.exit(0);});return}let{spawn:r}=await import('child_process'),a=await import('os'),n=E.join(a.tmpdir(),"emailr-preview.pid");try{let f=O.readFileSync(n,"utf-8").trim();process.kill(parseInt(f,10),"SIGTERM");}catch{}let s=`
1135
1137
  const http = require('http');
1136
1138
  const fs = require('fs');
1137
1139
  const path = require('path');
@@ -1171,10 +1173,10 @@ To create template: emailr templates create --name "Template Name" --subject "Em
1171
1173
  });
1172
1174
  });
1173
1175
  process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); } catch {} process.exit(0); });
1174
- `,d=s("node",["-e",n],{detached:!0,stdio:["ignore","pipe","ignore"]}),h="";await new Promise(u=>{d.stdout?.on("data",b=>{let w=b.toString().match(/PORT:(\d+)/);w&&(h=w[1],u());}),setTimeout(u,3e3);}),d.unref();let y=`http://127.0.0.1:${h}/`;if(console.log(`
1175
- Live Preview: ${y}`),console.log(`File: ${e}`),console.log(`
1176
+ `,d=r("node",["-e",s],{detached:!0,stdio:["ignore","pipe","ignore"]}),b="";await new Promise(f=>{d.stdout?.on("data",h=>{let w=h.toString().match(/PORT:(\d+)/);w&&(b=w[1],f());}),setTimeout(f,3e3);}),d.unref();let S=`http://127.0.0.1:${b}/`;if(console.log(`
1177
+ Live Preview: ${S}`),console.log(`File: ${e}`),console.log(`
1176
1178
  Edit the file and see live updates in the browser.`),console.log(`
1177
- When done:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),console.log(" emailr templates stop-preview"),t.open!==!1)try{await(await import('open')).default(y);}catch{}process.exit(0);}catch(e){l(e instanceof Error?e.message:"Failed to start draft session"),process.exit(1);}}),i.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 ${t.file}`),console.log(" emailr templates stop-preview"),t.open!==!1)try{await(await import('open')).default(S);}catch{}process.exit(0);}catch(e){l(e instanceof Error?e.message:"Failed to start draft session"),process.exit(1);}}),i.command("stop-preview").description(`Stop any running background preview server
1178
1180
 
1179
1181
  USAGE
1180
1182
  emailr templates stop-preview
@@ -1191,7 +1193,7 @@ EXAMPLES
1191
1193
  SEE ALSO
1192
1194
  emailr templates edit Start live editing session
1193
1195
  emailr templates draft Start live drafting session
1194
- emailr templates preview Preview a template in browser`).action(async()=>{let t=await import('os'),e=_.join(t.tmpdir(),"emailr-preview.pid");try{let o=O.readFileSync(e,"utf-8").trim();process.kill(parseInt(o,10),"SIGTERM"),O.unlinkSync(e),p("Preview server stopped");}catch{p("No preview server running");}}),i}function je(){let i=new Command("domains").description(`Manage sending domains
1196
+ emailr templates preview Preview a template in browser`).action(async()=>{let t=await import('os'),e=E.join(t.tmpdir(),"emailr-preview.pid");try{let o=O.readFileSync(e,"utf-8").trim();process.kill(parseInt(o,10),"SIGTERM"),O.unlinkSync(e),p("Preview server stopped");}catch{p("No preview server running");}}),i}function Ie(){let i=new Command("domains").description(`Manage sending domains
1195
1197
 
1196
1198
  USAGE
1197
1199
  emailr domains <subcommand> [options]
@@ -1299,7 +1301,7 @@ EXAMPLES
1299
1301
  emailr domains list --format json | jq '.[] | select(.status == "verified")'
1300
1302
 
1301
1303
  # Count domains
1302
- emailr domains list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),s=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.list();if(t.format==="json")m(s,"json");else {let a=s.map(r=>({ID:r.id,Domain:r.domain,Status:r.status,DKIM:r.dkim_verified,SPF:r.spf_verified,DMARC:r.dmarc_verified,Created:r.created_at}));m(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list domains"),process.exit(1);}}),i.command("get <domain_id>").description(`Get domain details
1304
+ emailr domains list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.list();if(t.format==="json")m(r,"json");else {let a=r.map(n=>({ID:n.id,Domain:n.domain,Status:n.status,DKIM:n.dkim_verified,SPF:n.spf_verified,DMARC:n.dmarc_verified,Created:n.created_at}));m(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list domains"),process.exit(1);}}),i.command("get <domain_id>").description(`Get domain details
1303
1305
 
1304
1306
  USAGE
1305
1307
  emailr domains get <domain_id> [options]
@@ -1379,7 +1381,7 @@ NEXT STEPS
1379
1381
  1. Copy the DNS records shown in the output
1380
1382
  2. Add them to your DNS provider (Cloudflare, Route53, etc.)
1381
1383
  3. Wait for DNS propagation (up to 48 hours)
1382
- 4. Run: emailr domains verify <domain_id>`).option("--receiving-subdomain <subdomain>","Subdomain for receiving emails").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={domain:t};e.receivingSubdomain&&(a.receivingSubdomain=e.receivingSubdomain);let r=await s.domains.add(a);if(e.format==="json")m(r,"json");else {if(p(`Domain added: ${r.domain}`),f("Add the following DNS records to verify your domain:"),console.log(""),r.dns_records){let n=[{Type:"DKIM",...ee(r.dns_records.dkim)},{Type:"SPF",...ee(r.dns_records.spf)},{Type:"DMARC",...ee(r.dns_records.dmarc)}];m(n,"table");}console.log(""),f(`Run 'emailr domains verify ${r.id}' after adding DNS records`);}}catch(o){l(o instanceof Error?o.message:"Failed to add domain"),process.exit(1);}}),i.command("verify <domain_id>").description(`Verify a domain
1384
+ 4. Run: emailr domains verify <domain_id>`).option("--receiving-subdomain <subdomain>","Subdomain for receiving emails").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={domain:t};e.receivingSubdomain&&(a.receivingSubdomain=e.receivingSubdomain);let n=await r.domains.add(a);if(e.format==="json")m(n,"json");else {if(p(`Domain added: ${n.domain}`),u("Add the following DNS records to verify your domain:"),console.log(""),n.dns_records){let s=[{Type:"DKIM",...te(n.dns_records.dkim)},{Type:"SPF",...te(n.dns_records.spf)},{Type:"DMARC",...te(n.dns_records.dmarc)}];m(s,"table");}console.log(""),u(`Run 'emailr domains verify ${n.id}' after adding DNS records`);}}catch(o){l(o instanceof Error?o.message:"Failed to add domain"),process.exit(1);}}),i.command("verify <domain_id>").description(`Verify a domain
1383
1385
 
1384
1386
  USAGE
1385
1387
  emailr domains verify <domain_id> [options]
@@ -1417,7 +1419,7 @@ TROUBLESHOOTING
1417
1419
  1. Run 'check-dns' to see which records are missing
1418
1420
  2. Verify records are correctly configured with your DNS provider
1419
1421
  3. Wait for DNS propagation (can take up to 48 hours)
1420
- 4. Try verification again`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.verify(t);e.format==="json"?m(a,"json"):a.verified?p("Domain verified successfully!"):(f(`Domain status: ${a.status}`),a.dkim_status&&f(`DKIM status: ${a.dkim_status}`));}catch(o){l(o instanceof Error?o.message:"Failed to verify domain"),process.exit(1);}}),i.command("check-dns <domain_id>").description(`Check DNS records for a domain
1422
+ 4. Try verification again`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.verify(t);e.format==="json"?m(a,"json"):a.verified?p("Domain verified successfully!"):(u(`Domain status: ${a.status}`),a.dkim_status&&u(`DKIM status: ${a.dkim_status}`));}catch(o){l(o instanceof Error?o.message:"Failed to verify domain"),process.exit(1);}}),i.command("check-dns <domain_id>").description(`Check DNS records for a domain
1421
1423
 
1422
1424
  USAGE
1423
1425
  emailr domains check-dns <domain_id> [options]
@@ -1464,7 +1466,7 @@ COMMON ISSUES
1464
1466
 
1465
1467
  Multiple records found:
1466
1468
  - Remove duplicate TXT records
1467
- - Keep only the Emailr-specific record`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.checkDns(t);if(e.format==="json")m(a,"json");else {let r=Object.entries(a).map(([n,d])=>({Record:n,Verified:d.verified,Expected:d.expected||"-",Found:d.found?.join(", ")||"-"}));m(r,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to check DNS"),process.exit(1);}}),i.command("delete <domain_id>").description(`Delete a domain
1469
+ - Keep only the Emailr-specific record`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.checkDns(t);if(e.format==="json")m(a,"json");else {let n=Object.entries(a).map(([s,d])=>({Record:s,Verified:d.verified,Expected:d.expected||"-",Found:d.found?.join(", ")||"-"}));m(n,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to check DNS"),process.exit(1);}}),i.command("delete <domain_id>").description(`Delete a domain
1468
1470
 
1469
1471
  USAGE
1470
1472
  emailr domains delete <domain_id> [options]
@@ -1494,7 +1496,7 @@ WARNING
1494
1496
  This action is permanent and cannot be undone.
1495
1497
  - Emails from this domain will fail to send
1496
1498
  - DNS records can be removed from your DNS provider
1497
- - Re-add the domain if you need to restore functionality`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.delete(t),p(`Domain deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete domain"),process.exit(1);}}),i}function ee(i){return {"Record Type":i.type,Name:i.name,Value:i.value.length>50?i.value.substring(0,47)+"...":i.value,...i.priority!==void 0&&{Priority:i.priority}}}function xe(){let i=new Command("config").description(`Manage CLI configuration
1499
+ - Re-add the domain if you need to restore functionality`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.delete(t),p(`Domain deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete domain"),process.exit(1);}}),i}function te(i){return {"Record Type":i.type,Name:i.name,Value:i.value.length>50?i.value.substring(0,47)+"...":i.value,...i.priority!==void 0&&{Priority:i.priority}}}function Ne(){let i=new Command("config").description(`Manage CLI configuration
1498
1500
 
1499
1501
  USAGE
1500
1502
  emailr config <subcommand> [options]
@@ -1587,7 +1589,7 @@ EXAMPLES
1587
1589
 
1588
1590
  NOTE
1589
1591
  Environment variables (EMAILR_API_KEY, EMAILR_BASE_URL) take precedence
1590
- over config file values at runtime.`).action(async(t,e)=>{try{let o=["api-key","base-url","format"],s=t.toLowerCase();o.includes(s)||(l(`Invalid config key: ${t}`),f(`Valid keys: ${o.join(", ")}`),process.exit(1));let r={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[s];s==="format"&&!["json","table"].includes(e)&&(l(`Invalid format value: ${e}`),f("Valid formats: json, table"),process.exit(1)),G({[r]:e}),p(`Configuration saved: ${t} = ${s==="api-key"?"***":e}`),f(`Config file: ${N()}`);}catch(o){l(o instanceof Error?o.message:"Failed to save configuration"),process.exit(1);}}),i.command("get <key>").description(`Get a configuration value
1592
+ over config file values at runtime.`).action(async(t,e)=>{try{let o=["api-key","base-url","format"],r=t.toLowerCase();o.includes(r)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${o.join(", ")}`),process.exit(1));let n={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[r];r==="format"&&!["json","table"].includes(e)&&(l(`Invalid format value: ${e}`),u("Valid formats: json, table"),process.exit(1)),G({[n]:e}),p(`Configuration saved: ${t} = ${r==="api-key"?"***":e}`),u(`Config file: ${I()}`);}catch(o){l(o instanceof Error?o.message:"Failed to save configuration"),process.exit(1);}}),i.command("get <key>").description(`Get a configuration value
1591
1593
 
1592
1594
  USAGE
1593
1595
  emailr config get <key>
@@ -1615,7 +1617,7 @@ EXAMPLES
1615
1617
  emailr config get format
1616
1618
 
1617
1619
  SEE ALSO
1618
- emailr config list Show all configuration values`).action(async t=>{try{let e=["api-key","base-url","format"],o=t.toLowerCase();e.includes(o)||(l(`Invalid config key: ${t}`),f(`Valid keys: ${e.join(", ")}`),process.exit(1));let a={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[o],r=se(a);r?console.log(o==="api-key"?r.substring(0,8)+"..."+r.substring(r.length-4):r):f(`${t} is not set`);}catch(e){l(e instanceof Error?e.message:"Failed to get configuration"),process.exit(1);}}),i.command("list").description(`List all configuration values
1620
+ emailr config list Show all configuration values`).action(async t=>{try{let e=["api-key","base-url","format"],o=t.toLowerCase();e.includes(o)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${e.join(", ")}`),process.exit(1));let a={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[o],n=me(a);n?console.log(o==="api-key"?n.substring(0,8)+"..."+n.substring(n.length-4):n):u(`${t} is not set`);}catch(e){l(e instanceof Error?e.message:"Failed to get configuration"),process.exit(1);}}),i.command("list").description(`List all configuration values
1619
1621
 
1620
1622
  USAGE
1621
1623
  emailr config list [options]
@@ -1643,7 +1645,7 @@ EXAMPLES
1643
1645
 
1644
1646
  SEE ALSO
1645
1647
  emailr config get Get a single configuration value
1646
- emailr config path Show config file location`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o={"api-key":e.apiKey?e.apiKey.substring(0,8)+"..."+e.apiKey.substring(e.apiKey.length-4):"(not set)","base-url":e.baseUrl||"(default)",format:e.format||"table"};if(t.format==="json")m(o,"json");else {let s=Object.entries(o).map(([a,r])=>({Key:a,Value:r}));m(s,"table");}console.log(""),f(`Config file: ${N()}`);}catch(e){e instanceof Error&&e.message.includes("No API key configured")?(f("No configuration found."),f("Run 'emailr config set api-key <your-api-key>' to get started.")):(l(e instanceof Error?e.message:"Failed to list configuration"),process.exit(1));}}),i.command("path").description(`Show the configuration file path
1648
+ emailr config path Show config file location`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o={"api-key":e.apiKey?e.apiKey.substring(0,8)+"..."+e.apiKey.substring(e.apiKey.length-4):"(not set)","base-url":e.baseUrl||"(default)",format:e.format||"table"};if(t.format==="json")m(o,"json");else {let r=Object.entries(o).map(([a,n])=>({Key:a,Value:n}));m(r,"table");}console.log(""),u(`Config file: ${I()}`);}catch(e){e instanceof Error&&e.message.includes("No API key configured")?(u("No configuration found."),u("Run 'emailr config set api-key <your-api-key>' to get started.")):(l(e instanceof Error?e.message:"Failed to list configuration"),process.exit(1));}}),i.command("path").description(`Show the configuration file path
1647
1649
 
1648
1650
  USAGE
1649
1651
  emailr config path
@@ -1670,7 +1672,7 @@ EXAMPLES
1670
1672
  open $(emailr config path)
1671
1673
 
1672
1674
  # View config file contents
1673
- cat $(emailr config path)`).action(()=>{console.log(N());}),i.command("init").description(`Initialize configuration interactively
1675
+ cat $(emailr config path)`).action(()=>{console.log(I());}),i.command("init").description(`Initialize configuration interactively
1674
1676
 
1675
1677
  USAGE
1676
1678
  emailr config init [options]
@@ -1703,7 +1705,7 @@ ALTERNATIVE: ENVIRONMENT VARIABLES
1703
1705
 
1704
1706
  SEE ALSO
1705
1707
  emailr config set Set individual configuration values
1706
- emailr config list View current configuration`).option("--api-key <key>","API key to use").option("--base-url <url>","Base URL for API").action(async t=>{try{t.apiKey?(G({apiKey:t.apiKey,baseUrl:t.baseUrl}),p("Configuration initialized!"),f(`Config file: ${N()}`)):(f("Initialize your Emailr CLI configuration:"),console.log(""),f("Run with --api-key flag:"),console.log(" emailr config init --api-key <your-api-key>"),console.log(""),f("Or set environment variable:"),console.log(" export EMAILR_API_KEY=<your-api-key>"));}catch(e){l(e instanceof Error?e.message:"Failed to initialize configuration"),process.exit(1);}}),i}function Ne(){let i=new Command("broadcasts").description(`Manage broadcast campaigns
1708
+ emailr config list View current configuration`).option("--api-key <key>","API key to use").option("--base-url <url>","Base URL for API").action(async t=>{try{t.apiKey?(G({apiKey:t.apiKey,baseUrl:t.baseUrl}),p("Configuration initialized!"),u(`Config file: ${I()}`)):(u("Initialize your Emailr CLI configuration:"),console.log(""),u("Run with --api-key flag:"),console.log(" emailr config init --api-key <your-api-key>"),console.log(""),u("Or set environment variable:"),console.log(" export EMAILR_API_KEY=<your-api-key>"));}catch(e){l(e instanceof Error?e.message:"Failed to initialize configuration"),process.exit(1);}}),i}function Ce(){let i=new Command("broadcasts").description(`Manage broadcast campaigns
1707
1709
 
1708
1710
  USAGE
1709
1711
  emailr broadcasts <subcommand> [options]
@@ -1853,7 +1855,7 @@ EXAMPLES
1853
1855
  emailr broadcasts list --format json
1854
1856
 
1855
1857
  # Pipe JSON to jq for processing
1856
- emailr broadcasts list --format json | jq '.[] | select(.status == "scheduled")'`).option("--status <status>","Filter by status (draft, scheduled, sending, sent)").option("--limit <number>","Number of broadcasts to return","20").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={status:t.status,limit:parseInt(t.limit)};if(t.tags){let r=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean);r.length>0&&(s.tags=r.join(","));}let a=await o.broadcasts.list(s);if(t.format==="json")m(a,"json");else {if(a.length===0){console.log("No broadcasts found.");return}let r=a.map(n=>({ID:n.id,Name:n.name,Subject:n.subject.substring(0,30)+(n.subject.length>30?"...":""),Status:n.status,Tags:n.tags?.join(", ")||"-",Recipients:n.total_recipients||0,Sent:n.sent_count||0,Created:new Date(n.created_at).toLocaleDateString()}));m(r,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list broadcasts"),process.exit(1);}}),i.command("get <broadcast_id>").description(`Get broadcast details
1858
+ emailr broadcasts list --format json | jq '.[] | select(.status == "scheduled")'`).option("--status <status>","Filter by status (draft, scheduled, sending, sent)").option("--limit <number>","Number of broadcasts to return","20").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={status:t.status,limit:parseInt(t.limit)};if(t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let a=await o.broadcasts.list(r);if(t.format==="json")m(a,"json");else {if(a.length===0){console.log("No broadcasts found.");return}let n=a.map(s=>({ID:s.id,Name:s.name,Subject:s.subject.substring(0,30)+(s.subject.length>30?"...":""),Status:s.status,Tags:s.tags?.join(", ")||"-",Recipients:s.total_recipients||0,Sent:s.sent_count||0,Created:new Date(s.created_at).toLocaleDateString()}));m(n,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list broadcasts"),process.exit(1);}}),i.command("get <broadcast_id>").description(`Get broadcast details
1857
1859
 
1858
1860
  USAGE
1859
1861
  emailr broadcasts get <broadcast_id> [options]
@@ -1904,6 +1906,7 @@ OPTIONS
1904
1906
  --from-name <name> Sender display name (e.g. "Acme Inc")
1905
1907
  --reply-to <email> Reply-To email address
1906
1908
  --preview-text <text> Preview text (preheader) shown in email clients
1909
+ --inbox-id <inbox_id> Inbox ID for sender identity defaults
1907
1910
  --template <template_id> Template ID to use for content
1908
1911
  --segment <segment_id> Segment ID to target recipients
1909
1912
  --html <html_content> Inline HTML content (alternative to --template)
@@ -1968,7 +1971,7 @@ EXAMPLES
1968
1971
 
1969
1972
  SEE ALSO
1970
1973
  emailr templates Create and manage email templates
1971
- 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("--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 t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={name:t.name,subject:t.subject,from_email:t.from,from_name:t.fromName,reply_to:t.replyTo,preview_text:t.previewText,template_id:t.template,segment_id:t.segment,html_content:t.html,text_content:t.text,scheduled_at:t.schedule};t.tags&&(s.tags=t.tags.split(",").map(r=>r.trim().toLowerCase()).filter(Boolean));let a=await o.broadcasts.create(s);t.format==="json"?m(a,"json"):(p("Broadcast created successfully!"),m({ID:a.id,Name:a.name,Status:a.status,Tags:a.tags?.join(", ")||"-","Scheduled At":a.scheduled_at||"Not scheduled"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create broadcast"),process.exit(1);}}),i.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").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("--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 t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={name:t.name,subject:t.subject,from_email:t.from,from_name:t.fromName,reply_to:t.replyTo,preview_text:t.previewText,inbox_id:t.inboxId,template_id:t.template,segment_id:t.segment,html_content:t.html,text_content:t.text,scheduled_at:t.schedule};t.tags&&(r.tags=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let a=await o.broadcasts.create(r);t.format==="json"?m(a,"json"):(p("Broadcast created successfully!"),m({ID:a.id,Name:a.name,Status:a.status,Tags:a.tags?.join(", ")||"-","Scheduled At":a.scheduled_at||"Not scheduled"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create broadcast"),process.exit(1);}}),i.command("send <broadcast_id>").description(`Send a broadcast immediately
1972
1975
 
1973
1976
  USAGE
1974
1977
  emailr broadcasts send <broadcast_id> [options]
@@ -2098,6 +2101,7 @@ OPTIONS
2098
2101
  --from-name <name> Update sender display name
2099
2102
  --reply-to <email> Update Reply-To email address
2100
2103
  --preview-text <text> Update preview text (preheader)
2104
+ --inbox-id <inbox_id> Update inbox ID for sender identity defaults (use "none" to clear)
2101
2105
  --template <id> Update template ID (use "none" to clear)
2102
2106
  --segment <id> Update segment ID (use "none" to clear)
2103
2107
  --topic <id> Update topic ID (use "none" to clear)
@@ -2131,7 +2135,7 @@ EXAMPLES
2131
2135
  emailr broadcasts update brd_abc123 --name "Updated" --format json
2132
2136
 
2133
2137
  NOTE
2134
- Only broadcasts in 'draft' or 'scheduled' status can be updated.`).option("--name <name>","Broadcast name").option("--subject <subject>","Email subject").option("--from <email>","Sender email address").option("--from-name <name>","Sender display name").option("--reply-to <email>","Reply-To email address").option("--preview-text <text>","Preview text (preheader)").option("--template <id>",'Template ID (use "none" to clear)').option("--segment <id>",'Segment ID (use "none" to clear)').option("--topic <id>",'Topic ID (use "none" to clear)').option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>",'Schedule time ISO 8601 (use "none" to clear)').option("--tags <tags>","Comma-separated tags (replaces existing)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};e.name&&(a.name=e.name),e.subject&&(a.subject=e.subject),e.from&&(a.from_email=e.from),e.fromName&&(a.from_name=e.fromName),e.replyTo&&(a.reply_to=e.replyTo),e.previewText&&(a.preview_text=e.previewText),e.html&&(a.html_content=e.html),e.text&&(a.text_content=e.text),e.template&&(a.template_id=e.template==="none"?null:e.template),e.segment&&(a.segment_id=e.segment==="none"?null:e.segment),e.topic&&(a.topic_id=e.topic==="none"?null:e.topic),e.schedule&&(a.scheduled_at=e.schedule==="none"?null:e.schedule),e.tags&&(a.tags=e.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean)),Object.keys(a).length===0&&(l("No update fields specified. Use --help to see available options."),process.exit(1));let r=await s.broadcasts.update(t,a);e.format==="json"?m(r,"json"):(p("Broadcast updated successfully!"),m({ID:r.id,Name:r.name,Subject:r.subject,Status:r.status,Tags:r.tags?.join(", ")||"-","Scheduled At":r.scheduled_at||"Not scheduled"},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update broadcast"),process.exit(1);}}),i.command("delete <broadcast_id>").description(`Delete a broadcast
2138
+ Only broadcasts in 'draft' or 'scheduled' status can be updated.`).option("--name <name>","Broadcast name").option("--subject <subject>","Email subject").option("--from <email>","Sender email address").option("--from-name <name>","Sender display name").option("--reply-to <email>","Reply-To email address").option("--preview-text <text>","Preview text (preheader)").option("--inbox-id <inbox_id>",'Inbox ID for sender identity (use "none" to clear)').option("--template <id>",'Template ID (use "none" to clear)').option("--segment <id>",'Segment ID (use "none" to clear)').option("--topic <id>",'Topic ID (use "none" to clear)').option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>",'Schedule time ISO 8601 (use "none" to clear)').option("--tags <tags>","Comma-separated tags (replaces existing)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};e.name&&(a.name=e.name),e.subject&&(a.subject=e.subject),e.from&&(a.from_email=e.from),e.fromName&&(a.from_name=e.fromName),e.replyTo&&(a.reply_to=e.replyTo),e.previewText&&(a.preview_text=e.previewText),e.html&&(a.html_content=e.html),e.text&&(a.text_content=e.text),e.inboxId&&(a.inbox_id=e.inboxId==="none"?null:e.inboxId),e.template&&(a.template_id=e.template==="none"?null:e.template),e.segment&&(a.segment_id=e.segment==="none"?null:e.segment),e.topic&&(a.topic_id=e.topic==="none"?null:e.topic),e.schedule&&(a.scheduled_at=e.schedule==="none"?null:e.schedule),e.tags&&(a.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean)),Object.keys(a).length===0&&(l("No update fields specified. Use --help to see available options."),process.exit(1));let n=await r.broadcasts.update(t,a);e.format==="json"?m(n,"json"):(p("Broadcast updated successfully!"),m({ID:n.id,Name:n.name,Subject:n.subject,Status:n.status,Tags:n.tags?.join(", ")||"-","Scheduled At":n.scheduled_at||"Not scheduled"},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update broadcast"),process.exit(1);}}),i.command("delete <broadcast_id>").description(`Delete a broadcast
2135
2139
 
2136
2140
  USAGE
2137
2141
  emailr broadcasts delete <broadcast_id> [options]
@@ -2159,7 +2163,7 @@ EXAMPLES
2159
2163
 
2160
2164
  WARNING
2161
2165
  This action is permanent and cannot be undone.
2162
- All delivery statistics will be lost.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.delete(t);e.format==="json"?m(a,"json"):p("Broadcast deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete broadcast"),process.exit(1);}}),i}function Ie(){let i=new Command("webhooks").description(`Manage webhooks
2166
+ All delivery statistics will be lost.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.delete(t);e.format==="json"?m(a,"json"):p("Broadcast deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete broadcast"),process.exit(1);}}),i}function Ue(){let i=new Command("webhooks").description(`Manage webhooks
2163
2167
 
2164
2168
  USAGE
2165
2169
  emailr webhooks <subcommand> [options]
@@ -2312,7 +2316,7 @@ EXAMPLES
2312
2316
  emailr webhooks list --format json | jq '.[] | select(.active == true)'
2313
2317
 
2314
2318
  # Count webhooks
2315
- emailr webhooks list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),s=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).webhooks.list();if(t.format==="json")m(s,"json");else {if(s.data.length===0){console.log("No webhooks found.");return}let a=s.data.map(r=>({ID:r.id,Name:r.name,URL:r.url.substring(0,40)+(r.url.length>40?"...":""),Events:r.events.join(", "),Active:r.active?"Yes":"No"}));m(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list webhooks"),process.exit(1);}}),i.command("get <webhook_id>").description(`Get webhook details
2319
+ emailr webhooks list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).webhooks.list();if(t.format==="json")m(r,"json");else {if(r.data.length===0){console.log("No webhooks found.");return}let a=r.data.map(n=>({ID:n.id,Name:n.name,URL:n.url.substring(0,40)+(n.url.length>40?"...":""),Events:n.events.join(", "),Active:n.active?"Yes":"No"}));m(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list webhooks"),process.exit(1);}}),i.command("get <webhook_id>").description(`Get webhook details
2316
2320
 
2317
2321
  USAGE
2318
2322
  emailr webhooks get <webhook_id> [options]
@@ -2417,7 +2421,7 @@ EXAMPLES
2417
2421
 
2418
2422
  NOTE
2419
2423
  Save the webhook secret returned by this command. You'll need it
2420
- to verify webhook signatures in your endpoint.`).requiredOption("--name <name>","Webhook name").requiredOption("--url <url>","Webhook URL").requiredOption("--events <events>","Events to subscribe to (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s=t.events.split(",").map(r=>r.trim()),a=await o.webhooks.create({name:t.name,url:t.url,events:s});t.format==="json"?m(a,"json"):(p("Webhook created successfully!"),m({ID:a.id,Name:a.name,URL:a.url,Secret:a.secret},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create webhook"),process.exit(1);}}),i.command("update <webhook_id>").description(`Update a webhook
2424
+ to verify webhook signatures in your endpoint.`).requiredOption("--name <name>","Webhook name").requiredOption("--url <url>","Webhook URL").requiredOption("--events <events>","Events to subscribe to (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r=t.events.split(",").map(n=>n.trim()),a=await o.webhooks.create({name:t.name,url:t.url,events:r});t.format==="json"?m(a,"json"):(p("Webhook created successfully!"),m({ID:a.id,Name:a.name,URL:a.url,Secret:a.secret},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create webhook"),process.exit(1);}}),i.command("update <webhook_id>").description(`Update a webhook
2421
2425
 
2422
2426
  USAGE
2423
2427
  emailr webhooks update <webhook_id> [options]
@@ -2462,7 +2466,7 @@ EXAMPLES
2462
2466
  --events "email.delivered,email.bounced"
2463
2467
 
2464
2468
  # Get JSON output
2465
- emailr webhooks update whk_abc123 --name "New Name" --format json`).option("--name <name>","New webhook name").option("--url <url>","New webhook URL").option("--events <events>","New events (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};e.name&&(a.name=e.name),e.url&&(a.url=e.url),e.events&&(a.events=e.events.split(",").map(n=>n.trim()));let r=await s.webhooks.update(t,a);e.format==="json"?m(r,"json"):(p("Webhook updated successfully!"),m({ID:r.id,Name:r.name,URL:r.url},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update webhook"),process.exit(1);}}),i.command("enable <webhook_id>").description(`Enable a webhook
2469
+ emailr webhooks update whk_abc123 --name "New Name" --format json`).option("--name <name>","New webhook name").option("--url <url>","New webhook URL").option("--events <events>","New events (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};e.name&&(a.name=e.name),e.url&&(a.url=e.url),e.events&&(a.events=e.events.split(",").map(s=>s.trim()));let n=await r.webhooks.update(t,a);e.format==="json"?m(n,"json"):(p("Webhook updated successfully!"),m({ID:n.id,Name:n.name,URL:n.url},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update webhook"),process.exit(1);}}),i.command("enable <webhook_id>").description(`Enable a webhook
2466
2470
 
2467
2471
  USAGE
2468
2472
  emailr webhooks enable <webhook_id> [options]
@@ -2543,7 +2547,7 @@ EXAMPLES
2543
2547
 
2544
2548
  WARNING
2545
2549
  This action is permanent and cannot be undone.
2546
- Create a new webhook if you need to restore functionality.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.delete(t);e.format==="json"?m(a,"json"):p("Webhook deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete webhook"),process.exit(1);}}),i}function Ce(){let i=new Command("segments").description(`Manage contact segments
2550
+ Create a new webhook if you need to restore functionality.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.delete(t);e.format==="json"?m(a,"json"):p("Webhook deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete webhook"),process.exit(1);}}),i}function Pe(){let i=new Command("segments").description(`Manage contact segments
2547
2551
 
2548
2552
  USAGE
2549
2553
  emailr segments <subcommand> [options]
@@ -2683,7 +2687,7 @@ EXAMPLES
2683
2687
  emailr segments list --format json
2684
2688
 
2685
2689
  # Pipe JSON to jq for processing
2686
- emailr segments list --format json | jq '.[].name'`).option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s={};if(t.tags){let r=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean);r.length>0&&(s.tags=r.join(","));}let a=await o.segments.list(s);if(t.format==="json")m(a,"json");else {if(a.length===0){console.log("No segments found.");return}let r=a.map(n=>({ID:n.id,Name:n.name,Description:(n.description||"").substring(0,30)+((n.description?.length||0)>30?"...":""),Tags:n.tags?.join(", ")||"-","Created At":new Date(n.created_at).toLocaleDateString()}));m(r,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list segments"),process.exit(1);}}),i.command("get <segment_id>").description(`Get segment details
2690
+ emailr segments list --format json | jq '.[].name'`).option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={};if(t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let a=await o.segments.list(r);if(t.format==="json")m(a,"json");else {if(a.length===0){console.log("No segments found.");return}let n=a.map(s=>({ID:s.id,Name:s.name,Description:(s.description||"").substring(0,30)+((s.description?.length||0)>30?"...":""),Tags:s.tags?.join(", ")||"-","Created At":new Date(s.created_at).toLocaleDateString()}));m(n,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list segments"),process.exit(1);}}),i.command("get <segment_id>").description(`Get segment details
2687
2691
 
2688
2692
  USAGE
2689
2693
  emailr segments get <segment_id> [options]
@@ -2760,7 +2764,7 @@ EXAMPLES
2760
2764
  # Get JSON output for scripting
2761
2765
  emailr segments create --name "Test" \\
2762
2766
  --conditions '[{"field": "subscribed", "operator": "equals", "value": true}]' \\
2763
- --format json`).requiredOption("--name <name>","Segment name").requiredOption("--conditions <json>","Segment conditions as JSON").option("--description <description>","Segment description").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),s;try{s=JSON.parse(t.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let a={name:t.name,description:t.description,conditions:s};t.tags&&(a.tags=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let r=await o.segments.create(a);t.format==="json"?m(r,"json"):(p("Segment created successfully!"),m({ID:r.id,Name:r.name,Tags:r.tags?.join(", ")||"-"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create segment"),process.exit(1);}}),i.command("update <segment_id>").description(`Update a segment
2767
+ --format json`).requiredOption("--name <name>","Segment name").requiredOption("--conditions <json>","Segment conditions as JSON").option("--description <description>","Segment description").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r;try{r=JSON.parse(t.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let a={name:t.name,description:t.description,conditions:r};t.tags&&(a.tags=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await o.segments.create(a);t.format==="json"?m(n,"json"):(p("Segment created successfully!"),m({ID:n.id,Name:n.name,Tags:n.tags?.join(", ")||"-"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create segment"),process.exit(1);}}),i.command("update <segment_id>").description(`Update a segment
2764
2768
 
2765
2769
  USAGE
2766
2770
  emailr segments update <segment_id> [options]
@@ -2806,7 +2810,7 @@ EXAMPLES
2806
2810
  --conditions '[{"field": "metadata.plan", "operator": "equals", "value": "enterprise"}]'
2807
2811
 
2808
2812
  # Get JSON output
2809
- emailr segments update seg_abc123 --name "Updated" --format json`).option("--name <name>","New segment name").option("--description <description>","New description").option("--conditions <json>","New conditions as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.name&&(a.name=e.name),e.description&&(a.description=e.description),e.conditions)try{a.conditions=JSON.parse(e.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}e.tags&&(a.tags=e.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let r=await s.segments.update(t,a);e.format==="json"?m(r,"json"):(p("Segment updated successfully!"),m({ID:r.id,Name:r.name,Tags:r.tags?.join(", ")||"-"},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update segment"),process.exit(1);}}),i.command("delete <segment_id>").description(`Delete a segment
2813
+ emailr segments update seg_abc123 --name "Updated" --format json`).option("--name <name>","New segment name").option("--description <description>","New description").option("--conditions <json>","New conditions as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.name&&(a.name=e.name),e.description&&(a.description=e.description),e.conditions)try{a.conditions=JSON.parse(e.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}e.tags&&(a.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.segments.update(t,a);e.format==="json"?m(n,"json"):(p("Segment updated successfully!"),m({ID:n.id,Name:n.name,Tags:n.tags?.join(", ")||"-"},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update segment"),process.exit(1);}}),i.command("delete <segment_id>").description(`Delete a segment
2810
2814
 
2811
2815
  USAGE
2812
2816
  emailr segments delete <segment_id>
@@ -2862,7 +2866,7 @@ EXAMPLES
2862
2866
 
2863
2867
  TIP
2864
2868
  Use this before sending broadcasts to verify your segment
2865
- targets the expected number of contacts.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.getContactsCount(t);e.format==="json"?m(a,"json"):console.log(`Segment contains ${a.count} contacts.`);}catch(o){l(o instanceof Error?o.message:"Failed to get segment count"),process.exit(1);}}),i}function pt(i){try{let t=i.startsWith("http")?i:`http://localhost${i}`,e=new URL(t);return {key:e.searchParams.get("key")??void 0,code:e.searchParams.get("code")??void 0,state:e.searchParams.get("state")??void 0,error:e.searchParams.get("error")??void 0,message:e.searchParams.get("message")??void 0}}catch{return {}}}function ut(){return `<!DOCTYPE html>
2869
+ targets the expected number of contacts.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.getContactsCount(t);e.format==="json"?m(a,"json"):console.log(`Segment contains ${a.count} contacts.`);}catch(o){l(o instanceof Error?o.message:"Failed to get segment count"),process.exit(1);}}),i}function wt(i){try{let t=i.startsWith("http")?i:`http://localhost${i}`,e=new URL(t);return {key:e.searchParams.get("key")??void 0,code:e.searchParams.get("code")??void 0,state:e.searchParams.get("state")??void 0,error:e.searchParams.get("error")??void 0,message:e.searchParams.get("message")??void 0}}catch{return {}}}function yt(){return `<!DOCTYPE html>
2866
2870
  <html lang="en">
2867
2871
  <head>
2868
2872
  <meta charset="UTF-8">
@@ -2908,7 +2912,7 @@ TIP
2908
2912
  <p>You can close this window and return to your terminal.</p>
2909
2913
  </div>
2910
2914
  </body>
2911
- </html>`}function te(i){return `<!DOCTYPE html>
2915
+ </html>`}function ae(i){return `<!DOCTYPE html>
2912
2916
  <html lang="en">
2913
2917
  <head>
2914
2918
  <meta charset="UTF-8">
@@ -2963,7 +2967,7 @@ TIP
2963
2967
  <p style="margin-top: 1rem;">Please close this window and try again.</p>
2964
2968
  </div>
2965
2969
  </body>
2966
- </html>`}function Ue(){let i=null,t=0,e=null,o=null,s=null;return {async start(){return new Promise((a,r)=>{i=Xe.createServer((n,d)=>{if(n.method!=="GET"||!n.url?.startsWith("/callback")){d.writeHead(404,{"Content-Type":"text/plain"}),d.end("Not Found");return}let h=pt(n.url);if(s&&h.state!==s){d.writeHead(400,{"Content-Type":"text/html"}),d.end(te("Security verification failed. State parameter mismatch.")),e&&e({success:false,error:"State parameter mismatch"});return}if(h.error){let u=h.message||h.error;d.writeHead(200,{"Content-Type":"text/html"}),d.end(te(u)),e&&e({success:false,error:u});return}let y=h.key||h.code;if(y){d.writeHead(200,{"Content-Type":"text/html"}),d.end(ut()),e&&e({success:true,apiKey:y});return}d.writeHead(400,{"Content-Type":"text/html"}),d.end(te("Invalid callback: missing required parameters.")),e&&e({success:false,error:"Invalid callback: missing required parameters"});}),i.listen(0,"127.0.0.1",()=>{let n=i.address();n&&typeof n=="object"?(t=n.port,a({port:t,url:`http://127.0.0.1:${t}/callback`})):r(new Error("Failed to get server address"));}),i.on("error",n=>{r(new Error(`Failed to start callback server: ${n.message}`));});})},async waitForCallback(a,r){return s=a,new Promise(n=>{e=n,o=setTimeout(()=>{e&&e({success:false,error:"Login timed out. Please try again."});},r);})},async stop(){if(o&&(clearTimeout(o),o=null),i)return new Promise(a=>{i.close(()=>{i=null,a();});})}}}function Pe(){return ft.randomBytes(32).toString("hex")}function ke(i){return new Promise(t=>{let e=process.platform,o;switch(e){case "darwin":o=`open "${i}"`;break;case "win32":o=`start "" "${i}"`;break;default:o=`xdg-open "${i}"`;break}exec(o,s=>{t(!s);});})}var B=120,gt=process.env.EMAILR_WEB_URL||"https://app.emailr.dev";function wt(i,t){let e=`http://127.0.0.1:${t}/callback`,o=new URLSearchParams({state:i,callback_url:e});return `${gt}/consent/authorize?${o.toString()}`}function Ae(){return new Command("login").description("Log in to Emailr via browser authentication").option("-t, --timeout <seconds>","Timeout in seconds",String(B)).option("--no-browser","Don't automatically open the browser").action(async t=>{await yt({timeout:parseInt(t.timeout,10)||B,noBrowser:t.browser===false});})}async function yt(i){let t=Ue(),e=(i.timeout||B)*1e3;try{f("Starting authentication server...");let{port:o,url:s}=await t.start(),a=Pe(),r=wt(a,o);console.log(""),f("Authorization URL:"),console.log(` ${r}`),console.log(""),i.noBrowser?f("Please open the URL above in your browser to continue."):await ke(r)?f("Browser opened. Please complete authentication in your browser."):(I("Could not open browser automatically."),f("Please open the URL above in your browser to continue.")),console.log(""),f(`Waiting for authentication (timeout: ${i.timeout||B}s)...`);let n=await t.waitForCallback(a,e);n.success&&n.apiKey?(G({apiKey:n.apiKey}),console.log(""),p("Login successful!"),f(`API key saved to: ${N()}`),f("You can now use the Emailr CLI.")):(console.log(""),l(n.error||"Authentication failed."),f("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1));}catch(o){console.log(""),l(o instanceof Error?o.message:"An unexpected error occurred."),f("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1);}finally{await t.stop();}}var oe=_.join(V.homedir(),".config","opencode","skills","emailr-cli"),Ot=`---
2970
+ </html>`}function Ae(){let i=null,t=0,e=null,o=null,r=null;return {async start(){return new Promise((a,n)=>{i=tt.createServer((s,d)=>{if(s.method!=="GET"||!s.url?.startsWith("/callback")){d.writeHead(404,{"Content-Type":"text/plain"}),d.end("Not Found");return}let b=wt(s.url);if(r&&b.state!==r){d.writeHead(400,{"Content-Type":"text/html"}),d.end(ae("Security verification failed. State parameter mismatch.")),e&&e({success:false,error:"State parameter mismatch"});return}if(b.error){let f=b.message||b.error;d.writeHead(200,{"Content-Type":"text/html"}),d.end(ae(f)),e&&e({success:false,error:f});return}let S=b.key||b.code;if(S){d.writeHead(200,{"Content-Type":"text/html"}),d.end(yt()),e&&e({success:true,apiKey:S});return}d.writeHead(400,{"Content-Type":"text/html"}),d.end(ae("Invalid callback: missing required parameters.")),e&&e({success:false,error:"Invalid callback: missing required parameters"});}),i.listen(0,"127.0.0.1",()=>{let s=i.address();s&&typeof s=="object"?(t=s.port,a({port:t,url:`http://127.0.0.1:${t}/callback`})):n(new Error("Failed to get server address"));}),i.on("error",s=>{n(new Error(`Failed to start callback server: ${s.message}`));});})},async waitForCallback(a,n){return r=a,new Promise(s=>{e=s,o=setTimeout(()=>{e&&e({success:false,error:"Login timed out. Please try again."});},n);})},async stop(){if(o&&(clearTimeout(o),o=null),i)return new Promise(a=>{i.close(()=>{i=null,a();});})}}}function ke(){return St.randomBytes(32).toString("hex")}function Re(i){return new Promise(t=>{let e=process.platform,o;switch(e){case "darwin":o=`open "${i}"`;break;case "win32":o=`start "" "${i}"`;break;default:o=`xdg-open "${i}"`;break}exec(o,r=>{t(!r);});})}var V=120,Ot=process.env.EMAILR_WEB_URL||"https://app.emailr.dev";function xt(i,t){let e=`http://127.0.0.1:${t}/callback`,o=new URLSearchParams({state:i,callback_url:e});return `${Ot}/consent/authorize?${o.toString()}`}function Me(){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 t=>{await Et({timeout:parseInt(t.timeout,10)||V,noBrowser:t.browser===false});})}async function Et(i){let t=Ae(),e=(i.timeout||V)*1e3;try{u("Starting authentication server...");let{port:o,url:r}=await t.start(),a=ke(),n=xt(a,o);console.log(""),u("Authorization URL:"),console.log(` ${n}`),console.log(""),i.noBrowser?u("Please open the URL above in your browser to continue."):await Re(n)?u("Browser opened. Please complete authentication in your browser."):(N("Could not open browser automatically."),u("Please open the URL above in your browser to continue.")),console.log(""),u(`Waiting for authentication (timeout: ${i.timeout||V}s)...`);let s=await t.waitForCallback(a,e);s.success&&s.apiKey?(G({apiKey:s.apiKey}),console.log(""),p("Login successful!"),u(`API key saved to: ${I()}`),u("You can now use the Emailr CLI.")):(console.log(""),l(s.error||"Authentication failed."),u("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1));}catch(o){console.log(""),l(o instanceof Error?o.message:"An unexpected error occurred."),u("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1);}finally{await t.stop();}}var ie=E.join(X.homedir(),".config","opencode","skills","emailr-cli"),Nt=`---
2967
2971
  name: emailr-cli
2968
2972
  description: Operate the Emailr CLI to send emails, manage contacts, templates, domains, broadcasts, webhooks, and segments. Includes LIVE PREVIEW editing for templates with hot-reload.
2969
2973
  ---
@@ -3003,7 +3007,7 @@ IMPORTANT: Always use \`--background\` flag so the command returns immediately a
3003
3007
  - Create: \`emailr templates create --name "Name" --subject "Subject" --html-file ./template.html\`
3004
3008
  - Update: \`emailr templates update <id> --html-file ./template.html\`
3005
3009
  - Delete: \`emailr templates delete <id>\`
3006
- `;function Et(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function _t(){console.log("Installing OpenCode AI agent...");try{return execSync("npm install -g opencode-ai@latest",{stdio:"inherit"}),!0}catch{if(process.platform==="darwin")try{return execSync("brew install anomalyco/tap/opencode",{stdio:"inherit"}),!0}catch{return false}return false}}function jt(){O.existsSync(oe)||O.mkdirSync(oe,{recursive:true});let i=_.join(oe,"SKILL.md");O.writeFileSync(i,Ot,"utf-8");}function Me(){return new Command("agent").description(`Launch an AI agent with Emailr CLI expertise
3010
+ `;function Ct(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function Ut(){console.log("Installing OpenCode AI agent...");try{return execSync("npm install -g opencode-ai@latest",{stdio:"inherit"}),!0}catch{if(process.platform==="darwin")try{return execSync("brew install anomalyco/tap/opencode",{stdio:"inherit"}),!0}catch{return false}return false}}function Pt(){O.existsSync(ie)||O.mkdirSync(ie,{recursive:true});let i=E.join(ie,"SKILL.md");O.writeFileSync(i,Nt,"utf-8");}function Le(){return new Command("agent").description(`Launch an AI agent with Emailr CLI expertise
3007
3011
 
3008
3012
  This opens OpenCode (opencode.ai), an AI coding agent that knows how to use all Emailr CLI commands.
3009
3013
  The agent can help you:
@@ -3011,10 +3015,10 @@ The agent can help you:
3011
3015
  - Manage contacts, broadcasts, and segments
3012
3016
  - Configure domains and webhooks
3013
3017
 
3014
- The agent runs in your terminal with full access to the emailr CLI.`).option("--install","Install OpenCode if not already installed").option("--model <model>","Model to use (e.g., anthropic/claude-sonnet-4)").action(async t=>{Et()||(t.install?_t()||(l("Failed to install OpenCode. Please install manually:"),console.log(" npm install -g opencode-ai@latest"),console.log(" # or"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)):(l("OpenCode AI agent is not installed."),console.log(`
3015
- Install it with one of:`),console.log(" emailr agent --install"),console.log(" npm install -g opencode-ai@latest"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)));try{jt(),p("Emailr CLI skill loaded");}catch(s){I(`Could not install skill: ${s instanceof Error?s.message:String(s)}`);}let e=[];t.model&&e.push("--model",t.model),console.log(`
3018
+ The agent runs in your terminal with full access to the emailr CLI.`).option("--install","Install OpenCode if not already installed").option("--model <model>","Model to use (e.g., anthropic/claude-sonnet-4)").action(async t=>{Ct()||(t.install?Ut()||(l("Failed to install OpenCode. Please install manually:"),console.log(" npm install -g opencode-ai@latest"),console.log(" # or"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)):(l("OpenCode AI agent is not installed."),console.log(`
3019
+ Install it with one of:`),console.log(" emailr agent --install"),console.log(" npm install -g opencode-ai@latest"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)));try{Pt(),p("Emailr CLI skill loaded");}catch(r){N(`Could not install skill: ${r instanceof Error?r.message:String(r)}`);}let e=[];t.model&&e.push("--model",t.model),console.log(`
3016
3020
  Starting Emailr AI Agent...`),console.log("The agent has the emailr-cli skill loaded."),console.log(`Ask it to help with emails, templates, contacts, or broadcasts.
3017
- `);let o=spawn("opencode",e,{stdio:"inherit",env:process.env});o.on("error",s=>{l(`Failed to start agent: ${s.message}`),process.exit(1);}),o.on("exit",s=>{process.exit(s??0);});})}function Fe(){let i=new Command("inbox").description(`Manage inbound emails (inbox)
3021
+ `);let o=spawn("opencode",e,{stdio:"inherit",env:process.env});o.on("error",r=>{l(`Failed to start agent: ${r.message}`),process.exit(1);}),o.on("exit",r=>{process.exit(r??0);});})}function Ke(){let i=new Command("inbox").description(`Manage inbound emails (inbox)
3018
3022
 
3019
3023
  USAGE
3020
3024
  emailr inbox <subcommand> [options]
@@ -3043,6 +3047,9 @@ EXAMPLES
3043
3047
  # Search by sender email
3044
3048
  emailr inbox list --email sender@example.com
3045
3049
 
3050
+ # Filter by inbox
3051
+ emailr inbox list --inbox-id <inbox-uuid>
3052
+
3046
3053
  # View a specific email
3047
3054
  emailr inbox get <email-id>
3048
3055
 
@@ -3076,6 +3083,7 @@ OPTIONS
3076
3083
  --page <number> Page number (default: 1)
3077
3084
  --email <address> Filter by sender email (partial match)
3078
3085
  --domain <domain> Filter by sender domain
3086
+ --inbox-id <id> Filter by inbox ID
3079
3087
  --format <format> Output format: json | table (default: table)
3080
3088
 
3081
3089
  EXAMPLES
@@ -3085,9 +3093,12 @@ EXAMPLES
3085
3093
  emailr inbox list --domain example.com
3086
3094
  emailr inbox list --format json
3087
3095
 
3096
+ # Filter by inbox
3097
+ emailr inbox list --inbox-id <inbox-uuid>
3098
+
3088
3099
  OUTPUT FORMATS
3089
3100
  --format json Machine-readable JSON with data array and pagination
3090
- --format table Human-readable table with ID, From, Subject, To, Date (default)`).option("--limit <count>","Number of emails to return","20").option("--page <number>","Page number","1").option("--email <address>","Filter by sender email (partial match)").option("--domain <domain>","Filter by sender domain").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),s=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).emails.list({status:"received",page:parseInt(t.page),limit:Math.min(parseInt(t.limit),100),email:t.email,domain:t.domain});if(t.format==="json")m(s,"json");else {if(!s.data||s.data.length===0){f("No emails in inbox");return}let r=s.data.map(n=>({ID:n.id,From:n.from_email,Subject:De(n.subject||"(no subject)",40),To:n.to_email,Date:re(n.created_at),Attachments:n.attachments?.length||0}));m(r,"table"),f(`Page ${s.pagination.page} of ${s.pagination.pages} (${s.pagination.total} total)`);}}catch(e){l(e instanceof Error?e.message:"Failed to list inbox"),process.exit(1);}}),i.command("get <id>").description(`View a received email
3101
+ --format table Human-readable table with ID, From, Subject, To, Date (default)`).option("--limit <count>","Number of emails to return","20").option("--page <number>","Page number","1").option("--email <address>","Filter by sender email (partial match)").option("--domain <domain>","Filter by sender domain").option("--inbox-id <inbox_id>","Filter by inbox ID").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=c(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={status:"received",page:parseInt(t.page),limit:Math.min(parseInt(t.limit),100),email:t.email,domain:t.domain};t.inboxId&&(r.inbox_id=t.inboxId);let a=await o.emails.list(r);if(t.format==="json")m(a,"json");else {if(!a.data||a.data.length===0){u("No emails in inbox");return}let s=a.data.map(d=>({ID:d.id,From:d.from_email,Subject:Ge(d.subject||"(no subject)",40),To:d.to_email,Date:re(d.created_at),Attachments:d.attachments?.length||0}));m(s,"table"),u(`Page ${a.pagination.page} of ${a.pagination.pages} (${a.pagination.total} total)`);}}catch(e){l(e instanceof Error?e.message:"Failed to list inbox"),process.exit(1);}}),i.command("get <id>").description(`View a received email
3091
3102
 
3092
3103
  USAGE
3093
3104
  emailr inbox get <email-id> [options]
@@ -3108,7 +3119,7 @@ EXAMPLES
3108
3119
 
3109
3120
  OUTPUT FORMATS
3110
3121
  --format json Full email object with all fields as JSON
3111
- --format table Key-value table with email metadata and content preview (default)`).option("--content <type>","Content to display: preview | text | html","preview").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).emails.get(t);if(e.format==="json")m(a,"json");else {if(m({ID:a.id,From:a.from_email,To:a.to_email,Subject:a.subject||"(no subject)",Date:re(a.created_at),Status:a.status,"Thread ID":a.thread_id||"-","Parent Email":a.parent_email_id||"-",Attachments:a.attachments?.length||0},"table"),console.log(""),e.content==="html")console.log(a.html_content||"(no HTML content)");else if(e.content==="text")console.log(a.text_content||"(no text content)");else {let n=a.text_content||a.html_content?.replace(/<[^>]*>/g,"")||"(no content)";console.log(n);}if(a.attachments&&a.attachments.length>0){console.log(""),f("Attachments:");let n=a.attachments.map(d=>({Filename:d.filename,Type:d.contentType||d.content_type||"-",Size:Nt(d.size||0)}));m(n,"table");}}}catch(o){l(o instanceof Error?o.message:"Failed to get email"),process.exit(1);}}),i.command("thread <id>").description(`View conversation thread for an email
3122
+ --format table Key-value table with email metadata and content preview (default)`).option("--content <type>","Content to display: preview | text | html","preview").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).emails.get(t);if(e.format==="json")m(a,"json");else {if(m({ID:a.id,From:a.from_email,To:a.to_email,Subject:a.subject||"(no subject)",Date:re(a.created_at),Status:a.status,"Thread ID":a.thread_id||"-","Parent Email":a.parent_email_id||"-",Attachments:a.attachments?.length||0},"table"),console.log(""),e.content==="html")console.log(a.html_content||"(no HTML content)");else if(e.content==="text")console.log(a.text_content||"(no text content)");else {let s=a.text_content||a.html_content?.replace(/<[^>]*>/g,"")||"(no content)";console.log(s);}if(a.attachments&&a.attachments.length>0){console.log(""),u("Attachments:");let s=a.attachments.map(d=>({Filename:d.filename,Type:d.contentType||d.content_type||"-",Size:kt(d.size||0)}));m(s,"table");}}}catch(o){l(o instanceof Error?o.message:"Failed to get email"),process.exit(1);}}),i.command("thread <id>").description(`View conversation thread for an email
3112
3123
 
3113
3124
  USAGE
3114
3125
  emailr inbox thread <email-id> [options]
@@ -3127,7 +3138,7 @@ EXAMPLES
3127
3138
 
3128
3139
  OUTPUT FORMATS
3129
3140
  --format json JSON array of all emails in the thread
3130
- --format table Chronological conversation view with direction indicators (default)`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=await s.emails.get(t),r=a.thread_id||a.id,n=[],d=new Set;if(n.push(a),d.add(a.id),a.thread_id&&a.thread_id!==a.id)try{let u=await s.emails.get(a.thread_id);d.has(u.id)||(n.push(u),d.add(u.id));}catch{}let h=await s.emails.list({limit:100});if(h.data)for(let u of h.data)d.has(u.id)||(u.thread_id===r||u.parent_email_id===a.id||u.id===a.parent_email_id)&&(n.push(u),d.add(u.id));if(n.sort((u,b)=>new Date(u.created_at).getTime()-new Date(b.created_at).getTime()),e.format==="json")m(n,"json");else {n.length<=1&&f("No other emails in this conversation"),f(`Conversation (${n.length} email${n.length!==1?"s":""}):`),console.log("");for(let u of n){let b=u.status==="received"?"\u2190 IN ":"\u2192 OUT",w=u.id===t?" \u25C0 (selected)":"";console.log(`${b} ${re(u.created_at)}${w}`),console.log(` From: ${u.from_email}`),console.log(` To: ${u.to_email}`),console.log(` Subject: ${u.subject||"(no subject)"}`);let j=u.text_content||u.html_content?.replace(/<[^>]*>/g,"")||"";j&&console.log(` ${De(j.trim(),120)}`),console.log("");}}await new Promise(u=>process.stdout.write("",()=>u())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to load thread"),process.exit(1);}}),i.command("reply <id>").description(`Reply to a received email
3141
+ --format table Chronological conversation view with direction indicators (default)`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=await r.emails.get(t),n=a.thread_id||a.id,s=[],d=new Set;if(s.push(a),d.add(a.id),a.thread_id&&a.thread_id!==a.id)try{let f=await r.emails.get(a.thread_id);d.has(f.id)||(s.push(f),d.add(f.id));}catch{}let b=await r.emails.list({limit:100});if(b.data)for(let f of b.data)d.has(f.id)||(f.thread_id===n||f.parent_email_id===a.id||f.id===a.parent_email_id)&&(s.push(f),d.add(f.id));if(s.sort((f,h)=>new Date(f.created_at).getTime()-new Date(h.created_at).getTime()),e.format==="json")m(s,"json");else {s.length<=1&&u("No other emails in this conversation"),u(`Conversation (${s.length} email${s.length!==1?"s":""}):`),console.log("");for(let f of s){let h=f.status==="received"?"\u2190 IN ":"\u2192 OUT",w=f.id===t?" \u25C0 (selected)":"";console.log(`${h} ${re(f.created_at)}${w}`),console.log(` From: ${f.from_email}`),console.log(` To: ${f.to_email}`),console.log(` Subject: ${f.subject||"(no subject)"}`);let _=f.text_content||f.html_content?.replace(/<[^>]*>/g,"")||"";_&&console.log(` ${Ge(_.trim(),120)}`),console.log("");}}await new Promise(f=>process.stdout.write("",()=>f())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to load thread"),process.exit(1);}}),i.command("reply <id>").description(`Reply to a received email
3131
3142
 
3132
3143
  USAGE
3133
3144
  emailr inbox reply <email-id> [options]
@@ -3163,7 +3174,7 @@ EXAMPLES
3163
3174
 
3164
3175
  OUTPUT FORMATS
3165
3176
  --format json Machine-readable JSON with message_id and status
3166
- --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(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=await s.emails.get(t),r=e.html,n=e.text;if(e.htmlFile)try{r=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);}!r&&!n&&(l("Reply content is required. Use --html, --text, --html-file, or --text-file"),process.exit(1));let d=e.subject||(a.subject?.startsWith("Re:")?a.subject:`Re: ${a.subject||""}`),h={to:a.from_email,from:e.from||a.to_email,subject:d,html:r||void 0,text:n||r?.replace(/<[^>]*>/g,"")||void 0,replyTo:{in_reply_to:a.message_id||a.ses_message_id,thread_id:a.thread_id||a.id,parent_email_id:a.id}};if(e.cc){let b=e.cc.split(",").map(w=>w.trim());h.cc=b.length===1?b[0]:b;}if(e.bcc){let b=e.bcc.split(",").map(w=>w.trim());h.bcc=b.length===1?b[0]:b;}let y=await s.emails.send(h);e.format==="json"?m(y,"json"):(p("Reply sent successfully!"),m({"Message ID":y.message_id,To:a.from_email,Subject:d,Status:y.status},"table")),await new Promise(b=>process.stdout.write("",()=>b())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to send reply"),process.exit(1);}}),i.command("forward <id>").description(`Forward a received email
3177
+ --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(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=await r.emails.get(t),n=e.html,s=e.text;if(e.htmlFile)try{n=readFileSync(e.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${e.htmlFile}`),process.exit(1);}if(e.textFile)try{s=readFileSync(e.textFile,"utf-8");}catch{l(`Failed to read text file: ${e.textFile}`),process.exit(1);}!n&&!s&&(l("Reply content is required. Use --html, --text, --html-file, or --text-file"),process.exit(1));let d=e.subject||(a.subject?.startsWith("Re:")?a.subject:`Re: ${a.subject||""}`),b={to:a.from_email,from:e.from||a.to_email,subject:d,html:n||void 0,text:s||n?.replace(/<[^>]*>/g,"")||void 0,replyTo:{in_reply_to:a.message_id||a.ses_message_id,thread_id:a.thread_id||a.id,parent_email_id:a.id}};if(e.cc){let h=e.cc.split(",").map(w=>w.trim());b.cc=h.length===1?h[0]:h;}if(e.bcc){let h=e.bcc.split(",").map(w=>w.trim());b.bcc=h.length===1?h[0]:h;}let S=await r.emails.send(b);e.format==="json"?m(S,"json"):(p("Reply sent successfully!"),m({"Message ID":S.message_id,To:a.from_email,Subject:d,Status:S.status},"table")),await new Promise(h=>process.stdout.write("",()=>h())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to send reply"),process.exit(1);}}),i.command("forward <id>").description(`Forward a received email
3167
3178
 
3168
3179
  USAGE
3169
3180
  emailr inbox forward <email-id> --to <recipients> [options]
@@ -3189,7 +3200,178 @@ EXAMPLES
3189
3200
 
3190
3201
  OUTPUT FORMATS
3191
3202
  --format json Machine-readable JSON with message_id and status
3192
- --format table Human-readable summary with Message ID, To, Recipients, Status (default)`).requiredOption("--to <emails>","Recipient email addresses (comma-separated)").option("--message <text>","Message to include with forwarded email").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),s=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=e.to.split(",").map(d=>d.trim()).filter(Boolean),r=await s.emails.forward({email_id:t,to:a.length===1?a[0]:a,message:e.message});e.format==="json"?m(r,"json"):(p("Email forwarded successfully!"),m({"Message ID":r.message_id,To:a.join(", "),Recipients:r.recipients,Status:r.status},"table")),await new Promise(d=>process.stdout.write("",()=>d())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to forward email"),process.exit(1);}}),i}function De(i,t){return i.length<=t?i:i.slice(0,t-1)+"\u2026"}function re(i){return new Date(i).toLocaleString()}function Nt(i){return i<1024?i+" B":i<1024*1024?(i/1024).toFixed(1)+" KB":(i/(1024*1024)).toFixed(1)+" MB"}var S=new Command;S.name("emailr").description(`Emailr CLI - Send emails and manage your email infrastructure
3203
+ --format table Human-readable summary with Message ID, To, Recipients, Status (default)`).requiredOption("--to <emails>","Recipient email addresses (comma-separated)").option("--message <text>","Message to include with forwarded email").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=e.to.split(",").map(d=>d.trim()).filter(Boolean),n=await r.emails.forward({email_id:t,to:a.length===1?a[0]:a,message:e.message});e.format==="json"?m(n,"json"):(p("Email forwarded successfully!"),m({"Message ID":n.message_id,To:a.join(", "),Recipients:n.recipients,Status:n.status},"table")),await new Promise(d=>process.stdout.write("",()=>d())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to forward email"),process.exit(1);}}),i}function Ge(i,t){return i.length<=t?i:i.slice(0,t-1)+"\u2026"}function re(i){return new Date(i).toLocaleString()}function kt(i){return i<1024?i+" B":i<1024*1024?(i/1024).toFixed(1)+" KB":(i/(1024*1024)).toFixed(1)+" MB"}function qe(){let i=new Command("inboxes").description(`Manage inboxes
3204
+
3205
+ USAGE
3206
+ emailr inboxes <subcommand> [options]
3207
+
3208
+ DESCRIPTION
3209
+ Create, list, get, update, and delete inboxes. Each inbox represents
3210
+ a distinct email identity with a name, username, and domain.
3211
+
3212
+ SUBCOMMANDS
3213
+ create Create a new inbox
3214
+ list List all inboxes
3215
+ get <id> Get an inbox by ID
3216
+ update <id> Update an inbox
3217
+ delete <id> Delete an inbox
3218
+
3219
+ EXAMPLES
3220
+ # Create an inbox
3221
+ emailr inboxes create --name "Support" --username support --domain example.com
3222
+
3223
+ # List all inboxes
3224
+ emailr inboxes list
3225
+
3226
+ # Get inbox details
3227
+ emailr inboxes get inb_abc123
3228
+
3229
+ # Update inbox name
3230
+ emailr inboxes update inb_abc123 --name "Customer Support"
3231
+
3232
+ # Delete an inbox
3233
+ emailr inboxes delete inb_abc123`);return i.command("create").description(`Create a new inbox
3234
+
3235
+ USAGE
3236
+ emailr inboxes create --name <name> --username <username> --domain <domain> [options]
3237
+
3238
+ DESCRIPTION
3239
+ Creates a new inbox with the specified name, username, and domain.
3240
+ The domain must be verified for your organization.
3241
+
3242
+ OPTIONS
3243
+ --name <name> Display name for the inbox (required)
3244
+ --username <username> Username part of the email address (required)
3245
+ --domain <domain> Domain for the email address (required)
3246
+ --format <format> Output format: json | table (default: table)
3247
+
3248
+ EXAMPLES
3249
+ # Create a support inbox
3250
+ emailr inboxes create --name "Support" --username support --domain example.com
3251
+
3252
+ # Create and get JSON output
3253
+ emailr inboxes create --name "Sales" --username sales --domain example.com --format json`).requiredOption("--name <name>","Inbox display name").requiredOption("--username <username>","Username for the email address").requiredOption("--domain <domain>","Domain for the email address").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=c(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).inboxes.create({name:t.name,username:t.username,domain:t.domain});t.format==="json"?m(r,"json"):(p(`Inbox created: ${r.id}`),m({ID:r.id,Name:r.name,"From Address":r.from_address,"Inbound Address":r.inbound_address,Created:r.created_at},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create inbox"),process.exit(1);}}),i.command("list").description(`List all inboxes
3254
+
3255
+ USAGE
3256
+ emailr inboxes list [options]
3257
+
3258
+ DESCRIPTION
3259
+ Retrieves all inboxes for your organization.
3260
+
3261
+ OPTIONS
3262
+ --format <format> Output format: json | table (default: table)
3263
+
3264
+ EXAMPLES
3265
+ # List all inboxes
3266
+ emailr inboxes list
3267
+
3268
+ # Get JSON output
3269
+ emailr inboxes list --format json`).option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=c(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).inboxes.list();if(t.format==="json")m(r,"json");else {if(r.length===0){console.log("No inboxes found.");return}let a=r.map(n=>({ID:n.id,Name:n.name,"From Address":n.from_address,"Inbound Address":n.inbound_address,Created:new Date(n.created_at).toLocaleDateString()}));m(a,"table"),console.log(`
3270
+ Total: ${r.length}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list inboxes"),process.exit(1);}}),i.command("get <inbox_id>").description(`Get an inbox by ID
3271
+
3272
+ USAGE
3273
+ emailr inboxes get <inbox_id> [options]
3274
+
3275
+ DESCRIPTION
3276
+ Retrieves detailed information about a specific inbox.
3277
+
3278
+ ARGUMENTS
3279
+ <inbox_id> The unique inbox identifier
3280
+
3281
+ OPTIONS
3282
+ --format <format> Output format: json | table (default: table)
3283
+
3284
+ EXAMPLES
3285
+ # Get inbox details
3286
+ emailr inboxes get inb_abc123
3287
+
3288
+ # Get JSON output
3289
+ emailr inboxes get inb_abc123 --format json`).option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).inboxes.get(t);e.format==="json"?m(a,"json"):m({ID:a.id,Name:a.name,Username:a.username,Domain:a.domain,"From Address":a.from_address,"Inbound Address":a.inbound_address,Created:a.created_at,Updated:a.updated_at},"table");}catch(o){l(o instanceof Error?o.message:"Failed to get inbox"),process.exit(1);}}),i.command("update <inbox_id>").description(`Update an inbox
3290
+
3291
+ USAGE
3292
+ emailr inboxes update <inbox_id> --name <name> [options]
3293
+
3294
+ DESCRIPTION
3295
+ Updates an inbox's display name. Username and domain are immutable
3296
+ after creation.
3297
+
3298
+ ARGUMENTS
3299
+ <inbox_id> The unique inbox identifier
3300
+
3301
+ OPTIONS
3302
+ --name <name> New display name for the inbox
3303
+ --format <format> Output format: json | table (default: table)
3304
+
3305
+ EXAMPLES
3306
+ # Update inbox name
3307
+ emailr inboxes update inb_abc123 --name "Customer Support"
3308
+
3309
+ # Get JSON output
3310
+ emailr inboxes update inb_abc123 --name "New Name" --format json`).option("--name <name>","New inbox display name").option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{e.name||(l("No update fields specified. Use --name to update the inbox name."),process.exit(1));let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};e.name&&(a.name=e.name);let n=await r.inboxes.update(t,a);e.format==="json"?m(n,"json"):(p(`Inbox updated: ${n.id}`),m({ID:n.id,Name:n.name,"From Address":n.from_address,"Inbound Address":n.inbound_address,Updated:n.updated_at},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update inbox"),process.exit(1);}}),i.command("delete <inbox_id>").description(`Delete an inbox
3311
+
3312
+ USAGE
3313
+ emailr inboxes delete <inbox_id> [options]
3314
+
3315
+ DESCRIPTION
3316
+ Permanently deletes an inbox. Existing emails associated with this
3317
+ inbox will have their inbox reference cleared but are preserved.
3318
+
3319
+ ARGUMENTS
3320
+ <inbox_id> The unique inbox identifier
3321
+
3322
+ OPTIONS
3323
+ --format <format> Output format: json | table (default: table)
3324
+
3325
+ EXAMPLES
3326
+ # Delete an inbox
3327
+ emailr inboxes delete inb_abc123
3328
+
3329
+ # Get JSON output
3330
+ emailr inboxes delete inb_abc123 --format json
3331
+
3332
+ WARNING
3333
+ This action is permanent and cannot be undone.`).option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).inboxes.delete(t);e.format==="json"?m(a,"json"):p(`Inbox deleted: ${t}`);}catch(o){l(o instanceof Error?o.message:"Failed to delete inbox"),process.exit(1);}}),i}function $e(){let i=new Command("threads").description(`Manage email threads and labels
3334
+
3335
+ USAGE
3336
+ emailr threads <subcommand> [options]
3337
+
3338
+ DESCRIPTION
3339
+ List, view, and manage email threads. Threads group related emails
3340
+ into conversations. Use labels to organize threads (inbox, starred,
3341
+ archived, spam, trash, etc).
3342
+
3343
+ SUBCOMMANDS
3344
+ list List threads filtered by label
3345
+ get <id> View a thread with all messages
3346
+ label <id> Add or remove labels from a thread
3347
+
3348
+ EXAMPLES
3349
+ # List inbox threads
3350
+ emailr threads list
3351
+
3352
+ # List sent threads
3353
+ emailr threads list --label sent
3354
+
3355
+ # List starred threads for a specific inbox
3356
+ emailr threads list --label starred --inbox-id <uuid>
3357
+
3358
+ # Search threads
3359
+ emailr threads list --search "invoice"
3360
+
3361
+ # View a thread
3362
+ emailr threads get <thread-id>
3363
+
3364
+ # Star a thread
3365
+ emailr threads label <thread-id> --add starred
3366
+
3367
+ # Archive a thread (remove from inbox)
3368
+ emailr threads label <thread-id> --add archived --remove inbox
3369
+
3370
+ # Move to trash
3371
+ emailr threads label <thread-id> --add trash --remove inbox
3372
+
3373
+ # Mark as spam
3374
+ emailr threads label <thread-id> --add spam --remove inbox`);return i.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 e=c(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.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(r,"json");else {if(!r.data||r.data.length===0){u(`No threads in ${t.label}`);return}let n=r.data.map(s=>({"Thread ID":s.thread_id.slice(0,8)+"\u2026",Subject:He(s.subject||"(no subject)",40),From:s.from_email,Messages:s.message_count,Labels:(s.labels||[]).join(", "),Updated:Je(s.updated_at)}));m(n,"table"),u(`Page ${r.pagination.page} of ${r.pagination.pages} (${r.pagination.total} total)`);}}catch(e){l(e instanceof Error?e.message:"Failed to list threads"),process.exit(1);}}),i.command("get <id>").description("View a thread with all messages").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=c(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).threads.get(t);if(e.format==="json")m(a,"json");else {u(`Thread: ${a.subject||"(no subject)"}`),u(`Labels: ${a.labels.join(", ")||"none"}`),u(`Messages: ${a.messages.length}`),console.log("");for(let s of a.messages){let d=s.status==="received"?"\u2190 IN ":"\u2192 OUT";console.log(`${d} ${Je(s.created_at)}`),console.log(` From: ${s.from_email}`),console.log(` To: ${s.to_email}`),s.cc_emails?.length&&console.log(` Cc: ${s.cc_emails.join(", ")}`),console.log(` Labels: ${s.labels.join(", ")||"none"}`);let b=s.text_content||s.html_content?.replace(/<[^>]*>/g,"")||"";b&&console.log(` ${He(b.trim(),120)}`),console.log("");}}await new Promise(s=>process.stdout.write("",()=>s())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to get thread"),process.exit(1);}}),i.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,e)=>{try{!e.add&&!e.remove&&(l("Specify --add and/or --remove labels"),process.exit(1));let o=c(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=e.add?e.add.split(",").map(b=>b.trim()):void 0,n=e.remove?e.remove.split(",").map(b=>b.trim()):void 0,s=await r.threads.updateLabels(t,{add:a,remove:n});e.format==="json"?m(s,"json"):(p(`Updated ${s.updated} email(s) in thread`),a&&u(`Added: ${a.join(", ")}`),n&&u(`Removed: ${n.join(", ")}`)),await new Promise(b=>process.stdout.write("",()=>b())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to update labels"),process.exit(1);}}),i}function He(i,t){return i.length<=t?i:i.slice(0,t-1)+"\u2026"}function Je(i){return new Date(i).toLocaleString()}var y=new Command;y.name("emailr").description(`Emailr CLI - Send emails and manage your email infrastructure
3193
3375
 
3194
3376
  USAGE
3195
3377
  emailr <command> [subcommand] [options]
@@ -3202,6 +3384,8 @@ DESCRIPTION
3202
3384
  COMMANDS
3203
3385
  send Send individual emails with HTML/text content or templates
3204
3386
  inbox View and manage received (inbound) emails
3387
+ inboxes Create and manage email inboxes
3388
+ threads List, view, and manage email threads and labels
3205
3389
  templates Create, edit, preview, and publish email templates
3206
3390
  contacts Manage email contacts and their metadata
3207
3391
  segments Create and manage contact segments with conditions
@@ -3291,4 +3475,4 @@ AGENTIC WORKFLOW
3291
3475
 
3292
3476
  MORE INFORMATION
3293
3477
  Run 'emailr <command> --help' for detailed help on any command.
3294
- Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.8.0");S.addCommand(de());S.addCommand(Fe());S.addCommand(pe());S.addCommand(_e());S.addCommand(je());S.addCommand(Ne());S.addCommand(Ie());S.addCommand(Ce());S.addCommand(xe());S.addCommand(Ae());S.addCommand(Me());S.parse();
3478
+ Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.9.0");y.addCommand(ue());y.addCommand(Ke());y.addCommand(qe());y.addCommand($e());y.addCommand(fe());y.addCommand(je());y.addCommand(Ie());y.addCommand(Ce());y.addCommand(Ue());y.addCommand(Pe());y.addCommand(Ne());y.addCommand(Me());y.addCommand(Le());y.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emailr-cli",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Command-line interface for the Emailr email API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "chalk": "^5.3.0",
24
24
  "cli-table3": "^0.6.3",
25
25
  "commander": "^12.0.0",
26
- "emailr": "^1.3.4",
26
+ "emailr": "^1.4.0",
27
27
  "open": "^11.0.0"
28
28
  },
29
29
  "devDependencies": {