apiblaze 0.17.11 → 0.17.14
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 +647 -504
- 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.14";
|
|
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
|
}
|
|
@@ -5189,16 +5229,19 @@ function chatUrl(p) {
|
|
|
5189
5229
|
function renderToolEvents(events) {
|
|
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
|
+
console.log(import_chalk33.default.dim(` \u21B3 ${e.method} ${e.url}`));
|
|
5236
|
+
}
|
|
5194
5237
|
}
|
|
5195
5238
|
}
|
|
5196
5239
|
function billingLine(billing) {
|
|
5197
5240
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
5198
5241
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
5199
|
-
let line =
|
|
5242
|
+
let line = import_chalk33.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk33.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
5200
5243
|
if (typeof billing.credits_remaining === "number") {
|
|
5201
|
-
line +=
|
|
5244
|
+
line += import_chalk33.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
5202
5245
|
}
|
|
5203
5246
|
return line;
|
|
5204
5247
|
}
|
|
@@ -5207,26 +5250,26 @@ function freeBudgetWarning(billing, anon) {
|
|
|
5207
5250
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
5208
5251
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
5209
5252
|
if (left > 8) return null;
|
|
5210
|
-
if (left <= 0) return
|
|
5211
|
-
return
|
|
5253
|
+
if (left <= 0) return import_chalk33.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
5254
|
+
return import_chalk33.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
5212
5255
|
}
|
|
5213
5256
|
function printAssistant(delta) {
|
|
5214
5257
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
5215
5258
|
const m = delta[i];
|
|
5216
5259
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
5217
|
-
console.log("\n" +
|
|
5260
|
+
console.log("\n" + import_chalk33.default.green("assistant \u203A ") + m.content + "\n");
|
|
5218
5261
|
return;
|
|
5219
5262
|
}
|
|
5220
5263
|
}
|
|
5221
5264
|
}
|
|
5222
5265
|
function verboseTrace(p, hasByo) {
|
|
5223
5266
|
if (!isVerbose()) return;
|
|
5224
|
-
console.log(
|
|
5225
|
-
console.log(
|
|
5226
|
-
console.log(
|
|
5227
|
-
console.log(
|
|
5228
|
-
console.log(
|
|
5229
|
-
console.log(
|
|
5267
|
+
console.log(import_chalk33.default.dim("\n" + "\u2500".repeat(64)));
|
|
5268
|
+
console.log(import_chalk33.default.bold("--verbose: this turn POSTed to the data-plane MCP host"));
|
|
5269
|
+
console.log(import_chalk33.default.green(` curl -sS -X POST ${chatUrl(p)} \\`));
|
|
5270
|
+
console.log(import_chalk33.default.green(` -H "X-API-Key: ${maskSecret(p.dpKey)}" \\`));
|
|
5271
|
+
console.log(import_chalk33.default.green(" -H 'Content-Type: application/json' \\"));
|
|
5272
|
+
console.log(import_chalk33.default.green(` -d '{"turn_id":"\u2026","messages":[\u2026],"environment":"${p.environment}"${hasByo ? ',"llm_api_key":"***","llm_provider":"\u2026"' : ""}}'`));
|
|
5230
5273
|
}
|
|
5231
5274
|
async function replTurn(p, messages, userText) {
|
|
5232
5275
|
messages.push({ role: "user", content: userText });
|
|
@@ -5249,7 +5292,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5249
5292
|
});
|
|
5250
5293
|
} catch (err) {
|
|
5251
5294
|
spinner.fail("Network error.");
|
|
5252
|
-
console.log(
|
|
5295
|
+
console.log(import_chalk33.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
5253
5296
|
return;
|
|
5254
5297
|
}
|
|
5255
5298
|
let data = null;
|
|
@@ -5267,12 +5310,12 @@ async function replTurn(p, messages, userText) {
|
|
|
5267
5310
|
return;
|
|
5268
5311
|
}
|
|
5269
5312
|
if (res.status === 401) {
|
|
5270
|
-
console.log(
|
|
5313
|
+
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
5314
|
return;
|
|
5272
5315
|
}
|
|
5273
5316
|
if (!res.ok || !data) {
|
|
5274
5317
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
5275
|
-
console.log(
|
|
5318
|
+
console.log(import_chalk33.default.red(` Chat error: ${err}`));
|
|
5276
5319
|
return;
|
|
5277
5320
|
}
|
|
5278
5321
|
if (Array.isArray(data.delta)) {
|
|
@@ -5287,36 +5330,36 @@ async function replTurn(p, messages, userText) {
|
|
|
5287
5330
|
verboseTrace(p, !!llm2);
|
|
5288
5331
|
if (!data.continue) return;
|
|
5289
5332
|
}
|
|
5290
|
-
console.log(
|
|
5333
|
+
console.log(import_chalk33.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
5291
5334
|
}
|
|
5292
5335
|
function renderUpsell(p, upsell) {
|
|
5293
5336
|
const loggedIn = !!loadCredentials();
|
|
5294
|
-
console.log("\n" +
|
|
5337
|
+
console.log("\n" + import_chalk33.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
5295
5338
|
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
5296
5339
|
if (!loggedIn) {
|
|
5297
|
-
console.log(
|
|
5340
|
+
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
5341
|
} else {
|
|
5299
|
-
console.log(
|
|
5342
|
+
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
5343
|
}
|
|
5301
5344
|
} else if (upsell.reason === "INFLIGHT") {
|
|
5302
|
-
console.log(
|
|
5345
|
+
console.log(import_chalk33.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
5303
5346
|
}
|
|
5304
5347
|
console.log();
|
|
5305
5348
|
}
|
|
5306
|
-
var apichatsPath = () =>
|
|
5349
|
+
var apichatsPath = () => path6.join(getApiblazeDir(), "apichats.json");
|
|
5307
5350
|
function loadApichats() {
|
|
5308
5351
|
try {
|
|
5309
|
-
const list = JSON.parse(
|
|
5352
|
+
const list = JSON.parse(fs9.readFileSync(apichatsPath(), "utf-8"));
|
|
5310
5353
|
return Array.isArray(list) ? list : [];
|
|
5311
5354
|
} catch {
|
|
5312
5355
|
return [];
|
|
5313
5356
|
}
|
|
5314
5357
|
}
|
|
5315
5358
|
function writeApichats(list) {
|
|
5316
|
-
|
|
5317
|
-
|
|
5359
|
+
fs9.mkdirSync(getApiblazeDir(), { recursive: true });
|
|
5360
|
+
fs9.writeFileSync(apichatsPath(), JSON.stringify(list, null, 2), "utf-8");
|
|
5318
5361
|
try {
|
|
5319
|
-
|
|
5362
|
+
fs9.chmodSync(apichatsPath(), 384);
|
|
5320
5363
|
} catch {
|
|
5321
5364
|
}
|
|
5322
5365
|
}
|
|
@@ -5338,21 +5381,99 @@ function saveTranscript(p, messages) {
|
|
|
5338
5381
|
list[i].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5339
5382
|
writeApichats(list);
|
|
5340
5383
|
}
|
|
5384
|
+
function claimApichat(p, ownerUserId) {
|
|
5385
|
+
const list = loadApichats();
|
|
5386
|
+
const i = list.findIndex((a) => apichatKey(a) === apichatKey(p));
|
|
5387
|
+
if (i < 0) return;
|
|
5388
|
+
list[i].ownerUserId = ownerUserId;
|
|
5389
|
+
list[i].anon = false;
|
|
5390
|
+
list[i].mcpHost = p.mcpHost;
|
|
5391
|
+
list[i].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5392
|
+
writeApichats(list);
|
|
5393
|
+
}
|
|
5394
|
+
async function mintDurableProxyKey(teamId, tenant2) {
|
|
5395
|
+
const out = await admin({
|
|
5396
|
+
method: "POST",
|
|
5397
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/admin/keys`,
|
|
5398
|
+
body: { key_type: "apiblaze-consumer-admin", environment: "prod" },
|
|
5399
|
+
// no expiry → durable
|
|
5400
|
+
summary: "Mint a durable data-plane key for apichat"
|
|
5401
|
+
});
|
|
5402
|
+
if (!out?.key) throw new Error("key mint returned no key");
|
|
5403
|
+
return out.key;
|
|
5404
|
+
}
|
|
5405
|
+
async function openServerProxy(project) {
|
|
5406
|
+
const version2 = project.apiVersion || "1.0.0";
|
|
5407
|
+
const environment = "prod";
|
|
5408
|
+
const mcpHost = `${project.projectId}.mcp.abz.run`;
|
|
5409
|
+
const tenant2 = project.tenant || project.projectId;
|
|
5410
|
+
const me = loadCredentials()?.apiblazeUserId;
|
|
5411
|
+
const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
|
|
5412
|
+
let dpKey = prior?.dpKey;
|
|
5413
|
+
if (!dpKey) {
|
|
5414
|
+
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`));
|
|
5415
|
+
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
5416
|
+
console.log(` ${import_chalk33.default.green("\u2714")} API key: ${import_chalk33.default.dim(maskSecret(dpKey))}`);
|
|
5417
|
+
}
|
|
5418
|
+
const p = {
|
|
5419
|
+
projectId: project.projectId,
|
|
5420
|
+
version: version2,
|
|
5421
|
+
environment,
|
|
5422
|
+
dpKey,
|
|
5423
|
+
mcpHost,
|
|
5424
|
+
proxyUrl: `https://${project.projectId}.abz.run/${version2}/${environment}`,
|
|
5425
|
+
anon: false
|
|
5426
|
+
};
|
|
5427
|
+
const spinner = (0, import_ora16.default)("Preparing the chat\u2026").start();
|
|
5428
|
+
try {
|
|
5429
|
+
const raw = await admin({
|
|
5430
|
+
method: "GET",
|
|
5431
|
+
path: `/projects/${project.projectId}/${version2}/openapi`,
|
|
5432
|
+
summary: "Fetch OpenAPI spec"
|
|
5433
|
+
}).catch(() => null);
|
|
5434
|
+
const spec2 = raw && (raw.spec ?? raw);
|
|
5435
|
+
spinner.stop();
|
|
5436
|
+
if (spec2 && (spec2.paths || spec2.openapi)) {
|
|
5437
|
+
await publishMcp(p, spec2);
|
|
5438
|
+
} else {
|
|
5439
|
+
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`."));
|
|
5440
|
+
}
|
|
5441
|
+
} catch (err) {
|
|
5442
|
+
spinner.fail("Could not open the proxy.");
|
|
5443
|
+
throw err;
|
|
5444
|
+
}
|
|
5445
|
+
upsertApichat({
|
|
5446
|
+
name: project.projectId,
|
|
5447
|
+
target: project.projectName,
|
|
5448
|
+
projectId: project.projectId,
|
|
5449
|
+
version: version2,
|
|
5450
|
+
environment,
|
|
5451
|
+
mcpHost,
|
|
5452
|
+
dpKey,
|
|
5453
|
+
anon: false,
|
|
5454
|
+
ownerUserId: me,
|
|
5455
|
+
createdAt: prior?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5456
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5457
|
+
messages: prior?.messages
|
|
5458
|
+
// preserve any earlier transcript; this session starts fresh
|
|
5459
|
+
});
|
|
5460
|
+
return { p, messages: [] };
|
|
5461
|
+
}
|
|
5341
5462
|
function discoverLocalSpecs() {
|
|
5342
5463
|
const cwd = process.cwd();
|
|
5343
5464
|
const known = ["openapi.yaml", "openapi.yml", "openapi.json", "swagger.yaml", "swagger.yml", "swagger.json", "api.yaml", "api.yml", "api.json"];
|
|
5344
5465
|
const found = [];
|
|
5345
5466
|
for (const n of known) {
|
|
5346
5467
|
try {
|
|
5347
|
-
if (
|
|
5468
|
+
if (fs9.statSync(path6.join(cwd, n)).isFile()) found.push(n);
|
|
5348
5469
|
} catch {
|
|
5349
5470
|
}
|
|
5350
5471
|
}
|
|
5351
5472
|
try {
|
|
5352
|
-
const files =
|
|
5473
|
+
const files = fs9.readdirSync(cwd).filter((f) => /\.(ya?ml|json)$/i.test(f) && !found.includes(f));
|
|
5353
5474
|
for (const f of files.slice(0, 60)) {
|
|
5354
5475
|
try {
|
|
5355
|
-
const head =
|
|
5476
|
+
const head = fs9.readFileSync(path6.join(cwd, f), "utf-8").slice(0, 4e3);
|
|
5356
5477
|
if (/["']?openapi["']?\s*:/i.test(head) || /["']?swagger["']?\s*:/i.test(head) || /^\s*paths\s*:/im.test(head) || /"paths"\s*:/.test(head)) {
|
|
5357
5478
|
found.push(f);
|
|
5358
5479
|
}
|
|
@@ -5365,12 +5486,27 @@ function discoverLocalSpecs() {
|
|
|
5365
5486
|
}
|
|
5366
5487
|
async function noArgsMenu(opts) {
|
|
5367
5488
|
const { default: inquirer3 } = await import("inquirer");
|
|
5368
|
-
const
|
|
5489
|
+
const me = loadCredentials()?.apiblazeUserId;
|
|
5490
|
+
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
5369
5491
|
const choices = saved.map((a) => ({
|
|
5370
|
-
name: `Chat with ${
|
|
5492
|
+
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` : ""}`)}`,
|
|
5371
5493
|
value: { type: "existing", a }
|
|
5372
5494
|
}));
|
|
5373
|
-
|
|
5495
|
+
const creds = loadCredentials();
|
|
5496
|
+
if (creds?.teamId) {
|
|
5497
|
+
const savedIds = new Set(saved.map((a) => a.projectId));
|
|
5498
|
+
try {
|
|
5499
|
+
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
5500
|
+
for (const pr of proxies) {
|
|
5501
|
+
choices.push({
|
|
5502
|
+
name: `Chat with ${import_chalk33.default.bold(pr.projectName)} ${import_chalk33.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
5503
|
+
value: { type: "server", project: pr }
|
|
5504
|
+
});
|
|
5505
|
+
}
|
|
5506
|
+
} catch {
|
|
5507
|
+
}
|
|
5508
|
+
}
|
|
5509
|
+
choices.push({ name: import_chalk33.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
5374
5510
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
5375
5511
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
5376
5512
|
]);
|
|
@@ -5402,6 +5538,9 @@ async function noArgsMenu(opts) {
|
|
|
5402
5538
|
};
|
|
5403
5539
|
return { p, messages };
|
|
5404
5540
|
}
|
|
5541
|
+
if (pick2.type === "server") {
|
|
5542
|
+
return await openServerProxy(pick2.project);
|
|
5543
|
+
}
|
|
5405
5544
|
const { source } = await inquirer3.prompt([
|
|
5406
5545
|
{
|
|
5407
5546
|
type: "list",
|
|
@@ -5439,31 +5578,31 @@ async function noArgsMenu(opts) {
|
|
|
5439
5578
|
async function runRepl(p, initialMessages) {
|
|
5440
5579
|
const { default: inquirer3 } = await import("inquirer");
|
|
5441
5580
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
5442
|
-
console.log("\n" +
|
|
5443
|
-
if (messages.length) console.log(
|
|
5581
|
+
console.log("\n" + import_chalk33.default.cyan.bold("Chat with your API") + import_chalk33.default.dim(` \xB7 ${p.mcpHost}`));
|
|
5582
|
+
if (messages.length) console.log(import_chalk33.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
5444
5583
|
const llm2 = loadLlmConfig();
|
|
5445
5584
|
console.log(
|
|
5446
|
-
|
|
5585
|
+
import_chalk33.default.dim(
|
|
5447
5586
|
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."
|
|
5448
5587
|
)
|
|
5449
5588
|
);
|
|
5450
5589
|
for (; ; ) {
|
|
5451
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
5590
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk33.default.green("you \u203A") }]);
|
|
5452
5591
|
const text = (input ?? "").trim();
|
|
5453
5592
|
if (!text) continue;
|
|
5454
5593
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
5455
5594
|
if (text === "/login") {
|
|
5456
5595
|
try {
|
|
5457
5596
|
await runLogin();
|
|
5458
|
-
console.log(
|
|
5597
|
+
console.log(import_chalk33.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
5459
5598
|
} catch (err) {
|
|
5460
|
-
console.log(
|
|
5599
|
+
console.log(import_chalk33.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5461
5600
|
}
|
|
5462
5601
|
continue;
|
|
5463
5602
|
}
|
|
5464
5603
|
if (text === "/claim") {
|
|
5465
5604
|
if (!loadCredentials()) {
|
|
5466
|
-
console.log(
|
|
5605
|
+
console.log(import_chalk33.default.yellow(" Log in first: `/login`, then `/claim`."));
|
|
5467
5606
|
continue;
|
|
5468
5607
|
}
|
|
5469
5608
|
try {
|
|
@@ -5471,24 +5610,25 @@ async function runRepl(p, initialMessages) {
|
|
|
5471
5610
|
if (p.mcpHost.endsWith(".mcp.tryabz.run")) {
|
|
5472
5611
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
5473
5612
|
p.anon = false;
|
|
5474
|
-
|
|
5613
|
+
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
5614
|
+
console.log(import_chalk33.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
5475
5615
|
}
|
|
5476
5616
|
} catch (err) {
|
|
5477
|
-
console.log(
|
|
5617
|
+
console.log(import_chalk33.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5478
5618
|
}
|
|
5479
5619
|
continue;
|
|
5480
5620
|
}
|
|
5481
5621
|
if (text.startsWith("/")) {
|
|
5482
|
-
console.log(
|
|
5622
|
+
console.log(import_chalk33.default.dim(" Commands: /login /claim /exit"));
|
|
5483
5623
|
continue;
|
|
5484
5624
|
}
|
|
5485
5625
|
await replTurn(p, messages, text);
|
|
5486
5626
|
saveTranscript(p, messages);
|
|
5487
5627
|
}
|
|
5488
|
-
console.log(
|
|
5628
|
+
console.log(import_chalk33.default.dim("\nBye."));
|
|
5489
5629
|
}
|
|
5490
5630
|
async function runApichat(opts) {
|
|
5491
|
-
console.log(
|
|
5631
|
+
console.log(import_chalk33.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
5492
5632
|
if (!opts.openapispec && !opts.target) {
|
|
5493
5633
|
if (!process.stdin.isTTY) {
|
|
5494
5634
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -5501,7 +5641,7 @@ async function runApichat(opts) {
|
|
|
5501
5641
|
}
|
|
5502
5642
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
5503
5643
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
5504
|
-
console.log(` ${
|
|
5644
|
+
console.log(` ${import_chalk33.default.dim("Target:")} ${import_chalk33.default.bold(target)}`);
|
|
5505
5645
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
5506
5646
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
5507
5647
|
fail4(
|
|
@@ -5510,7 +5650,7 @@ async function runApichat(opts) {
|
|
|
5510
5650
|
);
|
|
5511
5651
|
}
|
|
5512
5652
|
const p = await provision(spec2, target, opts);
|
|
5513
|
-
console.log(` ${
|
|
5653
|
+
console.log(` ${import_chalk33.default.dim("Proxy: ")} ${import_chalk33.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
5514
5654
|
upsertApichat({
|
|
5515
5655
|
name: p.projectId,
|
|
5516
5656
|
target,
|
|
@@ -5520,6 +5660,8 @@ async function runApichat(opts) {
|
|
|
5520
5660
|
mcpHost: p.mcpHost,
|
|
5521
5661
|
dpKey: p.dpKey,
|
|
5522
5662
|
anon: p.anon,
|
|
5663
|
+
ownerUserId: loadCredentials()?.apiblazeUserId,
|
|
5664
|
+
// undefined while anon
|
|
5523
5665
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5524
5666
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5525
5667
|
});
|
|
@@ -5527,22 +5669,22 @@ async function runApichat(opts) {
|
|
|
5527
5669
|
const secret = await captureTargetSecret(auth, opts);
|
|
5528
5670
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
5529
5671
|
} else {
|
|
5530
|
-
console.log(
|
|
5672
|
+
console.log(import_chalk33.default.dim(" Target auth: none required."));
|
|
5531
5673
|
}
|
|
5532
5674
|
const specText = JSON.stringify(spec2);
|
|
5533
5675
|
await uploadSpec(p, specText, opts);
|
|
5534
5676
|
const mcpUrl = await publishMcp(p, spec2);
|
|
5535
5677
|
console.log();
|
|
5536
|
-
if (p.proxyUrl) console.log(` ${
|
|
5537
|
-
if (mcpUrl) console.log(` ${
|
|
5678
|
+
if (p.proxyUrl) console.log(` ${import_chalk33.default.green("\u2713")} proxy ${import_chalk33.default.bold(p.proxyUrl)}`);
|
|
5679
|
+
if (mcpUrl) console.log(` ${import_chalk33.default.green("\u2713")} mcp ${import_chalk33.default.bold(mcpUrl)}`);
|
|
5538
5680
|
if (p.anon) {
|
|
5539
|
-
console.log(
|
|
5681
|
+
console.log(import_chalk33.default.dim("\n Anonymous workspace \u2014 /claim inside the chat (after /login) to keep it beyond 30 days."));
|
|
5540
5682
|
}
|
|
5541
5683
|
await runRepl(p);
|
|
5542
5684
|
}
|
|
5543
5685
|
|
|
5544
5686
|
// src/commands/consumer.ts
|
|
5545
|
-
var
|
|
5687
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
5546
5688
|
var import_ora17 = __toESM(require("ora"));
|
|
5547
5689
|
init_admin();
|
|
5548
5690
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
@@ -5563,7 +5705,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
5563
5705
|
function requireConsumer() {
|
|
5564
5706
|
const c = loadConsumer();
|
|
5565
5707
|
if (!c) {
|
|
5566
|
-
console.error(
|
|
5708
|
+
console.error(import_chalk34.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
5567
5709
|
process.exit(1);
|
|
5568
5710
|
}
|
|
5569
5711
|
return c;
|
|
@@ -5574,7 +5716,7 @@ async function runConsumerLogin(opts) {
|
|
|
5574
5716
|
let clientId = opts.client;
|
|
5575
5717
|
if (clientId) {
|
|
5576
5718
|
if (!tenant2) {
|
|
5577
|
-
console.error(
|
|
5719
|
+
console.error(import_chalk34.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
5578
5720
|
process.exit(1);
|
|
5579
5721
|
}
|
|
5580
5722
|
} else {
|
|
@@ -5592,19 +5734,19 @@ async function runConsumerLogin(opts) {
|
|
|
5592
5734
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
5593
5735
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
5594
5736
|
if (!pick2) {
|
|
5595
|
-
console.error(
|
|
5737
|
+
console.error(import_chalk34.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
5596
5738
|
process.exit(1);
|
|
5597
5739
|
}
|
|
5598
5740
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
5599
5741
|
}
|
|
5600
5742
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
5601
|
-
console.log(`${
|
|
5743
|
+
console.log(`${import_chalk34.default.cyan("\u2192")} Logging in to ${import_chalk34.default.bold(tenant2)} as a consumer...`);
|
|
5602
5744
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
5603
5745
|
console.log(`
|
|
5604
|
-
Open: ${
|
|
5605
|
-
console.log(` Code: ${
|
|
5746
|
+
Open: ${import_chalk34.default.underline(verificationUri)}`);
|
|
5747
|
+
console.log(` Code: ${import_chalk34.default.bold(userCode)}
|
|
5606
5748
|
`);
|
|
5607
|
-
console.log(
|
|
5749
|
+
console.log(import_chalk34.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
5608
5750
|
}, portalResource);
|
|
5609
5751
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
5610
5752
|
const creds = {
|
|
@@ -5619,7 +5761,7 @@ async function runConsumerLogin(opts) {
|
|
|
5619
5761
|
obtainedAt: Date.now()
|
|
5620
5762
|
};
|
|
5621
5763
|
saveConsumer(creds);
|
|
5622
|
-
console.log(
|
|
5764
|
+
console.log(import_chalk34.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
5623
5765
|
}
|
|
5624
5766
|
async function runConsumerTokens(opts) {
|
|
5625
5767
|
const creds = requireConsumer();
|
|
@@ -5632,18 +5774,18 @@ async function runConsumerTokens(opts) {
|
|
|
5632
5774
|
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));
|
|
5633
5775
|
return;
|
|
5634
5776
|
}
|
|
5635
|
-
console.log(`${
|
|
5777
|
+
console.log(`${import_chalk34.default.cyan("Consumer")} ${import_chalk34.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk34.default.bold(fresh.tenant)}
|
|
5636
5778
|
`);
|
|
5637
|
-
console.log(`${
|
|
5779
|
+
console.log(`${import_chalk34.default.bold("access_token")} ${import_chalk34.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
5638
5780
|
${fresh.accessToken}
|
|
5639
5781
|
`);
|
|
5640
|
-
if (fresh.idToken) console.log(`${
|
|
5782
|
+
if (fresh.idToken) console.log(`${import_chalk34.default.bold("id_token")} ${import_chalk34.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
5641
5783
|
${fresh.idToken}
|
|
5642
5784
|
`);
|
|
5643
|
-
if (fresh.refreshToken) console.log(`${
|
|
5785
|
+
if (fresh.refreshToken) console.log(`${import_chalk34.default.bold("refresh_token")}
|
|
5644
5786
|
${fresh.refreshToken}
|
|
5645
5787
|
`);
|
|
5646
|
-
console.log(
|
|
5788
|
+
console.log(import_chalk34.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
5647
5789
|
}
|
|
5648
5790
|
async function runConsumerApikeys(opts) {
|
|
5649
5791
|
const creds = requireConsumer();
|
|
@@ -5653,8 +5795,8 @@ async function runConsumerApikeys(opts) {
|
|
|
5653
5795
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
5654
5796
|
spinner.stop();
|
|
5655
5797
|
if (list.status >= 400) {
|
|
5656
|
-
console.error(
|
|
5657
|
-
if (list.status === 401) console.error(
|
|
5798
|
+
console.error(import_chalk34.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
5799
|
+
if (list.status === 401) console.error(import_chalk34.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
5658
5800
|
process.exit(1);
|
|
5659
5801
|
}
|
|
5660
5802
|
const keys = list.data?.keys ?? [];
|
|
@@ -5662,16 +5804,16 @@ async function runConsumerApikeys(opts) {
|
|
|
5662
5804
|
if (opts.json) {
|
|
5663
5805
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
5664
5806
|
} else if (!keys.length) {
|
|
5665
|
-
console.log(
|
|
5807
|
+
console.log(import_chalk34.default.yellow("No API keys yet."));
|
|
5666
5808
|
} else {
|
|
5667
5809
|
for (const k of keys) {
|
|
5668
5810
|
const clear = revealMap[k.environment]?.key;
|
|
5669
|
-
const shown = clear ?
|
|
5670
|
-
const exp = k.expires_at ?
|
|
5671
|
-
console.log(` ${
|
|
5811
|
+
const shown = clear ? import_chalk34.default.green(clear) : import_chalk34.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
5812
|
+
const exp = k.expires_at ? import_chalk34.default.dim(`exp ${k.expires_at}`) : import_chalk34.default.dim("no expiry");
|
|
5813
|
+
console.log(` ${import_chalk34.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk34.default.dim(k.description ?? "")}`);
|
|
5672
5814
|
}
|
|
5673
5815
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
5674
|
-
console.log(
|
|
5816
|
+
console.log(import_chalk34.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
5675
5817
|
}
|
|
5676
5818
|
}
|
|
5677
5819
|
if (opts.json) return;
|
|
@@ -5693,34 +5835,34 @@ async function runConsumerApikeys(opts) {
|
|
|
5693
5835
|
}
|
|
5694
5836
|
s2.succeed("Key created.");
|
|
5695
5837
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
5696
|
-
if (key) console.log(` ${
|
|
5697
|
-
else console.log(
|
|
5838
|
+
if (key) console.log(` ${import_chalk34.default.green(key)} ${import_chalk34.default.dim("(shown once \u2014 store it now)")}`);
|
|
5839
|
+
else console.log(import_chalk34.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
5698
5840
|
}
|
|
5699
5841
|
|
|
5700
5842
|
// src/commands/sidecar.ts
|
|
5701
|
-
var
|
|
5843
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
5702
5844
|
var import_ora18 = __toESM(require("ora"));
|
|
5703
|
-
var
|
|
5704
|
-
var
|
|
5845
|
+
var fs10 = __toESM(require("fs"));
|
|
5846
|
+
var path7 = __toESM(require("path"));
|
|
5705
5847
|
init_admin();
|
|
5706
5848
|
init_auth();
|
|
5707
5849
|
function detectNextProject(root) {
|
|
5708
|
-
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) =>
|
|
5850
|
+
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs10.existsSync(path7.join(root, f)));
|
|
5709
5851
|
let hasDep = false;
|
|
5710
5852
|
try {
|
|
5711
|
-
const pkg = JSON.parse(
|
|
5853
|
+
const pkg = JSON.parse(fs10.readFileSync(path7.join(root, "package.json"), "utf8"));
|
|
5712
5854
|
hasDep = !!(pkg.dependencies?.next || pkg.devDependencies?.next);
|
|
5713
5855
|
} catch {
|
|
5714
5856
|
}
|
|
5715
|
-
const appDir =
|
|
5716
|
-
const pagesDir =
|
|
5857
|
+
const appDir = fs10.existsSync(path7.join(root, "app")) || fs10.existsSync(path7.join(root, "src", "app"));
|
|
5858
|
+
const pagesDir = fs10.existsSync(path7.join(root, "pages")) || fs10.existsSync(path7.join(root, "src", "pages"));
|
|
5717
5859
|
return { found: hasConfig || hasDep || appDir || pagesDir, router: appDir ? "app" : pagesDir ? "pages" : null };
|
|
5718
5860
|
}
|
|
5719
5861
|
function upsertEnvLocal(root, token) {
|
|
5720
|
-
const p =
|
|
5862
|
+
const p = path7.join(root, ".env.local");
|
|
5721
5863
|
let existing = "";
|
|
5722
5864
|
try {
|
|
5723
|
-
existing =
|
|
5865
|
+
existing = fs10.readFileSync(p, "utf8");
|
|
5724
5866
|
} catch {
|
|
5725
5867
|
}
|
|
5726
5868
|
const had = /^APIBLAZE_API_KEY=/m.test(existing) || /^APIBLAZE_TOKEN=/m.test(existing);
|
|
@@ -5735,15 +5877,15 @@ function upsertEnvLocal(root, token) {
|
|
|
5735
5877
|
next = (next.endsWith("\n") ? next : next + "\n") + `APIBLAZE_SIDECAR_VERBOSE=true
|
|
5736
5878
|
`;
|
|
5737
5879
|
}
|
|
5738
|
-
|
|
5880
|
+
fs10.writeFileSync(p, next);
|
|
5739
5881
|
return had ? "rotated" : "created";
|
|
5740
5882
|
}
|
|
5741
5883
|
function installSidecarPackage(root) {
|
|
5742
|
-
if (
|
|
5743
|
-
console.log(` ${
|
|
5884
|
+
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
5885
|
+
console.log(` ${import_chalk35.default.green("\u2713")} apiblaze package already installed`);
|
|
5744
5886
|
return;
|
|
5745
5887
|
}
|
|
5746
|
-
const has = (f) =>
|
|
5888
|
+
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
5747
5889
|
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" };
|
|
5748
5890
|
const spinner = (0, import_ora18.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
5749
5891
|
try {
|
|
@@ -5751,12 +5893,12 @@ function installSidecarPackage(root) {
|
|
|
5751
5893
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
5752
5894
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
5753
5895
|
} catch {
|
|
5754
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
5896
|
+
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")}.`);
|
|
5755
5897
|
}
|
|
5756
5898
|
}
|
|
5757
5899
|
function readEnvKey(root) {
|
|
5758
5900
|
try {
|
|
5759
|
-
const s =
|
|
5901
|
+
const s = fs10.readFileSync(path7.join(root, ".env.local"), "utf8");
|
|
5760
5902
|
const m = s.match(/^APIBLAZE_API_KEY=(.+)$/m) ?? s.match(/^APIBLAZE_TOKEN=(.+)$/m);
|
|
5761
5903
|
return m ? m[1].trim() : null;
|
|
5762
5904
|
} catch {
|
|
@@ -5764,16 +5906,16 @@ function readEnvKey(root) {
|
|
|
5764
5906
|
}
|
|
5765
5907
|
}
|
|
5766
5908
|
function ensureGitignored(root) {
|
|
5767
|
-
const p =
|
|
5909
|
+
const p = path7.join(root, ".gitignore");
|
|
5768
5910
|
let c = "";
|
|
5769
5911
|
try {
|
|
5770
|
-
c =
|
|
5912
|
+
c = fs10.readFileSync(p, "utf8");
|
|
5771
5913
|
} catch {
|
|
5772
5914
|
}
|
|
5773
|
-
if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c))
|
|
5915
|
+
if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c)) fs10.writeFileSync(p, (c && !c.endsWith("\n") ? c + "\n" : c) + ".env.local\n");
|
|
5774
5916
|
}
|
|
5775
5917
|
function wireInstrumentation(root) {
|
|
5776
|
-
const existing = ["instrumentation.ts", "instrumentation.js",
|
|
5918
|
+
const existing = ["instrumentation.ts", "instrumentation.js", path7.join("src", "instrumentation.ts")].map((c) => path7.join(root, c)).find((f) => fs10.existsSync(f));
|
|
5777
5919
|
const body = `import { register as apiblaze } from "apiblaze/sidecar";
|
|
5778
5920
|
|
|
5779
5921
|
export function register() {
|
|
@@ -5781,18 +5923,18 @@ export function register() {
|
|
|
5781
5923
|
}
|
|
5782
5924
|
`;
|
|
5783
5925
|
if (!existing) {
|
|
5784
|
-
|
|
5926
|
+
fs10.writeFileSync(path7.join(root, "instrumentation.ts"), body);
|
|
5785
5927
|
return "created";
|
|
5786
5928
|
}
|
|
5787
|
-
const cur =
|
|
5929
|
+
const cur = fs10.readFileSync(existing, "utf8");
|
|
5788
5930
|
if (cur.includes("apiblaze/sidecar")) return "present";
|
|
5789
5931
|
if (/export\s+function\s+register\s*\(/.test(cur)) {
|
|
5790
|
-
|
|
5932
|
+
fs10.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
|
|
5791
5933
|
` + cur.replace(/export\s+function\s+register\s*\(\s*\)\s*\{/, (m) => `${m}
|
|
5792
5934
|
apiblaze();`));
|
|
5793
5935
|
return "patched";
|
|
5794
5936
|
}
|
|
5795
|
-
|
|
5937
|
+
fs10.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
|
|
5796
5938
|
${cur}
|
|
5797
5939
|
// call apiblaze() inside your register() export.
|
|
5798
5940
|
`);
|
|
@@ -5871,17 +6013,17 @@ export default async function Page() {
|
|
|
5871
6013
|
function generateInspector(root, router) {
|
|
5872
6014
|
try {
|
|
5873
6015
|
if (router === "pages") {
|
|
5874
|
-
const dir2 =
|
|
5875
|
-
const f2 =
|
|
5876
|
-
|
|
5877
|
-
return
|
|
5878
|
-
}
|
|
5879
|
-
const base =
|
|
5880
|
-
const dir =
|
|
5881
|
-
|
|
5882
|
-
const f =
|
|
5883
|
-
|
|
5884
|
-
return
|
|
6016
|
+
const dir2 = fs10.existsSync(path7.join(root, "src", "pages")) ? path7.join(root, "src", "pages") : path7.join(root, "pages");
|
|
6017
|
+
const f2 = path7.join(dir2, "abz-inspector.tsx");
|
|
6018
|
+
fs10.writeFileSync(f2, INSPECTOR_PAGE);
|
|
6019
|
+
return path7.relative(root, f2);
|
|
6020
|
+
}
|
|
6021
|
+
const base = fs10.existsSync(path7.join(root, "src", "app")) ? path7.join(root, "src", "app") : path7.join(root, "app");
|
|
6022
|
+
const dir = path7.join(base, "abz-inspector");
|
|
6023
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
6024
|
+
const f = path7.join(dir, "page.tsx");
|
|
6025
|
+
fs10.writeFileSync(f, INSPECTOR_PAGE);
|
|
6026
|
+
return path7.relative(root, f);
|
|
5885
6027
|
} catch {
|
|
5886
6028
|
return null;
|
|
5887
6029
|
}
|
|
@@ -5902,29 +6044,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
5902
6044
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
5903
6045
|
const envState = upsertEnvLocal(root, out.token);
|
|
5904
6046
|
ensureGitignored(root);
|
|
5905
|
-
console.log(` ${
|
|
5906
|
-
console.log(` ${
|
|
6047
|
+
console.log(` ${import_chalk35.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6048
|
+
console.log(` ${import_chalk35.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
5907
6049
|
installSidecarPackage(root);
|
|
5908
6050
|
let inspectorPath = null;
|
|
5909
6051
|
if (!opts.noInspector) {
|
|
5910
6052
|
inspectorPath = generateInspector(root, router);
|
|
5911
|
-
if (inspectorPath) console.log(` ${
|
|
6053
|
+
if (inspectorPath) console.log(` ${import_chalk35.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
5912
6054
|
}
|
|
5913
6055
|
console.log("");
|
|
5914
|
-
console.log(
|
|
5915
|
-
console.log(` 1. ${
|
|
6056
|
+
console.log(import_chalk35.default.bold("Done (no account needed). What happens next:"));
|
|
6057
|
+
console.log(` 1. ${import_chalk35.default.cyan("npm run dev")} and use your app.`);
|
|
5916
6058
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
5917
|
-
console.log(` ${
|
|
6059
|
+
console.log(` ${import_chalk35.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
5918
6060
|
console.log("");
|
|
5919
|
-
console.log(
|
|
5920
|
-
console.log(` ${
|
|
5921
|
-
console.log(
|
|
6061
|
+
console.log(import_chalk35.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
6062
|
+
console.log(` ${import_chalk35.default.cyan("apiblaze login")} then ${import_chalk35.default.cyan("apiblaze claim")} ${import_chalk35.default.dim("(no code needed here)")}`);
|
|
6063
|
+
console.log(import_chalk35.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
5922
6064
|
}
|
|
5923
6065
|
async function runSidecar(opts) {
|
|
5924
|
-
const root =
|
|
6066
|
+
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
5925
6067
|
const detected = detectNextProject(root);
|
|
5926
6068
|
if (!detected.found) {
|
|
5927
|
-
console.log(
|
|
6069
|
+
console.log(import_chalk35.default.yellow(`No Next.js project detected in ${root}.`));
|
|
5928
6070
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
5929
6071
|
return;
|
|
5930
6072
|
}
|
|
@@ -5935,10 +6077,10 @@ async function runSidecar(opts) {
|
|
|
5935
6077
|
if (!loadCredentials()) {
|
|
5936
6078
|
upsertEnvLocal(root, readEnvKey(root));
|
|
5937
6079
|
ensureGitignored(root);
|
|
5938
|
-
console.log(` ${
|
|
5939
|
-
console.log(` ${
|
|
6080
|
+
console.log(` ${import_chalk35.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
6081
|
+
console.log(` ${import_chalk35.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
5940
6082
|
installSidecarPackage(root);
|
|
5941
|
-
console.log(
|
|
6083
|
+
console.log(import_chalk35.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
5942
6084
|
return;
|
|
5943
6085
|
}
|
|
5944
6086
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -5961,38 +6103,38 @@ async function runSidecar(opts) {
|
|
|
5961
6103
|
throw err;
|
|
5962
6104
|
}
|
|
5963
6105
|
} else {
|
|
5964
|
-
console.log(
|
|
6106
|
+
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).`));
|
|
5965
6107
|
}
|
|
5966
6108
|
const envState = upsertEnvLocal(root, token);
|
|
5967
6109
|
ensureGitignored(root);
|
|
5968
|
-
console.log(` ${
|
|
6110
|
+
console.log(` ${import_chalk35.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
5969
6111
|
const wireState = wireInstrumentation(root);
|
|
5970
|
-
console.log(` ${
|
|
6112
|
+
console.log(` ${import_chalk35.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
5971
6113
|
installSidecarPackage(root);
|
|
5972
6114
|
let inspectorPath = null;
|
|
5973
6115
|
if (!opts.noInspector) {
|
|
5974
6116
|
inspectorPath = generateInspector(root, detected.router);
|
|
5975
|
-
if (inspectorPath) console.log(` ${
|
|
6117
|
+
if (inspectorPath) console.log(` ${import_chalk35.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
5976
6118
|
}
|
|
5977
6119
|
console.log("");
|
|
5978
|
-
console.log(
|
|
5979
|
-
console.log(` 1. ${
|
|
5980
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
5981
|
-
console.log(` 3. Approve the ones to route: ${
|
|
6120
|
+
console.log(import_chalk35.default.bold("Done. What happens next:"));
|
|
6121
|
+
console.log(` 1. ${import_chalk35.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
6122
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk35.default.bold("candidates")} \u2014 list them: ${import_chalk35.default.cyan("apiblaze sidecar")}`);
|
|
6123
|
+
console.log(` 3. Approve the ones to route: ${import_chalk35.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
5982
6124
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
5983
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
5984
|
-
if (switchingTeam) console.log(
|
|
6125
|
+
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)`);
|
|
6126
|
+
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>\`.`));
|
|
5985
6127
|
console.log("");
|
|
5986
|
-
console.log(
|
|
5987
|
-
console.log(
|
|
5988
|
-
console.log(
|
|
6128
|
+
console.log(import_chalk35.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
6129
|
+
console.log(import_chalk35.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
6130
|
+
console.log(import_chalk35.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
5989
6131
|
console.log("");
|
|
5990
|
-
console.log(
|
|
5991
|
-
console.log(
|
|
6132
|
+
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."));
|
|
6133
|
+
console.log(import_chalk35.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
5992
6134
|
}
|
|
5993
6135
|
|
|
5994
6136
|
// src/commands/origins.ts
|
|
5995
|
-
var
|
|
6137
|
+
var import_chalk36 = __toESM(require("chalk"));
|
|
5996
6138
|
var import_ora19 = __toESM(require("ora"));
|
|
5997
6139
|
init_admin();
|
|
5998
6140
|
init_auth();
|
|
@@ -6002,7 +6144,7 @@ async function runOriginsList(opts) {
|
|
|
6002
6144
|
if (!loadCredentials()) {
|
|
6003
6145
|
const cred = loadAnonCred();
|
|
6004
6146
|
if (!cred) {
|
|
6005
|
-
console.log(
|
|
6147
|
+
console.log(import_chalk36.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
6006
6148
|
return;
|
|
6007
6149
|
}
|
|
6008
6150
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -6020,27 +6162,27 @@ async function runOriginsList(opts) {
|
|
|
6020
6162
|
}
|
|
6021
6163
|
const routed = out.routed ?? [];
|
|
6022
6164
|
const candidates = out.candidates ?? [];
|
|
6023
|
-
console.log(
|
|
6165
|
+
console.log(import_chalk36.default.bold(`
|
|
6024
6166
|
Routed through APIblaze (${routed.length})`));
|
|
6025
|
-
if (!routed.length) console.log(
|
|
6026
|
-
for (const r of routed) console.log(` ${
|
|
6027
|
-
console.log(
|
|
6167
|
+
if (!routed.length) console.log(import_chalk36.default.dim(" none yet"));
|
|
6168
|
+
for (const r of routed) console.log(` ${import_chalk36.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk36.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
6169
|
+
console.log(import_chalk36.default.bold(`
|
|
6028
6170
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
6029
|
-
if (!candidates.length) console.log(
|
|
6171
|
+
if (!candidates.length) console.log(import_chalk36.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
6030
6172
|
for (const c of candidates) {
|
|
6031
|
-
console.log(` ${
|
|
6173
|
+
console.log(` ${import_chalk36.default.yellow("\u25CB")} ${c.origin} ${import_chalk36.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
6032
6174
|
}
|
|
6033
6175
|
if (candidates.length) {
|
|
6034
|
-
console.log(
|
|
6176
|
+
console.log(import_chalk36.default.dim(`
|
|
6035
6177
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
6036
|
-
console.log(
|
|
6178
|
+
console.log(import_chalk36.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
6037
6179
|
}
|
|
6038
6180
|
}
|
|
6039
6181
|
async function runOriginsApprove(origin, opts) {
|
|
6040
6182
|
if (!loadCredentials()) {
|
|
6041
6183
|
const cred = loadAnonCred();
|
|
6042
6184
|
if (!cred) {
|
|
6043
|
-
console.error(
|
|
6185
|
+
console.error(import_chalk36.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
6044
6186
|
process.exit(1);
|
|
6045
6187
|
}
|
|
6046
6188
|
const spinner2 = (0, import_ora19.default)(`Approving ${origin} (anonymous)...`).start();
|
|
@@ -6093,7 +6235,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
6093
6235
|
}
|
|
6094
6236
|
|
|
6095
6237
|
// src/commands/op.ts
|
|
6096
|
-
var
|
|
6238
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
6097
6239
|
init_auth();
|
|
6098
6240
|
init_trace();
|
|
6099
6241
|
init_types();
|
|
@@ -6126,82 +6268,82 @@ function printResidue(report, applied) {
|
|
|
6126
6268
|
const up = report?.upstash ?? {};
|
|
6127
6269
|
const fga = report?.fga ?? {};
|
|
6128
6270
|
const ghosts = report?.ghosts ?? {};
|
|
6129
|
-
console.log(
|
|
6130
|
-
console.log(
|
|
6271
|
+
console.log(import_chalk37.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
6272
|
+
console.log(import_chalk37.default.bold("\n Upstash"));
|
|
6131
6273
|
const orphans = up.orphans ?? [];
|
|
6132
|
-
if (orphans.length === 0) console.log(
|
|
6133
|
-
for (const o of orphans) console.log(` ${
|
|
6134
|
-
console.log(
|
|
6274
|
+
if (orphans.length === 0) console.log(import_chalk37.default.green(" no orphaned keys"));
|
|
6275
|
+
for (const o of orphans) console.log(` ${import_chalk37.default.yellow(o.key)} ${import_chalk37.default.dim(`\u2014 ${o.reason}`)}`);
|
|
6276
|
+
console.log(import_chalk37.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
6135
6277
|
if (up.anon_wallet_detail) {
|
|
6136
6278
|
const d = up.anon_wallet_detail;
|
|
6137
|
-
console.log(
|
|
6279
|
+
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)"}`));
|
|
6138
6280
|
}
|
|
6139
6281
|
if (up.keyspace_census) {
|
|
6140
6282
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
6141
|
-
console.log(
|
|
6283
|
+
console.log(import_chalk37.default.dim(` keyspace: ${census}`));
|
|
6142
6284
|
}
|
|
6143
|
-
if (up.unknown?.length) console.log(
|
|
6144
|
-
if (applied) console.log(` ${
|
|
6145
|
-
for (const e of up.errors ?? []) console.log(
|
|
6146
|
-
console.log(
|
|
6285
|
+
if (up.unknown?.length) console.log(import_chalk37.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
6286
|
+
if (applied) console.log(` ${import_chalk37.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
6287
|
+
for (const e of up.errors ?? []) console.log(import_chalk37.default.red(` error: ${e}`));
|
|
6288
|
+
console.log(import_chalk37.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
6147
6289
|
if (applied) {
|
|
6148
6290
|
const swept = fga?.swept ?? [];
|
|
6149
|
-
if (swept.length === 0) console.log(
|
|
6291
|
+
if (swept.length === 0) console.log(import_chalk37.default.green(" no orphaned stores"));
|
|
6150
6292
|
for (const s of swept) {
|
|
6151
6293
|
console.log(
|
|
6152
|
-
` ${
|
|
6294
|
+
` ${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`)}`
|
|
6153
6295
|
);
|
|
6154
6296
|
}
|
|
6155
|
-
if (fga?.remaining) console.log(
|
|
6297
|
+
if (fga?.remaining) console.log(import_chalk37.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
6156
6298
|
const st = fga?.side_tables;
|
|
6157
|
-
if (st) console.log(
|
|
6299
|
+
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})` : ""}`));
|
|
6158
6300
|
} else {
|
|
6159
6301
|
const fgaOrphans = fga?.orphans ?? [];
|
|
6160
|
-
if (fgaOrphans.length === 0) console.log(
|
|
6302
|
+
if (fgaOrphans.length === 0) console.log(import_chalk37.default.green(" no orphaned stores"));
|
|
6161
6303
|
for (const s of fgaOrphans) {
|
|
6162
6304
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
6163
|
-
console.log(` ${
|
|
6305
|
+
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)`)}`);
|
|
6164
6306
|
}
|
|
6165
|
-
console.log(
|
|
6307
|
+
console.log(import_chalk37.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
6166
6308
|
const st = fga?.side_tables;
|
|
6167
|
-
if (st) console.log(
|
|
6309
|
+
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`));
|
|
6168
6310
|
}
|
|
6169
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
6170
|
-
console.log(
|
|
6311
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk37.default.red(` error: ${e}`));
|
|
6312
|
+
console.log(import_chalk37.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
6171
6313
|
if (applied) {
|
|
6172
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
6173
|
-
else console.log(` ${
|
|
6314
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk37.default.green(" no ghost tuples"));
|
|
6315
|
+
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)`)}`);
|
|
6174
6316
|
} else {
|
|
6175
6317
|
const n = ghosts?.ghost_count ?? 0;
|
|
6176
|
-
if (n === 0) console.log(
|
|
6318
|
+
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)`)}`));
|
|
6177
6319
|
else {
|
|
6178
|
-
console.log(
|
|
6320
|
+
console.log(import_chalk37.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
6179
6321
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
6180
|
-
console.log(
|
|
6322
|
+
console.log(import_chalk37.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
6181
6323
|
}
|
|
6182
|
-
if (n > 20) console.log(
|
|
6324
|
+
if (n > 20) console.log(import_chalk37.default.dim(` \u2026 and ${n - 20} more`));
|
|
6183
6325
|
}
|
|
6184
6326
|
}
|
|
6185
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
6327
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk37.default.red(` error: ${e}`));
|
|
6186
6328
|
console.log();
|
|
6187
6329
|
}
|
|
6188
6330
|
async function runOp(sub, opts = {}) {
|
|
6189
6331
|
if (!loadCredentials()) {
|
|
6190
|
-
console.log(
|
|
6332
|
+
console.log(import_chalk37.default.dim("Not logged in. Run `apiblaze login`."));
|
|
6191
6333
|
return;
|
|
6192
6334
|
}
|
|
6193
6335
|
if (!isOperatorLogin()) {
|
|
6194
|
-
console.log(
|
|
6336
|
+
console.log(import_chalk37.default.dim("`apiblaze op` is only available to platform operators."));
|
|
6195
6337
|
return;
|
|
6196
6338
|
}
|
|
6197
6339
|
switch (sub) {
|
|
6198
6340
|
case void 0:
|
|
6199
6341
|
case "menu": {
|
|
6200
|
-
console.log(
|
|
6201
|
-
console.log(` ${
|
|
6202
|
-
console.log(` ${
|
|
6203
|
-
console.log(` ${
|
|
6204
|
-
console.log(
|
|
6342
|
+
console.log(import_chalk37.default.bold("\nOperator menu"));
|
|
6343
|
+
console.log(` ${import_chalk37.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
6344
|
+
console.log(` ${import_chalk37.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk37.default.dim("-y to skip")})`);
|
|
6345
|
+
console.log(` ${import_chalk37.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
6346
|
+
console.log(import_chalk37.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
6205
6347
|
`));
|
|
6206
6348
|
return;
|
|
6207
6349
|
}
|
|
@@ -6220,17 +6362,17 @@ async function runOp(sub, opts = {}) {
|
|
|
6220
6362
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
6221
6363
|
printResidue(report, false);
|
|
6222
6364
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
6223
|
-
console.log(
|
|
6365
|
+
console.log(import_chalk37.default.green("Nothing to sweep."));
|
|
6224
6366
|
return;
|
|
6225
6367
|
}
|
|
6226
6368
|
if (!opts.yes) {
|
|
6227
6369
|
const readline2 = await import("readline/promises");
|
|
6228
6370
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
6229
6371
|
const answer = await rl.question(
|
|
6230
|
-
|
|
6372
|
+
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: `)
|
|
6231
6373
|
);
|
|
6232
6374
|
rl.close();
|
|
6233
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
6375
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk37.default.dim("Aborted."));
|
|
6234
6376
|
}
|
|
6235
6377
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
6236
6378
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -6241,15 +6383,15 @@ async function runOp(sub, opts = {}) {
|
|
|
6241
6383
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
6242
6384
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
6243
6385
|
const accounts = data?.accounts ?? [];
|
|
6244
|
-
if (accounts.length === 0) return void console.log(
|
|
6386
|
+
if (accounts.length === 0) return void console.log(import_chalk37.default.dim("No credit wallets."));
|
|
6245
6387
|
for (const a of accounts) {
|
|
6246
6388
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
6247
|
-
console.log(` ${
|
|
6389
|
+
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") : ""}`);
|
|
6248
6390
|
}
|
|
6249
6391
|
return;
|
|
6250
6392
|
}
|
|
6251
6393
|
default:
|
|
6252
|
-
console.log(
|
|
6394
|
+
console.log(import_chalk37.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
6253
6395
|
}
|
|
6254
6396
|
}
|
|
6255
6397
|
|
|
@@ -6311,7 +6453,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
6311
6453
|
try {
|
|
6312
6454
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
6313
6455
|
if (Number.isNaN(resolved)) {
|
|
6314
|
-
console.error(
|
|
6456
|
+
console.error(import_chalk38.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
6315
6457
|
process.exit(1);
|
|
6316
6458
|
}
|
|
6317
6459
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -6324,6 +6466,7 @@ var consumer = program.command("consumer").description("Act as a consumer of you
|
|
|
6324
6466
|
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)));
|
|
6325
6467
|
consumer.command("tokens").description("Show the logged-in consumer access / refresh / id tokens").option("--json", "Output machine-readable JSON").action(action((opts) => runConsumerTokens(opts)));
|
|
6326
6468
|
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)));
|
|
6469
|
+
program.command("flush").description("Wipe ALL local state (login, workspace keys, apichats, DP keys, secrets)").option("-y, --yes", "Skip the confirmation prompt").action(runFlush);
|
|
6327
6470
|
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)));
|
|
6328
6471
|
program.command("whoami").description("Show who you are \u2014 both API Producer and API Consumer").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
6329
6472
|
try {
|
|
@@ -6409,7 +6552,7 @@ function groupedCommandHelp() {
|
|
|
6409
6552
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
6410
6553
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
6411
6554
|
}).filter(Boolean).join("\n");
|
|
6412
|
-
return `${
|
|
6555
|
+
return `${import_chalk38.default.bold(g.title)}
|
|
6413
6556
|
${rows}`;
|
|
6414
6557
|
}).join("\n\n");
|
|
6415
6558
|
}
|
|
@@ -6443,14 +6586,14 @@ async function recoverStaleTeam() {
|
|
|
6443
6586
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
6444
6587
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
6445
6588
|
if (!linked) {
|
|
6446
|
-
console.error(
|
|
6589
|
+
console.error(import_chalk38.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
6447
6590
|
return;
|
|
6448
6591
|
}
|
|
6449
6592
|
if (linked.teamId === creds.teamId) return;
|
|
6450
6593
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
6451
6594
|
delete next.activeTenant;
|
|
6452
6595
|
saveCredentials(next);
|
|
6453
|
-
console.error(
|
|
6596
|
+
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.`));
|
|
6454
6597
|
} catch {
|
|
6455
6598
|
}
|
|
6456
6599
|
}
|
|
@@ -6458,16 +6601,16 @@ async function printError(err) {
|
|
|
6458
6601
|
if (err instanceof ApiError) {
|
|
6459
6602
|
const data = err.body;
|
|
6460
6603
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
6461
|
-
console.error(
|
|
6604
|
+
console.error(import_chalk38.default.red(`
|
|
6462
6605
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
6463
6606
|
if (err.status === 403 || err.status === 404) {
|
|
6464
6607
|
await recoverStaleTeam();
|
|
6465
6608
|
}
|
|
6466
6609
|
} else if (err instanceof Error) {
|
|
6467
|
-
console.error(
|
|
6610
|
+
console.error(import_chalk38.default.red(`
|
|
6468
6611
|
Error: ${err.message}`));
|
|
6469
6612
|
} else {
|
|
6470
|
-
console.error(
|
|
6613
|
+
console.error(import_chalk38.default.red("\nUnknown error"));
|
|
6471
6614
|
}
|
|
6472
6615
|
}
|
|
6473
6616
|
program.parse(process.argv);
|