@myapihq/cli 1.3.6 → 1.3.11
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/account.d.ts +6 -0
- package/dist/commands/account.js +75 -0
- package/dist/commands/config.js +2 -2
- package/dist/commands/crm/companies.js +1 -0
- package/dist/commands/crm/contacts.js +1 -0
- package/dist/commands/doctor-setup.test.d.ts +1 -0
- package/dist/commands/doctor-setup.test.js +74 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.js +73 -1
- package/dist/commands/domain.d.ts +1 -0
- package/dist/commands/domain.js +38 -1
- package/dist/commands/email/mailbox.js +19 -0
- package/dist/commands/funnel.d.ts +1 -0
- package/dist/commands/funnel.js +219 -16
- package/dist/commands/llm.js +257 -24
- package/dist/commands/queue.js +1 -0
- package/dist/commands/workflow-validation.test.js +11 -2
- package/dist/commands/workflow.js +15 -2
- package/dist/completion.js +4 -3
- package/dist/exposes.test.js +1 -0
- package/dist/index.js +7 -0
- package/dist/skills/my-funnel-api/SKILL.md +35 -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
|
@@ -94,10 +94,19 @@ describe('_validateSteps — http_request / http (2026-05-15)', () => {
|
|
|
94
94
|
.toMatch(/missing required field "url"/);
|
|
95
95
|
});
|
|
96
96
|
it('rejects non-http(s) URLs', () => {
|
|
97
|
+
// post-brutalize: real URL parse instead of prefix regex —
|
|
98
|
+
// /must use http:\/\/ or https:\/\/ scheme/ applies to anything
|
|
99
|
+
// that parses to a non-http(s) protocol (ftp, javascript, file,
|
|
100
|
+
// gopher, data, …); strings that don't parse as URLs at all get
|
|
101
|
+
// the separate "not a valid URL" message.
|
|
97
102
|
expect(_validateSteps([{ ...VALID_HTTP, url: 'ftp://example.com' }]))
|
|
98
|
-
.toMatch(/must
|
|
103
|
+
.toMatch(/must use http:\/\/ or https:\/\/ scheme/);
|
|
99
104
|
expect(_validateSteps([{ ...VALID_HTTP, url: 'javascript:alert(1)' }]))
|
|
100
|
-
.toMatch(/must
|
|
105
|
+
.toMatch(/must use http:\/\/ or https:\/\/ scheme/);
|
|
106
|
+
expect(_validateSteps([{ ...VALID_HTTP, url: 'file:///etc/passwd' }]))
|
|
107
|
+
.toMatch(/must use http:\/\/ or https:\/\/ scheme/);
|
|
108
|
+
expect(_validateSteps([{ ...VALID_HTTP, url: 'not a url' }]))
|
|
109
|
+
.toMatch(/is not a valid URL/);
|
|
101
110
|
});
|
|
102
111
|
it('rejects unknown HTTP methods', () => {
|
|
103
112
|
expect(_validateSteps([{ ...VALID_HTTP, method: 'TRACE' }]))
|
|
@@ -91,8 +91,21 @@ export function _validateSteps(steps) {
|
|
|
91
91
|
if (!s.url || typeof s.url !== 'string') {
|
|
92
92
|
return `${where} (${s.type}): missing required field "url".`;
|
|
93
93
|
}
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
// Real URL parse instead of prefix regex — catches javascript:,
|
|
95
|
+
// file:, gopher:, data:, and whitespace/null-byte hosts that pass
|
|
96
|
+
// /^https?:\/\// but fail proper parsing. Mirrors the scheme check
|
|
97
|
+
// on webhook.forward_url and queue.consumer_url; same shape as the
|
|
98
|
+
// SDK helper. The backend ALSO validates — see
|
|
99
|
+
// docs/cross-repo-prompts/backend-workflow-step-validation.md.
|
|
100
|
+
let parsedUrl;
|
|
101
|
+
try {
|
|
102
|
+
parsedUrl = new URL(s.url);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return `${where} (${s.type}): "url" is not a valid URL — got "${s.url}".`;
|
|
106
|
+
}
|
|
107
|
+
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
108
|
+
return `${where} (${s.type}): "url" must use http:// or https:// scheme — got "${parsedUrl.protocol}" in "${s.url}".`;
|
|
96
109
|
}
|
|
97
110
|
if (s.method !== undefined) {
|
|
98
111
|
if (typeof s.method !== 'string' || !HTTP_METHODS.has(s.method.toUpperCase())) {
|
package/dist/completion.js
CHANGED
|
@@ -26,7 +26,7 @@ const BLOCK_END = `# end ${PROGRAM} completion`;
|
|
|
26
26
|
// test/smoke/completion.test.ts, which fails if a command in `myapi --help`
|
|
27
27
|
// is missing here.
|
|
28
28
|
export const COMMANDS = [
|
|
29
|
-
'audience', 'auth', 'billing', 'company', 'completion', 'config', 'container',
|
|
29
|
+
'account', 'audience', 'auth', 'billing', 'company', 'completion', 'config', 'container',
|
|
30
30
|
'crm', 'database', 'domain', 'email', 'fn', 'funnel', 'git', 'help', 'image',
|
|
31
31
|
'doctor', 'install-skills', 'keys', 'llm', 'org', 'payments', 'people', 'pixel', 'queue',
|
|
32
32
|
'setup', 'status', 'storage', 'task', 'update', 'url', 'webhook', 'whoami',
|
|
@@ -35,10 +35,11 @@ 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'],
|
|
38
39
|
auth: ['setup', 'import-key', 'whoami', 'link', 'switch', 'install-skills', 'config', 'registrant', 'api-keys'],
|
|
39
40
|
org: ['create', 'delete', 'get', 'import', 'list', 'sync-brand', 'update'],
|
|
40
41
|
billing: ['balance', 'history', 'usage', 'setup', 'topup', 'spend-cap'],
|
|
41
|
-
domain: ['assign', 'check', 'email-setup', 'import', 'list', 'records', 'register', 'renew', 'retry-provisioning', 'settings', 'status'],
|
|
42
|
+
domain: ['assign', 'check', 'email-setup', 'import', 'list', 'mail-server-resync', 'records', 'register', 'renew', 'retry-provisioning', 'settings', 'status'],
|
|
42
43
|
funnel: ['create', 'delete', 'get', 'list', 'pages', 'publish', 'push', 'verify'],
|
|
43
44
|
webhook: ['create', 'delete', 'delivery', 'list', 'update'],
|
|
44
45
|
workflow: ['create', 'delete', 'disable', 'enable', 'get', 'get-run', 'list', 'runs', 'update'],
|
|
@@ -49,7 +50,7 @@ export const SUBCOMMANDS = {
|
|
|
49
50
|
people: ['search', 'get'],
|
|
50
51
|
company: ['search', 'get'],
|
|
51
52
|
audience: ['create', 'list', 'get', 'update', 'delete', 'members', 'refresh'],
|
|
52
|
-
llm: ['complete', 'embed', 'models'],
|
|
53
|
+
llm: ['complete', 'embed', 'models', 'classify', 'extract', 'summarize', 'draft'],
|
|
53
54
|
database: ['namespaces', 'create', 'delete-namespace', 'keys', 'get', 'set', 'del'],
|
|
54
55
|
crm: ['contacts', 'companies'],
|
|
55
56
|
url: ['shorten'],
|
package/dist/exposes.test.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import * as fs from 'fs';
|
|
|
6
6
|
import { parseFlags } from './flags.js';
|
|
7
7
|
const pkgPath = new URL('../package.json', import.meta.url);
|
|
8
8
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
9
|
+
import * as accountCmd from './commands/account.js';
|
|
9
10
|
import * as keysCmd from './commands/keys.js';
|
|
10
11
|
import * as billingCmd from './commands/billing.js';
|
|
11
12
|
import * as orgCmd from './commands/org.js';
|
|
@@ -40,6 +41,7 @@ import * as doctorCmd from './commands/doctor.js';
|
|
|
40
41
|
// into a single schema for the upfront parse, so adding a new value flag in
|
|
41
42
|
// one command means editing one file (its SCHEMA), not a global allowlist.
|
|
42
43
|
const COMBINED_SCHEMA = {
|
|
44
|
+
...accountCmd.SCHEMA,
|
|
43
45
|
...authCmd.SCHEMA,
|
|
44
46
|
...billingCmd.SCHEMA,
|
|
45
47
|
...configCmd.SCHEMA,
|
|
@@ -170,6 +172,9 @@ async function main() {
|
|
|
170
172
|
case 'org':
|
|
171
173
|
await orgCmd.run(subcommand, restArgs, flags);
|
|
172
174
|
break;
|
|
175
|
+
case 'account':
|
|
176
|
+
await accountCmd.run(subcommand, restArgs, flags);
|
|
177
|
+
break;
|
|
173
178
|
case 'billing':
|
|
174
179
|
await billingCmd.run(subcommand, restArgs, flags);
|
|
175
180
|
break;
|
|
@@ -367,6 +372,7 @@ const HELP_TARGETS = {
|
|
|
367
372
|
task: f => taskCmd.run(undefined, [], f),
|
|
368
373
|
doctor: f => doctorCmd.run(undefined, [], f),
|
|
369
374
|
org: f => orgCmd.run(undefined, [], f),
|
|
375
|
+
account: f => accountCmd.run(undefined, [], f),
|
|
370
376
|
billing: f => billingCmd.run(undefined, [], f),
|
|
371
377
|
keys: f => keysCmd.run(undefined, [], f),
|
|
372
378
|
config: f => configCmd.run(undefined, [], f),
|
|
@@ -399,6 +405,7 @@ Usage: myapi <command> [subcommand] [args]
|
|
|
399
405
|
myapi --version
|
|
400
406
|
|
|
401
407
|
Commands:
|
|
408
|
+
account Account-scoped settings (mailing address)
|
|
402
409
|
audience Save filter snapshots as named audiences (people or companies)
|
|
403
410
|
auth Manage account · setup · whoami · link
|
|
404
411
|
billing Check balance and manage billing
|
|
@@ -17,7 +17,7 @@ Funnels are the publishing surface. You create a funnel under an org (one comman
|
|
|
17
17
|
|
|
18
18
|
By default, your funnel lives on a free preview subdomain (`*.makeautonomous.com`) you get with every org. To serve on a custom domain, register and assign one via **mydomainapi** first.
|
|
19
19
|
|
|
20
|
-
Every funnel
|
|
20
|
+
Every funnel auto-provisions a **webhook** at creation time (`org_webhook_id`). The funnel exposes two public proxy endpoints your HTML can call directly (no API key needed): a **form submit** at `POST /funnel/funnels/{id}/submit/{slug}` that delivers submissions through that webhook (CRM upsert + bound workflows fire automatically), and an **analytics/event ingest** for pageviews and click tracking. For multi-form funnels you can bind individual slugs to specific destinations with `myapi funnel form ... --capture-to webhook:<id>`.
|
|
21
21
|
<!-- llm:end -->
|
|
22
22
|
|
|
23
23
|
## Commands
|
|
@@ -30,6 +30,7 @@ Every funnel includes two public proxy endpoints your HTML can call directly (no
|
|
|
30
30
|
| `myapi funnel delete <id>` | Delete the funnel and purge its edge pages |
|
|
31
31
|
| `myapi funnel push [slug]` | Push HTML from stdin to a slug (default: `/`) |
|
|
32
32
|
| `myapi funnel pages [funnel_id]` | List the pages currently published to a funnel |
|
|
33
|
+
| `myapi funnel form [funnel_id]` | Emit canonical form HTML (and register a binding with `--capture-to`) |
|
|
33
34
|
| `myapi funnel verify [slug]` | Verify a published page is reachable + check links/webhooks |
|
|
34
35
|
<!-- generated:end -->
|
|
35
36
|
|
|
@@ -58,12 +59,41 @@ myapi funnel delete <funnel_id>
|
|
|
58
59
|
Omitting `[slug]` defaults to `/`. The funnel id is resolved from `--funnel`, the saved default funnel, or — only if the org has exactly one funnel — auto-picked.
|
|
59
60
|
<!-- llm:end -->
|
|
60
61
|
|
|
61
|
-
## Form
|
|
62
|
+
## Form submissions (canonical recipe)
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
The funnel submit proxy is the canonical form-capture path. **Never hardcode `https://api.mywebhookapi.com/webhook/in/<slug>` into funnel HTML** — that URL leaks into your git history, breaks on rotation, gets scraped, and locks the platform out of future form-quality features (rate-limit, captcha, anti-bot, field validation). Use the proxy instead — same plumbing, hidden URL, automatic CRM ingest on `email`.
|
|
64
65
|
|
|
65
|
-
-
|
|
66
|
-
|
|
66
|
+
Zero-config form (the happy path most agents want):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
myapi funnel create
|
|
70
|
+
myapi funnel form --slug join --fields email:required,name > snippet.html
|
|
71
|
+
# paste snippet.html into your page (or pipe through funnel push):
|
|
72
|
+
cat page-with-snippet.html | myapi funnel push /
|
|
73
|
+
# Submissions land in the funnel's auto-provisioned webhook → CRM upsert on `email` → any bound workflow fires.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`funnel form` always registers a per-slug binding (idempotent on repeat). Without `--capture-to` it auto-targets the funnel's `org_webhook_id` — same destination as the fallback, with server-side honeypot + field validation turned on.
|
|
77
|
+
|
|
78
|
+
Multi-form funnel (each slug to a different destination):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Bind /checkout to a specific webhook:
|
|
82
|
+
myapi funnel form <funnel_id> --slug checkout \
|
|
83
|
+
--fields email:required,plan,phone:tel \
|
|
84
|
+
--capture-to webhook:<checkout_webhook_id>
|
|
85
|
+
|
|
86
|
+
# Bind /survey to a workflow directly:
|
|
87
|
+
myapi funnel form <funnel_id> --slug survey \
|
|
88
|
+
--fields email:required,score:number \
|
|
89
|
+
--capture-to workflow:<survey_workflow_id>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`--capture-to` registers the per-slug binding via `POST /funnel/orgs/.../funnels/{id}/forms`. Without it, submissions fall through to the funnel's default webhook.
|
|
93
|
+
|
|
94
|
+
## Analytics
|
|
95
|
+
|
|
96
|
+
`POST /funnel/funnels/{id}/event` — public proxy for pageviews, clicks, and pixel events (no API key needed). Rate-limited to 60 req/min per funnel.
|
|
67
97
|
|
|
68
98
|
## Notes
|
|
69
99
|
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
# my-llm-api
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Two-surface LLM primitive. Self-hosted open-source inference on the raw surface, objective verbs on the verb surface. Pricing is in cents per 1M tokens at upstream rate.
|
|
4
4
|
|
|
5
5
|
## What it does
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- **
|
|
7
|
+
Two surfaces, one credential, one balance:
|
|
8
|
+
|
|
9
|
+
- **Raw** — `complete`, `embed`, `models`. You pick a self-hosted catalog model and shape the prompt yourself. Today's catalog: `Qwen/Qwen3-Coder-30B-A3B-Instruct` (1 chat model, no embed model). Proprietary models are not callable here — that's how MyAPI stays off the reseller framing.
|
|
10
|
+
- **Verbs** — `classify`, `extract`, `summarize`, `draft`. You ask for a task done; the model is implementation detail and is never named in the response. This is where any future proprietary model lives, wrapped behind the verb contract.
|
|
10
11
|
|
|
11
12
|
## Quickstart
|
|
12
13
|
|
|
13
14
|
```bash
|
|
15
|
+
# Catalog (live — reflects what the gateway actually serves)
|
|
14
16
|
myapi llm models
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
|
|
18
|
+
# Raw — picks the first chat model from the catalog automatically
|
|
19
|
+
myapi llm complete "explain HMAC in two sentences"
|
|
20
|
+
|
|
21
|
+
# Verbs — recommended for workflow steps
|
|
22
|
+
myapi llm classify "I want a refund" --labels billing,sales,support
|
|
23
|
+
myapi llm extract "Acme Corp has 250 employees" \
|
|
24
|
+
--schema '{"type":"object","properties":{"company":{"type":"string"},"employees":{"type":"integer"}}}'
|
|
25
|
+
myapi llm summarize --file long.md --style exec
|
|
26
|
+
myapi llm draft --kind email --prompt "Friendly welcome, under 60 words"
|
|
17
27
|
```
|
|
18
28
|
|
|
19
29
|
## Authentication
|
|
@@ -26,10 +36,10 @@ Requires `api_key` and `org_id` from **myapihq**. Inference cost is debited from
|
|
|
26
36
|
|
|
27
37
|
## When to use
|
|
28
38
|
|
|
29
|
-
This is the **workflow-step LLM** — use it inside scripted pipelines (summarize a doc, classify an email,
|
|
39
|
+
This is the **workflow-step LLM** — use it inside scripted pipelines (summarize a doc, classify an email, extract structured data, draft a reply). It is **not** a replacement for your own reasoning if you're an agent; you already have a model.
|
|
30
40
|
|
|
31
41
|
## Documentation
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
Two-surface design, raw/verb shapes, and cost semantics: see `SKILL.md`.
|
|
34
44
|
|
|
35
45
|
Run `myapi llm --help` for inline reference.
|
|
@@ -1,29 +1,39 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: my-llm-api
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.1.0
|
|
4
4
|
description: >
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
triggers: [llm, completion, chat, embed, embedding,
|
|
5
|
+
Two-surface LLM primitive. Raw chat completion against self-hosted
|
|
6
|
+
open-source models (you pick the model), and objective verbs
|
|
7
|
+
(classify / extract / summarize / draft) that hide the model behind a
|
|
8
|
+
task. Pricing in cents per 1M tokens; charged from your MyAPI balance.
|
|
9
|
+
triggers: [llm, completion, chat, embed, embedding, inference, classify, extract, summarize, draft, qwen]
|
|
10
10
|
checksum: sha256-pending
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# MyLLMAPI
|
|
14
14
|
|
|
15
|
-
A
|
|
15
|
+
A two-surface LLM gateway:
|
|
16
|
+
|
|
17
|
+
- **Raw** — `complete` / `embed` / `models`. You pick a self-hosted catalog model (today: `Qwen/Qwen3-Coder-30B-A3B-Instruct`). Proprietary models are not callable here.
|
|
18
|
+
- **Verbs** — `classify` / `extract` / `summarize` / `draft`. You ask for a task done; the model is implementation detail and is never named in the response.
|
|
19
|
+
|
|
20
|
+
Pricing is in cents per 1M tokens at the actual upstream rate; cost is debited from your MyAPI balance.
|
|
16
21
|
|
|
17
22
|
## Capabilities
|
|
18
23
|
<!-- llm:start -->
|
|
19
|
-
Use this for workflow tasks — summarize a doc, classify an inbound email,
|
|
24
|
+
Use this for workflow tasks — summarize a doc, classify an inbound email, extract structured data, draft a reply. The reply goes to stdout; a one-line usage footer (tokens + cost + finish reason) goes to stderr, so `myapi llm complete ... | jq` and similar pipelines work as expected.
|
|
25
|
+
|
|
26
|
+
**Don't use this as your own model.** If you're an agent reading this, you already have a more capable model than what's exposed here. Reach for the LLM verbs when you're scripting a recurring step where a small/cheap model is the right tool — not for one-shot reasoning you can just do yourself.
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
### Raw vs. verbs
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
- **Raw `complete`** — full control: pick the model, build the `messages` array, set `max_tokens`/`temperature`/`stop`. Use when shape matters.
|
|
31
|
+
- **Verbs** — you want a *result* (label, JSON, summary, draft). Use when you don't care which model runs underneath.
|
|
32
|
+
|
|
33
|
+
### Raw `complete` request
|
|
24
34
|
```json
|
|
25
35
|
{
|
|
26
|
-
"model": "
|
|
36
|
+
"model": "Qwen/Qwen3-Coder-30B-A3B-Instruct",
|
|
27
37
|
"messages": [
|
|
28
38
|
{ "role": "system", "content": "You are a terse editor." },
|
|
29
39
|
{ "role": "user", "content": "Tighten this paragraph: ..." }
|
|
@@ -36,90 +46,109 @@ Use this for workflow tasks — summarize a doc, classify an inbound email, embe
|
|
|
36
46
|
|
|
37
47
|
Roles: `system | user | assistant`. Multiple system messages collapse to one instruction. `max_tokens`, `temperature`, and `stop` are optional — per-model defaults apply.
|
|
38
48
|
|
|
39
|
-
###
|
|
49
|
+
### Raw `complete` response
|
|
40
50
|
```json
|
|
41
51
|
{
|
|
42
|
-
"model": "
|
|
52
|
+
"model": "Qwen/Qwen3-Coder-30B-A3B-Instruct",
|
|
43
53
|
"content": "...assistant reply...",
|
|
44
54
|
"finish_reason": "stop",
|
|
45
|
-
"usage": { "input_tokens": 42, "output_tokens": 87, "
|
|
55
|
+
"usage": { "input_tokens": 42, "output_tokens": 87, "cost_cents": 0.029 }
|
|
46
56
|
}
|
|
47
57
|
```
|
|
48
58
|
|
|
49
|
-
`finish_reason` is
|
|
59
|
+
`finish_reason` is one of `stop` (normal), `length` (hit max_tokens), `filter` (blocked).
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"model": "gemini-embedding-001",
|
|
55
|
-
"embeddings": [[-0.0226, 0.0118, ...]],
|
|
56
|
-
"usage": { "input_tokens": 5, "cost_usd": 0.0000007 }
|
|
57
|
-
}
|
|
58
|
-
```
|
|
61
|
+
If `model` isn't in the self-hosted catalog the server returns `MODEL_NOT_IN_RAW_CATALOG` — that's the signal to use a verb instead, not to retry with a different `--model`.
|
|
62
|
+
|
|
63
|
+
### Raw `embed`
|
|
59
64
|
|
|
60
|
-
|
|
65
|
+
No embedding model is served on the raw catalog today; calls return `EMBED_NOT_AVAILABLE`. Use a dedicated embedding API for now.
|
|
61
66
|
|
|
62
67
|
### Model catalog
|
|
63
|
-
- Chat models: `id`, `kind: 'chat'`, `
|
|
64
|
-
- Embed models: `id`, `kind: 'embed'`, `dimensions`, `
|
|
68
|
+
- Chat models: `id`, `kind: 'chat'`, `context_window`, `input_cost_per_1m_cents`, `output_cost_per_1m_cents`
|
|
69
|
+
- Embed models: `id`, `kind: 'embed'`, `dimensions`, `input_cost_per_1m_cents`
|
|
70
|
+
|
|
71
|
+
The catalog is **live** — it reflects what the inference gateway actually serves, refreshed every 15 minutes. Always query `models` rather than hard-coding ids.
|
|
72
|
+
|
|
73
|
+
### Verb requests + responses
|
|
74
|
+
|
|
75
|
+
Every verb takes an optional `tier` (`fast | reasoning | cheap`) — opaque routing hint, server picks the model. Response usage block is identical across verbs.
|
|
76
|
+
|
|
77
|
+
| Verb | Request body | Response `data` |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| `classify` | `{ input, labels: string[], multi?: boolean, tier? }` | `{ label }` or `{ labels: string[] }` (when `multi`) |
|
|
80
|
+
| `extract` | `{ input, schema: <json-schema>, tier? }` | `{ data: <object conforming to schema> }` |
|
|
81
|
+
| `summarize`| `{ input, style?: 'brief'\|'exec'\|'bullet', tier? }` | `{ summary }` |
|
|
82
|
+
| `draft` | `{ input?, kind: string, context?: object, prompt?: string, tier? }` | `{ text }` |
|
|
83
|
+
|
|
84
|
+
Shared usage block on every verb:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{ "tier_used": "fast", "tokens_in": 65, "tokens_out": 37, "cost_cents": 0.005 }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The model/provider is **never** named in the verb response. That's the point — the verb is the contract, the model is implementation.
|
|
65
91
|
|
|
66
|
-
Today: `gemini-3.1-pro-preview`, `gemini-3.1-flash-lite-preview`, `gemini-embedding-001`. Catalog is live — always query `models` rather than hard-coding ids.
|
|
67
92
|
<!-- llm:end -->
|
|
68
93
|
|
|
69
94
|
## Commands
|
|
70
95
|
<!-- generated:start -->
|
|
71
96
|
| Command | What it does |
|
|
72
97
|
|---|---|
|
|
73
|
-
| `myapi llm models [--kind chat\|embed] [--json]` | List
|
|
74
|
-
| `myapi llm complete "<prompt>" [--model <id>] [--system "<s>"] [--max-tokens N] [--temperature 0..1] [--stop <csv>] [--file <path>] [--json]` |
|
|
75
|
-
| `myapi llm embed "<text>" --model <id> [--
|
|
98
|
+
| `myapi llm models [--kind chat\|embed] [--json]` | List the live model catalog with pricing (cents/1M) |
|
|
99
|
+
| `myapi llm complete "<prompt>" [--model <id>] [--system "<s>"] [--max-tokens N] [--temperature 0..1] [--stop <csv>] [--file <path>] [--json]` | Raw chat completion; reply to stdout, usage to stderr. Defaults to the first chat model in the catalog |
|
|
100
|
+
| `myapi llm embed "<text>" --model <id> [--json]` | Embed a string (no model served today — returns EMBED_NOT_AVAILABLE) |
|
|
101
|
+
| `myapi llm classify "<input>" --labels <csv> [--multi] [--tier <t>] [--json]` | Pick a label from a set |
|
|
102
|
+
| `myapi llm extract "<input>" --schema <path\|json> [--tier <t>] [--json]` | Pull structured data conforming to a JSON Schema |
|
|
103
|
+
| `myapi llm summarize "<input>" [--style brief\|exec\|bullet] [--tier <t>] [--json]` | Summarize text |
|
|
104
|
+
| `myapi llm draft --kind <what> [--prompt "<s>"] [--context <json>] ["<src>"] [--tier <t>] [--json]` | Draft an email / reply / message / … |
|
|
76
105
|
<!-- generated:end -->
|
|
77
106
|
|
|
78
|
-
Pass `-` as the prompt to read from stdin. Pass `--file <path>` to read
|
|
107
|
+
Pass `-` as the prompt/input to read from stdin. Pass `--file <path>` to read longer content from disk.
|
|
79
108
|
|
|
80
109
|
## Examples
|
|
81
110
|
<!-- llm:start -->
|
|
82
111
|
```bash
|
|
83
|
-
# List the catalog
|
|
112
|
+
# List the live catalog
|
|
84
113
|
myapi llm models
|
|
85
|
-
|
|
86
|
-
myapi llm models --kind embed --json | jq '.models[].id'
|
|
114
|
+
myapi llm models --kind chat --json | jq '.models[].id'
|
|
87
115
|
|
|
88
|
-
#
|
|
116
|
+
# Raw completion — picks the first chat model from the catalog
|
|
89
117
|
myapi llm complete "Summarize in 12 words: $(cat README.md)"
|
|
90
118
|
|
|
91
|
-
#
|
|
92
|
-
myapi llm complete "
|
|
93
|
-
--model
|
|
94
|
-
--system "You are a
|
|
95
|
-
--max-tokens
|
|
119
|
+
# Pin a specific model
|
|
120
|
+
myapi llm complete "Refactor this function: ..." \
|
|
121
|
+
--model Qwen/Qwen3-Coder-30B-A3B-Instruct \
|
|
122
|
+
--system "You are a careful Go reviewer." \
|
|
123
|
+
--max-tokens 600
|
|
96
124
|
|
|
97
|
-
#
|
|
98
|
-
cat email.txt | myapi llm complete - \
|
|
99
|
-
--system "Classify intent: support | sales | spam"
|
|
125
|
+
# ── Verbs (recommended for workflow steps) ──────────────────────────────
|
|
100
126
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
> doc.vec.json
|
|
104
|
-
```
|
|
127
|
+
myapi llm classify "I was charged twice — please refund." \
|
|
128
|
+
--labels billing,technical,sales,spam
|
|
105
129
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
--
|
|
113
|
-
|
|
130
|
+
myapi llm extract "Acme Corp employs 250 people in Berlin." \
|
|
131
|
+
--schema '{"type":"object","properties":{"company":{"type":"string"},"employees":{"type":"integer"}}}'
|
|
132
|
+
|
|
133
|
+
myapi llm summarize --file long-thread.txt --style bullet
|
|
134
|
+
|
|
135
|
+
myapi llm draft --kind email \
|
|
136
|
+
--prompt "Friendly welcome, under 60 words." \
|
|
137
|
+
--context '{"recipient":"a new signup","product":"MyAPI"}'
|
|
138
|
+
|
|
139
|
+
# Classify + route an inbound webhook delivery
|
|
140
|
+
BODY=$(myapi webhook delivery <id> --json | jq -r '.body')
|
|
141
|
+
INTENT=$(printf '%s' "$BODY" | myapi llm classify - \
|
|
142
|
+
--labels support,sales,spam --json | jq -r '.data.label')
|
|
114
143
|
```
|
|
115
144
|
<!-- llm:end -->
|
|
116
145
|
|
|
117
146
|
## Notes
|
|
118
147
|
|
|
119
|
-
-
|
|
120
|
-
- **
|
|
121
|
-
- **
|
|
122
|
-
|
|
123
|
-
- **
|
|
124
|
-
|
|
125
|
-
|
|
148
|
+
- **`draft` context safety — what the platform does and doesn't do.** Every field in `context` is quoted into the prompt as referent data the model is told to incorporate. Sensitive-named keys (`secret`, `api_key`, `password`, `token`, etc.) are **not** automatically redacted — if you pass `{"secret": "abc123"}`, the model may include `"abc123"` in the output. The verb does two things on top:
|
|
149
|
+
- **Injection defense.** Keys named `instructions`, `system`, `system_prompt`, `prompt`, `override`, `directives`, and the JS prototype-pollution sentinels are stripped at the boundary and surfaced in `meta.warnings`; the model can't be hijacked through them.
|
|
150
|
+
- **Output guardrail.** After generation, every fact value (length ≥ 4) is substring-scanned in the output; matches surface under `meta.guardrails.facts_in_output: ["secret", "api_key", ...]`. This is a **signal, not a redaction** — you see when a value landed in the body.
|
|
151
|
+
Rule of thumb: if a value must not appear in the output, **do not put it in `context`**. Pass identifiers (recipient name, account id, topic) and let the prompt reference them indirectly; keep credentials, PII, and any internal-only metadata out of the body entirely.
|
|
152
|
+
- **Self-hosted raw, server-picked verbs.** Raw runs on MyAPI's TPU; verbs route wherever the server picks (future proprietary models land here, wrapped behind the verb contract).
|
|
153
|
+
- **Cost + latency.** `usage.cost_cents` is authoritative — no markup today. Qwen3-Coder-30B-A3B: 200–600ms to first token, 1–3s end-to-end on a few-hundred-token reply.
|
|
154
|
+
- **Live catalog, no streaming, no BYOK.** Don't hard-code model ids — `models` is the source of truth (CLI picks if `--model` omitted). Full reply only.
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
name: my-webhook-api
|
|
3
3
|
version: 1.0.0
|
|
4
4
|
description: >
|
|
5
|
-
Inbound webhook endpoints
|
|
6
|
-
triggers: [webhook, inbound, receiver,
|
|
5
|
+
Inbound webhook endpoints for non-funnel sources — Stripe, GitHub, custom services. Funnel forms use the my-funnel-api proxy.
|
|
6
|
+
triggers: [webhook, inbound, receiver, stripe events, github webhook, slack notification, delivery, payload, event ingest]
|
|
7
7
|
checksum: sha256-pending
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -13,6 +13,8 @@ Per-org HTTP endpoints that accept inbound POSTs and durably store every deliver
|
|
|
13
13
|
|
|
14
14
|
## Capabilities
|
|
15
15
|
<!-- llm:start -->
|
|
16
|
+
**Scope: non-funnel inbound.** Use this for Stripe → MyAPI, GitHub → MyAPI, custom services → MyAPI, anything where an *external* system POSTs to you. **Funnel forms use the funnel proxy** (see `my-funnel-api`: `myapi funnel form ... --capture-to webhook:<id>` if you need to bind a specific destination; otherwise the funnel's auto-provisioned webhook handles it). Don't hardcode `api.mywebhookapi.com/webhook/in/<slug>` URLs into funnel HTML.
|
|
17
|
+
|
|
16
18
|
Each endpoint has a unique inbound URL minted at create time. Anyone who knows the URL can POST to it; the body is stored verbatim along with headers and a timestamp. Deliveries are kept indefinitely (until you delete the endpoint).
|
|
17
19
|
|
|
18
20
|
The inbound URL accepts **any JSON body** — no enforced schema, no required fields. Whatever you POST is what gets stored. Two consequences:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myapihq/cli",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.11",
|
|
5
5
|
"description": "MyAPI command-line interface",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -24,12 +24,13 @@
|
|
|
24
24
|
"check-coverage": "npm run build && node scripts/check-coverage.js",
|
|
25
25
|
"check-coverage:live": "npm run build && node scripts/check-coverage.js --live",
|
|
26
26
|
"update-schema": "node scripts/update-schema.js",
|
|
27
|
+
"lint:changelog": "node ../../scripts/lint-changelog.js",
|
|
28
|
+
"lint:help-order": "node scripts/lint-help-order.js",
|
|
27
29
|
"lint:skills": "node scripts/lint-skills.js",
|
|
28
|
-
"lint:skills:strict": "node scripts/lint-skills.js --strict"
|
|
29
|
-
"lint:changelog": "node ../../scripts/lint-changelog.js"
|
|
30
|
+
"lint:skills:strict": "node scripts/lint-skills.js --strict"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@myapihq/sdk": "^1.3.
|
|
33
|
+
"@myapihq/sdk": "^1.3.11"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@types/node": "^25.6.0",
|