@myapihq/cli 1.3.5 → 1.3.8
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/audience.js +3 -3
- package/dist/commands/auth.d.ts +1 -1
- package/dist/commands/auth.js +10 -10
- package/dist/commands/billing.js +3 -3
- package/dist/commands/company.js +1 -1
- package/dist/commands/config.js +3 -3
- package/dist/commands/container.js +3 -3
- package/dist/commands/crm/companies.js +6 -5
- package/dist/commands/crm/contacts.js +7 -6
- package/dist/commands/crm/index.js +1 -1
- package/dist/commands/database.js +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/domain.js +11 -11
- package/dist/commands/email/campaign.js +5 -5
- package/dist/commands/email/index.js +2 -2
- package/dist/commands/email/mailbox.js +3 -3
- package/dist/commands/email/message.js +4 -4
- package/dist/commands/email/template.js +3 -3
- package/dist/commands/fn.js +3 -3
- package/dist/commands/funnel.d.ts +1 -0
- package/dist/commands/funnel.js +187 -8
- package/dist/commands/image.js +2 -2
- package/dist/commands/keys.js +1 -1
- package/dist/commands/llm.js +257 -24
- package/dist/commands/org.js +4 -4
- package/dist/commands/payments.js +3 -3
- package/dist/commands/people.js +1 -1
- package/dist/commands/pixel.js +3 -3
- package/dist/commands/queue.js +1 -0
- package/dist/commands/storage.js +4 -4
- package/dist/commands/webhook.js +2 -2
- package/dist/commands/workflow-validation.test.js +11 -2
- package/dist/commands/workflow.js +21 -8
- package/dist/completion.js +1 -1
- package/dist/index.js +21 -21
- package/dist/skills/my-funnel-api/SKILL.md +33 -5
- package/dist/skills/my-llm-api/README.md +18 -8
- package/dist/skills/my-llm-api/SKILL.md +91 -62
- package/dist/skills/my-webhook-api/SKILL.md +4 -2
- package/package.json +5 -4
|
@@ -211,12 +211,12 @@ export async function run(subcommand, args, flags) {
|
|
|
211
211
|
|
|
212
212
|
Subcommands:
|
|
213
213
|
create Save a filter as a named audience (people or company source)
|
|
214
|
-
list List all audiences in the org
|
|
215
|
-
get Get a single audience (with its filter + current member_count)
|
|
216
|
-
update Patch name / description / filter (re-evaluates count if filter changes)
|
|
217
214
|
delete Remove an audience
|
|
215
|
+
get Get a single audience (with its filter + current member_count)
|
|
216
|
+
list List all audiences in the org
|
|
218
217
|
members Get paged member snapshot (people or companies depending on source)
|
|
219
218
|
refresh Re-evaluate filter against current data; returns delta vs previous
|
|
219
|
+
update Patch name / description / filter (re-evaluates count if filter changes)
|
|
220
220
|
|
|
221
221
|
All commands accept --org <id> (or set a default: myapi config set-org <id>).
|
|
222
222
|
|
package/dist/commands/auth.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ 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 HELP = "Usage: myapi auth <subcommand>\n\nSubcommands:\n
|
|
6
|
+
export declare const HELP = "Usage: myapi auth <subcommand>\n\nSubcommands:\n api-keys Manage API keys \u00B7 list / create / revoke\n config Manage CLI defaults (org, funnel, domain) \u00B7 supports set-org / set-funnel / set-domain\n import-key Import an existing API key non-interactively\n install-skills DEPRECATED \u2014 use `myapi install-skills` instead\n keys Alias for api-keys\n link [email] Upgrade anonymous account to registered (or add a second session)\n Use myapi auth setup to create a completely new account\n registrant Manage stored WHOIS contact info for domain registration \u00B7 set / get / clear\n setup Configure your account\n switch [index] Switch active account by index or email\n whoami Show current account \u00B7 supports --json";
|
|
7
7
|
export declare const INSTALL_SKILLS_HELP = "Usage: myapi install-skills\n\nInstalls the MyAPI skills pack for AI coding agents (Claude, Gemini, Cursor).\n\nThis command writes skill definition files to:\n ~/.agents/skills/myapi/\n\nAnd creates symlinks in the appropriate agent config directories:\n ~/.claude/ (Claude)\n ~/.gemini/ (Gemini)\n ~/.cursor/ (Cursor, if detected)\n\nThese files teach agents how to use the MyAPI CLI and API directly.\nRun this command again to update existing skills to the latest version.\n\nNote: `myapi auth install-skills` is deprecated and will be removed in the next minor.\nUse `myapi install-skills` going forward.";
|
|
8
8
|
export declare function link(flags?: Flags, emailArg?: string): Promise<void>;
|
|
9
9
|
export declare function whoami(flags?: Flags): Promise<void>;
|
package/dist/commands/auth.js
CHANGED
|
@@ -20,17 +20,17 @@ export const SCHEMA = {
|
|
|
20
20
|
export const HELP = `Usage: myapi auth <subcommand>
|
|
21
21
|
|
|
22
22
|
Subcommands:
|
|
23
|
-
|
|
23
|
+
api-keys Manage API keys · list / create / revoke
|
|
24
|
+
config Manage CLI defaults (org, funnel, domain) · supports set-org / set-funnel / set-domain
|
|
24
25
|
import-key Import an existing API key non-interactively
|
|
25
|
-
|
|
26
|
+
install-skills DEPRECATED — use \`myapi install-skills\` instead
|
|
27
|
+
keys Alias for api-keys
|
|
26
28
|
link [email] Upgrade anonymous account to registered (or add a second session)
|
|
27
29
|
Use myapi auth setup to create a completely new account
|
|
28
|
-
switch [index] Switch active account by index or email
|
|
29
|
-
config Manage CLI defaults (org, funnel, domain) · supports set-org / set-funnel / set-domain
|
|
30
30
|
registrant Manage stored WHOIS contact info for domain registration · set / get / clear
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
setup Configure your account
|
|
32
|
+
switch [index] Switch active account by index or email
|
|
33
|
+
whoami Show current account · supports --json`;
|
|
34
34
|
export const INSTALL_SKILLS_HELP = `Usage: myapi install-skills
|
|
35
35
|
|
|
36
36
|
Installs the MyAPI skills pack for AI coding agents (Claude, Gemini, Cursor).
|
|
@@ -274,10 +274,10 @@ requires (ICANN-mandated). Stored in ~/.myapi/config.json under the active
|
|
|
274
274
|
account; never synced to the backend except as a per-request field.
|
|
275
275
|
|
|
276
276
|
Subcommands:
|
|
277
|
-
|
|
278
|
-
'<json>' to set non-interactively (agent-friendly).
|
|
277
|
+
clear Remove the stored registrant from the active account.
|
|
279
278
|
get Print the stored registrant (or "not set").
|
|
280
|
-
|
|
279
|
+
set Interactive prompt for all fields. Or pass --registrant-json
|
|
280
|
+
'<json>' to set non-interactively (agent-friendly).`;
|
|
281
281
|
export async function registrant(sub, flags = {}) {
|
|
282
282
|
if (flags.help || !sub) {
|
|
283
283
|
info(REGISTRANT_HELP);
|
package/dist/commands/billing.js
CHANGED
|
@@ -50,10 +50,10 @@ export async function run(subcommand, args, flags) {
|
|
|
50
50
|
Subcommands:
|
|
51
51
|
balance Check balance, credits, and payment method status
|
|
52
52
|
history View recent transactions and top-ups
|
|
53
|
-
usage Spend rolled up by service (this month, or --period 30d)
|
|
54
|
-
topup Top up your balance (whole dollars)
|
|
55
53
|
setup Open a checkout link to add or update payment method
|
|
56
|
-
spend-cap Set/show/clear the account-level spend ceiling (IAM Layer 2)
|
|
54
|
+
spend-cap Set/show/clear the account-level spend ceiling (IAM Layer 2)
|
|
55
|
+
topup Top up your balance (whole dollars)
|
|
56
|
+
usage Spend rolled up by service (this month, or --period 30d)`);
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
59
|
if (flags.help) {
|
package/dist/commands/company.js
CHANGED
|
@@ -126,8 +126,8 @@ export async function run(subcommand, args, flags) {
|
|
|
126
126
|
info(`Usage: myapi company <subcommand>
|
|
127
127
|
|
|
128
128
|
Subcommands:
|
|
129
|
-
search Filter companies across the Goldfox database
|
|
130
129
|
get Get a single company (optionally with embedded people)
|
|
130
|
+
search Filter companies across the Goldfox database
|
|
131
131
|
|
|
132
132
|
All commands accept --org <id> (or set a default: myapi config set-org <id>).
|
|
133
133
|
Filter shape is shared with people + audience — see myapi audience --help.`);
|
package/dist/commands/config.js
CHANGED
|
@@ -98,10 +98,10 @@ export async function run(subcommand, args, flags) {
|
|
|
98
98
|
Manages CLI defaults (org, funnel, domain) so you don't have to pass --org / --domain on every command.
|
|
99
99
|
|
|
100
100
|
Subcommands:
|
|
101
|
-
view Show current defaults
|
|
102
|
-
set-org Set default organization (validates access)
|
|
103
|
-
set-funnel Set default funnel (must be owned by default org)
|
|
104
101
|
set-domain Set default domain
|
|
102
|
+
set-funnel Set default funnel (must be owned by default org)
|
|
103
|
+
set-org Set default organization (validates access)
|
|
104
|
+
view Show current defaults
|
|
105
105
|
|
|
106
106
|
Examples:
|
|
107
107
|
myapi ${via} set-org <id>
|
|
@@ -280,12 +280,12 @@ dependencies and long execution.
|
|
|
280
280
|
|
|
281
281
|
Subcommands:
|
|
282
282
|
create Register a container and get its scoped API key (returned once)
|
|
283
|
+
delete <id> Soft-delete and revoke its scoped API key
|
|
283
284
|
deploy <id> <image> Ship a pre-built image and go live
|
|
284
|
-
|
|
285
|
+
domain <id> <domain> Bind a custom domain (--remove to unbind)
|
|
285
286
|
get <id> Inspect a container
|
|
287
|
+
list List containers in your org
|
|
286
288
|
logs <id> Show recent runtime logs (--tail <n>)
|
|
287
|
-
domain <id> <domain> Bind a custom domain (--remove to unbind)
|
|
288
|
-
delete <id> Soft-delete and revoke its scoped API key
|
|
289
289
|
|
|
290
290
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
291
291
|
return;
|
|
@@ -10,6 +10,7 @@ export const EXPOSES = [
|
|
|
10
10
|
'GET /crm/orgs/{org_id}/companies/{id}',
|
|
11
11
|
'PATCH /crm/orgs/{org_id}/companies/{id}',
|
|
12
12
|
'DELETE /crm/orgs/{org_id}/companies/{id}',
|
|
13
|
+
'POST /crm/orgs/{org_id}/companies/{id}/restore',
|
|
13
14
|
];
|
|
14
15
|
export const SCHEMA = {};
|
|
15
16
|
function csv(v) {
|
|
@@ -159,14 +160,14 @@ export async function run(subcommand, args, flags) {
|
|
|
159
160
|
info(`Usage: myapi crm companies <subcommand>
|
|
160
161
|
|
|
161
162
|
Subcommands:
|
|
162
|
-
list List all companies
|
|
163
|
-
search Filter by stage / source / domain
|
|
164
163
|
create <domain> Manually create (source='manual')
|
|
165
|
-
get <id> Fetch one company
|
|
166
|
-
update <id> Patch stage, name, custom JSON
|
|
167
164
|
delete <id> Soft-delete
|
|
168
|
-
|
|
165
|
+
get <id> Fetch one company
|
|
166
|
+
list List all companies
|
|
169
167
|
promote <domain> Promote a Goldfox company → CRM (Goldfox company id IS the domain)
|
|
168
|
+
restore <id> Undo a soft-delete
|
|
169
|
+
search Filter by stage / source / domain
|
|
170
|
+
update <id> Patch stage, name, custom JSON
|
|
170
171
|
|
|
171
172
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
172
173
|
return;
|
|
@@ -10,6 +10,7 @@ export const EXPOSES = [
|
|
|
10
10
|
'GET /crm/orgs/{org_id}/contacts/{id}',
|
|
11
11
|
'PATCH /crm/orgs/{org_id}/contacts/{id}',
|
|
12
12
|
'DELETE /crm/orgs/{org_id}/contacts/{id}',
|
|
13
|
+
'POST /crm/orgs/{org_id}/contacts/{id}/restore',
|
|
13
14
|
'GET /crm/orgs/{org_id}/contacts/{id}/events',
|
|
14
15
|
];
|
|
15
16
|
export const SCHEMA = {};
|
|
@@ -205,15 +206,15 @@ export async function run(subcommand, args, flags) {
|
|
|
205
206
|
info(`Usage: myapi crm contacts <subcommand>
|
|
206
207
|
|
|
207
208
|
Subcommands:
|
|
208
|
-
list Most-recently-engaged contacts (no filter)
|
|
209
|
-
search Filter by stage / source / email / engagement window
|
|
210
209
|
create <email> Manually create (source='manual')
|
|
211
|
-
get <id> Fetch one contact (with embedded Goldfox enrichment, when available)
|
|
212
|
-
update <id> Patch stage, names, company_id, custom JSON
|
|
213
210
|
delete <id> Soft-delete (events retained)
|
|
214
|
-
restore <id> Undo a soft-delete
|
|
215
|
-
promote <goldfox_person_id> Promote a Goldfox lead → CRM contact (idempotent)
|
|
216
211
|
events <id> Timeline of events on a contact (newest first)
|
|
212
|
+
get <id> Fetch one contact (with embedded Goldfox enrichment, when available)
|
|
213
|
+
list Most-recently-engaged contacts (no filter)
|
|
214
|
+
promote <goldfox_person_id> Promote a Goldfox lead → CRM contact (idempotent)
|
|
215
|
+
restore <id> Undo a soft-delete
|
|
216
|
+
search Filter by stage / source / email / engagement window
|
|
217
|
+
update <id> Patch stage, names, company_id, custom JSON
|
|
217
218
|
|
|
218
219
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
219
220
|
return;
|
|
@@ -39,8 +39,8 @@ export async function run(subcommand, args, flags) {
|
|
|
39
39
|
info(`Usage: myapi crm <namespace> <subcommand>
|
|
40
40
|
|
|
41
41
|
Namespaces:
|
|
42
|
-
contacts The people you've engaged with (private to org)
|
|
43
42
|
companies The companies you've engaged with (private to org)
|
|
43
|
+
contacts The people you've engaged with (private to org)
|
|
44
44
|
|
|
45
45
|
Each namespace shares the same subcommands:
|
|
46
46
|
list List with no filter (alias for search)
|
|
@@ -224,9 +224,9 @@ export async function run(subcommand, args, flags) {
|
|
|
224
224
|
info(`Usage: myapi database <subcommand>
|
|
225
225
|
|
|
226
226
|
Namespaces:
|
|
227
|
-
namespaces List namespaces in the org
|
|
228
227
|
create <name> Create a namespace
|
|
229
228
|
delete-namespace <name> Delete a namespace AND all its keys (irreversible)
|
|
229
|
+
namespaces List namespaces in the org
|
|
230
230
|
|
|
231
231
|
Keys (require --ns <namespace>):
|
|
232
232
|
keys List keys in a namespace
|
package/dist/commands/doctor.js
CHANGED
|
@@ -276,6 +276,6 @@ Exit codes:
|
|
|
276
276
|
1 one or more critical issues
|
|
277
277
|
|
|
278
278
|
Options:
|
|
279
|
-
--verbose Show passing checks too.
|
|
280
279
|
--json Machine-readable output.
|
|
281
|
-
--org <id> Override the default org
|
|
280
|
+
--org <id> Override the default org.
|
|
281
|
+
--verbose Show passing checks too.`;
|
package/dist/commands/domain.js
CHANGED
|
@@ -443,12 +443,12 @@ async function recordsDelete(domainArg, recordId, flags) {
|
|
|
443
443
|
const RECORDS_HELP = `Usage: myapi domain records <subcommand> <domain> [...]
|
|
444
444
|
|
|
445
445
|
Subcommands:
|
|
446
|
-
list <domain> [--type A|AAAA|CNAME|MX|TXT]
|
|
447
|
-
get <domain> <record-id>
|
|
448
446
|
create <domain> --type <T> --name <n> --content <c> [--ttl <n>]
|
|
449
447
|
[--priority <n>] [--proxied]
|
|
450
|
-
update <domain> <record-id> [--content ...] [--ttl ...] [--priority ...] [--proxied]
|
|
451
448
|
delete <domain> <record-id> [--yes]
|
|
449
|
+
get <domain> <record-id>
|
|
450
|
+
list <domain> [--type A|AAAA|CNAME|MX|TXT]
|
|
451
|
+
update <domain> <record-id> [--content ...] [--ttl ...] [--priority ...] [--proxied]
|
|
452
452
|
|
|
453
453
|
Name normalization:
|
|
454
454
|
--name=@ Apex (e.g. example.com)
|
|
@@ -605,19 +605,19 @@ export async function run(subcommand, args, flags) {
|
|
|
605
605
|
info(`Usage: myapi domain <subcommand>
|
|
606
606
|
|
|
607
607
|
Subcommands:
|
|
608
|
-
|
|
608
|
+
assign Assign domain to an org
|
|
609
609
|
check Check domain availability
|
|
610
|
-
|
|
610
|
+
email-setup Opt in to MyAPI-managed email on a subdomain (default: mail.<domain>)
|
|
611
611
|
import Bring your own domain (BYOD) — registrar-agnostic
|
|
612
|
+
list List domains
|
|
613
|
+
records Manage DNS records in the zone (list / get / create / update / delete)
|
|
614
|
+
register Register a domain
|
|
612
615
|
renew Renew a registered domain for another period
|
|
613
|
-
|
|
614
|
-
unassign Unassign domain from its current org
|
|
615
|
-
status Get domain status
|
|
616
|
+
retry-provisioning Re-run provisioning when status=infra_error and the failure is retryable
|
|
616
617
|
settings Get edge/CDN settings
|
|
618
|
+
status Get domain status
|
|
619
|
+
unassign Unassign domain from its current org
|
|
617
620
|
update-settings Update edge/CDN settings
|
|
618
|
-
records Manage DNS records in the zone (list / get / create / update / delete)
|
|
619
|
-
email-setup Opt in to MyAPI-managed email on a subdomain (default: mail.<domain>)
|
|
620
|
-
retry-provisioning Re-run provisioning when status=infra_error and the failure is retryable
|
|
621
621
|
|
|
622
622
|
Tip: Set defaults with "myapi config set-org <id>" / "set-domain <domain>" to skip flags on every command.`);
|
|
623
623
|
return;
|
|
@@ -163,15 +163,15 @@ export async function run(sub, args, flags) {
|
|
|
163
163
|
|
|
164
164
|
Subcommands:
|
|
165
165
|
create <name> Create a campaign (--template-id --from)
|
|
166
|
-
list List campaigns
|
|
167
166
|
get <id> Get a single campaign
|
|
168
|
-
|
|
169
|
-
upload-contacts <id> Upload contact list (--emails a@x,b@y)
|
|
170
|
-
upload-contacts-file <id> Upload contacts from a CSV file (--file path/to/contacts.csv)
|
|
171
|
-
start <id> Start a campaign
|
|
167
|
+
list List campaigns
|
|
172
168
|
pause <id> Pause a campaign
|
|
173
169
|
resume <id> Resume a campaign
|
|
170
|
+
start <id> Start a campaign
|
|
174
171
|
stats <id> Get campaign stats
|
|
172
|
+
update <id> Patch name / per-day-limit
|
|
173
|
+
upload-contacts <id> Upload contact list (--emails a@x,b@y)
|
|
174
|
+
upload-contacts-file <id> Upload contacts from a CSV file (--file path/to/contacts.csv)
|
|
175
175
|
|
|
176
176
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
177
177
|
return;
|
|
@@ -79,12 +79,12 @@ export async function run(subcommand, args, flags) {
|
|
|
79
79
|
info(`Usage: myapi email <namespace> <subcommand>
|
|
80
80
|
|
|
81
81
|
Namespaces:
|
|
82
|
+
campaign Email campaigns (org-scoped) — create, list, get, update, upload-contacts, start, pause, resume, stats
|
|
82
83
|
mailbox Mailboxes (account-scoped) — create, list, activate-sending
|
|
83
84
|
message Send & read mail (account-scoped) — send, status, sent, inbox, outbox, get
|
|
84
|
-
warmup Warmup controls (account-scoped) — start, pause, resume, stop, stats
|
|
85
85
|
template Email templates (org-scoped) — generate, list, edit, send-test, delete
|
|
86
|
-
campaign Email campaigns (org-scoped) — create, list, get, update, upload-contacts, start, pause, resume, stats
|
|
87
86
|
verify Sync single-address email verification (org-scoped) — syntax + DNS + Microsoft probe
|
|
87
|
+
warmup Warmup controls (account-scoped) — start, pause, resume, stop, stats
|
|
88
88
|
|
|
89
89
|
Examples:
|
|
90
90
|
myapi email mailbox create hello@example.com
|
|
@@ -94,11 +94,11 @@ export async function run(sub, args, flags) {
|
|
|
94
94
|
info(`Usage: myapi email mailbox <subcommand>
|
|
95
95
|
|
|
96
96
|
Subcommands:
|
|
97
|
+
activate-sending Activate outbound sending for a mailbox
|
|
98
|
+
clear-forwarding Stop forwarding for a mailbox
|
|
97
99
|
create Create a mailbox (positional <user@domain> or --username/--domain)
|
|
98
100
|
list List mailboxes on a domain (--domain) or orphaned ones (--filter unassigned)
|
|
99
|
-
|
|
100
|
-
set-forwarding Forward a copy of incoming mail to an external address
|
|
101
|
-
clear-forwarding Stop forwarding for a mailbox`);
|
|
101
|
+
set-forwarding Forward a copy of incoming mail to an external address`);
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
if (flags.help) {
|
|
@@ -118,12 +118,12 @@ export async function run(sub, args, flags) {
|
|
|
118
118
|
info(`Usage: myapi email message <subcommand>
|
|
119
119
|
|
|
120
120
|
Subcommands:
|
|
121
|
-
|
|
122
|
-
status <id> Get delivery status of a sent message
|
|
123
|
-
sent List sent emails
|
|
121
|
+
get <id> Get a single message (--address <email>)
|
|
124
122
|
inbox <addr> Read inbox for a mailbox address
|
|
125
123
|
outbox <addr> Read outbox for a mailbox address
|
|
126
|
-
|
|
124
|
+
send Send an email (transactional or templated)
|
|
125
|
+
sent List sent emails
|
|
126
|
+
status <id> Get delivery status of a sent message`);
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
129
|
if (flags.help) {
|
|
@@ -135,13 +135,13 @@ export async function run(sub, args, flags) {
|
|
|
135
135
|
info(`Usage: myapi email template <subcommand>
|
|
136
136
|
|
|
137
137
|
Subcommands:
|
|
138
|
+
delete <id> Delete a template
|
|
139
|
+
edit <id> Edit a template via prompt
|
|
138
140
|
generate <name> Generate AI template (--prompt)
|
|
139
|
-
list List templates
|
|
140
141
|
get <id> Get a template's metadata (subject, preview URL, ...)
|
|
142
|
+
list List templates
|
|
141
143
|
preview <id> Get a template's rendered preview (no auth required)
|
|
142
|
-
edit <id> Edit a template via prompt
|
|
143
144
|
send-test <id> Send a test email for a template (--to)
|
|
144
|
-
delete <id> Delete a template
|
|
145
145
|
|
|
146
146
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
147
147
|
return;
|
package/dist/commands/fn.js
CHANGED
|
@@ -240,12 +240,12 @@ Create, deploy, and manage functions on the MyAPI edge runtime.
|
|
|
240
240
|
|
|
241
241
|
Subcommands:
|
|
242
242
|
create Register a function and get its scoped API key (returned once)
|
|
243
|
+
delete <id> Soft-delete and revoke its scoped API key
|
|
243
244
|
deploy <id> <file> Upload a JS bundle and go live
|
|
244
245
|
env <id> <k> <v> Set a Worker Secret on a deployed function
|
|
245
|
-
runs <id> List recent invocation records
|
|
246
|
-
list List functions in your org
|
|
247
246
|
get <id> Inspect a function
|
|
248
|
-
|
|
247
|
+
list List functions in your org
|
|
248
|
+
runs <id> List recent invocation records
|
|
249
249
|
|
|
250
250
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
251
251
|
return;
|
|
@@ -9,6 +9,7 @@ export declare function get(id: string, flags: Flags): Promise<void>;
|
|
|
9
9
|
export declare function del(id: string, flags: Flags): Promise<void>;
|
|
10
10
|
export declare function pages(funnelArg: string, flags: Flags): Promise<void>;
|
|
11
11
|
export declare function push(slug: string, flags: Flags): Promise<void>;
|
|
12
|
+
export declare function formCmd(funnelArg: string | undefined, flags: Flags): Promise<void>;
|
|
12
13
|
export declare function verify(slug: string, flags: Flags): Promise<void>;
|
|
13
14
|
export declare function publish(dir: string, flags: Flags): Promise<void>;
|
|
14
15
|
export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
|
package/dist/commands/funnel.js
CHANGED
|
@@ -14,6 +14,13 @@ export const EXPOSES = [
|
|
|
14
14
|
'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/verify',
|
|
15
15
|
'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/files',
|
|
16
16
|
'GET /funnel/orgs/{org_id}/funnels/{funnel_id}/pages',
|
|
17
|
+
// form bindings — created/listed/removed via `funnel form --capture-to`
|
|
18
|
+
// and the underlying SDK helpers. List/Delete don't have dedicated CLI
|
|
19
|
+
// subcommands yet; the SDK functions are wired and customers using the
|
|
20
|
+
// SDK directly need them in EXPOSES for coverage to be honest.
|
|
21
|
+
'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/forms',
|
|
22
|
+
'GET /funnel/orgs/{org_id}/funnels/{funnel_id}/forms',
|
|
23
|
+
'DELETE /funnel/orgs/{org_id}/funnels/{funnel_id}/forms/{slug}',
|
|
17
24
|
'GET /hq/orgs/{org_id}',
|
|
18
25
|
];
|
|
19
26
|
export const SCHEMA = {
|
|
@@ -21,6 +28,12 @@ export const SCHEMA = {
|
|
|
21
28
|
slug: 'string',
|
|
22
29
|
env: 'string',
|
|
23
30
|
'api-fn': 'string',
|
|
31
|
+
// form
|
|
32
|
+
fields: 'string',
|
|
33
|
+
cta: 'string',
|
|
34
|
+
success: 'string',
|
|
35
|
+
'capture-to': 'string',
|
|
36
|
+
honeypot: 'string',
|
|
24
37
|
};
|
|
25
38
|
// Backend (2026-05-15): POST /funnels accepts an optional `name` (defaults
|
|
26
39
|
// to the org's preview_subdomain for back-compat). Mirror the backend's
|
|
@@ -178,6 +191,129 @@ export async function push(slug, flags) {
|
|
|
178
191
|
}
|
|
179
192
|
}
|
|
180
193
|
}
|
|
194
|
+
function parseField(token) {
|
|
195
|
+
// <name>[:<modifier>]... — modifiers are types or `required`, order-free.
|
|
196
|
+
const parts = token.split(':').map(p => p.trim()).filter(Boolean);
|
|
197
|
+
const name = parts[0];
|
|
198
|
+
if (!name || !/^[a-z][a-z0-9_]*$/i.test(name)) {
|
|
199
|
+
error(`Invalid field name "${name}". Lowercase letters/digits/underscores; starts with a letter.`);
|
|
200
|
+
}
|
|
201
|
+
let type;
|
|
202
|
+
let required = false;
|
|
203
|
+
for (const mod of parts.slice(1)) {
|
|
204
|
+
const m = mod.toLowerCase();
|
|
205
|
+
if (m === 'required')
|
|
206
|
+
required = true;
|
|
207
|
+
else if (m === 'text' || m === 'email' || m === 'number' || m === 'tel')
|
|
208
|
+
type = m;
|
|
209
|
+
else
|
|
210
|
+
error(`Unknown field modifier "${mod}" on "${name}". Use one of: text, email, number, tel, required.`);
|
|
211
|
+
}
|
|
212
|
+
if (!type) {
|
|
213
|
+
const n = name.toLowerCase();
|
|
214
|
+
type = n === 'email' ? 'email'
|
|
215
|
+
: n === 'phone' || n === 'tel' ? 'tel'
|
|
216
|
+
: 'text';
|
|
217
|
+
}
|
|
218
|
+
return { name, type, required };
|
|
219
|
+
}
|
|
220
|
+
function parseCaptureTo(raw) {
|
|
221
|
+
const m = raw.match(/^(webhook|workflow):([a-f0-9-]{36})$/i);
|
|
222
|
+
if (!m)
|
|
223
|
+
error(`Invalid --capture-to "${raw}". Expected webhook:<uuid> or workflow:<uuid>.`);
|
|
224
|
+
return { kind: m[1].toLowerCase(), id: m[2] };
|
|
225
|
+
}
|
|
226
|
+
export async function formCmd(funnelArg, flags) {
|
|
227
|
+
const config = requireConfig();
|
|
228
|
+
const orgId = requireOrg(flags, config, 'myapi funnel form [funnel-id] [--slug <s>] [--fields <spec>] [--capture-to <dest>] [--honeypot <name>] [--cta "<text>"] [--success "<text>"] [--org <id>] [--json]');
|
|
229
|
+
// Resolve funnel id the same way push/verify do.
|
|
230
|
+
let funnelId = funnelArg || flags.funnel || config.default_funnel;
|
|
231
|
+
if (!funnelId) {
|
|
232
|
+
const existing = await sdkFunnel.listFunnels(config.api_key, orgId);
|
|
233
|
+
if (existing.length === 0)
|
|
234
|
+
error('No funnel found for this org. Create one with: myapi funnel create');
|
|
235
|
+
if (existing.length > 1) {
|
|
236
|
+
error(`Multiple funnels exist and no default is set.\nPass the funnel id, or set a default:\n myapi config set-funnel <id>\n\nFunnels:\n${existing.map(f => ` ${f.id}`).join('\n')}`);
|
|
237
|
+
}
|
|
238
|
+
funnelId = existing[0].id;
|
|
239
|
+
}
|
|
240
|
+
const rawSlug = flags.slug || 'join';
|
|
241
|
+
const slug = rawSlug.startsWith('/') ? rawSlug.slice(1) : rawSlug;
|
|
242
|
+
if (!/^[a-z0-9][a-z0-9-]{0,40}$/i.test(slug)) {
|
|
243
|
+
error(`Invalid --slug "${slug}". Lowercase letters/digits/hyphens, 1-41 chars, starts with a letter or digit.`);
|
|
244
|
+
}
|
|
245
|
+
// `--fields email:required,name,phone:tel,plan:text:required`
|
|
246
|
+
const fieldsArg = (flags.fields || 'email:email:required').trim();
|
|
247
|
+
const fieldSpecs = fieldsArg.split(',').map(s => s.trim()).filter(Boolean).map(parseField);
|
|
248
|
+
if (fieldSpecs.length === 0)
|
|
249
|
+
error('--fields must list at least one field. Example: --fields email:required,name');
|
|
250
|
+
// Honeypot field — match the backend default unless --honeypot overrides.
|
|
251
|
+
const honeypot = flags.honeypot || 'middle_name';
|
|
252
|
+
if (!/^[a-z][a-z0-9_]*$/i.test(honeypot))
|
|
253
|
+
error(`Invalid --honeypot "${honeypot}".`);
|
|
254
|
+
for (const f of fieldSpecs)
|
|
255
|
+
if (f.name === honeypot)
|
|
256
|
+
error(`Honeypot "${honeypot}" collides with a real field. Pick a different --honeypot name.`);
|
|
257
|
+
const cta = flags.cta || 'Sign up';
|
|
258
|
+
const successMsg = flags.success || "Thanks — we'll be in touch.";
|
|
259
|
+
// Optional binding via --capture-to.
|
|
260
|
+
let registeredBinding;
|
|
261
|
+
if (typeof flags['capture-to'] === 'string' && flags['capture-to']) {
|
|
262
|
+
const dest = parseCaptureTo(flags['capture-to']);
|
|
263
|
+
const binding = {
|
|
264
|
+
slug,
|
|
265
|
+
destination: `${dest.kind}:${dest.id}`,
|
|
266
|
+
fields: fieldSpecs.map(f => ({ name: f.name, required: f.required })),
|
|
267
|
+
honeypot_field: honeypot,
|
|
268
|
+
};
|
|
269
|
+
registeredBinding = await sdkFunnel.createFormBinding(config.api_key, orgId, funnelId, binding);
|
|
270
|
+
}
|
|
271
|
+
const url = `https://api.myapihq.com/funnel/funnels/${funnelId}/submit/${encodeURIComponent(slug)}`;
|
|
272
|
+
const escAttr = (s) => s.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
273
|
+
const escText = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
274
|
+
// Per-field labels — Email uses the canonical placeholder; others
|
|
275
|
+
// Title-Case the name.
|
|
276
|
+
const inputs = fieldSpecs.map(f => {
|
|
277
|
+
const label = f.name.charAt(0).toUpperCase() + f.name.slice(1).replace(/_/g, ' ');
|
|
278
|
+
const req = f.required ? ' required' : '';
|
|
279
|
+
return ` <label>${escText(label)} <input type="${f.type}" name="${escAttr(f.name)}"${req}></label>`;
|
|
280
|
+
}).join('\n');
|
|
281
|
+
// Honeypot — hidden via inline style, off the tab order, autocomplete
|
|
282
|
+
// disabled. Bots fill it; humans don't.
|
|
283
|
+
const honeypotInput = ` <input type="text" name="${escAttr(honeypot)}" style="display:none" tabindex="-1" autocomplete="off">`;
|
|
284
|
+
const html = `<form action="${escAttr(url)}" method="POST" data-myapi-form>
|
|
285
|
+
${inputs}
|
|
286
|
+
${honeypotInput}
|
|
287
|
+
<button type="submit">${escText(cta)}</button>
|
|
288
|
+
</form>
|
|
289
|
+
<script>
|
|
290
|
+
document.querySelectorAll('[data-myapi-form]').forEach(f => {
|
|
291
|
+
f.addEventListener('submit', async e => {
|
|
292
|
+
e.preventDefault();
|
|
293
|
+
const r = await fetch(f.action, { method:'POST', headers:{'Content-Type':'application/json'},
|
|
294
|
+
body: JSON.stringify(Object.fromEntries(new FormData(f))) });
|
|
295
|
+
if (r.ok) f.outerHTML = ${JSON.stringify(`<p>${escText(successMsg)}</p>`)};
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
</script>`;
|
|
299
|
+
if (flags.json) {
|
|
300
|
+
printJson({
|
|
301
|
+
action_url: url,
|
|
302
|
+
slug,
|
|
303
|
+
fields: fieldSpecs,
|
|
304
|
+
honeypot_field: honeypot,
|
|
305
|
+
binding_registered: !!registeredBinding,
|
|
306
|
+
binding: registeredBinding,
|
|
307
|
+
html,
|
|
308
|
+
});
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (registeredBinding) {
|
|
312
|
+
// Print confirmation on stderr so stdout stays the clean snippet.
|
|
313
|
+
process.stderr.write(`✓ Registered form binding: slug=${slug} → ${registeredBinding.destination}\n`);
|
|
314
|
+
}
|
|
315
|
+
process.stdout.write(html + '\n');
|
|
316
|
+
}
|
|
181
317
|
// Verify uses the same shape as push: slug positional (default '/'), funnel
|
|
182
318
|
// resolved from --funnel / default / the org's only funnel.
|
|
183
319
|
export async function verify(slug, flags) {
|
|
@@ -260,10 +396,51 @@ export async function publish(dir, flags) {
|
|
|
260
396
|
}
|
|
261
397
|
// ── Dispatcher ───────────────────────────────────────────────────────────────
|
|
262
398
|
const SUBCOMMAND_USAGE = {
|
|
263
|
-
'list': 'myapi funnel list [--org <id>] [--json]',
|
|
264
399
|
'create': 'myapi funnel create [--name <name>] [--org <id>]',
|
|
265
|
-
'get': 'myapi funnel get <id> [--org <id>] [--json]',
|
|
266
400
|
'delete': 'myapi funnel delete <id> [--org <id>]',
|
|
401
|
+
'form': `myapi funnel form [funnel-id] [--slug <slug>] [--fields <spec>]
|
|
402
|
+
[--capture-to <dest>] [--honeypot <name>]
|
|
403
|
+
[--cta "<button text>"] [--success "<message>"] [--org <id>] [--json]
|
|
404
|
+
|
|
405
|
+
Emits a ready-to-paste HTML snippet for a form that posts to the canonical
|
|
406
|
+
funnel submit proxy. Submissions to any slug fall through to the funnel's
|
|
407
|
+
auto-provisioned webhook by default (CRM ingest + bound workflows fire
|
|
408
|
+
without further wiring). The proxy URL is platform-stable; slug names the
|
|
409
|
+
form so workflows can route on it; an auto-honeypot field stops casual
|
|
410
|
+
bots.
|
|
411
|
+
|
|
412
|
+
--slug <s> Form identifier — appears in the proxy URL.
|
|
413
|
+
Defaults to "join". Lowercase letters/digits/hyphens.
|
|
414
|
+
--fields <spec> Comma-separated field specs.
|
|
415
|
+
Each spec: <name>[:<type>][:required] (modifiers
|
|
416
|
+
order-free). Types: text (default), email, number,
|
|
417
|
+
tel. Name auto-promotes to email if name="email",
|
|
418
|
+
to tel if name="phone" or "tel".
|
|
419
|
+
Defaults to "email:email:required".
|
|
420
|
+
--capture-to <dest> Optional. Register a per-slug binding to the
|
|
421
|
+
destination via POST /funnel/.../forms. Format:
|
|
422
|
+
webhook:<uuid> or workflow:<uuid>. Without it,
|
|
423
|
+
submissions fall through to the funnel's default
|
|
424
|
+
webhook (most agents want this).
|
|
425
|
+
--honeypot <name> Hidden bot-trap input name. Default "middle_name".
|
|
426
|
+
--cta "<text>" Submit button text. Defaults to "Sign up".
|
|
427
|
+
--success "<text>" Message shown on successful submit.
|
|
428
|
+
|
|
429
|
+
Examples:
|
|
430
|
+
myapi funnel form # email-required on default funnel
|
|
431
|
+
myapi funnel form --slug waitlist --fields email:required,name
|
|
432
|
+
myapi funnel form <funnel> --slug checkout \\
|
|
433
|
+
--fields email:required,plan,phone:tel \\
|
|
434
|
+
--capture-to webhook:<checkout_webhook_id>
|
|
435
|
+
myapi funnel form --json # machine-readable
|
|
436
|
+
myapi funnel form --slug join > form.html # snippet to a file
|
|
437
|
+
|
|
438
|
+
Do NOT hardcode webhook inbound URLs into funnel HTML. Use this verb
|
|
439
|
+
instead — the proxy preserves slug routing, hides the URL, and gets
|
|
440
|
+
future platform features (rate-limit, captcha, field validation) for
|
|
441
|
+
free.`,
|
|
442
|
+
'get': 'myapi funnel get <id> [--org <id>] [--json]',
|
|
443
|
+
'list': 'myapi funnel list [--org <id>] [--json]',
|
|
267
444
|
'pages': 'myapi funnel pages [funnel_id] [--funnel <id>] [--org <id>] [--json]',
|
|
268
445
|
'publish': `myapi funnel publish <dir> [--funnel <id>] [--env dev|prod] [--api-fn <id>] [--org <id>]
|
|
269
446
|
|
|
@@ -312,13 +489,14 @@ export async function run(subcommand, args, flags) {
|
|
|
312
489
|
info(`Usage: myapi funnel <subcommand>
|
|
313
490
|
|
|
314
491
|
Subcommands:
|
|
315
|
-
list List funnels
|
|
316
492
|
create Create a funnel (optional --name; backend defaults to org's preview_subdomain)
|
|
317
|
-
get <id> Get funnel details
|
|
318
493
|
delete Delete a funnel
|
|
494
|
+
form Emit HTML for a form that posts to the canonical submit proxy
|
|
495
|
+
get <id> Get funnel details
|
|
496
|
+
list List funnels
|
|
497
|
+
pages List the pages currently published to a funnel
|
|
319
498
|
publish Publish a whole local directory as the site (dev or prod)
|
|
320
499
|
push Push a raw HTML page from stdin to a slug
|
|
321
|
-
pages List the pages currently published to a funnel
|
|
322
500
|
verify Verify a published page is reachable
|
|
323
501
|
|
|
324
502
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
@@ -333,14 +511,15 @@ All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
|
333
511
|
return;
|
|
334
512
|
}
|
|
335
513
|
switch (subcommand) {
|
|
336
|
-
case 'list': return list(flags);
|
|
337
514
|
case 'create': return create(flags);
|
|
338
|
-
case 'get': return get(args[0], flags);
|
|
339
515
|
case 'delete': return del(args[0], flags);
|
|
516
|
+
case 'form': return formCmd(args[0], flags);
|
|
517
|
+
case 'get': return get(args[0], flags);
|
|
518
|
+
case 'list': return list(flags);
|
|
519
|
+
case 'pages': return pages(args[0], flags);
|
|
340
520
|
case 'publish': return publish(args[0], flags);
|
|
341
521
|
case 'push': return push(args[0], flags);
|
|
342
522
|
case 'verify': return verify(args[0], flags);
|
|
343
|
-
case 'pages': return pages(args[0], flags);
|
|
344
523
|
default: error(`Unknown subcommand: ${subcommand}. Run "myapi funnel --help" for a list of valid subcommands.`);
|
|
345
524
|
}
|
|
346
525
|
}
|