@mailmodo/cli 0.0.46-beta.pr48.74 → 0.0.46

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.
@@ -1,7 +1,7 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
3
  import open from 'open';
4
- import { BaseCommand, FREE_TIER } from '../../lib/base-command.js';
4
+ import { BaseCommand } from '../../lib/base-command.js';
5
5
  import { API_ENDPOINTS } from '../../lib/constants.js';
6
6
  import { INFO } from '../../lib/messages.js';
7
7
  import { loadYaml, saveYaml } from '../../lib/yaml-config.js';
@@ -200,12 +200,6 @@ export default class Billing extends BaseCommand {
200
200
  this.log('');
201
201
  }
202
202
  async setCap(cap, autoChargeBlockCount, jsonOutput) {
203
- this.validateBillingCapInputs({ autoChargeBlockCount, cap });
204
- const tier = await this.fetchBillingTier();
205
- if (tier === FREE_TIER) {
206
- this.warnFreeTierCapBlocked(jsonOutput);
207
- return;
208
- }
209
203
  const data = await this.applyBillingCap({
210
204
  autoChargeBlockCount,
211
205
  cap,
@@ -4,7 +4,7 @@ import chalk from 'chalk';
4
4
  import { existsSync } from 'node:fs';
5
5
  import { readFile } from 'node:fs/promises';
6
6
  import { resolve } from 'node:path';
7
- import { BaseCommand, FREE_TIER } from '../../lib/base-command.js';
7
+ import { BaseCommand } from '../../lib/base-command.js';
8
8
  import { API_ENDPOINTS } from '../../lib/constants.js';
9
9
  import { INFO } from '../../lib/messages.js';
10
10
  import { saveYaml } from '../../lib/yaml-config.js';
@@ -52,18 +52,13 @@ export default class Settings extends BaseCommand {
52
52
  this.log(JSON.stringify({ settings: yamlConfig.project }, null, 2));
53
53
  return;
54
54
  }
55
- const [domainVerified, tier] = await Promise.all([
56
- this.fetchDomainVerified(yamlConfig.project.domain),
57
- this.fetchBillingTier(),
58
- ]);
55
+ const domainVerified = await this.fetchDomainVerified(yamlConfig.project.domain);
59
56
  this.log(`\n Current settings for ${chalk.bold(yamlConfig.project.name || 'project')}:\n`);
60
57
  for (const [group, keys] of Object.entries(SETTINGS_GROUPS)) {
61
- if (group === 'billing' && tier === FREE_TIER)
62
- continue;
63
58
  this.displaySettingsGroup(group, keys, yamlConfig.project, domainVerified);
64
59
  }
65
60
  if (!flags.yes) {
66
- await this.promptEditSetting(yamlConfig, tier);
61
+ await this.promptEditSetting(yamlConfig);
67
62
  }
68
63
  }
69
64
  async applySetFlag(setFlag, yamlConfig, isJson) {
@@ -79,7 +74,7 @@ export default class Settings extends BaseCommand {
79
74
  this.error(`Unknown setting: ${key}`);
80
75
  }
81
76
  if (propKey === 'monthlyCap') {
82
- await this.applyMonthlyCapChange(yamlConfig, value, isJson, null);
77
+ await this.applyMonthlyCapChange(yamlConfig, value, isJson);
83
78
  return;
84
79
  }
85
80
  project[propKey] = value;
@@ -91,17 +86,12 @@ export default class Settings extends BaseCommand {
91
86
  this.log(`\n ${chalk.green('✓')} ${key} updated to ${chalk.cyan(value)}`);
92
87
  this.log(` ${INFO.DEPLOY_TO_APPLY}\n`);
93
88
  }
94
- async applyMonthlyCapChange(yamlConfig, rawValue, isJson, knownTier) {
89
+ async applyMonthlyCapChange(yamlConfig, rawValue, isJson) {
95
90
  const parsed = Number(rawValue);
96
91
  if (!Number.isInteger(parsed) || parsed < 1) {
97
92
  this.error('monthly_cap must be a positive integer (blocks).');
98
93
  }
99
94
  await this.ensureAuth();
100
- const tier = knownTier ?? (await this.fetchBillingTier());
101
- if (tier === FREE_TIER) {
102
- this.warnFreeTierCapBlocked(isJson);
103
- return;
104
- }
105
95
  const data = await this.applyBillingCap({ cap: parsed, json: isJson });
106
96
  yamlConfig.project.monthlyCap = data.capBlocks;
107
97
  await saveYaml(yamlConfig);
@@ -156,7 +146,7 @@ export default class Settings extends BaseCommand {
156
146
  * Prompts the user to pick a setting key to edit and dispatches
157
147
  * to the appropriate handler for that key.
158
148
  */
159
- async promptEditSetting(yamlConfig, tier) {
149
+ async promptEditSetting(yamlConfig) {
160
150
  const { project } = yamlConfig;
161
151
  const editKey = await input({
162
152
  default: 'n',
@@ -164,10 +154,6 @@ export default class Settings extends BaseCommand {
164
154
  });
165
155
  if (editKey === 'n')
166
156
  return;
167
- if (editKey === 'monthly_cap' && tier === FREE_TIER) {
168
- this.warnFreeTierCapBlocked(false);
169
- return;
170
- }
171
157
  const editPropKey = settingKeyToProp(editKey);
172
158
  if (!(editPropKey in project)) {
173
159
  if (editKey === 'logo_file') {
@@ -195,7 +181,7 @@ export default class Settings extends BaseCommand {
195
181
  const newValue = await input({
196
182
  message: 'New monthly cap (blocks):',
197
183
  });
198
- await this.applyMonthlyCapChange(yamlConfig, newValue, false, tier);
184
+ await this.applyMonthlyCapChange(yamlConfig, newValue, false);
199
185
  return;
200
186
  }
201
187
  if (editKey === 'email_style') {
@@ -8,7 +8,6 @@ export interface BillingCapUpdateResult {
8
8
  capEmails: number;
9
9
  message: string;
10
10
  }
11
- export declare const FREE_TIER = "free";
12
11
  /**
13
12
  * Abstract base command providing shared functionality for all Mailmodo CLI commands.
14
13
  * Subclasses inherit --json and --yes base flags, authentication enforcement,
@@ -75,15 +74,6 @@ export declare abstract class BaseCommand extends Command {
75
74
  fromName: string;
76
75
  replyTo: string;
77
76
  }>;
78
- /**
79
- * Validates the inputs that would be sent to `POST /billing/cap`. Extracted
80
- * so callers can run cheap, no-API local checks (positive integers) before
81
- * the tier lookup or any other network round trip.
82
- */
83
- protected validateBillingCapInputs(options: {
84
- autoChargeBlockCount?: number;
85
- cap: number;
86
- }): void;
87
77
  /**
88
78
  * Updates the account's monthly sending cap on the server.
89
79
  * Validates inputs, wraps the POST /billing/cap call in a spinner, and
@@ -95,21 +85,6 @@ export declare abstract class BaseCommand extends Command {
95
85
  cap: number;
96
86
  json: boolean;
97
87
  }): Promise<BillingCapUpdateResult>;
98
- /**
99
- * Shared "free-tier users can't set a monthly cap" notice for both the
100
- * billing and settings commands. Prints a yellow warning in text mode and a
101
- * `status: 'blocked'` payload in JSON mode so machine-readable callers can
102
- * distinguish from a successful update.
103
- */
104
- protected warnFreeTierCapBlocked(jsonOutput: boolean): void;
105
- /**
106
- * Fetches the account's billing tier from `/billing/status`. Returns `null`
107
- * when the request cannot be completed (no credentials, network failure,
108
- * non-OK response) so callers can degrade gracefully without halting.
109
- * Callers that need a hard "free vs paid" decision should treat `null` as
110
- * unknown and fall through to their default behavior.
111
- */
112
- protected fetchBillingTier(): Promise<null | string>;
113
88
  protected registerDomain(yamlConfig: MailmodoYaml, inputs: {
114
89
  address: string;
115
90
  domain: string;
@@ -7,7 +7,6 @@ import { loadConfig } from './config.js';
7
7
  import { API_ENDPOINTS } from './constants.js';
8
8
  import { ERRORS, INFO, PROMPTS, recordLabel, VALIDATION } from './messages.js';
9
9
  import { loadYaml, saveYaml } from './yaml-config.js';
10
- export const FREE_TIER = 'free';
11
10
  /**
12
11
  * Abstract base command providing shared functionality for all Mailmodo CLI commands.
13
12
  * Subclasses inherit --json and --yes base flags, authentication enforcement,
@@ -147,11 +146,12 @@ export class BaseCommand extends Command {
147
146
  return { address, domain, fromEmail, fromName, replyTo };
148
147
  }
149
148
  /**
150
- * Validates the inputs that would be sent to `POST /billing/cap`. Extracted
151
- * so callers can run cheap, no-API local checks (positive integers) before
152
- * the tier lookup or any other network round trip.
149
+ * Updates the account's monthly sending cap on the server.
150
+ * Validates inputs, wraps the POST /billing/cap call in a spinner, and
151
+ * surfaces errors via handleApiError. The caller is responsible for any
152
+ * local persistence (e.g. writing `monthlyCap` to mailmodo.yaml).
153
153
  */
154
- validateBillingCapInputs(options) {
154
+ async applyBillingCap(options) {
155
155
  if (options.cap < 1) {
156
156
  this.error('Cap must be at least 1 block.');
157
157
  }
@@ -159,15 +159,6 @@ export class BaseCommand extends Command {
159
159
  options.autoChargeBlockCount < 1) {
160
160
  this.error('Auto-charge block count must be at least 1 block.');
161
161
  }
162
- }
163
- /**
164
- * Updates the account's monthly sending cap on the server.
165
- * Validates inputs, wraps the POST /billing/cap call in a spinner, and
166
- * surfaces errors via handleApiError. The caller is responsible for any
167
- * local persistence (e.g. writing `monthlyCap` to mailmodo.yaml).
168
- */
169
- async applyBillingCap(options) {
170
- this.validateBillingCapInputs(options);
171
162
  const payload = options.autoChargeBlockCount === undefined
172
163
  ? { cap: options.cap }
173
164
  : {
@@ -180,42 +171,6 @@ export class BaseCommand extends Command {
180
171
  }
181
172
  return response.data;
182
173
  }
183
- /**
184
- * Shared "free-tier users can't set a monthly cap" notice for both the
185
- * billing and settings commands. Prints a yellow warning in text mode and a
186
- * `status: 'blocked'` payload in JSON mode so machine-readable callers can
187
- * distinguish from a successful update.
188
- */
189
- warnFreeTierCapBlocked(jsonOutput) {
190
- if (jsonOutput) {
191
- this.log(JSON.stringify({
192
- message: INFO.FREE_TIER_CAP_BLOCKED,
193
- status: 'blocked',
194
- tier: FREE_TIER,
195
- }, null, 2));
196
- return;
197
- }
198
- this.warn(chalk.yellow(INFO.FREE_TIER_CAP_BLOCKED));
199
- }
200
- /**
201
- * Fetches the account's billing tier from `/billing/status`. Returns `null`
202
- * when the request cannot be completed (no credentials, network failure,
203
- * non-OK response) so callers can degrade gracefully without halting.
204
- * Callers that need a hard "free vs paid" decision should treat `null` as
205
- * unknown and fall through to their default behavior.
206
- */
207
- async fetchBillingTier() {
208
- try {
209
- await this.ensureAuth();
210
- const response = await this.apiClient.get(API_ENDPOINTS.BILLING_STATUS);
211
- if (!response.ok)
212
- return null;
213
- return response.data?.tier ?? null;
214
- }
215
- catch {
216
- return null;
217
- }
218
- }
219
174
  async registerDomain(yamlConfig, inputs, json) {
220
175
  const apiPayload = {
221
176
  address: inputs.address,
@@ -32,7 +32,6 @@ export declare const INFO: {
32
32
  readonly DOMAIN_NOT_DEPLOYED_HINT: `When ready, run: ${string}
33
33
  Then: ${string}`;
34
34
  readonly DOMAIN_PENDING_VERIFICATION: `Your domain is not verified yet. Please verify it first. Run ${string} to check the status.`;
35
- readonly FREE_TIER_CAP_BLOCKED: `Monthly cap is a paid-tier setting and is not available on the free tier. Run ${string} to add a payment method, then set a cap.`;
36
35
  readonly SEQUENCES_NOT_DEPLOYED: `Sequences saved but ${string}.`;
37
36
  };
38
37
  export declare function yamlParseError(detail: string): string;
@@ -32,7 +32,6 @@ export const INFO = {
32
32
  DNS_RECORDS_FAILED: chalk.yellow('Some records failed.'),
33
33
  DOMAIN_NOT_DEPLOYED_HINT: `When ready, run: ${chalk.cyan('mailmodo domain')}\n Then: ${chalk.cyan('mailmodo deploy')}`,
34
34
  DOMAIN_PENDING_VERIFICATION: `Your domain is not verified yet. Please verify it first. Run ${chalk.cyan('mailmodo domain --verify')} to check the status.`,
35
- FREE_TIER_CAP_BLOCKED: `Monthly cap is a paid-tier setting and is not available on the free tier. Run ${chalk.cyan("'mailmodo billing --checkout'")} to add a payment method, then set a cap.`,
36
35
  SEQUENCES_NOT_DEPLOYED: `Sequences saved but ${chalk.yellow('NOT deployed')}.`,
37
36
  };
38
37
  export function yamlParseError(detail) {
@@ -228,19 +228,13 @@
228
228
  "index.js"
229
229
  ]
230
230
  },
231
- "edit": {
231
+ "emails": {
232
232
  "aliases": [],
233
- "args": {
234
- "id": {
235
- "description": "Email template ID to edit",
236
- "name": "id",
237
- "required": true
238
- }
239
- },
240
- "description": "Edit an email using AI-assisted natural language changes",
233
+ "args": {},
234
+ "description": "List and view configured email sequences",
241
235
  "examples": [
242
- "<%= config.bin %> edit welcome",
243
- "<%= config.bin %> edit welcome --change \"make subject more urgent\" --yes"
236
+ "<%= config.bin %> emails",
237
+ "<%= config.bin %> emails --json"
244
238
  ],
245
239
  "flags": {
246
240
  "json": {
@@ -255,18 +249,11 @@
255
249
  "name": "yes",
256
250
  "allowNo": false,
257
251
  "type": "boolean"
258
- },
259
- "change": {
260
- "description": "Natural language description of the change",
261
- "name": "change",
262
- "hasDynamicHelp": false,
263
- "multiple": false,
264
- "type": "option"
265
252
  }
266
253
  },
267
254
  "hasDynamicHelp": false,
268
255
  "hiddenAliases": [],
269
- "id": "edit",
256
+ "id": "emails",
270
257
  "pluginAlias": "@mailmodo/cli",
271
258
  "pluginName": "@mailmodo/cli",
272
259
  "pluginType": "core",
@@ -276,17 +263,23 @@
276
263
  "relativePath": [
277
264
  "dist",
278
265
  "commands",
279
- "edit",
266
+ "emails",
280
267
  "index.js"
281
268
  ]
282
269
  },
283
- "emails": {
270
+ "edit": {
284
271
  "aliases": [],
285
- "args": {},
286
- "description": "List and view configured email sequences",
272
+ "args": {
273
+ "id": {
274
+ "description": "Email template ID to edit",
275
+ "name": "id",
276
+ "required": true
277
+ }
278
+ },
279
+ "description": "Edit an email using AI-assisted natural language changes",
287
280
  "examples": [
288
- "<%= config.bin %> emails",
289
- "<%= config.bin %> emails --json"
281
+ "<%= config.bin %> edit welcome",
282
+ "<%= config.bin %> edit welcome --change \"make subject more urgent\" --yes"
290
283
  ],
291
284
  "flags": {
292
285
  "json": {
@@ -301,11 +294,18 @@
301
294
  "name": "yes",
302
295
  "allowNo": false,
303
296
  "type": "boolean"
297
+ },
298
+ "change": {
299
+ "description": "Natural language description of the change",
300
+ "name": "change",
301
+ "hasDynamicHelp": false,
302
+ "multiple": false,
303
+ "type": "option"
304
304
  }
305
305
  },
306
306
  "hasDynamicHelp": false,
307
307
  "hiddenAliases": [],
308
- "id": "emails",
308
+ "id": "edit",
309
309
  "pluginAlias": "@mailmodo/cli",
310
310
  "pluginName": "@mailmodo/cli",
311
311
  "pluginType": "core",
@@ -315,7 +315,7 @@
315
315
  "relativePath": [
316
316
  "dist",
317
317
  "commands",
318
- "emails",
318
+ "edit",
319
319
  "index.js"
320
320
  ]
321
321
  },
@@ -657,5 +657,5 @@
657
657
  ]
658
658
  }
659
659
  },
660
- "version": "0.0.46-beta.pr48.74"
660
+ "version": "0.0.46"
661
661
  }
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.46-beta.pr48.74",
4
+ "version": "0.0.46",
5
5
  "author": "provishalk",
6
6
  "bin": {
7
7
  "mailmodo": "bin/run.js"