@mailmodo/cli 0.0.55-beta.pr57.93 → 0.0.55
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/commands/billing/index.d.ts +11 -1
- package/dist/commands/billing/index.js +184 -28
- package/dist/commands/contacts/index.d.ts +19 -1
- package/dist/commands/contacts/index.js +114 -21
- package/dist/commands/deploy/index.js +4 -4
- package/dist/commands/deployments/index.d.ts +4 -1
- package/dist/commands/deployments/index.js +52 -11
- package/dist/commands/domain/index.d.ts +14 -1
- package/dist/commands/domain/index.js +100 -19
- package/dist/commands/edit/index.d.ts +20 -2
- package/dist/commands/edit/index.js +258 -30
- package/dist/commands/emails/index.d.ts +2 -1
- package/dist/commands/emails/index.js +91 -26
- package/dist/commands/init/index.d.ts +3 -1
- package/dist/commands/init/index.js +199 -41
- package/dist/commands/login/index.d.ts +0 -2
- package/dist/commands/login/index.js +76 -32
- package/dist/commands/logs/index.d.ts +8 -1
- package/dist/commands/logs/index.js +55 -12
- package/dist/commands/preview/index.d.ts +19 -1
- package/dist/commands/preview/index.js +212 -30
- package/dist/commands/sdk/index.d.ts +3 -1
- package/dist/commands/sdk/index.js +46 -14
- package/dist/commands/settings/index.d.ts +22 -1
- package/dist/commands/settings/index.js +246 -34
- package/dist/commands/status/index.d.ts +0 -1
- package/dist/commands/status/index.js +39 -13
- package/dist/lib/{commands/deploy → deploy}/domain-setup.d.ts +1 -1
- package/dist/lib/{commands/deploy → deploy}/domain-setup.js +2 -2
- package/dist/lib/{commands/deploy → deploy}/output.d.ts +1 -1
- package/dist/lib/{commands/deploy → deploy}/output.js +2 -2
- package/dist/lib/{commands/deploy → deploy}/payload.d.ts +1 -1
- package/dist/lib/{commands/deploy → deploy}/payload.js +2 -2
- package/dist/lib/{commands/deploy → deploy}/sequence-status.js +2 -2
- package/dist/lib/{commands/deploy → deploy}/types.d.ts +4 -4
- package/dist/lib/templates/missing-templates.d.ts +1 -1
- package/dist/lib/templates/missing-templates.js +1 -1
- package/oclif.manifest.json +54 -54
- package/package.json +1 -1
- package/dist/lib/commands/billing/checkout-status.d.ts +0 -3
- package/dist/lib/commands/billing/checkout-status.js +0 -63
- package/dist/lib/commands/billing/format.d.ts +0 -7
- package/dist/lib/commands/billing/format.js +0 -63
- package/dist/lib/commands/billing/purchase-cap.d.ts +0 -7
- package/dist/lib/commands/billing/purchase-cap.js +0 -57
- package/dist/lib/commands/billing/types.d.ts +0 -72
- package/dist/lib/commands/contacts/actions.d.ts +0 -3
- package/dist/lib/commands/contacts/actions.js +0 -49
- package/dist/lib/commands/contacts/export-delete.d.ts +0 -9
- package/dist/lib/commands/contacts/export-delete.js +0 -51
- package/dist/lib/commands/contacts/types.d.ts +0 -35
- package/dist/lib/commands/contacts/types.js +0 -1
- package/dist/lib/commands/deploy/types.js +0 -1
- package/dist/lib/commands/deployments/output.d.ts +0 -2
- package/dist/lib/commands/deployments/output.js +0 -68
- package/dist/lib/commands/deployments/types.d.ts +0 -24
- package/dist/lib/commands/deployments/types.js +0 -1
- package/dist/lib/commands/domain/setup.d.ts +0 -8
- package/dist/lib/commands/domain/setup.js +0 -53
- package/dist/lib/commands/domain/types.d.ts +0 -56
- package/dist/lib/commands/domain/types.js +0 -1
- package/dist/lib/commands/domain/verify.d.ts +0 -5
- package/dist/lib/commands/domain/verify.js +0 -50
- package/dist/lib/commands/edit/diff.d.ts +0 -7
- package/dist/lib/commands/edit/diff.js +0 -65
- package/dist/lib/commands/edit/display.d.ts +0 -5
- package/dist/lib/commands/edit/display.js +0 -53
- package/dist/lib/commands/edit/flow.d.ts +0 -8
- package/dist/lib/commands/edit/flow.js +0 -70
- package/dist/lib/commands/edit/persist.d.ts +0 -5
- package/dist/lib/commands/edit/persist.js +0 -65
- package/dist/lib/commands/edit/types.d.ts +0 -37
- package/dist/lib/commands/edit/types.js +0 -1
- package/dist/lib/commands/emails/editor.d.ts +0 -2
- package/dist/lib/commands/emails/editor.js +0 -43
- package/dist/lib/commands/emails/output.d.ts +0 -4
- package/dist/lib/commands/emails/output.js +0 -36
- package/dist/lib/commands/emails/types.d.ts +0 -3
- package/dist/lib/commands/emails/types.js +0 -1
- package/dist/lib/commands/init/analysis.d.ts +0 -3
- package/dist/lib/commands/init/analysis.js +0 -69
- package/dist/lib/commands/init/output.d.ts +0 -12
- package/dist/lib/commands/init/output.js +0 -39
- package/dist/lib/commands/init/payload.d.ts +0 -8
- package/dist/lib/commands/init/payload.js +0 -78
- package/dist/lib/commands/init/types.d.ts +0 -57
- package/dist/lib/commands/init/types.js +0 -1
- package/dist/lib/commands/login/output.d.ts +0 -8
- package/dist/lib/commands/login/output.js +0 -53
- package/dist/lib/commands/login/types.d.ts +0 -19
- package/dist/lib/commands/login/types.js +0 -1
- package/dist/lib/commands/logs/output.d.ts +0 -2
- package/dist/lib/commands/logs/output.js +0 -52
- package/dist/lib/commands/logs/types.d.ts +0 -23
- package/dist/lib/commands/logs/types.js +0 -1
- package/dist/lib/commands/preview/actions.d.ts +0 -11
- package/dist/lib/commands/preview/actions.js +0 -43
- package/dist/lib/commands/preview/render.d.ts +0 -3
- package/dist/lib/commands/preview/render.js +0 -30
- package/dist/lib/commands/preview/server.d.ts +0 -8
- package/dist/lib/commands/preview/server.js +0 -63
- package/dist/lib/commands/preview/types.d.ts +0 -19
- package/dist/lib/commands/preview/types.js +0 -1
- package/dist/lib/commands/preview/wrapper-html.d.ts +0 -2
- package/dist/lib/commands/preview/wrapper-html.js +0 -35
- package/dist/lib/commands/sdk/output.d.ts +0 -2
- package/dist/lib/commands/sdk/output.js +0 -42
- package/dist/lib/commands/sdk/types.d.ts +0 -21
- package/dist/lib/commands/sdk/types.js +0 -1
- package/dist/lib/commands/settings/actions.d.ts +0 -10
- package/dist/lib/commands/settings/actions.js +0 -56
- package/dist/lib/commands/settings/display.d.ts +0 -15
- package/dist/lib/commands/settings/display.js +0 -69
- package/dist/lib/commands/settings/logo-domain.d.ts +0 -3
- package/dist/lib/commands/settings/logo-domain.js +0 -47
- package/dist/lib/commands/settings/prompt.d.ts +0 -2
- package/dist/lib/commands/settings/prompt.js +0 -82
- package/dist/lib/commands/settings/types.d.ts +0 -65
- package/dist/lib/commands/settings/types.js +0 -1
- package/dist/lib/commands/status/output.d.ts +0 -2
- package/dist/lib/commands/status/output.js +0 -49
- package/dist/lib/commands/status/types.d.ts +0 -28
- package/dist/lib/commands/status/types.js +0 -1
- /package/dist/lib/{commands/deploy → deploy}/sequence-status.d.ts +0 -0
- /package/dist/lib/{commands/billing → deploy}/types.js +0 -0
package/oclif.manifest.json
CHANGED
|
@@ -573,19 +573,14 @@
|
|
|
573
573
|
"index.js"
|
|
574
574
|
]
|
|
575
575
|
},
|
|
576
|
-
"
|
|
576
|
+
"settings": {
|
|
577
577
|
"aliases": [],
|
|
578
|
-
"args": {
|
|
579
|
-
|
|
580
|
-
"description": "Email template ID to preview",
|
|
581
|
-
"name": "id"
|
|
582
|
-
}
|
|
583
|
-
},
|
|
584
|
-
"description": "Preview an email in browser, as text, or send a test",
|
|
578
|
+
"args": {},
|
|
579
|
+
"description": "View and update project settings",
|
|
585
580
|
"examples": [
|
|
586
|
-
"<%= config.bin %>
|
|
587
|
-
"<%= config.bin %>
|
|
588
|
-
"<%= config.bin %>
|
|
581
|
+
"<%= config.bin %> settings",
|
|
582
|
+
"<%= config.bin %> settings --set brand_color=#0F3460",
|
|
583
|
+
"<%= config.bin %> settings --json"
|
|
589
584
|
],
|
|
590
585
|
"flags": {
|
|
591
586
|
"json": {
|
|
@@ -601,23 +596,17 @@
|
|
|
601
596
|
"allowNo": false,
|
|
602
597
|
"type": "boolean"
|
|
603
598
|
},
|
|
604
|
-
"
|
|
605
|
-
"description": "
|
|
606
|
-
"name": "
|
|
599
|
+
"set": {
|
|
600
|
+
"description": "Set a setting (format: key=value)",
|
|
601
|
+
"name": "set",
|
|
607
602
|
"hasDynamicHelp": false,
|
|
608
603
|
"multiple": false,
|
|
609
604
|
"type": "option"
|
|
610
|
-
},
|
|
611
|
-
"text": {
|
|
612
|
-
"description": "Output plain text version (for AI agents)",
|
|
613
|
-
"name": "text",
|
|
614
|
-
"allowNo": false,
|
|
615
|
-
"type": "boolean"
|
|
616
605
|
}
|
|
617
606
|
},
|
|
618
607
|
"hasDynamicHelp": false,
|
|
619
608
|
"hiddenAliases": [],
|
|
620
|
-
"id": "
|
|
609
|
+
"id": "settings",
|
|
621
610
|
"pluginAlias": "@mailmodo/cli",
|
|
622
611
|
"pluginName": "@mailmodo/cli",
|
|
623
612
|
"pluginType": "core",
|
|
@@ -627,18 +616,17 @@
|
|
|
627
616
|
"relativePath": [
|
|
628
617
|
"dist",
|
|
629
618
|
"commands",
|
|
630
|
-
"
|
|
619
|
+
"settings",
|
|
631
620
|
"index.js"
|
|
632
621
|
]
|
|
633
622
|
},
|
|
634
|
-
"
|
|
623
|
+
"status": {
|
|
635
624
|
"aliases": [],
|
|
636
625
|
"args": {},
|
|
637
|
-
"description": "
|
|
626
|
+
"description": "View email performance metrics and quota usage",
|
|
638
627
|
"examples": [
|
|
639
|
-
"<%= config.bin %>
|
|
640
|
-
"<%= config.bin %>
|
|
641
|
-
"<%= config.bin %> sdk --json"
|
|
628
|
+
"<%= config.bin %> status",
|
|
629
|
+
"<%= config.bin %> status --json"
|
|
642
630
|
],
|
|
643
631
|
"flags": {
|
|
644
632
|
"json": {
|
|
@@ -653,18 +641,11 @@
|
|
|
653
641
|
"name": "yes",
|
|
654
642
|
"allowNo": false,
|
|
655
643
|
"type": "boolean"
|
|
656
|
-
},
|
|
657
|
-
"sequence-id": {
|
|
658
|
-
"description": "Limit output to a single active sequence by ID (default: all active sequences)",
|
|
659
|
-
"name": "sequence-id",
|
|
660
|
-
"hasDynamicHelp": false,
|
|
661
|
-
"multiple": false,
|
|
662
|
-
"type": "option"
|
|
663
644
|
}
|
|
664
645
|
},
|
|
665
646
|
"hasDynamicHelp": false,
|
|
666
647
|
"hiddenAliases": [],
|
|
667
|
-
"id": "
|
|
648
|
+
"id": "status",
|
|
668
649
|
"pluginAlias": "@mailmodo/cli",
|
|
669
650
|
"pluginName": "@mailmodo/cli",
|
|
670
651
|
"pluginType": "core",
|
|
@@ -674,18 +655,23 @@
|
|
|
674
655
|
"relativePath": [
|
|
675
656
|
"dist",
|
|
676
657
|
"commands",
|
|
677
|
-
"
|
|
658
|
+
"status",
|
|
678
659
|
"index.js"
|
|
679
660
|
]
|
|
680
661
|
},
|
|
681
|
-
"
|
|
662
|
+
"preview": {
|
|
682
663
|
"aliases": [],
|
|
683
|
-
"args": {
|
|
684
|
-
|
|
664
|
+
"args": {
|
|
665
|
+
"id": {
|
|
666
|
+
"description": "Email template ID to preview",
|
|
667
|
+
"name": "id"
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
"description": "Preview an email in browser, as text, or send a test",
|
|
685
671
|
"examples": [
|
|
686
|
-
"<%= config.bin %>
|
|
687
|
-
"<%= config.bin %>
|
|
688
|
-
"<%= config.bin %>
|
|
672
|
+
"<%= config.bin %> preview welcome",
|
|
673
|
+
"<%= config.bin %> preview welcome --text",
|
|
674
|
+
"<%= config.bin %> preview welcome --send me@example.com"
|
|
689
675
|
],
|
|
690
676
|
"flags": {
|
|
691
677
|
"json": {
|
|
@@ -701,17 +687,23 @@
|
|
|
701
687
|
"allowNo": false,
|
|
702
688
|
"type": "boolean"
|
|
703
689
|
},
|
|
704
|
-
"
|
|
705
|
-
"description": "
|
|
706
|
-
"name": "
|
|
690
|
+
"send": {
|
|
691
|
+
"description": "Send test email to this address",
|
|
692
|
+
"name": "send",
|
|
707
693
|
"hasDynamicHelp": false,
|
|
708
694
|
"multiple": false,
|
|
709
695
|
"type": "option"
|
|
696
|
+
},
|
|
697
|
+
"text": {
|
|
698
|
+
"description": "Output plain text version (for AI agents)",
|
|
699
|
+
"name": "text",
|
|
700
|
+
"allowNo": false,
|
|
701
|
+
"type": "boolean"
|
|
710
702
|
}
|
|
711
703
|
},
|
|
712
704
|
"hasDynamicHelp": false,
|
|
713
705
|
"hiddenAliases": [],
|
|
714
|
-
"id": "
|
|
706
|
+
"id": "preview",
|
|
715
707
|
"pluginAlias": "@mailmodo/cli",
|
|
716
708
|
"pluginName": "@mailmodo/cli",
|
|
717
709
|
"pluginType": "core",
|
|
@@ -721,17 +713,18 @@
|
|
|
721
713
|
"relativePath": [
|
|
722
714
|
"dist",
|
|
723
715
|
"commands",
|
|
724
|
-
"
|
|
716
|
+
"preview",
|
|
725
717
|
"index.js"
|
|
726
718
|
]
|
|
727
719
|
},
|
|
728
|
-
"
|
|
720
|
+
"sdk": {
|
|
729
721
|
"aliases": [],
|
|
730
722
|
"args": {},
|
|
731
|
-
"description": "
|
|
723
|
+
"description": "Show the SDK track() / identify() reference for deployed sequences",
|
|
732
724
|
"examples": [
|
|
733
|
-
"<%= config.bin %>
|
|
734
|
-
"<%= config.bin %>
|
|
725
|
+
"<%= config.bin %> sdk",
|
|
726
|
+
"<%= config.bin %> sdk --sequence-id a1b2c3d4",
|
|
727
|
+
"<%= config.bin %> sdk --json"
|
|
735
728
|
],
|
|
736
729
|
"flags": {
|
|
737
730
|
"json": {
|
|
@@ -746,11 +739,18 @@
|
|
|
746
739
|
"name": "yes",
|
|
747
740
|
"allowNo": false,
|
|
748
741
|
"type": "boolean"
|
|
742
|
+
},
|
|
743
|
+
"sequence-id": {
|
|
744
|
+
"description": "Limit output to a single active sequence by ID (default: all active sequences)",
|
|
745
|
+
"name": "sequence-id",
|
|
746
|
+
"hasDynamicHelp": false,
|
|
747
|
+
"multiple": false,
|
|
748
|
+
"type": "option"
|
|
749
749
|
}
|
|
750
750
|
},
|
|
751
751
|
"hasDynamicHelp": false,
|
|
752
752
|
"hiddenAliases": [],
|
|
753
|
-
"id": "
|
|
753
|
+
"id": "sdk",
|
|
754
754
|
"pluginAlias": "@mailmodo/cli",
|
|
755
755
|
"pluginName": "@mailmodo/cli",
|
|
756
756
|
"pluginType": "core",
|
|
@@ -760,10 +760,10 @@
|
|
|
760
760
|
"relativePath": [
|
|
761
761
|
"dist",
|
|
762
762
|
"commands",
|
|
763
|
-
"
|
|
763
|
+
"sdk",
|
|
764
764
|
"index.js"
|
|
765
765
|
]
|
|
766
766
|
}
|
|
767
767
|
},
|
|
768
|
-
"version": "0.0.55
|
|
768
|
+
"version": "0.0.55"
|
|
769
769
|
}
|
package/package.json
CHANGED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import open from 'open';
|
|
3
|
-
import { FREE_TIER } from '../../base-command.js';
|
|
4
|
-
import { API_ENDPOINTS } from '../../constants.js';
|
|
5
|
-
import { INFO } from '../../messages.js';
|
|
6
|
-
import { formatAutoCharge, formatCap, formatCurrency, formatPaymentMethod, formatUsageBlock, } from './format.js';
|
|
7
|
-
export async function startCheckout(ctx, jsonOutput) {
|
|
8
|
-
const response = await ctx.spinner(' Creating checkout session...', jsonOutput, () => ctx.post(API_ENDPOINTS.BILLING_CHECKOUT));
|
|
9
|
-
if (!response.ok) {
|
|
10
|
-
ctx.onApiError(response);
|
|
11
|
-
}
|
|
12
|
-
const { checkoutUrl } = response.data;
|
|
13
|
-
if (jsonOutput) {
|
|
14
|
-
ctx.log(JSON.stringify({ checkoutUrl }, null, 2));
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
ctx.log(`\n ${chalk.bold('Stripe Checkout')} — add or update your payment method.`);
|
|
18
|
-
ctx.log(` ${chalk.dim(checkoutUrl)}\n`);
|
|
19
|
-
if (!process.env.CI) {
|
|
20
|
-
try {
|
|
21
|
-
await open(checkoutUrl);
|
|
22
|
-
ctx.log(` ${INFO.BROWSER_OPENING}\n`);
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
ctx.log(` ${INFO.BROWSER_OPEN_FAILED}\n`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function logStatusFields(ctx, data) {
|
|
30
|
-
ctx.log('');
|
|
31
|
-
ctx.log(` Tier: ${data.tier}`);
|
|
32
|
-
ctx.log(` Payment: ${formatPaymentMethod(data)}`);
|
|
33
|
-
ctx.log(` Auto-charge: ${formatAutoCharge(data)}`);
|
|
34
|
-
if (data.tier !== FREE_TIER) {
|
|
35
|
-
ctx.log(` Monthly cap: ${formatCap(data.cap)}`);
|
|
36
|
-
}
|
|
37
|
-
ctx.log(` Total spent: ${formatCurrency(data.totalSpent, data.spentCurrency)}`);
|
|
38
|
-
if (data.activeBlocks.length === 0) {
|
|
39
|
-
ctx.log(' Active blocks: none');
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
ctx.log(' Active blocks:');
|
|
43
|
-
for (const block of data.activeBlocks) {
|
|
44
|
-
ctx.log(` - ${formatUsageBlock(block)}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
ctx.log('');
|
|
48
|
-
}
|
|
49
|
-
export async function showStatus(ctx, jsonOutput, statusOnly) {
|
|
50
|
-
const response = await ctx.spinner(' Loading billing status...', jsonOutput, () => ctx.get(API_ENDPOINTS.BILLING_STATUS));
|
|
51
|
-
if (!response.ok) {
|
|
52
|
-
ctx.onApiError(response);
|
|
53
|
-
}
|
|
54
|
-
const { data } = response;
|
|
55
|
-
if (jsonOutput) {
|
|
56
|
-
ctx.log(JSON.stringify(data, null, 2));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
logStatusFields(ctx, data);
|
|
60
|
-
if (!data.hasPaymentMethod && !statusOnly) {
|
|
61
|
-
await startCheckout(ctx, jsonOutput);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { BillingCap, BillingStatusResponse, BillingUsageBlock } from './types.js';
|
|
2
|
-
export declare function pluralize(word: string, count: number): string;
|
|
3
|
-
export declare function formatAutoCharge(data: BillingStatusResponse): string;
|
|
4
|
-
export declare function formatCap(cap: BillingCap): string;
|
|
5
|
-
export declare function formatCurrency(amount: number | string, currency: string): string;
|
|
6
|
-
export declare function formatPaymentMethod(data: BillingStatusResponse): string;
|
|
7
|
-
export declare function formatUsageBlock(block: BillingUsageBlock): string;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
export function pluralize(word, count) {
|
|
2
|
-
return count === 1 ? word : `${word}s`;
|
|
3
|
-
}
|
|
4
|
-
export function formatAutoCharge(data) {
|
|
5
|
-
if (!data.autoChargeEnabled) {
|
|
6
|
-
return 'disabled';
|
|
7
|
-
}
|
|
8
|
-
if (typeof data.autoChargeBlockCount !== 'number') {
|
|
9
|
-
return 'enabled';
|
|
10
|
-
}
|
|
11
|
-
return `enabled (${data.autoChargeBlockCount} ${pluralize('block', data.autoChargeBlockCount)})`;
|
|
12
|
-
}
|
|
13
|
-
export function formatCap(cap) {
|
|
14
|
-
const hasBlocks = typeof cap.inBlocks === 'number';
|
|
15
|
-
const hasEmails = typeof cap.inEmails === 'number';
|
|
16
|
-
if (hasBlocks && hasEmails) {
|
|
17
|
-
return `${cap.inBlocks} ${pluralize('block', cap.inBlocks)} (${cap.inEmails.toLocaleString()} ${pluralize('email', cap.inEmails)})`;
|
|
18
|
-
}
|
|
19
|
-
if (hasBlocks) {
|
|
20
|
-
return `${cap.inBlocks} ${pluralize('block', cap.inBlocks)}`;
|
|
21
|
-
}
|
|
22
|
-
if (hasEmails) {
|
|
23
|
-
return `${cap.inEmails.toLocaleString()} ${pluralize('email', cap.inEmails)}`;
|
|
24
|
-
}
|
|
25
|
-
return 'not set';
|
|
26
|
-
}
|
|
27
|
-
export function formatCurrency(amount, currency) {
|
|
28
|
-
const numericAmount = typeof amount === 'number' ? amount : Number.parseFloat(amount);
|
|
29
|
-
const normalizedCurrency = currency.toUpperCase();
|
|
30
|
-
if (Number.isFinite(numericAmount)) {
|
|
31
|
-
try {
|
|
32
|
-
return new Intl.NumberFormat('en-US', {
|
|
33
|
-
currency: normalizedCurrency,
|
|
34
|
-
style: 'currency',
|
|
35
|
-
}).format(numericAmount);
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return `${numericAmount.toFixed(2)} ${normalizedCurrency}`;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return `${String(amount)} ${normalizedCurrency}`;
|
|
42
|
-
}
|
|
43
|
-
export function formatPaymentMethod(data) {
|
|
44
|
-
if (!data.hasPaymentMethod) {
|
|
45
|
-
return 'No payment method on file';
|
|
46
|
-
}
|
|
47
|
-
const primaryMethod = data.paymentMethod[0];
|
|
48
|
-
if (!primaryMethod) {
|
|
49
|
-
return 'Payment method on file';
|
|
50
|
-
}
|
|
51
|
-
const brand = primaryMethod.brand || 'Card';
|
|
52
|
-
return primaryMethod.last4
|
|
53
|
-
? `${brand} ending ${primaryMethod.last4}`
|
|
54
|
-
: `${brand} on file`;
|
|
55
|
-
}
|
|
56
|
-
export function formatUsageBlock(block) {
|
|
57
|
-
const allowance = block.blockAllowance ?? Math.max(block.blocksCount * block.blockSize, 0);
|
|
58
|
-
const used = Math.max(block.emailsSent ?? 0, 0);
|
|
59
|
-
const activationSuffix = block.activatedAt
|
|
60
|
-
? `, activated at ${new Date(block.activatedAt).toLocaleDateString('en-US')}`
|
|
61
|
-
: '';
|
|
62
|
-
return `${block.type} block (${used + allowance}): ${used} ${pluralize('email', used)} sent, ${allowance} ${pluralize('email', allowance)} remaining${activationSuffix}`;
|
|
63
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { BillingCtx } from './types.js';
|
|
2
|
-
export declare function purchaseBlocks(ctx: BillingCtx, blocksCount: number, jsonOutput: boolean): Promise<void>;
|
|
3
|
-
export declare function setCap(ctx: BillingCtx, opts: {
|
|
4
|
-
autoChargeBlockCount?: number;
|
|
5
|
-
cap: number;
|
|
6
|
-
json: boolean;
|
|
7
|
-
}): Promise<void>;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { FREE_TIER } from '../../base-command.js';
|
|
3
|
-
import { API_ENDPOINTS } from '../../constants.js';
|
|
4
|
-
import { loadYaml, saveYaml } from '../../yaml-config.js';
|
|
5
|
-
import { pluralize } from './format.js';
|
|
6
|
-
async function persistMonthlyCap(ctx, capBlocks) {
|
|
7
|
-
const yamlConfig = await loadYaml();
|
|
8
|
-
if (!yamlConfig)
|
|
9
|
-
return;
|
|
10
|
-
if (yamlConfig.project.monthlyCap === capBlocks)
|
|
11
|
-
return;
|
|
12
|
-
yamlConfig.project.monthlyCap = capBlocks;
|
|
13
|
-
await saveYaml(yamlConfig);
|
|
14
|
-
await ctx.syncYaml();
|
|
15
|
-
}
|
|
16
|
-
export async function purchaseBlocks(ctx, blocksCount, jsonOutput) {
|
|
17
|
-
if (blocksCount < 1) {
|
|
18
|
-
ctx.error('Purchase block count must be at least 1 block.');
|
|
19
|
-
}
|
|
20
|
-
const response = await ctx.spinner(' Initiating block purchase...', jsonOutput, () => ctx.post(API_ENDPOINTS.BILLING_PURCHASE, {
|
|
21
|
-
blocksCount,
|
|
22
|
-
}));
|
|
23
|
-
if (!response.ok) {
|
|
24
|
-
ctx.onApiError(response);
|
|
25
|
-
}
|
|
26
|
-
if (jsonOutput) {
|
|
27
|
-
ctx.log(JSON.stringify(response.data, null, 2));
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
ctx.log(`\n ${chalk.green('✓')} ${response.data.message}`);
|
|
31
|
-
ctx.log(` Blocks: ${blocksCount} ${pluralize('block', blocksCount)}`);
|
|
32
|
-
ctx.log(` Payment intent:${` ${chalk.dim(response.data.paymentIntentId)}`}`);
|
|
33
|
-
ctx.log('');
|
|
34
|
-
}
|
|
35
|
-
export async function setCap(ctx, opts) {
|
|
36
|
-
ctx.validateBillingCapInputs({
|
|
37
|
-
autoChargeBlockCount: opts.autoChargeBlockCount,
|
|
38
|
-
cap: opts.cap,
|
|
39
|
-
});
|
|
40
|
-
const tier = await ctx.fetchBillingTier();
|
|
41
|
-
if (tier === FREE_TIER) {
|
|
42
|
-
ctx.warnFreeTierCapBlocked(opts.json);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const data = await ctx.applyBillingCap(opts);
|
|
46
|
-
await persistMonthlyCap(ctx, data.capBlocks);
|
|
47
|
-
if (opts.json) {
|
|
48
|
-
ctx.log(JSON.stringify(data, null, 2));
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
ctx.log(`\n ${chalk.green('✓')} ${data.message}`);
|
|
52
|
-
ctx.log(` Monthly cap: ${chalk.bold(String(data.capBlocks))} ${pluralize('block', data.capBlocks)} (${data.capEmails.toLocaleString()} emails)`);
|
|
53
|
-
if (data.autoChargeBlockCount !== undefined) {
|
|
54
|
-
ctx.log(` Auto-charge: ${data.autoChargeBlockCount} ${pluralize('block', data.autoChargeBlockCount)}`);
|
|
55
|
-
}
|
|
56
|
-
ctx.log('');
|
|
57
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { ApiResponse } from '../../api-client.js';
|
|
2
|
-
import type { BillingCapUpdateResult } from '../../base-command.js';
|
|
3
|
-
export type { BillingCapUpdateResult } from '../../base-command.js';
|
|
4
|
-
export interface BillingCap {
|
|
5
|
-
capUpdatedAt: null | string;
|
|
6
|
-
inBlocks: null | number;
|
|
7
|
-
inEmails: null | number;
|
|
8
|
-
}
|
|
9
|
-
export interface BillingPaymentMethod {
|
|
10
|
-
brand?: string;
|
|
11
|
-
last4?: string;
|
|
12
|
-
type?: string;
|
|
13
|
-
}
|
|
14
|
-
export interface BillingUsageBlock {
|
|
15
|
-
activatedAt?: string;
|
|
16
|
-
blockAllowance?: number;
|
|
17
|
-
blockSize: number;
|
|
18
|
-
blocksCount: number;
|
|
19
|
-
emailsSent: number;
|
|
20
|
-
id: string;
|
|
21
|
-
status: string;
|
|
22
|
-
type: string;
|
|
23
|
-
}
|
|
24
|
-
export interface BillingStatusResponse {
|
|
25
|
-
activeBlocks: BillingUsageBlock[];
|
|
26
|
-
autoChargeBlockCount: null | number;
|
|
27
|
-
autoChargeEnabled: boolean;
|
|
28
|
-
cap: BillingCap;
|
|
29
|
-
hasPaymentMethod: boolean;
|
|
30
|
-
paymentMethod: BillingPaymentMethod[];
|
|
31
|
-
spentCurrency: string;
|
|
32
|
-
tier: string;
|
|
33
|
-
totalSpent: number | string;
|
|
34
|
-
}
|
|
35
|
-
export interface CheckoutResponse {
|
|
36
|
-
checkoutUrl: string;
|
|
37
|
-
}
|
|
38
|
-
export interface BillingPurchaseResponse {
|
|
39
|
-
message: string;
|
|
40
|
-
paymentIntentId: string;
|
|
41
|
-
}
|
|
42
|
-
export type BillingFlags = {
|
|
43
|
-
'auto-charge-block-count'?: number;
|
|
44
|
-
cap?: number;
|
|
45
|
-
checkout: boolean;
|
|
46
|
-
json: boolean;
|
|
47
|
-
purchase?: number;
|
|
48
|
-
status: boolean;
|
|
49
|
-
};
|
|
50
|
-
export type BillingCtx = {
|
|
51
|
-
applyBillingCap(opts: {
|
|
52
|
-
autoChargeBlockCount?: number;
|
|
53
|
-
cap: number;
|
|
54
|
-
json: boolean;
|
|
55
|
-
}): Promise<BillingCapUpdateResult>;
|
|
56
|
-
error(msg: string): never;
|
|
57
|
-
fetchBillingTier(): Promise<null | string>;
|
|
58
|
-
get<T>(path: string, params?: Record<string, string>): Promise<ApiResponse<T>>;
|
|
59
|
-
log(msg?: string): void;
|
|
60
|
-
onApiError(resp: {
|
|
61
|
-
error?: string;
|
|
62
|
-
status: number;
|
|
63
|
-
}): never;
|
|
64
|
-
post<T>(path: string, body?: unknown): Promise<ApiResponse<T>>;
|
|
65
|
-
spinner<T>(text: string, json: boolean, work: () => Promise<T>): Promise<T>;
|
|
66
|
-
syncYaml(): Promise<void>;
|
|
67
|
-
validateBillingCapInputs(opts: {
|
|
68
|
-
autoChargeBlockCount?: number;
|
|
69
|
-
cap: number;
|
|
70
|
-
}): void;
|
|
71
|
-
warnFreeTierCapBlocked(json: boolean): void;
|
|
72
|
-
};
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { API_ENDPOINTS } from '../../constants.js';
|
|
3
|
-
export async function showSummary(ctx, json) {
|
|
4
|
-
const response = await ctx.spinner(' Loading contacts...', json, () => ctx.get(API_ENDPOINTS.CONTACTS));
|
|
5
|
-
if (!response.ok) {
|
|
6
|
-
ctx.onApiError(response);
|
|
7
|
-
}
|
|
8
|
-
const { data } = response;
|
|
9
|
-
if (json) {
|
|
10
|
-
ctx.log(JSON.stringify(data, null, 2));
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
ctx.log(`\n Total: ${chalk.bold(String(data.total ?? 0))} ` +
|
|
14
|
-
`Active: ${chalk.green(String(data.active ?? 0))} ` +
|
|
15
|
-
`Unsubscribed: ${chalk.yellow(String(data.unsubscribed ?? 0))} ` +
|
|
16
|
-
`Bounced: ${chalk.red(String(data.bounced ?? 0))}\n`);
|
|
17
|
-
}
|
|
18
|
-
export async function searchContact(ctx, email, json) {
|
|
19
|
-
const encodedEmail = encodeURIComponent(email);
|
|
20
|
-
const response = await ctx.spinner(' Looking up contact...', json, () => ctx.get(`${API_ENDPOINTS.CONTACTS}/${encodedEmail}`));
|
|
21
|
-
if (!response.ok) {
|
|
22
|
-
if (response.status === 404) {
|
|
23
|
-
if (json) {
|
|
24
|
-
ctx.log(JSON.stringify({ email, error: 'Contact not found' }, null, 2));
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
ctx.log(`\n Contact '${email}' not found.\n`);
|
|
28
|
-
}
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
ctx.onApiError(response);
|
|
32
|
-
}
|
|
33
|
-
const contact = response.data;
|
|
34
|
-
if (json) {
|
|
35
|
-
ctx.log(JSON.stringify(contact, null, 2));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
ctx.log(`\n ${chalk.bold('Email:')} ${contact.email}`);
|
|
39
|
-
ctx.log(` ${chalk.bold('Status:')} ${contact.status}`);
|
|
40
|
-
ctx.log(` ${chalk.bold('Added:')} ${contact.added}`);
|
|
41
|
-
ctx.log(` ${chalk.bold('Next email:')} ${contact.nextEmail || 'None'}`);
|
|
42
|
-
if (contact.properties && Object.keys(contact.properties).length > 0) {
|
|
43
|
-
ctx.log(` ${chalk.bold('Properties:')}`);
|
|
44
|
-
for (const [key, value] of Object.entries(contact.properties)) {
|
|
45
|
-
ctx.log(` ${key}: ${String(value)}`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
ctx.log('');
|
|
49
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ContactsCtx } from './types.js';
|
|
2
|
-
export declare function exportContacts(ctx: ContactsCtx, opts: {
|
|
3
|
-
json: boolean;
|
|
4
|
-
}): Promise<void>;
|
|
5
|
-
export declare function deleteContact(ctx: ContactsCtx, opts: {
|
|
6
|
-
email: string;
|
|
7
|
-
json: boolean;
|
|
8
|
-
skipConfirm: boolean;
|
|
9
|
-
}): Promise<void>;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { confirm } from '@inquirer/prompts';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { writeFile } from 'node:fs/promises';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { API_ENDPOINTS } from '../../constants.js';
|
|
6
|
-
export async function exportContacts(ctx, opts) {
|
|
7
|
-
const { json } = opts;
|
|
8
|
-
const response = await ctx.spinner(' Preparing contact export...', json, () => ctx.get(API_ENDPOINTS.CONTACTS_EXPORT));
|
|
9
|
-
if (!response.ok) {
|
|
10
|
-
ctx.onApiError(response);
|
|
11
|
-
}
|
|
12
|
-
if (json) {
|
|
13
|
-
ctx.log(JSON.stringify(response.data, null, 2));
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
ctx.log(`\n ${chalk.green('✓')} Contact export started.`);
|
|
17
|
-
const { downloadUrl, status } = response.data;
|
|
18
|
-
if (!downloadUrl) {
|
|
19
|
-
ctx.log(`\n Export status: ${status ?? 'unknown'}. No download URL yet.\n`);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const fileResult = await ctx.spinner(' Downloading CSV file...', json, () => ctx.getPublicFile(downloadUrl.trim()));
|
|
23
|
-
if (!fileResult.ok) {
|
|
24
|
-
ctx.error(`Download failed: ${fileResult.status} ${fileResult.error ?? ''}\n URL: ${fileResult.debug.fullUrl}`);
|
|
25
|
-
}
|
|
26
|
-
const outputPath = resolve('contacts.csv');
|
|
27
|
-
await writeFile(outputPath, Buffer.from(fileResult.body));
|
|
28
|
-
ctx.log(`\n ${chalk.green('✓')} Contact export saved to ${chalk.cyan(outputPath)}\n`);
|
|
29
|
-
}
|
|
30
|
-
export async function deleteContact(ctx, opts) {
|
|
31
|
-
const { email, json, skipConfirm } = opts;
|
|
32
|
-
if (!skipConfirm) {
|
|
33
|
-
const confirmed = await confirm({
|
|
34
|
-
default: false,
|
|
35
|
-
message: `Permanently delete ${email} and all their data? This cannot be undone.`,
|
|
36
|
-
});
|
|
37
|
-
if (!confirmed) {
|
|
38
|
-
ctx.log('\n Delete cancelled.\n');
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
const response = await ctx.spinner(' Deleting contact...', json, () => ctx.delete(`${API_ENDPOINTS.CONTACTS}/${encodeURIComponent(email)}`));
|
|
43
|
-
if (!response.ok) {
|
|
44
|
-
ctx.onApiError(response);
|
|
45
|
-
}
|
|
46
|
-
if (json) {
|
|
47
|
-
ctx.log(JSON.stringify({ deleted: true, email }, null, 2));
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
ctx.log(`\n ${chalk.green('✓')} Contact ${email} permanently deleted.\n`);
|
|
51
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { ApiResponse } from '../../api-client.js';
|
|
2
|
-
import type { FileFetchResult } from '../../fetch-file.js';
|
|
3
|
-
export interface ContactSummaryResponse {
|
|
4
|
-
active: number;
|
|
5
|
-
bounced: number;
|
|
6
|
-
total: number;
|
|
7
|
-
unsubscribed: number;
|
|
8
|
-
}
|
|
9
|
-
export interface ContactDetailResponse {
|
|
10
|
-
added: string;
|
|
11
|
-
email: string;
|
|
12
|
-
nextEmail: null | string;
|
|
13
|
-
properties: Record<string, unknown>;
|
|
14
|
-
status: string;
|
|
15
|
-
}
|
|
16
|
-
export interface ContactExportResponse {
|
|
17
|
-
downloadUrl: string;
|
|
18
|
-
status: string;
|
|
19
|
-
}
|
|
20
|
-
export type ContactsFlags = {
|
|
21
|
-
json: boolean;
|
|
22
|
-
yes: boolean;
|
|
23
|
-
};
|
|
24
|
-
export type ContactsCtx = {
|
|
25
|
-
delete<T = Record<string, unknown>>(path: string): Promise<ApiResponse<T>>;
|
|
26
|
-
error(msg: string): never;
|
|
27
|
-
get<T>(path: string, params?: Record<string, string>): Promise<ApiResponse<T>>;
|
|
28
|
-
getPublicFile(url: string): Promise<FileFetchResult>;
|
|
29
|
-
log(msg?: string): void;
|
|
30
|
-
onApiError(resp: {
|
|
31
|
-
error?: string;
|
|
32
|
-
status: number;
|
|
33
|
-
}): never;
|
|
34
|
-
spinner<T>(text: string, json: boolean, work: () => Promise<T>): Promise<T>;
|
|
35
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|