@myapihq/cli 1.3.12 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) 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.js +1 -1
  6. package/dist/commands/config.js +1 -1
  7. package/dist/commands/container.d.ts +1 -0
  8. package/dist/commands/container.js +115 -11
  9. package/dist/commands/domain.js +2 -2
  10. package/dist/commands/email/index.js +0 -13
  11. package/dist/commands/fn.d.ts +1 -0
  12. package/dist/commands/fn.js +64 -12
  13. package/dist/commands/keys.js +3 -3
  14. package/dist/commands/queue.d.ts +1 -0
  15. package/dist/commands/queue.js +10 -0
  16. package/dist/commands/setup.js +8 -8
  17. package/dist/commands/status.d.ts +1 -1
  18. package/dist/commands/status.js +11 -27
  19. package/dist/commands/storage.js +3 -1
  20. package/dist/commands/workflow.js +21 -6
  21. package/dist/completion.js +5 -4
  22. package/dist/config.js +1 -1
  23. package/dist/exposes.test.js +1 -2
  24. package/dist/index.js +31 -28
  25. package/dist/registrant.js +5 -5
  26. package/dist/sdk-queue.test.js +3 -2
  27. package/dist/skills/my-api-hq/SKILL.md +15 -12
  28. package/dist/skills/my-auth-api/README.md +33 -0
  29. package/dist/skills/my-auth-api/SKILL.md +112 -0
  30. package/dist/skills/my-auth-api/claude/.claude-plugin/plugin.json +6 -0
  31. package/dist/skills/my-auth-api/openapi/.gitkeep +0 -0
  32. package/dist/skills/my-crm-api/SKILL.md +1 -1
  33. package/dist/skills/my-domain-api/SKILL.md +4 -4
  34. package/dist/skills/my-email-api/README.md +1 -1
  35. package/dist/skills/my-email-api/SKILL.md +7 -18
  36. package/dist/skills/my-email-api/claude/.claude-plugin/plugin.json +1 -1
  37. package/dist/skills/my-email-verify-api/SKILL.md +1 -1
  38. package/dist/skills/my-email-verify-api/claude/.claude-plugin/plugin.json +1 -1
  39. package/dist/skills/my-git-api/README.md +43 -0
  40. package/dist/skills/my-git-api/SKILL.md +115 -0
  41. package/dist/skills/my-git-api/claude/.claude-plugin/plugin.json +6 -0
  42. package/dist/skills/my-git-api/openapi/.gitkeep +0 -0
  43. package/dist/skills/my-llm-api/claude/.claude-plugin/plugin.json +1 -1
  44. package/package.json +5 -4
  45. package/dist/commands/auth.d.ts +0 -11
  46. package/dist/commands/auth.js +0 -345
  47. package/dist/commands/email/campaign.d.ts +0 -4
  48. package/dist/commands/email/campaign.js +0 -200
@@ -15,6 +15,8 @@ export const EXPOSES = [
15
15
  ];
16
16
  export const SCHEMA = {
17
17
  cron: 'string',
18
+ scope: 'string',
19
+ set: 'string',
18
20
  };
19
21
  // Backend: Story 1 (function CRUD + scoped key) and Story 2/4/5 (deploy a
20
22
  // JS bundle to Cloudflare Workers, list runs, set env secrets).
@@ -67,6 +69,14 @@ export async function create(flags) {
67
69
  };
68
70
  if (cron)
69
71
  payload.cron_schedule = cron;
72
+ // --scope narrows the minted key's slot grants. Repeatable, but the flag
73
+ // parser keeps only the last occurrence, so we also accept a comma-joined
74
+ // list (e.g. --scope email,storage). Grants can never exceed the caller's.
75
+ if (typeof flags.scope === 'string') {
76
+ const scopes = flags.scope.split(',').map(s => s.trim()).filter(Boolean);
77
+ if (scopes.length > 0)
78
+ payload.scopes = scopes;
79
+ }
70
80
  const result = await sdkFn.createFunction(config.api_key, orgId, payload);
71
81
  success(`Function created: ${result.function.id}`);
72
82
  info(`Name: ${result.function.name}`);
@@ -77,7 +87,8 @@ export async function create(flags) {
77
87
  info('');
78
88
  info(`Scoped API key (returned once — save it if you need it):`);
79
89
  info(` ${result.scoped_api_key}`);
80
- info(` (id: ${result.scoped_api_key_id}; scopes: slot_call; rejected at /hq/*, /admin/*, /internal/*)`);
90
+ const scopeNote = payload.scopes && payload.scopes.length > 0 ? `slots: ${payload.scopes.join(', ')}` : 'scopes: slot_call';
91
+ info(` (id: ${result.scoped_api_key_id}; ${scopeNote}; rejected at /hq/*, /admin/*, /internal/*)`);
81
92
  if (!result.function.invocation_url) {
82
93
  info('');
83
94
  banner(`Next: deploy code with myapi fn deploy ${result.function.id} <bundle.js>`);
@@ -153,14 +164,48 @@ export async function deploy(id, bundlePath, flags) {
153
164
  info(`Scoped API key was rotated. New value (returned once — save it if you need it):`);
154
165
  info(` ${result.scoped_api_key}`);
155
166
  }
156
- // env sets a Worker Secret (Stripe key, etc.) on a deployed function.
167
+ // _parseSetPairs parses `--set K=V` entries into a map. Accepts a single
168
+ // string (comma-joined: K=V,K2=V2) or an array of strings (when the flag is
169
+ // repeated and the parser preserves them). Returns the map or an error
170
+ // message string (pure form, for tests).
171
+ export function _parseSetPairs(raw) {
172
+ const entries = [];
173
+ for (const chunk of Array.isArray(raw) ? raw : [raw]) {
174
+ for (const pair of chunk.split(',').map(s => s.trim()).filter(Boolean))
175
+ entries.push(pair);
176
+ }
177
+ const env = {};
178
+ for (const pair of entries) {
179
+ const eq = pair.indexOf('=');
180
+ if (eq < 1)
181
+ return `Invalid --set entry "${pair}". Use KEY=VALUE.`;
182
+ env[pair.slice(0, eq)] = pair.slice(eq + 1);
183
+ }
184
+ return env;
185
+ }
186
+ // env sets Worker Secret(s) (Stripe key, etc.) on a deployed function.
187
+ // Single form: myapi fn env <id> <name> <value>
188
+ // Bulk form: myapi fn env <id> --set K=V[,K2=V2 ...]
157
189
  export async function setEnv(id, name, value, flags) {
158
190
  const config = requireConfig();
159
- const orgId = requireOrg(flags, config, 'myapi fn env <id> <name> <value> [--org <id>]');
191
+ const orgId = requireOrg(flags, config, 'myapi fn env <id> <name> <value> | --set K=V [--org <id>]');
160
192
  if (!id)
161
- error('Missing id.\nUsage: myapi fn env <id> <name> <value>');
193
+ error('Missing id.\nUsage: myapi fn env <id> <name> <value> (or --set K=V for bulk)');
194
+ // Bulk path: one --set string (comma-joined) or repeated --set occurrences.
195
+ if (flags.set !== undefined && flags.set !== true) {
196
+ const env = _parseSetPairs(flags.set);
197
+ if (typeof env === 'string')
198
+ error(env);
199
+ if (Object.keys(env).length === 0)
200
+ error('No secrets given. Usage: myapi fn env <id> --set KEY=VALUE');
201
+ const result = await sdkFn.setFunctionEnvBulk(config.api_key, orgId, id, env);
202
+ success(`Set ${result.set} secret${result.set === 1 ? '' : 's'} on function ${id}`);
203
+ info('Values are encrypted at rest by Cloudflare and never stored or echoed by MyAPI.');
204
+ return;
205
+ }
206
+ // Single-secret path (original form).
162
207
  if (!name)
163
- error('Missing secret name.\nUsage: myapi fn env <id> <name> <value>');
208
+ error('Missing secret name.\nUsage: myapi fn env <id> <name> <value> (or --set K=V for bulk)');
164
209
  if (value === undefined)
165
210
  error('Missing secret value.\nUsage: myapi fn env <id> <name> <value>');
166
211
  await sdkFn.setFunctionEnv(config.api_key, orgId, id, name, value);
@@ -191,7 +236,7 @@ export async function runs(id, flags) {
191
236
  }
192
237
  // ── Dispatcher ───────────────────────────────────────────────────────────────
193
238
  const SUBCOMMAND_USAGE = {
194
- 'create': `myapi fn create --name <name> [--cron <expr>] [--org <id>]
239
+ 'create': `myapi fn create --name <name> [--cron <expr>] [--scope <slot>[,<slot>...]] [--org <id>]
195
240
 
196
241
  Persists a function record + issues a scoped API key. Deploy code separately
197
242
  with "myapi fn deploy".
@@ -200,9 +245,15 @@ Triggers:
200
245
  (default) HTTP — function gets a public invocation URL once deployed.
201
246
  --cron <expr> Cron — function runs on the schedule (e.g. "0 8 * * *").
202
247
 
248
+ Scoping:
249
+ --scope <slot> Narrow the minted key's slot grants (comma-separated list,
250
+ e.g. --scope email,storage). Omit to inherit your grants.
251
+ The key can never out-reach the credential that created it.
252
+
203
253
  Examples:
204
254
  myapi fn create --name my-app-api
205
255
  myapi fn create --name daily-report --cron "0 8 * * *"
256
+ myapi fn create --name mailer --scope email,storage
206
257
 
207
258
  The response returns the scoped API key ONCE. It lets the function call
208
259
  other MyAPI slots with its own permissions (scopes=slot_call; rejected at
@@ -218,13 +269,14 @@ once. After deploy the function has a live invocation URL.
218
269
  Example:
219
270
  myapi fn deploy <id> ./dist/bundle.js`,
220
271
  'env': `myapi fn env <id> <name> <value> [--org <id>]
272
+ myapi fn env <id> --set KEY=VALUE[,KEY2=VALUE2 ...] [--org <id>]
221
273
 
222
- Sets a secret (Stripe key, API token, ...) as a Cloudflare Worker Secret on
223
- a deployed function. The value is encrypted at rest and never stored in
224
- MyAPI or echoed back. The function must already be deployed.
274
+ Sets one or more secrets (Stripe key, API token, ...) as Cloudflare Worker
275
+ Secrets on a deployed function. Values are encrypted at rest and never stored
276
+ in MyAPI or echoed back. The function must already be deployed.
225
277
 
226
- Example:
227
- myapi fn env <id> STRIPE_KEY sk_live_...`,
278
+ Single: myapi fn env <id> STRIPE_KEY sk_live_...
279
+ Bulk: myapi fn env <id> --set STRIPE_KEY=sk_live_...,WEBHOOK_SECRET=whsec_...`,
228
280
  'runs': `myapi fn runs <id> [--org <id>] [--json]
229
281
 
230
282
  Lists recent invocation records (most recent first, up to 100).`,
@@ -242,7 +294,7 @@ Subcommands:
242
294
  create Register a function and get its scoped API key (returned once)
243
295
  delete <id> Soft-delete and revoke its scoped API key
244
296
  deploy <id> <file> Upload a JS bundle and go live
245
- env <id> <k> <v> Set a Worker Secret on a deployed function
297
+ env <id> <k> <v> Set a Worker Secret (or --set K=V for bulk) on a deployed function
246
298
  get <id> Inspect a function
247
299
  list List functions in your org
248
300
  runs <id> List recent invocation records
@@ -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,4 +1,4 @@
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.
@@ -7,9 +7,8 @@ import { info, error, printJson } from '../output.js';
7
7
  export const EXPOSES = [
8
8
  'GET /hq/billing/balance',
9
9
  'GET /hq/account/free-tier',
10
- 'GET /domain/orgs/{org_id}/domains',
10
+ 'GET /domain/orgs/{org_id}/list',
11
11
  'GET /funnel/orgs/{org_id}/funnels',
12
- 'GET /email/orgs/{org_id}/campaigns',
13
12
  'GET /webhook/orgs/{org_id}/endpoints',
14
13
  'GET /workflow/orgs/{org_id}/workflows',
15
14
  ];
@@ -20,7 +19,7 @@ export const STATUS_HELP = `Usage: myapi status [--org <id>] [--json]
20
19
 
21
20
  Single-screen view of your account and what's running in the default org:
22
21
  identity, balance, free-tier usage, plus resource counts across domains,
23
- funnels, campaigns, webhooks, and workflows. Useful before a
22
+ funnels, webhooks, and workflows. Useful before a
24
23
  co-build session or when checking "what's mid-flight" without running six
25
24
  list commands.
26
25
 
@@ -37,7 +36,7 @@ export async function run(_subcommand, _args, flags = {}) {
37
36
  }
38
37
  const config = loadConfig();
39
38
  if (!config?.api_key)
40
- error('Not configured. Run: myapi auth setup');
39
+ error('Not configured. Run: myapi account setup');
41
40
  const orgId = flags.org ?? config.default_org;
42
41
  // Account-level info works without an org; org-scoped resources are skipped if none set.
43
42
  const orgKnown = Boolean(orgId);
@@ -55,20 +54,18 @@ export async function run(_subcommand, _args, flags = {}) {
55
54
  // the snapshot as untrustworthy rather than silently printing local config.
56
55
  const keyRejected = accountResults.some(r => r.status === 'rejected' && r.reason instanceof MyApiError && r.reason.status === 401);
57
56
  // Org-scoped (skipped if no default org set).
58
- let domains = null, funnels = null, campaigns = null, webhooks = null, workflows = null;
57
+ let domains = null, funnels = null, webhooks = null, workflows = null;
59
58
  if (orgKnown) {
60
59
  const orgResults = await Promise.allSettled([
61
60
  domain.listDomains(config.api_key, orgId),
62
61
  funnel.listFunnels(config.api_key, orgId),
63
- email.listCampaigns(config.api_key, orgId),
64
62
  webhook.listEndpoints(config.api_key, orgId),
65
63
  workflow.listWorkflows(config.api_key, orgId),
66
64
  ]);
67
65
  domains = unwrap(orgResults[0]);
68
66
  funnels = unwrap(orgResults[1]);
69
- campaigns = unwrap(orgResults[2]);
70
- webhooks = unwrap(orgResults[3]);
71
- workflows = unwrap(orgResults[4]);
67
+ webhooks = unwrap(orgResults[2]);
68
+ workflows = unwrap(orgResults[3]);
72
69
  }
73
70
  if (flags.json) {
74
71
  printJson({
@@ -83,13 +80,13 @@ export async function run(_subcommand, _args, flags = {}) {
83
80
  balance,
84
81
  free_tier: freeTier,
85
82
  org_resources: orgKnown ? {
86
- domains, funnels, campaigns, webhooks, workflows,
83
+ domains, funnels, webhooks, workflows,
87
84
  } : null,
88
85
  });
89
86
  return;
90
87
  }
91
88
  if (keyRejected) {
92
- info('⚠ The backend rejected your API key. Values may be stale — run: myapi auth setup');
89
+ info('⚠ The backend rejected your API key. Values may be stale — run: myapi account setup');
93
90
  info('');
94
91
  }
95
92
  // ── Identity ──────────────────────────────────────────────────────────────
@@ -101,7 +98,7 @@ export async function run(_subcommand, _args, flags = {}) {
101
98
  info(`Type: ${config.is_anonymous ? 'anonymous' : 'registered'}`);
102
99
  info(`Balance: ${balance ? `${balance.balance_display} | Credits: ${balance.credits_display}` : '—'}`);
103
100
  if (config.is_anonymous) {
104
- info(' → Link an email to unlock $5 free credit + paid actions: myapi auth link <email>');
101
+ info(' → Link an email to unlock $5 free credit + paid actions: myapi account link <email>');
105
102
  }
106
103
  if (Array.isArray(freeTier) && freeTier.length > 0) {
107
104
  const parts = freeTier
@@ -114,7 +111,7 @@ export async function run(_subcommand, _args, flags = {}) {
114
111
  info('');
115
112
  info('Resources:');
116
113
  if (!orgKnown) {
117
- info(' (set a default org to see domains/funnels/campaigns/webhooks/workflows)');
114
+ info(' (set a default org to see domains/funnels/webhooks/workflows)');
118
115
  return;
119
116
  }
120
117
  if (domains) {
@@ -126,19 +123,6 @@ export async function run(_subcommand, _args, flags = {}) {
126
123
  else
127
124
  info(' Domains: —');
128
125
  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
126
  info(` Webhooks: ${webhooks ? webhooks.length : '—'}`);
143
127
  if (workflows) {
144
128
  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',