@myapihq/cli 1.0.63 → 1.0.65

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.
Files changed (49) hide show
  1. package/dist/commands/auth.js +12 -4
  2. package/dist/commands/config.d.ts +4 -4
  3. package/dist/commands/config.js +12 -12
  4. package/dist/commands/funnel.js +1 -1
  5. package/dist/commands/keys.js +2 -2
  6. package/dist/commands/org.js +1 -1
  7. package/dist/commands/update.js +1 -1
  8. package/dist/skills/my-api-hq/README.md +37 -0
  9. package/dist/skills/my-api-hq/SKILL.md +116 -0
  10. package/dist/skills/my-api-hq/claude/.claude-plugin/plugin.json +6 -0
  11. package/dist/skills/my-api-hq/make/.gitkeep +0 -0
  12. package/dist/skills/my-api-hq/n8n/.gitkeep +0 -0
  13. package/dist/skills/my-api-hq/openapi/.gitkeep +0 -0
  14. package/dist/skills/my-domain-api/README.md +37 -0
  15. package/dist/skills/my-domain-api/SKILL.md +83 -0
  16. package/dist/skills/my-domain-api/claude/.claude-plugin/plugin.json +6 -0
  17. package/dist/skills/my-domain-api/make/.gitkeep +0 -0
  18. package/dist/skills/my-domain-api/n8n/.gitkeep +0 -0
  19. package/dist/skills/my-domain-api/openapi/.gitkeep +0 -0
  20. package/dist/skills/my-funnel-api/README.md +39 -0
  21. package/dist/skills/my-funnel-api/SKILL.md +35 -0
  22. package/dist/skills/my-funnel-api/claude/.claude-plugin/plugin.json +6 -0
  23. package/dist/skills/my-funnel-api/make/.gitkeep +0 -0
  24. package/dist/skills/my-funnel-api/n8n/.gitkeep +0 -0
  25. package/dist/skills/my-funnel-api/openapi/.gitkeep +0 -0
  26. package/package.json +4 -1
  27. package/scripts/copy-skills.js +0 -13
  28. package/src/commands/auth.ts +0 -190
  29. package/src/commands/billing.ts +0 -92
  30. package/src/commands/config.ts +0 -71
  31. package/src/commands/domain.ts +0 -134
  32. package/src/commands/email.ts +0 -185
  33. package/src/commands/funnel.ts +0 -123
  34. package/src/commands/image.ts +0 -85
  35. package/src/commands/keys.ts +0 -68
  36. package/src/commands/org.ts +0 -135
  37. package/src/commands/pixel.ts +0 -62
  38. package/src/commands/setup.ts +0 -335
  39. package/src/commands/storage.ts +0 -56
  40. package/src/commands/update.ts +0 -89
  41. package/src/commands/url.ts +0 -28
  42. package/src/commands/webhook.ts +0 -66
  43. package/src/commands/workflow.ts +0 -102
  44. package/src/config.ts +0 -123
  45. package/src/index.ts +0 -203
  46. package/src/output.ts +0 -49
  47. package/src/utils.ts +0 -45
  48. package/thank-you.html +0 -56
  49. package/tsconfig.json +0 -15
package/src/config.ts DELETED
@@ -1,123 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import * as os from 'os';
4
-
5
- export interface AccountEntry {
6
- api_key: string;
7
- account_id: string;
8
- email?: string;
9
- default_org?: string;
10
- default_funnel?: string;
11
- default_domain?: string;
12
- is_anonymous?: boolean;
13
- skills_installed?: boolean;
14
- }
15
-
16
- // Flat interface used by all command files — represents the active account.
17
- export interface Config extends AccountEntry {
18
- autocomplete_setup?: boolean;
19
- }
20
-
21
- export interface FullConfig {
22
- active: number;
23
- accounts: AccountEntry[];
24
- autocomplete_setup?: boolean;
25
- }
26
-
27
- export const CONFIG_DIR = path.join(os.homedir(), '.myapi');
28
- export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
29
-
30
- function ensureDir() {
31
- if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
32
- }
33
-
34
- function readRaw(): any {
35
- try {
36
- if (fs.existsSync(CONFIG_FILE)) return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
37
- } catch { /* ignore */ }
38
- return null;
39
- }
40
-
41
- function isFullConfig(raw: any): raw is FullConfig {
42
- return raw && Array.isArray(raw.accounts);
43
- }
44
-
45
- // Migrate a flat (legacy) config to the new multi-account shape.
46
- function migrate(raw: any): FullConfig {
47
- const { autocomplete_setup, ...account } = raw;
48
- return { active: 0, accounts: [account as AccountEntry], autocomplete_setup };
49
- }
50
-
51
- export function loadFullConfig(): FullConfig | null {
52
- const raw = readRaw();
53
- if (!raw) return null;
54
- return isFullConfig(raw) ? raw : migrate(raw);
55
- }
56
-
57
- // loadConfig returns the active account merged with globals — unchanged interface for all callers.
58
- export function loadConfig(): Config | null {
59
- const envKey = process.env.MYAPI_API_KEY || process.env.MYAPI_KEY;
60
- const full = loadFullConfig();
61
-
62
- if (!full && envKey) return { api_key: envKey, account_id: '' };
63
- if (!full) return null;
64
-
65
- const active = full.accounts[full.active] ?? full.accounts[0];
66
- if (!active) return null;
67
-
68
- const config: Config = {
69
- ...active,
70
- autocomplete_setup: full.autocomplete_setup,
71
- };
72
- if (envKey) config.api_key = envKey;
73
- return config;
74
- }
75
-
76
- export function saveConfig(config: Config): void {
77
- ensureDir();
78
- const full = loadFullConfig() ?? { active: 0, accounts: [] };
79
- const { autocomplete_setup, ...account } = config;
80
- if (full.accounts.length === 0) full.accounts.push(account as AccountEntry);
81
- else full.accounts[full.active] = account as AccountEntry;
82
- if (autocomplete_setup !== undefined) full.autocomplete_setup = autocomplete_setup;
83
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(full, null, 2), { mode: 0o600 });
84
- }
85
-
86
- export function addAccount(account: AccountEntry): number {
87
- ensureDir();
88
- const full = loadFullConfig() ?? { active: 0, accounts: [] };
89
- // Replace if same account_id already exists.
90
- const existing = full.accounts.findIndex(a => a.account_id === account.account_id);
91
- if (existing >= 0) {
92
- full.accounts[existing] = account;
93
- full.active = existing;
94
- } else {
95
- full.accounts.push(account);
96
- full.active = full.accounts.length - 1;
97
- }
98
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(full, null, 2), { mode: 0o600 });
99
- return full.active;
100
- }
101
-
102
- export function switchAccount(index: number): boolean {
103
- const full = loadFullConfig();
104
- if (!full || index < 0 || index >= full.accounts.length) return false;
105
- full.active = index;
106
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(full, null, 2), { mode: 0o600 });
107
- return true;
108
- }
109
-
110
- export function listAccounts(): Array<AccountEntry & { index: number; active: boolean }> {
111
- const full = loadFullConfig();
112
- if (!full) return [];
113
- return full.accounts.map((a, i) => ({ ...a, index: i, active: i === full.active }));
114
- }
115
-
116
- export function requireConfig(): Config {
117
- const config = loadConfig();
118
- if (!config || !config.api_key) {
119
- console.error("No API key found. Provide MYAPI_KEY env var or run: myapi auth setup");
120
- process.exit(1);
121
- }
122
- return config;
123
- }
package/src/index.ts DELETED
@@ -1,203 +0,0 @@
1
- #!/usr/bin/env node
2
- // manually maintained — do not regenerate from scripts/generate-indexes.js
3
- import { parseArgs } from './utils.js';
4
- import { error, info, success } from './output.js';
5
- import { loadConfig } from './config.js';
6
- import { MyApiError } from '@myapihq/sdk';
7
- import * as fs from 'fs';
8
-
9
- const pkgPath = new URL('../package.json', import.meta.url);
10
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
11
- import * as keysCmd from './commands/keys.js';
12
- import * as billingCmd from './commands/billing.js';
13
- import * as orgCmd from './commands/org.js';
14
- import * as setupCmd from './commands/setup.js';
15
- import * as updateCmd from './commands/update.js';
16
- import * as domainCmd from './commands/domain.js';
17
- import * as funnelCmd from './commands/funnel.js';
18
- import * as authCmd from './commands/auth.js';
19
- import * as configCmd from './commands/config.js';
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
-
38
- async function main() {
39
- const updatePromise = updateCmd.checkForUpdate(pkg.version).catch(() => {});
40
-
41
- const { args, flags } = parseArgs(process.argv.slice(2));
42
-
43
- if (flags.version || flags.v || flags.V) {
44
- const [latest] = await Promise.all([updateCmd.latestVersion(), updatePromise]);
45
- const updateNote = latest && updateCmd.isNewer(latest, pkg.version)
46
- ? ` (update available: ${latest})`
47
- : '';
48
- info(`myapi ${pkg.version}${updateNote}`);
49
- return;
50
- }
51
-
52
- if (flags.help && args.length === 0) {
53
- printHelp();
54
- return;
55
- }
56
-
57
- if (args.length === 0) {
58
- const config = loadConfig();
59
- if (!config?.api_key) {
60
- info('No account found. Run: myapi auth setup');
61
- info('');
62
- }
63
- printHelp();
64
- await updatePromise;
65
- return;
66
- }
67
-
68
- const [command, subcommand, ...restArgs] = args;
69
- try {
70
- switch (command) {
71
- case 'auth':
72
- if (!subcommand || (flags.help && !subcommand)) {
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 · supports --json\n link [email] Upgrade anonymous account to registered (or add a second session)\n Use myapi auth setup to create a completely new account\n switch [index] Switch active account by index or email\n config Manage CLI defaults (org, funnel, domain) · supports set-org / set-funnel / set-domain\n install-skills Install or update the MyAPI skills pack for AI agents\n api-keys Manage API keys · list / create / revoke\n keys Alias for api-keys');
74
- break;
75
- }
76
- if (subcommand === 'setup') await setupCmd.setup(flags);
77
- else if (subcommand === 'import-key') await setupCmd.importKey(restArgs[0], flags);
78
- else if (subcommand === 'whoami') await authCmd.whoami(flags);
79
- else if (subcommand === 'link') await authCmd.link(flags, restArgs[0]);
80
- else if (subcommand === 'switch') await authCmd.switchCmd(flags, restArgs[0]);
81
- else if (subcommand === 'install-skills') {
82
- if (flags.help) {
83
- info('Usage: myapi auth install-skills\n\nInstalls the MyAPI skills pack for AI coding agents (Claude, Gemini, Cursor).\n\nThis command writes skill definition files to:\n ~/.agents/skills/myapi/\n\nAnd creates symlinks in the appropriate agent config directories:\n ~/.claude/ (Claude)\n ~/.gemini/ (Gemini)\n ~/.cursor/ (Cursor, if detected)\n\nThese files teach agents how to use the MyAPI CLI and API directly.\nRun this command again to update existing skills to the latest version.');
84
- break;
85
- }
86
- await setupCmd.installSkills();
87
- success('› Skills installed.');
88
- } else if (subcommand === 'config') await configCmd.run(restArgs[0], restArgs.slice(1), flags);
89
- else if (subcommand === 'api-keys') {
90
- if (flags.help && !restArgs[0]) {
91
- info('Usage: myapi auth api-keys <subcommand>\n\nManage programmatic API keys for your account. API keys are used to authenticate\nrequests to the MyAPI SDK and REST API.\n\nSubcommands:\n list List all API keys with their IDs and creation dates\n create Create a new API key (the key value is shown once)\n revoke <id> Permanently revoke an API key by ID\n\nExamples:\n myapi auth api-keys list\n myapi auth api-keys create\n myapi auth api-keys revoke xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
92
- break;
93
- }
94
- if (!restArgs[0]) { info('Run: myapi auth api-keys --help'); break; }
95
- if (restArgs[0] === 'create') await keysCmd.createNew(flags);
96
- else if (restArgs[0] === 'list') await keysCmd.list(flags);
97
- else if (restArgs[0] === 'revoke') await keysCmd.revoke(restArgs[1], flags);
98
- } else info('Unknown subcommand. Run: myapi auth --help');
99
- break;
100
- case 'update':
101
- await updateCmd.update(flags);
102
- break;
103
- case 'org':
104
- if (!subcommand || (flags.help && !subcommand)) {
105
- info('Usage: myapi org <subcommand>\n\nSubcommands:\n list List organizations\n create Create an organization (use --yes to auto-set it as your default org and funnel)\n get Get details of an organization\n delete Delete an organization\n import Extract org from domain');
106
- break;
107
- }
108
- if (subcommand === 'list') await orgCmd.list(flags);
109
- else if (subcommand === 'create') await orgCmd.create(flags);
110
- else if (subcommand === 'get') await orgCmd.get(restArgs[0], flags);
111
- else if (subcommand === 'delete') await orgCmd.del(restArgs[0], flags);
112
- else if (subcommand === 'import') await orgCmd.importOrg(restArgs, flags);
113
- else printHelp();
114
- break;
115
- case 'billing':
116
- if (!subcommand || (flags.help && !subcommand)) {
117
- info('Usage: myapi billing <subcommand>\n\nSubcommands:\n balance Check balance\n history View billing history\n topup Top up your balance\n setup Setup a payment method');
118
- break;
119
- }
120
- if (subcommand === 'balance') await billingCmd.balance(flags);
121
- else if (subcommand === 'history') await billingCmd.history(flags);
122
- else if (subcommand === 'topup') await billingCmd.topup(restArgs[0], flags);
123
- else if (subcommand === 'setup') await billingCmd.setup(flags);
124
- else printHelp();
125
- break;
126
- case 'domain':
127
- await domainCmd.run(subcommand, restArgs, flags);
128
- break;
129
- case 'funnel':
130
- await funnelCmd.run(subcommand, restArgs, flags);
131
- break;
132
- // Convenience aliases
133
- case 'setup':
134
- await setupCmd.setup(flags);
135
- break;
136
- case 'whoami':
137
- await authCmd.whoami(flags);
138
- break;
139
- case 'keys':
140
- if (!subcommand || flags.help) {
141
- info('Usage: myapi keys <subcommand>\n\nManage programmatic API keys for your account. API keys are used to authenticate\nrequests to the MyAPI SDK and REST API.\n\nSubcommands:\n list List all API keys with their IDs and creation dates\n create Create a new API key (the key value is shown once)\n revoke <id> Permanently revoke an API key by ID\n\nExamples:\n myapi keys list\n myapi keys create\n myapi keys revoke xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n\nAlias for: myapi auth api-keys');
142
- break;
143
- }
144
- if (subcommand === 'create') await keysCmd.createNew(flags);
145
- else if (subcommand === 'list') await keysCmd.list(flags);
146
- else if (subcommand === 'revoke') await keysCmd.revoke(restArgs[0], flags);
147
- break;
148
- case 'config':
149
- await configCmd.run(subcommand, restArgs, { ...flags, _via: 'config' });
150
- break;
151
- case 'help':
152
- printHelp();
153
- break;
154
- default:
155
- error(`Unknown command: ${command}. Run "myapi" for available commands.`);
156
- }
157
- } catch (err: any) {
158
- if (err instanceof MyApiError) {
159
- if (err.status === 401) error('Invalid API key. Run: myapi auth setup');
160
- else if (err.status === 402) {
161
- if (err.code === 'REGISTRATION_REQUIRED' || err.code === 'UPGRADE_REQUIRED') error('A verified email is required. Run: myapi auth link');
162
- else if (err.code === 'NO_PAYMENT_METHOD') error('No payment method on file. Run: myapi billing setup');
163
- else error(`Insufficient balance. Run: myapi billing topup <amount>`);
164
- }
165
- else error(friendlyError(err.code) || err.message);
166
- } else {
167
- error(err.message || (typeof err === 'object' ? JSON.stringify(err) : String(err)));
168
- }
169
- }
170
- }
171
-
172
- function printHelp() {
173
- const config = loadConfig();
174
- const quickStart = config?.api_key
175
- ? `Quick start:
176
- echo '<h1>Hello!</h1>' | myapi funnel push /`
177
- : `Quick start:
178
- myapi auth setup --help`;
179
- info(`myapi - MyAPI command-line interface
180
-
181
- Usage: myapi <command> [subcommand] [args]
182
- myapi --version
183
-
184
- Commands:
185
- auth Manage account · setup · whoami · link
186
- billing Check balance and manage billing
187
- org Manage organizations (tip: myapi org create "name" --yes to auto-set as default)
188
- update Update CLI and skills to the latest version
189
- domain Manage domain configurations
190
- funnel Manage websites (publish pages, custom domains, funnels)
191
-
192
- Aliases:
193
- whoami → myapi auth whoami
194
- keys → myapi auth api-keys
195
- setup → myapi auth setup
196
- config → myapi auth config
197
-
198
- Run "myapi <command> --help" for subcommand help.
199
-
200
- ${quickStart}`);
201
- }
202
-
203
- main().catch(err => { error(err.message || (typeof err === 'object' ? JSON.stringify(err) : String(err))); });
package/src/output.ts DELETED
@@ -1,49 +0,0 @@
1
- const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
2
-
3
- export function success(message: string): void {
4
- console.log(useColor ? `\x1b[32m✓\x1b[0m ${message}` : `✓ ${message}`);
5
- }
6
-
7
- export function error(message: string): never {
8
- console.error(useColor ? `\x1b[31m✗\x1b[0m ${message}` : `✗ ${message}`);
9
- process.exit(1);
10
- }
11
-
12
- export function info(message: string): void {
13
- console.log(message);
14
- }
15
-
16
- export function printJson(data: unknown): void {
17
- console.log(JSON.stringify(data, null, 2));
18
- }
19
-
20
- export function printTable(rows: Record<string, unknown>[]): void {
21
- if (process.argv.includes('--json')) {
22
- printJson(rows);
23
- return;
24
- }
25
-
26
- if (rows.length === 0) {
27
- console.log("No data found.");
28
- return;
29
- }
30
-
31
- const columns = Object.keys(rows[0]);
32
- const colWidths = columns.map(col => {
33
- return Math.max(
34
- col.length,
35
- ...rows.map(row => String(row[col] ?? '').length)
36
- );
37
- });
38
-
39
- const printRow = (row: string[]) => {
40
- console.log(row.map((cell, i) => cell.padEnd(colWidths[i] + 2)).join(''));
41
- };
42
-
43
- printRow(columns);
44
- console.log(colWidths.map(w => '-'.repeat(w + 2)).join(''));
45
-
46
- for (const row of rows) {
47
- printRow(columns.map(col => String(row[col] ?? '')));
48
- }
49
- }
package/src/utils.ts DELETED
@@ -1,45 +0,0 @@
1
- export function parseArgs(argv: string[]) {
2
- const args: string[] = [];
3
- const flags: Record<string, string | boolean> = {};
4
-
5
- for (let i = 0; i < argv.length; i++) {
6
- const arg = argv[i];
7
- if (arg === '-h') {
8
- flags['help'] = true;
9
- } else if (arg === '-v') {
10
- flags['version'] = true;
11
- } else if (arg.startsWith('--')) {
12
- if (arg.includes('=')) {
13
- const [key, value] = arg.slice(2).split('=', 2);
14
- flags[key] = value;
15
- } else {
16
- const next = argv[i + 1];
17
- if (next && !next.startsWith('--')) {
18
- flags[arg.slice(2)] = next;
19
- i++;
20
- } else {
21
- flags[arg.slice(2)] = true;
22
- }
23
- }
24
- } else {
25
- args.push(arg);
26
- }
27
- }
28
-
29
- return { args, flags };
30
- }
31
-
32
- export function sleep(ms: number) {
33
- return new Promise(resolve => setTimeout(resolve, ms));
34
- }
35
-
36
- /**
37
- * Formats an ISO/Go timestamp string to "YYYY-MM-DD HH:mm" (UTC).
38
- * Strips Go's " +0000 UTC" suffix before parsing.
39
- */
40
- export function formatDate(str: string): string {
41
- const clean = str.replace(' +0000 UTC', 'Z');
42
- const date = new Date(clean);
43
- if (isNaN(date.getTime())) return str;
44
- return date.toISOString().replace('T', ' ').slice(0, 16);
45
- }
package/thank-you.html DELETED
@@ -1,56 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Thank You</title>
7
- <style>
8
- body {
9
- font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
10
- background-color: #f3f4f6;
11
- color: #1f2937;
12
- display: flex;
13
- align-items: center;
14
- justify-content: center;
15
- height: 100vh;
16
- margin: 0;
17
- text-align: center;
18
- }
19
- .container {
20
- background: white;
21
- padding: 3rem;
22
- border-radius: 1rem;
23
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
24
- max-width: 400px;
25
- width: 90%;
26
- }
27
- h1 {
28
- margin-top: 0;
29
- color: #4f46e5;
30
- font-size: 2.25rem;
31
- margin-bottom: 0.5rem;
32
- }
33
- p {
34
- color: #6b7280;
35
- line-height: 1.6;
36
- margin-bottom: 0;
37
- font-size: 1.125rem;
38
- }
39
- .icon {
40
- width: 64px;
41
- height: 64px;
42
- color: #10b981;
43
- margin-bottom: 1.5rem;
44
- }
45
- </style>
46
- </head>
47
- <body>
48
- <div class="container">
49
- <svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
50
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
51
- </svg>
52
- <h1>Thank You!</h1>
53
- <p>We've received your request and will be in touch shortly.</p>
54
- </div>
55
- </body>
56
- </html>
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "declaration": true,
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true
13
- },
14
- "include": ["src/**/*"]
15
- }