@myapihq/cli 1.1.0-wip.0 → 1.1.0-wip.2

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 (43) hide show
  1. package/dist/commands/auth.js +75 -33
  2. package/dist/commands/billing.js +3 -5
  3. package/dist/commands/domain.js +8 -1
  4. package/dist/commands/email/campaign.d.ts +2 -0
  5. package/dist/commands/email/campaign.js +152 -0
  6. package/dist/commands/email/index.d.ts +4 -0
  7. package/dist/commands/email/index.js +98 -0
  8. package/dist/commands/email/mailbox.d.ts +2 -0
  9. package/dist/commands/email/mailbox.js +88 -0
  10. package/dist/commands/email/message.d.ts +2 -0
  11. package/dist/commands/email/message.js +115 -0
  12. package/dist/commands/email/template.d.ts +2 -0
  13. package/dist/commands/email/template.js +106 -0
  14. package/dist/commands/email/warmup.d.ts +2 -0
  15. package/dist/commands/email/warmup.js +43 -0
  16. package/dist/commands/email.js +71 -51
  17. package/dist/commands/funnel.d.ts +1 -1
  18. package/dist/commands/funnel.js +31 -9
  19. package/dist/commands/keys.d.ts +1 -1
  20. package/dist/commands/keys.js +6 -9
  21. package/dist/commands/org.js +17 -34
  22. package/dist/commands/setup.js +143 -115
  23. package/dist/commands/update.js +5 -0
  24. package/dist/commands/webhook.d.ts +1 -1
  25. package/dist/commands/webhook.js +23 -7
  26. package/dist/commands/workflow.d.ts +1 -1
  27. package/dist/commands/workflow.js +45 -6
  28. package/dist/config.js +10 -5
  29. package/dist/helpers.js +8 -6
  30. package/dist/index.js +9 -3
  31. package/dist/output.d.ts +3 -1
  32. package/dist/output.js +12 -0
  33. package/dist/prompt.d.ts +24 -0
  34. package/dist/prompt.js +41 -0
  35. package/dist/skills/my-email-api/README.md +1 -1
  36. package/dist/skills/my-email-api/SKILL.md +4 -6
  37. package/dist/skills/my-webhook-api/README.md +1 -1
  38. package/dist/skills/my-webhook-api/SKILL.md +77 -4
  39. package/dist/skills/my-workflow-api/README.md +3 -4
  40. package/dist/skills/my-workflow-api/SKILL.md +79 -10
  41. package/dist/utils.d.ts +26 -0
  42. package/dist/utils.js +32 -0
  43. package/package.json +1 -1
@@ -0,0 +1,115 @@
1
+ import { email as sdkEmail } from '@myapihq/sdk';
2
+ import { requireConfig } from '../../config.js';
3
+ import { success, error, printTable, printJson, info } from '../../output.js';
4
+ async function send(flags) {
5
+ const config = requireConfig();
6
+ if (!flags.from || !flags.to || !flags.subject) {
7
+ error('Missing required arguments.\nUsage: myapi email message send --from <email> --to <email> --subject <str> [--body <str> | --html <str> | --template-id <id> [--template-vars <json>]]');
8
+ }
9
+ if (!flags.body && !flags.html && !flags['template-id'])
10
+ error('Must provide one of --body, --html, or --template-id');
11
+ if ((flags.body || flags.html) && flags['template-id'])
12
+ error('Cannot combine --body/--html with --template-id');
13
+ let templateVars;
14
+ if (flags['template-vars']) {
15
+ try {
16
+ templateVars = JSON.parse(flags['template-vars']);
17
+ }
18
+ catch {
19
+ error('--template-vars must be valid JSON');
20
+ }
21
+ }
22
+ const res = await sdkEmail.sendEmail(config.api_key, {
23
+ from: flags.from,
24
+ to: [flags.to],
25
+ subject: flags.subject,
26
+ text: flags.body,
27
+ html: flags.html,
28
+ template_id: flags['template-id'],
29
+ template_vars: templateVars,
30
+ });
31
+ success(`Email sent! Message ID: ${res.message_id}`);
32
+ }
33
+ async function status(messageId, _flags) {
34
+ const config = requireConfig();
35
+ if (!messageId)
36
+ error('Missing required arguments.\nUsage: myapi email message status <message_id>');
37
+ const res = await sdkEmail.getEmailStatus(config.api_key, messageId);
38
+ printJson(res);
39
+ }
40
+ async function sent(flags) {
41
+ const config = requireConfig();
42
+ const limit = flags.limit || 50;
43
+ const offset = flags.offset || 0;
44
+ const emails = await sdkEmail.getSentEmails(config.api_key, limit, offset);
45
+ printTable(emails, {
46
+ flags,
47
+ empty: 'No sent emails yet.',
48
+ });
49
+ }
50
+ async function inbox(address, flags) {
51
+ const config = requireConfig();
52
+ if (!address)
53
+ error('Missing required arguments.\nUsage: myapi email message inbox <address>');
54
+ const messages = await sdkEmail.getInbox(config.api_key, address);
55
+ printTable(messages, {
56
+ flags,
57
+ empty: `No messages in ${address}.`,
58
+ });
59
+ }
60
+ async function outbox(address, flags) {
61
+ const config = requireConfig();
62
+ if (!address)
63
+ error('Missing required arguments.\nUsage: myapi email message outbox <address>');
64
+ const messages = await sdkEmail.getOutbox(config.api_key, address);
65
+ printTable(messages, {
66
+ flags,
67
+ empty: `No outbound messages from ${address}.`,
68
+ });
69
+ }
70
+ async function get(messageId, flags) {
71
+ const config = requireConfig();
72
+ if (!messageId || !flags.address)
73
+ error('Missing required arguments.\nUsage: myapi email message get <message_id> --address <email>');
74
+ const res = await sdkEmail.getMessage(config.api_key, messageId, flags.address);
75
+ printJson(res);
76
+ }
77
+ const USAGE = {
78
+ 'send': 'myapi email message send --from <email> --to <email> --subject <str> [--body <str> | --html <str> | --template-id <id> [--template-vars <json>]]',
79
+ 'status': 'myapi email message status <message_id>',
80
+ 'sent': 'myapi email message sent [--limit <n>] [--offset <n>]',
81
+ 'inbox': 'myapi email message inbox <address>',
82
+ 'outbox': 'myapi email message outbox <address>',
83
+ 'get': 'myapi email message get <message_id> --address <email>',
84
+ };
85
+ export async function run(sub, args, flags) {
86
+ if (!sub || (flags.help && !sub)) {
87
+ info(`Usage: myapi email message <subcommand>
88
+
89
+ Subcommands:
90
+ send Send an email (transactional or templated)
91
+ status <id> Get delivery status of a sent message
92
+ sent List sent emails
93
+ inbox <addr> Read inbox for a mailbox address
94
+ outbox <addr> Read outbox for a mailbox address
95
+ get <id> Get a single message (--address <email>)`);
96
+ return;
97
+ }
98
+ if (flags.help) {
99
+ const usage = USAGE[sub];
100
+ if (usage)
101
+ info(`Usage: ${usage}`);
102
+ else
103
+ info(`Unknown subcommand: ${sub}. Run "myapi email message --help" for the list.`);
104
+ return;
105
+ }
106
+ switch (sub) {
107
+ case 'send': return send(flags);
108
+ case 'status': return status(args[0], flags);
109
+ case 'sent': return sent(flags);
110
+ case 'inbox': return inbox(args[0], flags);
111
+ case 'outbox': return outbox(args[0], flags);
112
+ case 'get': return get(args[0], flags);
113
+ default: error(`Unknown subcommand: ${sub}. Run "myapi email message --help" for the list.`);
114
+ }
115
+ }
@@ -0,0 +1,2 @@
1
+ import { type Flags } from '../../helpers.js';
2
+ export declare function run(sub: string | undefined, args: string[], flags: Flags): Promise<void>;
@@ -0,0 +1,106 @@
1
+ import { email as sdkEmail } from '@myapihq/sdk';
2
+ import { requireConfig } from '../../config.js';
3
+ import { success, error, printTable, printJson, info } from '../../output.js';
4
+ import { pollJob } from '../../utils.js';
5
+ import { requireOrg } from '../../helpers.js';
6
+ async function generate(nameArg, flags) {
7
+ const config = requireConfig();
8
+ const orgId = requireOrg(flags, config, 'myapi email template generate <name> --prompt <str> [--org <id>]');
9
+ const name = nameArg || flags.name;
10
+ if (!name || !flags.prompt) {
11
+ error('Missing required arguments.\nUsage: myapi email template generate <name> --prompt <str> [--org <id>]\n or: myapi email template generate --name <name> --prompt <str> [--org <id>]');
12
+ }
13
+ const job = await sdkEmail.generateTemplate(config.api_key, orgId, {
14
+ prompt: flags.prompt,
15
+ name,
16
+ });
17
+ const status = await pollJob({
18
+ label: 'Generating template',
19
+ timeoutMs: 90_000,
20
+ intervalMs: 3000,
21
+ check: () => sdkEmail.getTemplateJobStatus(config.api_key, orgId, job.job_id),
22
+ isDone: s => s.status === 'completed',
23
+ isFailed: s => s.status === 'failed',
24
+ failedMessage: 'Template generation failed',
25
+ timeoutMessage: 'Template generation timed out',
26
+ });
27
+ success(`Template generated! ID: ${status.template_id}\nPreview: ${status.result?.preview_url}`);
28
+ }
29
+ async function list(flags) {
30
+ const config = requireConfig();
31
+ const orgId = requireOrg(flags, config, 'myapi email template list [--org <id>]');
32
+ const templates = await sdkEmail.listTemplates(config.api_key, orgId);
33
+ if (flags.json) {
34
+ printJson(templates);
35
+ return;
36
+ }
37
+ printTable(templates, {
38
+ flags,
39
+ empty: 'No templates yet. Generate one with: myapi email template generate --prompt <p> --name <n>',
40
+ });
41
+ }
42
+ async function edit(id, flags) {
43
+ const config = requireConfig();
44
+ const orgId = requireOrg(flags, config, 'myapi email template edit <id> --prompt <str> [--org <id>]');
45
+ if (!id || !flags.prompt)
46
+ error('Missing required arguments.\nUsage: myapi email template edit <id> --prompt <str> [--org <id>]');
47
+ const res = await sdkEmail.editTemplate(config.api_key, orgId, id, flags.prompt);
48
+ success(`Template ${res.template_id} edited\nPreview: ${res.preview_url}`);
49
+ }
50
+ async function sendTest(id, flags) {
51
+ const config = requireConfig();
52
+ const orgId = requireOrg(flags, config, 'myapi email template send-test <template_id> --to <email> [--org <id>]');
53
+ if (!id || !flags.to)
54
+ error('Missing required arguments.\nUsage: myapi email template send-test <template_id> --to <email> [--org <id>]');
55
+ const res = await sdkEmail.sendTestEmail(config.api_key, orgId, id, flags.to);
56
+ success(`Test sent! Message ID: ${res.message_id}`);
57
+ }
58
+ async function del(id, flags) {
59
+ const config = requireConfig();
60
+ const orgId = requireOrg(flags, config, 'myapi email template delete <id> [--org <id>]');
61
+ if (!id)
62
+ error('Missing required arguments.\nUsage: myapi email template delete <id> [--org <id>]');
63
+ await sdkEmail.deleteTemplate(config.api_key, orgId, id);
64
+ success(`Deleted template ${id}`);
65
+ }
66
+ const USAGE = {
67
+ 'generate': `myapi email template generate <name> --prompt <str> [--org <id>]
68
+ myapi email template generate --name <name> --prompt <str> [--org <id>]
69
+
70
+ Either form works; the positional name is the recommended shape.`,
71
+ 'list': 'myapi email template list [--org <id>] [--json]',
72
+ 'edit': 'myapi email template edit <id> --prompt <str> [--org <id>]',
73
+ 'send-test': 'myapi email template send-test <template_id> --to <email> [--org <id>]',
74
+ 'delete': 'myapi email template delete <id> [--org <id>]',
75
+ };
76
+ export async function run(sub, args, flags) {
77
+ if (!sub || (flags.help && !sub)) {
78
+ info(`Usage: myapi email template <subcommand>
79
+
80
+ Subcommands:
81
+ generate <name> Generate AI template (--prompt)
82
+ list List templates
83
+ edit <id> Edit a template via prompt
84
+ send-test <id> Send a test email for a template (--to)
85
+ delete <id> Delete a template
86
+
87
+ All commands accept --org <id> (or set default: myapi config set-org <id>).`);
88
+ return;
89
+ }
90
+ if (flags.help) {
91
+ const usage = USAGE[sub];
92
+ if (usage)
93
+ info(`Usage: ${usage}`);
94
+ else
95
+ info(`Unknown subcommand: ${sub}. Run "myapi email template --help" for the list.`);
96
+ return;
97
+ }
98
+ switch (sub) {
99
+ case 'generate': return generate(args[0], flags);
100
+ case 'list': return list(flags);
101
+ case 'edit': return edit(args[0], flags);
102
+ case 'send-test': return sendTest(args[0], flags);
103
+ case 'delete': return del(args[0], flags);
104
+ default: error(`Unknown subcommand: ${sub}. Run "myapi email template --help" for the list.`);
105
+ }
106
+ }
@@ -0,0 +1,2 @@
1
+ import type { Flags } from '../../helpers.js';
2
+ export declare function run(sub: string | undefined, args: string[], flags: Flags): Promise<void>;
@@ -0,0 +1,43 @@
1
+ import { email as sdkEmail } from '@myapihq/sdk';
2
+ import { requireConfig } from '../../config.js';
3
+ import { success, error, printJson, info } from '../../output.js';
4
+ export async function run(sub, args, flags) {
5
+ if (!sub || (flags.help && !sub)) {
6
+ info('Usage: myapi email warmup <start|pause|resume|stop|stats> --address <email>');
7
+ return;
8
+ }
9
+ if (flags.help) {
10
+ info(`Usage: myapi email warmup ${sub} --address <email>`);
11
+ return;
12
+ }
13
+ const config = requireConfig();
14
+ const address = flags.address || args[0];
15
+ if (!address)
16
+ error('Missing required argument: --address <email>');
17
+ switch (sub) {
18
+ case 'start': {
19
+ const res = await sdkEmail.startWarmup(config.api_key, address);
20
+ success(`Warmup started: ${address} (${res.warmup_status})`);
21
+ return;
22
+ }
23
+ case 'pause':
24
+ await sdkEmail.pauseWarmup(config.api_key, address);
25
+ success(`Warmup paused: ${address}`);
26
+ return;
27
+ case 'resume':
28
+ await sdkEmail.resumeWarmup(config.api_key, address);
29
+ success(`Warmup resumed: ${address}`);
30
+ return;
31
+ case 'stop':
32
+ await sdkEmail.stopWarmup(config.api_key, address);
33
+ success(`Warmup stopped: ${address}`);
34
+ return;
35
+ case 'stats': {
36
+ const res = await sdkEmail.getWarmupStats(config.api_key, address);
37
+ printJson(res);
38
+ return;
39
+ }
40
+ default:
41
+ error(`Unknown warmup subcommand: ${sub}. Use start|pause|resume|stop|stats`);
42
+ }
43
+ }
@@ -1,7 +1,7 @@
1
1
  import { email as sdkEmail } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
3
  import { success, error, printTable, printJson, info } from '../output.js';
4
- import { sleep } from '../utils.js';
4
+ import { pollJob } from '../utils.js';
5
5
  import { requireOrg } from '../helpers.js';
6
6
  export const SCHEMA = {
7
7
  org: 'string',
@@ -25,12 +25,25 @@ export const SCHEMA = {
25
25
  'per-day': 'number',
26
26
  };
27
27
  // ─── Mailbox ─────────────────────────────────────────────────────────────────
28
- async function mailboxCreate(flags) {
28
+ async function mailboxCreate(addressArg, flags) {
29
29
  const config = requireConfig();
30
- const domain = flags.domain || config.default_domain;
31
- const username = flags.username;
32
- if (!domain || !username) {
33
- error('Missing required arguments.\nUsage: myapi email mailbox create --domain <domain> --username <user> [--display-name <name>]\n(Or set default: myapi config set-domain <domain>)');
30
+ // Convention: first required arg is positional. For mailboxes that's the
31
+ // full address `user@domain`. The flag form (--username + --domain) is
32
+ // still accepted for back-compat / scripts that built up parts separately.
33
+ let username = flags.username;
34
+ let domain = flags.domain || config.default_domain;
35
+ if (addressArg) {
36
+ const at = addressArg.indexOf('@');
37
+ if (at > 0 && at < addressArg.length - 1) {
38
+ username = addressArg.slice(0, at);
39
+ domain = addressArg.slice(at + 1);
40
+ }
41
+ else {
42
+ error(`"${addressArg}" is not a valid email address. Expected format: user@domain.com`);
43
+ }
44
+ }
45
+ if (!username || !domain) {
46
+ error('Missing required arguments.\nUsage: myapi email mailbox create <user@domain> [--display-name <name>]\n or: myapi email mailbox create --username <u> --domain <d> [--display-name <name>]\n(Or set default: myapi config set-domain <domain>)');
34
47
  }
35
48
  const res = await sdkEmail.createMailbox(config.api_key, domain, username, flags['display-name']);
36
49
  // Backend response sometimes omits address; we know it from the inputs.
@@ -39,7 +52,7 @@ async function mailboxCreate(flags) {
39
52
  async function mailboxList(flags) {
40
53
  const config = requireConfig();
41
54
  if (!flags.domain && flags.filter !== 'unassigned') {
42
- error('Must provide either --domain <domain> or --filter unassigned');
55
+ error('Must provide either --domain <domain> (mailboxes on that domain) or --filter unassigned (mailboxes whose domain is not assigned to any org).');
43
56
  }
44
57
  const mailboxes = await sdkEmail.listMailboxes(config.api_key, {
45
58
  domain: flags.domain,
@@ -59,17 +72,26 @@ async function mailboxActivateSending(flags) {
59
72
  success(`Sending activated: ${address} (status: ${res.status})`);
60
73
  }
61
74
  const MAILBOX_USAGE = {
62
- 'create': 'myapi email mailbox create --domain <domain> --username <user> [--display-name <name>]',
63
- 'list': 'myapi email mailbox list (--domain <domain> | --filter unassigned)',
75
+ 'create': `myapi email mailbox create <user@domain> [--display-name <name>]
76
+ myapi email mailbox create --username <u> --domain <d> [--display-name <name>]
77
+
78
+ Either form works. The positional address is the recommended shape and
79
+ matches the rest of the CLI ("first required arg is positional").`,
80
+ 'list': `myapi email mailbox list (--domain <domain> | --filter unassigned)
81
+
82
+ --domain <domain> List mailboxes on the given domain.
83
+ --filter unassigned List mailboxes whose domain is NOT assigned to any
84
+ org (orphaned mailboxes — domain was unassigned
85
+ without first deleting them).`,
64
86
  'activate-sending': 'myapi email mailbox activate-sending --address <email>',
65
87
  };
66
- async function mailboxRun(sub, flags) {
88
+ async function mailboxRun(sub, args, flags) {
67
89
  if (!sub || (flags.help && !sub)) {
68
90
  info(`Usage: myapi email mailbox <subcommand>
69
91
 
70
92
  Subcommands:
71
- create Create a mailbox on a domain
72
- list List mailboxes (--domain or --filter unassigned)
93
+ create Create a mailbox (positional <user@domain> or --username/--domain)
94
+ list List mailboxes on a domain (--domain) or orphaned ones (--filter unassigned)
73
95
  activate-sending Activate outbound sending for a mailbox`);
74
96
  return;
75
97
  }
@@ -82,7 +104,7 @@ Subcommands:
82
104
  return;
83
105
  }
84
106
  switch (sub) {
85
- case 'create': return mailboxCreate(flags);
107
+ case 'create': return mailboxCreate(args[0], flags);
86
108
  case 'list': return mailboxList(flags);
87
109
  case 'activate-sending': return mailboxActivateSending(flags);
88
110
  default: error(`Unknown subcommand: ${sub}. Run "myapi email mailbox --help" for the list.`);
@@ -243,37 +265,28 @@ async function warmupRun(sub, args, flags) {
243
265
  }
244
266
  }
245
267
  // ─── Templates ───────────────────────────────────────────────────────────────
246
- async function templateGenerate(flags) {
268
+ async function templateGenerate(nameArg, flags) {
247
269
  const config = requireConfig();
248
- const orgId = requireOrg(flags, config, 'myapi email template generate --prompt <str> --name <str> [--org <id>]');
249
- if (!flags.prompt || !flags.name) {
250
- error('Missing required arguments.\nUsage: myapi email template generate --prompt <str> --name <str> [--org <id>]');
270
+ const orgId = requireOrg(flags, config, 'myapi email template generate <name> --prompt <str> [--org <id>]');
271
+ const name = nameArg || flags.name;
272
+ if (!name || !flags.prompt) {
273
+ error('Missing required arguments.\nUsage: myapi email template generate <name> --prompt <str> [--org <id>]\n or: myapi email template generate --name <name> --prompt <str> [--org <id>]');
251
274
  }
252
275
  const job = await sdkEmail.generateTemplate(config.api_key, orgId, {
253
276
  prompt: flags.prompt,
254
- name: flags.name,
277
+ name,
255
278
  });
256
- process.stdout.write('Generating template ');
257
- const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
258
- let i = 0;
259
- let elapsed = 0;
260
- while (elapsed < 90000) {
261
- const status = await sdkEmail.getTemplateJobStatus(config.api_key, orgId, job.job_id);
262
- if (status.status === 'completed') {
263
- process.stdout.write('\r\x1b[K');
264
- success(`Template generated! ID: ${status.template_id}\nPreview: ${status.result?.preview_url}`);
265
- return;
266
- }
267
- if (status.status === 'failed') {
268
- process.stdout.write('\r\x1b[K');
269
- error('Template generation failed');
270
- }
271
- process.stdout.write(`\rGenerating template ${chars[i++ % chars.length]}`);
272
- await sleep(3000);
273
- elapsed += 3000;
274
- }
275
- process.stdout.write('\r\x1b[K');
276
- error('Template generation timed out');
279
+ const status = await pollJob({
280
+ label: 'Generating template',
281
+ timeoutMs: 90_000,
282
+ intervalMs: 3000,
283
+ check: () => sdkEmail.getTemplateJobStatus(config.api_key, orgId, job.job_id),
284
+ isDone: s => s.status === 'completed',
285
+ isFailed: s => s.status === 'failed',
286
+ failedMessage: 'Template generation failed',
287
+ timeoutMessage: 'Template generation timed out',
288
+ });
289
+ success(`Template generated! ID: ${status.template_id}\nPreview: ${status.result?.preview_url}`);
277
290
  }
278
291
  async function templateList(flags) {
279
292
  const config = requireConfig();
@@ -313,7 +326,10 @@ async function templateDelete(id, flags) {
313
326
  success(`Deleted template ${id}`);
314
327
  }
315
328
  const TEMPLATE_USAGE = {
316
- 'generate': 'myapi email template generate --prompt <str> --name <str> [--org <id>]',
329
+ 'generate': `myapi email template generate <name> --prompt <str> [--org <id>]
330
+ myapi email template generate --name <name> --prompt <str> [--org <id>]
331
+
332
+ Either form works; the positional name is the recommended shape.`,
317
333
  'list': 'myapi email template list [--org <id>] [--json]',
318
334
  'edit': 'myapi email template edit <id> --prompt <str> [--org <id>]',
319
335
  'send-test': 'myapi email template send-test <template_id> --to <email> [--org <id>]',
@@ -324,7 +340,7 @@ async function templateRun(sub, args, flags) {
324
340
  info(`Usage: myapi email template <subcommand>
325
341
 
326
342
  Subcommands:
327
- generate Generate AI template (--prompt --name)
343
+ generate <name> Generate AI template (--prompt)
328
344
  list List templates
329
345
  edit <id> Edit a template via prompt
330
346
  send-test <id> Send a test email for a template (--to)
@@ -342,7 +358,7 @@ All commands accept --org <id> (or set default: myapi config set-org <id>).`);
342
358
  return;
343
359
  }
344
360
  switch (sub) {
345
- case 'generate': return templateGenerate(flags);
361
+ case 'generate': return templateGenerate(args[0], flags);
346
362
  case 'list': return templateList(flags);
347
363
  case 'edit': return templateEdit(args[0], flags);
348
364
  case 'send-test': return templateSendTest(args[0], flags);
@@ -351,15 +367,16 @@ All commands accept --org <id> (or set default: myapi config set-org <id>).`);
351
367
  }
352
368
  }
353
369
  // ─── Campaigns ───────────────────────────────────────────────────────────────
354
- async function campaignCreate(flags) {
370
+ async function campaignCreate(nameArg, flags) {
355
371
  const config = requireConfig();
356
- const orgId = requireOrg(flags, config, 'myapi email campaign create --name <str> --template-id <id> --from <email> [--per-day <n>] [--org <id>]');
357
- if (!flags.name || !flags['template-id'] || !flags.from) {
358
- error('Missing required arguments.\nUsage: myapi email campaign create --name <str> --template-id <id> --from <email> [--per-day <n>] [--org <id>]');
372
+ const orgId = requireOrg(flags, config, 'myapi email campaign create <name> --template-id <id> --from <email> [--per-day <n>] [--org <id>]');
373
+ const name = nameArg || flags.name;
374
+ if (!name || !flags['template-id'] || !flags.from) {
375
+ error('Missing required arguments.\nUsage: myapi email campaign create <name> --template-id <id> --from <email> [--per-day <n>] [--org <id>]\n or: myapi email campaign create --name <name> --template-id <id> --from <email> [--per-day <n>] [--org <id>]');
359
376
  }
360
377
  const perDayLimit = flags['per-day'];
361
378
  const res = await sdkEmail.createCampaign(config.api_key, orgId, {
362
- name: flags.name,
379
+ name,
363
380
  template_id: flags['template-id'],
364
381
  from_address: flags.from,
365
382
  per_day_limit: perDayLimit,
@@ -446,7 +463,10 @@ async function campaignStats(id, flags) {
446
463
  printJson(stats);
447
464
  }
448
465
  const CAMPAIGN_USAGE = {
449
- 'create': 'myapi email campaign create --name <str> --template-id <id> --from <email> [--per-day <n>] [--org <id>]',
466
+ 'create': `myapi email campaign create <name> --template-id <id> --from <email> [--per-day <n>] [--org <id>]
467
+ myapi email campaign create --name <name> --template-id <id> --from <email> [--per-day <n>] [--org <id>]
468
+
469
+ Either form works; the positional name is the recommended shape.`,
450
470
  'list': 'myapi email campaign list [--org <id>]',
451
471
  'get': 'myapi email campaign get <id> [--org <id>]',
452
472
  'update': 'myapi email campaign update <id> [--name <str>] [--per-day <n>] [--org <id>]',
@@ -461,7 +481,7 @@ async function campaignRun(sub, args, flags) {
461
481
  info(`Usage: myapi email campaign <subcommand>
462
482
 
463
483
  Subcommands:
464
- create Create a campaign (--name --template-id --from)
484
+ create <name> Create a campaign (--template-id --from)
465
485
  list List campaigns
466
486
  get <id> Get a single campaign
467
487
  update <id> Patch name / per-day-limit
@@ -483,7 +503,7 @@ All commands accept --org <id> (or set default: myapi config set-org <id>).`);
483
503
  return;
484
504
  }
485
505
  switch (sub) {
486
- case 'create': return campaignCreate(flags);
506
+ case 'create': return campaignCreate(args[0], flags);
487
507
  case 'list': return campaignList(flags);
488
508
  case 'get': return campaignGet(args[0], flags);
489
509
  case 'update': return campaignUpdate(args[0], flags);
@@ -556,7 +576,7 @@ Run "myapi email <namespace> --help" for namespace-specific commands.`);
556
576
  }
557
577
  async function dispatchNamespace(ns, sub, restArgs, flags) {
558
578
  switch (ns) {
559
- case 'mailbox': return mailboxRun(sub, flags);
579
+ case 'mailbox': return mailboxRun(sub, restArgs, flags);
560
580
  case 'message': return messageRun(sub, restArgs, flags);
561
581
  case 'warmup': return warmupRun(sub, restArgs, flags);
562
582
  case 'template': return templateRun(sub, restArgs, flags);
@@ -6,5 +6,5 @@ export declare function create(flags: Flags): Promise<void>;
6
6
  export declare function get(id: string, flags: Flags): Promise<void>;
7
7
  export declare function del(id: string, flags: Flags): Promise<void>;
8
8
  export declare function push(slug: string, flags: Flags): Promise<void>;
9
- export declare function verify(id: string, flags: Flags): Promise<void>;
9
+ export declare function verify(slug: string, flags: Flags): Promise<void>;
10
10
  export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
@@ -43,6 +43,10 @@ export async function get(id, flags) {
43
43
  if (!id)
44
44
  error('Missing required arguments.\nUsage: myapi funnel get <id> [--org <id>]');
45
45
  const raw = await sdkFunnel.getFunnel(config.api_key, orgId, id);
46
+ // Backend response shape varies between two layouts:
47
+ // 1) { funnel: {...}, subdomain_url, domain_url } (newer, wrapped)
48
+ // 2) { id, ..., subdomain_url, domain_url } (older, flat)
49
+ // Coalesce both so this works regardless of which shape we get back.
46
50
  const funnel = raw.funnel ?? raw;
47
51
  const subdomain_url = raw.subdomain_url ?? funnel.subdomain_url;
48
52
  const domain_url = raw.domain_url ?? funnel.domain_url;
@@ -114,15 +118,24 @@ export async function push(slug, flags) {
114
118
  }
115
119
  }
116
120
  }
117
- export async function verify(id, flags) {
121
+ // Verify uses the same shape as push: slug positional (default '/'), funnel
122
+ // resolved from --funnel / default / the org's only funnel.
123
+ export async function verify(slug, flags) {
118
124
  const config = requireConfig();
119
- const orgId = requireOrg(flags, config, 'myapi funnel verify <id> [--slug <slug>] [--org <id>]');
120
- if (!id)
121
- error('Missing required arguments.\nUsage: myapi funnel verify <id> [--slug <slug>] [--org <id>]');
122
- const opts = {};
123
- if (flags.slug)
124
- opts.slug = flags.slug;
125
- const v = await sdkFunnel.verifyFunnel(config.api_key, orgId, id, opts);
125
+ const orgId = requireOrg(flags, config, 'myapi funnel verify [slug] [--funnel <id>] [--org <id>]');
126
+ const rawSlug = slug || flags.slug || '/';
127
+ const finalSlug = rawSlug.startsWith('/') ? rawSlug : `/${rawSlug}`;
128
+ let funnelId = flags.funnel || config.default_funnel;
129
+ if (!funnelId) {
130
+ const existing = await sdkFunnel.listFunnels(config.api_key, orgId);
131
+ if (existing.length === 0)
132
+ error('No funnel found for this org. Create one with: myapi funnel create');
133
+ if (existing.length > 1) {
134
+ error(`Multiple funnels exist for this org and no default is set.\nPick one with --funnel <id>, or set a default:\n myapi config set-funnel <id>\n\nFunnels:\n${existing.map(f => ` ${f.id}`).join('\n')}`);
135
+ }
136
+ funnelId = existing[0].id;
137
+ }
138
+ const v = await sdkFunnel.verifyFunnel(config.api_key, orgId, funnelId, { slug: finalSlug });
126
139
  printJson(v);
127
140
  }
128
141
  // ── Dispatcher ───────────────────────────────────────────────────────────────
@@ -141,7 +154,16 @@ Examples:
141
154
  cat about.html | myapi funnel push /about
142
155
  myapi funnel push < index.html
143
156
  cat p.html | myapi funnel push /pricing --funnel <uuid>`,
144
- 'verify': 'myapi funnel verify <id> [--slug <slug>] [--org <id>]',
157
+ 'verify': `myapi funnel verify [slug] [--funnel <id>] [--org <id>]
158
+
159
+ Verifies the page at <slug> on a funnel. Slug defaults to '/'. Funnel is
160
+ resolved from --funnel, the default funnel, or (only if the org has exactly
161
+ one) auto-picked — same shape as "funnel push".
162
+
163
+ Examples:
164
+ myapi funnel verify # verifies '/' on the default funnel
165
+ myapi funnel verify /pricing
166
+ myapi funnel verify / --funnel <uuid>`,
145
167
  };
146
168
  export async function run(subcommand, args, flags) {
147
169
  if (!subcommand || (flags.help && !subcommand)) {
@@ -5,4 +5,4 @@ export declare function run(subcommand: string | undefined, args: string[], flag
5
5
  export declare function runApiKeys(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
6
6
  export declare function createNew(flags: Flags): Promise<void>;
7
7
  export declare function list(flags: Flags): Promise<void>;
8
- export declare function revoke(id: string, flags: Flags): Promise<void>;
8
+ export declare function revoke(id: string, _flags: Flags): Promise<void>;
@@ -2,7 +2,8 @@ import { hq } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
3
  import { success, error, printTable, info, printJson } from '../output.js';
4
4
  import { formatDate } from '../utils.js';
5
- import * as readline from 'readline';
5
+ import { ask } from '../prompt.js';
6
+ import { requireArg } from '../helpers.js';
6
7
  export const SCHEMA = {
7
8
  name: 'string',
8
9
  };
@@ -62,11 +63,8 @@ export async function runApiKeys(subcommand, args, flags) {
62
63
  export async function createNew(flags) {
63
64
  const config = requireConfig();
64
65
  let name = flags.name || '';
65
- if (!name) {
66
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
67
- name = await new Promise(resolve => rl.question('Enter a name for the new key: ', resolve));
68
- rl.close();
69
- }
66
+ if (!name)
67
+ name = await ask('Enter a name for the new key: ');
70
68
  if (!name.trim())
71
69
  error('Key name cannot be empty. Use --name <name>');
72
70
  const keyInfo = await hq.createApiKey(config.api_key, name.trim());
@@ -91,9 +89,8 @@ export async function list(flags) {
91
89
  empty: 'No API keys yet. Create one with: myapi keys create',
92
90
  });
93
91
  }
94
- export async function revoke(id, flags) {
95
- if (!id)
96
- error('Missing key ID. Usage: myapi keys revoke <id>');
92
+ export async function revoke(id, _flags) {
93
+ requireArg(id, 'id', 'myapi keys revoke <id>');
97
94
  const config = requireConfig();
98
95
  await hq.revokeApiKey(config.api_key, id);
99
96
  success(`Key ${id} revoked successfully.`);