@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.
- package/dist/commands/auth.js +12 -4
- package/dist/commands/config.d.ts +4 -4
- package/dist/commands/config.js +12 -12
- package/dist/commands/funnel.js +1 -1
- package/dist/commands/keys.js +2 -2
- package/dist/commands/org.js +1 -1
- package/dist/commands/update.js +1 -1
- package/dist/skills/my-api-hq/README.md +37 -0
- package/dist/skills/my-api-hq/SKILL.md +116 -0
- package/dist/skills/my-api-hq/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-api-hq/make/.gitkeep +0 -0
- package/dist/skills/my-api-hq/n8n/.gitkeep +0 -0
- package/dist/skills/my-api-hq/openapi/.gitkeep +0 -0
- package/dist/skills/my-domain-api/README.md +37 -0
- package/dist/skills/my-domain-api/SKILL.md +83 -0
- package/dist/skills/my-domain-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-domain-api/make/.gitkeep +0 -0
- package/dist/skills/my-domain-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-domain-api/openapi/.gitkeep +0 -0
- package/dist/skills/my-funnel-api/README.md +39 -0
- package/dist/skills/my-funnel-api/SKILL.md +35 -0
- package/dist/skills/my-funnel-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-funnel-api/make/.gitkeep +0 -0
- package/dist/skills/my-funnel-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-funnel-api/openapi/.gitkeep +0 -0
- package/package.json +4 -1
- package/scripts/copy-skills.js +0 -13
- package/src/commands/auth.ts +0 -190
- package/src/commands/billing.ts +0 -92
- package/src/commands/config.ts +0 -71
- package/src/commands/domain.ts +0 -134
- package/src/commands/email.ts +0 -185
- package/src/commands/funnel.ts +0 -123
- package/src/commands/image.ts +0 -85
- package/src/commands/keys.ts +0 -68
- package/src/commands/org.ts +0 -135
- package/src/commands/pixel.ts +0 -62
- package/src/commands/setup.ts +0 -335
- package/src/commands/storage.ts +0 -56
- package/src/commands/update.ts +0 -89
- package/src/commands/url.ts +0 -28
- package/src/commands/webhook.ts +0 -66
- package/src/commands/workflow.ts +0 -102
- package/src/config.ts +0 -123
- package/src/index.ts +0 -203
- package/src/output.ts +0 -49
- package/src/utils.ts +0 -45
- package/thank-you.html +0 -56
- package/tsconfig.json +0 -15
package/src/commands/setup.ts
DELETED
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as os from 'os';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import * as readline from 'readline';
|
|
5
|
-
|
|
6
|
-
import { loadConfig, saveConfig, addAccount, loadFullConfig } from '../config.js';
|
|
7
|
-
import { info, success } from '../output.js';
|
|
8
|
-
|
|
9
|
-
const API_BASE = process.env.MYAPI_API_BASE ?? 'https://api.myapihq.com';
|
|
10
|
-
|
|
11
|
-
function ask(rl: readline.Interface, q: string): Promise<string> {
|
|
12
|
-
return new Promise(resolve => rl.question(q, resolve));
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function yn(answer: string, defaultYes = true): boolean {
|
|
16
|
-
const t = answer.trim().toLowerCase();
|
|
17
|
-
if (t === '') return defaultYes;
|
|
18
|
-
return t === 'y' || t === 'yes';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async function post(path: string, body: unknown): Promise<unknown> {
|
|
22
|
-
const res = await fetch(`${API_BASE}${path}`, {
|
|
23
|
-
method: 'POST',
|
|
24
|
-
headers: { 'Content-Type': 'application/json' },
|
|
25
|
-
body: JSON.stringify(body),
|
|
26
|
-
});
|
|
27
|
-
const json = await res.json() as { data?: unknown; error?: string };
|
|
28
|
-
if (!res.ok) throw new Error(json.error ?? `HTTP ${res.status}`);
|
|
29
|
-
return json.data ?? json;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
// Skills installation
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
|
|
36
|
-
const AGENT_DIRS: Record<string, string> = {
|
|
37
|
-
claude: path.join(os.homedir(), '.claude', 'skills'),
|
|
38
|
-
gemini: path.join(os.homedir(), '.gemini', 'skills'),
|
|
39
|
-
cursor: path.join(os.homedir(), '.cursor', 'rules'),
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const SKILLS_CANONICAL = path.join(os.homedir(), '.agents', 'skills', 'myapi');
|
|
43
|
-
|
|
44
|
-
// Skills are bundled inside the npm package at build time from skills/*/SKILL.md
|
|
45
|
-
const BUNDLED_SKILLS_DIR = path.join(
|
|
46
|
-
path.dirname(new URL(import.meta.url).pathname),
|
|
47
|
-
'..',
|
|
48
|
-
'skills',
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
export async function installSkills(): Promise<void> {
|
|
52
|
-
if (!fs.existsSync(BUNDLED_SKILLS_DIR)) {
|
|
53
|
-
info('No bundled skills found — skipping skills install.');
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Each bundled file is <skill-name>.md — install as <skill-name>/SKILL.md
|
|
58
|
-
const files = fs.readdirSync(BUNDLED_SKILLS_DIR).filter(f => f.endsWith('.md'));
|
|
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'));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Symlink each skill directory into agent dirs
|
|
69
|
-
for (const [agent, dir] of Object.entries(AGENT_DIRS)) {
|
|
70
|
-
try {
|
|
71
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
72
|
-
for (const skill of skills) {
|
|
73
|
-
const link = path.join(dir, skill);
|
|
74
|
-
const target = path.join(SKILLS_CANONICAL, skill);
|
|
75
|
-
try {
|
|
76
|
-
try { fs.rmSync(link, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
77
|
-
fs.symlinkSync(target, link);
|
|
78
|
-
} catch {
|
|
79
|
-
// Non-fatal.
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
info(` ✓ ${agent}`);
|
|
83
|
-
} catch {
|
|
84
|
-
// Silently skip agents whose directory can't be created.
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
|
-
// Registered flow
|
|
91
|
-
// ---------------------------------------------------------------------------
|
|
92
|
-
|
|
93
|
-
async function registeredFlow(rl: readline.Interface): Promise<{
|
|
94
|
-
api_key: string;
|
|
95
|
-
account_id: string;
|
|
96
|
-
default_org: string;
|
|
97
|
-
default_funnel: string;
|
|
98
|
-
email: string;
|
|
99
|
-
}> {
|
|
100
|
-
const email = (await ask(rl, '› Email? ')).trim();
|
|
101
|
-
|
|
102
|
-
await post('/hq/account/send-code', { email });
|
|
103
|
-
info(`› Sent a code to ${email} · paste it below`);
|
|
104
|
-
|
|
105
|
-
const code = (await ask(rl, '› Code? ')).trim();
|
|
106
|
-
const data = await post('/hq/account/verify-code', { email, code }) as {
|
|
107
|
-
api_key: string;
|
|
108
|
-
account_id: string;
|
|
109
|
-
default_org: string;
|
|
110
|
-
default_funnel: string;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
return { ...data, email };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ---------------------------------------------------------------------------
|
|
117
|
-
// Anonymous flow
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
|
|
120
|
-
async function anonymousFlow(): Promise<{
|
|
121
|
-
api_key: string;
|
|
122
|
-
account_id: string;
|
|
123
|
-
default_org: string;
|
|
124
|
-
default_funnel: string;
|
|
125
|
-
subdomain_url: string;
|
|
126
|
-
}> {
|
|
127
|
-
const data = await post('/hq/account/anonymous', {}) as {
|
|
128
|
-
api_key: string;
|
|
129
|
-
account_id: string;
|
|
130
|
-
default_org: string;
|
|
131
|
-
default_funnel: string;
|
|
132
|
-
subdomain_url: string;
|
|
133
|
-
};
|
|
134
|
-
return data;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// ---------------------------------------------------------------------------
|
|
138
|
-
// Main setup command
|
|
139
|
-
// ---------------------------------------------------------------------------
|
|
140
|
-
|
|
141
|
-
// myapi auth import-key <key> — non-interactively import a raw API key.
|
|
142
|
-
export async function importKey(apiKey: string, flags: Record<string, string | boolean>) {
|
|
143
|
-
if (!apiKey) {
|
|
144
|
-
info('Usage: myapi auth import-key <api_key> [--install-skills] [--no-skills]');
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const auth = { Authorization: `Bearer ${apiKey}` };
|
|
148
|
-
let accountId = '';
|
|
149
|
-
let email: string | undefined;
|
|
150
|
-
let defaultOrg = '';
|
|
151
|
-
let defaultFunnel = '';
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
const meRes = await fetch(`${API_BASE}/hq/account/me`, { headers: auth });
|
|
155
|
-
const meJson = await meRes.json() as any;
|
|
156
|
-
if (!meRes.ok) throw new Error(meJson.error ?? `HTTP ${meRes.status}`);
|
|
157
|
-
const me = meJson.data ?? meJson;
|
|
158
|
-
accountId = me.account_id ?? '';
|
|
159
|
-
email = me.email || undefined;
|
|
160
|
-
} catch (e: any) {
|
|
161
|
-
throw new Error(`Could not verify API key: ${e.message}`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Fetch org/funnel defaults.
|
|
165
|
-
try {
|
|
166
|
-
const orgRes = await fetch(`${API_BASE}/hq/orgs`, { headers: auth });
|
|
167
|
-
if (orgRes.ok) {
|
|
168
|
-
const orgs = ((await orgRes.json() as any)?.data ?? []);
|
|
169
|
-
if (orgs.length > 0) {
|
|
170
|
-
defaultOrg = orgs[orgs.length - 1].id;
|
|
171
|
-
const fRes = await fetch(`${API_BASE}/funnel/orgs/${defaultOrg}/funnels`, { headers: auth });
|
|
172
|
-
if (fRes.ok) {
|
|
173
|
-
const funnels = ((await fRes.json() as any)?.data ?? []);
|
|
174
|
-
if (funnels.length > 0) defaultFunnel = funnels[funnels.length - 1].id;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
} catch { /* non-fatal */ }
|
|
179
|
-
|
|
180
|
-
const wantsSkills = flags['install-skills'] ? true : flags['no-skills'] ? false : true;
|
|
181
|
-
|
|
182
|
-
addAccount({ api_key: apiKey, account_id: accountId, email, default_org: defaultOrg, default_funnel: defaultFunnel, skills_installed: wantsSkills });
|
|
183
|
-
success(`› ✓ Key imported${email ? ` · ${email}` : ''}`);
|
|
184
|
-
if (wantsSkills) await installSkills();
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export async function setup(flags: Record<string, string | boolean> = {}) {
|
|
188
|
-
if (flags.help) {
|
|
189
|
-
info('Usage: myapi auth setup [--anonymous] [--yes] [--install-skills|--no-skills]\n\nConfigures your account and stores your default org and funnel so you don\'t need to pass --org or --funnel on every command.\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');
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
info('› Configuring MyAPI…');
|
|
194
|
-
|
|
195
|
-
const existing = loadConfig();
|
|
196
|
-
|
|
197
|
-
// Already configured — ask before adding a new account.
|
|
198
|
-
if (existing?.api_key && !flags.yes) {
|
|
199
|
-
const full = loadFullConfig();
|
|
200
|
-
const total = full?.accounts.length ?? 1;
|
|
201
|
-
const activeLabel = existing.email ?? existing.account_id;
|
|
202
|
-
const activeType = existing.is_anonymous ? 'anonymous' : 'registered';
|
|
203
|
-
const othersNote = total > 1 ? ` · ${total - 1} more saved` : '';
|
|
204
|
-
|
|
205
|
-
info(`› Connected: ${activeLabel} (${activeType})${othersNote}`);
|
|
206
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
207
|
-
const add = await ask(rl, '› Connect a new account? (y/N) ');
|
|
208
|
-
if (!yn(add, false)) {
|
|
209
|
-
if (!existing.skills_installed) {
|
|
210
|
-
const ans = await ask(rl, '› Install the MyAPI skills pack? (Y/n) ');
|
|
211
|
-
rl.close();
|
|
212
|
-
if (yn(ans)) {
|
|
213
|
-
await installSkills();
|
|
214
|
-
success('› Skills installed.');
|
|
215
|
-
saveConfig({ ...existing, skills_installed: true });
|
|
216
|
-
}
|
|
217
|
-
} else {
|
|
218
|
-
rl.close();
|
|
219
|
-
}
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
rl.close();
|
|
223
|
-
// Fall through to setup flow — new account will be added to the list.
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
227
|
-
|
|
228
|
-
let apiKey = '';
|
|
229
|
-
let accountId = '';
|
|
230
|
-
let defaultOrg = '';
|
|
231
|
-
let defaultFunnel = '';
|
|
232
|
-
let subdomainUrl = '';
|
|
233
|
-
let isAnonymous = false;
|
|
234
|
-
let email = '';
|
|
235
|
-
|
|
236
|
-
// Determine skills preference from flags before any prompts.
|
|
237
|
-
const skillsFromFlag = flags['install-skills'] ? true : flags['no-skills'] ? false : null;
|
|
238
|
-
|
|
239
|
-
try {
|
|
240
|
-
const useAnon = flags.anonymous || flags.anon;
|
|
241
|
-
const createAns = useAnon ? 'n' : await ask(rl, '› Register with email? (Y/n, or N to continue anonymously) ');
|
|
242
|
-
|
|
243
|
-
if (yn(createAns)) {
|
|
244
|
-
const data = await registeredFlow(rl);
|
|
245
|
-
apiKey = data.api_key;
|
|
246
|
-
accountId = data.account_id;
|
|
247
|
-
defaultOrg = data.default_org;
|
|
248
|
-
defaultFunnel = data.default_funnel;
|
|
249
|
-
email = data.email;
|
|
250
|
-
} else {
|
|
251
|
-
info('› Continuing as anonymous — sites ship to *.makeautonomous.com');
|
|
252
|
-
const data = await anonymousFlow();
|
|
253
|
-
apiKey = data.api_key;
|
|
254
|
-
accountId = data.account_id;
|
|
255
|
-
defaultOrg = data.default_org;
|
|
256
|
-
defaultFunnel = data.default_funnel;
|
|
257
|
-
subdomainUrl = data.subdomain_url;
|
|
258
|
-
isAnonymous = true;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const wantsSkills = skillsFromFlag !== null ? skillsFromFlag : flags.yes ? true : yn(await ask(rl, '› Install the MyAPI skills pack? (Y/n) '));
|
|
262
|
-
|
|
263
|
-
addAccount({
|
|
264
|
-
api_key: apiKey,
|
|
265
|
-
account_id: accountId,
|
|
266
|
-
email: email || undefined,
|
|
267
|
-
default_org: defaultOrg,
|
|
268
|
-
default_funnel: defaultFunnel,
|
|
269
|
-
is_anonymous: isAnonymous,
|
|
270
|
-
skills_installed: wantsSkills,
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
success(`› ✓ saved to ~/.myapi/config.json`);
|
|
274
|
-
|
|
275
|
-
if (wantsSkills) {
|
|
276
|
-
await installSkills();
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Validate key and ensure org/funnel defaults are correct.
|
|
280
|
-
try {
|
|
281
|
-
const auth = { Authorization: `Bearer ${apiKey}` };
|
|
282
|
-
const meRes = await fetch(`${API_BASE}/hq/account/me`, { headers: auth });
|
|
283
|
-
if (!meRes.ok) {
|
|
284
|
-
info('› Warning: could not verify API key — check your connection.');
|
|
285
|
-
} else {
|
|
286
|
-
// Verify default org exists; if not, fetch the most recent one.
|
|
287
|
-
let resolvedOrg = defaultOrg;
|
|
288
|
-
let resolvedFunnel = defaultFunnel;
|
|
289
|
-
|
|
290
|
-
const orgRes = await fetch(`${API_BASE}/hq/orgs`, { headers: auth });
|
|
291
|
-
if (orgRes.ok) {
|
|
292
|
-
const orgs = (await orgRes.json() as any)?.data ?? [];
|
|
293
|
-
if (orgs.length > 0) {
|
|
294
|
-
const latest = orgs[orgs.length - 1];
|
|
295
|
-
if (!resolvedOrg || !orgs.find((o: any) => o.id === resolvedOrg)) {
|
|
296
|
-
resolvedOrg = latest.id;
|
|
297
|
-
info(`› Auto-selected org: ${latest.name} (${resolvedOrg})`);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Verify default funnel exists within the org.
|
|
301
|
-
const fRes = await fetch(`${API_BASE}/funnel/orgs/${resolvedOrg}/funnels`, { headers: auth });
|
|
302
|
-
if (fRes.ok) {
|
|
303
|
-
const funnels = (await fRes.json() as any)?.data ?? [];
|
|
304
|
-
if (funnels.length > 0 && (!resolvedFunnel || !funnels.find((f: any) => f.id === resolvedFunnel))) {
|
|
305
|
-
resolvedFunnel = funnels[funnels.length - 1].id;
|
|
306
|
-
info(`› Auto-selected funnel: ${resolvedFunnel}`);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (resolvedOrg !== defaultOrg || resolvedFunnel !== defaultFunnel) {
|
|
311
|
-
const full = loadFullConfig()!;
|
|
312
|
-
const idx = full.active;
|
|
313
|
-
full.accounts[idx].default_org = resolvedOrg;
|
|
314
|
-
full.accounts[idx].default_funnel = resolvedFunnel;
|
|
315
|
-
fs.writeFileSync(path.join(os.homedir(), '.myapi', 'config.json'), JSON.stringify(full, null, 2), { mode: 0o600 });
|
|
316
|
-
defaultOrg = resolvedOrg;
|
|
317
|
-
defaultFunnel = resolvedFunnel;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
} catch { /* non-fatal */ }
|
|
323
|
-
|
|
324
|
-
if (isAnonymous) {
|
|
325
|
-
success('› Setup complete. No card, no email, ready to ship.');
|
|
326
|
-
if (subdomainUrl) info(`› Your funnel: ${subdomainUrl}`);
|
|
327
|
-
info('› Upgrade anytime — myapi auth link');
|
|
328
|
-
} else {
|
|
329
|
-
success(`› Setup complete. Org ${defaultOrg} ready · $5.00 free credits added.`);
|
|
330
|
-
}
|
|
331
|
-
} finally {
|
|
332
|
-
rl.close();
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
package/src/commands/storage.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { storage as sdkStorage } from '@myapihq/sdk';
|
|
2
|
-
import { requireConfig } from '../config.js';
|
|
3
|
-
import { success, error, printTable, info, printJson } from '../output.js';
|
|
4
|
-
|
|
5
|
-
export async function list(flags: Record<string, string | boolean>) {
|
|
6
|
-
const config = requireConfig();
|
|
7
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
8
|
-
if (!orgId) error("Missing required arguments.\nUsage: myapi storage list --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
9
|
-
|
|
10
|
-
const assets = await sdkStorage.listAssets(config.api_key, orgId);
|
|
11
|
-
if (flags.json) printJson(assets);
|
|
12
|
-
else printTable(assets as unknown as Record<string, unknown>[]);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function ingest(url: string, flags: Record<string, string | boolean>) {
|
|
16
|
-
const config = requireConfig();
|
|
17
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
18
|
-
|
|
19
|
-
if (!orgId || !url) {
|
|
20
|
-
error("Missing required arguments.\nUsage: myapi storage ingest <url> [--name <name>] --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const res = await sdkStorage.ingestAsset(config.api_key, orgId, url, flags.name as string);
|
|
24
|
-
success(`Asset ingested! ID: ${res.asset_id}\nHosted URL: ${res.url}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function del(id: string, flags: Record<string, string | boolean>) {
|
|
28
|
-
const config = requireConfig();
|
|
29
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
30
|
-
|
|
31
|
-
if (!orgId || !id) {
|
|
32
|
-
error("Missing required arguments.\nUsage: myapi storage delete <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
await sdkStorage.deleteAsset(config.api_key, orgId, id);
|
|
36
|
-
success(`Asset ${id} deleted`);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
|
|
40
|
-
if (!subcommand || (flags.help && !subcommand)) {
|
|
41
|
-
info('Usage: myapi storage <subcommand>\n\nSubcommands:\n list List all your uploaded assets\n ingest Ingest a public image URL into your edge storage\n delete Delete a stored asset\n\nNote: All storage commands require the --org <id> flag.');
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (flags.help) {
|
|
46
|
-
if (subcommand === 'list') info('Usage: myapi storage list --org <id> [--json]');
|
|
47
|
-
else if (subcommand === 'ingest') info('Usage: myapi storage ingest <url> [--name <name>] --org <id>\n\nDownloads a public image (JPEG/PNG) and permanently hosts it on your MyAPI storage. Returns the new URL.');
|
|
48
|
-
else if (subcommand === 'delete') info('Usage: myapi storage delete <asset_id> --org <id>');
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (subcommand === 'list') await list(flags);
|
|
53
|
-
else if (subcommand === 'ingest') await ingest(args[0], flags);
|
|
54
|
-
else if (subcommand === 'delete') await del(args[0], flags);
|
|
55
|
-
else error(`Unknown subcommand: ${subcommand}. Run "myapi storage --help" for a list of valid subcommands.`);
|
|
56
|
-
}
|
package/src/commands/update.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
import { loadConfig } from '../config.js';
|
|
3
|
-
import { info, success } from '../output.js';
|
|
4
|
-
import { installSkills } from './setup.js';
|
|
5
|
-
|
|
6
|
-
const REGISTRY_URL = 'https://registry.npmjs.org/@myapihq/cli/latest';
|
|
7
|
-
|
|
8
|
-
// Use the npm binary next to the active node — critical for nvm users.
|
|
9
|
-
function npmBin(): string {
|
|
10
|
-
return process.execPath.replace(/[/\\]node$/, '/npm');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// checkForUpdate runs silently in the background on every command.
|
|
14
|
-
// Auto-installs if a newer version is available.
|
|
15
|
-
export async function checkForUpdate(currentVersion: string): Promise<void> {
|
|
16
|
-
try {
|
|
17
|
-
const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(3000) });
|
|
18
|
-
if (!res.ok) return;
|
|
19
|
-
const data = await res.json() as { version?: string };
|
|
20
|
-
const latest = data.version;
|
|
21
|
-
|
|
22
|
-
if (latest && isNewer(latest, currentVersion)) {
|
|
23
|
-
info(`\n› New version available (${currentVersion} → ${latest}) — installing…`);
|
|
24
|
-
try {
|
|
25
|
-
// Pin the exact version so npm can't resolve to a cached older one.
|
|
26
|
-
execSync(`"${npmBin()}" install -g @myapihq/cli@${latest}`, { stdio: 'pipe' });
|
|
27
|
-
const config = loadConfig();
|
|
28
|
-
if (config?.skills_installed) {
|
|
29
|
-
await installSkills();
|
|
30
|
-
}
|
|
31
|
-
info(`› Updated to ${latest} — active after this command completes.\n`);
|
|
32
|
-
} catch (installErr: any) {
|
|
33
|
-
const msg = installErr?.stderr?.toString?.() || installErr?.message || String(installErr);
|
|
34
|
-
info(`› Auto-update failed: ${msg.trim()}`);
|
|
35
|
-
info(`› Run manually: npm install -g @myapihq/cli@${latest}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
} catch {
|
|
39
|
-
// Network errors are silently ignored.
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// myapi update — explicit update, same logic as auto-update.
|
|
44
|
-
export async function update(flags: Record<string, string | boolean> = {}): Promise<void> {
|
|
45
|
-
if (flags.help) {
|
|
46
|
-
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\nNote: version pinning and rollback are not supported — always installs the latest.\nThe CLI also auto-updates silently on each command when a newer version is detected.');
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
info('› Checking for updates…');
|
|
50
|
-
let latest = '';
|
|
51
|
-
try {
|
|
52
|
-
const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(5000) });
|
|
53
|
-
if (!res.ok) throw new Error(`registry returned ${res.status}`);
|
|
54
|
-
const data = await res.json() as { version?: string };
|
|
55
|
-
latest = data.version ?? '';
|
|
56
|
-
if (latest) info(`› Installing @myapihq/cli@${latest}…`);
|
|
57
|
-
} catch { /* proceed anyway */ }
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const pkg = latest ? `@myapihq/cli@${latest}` : '@myapihq/cli@latest';
|
|
61
|
-
execSync(`"${npmBin()}" install -g ${pkg}`, { stdio: 'inherit' });
|
|
62
|
-
} catch {
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
info('› Refreshing skills…');
|
|
66
|
-
await installSkills();
|
|
67
|
-
success('› Up to date.');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// latestVersion fetches the current published version for --version display.
|
|
71
|
-
export async function latestVersion(): Promise<string | null> {
|
|
72
|
-
try {
|
|
73
|
-
const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(3000) });
|
|
74
|
-
if (!res.ok) return null;
|
|
75
|
-
const data = await res.json() as { version?: string };
|
|
76
|
-
return data.version ?? null;
|
|
77
|
-
} catch {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function isNewer(latest: string, current: string): boolean {
|
|
83
|
-
const toNum = (v: string) => v.split('.').map(Number);
|
|
84
|
-
const [lMaj, lMin, lPat] = toNum(latest);
|
|
85
|
-
const [cMaj, cMin, cPat] = toNum(current);
|
|
86
|
-
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
87
|
-
if (lMin !== cMin) return lMin > cMin;
|
|
88
|
-
return lPat > cPat;
|
|
89
|
-
}
|
package/src/commands/url.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { url as sdkUrl } from '@myapihq/sdk';
|
|
2
|
-
import { requireConfig } from '../config.js';
|
|
3
|
-
import { success, error, info, printJson } from '../output.js';
|
|
4
|
-
|
|
5
|
-
export async function shorten(targetUrl: string, flags: Record<string, string | boolean>) {
|
|
6
|
-
const config = requireConfig();
|
|
7
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
8
|
-
if (!orgId || !targetUrl) error("Missing required arguments.\nUsage: myapi url shorten <url> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
9
|
-
|
|
10
|
-
const res = await sdkUrl.shortenUrl(config.api_key, orgId, targetUrl);
|
|
11
|
-
if (flags.json) printJson(res);
|
|
12
|
-
else success(`Shortened URL: ${res.short_url}\nCode: ${res.short_code}`);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
|
|
16
|
-
if (!subcommand || (flags.help && !subcommand)) {
|
|
17
|
-
info('Usage: myapi url <subcommand>\n\nSubcommands:\n shorten Shorten a long URL\n\nNote: All url commands require the --org <id> flag.');
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (flags.help) {
|
|
22
|
-
if (subcommand === 'shorten') info('Usage: myapi url shorten <url> --org <id> [--json]\n\nShortens a long URL and returns the compact myurlto.com link.');
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (subcommand === 'shorten') await shorten(args[0], flags);
|
|
27
|
-
else error(`Unknown subcommand: ${subcommand}. Run "myapi url --help" for a list of valid subcommands.`);
|
|
28
|
-
}
|
package/src/commands/webhook.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { webhook as sdkWebhook } from '@myapihq/sdk';
|
|
2
|
-
import { requireConfig } from '../config.js';
|
|
3
|
-
import { success, error, printTable, info, printJson } from '../output.js';
|
|
4
|
-
|
|
5
|
-
export async function list(flags: Record<string, string | boolean>) {
|
|
6
|
-
const config = requireConfig();
|
|
7
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
8
|
-
if (!orgId) error("Missing required arguments.\nUsage: myapi webhook list --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
9
|
-
|
|
10
|
-
const endpoints = await sdkWebhook.listEndpoints(config.api_key, orgId);
|
|
11
|
-
if (flags.json) printJson(endpoints);
|
|
12
|
-
else printTable(endpoints as unknown as Record<string, unknown>[]);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function create(flags: Record<string, string | boolean>) {
|
|
16
|
-
const config = requireConfig();
|
|
17
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
18
|
-
const name = flags.name as string;
|
|
19
|
-
const description = flags.description as string;
|
|
20
|
-
|
|
21
|
-
if (!orgId || !name) {
|
|
22
|
-
error("Missing required arguments.\nUsage: myapi webhook create --name <name> [--description <desc>] --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const res = await sdkWebhook.createEndpoint(config.api_key, orgId, name, description);
|
|
26
|
-
success(`Webhook created! ID: ${res.id}\nInbound URL: ${res.inbound_url}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function del(id: string, flags: Record<string, string | boolean>) {
|
|
30
|
-
const config = requireConfig();
|
|
31
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
32
|
-
if (!orgId || !id) error("Missing required arguments.\nUsage: myapi webhook delete <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
33
|
-
|
|
34
|
-
await sdkWebhook.deleteEndpoint(config.api_key, orgId, id);
|
|
35
|
-
success(`Webhook ${id} deleted`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export async function delivery(id: string, flags: Record<string, string | boolean>) {
|
|
39
|
-
const config = requireConfig();
|
|
40
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
41
|
-
if (!orgId || !id) error("Missing required arguments.\nUsage: myapi webhook delivery <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
42
|
-
|
|
43
|
-
const res = await sdkWebhook.getDelivery(config.api_key, orgId, id);
|
|
44
|
-
printJson(res);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
|
|
48
|
-
if (!subcommand || (flags.help && !subcommand)) {
|
|
49
|
-
info('Usage: myapi webhook <subcommand>\n\nSubcommands:\n list List all inbound webhook endpoints\n create Create a new endpoint to receive data (returns an inbound URL)\n delete Delete a webhook endpoint\n delivery Inspect a specific webhook delivery (headers, payload, status)\n\nNote: All webhook commands require the --org <id> flag.');
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (flags.help) {
|
|
54
|
-
if (subcommand === 'list') info('Usage: myapi webhook list --org <id> [--json]\n\nLists all webhook endpoints for the organization, including their IDs and inbound slugs.');
|
|
55
|
-
else if (subcommand === 'create') info('Usage: myapi webhook create --name <name> [--description <desc>] --org <id>\n\nCreates a new inbound webhook endpoint. Returns the unique inbound URL to give to third parties.');
|
|
56
|
-
else if (subcommand === 'delete') info('Usage: myapi webhook delete <id> --org <id>\n\nPermanently deletes the specified webhook endpoint.');
|
|
57
|
-
else if (subcommand === 'delivery') info('Usage: myapi webhook delivery <delivery_id> --org <id>\n\nRetrieves the exact HTTP headers, JSON payload, and processing status of a specific received webhook. Useful for debugging inbound data.');
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (subcommand === 'list') await list(flags);
|
|
62
|
-
else if (subcommand === 'create') await create(flags);
|
|
63
|
-
else if (subcommand === 'delete') await del(args[0], flags);
|
|
64
|
-
else if (subcommand === 'delivery') await delivery(args[0], flags);
|
|
65
|
-
else error(`Unknown subcommand: ${subcommand}. Run "myapi webhook --help" for a list of valid subcommands.`);
|
|
66
|
-
}
|
package/src/commands/workflow.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { workflow as sdkWorkflow } from '@myapihq/sdk';
|
|
2
|
-
import { requireConfig } from '../config.js';
|
|
3
|
-
import { success, error, printTable, info, printJson } from '../output.js';
|
|
4
|
-
|
|
5
|
-
export async function list(flags: Record<string, string | boolean>) {
|
|
6
|
-
const config = requireConfig();
|
|
7
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
8
|
-
if (!orgId) error("Missing required arguments.\nUsage: myapi workflow list --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
9
|
-
|
|
10
|
-
const workflows = await sdkWorkflow.listWorkflows(config.api_key, orgId);
|
|
11
|
-
if (flags.json) printJson(workflows);
|
|
12
|
-
else printTable(workflows as unknown as Record<string, unknown>[]);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function create(flags: Record<string, string | boolean>) {
|
|
16
|
-
const config = requireConfig();
|
|
17
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
18
|
-
const name = flags.name as string;
|
|
19
|
-
const endpointId = flags['endpoint-id'] as string;
|
|
20
|
-
|
|
21
|
-
if (!orgId || !name || !endpointId || !flags.steps) {
|
|
22
|
-
error("Missing required arguments.\nUsage: myapi workflow create --name <name> --endpoint-id <id> --steps <json> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let steps;
|
|
26
|
-
try {
|
|
27
|
-
steps = JSON.parse(flags.steps as string);
|
|
28
|
-
} catch (err) {
|
|
29
|
-
error("Invalid JSON for --steps");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const wf = await sdkWorkflow.createWorkflow(config.api_key, orgId, {
|
|
33
|
-
name,
|
|
34
|
-
trigger_config: { endpoint_id: endpointId },
|
|
35
|
-
steps
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
await sdkWorkflow.enableWorkflow(config.api_key, orgId, wf.id);
|
|
39
|
-
success(`Workflow created and enabled! ID: ${wf.id}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function enable(id: string, flags: Record<string, string | boolean>) {
|
|
43
|
-
const config = requireConfig();
|
|
44
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
45
|
-
if (!orgId || !id) error("Missing required arguments.\nUsage: myapi workflow enable <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
46
|
-
|
|
47
|
-
await sdkWorkflow.enableWorkflow(config.api_key, orgId, id);
|
|
48
|
-
success(`Workflow ${id} enabled`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export async function disable(id: string, flags: Record<string, string | boolean>) {
|
|
52
|
-
const config = requireConfig();
|
|
53
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
54
|
-
if (!orgId || !id) error("Missing required arguments.\nUsage: myapi workflow disable <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
55
|
-
|
|
56
|
-
await sdkWorkflow.disableWorkflow(config.api_key, orgId, id);
|
|
57
|
-
success(`Disabled workflow ${id}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export async function del(id: string, flags: Record<string, string | boolean>) {
|
|
61
|
-
const config = requireConfig();
|
|
62
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
63
|
-
if (!orgId || !id) error("Missing required arguments.\nUsage: myapi workflow delete <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
64
|
-
|
|
65
|
-
await sdkWorkflow.deleteWorkflow(config.api_key, orgId, id);
|
|
66
|
-
success(`Deleted workflow ${id}`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export async function runs(id: string, flags: Record<string, string | boolean>) {
|
|
70
|
-
const config = requireConfig();
|
|
71
|
-
const orgId = (flags.org as string) || config.default_org;
|
|
72
|
-
if (!orgId || !id) error("Missing required arguments.\nUsage: myapi workflow runs <id> --org <id>\n(Or set defaults via: myapi config set-org <id>)");
|
|
73
|
-
|
|
74
|
-
const wRuns = await sdkWorkflow.listWorkflowRuns(config.api_key, orgId, id);
|
|
75
|
-
if (flags.json) printJson(wRuns);
|
|
76
|
-
else printTable(wRuns as unknown as Record<string, unknown>[]);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export async function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>) {
|
|
80
|
-
if (!subcommand || (flags.help && !subcommand)) {
|
|
81
|
-
info('Usage: myapi workflow <subcommand>\n\nSubcommands:\n list List all workflows\n create Create and enable a new workflow\n enable Enable a workflow\n disable Disable a workflow\n delete Delete a workflow\n runs List recent executions (runs) of a workflow\n\nNote: All workflow commands require the --org <id> flag.');
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (flags.help) {
|
|
86
|
-
if (subcommand === 'list') info('Usage: myapi workflow list --org <id> [--json]\n\nLists all workflows in your organization, showing their ID, name, status, and attached webhook endpoint.');
|
|
87
|
-
else if (subcommand === 'create') info('Usage: myapi workflow create --name <name> --endpoint-id <id> --steps <json_array> --org <id>\n\nCreates a new webhook-triggered workflow and immediately enables it.\n\nArguments:\n --name A friendly name for your workflow.\n --endpoint-id The UUID of the my-webhook-api endpoint that will trigger this workflow.\n --steps A raw JSON array defining the actions to take when triggered.\n\nExample Steps Payload:\n \'[{"type": "send_email", "from": "hello@example.com", "to": "{{payload.user.email}}", "subject": "Welcome!", "template_id": "abc-123"}]\n \'[{"type": "slack_message", "webhook_url": "https://hooks.slack.com/...", "text": "New lead: {{payload.name}}"}]\'');
|
|
88
|
-
else if (subcommand === 'enable') info('Usage: myapi workflow enable <id> --org <id>\n\nActivates a disabled workflow so it begins listening to its webhook trigger again.');
|
|
89
|
-
else if (subcommand === 'disable') info('Usage: myapi workflow disable <id> --org <id>\n\nPauses a workflow. The attached webhook will still accept data, but the workflow steps will not execute.');
|
|
90
|
-
else if (subcommand === 'delete') info('Usage: myapi workflow delete <id> --org <id>\n\nDeletes the workflow permanently.');
|
|
91
|
-
else if (subcommand === 'runs') info('Usage: myapi workflow runs <workflow_id> --org <id> [--json]\n\nLists the 100 most recent executions of the specified workflow, including completion status and any error messages.');
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (subcommand === 'list') await list(flags);
|
|
96
|
-
else if (subcommand === 'create') await create(flags);
|
|
97
|
-
else if (subcommand === 'enable') await enable(args[0], flags);
|
|
98
|
-
else if (subcommand === 'disable') await disable(args[0], flags);
|
|
99
|
-
else if (subcommand === 'delete') await del(args[0], flags);
|
|
100
|
-
else if (subcommand === 'runs') await runs(args[0], flags);
|
|
101
|
-
else error(`Unknown subcommand: ${subcommand}. Run "myapi workflow --help" for a list of valid subcommands.`);
|
|
102
|
-
}
|