@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/dist/commands/auth.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
2
|
import { loadConfig, saveConfig, addAccount, switchAccount, listAccounts } from '../config.js';
|
|
3
3
|
import { info, success, error } from '../output.js';
|
|
4
|
+
import { installSkills } from './setup.js';
|
|
5
|
+
import { hq } from '@myapihq/sdk';
|
|
4
6
|
const API_BASE = process.env.MYAPI_API_BASE ?? 'https://api.myapihq.com';
|
|
5
7
|
function ask(rl, q) {
|
|
6
8
|
return new Promise(resolve => rl.question(q, resolve));
|
|
@@ -64,6 +66,8 @@ export async function signup() {
|
|
|
64
66
|
info(`› Sent a code to ${email} · paste it below`);
|
|
65
67
|
const code = (await ask(rl, '› Code? ')).trim();
|
|
66
68
|
const data = await post('/hq/account/verify-code', { email, code });
|
|
69
|
+
const skillsAns = (await ask(rl, '› Install the MyAPI skills pack? (Y/n) ')).trim();
|
|
70
|
+
const wantsSkills = yn(skillsAns);
|
|
67
71
|
if (upgradeOk) {
|
|
68
72
|
// Upgrade: update current account in place.
|
|
69
73
|
saveConfig({
|
|
@@ -74,6 +78,7 @@ export async function signup() {
|
|
|
74
78
|
default_org: data.default_org || config.default_org,
|
|
75
79
|
default_funnel: data.default_funnel || config.default_funnel,
|
|
76
80
|
is_anonymous: false,
|
|
81
|
+
skills_installed: wantsSkills,
|
|
77
82
|
});
|
|
78
83
|
success(`› Welcome! Account upgraded · ${email}`);
|
|
79
84
|
}
|
|
@@ -87,10 +92,13 @@ export async function signup() {
|
|
|
87
92
|
default_org: data.default_org,
|
|
88
93
|
default_funnel: data.default_funnel,
|
|
89
94
|
is_anonymous: false,
|
|
95
|
+
skills_installed: wantsSkills,
|
|
90
96
|
});
|
|
91
97
|
success(`› Signed in · ${email} (account #${idx + 1})`);
|
|
92
98
|
info(`› Use "myapi auth switch" to toggle between accounts.`);
|
|
93
99
|
}
|
|
100
|
+
if (wantsSkills)
|
|
101
|
+
await installSkills();
|
|
94
102
|
}
|
|
95
103
|
finally {
|
|
96
104
|
rl.close();
|
|
@@ -107,6 +115,13 @@ export async function whoami() {
|
|
|
107
115
|
info(`Org: ${config.default_org ?? '(none)'}`);
|
|
108
116
|
info(`Funnel: ${config.default_funnel ?? '(none)'}`);
|
|
109
117
|
info(`Type: ${config.is_anonymous ? 'anonymous' : 'registered'}`);
|
|
118
|
+
try {
|
|
119
|
+
const bal = await hq.getBalance(config.api_key);
|
|
120
|
+
info(`Balance: ${bal.balance_display} | Credits: ${bal.credits_display}`);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Balance fetch is best-effort; don't fail whoami if it errors.
|
|
124
|
+
}
|
|
110
125
|
}
|
|
111
126
|
// myapi auth switch — switch between saved accounts.
|
|
112
127
|
export async function switchCmd() {
|
package/dist/commands/billing.js
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
|
export async function balance(flags) {
|
|
5
7
|
if (flags.help) {
|
|
6
8
|
info('Usage: myapi billing balance\n\nShows your current account balance, credits, and payment method status.');
|
|
@@ -8,10 +10,10 @@ export async function balance(flags) {
|
|
|
8
10
|
}
|
|
9
11
|
const config = requireConfig();
|
|
10
12
|
const result = await hq.getBalance(config.api_key);
|
|
11
|
-
const bal = result.balance_display || `$${(result.balance_cents / 100).toFixed(2)}`;
|
|
12
|
-
const cred = result.credits_display || `$${((result.credits_cents || 0) / 100).toFixed(2)}`;
|
|
13
13
|
const pm = result.has_payment_method ? 'yes' : 'no';
|
|
14
|
-
|
|
14
|
+
const accountType = config.is_anonymous ? 'anonymous' : `registered (${config.email ?? ''})`;
|
|
15
|
+
info(`Account: ${accountType}`);
|
|
16
|
+
info(`Balance: ${result.balance_display} | Credits: ${result.credits_display} | Payment method: ${pm}`);
|
|
15
17
|
}
|
|
16
18
|
export async function history(flags) {
|
|
17
19
|
if (flags.help) {
|
|
@@ -24,41 +26,40 @@ export async function history(flags) {
|
|
|
24
26
|
printJson(items);
|
|
25
27
|
return;
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Type: item.type || 'unknown',
|
|
38
|
-
Amount: amountStr,
|
|
39
|
-
Status: item.status ? item.status.charAt(0).toUpperCase() + item.status.slice(1) : '',
|
|
40
|
-
Date: dateStr
|
|
41
|
-
};
|
|
42
|
-
});
|
|
29
|
+
if (items.length === 0) {
|
|
30
|
+
info('No transactions yet.');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const formattedItems = items.map(item => ({
|
|
34
|
+
Type: item.type || 'unknown',
|
|
35
|
+
Amount: item.amount_display,
|
|
36
|
+
Status: item.status ? item.status.charAt(0).toUpperCase() + item.status.slice(1) : '',
|
|
37
|
+
Date: formatDate(item.created_at),
|
|
38
|
+
}));
|
|
43
39
|
printTable(formattedItems);
|
|
44
40
|
}
|
|
45
41
|
export async function topup(amountStr, flags) {
|
|
46
42
|
if (flags.help) {
|
|
47
|
-
info('Usage: myapi billing topup <
|
|
43
|
+
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');
|
|
48
44
|
return;
|
|
49
45
|
}
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
const amount = Math.round(parseFloat(amountStr));
|
|
47
|
+
if (!amountStr || isNaN(amount) || amount <= 0) {
|
|
48
|
+
error("Amount must be a positive whole number of dollars (e.g. myapi billing topup 10)");
|
|
52
49
|
return;
|
|
53
50
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
if (!flags.yes && !flags.y && amount >= 50) {
|
|
52
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
53
|
+
const ans = await new Promise(resolve => rl.question(`› Charge $${amount} to your saved payment method? (y/N) `, resolve));
|
|
54
|
+
rl.close();
|
|
55
|
+
if (ans.trim().toLowerCase() !== 'y' && ans.trim().toLowerCase() !== 'yes') {
|
|
56
|
+
info('Cancelled.');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
58
59
|
}
|
|
59
60
|
const config = requireConfig();
|
|
60
|
-
const result = await hq.topUp(config.api_key,
|
|
61
|
-
success(`Top up successful! New balance:
|
|
61
|
+
const result = await hq.topUp(config.api_key, amount);
|
|
62
|
+
success(`Top up successful! New balance: ${result.new_balance_display}`);
|
|
62
63
|
}
|
|
63
64
|
export async function setup(flags) {
|
|
64
65
|
if (flags.help) {
|
|
@@ -4,6 +4,7 @@ export declare function importDomain(domainArg: string, flags: Record<string, st
|
|
|
4
4
|
export declare function list(flags: Record<string, string | boolean>): Promise<void>;
|
|
5
5
|
export declare function assign(domainArg: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
6
6
|
export declare function unassign(domainArg: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
7
|
+
export declare function status(domainArg: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
7
8
|
export declare function settings(domainArg: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
8
9
|
export declare function updateSettings(domainArg: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
9
10
|
export declare function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>): Promise<void>;
|
package/dist/commands/domain.js
CHANGED
|
@@ -6,13 +6,14 @@ export async function check(domainArg, flags) {
|
|
|
6
6
|
const orgId = flags.org || config.default_org;
|
|
7
7
|
const domain = domainArg || config.default_domain;
|
|
8
8
|
if (!orgId || !domain)
|
|
9
|
-
error("Missing required arguments.\nUsage: myapi domain check <domain> --org <id>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
9
|
+
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
10
|
const res = await sdkDomain.checkDomain(config.api_key, orgId, domain);
|
|
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
|
}
|
|
14
15
|
else {
|
|
15
|
-
info(
|
|
16
|
+
info(`${domain} — ${res.message || 'Not available'} ✗`);
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
export async function register(domainArg, flags) {
|
|
@@ -20,7 +21,7 @@ export async function register(domainArg, flags) {
|
|
|
20
21
|
const orgId = flags.org || config.default_org;
|
|
21
22
|
const domain = domainArg || config.default_domain;
|
|
22
23
|
if (!orgId || !domain)
|
|
23
|
-
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>)");
|
|
24
|
+
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>)");
|
|
24
25
|
const years = parseInt(flags.years) || 1;
|
|
25
26
|
const res = await sdkDomain.registerDomain(config.api_key, orgId, domain, years);
|
|
26
27
|
success(`Registered ${domain}!\n${JSON.stringify(res, null, 2)}`);
|
|
@@ -30,7 +31,7 @@ export async function importDomain(domainArg, flags) {
|
|
|
30
31
|
const orgId = flags.org || config.default_org;
|
|
31
32
|
const domain = domainArg || config.default_domain;
|
|
32
33
|
if (!orgId || !domain)
|
|
33
|
-
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>)");
|
|
34
|
+
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>)");
|
|
34
35
|
const payload = { domain };
|
|
35
36
|
if (flags['namecheap-user'])
|
|
36
37
|
payload.namecheap_api_user = flags['namecheap-user'];
|
|
@@ -43,7 +44,7 @@ export async function list(flags) {
|
|
|
43
44
|
const config = requireConfig();
|
|
44
45
|
const orgId = flags.org || config.default_org;
|
|
45
46
|
if (!orgId)
|
|
46
|
-
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>)");
|
|
47
|
+
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>)");
|
|
47
48
|
const filter = flags.filter;
|
|
48
49
|
const domains = await sdkDomain.listDomains(config.api_key, orgId, filter);
|
|
49
50
|
printTable(domains);
|
|
@@ -54,8 +55,8 @@ export async function assign(domainArg, flags) {
|
|
|
54
55
|
const domain = domainArg || config.default_domain;
|
|
55
56
|
const targetOrgId = flags.target;
|
|
56
57
|
if (!orgId || !domain || !targetOrgId)
|
|
57
|
-
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>)");
|
|
58
|
-
|
|
58
|
+
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>)");
|
|
59
|
+
await sdkDomain.assignDomain(config.api_key, orgId, domain, targetOrgId);
|
|
59
60
|
success(`Assigned ${domain} to org ${targetOrgId}`);
|
|
60
61
|
}
|
|
61
62
|
export async function unassign(domainArg, flags) {
|
|
@@ -63,16 +64,31 @@ export async function unassign(domainArg, flags) {
|
|
|
63
64
|
const orgId = flags.org || config.default_org;
|
|
64
65
|
const domain = domainArg || config.default_domain;
|
|
65
66
|
if (!orgId || !domain)
|
|
66
|
-
error("Missing required arguments.\nUsage: myapi domain unassign <domain> --org <id>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
67
|
-
|
|
67
|
+
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>)");
|
|
68
|
+
await sdkDomain.unassignDomain(config.api_key, orgId, domain);
|
|
68
69
|
success(`Unassigned ${domain} from org ${orgId}`);
|
|
69
70
|
}
|
|
71
|
+
export async function status(domainArg, flags) {
|
|
72
|
+
const config = requireConfig();
|
|
73
|
+
const orgId = flags.org || config.default_org;
|
|
74
|
+
const domain = domainArg || config.default_domain;
|
|
75
|
+
if (!orgId || !domain)
|
|
76
|
+
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>)");
|
|
77
|
+
const res = await sdkDomain.getDomainStatus(config.api_key, orgId, domain);
|
|
78
|
+
const display = {
|
|
79
|
+
domain: res.domain,
|
|
80
|
+
status: res.status,
|
|
81
|
+
};
|
|
82
|
+
if (res.expires_at)
|
|
83
|
+
display.expires_at = res.expires_at;
|
|
84
|
+
printJson(display);
|
|
85
|
+
}
|
|
70
86
|
export async function settings(domainArg, flags) {
|
|
71
87
|
const config = requireConfig();
|
|
72
88
|
const orgId = flags.org || config.default_org;
|
|
73
89
|
const domain = domainArg || config.default_domain;
|
|
74
90
|
if (!orgId || !domain)
|
|
75
|
-
error("Missing required arguments.\nUsage: myapi domain settings <domain> --org <id>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
|
|
91
|
+
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>)");
|
|
76
92
|
const res = await sdkDomain.getDomainSettings(config.api_key, orgId, domain);
|
|
77
93
|
printJson(res);
|
|
78
94
|
}
|
|
@@ -81,7 +97,7 @@ export async function updateSettings(domainArg, flags) {
|
|
|
81
97
|
const orgId = flags.org || config.default_org;
|
|
82
98
|
const domain = domainArg || config.default_domain;
|
|
83
99
|
if (!orgId || !domain)
|
|
84
|
-
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>)");
|
|
100
|
+
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>)");
|
|
85
101
|
const payload = {};
|
|
86
102
|
if (flags.security)
|
|
87
103
|
payload.security_level = flags.security;
|
|
@@ -94,7 +110,7 @@ export async function updateSettings(domainArg, flags) {
|
|
|
94
110
|
}
|
|
95
111
|
export async function run(subcommand, args, flags) {
|
|
96
112
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
97
|
-
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.');
|
|
113
|
+
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.');
|
|
98
114
|
return;
|
|
99
115
|
}
|
|
100
116
|
if (flags.help) {
|
|
@@ -110,6 +126,8 @@ export async function run(subcommand, args, flags) {
|
|
|
110
126
|
info('Usage: myapi domain assign <domain> --org <id> --target <target_org_id>\n\nAssigns a domain to a different organization.');
|
|
111
127
|
else if (subcommand === 'unassign')
|
|
112
128
|
info('Usage: myapi domain unassign <domain> --org <id>\n\nUnassigns a domain from its current organization.');
|
|
129
|
+
else if (subcommand === 'status')
|
|
130
|
+
info('Usage: myapi domain status <domain> --org <id>\n\nGets the registration status of a domain.');
|
|
113
131
|
else if (subcommand === 'settings')
|
|
114
132
|
info('Usage: myapi domain settings <domain> --org <id>\n\nGets the DNS settings and configuration for a domain.');
|
|
115
133
|
else if (subcommand === 'update-settings')
|
|
@@ -128,6 +146,8 @@ export async function run(subcommand, args, flags) {
|
|
|
128
146
|
await assign(args[0], flags);
|
|
129
147
|
else if (subcommand === 'unassign')
|
|
130
148
|
await unassign(args[0], flags);
|
|
149
|
+
else if (subcommand === 'status')
|
|
150
|
+
await status(args[0], flags);
|
|
131
151
|
else if (subcommand === 'settings')
|
|
132
152
|
await settings(args[0], flags);
|
|
133
153
|
else if (subcommand === 'update-settings')
|
package/dist/commands/org.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { hq } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
|
-
import { success, error, printJson, info } from '../output.js';
|
|
4
|
-
import { sleep } from '../utils.js';
|
|
3
|
+
import { success, error, printTable, printJson, info } from '../output.js';
|
|
4
|
+
import { sleep, formatDate } from '../utils.js';
|
|
5
5
|
export async function create(flags) {
|
|
6
6
|
if (flags.help) {
|
|
7
7
|
info('Usage: myapi org create --name="My Org" [options]\n\nOptions:\n --name Organization name (required)\n --tagline Short tagline\n --description Detailed description\n --business-sector Sector (e.g. Technology)\n --logo-url URL to logo image');
|
|
@@ -29,12 +29,17 @@ export async function create(flags) {
|
|
|
29
29
|
}
|
|
30
30
|
export async function list(flags) {
|
|
31
31
|
if (flags.help) {
|
|
32
|
-
info('Usage: myapi org list\n\nLists all organizations in your account
|
|
32
|
+
info('Usage: myapi org list\n\nLists all organizations in your account.');
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
35
|
const config = requireConfig();
|
|
36
36
|
const orgs = await hq.listOrgs(config.api_key);
|
|
37
|
-
|
|
37
|
+
const rows = orgs.map((o) => ({
|
|
38
|
+
id: o.id,
|
|
39
|
+
name: o.name,
|
|
40
|
+
created_at: o.created_at ? formatDate(o.created_at) : '',
|
|
41
|
+
}));
|
|
42
|
+
printTable(rows);
|
|
38
43
|
}
|
|
39
44
|
export async function get(id, flags) {
|
|
40
45
|
if (flags.help) {
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export declare function installSkills(): Promise<void>;
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function importKey(apiKey: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
3
|
+
export declare function setup(flags?: Record<string, string | boolean>): Promise<void>;
|
package/dist/commands/setup.js
CHANGED
|
@@ -96,11 +96,61 @@ async function anonymousFlow() {
|
|
|
96
96
|
// ---------------------------------------------------------------------------
|
|
97
97
|
// Main setup command
|
|
98
98
|
// ---------------------------------------------------------------------------
|
|
99
|
-
|
|
99
|
+
// myapi auth import-key <key> — non-interactively import a raw API key.
|
|
100
|
+
export async function importKey(apiKey, flags) {
|
|
101
|
+
if (!apiKey) {
|
|
102
|
+
info('Usage: myapi auth import-key <api_key> [--install-skills] [--no-skills]');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const auth = { Authorization: `Bearer ${apiKey}` };
|
|
106
|
+
let accountId = '';
|
|
107
|
+
let email;
|
|
108
|
+
let defaultOrg = '';
|
|
109
|
+
let defaultFunnel = '';
|
|
110
|
+
try {
|
|
111
|
+
const meRes = await fetch(`${API_BASE}/hq/account/me`, { headers: auth });
|
|
112
|
+
const meJson = await meRes.json();
|
|
113
|
+
if (!meRes.ok)
|
|
114
|
+
throw new Error(meJson.error ?? `HTTP ${meRes.status}`);
|
|
115
|
+
const me = meJson.data ?? meJson;
|
|
116
|
+
accountId = me.account_id ?? '';
|
|
117
|
+
email = me.email || undefined;
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
throw new Error(`Could not verify API key: ${e.message}`);
|
|
121
|
+
}
|
|
122
|
+
// Fetch org/funnel defaults.
|
|
123
|
+
try {
|
|
124
|
+
const orgRes = await fetch(`${API_BASE}/hq/orgs`, { headers: auth });
|
|
125
|
+
if (orgRes.ok) {
|
|
126
|
+
const orgs = ((await orgRes.json())?.data ?? []);
|
|
127
|
+
if (orgs.length > 0) {
|
|
128
|
+
defaultOrg = orgs[orgs.length - 1].id;
|
|
129
|
+
const fRes = await fetch(`${API_BASE}/funnel/orgs/${defaultOrg}/funnels`, { headers: auth });
|
|
130
|
+
if (fRes.ok) {
|
|
131
|
+
const funnels = ((await fRes.json())?.data ?? []);
|
|
132
|
+
if (funnels.length > 0)
|
|
133
|
+
defaultFunnel = funnels[funnels.length - 1].id;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch { /* non-fatal */ }
|
|
139
|
+
const wantsSkills = flags['install-skills'] ? true : flags['no-skills'] ? false : true;
|
|
140
|
+
addAccount({ api_key: apiKey, account_id: accountId, pin: '', email, default_org: defaultOrg, default_funnel: defaultFunnel, skills_installed: wantsSkills });
|
|
141
|
+
success(`› ✓ Key imported${email ? ` · ${email}` : ''}`);
|
|
142
|
+
if (wantsSkills)
|
|
143
|
+
await installSkills();
|
|
144
|
+
}
|
|
145
|
+
export async function setup(flags = {}) {
|
|
146
|
+
if (flags.help) {
|
|
147
|
+
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');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
100
150
|
info('› Configuring MyAPI…');
|
|
101
151
|
const existing = loadConfig();
|
|
102
152
|
// Already configured — ask before adding a new account.
|
|
103
|
-
if (existing?.api_key) {
|
|
153
|
+
if (existing?.api_key && !flags.yes) {
|
|
104
154
|
const full = loadFullConfig();
|
|
105
155
|
const total = full?.accounts.length ?? 1;
|
|
106
156
|
const activeLabel = existing.email ?? existing.account_id;
|
|
@@ -135,8 +185,11 @@ export async function setup() {
|
|
|
135
185
|
let subdomainUrl = '';
|
|
136
186
|
let isAnonymous = false;
|
|
137
187
|
let email = '';
|
|
188
|
+
// Determine skills preference from flags before any prompts.
|
|
189
|
+
const skillsFromFlag = flags['install-skills'] ? true : flags['no-skills'] ? false : null;
|
|
138
190
|
try {
|
|
139
|
-
const
|
|
191
|
+
const useAnon = flags.anonymous || flags.anon;
|
|
192
|
+
const createAns = useAnon ? 'n' : await ask(rl, '› Create an account? (Y/n) ');
|
|
140
193
|
if (yn(createAns)) {
|
|
141
194
|
const data = await registeredFlow(rl);
|
|
142
195
|
apiKey = data.api_key;
|
|
@@ -155,8 +208,8 @@ export async function setup() {
|
|
|
155
208
|
subdomainUrl = data.subdomain_url;
|
|
156
209
|
isAnonymous = true;
|
|
157
210
|
}
|
|
158
|
-
const skillsAns = await ask(rl, '› Install the MyAPI skills pack? (Y/n) ');
|
|
159
|
-
const wantsSkills = yn(skillsAns);
|
|
211
|
+
const skillsAns = skillsFromFlag !== null ? (skillsFromFlag ? 'y' : 'n') : await ask(rl, '› Install the MyAPI skills pack? (Y/n) ');
|
|
212
|
+
const wantsSkills = skillsFromFlag !== null ? skillsFromFlag : yn(skillsAns);
|
|
160
213
|
addAccount({
|
|
161
214
|
api_key: apiKey,
|
|
162
215
|
account_id: accountId,
|
package/dist/commands/update.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
|
+
import { loadConfig } from '../config.js';
|
|
2
3
|
import { info, success } from '../output.js';
|
|
3
4
|
import { installSkills } from './setup.js';
|
|
4
5
|
const REGISTRY_URL = 'https://registry.npmjs.org/@myapihq/cli/latest';
|
|
@@ -14,7 +15,10 @@ export async function checkForUpdate(currentVersion) {
|
|
|
14
15
|
if (latest && isNewer(latest, currentVersion)) {
|
|
15
16
|
info(`\n› New version available (${currentVersion} → ${latest}) — installing…`);
|
|
16
17
|
execSync('npm install -g @myapihq/cli', { stdio: 'pipe' });
|
|
17
|
-
|
|
18
|
+
const config = loadConfig();
|
|
19
|
+
if (config?.skills_installed) {
|
|
20
|
+
await installSkills();
|
|
21
|
+
}
|
|
18
22
|
success(`› MyAPI CLI updated to ${latest}. Re-run your command.\n`);
|
|
19
23
|
process.exit(0);
|
|
20
24
|
}
|
package/dist/config.js
CHANGED
|
@@ -31,7 +31,7 @@ export function loadFullConfig() {
|
|
|
31
31
|
}
|
|
32
32
|
// loadConfig returns the active account merged with globals — unchanged interface for all callers.
|
|
33
33
|
export function loadConfig() {
|
|
34
|
-
const envKey = process.env.MYAPI_KEY;
|
|
34
|
+
const envKey = process.env.MYAPI_API_KEY || process.env.MYAPI_KEY;
|
|
35
35
|
const full = loadFullConfig();
|
|
36
36
|
if (!full && envKey)
|
|
37
37
|
return { api_key: envKey, account_id: '', pin: '' };
|
package/dist/utils.d.ts
CHANGED
|
@@ -3,3 +3,8 @@ export declare function parseArgs(argv: string[]): {
|
|
|
3
3
|
flags: Record<string, string | boolean>;
|
|
4
4
|
};
|
|
5
5
|
export declare function sleep(ms: number): Promise<unknown>;
|
|
6
|
+
/**
|
|
7
|
+
* Formats an ISO/Go timestamp string to "YYYY-MM-DD HH:mm" (UTC).
|
|
8
|
+
* Strips Go's " +0000 UTC" suffix before parsing.
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatDate(str: string): string;
|
package/dist/utils.js
CHANGED
|
@@ -28,3 +28,14 @@ export function parseArgs(argv) {
|
|
|
28
28
|
export function sleep(ms) {
|
|
29
29
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Formats an ISO/Go timestamp string to "YYYY-MM-DD HH:mm" (UTC).
|
|
33
|
+
* Strips Go's " +0000 UTC" suffix before parsing.
|
|
34
|
+
*/
|
|
35
|
+
export function formatDate(str) {
|
|
36
|
+
const clean = str.replace(' +0000 UTC', 'Z');
|
|
37
|
+
const date = new Date(clean);
|
|
38
|
+
if (isNaN(date.getTime()))
|
|
39
|
+
return str;
|
|
40
|
+
return date.toISOString().replace('T', ' ').slice(0, 16);
|
|
41
|
+
}
|
package/package.json
CHANGED
package/scripts/copy-skills.js
CHANGED
|
@@ -1,36 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
import { readdirSync, mkdirSync, copyFileSync, existsSync, readFileSync, rmSync } from 'fs';
|
|
2
|
+
// Skills are not bundled in the CLI package for now.
|
|
3
|
+
import { existsSync, rmSync, mkdirSync } from 'fs';
|
|
5
4
|
import { join, dirname } from 'path';
|
|
6
5
|
import { fileURLToPath } from 'url';
|
|
7
6
|
|
|
8
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const repoRoot = join(__dirname, '..', '..', '..');
|
|
10
|
-
const skillsRoot = join(repoRoot, 'skills');
|
|
11
8
|
const dest = join(__dirname, '..', 'src', 'skills');
|
|
12
9
|
|
|
13
|
-
if (!existsSync(skillsRoot)) {
|
|
14
|
-
console.log('copy-skills: no skills/ directory found, skipping.');
|
|
15
|
-
process.exit(0);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Always start clean so unpublished skills don't linger.
|
|
19
10
|
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
20
11
|
mkdirSync(dest, { recursive: true });
|
|
21
12
|
|
|
22
|
-
|
|
23
|
-
for (const skillDir of readdirSync(skillsRoot)) {
|
|
24
|
-
const src = join(skillsRoot, skillDir, 'SKILL.md');
|
|
25
|
-
const pluginJson = join(skillsRoot, skillDir, 'claude', '.claude-plugin', 'plugin.json');
|
|
26
|
-
if (!existsSync(src)) continue;
|
|
27
|
-
// Only bundle skills marked published: true
|
|
28
|
-
if (existsSync(pluginJson)) {
|
|
29
|
-
const plugin = JSON.parse(readFileSync(pluginJson, 'utf-8'));
|
|
30
|
-
if (!plugin.published) continue;
|
|
31
|
-
}
|
|
32
|
-
copyFileSync(src, join(dest, `${skillDir}.md`));
|
|
33
|
-
copied++;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log(`copy-skills: copied ${copied} skill(s) to src/skills/`);
|
|
13
|
+
console.log('copy-skills: no skills bundled.');
|
package/src/commands/auth.ts
CHANGED
|
@@ -2,6 +2,8 @@ import * as readline from 'readline';
|
|
|
2
2
|
|
|
3
3
|
import { loadConfig, saveConfig, addAccount, switchAccount, listAccounts } from '../config.js';
|
|
4
4
|
import { info, success, error } from '../output.js';
|
|
5
|
+
import { installSkills } from './setup.js';
|
|
6
|
+
import { hq } from '@myapihq/sdk';
|
|
5
7
|
|
|
6
8
|
const API_BASE = process.env.MYAPI_API_BASE ?? 'https://api.myapihq.com';
|
|
7
9
|
|
|
@@ -72,6 +74,9 @@ export async function signup() {
|
|
|
72
74
|
default_funnel: string;
|
|
73
75
|
};
|
|
74
76
|
|
|
77
|
+
const skillsAns = (await ask(rl, '› Install the MyAPI skills pack? (Y/n) ')).trim();
|
|
78
|
+
const wantsSkills = yn(skillsAns);
|
|
79
|
+
|
|
75
80
|
if (upgradeOk) {
|
|
76
81
|
// Upgrade: update current account in place.
|
|
77
82
|
saveConfig({
|
|
@@ -82,6 +87,7 @@ export async function signup() {
|
|
|
82
87
|
default_org: data.default_org || config!.default_org,
|
|
83
88
|
default_funnel: data.default_funnel || config!.default_funnel,
|
|
84
89
|
is_anonymous: false,
|
|
90
|
+
skills_installed: wantsSkills,
|
|
85
91
|
});
|
|
86
92
|
success(`› Welcome! Account upgraded · ${email}`);
|
|
87
93
|
} else {
|
|
@@ -94,10 +100,13 @@ export async function signup() {
|
|
|
94
100
|
default_org: data.default_org,
|
|
95
101
|
default_funnel: data.default_funnel,
|
|
96
102
|
is_anonymous: false,
|
|
103
|
+
skills_installed: wantsSkills,
|
|
97
104
|
});
|
|
98
105
|
success(`› Signed in · ${email} (account #${idx + 1})`);
|
|
99
106
|
info(`› Use "myapi auth switch" to toggle between accounts.`);
|
|
100
107
|
}
|
|
108
|
+
|
|
109
|
+
if (wantsSkills) await installSkills();
|
|
101
110
|
} finally {
|
|
102
111
|
rl.close();
|
|
103
112
|
}
|
|
@@ -113,6 +122,13 @@ export async function whoami() {
|
|
|
113
122
|
info(`Org: ${config!.default_org ?? '(none)'}`);
|
|
114
123
|
info(`Funnel: ${config!.default_funnel ?? '(none)'}`);
|
|
115
124
|
info(`Type: ${config!.is_anonymous ? 'anonymous' : 'registered'}`);
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const bal = await hq.getBalance(config!.api_key);
|
|
128
|
+
info(`Balance: ${bal.balance_display} | Credits: ${bal.credits_display}`);
|
|
129
|
+
} catch {
|
|
130
|
+
// Balance fetch is best-effort; don't fail whoami if it errors.
|
|
131
|
+
}
|
|
116
132
|
}
|
|
117
133
|
|
|
118
134
|
// myapi auth switch — switch between saved accounts.
|