emailr-cli 1.7.0 → 1.7.2

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 +122 -76
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import {Command}from'commander';import {Emailr}from'emailr';import v,{readFileSync}from'fs';import B from'os';import N from'path';import re from'cli-table3';import S from'chalk';import Je from'http';import {URL}from'url';import ct from'crypto';import {spawn,execSync,exec}from'child_process';var Re=[N.join(B.homedir(),".emailrrc"),N.join(B.homedir(),".config","emailr","config.json")];function m(){if(process.env.EMAILR_API_KEY)return {apiKey:process.env.EMAILR_API_KEY,baseUrl:process.env.EMAILR_BASE_URL,format:process.env.EMAILR_FORMAT||"table"};for(let i of Re)if(v.existsSync(i))try{let t=v.readFileSync(i,"utf-8"),e=JSON.parse(t);if(e.apiKey)return {apiKey:e.apiKey,baseUrl:e.baseUrl,format:e.format||"table"}}catch{}throw new Error(`No API key configured.
2
+ import {Command}from'commander';import {Emailr}from'emailr';import v,{readFileSync}from'fs';import B from'os';import _ from'path';import re from'cli-table3';import S from'chalk';import Je from'http';import {URL}from'url';import ct from'crypto';import {spawn,execSync,exec}from'child_process';var Re=[_.join(B.homedir(),".emailrrc"),_.join(B.homedir(),".config","emailr","config.json")];function m(){if(process.env.EMAILR_API_KEY)return {apiKey:process.env.EMAILR_API_KEY,baseUrl:process.env.EMAILR_BASE_URL,format:process.env.EMAILR_FORMAT||"table"};for(let i of Re)if(v.existsSync(i))try{let t=v.readFileSync(i,"utf-8"),e=JSON.parse(t);if(e.apiKey)return {apiKey:e.apiKey,baseUrl:e.baseUrl,format:e.format||"table"}}catch{}throw new Error(`No API key configured.
3
3
 
4
4
  Set the EMAILR_API_KEY environment variable:
5
5
  export EMAILR_API_KEY=your-api-key
@@ -7,8 +7,8 @@ Set the EMAILR_API_KEY environment variable:
7
7
  Or create a config file at ~/.emailrrc:
8
8
  { "apiKey": "your-api-key" }
9
9
 
10
- Or run: emailr config set api-key <your-api-key>`)}function k(){let i=N.join(B.homedir(),".config","emailr");return N.join(i,"config.json")}function G(i){let t=k(),e=N.dirname(t);v.existsSync(e)||v.mkdirSync(e,{recursive:true});let a={};if(v.existsSync(t))try{a=JSON.parse(v.readFileSync(t,"utf-8"));}catch{}let r={...a,...i};v.writeFileSync(t,JSON.stringify(r,null,2)+`
11
- `);}function ne(i){try{return m()[i]?.toString()}catch{return}}function c(i,t="table"){t==="json"?console.log(JSON.stringify(i,null,2)):Me(i);}function Me(i){Array.isArray(i)?Le(i):typeof i=="object"&&i!==null?De(i):console.log(i);}function Le(i){if(i.length===0){console.log(S.gray("No results"));return}let t=i[0];if(typeof t!="object"||t===null){i.forEach(r=>console.log(r));return}let e=Object.keys(t),a=new re({head:e.map(r=>S.cyan(r)),style:{head:[],border:[]}});for(let r of i){let o=e.map(n=>{let s=r[n];return se(s)});a.push(o);}console.log(a.toString());}function De(i){let t=new re({style:{head:[],border:[]}});for(let[e,a]of Object.entries(i))t.push([S.cyan(e),se(a)]);console.log(t.toString());}function se(i){return i==null?S.gray("-"):typeof i=="boolean"?i?S.green("\u2713"):S.red("\u2717"):typeof i=="object"?JSON.stringify(i):String(i)}function d(i){console.log(S.green("\u2713"),i);}function l(i){console.error(S.red("\u2717"),i);}function I(i){console.warn(S.yellow("\u26A0"),i);}function u(i){console.log(S.blue("\u2139"),i);}function ce(){return new Command("send").description(`Send an email
10
+ Or run: emailr config set api-key <your-api-key>`)}function I(){let i=_.join(B.homedir(),".config","emailr");return _.join(i,"config.json")}function G(i){let t=I(),e=_.dirname(t);v.existsSync(e)||v.mkdirSync(e,{recursive:true});let o={};if(v.existsSync(t))try{o=JSON.parse(v.readFileSync(t,"utf-8"));}catch{}let r={...o,...i};v.writeFileSync(t,JSON.stringify(r,null,2)+`
11
+ `);}function ne(i){try{return m()[i]?.toString()}catch{return}}function c(i,t="table"){t==="json"?console.log(JSON.stringify(i,null,2)):Me(i);}function Me(i){Array.isArray(i)?Le(i):typeof i=="object"&&i!==null?De(i):console.log(i);}function Le(i){if(i.length===0){console.log(S.gray("No results"));return}let t=i[0];if(typeof t!="object"||t===null){i.forEach(r=>console.log(r));return}let e=Object.keys(t),o=new re({head:e.map(r=>S.cyan(r)),style:{head:[],border:[]}});for(let r of i){let a=e.map(n=>{let s=r[n];return se(s)});o.push(a);}console.log(o.toString());}function De(i){let t=new re({style:{head:[],border:[]}});for(let[e,o]of Object.entries(i))t.push([S.cyan(e),se(o)]);console.log(t.toString());}function se(i){return i==null?S.gray("-"):typeof i=="boolean"?i?S.green("\u2713"):S.red("\u2717"):typeof i=="object"?JSON.stringify(i):String(i)}function d(i){console.log(S.green("\u2713"),i);}function l(i){console.error(S.red("\u2717"),i);}function k(i){console.warn(S.yellow("\u26A0"),i);}function u(i){console.log(S.blue("\u2139"),i);}function ce(){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=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r=t.to.split(",").map(s=>s.trim()),o={to:r.length===1?r[0]:r};if(t.from&&(o.from=t.from),t.subject&&(o.subject=t.subject),t.htmlFile)try{o.html=readFileSync(t.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${t.htmlFile}`),process.exit(1);}else t.html&&(o.html=t.html);if(t.textFile)try{o.text=readFileSync(t.textFile,"utf-8");}catch{l(`Failed to read text file: ${t.textFile}`),process.exit(1);}else t.text&&(o.text=t.text);if(t.template&&(o.template_id=t.template),t.templateData)try{o.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(p=>p.trim());o.cc=s.length===1?s[0]:s;}if(t.bcc){let s=t.bcc.split(",").map(p=>p.trim());o.bcc=s.length===1?s[0]:s;}t.replyTo&&(o.reply_to_email=t.replyTo),t.schedule&&(o.scheduled_at=t.schedule);let n=await a.emails.send(o);t.format==="json"?c(n,"json"):(d("Email sent successfully!"),c({"Message ID":n.message_id,Recipients:n.recipients,Status:n.status,...n.scheduled_at&&{"Scheduled At":n.scheduled_at}},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to send email"),process.exit(1);}})}function me(){let i=new Command("contacts").description(`Manage contacts
159
+ emailr broadcasts Send bulk emails to segments`).requiredOption("--to <email>","Recipient email address (comma-separated for multiple)").option("--from <email>","Sender email address").option("--subject <subject>","Email subject").option("--html <html>","HTML content (inline)").option("--text <text>","Plain text content (inline)").option("--html-file <path>","Read HTML content from file").option("--text-file <path>","Read plain text content from file").option("--template <id>","Template ID to use").option("--template-data <json>","Template data as JSON").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--reply-to <email>","Reply-to email address").option("--schedule <datetime>","Schedule send time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r=t.to.split(",").map(s=>s.trim()),a={to:r.length===1?r[0]:r};if(t.from&&(a.from=t.from),t.subject&&(a.subject=t.subject),t.htmlFile)try{a.html=readFileSync(t.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${t.htmlFile}`),process.exit(1);}else t.html&&(a.html=t.html);if(t.textFile)try{a.text=readFileSync(t.textFile,"utf-8");}catch{l(`Failed to read text file: ${t.textFile}`),process.exit(1);}else t.text&&(a.text=t.text);if(t.template&&(a.template_id=t.template),t.templateData)try{a.template_data=JSON.parse(t.templateData);}catch{l("Invalid JSON for --template-data"),process.exit(1);}if(t.cc){let s=t.cc.split(",").map(p=>p.trim());a.cc=s.length===1?s[0]:s;}if(t.bcc){let s=t.bcc.split(",").map(p=>p.trim());a.bcc=s.length===1?s[0]:s;}t.replyTo&&(a.reply_to_email=t.replyTo),t.schedule&&(a.scheduled_at=t.schedule);let n=await o.emails.send(a);t.format==="json"?c(n,"json"):(d("Email sent successfully!"),c({"Message ID":n.message_id,Recipients:n.recipients,Status:n.status,...n.scheduled_at&&{"Scheduled At":n.scheduled_at}},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to send email"),process.exit(1);}})}function me(){let i=new Command("contacts").description(`Manage contacts
160
160
 
161
161
  USAGE
162
162
  emailr contacts <subcommand> [options]
@@ -284,8 +284,8 @@ EXAMPLES
284
284
  emailr contacts list --format json
285
285
 
286
286
  # Combine filters with pagination
287
- 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("--tags <tags>","Filter by tags (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={limit:parseInt(t.limit,10),offset:parseInt(t.offset,10)};if(t.subscribed&&(r.subscribed=!0),t.unsubscribed&&(r.subscribed=!1),t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let o=await a.contacts.list(r);if(t.format==="json")c(o,"json");else {let n=o.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}));c(n,"table"),console.log(`
288
- Total: ${o.total}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list contacts"),process.exit(1);}}),i.command("get <contact_id>").description(`Get a contact by ID
287
+ 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=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={limit:parseInt(t.limit,10),offset:parseInt(t.offset,10)};if(t.subscribed&&(r.subscribed=!0),t.unsubscribed&&(r.subscribed=!1),t.search&&(r.search=t.search),t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let a=await o.contacts.list(r);if(t.format==="json")c(a,"json");else {let n=a.contacts.map(s=>({ID:s.id,Email:s.email,Name:[s.first_name,s.last_name].filter(Boolean).join(" ")||"-",Subscribed:s.subscribed,Tags:s.tags?.join(", ")||"-",Created:s.created_at}));c(n,"table"),console.log(`
288
+ Total: ${a.total}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list contacts"),process.exit(1);}}),i.command("get <contact_id>").description(`Get a contact by ID
289
289
 
290
290
  USAGE
291
291
  emailr contacts get <contact_id> [options]
@@ -312,7 +312,7 @@ EXAMPLES
312
312
  emailr contacts get con_abc123 --format json
313
313
 
314
314
  # Pipe JSON to jq for processing
315
- emailr contacts get con_abc123 --format json | jq '.metadata'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).contacts.get(t);c(o,e.format);}catch(a){l(a instanceof Error?a.message:"Failed to get contact"),process.exit(1);}}),i.command("create").description(`Create a new contact
315
+ emailr contacts get con_abc123 --format json | jq '.metadata'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).contacts.get(t);c(a,e.format);}catch(o){l(o instanceof Error?o.message:"Failed to get contact"),process.exit(1);}}),i.command("create").description(`Create a new contact
316
316
 
317
317
  USAGE
318
318
  emailr contacts create --email <email_address> [options]
@@ -354,7 +354,7 @@ EXAMPLES
354
354
  emailr contacts create --email "user@example.com" --subscribed false
355
355
 
356
356
  # Get JSON output for scripting
357
- 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=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={email:t.email};if(t.firstName&&(r.first_name=t.firstName),t.lastName&&(r.last_name=t.lastName),t.subscribed!==void 0&&(r.subscribed=t.subscribed),t.metadata)try{r.metadata=JSON.parse(t.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}t.tags&&(r.tags=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let o=await a.contacts.create(r);t.format==="json"?c(o,"json"):(d(`Contact created: ${o.id}`),c(o,"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create contact"),process.exit(1);}}),i.command("update <contact_id>").description(`Update a contact
357
+ 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=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={email:t.email};if(t.firstName&&(r.first_name=t.firstName),t.lastName&&(r.last_name=t.lastName),t.subscribed!==void 0&&(r.subscribed=t.subscribed),t.metadata)try{r.metadata=JSON.parse(t.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}t.tags&&(r.tags=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let a=await o.contacts.create(r);t.format==="json"?c(a,"json"):(d(`Contact created: ${a.id}`),c(a,"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create contact"),process.exit(1);}}),i.command("update <contact_id>").description(`Update a contact
358
358
 
359
359
  USAGE
360
360
  emailr contacts update <contact_id> [options]
@@ -402,7 +402,7 @@ EXAMPLES
402
402
  emailr contacts update con_abc123 --metadata '{"plan": "enterprise", "upgraded": true}'
403
403
 
404
404
  # Get JSON output
405
- 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 a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={};if(e.email&&(o.email=e.email),e.firstName&&(o.first_name=e.firstName),e.lastName&&(o.last_name=e.lastName),e.subscribed&&(o.subscribed=!0),e.unsubscribed&&(o.subscribed=!1),e.metadata)try{o.metadata=JSON.parse(e.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}e.tags&&(o.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.contacts.update(t,o);e.format==="json"?c(n,"json"):(d(`Contact updated: ${n.id}`),c(n,"table"));}catch(a){l(a instanceof Error?a.message:"Failed to update contact"),process.exit(1);}}),i.command("delete <contact_id>").description(`Delete a contact
405
+ emailr contacts update con_abc123 --first-name "Jane" --format json`).option("--email <email>","New email address").option("--first-name <name>","First name").option("--last-name <name>","Last name").option("--subscribed","Mark as subscribed").option("--unsubscribed","Mark as unsubscribed").option("--metadata <json>","Metadata as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.email&&(a.email=e.email),e.firstName&&(a.first_name=e.firstName),e.lastName&&(a.last_name=e.lastName),e.subscribed&&(a.subscribed=!0),e.unsubscribed&&(a.subscribed=!1),e.metadata)try{a.metadata=JSON.parse(e.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}e.tags&&(a.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.contacts.update(t,a);e.format==="json"?c(n,"json"):(d(`Contact updated: ${n.id}`),c(n,"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update contact"),process.exit(1);}}),i.command("delete <contact_id>").description(`Delete a contact
406
406
 
407
407
  USAGE
408
408
  emailr contacts delete <contact_id>
@@ -419,7 +419,7 @@ EXAMPLES
419
419
  emailr contacts delete con_abc123
420
420
 
421
421
  WARNING
422
- This action is permanent and cannot be undone.`).action(async t=>{try{let e=m();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).contacts.delete(t),d(`Contact deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete contact"),process.exit(1);}}),i}function pe(){return N.join(B.homedir(),".config","emailr","templates")}function qe(){let i=pe();v.existsSync(i)||v.mkdirSync(i,{recursive:true});}function W(i){return N.join(pe(),`${i}.html`)}function X(i,t){qe();let e=W(i);v.writeFileSync(e,t,"utf-8");}function ue(i){let t=W(i);return v.existsSync(t)?v.readFileSync(t,"utf-8"):null}function fe(i){let t=W(i);return v.existsSync(t)}var w=null,C=null,H=false;function he(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function $e(i){return i===null||i.trim()===""}function Be(i){return `<!DOCTYPE html>
422
+ This action is permanent and cannot be undone.`).action(async t=>{try{let e=m();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).contacts.delete(t),d(`Contact deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete contact"),process.exit(1);}}),i}function pe(){return _.join(B.homedir(),".config","emailr","templates")}function qe(){let i=pe();v.existsSync(i)||v.mkdirSync(i,{recursive:true});}function W(i){return _.join(pe(),`${i}.html`)}function X(i,t){qe();let e=W(i);v.writeFileSync(e,t,"utf-8");}function ue(i){let t=W(i);return v.existsSync(t)?v.readFileSync(t,"utf-8"):null}function fe(i){let t=W(i);return v.existsSync(t)}var w=null,U=null,H=false;function he(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}function $e(i){return i===null||i.trim()===""}function Be(i){return `<!DOCTYPE html>
423
423
  <html lang="en">
424
424
  <head>
425
425
  <meta charset="UTF-8">
@@ -548,7 +548,7 @@ WARNING
548
548
  <p style="margin-top: 1rem;">Try creating or retrieving the template first using the CLI.</p>
549
549
  </div>
550
550
  </body>
551
- </html>`}function We(i){let t=i.match(/^\/preview\/([^/]+)$/);return t?t[1]:null}function Xe(){return (i,t)=>{if(i.method!=="GET"){t.writeHead(405,{"Content-Type":"text/plain"}),t.end("Method Not Allowed");return}let e=i.url||"/",a=We(e);if(!a){t.writeHead(404,{"Content-Type":"text/plain"}),t.end("Not Found");return}if(!fe(a)){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(be(a));return}let r=ue(a);if(r===null){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(be(a));return}if($e(r)){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(Be(a));return}t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(r);}}var ge={async start(){return H&&C!==null?C:new Promise((i,t)=>{w=Je.createServer(Xe()),w.listen(0,"127.0.0.1",()=>{let e=w.address();e&&typeof e=="object"?(C=e.port,H=true,w.unref(),i(C)):t(new Error("Failed to get server address"));}),w.on("error",e=>{H=false,C=null,w=null,t(new Error(`Failed to start preview server: ${e.message}`));});})},getPort(){return C},isRunning(){return H},async stop(){if(w)return new Promise(i=>{w.close(()=>{w=null,C=null,H=false,i();});})}};function we(){return ge}async function V(i){try{return `http://127.0.0.1:${await ge.start()}/preview/${i}`}catch{return null}}function ye(){w&&w.ref();}var P=null,z=null,J=null,R=[],ve=`
551
+ </html>`}function We(i){let t=i.match(/^\/preview\/([^/]+)$/);return t?t[1]:null}function Xe(){return (i,t)=>{if(i.method!=="GET"){t.writeHead(405,{"Content-Type":"text/plain"}),t.end("Method Not Allowed");return}let e=i.url||"/",o=We(e);if(!o){t.writeHead(404,{"Content-Type":"text/plain"}),t.end("Not Found");return}if(!fe(o)){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(be(o));return}let r=ue(o);if(r===null){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(be(o));return}if($e(r)){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(Be(o));return}t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(r);}}var ge={async start(){return H&&U!==null?U:new Promise((i,t)=>{w=Je.createServer(Xe()),w.listen(0,"127.0.0.1",()=>{let e=w.address();e&&typeof e=="object"?(U=e.port,H=true,w.unref(),i(U)):t(new Error("Failed to get server address"));}),w.on("error",e=>{H=false,U=null,w=null,t(new Error(`Failed to start preview server: ${e.message}`));});})},getPort(){return U},isRunning(){return H},async stop(){if(w)return new Promise(i=>{w.close(()=>{w=null,U=null,H=false,i();});})}};function we(){return ge}async function V(i){try{return `http://127.0.0.1:${await ge.start()}/preview/${i}`}catch{return null}}function ye(){w&&w.ref();}var P=null,z=null,J=null,R=[],ve=`
552
552
  <script>
553
553
  (function() {
554
554
  const evtSource = new EventSource('/__live-reload');
@@ -564,9 +564,9 @@ WARNING
564
564
  </script>
565
565
  `;function Ye(i){return i.includes("</body>")?i.replace("</body>",`${ve}</body>`):i+ve}function Ze(){R.forEach(i=>{try{i.write(`data: reload
566
566
 
567
- `);}catch{}});}async function Y(i,t){let e=N.resolve(i);return new Promise((a,r)=>{P=Je.createServer((o,n)=>{if(o.url==="/__live-reload"){n.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),n.write(`data: connected
567
+ `);}catch{}});}async function Y(i,t){let e=_.resolve(i);return new Promise((o,r)=>{P=Je.createServer((a,n)=>{if(a.url==="/__live-reload"){n.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),n.write(`data: connected
568
568
 
569
- `),R.push(n),o.on("close",()=>{R=R.filter(s=>s!==n);});return}if(o.method==="GET"){try{let s=v.readFileSync(e,"utf-8"),p=Ye(s);n.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),n.end(p);}catch(s){n.writeHead(500,{"Content-Type":"text/plain"}),n.end(`Error reading file: ${s instanceof Error?s.message:String(s)}`);}return}n.writeHead(405,{"Content-Type":"text/plain"}),n.end("Method Not Allowed");}),P.listen(0,"127.0.0.1",()=>{let o=P.address();if(o&&typeof o=="object"){z=o.port;let n=null;J=v.watch(e,s=>{s==="change"&&(n&&clearTimeout(n),n=setTimeout(()=>{Ze(),t?.();},100));}),a(z);}else r(new Error("Failed to get server address"));}),P.on("error",o=>{r(new Error(`Failed to start server: ${o.message}`));});})}async function Z(){if(J&&(J.close(),J=null),R.forEach(i=>{try{i.end();}catch{}}),R=[],P)return new Promise(i=>{P.close(()=>{P=null,z=null,i();});})}async function Te(i){try{let t=i.html_content??"";X(i.id,t);}catch(t){return I(`Could not save template for preview: ${t instanceof Error?t.message:String(t)}`),null}try{let t=await V(i.id);return t===null?(I("Could not start preview server"),null):t}catch(t){return I(`Could not generate preview URL: ${t instanceof Error?t.message:String(t)}`),null}}function Oe(){let i=new Command("templates").description(`Manage email templates
569
+ `),R.push(n),a.on("close",()=>{R=R.filter(s=>s!==n);});return}if(a.method==="GET"){try{let s=v.readFileSync(e,"utf-8"),p=Ye(s);n.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),n.end(p);}catch(s){n.writeHead(500,{"Content-Type":"text/plain"}),n.end(`Error reading file: ${s instanceof Error?s.message:String(s)}`);}return}n.writeHead(405,{"Content-Type":"text/plain"}),n.end("Method Not Allowed");}),P.listen(0,"127.0.0.1",()=>{let a=P.address();if(a&&typeof a=="object"){z=a.port;let n=null;J=v.watch(e,s=>{s==="change"&&(n&&clearTimeout(n),n=setTimeout(()=>{Ze(),t?.();},100));}),o(z);}else r(new Error("Failed to get server address"));}),P.on("error",a=>{r(new Error(`Failed to start server: ${a.message}`));});})}async function Z(){if(J&&(J.close(),J=null),R.forEach(i=>{try{i.end();}catch{}}),R=[],P)return new Promise(i=>{P.close(()=>{P=null,z=null,i();});})}async function Te(i){try{let t=i.html_content??"";X(i.id,t);}catch(t){return k(`Could not save template for preview: ${t instanceof Error?t.message:String(t)}`),null}try{let t=await V(i.id);return t===null?(k("Could not start preview server"),null):t}catch(t){return k(`Could not generate preview URL: ${t instanceof Error?t.message:String(t)}`),null}}function Oe(){let i=new Command("templates").description(`Manage email templates
570
570
 
571
571
  USAGE
572
572
  emailr templates <subcommand> [options]
@@ -666,8 +666,8 @@ EXAMPLES
666
666
  emailr templates list --page 2 --limit 10
667
667
 
668
668
  # Get JSON output for scripting
669
- 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=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={limit:parseInt(t.limit,10),page:parseInt(t.page,10)};if(t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let o=await a.templates.list(r);if(t.format==="json")c(o,"json");else {let n=o.map(s=>({ID:s.id,Name:s.name,Subject:s.subject,Variables:s.variables?.join(", ")||"-",Tags:s.tags?.join(", ")||"-",Created:s.created_at}));c(n,"table"),console.log(`
670
- Total: ${o.length}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list templates"),process.exit(1);}}),i.command("get <template_id>").description(`Get a template by ID
669
+ 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=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={limit:parseInt(t.limit,10),page:parseInt(t.page,10)};if(t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let a=await o.templates.list(r);if(t.format==="json")c(a,"json");else {let n=a.map(s=>({ID:s.id,Name:s.name,Subject:s.subject,Variables:s.variables?.join(", ")||"-",Tags:s.tags?.join(", ")||"-",Created:s.created_at}));c(n,"table"),console.log(`
670
+ Total: ${a.length}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list templates"),process.exit(1);}}),i.command("get <template_id>").description(`Get a template by ID
671
671
 
672
672
  USAGE
673
673
  emailr templates get <template_id> [options]
@@ -695,7 +695,7 @@ EXAMPLES
695
695
  emailr templates get tpl_abc123 --format json
696
696
 
697
697
  # Pipe JSON to jq for processing
698
- emailr templates get tpl_abc123 --format json | jq '.html_content'`).option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).templates.get(t),n=await Te({id:o.id,html_content:o.html_content??void 0}),s=o.preview_html?`${a.baseUrl}/preview/${o.id}`:null;if(e.format==="json")c({...o,preview_url:s??n},"json");else {let p={ID:o.id,Name:o.name,Subject:o.subject,Variables:o.variables?.join(", ")||"-",Created:o.created_at};s?p["Preview URL"]=s:n&&(p["Local Preview"]=n),c(p,"table");}}catch(a){l(a instanceof Error?a.message:"Failed to get template"),process.exit(1);}}),i.command("fetch <template_id>").description(`Download template HTML to file or stdout
698
+ emailr templates get tpl_abc123 --format json | jq '.html_content'`).option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).templates.get(t),n=await Te({id:a.id,html_content:a.html_content??void 0}),s=a.preview_html?`${o.baseUrl}/preview/${a.id}`:null;if(e.format==="json")c({...a,preview_url:s??n},"json");else {let p={ID:a.id,Name:a.name,Subject:a.subject,Variables:a.variables?.join(", ")||"-",Created:a.created_at};s?p["Preview URL"]=s:n&&(p["Local Preview"]=n),c(p,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to get template"),process.exit(1);}}),i.command("fetch <template_id>").description(`Download template HTML to file or stdout
699
699
 
700
700
  USAGE
701
701
  emailr templates fetch <template_id> [options]
@@ -729,8 +729,8 @@ AGENTIC WORKFLOW
729
729
  This is step 1 of the agentic workflow:
730
730
  1. Fetch: emailr templates fetch <id> --output template.html
731
731
  2. Edit locally or with AI assistance
732
- 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 a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).templates.fetch(t,{preview:e.preview??!1});if(o||(e.preview?(l("No preview HTML exists for this template"),console.log(`
733
- Run 'emailr templates push-preview <id> --html-file <path>' to create a preview.`)):l("Template has no HTML content"),process.exit(1)),e.output){let n=N.resolve(e.output);v.writeFileSync(n,o,"utf-8"),d(`HTML saved to: ${n}`);}else console.log(o);}catch(a){l(a instanceof Error?a.message:"Failed to fetch template"),process.exit(1);}}),i.command("push-preview <template_id>").description(`Upload HTML to template's preview for sharing with AI agents
732
+ 3. Push preview: emailr templates push-preview <id> --html-file template.html`).option("--output <file_path>","Write HTML to file (default: stdout)").option("--preview","Fetch preview HTML instead of published HTML").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).templates.fetch(t,{preview:e.preview??!1});if(a||(e.preview?(l("No preview HTML exists for this template"),console.log(`
733
+ Run 'emailr templates push-preview <id> --html-file <path>' to create a preview.`)):l("Template has no HTML content"),process.exit(1)),e.output){let n=_.resolve(e.output);v.writeFileSync(n,a,"utf-8"),d(`HTML saved to: ${n}`);}else console.log(a);}catch(o){l(o instanceof Error?o.message:"Failed to fetch template"),process.exit(1);}}),i.command("push-preview <template_id>").description(`Upload HTML to template's preview for sharing with AI agents
734
734
 
735
735
  USAGE
736
736
  emailr templates push-preview <template_id> --html-file <file_path>
@@ -774,8 +774,8 @@ AGENTIC WORKFLOW
774
774
  3. Push preview: emailr templates push-preview <id> --html-file template.html
775
775
  4. Share the preview URL with your AI agent for feedback
776
776
  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(`
777
- 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 a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o;if(e.htmlFile){let p=N.resolve(e.htmlFile);try{o=v.readFileSync(p,"utf-8");}catch{l(`Failed to read file: ${p}`),process.exit(1);}}else o=e.html;let n=await r.templates.pushPreview(t,o),s="updated";e.format==="json"?c({template_id:t,preview_url:n.preview_url,status:s},"json"):(d("Preview uploaded successfully"),c({"Template ID":t,"Preview URL":n.preview_url,Status:s},"table"),console.log(`
778
- Share this URL with your AI agent for feedback.`));}catch(a){l(a instanceof Error?a.message:"Failed to push preview"),process.exit(1);}}),i.command("create").description(`Create a new email template
777
+ Usage:`),console.log(" emailr templates push-preview <id> --html-file <path>"),console.log(" emailr templates push-preview <id> --html <content>"),process.exit(1)),e.htmlFile&&e.html&&(l("Cannot use both --html-file and --html. Choose one."),process.exit(1));let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a;if(e.htmlFile){let p=_.resolve(e.htmlFile);try{a=v.readFileSync(p,"utf-8");}catch{l(`Failed to read file: ${p}`),process.exit(1);}}else a=e.html;let n=await r.templates.pushPreview(t,a),s="updated";e.format==="json"?c({template_id:t,preview_url:n.preview_url,status:s},"json"):(d("Preview uploaded successfully"),c({"Template ID":t,"Preview URL":n.preview_url,Status:s},"table"),console.log(`
778
+ Share this URL with your AI agent for feedback.`));}catch(o){l(o instanceof Error?o.message:"Failed to push preview"),process.exit(1);}}),i.command("create").description(`Create a new email template
779
779
 
780
780
  USAGE
781
781
  emailr templates create --name <template_name> --subject <subject_line> [options]
@@ -820,7 +820,7 @@ EXAMPLES
820
820
 
821
821
  TIP
822
822
  For live preview while building, use "emailr templates draft" first.
823
- 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("--reply-to <email_address>","Default reply-to email address").option("--preview-text <text>","Preview text shown in email clients").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={name:t.name,subject:t.subject};if(t.htmlFile){let s=await import('fs');r.html_content=s.readFileSync(t.htmlFile,"utf-8");}else t.html&&(r.html_content=t.html);if(t.textFile){let s=await import('fs');r.text_content=s.readFileSync(t.textFile,"utf-8");}else t.text&&(r.text_content=t.text);t.from&&(r.from_email=t.from),t.replyTo&&(r.reply_to=t.replyTo),t.previewText&&(r.preview_text=t.previewText),t.tags&&(r.tags=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let o=await a.templates.create(r),n=await Te({id:o.id,html_content:o.html_content??void 0});if(t.format==="json")c({...o,preview_url:n},"json");else {d(`Template created: ${o.id}`);let s={ID:o.id,Name:o.name,Subject:o.subject,Variables:o.variables?.join(", ")||"-",Tags:o.tags?.join(", ")||"-"};n&&(s["Preview URL"]=n),c(s,"table"),n&&console.log(`
823
+ 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("--reply-to <email_address>","Default reply-to email address").option("--preview-text <text>","Preview text shown in email clients").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={name:t.name,subject:t.subject};if(t.htmlFile){let s=await import('fs');r.html_content=s.readFileSync(t.htmlFile,"utf-8");}else t.html&&(r.html_content=t.html);if(t.textFile){let s=await import('fs');r.text_content=s.readFileSync(t.textFile,"utf-8");}else t.text&&(r.text_content=t.text);t.from&&(r.from_email=t.from),t.replyTo&&(r.reply_to=t.replyTo),t.previewText&&(r.preview_text=t.previewText),t.tags&&(r.tags=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let a=await o.templates.create(r),n=await Te({id:a.id,html_content:a.html_content??void 0});if(t.format==="json")c({...a,preview_url:n},"json");else {d(`Template created: ${a.id}`);let s={ID:a.id,Name:a.name,Subject:a.subject,Variables:a.variables?.join(", ")||"-",Tags:a.tags?.join(", ")||"-"};n&&(s["Preview URL"]=n),c(s,"table"),n&&console.log(`
824
824
  Open the Preview URL in your browser to view the rendered template.`);}}catch(e){l(e instanceof Error?e.message:"Failed to create template"),process.exit(1);}}),i.command("update <template_id>").description(`Update an existing email template
825
825
 
826
826
  USAGE
@@ -873,7 +873,7 @@ AGENTIC WORKFLOW
873
873
  2. Edit locally or with AI assistance
874
874
  3. Push preview: emailr templates push-preview <id> --html-file template.html
875
875
  4. Share URL with AI agent, iterate on feedback
876
- 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("--reply-to <email_address>","New default reply-to email address").option("--preview-text <text>","New preview text").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={};if(e.name&&(o.name=e.name),e.subject&&(o.subject=e.subject),e.htmlFile){let s=await import('fs');o.html_content=s.readFileSync(e.htmlFile,"utf-8");}else e.html&&(o.html_content=e.html);if(e.textFile){let s=await import('fs');o.text_content=s.readFileSync(e.textFile,"utf-8");}else e.text&&(o.text_content=e.text);e.from&&(o.from_email=e.from),e.replyTo&&(o.reply_to=e.replyTo),e.previewText&&(o.preview_text=e.previewText),e.tags&&(o.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.templates.update(t,o);if(e.format==="json")c(n,"json");else {d(`Template updated: ${n.id}`);let s={ID:n.id,Name:n.name,Subject:n.subject,Variables:n.variables?.join(", ")||"-",Tags:n.tags?.join(", ")||"-",Updated:n.updated_at};c(s,"table");}}catch(a){l(a instanceof Error?a.message:"Failed to update template"),process.exit(1);}}),i.command("delete <template_id>").description(`Delete a template
876
+ 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("--reply-to <email_address>","New default reply-to email address").option("--preview-text <text>","New preview text").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.name&&(a.name=e.name),e.subject&&(a.subject=e.subject),e.htmlFile){let s=await import('fs');a.html_content=s.readFileSync(e.htmlFile,"utf-8");}else e.html&&(a.html_content=e.html);if(e.textFile){let s=await import('fs');a.text_content=s.readFileSync(e.textFile,"utf-8");}else e.text&&(a.text_content=e.text);e.from&&(a.from_email=e.from),e.replyTo&&(a.reply_to=e.replyTo),e.previewText&&(a.preview_text=e.previewText),e.tags&&(a.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.templates.update(t,a);if(e.format==="json")c(n,"json");else {d(`Template updated: ${n.id}`);let s={ID:n.id,Name:n.name,Subject:n.subject,Variables:n.variables?.join(", ")||"-",Tags:n.tags?.join(", ")||"-",Updated:n.updated_at};c(s,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to update template"),process.exit(1);}}),i.command("delete <template_id>").description(`Delete a template
877
877
 
878
878
  USAGE
879
879
  emailr templates delete <template_id>
@@ -919,12 +919,12 @@ EXAMPLES
919
919
 
920
920
  SEE ALSO
921
921
  emailr templates edit Live editing with hot-reload
922
- 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 a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl});console.log(`Fetching template ${t}...`);let o=await r.templates.get(t),n=o.html_content??"";if(X(o.id,n),e.foreground){let f=await V(o.id);if(f||(l("Failed to start preview server"),process.exit(1)),ye(),console.log(`
923
- Template: ${o.name}`),console.log(`Preview URL: ${f}`),e.open!==!1)try{await(await import('open')).default(f),console.log(`
922
+ emailr templates stop-preview Stop background preview server`).option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(t,e)=>{try{let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl});console.log(`Fetching template ${t}...`);let a=await r.templates.get(t),n=a.html_content??"";if(X(a.id,n),e.foreground){let f=await V(a.id);if(f||(l("Failed to start preview server"),process.exit(1)),ye(),console.log(`
923
+ Template: ${a.name}`),console.log(`Preview URL: ${f}`),e.open!==!1)try{await(await import('open')).default(f),console.log(`
924
924
  Browser opened.`);}catch{console.log(`
925
925
  Could not open browser automatically. Open the URL above manually.`);}process.on("SIGINT",async()=>{console.log(`
926
926
 
927
- Stopping preview server...`),await we().stop(),console.log("Done."),process.exit(0);});return}let s=N.join(process.cwd(),`.template-preview-${o.id}.html`);v.writeFileSync(s,n,"utf-8");let{spawn:p}=await import('child_process'),b=await import('os'),T=N.join(b.tmpdir(),"emailr-preview.pid");try{let f=v.readFileSync(T,"utf-8").trim();process.kill(parseInt(f,10),"SIGTERM");}catch{}let h=`
927
+ Stopping preview server...`),await we().stop(),console.log("Done."),process.exit(0);});return}let s=_.join(process.cwd(),`.template-preview-${a.id}.html`);v.writeFileSync(s,n,"utf-8");let{spawn:p}=await import('child_process'),b=await import('os'),T=_.join(b.tmpdir(),"emailr-preview.pid");try{let f=v.readFileSync(T,"utf-8").trim();process.kill(parseInt(f,10),"SIGTERM");}catch{}let h=`
928
928
  const http = require('http');
929
929
  const fs = require('fs');
930
930
  const path = require('path');
@@ -949,9 +949,9 @@ Stopping preview server...`),await we().stop(),console.log("Done."),process.exit
949
949
  console.log('PORT:' + port);
950
950
  });
951
951
  process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); fs.unlinkSync(filePath); } catch {} process.exit(0); });
952
- `,y=p("node",["-e",h],{detached:!0,stdio:["ignore","pipe","ignore"]}),O="";await new Promise(f=>{y.stdout?.on("data",_=>{let q=_.toString().match(/PORT:(\d+)/);q&&(O=q[1],f());}),setTimeout(f,3e3);}),y.unref();let D=`http://127.0.0.1:${O}/`;if(console.log(`
953
- Template: ${o.name}`),console.log(`Preview URL: ${D}`),console.log(`
954
- To stop: emailr templates stop-preview`),e.open!==!1)try{await(await import('open')).default(D);}catch{}process.exit(0);}catch(a){l(a instanceof Error?a.message:"Failed to preview template"),process.exit(1);}}),i.command("edit <template_id>").description(`Start a live editing session for a template with hot-reload
952
+ `,y=p("node",["-e",h],{detached:!0,stdio:["ignore","pipe","ignore"]}),O="";await new Promise(f=>{y.stdout?.on("data",N=>{let q=N.toString().match(/PORT:(\d+)/);q&&(O=q[1],f());}),setTimeout(f,3e3);}),y.unref();let D=`http://127.0.0.1:${O}/`;if(console.log(`
953
+ Template: ${a.name}`),console.log(`Preview URL: ${D}`),console.log(`
954
+ To stop: emailr templates stop-preview`),e.open!==!1)try{await(await import('open')).default(D);}catch{}process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to preview template"),process.exit(1);}}),i.command("edit <template_id>").description(`Start a live editing session for a template with hot-reload
955
955
 
956
956
  USAGE
957
957
  emailr templates edit <template_id> [options]
@@ -991,17 +991,17 @@ EXAMPLES
991
991
  SEE ALSO
992
992
  emailr templates draft Draft a new template with live preview
993
993
  emailr templates update Save changes to the template
994
- 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 a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o=N.resolve(e.file);console.log(`Fetching template ${t}...`);let n=await r.templates.get(t),s=n.html_content??"";if(v.writeFileSync(o,s,"utf-8"),console.log(`Template saved to: ${o}`),e.foreground){let _=`http://127.0.0.1:${await Y(o,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
995
- Template: ${n.name}`),console.log(`Template ID: ${n.id}`),console.log(`Live Preview: ${_}`),console.log(`File: ${o}`),console.log(`
996
- 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(_);}catch{}process.on("SIGINT",async()=>{console.log(`
994
+ emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for editing","./template.html").option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(t,e)=>{try{let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a=_.resolve(e.file);console.log(`Fetching template ${t}...`);let n=await r.templates.get(t),s=n.html_content??"";if(v.writeFileSync(a,s,"utf-8"),console.log(`Template saved to: ${a}`),e.foreground){let N=`http://127.0.0.1:${await Y(a,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
995
+ Template: ${n.name}`),console.log(`Template ID: ${n.id}`),console.log(`Live Preview: ${N}`),console.log(`File: ${a}`),console.log(`
996
+ 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(N);}catch{}process.on("SIGINT",async()=>{console.log(`
997
997
 
998
998
  Stopping live preview server...`),await Z(),console.log("Done."),console.log(`
999
- To save changes: emailr templates update ${t} --html-file ${e.file}`),process.exit(0);});return}let{spawn:p}=await import('child_process'),b=await import('os'),T=N.join(b.tmpdir(),"emailr-preview.pid");try{let f=v.readFileSync(T,"utf-8").trim();process.kill(parseInt(f,10),"SIGTERM");}catch{}let h=`
999
+ To save changes: emailr templates update ${t} --html-file ${e.file}`),process.exit(0);});return}let{spawn:p}=await import('child_process'),b=await import('os'),T=_.join(b.tmpdir(),"emailr-preview.pid");try{let f=v.readFileSync(T,"utf-8").trim();process.kill(parseInt(f,10),"SIGTERM");}catch{}let h=`
1000
1000
  const http = require('http');
1001
1001
  const fs = require('fs');
1002
1002
  const path = require('path');
1003
1003
  const os = require('os');
1004
- const filePath = ${JSON.stringify(o)};
1004
+ const filePath = ${JSON.stringify(a)};
1005
1005
  const pidFile = path.join(os.tmpdir(), 'emailr-preview.pid');
1006
1006
  const portFile = path.join(os.tmpdir(), 'emailr-preview.port');
1007
1007
  let clients = [];
@@ -1036,10 +1036,10 @@ To save changes: emailr templates update ${t} --html-file ${e.file}`),process.ex
1036
1036
  });
1037
1037
  });
1038
1038
  process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); } catch {} process.exit(0); });
1039
- `,y=p("node",["-e",h],{detached:!0,stdio:["ignore","pipe","ignore"]}),O="";await new Promise(f=>{y.stdout?.on("data",_=>{let ie=_.toString().match(/PORT:(\d+)/);ie&&(O=ie[1],f());}),setTimeout(f,3e3);}),y.unref();let D=`http://127.0.0.1:${O}/`;if(console.log(`
1040
- Template: ${n.name}`),console.log(`Template ID: ${n.id}`),console.log(`Live Preview: ${D}`),console.log(`File: ${o}`),console.log(`
1039
+ `,y=p("node",["-e",h],{detached:!0,stdio:["ignore","pipe","ignore"]}),O="";await new Promise(f=>{y.stdout?.on("data",N=>{let ie=N.toString().match(/PORT:(\d+)/);ie&&(O=ie[1],f());}),setTimeout(f,3e3);}),y.unref();let D=`http://127.0.0.1:${O}/`;if(console.log(`
1040
+ Template: ${n.name}`),console.log(`Template ID: ${n.id}`),console.log(`Live Preview: ${D}`),console.log(`File: ${a}`),console.log(`
1041
1041
  Edit the file and see live updates in the browser.`),console.log(`
1042
- 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(D);}catch{}process.exit(0);}catch(a){l(a instanceof Error?a.message:"Failed to edit template"),process.exit(1);}}),i.command("draft").description(`Start a live drafting session for a new template with hot-reload
1042
+ 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(D);}catch{}process.exit(0);}catch(o){l(o instanceof Error?o.message:"Failed to edit template"),process.exit(1);}}),i.command("draft").description(`Start a live drafting session for a new template with hot-reload
1043
1043
 
1044
1044
  USAGE
1045
1045
  emailr templates draft [options]
@@ -1081,7 +1081,7 @@ EXAMPLES
1081
1081
  SEE ALSO
1082
1082
  emailr templates edit Edit an existing template with live preview
1083
1083
  emailr templates create Create the template from your draft
1084
- 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=N.resolve(t.file),a;if(t.blank?a="":a=`<!DOCTYPE html>
1084
+ emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for drafting","./new-template.html").option("--no-open","Do not automatically open browser").option("--blank","Start with a blank file instead of starter template").option("--foreground","Keep process running in foreground (blocks terminal)").action(async t=>{try{let e=_.resolve(t.file),o;if(t.blank?o="":o=`<!DOCTYPE html>
1085
1085
  <html>
1086
1086
  <head>
1087
1087
  <meta charset="UTF-8">
@@ -1116,13 +1116,13 @@ SEE ALSO
1116
1116
  <p><a href="{{unsubscribe_link}}">Unsubscribe</a></p>
1117
1117
  </div>
1118
1118
  </body>
1119
- </html>`,v.writeFileSync(e,a,"utf-8"),console.log(`Template draft created: ${e}`),t.foreground){let y=`http://127.0.0.1:${await Y(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1119
+ </html>`,v.writeFileSync(e,o,"utf-8"),console.log(`Template draft created: ${e}`),t.foreground){let y=`http://127.0.0.1:${await Y(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
1120
1120
  Live Preview: ${y}`),console.log(`File: ${e}`),console.log(`
1121
1121
  Watching for changes... Edit the file and see live updates.`),console.log(`
1122
1122
  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(y);}catch{}process.on("SIGINT",async()=>{console.log(`
1123
1123
 
1124
1124
  Stopping live preview server...`),await Z(),console.log("Done."),console.log(`
1125
- To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),process.exit(0);});return}let{spawn:r}=await import('child_process'),o=await import('os'),n=N.join(o.tmpdir(),"emailr-preview.pid");try{let h=v.readFileSync(n,"utf-8").trim();process.kill(parseInt(h,10),"SIGTERM");}catch{}let s=`
1125
+ To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),process.exit(0);});return}let{spawn:r}=await import('child_process'),a=await import('os'),n=_.join(a.tmpdir(),"emailr-preview.pid");try{let h=v.readFileSync(n,"utf-8").trim();process.kill(parseInt(h,10),"SIGTERM");}catch{}let s=`
1126
1126
  const http = require('http');
1127
1127
  const fs = require('fs');
1128
1128
  const path = require('path');
@@ -1182,7 +1182,7 @@ EXAMPLES
1182
1182
  SEE ALSO
1183
1183
  emailr templates edit Start live editing session
1184
1184
  emailr templates draft Start live drafting session
1185
- emailr templates preview Preview a template in browser`).action(async()=>{let t=await import('os'),e=N.join(t.tmpdir(),"emailr-preview.pid");try{let a=v.readFileSync(e,"utf-8").trim();process.kill(parseInt(a,10),"SIGTERM"),v.unlinkSync(e),d("Preview server stopped");}catch{d("No preview server running");}}),i}function Ee(){let i=new Command("domains").description(`Manage sending domains
1185
+ emailr templates preview Preview a template in browser`).action(async()=>{let t=await import('os'),e=_.join(t.tmpdir(),"emailr-preview.pid");try{let o=v.readFileSync(e,"utf-8").trim();process.kill(parseInt(o,10),"SIGTERM"),v.unlinkSync(e),d("Preview server stopped");}catch{d("No preview server running");}}),i}function Ee(){let i=new Command("domains").description(`Manage sending domains
1186
1186
 
1187
1187
  USAGE
1188
1188
  emailr domains <subcommand> [options]
@@ -1290,7 +1290,7 @@ EXAMPLES
1290
1290
  emailr domains list --format json | jq '.[] | select(.status == "verified")'
1291
1291
 
1292
1292
  # Count domains
1293
- emailr domains list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.list();if(t.format==="json")c(r,"json");else {let o=r.map(n=>({ID:n.id,Domain:n.domain,Status:n.status,DKIM:n.dkim_verified,SPF:n.spf_verified,DMARC:n.dmarc_verified,Created:n.created_at}));c(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list domains"),process.exit(1);}}),i.command("get <domain_id>").description(`Get domain details
1293
+ emailr domains list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.list();if(t.format==="json")c(r,"json");else {let a=r.map(n=>({ID:n.id,Domain:n.domain,Status:n.status,DKIM:n.dkim_verified,SPF:n.spf_verified,DMARC:n.dmarc_verified,Created:n.created_at}));c(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list domains"),process.exit(1);}}),i.command("get <domain_id>").description(`Get domain details
1294
1294
 
1295
1295
  USAGE
1296
1296
  emailr domains get <domain_id> [options]
@@ -1317,7 +1317,7 @@ EXAMPLES
1317
1317
  emailr domains get dom_abc123 --format json
1318
1318
 
1319
1319
  # Extract DNS records
1320
- emailr domains get dom_abc123 --format json | jq '.dns_records'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).domains.get(t);c(o,e.format);}catch(a){l(a instanceof Error?a.message:"Failed to get domain"),process.exit(1);}}),i.command("add <domain_name>").description(`Add a new domain
1320
+ emailr domains get dom_abc123 --format json | jq '.dns_records'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.get(t);c(a,e.format);}catch(o){l(o instanceof Error?o.message:"Failed to get domain"),process.exit(1);}}),i.command("add <domain_name>").description(`Add a new domain
1321
1321
 
1322
1322
  USAGE
1323
1323
  emailr domains add <domain_name> [options]
@@ -1370,7 +1370,7 @@ NEXT STEPS
1370
1370
  1. Copy the DNS records shown in the output
1371
1371
  2. Add them to your DNS provider (Cloudflare, Route53, etc.)
1372
1372
  3. Wait for DNS propagation (up to 48 hours)
1373
- 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 a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={domain:t};e.receivingSubdomain&&(o.receivingSubdomain=e.receivingSubdomain);let n=await r.domains.add(o);if(e.format==="json")c(n,"json");else {if(d(`Domain added: ${n.domain}`),u("Add the following DNS records to verify your domain:"),console.log(""),n.dns_records){let s=[{Type:"DKIM",...Q(n.dns_records.dkim)},{Type:"SPF",...Q(n.dns_records.spf)},{Type:"DMARC",...Q(n.dns_records.dmarc)}];c(s,"table");}console.log(""),u(`Run 'emailr domains verify ${n.id}' after adding DNS records`);}}catch(a){l(a instanceof Error?a.message:"Failed to add domain"),process.exit(1);}}),i.command("verify <domain_id>").description(`Verify a domain
1373
+ 4. Run: emailr domains verify <domain_id>`).option("--receiving-subdomain <subdomain>","Subdomain for receiving emails").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={domain:t};e.receivingSubdomain&&(a.receivingSubdomain=e.receivingSubdomain);let n=await r.domains.add(a);if(e.format==="json")c(n,"json");else {if(d(`Domain added: ${n.domain}`),u("Add the following DNS records to verify your domain:"),console.log(""),n.dns_records){let s=[{Type:"DKIM",...Q(n.dns_records.dkim)},{Type:"SPF",...Q(n.dns_records.spf)},{Type:"DMARC",...Q(n.dns_records.dmarc)}];c(s,"table");}console.log(""),u(`Run 'emailr domains verify ${n.id}' after adding DNS records`);}}catch(o){l(o instanceof Error?o.message:"Failed to add domain"),process.exit(1);}}),i.command("verify <domain_id>").description(`Verify a domain
1374
1374
 
1375
1375
  USAGE
1376
1376
  emailr domains verify <domain_id> [options]
@@ -1408,7 +1408,7 @@ TROUBLESHOOTING
1408
1408
  1. Run 'check-dns' to see which records are missing
1409
1409
  2. Verify records are correctly configured with your DNS provider
1410
1410
  3. Wait for DNS propagation (can take up to 48 hours)
1411
- 4. Try verification again`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).domains.verify(t);e.format==="json"?c(o,"json"):o.verified?d("Domain verified successfully!"):(u(`Domain status: ${o.status}`),o.dkim_status&&u(`DKIM status: ${o.dkim_status}`));}catch(a){l(a instanceof Error?a.message:"Failed to verify domain"),process.exit(1);}}),i.command("check-dns <domain_id>").description(`Check DNS records for a domain
1411
+ 4. Try verification again`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.verify(t);e.format==="json"?c(a,"json"):a.verified?d("Domain verified successfully!"):(u(`Domain status: ${a.status}`),a.dkim_status&&u(`DKIM status: ${a.dkim_status}`));}catch(o){l(o instanceof Error?o.message:"Failed to verify domain"),process.exit(1);}}),i.command("check-dns <domain_id>").description(`Check DNS records for a domain
1412
1412
 
1413
1413
  USAGE
1414
1414
  emailr domains check-dns <domain_id> [options]
@@ -1455,7 +1455,7 @@ COMMON ISSUES
1455
1455
 
1456
1456
  Multiple records found:
1457
1457
  - Remove duplicate TXT records
1458
- - Keep only the Emailr-specific record`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).domains.checkDns(t);if(e.format==="json")c(o,"json");else {let n=Object.entries(o).map(([s,p])=>({Record:s,Verified:p.verified,Expected:p.expected||"-",Found:p.found?.join(", ")||"-"}));c(n,"table");}}catch(a){l(a instanceof Error?a.message:"Failed to check DNS"),process.exit(1);}}),i.command("delete <domain_id>").description(`Delete a domain
1458
+ - Keep only the Emailr-specific record`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.checkDns(t);if(e.format==="json")c(a,"json");else {let n=Object.entries(a).map(([s,p])=>({Record:s,Verified:p.verified,Expected:p.expected||"-",Found:p.found?.join(", ")||"-"}));c(n,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to check DNS"),process.exit(1);}}),i.command("delete <domain_id>").description(`Delete a domain
1459
1459
 
1460
1460
  USAGE
1461
1461
  emailr domains delete <domain_id> [options]
@@ -1485,7 +1485,7 @@ WARNING
1485
1485
  This action is permanent and cannot be undone.
1486
1486
  - Emails from this domain will fail to send
1487
1487
  - DNS records can be removed from your DNS provider
1488
- - Re-add the domain if you need to restore functionality`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.delete(t),d(`Domain deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete domain"),process.exit(1);}}),i}function Q(i){return {"Record Type":i.type,Name:i.name,Value:i.value.length>50?i.value.substring(0,47)+"...":i.value,...i.priority!==void 0&&{Priority:i.priority}}}function Ne(){let i=new Command("config").description(`Manage CLI configuration
1488
+ - Re-add the domain if you need to restore functionality`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.delete(t),d(`Domain deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete domain"),process.exit(1);}}),i}function Q(i){return {"Record Type":i.type,Name:i.name,Value:i.value.length>50?i.value.substring(0,47)+"...":i.value,...i.priority!==void 0&&{Priority:i.priority}}}function _e(){let i=new Command("config").description(`Manage CLI configuration
1489
1489
 
1490
1490
  USAGE
1491
1491
  emailr config <subcommand> [options]
@@ -1578,7 +1578,7 @@ EXAMPLES
1578
1578
 
1579
1579
  NOTE
1580
1580
  Environment variables (EMAILR_API_KEY, EMAILR_BASE_URL) take precedence
1581
- over config file values at runtime.`).action(async(t,e)=>{try{let a=["api-key","base-url","format"],r=t.toLowerCase();a.includes(r)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${a.join(", ")}`),process.exit(1));let n={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[r];r==="format"&&!["json","table"].includes(e)&&(l(`Invalid format value: ${e}`),u("Valid formats: json, table"),process.exit(1)),G({[n]:e}),d(`Configuration saved: ${t} = ${r==="api-key"?"***":e}`),u(`Config file: ${k()}`);}catch(a){l(a instanceof Error?a.message:"Failed to save configuration"),process.exit(1);}}),i.command("get <key>").description(`Get a configuration value
1581
+ over config file values at runtime.`).action(async(t,e)=>{try{let o=["api-key","base-url","format"],r=t.toLowerCase();o.includes(r)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${o.join(", ")}`),process.exit(1));let n={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[r];r==="format"&&!["json","table"].includes(e)&&(l(`Invalid format value: ${e}`),u("Valid formats: json, table"),process.exit(1)),G({[n]:e}),d(`Configuration saved: ${t} = ${r==="api-key"?"***":e}`),u(`Config file: ${I()}`);}catch(o){l(o instanceof Error?o.message:"Failed to save configuration"),process.exit(1);}}),i.command("get <key>").description(`Get a configuration value
1582
1582
 
1583
1583
  USAGE
1584
1584
  emailr config get <key>
@@ -1606,7 +1606,7 @@ EXAMPLES
1606
1606
  emailr config get format
1607
1607
 
1608
1608
  SEE ALSO
1609
- emailr config list Show all configuration values`).action(async t=>{try{let e=["api-key","base-url","format"],a=t.toLowerCase();e.includes(a)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${e.join(", ")}`),process.exit(1));let o={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[a],n=ne(o);n?console.log(a==="api-key"?n.substring(0,8)+"..."+n.substring(n.length-4):n):u(`${t} is not set`);}catch(e){l(e instanceof Error?e.message:"Failed to get configuration"),process.exit(1);}}),i.command("list").description(`List all configuration values
1609
+ emailr config list Show all configuration values`).action(async t=>{try{let e=["api-key","base-url","format"],o=t.toLowerCase();e.includes(o)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${e.join(", ")}`),process.exit(1));let a={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[o],n=ne(a);n?console.log(o==="api-key"?n.substring(0,8)+"..."+n.substring(n.length-4):n):u(`${t} is not set`);}catch(e){l(e instanceof Error?e.message:"Failed to get configuration"),process.exit(1);}}),i.command("list").description(`List all configuration values
1610
1610
 
1611
1611
  USAGE
1612
1612
  emailr config list [options]
@@ -1634,7 +1634,7 @@ EXAMPLES
1634
1634
 
1635
1635
  SEE ALSO
1636
1636
  emailr config get Get a single configuration value
1637
- emailr config path Show config file location`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),a={"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")c(a,"json");else {let r=Object.entries(a).map(([o,n])=>({Key:o,Value:n}));c(r,"table");}console.log(""),u(`Config file: ${k()}`);}catch(e){e instanceof Error&&e.message.includes("No API key configured")?(u("No configuration found."),u("Run 'emailr config set api-key <your-api-key>' to get started.")):(l(e instanceof Error?e.message:"Failed to list configuration"),process.exit(1));}}),i.command("path").description(`Show the configuration file path
1637
+ emailr config path Show config file location`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),o={"api-key":e.apiKey?e.apiKey.substring(0,8)+"..."+e.apiKey.substring(e.apiKey.length-4):"(not set)","base-url":e.baseUrl||"(default)",format:e.format||"table"};if(t.format==="json")c(o,"json");else {let r=Object.entries(o).map(([a,n])=>({Key:a,Value:n}));c(r,"table");}console.log(""),u(`Config file: ${I()}`);}catch(e){e instanceof Error&&e.message.includes("No API key configured")?(u("No configuration found."),u("Run 'emailr config set api-key <your-api-key>' to get started.")):(l(e instanceof Error?e.message:"Failed to list configuration"),process.exit(1));}}),i.command("path").description(`Show the configuration file path
1638
1638
 
1639
1639
  USAGE
1640
1640
  emailr config path
@@ -1661,7 +1661,7 @@ EXAMPLES
1661
1661
  open $(emailr config path)
1662
1662
 
1663
1663
  # View config file contents
1664
- cat $(emailr config path)`).action(()=>{console.log(k());}),i.command("init").description(`Initialize configuration interactively
1664
+ cat $(emailr config path)`).action(()=>{console.log(I());}),i.command("init").description(`Initialize configuration interactively
1665
1665
 
1666
1666
  USAGE
1667
1667
  emailr config init [options]
@@ -1694,7 +1694,7 @@ ALTERNATIVE: ENVIRONMENT VARIABLES
1694
1694
 
1695
1695
  SEE ALSO
1696
1696
  emailr config set Set individual configuration values
1697
- 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}),d("Configuration initialized!"),u(`Config file: ${k()}`)):(u("Initialize your Emailr CLI configuration:"),console.log(""),u("Run with --api-key flag:"),console.log(" emailr config init --api-key <your-api-key>"),console.log(""),u("Or set environment variable:"),console.log(" export EMAILR_API_KEY=<your-api-key>"));}catch(e){l(e instanceof Error?e.message:"Failed to initialize configuration"),process.exit(1);}}),i}function _e(){let i=new Command("broadcasts").description(`Manage broadcast campaigns
1697
+ 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}),d("Configuration initialized!"),u(`Config file: ${I()}`)):(u("Initialize your Emailr CLI configuration:"),console.log(""),u("Run with --api-key flag:"),console.log(" emailr config init --api-key <your-api-key>"),console.log(""),u("Or set environment variable:"),console.log(" export EMAILR_API_KEY=<your-api-key>"));}catch(e){l(e instanceof Error?e.message:"Failed to initialize configuration"),process.exit(1);}}),i}function je(){let i=new Command("broadcasts").description(`Manage broadcast campaigns
1698
1698
 
1699
1699
  USAGE
1700
1700
  emailr broadcasts <subcommand> [options]
@@ -1719,6 +1719,7 @@ SUBCOMMANDS
1719
1719
  list List all broadcasts with optional status filter
1720
1720
  get <id> Get broadcast details and delivery statistics
1721
1721
  create Create a new broadcast campaign
1722
+ update <id> Update a draft or scheduled broadcast
1722
1723
  send <id> Send a broadcast immediately
1723
1724
  schedule <id> Schedule a broadcast for future delivery
1724
1725
  cancel <id> Cancel a scheduled broadcast
@@ -1842,7 +1843,7 @@ EXAMPLES
1842
1843
  emailr broadcasts list --format json
1843
1844
 
1844
1845
  # Pipe JSON to jq for processing
1845
- 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=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={status:t.status,limit:parseInt(t.limit)};if(t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let o=await a.broadcasts.list(r);if(t.format==="json")c(o,"json");else {if(o.length===0){console.log("No broadcasts found.");return}let n=o.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()}));c(n,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list broadcasts"),process.exit(1);}}),i.command("get <broadcast_id>").description(`Get broadcast details
1846
+ 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=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={status:t.status,limit:parseInt(t.limit)};if(t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let a=await o.broadcasts.list(r);if(t.format==="json")c(a,"json");else {if(a.length===0){console.log("No broadcasts found.");return}let n=a.map(s=>({ID:s.id,Name:s.name,Subject:s.subject.substring(0,30)+(s.subject.length>30?"...":""),Status:s.status,Tags:s.tags?.join(", ")||"-",Recipients:s.total_recipients||0,Sent:s.sent_count||0,Created:new Date(s.created_at).toLocaleDateString()}));c(n,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list broadcasts"),process.exit(1);}}),i.command("get <broadcast_id>").description(`Get broadcast details
1846
1847
 
1847
1848
  USAGE
1848
1849
  emailr broadcasts get <broadcast_id> [options]
@@ -1877,7 +1878,7 @@ EXAMPLES
1877
1878
  emailr broadcasts get brd_abc123 --format json
1878
1879
 
1879
1880
  # Pipe JSON to jq for processing
1880
- 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 a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.get(t);e.format==="json"?c(o,"json"):c({ID:o.id,Name:o.name,Subject:o.subject,"From Email":o.from_email,Status:o.status,Tags:o.tags?.join(", ")||"-","Total Recipients":o.total_recipients||0,"Sent Count":o.sent_count||0,Delivered:o.delivered_count||0,Opened:o.opened_count||0,Clicked:o.clicked_count||0,Bounced:o.bounced_count||0,"Scheduled At":o.scheduled_at||"N/A","Started At":o.started_at||"N/A","Completed At":o.completed_at||"N/A","Created At":o.created_at},"table");}catch(a){l(a instanceof Error?a.message:"Failed to get broadcast"),process.exit(1);}}),i.command("create").description(`Create a new broadcast
1881
+ 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 o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.get(t);e.format==="json"?c(a,"json"):c({ID:a.id,Name:a.name,Subject:a.subject,"From Email":a.from_email,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(o){l(o instanceof Error?o.message:"Failed to get broadcast"),process.exit(1);}}),i.command("create").description(`Create a new broadcast
1881
1882
 
1882
1883
  USAGE
1883
1884
  emailr broadcasts create --name <name> --subject <subject> --from <email> [options]
@@ -1953,7 +1954,7 @@ EXAMPLES
1953
1954
 
1954
1955
  SEE ALSO
1955
1956
  emailr templates Create and manage email templates
1956
- emailr segments Create and manage contact segments`).requiredOption("--name <name>","Broadcast name").requiredOption("--subject <subject>","Email subject").requiredOption("--from <email>","Sender email address").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=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={name:t.name,subject:t.subject,from_email:t.from,template_id:t.template,segment_id:t.segment,html_content:t.html,text_content:t.text,scheduled_at:t.schedule};t.tags&&(r.tags=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let o=await a.broadcasts.create(r);t.format==="json"?c(o,"json"):(d("Broadcast created successfully!"),c({ID:o.id,Name:o.name,Status:o.status,Tags:o.tags?.join(", ")||"-","Scheduled At":o.scheduled_at||"Not scheduled"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create broadcast"),process.exit(1);}}),i.command("send <broadcast_id>").description(`Send a broadcast immediately
1957
+ emailr segments Create and manage contact segments`).requiredOption("--name <name>","Broadcast name").requiredOption("--subject <subject>","Email subject").requiredOption("--from <email>","Sender email address").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=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={name:t.name,subject:t.subject,from_email:t.from,template_id:t.template,segment_id:t.segment,html_content:t.html,text_content:t.text,scheduled_at:t.schedule};t.tags&&(r.tags=t.tags.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean));let a=await o.broadcasts.create(r);t.format==="json"?c(a,"json"):(d("Broadcast created successfully!"),c({ID:a.id,Name:a.name,Status:a.status,Tags:a.tags?.join(", ")||"-","Scheduled At":a.scheduled_at||"Not scheduled"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create broadcast"),process.exit(1);}}),i.command("update <broadcast_id>").description("Update a draft or scheduled broadcast").option("--name <name>","Broadcast name").option("--subject <subject>","Email subject").option("--from <email>","Sender email address").option("--template <id>","Template ID").option("--segment <id>","Segment ID").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,e)=>{try{let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};e.name&&(a.name=e.name),e.subject&&(a.subject=e.subject),e.from&&(a.from_email=e.from),e.template&&(a.template_id=e.template),e.segment&&(a.segment_id=e.segment),e.html&&(a.html_content=e.html),e.text&&(a.text_content=e.text),e.schedule&&(a.scheduled_at=new Date(e.schedule).toISOString()),e.tags&&(a.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.broadcasts.update(t,a);e.format==="json"?c(n,"json"):(d("Broadcast updated successfully!"),c({ID:n.id,Name:n.name,Status:n.status,Tags:n.tags?.join(", ")||"-"},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update broadcast"),process.exit(1);}}),i.command("send <broadcast_id>").description(`Send a broadcast immediately
1957
1958
 
1958
1959
  USAGE
1959
1960
  emailr broadcasts send <broadcast_id> [options]
@@ -1986,7 +1987,7 @@ EXAMPLES
1986
1987
  NOTE
1987
1988
  Sending is asynchronous. The command returns when sending starts,
1988
1989
  not when all emails are delivered. Use 'emailr broadcasts get' to
1989
- check delivery progress.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.send(t);e.format==="json"?c(o,"json"):(d("Broadcast sent successfully!"),c({Success:o.success,Sent:o.sent,Total:o.total},"table"));}catch(a){l(a instanceof Error?a.message:"Failed to send broadcast"),process.exit(1);}}),i.command("schedule <broadcast_id>").description(`Schedule a broadcast for future delivery
1990
+ check delivery progress.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.send(t);e.format==="json"?c(a,"json"):(d("Broadcast sent successfully!"),c({Success:a.success,Sent:a.sent,Total:a.total},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to send broadcast"),process.exit(1);}}),i.command("schedule <broadcast_id>").description(`Schedule a broadcast for future delivery
1990
1991
 
1991
1992
  USAGE
1992
1993
  emailr broadcasts schedule <broadcast_id> --at <datetime> [options]
@@ -2036,7 +2037,7 @@ EXAMPLES
2036
2037
  emailr broadcasts schedule brd_abc123 --at "2024-12-25T10:00:00Z" --format json
2037
2038
 
2038
2039
  SEE ALSO
2039
- 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 a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.schedule(t,e.at);e.format==="json"?c(o,"json"):(d("Broadcast scheduled successfully!"),c({ID:o.id,Name:o.name,Status:o.status,"Scheduled At":o.scheduled_at},"table"));}catch(a){l(a instanceof Error?a.message:"Failed to schedule broadcast"),process.exit(1);}}),i.command("cancel <broadcast_id>").description(`Cancel a scheduled broadcast
2040
+ 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 o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.schedule(t,e.at);e.format==="json"?c(a,"json"):(d("Broadcast scheduled successfully!"),c({ID:a.id,Name:a.name,Status:a.status,"Scheduled At":a.scheduled_at},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to schedule broadcast"),process.exit(1);}}),i.command("cancel <broadcast_id>").description(`Cancel a scheduled broadcast
2040
2041
 
2041
2042
  USAGE
2042
2043
  emailr broadcasts cancel <broadcast_id> [options]
@@ -2064,7 +2065,52 @@ EXAMPLES
2064
2065
  emailr broadcasts cancel brd_abc123 --format json
2065
2066
 
2066
2067
  NOTE
2067
- Broadcasts that are already 'sending' or 'sent' cannot be cancelled.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.cancel(t);e.format==="json"?c(o,"json"):d("Broadcast cancelled successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to cancel broadcast"),process.exit(1);}}),i.command("delete <broadcast_id>").description(`Delete a broadcast
2068
+ Broadcasts that are already 'sending' or 'sent' cannot be cancelled.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.cancel(t);e.format==="json"?c(a,"json"):d("Broadcast cancelled successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to cancel broadcast"),process.exit(1);}}),i.command("update <broadcast_id>").description(`Update a broadcast
2069
+
2070
+ USAGE
2071
+ emailr broadcasts update <broadcast_id> [options]
2072
+
2073
+ DESCRIPTION
2074
+ Updates a broadcast that has not been sent yet (draft or scheduled status).
2075
+ Only the fields you specify will be updated; all others remain unchanged.
2076
+
2077
+ ARGUMENTS
2078
+ <broadcast_id> The unique broadcast identifier (e.g., brd_abc123)
2079
+
2080
+ OPTIONS
2081
+ --name <name> Update broadcast name
2082
+ --subject <subject> Update email subject line
2083
+ --from <email> Update sender email address
2084
+ --template <id> Update template ID (use "none" to clear)
2085
+ --segment <id> Update segment ID (use "none" to clear)
2086
+ --topic <id> Update topic ID (use "none" to clear)
2087
+ --html <html> Update HTML content
2088
+ --text <text> Update plain text content
2089
+ --schedule <datetime> Update schedule time (ISO 8601, use "none" to clear)
2090
+ --tags <tags> Update tags (comma-separated, replaces existing)
2091
+ --format <format> Output format: json | table (default: table)
2092
+
2093
+ EXAMPLES
2094
+ # Update broadcast name and subject
2095
+ emailr broadcasts update brd_abc123 --name "New Name" --subject "New Subject"
2096
+
2097
+ # Change the template
2098
+ emailr broadcasts update brd_abc123 --template tpl_xyz789
2099
+
2100
+ # Clear the template (use inline content instead)
2101
+ emailr broadcasts update brd_abc123 --template none --html "<p>New content</p>"
2102
+
2103
+ # Reschedule a broadcast
2104
+ emailr broadcasts update brd_abc123 --schedule "2024-12-25T10:00:00Z"
2105
+
2106
+ # Update tags
2107
+ emailr broadcasts update brd_abc123 --tags "newsletter,weekly"
2108
+
2109
+ # Get JSON output
2110
+ emailr broadcasts update brd_abc123 --name "Updated" --format json
2111
+
2112
+ NOTE
2113
+ 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("--template <id>",'Template ID (use "none" to clear)').option("--segment <id>",'Segment ID (use "none" to clear)').option("--topic <id>",'Topic ID (use "none" to clear)').option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>",'Schedule time ISO 8601 (use "none" to clear)').option("--tags <tags>","Comma-separated tags (replaces existing)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};e.name&&(a.name=e.name),e.subject&&(a.subject=e.subject),e.from&&(a.from_email=e.from),e.html&&(a.html_content=e.html),e.text&&(a.text_content=e.text),e.template&&(a.template_id=e.template==="none"?null:e.template),e.segment&&(a.segment_id=e.segment==="none"?null:e.segment),e.topic&&(a.topic_id=e.topic==="none"?null:e.topic),e.schedule&&(a.scheduled_at=e.schedule==="none"?null:e.schedule),e.tags&&(a.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean)),Object.keys(a).length===0&&(l("No update fields specified. Use --help to see available options."),process.exit(1));let n=await r.broadcasts.update(t,a);e.format==="json"?c(n,"json"):(d("Broadcast updated successfully!"),c({ID:n.id,Name:n.name,Subject:n.subject,Status:n.status,Tags:n.tags?.join(", ")||"-","Scheduled At":n.scheduled_at||"Not scheduled"},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update broadcast"),process.exit(1);}}),i.command("delete <broadcast_id>").description(`Delete a broadcast
2068
2114
 
2069
2115
  USAGE
2070
2116
  emailr broadcasts delete <broadcast_id> [options]
@@ -2092,7 +2138,7 @@ EXAMPLES
2092
2138
 
2093
2139
  WARNING
2094
2140
  This action is permanent and cannot be undone.
2095
- All delivery statistics will be lost.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.delete(t);e.format==="json"?c(o,"json"):d("Broadcast deleted successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to delete broadcast"),process.exit(1);}}),i}function ke(){let i=new Command("webhooks").description(`Manage webhooks
2141
+ All delivery statistics will be lost.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.delete(t);e.format==="json"?c(a,"json"):d("Broadcast deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete broadcast"),process.exit(1);}}),i}function Ne(){let i=new Command("webhooks").description(`Manage webhooks
2096
2142
 
2097
2143
  USAGE
2098
2144
  emailr webhooks <subcommand> [options]
@@ -2245,7 +2291,7 @@ EXAMPLES
2245
2291
  emailr webhooks list --format json | jq '.[] | select(.active == true)'
2246
2292
 
2247
2293
  # Count webhooks
2248
- emailr webhooks list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).webhooks.list();if(t.format==="json")c(r,"json");else {if(r.data.length===0){console.log("No webhooks found.");return}let o=r.data.map(n=>({ID:n.id,Name:n.name,URL:n.url.substring(0,40)+(n.url.length>40?"...":""),Events:n.events.join(", "),Active:n.active?"Yes":"No"}));c(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list webhooks"),process.exit(1);}}),i.command("get <webhook_id>").description(`Get webhook details
2294
+ emailr webhooks list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).webhooks.list();if(t.format==="json")c(r,"json");else {if(r.data.length===0){console.log("No webhooks found.");return}let a=r.data.map(n=>({ID:n.id,Name:n.name,URL:n.url.substring(0,40)+(n.url.length>40?"...":""),Events:n.events.join(", "),Active:n.active?"Yes":"No"}));c(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list webhooks"),process.exit(1);}}),i.command("get <webhook_id>").description(`Get webhook details
2249
2295
 
2250
2296
  USAGE
2251
2297
  emailr webhooks get <webhook_id> [options]
@@ -2276,7 +2322,7 @@ EXAMPLES
2276
2322
  emailr webhooks get whk_abc123 --format json
2277
2323
 
2278
2324
  # Extract just the secret
2279
- emailr webhooks get whk_abc123 --format json | jq -r '.secret'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).webhooks.get(t);e.format==="json"?c(o,"json"):c({ID:o.id,Name:o.name,URL:o.url,Events:o.events.join(", "),Active:o.active?"Yes":"No",Secret:o.secret,"Created At":o.created_at},"table");}catch(a){l(a instanceof Error?a.message:"Failed to get webhook"),process.exit(1);}}),i.command("create").description(`Create a new webhook
2325
+ emailr webhooks get whk_abc123 --format json | jq -r '.secret'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.get(t);e.format==="json"?c(a,"json"):c({ID:a.id,Name:a.name,URL:a.url,Events:a.events.join(", "),Active:a.active?"Yes":"No",Secret:a.secret,"Created At":a.created_at},"table");}catch(o){l(o instanceof Error?o.message:"Failed to get webhook"),process.exit(1);}}),i.command("create").description(`Create a new webhook
2280
2326
 
2281
2327
  USAGE
2282
2328
  emailr webhooks create --name <name> --url <url> --events <events> [options]
@@ -2350,7 +2396,7 @@ EXAMPLES
2350
2396
 
2351
2397
  NOTE
2352
2398
  Save the webhook secret returned by this command. You'll need it
2353
- to verify webhook signatures in your endpoint.`).requiredOption("--name <name>","Webhook name").requiredOption("--url <url>","Webhook URL").requiredOption("--events <events>","Events to subscribe to (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r=t.events.split(",").map(n=>n.trim()),o=await a.webhooks.create({name:t.name,url:t.url,events:r});t.format==="json"?c(o,"json"):(d("Webhook created successfully!"),c({ID:o.id,Name:o.name,URL:o.url,Secret:o.secret},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create webhook"),process.exit(1);}}),i.command("update <webhook_id>").description(`Update a webhook
2399
+ to verify webhook signatures in your endpoint.`).requiredOption("--name <name>","Webhook name").requiredOption("--url <url>","Webhook URL").requiredOption("--events <events>","Events to subscribe to (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r=t.events.split(",").map(n=>n.trim()),a=await o.webhooks.create({name:t.name,url:t.url,events:r});t.format==="json"?c(a,"json"):(d("Webhook created successfully!"),c({ID:a.id,Name:a.name,URL:a.url,Secret:a.secret},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create webhook"),process.exit(1);}}),i.command("update <webhook_id>").description(`Update a webhook
2354
2400
 
2355
2401
  USAGE
2356
2402
  emailr webhooks update <webhook_id> [options]
@@ -2395,7 +2441,7 @@ EXAMPLES
2395
2441
  --events "email.delivered,email.bounced"
2396
2442
 
2397
2443
  # Get JSON output
2398
- emailr webhooks update whk_abc123 --name "New Name" --format json`).option("--name <name>","New webhook name").option("--url <url>","New webhook URL").option("--events <events>","New events (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={};e.name&&(o.name=e.name),e.url&&(o.url=e.url),e.events&&(o.events=e.events.split(",").map(s=>s.trim()));let n=await r.webhooks.update(t,o);e.format==="json"?c(n,"json"):(d("Webhook updated successfully!"),c({ID:n.id,Name:n.name,URL:n.url},"table"));}catch(a){l(a instanceof Error?a.message:"Failed to update webhook"),process.exit(1);}}),i.command("enable <webhook_id>").description(`Enable a webhook
2444
+ emailr webhooks update whk_abc123 --name "New Name" --format json`).option("--name <name>","New webhook name").option("--url <url>","New webhook URL").option("--events <events>","New events (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};e.name&&(a.name=e.name),e.url&&(a.url=e.url),e.events&&(a.events=e.events.split(",").map(s=>s.trim()));let n=await r.webhooks.update(t,a);e.format==="json"?c(n,"json"):(d("Webhook updated successfully!"),c({ID:n.id,Name:n.name,URL:n.url},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update webhook"),process.exit(1);}}),i.command("enable <webhook_id>").description(`Enable a webhook
2399
2445
 
2400
2446
  USAGE
2401
2447
  emailr webhooks enable <webhook_id> [options]
@@ -2419,7 +2465,7 @@ EXAMPLES
2419
2465
  emailr webhooks enable whk_abc123
2420
2466
 
2421
2467
  # Get JSON output
2422
- emailr webhooks enable whk_abc123 --format json`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).webhooks.enable(t);e.format==="json"?c(o,"json"):d("Webhook enabled successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to enable webhook"),process.exit(1);}}),i.command("disable <webhook_id>").description(`Disable a webhook
2468
+ emailr webhooks enable whk_abc123 --format json`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.enable(t);e.format==="json"?c(a,"json"):d("Webhook enabled successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to enable webhook"),process.exit(1);}}),i.command("disable <webhook_id>").description(`Disable a webhook
2423
2469
 
2424
2470
  USAGE
2425
2471
  emailr webhooks disable <webhook_id> [options]
@@ -2448,7 +2494,7 @@ EXAMPLES
2448
2494
 
2449
2495
  NOTE
2450
2496
  Events are not queued while webhook is disabled. If you need to
2451
- temporarily stop processing, consider handling this in your endpoint.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).webhooks.disable(t);e.format==="json"?c(o,"json"):d("Webhook disabled successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to disable webhook"),process.exit(1);}}),i.command("delete <webhook_id>").description(`Delete a webhook
2497
+ temporarily stop processing, consider handling this in your endpoint.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.disable(t);e.format==="json"?c(a,"json"):d("Webhook disabled successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to disable webhook"),process.exit(1);}}),i.command("delete <webhook_id>").description(`Delete a webhook
2452
2498
 
2453
2499
  USAGE
2454
2500
  emailr webhooks delete <webhook_id> [options]
@@ -2476,7 +2522,7 @@ EXAMPLES
2476
2522
 
2477
2523
  WARNING
2478
2524
  This action is permanent and cannot be undone.
2479
- Create a new webhook if you need to restore functionality.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).webhooks.delete(t);e.format==="json"?c(o,"json"):d("Webhook deleted successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to delete webhook"),process.exit(1);}}),i}function Ie(){let i=new Command("segments").description(`Manage contact segments
2525
+ Create a new webhook if you need to restore functionality.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.delete(t);e.format==="json"?c(a,"json"):d("Webhook deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete webhook"),process.exit(1);}}),i}function Ie(){let i=new Command("segments").description(`Manage contact segments
2480
2526
 
2481
2527
  USAGE
2482
2528
  emailr segments <subcommand> [options]
@@ -2615,7 +2661,7 @@ EXAMPLES
2615
2661
  emailr segments list --format json
2616
2662
 
2617
2663
  # Pipe JSON to jq for processing
2618
- 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=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={};if(t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let o=await a.segments.list(r);if(t.format==="json")c(o,"json");else {if(o.length===0){console.log("No segments found.");return}let n=o.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()}));c(n,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list segments"),process.exit(1);}}),i.command("get <segment_id>").description(`Get segment details
2664
+ 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=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={};if(t.tags){let n=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean);n.length>0&&(r.tags=n.join(","));}let a=await o.segments.list(r);if(t.format==="json")c(a,"json");else {if(a.length===0){console.log("No segments found.");return}let n=a.map(s=>({ID:s.id,Name:s.name,Description:(s.description||"").substring(0,30)+((s.description?.length||0)>30?"...":""),Tags:s.tags?.join(", ")||"-","Created At":new Date(s.created_at).toLocaleDateString()}));c(n,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list segments"),process.exit(1);}}),i.command("get <segment_id>").description(`Get segment details
2619
2665
 
2620
2666
  USAGE
2621
2667
  emailr segments get <segment_id> [options]
@@ -2642,7 +2688,7 @@ EXAMPLES
2642
2688
  emailr segments get seg_abc123 --format json
2643
2689
 
2644
2690
  # Pipe JSON to jq to view conditions
2645
- emailr segments get seg_abc123 --format json | jq '.conditions'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).segments.get(t);e.format==="json"?c(o,"json"):c({ID:o.id,Name:o.name,Description:o.description||"N/A",Conditions:JSON.stringify(o.conditions),Tags:o.tags?.join(", ")||"-","Created At":o.created_at,"Updated At":o.updated_at},"table");}catch(a){l(a instanceof Error?a.message:"Failed to get segment"),process.exit(1);}}),i.command("create").description(`Create a new segment
2691
+ emailr segments get seg_abc123 --format json | jq '.conditions'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.get(t);e.format==="json"?c(a,"json"):c({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(o){l(o instanceof Error?o.message:"Failed to get segment"),process.exit(1);}}),i.command("create").description(`Create a new segment
2646
2692
 
2647
2693
  USAGE
2648
2694
  emailr segments create --name <segment_name> --conditions <json> [options]
@@ -2691,7 +2737,7 @@ EXAMPLES
2691
2737
  # Get JSON output for scripting
2692
2738
  emailr segments create --name "Test" \\
2693
2739
  --conditions '[{"field": "subscribed", "operator": "equals", "value": true}]' \\
2694
- --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=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r;try{r=JSON.parse(t.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let o={name:t.name,description:t.description,conditions:r};t.tags&&(o.tags=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await a.segments.create(o);t.format==="json"?c(n,"json"):(d("Segment created successfully!"),c({ID:n.id,Name:n.name,Tags:n.tags?.join(", ")||"-"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create segment"),process.exit(1);}}),i.command("update <segment_id>").description(`Update a segment
2740
+ --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=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r;try{r=JSON.parse(t.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let a={name:t.name,description:t.description,conditions:r};t.tags&&(a.tags=t.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await o.segments.create(a);t.format==="json"?c(n,"json"):(d("Segment created successfully!"),c({ID:n.id,Name:n.name,Tags:n.tags?.join(", ")||"-"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create segment"),process.exit(1);}}),i.command("update <segment_id>").description(`Update a segment
2695
2741
 
2696
2742
  USAGE
2697
2743
  emailr segments update <segment_id> [options]
@@ -2736,7 +2782,7 @@ EXAMPLES
2736
2782
  --conditions '[{"field": "metadata.plan", "operator": "equals", "value": "enterprise"}]'
2737
2783
 
2738
2784
  # Get JSON output
2739
- 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 a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={};if(e.name&&(o.name=e.name),e.description&&(o.description=e.description),e.conditions)try{o.conditions=JSON.parse(e.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}e.tags&&(o.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.segments.update(t,o);e.format==="json"?c(n,"json"):(d("Segment updated successfully!"),c({ID:n.id,Name:n.name,Tags:n.tags?.join(", ")||"-"},"table"));}catch(a){l(a instanceof Error?a.message:"Failed to update segment"),process.exit(1);}}),i.command("delete <segment_id>").description(`Delete a segment
2785
+ emailr segments update seg_abc123 --name "Updated" --format json`).option("--name <name>","New segment name").option("--description <description>","New description").option("--conditions <json>","New conditions as JSON").option("--tags <tags>","Comma-separated tags").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),r=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.name&&(a.name=e.name),e.description&&(a.description=e.description),e.conditions)try{a.conditions=JSON.parse(e.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}e.tags&&(a.tags=e.tags.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));let n=await r.segments.update(t,a);e.format==="json"?c(n,"json"):(d("Segment updated successfully!"),c({ID:n.id,Name:n.name,Tags:n.tags?.join(", ")||"-"},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update segment"),process.exit(1);}}),i.command("delete <segment_id>").description(`Delete a segment
2740
2786
 
2741
2787
  USAGE
2742
2788
  emailr segments delete <segment_id>
@@ -2761,7 +2807,7 @@ EXAMPLES
2761
2807
 
2762
2808
  WARNING
2763
2809
  This action is permanent and cannot be undone.
2764
- Any broadcasts targeting this segment will need to be updated.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).segments.delete(t);e.format==="json"?c(o,"json"):d("Segment deleted successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to delete segment"),process.exit(1);}}),i.command("count <segment_id>").description(`Get the number of contacts in a segment
2810
+ Any broadcasts targeting this segment will need to be updated.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.delete(t);e.format==="json"?c(a,"json"):d("Segment deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete segment"),process.exit(1);}}),i.command("count <segment_id>").description(`Get the number of contacts in a segment
2765
2811
 
2766
2812
  USAGE
2767
2813
  emailr segments count <segment_id> [options]
@@ -2792,7 +2838,7 @@ EXAMPLES
2792
2838
 
2793
2839
  TIP
2794
2840
  Use this before sending broadcasts to verify your segment
2795
- targets the expected number of contacts.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).segments.getContactsCount(t);e.format==="json"?c(o,"json"):console.log(`Segment contains ${o.count} contacts.`);}catch(a){l(a instanceof Error?a.message:"Failed to get segment count"),process.exit(1);}}),i}function st(i){try{let t=i.startsWith("http")?i:`http://localhost${i}`,e=new URL(t);return {key:e.searchParams.get("key")??void 0,code:e.searchParams.get("code")??void 0,state:e.searchParams.get("state")??void 0,error:e.searchParams.get("error")??void 0,message:e.searchParams.get("message")??void 0}}catch{return {}}}function lt(){return `<!DOCTYPE html>
2841
+ targets the expected number of contacts.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.getContactsCount(t);e.format==="json"?c(a,"json"):console.log(`Segment contains ${a.count} contacts.`);}catch(o){l(o instanceof Error?o.message:"Failed to get segment count"),process.exit(1);}}),i}function st(i){try{let t=i.startsWith("http")?i:`http://localhost${i}`,e=new URL(t);return {key:e.searchParams.get("key")??void 0,code:e.searchParams.get("code")??void 0,state:e.searchParams.get("state")??void 0,error:e.searchParams.get("error")??void 0,message:e.searchParams.get("message")??void 0}}catch{return {}}}function lt(){return `<!DOCTYPE html>
2796
2842
  <html lang="en">
2797
2843
  <head>
2798
2844
  <meta charset="UTF-8">
@@ -2893,7 +2939,7 @@ TIP
2893
2939
  <p style="margin-top: 1rem;">Please close this window and try again.</p>
2894
2940
  </div>
2895
2941
  </body>
2896
- </html>`}function je(){let i=null,t=0,e=null,a=null,r=null;return {async start(){return new Promise((o,n)=>{i=Je.createServer((s,p)=>{if(s.method!=="GET"||!s.url?.startsWith("/callback")){p.writeHead(404,{"Content-Type":"text/plain"}),p.end("Not Found");return}let b=st(s.url);if(r&&b.state!==r){p.writeHead(400,{"Content-Type":"text/html"}),p.end(ee("Security verification failed. State parameter mismatch.")),e&&e({success:false,error:"State parameter mismatch"});return}if(b.error){let h=b.message||b.error;p.writeHead(200,{"Content-Type":"text/html"}),p.end(ee(h)),e&&e({success:false,error:h});return}let T=b.key||b.code;if(T){p.writeHead(200,{"Content-Type":"text/html"}),p.end(lt()),e&&e({success:true,apiKey:T});return}p.writeHead(400,{"Content-Type":"text/html"}),p.end(ee("Invalid callback: missing required parameters.")),e&&e({success:false,error:"Invalid callback: missing required parameters"});}),i.listen(0,"127.0.0.1",()=>{let s=i.address();s&&typeof s=="object"?(t=s.port,o({port:t,url:`http://127.0.0.1:${t}/callback`})):n(new Error("Failed to get server address"));}),i.on("error",s=>{n(new Error(`Failed to start callback server: ${s.message}`));});})},async waitForCallback(o,n){return r=o,new Promise(s=>{e=s,a=setTimeout(()=>{e&&e({success:false,error:"Login timed out. Please try again."});},n);})},async stop(){if(a&&(clearTimeout(a),a=null),i)return new Promise(o=>{i.close(()=>{i=null,o();});})}}}function Ce(){return ct.randomBytes(32).toString("hex")}function Pe(i){return new Promise(t=>{let e=process.platform,a;switch(e){case "darwin":a=`open "${i}"`;break;case "win32":a=`start "" "${i}"`;break;default:a=`xdg-open "${i}"`;break}exec(a,r=>{t(!r);});})}var $=120,pt=process.env.EMAILR_WEB_URL||"https://app.emailr.dev";function ut(i,t){let e=`http://127.0.0.1:${t}/callback`,a=new URLSearchParams({state:i,callback_url:e});return `${pt}/consent/authorize?${a.toString()}`}function Ae(){return new Command("login").description("Log in to Emailr via browser authentication").option("-t, --timeout <seconds>","Timeout in seconds",String($)).option("--no-browser","Don't automatically open the browser").action(async t=>{await ft({timeout:parseInt(t.timeout,10)||$,noBrowser:t.browser===false});})}async function ft(i){let t=je(),e=(i.timeout||$)*1e3;try{u("Starting authentication server...");let{port:a,url:r}=await t.start(),o=Ce(),n=ut(o,a);console.log(""),u("Authorization URL:"),console.log(` ${n}`),console.log(""),i.noBrowser?u("Please open the URL above in your browser to continue."):await Pe(n)?u("Browser opened. Please complete authentication in your browser."):(I("Could not open browser automatically."),u("Please open the URL above in your browser to continue.")),console.log(""),u(`Waiting for authentication (timeout: ${i.timeout||$}s)...`);let s=await t.waitForCallback(o,e);s.success&&s.apiKey?(G({apiKey:s.apiKey}),console.log(""),d("Login successful!"),u(`API key saved to: ${k()}`),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(a){console.log(""),l(a instanceof Error?a.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 ae=N.join(B.homedir(),".config","opencode","skills","emailr-cli"),wt=`---
2942
+ </html>`}function ke(){let i=null,t=0,e=null,o=null,r=null;return {async start(){return new Promise((a,n)=>{i=Je.createServer((s,p)=>{if(s.method!=="GET"||!s.url?.startsWith("/callback")){p.writeHead(404,{"Content-Type":"text/plain"}),p.end("Not Found");return}let b=st(s.url);if(r&&b.state!==r){p.writeHead(400,{"Content-Type":"text/html"}),p.end(ee("Security verification failed. State parameter mismatch.")),e&&e({success:false,error:"State parameter mismatch"});return}if(b.error){let h=b.message||b.error;p.writeHead(200,{"Content-Type":"text/html"}),p.end(ee(h)),e&&e({success:false,error:h});return}let T=b.key||b.code;if(T){p.writeHead(200,{"Content-Type":"text/html"}),p.end(lt()),e&&e({success:true,apiKey:T});return}p.writeHead(400,{"Content-Type":"text/html"}),p.end(ee("Invalid callback: missing required parameters.")),e&&e({success:false,error:"Invalid callback: missing required parameters"});}),i.listen(0,"127.0.0.1",()=>{let s=i.address();s&&typeof s=="object"?(t=s.port,a({port:t,url:`http://127.0.0.1:${t}/callback`})):n(new Error("Failed to get server address"));}),i.on("error",s=>{n(new Error(`Failed to start callback server: ${s.message}`));});})},async waitForCallback(a,n){return r=a,new Promise(s=>{e=s,o=setTimeout(()=>{e&&e({success:false,error:"Login timed out. Please try again."});},n);})},async stop(){if(o&&(clearTimeout(o),o=null),i)return new Promise(a=>{i.close(()=>{i=null,a();});})}}}function Ce(){return ct.randomBytes(32).toString("hex")}function Ue(i){return new Promise(t=>{let e=process.platform,o;switch(e){case "darwin":o=`open "${i}"`;break;case "win32":o=`start "" "${i}"`;break;default:o=`xdg-open "${i}"`;break}exec(o,r=>{t(!r);});})}var $=120,pt=process.env.EMAILR_WEB_URL||"https://app.emailr.dev";function ut(i,t){let e=`http://127.0.0.1:${t}/callback`,o=new URLSearchParams({state:i,callback_url:e});return `${pt}/consent/authorize?${o.toString()}`}function Pe(){return new Command("login").description("Log in to Emailr via browser authentication").option("-t, --timeout <seconds>","Timeout in seconds",String($)).option("--no-browser","Don't automatically open the browser").action(async t=>{await ft({timeout:parseInt(t.timeout,10)||$,noBrowser:t.browser===false});})}async function ft(i){let t=ke(),e=(i.timeout||$)*1e3;try{u("Starting authentication server...");let{port:o,url:r}=await t.start(),a=Ce(),n=ut(a,o);console.log(""),u("Authorization URL:"),console.log(` ${n}`),console.log(""),i.noBrowser?u("Please open the URL above in your browser to continue."):await Ue(n)?u("Browser opened. Please complete authentication in your browser."):(k("Could not open browser automatically."),u("Please open the URL above in your browser to continue.")),console.log(""),u(`Waiting for authentication (timeout: ${i.timeout||$}s)...`);let s=await t.waitForCallback(a,e);s.success&&s.apiKey?(G({apiKey:s.apiKey}),console.log(""),d("Login successful!"),u(`API key saved to: ${I()}`),u("You can now use the Emailr CLI.")):(console.log(""),l(s.error||"Authentication failed."),u("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1));}catch(o){console.log(""),l(o instanceof Error?o.message:"An unexpected error occurred."),u("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1);}finally{await t.stop();}}var ae=_.join(B.homedir(),".config","opencode","skills","emailr-cli"),wt=`---
2897
2943
  name: emailr-cli
2898
2944
  description: Operate the Emailr CLI to send emails, manage contacts, templates, domains, broadcasts, webhooks, and segments. Includes LIVE PREVIEW editing for templates with hot-reload.
2899
2945
  ---
@@ -2933,7 +2979,7 @@ IMPORTANT: Always use \`--background\` flag so the command returns immediately a
2933
2979
  - Create: \`emailr templates create --name "Name" --subject "Subject" --html-file ./template.html\`
2934
2980
  - Update: \`emailr templates update <id> --html-file ./template.html\`
2935
2981
  - Delete: \`emailr templates delete <id>\`
2936
- `;function yt(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function St(){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 vt(){v.existsSync(ae)||v.mkdirSync(ae,{recursive:true});let i=N.join(ae,"SKILL.md");v.writeFileSync(i,wt,"utf-8");}function xe(){return new Command("agent").description(`Launch an AI agent with Emailr CLI expertise
2982
+ `;function yt(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function St(){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 vt(){v.existsSync(ae)||v.mkdirSync(ae,{recursive:true});let i=_.join(ae,"SKILL.md");v.writeFileSync(i,wt,"utf-8");}function xe(){return new Command("agent").description(`Launch an AI agent with Emailr CLI expertise
2937
2983
 
2938
2984
  This opens OpenCode (opencode.ai), an AI coding agent that knows how to use all Emailr CLI commands.
2939
2985
  The agent can help you:
@@ -2942,9 +2988,9 @@ The agent can help you:
2942
2988
  - Configure domains and webhooks
2943
2989
 
2944
2990
  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=>{yt()||(t.install?St()||(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(`
2945
- 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{vt(),d("Emailr CLI skill loaded");}catch(r){I(`Could not install skill: ${r instanceof Error?r.message:String(r)}`);}let e=[];t.model&&e.push("--model",t.model),console.log(`
2991
+ 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{vt(),d("Emailr CLI skill loaded");}catch(r){k(`Could not install skill: ${r instanceof Error?r.message:String(r)}`);}let e=[];t.model&&e.push("--model",t.model),console.log(`
2946
2992
  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.
2947
- `);let a=spawn("opencode",e,{stdio:"inherit",env:process.env});a.on("error",r=>{l(`Failed to start agent: ${r.message}`),process.exit(1);}),a.on("exit",r=>{process.exit(r??0);});})}var g=new Command;g.name("emailr").description(`Emailr CLI - Send emails and manage your email infrastructure
2993
+ `);let o=spawn("opencode",e,{stdio:"inherit",env:process.env});o.on("error",r=>{l(`Failed to start agent: ${r.message}`),process.exit(1);}),o.on("exit",r=>{process.exit(r??0);});})}var g=new Command;g.name("emailr").description(`Emailr CLI - Send emails and manage your email infrastructure
2948
2994
 
2949
2995
  USAGE
2950
2996
  emailr <command> [subcommand] [options]
@@ -3030,4 +3076,4 @@ AGENTIC WORKFLOW
3030
3076
 
3031
3077
  MORE INFORMATION
3032
3078
  Run 'emailr <command> --help' for detailed help on any command.
3033
- Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.5.4");g.addCommand(ce());g.addCommand(me());g.addCommand(Oe());g.addCommand(Ee());g.addCommand(_e());g.addCommand(ke());g.addCommand(Ie());g.addCommand(Ne());g.addCommand(Ae());g.addCommand(xe());g.parse();
3079
+ Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.5.4");g.addCommand(ce());g.addCommand(me());g.addCommand(Oe());g.addCommand(Ee());g.addCommand(je());g.addCommand(Ne());g.addCommand(Ie());g.addCommand(_e());g.addCommand(Pe());g.addCommand(xe());g.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emailr-cli",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Command-line interface for the Emailr email API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "chalk": "^5.3.0",
24
24
  "cli-table3": "^0.6.3",
25
25
  "commander": "^12.0.0",
26
- "emailr": "^1.3.0",
26
+ "emailr": "^1.3.1",
27
27
  "open": "^11.0.0"
28
28
  },
29
29
  "devDependencies": {