@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
package/dist/commands/image.js
CHANGED
|
@@ -191,12 +191,12 @@ export async function run(subcommand, args, flags) {
|
|
|
191
191
|
info(`Usage: myapi image <subcommand>
|
|
192
192
|
|
|
193
193
|
Subcommands:
|
|
194
|
-
|
|
194
|
+
delete <job_id> Delete the asset (job history retained)
|
|
195
195
|
generate <prompt> Generate a new AI image (async, polls up to 90s; pricing per model — see "image models")
|
|
196
196
|
get <job_id> Fetch full job metadata (JSON or human-readable)
|
|
197
197
|
get-url <job_id> Print just the asset URL (curl-friendly)
|
|
198
|
+
list List all your generated images
|
|
198
199
|
models List available image models with per-image pricing
|
|
199
|
-
delete <job_id> Delete the asset (job history retained)
|
|
200
200
|
|
|
201
201
|
All commands accept --org <id> (or set default: myapi config set-org <id>).
|
|
202
202
|
The asset lands in your org's storage and is also visible via "myapi storage list".`);
|
package/dist/commands/keys.js
CHANGED
|
@@ -174,8 +174,8 @@ slot grants, and an optional spend cap. A minted key is always a subset of the
|
|
|
174
174
|
key that minted it (no privilege escalation).
|
|
175
175
|
|
|
176
176
|
Subcommands:
|
|
177
|
-
list List keys with kind, scope, grants, and spend cap
|
|
178
177
|
create Mint a new key (value shown once)
|
|
178
|
+
list List keys with kind, scope, grants, and spend cap
|
|
179
179
|
revoke <id> Revoke one key by ID
|
|
180
180
|
revoke-all Kill switch — revoke every key (or --kind function|manual|account)
|
|
181
181
|
|
package/dist/commands/llm.js
CHANGED
|
@@ -7,6 +7,7 @@ export const EXPOSES = [
|
|
|
7
7
|
'POST /llm/orgs/{org_id}/complete',
|
|
8
8
|
'POST /llm/orgs/{org_id}/embed',
|
|
9
9
|
'GET /llm/orgs/{org_id}/models',
|
|
10
|
+
'POST /llm/orgs/{org_id}/tasks/{verb}',
|
|
10
11
|
];
|
|
11
12
|
export const SCHEMA = {
|
|
12
13
|
org: 'string',
|
|
@@ -16,8 +17,36 @@ export const SCHEMA = {
|
|
|
16
17
|
temperature: 'number',
|
|
17
18
|
stop: 'string',
|
|
18
19
|
file: 'string',
|
|
20
|
+
// verb-specific
|
|
21
|
+
labels: 'string',
|
|
22
|
+
multi: 'boolean',
|
|
23
|
+
schema: 'string',
|
|
24
|
+
style: 'string',
|
|
19
25
|
kind: 'string',
|
|
26
|
+
context: 'string',
|
|
27
|
+
prompt: 'string',
|
|
28
|
+
tier: 'string',
|
|
20
29
|
};
|
|
30
|
+
// Cache the catalog once per CLI invocation so verbs (which don't take a
|
|
31
|
+
// --model flag) can fall back to "the first chat model" without an extra
|
|
32
|
+
// network round-trip per command.
|
|
33
|
+
let cachedModels = null;
|
|
34
|
+
async function loadModels(apiKey, orgId) {
|
|
35
|
+
if (cachedModels)
|
|
36
|
+
return cachedModels;
|
|
37
|
+
const res = await sdkLlm.listModels(apiKey, orgId);
|
|
38
|
+
cachedModels = res.models;
|
|
39
|
+
return cachedModels;
|
|
40
|
+
}
|
|
41
|
+
async function defaultChatModel(apiKey, orgId) {
|
|
42
|
+
const models = await loadModels(apiKey, orgId);
|
|
43
|
+
const chat = models.find(m => m.kind === 'chat');
|
|
44
|
+
if (!chat) {
|
|
45
|
+
error('No chat model available — run "myapi llm models" to inspect the catalog.');
|
|
46
|
+
throw new Error('unreachable'); // satisfy the type checker
|
|
47
|
+
}
|
|
48
|
+
return chat.id;
|
|
49
|
+
}
|
|
21
50
|
function readPromptArg(promptArg, flags) {
|
|
22
51
|
if (typeof flags.file === 'string' && flags.file) {
|
|
23
52
|
return fs.readFileSync(flags.file, 'utf-8');
|
|
@@ -27,11 +56,62 @@ function readPromptArg(promptArg, flags) {
|
|
|
27
56
|
}
|
|
28
57
|
return promptArg ?? '';
|
|
29
58
|
}
|
|
30
|
-
|
|
59
|
+
function fmtCostCents(cents) {
|
|
60
|
+
// Sub-cent calls (the common case for short prompts) get four decimals so
|
|
61
|
+
// the footer stays informative — "0.0063¢" reads honestly as "less than a
|
|
62
|
+
// cent." Larger numbers collapse to two decimals.
|
|
63
|
+
return cents < 1 ? `${cents.toFixed(4)}¢` : `${cents.toFixed(2)}¢`;
|
|
64
|
+
}
|
|
65
|
+
function parseSchemaFlag(flags) {
|
|
66
|
+
if (typeof flags.schema !== 'string' || !flags.schema)
|
|
67
|
+
return null;
|
|
68
|
+
// Accept either a path to a JSON file or an inline JSON string.
|
|
69
|
+
let raw = flags.schema;
|
|
70
|
+
if (fs.existsSync(raw)) {
|
|
71
|
+
try {
|
|
72
|
+
raw = fs.readFileSync(raw, 'utf-8');
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
error(`Could not read --schema file: ${e.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(raw);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
error('--schema must be valid JSON (inline) or a path to a JSON file');
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function parseContextFlag(flags) {
|
|
87
|
+
if (typeof flags.context !== 'string' || !flags.context)
|
|
88
|
+
return undefined;
|
|
89
|
+
try {
|
|
90
|
+
const parsed = JSON.parse(flags.context);
|
|
91
|
+
if (typeof parsed === 'object' && parsed !== null)
|
|
92
|
+
return parsed;
|
|
93
|
+
error('--context must be a JSON object');
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
error('--context must be valid JSON');
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function tierFromFlag(flags) {
|
|
102
|
+
if (typeof flags.tier !== 'string' || !flags.tier)
|
|
103
|
+
return undefined;
|
|
104
|
+
if (flags.tier === 'fast' || flags.tier === 'reasoning' || flags.tier === 'cheap')
|
|
105
|
+
return flags.tier;
|
|
106
|
+
error('--tier must be one of: fast, reasoning, cheap');
|
|
107
|
+
}
|
|
108
|
+
// ── raw ──────────────────────────────────────────────────────────────────
|
|
31
109
|
async function complete(promptArg, flags) {
|
|
32
110
|
const config = requireConfig();
|
|
33
111
|
const orgId = requireOrg(flags, config, 'myapi llm complete "<prompt>" [--model <id>] [--system <s>] [--max-tokens N] [--temperature 0..1] [--stop <csv>] [--org <id>]');
|
|
34
|
-
const model = typeof flags.model === 'string' && flags.model
|
|
112
|
+
const model = typeof flags.model === 'string' && flags.model
|
|
113
|
+
? flags.model
|
|
114
|
+
: await defaultChatModel(config.api_key, orgId);
|
|
35
115
|
const prompt = readPromptArg(promptArg, flags);
|
|
36
116
|
if (!prompt.trim())
|
|
37
117
|
error('Empty prompt. Pass <prompt> as an argument, "-" to read stdin, or --file <path>.');
|
|
@@ -56,7 +136,7 @@ async function complete(promptArg, flags) {
|
|
|
56
136
|
}
|
|
57
137
|
info(res.content);
|
|
58
138
|
// Usage footer on stderr so it doesn't pollute piped output.
|
|
59
|
-
process.stderr.write(`\n— ${res.model} · ${res.usage.input_tokens}+${res.usage.output_tokens} tokens ·
|
|
139
|
+
process.stderr.write(`\n— ${res.model} · ${res.usage.input_tokens}+${res.usage.output_tokens ?? 0} tokens · ${fmtCostCents(res.usage.cost_cents)} · ${res.finish_reason}\n`);
|
|
60
140
|
}
|
|
61
141
|
async function embed(inputArg, flags) {
|
|
62
142
|
const config = requireConfig();
|
|
@@ -73,7 +153,7 @@ async function embed(inputArg, flags) {
|
|
|
73
153
|
return;
|
|
74
154
|
}
|
|
75
155
|
const first = res.embeddings[0] ?? [];
|
|
76
|
-
info(`${res.model} · dim=${first.length} · ${res.usage.input_tokens} tokens ·
|
|
156
|
+
info(`${res.model} · dim=${first.length} · ${res.usage.input_tokens} tokens · ${fmtCostCents(res.usage.cost_cents)}`);
|
|
77
157
|
info(`first 5: [${first.slice(0, 5).map(n => n.toFixed(6)).join(', ')}${first.length > 5 ? ', ...' : ''}]`);
|
|
78
158
|
info(`(pass --json for the full vector)`);
|
|
79
159
|
}
|
|
@@ -90,49 +170,198 @@ async function listModels(flags) {
|
|
|
90
170
|
printTable(models.map(m => ({
|
|
91
171
|
id: m.id,
|
|
92
172
|
kind: m.kind,
|
|
93
|
-
context: m.
|
|
173
|
+
context: m.context_window ?? '',
|
|
94
174
|
dimensions: m.dimensions ?? '',
|
|
95
|
-
|
|
96
|
-
|
|
175
|
+
'in ¢/1M': m.input_cost_per_1m_cents,
|
|
176
|
+
'out ¢/1M': m.output_cost_per_1m_cents != null ? m.output_cost_per_1m_cents : '',
|
|
97
177
|
})), { flags, empty: 'No models available.' });
|
|
98
178
|
}
|
|
179
|
+
// ── verbs ────────────────────────────────────────────────────────────────
|
|
180
|
+
function printVerbFooter(usage) {
|
|
181
|
+
process.stderr.write(`\n— tier=${usage.tier_used} · ${usage.tokens_in}+${usage.tokens_out} tokens · ${fmtCostCents(usage.cost_cents)}\n`);
|
|
182
|
+
}
|
|
183
|
+
async function classify(inputArg, flags) {
|
|
184
|
+
const config = requireConfig();
|
|
185
|
+
const orgId = requireOrg(flags, config, 'myapi llm classify "<input>" --labels <csv> [--multi] [--tier <t>] [--org <id>]');
|
|
186
|
+
const input = readPromptArg(inputArg, flags);
|
|
187
|
+
if (!input.trim())
|
|
188
|
+
error('Empty input. Pass <input> as an argument, "-" to read stdin, or --file <path>.');
|
|
189
|
+
if (typeof flags.labels !== 'string' || !flags.labels) {
|
|
190
|
+
error('Missing required flag: --labels <comma-separated>');
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const labels = flags.labels.split(',').map(s => s.trim()).filter(Boolean);
|
|
194
|
+
if (labels.length === 0)
|
|
195
|
+
error('--labels must contain at least one label');
|
|
196
|
+
const multi = flags.multi === true;
|
|
197
|
+
const res = await sdkLlm.classify(config.api_key, orgId, { input, labels, multi, tier: tierFromFlag(flags) });
|
|
198
|
+
if (flags.json) {
|
|
199
|
+
printJson(res);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (multi) {
|
|
203
|
+
const arr = res.data.labels ?? [];
|
|
204
|
+
info(arr.length ? arr.join(', ') : '(no matching labels)');
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
info(res.data.label ?? '(no label)');
|
|
208
|
+
}
|
|
209
|
+
printVerbFooter(res.usage);
|
|
210
|
+
}
|
|
211
|
+
async function extract(inputArg, flags) {
|
|
212
|
+
const config = requireConfig();
|
|
213
|
+
const orgId = requireOrg(flags, config, 'myapi llm extract "<input>" --schema <path|json> [--tier <t>] [--org <id>]');
|
|
214
|
+
const input = readPromptArg(inputArg, flags);
|
|
215
|
+
if (!input.trim())
|
|
216
|
+
error('Empty input. Pass <input> as an argument, "-" to read stdin, or --file <path>.');
|
|
217
|
+
const schema = parseSchemaFlag(flags);
|
|
218
|
+
if (!schema) {
|
|
219
|
+
error('Missing required flag: --schema <path|json>');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const res = await sdkLlm.extract(config.api_key, orgId, { input, schema, tier: tierFromFlag(flags) });
|
|
223
|
+
if (flags.json) {
|
|
224
|
+
printJson(res);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
info(JSON.stringify(res.data.data, null, 2));
|
|
228
|
+
printVerbFooter(res.usage);
|
|
229
|
+
}
|
|
230
|
+
async function summarize(inputArg, flags) {
|
|
231
|
+
const config = requireConfig();
|
|
232
|
+
const orgId = requireOrg(flags, config, 'myapi llm summarize "<input>" [--style brief|exec|bullet] [--tier <t>] [--org <id>]');
|
|
233
|
+
const input = readPromptArg(inputArg, flags);
|
|
234
|
+
if (!input.trim())
|
|
235
|
+
error('Empty input. Pass <input> as an argument, "-" to read stdin, or --file <path>.');
|
|
236
|
+
const style = typeof flags.style === 'string' && flags.style ? flags.style : undefined;
|
|
237
|
+
if (style && !['brief', 'exec', 'bullet'].includes(style)) {
|
|
238
|
+
error('--style must be one of: brief, exec, bullet');
|
|
239
|
+
}
|
|
240
|
+
const res = await sdkLlm.summarize(config.api_key, orgId, {
|
|
241
|
+
input,
|
|
242
|
+
style: style,
|
|
243
|
+
tier: tierFromFlag(flags),
|
|
244
|
+
});
|
|
245
|
+
if (flags.json) {
|
|
246
|
+
printJson(res);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
info(res.data.summary);
|
|
250
|
+
printVerbFooter(res.usage);
|
|
251
|
+
}
|
|
252
|
+
async function draft(inputArg, flags) {
|
|
253
|
+
const config = requireConfig();
|
|
254
|
+
const orgId = requireOrg(flags, config, 'myapi llm draft --kind <what> [--prompt "<instructions>"] [--context <json>] ["<source text>"] [--tier <t>] [--org <id>]');
|
|
255
|
+
if (typeof flags.kind !== 'string' || !flags.kind) {
|
|
256
|
+
error('Missing required flag: --kind <email|message|reply|...>');
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const input = readPromptArg(inputArg, flags);
|
|
260
|
+
const promptText = typeof flags.prompt === 'string' ? flags.prompt : '';
|
|
261
|
+
const ctx = parseContextFlag(flags);
|
|
262
|
+
if (!input.trim() && !promptText.trim() && (!ctx || Object.keys(ctx).length === 0)) {
|
|
263
|
+
error('draft needs at least one of: <source text> (arg or --file), --prompt, or --context.');
|
|
264
|
+
}
|
|
265
|
+
const res = await sdkLlm.draft(config.api_key, orgId, {
|
|
266
|
+
input: input || undefined,
|
|
267
|
+
kind: flags.kind,
|
|
268
|
+
context: ctx,
|
|
269
|
+
prompt: promptText || undefined,
|
|
270
|
+
tier: tierFromFlag(flags),
|
|
271
|
+
});
|
|
272
|
+
if (flags.json) {
|
|
273
|
+
printJson(res);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
info(res.data.text);
|
|
277
|
+
printVerbFooter(res.usage);
|
|
278
|
+
}
|
|
279
|
+
// ── help ─────────────────────────────────────────────────────────────────
|
|
99
280
|
const SUBCOMMAND_USAGE = {
|
|
100
281
|
complete: `myapi llm complete "<prompt>" [--model <id>]
|
|
101
282
|
[--system "<system msg>"] [--max-tokens N] [--temperature 0..1]
|
|
102
283
|
[--stop <csv>] [--file <path>] [--org <id>] [--json]
|
|
103
284
|
|
|
104
|
-
|
|
105
|
-
|
|
285
|
+
Raw chat completion against a self-hosted catalog model. Without --model,
|
|
286
|
+
picks the first chat model from "myapi llm models" (today:
|
|
287
|
+
Qwen/Qwen3-Coder-30B-A3B-Instruct).
|
|
106
288
|
|
|
107
|
-
Pass "-" as the prompt to read from stdin. The reply goes to stdout;
|
|
108
|
-
|
|
109
|
-
pollute piped output.
|
|
289
|
+
Pass "-" as the prompt to read from stdin. The reply goes to stdout; a
|
|
290
|
+
one-line usage footer (tokens + cost in cents) goes to stderr so it
|
|
291
|
+
doesn't pollute piped output.
|
|
110
292
|
|
|
111
293
|
Examples:
|
|
112
294
|
myapi llm complete "summarize: $(cat README.md)"
|
|
113
|
-
cat draft.md | myapi llm complete - --
|
|
114
|
-
myapi llm complete --file prompt.txt --max-tokens 200`,
|
|
295
|
+
cat draft.md | myapi llm complete - --system "You are an editor" --max-tokens 200`,
|
|
115
296
|
embed: `myapi llm embed "<text>" --model <id> [--file <path>] [--org <id>] [--json]
|
|
116
297
|
|
|
117
|
-
|
|
298
|
+
No embedding model is served on the raw catalog today — this returns
|
|
299
|
+
EMBED_NOT_AVAILABLE until one is. Use a dedicated embedding API for now.`,
|
|
300
|
+
models: `myapi llm models [--kind chat|embed] [--org <id>] [--json]
|
|
301
|
+
|
|
302
|
+
Lists the live model catalog with per-1M-token pricing in cents.
|
|
303
|
+
The catalog reflects exactly what the inference gateway serves.`,
|
|
304
|
+
classify: `myapi llm classify "<input>" --labels <a,b,c> [--multi]
|
|
305
|
+
[--tier fast|reasoning|cheap] [--file <path>] [--org <id>] [--json]
|
|
306
|
+
|
|
307
|
+
Pick the best label (or with --multi, all applicable labels) from a set.
|
|
308
|
+
Output goes to stdout; usage footer to stderr.
|
|
118
309
|
|
|
119
310
|
Examples:
|
|
120
|
-
myapi llm
|
|
121
|
-
|
|
122
|
-
|
|
311
|
+
myapi llm classify "I was charged twice and need a refund" \\
|
|
312
|
+
--labels billing,technical,sales,spam
|
|
313
|
+
cat email.txt | myapi llm classify - --labels urgent,bug,question --multi`,
|
|
314
|
+
extract: `myapi llm extract "<input>" --schema <path|json>
|
|
315
|
+
[--tier <t>] [--file <path>] [--org <id>] [--json]
|
|
316
|
+
|
|
317
|
+
Extract data from <input> conforming to a JSON Schema. --schema accepts
|
|
318
|
+
an inline JSON string OR a path to a .json file. Stdout is the extracted
|
|
319
|
+
JSON object.
|
|
320
|
+
|
|
321
|
+
Examples:
|
|
322
|
+
myapi llm extract "Acme Corp has 250 employees" \\
|
|
323
|
+
--schema '{"type":"object","properties":{"company":{"type":"string"},"employees":{"type":"integer"}}}'
|
|
324
|
+
myapi llm extract - --schema ./order.schema.json < order.eml`,
|
|
325
|
+
summarize: `myapi llm summarize "<input>" [--style brief|exec|bullet]
|
|
326
|
+
[--tier <t>] [--file <path>] [--org <id>] [--json]
|
|
123
327
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
328
|
+
Summarize text. Default style 'brief' (1-2 sentences); 'exec' is a
|
|
329
|
+
decision-maker summary; 'bullet' is a short list.
|
|
330
|
+
|
|
331
|
+
Examples:
|
|
332
|
+
myapi llm summarize --file long-thread.txt --style bullet
|
|
333
|
+
cat report.md | myapi llm summarize - --style exec`,
|
|
334
|
+
draft: `myapi llm draft --kind <what> [--prompt "<instructions>"]
|
|
335
|
+
[--context '<json>'] ["<source text>"] [--file <path>]
|
|
336
|
+
[--tier <t>] [--org <id>] [--json]
|
|
337
|
+
|
|
338
|
+
Draft a piece of writing (email, reply, message, …). Provide at least one
|
|
339
|
+
of: <source text> (arg or --file), --prompt, or --context (JSON of facts).
|
|
340
|
+
|
|
341
|
+
Examples:
|
|
342
|
+
myapi llm draft --kind email \\
|
|
343
|
+
--prompt "Friendly welcome to the beta. Under 60 words." \\
|
|
344
|
+
--context '{"recipient":"a new signup","product":"MyAPI"}'
|
|
345
|
+
cat inbound.eml | myapi llm draft - --kind reply --prompt "Acknowledge and ask for the order ID"`,
|
|
127
346
|
};
|
|
128
347
|
export async function run(subcommand, args, flags) {
|
|
129
348
|
if (!subcommand || (flags.help && !subcommand)) {
|
|
130
349
|
info(`Usage: myapi llm <subcommand>
|
|
131
350
|
|
|
351
|
+
Two surfaces:
|
|
352
|
+
|
|
353
|
+
Raw — you pick a self-hosted catalog model and shape the prompt yourself.
|
|
354
|
+
Verbs — you ask for a task done; the model is implementation detail and
|
|
355
|
+
is never named in the response.
|
|
356
|
+
|
|
132
357
|
Subcommands:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
358
|
+
classify Pick a label from a set
|
|
359
|
+
complete Raw chat completion against a catalog model
|
|
360
|
+
draft Write something (email | reply | message | …)
|
|
361
|
+
embed Raw embeddings (no model served on raw today)
|
|
362
|
+
extract Pull structured data conforming to a JSON Schema
|
|
363
|
+
models List the live model catalog (id, kind, context, cents/1M)
|
|
364
|
+
summarize Summarize text (brief | exec | bullet)
|
|
136
365
|
|
|
137
366
|
All commands accept --org <id> (or set default: myapi config set-org <id>).
|
|
138
367
|
Use this for workflow steps and scripted pipelines — not as a replacement
|
|
@@ -151,6 +380,10 @@ for your own reasoning. The agent has its own model already.`);
|
|
|
151
380
|
case 'complete': return complete(args[0], flags);
|
|
152
381
|
case 'embed': return embed(args[0], flags);
|
|
153
382
|
case 'models': return listModels(flags);
|
|
383
|
+
case 'classify': return classify(args[0], flags);
|
|
384
|
+
case 'extract': return extract(args[0], flags);
|
|
385
|
+
case 'summarize': return summarize(args[0], flags);
|
|
386
|
+
case 'draft': return draft(args[0], flags);
|
|
154
387
|
default: error(`Unknown subcommand: ${subcommand}. Run "myapi llm --help" for valid subcommands.`);
|
|
155
388
|
}
|
|
156
389
|
}
|
package/dist/commands/org.js
CHANGED
|
@@ -217,12 +217,12 @@ export async function run(subcommand, args, flags) {
|
|
|
217
217
|
info(`Usage: myapi org <subcommand>
|
|
218
218
|
|
|
219
219
|
Subcommands:
|
|
220
|
-
list List organizations
|
|
221
220
|
create Create an organization (e.g. myapi org create "Name" --yes)
|
|
222
|
-
update Update an organization's fields (name, tagline, description, etc.)
|
|
223
|
-
get Get details of an organization
|
|
224
221
|
delete Delete an organization
|
|
225
|
-
|
|
222
|
+
get Get details of an organization
|
|
223
|
+
list List organizations
|
|
224
|
+
sync-brand Sync brand info (name, logo, description) from an existing website
|
|
225
|
+
update Update an organization's fields (name, tagline, description, etc.)`);
|
|
226
226
|
return;
|
|
227
227
|
}
|
|
228
228
|
if (flags.help) {
|
|
@@ -189,12 +189,12 @@ Take payments with Stripe Checkout. Connect your Stripe account, then
|
|
|
189
189
|
create one-off or recurring charges and refund them.
|
|
190
190
|
|
|
191
191
|
Subcommands:
|
|
192
|
-
connect Link your Stripe account (T0 — bring your own key)
|
|
193
|
-
status Show the org's Stripe connection status
|
|
194
192
|
charge Create a charge and get a hosted checkout URL
|
|
195
|
-
|
|
193
|
+
connect Link your Stripe account (T0 — bring your own key)
|
|
196
194
|
get <charge_id> Inspect a charge
|
|
195
|
+
list List charges in your org
|
|
197
196
|
refund <charge_id> Full-refund a charge
|
|
197
|
+
status Show the org's Stripe connection status
|
|
198
198
|
|
|
199
199
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
200
200
|
return;
|
package/dist/commands/people.js
CHANGED
|
@@ -113,8 +113,8 @@ export async function run(subcommand, args, flags) {
|
|
|
113
113
|
info(`Usage: myapi people <subcommand>
|
|
114
114
|
|
|
115
115
|
Subcommands:
|
|
116
|
-
search Filter people across the Goldfox database
|
|
117
116
|
get Get a single person (with embedded company)
|
|
117
|
+
search Filter people across the Goldfox database
|
|
118
118
|
|
|
119
119
|
All commands accept --org <id> (or set a default: myapi config set-org <id>).
|
|
120
120
|
Filter shape is shared with company + audience — see myapi audience --help.`);
|
package/dist/commands/pixel.js
CHANGED
|
@@ -156,12 +156,12 @@ export async function run(subcommand, args, flags) {
|
|
|
156
156
|
info(`Usage: myapi pixel <subcommand>
|
|
157
157
|
|
|
158
158
|
Subcommands:
|
|
159
|
+
audience Geographic distribution sample of your pixel audience
|
|
160
|
+
events Engagement events (sent / open / click / page_visit) by campaign or domain
|
|
161
|
+
identity Resolve the identity graph (emails, IPs, profiles) for a pixel ID
|
|
159
162
|
interactions Get a unified timeline of visits and events
|
|
160
163
|
(requires at least one filter: --website, --campaign-id, or --domain)
|
|
161
164
|
visits Page-visit timeline scoped to a website host
|
|
162
|
-
events Engagement events (sent / open / click / page_visit) by campaign or domain
|
|
163
|
-
audience Geographic distribution sample of your pixel audience
|
|
164
|
-
identity Resolve the identity graph (emails, IPs, profiles) for a pixel ID
|
|
165
165
|
|
|
166
166
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
167
167
|
return;
|
package/dist/commands/queue.js
CHANGED
|
@@ -7,6 +7,7 @@ export const EXPOSES = [
|
|
|
7
7
|
'POST /queue/orgs/{org_id}/queues',
|
|
8
8
|
'GET /queue/orgs/{org_id}/queues',
|
|
9
9
|
'GET /queue/orgs/{org_id}/queues/{name}',
|
|
10
|
+
'DELETE /queue/orgs/{org_id}/queues/{name}',
|
|
10
11
|
'POST /queue/orgs/{org_id}/queues/{name}/jobs',
|
|
11
12
|
'GET /queue/orgs/{org_id}/queues/{name}/jobs',
|
|
12
13
|
'GET /queue/orgs/{org_id}/jobs/{id}',
|
package/dist/commands/storage.js
CHANGED
|
@@ -141,12 +141,12 @@ export async function run(subcommand, args, flags) {
|
|
|
141
141
|
info(`Usage: myapi storage <subcommand>
|
|
142
142
|
|
|
143
143
|
Subcommands:
|
|
144
|
-
|
|
145
|
-
ingest <url> Server pulls a public URL into storage
|
|
146
|
-
upload <file> Direct upload of a local file
|
|
144
|
+
delete <asset_id> Permanently delete a stored asset
|
|
147
145
|
get <asset_id> Fetch asset metadata (name, URL, created_at)
|
|
148
146
|
get-url <asset_id> Print the public CDN URL (no API call)
|
|
149
|
-
|
|
147
|
+
ingest <url> Server pulls a public URL into storage
|
|
148
|
+
list List all your stored assets
|
|
149
|
+
upload <file> Direct upload of a local file
|
|
150
150
|
|
|
151
151
|
All commands accept --org <id> (or set default: myapi config set-org <id>).`);
|
|
152
152
|
return;
|
package/dist/commands/webhook.js
CHANGED
|
@@ -159,11 +159,11 @@ export async function run(subcommand, args, flags) {
|
|
|
159
159
|
info(`Usage: myapi webhook <subcommand>
|
|
160
160
|
|
|
161
161
|
Subcommands:
|
|
162
|
-
list List all inbound webhook endpoints
|
|
163
162
|
create Create a new endpoint to receive data (returns an inbound URL)
|
|
164
|
-
update Patch endpoint name / description / crm-email-path / forward-url
|
|
165
163
|
delete Delete a webhook endpoint
|
|
166
164
|
delivery Inspect a specific webhook delivery (payload, received_at)
|
|
165
|
+
list List all inbound webhook endpoints
|
|
166
|
+
update Patch endpoint name / description / crm-email-path / forward-url
|
|
167
167
|
|
|
168
168
|
All commands accept --org <id> (or set default: myapi config set-org <id>).
|
|
169
169
|
|
|
@@ -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())) {
|
|
@@ -360,15 +373,15 @@ export async function run(subcommand, args, flags) {
|
|
|
360
373
|
myapi workflow create --help
|
|
361
374
|
|
|
362
375
|
Subcommands:
|
|
363
|
-
list List all workflows
|
|
364
|
-
get <id> Show a single workflow
|
|
365
376
|
create Create a workflow (enabled by default; --no-enable to stage)
|
|
366
|
-
update <id> Patch name / endpoint-id / steps
|
|
367
|
-
enable <id> Enable a workflow
|
|
368
|
-
disable <id> Disable a workflow
|
|
369
377
|
delete <id> Delete a workflow
|
|
370
|
-
|
|
378
|
+
disable <id> Disable a workflow
|
|
379
|
+
enable <id> Enable a workflow
|
|
380
|
+
get <id> Show a single workflow
|
|
371
381
|
get-run <run_id> Show a single run with full payload
|
|
382
|
+
list List all workflows
|
|
383
|
+
runs <workflow_id> List recent executions of a workflow
|
|
384
|
+
update <id> Patch name / endpoint-id / steps
|
|
372
385
|
|
|
373
386
|
Step types for --steps:
|
|
374
387
|
send_email {"type":"send_email","from":"<mailbox>","to":"{{payload.email}}","subject":"...","template_id":"<id>"|"html":"..."}
|
package/dist/completion.js
CHANGED
|
@@ -49,7 +49,7 @@ export const SUBCOMMANDS = {
|
|
|
49
49
|
people: ['search', 'get'],
|
|
50
50
|
company: ['search', 'get'],
|
|
51
51
|
audience: ['create', 'list', 'get', 'update', 'delete', 'members', 'refresh'],
|
|
52
|
-
llm: ['complete', 'embed', 'models'],
|
|
52
|
+
llm: ['complete', 'embed', 'models', 'classify', 'extract', 'summarize', 'draft'],
|
|
53
53
|
database: ['namespaces', 'create', 'delete-namespace', 'keys', 'get', 'set', 'del'],
|
|
54
54
|
crm: ['contacts', 'companies'],
|
|
55
55
|
url: ['shorten'],
|
package/dist/index.js
CHANGED
|
@@ -399,40 +399,40 @@ Usage: myapi <command> [subcommand] [args]
|
|
|
399
399
|
myapi --version
|
|
400
400
|
|
|
401
401
|
Commands:
|
|
402
|
+
audience Save filter snapshots as named audiences (people or companies)
|
|
402
403
|
auth Manage account · setup · whoami · link
|
|
403
|
-
status Single-screen view of account + resources in the default org
|
|
404
|
-
install-skills Install or update the MyAPI skills pack for AI agents
|
|
405
404
|
billing Check balance and manage billing
|
|
406
|
-
|
|
407
|
-
|
|
405
|
+
company Search the company database (filter by industry, size, country, ...)
|
|
406
|
+
container Run containers — services, workers, and scheduled jobs
|
|
407
|
+
crm Canonical store of engaged contacts + companies, with auto-ingest from webhooks
|
|
408
|
+
database KV store with namespaces + CAS — the substrate for stateful agent apps
|
|
409
|
+
doctor Org-wide consistency check — funnels, webhooks, domains, containers
|
|
408
410
|
domain Manage domain configurations
|
|
409
|
-
|
|
411
|
+
email Manage mailboxes, send/read email, templates, and campaigns
|
|
410
412
|
fn Create and deploy functions on the edge runtime
|
|
411
|
-
|
|
413
|
+
funnel Manage websites (publish pages, custom domains, funnels)
|
|
412
414
|
git Hosted git repositories — repos, commits, branches, history
|
|
415
|
+
image Generate AI images and manage them in storage
|
|
416
|
+
install-skills Install or update the MyAPI skills pack for AI agents
|
|
417
|
+
llm Run LLM completions and embeddings (chat + embed, with usage/cost)
|
|
418
|
+
org Manage organizations (tip: myapi org create "name" --yes to auto-set as default)
|
|
419
|
+
payments Take payments with Stripe Checkout (connect, charge, refund)
|
|
420
|
+
people Search the contact database (filter by industry, seniority, country, ...)
|
|
421
|
+
pixel Read pixel analytics: visits, events, identity resolution
|
|
413
422
|
queue Durable job queue — enqueue work, retried against an HTTP consumer
|
|
423
|
+
status Single-screen view of account + resources in the default org
|
|
424
|
+
storage Upload, ingest, list, and serve assets from edge storage
|
|
414
425
|
task Agent-task queue — file, claim, and resolve units of work
|
|
415
|
-
|
|
416
|
-
|
|
426
|
+
update Update CLI and skills to the latest version
|
|
427
|
+
url Shorten URLs to compact myurlto.com links
|
|
417
428
|
webhook Manage inbound webhook endpoints and inspect deliveries
|
|
418
|
-
email Manage mailboxes, send/read email, templates, and campaigns
|
|
419
429
|
workflow Run actions (send email, post to Slack) when a webhook fires
|
|
420
|
-
image Generate AI images and manage them in storage
|
|
421
|
-
storage Upload, ingest, list, and serve assets from edge storage
|
|
422
|
-
pixel Read pixel analytics: visits, events, identity resolution
|
|
423
|
-
people Search the contact database (filter by industry, seniority, country, ...)
|
|
424
|
-
company Search the company database (filter by industry, size, country, ...)
|
|
425
|
-
audience Save filter snapshots as named audiences (people or companies)
|
|
426
|
-
llm Run LLM completions and embeddings (chat + embed, with usage/cost)
|
|
427
|
-
database KV store with namespaces + CAS — the substrate for stateful agent apps
|
|
428
|
-
crm Canonical store of engaged contacts + companies, with auto-ingest from webhooks
|
|
429
|
-
url Shorten URLs to compact myurlto.com links
|
|
430
430
|
|
|
431
431
|
Aliases:
|
|
432
|
-
|
|
432
|
+
config → myapi auth config
|
|
433
433
|
keys → myapi auth api-keys
|
|
434
434
|
setup → myapi auth setup
|
|
435
|
-
|
|
435
|
+
whoami → myapi auth whoami
|
|
436
436
|
|
|
437
437
|
Run "myapi <command> --help" for subcommand help.
|
|
438
438
|
|