@myapihq/cli 1.3.13 → 2.1.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 (57) hide show
  1. package/dist/commands/account.d.ts +7 -1
  2. package/dist/commands/account.js +356 -18
  3. package/dist/commands/authproduct.d.ts +7 -0
  4. package/dist/commands/authproduct.js +286 -0
  5. package/dist/commands/billing-auto-recharge.test.d.ts +1 -0
  6. package/dist/commands/billing-auto-recharge.test.js +103 -0
  7. package/dist/commands/billing.d.ts +3 -0
  8. package/dist/commands/billing.js +116 -1
  9. package/dist/commands/config.js +1 -1
  10. package/dist/commands/container.d.ts +1 -0
  11. package/dist/commands/container.js +115 -11
  12. package/dist/commands/doctor-setup.test.js +86 -1
  13. package/dist/commands/doctor.d.ts +8 -0
  14. package/dist/commands/doctor.js +70 -15
  15. package/dist/commands/domain.js +2 -2
  16. package/dist/commands/email/index.js +0 -13
  17. package/dist/commands/fn.d.ts +1 -0
  18. package/dist/commands/fn.js +64 -12
  19. package/dist/commands/keys.js +3 -3
  20. package/dist/commands/queue.d.ts +1 -0
  21. package/dist/commands/queue.js +10 -0
  22. package/dist/commands/setup.js +8 -8
  23. package/dist/commands/status.d.ts +1 -1
  24. package/dist/commands/status.js +23 -27
  25. package/dist/commands/storage.js +3 -1
  26. package/dist/commands/workflow.js +21 -6
  27. package/dist/completion.js +5 -4
  28. package/dist/config.js +1 -1
  29. package/dist/exposes.test.js +1 -2
  30. package/dist/index.js +57 -33
  31. package/dist/registrant.js +5 -5
  32. package/dist/sdk-queue.test.js +3 -2
  33. package/dist/skills/my-api-hq/README.md +1 -0
  34. package/dist/skills/my-api-hq/SKILL.md +27 -14
  35. package/dist/skills/my-auth-api/README.md +33 -0
  36. package/dist/skills/my-auth-api/SKILL.md +112 -0
  37. package/dist/skills/my-auth-api/claude/.claude-plugin/plugin.json +6 -0
  38. package/dist/skills/my-auth-api/openapi/.gitkeep +0 -0
  39. package/dist/skills/my-crm-api/SKILL.md +1 -1
  40. package/dist/skills/my-domain-api/SKILL.md +5 -5
  41. package/dist/skills/my-email-api/README.md +2 -3
  42. package/dist/skills/my-email-api/SKILL.md +7 -18
  43. package/dist/skills/my-email-api/claude/.claude-plugin/plugin.json +1 -1
  44. package/dist/skills/my-email-verify-api/SKILL.md +1 -1
  45. package/dist/skills/my-email-verify-api/claude/.claude-plugin/plugin.json +1 -1
  46. package/dist/skills/my-funnel-api/SKILL.md +1 -1
  47. package/dist/skills/my-git-api/README.md +43 -0
  48. package/dist/skills/my-git-api/SKILL.md +115 -0
  49. package/dist/skills/my-git-api/claude/.claude-plugin/plugin.json +6 -0
  50. package/dist/skills/my-git-api/openapi/.gitkeep +0 -0
  51. package/dist/skills/my-llm-api/README.md +1 -1
  52. package/dist/skills/my-llm-api/claude/.claude-plugin/plugin.json +1 -1
  53. package/package.json +2 -2
  54. package/dist/commands/auth.d.ts +0 -11
  55. package/dist/commands/auth.js +0 -345
  56. package/dist/commands/email/campaign.d.ts +0 -4
  57. package/dist/commands/email/campaign.js +0 -200
@@ -162,7 +162,7 @@ export async function revokeAll(flags) {
162
162
  const res = await hq.revokeAllKeys(config.api_key, kind);
163
163
  success(`Revoked ${res.revoked} key(s).`);
164
164
  if (!kind) {
165
- info('Your current key was revoked too — re-authenticate with: myapi auth setup');
165
+ info('Your current key was revoked too — re-authenticate with: myapi account setup');
166
166
  }
167
167
  }
168
168
  // ── Dispatcher ───────────────────────────────────────────────────────────────
@@ -186,7 +186,7 @@ create flags:
186
186
  --org <org_id> Lock the key to one org. Omit for account-wide.
187
187
  --spend-cap <usd> Per-key spend ceiling in dollars, e.g. --spend-cap 50
188
188
 
189
- ${prefix === 'keys' ? 'Alias for: myapi auth api-keys' : 'Alias: myapi keys <subcommand>'}`;
189
+ ${prefix === 'keys' ? 'Alias for: myapi account api-keys' : 'Alias: myapi keys <subcommand>'}`;
190
190
  }
191
191
  function subcommandUsage(prefix) {
192
192
  return {
@@ -203,7 +203,7 @@ Omitting --grant mints an unrestricted key. --grant slots: ${[...hq.GRANTABLE_SL
203
203
  'revoke-all': `myapi ${prefix} revoke-all [--kind function|manual|account] [--yes]
204
204
 
205
205
  Kill switch. With no --kind, revokes EVERY active key in the account — including
206
- the key this CLI is authenticated with (recovery: myapi auth setup). --kind
206
+ the key this CLI is authenticated with (recovery: myapi account setup). --kind
207
207
  narrows it to one provenance class. Destructive — confirms unless --yes.`,
208
208
  };
209
209
  }
@@ -11,4 +11,5 @@ export declare function get(name: string, flags: Flags): Promise<void>;
11
11
  export declare function enqueue(name: string, flags: Flags): Promise<void>;
12
12
  export declare function jobs(name: string, flags: Flags): Promise<void>;
13
13
  export declare function job(jobId: string, flags: Flags): Promise<void>;
14
+ export declare function del(name: string, flags: Flags): Promise<void>;
14
15
  export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
@@ -165,11 +165,19 @@ export async function job(jobId, flags) {
165
165
  if (j.last_error)
166
166
  info(`Error: ${j.last_error}`);
167
167
  }
168
+ export async function del(name, flags) {
169
+ const config = requireConfig();
170
+ const orgId = requireOrg(flags, config, 'myapi queue delete <name> [--org <id>]');
171
+ requireArg(name, 'name', 'myapi queue delete <name>');
172
+ await sdkQueue.deleteQueue(config.api_key, orgId, name);
173
+ success(`Deleted queue: ${name}`);
174
+ }
168
175
  // ── Dispatcher ───────────────────────────────────────────────────────────────
169
176
  const SUBCOMMAND_USAGE = {
170
177
  'create': 'myapi queue create <name> --consumer-url <url> [--max-attempts <n>] [--max-concurrency <n>] [--org <id>]',
171
178
  'list': 'myapi queue list [--org <id>] [--json]',
172
179
  'get': 'myapi queue get <name> [--org <id>] [--json]',
180
+ 'delete': 'myapi queue delete <name> [--org <id>]',
173
181
  'enqueue': 'myapi queue enqueue <name> --payload <json> [--dedup-key <k>] [--delay <seconds>] [--depends-on <id,id>] [--org <id>]',
174
182
  'jobs': 'myapi queue jobs <name> [--status <s>] [--limit <n>] [--org <id>] [--json]',
175
183
  'job': 'myapi queue job <job_id> [--org <id>] [--json]',
@@ -185,6 +193,7 @@ Queues:
185
193
  create <name> Create a queue (--consumer-url, --max-attempts, --max-concurrency)
186
194
  list List queues
187
195
  get <name> Show a queue's policy
196
+ delete <name> Delete a queue (and its jobs)
188
197
 
189
198
  Jobs:
190
199
  enqueue <name> Enqueue a job (--payload, --dedup-key, --delay, --depends-on)
@@ -208,6 +217,7 @@ All commands accept --org <id> (or set default: myapi config set-org <id>).`);
208
217
  case 'create': return create(args[0], flags);
209
218
  case 'list': return list(flags);
210
219
  case 'get': return get(args[0], flags);
220
+ case 'delete': return del(args[0], flags);
211
221
  case 'enqueue': return enqueue(args[0], flags);
212
222
  case 'jobs': return jobs(args[0], flags);
213
223
  case 'job': return job(args[0], flags);
@@ -172,10 +172,10 @@ async function anonymousFlow() {
172
172
  // ---------------------------------------------------------------------------
173
173
  // Main setup command
174
174
  // ---------------------------------------------------------------------------
175
- const IMPORT_KEY_HELP = `Usage: myapi auth import-key <api_key> [--install-skills] [--no-skills]
175
+ const IMPORT_KEY_HELP = `Usage: myapi account import-key <api_key> [--install-skills] [--no-skills]
176
176
 
177
177
  Imports an existing API key non-interactively. Use this in CI, Docker, or any
178
- environment where the interactive "myapi auth setup" flow is not practical.
178
+ environment where the interactive "myapi account setup" flow is not practical.
179
179
 
180
180
  The key is validated against the API before being saved. Your default org and
181
181
  funnel are auto-detected from the account and written to the local config.
@@ -185,9 +185,9 @@ Flags:
185
185
  --no-skills Skip skills installation even if previously installed
186
186
 
187
187
  Examples:
188
- myapi auth import-key hq_live_xxxxxxxxxxxxxxxxxxxx
189
- myapi auth import-key hq_live_xxxxxxxxxxxxxxxxxxxx --install-skills`;
190
- const SETUP_HELP = `Usage: myapi auth setup [--anonymous] [--yes] [--install-skills|--no-skills]
188
+ myapi account import-key hq_live_xxxxxxxxxxxxxxxxxxxx
189
+ myapi account import-key hq_live_xxxxxxxxxxxxxxxxxxxx --install-skills`;
190
+ const SETUP_HELP = `Usage: myapi account setup [--anonymous] [--yes] [--install-skills|--no-skills]
191
191
 
192
192
  Configures your account and stores your default org and funnel so you don't
193
193
  need to pass --org or --funnel on every command.
@@ -197,7 +197,7 @@ Flags:
197
197
  --yes Skip confirmation prompts
198
198
  --install-skills Auto-install skills pack
199
199
  --no-skills Skip skills installation`;
200
- // myapi auth import-key <key> — non-interactively import a raw API key.
200
+ // myapi account import-key <key> — non-interactively import a raw API key.
201
201
  export async function importKey(apiKey, flags) {
202
202
  if (flags.help || !apiKey) {
203
203
  info(IMPORT_KEY_HELP);
@@ -278,7 +278,7 @@ export async function setup(flags = {}) {
278
278
  }
279
279
  else {
280
280
  info('› Continuing as anonymous — sites ship to *.makeautonomous.com');
281
- info(' (Anonymous accounts have $0 credit. Link an email anytime to unlock $5 free credit and paid actions: myapi auth link <email>)');
281
+ info(' (Anonymous accounts have $0 credit. Link an email anytime to unlock $5 free credit and paid actions: myapi account link <email>)');
282
282
  const data = await anonymousFlow();
283
283
  apiKey = data.api_key;
284
284
  accountId = data.account_id;
@@ -320,7 +320,7 @@ export async function setup(flags = {}) {
320
320
  success('› Setup complete. No card, no email, ready to ship.');
321
321
  if (subdomainUrl)
322
322
  info(`› Your funnel: ${subdomainUrl}`);
323
- info('› Upgrade anytime — myapi auth link');
323
+ info('› Upgrade anytime — myapi account link');
324
324
  }
325
325
  else {
326
326
  success(`› Setup complete. Org ${defaultOrg} ready · $5.00 free credits added.`);
@@ -3,5 +3,5 @@ import { type Flags } from '../helpers.js';
3
3
  import type { Exposes } from '../exposes.js';
4
4
  export declare const EXPOSES: Exposes;
5
5
  export declare const SCHEMA: FlagSchema;
6
- export declare const STATUS_HELP = "Usage: myapi status [--org <id>] [--json]\n\nSingle-screen view of your account and what's running in the default org:\nidentity, balance, free-tier usage, plus resource counts across domains,\nfunnels, campaigns, webhooks, and workflows. Useful before a\nco-build session or when checking \"what's mid-flight\" without running six\nlist commands.\n\nEach section is best-effort \u2014 a failing service is shown as \"\u2014\", not an\nerror, so the rest of the snapshot still prints.\n";
6
+ export declare const STATUS_HELP = "Usage: myapi status [--org <id>] [--json]\n\nSingle-screen view of your account and what's running in the default org:\nidentity, balance, free-tier usage, plus resource counts across domains,\nfunnels, webhooks, and workflows. Useful before a\nco-build session or when checking \"what's mid-flight\" without running six\nlist commands.\n\nEach section is best-effort \u2014 a failing service is shown as \"\u2014\", not an\nerror, so the rest of the snapshot still prints.\n";
7
7
  export declare function run(_subcommand: string | undefined, _args: string[], flags?: Flags): Promise<void>;
@@ -1,15 +1,16 @@
1
- import { hq, domain, funnel, email, webhook, workflow, MyApiError } from '@myapihq/sdk';
1
+ import { hq, domain, funnel, webhook, workflow, MyApiError } from '@myapihq/sdk';
2
2
  // listMailboxes is omitted from this view: the backend requires a domain or
3
3
  // filter, so there is no global "all mailboxes in this org" call. Use
4
4
  // `myapi email mailbox list --domain <d>` for per-domain detail.
5
5
  import { loadConfig } from '../config.js';
6
+ import { autoRechargeSummary } from './billing.js';
6
7
  import { info, error, printJson } from '../output.js';
7
8
  export const EXPOSES = [
8
9
  'GET /hq/billing/balance',
10
+ 'GET /hq/billing/auto-recharge',
9
11
  'GET /hq/account/free-tier',
10
- 'GET /domain/orgs/{org_id}/domains',
12
+ 'GET /domain/orgs/{org_id}/list',
11
13
  'GET /funnel/orgs/{org_id}/funnels',
12
- 'GET /email/orgs/{org_id}/campaigns',
13
14
  'GET /webhook/orgs/{org_id}/endpoints',
14
15
  'GET /workflow/orgs/{org_id}/workflows',
15
16
  ];
@@ -20,7 +21,7 @@ export const STATUS_HELP = `Usage: myapi status [--org <id>] [--json]
20
21
 
21
22
  Single-screen view of your account and what's running in the default org:
22
23
  identity, balance, free-tier usage, plus resource counts across domains,
23
- funnels, campaigns, webhooks, and workflows. Useful before a
24
+ funnels, webhooks, and workflows. Useful before a
24
25
  co-build session or when checking "what's mid-flight" without running six
25
26
  list commands.
26
27
 
@@ -37,7 +38,7 @@ export async function run(_subcommand, _args, flags = {}) {
37
38
  }
38
39
  const config = loadConfig();
39
40
  if (!config?.api_key)
40
- error('Not configured. Run: myapi auth setup');
41
+ error('Not configured. Run: myapi account setup');
41
42
  const orgId = flags.org ?? config.default_org;
42
43
  // Account-level info works without an org; org-scoped resources are skipped if none set.
43
44
  const orgKnown = Boolean(orgId);
@@ -45,8 +46,10 @@ export async function run(_subcommand, _args, flags = {}) {
45
46
  const accountResults = await Promise.allSettled([
46
47
  hq.getBalance(config.api_key),
47
48
  hq.getFreeTier(config.api_key),
49
+ hq.getAutoRecharge(config.api_key),
48
50
  ]);
49
51
  const balance = unwrap(accountResults[0]);
52
+ const autoR = unwrap(accountResults[2]);
50
53
  // The backend currently returns an array of { service, used, allowance }
51
54
  // per-service entries. The SDK type lies about this (claims scalar) — same
52
55
  // bug surfaces in `whoami`. Treat as unknown here and narrow at render time.
@@ -55,20 +58,18 @@ export async function run(_subcommand, _args, flags = {}) {
55
58
  // the snapshot as untrustworthy rather than silently printing local config.
56
59
  const keyRejected = accountResults.some(r => r.status === 'rejected' && r.reason instanceof MyApiError && r.reason.status === 401);
57
60
  // Org-scoped (skipped if no default org set).
58
- let domains = null, funnels = null, campaigns = null, webhooks = null, workflows = null;
61
+ let domains = null, funnels = null, webhooks = null, workflows = null;
59
62
  if (orgKnown) {
60
63
  const orgResults = await Promise.allSettled([
61
64
  domain.listDomains(config.api_key, orgId),
62
65
  funnel.listFunnels(config.api_key, orgId),
63
- email.listCampaigns(config.api_key, orgId),
64
66
  webhook.listEndpoints(config.api_key, orgId),
65
67
  workflow.listWorkflows(config.api_key, orgId),
66
68
  ]);
67
69
  domains = unwrap(orgResults[0]);
68
70
  funnels = unwrap(orgResults[1]);
69
- campaigns = unwrap(orgResults[2]);
70
- webhooks = unwrap(orgResults[3]);
71
- workflows = unwrap(orgResults[4]);
71
+ webhooks = unwrap(orgResults[2]);
72
+ workflows = unwrap(orgResults[3]);
72
73
  }
73
74
  if (flags.json) {
74
75
  printJson({
@@ -81,15 +82,16 @@ export async function run(_subcommand, _args, flags = {}) {
81
82
  key_rejected: keyRejected,
82
83
  },
83
84
  balance,
85
+ auto_recharge: autoR,
84
86
  free_tier: freeTier,
85
87
  org_resources: orgKnown ? {
86
- domains, funnels, campaigns, webhooks, workflows,
88
+ domains, funnels, webhooks, workflows,
87
89
  } : null,
88
90
  });
89
91
  return;
90
92
  }
91
93
  if (keyRejected) {
92
- info('⚠ The backend rejected your API key. Values may be stale — run: myapi auth setup');
94
+ info('⚠ The backend rejected your API key. Values may be stale — run: myapi account setup');
93
95
  info('');
94
96
  }
95
97
  // ── Identity ──────────────────────────────────────────────────────────────
@@ -101,7 +103,14 @@ export async function run(_subcommand, _args, flags = {}) {
101
103
  info(`Type: ${config.is_anonymous ? 'anonymous' : 'registered'}`);
102
104
  info(`Balance: ${balance ? `${balance.balance_display} | Credits: ${balance.credits_display}` : '—'}`);
103
105
  if (config.is_anonymous) {
104
- info(' → Link an email to unlock $5 free credit + paid actions: myapi auth link <email>');
106
+ info(' → Link an email to unlock $5 free credit + paid actions: myapi account link <email>');
107
+ }
108
+ // Auto-recharge: only worth a line when it's on (or off but a refill recently
109
+ // failed/capped and needs attention).
110
+ if (autoR) {
111
+ const summary = autoRechargeSummary(autoR);
112
+ if (summary)
113
+ info(`Recharge: ${summary}`);
105
114
  }
106
115
  if (Array.isArray(freeTier) && freeTier.length > 0) {
107
116
  const parts = freeTier
@@ -114,7 +123,7 @@ export async function run(_subcommand, _args, flags = {}) {
114
123
  info('');
115
124
  info('Resources:');
116
125
  if (!orgKnown) {
117
- info(' (set a default org to see domains/funnels/campaigns/webhooks/workflows)');
126
+ info(' (set a default org to see domains/funnels/webhooks/workflows)');
118
127
  return;
119
128
  }
120
129
  if (domains) {
@@ -126,19 +135,6 @@ export async function run(_subcommand, _args, flags = {}) {
126
135
  else
127
136
  info(' Domains: —');
128
137
  info(` Funnels: ${funnels ? funnels.length : '—'}`);
129
- if (campaigns) {
130
- // Campaigns: backend statuses include 'draft', 'active', 'paused', 'completed'.
131
- // Surface counts for the two states that matter mid-flight.
132
- const active = campaigns.filter(c => /active|running/i.test(c.status)).length;
133
- const paused = campaigns.filter(c => /paused/i.test(c.status)).length;
134
- const extras = [
135
- active > 0 ? `${active} active` : null,
136
- paused > 0 ? `${paused} paused` : null,
137
- ].filter(Boolean).join(', ');
138
- info(` Campaigns: ${campaigns.length}${extras ? ` (${extras})` : ''}`);
139
- }
140
- else
141
- info(' Campaigns: —');
142
138
  info(` Webhooks: ${webhooks ? webhooks.length : '—'}`);
143
139
  if (workflows) {
144
140
  const enabled = workflows.filter(w => w.enabled).length;
@@ -26,6 +26,8 @@ const EXT_TO_CT = {
26
26
  '.jpeg': 'image/jpeg',
27
27
  '.gif': 'image/gif',
28
28
  '.webp': 'image/webp',
29
+ '.svg': 'image/svg+xml',
30
+ '.pdf': 'application/pdf',
29
31
  '.mp4': 'video/mp4',
30
32
  '.webm': 'video/webm',
31
33
  };
@@ -64,7 +66,7 @@ async function upload(filePath, flags) {
64
66
  const ext = extname(filePath).toLowerCase();
65
67
  const contentType = EXT_TO_CT[ext];
66
68
  if (!contentType) {
67
- error(`Unsupported file extension "${ext}". Supported: ${Object.keys(EXT_TO_CT).join(', ')}.\n(SVG and PDF still go through "myapi storage ingest <url>" today.)`);
69
+ error(`Unsupported file extension "${ext}". Supported: ${Object.keys(EXT_TO_CT).join(', ')}.\nFor other types, host the file and use "myapi storage ingest <url>".`);
68
70
  }
69
71
  let data;
70
72
  try {
@@ -19,6 +19,8 @@ export const SCHEMA = {
19
19
  'endpoint-id': 'string',
20
20
  steps: 'string',
21
21
  'no-enable': 'boolean',
22
+ limit: 'number',
23
+ cursor: 'string',
22
24
  };
23
25
  // Mirrors the backend's SupportedStepTypes list. Both alias and underscore
24
26
  // forms are accepted by the workflow runner. Keep this in sync if the
@@ -279,18 +281,27 @@ export async function del(id, flags) {
279
281
  }
280
282
  export async function runs(id, flags) {
281
283
  const config = requireConfig();
282
- const orgId = requireOrg(flags, config, 'myapi workflow runs <id> [--org <id>]');
284
+ const orgId = requireOrg(flags, config, 'myapi workflow runs <id> [--limit <n>] [--cursor <id>] [--org <id>]');
283
285
  if (!id)
284
- error('Missing required arguments.\nUsage: myapi workflow runs <workflow_id> [--org <id>]');
285
- const wRuns = await sdkWorkflow.listWorkflowRuns(config.api_key, orgId, id);
286
+ error('Missing required arguments.\nUsage: myapi workflow runs <workflow_id> [--limit <n>] [--cursor <id>] [--org <id>]');
287
+ const opts = {};
288
+ if (typeof flags.limit === 'number')
289
+ opts.limit = flags.limit;
290
+ if (typeof flags.cursor === 'string')
291
+ opts.cursor = flags.cursor;
292
+ const page = await sdkWorkflow.listWorkflowRuns(config.api_key, orgId, id, opts);
286
293
  if (flags.json) {
287
- printJson(wRuns);
294
+ printJson(page);
288
295
  return;
289
296
  }
290
- printTable(wRuns.map(summarizeRun), {
297
+ printTable(page.runs.map(summarizeRun), {
291
298
  flags,
292
299
  empty: 'No runs yet for this workflow.',
293
300
  });
301
+ if (page.next_cursor) {
302
+ info('');
303
+ info(`More runs available. Next page: myapi workflow runs ${id} --cursor ${page.next_cursor}`);
304
+ }
294
305
  }
295
306
  export async function getRun(runId, flags) {
296
307
  const config = requireConfig();
@@ -360,7 +371,11 @@ See the my-workflow-api skill for the full form-to-email recipe.`,
360
371
  'enable': 'myapi workflow enable <id> [--org <id>]',
361
372
  'disable': 'myapi workflow disable <id> [--org <id>]',
362
373
  'delete': 'myapi workflow delete <id> [--org <id>]',
363
- 'runs': 'myapi workflow runs <workflow_id> [--org <id>] [--json]',
374
+ 'runs': `myapi workflow runs <workflow_id> [--limit <n>] [--cursor <id>] [--org <id>] [--json]
375
+
376
+ Lists recent runs, newest first (keyset-paginated). --limit is 1-500
377
+ (default 100). When more pages exist, the next cursor is printed; pass it
378
+ back with --cursor to fetch the following page.`,
364
379
  'get-run': 'myapi workflow get-run <run_id> [--org <id>]',
365
380
  };
366
381
  export async function run(subcommand, args, flags) {
@@ -35,15 +35,16 @@ export const COMMANDS = [
35
35
  // command → subcommands, for `myapi <command> <TAB>`. Mirrors each
36
36
  // command's dispatcher; commands absent here take no subcommand.
37
37
  export const SUBCOMMANDS = {
38
- account: ['mailing-address'],
39
- auth: ['setup', 'import-key', 'whoami', 'link', 'switch', 'install-skills', 'config', 'registrant', 'api-keys'],
38
+ account: ['setup', 'import-key', 'whoami', 'link', 'switch', 'install-skills', 'config', 'registrant', 'api-keys', 'keys', 'mailing-address'],
39
+ // The end-user auth product. Operator/account commands live under `account`.
40
+ auth: ['tenant', 'client', 'usage', 'domain'],
40
41
  org: ['create', 'delete', 'get', 'import', 'list', 'sync-brand', 'update'],
41
42
  billing: ['balance', 'history', 'usage', 'setup', 'topup', 'spend-cap'],
42
43
  domain: ['assign', 'check', 'email-setup', 'import', 'list', 'mail-server-resync', 'records', 'register', 'renew', 'retry-provisioning', 'settings', 'status'],
43
44
  funnel: ['create', 'delete', 'get', 'list', 'pages', 'publish', 'push', 'verify'],
44
45
  webhook: ['create', 'delete', 'delivery', 'list', 'update'],
45
46
  workflow: ['create', 'delete', 'disable', 'enable', 'get', 'get-run', 'list', 'runs', 'update'],
46
- email: ['mailbox', 'message', 'warmup', 'template', 'campaign', 'verify'],
47
+ email: ['mailbox', 'message', 'warmup', 'template', 'verify'],
47
48
  image: ['delete', 'generate', 'get', 'get-url', 'list', 'models'],
48
49
  storage: ['delete', 'get', 'get-url', 'ingest', 'list', 'upload'],
49
50
  pixel: ['audience', 'events', 'identity', 'interactions', 'visits'],
@@ -60,7 +61,7 @@ export const SUBCOMMANDS = {
60
61
  payments: ['connect', 'status', 'charge', 'list', 'get', 'refund'],
61
62
  container: ['create', 'deploy', 'list', 'get', 'logs', 'domain', 'delete'],
62
63
  git: ['create', 'list', 'get', 'delete', 'refs', 'log', 'show', 'tree', 'blob', 'diff', 'commit', 'create-branch', 'delete-branch', 'tag', 'merge', 'repack'],
63
- queue: ['create', 'list', 'get', 'enqueue', 'jobs', 'job'],
64
+ queue: ['create', 'list', 'get', 'delete', 'enqueue', 'jobs', 'job'],
64
65
  task: ['create', 'list', 'get', 'claim', 'extend', 'resolve', 'fail', 'cancel'],
65
66
  completion: ['install', 'uninstall'],
66
67
  };
package/dist/config.js CHANGED
@@ -98,7 +98,7 @@ export function listAccounts() {
98
98
  export function requireConfig() {
99
99
  const config = loadConfig();
100
100
  if (!config || !config.api_key) {
101
- console.error("No API key found. Provide MYAPI_KEY env var or run: myapi auth setup");
101
+ console.error("No API key found. Provide MYAPI_KEY env var or run: myapi account setup");
102
102
  process.exit(1);
103
103
  }
104
104
  return config;
@@ -7,7 +7,7 @@ import * as url from 'url';
7
7
  // surface.
8
8
  const COMMAND_MODULES = [
9
9
  './commands/account.js',
10
- './commands/auth.js',
10
+ './commands/authproduct.js',
11
11
  './commands/billing.js',
12
12
  './commands/config.js',
13
13
  './commands/crm/index.js',
@@ -19,7 +19,6 @@ const COMMAND_MODULES = [
19
19
  './commands/email/mailbox.js',
20
20
  './commands/email/message.js',
21
21
  './commands/email/template.js',
22
- './commands/email/campaign.js',
23
22
  './commands/email/warmup.js',
24
23
  './commands/email/verify.js',
25
24
  './commands/funnel.js',
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import * as imageCmd from './commands/image.js';
21
21
  import * as storageCmd from './commands/storage.js';
22
22
  import * as pixelCmd from './commands/pixel.js';
23
23
  import * as urlCmd from './commands/url.js';
24
- import * as authCmd from './commands/auth.js';
24
+ import * as authProductCmd from './commands/authproduct.js';
25
25
  import * as configCmd from './commands/config.js';
26
26
  import * as statusCmd from './commands/status.js';
27
27
  import * as peopleCmd from './commands/people.js';
@@ -42,7 +42,7 @@ import * as doctorCmd from './commands/doctor.js';
42
42
  // one command means editing one file (its SCHEMA), not a global allowlist.
43
43
  const COMBINED_SCHEMA = {
44
44
  ...accountCmd.SCHEMA,
45
- ...authCmd.SCHEMA,
45
+ ...authProductCmd.SCHEMA,
46
46
  ...billingCmd.SCHEMA,
47
47
  ...configCmd.SCHEMA,
48
48
  ...domainCmd.SCHEMA,
@@ -153,7 +153,7 @@ async function main() {
153
153
  if (args.length === 0) {
154
154
  const config = loadConfig();
155
155
  if (!config?.api_key) {
156
- info('No account found. Run: myapi auth setup');
156
+ info('No account found. Run: myapi account setup');
157
157
  info('');
158
158
  }
159
159
  printHelp();
@@ -164,7 +164,9 @@ async function main() {
164
164
  try {
165
165
  switch (command) {
166
166
  case 'auth':
167
- await dispatchAuth(subcommand, restArgs, flags);
167
+ // `auth` is the end-user auth product (my-auth-api). Operator/account
168
+ // commands live under `myapi account`.
169
+ await authProductCmd.run(subcommand, restArgs, flags);
168
170
  break;
169
171
  case 'update':
170
172
  await updateCmd.update(flags);
@@ -173,7 +175,7 @@ async function main() {
173
175
  await orgCmd.run(subcommand, restArgs, flags);
174
176
  break;
175
177
  case 'account':
176
- await accountCmd.run(subcommand, restArgs, flags);
178
+ await dispatchAccount(subcommand, restArgs, flags);
177
179
  break;
178
180
  case 'billing':
179
181
  await billingCmd.run(subcommand, restArgs, flags);
@@ -249,7 +251,7 @@ async function main() {
249
251
  await setupCmd.setup(flags);
250
252
  break;
251
253
  case 'whoami':
252
- await authCmd.whoami(flags);
254
+ await accountCmd.whoami(flags);
253
255
  break;
254
256
  case 'status':
255
257
  await statusCmd.run(subcommand, restArgs, flags);
@@ -262,7 +264,7 @@ async function main() {
262
264
  break;
263
265
  case 'install-skills':
264
266
  if (flags.help) {
265
- info(authCmd.INSTALL_SKILLS_HELP);
267
+ info(accountCmd.INSTALL_SKILLS_HELP);
266
268
  break;
267
269
  }
268
270
  await setupCmd.installSkills();
@@ -292,21 +294,42 @@ async function main() {
292
294
  catch (err) {
293
295
  if (err instanceof MyApiError) {
294
296
  if (err.status === 401)
295
- error('Invalid API key. Run: myapi auth setup');
297
+ error('Invalid API key. Run: myapi account setup');
296
298
  else if (err.status === 402) {
299
+ const body = (err.body ?? {});
297
300
  if (err.code === 'REGISTRATION_REQUIRED' || err.code === 'UPGRADE_REQUIRED')
298
- error('A verified email is required. Run: myapi auth link');
301
+ error('A verified email is required. Run: myapi account link');
299
302
  else if (err.code === 'NO_PAYMENT_METHOD')
300
303
  error('No payment method on file. Run: myapi billing setup');
304
+ else if (err.code === 'SPEND_CAP_EXCEEDED') {
305
+ // A self-imposed ceiling, NOT an empty wallet — topping up won't
306
+ // help. Point at the cap, not the balance.
307
+ const cap = typeof body.cap_cents === 'number' ? `$${(body.cap_cents / 100).toFixed(2)}` : 'your cap';
308
+ const spent = typeof body.spent_cents === 'number' ? `$${(body.spent_cents / 100).toFixed(2)} of ` : '';
309
+ const period = typeof body.period === 'string' ? body.period : 'period';
310
+ error(`Spend cap reached (${spent}${cap} this ${period}). This is a ceiling you set, not an empty wallet — raise or clear it: myapi billing spend-cap`);
311
+ }
301
312
  else {
302
- // Anonymous accounts can't top up (no payment surface). The
303
- // natural unblock is to link an email the upgrade grants the
304
- // free credit and opens the paid surface.
313
+ // INSUFFICIENT_FUNDS (or legacy INSUFFICIENT_BALANCE): empty wallet.
314
+ // Tailor the unblock to the auto-recharge state the backend reports
315
+ // so an agent knows whether to wait, fix a card, or top up.
316
+ const state = body.auto_recharge;
305
317
  const cfg = loadConfig();
306
- if (cfg?.is_anonymous) {
307
- error('Insufficient balance. Anonymous accounts have no free credit link an email to unlock $5: myapi auth link <email>');
318
+ if (state === 'in_flight') {
319
+ const secs = typeof body.retry_after_seconds === 'number' ? body.retry_after_seconds : 5;
320
+ error(`Wallet empty — auto-recharge is in flight; retry in ~${secs}s.`);
308
321
  }
309
- error('Insufficient balance. Run: myapi billing topup <amount>');
322
+ else if (state === 'capped')
323
+ error('Wallet empty and auto-recharge hit your monthly cap. Raise it: myapi billing auto-recharge set --monthly-cap <amount>, or top up: myapi billing topup <amount>');
324
+ else if (state === 'failed')
325
+ error('Wallet empty and the last auto-recharge was declined. Fix your card: myapi billing setup, or top up: myapi billing topup <amount>');
326
+ else if (state === 'no_pm')
327
+ error('Wallet empty and auto-recharge has no payment method. Add one: myapi billing setup, or top up: myapi billing topup <amount>');
328
+ // Anonymous accounts can't top up (no payment surface) — link to unlock.
329
+ else if (cfg?.is_anonymous)
330
+ error('Insufficient balance. Anonymous accounts have no free credit — link an email to unlock $5: myapi account link <email>');
331
+ else
332
+ error('Insufficient balance. Top up: myapi billing topup <amount> — or keep it funded automatically: myapi billing auto-recharge set');
310
333
  }
311
334
  }
312
335
  else
@@ -320,33 +343,34 @@ async function main() {
320
343
  }
321
344
  }
322
345
  }
323
- async function dispatchAuth(subcommand, restArgs, flags) {
346
+ async function dispatchAccount(subcommand, restArgs, flags) {
324
347
  if (!subcommand || (flags.help && !subcommand)) {
325
- info(authCmd.HELP);
348
+ info(accountCmd.HELP);
326
349
  return;
327
350
  }
328
351
  switch (subcommand) {
352
+ case 'mailing-address': return accountCmd.run('mailing-address', restArgs, flags);
329
353
  case 'setup': return setupCmd.setup(flags);
330
354
  case 'import-key': return setupCmd.importKey(restArgs[0], flags);
331
- case 'whoami': return authCmd.whoami(flags);
332
- case 'link': return authCmd.link(flags, restArgs[0]);
333
- case 'switch': return authCmd.switchCmd(flags, restArgs[0]);
355
+ case 'whoami': return accountCmd.whoami(flags);
356
+ case 'link': return accountCmd.link(flags, restArgs[0]);
357
+ case 'switch': return accountCmd.switchCmd(flags, restArgs[0]);
334
358
  case 'install-skills':
335
359
  // DEPRECATED: install-skills is now a top-level command. The auth subcommand
336
360
  // form is kept for one minor version with a warning, then will be removed.
337
- banner('› Note: `myapi auth install-skills` is deprecated. Use `myapi install-skills` instead.');
361
+ banner('› Note: `myapi account install-skills` is deprecated. Use `myapi install-skills` instead.');
338
362
  if (flags.help) {
339
- info(authCmd.INSTALL_SKILLS_HELP);
363
+ info(accountCmd.INSTALL_SKILLS_HELP);
340
364
  return;
341
365
  }
342
366
  await setupCmd.installSkills();
343
367
  success('› Skills installed.');
344
368
  return;
345
369
  case 'config': return configCmd.run(restArgs[0], restArgs.slice(1), flags);
346
- case 'registrant': return authCmd.registrant(restArgs[0], flags);
370
+ case 'registrant': return accountCmd.registrant(restArgs[0], flags);
347
371
  case 'api-keys':
348
372
  case 'keys': return keysCmd.runApiKeys(restArgs[0], restArgs.slice(1), flags);
349
- default: info('Unknown subcommand. Run: myapi auth --help');
373
+ default: info('Unknown subcommand. Run: myapi account --help');
350
374
  }
351
375
  }
352
376
  const HELP_TARGETS = {
@@ -372,7 +396,7 @@ const HELP_TARGETS = {
372
396
  task: f => taskCmd.run(undefined, [], f),
373
397
  doctor: f => doctorCmd.run(undefined, [], f),
374
398
  org: f => orgCmd.run(undefined, [], f),
375
- account: f => accountCmd.run(undefined, [], f),
399
+ account: f => dispatchAccount(undefined, [], f),
376
400
  billing: f => billingCmd.run(undefined, [], f),
377
401
  keys: f => keysCmd.run(undefined, [], f),
378
402
  config: f => configCmd.run(undefined, [], f),
@@ -383,7 +407,7 @@ async function dispatchHelp(target) {
383
407
  return;
384
408
  }
385
409
  if (target === 'auth') {
386
- info(authCmd.HELP);
410
+ info(authProductCmd.HELP);
387
411
  return;
388
412
  }
389
413
  const handler = HELP_TARGETS[target];
@@ -398,16 +422,16 @@ function printHelp() {
398
422
  ? `Quick start:
399
423
  echo '<h1>Hello!</h1>' | myapi funnel push /`
400
424
  : `Quick start:
401
- myapi auth setup --help`;
425
+ myapi account setup --help`;
402
426
  info(`myapi - MyAPI command-line interface
403
427
 
404
428
  Usage: myapi <command> [subcommand] [args]
405
429
  myapi --version
406
430
 
407
431
  Commands:
408
- account Account-scoped settings (mailing address)
432
+ account Your MyAPI account · setup · whoami · link · keys · config · mailing-address
409
433
  audience Save filter snapshots as named audiences (people or companies)
410
- auth Manage account · setup · whoami · link
434
+ auth Auth for your app's end users OIDC tenant, clients, usage, custom domain
411
435
  billing Check balance and manage billing
412
436
  company Search the company database (filter by industry, size, country, ...)
413
437
  container Run containers — services, workers, and scheduled jobs
@@ -436,10 +460,10 @@ Commands:
436
460
  workflow Run actions (send email, post to Slack) when a webhook fires
437
461
 
438
462
  Aliases:
439
- config → myapi auth config
440
- keys → myapi auth api-keys
441
- setup → myapi auth setup
442
- whoami → myapi auth whoami
463
+ config → myapi account config
464
+ keys → myapi account api-keys
465
+ setup → myapi account setup
466
+ whoami → myapi account whoami
443
467
 
444
468
  Run "myapi <command> --help" for subcommand help.
445
469