@myapihq/cli 1.2.0 → 1.2.2
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.
|
@@ -5,6 +5,7 @@ export declare const EXPOSES: Exposes;
|
|
|
5
5
|
export declare const SCHEMA: FlagSchema;
|
|
6
6
|
export declare function check(domainArg: string, flags: Flags): Promise<void>;
|
|
7
7
|
export declare function register(domainArg: string, flags: Flags): Promise<void>;
|
|
8
|
+
export declare function importCmd(domainArg: string, flags: Flags): Promise<void>;
|
|
8
9
|
export declare function renew(domainArg: string, flags: Flags): Promise<void>;
|
|
9
10
|
export declare function list(flags: Flags): Promise<void>;
|
|
10
11
|
export declare function assign(domainArg: string, flags: Flags): Promise<void>;
|
package/dist/commands/domain.js
CHANGED
|
@@ -23,6 +23,8 @@ export const SCHEMA = {
|
|
|
23
23
|
security: 'string',
|
|
24
24
|
'browser-check': 'string',
|
|
25
25
|
'purge-cache': 'boolean',
|
|
26
|
+
// Poll `status` until the domain reaches a terminal state.
|
|
27
|
+
'watch': 'boolean',
|
|
26
28
|
// WHOIS registrant — required by ICANN at register time. Resolution
|
|
27
29
|
// order is JSON > per-field flags > stored config > TTY prompt > error.
|
|
28
30
|
// See packages/cli/src/registrant.ts for the full flow.
|
|
@@ -36,6 +38,14 @@ export const SCHEMA = {
|
|
|
36
38
|
'registrant-postal-code': 'string',
|
|
37
39
|
'registrant-country': 'string',
|
|
38
40
|
'registrant-organization': 'string',
|
|
41
|
+
// DNS records sub-surface
|
|
42
|
+
type: 'string',
|
|
43
|
+
name: 'string',
|
|
44
|
+
content: 'string',
|
|
45
|
+
ttl: 'number',
|
|
46
|
+
priority: 'number',
|
|
47
|
+
proxied: 'boolean',
|
|
48
|
+
yes: 'boolean',
|
|
39
49
|
};
|
|
40
50
|
export async function check(domainArg, flags) {
|
|
41
51
|
const config = requireConfig();
|
|
@@ -78,6 +88,41 @@ export async function register(domainArg, flags) {
|
|
|
78
88
|
}
|
|
79
89
|
// Renew a registered domain for one more registration period (typically 1 year).
|
|
80
90
|
// Charged against the org's billing balance — surface a confirmation unless --yes.
|
|
91
|
+
// BYOD: snapshot existing DNS records, create a CF zone, hand back the NS
|
|
92
|
+
// values the customer needs to set at their current registrar. No registrar
|
|
93
|
+
// credentials needed — backend is registrar-agnostic now.
|
|
94
|
+
export async function importCmd(domainArg, flags) {
|
|
95
|
+
const config = requireConfig();
|
|
96
|
+
const orgId = requireOrg(flags, config, 'myapi domain import <domain> [--org <id>]');
|
|
97
|
+
if (!domainArg)
|
|
98
|
+
error('Missing required arguments.\nUsage: myapi domain import <domain> [--org <id>]');
|
|
99
|
+
const res = await sdkDomain.importDomain(config.api_key, orgId, domainArg);
|
|
100
|
+
if (flags.json) {
|
|
101
|
+
printJson(res);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
success(`Imported ${res.domain} (status: ${res.status})`);
|
|
105
|
+
info('');
|
|
106
|
+
info(`Nameservers (set these at your current registrar):`);
|
|
107
|
+
for (const ns of res.nameservers)
|
|
108
|
+
info(` ${ns}`);
|
|
109
|
+
info('');
|
|
110
|
+
if (res.preserved_records.length > 0) {
|
|
111
|
+
info(`Preserved DNS records (${res.preserved_count} found — verify before the NS change):`);
|
|
112
|
+
printTable(res.preserved_records.map(r => ({ type: r.type, name: r.name, content: r.content, ttl: r.ttl })), { flags });
|
|
113
|
+
info('');
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
info(`No DNS records detected via public probe — verify directly with your registrar before the NS change.`);
|
|
117
|
+
info('');
|
|
118
|
+
}
|
|
119
|
+
info(res.probe_warning);
|
|
120
|
+
info('');
|
|
121
|
+
info(res.next_step);
|
|
122
|
+
info('');
|
|
123
|
+
info(`After changing nameservers, watch activation with:`);
|
|
124
|
+
info(` myapi domain status ${res.domain} --watch`);
|
|
125
|
+
}
|
|
81
126
|
export async function renew(domainArg, flags) {
|
|
82
127
|
const config = requireConfig();
|
|
83
128
|
const orgId = requireOrg(flags, config, 'myapi domain renew <domain> [--yes] [--org <id>]');
|
|
@@ -124,21 +169,55 @@ export async function unassign(domainArg, flags) {
|
|
|
124
169
|
await sdkDomain.unassignDomain(config.api_key, orgId, domainArg);
|
|
125
170
|
success(`Unassigned ${domainArg} from org ${orgId}`);
|
|
126
171
|
}
|
|
172
|
+
// Terminal states — stop polling under --watch.
|
|
173
|
+
const TERMINAL_STATUSES = new Set(['active', 'failed', 'error', 'expired']);
|
|
127
174
|
export async function status(domainArg, flags) {
|
|
128
175
|
const config = requireConfig();
|
|
129
|
-
const orgId = requireOrg(flags, config, 'myapi domain status <domain> [--org <id>]');
|
|
130
|
-
const domain = requireDomain(domainArg, flags, config, 'myapi domain status <domain> [--org <id>]');
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
176
|
+
const orgId = requireOrg(flags, config, 'myapi domain status <domain> [--org <id>] [--watch]');
|
|
177
|
+
const domain = requireDomain(domainArg, flags, config, 'myapi domain status <domain> [--org <id>] [--watch]');
|
|
178
|
+
if (!flags.watch) {
|
|
179
|
+
const res = await sdkDomain.getDomainStatus(config.api_key, orgId, domain);
|
|
180
|
+
if (flags.json) {
|
|
181
|
+
printJson(res);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
renderStatus(res);
|
|
134
185
|
return;
|
|
135
186
|
}
|
|
187
|
+
// Watch mode: 10s × 30 (5 min), then 30s × 60 (30 min). Total ~35 min budget.
|
|
188
|
+
// Backend live-checks CF on each poll for `pending_ns_change` rows, so polling
|
|
189
|
+
// both reports state AND triggers the flip to `provisioning`.
|
|
190
|
+
let lastStatus = '';
|
|
191
|
+
const schedule = [
|
|
192
|
+
{ count: 30, intervalMs: 10_000 },
|
|
193
|
+
{ count: 60, intervalMs: 30_000 },
|
|
194
|
+
];
|
|
195
|
+
info(`Watching ${domain} — press Ctrl-C to stop.`);
|
|
196
|
+
for (const phase of schedule) {
|
|
197
|
+
for (let i = 0; i < phase.count; i++) {
|
|
198
|
+
const res = await sdkDomain.getDomainStatus(config.api_key, orgId, domain);
|
|
199
|
+
if (res.status !== lastStatus) {
|
|
200
|
+
renderStatus(res);
|
|
201
|
+
lastStatus = res.status;
|
|
202
|
+
}
|
|
203
|
+
if (TERMINAL_STATUSES.has(res.status))
|
|
204
|
+
return;
|
|
205
|
+
await new Promise(r => setTimeout(r, phase.intervalMs));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
info(`Watch budget exhausted (~35 min). Rerun "myapi domain status ${domain} --watch" to keep waiting.`);
|
|
209
|
+
}
|
|
210
|
+
function renderStatus(res) {
|
|
136
211
|
info(`Domain: ${res.domain}`);
|
|
137
212
|
info(`Status: ${res.status}`);
|
|
138
213
|
if (res.expires_at)
|
|
139
214
|
info(`Expires: ${formatDate(res.expires_at)}`);
|
|
140
|
-
if (res.status === '
|
|
215
|
+
if (res.status === 'pending_ns_change') {
|
|
216
|
+
info('Waiting for you to change nameservers at your current registrar. Each poll re-checks Cloudflare.');
|
|
217
|
+
}
|
|
218
|
+
if (res.status === 'active') {
|
|
141
219
|
info('Note: if recently activated, the SSL certificate may still be provisioning — allow a few minutes before the site is reachable over HTTPS.');
|
|
220
|
+
}
|
|
142
221
|
}
|
|
143
222
|
export async function settings(domainArg, flags) {
|
|
144
223
|
const config = requireConfig();
|
|
@@ -164,6 +243,157 @@ export async function updateSettings(domainArg, flags) {
|
|
|
164
243
|
const res = await sdkDomain.updateDomainSettings(config.api_key, orgId, domain, payload);
|
|
165
244
|
success(`Updated settings for ${domain}!\n${JSON.stringify(res, null, 2)}`);
|
|
166
245
|
}
|
|
246
|
+
// ── DNS records sub-surface (`myapi domain records ...`) ────────────────────
|
|
247
|
+
const ALLOWED_RECORD_TYPES = new Set(['A', 'AAAA', 'CNAME', 'MX', 'TXT']);
|
|
248
|
+
function parseRecordType(raw, where) {
|
|
249
|
+
if (typeof raw !== 'string' || !ALLOWED_RECORD_TYPES.has(raw.toUpperCase())) {
|
|
250
|
+
error(`${where}: --type must be one of A, AAAA, CNAME, MX, TXT (got: ${raw ?? '(missing)'}).`);
|
|
251
|
+
}
|
|
252
|
+
return raw.toUpperCase();
|
|
253
|
+
}
|
|
254
|
+
function recordRow(r) {
|
|
255
|
+
return {
|
|
256
|
+
id: r.id,
|
|
257
|
+
type: r.type,
|
|
258
|
+
name: r.name,
|
|
259
|
+
content: r.content,
|
|
260
|
+
ttl: r.ttl === 1 ? 'auto' : String(r.ttl),
|
|
261
|
+
priority: r.priority ?? '—',
|
|
262
|
+
proxied: r.proxied === null ? '—' : (r.proxied ? 'yes' : 'no'),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
async function recordsList(domainArg, flags) {
|
|
266
|
+
const config = requireConfig();
|
|
267
|
+
const orgId = requireOrg(flags, config, 'myapi domain records list <domain> [--type <T>] [--org <id>]');
|
|
268
|
+
const domain = requireDomain(domainArg, flags, config, 'myapi domain records list <domain> [--type <T>] [--org <id>]');
|
|
269
|
+
const type = flags.type ? parseRecordType(flags.type, 'records list') : undefined;
|
|
270
|
+
const records = await sdkDomain.listDnsRecords(config.api_key, orgId, domain, type);
|
|
271
|
+
if (flags.json) {
|
|
272
|
+
printJson(records);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
printTable(records.map(recordRow), { flags, empty: 'No records in this zone.' });
|
|
276
|
+
}
|
|
277
|
+
async function recordsGet(domainArg, recordId, flags) {
|
|
278
|
+
const config = requireConfig();
|
|
279
|
+
const orgId = requireOrg(flags, config, 'myapi domain records get <domain> <record-id> [--org <id>]');
|
|
280
|
+
const domain = requireDomain(domainArg, flags, config, 'myapi domain records get <domain> <record-id> [--org <id>]');
|
|
281
|
+
if (!recordId)
|
|
282
|
+
error('Missing record id.\nUsage: myapi domain records get <domain> <record-id>');
|
|
283
|
+
const r = await sdkDomain.getDnsRecord(config.api_key, orgId, domain, recordId);
|
|
284
|
+
if (flags.json) {
|
|
285
|
+
printJson(r);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
printJson(r);
|
|
289
|
+
}
|
|
290
|
+
async function recordsCreate(domainArg, flags) {
|
|
291
|
+
const config = requireConfig();
|
|
292
|
+
const orgId = requireOrg(flags, config, 'myapi domain records create <domain> --type <T> --name <n> --content <c> [...]');
|
|
293
|
+
const domain = requireDomain(domainArg, flags, config, 'myapi domain records create <domain> --type <T> --name <n> --content <c> [...]');
|
|
294
|
+
const type = parseRecordType(flags.type, 'records create');
|
|
295
|
+
const name = typeof flags.name === 'string' ? flags.name : '';
|
|
296
|
+
const content = typeof flags.content === 'string' ? flags.content : '';
|
|
297
|
+
if (!name)
|
|
298
|
+
error('Missing --name. For apex, pass --name=@ or --name=<domain>.');
|
|
299
|
+
if (!content)
|
|
300
|
+
error('Missing --content.');
|
|
301
|
+
const input = { type, name, content };
|
|
302
|
+
if (typeof flags.ttl === 'number')
|
|
303
|
+
input.ttl = flags.ttl;
|
|
304
|
+
if (typeof flags.priority === 'number')
|
|
305
|
+
input.priority = flags.priority;
|
|
306
|
+
if (typeof flags.proxied === 'boolean')
|
|
307
|
+
input.proxied = flags.proxied;
|
|
308
|
+
if (type === 'MX' && input.priority === undefined)
|
|
309
|
+
error('Missing --priority. Required for MX records (typical value: 10).');
|
|
310
|
+
const r = await sdkDomain.createDnsRecord(config.api_key, orgId, domain, input);
|
|
311
|
+
success(`Created ${r.type} record ${r.id}`);
|
|
312
|
+
printJson(r);
|
|
313
|
+
}
|
|
314
|
+
async function recordsUpdate(domainArg, recordId, flags) {
|
|
315
|
+
const config = requireConfig();
|
|
316
|
+
const orgId = requireOrg(flags, config, 'myapi domain records update <domain> <record-id> [--content ...] [...]');
|
|
317
|
+
const domain = requireDomain(domainArg, flags, config, 'myapi domain records update <domain> <record-id> [--content ...] [...]');
|
|
318
|
+
if (!recordId)
|
|
319
|
+
error('Missing record id.\nUsage: myapi domain records update <domain> <record-id> [flags]');
|
|
320
|
+
const patch = {};
|
|
321
|
+
if (typeof flags.name === 'string')
|
|
322
|
+
patch.name = flags.name;
|
|
323
|
+
if (typeof flags.content === 'string')
|
|
324
|
+
patch.content = flags.content;
|
|
325
|
+
if (typeof flags.ttl === 'number')
|
|
326
|
+
patch.ttl = flags.ttl;
|
|
327
|
+
if (typeof flags.priority === 'number')
|
|
328
|
+
patch.priority = flags.priority;
|
|
329
|
+
if (typeof flags.proxied === 'boolean')
|
|
330
|
+
patch.proxied = flags.proxied;
|
|
331
|
+
if (Object.keys(patch).length === 0)
|
|
332
|
+
error('Nothing to update. Pass at least one of --name, --content, --ttl, --priority, --proxied.');
|
|
333
|
+
const r = await sdkDomain.updateDnsRecord(config.api_key, orgId, domain, recordId, patch);
|
|
334
|
+
success(`Updated record ${r.id}`);
|
|
335
|
+
printJson(r);
|
|
336
|
+
}
|
|
337
|
+
async function recordsDelete(domainArg, recordId, flags) {
|
|
338
|
+
const config = requireConfig();
|
|
339
|
+
const orgId = requireOrg(flags, config, 'myapi domain records delete <domain> <record-id> [--yes] [--org <id>]');
|
|
340
|
+
const domain = requireDomain(domainArg, flags, config, 'myapi domain records delete <domain> <record-id> [--yes] [--org <id>]');
|
|
341
|
+
if (!recordId)
|
|
342
|
+
error('Missing record id.\nUsage: myapi domain records delete <domain> <record-id> [--yes]');
|
|
343
|
+
if (!flags.yes) {
|
|
344
|
+
info(`About to delete record ${recordId} from ${domain}. Re-run with --yes to confirm.`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
await sdkDomain.deleteDnsRecord(config.api_key, orgId, domain, recordId);
|
|
348
|
+
success(`Deleted record ${recordId}`);
|
|
349
|
+
}
|
|
350
|
+
const RECORDS_HELP = `Usage: myapi domain records <subcommand> <domain> [...]
|
|
351
|
+
|
|
352
|
+
Subcommands:
|
|
353
|
+
list <domain> [--type A|AAAA|CNAME|MX|TXT]
|
|
354
|
+
get <domain> <record-id>
|
|
355
|
+
create <domain> --type <T> --name <n> --content <c> [--ttl <n>]
|
|
356
|
+
[--priority <n>] [--proxied]
|
|
357
|
+
update <domain> <record-id> [--content ...] [--ttl ...] [--priority ...] [--proxied]
|
|
358
|
+
delete <domain> <record-id> [--yes]
|
|
359
|
+
|
|
360
|
+
Name normalization:
|
|
361
|
+
--name=@ Apex (e.g. example.com)
|
|
362
|
+
--name=mail Host-only — backend appends the zone
|
|
363
|
+
--name=mail.example.com FQDN
|
|
364
|
+
--name="" Apex (empty == @)
|
|
365
|
+
|
|
366
|
+
Notes:
|
|
367
|
+
--ttl defaults to 1 (Cloudflare "automatic"). Explicit range: [60, 86400].
|
|
368
|
+
--priority is required for MX (typical: 10).
|
|
369
|
+
--proxied (CF "orange-cloud") applies to A/AAAA/CNAME only.
|
|
370
|
+
|
|
371
|
+
Examples:
|
|
372
|
+
Fix the broken SPF on x80security.com:
|
|
373
|
+
myapi domain records list x80security.com --type TXT
|
|
374
|
+
myapi domain records delete x80security.com <bad-record-id> --yes
|
|
375
|
+
myapi domain records create x80security.com --type TXT --name @ \\
|
|
376
|
+
--content 'v=spf1 include:_spf.mailersend.net include:_spf.google.com ~all'
|
|
377
|
+
myapi domain records create x80security.com --type TXT --name @ \\
|
|
378
|
+
--content 'google-site-verification=...'`;
|
|
379
|
+
async function recordsRun(sub, args, flags) {
|
|
380
|
+
if (!sub || (flags.help && !sub)) {
|
|
381
|
+
info(RECORDS_HELP);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (flags.help) {
|
|
385
|
+
info(RECORDS_HELP);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
switch (sub) {
|
|
389
|
+
case 'list': return recordsList(args[0], flags);
|
|
390
|
+
case 'get': return recordsGet(args[0], args[1], flags);
|
|
391
|
+
case 'create': return recordsCreate(args[0], flags);
|
|
392
|
+
case 'update': return recordsUpdate(args[0], args[1], flags);
|
|
393
|
+
case 'delete': return recordsDelete(args[0], args[1], flags);
|
|
394
|
+
default: error(`Unknown records subcommand: ${sub}. Run "myapi domain records --help".`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
167
397
|
// ── Dispatcher ───────────────────────────────────────────────────────────────
|
|
168
398
|
const SUBCOMMAND_USAGE = {
|
|
169
399
|
'list': `myapi domain list [--filter=all|unassigned|org] [--org <id>] [--json]
|
|
@@ -203,6 +433,20 @@ Registrant (required at every register call):
|
|
|
203
433
|
|
|
204
434
|
After registering: myapi domain assign <domain>
|
|
205
435
|
DNS propagation takes a few minutes — track it with: myapi domain status <domain>`,
|
|
436
|
+
'import': `myapi domain import <domain> [--org <id>] [--json]
|
|
437
|
+
|
|
438
|
+
Bring your own domain (BYOD). Snapshots existing DNS records via a best-effort
|
|
439
|
+
public probe and creates a Cloudflare zone for the domain. Returns the
|
|
440
|
+
nameservers you need to set at your current registrar.
|
|
441
|
+
|
|
442
|
+
No registrar credentials needed — works with any registrar (Namecheap, GoDaddy,
|
|
443
|
+
Cloudflare Registrar, etc.).
|
|
444
|
+
|
|
445
|
+
After running:
|
|
446
|
+
1. Verify the preserved-records table covers your MX/SPF/DKIM/DMARC etc.
|
|
447
|
+
2. At your current registrar, change the nameservers to the values returned.
|
|
448
|
+
3. Wait for propagation. Use "myapi domain status <domain> --watch" to track
|
|
449
|
+
activation — the backend live-checks Cloudflare each time you poll.`,
|
|
206
450
|
'renew': `myapi domain renew <domain> [--org <id>]
|
|
207
451
|
|
|
208
452
|
Renews a registered domain for one more registration period (typically 1 year).
|
|
@@ -217,11 +461,17 @@ https://<domain> after DNS propagation completes.
|
|
|
217
461
|
|
|
218
462
|
To transfer to a different org: unassign first, then assign to the new org.`,
|
|
219
463
|
'unassign': 'myapi domain unassign <domain> [--org <id>]',
|
|
220
|
-
'status':
|
|
464
|
+
'status': `myapi domain status <domain> [--org <id>] [--watch] [--json]
|
|
465
|
+
|
|
466
|
+
--watch Poll until the domain reaches a terminal state (active / failed /
|
|
467
|
+
expired). Backoff: 10s × 30 then 30s × 60 (~35 min budget). Useful
|
|
468
|
+
after "myapi domain import" — each poll also live-checks Cloudflare,
|
|
469
|
+
so polling drives the flip from pending_ns_change → provisioning.`,
|
|
221
470
|
'settings': `myapi domain settings <domain> [--org <id>]
|
|
222
471
|
|
|
223
472
|
Gets current edge/CDN settings (security level, browser check, cache).
|
|
224
473
|
Use "myapi domain update-settings" to change them.`,
|
|
474
|
+
'records': RECORDS_HELP,
|
|
225
475
|
'update-settings': `myapi domain update-settings <domain> [--security=<level>] [--browser-check=on|off] [--purge-cache] [--org <id>]
|
|
226
476
|
|
|
227
477
|
--security=<level> essentially_off | low | medium | high | under_attack (default: medium)
|
|
@@ -239,12 +489,14 @@ Subcommands:
|
|
|
239
489
|
list List domains
|
|
240
490
|
check Check domain availability
|
|
241
491
|
register Register a domain
|
|
492
|
+
import Bring your own domain (BYOD) — registrar-agnostic
|
|
242
493
|
renew Renew a registered domain for another period
|
|
243
494
|
assign Assign domain to an org
|
|
244
495
|
unassign Unassign domain from its current org
|
|
245
496
|
status Get domain status
|
|
246
497
|
settings Get edge/CDN settings
|
|
247
498
|
update-settings Update edge/CDN settings
|
|
499
|
+
records Manage DNS records in the zone (list / get / create / update / delete)
|
|
248
500
|
|
|
249
501
|
Tip: Set defaults with "myapi config set-org <id>" / "set-domain <domain>" to skip flags on every command.`);
|
|
250
502
|
return;
|
|
@@ -261,12 +513,14 @@ Tip: Set defaults with "myapi config set-org <id>" / "set-domain <domain>" to sk
|
|
|
261
513
|
case 'list': return list(flags);
|
|
262
514
|
case 'check': return check(args[0], flags);
|
|
263
515
|
case 'register': return register(args[0], flags);
|
|
516
|
+
case 'import': return importCmd(args[0], flags);
|
|
264
517
|
case 'renew': return renew(args[0], flags);
|
|
265
518
|
case 'assign': return assign(args[0], flags);
|
|
266
519
|
case 'unassign': return unassign(args[0], flags);
|
|
267
520
|
case 'status': return status(args[0], flags);
|
|
268
521
|
case 'settings': return settings(args[0], flags);
|
|
269
522
|
case 'update-settings': return updateSettings(args[0], flags);
|
|
523
|
+
case 'records': return recordsRun(args[0], args.slice(1), flags);
|
|
270
524
|
default: error(`Unknown subcommand: ${subcommand}. Run "myapi domain --help" for a list of valid subcommands.`);
|
|
271
525
|
}
|
|
272
526
|
}
|
package/dist/index.js
CHANGED
|
@@ -82,6 +82,13 @@ const ERROR_MESSAGES = {
|
|
|
82
82
|
INSUFFICIENT_BALANCE: 'Insufficient balance. Run: myapi billing topup <amount>',
|
|
83
83
|
INVALID_AMOUNT: 'Amount out of range. Maximum single top-up is $100. Run: myapi billing topup <amount>',
|
|
84
84
|
SERVICE_NOT_LAUNCHED: 'This service is disabled pre-launch. Track availability via: myapi status',
|
|
85
|
+
RECORD_NOT_FOUND: 'DNS record not found in this zone.',
|
|
86
|
+
INVALID_RECORD_TYPE: 'Unsupported record type. Allowed: A, AAAA, CNAME, MX, TXT.',
|
|
87
|
+
MX_PRIORITY_REQUIRED: 'MX records require --priority (typical value: 10).',
|
|
88
|
+
INVALID_TTL: 'Invalid TTL. Use the auto sentinel (1) or a value between 60 and 86400 seconds.',
|
|
89
|
+
INVALID_RECORD_CONTENT: 'Invalid record content for this type.',
|
|
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.',
|
|
85
92
|
// invalid_json_response intentionally absent — the SDK's MyApiError now
|
|
86
93
|
// builds a useful detailed message for that case (status + URL + body
|
|
87
94
|
// snippet), and friendlyError(err.code) would override it.
|
|
@@ -50,9 +50,15 @@ Resolution at register time (highest wins): `--registrant-json` → per-field fl
|
|
|
50
50
|
| `myapi domain list [--filter all\|unassigned\|org]` | List domains in your account |
|
|
51
51
|
| `myapi domain assign <domain>` | Assign domain to your default (or `--org`) org |
|
|
52
52
|
| `myapi domain unassign <domain>` | Remove domain from its org |
|
|
53
|
-
| `myapi domain
|
|
53
|
+
| `myapi domain import <domain>` | Bring-your-own-domain. Snapshots current DNS, returns nameservers to set at your existing registrar — no registrar credentials needed |
|
|
54
|
+
| `myapi domain status <domain> [--watch]` | Registration + DNS propagation status. `--watch` polls until terminal (10s × 30 → 30s × 60) |
|
|
54
55
|
| `myapi domain settings <domain>` | View edge/CDN settings |
|
|
55
56
|
| `myapi domain update-settings <domain>` | Change security level, browser check, purge cache |
|
|
57
|
+
| `myapi domain records list <domain> [--type T]` | List DNS records in the zone |
|
|
58
|
+
| `myapi domain records get <domain> <id>` | Fetch one record |
|
|
59
|
+
| `myapi domain records create <domain> --type T --name n --content c` | Create a record (priority required for MX) |
|
|
60
|
+
| `myapi domain records update <domain> <id> [--content c] [...]` | Update a record (type cannot change) |
|
|
61
|
+
| `myapi domain records delete <domain> <id> --yes` | Delete a record |
|
|
56
62
|
<!-- generated:end -->
|
|
57
63
|
|
|
58
64
|
## Examples
|
|
@@ -73,6 +79,18 @@ myapi domain renew example.com
|
|
|
73
79
|
# Tune the edge for AI bot traffic
|
|
74
80
|
myapi domain update-settings example.com \
|
|
75
81
|
--security=essentially_off --browser-check=off --purge-cache
|
|
82
|
+
|
|
83
|
+
# Bring an existing domain — works with any registrar (Namecheap, GoDaddy, CF Registrar, etc.)
|
|
84
|
+
myapi domain import example.com
|
|
85
|
+
# → Returns nameservers; set them at your current registrar.
|
|
86
|
+
myapi domain status example.com --watch
|
|
87
|
+
# → Polls until active. Backend live-checks Cloudflare each poll.
|
|
88
|
+
|
|
89
|
+
# Fix a record after import (e.g. clean up SPF)
|
|
90
|
+
myapi domain records list example.com --type TXT
|
|
91
|
+
myapi domain records delete example.com <bad-id> --yes
|
|
92
|
+
myapi domain records create example.com --type TXT --name @ \
|
|
93
|
+
--content 'v=spf1 include:_spf.google.com ~all'
|
|
76
94
|
```
|
|
77
95
|
|
|
78
96
|
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.
|
|
4
|
+
"version": "1.2.2",
|
|
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.
|
|
30
|
+
"@myapihq/sdk": "^1.2.2",
|
|
31
31
|
"omelette": "^0.4.17"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { FlagSchema } from '../flags.js';
|
|
2
|
-
import { type Flags } from '../helpers.js';
|
|
3
|
-
import type { Exposes } from '../exposes.js';
|
|
4
|
-
export declare const EXPOSES: Exposes;
|
|
5
|
-
export declare const SCHEMA: FlagSchema;
|
|
6
|
-
export declare const VERIFY_HELP = "Usage: myapi verify <email> [--org <id>] [--json]\n\nSync single-address email verification. Cheap layer only: syntax + DNS +\nMicrosoft GetCredentialType. Returns a verdict in <1s for ~50% of inputs;\nthe rest get verdict='unknown' with smtp_recommended=true.\n\nVerdicts:\n deliverable high-confidence \u2014 the address accepts mail\n undeliverable high-confidence \u2014 syntax bad, DNS missing, or Microsoft rejects\n unknown not enough signal; consider an SMTP probe (not in this API)\n\nUse --json for the full check breakdown (syntax / DNS / Microsoft probes).\n";
|
|
7
|
-
export declare function run(arg: string | undefined, _rest: string[], flags?: Flags): Promise<void>;
|
package/dist/commands/verify.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { email as sdkEmail } from '@myapihq/sdk';
|
|
2
|
-
import { requireConfig } from '../config.js';
|
|
3
|
-
import { error, info, printJson } from '../output.js';
|
|
4
|
-
import { requireOrg } from '../helpers.js';
|
|
5
|
-
export const EXPOSES = [
|
|
6
|
-
'POST /email/orgs/{org_id}/verify',
|
|
7
|
-
];
|
|
8
|
-
export const SCHEMA = {
|
|
9
|
-
org: 'string',
|
|
10
|
-
};
|
|
11
|
-
export const VERIFY_HELP = `Usage: myapi verify <email> [--org <id>] [--json]
|
|
12
|
-
|
|
13
|
-
Sync single-address email verification. Cheap layer only: syntax + DNS +
|
|
14
|
-
Microsoft GetCredentialType. Returns a verdict in <1s for ~50% of inputs;
|
|
15
|
-
the rest get verdict='unknown' with smtp_recommended=true.
|
|
16
|
-
|
|
17
|
-
Verdicts:
|
|
18
|
-
deliverable high-confidence — the address accepts mail
|
|
19
|
-
undeliverable high-confidence — syntax bad, DNS missing, or Microsoft rejects
|
|
20
|
-
unknown not enough signal; consider an SMTP probe (not in this API)
|
|
21
|
-
|
|
22
|
-
Use --json for the full check breakdown (syntax / DNS / Microsoft probes).
|
|
23
|
-
`;
|
|
24
|
-
export async function run(arg, _rest, flags = {}) {
|
|
25
|
-
if (flags.help) {
|
|
26
|
-
info(VERIFY_HELP);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (!arg)
|
|
30
|
-
error('Missing required argument <email>.\nUsage: myapi verify <email> [--org <id>] [--json]');
|
|
31
|
-
const config = requireConfig();
|
|
32
|
-
const orgId = requireOrg(flags, config, 'myapi verify <email> [--org <id>]');
|
|
33
|
-
const res = await sdkEmail.verifyEmail(config.api_key, orgId, arg);
|
|
34
|
-
if (flags.json) {
|
|
35
|
-
printJson(res);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
// Compact human render. The verdict + confidence is the headline; the
|
|
39
|
-
// checks block is verbose enough that we hide it behind --json.
|
|
40
|
-
const verdictLabel = res.verdict === 'deliverable' ? '✓ deliverable'
|
|
41
|
-
: res.verdict === 'undeliverable' ? '✗ undeliverable'
|
|
42
|
-
: '? unknown';
|
|
43
|
-
info(`Email: ${res.email}`);
|
|
44
|
-
info(`Verdict: ${verdictLabel} (confidence ${res.confidence.toFixed(2)})`);
|
|
45
|
-
info(`SMTP next: ${res.smtp_recommended ? 'yes — consider an SMTP probe' : 'no — verdict is definitive'}`);
|
|
46
|
-
info(`Took: ${res.elapsed_ms}ms`);
|
|
47
|
-
if (res.checks.syntax.detail)
|
|
48
|
-
info(`Syntax: ${res.checks.syntax.detail}`);
|
|
49
|
-
if (res.checks.dns && res.checks.dns.mx_records && res.checks.dns.mx_records.length > 0) {
|
|
50
|
-
const mx = res.checks.dns.mx_records.filter(Boolean);
|
|
51
|
-
if (mx.length > 0)
|
|
52
|
-
info(`MX: ${mx.slice(0, 3).join(', ')}${mx.length > 3 ? ` (+${mx.length - 3} more)` : ''}`);
|
|
53
|
-
}
|
|
54
|
-
if (res.checks.microsoft)
|
|
55
|
-
info(`Microsoft: ${res.checks.microsoft.verdict}`);
|
|
56
|
-
}
|