@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.
- package/dist/commands/email/mailbox.js +1 -1
- package/dist/commands/funnel.d.ts +1 -1
- package/dist/commands/funnel.js +27 -9
- package/dist/commands/workflow.js +25 -5
- package/dist/index.js +3 -0
- package/dist/skills/my-workflow-api/README.md +1 -1
- package/dist/skills/my-workflow-api/SKILL.md +17 -6
- package/package.json +1 -1
|
@@ -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} (
|
|
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(
|
|
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>;
|
package/dist/commands/funnel.js
CHANGED
|
@@ -118,15 +118,24 @@ export async function push(slug, flags) {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
|
|
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
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
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':
|
|
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
|
|
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
|
-
|
|
170
|
-
|
|
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,
|
|
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": "
|
|
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": "
|
|
65
|
-
"
|
|
66
|
-
"
|
|
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
|
-
|
|
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
|
|