emailr-cli 1.12.2 → 1.14.0

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