@myapihq/cli 1.0.82 → 1.1.0-wip.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/commands/auth.d.ts +8 -3
  2. package/dist/commands/auth.js +11 -29
  3. package/dist/commands/billing.d.ts +8 -4
  4. package/dist/commands/billing.js +43 -22
  5. package/dist/commands/config.d.ts +8 -5
  6. package/dist/commands/config.js +63 -28
  7. package/dist/commands/domain.d.ts +12 -9
  8. package/dist/commands/domain.js +117 -86
  9. package/dist/commands/email.d.ts +4 -12
  10. package/dist/commands/email.js +490 -128
  11. package/dist/commands/funnel.d.ts +10 -7
  12. package/dist/commands/funnel.js +78 -55
  13. package/dist/commands/image.js +25 -15
  14. package/dist/commands/keys.d.ts +8 -3
  15. package/dist/commands/keys.js +67 -28
  16. package/dist/commands/org.d.ts +9 -5
  17. package/dist/commands/org.js +121 -53
  18. package/dist/commands/pixel.js +23 -11
  19. package/dist/commands/setup.d.ts +3 -2
  20. package/dist/commands/setup.js +44 -77
  21. package/dist/commands/storage.js +25 -15
  22. package/dist/commands/update.d.ts +2 -1
  23. package/dist/commands/url.js +19 -7
  24. package/dist/commands/webhook.d.ts +8 -5
  25. package/dist/commands/webhook.js +52 -36
  26. package/dist/commands/workflow.d.ts +13 -7
  27. package/dist/commands/workflow.js +158 -56
  28. package/dist/flags.d.ts +8 -0
  29. package/dist/flags.js +88 -0
  30. package/dist/flags.test.d.ts +1 -0
  31. package/dist/flags.test.js +73 -0
  32. package/dist/helpers.d.ts +6 -0
  33. package/dist/helpers.js +29 -0
  34. package/dist/index.js +97 -107
  35. package/dist/output.d.ts +10 -1
  36. package/dist/output.js +4 -6
  37. package/dist/skills/my-email-api/README.md +45 -0
  38. package/dist/skills/my-email-api/SKILL.md +106 -0
  39. package/dist/skills/my-email-api/claude/.claude-plugin/plugin.json +6 -0
  40. package/dist/skills/my-email-api/make/.gitkeep +0 -0
  41. package/dist/skills/my-email-api/n8n/.gitkeep +0 -0
  42. package/dist/skills/my-email-api/openapi/.gitkeep +0 -0
  43. package/dist/skills/my-webhook-api/README.md +40 -0
  44. package/dist/skills/my-webhook-api/SKILL.md +65 -0
  45. package/dist/skills/my-webhook-api/claude/.claude-plugin/plugin.json +6 -0
  46. package/dist/skills/my-webhook-api/make/.gitkeep +0 -0
  47. package/dist/skills/my-webhook-api/n8n/.gitkeep +0 -0
  48. package/dist/skills/my-webhook-api/openapi/.gitkeep +0 -0
  49. package/dist/skills/my-workflow-api/README.md +37 -0
  50. package/dist/skills/my-workflow-api/SKILL.md +98 -0
  51. package/dist/skills/my-workflow-api/claude/.claude-plugin/plugin.json +6 -0
  52. package/dist/skills/my-workflow-api/make/.gitkeep +0 -0
  53. package/dist/skills/my-workflow-api/n8n/.gitkeep +0 -0
  54. package/dist/skills/my-workflow-api/openapi/.gitkeep +0 -0
  55. package/dist/utils.d.ts +0 -4
  56. package/dist/utils.js +0 -33
  57. package/dist/utils.test.d.ts +1 -0
  58. package/dist/utils.test.js +48 -0
  59. package/package.json +9 -4
  60. package/dist/commands/account.d.ts +0 -4
  61. package/dist/commands/account.js +0 -80
@@ -3,17 +3,22 @@ import { hq, funnel as sdkFunnel } from '@myapihq/sdk';
3
3
  import { requireConfig, saveConfig } from '../config.js';
4
4
  import { success, error, printTable, printJson, info } from '../output.js';
5
5
  import { sleep, formatDate } from '../utils.js';
6
- export async function create(flags) {
7
- if (flags.help) {
8
- info('Usage: myapi org create --name <name> [options]\n\nCreates a new organization. A funnel (website) is automatically created alongside it.\n\nOptions:\n --name Organization name (required). Use quotes for names with spaces.\n --tagline Short tagline\n --description Detailed description\n --business-sector Sector (e.g. Technology)\n --logo-url URL to logo image\n --yes Skip the "set as default?" prompt (useful in CI / agent workflows)\n\nExamples:\n myapi org create --name "Acme Inc"\n myapi org create --name "Acme Inc" --yes');
9
- return;
10
- }
11
- if (!flags.name) {
12
- error("Missing --name flag. Run 'myapi org create --help' for details.");
13
- return;
14
- }
6
+ import { requireOrg } from '../helpers.js';
7
+ export const SCHEMA = {
8
+ name: 'string',
9
+ tagline: 'string',
10
+ description: 'string',
11
+ 'business-sector': 'string',
12
+ 'logo-url': 'string',
13
+ org: 'string',
14
+ };
15
+ const SYNC_BRAND_TIMEOUT_MS = 5 * 60 * 1000;
16
+ export async function create(restArgs, flags) {
17
+ const name = flags.name || restArgs[0];
18
+ if (!name)
19
+ error('Missing org name. Usage: myapi org create "My Company" [--yes]');
15
20
  const config = requireConfig();
16
- const payload = { name: flags.name };
21
+ const payload = { name };
17
22
  if (flags.tagline)
18
23
  payload.tagline = flags.tagline;
19
24
  if (flags.description)
@@ -23,31 +28,41 @@ export async function create(flags) {
23
28
  if (flags['logo-url'])
24
29
  payload.logo_url = flags['logo-url'];
25
30
  const org = await hq.createOrg(config.api_key, payload);
26
- success(`Org created! ID: ${org.id}, Name: ${org.name}`);
27
- if (org.preview_subdomain) {
28
- info(`Preview domain: https://${org.preview_subdomain}.makeautonomous.com`);
31
+ if (!flags.json) {
32
+ success(`Org created! ID: ${org.id}, Name: ${org.name}`);
33
+ if (org.preview_subdomain) {
34
+ info(`Preview domain: https://${org.preview_subdomain}.makeautonomous.com`);
35
+ }
29
36
  }
30
37
  const setDefault = flags.yes || await new Promise(resolve => {
31
38
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
32
- rl.question(`› Set as default org and funnel? (Y/n) `, ans => {
39
+ rl.question('› Set as default org and funnel? (Y/n) ', ans => {
33
40
  rl.close();
34
41
  resolve(ans.trim().toLowerCase() !== 'n');
35
42
  });
36
43
  });
44
+ let funnelId;
37
45
  if (setDefault) {
38
46
  config.default_org = org.id;
39
47
  const funnels = await sdkFunnel.listFunnels(config.api_key, org.id);
40
- if (funnels.length > 0)
48
+ if (funnels.length > 0) {
41
49
  config.default_funnel = funnels[0].id;
50
+ funnelId = funnels[0].id;
51
+ }
42
52
  saveConfig(config);
43
- success(`Default org${funnels.length > 0 ? ' and funnel' : ''} updated.`);
53
+ if (!flags.json)
54
+ success(`Default org${funnelId ? ' and funnel' : ''} updated.`);
55
+ }
56
+ if (flags.json) {
57
+ const result = { id: org.id, name: org.name };
58
+ if (org.preview_subdomain)
59
+ result.preview_url = `https://${org.preview_subdomain}.makeautonomous.com`;
60
+ if (funnelId)
61
+ result.funnel_id = funnelId;
62
+ printJson(result);
44
63
  }
45
64
  }
46
65
  export async function list(flags) {
47
- if (flags.help) {
48
- info('Usage: myapi org list [--json]\n\nLists all organizations in your account.\n\nFlags:\n --json Output raw JSON');
49
- return;
50
- }
51
66
  const config = requireConfig();
52
67
  const orgs = await hq.listOrgs(config.api_key);
53
68
  if (flags.json) {
@@ -68,19 +83,16 @@ export async function list(flags) {
68
83
  name: o.id === config.default_org ? `${o.name} *` : o.name,
69
84
  created_at: o.created_at ? formatDate(o.created_at) : '',
70
85
  }));
71
- printTable(rows);
86
+ printTable(rows, {
87
+ flags,
88
+ empty: 'No organizations found. Create one with: myapi org create "My Company"',
89
+ });
72
90
  }
73
91
  export async function get(id, flags) {
74
- if (flags.help) {
75
- info('Usage: myapi org get <id> [--json]\n\nFetches details of a specific organization.\n\nFlags:\n --json Output raw JSON');
76
- return;
77
- }
78
92
  const config = requireConfig();
79
93
  const resolvedId = id || config.default_org;
80
- if (!resolvedId) {
81
- error("Missing org id. Usage: myapi org get <id>\n(Or set a default: myapi config set-org <id>)");
82
- return;
83
- }
94
+ if (!resolvedId)
95
+ error('Missing org id. Usage: myapi org get <id>\n(Or set a default: myapi config set-org <id>)');
84
96
  const org = await hq.getOrg(config.api_key, resolvedId);
85
97
  if (flags.json) {
86
98
  printJson(org);
@@ -95,49 +107,105 @@ export async function get(id, flags) {
95
107
  if (org.preview_subdomain)
96
108
  info(`Preview: https://${org.preview_subdomain}.makeautonomous.com`);
97
109
  }
98
- export async function del(id, flags) {
99
- if (flags.help) {
100
- info('Usage: myapi org delete <id>\n\nPermanently deletes an organization and its associated funnels.\nNote: any registered domains assigned to this org must be unassigned first.\n\nNo confirmation prompt is shown — this action is immediate.');
101
- return;
102
- }
103
- if (!id) {
104
- error("Missing org id. Usage: myapi org delete <id>");
105
- return;
106
- }
110
+ export async function del(id, _flags) {
111
+ if (!id)
112
+ error('Missing org id. Usage: myapi org delete <id>');
107
113
  const config = requireConfig();
108
114
  await hq.deleteOrg(config.api_key, id);
109
115
  success(`Org ${id} deleted`);
110
116
  }
111
117
  export async function importOrg(args, flags) {
112
- if (flags.help) {
113
- info('Usage: myapi org sync-brand <domain> --org <id>\n\nFetches an existing website and extracts brand signals (name, logo, description,\nbusiness sector) to enrich your organization profile automatically.\n\nThis does NOT register or transfer the domain — it only reads from the site.\nThe org must already exist before running this command.\n\nPrerequisite:\n myapi org create --name "My Brand" --yes\n\nExample:\n myapi org sync-brand example.com --org <id>');
114
- return;
115
- }
116
118
  const config = requireConfig();
117
119
  const domain = args[0] || config.default_domain;
118
- const orgId = flags.org || config.default_org;
119
- if (!domain || !orgId) {
120
- error("Missing required arguments.\nUsage: myapi org import <domain> --org <id>\n(Or set defaults via: myapi config set-org <id> / set-domain <domain>)");
121
- return;
122
- }
120
+ const orgId = requireOrg(flags, config, 'myapi org sync-brand <domain> [--org <id>]');
121
+ if (!domain)
122
+ error('Missing required arguments.\nUsage: myapi org sync-brand <domain> [--org <id>]\n(Or set default: myapi config set-domain <domain>)');
123
123
  const result = await hq.importOrg(config.api_key, orgId, domain);
124
124
  const importId = result.job_id;
125
- process.stdout.write("Importing ");
125
+ process.stdout.write('Importing ');
126
126
  const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
127
127
  let i = 0;
128
- while (true) {
128
+ let elapsed = 0;
129
+ while (elapsed < SYNC_BRAND_TIMEOUT_MS) {
129
130
  const status = await hq.getOrgImportStatus(config.api_key, importId);
130
131
  if (status.status === 'awaiting_confirm') {
131
132
  process.stdout.write('\r\x1b[K');
132
- break;
133
+ const org = await hq.confirmOrgImport(config.api_key, importId);
134
+ success(`Import complete! Org ID: ${org.id}`);
135
+ return;
133
136
  }
134
137
  if (status.status === 'failed') {
135
138
  process.stdout.write('\r\x1b[K');
136
- error("Import failed");
139
+ error('Import failed');
137
140
  }
138
141
  process.stdout.write(`\rImporting ${chars[i++ % chars.length]}`);
139
142
  await sleep(3000);
143
+ elapsed += 3000;
144
+ }
145
+ process.stdout.write('\r\x1b[K');
146
+ error(`Import timed out after ${SYNC_BRAND_TIMEOUT_MS / 1000}s. The job may still complete in the background — re-run "myapi org sync-brand" to retry.`);
147
+ }
148
+ // ── Dispatcher ───────────────────────────────────────────────────────────────
149
+ const SUBCOMMAND_USAGE = {
150
+ 'list': 'myapi org list [--json]',
151
+ 'create': `myapi org create <name> [--tagline <str>] [--description <str>] [--business-sector <str>] [--logo-url <url>] [--yes] [--json]
152
+ myapi org create --name <name> [...]
153
+
154
+ Creates a new organization. A funnel (website) is automatically created alongside it.
155
+ Pass --yes to skip the "set as default?" prompt (useful in CI / agent workflows).
156
+
157
+ Examples:
158
+ myapi org create "Acme Inc"
159
+ myapi org create "Acme Inc" --yes
160
+ myapi org create --name "Acme Inc" --yes`,
161
+ 'get': 'myapi org get <id> [--json]',
162
+ 'delete': `myapi org delete <id>
163
+
164
+ Permanently deletes an organization and its associated funnels.
165
+ Any registered domains assigned to this org must be unassigned first.
166
+ No confirmation prompt — the action is immediate.`,
167
+ 'sync-brand': `myapi org sync-brand <domain> [--org <id>]
168
+
169
+ Fetches an existing website and extracts brand signals (name, logo, description,
170
+ business sector) to enrich your organization profile.
171
+
172
+ Does NOT register or transfer the domain — it only reads from the site.
173
+ The org must already exist. Polls for up to 5 minutes.
174
+
175
+ Example:
176
+ myapi org create --name "My Brand" --yes
177
+ myapi org sync-brand example.com --org <id>`,
178
+ };
179
+ export async function run(subcommand, args, flags) {
180
+ if (!subcommand || (flags.help && !subcommand)) {
181
+ info(`Usage: myapi org <subcommand>
182
+
183
+ Subcommands:
184
+ list List organizations
185
+ create Create an organization (e.g. myapi org create "Name" --yes)
186
+ get Get details of an organization
187
+ delete Delete an organization
188
+ sync-brand Sync brand info (name, logo, description) from an existing website`);
189
+ return;
190
+ }
191
+ if (flags.help) {
192
+ const lookup = subcommand === 'import' ? 'sync-brand' : subcommand;
193
+ const usage = SUBCOMMAND_USAGE[lookup];
194
+ if (usage)
195
+ info(`Usage: ${usage}`);
196
+ else
197
+ info(`Unknown subcommand: ${subcommand}. Run "myapi org --help" for the list.`);
198
+ return;
199
+ }
200
+ switch (subcommand) {
201
+ case 'list': return list(flags);
202
+ case 'create': return create(args, flags);
203
+ case 'get': return get(args[0], flags);
204
+ case 'delete': return del(args[0], flags);
205
+ case 'sync-brand': return importOrg(args, flags);
206
+ case 'import':
207
+ process.stderr.write('› Note: "org import" is deprecated — use "org sync-brand" instead.\n');
208
+ return importOrg(args, flags);
209
+ default: error(`Unknown subcommand: ${subcommand}. Run "myapi org --help" for available subcommands.`);
140
210
  }
141
- const org = await hq.confirmOrgImport(config.api_key, importId);
142
- success(`Import complete! Org ID: ${org.id}`);
143
211
  }
@@ -43,22 +43,34 @@ export async function identity(pixelId, flags) {
43
43
  const res = await sdkPixel.getIdentity(config.api_key, orgId, pixelId);
44
44
  printJson(res);
45
45
  }
46
+ // ── Dispatcher ───────────────────────────────────────────────────────────────
47
+ const SUBCOMMAND_USAGE = {
48
+ 'interactions': 'myapi pixel interactions [--website <domain>] [--campaign-id <id>] [--domain <domain>] [--from <iso8601>] [--to <iso8601>] [--limit <num>] [--offset <num>] [--org <id>] [--json]',
49
+ 'identity': 'myapi pixel identity <pixel_id> [--org <id>]',
50
+ };
46
51
  export async function run(subcommand, args, flags) {
47
52
  if (!subcommand || (flags.help && !subcommand)) {
48
- info('Usage: myapi pixel <subcommand>\n\nSubcommands:\n interactions Get a unified timeline of visits and events\n identity Resolve the identity graph for a pixel ID\n\nNote: All pixel commands require the --org <id> flag.');
53
+ info(`Usage: myapi pixel <subcommand>
54
+
55
+ Subcommands:
56
+ interactions Get a unified timeline of visits and events
57
+ (requires at least one filter: --website, --campaign-id, or --domain)
58
+ identity Resolve the identity graph (emails, IPs, profiles) for a pixel ID
59
+
60
+ All commands accept --org <id> (or set default: myapi config set-org <id>).`);
49
61
  return;
50
62
  }
51
63
  if (flags.help) {
52
- if (subcommand === 'interactions')
53
- info('Usage: myapi pixel interactions --org <id> [--website <domain>] [--campaign-id <id>] [--domain <domain>] [--from <iso8601>] [--to <iso8601>] [--limit <num>] [--offset <num>] [--json]\n\nRetrieves a merged timeline of web visits and email events. Requires at least one filter (--website, --campaign-id, or --domain).');
54
- else if (subcommand === 'identity')
55
- info('Usage: myapi pixel identity <pixel_id> --org <id>\n\nResolves the full identity graph (emails, IPs, profiles) for a specific pixel tracker ID.');
64
+ const usage = SUBCOMMAND_USAGE[subcommand];
65
+ if (usage)
66
+ info(`Usage: ${usage}`);
67
+ else
68
+ info(`Unknown subcommand: ${subcommand}. Run "myapi pixel --help" for the list.`);
56
69
  return;
57
70
  }
58
- if (subcommand === 'interactions')
59
- await interactions(flags);
60
- else if (subcommand === 'identity')
61
- await identity(args[0], flags);
62
- else
63
- error(`Unknown subcommand: ${subcommand}. Run "myapi pixel --help" for a list of valid subcommands.`);
71
+ switch (subcommand) {
72
+ case 'interactions': return interactions(flags);
73
+ case 'identity': return identity(args[0], flags);
74
+ default: error(`Unknown subcommand: ${subcommand}. Run "myapi pixel --help" for a list of valid subcommands.`);
75
+ }
64
76
  }
@@ -1,3 +1,4 @@
1
+ import type { Flags } from '../helpers.js';
1
2
  export declare function installSkills(): Promise<void>;
2
- export declare function importKey(apiKey: string, flags: Record<string, string | boolean>): Promise<void>;
3
- export declare function setup(flags?: Record<string, string | boolean>): Promise<void>;
3
+ export declare function importKey(apiKey: string, flags: Flags): Promise<void>;
4
+ export declare function setup(flags?: Flags): Promise<void>;
@@ -4,7 +4,7 @@ import * as path from 'path';
4
4
  import * as readline from 'readline';
5
5
  import { loadConfig, saveConfig, addAccount, loadFullConfig } from '../config.js';
6
6
  import { info, success } from '../output.js';
7
- const API_BASE = process.env.MYAPI_API_BASE ?? 'https://api.myapihq.com';
7
+ import { hq, funnel as sdkFunnel } from '@myapihq/sdk';
8
8
  function ask(rl, q) {
9
9
  return new Promise(resolve => rl.question(q, resolve));
10
10
  }
@@ -14,17 +14,6 @@ function yn(answer, defaultYes = true) {
14
14
  return defaultYes;
15
15
  return t === 'y' || t === 'yes';
16
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
- }
28
17
  // ---------------------------------------------------------------------------
29
18
  // Skills installation
30
19
  // ---------------------------------------------------------------------------
@@ -55,7 +44,9 @@ export async function installSkills() {
55
44
  fs.mkdirSync(skillDir, { recursive: true });
56
45
  fs.copyFileSync(path.join(BUNDLED_SKILLS_DIR, skill, 'SKILL.md'), path.join(skillDir, 'SKILL.md'));
57
46
  }
58
- // Symlink each skill into agent config directories
47
+ // Symlink each skill into agent config directories. Surface failures so
48
+ // users can fix permission issues — skills that didn't install will not
49
+ // be available to the agent and "skills installed" would otherwise lie.
59
50
  for (const [agent, dir] of Object.entries(AGENT_DIRS)) {
60
51
  try {
61
52
  fs.mkdirSync(dir, { recursive: true });
@@ -70,8 +61,15 @@ export async function installSkills() {
70
61
  }
71
62
  info(` ✓ ${agent}`);
72
63
  }
73
- catch {
74
- // Silently skip agents whose directory can't be created.
64
+ catch (err) {
65
+ // Skip agents not installed on this machine (ENOENT on parent dir is OK).
66
+ // Surface anything else so the user knows skills aren't fully installed.
67
+ const code = err?.code;
68
+ if (code === 'ENOENT' || code === 'ENOTDIR') {
69
+ // Agent likely not installed — quietly skip.
70
+ continue;
71
+ }
72
+ info(` ✗ ${agent}: ${err.message || code} (${dir})`);
75
73
  }
76
74
  }
77
75
  }
@@ -80,18 +78,14 @@ export async function installSkills() {
80
78
  // ---------------------------------------------------------------------------
81
79
  async function registeredFlow(rl) {
82
80
  const email = (await ask(rl, '› Email? ')).trim();
83
- await post('/hq/account/send-code', { email });
81
+ await hq.sendCode(email);
84
82
  info(`› Sent a code to ${email} · paste it below`);
85
83
  const code = (await ask(rl, '› Code? ')).trim();
86
- const data = await post('/hq/account/verify-code', { email, code });
84
+ const data = await hq.verifyCode(email, code);
87
85
  return { ...data, email };
88
86
  }
89
- // ---------------------------------------------------------------------------
90
- // Anonymous flow
91
- // ---------------------------------------------------------------------------
92
87
  async function anonymousFlow() {
93
- const data = await post('/hq/account/anonymous', {});
94
- return data;
88
+ return hq.createAnonymousAccount();
95
89
  }
96
90
  // ---------------------------------------------------------------------------
97
91
  // Main setup command
@@ -102,37 +96,25 @@ export async function importKey(apiKey, flags) {
102
96
  info('Usage: myapi auth import-key <api_key> [--install-skills] [--no-skills]\n\nImports an existing API key non-interactively. Use this in CI, Docker, or any\nenvironment where the interactive "myapi auth setup" flow is not practical.\n\nThe key is validated against the API before being saved. Your default org and\nfunnel are auto-detected from the account and written to the local config.\n\nFlags:\n --install-skills Also install the MyAPI skills pack for AI agents after importing\n --no-skills Skip skills installation even if previously installed\n\nExamples:\n myapi auth import-key hq_live_xxxxxxxxxxxxxxxxxxxx\n myapi auth import-key hq_live_xxxxxxxxxxxxxxxxxxxx --install-skills');
103
97
  return;
104
98
  }
105
- const auth = { Authorization: `Bearer ${apiKey}` };
106
99
  let accountId = '';
107
100
  let email;
108
101
  let defaultOrg = '';
109
102
  let defaultFunnel = '';
110
103
  try {
111
- const meRes = await fetch(`${API_BASE}/hq/account/me`, { headers: auth });
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;
104
+ const me = await hq.getAccount(apiKey);
116
105
  accountId = me.account_id ?? '';
117
106
  email = me.email || undefined;
118
107
  }
119
108
  catch (e) {
120
109
  throw new Error(`Could not verify API key: ${e.message}`);
121
110
  }
122
- // Fetch org/funnel defaults.
123
111
  try {
124
- const orgRes = await fetch(`${API_BASE}/hq/orgs`, { headers: auth });
125
- if (orgRes.ok) {
126
- const orgs = ((await orgRes.json())?.data ?? []);
127
- if (orgs.length > 0) {
128
- defaultOrg = orgs[orgs.length - 1].id;
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
- }
112
+ const orgs = await hq.listOrgs(apiKey);
113
+ if (orgs.length > 0) {
114
+ defaultOrg = orgs[orgs.length - 1].id;
115
+ const funnels = await sdkFunnel.listFunnels(apiKey, defaultOrg);
116
+ if (funnels.length > 0)
117
+ defaultFunnel = funnels[funnels.length - 1].id;
136
118
  }
137
119
  }
138
120
  catch { /* non-fatal */ }
@@ -224,42 +206,27 @@ export async function setup(flags = {}) {
224
206
  }
225
207
  // Validate key and ensure org/funnel defaults are correct.
226
208
  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
- }
209
+ await hq.getAccount(apiKey);
210
+ let resolvedOrg = defaultOrg;
211
+ let resolvedFunnel = defaultFunnel;
212
+ const orgs = await hq.listOrgs(apiKey);
213
+ if (orgs.length > 0) {
214
+ const latest = orgs[orgs.length - 1];
215
+ if (!resolvedOrg || !orgs.find(o => o.id === resolvedOrg)) {
216
+ resolvedOrg = latest.id;
217
+ info(`› Auto-selected org: ${latest.name} (${resolvedOrg})`);
218
+ }
219
+ const funnels = await sdkFunnel.listFunnels(apiKey, resolvedOrg);
220
+ if (funnels.length > 0 && (!resolvedFunnel || !funnels.find((f) => f.id === resolvedFunnel))) {
221
+ resolvedFunnel = funnels[funnels.length - 1].id;
222
+ info(`› Auto-selected funnel: ${resolvedFunnel}`);
223
+ }
224
+ if (resolvedOrg !== defaultOrg || resolvedFunnel !== defaultFunnel) {
225
+ const current = loadConfig();
226
+ if (current) {
227
+ saveConfig({ ...current, default_org: resolvedOrg, default_funnel: resolvedFunnel });
228
+ defaultOrg = resolvedOrg;
229
+ defaultFunnel = resolvedFunnel;
263
230
  }
264
231
  }
265
232
  }
@@ -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('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.');
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
- if (subcommand === 'list')
40
- info('Usage: myapi storage list --org <id> [--json]');
41
- else if (subcommand === 'ingest')
42
- 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.');
43
- else if (subcommand === 'delete')
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
- if (subcommand === 'list')
48
- await list(flags);
49
- else if (subcommand === 'ingest')
50
- await ingest(args[0], flags);
51
- else if (subcommand === 'delete')
52
- await del(args[0], flags);
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?: Record<string, string | boolean>): Promise<void>;
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;
@@ -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('Usage: myapi url <subcommand>\n\nSubcommands:\n shorten Shorten a long URL\n\nNote: All url commands require the --org <id> flag.');
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
- if (subcommand === 'shorten')
22
- info('Usage: myapi url shorten <url> --org <id> [--json]\n\nShortens a long URL and returns the compact myurlto.com link.');
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
- if (subcommand === 'shorten')
26
- await shorten(args[0], flags);
27
- else
28
- error(`Unknown subcommand: ${subcommand}. Run "myapi url --help" for a list of valid subcommands.`);
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
- export declare function list(flags: Record<string, string | boolean>): Promise<void>;
2
- export declare function create(flags: Record<string, string | boolean>): Promise<void>;
3
- export declare function del(id: string, flags: Record<string, string | boolean>): Promise<void>;
4
- export declare function delivery(id: string, flags: Record<string, string | boolean>): Promise<void>;
5
- export declare function run(subcommand: string | undefined, args: string[], flags: Record<string, string | boolean>): Promise<void>;
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(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>;