@myapihq/cli 1.0.53 → 1.0.58
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.d.ts +1 -1
- package/dist/commands/auth.js +26 -12
- package/dist/commands/billing.js +9 -1
- package/dist/commands/config.js +19 -5
- package/dist/commands/domain.js +18 -8
- package/dist/commands/funnel.js +14 -4
- package/dist/commands/keys.js +3 -9
- package/dist/commands/org.js +12 -4
- package/dist/index.js +51 -8
- package/dist/output.js +3 -2
- package/dist/utils.js +7 -1
- package/package.json +1 -1
- package/src/commands/auth.ts +26 -12
- package/src/commands/billing.ts +10 -1
- package/src/commands/config.ts +17 -5
- package/src/commands/domain.ts +15 -8
- package/src/commands/funnel.ts +10 -3
- package/src/commands/keys.ts +3 -10
- package/src/commands/org.ts +9 -4
- package/src/index.ts +47 -7
- package/src/output.ts +4 -2
- package/src/utils.ts +5 -1
package/dist/commands/auth.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare function link(flags?: Record<string, string | boolean
|
|
1
|
+
export declare function link(flags?: Record<string, string | boolean>, emailArg?: string): Promise<void>;
|
|
2
2
|
export declare function whoami(flags?: Record<string, string | boolean>): Promise<void>;
|
|
3
3
|
export declare function switchCmd(flags?: Record<string, string | boolean>, indexArg?: string): Promise<void>;
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
2
|
import { loadConfig, saveConfig, addAccount, switchAccount, listAccounts } from '../config.js';
|
|
3
|
-
import { info, success, error } from '../output.js';
|
|
3
|
+
import { info, success, error, printJson } from '../output.js';
|
|
4
4
|
import { installSkills } from './setup.js';
|
|
5
5
|
import { hq } from '@myapihq/sdk';
|
|
6
6
|
const API_BASE = process.env.MYAPI_API_BASE ?? 'https://api.myapihq.com';
|
|
@@ -39,9 +39,9 @@ async function patch(path, body, apiKey) {
|
|
|
39
39
|
return json.data ?? json;
|
|
40
40
|
}
|
|
41
41
|
// myapi auth link — upgrade anonymous session to registered account.
|
|
42
|
-
export async function link(flags = {}) {
|
|
42
|
+
export async function link(flags = {}, emailArg) {
|
|
43
43
|
if (flags.help) {
|
|
44
|
-
info('Usage: myapi auth link\n\
|
|
44
|
+
info('Usage: myapi auth link [email]\n\nAttaches a verified email to your current anonymous account, converting it into\na registered account. A one-time code will be sent to the email to confirm.\n\nIf that email already belongs to an existing MyAPI account, you will be signed\ninto that account instead — it is added as a second active session alongside\nyour current one.\n\nTo create a brand-new account from scratch, use: myapi auth setup');
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
const config = loadConfig();
|
|
@@ -51,7 +51,9 @@ export async function link(flags = {}) {
|
|
|
51
51
|
error('Already registered. Use your existing account.');
|
|
52
52
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
53
53
|
try {
|
|
54
|
-
const email = (await ask(rl, '› Email? ')).trim();
|
|
54
|
+
const email = emailArg || (await ask(rl, '› Email? ')).trim();
|
|
55
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
|
|
56
|
+
error(`Invalid email address: "${email}".`);
|
|
55
57
|
let upgradeOk = true;
|
|
56
58
|
try {
|
|
57
59
|
await patch('/hq/account/upgrade', { email }, config.api_key);
|
|
@@ -108,25 +110,36 @@ export async function link(flags = {}) {
|
|
|
108
110
|
// myapi auth whoami — show current session info.
|
|
109
111
|
export async function whoami(flags = {}) {
|
|
110
112
|
if (flags.help) {
|
|
111
|
-
info('Usage: myapi auth whoami\n\nDisplays the active account details: email, account ID, default org, default funnel, account type, and current balance
|
|
113
|
+
info('Usage: myapi auth whoami [--json]\n\nDisplays the active account details: email, account ID, default org, default funnel, account type, and current balance.\n\nFlags:\n --json Output raw JSON');
|
|
112
114
|
return;
|
|
113
115
|
}
|
|
114
116
|
const config = loadConfig();
|
|
115
117
|
if (!config?.api_key)
|
|
116
118
|
error('Not configured. Run: myapi auth setup');
|
|
119
|
+
let balance = null;
|
|
120
|
+
try {
|
|
121
|
+
balance = await hq.getBalance(config.api_key);
|
|
122
|
+
}
|
|
123
|
+
catch { }
|
|
124
|
+
if (flags.json) {
|
|
125
|
+
printJson({
|
|
126
|
+
email: config.email ?? null,
|
|
127
|
+
account_id: config.account_id,
|
|
128
|
+
default_org: config.default_org ?? null,
|
|
129
|
+
default_funnel: config.default_funnel ?? null,
|
|
130
|
+
is_anonymous: config.is_anonymous ?? false,
|
|
131
|
+
balance: balance?.balance_display ?? null,
|
|
132
|
+
credits: balance?.credits_display ?? null,
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
117
136
|
if (config.email)
|
|
118
137
|
info(`Email: ${config.email}`);
|
|
119
138
|
info(`Account: ${config.account_id}`);
|
|
120
139
|
info(`Org: ${config.default_org ?? '(none)'}`);
|
|
121
140
|
info(`Funnel: ${config.default_funnel ?? '(none)'}`);
|
|
122
141
|
info(`Type: ${config.is_anonymous ? 'anonymous' : 'registered'}`);
|
|
123
|
-
|
|
124
|
-
const bal = await hq.getBalance(config.api_key);
|
|
125
|
-
info(`Balance: ${bal.balance_display} | Credits: ${bal.credits_display}`);
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
info(`Balance: (unavailable)`);
|
|
129
|
-
}
|
|
142
|
+
info(`Balance: ${balance ? `${balance.balance_display} | Credits: ${balance.credits_display}` : '(unavailable)'}`);
|
|
130
143
|
}
|
|
131
144
|
// myapi auth switch [index] — switch between saved accounts.
|
|
132
145
|
export async function switchCmd(flags = {}, indexArg) {
|
|
@@ -139,6 +152,7 @@ export async function switchCmd(flags = {}, indexArg) {
|
|
|
139
152
|
error('No accounts configured. Run: myapi auth setup');
|
|
140
153
|
if (accounts.length === 1) {
|
|
141
154
|
info(`Only one account configured: ${accounts[0].email ?? accounts[0].account_id}`);
|
|
155
|
+
info('To add another account, run: myapi auth setup');
|
|
142
156
|
return;
|
|
143
157
|
}
|
|
144
158
|
info('Accounts:');
|
package/dist/commands/billing.js
CHANGED
|
@@ -5,11 +5,19 @@ import { success, error, printTable, info, printJson } from '../output.js';
|
|
|
5
5
|
import { formatDate } from '../utils.js';
|
|
6
6
|
export async function balance(flags) {
|
|
7
7
|
if (flags.help) {
|
|
8
|
-
info('Usage: myapi billing balance\n\nShows your current account balance, credits, and payment method status
|
|
8
|
+
info('Usage: myapi billing balance [--json]\n\nShows your current account balance, credits, and payment method status.\n\nFlags:\n --json Output raw JSON');
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
11
|
const config = requireConfig();
|
|
12
12
|
const result = await hq.getBalance(config.api_key);
|
|
13
|
+
if (flags.json) {
|
|
14
|
+
printJson({
|
|
15
|
+
balance: result.balance_display,
|
|
16
|
+
credits: result.credits_display,
|
|
17
|
+
has_payment_method: result.has_payment_method,
|
|
18
|
+
});
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
13
21
|
const pm = result.has_payment_method ? 'yes' : 'no';
|
|
14
22
|
const accountType = config.is_anonymous ? 'anonymous' : `registered (${config.email ?? ''})`;
|
|
15
23
|
info(`Account: ${accountType}`);
|
package/dist/commands/config.js
CHANGED
|
@@ -1,24 +1,37 @@
|
|
|
1
|
+
import { hq, funnel as sdkFunnel } from '@myapihq/sdk';
|
|
1
2
|
import { requireConfig, saveConfig } from '../config.js';
|
|
2
3
|
import { success, error, info } from '../output.js';
|
|
4
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3
5
|
export async function setOrg(id, flags) {
|
|
4
6
|
if (!id || flags.help) {
|
|
5
7
|
info("Usage: myapi config set-org <id>\n\nSets a default organization ID for subsequent commands.");
|
|
6
8
|
return;
|
|
7
9
|
}
|
|
10
|
+
if (!UUID_RE.test(id))
|
|
11
|
+
error(`Invalid organization ID: "${id}". Expected a UUID (e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).`);
|
|
8
12
|
const config = requireConfig();
|
|
9
|
-
|
|
13
|
+
info('› Validating…');
|
|
14
|
+
const org = await hq.getOrg(config.api_key, id);
|
|
15
|
+
config.default_org = org.id;
|
|
10
16
|
saveConfig(config);
|
|
11
|
-
success(`Default organization set to: ${id}`);
|
|
17
|
+
success(`Default organization set to: ${org.name || org.id}`);
|
|
12
18
|
}
|
|
13
19
|
export async function setFunnel(id, flags) {
|
|
14
20
|
if (!id || flags.help) {
|
|
15
21
|
info("Usage: myapi config set-funnel <id>\n\nSets a default funnel ID for subsequent commands.");
|
|
16
22
|
return;
|
|
17
23
|
}
|
|
24
|
+
if (!UUID_RE.test(id))
|
|
25
|
+
error(`Invalid funnel ID: "${id}". Expected a UUID (e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).`);
|
|
18
26
|
const config = requireConfig();
|
|
19
|
-
|
|
27
|
+
const orgId = config.default_org;
|
|
28
|
+
if (!orgId)
|
|
29
|
+
error('No default organization set. Run: myapi auth config set-org <id>');
|
|
30
|
+
info('› Validating…');
|
|
31
|
+
const funnel = await sdkFunnel.getFunnel(config.api_key, orgId, id);
|
|
32
|
+
config.default_funnel = funnel.id;
|
|
20
33
|
saveConfig(config);
|
|
21
|
-
success(`Default funnel set to: ${id}`);
|
|
34
|
+
success(`Default funnel set to: ${funnel.id}`);
|
|
22
35
|
}
|
|
23
36
|
export async function setDomain(domain, flags) {
|
|
24
37
|
if (!domain || flags.help) {
|
|
@@ -41,8 +54,9 @@ export async function view(flags) {
|
|
|
41
54
|
info(`Default Domain: ${config.default_domain || 'Not set'}`);
|
|
42
55
|
}
|
|
43
56
|
export async function run(subcommand, args, flags) {
|
|
57
|
+
const via = flags._via || 'auth config';
|
|
44
58
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
45
|
-
info(
|
|
59
|
+
info(`Usage: myapi ${via} <subcommand>\n\nSubcommands:\n view View current config\n set-org Set default organization\n set-funnel Set default funnel\n set-domain Set default domain`);
|
|
46
60
|
return;
|
|
47
61
|
}
|
|
48
62
|
if (subcommand === 'view')
|
package/dist/commands/domain.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { domain as sdkDomain } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
3
|
import { success, error, info, printTable, printJson } from '../output.js';
|
|
4
|
+
import { formatDate } from '../utils.js';
|
|
4
5
|
export async function check(domainArg, flags) {
|
|
5
6
|
const config = requireConfig();
|
|
6
7
|
const orgId = flags.org || config.default_org;
|
|
@@ -8,6 +9,10 @@ export async function check(domainArg, flags) {
|
|
|
8
9
|
if (!orgId || !domain)
|
|
9
10
|
error("Missing required arguments.\nUsage: myapi domain check <domain> --org <id>\n(Set defaults: myapi auth config set-org <id> / myapi auth config set-domain <domain>)");
|
|
10
11
|
const res = await sdkDomain.checkDomain(config.api_key, orgId, domain);
|
|
12
|
+
if (flags.json) {
|
|
13
|
+
printJson(res);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
11
16
|
if (res.available) {
|
|
12
17
|
const price = res.price_display || `$${(res.price_cents / 100).toFixed(2)}`;
|
|
13
18
|
info(`${domain} — Available ✓ ${price}/yr`);
|
|
@@ -48,6 +53,10 @@ export async function list(flags) {
|
|
|
48
53
|
error("Missing required arguments.\nUsage: myapi domain list --org <id> [--filter=all|unassigned|org]\n(Set default: myapi auth config set-org <id>)");
|
|
49
54
|
const filter = flags.filter;
|
|
50
55
|
const domains = await sdkDomain.listDomains(config.api_key, orgId, filter);
|
|
56
|
+
if (domains.length === 0) {
|
|
57
|
+
info('No domains found in this organization. Register one with: myapi domain register <name>');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
51
60
|
printTable(domains);
|
|
52
61
|
}
|
|
53
62
|
export async function assign(domainArg, flags) {
|
|
@@ -76,13 +85,14 @@ export async function status(domainArg, flags) {
|
|
|
76
85
|
if (!orgId || !domain)
|
|
77
86
|
error("Missing required arguments.\nUsage: myapi domain status <domain> --org <id>\n(Set defaults: myapi auth config set-org <id> / myapi auth config set-domain <domain>)");
|
|
78
87
|
const res = await sdkDomain.getDomainStatus(config.api_key, orgId, domain);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
88
|
+
if (flags.json) {
|
|
89
|
+
printJson(res);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
info(`Domain: ${res.domain}`);
|
|
93
|
+
info(`Status: ${res.status}`);
|
|
83
94
|
if (res.expires_at)
|
|
84
|
-
|
|
85
|
-
printJson(display);
|
|
95
|
+
info(`Expires: ${formatDate(res.expires_at)}`);
|
|
86
96
|
}
|
|
87
97
|
export async function settings(domainArg, flags) {
|
|
88
98
|
const config = requireConfig();
|
|
@@ -118,7 +128,7 @@ export async function run(subcommand, args, flags) {
|
|
|
118
128
|
if (subcommand === 'list')
|
|
119
129
|
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.');
|
|
120
130
|
else if (subcommand === 'check')
|
|
121
|
-
info('Usage: myapi domain check <domain> --org <id
|
|
131
|
+
info('Usage: myapi domain check <domain> --org <id> [--json]\n\nChecks if a domain name is available for registration.\n\nFlags:\n --json Output raw JSON');
|
|
122
132
|
else if (subcommand === 'register')
|
|
123
133
|
info('Usage: myapi domain register <domain> --org <id> [--years <num>]\n\nRegisters a new domain name.');
|
|
124
134
|
else if (subcommand === 'import')
|
|
@@ -128,7 +138,7 @@ export async function run(subcommand, args, flags) {
|
|
|
128
138
|
else if (subcommand === 'unassign')
|
|
129
139
|
info('Usage: myapi domain unassign <domain> --org <id>\n\nUnassigns a domain from its current organization.');
|
|
130
140
|
else if (subcommand === 'status')
|
|
131
|
-
info('Usage: myapi domain status <domain> --org <id
|
|
141
|
+
info('Usage: myapi domain status <domain> --org <id> [--json]\n\nGets the registration status of a domain.\n\nFlags:\n --json Output raw JSON');
|
|
132
142
|
else if (subcommand === 'settings')
|
|
133
143
|
info('Usage: myapi domain settings <domain> --org <id>\n\nGets the DNS settings and configuration for a domain.');
|
|
134
144
|
else if (subcommand === 'update-settings')
|
package/dist/commands/funnel.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { funnel as sdkFunnel, hq } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
3
|
import { success, error, printTable, info, printJson } from '../output.js';
|
|
4
|
+
import { formatDate } from '../utils.js';
|
|
4
5
|
export async function list(flags) {
|
|
5
6
|
const config = requireConfig();
|
|
6
7
|
const orgId = flags.org || config.default_org;
|
|
7
8
|
if (!orgId)
|
|
8
9
|
error("Missing required arguments.\nUsage: myapi funnel list --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
9
10
|
const funnels = await sdkFunnel.listFunnels(config.api_key, orgId);
|
|
10
|
-
|
|
11
|
+
const rows = funnels.map(f => ({ id: f.id, created_at: formatDate(f.created_at), updated_at: formatDate(f.updated_at) }));
|
|
12
|
+
if (rows.length === 0) {
|
|
13
|
+
info('No funnels found. Create one with: myapi funnel create');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
printTable(rows);
|
|
11
17
|
}
|
|
12
18
|
export async function create(flags) {
|
|
13
19
|
const config = requireConfig();
|
|
@@ -42,7 +48,11 @@ export async function push(id, slug, flags) {
|
|
|
42
48
|
const config = requireConfig();
|
|
43
49
|
const orgId = flags.org || config.default_org;
|
|
44
50
|
const funnelId = id || config.default_funnel;
|
|
45
|
-
|
|
51
|
+
if (slug && flags.slug && slug !== flags.slug) {
|
|
52
|
+
info(`› Note: positional slug "${slug}" takes precedence over --slug "${flags.slug}".`);
|
|
53
|
+
}
|
|
54
|
+
const rawSlug = slug || flags.slug || '/';
|
|
55
|
+
const finalSlug = rawSlug.startsWith('/') ? rawSlug : `/${rawSlug}`;
|
|
46
56
|
if (!orgId || !funnelId) {
|
|
47
57
|
error("Missing required arguments.\nUsage: myapi funnel push [funnel_id] [slug] < index.html\n(Defaults to your configured funnel and slug '/' when omitted)");
|
|
48
58
|
}
|
|
@@ -53,8 +63,8 @@ export async function push(id, slug, flags) {
|
|
|
53
63
|
process.stdin.on('end', () => resolve(data));
|
|
54
64
|
process.stdin.on('error', reject);
|
|
55
65
|
});
|
|
56
|
-
if (!html)
|
|
57
|
-
error("No
|
|
66
|
+
if (!html.trim())
|
|
67
|
+
error("No content provided via stdin. Usage: echo '<h1>Hello</h1>' | myapi funnel push [id] [slug]");
|
|
58
68
|
const result = await sdkFunnel.pushFunnelPage(config.api_key, orgId, funnelId, { slug: finalSlug, html });
|
|
59
69
|
success(`Pushed page to ${finalSlug}`);
|
|
60
70
|
if (result?.url) {
|
package/dist/commands/keys.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hq } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
3
|
import { success, error, printTable, info, printJson } from '../output.js';
|
|
4
|
+
import { formatDate } from '../utils.js';
|
|
4
5
|
import * as readline from 'readline';
|
|
5
6
|
export async function createNew(flags) {
|
|
6
7
|
if (flags.help) {
|
|
@@ -33,15 +34,8 @@ export async function list(flags) {
|
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
36
|
const formattedKeys = keysList.map(k => {
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const createdStr = isNaN(createdDate.getTime()) ? k.created_at : createdDate.toLocaleString();
|
|
39
|
-
// Format last used date
|
|
40
|
-
let lastUsedStr = 'Never';
|
|
41
|
-
if (k.last_used_at) {
|
|
42
|
-
const lastUsedDate = new Date(k.last_used_at);
|
|
43
|
-
lastUsedStr = isNaN(lastUsedDate.getTime()) ? k.last_used_at : lastUsedDate.toLocaleString();
|
|
44
|
-
}
|
|
37
|
+
const createdStr = formatDate(k.created_at);
|
|
38
|
+
const lastUsedStr = k.last_used_at ? formatDate(k.last_used_at) : 'Never';
|
|
45
39
|
return {
|
|
46
40
|
Name: k.name || 'Unnamed',
|
|
47
41
|
Prefix: k.prefix,
|
package/dist/commands/org.js
CHANGED
|
@@ -43,7 +43,7 @@ export async function list(flags) {
|
|
|
43
43
|
}
|
|
44
44
|
export async function get(id, flags) {
|
|
45
45
|
if (flags.help) {
|
|
46
|
-
info('Usage: myapi org get <id
|
|
46
|
+
info('Usage: myapi org get <id> [--json]\n\nFetches details of a specific organization.\n\nFlags:\n --json Output raw JSON');
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
if (!id) {
|
|
@@ -52,10 +52,18 @@ export async function get(id, flags) {
|
|
|
52
52
|
}
|
|
53
53
|
const config = requireConfig();
|
|
54
54
|
const org = await hq.getOrg(config.api_key, id);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
if (flags.json) {
|
|
56
|
+
printJson(org);
|
|
57
|
+
return;
|
|
58
58
|
}
|
|
59
|
+
info(`ID: ${org.id}`);
|
|
60
|
+
info(`Name: ${org.name ?? '(none)'}`);
|
|
61
|
+
if (org.tagline)
|
|
62
|
+
info(`Tagline: ${org.tagline}`);
|
|
63
|
+
if (org.created_at)
|
|
64
|
+
info(`Created: ${formatDate(org.created_at)}`);
|
|
65
|
+
if (org.preview_subdomain)
|
|
66
|
+
info(`Preview: https://${org.preview_subdomain}.makeautonomous.com`);
|
|
59
67
|
}
|
|
60
68
|
export async function del(id, flags) {
|
|
61
69
|
if (flags.help) {
|
package/dist/index.js
CHANGED
|
@@ -16,10 +16,25 @@ import * as domainCmd from './commands/domain.js';
|
|
|
16
16
|
import * as funnelCmd from './commands/funnel.js';
|
|
17
17
|
import * as authCmd from './commands/auth.js';
|
|
18
18
|
import * as configCmd from './commands/config.js';
|
|
19
|
+
const ERROR_MESSAGES = {
|
|
20
|
+
DOMAIN_NOT_FOUND: 'Domain not found.',
|
|
21
|
+
INVALID_DOMAIN: 'Invalid domain name.',
|
|
22
|
+
ORG_NOT_FOUND: 'Organization not found.',
|
|
23
|
+
org_not_found: 'Organization not found.',
|
|
24
|
+
FUNNEL_NOT_FOUND: 'Funnel not found.',
|
|
25
|
+
funnel_not_found: 'Funnel not found.',
|
|
26
|
+
db_error: 'Resource not found or invalid ID.',
|
|
27
|
+
NOT_FOUND: 'Resource not found.',
|
|
28
|
+
FORBIDDEN: 'You do not have permission to perform this action.',
|
|
29
|
+
RATE_LIMITED: 'Too many requests. Please wait a moment and try again.',
|
|
30
|
+
};
|
|
31
|
+
function friendlyError(code) {
|
|
32
|
+
return ERROR_MESSAGES[code] || code;
|
|
33
|
+
}
|
|
19
34
|
async function main() {
|
|
20
35
|
const updatePromise = updateCmd.checkForUpdate(pkg.version).catch(() => { });
|
|
21
36
|
const { args, flags } = parseArgs(process.argv.slice(2));
|
|
22
|
-
if (flags.version || flags.v) {
|
|
37
|
+
if (flags.version || flags.v || flags.V) {
|
|
23
38
|
const [latest] = await Promise.all([updateCmd.latestVersion(), updatePromise]);
|
|
24
39
|
const updateNote = latest && updateCmd.isNewer(latest, pkg.version)
|
|
25
40
|
? ` (update available: ${latest})`
|
|
@@ -27,6 +42,10 @@ async function main() {
|
|
|
27
42
|
info(`myapi ${pkg.version}${updateNote}`);
|
|
28
43
|
return;
|
|
29
44
|
}
|
|
45
|
+
if (flags.help && args.length === 0) {
|
|
46
|
+
printHelp();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
30
49
|
if (args.length === 0) {
|
|
31
50
|
const config = loadConfig();
|
|
32
51
|
if (!config?.api_key) {
|
|
@@ -42,7 +61,7 @@ async function main() {
|
|
|
42
61
|
switch (command) {
|
|
43
62
|
case 'auth':
|
|
44
63
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
45
|
-
info('Usage: myapi auth <subcommand>\n\nSubcommands:\n setup Configure your account\n import-key Import an existing API key non-interactively\n whoami Show current account\n link
|
|
64
|
+
info('Usage: myapi auth <subcommand>\n\nSubcommands:\n setup Configure your account\n import-key Import an existing API key non-interactively\n whoami Show current account\n link [email] Attach an email to your anonymous account (upgrades it to registered)\n If the email already has an account, signs you into it as a second session\n switch [index] Switch between accounts\n config Manage CLI defaults (org_id, domain…)\n install-skills Install the MyAPI skills pack\n api-keys Manage API keys');
|
|
46
65
|
break;
|
|
47
66
|
}
|
|
48
67
|
if (subcommand === 'setup')
|
|
@@ -52,7 +71,7 @@ async function main() {
|
|
|
52
71
|
else if (subcommand === 'whoami')
|
|
53
72
|
await authCmd.whoami(flags);
|
|
54
73
|
else if (subcommand === 'link')
|
|
55
|
-
await authCmd.link(flags);
|
|
74
|
+
await authCmd.link(flags, restArgs[0]);
|
|
56
75
|
else if (subcommand === 'switch')
|
|
57
76
|
await authCmd.switchCmd(flags, restArgs[0]);
|
|
58
77
|
else if (subcommand === 'install-skills') {
|
|
@@ -131,8 +150,26 @@ async function main() {
|
|
|
131
150
|
case 'setup':
|
|
132
151
|
await setupCmd.setup(flags);
|
|
133
152
|
break;
|
|
153
|
+
case 'whoami':
|
|
154
|
+
await authCmd.whoami(flags);
|
|
155
|
+
break;
|
|
156
|
+
case 'keys':
|
|
157
|
+
if (!subcommand) {
|
|
158
|
+
info('Usage: myapi keys <list|create|revoke>');
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
if (subcommand === 'create')
|
|
162
|
+
await keysCmd.createNew(flags);
|
|
163
|
+
else if (subcommand === 'list')
|
|
164
|
+
await keysCmd.list(flags);
|
|
165
|
+
else if (subcommand === 'revoke')
|
|
166
|
+
await keysCmd.revoke(restArgs[0], flags);
|
|
167
|
+
break;
|
|
134
168
|
case 'config':
|
|
135
|
-
await configCmd.run(subcommand, restArgs, flags);
|
|
169
|
+
await configCmd.run(subcommand, restArgs, { ...flags, _via: 'config' });
|
|
170
|
+
break;
|
|
171
|
+
case 'help':
|
|
172
|
+
printHelp();
|
|
136
173
|
break;
|
|
137
174
|
default:
|
|
138
175
|
error(`Unknown command: ${command}. Run "myapi" for available commands.`);
|
|
@@ -143,15 +180,15 @@ async function main() {
|
|
|
143
180
|
if (err.status === 401)
|
|
144
181
|
error('Invalid API key. Run: myapi auth setup');
|
|
145
182
|
else if (err.status === 402) {
|
|
146
|
-
if (err.code === 'REGISTRATION_REQUIRED')
|
|
147
|
-
error('A verified email is required. Run: myapi auth link');
|
|
148
|
-
else if (err.code === 'UPGRADE_REQUIRED')
|
|
183
|
+
if (err.code === 'REGISTRATION_REQUIRED' || err.code === 'UPGRADE_REQUIRED')
|
|
149
184
|
error('A verified email is required. Run: myapi auth link');
|
|
185
|
+
else if (err.code === 'NO_PAYMENT_METHOD')
|
|
186
|
+
error('No payment method on file. Run: myapi billing setup');
|
|
150
187
|
else
|
|
151
188
|
error(`Insufficient balance. Run: myapi billing topup <amount>`);
|
|
152
189
|
}
|
|
153
190
|
else
|
|
154
|
-
error(err.code || err.message);
|
|
191
|
+
error(friendlyError(err.code) || err.message);
|
|
155
192
|
}
|
|
156
193
|
else {
|
|
157
194
|
error(err.message || (typeof err === 'object' ? JSON.stringify(err) : String(err)));
|
|
@@ -179,6 +216,12 @@ Commands:
|
|
|
179
216
|
domain Manage domain configurations
|
|
180
217
|
funnel Manage headless funnels and pages
|
|
181
218
|
|
|
219
|
+
Aliases:
|
|
220
|
+
whoami → myapi auth whoami
|
|
221
|
+
keys → myapi auth api-keys
|
|
222
|
+
setup → myapi auth setup
|
|
223
|
+
config → myapi auth config
|
|
224
|
+
|
|
182
225
|
Run "myapi <command> --help" for subcommand help.
|
|
183
226
|
|
|
184
227
|
${quickStart}`);
|
package/dist/output.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
1
2
|
export function success(message) {
|
|
2
|
-
console.log(`\x1b[32m✓\x1b[0m ${message}`);
|
|
3
|
+
console.log(useColor ? `\x1b[32m✓\x1b[0m ${message}` : `✓ ${message}`);
|
|
3
4
|
}
|
|
4
5
|
export function error(message) {
|
|
5
|
-
console.error(`\x1b[31m✗\x1b[0m ${message}`);
|
|
6
|
+
console.error(useColor ? `\x1b[31m✗\x1b[0m ${message}` : `✗ ${message}`);
|
|
6
7
|
process.exit(1);
|
|
7
8
|
}
|
|
8
9
|
export function info(message) {
|
package/dist/utils.js
CHANGED
|
@@ -3,7 +3,13 @@ export function parseArgs(argv) {
|
|
|
3
3
|
const flags = {};
|
|
4
4
|
for (let i = 0; i < argv.length; i++) {
|
|
5
5
|
const arg = argv[i];
|
|
6
|
-
if (arg
|
|
6
|
+
if (arg === '-h') {
|
|
7
|
+
flags['help'] = true;
|
|
8
|
+
}
|
|
9
|
+
else if (arg === '-v') {
|
|
10
|
+
flags['version'] = true;
|
|
11
|
+
}
|
|
12
|
+
else if (arg.startsWith('--')) {
|
|
7
13
|
if (arg.includes('=')) {
|
|
8
14
|
const [key, value] = arg.slice(2).split('=', 2);
|
|
9
15
|
flags[key] = value;
|
package/package.json
CHANGED
package/src/commands/auth.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
2
|
|
|
3
3
|
import { loadConfig, saveConfig, addAccount, switchAccount, listAccounts } from '../config.js';
|
|
4
|
-
import { info, success, error } from '../output.js';
|
|
4
|
+
import { info, success, error, printJson } from '../output.js';
|
|
5
5
|
import { installSkills } from './setup.js';
|
|
6
6
|
import { hq } from '@myapihq/sdk';
|
|
7
7
|
|
|
@@ -42,9 +42,9 @@ async function patch(path: string, body: unknown, apiKey: string): Promise<unkno
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// myapi auth link — upgrade anonymous session to registered account.
|
|
45
|
-
export async function link(flags: Record<string, string | boolean> = {}) {
|
|
45
|
+
export async function link(flags: Record<string, string | boolean> = {}, emailArg?: string) {
|
|
46
46
|
if (flags.help) {
|
|
47
|
-
info('Usage: myapi auth link\n\
|
|
47
|
+
info('Usage: myapi auth link [email]\n\nAttaches a verified email to your current anonymous account, converting it into\na registered account. A one-time code will be sent to the email to confirm.\n\nIf that email already belongs to an existing MyAPI account, you will be signed\ninto that account instead — it is added as a second active session alongside\nyour current one.\n\nTo create a brand-new account from scratch, use: myapi auth setup');
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
const config = loadConfig();
|
|
@@ -53,7 +53,8 @@ export async function link(flags: Record<string, string | boolean> = {}) {
|
|
|
53
53
|
|
|
54
54
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
55
55
|
try {
|
|
56
|
-
const email = (await ask(rl, '› Email? ')).trim();
|
|
56
|
+
const email = emailArg || (await ask(rl, '› Email? ')).trim();
|
|
57
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) error(`Invalid email address: "${email}".`);
|
|
57
58
|
|
|
58
59
|
let upgradeOk = true;
|
|
59
60
|
try {
|
|
@@ -116,24 +117,36 @@ export async function link(flags: Record<string, string | boolean> = {}) {
|
|
|
116
117
|
// myapi auth whoami — show current session info.
|
|
117
118
|
export async function whoami(flags: Record<string, string | boolean> = {}) {
|
|
118
119
|
if (flags.help) {
|
|
119
|
-
info('Usage: myapi auth whoami\n\nDisplays the active account details: email, account ID, default org, default funnel, account type, and current balance
|
|
120
|
+
info('Usage: myapi auth whoami [--json]\n\nDisplays the active account details: email, account ID, default org, default funnel, account type, and current balance.\n\nFlags:\n --json Output raw JSON');
|
|
120
121
|
return;
|
|
121
122
|
}
|
|
122
123
|
const config = loadConfig();
|
|
123
124
|
if (!config?.api_key) error('Not configured. Run: myapi auth setup');
|
|
124
125
|
|
|
126
|
+
let balance: { balance_display: string; credits_display: string } | null = null;
|
|
127
|
+
try {
|
|
128
|
+
balance = await hq.getBalance(config!.api_key);
|
|
129
|
+
} catch {}
|
|
130
|
+
|
|
131
|
+
if (flags.json) {
|
|
132
|
+
printJson({
|
|
133
|
+
email: config!.email ?? null,
|
|
134
|
+
account_id: config!.account_id,
|
|
135
|
+
default_org: config!.default_org ?? null,
|
|
136
|
+
default_funnel: config!.default_funnel ?? null,
|
|
137
|
+
is_anonymous: config!.is_anonymous ?? false,
|
|
138
|
+
balance: balance?.balance_display ?? null,
|
|
139
|
+
credits: balance?.credits_display ?? null,
|
|
140
|
+
});
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
125
144
|
if (config!.email) info(`Email: ${config!.email}`);
|
|
126
145
|
info(`Account: ${config!.account_id}`);
|
|
127
146
|
info(`Org: ${config!.default_org ?? '(none)'}`);
|
|
128
147
|
info(`Funnel: ${config!.default_funnel ?? '(none)'}`);
|
|
129
148
|
info(`Type: ${config!.is_anonymous ? 'anonymous' : 'registered'}`);
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
const bal = await hq.getBalance(config!.api_key);
|
|
133
|
-
info(`Balance: ${bal.balance_display} | Credits: ${bal.credits_display}`);
|
|
134
|
-
} catch {
|
|
135
|
-
info(`Balance: (unavailable)`);
|
|
136
|
-
}
|
|
149
|
+
info(`Balance: ${balance ? `${balance.balance_display} | Credits: ${balance.credits_display}` : '(unavailable)'}`);
|
|
137
150
|
}
|
|
138
151
|
|
|
139
152
|
// myapi auth switch [index] — switch between saved accounts.
|
|
@@ -146,6 +159,7 @@ export async function switchCmd(flags: Record<string, string | boolean> = {}, in
|
|
|
146
159
|
if (accounts.length === 0) error('No accounts configured. Run: myapi auth setup');
|
|
147
160
|
if (accounts.length === 1) {
|
|
148
161
|
info(`Only one account configured: ${accounts[0].email ?? accounts[0].account_id}`);
|
|
162
|
+
info('To add another account, run: myapi auth setup');
|
|
149
163
|
return;
|
|
150
164
|
}
|
|
151
165
|
|
package/src/commands/billing.ts
CHANGED
|
@@ -6,12 +6,21 @@ import { formatDate } from '../utils.js';
|
|
|
6
6
|
|
|
7
7
|
export async function balance(flags: Record<string, string | boolean>) {
|
|
8
8
|
if (flags.help) {
|
|
9
|
-
info('Usage: myapi billing balance\n\nShows your current account balance, credits, and payment method status
|
|
9
|
+
info('Usage: myapi billing balance [--json]\n\nShows your current account balance, credits, and payment method status.\n\nFlags:\n --json Output raw JSON');
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
const config = requireConfig();
|
|
13
13
|
const result = await hq.getBalance(config.api_key);
|
|
14
14
|
|
|
15
|
+
if (flags.json) {
|
|
16
|
+
printJson({
|
|
17
|
+
balance: result.balance_display,
|
|
18
|
+
credits: result.credits_display,
|
|
19
|
+
has_payment_method: result.has_payment_method,
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
const pm = result.has_payment_method ? 'yes' : 'no';
|
|
16
25
|
const accountType = config.is_anonymous ? 'anonymous' : `registered (${config.email ?? ''})`;
|
|
17
26
|
info(`Account: ${accountType}`);
|
package/src/commands/config.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
+
import { hq, funnel as sdkFunnel } from '@myapihq/sdk';
|
|
1
2
|
import { requireConfig, saveConfig } from '../config.js';
|
|
2
3
|
import { success, error, info } from '../output.js';
|
|
3
4
|
|
|
5
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6
|
+
|
|
4
7
|
export async function setOrg(id: string, flags: Record<string, string | boolean>) {
|
|
5
8
|
if (!id || flags.help) {
|
|
6
9
|
info("Usage: myapi config set-org <id>\n\nSets a default organization ID for subsequent commands.");
|
|
7
10
|
return;
|
|
8
11
|
}
|
|
12
|
+
if (!UUID_RE.test(id)) error(`Invalid organization ID: "${id}". Expected a UUID (e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).`);
|
|
9
13
|
const config = requireConfig();
|
|
10
|
-
|
|
14
|
+
info('› Validating…');
|
|
15
|
+
const org = await hq.getOrg(config.api_key, id);
|
|
16
|
+
config.default_org = org.id;
|
|
11
17
|
saveConfig(config);
|
|
12
|
-
success(`Default organization set to: ${id}`);
|
|
18
|
+
success(`Default organization set to: ${org.name || org.id}`);
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
export async function setFunnel(id: string, flags: Record<string, string | boolean>) {
|
|
@@ -17,10 +23,15 @@ export async function setFunnel(id: string, flags: Record<string, string | boole
|
|
|
17
23
|
info("Usage: myapi config set-funnel <id>\n\nSets a default funnel ID for subsequent commands.");
|
|
18
24
|
return;
|
|
19
25
|
}
|
|
26
|
+
if (!UUID_RE.test(id)) error(`Invalid funnel ID: "${id}". Expected a UUID (e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).`);
|
|
20
27
|
const config = requireConfig();
|
|
21
|
-
|
|
28
|
+
const orgId = config.default_org;
|
|
29
|
+
if (!orgId) error('No default organization set. Run: myapi auth config set-org <id>');
|
|
30
|
+
info('› Validating…');
|
|
31
|
+
const funnel = await sdkFunnel.getFunnel(config.api_key, orgId, id);
|
|
32
|
+
config.default_funnel = funnel.id;
|
|
22
33
|
saveConfig(config);
|
|
23
|
-
success(`Default funnel set to: ${id}`);
|
|
34
|
+
success(`Default funnel set to: ${funnel.id}`);
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
export async function setDomain(domain: string, flags: Record<string, string | boolean>) {
|
|
@@ -46,8 +57,9 @@ export async function view(flags: Record<string, string | boolean>) {
|
|
|
46
57
|
}
|
|
47
58
|
|
|
48
59
|
export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
|
|
60
|
+
const via = (flags._via as string) || 'auth config';
|
|
49
61
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
50
|
-
info(
|
|
62
|
+
info(`Usage: myapi ${via} <subcommand>\n\nSubcommands:\n view View current config\n set-org Set default organization\n set-funnel Set default funnel\n set-domain Set default domain`);
|
|
51
63
|
return;
|
|
52
64
|
}
|
|
53
65
|
|
package/src/commands/domain.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { domain as sdkDomain } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
3
|
import { success, error, info, printTable, printJson } from '../output.js';
|
|
4
|
+
import { formatDate } from '../utils.js';
|
|
4
5
|
|
|
5
6
|
export async function check(domainArg: string, flags: Record<string, string | boolean>) {
|
|
6
7
|
const config = requireConfig();
|
|
@@ -8,6 +9,10 @@ export async function check(domainArg: string, flags: Record<string, string | bo
|
|
|
8
9
|
const domain = domainArg || (config.default_domain as string);
|
|
9
10
|
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain check <domain> --org <id>\n(Set defaults: myapi auth config set-org <id> / myapi auth config set-domain <domain>)");
|
|
10
11
|
const res = await sdkDomain.checkDomain(config.api_key, orgId, domain) as { available: boolean; price_cents: number; price_display?: string; message?: unknown };
|
|
12
|
+
if (flags.json) {
|
|
13
|
+
printJson(res);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
11
16
|
if (res.available) {
|
|
12
17
|
const price = res.price_display || `$${(res.price_cents / 100).toFixed(2)}`;
|
|
13
18
|
info(`${domain} — Available ✓ ${price}/yr`);
|
|
@@ -45,6 +50,7 @@ export async function list(flags: Record<string, string | boolean>) {
|
|
|
45
50
|
if (!orgId) error("Missing required arguments.\nUsage: myapi domain list --org <id> [--filter=all|unassigned|org]\n(Set default: myapi auth config set-org <id>)");
|
|
46
51
|
const filter = flags.filter as 'all' | 'unassigned' | 'org' | undefined;
|
|
47
52
|
const domains = await sdkDomain.listDomains(config.api_key, orgId, filter);
|
|
53
|
+
if ((domains as unknown[]).length === 0) { info('No domains found in this organization. Register one with: myapi domain register <name>'); return; }
|
|
48
54
|
printTable(domains as unknown as Record<string, unknown>[]);
|
|
49
55
|
}
|
|
50
56
|
|
|
@@ -73,12 +79,13 @@ export async function status(domainArg: string, flags: Record<string, string | b
|
|
|
73
79
|
const domain = domainArg || (config.default_domain as string);
|
|
74
80
|
if (!orgId || !domain) error("Missing required arguments.\nUsage: myapi domain status <domain> --org <id>\n(Set defaults: myapi auth config set-org <id> / myapi auth config set-domain <domain>)");
|
|
75
81
|
const res = await sdkDomain.getDomainStatus(config.api_key, orgId, domain);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
if (flags.json) {
|
|
83
|
+
printJson(res);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
info(`Domain: ${res.domain}`);
|
|
87
|
+
info(`Status: ${res.status}`);
|
|
88
|
+
if ((res as any).expires_at) info(`Expires: ${formatDate((res as any).expires_at)}`);
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
export async function settings(domainArg: string, flags: Record<string, string | boolean>) {
|
|
@@ -112,12 +119,12 @@ export async function run(subcommand: string | undefined, args: string[], flags:
|
|
|
112
119
|
|
|
113
120
|
if (flags.help) {
|
|
114
121
|
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.');
|
|
115
|
-
else if (subcommand === 'check') info('Usage: myapi domain check <domain> --org <id
|
|
122
|
+
else if (subcommand === 'check') info('Usage: myapi domain check <domain> --org <id> [--json]\n\nChecks if a domain name is available for registration.\n\nFlags:\n --json Output raw JSON');
|
|
116
123
|
else if (subcommand === 'register') info('Usage: myapi domain register <domain> --org <id> [--years <num>]\n\nRegisters a new domain name.');
|
|
117
124
|
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.');
|
|
118
125
|
else if (subcommand === 'assign') info('Usage: myapi domain assign <domain> --org <id> --target <target_org_id>\n\nAssigns a domain to a different organization.');
|
|
119
126
|
else if (subcommand === 'unassign') info('Usage: myapi domain unassign <domain> --org <id>\n\nUnassigns a domain from its current organization.');
|
|
120
|
-
else if (subcommand === 'status') info('Usage: myapi domain status <domain> --org <id
|
|
127
|
+
else if (subcommand === 'status') info('Usage: myapi domain status <domain> --org <id> [--json]\n\nGets the registration status of a domain.\n\nFlags:\n --json Output raw JSON');
|
|
121
128
|
else if (subcommand === 'settings') info('Usage: myapi domain settings <domain> --org <id>\n\nGets the DNS settings and configuration for a domain.');
|
|
122
129
|
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.');
|
|
123
130
|
return;
|
package/src/commands/funnel.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { funnel as sdkFunnel, hq } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
3
|
import { success, error, printTable, info, printJson } from '../output.js';
|
|
4
|
+
import { formatDate } from '../utils.js';
|
|
4
5
|
import * as fs from 'fs';
|
|
5
6
|
|
|
6
7
|
export async function list(flags: Record<string, string | boolean>) {
|
|
@@ -8,7 +9,9 @@ export async function list(flags: Record<string, string | boolean>) {
|
|
|
8
9
|
const orgId = (flags.org as string) || config.default_org;
|
|
9
10
|
if (!orgId) error("Missing required arguments.\nUsage: myapi funnel list --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
10
11
|
const funnels = await sdkFunnel.listFunnels(config.api_key, orgId);
|
|
11
|
-
|
|
12
|
+
const rows = funnels.map(f => ({ id: f.id, created_at: formatDate(f.created_at), updated_at: formatDate(f.updated_at) }));
|
|
13
|
+
if (rows.length === 0) { info('No funnels found. Create one with: myapi funnel create'); return; }
|
|
14
|
+
printTable(rows as unknown as Record<string, unknown>[]);
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
export async function create(flags: Record<string, string | boolean>) {
|
|
@@ -45,7 +48,11 @@ export async function push(id: string, slug: string, flags: Record<string, strin
|
|
|
45
48
|
const config = requireConfig();
|
|
46
49
|
const orgId = (flags.org as string) || config.default_org;
|
|
47
50
|
const funnelId = id || config.default_funnel;
|
|
48
|
-
|
|
51
|
+
if (slug && flags.slug && slug !== flags.slug) {
|
|
52
|
+
info(`› Note: positional slug "${slug}" takes precedence over --slug "${flags.slug}".`);
|
|
53
|
+
}
|
|
54
|
+
const rawSlug = slug || (flags.slug as string) || '/';
|
|
55
|
+
const finalSlug = rawSlug.startsWith('/') ? rawSlug : `/${rawSlug}`;
|
|
49
56
|
|
|
50
57
|
if (!orgId || !funnelId) {
|
|
51
58
|
error("Missing required arguments.\nUsage: myapi funnel push [funnel_id] [slug] < index.html\n(Defaults to your configured funnel and slug '/' when omitted)");
|
|
@@ -59,7 +66,7 @@ export async function push(id: string, slug: string, flags: Record<string, strin
|
|
|
59
66
|
process.stdin.on('error', reject);
|
|
60
67
|
});
|
|
61
68
|
|
|
62
|
-
if (!html) error("No
|
|
69
|
+
if (!html.trim()) error("No content provided via stdin. Usage: echo '<h1>Hello</h1>' | myapi funnel push [id] [slug]");
|
|
63
70
|
|
|
64
71
|
const result = await sdkFunnel.pushFunnelPage(config.api_key, orgId, funnelId, { slug: finalSlug, html });
|
|
65
72
|
success(`Pushed page to ${finalSlug}`);
|
package/src/commands/keys.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hq } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
3
|
import { success, error, printTable, info, printJson } from '../output.js';
|
|
4
|
+
import { formatDate } from '../utils.js';
|
|
4
5
|
import * as readline from 'readline';
|
|
5
6
|
|
|
6
7
|
export async function createNew(flags: Record<string, string | boolean>) {
|
|
@@ -37,16 +38,8 @@ export async function list(flags: Record<string, string | boolean>) {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
const formattedKeys = keysList.map(k => {
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const createdStr = isNaN(createdDate.getTime()) ? k.created_at : createdDate.toLocaleString();
|
|
43
|
-
|
|
44
|
-
// Format last used date
|
|
45
|
-
let lastUsedStr = 'Never';
|
|
46
|
-
if (k.last_used_at) {
|
|
47
|
-
const lastUsedDate = new Date(k.last_used_at);
|
|
48
|
-
lastUsedStr = isNaN(lastUsedDate.getTime()) ? k.last_used_at : lastUsedDate.toLocaleString();
|
|
49
|
-
}
|
|
41
|
+
const createdStr = formatDate(k.created_at);
|
|
42
|
+
const lastUsedStr = k.last_used_at ? formatDate(k.last_used_at) : 'Never';
|
|
50
43
|
|
|
51
44
|
return {
|
|
52
45
|
Name: k.name || 'Unnamed',
|
package/src/commands/org.ts
CHANGED
|
@@ -43,7 +43,7 @@ export async function list(flags: Record<string, string | boolean>) {
|
|
|
43
43
|
|
|
44
44
|
export async function get(id: string, flags: Record<string, string | boolean>) {
|
|
45
45
|
if (flags.help) {
|
|
46
|
-
info('Usage: myapi org get <id
|
|
46
|
+
info('Usage: myapi org get <id> [--json]\n\nFetches details of a specific organization.\n\nFlags:\n --json Output raw JSON');
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
if (!id) {
|
|
@@ -52,10 +52,15 @@ export async function get(id: string, flags: Record<string, string | boolean>) {
|
|
|
52
52
|
}
|
|
53
53
|
const config = requireConfig();
|
|
54
54
|
const org = await hq.getOrg(config.api_key, id);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
if (flags.json) {
|
|
56
|
+
printJson(org);
|
|
57
|
+
return;
|
|
58
58
|
}
|
|
59
|
+
info(`ID: ${org.id}`);
|
|
60
|
+
info(`Name: ${org.name ?? '(none)'}`);
|
|
61
|
+
if ((org as any).tagline) info(`Tagline: ${(org as any).tagline}`);
|
|
62
|
+
if ((org as any).created_at) info(`Created: ${formatDate((org as any).created_at)}`);
|
|
63
|
+
if (org.preview_subdomain) info(`Preview: https://${org.preview_subdomain}.makeautonomous.com`);
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
export async function del(id: string, flags: Record<string, string | boolean>) {
|
package/src/index.ts
CHANGED
|
@@ -18,12 +18,29 @@ import * as funnelCmd from './commands/funnel.js';
|
|
|
18
18
|
import * as authCmd from './commands/auth.js';
|
|
19
19
|
import * as configCmd from './commands/config.js';
|
|
20
20
|
|
|
21
|
+
const ERROR_MESSAGES: Record<string, string> = {
|
|
22
|
+
DOMAIN_NOT_FOUND: 'Domain not found.',
|
|
23
|
+
INVALID_DOMAIN: 'Invalid domain name.',
|
|
24
|
+
ORG_NOT_FOUND: 'Organization not found.',
|
|
25
|
+
org_not_found: 'Organization not found.',
|
|
26
|
+
FUNNEL_NOT_FOUND: 'Funnel not found.',
|
|
27
|
+
funnel_not_found: 'Funnel not found.',
|
|
28
|
+
db_error: 'Resource not found or invalid ID.',
|
|
29
|
+
NOT_FOUND: 'Resource not found.',
|
|
30
|
+
FORBIDDEN: 'You do not have permission to perform this action.',
|
|
31
|
+
RATE_LIMITED: 'Too many requests. Please wait a moment and try again.',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function friendlyError(code: string): string {
|
|
35
|
+
return ERROR_MESSAGES[code] || code;
|
|
36
|
+
}
|
|
37
|
+
|
|
21
38
|
async function main() {
|
|
22
39
|
const updatePromise = updateCmd.checkForUpdate(pkg.version).catch(() => {});
|
|
23
40
|
|
|
24
41
|
const { args, flags } = parseArgs(process.argv.slice(2));
|
|
25
42
|
|
|
26
|
-
if (flags.version || flags.v) {
|
|
43
|
+
if (flags.version || flags.v || flags.V) {
|
|
27
44
|
const [latest] = await Promise.all([updateCmd.latestVersion(), updatePromise]);
|
|
28
45
|
const updateNote = latest && updateCmd.isNewer(latest, pkg.version)
|
|
29
46
|
? ` (update available: ${latest})`
|
|
@@ -32,6 +49,11 @@ async function main() {
|
|
|
32
49
|
return;
|
|
33
50
|
}
|
|
34
51
|
|
|
52
|
+
if (flags.help && args.length === 0) {
|
|
53
|
+
printHelp();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
35
57
|
if (args.length === 0) {
|
|
36
58
|
const config = loadConfig();
|
|
37
59
|
if (!config?.api_key) {
|
|
@@ -48,13 +70,13 @@ async function main() {
|
|
|
48
70
|
switch (command) {
|
|
49
71
|
case 'auth':
|
|
50
72
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
51
|
-
info('Usage: myapi auth <subcommand>\n\nSubcommands:\n setup Configure your account\n import-key Import an existing API key non-interactively\n whoami Show current account\n link
|
|
73
|
+
info('Usage: myapi auth <subcommand>\n\nSubcommands:\n setup Configure your account\n import-key Import an existing API key non-interactively\n whoami Show current account\n link [email] Attach an email to your anonymous account (upgrades it to registered)\n If the email already has an account, signs you into it as a second session\n switch [index] Switch between accounts\n config Manage CLI defaults (org_id, domain…)\n install-skills Install the MyAPI skills pack\n api-keys Manage API keys');
|
|
52
74
|
break;
|
|
53
75
|
}
|
|
54
76
|
if (subcommand === 'setup') await setupCmd.setup(flags);
|
|
55
77
|
else if (subcommand === 'import-key') await setupCmd.importKey(restArgs[0], flags);
|
|
56
78
|
else if (subcommand === 'whoami') await authCmd.whoami(flags);
|
|
57
|
-
else if (subcommand === 'link') await authCmd.link(flags);
|
|
79
|
+
else if (subcommand === 'link') await authCmd.link(flags, restArgs[0]);
|
|
58
80
|
else if (subcommand === 'switch') await authCmd.switchCmd(flags, restArgs[0]);
|
|
59
81
|
else if (subcommand === 'install-skills') {
|
|
60
82
|
if (flags.help) {
|
|
@@ -111,8 +133,20 @@ async function main() {
|
|
|
111
133
|
case 'setup':
|
|
112
134
|
await setupCmd.setup(flags);
|
|
113
135
|
break;
|
|
136
|
+
case 'whoami':
|
|
137
|
+
await authCmd.whoami(flags);
|
|
138
|
+
break;
|
|
139
|
+
case 'keys':
|
|
140
|
+
if (!subcommand) { info('Usage: myapi keys <list|create|revoke>'); break; }
|
|
141
|
+
if (subcommand === 'create') await keysCmd.createNew(flags);
|
|
142
|
+
else if (subcommand === 'list') await keysCmd.list(flags);
|
|
143
|
+
else if (subcommand === 'revoke') await keysCmd.revoke(restArgs[0], flags);
|
|
144
|
+
break;
|
|
114
145
|
case 'config':
|
|
115
|
-
await configCmd.run(subcommand, restArgs, flags);
|
|
146
|
+
await configCmd.run(subcommand, restArgs, { ...flags, _via: 'config' });
|
|
147
|
+
break;
|
|
148
|
+
case 'help':
|
|
149
|
+
printHelp();
|
|
116
150
|
break;
|
|
117
151
|
default:
|
|
118
152
|
error(`Unknown command: ${command}. Run "myapi" for available commands.`);
|
|
@@ -121,11 +155,11 @@ async function main() {
|
|
|
121
155
|
if (err instanceof MyApiError) {
|
|
122
156
|
if (err.status === 401) error('Invalid API key. Run: myapi auth setup');
|
|
123
157
|
else if (err.status === 402) {
|
|
124
|
-
if (err.code === 'REGISTRATION_REQUIRED') error('A verified email is required. Run: myapi auth link');
|
|
125
|
-
else if (err.code === '
|
|
158
|
+
if (err.code === 'REGISTRATION_REQUIRED' || err.code === 'UPGRADE_REQUIRED') error('A verified email is required. Run: myapi auth link');
|
|
159
|
+
else if (err.code === 'NO_PAYMENT_METHOD') error('No payment method on file. Run: myapi billing setup');
|
|
126
160
|
else error(`Insufficient balance. Run: myapi billing topup <amount>`);
|
|
127
161
|
}
|
|
128
|
-
else error(err.code || err.message);
|
|
162
|
+
else error(friendlyError(err.code) || err.message);
|
|
129
163
|
} else {
|
|
130
164
|
error(err.message || (typeof err === 'object' ? JSON.stringify(err) : String(err)));
|
|
131
165
|
}
|
|
@@ -153,6 +187,12 @@ Commands:
|
|
|
153
187
|
domain Manage domain configurations
|
|
154
188
|
funnel Manage headless funnels and pages
|
|
155
189
|
|
|
190
|
+
Aliases:
|
|
191
|
+
whoami → myapi auth whoami
|
|
192
|
+
keys → myapi auth api-keys
|
|
193
|
+
setup → myapi auth setup
|
|
194
|
+
config → myapi auth config
|
|
195
|
+
|
|
156
196
|
Run "myapi <command> --help" for subcommand help.
|
|
157
197
|
|
|
158
198
|
${quickStart}`);
|
package/src/output.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
2
|
+
|
|
1
3
|
export function success(message: string): void {
|
|
2
|
-
console.log(`\x1b[32m✓\x1b[0m ${message}`);
|
|
4
|
+
console.log(useColor ? `\x1b[32m✓\x1b[0m ${message}` : `✓ ${message}`);
|
|
3
5
|
}
|
|
4
6
|
|
|
5
7
|
export function error(message: string): never {
|
|
6
|
-
console.error(`\x1b[31m✗\x1b[0m ${message}`);
|
|
8
|
+
console.error(useColor ? `\x1b[31m✗\x1b[0m ${message}` : `✗ ${message}`);
|
|
7
9
|
process.exit(1);
|
|
8
10
|
}
|
|
9
11
|
|
package/src/utils.ts
CHANGED
|
@@ -4,7 +4,11 @@ export function parseArgs(argv: string[]) {
|
|
|
4
4
|
|
|
5
5
|
for (let i = 0; i < argv.length; i++) {
|
|
6
6
|
const arg = argv[i];
|
|
7
|
-
if (arg
|
|
7
|
+
if (arg === '-h') {
|
|
8
|
+
flags['help'] = true;
|
|
9
|
+
} else if (arg === '-v') {
|
|
10
|
+
flags['version'] = true;
|
|
11
|
+
} else if (arg.startsWith('--')) {
|
|
8
12
|
if (arg.includes('=')) {
|
|
9
13
|
const [key, value] = arg.slice(2).split('=', 2);
|
|
10
14
|
flags[key] = value;
|