@myapihq/cli 1.2.1 → 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.
- package/dist/commands/domain.js +162 -0
- package/dist/index.js +7 -0
- package/dist/skills/my-domain-api/SKILL.md +19 -1
- package/package.json +2 -2
package/dist/commands/domain.js
CHANGED
|
@@ -38,6 +38,14 @@ export const SCHEMA = {
|
|
|
38
38
|
'registrant-postal-code': 'string',
|
|
39
39
|
'registrant-country': 'string',
|
|
40
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',
|
|
41
49
|
};
|
|
42
50
|
export async function check(domainArg, flags) {
|
|
43
51
|
const config = requireConfig();
|
|
@@ -235,6 +243,157 @@ export async function updateSettings(domainArg, flags) {
|
|
|
235
243
|
const res = await sdkDomain.updateDomainSettings(config.api_key, orgId, domain, payload);
|
|
236
244
|
success(`Updated settings for ${domain}!\n${JSON.stringify(res, null, 2)}`);
|
|
237
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
|
+
}
|
|
238
397
|
// ── Dispatcher ───────────────────────────────────────────────────────────────
|
|
239
398
|
const SUBCOMMAND_USAGE = {
|
|
240
399
|
'list': `myapi domain list [--filter=all|unassigned|org] [--org <id>] [--json]
|
|
@@ -312,6 +471,7 @@ To transfer to a different org: unassign first, then assign to the new org.`,
|
|
|
312
471
|
|
|
313
472
|
Gets current edge/CDN settings (security level, browser check, cache).
|
|
314
473
|
Use "myapi domain update-settings" to change them.`,
|
|
474
|
+
'records': RECORDS_HELP,
|
|
315
475
|
'update-settings': `myapi domain update-settings <domain> [--security=<level>] [--browser-check=on|off] [--purge-cache] [--org <id>]
|
|
316
476
|
|
|
317
477
|
--security=<level> essentially_off | low | medium | high | under_attack (default: medium)
|
|
@@ -336,6 +496,7 @@ Subcommands:
|
|
|
336
496
|
status Get domain status
|
|
337
497
|
settings Get edge/CDN settings
|
|
338
498
|
update-settings Update edge/CDN settings
|
|
499
|
+
records Manage DNS records in the zone (list / get / create / update / delete)
|
|
339
500
|
|
|
340
501
|
Tip: Set defaults with "myapi config set-org <id>" / "set-domain <domain>" to skip flags on every command.`);
|
|
341
502
|
return;
|
|
@@ -359,6 +520,7 @@ Tip: Set defaults with "myapi config set-org <id>" / "set-domain <domain>" to sk
|
|
|
359
520
|
case 'status': return status(args[0], flags);
|
|
360
521
|
case 'settings': return settings(args[0], flags);
|
|
361
522
|
case 'update-settings': return updateSettings(args[0], flags);
|
|
523
|
+
case 'records': return recordsRun(args[0], args.slice(1), flags);
|
|
362
524
|
default: error(`Unknown subcommand: ${subcommand}. Run "myapi domain --help" for a list of valid subcommands.`);
|
|
363
525
|
}
|
|
364
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": {
|