@myapihq/cli 1.0.84 → 1.1.0-wip.1
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 +8 -3
- package/dist/commands/auth.js +84 -60
- package/dist/commands/billing.d.ts +8 -4
- package/dist/commands/billing.js +46 -27
- package/dist/commands/config.d.ts +8 -5
- package/dist/commands/config.js +52 -27
- package/dist/commands/domain.d.ts +12 -9
- package/dist/commands/domain.js +124 -86
- package/dist/commands/email/campaign.d.ts +2 -0
- package/dist/commands/email/campaign.js +152 -0
- package/dist/commands/email/index.d.ts +4 -0
- package/dist/commands/email/index.js +98 -0
- package/dist/commands/email/mailbox.d.ts +2 -0
- package/dist/commands/email/mailbox.js +88 -0
- package/dist/commands/email/message.d.ts +2 -0
- package/dist/commands/email/message.js +115 -0
- package/dist/commands/email/template.d.ts +2 -0
- package/dist/commands/email/template.js +106 -0
- package/dist/commands/email/warmup.d.ts +2 -0
- package/dist/commands/email/warmup.js +43 -0
- package/dist/commands/email.d.ts +4 -12
- package/dist/commands/email.js +528 -146
- package/dist/commands/funnel.d.ts +10 -7
- package/dist/commands/funnel.js +79 -55
- package/dist/commands/image.js +25 -15
- package/dist/commands/keys.d.ts +8 -3
- package/dist/commands/keys.js +71 -35
- package/dist/commands/org.d.ts +9 -5
- package/dist/commands/org.js +100 -64
- package/dist/commands/pixel.js +23 -11
- package/dist/commands/setup.d.ts +3 -2
- package/dist/commands/setup.js +160 -165
- package/dist/commands/storage.js +25 -15
- package/dist/commands/update.d.ts +2 -1
- package/dist/commands/update.js +5 -0
- package/dist/commands/url.js +19 -7
- package/dist/commands/webhook.d.ts +8 -5
- package/dist/commands/webhook.js +70 -38
- package/dist/commands/workflow.d.ts +13 -7
- package/dist/commands/workflow.js +179 -58
- package/dist/config.js +10 -5
- package/dist/flags.d.ts +8 -0
- package/dist/flags.js +88 -0
- package/dist/flags.test.d.ts +1 -0
- package/dist/flags.test.js +73 -0
- package/dist/helpers.d.ts +6 -0
- package/dist/helpers.js +31 -0
- package/dist/index.js +98 -109
- package/dist/output.d.ts +12 -1
- package/dist/output.js +16 -6
- package/dist/prompt.d.ts +24 -0
- package/dist/prompt.js +41 -0
- package/dist/skills/my-email-api/README.md +45 -0
- package/dist/skills/my-email-api/SKILL.md +104 -0
- package/dist/skills/my-email-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-email-api/make/.gitkeep +0 -0
- package/dist/skills/my-email-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-email-api/openapi/.gitkeep +0 -0
- package/dist/skills/my-webhook-api/README.md +40 -0
- package/dist/skills/my-webhook-api/SKILL.md +138 -0
- package/dist/skills/my-webhook-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-webhook-api/make/.gitkeep +0 -0
- package/dist/skills/my-webhook-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-webhook-api/openapi/.gitkeep +0 -0
- package/dist/skills/my-workflow-api/README.md +36 -0
- package/dist/skills/my-workflow-api/SKILL.md +156 -0
- package/dist/skills/my-workflow-api/claude/.claude-plugin/plugin.json +6 -0
- package/dist/skills/my-workflow-api/make/.gitkeep +0 -0
- package/dist/skills/my-workflow-api/n8n/.gitkeep +0 -0
- package/dist/skills/my-workflow-api/openapi/.gitkeep +0 -0
- package/dist/utils.d.ts +26 -4
- package/dist/utils.js +32 -33
- package/dist/utils.test.d.ts +1 -0
- package/dist/utils.test.js +48 -0
- package/package.json +9 -4
- package/dist/commands/account.d.ts +0 -4
- package/dist/commands/account.js +0 -80
package/dist/commands/setup.js
CHANGED
|
@@ -1,30 +1,10 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as os from 'os';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import * as readline from 'readline';
|
|
5
4
|
import { loadConfig, saveConfig, addAccount, loadFullConfig } from '../config.js';
|
|
6
5
|
import { info, success } from '../output.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return new Promise(resolve => rl.question(q, resolve));
|
|
10
|
-
}
|
|
11
|
-
function yn(answer, defaultYes = true) {
|
|
12
|
-
const t = answer.trim().toLowerCase();
|
|
13
|
-
if (t === '')
|
|
14
|
-
return defaultYes;
|
|
15
|
-
return t === 'y' || t === 'yes';
|
|
16
|
-
}
|
|
17
|
-
async function post(path, body) {
|
|
18
|
-
const res = await fetch(`${API_BASE}${path}`, {
|
|
19
|
-
method: 'POST',
|
|
20
|
-
headers: { 'Content-Type': 'application/json' },
|
|
21
|
-
body: JSON.stringify(body),
|
|
22
|
-
});
|
|
23
|
-
const json = await res.json();
|
|
24
|
-
if (!res.ok)
|
|
25
|
-
throw new Error(json.error ?? `HTTP ${res.status}`);
|
|
26
|
-
return json.data ?? json;
|
|
27
|
-
}
|
|
6
|
+
import { ask, confirm } from '../prompt.js';
|
|
7
|
+
import { hq, funnel as sdkFunnel } from '@myapihq/sdk';
|
|
28
8
|
// ---------------------------------------------------------------------------
|
|
29
9
|
// Skills installation
|
|
30
10
|
// ---------------------------------------------------------------------------
|
|
@@ -55,96 +35,153 @@ export async function installSkills() {
|
|
|
55
35
|
fs.mkdirSync(skillDir, { recursive: true });
|
|
56
36
|
fs.copyFileSync(path.join(BUNDLED_SKILLS_DIR, skill, 'SKILL.md'), path.join(skillDir, 'SKILL.md'));
|
|
57
37
|
}
|
|
58
|
-
// Symlink each skill into agent config directories
|
|
38
|
+
// Symlink each skill into agent config directories. Surface failures so
|
|
39
|
+
// users can fix permission issues — skills that didn't install will not
|
|
40
|
+
// be available to the agent and "skills installed" would otherwise lie.
|
|
59
41
|
for (const [agent, dir] of Object.entries(AGENT_DIRS)) {
|
|
60
42
|
try {
|
|
61
43
|
fs.mkdirSync(dir, { recursive: true });
|
|
62
44
|
for (const skill of skills) {
|
|
63
45
|
const link = path.join(dir, skill);
|
|
64
46
|
const target = path.join(SKILLS_CANONICAL, skill);
|
|
47
|
+
// Only remove if it's already a symlink we own. Avoids clobbering a
|
|
48
|
+
// user-authored skill that happens to share the same `my-X-api/`
|
|
49
|
+
// name. ENOENT here just means "nothing to clean up".
|
|
65
50
|
try {
|
|
66
|
-
fs.
|
|
51
|
+
const stat = fs.lstatSync(link);
|
|
52
|
+
if (stat.isSymbolicLink())
|
|
53
|
+
fs.rmSync(link, { force: true });
|
|
67
54
|
}
|
|
68
|
-
catch { /*
|
|
55
|
+
catch { /* ENOENT: nothing there */ }
|
|
69
56
|
fs.symlinkSync(target, link);
|
|
70
57
|
}
|
|
71
58
|
info(` ✓ ${agent}`);
|
|
72
59
|
}
|
|
73
|
-
catch {
|
|
74
|
-
//
|
|
60
|
+
catch (err) {
|
|
61
|
+
// Skip agents not installed on this machine (ENOENT on parent dir is OK).
|
|
62
|
+
// Surface anything else so the user knows skills aren't fully installed.
|
|
63
|
+
const code = err?.code;
|
|
64
|
+
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
65
|
+
// Agent likely not installed — quietly skip.
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
info(` ✗ ${agent}: ${err.message || code} (${dir})`);
|
|
75
69
|
}
|
|
76
70
|
}
|
|
77
71
|
}
|
|
78
72
|
// ---------------------------------------------------------------------------
|
|
73
|
+
// Shared helpers
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Resolve the user's "install skills?" preference: explicit flag wins, else
|
|
76
|
+
// prompt (defaulting to yes). `--yes` short-circuits to true.
|
|
77
|
+
async function resolveSkillsPreference(flags) {
|
|
78
|
+
if (flags['install-skills'])
|
|
79
|
+
return true;
|
|
80
|
+
if (flags['no-skills'])
|
|
81
|
+
return false;
|
|
82
|
+
if (flags.yes)
|
|
83
|
+
return true;
|
|
84
|
+
return confirm('› Install the MyAPI skills pack? (Y/n) ', true);
|
|
85
|
+
}
|
|
86
|
+
// Resolve default org and funnel for a key by inspecting the account's orgs.
|
|
87
|
+
// Used by both `setup` and `import-key`. Picks the most recently-created
|
|
88
|
+
// org/funnel as the default; preserves the caller's hint if it still exists.
|
|
89
|
+
async function resolveDefaults(apiKey, hint = {}) {
|
|
90
|
+
let org = hint.org ?? '';
|
|
91
|
+
let funnel = hint.funnel ?? '';
|
|
92
|
+
try {
|
|
93
|
+
const orgs = await hq.listOrgs(apiKey);
|
|
94
|
+
if (orgs.length > 0) {
|
|
95
|
+
if (!org || !orgs.find(o => o.id === org)) {
|
|
96
|
+
org = orgs[orgs.length - 1].id;
|
|
97
|
+
}
|
|
98
|
+
const funnels = await sdkFunnel.listFunnels(apiKey, org);
|
|
99
|
+
if (funnels.length > 0 && (!funnel || !funnels.find((f) => f.id === funnel))) {
|
|
100
|
+
funnel = funnels[funnels.length - 1].id;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch { /* non-fatal */ }
|
|
105
|
+
return { org, funnel };
|
|
106
|
+
}
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
79
108
|
// Registered flow
|
|
80
109
|
// ---------------------------------------------------------------------------
|
|
81
|
-
async function registeredFlow(
|
|
82
|
-
const email =
|
|
83
|
-
await
|
|
110
|
+
async function registeredFlow() {
|
|
111
|
+
const email = await ask('› Email? ');
|
|
112
|
+
await hq.sendCode(email);
|
|
84
113
|
info(`› Sent a code to ${email} · paste it below`);
|
|
85
|
-
const code =
|
|
86
|
-
const data = await
|
|
114
|
+
const code = await ask('› Code? ');
|
|
115
|
+
const data = await hq.verifyCode(email, code);
|
|
87
116
|
return { ...data, email };
|
|
88
117
|
}
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
|
-
// Anonymous flow
|
|
91
|
-
// ---------------------------------------------------------------------------
|
|
92
118
|
async function anonymousFlow() {
|
|
93
|
-
|
|
94
|
-
return data;
|
|
119
|
+
return hq.createAnonymousAccount();
|
|
95
120
|
}
|
|
96
121
|
// ---------------------------------------------------------------------------
|
|
97
122
|
// Main setup command
|
|
98
123
|
// ---------------------------------------------------------------------------
|
|
124
|
+
const IMPORT_KEY_HELP = `Usage: myapi auth import-key <api_key> [--install-skills] [--no-skills]
|
|
125
|
+
|
|
126
|
+
Imports an existing API key non-interactively. Use this in CI, Docker, or any
|
|
127
|
+
environment where the interactive "myapi auth setup" flow is not practical.
|
|
128
|
+
|
|
129
|
+
The key is validated against the API before being saved. Your default org and
|
|
130
|
+
funnel are auto-detected from the account and written to the local config.
|
|
131
|
+
|
|
132
|
+
Flags:
|
|
133
|
+
--install-skills Also install the MyAPI skills pack for AI agents after importing
|
|
134
|
+
--no-skills Skip skills installation even if previously installed
|
|
135
|
+
|
|
136
|
+
Examples:
|
|
137
|
+
myapi auth import-key hq_live_xxxxxxxxxxxxxxxxxxxx
|
|
138
|
+
myapi auth import-key hq_live_xxxxxxxxxxxxxxxxxxxx --install-skills`;
|
|
139
|
+
const SETUP_HELP = `Usage: myapi auth setup [--anonymous] [--yes] [--install-skills|--no-skills]
|
|
140
|
+
|
|
141
|
+
Configures your account and stores your default org and funnel so you don't
|
|
142
|
+
need to pass --org or --funnel on every command.
|
|
143
|
+
|
|
144
|
+
Flags:
|
|
145
|
+
--anonymous Skip registration, create anonymous account
|
|
146
|
+
--yes Skip confirmation prompts
|
|
147
|
+
--install-skills Auto-install skills pack
|
|
148
|
+
--no-skills Skip skills installation`;
|
|
99
149
|
// myapi auth import-key <key> — non-interactively import a raw API key.
|
|
100
150
|
export async function importKey(apiKey, flags) {
|
|
101
151
|
if (flags.help || !apiKey) {
|
|
102
|
-
info(
|
|
152
|
+
info(IMPORT_KEY_HELP);
|
|
103
153
|
return;
|
|
104
154
|
}
|
|
105
|
-
const auth = { Authorization: `Bearer ${apiKey}` };
|
|
106
155
|
let accountId = '';
|
|
107
156
|
let email;
|
|
108
|
-
let defaultOrg = '';
|
|
109
|
-
let defaultFunnel = '';
|
|
110
157
|
try {
|
|
111
|
-
const
|
|
112
|
-
const meJson = await meRes.json();
|
|
113
|
-
if (!meRes.ok)
|
|
114
|
-
throw new Error(meJson.error ?? `HTTP ${meRes.status}`);
|
|
115
|
-
const me = meJson.data ?? meJson;
|
|
158
|
+
const me = await hq.getAccount(apiKey);
|
|
116
159
|
accountId = me.account_id ?? '';
|
|
117
160
|
email = me.email || undefined;
|
|
118
161
|
}
|
|
119
162
|
catch (e) {
|
|
120
163
|
throw new Error(`Could not verify API key: ${e.message}`);
|
|
121
164
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const fRes = await fetch(`${API_BASE}/funnel/orgs/${defaultOrg}/funnels`, { headers: auth });
|
|
130
|
-
if (fRes.ok) {
|
|
131
|
-
const funnels = ((await fRes.json())?.data ?? []);
|
|
132
|
-
if (funnels.length > 0)
|
|
133
|
-
defaultFunnel = funnels[funnels.length - 1].id;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
catch { /* non-fatal */ }
|
|
139
|
-
const wantsSkills = flags['install-skills'] ? true : flags['no-skills'] ? false : true;
|
|
140
|
-
addAccount({ api_key: apiKey, account_id: accountId, email, default_org: defaultOrg, default_funnel: defaultFunnel, skills_installed: wantsSkills });
|
|
165
|
+
const { org: defaultOrg, funnel: defaultFunnel } = await resolveDefaults(apiKey);
|
|
166
|
+
const wantsSkills = flags['no-skills'] ? false : true;
|
|
167
|
+
addAccount({
|
|
168
|
+
api_key: apiKey, account_id: accountId, email,
|
|
169
|
+
default_org: defaultOrg, default_funnel: defaultFunnel,
|
|
170
|
+
skills_installed: wantsSkills,
|
|
171
|
+
});
|
|
141
172
|
success(`› ✓ Key imported${email ? ` · ${email}` : ''}`);
|
|
142
173
|
if (wantsSkills)
|
|
143
174
|
await installSkills();
|
|
144
175
|
}
|
|
176
|
+
// Persist a new account + (optionally) install skills.
|
|
177
|
+
async function installAndPersistSkills(account, wantsSkills) {
|
|
178
|
+
addAccount({ ...account, skills_installed: wantsSkills });
|
|
179
|
+
if (wantsSkills)
|
|
180
|
+
await installSkills();
|
|
181
|
+
}
|
|
145
182
|
export async function setup(flags = {}) {
|
|
146
183
|
if (flags.help) {
|
|
147
|
-
info(
|
|
184
|
+
info(SETUP_HELP);
|
|
148
185
|
return;
|
|
149
186
|
}
|
|
150
187
|
info('› Configuring MyAPI…');
|
|
@@ -157,27 +194,20 @@ export async function setup(flags = {}) {
|
|
|
157
194
|
const activeType = existing.is_anonymous ? 'anonymous' : 'registered';
|
|
158
195
|
const othersNote = total > 1 ? ` · ${total - 1} more saved` : '';
|
|
159
196
|
info(`› Connected: ${activeLabel} (${activeType})${othersNote}`);
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
if (!yn(add, false)) {
|
|
197
|
+
const add = await confirm('› Connect a new account? (y/N) ', false);
|
|
198
|
+
if (!add) {
|
|
163
199
|
if (!existing.skills_installed) {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
if (yn(ans)) {
|
|
200
|
+
const wants = await resolveSkillsPreference(flags);
|
|
201
|
+
if (wants) {
|
|
167
202
|
await installSkills();
|
|
168
203
|
success('› Skills installed.');
|
|
169
204
|
saveConfig({ ...existing, skills_installed: true });
|
|
170
205
|
}
|
|
171
206
|
}
|
|
172
|
-
else {
|
|
173
|
-
rl.close();
|
|
174
|
-
}
|
|
175
207
|
return;
|
|
176
208
|
}
|
|
177
|
-
rl.close();
|
|
178
209
|
// Fall through to setup flow — new account will be added to the list.
|
|
179
210
|
}
|
|
180
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
181
211
|
let apiKey = '';
|
|
182
212
|
let accountId = '';
|
|
183
213
|
let defaultOrg = '';
|
|
@@ -185,97 +215,62 @@ export async function setup(flags = {}) {
|
|
|
185
215
|
let subdomainUrl = '';
|
|
186
216
|
let isAnonymous = false;
|
|
187
217
|
let email = '';
|
|
188
|
-
|
|
189
|
-
const
|
|
218
|
+
const useAnon = flags.anonymous || flags.anon;
|
|
219
|
+
const wantsRegister = useAnon ? false : await confirm('› Register with email? (Y/n, or N to continue anonymously) ', true);
|
|
220
|
+
if (wantsRegister) {
|
|
221
|
+
const data = await registeredFlow();
|
|
222
|
+
apiKey = data.api_key;
|
|
223
|
+
accountId = data.account_id;
|
|
224
|
+
defaultOrg = data.default_org;
|
|
225
|
+
defaultFunnel = data.default_funnel;
|
|
226
|
+
email = data.email;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
info('› Continuing as anonymous — sites ship to *.makeautonomous.com');
|
|
230
|
+
const data = await anonymousFlow();
|
|
231
|
+
apiKey = data.api_key;
|
|
232
|
+
accountId = data.account_id;
|
|
233
|
+
defaultOrg = data.default_org;
|
|
234
|
+
defaultFunnel = data.default_funnel;
|
|
235
|
+
subdomainUrl = data.subdomain_url;
|
|
236
|
+
isAnonymous = true;
|
|
237
|
+
}
|
|
238
|
+
const wantsSkills = await resolveSkillsPreference(flags);
|
|
239
|
+
await installAndPersistSkills({
|
|
240
|
+
api_key: apiKey,
|
|
241
|
+
account_id: accountId,
|
|
242
|
+
email: email || undefined,
|
|
243
|
+
default_org: defaultOrg,
|
|
244
|
+
default_funnel: defaultFunnel,
|
|
245
|
+
is_anonymous: isAnonymous,
|
|
246
|
+
}, wantsSkills);
|
|
247
|
+
success(`› ✓ saved to ~/.myapi/config.json`);
|
|
248
|
+
// Validate key and ensure org/funnel defaults are still correct.
|
|
249
|
+
// (resolveDefaults is non-fatal if the API call fails.)
|
|
190
250
|
try {
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const data = await anonymousFlow();
|
|
204
|
-
apiKey = data.api_key;
|
|
205
|
-
accountId = data.account_id;
|
|
206
|
-
defaultOrg = data.default_org;
|
|
207
|
-
defaultFunnel = data.default_funnel;
|
|
208
|
-
subdomainUrl = data.subdomain_url;
|
|
209
|
-
isAnonymous = true;
|
|
210
|
-
}
|
|
211
|
-
const wantsSkills = skillsFromFlag !== null ? skillsFromFlag : flags.yes ? true : yn(await ask(rl, '› Install the MyAPI skills pack? (Y/n) '));
|
|
212
|
-
addAccount({
|
|
213
|
-
api_key: apiKey,
|
|
214
|
-
account_id: accountId,
|
|
215
|
-
email: email || undefined,
|
|
216
|
-
default_org: defaultOrg,
|
|
217
|
-
default_funnel: defaultFunnel,
|
|
218
|
-
is_anonymous: isAnonymous,
|
|
219
|
-
skills_installed: wantsSkills,
|
|
220
|
-
});
|
|
221
|
-
success(`› ✓ saved to ~/.myapi/config.json`);
|
|
222
|
-
if (wantsSkills) {
|
|
223
|
-
await installSkills();
|
|
224
|
-
}
|
|
225
|
-
// Validate key and ensure org/funnel defaults are correct.
|
|
226
|
-
try {
|
|
227
|
-
const auth = { Authorization: `Bearer ${apiKey}` };
|
|
228
|
-
const meRes = await fetch(`${API_BASE}/hq/account/me`, { headers: auth });
|
|
229
|
-
if (!meRes.ok) {
|
|
230
|
-
info('› Warning: could not verify API key — check your connection.');
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
// Verify default org exists; if not, fetch the most recent one.
|
|
234
|
-
let resolvedOrg = defaultOrg;
|
|
235
|
-
let resolvedFunnel = defaultFunnel;
|
|
236
|
-
const orgRes = await fetch(`${API_BASE}/hq/orgs`, { headers: auth });
|
|
237
|
-
if (orgRes.ok) {
|
|
238
|
-
const orgs = (await orgRes.json())?.data ?? [];
|
|
239
|
-
if (orgs.length > 0) {
|
|
240
|
-
const latest = orgs[orgs.length - 1];
|
|
241
|
-
if (!resolvedOrg || !orgs.find((o) => o.id === resolvedOrg)) {
|
|
242
|
-
resolvedOrg = latest.id;
|
|
243
|
-
info(`› Auto-selected org: ${latest.name} (${resolvedOrg})`);
|
|
244
|
-
}
|
|
245
|
-
// Verify default funnel exists within the org.
|
|
246
|
-
const fRes = await fetch(`${API_BASE}/funnel/orgs/${resolvedOrg}/funnels`, { headers: auth });
|
|
247
|
-
if (fRes.ok) {
|
|
248
|
-
const funnels = (await fRes.json())?.data ?? [];
|
|
249
|
-
if (funnels.length > 0 && (!resolvedFunnel || !funnels.find((f) => f.id === resolvedFunnel))) {
|
|
250
|
-
resolvedFunnel = funnels[funnels.length - 1].id;
|
|
251
|
-
info(`› Auto-selected funnel: ${resolvedFunnel}`);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (resolvedOrg !== defaultOrg || resolvedFunnel !== defaultFunnel) {
|
|
255
|
-
const full = loadFullConfig();
|
|
256
|
-
const idx = full.active;
|
|
257
|
-
full.accounts[idx].default_org = resolvedOrg;
|
|
258
|
-
full.accounts[idx].default_funnel = resolvedFunnel;
|
|
259
|
-
fs.writeFileSync(path.join(os.homedir(), '.myapi', 'config.json'), JSON.stringify(full, null, 2), { mode: 0o600 });
|
|
260
|
-
defaultOrg = resolvedOrg;
|
|
261
|
-
defaultFunnel = resolvedFunnel;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
251
|
+
await hq.getAccount(apiKey);
|
|
252
|
+
const resolved = await resolveDefaults(apiKey, { org: defaultOrg, funnel: defaultFunnel });
|
|
253
|
+
if (resolved.org !== defaultOrg || resolved.funnel !== defaultFunnel) {
|
|
254
|
+
const current = loadConfig();
|
|
255
|
+
if (current) {
|
|
256
|
+
saveConfig({ ...current, default_org: resolved.org, default_funnel: resolved.funnel });
|
|
257
|
+
if (resolved.org !== defaultOrg)
|
|
258
|
+
info(`› Auto-selected org: ${resolved.org}`);
|
|
259
|
+
if (resolved.funnel !== defaultFunnel)
|
|
260
|
+
info(`› Auto-selected funnel: ${resolved.funnel}`);
|
|
261
|
+
defaultOrg = resolved.org;
|
|
262
|
+
defaultFunnel = resolved.funnel;
|
|
265
263
|
}
|
|
266
264
|
}
|
|
267
|
-
catch { /* non-fatal */ }
|
|
268
|
-
if (isAnonymous) {
|
|
269
|
-
success('› Setup complete. No card, no email, ready to ship.');
|
|
270
|
-
if (subdomainUrl)
|
|
271
|
-
info(`› Your funnel: ${subdomainUrl}`);
|
|
272
|
-
info('› Upgrade anytime — myapi auth link');
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
success(`› Setup complete. Org ${defaultOrg} ready · $5.00 free credits added.`);
|
|
276
|
-
}
|
|
277
265
|
}
|
|
278
|
-
|
|
279
|
-
|
|
266
|
+
catch { /* non-fatal */ }
|
|
267
|
+
if (isAnonymous) {
|
|
268
|
+
success('› Setup complete. No card, no email, ready to ship.');
|
|
269
|
+
if (subdomainUrl)
|
|
270
|
+
info(`› Your funnel: ${subdomainUrl}`);
|
|
271
|
+
info('› Upgrade anytime — myapi auth link');
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
success(`› Setup complete. Org ${defaultOrg} ready · $5.00 free credits added.`);
|
|
280
275
|
}
|
|
281
276
|
}
|
package/dist/commands/storage.js
CHANGED
|
@@ -30,26 +30,36 @@ export async function del(id, flags) {
|
|
|
30
30
|
await sdkStorage.deleteAsset(config.api_key, orgId, id);
|
|
31
31
|
success(`Asset ${id} deleted`);
|
|
32
32
|
}
|
|
33
|
+
// ── Dispatcher ───────────────────────────────────────────────────────────────
|
|
34
|
+
const SUBCOMMAND_USAGE = {
|
|
35
|
+
'list': 'myapi storage list [--org <id>] [--json]',
|
|
36
|
+
'ingest': 'myapi storage ingest <url> [--name <name>] [--org <id>]',
|
|
37
|
+
'delete': 'myapi storage delete <asset_id> [--org <id>]',
|
|
38
|
+
};
|
|
33
39
|
export async function run(subcommand, args, flags) {
|
|
34
40
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
35
|
-
info(
|
|
41
|
+
info(`Usage: myapi storage <subcommand>
|
|
42
|
+
|
|
43
|
+
Subcommands:
|
|
44
|
+
list List all your uploaded assets
|
|
45
|
+
ingest Ingest a public image URL into your edge storage
|
|
46
|
+
delete Delete a stored asset
|
|
47
|
+
|
|
48
|
+
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
36
49
|
return;
|
|
37
50
|
}
|
|
38
51
|
if (flags.help) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
info('Usage: myapi storage delete <asset_id> --org <id>');
|
|
52
|
+
const usage = SUBCOMMAND_USAGE[subcommand];
|
|
53
|
+
if (usage)
|
|
54
|
+
info(`Usage: ${usage}`);
|
|
55
|
+
else
|
|
56
|
+
info(`Unknown subcommand: ${subcommand}. Run "myapi storage --help" for the list.`);
|
|
45
57
|
return;
|
|
46
58
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
else
|
|
54
|
-
error(`Unknown subcommand: ${subcommand}. Run "myapi storage --help" for a list of valid subcommands.`);
|
|
59
|
+
switch (subcommand) {
|
|
60
|
+
case 'list': return list(flags);
|
|
61
|
+
case 'ingest': return ingest(args[0], flags);
|
|
62
|
+
case 'delete': return del(args[0], flags);
|
|
63
|
+
default: error(`Unknown subcommand: ${subcommand}. Run "myapi storage --help" for a list of valid subcommands.`);
|
|
64
|
+
}
|
|
55
65
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import type { Flags } from '../helpers.js';
|
|
1
2
|
export declare function checkForUpdate(currentVersion: string): Promise<void>;
|
|
2
|
-
export declare function update(flags?:
|
|
3
|
+
export declare function update(flags?: Flags): Promise<void>;
|
|
3
4
|
export declare function latestVersion(): Promise<string | null>;
|
|
4
5
|
export declare function isNewer(latest: string, current: string): boolean;
|
package/dist/commands/update.js
CHANGED
|
@@ -126,6 +126,11 @@ export async function latestVersion() {
|
|
|
126
126
|
return null;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
// Stable-only semver comparison (1.2.3). Prerelease versions (1.2.3-wip.0)
|
|
130
|
+
// produce NaN in the patch slot, and `n > NaN` is always false — which is
|
|
131
|
+
// what we want: a prerelease user should not be auto-rolled back to stable.
|
|
132
|
+
// Auto-update is opt-in via the `latest` dist-tag (always stable); see
|
|
133
|
+
// checkForUpdate() above.
|
|
129
134
|
export function isNewer(latest, current) {
|
|
130
135
|
const toNum = (v) => v.split('.').map(Number);
|
|
131
136
|
const [lMaj, lMin, lPat] = toNum(latest);
|
package/dist/commands/url.js
CHANGED
|
@@ -12,18 +12,30 @@ export async function shorten(targetUrl, flags) {
|
|
|
12
12
|
else
|
|
13
13
|
success(`Shortened URL: ${res.short_url}\nCode: ${res.short_code}`);
|
|
14
14
|
}
|
|
15
|
+
// ── Dispatcher ───────────────────────────────────────────────────────────────
|
|
16
|
+
const SUBCOMMAND_USAGE = {
|
|
17
|
+
'shorten': 'myapi url shorten <url> [--org <id>] [--json]',
|
|
18
|
+
};
|
|
15
19
|
export async function run(subcommand, args, flags) {
|
|
16
20
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
17
|
-
info(
|
|
21
|
+
info(`Usage: myapi url <subcommand>
|
|
22
|
+
|
|
23
|
+
Subcommands:
|
|
24
|
+
shorten Shorten a long URL (returns a myurlto.com link)
|
|
25
|
+
|
|
26
|
+
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
18
27
|
return;
|
|
19
28
|
}
|
|
20
29
|
if (flags.help) {
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
const usage = SUBCOMMAND_USAGE[subcommand];
|
|
31
|
+
if (usage)
|
|
32
|
+
info(`Usage: ${usage}`);
|
|
33
|
+
else
|
|
34
|
+
info(`Unknown subcommand: ${subcommand}. Run "myapi url --help" for the list.`);
|
|
23
35
|
return;
|
|
24
36
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
switch (subcommand) {
|
|
38
|
+
case 'shorten': return shorten(args[0], flags);
|
|
39
|
+
default: error(`Unknown subcommand: ${subcommand}. Run "myapi url --help" for a list of valid subcommands.`);
|
|
40
|
+
}
|
|
29
41
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
1
|
+
import type { FlagSchema } from '../flags.js';
|
|
2
|
+
import { type Flags } from '../helpers.js';
|
|
3
|
+
export declare const SCHEMA: FlagSchema;
|
|
4
|
+
export declare function list(flags: Flags): Promise<void>;
|
|
5
|
+
export declare function create(nameArg: string | undefined, flags: Flags): Promise<void>;
|
|
6
|
+
export declare function del(id: string, flags: Flags): Promise<void>;
|
|
7
|
+
export declare function delivery(id: string, flags: Flags): Promise<void>;
|
|
8
|
+
export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
|