@myapihq/cli 1.0.25 → 1.0.28
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 +4 -2
- package/dist/config.d.ts +19 -1
- package/dist/config.js +85 -21
- package/dist/index.js +65 -22
- 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 +4 -2
- package/src/config.ts +93 -23
- package/src/index.ts +56 -21
- 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
|
@@ -22,8 +22,10 @@ export async function checkForUpdate(currentVersion: string): Promise<void> {
|
|
|
22
22
|
const data = await res.json() as { version?: string };
|
|
23
23
|
const latest = data.version;
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
// Re-read config at write time to avoid clobbering changes made during setup.
|
|
26
|
+
const current = loadConfig();
|
|
27
|
+
if (current) {
|
|
28
|
+
saveConfig({ ...current, last_update_check: now });
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
if (latest && isNewer(latest, currentVersion)) {
|
package/src/config.ts
CHANGED
|
@@ -2,56 +2,126 @@ 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;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Flat interface used by all command files — represents the active account.
|
|
18
|
+
export interface Config extends AccountEntry {
|
|
14
19
|
last_update_check?: number;
|
|
15
20
|
autocomplete_setup?: boolean;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
export interface FullConfig {
|
|
24
|
+
active: number;
|
|
25
|
+
accounts: AccountEntry[];
|
|
26
|
+
last_update_check?: number;
|
|
27
|
+
autocomplete_setup?: boolean;
|
|
28
|
+
}
|
|
20
29
|
|
|
21
|
-
export
|
|
22
|
-
|
|
30
|
+
export const CONFIG_DIR = path.join(os.homedir(), '.myapi');
|
|
31
|
+
export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
32
|
+
|
|
33
|
+
function ensureDir() {
|
|
34
|
+
if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readRaw(): any {
|
|
23
38
|
try {
|
|
24
|
-
if (fs.existsSync(CONFIG_FILE))
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
if (fs.existsSync(CONFIG_FILE)) return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
40
|
+
} catch { /* ignore */ }
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isFullConfig(raw: any): raw is FullConfig {
|
|
45
|
+
return raw && Array.isArray(raw.accounts);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Migrate a flat (legacy) config to the new multi-account shape.
|
|
49
|
+
function migrate(raw: any): FullConfig {
|
|
50
|
+
const { last_update_check, autocomplete_setup, ...account } = raw;
|
|
51
|
+
return { active: 0, accounts: [account as AccountEntry], last_update_check, autocomplete_setup };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function loadFullConfig(): FullConfig | null {
|
|
55
|
+
const raw = readRaw();
|
|
56
|
+
if (!raw) return null;
|
|
57
|
+
return isFullConfig(raw) ? raw : migrate(raw);
|
|
58
|
+
}
|
|
31
59
|
|
|
60
|
+
// loadConfig returns the active account merged with globals — unchanged interface for all callers.
|
|
61
|
+
export function loadConfig(): Config | null {
|
|
32
62
|
const envKey = process.env.MYAPI_KEY;
|
|
33
|
-
|
|
34
|
-
fileConfig.api_key = envKey;
|
|
35
|
-
}
|
|
63
|
+
const full = loadFullConfig();
|
|
36
64
|
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
65
|
+
if (!full && envKey) return { api_key: envKey, account_id: '', pin: '' };
|
|
66
|
+
if (!full) return null;
|
|
67
|
+
|
|
68
|
+
const active = full.accounts[full.active] ?? full.accounts[0];
|
|
69
|
+
if (!active) return null;
|
|
40
70
|
|
|
41
|
-
|
|
71
|
+
const config: Config = {
|
|
72
|
+
...active,
|
|
73
|
+
last_update_check: full.last_update_check,
|
|
74
|
+
autocomplete_setup: full.autocomplete_setup,
|
|
75
|
+
};
|
|
76
|
+
if (envKey) config.api_key = envKey;
|
|
77
|
+
return config;
|
|
42
78
|
}
|
|
43
79
|
|
|
44
80
|
export function saveConfig(config: Config): void {
|
|
45
|
-
|
|
46
|
-
|
|
81
|
+
ensureDir();
|
|
82
|
+
const full = loadFullConfig() ?? { active: 0, accounts: [] };
|
|
83
|
+
const { last_update_check, autocomplete_setup, ...account } = config;
|
|
84
|
+
if (full.accounts.length === 0) full.accounts.push(account as AccountEntry);
|
|
85
|
+
else full.accounts[full.active] = account as AccountEntry;
|
|
86
|
+
if (last_update_check !== undefined) full.last_update_check = last_update_check;
|
|
87
|
+
if (autocomplete_setup !== undefined) full.autocomplete_setup = autocomplete_setup;
|
|
88
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(full, null, 2), { mode: 0o600 });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function addAccount(account: AccountEntry): number {
|
|
92
|
+
ensureDir();
|
|
93
|
+
const full = loadFullConfig() ?? { active: 0, accounts: [] };
|
|
94
|
+
// Replace if same account_id already exists.
|
|
95
|
+
const existing = full.accounts.findIndex(a => a.account_id === account.account_id);
|
|
96
|
+
if (existing >= 0) {
|
|
97
|
+
full.accounts[existing] = account;
|
|
98
|
+
full.active = existing;
|
|
99
|
+
} else {
|
|
100
|
+
full.accounts.push(account);
|
|
101
|
+
full.active = full.accounts.length - 1;
|
|
47
102
|
}
|
|
48
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(
|
|
103
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(full, null, 2), { mode: 0o600 });
|
|
104
|
+
return full.active;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function switchAccount(index: number): boolean {
|
|
108
|
+
const full = loadFullConfig();
|
|
109
|
+
if (!full || index < 0 || index >= full.accounts.length) return false;
|
|
110
|
+
full.active = index;
|
|
111
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(full, null, 2), { mode: 0o600 });
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function listAccounts(): Array<AccountEntry & { index: number; active: boolean }> {
|
|
116
|
+
const full = loadFullConfig();
|
|
117
|
+
if (!full) return [];
|
|
118
|
+
return full.accounts.map((a, i) => ({ ...a, index: i, active: i === full.active }));
|
|
49
119
|
}
|
|
50
120
|
|
|
51
121
|
export function requireConfig(): Config {
|
|
52
122
|
const config = loadConfig();
|
|
53
123
|
if (!config || !config.api_key) {
|
|
54
|
-
console.error("No API key found. Provide MYAPI_KEY env var or run: myapi setup");
|
|
124
|
+
console.error("No API key found. Provide MYAPI_KEY env var or run: myapi auth setup");
|
|
55
125
|
process.exit(1);
|
|
56
126
|
}
|
|
57
127
|
return config;
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { parseArgs } from './utils.js';
|
|
|
4
4
|
import { error, info, success } from './output.js';
|
|
5
5
|
import { loadConfig } from './config.js';
|
|
6
6
|
import { MyApiError } from '@myapihq/sdk';
|
|
7
|
-
import updateNotifier from 'update-notifier';
|
|
8
7
|
import * as fs from 'fs';
|
|
9
8
|
|
|
10
9
|
const pkgPath = new URL('../package.json', import.meta.url);
|
|
@@ -13,26 +12,62 @@ import * as keysCmd from './commands/keys.js';
|
|
|
13
12
|
import * as billingCmd from './commands/billing.js';
|
|
14
13
|
import * as orgCmd from './commands/org.js';
|
|
15
14
|
import * as setupCmd from './commands/setup.js';
|
|
16
|
-
import * as
|
|
15
|
+
import * as updateCmd from './commands/update.js';
|
|
17
16
|
import * as domainCmd from './commands/domain.js';
|
|
18
17
|
import * as funnelCmd from './commands/funnel.js';
|
|
18
|
+
import * as authCmd from './commands/auth.js';
|
|
19
|
+
import * as configCmd from './commands/config.js';
|
|
19
20
|
|
|
20
21
|
async function main() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} catch(e) {}
|
|
22
|
+
// Fire-and-forget auto-update check — never blocks the command.
|
|
23
|
+
updateCmd.checkForUpdate(pkg.version).catch(() => {});
|
|
24
24
|
|
|
25
25
|
const { args, flags } = parseArgs(process.argv.slice(2));
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
if (flags.version || flags.v) {
|
|
28
|
+
const latest = await updateCmd.latestVersion();
|
|
29
|
+
const updateNote = latest && updateCmd.isNewer(latest, pkg.version)
|
|
30
|
+
? ` (update available: ${latest})`
|
|
31
|
+
: '';
|
|
32
|
+
info(`myapi ${pkg.version}${updateNote}`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (args.length === 0) {
|
|
37
|
+
const config = loadConfig();
|
|
38
|
+
if (!config?.api_key) {
|
|
39
|
+
info('No account found. Run: myapi auth setup');
|
|
40
|
+
info('');
|
|
41
|
+
}
|
|
42
|
+
printHelp();
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
const [command, subcommand, ...restArgs] = args;
|
|
28
47
|
try {
|
|
29
48
|
switch (command) {
|
|
30
|
-
case '
|
|
31
|
-
if (flags.help) {
|
|
32
|
-
info('Usage: myapi setup\n\
|
|
49
|
+
case 'auth':
|
|
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');
|
|
33
52
|
break;
|
|
34
53
|
}
|
|
35
|
-
await setupCmd.setup();
|
|
54
|
+
if (subcommand === 'setup') await setupCmd.setup();
|
|
55
|
+
else if (subcommand === 'whoami') await authCmd.whoami();
|
|
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');
|
|
68
|
+
break;
|
|
69
|
+
case 'update':
|
|
70
|
+
await updateCmd.update();
|
|
36
71
|
break;
|
|
37
72
|
case 'keys':
|
|
38
73
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
@@ -67,9 +102,6 @@ async function main() {
|
|
|
67
102
|
else if (subcommand === 'setup') await billingCmd.setup(flags);
|
|
68
103
|
else printHelp();
|
|
69
104
|
break;
|
|
70
|
-
case 'config':
|
|
71
|
-
await configCmd.run(subcommand, restArgs, flags);
|
|
72
|
-
break;
|
|
73
105
|
case 'domain':
|
|
74
106
|
await domainCmd.run(subcommand, restArgs, flags);
|
|
75
107
|
break;
|
|
@@ -90,27 +122,30 @@ async function main() {
|
|
|
90
122
|
}
|
|
91
123
|
|
|
92
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`;
|
|
93
132
|
info(`myapi - MyAPI command-line interface
|
|
94
133
|
|
|
95
134
|
Usage: myapi <command> [subcommand] [args]
|
|
135
|
+
myapi --version
|
|
96
136
|
|
|
97
137
|
Commands:
|
|
98
138
|
keys Manage API keys
|
|
99
139
|
billing Check balance and manage billing
|
|
100
140
|
org Manage organizations
|
|
101
|
-
setup
|
|
102
|
-
|
|
141
|
+
setup Configure account · whoami · signup · config · install-skills
|
|
142
|
+
update Update CLI and skills to the latest version
|
|
103
143
|
domain Manage domain configurations
|
|
104
144
|
funnel Manage headless funnels and pages
|
|
105
145
|
|
|
106
146
|
Run "myapi <command> --help" for subcommand help.
|
|
107
147
|
|
|
108
|
-
|
|
109
|
-
myapi org create --name "Acme Inc"
|
|
110
|
-
myapi domain register acme.com --org <org_id>
|
|
111
|
-
myapi domain assign acme.com --org <org_id> --target <org_id>
|
|
112
|
-
myapi funnel create --org <org_id>
|
|
113
|
-
echo '<h1>Hello!</h1>' | myapi funnel push <funnel_id> / --org <org_id>`);
|
|
148
|
+
${quickStart}`);
|
|
114
149
|
}
|
|
115
150
|
|
|
116
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.
|