@myapihq/cli 1.2.2 → 1.2.4

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.
@@ -11,6 +11,8 @@ export declare function list(flags: Flags): Promise<void>;
11
11
  export declare function assign(domainArg: string, flags: Flags): Promise<void>;
12
12
  export declare function unassign(domainArg: string, flags: Flags): Promise<void>;
13
13
  export declare function status(domainArg: string, flags: Flags): Promise<void>;
14
+ export declare function emailSetup(domainArg: string, flags: Flags): Promise<void>;
15
+ export declare function retryProvisioning(domainArg: string, flags: Flags): Promise<void>;
14
16
  export declare function settings(domainArg: string, flags: Flags): Promise<void>;
15
17
  export declare function updateSettings(domainArg: string, flags: Flags): Promise<void>;
16
18
  export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
@@ -14,6 +14,8 @@ export const EXPOSES = [
14
14
  'GET /domain/orgs/{org_id}/{domain}/settings',
15
15
  'POST /domain/orgs/{org_id}/{domain}/settings',
16
16
  'GET /domain/orgs/{org_id}/{domain}/status',
17
+ 'POST /domain/orgs/{org_id}/{domain}/email-infra',
18
+ 'POST /domain/orgs/{org_id}/{domain}/retry-provisioning',
17
19
  ];
18
20
  export const SCHEMA = {
19
21
  org: 'string',
@@ -46,6 +48,8 @@ export const SCHEMA = {
46
48
  priority: 'number',
47
49
  proxied: 'boolean',
48
50
  yes: 'boolean',
51
+ // Email-infra opt-in
52
+ subdomain: 'string',
49
53
  };
50
54
  export async function check(domainArg, flags) {
51
55
  const config = requireConfig();
@@ -169,8 +173,10 @@ export async function unassign(domainArg, flags) {
169
173
  await sdkDomain.unassignDomain(config.api_key, orgId, domainArg);
170
174
  success(`Unassigned ${domainArg} from org ${orgId}`);
171
175
  }
172
- // Terminal states — stop polling under --watch.
173
- const TERMINAL_STATUSES = new Set(['active', 'failed', 'error', 'expired']);
176
+ // Terminal states — stop polling under --watch. `infra_error` is included
177
+ // because watching past it is pointless — the user has to act (retry or
178
+ // contact support); a re-watch after retry is the natural next step.
179
+ const TERMINAL_STATUSES = new Set(['active', 'failed', 'error', 'expired', 'infra_error']);
174
180
  export async function status(domainArg, flags) {
175
181
  const config = requireConfig();
176
182
  const orgId = requireOrg(flags, config, 'myapi domain status <domain> [--org <id>] [--watch]');
@@ -212,13 +218,63 @@ function renderStatus(res) {
212
218
  info(`Status: ${res.status}`);
213
219
  if (res.expires_at)
214
220
  info(`Expires: ${formatDate(res.expires_at)}`);
221
+ if (res.dns_active !== undefined)
222
+ info(`DNS: ${res.dns_active ? 'active' : 'pending'}`);
223
+ if (res.email_infra) {
224
+ const where = res.email_subdomain ? ` on ${res.email_subdomain}` : '';
225
+ info(`Email: ${res.email_infra}${where}`);
226
+ }
215
227
  if (res.status === 'pending_ns_change') {
216
228
  info('Waiting for you to change nameservers at your current registrar. Each poll re-checks Cloudflare.');
217
229
  }
230
+ if (res.status === 'infra_error' && res.error_detail) {
231
+ info(` Failed step: ${res.error_detail.failed_step}`);
232
+ info(` Message: ${res.error_detail.message}`);
233
+ if (res.error_detail.attempt_count !== undefined) {
234
+ const last = res.error_detail.last_attempt_at ? `; last try ${res.error_detail.last_attempt_at}` : '';
235
+ info(` Attempts: ${res.error_detail.attempt_count}${last}`);
236
+ }
237
+ if (res.error_detail.retryable) {
238
+ info(` → myapi domain retry-provisioning ${res.domain}`);
239
+ }
240
+ else {
241
+ info(' Not retryable — contact support.');
242
+ }
243
+ }
218
244
  if (res.status === 'active') {
219
245
  info('Note: if recently activated, the SSL certificate may still be provisioning — allow a few minutes before the site is reachable over HTTPS.');
220
246
  }
221
247
  }
248
+ // ── Email infra opt-in + retry-provisioning ──────────────────────────────────
249
+ export async function emailSetup(domainArg, flags) {
250
+ const config = requireConfig();
251
+ const orgId = requireOrg(flags, config, 'myapi domain email-setup <domain> [--subdomain <label>] [--org <id>]');
252
+ const domain = requireDomain(domainArg, flags, config, 'myapi domain email-setup <domain> [--subdomain <label>] [--org <id>]');
253
+ const subdomain = typeof flags.subdomain === 'string' ? flags.subdomain : undefined;
254
+ const res = await sdkDomain.setupEmailInfra(config.api_key, orgId, domain, subdomain);
255
+ if (flags.json) {
256
+ printJson(res);
257
+ return;
258
+ }
259
+ success(`Email infra provisioning on ${res.email_subdomain} (state: ${res.email_infra})`);
260
+ if (res.next_step)
261
+ info(res.next_step);
262
+ info(`Track with: myapi domain status ${domain} --watch`);
263
+ }
264
+ export async function retryProvisioning(domainArg, flags) {
265
+ const config = requireConfig();
266
+ const orgId = requireOrg(flags, config, 'myapi domain retry-provisioning <domain> [--org <id>]');
267
+ const domain = requireDomain(domainArg, flags, config, 'myapi domain retry-provisioning <domain> [--org <id>]');
268
+ const res = await sdkDomain.retryProvisioning(config.api_key, orgId, domain);
269
+ if (flags.json) {
270
+ printJson(res);
271
+ return;
272
+ }
273
+ success(`Retry triggered for ${res.domain}`);
274
+ if (res.next_step)
275
+ info(res.next_step);
276
+ info(`Track with: myapi domain status ${domain} --watch`);
277
+ }
222
278
  export async function settings(domainArg, flags) {
223
279
  const config = requireConfig();
224
280
  const orgId = requireOrg(flags, config, 'myapi domain settings <domain> [--org <id>]');
@@ -472,6 +528,24 @@ To transfer to a different org: unassign first, then assign to the new org.`,
472
528
  Gets current edge/CDN settings (security level, browser check, cache).
473
529
  Use "myapi domain update-settings" to change them.`,
474
530
  'records': RECORDS_HELP,
531
+ 'email-setup': `myapi domain email-setup <domain> [--subdomain <label>] [--org <id>]
532
+
533
+ Opts in to MyAPI-managed outbound email on a subdomain of the domain.
534
+ The apex is never touched — this is safe to run on a domain whose apex
535
+ email is served by Google Workspace / Microsoft 365 / your existing provider.
536
+
537
+ Defaults the subdomain to "mail" (i.e. mail.<domain>). Pass --subdomain to
538
+ override (e.g. --subdomain=notifications for notifications.<domain>).
539
+
540
+ Backend provisions an SES identity on <subdomain>.<domain> and writes
541
+ DKIM / SPF / DMARC records to the CF zone. Track readiness via:
542
+ myapi domain status <domain> --watch`,
543
+ 'retry-provisioning': `myapi domain retry-provisioning <domain> [--org <id>]
544
+
545
+ Re-runs domain provisioning when status=infra_error and error_detail.retryable=true.
546
+ The CLI surfaces the retry hint automatically when status renders infra_error.
547
+
548
+ After triggering retry, poll: myapi domain status <domain> --watch`,
475
549
  'update-settings': `myapi domain update-settings <domain> [--security=<level>] [--browser-check=on|off] [--purge-cache] [--org <id>]
476
550
 
477
551
  --security=<level> essentially_off | low | medium | high | under_attack (default: medium)
@@ -497,6 +571,8 @@ Subcommands:
497
571
  settings Get edge/CDN settings
498
572
  update-settings Update edge/CDN settings
499
573
  records Manage DNS records in the zone (list / get / create / update / delete)
574
+ email-setup Opt in to MyAPI-managed email on a subdomain (default: mail.<domain>)
575
+ retry-provisioning Re-run provisioning when status=infra_error and the failure is retryable
500
576
 
501
577
  Tip: Set defaults with "myapi config set-org <id>" / "set-domain <domain>" to skip flags on every command.`);
502
578
  return;
@@ -521,6 +597,8 @@ Tip: Set defaults with "myapi config set-org <id>" / "set-domain <domain>" to sk
521
597
  case 'settings': return settings(args[0], flags);
522
598
  case 'update-settings': return updateSettings(args[0], flags);
523
599
  case 'records': return recordsRun(args[0], args.slice(1), flags);
600
+ case 'email-setup': return emailSetup(args[0], flags);
601
+ case 'retry-provisioning': return retryProvisioning(args[0], flags);
524
602
  default: error(`Unknown subcommand: ${subcommand}. Run "myapi domain --help" for a list of valid subcommands.`);
525
603
  }
526
604
  }
package/dist/index.js CHANGED
@@ -88,13 +88,26 @@ const ERROR_MESSAGES = {
88
88
  INVALID_TTL: 'Invalid TTL. Use the auto sentinel (1) or a value between 60 and 86400 seconds.',
89
89
  INVALID_RECORD_CONTENT: 'Invalid record content for this type.',
90
90
  RECORD_LIMIT_EXCEEDED: 'Cloudflare per-zone record limit reached.',
91
- CF_API_ERROR: 'Cloudflare API error. The backend will surface a `cf_message` field with the underlying detail.',
91
+ CF_API_ERROR: 'Cloudflare API error.',
92
92
  // invalid_json_response intentionally absent — the SDK's MyApiError now
93
93
  // builds a useful detailed message for that case (status + URL + body
94
94
  // snippet), and friendlyError(err.code) would override it.
95
95
  };
96
- function friendlyError(code) {
97
- return ERROR_MESSAGES[code] || code;
96
+ function friendlyError(err) {
97
+ const body = err.body ?? {};
98
+ // CF_API_ERROR: the cf_message already includes a "Cloudflare API error"
99
+ // prefix, so use it verbatim (with cf_status if present) instead of doubling.
100
+ if (err.code === 'CF_API_ERROR' && typeof body.cf_message === 'string') {
101
+ return typeof body.cf_status === 'number'
102
+ ? `${body.cf_message} (HTTP ${body.cf_status})`
103
+ : body.cf_message;
104
+ }
105
+ const base = ERROR_MESSAGES[err.code] || err.code;
106
+ // Generic fallback: append the backend's `message` when it adds info beyond
107
+ // the friendly mapping. Future per-service detail fields can be added here.
108
+ if (err.detail && err.detail !== base)
109
+ return `${base} — ${err.detail}`;
110
+ return base;
98
111
  }
99
112
  async function main() {
100
113
  // Shell autocomplete: if invoked by the shell with completion env vars,
@@ -250,7 +263,7 @@ async function main() {
250
263
  }
251
264
  }
252
265
  else
253
- error(friendlyError(err.code) || err.message);
266
+ error(friendlyError(err) || err.message);
254
267
  }
255
268
  else {
256
269
  // Don't JSON.stringify Error instances — that returns "{}" because
@@ -59,6 +59,8 @@ Resolution at register time (highest wins): `--registrant-json` → per-field fl
59
59
  | `myapi domain records create <domain> --type T --name n --content c` | Create a record (priority required for MX) |
60
60
  | `myapi domain records update <domain> <id> [--content c] [...]` | Update a record (type cannot change) |
61
61
  | `myapi domain records delete <domain> <id> --yes` | Delete a record |
62
+ | `myapi domain email-setup <domain> [--subdomain <label>]` | Opt in to MyAPI-managed email on a subdomain (default: `mail.<domain>`). Apex is never touched |
63
+ | `myapi domain retry-provisioning <domain>` | Re-run provisioning when status=infra_error and error_detail.retryable=true |
62
64
  <!-- generated:end -->
63
65
 
64
66
  ## Examples
@@ -91,6 +93,14 @@ myapi domain records list example.com --type TXT
91
93
  myapi domain records delete example.com <bad-id> --yes
92
94
  myapi domain records create example.com --type TXT --name @ \
93
95
  --content 'v=spf1 include:_spf.google.com ~all'
96
+
97
+ # Opt in to MyAPI email on a subdomain (apex Google Workspace stays untouched)
98
+ myapi domain email-setup example.com # → mail.example.com
99
+ myapi domain email-setup example.com --subdomain=notifications
100
+
101
+ # Recover from infra_error
102
+ myapi domain status example.com # CLI prints the retry hint
103
+ myapi domain retry-provisioning example.com
94
104
  ```
95
105
 
96
106
  Security levels: `essentially_off` · `low` · `medium` · `high` · `under_attack`.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@myapihq/cli",
3
3
  "license": "Apache-2.0",
4
- "version": "1.2.2",
4
+ "version": "1.2.4",
5
5
  "description": "MyAPI command-line interface",
6
6
  "type": "module",
7
7
  "files": [
@@ -27,7 +27,7 @@
27
27
  "lint:changelog": "node ../../scripts/lint-changelog.js"
28
28
  },
29
29
  "dependencies": {
30
- "@myapihq/sdk": "^1.2.2",
30
+ "@myapihq/sdk": "^1.2.4",
31
31
  "omelette": "^0.4.17"
32
32
  },
33
33
  "devDependencies": {