emailr-cli 1.12.0 → 1.12.1

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 +140 -456
  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 x,{readFileSync}from'fs';import V from'os';import E from'path';import de from'cli-table3';import T from'chalk';import at from'http';import {URL}from'url';import vt from'crypto';import {spawn,execSync,exec}from'child_process';var We=[E.join(V.homedir(),".emailrrc"),E.join(V.homedir(),".config","emailr","config.json")];function d(){if(process.env.EMAILR_API_KEY)return {apiKey:process.env.EMAILR_API_KEY,baseUrl:process.env.EMAILR_BASE_URL,format:process.env.EMAILR_FORMAT||"table"};for(let i of We)if(x.existsSync(i))try{let a=x.readFileSync(i,"utf-8"),e=JSON.parse(a);if(e.apiKey)return {apiKey:e.apiKey,baseUrl:e.baseUrl,format:e.format||"table"}}catch{}throw new Error(`No API key configured.
2
+ import {Command}from'commander';import {Emailr}from'emailr';import T,{readFileSync}from'fs';import V from'os';import E from'path';import de from'cli-table3';import x from'chalk';import at from'http';import {URL}from'url';import vt from'crypto';import {spawn,execSync,exec}from'child_process';var We=[E.join(V.homedir(),".emailrrc"),E.join(V.homedir(),".config","emailr","config.json")];function d(){if(process.env.EMAILR_API_KEY)return {apiKey:process.env.EMAILR_API_KEY,baseUrl:process.env.EMAILR_BASE_URL,format:process.env.EMAILR_FORMAT||"table"};for(let r of We)if(T.existsSync(r))try{let t=T.readFileSync(r,"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 I(){let i=E.join(V.homedir(),".config","emailr");return E.join(i,"config.json")}function K(i){let a=I(),e=E.dirname(a);x.existsSync(e)||x.mkdirSync(e,{recursive:true});let o={};if(x.existsSync(a))try{o=JSON.parse(x.readFileSync(a,"utf-8"));}catch{}let n={...o,...i};x.writeFileSync(a,JSON.stringify(n,null,2)+`
11
- `);}function ce(i){try{return d()[i]?.toString()}catch{return}}function m(i,a="table"){a==="json"?console.log(JSON.stringify(i,null,2)):Xe(i);}function Xe(i){Array.isArray(i)?Ve(i):typeof i=="object"&&i!==null?ze(i):console.log(i);}function Ve(i){if(i.length===0){console.log(T.gray("No results"));return}let a=i[0];if(typeof a!="object"||a===null){i.forEach(n=>console.log(n));return}let e=Object.keys(a),o=new de({head:e.map(n=>T.cyan(n)),style:{head:[],border:[]}});for(let n of i){let t=e.map(r=>{let s=n[r];return pe(s)});o.push(t);}console.log(o.toString());}function ze(i){let a=new de({style:{head:[],border:[]}});for(let[e,o]of Object.entries(i))a.push([T.cyan(e),pe(o)]);console.log(a.toString());}function pe(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 fe(){return new Command("send").description(`Send an email
10
+ Or run: emailr config set api-key <your-api-key>`)}function I(){let r=E.join(V.homedir(),".config","emailr");return E.join(r,"config.json")}function G(r){let t=I(),e=E.dirname(t);T.existsSync(e)||T.mkdirSync(e,{recursive:true});let i={};if(T.existsSync(t))try{i=JSON.parse(T.readFileSync(t,"utf-8"));}catch{}let n={...i,...r};T.writeFileSync(t,JSON.stringify(n,null,2)+`
11
+ `);}function ce(r){try{return d()[r]?.toString()}catch{return}}function m(r,t="table"){t==="json"?console.log(JSON.stringify(r,null,2)):Xe(r);}function Xe(r){Array.isArray(r)?Ve(r):typeof r=="object"&&r!==null?ze(r):console.log(r);}function Ve(r){if(r.length===0){console.log(x.gray("No results"));return}let t=r[0];if(typeof t!="object"||t===null){r.forEach(n=>console.log(n));return}let e=Object.keys(t),i=new de({head:e.map(n=>x.cyan(n)),style:{head:[],border:[]}});for(let n of r){let a=e.map(o=>{let s=n[o];return pe(s)});i.push(a);}console.log(i.toString());}function ze(r){let t=new de({style:{head:[],border:[]}});for(let[e,i]of Object.entries(r))t.push([x.cyan(e),pe(i)]);console.log(t.toString());}function pe(r){return r==null?x.gray("-"):typeof r=="boolean"?r?x.green("\u2713"):x.red("\u2717"):typeof r=="object"?JSON.stringify(r):String(r)}function p(r){console.log(x.green("\u2713"),r);}function l(r){console.error(x.red("\u2717"),r);}function C(r){console.warn(x.yellow("\u26A0"),r);}function u(r){console.log(x.blue("\u2139"),r);}function fe(){return new Command("send").description(`Send an email
12
12
 
13
13
  USAGE
14
14
  emailr send --to <email_address> [options]
@@ -156,7 +156,7 @@ EXAMPLES
156
156
  SEE ALSO
157
157
  emailr templates Manage email templates
158
158
  emailr contacts Manage contacts
159
- emailr broadcasts Send bulk emails to segments`).requiredOption("--to <email>","Recipient email address (comma-separated for multiple)").option("--from <email>","Sender email address").option("--subject <subject>","Email subject").option("--html <html>","HTML content (inline)").option("--text <text>","Plain text content (inline)").option("--html-file <path>","Read HTML content from file").option("--text-file <path>","Read plain text content from file").option("--template <id>","Template ID to use").option("--template-data <json>","Template data as JSON").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--reply-to <email>","Reply-to email address").option("--schedule <datetime>","Schedule send time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=a.to.split(",").map(s=>s.trim()),t={to:n.length===1?n[0]:n};if(a.from&&(t.from=a.from),a.subject&&(t.subject=a.subject),a.htmlFile)try{t.html=readFileSync(a.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${a.htmlFile}`),process.exit(1);}else a.html&&(t.html=a.html);if(a.textFile)try{t.text=readFileSync(a.textFile,"utf-8");}catch{l(`Failed to read text file: ${a.textFile}`),process.exit(1);}else a.text&&(t.text=a.text);if(a.template&&(t.template_id=a.template),a.templateData)try{t.template_data=JSON.parse(a.templateData);}catch{l("Invalid JSON for --template-data"),process.exit(1);}if(a.cc){let s=a.cc.split(",").map(c=>c.trim());t.cc=s.length===1?s[0]:s;}if(a.bcc){let s=a.bcc.split(",").map(c=>c.trim());t.bcc=s.length===1?s[0]:s;}a.replyTo&&(t.reply_to_email=a.replyTo),a.schedule&&(t.scheduled_at=a.schedule);let r=await o.emails.send(t);a.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 be(){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=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=t.to.split(",").map(s=>s.trim()),a={to:n.length===1?n[0]:n};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(c=>c.trim());a.cc=s.length===1?s[0]:s;}if(t.bcc){let s=t.bcc.split(",").map(c=>c.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 o=await i.emails.send(a);t.format==="json"?m(o,"json"):(p("Email sent successfully!"),m({"Message ID":o.message_id,Recipients:o.recipients,Status:o.status,...o.scheduled_at&&{"Scheduled At":o.scheduled_at}},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to send email"),process.exit(1);}})}function be(){let r=new Command("contacts").description(`Manage contacts
160
160
 
161
161
  USAGE
162
162
  emailr contacts <subcommand> [options]
@@ -238,7 +238,7 @@ EXAMPLES
238
238
 
239
239
  SEE ALSO
240
240
  emailr segments Manage contact segments
241
- emailr broadcasts Send emails to contacts`);return i.command("list").description(`List all contacts
241
+ emailr broadcasts Send emails to contacts`);return r.command("list").description(`List all contacts
242
242
 
243
243
  USAGE
244
244
  emailr contacts list [options]
@@ -286,8 +286,8 @@ EXAMPLES
286
286
  emailr contacts list --format json
287
287
 
288
288
  # Combine filters with pagination
289
- emailr contacts list --subscribed --limit 100 --offset 0`).option("--limit <number>","Number of contacts to return","20").option("--offset <number>","Offset for pagination","0").option("--subscribed","Only show subscribed contacts").option("--unsubscribed","Only show unsubscribed contacts").option("--search <query>","Search by email, first name, or last name").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={limit:parseInt(a.limit,10),offset:parseInt(a.offset,10)};if(a.subscribed&&(n.subscribed=!0),a.unsubscribed&&(n.subscribed=!1),a.search&&(n.search=a.search),a.tags){let r=a.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);r.length>0&&(n.tags=r.join(","));}let t=await o.contacts.list(n);if(a.format==="json")m(t,"json");else {let r=t.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(r,"table"),console.log(`
290
- Total: ${t.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
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=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={limit:parseInt(t.limit,10),offset:parseInt(t.offset,10)};if(t.subscribed&&(n.subscribed=!0),t.unsubscribed&&(n.subscribed=!1),t.search&&(n.search=t.search),t.tags){let o=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);o.length>0&&(n.tags=o.join(","));}let a=await i.contacts.list(n);if(t.format==="json")m(a,"json");else {let o=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(o,"table"),console.log(`
290
+ Total: ${a.total}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list contacts"),process.exit(1);}}),r.command("get <contact_id>").description(`Get a contact by ID
291
291
 
292
292
  USAGE
293
293
  emailr contacts get <contact_id> [options]
@@ -314,7 +314,7 @@ EXAMPLES
314
314
  emailr contacts get con_abc123 --format json
315
315
 
316
316
  # Pipe JSON to jq for processing
317
- emailr contacts get con_abc123 --format json | jq '.metadata'`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).contacts.get(a);m(t,e.format);}catch(o){l(o instanceof Error?o.message:"Failed to get contact"),process.exit(1);}}),i.command("create").description(`Create a new contact
317
+ emailr contacts get con_abc123 --format json | jq '.metadata'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).contacts.get(t);m(a,e.format);}catch(i){l(i instanceof Error?i.message:"Failed to get contact"),process.exit(1);}}),r.command("create").description(`Create a new contact
318
318
 
319
319
  USAGE
320
320
  emailr contacts create --email <email_address> [options]
@@ -357,7 +357,7 @@ EXAMPLES
357
357
  emailr contacts create --email "user@example.com" --subscribed false
358
358
 
359
359
  # Get JSON output for scripting
360
- emailr contacts create --email "user@example.com" --format json`).requiredOption("--email <email>","Contact email address").option("--first-name <name>","First name").option("--last-name <name>","Last name").option("--subscribed","Mark as subscribed (default: true)").option("--metadata <json>","Metadata as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={email:a.email};if(a.firstName&&(n.first_name=a.firstName),a.lastName&&(n.last_name=a.lastName),a.subscribed!==void 0&&(n.subscribed=a.subscribed),a.metadata)try{n.metadata=JSON.parse(a.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}a.tags&&(n.tags=a.tags.split(",").map(r=>r.trim().toLowerCase()).filter(Boolean));let t=await o.contacts.create(n);a.format==="json"?m(t,"json"):(p(`Contact created: ${t.id}`),m(t,"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=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={email:t.email};if(t.firstName&&(n.first_name=t.firstName),t.lastName&&(n.last_name=t.lastName),t.subscribed!==void 0&&(n.subscribed=t.subscribed),t.metadata)try{n.metadata=JSON.parse(t.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}t.tags&&(n.tags=t.tags.split(",").map(o=>o.trim().toLowerCase()).filter(Boolean));let a=await i.contacts.create(n);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);}}),r.command("update <contact_id>").description(`Update a contact
361
361
 
362
362
  USAGE
363
363
  emailr contacts update <contact_id> [options]
@@ -406,7 +406,7 @@ EXAMPLES
406
406
  emailr contacts update con_abc123 --metadata '{"plan": "enterprise", "upgraded": true}'
407
407
 
408
408
  # Get JSON output
409
- emailr contacts update con_abc123 --first-name "Jane" --format json`).option("--email <email>","New email address").option("--first-name <name>","First name").option("--last-name <name>","Last name").option("--subscribed","Mark as subscribed").option("--unsubscribed","Mark as unsubscribed").option("--metadata <json>","Metadata as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t={};if(e.email&&(t.email=e.email),e.firstName&&(t.first_name=e.firstName),e.lastName&&(t.last_name=e.lastName),e.subscribed&&(t.subscribed=!0),e.unsubscribed&&(t.subscribed=!1),e.metadata)try{t.metadata=JSON.parse(e.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}e.tags&&(t.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let r=await n.contacts.update(a,t);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 i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.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 o=await n.contacts.update(t,a);e.format==="json"?m(o,"json"):(p(`Contact updated: ${o.id}`),m(o,"table"));}catch(i){l(i instanceof Error?i.message:"Failed to update contact"),process.exit(1);}}),r.command("delete <contact_id>").description(`Delete a contact
410
410
 
411
411
  USAGE
412
412
  emailr contacts delete <contact_id>
@@ -423,7 +423,7 @@ EXAMPLES
423
423
  emailr contacts delete con_abc123
424
424
 
425
425
  WARNING
426
- This action is permanent and cannot be undone.`).action(async a=>{try{let e=d();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).contacts.delete(a),p(`Contact deleted: ${a}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete contact"),process.exit(1);}}),i}function ge(){return E.join(V.homedir(),".config","emailr","templates")}function tt(){let i=ge();x.existsSync(i)||x.mkdirSync(i,{recursive:true});}function z(i){return E.join(ge(),`${i}.html`)}function Y(i,a){tt();let e=z(i);x.writeFileSync(e,a,"utf-8");}function we(i){let a=z(i);return x.existsSync(a)?x.readFileSync(a,"utf-8"):null}function ye(i){let a=z(i);return x.existsSync(a)}var v=null,k=null,q=false;function ve(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function ot(i){return i===null||i.trim()===""}function it(i){return `<!DOCTYPE html>
426
+ This action is permanent and cannot be undone.`).action(async t=>{try{let e=d();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);}}),r}function ge(){return E.join(V.homedir(),".config","emailr","templates")}function tt(){let r=ge();T.existsSync(r)||T.mkdirSync(r,{recursive:true});}function z(r){return E.join(ge(),`${r}.html`)}function Y(r,t){tt();let e=z(r);T.writeFileSync(e,t,"utf-8");}function ye(r){let t=z(r);return T.existsSync(t)?T.readFileSync(t,"utf-8"):null}function we(r){let t=z(r);return T.existsSync(t)}var v=null,P=null,H=false;function ve(r){return r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function it(r){return r===null||r.trim()===""}function ot(r){return `<!DOCTYPE html>
427
427
  <html lang="en">
428
428
  <head>
429
429
  <meta charset="UTF-8">
@@ -489,14 +489,14 @@ WARNING
489
489
  <div class="icon">\u{1F4ED}</div>
490
490
  <h1>No Content Available</h1>
491
491
  <p>This template exists but has no HTML content to display.</p>
492
- <div class="template-id">${ve(i)}</div>
492
+ <div class="template-id">${ve(r)}</div>
493
493
  <div class="hint">
494
494
  <p><strong>To add content:</strong></p>
495
495
  <p>Update the template using the CLI with the <code>--html</code> option or provide HTML content when creating the template.</p>
496
496
  </div>
497
497
  </div>
498
498
  </body>
499
- </html>`}function Se(i){return `<!DOCTYPE html>
499
+ </html>`}function Se(r){return `<!DOCTYPE html>
500
500
  <html lang="en">
501
501
  <head>
502
502
  <meta charset="UTF-8">
@@ -548,11 +548,11 @@ WARNING
548
548
  <div class="icon">404</div>
549
549
  <h1>Template Not Found</h1>
550
550
  <p>The requested template could not be found in local storage.</p>
551
- <div class="template-id">${ve(i)}</div>
551
+ <div class="template-id">${ve(r)}</div>
552
552
  <p style="margin-top: 1rem;">Try creating or retrieving the template first using the CLI.</p>
553
553
  </div>
554
554
  </body>
555
- </html>`}function rt(i){let a=i.match(/^\/preview\/([^/]+)$/);return a?a[1]:null}function nt(){return (i,a)=>{if(i.method!=="GET"){a.writeHead(405,{"Content-Type":"text/plain"}),a.end("Method Not Allowed");return}let e=i.url||"/",o=rt(e);if(!o){a.writeHead(404,{"Content-Type":"text/plain"}),a.end("Not Found");return}if(!ye(o)){a.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),a.end(Se(o));return}let n=we(o);if(n===null){a.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),a.end(Se(o));return}if(ot(n)){a.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),a.end(it(o));return}a.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),a.end(n);}}var Te={async start(){return q&&k!==null?k:new Promise((i,a)=>{v=at.createServer(nt()),v.listen(0,"127.0.0.1",()=>{let e=v.address();e&&typeof e=="object"?(k=e.port,q=true,v.unref(),i(k)):a(new Error("Failed to get server address"));}),v.on("error",e=>{q=false,k=null,v=null,a(new Error(`Failed to start preview server: ${e.message}`));});})},getPort(){return k},isRunning(){return q},async stop(){if(v)return new Promise(i=>{v.close(()=>{v=null,k=null,q=false,i();});})}};function xe(){return Te}async function Z(i){try{return `http://127.0.0.1:${await Te.start()}/preview/${i}`}catch{return null}}function Oe(){v&&v.ref();}var P=null,Q=null,W=null,M=[],_e=`
555
+ </html>`}function rt(r){let t=r.match(/^\/preview\/([^/]+)$/);return t?t[1]:null}function nt(){return (r,t)=>{if(r.method!=="GET"){t.writeHead(405,{"Content-Type":"text/plain"}),t.end("Method Not Allowed");return}let e=r.url||"/",i=rt(e);if(!i){t.writeHead(404,{"Content-Type":"text/plain"}),t.end("Not Found");return}if(!we(i)){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(Se(i));return}let n=ye(i);if(n===null){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(Se(i));return}if(it(n)){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(ot(i));return}t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(n);}}var xe={async start(){return H&&P!==null?P:new Promise((r,t)=>{v=at.createServer(nt()),v.listen(0,"127.0.0.1",()=>{let e=v.address();e&&typeof e=="object"?(P=e.port,H=true,v.unref(),r(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(r=>{v.close(()=>{v=null,P=null,H=false,r();});})}};function Te(){return xe}async function Z(r){try{return `http://127.0.0.1:${await xe.start()}/preview/${r}`}catch{return null}}function Oe(){v&&v.ref();}var A=null,Q=null,W=null,k=[],_e=`
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 mt(i){return i.includes("</body>")?i.replace("</body>",`${_e}</body>`):i+_e}function ct(){M.forEach(i=>{try{i.write(`data: reload
569
+ `;function mt(r){return r.includes("</body>")?r.replace("</body>",`${_e}</body>`):r+_e}function ct(){k.forEach(r=>{try{r.write(`data: reload
570
570
 
571
- `);}catch{}});}async function ee(i,a){let e=E.resolve(i);return new Promise((o,n)=>{P=at.createServer((t,r)=>{if(t.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 ee(r,t){let e=E.resolve(r);return new Promise((i,n)=>{A=at.createServer((a,o)=>{if(a.url==="/__live-reload"){o.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),o.write(`data: connected
572
572
 
573
- `),M.push(r),t.on("close",()=>{M=M.filter(s=>s!==r);});return}if(t.method==="GET"){try{let s=x.readFileSync(e,"utf-8"),c=mt(s);r.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),r.end(c);}catch(s){r.writeHead(500,{"Content-Type":"text/plain"}),r.end(`Error reading file: ${s instanceof Error?s.message:String(s)}`);}return}r.writeHead(405,{"Content-Type":"text/plain"}),r.end("Method Not Allowed");}),P.listen(0,"127.0.0.1",()=>{let t=P.address();if(t&&typeof t=="object"){Q=t.port;let r=null;W=x.watch(e,s=>{s==="change"&&(r&&clearTimeout(r),r=setTimeout(()=>{ct(),a?.();},100));}),o(Q);}else n(new Error("Failed to get server address"));}),P.on("error",t=>{n(new Error(`Failed to start server: ${t.message}`));});})}async function te(){if(W&&(W.close(),W=null),M.forEach(i=>{try{i.end();}catch{}}),M=[],P)return new Promise(i=>{P.close(()=>{P=null,Q=null,i();});})}async function je(i){try{let a=i.html_content??"";Y(i.id,a);}catch(a){return N(`Could not save template for preview: ${a instanceof Error?a.message:String(a)}`),null}try{let a=await Z(i.id);return a===null?(N("Could not start preview server"),null):a}catch(a){return N(`Could not generate preview URL: ${a instanceof Error?a.message:String(a)}`),null}}function Ie(){let i=new Command("templates").description(`Manage email templates
573
+ `),k.push(o),a.on("close",()=>{k=k.filter(s=>s!==o);});return}if(a.method==="GET"){try{let s=T.readFileSync(e,"utf-8"),c=mt(s);o.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),o.end(c);}catch(s){o.writeHead(500,{"Content-Type":"text/plain"}),o.end(`Error reading file: ${s instanceof Error?s.message:String(s)}`);}return}o.writeHead(405,{"Content-Type":"text/plain"}),o.end("Method Not Allowed");}),A.listen(0,"127.0.0.1",()=>{let a=A.address();if(a&&typeof a=="object"){Q=a.port;let o=null;W=T.watch(e,s=>{s==="change"&&(o&&clearTimeout(o),o=setTimeout(()=>{ct(),t?.();},100));}),i(Q);}else n(new Error("Failed to get server address"));}),A.on("error",a=>{n(new Error(`Failed to start server: ${a.message}`));});})}async function te(){if(W&&(W.close(),W=null),k.forEach(r=>{try{r.end();}catch{}}),k=[],A)return new Promise(r=>{A.close(()=>{A=null,Q=null,r();});})}async function je(r){try{let t=r.html_content??"";Y(r.id,t);}catch(t){return C(`Could not save template for preview: ${t instanceof Error?t.message:String(t)}`),null}try{let t=await Z(r.id);return t===null?(C("Could not start preview server"),null):t}catch(t){return C(`Could not generate preview URL: ${t instanceof Error?t.message:String(t)}`),null}}function Ie(){let r=new Command("templates").description(`Manage email templates
574
574
 
575
575
  USAGE
576
576
  emailr templates <subcommand> [options]
@@ -641,7 +641,7 @@ AGENTIC WORKFLOW
641
641
 
642
642
  SEE ALSO
643
643
  emailr send Send emails using templates
644
- emailr broadcasts Send bulk emails to segments`);return i.command("list").description(`List all templates
644
+ emailr broadcasts Send bulk emails to segments`);return r.command("list").description(`List all templates
645
645
 
646
646
  USAGE
647
647
  emailr templates list [options]
@@ -671,8 +671,8 @@ EXAMPLES
671
671
  emailr templates list --page 2 --limit 10
672
672
 
673
673
  # Get JSON output for scripting
674
- emailr templates list --format json`).option("--limit <count>","Number of templates to return","20").option("--page <page_number>","Page number for pagination","1").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format: json | table","table").action(async a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={limit:parseInt(a.limit,10),page:parseInt(a.page,10)};if(a.tags){let r=a.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);r.length>0&&(n.tags=r.join(","));}let t=await o.templates.list(n);if(a.format==="json")m(t,"json");else {let r=t.map(s=>({ID:s.id,Name:s.name,Subject:s.subject,Variables:s.variables?.join(", ")||"-",Tags:s.tags?.join(", ")||"-",Created:s.created_at}));m(r,"table"),console.log(`
675
- Total: ${t.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
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=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={limit:parseInt(t.limit,10),page:parseInt(t.page,10)};if(t.tags){let o=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);o.length>0&&(n.tags=o.join(","));}let a=await i.templates.list(n);if(t.format==="json")m(a,"json");else {let o=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(o,"table"),console.log(`
675
+ Total: ${a.length}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list templates"),process.exit(1);}}),r.command("get <template_id>").description(`Get a template by ID
676
676
 
677
677
  USAGE
678
678
  emailr templates get <template_id> [options]
@@ -700,7 +700,7 @@ EXAMPLES
700
700
  emailr templates get tpl_abc123 --format json
701
701
 
702
702
  # Pipe JSON to jq for processing
703
- emailr templates get tpl_abc123 --format json | jq '.html_content'`).option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).templates.get(a),r=await je({id:t.id,html_content:t.html_content??void 0}),s=t.preview_html?`${o.baseUrl}/preview/${t.id}`:null;if(e.format==="json")m({...t,preview_url:s??r},"json");else {let c={ID:t.id,Name:t.name,Subject:t.subject,Variables:t.variables?.join(", ")||"-",Created:t.created_at};s?c["Preview URL"]=s:r&&(c["Local Preview"]=r),m(c,"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 i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).templates.get(t),o=await je({id:a.id,html_content:a.html_content??void 0}),s=a.preview_html?`${i.baseUrl}/preview/${a.id}`:null;if(e.format==="json")m({...a,preview_url:s??o},"json");else {let c={ID:a.id,Name:a.name,Subject:a.subject,Variables:a.variables?.join(", ")||"-",Created:a.created_at};s?c["Preview URL"]=s:o&&(c["Local Preview"]=o),m(c,"table");}}catch(i){l(i instanceof Error?i.message:"Failed to get template"),process.exit(1);}}),r.command("fetch <template_id>").description(`Download template HTML to file or stdout
704
704
 
705
705
  USAGE
706
706
  emailr templates fetch <template_id> [options]
@@ -734,8 +734,8 @@ AGENTIC WORKFLOW
734
734
  This is step 1 of the agentic workflow:
735
735
  1. Fetch: emailr templates fetch <id> --output template.html
736
736
  2. Edit locally or with AI assistance
737
- 3. Push preview: emailr templates push-preview <id> --html-file template.html`).option("--output <file_path>","Write HTML to file (default: stdout)").option("--preview","Fetch preview HTML instead of published HTML").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).templates.fetch(a,{preview:e.preview??!1});if(t||(e.preview?(l("No preview HTML exists for this template"),console.log(`
738
- Run 'emailr templates push-preview <id> --html-file <path>' to create a preview.`)):l("Template has no HTML content"),process.exit(1)),e.output){let r=E.resolve(e.output);x.writeFileSync(r,t,"utf-8"),p(`HTML saved to: ${r}`);}else console.log(t);}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
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 i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.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 o=E.resolve(e.output);T.writeFileSync(o,a,"utf-8"),p(`HTML saved to: ${o}`);}else console.log(a);}catch(i){l(i instanceof Error?i.message:"Failed to fetch template"),process.exit(1);}}),r.command("push-preview <template_id>").description(`Upload HTML to template's preview for sharing with AI agents
739
739
 
740
740
  USAGE
741
741
  emailr templates push-preview <template_id> --html-file <file_path>
@@ -778,9 +778,9 @@ AGENTIC WORKFLOW
778
778
  2. Edit locally or with AI assistance
779
779
  3. Push preview: emailr templates push-preview <id> --html-file template.html
780
780
  4. Share the preview URL with your AI agent for feedback
781
- 5. Repeat steps 2-4 until satisfied`).option("--html-file <file_path>","Read HTML content from file").option("--html <html_content>","Inline HTML content").option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{!e.htmlFile&&!e.html&&(l("Either --html-file or --html is required"),console.log(`
782
- Usage:`),console.log(" emailr templates push-preview <id> --html-file <path>"),console.log(" emailr templates push-preview <id> --html <content>"),process.exit(1)),e.htmlFile&&e.html&&(l("Cannot use both --html-file and --html. Choose one."),process.exit(1));let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t;if(e.htmlFile){let c=E.resolve(e.htmlFile);try{t=x.readFileSync(c,"utf-8");}catch{l(`Failed to read file: ${c}`),process.exit(1);}}else t=e.html;let r=await n.templates.pushPreview(a,t),s="updated";e.format==="json"?m({template_id:a,preview_url:r.preview_url,status:s},"json"):(p("Preview uploaded successfully"),m({"Template ID":a,"Preview URL":r.preview_url,Status:s},"table"),console.log(`
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
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 i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),a;if(e.htmlFile){let c=E.resolve(e.htmlFile);try{a=T.readFileSync(c,"utf-8");}catch{l(`Failed to read file: ${c}`),process.exit(1);}}else a=e.html;let o=await n.templates.pushPreview(t,a),s="updated";e.format==="json"?m({template_id:t,preview_url:o.preview_url,status:s},"json"):(p("Preview uploaded successfully"),m({"Template ID":t,"Preview URL":o.preview_url,Status:s},"table"),console.log(`
783
+ Share this URL with your AI agent for feedback.`));}catch(i){l(i instanceof Error?i.message:"Failed to push preview"),process.exit(1);}}),r.command("create").description(`Create a new email template
784
784
 
785
785
  USAGE
786
786
  emailr templates create --name <template_name> --subject <subject_line> [options]
@@ -828,8 +828,8 @@ EXAMPLES
828
828
 
829
829
  TIP
830
830
  For live preview while building, use "emailr templates draft" first.
831
- It creates a local file with hot-reload preview, then use this command to save.`).requiredOption("--name <template_name>","Template name").requiredOption("--subject <subject_line>","Email subject line").option("--html <html_content>","Inline HTML content").option("--text <text_content>","Plain text content").option("--html-file <file_path>","Read HTML content from file").option("--text-file <file_path>","Read text content from file").option("--from <email_address>","Default from email address").option("--from-name <name>","Sender display name").option("--reply-to <email_address>","Default reply-to email address").option("--preview-text <text>","Preview text shown in email clients").option("--inbox-id <inbox_id>","Inbox ID for sender identity defaults").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={name:a.name,subject:a.subject};if(a.htmlFile){let s=await import('fs');n.html_content=s.readFileSync(a.htmlFile,"utf-8");}else a.html&&(n.html_content=a.html);if(a.textFile){let s=await import('fs');n.text_content=s.readFileSync(a.textFile,"utf-8");}else a.text&&(n.text_content=a.text);a.from&&(n.from_email=a.from),a.fromName&&(n.from_name=a.fromName),a.replyTo&&(n.reply_to=a.replyTo),a.previewText&&(n.preview_text=a.previewText),a.inboxId&&(n.inbox_id=a.inboxId),a.tags&&(n.tags=a.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let t=await o.templates.create(n),r=await je({id:t.id,html_content:t.html_content??void 0});if(a.format==="json")m({...t,preview_url:r},"json");else {p(`Template created: ${t.id}`);let s={ID:t.id,Name:t.name,Subject:t.subject,Variables:t.variables?.join(", ")||"-",Tags:t.tags?.join(", ")||"-"};r&&(s["Preview URL"]=r),m(s,"table"),r&&console.log(`
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
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=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={name:t.name,subject:t.subject};if(t.htmlFile){let s=await import('fs');n.html_content=s.readFileSync(t.htmlFile,"utf-8");}else t.html&&(n.html_content=t.html);if(t.textFile){let s=await import('fs');n.text_content=s.readFileSync(t.textFile,"utf-8");}else t.text&&(n.text_content=t.text);t.from&&(n.from_email=t.from),t.fromName&&(n.from_name=t.fromName),t.replyTo&&(n.reply_to=t.replyTo),t.previewText&&(n.preview_text=t.previewText),t.inboxId&&(n.inbox_id=t.inboxId),t.tags&&(n.tags=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let a=await i.templates.create(n),o=await je({id:a.id,html_content:a.html_content??void 0});if(t.format==="json")m({...a,preview_url:o},"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(", ")||"-"};o&&(s["Preview URL"]=o),m(s,"table"),o&&console.log(`
832
+ Open the Preview URL in your browser to view the rendered template.`);}}catch(e){l(e instanceof Error?e.message:"Failed to create template"),process.exit(1);}}),r.command("update <template_id>").description(`Update an existing email template
833
833
 
834
834
  USAGE
835
835
  emailr templates update <template_id> [options]
@@ -884,7 +884,7 @@ AGENTIC WORKFLOW
884
884
  2. Edit locally or with AI assistance
885
885
  3. Push preview: emailr templates push-preview <id> --html-file template.html
886
886
  4. Share URL with AI agent, iterate on feedback
887
- 5. Publish: emailr templates update <id> --html-file template.html`).option("--name <template_name>","New template name").option("--subject <subject_line>","New email subject line").option("--html <html_content>","New inline HTML content").option("--text <text_content>","New plain text content").option("--html-file <file_path>","Read new HTML content from file").option("--text-file <file_path>","Read new text content from file").option("--from <email_address>","New default from email address").option("--from-name <name>","New sender display name").option("--reply-to <email_address>","New default reply-to email address").option("--preview-text <text>","New preview text").option("--inbox-id <inbox_id>",'Inbox ID for sender identity (use "none" to clear)').option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t={};if(e.name&&(t.name=e.name),e.subject&&(t.subject=e.subject),e.htmlFile){let s=await import('fs');t.html_content=s.readFileSync(e.htmlFile,"utf-8");}else e.html&&(t.html_content=e.html);if(e.textFile){let s=await import('fs');t.text_content=s.readFileSync(e.textFile,"utf-8");}else e.text&&(t.text_content=e.text);e.from&&(t.from_email=e.from),e.fromName&&(t.from_name=e.fromName),e.replyTo&&(t.reply_to=e.replyTo),e.previewText&&(t.preview_text=e.previewText),e.inboxId&&(t.inbox_id=e.inboxId==="none"?null:e.inboxId),e.tags&&(t.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let r=await n.templates.update(a,t);if(e.format==="json")m(r,"json");else {p(`Template updated: ${r.id}`);let s={ID:r.id,Name:r.name,Subject:r.subject,Variables:r.variables?.join(", ")||"-",Tags:r.tags?.join(", ")||"-",Updated:r.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
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 i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.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 o=await n.templates.update(t,a);if(e.format==="json")m(o,"json");else {p(`Template updated: ${o.id}`);let s={ID:o.id,Name:o.name,Subject:o.subject,Variables:o.variables?.join(", ")||"-",Tags:o.tags?.join(", ")||"-",Updated:o.updated_at};m(s,"table");}}catch(i){l(i instanceof Error?i.message:"Failed to update template"),process.exit(1);}}),r.command("delete <template_id>").description(`Delete a template
888
888
 
889
889
  USAGE
890
890
  emailr templates delete <template_id>
@@ -901,7 +901,7 @@ EXAMPLES
901
901
  emailr templates delete tpl_abc123
902
902
 
903
903
  WARNING
904
- This action is permanent and cannot be undone.`).action(async a=>{try{let e=d();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).templates.delete(a),p(`Template deleted: ${a}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete template"),process.exit(1);}}),i.command("preview <template_id>").description(`Preview a template in the browser
904
+ This action is permanent and cannot be undone.`).action(async t=>{try{let e=d();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).templates.delete(t),p(`Template deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete template"),process.exit(1);}}),r.command("preview <template_id>").description(`Preview a template in the browser
905
905
 
906
906
  USAGE
907
907
  emailr templates preview <template_id> [options]
@@ -930,12 +930,12 @@ EXAMPLES
930
930
 
931
931
  SEE ALSO
932
932
  emailr templates edit Live editing with hot-reload
933
- emailr templates stop-preview Stop background preview server`).option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl});console.log(`Fetching template ${a}...`);let t=await n.templates.get(a),r=t.html_content??"";if(Y(t.id,r),e.foreground){let w=await Z(t.id);if(w||(l("Failed to start preview server"),process.exit(1)),Oe(),console.log(`
934
- Template: ${t.name}`),console.log(`Preview URL: ${w}`),e.open!==!1)try{await(await import('open')).default(w),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 i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl});console.log(`Fetching template ${t}...`);let a=await n.templates.get(t),o=a.html_content??"";if(Y(a.id,o),e.foreground){let y=await Z(a.id);if(y||(l("Failed to start preview server"),process.exit(1)),Oe(),console.log(`
934
+ Template: ${a.name}`),console.log(`Preview URL: ${y}`),e.open!==!1)try{await(await import('open')).default(y),console.log(`
935
935
  Browser opened.`);}catch{console.log(`
936
936
  Could not open browser automatically. Open the URL above manually.`);}process.on("SIGINT",async()=>{console.log(`
937
937
 
938
- Stopping preview server...`),await xe().stop(),console.log("Done."),process.exit(0);});return}let s=E.join(process.cwd(),`.template-preview-${t.id}.html`);x.writeFileSync(s,r,"utf-8");let{spawn:c}=await import('child_process'),b=await import('os'),g=E.join(b.tmpdir(),"emailr-preview.pid");try{let w=x.readFileSync(g,"utf-8").trim();process.kill(parseInt(w,10),"SIGTERM");}catch{}let f=`
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`);T.writeFileSync(s,o,"utf-8");let{spawn:c}=await import('child_process'),b=await import('os'),g=E.join(b.tmpdir(),"emailr-preview.pid");try{let y=T.readFileSync(g,"utf-8").trim();process.kill(parseInt(y,10),"SIGTERM");}catch{}let f=`
939
939
  const http = require('http');
940
940
  const fs = require('fs');
941
941
  const path = require('path');
@@ -960,9 +960,9 @@ Stopping preview server...`),await xe().stop(),console.log("Done."),process.exit
960
960
  console.log('PORT:' + port);
961
961
  });
962
962
  process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); fs.unlinkSync(filePath); } catch {} process.exit(0); });
963
- `,h=c("node",["-e",f],{detached:!0,stdio:["ignore","pipe","ignore"]}),S="";await new Promise(w=>{h.stdout?.on("data",j=>{let B=j.toString().match(/PORT:(\d+)/);B&&(S=B[1],w());}),setTimeout(w,3e3);}),h.unref();let _=`http://127.0.0.1:${S}/`;if(console.log(`
964
- Template: ${t.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
963
+ `,h=c("node",["-e",f],{detached:!0,stdio:["ignore","pipe","ignore"]}),S="";await new Promise(y=>{h.stdout?.on("data",j=>{let B=j.toString().match(/PORT:(\d+)/);B&&(S=B[1],y());}),setTimeout(y,3e3);}),h.unref();let _=`http://127.0.0.1:${S}/`;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(i){l(i instanceof Error?i.message:"Failed to preview template"),process.exit(1);}}),r.command("edit <template_id>").description(`Start a live editing session for a template with hot-reload
966
966
 
967
967
  USAGE
968
968
  emailr templates edit <template_id> [options]
@@ -1002,17 +1002,17 @@ EXAMPLES
1002
1002
  SEE ALSO
1003
1003
  emailr templates draft Draft a new template with live preview
1004
1004
  emailr templates update Save changes to the template
1005
- emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for editing","./template.html").option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t=E.resolve(e.file);console.log(`Fetching template ${a}...`);let r=await n.templates.get(a),s=r.html_content??"";if(x.writeFileSync(t,s,"utf-8"),console.log(`Template saved to: ${t}`),e.foreground){let j=`http://127.0.0.1:${await ee(t,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1006
- Template: ${r.name}`),console.log(`Template ID: ${r.id}`),console.log(`Live Preview: ${j}`),console.log(`File: ${t}`),console.log(`
1007
- Watching for changes... Edit the file and see live updates.`),console.log(`When done, run: emailr templates update ${a} --html-file ${e.file}`),e.open!==!1)try{await(await import('open')).default(j);}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 i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),a=E.resolve(e.file);console.log(`Fetching template ${t}...`);let o=await n.templates.get(t),s=o.html_content??"";if(T.writeFileSync(a,s,"utf-8"),console.log(`Template saved to: ${a}`),e.foreground){let j=`http://127.0.0.1:${await ee(a,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1006
+ Template: ${o.name}`),console.log(`Template ID: ${o.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(`
1008
1008
 
1009
1009
  Stopping live preview server...`),await te(),console.log("Done."),console.log(`
1010
- To save changes: emailr templates update ${a} --html-file ${e.file}`),process.exit(0);});return}let{spawn:c}=await import('child_process'),b=await import('os'),g=E.join(b.tmpdir(),"emailr-preview.pid");try{let w=x.readFileSync(g,"utf-8").trim();process.kill(parseInt(w,10),"SIGTERM");}catch{}let f=`
1010
+ To save changes: emailr templates update ${t} --html-file ${e.file}`),process.exit(0);});return}let{spawn:c}=await import('child_process'),b=await import('os'),g=E.join(b.tmpdir(),"emailr-preview.pid");try{let y=T.readFileSync(g,"utf-8").trim();process.kill(parseInt(y,10),"SIGTERM");}catch{}let f=`
1011
1011
  const http = require('http');
1012
1012
  const fs = require('fs');
1013
1013
  const path = require('path');
1014
1014
  const os = require('os');
1015
- const filePath = ${JSON.stringify(t)};
1015
+ const filePath = ${JSON.stringify(a)};
1016
1016
  const pidFile = path.join(os.tmpdir(), 'emailr-preview.pid');
1017
1017
  const portFile = path.join(os.tmpdir(), 'emailr-preview.port');
1018
1018
  let clients = [];
@@ -1047,10 +1047,10 @@ To save changes: emailr templates update ${a} --html-file ${e.file}`),process.ex
1047
1047
  });
1048
1048
  });
1049
1049
  process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); } catch {} process.exit(0); });
1050
- `,h=c("node",["-e",f],{detached:!0,stdio:["ignore","pipe","ignore"]}),S="";await new Promise(w=>{h.stdout?.on("data",j=>{let me=j.toString().match(/PORT:(\d+)/);me&&(S=me[1],w());}),setTimeout(w,3e3);}),h.unref();let _=`http://127.0.0.1:${S}/`;if(console.log(`
1051
- Template: ${r.name}`),console.log(`Template ID: ${r.id}`),console.log(`Live Preview: ${_}`),console.log(`File: ${t}`),console.log(`
1050
+ `,h=c("node",["-e",f],{detached:!0,stdio:["ignore","pipe","ignore"]}),S="";await new Promise(y=>{h.stdout?.on("data",j=>{let me=j.toString().match(/PORT:(\d+)/);me&&(S=me[1],y());}),setTimeout(y,3e3);}),h.unref();let _=`http://127.0.0.1:${S}/`;if(console.log(`
1051
+ Template: ${o.name}`),console.log(`Template ID: ${o.id}`),console.log(`Live Preview: ${_}`),console.log(`File: ${a}`),console.log(`
1052
1052
  Edit the file and see live updates in the browser.`),console.log(`
1053
- When done:`),console.log(` emailr templates update ${a} --html-file ${e.file}`),console.log(" emailr templates stop-preview"),e.open!==!1)try{await(await import('open')).default(_);}catch{}process.exit(0);}catch(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(i){l(i instanceof Error?i.message:"Failed to edit template"),process.exit(1);}}),r.command("draft").description(`Start a live drafting session for a new template with hot-reload
1054
1054
 
1055
1055
  USAGE
1056
1056
  emailr templates draft [options]
@@ -1092,7 +1092,7 @@ EXAMPLES
1092
1092
  SEE ALSO
1093
1093
  emailr templates edit Edit an existing template with live preview
1094
1094
  emailr templates create Create the template from your draft
1095
- emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for drafting","./new-template.html").option("--no-open","Do not automatically open browser").option("--blank","Start with a blank file instead of starter template").option("--foreground","Keep process running in foreground (blocks terminal)").action(async a=>{try{let e=E.resolve(a.file),o;if(a.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),i;if(t.blank?i="":i=`<!DOCTYPE html>
1096
1096
  <html>
1097
1097
  <head>
1098
1098
  <meta charset="UTF-8">
@@ -1127,13 +1127,13 @@ SEE ALSO
1127
1127
  <p><a href="{{unsubscribe_link}}">Unsubscribe</a></p>
1128
1128
  </div>
1129
1129
  </body>
1130
- </html>`,x.writeFileSync(e,o,"utf-8"),console.log(`Template draft created: ${e}`),a.foreground){let h=`http://127.0.0.1:${await ee(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1130
+ </html>`,T.writeFileSync(e,i,"utf-8"),console.log(`Template draft created: ${e}`),t.foreground){let h=`http://127.0.0.1:${await ee(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1131
1131
  Live Preview: ${h}`),console.log(`File: ${e}`),console.log(`
1132
1132
  Watching for changes... Edit the file and see live updates.`),console.log(`
1133
- When done, create the template:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${a.file}`),a.open!==!1)try{await(await import('open')).default(h);}catch{}process.on("SIGINT",async()=>{console.log(`
1133
+ When done, create the template:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),t.open!==!1)try{await(await import('open')).default(h);}catch{}process.on("SIGINT",async()=>{console.log(`
1134
1134
 
1135
1135
  Stopping live preview server...`),await te(),console.log("Done."),console.log(`
1136
- To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${a.file}`),process.exit(0);});return}let{spawn:n}=await import('child_process'),t=await import('os'),r=E.join(t.tmpdir(),"emailr-preview.pid");try{let f=x.readFileSync(r,"utf-8").trim();process.kill(parseInt(f,10),"SIGTERM");}catch{}let s=`
1136
+ To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),process.exit(0);});return}let{spawn:n}=await import('child_process'),a=await import('os'),o=E.join(a.tmpdir(),"emailr-preview.pid");try{let f=T.readFileSync(o,"utf-8").trim();process.kill(parseInt(f,10),"SIGTERM");}catch{}let s=`
1137
1137
  const http = require('http');
1138
1138
  const fs = require('fs');
1139
1139
  const path = require('path');
@@ -1176,7 +1176,7 @@ To create template: emailr templates create --name "Template Name" --subject "Em
1176
1176
  `,c=n("node",["-e",s],{detached:!0,stdio:["ignore","pipe","ignore"]}),b="";await new Promise(f=>{c.stdout?.on("data",h=>{let S=h.toString().match(/PORT:(\d+)/);S&&(b=S[1],f());}),setTimeout(f,3e3);}),c.unref();let g=`http://127.0.0.1:${b}/`;if(console.log(`
1177
1177
  Live Preview: ${g}`),console.log(`File: ${e}`),console.log(`
1178
1178
  Edit the file and see live updates in the browser.`),console.log(`
1179
- When done:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${a.file}`),console.log(" emailr templates stop-preview"),a.open!==!1)try{await(await import('open')).default(g);}catch{}process.exit(0);}catch(e){l(e instanceof Error?e.message:"Failed to start draft session"),process.exit(1);}}),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(g);}catch{}process.exit(0);}catch(e){l(e instanceof Error?e.message:"Failed to start draft session"),process.exit(1);}}),r.command("stop-preview").description(`Stop any running background preview server
1180
1180
 
1181
1181
  USAGE
1182
1182
  emailr templates stop-preview
@@ -1193,7 +1193,7 @@ EXAMPLES
1193
1193
  SEE ALSO
1194
1194
  emailr templates edit Start live editing session
1195
1195
  emailr templates draft Start live drafting session
1196
- emailr templates preview Preview a template in browser`).action(async()=>{let a=await import('os'),e=E.join(a.tmpdir(),"emailr-preview.pid");try{let o=x.readFileSync(e,"utf-8").trim();process.kill(parseInt(o,10),"SIGTERM"),x.unlinkSync(e),p("Preview server stopped");}catch{p("No preview server running");}}),i}function Ne(){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 i=T.readFileSync(e,"utf-8").trim();process.kill(parseInt(i,10),"SIGTERM"),T.unlinkSync(e),p("Preview server stopped");}catch{p("No preview server running");}}),r}function Ce(){let r=new Command("domains").description(`Manage sending domains
1197
1197
 
1198
1198
  USAGE
1199
1199
  emailr domains <subcommand> [options]
@@ -1274,7 +1274,7 @@ VERIFICATION STATUS
1274
1274
 
1275
1275
  SEE ALSO
1276
1276
  emailr send Send emails using verified domains
1277
- emailr webhooks Receive domain verification events`);return i.command("list").description(`List all domains
1277
+ emailr webhooks Receive domain verification events`);return r.command("list").description(`List all domains
1278
1278
 
1279
1279
  USAGE
1280
1280
  emailr domains list [options]
@@ -1301,7 +1301,7 @@ EXAMPLES
1301
1301
  emailr domains list --format json | jq '.[] | select(.status == "verified")'
1302
1302
 
1303
1303
  # Count domains
1304
- emailr domains list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),n=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.list();if(a.format==="json")m(n,"json");else {let t=n.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(t,"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=d(),n=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.list();if(t.format==="json")m(n,"json");else {let a=n.map(o=>({ID:o.id,Domain:o.domain,Status:o.status,DKIM:o.dkim_verified,SPF:o.spf_verified,DMARC:o.dmarc_verified,Created:o.created_at}));m(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list domains"),process.exit(1);}}),r.command("get <domain_id>").description(`Get domain details
1305
1305
 
1306
1306
  USAGE
1307
1307
  emailr domains get <domain_id> [options]
@@ -1328,7 +1328,7 @@ EXAMPLES
1328
1328
  emailr domains get dom_abc123 --format json
1329
1329
 
1330
1330
  # Extract DNS records
1331
- emailr domains get dom_abc123 --format json | jq '.dns_records'`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.get(a);m(t,e.format);}catch(o){l(o instanceof Error?o.message:"Failed to get domain"),process.exit(1);}}),i.command("add <domain_name>").description(`Add a new domain
1331
+ emailr domains get dom_abc123 --format json | jq '.dns_records'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).domains.get(t);m(a,e.format);}catch(i){l(i instanceof Error?i.message:"Failed to get domain"),process.exit(1);}}),r.command("add <domain_name>").description(`Add a new domain
1332
1332
 
1333
1333
  USAGE
1334
1334
  emailr domains add <domain_name> [options]
@@ -1381,7 +1381,7 @@ NEXT STEPS
1381
1381
  1. Copy the DNS records shown in the output
1382
1382
  2. Add them to your DNS provider (Cloudflare, Route53, etc.)
1383
1383
  3. Wait for DNS propagation (up to 48 hours)
1384
- 4. Run: emailr domains verify <domain_id>`).option("--receiving-subdomain <subdomain>","Subdomain for receiving emails").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t={domain:a};e.receivingSubdomain&&(t.receivingSubdomain=e.receivingSubdomain);let r=await n.domains.add(t);if(e.format==="json")m(r,"json");else {if(p(`Domain added: ${r.domain}`),u("Add the following DNS records to verify your domain:"),console.log(""),r.dns_records){let s=[{Type:"DKIM",...ae(r.dns_records.dkim)},{Type:"SPF",...ae(r.dns_records.spf)},{Type:"DMARC",...ae(r.dns_records.dmarc)}];m(s,"table");}console.log(""),u(`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 i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),a={domain:t};e.receivingSubdomain&&(a.receivingSubdomain=e.receivingSubdomain);let o=await n.domains.add(a);if(e.format==="json")m(o,"json");else {if(p(`Domain added: ${o.domain}`),u("Add the following DNS records to verify your domain:"),console.log(""),o.dns_records){let s=[{Type:"DKIM",...ae(o.dns_records.dkim)},{Type:"SPF",...ae(o.dns_records.spf)},{Type:"DMARC",...ae(o.dns_records.dmarc)}];m(s,"table");}console.log(""),u(`Run 'emailr domains verify ${o.id}' after adding DNS records`);}}catch(i){l(i instanceof Error?i.message:"Failed to add domain"),process.exit(1);}}),r.command("verify <domain_id>").description(`Verify a domain
1385
1385
 
1386
1386
  USAGE
1387
1387
  emailr domains verify <domain_id> [options]
@@ -1419,7 +1419,7 @@ TROUBLESHOOTING
1419
1419
  1. Run 'check-dns' to see which records are missing
1420
1420
  2. Verify records are correctly configured with your DNS provider
1421
1421
  3. Wait for DNS propagation (can take up to 48 hours)
1422
- 4. Try verification again`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.verify(a);e.format==="json"?m(t,"json"):t.verified?p("Domain verified successfully!"):(u(`Domain status: ${t.status}`),t.dkim_status&&u(`DKIM status: ${t.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 i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.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(i){l(i instanceof Error?i.message:"Failed to verify domain"),process.exit(1);}}),r.command("check-dns <domain_id>").description(`Check DNS records for a domain
1423
1423
 
1424
1424
  USAGE
1425
1425
  emailr domains check-dns <domain_id> [options]
@@ -1466,7 +1466,7 @@ COMMON ISSUES
1466
1466
 
1467
1467
  Multiple records found:
1468
1468
  - Remove duplicate TXT records
1469
- - Keep only the Emailr-specific record`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.checkDns(a);if(e.format==="json")m(t,"json");else {let r=Object.entries(t).map(([s,c])=>({Record:s,Verified:c.verified,Expected:c.expected||"-",Found:c.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 i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).domains.checkDns(t);if(e.format==="json")m(a,"json");else {let o=Object.entries(a).map(([s,c])=>({Record:s,Verified:c.verified,Expected:c.expected||"-",Found:c.found?.join(", ")||"-"}));m(o,"table");}}catch(i){l(i instanceof Error?i.message:"Failed to check DNS"),process.exit(1);}}),r.command("delete <domain_id>").description(`Delete a domain
1470
1470
 
1471
1471
  USAGE
1472
1472
  emailr domains delete <domain_id> [options]
@@ -1496,7 +1496,7 @@ WARNING
1496
1496
  This action is permanent and cannot be undone.
1497
1497
  - Emails from this domain will fail to send
1498
1498
  - DNS records can be removed from your DNS provider
1499
- - Re-add the domain if you need to restore functionality`).option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.delete(a),p(`Domain deleted: ${a}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete domain"),process.exit(1);}}),i}function ae(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 Ce(){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=d();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);}}),r}function ae(r){return {"Record Type":r.type,Name:r.name,Value:r.value.length>50?r.value.substring(0,47)+"...":r.value,...r.priority!==void 0&&{Priority:r.priority}}}function Ne(){let r=new Command("config").description(`Manage CLI configuration
1500
1500
 
1501
1501
  USAGE
1502
1502
  emailr config <subcommand> [options]
@@ -1559,7 +1559,7 @@ EXAMPLES
1559
1559
  emailr templates list
1560
1560
 
1561
1561
  SEE ALSO
1562
- emailr --help Show all available commands`);return i.command("set <key> <value>").description(`Set a configuration value
1562
+ emailr --help Show all available commands`);return r.command("set <key> <value>").description(`Set a configuration value
1563
1563
 
1564
1564
  USAGE
1565
1565
  emailr config set <key> <value>
@@ -1589,7 +1589,7 @@ EXAMPLES
1589
1589
 
1590
1590
  NOTE
1591
1591
  Environment variables (EMAILR_API_KEY, EMAILR_BASE_URL) take precedence
1592
- over config file values at runtime.`).action(async(a,e)=>{try{let o=["api-key","base-url","format"],n=a.toLowerCase();o.includes(n)||(l(`Invalid config key: ${a}`),u(`Valid keys: ${o.join(", ")}`),process.exit(1));let r={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[n];n==="format"&&!["json","table"].includes(e)&&(l(`Invalid format value: ${e}`),u("Valid formats: json, table"),process.exit(1)),K({[r]:e}),p(`Configuration saved: ${a} = ${n==="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
1592
+ over config file values at runtime.`).action(async(t,e)=>{try{let i=["api-key","base-url","format"],n=t.toLowerCase();i.includes(n)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${i.join(", ")}`),process.exit(1));let o={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[n];n==="format"&&!["json","table"].includes(e)&&(l(`Invalid format value: ${e}`),u("Valid formats: json, table"),process.exit(1)),G({[o]:e}),p(`Configuration saved: ${t} = ${n==="api-key"?"***":e}`),u(`Config file: ${I()}`);}catch(i){l(i instanceof Error?i.message:"Failed to save configuration"),process.exit(1);}}),r.command("get <key>").description(`Get a configuration value
1593
1593
 
1594
1594
  USAGE
1595
1595
  emailr config get <key>
@@ -1617,7 +1617,7 @@ EXAMPLES
1617
1617
  emailr config get format
1618
1618
 
1619
1619
  SEE ALSO
1620
- emailr config list Show all configuration values`).action(async a=>{try{let e=["api-key","base-url","format"],o=a.toLowerCase();e.includes(o)||(l(`Invalid config key: ${a}`),u(`Valid keys: ${e.join(", ")}`),process.exit(1));let t={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[o],r=ce(t);r?console.log(o==="api-key"?r.substring(0,8)+"..."+r.substring(r.length-4):r):u(`${a} 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"],i=t.toLowerCase();e.includes(i)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${e.join(", ")}`),process.exit(1));let a={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[i],o=ce(a);o?console.log(i==="api-key"?o.substring(0,8)+"..."+o.substring(o.length-4):o):u(`${t} is not set`);}catch(e){l(e instanceof Error?e.message:"Failed to get configuration"),process.exit(1);}}),r.command("list").description(`List all configuration values
1621
1621
 
1622
1622
  USAGE
1623
1623
  emailr config list [options]
@@ -1645,7 +1645,7 @@ EXAMPLES
1645
1645
 
1646
1646
  SEE ALSO
1647
1647
  emailr config get Get a single configuration value
1648
- emailr config path Show config file location`).option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),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(a.format==="json")m(o,"json");else {let n=Object.entries(o).map(([t,r])=>({Key:t,Value:r}));m(n,"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
1648
+ emailr config path Show config file location`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=d(),i={"api-key":e.apiKey?e.apiKey.substring(0,8)+"..."+e.apiKey.substring(e.apiKey.length-4):"(not set)","base-url":e.baseUrl||"(default)",format:e.format||"table"};if(t.format==="json")m(i,"json");else {let n=Object.entries(i).map(([a,o])=>({Key:a,Value:o}));m(n,"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));}}),r.command("path").description(`Show the configuration file path
1649
1649
 
1650
1650
  USAGE
1651
1651
  emailr config path
@@ -1672,7 +1672,7 @@ EXAMPLES
1672
1672
  open $(emailr config path)
1673
1673
 
1674
1674
  # View config file contents
1675
- cat $(emailr config path)`).action(()=>{console.log(I());}),i.command("init").description(`Initialize configuration interactively
1675
+ cat $(emailr config path)`).action(()=>{console.log(I());}),r.command("init").description(`Initialize configuration interactively
1676
1676
 
1677
1677
  USAGE
1678
1678
  emailr config init [options]
@@ -1705,7 +1705,7 @@ ALTERNATIVE: ENVIRONMENT VARIABLES
1705
1705
 
1706
1706
  SEE ALSO
1707
1707
  emailr config set Set individual configuration values
1708
- emailr config list View current configuration`).option("--api-key <key>","API key to use").option("--base-url <url>","Base URL for API").action(async a=>{try{a.apiKey?(K({apiKey:a.apiKey,baseUrl:a.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 Ue(){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);}}),r}function Ue(){let r=new Command("broadcasts").description(`Manage broadcast campaigns
1709
1709
 
1710
1710
  USAGE
1711
1711
  emailr broadcasts <subcommand> [options]
@@ -1810,7 +1810,7 @@ EXAMPLES
1810
1810
  SEE ALSO
1811
1811
  emailr templates Manage email templates
1812
1812
  emailr segments Manage contact segments
1813
- emailr contacts Manage individual contacts`);return i.command("list").description(`List all broadcasts
1813
+ emailr contacts Manage individual contacts`);return r.command("list").description(`List all broadcasts
1814
1814
 
1815
1815
  USAGE
1816
1816
  emailr broadcasts list [options]
@@ -1855,7 +1855,7 @@ EXAMPLES
1855
1855
  emailr broadcasts list --format json
1856
1856
 
1857
1857
  # Pipe JSON to jq for processing
1858
- emailr broadcasts list --format json | jq '.[] | select(.status == "scheduled")'`).option("--status <status>","Filter by status (draft, scheduled, sending, sent)").option("--limit <number>","Number of broadcasts to return","20").option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={status:a.status,limit:parseInt(a.limit)};if(a.tags){let r=a.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);r.length>0&&(n.tags=r.join(","));}let t=await o.broadcasts.list(n);if(a.format==="json")m(t,"json");else {if(t.length===0){console.log("No broadcasts found.");return}let r=t.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(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=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={status:t.status,limit:parseInt(t.limit)};if(t.tags){let o=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);o.length>0&&(n.tags=o.join(","));}let a=await i.broadcasts.list(n);if(t.format==="json")m(a,"json");else {if(a.length===0){console.log("No broadcasts found.");return}let o=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(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list broadcasts"),process.exit(1);}}),r.command("get <broadcast_id>").description(`Get broadcast details
1859
1859
 
1860
1860
  USAGE
1861
1861
  emailr broadcasts get <broadcast_id> [options]
@@ -1890,7 +1890,7 @@ EXAMPLES
1890
1890
  emailr broadcasts get brd_abc123 --format json
1891
1891
 
1892
1892
  # Pipe JSON to jq for processing
1893
- emailr broadcasts get brd_abc123 --format json | jq '{sent: .sent_count, opened: .opened_count}'`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.get(a);e.format==="json"?m(t,"json"):m({ID:t.id,Name:t.name,Subject:t.subject,"From Email":t.from_email,"From Name":t.from_name||"-",Status:t.status,Tags:t.tags?.join(", ")||"-","Total Recipients":t.total_recipients||0,"Sent Count":t.sent_count||0,Delivered:t.delivered_count||0,Opened:t.opened_count||0,Clicked:t.clicked_count||0,Bounced:t.bounced_count||0,"Scheduled At":t.scheduled_at||"N/A","Started At":t.started_at||"N/A","Completed At":t.completed_at||"N/A","Created At":t.created_at},"table");}catch(o){l(o instanceof Error?o.message:"Failed to get broadcast"),process.exit(1);}}),i.command("create").description(`Create a new broadcast
1893
+ emailr broadcasts get brd_abc123 --format json | jq '{sent: .sent_count, opened: .opened_count}'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.get(t);e.format==="json"?m(a,"json"):m({ID:a.id,Name:a.name,Subject:a.subject,"From Email":a.from_email,"From Name":a.from_name||"-",Status:a.status,Tags:a.tags?.join(", ")||"-","Total Recipients":a.total_recipients||0,"Sent Count":a.sent_count||0,Delivered:a.delivered_count||0,Opened:a.opened_count||0,Clicked:a.clicked_count||0,Bounced:a.bounced_count||0,"Scheduled At":a.scheduled_at||"N/A","Started At":a.started_at||"N/A","Completed At":a.completed_at||"N/A","Created At":a.created_at},"table");}catch(i){l(i instanceof Error?i.message:"Failed to get broadcast"),process.exit(1);}}),r.command("create").description(`Create a new broadcast
1894
1894
 
1895
1895
  USAGE
1896
1896
  emailr broadcasts create --name <name> --subject <subject> --from <email> [options]
@@ -1971,7 +1971,7 @@ EXAMPLES
1971
1971
 
1972
1972
  SEE ALSO
1973
1973
  emailr templates Create and manage email templates
1974
- emailr segments Create and manage contact segments`).requiredOption("--name <name>","Broadcast name").requiredOption("--subject <subject>","Email subject").requiredOption("--from <email>","Sender email address").option("--from-name <name>","Sender display name").option("--reply-to <email>","Reply-To email address").option("--preview-text <text>","Preview text (preheader)").option("--inbox-id <inbox_id>","Inbox ID for sender identity defaults").option("--inbox-ids <ids>","Comma-separated inbox IDs for inbox rotation").option("--sending-speed <speed>","Sending speed: auto | slow | normal | instant (default: auto)").option("--template <id>","Template ID to use").option("--segment <id>","Segment ID to target").option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>","Schedule time (ISO 8601)").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={name:a.name,subject:a.subject,from_email:a.from,from_name:a.fromName,reply_to:a.replyTo,preview_text:a.previewText,inbox_id:a.inboxId,template_id:a.template,segment_id:a.segment,html_content:a.html,text_content:a.text,scheduled_at:a.schedule};a.inboxIds&&(n.inbox_ids=a.inboxIds.split(",").map(r=>r.trim()).filter(Boolean)),a.sendingSpeed&&(n.sending_speed=a.sendingSpeed),a.tags&&(n.tags=a.tags.split(",").map(r=>r.trim().toLowerCase()).filter(Boolean));let t=await o.broadcasts.create(n);a.format==="json"?m(t,"json"):(p("Broadcast created successfully!"),m({ID:t.id,Name:t.name,Status:t.status,Tags:t.tags?.join(", ")||"-","Scheduled At":t.scheduled_at||"Not scheduled"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create broadcast"),process.exit(1);}}),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("--inbox-ids <ids>","Comma-separated inbox IDs for inbox rotation").option("--sending-speed <speed>","Sending speed: auto | slow | normal | instant (default: auto)").option("--template <id>","Template ID to use").option("--segment <id>","Segment ID to target").option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>","Schedule time (ISO 8601)").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={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.inboxIds&&(n.inbox_ids=t.inboxIds.split(",").map(o=>o.trim()).filter(Boolean)),t.sendingSpeed&&(n.sending_speed=t.sendingSpeed),t.tags&&(n.tags=t.tags.split(",").map(o=>o.trim().toLowerCase()).filter(Boolean));let a=await i.broadcasts.create(n);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);}}),r.command("send <broadcast_id>").description(`Send a broadcast immediately
1975
1975
 
1976
1976
  USAGE
1977
1977
  emailr broadcasts send <broadcast_id> [options]
@@ -2004,7 +2004,7 @@ EXAMPLES
2004
2004
  NOTE
2005
2005
  Sending is asynchronous. The command returns when sending starts,
2006
2006
  not when all emails are delivered. Use 'emailr broadcasts get' to
2007
- check delivery progress.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.send(a);e.format==="json"?m(t,"json"):(p("Broadcast sent successfully!"),m({Success:t.success,Sent:t.sent,Total:t.total},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to send broadcast"),process.exit(1);}}),i.command("schedule <broadcast_id>").description(`Schedule a broadcast for future delivery
2007
+ check delivery progress.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.send(t);e.format==="json"?m(a,"json"):(p("Broadcast sent successfully!"),m({Success:a.success,Sent:a.sent,Total:a.total},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to send broadcast"),process.exit(1);}}),r.command("schedule <broadcast_id>").description(`Schedule a broadcast for future delivery
2008
2008
 
2009
2009
  USAGE
2010
2010
  emailr broadcasts schedule <broadcast_id> --at <datetime> [options]
@@ -2054,7 +2054,7 @@ EXAMPLES
2054
2054
  emailr broadcasts schedule brd_abc123 --at "2024-12-25T10:00:00Z" --format json
2055
2055
 
2056
2056
  SEE ALSO
2057
- emailr broadcasts cancel Cancel a scheduled broadcast`).requiredOption("--at <datetime>","Schedule time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.schedule(a,e.at);e.format==="json"?m(t,"json"):(p("Broadcast scheduled successfully!"),m({ID:t.id,Name:t.name,Status:t.status,"Scheduled At":t.scheduled_at},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to schedule broadcast"),process.exit(1);}}),i.command("cancel <broadcast_id>").description(`Cancel a scheduled broadcast
2057
+ emailr broadcasts cancel Cancel a scheduled broadcast`).requiredOption("--at <datetime>","Schedule time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.schedule(t,e.at);e.format==="json"?m(a,"json"):(p("Broadcast scheduled successfully!"),m({ID:a.id,Name:a.name,Status:a.status,"Scheduled At":a.scheduled_at},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to schedule broadcast"),process.exit(1);}}),r.command("cancel <broadcast_id>").description(`Cancel a scheduled broadcast
2058
2058
 
2059
2059
  USAGE
2060
2060
  emailr broadcasts cancel <broadcast_id> [options]
@@ -2082,7 +2082,7 @@ EXAMPLES
2082
2082
  emailr broadcasts cancel brd_abc123 --format json
2083
2083
 
2084
2084
  NOTE
2085
- Broadcasts that are already 'sending' or 'sent' cannot be cancelled.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.cancel(a);e.format==="json"?m(t,"json"):p("Broadcast cancelled successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to cancel broadcast"),process.exit(1);}}),i.command("update <broadcast_id>").description(`Update a broadcast
2085
+ Broadcasts that are already 'sending' or 'sent' cannot be cancelled.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.cancel(t);e.format==="json"?m(a,"json"):p("Broadcast cancelled successfully!");}catch(i){l(i instanceof Error?i.message:"Failed to cancel broadcast"),process.exit(1);}}),r.command("update <broadcast_id>").description(`Update a broadcast
2086
2086
 
2087
2087
  USAGE
2088
2088
  emailr broadcasts update <broadcast_id> [options]
@@ -2135,7 +2135,7 @@ EXAMPLES
2135
2135
  emailr broadcasts update brd_abc123 --name "Updated" --format json
2136
2136
 
2137
2137
  NOTE
2138
- Only broadcasts in 'draft' or 'scheduled' status can be updated.`).option("--name <name>","Broadcast name").option("--subject <subject>","Email subject").option("--from <email>","Sender email address").option("--from-name <name>","Sender display name").option("--reply-to <email>","Reply-To email address").option("--preview-text <text>","Preview text (preheader)").option("--inbox-id <inbox_id>",'Inbox ID for sender identity (use "none" to clear)').option("--inbox-ids <ids>",'Comma-separated inbox IDs for rotation (use "none" to clear)').option("--sending-speed <speed>",'Sending speed: auto | slow | normal | instant (use "none" to clear)').option("--template <id>",'Template ID (use "none" to clear)').option("--segment <id>",'Segment ID (use "none" to clear)').option("--topic <id>",'Topic ID (use "none" to clear)').option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>",'Schedule time ISO 8601 (use "none" to clear)').option("--tags <tags>","Comma-separated tags (replaces existing)").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t={};e.name&&(t.name=e.name),e.subject&&(t.subject=e.subject),e.from&&(t.from_email=e.from),e.fromName&&(t.from_name=e.fromName),e.replyTo&&(t.reply_to=e.replyTo),e.previewText&&(t.preview_text=e.previewText),e.html&&(t.html_content=e.html),e.text&&(t.text_content=e.text),e.inboxId&&(t.inbox_id=e.inboxId==="none"?null:e.inboxId),e.inboxIds&&(t.inbox_ids=e.inboxIds==="none"?null:e.inboxIds.split(",").map(s=>s.trim()).filter(Boolean)),e.sendingSpeed&&(t.sending_speed=e.sendingSpeed==="none"?null:e.sendingSpeed),e.template&&(t.template_id=e.template==="none"?null:e.template),e.segment&&(t.segment_id=e.segment==="none"?null:e.segment),e.topic&&(t.topic_id=e.topic==="none"?null:e.topic),e.schedule&&(t.scheduled_at=e.schedule==="none"?null:e.schedule),e.tags&&(t.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean)),Object.keys(t).length===0&&(l("No update fields specified. Use --help to see available options."),process.exit(1));let r=await n.broadcasts.update(a,t);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("--inbox-ids <ids>",'Comma-separated inbox IDs for rotation (use "none" to clear)').option("--sending-speed <speed>",'Sending speed: auto | slow | normal | instant (use "none" to clear)').option("--template <id>",'Template ID (use "none" to clear)').option("--segment <id>",'Segment ID (use "none" to clear)').option("--topic <id>",'Topic ID (use "none" to clear)').option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>",'Schedule time ISO 8601 (use "none" to clear)').option("--tags <tags>","Comma-separated tags (replaces existing)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.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.inboxIds&&(a.inbox_ids=e.inboxIds==="none"?null:e.inboxIds.split(",").map(s=>s.trim()).filter(Boolean)),e.sendingSpeed&&(a.sending_speed=e.sendingSpeed==="none"?null:e.sendingSpeed),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 o=await n.broadcasts.update(t,a);e.format==="json"?m(o,"json"):(p("Broadcast updated successfully!"),m({ID:o.id,Name:o.name,Subject:o.subject,Status:o.status,Tags:o.tags?.join(", ")||"-","Scheduled At":o.scheduled_at||"Not scheduled"},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to update broadcast"),process.exit(1);}}),r.command("delete <broadcast_id>").description(`Delete a broadcast
2139
2139
 
2140
2140
  USAGE
2141
2141
  emailr broadcasts delete <broadcast_id> [options]
@@ -2163,391 +2163,76 @@ EXAMPLES
2163
2163
 
2164
2164
  WARNING
2165
2165
  This action is permanent and cannot be undone.
2166
- All delivery statistics will be lost.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.delete(a);e.format==="json"?m(t,"json"):p("Broadcast deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete broadcast"),process.exit(1);}}),i}function ke(){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 i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).broadcasts.delete(t);e.format==="json"?m(a,"json"):p("Broadcast deleted successfully!");}catch(i){l(i instanceof Error?i.message:"Failed to delete broadcast"),process.exit(1);}}),r}function Pe(){let r=new Command("webhooks").description(`Manage webhooks
2167
2167
 
2168
2168
  USAGE
2169
2169
  emailr webhooks <subcommand> [options]
2170
2170
 
2171
2171
  DESCRIPTION
2172
- Create, update, and manage webhook endpoints for receiving real-time
2173
- notifications about email and contact events. Webhooks allow you to
2174
- integrate Emailr with your own systems and automate workflows.
2172
+ Create and manage webhook endpoints for receiving real-time
2173
+ notifications about email, contact, and domain events.
2175
2174
 
2176
2175
  SUBCOMMANDS
2177
2176
  list List all webhooks
2178
- get <id> Get webhook details including secret
2179
2177
  create Create a new webhook endpoint
2180
- update <id> Update webhook name, URL, or events
2181
- enable <id> Enable a disabled webhook
2182
- disable <id> Disable a webhook (stops event delivery)
2178
+ toggle <id> Toggle webhook active state (enable/disable)
2183
2179
  delete <id> Delete a webhook permanently
2180
+ deliveries <id> List recent deliveries for a webhook
2184
2181
 
2185
- AVAILABLE EVENT TYPES
2186
- Email Events:
2187
- email.sent Email accepted for delivery
2188
- email.delivered Email successfully delivered to recipient
2189
- email.bounced Email permanently rejected by recipient server
2190
- email.complained Recipient marked email as spam
2191
- email.opened Recipient opened the email
2192
- email.clicked Recipient clicked a link in the email
2193
- email.failed Email delivery failed
2194
- email.delivery_delayed Email delivery temporarily delayed
2195
-
2196
- Contact Events:
2197
- contact.created New contact added to audience
2198
- contact.updated Contact information updated
2199
- contact.deleted Contact removed from audience
2200
-
2201
- Domain Events:
2202
- domain.verified Domain identity verified
2203
- domain.verification_failed Domain verification failed
2204
- domain.mail_from_verified MAIL FROM domain verified
2205
- domain.mail_from_failed MAIL FROM verification failed
2206
- domain.deactivated Domain deactivated
2207
-
2208
- OPTIONS
2209
- --format <format> Output format: json | table (default: table)
2210
-
2211
- OUTPUT FORMATS
2212
- --format json Machine-readable JSON output
2213
- --format table Human-readable table output (default)
2214
-
2215
- EVENTS OPTION FORMAT
2216
- The --events option accepts a comma-separated list of event types:
2217
-
2218
- Single event:
2219
- --events "email.sent"
2220
-
2221
- Multiple events:
2222
- --events "email.sent,email.delivered,email.bounced"
2223
-
2224
- All email events:
2225
- --events "email.sent,email.delivered,email.bounced,email.complained,email.opened,email.clicked,email.failed"
2226
-
2227
- Contact events:
2228
- --events "contact.created,contact.updated,contact.deleted"
2229
-
2230
- Mixed events:
2231
- --events "email.delivered,email.bounced,contact.created"
2182
+ WEBHOOK TYPES
2183
+ transactional Email lifecycle events (sent, delivered, bounced, etc.)
2184
+ domain Domain verification lifecycle events
2185
+ receiving Inbound email received events
2232
2186
 
2233
2187
  EXAMPLES
2234
2188
  # List all webhooks
2235
2189
  emailr webhooks list
2236
2190
 
2237
- # Get webhook details (includes secret for signature verification)
2238
- emailr webhooks get whk_abc123
2239
-
2240
2191
  # Create webhook for email delivery events
2241
2192
  emailr webhooks create --name "Delivery Tracker" \\
2242
2193
  --url "https://example.com/webhooks/emailr" \\
2243
- --events "email.sent,email.delivered,email.bounced"
2244
-
2245
- # Create webhook for all email tracking events
2246
- emailr webhooks create --name "Email Analytics" \\
2247
- --url "https://example.com/webhooks/analytics" \\
2248
- --events "email.delivered,email.opened,email.clicked,email.bounced"
2194
+ --type transactional
2249
2195
 
2250
- # Create webhook for contact changes
2251
- emailr webhooks create --name "CRM Sync" \\
2252
- --url "https://example.com/webhooks/crm" \\
2253
- --events "contact.created,contact.updated,contact.deleted"
2196
+ # Create webhook for inbound emails scoped to specific inboxes
2197
+ emailr webhooks create --name "Inbound" \\
2198
+ --url "https://example.com/webhooks/inbound" \\
2199
+ --type receiving --inbox-ids "inbox-uuid-1,inbox-uuid-2"
2254
2200
 
2255
- # Update webhook URL
2256
- emailr webhooks update whk_abc123 --url "https://new-url.com/webhook"
2201
+ # Toggle webhook on/off
2202
+ emailr webhooks toggle whk_abc123
2257
2203
 
2258
- # Update webhook events
2259
- emailr webhooks update whk_abc123 --events "email.sent,email.delivered"
2260
-
2261
- # Disable webhook temporarily
2262
- emailr webhooks disable whk_abc123
2263
-
2264
- # Re-enable webhook
2265
- emailr webhooks enable whk_abc123
2204
+ # View recent deliveries
2205
+ emailr webhooks deliveries whk_abc123
2266
2206
 
2267
2207
  # Delete webhook
2268
- emailr webhooks delete whk_abc123
2269
-
2270
- # Get JSON output for scripting
2271
- emailr webhooks list --format json
2272
-
2273
- WEBHOOK PAYLOAD
2274
- Webhooks receive POST requests with JSON payloads:
2275
-
2276
- {
2277
- "type": "email.delivered",
2278
- "created_at": "2024-02-22T23:41:12.126Z",
2279
- "data": {
2280
- "email_id": "em_abc123",
2281
- "to": "recipient@example.com",
2282
- "subject": "Your order confirmation"
2283
- }
2284
- }
2285
-
2286
- SIGNATURE VERIFICATION
2287
- Each webhook request includes an X-Emailr-Signature header.
2288
- Use the webhook secret (shown in 'get' command) to verify signatures.
2289
-
2290
- SEE ALSO
2291
- emailr domains Manage sending domains
2292
- emailr send Send emails directly`);return i.command("list").description(`List all webhooks
2293
-
2294
- USAGE
2295
- emailr webhooks list [options]
2296
-
2297
- DESCRIPTION
2298
- Retrieves a list of all webhook endpoints configured for your account.
2299
- Shows webhook ID, name, URL, subscribed events, and active status.
2300
-
2301
- OPTIONS
2302
- --format <format> Output format: json | table (default: table)
2208
+ emailr webhooks delete whk_abc123`);return r.command("list").description(`List all webhooks
2303
2209
 
2304
2210
  OUTPUT FORMATS
2305
2211
  --format json Machine-readable JSON array of webhook objects
2306
- --format table Human-readable table with columns: ID, Name, URL, Events, Active
2212
+ --format table Human-readable table with ID, Name, URL, Type, Events, Active`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=d(),n=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).webhooks.list();if(t.format==="json")m(n,"json");else {if(n.length===0){console.log("No webhooks found.");return}let a=n.map(o=>({ID:o.id,Name:o.name,URL:o.url.substring(0,40)+(o.url.length>40?"...":""),Type:o.type,Events:o.events.join(", "),Active:o.active?"Yes":"No"}));m(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list webhooks"),process.exit(1);}}),r.command("create").description(`Create a new webhook
2307
2213
 
2308
- EXAMPLES
2309
- # List all webhooks
2310
- emailr webhooks list
2311
-
2312
- # Get JSON output for scripting
2313
- emailr webhooks list --format json
2314
-
2315
- # Filter active webhooks with jq
2316
- emailr webhooks list --format json | jq '.[] | select(.active == true)'
2317
-
2318
- # Count webhooks
2319
- emailr webhooks list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),n=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).webhooks.list();if(a.format==="json")m(n,"json");else {if(n.data.length===0){console.log("No webhooks found.");return}let t=n.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(t,"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
2320
-
2321
- USAGE
2322
- emailr webhooks get <webhook_id> [options]
2323
-
2324
- DESCRIPTION
2325
- Retrieves detailed information about a specific webhook including
2326
- the webhook secret used for signature verification.
2327
-
2328
- ARGUMENTS
2329
- <webhook_id> The unique webhook identifier (e.g., whk_abc123)
2330
-
2331
- OPTIONS
2332
- --format <format> Output format: json | table (default: table)
2333
-
2334
- OUTPUT FORMATS
2335
- --format json Full webhook object with all fields including secret
2336
- --format table Summary table with webhook details
2337
-
2338
- WEBHOOK SECRET
2339
- The secret is used to verify webhook signatures. Each webhook request
2340
- includes an X-Emailr-Signature header that you can verify using this secret.
2341
-
2342
- EXAMPLES
2343
- # Get webhook details
2344
- emailr webhooks get whk_abc123
2345
-
2346
- # Get full webhook data as JSON
2347
- emailr webhooks get whk_abc123 --format json
2348
-
2349
- # Extract just the secret
2350
- emailr webhooks get whk_abc123 --format json | jq -r '.secret'`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.get(a);e.format==="json"?m(t,"json"):m({ID:t.id,Name:t.name,URL:t.url,Events:t.events.join(", "),Active:t.active?"Yes":"No",Secret:t.secret,"Created At":t.created_at},"table");}catch(o){l(o instanceof Error?o.message:"Failed to get webhook"),process.exit(1);}}),i.command("create").description(`Create a new webhook
2351
-
2352
- USAGE
2353
- emailr webhooks create --name <name> --url <url> --events <events> [options]
2354
-
2355
- DESCRIPTION
2356
- Creates a new webhook endpoint to receive real-time notifications
2357
- about email and contact events. The webhook is enabled by default.
2358
-
2359
- OPTIONS
2360
- --name <webhook_name> Webhook name for identification (required)
2361
- --url <endpoint_url> HTTPS URL to receive webhook POST requests (required)
2362
- --events <event_list> Comma-separated list of event types (required)
2363
- --format <format> Output format: json | table (default: table)
2364
-
2365
- AVAILABLE EVENT TYPES
2366
- Email Events:
2367
- email.sent Email accepted for delivery
2368
- email.delivered Email successfully delivered
2369
- email.bounced Email permanently rejected
2370
- email.complained Recipient marked as spam
2371
- email.opened Recipient opened email
2372
- email.clicked Recipient clicked link
2373
- email.failed Delivery failed
2374
- email.delivery_delayed Delivery temporarily delayed
2375
-
2376
- Contact Events:
2377
- contact.created New contact added
2378
- contact.updated Contact updated
2379
- contact.deleted Contact removed
2380
-
2381
- Domain Events:
2382
- domain.verified Domain verified
2383
- domain.verification_failed Verification failed
2384
- domain.mail_from_verified MAIL FROM verified
2385
- domain.mail_from_failed MAIL FROM failed
2386
- domain.deactivated Domain deactivated
2387
-
2388
- EVENTS FORMAT
2389
- Provide events as a comma-separated list (no spaces after commas):
2390
- --events "email.sent,email.delivered,email.bounced"
2214
+ WEBHOOK TYPES
2215
+ transactional Subscribes to: email.sent, email.delivered, email.bounced,
2216
+ email.complained, email.opened, email.clicked, email.failed
2217
+ domain Subscribes to: domain.verified, domain.verification_failed,
2218
+ domain.mail_from_verified, domain.mail_from_failed, domain.deactivated
2219
+ receiving Subscribes to: email.received (optionally scoped to inbox IDs)
2391
2220
 
2392
2221
  OUTPUT FORMATS
2393
2222
  --format json Full webhook object including generated secret
2394
- --format table Summary with ID, Name, URL, and Secret
2395
-
2396
- EXAMPLES
2397
- # Create webhook for delivery tracking
2398
- emailr webhooks create --name "Delivery Tracker" \\
2399
- --url "https://example.com/webhooks/emailr" \\
2400
- --events "email.sent,email.delivered,email.bounced"
2401
-
2402
- # Create webhook for email analytics
2403
- emailr webhooks create --name "Analytics" \\
2404
- --url "https://analytics.example.com/hook" \\
2405
- --events "email.delivered,email.opened,email.clicked"
2406
-
2407
- # Create webhook for CRM integration
2408
- emailr webhooks create --name "CRM Sync" \\
2409
- --url "https://crm.example.com/emailr" \\
2410
- --events "contact.created,contact.updated,contact.deleted"
2411
-
2412
- # Create webhook for bounce handling
2413
- emailr webhooks create --name "Bounce Handler" \\
2414
- --url "https://example.com/bounces" \\
2415
- --events "email.bounced,email.complained"
2416
-
2417
- # Get JSON output with secret
2418
- emailr webhooks create --name "Test" \\
2419
- --url "https://example.com/test" \\
2420
- --events "email.sent" --format json
2421
-
2422
- NOTE
2423
- Save the webhook secret returned by this command. You'll need it
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 a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n=a.events.split(",").map(r=>r.trim()),t=await o.webhooks.create({name:a.name,url:a.url,events:n});a.format==="json"?m(t,"json"):(p("Webhook created successfully!"),m({ID:t.id,Name:t.name,URL:t.url,Secret:t.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
2425
-
2426
- USAGE
2427
- emailr webhooks update <webhook_id> [options]
2428
-
2429
- DESCRIPTION
2430
- Updates an existing webhook's name, URL, or subscribed events.
2431
- Only provide the options you want to change.
2432
-
2433
- ARGUMENTS
2434
- <webhook_id> The unique webhook identifier (e.g., whk_abc123)
2435
-
2436
- OPTIONS
2437
- --name <webhook_name> New webhook name
2438
- --url <endpoint_url> New webhook URL
2439
- --events <event_list> New comma-separated list of events (replaces existing)
2440
- --format <format> Output format: json | table (default: table)
2441
-
2442
- EVENTS FORMAT
2443
- The --events option replaces all existing events:
2444
- --events "email.sent,email.delivered"
2445
-
2446
- To add events, first get current events, then include all in update.
2447
-
2448
- OUTPUT FORMATS
2449
- --format json Updated webhook object
2450
- --format table Summary with ID, Name, URL
2451
-
2452
- EXAMPLES
2453
- # Update webhook name
2454
- emailr webhooks update whk_abc123 --name "New Name"
2455
-
2456
- # Update webhook URL
2457
- emailr webhooks update whk_abc123 --url "https://new-url.com/webhook"
2458
-
2459
- # Update subscribed events
2460
- emailr webhooks update whk_abc123 --events "email.sent,email.delivered,email.bounced"
2461
-
2462
- # Update multiple fields
2463
- emailr webhooks update whk_abc123 \\
2464
- --name "Updated Webhook" \\
2465
- --url "https://new-url.com/hook" \\
2466
- --events "email.delivered,email.bounced"
2467
-
2468
- # Get JSON output
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(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t={};e.name&&(t.name=e.name),e.url&&(t.url=e.url),e.events&&(t.events=e.events.split(",").map(s=>s.trim()));let r=await n.webhooks.update(a,t);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
2470
-
2471
- USAGE
2472
- emailr webhooks enable <webhook_id> [options]
2473
-
2474
- DESCRIPTION
2475
- Enables a disabled webhook to resume receiving event notifications.
2476
- Use this after fixing issues with your webhook endpoint.
2477
-
2478
- ARGUMENTS
2479
- <webhook_id> The unique webhook identifier (e.g., whk_abc123)
2480
-
2481
- OPTIONS
2482
- --format <format> Output format: json | table (default: table)
2223
+ --format table Summary with ID, Name, URL, Type, and Secret`).requiredOption("--name <name>","Webhook name").requiredOption("--url <url>","Webhook URL (HTTPS)").requiredOption("--type <type>","Webhook type: transactional | domain | receiving").option("--inbox-ids <ids>","Comma-separated inbox UUIDs (only for receiving type)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=["transactional","domain","receiving"];e.includes(t.type)||(l(`Invalid type "${t.type}". Must be one of: ${e.join(", ")}`),process.exit(1));let i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),a={name:t.name,url:t.url,type:t.type};t.inboxIds&&(a.inbox_ids=t.inboxIds.split(",").map(s=>s.trim()));let o=await n.webhooks.create(a);t.format==="json"?m(o,"json"):(p("Webhook created successfully!"),m({ID:o.id,Name:o.name,URL:o.url,Type:o.type,Events:o.events.join(", "),Secret:o.secret},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create webhook"),process.exit(1);}}),r.command("toggle <webhook_id>").description(`Toggle webhook active state (enable/disable)
2483
2224
 
2484
2225
  OUTPUT FORMATS
2485
2226
  --format json Returns: { success: boolean, active: boolean }
2486
- --format table Human-readable success message
2487
-
2488
- EXAMPLES
2489
- # Enable a webhook
2490
- emailr webhooks enable whk_abc123
2491
-
2492
- # Get JSON output
2493
- emailr webhooks enable whk_abc123 --format json`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.enable(a);e.format==="json"?m(t,"json"):p("Webhook enabled successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to enable webhook"),process.exit(1);}}),i.command("disable <webhook_id>").description(`Disable a webhook
2494
-
2495
- USAGE
2496
- emailr webhooks disable <webhook_id> [options]
2497
-
2498
- DESCRIPTION
2499
- Disables a webhook to temporarily stop receiving event notifications.
2500
- Events that occur while disabled are not queued or retried.
2501
- Use 'enable' to resume receiving events.
2502
-
2503
- ARGUMENTS
2504
- <webhook_id> The unique webhook identifier (e.g., whk_abc123)
2505
-
2506
- OPTIONS
2507
- --format <format> Output format: json | table (default: table)
2227
+ --format table Human-readable success message`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).webhooks.toggle(t);e.format==="json"?m(a,"json"):p(`Webhook ${a.active?"enabled":"disabled"} successfully!`);}catch(i){l(i instanceof Error?i.message:"Failed to toggle webhook"),process.exit(1);}}),r.command("deliveries <webhook_id>").description(`List recent deliveries for a webhook
2508
2228
 
2509
2229
  OUTPUT FORMATS
2510
- --format json Returns: { success: boolean, active: boolean }
2511
- --format table Human-readable success message
2512
-
2513
- EXAMPLES
2514
- # Disable a webhook
2515
- emailr webhooks disable whk_abc123
2516
-
2517
- # Get JSON output
2518
- emailr webhooks disable whk_abc123 --format json
2519
-
2520
- NOTE
2521
- Events are not queued while webhook is disabled. If you need to
2522
- temporarily stop processing, consider handling this in your endpoint.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.disable(a);e.format==="json"?m(t,"json"):p("Webhook disabled successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to disable webhook"),process.exit(1);}}),i.command("delete <webhook_id>").description(`Delete a webhook
2523
-
2524
- USAGE
2525
- emailr webhooks delete <webhook_id> [options]
2526
-
2527
- DESCRIPTION
2528
- Permanently deletes a webhook endpoint. This action cannot be undone.
2529
- The webhook will immediately stop receiving events.
2530
-
2531
- ARGUMENTS
2532
- <webhook_id> The unique webhook identifier (e.g., whk_abc123)
2533
-
2534
- OPTIONS
2535
- --format <format> Output format: json | table (default: table)
2230
+ --format json Machine-readable JSON array of delivery objects
2231
+ --format table Human-readable table with ID, Event, Status, Attempts, Dates`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).webhooks.listDeliveries(t);if(e.format==="json")m(a,"json");else {if(a.length===0){console.log("No deliveries found.");return}let o=a.map(s=>({ID:s.id,Event:s.event_type,Status:s.response_status??"-",Attempts:s.attempt_count,"Delivered At":s.delivered_at??"-",Created:new Date(s.created_at).toLocaleString()}));m(o,"table");}}catch(i){l(i instanceof Error?i.message:"Failed to list deliveries"),process.exit(1);}}),r.command("delete <webhook_id>").description(`Delete a webhook permanently
2536
2232
 
2537
2233
  OUTPUT FORMATS
2538
2234
  --format json Returns: { success: boolean }
2539
- --format table Human-readable success message
2540
-
2541
- EXAMPLES
2542
- # Delete a webhook
2543
- emailr webhooks delete whk_abc123
2544
-
2545
- # Get JSON output
2546
- emailr webhooks delete whk_abc123 --format json
2547
-
2548
- WARNING
2549
- This action is permanent and cannot be undone.
2550
- Create a new webhook if you need to restore functionality.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.delete(a);e.format==="json"?m(t,"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
2235
+ --format table Human-readable success message`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).webhooks.delete(t);e.format==="json"?m(a,"json"):p("Webhook deleted successfully!");}catch(i){l(i instanceof Error?i.message:"Failed to delete webhook"),process.exit(1);}}),r}function Ae(){let r=new Command("segments").description(`Manage contact segments
2551
2236
 
2552
2237
  USAGE
2553
2238
  emailr segments <subcommand> [options]
@@ -2662,7 +2347,7 @@ EXAMPLES
2662
2347
 
2663
2348
  SEE ALSO
2664
2349
  emailr contacts Manage individual contacts
2665
- emailr broadcasts Send emails to segments`);return i.command("list").description(`List all segments
2350
+ emailr broadcasts Send emails to segments`);return r.command("list").description(`List all segments
2666
2351
 
2667
2352
  USAGE
2668
2353
  emailr segments list [options]
@@ -2687,7 +2372,7 @@ EXAMPLES
2687
2372
  emailr segments list --format json
2688
2373
 
2689
2374
  # Pipe JSON to jq for processing
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 a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={};if(a.tags){let r=a.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);r.length>0&&(n.tags=r.join(","));}let t=await o.segments.list(n);if(a.format==="json")m(t,"json");else {if(t.length===0){console.log("No segments found.");return}let r=t.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(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
2375
+ emailr segments list --format json | jq '.[].name'`).option("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={};if(t.tags){let o=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);o.length>0&&(n.tags=o.join(","));}let a=await i.segments.list(n);if(t.format==="json")m(a,"json");else {if(a.length===0){console.log("No segments found.");return}let o=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(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list segments"),process.exit(1);}}),r.command("get <segment_id>").description(`Get segment details
2691
2376
 
2692
2377
  USAGE
2693
2378
  emailr segments get <segment_id> [options]
@@ -2714,7 +2399,7 @@ EXAMPLES
2714
2399
  emailr segments get seg_abc123 --format json
2715
2400
 
2716
2401
  # Pipe JSON to jq to view conditions
2717
- emailr segments get seg_abc123 --format json | jq '.conditions'`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.get(a);e.format==="json"?m(t,"json"):m({ID:t.id,Name:t.name,Description:t.description||"N/A",Conditions:JSON.stringify(t.conditions),Tags:t.tags?.join(", ")||"-","Created At":t.created_at,"Updated At":t.updated_at},"table");}catch(o){l(o instanceof Error?o.message:"Failed to get segment"),process.exit(1);}}),i.command("create").description(`Create a new segment
2402
+ emailr segments get seg_abc123 --format json | jq '.conditions'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).segments.get(t);e.format==="json"?m(a,"json"):m({ID:a.id,Name:a.name,Description:a.description||"N/A",Conditions:JSON.stringify(a.conditions),Tags:a.tags?.join(", ")||"-","Created At":a.created_at,"Updated At":a.updated_at},"table");}catch(i){l(i instanceof Error?i.message:"Failed to get segment"),process.exit(1);}}),r.command("create").description(`Create a new segment
2718
2403
 
2719
2404
  USAGE
2720
2405
  emailr segments create --name <segment_name> --conditions <json> [options]
@@ -2764,7 +2449,7 @@ EXAMPLES
2764
2449
  # Get JSON output for scripting
2765
2450
  emailr segments create --name "Test" \\
2766
2451
  --conditions '[{"field": "subscribed", "operator": "equals", "value": true}]' \\
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 a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n;try{n=JSON.parse(a.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let t={name:a.name,description:a.description,conditions:n};a.tags&&(t.tags=a.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let r=await o.segments.create(t);a.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
2452
+ --format json`).requiredOption("--name <name>","Segment name").requiredOption("--conditions <json>","Segment conditions as JSON").option("--description <description>","Segment description").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n;try{n=JSON.parse(t.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let a={name:t.name,description:t.description,conditions:n};t.tags&&(a.tags=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let o=await i.segments.create(a);t.format==="json"?m(o,"json"):(p("Segment created successfully!"),m({ID:o.id,Name:o.name,Tags:o.tags?.join(", ")||"-"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create segment"),process.exit(1);}}),r.command("update <segment_id>").description(`Update a segment
2768
2453
 
2769
2454
  USAGE
2770
2455
  emailr segments update <segment_id> [options]
@@ -2810,7 +2495,7 @@ EXAMPLES
2810
2495
  --conditions '[{"field": "metadata.plan", "operator": "equals", "value": "enterprise"}]'
2811
2496
 
2812
2497
  # Get JSON output
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(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t={};if(e.name&&(t.name=e.name),e.description&&(t.description=e.description),e.conditions)try{t.conditions=JSON.parse(e.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}e.tags&&(t.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let r=await n.segments.update(a,t);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
2498
+ emailr segments update seg_abc123 --name "Updated" --format json`).option("--name <name>","New segment name").option("--description <description>","New description").option("--conditions <json>","New conditions as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.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 o=await n.segments.update(t,a);e.format==="json"?m(o,"json"):(p("Segment updated successfully!"),m({ID:o.id,Name:o.name,Tags:o.tags?.join(", ")||"-"},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to update segment"),process.exit(1);}}),r.command("delete <segment_id>").description(`Delete a segment
2814
2499
 
2815
2500
  USAGE
2816
2501
  emailr segments delete <segment_id>
@@ -2835,7 +2520,7 @@ EXAMPLES
2835
2520
 
2836
2521
  WARNING
2837
2522
  This action is permanent and cannot be undone.
2838
- Any broadcasts targeting this segment will need to be updated.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.delete(a);e.format==="json"?m(t,"json"):p("Segment deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete segment"),process.exit(1);}}),i.command("count <segment_id>").description(`Get the number of contacts in a segment
2523
+ Any broadcasts targeting this segment will need to be updated.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).segments.delete(t);e.format==="json"?m(a,"json"):p("Segment deleted successfully!");}catch(i){l(i instanceof Error?i.message:"Failed to delete segment"),process.exit(1);}}),r.command("count <segment_id>").description(`Get the number of contacts in a segment
2839
2524
 
2840
2525
  USAGE
2841
2526
  emailr segments count <segment_id> [options]
@@ -2866,7 +2551,7 @@ EXAMPLES
2866
2551
 
2867
2552
  TIP
2868
2553
  Use this before sending broadcasts to verify your segment
2869
- targets the expected number of contacts.`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.getContactsCount(a);e.format==="json"?m(t,"json"):console.log(`Segment contains ${t.count} contacts.`);}catch(o){l(o instanceof Error?o.message:"Failed to get segment count"),process.exit(1);}}),i}function yt(i){try{let a=i.startsWith("http")?i:`http://localhost${i}`,e=new URL(a);return {key:e.searchParams.get("key")??void 0,code:e.searchParams.get("code")??void 0,state:e.searchParams.get("state")??void 0,error:e.searchParams.get("error")??void 0,message:e.searchParams.get("message")??void 0}}catch{return {}}}function St(){return `<!DOCTYPE html>
2554
+ targets the expected number of contacts.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).segments.getContactsCount(t);e.format==="json"?m(a,"json"):console.log(`Segment contains ${a.count} contacts.`);}catch(i){l(i instanceof Error?i.message:"Failed to get segment count"),process.exit(1);}}),r}function wt(r){try{let t=r.startsWith("http")?r:`http://localhost${r}`,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 St(){return `<!DOCTYPE html>
2870
2555
  <html lang="en">
2871
2556
  <head>
2872
2557
  <meta charset="UTF-8">
@@ -2912,7 +2597,7 @@ TIP
2912
2597
  <p>You can close this window and return to your terminal.</p>
2913
2598
  </div>
2914
2599
  </body>
2915
- </html>`}function oe(i){return `<!DOCTYPE html>
2600
+ </html>`}function ie(r){return `<!DOCTYPE html>
2916
2601
  <html lang="en">
2917
2602
  <head>
2918
2603
  <meta charset="UTF-8">
@@ -2963,11 +2648,11 @@ TIP
2963
2648
  <div class="icon">\u2717</div>
2964
2649
  <h1>Login Failed</h1>
2965
2650
  <p>Something went wrong during authentication.</p>
2966
- <div class="error-message">${i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}</div>
2651
+ <div class="error-message">${r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}</div>
2967
2652
  <p style="margin-top: 1rem;">Please close this window and try again.</p>
2968
2653
  </div>
2969
2654
  </body>
2970
- </html>`}function Ae(){let i=null,a=0,e=null,o=null,n=null;return {async start(){return new Promise((t,r)=>{i=at.createServer((s,c)=>{if(s.method!=="GET"||!s.url?.startsWith("/callback")){c.writeHead(404,{"Content-Type":"text/plain"}),c.end("Not Found");return}let b=yt(s.url);if(n&&b.state!==n){c.writeHead(400,{"Content-Type":"text/html"}),c.end(oe("Security verification failed. State parameter mismatch.")),e&&e({success:false,error:"State parameter mismatch"});return}if(b.error){let f=b.message||b.error;c.writeHead(200,{"Content-Type":"text/html"}),c.end(oe(f)),e&&e({success:false,error:f});return}let g=b.key||b.code;if(g){c.writeHead(200,{"Content-Type":"text/html"}),c.end(St()),e&&e({success:true,apiKey:g});return}c.writeHead(400,{"Content-Type":"text/html"}),c.end(oe("Invalid callback: missing required parameters.")),e&&e({success:false,error:"Invalid callback: missing required parameters"});}),i.listen(0,"127.0.0.1",()=>{let s=i.address();s&&typeof s=="object"?(a=s.port,t({port:a,url:`http://127.0.0.1:${a}/callback`})):r(new Error("Failed to get server address"));}),i.on("error",s=>{r(new Error(`Failed to start callback server: ${s.message}`));});})},async waitForCallback(t,r){return n=t,new Promise(s=>{e=s,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(t=>{i.close(()=>{i=null,t();});})}}}function Re(){return vt.randomBytes(32).toString("hex")}function De(i){return new Promise(a=>{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,n=>{a(!n);});})}var X=120,Ot=process.env.EMAILR_WEB_URL||"https://app.emailr.dev";function Et(i,a){let e=`http://127.0.0.1:${a}/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(X)).option("--no-browser","Don't automatically open the browser").action(async a=>{await _t({timeout:parseInt(a.timeout,10)||X,noBrowser:a.browser===false});})}async function _t(i){let a=Ae(),e=(i.timeout||X)*1e3;try{u("Starting authentication server...");let{port:o,url:n}=await a.start(),t=Re(),r=Et(t,o);console.log(""),u("Authorization URL:"),console.log(` ${r}`),console.log(""),i.noBrowser?u("Please open the URL above in your browser to continue."):await De(r)?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||X}s)...`);let s=await a.waitForCallback(t,e);s.success&&s.apiKey?(K({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 a.stop();}}var re=E.join(V.homedir(),".config","opencode","skills","emailr-cli"),Ct=`---
2655
+ </html>`}function Re(){let r=null,t=0,e=null,i=null,n=null;return {async start(){return new Promise((a,o)=>{r=at.createServer((s,c)=>{if(s.method!=="GET"||!s.url?.startsWith("/callback")){c.writeHead(404,{"Content-Type":"text/plain"}),c.end("Not Found");return}let b=wt(s.url);if(n&&b.state!==n){c.writeHead(400,{"Content-Type":"text/html"}),c.end(ie("Security verification failed. State parameter mismatch.")),e&&e({success:false,error:"State parameter mismatch"});return}if(b.error){let f=b.message||b.error;c.writeHead(200,{"Content-Type":"text/html"}),c.end(ie(f)),e&&e({success:false,error:f});return}let g=b.key||b.code;if(g){c.writeHead(200,{"Content-Type":"text/html"}),c.end(St()),e&&e({success:true,apiKey:g});return}c.writeHead(400,{"Content-Type":"text/html"}),c.end(ie("Invalid callback: missing required parameters.")),e&&e({success:false,error:"Invalid callback: missing required parameters"});}),r.listen(0,"127.0.0.1",()=>{let s=r.address();s&&typeof s=="object"?(t=s.port,a({port:t,url:`http://127.0.0.1:${t}/callback`})):o(new Error("Failed to get server address"));}),r.on("error",s=>{o(new Error(`Failed to start callback server: ${s.message}`));});})},async waitForCallback(a,o){return n=a,new Promise(s=>{e=s,i=setTimeout(()=>{e&&e({success:false,error:"Login timed out. Please try again."});},o);})},async stop(){if(i&&(clearTimeout(i),i=null),r)return new Promise(a=>{r.close(()=>{r=null,a();});})}}}function De(){return vt.randomBytes(32).toString("hex")}function ke(r){return new Promise(t=>{let e=process.platform,i;switch(e){case "darwin":i=`open "${r}"`;break;case "win32":i=`start "" "${r}"`;break;default:i=`xdg-open "${r}"`;break}exec(i,n=>{t(!n);});})}var X=120,Ot=process.env.EMAILR_WEB_URL||"https://app.emailr.dev";function Et(r,t){let e=`http://127.0.0.1:${t}/callback`,i=new URLSearchParams({state:r,callback_url:e});return `${Ot}/consent/authorize?${i.toString()}`}function Me(){return new Command("login").description("Log in to Emailr via browser authentication").option("-t, --timeout <seconds>","Timeout in seconds",String(X)).option("--no-browser","Don't automatically open the browser").action(async t=>{await _t({timeout:parseInt(t.timeout,10)||X,noBrowser:t.browser===false});})}async function _t(r){let t=Re(),e=(r.timeout||X)*1e3;try{u("Starting authentication server...");let{port:i,url:n}=await t.start(),a=De(),o=Et(a,i);console.log(""),u("Authorization URL:"),console.log(` ${o}`),console.log(""),r.noBrowser?u("Please open the URL above in your browser to continue."):await ke(o)?u("Browser opened. Please complete authentication in your browser."):(C("Could not open browser automatically."),u("Please open the URL above in your browser to continue.")),console.log(""),u(`Waiting for authentication (timeout: ${r.timeout||X}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(i){console.log(""),l(i instanceof Error?i.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 re=E.join(V.homedir(),".config","opencode","skills","emailr-cli"),Nt=`---
2971
2656
  name: emailr-cli
2972
2657
  description: Operate the Emailr CLI to send emails, reply to threads, manage contacts, templates, inboxes, domains, broadcasts, webhooks, and segments. Includes thread reply auto-resolution and live preview editing for templates.
2973
2658
  ---
@@ -3083,7 +2768,7 @@ emailr domains add example.com
3083
2768
  emailr domains verify example.com
3084
2769
  emailr domains list
3085
2770
  \`\`\`
3086
- `;function Ut(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function kt(){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(){x.existsSync(re)||x.mkdirSync(re,{recursive:true});let i=E.join(re,"SKILL.md");x.writeFileSync(i,Ct,"utf-8");}function Fe(){return new Command("agent").description(`Launch an AI agent with Emailr CLI expertise
2771
+ `;function Ut(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function Pt(){console.log("Installing OpenCode AI agent...");try{return execSync("npm install -g opencode-ai@latest",{stdio:"inherit"}),!0}catch{if(process.platform==="darwin")try{return execSync("brew install anomalyco/tap/opencode",{stdio:"inherit"}),!0}catch{return false}return false}}function At(){T.existsSync(re)||T.mkdirSync(re,{recursive:true});let r=E.join(re,"SKILL.md");T.writeFileSync(r,Nt,"utf-8");}function Fe(){return new Command("agent").description(`Launch an AI agent with Emailr CLI expertise
3087
2772
 
3088
2773
  This opens OpenCode (opencode.ai), an AI coding agent that knows how to use all Emailr CLI commands.
3089
2774
  The agent can help you:
@@ -3091,10 +2776,10 @@ The agent can help you:
3091
2776
  - Manage contacts, broadcasts, and segments
3092
2777
  - Configure domains and webhooks
3093
2778
 
3094
- The agent runs in your terminal with full access to the emailr CLI.`).option("--install","Install OpenCode if not already installed").option("--model <model>","Model to use (e.g., anthropic/claude-sonnet-4)").action(async a=>{Ut()||(a.install?kt()||(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(`
3095
- 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(n){N(`Could not install skill: ${n instanceof Error?n.message:String(n)}`);}let e=[];a.model&&e.push("--model",a.model),console.log(`
2779
+ The agent runs in your terminal with full access to the emailr CLI.`).option("--install","Install OpenCode if not already installed").option("--model <model>","Model to use (e.g., anthropic/claude-sonnet-4)").action(async t=>{Ut()||(t.install?Pt()||(l("Failed to install OpenCode. Please install manually:"),console.log(" npm install -g opencode-ai@latest"),console.log(" # or"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)):(l("OpenCode AI agent is not installed."),console.log(`
2780
+ Install it with one of:`),console.log(" emailr agent --install"),console.log(" npm install -g opencode-ai@latest"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)));try{At(),p("Emailr CLI skill loaded");}catch(n){C(`Could not install skill: ${n instanceof Error?n.message:String(n)}`);}let e=[];t.model&&e.push("--model",t.model),console.log(`
3096
2781
  Starting Emailr AI Agent...`),console.log("The agent has the emailr-cli skill loaded."),console.log(`Ask it to help with emails, templates, contacts, or broadcasts.
3097
- `);let o=spawn("opencode",e,{stdio:"inherit",env:process.env});o.on("error",n=>{l(`Failed to start agent: ${n.message}`),process.exit(1);}),o.on("exit",n=>{process.exit(n??0);});})}function He(){let i=new Command("inbox").description(`Manage inbound emails (inbox)
2782
+ `);let i=spawn("opencode",e,{stdio:"inherit",env:process.env});i.on("error",n=>{l(`Failed to start agent: ${n.message}`),process.exit(1);}),i.on("exit",n=>{process.exit(n??0);});})}function He(){let r=new Command("inbox").description(`Manage inbound emails (inbox)
3098
2783
 
3099
2784
  USAGE
3100
2785
  emailr inbox <subcommand> [options]
@@ -3146,7 +2831,7 @@ EXAMPLES
3146
2831
 
3147
2832
  SEE ALSO
3148
2833
  emailr send Send individual emails
3149
- emailr emails View all emails (sent and received)`);return i.command("list").description(`List received emails
2834
+ emailr emails View all emails (sent and received)`);return r.command("list").description(`List received emails
3150
2835
 
3151
2836
  USAGE
3152
2837
  emailr inbox list [options]
@@ -3174,7 +2859,7 @@ EXAMPLES
3174
2859
 
3175
2860
  OUTPUT FORMATS
3176
2861
  --format json Machine-readable JSON with data array and pagination
3177
- --format table Human-readable table with ID, From, Subject, To, Date (default)`).option("--limit <count>","Number of emails to return","20").option("--page <number>","Page number","1").option("--email <address>","Filter by sender email (partial match)").option("--domain <domain>","Filter by sender domain").option("--inbox-id <inbox_id>","Filter by inbox ID").option("--format <format>","Output format (json|table)","table").action(async a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={status:"received",page:parseInt(a.page),limit:Math.min(parseInt(a.limit),100),email:a.email,domain:a.domain};a.inboxId&&(n.inbox_id=a.inboxId);let t=await o.emails.list(n);if(a.format==="json")m(t,"json");else {if(!t.data||t.data.length===0){u("No emails in inbox");return}let s=t.data.map(c=>({ID:c.id,From:c.from_email,Subject:Ke(c.subject||"(no subject)",40),To:c.to_email,Date:se(c.created_at),Attachments:c.attachments?.length||0}));m(s,"table"),u(`Page ${t.pagination.page} of ${t.pagination.pages} (${t.pagination.total} total)`);}}catch(e){l(e instanceof Error?e.message:"Failed to list inbox"),process.exit(1);}}),i.command("get <id>").description(`View a received email
2862
+ --format table Human-readable table with ID, From, Subject, To, Date (default)`).option("--limit <count>","Number of emails to return","20").option("--page <number>","Page number","1").option("--email <address>","Filter by sender email (partial match)").option("--domain <domain>","Filter by sender domain").option("--inbox-id <inbox_id>","Filter by inbox ID").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={status:"received",page:parseInt(t.page),limit:Math.min(parseInt(t.limit),100),email:t.email,domain:t.domain};t.inboxId&&(n.inbox_id=t.inboxId);let a=await i.emails.list(n);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(c=>({ID:c.id,From:c.from_email,Subject:Ke(c.subject||"(no subject)",40),To:c.to_email,Date:se(c.created_at),Attachments:c.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);}}),r.command("get <id>").description(`View a received email
3178
2863
 
3179
2864
  USAGE
3180
2865
  emailr inbox get <email-id> [options]
@@ -3195,7 +2880,7 @@ EXAMPLES
3195
2880
 
3196
2881
  OUTPUT FORMATS
3197
2882
  --format json Full email object with all fields as JSON
3198
- --format table Key-value table with email metadata and content preview (default)`).option("--content <type>","Content to display: preview | text | html","preview").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).emails.get(a);if(e.format==="json")m(t,"json");else {if(m({ID:t.id,From:t.from_email,To:t.to_email,Subject:t.subject||"(no subject)",Date:se(t.created_at),Status:t.status,"Thread ID":t.thread_id||"-","Parent Email":t.parent_email_id||"-",Attachments:t.attachments?.length||0},"table"),console.log(""),e.content==="html")console.log(t.html_content||"(no HTML content)");else if(e.content==="text")console.log(t.text_content||"(no text content)");else {let s=t.text_content||t.html_content?.replace(/<[^>]*>/g,"")||"(no content)";console.log(s);}if(t.attachments&&t.attachments.length>0){console.log(""),u("Attachments:");let s=t.attachments.map(c=>({Filename:c.filename,Type:c.contentType||c.content_type||"-",Size:Rt(c.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
2883
+ --format table Key-value table with email metadata and content preview (default)`).option("--content <type>","Content to display: preview | text | html","preview").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.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:se(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(c=>({Filename:c.filename,Type:c.contentType||c.content_type||"-",Size:Dt(c.size||0)}));m(s,"table");}}}catch(i){l(i instanceof Error?i.message:"Failed to get email"),process.exit(1);}}),r.command("thread <id>").description(`View conversation thread for an email
3199
2884
 
3200
2885
  USAGE
3201
2886
  emailr inbox thread <email-id> [options]
@@ -3214,7 +2899,7 @@ EXAMPLES
3214
2899
 
3215
2900
  OUTPUT FORMATS
3216
2901
  --format json JSON array of all emails in the thread
3217
- --format table Chronological conversation view with direction indicators (default)`).option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t=await n.emails.get(a),r=t.thread_id||t.id,s=[],c=new Set;if(s.push(t),c.add(t.id),t.thread_id&&t.thread_id!==t.id)try{let f=await n.emails.get(t.thread_id);c.has(f.id)||(s.push(f),c.add(f.id));}catch{}let b=await n.emails.list({limit:100});if(b.data)for(let f of b.data)c.has(f.id)||(f.thread_id===r||f.parent_email_id===t.id||f.id===t.parent_email_id)&&(s.push(f),c.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",S=f.id===a?" \u25C0 (selected)":"";console.log(`${h} ${se(f.created_at)}${S}`),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(` ${Ke(_.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
2902
+ --format table Chronological conversation view with direction indicators (default)`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),a=await n.emails.get(t),o=a.thread_id||a.id,s=[],c=new Set;if(s.push(a),c.add(a.id),a.thread_id&&a.thread_id!==a.id)try{let f=await n.emails.get(a.thread_id);c.has(f.id)||(s.push(f),c.add(f.id));}catch{}let b=await n.emails.list({limit:100});if(b.data)for(let f of b.data)c.has(f.id)||(f.thread_id===o||f.parent_email_id===a.id||f.id===a.parent_email_id)&&(s.push(f),c.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",S=f.id===t?" \u25C0 (selected)":"";console.log(`${h} ${se(f.created_at)}${S}`),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(` ${Ke(_.trim(),120)}`),console.log("");}}await new Promise(f=>process.stdout.write("",()=>f())),process.exit(0);}catch(i){l(i instanceof Error?i.message:"Failed to load thread"),process.exit(1);}}),r.command("reply <id>").description(`Reply to a received email
3218
2903
 
3219
2904
  USAGE
3220
2905
  emailr inbox reply <email-id> [options]
@@ -3250,7 +2935,7 @@ EXAMPLES
3250
2935
 
3251
2936
  OUTPUT FORMATS
3252
2937
  --format json Machine-readable JSON with message_id and status
3253
- --format table Human-readable summary with Message ID, To, Subject, Status (default)`).option("--html <content>","HTML content for reply").option("--text <content>","Plain text content for reply").option("--html-file <path>","Read HTML content from file").option("--text-file <path>","Read plain text content from file").option("--from <email>","Override sender address").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--subject <subject>","Override reply subject").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t=await n.emails.get(a),r=e.html,s=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{s=readFileSync(e.textFile,"utf-8");}catch{l(`Failed to read text file: ${e.textFile}`),process.exit(1);}!r&&!s&&(l("Reply content is required. Use --html, --text, --html-file, or --text-file"),process.exit(1));let c=e.subject||(t.subject?.startsWith("Re:")?t.subject:`Re: ${t.subject||""}`),b={to:t.from_email,from:e.from||t.to_email,subject:c,html:r||void 0,text:s||r?.replace(/<[^>]*>/g,"")||void 0,replyTo:{in_reply_to:t.message_id||t.ses_message_id,thread_id:t.thread_id||t.id,parent_email_id:t.id}};if(e.cc){let h=e.cc.split(",").map(S=>S.trim());b.cc=h.length===1?h[0]:h;}if(e.bcc){let h=e.bcc.split(",").map(S=>S.trim());b.bcc=h.length===1?h[0]:h;}let g=await n.emails.send(b);e.format==="json"?m(g,"json"):(p("Reply sent successfully!"),m({"Message ID":g.message_id,To:t.from_email,Subject:c,Status:g.status},"table")),await new Promise(h=>process.stdout.write("",()=>h())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to send reply"),process.exit(1);}}),i.command("forward <id>").description(`Forward a received email
2938
+ --format table Human-readable summary with Message ID, To, Subject, Status (default)`).option("--html <content>","HTML content for reply").option("--text <content>","Plain text content for reply").option("--html-file <path>","Read HTML content from file").option("--text-file <path>","Read plain text content from file").option("--from <email>","Override sender address").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--subject <subject>","Override reply subject").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),a=await n.emails.get(t),o=e.html,s=e.text;if(e.htmlFile)try{o=readFileSync(e.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${e.htmlFile}`),process.exit(1);}if(e.textFile)try{s=readFileSync(e.textFile,"utf-8");}catch{l(`Failed to read text file: ${e.textFile}`),process.exit(1);}!o&&!s&&(l("Reply content is required. Use --html, --text, --html-file, or --text-file"),process.exit(1));let c=e.subject||(a.subject?.startsWith("Re:")?a.subject:`Re: ${a.subject||""}`),b={to:a.from_email,from:e.from||a.to_email,subject:c,html:o||void 0,text:s||o?.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(S=>S.trim());b.cc=h.length===1?h[0]:h;}if(e.bcc){let h=e.bcc.split(",").map(S=>S.trim());b.bcc=h.length===1?h[0]:h;}let g=await n.emails.send(b);e.format==="json"?m(g,"json"):(p("Reply sent successfully!"),m({"Message ID":g.message_id,To:a.from_email,Subject:c,Status:g.status},"table")),await new Promise(h=>process.stdout.write("",()=>h())),process.exit(0);}catch(i){l(i instanceof Error?i.message:"Failed to send reply"),process.exit(1);}}),r.command("forward <id>").description(`Forward a received email
3254
2939
 
3255
2940
  USAGE
3256
2941
  emailr inbox forward <email-id> --to <recipients> [options]
@@ -3276,7 +2961,7 @@ EXAMPLES
3276
2961
 
3277
2962
  OUTPUT FORMATS
3278
2963
  --format json Machine-readable JSON with message_id and status
3279
- --format table Human-readable summary with Message ID, To, Recipients, Status (default)`).requiredOption("--to <emails>","Recipient email addresses (comma-separated)").option("--message <text>","Message to include with forwarded email").option("--format <format>","Output format (json|table)","table").action(async(a,e)=>{try{let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t=e.to.split(",").map(c=>c.trim()).filter(Boolean),r=await n.emails.forward({email_id:a,to:t.length===1?t[0]:t,message:e.message});e.format==="json"?m(r,"json"):(p("Email forwarded successfully!"),m({"Message ID":r.message_id,To:t.join(", "),Recipients:r.recipients,Status:r.status},"table")),await new Promise(c=>process.stdout.write("",()=>c())),process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to forward email"),process.exit(1);}}),i}function Ke(i,a){return i.length<=a?i:i.slice(0,a-1)+"\u2026"}function se(i){return new Date(i).toLocaleString()}function Rt(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
2964
+ --format table Human-readable summary with Message ID, To, Recipients, Status (default)`).requiredOption("--to <emails>","Recipient email addresses (comma-separated)").option("--message <text>","Message to include with forwarded email").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),a=e.to.split(",").map(c=>c.trim()).filter(Boolean),o=await n.emails.forward({email_id:t,to:a.length===1?a[0]:a,message:e.message});e.format==="json"?m(o,"json"):(p("Email forwarded successfully!"),m({"Message ID":o.message_id,To:a.join(", "),Recipients:o.recipients,Status:o.status},"table")),await new Promise(c=>process.stdout.write("",()=>c())),process.exit(0);}catch(i){l(i instanceof Error?i.message:"Failed to forward email"),process.exit(1);}}),r}function Ke(r,t){return r.length<=t?r:r.slice(0,t-1)+"\u2026"}function se(r){return new Date(r).toLocaleString()}function Dt(r){return r<1024?r+" B":r<1024*1024?(r/1024).toFixed(1)+" KB":(r/(1024*1024)).toFixed(1)+" MB"}function qe(){let r=new Command("inboxes").description(`Manage inboxes
3280
2965
 
3281
2966
  USAGE
3282
2967
  emailr inboxes <subcommand> [options]
@@ -3306,7 +2991,7 @@ EXAMPLES
3306
2991
  emailr inboxes update inb_abc123 --name "Customer Support"
3307
2992
 
3308
2993
  # Delete an inbox
3309
- emailr inboxes delete inb_abc123`);return i.command("create").description(`Create a new inbox
2994
+ emailr inboxes delete inb_abc123`);return r.command("create").description(`Create a new inbox
3310
2995
 
3311
2996
  USAGE
3312
2997
  emailr inboxes create --name <name> --username <username> --domain <domain> [options]
@@ -3329,7 +3014,7 @@ EXAMPLES
3329
3014
  emailr inboxes create --name "Support" --username support --domain example.com --reply-to replies@example.com
3330
3015
 
3331
3016
  # Create and get JSON output
3332
- emailr inboxes create --name "Sales" --username sales --domain example.com --format json`).requiredOption("--name <name>","Inbox display name").requiredOption("--username <username>","Username for the email address").requiredOption("--domain <domain>","Domain for the email address").option("--reply-to <email>","Custom reply-to address (defaults to username@mail.domain)").option("--format <format>","Output format: json | table","table").action(async a=>{try{let e=d(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={name:a.name,username:a.username,domain:a.domain};a.replyTo&&(n.reply_to=a.replyTo);let t=await o.inboxes.create(n);a.format==="json"?m(t,"json"):(p(`Inbox created: ${t.id}`),m({ID:t.id,Name:t.name,"From Address":t.from_address,"Reply To":t.reply_to,"Inbound Address":t.inbound_address,Created:t.created_at},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create inbox"),process.exit(1);}}),i.command("list").description(`List all inboxes
3017
+ emailr inboxes create --name "Sales" --username sales --domain example.com --format json`).requiredOption("--name <name>","Inbox display name").requiredOption("--username <username>","Username for the email address").requiredOption("--domain <domain>","Domain for the email address").option("--reply-to <email>","Custom reply-to address (defaults to username@mail.domain)").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=d(),i=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),n={name:t.name,username:t.username,domain:t.domain};t.replyTo&&(n.reply_to=t.replyTo);let a=await i.inboxes.create(n);t.format==="json"?m(a,"json"):(p(`Inbox created: ${a.id}`),m({ID:a.id,Name:a.name,"From Address":a.from_address,"Reply To":a.reply_to,"Inbound Address":a.inbound_address,Created:a.created_at},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create inbox"),process.exit(1);}}),r.command("list").description(`List all inboxes
3333
3018
 
3334
3019
  USAGE
3335
3020
  emailr inboxes list [options]
@@ -3345,8 +3030,8 @@ EXAMPLES
3345
3030
  emailr inboxes list
3346
3031
 
3347
3032
  # Get JSON output
3348
- emailr inboxes list --format json`).option("--format <format>","Output format: json | table","table").action(async a=>{try{let e=d(),n=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).inboxes.list();if(a.format==="json")m(n,"json");else {if(n.length===0){console.log("No inboxes found.");return}let t=n.map(r=>({ID:r.id,Name:r.name,"From Address":r.from_address,"Reply To":r.reply_to,"Inbound Address":r.inbound_address,Created:new Date(r.created_at).toLocaleDateString()}));m(t,"table"),console.log(`
3349
- Total: ${n.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
3033
+ emailr inboxes list --format json`).option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=d(),n=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).inboxes.list();if(t.format==="json")m(n,"json");else {if(n.length===0){console.log("No inboxes found.");return}let a=n.map(o=>({ID:o.id,Name:o.name,"From Address":o.from_address,"Reply To":o.reply_to,"Inbound Address":o.inbound_address,Created:new Date(o.created_at).toLocaleDateString()}));m(a,"table"),console.log(`
3034
+ Total: ${n.length}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list inboxes"),process.exit(1);}}),r.command("get <inbox_id>").description(`Get an inbox by ID
3350
3035
 
3351
3036
  USAGE
3352
3037
  emailr inboxes get <inbox_id> [options]
@@ -3365,7 +3050,7 @@ EXAMPLES
3365
3050
  emailr inboxes get inb_abc123
3366
3051
 
3367
3052
  # Get JSON output
3368
- emailr inboxes get inb_abc123 --format json`).option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).inboxes.get(a);e.format==="json"?m(t,"json"):m({ID:t.id,Name:t.name,Username:t.username,Domain:t.domain,"From Address":t.from_address,"Reply To":t.reply_to,"Inbound Address":t.inbound_address,Created:t.created_at,Updated:t.updated_at},"table");}catch(o){l(o instanceof Error?o.message:"Failed to get inbox"),process.exit(1);}}),i.command("update <inbox_id>").description(`Update an inbox
3053
+ emailr inboxes get inb_abc123 --format json`).option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.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,"Reply To":a.reply_to,"Inbound Address":a.inbound_address,Created:a.created_at,Updated:a.updated_at},"table");}catch(i){l(i instanceof Error?i.message:"Failed to get inbox"),process.exit(1);}}),r.command("update <inbox_id>").description(`Update an inbox
3369
3054
 
3370
3055
  USAGE
3371
3056
  emailr inboxes update <inbox_id> --name <name> [options]
@@ -3392,7 +3077,7 @@ EXAMPLES
3392
3077
  emailr inboxes update inb_abc123 --reply-to none
3393
3078
 
3394
3079
  # Get JSON output
3395
- emailr inboxes update inb_abc123 --name "New Name" --format json`).option("--name <name>","New inbox display name").option("--reply-to <email>",'New reply-to address (use "none" to reset to default)').option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{!e.name&&!e.replyTo&&(l("No update fields specified. Use --name or --reply-to to update the inbox."),process.exit(1));let o=d(),n=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),t={};e.name&&(t.name=e.name),e.replyTo&&(t.reply_to=e.replyTo==="none"?null:e.replyTo);let r=await n.inboxes.update(a,t);e.format==="json"?m(r,"json"):(p(`Inbox updated: ${r.id}`),m({ID:r.id,Name:r.name,"From Address":r.from_address,"Reply To":r.reply_to,"Inbound Address":r.inbound_address,Updated:r.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
3080
+ emailr inboxes update inb_abc123 --name "New Name" --format json`).option("--name <name>","New inbox display name").option("--reply-to <email>",'New reply-to address (use "none" to reset to default)').option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{!e.name&&!e.replyTo&&(l("No update fields specified. Use --name or --reply-to to update the inbox."),process.exit(1));let i=d(),n=new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}),a={};e.name&&(a.name=e.name),e.replyTo&&(a.reply_to=e.replyTo==="none"?null:e.replyTo);let o=await n.inboxes.update(t,a);e.format==="json"?m(o,"json"):(p(`Inbox updated: ${o.id}`),m({ID:o.id,Name:o.name,"From Address":o.from_address,"Reply To":o.reply_to,"Inbound Address":o.inbound_address,Updated:o.updated_at},"table"));}catch(i){l(i instanceof Error?i.message:"Failed to update inbox"),process.exit(1);}}),r.command("delete <inbox_id>").description(`Delete an inbox
3396
3081
 
3397
3082
  USAGE
3398
3083
  emailr inboxes delete <inbox_id> [options]
@@ -3415,7 +3100,7 @@ EXAMPLES
3415
3100
  emailr inboxes delete inb_abc123 --format json
3416
3101
 
3417
3102
  WARNING
3418
- This action is permanent and cannot be undone.`).option("--format <format>","Output format: json | table","table").action(async(a,e)=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).inboxes.delete(a);e.format==="json"?m(t,"json"):p(`Inbox deleted: ${a}`);}catch(o){l(o instanceof Error?o.message:"Failed to delete inbox"),process.exit(1);}}),i}function Je(){let i=new Command("threads").description(`Manage email threads, labels, replies, and drafts
3103
+ This action is permanent and cannot be undone.`).option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).inboxes.delete(t);e.format==="json"?m(a,"json"):p(`Inbox deleted: ${t}`);}catch(i){l(i instanceof Error?i.message:"Failed to delete inbox"),process.exit(1);}}),r}function Je(){let r=new Command("threads").description(`Manage email threads, labels, replies, and drafts
3419
3104
 
3420
3105
  USAGE
3421
3106
  emailr threads <subcommand> [options]
@@ -3447,9 +3132,9 @@ EXAMPLES
3447
3132
  emailr threads list
3448
3133
 
3449
3134
  # Star a thread
3450
- emailr threads label <thread-id> --add starred`);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 e=>{try{let o=d(),t=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).threads.list({label:e.label,page:parseInt(e.page),limit:Math.min(parseInt(e.limit),100),search:e.search,inbox_id:e.inboxId});if(e.format==="json")m(t,"json");else {if(!t.data||t.data.length===0){u(`No threads in ${e.label}`);return}let s=t.data.map(c=>({"Thread ID":c.thread_id.slice(0,8)+"\u2026",Subject:le(c.subject||"(no subject)",40),From:c.from_email,Messages:c.message_count,Labels:(c.labels||[]).join(", "),Updated:$e(c.updated_at)}));m(s,"table"),u(`Page ${t.pagination.page} of ${t.pagination.pages} (${t.pagination.total} total)`);}}catch(o){l(o instanceof Error?o.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(e,o)=>{try{let n=d(),r=await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.get(e);if(o.format==="json")m(r,"json");else {u(`Thread: ${r.subject||"(no subject)"}`),u(`Labels: ${r.labels.join(", ")||"none"}`),u(`Messages: ${r.messages.length}`),console.log("");for(let c of r.messages){let b=c.status==="received"?"\u2190 IN ":"\u2192 OUT";console.log(`${b} ${$e(c.created_at)}`),console.log(` From: ${c.from_email}`),console.log(` To: ${c.to_email}`),c.cc_emails?.length&&console.log(` Cc: ${c.cc_emails.join(", ")}`),console.log(` Labels: ${c.labels.join(", ")||"none"}`);let g=c.text_content||c.html_content?.replace(/<[^>]*>/g,"")||"";g&&console.log(` ${le(g.trim(),120)}`),console.log("");}}await new Promise(c=>process.stdout.write("",()=>c())),process.exit(0);}catch(n){l(n instanceof Error?n.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(e,o)=>{try{!o.add&&!o.remove&&(l("Specify --add and/or --remove labels"),process.exit(1));let n=d(),t=new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}),r=o.add?o.add.split(",").map(g=>g.trim()):void 0,s=o.remove?o.remove.split(",").map(g=>g.trim()):void 0,c=await t.threads.updateLabels(e,{add:r,remove:s});o.format==="json"?m(c,"json"):(p(`Updated ${c.updated} email(s) in thread`),r&&u(`Added: ${r.join(", ")}`),s&&u(`Removed: ${s.join(", ")}`)),await new Promise(g=>process.stdout.write("",()=>g())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to update labels"),process.exit(1);}}),i.command("reply <id>").description("Reply to a thread (from/to/subject auto-resolved from inbox)").option("--html <content>","HTML content for the reply").option("--text <content>","Plain text content for the reply").option("--to <email>","Override recipient (auto-resolved if omitted)").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--from <email>","Override sender (auto-resolved from inbox if omitted)").option("--from-name <name>","Override sender name (auto-resolved from inbox if omitted)").option("--reply-to <email>","Override Reply-To (auto-resolved from inbox if omitted)").option("--format <format>","Output format (json|table)","table").action(async(e,o)=>{try{!o.html&&!o.text&&(l("Provide --html or --text for the reply body"),process.exit(1));let n=d(),t=new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}),r={};o.html&&(r.html=o.html),o.text&&(r.text=o.text),o.to&&(r.to=o.to),o.cc&&(r.cc=o.cc.split(",").map(b=>b.trim())),o.bcc&&(r.bcc=o.bcc.split(",").map(b=>b.trim())),o.from&&(r.from=o.from),o.fromName&&(r.from_name=o.fromName),o.replyTo&&(r.reply_to_email=o.replyTo);let s=await t.threads.reply(e,r);o.format==="json"?m(s,"json"):(p(`Reply sent to ${s.recipients} recipient(s)`),u(`Message ID: ${s.message_id}`)),await new Promise(b=>process.stdout.write("",()=>b())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to send reply"),process.exit(1);}});let a=i.command("draft").description("Manage draft replies for threads");return a.command("save <id>").description("Save a draft reply for a thread").option("--html <content>","HTML content").option("--text <content>","Plain text content").option("--to <email>","Recipient").option("--cc <emails>","CC recipients").option("--bcc <emails>","BCC recipients").option("--from <email>","Sender").option("--reply-to <email>","Reply-To address").option("--format <format>","Output format (json|table)","table").action(async(e,o)=>{try{let n=d(),t=new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}),r={};o.html&&(r.html=o.html),o.text&&(r.text=o.text),o.to&&(r.to=o.to),o.cc&&(r.cc=o.cc),o.bcc&&(r.bcc=o.bcc),o.from&&(r.from=o.from),o.replyTo&&(r.reply_to_email=o.replyTo);let s=await t.threads.saveDraft(e,r);o.format==="json"?m(s,"json"):(p(`Draft saved for thread ${e}`),u(`Draft ID: ${s.id}`)),await new Promise(b=>process.stdout.write("",()=>b())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to save draft"),process.exit(1);}}),a.command("get <id>").description("Get the saved draft for a thread").option("--format <format>","Output format (json|table)","table").action(async(e,o)=>{try{let n=d(),r=await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.getDraft(e);o.format==="json"?m(r,"json"):(u(`Draft for thread: ${e}`),u(`To: ${r.to_email||"(auto)"}`),u(`From: ${r.from_email||"(auto)"}`),u(`Subject: ${r.subject||"(auto)"}`),r.html_content&&console.log(`
3135
+ emailr threads label <thread-id> --add starred`);r.command("list").description("List threads filtered by label").option("--label <label>","Filter by label (inbox, sent, starred, spam, trash, archived, all)","inbox").option("--limit <count>","Number of threads to return","20").option("--page <number>","Page number","1").option("--search <query>","Search by subject or sender").option("--inbox-id <id>","Filter by inbox ID").option("--format <format>","Output format (json|table)","table").action(async e=>{try{let i=d(),a=await new Emailr({apiKey:i.apiKey,baseUrl:i.baseUrl}).threads.list({label:e.label,page:parseInt(e.page),limit:Math.min(parseInt(e.limit),100),search:e.search,inbox_id:e.inboxId});if(e.format==="json")m(a,"json");else {if(!a.data||a.data.length===0){u(`No threads in ${e.label}`);return}let s=a.data.map(c=>({"Thread ID":c.thread_id.slice(0,8)+"\u2026",Subject:le(c.subject||"(no subject)",40),From:c.from_email,Messages:c.message_count,Labels:(c.labels||[]).join(", "),Updated:$e(c.updated_at)}));m(s,"table"),u(`Page ${a.pagination.page} of ${a.pagination.pages} (${a.pagination.total} total)`);}}catch(i){l(i instanceof Error?i.message:"Failed to list threads"),process.exit(1);}}),r.command("get <id>").description("View a thread with all messages").option("--format <format>","Output format (json|table)","table").action(async(e,i)=>{try{let n=d(),o=await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.get(e);if(i.format==="json")m(o,"json");else {u(`Thread: ${o.subject||"(no subject)"}`),u(`Labels: ${o.labels.join(", ")||"none"}`),u(`Messages: ${o.messages.length}`),console.log("");for(let c of o.messages){let b=c.status==="received"?"\u2190 IN ":"\u2192 OUT";console.log(`${b} ${$e(c.created_at)}`),console.log(` From: ${c.from_email}`),console.log(` To: ${c.to_email}`),c.cc_emails?.length&&console.log(` Cc: ${c.cc_emails.join(", ")}`),console.log(` Labels: ${c.labels.join(", ")||"none"}`);let g=c.text_content||c.html_content?.replace(/<[^>]*>/g,"")||"";g&&console.log(` ${le(g.trim(),120)}`),console.log("");}}await new Promise(c=>process.stdout.write("",()=>c())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to get thread"),process.exit(1);}}),r.command("label <id>").description("Add or remove labels from a thread").option("--add <labels>","Labels to add (comma-separated)").option("--remove <labels>","Labels to remove (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async(e,i)=>{try{!i.add&&!i.remove&&(l("Specify --add and/or --remove labels"),process.exit(1));let n=d(),a=new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}),o=i.add?i.add.split(",").map(g=>g.trim()):void 0,s=i.remove?i.remove.split(",").map(g=>g.trim()):void 0,c=await a.threads.updateLabels(e,{add:o,remove:s});i.format==="json"?m(c,"json"):(p(`Updated ${c.updated} email(s) in thread`),o&&u(`Added: ${o.join(", ")}`),s&&u(`Removed: ${s.join(", ")}`)),await new Promise(g=>process.stdout.write("",()=>g())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to update labels"),process.exit(1);}}),r.command("reply <id>").description("Reply to a thread (from/to/subject auto-resolved from inbox)").option("--html <content>","HTML content for the reply").option("--text <content>","Plain text content for the reply").option("--to <email>","Override recipient (auto-resolved if omitted)").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--from <email>","Override sender (auto-resolved from inbox if omitted)").option("--from-name <name>","Override sender name (auto-resolved from inbox if omitted)").option("--reply-to <email>","Override Reply-To (auto-resolved from inbox if omitted)").option("--format <format>","Output format (json|table)","table").action(async(e,i)=>{try{!i.html&&!i.text&&(l("Provide --html or --text for the reply body"),process.exit(1));let n=d(),a=new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}),o={};i.html&&(o.html=i.html),i.text&&(o.text=i.text),i.to&&(o.to=i.to),i.cc&&(o.cc=i.cc.split(",").map(b=>b.trim())),i.bcc&&(o.bcc=i.bcc.split(",").map(b=>b.trim())),i.from&&(o.from=i.from),i.fromName&&(o.from_name=i.fromName),i.replyTo&&(o.reply_to_email=i.replyTo);let s=await a.threads.reply(e,o);i.format==="json"?m(s,"json"):(p(`Reply sent to ${s.recipients} recipient(s)`),u(`Message ID: ${s.message_id}`)),await new Promise(b=>process.stdout.write("",()=>b())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to send reply"),process.exit(1);}});let t=r.command("draft").description("Manage draft replies for threads");return t.command("save <id>").description("Save a draft reply for a thread").option("--html <content>","HTML content").option("--text <content>","Plain text content").option("--to <email>","Recipient").option("--cc <emails>","CC recipients").option("--bcc <emails>","BCC recipients").option("--from <email>","Sender").option("--reply-to <email>","Reply-To address").option("--format <format>","Output format (json|table)","table").action(async(e,i)=>{try{let n=d(),a=new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}),o={};i.html&&(o.html=i.html),i.text&&(o.text=i.text),i.to&&(o.to=i.to),i.cc&&(o.cc=i.cc),i.bcc&&(o.bcc=i.bcc),i.from&&(o.from=i.from),i.replyTo&&(o.reply_to_email=i.replyTo);let s=await a.threads.saveDraft(e,o);i.format==="json"?m(s,"json"):(p(`Draft saved for thread ${e}`),u(`Draft ID: ${s.id}`)),await new Promise(b=>process.stdout.write("",()=>b())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to save draft"),process.exit(1);}}),t.command("get <id>").description("Get the saved draft for a thread").option("--format <format>","Output format (json|table)","table").action(async(e,i)=>{try{let n=d(),o=await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.getDraft(e);i.format==="json"?m(o,"json"):(u(`Draft for thread: ${e}`),u(`To: ${o.to_email||"(auto)"}`),u(`From: ${o.from_email||"(auto)"}`),u(`Subject: ${o.subject||"(auto)"}`),o.html_content&&console.log(`
3451
3136
  Content:
3452
- ${le(r.html_content.replace(/<[^>]*>/g,""),200)}`)),await new Promise(c=>process.stdout.write("",()=>c())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"No draft found"),process.exit(1);}}),a.command("delete <id>").description("Delete the draft for a thread").option("--format <format>","Output format (json|table)","table").action(async(e,o)=>{try{let n=d();await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.deleteDraft(e),o.format==="json"?m({success:!0},"json"):p(`Draft deleted for thread ${e}`),await new Promise(s=>process.stdout.write("",()=>s())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to delete draft"),process.exit(1);}}),i}function le(i,a){return i.length<=a?i:i.slice(0,a-1)+"\u2026"}function $e(i){return new Date(i).toLocaleString()}var Ft=`# Emailr CLI \u2014 Agent Skill Guide
3137
+ ${le(o.html_content.replace(/<[^>]*>/g,""),200)}`)),await new Promise(c=>process.stdout.write("",()=>c())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"No draft found"),process.exit(1);}}),t.command("delete <id>").description("Delete the draft for a thread").option("--format <format>","Output format (json|table)","table").action(async(e,i)=>{try{let n=d();await new Emailr({apiKey:n.apiKey,baseUrl:n.baseUrl}).threads.deleteDraft(e),i.format==="json"?m({success:!0},"json"):p(`Draft deleted for thread ${e}`),await new Promise(s=>process.stdout.write("",()=>s())),process.exit(0);}catch(n){l(n instanceof Error?n.message:"Failed to delete draft"),process.exit(1);}}),r}function le(r,t){return r.length<=t?r:r.slice(0,t-1)+"\u2026"}function $e(r){return new Date(r).toLocaleString()}var Ft=`# Emailr CLI \u2014 Agent Skill Guide
3453
3138
 
3454
3139
  ## Setup
3455
3140
 
@@ -3574,14 +3259,13 @@ emailr broadcasts delete <id>
3574
3259
  ### webhooks \u2014 Configure event notifications
3575
3260
  \`\`\`bash
3576
3261
  emailr webhooks list
3577
- emailr webhooks create --name "Tracker" --url "https://x.com/hook" --events "email.delivered,email.bounced"
3578
- emailr webhooks get <id> # includes secret
3579
- emailr webhooks update <id> --events "email.delivered"
3580
- emailr webhooks enable <id>
3581
- emailr webhooks disable <id>
3262
+ emailr webhooks create --name "Tracker" --url "https://x.com/hook" --type transactional
3263
+ emailr webhooks create --name "Inbound" --url "https://x.com/hook" --type receiving --inbox-ids "id1,id2"
3264
+ emailr webhooks toggle <id> # enable/disable
3265
+ emailr webhooks deliveries <id> # view recent deliveries
3582
3266
  emailr webhooks delete <id>
3583
3267
  \`\`\`
3584
- Events: email.sent, email.delivered, email.bounced, email.complained, email.opened, email.clicked, email.failed, email.delivery_delayed, contact.created, contact.updated, contact.deleted, domain.verified, domain.verification_failed
3268
+ Types: transactional (email events), domain (domain events), receiving (inbound email)
3585
3269
 
3586
3270
  ### domains \u2014 Manage sending domains
3587
3271
  \`\`\`bash
@@ -3677,7 +3361,7 @@ EXAMPLES
3677
3361
  emailr skill > emailr-skill.md
3678
3362
 
3679
3363
  # Feed to an AI agent context
3680
- emailr skill | pbcopy`).action(()=>{process.stdout.write(Ft);})}var y=new Command;y.name("emailr").description(`Emailr CLI - Send emails and manage your email infrastructure
3364
+ emailr skill | pbcopy`).action(()=>{process.stdout.write(Ft);})}var w=new Command;w.name("emailr").description(`Emailr CLI - Send emails and manage your email infrastructure
3681
3365
 
3682
3366
  USAGE
3683
3367
  emailr <command> [subcommand] [options]
@@ -3771,7 +3455,7 @@ EXAMPLES
3771
3455
 
3772
3456
  # Configure a webhook for delivery events
3773
3457
  emailr webhooks create --name "Deliveries" --url "https://..." \\
3774
- --events "email.delivered,email.bounced"
3458
+ --type transactional
3775
3459
 
3776
3460
  # Get JSON output for scripting
3777
3461
  emailr contacts list --format json | jq '.[].email'
@@ -3792,4 +3476,4 @@ AGENTIC WORKFLOW
3792
3476
 
3793
3477
  MORE INFORMATION
3794
3478
  Run 'emailr <command> --help' for detailed help on any command.
3795
- Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.12.0");y.addCommand(fe());y.addCommand(He());y.addCommand(qe());y.addCommand(Je());y.addCommand(be());y.addCommand(Ie());y.addCommand(Ne());y.addCommand(Ue());y.addCommand(ke());y.addCommand(Pe());y.addCommand(Ce());y.addCommand(Me());y.addCommand(Fe());y.addCommand(Be());y.parse();
3479
+ Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.12.1");w.addCommand(fe());w.addCommand(He());w.addCommand(qe());w.addCommand(Je());w.addCommand(be());w.addCommand(Ie());w.addCommand(Ce());w.addCommand(Ue());w.addCommand(Pe());w.addCommand(Ae());w.addCommand(Ne());w.addCommand(Me());w.addCommand(Fe());w.addCommand(Be());w.parse();