apiblaze 0.19.19 → 0.19.20
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 +113 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -932,7 +932,7 @@ var import_commander = require("commander");
|
|
|
932
932
|
var import_chalk44 = __toESM(require("chalk"));
|
|
933
933
|
|
|
934
934
|
// package.json
|
|
935
|
-
var version = "0.19.
|
|
935
|
+
var version = "0.19.20";
|
|
936
936
|
|
|
937
937
|
// src/index.ts
|
|
938
938
|
init_types();
|
|
@@ -6054,6 +6054,68 @@ async function captureTargetSecret(auth, opts) {
|
|
|
6054
6054
|
if (!secret) fail4("No credential entered.");
|
|
6055
6055
|
return secret;
|
|
6056
6056
|
}
|
|
6057
|
+
async function dataPlaneAuth(p) {
|
|
6058
|
+
if (!p.consumerAuth) {
|
|
6059
|
+
if (!p.dpKey) throw new Error("No API key for this proxy.");
|
|
6060
|
+
return { "X-API-Key": p.dpKey };
|
|
6061
|
+
}
|
|
6062
|
+
const stored = loadConsumer();
|
|
6063
|
+
if (!stored) {
|
|
6064
|
+
throw new Error("Consumer login required \u2014 run `apiblaze apichat` again to sign in.");
|
|
6065
|
+
}
|
|
6066
|
+
const fresh = await validConsumerToken(stored) ?? stored;
|
|
6067
|
+
if (fresh.accessToken !== stored.accessToken) saveConsumer(fresh);
|
|
6068
|
+
return { Authorization: `Bearer ${fresh.accessToken}` };
|
|
6069
|
+
}
|
|
6070
|
+
async function ensureConsumerLogin(teamId, tenant2) {
|
|
6071
|
+
const existing = loadConsumer();
|
|
6072
|
+
if (existing && existing.tenant === tenant2) {
|
|
6073
|
+
const fresh = await validConsumerToken(existing);
|
|
6074
|
+
if (fresh) {
|
|
6075
|
+
if (fresh.accessToken !== existing.accessToken) saveConsumer(fresh);
|
|
6076
|
+
console.log(import_chalk39.default.dim(` Using your consumer session on ${import_chalk39.default.bold(tenant2)}${fresh.email ? ` (${fresh.email})` : ""}.`));
|
|
6077
|
+
return fresh;
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
const spinner = (0, import_ora21.default)(`Finding the login app for ${tenant2}...`).start();
|
|
6081
|
+
const clients = await admin({
|
|
6082
|
+
method: "GET",
|
|
6083
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`,
|
|
6084
|
+
summary: `List app clients for ${tenant2}`
|
|
6085
|
+
}).catch(() => []);
|
|
6086
|
+
spinner.stop();
|
|
6087
|
+
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
6088
|
+
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
6089
|
+
if (!pick2) {
|
|
6090
|
+
throw new Error(
|
|
6091
|
+
`Tenant "${tenant2}" has no login app configured, so there is no way to sign in as a consumer. Add one in the dashboard (Tenants \u2192 app clients).`
|
|
6092
|
+
);
|
|
6093
|
+
}
|
|
6094
|
+
const clientId = pick2.client_id ?? pick2.clientId;
|
|
6095
|
+
console.log(`${import_chalk39.default.cyan("\u2192")} This proxy signs consumers in with OAuth \u2014 logging you in to ${import_chalk39.default.bold(tenant2)}...`);
|
|
6096
|
+
const result = await deviceLogin(clientId, "openid email profile offline_access", ({ verificationUri, userCode }) => {
|
|
6097
|
+
console.log(`
|
|
6098
|
+
Open: ${import_chalk39.default.underline(verificationUri)}`);
|
|
6099
|
+
console.log(` Code: ${import_chalk39.default.bold(userCode)}
|
|
6100
|
+
`);
|
|
6101
|
+
console.log(import_chalk39.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
6102
|
+
}, `https://${tenant2}.portal.apiblaze.com/1.0.0`);
|
|
6103
|
+
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
6104
|
+
const creds = {
|
|
6105
|
+
tenant: tenant2,
|
|
6106
|
+
clientId,
|
|
6107
|
+
accessToken: result.accessToken,
|
|
6108
|
+
refreshToken: result.refreshToken,
|
|
6109
|
+
idToken: result.idToken,
|
|
6110
|
+
expiresAt: Date.now() + (result.expiresIn ?? 3600) * 1e3,
|
|
6111
|
+
scope: result.scope,
|
|
6112
|
+
email: typeof claims.email === "string" ? claims.email : void 0,
|
|
6113
|
+
obtainedAt: Date.now()
|
|
6114
|
+
};
|
|
6115
|
+
saveConsumer(creds);
|
|
6116
|
+
console.log(` ${import_chalk39.default.green("\u2714")} Signed in as${creds.email ? ` ${import_chalk39.default.bold(creds.email)}` : " a consumer"} on ${tenant2}.`);
|
|
6117
|
+
return creds;
|
|
6118
|
+
}
|
|
6057
6119
|
async function cpPost(anon, path8, body, summary) {
|
|
6058
6120
|
if (anon) {
|
|
6059
6121
|
const cred = loadAnonCred();
|
|
@@ -6235,7 +6297,7 @@ async function publishMcp(p, spec2) {
|
|
|
6235
6297
|
const url = `https://${p.mcpHost}/${p.version}/${p.environment}/mcp/generate`;
|
|
6236
6298
|
const res = await fetch(url, {
|
|
6237
6299
|
method: "POST",
|
|
6238
|
-
headers: { "Content-Type": "application/json",
|
|
6300
|
+
headers: { "Content-Type": "application/json", ...await dataPlaneAuth(p) },
|
|
6239
6301
|
body: JSON.stringify({ openapi: spec2, environment: p.environment })
|
|
6240
6302
|
});
|
|
6241
6303
|
const out = await res.json().catch(() => null);
|
|
@@ -6263,15 +6325,16 @@ function maskKey(k) {
|
|
|
6263
6325
|
return k.length <= 8 ? "****" : `${k.slice(0, 4)}\u2026${k.slice(-4)}`;
|
|
6264
6326
|
}
|
|
6265
6327
|
var revealAuth = false;
|
|
6266
|
-
function renderToolEvents(events,
|
|
6328
|
+
function renderToolEvents(events, cred) {
|
|
6267
6329
|
for (const e of events ?? []) {
|
|
6268
6330
|
const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
|
|
6269
6331
|
const mark = ok ? import_chalk39.default.green("\u2713") : import_chalk39.default.red("\u2717");
|
|
6270
6332
|
console.log(` ${mark} ${import_chalk39.default.cyan(e.name)} ${import_chalk39.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
|
|
6271
6333
|
if (isVerbose() && e.method && e.url) {
|
|
6272
|
-
console.log(import_chalk39.default.dim(` curl -sS -X ${e.method} '${e.url}'${
|
|
6273
|
-
if (
|
|
6274
|
-
const
|
|
6334
|
+
console.log(import_chalk39.default.dim(` curl -sS -X ${e.method} '${e.url}'${cred ? " \\" : ""}`));
|
|
6335
|
+
if (cred) {
|
|
6336
|
+
const shown = revealAuth ? cred.value : maskKey(cred.value);
|
|
6337
|
+
const keyLine = ` -H '${cred.header}: ${shown}'`;
|
|
6275
6338
|
const hint = revealAuth ? "" : import_chalk39.default.yellow(" \u2190 /showauth will reveal this");
|
|
6276
6339
|
console.log(import_chalk39.default.dim(keyLine) + hint);
|
|
6277
6340
|
}
|
|
@@ -6327,7 +6390,7 @@ async function replTurn(p, messages, userText) {
|
|
|
6327
6390
|
try {
|
|
6328
6391
|
res = await fetch(chatUrl(p), {
|
|
6329
6392
|
method: "POST",
|
|
6330
|
-
headers: { "Content-Type": "application/json",
|
|
6393
|
+
headers: { "Content-Type": "application/json", ...await dataPlaneAuth(p) },
|
|
6331
6394
|
body: JSON.stringify(body)
|
|
6332
6395
|
});
|
|
6333
6396
|
} catch (err) {
|
|
@@ -6359,7 +6422,13 @@ async function replTurn(p, messages, userText) {
|
|
|
6359
6422
|
return;
|
|
6360
6423
|
}
|
|
6361
6424
|
if (Array.isArray(data.delta)) {
|
|
6362
|
-
renderToolEvents(
|
|
6425
|
+
renderToolEvents(
|
|
6426
|
+
data.tool_events,
|
|
6427
|
+
p.consumerAuth ? (() => {
|
|
6428
|
+
const t = loadConsumer()?.accessToken;
|
|
6429
|
+
return t ? { header: "Authorization", value: `Bearer ${t}` } : void 0;
|
|
6430
|
+
})() : p.dpKey ? { header: "X-API-Key", value: p.dpKey } : void 0
|
|
6431
|
+
);
|
|
6363
6432
|
for (const m of data.delta) messages.push(m);
|
|
6364
6433
|
printAssistant(data.delta);
|
|
6365
6434
|
}
|
|
@@ -6447,24 +6516,50 @@ async function mintDurableProxyKey(teamId, tenant2) {
|
|
|
6447
6516
|
if (!out?.key) throw new Error("key mint returned no key");
|
|
6448
6517
|
return out.key;
|
|
6449
6518
|
}
|
|
6519
|
+
async function fetchAcceptedMethods(project) {
|
|
6520
|
+
try {
|
|
6521
|
+
const listing = await admin({
|
|
6522
|
+
method: "GET",
|
|
6523
|
+
path: `/projects?team_id=${encodeURIComponent(project.teamId)}`,
|
|
6524
|
+
summary: `Read the access policy for ${project.projectName}`
|
|
6525
|
+
});
|
|
6526
|
+
const rows = listing?.projects ?? [];
|
|
6527
|
+
const row = rows.find((r) => r.project_id === project.projectId && r.api_version === project.apiVersion) ?? rows.find((r) => r.project_id === project.projectId);
|
|
6528
|
+
const policy = row?.config?.requests_policy;
|
|
6529
|
+
if (!policy || policy.mode !== "authenticate") return null;
|
|
6530
|
+
return Array.isArray(policy.methods) && policy.methods.length ? policy.methods : null;
|
|
6531
|
+
} catch {
|
|
6532
|
+
return null;
|
|
6533
|
+
}
|
|
6534
|
+
}
|
|
6450
6535
|
async function openServerProxy(project) {
|
|
6451
6536
|
const version2 = project.apiVersion || "1.0.0";
|
|
6452
6537
|
const environment = "prod";
|
|
6453
6538
|
const mcpHost = `${project.projectId}.mcp.abz.run`;
|
|
6454
6539
|
const tenant2 = project.tenant || project.projectId;
|
|
6455
6540
|
const me = loadCredentials()?.apiblazeUserId;
|
|
6456
|
-
const prior = loadApichats().find((a) => a.projectId === project.projectId
|
|
6541
|
+
const prior = loadApichats().find((a) => a.projectId === project.projectId);
|
|
6542
|
+
const acceptedMethods = await fetchAcceptedMethods(project);
|
|
6543
|
+
const acceptsApiKey = acceptedMethods === null || acceptedMethods.includes("api_key");
|
|
6457
6544
|
let dpKey = prior?.dpKey;
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6545
|
+
let consumerAuth = false;
|
|
6546
|
+
if (acceptsApiKey) {
|
|
6547
|
+
if (!dpKey) {
|
|
6548
|
+
console.log(import_chalk39.default.dim(` Minting an API key for tenant ${import_chalk39.default.bold(tenant2)} to query project ${import_chalk39.default.bold(project.projectName)}\u2026`));
|
|
6549
|
+
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
6550
|
+
console.log(` ${import_chalk39.default.green("\u2714")} API key: ${import_chalk39.default.dim(maskKey(dpKey))}`);
|
|
6551
|
+
}
|
|
6552
|
+
} else {
|
|
6553
|
+
consumerAuth = true;
|
|
6554
|
+
dpKey = void 0;
|
|
6555
|
+
await ensureConsumerLogin(project.teamId, tenant2);
|
|
6462
6556
|
}
|
|
6463
6557
|
const p = {
|
|
6464
6558
|
projectId: project.projectId,
|
|
6465
6559
|
version: version2,
|
|
6466
6560
|
environment,
|
|
6467
6561
|
dpKey,
|
|
6562
|
+
consumerAuth,
|
|
6468
6563
|
mcpHost,
|
|
6469
6564
|
proxyUrl: `https://${project.projectId}.abz.run/${version2}/${environment}`,
|
|
6470
6565
|
anon: false,
|
|
@@ -6497,6 +6592,7 @@ async function openServerProxy(project) {
|
|
|
6497
6592
|
environment,
|
|
6498
6593
|
mcpHost,
|
|
6499
6594
|
dpKey,
|
|
6595
|
+
consumerAuth,
|
|
6500
6596
|
anon: false,
|
|
6501
6597
|
ownerUserId: me,
|
|
6502
6598
|
createdAt: prior?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6579,6 +6675,9 @@ async function noArgsMenu(opts) {
|
|
|
6579
6675
|
version: a.version,
|
|
6580
6676
|
environment: a.environment,
|
|
6581
6677
|
dpKey: a.dpKey,
|
|
6678
|
+
// Carry the door forward — resuming an OAuth-only chat must not fall back to
|
|
6679
|
+
// looking for a key that was never minted.
|
|
6680
|
+
consumerAuth: a.consumerAuth,
|
|
6582
6681
|
mcpHost: a.mcpHost,
|
|
6583
6682
|
proxyUrl: void 0,
|
|
6584
6683
|
anon: a.anon,
|
|
@@ -6721,6 +6820,7 @@ async function runApichat(opts) {
|
|
|
6721
6820
|
environment: p.environment,
|
|
6722
6821
|
mcpHost: p.mcpHost,
|
|
6723
6822
|
dpKey: p.dpKey,
|
|
6823
|
+
consumerAuth: p.consumerAuth,
|
|
6724
6824
|
anon: p.anon,
|
|
6725
6825
|
ownerUserId: loadCredentials()?.apiblazeUserId,
|
|
6726
6826
|
// undefined while anon
|