@myapihq/cli 1.0.73 → 1.0.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/domain.js +13 -3
- package/dist/commands/funnel.d.ts +1 -1
- package/dist/commands/funnel.js +27 -11
- package/dist/commands/org.js +15 -5
- package/dist/commands/update.js +43 -23
- package/dist/index.js +16 -4
- package/dist/output.d.ts +1 -0
- package/dist/output.js +3 -0
- package/dist/utils.js +1 -1
- package/package.json +1 -1
package/dist/commands/domain.js
CHANGED
|
@@ -6,11 +6,21 @@ export async function check(domainArg, flags) {
|
|
|
6
6
|
const config = requireConfig();
|
|
7
7
|
const orgId = flags.org || config.default_org;
|
|
8
8
|
const domain = domainArg || config.default_domain;
|
|
9
|
-
if (!orgId || !domain)
|
|
10
|
-
|
|
9
|
+
if (!orgId || !domain) {
|
|
10
|
+
const hint = config.default_org
|
|
11
|
+
? `(current default org: ${config.default_org})`
|
|
12
|
+
: '(Set defaults: myapi config set-org <id> / myapi config set-domain <domain>)';
|
|
13
|
+
error(`Missing required arguments.\nUsage: myapi domain check <domain>${config.default_org ? '' : ' --org <id>'}\n${hint}`);
|
|
14
|
+
}
|
|
11
15
|
const res = await sdkDomain.checkDomain(config.api_key, orgId, domain);
|
|
12
16
|
if (flags.json) {
|
|
13
|
-
|
|
17
|
+
const out = { domain, available: res.available };
|
|
18
|
+
if (res.available) {
|
|
19
|
+
out.price_cents = res.price_cents;
|
|
20
|
+
if (res.price_display)
|
|
21
|
+
out.price_display = res.price_display;
|
|
22
|
+
}
|
|
23
|
+
printJson(out);
|
|
14
24
|
return;
|
|
15
25
|
}
|
|
16
26
|
if (res.available) {
|
|
@@ -2,6 +2,6 @@ export declare function list(flags: Record<string, string | boolean>): Promise<v
|
|
|
2
2
|
export declare function create(flags: Record<string, string | boolean>): Promise<void>;
|
|
3
3
|
export declare function get(id: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
4
4
|
export declare function del(id: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
5
|
-
export declare function push(
|
|
5
|
+
export declare function push(slug: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
6
6
|
export declare function verify(id: string, flags: Record<string, string | boolean>): Promise<void>;
|
|
7
7
|
export declare function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>): Promise<void>;
|
package/dist/commands/funnel.js
CHANGED
|
@@ -12,7 +12,12 @@ export async function list(flags) {
|
|
|
12
12
|
printJson(funnels);
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
|
-
const rows = funnels.map(f => ({
|
|
15
|
+
const rows = funnels.map(f => ({
|
|
16
|
+
id: f.id,
|
|
17
|
+
preview_url: f.subdomain_url || f.domain_url || '',
|
|
18
|
+
created_at: formatDate(f.created_at),
|
|
19
|
+
updated_at: formatDate(f.updated_at),
|
|
20
|
+
}));
|
|
16
21
|
if (rows.length === 0) {
|
|
17
22
|
info('No funnels found. Create one with: myapi funnel create');
|
|
18
23
|
return;
|
|
@@ -38,7 +43,21 @@ export async function get(id, flags) {
|
|
|
38
43
|
if (!orgId || !id)
|
|
39
44
|
error("Missing required arguments.\nUsage: myapi funnel get <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
40
45
|
const funnel = await sdkFunnel.getFunnel(config.api_key, orgId, id);
|
|
41
|
-
|
|
46
|
+
if (flags.json) {
|
|
47
|
+
printJson(funnel);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
info(`ID: ${funnel.id}`);
|
|
51
|
+
if (funnel.domain_url)
|
|
52
|
+
info(`Domain: ${funnel.domain_url}`);
|
|
53
|
+
else if (funnel.subdomain_url)
|
|
54
|
+
info(`Preview: ${funnel.subdomain_url}`);
|
|
55
|
+
if (funnel.created_at)
|
|
56
|
+
info(`Created: ${formatDate(funnel.created_at)}`);
|
|
57
|
+
if (funnel.updated_at)
|
|
58
|
+
info(`Updated: ${formatDate(funnel.updated_at)}`);
|
|
59
|
+
if (Array.isArray(funnel.pages))
|
|
60
|
+
info(`Pages: ${funnel.pages.length}`);
|
|
42
61
|
}
|
|
43
62
|
export async function del(id, flags) {
|
|
44
63
|
const config = requireConfig();
|
|
@@ -48,17 +67,14 @@ export async function del(id, flags) {
|
|
|
48
67
|
await sdkFunnel.deleteFunnel(config.api_key, orgId, id);
|
|
49
68
|
success(`Funnel ${id} deleted`);
|
|
50
69
|
}
|
|
51
|
-
export async function push(
|
|
70
|
+
export async function push(slug, flags) {
|
|
52
71
|
const config = requireConfig();
|
|
53
72
|
const orgId = flags.org || config.default_org;
|
|
54
73
|
if (!orgId)
|
|
55
|
-
error("Missing org. Set a default with: myapi
|
|
56
|
-
if (slug && flags.slug && slug !== flags.slug) {
|
|
57
|
-
info(`› Note: positional slug "${slug}" takes precedence over --slug "${flags.slug}".`);
|
|
58
|
-
}
|
|
74
|
+
error("Missing org. Set a default with: myapi config set-org <id>");
|
|
59
75
|
const rawSlug = slug || flags.slug || '/';
|
|
60
76
|
const finalSlug = rawSlug.startsWith('/') ? rawSlug : `/${rawSlug}`;
|
|
61
|
-
let funnelId =
|
|
77
|
+
let funnelId = flags.funnel || config.default_funnel;
|
|
62
78
|
if (!funnelId) {
|
|
63
79
|
const existing = await sdkFunnel.listFunnels(config.api_key, orgId);
|
|
64
80
|
if (existing.length === 0)
|
|
@@ -75,7 +91,7 @@ export async function push(id, slug, flags) {
|
|
|
75
91
|
process.stdin.on('error', reject);
|
|
76
92
|
});
|
|
77
93
|
if (!html.trim())
|
|
78
|
-
error("No content provided via stdin. Usage: echo '<h1>Hello</h1>' | myapi funnel push [
|
|
94
|
+
error("No content provided via stdin. Usage: echo '<h1>Hello</h1>' | myapi funnel push [slug]");
|
|
79
95
|
const result = await sdkFunnel.pushFunnelPage(config.api_key, orgId, funnelId, { slug: finalSlug, html });
|
|
80
96
|
success(`Pushed page to ${finalSlug}`);
|
|
81
97
|
if (result?.url) {
|
|
@@ -114,7 +130,7 @@ export async function run(subcommand, args, flags) {
|
|
|
114
130
|
else if (subcommand === 'delete')
|
|
115
131
|
info('Usage: myapi funnel delete <id> --org <id>\n\nDeletes a funnel permanently.\n\nFlags:\n --org <id> Organization ID (or set default via myapi auth config set-org)');
|
|
116
132
|
else if (subcommand === 'push')
|
|
117
|
-
info('Usage: myapi funnel push [
|
|
133
|
+
info('Usage: myapi funnel push [slug] < page.html\n\nPublishes an HTML page from stdin to a path within your funnel (website).\nThe funnel is always resolved from your default or --funnel flag.\n\nFlags:\n --org <id> Organization ID (or set default via myapi config set-org)\n --funnel <id> Funnel ID (or set default via myapi config set-funnel)\n --slug <path> Alternative way to specify the slug\n\nExamples:\n echo \'<h1>Hello</h1>\' | myapi funnel push / # push to /\n cat about.html | myapi funnel push /about # push to /about\n myapi funnel push < index.html # push to / on default funnel\n cat p.html | myapi funnel push /pricing --funnel <uuid>');
|
|
118
134
|
else if (subcommand === 'pull')
|
|
119
135
|
info('Usage: myapi funnel pull [funnel_id] [slug]\n\nFetches the HTML content of a funnel page.');
|
|
120
136
|
return;
|
|
@@ -128,7 +144,7 @@ export async function run(subcommand, args, flags) {
|
|
|
128
144
|
else if (subcommand === 'delete')
|
|
129
145
|
await del(args[0], flags);
|
|
130
146
|
else if (subcommand === 'push')
|
|
131
|
-
await push(args[0],
|
|
147
|
+
await push(args[0], flags);
|
|
132
148
|
else if (subcommand === 'verify')
|
|
133
149
|
await verify(args[0], flags);
|
|
134
150
|
else
|
package/dist/commands/org.js
CHANGED
|
@@ -51,7 +51,16 @@ export async function list(flags) {
|
|
|
51
51
|
const config = requireConfig();
|
|
52
52
|
const orgs = await hq.listOrgs(config.api_key);
|
|
53
53
|
if (flags.json) {
|
|
54
|
-
|
|
54
|
+
if (flags.verbose) {
|
|
55
|
+
printJson(orgs);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
printJson(orgs.map((o) => ({
|
|
59
|
+
id: o.id,
|
|
60
|
+
name: o.name,
|
|
61
|
+
preview_subdomain: o.preview_subdomain || undefined,
|
|
62
|
+
created_at: o.created_at || undefined,
|
|
63
|
+
})));
|
|
55
64
|
return;
|
|
56
65
|
}
|
|
57
66
|
const rows = orgs.map((o) => ({
|
|
@@ -66,12 +75,13 @@ export async function get(id, flags) {
|
|
|
66
75
|
info('Usage: myapi org get <id> [--json]\n\nFetches details of a specific organization.\n\nFlags:\n --json Output raw JSON');
|
|
67
76
|
return;
|
|
68
77
|
}
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
const config = requireConfig();
|
|
79
|
+
const resolvedId = id || config.default_org;
|
|
80
|
+
if (!resolvedId) {
|
|
81
|
+
error("Missing org id. Usage: myapi org get <id>\n(Or set a default: myapi config set-org <id>)");
|
|
71
82
|
return;
|
|
72
83
|
}
|
|
73
|
-
const
|
|
74
|
-
const org = await hq.getOrg(config.api_key, id);
|
|
84
|
+
const org = await hq.getOrg(config.api_key, resolvedId);
|
|
75
85
|
if (flags.json) {
|
|
76
86
|
printJson(org);
|
|
77
87
|
return;
|
package/dist/commands/update.js
CHANGED
|
@@ -1,29 +1,55 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
3
|
import { loadConfig } from '../config.js';
|
|
4
|
-
import { info, success } from '../output.js';
|
|
4
|
+
import { info, success, banner } from '../output.js';
|
|
5
5
|
import { installSkills } from './setup.js';
|
|
6
6
|
const REGISTRY_URL = 'https://registry.npmjs.org/@myapihq/cli/latest';
|
|
7
|
+
const PACKUMENT_URL = 'https://registry.npmjs.org/@myapihq/cli';
|
|
8
|
+
// Verify the version is listed in the full packument — the same data source
|
|
9
|
+
// `npm install` consults. The /latest endpoint and the tarball URL are on
|
|
10
|
+
// different CDN caches and can update before the packument does, causing
|
|
11
|
+
// `npm install` to fail with ETARGET. This check eliminates that race.
|
|
12
|
+
async function isVersionInstallable(version) {
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(PACKUMENT_URL, {
|
|
15
|
+
headers: { Accept: 'application/vnd.npm.install-v1+json' },
|
|
16
|
+
signal: AbortSignal.timeout(5000),
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok)
|
|
19
|
+
return false;
|
|
20
|
+
const data = await res.json();
|
|
21
|
+
return Boolean(data.versions && data.versions[version]);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
7
27
|
// Resolve the npm binary and global prefix to use for self-update.
|
|
8
28
|
// We install to the same prefix where the current myapi binary lives,
|
|
9
29
|
// so the update actually replaces the running binary.
|
|
30
|
+
// `--prefer-online` forces npm to revalidate its local packument cache
|
|
31
|
+
// against the registry — without it, a stale local cache can also produce
|
|
32
|
+
// ETARGET even when the registry itself has the version.
|
|
10
33
|
function npmInstallArgs(version) {
|
|
11
34
|
const npmCandidate = process.execPath.replace(/[/\\]node(\.exe)?$/, '/npm');
|
|
12
35
|
const bin = existsSync(npmCandidate) ? npmCandidate : 'npm';
|
|
13
|
-
|
|
36
|
+
const baseArgs = ['install', '-g', '--prefer-online', `@myapihq/cli@${version}`];
|
|
14
37
|
try {
|
|
15
|
-
const self = process.argv[1];
|
|
16
|
-
const binDir = self.replace(/[/\\][^/\\]+$/, '');
|
|
17
|
-
const prefix = binDir.replace(/[/\\]bin$/, '');
|
|
18
|
-
return { bin, args: ['install', '-g', '--prefix', prefix, `@myapihq/cli@${version}`] };
|
|
38
|
+
const self = process.argv[1];
|
|
39
|
+
const binDir = self.replace(/[/\\][^/\\]+$/, '');
|
|
40
|
+
const prefix = binDir.replace(/[/\\]bin$/, '');
|
|
41
|
+
return { bin, args: ['install', '-g', '--prefer-online', '--prefix', prefix, `@myapihq/cli@${version}`] };
|
|
19
42
|
}
|
|
20
43
|
catch {
|
|
21
|
-
return { bin, args:
|
|
44
|
+
return { bin, args: baseArgs };
|
|
22
45
|
}
|
|
23
46
|
}
|
|
24
47
|
// checkForUpdate runs silently in the background on every command.
|
|
25
48
|
// Auto-installs if a newer version is available.
|
|
49
|
+
// Suppress entirely with MYAPI_NO_UPDATE=1 or when stdout is not a TTY.
|
|
26
50
|
export async function checkForUpdate(currentVersion) {
|
|
51
|
+
if (process.env.MYAPI_NO_UPDATE === '1' || !process.stdout.isTTY)
|
|
52
|
+
return;
|
|
27
53
|
try {
|
|
28
54
|
const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(3000) });
|
|
29
55
|
if (!res.ok)
|
|
@@ -31,18 +57,12 @@ export async function checkForUpdate(currentVersion) {
|
|
|
31
57
|
const data = await res.json();
|
|
32
58
|
const latest = data.version;
|
|
33
59
|
if (latest && isNewer(latest, currentVersion)) {
|
|
34
|
-
// Verify the
|
|
35
|
-
// the
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const check = await fetch(tarballUrl, { method: 'HEAD', signal: AbortSignal.timeout(3000) });
|
|
39
|
-
if (!check.ok)
|
|
40
|
-
return; // Tarball not ready yet — silently skip, retry next command.
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
60
|
+
// Verify the version is actually installable before attempting install —
|
|
61
|
+
// the /latest endpoint can report a new version before the packument
|
|
62
|
+
// (which `npm install` consults) has propagated through the CDN.
|
|
63
|
+
if (!(await isVersionInstallable(latest)))
|
|
43
64
|
return;
|
|
44
|
-
}
|
|
45
|
-
info(`\n› New version available (${currentVersion} → ${latest}) — installing…`);
|
|
65
|
+
banner(`\n› New version available (${currentVersion} → ${latest}) — installing…`);
|
|
46
66
|
try {
|
|
47
67
|
const { bin, args } = npmInstallArgs(latest);
|
|
48
68
|
execSync(`"${bin}" ${args.map(a => `"${a}"`).join(' ')}`, { stdio: 'pipe' });
|
|
@@ -50,12 +70,12 @@ export async function checkForUpdate(currentVersion) {
|
|
|
50
70
|
if (config?.skills_installed) {
|
|
51
71
|
await installSkills();
|
|
52
72
|
}
|
|
53
|
-
|
|
73
|
+
banner(`› Updated to ${latest} — active after this command completes.\n`);
|
|
54
74
|
}
|
|
55
75
|
catch (installErr) {
|
|
56
76
|
const msg = installErr?.stderr?.toString?.() || installErr?.message || String(installErr);
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
banner(`› Auto-update failed: ${msg.trim()}`);
|
|
78
|
+
banner(`› Run manually: npm install -g @myapihq/cli@latest`);
|
|
59
79
|
}
|
|
60
80
|
}
|
|
61
81
|
}
|
|
@@ -66,7 +86,7 @@ export async function checkForUpdate(currentVersion) {
|
|
|
66
86
|
// myapi update — explicit update, same logic as auto-update.
|
|
67
87
|
export async function update(flags = {}) {
|
|
68
88
|
if (flags.help) {
|
|
69
|
-
info('Usage: myapi update\n\nUpdates the MyAPI CLI and installed skills to the latest published version.\nEquivalent to: npm install -g @myapihq/cli@latest\n\nNotes:\n - Version pinning and rollback are not supported — always installs the latest.\n - The CLI also auto-updates silently on each command when a newer version is\n detected
|
|
89
|
+
info('Usage: myapi update\n\nUpdates the MyAPI CLI and installed skills to the latest published version.\nEquivalent to: npm install -g @myapihq/cli@latest\n\nNotes:\n - Version pinning and rollback are not supported — always installs the latest.\n - The CLI also auto-updates silently on each command when a newer version is\n detected (banner goes to stderr, suppressed on non-TTY stdout).\n - To disable auto-update: set MYAPI_NO_UPDATE=1 in your environment.\n - For a pinned version in CI: npm install -g @myapihq/cli@<version>');
|
|
70
90
|
return;
|
|
71
91
|
}
|
|
72
92
|
info('› Checking for updates…');
|
|
@@ -91,7 +111,7 @@ export async function update(flags = {}) {
|
|
|
91
111
|
}
|
|
92
112
|
info('› Refreshing skills…');
|
|
93
113
|
await installSkills();
|
|
94
|
-
success('
|
|
114
|
+
success('Up to date.');
|
|
95
115
|
}
|
|
96
116
|
// latestVersion fetches the current published version for --version display.
|
|
97
117
|
export async function latestVersion() {
|
package/dist/index.js
CHANGED
|
@@ -23,25 +23,32 @@ const ERROR_MESSAGES = {
|
|
|
23
23
|
org_not_found: 'Organization not found.',
|
|
24
24
|
FUNNEL_NOT_FOUND: 'Funnel not found.',
|
|
25
25
|
funnel_not_found: 'Funnel not found.',
|
|
26
|
+
funnel_already_exists: 'Your org already has a funnel. Use: myapi funnel list',
|
|
27
|
+
FUNNEL_ALREADY_EXISTS: 'Your org already has a funnel. Use: myapi funnel list',
|
|
28
|
+
domain_already_registered: 'This domain is already registered under your account.',
|
|
29
|
+
DOMAIN_ALREADY_REGISTERED: 'This domain is already registered under your account.',
|
|
30
|
+
domain_not_available: 'This domain is not available for registration.',
|
|
31
|
+
DOMAIN_NOT_AVAILABLE: 'This domain is not available for registration.',
|
|
26
32
|
db_error: 'Resource not found or invalid ID.',
|
|
27
33
|
NOT_FOUND: 'Resource not found.',
|
|
28
34
|
FORBIDDEN: 'You do not have permission to perform this action.',
|
|
29
35
|
RATE_LIMITED: 'Too many requests. Please wait a moment and try again.',
|
|
36
|
+
INSUFFICIENT_BALANCE: 'Insufficient balance. Run: myapi billing topup <amount>',
|
|
30
37
|
};
|
|
31
38
|
function friendlyError(code) {
|
|
32
39
|
return ERROR_MESSAGES[code] || code;
|
|
33
40
|
}
|
|
34
41
|
async function main() {
|
|
35
|
-
const updatePromise = updateCmd.checkForUpdate(pkg.version).catch(() => { });
|
|
36
42
|
const { args, flags } = parseArgs(process.argv.slice(2));
|
|
37
43
|
if (flags.version || flags.v || flags.V) {
|
|
38
|
-
const
|
|
44
|
+
const latest = await updateCmd.latestVersion();
|
|
39
45
|
const updateNote = latest && updateCmd.isNewer(latest, pkg.version)
|
|
40
46
|
? ` (update available: ${latest})`
|
|
41
47
|
: '';
|
|
42
48
|
info(`myapi ${pkg.version}${updateNote}`);
|
|
43
49
|
return;
|
|
44
50
|
}
|
|
51
|
+
const updatePromise = updateCmd.checkForUpdate(pkg.version).catch(() => { });
|
|
45
52
|
if (flags.help && args.length === 0) {
|
|
46
53
|
printHelp();
|
|
47
54
|
return;
|
|
@@ -54,7 +61,7 @@ async function main() {
|
|
|
54
61
|
}
|
|
55
62
|
printHelp();
|
|
56
63
|
await updatePromise;
|
|
57
|
-
|
|
64
|
+
process.exit(1);
|
|
58
65
|
}
|
|
59
66
|
const [command, subcommand, ...restArgs] = args;
|
|
60
67
|
try {
|
|
@@ -177,7 +184,12 @@ async function main() {
|
|
|
177
184
|
success('› Skills installed.');
|
|
178
185
|
break;
|
|
179
186
|
case 'help':
|
|
180
|
-
|
|
187
|
+
if (subcommand) {
|
|
188
|
+
info(`Run: myapi ${subcommand} --help`);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
printHelp();
|
|
192
|
+
}
|
|
181
193
|
break;
|
|
182
194
|
default:
|
|
183
195
|
error(`Unknown command: ${command}. Run "myapi" for available commands.`);
|
package/dist/output.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare function success(message: string): void;
|
|
2
2
|
export declare function error(message: string): never;
|
|
3
3
|
export declare function info(message: string): void;
|
|
4
|
+
export declare function banner(message: string): void;
|
|
4
5
|
export declare function printJson(data: unknown): void;
|
|
5
6
|
export declare function printTable(rows: Record<string, unknown>[]): void;
|
package/dist/output.js
CHANGED
|
@@ -9,6 +9,9 @@ export function error(message) {
|
|
|
9
9
|
export function info(message) {
|
|
10
10
|
console.log(message);
|
|
11
11
|
}
|
|
12
|
+
export function banner(message) {
|
|
13
|
+
process.stderr.write(message + '\n');
|
|
14
|
+
}
|
|
12
15
|
export function printJson(data) {
|
|
13
16
|
console.log(JSON.stringify(data, null, 2));
|
|
14
17
|
}
|
package/dist/utils.js
CHANGED