apiblaze 0.20.31 → 0.20.34
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 +100 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1024,7 +1024,7 @@ var import_commander = require("commander");
|
|
|
1024
1024
|
var import_chalk54 = __toESM(require("chalk"));
|
|
1025
1025
|
|
|
1026
1026
|
// package.json
|
|
1027
|
-
var version = "0.20.
|
|
1027
|
+
var version = "0.20.33";
|
|
1028
1028
|
|
|
1029
1029
|
// src/index.ts
|
|
1030
1030
|
init_types();
|
|
@@ -2102,6 +2102,7 @@ function suggestTenantName(projectSlug, taken) {
|
|
|
2102
2102
|
var fs8 = __toESM(require("fs"));
|
|
2103
2103
|
var path6 = __toESM(require("path"));
|
|
2104
2104
|
var crypto2 = __toESM(require("crypto"));
|
|
2105
|
+
var import_child_process3 = require("child_process");
|
|
2105
2106
|
var import_chalk14 = __toESM(require("chalk"));
|
|
2106
2107
|
var import_ora4 = __toESM(require("ora"));
|
|
2107
2108
|
var import_yaml2 = require("yaml");
|
|
@@ -2575,6 +2576,67 @@ var shellQuote = (s) => `"${s.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
|
2575
2576
|
function directedPrompt(spec2, question) {
|
|
2576
2577
|
return `Use the "${spec2.name}" MCP server (it exposes this API's tools) to answer: ${question}`;
|
|
2577
2578
|
}
|
|
2579
|
+
function agentInstructionFile(cli, dir = process.cwd()) {
|
|
2580
|
+
return path4.join(dir, cli === "claude" ? "CLAUDE.md" : "AGENTS.md");
|
|
2581
|
+
}
|
|
2582
|
+
var marker = (name) => ({
|
|
2583
|
+
start: `<!-- apiblaze:mcp:${name} -->`,
|
|
2584
|
+
end: `<!-- /apiblaze:mcp:${name} -->`
|
|
2585
|
+
});
|
|
2586
|
+
function stripMarkedBlock(text, name) {
|
|
2587
|
+
const m = marker(name);
|
|
2588
|
+
for (; ; ) {
|
|
2589
|
+
const i = text.indexOf(m.start);
|
|
2590
|
+
if (i < 0) return text;
|
|
2591
|
+
const j = text.indexOf(m.end, i);
|
|
2592
|
+
if (j < 0) return text.slice(0, i);
|
|
2593
|
+
let k = j + m.end.length;
|
|
2594
|
+
if (text[k] === "\n") k++;
|
|
2595
|
+
text = text.slice(0, i) + text.slice(k);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
function writeAgentInstruction(cli, spec2, dir = process.cwd()) {
|
|
2599
|
+
try {
|
|
2600
|
+
const file = agentInstructionFile(cli, dir);
|
|
2601
|
+
const m = marker(spec2.name);
|
|
2602
|
+
const block = [
|
|
2603
|
+
m.start,
|
|
2604
|
+
`## ${spec2.projectLabel} API \u2014 use the "${spec2.name}" MCP server`,
|
|
2605
|
+
"",
|
|
2606
|
+
`This directory is connected to the \`${spec2.name}\` MCP server, which exposes the`,
|
|
2607
|
+
`${spec2.projectLabel} API as tools. For any question about this API \u2014 its data, its`,
|
|
2608
|
+
"records, what it can do \u2014 call those tools rather than answering from memory or",
|
|
2609
|
+
"asking for data the API can provide.",
|
|
2610
|
+
"",
|
|
2611
|
+
`_Added by \`apiblaze apichat\`. Remove this block, or run \`npx apiblaze apichat ${spec2.name} --remove-mcp ${cli}\`._`,
|
|
2612
|
+
m.end,
|
|
2613
|
+
""
|
|
2614
|
+
].join("\n");
|
|
2615
|
+
let text = "";
|
|
2616
|
+
try {
|
|
2617
|
+
text = fs5.readFileSync(file, "utf-8");
|
|
2618
|
+
} catch {
|
|
2619
|
+
}
|
|
2620
|
+
const cleaned = stripMarkedBlock(text, spec2.name);
|
|
2621
|
+
const sep = cleaned.length && !cleaned.endsWith("\n\n") ? cleaned.endsWith("\n") ? "\n" : "\n\n" : "";
|
|
2622
|
+
fs5.writeFileSync(file, cleaned + sep + block, "utf-8");
|
|
2623
|
+
return file;
|
|
2624
|
+
} catch {
|
|
2625
|
+
return null;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
function removeAgentInstruction(cli, name, dir = process.cwd()) {
|
|
2629
|
+
try {
|
|
2630
|
+
const file = agentInstructionFile(cli, dir);
|
|
2631
|
+
const text = fs5.readFileSync(file, "utf-8");
|
|
2632
|
+
const cleaned = stripMarkedBlock(text, name);
|
|
2633
|
+
if (cleaned === text) return false;
|
|
2634
|
+
fs5.writeFileSync(file, cleaned.replace(/\n{3,}/g, "\n\n"), "utf-8");
|
|
2635
|
+
return true;
|
|
2636
|
+
} catch {
|
|
2637
|
+
return false;
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2578
2640
|
function claudeSystemHint(spec2) {
|
|
2579
2641
|
return `You are connected to the "${spec2.name}" MCP server, which exposes the ${spec2.projectLabel} API as tools (mcp__${spec2.name}__*). For any question about this API, call those tools rather than answering from memory or asking the user for data the API can provide.`;
|
|
2580
2642
|
}
|
|
@@ -2658,8 +2720,20 @@ async function installAndDemo(cli, spec2, getQuestion, log = console.log) {
|
|
|
2658
2720
|
if (ans.status !== 0) log(import_chalk12.default.yellow(`
|
|
2659
2721
|
${cli.label} exited with ${ans.status ?? "no status"} \u2014 the MCP stays installed.`));
|
|
2660
2722
|
}
|
|
2661
|
-
|
|
2662
|
-
|
|
2723
|
+
const instructionFile = writeAgentInstruction(cli.kind, spec2);
|
|
2724
|
+
const off = `npx apiblaze apichat ${spec2.name} --remove-mcp ${cli.kind}`;
|
|
2725
|
+
const mdName = cli.kind === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
2726
|
+
log("");
|
|
2727
|
+
log(import_chalk12.default.bgRed.white.bold(` ${cli.label} will launch with your MCP to ${spec2.projectLabel} enabled from now on. `));
|
|
2728
|
+
log(import_chalk12.default.red.bold(` Every ${cli.label} session in this directory can call your API's tools.`));
|
|
2729
|
+
if (instructionFile) {
|
|
2730
|
+
log(import_chalk12.default.red(` We added a "use this MCP" block to your ${import_chalk12.default.bold(mdName)} \u2014 that is what makes it stick:`));
|
|
2731
|
+
log(import_chalk12.default.red(` ${instructionFile}`));
|
|
2732
|
+
} else {
|
|
2733
|
+
log(import_chalk12.default.red(` (Could not write ${mdName} here, so this applies only to sessions apichat starts.)`));
|
|
2734
|
+
}
|
|
2735
|
+
log(import_chalk12.default.red(` Turn it off (removes the MCP server AND the ${mdName} block):`));
|
|
2736
|
+
log(import_chalk12.default.red(` ${import_chalk12.default.bold(off)}`));
|
|
2663
2737
|
return true;
|
|
2664
2738
|
}
|
|
2665
2739
|
|
|
@@ -4334,6 +4408,23 @@ async function startChat(p, messages, opts) {
|
|
|
4334
4408
|
}
|
|
4335
4409
|
await runRepl(p, messages);
|
|
4336
4410
|
}
|
|
4411
|
+
async function removeExternalMcp(projectId, cliKind) {
|
|
4412
|
+
const kind = cliKind.toLowerCase();
|
|
4413
|
+
if (kind !== "claude" && kind !== "codex") fail3(`--remove-mcp takes "claude" or "codex", not "${cliKind}".`);
|
|
4414
|
+
const label3 = kind === "claude" ? "Claude CLI" : "Codex CLI";
|
|
4415
|
+
let removed = false;
|
|
4416
|
+
if (kind === "claude") {
|
|
4417
|
+
const r = (0, import_child_process3.spawnSync)("claude", ["mcp", "remove", projectId], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 15e3 });
|
|
4418
|
+
removed = r.status === 0;
|
|
4419
|
+
} else {
|
|
4420
|
+
removed = removeCodexServer(projectId);
|
|
4421
|
+
}
|
|
4422
|
+
console.log(removed ? ` ${import_chalk14.default.green("\u2714")} Removed the ${import_chalk14.default.bold(projectId)} MCP server from ${label3}.` : import_chalk14.default.dim(` ${label3} had no ${projectId} MCP server configured.`));
|
|
4423
|
+
const hadNote = removeAgentInstruction(kind, projectId);
|
|
4424
|
+
console.log(hadNote ? ` ${import_chalk14.default.green("\u2714")} Removed the standing instruction from ${agentInstructionFile(kind)}.` : import_chalk14.default.dim(` No standing instruction found in ${agentInstructionFile(kind)}.`));
|
|
4425
|
+
console.log(import_chalk14.default.dim(`
|
|
4426
|
+
${label3} will no longer launch with your API's tools in this directory.`));
|
|
4427
|
+
}
|
|
4337
4428
|
async function runApichat(opts) {
|
|
4338
4429
|
setVerbose(opts.verbose === true);
|
|
4339
4430
|
console.log(import_chalk14.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
@@ -4346,6 +4437,11 @@ async function runApichat(opts) {
|
|
|
4346
4437
|
opts.target = void 0;
|
|
4347
4438
|
}
|
|
4348
4439
|
}
|
|
4440
|
+
if (opts.removeMcp) {
|
|
4441
|
+
if (!opts.project) fail3("Name the proxy: apiblaze apichat <project> --remove-mcp claude|codex");
|
|
4442
|
+
await removeExternalMcp(opts.project, opts.removeMcp);
|
|
4443
|
+
return;
|
|
4444
|
+
}
|
|
4349
4445
|
if (opts.project) {
|
|
4350
4446
|
const opened = await openDirectProject(opts.project, opts);
|
|
4351
4447
|
if (await maybeInstallExternalCli(opened.p, opts)) return;
|
|
@@ -11695,7 +11791,7 @@ agent.command("authz").description("Chat to design and turn on access rules for
|
|
|
11695
11791
|
program.command("rule").description("Author an object-level access rule in plain English, in one shot (billed per turn)").argument("<rule>", 'The rule in plain English, e.g. "users see only their own rows"').argument("<project>", "Project name or id").option("--enforce", "Turn enforcement on immediately (default: shadow-publish only)").option("--apiversion <version>", "API version (defaults to the project's)").action(action((rule, project, opts) => runRule(rule, project, opts)));
|
|
11696
11792
|
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)));
|
|
11697
11793
|
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)));
|
|
11698
|
-
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.").action(action((project, opts) => runApichat({ ...opts, project, openapispec: opts.openapispec ?? opts.openapi })));
|
|
11794
|
+
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) => runApichat({ ...opts, project, openapispec: opts.openapispec ?? opts.openapi })));
|
|
11699
11795
|
var llm = program.command("llm").description("Manage a local LLM provider key for chat (optional \u2014 lifts model quality, bills your key)");
|
|
11700
11796
|
llm.command("set-key").description("Store an LLM provider key locally (OpenRouter/Anthropic/DeepSeek/OpenAI)").argument("[key]", "The API key (omit to enter it hidden at a prompt)").option("--model <id>", "Model id to use with this key (e.g. anthropic/claude-haiku-4.5)").action(action((key, opts) => runLlmSetKey(key, opts)));
|
|
11701
11797
|
llm.command("show").description("Show the locally stored LLM key (masked)").action(action(() => runLlmShow()));
|