@myapihq/cli 2.1.0 → 2.3.1
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.
|
@@ -3,5 +3,5 @@ import { type Flags } from '../helpers.js';
|
|
|
3
3
|
import type { Exposes } from '../exposes.js';
|
|
4
4
|
export declare const SCHEMA: FlagSchema;
|
|
5
5
|
export declare const EXPOSES: Exposes;
|
|
6
|
-
export declare const HELP = "Usage: myapi auth <subcommand>\n\nAuthentication for your app's end users \u2014 a managed OIDC identity provider\n(\u00E0 la Kinde/Auth0). One auth tenant per org; register OIDC clients (apps)\nagainst it; sign users in via the hosted login page or the JS SDK; verify\ntokens locally against the tenant JWKS.\n\nEnd users sign in with managed Google, email/password, or magic links \u2014 pick\nwhich via `tenant create --connections`. (Operator/account commands moved to\n`myapi account`.)\n\nSubcommands:\n client Register and
|
|
6
|
+
export declare const HELP = "Usage: myapi auth <subcommand>\n\nAuthentication for your app's end users \u2014 a managed OIDC identity provider\n(\u00E0 la Kinde/Auth0). One auth tenant per org; register OIDC clients (apps)\nagainst it; sign users in via the hosted login page or the JS SDK; verify\ntokens locally against the tenant JWKS.\n\nEnd users sign in with managed Google, email/password, or magic links \u2014 pick\nwhich via `tenant create --connections`. (Operator/account commands moved to\n`myapi account`.)\n\nSubcommands:\n client Register, list, delete, and rotate OIDC clients (your apps)\n domain Serve auth on your own domain (auth.acme.com)\n tenant Show or create your org's OIDC auth tenant (+ sign-in methods)\n usage Monthly active users (MAU) for the current period";
|
|
7
7
|
export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { auth as sdkAuth } from '@myapihq/sdk';
|
|
2
2
|
import { requireConfig } from '../config.js';
|
|
3
|
+
import { confirm, isNonInteractive } from '../prompt.js';
|
|
3
4
|
import { success, error, info, printTable, printJson } from '../output.js';
|
|
4
5
|
import { requireOrg } from '../helpers.js';
|
|
5
6
|
// `myapi auth` — the END-USER auth product (my-auth-api): a managed OIDC IdP
|
|
@@ -21,8 +22,11 @@ export const EXPOSES = [
|
|
|
21
22
|
'POST /auth/orgs/{org_id}/clients',
|
|
22
23
|
'GET /auth/orgs/{org_id}/clients',
|
|
23
24
|
'GET /auth/orgs/{org_id}/usage',
|
|
25
|
+
'DELETE /auth/orgs/{org_id}/clients/{client_id}',
|
|
26
|
+
'POST /auth/orgs/{org_id}/clients/{client_id}/rotate',
|
|
24
27
|
'POST /auth/orgs/{org_id}/domain',
|
|
25
28
|
'GET /auth/orgs/{org_id}/domain',
|
|
29
|
+
'POST /auth/orgs/{org_id}/domain/verify',
|
|
26
30
|
'DELETE /auth/orgs/{org_id}/domain',
|
|
27
31
|
];
|
|
28
32
|
const SUBCOMMAND_USAGE = {
|
|
@@ -40,27 +44,35 @@ in against. One per org.
|
|
|
40
44
|
'usage': `myapi auth usage [--org <id>] [--json]
|
|
41
45
|
|
|
42
46
|
Monthly active users (MAU) for the current period — auth is billed per MAU.`,
|
|
43
|
-
'domain': `myapi auth domain [show|set|delete] [--domain <host>] [--org <id>] [--json]
|
|
47
|
+
'domain': `myapi auth domain [show|set|verify|delete] [--domain <host>] [--org <id>] [--json]
|
|
44
48
|
|
|
45
49
|
Serve auth on your own domain (e.g. auth.acme.com) instead of the default issuer.
|
|
50
|
+
Three steps: set → publish the ownership TXT → verify → publish the A record.
|
|
46
51
|
|
|
47
52
|
myapi auth domain Show the current custom domain + status
|
|
48
|
-
myapi auth domain set --domain auth.acme.com Register it (prints
|
|
53
|
+
myapi auth domain set --domain auth.acme.com Register it (prints a TXT challenge to publish)
|
|
54
|
+
myapi auth domain verify Check the TXT; on success prints the A record
|
|
49
55
|
myapi auth domain delete Remove the custom domain
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
Flow: 'set' returns a TXT record to prove ownership; create it, then 'verify'.
|
|
58
|
+
Once verified, create the printed A record; TLS provisions automatically (~30 min)
|
|
59
|
+
and the domain becomes your issuer when active.`,
|
|
60
|
+
'client': `myapi auth client <list|create|delete|rotate> [--org <id>] [--json]
|
|
54
61
|
|
|
55
62
|
OIDC clients are the apps that authenticate against your tenant.
|
|
56
63
|
|
|
57
64
|
myapi auth client list
|
|
58
65
|
myapi auth client create --name "My App" --type spa --redirect https://app.example.com/callback
|
|
66
|
+
myapi auth client delete <client_id> [--yes]
|
|
67
|
+
myapi auth client rotate <client_id>
|
|
59
68
|
|
|
60
|
-
--name <n> Human label for the app (required)
|
|
69
|
+
--name <n> Human label for the app (required for create)
|
|
61
70
|
--type spa|web spa = public (no secret); web = confidential (secret once)
|
|
62
|
-
--redirect <urls> Allowed redirect URIs, comma-separated (required).
|
|
63
|
-
Absolute https (or http://localhost for dev)
|
|
71
|
+
--redirect <urls> Allowed redirect URIs, comma-separated (required for create).
|
|
72
|
+
Absolute https (or http://localhost for dev).
|
|
73
|
+
|
|
74
|
+
delete Revoke a client (irreversible; stops authenticating immediately).
|
|
75
|
+
rotate Re-issue a 'web' client's secret (shown once; old secret stops working).`,
|
|
64
76
|
};
|
|
65
77
|
export const HELP = `Usage: myapi auth <subcommand>
|
|
66
78
|
|
|
@@ -74,7 +86,7 @@ which via \`tenant create --connections\`. (Operator/account commands moved to
|
|
|
74
86
|
\`myapi account\`.)
|
|
75
87
|
|
|
76
88
|
Subcommands:
|
|
77
|
-
client Register and
|
|
89
|
+
client Register, list, delete, and rotate OIDC clients (your apps)
|
|
78
90
|
domain Serve auth on your own domain (auth.acme.com)
|
|
79
91
|
tenant Show or create your org's OIDC auth tenant (+ sign-in methods)
|
|
80
92
|
usage Monthly active users (MAU) for the current period`;
|
|
@@ -165,25 +177,34 @@ async function usage(flags) {
|
|
|
165
177
|
info(`Period: ${u.period}`);
|
|
166
178
|
info(`Active users: ${u.active_users} MAU`);
|
|
167
179
|
info(`Price: $${(u.price_cents_each / 100).toFixed(2)} per MAU`);
|
|
180
|
+
if (u.as_of)
|
|
181
|
+
info(`As of: ${u.as_of} (MAU is aggregated, not real-time)`);
|
|
168
182
|
}
|
|
169
183
|
async function domain(args, flags) {
|
|
170
184
|
const action = args[0] || 'show';
|
|
171
185
|
const config = requireConfig();
|
|
172
|
-
const orgId = requireOrg(flags, config, 'myapi auth domain [show|set|delete] [--org <id>]');
|
|
186
|
+
const orgId = requireOrg(flags, config, 'myapi auth domain [show|set|verify|delete] [--org <id>]');
|
|
173
187
|
const showDomain = (d) => {
|
|
174
188
|
info(`Domain: ${d.domain}`);
|
|
175
189
|
info(`Status: ${d.status}`);
|
|
176
|
-
if (d.status
|
|
190
|
+
if (d.status === 'awaiting_verification' && d.verification) {
|
|
191
|
+
info('');
|
|
192
|
+
info('Prove ownership — create this DNS record:');
|
|
193
|
+
info(` ${d.verification.type} ${d.verification.name} → ${d.verification.value}`);
|
|
194
|
+
info('\nThen run: myapi auth domain verify');
|
|
195
|
+
}
|
|
196
|
+
else if (d.dns) {
|
|
177
197
|
info('');
|
|
178
|
-
info(
|
|
198
|
+
info('DNS — create this record:');
|
|
179
199
|
info(` ${d.dns.type} ${d.dns.name} → ${d.dns.value}`);
|
|
180
|
-
if (d.next)
|
|
181
|
-
info(`\n${d.next}`);
|
|
182
200
|
}
|
|
183
|
-
|
|
201
|
+
if (d.status === 'active') {
|
|
184
202
|
info(`Issuer: ${d.issuer}`);
|
|
185
203
|
info(`Login URL: ${d.login_url}`);
|
|
186
204
|
}
|
|
205
|
+
else if (d.next) {
|
|
206
|
+
info(`\n${d.next}`);
|
|
207
|
+
}
|
|
187
208
|
};
|
|
188
209
|
if (action === 'show') {
|
|
189
210
|
try {
|
|
@@ -215,6 +236,35 @@ async function domain(args, flags) {
|
|
|
215
236
|
showDomain(d);
|
|
216
237
|
return;
|
|
217
238
|
}
|
|
239
|
+
if (action === 'verify') {
|
|
240
|
+
try {
|
|
241
|
+
const d = await sdkAuth.verifyDomain(config.api_key, orgId);
|
|
242
|
+
if (flags.json) {
|
|
243
|
+
printJson(d);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (d.status === 'awaiting_verification') {
|
|
247
|
+
// Shouldn't normally happen (backend returns 422 when unverified), but
|
|
248
|
+
// be defensive.
|
|
249
|
+
info('Still awaiting verification — the TXT record was not found.');
|
|
250
|
+
showDomain(d);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
success(`Ownership verified — domain is now "${d.status}".`);
|
|
254
|
+
showDomain(d);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
if (e?.code === 'DOMAIN_NOT_VERIFIED') {
|
|
259
|
+
const exp = e?.body?.expected;
|
|
260
|
+
error(`Ownership TXT record not found yet (DNS can take a few minutes). Create it and retry:\n ${exp ? `${exp.type} ${exp.name} → ${exp.value}` : 'see: myapi auth domain show'}`);
|
|
261
|
+
}
|
|
262
|
+
if (e?.code === 'NO_DOMAIN' || e?.status === 404)
|
|
263
|
+
error('No custom auth domain set. Set one first: myapi auth domain set --domain auth.example.com');
|
|
264
|
+
throw e;
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
218
268
|
if (action === 'delete' || action === 'remove') {
|
|
219
269
|
const host = flags.domain || args[1];
|
|
220
270
|
let target = host;
|
|
@@ -231,12 +281,12 @@ async function domain(args, flags) {
|
|
|
231
281
|
success(`Custom auth domain removed: ${target}`);
|
|
232
282
|
return;
|
|
233
283
|
}
|
|
234
|
-
error(`Unknown action "${action}". Use: myapi auth domain [show|set|delete]`);
|
|
284
|
+
error(`Unknown action "${action}". Use: myapi auth domain [show|set|verify|delete]`);
|
|
235
285
|
}
|
|
236
286
|
async function client(args, flags) {
|
|
237
287
|
const action = args[0] || 'list';
|
|
238
288
|
const config = requireConfig();
|
|
239
|
-
const orgId = requireOrg(flags, config, 'myapi auth client <list|create> [--org <id>]');
|
|
289
|
+
const orgId = requireOrg(flags, config, 'myapi auth client <list|create|delete|rotate> [--org <id>]');
|
|
240
290
|
if (action === 'list') {
|
|
241
291
|
const res = await sdkAuth.listClients(config.api_key, orgId);
|
|
242
292
|
if (flags.json) {
|
|
@@ -282,5 +332,48 @@ async function client(args, flags) {
|
|
|
282
332
|
}
|
|
283
333
|
return;
|
|
284
334
|
}
|
|
285
|
-
|
|
335
|
+
if (action === 'delete') {
|
|
336
|
+
const clientId = args[1];
|
|
337
|
+
if (!clientId)
|
|
338
|
+
error('Usage: myapi auth client delete <client_id> [--yes]');
|
|
339
|
+
// Destructive + irreversible — echo the exact client_id and confirm (the
|
|
340
|
+
// client stops authenticating immediately). --yes bypasses; refuse to hang
|
|
341
|
+
// in non-interactive contexts without it.
|
|
342
|
+
if (!flags.yes && !flags.y) {
|
|
343
|
+
if (isNonInteractive()) {
|
|
344
|
+
error(`Deleting client ${clientId} is irreversible. Re-run with --yes:\n myapi auth client delete ${clientId} --yes`);
|
|
345
|
+
}
|
|
346
|
+
const ok = await confirm(`› Delete OIDC client ${clientId}? It stops authenticating immediately. (y/N) `, false);
|
|
347
|
+
if (!ok) {
|
|
348
|
+
info('Cancelled.');
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const res = await sdkAuth.deleteClient(config.api_key, orgId, clientId);
|
|
353
|
+
if (flags.json) {
|
|
354
|
+
printJson(res);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
success(`Client deleted: ${clientId}`);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (action === 'rotate') {
|
|
361
|
+
const clientId = args[1];
|
|
362
|
+
if (!clientId)
|
|
363
|
+
error('Usage: myapi auth client rotate <client_id>');
|
|
364
|
+
const c = await sdkAuth.rotateClient(config.api_key, orgId, clientId);
|
|
365
|
+
if (flags.json) {
|
|
366
|
+
printJson(c);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
success(`Secret rotated for client: ${c.client_id || clientId}`);
|
|
370
|
+
info('The previous secret stops working immediately.');
|
|
371
|
+
if (c.client_secret) {
|
|
372
|
+
info('');
|
|
373
|
+
info('New client secret (shown ONCE — store it now, it cannot be retrieved again):');
|
|
374
|
+
info(` ${c.client_secret}`);
|
|
375
|
+
}
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
error(`Unknown action "${action}". Use: myapi auth client <list|create|delete|rotate>`);
|
|
286
379
|
}
|
package/dist/commands/llm.js
CHANGED
|
@@ -47,6 +47,25 @@ async function defaultChatModel(apiKey, orgId) {
|
|
|
47
47
|
}
|
|
48
48
|
return chat.id;
|
|
49
49
|
}
|
|
50
|
+
// Resolve the embed model from --model, else the live catalog. Unlike chat
|
|
51
|
+
// we don't silently pick a default when several are served — embedding
|
|
52
|
+
// vectors aren't cross-model compatible, so an ambiguous pick is a footgun.
|
|
53
|
+
// One served embed model → use it; none → catalog-truthful error (not a
|
|
54
|
+
// hardcoded "not available today"); many → make the caller choose.
|
|
55
|
+
async function resolveEmbedModel(apiKey, orgId, flags) {
|
|
56
|
+
if (typeof flags.model === 'string' && flags.model)
|
|
57
|
+
return flags.model;
|
|
58
|
+
const embeds = (await loadModels(apiKey, orgId)).filter(m => m.kind === 'embed');
|
|
59
|
+
if (embeds.length === 1)
|
|
60
|
+
return embeds[0].id;
|
|
61
|
+
if (embeds.length === 0) {
|
|
62
|
+
error('No embedding model is currently served — the catalog has none. Run "myapi llm models --kind embed" to check.');
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
error(`Multiple embedding models available — pass --model <id>. Options: ${embeds.map(m => m.id).join(', ')}`);
|
|
66
|
+
}
|
|
67
|
+
throw new Error('unreachable'); // satisfy the type checker
|
|
68
|
+
}
|
|
50
69
|
function readPromptArg(promptArg, flags) {
|
|
51
70
|
if (typeof flags.file === 'string' && flags.file) {
|
|
52
71
|
return fs.readFileSync(flags.file, 'utf-8');
|
|
@@ -140,10 +159,8 @@ async function complete(promptArg, flags) {
|
|
|
140
159
|
}
|
|
141
160
|
async function embed(inputArg, flags) {
|
|
142
161
|
const config = requireConfig();
|
|
143
|
-
const orgId = requireOrg(flags, config, 'myapi llm embed "<text>" --model <id> [--org <id>]');
|
|
144
|
-
const model =
|
|
145
|
-
if (!model)
|
|
146
|
-
error('Missing required flag: --model. Run "myapi llm models" to list embed models.');
|
|
162
|
+
const orgId = requireOrg(flags, config, 'myapi llm embed "<text>" [--model <id>] [--org <id>]');
|
|
163
|
+
const model = await resolveEmbedModel(config.api_key, orgId, flags);
|
|
147
164
|
const text = readPromptArg(inputArg, flags);
|
|
148
165
|
if (!text.trim())
|
|
149
166
|
error('Empty input. Pass <text> as an argument, "-" to read stdin, or --file <path>.');
|
|
@@ -283,8 +300,7 @@ const SUBCOMMAND_USAGE = {
|
|
|
283
300
|
[--stop <csv>] [--file <path>] [--org <id>] [--json]
|
|
284
301
|
|
|
285
302
|
Raw chat completion against a self-hosted catalog model. Without --model,
|
|
286
|
-
picks the first chat model from "myapi llm models"
|
|
287
|
-
Qwen/Qwen3-Coder-30B-A3B-Instruct).
|
|
303
|
+
picks the first chat model from "myapi llm models".
|
|
288
304
|
|
|
289
305
|
Pass "-" as the prompt to read from stdin. The reply goes to stdout; a
|
|
290
306
|
one-line usage footer (tokens + cost in cents) goes to stderr so it
|
|
@@ -293,10 +309,11 @@ const SUBCOMMAND_USAGE = {
|
|
|
293
309
|
Examples:
|
|
294
310
|
myapi llm complete "summarize: $(cat README.md)"
|
|
295
311
|
cat draft.md | myapi llm complete - --system "You are an editor" --max-tokens 200`,
|
|
296
|
-
embed: `myapi llm embed "<text>" --model <id> [--file <path>] [--org <id>] [--json]
|
|
312
|
+
embed: `myapi llm embed "<text>" [--model <id>] [--file <path>] [--org <id>] [--json]
|
|
297
313
|
|
|
298
|
-
|
|
299
|
-
|
|
314
|
+
Embed text into a dense vector. --model is optional when the catalog serves
|
|
315
|
+
exactly one embedding model; otherwise pass one from "myapi llm models
|
|
316
|
+
--kind embed". Returns EMBED_NOT_AVAILABLE if no embedding model is served.`,
|
|
300
317
|
models: `myapi llm models [--kind chat|embed] [--org <id>] [--json]
|
|
301
318
|
|
|
302
319
|
Lists the live model catalog with per-1M-token pricing in cents.
|
|
@@ -358,7 +375,7 @@ Subcommands:
|
|
|
358
375
|
classify Pick a label from a set
|
|
359
376
|
complete Raw chat completion against a catalog model
|
|
360
377
|
draft Write something (email | reply | message | …)
|
|
361
|
-
embed Raw embeddings
|
|
378
|
+
embed Raw embeddings against a catalog embed model
|
|
362
379
|
extract Pull structured data conforming to a JSON Schema
|
|
363
380
|
models List the live model catalog (id, kind, context, cents/1M)
|
|
364
381
|
summarize Summarize text (brief | exec | bullet)
|
|
@@ -13,7 +13,7 @@ The root service. It manages accounts, API keys, organizations, and billing. No
|
|
|
13
13
|
|
|
14
14
|
## Capabilities
|
|
15
15
|
<!-- llm:start -->
|
|
16
|
-
MyApiHQ is the platform's foundation. Every other service (domain, funnel,
|
|
16
|
+
MyApiHQ is the platform's foundation. Every other service (domain, funnel, auth, payments, fn, workflow, database, storage, email, webhook, crm, llm, image, pixel, url) requires both an `api_key` and (for org-scoped resources) an `org_id` minted here. Setup is one command — `myapi account setup` — which provisions an account, generates an api_key, creates a default org, and stores everything in `~/.myapi/config.json`. Subsequent commands pick up those defaults automatically.
|
|
17
17
|
|
|
18
18
|
```
|
|
19
19
|
myapihq ──► org_id + api_key
|
|
@@ -39,9 +39,10 @@ its clients.
|
|
|
39
39
|
returns a `client_secret` **once** — store it immediately. `--redirect` lists
|
|
40
40
|
allowed callback URIs (absolute https, or http://localhost for dev).
|
|
41
41
|
- **Usage** — `auth usage` shows monthly active users (auth is billed per MAU).
|
|
42
|
-
- **Custom domain** —
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
- **Custom domain** — serve auth on `auth.acme.com`. Three steps: `auth domain
|
|
43
|
+
set --domain auth.acme.com` prints a **TXT ownership challenge**; publish it,
|
|
44
|
+
then `auth domain verify`; that prints the **A record** to publish, TLS
|
|
45
|
+
provisions automatically, and the domain becomes your issuer once active.
|
|
45
46
|
|
|
46
47
|
**Sign-in methods** are chosen per tenant via
|
|
47
48
|
`auth tenant create --connections google,password,magic` (default `google`):
|
|
@@ -61,8 +62,10 @@ CLI is only the management surface.
|
|
|
61
62
|
| `myapi auth tenant create` | Create/enable the tenant (`--connections google,password,magic`; `--theme <json>`) |
|
|
62
63
|
| `myapi auth client list` | List the OIDC clients (apps) registered to your tenant |
|
|
63
64
|
| `myapi auth client create` | Register an OIDC client (`--name`, `--type spa\|web`, `--redirect`) |
|
|
64
|
-
| `myapi auth
|
|
65
|
-
| `myapi auth
|
|
65
|
+
| `myapi auth client delete <id>` | Revoke a client (irreversible); `--yes` to skip the confirm |
|
|
66
|
+
| `myapi auth client rotate <id>` | Re-issue a `web` client's secret (shown once) |
|
|
67
|
+
| `myapi auth usage` | Monthly active users (MAU) for the current period (`as_of` shows freshness) |
|
|
68
|
+
| `myapi auth domain` | Custom auth domain: `set` (TXT challenge) → `verify` → A record; also `show`/`delete` |
|
|
66
69
|
<!-- generated:end -->
|
|
67
70
|
|
|
68
71
|
## Examples
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: my-llm-api
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.2.0
|
|
4
4
|
description: >
|
|
5
5
|
Two-surface LLM primitive. Raw chat completion against self-hosted
|
|
6
6
|
open-source models (you pick the model), and objective verbs
|
|
@@ -14,7 +14,7 @@ checksum: sha256-pending
|
|
|
14
14
|
|
|
15
15
|
A two-surface LLM gateway:
|
|
16
16
|
|
|
17
|
-
- **Raw** — `complete` / `embed` / `models`. You pick a self-hosted catalog model (
|
|
17
|
+
- **Raw** — `complete` / `embed` / `models`. You pick a self-hosted catalog model (run `myapi llm models` for the live list). Proprietary models are not callable here.
|
|
18
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
19
|
|
|
20
20
|
Pricing is in cents per 1M tokens at the actual upstream rate; cost is debited from your MyAPI balance.
|
|
@@ -33,7 +33,7 @@ Use this for workflow tasks — summarize a doc, classify an inbound email, extr
|
|
|
33
33
|
### Raw `complete` request
|
|
34
34
|
```json
|
|
35
35
|
{
|
|
36
|
-
"model": "
|
|
36
|
+
"model": "<model-id>",
|
|
37
37
|
"messages": [
|
|
38
38
|
{ "role": "system", "content": "You are a terse editor." },
|
|
39
39
|
{ "role": "user", "content": "Tighten this paragraph: ..." }
|
|
@@ -49,7 +49,7 @@ Roles: `system | user | assistant`. Multiple system messages collapse to one ins
|
|
|
49
49
|
### Raw `complete` response
|
|
50
50
|
```json
|
|
51
51
|
{
|
|
52
|
-
"model": "
|
|
52
|
+
"model": "<model-id>",
|
|
53
53
|
"content": "...assistant reply...",
|
|
54
54
|
"finish_reason": "stop",
|
|
55
55
|
"usage": { "input_tokens": 42, "output_tokens": 87, "cost_cents": 0.029 }
|
|
@@ -62,7 +62,7 @@ If `model` isn't in the self-hosted catalog the server returns `MODEL_NOT_IN_RAW
|
|
|
62
62
|
|
|
63
63
|
### Raw `embed`
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
Embed text into a dense vector. `--model` is optional when the catalog serves exactly one embed model; else pass one from `myapi llm models --kind embed`. Returns `EMBED_NOT_AVAILABLE` when none is served.
|
|
66
66
|
|
|
67
67
|
### Model catalog
|
|
68
68
|
- Chat models: `id`, `kind: 'chat'`, `context_window`, `input_cost_per_1m_cents`, `output_cost_per_1m_cents`
|
|
@@ -99,11 +99,11 @@ client = OpenAI(
|
|
|
99
99
|
api_key="hq_live_…",
|
|
100
100
|
base_url="https://api.myapihq.com/llm/orgs/<org_id>/v1",
|
|
101
101
|
)
|
|
102
|
-
r = client.chat.completions.create(model="
|
|
102
|
+
r = client.chat.completions.create(model="<model-id>",
|
|
103
103
|
messages=[{"role":"user","content":"Hi"}])
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
Use raw `complete`
|
|
106
|
+
Use raw `complete` for first-party code; the compat path for existing OpenAI/LangChain tooling.
|
|
107
107
|
|
|
108
108
|
<!-- llm:end -->
|
|
109
109
|
|
|
@@ -113,7 +113,7 @@ Use raw `complete` (MyAPI shape) for first-party integrations; use the OpenAI-co
|
|
|
113
113
|
|---|---|
|
|
114
114
|
| `myapi llm models [--kind chat\|embed] [--json]` | List the live model catalog with pricing (cents/1M) |
|
|
115
115
|
| `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 |
|
|
116
|
-
| `myapi llm embed "<text>" --model <id> [--json]` | Embed a string
|
|
116
|
+
| `myapi llm embed "<text>" [--model <id>] [--json]` | Embed a string into a vector; `--model` optional when the catalog has one embed model |
|
|
117
117
|
| `myapi llm classify "<input>" --labels <csv> [--multi] [--tier <t>] [--json]` | Pick a label from a set |
|
|
118
118
|
| `myapi llm extract "<input>" --schema <path\|json> [--tier <t>] [--json]` | Pull structured data conforming to a JSON Schema |
|
|
119
119
|
| `myapi llm summarize "<input>" [--style brief\|exec\|bullet] [--tier <t>] [--json]` | Summarize text |
|
|
@@ -132,9 +132,9 @@ myapi llm models --kind chat --json | jq '.models[].id'
|
|
|
132
132
|
# Raw completion — picks the first chat model from the catalog
|
|
133
133
|
myapi llm complete "Summarize in 12 words: $(cat README.md)"
|
|
134
134
|
|
|
135
|
-
# Pin a specific model
|
|
135
|
+
# Pin a specific model (ids come from `myapi llm models`)
|
|
136
136
|
myapi llm complete "Refactor this function: ..." \
|
|
137
|
-
--model
|
|
137
|
+
--model <model-id> \
|
|
138
138
|
--system "You are a careful Go reviewer." \
|
|
139
139
|
--max-tokens 600
|
|
140
140
|
|
|
@@ -163,5 +163,5 @@ INTENT=$(printf '%s' "$BODY" | myapi llm classify - \
|
|
|
163
163
|
|
|
164
164
|
- **`draft` context safety.** `context` fields are quoted into the prompt verbatim; sensitive-named keys (`secret`, `api_key`, `password`, …) are NOT redacted. Two guards on top: (a) injection-defense strips `instructions`/`system`/`prompt`/`override` keys and surfaces them in `meta.warnings`; (b) output guardrail substring-scans fact values (length ≥ 4) in the response and lists matches in `meta.guardrails.facts_in_output` (signal, not redaction). Rule of thumb: never put credentials, PII, or internal metadata in `context` — pass identifiers, reference them indirectly.
|
|
165
165
|
- **Self-hosted raw, server-picked verbs.** Raw runs on MyAPI's TPU; verbs route wherever the server picks.
|
|
166
|
-
- **Cost + latency.** `usage.cost_cents` is authoritative — no markup.
|
|
166
|
+
- **Cost + latency.** `usage.cost_cents` is authoritative — no markup. Varies by tier: 200–600 ms to first token, 1–3 s end-to-end.
|
|
167
167
|
- **Live catalog, no streaming, no BYOK.** Don't hard-code ids — `models` is truth (CLI auto-picks if `--model` omitted). Full reply only.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myapihq/cli",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "2.1
|
|
4
|
+
"version": "2.3.1",
|
|
5
5
|
"description": "MyAPI command-line interface",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"lint:skills:strict": "node scripts/copy-skills.js && node scripts/lint-skills.js --strict"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@myapihq/sdk": "^2.1
|
|
34
|
+
"@myapihq/sdk": "^2.3.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.6.0",
|