@myapihq/cli 1.0.27 → 1.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +84 -13
- package/dist/commands/config.d.ts +1 -0
- package/dist/commands/config.js +15 -2
- package/dist/commands/funnel.js +8 -6
- package/dist/commands/setup.js +87 -36
- package/dist/commands/update.js +0 -10
- package/dist/config.d.ts +18 -2
- package/dist/config.js +82 -21
- package/dist/index.js +43 -21
- package/dist/skills/my-api-hq.md +116 -0
- package/dist/skills/my-domain-api.md +83 -0
- package/dist/skills/my-funnel-api.md +35 -0
- package/package.json +2 -2
- package/scripts/copy-skills.js +3 -1
- package/src/commands/auth.ts +81 -14
- package/src/commands/config.ts +15 -2
- package/src/commands/funnel.ts +9 -7
- package/src/commands/setup.ts +90 -37
- package/src/commands/update.ts +0 -13
- package/src/config.ts +90 -24
- package/src/index.ts +30 -19
- package/src/skills/my-funnel-api.md +1 -1
package/src/commands/setup.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as os from 'os';
|
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import * as readline from 'readline';
|
|
5
5
|
|
|
6
|
-
import { loadConfig, saveConfig } from '../config.js';
|
|
6
|
+
import { loadConfig, saveConfig, addAccount, loadFullConfig } from '../config.js';
|
|
7
7
|
import { info, success } from '../output.js';
|
|
8
8
|
|
|
9
9
|
const API_BASE = process.env.MYAPI_API_BASE ?? 'https://api.myapihq.com';
|
|
@@ -54,30 +54,29 @@ export async function installSkills(): Promise<void> {
|
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
// Each bundled file is <skill-name>.md — install as <skill-name>/SKILL.md
|
|
59
58
|
const files = fs.readdirSync(BUNDLED_SKILLS_DIR).filter(f => f.endsWith('.md'));
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
const skills = files.map(f => f.replace(/\.md$/, ''));
|
|
60
|
+
|
|
61
|
+
// Write canonical copies: ~/.agents/skills/myapi/<skill-name>/SKILL.md
|
|
62
|
+
for (const skill of skills) {
|
|
63
|
+
const skillDir = path.join(SKILLS_CANONICAL, skill);
|
|
64
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
65
|
+
fs.copyFileSync(path.join(BUNDLED_SKILLS_DIR, `${skill}.md`), path.join(skillDir, 'SKILL.md'));
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
// Symlink each skill directory into agent dirs
|
|
66
69
|
for (const [agent, dir] of Object.entries(AGENT_DIRS)) {
|
|
67
70
|
try {
|
|
68
71
|
fs.mkdirSync(dir, { recursive: true });
|
|
69
|
-
for (const
|
|
70
|
-
const link = path.join(dir,
|
|
71
|
-
const target = path.join(SKILLS_CANONICAL,
|
|
72
|
+
for (const skill of skills) {
|
|
73
|
+
const link = path.join(dir, skill);
|
|
74
|
+
const target = path.join(SKILLS_CANONICAL, skill);
|
|
72
75
|
try {
|
|
73
|
-
|
|
74
|
-
const stat = fs.lstatSync(link);
|
|
75
|
-
if (stat.isSymbolicLink() && fs.readlinkSync(link) === target) continue;
|
|
76
|
-
fs.unlinkSync(link);
|
|
77
|
-
}
|
|
76
|
+
try { fs.rmSync(link, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
78
77
|
fs.symlinkSync(target, link);
|
|
79
78
|
} catch {
|
|
80
|
-
// Non-fatal
|
|
79
|
+
// Non-fatal.
|
|
81
80
|
}
|
|
82
81
|
}
|
|
83
82
|
info(` ✓ ${agent}`);
|
|
@@ -96,6 +95,7 @@ async function registeredFlow(rl: readline.Interface): Promise<{
|
|
|
96
95
|
account_id: string;
|
|
97
96
|
default_org: string;
|
|
98
97
|
default_funnel: string;
|
|
98
|
+
email: string;
|
|
99
99
|
}> {
|
|
100
100
|
const email = (await ask(rl, '› Email? ')).trim();
|
|
101
101
|
|
|
@@ -110,7 +110,7 @@ async function registeredFlow(rl: readline.Interface): Promise<{
|
|
|
110
110
|
default_funnel: string;
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
-
return data;
|
|
113
|
+
return { ...data, email };
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
// ---------------------------------------------------------------------------
|
|
@@ -143,28 +143,33 @@ export async function setup() {
|
|
|
143
143
|
|
|
144
144
|
const existing = loadConfig();
|
|
145
145
|
|
|
146
|
-
// Already configured —
|
|
146
|
+
// Already configured — ask before adding a new account.
|
|
147
147
|
if (existing?.api_key) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
148
|
+
const full = loadFullConfig();
|
|
149
|
+
const total = full?.accounts.length ?? 1;
|
|
150
|
+
const activeLabel = existing.email ?? existing.account_id;
|
|
151
|
+
const activeType = existing.is_anonymous ? 'anonymous' : 'registered';
|
|
152
|
+
const othersNote = total > 1 ? ` · ${total - 1} more saved` : '';
|
|
153
|
+
|
|
154
|
+
info(`› Connected: ${activeLabel} (${activeType})${othersNote}`);
|
|
155
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
156
|
+
const add = await ask(rl, '› Connect a new account? (y/N) ');
|
|
157
|
+
if (!yn(add, false)) {
|
|
158
|
+
if (!existing.skills_installed) {
|
|
159
|
+
const ans = await ask(rl, '› Install the MyAPI skills pack? (Y/n) ');
|
|
160
|
+
rl.close();
|
|
161
|
+
if (yn(ans)) {
|
|
162
|
+
await installSkills();
|
|
163
|
+
success('› Skills installed.');
|
|
164
|
+
saveConfig({ ...existing, skills_installed: true });
|
|
164
165
|
}
|
|
165
|
-
|
|
166
|
+
} else {
|
|
167
|
+
rl.close();
|
|
166
168
|
}
|
|
167
|
-
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
rl.close();
|
|
172
|
+
// Fall through to setup flow — new account will be added to the list.
|
|
168
173
|
}
|
|
169
174
|
|
|
170
175
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -175,6 +180,7 @@ export async function setup() {
|
|
|
175
180
|
let defaultFunnel = '';
|
|
176
181
|
let subdomainUrl = '';
|
|
177
182
|
let isAnonymous = false;
|
|
183
|
+
let email = '';
|
|
178
184
|
|
|
179
185
|
try {
|
|
180
186
|
const createAns = await ask(rl, '› Create an account? (Y/n) ');
|
|
@@ -185,6 +191,7 @@ export async function setup() {
|
|
|
185
191
|
accountId = data.account_id;
|
|
186
192
|
defaultOrg = data.default_org;
|
|
187
193
|
defaultFunnel = data.default_funnel;
|
|
194
|
+
email = data.email;
|
|
188
195
|
} else {
|
|
189
196
|
info('› Continuing as anonymous — sites ship to *.makeautonomous.com');
|
|
190
197
|
const data = await anonymousFlow();
|
|
@@ -199,10 +206,11 @@ export async function setup() {
|
|
|
199
206
|
const skillsAns = await ask(rl, '› Install the MyAPI skills pack? (Y/n) ');
|
|
200
207
|
const wantsSkills = yn(skillsAns);
|
|
201
208
|
|
|
202
|
-
|
|
209
|
+
addAccount({
|
|
203
210
|
api_key: apiKey,
|
|
204
211
|
account_id: accountId,
|
|
205
212
|
pin: '',
|
|
213
|
+
email: email || undefined,
|
|
206
214
|
default_org: defaultOrg,
|
|
207
215
|
default_funnel: defaultFunnel,
|
|
208
216
|
is_anonymous: isAnonymous,
|
|
@@ -215,6 +223,51 @@ export async function setup() {
|
|
|
215
223
|
await installSkills();
|
|
216
224
|
}
|
|
217
225
|
|
|
226
|
+
// Validate key and ensure org/funnel defaults are correct.
|
|
227
|
+
try {
|
|
228
|
+
const auth = { Authorization: `Bearer ${apiKey}` };
|
|
229
|
+
const meRes = await fetch(`${API_BASE}/hq/account/me`, { headers: auth });
|
|
230
|
+
if (!meRes.ok) {
|
|
231
|
+
info('› Warning: could not verify API key — check your connection.');
|
|
232
|
+
} else {
|
|
233
|
+
// Verify default org exists; if not, fetch the most recent one.
|
|
234
|
+
let resolvedOrg = defaultOrg;
|
|
235
|
+
let resolvedFunnel = defaultFunnel;
|
|
236
|
+
|
|
237
|
+
const orgRes = await fetch(`${API_BASE}/hq/orgs`, { headers: auth });
|
|
238
|
+
if (orgRes.ok) {
|
|
239
|
+
const orgs = (await orgRes.json() as any)?.data ?? [];
|
|
240
|
+
if (orgs.length > 0) {
|
|
241
|
+
const latest = orgs[orgs.length - 1];
|
|
242
|
+
if (!resolvedOrg || !orgs.find((o: any) => o.id === resolvedOrg)) {
|
|
243
|
+
resolvedOrg = latest.id;
|
|
244
|
+
info(`› Auto-selected org: ${latest.name} (${resolvedOrg})`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Verify default funnel exists within the org.
|
|
248
|
+
const fRes = await fetch(`${API_BASE}/funnel/orgs/${resolvedOrg}/funnels`, { headers: auth });
|
|
249
|
+
if (fRes.ok) {
|
|
250
|
+
const funnels = (await fRes.json() as any)?.data ?? [];
|
|
251
|
+
if (funnels.length > 0 && (!resolvedFunnel || !funnels.find((f: any) => f.id === resolvedFunnel))) {
|
|
252
|
+
resolvedFunnel = funnels[funnels.length - 1].id;
|
|
253
|
+
info(`› Auto-selected funnel: ${resolvedFunnel}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (resolvedOrg !== defaultOrg || resolvedFunnel !== defaultFunnel) {
|
|
258
|
+
const full = loadFullConfig()!;
|
|
259
|
+
const idx = full.active;
|
|
260
|
+
full.accounts[idx].default_org = resolvedOrg;
|
|
261
|
+
full.accounts[idx].default_funnel = resolvedFunnel;
|
|
262
|
+
fs.writeFileSync(path.join(os.homedir(), '.myapi', 'config.json'), JSON.stringify(full, null, 2), { mode: 0o600 });
|
|
263
|
+
defaultOrg = resolvedOrg;
|
|
264
|
+
defaultFunnel = resolvedFunnel;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} catch { /* non-fatal */ }
|
|
270
|
+
|
|
218
271
|
if (isAnonymous) {
|
|
219
272
|
success('› Setup complete. No card, no email, ready to ship.');
|
|
220
273
|
if (subdomainUrl) info(`› Your funnel: ${subdomainUrl}`);
|
package/src/commands/update.ts
CHANGED
|
@@ -1,31 +1,18 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
|
-
import { loadConfig, saveConfig } from '../config.js';
|
|
3
2
|
import { info, success } from '../output.js';
|
|
4
3
|
import { installSkills } from './setup.js';
|
|
5
4
|
|
|
6
5
|
const REGISTRY_URL = 'https://registry.npmjs.org/@myapihq/cli/latest';
|
|
7
|
-
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
8
6
|
|
|
9
7
|
// checkForUpdate runs silently in the background on every command.
|
|
10
8
|
// Auto-installs if a newer version is available.
|
|
11
9
|
export async function checkForUpdate(currentVersion: string): Promise<void> {
|
|
12
|
-
const config = loadConfig();
|
|
13
|
-
const now = Date.now();
|
|
14
|
-
|
|
15
|
-
if (config?.last_update_check && now - config.last_update_check < CHECK_INTERVAL_MS) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
10
|
try {
|
|
20
11
|
const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(3000) });
|
|
21
12
|
if (!res.ok) return;
|
|
22
13
|
const data = await res.json() as { version?: string };
|
|
23
14
|
const latest = data.version;
|
|
24
15
|
|
|
25
|
-
if (config) {
|
|
26
|
-
saveConfig({ ...config, last_update_check: now });
|
|
27
|
-
}
|
|
28
|
-
|
|
29
16
|
if (latest && isNewer(latest, currentVersion)) {
|
|
30
17
|
info(`\n› New version available (${currentVersion} → ${latest}) — installing…`);
|
|
31
18
|
execSync('npm install -g @myapihq/cli', { stdio: 'pipe' });
|
package/src/config.ts
CHANGED
|
@@ -2,56 +2,122 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
|
|
5
|
-
export interface
|
|
5
|
+
export interface AccountEntry {
|
|
6
6
|
api_key: string;
|
|
7
7
|
account_id: string;
|
|
8
8
|
pin: string;
|
|
9
|
+
email?: string;
|
|
9
10
|
default_org?: string;
|
|
10
11
|
default_funnel?: string;
|
|
11
12
|
default_domain?: string;
|
|
12
13
|
is_anonymous?: boolean;
|
|
13
14
|
skills_installed?: boolean;
|
|
14
|
-
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Flat interface used by all command files — represents the active account.
|
|
18
|
+
export interface Config extends AccountEntry {
|
|
15
19
|
autocomplete_setup?: boolean;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
export interface FullConfig {
|
|
23
|
+
active: number;
|
|
24
|
+
accounts: AccountEntry[];
|
|
25
|
+
autocomplete_setup?: boolean;
|
|
26
|
+
}
|
|
20
27
|
|
|
21
|
-
export
|
|
22
|
-
|
|
28
|
+
export const CONFIG_DIR = path.join(os.homedir(), '.myapi');
|
|
29
|
+
export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
30
|
+
|
|
31
|
+
function ensureDir() {
|
|
32
|
+
if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readRaw(): any {
|
|
23
36
|
try {
|
|
24
|
-
if (fs.existsSync(CONFIG_FILE))
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
if (fs.existsSync(CONFIG_FILE)) return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
38
|
+
} catch { /* ignore */ }
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isFullConfig(raw: any): raw is FullConfig {
|
|
43
|
+
return raw && Array.isArray(raw.accounts);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Migrate a flat (legacy) config to the new multi-account shape.
|
|
47
|
+
function migrate(raw: any): FullConfig {
|
|
48
|
+
const { autocomplete_setup, ...account } = raw;
|
|
49
|
+
return { active: 0, accounts: [account as AccountEntry], autocomplete_setup };
|
|
50
|
+
}
|
|
31
51
|
|
|
52
|
+
export function loadFullConfig(): FullConfig | null {
|
|
53
|
+
const raw = readRaw();
|
|
54
|
+
if (!raw) return null;
|
|
55
|
+
return isFullConfig(raw) ? raw : migrate(raw);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// loadConfig returns the active account merged with globals — unchanged interface for all callers.
|
|
59
|
+
export function loadConfig(): Config | null {
|
|
32
60
|
const envKey = process.env.MYAPI_KEY;
|
|
33
|
-
|
|
34
|
-
fileConfig.api_key = envKey;
|
|
35
|
-
}
|
|
61
|
+
const full = loadFullConfig();
|
|
36
62
|
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
if (!full && envKey) return { api_key: envKey, account_id: '', pin: '' };
|
|
64
|
+
if (!full) return null;
|
|
65
|
+
|
|
66
|
+
const active = full.accounts[full.active] ?? full.accounts[0];
|
|
67
|
+
if (!active) return null;
|
|
40
68
|
|
|
41
|
-
|
|
69
|
+
const config: Config = {
|
|
70
|
+
...active,
|
|
71
|
+
autocomplete_setup: full.autocomplete_setup,
|
|
72
|
+
};
|
|
73
|
+
if (envKey) config.api_key = envKey;
|
|
74
|
+
return config;
|
|
42
75
|
}
|
|
43
76
|
|
|
44
77
|
export function saveConfig(config: Config): void {
|
|
45
|
-
|
|
46
|
-
|
|
78
|
+
ensureDir();
|
|
79
|
+
const full = loadFullConfig() ?? { active: 0, accounts: [] };
|
|
80
|
+
const { autocomplete_setup, ...account } = config;
|
|
81
|
+
if (full.accounts.length === 0) full.accounts.push(account as AccountEntry);
|
|
82
|
+
else full.accounts[full.active] = account as AccountEntry;
|
|
83
|
+
if (autocomplete_setup !== undefined) full.autocomplete_setup = autocomplete_setup;
|
|
84
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(full, null, 2), { mode: 0o600 });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function addAccount(account: AccountEntry): number {
|
|
88
|
+
ensureDir();
|
|
89
|
+
const full = loadFullConfig() ?? { active: 0, accounts: [] };
|
|
90
|
+
// Replace if same account_id already exists.
|
|
91
|
+
const existing = full.accounts.findIndex(a => a.account_id === account.account_id);
|
|
92
|
+
if (existing >= 0) {
|
|
93
|
+
full.accounts[existing] = account;
|
|
94
|
+
full.active = existing;
|
|
95
|
+
} else {
|
|
96
|
+
full.accounts.push(account);
|
|
97
|
+
full.active = full.accounts.length - 1;
|
|
47
98
|
}
|
|
48
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(
|
|
99
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(full, null, 2), { mode: 0o600 });
|
|
100
|
+
return full.active;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function switchAccount(index: number): boolean {
|
|
104
|
+
const full = loadFullConfig();
|
|
105
|
+
if (!full || index < 0 || index >= full.accounts.length) return false;
|
|
106
|
+
full.active = index;
|
|
107
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(full, null, 2), { mode: 0o600 });
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function listAccounts(): Array<AccountEntry & { index: number; active: boolean }> {
|
|
112
|
+
const full = loadFullConfig();
|
|
113
|
+
if (!full) return [];
|
|
114
|
+
return full.accounts.map((a, i) => ({ ...a, index: i, active: i === full.active }));
|
|
49
115
|
}
|
|
50
116
|
|
|
51
117
|
export function requireConfig(): Config {
|
|
52
118
|
const config = loadConfig();
|
|
53
119
|
if (!config || !config.api_key) {
|
|
54
|
-
console.error("No API key found. Provide MYAPI_KEY env var or run: myapi setup");
|
|
120
|
+
console.error("No API key found. Provide MYAPI_KEY env var or run: myapi auth setup");
|
|
55
121
|
process.exit(1);
|
|
56
122
|
}
|
|
57
123
|
return config;
|
package/src/index.ts
CHANGED
|
@@ -12,11 +12,11 @@ import * as keysCmd from './commands/keys.js';
|
|
|
12
12
|
import * as billingCmd from './commands/billing.js';
|
|
13
13
|
import * as orgCmd from './commands/org.js';
|
|
14
14
|
import * as setupCmd from './commands/setup.js';
|
|
15
|
-
import * as configCmd from './commands/config.js';
|
|
16
|
-
import * as authCmd from './commands/auth.js';
|
|
17
15
|
import * as updateCmd from './commands/update.js';
|
|
18
16
|
import * as domainCmd from './commands/domain.js';
|
|
19
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
20
|
|
|
21
21
|
async function main() {
|
|
22
22
|
// Fire-and-forget auto-update check — never blocks the command.
|
|
@@ -36,8 +36,8 @@ async function main() {
|
|
|
36
36
|
if (args.length === 0) {
|
|
37
37
|
const config = loadConfig();
|
|
38
38
|
if (!config?.api_key) {
|
|
39
|
-
info('No account found. Run: myapi setup');
|
|
40
|
-
|
|
39
|
+
info('No account found. Run: myapi auth setup');
|
|
40
|
+
info('');
|
|
41
41
|
}
|
|
42
42
|
printHelp();
|
|
43
43
|
process.exit(0);
|
|
@@ -46,13 +46,25 @@ async function main() {
|
|
|
46
46
|
const [command, subcommand, ...restArgs] = args;
|
|
47
47
|
try {
|
|
48
48
|
switch (command) {
|
|
49
|
-
case 'setup':
|
|
50
|
-
await setupCmd.setup();
|
|
51
|
-
break;
|
|
52
49
|
case 'auth':
|
|
53
|
-
if (subcommand
|
|
50
|
+
if (!subcommand || flags.help) {
|
|
51
|
+
info('Usage: myapi auth <subcommand>\n\nSubcommands:\n setup Configure your account\n whoami Show current account\n signup Upgrade anonymous account to registered\n switch Switch between accounts\n config Manage CLI defaults (org_id, domain…)\n install-skills Install the MyAPI skills pack\n api-keys Manage API keys');
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
if (subcommand === 'setup') await setupCmd.setup();
|
|
54
55
|
else if (subcommand === 'whoami') await authCmd.whoami();
|
|
55
|
-
else
|
|
56
|
+
else if (subcommand === 'signup') await authCmd.signup();
|
|
57
|
+
else if (subcommand === 'switch') await authCmd.switchCmd();
|
|
58
|
+
else if (subcommand === 'install-skills') {
|
|
59
|
+
await setupCmd.installSkills();
|
|
60
|
+
success('› Skills installed.');
|
|
61
|
+
} else if (subcommand === 'config') await configCmd.run(restArgs[0], restArgs.slice(1), flags);
|
|
62
|
+
else if (subcommand === 'api-keys') {
|
|
63
|
+
if (!restArgs[0]) { info('Usage: myapi auth api-keys <list|create|revoke>'); break; }
|
|
64
|
+
if (restArgs[0] === 'create') await keysCmd.createNew(flags);
|
|
65
|
+
else if (restArgs[0] === 'list') await keysCmd.list(flags);
|
|
66
|
+
else if (restArgs[0] === 'revoke') await keysCmd.revoke(restArgs[1], flags);
|
|
67
|
+
} else info('Unknown subcommand. Run: myapi auth --help');
|
|
56
68
|
break;
|
|
57
69
|
case 'update':
|
|
58
70
|
await updateCmd.update();
|
|
@@ -90,9 +102,6 @@ async function main() {
|
|
|
90
102
|
else if (subcommand === 'setup') await billingCmd.setup(flags);
|
|
91
103
|
else printHelp();
|
|
92
104
|
break;
|
|
93
|
-
case 'config':
|
|
94
|
-
await configCmd.run(subcommand, restArgs, flags);
|
|
95
|
-
break;
|
|
96
105
|
case 'domain':
|
|
97
106
|
await domainCmd.run(subcommand, restArgs, flags);
|
|
98
107
|
break;
|
|
@@ -113,6 +122,13 @@ async function main() {
|
|
|
113
122
|
}
|
|
114
123
|
|
|
115
124
|
function printHelp() {
|
|
125
|
+
const config = loadConfig();
|
|
126
|
+
const quickStart = config?.api_key
|
|
127
|
+
? `Quick start:
|
|
128
|
+
myapi funnel create
|
|
129
|
+
echo '<h1>Hello!</h1>' | myapi funnel push <funnel_id> /`
|
|
130
|
+
: `Quick start:
|
|
131
|
+
myapi setup`;
|
|
116
132
|
info(`myapi - MyAPI command-line interface
|
|
117
133
|
|
|
118
134
|
Usage: myapi <command> [subcommand] [args]
|
|
@@ -122,19 +138,14 @@ Commands:
|
|
|
122
138
|
keys Manage API keys
|
|
123
139
|
billing Check balance and manage billing
|
|
124
140
|
org Manage organizations
|
|
125
|
-
setup Configure
|
|
126
|
-
config Manage CLI defaults like org_id and domain
|
|
127
|
-
auth Manage authentication (signup, whoami)
|
|
141
|
+
setup Configure account · whoami · signup · config · install-skills
|
|
128
142
|
update Update CLI and skills to the latest version
|
|
129
143
|
domain Manage domain configurations
|
|
130
144
|
funnel Manage headless funnels and pages
|
|
131
145
|
|
|
132
146
|
Run "myapi <command> --help" for subcommand help.
|
|
133
147
|
|
|
134
|
-
|
|
135
|
-
myapi setup
|
|
136
|
-
myapi funnel create
|
|
137
|
-
echo '<h1>Hello!</h1>' | myapi funnel push <funnel_id> /`);
|
|
148
|
+
${quickStart}`);
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
main().catch(err => { error(err.message || (typeof err === 'object' ? JSON.stringify(err) : String(err))); });
|
|
@@ -11,7 +11,7 @@ These endpoints manage the database records and structural configuration of funn
|
|
|
11
11
|
|
|
12
12
|
- `GET /funnel/orgs/{org_id}/funnels`
|
|
13
13
|
Lists all funnels for the specified organization.
|
|
14
|
-
- `POST /funnel/orgs/{org_id}/funnels
|
|
14
|
+
- `POST /funnel/orgs/{org_id}/funnels`
|
|
15
15
|
Creates a new funnel entry. Expects basic configuration metadata (name, domain, etc.). Body: `{ "domain": "example.com" }`
|
|
16
16
|
- `GET /funnel/orgs/{org_id}/funnels/{id}`
|
|
17
17
|
Retrieves the metadata and configuration details of a specific funnel.
|