@mailmodo/cli 0.0.55 → 0.0.56-beta.pr58.100

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 -184
  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.js +12 -7
  6. package/dist/commands/deployments/index.d.ts +1 -4
  7. package/dist/commands/deployments/index.js +11 -52
  8. package/dist/commands/domain/index.d.ts +1 -14
  9. package/dist/commands/domain/index.js +19 -100
  10. package/dist/commands/edit/index.d.ts +2 -20
  11. package/dist/commands/edit/index.js +33 -258
  12. package/dist/commands/emails/index.d.ts +1 -2
  13. package/dist/commands/emails/index.js +26 -91
  14. package/dist/commands/init/index.d.ts +1 -3
  15. package/dist/commands/init/index.js +47 -199
  16. package/dist/commands/login/index.d.ts +2 -0
  17. package/dist/commands/login/index.js +32 -79
  18. package/dist/commands/logs/index.d.ts +1 -8
  19. package/dist/commands/logs/index.js +12 -55
  20. package/dist/commands/preview/index.d.ts +1 -19
  21. package/dist/commands/preview/index.js +32 -212
  22. package/dist/commands/sdk/index.d.ts +1 -3
  23. package/dist/commands/sdk/index.js +14 -46
  24. package/dist/commands/settings/index.d.ts +1 -22
  25. package/dist/commands/settings/index.js +34 -246
  26. package/dist/commands/status/index.d.ts +1 -0
  27. package/dist/commands/status/index.js +13 -39
  28. package/dist/lib/base-command.d.ts +35 -10
  29. package/dist/lib/base-command.js +169 -17
  30. package/dist/lib/commands/billing/checkout-status.d.ts +3 -0
  31. package/dist/lib/commands/billing/checkout-status.js +63 -0
  32. package/dist/lib/commands/billing/format.d.ts +7 -0
  33. package/dist/lib/commands/billing/format.js +63 -0
  34. package/dist/lib/commands/billing/purchase-cap.d.ts +7 -0
  35. package/dist/lib/commands/billing/purchase-cap.js +57 -0
  36. package/dist/lib/commands/billing/types.d.ts +72 -0
  37. package/dist/lib/commands/contacts/actions.d.ts +3 -0
  38. package/dist/lib/commands/contacts/actions.js +49 -0
  39. package/dist/lib/commands/contacts/export-delete.d.ts +9 -0
  40. package/dist/lib/commands/contacts/export-delete.js +51 -0
  41. package/dist/lib/commands/contacts/types.d.ts +35 -0
  42. package/dist/lib/commands/contacts/types.js +1 -0
  43. package/dist/lib/{deploy → commands/deploy}/domain-setup.d.ts +1 -1
  44. package/dist/lib/{deploy → commands/deploy}/domain-setup.js +2 -2
  45. package/dist/lib/{deploy → commands/deploy}/output.d.ts +1 -1
  46. package/dist/lib/{deploy → commands/deploy}/output.js +2 -2
  47. package/dist/lib/{deploy → commands/deploy}/payload.d.ts +1 -1
  48. package/dist/lib/{deploy → commands/deploy}/payload.js +2 -2
  49. package/dist/lib/{deploy → commands/deploy}/sequence-status.js +2 -2
  50. package/dist/lib/{deploy → commands/deploy}/types.d.ts +4 -4
  51. package/dist/lib/commands/deploy/types.js +1 -0
  52. package/dist/lib/commands/deployments/output.d.ts +2 -0
  53. package/dist/lib/commands/deployments/output.js +68 -0
  54. package/dist/lib/commands/deployments/types.d.ts +24 -0
  55. package/dist/lib/commands/deployments/types.js +1 -0
  56. package/dist/lib/commands/domain/setup.d.ts +8 -0
  57. package/dist/lib/commands/domain/setup.js +53 -0
  58. package/dist/lib/commands/domain/types.d.ts +56 -0
  59. package/dist/lib/commands/domain/types.js +1 -0
  60. package/dist/lib/commands/domain/verify.d.ts +5 -0
  61. package/dist/lib/commands/domain/verify.js +50 -0
  62. package/dist/lib/commands/edit/diff.d.ts +7 -0
  63. package/dist/lib/commands/edit/diff.js +65 -0
  64. package/dist/lib/commands/edit/display.d.ts +5 -0
  65. package/dist/lib/commands/edit/display.js +53 -0
  66. package/dist/lib/commands/edit/flow.d.ts +8 -0
  67. package/dist/lib/commands/edit/flow.js +70 -0
  68. package/dist/lib/commands/edit/persist.d.ts +5 -0
  69. package/dist/lib/commands/edit/persist.js +67 -0
  70. package/dist/lib/commands/edit/types.d.ts +38 -0
  71. package/dist/lib/commands/edit/types.js +1 -0
  72. package/dist/lib/commands/emails/editor.d.ts +2 -0
  73. package/dist/lib/commands/emails/editor.js +43 -0
  74. package/dist/lib/commands/emails/output.d.ts +4 -0
  75. package/dist/lib/commands/emails/output.js +36 -0
  76. package/dist/lib/commands/emails/types.d.ts +3 -0
  77. package/dist/lib/commands/emails/types.js +1 -0
  78. package/dist/lib/commands/init/analysis.d.ts +3 -0
  79. package/dist/lib/commands/init/analysis.js +73 -0
  80. package/dist/lib/commands/init/output.d.ts +12 -0
  81. package/dist/lib/commands/init/output.js +39 -0
  82. package/dist/lib/commands/init/payload.d.ts +8 -0
  83. package/dist/lib/commands/init/payload.js +78 -0
  84. package/dist/lib/commands/init/types.d.ts +57 -0
  85. package/dist/lib/commands/init/types.js +1 -0
  86. package/dist/lib/commands/login/output.d.ts +8 -0
  87. package/dist/lib/commands/login/output.js +40 -0
  88. package/dist/lib/commands/login/types.d.ts +19 -0
  89. package/dist/lib/commands/login/types.js +1 -0
  90. package/dist/lib/commands/logs/output.d.ts +2 -0
  91. package/dist/lib/commands/logs/output.js +52 -0
  92. package/dist/lib/commands/logs/types.d.ts +23 -0
  93. package/dist/lib/commands/logs/types.js +1 -0
  94. package/dist/lib/commands/preview/actions.d.ts +11 -0
  95. package/dist/lib/commands/preview/actions.js +43 -0
  96. package/dist/lib/commands/preview/render.d.ts +3 -0
  97. package/dist/lib/commands/preview/render.js +30 -0
  98. package/dist/lib/commands/preview/server.d.ts +8 -0
  99. package/dist/lib/commands/preview/server.js +63 -0
  100. package/dist/lib/commands/preview/types.d.ts +22 -0
  101. package/dist/lib/commands/preview/types.js +1 -0
  102. package/dist/lib/commands/preview/wrapper-html.d.ts +2 -0
  103. package/dist/lib/commands/preview/wrapper-html.js +35 -0
  104. package/dist/lib/commands/sdk/output.d.ts +2 -0
  105. package/dist/lib/commands/sdk/output.js +42 -0
  106. package/dist/lib/commands/sdk/types.d.ts +21 -0
  107. package/dist/lib/commands/sdk/types.js +1 -0
  108. package/dist/lib/commands/settings/actions.d.ts +10 -0
  109. package/dist/lib/commands/settings/actions.js +56 -0
  110. package/dist/lib/commands/settings/display.d.ts +15 -0
  111. package/dist/lib/commands/settings/display.js +69 -0
  112. package/dist/lib/commands/settings/logo-domain.d.ts +3 -0
  113. package/dist/lib/commands/settings/logo-domain.js +47 -0
  114. package/dist/lib/commands/settings/prompt.d.ts +2 -0
  115. package/dist/lib/commands/settings/prompt.js +82 -0
  116. package/dist/lib/commands/settings/types.d.ts +65 -0
  117. package/dist/lib/commands/settings/types.js +1 -0
  118. package/dist/lib/commands/status/output.d.ts +2 -0
  119. package/dist/lib/commands/status/output.js +49 -0
  120. package/dist/lib/commands/status/types.d.ts +28 -0
  121. package/dist/lib/commands/status/types.js +1 -0
  122. package/dist/lib/constants.d.ts +3 -2
  123. package/dist/lib/constants.js +4 -5
  124. package/dist/lib/messages.d.ts +11 -0
  125. package/dist/lib/messages.js +31 -0
  126. package/dist/lib/templates/missing-templates.d.ts +16 -2
  127. package/dist/lib/templates/missing-templates.js +34 -22
  128. package/dist/lib/templates/regenerate.d.ts +10 -0
  129. package/dist/lib/templates/regenerate.js +29 -0
  130. package/dist/lib/templates/sync.d.ts +33 -0
  131. package/dist/lib/templates/sync.js +106 -0
  132. package/dist/lib/templates/types.d.ts +3 -0
  133. package/oclif.manifest.json +54 -54
  134. package/package.json +1 -1
  135. /package/dist/lib/{deploy → commands/billing}/types.js +0 -0
  136. /package/dist/lib/{deploy → commands/deploy}/sequence-status.d.ts +0 -0
@@ -1,5 +1,19 @@
1
1
  import { type MailmodoYaml } from '../yaml-config.js';
2
- import type { DeployFlags } from '../deploy/types.js';
2
+ import type { DeployFlags } from '../commands/deploy/types.js';
3
3
  import type { RegenCtx } from './types.js';
4
+ /**
5
+ * Returns the IDs of emails whose template file does not exist on disk.
6
+ * Resolves the correct filename for each email by accounting for per-email
7
+ * and project-level style settings (branded vs. plain).
8
+ */
4
9
  export declare function getMissingTemplateIds(yamlConfig: MailmodoYaml): string[];
5
- export declare function handleMissingTemplates(ctx: RegenCtx, yamlConfig: MailmodoYaml, missingIds: string[], flags: DeployFlags): Promise<boolean>;
10
+ /**
11
+ * Handles the case where one or more template files are missing before a
12
+ * command can proceed. First attempts a silent server restore for all missing
13
+ * IDs; if the server has backups for all of them the command continues without
14
+ * any user interaction. Only falls through to an interactive prompt (or an
15
+ * immediate error in --json / --yes mode) for files the server also does not have.
16
+ * Returns true if the situation was resolved (restored or regenerated), false
17
+ * if the user chose to abort.
18
+ */
19
+ export declare function handleMissingTemplates(ctx: RegenCtx, yamlConfig: MailmodoYaml, missingIds: string[], flags: DeployFlags): Promise<'regenerated' | 'restored' | false>;
@@ -2,44 +2,56 @@ import { existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { select } from '@inquirer/prompts';
4
4
  import chalk from 'chalk';
5
- import { API_ENDPOINTS, TEMPLATES_DIR } from '../constants.js';
5
+ import { TEMPLATES_DIR } from '../constants.js';
6
6
  import { MISSING_TEMPLATES } from '../messages.js';
7
- import { getTemplateFilename, saveTemplate, } from '../yaml-config.js';
8
- import { buildRegeneratePayload } from '../deploy/payload.js';
7
+ import { getTemplateFilename } from '../yaml-config.js';
8
+ import { regenerateMissingTemplates } from './regenerate.js';
9
+ /**
10
+ * Returns the IDs of emails whose template file does not exist on disk.
11
+ * Resolves the correct filename for each email by accounting for per-email
12
+ * and project-level style settings (branded vs. plain).
13
+ */
9
14
  export function getMissingTemplateIds(yamlConfig) {
10
15
  return yamlConfig.emails
11
16
  .filter((e) => !existsSync(join(process.cwd(), TEMPLATES_DIR, getTemplateFilename(e.id, e.style, yamlConfig.project?.emailStyle))))
12
17
  .map((e) => e.id);
13
18
  }
14
- async function regenerateMissingTemplates(ctx, yamlConfig, missingIds, flags) {
15
- const response = await ctx.spinner(` ${MISSING_TEMPLATES.REGENERATE_SPINNER}`, flags.json, () => ctx.post(API_ENDPOINTS.GENERATE, buildRegeneratePayload(yamlConfig, missingIds)));
16
- if (!response.ok)
17
- ctx.onApiError(response);
18
- const saves = [];
19
- for (const email of response.data?.emails ?? []) {
20
- if (!/^[\w-]+$/.test(email.id))
21
- continue;
22
- if (email.html)
23
- saves.push(saveTemplate(`${email.id}.html`, email.html));
24
- if (email.plainHtml)
25
- saves.push(saveTemplate(`${email.id}_plain.html`, email.plainHtml));
26
- }
27
- await Promise.all(saves);
28
- await ctx.syncYaml();
19
+ /**
20
+ * Attempts to restore each missing template from the server without showing
21
+ * any prompt or message to the user. Returns the IDs that could not be
22
+ * restored — either because the server has no backup or a network error occurred.
23
+ */
24
+ async function silentlyRestoreFromServer(ctx, missingIds) {
25
+ const results = await Promise.all(missingIds.map((id) => ctx.fetchTemplate(id)));
26
+ // Keep only the IDs where the server fetch returned false (not restored)
27
+ return missingIds.filter((_, i) => !results[i]);
29
28
  }
29
+ /**
30
+ * Handles the case where one or more template files are missing before a
31
+ * command can proceed. First attempts a silent server restore for all missing
32
+ * IDs; if the server has backups for all of them the command continues without
33
+ * any user interaction. Only falls through to an interactive prompt (or an
34
+ * immediate error in --json / --yes mode) for files the server also does not have.
35
+ * Returns true if the situation was resolved (restored or regenerated), false
36
+ * if the user chose to abort.
37
+ */
30
38
  export async function handleMissingTemplates(ctx, yamlConfig, missingIds, flags) {
39
+ // Try to silently recover from server before interrupting the user
40
+ const stillMissing = await silentlyRestoreFromServer(ctx, missingIds);
41
+ if (stillMissing.length === 0)
42
+ return 'restored';
31
43
  if (flags.json) {
32
44
  ctx.log(JSON.stringify({
33
45
  error: 'missing_templates',
34
46
  message: MISSING_TEMPLATES.YES_ERROR,
35
- missingTemplates: missingIds.map((id) => `${id}.html`),
47
+ missingTemplates: stillMissing.map((id) => `${id}.html`),
36
48
  }, null, 2));
37
49
  ctx.exit(1);
38
50
  }
39
51
  if (flags.yes)
40
52
  ctx.error(MISSING_TEMPLATES.YES_ERROR);
41
53
  ctx.log(`\n ${MISSING_TEMPLATES.HEADER}`);
42
- for (const id of missingIds)
54
+ for (const id of stillMissing)
43
55
  ctx.log(` ${chalk.red('✗')} mailmodo/${id}.html`);
44
56
  ctx.log(`\n ${MISSING_TEMPLATES.REGENERATE_NOTE}\n`);
45
57
  const action = await select({
@@ -56,6 +68,6 @@ export async function handleMissingTemplates(ctx, yamlConfig, missingIds, flags)
56
68
  ctx.log(`\n ${MISSING_TEMPLATES.ABORT_HINT}\n`);
57
69
  return false;
58
70
  }
59
- await regenerateMissingTemplates(ctx, yamlConfig, missingIds, flags);
60
- return true;
71
+ await regenerateMissingTemplates(ctx, yamlConfig, stillMissing, flags);
72
+ return 'regenerated';
61
73
  }
@@ -0,0 +1,10 @@
1
+ import { type MailmodoYaml } from '../yaml-config.js';
2
+ import type { DeployFlags } from '../commands/deploy/types.js';
3
+ import type { RegenCtx } from './types.js';
4
+ /**
5
+ * Calls POST /email/generate with the missing email IDs, writes the returned
6
+ * HTML files to disk, then syncs both the YAML and all templates to the server.
7
+ * Called when the user chooses "Re-generate via AI" from the missing-template
8
+ * prompt, or when no server backup exists to restore from.
9
+ */
10
+ export declare function regenerateMissingTemplates(ctx: RegenCtx, yamlConfig: MailmodoYaml, missingIds: string[], flags: DeployFlags): Promise<void>;
@@ -0,0 +1,29 @@
1
+ import { API_ENDPOINTS } from '../constants.js';
2
+ import { saveTemplate } from '../yaml-config.js';
3
+ import { buildRegeneratePayload } from '../commands/deploy/payload.js';
4
+ import { MISSING_TEMPLATES } from '../messages.js';
5
+ /**
6
+ * Calls POST /email/generate with the missing email IDs, writes the returned
7
+ * HTML files to disk, then syncs both the YAML and all templates to the server.
8
+ * Called when the user chooses "Re-generate via AI" from the missing-template
9
+ * prompt, or when no server backup exists to restore from.
10
+ */
11
+ export async function regenerateMissingTemplates(ctx, yamlConfig, missingIds, flags) {
12
+ const response = await ctx.spinner(` ${MISSING_TEMPLATES.REGENERATE_SPINNER}`, flags.json, () => ctx.post(API_ENDPOINTS.GENERATE, buildRegeneratePayload(yamlConfig, missingIds)));
13
+ if (!response.ok)
14
+ ctx.onApiError(response);
15
+ const saves = [];
16
+ for (const email of response.data?.emails ?? []) {
17
+ // Guard against path traversal: only allow alphanumeric, dash, and underscore IDs
18
+ if (!/^[\w-]+$/.test(email.id))
19
+ continue;
20
+ if (email.html)
21
+ saves.push(saveTemplate(`${email.id}.html`, email.html));
22
+ if (email.plainHtml)
23
+ saves.push(saveTemplate(`${email.id}_plain.html`, email.plainHtml));
24
+ }
25
+ await Promise.all(saves);
26
+ await ctx.syncYaml();
27
+ // Back up the freshly generated templates so the server reflects the new state
28
+ await ctx.syncTemplates(yamlConfig);
29
+ }
@@ -0,0 +1,33 @@
1
+ import type { ApiClient } from '../api-client.js';
2
+ import { type MailmodoYaml } from '../yaml-config.js';
3
+ /**
4
+ * Bulk-uploads all template HTML files referenced in the YAML to the server
5
+ * as a backup. Only files present on disk are sent; any extra files in the
6
+ * mailmodo/ folder that are not listed in the YAML are intentionally skipped.
7
+ * All files are read in parallel before building the multipart payload.
8
+ */
9
+ export declare function syncTemplatesToServer(client: ApiClient, yaml: MailmodoYaml): Promise<void>;
10
+ /**
11
+ * Uploads a single template's HTML files to the server for incremental sync.
12
+ * Used by the edit command after a change so only the modified template is
13
+ * re-uploaded instead of the full set. The branded HTML (`html` field) is
14
+ * required by the API; the plain variant (`plainHtml`) is uploaded only if
15
+ * its file exists on disk.
16
+ */
17
+ export declare function syncTemplateToServer(client: ApiClient, emailId: string): Promise<void>;
18
+ /**
19
+ * Downloads all backed-up templates from the server and writes them to the
20
+ * local mailmodo/ folder. Only templates whose emailId appears in the YAML
21
+ * are written to disk — extra templates stored on the server (e.g. from a
22
+ * previous project state) are intentionally ignored to keep the local folder
23
+ * in sync with the current YAML.
24
+ * Returns true if at least one file was written, false otherwise.
25
+ */
26
+ export declare function fetchTemplatesFromServer(client: ApiClient, yaml: MailmodoYaml): Promise<boolean>;
27
+ /**
28
+ * Downloads a single backed-up template from the server by emailId and writes
29
+ * it to the local mailmodo/ folder. Returns true if the template was found and
30
+ * written successfully, false if it has not been backed up or on any error.
31
+ * The plain HTML variant is only written if the server returned a non-empty value.
32
+ */
33
+ export declare function fetchTemplateFromServer(client: ApiClient, emailId: string): Promise<boolean>;
@@ -0,0 +1,106 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { API_ENDPOINTS, TEMPLATES_DIR } from '../constants.js';
5
+ import { saveTemplate } from '../yaml-config.js';
6
+ /**
7
+ * Bulk-uploads all template HTML files referenced in the YAML to the server
8
+ * as a backup. Only files present on disk are sent; any extra files in the
9
+ * mailmodo/ folder that are not listed in the YAML are intentionally skipped.
10
+ * All files are read in parallel before building the multipart payload.
11
+ */
12
+ export async function syncTemplatesToServer(client, yaml) {
13
+ // Collect filenames for both branded and plain variants, filtered to those that exist on disk
14
+ const filenames = yaml.emails
15
+ .flatMap((email) => [`${email.id}.html`, `${email.id}_plain.html`])
16
+ .filter((f) => existsSync(join(process.cwd(), TEMPLATES_DIR, f)));
17
+ if (filenames.length === 0)
18
+ return;
19
+ // Read all files in parallel to avoid sequential await-in-loop
20
+ const files = await Promise.all(filenames.map(async (f) => ({
21
+ content: await readFile(join(process.cwd(), TEMPLATES_DIR, f), 'utf8'),
22
+ name: f,
23
+ })));
24
+ // API expects multipart fields keyed by the template filename (e.g. abc123.html)
25
+ const formData = new FormData();
26
+ for (const { name, content } of files) {
27
+ formData.append(name, new Blob([content], { type: 'text/html' }), name);
28
+ }
29
+ await client.postFormData(API_ENDPOINTS.ASSETS_TEMPLATES, formData);
30
+ }
31
+ /**
32
+ * Uploads a single template's HTML files to the server for incremental sync.
33
+ * Used by the edit command after a change so only the modified template is
34
+ * re-uploaded instead of the full set. The branded HTML (`html` field) is
35
+ * required by the API; the plain variant (`plainHtml`) is uploaded only if
36
+ * its file exists on disk.
37
+ */
38
+ export async function syncTemplateToServer(client, emailId) {
39
+ const htmlPath = join(process.cwd(), TEMPLATES_DIR, `${emailId}.html`);
40
+ // API requires the branded HTML field; skip entirely if the file is absent
41
+ if (!existsSync(htmlPath))
42
+ return;
43
+ const formData = new FormData();
44
+ const html = await readFile(htmlPath, 'utf8');
45
+ formData.append('html', new Blob([html], { type: 'text/html' }), `${emailId}.html`);
46
+ const plainPath = join(process.cwd(), TEMPLATES_DIR, `${emailId}_plain.html`);
47
+ if (existsSync(plainPath)) {
48
+ const plainHtml = await readFile(plainPath, 'utf8');
49
+ formData.append('plainHtml', new Blob([plainHtml], { type: 'text/html' }), `${emailId}_plain.html`);
50
+ }
51
+ await client.postFormData(`${API_ENDPOINTS.ASSETS_TEMPLATES}/${emailId}`, formData);
52
+ }
53
+ /**
54
+ * Downloads all backed-up templates from the server and writes them to the
55
+ * local mailmodo/ folder. Only templates whose emailId appears in the YAML
56
+ * are written to disk — extra templates stored on the server (e.g. from a
57
+ * previous project state) are intentionally ignored to keep the local folder
58
+ * in sync with the current YAML.
59
+ * Returns true if at least one file was written, false otherwise.
60
+ */
61
+ export async function fetchTemplatesFromServer(client, yaml) {
62
+ try {
63
+ const resp = await client.get(API_ENDPOINTS.ASSETS_TEMPLATES);
64
+ if (!resp.ok || !resp.data?.templates?.length)
65
+ return false;
66
+ // Build a set of valid IDs from the YAML to filter out stale server entries
67
+ const validIds = new Set(yaml.emails.map((e) => e.id));
68
+ const saves = [];
69
+ for (const t of resp.data.templates) {
70
+ if (!validIds.has(t.emailId))
71
+ continue;
72
+ // The API returns an empty string when a variant was never stored; skip those
73
+ if (t.html)
74
+ saves.push(saveTemplate(`${t.emailId}.html`, t.html));
75
+ if (t.plainHtml)
76
+ saves.push(saveTemplate(`${t.emailId}_plain.html`, t.plainHtml));
77
+ }
78
+ await Promise.all(saves);
79
+ return saves.length > 0;
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }
85
+ /**
86
+ * Downloads a single backed-up template from the server by emailId and writes
87
+ * it to the local mailmodo/ folder. Returns true if the template was found and
88
+ * written successfully, false if it has not been backed up or on any error.
89
+ * The plain HTML variant is only written if the server returned a non-empty value.
90
+ */
91
+ export async function fetchTemplateFromServer(client, emailId) {
92
+ try {
93
+ const resp = await client.get(`${API_ENDPOINTS.ASSETS_TEMPLATES}/${emailId}`);
94
+ // A missing or empty html field means the template was never backed up
95
+ if (!resp.ok || !resp.data?.html)
96
+ return false;
97
+ await saveTemplate(`${emailId}.html`, resp.data.html);
98
+ // plainHtml is empty string when no plain variant is stored; skip in that case
99
+ if (resp.data.plainHtml)
100
+ await saveTemplate(`${emailId}_plain.html`, resp.data.plainHtml);
101
+ return true;
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
@@ -1,7 +1,9 @@
1
1
  import type { ApiResponse } from '../api-client.js';
2
+ import type { MailmodoYaml } from '../yaml-config.js';
2
3
  export type RegenCtx = {
3
4
  error(msg: string): never;
4
5
  exit(code?: number): never;
6
+ fetchTemplate(emailId: string): Promise<boolean>;
5
7
  log(msg?: string): void;
6
8
  onApiError(resp: {
7
9
  error?: string;
@@ -9,5 +11,6 @@ export type RegenCtx = {
9
11
  }): never;
10
12
  post<T = Record<string, unknown>>(path: string, body?: Record<string, unknown> | unknown): Promise<ApiResponse<T>>;
11
13
  spinner<T>(text: string, json: boolean, work: () => Promise<T>): Promise<T>;
14
+ syncTemplates(yaml: MailmodoYaml): Promise<void>;
12
15
  syncYaml(): Promise<void>;
13
16
  };
@@ -573,14 +573,19 @@
573
573
  "index.js"
574
574
  ]
575
575
  },
576
- "settings": {
576
+ "preview": {
577
577
  "aliases": [],
578
- "args": {},
579
- "description": "View and update project settings",
578
+ "args": {
579
+ "id": {
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",
580
585
  "examples": [
581
- "<%= config.bin %> settings",
582
- "<%= config.bin %> settings --set brand_color=#0F3460",
583
- "<%= config.bin %> settings --json"
586
+ "<%= config.bin %> preview welcome",
587
+ "<%= config.bin %> preview welcome --text",
588
+ "<%= config.bin %> preview welcome --send me@example.com"
584
589
  ],
585
590
  "flags": {
586
591
  "json": {
@@ -596,17 +601,23 @@
596
601
  "allowNo": false,
597
602
  "type": "boolean"
598
603
  },
599
- "set": {
600
- "description": "Set a setting (format: key=value)",
601
- "name": "set",
604
+ "send": {
605
+ "description": "Send test email to this address",
606
+ "name": "send",
602
607
  "hasDynamicHelp": false,
603
608
  "multiple": false,
604
609
  "type": "option"
610
+ },
611
+ "text": {
612
+ "description": "Output plain text version (for AI agents)",
613
+ "name": "text",
614
+ "allowNo": false,
615
+ "type": "boolean"
605
616
  }
606
617
  },
607
618
  "hasDynamicHelp": false,
608
619
  "hiddenAliases": [],
609
- "id": "settings",
620
+ "id": "preview",
610
621
  "pluginAlias": "@mailmodo/cli",
611
622
  "pluginName": "@mailmodo/cli",
612
623
  "pluginType": "core",
@@ -616,17 +627,18 @@
616
627
  "relativePath": [
617
628
  "dist",
618
629
  "commands",
619
- "settings",
630
+ "preview",
620
631
  "index.js"
621
632
  ]
622
633
  },
623
- "status": {
634
+ "sdk": {
624
635
  "aliases": [],
625
636
  "args": {},
626
- "description": "View email performance metrics and quota usage",
637
+ "description": "Show the SDK track() / identify() reference for deployed sequences",
627
638
  "examples": [
628
- "<%= config.bin %> status",
629
- "<%= config.bin %> status --json"
639
+ "<%= config.bin %> sdk",
640
+ "<%= config.bin %> sdk --sequence-id a1b2c3d4",
641
+ "<%= config.bin %> sdk --json"
630
642
  ],
631
643
  "flags": {
632
644
  "json": {
@@ -641,11 +653,18 @@
641
653
  "name": "yes",
642
654
  "allowNo": false,
643
655
  "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"
644
663
  }
645
664
  },
646
665
  "hasDynamicHelp": false,
647
666
  "hiddenAliases": [],
648
- "id": "status",
667
+ "id": "sdk",
649
668
  "pluginAlias": "@mailmodo/cli",
650
669
  "pluginName": "@mailmodo/cli",
651
670
  "pluginType": "core",
@@ -655,23 +674,18 @@
655
674
  "relativePath": [
656
675
  "dist",
657
676
  "commands",
658
- "status",
677
+ "sdk",
659
678
  "index.js"
660
679
  ]
661
680
  },
662
- "preview": {
681
+ "settings": {
663
682
  "aliases": [],
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",
683
+ "args": {},
684
+ "description": "View and update project settings",
671
685
  "examples": [
672
- "<%= config.bin %> preview welcome",
673
- "<%= config.bin %> preview welcome --text",
674
- "<%= config.bin %> preview welcome --send me@example.com"
686
+ "<%= config.bin %> settings",
687
+ "<%= config.bin %> settings --set brand_color=#0F3460",
688
+ "<%= config.bin %> settings --json"
675
689
  ],
676
690
  "flags": {
677
691
  "json": {
@@ -687,23 +701,17 @@
687
701
  "allowNo": false,
688
702
  "type": "boolean"
689
703
  },
690
- "send": {
691
- "description": "Send test email to this address",
692
- "name": "send",
704
+ "set": {
705
+ "description": "Set a setting (format: key=value)",
706
+ "name": "set",
693
707
  "hasDynamicHelp": false,
694
708
  "multiple": false,
695
709
  "type": "option"
696
- },
697
- "text": {
698
- "description": "Output plain text version (for AI agents)",
699
- "name": "text",
700
- "allowNo": false,
701
- "type": "boolean"
702
710
  }
703
711
  },
704
712
  "hasDynamicHelp": false,
705
713
  "hiddenAliases": [],
706
- "id": "preview",
714
+ "id": "settings",
707
715
  "pluginAlias": "@mailmodo/cli",
708
716
  "pluginName": "@mailmodo/cli",
709
717
  "pluginType": "core",
@@ -713,18 +721,17 @@
713
721
  "relativePath": [
714
722
  "dist",
715
723
  "commands",
716
- "preview",
724
+ "settings",
717
725
  "index.js"
718
726
  ]
719
727
  },
720
- "sdk": {
728
+ "status": {
721
729
  "aliases": [],
722
730
  "args": {},
723
- "description": "Show the SDK track() / identify() reference for deployed sequences",
731
+ "description": "View email performance metrics and quota usage",
724
732
  "examples": [
725
- "<%= config.bin %> sdk",
726
- "<%= config.bin %> sdk --sequence-id a1b2c3d4",
727
- "<%= config.bin %> sdk --json"
733
+ "<%= config.bin %> status",
734
+ "<%= config.bin %> status --json"
728
735
  ],
729
736
  "flags": {
730
737
  "json": {
@@ -739,18 +746,11 @@
739
746
  "name": "yes",
740
747
  "allowNo": false,
741
748
  "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": "sdk",
753
+ "id": "status",
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
- "sdk",
763
+ "status",
764
764
  "index.js"
765
765
  ]
766
766
  }
767
767
  },
768
- "version": "0.0.55"
768
+ "version": "0.0.56-beta.pr58.100"
769
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.55",
4
+ "version": "0.0.56-beta.pr58.100",
5
5
  "author": "provishalk",
6
6
  "bin": {
7
7
  "mailmodo": "bin/run.js"
File without changes