@mailmodo/cli 0.0.54 → 0.0.55-beta.pr57.92

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 (136) hide show
  1. package/dist/commands/billing/index.d.ts +1 -11
  2. package/dist/commands/billing/index.js +28 -181
  3. package/dist/commands/contacts/index.d.ts +1 -19
  4. package/dist/commands/contacts/index.js +21 -114
  5. package/dist/commands/deploy/index.d.ts +1 -32
  6. package/dist/commands/deploy/index.js +52 -303
  7. package/dist/commands/deployments/index.d.ts +1 -4
  8. package/dist/commands/deployments/index.js +11 -52
  9. package/dist/commands/domain/index.d.ts +1 -14
  10. package/dist/commands/domain/index.js +19 -100
  11. package/dist/commands/edit/index.d.ts +2 -20
  12. package/dist/commands/edit/index.js +35 -244
  13. package/dist/commands/emails/index.d.ts +1 -2
  14. package/dist/commands/emails/index.js +26 -91
  15. package/dist/commands/init/index.d.ts +1 -2
  16. package/dist/commands/init/index.js +43 -179
  17. package/dist/commands/login/index.d.ts +2 -0
  18. package/dist/commands/login/index.js +35 -64
  19. package/dist/commands/logs/index.d.ts +1 -8
  20. package/dist/commands/logs/index.js +12 -55
  21. package/dist/commands/preview/index.d.ts +1 -19
  22. package/dist/commands/preview/index.js +40 -210
  23. package/dist/commands/sdk/index.d.ts +1 -3
  24. package/dist/commands/sdk/index.js +14 -46
  25. package/dist/commands/settings/index.d.ts +1 -22
  26. package/dist/commands/settings/index.js +35 -241
  27. package/dist/commands/status/index.d.ts +1 -0
  28. package/dist/commands/status/index.js +13 -39
  29. package/dist/lib/api-client.d.ts +5 -0
  30. package/dist/lib/api-client.js +45 -0
  31. package/dist/lib/base-command.d.ts +25 -1
  32. package/dist/lib/base-command.js +91 -5
  33. package/dist/lib/commands/billing/checkout-status.d.ts +3 -0
  34. package/dist/lib/commands/billing/checkout-status.js +63 -0
  35. package/dist/lib/commands/billing/format.d.ts +7 -0
  36. package/dist/lib/commands/billing/format.js +63 -0
  37. package/dist/lib/commands/billing/purchase-cap.d.ts +7 -0
  38. package/dist/lib/commands/billing/purchase-cap.js +57 -0
  39. package/dist/lib/commands/billing/types.d.ts +72 -0
  40. package/dist/lib/commands/billing/types.js +1 -0
  41. package/dist/lib/commands/contacts/actions.d.ts +3 -0
  42. package/dist/lib/commands/contacts/actions.js +49 -0
  43. package/dist/lib/commands/contacts/export-delete.d.ts +9 -0
  44. package/dist/lib/commands/contacts/export-delete.js +51 -0
  45. package/dist/lib/commands/contacts/types.d.ts +35 -0
  46. package/dist/lib/commands/contacts/types.js +1 -0
  47. package/dist/lib/commands/deploy/domain-setup.d.ts +8 -0
  48. package/dist/lib/commands/deploy/domain-setup.js +82 -0
  49. package/dist/lib/commands/deploy/output.d.ts +5 -0
  50. package/dist/lib/commands/deploy/output.js +61 -0
  51. package/dist/lib/commands/deploy/payload.d.ts +41 -0
  52. package/dist/lib/commands/deploy/payload.js +95 -0
  53. package/dist/lib/commands/deploy/sequence-status.d.ts +3 -0
  54. package/dist/lib/commands/deploy/sequence-status.js +56 -0
  55. package/dist/lib/commands/deploy/types.d.ts +88 -0
  56. package/dist/lib/commands/deploy/types.js +1 -0
  57. package/dist/lib/commands/deployments/output.d.ts +2 -0
  58. package/dist/lib/commands/deployments/output.js +68 -0
  59. package/dist/lib/commands/deployments/types.d.ts +24 -0
  60. package/dist/lib/commands/deployments/types.js +1 -0
  61. package/dist/lib/commands/domain/setup.d.ts +8 -0
  62. package/dist/lib/commands/domain/setup.js +53 -0
  63. package/dist/lib/commands/domain/types.d.ts +56 -0
  64. package/dist/lib/commands/domain/types.js +1 -0
  65. package/dist/lib/commands/domain/verify.d.ts +5 -0
  66. package/dist/lib/commands/domain/verify.js +50 -0
  67. package/dist/lib/commands/edit/diff.d.ts +7 -0
  68. package/dist/lib/commands/edit/diff.js +65 -0
  69. package/dist/lib/commands/edit/display.d.ts +5 -0
  70. package/dist/lib/commands/edit/display.js +53 -0
  71. package/dist/lib/commands/edit/flow.d.ts +8 -0
  72. package/dist/lib/commands/edit/flow.js +70 -0
  73. package/dist/lib/commands/edit/persist.d.ts +5 -0
  74. package/dist/lib/commands/edit/persist.js +65 -0
  75. package/dist/lib/commands/edit/types.d.ts +37 -0
  76. package/dist/lib/commands/edit/types.js +1 -0
  77. package/dist/lib/commands/emails/editor.d.ts +2 -0
  78. package/dist/lib/commands/emails/editor.js +43 -0
  79. package/dist/lib/commands/emails/output.d.ts +4 -0
  80. package/dist/lib/commands/emails/output.js +36 -0
  81. package/dist/lib/commands/emails/types.d.ts +3 -0
  82. package/dist/lib/commands/emails/types.js +1 -0
  83. package/dist/lib/commands/init/analysis.d.ts +3 -0
  84. package/dist/lib/commands/init/analysis.js +69 -0
  85. package/dist/lib/commands/init/output.d.ts +12 -0
  86. package/dist/lib/commands/init/output.js +39 -0
  87. package/dist/lib/commands/init/payload.d.ts +8 -0
  88. package/dist/lib/commands/init/payload.js +78 -0
  89. package/dist/lib/commands/init/types.d.ts +57 -0
  90. package/dist/lib/commands/init/types.js +1 -0
  91. package/dist/lib/commands/login/output.d.ts +8 -0
  92. package/dist/lib/commands/login/output.js +53 -0
  93. package/dist/lib/commands/login/types.d.ts +19 -0
  94. package/dist/lib/commands/login/types.js +1 -0
  95. package/dist/lib/commands/logs/output.d.ts +2 -0
  96. package/dist/lib/commands/logs/output.js +52 -0
  97. package/dist/lib/commands/logs/types.d.ts +23 -0
  98. package/dist/lib/commands/logs/types.js +1 -0
  99. package/dist/lib/commands/preview/actions.d.ts +11 -0
  100. package/dist/lib/commands/preview/actions.js +43 -0
  101. package/dist/lib/commands/preview/render.d.ts +3 -0
  102. package/dist/lib/commands/preview/render.js +30 -0
  103. package/dist/lib/commands/preview/server.d.ts +8 -0
  104. package/dist/lib/commands/preview/server.js +63 -0
  105. package/dist/lib/commands/preview/types.d.ts +19 -0
  106. package/dist/lib/commands/preview/types.js +1 -0
  107. package/dist/lib/commands/preview/wrapper-html.d.ts +2 -0
  108. package/dist/lib/commands/preview/wrapper-html.js +35 -0
  109. package/dist/lib/commands/sdk/output.d.ts +2 -0
  110. package/dist/lib/commands/sdk/output.js +42 -0
  111. package/dist/lib/commands/sdk/types.d.ts +21 -0
  112. package/dist/lib/commands/sdk/types.js +1 -0
  113. package/dist/lib/commands/settings/actions.d.ts +10 -0
  114. package/dist/lib/commands/settings/actions.js +56 -0
  115. package/dist/lib/commands/settings/display.d.ts +15 -0
  116. package/dist/lib/commands/settings/display.js +69 -0
  117. package/dist/lib/commands/settings/logo-domain.d.ts +3 -0
  118. package/dist/lib/commands/settings/logo-domain.js +47 -0
  119. package/dist/lib/commands/settings/prompt.d.ts +2 -0
  120. package/dist/lib/commands/settings/prompt.js +82 -0
  121. package/dist/lib/commands/settings/types.d.ts +65 -0
  122. package/dist/lib/commands/settings/types.js +1 -0
  123. package/dist/lib/commands/status/output.d.ts +2 -0
  124. package/dist/lib/commands/status/output.js +49 -0
  125. package/dist/lib/commands/status/types.d.ts +28 -0
  126. package/dist/lib/commands/status/types.js +1 -0
  127. package/dist/lib/constants.d.ts +1 -0
  128. package/dist/lib/constants.js +1 -0
  129. package/dist/lib/messages.d.ts +22 -0
  130. package/dist/lib/messages.js +22 -0
  131. package/dist/lib/templates/missing-templates.d.ts +5 -0
  132. package/dist/lib/templates/missing-templates.js +61 -0
  133. package/dist/lib/templates/types.d.ts +13 -0
  134. package/dist/lib/templates/types.js +1 -0
  135. package/oclif.manifest.json +66 -66
  136. package/package.json +1 -1
@@ -1,9 +1,8 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import { confirm, input, select } from '@inquirer/prompts';
3
- import chalk from 'chalk';
4
2
  import { BaseCommand } from '../../lib/base-command.js';
5
- import { API_ENDPOINTS } from '../../lib/constants.js';
6
- import { loadTemplate, getTemplateFilename, saveTemplate, saveYaml, } from '../../lib/yaml-config.js';
3
+ import { getTemplateFilename, loadTemplate } from '../../lib/yaml-config.js';
4
+ import { handleMissingTemplates } from '../../lib/templates/missing-templates.js';
5
+ import { askChangeDescription, runEditStep, } from '../../lib/commands/edit/flow.js';
7
6
  export default class Edit extends BaseCommand {
8
7
  static args = {
9
8
  id: Args.string({
@@ -32,7 +31,7 @@ export default class Edit extends BaseCommand {
32
31
  }
33
32
  const email = yamlConfig.emails[emailIndex];
34
33
  const templateFilename = getTemplateFilename(email.id, email.style, yamlConfig.project?.emailStyle);
35
- const ctx = {
34
+ const editCtx = {
36
35
  email,
37
36
  emailIndex,
38
37
  templateFilename,
@@ -43,247 +42,39 @@ export default class Edit extends BaseCommand {
43
42
  json: flags.json ?? false,
44
43
  yes: flags.yes ?? false,
45
44
  };
46
- const initialChange = flags.change?.trim() || (await this.askChangeDescription());
47
- await this.runEditStep(ctx, initialChange, editFlags);
48
- }
49
- async runEditStep(ctx, changeDescription, flags) {
50
- const response = await this.withApiSpinner({ json: flags.json, text: ' Applying AI edits...' }, () => this.callEditApi(changeDescription, ctx.email, ctx.templateHtml));
51
- if (!response.ok) {
52
- this.handleAiQuotaError(response, 'edit');
53
- this.handleApiError(response);
54
- }
55
- const updated = response.data;
56
- if (flags.json) {
57
- this.log(JSON.stringify(this.buildDiffPreview(ctx.email, updated, ctx.templateHtml), null, 2));
58
- }
59
- else {
60
- this.showChangeSummary(ctx.email, updated, ctx.templateHtml);
61
- }
62
- if (flags.yes) {
63
- await this.finalizeEdit(ctx, updated, flags);
64
- return;
65
- }
66
- await this.handleUserAction(ctx, updated, changeDescription, flags);
67
- }
68
- async handleUserAction(ctx, updated, changeDescription, flags) {
69
- this.log('');
70
- const action = await this.promptEditAction();
71
- if (action === 'skip') {
72
- this.log('\n Changes discarded.\n');
73
- return;
74
- }
75
- if (action === 'retry') {
76
- const newChange = await this.askChangeDescription();
77
- await this.runEditStep(ctx, newChange, flags);
78
- return;
79
- }
80
- await this.finalizeEdit(ctx, updated, flags);
81
- }
82
- callEditApi(changeDescription, email, templateHtml) {
83
- return this.apiClient.post(API_ENDPOINTS.EDIT, {
84
- changeRequest: changeDescription,
85
- currentEmail: {
86
- condition: email.condition,
87
- goal: email.goal,
88
- html: templateHtml,
89
- id: email.id,
90
- previewText: email.previewText,
91
- subject: email.subject,
92
- trigger: email.trigger,
93
- },
94
- });
95
- }
96
- async finalizeEdit(ctx, updated, flags) {
97
- const oldSubject = ctx.email.subject;
98
- this.applyEmailChanges(ctx.email, updated);
99
- await this.persistChanges(ctx, updated);
100
- if (flags.json) {
101
- this.logJsonResult(ctx.email, updated, oldSubject);
102
- }
103
- else if (flags.yes) {
104
- this.log(`\n Updated ${chalk.green('mailmodo.yaml')}\n`);
105
- }
106
- else {
107
- await this.handleAcceptOutput(ctx.email);
108
- }
109
- }
110
- applyEmailChanges(email, updated) {
111
- if (updated.subject)
112
- email.subject = updated.subject;
113
- if (updated.previewText)
114
- email.previewText = updated.previewText;
115
- if (updated.ctaText)
116
- email.ctaText = updated.ctaText;
117
- }
118
- async persistChanges(ctx, updated) {
119
- const updatedYaml = {
120
- ...ctx.yamlConfig,
121
- emails: [...ctx.yamlConfig.emails],
45
+ if (!editCtx.templateHtml) {
46
+ const regenerated = await handleMissingTemplates(this.makeRegenCtx(), yamlConfig, [email.id], editFlags);
47
+ if (!regenerated)
48
+ return;
49
+ editCtx.templateHtml = await loadTemplate(templateFilename);
50
+ if (!editCtx.templateHtml)
51
+ this.error('Template regeneration failed.');
52
+ }
53
+ const initialChange = flags.change?.trim() || (await askChangeDescription());
54
+ await runEditStep(this.makeCtx(), editCtx, initialChange, editFlags);
55
+ }
56
+ makeCtx() {
57
+ return {
58
+ error: (msg) => this.error(msg),
59
+ exit: (code) => this.exit(code),
60
+ handleAiQuotaError: (r, feature) => this.handleAiQuotaError(r, feature),
61
+ log: (msg) => this.log(msg),
62
+ onApiError: (r) => this.handleApiError(r),
63
+ post: (path, body) => this.apiClient.post(path, body),
64
+ runCommand: (id, argv) => this.config.runCommand(id, argv),
65
+ spinner: (text, json, work) => this.withApiSpinner({ json, text }, work),
66
+ syncYaml: () => this.syncYamlToServer(),
122
67
  };
123
- updatedYaml.emails[ctx.emailIndex] = ctx.email;
124
- await saveYaml(updatedYaml);
125
- if (updated.html) {
126
- await saveTemplate(ctx.templateFilename, updated.html);
127
- }
128
- }
129
- logJsonResult(email, updated, oldSubject) {
130
- this.log(JSON.stringify({
131
- diff: {
132
- previewText: updated.previewText && updated.previewText !== email.previewText
133
- ? { new: updated.previewText, old: email.previewText }
134
- : undefined,
135
- subject: oldSubject === email.subject
136
- ? undefined
137
- : { new: email.subject, old: oldSubject },
138
- },
139
- email,
140
- status: 'updated',
141
- }, null, 2));
142
- }
143
- async handleAcceptOutput(email) {
144
- this.log(`\n Updated ${chalk.green('mailmodo.yaml')}`);
145
- const shouldPreview = await confirm({
146
- default: true,
147
- message: 'Preview the change?',
148
- });
149
- if (shouldPreview) {
150
- await this.config.runCommand('preview', [email.id]);
151
- }
152
- else {
153
- this.log(` Run: ${chalk.cyan(`mailmodo preview ${email.id}`)}\n`);
154
- }
155
- }
156
- async promptEditAction() {
157
- return select({
158
- message: 'Accept, try again, or skip?',
159
- choices: [
160
- { name: 'Accept', value: 'accept' },
161
- { name: 'Try again', value: 'retry' },
162
- { name: 'Skip', value: 'skip' },
163
- ],
164
- });
165
- }
166
- async askChangeDescription() {
167
- return input({
168
- message: 'What do you want to change?',
169
- validate: (value) => value?.trim() ? true : 'Please describe the change',
170
- });
171
- }
172
- showFieldDiff(label, oldVal, newVal) {
173
- if (!newVal || oldVal === newVal)
174
- return false;
175
- this.log(`\n ${label}:`);
176
- if (oldVal)
177
- this.log(` ${chalk.red(`- ${oldVal}`)}`);
178
- this.log(` ${chalk.green(`+ ${newVal}`)}`);
179
- return true;
180
- }
181
- stripHtml(html) {
182
- return html
183
- .replaceAll(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
184
- .replaceAll(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
185
- .replaceAll(/<[^>]+>/g, ' ')
186
- .replaceAll('&nbsp;', ' ')
187
- .replaceAll('&amp;', '&')
188
- .replaceAll('&lt;', '<')
189
- .replaceAll('&gt;', '>')
190
- .replaceAll(/\s+/g, ' ')
191
- .trim();
192
- }
193
- truncate(text, max) {
194
- return text.length > max ? `${text.slice(0, max)}…` : text;
195
- }
196
- showHtmlChange(oldHtml, newHtml) {
197
- if (!newHtml || oldHtml === newHtml)
198
- return false;
199
- this.log(`\n HTML Body:`);
200
- const MAX = 500;
201
- if (oldHtml) {
202
- const oldText = this.truncate(this.stripHtml(oldHtml), MAX);
203
- this.log(` ${chalk.red(`- ${oldText}`)}`);
204
- }
205
- const newText = this.truncate(this.stripHtml(newHtml), MAX);
206
- this.log(` ${chalk.green(`+ ${newText}`)}`);
207
- return true;
208
- }
209
- showUnchangedField(label, value) {
210
- if (!value)
211
- return;
212
- this.log(`\n ${label}:`);
213
- this.log(` ${chalk.dim(value)}`);
214
- }
215
- showUnchangedHtml(templateHtml) {
216
- if (!templateHtml)
217
- return;
218
- this.log(`\n HTML Body:`);
219
- this.log(` ${chalk.dim(this.truncate(this.stripHtml(templateHtml), 500))}`);
220
- }
221
- showSuggestedChanges(email, updated, templateHtml, changed) {
222
- this.log('\n Suggested Changes:');
223
- if (changed.subject)
224
- this.showFieldDiff('Subject', email.subject, updated.subject);
225
- if (changed.preview)
226
- this.showFieldDiff('Preview Text', email.previewText, updated.previewText);
227
- if (changed.html)
228
- this.showHtmlChange(templateHtml, updated.html);
229
- if (changed.cta)
230
- this.showFieldDiff('CTA Text', undefined, updated.ctaText);
231
- if (!changed.subject && !changed.preview && !changed.html && !changed.cta) {
232
- this.log(`\n ${chalk.dim('No changes detected.')}`);
233
- }
234
- }
235
- showUnchanged(email, templateHtml, changed) {
236
- const hasContent = !changed.subject ||
237
- (!changed.preview && Boolean(email.previewText)) ||
238
- (!changed.html && Boolean(templateHtml));
239
- if (!hasContent)
240
- return;
241
- this.log('\n Unchanged:');
242
- if (!changed.subject)
243
- this.showUnchangedField('Subject', email.subject);
244
- if (!changed.preview)
245
- this.showUnchangedField('Preview Text', email.previewText);
246
- if (!changed.html)
247
- this.showUnchangedHtml(templateHtml);
248
- }
249
- buildDiffPreview(email, updated, templateHtml) {
250
- const subjectChanged = Boolean(updated.subject) && updated.subject !== email.subject;
251
- const previewChanged = Boolean(updated.previewText) && updated.previewText !== email.previewText;
252
- const htmlChanged = Boolean(updated.html) && updated.html !== templateHtml;
253
- const diff = {};
254
- diff.subject = subjectChanged
255
- ? { new: updated.subject, old: email.subject }
256
- : { unchanged: true, value: email.subject };
257
- if (email.previewText ?? updated.previewText) {
258
- diff.previewText = previewChanged
259
- ? { new: updated.previewText, old: email.previewText }
260
- : { unchanged: true, value: email.previewText };
261
- }
262
- if (templateHtml ?? updated.html) {
263
- const oldText = templateHtml
264
- ? this.truncate(this.stripHtml(templateHtml), 500)
265
- : null;
266
- const newText = updated.html
267
- ? this.truncate(this.stripHtml(updated.html), 500)
268
- : null;
269
- diff.html = htmlChanged
270
- ? { new: newText, old: oldText }
271
- : { unchanged: true, value: oldText };
272
- }
273
- if (updated.ctaText) {
274
- diff.ctaText = { new: updated.ctaText };
275
- }
276
- return { diff };
277
68
  }
278
- showChangeSummary(email, updated, templateHtml) {
279
- const changed = {
280
- cta: Boolean(updated.ctaText),
281
- html: Boolean(updated.html) && updated.html !== templateHtml,
282
- preview: Boolean(updated.previewText) &&
283
- updated.previewText !== email.previewText,
284
- subject: Boolean(updated.subject) && updated.subject !== email.subject,
69
+ makeRegenCtx() {
70
+ return {
71
+ error: (msg) => this.error(msg),
72
+ exit: (code) => this.exit(code),
73
+ log: (msg) => this.log(msg),
74
+ onApiError: (r) => this.handleApiError(r),
75
+ post: (path, body) => this.apiClient.post(path, body),
76
+ spinner: (text, json, work) => this.withApiSpinner({ json, text }, work),
77
+ syncYaml: () => this.syncYamlToServer(),
285
78
  };
286
- this.showSuggestedChanges(email, updated, templateHtml, changed);
287
- this.showUnchanged(email, templateHtml, changed);
288
79
  }
289
80
  }
@@ -7,6 +7,5 @@ export default class Emails extends BaseCommand {
7
7
  yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
8
  };
9
9
  run(): Promise<void>;
10
- private openTemplateInEditor;
11
- private trySpawnEditor;
10
+ private makeCtx;
12
11
  }
@@ -1,9 +1,7 @@
1
- import { spawn } from 'node:child_process';
2
- import { join } from 'node:path';
3
1
  import { confirm, input } from '@inquirer/prompts';
4
- import chalk from 'chalk';
5
- import open from 'open';
6
2
  import { BaseCommand } from '../../lib/base-command.js';
3
+ import { openTemplateInEditor } from '../../lib/commands/emails/editor.js';
4
+ import { renderEmailDetail, renderEmailTable, } from '../../lib/commands/emails/output.js';
7
5
  export default class Emails extends BaseCommand {
8
6
  static description = 'List and view configured email sequences';
9
7
  static examples = [
@@ -17,100 +15,37 @@ export default class Emails extends BaseCommand {
17
15
  const { flags } = await this.parse(Emails);
18
16
  const yamlConfig = await this.ensureYaml();
19
17
  const { emails } = yamlConfig;
18
+ const ctx = this.makeCtx();
20
19
  if (flags.json) {
21
20
  this.log(JSON.stringify({ emails, total: emails.length }, null, 2));
22
21
  return;
23
22
  }
24
- this.log(`\n ${chalk.bold(String(emails.length))} emails configured in mailmodo.yaml:\n`);
25
- const idColW = Math.max(...emails.map((e) => e.id.length), 'ID'.length) + 2;
26
- const triggerColW = Math.max(...emails.map((e) => e.trigger.length), 'Trigger'.length) + 2;
27
- const delayColW = Math.max(...emails.map((e) => String(e.delay).length), 'Delay'.length) +
28
- 2;
29
- const hasConditions = emails.some((e) => e.condition);
30
- this.log(` ${chalk.bold('ID'.padEnd(idColW))}${chalk.bold('Trigger'.padEnd(triggerColW))}${chalk.bold('Delay'.padEnd(delayColW))}${hasConditions ? chalk.bold('Condition') : ''}`);
31
- this.log(` ${'─'.repeat(idColW + triggerColW + delayColW + (hasConditions ? 'Condition'.length : 0))}`);
32
- for (const email of emails) {
33
- const id = chalk.cyan(email.id.padEnd(idColW));
34
- const trigger = email.trigger.padEnd(triggerColW);
35
- const delay = String(email.delay).padEnd(delayColW);
36
- const condition = email.condition ? chalk.dim(email.condition) : '';
37
- this.log(` ${id}${trigger}${delay}${condition}`);
38
- }
39
- this.log('');
40
- if (!flags.yes) {
41
- const templateId = await input({
42
- default: 'n',
43
- message: "View an email? (id or 'n'):",
44
- });
45
- if (templateId !== 'n') {
46
- const email = emails.find((e) => e.id === templateId);
47
- if (!email) {
48
- this.log(`\n Template '${templateId}' not found.\n`);
49
- return;
50
- }
51
- this.log('');
52
- this.log(` ${chalk.bold('ID:')} ${email.id}`);
53
- this.log(` ${chalk.bold('Trigger:')} ${email.trigger}`);
54
- this.log(` ${chalk.bold('Delay:')} ${email.delay === 0 || email.delay === '0' ? '0 (immediate)' : email.delay}`);
55
- this.log(` ${chalk.bold('Subject:')} ${email.subject}`);
56
- this.log(` ${chalk.bold('Template:')} ${email.template}`);
57
- if (email.style) {
58
- this.log(` ${chalk.bold('Style:')} ${email.style}`);
59
- }
60
- if (email.condition) {
61
- this.log(` ${chalk.bold('Condition:')} ${email.condition}`);
62
- }
63
- if (email.goal) {
64
- this.log(` ${chalk.bold('Goal:')} ${email.goal}`);
65
- }
66
- this.log('');
67
- const openIt = await confirm({
68
- default: true,
69
- message: 'Open template in editor?',
70
- });
71
- if (openIt) {
72
- await this.openTemplateInEditor(email.template);
73
- }
74
- }
75
- }
76
- }
77
- async openTemplateInEditor(template) {
78
- const templatePath = join(process.cwd(), template);
79
- this.log(`\n Opening ${template}...\n`);
80
- const editor = process.env.VISUAL || process.env.EDITOR;
81
- if (editor) {
82
- const [cmd, ...editorArgs] = editor.trim().split(/\s+/);
83
- const launched = await new Promise((resolve) => {
84
- const child = spawn(cmd, [...editorArgs, templatePath], {
85
- stdio: 'inherit',
86
- });
87
- child.on('error', () => resolve(false));
88
- child.on('close', () => resolve(true));
89
- });
90
- if (launched)
91
- return;
92
- }
93
- if (await this.trySpawnEditor('code', templatePath))
23
+ renderEmailTable(ctx, emails);
24
+ if (flags.yes)
25
+ return;
26
+ const templateId = await input({
27
+ default: 'n',
28
+ message: "View an email? (id or 'n'):",
29
+ });
30
+ if (templateId === 'n')
31
+ return;
32
+ const email = emails.find((e) => e.id === templateId);
33
+ if (!email) {
34
+ this.log(`\n Template '${templateId}' not found.\n`);
94
35
  return;
95
- try {
96
- await open(templatePath);
97
36
  }
98
- catch {
99
- this.log(` ${chalk.dim(`Could not open editor. Open the file manually: ${templatePath}`)}`);
37
+ renderEmailDetail(ctx, email);
38
+ const openIt = await confirm({
39
+ default: true,
40
+ message: 'Open template in editor?',
41
+ });
42
+ if (openIt) {
43
+ await openTemplateInEditor(ctx, email.template);
100
44
  }
101
45
  }
102
- trySpawnEditor(editor, filePath) {
103
- const [cmd, args] = process.platform === 'win32'
104
- ? ['cmd.exe', ['/c', editor, filePath]]
105
- : [editor, [filePath]];
106
- return new Promise((resolve) => {
107
- const child = spawn(cmd, [...args], { stdio: 'ignore' });
108
- child.on('error', () => {
109
- resolve(false);
110
- });
111
- child.on('close', (code) => {
112
- resolve(code === 0);
113
- });
114
- });
46
+ makeCtx() {
47
+ return {
48
+ log: (msg) => this.log(msg),
49
+ };
115
50
  }
116
51
  }
@@ -8,6 +8,5 @@ export default class Init extends BaseCommand {
8
8
  yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
9
  };
10
10
  run(): Promise<void>;
11
- private persistMonthlyCap;
12
- private confirmOverwriteIfNeeded;
11
+ private makeCtx;
13
12
  }