@mailmodo/cli 0.0.56-beta.pr58.108 → 0.0.56-beta.pr58.94

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 (39) hide show
  1. package/dist/commands/deploy/index.js +3 -5
  2. package/dist/commands/init/index.js +3 -11
  3. package/dist/commands/login/index.js +5 -2
  4. package/dist/lib/api-client.d.ts +0 -2
  5. package/dist/lib/api-client.js +2 -2
  6. package/dist/lib/base-command.d.ts +13 -10
  7. package/dist/lib/base-command.js +27 -86
  8. package/dist/lib/commands/edit/diff.js +2 -2
  9. package/dist/lib/commands/edit/persist.js +4 -5
  10. package/dist/lib/commands/emails/editor.js +1 -1
  11. package/dist/lib/commands/init/analysis.js +1 -5
  12. package/dist/lib/commands/login/output.d.ts +2 -2
  13. package/dist/lib/commands/login/output.js +18 -5
  14. package/dist/lib/config.d.ts +0 -2
  15. package/dist/lib/config.js +10 -19
  16. package/dist/lib/constants.d.ts +2 -3
  17. package/dist/lib/constants.js +5 -4
  18. package/dist/lib/messages.d.ts +0 -18
  19. package/dist/lib/messages.js +0 -38
  20. package/dist/lib/templates/missing-templates.d.ts +1 -1
  21. package/dist/lib/templates/missing-templates.js +2 -2
  22. package/dist/lib/yaml-config.d.ts +0 -1
  23. package/dist/lib/yaml-config.js +0 -8
  24. package/oclif.manifest.json +32 -199
  25. package/package.json +1 -1
  26. package/dist/commands/report/index.d.ts +0 -22
  27. package/dist/commands/report/index.js +0 -123
  28. package/dist/lib/commands/report/output-entries.d.ts +0 -2
  29. package/dist/lib/commands/report/output-entries.js +0 -59
  30. package/dist/lib/commands/report/output-timeseries.d.ts +0 -2
  31. package/dist/lib/commands/report/output-timeseries.js +0 -28
  32. package/dist/lib/commands/report/output.d.ts +0 -3
  33. package/dist/lib/commands/report/output.js +0 -56
  34. package/dist/lib/commands/report/payload.d.ts +0 -2
  35. package/dist/lib/commands/report/payload.js +0 -49
  36. package/dist/lib/commands/report/prompt.d.ts +0 -2
  37. package/dist/lib/commands/report/prompt.js +0 -82
  38. package/dist/lib/commands/report/types.d.ts +0 -97
  39. package/dist/lib/commands/report/types.js +0 -1
@@ -14,16 +14,6 @@ export const PROMPTS = {
14
14
  REPLY_TO: 'Reply-to address (optional, press Enter to use sender email):',
15
15
  SENDER_EMAIL: 'Sender email address:',
16
16
  };
17
- export const BLANK_DIR = {
18
- CHOICE_FRESH: 'Start fresh — new emails will be generated (your domain, sender, and address settings will be kept)',
19
- CHOICE_RESTORE: 'Restore project files from server',
20
- CHOICE_RESTORE_INIT: 'Restore existing project files from server',
21
- CHOICE_SKIP: "Skip — I'll navigate to my project directory manually",
22
- PROMPT: 'No mailmodo.yaml or templates found in this directory.',
23
- PROMPT_INIT: 'A project was found on the server. What would you like to do?',
24
- RESTORED_INIT: `Project restored from server. Run ${chalk.cyan('mailmodo deploy')} to re-deploy, or ${chalk.cyan('mailmodo emails')} to review your emails.`,
25
- SKIP_HINT: `Navigate to your project directory and run your command there, or run ${chalk.cyan('mailmodo init')} to start a new project here.`,
26
- };
27
17
  export const MISSING_TEMPLATES = {
28
18
  ABORT_HINT: `Restore the missing files from version control, then run ${chalk.cyan('mailmodo deploy')} again.`,
29
19
  CHOICE_ABORT: 'Abort (restore from version control)',
@@ -64,13 +54,6 @@ export const INFO = {
64
54
  YAML_RESTORED_FROM_SERVER: chalk.dim(' mailmodo.yaml not found locally — restored from server.'),
65
55
  YAML_RESTORED_ON_LOGIN: ` mailmodo.yaml restored from server. Run ${chalk.cyan("'mailmodo deploy'")} to re-deploy your sequences.`,
66
56
  };
67
- export const REPORTS = {
68
- FETCH_SPINNER: ' Fetching report...',
69
- FROM_TO_REQUIRED: '--from and --to must both be provided together.',
70
- NO_DATA: 'No data found for the given filters and time range.',
71
- TIME_RANGE_REQUIRED: 'A time range is required. Use --preset or --from/--to.',
72
- TOO_MANY_ITEMS: 'Filter arrays cannot exceed 100 items each.',
73
- };
74
57
  export const DEPLOY = {
75
58
  CHANGES_HEADER: 'Changes vs. last deployment:',
76
59
  DEPLOYING_HEADER: 'Deploying:',
@@ -80,27 +63,6 @@ export const DEPLOY = {
80
63
  SDK_ONBOARDING_HEADER: chalk.bold('ADD THIS TO YOUR APP (one-time only):'),
81
64
  SUCCESS: `${chalk.green('Deployed.')} Emails are live.`,
82
65
  };
83
- export function restoredFromServerHint(ids) {
84
- if (ids.length === 1) {
85
- const [id] = ids;
86
- return (`Template ${chalk.cyan(id)} was not found locally and has been refreshed from the server.\n` +
87
- ` Review it with ${chalk.cyan(`mailmodo preview ${id}`)}, then run ${chalk.cyan('mailmodo deploy')} again.`);
88
- }
89
- const header = 'Template ID';
90
- const colWidth = Math.max(header.length, ...ids.map((id) => id.length));
91
- const pad = (s) => s.padEnd(colWidth);
92
- const hr = '─'.repeat(colWidth + 2);
93
- const table = [
94
- ` ┌${hr}┐`,
95
- ` │ ${chalk.bold(pad(header))} │`,
96
- ` ├${hr}┤`,
97
- ...ids.map((id) => ` │ ${chalk.cyan(pad(id))} │`),
98
- ` └${hr}┘`,
99
- ].join('\n');
100
- return (`${ids.length} templates were not found locally and have been refreshed from the server:\n\n` +
101
- `${table}\n\n` +
102
- ` Review each with ${chalk.cyan('mailmodo preview <id>')}, then run ${chalk.cyan('mailmodo deploy')} again.`);
103
- }
104
66
  export function pauseSuccess(sequenceId) {
105
67
  return `Sequence ${chalk.cyan(sequenceId)} paused. Run ${chalk.cyan(`mailmodo deploy --resume ${sequenceId}`)} to resume.`;
106
68
  }
@@ -16,4 +16,4 @@ export declare function getMissingTemplateIds(yamlConfig: MailmodoYaml): string[
16
16
  * Returns true if the situation was resolved (restored or regenerated), false
17
17
  * if the user chose to abort.
18
18
  */
19
- export declare function handleMissingTemplates(ctx: RegenCtx, yamlConfig: MailmodoYaml, missingIds: string[], flags: DeployFlags): Promise<'regenerated' | 'restored' | false>;
19
+ export declare function handleMissingTemplates(ctx: RegenCtx, yamlConfig: MailmodoYaml, missingIds: string[], flags: DeployFlags): Promise<boolean>;
@@ -39,7 +39,7 @@ export async function handleMissingTemplates(ctx, yamlConfig, missingIds, flags)
39
39
  // Try to silently recover from server before interrupting the user
40
40
  const stillMissing = await silentlyRestoreFromServer(ctx, missingIds);
41
41
  if (stillMissing.length === 0)
42
- return 'restored';
42
+ return true;
43
43
  if (flags.json) {
44
44
  ctx.log(JSON.stringify({
45
45
  error: 'missing_templates',
@@ -69,5 +69,5 @@ export async function handleMissingTemplates(ctx, yamlConfig, missingIds, flags)
69
69
  return false;
70
70
  }
71
71
  await regenerateMissingTemplates(ctx, yamlConfig, stillMissing, flags);
72
- return 'regenerated';
72
+ return true;
73
73
  }
@@ -44,7 +44,6 @@ export interface MailmodoYaml {
44
44
  * formatted Error with the line number if the file contains invalid YAML syntax.
45
45
  */
46
46
  export declare function loadYaml(cwd?: string): Promise<MailmodoYaml | null>;
47
- export declare function parseYamlText(text: string): MailmodoYaml | null;
48
47
  /**
49
48
  * Serializes and writes the mailmodo.yaml configuration to disk.
50
49
  *
@@ -25,14 +25,6 @@ export async function loadYaml(cwd) {
25
25
  throw new Error(yamlParseError(error.message));
26
26
  }
27
27
  }
28
- export function parseYamlText(text) {
29
- try {
30
- return load(text);
31
- }
32
- catch {
33
- return null;
34
- }
35
- }
36
28
  /**
37
29
  * Serializes and writes the mailmodo.yaml configuration to disk.
38
30
  *
@@ -465,44 +465,6 @@
465
465
  "index.js"
466
466
  ]
467
467
  },
468
- "logout": {
469
- "aliases": [],
470
- "args": {},
471
- "description": "Sign out by removing saved credentials from this machine",
472
- "examples": [
473
- "<%= config.bin %> logout"
474
- ],
475
- "flags": {
476
- "json": {
477
- "description": "Output as JSON",
478
- "name": "json",
479
- "allowNo": false,
480
- "type": "boolean"
481
- },
482
- "yes": {
483
- "char": "y",
484
- "description": "Skip confirmation prompts",
485
- "name": "yes",
486
- "allowNo": false,
487
- "type": "boolean"
488
- }
489
- },
490
- "hasDynamicHelp": false,
491
- "hiddenAliases": [],
492
- "id": "logout",
493
- "pluginAlias": "@mailmodo/cli",
494
- "pluginName": "@mailmodo/cli",
495
- "pluginType": "core",
496
- "strict": true,
497
- "enableJsonFlag": false,
498
- "isESM": true,
499
- "relativePath": [
500
- "dist",
501
- "commands",
502
- "logout",
503
- "index.js"
504
- ]
505
- },
506
468
  "logs": {
507
469
  "aliases": [],
508
470
  "args": {},
@@ -631,17 +593,14 @@
631
593
  "index.js"
632
594
  ]
633
595
  },
634
- "report": {
596
+ "sdk": {
635
597
  "aliases": [],
636
598
  "args": {},
637
- "description": "Fetch an email analytics report",
599
+ "description": "Show the SDK track() / identify() reference for deployed sequences",
638
600
  "examples": [
639
- "<%= config.bin %> report --preset last7d",
640
- "<%= config.bin %> report --preset last30d --group-by emailId",
641
- "<%= config.bin %> report --from YYYY-MM-DD --to YYYY-MM-DD --output timeseries",
642
- "<%= config.bin %> report --preset last7d --output entries --page 1",
643
- "<%= config.bin %> report --preset last30d --sequence welcome-flow --event opened",
644
- "<%= config.bin %> report --preset last7d --json"
601
+ "<%= config.bin %> sdk",
602
+ "<%= config.bin %> sdk --sequence-id a1b2c3d4",
603
+ "<%= config.bin %> sdk --json"
645
604
  ],
646
605
  "flags": {
647
606
  "json": {
@@ -657,126 +616,9 @@
657
616
  "allowNo": false,
658
617
  "type": "boolean"
659
618
  },
660
- "contact": {
661
- "description": "Filter by contact email (repeatable)",
662
- "name": "contact",
663
- "hasDynamicHelp": false,
664
- "multiple": true,
665
- "type": "option"
666
- },
667
- "email-id": {
668
- "description": "Filter by email template ID (repeatable)",
669
- "name": "email-id",
670
- "hasDynamicHelp": false,
671
- "multiple": true,
672
- "type": "option"
673
- },
674
- "event": {
675
- "description": "Filter by event type (repeatable)",
676
- "name": "event",
677
- "hasDynamicHelp": false,
678
- "multiple": true,
679
- "options": [
680
- "bounced",
681
- "clicked",
682
- "complained",
683
- "delivered",
684
- "opened",
685
- "sent",
686
- "skipped",
687
- "unsubscribed"
688
- ],
689
- "type": "option"
690
- },
691
- "from": {
692
- "description": "Start of time range, inclusive (YYYY-MM-DD)",
693
- "exclusive": [
694
- "preset"
695
- ],
696
- "name": "from",
697
- "hasDynamicHelp": false,
698
- "multiple": false,
699
- "type": "option"
700
- },
701
- "group-by": {
702
- "description": "Group results by dimension",
703
- "name": "group-by",
704
- "default": "none",
705
- "hasDynamicHelp": false,
706
- "multiple": false,
707
- "options": [
708
- "contact",
709
- "day",
710
- "emailId",
711
- "hour",
712
- "none",
713
- "sequenceId",
714
- "status"
715
- ],
716
- "type": "option"
717
- },
718
- "limit": {
719
- "description": "Entries per page, max 200 (entries output only)",
720
- "name": "limit",
721
- "default": 50,
722
- "hasDynamicHelp": false,
723
- "multiple": false,
724
- "type": "option"
725
- },
726
- "output": {
727
- "description": "Output shape: summary | entries | timeseries",
728
- "name": "output",
729
- "default": "summary",
730
- "hasDynamicHelp": false,
731
- "multiple": false,
732
- "options": [
733
- "entries",
734
- "summary",
735
- "timeseries"
736
- ],
737
- "type": "option"
738
- },
739
- "page": {
740
- "description": "Page number (entries output only)",
741
- "name": "page",
742
- "default": 1,
743
- "hasDynamicHelp": false,
744
- "multiple": false,
745
- "type": "option"
746
- },
747
- "preset": {
748
- "description": "Relative time range preset",
749
- "exclusive": [
750
- "from",
751
- "to"
752
- ],
753
- "name": "preset",
754
- "hasDynamicHelp": false,
755
- "multiple": false,
756
- "options": [
757
- "last30d",
758
- "last7d",
759
- "last90d",
760
- "lastMonth",
761
- "thisMonth",
762
- "today",
763
- "yesterday"
764
- ],
765
- "type": "option"
766
- },
767
- "sequence": {
768
- "description": "Filter by sequence ID (repeatable)",
769
- "name": "sequence",
770
- "hasDynamicHelp": false,
771
- "multiple": true,
772
- "type": "option"
773
- },
774
- "to": {
775
- "description": "End of time range, exclusive (YYYY-MM-DD)",
776
- "exclusive": [
777
- "preset"
778
- ],
779
- "name": "to",
619
+ "sequence-id": {
620
+ "description": "Limit output to a single active sequence by ID (default: all active sequences)",
621
+ "name": "sequence-id",
780
622
  "hasDynamicHelp": false,
781
623
  "multiple": false,
782
624
  "type": "option"
@@ -784,7 +626,7 @@
784
626
  },
785
627
  "hasDynamicHelp": false,
786
628
  "hiddenAliases": [],
787
- "id": "report",
629
+ "id": "sdk",
788
630
  "pluginAlias": "@mailmodo/cli",
789
631
  "pluginName": "@mailmodo/cli",
790
632
  "pluginType": "core",
@@ -794,18 +636,18 @@
794
636
  "relativePath": [
795
637
  "dist",
796
638
  "commands",
797
- "report",
639
+ "sdk",
798
640
  "index.js"
799
641
  ]
800
642
  },
801
- "sdk": {
643
+ "settings": {
802
644
  "aliases": [],
803
645
  "args": {},
804
- "description": "Show the SDK track() / identify() reference for deployed sequences",
646
+ "description": "View and update project settings",
805
647
  "examples": [
806
- "<%= config.bin %> sdk",
807
- "<%= config.bin %> sdk --sequence-id a1b2c3d4",
808
- "<%= config.bin %> sdk --json"
648
+ "<%= config.bin %> settings",
649
+ "<%= config.bin %> settings --set brand_color=#0F3460",
650
+ "<%= config.bin %> settings --json"
809
651
  ],
810
652
  "flags": {
811
653
  "json": {
@@ -821,9 +663,9 @@
821
663
  "allowNo": false,
822
664
  "type": "boolean"
823
665
  },
824
- "sequence-id": {
825
- "description": "Limit output to a single active sequence by ID (default: all active sequences)",
826
- "name": "sequence-id",
666
+ "set": {
667
+ "description": "Set a setting (format: key=value)",
668
+ "name": "set",
827
669
  "hasDynamicHelp": false,
828
670
  "multiple": false,
829
671
  "type": "option"
@@ -831,7 +673,7 @@
831
673
  },
832
674
  "hasDynamicHelp": false,
833
675
  "hiddenAliases": [],
834
- "id": "sdk",
676
+ "id": "settings",
835
677
  "pluginAlias": "@mailmodo/cli",
836
678
  "pluginName": "@mailmodo/cli",
837
679
  "pluginType": "core",
@@ -841,18 +683,17 @@
841
683
  "relativePath": [
842
684
  "dist",
843
685
  "commands",
844
- "sdk",
686
+ "settings",
845
687
  "index.js"
846
688
  ]
847
689
  },
848
- "settings": {
690
+ "status": {
849
691
  "aliases": [],
850
692
  "args": {},
851
- "description": "View and update project settings",
693
+ "description": "View email performance metrics and quota usage",
852
694
  "examples": [
853
- "<%= config.bin %> settings",
854
- "<%= config.bin %> settings --set brand_color=#0F3460",
855
- "<%= config.bin %> settings --json"
695
+ "<%= config.bin %> status",
696
+ "<%= config.bin %> status --json"
856
697
  ],
857
698
  "flags": {
858
699
  "json": {
@@ -867,18 +708,11 @@
867
708
  "name": "yes",
868
709
  "allowNo": false,
869
710
  "type": "boolean"
870
- },
871
- "set": {
872
- "description": "Set a setting (format: key=value)",
873
- "name": "set",
874
- "hasDynamicHelp": false,
875
- "multiple": false,
876
- "type": "option"
877
711
  }
878
712
  },
879
713
  "hasDynamicHelp": false,
880
714
  "hiddenAliases": [],
881
- "id": "settings",
715
+ "id": "status",
882
716
  "pluginAlias": "@mailmodo/cli",
883
717
  "pluginName": "@mailmodo/cli",
884
718
  "pluginType": "core",
@@ -888,17 +722,16 @@
888
722
  "relativePath": [
889
723
  "dist",
890
724
  "commands",
891
- "settings",
725
+ "status",
892
726
  "index.js"
893
727
  ]
894
728
  },
895
- "status": {
729
+ "logout": {
896
730
  "aliases": [],
897
731
  "args": {},
898
- "description": "View email performance metrics and quota usage",
732
+ "description": "Sign out by removing saved credentials from this machine",
899
733
  "examples": [
900
- "<%= config.bin %> status",
901
- "<%= config.bin %> status --json"
734
+ "<%= config.bin %> logout"
902
735
  ],
903
736
  "flags": {
904
737
  "json": {
@@ -917,7 +750,7 @@
917
750
  },
918
751
  "hasDynamicHelp": false,
919
752
  "hiddenAliases": [],
920
- "id": "status",
753
+ "id": "logout",
921
754
  "pluginAlias": "@mailmodo/cli",
922
755
  "pluginName": "@mailmodo/cli",
923
756
  "pluginType": "core",
@@ -927,10 +760,10 @@
927
760
  "relativePath": [
928
761
  "dist",
929
762
  "commands",
930
- "status",
763
+ "logout",
931
764
  "index.js"
932
765
  ]
933
766
  }
934
767
  },
935
- "version": "0.0.56-beta.pr58.108"
768
+ "version": "0.0.56-beta.pr58.94"
936
769
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mailmodo/cli",
3
3
  "description": "Email lifecycle automation for the AI-native builder generation.",
4
- "version": "0.0.56-beta.pr58.108",
4
+ "version": "0.0.56-beta.pr58.94",
5
5
  "author": "provishalk",
6
6
  "bin": {
7
7
  "mailmodo": "bin/run.js"
@@ -1,22 +0,0 @@
1
- import { BaseCommand } from '../../lib/base-command.js';
2
- export default class Report extends BaseCommand {
3
- static description: string;
4
- static examples: string[];
5
- static flags: {
6
- contact: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
- 'email-id': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
- event: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
- from: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
- 'group-by': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
- limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
12
- output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
- page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
14
- preset: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
- sequence: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
- to: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
- json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
- yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
- };
20
- run(): Promise<void>;
21
- private makeCtx;
22
- }
@@ -1,123 +0,0 @@
1
- import { Flags } from '@oclif/core';
2
- import { BaseCommand } from '../../lib/base-command.js';
3
- import { API_ENDPOINTS } from '../../lib/constants.js';
4
- import { REPORTS } from '../../lib/messages.js';
5
- import { buildReportPayload } from '../../lib/commands/report/payload.js';
6
- import { renderReport } from '../../lib/commands/report/output.js';
7
- import { promptReportFlags } from '../../lib/commands/report/prompt.js';
8
- export default class Report extends BaseCommand {
9
- static description = 'Fetch an email analytics report';
10
- static examples = [
11
- '<%= config.bin %> report --preset last7d',
12
- '<%= config.bin %> report --preset last30d --group-by emailId',
13
- '<%= config.bin %> report --from YYYY-MM-DD --to YYYY-MM-DD --output timeseries',
14
- '<%= config.bin %> report --preset last7d --output entries --page 1',
15
- '<%= config.bin %> report --preset last30d --sequence welcome-flow --event opened',
16
- '<%= config.bin %> report --preset last7d --json',
17
- ];
18
- static flags = {
19
- ...BaseCommand.baseFlags,
20
- contact: Flags.string({
21
- description: 'Filter by contact email (repeatable)',
22
- multiple: true,
23
- }),
24
- 'email-id': Flags.string({
25
- description: 'Filter by email template ID (repeatable)',
26
- multiple: true,
27
- }),
28
- event: Flags.string({
29
- description: 'Filter by event type (repeatable)',
30
- multiple: true,
31
- options: [
32
- 'bounced',
33
- 'clicked',
34
- 'complained',
35
- 'delivered',
36
- 'opened',
37
- 'sent',
38
- 'skipped',
39
- 'unsubscribed',
40
- ],
41
- }),
42
- from: Flags.string({
43
- description: 'Start of time range, inclusive (YYYY-MM-DD)',
44
- exclusive: ['preset'],
45
- }),
46
- 'group-by': Flags.string({
47
- default: 'none',
48
- description: 'Group results by dimension',
49
- options: [
50
- 'contact',
51
- 'day',
52
- 'emailId',
53
- 'hour',
54
- 'none',
55
- 'sequenceId',
56
- 'status',
57
- ],
58
- }),
59
- limit: Flags.integer({
60
- default: 50,
61
- description: 'Entries per page, max 200 (entries output only)',
62
- max: 200,
63
- }),
64
- output: Flags.string({
65
- default: 'summary',
66
- description: 'Output shape: summary | entries | timeseries',
67
- options: ['entries', 'summary', 'timeseries'],
68
- }),
69
- page: Flags.integer({
70
- default: 1,
71
- description: 'Page number (entries output only)',
72
- }),
73
- preset: Flags.string({
74
- description: 'Relative time range preset',
75
- exclusive: ['from', 'to'],
76
- options: [
77
- 'last30d',
78
- 'last7d',
79
- 'last90d',
80
- 'lastMonth',
81
- 'thisMonth',
82
- 'today',
83
- 'yesterday',
84
- ],
85
- }),
86
- sequence: Flags.string({
87
- description: 'Filter by sequence ID (repeatable)',
88
- multiple: true,
89
- }),
90
- to: Flags.string({
91
- description: 'End of time range, exclusive (YYYY-MM-DD)',
92
- exclusive: ['preset'],
93
- }),
94
- };
95
- async run() {
96
- const { flags } = await this.parse(Report);
97
- await this.ensureAuth();
98
- if ((flags.from && !flags.to) || (!flags.from && flags.to)) {
99
- this.error(REPORTS.FROM_TO_REQUIRED);
100
- }
101
- const resolved = flags.json
102
- ? flags
103
- : await promptReportFlags(flags);
104
- const ctx = this.makeCtx();
105
- const payload = buildReportPayload(resolved, (m) => this.error(m));
106
- const response = await ctx.spinner(REPORTS.FETCH_SPINNER, flags.json, () => ctx.post(API_ENDPOINTS.REPORTS, payload));
107
- if (!response.ok)
108
- ctx.onApiError(response);
109
- if (flags.json) {
110
- this.log(JSON.stringify(response.data, null, 2));
111
- return;
112
- }
113
- renderReport(ctx, response.data, resolved.output);
114
- }
115
- makeCtx() {
116
- return {
117
- log: (msg) => this.log(msg),
118
- onApiError: (r) => this.handleApiError(r),
119
- post: (path, body) => this.apiClient.post(path, body),
120
- spinner: (text, json, work) => this.withApiSpinner({ json, text }, work),
121
- };
122
- }
123
- }
@@ -1,2 +0,0 @@
1
- import type { EntriesResponse, ReportCtx } from './types.js';
2
- export declare function renderEntries(ctx: ReportCtx, data: EntriesResponse): void;
@@ -1,59 +0,0 @@
1
- import chalk from 'chalk';
2
- const TIME_W = 18;
3
- const EMAIL_W = 24;
4
- const SEQ_W = 12;
5
- const STATUS_W = 14;
6
- const CONTACT_COL = TIME_W + EMAIL_W + SEQ_W + STATUS_W;
7
- function statusColor(status) {
8
- switch (status) {
9
- case 'bounced':
10
- case 'complained': {
11
- return chalk.red;
12
- }
13
- case 'clicked':
14
- case 'delivered':
15
- case 'opened':
16
- case 'sent': {
17
- return chalk.green;
18
- }
19
- case 'skipped': {
20
- return chalk.yellow;
21
- }
22
- default: {
23
- return chalk.white;
24
- }
25
- }
26
- }
27
- function truncate(s, width) {
28
- return s.length > width ? s.slice(0, width - 1) + '…' : s;
29
- }
30
- function shortSeq(id) {
31
- return id.length > 8 ? id.slice(0, 8) + '…' : id;
32
- }
33
- export function renderEntries(ctx, data) {
34
- const { entries, limit, page, total } = data;
35
- ctx.log(`\n ${'Time'.padEnd(TIME_W)}${'Template'.padEnd(EMAIL_W)}` +
36
- `${'Sequence'.padEnd(SEQ_W)}${'Status'.padEnd(STATUS_W)}Contact`);
37
- ctx.log(` ${'─'.repeat(CONTACT_COL + 28)}`);
38
- if (!entries?.length) {
39
- ctx.log(` ${chalk.dim('No entries found.')}`);
40
- ctx.log('');
41
- return;
42
- }
43
- for (const entry of entries) {
44
- const time = (entry.timestamp || '').padEnd(TIME_W);
45
- const email = truncate(entry.emailId || '', EMAIL_W - 2).padEnd(EMAIL_W);
46
- const seq = chalk.dim(shortSeq(entry.sequenceId || '').padEnd(SEQ_W));
47
- const status = statusColor(entry.status)((entry.status || '').padEnd(STATUS_W));
48
- ctx.log(` ${time}${email}${seq}${status}${entry.contact || ''}`);
49
- if (entry.reason) {
50
- const label = entry.status === 'skipped' ? 'condition not met' : 'reason';
51
- ctx.log(` ${' '.repeat(CONTACT_COL)}${chalk.dim(`└ ${label}: ${entry.reason}`)}`);
52
- }
53
- }
54
- const totalPages = Math.ceil(total / limit);
55
- ctx.log(`\n Page ${page} of ${totalPages} · ${total} total entries`);
56
- if (page < totalPages)
57
- ctx.log(` ${chalk.dim(`Next: --page ${page + 1}`)}`);
58
- ctx.log('');
59
- }
@@ -1,2 +0,0 @@
1
- import type { ReportCtx, TimeseriesResponse } from './types.js';
2
- export declare function renderTimeseries(ctx: ReportCtx, data: TimeseriesResponse): void;