@myapihq/cli 1.1.0-wip.1 → 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.
@@ -45,7 +45,7 @@ async function activateSending(flags) {
45
45
  if (!address)
46
46
  error('Missing required arguments.\nUsage: myapi email mailbox activate-sending --address <email>');
47
47
  const res = await sdkEmail.activateSending(config.api_key, address);
48
- success(`Sending activated: ${address} (status: ${res.status})`);
48
+ success(`Sending activated: ${address} (${res.emails_quota_remaining} emails/day quota)`);
49
49
  }
50
50
  const USAGE = {
51
51
  'create': `myapi email mailbox create <user@domain> [--display-name <name>]
@@ -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>;
@@ -118,15 +118,24 @@ export async function push(slug, flags) {
118
118
  }
119
119
  }
120
120
  }
121
- 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) {
122
124
  const config = requireConfig();
123
- const orgId = requireOrg(flags, config, 'myapi funnel verify <id> [--slug <slug>] [--org <id>]');
124
- if (!id)
125
- error('Missing required arguments.\nUsage: myapi funnel verify <id> [--slug <slug>] [--org <id>]');
126
- const opts = {};
127
- if (flags.slug)
128
- opts.slug = flags.slug;
129
- 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 });
130
139
  printJson(v);
131
140
  }
132
141
  // ── Dispatcher ───────────────────────────────────────────────────────────────
@@ -145,7 +154,16 @@ Examples:
145
154
  cat about.html | myapi funnel push /about
146
155
  myapi funnel push < index.html
147
156
  cat p.html | myapi funnel push /pricing --funnel <uuid>`,
148
- '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>`,
149
167
  };
150
168
  export async function run(subcommand, args, flags) {
151
169
  if (!subcommand || (flags.help && !subcommand)) {
@@ -9,6 +9,23 @@ export const SCHEMA = {
9
9
  steps: 'string',
10
10
  'no-enable': 'boolean',
11
11
  };
12
+ // Mirrors the backend's SupportedStepTypes list. Both alias and underscore
13
+ // forms are accepted by the workflow runner. Keep this in sync if the
14
+ // backend grows new step types.
15
+ const SUPPORTED_STEP_TYPES = ['send_email', 'email', 'slack_message', 'slack'];
16
+ function validateSteps(steps) {
17
+ if (!Array.isArray(steps))
18
+ error('--steps must be a JSON array of step objects.');
19
+ steps.forEach((s, i) => {
20
+ if (!s || typeof s !== 'object')
21
+ error(`step ${i}: must be a JSON object.`);
22
+ if (!s.type)
23
+ error(`step ${i}: missing required field "type". Supported: ${SUPPORTED_STEP_TYPES.join(', ')}`);
24
+ if (!SUPPORTED_STEP_TYPES.includes(s.type)) {
25
+ error(`step ${i}: unknown type "${s.type}". Supported: ${SUPPORTED_STEP_TYPES.join(', ')}`);
26
+ }
27
+ });
28
+ }
12
29
  function summarizeWorkflow(w) {
13
30
  return {
14
31
  id: w.id,
@@ -67,6 +84,7 @@ export async function create(nameArg, flags) {
67
84
  catch {
68
85
  error('Invalid JSON for --steps');
69
86
  }
87
+ validateSteps(steps);
70
88
  const wf = await sdkWorkflow.createWorkflow(config.api_key, orgId, {
71
89
  name,
72
90
  trigger_config: { endpoint_id: endpointId },
@@ -98,6 +116,7 @@ export async function update(id, flags) {
98
116
  catch {
99
117
  error('Invalid JSON for --steps');
100
118
  }
119
+ validateSteps(payload.steps);
101
120
  }
102
121
  if (!payload.name && !payload.trigger_config && !payload.steps) {
103
122
  error('Nothing to update. Provide at least one of --name, --endpoint-id, --steps.');
@@ -162,12 +181,13 @@ const SUBCOMMAND_USAGE = {
162
181
  Either form works; the positional name is the recommended shape.
163
182
 
164
183
  --steps is a JSON array of step objects. Supported step types:
165
- send_email — fields: from, to, subject, body | template_id, template_vars
166
- slack — fields: webhook_url, text
167
- http — fields: method, url, body, headers
184
+ send_email | email — fields: from, to, subject, body | template_id, template_vars
185
+ slack_message | slack — fields: webhook_url, text
168
186
 
169
- Field values support {{ payload.field }} templating to reference the
170
- incoming webhook payload, e.g. "to": "{{ payload.email }}".
187
+ Either alias works (e.g. type: "email" and type: "send_email" both run
188
+ the same step). Field values support {{ payload.field }} templating to
189
+ reference the incoming webhook payload, e.g. "to": "{{ payload.email }}".
190
+ Unknown step types are rejected at create time, not at execute time.
171
191
 
172
192
  Example — fire on every webhook POST, send a thank-you email:
173
193
  myapi workflow create "Contact handler" --endpoint-id <wid> --steps '[
package/dist/index.js CHANGED
@@ -58,6 +58,9 @@ const ERROR_MESSAGES = {
58
58
  RATE_LIMITED: 'Too many requests. Please wait a moment and try again.',
59
59
  INSUFFICIENT_BALANCE: 'Insufficient balance. Run: myapi billing topup <amount>',
60
60
  INVALID_AMOUNT: 'Amount out of range. Maximum single top-up is $100. Run: myapi billing topup <amount>',
61
+ // invalid_json_response intentionally absent — the SDK's MyApiError now
62
+ // builds a useful detailed message for that case (status + URL + body
63
+ // snippet), and friendlyError(err.code) would override it.
61
64
  };
62
65
  function friendlyError(code) {
63
66
  return ERROR_MESSAGES[code] || code;
@@ -6,7 +6,7 @@ Run a chain of actions (send email, post to Slack, call an HTTP endpoint) every
6
6
  ## What it does
7
7
 
8
8
  - Bind ordered step chains to webhook endpoints
9
- - Steps can: send transactional email, hit Slack, call any HTTP URL
9
+ - Steps can: send transactional email, post to Slack
10
10
  - Template values from the inbound webhook payload (`{{ payload.field }}`)
11
11
  - Per-run status tracking (attempt, error, started/finished timestamps)
12
12
  - Enable/disable without losing config
@@ -51,25 +51,36 @@ myapi workflow get-run <run_id>
51
51
 
52
52
  `--steps` is a JSON array of step objects. Each step has a `type` and type-specific fields.
53
53
 
54
+ Supported step types (alias forms in parentheses):
55
+
56
+ | `type` | Aliases | Required fields |
57
+ |---|---|---|
58
+ | `send_email` | `email` | `from`, `to`, `subject`, plus one of `body` / `html` / `template_id` |
59
+ | `slack_message` | `slack` | `webhook_url`, `text` |
60
+
54
61
  ```json
55
62
  [
56
63
  {
57
- "type": "send_email",
64
+ "type": "email",
58
65
  "from": "hello@yourdomain.com",
59
66
  "to": "{{ payload.email }}",
60
67
  "subject": "Welcome, {{ payload.name }}",
61
68
  "template_id": "<template_id>"
62
69
  },
63
70
  {
64
- "type": "http",
65
- "method": "POST",
66
- "url": "https://crm.example.com/contacts",
67
- "body": "{{ payload | json }}"
71
+ "type": "slack",
72
+ "webhook_url": "https://hooks.slack.com/services/T.../B.../xxx",
73
+ "text": "New submission from {{ payload.name }}"
68
74
  }
69
75
  ]
70
76
  ```
71
77
 
72
- The webhook payload is available as `{{ payload }}` and individual fields as `{{ payload.fieldname }}`.
78
+ Unknown step types are rejected at workflow create time, so typos surface
79
+ immediately instead of after 3 failed retries during execution.
80
+
81
+ The webhook payload is available as `{{ payload }}` and individual fields
82
+ as `{{ payload.fieldname }}`. Whitespace inside the braces is fine —
83
+ both `{{ payload.email }}` and `{{payload.email}}` work.
73
84
 
74
85
  ## End-to-end recipe — react to a contact form submission
75
86
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myapihq/cli",
3
- "version": "1.1.0-wip.1",
3
+ "version": "1.1.0-wip.2",
4
4
  "description": "MyAPI command-line interface",
5
5
  "type": "module",
6
6
  "files": [