apiblaze 0.17.7 → 0.17.9
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 +338 -104
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -137,9 +137,9 @@ async function createProxyAnonymous(body) {
|
|
|
137
137
|
}
|
|
138
138
|
return res.json();
|
|
139
139
|
}
|
|
140
|
-
async function apiFetch(
|
|
140
|
+
async function apiFetch(path7, options = {}) {
|
|
141
141
|
const token = getAccessToken();
|
|
142
|
-
const url = `${DASHBOARD_BASE}${
|
|
142
|
+
const url = `${DASHBOARD_BASE}${path7}`;
|
|
143
143
|
const res = await fetch(url, {
|
|
144
144
|
...options,
|
|
145
145
|
headers: {
|
|
@@ -165,12 +165,12 @@ async function apiFetch(path6, options = {}) {
|
|
|
165
165
|
}
|
|
166
166
|
return res.json();
|
|
167
167
|
}
|
|
168
|
-
async function agentCall(
|
|
168
|
+
async function agentCall(path7, method, body) {
|
|
169
169
|
const token = getAccessToken();
|
|
170
170
|
const res = await fetch(`${DASHBOARD_BASE}/api/cli/agents`, {
|
|
171
171
|
method: "POST",
|
|
172
172
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
173
|
-
body: JSON.stringify({ path:
|
|
173
|
+
body: JSON.stringify({ path: path7, method, body })
|
|
174
174
|
});
|
|
175
175
|
let data = null;
|
|
176
176
|
try {
|
|
@@ -699,7 +699,7 @@ var import_commander = require("commander");
|
|
|
699
699
|
var import_chalk37 = __toESM(require("chalk"));
|
|
700
700
|
|
|
701
701
|
// package.json
|
|
702
|
-
var version = "0.17.
|
|
702
|
+
var version = "0.17.9";
|
|
703
703
|
|
|
704
704
|
// src/index.ts
|
|
705
705
|
init_types();
|
|
@@ -949,11 +949,11 @@ function decodeJwt(token) {
|
|
|
949
949
|
return null;
|
|
950
950
|
}
|
|
951
951
|
}
|
|
952
|
-
function maskPath(
|
|
953
|
-
const q =
|
|
954
|
-
if (q < 0) return
|
|
955
|
-
const base =
|
|
956
|
-
const query =
|
|
952
|
+
function maskPath(path7) {
|
|
953
|
+
const q = path7.indexOf("?");
|
|
954
|
+
if (q < 0) return path7;
|
|
955
|
+
const base = path7.slice(0, q);
|
|
956
|
+
const query = path7.slice(q + 1);
|
|
957
957
|
const masked = query.split("&").map((pair) => {
|
|
958
958
|
const eq = pair.indexOf("=");
|
|
959
959
|
if (eq < 0) return pair;
|
|
@@ -4715,6 +4715,7 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
4715
4715
|
|
|
4716
4716
|
// src/commands/apichat.ts
|
|
4717
4717
|
var fs8 = __toESM(require("fs"));
|
|
4718
|
+
var path5 = __toESM(require("path"));
|
|
4718
4719
|
var crypto2 = __toESM(require("crypto"));
|
|
4719
4720
|
var import_chalk32 = __toESM(require("chalk"));
|
|
4720
4721
|
var import_ora16 = __toESM(require("ora"));
|
|
@@ -4901,40 +4902,75 @@ function resolveTarget(spec2, opts, sourceUrl) {
|
|
|
4901
4902
|
fail4(`Could not resolve servers[0].url ("${raw}").`, "Re-run with --target <upstream base URL>.");
|
|
4902
4903
|
}
|
|
4903
4904
|
}
|
|
4904
|
-
function
|
|
4905
|
-
const
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4905
|
+
function schemeToAuth(id, s) {
|
|
4906
|
+
const type = String(s && s.type || "").toLowerCase();
|
|
4907
|
+
if (type === "apikey") {
|
|
4908
|
+
const name = String(s.name || "X-API-Key");
|
|
4909
|
+
const loc = String(s.in || "header").toLowerCase();
|
|
4910
|
+
if (loc === "query") return { destination: `param:${name}`, prefix: "", label: `${id} (query "${name}")` };
|
|
4911
|
+
const dest = loc === "cookie" ? "header:Cookie" : `header:${name}`;
|
|
4912
|
+
const prefix = loc === "cookie" ? `${name}=` : "";
|
|
4913
|
+
return { destination: dest, prefix, label: `${id} (${loc} "${name}")` };
|
|
4914
|
+
}
|
|
4915
|
+
if (type === "http") {
|
|
4916
|
+
const scheme = String(s.scheme || "").toLowerCase();
|
|
4917
|
+
if (scheme === "bearer") return { destination: "header:Authorization", prefix: "Bearer ", label: `${id} (bearer token)` };
|
|
4918
|
+
if (scheme === "basic") return { destination: "header:Authorization", prefix: "Basic ", label: `${id} (basic auth)`, basic: true };
|
|
4919
|
+
}
|
|
4920
|
+
return null;
|
|
4921
|
+
}
|
|
4922
|
+
async function resolveTargetAuth(spec2, opts) {
|
|
4923
|
+
const defs = spec2.components && spec2.components.securitySchemes || spec2.securityDefinitions || {};
|
|
4924
|
+
const req = Array.isArray(spec2.security) ? spec2.security : null;
|
|
4925
|
+
if (!req || req.length === 0) return null;
|
|
4926
|
+
const noneAllowed = req.some((alt) => alt && typeof alt === "object" && Object.keys(alt).length === 0);
|
|
4927
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4928
|
+
const candidates = [];
|
|
4929
|
+
let sawOAuth = false;
|
|
4930
|
+
for (const alt of req) {
|
|
4931
|
+
if (!alt || typeof alt !== "object") continue;
|
|
4932
|
+
for (const name of Object.keys(alt)) {
|
|
4933
|
+
if (seen.has(name)) continue;
|
|
4934
|
+
seen.add(name);
|
|
4935
|
+
const def = defs[name];
|
|
4936
|
+
const t = String(def && def.type || "").toLowerCase();
|
|
4937
|
+
if (t === "oauth2" || t === "openidconnect") {
|
|
4938
|
+
sawOAuth = true;
|
|
4939
|
+
continue;
|
|
4916
4940
|
}
|
|
4917
|
-
|
|
4918
|
-
|
|
4941
|
+
const a = def ? schemeToAuth(name, def) : null;
|
|
4942
|
+
if (a) candidates.push(a);
|
|
4919
4943
|
}
|
|
4920
4944
|
}
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
if (
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
const prefix = loc === "cookie" ? `${name}=` : "";
|
|
4929
|
-
return { destination: dest, prefix, label: `${id} (${loc} "${name}")` };
|
|
4930
|
-
}
|
|
4931
|
-
if (type === "http") {
|
|
4932
|
-
const scheme = String(s.scheme || "").toLowerCase();
|
|
4933
|
-
if (scheme === "bearer") return { destination: "header:Authorization", prefix: "Bearer ", label: `${id} (bearer token)` };
|
|
4934
|
-
if (scheme === "basic") return { destination: "header:Authorization", prefix: "Basic ", label: `${id} (basic auth)`, basic: true };
|
|
4945
|
+
if (candidates.length === 0) {
|
|
4946
|
+
if (noneAllowed) return null;
|
|
4947
|
+
if (sawOAuth && !opts.force) {
|
|
4948
|
+
fail4(
|
|
4949
|
+
"The API requires OAuth (oauth2/openIdConnect) for the target, which apichat cannot mint non-interactively.",
|
|
4950
|
+
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
4951
|
+
);
|
|
4935
4952
|
}
|
|
4953
|
+
if (sawOAuth) console.log(import_chalk32.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
|
|
4954
|
+
return null;
|
|
4936
4955
|
}
|
|
4937
|
-
return
|
|
4956
|
+
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
4957
|
+
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
4958
|
+
if (!interactive) return noneAllowed ? null : candidates[0];
|
|
4959
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
4960
|
+
const choices = [
|
|
4961
|
+
...candidates.map((c, i) => ({ name: c.label, value: i })),
|
|
4962
|
+
...noneAllowed ? [{ name: "None \u2014 the API is public / I'll configure target auth later", value: -1 }] : []
|
|
4963
|
+
];
|
|
4964
|
+
const { pick: pick2 } = await inquirer3.prompt([
|
|
4965
|
+
{
|
|
4966
|
+
type: "list",
|
|
4967
|
+
name: "pick",
|
|
4968
|
+
message: "This API accepts more than one auth method for the target \u2014 which should the proxy use?",
|
|
4969
|
+
choices,
|
|
4970
|
+
default: noneAllowed ? -1 : 0
|
|
4971
|
+
}
|
|
4972
|
+
]);
|
|
4973
|
+
return pick2 === -1 ? null : candidates[pick2];
|
|
4938
4974
|
}
|
|
4939
4975
|
async function captureTargetSecret(auth, opts) {
|
|
4940
4976
|
const interactive = !!process.stdin.isTTY;
|
|
@@ -4967,65 +5003,92 @@ async function captureTargetSecret(auth, opts) {
|
|
|
4967
5003
|
if (!secret) fail4("No credential entered.");
|
|
4968
5004
|
return secret;
|
|
4969
5005
|
}
|
|
4970
|
-
async function cpPost(anon,
|
|
5006
|
+
async function cpPost(anon, path7, body, summary) {
|
|
4971
5007
|
if (anon) {
|
|
4972
5008
|
const cred = loadAnonCred();
|
|
4973
5009
|
if (!cred) throw new Error("Anonymous workspace credential missing.");
|
|
4974
|
-
return cpFetch(cred.cp_key,
|
|
5010
|
+
return cpFetch(cred.cp_key, path7, { method: "POST", body: JSON.stringify(body) });
|
|
4975
5011
|
}
|
|
4976
|
-
return admin({ method: "POST", path:
|
|
5012
|
+
return admin({ method: "POST", path: path7, body, summary });
|
|
4977
5013
|
}
|
|
4978
5014
|
async function provision(spec2, target, opts) {
|
|
4979
5015
|
const loggedIn = !!loadCredentials();
|
|
4980
5016
|
const anon = !loggedIn;
|
|
4981
|
-
|
|
4982
|
-
|
|
5017
|
+
const salt = () => Math.random().toString(36).slice(2, 6);
|
|
5018
|
+
let base = opts.name ? normalizeName2(opts.name) : "";
|
|
5019
|
+
if (!base) {
|
|
4983
5020
|
try {
|
|
4984
5021
|
const host = new URL(target).hostname;
|
|
4985
|
-
|
|
4986
|
-
if (
|
|
5022
|
+
base = normalizeName2(host.split(".")[0]);
|
|
5023
|
+
if (base.length < 3) base = normalizeName2(host);
|
|
4987
5024
|
} catch {
|
|
4988
5025
|
}
|
|
4989
5026
|
}
|
|
4990
|
-
if (!
|
|
4991
|
-
if (!
|
|
5027
|
+
if (!base && spec2.info && typeof spec2.info.title === "string") base = normalizeName2(spec2.info.title);
|
|
5028
|
+
if (!base || base.length < 3) base = "apichat";
|
|
5029
|
+
let name = opts.name ? base : `${base}-${salt()}`;
|
|
4992
5030
|
const spinner = (0, import_ora16.default)("Provisioning an api_key proxy...").start();
|
|
4993
5031
|
let result;
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
const cred = loadAnonCred();
|
|
5006
|
-
const body = {
|
|
5007
|
-
name,
|
|
5008
|
-
subdomain: name,
|
|
5009
|
-
target,
|
|
5010
|
-
target_url: target,
|
|
5011
|
-
auth_type: "api_key",
|
|
5012
|
-
...opts.apiversion ? { api_version: opts.apiversion } : {}
|
|
5013
|
-
};
|
|
5014
|
-
if (cred) {
|
|
5015
|
-
result = await cpFetch(cred.cp_key, "/projects", { method: "POST", body: JSON.stringify(body) });
|
|
5032
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
5033
|
+
try {
|
|
5034
|
+
if (loggedIn) {
|
|
5035
|
+
const creds = loadCredentials();
|
|
5036
|
+
result = await createProxy({
|
|
5037
|
+
name,
|
|
5038
|
+
target_url: target,
|
|
5039
|
+
auth_type: "api_key",
|
|
5040
|
+
team_id: creds.teamId,
|
|
5041
|
+
...opts.apiversion ? { api_version: opts.apiversion } : {}
|
|
5042
|
+
});
|
|
5016
5043
|
} else {
|
|
5017
|
-
|
|
5018
|
-
|
|
5044
|
+
const cred = loadAnonCred();
|
|
5045
|
+
const body = {
|
|
5046
|
+
name,
|
|
5047
|
+
subdomain: name,
|
|
5048
|
+
target,
|
|
5049
|
+
target_url: target,
|
|
5050
|
+
auth_type: "api_key",
|
|
5051
|
+
...opts.apiversion ? { api_version: opts.apiversion } : {}
|
|
5052
|
+
};
|
|
5053
|
+
if (cred) {
|
|
5054
|
+
result = await cpFetch(cred.cp_key, "/projects", { method: "POST", body: JSON.stringify(body) });
|
|
5055
|
+
} else {
|
|
5056
|
+
result = await createProxyAnonymous(body);
|
|
5057
|
+
if (result.cp_key && result.team_id) saveAnonCred(result.cp_key, result.team_id, result.claim_code);
|
|
5058
|
+
}
|
|
5019
5059
|
}
|
|
5060
|
+
spinner.succeed(`Proxy provisioned${name !== base ? ` as "${name}"` : ""}.`);
|
|
5061
|
+
break;
|
|
5062
|
+
} catch (err) {
|
|
5063
|
+
const status = err instanceof ApiError ? err.status : void 0;
|
|
5064
|
+
const collision = err instanceof ApiError && (err.status === 409 || /exist|taken|available/i.test(err.message));
|
|
5065
|
+
if (collision && attempt < 3) {
|
|
5066
|
+
name = `${base}-${salt()}`;
|
|
5067
|
+
continue;
|
|
5068
|
+
}
|
|
5069
|
+
if (status === 401) {
|
|
5070
|
+
if (!loggedIn && attempt < 3) {
|
|
5071
|
+
clearAnonCred();
|
|
5072
|
+
continue;
|
|
5073
|
+
}
|
|
5074
|
+
spinner.fail("Provisioning failed.");
|
|
5075
|
+
if (loggedIn) {
|
|
5076
|
+
fail4(
|
|
5077
|
+
"Your login session was rejected \u2014 it may have expired.",
|
|
5078
|
+
"Run `apiblaze login` to refresh, then re-run `apiblaze apichat`."
|
|
5079
|
+
);
|
|
5080
|
+
}
|
|
5081
|
+
fail4(
|
|
5082
|
+
"Could not start an anonymous workspace right now.",
|
|
5083
|
+
"Try again in a moment, or run `apiblaze login` to chat with your account."
|
|
5084
|
+
);
|
|
5085
|
+
}
|
|
5086
|
+
spinner.fail("Provisioning failed.");
|
|
5087
|
+
if (collision) fail4(`A proxy named "${name}" already exists.`, "Re-run with --name <other>.");
|
|
5088
|
+
throw err;
|
|
5020
5089
|
}
|
|
5021
|
-
spinner.succeed("Proxy provisioned.");
|
|
5022
|
-
} catch (err) {
|
|
5023
|
-
spinner.fail("Provisioning failed.");
|
|
5024
|
-
if (err instanceof ApiError && (err.status === 409 || /exist|taken|available/i.test(err.message))) {
|
|
5025
|
-
fail4(`A proxy named "${name}" already exists.`, "Re-run with --name <other> (or `apiblaze delete` it first).");
|
|
5026
|
-
}
|
|
5027
|
-
throw err;
|
|
5028
5090
|
}
|
|
5091
|
+
if (!result) fail4("Provisioning failed after retries.");
|
|
5029
5092
|
const projectId = result.project_id;
|
|
5030
5093
|
const version2 = result.api_version || "1.0.0";
|
|
5031
5094
|
const keys = result.api_keys ?? {};
|
|
@@ -5133,7 +5196,19 @@ function renderToolEvents(events) {
|
|
|
5133
5196
|
function billingLine(billing) {
|
|
5134
5197
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
5135
5198
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
5136
|
-
|
|
5199
|
+
let line = import_chalk32.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk32.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
5200
|
+
if (typeof billing.credits_remaining === "number") {
|
|
5201
|
+
line += import_chalk32.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
5202
|
+
}
|
|
5203
|
+
return line;
|
|
5204
|
+
}
|
|
5205
|
+
function freeBudgetWarning(billing, anon) {
|
|
5206
|
+
if (!anon || !billing || typeof billing.free_remaining_cents !== "number") return null;
|
|
5207
|
+
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
5208
|
+
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
5209
|
+
if (left > 8) return null;
|
|
5210
|
+
if (left <= 0) return import_chalk32.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
5211
|
+
return import_chalk32.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
5137
5212
|
}
|
|
5138
5213
|
function printAssistant(delta) {
|
|
5139
5214
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
@@ -5207,6 +5282,8 @@ async function replTurn(p, messages, userText) {
|
|
|
5207
5282
|
}
|
|
5208
5283
|
const bl = billingLine(data.billing);
|
|
5209
5284
|
if (bl) console.log(bl);
|
|
5285
|
+
const warn = freeBudgetWarning(data.billing, p.anon);
|
|
5286
|
+
if (warn) console.log(warn);
|
|
5210
5287
|
verboseTrace(p, !!llm2);
|
|
5211
5288
|
if (!data.continue) return;
|
|
5212
5289
|
}
|
|
@@ -5226,10 +5303,144 @@ function renderUpsell(p, upsell) {
|
|
|
5226
5303
|
}
|
|
5227
5304
|
console.log();
|
|
5228
5305
|
}
|
|
5229
|
-
|
|
5306
|
+
var apichatsPath = () => path5.join(getApiblazeDir(), "apichats.json");
|
|
5307
|
+
function loadApichats() {
|
|
5308
|
+
try {
|
|
5309
|
+
const list = JSON.parse(fs8.readFileSync(apichatsPath(), "utf-8"));
|
|
5310
|
+
return Array.isArray(list) ? list : [];
|
|
5311
|
+
} catch {
|
|
5312
|
+
return [];
|
|
5313
|
+
}
|
|
5314
|
+
}
|
|
5315
|
+
function writeApichats(list) {
|
|
5316
|
+
fs8.mkdirSync(getApiblazeDir(), { recursive: true });
|
|
5317
|
+
fs8.writeFileSync(apichatsPath(), JSON.stringify(list, null, 2), "utf-8");
|
|
5318
|
+
try {
|
|
5319
|
+
fs8.chmodSync(apichatsPath(), 384);
|
|
5320
|
+
} catch {
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
5323
|
+
function apichatKey(a) {
|
|
5324
|
+
return `${a.projectId}::${a.version}::${a.environment}`;
|
|
5325
|
+
}
|
|
5326
|
+
function upsertApichat(entry) {
|
|
5327
|
+
const list = loadApichats();
|
|
5328
|
+
const i = list.findIndex((a) => apichatKey(a) === apichatKey(entry));
|
|
5329
|
+
if (i >= 0) list[i] = { ...list[i], ...entry };
|
|
5330
|
+
else list.unshift(entry);
|
|
5331
|
+
writeApichats(list.slice(0, 30));
|
|
5332
|
+
}
|
|
5333
|
+
function saveTranscript(p, messages) {
|
|
5334
|
+
const list = loadApichats();
|
|
5335
|
+
const i = list.findIndex((a) => apichatKey(a) === apichatKey(p));
|
|
5336
|
+
if (i < 0) return;
|
|
5337
|
+
list[i].messages = messages.slice(-40);
|
|
5338
|
+
list[i].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5339
|
+
writeApichats(list);
|
|
5340
|
+
}
|
|
5341
|
+
function discoverLocalSpecs() {
|
|
5342
|
+
const cwd = process.cwd();
|
|
5343
|
+
const known = ["openapi.yaml", "openapi.yml", "openapi.json", "swagger.yaml", "swagger.yml", "swagger.json", "api.yaml", "api.yml", "api.json"];
|
|
5344
|
+
const found = [];
|
|
5345
|
+
for (const n of known) {
|
|
5346
|
+
try {
|
|
5347
|
+
if (fs8.statSync(path5.join(cwd, n)).isFile()) found.push(n);
|
|
5348
|
+
} catch {
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5351
|
+
try {
|
|
5352
|
+
const files = fs8.readdirSync(cwd).filter((f) => /\.(ya?ml|json)$/i.test(f) && !found.includes(f));
|
|
5353
|
+
for (const f of files.slice(0, 60)) {
|
|
5354
|
+
try {
|
|
5355
|
+
const head = fs8.readFileSync(path5.join(cwd, f), "utf-8").slice(0, 4e3);
|
|
5356
|
+
if (/["']?openapi["']?\s*:/i.test(head) || /["']?swagger["']?\s*:/i.test(head) || /^\s*paths\s*:/im.test(head) || /"paths"\s*:/.test(head)) {
|
|
5357
|
+
found.push(f);
|
|
5358
|
+
}
|
|
5359
|
+
} catch {
|
|
5360
|
+
}
|
|
5361
|
+
}
|
|
5362
|
+
} catch {
|
|
5363
|
+
}
|
|
5364
|
+
return found;
|
|
5365
|
+
}
|
|
5366
|
+
async function noArgsMenu(opts) {
|
|
5230
5367
|
const { default: inquirer3 } = await import("inquirer");
|
|
5231
|
-
const
|
|
5368
|
+
const saved = loadApichats();
|
|
5369
|
+
const choices = saved.map((a) => ({
|
|
5370
|
+
name: `Chat with ${import_chalk32.default.bold(a.name)} ${import_chalk32.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
|
|
5371
|
+
value: { type: "existing", a }
|
|
5372
|
+
}));
|
|
5373
|
+
choices.push({ name: import_chalk32.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
5374
|
+
const { pick: pick2 } = await inquirer3.prompt([
|
|
5375
|
+
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
5376
|
+
]);
|
|
5377
|
+
if (pick2.type === "existing") {
|
|
5378
|
+
const a = pick2.a;
|
|
5379
|
+
let messages = [];
|
|
5380
|
+
if (a.messages && a.messages.length) {
|
|
5381
|
+
const { mode } = await inquirer3.prompt([
|
|
5382
|
+
{
|
|
5383
|
+
type: "list",
|
|
5384
|
+
name: "mode",
|
|
5385
|
+
message: "Resume the previous conversation, or start fresh?",
|
|
5386
|
+
choices: [
|
|
5387
|
+
{ name: `Resume (${a.messages.length} messages)`, value: "resume" },
|
|
5388
|
+
{ name: "Start fresh", value: "fresh" }
|
|
5389
|
+
]
|
|
5390
|
+
}
|
|
5391
|
+
]);
|
|
5392
|
+
if (mode === "resume") messages = a.messages.slice();
|
|
5393
|
+
}
|
|
5394
|
+
const p = {
|
|
5395
|
+
projectId: a.projectId,
|
|
5396
|
+
version: a.version,
|
|
5397
|
+
environment: a.environment,
|
|
5398
|
+
dpKey: a.dpKey,
|
|
5399
|
+
mcpHost: a.mcpHost,
|
|
5400
|
+
proxyUrl: void 0,
|
|
5401
|
+
anon: a.anon
|
|
5402
|
+
};
|
|
5403
|
+
return { p, messages };
|
|
5404
|
+
}
|
|
5405
|
+
const { source } = await inquirer3.prompt([
|
|
5406
|
+
{
|
|
5407
|
+
type: "list",
|
|
5408
|
+
name: "source",
|
|
5409
|
+
message: "Where is the API spec?",
|
|
5410
|
+
choices: [
|
|
5411
|
+
{ name: "A local OpenAPI file", value: "file" },
|
|
5412
|
+
{ name: "A spec URL", value: "url" },
|
|
5413
|
+
{ name: "Discover it from a running API (target URL)", value: "target" }
|
|
5414
|
+
]
|
|
5415
|
+
}
|
|
5416
|
+
]);
|
|
5417
|
+
if (source === "file") {
|
|
5418
|
+
const cands = discoverLocalSpecs();
|
|
5419
|
+
if (cands.length) {
|
|
5420
|
+
const { file } = await inquirer3.prompt([
|
|
5421
|
+
{
|
|
5422
|
+
type: "list",
|
|
5423
|
+
name: "file",
|
|
5424
|
+
message: "Pick a spec file (found in this directory):",
|
|
5425
|
+
choices: [...cands.map((f) => ({ name: f, value: f })), { name: "Enter a path manually\u2026", value: "__manual__" }]
|
|
5426
|
+
}
|
|
5427
|
+
]);
|
|
5428
|
+
opts.openapispec = file === "__manual__" ? (await inquirer3.prompt([{ type: "input", name: "p", message: "Path to the OpenAPI file:" }])).p : file;
|
|
5429
|
+
} else {
|
|
5430
|
+
opts.openapispec = (await inquirer3.prompt([{ type: "input", name: "p", message: "Path to the OpenAPI file:" }])).p;
|
|
5431
|
+
}
|
|
5432
|
+
} else if (source === "url") {
|
|
5433
|
+
opts.openapispec = (await inquirer3.prompt([{ type: "input", name: "u", message: "OpenAPI spec URL:" }])).u;
|
|
5434
|
+
} else {
|
|
5435
|
+
opts.target = (await inquirer3.prompt([{ type: "input", name: "t", message: "Target API base URL:" }])).t;
|
|
5436
|
+
}
|
|
5437
|
+
return null;
|
|
5438
|
+
}
|
|
5439
|
+
async function runRepl(p, initialMessages) {
|
|
5440
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
5441
|
+
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
5232
5442
|
console.log("\n" + import_chalk32.default.cyan.bold("Chat with your API") + import_chalk32.default.dim(` \xB7 ${p.mcpHost}`));
|
|
5443
|
+
if (messages.length) console.log(import_chalk32.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
5233
5444
|
const llm2 = loadLlmConfig();
|
|
5234
5445
|
console.log(
|
|
5235
5446
|
import_chalk32.default.dim(
|
|
@@ -5272,15 +5483,26 @@ async function runRepl(p) {
|
|
|
5272
5483
|
continue;
|
|
5273
5484
|
}
|
|
5274
5485
|
await replTurn(p, messages, text);
|
|
5486
|
+
saveTranscript(p, messages);
|
|
5275
5487
|
}
|
|
5276
5488
|
console.log(import_chalk32.default.dim("\nBye."));
|
|
5277
5489
|
}
|
|
5278
5490
|
async function runApichat(opts) {
|
|
5279
5491
|
console.log(import_chalk32.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
5492
|
+
if (!opts.openapispec && !opts.target) {
|
|
5493
|
+
if (!process.stdin.isTTY) {
|
|
5494
|
+
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
5495
|
+
}
|
|
5496
|
+
const resumed = await noArgsMenu(opts);
|
|
5497
|
+
if (resumed) {
|
|
5498
|
+
await runRepl(resumed.p, resumed.messages);
|
|
5499
|
+
return;
|
|
5500
|
+
}
|
|
5501
|
+
}
|
|
5280
5502
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
5281
5503
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
5282
5504
|
console.log(` ${import_chalk32.default.dim("Target:")} ${import_chalk32.default.bold(target)}`);
|
|
5283
|
-
const auth =
|
|
5505
|
+
const auth = await resolveTargetAuth(spec2, opts);
|
|
5284
5506
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
5285
5507
|
fail4(
|
|
5286
5508
|
`This API needs target credentials (${auth.label}) and there is no TTY to prompt.`,
|
|
@@ -5289,6 +5511,18 @@ async function runApichat(opts) {
|
|
|
5289
5511
|
}
|
|
5290
5512
|
const p = await provision(spec2, target, opts);
|
|
5291
5513
|
console.log(` ${import_chalk32.default.dim("Proxy: ")} ${import_chalk32.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
5514
|
+
upsertApichat({
|
|
5515
|
+
name: p.projectId,
|
|
5516
|
+
target,
|
|
5517
|
+
projectId: p.projectId,
|
|
5518
|
+
version: p.version,
|
|
5519
|
+
environment: p.environment,
|
|
5520
|
+
mcpHost: p.mcpHost,
|
|
5521
|
+
dpKey: p.dpKey,
|
|
5522
|
+
anon: p.anon,
|
|
5523
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5524
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5525
|
+
});
|
|
5292
5526
|
if (auth) {
|
|
5293
5527
|
const secret = await captureTargetSecret(auth, opts);
|
|
5294
5528
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
@@ -5467,23 +5701,23 @@ async function runConsumerApikeys(opts) {
|
|
|
5467
5701
|
var import_chalk34 = __toESM(require("chalk"));
|
|
5468
5702
|
var import_ora18 = __toESM(require("ora"));
|
|
5469
5703
|
var fs9 = __toESM(require("fs"));
|
|
5470
|
-
var
|
|
5704
|
+
var path6 = __toESM(require("path"));
|
|
5471
5705
|
init_admin();
|
|
5472
5706
|
init_auth();
|
|
5473
5707
|
function detectNextProject(root) {
|
|
5474
|
-
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs9.existsSync(
|
|
5708
|
+
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs9.existsSync(path6.join(root, f)));
|
|
5475
5709
|
let hasDep = false;
|
|
5476
5710
|
try {
|
|
5477
|
-
const pkg = JSON.parse(fs9.readFileSync(
|
|
5711
|
+
const pkg = JSON.parse(fs9.readFileSync(path6.join(root, "package.json"), "utf8"));
|
|
5478
5712
|
hasDep = !!(pkg.dependencies?.next || pkg.devDependencies?.next);
|
|
5479
5713
|
} catch {
|
|
5480
5714
|
}
|
|
5481
|
-
const appDir = fs9.existsSync(
|
|
5482
|
-
const pagesDir = fs9.existsSync(
|
|
5715
|
+
const appDir = fs9.existsSync(path6.join(root, "app")) || fs9.existsSync(path6.join(root, "src", "app"));
|
|
5716
|
+
const pagesDir = fs9.existsSync(path6.join(root, "pages")) || fs9.existsSync(path6.join(root, "src", "pages"));
|
|
5483
5717
|
return { found: hasConfig || hasDep || appDir || pagesDir, router: appDir ? "app" : pagesDir ? "pages" : null };
|
|
5484
5718
|
}
|
|
5485
5719
|
function upsertEnvLocal(root, token) {
|
|
5486
|
-
const p =
|
|
5720
|
+
const p = path6.join(root, ".env.local");
|
|
5487
5721
|
let existing = "";
|
|
5488
5722
|
try {
|
|
5489
5723
|
existing = fs9.readFileSync(p, "utf8");
|
|
@@ -5505,11 +5739,11 @@ function upsertEnvLocal(root, token) {
|
|
|
5505
5739
|
return had ? "rotated" : "created";
|
|
5506
5740
|
}
|
|
5507
5741
|
function installSidecarPackage(root) {
|
|
5508
|
-
if (fs9.existsSync(
|
|
5742
|
+
if (fs9.existsSync(path6.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
5509
5743
|
console.log(` ${import_chalk34.default.green("\u2713")} apiblaze package already installed`);
|
|
5510
5744
|
return;
|
|
5511
5745
|
}
|
|
5512
|
-
const has = (f) => fs9.existsSync(
|
|
5746
|
+
const has = (f) => fs9.existsSync(path6.join(root, f));
|
|
5513
5747
|
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" };
|
|
5514
5748
|
const spinner = (0, import_ora18.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
5515
5749
|
try {
|
|
@@ -5522,7 +5756,7 @@ function installSidecarPackage(root) {
|
|
|
5522
5756
|
}
|
|
5523
5757
|
function readEnvKey(root) {
|
|
5524
5758
|
try {
|
|
5525
|
-
const s = fs9.readFileSync(
|
|
5759
|
+
const s = fs9.readFileSync(path6.join(root, ".env.local"), "utf8");
|
|
5526
5760
|
const m = s.match(/^APIBLAZE_API_KEY=(.+)$/m) ?? s.match(/^APIBLAZE_TOKEN=(.+)$/m);
|
|
5527
5761
|
return m ? m[1].trim() : null;
|
|
5528
5762
|
} catch {
|
|
@@ -5530,7 +5764,7 @@ function readEnvKey(root) {
|
|
|
5530
5764
|
}
|
|
5531
5765
|
}
|
|
5532
5766
|
function ensureGitignored(root) {
|
|
5533
|
-
const p =
|
|
5767
|
+
const p = path6.join(root, ".gitignore");
|
|
5534
5768
|
let c = "";
|
|
5535
5769
|
try {
|
|
5536
5770
|
c = fs9.readFileSync(p, "utf8");
|
|
@@ -5539,7 +5773,7 @@ function ensureGitignored(root) {
|
|
|
5539
5773
|
if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c)) fs9.writeFileSync(p, (c && !c.endsWith("\n") ? c + "\n" : c) + ".env.local\n");
|
|
5540
5774
|
}
|
|
5541
5775
|
function wireInstrumentation(root) {
|
|
5542
|
-
const existing = ["instrumentation.ts", "instrumentation.js",
|
|
5776
|
+
const existing = ["instrumentation.ts", "instrumentation.js", path6.join("src", "instrumentation.ts")].map((c) => path6.join(root, c)).find((f) => fs9.existsSync(f));
|
|
5543
5777
|
const body = `import { register as apiblaze } from "apiblaze/sidecar";
|
|
5544
5778
|
|
|
5545
5779
|
export function register() {
|
|
@@ -5547,7 +5781,7 @@ export function register() {
|
|
|
5547
5781
|
}
|
|
5548
5782
|
`;
|
|
5549
5783
|
if (!existing) {
|
|
5550
|
-
fs9.writeFileSync(
|
|
5784
|
+
fs9.writeFileSync(path6.join(root, "instrumentation.ts"), body);
|
|
5551
5785
|
return "created";
|
|
5552
5786
|
}
|
|
5553
5787
|
const cur = fs9.readFileSync(existing, "utf8");
|
|
@@ -5637,17 +5871,17 @@ export default async function Page() {
|
|
|
5637
5871
|
function generateInspector(root, router) {
|
|
5638
5872
|
try {
|
|
5639
5873
|
if (router === "pages") {
|
|
5640
|
-
const dir2 = fs9.existsSync(
|
|
5641
|
-
const f2 =
|
|
5874
|
+
const dir2 = fs9.existsSync(path6.join(root, "src", "pages")) ? path6.join(root, "src", "pages") : path6.join(root, "pages");
|
|
5875
|
+
const f2 = path6.join(dir2, "abz-inspector.tsx");
|
|
5642
5876
|
fs9.writeFileSync(f2, INSPECTOR_PAGE);
|
|
5643
|
-
return
|
|
5877
|
+
return path6.relative(root, f2);
|
|
5644
5878
|
}
|
|
5645
|
-
const base = fs9.existsSync(
|
|
5646
|
-
const dir =
|
|
5879
|
+
const base = fs9.existsSync(path6.join(root, "src", "app")) ? path6.join(root, "src", "app") : path6.join(root, "app");
|
|
5880
|
+
const dir = path6.join(base, "abz-inspector");
|
|
5647
5881
|
fs9.mkdirSync(dir, { recursive: true });
|
|
5648
|
-
const f =
|
|
5882
|
+
const f = path6.join(dir, "page.tsx");
|
|
5649
5883
|
fs9.writeFileSync(f, INSPECTOR_PAGE);
|
|
5650
|
-
return
|
|
5884
|
+
return path6.relative(root, f);
|
|
5651
5885
|
} catch {
|
|
5652
5886
|
return null;
|
|
5653
5887
|
}
|
|
@@ -5687,7 +5921,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
5687
5921
|
console.log(import_chalk34.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
5688
5922
|
}
|
|
5689
5923
|
async function runSidecar(opts) {
|
|
5690
|
-
const root =
|
|
5924
|
+
const root = path6.resolve(opts.dir ?? process.cwd());
|
|
5691
5925
|
const detected = detectNextProject(root);
|
|
5692
5926
|
if (!detected.found) {
|
|
5693
5927
|
console.log(import_chalk34.default.yellow(`No Next.js project detected in ${root}.`));
|
|
@@ -5746,7 +5980,7 @@ async function runSidecar(opts) {
|
|
|
5746
5980
|
console.log(` 2. The origins your app calls appear as ${import_chalk34.default.bold("candidates")} \u2014 list them: ${import_chalk34.default.cyan("apiblaze sidecar")}`);
|
|
5747
5981
|
console.log(` 3. Approve the ones to route: ${import_chalk34.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
5748
5982
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
5749
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk34.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${
|
|
5983
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk34.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path6.dirname(inspectorPath)} before shipping)`);
|
|
5750
5984
|
if (switchingTeam) console.log(import_chalk34.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
5751
5985
|
console.log("");
|
|
5752
5986
|
console.log(import_chalk34.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|