@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.
@@ -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() {
@@ -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
- info(`Balance: ${bal} | Credits: ${cred} | Payment method: ${pm}`);
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
- const formattedItems = items.map(item => {
28
- // Attempt to parse the date cleanly (handling Go's "+0000 UTC" suffix)
29
- const cleanDate = item.created_at.replace(' +0000 UTC', 'Z');
30
- const date = new Date(cleanDate);
31
- const dateStr = isNaN(date.getTime()) ? item.created_at : date.toLocaleString();
32
- // Format amount
33
- const isNegative = item.amount_cents < 0;
34
- const absAmount = Math.abs(item.amount_cents) / 100;
35
- const amountStr = `${isNegative ? '-' : ''}$${absAmount.toFixed(2)}`;
36
- return {
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 <amount_in_dollars>\n\nAdds funds to your account balance using your saved payment method.\nExample: myapi billing topup 10.00');
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
- if (!amountStr) {
51
- error("Missing amount. Usage: myapi billing topup <amount_in_dollars>");
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
- const amount = parseFloat(amountStr);
55
- if (isNaN(amount)) {
56
- error("Invalid amount. Must be a number in dollars (e.g., 10.00)");
57
- return;
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, Math.round(amount * 100));
61
- success(`Top up successful! New balance: $${(result.new_balance_cents / 100).toFixed(2)}`);
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>;
@@ -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
- info(`${domain} Available ✓ Price: $${(res.price_cents / 100).toFixed(2)}/yr`);
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(`Not available ✗`);
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
- const res = await sdkDomain.assignDomain(config.api_key, orgId, domain, targetOrgId);
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
- const res = await sdkDomain.unassignDomain(config.api_key, orgId, domain);
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')
@@ -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 as JSON.');
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
- printJson(orgs);
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) {
@@ -1,2 +1,3 @@
1
1
  export declare function installSkills(): Promise<void>;
2
- export declare function setup(): Promise<void>;
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>;
@@ -96,11 +96,61 @@ async function anonymousFlow() {
96
96
  // ---------------------------------------------------------------------------
97
97
  // Main setup command
98
98
  // ---------------------------------------------------------------------------
99
- export async function setup() {
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 createAns = await ask(rl, '› Create an account? (Y/n) ');
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,
@@ -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
- await installSkills();
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myapihq/cli",
3
- "version": "1.0.29",
3
+ "version": "1.0.39",
4
4
  "description": "MyAPI command-line interface",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,36 +1,13 @@
1
1
  #!/usr/bin/env node
2
- // Copies skills/*/SKILL.md from the repo root into src/skills/ so they get
3
- // bundled in the npm package. Runs as prebuild.
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
- let copied = 0;
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.');
@@ -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.