emailr-cli 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +30 -30
- 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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
12
12
|
|
|
13
13
|
USAGE
|
|
14
14
|
emailr send --to <email_address> [options]
|
|
@@ -284,7 +284,7 @@ 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("--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)};t.subscribed&&(r.subscribed=!0),t.unsubscribed&&(r.subscribed=!1);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,Created:s.created_at}));c(n,"table"),console.log(`
|
|
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
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
|
|
289
289
|
|
|
290
290
|
USAGE
|
|
@@ -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("--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);}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(),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
|
|
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("--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);}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 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
|
|
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(
|
|
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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
|
|
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=`
|
|
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)=>{
|
|
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
|
|
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");}),
|
|
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
|
|
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("--format <format>","Output format: json | table","table").action(async t=>{try{let e=m(),
|
|
670
|
-
Total: ${
|
|
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
|
|
671
671
|
|
|
672
672
|
USAGE
|
|
673
673
|
emailr templates get <template_id> [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("--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);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(", ")||"-"};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(),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(`
|
|
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("--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);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(", ")||"-",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 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
|
|
877
877
|
|
|
878
878
|
USAGE
|
|
879
879
|
emailr templates delete <template_id>
|
|
@@ -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: ${
|
|
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
|
|
1582
1582
|
|
|
1583
1583
|
USAGE
|
|
1584
1584
|
emailr config get <key>
|
|
@@ -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: ${
|
|
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
|
|
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(
|
|
1664
|
+
cat $(emailr config path)`).action(()=>{console.log(k());}),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: ${
|
|
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
|
|
1698
1698
|
|
|
1699
1699
|
USAGE
|
|
1700
1700
|
emailr broadcasts <subcommand> [options]
|
|
@@ -1842,7 +1842,7 @@ EXAMPLES
|
|
|
1842
1842
|
emailr broadcasts list --format json
|
|
1843
1843
|
|
|
1844
1844
|
# 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("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),
|
|
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
1846
|
|
|
1847
1847
|
USAGE
|
|
1848
1848
|
emailr broadcasts get <broadcast_id> [options]
|
|
@@ -1877,7 +1877,7 @@ EXAMPLES
|
|
|
1877
1877
|
emailr broadcasts get brd_abc123 --format json
|
|
1878
1878
|
|
|
1879
1879
|
# 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,"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
|
|
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
1881
|
|
|
1882
1882
|
USAGE
|
|
1883
1883
|
emailr broadcasts create --name <name> --subject <subject> --from <email> [options]
|
|
@@ -1953,7 +1953,7 @@ EXAMPLES
|
|
|
1953
1953
|
|
|
1954
1954
|
SEE ALSO
|
|
1955
1955
|
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("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),
|
|
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
1957
|
|
|
1958
1958
|
USAGE
|
|
1959
1959
|
emailr broadcasts send <broadcast_id> [options]
|
|
@@ -2092,7 +2092,7 @@ EXAMPLES
|
|
|
2092
2092
|
|
|
2093
2093
|
WARNING
|
|
2094
2094
|
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
|
|
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
|
|
2096
2096
|
|
|
2097
2097
|
USAGE
|
|
2098
2098
|
emailr webhooks <subcommand> [options]
|
|
@@ -2476,7 +2476,7 @@ EXAMPLES
|
|
|
2476
2476
|
|
|
2477
2477
|
WARNING
|
|
2478
2478
|
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
|
|
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
|
|
2480
2480
|
|
|
2481
2481
|
USAGE
|
|
2482
2482
|
emailr segments <subcommand> [options]
|
|
@@ -2615,7 +2615,7 @@ EXAMPLES
|
|
|
2615
2615
|
emailr segments list --format json
|
|
2616
2616
|
|
|
2617
2617
|
# Pipe JSON to jq for processing
|
|
2618
|
-
emailr segments list --format json | jq '.[].name'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),
|
|
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
|
|
2619
2619
|
|
|
2620
2620
|
USAGE
|
|
2621
2621
|
emailr segments get <segment_id> [options]
|
|
@@ -2642,7 +2642,7 @@ EXAMPLES
|
|
|
2642
2642
|
emailr segments get seg_abc123 --format json
|
|
2643
2643
|
|
|
2644
2644
|
# 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),"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
|
|
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
|
|
2646
2646
|
|
|
2647
2647
|
USAGE
|
|
2648
2648
|
emailr segments create --name <segment_name> --conditions <json> [options]
|
|
@@ -2691,7 +2691,7 @@ EXAMPLES
|
|
|
2691
2691
|
# Get JSON output for scripting
|
|
2692
2692
|
emailr segments create --name "Test" \\
|
|
2693
2693
|
--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("--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=
|
|
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
|
|
2695
2695
|
|
|
2696
2696
|
USAGE
|
|
2697
2697
|
emailr segments update <segment_id> [options]
|
|
@@ -2736,7 +2736,7 @@ EXAMPLES
|
|
|
2736
2736
|
--conditions '[{"field": "metadata.plan", "operator": "equals", "value": "enterprise"}]'
|
|
2737
2737
|
|
|
2738
2738
|
# 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("--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);}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},"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
|
|
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
|
|
2740
2740
|
|
|
2741
2741
|
USAGE
|
|
2742
2742
|
emailr segments delete <segment_id>
|
|
@@ -2893,7 +2893,7 @@ TIP
|
|
|
2893
2893
|
<p style="margin-top: 1rem;">Please close this window and try again.</p>
|
|
2894
2894
|
</div>
|
|
2895
2895
|
</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
|
|
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=`---
|
|
2897
2897
|
name: emailr-cli
|
|
2898
2898
|
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
2899
|
---
|
|
@@ -2942,7 +2942,7 @@ The agent can help you:
|
|
|
2942
2942
|
- Configure domains and webhooks
|
|
2943
2943
|
|
|
2944
2944
|
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){
|
|
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(`
|
|
2946
2946
|
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
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
|
|
2948
2948
|
|
|
@@ -3030,4 +3030,4 @@ AGENTIC WORKFLOW
|
|
|
3030
3030
|
|
|
3031
3031
|
MORE INFORMATION
|
|
3032
3032
|
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(
|
|
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();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emailr-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Command-line interface for the Emailr email API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"chalk": "^5.3.0",
|
|
24
24
|
"cli-table3": "^0.6.3",
|
|
25
25
|
"commander": "^12.0.0",
|
|
26
|
-
"emailr": "^1.
|
|
26
|
+
"emailr": "^1.3.0",
|
|
27
27
|
"open": "^11.0.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|