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.
Files changed (2) hide show
  1. package/dist/index.js +271 -30
  2. 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 import_fs5 = __toESM(require("fs"));
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.15";
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 import_fs4 = require("fs");
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, import_fs4.writeFileSync)(outFile, buf);
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 fs12 = __toESM(require("fs"));
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 = fs12.readFileSync(opts.file, "utf-8");
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 fs13 = __toESM(require("fs"));
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) => fs13.existsSync(path10.join(root, 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(fs13.readFileSync(path10.join(root, "package.json"), "utf8"));
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 = fs13.existsSync(path10.join(root, "app")) || fs13.existsSync(path10.join(root, "src", "app"));
13804
- const pagesDir = fs13.existsSync(path10.join(root, "pages")) || fs13.existsSync(path10.join(root, "src", "pages"));
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 = fs13.readFileSync(p, "utf8");
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
- fs13.writeFileSync(p, next);
14055
+ fs14.writeFileSync(p, next);
13827
14056
  return had ? "rotated" : "created";
13828
14057
  }
13829
14058
  function installSidecarPackage(root) {
13830
- if (fs13.existsSync(path10.join(root, "node_modules", "apiblaze", "package.json"))) {
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) => fs13.existsSync(path10.join(root, 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 = fs13.readFileSync(path10.join(root, ".env.local"), "utf8");
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 = fs13.readFileSync(p, "utf8");
14087
+ c = fs14.readFileSync(p, "utf8");
13859
14088
  } catch {
13860
14089
  }
13861
- if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c)) fs13.writeFileSync(p, (c && !c.endsWith("\n") ? c + "\n" : c) + ".env.local\n");
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) => fs13.existsSync(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
- fs13.writeFileSync(path10.join(root, "instrumentation.ts"), body);
14101
+ fs14.writeFileSync(path10.join(root, "instrumentation.ts"), body);
13873
14102
  return "created";
13874
14103
  }
13875
- const cur = fs13.readFileSync(existing, "utf8");
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
- fs13.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
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
- fs13.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
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 = fs13.existsSync(path10.join(root, "src", "pages")) ? path10.join(root, "src", "pages") : path10.join(root, "pages");
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
- fs13.writeFileSync(f2, INSPECTOR_PAGE);
14197
+ fs14.writeFileSync(f2, INSPECTOR_PAGE);
13969
14198
  return path10.relative(root, f2);
13970
14199
  }
13971
- const base2 = fs13.existsSync(path10.join(root, "src", "app")) ? path10.join(root, "src", "app") : path10.join(root, "app");
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
- fs13.mkdirSync(dir, { recursive: true });
14202
+ fs14.mkdirSync(dir, { recursive: true });
13974
14203
  const f = path10.join(dir, "page.tsx");
13975
- fs13.writeFileSync(f, INSPECTOR_PAGE);
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) || import_fs5.default.existsSync(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" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.21.15",
3
+ "version": "0.21.16",
4
4
  "description": "APIblaze CLI — Chat with your APIs, Manage your API keys, users and groups with the APIblaze serverless proxy",
5
5
  "keywords": [
6
6
  "apiblaze",