apiblaze 0.17.12 → 0.17.15
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 +630 -509
- 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(path8, options = {}) {
|
|
141
141
|
const token = getAccessToken();
|
|
142
|
-
const url = `${DASHBOARD_BASE}${
|
|
142
|
+
const url = `${DASHBOARD_BASE}${path8}`;
|
|
143
143
|
const res = await fetch(url, {
|
|
144
144
|
...options,
|
|
145
145
|
headers: {
|
|
@@ -165,12 +165,12 @@ async function apiFetch(path7, options = {}) {
|
|
|
165
165
|
}
|
|
166
166
|
return res.json();
|
|
167
167
|
}
|
|
168
|
-
async function agentCall(
|
|
168
|
+
async function agentCall(path8, 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: path8, method, body })
|
|
174
174
|
});
|
|
175
175
|
let data = null;
|
|
176
176
|
try {
|
|
@@ -389,32 +389,32 @@ function maskBody(body) {
|
|
|
389
389
|
}
|
|
390
390
|
function renderTrace() {
|
|
391
391
|
if (!verbose || entries.length === 0) return;
|
|
392
|
-
console.log(
|
|
393
|
-
console.log(
|
|
392
|
+
console.log(import_chalk12.default.dim("\n" + "\u2500".repeat(64)));
|
|
393
|
+
console.log(import_chalk12.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
|
|
394
394
|
console.log(
|
|
395
|
-
|
|
395
|
+
import_chalk12.default.dim("The same thing on the official API \u2014 copy/paste with your control-plane key\n(get one from the Developers section of dashboard.apiblaze.com, then\n`export APIBLAZE_CONTROLPLANE_APIKEY=sk_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
|
|
396
396
|
);
|
|
397
397
|
entries.forEach((e, i) => {
|
|
398
|
-
const n = entries.length > 1 ?
|
|
399
|
-
if (e.summary) console.log(`${n}${
|
|
398
|
+
const n = entries.length > 1 ? import_chalk12.default.bold(`${i + 1}. `) : "";
|
|
399
|
+
if (e.summary) console.log(`${n}${import_chalk12.default.cyan(e.summary)}${e.status ? import_chalk12.default.dim(` (HTTP ${e.status})`) : ""}`);
|
|
400
400
|
const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
|
|
401
401
|
const masked = maskBody(e.body);
|
|
402
402
|
const hasBody = e.method !== "GET" && masked !== void 0;
|
|
403
|
-
console.log(
|
|
404
|
-
console.log(
|
|
403
|
+
console.log(import_chalk12.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
|
|
404
|
+
console.log(import_chalk12.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
|
|
405
405
|
if (hasBody) {
|
|
406
|
-
console.log(
|
|
407
|
-
console.log(
|
|
406
|
+
console.log(import_chalk12.default.green(" -H 'Content-Type: application/json' \\"));
|
|
407
|
+
console.log(import_chalk12.default.green(` -d '${masked}'`));
|
|
408
408
|
}
|
|
409
409
|
if (i < entries.length - 1) console.log();
|
|
410
410
|
});
|
|
411
411
|
entries.length = 0;
|
|
412
412
|
}
|
|
413
|
-
var
|
|
413
|
+
var import_chalk12, CONTROL_API_VERSION, verbose, entries, SECRET_KEY;
|
|
414
414
|
var init_trace = __esm({
|
|
415
415
|
"src/lib/trace.ts"() {
|
|
416
416
|
"use strict";
|
|
417
|
-
|
|
417
|
+
import_chalk12 = __toESM(require("chalk"));
|
|
418
418
|
CONTROL_API_VERSION = "1.0.0";
|
|
419
419
|
verbose = false;
|
|
420
420
|
entries = [];
|
|
@@ -455,13 +455,13 @@ function formatBilling(billing) {
|
|
|
455
455
|
}
|
|
456
456
|
function maybePrintBilling(data) {
|
|
457
457
|
const line = formatBilling(data?.billing);
|
|
458
|
-
if (line) console.log(
|
|
458
|
+
if (line) console.log(import_chalk13.default.magenta(` ${line}`));
|
|
459
459
|
}
|
|
460
|
-
var
|
|
460
|
+
var import_chalk13, DASHBOARD_BASE3;
|
|
461
461
|
var init_admin = __esm({
|
|
462
462
|
"src/lib/admin.ts"() {
|
|
463
463
|
"use strict";
|
|
464
|
-
|
|
464
|
+
import_chalk13 = __toESM(require("chalk"));
|
|
465
465
|
init_auth();
|
|
466
466
|
init_trace();
|
|
467
467
|
init_types();
|
|
@@ -481,7 +481,7 @@ async function createTenantRow(teamId) {
|
|
|
481
481
|
const { name } = await inquirer3.prompt([{
|
|
482
482
|
type: "input",
|
|
483
483
|
name: "name",
|
|
484
|
-
message: `Tenant name ${
|
|
484
|
+
message: `Tenant name ${import_chalk23.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
|
|
485
485
|
validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
|
|
486
486
|
}]);
|
|
487
487
|
const slug = name.trim();
|
|
@@ -496,7 +496,7 @@ async function createTenantRow(teamId) {
|
|
|
496
496
|
} catch (err) {
|
|
497
497
|
const msg = err instanceof Error ? err.message : String(err);
|
|
498
498
|
if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
|
|
499
|
-
console.log(
|
|
499
|
+
console.log(import_chalk23.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
|
|
500
500
|
continue;
|
|
501
501
|
}
|
|
502
502
|
throw err;
|
|
@@ -513,11 +513,11 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
513
513
|
pageSize: 10,
|
|
514
514
|
choices: [
|
|
515
515
|
...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
|
|
516
|
-
...opts.allowSkip ? [new inquirer3.Separator(), { name:
|
|
516
|
+
...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk23.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
|
|
517
517
|
]
|
|
518
518
|
}]);
|
|
519
519
|
if (!provider) {
|
|
520
|
-
console.log(
|
|
520
|
+
console.log(import_chalk23.default.dim(" No login method yet \u2014 add one later under Login methods."));
|
|
521
521
|
return false;
|
|
522
522
|
}
|
|
523
523
|
const opt = PROVIDERS.find((p) => p.id === provider);
|
|
@@ -563,26 +563,26 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
563
563
|
async function createTenantInteractive(teamId) {
|
|
564
564
|
const { default: inquirer3 } = await import("inquirer");
|
|
565
565
|
const tenant2 = await createTenantRow(teamId);
|
|
566
|
-
console.log(
|
|
566
|
+
console.log(import_chalk23.default.green(` Tenant ${import_chalk23.default.bold(tenant2)} created.`));
|
|
567
567
|
const { users } = await inquirer3.prompt([{
|
|
568
568
|
type: "confirm",
|
|
569
569
|
name: "users",
|
|
570
570
|
default: false,
|
|
571
|
-
message: `Enable Users & groups? ${
|
|
571
|
+
message: `Enable Users & groups? ${import_chalk23.default.dim(`(identity/key management at ${tenant2}.iam.apiblaze.com)`)}`
|
|
572
572
|
}]);
|
|
573
573
|
if (users) {
|
|
574
574
|
await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
|
|
575
575
|
});
|
|
576
|
-
console.log(
|
|
576
|
+
console.log(import_chalk23.default.green(" Users & groups enabled."));
|
|
577
577
|
}
|
|
578
578
|
await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
|
|
579
579
|
return tenant2;
|
|
580
580
|
}
|
|
581
|
-
var
|
|
581
|
+
var import_chalk23, import_ora8, PROVIDERS, DEFAULT_SCOPES;
|
|
582
582
|
var init_tenant_create = __esm({
|
|
583
583
|
"src/lib/tenant-create.ts"() {
|
|
584
584
|
"use strict";
|
|
585
|
-
|
|
585
|
+
import_chalk23 = __toESM(require("chalk"));
|
|
586
586
|
import_ora8 = __toESM(require("ora"));
|
|
587
587
|
init_admin();
|
|
588
588
|
PROVIDERS = [
|
|
@@ -621,10 +621,10 @@ async function fetchPage(teamId, q) {
|
|
|
621
621
|
}
|
|
622
622
|
function label(t, defaultTenant, active) {
|
|
623
623
|
const tags = [
|
|
624
|
-
t.tenant_name === active ?
|
|
625
|
-
t.tenant_name === defaultTenant ?
|
|
624
|
+
t.tenant_name === active ? import_chalk24.default.cyan("active scope") : "",
|
|
625
|
+
t.tenant_name === defaultTenant ? import_chalk24.default.dim("team default") : ""
|
|
626
626
|
].filter(Boolean).join(", ");
|
|
627
|
-
const disp = t.display_name && t.display_name !== t.tenant_name ?
|
|
627
|
+
const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk24.default.dim(` ${t.display_name}`) : "";
|
|
628
628
|
return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
|
|
629
629
|
}
|
|
630
630
|
async function pickTenant(teamId, opts = {}) {
|
|
@@ -639,7 +639,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
639
639
|
const { make } = await inquirer3.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
|
|
640
640
|
if (make) return await createTenantInline(teamId);
|
|
641
641
|
}
|
|
642
|
-
console.error(
|
|
642
|
+
console.error(import_chalk24.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
643
643
|
return null;
|
|
644
644
|
}
|
|
645
645
|
const truncated = page.total > page.rows.length;
|
|
@@ -648,7 +648,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
648
648
|
value: t.tenant_name
|
|
649
649
|
}));
|
|
650
650
|
if (truncated || q) {
|
|
651
|
-
choices.push(new inquirer3.Separator(
|
|
651
|
+
choices.push(new inquirer3.Separator(import_chalk24.default.dim(
|
|
652
652
|
truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
|
|
653
653
|
)));
|
|
654
654
|
choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
|
|
@@ -682,11 +682,11 @@ async function createTenantInline(teamId) {
|
|
|
682
682
|
const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
683
683
|
return createTenantInteractive2(teamId);
|
|
684
684
|
}
|
|
685
|
-
var
|
|
685
|
+
var import_chalk24, import_ora9, PAGE;
|
|
686
686
|
var init_tenant_pick = __esm({
|
|
687
687
|
"src/lib/tenant-pick.ts"() {
|
|
688
688
|
"use strict";
|
|
689
|
-
|
|
689
|
+
import_chalk24 = __toESM(require("chalk"));
|
|
690
690
|
import_ora9 = __toESM(require("ora"));
|
|
691
691
|
init_admin();
|
|
692
692
|
init_auth();
|
|
@@ -696,10 +696,10 @@ var init_tenant_pick = __esm({
|
|
|
696
696
|
|
|
697
697
|
// src/index.ts
|
|
698
698
|
var import_commander = require("commander");
|
|
699
|
-
var
|
|
699
|
+
var import_chalk38 = __toESM(require("chalk"));
|
|
700
700
|
|
|
701
701
|
// package.json
|
|
702
|
-
var version = "0.17.
|
|
702
|
+
var version = "0.17.15";
|
|
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(path8) {
|
|
953
|
+
const q = path8.indexOf("?");
|
|
954
|
+
if (q < 0) return path8;
|
|
955
|
+
const base = path8.slice(0, q);
|
|
956
|
+
const query = path8.slice(q + 1);
|
|
957
957
|
const masked = query.split("&").map((pair) => {
|
|
958
958
|
const eq = pair.indexOf("=");
|
|
959
959
|
if (eq < 0) return pair;
|
|
@@ -2185,8 +2185,48 @@ async function runLogout(opts = {}) {
|
|
|
2185
2185
|
}
|
|
2186
2186
|
}
|
|
2187
2187
|
|
|
2188
|
-
// src/commands/
|
|
2188
|
+
// src/commands/flush.ts
|
|
2189
2189
|
var import_chalk9 = __toESM(require("chalk"));
|
|
2190
|
+
var fs6 = __toESM(require("fs"));
|
|
2191
|
+
var path4 = __toESM(require("path"));
|
|
2192
|
+
init_auth();
|
|
2193
|
+
async function runFlush(opts) {
|
|
2194
|
+
const dir = getApiblazeDir();
|
|
2195
|
+
const entries2 = fs6.existsSync(dir) ? fs6.readdirSync(dir) : [];
|
|
2196
|
+
if (!entries2.length) {
|
|
2197
|
+
console.log(import_chalk9.default.dim("Nothing to flush \u2014 no local apiblaze config found."));
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
console.log(import_chalk9.default.yellow("This removes ALL local apiblaze state from this machine:"));
|
|
2201
|
+
console.log(import_chalk9.default.dim(" \u2022 login credentials & anonymous-workspace keys"));
|
|
2202
|
+
console.log(import_chalk9.default.dim(" \u2022 saved apichats \u2014 their DP API keys and transcripts"));
|
|
2203
|
+
console.log(import_chalk9.default.dim(" \u2022 your BYO LLM key and any cached secrets"));
|
|
2204
|
+
console.log(import_chalk9.default.dim(` (${entries2.length} item(s) in ${dir})`));
|
|
2205
|
+
console.log(import_chalk9.default.dim(" It does NOT delete server-side proxies/keys \u2014 those GC or are revoked separately."));
|
|
2206
|
+
if (!opts.yes) {
|
|
2207
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
2208
|
+
const { ok } = await inquirer3.prompt([
|
|
2209
|
+
{ type: "confirm", name: "ok", message: "Delete all local state?", default: false }
|
|
2210
|
+
]);
|
|
2211
|
+
if (!ok) {
|
|
2212
|
+
console.log(import_chalk9.default.dim("Cancelled \u2014 nothing removed."));
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
let removed = 0;
|
|
2217
|
+
for (const e of entries2) {
|
|
2218
|
+
try {
|
|
2219
|
+
fs6.rmSync(path4.join(dir, e), { recursive: true, force: true });
|
|
2220
|
+
removed++;
|
|
2221
|
+
} catch (err) {
|
|
2222
|
+
console.log(import_chalk9.default.red(` \u2717 could not remove ${e}: ${err instanceof Error ? err.message : "error"}`));
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
console.log(`${import_chalk9.default.green("\u2714")} Flushed \u2014 removed ${removed} item(s). You're fully logged out and clean.`);
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
// src/commands/whoami.ts
|
|
2229
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
2190
2230
|
init_auth();
|
|
2191
2231
|
init_anon_cred();
|
|
2192
2232
|
async function runWhoami(opts = {}) {
|
|
@@ -2209,43 +2249,43 @@ async function runWhoami(opts = {}) {
|
|
|
2209
2249
|
}, null, 2));
|
|
2210
2250
|
return;
|
|
2211
2251
|
}
|
|
2212
|
-
console.log(
|
|
2252
|
+
console.log(import_chalk10.default.bold("API Producer"));
|
|
2213
2253
|
if (!creds) {
|
|
2214
|
-
console.log(
|
|
2254
|
+
console.log(import_chalk10.default.dim(" Not logged in. Run `apiblaze login`."));
|
|
2215
2255
|
} else {
|
|
2216
2256
|
const who = creds.githubHandle ?? creds.email ?? creds.apiblazeUserId ?? "unknown";
|
|
2217
|
-
console.log(` ${
|
|
2257
|
+
console.log(` ${import_chalk10.default.cyan("Signed in as")} ${import_chalk10.default.bold(who)}`);
|
|
2218
2258
|
if (creds.email && creds.email !== who) console.log(` Email: ${creds.email}`);
|
|
2219
|
-
if (creds.teamName || creds.teamId) console.log(` Team: ${
|
|
2220
|
-
if (creds.activeTenant) console.log(` Tenant scope: ${
|
|
2221
|
-
if (Date.now() >= creds.expiresAt) console.log(
|
|
2259
|
+
if (creds.teamName || creds.teamId) console.log(` Team: ${import_chalk10.default.bold(creds.teamName ?? creds.teamId)}`);
|
|
2260
|
+
if (creds.activeTenant) console.log(` Tenant scope: ${import_chalk10.default.bold(creds.activeTenant)} ${import_chalk10.default.dim("(clear: `apiblaze tenant use --clear`)")}`);
|
|
2261
|
+
if (Date.now() >= creds.expiresAt) console.log(import_chalk10.default.yellow(" \u26A0 Session expired \u2014 run `apiblaze login`."));
|
|
2222
2262
|
}
|
|
2223
2263
|
if (anon) {
|
|
2224
2264
|
const masked = `${anon.cp_key.slice(0, 11)}\u2026${anon.cp_key.slice(-4)}`;
|
|
2225
|
-
console.log(
|
|
2226
|
-
console.log(` Team: ${
|
|
2227
|
-
console.log(` CP key: ${
|
|
2265
|
+
console.log(import_chalk10.default.bold("\nAnonymous workspace") + import_chalk10.default.dim(" (created without an account \u2014 unclaimed)"));
|
|
2266
|
+
console.log(` Team: ${import_chalk10.default.bold(anon.team_id)}`);
|
|
2267
|
+
console.log(` CP key: ${import_chalk10.default.bold(masked)} ${import_chalk10.default.dim("controls the proxies you created anonymously")}`);
|
|
2228
2268
|
console.log(
|
|
2229
|
-
|
|
2269
|
+
import_chalk10.default.dim(` Keep it: ${import_chalk10.default.cyan("apiblaze login")} then ${import_chalk10.default.cyan("apiblaze claim")}`) + (anon.claim_code ? import_chalk10.default.dim(` \xB7 from another machine: ${import_chalk10.default.cyan(`apiblaze claim ${anon.claim_code}`)}`) : "")
|
|
2230
2270
|
);
|
|
2231
|
-
if (creds) console.log(
|
|
2271
|
+
if (creds) console.log(import_chalk10.default.dim(" (you're logged in \u2014 `apiblaze claim` folds this workspace into your account)"));
|
|
2232
2272
|
}
|
|
2233
|
-
console.log(
|
|
2273
|
+
console.log(import_chalk10.default.bold("\nAPI Consumer"));
|
|
2234
2274
|
if (!consumer2) {
|
|
2235
|
-
console.log(
|
|
2275
|
+
console.log(import_chalk10.default.dim(" Not logged in. Run `apiblaze consumer login`."));
|
|
2236
2276
|
} else {
|
|
2237
|
-
console.log(` ${
|
|
2238
|
-
console.log(` Tenant: ${
|
|
2239
|
-
if (Date.now() >= consumer2.expiresAt) console.log(
|
|
2277
|
+
console.log(` ${import_chalk10.default.cyan("Signed in as")} ${import_chalk10.default.bold(consumer2.email ?? consumer2.tenant)}`);
|
|
2278
|
+
console.log(` Tenant: ${import_chalk10.default.bold(consumer2.tenant)}`);
|
|
2279
|
+
if (Date.now() >= consumer2.expiresAt) console.log(import_chalk10.default.yellow(" \u26A0 Token expired \u2014 it will refresh on next use, or re-run `apiblaze consumer login`."));
|
|
2240
2280
|
}
|
|
2241
2281
|
}
|
|
2242
2282
|
|
|
2243
2283
|
// src/commands/team.ts
|
|
2244
|
-
var
|
|
2284
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
2245
2285
|
init_auth();
|
|
2246
2286
|
init_api();
|
|
2247
2287
|
function fail3(message) {
|
|
2248
|
-
console.error(
|
|
2288
|
+
console.error(import_chalk11.default.red(`Error: ${message}`));
|
|
2249
2289
|
process.exit(1);
|
|
2250
2290
|
}
|
|
2251
2291
|
async function runTeam(arg) {
|
|
@@ -2267,7 +2307,7 @@ async function runTeam(arg) {
|
|
|
2267
2307
|
}
|
|
2268
2308
|
} else if (teams.length === 1) {
|
|
2269
2309
|
chosen = teams[0];
|
|
2270
|
-
console.log(`${
|
|
2310
|
+
console.log(`${import_chalk11.default.cyan("\u2192")} You only have one team: ${import_chalk11.default.bold(chosen.name)}`);
|
|
2271
2311
|
} else if (process.stdin.isTTY) {
|
|
2272
2312
|
const { default: inquirer3 } = await import("inquirer");
|
|
2273
2313
|
const { picked } = await inquirer3.prompt([{
|
|
@@ -2282,47 +2322,47 @@ async function runTeam(arg) {
|
|
|
2282
2322
|
fail3(`Specify a team: \`apiblaze team <name|id>\`. Available: ${teams.map((t) => t.name).join(", ")}.`);
|
|
2283
2323
|
}
|
|
2284
2324
|
saveCredentials({ ...creds, teamId: chosen.teamId, teamName: chosen.name });
|
|
2285
|
-
console.log(
|
|
2286
|
-
\u2714 Active team: ${
|
|
2325
|
+
console.log(import_chalk11.default.green(`
|
|
2326
|
+
\u2714 Active team: ${import_chalk11.default.bold(chosen.name)}`));
|
|
2287
2327
|
}
|
|
2288
2328
|
|
|
2289
2329
|
// src/commands/authz.ts
|
|
2290
|
-
var
|
|
2330
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
2291
2331
|
init_auth();
|
|
2292
2332
|
init_api();
|
|
2293
2333
|
|
|
2294
2334
|
// src/lib/agent-chat.ts
|
|
2295
2335
|
var import_readline = __toESM(require("readline"));
|
|
2296
|
-
var
|
|
2336
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
2297
2337
|
init_api();
|
|
2298
2338
|
init_admin();
|
|
2299
2339
|
async function runAgentChatRepl(opts) {
|
|
2300
2340
|
const { title, subtitle, endpoint, buildBody, seedPrompt, summarizeProposal, commands } = opts;
|
|
2301
|
-
console.log("\n" +
|
|
2302
|
-
if (subtitle) console.log(
|
|
2341
|
+
console.log("\n" + import_chalk14.default.cyan.bold(title));
|
|
2342
|
+
if (subtitle) console.log(import_chalk14.default.dim(subtitle));
|
|
2303
2343
|
const messages = [];
|
|
2304
2344
|
const ctx = { proposal: null, lastData: null, log: (s) => console.log(s) };
|
|
2305
2345
|
const cmdByName = new Map(commands.map((c) => [c.name, c]));
|
|
2306
2346
|
const footer = () => {
|
|
2307
2347
|
const parts = [
|
|
2308
|
-
|
|
2309
|
-
...commands.map((c) =>
|
|
2310
|
-
|
|
2311
|
-
|
|
2348
|
+
import_chalk14.default.dim("type to chat/refine"),
|
|
2349
|
+
...commands.map((c) => import_chalk14.default.dim(`/${c.name} ${c.describe}`)),
|
|
2350
|
+
import_chalk14.default.dim("/show"),
|
|
2351
|
+
import_chalk14.default.dim("/drop discard & exit")
|
|
2312
2352
|
];
|
|
2313
|
-
return " " +
|
|
2353
|
+
return " " + import_chalk14.default.dim("[ ") + parts.join(import_chalk14.default.dim(" \xB7 ")) + import_chalk14.default.dim(" ]");
|
|
2314
2354
|
};
|
|
2315
2355
|
async function turn(content) {
|
|
2316
2356
|
messages.push({ role: "user", content });
|
|
2317
|
-
process.stdout.write(
|
|
2357
|
+
process.stdout.write(import_chalk14.default.dim(" \u2026thinking\n"));
|
|
2318
2358
|
const { status, data } = await agentCall(endpoint, "POST", { messages, ...buildBody(messages) });
|
|
2319
2359
|
if (status === 402) {
|
|
2320
|
-
console.log(
|
|
2360
|
+
console.log(import_chalk14.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
|
|
2321
2361
|
messages.pop();
|
|
2322
2362
|
return;
|
|
2323
2363
|
}
|
|
2324
2364
|
if (status >= 400 || !data) {
|
|
2325
|
-
console.log(
|
|
2365
|
+
console.log(import_chalk14.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
|
|
2326
2366
|
`));
|
|
2327
2367
|
messages.pop();
|
|
2328
2368
|
return;
|
|
@@ -2330,19 +2370,19 @@ async function runAgentChatRepl(opts) {
|
|
|
2330
2370
|
ctx.lastData = data;
|
|
2331
2371
|
const reply = data.reply ?? data.message ?? "(no reply)";
|
|
2332
2372
|
messages.push({ role: "assistant", content: reply });
|
|
2333
|
-
console.log("\n" +
|
|
2373
|
+
console.log("\n" + import_chalk14.default.cyan("agent \u203A ") + reply + "\n");
|
|
2334
2374
|
if (data.proposal) {
|
|
2335
2375
|
ctx.proposal = data.proposal;
|
|
2336
2376
|
const summary = summarizeProposal?.(data);
|
|
2337
|
-
if (summary) console.log(
|
|
2377
|
+
if (summary) console.log(import_chalk14.default.yellow(" " + summary));
|
|
2338
2378
|
const ready = commands.filter((c) => c.needsProposal).map((c) => `/${c.name}`).join(" or ");
|
|
2339
|
-
if (ready) console.log(
|
|
2379
|
+
if (ready) console.log(import_chalk14.default.dim(` Ready \u2014 ${ready} to make it official, or keep refining.`));
|
|
2340
2380
|
console.log();
|
|
2341
2381
|
}
|
|
2342
2382
|
const line = formatBilling(data.billing);
|
|
2343
2383
|
if (line) {
|
|
2344
2384
|
const llm2 = data.llm;
|
|
2345
|
-
console.log(
|
|
2385
|
+
console.log(import_chalk14.default.dim(` ${line}${llm2?.model ? ` \xB7 ${llm2.model}` : ""}`));
|
|
2346
2386
|
}
|
|
2347
2387
|
}
|
|
2348
2388
|
const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -2350,26 +2390,26 @@ async function runAgentChatRepl(opts) {
|
|
|
2350
2390
|
await turn(seedPrompt);
|
|
2351
2391
|
for (; ; ) {
|
|
2352
2392
|
console.log(footer());
|
|
2353
|
-
const line = (await ask(
|
|
2393
|
+
const line = (await ask(import_chalk14.default.green("you \u203A "))).trim();
|
|
2354
2394
|
if (!line) continue;
|
|
2355
2395
|
if (line === "/drop" || line === "/exit" || line === "/quit") {
|
|
2356
|
-
console.log(
|
|
2396
|
+
console.log(import_chalk14.default.dim(" Dropped \u2014 nothing applied.\n"));
|
|
2357
2397
|
break;
|
|
2358
2398
|
}
|
|
2359
2399
|
if (line === "/show") {
|
|
2360
|
-
if (!ctx.proposal) console.log(
|
|
2400
|
+
if (!ctx.proposal) console.log(import_chalk14.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
|
|
2361
2401
|
else console.log("\n" + JSON.stringify(ctx.proposal, null, 2) + "\n");
|
|
2362
2402
|
continue;
|
|
2363
2403
|
}
|
|
2364
2404
|
if (line.startsWith("/")) {
|
|
2365
2405
|
const cmd = cmdByName.get(line.slice(1));
|
|
2366
2406
|
if (!cmd) {
|
|
2367
|
-
console.log(
|
|
2407
|
+
console.log(import_chalk14.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
|
|
2368
2408
|
`));
|
|
2369
2409
|
continue;
|
|
2370
2410
|
}
|
|
2371
2411
|
if (cmd.needsProposal && !ctx.proposal) {
|
|
2372
|
-
console.log(
|
|
2412
|
+
console.log(import_chalk14.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
|
|
2373
2413
|
continue;
|
|
2374
2414
|
}
|
|
2375
2415
|
await cmd.run(ctx);
|
|
@@ -2398,11 +2438,11 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
2398
2438
|
if (!enable) {
|
|
2399
2439
|
const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant2)}`, "POST", proposal.model);
|
|
2400
2440
|
if (m.status === 404) {
|
|
2401
|
-
ctx.log(
|
|
2441
|
+
ctx.log(import_chalk15.default.red(" Authorization store not provisioned yet. Open the dashboard Authorization tab for this project once (it auto-provisions the store), then re-run.\n"));
|
|
2402
2442
|
return;
|
|
2403
2443
|
}
|
|
2404
2444
|
if (m.status >= 400) {
|
|
2405
|
-
ctx.log(
|
|
2445
|
+
ctx.log(import_chalk15.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
|
|
2406
2446
|
`));
|
|
2407
2447
|
return;
|
|
2408
2448
|
}
|
|
@@ -2431,14 +2471,14 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
2431
2471
|
if (enable) {
|
|
2432
2472
|
const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant: tenant2 });
|
|
2433
2473
|
if (e.status >= 400) {
|
|
2434
|
-
ctx.log(
|
|
2474
|
+
ctx.log(import_chalk15.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
|
|
2435
2475
|
`));
|
|
2436
2476
|
return;
|
|
2437
2477
|
}
|
|
2438
|
-
ctx.log(
|
|
2478
|
+
ctx.log(import_chalk15.default.green(` \u2713 Enforcement ON \u2014 ${ok} route(s) + project switch on${fail5 ? `, ${fail5} failed` : ""}.
|
|
2439
2479
|
`));
|
|
2440
2480
|
} else {
|
|
2441
|
-
ctx.log(
|
|
2481
|
+
ctx.log(import_chalk15.default.green(` \u2713 Published (shadow): model + ${ok} route(s)${fail5 ? `, ${fail5} failed` : ""}. Review, then /enable.
|
|
2442
2482
|
`));
|
|
2443
2483
|
}
|
|
2444
2484
|
}
|
|
@@ -2463,7 +2503,7 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
2463
2503
|
}
|
|
2464
2504
|
|
|
2465
2505
|
// src/commands/openapi.ts
|
|
2466
|
-
var
|
|
2506
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
2467
2507
|
init_auth();
|
|
2468
2508
|
init_api();
|
|
2469
2509
|
async function runOpenapi(projectArg, apiVersionArg) {
|
|
@@ -2474,10 +2514,10 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
2474
2514
|
if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
|
|
2475
2515
|
const projectId = match.projectId;
|
|
2476
2516
|
const apiVersion = apiVersionArg || match.apiVersion;
|
|
2477
|
-
console.log(
|
|
2517
|
+
console.log(import_chalk16.default.dim("Gathering captured traffic samples\u2026"));
|
|
2478
2518
|
const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
|
|
2479
2519
|
if (routesRes.status >= 400) {
|
|
2480
|
-
console.log(
|
|
2520
|
+
console.log(import_chalk16.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
|
|
2481
2521
|
return;
|
|
2482
2522
|
}
|
|
2483
2523
|
const routes = routesRes.data?.routes ?? (Array.isArray(routesRes.data) ? routesRes.data : []);
|
|
@@ -2490,25 +2530,25 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
2490
2530
|
}
|
|
2491
2531
|
const sampleIds = [...ids];
|
|
2492
2532
|
if (sampleIds.length === 0) {
|
|
2493
|
-
console.log(
|
|
2533
|
+
console.log(import_chalk16.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
|
|
2494
2534
|
return;
|
|
2495
2535
|
}
|
|
2496
2536
|
async function publish(ctx) {
|
|
2497
2537
|
const patch = ctx.proposal.patch;
|
|
2498
2538
|
if (!Array.isArray(patch) || patch.length === 0) {
|
|
2499
|
-
ctx.log(
|
|
2539
|
+
ctx.log(import_chalk16.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
|
|
2500
2540
|
return;
|
|
2501
2541
|
}
|
|
2502
2542
|
const specSource = ctx.lastData?.spec_source ?? "unknown";
|
|
2503
2543
|
const endpoint = specSource === "github" ? "open-pr" : "publish-openapi";
|
|
2504
2544
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/samples/${endpoint}`, "POST", { patch, sample_ids_used: sampleIds });
|
|
2505
2545
|
if (pub.status >= 400) {
|
|
2506
|
-
ctx.log(
|
|
2546
|
+
ctx.log(import_chalk16.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
2507
2547
|
`));
|
|
2508
2548
|
return;
|
|
2509
2549
|
}
|
|
2510
2550
|
const prUrl = pub.data?.pr_url;
|
|
2511
|
-
ctx.log(
|
|
2551
|
+
ctx.log(import_chalk16.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
|
|
2512
2552
|
` : " \u2713 Published updated OpenAPI spec.\n"));
|
|
2513
2553
|
}
|
|
2514
2554
|
await runAgentChatRepl({
|
|
@@ -2529,7 +2569,7 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
2529
2569
|
}
|
|
2530
2570
|
|
|
2531
2571
|
// src/commands/mcp.ts
|
|
2532
|
-
var
|
|
2572
|
+
var import_chalk17 = __toESM(require("chalk"));
|
|
2533
2573
|
init_auth();
|
|
2534
2574
|
init_api();
|
|
2535
2575
|
async function runMcp(projectArg, apiVersionArg, opts) {
|
|
@@ -2546,11 +2586,11 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
2546
2586
|
const spec2 = ctx.proposal;
|
|
2547
2587
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec: spec2 });
|
|
2548
2588
|
if (pub.status >= 400) {
|
|
2549
|
-
ctx.log(
|
|
2589
|
+
ctx.log(import_chalk17.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
2550
2590
|
`));
|
|
2551
2591
|
return;
|
|
2552
2592
|
}
|
|
2553
|
-
ctx.log(
|
|
2593
|
+
ctx.log(import_chalk17.default.green(` \u2713 Published MCP server \u2014 ${mcpHost}.mcp.apiblaze.com/${apiVersion}/${environment}.
|
|
2554
2594
|
`));
|
|
2555
2595
|
}
|
|
2556
2596
|
await runAgentChatRepl({
|
|
@@ -2572,18 +2612,18 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
2572
2612
|
}
|
|
2573
2613
|
|
|
2574
2614
|
// src/commands/delete.ts
|
|
2575
|
-
var
|
|
2615
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
2576
2616
|
var import_ora5 = __toESM(require("ora"));
|
|
2577
2617
|
init_admin();
|
|
2578
2618
|
|
|
2579
2619
|
// src/lib/resolve.ts
|
|
2580
|
-
var
|
|
2620
|
+
var import_chalk18 = __toESM(require("chalk"));
|
|
2581
2621
|
init_auth();
|
|
2582
2622
|
init_api();
|
|
2583
2623
|
function requireAuth() {
|
|
2584
2624
|
const creds = loadCredentials();
|
|
2585
2625
|
if (!creds) {
|
|
2586
|
-
console.error(
|
|
2626
|
+
console.error(import_chalk18.default.red("Not logged in. Run `apiblaze login` first."));
|
|
2587
2627
|
process.exit(1);
|
|
2588
2628
|
}
|
|
2589
2629
|
return creds;
|
|
@@ -2592,7 +2632,7 @@ async function resolveTeam(opt) {
|
|
|
2592
2632
|
const creds = requireAuth();
|
|
2593
2633
|
if (!opt) {
|
|
2594
2634
|
if (!creds.teamId) {
|
|
2595
|
-
console.error(
|
|
2635
|
+
console.error(import_chalk18.default.red("No active team. Run `apiblaze login` or pass --team."));
|
|
2596
2636
|
process.exit(1);
|
|
2597
2637
|
}
|
|
2598
2638
|
return { teamId: creds.teamId, teamName: creds.teamName };
|
|
@@ -2601,7 +2641,7 @@ async function resolveTeam(opt) {
|
|
|
2601
2641
|
const teams = await getTeams().catch(() => []);
|
|
2602
2642
|
const match = teams.find((t) => t.name === opt || t.teamId === opt);
|
|
2603
2643
|
if (!match) {
|
|
2604
|
-
console.error(
|
|
2644
|
+
console.error(import_chalk18.default.red(`Team "${opt}" not found.${teams.length ? " Available: " + teams.map((t) => t.name).join(", ") : ""}`));
|
|
2605
2645
|
process.exit(1);
|
|
2606
2646
|
}
|
|
2607
2647
|
return { teamId: match.teamId, teamName: match.name };
|
|
@@ -2612,13 +2652,13 @@ async function resolveProject(teamId, nameOrId, version2) {
|
|
|
2612
2652
|
(p) => p.projectId === nameOrId || p.projectName === nameOrId
|
|
2613
2653
|
);
|
|
2614
2654
|
if (candidates.length === 0) {
|
|
2615
|
-
console.error(
|
|
2616
|
-
if (projects.length) console.error(
|
|
2655
|
+
console.error(import_chalk18.default.red(`Project "${nameOrId}" not found in this team.`));
|
|
2656
|
+
if (projects.length) console.error(import_chalk18.default.dim(" Known: " + projects.map((p) => p.projectName).join(", ")));
|
|
2617
2657
|
process.exit(1);
|
|
2618
2658
|
}
|
|
2619
2659
|
const chosen = version2 ? candidates.find((p) => p.apiVersion === version2) : candidates[0];
|
|
2620
2660
|
if (!chosen) {
|
|
2621
|
-
console.error(
|
|
2661
|
+
console.error(import_chalk18.default.red(`Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((p) => p.apiVersion).join(", ")}`));
|
|
2622
2662
|
process.exit(1);
|
|
2623
2663
|
}
|
|
2624
2664
|
return {
|
|
@@ -2653,10 +2693,10 @@ async function runDelete(project, version2, opts) {
|
|
|
2653
2693
|
return;
|
|
2654
2694
|
}
|
|
2655
2695
|
} else {
|
|
2656
|
-
console.log(`${
|
|
2696
|
+
console.log(`${import_chalk19.default.red.bold("Delete")} ${import_chalk19.default.bold(proj2.projectName)} ${import_chalk19.default.dim("v" + proj2.apiVersion)} ${import_chalk19.default.dim("(" + proj2.projectId + ")")}`);
|
|
2657
2697
|
if (impact && typeof impact === "object") {
|
|
2658
2698
|
const counts = impact.counts ?? impact.impact ?? impact;
|
|
2659
|
-
console.log(
|
|
2699
|
+
console.log(import_chalk19.default.dim(" This removes (cascade): ") + import_chalk19.default.yellow(JSON.stringify(counts)));
|
|
2660
2700
|
}
|
|
2661
2701
|
if (!opts.yes) {
|
|
2662
2702
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -2664,7 +2704,7 @@ async function runDelete(project, version2, opts) {
|
|
|
2664
2704
|
{ type: "confirm", name: "confirm", message: `Permanently delete ${proj2.projectName} v${proj2.apiVersion}? This cannot be undone.`, default: false }
|
|
2665
2705
|
]);
|
|
2666
2706
|
if (!confirm) {
|
|
2667
|
-
console.log(
|
|
2707
|
+
console.log(import_chalk19.default.dim("Aborted."));
|
|
2668
2708
|
return;
|
|
2669
2709
|
}
|
|
2670
2710
|
}
|
|
@@ -2685,7 +2725,7 @@ async function runDelete(project, version2, opts) {
|
|
|
2685
2725
|
}
|
|
2686
2726
|
|
|
2687
2727
|
// src/commands/export.ts
|
|
2688
|
-
var
|
|
2728
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
2689
2729
|
var import_fs3 = require("fs");
|
|
2690
2730
|
init_admin();
|
|
2691
2731
|
init_auth();
|
|
@@ -2703,7 +2743,7 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2703
2743
|
if (opts.noConsumers) q.set("include_consumers", "0");
|
|
2704
2744
|
if (opts.keys) q.set("keys", opts.keys);
|
|
2705
2745
|
const qs = q.toString() ? `?${q.toString()}` : "";
|
|
2706
|
-
console.log(
|
|
2746
|
+
console.log(import_chalk20.default.dim(`Queuing ${target} export of ${projectId}@${version2}\u2026`));
|
|
2707
2747
|
const job = await admin({
|
|
2708
2748
|
path: `/projects/${projectId}/${version2}/export/${target}${qs}`,
|
|
2709
2749
|
method: "GET",
|
|
@@ -2713,11 +2753,11 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2713
2753
|
const statusPath = `/projects/${projectId}/${version2}/export/jobs/${job.job_id}`;
|
|
2714
2754
|
let status = "queued";
|
|
2715
2755
|
const started = Date.now();
|
|
2716
|
-
process.stdout.write(
|
|
2756
|
+
process.stdout.write(import_chalk20.default.dim("Building bundle"));
|
|
2717
2757
|
while (status !== "ready" && status !== "failed") {
|
|
2718
2758
|
if (Date.now() - started > 10 * 6e4) throw new Error("Export timed out after 10 minutes.");
|
|
2719
2759
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
2720
|
-
process.stdout.write(
|
|
2760
|
+
process.stdout.write(import_chalk20.default.dim("."));
|
|
2721
2761
|
const st = await admin({ path: statusPath, method: "GET", summary: "poll export" });
|
|
2722
2762
|
status = st?.status ?? "queued";
|
|
2723
2763
|
if (status === "failed") {
|
|
@@ -2735,14 +2775,14 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2735
2775
|
if (!res.ok) throw new Error(`Download failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
2736
2776
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
2737
2777
|
(0, import_fs3.writeFileSync)(outFile, buf);
|
|
2738
|
-
console.log(
|
|
2739
|
-
if (job.decryption_key) console.log(
|
|
2740
|
-
console.log(
|
|
2741
|
-
if (target === "kong") console.log(
|
|
2778
|
+
console.log(import_chalk20.default.green(`\u2713 Saved ${outFile} (${(buf.length / 1024).toFixed(0)} KB)`));
|
|
2779
|
+
if (job.decryption_key) console.log(import_chalk20.default.yellow(`Decryption key (shown once): ${job.decryption_key}`));
|
|
2780
|
+
console.log(import_chalk20.default.dim("Read MIGRATION-REPORT.json inside the zip for the full fidelity ledger."));
|
|
2781
|
+
if (target === "kong") console.log(import_chalk20.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
|
|
2742
2782
|
}
|
|
2743
2783
|
|
|
2744
2784
|
// src/commands/config.ts
|
|
2745
|
-
var
|
|
2785
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
2746
2786
|
var import_ora6 = __toESM(require("ora"));
|
|
2747
2787
|
init_admin();
|
|
2748
2788
|
async function patchConfig(project, opts, body, summary) {
|
|
@@ -2765,7 +2805,7 @@ async function patchConfig(project, opts, body, summary) {
|
|
|
2765
2805
|
}
|
|
2766
2806
|
async function runTargetSet(project, opts) {
|
|
2767
2807
|
if (!opts.url) {
|
|
2768
|
-
console.error(
|
|
2808
|
+
console.error(import_chalk21.default.red("--url is required."));
|
|
2769
2809
|
process.exit(1);
|
|
2770
2810
|
}
|
|
2771
2811
|
const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
|
|
@@ -2778,38 +2818,38 @@ async function runThrottleSet(project, opts) {
|
|
|
2778
2818
|
if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
|
|
2779
2819
|
if (opts.period !== void 0) {
|
|
2780
2820
|
if (!["daily", "weekly", "monthly"].includes(opts.period)) {
|
|
2781
|
-
console.error(
|
|
2821
|
+
console.error(import_chalk21.default.red("--period must be daily, weekly, or monthly."));
|
|
2782
2822
|
process.exit(1);
|
|
2783
2823
|
}
|
|
2784
2824
|
throttling.quotaPeriod = opts.period;
|
|
2785
2825
|
}
|
|
2786
2826
|
if (Object.keys(throttling).length === 0) {
|
|
2787
|
-
console.error(
|
|
2827
|
+
console.error(import_chalk21.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
|
|
2788
2828
|
process.exit(1);
|
|
2789
2829
|
}
|
|
2790
2830
|
await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
|
|
2791
2831
|
}
|
|
2792
2832
|
async function runRename(project, opts) {
|
|
2793
2833
|
if (!opts.displayName) {
|
|
2794
|
-
console.error(
|
|
2834
|
+
console.error(import_chalk21.default.red("--display-name is required."));
|
|
2795
2835
|
process.exit(1);
|
|
2796
2836
|
}
|
|
2797
2837
|
await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
|
|
2798
2838
|
}
|
|
2799
2839
|
|
|
2800
2840
|
// src/commands/config-browse.ts
|
|
2801
|
-
var
|
|
2841
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
2802
2842
|
var import_ora14 = __toESM(require("ora"));
|
|
2803
2843
|
init_admin();
|
|
2804
2844
|
init_auth();
|
|
2805
2845
|
|
|
2806
2846
|
// src/commands/domain.ts
|
|
2807
|
-
var
|
|
2847
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
2808
2848
|
var import_ora7 = __toESM(require("ora"));
|
|
2809
2849
|
init_admin();
|
|
2810
2850
|
async function runDomainAdd(project, opts) {
|
|
2811
2851
|
if (!opts.domain) {
|
|
2812
|
-
console.error(
|
|
2852
|
+
console.error(import_chalk22.default.red("--domain is required."));
|
|
2813
2853
|
process.exit(1);
|
|
2814
2854
|
}
|
|
2815
2855
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -2828,9 +2868,9 @@ async function runDomainAdd(project, opts) {
|
|
|
2828
2868
|
console.log(JSON.stringify(out));
|
|
2829
2869
|
return;
|
|
2830
2870
|
}
|
|
2831
|
-
if (out?.cnameRecord) console.log(` ${
|
|
2832
|
-
if (out?.dcv) console.log(` ${
|
|
2833
|
-
if (out?.id) console.log(
|
|
2871
|
+
if (out?.cnameRecord) console.log(` ${import_chalk22.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
|
|
2872
|
+
if (out?.dcv) console.log(` ${import_chalk22.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
|
|
2873
|
+
if (out?.id) console.log(import_chalk22.default.dim(` domain id: ${out.id}`));
|
|
2834
2874
|
} catch (err) {
|
|
2835
2875
|
spinner.fail("Domain registration failed.");
|
|
2836
2876
|
throw err;
|
|
@@ -2850,14 +2890,14 @@ async function runDomainList(project, opts) {
|
|
|
2850
2890
|
return;
|
|
2851
2891
|
}
|
|
2852
2892
|
if (!domains.length) {
|
|
2853
|
-
console.log(
|
|
2893
|
+
console.log(import_chalk22.default.yellow("No custom domains."));
|
|
2854
2894
|
return;
|
|
2855
2895
|
}
|
|
2856
|
-
for (const d of domains) console.log(` ${
|
|
2896
|
+
for (const d of domains) console.log(` ${import_chalk22.default.bold(d.hostname)} ${import_chalk22.default.dim(d.status ?? "")} ${import_chalk22.default.dim(d.id ?? "")}`);
|
|
2857
2897
|
}
|
|
2858
2898
|
async function runDomainStatus(project, opts) {
|
|
2859
2899
|
if (!opts.id) {
|
|
2860
|
-
console.error(
|
|
2900
|
+
console.error(import_chalk22.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
2861
2901
|
process.exit(1);
|
|
2862
2902
|
}
|
|
2863
2903
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -2867,11 +2907,11 @@ async function runDomainStatus(project, opts) {
|
|
|
2867
2907
|
path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
|
|
2868
2908
|
summary: `Check custom-domain status`
|
|
2869
2909
|
});
|
|
2870
|
-
console.log(opts.json ? JSON.stringify(out) : ` ${
|
|
2910
|
+
console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk22.default.bold(out?.hostname ?? opts.id)}: ${import_chalk22.default.cyan(out?.status ?? "unknown")}`);
|
|
2871
2911
|
}
|
|
2872
2912
|
async function runDomainRemove(project, opts) {
|
|
2873
2913
|
if (!opts.id) {
|
|
2874
|
-
console.error(
|
|
2914
|
+
console.error(import_chalk22.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
2875
2915
|
process.exit(1);
|
|
2876
2916
|
}
|
|
2877
2917
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -2909,7 +2949,7 @@ async function runDomainSetBase(project, opts) {
|
|
|
2909
2949
|
}
|
|
2910
2950
|
|
|
2911
2951
|
// src/commands/tenant.ts
|
|
2912
|
-
var
|
|
2952
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
2913
2953
|
var import_ora10 = __toESM(require("ora"));
|
|
2914
2954
|
init_admin();
|
|
2915
2955
|
init_auth();
|
|
@@ -2917,20 +2957,20 @@ init_tenant_pick();
|
|
|
2917
2957
|
async function runTenantUse(query, opts) {
|
|
2918
2958
|
const creds = loadCredentials();
|
|
2919
2959
|
if (!creds) {
|
|
2920
|
-
console.error(
|
|
2960
|
+
console.error(import_chalk25.default.red("Not logged in. Run `apiblaze login` first."));
|
|
2921
2961
|
process.exit(1);
|
|
2922
2962
|
}
|
|
2923
2963
|
if (opts.clear) {
|
|
2924
2964
|
delete creds.activeTenant;
|
|
2925
2965
|
saveCredentials(creds);
|
|
2926
|
-
console.log(
|
|
2966
|
+
console.log(import_chalk25.default.green("Tenant scope cleared."));
|
|
2927
2967
|
return;
|
|
2928
2968
|
}
|
|
2929
2969
|
const { teamId } = await resolveTeam(opts.team);
|
|
2930
2970
|
const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
|
|
2931
2971
|
if (!slug) process.exit(1);
|
|
2932
2972
|
saveCredentials({ ...creds, activeTenant: slug });
|
|
2933
|
-
console.log(
|
|
2973
|
+
console.log(import_chalk25.default.green(`Tenant scope set to ${import_chalk25.default.bold(slug)}.`) + import_chalk25.default.dim(" (clear with `apiblaze tenant use --clear`)"));
|
|
2934
2974
|
}
|
|
2935
2975
|
async function runTenantList(opts) {
|
|
2936
2976
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -2947,22 +2987,22 @@ async function runTenantList(opts) {
|
|
|
2947
2987
|
return;
|
|
2948
2988
|
}
|
|
2949
2989
|
if (!tenants.length) {
|
|
2950
|
-
console.log(
|
|
2990
|
+
console.log(import_chalk25.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
2951
2991
|
return;
|
|
2952
2992
|
}
|
|
2953
2993
|
for (const t of tenants) {
|
|
2954
2994
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
2955
|
-
const display = typeof t === "string" ? "" :
|
|
2956
|
-
console.log(` ${
|
|
2995
|
+
const display = typeof t === "string" ? "" : import_chalk25.default.dim(` ${t.display_name ?? ""}`);
|
|
2996
|
+
console.log(` ${import_chalk25.default.bold(name)}${display}`);
|
|
2957
2997
|
}
|
|
2958
2998
|
const total = out?.total ?? tenants.length;
|
|
2959
2999
|
if (total > tenants.length) {
|
|
2960
|
-
console.log(
|
|
3000
|
+
console.log(import_chalk25.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
2961
3001
|
}
|
|
2962
3002
|
}
|
|
2963
3003
|
async function runTenantCreate(opts) {
|
|
2964
3004
|
if (!opts.name) {
|
|
2965
|
-
console.error(
|
|
3005
|
+
console.error(import_chalk25.default.red("--name (display name) is required."));
|
|
2966
3006
|
process.exit(1);
|
|
2967
3007
|
}
|
|
2968
3008
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -2974,7 +3014,7 @@ async function runTenantCreate(opts) {
|
|
|
2974
3014
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
2975
3015
|
summary: `Create tenant "${opts.name}"`
|
|
2976
3016
|
});
|
|
2977
|
-
spinner.succeed(`Created tenant ${
|
|
3017
|
+
spinner.succeed(`Created tenant ${import_chalk25.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
2978
3018
|
if (opts.json) console.log(JSON.stringify(out));
|
|
2979
3019
|
} catch (err) {
|
|
2980
3020
|
spinner.fail("Tenant create failed.");
|
|
@@ -2983,7 +3023,7 @@ async function runTenantCreate(opts) {
|
|
|
2983
3023
|
}
|
|
2984
3024
|
async function runTenantAttach(project, opts) {
|
|
2985
3025
|
if (!opts.tenant) {
|
|
2986
|
-
console.error(
|
|
3026
|
+
console.error(import_chalk25.default.red("--tenant <slug> is required."));
|
|
2987
3027
|
process.exit(1);
|
|
2988
3028
|
}
|
|
2989
3029
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3011,7 +3051,7 @@ async function runTenantDelete(slug, opts) {
|
|
|
3011
3051
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
3012
3052
|
]);
|
|
3013
3053
|
if (!confirm) {
|
|
3014
|
-
console.log(
|
|
3054
|
+
console.log(import_chalk25.default.dim("Aborted."));
|
|
3015
3055
|
return;
|
|
3016
3056
|
}
|
|
3017
3057
|
}
|
|
@@ -3030,7 +3070,7 @@ async function runTenantDelete(slug, opts) {
|
|
|
3030
3070
|
}
|
|
3031
3071
|
async function runTenantCors(opts) {
|
|
3032
3072
|
if (!opts.tenant) {
|
|
3033
|
-
console.error(
|
|
3073
|
+
console.error(import_chalk25.default.red("--tenant <slug> is required."));
|
|
3034
3074
|
process.exit(1);
|
|
3035
3075
|
}
|
|
3036
3076
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3052,7 +3092,7 @@ async function runTenantCors(opts) {
|
|
|
3052
3092
|
}
|
|
3053
3093
|
|
|
3054
3094
|
// src/commands/tenant-drill.ts
|
|
3055
|
-
var
|
|
3095
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
3056
3096
|
var import_ora11 = __toESM(require("ora"));
|
|
3057
3097
|
var import_crypto = require("crypto");
|
|
3058
3098
|
init_admin();
|
|
@@ -3082,15 +3122,15 @@ async function validScopedTenant(teamId, query) {
|
|
|
3082
3122
|
const next = { ...creds };
|
|
3083
3123
|
delete next.activeTenant;
|
|
3084
3124
|
saveCredentials2(next);
|
|
3085
|
-
console.log(
|
|
3125
|
+
console.log(import_chalk26.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
3086
3126
|
return void 0;
|
|
3087
3127
|
}
|
|
3088
3128
|
async function tenantHome(teamId, tenant2) {
|
|
3089
3129
|
const { default: inquirer3 } = await import("inquirer");
|
|
3090
3130
|
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
3091
|
-
console.log(
|
|
3131
|
+
console.log(import_chalk26.default.bold(`
|
|
3092
3132
|
Tenant ${tenant2}`));
|
|
3093
|
-
console.log(
|
|
3133
|
+
console.log(import_chalk26.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
3094
3134
|
for (; ; ) {
|
|
3095
3135
|
const spinner = (0, import_ora11.default)("Reading tenant state...").start();
|
|
3096
3136
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
@@ -3106,18 +3146,18 @@ Tenant ${tenant2}`));
|
|
|
3106
3146
|
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
3107
3147
|
const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
|
|
3108
3148
|
const nLogin = nClients + nIssuers + nOpaque;
|
|
3109
|
-
const onOff = (b) => b ?
|
|
3149
|
+
const onOff = (b) => b ? import_chalk26.default.green("on") : import_chalk26.default.dim("off");
|
|
3110
3150
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3111
3151
|
type: "list",
|
|
3112
3152
|
name: "pick",
|
|
3113
3153
|
message: `Tenant ${tenant2}:`,
|
|
3114
3154
|
pageSize: 12,
|
|
3115
3155
|
choices: [
|
|
3116
|
-
{ name: `Login methods (${nLogin}) ${
|
|
3117
|
-
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${
|
|
3118
|
-
{ name: `Portal admins (${nEmails}) ${
|
|
3119
|
-
new inquirer3.Separator(
|
|
3120
|
-
{ name: `CORS: ${cors?.cors ?
|
|
3156
|
+
{ name: `Login methods (${nLogin}) ${import_chalk26.default.dim("how your consumers sign in")}`, value: "login" },
|
|
3157
|
+
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk26.default.dim(`identity & key management (${tenant2}.iam.apiblaze.com)`)}`, value: "iam" },
|
|
3158
|
+
{ name: `Portal admins (${nEmails}) ${import_chalk26.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
|
|
3159
|
+
new inquirer3.Separator(import_chalk26.default.dim(" Settings")),
|
|
3160
|
+
{ name: `CORS: ${cors?.cors ? import_chalk26.default.cyan(JSON.stringify(cors.cors)) : import_chalk26.default.dim("(unset)")} ${import_chalk26.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
|
|
3121
3161
|
{ name: "\u2190 Back", value: "back" }
|
|
3122
3162
|
]
|
|
3123
3163
|
}]);
|
|
@@ -3130,7 +3170,7 @@ Tenant ${tenant2}`));
|
|
|
3130
3170
|
case "iam": {
|
|
3131
3171
|
const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
|
|
3132
3172
|
await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
3133
|
-
console.log(
|
|
3173
|
+
console.log(import_chalk26.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
3134
3174
|
break;
|
|
3135
3175
|
}
|
|
3136
3176
|
case "cors": {
|
|
@@ -3143,11 +3183,11 @@ Tenant ${tenant2}`));
|
|
|
3143
3183
|
if (v === "") break;
|
|
3144
3184
|
const parsed = v === "null" ? null : safeJson(v);
|
|
3145
3185
|
if (parsed === void 0) {
|
|
3146
|
-
console.log(
|
|
3186
|
+
console.log(import_chalk26.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
3147
3187
|
break;
|
|
3148
3188
|
}
|
|
3149
3189
|
await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
3150
|
-
console.log(
|
|
3190
|
+
console.log(import_chalk26.default.green(" CORS updated."));
|
|
3151
3191
|
break;
|
|
3152
3192
|
}
|
|
3153
3193
|
case "emails":
|
|
@@ -3171,15 +3211,15 @@ async function loginMethodsMenu(teamId, tenant2, base) {
|
|
|
3171
3211
|
const choices = [];
|
|
3172
3212
|
for (const c of appClients) {
|
|
3173
3213
|
const nP = (c.providers ?? []).length || c.providers_count || 0;
|
|
3174
|
-
choices.push({ name: `${
|
|
3214
|
+
choices.push({ name: `${import_chalk26.default.cyan("APIblaze-hosted")} ${import_chalk26.default.bold(c.name ?? c.clientId)} ${import_chalk26.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk26.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
|
|
3175
3215
|
}
|
|
3176
3216
|
for (const i of issuers) {
|
|
3177
|
-
choices.push({ name: `${
|
|
3217
|
+
choices.push({ name: `${import_chalk26.default.cyan("Your own \u2014 JWT ")} ${import_chalk26.default.bold(i.iss)} ${import_chalk26.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
|
|
3178
3218
|
}
|
|
3179
3219
|
if (opaque?.endpoint) {
|
|
3180
|
-
choices.push({ name: `${
|
|
3220
|
+
choices.push({ name: `${import_chalk26.default.cyan("Your own \u2014 opaque")} ${import_chalk26.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
|
|
3181
3221
|
}
|
|
3182
|
-
if (!choices.length) console.log(
|
|
3222
|
+
if (!choices.length) console.log(import_chalk26.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
|
|
3183
3223
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3184
3224
|
type: "list",
|
|
3185
3225
|
name: "pick",
|
|
@@ -3219,9 +3259,9 @@ async function addLoginMethod(teamId, tenant2, base) {
|
|
|
3219
3259
|
message: "How do people log in?",
|
|
3220
3260
|
pageSize: 8,
|
|
3221
3261
|
choices: [
|
|
3222
|
-
{ name: `APIblaze hosted login page ${
|
|
3223
|
-
{ name: `Your own hosted login \u2014 JWT ${
|
|
3224
|
-
{ name: `Your own login \u2014 opaque token ${
|
|
3262
|
+
{ name: `APIblaze hosted login page ${import_chalk26.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
|
|
3263
|
+
{ name: `Your own hosted login \u2014 JWT ${import_chalk26.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
|
|
3264
|
+
{ name: `Your own login \u2014 opaque token ${import_chalk26.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
|
|
3225
3265
|
{ name: "\u2190 Back", value: null }
|
|
3226
3266
|
]
|
|
3227
3267
|
}]);
|
|
@@ -3245,8 +3285,8 @@ function safeJson(s) {
|
|
|
3245
3285
|
async function emailsMenu(base, emails) {
|
|
3246
3286
|
const { default: inquirer3 } = await import("inquirer");
|
|
3247
3287
|
console.log();
|
|
3248
|
-
if (!emails.length) console.log(
|
|
3249
|
-
for (const e of emails) console.log(` ${
|
|
3288
|
+
if (!emails.length) console.log(import_chalk26.default.dim(" No consumer-admin emails."));
|
|
3289
|
+
for (const e of emails) console.log(` ${import_chalk26.default.bold(e.email ?? e)} ${import_chalk26.default.dim(e.status ?? "")}`);
|
|
3250
3290
|
const { act } = await inquirer3.prompt([{
|
|
3251
3291
|
type: "list",
|
|
3252
3292
|
name: "act",
|
|
@@ -3261,7 +3301,7 @@ async function emailsMenu(base, emails) {
|
|
|
3261
3301
|
if (act === "add") {
|
|
3262
3302
|
const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
3263
3303
|
await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
3264
|
-
console.log(
|
|
3304
|
+
console.log(import_chalk26.default.green(` ${email} added.`));
|
|
3265
3305
|
} else {
|
|
3266
3306
|
const { e } = await inquirer3.prompt([{
|
|
3267
3307
|
type: "list",
|
|
@@ -3271,7 +3311,7 @@ async function emailsMenu(base, emails) {
|
|
|
3271
3311
|
}]);
|
|
3272
3312
|
if (!e) return;
|
|
3273
3313
|
await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3274
|
-
console.log(
|
|
3314
|
+
console.log(import_chalk26.default.green(` ${e} removed.`));
|
|
3275
3315
|
}
|
|
3276
3316
|
}
|
|
3277
3317
|
async function addIssuer(base) {
|
|
@@ -3292,19 +3332,19 @@ async function addIssuer(base) {
|
|
|
3292
3332
|
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3293
3333
|
summary: `Add external issuer ${a.iss.trim()}`
|
|
3294
3334
|
});
|
|
3295
|
-
console.log(
|
|
3335
|
+
console.log(import_chalk26.default.green(" JWT login method saved."));
|
|
3296
3336
|
}
|
|
3297
3337
|
async function issuerHome(base, issuer) {
|
|
3298
3338
|
const { default: inquirer3 } = await import("inquirer");
|
|
3299
3339
|
console.log(`
|
|
3300
|
-
${
|
|
3340
|
+
${import_chalk26.default.bold(issuer.iss)} ${import_chalk26.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
|
|
3301
3341
|
const { act } = await inquirer3.prompt([{
|
|
3302
3342
|
type: "list",
|
|
3303
3343
|
name: "act",
|
|
3304
3344
|
message: "This JWT login method:",
|
|
3305
3345
|
choices: [
|
|
3306
3346
|
{ name: "Replace it (re-enter details)", value: "edit" },
|
|
3307
|
-
{ name:
|
|
3347
|
+
{ name: import_chalk26.default.red("Delete it"), value: "rm" },
|
|
3308
3348
|
{ name: "\u2190 Back", value: "back" }
|
|
3309
3349
|
]
|
|
3310
3350
|
}]);
|
|
@@ -3320,7 +3360,7 @@ async function issuerHome(base, issuer) {
|
|
|
3320
3360
|
path: `${base}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
|
|
3321
3361
|
summary: `Delete issuer ${issuer.iss}`
|
|
3322
3362
|
});
|
|
3323
|
-
console.log(
|
|
3363
|
+
console.log(import_chalk26.default.green(" Deleted."));
|
|
3324
3364
|
}
|
|
3325
3365
|
async function setOpaque(base, cur) {
|
|
3326
3366
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3329,19 +3369,19 @@ async function setOpaque(base, cur) {
|
|
|
3329
3369
|
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3330
3370
|
]);
|
|
3331
3371
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3332
|
-
console.log(
|
|
3372
|
+
console.log(import_chalk26.default.green(" Opaque login method set."));
|
|
3333
3373
|
}
|
|
3334
3374
|
async function opaqueHome(base, cur) {
|
|
3335
3375
|
const { default: inquirer3 } = await import("inquirer");
|
|
3336
3376
|
console.log(`
|
|
3337
|
-
${
|
|
3377
|
+
${import_chalk26.default.bold(cur.endpoint)} ${import_chalk26.default.dim(cur.method ?? "GET")}`);
|
|
3338
3378
|
const { act } = await inquirer3.prompt([{
|
|
3339
3379
|
type: "list",
|
|
3340
3380
|
name: "act",
|
|
3341
3381
|
message: "This opaque login method:",
|
|
3342
3382
|
choices: [
|
|
3343
3383
|
{ name: "Replace it", value: "edit" },
|
|
3344
|
-
{ name:
|
|
3384
|
+
{ name: import_chalk26.default.red("Delete it"), value: "rm" },
|
|
3345
3385
|
{ name: "\u2190 Back", value: "back" }
|
|
3346
3386
|
]
|
|
3347
3387
|
}]);
|
|
@@ -3351,7 +3391,7 @@ async function opaqueHome(base, cur) {
|
|
|
3351
3391
|
return;
|
|
3352
3392
|
}
|
|
3353
3393
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3354
|
-
console.log(
|
|
3394
|
+
console.log(import_chalk26.default.green(" Deleted."));
|
|
3355
3395
|
}
|
|
3356
3396
|
async function clientHome(base, summary) {
|
|
3357
3397
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3370,13 +3410,13 @@ async function clientHome(base, summary) {
|
|
|
3370
3410
|
message: `${c.name ?? id}:`,
|
|
3371
3411
|
pageSize: 12,
|
|
3372
3412
|
choices: [
|
|
3373
|
-
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${
|
|
3374
|
-
{ name: `Callback URLs: ${cb.length ?
|
|
3375
|
-
{ name: `Scopes: ${scopes.length ?
|
|
3413
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk26.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
3414
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk26.default.cyan(cb.join(", ")) : import_chalk26.default.dim("(none)")}`, value: "callbacks" },
|
|
3415
|
+
{ name: `Scopes: ${scopes.length ? import_chalk26.default.cyan(scopes.join(" ")) : import_chalk26.default.dim("(defaults)")}`, value: "scopes" },
|
|
3376
3416
|
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3377
3417
|
{ name: "Reveal client secret", value: "secret" },
|
|
3378
3418
|
{ name: "Rotate client secret", value: "rotate" },
|
|
3379
|
-
{ name:
|
|
3419
|
+
{ name: import_chalk26.default.red("Delete this app client"), value: "delete" },
|
|
3380
3420
|
{ name: "\u2190 Back", value: "back" }
|
|
3381
3421
|
]
|
|
3382
3422
|
}]);
|
|
@@ -3389,13 +3429,13 @@ async function clientHome(base, summary) {
|
|
|
3389
3429
|
case "callbacks": {
|
|
3390
3430
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3391
3431
|
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3392
|
-
console.log(
|
|
3432
|
+
console.log(import_chalk26.default.green(" Callbacks updated."));
|
|
3393
3433
|
break;
|
|
3394
3434
|
}
|
|
3395
3435
|
case "scopes": {
|
|
3396
3436
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3397
3437
|
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3398
|
-
console.log(
|
|
3438
|
+
console.log(import_chalk26.default.green(" Scopes updated."));
|
|
3399
3439
|
break;
|
|
3400
3440
|
}
|
|
3401
3441
|
case "expiries": {
|
|
@@ -3410,14 +3450,14 @@ async function clientHome(base, summary) {
|
|
|
3410
3450
|
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3411
3451
|
summary: "Update token expiries"
|
|
3412
3452
|
});
|
|
3413
|
-
console.log(
|
|
3453
|
+
console.log(import_chalk26.default.green(" Expiries updated."));
|
|
3414
3454
|
break;
|
|
3415
3455
|
}
|
|
3416
3456
|
case "secret": {
|
|
3417
3457
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3418
3458
|
if (!sure) break;
|
|
3419
3459
|
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3420
|
-
console.log(` ${
|
|
3460
|
+
console.log(` ${import_chalk26.default.bold("client_secret")}: ${import_chalk26.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3421
3461
|
break;
|
|
3422
3462
|
}
|
|
3423
3463
|
case "rotate": {
|
|
@@ -3425,14 +3465,14 @@ async function clientHome(base, summary) {
|
|
|
3425
3465
|
if (!sure) break;
|
|
3426
3466
|
const fresh = randomSecret();
|
|
3427
3467
|
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3428
|
-
console.log(` New ${
|
|
3468
|
+
console.log(` New ${import_chalk26.default.bold("client_secret")}: ${import_chalk26.default.green(fresh)} ${import_chalk26.default.dim("(store it now)")}`);
|
|
3429
3469
|
break;
|
|
3430
3470
|
}
|
|
3431
3471
|
case "delete": {
|
|
3432
3472
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Delete app client "${c.name ?? id}"? Consumers logged in through it will lose access.`, default: false }]);
|
|
3433
3473
|
if (!sure) break;
|
|
3434
3474
|
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
3435
|
-
console.log(
|
|
3475
|
+
console.log(import_chalk26.default.green(" App client deleted."));
|
|
3436
3476
|
return;
|
|
3437
3477
|
}
|
|
3438
3478
|
}
|
|
@@ -3459,9 +3499,9 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3459
3499
|
const providers = Array.isArray(raw) ? raw : [];
|
|
3460
3500
|
console.log();
|
|
3461
3501
|
for (const p of providers) {
|
|
3462
|
-
console.log(` ${
|
|
3502
|
+
console.log(` ${import_chalk26.default.bold(p.type)} ${import_chalk26.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
|
|
3463
3503
|
}
|
|
3464
|
-
if (!providers.length) console.log(
|
|
3504
|
+
if (!providers.length) console.log(import_chalk26.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
3465
3505
|
const { act } = await inquirer3.prompt([{
|
|
3466
3506
|
type: "list",
|
|
3467
3507
|
name: "act",
|
|
@@ -3519,31 +3559,31 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3519
3559
|
body.targetServerToken = routing;
|
|
3520
3560
|
}
|
|
3521
3561
|
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
3522
|
-
console.log(
|
|
3562
|
+
console.log(import_chalk26.default.green(` ${type} provider added.`));
|
|
3523
3563
|
} else {
|
|
3524
3564
|
const { p } = await inquirer3.prompt([{
|
|
3525
3565
|
type: "list",
|
|
3526
3566
|
name: "p",
|
|
3527
3567
|
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
3528
|
-
choices: [...providers.map((x) => ({ name: `${x.type} ${
|
|
3568
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk26.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3529
3569
|
}]);
|
|
3530
3570
|
if (!p) continue;
|
|
3531
3571
|
if (act === "rm") {
|
|
3532
3572
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Remove the ${p.type} provider? Consumers using it can no longer sign in.`, default: false }]);
|
|
3533
3573
|
if (!sure) continue;
|
|
3534
3574
|
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
3535
|
-
console.log(
|
|
3575
|
+
console.log(import_chalk26.default.green(` ${p.type} removed.`));
|
|
3536
3576
|
} else {
|
|
3537
3577
|
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
3538
|
-
console.log(` ${
|
|
3578
|
+
console.log(` ${import_chalk26.default.bold("client_secret")}: ${import_chalk26.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3539
3579
|
}
|
|
3540
3580
|
}
|
|
3541
3581
|
}
|
|
3542
3582
|
}
|
|
3543
3583
|
|
|
3544
3584
|
// src/commands/spec.ts
|
|
3545
|
-
var
|
|
3546
|
-
var
|
|
3585
|
+
var fs7 = __toESM(require("fs"));
|
|
3586
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
3547
3587
|
var import_ora12 = __toESM(require("ora"));
|
|
3548
3588
|
init_admin();
|
|
3549
3589
|
async function runSpecGet(project, opts) {
|
|
@@ -3558,14 +3598,14 @@ async function runSpecGet(project, opts) {
|
|
|
3558
3598
|
}
|
|
3559
3599
|
async function runSpecSet(project, opts) {
|
|
3560
3600
|
if (!opts.file) {
|
|
3561
|
-
console.error(
|
|
3601
|
+
console.error(import_chalk27.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
3562
3602
|
process.exit(1);
|
|
3563
3603
|
}
|
|
3564
3604
|
let specContent;
|
|
3565
3605
|
try {
|
|
3566
|
-
specContent =
|
|
3606
|
+
specContent = fs7.readFileSync(opts.file, "utf-8");
|
|
3567
3607
|
} catch {
|
|
3568
|
-
console.error(
|
|
3608
|
+
console.error(import_chalk27.default.red(`Cannot read file: ${opts.file}`));
|
|
3569
3609
|
process.exit(1);
|
|
3570
3610
|
}
|
|
3571
3611
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3587,13 +3627,13 @@ async function runSpecSet(project, opts) {
|
|
|
3587
3627
|
}
|
|
3588
3628
|
|
|
3589
3629
|
// src/commands/agent.ts
|
|
3590
|
-
var
|
|
3630
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
3591
3631
|
var import_ora13 = __toESM(require("ora"));
|
|
3592
3632
|
init_auth();
|
|
3593
3633
|
init_admin();
|
|
3594
3634
|
|
|
3595
3635
|
// src/lib/tools.ts
|
|
3596
|
-
var
|
|
3636
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
3597
3637
|
init_admin();
|
|
3598
3638
|
init_api();
|
|
3599
3639
|
async function proj(teamId, name, version2) {
|
|
@@ -3612,15 +3652,15 @@ var TOOLS = [
|
|
|
3612
3652
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
3613
3653
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
3614
3654
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
3615
|
-
const lines = [` ${
|
|
3616
|
-
if (res.devPortal) lines.push(` ${
|
|
3655
|
+
const lines = [` ${import_chalk28.default.dim("Proxy URL:")} ${import_chalk28.default.bold(url)}`];
|
|
3656
|
+
if (res.devPortal) lines.push(` ${import_chalk28.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
3617
3657
|
const envs = Object.keys(keys);
|
|
3618
3658
|
if (envs.length) {
|
|
3619
|
-
lines.push("", ` ${
|
|
3659
|
+
lines.push("", ` ${import_chalk28.default.bold("API keys")} ${import_chalk28.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
3620
3660
|
const w = Math.max(...envs.map((e) => e.length));
|
|
3621
|
-
for (const env of envs) lines.push(` ${
|
|
3661
|
+
for (const env of envs) lines.push(` ${import_chalk28.default.cyan(env.padEnd(w))} ${import_chalk28.default.green(keys[env])}`);
|
|
3622
3662
|
}
|
|
3623
|
-
if (tryIt) lines.push("", ` ${
|
|
3663
|
+
if (tryIt) lines.push("", ` ${import_chalk28.default.dim("Try it:")}`, ` ${import_chalk28.default.cyan(tryIt)}`);
|
|
3624
3664
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
3625
3665
|
}
|
|
3626
3666
|
},
|
|
@@ -3776,17 +3816,17 @@ function truncate(value, max = 1500) {
|
|
|
3776
3816
|
}
|
|
3777
3817
|
function printCost(resp) {
|
|
3778
3818
|
const line = formatBilling(resp.billing);
|
|
3779
|
-
if (line) console.log(
|
|
3819
|
+
if (line) console.log(import_chalk29.default.magenta(` ${line}`) + import_chalk29.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
|
|
3780
3820
|
}
|
|
3781
3821
|
async function runAgent(opts) {
|
|
3782
3822
|
requireAuth();
|
|
3783
3823
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
3784
3824
|
const { default: inquirer3 } = await import("inquirer");
|
|
3785
|
-
console.log(
|
|
3786
|
-
console.log(
|
|
3825
|
+
console.log(import_chalk29.default.bold("APIblaze agent") + import_chalk29.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
3826
|
+
console.log(import_chalk29.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
3787
3827
|
const history = [];
|
|
3788
3828
|
while (true) {
|
|
3789
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
3829
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk29.default.cyan("you") + " \u203A" }]);
|
|
3790
3830
|
const text = (input ?? "").trim();
|
|
3791
3831
|
if (!text) continue;
|
|
3792
3832
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
@@ -3800,14 +3840,14 @@ async function runAgent(opts) {
|
|
|
3800
3840
|
} catch (err) {
|
|
3801
3841
|
spinner.stop();
|
|
3802
3842
|
if (err instanceof ApiError && err.status === 402) {
|
|
3803
|
-
console.log(
|
|
3843
|
+
console.log(import_chalk29.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
3804
3844
|
break;
|
|
3805
3845
|
}
|
|
3806
3846
|
throw err;
|
|
3807
3847
|
}
|
|
3808
3848
|
history.push({ role: "assistant", content: resp.raw });
|
|
3809
3849
|
printCost(resp);
|
|
3810
|
-
if (resp.reply) console.log(
|
|
3850
|
+
if (resp.reply) console.log(import_chalk29.default.green("agent") + " \u203A " + resp.reply);
|
|
3811
3851
|
if (!resp.action) break;
|
|
3812
3852
|
const tool = findTool(resp.action.tool);
|
|
3813
3853
|
if (!tool) {
|
|
@@ -3832,11 +3872,11 @@ async function runAgent(opts) {
|
|
|
3832
3872
|
}
|
|
3833
3873
|
renderTrace();
|
|
3834
3874
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
3835
|
-
console.log(
|
|
3875
|
+
console.log(import_chalk29.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
3836
3876
|
}
|
|
3837
3877
|
}
|
|
3838
3878
|
}
|
|
3839
|
-
console.log(
|
|
3879
|
+
console.log(import_chalk29.default.dim("\nBye."));
|
|
3840
3880
|
}
|
|
3841
3881
|
|
|
3842
3882
|
// src/commands/config-browse.ts
|
|
@@ -3999,11 +4039,11 @@ function dig(blob, dotted) {
|
|
|
3999
4039
|
}
|
|
4000
4040
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
4001
4041
|
function show(v) {
|
|
4002
|
-
if (v === void 0) return
|
|
4003
|
-
if (v === null) return
|
|
4004
|
-
if (typeof v === "object") return
|
|
4005
|
-
if (typeof v === "boolean") return v ?
|
|
4006
|
-
return
|
|
4042
|
+
if (v === void 0) return import_chalk30.default.dim("(unset)");
|
|
4043
|
+
if (v === null) return import_chalk30.default.dim("null");
|
|
4044
|
+
if (typeof v === "object") return import_chalk30.default.cyan(JSON.stringify(v));
|
|
4045
|
+
if (typeof v === "boolean") return v ? import_chalk30.default.green("on") : import_chalk30.default.red("off");
|
|
4046
|
+
return import_chalk30.default.cyan(String(v));
|
|
4007
4047
|
}
|
|
4008
4048
|
function parseValue(raw) {
|
|
4009
4049
|
if (raw === "true") return true;
|
|
@@ -4045,10 +4085,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
4045
4085
|
}
|
|
4046
4086
|
}
|
|
4047
4087
|
var loginFirst = (what) => {
|
|
4048
|
-
console.log(
|
|
4088
|
+
console.log(import_chalk30.default.yellow(`
|
|
4049
4089
|
Log in first to ${what}.`));
|
|
4050
|
-
console.log(
|
|
4051
|
-
console.log(
|
|
4090
|
+
console.log(import_chalk30.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
4091
|
+
console.log(import_chalk30.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
4052
4092
|
};
|
|
4053
4093
|
async function runConfig(project, key, value, opts) {
|
|
4054
4094
|
const creds = loadCredentials();
|
|
@@ -4065,9 +4105,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
4065
4105
|
}
|
|
4066
4106
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
4067
4107
|
if (!setting) {
|
|
4068
|
-
console.error(
|
|
4069
|
-
console.error(
|
|
4070
|
-
console.error(
|
|
4108
|
+
console.error(import_chalk30.default.red(`Unknown setting "${key}".`));
|
|
4109
|
+
console.error(import_chalk30.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
4110
|
+
console.error(import_chalk30.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
4071
4111
|
process.exit(1);
|
|
4072
4112
|
}
|
|
4073
4113
|
if (value === void 0) {
|
|
@@ -4082,7 +4122,7 @@ async function pickProject(teamId) {
|
|
|
4082
4122
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4083
4123
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
4084
4124
|
if (!projects.length) {
|
|
4085
|
-
console.error(
|
|
4125
|
+
console.error(import_chalk30.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
4086
4126
|
process.exit(1);
|
|
4087
4127
|
}
|
|
4088
4128
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -4090,7 +4130,7 @@ async function pickProject(teamId) {
|
|
|
4090
4130
|
type: "list",
|
|
4091
4131
|
name: "picked",
|
|
4092
4132
|
message: "Which project?",
|
|
4093
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
4133
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk30.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
4094
4134
|
}]);
|
|
4095
4135
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
4096
4136
|
}
|
|
@@ -4101,25 +4141,25 @@ function printAll(proj2, cfg, json) {
|
|
|
4101
4141
|
console.log(JSON.stringify(out, null, 2));
|
|
4102
4142
|
return;
|
|
4103
4143
|
}
|
|
4104
|
-
console.log(
|
|
4144
|
+
console.log(import_chalk30.default.bold(`
|
|
4105
4145
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
4106
4146
|
`));
|
|
4107
4147
|
for (const group of SETTING_GROUPS) {
|
|
4108
|
-
console.log(
|
|
4148
|
+
console.log(import_chalk30.default.bold(group));
|
|
4109
4149
|
for (const s of SETTINGS.filter((x) => x.group === group)) {
|
|
4110
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
4150
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk30.default.dim(s.desc)}`);
|
|
4111
4151
|
}
|
|
4112
4152
|
console.log();
|
|
4113
4153
|
}
|
|
4114
|
-
console.log(
|
|
4154
|
+
console.log(import_chalk30.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
4115
4155
|
}
|
|
4116
4156
|
async function discoveryMenu(project) {
|
|
4117
4157
|
const { default: inquirer3 } = await import("inquirer");
|
|
4118
|
-
console.log(
|
|
4158
|
+
console.log(import_chalk30.default.bold(`
|
|
4119
4159
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
4120
4160
|
`));
|
|
4121
|
-
console.log(
|
|
4122
|
-
console.log(
|
|
4161
|
+
console.log(import_chalk30.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
4162
|
+
console.log(import_chalk30.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
4123
4163
|
for (; ; ) {
|
|
4124
4164
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
4125
4165
|
type: "list",
|
|
@@ -4127,13 +4167,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4127
4167
|
message: "Explore:",
|
|
4128
4168
|
pageSize: 20,
|
|
4129
4169
|
choices: [
|
|
4130
|
-
new inquirer3.Separator(
|
|
4170
|
+
new inquirer3.Separator(import_chalk30.default.bold("\u2014 Settings \u2014")),
|
|
4131
4171
|
...SETTING_GROUPS.map((g) => ({
|
|
4132
|
-
name: `${g} ${
|
|
4172
|
+
name: `${g} ${import_chalk30.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
4133
4173
|
value: { kind: "settings", g }
|
|
4134
4174
|
})),
|
|
4135
|
-
new inquirer3.Separator(
|
|
4136
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4175
|
+
new inquirer3.Separator(import_chalk30.default.bold("\u2014 Features \u2014")),
|
|
4176
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk30.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
4137
4177
|
new inquirer3.Separator(),
|
|
4138
4178
|
{ name: "Exit", value: { kind: "exit" } }
|
|
4139
4179
|
]
|
|
@@ -4142,24 +4182,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4142
4182
|
if (pick2.kind === "settings") {
|
|
4143
4183
|
console.log();
|
|
4144
4184
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
4145
|
-
console.log(` ${
|
|
4146
|
-
console.log(` ${
|
|
4185
|
+
console.log(` ${import_chalk30.default.bold(s.label.padEnd(28))} ${import_chalk30.default.dim(s.desc)}`);
|
|
4186
|
+
console.log(` ${import_chalk30.default.dim(" key: " + s.key)}`);
|
|
4147
4187
|
}
|
|
4148
4188
|
loginFirst("view or change these settings");
|
|
4149
4189
|
} else {
|
|
4150
4190
|
const f = pick2.f;
|
|
4151
4191
|
console.log(`
|
|
4152
|
-
${
|
|
4192
|
+
${import_chalk30.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
4153
4193
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
4154
4194
|
}
|
|
4155
4195
|
}
|
|
4156
4196
|
}
|
|
4157
4197
|
async function navigator(proj2, cfg, opts) {
|
|
4158
4198
|
const { default: inquirer3 } = await import("inquirer");
|
|
4159
|
-
console.log(
|
|
4199
|
+
console.log(import_chalk30.default.bold(`
|
|
4160
4200
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
4161
4201
|
`));
|
|
4162
|
-
console.log(
|
|
4202
|
+
console.log(import_chalk30.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
4163
4203
|
let blob = cfg;
|
|
4164
4204
|
for (; ; ) {
|
|
4165
4205
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
@@ -4168,10 +4208,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4168
4208
|
message: "Where to?",
|
|
4169
4209
|
pageSize: 20,
|
|
4170
4210
|
choices: [
|
|
4171
|
-
new inquirer3.Separator(
|
|
4211
|
+
new inquirer3.Separator(import_chalk30.default.bold("\u2014 Settings \u2014")),
|
|
4172
4212
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
4173
|
-
new inquirer3.Separator(
|
|
4174
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4213
|
+
new inquirer3.Separator(import_chalk30.default.bold("\u2014 Features \u2014")),
|
|
4214
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk30.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
4175
4215
|
new inquirer3.Separator(),
|
|
4176
4216
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
4177
4217
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -4211,7 +4251,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4211
4251
|
}
|
|
4212
4252
|
}
|
|
4213
4253
|
} catch (err) {
|
|
4214
|
-
console.error(
|
|
4254
|
+
console.error(import_chalk30.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
4215
4255
|
}
|
|
4216
4256
|
}
|
|
4217
4257
|
}
|
|
@@ -4225,7 +4265,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
4225
4265
|
message: group + ":",
|
|
4226
4266
|
pageSize: 16,
|
|
4227
4267
|
choices: [
|
|
4228
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
4268
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk30.default.dim(s2.desc)}`, value: s2 })),
|
|
4229
4269
|
new inquirer3.Separator(),
|
|
4230
4270
|
{ name: "\u2190 Back", value: null }
|
|
4231
4271
|
]
|
|
@@ -4243,7 +4283,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
4243
4283
|
} else if (s.type === "number") {
|
|
4244
4284
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
4245
4285
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
4246
|
-
console.log(
|
|
4286
|
+
console.log(import_chalk30.default.yellow(" Not a number \u2014 unchanged."));
|
|
4247
4287
|
continue;
|
|
4248
4288
|
}
|
|
4249
4289
|
value = Number(v);
|
|
@@ -4324,7 +4364,7 @@ async function buildCondition(phase) {
|
|
|
4324
4364
|
const items = [];
|
|
4325
4365
|
for (; ; ) {
|
|
4326
4366
|
const a = await inquirer3.prompt([
|
|
4327
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
4367
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk30.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
4328
4368
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
4329
4369
|
"eq",
|
|
4330
4370
|
"neq",
|
|
@@ -4355,7 +4395,7 @@ async function buildCondition(phase) {
|
|
|
4355
4395
|
function showCondition(cond) {
|
|
4356
4396
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
4357
4397
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
4358
|
-
return
|
|
4398
|
+
return import_chalk30.default.dim(` when ${s}`);
|
|
4359
4399
|
}
|
|
4360
4400
|
async function transformsMenu(proj2) {
|
|
4361
4401
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -4364,12 +4404,12 @@ async function transformsMenu(proj2) {
|
|
|
4364
4404
|
const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
|
|
4365
4405
|
const rules = out?.rules ?? [];
|
|
4366
4406
|
console.log();
|
|
4367
|
-
if (!rules.length) console.log(
|
|
4407
|
+
if (!rules.length) console.log(import_chalk30.default.dim(" No transform rules yet."));
|
|
4368
4408
|
for (const r of rules) {
|
|
4369
4409
|
const a = r.action ?? {};
|
|
4370
4410
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
4371
|
-
const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ?
|
|
4372
|
-
console.log(` ${r.enabled ?
|
|
4411
|
+
const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ? import_chalk30.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
|
|
4412
|
+
console.log(` ${r.enabled ? import_chalk30.default.green("\u25CF") : import_chalk30.default.dim("\u25CB")} ${import_chalk30.default.bold(r.name)} ${import_chalk30.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
|
|
4373
4413
|
}
|
|
4374
4414
|
const { act } = await inquirer3.prompt([{
|
|
4375
4415
|
type: "list",
|
|
@@ -4381,7 +4421,7 @@ async function transformsMenu(proj2) {
|
|
|
4381
4421
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
4382
4422
|
{ name: "Delete a rule", value: "delete" }
|
|
4383
4423
|
] : [],
|
|
4384
|
-
{ name:
|
|
4424
|
+
{ name: import_chalk30.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
4385
4425
|
{ name: "\u2190 Back", value: "back" }
|
|
4386
4426
|
]
|
|
4387
4427
|
}]);
|
|
@@ -4394,11 +4434,11 @@ async function transformsMenu(proj2) {
|
|
|
4394
4434
|
}]);
|
|
4395
4435
|
const body = parseValue(raw);
|
|
4396
4436
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
4397
|
-
console.log(
|
|
4437
|
+
console.log(import_chalk30.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
4398
4438
|
continue;
|
|
4399
4439
|
}
|
|
4400
4440
|
await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
|
|
4401
|
-
console.log(
|
|
4441
|
+
console.log(import_chalk30.default.green(" Rule created."));
|
|
4402
4442
|
continue;
|
|
4403
4443
|
}
|
|
4404
4444
|
if (act === "add") {
|
|
@@ -4414,7 +4454,7 @@ async function transformsMenu(proj2) {
|
|
|
4414
4454
|
{ name: "Remove a field", value: "remove" }
|
|
4415
4455
|
] }
|
|
4416
4456
|
]);
|
|
4417
|
-
const fieldHint =
|
|
4457
|
+
const fieldHint = import_chalk30.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
4418
4458
|
let action2;
|
|
4419
4459
|
if (ans.kind === "hardcode") {
|
|
4420
4460
|
const a = await inquirer3.prompt([
|
|
@@ -4461,16 +4501,16 @@ async function transformsMenu(proj2) {
|
|
|
4461
4501
|
type: "list",
|
|
4462
4502
|
name: "rule",
|
|
4463
4503
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
4464
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
4504
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk30.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
4465
4505
|
}]);
|
|
4466
4506
|
if (!rule) continue;
|
|
4467
4507
|
if (act === "toggle") {
|
|
4468
4508
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
4469
4509
|
await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
4470
|
-
console.log(
|
|
4510
|
+
console.log(import_chalk30.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
4471
4511
|
} else {
|
|
4472
4512
|
await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
4473
|
-
console.log(
|
|
4513
|
+
console.log(import_chalk30.default.green(` ${rule.name} deleted.`));
|
|
4474
4514
|
}
|
|
4475
4515
|
}
|
|
4476
4516
|
}
|
|
@@ -4482,9 +4522,9 @@ async function mappingsMenu(proj2) {
|
|
|
4482
4522
|
const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
|
|
4483
4523
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
4484
4524
|
console.log();
|
|
4485
|
-
if (!tables.length) console.log(
|
|
4525
|
+
if (!tables.length) console.log(import_chalk30.default.dim(" No mapping tables yet."));
|
|
4486
4526
|
for (const t of tables) {
|
|
4487
|
-
console.log(` ${
|
|
4527
|
+
console.log(` ${import_chalk30.default.bold(t.name)} ${import_chalk30.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
|
|
4488
4528
|
}
|
|
4489
4529
|
const { act } = await inquirer3.prompt([{
|
|
4490
4530
|
type: "list",
|
|
@@ -4504,11 +4544,11 @@ async function mappingsMenu(proj2) {
|
|
|
4504
4544
|
]);
|
|
4505
4545
|
const entries2 = parseValue(a.entries);
|
|
4506
4546
|
if (!Array.isArray(entries2)) {
|
|
4507
|
-
console.log(
|
|
4547
|
+
console.log(import_chalk30.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
4508
4548
|
continue;
|
|
4509
4549
|
}
|
|
4510
4550
|
await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
4511
|
-
console.log(
|
|
4551
|
+
console.log(import_chalk30.default.green(` Table "${a.name}" created.`));
|
|
4512
4552
|
} else {
|
|
4513
4553
|
const { table } = await inquirer3.prompt([{
|
|
4514
4554
|
type: "list",
|
|
@@ -4518,7 +4558,7 @@ async function mappingsMenu(proj2) {
|
|
|
4518
4558
|
}]);
|
|
4519
4559
|
if (!table) continue;
|
|
4520
4560
|
await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
4521
|
-
console.log(
|
|
4561
|
+
console.log(import_chalk30.default.green(` ${table.name} deleted.`));
|
|
4522
4562
|
}
|
|
4523
4563
|
}
|
|
4524
4564
|
}
|
|
@@ -4529,14 +4569,14 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4529
4569
|
const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
|
|
4530
4570
|
const tenants = out?.tenants ?? [];
|
|
4531
4571
|
console.log();
|
|
4532
|
-
if (!tenants.length) console.log(
|
|
4533
|
-
for (const t of tenants) console.log(` ${
|
|
4572
|
+
if (!tenants.length) console.log(import_chalk30.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
4573
|
+
for (const t of tenants) console.log(` ${import_chalk30.default.bold(t.tenant_name ?? t.name)} ${import_chalk30.default.dim(t.display_name ?? "")}`);
|
|
4534
4574
|
const { act } = await inquirer3.prompt([{
|
|
4535
4575
|
type: "list",
|
|
4536
4576
|
name: "act",
|
|
4537
4577
|
message: "Tenants:",
|
|
4538
4578
|
choices: [
|
|
4539
|
-
{ name: `Manage a tenant\u2026 ${
|
|
4579
|
+
{ name: `Manage a tenant\u2026 ${import_chalk30.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
4540
4580
|
{ name: "Attach a tenant to this project", value: "attach" },
|
|
4541
4581
|
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
4542
4582
|
{ name: "\u2190 Back", value: "back" }
|
|
@@ -4559,7 +4599,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4559
4599
|
}]);
|
|
4560
4600
|
if (!t) continue;
|
|
4561
4601
|
await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
4562
|
-
console.log(
|
|
4602
|
+
console.log(import_chalk30.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
4563
4603
|
}
|
|
4564
4604
|
}
|
|
4565
4605
|
}
|
|
@@ -4602,7 +4642,7 @@ async function specMenu(proj2, opts) {
|
|
|
4602
4642
|
choices: [
|
|
4603
4643
|
{ name: "Print the stored spec", value: "get" },
|
|
4604
4644
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
4605
|
-
{ name:
|
|
4645
|
+
{ name: import_chalk30.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
4606
4646
|
{ name: "\u2190 Back", value: "back" }
|
|
4607
4647
|
]
|
|
4608
4648
|
}]);
|
|
@@ -4610,7 +4650,7 @@ async function specMenu(proj2, opts) {
|
|
|
4610
4650
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
4611
4651
|
else if (act === "refresh") {
|
|
4612
4652
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
4613
|
-
console.log(
|
|
4653
|
+
console.log(import_chalk30.default.green(" Spec refresh triggered."));
|
|
4614
4654
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
4615
4655
|
}
|
|
4616
4656
|
async function agentsMenu(proj2, opts) {
|
|
@@ -4635,7 +4675,7 @@ async function agentsMenu(proj2, opts) {
|
|
|
4635
4675
|
}
|
|
4636
4676
|
|
|
4637
4677
|
// src/commands/key.ts
|
|
4638
|
-
var
|
|
4678
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
4639
4679
|
var import_ora15 = __toESM(require("ora"));
|
|
4640
4680
|
init_admin();
|
|
4641
4681
|
async function runApikeysMenu(opts) {
|
|
@@ -4664,11 +4704,11 @@ async function runKeyList(opts) {
|
|
|
4664
4704
|
return;
|
|
4665
4705
|
}
|
|
4666
4706
|
if (!keys.length) {
|
|
4667
|
-
console.log(
|
|
4707
|
+
console.log(import_chalk31.default.yellow("No developer keys."));
|
|
4668
4708
|
return;
|
|
4669
4709
|
}
|
|
4670
4710
|
for (const k of keys) {
|
|
4671
|
-
console.log(` ${
|
|
4711
|
+
console.log(` ${import_chalk31.default.bold(k.key_id ?? k.id)} ${import_chalk31.default.dim(k.description ?? "")} ${import_chalk31.default.dim(k.expires_at ?? "no expiry")}`);
|
|
4672
4712
|
}
|
|
4673
4713
|
}
|
|
4674
4714
|
async function runKeyMint(opts) {
|
|
@@ -4689,9 +4729,9 @@ async function runKeyMint(opts) {
|
|
|
4689
4729
|
console.log(JSON.stringify(out));
|
|
4690
4730
|
return;
|
|
4691
4731
|
}
|
|
4692
|
-
console.log(` ${
|
|
4693
|
-
console.log(` ${
|
|
4694
|
-
if (out?.expires_at) console.log(` ${
|
|
4732
|
+
console.log(` ${import_chalk31.default.bold("key_id")}: ${out?.key_id}`);
|
|
4733
|
+
console.log(` ${import_chalk31.default.bold("key")}: ${import_chalk31.default.green(out?.key)} ${import_chalk31.default.dim("(shown once \u2014 store it now)")}`);
|
|
4734
|
+
if (out?.expires_at) console.log(` ${import_chalk31.default.dim("expires:")} ${out.expires_at}`);
|
|
4695
4735
|
} catch (err) {
|
|
4696
4736
|
spinner.fail("Mint failed.");
|
|
4697
4737
|
throw err;
|
|
@@ -4714,10 +4754,10 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
4714
4754
|
}
|
|
4715
4755
|
|
|
4716
4756
|
// src/commands/apichat.ts
|
|
4717
|
-
var
|
|
4718
|
-
var
|
|
4757
|
+
var fs9 = __toESM(require("fs"));
|
|
4758
|
+
var path6 = __toESM(require("path"));
|
|
4719
4759
|
var crypto2 = __toESM(require("crypto"));
|
|
4720
|
-
var
|
|
4760
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
4721
4761
|
var import_ora16 = __toESM(require("ora"));
|
|
4722
4762
|
var import_yaml = require("yaml");
|
|
4723
4763
|
init_auth();
|
|
@@ -4726,12 +4766,12 @@ init_api();
|
|
|
4726
4766
|
init_admin();
|
|
4727
4767
|
|
|
4728
4768
|
// src/commands/llm.ts
|
|
4729
|
-
var
|
|
4730
|
-
var
|
|
4731
|
-
var
|
|
4769
|
+
var fs8 = __toESM(require("fs"));
|
|
4770
|
+
var path5 = __toESM(require("path"));
|
|
4771
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
4732
4772
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
4733
4773
|
init_auth();
|
|
4734
|
-
var LLM_PATH =
|
|
4774
|
+
var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
|
|
4735
4775
|
function detectProvider(key) {
|
|
4736
4776
|
const k = key.trim();
|
|
4737
4777
|
if (k.startsWith("sk-or-")) return "openrouter";
|
|
@@ -4742,15 +4782,15 @@ function detectProvider(key) {
|
|
|
4742
4782
|
}
|
|
4743
4783
|
function loadLlmConfig() {
|
|
4744
4784
|
try {
|
|
4745
|
-
return JSON.parse(
|
|
4785
|
+
return JSON.parse(fs8.readFileSync(LLM_PATH, "utf-8"));
|
|
4746
4786
|
} catch {
|
|
4747
4787
|
return null;
|
|
4748
4788
|
}
|
|
4749
4789
|
}
|
|
4750
4790
|
function saveLlmConfig(cfg) {
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4791
|
+
fs8.mkdirSync(getApiblazeDir(), { recursive: true });
|
|
4792
|
+
fs8.writeFileSync(LLM_PATH, JSON.stringify(cfg, null, 2), "utf-8");
|
|
4793
|
+
fs8.chmodSync(LLM_PATH, 384);
|
|
4754
4794
|
}
|
|
4755
4795
|
async function runLlmSetKey(keyArg, opts) {
|
|
4756
4796
|
let key = keyArg?.trim();
|
|
@@ -4772,27 +4812,27 @@ async function runLlmSetKey(keyArg, opts) {
|
|
|
4772
4812
|
}
|
|
4773
4813
|
const existing = loadLlmConfig();
|
|
4774
4814
|
saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
|
|
4775
|
-
console.log(`${
|
|
4776
|
-
console.log(
|
|
4777
|
-
if (opts.model) console.log(
|
|
4815
|
+
console.log(`${import_chalk32.default.green("\u2713")} Saved ${import_chalk32.default.bold(provider)} key ${maskSecret(key)}`);
|
|
4816
|
+
console.log(import_chalk32.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
|
|
4817
|
+
if (opts.model) console.log(import_chalk32.default.gray(` Model: ${opts.model}`));
|
|
4778
4818
|
}
|
|
4779
4819
|
async function runLlmShow() {
|
|
4780
4820
|
const cfg = loadLlmConfig();
|
|
4781
4821
|
if (!cfg) {
|
|
4782
|
-
console.log(
|
|
4822
|
+
console.log(import_chalk32.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
|
|
4783
4823
|
return;
|
|
4784
4824
|
}
|
|
4785
|
-
console.log(`Provider: ${
|
|
4825
|
+
console.log(`Provider: ${import_chalk32.default.bold(cfg.provider)}`);
|
|
4786
4826
|
console.log(`Key: ${maskSecret(cfg.key)}`);
|
|
4787
4827
|
if (cfg.model) console.log(`Model: ${cfg.model}`);
|
|
4788
|
-
console.log(
|
|
4828
|
+
console.log(import_chalk32.default.gray(`Stored at ${LLM_PATH}`));
|
|
4789
4829
|
}
|
|
4790
4830
|
async function runLlmClearKey() {
|
|
4791
4831
|
try {
|
|
4792
|
-
|
|
4793
|
-
console.log(`${
|
|
4832
|
+
fs8.unlinkSync(LLM_PATH);
|
|
4833
|
+
console.log(`${import_chalk32.default.green("\u2713")} Removed local LLM key.`);
|
|
4794
4834
|
} catch {
|
|
4795
|
-
console.log(
|
|
4835
|
+
console.log(import_chalk32.default.gray("No LLM key was set."));
|
|
4796
4836
|
}
|
|
4797
4837
|
}
|
|
4798
4838
|
|
|
@@ -4800,9 +4840,9 @@ async function runLlmClearKey() {
|
|
|
4800
4840
|
init_trace();
|
|
4801
4841
|
init_types();
|
|
4802
4842
|
function fail4(message, hint) {
|
|
4803
|
-
console.error(
|
|
4843
|
+
console.error(import_chalk33.default.red(`
|
|
4804
4844
|
Error: ${message}`));
|
|
4805
|
-
if (hint) console.error(
|
|
4845
|
+
if (hint) console.error(import_chalk33.default.dim(hint));
|
|
4806
4846
|
process.exit(1);
|
|
4807
4847
|
}
|
|
4808
4848
|
function normalizeName2(raw) {
|
|
@@ -4866,7 +4906,7 @@ async function loadSpec(opts) {
|
|
|
4866
4906
|
}
|
|
4867
4907
|
let text;
|
|
4868
4908
|
try {
|
|
4869
|
-
text =
|
|
4909
|
+
text = fs9.readFileSync(opts.openapispec, "utf-8");
|
|
4870
4910
|
} catch {
|
|
4871
4911
|
fail4(`Cannot read spec file: ${opts.openapispec}`);
|
|
4872
4912
|
}
|
|
@@ -4950,7 +4990,7 @@ async function resolveTargetAuth(spec2, opts) {
|
|
|
4950
4990
|
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
4951
4991
|
);
|
|
4952
4992
|
}
|
|
4953
|
-
if (sawOAuth) console.log(
|
|
4993
|
+
if (sawOAuth) console.log(import_chalk33.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
|
|
4954
4994
|
return null;
|
|
4955
4995
|
}
|
|
4956
4996
|
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
@@ -5003,13 +5043,13 @@ async function captureTargetSecret(auth, opts) {
|
|
|
5003
5043
|
if (!secret) fail4("No credential entered.");
|
|
5004
5044
|
return secret;
|
|
5005
5045
|
}
|
|
5006
|
-
async function cpPost(anon,
|
|
5046
|
+
async function cpPost(anon, path8, body, summary) {
|
|
5007
5047
|
if (anon) {
|
|
5008
5048
|
const cred = loadAnonCred();
|
|
5009
5049
|
if (!cred) throw new Error("Anonymous workspace credential missing.");
|
|
5010
|
-
return cpFetch(cred.cp_key,
|
|
5050
|
+
return cpFetch(cred.cp_key, path8, { method: "POST", body: JSON.stringify(body) });
|
|
5011
5051
|
}
|
|
5012
|
-
return admin({ method: "POST", path:
|
|
5052
|
+
return admin({ method: "POST", path: path8, body, summary });
|
|
5013
5053
|
}
|
|
5014
5054
|
async function provision(spec2, target, opts) {
|
|
5015
5055
|
const loggedIn = !!loadCredentials();
|
|
@@ -5144,7 +5184,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5144
5184
|
throw err;
|
|
5145
5185
|
}
|
|
5146
5186
|
if (out && out.reused === true) {
|
|
5147
|
-
console.log(
|
|
5187
|
+
console.log(import_chalk33.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
|
|
5148
5188
|
} else if (out && out.changed === true && out.previous_spec_hash) {
|
|
5149
5189
|
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
5150
5190
|
if (interactive) {
|
|
@@ -5152,7 +5192,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5152
5192
|
const { go } = await inquirer3.prompt([
|
|
5153
5193
|
{ type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
|
|
5154
5194
|
]);
|
|
5155
|
-
if (!go) console.log(
|
|
5195
|
+
if (!go) console.log(import_chalk33.default.dim(" Keeping the existing MCP catalogue."));
|
|
5156
5196
|
}
|
|
5157
5197
|
}
|
|
5158
5198
|
}
|
|
@@ -5186,19 +5226,24 @@ var CLIENT_ROUND_CAP = 12;
|
|
|
5186
5226
|
function chatUrl(p) {
|
|
5187
5227
|
return `https://${p.mcpHost}/${p.version}/${p.environment}/runtime-chat`;
|
|
5188
5228
|
}
|
|
5189
|
-
function renderToolEvents(events) {
|
|
5229
|
+
function renderToolEvents(events, dpKey) {
|
|
5190
5230
|
for (const e of events ?? []) {
|
|
5191
5231
|
const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
|
|
5192
|
-
const mark = ok ?
|
|
5193
|
-
console.log(` ${mark} ${
|
|
5232
|
+
const mark = ok ? import_chalk33.default.green("\u2713") : import_chalk33.default.red("\u2717");
|
|
5233
|
+
console.log(` ${mark} ${import_chalk33.default.cyan(e.name)} ${import_chalk33.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
|
|
5234
|
+
if (isVerbose() && e.method && e.url) {
|
|
5235
|
+
const auth = dpKey ? ` \\
|
|
5236
|
+
-H 'X-API-Key: ${maskSecret(dpKey)}'` : "";
|
|
5237
|
+
console.log(import_chalk33.default.dim(` curl -sS -X ${e.method} '${e.url}'${auth}`));
|
|
5238
|
+
}
|
|
5194
5239
|
}
|
|
5195
5240
|
}
|
|
5196
5241
|
function billingLine(billing) {
|
|
5197
5242
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
5198
5243
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
5199
|
-
let line =
|
|
5244
|
+
let line = import_chalk33.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk33.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
5200
5245
|
if (typeof billing.credits_remaining === "number") {
|
|
5201
|
-
line +=
|
|
5246
|
+
line += import_chalk33.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
5202
5247
|
}
|
|
5203
5248
|
return line;
|
|
5204
5249
|
}
|
|
@@ -5207,27 +5252,18 @@ function freeBudgetWarning(billing, anon) {
|
|
|
5207
5252
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
5208
5253
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
5209
5254
|
if (left > 8) return null;
|
|
5210
|
-
if (left <= 0) return
|
|
5211
|
-
return
|
|
5255
|
+
if (left <= 0) return import_chalk33.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
5256
|
+
return import_chalk33.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
5212
5257
|
}
|
|
5213
5258
|
function printAssistant(delta) {
|
|
5214
5259
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
5215
5260
|
const m = delta[i];
|
|
5216
5261
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
5217
|
-
console.log("\n" +
|
|
5262
|
+
console.log("\n" + import_chalk33.default.green("assistant \u203A ") + m.content + "\n");
|
|
5218
5263
|
return;
|
|
5219
5264
|
}
|
|
5220
5265
|
}
|
|
5221
5266
|
}
|
|
5222
|
-
function verboseTrace(p, hasByo) {
|
|
5223
|
-
if (!isVerbose()) return;
|
|
5224
|
-
console.log(import_chalk32.default.dim("\n" + "\u2500".repeat(64)));
|
|
5225
|
-
console.log(import_chalk32.default.bold("--verbose: this turn POSTed to the data-plane MCP host"));
|
|
5226
|
-
console.log(import_chalk32.default.green(` curl -sS -X POST ${chatUrl(p)} \\`));
|
|
5227
|
-
console.log(import_chalk32.default.green(` -H "X-API-Key: ${maskSecret(p.dpKey)}" \\`));
|
|
5228
|
-
console.log(import_chalk32.default.green(" -H 'Content-Type: application/json' \\"));
|
|
5229
|
-
console.log(import_chalk32.default.green(` -d '{"turn_id":"\u2026","messages":[\u2026],"environment":"${p.environment}"${hasByo ? ',"llm_api_key":"***","llm_provider":"\u2026"' : ""}}'`));
|
|
5230
|
-
}
|
|
5231
5267
|
async function replTurn(p, messages, userText) {
|
|
5232
5268
|
messages.push({ role: "user", content: userText });
|
|
5233
5269
|
const llm2 = loadLlmConfig();
|
|
@@ -5249,7 +5285,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5249
5285
|
});
|
|
5250
5286
|
} catch (err) {
|
|
5251
5287
|
spinner.fail("Network error.");
|
|
5252
|
-
console.log(
|
|
5288
|
+
console.log(import_chalk33.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
5253
5289
|
return;
|
|
5254
5290
|
}
|
|
5255
5291
|
let data = null;
|
|
@@ -5267,16 +5303,16 @@ async function replTurn(p, messages, userText) {
|
|
|
5267
5303
|
return;
|
|
5268
5304
|
}
|
|
5269
5305
|
if (res.status === 401) {
|
|
5270
|
-
console.log(
|
|
5306
|
+
console.log(import_chalk33.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
|
|
5271
5307
|
return;
|
|
5272
5308
|
}
|
|
5273
5309
|
if (!res.ok || !data) {
|
|
5274
5310
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
5275
|
-
console.log(
|
|
5311
|
+
console.log(import_chalk33.default.red(` Chat error: ${err}`));
|
|
5276
5312
|
return;
|
|
5277
5313
|
}
|
|
5278
5314
|
if (Array.isArray(data.delta)) {
|
|
5279
|
-
renderToolEvents(data.tool_events);
|
|
5315
|
+
renderToolEvents(data.tool_events, p.dpKey);
|
|
5280
5316
|
for (const m of data.delta) messages.push(m);
|
|
5281
5317
|
printAssistant(data.delta);
|
|
5282
5318
|
}
|
|
@@ -5284,39 +5320,38 @@ async function replTurn(p, messages, userText) {
|
|
|
5284
5320
|
if (bl) console.log(bl);
|
|
5285
5321
|
const warn = freeBudgetWarning(data.billing, p.anon);
|
|
5286
5322
|
if (warn) console.log(warn);
|
|
5287
|
-
verboseTrace(p, !!llm2);
|
|
5288
5323
|
if (!data.continue) return;
|
|
5289
5324
|
}
|
|
5290
|
-
console.log(
|
|
5325
|
+
console.log(import_chalk33.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
5291
5326
|
}
|
|
5292
5327
|
function renderUpsell(p, upsell) {
|
|
5293
5328
|
const loggedIn = !!loadCredentials();
|
|
5294
|
-
console.log("\n" +
|
|
5329
|
+
console.log("\n" + import_chalk33.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
5295
5330
|
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
5296
5331
|
if (!loggedIn) {
|
|
5297
|
-
console.log(
|
|
5332
|
+
console.log(import_chalk33.default.dim(" Options: `/login` for free login credit and higher limits, or `apiblaze llm set-key` to bring your own model key."));
|
|
5298
5333
|
} else {
|
|
5299
|
-
console.log(
|
|
5334
|
+
console.log(import_chalk33.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
|
|
5300
5335
|
}
|
|
5301
5336
|
} else if (upsell.reason === "INFLIGHT") {
|
|
5302
|
-
console.log(
|
|
5337
|
+
console.log(import_chalk33.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
5303
5338
|
}
|
|
5304
5339
|
console.log();
|
|
5305
5340
|
}
|
|
5306
|
-
var apichatsPath = () =>
|
|
5341
|
+
var apichatsPath = () => path6.join(getApiblazeDir(), "apichats.json");
|
|
5307
5342
|
function loadApichats() {
|
|
5308
5343
|
try {
|
|
5309
|
-
const list = JSON.parse(
|
|
5344
|
+
const list = JSON.parse(fs9.readFileSync(apichatsPath(), "utf-8"));
|
|
5310
5345
|
return Array.isArray(list) ? list : [];
|
|
5311
5346
|
} catch {
|
|
5312
5347
|
return [];
|
|
5313
5348
|
}
|
|
5314
5349
|
}
|
|
5315
5350
|
function writeApichats(list) {
|
|
5316
|
-
|
|
5317
|
-
|
|
5351
|
+
fs9.mkdirSync(getApiblazeDir(), { recursive: true });
|
|
5352
|
+
fs9.writeFileSync(apichatsPath(), JSON.stringify(list, null, 2), "utf-8");
|
|
5318
5353
|
try {
|
|
5319
|
-
|
|
5354
|
+
fs9.chmodSync(apichatsPath(), 384);
|
|
5320
5355
|
} catch {
|
|
5321
5356
|
}
|
|
5322
5357
|
}
|
|
@@ -5348,21 +5383,89 @@ function claimApichat(p, ownerUserId) {
|
|
|
5348
5383
|
list[i].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5349
5384
|
writeApichats(list);
|
|
5350
5385
|
}
|
|
5386
|
+
async function mintDurableProxyKey(teamId, tenant2) {
|
|
5387
|
+
const out = await admin({
|
|
5388
|
+
method: "POST",
|
|
5389
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/admin/keys`,
|
|
5390
|
+
body: { key_type: "apiblaze-consumer-admin", environment: "prod" },
|
|
5391
|
+
// no expiry → durable
|
|
5392
|
+
summary: "Mint a durable data-plane key for apichat"
|
|
5393
|
+
});
|
|
5394
|
+
if (!out?.key) throw new Error("key mint returned no key");
|
|
5395
|
+
return out.key;
|
|
5396
|
+
}
|
|
5397
|
+
async function openServerProxy(project) {
|
|
5398
|
+
const version2 = project.apiVersion || "1.0.0";
|
|
5399
|
+
const environment = "prod";
|
|
5400
|
+
const mcpHost = `${project.projectId}.mcp.abz.run`;
|
|
5401
|
+
const tenant2 = project.tenant || project.projectId;
|
|
5402
|
+
const me = loadCredentials()?.apiblazeUserId;
|
|
5403
|
+
const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
|
|
5404
|
+
let dpKey = prior?.dpKey;
|
|
5405
|
+
if (!dpKey) {
|
|
5406
|
+
console.log(import_chalk33.default.dim(` Minting an API key for tenant ${import_chalk33.default.bold(tenant2)} to query project ${import_chalk33.default.bold(project.projectName)}\u2026`));
|
|
5407
|
+
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
5408
|
+
console.log(` ${import_chalk33.default.green("\u2714")} API key: ${import_chalk33.default.dim(maskSecret(dpKey))}`);
|
|
5409
|
+
}
|
|
5410
|
+
const p = {
|
|
5411
|
+
projectId: project.projectId,
|
|
5412
|
+
version: version2,
|
|
5413
|
+
environment,
|
|
5414
|
+
dpKey,
|
|
5415
|
+
mcpHost,
|
|
5416
|
+
proxyUrl: `https://${project.projectId}.abz.run/${version2}/${environment}`,
|
|
5417
|
+
anon: false
|
|
5418
|
+
};
|
|
5419
|
+
const spinner = (0, import_ora16.default)("Preparing the chat\u2026").start();
|
|
5420
|
+
try {
|
|
5421
|
+
const raw = await admin({
|
|
5422
|
+
method: "GET",
|
|
5423
|
+
path: `/projects/${project.projectId}/${version2}/openapi`,
|
|
5424
|
+
summary: "Fetch OpenAPI spec"
|
|
5425
|
+
}).catch(() => null);
|
|
5426
|
+
const spec2 = raw && (raw.spec ?? raw);
|
|
5427
|
+
spinner.stop();
|
|
5428
|
+
if (spec2 && (spec2.paths || spec2.openapi)) {
|
|
5429
|
+
await publishMcp(p, spec2);
|
|
5430
|
+
} else {
|
|
5431
|
+
console.log(import_chalk33.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
|
|
5432
|
+
}
|
|
5433
|
+
} catch (err) {
|
|
5434
|
+
spinner.fail("Could not open the proxy.");
|
|
5435
|
+
throw err;
|
|
5436
|
+
}
|
|
5437
|
+
upsertApichat({
|
|
5438
|
+
name: project.projectId,
|
|
5439
|
+
target: project.projectName,
|
|
5440
|
+
projectId: project.projectId,
|
|
5441
|
+
version: version2,
|
|
5442
|
+
environment,
|
|
5443
|
+
mcpHost,
|
|
5444
|
+
dpKey,
|
|
5445
|
+
anon: false,
|
|
5446
|
+
ownerUserId: me,
|
|
5447
|
+
createdAt: prior?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5448
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5449
|
+
messages: prior?.messages
|
|
5450
|
+
// preserve any earlier transcript; this session starts fresh
|
|
5451
|
+
});
|
|
5452
|
+
return { p, messages: [] };
|
|
5453
|
+
}
|
|
5351
5454
|
function discoverLocalSpecs() {
|
|
5352
5455
|
const cwd = process.cwd();
|
|
5353
5456
|
const known = ["openapi.yaml", "openapi.yml", "openapi.json", "swagger.yaml", "swagger.yml", "swagger.json", "api.yaml", "api.yml", "api.json"];
|
|
5354
5457
|
const found = [];
|
|
5355
5458
|
for (const n of known) {
|
|
5356
5459
|
try {
|
|
5357
|
-
if (
|
|
5460
|
+
if (fs9.statSync(path6.join(cwd, n)).isFile()) found.push(n);
|
|
5358
5461
|
} catch {
|
|
5359
5462
|
}
|
|
5360
5463
|
}
|
|
5361
5464
|
try {
|
|
5362
|
-
const files =
|
|
5465
|
+
const files = fs9.readdirSync(cwd).filter((f) => /\.(ya?ml|json)$/i.test(f) && !found.includes(f));
|
|
5363
5466
|
for (const f of files.slice(0, 60)) {
|
|
5364
5467
|
try {
|
|
5365
|
-
const head =
|
|
5468
|
+
const head = fs9.readFileSync(path6.join(cwd, f), "utf-8").slice(0, 4e3);
|
|
5366
5469
|
if (/["']?openapi["']?\s*:/i.test(head) || /["']?swagger["']?\s*:/i.test(head) || /^\s*paths\s*:/im.test(head) || /"paths"\s*:/.test(head)) {
|
|
5367
5470
|
found.push(f);
|
|
5368
5471
|
}
|
|
@@ -5378,10 +5481,24 @@ async function noArgsMenu(opts) {
|
|
|
5378
5481
|
const me = loadCredentials()?.apiblazeUserId;
|
|
5379
5482
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
5380
5483
|
const choices = saved.map((a) => ({
|
|
5381
|
-
name: `Chat with ${
|
|
5484
|
+
name: `Chat with ${import_chalk33.default.bold(a.name)} ${import_chalk33.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
|
|
5382
5485
|
value: { type: "existing", a }
|
|
5383
5486
|
}));
|
|
5384
|
-
|
|
5487
|
+
const creds = loadCredentials();
|
|
5488
|
+
if (creds?.teamId) {
|
|
5489
|
+
const savedIds = new Set(saved.map((a) => a.projectId));
|
|
5490
|
+
try {
|
|
5491
|
+
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
5492
|
+
for (const pr of proxies) {
|
|
5493
|
+
choices.push({
|
|
5494
|
+
name: `Chat with ${import_chalk33.default.bold(pr.projectName)} ${import_chalk33.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
5495
|
+
value: { type: "server", project: pr }
|
|
5496
|
+
});
|
|
5497
|
+
}
|
|
5498
|
+
} catch {
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
choices.push({ name: import_chalk33.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
5385
5502
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
5386
5503
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
5387
5504
|
]);
|
|
@@ -5413,6 +5530,9 @@ async function noArgsMenu(opts) {
|
|
|
5413
5530
|
};
|
|
5414
5531
|
return { p, messages };
|
|
5415
5532
|
}
|
|
5533
|
+
if (pick2.type === "server") {
|
|
5534
|
+
return await openServerProxy(pick2.project);
|
|
5535
|
+
}
|
|
5416
5536
|
const { source } = await inquirer3.prompt([
|
|
5417
5537
|
{
|
|
5418
5538
|
type: "list",
|
|
@@ -5450,31 +5570,31 @@ async function noArgsMenu(opts) {
|
|
|
5450
5570
|
async function runRepl(p, initialMessages) {
|
|
5451
5571
|
const { default: inquirer3 } = await import("inquirer");
|
|
5452
5572
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
5453
|
-
console.log("\n" +
|
|
5454
|
-
if (messages.length) console.log(
|
|
5573
|
+
console.log("\n" + import_chalk33.default.cyan.bold("Chat with your API") + import_chalk33.default.dim(` \xB7 ${p.mcpHost}`));
|
|
5574
|
+
if (messages.length) console.log(import_chalk33.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
5455
5575
|
const llm2 = loadLlmConfig();
|
|
5456
5576
|
console.log(
|
|
5457
|
-
|
|
5577
|
+
import_chalk33.default.dim(
|
|
5458
5578
|
llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for credit \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
|
|
5459
5579
|
)
|
|
5460
5580
|
);
|
|
5461
5581
|
for (; ; ) {
|
|
5462
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
5582
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk33.default.green("you \u203A") }]);
|
|
5463
5583
|
const text = (input ?? "").trim();
|
|
5464
5584
|
if (!text) continue;
|
|
5465
5585
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
5466
5586
|
if (text === "/login") {
|
|
5467
5587
|
try {
|
|
5468
5588
|
await runLogin();
|
|
5469
|
-
console.log(
|
|
5589
|
+
console.log(import_chalk33.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
5470
5590
|
} catch (err) {
|
|
5471
|
-
console.log(
|
|
5591
|
+
console.log(import_chalk33.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5472
5592
|
}
|
|
5473
5593
|
continue;
|
|
5474
5594
|
}
|
|
5475
5595
|
if (text === "/claim") {
|
|
5476
5596
|
if (!loadCredentials()) {
|
|
5477
|
-
console.log(
|
|
5597
|
+
console.log(import_chalk33.default.yellow(" Log in first: `/login`, then `/claim`."));
|
|
5478
5598
|
continue;
|
|
5479
5599
|
}
|
|
5480
5600
|
try {
|
|
@@ -5483,24 +5603,24 @@ async function runRepl(p, initialMessages) {
|
|
|
5483
5603
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
5484
5604
|
p.anon = false;
|
|
5485
5605
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
5486
|
-
console.log(
|
|
5606
|
+
console.log(import_chalk33.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
5487
5607
|
}
|
|
5488
5608
|
} catch (err) {
|
|
5489
|
-
console.log(
|
|
5609
|
+
console.log(import_chalk33.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5490
5610
|
}
|
|
5491
5611
|
continue;
|
|
5492
5612
|
}
|
|
5493
5613
|
if (text.startsWith("/")) {
|
|
5494
|
-
console.log(
|
|
5614
|
+
console.log(import_chalk33.default.dim(" Commands: /login /claim /exit"));
|
|
5495
5615
|
continue;
|
|
5496
5616
|
}
|
|
5497
5617
|
await replTurn(p, messages, text);
|
|
5498
5618
|
saveTranscript(p, messages);
|
|
5499
5619
|
}
|
|
5500
|
-
console.log(
|
|
5620
|
+
console.log(import_chalk33.default.dim("\nBye."));
|
|
5501
5621
|
}
|
|
5502
5622
|
async function runApichat(opts) {
|
|
5503
|
-
console.log(
|
|
5623
|
+
console.log(import_chalk33.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
5504
5624
|
if (!opts.openapispec && !opts.target) {
|
|
5505
5625
|
if (!process.stdin.isTTY) {
|
|
5506
5626
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -5513,7 +5633,7 @@ async function runApichat(opts) {
|
|
|
5513
5633
|
}
|
|
5514
5634
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
5515
5635
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
5516
|
-
console.log(` ${
|
|
5636
|
+
console.log(` ${import_chalk33.default.dim("Target:")} ${import_chalk33.default.bold(target)}`);
|
|
5517
5637
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
5518
5638
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
5519
5639
|
fail4(
|
|
@@ -5522,7 +5642,7 @@ async function runApichat(opts) {
|
|
|
5522
5642
|
);
|
|
5523
5643
|
}
|
|
5524
5644
|
const p = await provision(spec2, target, opts);
|
|
5525
|
-
console.log(` ${
|
|
5645
|
+
console.log(` ${import_chalk33.default.dim("Proxy: ")} ${import_chalk33.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
5526
5646
|
upsertApichat({
|
|
5527
5647
|
name: p.projectId,
|
|
5528
5648
|
target,
|
|
@@ -5541,22 +5661,22 @@ async function runApichat(opts) {
|
|
|
5541
5661
|
const secret = await captureTargetSecret(auth, opts);
|
|
5542
5662
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
5543
5663
|
} else {
|
|
5544
|
-
console.log(
|
|
5664
|
+
console.log(import_chalk33.default.dim(" Target auth: none required."));
|
|
5545
5665
|
}
|
|
5546
5666
|
const specText = JSON.stringify(spec2);
|
|
5547
5667
|
await uploadSpec(p, specText, opts);
|
|
5548
5668
|
const mcpUrl = await publishMcp(p, spec2);
|
|
5549
5669
|
console.log();
|
|
5550
|
-
if (p.proxyUrl) console.log(` ${
|
|
5551
|
-
if (mcpUrl) console.log(` ${
|
|
5670
|
+
if (p.proxyUrl) console.log(` ${import_chalk33.default.green("\u2713")} proxy ${import_chalk33.default.bold(p.proxyUrl)}`);
|
|
5671
|
+
if (mcpUrl) console.log(` ${import_chalk33.default.green("\u2713")} mcp ${import_chalk33.default.bold(mcpUrl)}`);
|
|
5552
5672
|
if (p.anon) {
|
|
5553
|
-
console.log(
|
|
5673
|
+
console.log(import_chalk33.default.dim("\n Anonymous workspace \u2014 /claim inside the chat (after /login) to keep it beyond 30 days."));
|
|
5554
5674
|
}
|
|
5555
5675
|
await runRepl(p);
|
|
5556
5676
|
}
|
|
5557
5677
|
|
|
5558
5678
|
// src/commands/consumer.ts
|
|
5559
|
-
var
|
|
5679
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
5560
5680
|
var import_ora17 = __toESM(require("ora"));
|
|
5561
5681
|
init_admin();
|
|
5562
5682
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
@@ -5577,7 +5697,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
5577
5697
|
function requireConsumer() {
|
|
5578
5698
|
const c = loadConsumer();
|
|
5579
5699
|
if (!c) {
|
|
5580
|
-
console.error(
|
|
5700
|
+
console.error(import_chalk34.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
5581
5701
|
process.exit(1);
|
|
5582
5702
|
}
|
|
5583
5703
|
return c;
|
|
@@ -5588,7 +5708,7 @@ async function runConsumerLogin(opts) {
|
|
|
5588
5708
|
let clientId = opts.client;
|
|
5589
5709
|
if (clientId) {
|
|
5590
5710
|
if (!tenant2) {
|
|
5591
|
-
console.error(
|
|
5711
|
+
console.error(import_chalk34.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
5592
5712
|
process.exit(1);
|
|
5593
5713
|
}
|
|
5594
5714
|
} else {
|
|
@@ -5606,19 +5726,19 @@ async function runConsumerLogin(opts) {
|
|
|
5606
5726
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
5607
5727
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
5608
5728
|
if (!pick2) {
|
|
5609
|
-
console.error(
|
|
5729
|
+
console.error(import_chalk34.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
5610
5730
|
process.exit(1);
|
|
5611
5731
|
}
|
|
5612
5732
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
5613
5733
|
}
|
|
5614
5734
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
5615
|
-
console.log(`${
|
|
5735
|
+
console.log(`${import_chalk34.default.cyan("\u2192")} Logging in to ${import_chalk34.default.bold(tenant2)} as a consumer...`);
|
|
5616
5736
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
5617
5737
|
console.log(`
|
|
5618
|
-
Open: ${
|
|
5619
|
-
console.log(` Code: ${
|
|
5738
|
+
Open: ${import_chalk34.default.underline(verificationUri)}`);
|
|
5739
|
+
console.log(` Code: ${import_chalk34.default.bold(userCode)}
|
|
5620
5740
|
`);
|
|
5621
|
-
console.log(
|
|
5741
|
+
console.log(import_chalk34.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
5622
5742
|
}, portalResource);
|
|
5623
5743
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
5624
5744
|
const creds = {
|
|
@@ -5633,7 +5753,7 @@ async function runConsumerLogin(opts) {
|
|
|
5633
5753
|
obtainedAt: Date.now()
|
|
5634
5754
|
};
|
|
5635
5755
|
saveConsumer(creds);
|
|
5636
|
-
console.log(
|
|
5756
|
+
console.log(import_chalk34.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
5637
5757
|
}
|
|
5638
5758
|
async function runConsumerTokens(opts) {
|
|
5639
5759
|
const creds = requireConsumer();
|
|
@@ -5646,18 +5766,18 @@ async function runConsumerTokens(opts) {
|
|
|
5646
5766
|
console.log(JSON.stringify({ tenant: fresh.tenant, access_token: fresh.accessToken, refresh_token: fresh.refreshToken, id_token: fresh.idToken, expires_at: new Date(fresh.expiresAt).toISOString() }, null, 2));
|
|
5647
5767
|
return;
|
|
5648
5768
|
}
|
|
5649
|
-
console.log(`${
|
|
5769
|
+
console.log(`${import_chalk34.default.cyan("Consumer")} ${import_chalk34.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk34.default.bold(fresh.tenant)}
|
|
5650
5770
|
`);
|
|
5651
|
-
console.log(`${
|
|
5771
|
+
console.log(`${import_chalk34.default.bold("access_token")} ${import_chalk34.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
5652
5772
|
${fresh.accessToken}
|
|
5653
5773
|
`);
|
|
5654
|
-
if (fresh.idToken) console.log(`${
|
|
5774
|
+
if (fresh.idToken) console.log(`${import_chalk34.default.bold("id_token")} ${import_chalk34.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
5655
5775
|
${fresh.idToken}
|
|
5656
5776
|
`);
|
|
5657
|
-
if (fresh.refreshToken) console.log(`${
|
|
5777
|
+
if (fresh.refreshToken) console.log(`${import_chalk34.default.bold("refresh_token")}
|
|
5658
5778
|
${fresh.refreshToken}
|
|
5659
5779
|
`);
|
|
5660
|
-
console.log(
|
|
5780
|
+
console.log(import_chalk34.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
5661
5781
|
}
|
|
5662
5782
|
async function runConsumerApikeys(opts) {
|
|
5663
5783
|
const creds = requireConsumer();
|
|
@@ -5667,8 +5787,8 @@ async function runConsumerApikeys(opts) {
|
|
|
5667
5787
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
5668
5788
|
spinner.stop();
|
|
5669
5789
|
if (list.status >= 400) {
|
|
5670
|
-
console.error(
|
|
5671
|
-
if (list.status === 401) console.error(
|
|
5790
|
+
console.error(import_chalk34.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
5791
|
+
if (list.status === 401) console.error(import_chalk34.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
5672
5792
|
process.exit(1);
|
|
5673
5793
|
}
|
|
5674
5794
|
const keys = list.data?.keys ?? [];
|
|
@@ -5676,16 +5796,16 @@ async function runConsumerApikeys(opts) {
|
|
|
5676
5796
|
if (opts.json) {
|
|
5677
5797
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
5678
5798
|
} else if (!keys.length) {
|
|
5679
|
-
console.log(
|
|
5799
|
+
console.log(import_chalk34.default.yellow("No API keys yet."));
|
|
5680
5800
|
} else {
|
|
5681
5801
|
for (const k of keys) {
|
|
5682
5802
|
const clear = revealMap[k.environment]?.key;
|
|
5683
|
-
const shown = clear ?
|
|
5684
|
-
const exp = k.expires_at ?
|
|
5685
|
-
console.log(` ${
|
|
5803
|
+
const shown = clear ? import_chalk34.default.green(clear) : import_chalk34.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
5804
|
+
const exp = k.expires_at ? import_chalk34.default.dim(`exp ${k.expires_at}`) : import_chalk34.default.dim("no expiry");
|
|
5805
|
+
console.log(` ${import_chalk34.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk34.default.dim(k.description ?? "")}`);
|
|
5686
5806
|
}
|
|
5687
5807
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
5688
|
-
console.log(
|
|
5808
|
+
console.log(import_chalk34.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
5689
5809
|
}
|
|
5690
5810
|
}
|
|
5691
5811
|
if (opts.json) return;
|
|
@@ -5707,34 +5827,34 @@ async function runConsumerApikeys(opts) {
|
|
|
5707
5827
|
}
|
|
5708
5828
|
s2.succeed("Key created.");
|
|
5709
5829
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
5710
|
-
if (key) console.log(` ${
|
|
5711
|
-
else console.log(
|
|
5830
|
+
if (key) console.log(` ${import_chalk34.default.green(key)} ${import_chalk34.default.dim("(shown once \u2014 store it now)")}`);
|
|
5831
|
+
else console.log(import_chalk34.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
5712
5832
|
}
|
|
5713
5833
|
|
|
5714
5834
|
// src/commands/sidecar.ts
|
|
5715
|
-
var
|
|
5835
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
5716
5836
|
var import_ora18 = __toESM(require("ora"));
|
|
5717
|
-
var
|
|
5718
|
-
var
|
|
5837
|
+
var fs10 = __toESM(require("fs"));
|
|
5838
|
+
var path7 = __toESM(require("path"));
|
|
5719
5839
|
init_admin();
|
|
5720
5840
|
init_auth();
|
|
5721
5841
|
function detectNextProject(root) {
|
|
5722
|
-
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) =>
|
|
5842
|
+
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs10.existsSync(path7.join(root, f)));
|
|
5723
5843
|
let hasDep = false;
|
|
5724
5844
|
try {
|
|
5725
|
-
const pkg = JSON.parse(
|
|
5845
|
+
const pkg = JSON.parse(fs10.readFileSync(path7.join(root, "package.json"), "utf8"));
|
|
5726
5846
|
hasDep = !!(pkg.dependencies?.next || pkg.devDependencies?.next);
|
|
5727
5847
|
} catch {
|
|
5728
5848
|
}
|
|
5729
|
-
const appDir =
|
|
5730
|
-
const pagesDir =
|
|
5849
|
+
const appDir = fs10.existsSync(path7.join(root, "app")) || fs10.existsSync(path7.join(root, "src", "app"));
|
|
5850
|
+
const pagesDir = fs10.existsSync(path7.join(root, "pages")) || fs10.existsSync(path7.join(root, "src", "pages"));
|
|
5731
5851
|
return { found: hasConfig || hasDep || appDir || pagesDir, router: appDir ? "app" : pagesDir ? "pages" : null };
|
|
5732
5852
|
}
|
|
5733
5853
|
function upsertEnvLocal(root, token) {
|
|
5734
|
-
const p =
|
|
5854
|
+
const p = path7.join(root, ".env.local");
|
|
5735
5855
|
let existing = "";
|
|
5736
5856
|
try {
|
|
5737
|
-
existing =
|
|
5857
|
+
existing = fs10.readFileSync(p, "utf8");
|
|
5738
5858
|
} catch {
|
|
5739
5859
|
}
|
|
5740
5860
|
const had = /^APIBLAZE_API_KEY=/m.test(existing) || /^APIBLAZE_TOKEN=/m.test(existing);
|
|
@@ -5749,15 +5869,15 @@ function upsertEnvLocal(root, token) {
|
|
|
5749
5869
|
next = (next.endsWith("\n") ? next : next + "\n") + `APIBLAZE_SIDECAR_VERBOSE=true
|
|
5750
5870
|
`;
|
|
5751
5871
|
}
|
|
5752
|
-
|
|
5872
|
+
fs10.writeFileSync(p, next);
|
|
5753
5873
|
return had ? "rotated" : "created";
|
|
5754
5874
|
}
|
|
5755
5875
|
function installSidecarPackage(root) {
|
|
5756
|
-
if (
|
|
5757
|
-
console.log(` ${
|
|
5876
|
+
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
5877
|
+
console.log(` ${import_chalk35.default.green("\u2713")} apiblaze package already installed`);
|
|
5758
5878
|
return;
|
|
5759
5879
|
}
|
|
5760
|
-
const has = (f) =>
|
|
5880
|
+
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
5761
5881
|
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" };
|
|
5762
5882
|
const spinner = (0, import_ora18.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
5763
5883
|
try {
|
|
@@ -5765,12 +5885,12 @@ function installSidecarPackage(root) {
|
|
|
5765
5885
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
5766
5886
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
5767
5887
|
} catch {
|
|
5768
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
5888
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk35.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk35.default.cyan("npm run dev")}.`);
|
|
5769
5889
|
}
|
|
5770
5890
|
}
|
|
5771
5891
|
function readEnvKey(root) {
|
|
5772
5892
|
try {
|
|
5773
|
-
const s =
|
|
5893
|
+
const s = fs10.readFileSync(path7.join(root, ".env.local"), "utf8");
|
|
5774
5894
|
const m = s.match(/^APIBLAZE_API_KEY=(.+)$/m) ?? s.match(/^APIBLAZE_TOKEN=(.+)$/m);
|
|
5775
5895
|
return m ? m[1].trim() : null;
|
|
5776
5896
|
} catch {
|
|
@@ -5778,16 +5898,16 @@ function readEnvKey(root) {
|
|
|
5778
5898
|
}
|
|
5779
5899
|
}
|
|
5780
5900
|
function ensureGitignored(root) {
|
|
5781
|
-
const p =
|
|
5901
|
+
const p = path7.join(root, ".gitignore");
|
|
5782
5902
|
let c = "";
|
|
5783
5903
|
try {
|
|
5784
|
-
c =
|
|
5904
|
+
c = fs10.readFileSync(p, "utf8");
|
|
5785
5905
|
} catch {
|
|
5786
5906
|
}
|
|
5787
|
-
if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c))
|
|
5907
|
+
if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c)) fs10.writeFileSync(p, (c && !c.endsWith("\n") ? c + "\n" : c) + ".env.local\n");
|
|
5788
5908
|
}
|
|
5789
5909
|
function wireInstrumentation(root) {
|
|
5790
|
-
const existing = ["instrumentation.ts", "instrumentation.js",
|
|
5910
|
+
const existing = ["instrumentation.ts", "instrumentation.js", path7.join("src", "instrumentation.ts")].map((c) => path7.join(root, c)).find((f) => fs10.existsSync(f));
|
|
5791
5911
|
const body = `import { register as apiblaze } from "apiblaze/sidecar";
|
|
5792
5912
|
|
|
5793
5913
|
export function register() {
|
|
@@ -5795,18 +5915,18 @@ export function register() {
|
|
|
5795
5915
|
}
|
|
5796
5916
|
`;
|
|
5797
5917
|
if (!existing) {
|
|
5798
|
-
|
|
5918
|
+
fs10.writeFileSync(path7.join(root, "instrumentation.ts"), body);
|
|
5799
5919
|
return "created";
|
|
5800
5920
|
}
|
|
5801
|
-
const cur =
|
|
5921
|
+
const cur = fs10.readFileSync(existing, "utf8");
|
|
5802
5922
|
if (cur.includes("apiblaze/sidecar")) return "present";
|
|
5803
5923
|
if (/export\s+function\s+register\s*\(/.test(cur)) {
|
|
5804
|
-
|
|
5924
|
+
fs10.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
|
|
5805
5925
|
` + cur.replace(/export\s+function\s+register\s*\(\s*\)\s*\{/, (m) => `${m}
|
|
5806
5926
|
apiblaze();`));
|
|
5807
5927
|
return "patched";
|
|
5808
5928
|
}
|
|
5809
|
-
|
|
5929
|
+
fs10.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
|
|
5810
5930
|
${cur}
|
|
5811
5931
|
// call apiblaze() inside your register() export.
|
|
5812
5932
|
`);
|
|
@@ -5885,17 +6005,17 @@ export default async function Page() {
|
|
|
5885
6005
|
function generateInspector(root, router) {
|
|
5886
6006
|
try {
|
|
5887
6007
|
if (router === "pages") {
|
|
5888
|
-
const dir2 =
|
|
5889
|
-
const f2 =
|
|
5890
|
-
|
|
5891
|
-
return
|
|
5892
|
-
}
|
|
5893
|
-
const base =
|
|
5894
|
-
const dir =
|
|
5895
|
-
|
|
5896
|
-
const f =
|
|
5897
|
-
|
|
5898
|
-
return
|
|
6008
|
+
const dir2 = fs10.existsSync(path7.join(root, "src", "pages")) ? path7.join(root, "src", "pages") : path7.join(root, "pages");
|
|
6009
|
+
const f2 = path7.join(dir2, "abz-inspector.tsx");
|
|
6010
|
+
fs10.writeFileSync(f2, INSPECTOR_PAGE);
|
|
6011
|
+
return path7.relative(root, f2);
|
|
6012
|
+
}
|
|
6013
|
+
const base = fs10.existsSync(path7.join(root, "src", "app")) ? path7.join(root, "src", "app") : path7.join(root, "app");
|
|
6014
|
+
const dir = path7.join(base, "abz-inspector");
|
|
6015
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
6016
|
+
const f = path7.join(dir, "page.tsx");
|
|
6017
|
+
fs10.writeFileSync(f, INSPECTOR_PAGE);
|
|
6018
|
+
return path7.relative(root, f);
|
|
5899
6019
|
} catch {
|
|
5900
6020
|
return null;
|
|
5901
6021
|
}
|
|
@@ -5916,29 +6036,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
5916
6036
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
5917
6037
|
const envState = upsertEnvLocal(root, out.token);
|
|
5918
6038
|
ensureGitignored(root);
|
|
5919
|
-
console.log(` ${
|
|
5920
|
-
console.log(` ${
|
|
6039
|
+
console.log(` ${import_chalk35.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6040
|
+
console.log(` ${import_chalk35.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
5921
6041
|
installSidecarPackage(root);
|
|
5922
6042
|
let inspectorPath = null;
|
|
5923
6043
|
if (!opts.noInspector) {
|
|
5924
6044
|
inspectorPath = generateInspector(root, router);
|
|
5925
|
-
if (inspectorPath) console.log(` ${
|
|
6045
|
+
if (inspectorPath) console.log(` ${import_chalk35.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
5926
6046
|
}
|
|
5927
6047
|
console.log("");
|
|
5928
|
-
console.log(
|
|
5929
|
-
console.log(` 1. ${
|
|
6048
|
+
console.log(import_chalk35.default.bold("Done (no account needed). What happens next:"));
|
|
6049
|
+
console.log(` 1. ${import_chalk35.default.cyan("npm run dev")} and use your app.`);
|
|
5930
6050
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
5931
|
-
console.log(` ${
|
|
6051
|
+
console.log(` ${import_chalk35.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
5932
6052
|
console.log("");
|
|
5933
|
-
console.log(
|
|
5934
|
-
console.log(` ${
|
|
5935
|
-
console.log(
|
|
6053
|
+
console.log(import_chalk35.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
6054
|
+
console.log(` ${import_chalk35.default.cyan("apiblaze login")} then ${import_chalk35.default.cyan("apiblaze claim")} ${import_chalk35.default.dim("(no code needed here)")}`);
|
|
6055
|
+
console.log(import_chalk35.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
5936
6056
|
}
|
|
5937
6057
|
async function runSidecar(opts) {
|
|
5938
|
-
const root =
|
|
6058
|
+
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
5939
6059
|
const detected = detectNextProject(root);
|
|
5940
6060
|
if (!detected.found) {
|
|
5941
|
-
console.log(
|
|
6061
|
+
console.log(import_chalk35.default.yellow(`No Next.js project detected in ${root}.`));
|
|
5942
6062
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
5943
6063
|
return;
|
|
5944
6064
|
}
|
|
@@ -5949,10 +6069,10 @@ async function runSidecar(opts) {
|
|
|
5949
6069
|
if (!loadCredentials()) {
|
|
5950
6070
|
upsertEnvLocal(root, readEnvKey(root));
|
|
5951
6071
|
ensureGitignored(root);
|
|
5952
|
-
console.log(` ${
|
|
5953
|
-
console.log(` ${
|
|
6072
|
+
console.log(` ${import_chalk35.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
6073
|
+
console.log(` ${import_chalk35.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
5954
6074
|
installSidecarPackage(root);
|
|
5955
|
-
console.log(
|
|
6075
|
+
console.log(import_chalk35.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
5956
6076
|
return;
|
|
5957
6077
|
}
|
|
5958
6078
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -5975,38 +6095,38 @@ async function runSidecar(opts) {
|
|
|
5975
6095
|
throw err;
|
|
5976
6096
|
}
|
|
5977
6097
|
} else {
|
|
5978
|
-
console.log(
|
|
6098
|
+
console.log(import_chalk35.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
5979
6099
|
}
|
|
5980
6100
|
const envState = upsertEnvLocal(root, token);
|
|
5981
6101
|
ensureGitignored(root);
|
|
5982
|
-
console.log(` ${
|
|
6102
|
+
console.log(` ${import_chalk35.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
5983
6103
|
const wireState = wireInstrumentation(root);
|
|
5984
|
-
console.log(` ${
|
|
6104
|
+
console.log(` ${import_chalk35.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
5985
6105
|
installSidecarPackage(root);
|
|
5986
6106
|
let inspectorPath = null;
|
|
5987
6107
|
if (!opts.noInspector) {
|
|
5988
6108
|
inspectorPath = generateInspector(root, detected.router);
|
|
5989
|
-
if (inspectorPath) console.log(` ${
|
|
6109
|
+
if (inspectorPath) console.log(` ${import_chalk35.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
5990
6110
|
}
|
|
5991
6111
|
console.log("");
|
|
5992
|
-
console.log(
|
|
5993
|
-
console.log(` 1. ${
|
|
5994
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
5995
|
-
console.log(` 3. Approve the ones to route: ${
|
|
6112
|
+
console.log(import_chalk35.default.bold("Done. What happens next:"));
|
|
6113
|
+
console.log(` 1. ${import_chalk35.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
6114
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk35.default.bold("candidates")} \u2014 list them: ${import_chalk35.default.cyan("apiblaze sidecar")}`);
|
|
6115
|
+
console.log(` 3. Approve the ones to route: ${import_chalk35.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
5996
6116
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
5997
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
5998
|
-
if (switchingTeam) console.log(
|
|
6117
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk35.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
|
|
6118
|
+
if (switchingTeam) console.log(import_chalk35.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
5999
6119
|
console.log("");
|
|
6000
|
-
console.log(
|
|
6001
|
-
console.log(
|
|
6002
|
-
console.log(
|
|
6120
|
+
console.log(import_chalk35.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
6121
|
+
console.log(import_chalk35.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
6122
|
+
console.log(import_chalk35.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
6003
6123
|
console.log("");
|
|
6004
|
-
console.log(
|
|
6005
|
-
console.log(
|
|
6124
|
+
console.log(import_chalk35.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
6125
|
+
console.log(import_chalk35.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
6006
6126
|
}
|
|
6007
6127
|
|
|
6008
6128
|
// src/commands/origins.ts
|
|
6009
|
-
var
|
|
6129
|
+
var import_chalk36 = __toESM(require("chalk"));
|
|
6010
6130
|
var import_ora19 = __toESM(require("ora"));
|
|
6011
6131
|
init_admin();
|
|
6012
6132
|
init_auth();
|
|
@@ -6016,7 +6136,7 @@ async function runOriginsList(opts) {
|
|
|
6016
6136
|
if (!loadCredentials()) {
|
|
6017
6137
|
const cred = loadAnonCred();
|
|
6018
6138
|
if (!cred) {
|
|
6019
|
-
console.log(
|
|
6139
|
+
console.log(import_chalk36.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
6020
6140
|
return;
|
|
6021
6141
|
}
|
|
6022
6142
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -6034,27 +6154,27 @@ async function runOriginsList(opts) {
|
|
|
6034
6154
|
}
|
|
6035
6155
|
const routed = out.routed ?? [];
|
|
6036
6156
|
const candidates = out.candidates ?? [];
|
|
6037
|
-
console.log(
|
|
6157
|
+
console.log(import_chalk36.default.bold(`
|
|
6038
6158
|
Routed through APIblaze (${routed.length})`));
|
|
6039
|
-
if (!routed.length) console.log(
|
|
6040
|
-
for (const r of routed) console.log(` ${
|
|
6041
|
-
console.log(
|
|
6159
|
+
if (!routed.length) console.log(import_chalk36.default.dim(" none yet"));
|
|
6160
|
+
for (const r of routed) console.log(` ${import_chalk36.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk36.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
6161
|
+
console.log(import_chalk36.default.bold(`
|
|
6042
6162
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
6043
|
-
if (!candidates.length) console.log(
|
|
6163
|
+
if (!candidates.length) console.log(import_chalk36.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
6044
6164
|
for (const c of candidates) {
|
|
6045
|
-
console.log(` ${
|
|
6165
|
+
console.log(` ${import_chalk36.default.yellow("\u25CB")} ${c.origin} ${import_chalk36.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
6046
6166
|
}
|
|
6047
6167
|
if (candidates.length) {
|
|
6048
|
-
console.log(
|
|
6168
|
+
console.log(import_chalk36.default.dim(`
|
|
6049
6169
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
6050
|
-
console.log(
|
|
6170
|
+
console.log(import_chalk36.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
6051
6171
|
}
|
|
6052
6172
|
}
|
|
6053
6173
|
async function runOriginsApprove(origin, opts) {
|
|
6054
6174
|
if (!loadCredentials()) {
|
|
6055
6175
|
const cred = loadAnonCred();
|
|
6056
6176
|
if (!cred) {
|
|
6057
|
-
console.error(
|
|
6177
|
+
console.error(import_chalk36.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
6058
6178
|
process.exit(1);
|
|
6059
6179
|
}
|
|
6060
6180
|
const spinner2 = (0, import_ora19.default)(`Approving ${origin} (anonymous)...`).start();
|
|
@@ -6107,7 +6227,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
6107
6227
|
}
|
|
6108
6228
|
|
|
6109
6229
|
// src/commands/op.ts
|
|
6110
|
-
var
|
|
6230
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
6111
6231
|
init_auth();
|
|
6112
6232
|
init_trace();
|
|
6113
6233
|
init_types();
|
|
@@ -6140,82 +6260,82 @@ function printResidue(report, applied) {
|
|
|
6140
6260
|
const up = report?.upstash ?? {};
|
|
6141
6261
|
const fga = report?.fga ?? {};
|
|
6142
6262
|
const ghosts = report?.ghosts ?? {};
|
|
6143
|
-
console.log(
|
|
6144
|
-
console.log(
|
|
6263
|
+
console.log(import_chalk37.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
6264
|
+
console.log(import_chalk37.default.bold("\n Upstash"));
|
|
6145
6265
|
const orphans = up.orphans ?? [];
|
|
6146
|
-
if (orphans.length === 0) console.log(
|
|
6147
|
-
for (const o of orphans) console.log(` ${
|
|
6148
|
-
console.log(
|
|
6266
|
+
if (orphans.length === 0) console.log(import_chalk37.default.green(" no orphaned keys"));
|
|
6267
|
+
for (const o of orphans) console.log(` ${import_chalk37.default.yellow(o.key)} ${import_chalk37.default.dim(`\u2014 ${o.reason}`)}`);
|
|
6268
|
+
console.log(import_chalk37.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
6149
6269
|
if (up.anon_wallet_detail) {
|
|
6150
6270
|
const d = up.anon_wallet_detail;
|
|
6151
|
-
console.log(
|
|
6271
|
+
console.log(import_chalk37.default.dim(` anon wallets: ${d.count} ($${(d.total_cents / 100).toFixed(2)}), ${d.no_ttl} with NO TTL${d.no_ttl ? " \u26A0" : " (all self-expire)"}`));
|
|
6152
6272
|
}
|
|
6153
6273
|
if (up.keyspace_census) {
|
|
6154
6274
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
6155
|
-
console.log(
|
|
6275
|
+
console.log(import_chalk37.default.dim(` keyspace: ${census}`));
|
|
6156
6276
|
}
|
|
6157
|
-
if (up.unknown?.length) console.log(
|
|
6158
|
-
if (applied) console.log(` ${
|
|
6159
|
-
for (const e of up.errors ?? []) console.log(
|
|
6160
|
-
console.log(
|
|
6277
|
+
if (up.unknown?.length) console.log(import_chalk37.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
6278
|
+
if (applied) console.log(` ${import_chalk37.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
6279
|
+
for (const e of up.errors ?? []) console.log(import_chalk37.default.red(` error: ${e}`));
|
|
6280
|
+
console.log(import_chalk37.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
6161
6281
|
if (applied) {
|
|
6162
6282
|
const swept = fga?.swept ?? [];
|
|
6163
|
-
if (swept.length === 0) console.log(
|
|
6283
|
+
if (swept.length === 0) console.log(import_chalk37.default.green(" no orphaned stores"));
|
|
6164
6284
|
for (const s of swept) {
|
|
6165
6285
|
console.log(
|
|
6166
|
-
` ${
|
|
6286
|
+
` ${import_chalk37.default.yellow(s.store_id)} ${import_chalk37.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
6167
6287
|
);
|
|
6168
6288
|
}
|
|
6169
|
-
if (fga?.remaining) console.log(
|
|
6289
|
+
if (fga?.remaining) console.log(import_chalk37.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
6170
6290
|
const st = fga?.side_tables;
|
|
6171
|
-
if (st) console.log(
|
|
6291
|
+
if (st) console.log(import_chalk37.default.dim(` Neon side-tables purged: ${st.soft_deleted_stores} store records, ${st.orphan_models} models, ${st.orphan_changelog} changelog rows${st.error ? ` (${st.error})` : ""}`));
|
|
6172
6292
|
} else {
|
|
6173
6293
|
const fgaOrphans = fga?.orphans ?? [];
|
|
6174
|
-
if (fgaOrphans.length === 0) console.log(
|
|
6294
|
+
if (fgaOrphans.length === 0) console.log(import_chalk37.default.green(" no orphaned stores"));
|
|
6175
6295
|
for (const s of fgaOrphans) {
|
|
6176
6296
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
6177
|
-
console.log(` ${
|
|
6297
|
+
console.log(` ${import_chalk37.default.yellow(s.store_id)} ${import_chalk37.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
6178
6298
|
}
|
|
6179
|
-
console.log(
|
|
6299
|
+
console.log(import_chalk37.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
6180
6300
|
const st = fga?.side_tables;
|
|
6181
|
-
if (st) console.log(
|
|
6301
|
+
if (st) console.log(import_chalk37.default.dim(` Neon side-table residue: ${st.soft_deleted_stores} soft-deleted store records, ${st.orphan_models} orphan models, ${st.orphan_changelog} orphan changelog rows`));
|
|
6182
6302
|
}
|
|
6183
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
6184
|
-
console.log(
|
|
6303
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk37.default.red(` error: ${e}`));
|
|
6304
|
+
console.log(import_chalk37.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
6185
6305
|
if (applied) {
|
|
6186
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
6187
|
-
else console.log(` ${
|
|
6306
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk37.default.green(" no ghost tuples"));
|
|
6307
|
+
else console.log(` ${import_chalk37.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk37.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
6188
6308
|
} else {
|
|
6189
6309
|
const n = ghosts?.ghost_count ?? 0;
|
|
6190
|
-
if (n === 0) console.log(
|
|
6310
|
+
if (n === 0) console.log(import_chalk37.default.green(` no ghost tuples ${import_chalk37.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
6191
6311
|
else {
|
|
6192
|
-
console.log(
|
|
6312
|
+
console.log(import_chalk37.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
6193
6313
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
6194
|
-
console.log(
|
|
6314
|
+
console.log(import_chalk37.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
6195
6315
|
}
|
|
6196
|
-
if (n > 20) console.log(
|
|
6316
|
+
if (n > 20) console.log(import_chalk37.default.dim(` \u2026 and ${n - 20} more`));
|
|
6197
6317
|
}
|
|
6198
6318
|
}
|
|
6199
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
6319
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk37.default.red(` error: ${e}`));
|
|
6200
6320
|
console.log();
|
|
6201
6321
|
}
|
|
6202
6322
|
async function runOp(sub, opts = {}) {
|
|
6203
6323
|
if (!loadCredentials()) {
|
|
6204
|
-
console.log(
|
|
6324
|
+
console.log(import_chalk37.default.dim("Not logged in. Run `apiblaze login`."));
|
|
6205
6325
|
return;
|
|
6206
6326
|
}
|
|
6207
6327
|
if (!isOperatorLogin()) {
|
|
6208
|
-
console.log(
|
|
6328
|
+
console.log(import_chalk37.default.dim("`apiblaze op` is only available to platform operators."));
|
|
6209
6329
|
return;
|
|
6210
6330
|
}
|
|
6211
6331
|
switch (sub) {
|
|
6212
6332
|
case void 0:
|
|
6213
6333
|
case "menu": {
|
|
6214
|
-
console.log(
|
|
6215
|
-
console.log(` ${
|
|
6216
|
-
console.log(` ${
|
|
6217
|
-
console.log(` ${
|
|
6218
|
-
console.log(
|
|
6334
|
+
console.log(import_chalk37.default.bold("\nOperator menu"));
|
|
6335
|
+
console.log(` ${import_chalk37.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
6336
|
+
console.log(` ${import_chalk37.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk37.default.dim("-y to skip")})`);
|
|
6337
|
+
console.log(` ${import_chalk37.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
6338
|
+
console.log(import_chalk37.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
6219
6339
|
`));
|
|
6220
6340
|
return;
|
|
6221
6341
|
}
|
|
@@ -6234,17 +6354,17 @@ async function runOp(sub, opts = {}) {
|
|
|
6234
6354
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
6235
6355
|
printResidue(report, false);
|
|
6236
6356
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
6237
|
-
console.log(
|
|
6357
|
+
console.log(import_chalk37.default.green("Nothing to sweep."));
|
|
6238
6358
|
return;
|
|
6239
6359
|
}
|
|
6240
6360
|
if (!opts.yes) {
|
|
6241
6361
|
const readline2 = await import("readline/promises");
|
|
6242
6362
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
6243
6363
|
const answer = await rl.question(
|
|
6244
|
-
|
|
6364
|
+
import_chalk37.default.red(`Delete ${nUp} Upstash key(s) + ${nFga} OpenFGA store(s) + ${nGhost} ghost tuple(s) + ${nSide} Neon side-table row(s)? Type 'sweep' to confirm: `)
|
|
6245
6365
|
);
|
|
6246
6366
|
rl.close();
|
|
6247
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
6367
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk37.default.dim("Aborted."));
|
|
6248
6368
|
}
|
|
6249
6369
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
6250
6370
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -6255,15 +6375,15 @@ async function runOp(sub, opts = {}) {
|
|
|
6255
6375
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
6256
6376
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
6257
6377
|
const accounts = data?.accounts ?? [];
|
|
6258
|
-
if (accounts.length === 0) return void console.log(
|
|
6378
|
+
if (accounts.length === 0) return void console.log(import_chalk37.default.dim("No credit wallets."));
|
|
6259
6379
|
for (const a of accounts) {
|
|
6260
6380
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
6261
|
-
console.log(` ${
|
|
6381
|
+
console.log(` ${import_chalk37.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk37.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk37.default.dim(" \u2014 anon") : ""}`);
|
|
6262
6382
|
}
|
|
6263
6383
|
return;
|
|
6264
6384
|
}
|
|
6265
6385
|
default:
|
|
6266
|
-
console.log(
|
|
6386
|
+
console.log(import_chalk37.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
6267
6387
|
}
|
|
6268
6388
|
}
|
|
6269
6389
|
|
|
@@ -6325,7 +6445,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
6325
6445
|
try {
|
|
6326
6446
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
6327
6447
|
if (Number.isNaN(resolved)) {
|
|
6328
|
-
console.error(
|
|
6448
|
+
console.error(import_chalk38.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
6329
6449
|
process.exit(1);
|
|
6330
6450
|
}
|
|
6331
6451
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -6338,6 +6458,7 @@ var consumer = program.command("consumer").description("Act as a consumer of you
|
|
|
6338
6458
|
consumer.command("login").description("Log in to a tenant's portal as a consumer (device flow)").option("--team <id|name>", "Team whose tenants to choose from (producer mode)").option("--tenant <slug>", "Tenant to log in to (skips the picker)").option("--client <clientId>", "App client id \u2014 for standalone login without a producer session").action(action((opts) => runConsumerLogin(opts)));
|
|
6339
6459
|
consumer.command("tokens").description("Show the logged-in consumer access / refresh / id tokens").option("--json", "Output machine-readable JSON").action(action((opts) => runConsumerTokens(opts)));
|
|
6340
6460
|
consumer.command("apikeys").description("List your consumer API keys (reveals expiring ones), then offer to create one").option("--json", "Output machine-readable JSON").action(action((opts) => runConsumerApikeys(opts)));
|
|
6461
|
+
program.command("flush").description("Wipe ALL local state (login, workspace keys, apichats, DP keys, secrets)").option("-y, --yes", "Skip the confirmation prompt").action(runFlush);
|
|
6341
6462
|
program.command("logout").description("Sign out (asks whether to drop the Producer or Consumer login)").option("--producer", "Log out of the API Producer identity").option("--consumer", "Log out of the API Consumer identity").option("--all", "Log out of both").action(action((opts) => runLogout(opts)));
|
|
6342
6463
|
program.command("whoami").description("Show who you are \u2014 both API Producer and API Consumer").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
6343
6464
|
try {
|
|
@@ -6423,7 +6544,7 @@ function groupedCommandHelp() {
|
|
|
6423
6544
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
6424
6545
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
6425
6546
|
}).filter(Boolean).join("\n");
|
|
6426
|
-
return `${
|
|
6547
|
+
return `${import_chalk38.default.bold(g.title)}
|
|
6427
6548
|
${rows}`;
|
|
6428
6549
|
}).join("\n\n");
|
|
6429
6550
|
}
|
|
@@ -6457,14 +6578,14 @@ async function recoverStaleTeam() {
|
|
|
6457
6578
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
6458
6579
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
6459
6580
|
if (!linked) {
|
|
6460
|
-
console.error(
|
|
6581
|
+
console.error(import_chalk38.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
6461
6582
|
return;
|
|
6462
6583
|
}
|
|
6463
6584
|
if (linked.teamId === creds.teamId) return;
|
|
6464
6585
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
6465
6586
|
delete next.activeTenant;
|
|
6466
6587
|
saveCredentials(next);
|
|
6467
|
-
console.error(
|
|
6588
|
+
console.error(import_chalk38.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk38.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
6468
6589
|
} catch {
|
|
6469
6590
|
}
|
|
6470
6591
|
}
|
|
@@ -6472,16 +6593,16 @@ async function printError(err) {
|
|
|
6472
6593
|
if (err instanceof ApiError) {
|
|
6473
6594
|
const data = err.body;
|
|
6474
6595
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
6475
|
-
console.error(
|
|
6596
|
+
console.error(import_chalk38.default.red(`
|
|
6476
6597
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
6477
6598
|
if (err.status === 403 || err.status === 404) {
|
|
6478
6599
|
await recoverStaleTeam();
|
|
6479
6600
|
}
|
|
6480
6601
|
} else if (err instanceof Error) {
|
|
6481
|
-
console.error(
|
|
6602
|
+
console.error(import_chalk38.default.red(`
|
|
6482
6603
|
Error: ${err.message}`));
|
|
6483
6604
|
} else {
|
|
6484
|
-
console.error(
|
|
6605
|
+
console.error(import_chalk38.default.red("\nUnknown error"));
|
|
6485
6606
|
}
|
|
6486
6607
|
}
|
|
6487
6608
|
program.parse(process.argv);
|