apiblaze 0.21.44 → 0.21.45
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/index.js +25 -125
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__
|
|
|
34
34
|
var version;
|
|
35
35
|
var init_package = __esm({
|
|
36
36
|
"package.json"() {
|
|
37
|
-
version = "0.21.
|
|
37
|
+
version = "0.21.45";
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
|
|
@@ -8923,7 +8923,7 @@ async function claimJourney(work, checks, env) {
|
|
|
8923
8923
|
if (res.claimed) {
|
|
8924
8924
|
try {
|
|
8925
8925
|
await admin({ method: "DELETE", path: `/teams/${encodeURIComponent(teamId)}`, summary: "delete the promoted demo team" });
|
|
8926
|
-
for (let i = 0; i <
|
|
8926
|
+
for (let i = 0; i < 180 && !gone; i++) {
|
|
8927
8927
|
try {
|
|
8928
8928
|
await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}` });
|
|
8929
8929
|
} catch (e) {
|
|
@@ -8931,7 +8931,7 @@ async function claimJourney(work, checks, env) {
|
|
|
8931
8931
|
}
|
|
8932
8932
|
if (!gone) await new Promise((r) => setTimeout(r, 1e3));
|
|
8933
8933
|
}
|
|
8934
|
-
detail = gone ? "cascade done" : "still present after
|
|
8934
|
+
detail = gone ? "cascade done" : "still present after 180 s";
|
|
8935
8935
|
} catch (e) {
|
|
8936
8936
|
detail = `delete \u2192 ${fail7(e).status} ${fail7(e).error}`;
|
|
8937
8937
|
}
|
|
@@ -9108,13 +9108,26 @@ async function defaultName(openapi, teamId) {
|
|
|
9108
9108
|
}
|
|
9109
9109
|
return randomProxyName();
|
|
9110
9110
|
}
|
|
9111
|
+
function saltedName(name) {
|
|
9112
|
+
return `${name}${Math.random().toString(36).slice(2, 6)}`;
|
|
9113
|
+
}
|
|
9111
9114
|
async function chooseName(opts, openapi, teamId, interactive2) {
|
|
9112
9115
|
if (opts.name !== void 0) {
|
|
9113
9116
|
const name = normalizeName2(opts.name);
|
|
9114
9117
|
if (name.length < 3) throw new Error(`${cap(B.thingShort)} name must be at least 3 characters (letters and digits only).`);
|
|
9115
9118
|
const check = await checkProxyName(name, teamId).catch(() => null);
|
|
9116
9119
|
if (check && (!check.canUseProjectName || !check.canUseApiVersion)) {
|
|
9117
|
-
|
|
9120
|
+
if (opts.auto || !interactive2) {
|
|
9121
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
9122
|
+
const candidate = saltedName(name);
|
|
9123
|
+
const again = await checkProxyName(candidate, teamId).catch(() => null);
|
|
9124
|
+
if (!again || again.canUseProjectName && again.canUseApiVersion) {
|
|
9125
|
+
console.log(import_chalk20.default.dim(` "${name}" is taken (names are global) \u2014 using "${candidate}" instead.`));
|
|
9126
|
+
return candidate;
|
|
9127
|
+
}
|
|
9128
|
+
}
|
|
9129
|
+
}
|
|
9130
|
+
throw new Error(`${cap(B.thingShort)} name "${name}" is not available${check.message ? ` \u2014 ${check.message}` : ""}. Omit --name and a free one is chosen for you.`);
|
|
9118
9131
|
}
|
|
9119
9132
|
return name;
|
|
9120
9133
|
}
|
|
@@ -9972,7 +9985,6 @@ their own app).
|
|
|
9972
9985
|
}
|
|
9973
9986
|
function shipMarkdown() {
|
|
9974
9987
|
const legacy = legacySkillMarkdown();
|
|
9975
|
-
const at = legacy.indexOf("## Adopt");
|
|
9976
9988
|
const backendRules = `## What their backend does \u2014 and nothing else
|
|
9977
9989
|
|
|
9978
9990
|
The gateway does sign-in, keys, tenancy and single-record ownership. The backend does only:
|
|
@@ -9993,7 +10005,7 @@ No code yet? Write the smallest plain backend that does exactly that, plus its \
|
|
|
9993
10005
|
return withLatest(`# Put ${B.product} in front of their app
|
|
9994
10006
|
|
|
9995
10007
|
${backendRules}${blockedMarkdown()}
|
|
9996
|
-
${
|
|
10008
|
+
${legacy}`);
|
|
9997
10009
|
}
|
|
9998
10010
|
function skillMarkdown() {
|
|
9999
10011
|
return `${heroMarkdown()}
|
|
@@ -10003,121 +10015,7 @@ function legacySkillMarkdown() {
|
|
|
10003
10015
|
const P = B.product;
|
|
10004
10016
|
const cli = B.cli;
|
|
10005
10017
|
const LIB = "apiblaze";
|
|
10006
|
-
|
|
10007
|
-
const doors = BRAND === "mcpblaze" ? `${P} ships your code to agents (an MCP server); ${other} ships the same code to users (API keys + sign-in).` : `${P} ships your code to users (API keys + sign-in); ${other} ships the same code to agents (an MCP server).`;
|
|
10008
|
-
return `---
|
|
10009
|
-
name: ${SKILL_NAME}
|
|
10010
|
-
description: Use when the user asks what ${P} (or ${cli}) does or can do for them; says yes to a demo or asks to see / try / demo it; wants to ship, share, expose or make an API public; says "serve my backend behind a proxy and MCP"; wants auth, API keys, sign-in, multi-tenant registration, users and groups, rate limits (throttling), quotas or spend caps; wants agents to use the API through an MCP; or says users can reach each other's records or admin functions. Puts the ${P} gateway in front of the API instead of hand-rolling auth and tenancy in the backend.
|
|
10011
|
-
---
|
|
10012
|
-
|
|
10013
|
-
# Ship safely with ${P}
|
|
10014
|
-
|
|
10015
|
-
One gateway, two doors. ${doors}
|
|
10016
|
-
Same rules, one command. Nothing about auth, tenancy or ownership goes into the backend's
|
|
10017
|
-
business logic: the gateway sits in front of the code and enforces it.
|
|
10018
|
-
|
|
10019
|
-
## Which mode \u2014 decide this first
|
|
10020
|
-
|
|
10021
|
-
- They ask what ${P} does or can do \u2192 **Explain**. Glance at the workspace first (see
|
|
10022
|
-
Explain), answer, then offer either the demo or their own app. Run no \`npx ${cli}\`
|
|
10023
|
-
command \u2014 the whole answer is in this file.
|
|
10024
|
-
- They say yes to the demo, or ask to see / try / demo it \u2192 **Demo app**. It lives in its own
|
|
10025
|
-
folder and never touches their code.
|
|
10026
|
-
- They say yes to "can I read the code" \u2192 **Adopt**: scan their app, ask only what the scan
|
|
10027
|
-
could not find, then ship it through the Ship steps.
|
|
10028
|
-
- They have a backend, just wrote one, or ask you to BUILD one and then ship it \u2192 **Ship**.
|
|
10029
|
-
No code yet? First write the smallest plain backend that serves the resources they named,
|
|
10030
|
-
plus its \`openapi.yaml\`, start it on their port, then continue at Ship \xA71. Do NOT build
|
|
10031
|
-
the Demo app for this. The gateway does auth, tenancy and item ownership; the backend does
|
|
10032
|
-
only these three things:
|
|
10033
|
-
- stamp each new row's \`tenant\` with the \`x-abz-tenant-id\` header and its \`owner\` with
|
|
10034
|
-
the \`x-abz-user-id\` header;
|
|
10035
|
-
- filter every collection GET to rows whose \`tenant\` AND \`owner\` match those headers (the
|
|
10036
|
-
gateway's rules lock single records, never lists \u2014 a list route has no one record to
|
|
10037
|
-
check). Both, always: a user id is not unique across tenants ("ana" at one customer and
|
|
10038
|
-
"ana" at another are two people);
|
|
10039
|
-
- answer 401 to any call whose \`x-target-api-key\` is not \`process.env.TARGET_SERVER_SECRET\`
|
|
10040
|
-
(generate it with \`openssl rand -hex 24\`, put it in \`.env.local\`, start the backend with
|
|
10041
|
-
it; Ship \xA73b makes the proxy send it). If they say "use
|
|
10042
|
-
${cli} on this" or "ship it with ${cli}" and point at a backend, skip any pitch and run it.
|
|
10043
|
-
|
|
10044
|
-
Tie-breaks: a question ("what can it do", "what is it") is always **Explain**, whatever else the
|
|
10045
|
-
message names. Mentioning \`npx ${cli} skills\` is NOT a request to run anything \u2014 it is the
|
|
10046
|
-
command that installed this file. If a message fits two modes, the earliest bullet wins.
|
|
10047
|
-
|
|
10048
|
-
## Explain
|
|
10049
|
-
|
|
10050
|
-
Say this, in these words or close to them, then stop:
|
|
10051
|
-
|
|
10052
|
-
"${P} puts a gateway in front of your backend in one command: a proxy (every request passes
|
|
10053
|
-
through it first) and an MCP server (the same API, callable by AI agents like Claude). No auth
|
|
10054
|
-
or ownership logic goes into your code \u2014 your backend only has to trust one header. Each of
|
|
10055
|
-
your customers \u2014 a tenant \u2014 gets their own API key, created from a widget you drop into your
|
|
10056
|
-
site. People and agents sign in with hosted GitHub, or your own OAuth app. Each user can only
|
|
10057
|
-
touch the records they created; those rules come from your OpenAPI spec (the file listing your
|
|
10058
|
-
endpoints), and if you have none I draft one from your routes and show it to you first. One
|
|
10059
|
-
more command sets a rate limit, a daily quota and a spend cap. In dev it tunnels to localhost;
|
|
10060
|
-
in prod it points at your deploy."
|
|
10061
|
-
|
|
10062
|
-
Before answering, look at the workspace \u2014 file names and \`package.json\` only, no reading of
|
|
10063
|
-
code yet. It "has an app" if ANY of these is present: an \`openapi.*\` / \`swagger.*\` file; a
|
|
10064
|
-
server framework (express, fastify, hono, koa, nest, fastapi, flask, django, rails, gin, chi,
|
|
10065
|
-
net/http); or a frontend (next, react, vue, svelte, remix, nuxt) whose \`.env*\` file NAMES an
|
|
10066
|
-
API base-URL variable (\`API_URL\`, \`NEXT_PUBLIC_API_URL\`, \`VITE_API_URL\`, or similar) \u2014
|
|
10067
|
-
\`grep -l\` the env files, open nothing else. Then end with exactly one of these two lines:
|
|
10068
|
-
|
|
10069
|
-
- No app \u2192 **"Would you like me to spin up a demo app?"**
|
|
10070
|
-
- An app \u2192 **"I see you have an app already coded. Can I read the code to see how to ready it
|
|
10071
|
-
for human and agent traffic with ${P}?"**
|
|
10072
|
-
|
|
10073
|
-
## Demo app \u2014 the same reservation system, before and after
|
|
10074
|
-
|
|
10075
|
-
One Next.js app on port 3000. Two routes, same UI, same data: \`/before\` is how most apps
|
|
10076
|
-
ship; \`/\` is the same thing behind ${P}. Narrate each step in one line and show every
|
|
10077
|
-
\`npx ${cli}\` command \u2014 the commands are the product. Everything goes in \`${cli}-demo/\`.
|
|
10078
|
-
|
|
10079
|
-
1. **Scaffold.** \`npx create-next-app@latest ${cli}-demo\` (TypeScript, App Router). Then
|
|
10080
|
-
\`npm i hono ${LIB}\`.
|
|
10081
|
-
2. **The backend.** Hono mounted at \`app/api/[[...route]]/route.ts\` (\`import { handle } from
|
|
10082
|
-
'hono/vercel'\`). Three resources \u2014 restaurants, tables, reservations \u2014 in an in-memory Map,
|
|
10083
|
-
every row stamped with \`owner\` = the \`x-abz-user-id\` header (${P} sets it on every call it
|
|
10084
|
-
forwards). Every route requires \`X-Api-Secret: demo-secret\` OR an \`x-abz-user-id\` header \u2014
|
|
10085
|
-
the first is how /before gets in, the second is how the gateway gets in. Write
|
|
10086
|
-
\`openapi.yaml\` at the repo root: every path and method, and the \`id\` each POST returns \u2014
|
|
10087
|
-
the ownership rules are read from it.
|
|
10088
|
-
3. **/before.** \`app/before/page.tsx\`: list and create reservations through one server route,
|
|
10089
|
-
\`app/before/api/route.ts\`, which holds \`demo-secret\` and calls \`/api/...\` directly. Put the
|
|
10090
|
-
caption on the page: "Before \u2014 one shared secret, anyone holding it can do anything."
|
|
10091
|
-
4. **Put ${P} in front.** In the BACKGROUND, and leave it running \u2014 it is the tunnel:
|
|
10092
|
-
\`npx ${cli} dev --port 3000 --openapi ./openapi.yaml --name demo --auto\`
|
|
10093
|
-
It prints the API URL, the MCP URL, the tenant, and the bootstrap key ONCE. Write them to
|
|
10094
|
-
\`.env.local\` as \`APIBLAZE_URL\`, \`APIBLAZE_SERVER_KEY\`, \`APIBLAZE_TENANT\`. \`--auto\` locks
|
|
10095
|
-
all three resources to their creator \u2014 say so.
|
|
10096
|
-
5. **Users and groups.** Turn them on and make the group the demo is about:
|
|
10097
|
-
\`npx ${cli} iam demo on\`
|
|
10098
|
-
\`npx ${cli} group create reservationists --tenant <tenant>\`
|
|
10099
|
-
\`npx ${cli} group add-user <the demo user> reservationists --tenant <tenant>\`
|
|
10100
|
-
\`npx ${cli} admins add <the user's email> --tenant <tenant>\` (so the widget has an admin).
|
|
10101
|
-
Optional, billed per turn: \`npx ${cli} rule demo "only members of reservationists can create
|
|
10102
|
-
or change reservations"\`. If the user is logged out or their wallet has nothing available
|
|
10103
|
-
(\`npx ${cli} billing balance\`), keep the free creator-lock rules and say so \u2014 do not pretend
|
|
10104
|
-
the group rule applied.
|
|
10105
|
-
6. **/.** \`app/page.tsx\`: the same UI, plus a **user picker** (ana \xB7 ben \xB7 admin \u2014 a select, no
|
|
10106
|
-
real login; the demo is about authorization, not building sign-in). Its server route calls
|
|
10107
|
-
\`process.env.APIBLAZE_URL\` with \`X-API-Key: APIBLAZE_SERVER_KEY\` and \`X-End-User-Id: <the
|
|
10108
|
-
picked user>\`. Add the three widgets from \`${LIB}/react\`, each with its server route:
|
|
10109
|
-
- \`<ApiKeyWidget />\` + \`app/api/apiblaze/keys/route.ts\` (\`createApiblazeKeys\` from \`${LIB}/server\`)
|
|
10110
|
-
- \`<UsersGroupsWidget />\` + \`app/api/apiblaze/groups/route.ts\` (\`createApiblazeGroups\` \u2014
|
|
10111
|
-
the keys route with one word changed)
|
|
10112
|
-
- \`<ChatWidget />\` + \`app/api/apiblaze/chat/route.ts\` (\`createApiblazeChat\`)
|
|
10113
|
-
Exact snippets for the keys and chat pair: \`npx ${cli} integration demo --stack nextjs\`.
|
|
10114
|
-
Caption: "After \u2014 a key per tenant, every call names its user, and ${P} decides."
|
|
10115
|
-
7. **Prove it, in the browser.** \`npm run dev\`, open \`http://localhost:3000/before\` then
|
|
10116
|
-
\`http://localhost:3000/\`. On \`/\`: create a reservation as ana, switch to ben, try to cancel
|
|
10117
|
-
it \u2014 show the 403 in the UI. Open the groups widget: reservationists is there. Ask the chat
|
|
10118
|
-
widget to list reservations. Then say what each \`npx ${cli}\` command did, in order.
|
|
10119
|
-
|
|
10120
|
-
## Adopt \u2014 reading their code first
|
|
10018
|
+
return `## Adopt \u2014 reading their code first
|
|
10121
10019
|
|
|
10122
10020
|
They said yes. Scan properly now (routes, env files, auth, network calls), classify, then ask
|
|
10123
10021
|
ONLY for what the scan could not find \u2014 one short list of questions, not an interview.
|
|
@@ -10178,12 +10076,14 @@ ONLY for what the scan could not find \u2014 one short list of questions, not an
|
|
|
10178
10076
|
the report. Only ask "where do your users sign in today?" when they mention an existing
|
|
10179
10077
|
login. Their own OAuth app \u2192 \`--apikey --oauth '{"provider":"auth0","clientId":"\u2026","clientSecret":"\u2026"}'\`
|
|
10180
10078
|
(both doors: keys for tenants, their login for people and agents). Shapes below.
|
|
10181
|
-
3. **Local code** \u2192 \`npx ${cli} dev --port <port> --openapi ./openapi.yaml --
|
|
10079
|
+
3. **Local code** \u2192 \`npx ${cli} dev --port <port> --openapi ./openapi.yaml --auto <door flags>\`,
|
|
10182
10080
|
left running \u2014 it is the tunnel. Start it as its own detached process, never \`cmd &\` inside
|
|
10183
10081
|
a shell call that ends: \`nohup npx ${cli} dev \u2026 > ${cli}-dev.log 2>&1 < /dev/null &\`, then
|
|
10184
|
-
read the log.
|
|
10185
|
-
|
|
10186
|
-
|
|
10082
|
+
read the log. Leave \`--name\` out: names are global, so a free one is chosen and printed
|
|
10083
|
+
(\`--name\` only for a name the user asked for; a taken one is salted). If the tunnel stops,
|
|
10084
|
+
run the \`--project <name>\` line it printed: that reattaches and creates nothing. Never start
|
|
10085
|
+
a new name to get it back \u2014 that is a second proxy. **Deployed code** \u2192
|
|
10086
|
+
\`npx ${cli} create --target <url-or-spec> --auto <door flags>\`. Both print the
|
|
10187
10087
|
URL, the MCP URL (the "agents:" line) and what is locked. Run by you (no terminal), \`dev\`
|
|
10188
10088
|
saves the bootstrap key to \`.env.local\` as \`APIBLAZE_SERVER_KEY\` and never prints it;
|
|
10189
10089
|
\`create\` prints it ONCE \u2014 save it there yourself. Either way say only "saved your key to
|