apiblaze 0.21.15 → 0.21.16
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 +271 -30
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1347,12 +1347,12 @@ var init_tenant_pick = __esm({
|
|
|
1347
1347
|
});
|
|
1348
1348
|
|
|
1349
1349
|
// src/index.ts
|
|
1350
|
-
var
|
|
1350
|
+
var import_fs6 = __toESM(require("fs"));
|
|
1351
1351
|
var import_commander = require("commander");
|
|
1352
1352
|
var import_chalk67 = __toESM(require("chalk"));
|
|
1353
1353
|
|
|
1354
1354
|
// package.json
|
|
1355
|
-
var version = "0.21.
|
|
1355
|
+
var version = "0.21.16";
|
|
1356
1356
|
|
|
1357
1357
|
// src/index.ts
|
|
1358
1358
|
init_types();
|
|
@@ -7919,6 +7919,7 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
7919
7919
|
}
|
|
7920
7920
|
|
|
7921
7921
|
// src/commands/mcp.ts
|
|
7922
|
+
var import_fs4 = __toESM(require("fs"));
|
|
7922
7923
|
var import_chalk33 = __toESM(require("chalk"));
|
|
7923
7924
|
init_auth();
|
|
7924
7925
|
init_api();
|
|
@@ -7960,6 +7961,234 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
7960
7961
|
]
|
|
7961
7962
|
});
|
|
7962
7963
|
}
|
|
7964
|
+
var PROMPT_NAME_RE = /^[a-z0-9_-]{1,64}$/;
|
|
7965
|
+
function derivedBehavior(method) {
|
|
7966
|
+
const m = (method ?? "").toUpperCase();
|
|
7967
|
+
const destructive = m === "DELETE";
|
|
7968
|
+
return {
|
|
7969
|
+
readOnly: m === "GET" || m === "HEAD" || m === "OPTIONS",
|
|
7970
|
+
destructive,
|
|
7971
|
+
idempotent: m === "PUT" || m === "DELETE",
|
|
7972
|
+
confirm: destructive,
|
|
7973
|
+
alwaysAsync: false
|
|
7974
|
+
};
|
|
7975
|
+
}
|
|
7976
|
+
function effectiveBehavior(method, o) {
|
|
7977
|
+
const d = derivedBehavior(method);
|
|
7978
|
+
const readOnly = o?.readOnly ?? d.readOnly;
|
|
7979
|
+
const destructive = readOnly ? false : o?.destructive ?? d.destructive;
|
|
7980
|
+
return {
|
|
7981
|
+
readOnly,
|
|
7982
|
+
destructive,
|
|
7983
|
+
idempotent: o?.idempotent ?? d.idempotent,
|
|
7984
|
+
confirm: o?.confirm ?? destructive,
|
|
7985
|
+
alwaysAsync: o?.alwaysAsync ?? false
|
|
7986
|
+
};
|
|
7987
|
+
}
|
|
7988
|
+
function uiResourceUri(projectId, toolName) {
|
|
7989
|
+
return `ui://${projectId}/${toolName}`;
|
|
7990
|
+
}
|
|
7991
|
+
function normalizePrompt(name, raw) {
|
|
7992
|
+
if (!PROMPT_NAME_RE.test(name)) throw new Error(`Prompt name "${name}" must match ${PROMPT_NAME_RE} (lowercase letters, digits, _ and -).`);
|
|
7993
|
+
const p = raw ?? {};
|
|
7994
|
+
if (typeof p !== "object" || Array.isArray(p)) throw new Error("The prompt file must hold one JSON object.");
|
|
7995
|
+
let messages = p.messages;
|
|
7996
|
+
if (!Array.isArray(messages) && typeof p.text === "string") messages = [{ role: "user", text: p.text }];
|
|
7997
|
+
if (!Array.isArray(messages) || messages.length === 0) throw new Error('The prompt needs "messages": [{ "role": "user", "text": "\u2026" }] (or a "text" shorthand).');
|
|
7998
|
+
for (const m of messages) {
|
|
7999
|
+
if (!m || m.role !== "user" && m.role !== "assistant" || typeof m.text !== "string" || !m.text) {
|
|
8000
|
+
throw new Error('Each message needs role "user" | "assistant" and a non-empty "text".');
|
|
8001
|
+
}
|
|
8002
|
+
}
|
|
8003
|
+
if (p.arguments !== void 0 && (!Array.isArray(p.arguments) || p.arguments.some((a) => !a || typeof a.name !== "string"))) {
|
|
8004
|
+
throw new Error('"arguments" must be [{ "name": "\u2026", "description"?: "\u2026", "required"?: true }].');
|
|
8005
|
+
}
|
|
8006
|
+
return {
|
|
8007
|
+
name,
|
|
8008
|
+
...typeof p.title === "string" ? { title: p.title } : {},
|
|
8009
|
+
...typeof p.description === "string" ? { description: p.description } : {},
|
|
8010
|
+
...Array.isArray(p.arguments) ? { arguments: p.arguments } : {},
|
|
8011
|
+
messages
|
|
8012
|
+
};
|
|
8013
|
+
}
|
|
8014
|
+
function withPrompt(cat, prompt) {
|
|
8015
|
+
const prompts = [...cat.prompts ?? []];
|
|
8016
|
+
const i = prompts.findIndex((p) => p.name === prompt.name);
|
|
8017
|
+
if (i >= 0) prompts[i] = prompt;
|
|
8018
|
+
else prompts.push(prompt);
|
|
8019
|
+
return { ...cat, prompts };
|
|
8020
|
+
}
|
|
8021
|
+
function withoutPrompt(cat, name) {
|
|
8022
|
+
const prompts = cat.prompts ?? [];
|
|
8023
|
+
if (!prompts.some((p) => p.name === name)) throw new Error(`No prompt named "${name}". See \`${B.cli} mcp prompts list\`.`);
|
|
8024
|
+
return { ...cat, prompts: prompts.filter((p) => p.name !== name) };
|
|
8025
|
+
}
|
|
8026
|
+
function requireTool(cat, tool) {
|
|
8027
|
+
if (!cat.tools.some((t) => t.name === tool)) {
|
|
8028
|
+
const names = cat.tools.slice(0, 10).map((t) => t.name).join(", ");
|
|
8029
|
+
throw new Error(`No tool named "${tool}" in the published catalogue${names ? ` (tools: ${names}${cat.tools.length > 10 ? ", \u2026" : ""})` : ""}.`);
|
|
8030
|
+
}
|
|
8031
|
+
}
|
|
8032
|
+
function withOverride(cat, tool, flags) {
|
|
8033
|
+
requireTool(cat, tool);
|
|
8034
|
+
const patch = {};
|
|
8035
|
+
for (const k of ["readOnly", "destructive", "idempotent", "confirm", "alwaysAsync"]) {
|
|
8036
|
+
if (typeof flags[k] === "boolean") patch[k] = flags[k];
|
|
8037
|
+
}
|
|
8038
|
+
if (Object.keys(patch).length === 0) {
|
|
8039
|
+
throw new Error("Nothing to set. Pass --read-only, --destructive, --idempotent, --confirm, --always-async (or their --no-\u2026 forms).");
|
|
8040
|
+
}
|
|
8041
|
+
return { ...cat, overrides: { ...cat.overrides ?? {}, [tool]: { ...cat.overrides?.[tool] ?? {}, ...patch } } };
|
|
8042
|
+
}
|
|
8043
|
+
function withoutOverride(cat, tool) {
|
|
8044
|
+
requireTool(cat, tool);
|
|
8045
|
+
const overrides = { ...cat.overrides ?? {} };
|
|
8046
|
+
delete overrides[tool];
|
|
8047
|
+
return { ...cat, overrides };
|
|
8048
|
+
}
|
|
8049
|
+
function flagsLine(b) {
|
|
8050
|
+
const f = [];
|
|
8051
|
+
if (b.readOnly) f.push("read-only");
|
|
8052
|
+
if (b.idempotent) f.push("idempotent");
|
|
8053
|
+
if (b.destructive) f.push("destructive");
|
|
8054
|
+
if (b.confirm) f.push("asks to confirm");
|
|
8055
|
+
if (b.alwaysAsync) f.push("always async");
|
|
8056
|
+
return f.length ? f.join(", ") : "\u2014";
|
|
8057
|
+
}
|
|
8058
|
+
function renderOps(cat) {
|
|
8059
|
+
const rows = cat.tools.map((t) => {
|
|
8060
|
+
const method = t["x-route"]?.method?.toUpperCase() ?? "";
|
|
8061
|
+
const o = cat.overrides?.[t.name];
|
|
8062
|
+
return { name: t.name, route: `${method} ${t["x-route"]?.path ?? ""}`.trim(), eff: flagsLine(effectiveBehavior(method, o)), custom: !!o && Object.keys(o).length > 0 };
|
|
8063
|
+
});
|
|
8064
|
+
const w = Math.min(40, Math.max(4, ...rows.map((r) => r.name.length)));
|
|
8065
|
+
const rw = Math.min(40, Math.max(5, ...rows.map((r) => r.route.length)));
|
|
8066
|
+
return rows.map((r) => ` ${r.name.padEnd(w)} ${r.route.padEnd(rw)} ${r.eff}${r.custom ? import_chalk33.default.yellow(" (override)") : ""}`);
|
|
8067
|
+
}
|
|
8068
|
+
async function resolveMcpTarget(projectArg, opts) {
|
|
8069
|
+
const creds = loadCredentials();
|
|
8070
|
+
if (!creds) throw new Error("Not authenticated. Run `apiblaze login` first.");
|
|
8071
|
+
const projects = await getProjects(creds.teamId ?? "");
|
|
8072
|
+
const matches2 = projects.filter((p) => p.projectId === projectArg || p.projectName === projectArg);
|
|
8073
|
+
const wanted = opts.apiversion ?? opts.version;
|
|
8074
|
+
const match = (wanted ? matches2.find((p) => p.apiVersion === wanted) : matches2[0]) ?? null;
|
|
8075
|
+
if (!match) throw new Error(`Project "${projectArg}"${wanted ? ` version ${wanted}` : ""} not found in your active team. Run \`${B.cli} projects\`.`);
|
|
8076
|
+
return { projectId: match.projectId, apiVersion: wanted || match.apiVersion, environment: opts.environment || "prod" };
|
|
8077
|
+
}
|
|
8078
|
+
async function loadCatalogue(t) {
|
|
8079
|
+
const r = await agentCall(`/projects/${t.projectId}/${t.apiVersion}/mcp/spec?environment=${encodeURIComponent(t.environment)}`, "GET");
|
|
8080
|
+
if (r.status >= 400) throw new Error(`Could not read the MCP catalogue (${r.status}): ${r.data?.error ?? ""}`);
|
|
8081
|
+
const spec2 = r.data?.spec;
|
|
8082
|
+
if (!spec2 || !Array.isArray(spec2.tools)) {
|
|
8083
|
+
throw new Error(`No MCP catalogue published for ${t.projectId} ${t.apiVersion} (${t.environment}). Try --environment, or build one with \`${B.cli} agent mcp ${t.projectId}\`.`);
|
|
8084
|
+
}
|
|
8085
|
+
return spec2;
|
|
8086
|
+
}
|
|
8087
|
+
async function saveCatalogue(t, spec2) {
|
|
8088
|
+
const r = await agentCall(`/projects/${t.projectId}/${t.apiVersion}/mcp/spec`, "PUT", { environment: t.environment, spec: spec2 });
|
|
8089
|
+
if (r.status >= 400) {
|
|
8090
|
+
const d = r.data;
|
|
8091
|
+
throw new Error(`Save failed (${r.status}): ${d?.error ?? ""}${d?.details ? ` \u2014 ${typeof d.details === "string" ? d.details : JSON.stringify(d.details)}` : ""}`);
|
|
8092
|
+
}
|
|
8093
|
+
}
|
|
8094
|
+
async function runMcpPrompts(action2, project, name, opts) {
|
|
8095
|
+
const t = await resolveMcpTarget(project, opts);
|
|
8096
|
+
const cat = await loadCatalogue(t);
|
|
8097
|
+
const prompts = cat.prompts ?? [];
|
|
8098
|
+
if (action2 === "list") {
|
|
8099
|
+
if (opts.json) return void console.log(JSON.stringify({ environment: t.environment, prompts }, null, 2));
|
|
8100
|
+
if (!prompts.length) {
|
|
8101
|
+
console.log(import_chalk33.default.dim(`
|
|
8102
|
+
No prompts on ${t.projectId} (${t.environment}). Prompts are drafted by the MCP agent (\`${B.cli} agent mcp ${t.projectId}\`) or added with \`${B.cli} mcp prompts set\`.
|
|
8103
|
+
`));
|
|
8104
|
+
return;
|
|
8105
|
+
}
|
|
8106
|
+
console.log("");
|
|
8107
|
+
for (const p of prompts) {
|
|
8108
|
+
const args = (p.arguments ?? []).map((a) => a.required ? a.name : `${a.name}?`).join(", ");
|
|
8109
|
+
console.log(` ${import_chalk33.default.bold(p.name)}${args ? import_chalk33.default.dim(`(${args})`) : ""}${p.title || p.description ? ` ${p.title ?? p.description}` : ""}`);
|
|
8110
|
+
}
|
|
8111
|
+
console.log("");
|
|
8112
|
+
return;
|
|
8113
|
+
}
|
|
8114
|
+
if (!name) throw new Error(`Usage: ${B.cli} mcp prompts ${action2} <project> <name>`);
|
|
8115
|
+
if (action2 === "show") {
|
|
8116
|
+
const p = prompts.find((x) => x.name === name);
|
|
8117
|
+
if (!p) throw new Error(`No prompt named "${name}". See \`${B.cli} mcp prompts list ${project}\`.`);
|
|
8118
|
+
if (opts.json) return void console.log(JSON.stringify(p, null, 2));
|
|
8119
|
+
console.log(`
|
|
8120
|
+
${import_chalk33.default.bold(p.name)}${p.title ? ` \u2014 ${p.title}` : ""}`);
|
|
8121
|
+
if (p.description) console.log(` ${p.description}`);
|
|
8122
|
+
for (const a of p.arguments ?? []) console.log(import_chalk33.default.dim(` ${a.name}${a.required ? " (required)" : ""}${a.description ? `: ${a.description}` : ""}`));
|
|
8123
|
+
for (const m of p.messages) console.log(`
|
|
8124
|
+
[${m.role}] ${m.text}`);
|
|
8125
|
+
console.log("");
|
|
8126
|
+
return;
|
|
8127
|
+
}
|
|
8128
|
+
let next;
|
|
8129
|
+
if (action2 === "set") {
|
|
8130
|
+
if (!opts.file) throw new Error(`Usage: ${B.cli} mcp prompts set <project> <name> --file prompt.json`);
|
|
8131
|
+
let raw;
|
|
8132
|
+
try {
|
|
8133
|
+
raw = JSON.parse(import_fs4.default.readFileSync(opts.file, "utf8"));
|
|
8134
|
+
} catch (e) {
|
|
8135
|
+
throw new Error(`Could not read ${opts.file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
8136
|
+
}
|
|
8137
|
+
next = withPrompt(cat, normalizePrompt(name, raw));
|
|
8138
|
+
} else {
|
|
8139
|
+
next = withoutPrompt(cat, name);
|
|
8140
|
+
}
|
|
8141
|
+
await saveCatalogue(t, next);
|
|
8142
|
+
if (opts.json) return void console.log(JSON.stringify({ saved: true, environment: t.environment, prompts: next.prompts }, null, 2));
|
|
8143
|
+
console.log(import_chalk33.default.green(` \u2713 ${action2 === "set" ? "Saved" : "Removed"} prompt "${name}" on ${t.projectId} (${t.environment}).`));
|
|
8144
|
+
}
|
|
8145
|
+
async function runMcpOps(action2, project, tool, opts) {
|
|
8146
|
+
const t = await resolveMcpTarget(project, opts);
|
|
8147
|
+
const cat = await loadCatalogue(t);
|
|
8148
|
+
if (action2 === "list") {
|
|
8149
|
+
if (opts.json) {
|
|
8150
|
+
const tools = cat.tools.map((x) => {
|
|
8151
|
+
const method2 = x["x-route"]?.method?.toUpperCase();
|
|
8152
|
+
return {
|
|
8153
|
+
name: x.name,
|
|
8154
|
+
method: method2,
|
|
8155
|
+
path: x["x-route"]?.path,
|
|
8156
|
+
default: derivedBehavior(method2),
|
|
8157
|
+
override: cat.overrides?.[x.name] ?? null,
|
|
8158
|
+
effective: effectiveBehavior(method2, cat.overrides?.[x.name])
|
|
8159
|
+
};
|
|
8160
|
+
});
|
|
8161
|
+
return void console.log(JSON.stringify({ environment: t.environment, tools }, null, 2));
|
|
8162
|
+
}
|
|
8163
|
+
console.log(import_chalk33.default.dim(`
|
|
8164
|
+
${cat.tools.length} tool(s) on ${t.projectId} (${t.environment}). Defaults come from the HTTP method: GET/HEAD/OPTIONS read-only, PUT/DELETE idempotent, DELETE destructive + asks to confirm.
|
|
8165
|
+
`));
|
|
8166
|
+
for (const l of renderOps(cat)) console.log(l);
|
|
8167
|
+
console.log(import_chalk33.default.dim(`
|
|
8168
|
+
Override: ${B.cli} mcp ops set ${project} <tool> --read-only | --destructive | --confirm | --no-confirm | --always-async \xB7 Reset: ${B.cli} mcp ops reset ${project} <tool>
|
|
8169
|
+
`));
|
|
8170
|
+
return;
|
|
8171
|
+
}
|
|
8172
|
+
if (!tool) throw new Error(`Usage: ${B.cli} mcp ops ${action2} <project> <tool>`);
|
|
8173
|
+
const next = action2 === "set" ? withOverride(cat, tool, opts) : withoutOverride(cat, tool);
|
|
8174
|
+
await saveCatalogue(t, next);
|
|
8175
|
+
const method = cat.tools.find((x) => x.name === tool)?.["x-route"]?.method;
|
|
8176
|
+
const eff = effectiveBehavior(method, next.overrides?.[tool]);
|
|
8177
|
+
if (opts.json) return void console.log(JSON.stringify({ saved: true, tool, override: next.overrides?.[tool] ?? null, effective: eff }, null, 2));
|
|
8178
|
+
console.log(import_chalk33.default.green(` \u2713 ${tool}: ${flagsLine(eff)}${action2 === "reset" ? " (automatic default)" : ""}`));
|
|
8179
|
+
}
|
|
8180
|
+
async function runMcpUi(project, tool, opts) {
|
|
8181
|
+
const t = await resolveMcpTarget(project, opts);
|
|
8182
|
+
const cat = await loadCatalogue(t);
|
|
8183
|
+
requireTool(cat, tool);
|
|
8184
|
+
const uri = uiResourceUri(t.projectId, tool);
|
|
8185
|
+
if (opts.json) return void console.log(JSON.stringify({ tool, uri, kind: "automatic" }, null, 2));
|
|
8186
|
+
console.log(`
|
|
8187
|
+
${import_chalk33.default.bold(uri)}
|
|
8188
|
+
`);
|
|
8189
|
+
console.log(import_chalk33.default.dim(" This is the automatic viewer: MCP Apps clients render the tool's result with a generic UI built"));
|
|
8190
|
+
console.log(import_chalk33.default.dim(" from its response schema \u2014 nothing to configure. Uploading your own HTML for it comes later.\n"));
|
|
8191
|
+
}
|
|
7963
8192
|
|
|
7964
8193
|
// src/commands/delete.ts
|
|
7965
8194
|
var import_chalk35 = __toESM(require("chalk"));
|
|
@@ -8157,7 +8386,7 @@ async function runJobStatus(jobId, opts) {
|
|
|
8157
8386
|
|
|
8158
8387
|
// src/commands/export.ts
|
|
8159
8388
|
var import_chalk37 = __toESM(require("chalk"));
|
|
8160
|
-
var
|
|
8389
|
+
var import_fs5 = require("fs");
|
|
8161
8390
|
init_admin();
|
|
8162
8391
|
init_auth();
|
|
8163
8392
|
init_resolve();
|
|
@@ -8206,7 +8435,7 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
8206
8435
|
});
|
|
8207
8436
|
if (!res.ok) throw new Error(`Download failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
8208
8437
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
8209
|
-
(0,
|
|
8438
|
+
(0, import_fs5.writeFileSync)(outFile, buf);
|
|
8210
8439
|
console.log(import_chalk37.default.green(`\u2713 Saved ${outFile} (${(buf.length / 1024).toFixed(0)} KB)`));
|
|
8211
8440
|
if (job.decryption_key) console.log(import_chalk37.default.yellow(`Decryption key (shown once): ${job.decryption_key}`));
|
|
8212
8441
|
console.log(import_chalk37.default.dim("Read MIGRATION-REPORT.json inside the zip for the full fidelity ledger."));
|
|
@@ -11913,7 +12142,7 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
11913
12142
|
}
|
|
11914
12143
|
|
|
11915
12144
|
// src/commands/spec.ts
|
|
11916
|
-
var
|
|
12145
|
+
var fs13 = __toESM(require("fs"));
|
|
11917
12146
|
var import_chalk53 = __toESM(require("chalk"));
|
|
11918
12147
|
var import_ora24 = __toESM(require("ora"));
|
|
11919
12148
|
init_admin();
|
|
@@ -11936,7 +12165,7 @@ async function runSpecSet(project, opts) {
|
|
|
11936
12165
|
}
|
|
11937
12166
|
let specContent;
|
|
11938
12167
|
try {
|
|
11939
|
-
specContent =
|
|
12168
|
+
specContent = fs13.readFileSync(opts.file, "utf-8");
|
|
11940
12169
|
} catch {
|
|
11941
12170
|
console.error(import_chalk53.default.red(`Cannot read file: ${opts.file}`));
|
|
11942
12171
|
process.exit(1);
|
|
@@ -13787,28 +14016,28 @@ async function runConsumerApikeys(opts) {
|
|
|
13787
14016
|
// src/commands/sidecar.ts
|
|
13788
14017
|
var import_chalk60 = __toESM(require("chalk"));
|
|
13789
14018
|
var import_ora30 = __toESM(require("ora"));
|
|
13790
|
-
var
|
|
14019
|
+
var fs14 = __toESM(require("fs"));
|
|
13791
14020
|
var path10 = __toESM(require("path"));
|
|
13792
14021
|
init_admin();
|
|
13793
14022
|
init_resolve();
|
|
13794
14023
|
init_auth();
|
|
13795
14024
|
function detectNextProject(root) {
|
|
13796
|
-
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) =>
|
|
14025
|
+
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs14.existsSync(path10.join(root, f)));
|
|
13797
14026
|
let hasDep = false;
|
|
13798
14027
|
try {
|
|
13799
|
-
const pkg = JSON.parse(
|
|
14028
|
+
const pkg = JSON.parse(fs14.readFileSync(path10.join(root, "package.json"), "utf8"));
|
|
13800
14029
|
hasDep = !!(pkg.dependencies?.next || pkg.devDependencies?.next);
|
|
13801
14030
|
} catch {
|
|
13802
14031
|
}
|
|
13803
|
-
const appDir =
|
|
13804
|
-
const pagesDir =
|
|
14032
|
+
const appDir = fs14.existsSync(path10.join(root, "app")) || fs14.existsSync(path10.join(root, "src", "app"));
|
|
14033
|
+
const pagesDir = fs14.existsSync(path10.join(root, "pages")) || fs14.existsSync(path10.join(root, "src", "pages"));
|
|
13805
14034
|
return { found: hasConfig || hasDep || appDir || pagesDir, router: appDir ? "app" : pagesDir ? "pages" : null };
|
|
13806
14035
|
}
|
|
13807
14036
|
function upsertEnvLocal(root, token) {
|
|
13808
14037
|
const p = path10.join(root, ".env.local");
|
|
13809
14038
|
let existing = "";
|
|
13810
14039
|
try {
|
|
13811
|
-
existing =
|
|
14040
|
+
existing = fs14.readFileSync(p, "utf8");
|
|
13812
14041
|
} catch {
|
|
13813
14042
|
}
|
|
13814
14043
|
const had = /^APIBLAZE_API_KEY=/m.test(existing) || /^APIBLAZE_TOKEN=/m.test(existing);
|
|
@@ -13823,15 +14052,15 @@ function upsertEnvLocal(root, token) {
|
|
|
13823
14052
|
next = (next.endsWith("\n") ? next : next + "\n") + `APIBLAZE_SIDECAR_VERBOSE=true
|
|
13824
14053
|
`;
|
|
13825
14054
|
}
|
|
13826
|
-
|
|
14055
|
+
fs14.writeFileSync(p, next);
|
|
13827
14056
|
return had ? "rotated" : "created";
|
|
13828
14057
|
}
|
|
13829
14058
|
function installSidecarPackage(root) {
|
|
13830
|
-
if (
|
|
14059
|
+
if (fs14.existsSync(path10.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
13831
14060
|
console.log(` ${import_chalk60.default.green("\u2713")} apiblaze package already installed`);
|
|
13832
14061
|
return;
|
|
13833
14062
|
}
|
|
13834
|
-
const has = (f) =>
|
|
14063
|
+
const has = (f) => fs14.existsSync(path10.join(root, f));
|
|
13835
14064
|
const pm = has("bun.lockb") || has("bun.lock") ? { cmd: "bun", add: "add" } : has("pnpm-lock.yaml") ? { cmd: "pnpm", add: "add" } : has("yarn.lock") ? { cmd: "yarn", add: "add" } : { cmd: "npm", add: "install" };
|
|
13836
14065
|
const spinner = (0, import_ora30.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
13837
14066
|
try {
|
|
@@ -13844,7 +14073,7 @@ function installSidecarPackage(root) {
|
|
|
13844
14073
|
}
|
|
13845
14074
|
function readEnvKey(root) {
|
|
13846
14075
|
try {
|
|
13847
|
-
const s =
|
|
14076
|
+
const s = fs14.readFileSync(path10.join(root, ".env.local"), "utf8");
|
|
13848
14077
|
const m = s.match(/^APIBLAZE_API_KEY=(.+)$/m) ?? s.match(/^APIBLAZE_TOKEN=(.+)$/m);
|
|
13849
14078
|
return m ? m[1].trim() : null;
|
|
13850
14079
|
} catch {
|
|
@@ -13855,13 +14084,13 @@ function ensureGitignored(root) {
|
|
|
13855
14084
|
const p = path10.join(root, ".gitignore");
|
|
13856
14085
|
let c = "";
|
|
13857
14086
|
try {
|
|
13858
|
-
c =
|
|
14087
|
+
c = fs14.readFileSync(p, "utf8");
|
|
13859
14088
|
} catch {
|
|
13860
14089
|
}
|
|
13861
|
-
if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c))
|
|
14090
|
+
if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c)) fs14.writeFileSync(p, (c && !c.endsWith("\n") ? c + "\n" : c) + ".env.local\n");
|
|
13862
14091
|
}
|
|
13863
14092
|
function wireInstrumentation(root) {
|
|
13864
|
-
const existing = ["instrumentation.ts", "instrumentation.js", path10.join("src", "instrumentation.ts")].map((c) => path10.join(root, c)).find((f) =>
|
|
14093
|
+
const existing = ["instrumentation.ts", "instrumentation.js", path10.join("src", "instrumentation.ts")].map((c) => path10.join(root, c)).find((f) => fs14.existsSync(f));
|
|
13865
14094
|
const body = `import { register as apiblaze } from "apiblaze/sidecar";
|
|
13866
14095
|
|
|
13867
14096
|
export function register() {
|
|
@@ -13869,18 +14098,18 @@ export function register() {
|
|
|
13869
14098
|
}
|
|
13870
14099
|
`;
|
|
13871
14100
|
if (!existing) {
|
|
13872
|
-
|
|
14101
|
+
fs14.writeFileSync(path10.join(root, "instrumentation.ts"), body);
|
|
13873
14102
|
return "created";
|
|
13874
14103
|
}
|
|
13875
|
-
const cur =
|
|
14104
|
+
const cur = fs14.readFileSync(existing, "utf8");
|
|
13876
14105
|
if (cur.includes("apiblaze/sidecar")) return "present";
|
|
13877
14106
|
if (/export\s+function\s+register\s*\(/.test(cur)) {
|
|
13878
|
-
|
|
14107
|
+
fs14.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
|
|
13879
14108
|
` + cur.replace(/export\s+function\s+register\s*\(\s*\)\s*\{/, (m) => `${m}
|
|
13880
14109
|
apiblaze();`));
|
|
13881
14110
|
return "patched";
|
|
13882
14111
|
}
|
|
13883
|
-
|
|
14112
|
+
fs14.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
|
|
13884
14113
|
${cur}
|
|
13885
14114
|
// call apiblaze() inside your register() export.
|
|
13886
14115
|
`);
|
|
@@ -13963,16 +14192,16 @@ export default async function Page() {
|
|
|
13963
14192
|
function generateInspector(root, router) {
|
|
13964
14193
|
try {
|
|
13965
14194
|
if (router === "pages") {
|
|
13966
|
-
const dir2 =
|
|
14195
|
+
const dir2 = fs14.existsSync(path10.join(root, "src", "pages")) ? path10.join(root, "src", "pages") : path10.join(root, "pages");
|
|
13967
14196
|
const f2 = path10.join(dir2, "abz-inspector.tsx");
|
|
13968
|
-
|
|
14197
|
+
fs14.writeFileSync(f2, INSPECTOR_PAGE);
|
|
13969
14198
|
return path10.relative(root, f2);
|
|
13970
14199
|
}
|
|
13971
|
-
const base2 =
|
|
14200
|
+
const base2 = fs14.existsSync(path10.join(root, "src", "app")) ? path10.join(root, "src", "app") : path10.join(root, "app");
|
|
13972
14201
|
const dir = path10.join(base2, "abz-inspector");
|
|
13973
|
-
|
|
14202
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
13974
14203
|
const f = path10.join(dir, "page.tsx");
|
|
13975
|
-
|
|
14204
|
+
fs14.writeFileSync(f, INSPECTOR_PAGE);
|
|
13976
14205
|
return path10.relative(root, f);
|
|
13977
14206
|
} catch {
|
|
13978
14207
|
return null;
|
|
@@ -15415,8 +15644,20 @@ agent.command("authz").description("Chat to design and turn on access rules for
|
|
|
15415
15644
|
program.command("rule").description("Lock resources to the person who created them \u2014 or describe any access rule in plain English (that one is billed per turn)").argument("<project>", "Project name or id").argument("[sentence]", 'Optional rule in plain English, e.g. "users see only their own rows". Omit to pick from what the spec can protect.').option("--enforce", "Turn enforcement on right after saving (default: save in shadow mode, then ask)").option("--apiversion <version>", "API version (defaults to the project's)").action(action((project, sentence, opts) => runRule(project, sentence, opts)));
|
|
15416
15645
|
agent.command("openapi").description("Chat to build your API spec from real traffic").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").action(action((project, apiVersion) => runOpenapi(project, apiVersion)));
|
|
15417
15646
|
agent.command("mcp").description("Chat to build an MCP server for an API").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").option("--environment <env>", "Environment to publish (default: prod)").action(action((project, apiVersion, opts) => runMcp(project, apiVersion, opts)));
|
|
15647
|
+
var mcpCmd = program.command("mcp").description("Optional tweaks to a proxy's MCP server: prompts, per-tool behavior (read-only / destructive / confirm / always-async), UI resource");
|
|
15648
|
+
var mcpCommon = (c) => c.option("--apiversion <version>", "API version (defaults to the project's)").option("--environment <env>", "Catalogue environment (default: prod)").option("--json", "Output machine-readable JSON");
|
|
15649
|
+
var mcpPrompts = mcpCmd.command("prompts").description("Prompts the MCP server offers (drafted by the MCP agent; editable here)");
|
|
15650
|
+
mcpCommon(mcpPrompts.command("list").description("List the prompts").argument("<project>", "Project name or id")).action(action((project, opts) => runMcpPrompts("list", project, void 0, opts)));
|
|
15651
|
+
mcpCommon(mcpPrompts.command("show").description("Show one prompt").argument("<project>", "Project name or id").argument("<name>", "Prompt name")).action(action((project, name, opts) => runMcpPrompts("show", project, name, opts)));
|
|
15652
|
+
mcpCommon(mcpPrompts.command("set").description("Add or replace a prompt from a JSON file ({ title?, description?, arguments?, messages: [{ role, text }] }, {{arg}} placeholders)").argument("<project>", "Project name or id").argument("<name>", "Prompt name (a-z 0-9 _ -)").requiredOption("--file <path>", "prompt.json")).action(action((project, name, opts) => runMcpPrompts("set", project, name, opts)));
|
|
15653
|
+
mcpCommon(mcpPrompts.command("rm").description("Remove a prompt").argument("<project>", "Project name or id").argument("<name>", "Prompt name")).action(action((project, name, opts) => runMcpPrompts("rm", project, name, opts)));
|
|
15654
|
+
var mcpOps = mcpCmd.command("ops").description("Per-tool behavior. Default from the HTTP method: GET/HEAD/OPTIONS read-only, PUT/DELETE idempotent, DELETE destructive + asks to confirm");
|
|
15655
|
+
mcpCommon(mcpOps.command("list").description("Every tool with its effective behavior (overrides marked)").argument("<project>", "Project name or id")).action(action((project, opts) => runMcpOps("list", project, void 0, opts)));
|
|
15656
|
+
mcpCommon(mcpOps.command("set").description("Override one tool").argument("<project>", "Project name or id").argument("<tool>", "Tool name").option("--read-only", "Safe to call, changes nothing (e.g. a POST search)").option("--no-read-only", "Not read-only").option("--destructive", "Changes or deletes data (e.g. a GET with side effects)").option("--no-destructive", "Not destructive").option("--idempotent", "Repeating it has no extra effect").option("--no-idempotent", "Not idempotent").option("--confirm", "Ask the user to confirm before running").option("--no-confirm", "Never ask to confirm").option("--always-async", "Return a task handle immediately (long jobs)").option("--no-always-async", "Wait for the result (default)")).action(action((project, tool, opts) => runMcpOps("set", project, tool, opts)));
|
|
15657
|
+
mcpCommon(mcpOps.command("reset").description("Back to the automatic default for one tool").argument("<project>", "Project name or id").argument("<tool>", "Tool name")).action(action((project, tool, opts) => runMcpOps("reset", project, tool, opts)));
|
|
15658
|
+
mcpCommon(mcpCmd.command("ui").description("Print a tool's MCP Apps UI resource (ui://{project}/{tool}) \u2014 the automatic viewer").argument("<project>", "Project name or id").argument("<tool>", "Tool name")).action(action((project, tool, opts) => runMcpUi(project, tool, opts)));
|
|
15418
15659
|
program.command("apichat [project]").description("Turn any API into a chat: point at an OpenAPI spec \u2014 or chat an EXISTING proxy by name (no login needed)").option("--target <url|file>", "What to chat with \u2014 pass ANY of: a target server base URL (spec auto-discovered at /openapi.json etc.), a local OpenAPI file (./openapi.yaml), or a remote OpenAPI URL (https://acme.com/openapi.yaml)").addOption(new import_commander.Option("--openapi <file|url>", "Deprecated alias \u2014 --target now detects spec files/URLs itself").hideHelp()).addOption(new import_commander.Option("--openapispec <file|url>", "Deprecated alias for --openapi").hideHelp()).option("--name <name>", "Proxy name (defaults to the target host)").option("--apiversion <version>", "API version to create (e.g. 1.0.0)").option("--environment <env>", "Environment to chat against (default: prod anonymous / dev logged-in)").option("--access <mode>", 'Who can call this API once connected (e.g. via Claude): "open" = anyone who signs in, "invite" = only you + emails you pre-approve. Default: invite when logged in, open when anonymous.').option("--target-auth-env <ENV_VAR>", "Read the upstream credential from this env var (CI-safe; required when there is no TTY and the API needs auth)").option("--force", "Proceed even if the API uses oauth2/openIdConnect target auth (you configure target auth yourself later)").option("-y, --yes", "Skip confirmation prompts").option("--tenant <slug>", "Tenant (consumer namespace: portal, login, users) for the new proxy; omit to be asked").option("--apikey <key>", "Use this API key for the proxy's door (api_key proxies). Without it, apichat detects the door and asks \u2014 or runs the consumer login for OAuth doors.").option("--xenduserid <id>", "Assert this end-user id (X-End-User-Id) \u2014 required by proxies with identified/pre-approved enforcement; you are asked for one when the proxy demands it.").option("--verbose", "Show the per-turn proxy curl trace (hidden by default)").option("-p, --prompt <question>", "One-shot question: answered through the external agent CLI after an MCP install, or by apichat itself (exits after answering when there is no TTY)").option("--install-mcp <cli>", "Install this proxy's MCP into an external agent CLI without asking: claude | codex. Also re-offers after an earlier decline.").option("--remove-mcp <cli>", "Disconnect this proxy from an external agent CLI (claude | codex): removes the MCP server and the standing instruction").action(action((project, opts) => {
|
|
15419
|
-
const looksLikeSource = !!project && (/^https?:\/\//i.test(project) || /\.(ya?ml|json)$/i.test(project) ||
|
|
15660
|
+
const looksLikeSource = !!project && (/^https?:\/\//i.test(project) || /\.(ya?ml|json)$/i.test(project) || import_fs6.default.existsSync(project));
|
|
15420
15661
|
const target2 = opts.target ?? (looksLikeSource ? project : void 0);
|
|
15421
15662
|
return runApichat({ ...opts, target: target2, project: looksLikeSource ? void 0 : project, openapispec: opts.openapispec ?? opts.openapi });
|
|
15422
15663
|
}));
|
|
@@ -15580,7 +15821,7 @@ var HELP_GROUPS = [
|
|
|
15580
15821
|
{ title: "Chat", commands: ["docs", "apichat", "agent", "llm"] },
|
|
15581
15822
|
{ title: "Setup", commands: ["dev", "create", "integration", "skill", "login", "init", "sidecar", "claim", "team", "whoami", "logout", "flush"] },
|
|
15582
15823
|
{ title: "Recipes \u2014 install a working setup, or publish yours", commands: ["search", "show", "install", "publish", "withdraw"] },
|
|
15583
|
-
{ title: "Control plane commands", commands: ["config", "projects", "logs", "tenant", "group", "admins", "apikeys", "iam", "identified", "preapprove", "allowoauthregistration", "rule", "domain", "delete", "job", "target", "limits", "billing", "transform", "rename", "spec", "export"] },
|
|
15824
|
+
{ title: "Control plane commands", commands: ["config", "projects", "logs", "tenant", "group", "admins", "apikeys", "iam", "identified", "preapprove", "allowoauthregistration", "rule", "mcp", "domain", "delete", "job", "target", "limits", "billing", "transform", "rename", "spec", "export"] },
|
|
15584
15825
|
{ title: "Data plane commands", commands: [
|
|
15585
15826
|
{ parent: "consumer", sub: "login" },
|
|
15586
15827
|
{ parent: "consumer", sub: "apikeys" },
|