@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.
- package/dist/commands/billing/index.d.ts +1 -11
- package/dist/commands/billing/index.js +28 -184
- package/dist/commands/contacts/index.d.ts +1 -19
- package/dist/commands/contacts/index.js +21 -114
- package/dist/commands/deploy/index.js +12 -7
- package/dist/commands/deployments/index.d.ts +1 -4
- package/dist/commands/deployments/index.js +11 -52
- package/dist/commands/domain/index.d.ts +1 -14
- package/dist/commands/domain/index.js +19 -100
- package/dist/commands/edit/index.d.ts +2 -20
- package/dist/commands/edit/index.js +33 -258
- package/dist/commands/emails/index.d.ts +1 -2
- package/dist/commands/emails/index.js +26 -91
- package/dist/commands/init/index.d.ts +1 -3
- package/dist/commands/init/index.js +47 -199
- package/dist/commands/login/index.d.ts +2 -0
- package/dist/commands/login/index.js +32 -79
- package/dist/commands/logs/index.d.ts +1 -8
- package/dist/commands/logs/index.js +12 -55
- package/dist/commands/preview/index.d.ts +1 -19
- package/dist/commands/preview/index.js +32 -212
- package/dist/commands/sdk/index.d.ts +1 -3
- package/dist/commands/sdk/index.js +14 -46
- package/dist/commands/settings/index.d.ts +1 -22
- package/dist/commands/settings/index.js +34 -246
- package/dist/commands/status/index.d.ts +1 -0
- package/dist/commands/status/index.js +13 -39
- package/dist/lib/base-command.d.ts +35 -10
- package/dist/lib/base-command.js +169 -17
- package/dist/lib/commands/billing/checkout-status.d.ts +3 -0
- package/dist/lib/commands/billing/checkout-status.js +63 -0
- package/dist/lib/commands/billing/format.d.ts +7 -0
- package/dist/lib/commands/billing/format.js +63 -0
- package/dist/lib/commands/billing/purchase-cap.d.ts +7 -0
- package/dist/lib/commands/billing/purchase-cap.js +57 -0
- package/dist/lib/commands/billing/types.d.ts +72 -0
- package/dist/lib/commands/contacts/actions.d.ts +3 -0
- package/dist/lib/commands/contacts/actions.js +49 -0
- package/dist/lib/commands/contacts/export-delete.d.ts +9 -0
- package/dist/lib/commands/contacts/export-delete.js +51 -0
- package/dist/lib/commands/contacts/types.d.ts +35 -0
- package/dist/lib/commands/contacts/types.js +1 -0
- package/dist/lib/{deploy → commands/deploy}/domain-setup.d.ts +1 -1
- package/dist/lib/{deploy → commands/deploy}/domain-setup.js +2 -2
- package/dist/lib/{deploy → commands/deploy}/output.d.ts +1 -1
- package/dist/lib/{deploy → commands/deploy}/output.js +2 -2
- package/dist/lib/{deploy → commands/deploy}/payload.d.ts +1 -1
- package/dist/lib/{deploy → commands/deploy}/payload.js +2 -2
- package/dist/lib/{deploy → commands/deploy}/sequence-status.js +2 -2
- package/dist/lib/{deploy → commands/deploy}/types.d.ts +4 -4
- package/dist/lib/commands/deploy/types.js +1 -0
- package/dist/lib/commands/deployments/output.d.ts +2 -0
- package/dist/lib/commands/deployments/output.js +68 -0
- package/dist/lib/commands/deployments/types.d.ts +24 -0
- package/dist/lib/commands/deployments/types.js +1 -0
- package/dist/lib/commands/domain/setup.d.ts +8 -0
- package/dist/lib/commands/domain/setup.js +53 -0
- package/dist/lib/commands/domain/types.d.ts +56 -0
- package/dist/lib/commands/domain/types.js +1 -0
- package/dist/lib/commands/domain/verify.d.ts +5 -0
- package/dist/lib/commands/domain/verify.js +50 -0
- package/dist/lib/commands/edit/diff.d.ts +7 -0
- package/dist/lib/commands/edit/diff.js +65 -0
- package/dist/lib/commands/edit/display.d.ts +5 -0
- package/dist/lib/commands/edit/display.js +53 -0
- package/dist/lib/commands/edit/flow.d.ts +8 -0
- package/dist/lib/commands/edit/flow.js +70 -0
- package/dist/lib/commands/edit/persist.d.ts +5 -0
- package/dist/lib/commands/edit/persist.js +67 -0
- package/dist/lib/commands/edit/types.d.ts +38 -0
- package/dist/lib/commands/edit/types.js +1 -0
- package/dist/lib/commands/emails/editor.d.ts +2 -0
- package/dist/lib/commands/emails/editor.js +43 -0
- package/dist/lib/commands/emails/output.d.ts +4 -0
- package/dist/lib/commands/emails/output.js +36 -0
- package/dist/lib/commands/emails/types.d.ts +3 -0
- package/dist/lib/commands/emails/types.js +1 -0
- package/dist/lib/commands/init/analysis.d.ts +3 -0
- package/dist/lib/commands/init/analysis.js +73 -0
- package/dist/lib/commands/init/output.d.ts +12 -0
- package/dist/lib/commands/init/output.js +39 -0
- package/dist/lib/commands/init/payload.d.ts +8 -0
- package/dist/lib/commands/init/payload.js +78 -0
- package/dist/lib/commands/init/types.d.ts +57 -0
- package/dist/lib/commands/init/types.js +1 -0
- package/dist/lib/commands/login/output.d.ts +8 -0
- package/dist/lib/commands/login/output.js +40 -0
- package/dist/lib/commands/login/types.d.ts +19 -0
- package/dist/lib/commands/login/types.js +1 -0
- package/dist/lib/commands/logs/output.d.ts +2 -0
- package/dist/lib/commands/logs/output.js +52 -0
- package/dist/lib/commands/logs/types.d.ts +23 -0
- package/dist/lib/commands/logs/types.js +1 -0
- package/dist/lib/commands/preview/actions.d.ts +11 -0
- package/dist/lib/commands/preview/actions.js +43 -0
- package/dist/lib/commands/preview/render.d.ts +3 -0
- package/dist/lib/commands/preview/render.js +30 -0
- package/dist/lib/commands/preview/server.d.ts +8 -0
- package/dist/lib/commands/preview/server.js +63 -0
- package/dist/lib/commands/preview/types.d.ts +22 -0
- package/dist/lib/commands/preview/types.js +1 -0
- package/dist/lib/commands/preview/wrapper-html.d.ts +2 -0
- package/dist/lib/commands/preview/wrapper-html.js +35 -0
- package/dist/lib/commands/sdk/output.d.ts +2 -0
- package/dist/lib/commands/sdk/output.js +42 -0
- package/dist/lib/commands/sdk/types.d.ts +21 -0
- package/dist/lib/commands/sdk/types.js +1 -0
- package/dist/lib/commands/settings/actions.d.ts +10 -0
- package/dist/lib/commands/settings/actions.js +56 -0
- package/dist/lib/commands/settings/display.d.ts +15 -0
- package/dist/lib/commands/settings/display.js +69 -0
- package/dist/lib/commands/settings/logo-domain.d.ts +3 -0
- package/dist/lib/commands/settings/logo-domain.js +47 -0
- package/dist/lib/commands/settings/prompt.d.ts +2 -0
- package/dist/lib/commands/settings/prompt.js +82 -0
- package/dist/lib/commands/settings/types.d.ts +65 -0
- package/dist/lib/commands/settings/types.js +1 -0
- package/dist/lib/commands/status/output.d.ts +2 -0
- package/dist/lib/commands/status/output.js +49 -0
- package/dist/lib/commands/status/types.d.ts +28 -0
- package/dist/lib/commands/status/types.js +1 -0
- package/dist/lib/constants.d.ts +3 -2
- package/dist/lib/constants.js +4 -5
- package/dist/lib/messages.d.ts +11 -0
- package/dist/lib/messages.js +31 -0
- package/dist/lib/templates/missing-templates.d.ts +16 -2
- package/dist/lib/templates/missing-templates.js +34 -22
- package/dist/lib/templates/regenerate.d.ts +10 -0
- package/dist/lib/templates/regenerate.js +29 -0
- package/dist/lib/templates/sync.d.ts +33 -0
- package/dist/lib/templates/sync.js +106 -0
- package/dist/lib/templates/types.d.ts +3 -0
- package/oclif.manifest.json +54 -54
- package/package.json +1 -1
- /package/dist/lib/{deploy → commands/billing}/types.js +0 -0
- /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
|
-
|
|
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 {
|
|
5
|
+
import { TEMPLATES_DIR } from '../constants.js';
|
|
6
6
|
import { MISSING_TEMPLATES } from '../messages.js';
|
|
7
|
-
import { getTemplateFilename
|
|
8
|
-
import {
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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:
|
|
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
|
|
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,
|
|
60
|
-
return
|
|
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
|
};
|
package/oclif.manifest.json
CHANGED
|
@@ -573,14 +573,19 @@
|
|
|
573
573
|
"index.js"
|
|
574
574
|
]
|
|
575
575
|
},
|
|
576
|
-
"
|
|
576
|
+
"preview": {
|
|
577
577
|
"aliases": [],
|
|
578
|
-
"args": {
|
|
579
|
-
|
|
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 %>
|
|
582
|
-
"<%= config.bin %>
|
|
583
|
-
"<%= config.bin %>
|
|
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
|
-
"
|
|
600
|
-
"description": "
|
|
601
|
-
"name": "
|
|
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": "
|
|
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
|
-
"
|
|
630
|
+
"preview",
|
|
620
631
|
"index.js"
|
|
621
632
|
]
|
|
622
633
|
},
|
|
623
|
-
"
|
|
634
|
+
"sdk": {
|
|
624
635
|
"aliases": [],
|
|
625
636
|
"args": {},
|
|
626
|
-
"description": "
|
|
637
|
+
"description": "Show the SDK track() / identify() reference for deployed sequences",
|
|
627
638
|
"examples": [
|
|
628
|
-
"<%= config.bin %>
|
|
629
|
-
"<%= config.bin %>
|
|
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": "
|
|
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
|
-
"
|
|
677
|
+
"sdk",
|
|
659
678
|
"index.js"
|
|
660
679
|
]
|
|
661
680
|
},
|
|
662
|
-
"
|
|
681
|
+
"settings": {
|
|
663
682
|
"aliases": [],
|
|
664
|
-
"args": {
|
|
665
|
-
|
|
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 %>
|
|
673
|
-
"<%= config.bin %>
|
|
674
|
-
"<%= config.bin %>
|
|
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
|
-
"
|
|
691
|
-
"description": "
|
|
692
|
-
"name": "
|
|
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": "
|
|
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
|
-
"
|
|
724
|
+
"settings",
|
|
717
725
|
"index.js"
|
|
718
726
|
]
|
|
719
727
|
},
|
|
720
|
-
"
|
|
728
|
+
"status": {
|
|
721
729
|
"aliases": [],
|
|
722
730
|
"args": {},
|
|
723
|
-
"description": "
|
|
731
|
+
"description": "View email performance metrics and quota usage",
|
|
724
732
|
"examples": [
|
|
725
|
-
"<%= config.bin %>
|
|
726
|
-
"<%= config.bin %>
|
|
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": "
|
|
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
|
-
"
|
|
763
|
+
"status",
|
|
764
764
|
"index.js"
|
|
765
765
|
]
|
|
766
766
|
}
|
|
767
767
|
},
|
|
768
|
-
"version": "0.0.
|
|
768
|
+
"version": "0.0.56-beta.pr58.100"
|
|
769
769
|
}
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|