@myapihq/cli 1.0.29 → 1.0.39
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/auth.js +15 -0
- package/dist/commands/billing.js +29 -28
- package/dist/commands/domain.d.ts +1 -0
- package/dist/commands/domain.js +33 -13
- package/dist/commands/org.js +9 -4
- package/dist/commands/setup.d.ts +2 -1
- package/dist/commands/setup.js +58 -5
- package/dist/commands/update.js +5 -1
- package/dist/config.js +1 -1
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +11 -0
- package/package.json +1 -1
- package/scripts/copy-skills.js +3 -26
- package/src/commands/auth.ts +16 -0
- package/src/commands/billing.ts +36 -36
- package/src/commands/domain.ts +33 -16
- package/src/commands/org.ts +14 -9
- package/src/commands/setup.ts +60 -5
- package/src/commands/update.ts +5 -1
- package/src/config.ts +1 -1
- package/src/utils.ts +11 -0
- package/dist/skills/my-api-hq.md +0 -116
- package/dist/skills/my-domain-api.md +0 -83
- package/dist/skills/my-funnel-api.md +0 -35
- package/src/skills/my-api-hq.md +0 -116
- package/src/skills/my-domain-api.md +0 -83
- package/src/skills/my-funnel-api.md +0 -35
package/src/commands/billing.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
1
2
|
import { hq } from '@myapihq/sdk';
|
|
2
3
|
import { requireConfig } from '../config.js';
|
|
3
4
|
import { success, error, printTable, info, printJson } from '../output.js';
|
|
5
|
+
import { formatDate } from '../utils.js';
|
|
4
6
|
|
|
5
7
|
export async function balance(flags: Record<string, string | boolean>) {
|
|
6
8
|
if (flags.help) {
|
|
@@ -9,12 +11,11 @@ export async function balance(flags: Record<string, string | boolean>) {
|
|
|
9
11
|
}
|
|
10
12
|
const config = requireConfig();
|
|
11
13
|
const result = await hq.getBalance(config.api_key);
|
|
12
|
-
|
|
13
|
-
const bal = result.balance_display || `$${(result.balance_cents / 100).toFixed(2)}`;
|
|
14
|
-
const cred = result.credits_display || `$${((result.credits_cents || 0) / 100).toFixed(2)}`;
|
|
14
|
+
|
|
15
15
|
const pm = result.has_payment_method ? 'yes' : 'no';
|
|
16
|
-
|
|
17
|
-
info(`
|
|
16
|
+
const accountType = config.is_anonymous ? 'anonymous' : `registered (${config.email ?? ''})`;
|
|
17
|
+
info(`Account: ${accountType}`);
|
|
18
|
+
info(`Balance: ${result.balance_display} | Credits: ${result.credits_display} | Payment method: ${pm}`);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export async function history(flags: Record<string, string | boolean>) {
|
|
@@ -24,52 +25,51 @@ export async function history(flags: Record<string, string | boolean>) {
|
|
|
24
25
|
}
|
|
25
26
|
const config = requireConfig();
|
|
26
27
|
const items = await hq.getBillingHistory(config.api_key);
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
if (flags.json) {
|
|
29
30
|
printJson(items);
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Type: item.type || 'unknown',
|
|
46
|
-
Amount: amountStr,
|
|
47
|
-
Status: item.status ? item.status.charAt(0).toUpperCase() + item.status.slice(1) : '',
|
|
48
|
-
Date: dateStr
|
|
49
|
-
};
|
|
50
|
-
});
|
|
51
|
-
|
|
33
|
+
|
|
34
|
+
if (items.length === 0) {
|
|
35
|
+
info('No transactions yet.');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const formattedItems = items.map(item => ({
|
|
40
|
+
Type: item.type || 'unknown',
|
|
41
|
+
Amount: item.amount_display,
|
|
42
|
+
Status: item.status ? item.status.charAt(0).toUpperCase() + item.status.slice(1) : '',
|
|
43
|
+
Date: formatDate(item.created_at),
|
|
44
|
+
}));
|
|
45
|
+
|
|
52
46
|
printTable(formattedItems as unknown as Record<string, unknown>[]);
|
|
53
47
|
}
|
|
54
48
|
|
|
55
49
|
export async function topup(amountStr: string, flags: Record<string, string | boolean>) {
|
|
56
50
|
if (flags.help) {
|
|
57
|
-
info('Usage: myapi billing topup <
|
|
51
|
+
info('Usage: myapi billing topup <amount>\n\nAmount is in whole dollars (e.g. "10" charges $10).\nUse --yes to skip confirmation.\nExample: myapi billing topup 10');
|
|
58
52
|
return;
|
|
59
53
|
}
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
const amount = Math.round(parseFloat(amountStr));
|
|
55
|
+
if (!amountStr || isNaN(amount) || amount <= 0) {
|
|
56
|
+
error("Amount must be a positive whole number of dollars (e.g. myapi billing topup 10)");
|
|
62
57
|
return;
|
|
63
58
|
}
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
|
|
60
|
+
if (!flags.yes && !flags.y && amount >= 50) {
|
|
61
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
62
|
+
const ans = await new Promise<string>(resolve => rl.question(`› Charge $${amount} to your saved payment method? (y/N) `, resolve));
|
|
63
|
+
rl.close();
|
|
64
|
+
if (ans.trim().toLowerCase() !== 'y' && ans.trim().toLowerCase() !== 'yes') {
|
|
65
|
+
info('Cancelled.');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
const config = requireConfig();
|
|
71
|
-
const result = await hq.topUp(config.api_key,
|
|
72
|
-
success(`Top up successful! New balance:
|
|
71
|
+
const result = await hq.topUp(config.api_key, amount);
|
|
72
|
+
success(`Top up successful! New balance: ${result.new_balance_display}`);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
export async function setup(flags: Record<string, string | boolean>) {
|
package/src/commands/domain.ts
CHANGED
|
@@ -6,12 +6,13 @@ export async function check(domainArg: string, flags: Record<string, string | bo
|
|
|
6
6
|
const config = requireConfig();
|
|
7
7
|
const orgId = (flags.org as string) || config.default_org;
|
|
8
8
|
const domain = domainArg || (config.default_domain as string);
|
|
9
|
-
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain check <domain> --org <id>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
10
|
-
const res = await sdkDomain.checkDomain(config.api_key, orgId, domain);
|
|
9
|
+
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain check <domain> --org <id>\n(Or set defaults via: myapi auth config set-org <id> / set-domain <domain>)");
|
|
10
|
+
const res = await sdkDomain.checkDomain(config.api_key, orgId, domain) as { available: boolean; price_cents: number; price_display?: string; message?: string };
|
|
11
11
|
if (res.available) {
|
|
12
|
-
|
|
12
|
+
const price = res.price_display || `$${(res.price_cents / 100).toFixed(2)}`;
|
|
13
|
+
info(`${domain} — Available ✓ ${price}/yr`);
|
|
13
14
|
} else {
|
|
14
|
-
info(
|
|
15
|
+
info(`${domain} — ${res.message || 'Not available'} ✗`);
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -19,7 +20,7 @@ export async function register(domainArg: string, flags: Record<string, string |
|
|
|
19
20
|
const config = requireConfig();
|
|
20
21
|
const orgId = (flags.org as string) || config.default_org;
|
|
21
22
|
const domain = domainArg || (config.default_domain as string);
|
|
22
|
-
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain register <domain> --org <id> [--years <num>]\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
23
|
+
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain register <domain> --org <id> [--years <num>]\n(Or set defaults via: myapi auth config set-org <id> / set-domain <domain>)");
|
|
23
24
|
const years = parseInt(flags.years as string) || 1;
|
|
24
25
|
const res = await sdkDomain.registerDomain(config.api_key, orgId, domain, years);
|
|
25
26
|
success(`Registered ${domain}!\n${JSON.stringify(res, null, 2)}`);
|
|
@@ -29,7 +30,7 @@ export async function importDomain(domainArg: string, flags: Record<string, stri
|
|
|
29
30
|
const config = requireConfig();
|
|
30
31
|
const orgId = (flags.org as string) || config.default_org;
|
|
31
32
|
const domain = domainArg || (config.default_domain as string);
|
|
32
|
-
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain import <domain> --org <id> [--namecheap-user <user> --namecheap-key <key>]\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
33
|
+
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain import <domain> --org <id> [--namecheap-user <user> --namecheap-key <key>]\n(Or set defaults via: myapi auth config set-org <id> / set-domain <domain>)");
|
|
33
34
|
const payload: { domain: string; namecheap_api_user?: string; namecheap_api_key?: string } = { domain };
|
|
34
35
|
if (flags['namecheap-user']) payload.namecheap_api_user = flags['namecheap-user'] as string;
|
|
35
36
|
if (flags['namecheap-key']) payload.namecheap_api_key = flags['namecheap-key'] as string;
|
|
@@ -40,7 +41,7 @@ export async function importDomain(domainArg: string, flags: Record<string, stri
|
|
|
40
41
|
export async function list(flags: Record<string, string | boolean>) {
|
|
41
42
|
const config = requireConfig();
|
|
42
43
|
const orgId = (flags.org as string) || config.default_org;
|
|
43
|
-
if (!orgId) error("Missing required arguments.\nUsage: myapi domain list --org <id> [--filter=all|unassigned|org]\n(Or set a default org via: myapi config set-org <id>)");
|
|
44
|
+
if (!orgId) error("Missing required arguments.\nUsage: myapi domain list --org <id> [--filter=all|unassigned|org]\n(Or set a default org via: myapi auth config set-org <id>)");
|
|
44
45
|
const filter = flags.filter as 'all' | 'unassigned' | 'org' | undefined;
|
|
45
46
|
const domains = await sdkDomain.listDomains(config.api_key, orgId, filter);
|
|
46
47
|
printTable(domains as unknown as Record<string, unknown>[]);
|
|
@@ -51,8 +52,8 @@ export async function assign(domainArg: string, flags: Record<string, string | b
|
|
|
51
52
|
const orgId = (flags.org as string) || config.default_org;
|
|
52
53
|
const domain = domainArg || (config.default_domain as string);
|
|
53
54
|
const targetOrgId = flags.target as string;
|
|
54
|
-
if (!orgId || !domain || !targetOrgId) error("Missing required arguments.\nUsage: myapi domain assign <domain> --org <id> --target <target_org_id>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
55
|
-
|
|
55
|
+
if (!orgId || !domain || !targetOrgId) error("Missing required arguments.\nUsage: myapi domain assign <domain> --org <id> --target <target_org_id>\n(Or set defaults via: myapi auth config set-org <id> / set-domain <domain>)");
|
|
56
|
+
await sdkDomain.assignDomain(config.api_key, orgId, domain, targetOrgId);
|
|
56
57
|
success(`Assigned ${domain} to org ${targetOrgId}`);
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -60,16 +61,30 @@ export async function unassign(domainArg: string, flags: Record<string, string |
|
|
|
60
61
|
const config = requireConfig();
|
|
61
62
|
const orgId = (flags.org as string) || config.default_org;
|
|
62
63
|
const domain = domainArg || (config.default_domain as string);
|
|
63
|
-
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain unassign <domain> --org <id>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
64
|
-
|
|
64
|
+
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain unassign <domain> --org <id>\n(Or set defaults via: myapi auth config set-org <id> / set-domain <domain>)");
|
|
65
|
+
await sdkDomain.unassignDomain(config.api_key, orgId, domain);
|
|
65
66
|
success(`Unassigned ${domain} from org ${orgId}`);
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
export async function status(domainArg: string, flags: Record<string, string | boolean>) {
|
|
70
|
+
const config = requireConfig();
|
|
71
|
+
const orgId = (flags.org as string) || config.default_org;
|
|
72
|
+
const domain = domainArg || (config.default_domain as string);
|
|
73
|
+
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain status <domain> --org <id>\n(Or set defaults via: myapi auth config set-org <id> / set-domain <domain>)");
|
|
74
|
+
const res = await sdkDomain.getDomainStatus(config.api_key, orgId, domain);
|
|
75
|
+
const display: Record<string, unknown> = {
|
|
76
|
+
domain: res.domain,
|
|
77
|
+
status: res.status,
|
|
78
|
+
};
|
|
79
|
+
if (res.expires_at) display.expires_at = res.expires_at;
|
|
80
|
+
printJson(display);
|
|
81
|
+
}
|
|
82
|
+
|
|
68
83
|
export async function settings(domainArg: string, flags: Record<string, string | boolean>) {
|
|
69
84
|
const config = requireConfig();
|
|
70
85
|
const orgId = (flags.org as string) || config.default_org;
|
|
71
86
|
const domain = domainArg || (config.default_domain as string);
|
|
72
|
-
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain settings <domain> --org <id>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
87
|
+
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain settings <domain> --org <id>\n(Or set defaults via: myapi auth config set-org <id> / set-domain <domain>)");
|
|
73
88
|
const res = await sdkDomain.getDomainSettings(config.api_key, orgId, domain);
|
|
74
89
|
printJson(res);
|
|
75
90
|
}
|
|
@@ -78,22 +93,22 @@ export async function updateSettings(domainArg: string, flags: Record<string, st
|
|
|
78
93
|
const config = requireConfig();
|
|
79
94
|
const orgId = (flags.org as string) || config.default_org;
|
|
80
95
|
const domain = domainArg || (config.default_domain as string);
|
|
81
|
-
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain update-settings <domain> --org <id> [--security=...] [--browser-check=...] [--purge-cache]\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
96
|
+
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain update-settings <domain> --org <id> [--security=...] [--browser-check=...] [--purge-cache]\n(Or set defaults via: myapi auth config set-org <id> / set-domain <domain>)");
|
|
82
97
|
const payload: any = {};
|
|
83
98
|
if (flags.security) payload.security_level = flags.security as string;
|
|
84
99
|
if (flags['browser-check']) payload.browser_check = flags['browser-check'] as string;
|
|
85
100
|
if (flags['purge-cache']) payload.purge_cache = true;
|
|
86
|
-
|
|
101
|
+
|
|
87
102
|
const res = await sdkDomain.updateDomainSettings(config.api_key, orgId, domain, payload);
|
|
88
103
|
success(`Updated settings for ${domain}!\n${JSON.stringify(res, null, 2)}`);
|
|
89
104
|
}
|
|
90
105
|
|
|
91
106
|
export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
|
|
92
107
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
93
|
-
info('Usage: myapi domain <subcommand>\n\nSubcommands:\n list List domains\n check Check domain availability\n register Register a domain\n import Import an existing domain\n assign Assign domain to org\n unassign Unassign domain from its current org\n settings Get domain settings\n update-settings Update domain settings\n\nNote: All domain commands require the --org <id> flag.');
|
|
108
|
+
info('Usage: myapi domain <subcommand>\n\nSubcommands:\n list List domains\n check Check domain availability\n register Register a domain\n import Import an existing domain\n assign Assign domain to org\n unassign Unassign domain from its current org\n status Get domain status\n settings Get domain settings\n update-settings Update domain settings\n\nNote: All domain commands require the --org <id> flag.');
|
|
94
109
|
return;
|
|
95
110
|
}
|
|
96
|
-
|
|
111
|
+
|
|
97
112
|
if (flags.help) {
|
|
98
113
|
if (subcommand === 'list') info('Usage: myapi domain list --org <id> [--filter=all|unassigned|org]\n\nLists all registered domains in your account.\n --filter=all Lists all domains owned by the account.\n --filter=unassigned Lists domains not assigned to an org.\n --filter=org (Default) Lists domains assigned to the current org.');
|
|
99
114
|
else if (subcommand === 'check') info('Usage: myapi domain check <domain> --org <id>\n\nChecks if a domain name is available for registration.');
|
|
@@ -101,6 +116,7 @@ export async function run(subcommand: string | undefined, args: string[], flags:
|
|
|
101
116
|
else if (subcommand === 'import') info('Usage: myapi domain import <domain> --org <id> --namecheap-user <user> --namecheap-key <key>\n\nImports an existing domain from Namecheap.');
|
|
102
117
|
else if (subcommand === 'assign') info('Usage: myapi domain assign <domain> --org <id> --target <target_org_id>\n\nAssigns a domain to a different organization.');
|
|
103
118
|
else if (subcommand === 'unassign') info('Usage: myapi domain unassign <domain> --org <id>\n\nUnassigns a domain from its current organization.');
|
|
119
|
+
else if (subcommand === 'status') info('Usage: myapi domain status <domain> --org <id>\n\nGets the registration status of a domain.');
|
|
104
120
|
else if (subcommand === 'settings') info('Usage: myapi domain settings <domain> --org <id>\n\nGets the DNS settings and configuration for a domain.');
|
|
105
121
|
else if (subcommand === 'update-settings') info('Usage: myapi domain update-settings <domain> --org <id> [--security=essentially_off|medium|high|under_attack] [--browser-check=on|off] [--purge-cache]\n\nUpdates edge settings for a domain.');
|
|
106
122
|
return;
|
|
@@ -112,6 +128,7 @@ export async function run(subcommand: string | undefined, args: string[], flags:
|
|
|
112
128
|
else if (subcommand === 'import') await importDomain(args[0], flags);
|
|
113
129
|
else if (subcommand === 'assign') await assign(args[0], flags);
|
|
114
130
|
else if (subcommand === 'unassign') await unassign(args[0], flags);
|
|
131
|
+
else if (subcommand === 'status') await status(args[0], flags);
|
|
115
132
|
else if (subcommand === 'settings') await settings(args[0], flags);
|
|
116
133
|
else if (subcommand === 'update-settings') await updateSettings(args[0], flags);
|
|
117
134
|
else error(`Unknown subcommand: ${subcommand}. Run "myapi domain --help" for a list of valid subcommands.`);
|
package/src/commands/org.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { hq } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
3
|
import { success, error, printTable, printJson, info } from '../output.js';
|
|
4
|
-
import { sleep } from '../utils.js';
|
|
4
|
+
import { sleep, formatDate } from '../utils.js';
|
|
5
5
|
|
|
6
6
|
export async function create(flags: Record<string, string | boolean>) {
|
|
7
7
|
if (flags.help) {
|
|
@@ -18,7 +18,7 @@ export async function create(flags: Record<string, string | boolean>) {
|
|
|
18
18
|
if (flags.description) payload.description = flags.description as string;
|
|
19
19
|
if (flags['business-sector']) payload.business_sector = flags['business-sector'] as string;
|
|
20
20
|
if (flags['logo-url']) payload.logo_url = flags['logo-url'] as string;
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
const org = await hq.createOrg(config.api_key, payload);
|
|
23
23
|
success(`Org created! ID: ${org.id}, Name: ${org.name}`);
|
|
24
24
|
if (org.preview_subdomain) {
|
|
@@ -28,12 +28,17 @@ export async function create(flags: Record<string, string | boolean>) {
|
|
|
28
28
|
|
|
29
29
|
export async function list(flags: Record<string, string | boolean>) {
|
|
30
30
|
if (flags.help) {
|
|
31
|
-
info('Usage: myapi org list\n\nLists all organizations in your account
|
|
31
|
+
info('Usage: myapi org list\n\nLists all organizations in your account.');
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
const config = requireConfig();
|
|
35
35
|
const orgs = await hq.listOrgs(config.api_key);
|
|
36
|
-
|
|
36
|
+
const rows = orgs.map((o: any) => ({
|
|
37
|
+
id: o.id,
|
|
38
|
+
name: o.name,
|
|
39
|
+
created_at: o.created_at ? formatDate(o.created_at) : '',
|
|
40
|
+
}));
|
|
41
|
+
printTable(rows as unknown as Record<string, unknown>[]);
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
export async function get(id: string, flags: Record<string, string | boolean>) {
|
|
@@ -75,19 +80,19 @@ export async function importOrg(args: string[], flags: Record<string, string | b
|
|
|
75
80
|
const config = requireConfig();
|
|
76
81
|
const domain = args[0] || (config.default_domain as string);
|
|
77
82
|
const orgId = (flags.org as string) || config.default_org;
|
|
78
|
-
|
|
83
|
+
|
|
79
84
|
if (!domain || !orgId) {
|
|
80
85
|
error("Missing required arguments.\nUsage: myapi org import <domain> --org <id>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
81
86
|
return;
|
|
82
87
|
}
|
|
83
|
-
|
|
88
|
+
|
|
84
89
|
const result = await hq.importOrg(config.api_key, orgId, domain);
|
|
85
90
|
const importId = result.job_id;
|
|
86
|
-
|
|
91
|
+
|
|
87
92
|
process.stdout.write("Importing ");
|
|
88
93
|
const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
89
94
|
let i = 0;
|
|
90
|
-
|
|
95
|
+
|
|
91
96
|
while (true) {
|
|
92
97
|
const status = await hq.getOrgImportStatus(config.api_key, importId);
|
|
93
98
|
if (status.status === 'awaiting_confirm') {
|
|
@@ -101,7 +106,7 @@ export async function importOrg(args: string[], flags: Record<string, string | b
|
|
|
101
106
|
process.stdout.write(`\rImporting ${chars[i++ % chars.length]}`);
|
|
102
107
|
await sleep(3000);
|
|
103
108
|
}
|
|
104
|
-
|
|
109
|
+
|
|
105
110
|
const org = await hq.confirmOrgImport(config.api_key, importId);
|
|
106
111
|
success(`Import complete! Org ID: ${org.id}`);
|
|
107
112
|
}
|
package/src/commands/setup.ts
CHANGED
|
@@ -138,13 +138,64 @@ async function anonymousFlow(): Promise<{
|
|
|
138
138
|
// Main setup command
|
|
139
139
|
// ---------------------------------------------------------------------------
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
// myapi auth import-key <key> — non-interactively import a raw API key.
|
|
142
|
+
export async function importKey(apiKey: string, flags: Record<string, string | boolean>) {
|
|
143
|
+
if (!apiKey) {
|
|
144
|
+
info('Usage: myapi auth import-key <api_key> [--install-skills] [--no-skills]');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const auth = { Authorization: `Bearer ${apiKey}` };
|
|
148
|
+
let accountId = '';
|
|
149
|
+
let email: string | undefined;
|
|
150
|
+
let defaultOrg = '';
|
|
151
|
+
let defaultFunnel = '';
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const meRes = await fetch(`${API_BASE}/hq/account/me`, { headers: auth });
|
|
155
|
+
const meJson = await meRes.json() as any;
|
|
156
|
+
if (!meRes.ok) throw new Error(meJson.error ?? `HTTP ${meRes.status}`);
|
|
157
|
+
const me = meJson.data ?? meJson;
|
|
158
|
+
accountId = me.account_id ?? '';
|
|
159
|
+
email = me.email || undefined;
|
|
160
|
+
} catch (e: any) {
|
|
161
|
+
throw new Error(`Could not verify API key: ${e.message}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Fetch org/funnel defaults.
|
|
165
|
+
try {
|
|
166
|
+
const orgRes = await fetch(`${API_BASE}/hq/orgs`, { headers: auth });
|
|
167
|
+
if (orgRes.ok) {
|
|
168
|
+
const orgs = ((await orgRes.json() as any)?.data ?? []);
|
|
169
|
+
if (orgs.length > 0) {
|
|
170
|
+
defaultOrg = orgs[orgs.length - 1].id;
|
|
171
|
+
const fRes = await fetch(`${API_BASE}/funnel/orgs/${defaultOrg}/funnels`, { headers: auth });
|
|
172
|
+
if (fRes.ok) {
|
|
173
|
+
const funnels = ((await fRes.json() as any)?.data ?? []);
|
|
174
|
+
if (funnels.length > 0) defaultFunnel = funnels[funnels.length - 1].id;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} catch { /* non-fatal */ }
|
|
179
|
+
|
|
180
|
+
const wantsSkills = flags['install-skills'] ? true : flags['no-skills'] ? false : true;
|
|
181
|
+
|
|
182
|
+
addAccount({ api_key: apiKey, account_id: accountId, pin: '', email, default_org: defaultOrg, default_funnel: defaultFunnel, skills_installed: wantsSkills });
|
|
183
|
+
success(`› ✓ Key imported${email ? ` · ${email}` : ''}`);
|
|
184
|
+
if (wantsSkills) await installSkills();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function setup(flags: Record<string, string | boolean> = {}) {
|
|
188
|
+
if (flags.help) {
|
|
189
|
+
info('Usage: myapi auth setup [--anonymous] [--yes] [--install-skills|--no-skills]\n\nFlags:\n --anonymous Skip registration, create anonymous account\n --yes Skip confirmation prompts\n --install-skills Auto-install skills pack\n --no-skills Skip skills installation');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
142
193
|
info('› Configuring MyAPI…');
|
|
143
194
|
|
|
144
195
|
const existing = loadConfig();
|
|
145
196
|
|
|
146
197
|
// Already configured — ask before adding a new account.
|
|
147
|
-
if (existing?.api_key) {
|
|
198
|
+
if (existing?.api_key && !flags.yes) {
|
|
148
199
|
const full = loadFullConfig();
|
|
149
200
|
const total = full?.accounts.length ?? 1;
|
|
150
201
|
const activeLabel = existing.email ?? existing.account_id;
|
|
@@ -182,8 +233,12 @@ export async function setup() {
|
|
|
182
233
|
let isAnonymous = false;
|
|
183
234
|
let email = '';
|
|
184
235
|
|
|
236
|
+
// Determine skills preference from flags before any prompts.
|
|
237
|
+
const skillsFromFlag = flags['install-skills'] ? true : flags['no-skills'] ? false : null;
|
|
238
|
+
|
|
185
239
|
try {
|
|
186
|
-
const
|
|
240
|
+
const useAnon = flags.anonymous || flags.anon;
|
|
241
|
+
const createAns = useAnon ? 'n' : await ask(rl, '› Create an account? (Y/n) ');
|
|
187
242
|
|
|
188
243
|
if (yn(createAns)) {
|
|
189
244
|
const data = await registeredFlow(rl);
|
|
@@ -203,8 +258,8 @@ export async function setup() {
|
|
|
203
258
|
isAnonymous = true;
|
|
204
259
|
}
|
|
205
260
|
|
|
206
|
-
const skillsAns = await ask(rl, '› Install the MyAPI skills pack? (Y/n) ');
|
|
207
|
-
const wantsSkills = yn(skillsAns);
|
|
261
|
+
const skillsAns = skillsFromFlag !== null ? (skillsFromFlag ? 'y' : 'n') : await ask(rl, '› Install the MyAPI skills pack? (Y/n) ');
|
|
262
|
+
const wantsSkills = skillsFromFlag !== null ? skillsFromFlag : yn(skillsAns);
|
|
208
263
|
|
|
209
264
|
addAccount({
|
|
210
265
|
api_key: apiKey,
|
package/src/commands/update.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
|
+
import { loadConfig, saveConfig } from '../config.js';
|
|
2
3
|
import { info, success } from '../output.js';
|
|
3
4
|
import { installSkills } from './setup.js';
|
|
4
5
|
|
|
@@ -16,7 +17,10 @@ export async function checkForUpdate(currentVersion: string): Promise<void> {
|
|
|
16
17
|
if (latest && isNewer(latest, currentVersion)) {
|
|
17
18
|
info(`\n› New version available (${currentVersion} → ${latest}) — installing…`);
|
|
18
19
|
execSync('npm install -g @myapihq/cli', { stdio: 'pipe' });
|
|
19
|
-
|
|
20
|
+
const config = loadConfig();
|
|
21
|
+
if (config?.skills_installed) {
|
|
22
|
+
await installSkills();
|
|
23
|
+
}
|
|
20
24
|
success(`› MyAPI CLI updated to ${latest}. Re-run your command.\n`);
|
|
21
25
|
process.exit(0);
|
|
22
26
|
}
|
package/src/config.ts
CHANGED
|
@@ -57,7 +57,7 @@ export function loadFullConfig(): FullConfig | null {
|
|
|
57
57
|
|
|
58
58
|
// loadConfig returns the active account merged with globals — unchanged interface for all callers.
|
|
59
59
|
export function loadConfig(): Config | null {
|
|
60
|
-
const envKey = process.env.MYAPI_KEY;
|
|
60
|
+
const envKey = process.env.MYAPI_API_KEY || process.env.MYAPI_KEY;
|
|
61
61
|
const full = loadFullConfig();
|
|
62
62
|
|
|
63
63
|
if (!full && envKey) return { api_key: envKey, account_id: '', pin: '' };
|
package/src/utils.ts
CHANGED
|
@@ -28,3 +28,14 @@ export function parseArgs(argv: string[]) {
|
|
|
28
28
|
export function sleep(ms: number) {
|
|
29
29
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Formats an ISO/Go timestamp string to "YYYY-MM-DD HH:mm" (UTC).
|
|
34
|
+
* Strips Go's " +0000 UTC" suffix before parsing.
|
|
35
|
+
*/
|
|
36
|
+
export function formatDate(str: string): string {
|
|
37
|
+
const clean = str.replace(' +0000 UTC', 'Z');
|
|
38
|
+
const date = new Date(clean);
|
|
39
|
+
if (isNaN(date.getTime())) return str;
|
|
40
|
+
return date.toISOString().replace('T', ' ').slice(0, 16);
|
|
41
|
+
}
|
package/dist/skills/my-api-hq.md
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: my-api-hq
|
|
3
|
-
description: >
|
|
4
|
-
Core Identity and Billing hub. Manage auth, organizations (get org_id), and billing (checkout/topup).
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# MyApiHQ Skill
|
|
8
|
-
Root entry point for the ecosystem. All other skills require an `api_key` and often an `org_id` from here.
|
|
9
|
-
|
|
10
|
-
## Platform Conventions
|
|
11
|
-
|
|
12
|
-
### Response Envelope
|
|
13
|
-
Every response across all services is wrapped in:
|
|
14
|
-
```json
|
|
15
|
-
{
|
|
16
|
-
"success": true,
|
|
17
|
-
"data": { ... },
|
|
18
|
-
"error": null,
|
|
19
|
-
"meta": { "request_id": "...", "latency_ms": 12, "service": "...", "version": "v1" }
|
|
20
|
-
}
|
|
21
|
-
```
|
|
22
|
-
On error, `success` is `false`, `data` is `null`, and `error` contains a string error code or object. Always check `success` before reading `data`.
|
|
23
|
-
|
|
24
|
-
### Pagination
|
|
25
|
-
List endpoints accept `?limit=` and `?offset=` and return `total`, `limit`, `offset` in the body.
|
|
26
|
-
|
|
27
|
-
## Authentication & Key Management
|
|
28
|
-
|
|
29
|
-
You need to go to myapihq.com and register an account. Generate an api key and export it to pass it to the agent (as env).
|
|
30
|
-
|
|
31
|
-
### Account Login
|
|
32
|
-
```
|
|
33
|
-
POST /hq/account/login
|
|
34
|
-
{ "email": "...", "password": "..." }
|
|
35
|
-
→ { "data": { "token": "<JWT>" } }
|
|
36
|
-
```
|
|
37
|
-
Errors: `401` wrong password.
|
|
38
|
-
|
|
39
|
-
**Refresh Token:**
|
|
40
|
-
```
|
|
41
|
-
POST /hq/account/refresh
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**Google OAuth:**
|
|
45
|
-
- `GET /hq/auth/google/redirect` — Initiates Google OAuth flow.
|
|
46
|
-
- `GET /hq/auth/google/callback` — OAuth callback.
|
|
47
|
-
|
|
48
|
-
### API Key Management
|
|
49
|
-
**Generate Persistent Key:**
|
|
50
|
-
```
|
|
51
|
-
POST /hq/account/create/key
|
|
52
|
-
Authorization: Bearer <JWT from login>
|
|
53
|
-
{ "name": "MyKey" }
|
|
54
|
-
→ { "data": { "api_key": "hq_live_...", "id": "...", "prefix": "..." } }
|
|
55
|
-
```
|
|
56
|
-
Errors: `401` no auth.
|
|
57
|
-
|
|
58
|
-
Use `Authorization: Bearer <api_key>` for all subsequent requests across the entire ecosystem.
|
|
59
|
-
|
|
60
|
-
**List Keys:** `GET /hq/account/keys`
|
|
61
|
-
|
|
62
|
-
**Revoke Key:** `DELETE /hq/account/delete/key/{id}`
|
|
63
|
-
|
|
64
|
-
### Cross-Session Storage
|
|
65
|
-
**Always persist credentials immediately.** Write a `.env` file:
|
|
66
|
-
```
|
|
67
|
-
MYAPI_API_KEY=hq_live_...
|
|
68
|
-
MYAPI_ACCOUNT_ID=...
|
|
69
|
-
```
|
|
70
|
-
Also save to agent memory if your runtime supports it (e.g., Claude Code memory). On every new session, check for credentials before creating a new account.
|
|
71
|
-
|
|
72
|
-
## Organization Management
|
|
73
|
-
**You MUST create an org to get an `org_id` for other APIs.**
|
|
74
|
-
|
|
75
|
-
### Create Org (sync)
|
|
76
|
-
```
|
|
77
|
-
POST /hq/orgs
|
|
78
|
-
{ "name": "Acme Inc" (required), "tagline", "description", "business_sector",
|
|
79
|
-
"logo_url", "favicon_url", "og_image_url",
|
|
80
|
-
"color_palette": { "primary": "#hex", ... },
|
|
81
|
-
"font_family", "imagery_style", "headline", "subheadline", "cta_text",
|
|
82
|
-
"value_propositions": ["..."],
|
|
83
|
-
"social_links": { "twitter": "url", ... },
|
|
84
|
-
"canonical_url", "privacy_policy_url", "cookie_policy_url", "terms_url",
|
|
85
|
-
"gdpr_enabled": false, "default_language": "en", "tracking": {} }
|
|
86
|
-
→ { "data": { "id": "<org_id>", ... } }
|
|
87
|
-
```
|
|
88
|
-
Errors: `400` invalid_json · `422` name_required, invalid_field:color_palette, invalid_field:value_propositions, invalid_field:social_links, invalid_field:tracking · `402` insufficient balance (org creation has a cost on paid plan).
|
|
89
|
-
|
|
90
|
-
### Async Brand Import
|
|
91
|
-
```
|
|
92
|
-
POST /hq/org-imports
|
|
93
|
-
{ "org_id": "<id>" (required), "domain": "example.com" (required), "auto_accept": false }
|
|
94
|
-
→ { "data": { "job_id": "...", "status": "pending" } }
|
|
95
|
-
|
|
96
|
-
GET /hq/org-imports/{job_id}
|
|
97
|
-
→ Poll until status = "awaiting_confirm". Returns brand_preview.
|
|
98
|
-
|
|
99
|
-
POST /hq/org-imports/{job_id}/confirm
|
|
100
|
-
{ ...optional overrides matching POST /hq/orgs payload... }
|
|
101
|
-
→ { "data": { "id": "<org_id>", ... } }
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Manage Orgs
|
|
105
|
-
- `GET /hq/orgs` — list all orgs.
|
|
106
|
-
- `GET /hq/orgs/{id}` — get org details. Errors: `404` org_not_found.
|
|
107
|
-
- `PATCH /hq/orgs/{id}` — partial update, same fields as create. Errors: `400` invalid_json · `404` org_not_found · `422` invalid_field:*.
|
|
108
|
-
- `DELETE /hq/orgs/{id}` — delete org and cascade. Errors: `404` org_not_found.
|
|
109
|
-
|
|
110
|
-
## Billing
|
|
111
|
-
- **Setup Payment Method:** You need to do this from the myapihq dashboard directly.
|
|
112
|
-
- **Check Balance:** `GET /hq/billing/balance` → `{ "data": { "balance_cents": 1000, "balance_display": "$10.00", "credits_cents": 500, "credits_display": "$5.00", "has_payment_method": true } }`.
|
|
113
|
-
- **Billing History:** `GET /hq/billing/history`
|
|
114
|
-
- **Top Up:** `POST /hq/billing/topup` — `{ "amount_cents": 1000 }` → `{ "data": { "new_balance_cents": 2000, "new_balance_display": "$20.00" } }`.
|
|
115
|
-
|
|
116
|
-
**On 402 from any service:** check balance and top up here before retrying.
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: my-domain-api
|
|
3
|
-
description: >
|
|
4
|
-
Register new domains, check availability and pricing, import existing domains, and manage edge settings. Use this before creating mailboxes or funnels — both require an owned domain.
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# MyDomainAPI Skill
|
|
8
|
-
|
|
9
|
-
## Quick Start
|
|
10
|
-
1. `GET /domain/orgs/{org_id}/list?filter=all` — lists domains owned by your account. `filter` can be `all`, `unassigned`, or `org` (default).
|
|
11
|
-
2. `GET /domain/orgs/{org_id}/check/available/{domain}` — confirm availability and price.
|
|
12
|
-
3. `POST /domain/orgs/{org_id}/register` with `domain` and optional `years`.
|
|
13
|
-
4. Proceed to `my-email-api` for mailboxes or `my-funnel-api` for a website.
|
|
14
|
-
|
|
15
|
-
DNS is fully managed by the platform — enabling seamless email deliverability, tracking pixel, and edge delivery integration. Manual DNS record management is not exposed.
|
|
16
|
-
|
|
17
|
-
## Dependencies & Backlinks
|
|
18
|
-
- **Auth & Billing:** 401/402 → fall back to `my-api-hq`.
|
|
19
|
-
- **Next Steps:** After registration → `my-email-api` for mailboxes or `my-funnel-api` for a website.
|
|
20
|
-
|
|
21
|
-
## Authentication
|
|
22
|
-
`Authorization: Bearer <api_key>` (from `my-api-hq`).
|
|
23
|
-
|
|
24
|
-
## Endpoints
|
|
25
|
-
|
|
26
|
-
### Check Availability
|
|
27
|
-
```
|
|
28
|
-
GET /domain/orgs/{org_id}/check/available/{domain}
|
|
29
|
-
→ { "available": true, "price_cents": 1200 }
|
|
30
|
-
```
|
|
31
|
-
Errors: `400` INVALID_DOMAIN, TLD_NOT_SUPPORTED.
|
|
32
|
-
|
|
33
|
-
### Register Domain
|
|
34
|
-
```
|
|
35
|
-
POST /domain/orgs/{org_id}/register
|
|
36
|
-
{ "domain": "example.com", "years": 1 }
|
|
37
|
-
→ { "domain": "...", "status": "provisioning", "domain_id": "..." }
|
|
38
|
-
```
|
|
39
|
-
Errors: `400` invalid request, INVALID_DOMAIN, TLD_NOT_SUPPORTED · `409` DOMAIN_ALREADY_OWNED, DOMAIN_UNAVAILABLE · `402` INSUFFICIENT_BALANCE (includes `required_cents`) or UPGRADE_REQUIRED (free account) · `403` `already_owned` flag is not permitted.
|
|
40
|
-
|
|
41
|
-
### Import Existing Domain
|
|
42
|
-
```
|
|
43
|
-
POST /domain/orgs/{org_id}/import
|
|
44
|
-
{ "domain": "example.com", "namecheap_api_user": "optional", "namecheap_api_key": "optional" }
|
|
45
|
-
```
|
|
46
|
-
Sets up DNS and email infrastructure automatically. Optionally updates Namecheap NS if credentials are provided.
|
|
47
|
-
Errors: `402` insufficient balance.
|
|
48
|
-
|
|
49
|
-
To use Namecheap automation: go to **Profile > Tools > Namecheap API Access**, generate an API Key, and whitelist the MyAPI-HQ server IP — otherwise the API calls will be rejected.
|
|
50
|
-
|
|
51
|
-
### List & Status
|
|
52
|
-
```
|
|
53
|
-
GET /domain/orgs/{org_id}/list
|
|
54
|
-
GET /domain/orgs/{org_id}/{domain}/status
|
|
55
|
-
```
|
|
56
|
-
Errors (status): `404` DOMAIN_NOT_FOUND.
|
|
57
|
-
|
|
58
|
-
### Assign / Unassign Domain
|
|
59
|
-
```
|
|
60
|
-
POST /domain/orgs/{org_id}/{domain}/assign
|
|
61
|
-
{ "org_id": "<target_org_id>" } // Pass null to unassign
|
|
62
|
-
```
|
|
63
|
-
Associates a domain already in the account with a specific organization, or removes it from its current organization if `org_id` is null.
|
|
64
|
-
Errors: `404` DOMAIN_NOT_FOUND · `422` ORG_NOT_FOUND.
|
|
65
|
-
|
|
66
|
-
### Edge Settings
|
|
67
|
-
|
|
68
|
-
**Update:**
|
|
69
|
-
```
|
|
70
|
-
POST /domain/orgs/{org_id}/{domain}/settings
|
|
71
|
-
{
|
|
72
|
-
"security_level": "essentially_off", // essentially_off | medium | high | under_attack
|
|
73
|
-
"browser_check": "off", // on | off
|
|
74
|
-
"purge_cache": true
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
*To allow AI training bots and crawlers: set `security_level: "essentially_off"` and `browser_check: "off"`.*
|
|
78
|
-
|
|
79
|
-
**Get:**
|
|
80
|
-
```
|
|
81
|
-
GET /domain/orgs/{org_id}/{domain}/settings
|
|
82
|
-
→ { "domain": "...", "security_level": "...", "browser_check": "...", "ai_bots_protection": "disabled", "is_robots_txt_managed": false }
|
|
83
|
-
```
|