apiblaze 0.19.28 → 0.19.30
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 +1354 -447
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -237,7 +237,7 @@ var init_api = __esm({
|
|
|
237
237
|
"use strict";
|
|
238
238
|
init_auth();
|
|
239
239
|
init_types();
|
|
240
|
-
DASHBOARD_BASE = "https://dashboard.apiblaze.com";
|
|
240
|
+
DASHBOARD_BASE = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
241
241
|
PUBLIC_API_BASE = "https://api.apiblaze.com";
|
|
242
242
|
}
|
|
243
243
|
});
|
|
@@ -272,14 +272,14 @@ async function resolveLinkedTeam(opts) {
|
|
|
272
272
|
return { teamId: picked2.teamId, teamName: picked2.name };
|
|
273
273
|
}
|
|
274
274
|
if (opts.preferredId) {
|
|
275
|
-
console.
|
|
275
|
+
console.error(import_chalk.default.yellow("\nYour previously linked team is no longer available."));
|
|
276
276
|
}
|
|
277
277
|
if (teams.length === 1) {
|
|
278
|
-
console.
|
|
278
|
+
console.error(`${import_chalk.default.cyan("\u2192")} Linking to your team ${import_chalk.default.bold(teams[0].name)}.`);
|
|
279
279
|
return { teamId: teams[0].teamId, teamName: teams[0].name };
|
|
280
280
|
}
|
|
281
281
|
if (!opts.interactive) {
|
|
282
|
-
console.
|
|
282
|
+
console.error(import_chalk.default.yellow(`Linking to "${teams[0].name}" \u2014 pass --team to choose another.`));
|
|
283
283
|
return { teamId: teams[0].teamId, teamName: teams[0].name };
|
|
284
284
|
}
|
|
285
285
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -392,24 +392,24 @@ function maskBody(body) {
|
|
|
392
392
|
}
|
|
393
393
|
function renderTrace() {
|
|
394
394
|
if (!verbose || entries.length === 0) return;
|
|
395
|
-
console.
|
|
396
|
-
console.
|
|
397
|
-
console.
|
|
395
|
+
console.error(import_chalk6.default.dim("\n" + "\u2500".repeat(64)));
|
|
396
|
+
console.error(import_chalk6.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
|
|
397
|
+
console.error(
|
|
398
398
|
import_chalk6.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")
|
|
399
399
|
);
|
|
400
400
|
entries.forEach((e, i) => {
|
|
401
401
|
const n = entries.length > 1 ? import_chalk6.default.bold(`${i + 1}. `) : "";
|
|
402
|
-
if (e.summary) console.
|
|
402
|
+
if (e.summary) console.error(`${n}${import_chalk6.default.cyan(e.summary)}${e.status ? import_chalk6.default.dim(` (HTTP ${e.status})`) : ""}`);
|
|
403
403
|
const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
|
|
404
404
|
const masked = maskBody(e.body);
|
|
405
405
|
const hasBody = e.method !== "GET" && masked !== void 0;
|
|
406
|
-
console.
|
|
407
|
-
console.
|
|
406
|
+
console.error(import_chalk6.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
|
|
407
|
+
console.error(import_chalk6.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
|
|
408
408
|
if (hasBody) {
|
|
409
|
-
console.
|
|
410
|
-
console.
|
|
409
|
+
console.error(import_chalk6.default.green(" -H 'Content-Type: application/json' \\"));
|
|
410
|
+
console.error(import_chalk6.default.green(` -d '${masked}'`));
|
|
411
411
|
}
|
|
412
|
-
if (i < entries.length - 1) console.
|
|
412
|
+
if (i < entries.length - 1) console.error();
|
|
413
413
|
});
|
|
414
414
|
entries.length = 0;
|
|
415
415
|
}
|
|
@@ -458,7 +458,7 @@ function formatBilling(billing) {
|
|
|
458
458
|
}
|
|
459
459
|
function maybePrintBilling(data) {
|
|
460
460
|
const line = formatBilling(data?.billing);
|
|
461
|
-
if (line) console.
|
|
461
|
+
if (line) console.error(import_chalk7.default.magenta(` ${line}`));
|
|
462
462
|
}
|
|
463
463
|
var import_chalk7, DASHBOARD_BASE3;
|
|
464
464
|
var init_admin = __esm({
|
|
@@ -714,7 +714,7 @@ async function createTenantRow(teamId) {
|
|
|
714
714
|
const { name } = await inquirer3.prompt([{
|
|
715
715
|
type: "input",
|
|
716
716
|
name: "name",
|
|
717
|
-
message: `Tenant name ${
|
|
717
|
+
message: `Tenant name ${import_chalk30.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
|
|
718
718
|
validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
|
|
719
719
|
}]);
|
|
720
720
|
const slug = name.trim();
|
|
@@ -729,7 +729,7 @@ async function createTenantRow(teamId) {
|
|
|
729
729
|
} catch (err) {
|
|
730
730
|
const msg = err instanceof Error ? err.message : String(err);
|
|
731
731
|
if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
|
|
732
|
-
console.log(
|
|
732
|
+
console.log(import_chalk30.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
|
|
733
733
|
continue;
|
|
734
734
|
}
|
|
735
735
|
throw err;
|
|
@@ -746,11 +746,11 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
746
746
|
pageSize: 10,
|
|
747
747
|
choices: [
|
|
748
748
|
...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
|
|
749
|
-
...opts.allowSkip ? [new inquirer3.Separator(), { name:
|
|
749
|
+
...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk30.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
|
|
750
750
|
]
|
|
751
751
|
}]);
|
|
752
752
|
if (!provider) {
|
|
753
|
-
console.log(
|
|
753
|
+
console.log(import_chalk30.default.dim(" No login method yet \u2014 add one later under Login methods."));
|
|
754
754
|
return false;
|
|
755
755
|
}
|
|
756
756
|
const opt = PROVIDERS.find((p) => p.id === provider);
|
|
@@ -796,26 +796,26 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
796
796
|
async function createTenantInteractive(teamId) {
|
|
797
797
|
const { default: inquirer3 } = await import("inquirer");
|
|
798
798
|
const tenant2 = await createTenantRow(teamId);
|
|
799
|
-
console.log(
|
|
799
|
+
console.log(import_chalk30.default.green(` Tenant ${import_chalk30.default.bold(tenant2)} created.`));
|
|
800
800
|
const { users } = await inquirer3.prompt([{
|
|
801
801
|
type: "confirm",
|
|
802
802
|
name: "users",
|
|
803
803
|
default: false,
|
|
804
|
-
message: `Enable Users & groups? ${
|
|
804
|
+
message: `Enable Users & groups? ${import_chalk30.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
|
|
805
805
|
}]);
|
|
806
806
|
if (users) {
|
|
807
807
|
await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
|
|
808
808
|
});
|
|
809
|
-
console.log(
|
|
809
|
+
console.log(import_chalk30.default.green(" Users & groups enabled."));
|
|
810
810
|
}
|
|
811
811
|
await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
|
|
812
812
|
return tenant2;
|
|
813
813
|
}
|
|
814
|
-
var
|
|
814
|
+
var import_chalk30, import_ora10, PROVIDERS, DEFAULT_SCOPES;
|
|
815
815
|
var init_tenant_create = __esm({
|
|
816
816
|
"src/lib/tenant-create.ts"() {
|
|
817
817
|
"use strict";
|
|
818
|
-
|
|
818
|
+
import_chalk30 = __toESM(require("chalk"));
|
|
819
819
|
import_ora10 = __toESM(require("ora"));
|
|
820
820
|
init_admin();
|
|
821
821
|
PROVIDERS = [
|
|
@@ -841,7 +841,7 @@ var tenant_pick_exports = {};
|
|
|
841
841
|
__export(tenant_pick_exports, {
|
|
842
842
|
pickTenant: () => pickTenant
|
|
843
843
|
});
|
|
844
|
-
async function
|
|
844
|
+
async function fetchPage2(teamId, q) {
|
|
845
845
|
const out = await admin({
|
|
846
846
|
method: "GET",
|
|
847
847
|
path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1&limit=${PAGE}${q ? `&q=${encodeURIComponent(q)}` : ""}`,
|
|
@@ -854,10 +854,10 @@ async function fetchPage(teamId, q) {
|
|
|
854
854
|
}
|
|
855
855
|
function label(t, defaultTenant, active) {
|
|
856
856
|
const tags = [
|
|
857
|
-
t.tenant_name === active ?
|
|
858
|
-
t.tenant_name === defaultTenant ?
|
|
857
|
+
t.tenant_name === active ? import_chalk31.default.cyan("active scope") : "",
|
|
858
|
+
t.tenant_name === defaultTenant ? import_chalk31.default.dim("team default") : ""
|
|
859
859
|
].filter(Boolean).join(", ");
|
|
860
|
-
const disp = t.display_name && t.display_name !== t.tenant_name ?
|
|
860
|
+
const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk31.default.dim(` ${t.display_name}`) : "";
|
|
861
861
|
return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
|
|
862
862
|
}
|
|
863
863
|
async function pickTenant(teamId, opts = {}) {
|
|
@@ -866,13 +866,13 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
866
866
|
let q = opts.initialQuery ?? "";
|
|
867
867
|
for (; ; ) {
|
|
868
868
|
const spinner = (0, import_ora11.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
|
|
869
|
-
const page = await
|
|
869
|
+
const page = await fetchPage2(teamId, q).finally(() => spinner.stop());
|
|
870
870
|
if (!page.total && !q) {
|
|
871
871
|
if (opts.allowCreate) {
|
|
872
872
|
const { make } = await inquirer3.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
|
|
873
873
|
if (make) return await createTenantInline(teamId);
|
|
874
874
|
}
|
|
875
|
-
console.error(
|
|
875
|
+
console.error(import_chalk31.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
876
876
|
return null;
|
|
877
877
|
}
|
|
878
878
|
const truncated = page.total > page.rows.length;
|
|
@@ -881,7 +881,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
881
881
|
value: t.tenant_name
|
|
882
882
|
}));
|
|
883
883
|
if (truncated || q) {
|
|
884
|
-
choices.push(new inquirer3.Separator(
|
|
884
|
+
choices.push(new inquirer3.Separator(import_chalk31.default.dim(
|
|
885
885
|
truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
|
|
886
886
|
)));
|
|
887
887
|
choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
|
|
@@ -915,11 +915,11 @@ async function createTenantInline(teamId) {
|
|
|
915
915
|
const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
916
916
|
return createTenantInteractive2(teamId);
|
|
917
917
|
}
|
|
918
|
-
var
|
|
918
|
+
var import_chalk31, import_ora11, PAGE;
|
|
919
919
|
var init_tenant_pick = __esm({
|
|
920
920
|
"src/lib/tenant-pick.ts"() {
|
|
921
921
|
"use strict";
|
|
922
|
-
|
|
922
|
+
import_chalk31 = __toESM(require("chalk"));
|
|
923
923
|
import_ora11 = __toESM(require("ora"));
|
|
924
924
|
init_admin();
|
|
925
925
|
init_auth();
|
|
@@ -929,10 +929,10 @@ var init_tenant_pick = __esm({
|
|
|
929
929
|
|
|
930
930
|
// src/index.ts
|
|
931
931
|
var import_commander = require("commander");
|
|
932
|
-
var
|
|
932
|
+
var import_chalk48 = __toESM(require("chalk"));
|
|
933
933
|
|
|
934
934
|
// package.json
|
|
935
|
-
var version = "0.19.
|
|
935
|
+
var version = "0.19.30";
|
|
936
936
|
|
|
937
937
|
// src/index.ts
|
|
938
938
|
init_types();
|
|
@@ -942,7 +942,7 @@ var import_chalk2 = __toESM(require("chalk"));
|
|
|
942
942
|
var import_ora = __toESM(require("ora"));
|
|
943
943
|
init_auth();
|
|
944
944
|
init_team();
|
|
945
|
-
var DASHBOARD_BASE2 = "https://dashboard.apiblaze.com";
|
|
945
|
+
var DASHBOARD_BASE2 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
946
946
|
function openBrowser(url) {
|
|
947
947
|
const { exec } = require("child_process");
|
|
948
948
|
const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
@@ -3304,8 +3304,863 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
3304
3304
|
if (target === "kong") console.log(import_chalk24.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
|
|
3305
3305
|
}
|
|
3306
3306
|
|
|
3307
|
-
// src/commands/
|
|
3307
|
+
// src/commands/logs.ts
|
|
3308
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
3309
|
+
init_admin();
|
|
3310
|
+
init_auth();
|
|
3311
|
+
init_resolve();
|
|
3312
|
+
|
|
3313
|
+
// src/lib/log-view.ts
|
|
3308
3314
|
var import_chalk25 = __toESM(require("chalk"));
|
|
3315
|
+
var readline2 = __toESM(require("readline"));
|
|
3316
|
+
function statusColor(status) {
|
|
3317
|
+
if (status < 300) return import_chalk25.default.green;
|
|
3318
|
+
if (status < 400) return import_chalk25.default.blue;
|
|
3319
|
+
if (status < 500) return import_chalk25.default.yellow;
|
|
3320
|
+
return import_chalk25.default.red;
|
|
3321
|
+
}
|
|
3322
|
+
function methodColor(method) {
|
|
3323
|
+
switch ((method || "").toUpperCase()) {
|
|
3324
|
+
case "GET":
|
|
3325
|
+
return import_chalk25.default.green;
|
|
3326
|
+
case "POST":
|
|
3327
|
+
return import_chalk25.default.blue;
|
|
3328
|
+
case "PUT":
|
|
3329
|
+
case "PATCH":
|
|
3330
|
+
return import_chalk25.default.yellow;
|
|
3331
|
+
case "DELETE":
|
|
3332
|
+
return import_chalk25.default.red;
|
|
3333
|
+
default:
|
|
3334
|
+
return import_chalk25.default.white;
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
function clock(ts) {
|
|
3338
|
+
return new Date(ts).toLocaleTimeString(void 0, { hour12: false });
|
|
3339
|
+
}
|
|
3340
|
+
var ROUTE_W = 34;
|
|
3341
|
+
function routeCell(route) {
|
|
3342
|
+
const r = route || "\u2014";
|
|
3343
|
+
return (r.length > ROUTE_W ? `\u2026${r.slice(-(ROUTE_W - 1))}` : r).padEnd(ROUTE_W);
|
|
3344
|
+
}
|
|
3345
|
+
function formatRow(r) {
|
|
3346
|
+
const time = import_chalk25.default.dim(clock(r.ts));
|
|
3347
|
+
const method = methodColor(r.method)(String(r.method || "\u2014").padEnd(6));
|
|
3348
|
+
const route = routeCell(String(r.route || "\u2014"));
|
|
3349
|
+
const status = statusColor(r.status)(String(r.status ?? "\u2014").padStart(3));
|
|
3350
|
+
const ms = import_chalk25.default.dim(`${r.duration_ms}ms`.padStart(8));
|
|
3351
|
+
const step = r.error_step ? import_chalk25.default.dim(` ${r.error_step}`) : "";
|
|
3352
|
+
return `${time} ${method} ${route} ${status} ${ms}${step}`;
|
|
3353
|
+
}
|
|
3354
|
+
function printRow(r, json) {
|
|
3355
|
+
console.log(json ? JSON.stringify(r) : formatRow(r));
|
|
3356
|
+
}
|
|
3357
|
+
var WindowNotices = class {
|
|
3358
|
+
constructor() {
|
|
3359
|
+
this.saidUnavailable = false;
|
|
3360
|
+
this.saidGap = false;
|
|
3361
|
+
this.saidDropped = false;
|
|
3362
|
+
}
|
|
3363
|
+
note(res) {
|
|
3364
|
+
if (res.history_unavailable && !this.saidUnavailable) {
|
|
3365
|
+
this.saidUnavailable = true;
|
|
3366
|
+
console.error(import_chalk25.default.yellow("\u26A0 log history is unreachable right now \u2014 showing the live buffer only, older requests exist but cannot be read."));
|
|
3367
|
+
}
|
|
3368
|
+
if ((res.history_markers?.length ?? 0) > 0 && !this.saidGap) {
|
|
3369
|
+
this.saidGap = true;
|
|
3370
|
+
console.error(import_chalk25.default.yellow("\u26A0 this range includes a volume-ceiling gap \u2014 some rows were dropped at intake and are gone; counts are incomplete."));
|
|
3371
|
+
}
|
|
3372
|
+
if ((res.dropped_this_hour ?? 0) > 0 && !this.saidDropped) {
|
|
3373
|
+
this.saidDropped = true;
|
|
3374
|
+
console.error(import_chalk25.default.yellow(`\u26A0 volume ceiling \u2014 ${res.dropped_this_hour} rows dropped this hour; errors keep logging.`));
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
};
|
|
3378
|
+
var MAX_RECONNECTS = 5;
|
|
3379
|
+
var PING_MS = Number(process.env.APIBLAZE_TAIL_PING_MS) || 3e4;
|
|
3380
|
+
var LIVENESS_MS = PING_MS * 2.5;
|
|
3381
|
+
var STABLE_MS = 3e4;
|
|
3382
|
+
var SEEN_CAP = 1e4;
|
|
3383
|
+
var TAIL_INSPECT_BUFFER = 500;
|
|
3384
|
+
var TAIL_INSPECT_CHOICES = 40;
|
|
3385
|
+
async function runTailSession(opts) {
|
|
3386
|
+
const { default: WebSocket2 } = await import("ws");
|
|
3387
|
+
guardStdoutPipe();
|
|
3388
|
+
let closed = false;
|
|
3389
|
+
let everConnected = false;
|
|
3390
|
+
let reconnects = 0;
|
|
3391
|
+
let openedAt = 0;
|
|
3392
|
+
let lastFailure = "";
|
|
3393
|
+
let pingTimer;
|
|
3394
|
+
let activeSocket;
|
|
3395
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3396
|
+
const seenOrder = [];
|
|
3397
|
+
function remember(id) {
|
|
3398
|
+
seen.add(id);
|
|
3399
|
+
seenOrder.push(id);
|
|
3400
|
+
if (seenOrder.length > SEEN_CAP) seen.delete(seenOrder.shift());
|
|
3401
|
+
}
|
|
3402
|
+
const interactive = !opts.json && !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
3403
|
+
const recent = [];
|
|
3404
|
+
const held = [];
|
|
3405
|
+
let heldRowsDropped = 0;
|
|
3406
|
+
let heldNotesDropped = 0;
|
|
3407
|
+
let paused = false;
|
|
3408
|
+
let keysBound = false;
|
|
3409
|
+
function pushHeld(item) {
|
|
3410
|
+
if (held.length >= TAIL_INSPECT_BUFFER) {
|
|
3411
|
+
const victimIdx = held.findIndex((h) => h.kind === "row");
|
|
3412
|
+
if (victimIdx >= 0) {
|
|
3413
|
+
held.splice(victimIdx, 1);
|
|
3414
|
+
heldRowsDropped++;
|
|
3415
|
+
} else {
|
|
3416
|
+
held.shift();
|
|
3417
|
+
heldNotesDropped++;
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
held.push(item);
|
|
3421
|
+
}
|
|
3422
|
+
function diag(msg) {
|
|
3423
|
+
if (paused) {
|
|
3424
|
+
pushHeld({ kind: "note", msg });
|
|
3425
|
+
return;
|
|
3426
|
+
}
|
|
3427
|
+
console.error(msg);
|
|
3428
|
+
}
|
|
3429
|
+
function flushHeld() {
|
|
3430
|
+
if (!held.length && !heldRowsDropped && !heldNotesDropped) return;
|
|
3431
|
+
const rows = held.filter((h) => h.kind === "row").length;
|
|
3432
|
+
const total = rows + heldRowsDropped;
|
|
3433
|
+
if (total) console.log(import_chalk25.default.dim(`\u2500\u2500\u2500 ${total} request${total === 1 ? "" : "s"} arrived while paused \u2500\u2500\u2500`));
|
|
3434
|
+
for (const h of held) {
|
|
3435
|
+
if (h.kind === "row") printRow(h.row, opts.json);
|
|
3436
|
+
else console.error(h.msg);
|
|
3437
|
+
}
|
|
3438
|
+
if (heldRowsDropped || heldNotesDropped) {
|
|
3439
|
+
console.log(import_chalk25.default.dim(`\u2500\u2500\u2500 ${heldRowsDropped} older one${heldRowsDropped === 1 ? "" : "s"} not shown (pause buffer full) \u2014 browse them with \`logs list\` \u2500\u2500\u2500`));
|
|
3440
|
+
}
|
|
3441
|
+
held.length = 0;
|
|
3442
|
+
heldRowsDropped = 0;
|
|
3443
|
+
heldNotesDropped = 0;
|
|
3444
|
+
}
|
|
3445
|
+
function shutdown() {
|
|
3446
|
+
closed = true;
|
|
3447
|
+
if (pingTimer) clearInterval(pingTimer);
|
|
3448
|
+
unbindKeys();
|
|
3449
|
+
try {
|
|
3450
|
+
activeSocket?.close();
|
|
3451
|
+
} catch {
|
|
3452
|
+
}
|
|
3453
|
+
if (!opts.json) console.log(import_chalk25.default.dim("\nStopped."));
|
|
3454
|
+
process.exit(0);
|
|
3455
|
+
}
|
|
3456
|
+
function onKeypress(_s, key) {
|
|
3457
|
+
if (key?.ctrl && key.name === "c") {
|
|
3458
|
+
shutdown();
|
|
3459
|
+
return;
|
|
3460
|
+
}
|
|
3461
|
+
if (key?.name === "return" || key?.name === "enter") void openInspector();
|
|
3462
|
+
}
|
|
3463
|
+
function bindKeys() {
|
|
3464
|
+
if (!interactive || keysBound || paused) return;
|
|
3465
|
+
keysBound = true;
|
|
3466
|
+
readline2.emitKeypressEvents(process.stdin);
|
|
3467
|
+
process.stdin.setRawMode(true);
|
|
3468
|
+
process.stdin.on("keypress", onKeypress);
|
|
3469
|
+
process.stdin.resume();
|
|
3470
|
+
}
|
|
3471
|
+
function unbindKeys() {
|
|
3472
|
+
if (!keysBound) return;
|
|
3473
|
+
keysBound = false;
|
|
3474
|
+
process.stdin.off("keypress", onKeypress);
|
|
3475
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
3476
|
+
process.stdin.pause();
|
|
3477
|
+
}
|
|
3478
|
+
async function openInspector() {
|
|
3479
|
+
if (paused) return;
|
|
3480
|
+
if (recent.length === 0) {
|
|
3481
|
+
console.log(import_chalk25.default.dim(" nothing captured yet \u2014 waiting for a request\u2026"));
|
|
3482
|
+
return;
|
|
3483
|
+
}
|
|
3484
|
+
paused = true;
|
|
3485
|
+
unbindKeys();
|
|
3486
|
+
try {
|
|
3487
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
3488
|
+
console.log(import_chalk25.default.dim("\n\u2500\u2500\u2500 paused \xB7 still capturing in the background \u2500\u2500\u2500"));
|
|
3489
|
+
for (; ; ) {
|
|
3490
|
+
const choices = recent.slice(-TAIL_INSPECT_CHOICES).reverse().map((r) => ({ name: formatRow(r), value: r.request_id }));
|
|
3491
|
+
choices.push({ name: import_chalk25.default.dim("\u2039resume tail\u203A"), value: "__resume__" });
|
|
3492
|
+
const { picked } = await inquirer3.prompt([{
|
|
3493
|
+
type: "list",
|
|
3494
|
+
name: "picked",
|
|
3495
|
+
message: "Inspect which request?",
|
|
3496
|
+
choices,
|
|
3497
|
+
pageSize: 15
|
|
3498
|
+
}]);
|
|
3499
|
+
if (picked === "__resume__") break;
|
|
3500
|
+
const row = recent.find((r) => r.request_id === picked);
|
|
3501
|
+
if (row) printDetail(row, opts.detail);
|
|
3502
|
+
else console.log(import_chalk25.default.dim(" that request has scrolled out of the tail buffer \u2014 find it with `logs list`.\n"));
|
|
3503
|
+
}
|
|
3504
|
+
} finally {
|
|
3505
|
+
paused = false;
|
|
3506
|
+
flushHeld();
|
|
3507
|
+
console.log(import_chalk25.default.dim("\u2500\u2500\u2500 resumed \u2500\u2500\u2500"));
|
|
3508
|
+
bindKeys();
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
process.on("SIGINT", shutdown);
|
|
3512
|
+
function scheduleReconnect() {
|
|
3513
|
+
if (closed) return;
|
|
3514
|
+
if (reconnects >= MAX_RECONNECTS) {
|
|
3515
|
+
paused = false;
|
|
3516
|
+
flushHeld();
|
|
3517
|
+
console.error(import_chalk25.default.red(`Connection lost \u2014 giving up after repeated reconnects${lastFailure ? ` (last error: ${lastFailure})` : ""}.`));
|
|
3518
|
+
process.exit(1);
|
|
3519
|
+
}
|
|
3520
|
+
const delay = Math.min(1e3 * 2 ** reconnects, 15e3);
|
|
3521
|
+
reconnects++;
|
|
3522
|
+
diag(import_chalk25.default.dim(`disconnected${lastFailure ? ` (${lastFailure})` : ""} \u2014 reconnecting in ${Math.round(delay / 1e3)}s\u2026`));
|
|
3523
|
+
setTimeout(() => {
|
|
3524
|
+
if (!closed) void connect();
|
|
3525
|
+
}, delay);
|
|
3526
|
+
}
|
|
3527
|
+
async function connect() {
|
|
3528
|
+
let url;
|
|
3529
|
+
try {
|
|
3530
|
+
url = await opts.mintTicket();
|
|
3531
|
+
} catch (err) {
|
|
3532
|
+
if (!everConnected) {
|
|
3533
|
+
console.error(import_chalk25.default.red(`Could not start tail: ${err.message}`));
|
|
3534
|
+
process.exit(1);
|
|
3535
|
+
}
|
|
3536
|
+
lastFailure = `ticket mint failed: ${err.message}`;
|
|
3537
|
+
scheduleReconnect();
|
|
3538
|
+
return;
|
|
3539
|
+
}
|
|
3540
|
+
let socket;
|
|
3541
|
+
try {
|
|
3542
|
+
socket = new WebSocket2(url);
|
|
3543
|
+
} catch (err) {
|
|
3544
|
+
lastFailure = `bad tail URL: ${err.message}`;
|
|
3545
|
+
if (!everConnected) {
|
|
3546
|
+
console.error(import_chalk25.default.red(`Could not start tail: ${lastFailure}`));
|
|
3547
|
+
process.exit(1);
|
|
3548
|
+
}
|
|
3549
|
+
scheduleReconnect();
|
|
3550
|
+
return;
|
|
3551
|
+
}
|
|
3552
|
+
activeSocket = socket;
|
|
3553
|
+
let lastInbound = Date.now();
|
|
3554
|
+
socket.on("open", () => {
|
|
3555
|
+
const isReconnect = everConnected;
|
|
3556
|
+
everConnected = true;
|
|
3557
|
+
openedAt = Date.now();
|
|
3558
|
+
lastInbound = Date.now();
|
|
3559
|
+
lastFailure = "";
|
|
3560
|
+
if (isReconnect) {
|
|
3561
|
+
diag(import_chalk25.default.dim("reconnected \u2014 requests during the gap were not replayed (browse them with `logs list`)."));
|
|
3562
|
+
} else {
|
|
3563
|
+
diag(import_chalk25.default.dim(interactive ? "[Enter] inspect a request \xB7 [Ctrl+C] stop\n" : "Ctrl+C to stop."));
|
|
3564
|
+
}
|
|
3565
|
+
bindKeys();
|
|
3566
|
+
pingTimer = setInterval(() => {
|
|
3567
|
+
if (Date.now() - lastInbound > LIVENESS_MS) {
|
|
3568
|
+
lastFailure = `no pong for ${Math.round(LIVENESS_MS / 1e3)}s \u2014 connection presumed dead`;
|
|
3569
|
+
try {
|
|
3570
|
+
socket.terminate();
|
|
3571
|
+
} catch {
|
|
3572
|
+
}
|
|
3573
|
+
return;
|
|
3574
|
+
}
|
|
3575
|
+
try {
|
|
3576
|
+
socket.send("ping");
|
|
3577
|
+
} catch {
|
|
3578
|
+
}
|
|
3579
|
+
}, PING_MS);
|
|
3580
|
+
});
|
|
3581
|
+
socket.on("message", (data) => {
|
|
3582
|
+
lastInbound = Date.now();
|
|
3583
|
+
let frame;
|
|
3584
|
+
try {
|
|
3585
|
+
frame = JSON.parse(String(data));
|
|
3586
|
+
} catch {
|
|
3587
|
+
return;
|
|
3588
|
+
}
|
|
3589
|
+
if (frame?.type === "ceiling") {
|
|
3590
|
+
diag(import_chalk25.default.yellow(
|
|
3591
|
+
`\u26A0 volume ceiling \u2014 ${Number(frame.dropped_this_hour) || 0} rows dropped this hour; errors keep streaming.`
|
|
3592
|
+
));
|
|
3593
|
+
return;
|
|
3594
|
+
}
|
|
3595
|
+
if (!frame?.request_id || seen.has(frame.request_id)) return;
|
|
3596
|
+
remember(frame.request_id);
|
|
3597
|
+
if (opts.errorsOnly && Number(frame.status) < 400) return;
|
|
3598
|
+
recent.push(frame);
|
|
3599
|
+
if (recent.length > TAIL_INSPECT_BUFFER) recent.shift();
|
|
3600
|
+
if (paused) {
|
|
3601
|
+
pushHeld({ kind: "row", row: frame });
|
|
3602
|
+
return;
|
|
3603
|
+
}
|
|
3604
|
+
printRow(frame, opts.json);
|
|
3605
|
+
});
|
|
3606
|
+
socket.on("error", (err) => {
|
|
3607
|
+
lastFailure = err?.message || String(err);
|
|
3608
|
+
});
|
|
3609
|
+
socket.on("close", (code, reason) => {
|
|
3610
|
+
if (pingTimer) clearInterval(pingTimer);
|
|
3611
|
+
if (openedAt && Date.now() - openedAt >= STABLE_MS) reconnects = 0;
|
|
3612
|
+
openedAt = 0;
|
|
3613
|
+
const why = String(reason ?? "").trim();
|
|
3614
|
+
if (why) lastFailure = `socket closed: ${why} (${code})`;
|
|
3615
|
+
scheduleReconnect();
|
|
3616
|
+
});
|
|
3617
|
+
}
|
|
3618
|
+
await connect();
|
|
3619
|
+
}
|
|
3620
|
+
function localDayMs(ts) {
|
|
3621
|
+
const d = new Date(ts);
|
|
3622
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
3623
|
+
}
|
|
3624
|
+
function nextLocalDayMs(dayMs) {
|
|
3625
|
+
const d = new Date(dayMs);
|
|
3626
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1).getTime();
|
|
3627
|
+
}
|
|
3628
|
+
function dayKey(ts) {
|
|
3629
|
+
const d = new Date(ts);
|
|
3630
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
3631
|
+
}
|
|
3632
|
+
function dayLabel(dayMs, now) {
|
|
3633
|
+
const today = localDayMs(now);
|
|
3634
|
+
const n = new Date(now);
|
|
3635
|
+
const yesterday = new Date(n.getFullYear(), n.getMonth(), n.getDate() - 1).getTime();
|
|
3636
|
+
if (dayMs === today) return "Today";
|
|
3637
|
+
if (dayMs === yesterday) return "Yesterday";
|
|
3638
|
+
return new Date(dayMs).toLocaleDateString(void 0, { weekday: "short", month: "short", day: "numeric" });
|
|
3639
|
+
}
|
|
3640
|
+
function groupIntoDays(rows) {
|
|
3641
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
3642
|
+
const order = [];
|
|
3643
|
+
for (const r of [...rows].sort((a, b) => b.ts - a.ts)) {
|
|
3644
|
+
const k = dayKey(r.ts);
|
|
3645
|
+
let b = byKey.get(k);
|
|
3646
|
+
if (!b) {
|
|
3647
|
+
b = { key: k, dayMs: localDayMs(r.ts), rows: [], errors: 0 };
|
|
3648
|
+
byKey.set(k, b);
|
|
3649
|
+
order.push(b);
|
|
3650
|
+
}
|
|
3651
|
+
b.rows.push(r);
|
|
3652
|
+
if (r.status >= 400) b.errors++;
|
|
3653
|
+
}
|
|
3654
|
+
return order;
|
|
3655
|
+
}
|
|
3656
|
+
var SCAN_LIMIT = 200;
|
|
3657
|
+
var PAGE_LIMIT = 50;
|
|
3658
|
+
var JSON_PAGE_LIMIT = 500;
|
|
3659
|
+
var MAX_EMPTY_HOPS = 100;
|
|
3660
|
+
async function fetchPage(fetchWindow3, params, notices) {
|
|
3661
|
+
let cursor = params.cursor;
|
|
3662
|
+
for (let hop = 0; ; hop++) {
|
|
3663
|
+
const res = await fetchWindow3({ ...params, cursor });
|
|
3664
|
+
notices.note(res);
|
|
3665
|
+
const rows = res.rows ?? [];
|
|
3666
|
+
if (rows.length > 0 || !res.next_cursor) {
|
|
3667
|
+
return { rows, nextCursor: res.next_cursor ?? void 0 };
|
|
3668
|
+
}
|
|
3669
|
+
if (hop >= MAX_EMPTY_HOPS) {
|
|
3670
|
+
console.error(import_chalk25.default.yellow("\u26A0 stopped after scanning a long empty span \u2014 older rows may still exist."));
|
|
3671
|
+
return { rows: [], nextCursor: res.next_cursor, fuseTripped: true };
|
|
3672
|
+
}
|
|
3673
|
+
if (hop === 0) console.error(import_chalk25.default.dim("searching older history\u2026"));
|
|
3674
|
+
cursor = res.next_cursor;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
var epipeGuarded = false;
|
|
3678
|
+
function guardStdoutPipe() {
|
|
3679
|
+
if (epipeGuarded) return;
|
|
3680
|
+
epipeGuarded = true;
|
|
3681
|
+
process.stdout.on("error", (err) => {
|
|
3682
|
+
if (err?.code === "EPIPE") process.exit(0);
|
|
3683
|
+
throw err;
|
|
3684
|
+
});
|
|
3685
|
+
}
|
|
3686
|
+
var PLAIN_MAX_ROWS = 200;
|
|
3687
|
+
async function runPlainList(opts, notices) {
|
|
3688
|
+
guardStdoutPipe();
|
|
3689
|
+
const rows = [];
|
|
3690
|
+
let cursor;
|
|
3691
|
+
let more = false;
|
|
3692
|
+
let fuseTripped = false;
|
|
3693
|
+
while (rows.length < PLAIN_MAX_ROWS) {
|
|
3694
|
+
const page = await fetchPage(opts.fetchWindow, {
|
|
3695
|
+
limit: Math.min(PAGE_LIMIT, PLAIN_MAX_ROWS - rows.length),
|
|
3696
|
+
errorsOnly: opts.errorsOnly,
|
|
3697
|
+
cursor
|
|
3698
|
+
}, notices);
|
|
3699
|
+
rows.push(...page.rows);
|
|
3700
|
+
if (page.fuseTripped) {
|
|
3701
|
+
more = true;
|
|
3702
|
+
fuseTripped = true;
|
|
3703
|
+
break;
|
|
3704
|
+
}
|
|
3705
|
+
if (!page.nextCursor) break;
|
|
3706
|
+
cursor = page.nextCursor;
|
|
3707
|
+
if (rows.length >= PLAIN_MAX_ROWS) {
|
|
3708
|
+
more = true;
|
|
3709
|
+
break;
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
if (rows.length === 0) {
|
|
3713
|
+
console.log(import_chalk25.default.dim(fuseTripped ? "Nothing found in the scanned span \u2014 older requests may still exist." : opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
|
|
3714
|
+
return;
|
|
3715
|
+
}
|
|
3716
|
+
const now = Date.now();
|
|
3717
|
+
for (const d of groupIntoDays(rows)) {
|
|
3718
|
+
console.log(import_chalk25.default.bold(dayLabel(d.dayMs, now)));
|
|
3719
|
+
for (const r of d.rows) console.log(` ${formatRow(r)}`);
|
|
3720
|
+
console.log("");
|
|
3721
|
+
}
|
|
3722
|
+
const errs = rows.filter((r) => r.status >= 400).length;
|
|
3723
|
+
const tail = fuseTripped ? " \xB7 stopped early (long empty span) \u2014 older requests may still exist" : more ? " \xB7 showing the most recent \u2014 `--json` streams the rest" : "";
|
|
3724
|
+
console.log(import_chalk25.default.dim(
|
|
3725
|
+
`${rows.length} request${rows.length === 1 ? "" : "s"} \xB7 ${errs} error${errs === 1 ? "" : "s"}${tail}`
|
|
3726
|
+
));
|
|
3727
|
+
}
|
|
3728
|
+
async function runHistoryBrowser(opts) {
|
|
3729
|
+
const notices = new WindowNotices();
|
|
3730
|
+
if (opts.json) {
|
|
3731
|
+
guardStdoutPipe();
|
|
3732
|
+
let cursor2;
|
|
3733
|
+
let any = false;
|
|
3734
|
+
for (; ; ) {
|
|
3735
|
+
const page = await fetchPage(opts.fetchWindow, {
|
|
3736
|
+
limit: JSON_PAGE_LIMIT,
|
|
3737
|
+
errorsOnly: opts.errorsOnly,
|
|
3738
|
+
cursor: cursor2
|
|
3739
|
+
}, notices);
|
|
3740
|
+
for (const r of page.rows) console.log(JSON.stringify(r));
|
|
3741
|
+
any = any || page.rows.length > 0;
|
|
3742
|
+
if (page.fuseTripped || !page.nextCursor) {
|
|
3743
|
+
if (!any && !page.fuseTripped) console.error(import_chalk25.default.dim(opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
|
|
3744
|
+
return;
|
|
3745
|
+
}
|
|
3746
|
+
cursor2 = page.nextCursor;
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
if (opts.plain || !process.stdout.isTTY || !process.stdin.isTTY) {
|
|
3750
|
+
await runPlainList(opts, notices);
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
3753
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
3754
|
+
let scanRows = [];
|
|
3755
|
+
let scanCursor;
|
|
3756
|
+
{
|
|
3757
|
+
const first = await fetchPage(opts.fetchWindow, {
|
|
3758
|
+
limit: SCAN_LIMIT,
|
|
3759
|
+
errorsOnly: opts.errorsOnly
|
|
3760
|
+
}, notices);
|
|
3761
|
+
scanRows = first.rows;
|
|
3762
|
+
scanCursor = first.nextCursor;
|
|
3763
|
+
if (scanRows.length === 0) {
|
|
3764
|
+
console.log(import_chalk25.default.dim(first.fuseTripped ? "Nothing found in the scanned span \u2014 older rows may exist." : opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
|
|
3765
|
+
return;
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
let day;
|
|
3769
|
+
let scanFloorUnknown = false;
|
|
3770
|
+
for (; ; ) {
|
|
3771
|
+
const days = groupIntoDays(scanRows);
|
|
3772
|
+
const now2 = Date.now();
|
|
3773
|
+
const oldestKey = days[days.length - 1]?.key;
|
|
3774
|
+
const choices = days.map((d) => ({
|
|
3775
|
+
name: `${dayLabel(d.dayMs, now2).padEnd(12)} ${String(d.rows.length).padStart(4)}${(scanCursor !== void 0 || scanFloorUnknown) && d.key === oldestKey ? "+" : ""} requests${d.errors ? import_chalk25.default.red(` ${d.errors} errors`) : ""}`,
|
|
3776
|
+
value: d.key
|
|
3777
|
+
}));
|
|
3778
|
+
if (scanCursor) choices.push({ name: import_chalk25.default.dim("\u2026 older days"), value: "__older__" });
|
|
3779
|
+
const { chosenDay } = await inquirer3.prompt([{
|
|
3780
|
+
type: "list",
|
|
3781
|
+
name: "chosenDay",
|
|
3782
|
+
message: "Which day?",
|
|
3783
|
+
choices,
|
|
3784
|
+
pageSize: 20
|
|
3785
|
+
}]);
|
|
3786
|
+
if (chosenDay === "__older__") {
|
|
3787
|
+
const more = await fetchPage(opts.fetchWindow, {
|
|
3788
|
+
limit: SCAN_LIMIT,
|
|
3789
|
+
errorsOnly: opts.errorsOnly,
|
|
3790
|
+
cursor: scanCursor
|
|
3791
|
+
}, notices);
|
|
3792
|
+
scanRows = scanRows.concat(more.rows);
|
|
3793
|
+
if (more.fuseTripped) {
|
|
3794
|
+
scanFloorUnknown = true;
|
|
3795
|
+
scanCursor = void 0;
|
|
3796
|
+
} else {
|
|
3797
|
+
scanCursor = more.nextCursor;
|
|
3798
|
+
if (more.rows.length === 0 && !scanCursor) console.log(import_chalk25.default.dim("That was everything \u2014 no older requests."));
|
|
3799
|
+
}
|
|
3800
|
+
continue;
|
|
3801
|
+
}
|
|
3802
|
+
day = days.find((d) => d.key === chosenDay);
|
|
3803
|
+
break;
|
|
3804
|
+
}
|
|
3805
|
+
const since = day.dayMs;
|
|
3806
|
+
const until = nextLocalDayMs(day.dayMs) - 1;
|
|
3807
|
+
const now = Date.now();
|
|
3808
|
+
let cursor;
|
|
3809
|
+
let pageRows = [];
|
|
3810
|
+
let moreCursor;
|
|
3811
|
+
for (; ; ) {
|
|
3812
|
+
const page = await fetchPage(opts.fetchWindow, {
|
|
3813
|
+
limit: PAGE_LIMIT,
|
|
3814
|
+
since,
|
|
3815
|
+
until,
|
|
3816
|
+
errorsOnly: opts.errorsOnly,
|
|
3817
|
+
cursor
|
|
3818
|
+
}, notices);
|
|
3819
|
+
if (page.rows.length === 0 && pageRows.length === 0) {
|
|
3820
|
+
console.log(import_chalk25.default.dim(page.fuseTripped ? "Stopped scanning this day (long empty span) \u2014 older rows in it may exist." : "No requests for this day."));
|
|
3821
|
+
return;
|
|
3822
|
+
}
|
|
3823
|
+
if (page.rows.length === 0) {
|
|
3824
|
+
console.log(import_chalk25.default.dim(page.fuseTripped ? "Stopped scanning further into this day (long empty span) \u2014 older rows in it may exist." : "That was everything for this day."));
|
|
3825
|
+
moreCursor = void 0;
|
|
3826
|
+
} else {
|
|
3827
|
+
pageRows = page.rows;
|
|
3828
|
+
moreCursor = page.fuseTripped ? void 0 : page.nextCursor;
|
|
3829
|
+
}
|
|
3830
|
+
console.log("");
|
|
3831
|
+
const choices = pageRows.map((r) => ({
|
|
3832
|
+
name: formatRow(r),
|
|
3833
|
+
value: r.request_id
|
|
3834
|
+
}));
|
|
3835
|
+
if (moreCursor) choices.push({ name: import_chalk25.default.dim("\u2026 load more"), value: "__more__" });
|
|
3836
|
+
choices.push({ name: import_chalk25.default.dim("quit"), value: "__quit__" });
|
|
3837
|
+
for (; ; ) {
|
|
3838
|
+
const { picked } = await inquirer3.prompt([{
|
|
3839
|
+
type: "list",
|
|
3840
|
+
name: "picked",
|
|
3841
|
+
message: `${dayLabel(day.dayMs, now)} \u2014 select a request for detail`,
|
|
3842
|
+
choices,
|
|
3843
|
+
pageSize: 20
|
|
3844
|
+
}]);
|
|
3845
|
+
if (picked === "__quit__") return;
|
|
3846
|
+
if (picked === "__more__") {
|
|
3847
|
+
cursor = moreCursor;
|
|
3848
|
+
break;
|
|
3849
|
+
}
|
|
3850
|
+
const row = pageRows.find((r) => r.request_id === picked);
|
|
3851
|
+
if (row) printDetail(row, opts.detail);
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
var NOISE_HEADER = /^(cf-|sec-ch-|sec-fetch-|x-forwarded-|x-real-ip$)/i;
|
|
3856
|
+
var HEADER_PRIORITY = [
|
|
3857
|
+
"authorization",
|
|
3858
|
+
"x-api-key",
|
|
3859
|
+
"content-type",
|
|
3860
|
+
"accept",
|
|
3861
|
+
"x-end-user-id",
|
|
3862
|
+
"user-agent",
|
|
3863
|
+
"host",
|
|
3864
|
+
"origin",
|
|
3865
|
+
"referer",
|
|
3866
|
+
"content-length",
|
|
3867
|
+
"cookie"
|
|
3868
|
+
];
|
|
3869
|
+
var PANE_INDENT = " ".repeat(13);
|
|
3870
|
+
function wrapCells(items, width) {
|
|
3871
|
+
const cols = Math.max(40, width - PANE_INDENT.length);
|
|
3872
|
+
const lines = [];
|
|
3873
|
+
let line = "";
|
|
3874
|
+
for (const it of items) {
|
|
3875
|
+
if (line && line.length + 2 + it.length > cols) {
|
|
3876
|
+
lines.push(line);
|
|
3877
|
+
line = "";
|
|
3878
|
+
}
|
|
3879
|
+
line = line ? `${line} ${it}` : it;
|
|
3880
|
+
}
|
|
3881
|
+
if (line) lines.push(line);
|
|
3882
|
+
return lines.join(`
|
|
3883
|
+
${PANE_INDENT}`);
|
|
3884
|
+
}
|
|
3885
|
+
function renderHeaders(h) {
|
|
3886
|
+
if (!h) return import_chalk25.default.dim("\u2014");
|
|
3887
|
+
const width = process.stdout.columns || 100;
|
|
3888
|
+
if (h.values) {
|
|
3889
|
+
const e = Object.entries(h.values);
|
|
3890
|
+
if (!e.length) return import_chalk25.default.dim("none");
|
|
3891
|
+
const rank = (k) => {
|
|
3892
|
+
const i = HEADER_PRIORITY.indexOf(k.toLowerCase());
|
|
3893
|
+
return i === -1 ? NOISE_HEADER.test(k) ? 999 : 500 : i;
|
|
3894
|
+
};
|
|
3895
|
+
return e.sort((a, b) => rank(a[0]) - rank(b[0])).map(([k, v]) => `${import_chalk25.default.dim(k + ":")} ${v}`).join(`
|
|
3896
|
+
${PANE_INDENT}`);
|
|
3897
|
+
}
|
|
3898
|
+
if (!h.names?.length) return import_chalk25.default.dim("none");
|
|
3899
|
+
const names = [...h.names];
|
|
3900
|
+
const noise = names.filter((n) => NOISE_HEADER.test(n));
|
|
3901
|
+
const shown = names.filter((n) => !NOISE_HEADER.test(n)).sort((a, b) => {
|
|
3902
|
+
const ia = HEADER_PRIORITY.indexOf(a.toLowerCase());
|
|
3903
|
+
const ib = HEADER_PRIORITY.indexOf(b.toLowerCase());
|
|
3904
|
+
return (ia === -1 ? 500 : ia) - (ib === -1 ? 500 : ib) || a.localeCompare(b);
|
|
3905
|
+
});
|
|
3906
|
+
const body = shown.length ? wrapCells(shown, width) : import_chalk25.default.dim("(all forwarding headers)");
|
|
3907
|
+
const tail = noise.length ? `
|
|
3908
|
+
${PANE_INDENT}${import_chalk25.default.dim(`+ ${noise.length} forwarding/client-hint header${noise.length === 1 ? "" : "s"} (cf-*, sec-*)`)}` : "";
|
|
3909
|
+
return `${body}${tail} ${import_chalk25.default.dim("(names only)")}`;
|
|
3910
|
+
}
|
|
3911
|
+
function renderBody(b) {
|
|
3912
|
+
if (!b) return import_chalk25.default.dim("\u2014");
|
|
3913
|
+
const meta = [b.content_type, b.size != null ? `${b.size}B` : null, b.truncated ? "truncated" : null].filter(Boolean).join(" \xB7 ");
|
|
3914
|
+
if (b.not_captured) return import_chalk25.default.dim(`not captured (${b.not_captured})${meta ? " \xB7 " + meta : ""}`);
|
|
3915
|
+
const isShape = b.value === void 0 && b.shape !== void 0;
|
|
3916
|
+
const content = b.value !== void 0 ? b.value : b.shape;
|
|
3917
|
+
if (content === null || content === void 0 || content === "" || content === "empty") {
|
|
3918
|
+
return import_chalk25.default.dim(`empty${meta ? " \xB7 " + meta : ""}`);
|
|
3919
|
+
}
|
|
3920
|
+
const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
3921
|
+
const head = isShape ? import_chalk25.default.dim("shape only (values not captured)\n ") : "";
|
|
3922
|
+
return head + text.split("\n").join("\n ");
|
|
3923
|
+
}
|
|
3924
|
+
function pane(title, tone, view, empty) {
|
|
3925
|
+
console.log(` ${tone(title)}`);
|
|
3926
|
+
if (empty !== void 0) {
|
|
3927
|
+
console.log(` ${import_chalk25.default.dim(empty)}`);
|
|
3928
|
+
return;
|
|
3929
|
+
}
|
|
3930
|
+
const v = view ?? {};
|
|
3931
|
+
if (v.url) console.log(` ${import_chalk25.default.dim("target")} ${v.url}`);
|
|
3932
|
+
if (v.status != null) {
|
|
3933
|
+
console.log(` ${import_chalk25.default.dim("status")} ${statusColor(v.status)(String(v.status))}${v.status_text ? import_chalk25.default.dim(" " + v.status_text) : ""}`);
|
|
3934
|
+
}
|
|
3935
|
+
if (v.raw_path) console.log(` ${import_chalk25.default.dim("path")} ${v.raw_path}`);
|
|
3936
|
+
if (v.query && Object.keys(v.query).length) {
|
|
3937
|
+
console.log(` ${import_chalk25.default.dim("query")} ${Object.entries(v.query).map(([k, q]) => `${k}=${q}`).join(" ")}`);
|
|
3938
|
+
}
|
|
3939
|
+
if (v.jwt !== void 0) console.log(` ${import_chalk25.default.dim("jwt")} ${JSON.stringify(v.jwt)}`);
|
|
3940
|
+
console.log(` ${import_chalk25.default.dim("headers")} ${renderHeaders(v.headers)}`);
|
|
3941
|
+
console.log(` ${import_chalk25.default.dim("body")} ${renderBody(v.body)}`);
|
|
3942
|
+
}
|
|
3943
|
+
function printDetail(r, mode = "producer") {
|
|
3944
|
+
const v = r.views;
|
|
3945
|
+
console.log("");
|
|
3946
|
+
console.log(import_chalk25.default.bold(`${r.method} ${r.route}`) + " " + statusColor(r.status)(String(r.status)) + import_chalk25.default.dim(` ${r.duration_ms}ms`));
|
|
3947
|
+
const cost = typeof r.cost_cents === "number" && r.cost_cents > 0 ? ` \xB7 ${r.cost_cents}\xA2` : "";
|
|
3948
|
+
console.log(import_chalk25.default.dim(` request_id ${r.request_id}${r.environment ? ` \xB7 env ${r.environment}` : ""}${r.consumer_id ? ` \xB7 consumer ${r.consumer_id}` : ""}${cost}`));
|
|
3949
|
+
console.log("");
|
|
3950
|
+
if (!v) {
|
|
3951
|
+
console.log(import_chalk25.default.dim(" No view detail captured for this request.\n"));
|
|
3952
|
+
return;
|
|
3953
|
+
}
|
|
3954
|
+
pane("Request (before) \u2014 client \u2192 APIblaze", import_chalk25.default.cyan, v.request);
|
|
3955
|
+
if (mode === "producer") {
|
|
3956
|
+
pane(
|
|
3957
|
+
"Request (after) \u2014 APIblaze \u2192 upstream",
|
|
3958
|
+
import_chalk25.default.cyan,
|
|
3959
|
+
v.forwarded_request,
|
|
3960
|
+
v.forwarded_request ? void 0 : "Request did not reach an upstream."
|
|
3961
|
+
);
|
|
3962
|
+
pane(
|
|
3963
|
+
"Response (before) \u2014 upstream \u2192 APIblaze",
|
|
3964
|
+
import_chalk25.default.magenta,
|
|
3965
|
+
v.upstream_response,
|
|
3966
|
+
v.upstream_response ? void 0 : "No upstream response."
|
|
3967
|
+
);
|
|
3968
|
+
}
|
|
3969
|
+
pane("Response (after) \u2014 APIblaze \u2192 client", import_chalk25.default.magenta, v.response);
|
|
3970
|
+
console.log("");
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3973
|
+
// src/commands/logs.ts
|
|
3974
|
+
async function resolve(projectArg, versionArg, opts) {
|
|
3975
|
+
try {
|
|
3976
|
+
getAccessToken();
|
|
3977
|
+
} catch (err) {
|
|
3978
|
+
const consumerOnly = !loadCredentials() && !!loadConsumer();
|
|
3979
|
+
throw new Error(
|
|
3980
|
+
err.message + (consumerOnly ? "\nYou're signed in as an API Consumer. A proxy's full logs need a Producer login; for your own requests run `apiblaze consumer logs`." : "")
|
|
3981
|
+
);
|
|
3982
|
+
}
|
|
3983
|
+
const { teamId } = await resolveTeam(opts.team);
|
|
3984
|
+
const match = await resolveProject(teamId, projectArg, versionArg);
|
|
3985
|
+
const projectId = match.projectId;
|
|
3986
|
+
const version2 = versionArg || match.apiVersion;
|
|
3987
|
+
let tenant2 = opts.tenant;
|
|
3988
|
+
if (!tenant2) {
|
|
3989
|
+
const res = await admin({
|
|
3990
|
+
path: `/projects/${projectId}/${version2}/tenants`,
|
|
3991
|
+
method: "GET",
|
|
3992
|
+
summary: "list project tenants"
|
|
3993
|
+
});
|
|
3994
|
+
const tenants = res?.tenants ?? [];
|
|
3995
|
+
if (tenants.length === 0) {
|
|
3996
|
+
throw new Error(`No tenants attached to ${match.projectName}@${version2} \u2014 nothing to read logs for.`);
|
|
3997
|
+
} else if (tenants.length === 1) {
|
|
3998
|
+
tenant2 = tenants[0].tenant_name;
|
|
3999
|
+
} else if (opts.json || !process.stdin.isTTY) {
|
|
4000
|
+
throw new Error(`Project has ${tenants.length} tenants \u2014 pass --tenant <slug>. Attached: ${tenants.map((t) => t.tenant_name).join(", ")}`);
|
|
4001
|
+
} else {
|
|
4002
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
4003
|
+
const ans = await inquirer3.prompt([{
|
|
4004
|
+
type: "list",
|
|
4005
|
+
name: "tenant",
|
|
4006
|
+
message: "Which tenant?",
|
|
4007
|
+
choices: tenants.map((t) => ({
|
|
4008
|
+
name: t.display_name ? `${t.display_name} (${t.tenant_name})` : t.tenant_name,
|
|
4009
|
+
value: t.tenant_name
|
|
4010
|
+
}))
|
|
4011
|
+
}]);
|
|
4012
|
+
tenant2 = ans.tenant;
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
return { projectId, projectName: match.projectName, version: version2, tenant: tenant2 };
|
|
4016
|
+
}
|
|
4017
|
+
function otherPlaneHint() {
|
|
4018
|
+
if (loadConsumer()) console.log(import_chalk26.default.dim(" \u2039consumer view: apiblaze consumer logs\u203A"));
|
|
4019
|
+
}
|
|
4020
|
+
function fetchWindow(projectId, version2, tenant2) {
|
|
4021
|
+
return (params) => {
|
|
4022
|
+
const q = new URLSearchParams({ tenant: tenant2, limit: String(params.limit) });
|
|
4023
|
+
if (params.since !== void 0) q.set("since", String(params.since));
|
|
4024
|
+
if (params.until !== void 0) q.set("until", String(params.until));
|
|
4025
|
+
if (params.errorsOnly) q.set("errors_only", "true");
|
|
4026
|
+
if (params.cursor) q.set("cursor", params.cursor);
|
|
4027
|
+
return admin({
|
|
4028
|
+
path: `/projects/${projectId}/${version2}/logs/window?${q.toString()}`,
|
|
4029
|
+
method: "GET",
|
|
4030
|
+
summary: "read log window"
|
|
4031
|
+
});
|
|
4032
|
+
};
|
|
4033
|
+
}
|
|
4034
|
+
async function runLogsTail(projectArg, versionArg, opts) {
|
|
4035
|
+
const { projectId, projectName, version: version2, tenant: tenant2 } = await resolve(projectArg, versionArg, opts);
|
|
4036
|
+
if (!opts.json) {
|
|
4037
|
+
console.log(import_chalk26.default.bold("API Producer logs") + import_chalk26.default.dim(` \xB7 ${projectName}@${version2} \xB7 tenant ${tenant2}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
4038
|
+
console.log(import_chalk26.default.dim("every caller's requests, with cost and upstream detail"));
|
|
4039
|
+
otherPlaneHint();
|
|
4040
|
+
}
|
|
4041
|
+
await runTailSession({
|
|
4042
|
+
mintTicket: async () => {
|
|
4043
|
+
const res = await admin({
|
|
4044
|
+
path: `/projects/${projectId}/${version2}/logs/tail-ticket?tenant=${encodeURIComponent(tenant2)}`,
|
|
4045
|
+
method: "POST",
|
|
4046
|
+
summary: "mint log tail ticket"
|
|
4047
|
+
});
|
|
4048
|
+
if (!res?.ws_url) throw new Error("No ws_url returned by tail-ticket");
|
|
4049
|
+
return res.ws_url;
|
|
4050
|
+
},
|
|
4051
|
+
errorsOnly: opts.errorsOnly,
|
|
4052
|
+
json: opts.json,
|
|
4053
|
+
detail: "producer"
|
|
4054
|
+
});
|
|
4055
|
+
}
|
|
4056
|
+
async function runLogsList(projectArg, versionArg, opts) {
|
|
4057
|
+
const { projectId, projectName, version: version2, tenant: tenant2 } = await resolve(projectArg, versionArg, opts);
|
|
4058
|
+
if (!opts.json) {
|
|
4059
|
+
console.log(import_chalk26.default.bold("API Producer logs") + import_chalk26.default.dim(` \xB7 ${projectName}@${version2} \xB7 tenant ${tenant2}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
4060
|
+
console.log(import_chalk26.default.dim("every caller's requests, with cost and upstream detail"));
|
|
4061
|
+
otherPlaneHint();
|
|
4062
|
+
console.log("");
|
|
4063
|
+
}
|
|
4064
|
+
await runHistoryBrowser({
|
|
4065
|
+
fetchWindow: fetchWindow(projectId, version2, tenant2),
|
|
4066
|
+
errorsOnly: opts.errorsOnly,
|
|
4067
|
+
json: opts.json,
|
|
4068
|
+
plain: opts.plain,
|
|
4069
|
+
detail: "producer"
|
|
4070
|
+
});
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4073
|
+
// src/commands/consumer-logs.ts
|
|
4074
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
4075
|
+
init_auth();
|
|
4076
|
+
var CONSUMER_LOGS_BASE = process.env.APIBLAZE_CONSUMER_LOGS_BASE || "https://logs.apiblaze.com/consumer";
|
|
4077
|
+
function requireConsumer() {
|
|
4078
|
+
const c = loadConsumer();
|
|
4079
|
+
if (!c) {
|
|
4080
|
+
console.error(import_chalk27.default.red("Not logged in as an API Consumer. Run `apiblaze consumer login` first."));
|
|
4081
|
+
if (loadCredentials()) {
|
|
4082
|
+
console.error(import_chalk27.default.dim("You're signed in as an API Producer \u2014 for a proxy's full logs run `apiblaze logs <project>`."));
|
|
4083
|
+
}
|
|
4084
|
+
process.exit(1);
|
|
4085
|
+
}
|
|
4086
|
+
return { creds: c };
|
|
4087
|
+
}
|
|
4088
|
+
async function consumerLogsFetch(session, suffix, init) {
|
|
4089
|
+
const fresh = await validConsumerToken(session.creds) ?? session.creds;
|
|
4090
|
+
session.creds = fresh;
|
|
4091
|
+
const res = await fetch(`${CONSUMER_LOGS_BASE}${suffix}`, {
|
|
4092
|
+
...init,
|
|
4093
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${fresh.accessToken}`, ...init?.headers ?? {} }
|
|
4094
|
+
});
|
|
4095
|
+
let data = null;
|
|
4096
|
+
try {
|
|
4097
|
+
data = await res.json();
|
|
4098
|
+
} catch {
|
|
4099
|
+
}
|
|
4100
|
+
if (res.status === 404 || res.status === 405 || res.status === 501) {
|
|
4101
|
+
throw new Error("Consumer log access is not available on this tenant yet.");
|
|
4102
|
+
}
|
|
4103
|
+
if (res.status === 401) {
|
|
4104
|
+
throw new Error("Your consumer session was rejected \u2014 run `apiblaze consumer login` again.");
|
|
4105
|
+
}
|
|
4106
|
+
if (!res.ok) {
|
|
4107
|
+
const msg = data?.error ?? res.statusText;
|
|
4108
|
+
throw new Error(typeof msg === "string" ? msg : JSON.stringify(msg));
|
|
4109
|
+
}
|
|
4110
|
+
return data;
|
|
4111
|
+
}
|
|
4112
|
+
function otherPlaneHint2() {
|
|
4113
|
+
if (loadCredentials()) console.log(import_chalk27.default.dim(" \u2039producer view: apiblaze logs <project>\u203A"));
|
|
4114
|
+
}
|
|
4115
|
+
function fetchWindow2(session, project) {
|
|
4116
|
+
return (params) => {
|
|
4117
|
+
const q = new URLSearchParams({ limit: String(params.limit) });
|
|
4118
|
+
if (project) q.set("project", project);
|
|
4119
|
+
if (params.since !== void 0) q.set("since", String(params.since));
|
|
4120
|
+
if (params.until !== void 0) q.set("until", String(params.until));
|
|
4121
|
+
if (params.errorsOnly) q.set("errors_only", "true");
|
|
4122
|
+
if (params.cursor) q.set("cursor", params.cursor);
|
|
4123
|
+
return consumerLogsFetch(session, `/logs/window?${q.toString()}`);
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
async function runConsumerLogsTail(opts) {
|
|
4127
|
+
const session = requireConsumer();
|
|
4128
|
+
if (!opts.json) {
|
|
4129
|
+
console.log(import_chalk27.default.bold("API Consumer logs") + import_chalk27.default.dim(` \xB7 your own requests on ${session.creds.tenant}${opts.project ? ` \xB7 ${opts.project}` : ""}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
4130
|
+
console.log(import_chalk27.default.dim("cost and upstream details are producer-only and are not sent"));
|
|
4131
|
+
otherPlaneHint2();
|
|
4132
|
+
}
|
|
4133
|
+
await runTailSession({
|
|
4134
|
+
mintTicket: async () => {
|
|
4135
|
+
const q = opts.project ? `?project=${encodeURIComponent(opts.project)}` : "";
|
|
4136
|
+
const res = await consumerLogsFetch(session, `/logs/tail-ticket${q}`, { method: "POST" });
|
|
4137
|
+
if (!res?.ws_url) throw new Error("No ws_url returned by tail-ticket");
|
|
4138
|
+
return res.ws_url;
|
|
4139
|
+
},
|
|
4140
|
+
errorsOnly: opts.errorsOnly,
|
|
4141
|
+
json: opts.json,
|
|
4142
|
+
detail: "consumer"
|
|
4143
|
+
});
|
|
4144
|
+
}
|
|
4145
|
+
async function runConsumerLogsList(opts) {
|
|
4146
|
+
const session = requireConsumer();
|
|
4147
|
+
if (!opts.json) {
|
|
4148
|
+
console.log(import_chalk27.default.bold("API Consumer logs") + import_chalk27.default.dim(` \xB7 your own requests on ${session.creds.tenant}${opts.project ? ` \xB7 ${opts.project}` : ""}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
4149
|
+
console.log(import_chalk27.default.dim("cost and upstream details are producer-only and are not sent"));
|
|
4150
|
+
otherPlaneHint2();
|
|
4151
|
+
console.log("");
|
|
4152
|
+
}
|
|
4153
|
+
await runHistoryBrowser({
|
|
4154
|
+
fetchWindow: fetchWindow2(session, opts.project),
|
|
4155
|
+
errorsOnly: opts.errorsOnly,
|
|
4156
|
+
json: opts.json,
|
|
4157
|
+
plain: opts.plain,
|
|
4158
|
+
detail: "consumer"
|
|
4159
|
+
});
|
|
4160
|
+
}
|
|
4161
|
+
|
|
4162
|
+
// src/commands/config.ts
|
|
4163
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
3309
4164
|
var import_ora8 = __toESM(require("ora"));
|
|
3310
4165
|
init_admin();
|
|
3311
4166
|
init_resolve();
|
|
@@ -3329,7 +4184,7 @@ async function patchConfig(project, opts, body, summary) {
|
|
|
3329
4184
|
}
|
|
3330
4185
|
async function runTargetSet(project, opts) {
|
|
3331
4186
|
if (!opts.url) {
|
|
3332
|
-
console.error(
|
|
4187
|
+
console.error(import_chalk28.default.red("--url is required."));
|
|
3333
4188
|
process.exit(1);
|
|
3334
4189
|
}
|
|
3335
4190
|
const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
|
|
@@ -3342,40 +4197,40 @@ async function runThrottleSet(project, opts) {
|
|
|
3342
4197
|
if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
|
|
3343
4198
|
if (opts.period !== void 0) {
|
|
3344
4199
|
if (!["daily", "weekly", "monthly"].includes(opts.period)) {
|
|
3345
|
-
console.error(
|
|
4200
|
+
console.error(import_chalk28.default.red("--period must be daily, weekly, or monthly."));
|
|
3346
4201
|
process.exit(1);
|
|
3347
4202
|
}
|
|
3348
4203
|
throttling.quotaPeriod = opts.period;
|
|
3349
4204
|
}
|
|
3350
4205
|
if (Object.keys(throttling).length === 0) {
|
|
3351
|
-
console.error(
|
|
4206
|
+
console.error(import_chalk28.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
|
|
3352
4207
|
process.exit(1);
|
|
3353
4208
|
}
|
|
3354
4209
|
await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
|
|
3355
4210
|
}
|
|
3356
4211
|
async function runRename(project, opts) {
|
|
3357
4212
|
if (!opts.displayName) {
|
|
3358
|
-
console.error(
|
|
4213
|
+
console.error(import_chalk28.default.red("--display-name is required."));
|
|
3359
4214
|
process.exit(1);
|
|
3360
4215
|
}
|
|
3361
4216
|
await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
|
|
3362
4217
|
}
|
|
3363
4218
|
|
|
3364
4219
|
// src/commands/config-browse.ts
|
|
3365
|
-
var
|
|
4220
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
3366
4221
|
var import_ora16 = __toESM(require("ora"));
|
|
3367
4222
|
init_admin();
|
|
3368
4223
|
init_auth();
|
|
3369
4224
|
init_resolve();
|
|
3370
4225
|
|
|
3371
4226
|
// src/commands/domain.ts
|
|
3372
|
-
var
|
|
4227
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
3373
4228
|
var import_ora9 = __toESM(require("ora"));
|
|
3374
4229
|
init_admin();
|
|
3375
4230
|
init_resolve();
|
|
3376
4231
|
async function runDomainAdd(project, opts) {
|
|
3377
4232
|
if (!opts.domain) {
|
|
3378
|
-
console.error(
|
|
4233
|
+
console.error(import_chalk29.default.red("--domain is required."));
|
|
3379
4234
|
process.exit(1);
|
|
3380
4235
|
}
|
|
3381
4236
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3394,9 +4249,9 @@ async function runDomainAdd(project, opts) {
|
|
|
3394
4249
|
console.log(JSON.stringify(out));
|
|
3395
4250
|
return;
|
|
3396
4251
|
}
|
|
3397
|
-
if (out?.cnameRecord) console.log(` ${
|
|
3398
|
-
if (out?.dcv) console.log(` ${
|
|
3399
|
-
if (out?.id) console.log(
|
|
4252
|
+
if (out?.cnameRecord) console.log(` ${import_chalk29.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
|
|
4253
|
+
if (out?.dcv) console.log(` ${import_chalk29.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
|
|
4254
|
+
if (out?.id) console.log(import_chalk29.default.dim(` domain id: ${out.id}`));
|
|
3400
4255
|
} catch (err) {
|
|
3401
4256
|
spinner.fail("Domain registration failed.");
|
|
3402
4257
|
throw err;
|
|
@@ -3416,14 +4271,14 @@ async function runDomainList(project, opts) {
|
|
|
3416
4271
|
return;
|
|
3417
4272
|
}
|
|
3418
4273
|
if (!domains.length) {
|
|
3419
|
-
console.log(
|
|
4274
|
+
console.log(import_chalk29.default.yellow("No custom domains."));
|
|
3420
4275
|
return;
|
|
3421
4276
|
}
|
|
3422
|
-
for (const d of domains) console.log(` ${
|
|
4277
|
+
for (const d of domains) console.log(` ${import_chalk29.default.bold(d.hostname)} ${import_chalk29.default.dim(d.status ?? "")} ${import_chalk29.default.dim(d.id ?? "")}`);
|
|
3423
4278
|
}
|
|
3424
4279
|
async function runDomainStatus(project, opts) {
|
|
3425
4280
|
if (!opts.id) {
|
|
3426
|
-
console.error(
|
|
4281
|
+
console.error(import_chalk29.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
3427
4282
|
process.exit(1);
|
|
3428
4283
|
}
|
|
3429
4284
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3433,11 +4288,11 @@ async function runDomainStatus(project, opts) {
|
|
|
3433
4288
|
path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
|
|
3434
4289
|
summary: `Check custom-domain status`
|
|
3435
4290
|
});
|
|
3436
|
-
console.log(opts.json ? JSON.stringify(out) : ` ${
|
|
4291
|
+
console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk29.default.bold(out?.hostname ?? opts.id)}: ${import_chalk29.default.cyan(out?.status ?? "unknown")}`);
|
|
3437
4292
|
}
|
|
3438
4293
|
async function runDomainRemove(project, opts) {
|
|
3439
4294
|
if (!opts.id) {
|
|
3440
|
-
console.error(
|
|
4295
|
+
console.error(import_chalk29.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
3441
4296
|
process.exit(1);
|
|
3442
4297
|
}
|
|
3443
4298
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3475,7 +4330,7 @@ async function runDomainSetBase(project, opts) {
|
|
|
3475
4330
|
}
|
|
3476
4331
|
|
|
3477
4332
|
// src/commands/tenant.ts
|
|
3478
|
-
var
|
|
4333
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
3479
4334
|
var import_ora12 = __toESM(require("ora"));
|
|
3480
4335
|
init_admin();
|
|
3481
4336
|
init_resolve();
|
|
@@ -3484,20 +4339,20 @@ init_tenant_pick();
|
|
|
3484
4339
|
async function runTenantUse(query, opts) {
|
|
3485
4340
|
const creds = loadCredentials();
|
|
3486
4341
|
if (!creds) {
|
|
3487
|
-
console.error(
|
|
4342
|
+
console.error(import_chalk32.default.red("Not logged in. Run `apiblaze login` first."));
|
|
3488
4343
|
process.exit(1);
|
|
3489
4344
|
}
|
|
3490
4345
|
if (opts.clear) {
|
|
3491
4346
|
delete creds.activeTenant;
|
|
3492
4347
|
saveCredentials(creds);
|
|
3493
|
-
console.log(
|
|
4348
|
+
console.log(import_chalk32.default.green("Tenant scope cleared."));
|
|
3494
4349
|
return;
|
|
3495
4350
|
}
|
|
3496
4351
|
const { teamId } = await resolveTeam(opts.team);
|
|
3497
4352
|
const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
|
|
3498
4353
|
if (!slug) process.exit(1);
|
|
3499
4354
|
saveCredentials({ ...creds, activeTenant: slug });
|
|
3500
|
-
console.log(
|
|
4355
|
+
console.log(import_chalk32.default.green(`Tenant scope set to ${import_chalk32.default.bold(slug)}.`) + import_chalk32.default.dim(" (clear with `apiblaze tenant use --clear`)"));
|
|
3501
4356
|
}
|
|
3502
4357
|
async function runTenantList(opts) {
|
|
3503
4358
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -3514,22 +4369,22 @@ async function runTenantList(opts) {
|
|
|
3514
4369
|
return;
|
|
3515
4370
|
}
|
|
3516
4371
|
if (!tenants.length) {
|
|
3517
|
-
console.log(
|
|
4372
|
+
console.log(import_chalk32.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
3518
4373
|
return;
|
|
3519
4374
|
}
|
|
3520
4375
|
for (const t of tenants) {
|
|
3521
4376
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
3522
|
-
const display = typeof t === "string" ? "" :
|
|
3523
|
-
console.log(` ${
|
|
4377
|
+
const display = typeof t === "string" ? "" : import_chalk32.default.dim(` ${t.display_name ?? ""}`);
|
|
4378
|
+
console.log(` ${import_chalk32.default.bold(name)}${display}`);
|
|
3524
4379
|
}
|
|
3525
4380
|
const total = out?.total ?? tenants.length;
|
|
3526
4381
|
if (total > tenants.length) {
|
|
3527
|
-
console.log(
|
|
4382
|
+
console.log(import_chalk32.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
3528
4383
|
}
|
|
3529
4384
|
}
|
|
3530
4385
|
async function runTenantCreate(opts) {
|
|
3531
4386
|
if (!opts.name) {
|
|
3532
|
-
console.error(
|
|
4387
|
+
console.error(import_chalk32.default.red("--name (display name) is required."));
|
|
3533
4388
|
process.exit(1);
|
|
3534
4389
|
}
|
|
3535
4390
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3541,7 +4396,7 @@ async function runTenantCreate(opts) {
|
|
|
3541
4396
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
3542
4397
|
summary: `Create tenant "${opts.name}"`
|
|
3543
4398
|
});
|
|
3544
|
-
spinner.succeed(`Created tenant ${
|
|
4399
|
+
spinner.succeed(`Created tenant ${import_chalk32.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
3545
4400
|
if (opts.json) console.log(JSON.stringify(out));
|
|
3546
4401
|
} catch (err) {
|
|
3547
4402
|
spinner.fail("Tenant create failed.");
|
|
@@ -3550,7 +4405,7 @@ async function runTenantCreate(opts) {
|
|
|
3550
4405
|
}
|
|
3551
4406
|
async function runTenantAttach(project, opts) {
|
|
3552
4407
|
if (!opts.tenant) {
|
|
3553
|
-
console.error(
|
|
4408
|
+
console.error(import_chalk32.default.red("--tenant <slug> is required."));
|
|
3554
4409
|
process.exit(1);
|
|
3555
4410
|
}
|
|
3556
4411
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3578,7 +4433,7 @@ async function runTenantDelete(slug, opts) {
|
|
|
3578
4433
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
3579
4434
|
]);
|
|
3580
4435
|
if (!confirm) {
|
|
3581
|
-
console.log(
|
|
4436
|
+
console.log(import_chalk32.default.dim("Aborted."));
|
|
3582
4437
|
return;
|
|
3583
4438
|
}
|
|
3584
4439
|
}
|
|
@@ -3597,7 +4452,7 @@ async function runTenantDelete(slug, opts) {
|
|
|
3597
4452
|
}
|
|
3598
4453
|
async function runTenantCors(opts) {
|
|
3599
4454
|
if (!opts.tenant) {
|
|
3600
|
-
console.error(
|
|
4455
|
+
console.error(import_chalk32.default.red("--tenant <slug> is required."));
|
|
3601
4456
|
process.exit(1);
|
|
3602
4457
|
}
|
|
3603
4458
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3619,7 +4474,7 @@ async function runTenantCors(opts) {
|
|
|
3619
4474
|
}
|
|
3620
4475
|
|
|
3621
4476
|
// src/commands/tenant-drill.ts
|
|
3622
|
-
var
|
|
4477
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
3623
4478
|
var import_ora13 = __toESM(require("ora"));
|
|
3624
4479
|
var import_crypto = require("crypto");
|
|
3625
4480
|
init_admin();
|
|
@@ -3650,15 +4505,15 @@ async function validScopedTenant(teamId, query) {
|
|
|
3650
4505
|
const next = { ...creds };
|
|
3651
4506
|
delete next.activeTenant;
|
|
3652
4507
|
saveCredentials2(next);
|
|
3653
|
-
console.log(
|
|
4508
|
+
console.log(import_chalk33.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
3654
4509
|
return void 0;
|
|
3655
4510
|
}
|
|
3656
4511
|
async function tenantHome(teamId, tenant2) {
|
|
3657
4512
|
const { default: inquirer3 } = await import("inquirer");
|
|
3658
4513
|
const base2 = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
3659
|
-
console.log(
|
|
4514
|
+
console.log(import_chalk33.default.bold(`
|
|
3660
4515
|
Tenant ${tenant2}`));
|
|
3661
|
-
console.log(
|
|
4516
|
+
console.log(import_chalk33.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
3662
4517
|
for (; ; ) {
|
|
3663
4518
|
const spinner = (0, import_ora13.default)("Reading tenant state...").start();
|
|
3664
4519
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
@@ -3674,18 +4529,18 @@ Tenant ${tenant2}`));
|
|
|
3674
4529
|
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
3675
4530
|
const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
|
|
3676
4531
|
const nLogin = nClients + nIssuers + nOpaque;
|
|
3677
|
-
const onOff = (b) => b ?
|
|
4532
|
+
const onOff = (b) => b ? import_chalk33.default.green("on") : import_chalk33.default.dim("off");
|
|
3678
4533
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3679
4534
|
type: "list",
|
|
3680
4535
|
name: "pick",
|
|
3681
4536
|
message: `Tenant ${tenant2}:`,
|
|
3682
4537
|
pageSize: 12,
|
|
3683
4538
|
choices: [
|
|
3684
|
-
{ name: `Login methods (${nLogin}) ${
|
|
3685
|
-
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${
|
|
3686
|
-
{ name: `Portal admins (${nEmails}) ${
|
|
3687
|
-
new inquirer3.Separator(
|
|
3688
|
-
{ name: `CORS: ${cors?.cors ?
|
|
4539
|
+
{ name: `Login methods (${nLogin}) ${import_chalk33.default.dim("how your consumers sign in")}`, value: "login" },
|
|
4540
|
+
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk33.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
|
|
4541
|
+
{ name: `Portal admins (${nEmails}) ${import_chalk33.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
|
|
4542
|
+
new inquirer3.Separator(import_chalk33.default.dim(" Settings")),
|
|
4543
|
+
{ name: `CORS: ${cors?.cors ? import_chalk33.default.cyan(JSON.stringify(cors.cors)) : import_chalk33.default.dim("(unset)")} ${import_chalk33.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
|
|
3689
4544
|
{ name: "\u2190 Back", value: "back" }
|
|
3690
4545
|
]
|
|
3691
4546
|
}]);
|
|
@@ -3698,7 +4553,7 @@ Tenant ${tenant2}`));
|
|
|
3698
4553
|
case "iam": {
|
|
3699
4554
|
const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
|
|
3700
4555
|
await admin({ method: "PATCH", path: `${base2}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
3701
|
-
console.log(
|
|
4556
|
+
console.log(import_chalk33.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
3702
4557
|
break;
|
|
3703
4558
|
}
|
|
3704
4559
|
case "cors": {
|
|
@@ -3711,11 +4566,11 @@ Tenant ${tenant2}`));
|
|
|
3711
4566
|
if (v === "") break;
|
|
3712
4567
|
const parsed = v === "null" ? null : safeJson(v);
|
|
3713
4568
|
if (parsed === void 0) {
|
|
3714
|
-
console.log(
|
|
4569
|
+
console.log(import_chalk33.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
3715
4570
|
break;
|
|
3716
4571
|
}
|
|
3717
4572
|
await admin({ method: "PUT", path: `${base2}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
3718
|
-
console.log(
|
|
4573
|
+
console.log(import_chalk33.default.green(" CORS updated."));
|
|
3719
4574
|
break;
|
|
3720
4575
|
}
|
|
3721
4576
|
case "emails":
|
|
@@ -3739,15 +4594,15 @@ async function loginMethodsMenu(teamId, tenant2, base2) {
|
|
|
3739
4594
|
const choices = [];
|
|
3740
4595
|
for (const c of appClients) {
|
|
3741
4596
|
const nP = (c.providers ?? []).length || c.providers_count || 0;
|
|
3742
|
-
choices.push({ name: `${
|
|
4597
|
+
choices.push({ name: `${import_chalk33.default.cyan("APIblaze-hosted")} ${import_chalk33.default.bold(c.name ?? c.clientId)} ${import_chalk33.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk33.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
|
|
3743
4598
|
}
|
|
3744
4599
|
for (const i of issuers) {
|
|
3745
|
-
choices.push({ name: `${
|
|
4600
|
+
choices.push({ name: `${import_chalk33.default.cyan("Your own \u2014 JWT ")} ${import_chalk33.default.bold(i.iss)} ${import_chalk33.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
|
|
3746
4601
|
}
|
|
3747
4602
|
if (opaque?.endpoint) {
|
|
3748
|
-
choices.push({ name: `${
|
|
4603
|
+
choices.push({ name: `${import_chalk33.default.cyan("Your own \u2014 opaque")} ${import_chalk33.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
|
|
3749
4604
|
}
|
|
3750
|
-
if (!choices.length) console.log(
|
|
4605
|
+
if (!choices.length) console.log(import_chalk33.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
|
|
3751
4606
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3752
4607
|
type: "list",
|
|
3753
4608
|
name: "pick",
|
|
@@ -3787,9 +4642,9 @@ async function addLoginMethod(teamId, tenant2, base2) {
|
|
|
3787
4642
|
message: "How do people log in?",
|
|
3788
4643
|
pageSize: 8,
|
|
3789
4644
|
choices: [
|
|
3790
|
-
{ name: `APIblaze hosted login page ${
|
|
3791
|
-
{ name: `Your own hosted login \u2014 JWT ${
|
|
3792
|
-
{ name: `Your own login \u2014 opaque token ${
|
|
4645
|
+
{ name: `APIblaze hosted login page ${import_chalk33.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
|
|
4646
|
+
{ name: `Your own hosted login \u2014 JWT ${import_chalk33.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
|
|
4647
|
+
{ name: `Your own login \u2014 opaque token ${import_chalk33.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
|
|
3793
4648
|
{ name: "\u2190 Back", value: null }
|
|
3794
4649
|
]
|
|
3795
4650
|
}]);
|
|
@@ -3813,8 +4668,8 @@ function safeJson(s) {
|
|
|
3813
4668
|
async function emailsMenu(base2, emails) {
|
|
3814
4669
|
const { default: inquirer3 } = await import("inquirer");
|
|
3815
4670
|
console.log();
|
|
3816
|
-
if (!emails.length) console.log(
|
|
3817
|
-
for (const e of emails) console.log(` ${
|
|
4671
|
+
if (!emails.length) console.log(import_chalk33.default.dim(" No consumer-admin emails."));
|
|
4672
|
+
for (const e of emails) console.log(` ${import_chalk33.default.bold(e.email ?? e)} ${import_chalk33.default.dim(e.status ?? "")}`);
|
|
3818
4673
|
const { act } = await inquirer3.prompt([{
|
|
3819
4674
|
type: "list",
|
|
3820
4675
|
name: "act",
|
|
@@ -3829,7 +4684,7 @@ async function emailsMenu(base2, emails) {
|
|
|
3829
4684
|
if (act === "add") {
|
|
3830
4685
|
const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
3831
4686
|
await admin({ method: "POST", path: `${base2}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
3832
|
-
console.log(
|
|
4687
|
+
console.log(import_chalk33.default.green(` ${email} added.`));
|
|
3833
4688
|
} else {
|
|
3834
4689
|
const { e } = await inquirer3.prompt([{
|
|
3835
4690
|
type: "list",
|
|
@@ -3839,7 +4694,7 @@ async function emailsMenu(base2, emails) {
|
|
|
3839
4694
|
}]);
|
|
3840
4695
|
if (!e) return;
|
|
3841
4696
|
await admin({ method: "DELETE", path: `${base2}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3842
|
-
console.log(
|
|
4697
|
+
console.log(import_chalk33.default.green(` ${e} removed.`));
|
|
3843
4698
|
}
|
|
3844
4699
|
}
|
|
3845
4700
|
async function addIssuer(base2) {
|
|
@@ -3860,19 +4715,19 @@ async function addIssuer(base2) {
|
|
|
3860
4715
|
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3861
4716
|
summary: `Add external issuer ${a.iss.trim()}`
|
|
3862
4717
|
});
|
|
3863
|
-
console.log(
|
|
4718
|
+
console.log(import_chalk33.default.green(" JWT login method saved."));
|
|
3864
4719
|
}
|
|
3865
4720
|
async function issuerHome(base2, issuer) {
|
|
3866
4721
|
const { default: inquirer3 } = await import("inquirer");
|
|
3867
4722
|
console.log(`
|
|
3868
|
-
${
|
|
4723
|
+
${import_chalk33.default.bold(issuer.iss)} ${import_chalk33.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
|
|
3869
4724
|
const { act } = await inquirer3.prompt([{
|
|
3870
4725
|
type: "list",
|
|
3871
4726
|
name: "act",
|
|
3872
4727
|
message: "This JWT login method:",
|
|
3873
4728
|
choices: [
|
|
3874
4729
|
{ name: "Replace it (re-enter details)", value: "edit" },
|
|
3875
|
-
{ name:
|
|
4730
|
+
{ name: import_chalk33.default.red("Delete it"), value: "rm" },
|
|
3876
4731
|
{ name: "\u2190 Back", value: "back" }
|
|
3877
4732
|
]
|
|
3878
4733
|
}]);
|
|
@@ -3888,7 +4743,7 @@ async function issuerHome(base2, issuer) {
|
|
|
3888
4743
|
path: `${base2}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
|
|
3889
4744
|
summary: `Delete issuer ${issuer.iss}`
|
|
3890
4745
|
});
|
|
3891
|
-
console.log(
|
|
4746
|
+
console.log(import_chalk33.default.green(" Deleted."));
|
|
3892
4747
|
}
|
|
3893
4748
|
async function setOpaque(base2, cur) {
|
|
3894
4749
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3897,19 +4752,19 @@ async function setOpaque(base2, cur) {
|
|
|
3897
4752
|
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3898
4753
|
]);
|
|
3899
4754
|
await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3900
|
-
console.log(
|
|
4755
|
+
console.log(import_chalk33.default.green(" Opaque login method set."));
|
|
3901
4756
|
}
|
|
3902
4757
|
async function opaqueHome(base2, cur) {
|
|
3903
4758
|
const { default: inquirer3 } = await import("inquirer");
|
|
3904
4759
|
console.log(`
|
|
3905
|
-
${
|
|
4760
|
+
${import_chalk33.default.bold(cur.endpoint)} ${import_chalk33.default.dim(cur.method ?? "GET")}`);
|
|
3906
4761
|
const { act } = await inquirer3.prompt([{
|
|
3907
4762
|
type: "list",
|
|
3908
4763
|
name: "act",
|
|
3909
4764
|
message: "This opaque login method:",
|
|
3910
4765
|
choices: [
|
|
3911
4766
|
{ name: "Replace it", value: "edit" },
|
|
3912
|
-
{ name:
|
|
4767
|
+
{ name: import_chalk33.default.red("Delete it"), value: "rm" },
|
|
3913
4768
|
{ name: "\u2190 Back", value: "back" }
|
|
3914
4769
|
]
|
|
3915
4770
|
}]);
|
|
@@ -3919,7 +4774,7 @@ async function opaqueHome(base2, cur) {
|
|
|
3919
4774
|
return;
|
|
3920
4775
|
}
|
|
3921
4776
|
await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3922
|
-
console.log(
|
|
4777
|
+
console.log(import_chalk33.default.green(" Deleted."));
|
|
3923
4778
|
}
|
|
3924
4779
|
async function clientHome(base2, summary) {
|
|
3925
4780
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3938,13 +4793,13 @@ async function clientHome(base2, summary) {
|
|
|
3938
4793
|
message: `${c.name ?? id}:`,
|
|
3939
4794
|
pageSize: 12,
|
|
3940
4795
|
choices: [
|
|
3941
|
-
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${
|
|
3942
|
-
{ name: `Callback URLs: ${cb.length ?
|
|
3943
|
-
{ name: `Scopes: ${scopes.length ?
|
|
4796
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk33.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
4797
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk33.default.cyan(cb.join(", ")) : import_chalk33.default.dim("(none)")}`, value: "callbacks" },
|
|
4798
|
+
{ name: `Scopes: ${scopes.length ? import_chalk33.default.cyan(scopes.join(" ")) : import_chalk33.default.dim("(defaults)")}`, value: "scopes" },
|
|
3944
4799
|
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3945
4800
|
{ name: "Reveal client secret", value: "secret" },
|
|
3946
4801
|
{ name: "Rotate client secret", value: "rotate" },
|
|
3947
|
-
{ name:
|
|
4802
|
+
{ name: import_chalk33.default.red("Delete this app client"), value: "delete" },
|
|
3948
4803
|
{ name: "\u2190 Back", value: "back" }
|
|
3949
4804
|
]
|
|
3950
4805
|
}]);
|
|
@@ -3957,13 +4812,13 @@ async function clientHome(base2, summary) {
|
|
|
3957
4812
|
case "callbacks": {
|
|
3958
4813
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3959
4814
|
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3960
|
-
console.log(
|
|
4815
|
+
console.log(import_chalk33.default.green(" Callbacks updated."));
|
|
3961
4816
|
break;
|
|
3962
4817
|
}
|
|
3963
4818
|
case "scopes": {
|
|
3964
4819
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3965
4820
|
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3966
|
-
console.log(
|
|
4821
|
+
console.log(import_chalk33.default.green(" Scopes updated."));
|
|
3967
4822
|
break;
|
|
3968
4823
|
}
|
|
3969
4824
|
case "expiries": {
|
|
@@ -3978,14 +4833,14 @@ async function clientHome(base2, summary) {
|
|
|
3978
4833
|
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3979
4834
|
summary: "Update token expiries"
|
|
3980
4835
|
});
|
|
3981
|
-
console.log(
|
|
4836
|
+
console.log(import_chalk33.default.green(" Expiries updated."));
|
|
3982
4837
|
break;
|
|
3983
4838
|
}
|
|
3984
4839
|
case "secret": {
|
|
3985
4840
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3986
4841
|
if (!sure) break;
|
|
3987
4842
|
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3988
|
-
console.log(` ${
|
|
4843
|
+
console.log(` ${import_chalk33.default.bold("client_secret")}: ${import_chalk33.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3989
4844
|
break;
|
|
3990
4845
|
}
|
|
3991
4846
|
case "rotate": {
|
|
@@ -3993,14 +4848,14 @@ async function clientHome(base2, summary) {
|
|
|
3993
4848
|
if (!sure) break;
|
|
3994
4849
|
const fresh = randomSecret();
|
|
3995
4850
|
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3996
|
-
console.log(` New ${
|
|
4851
|
+
console.log(` New ${import_chalk33.default.bold("client_secret")}: ${import_chalk33.default.green(fresh)} ${import_chalk33.default.dim("(store it now)")}`);
|
|
3997
4852
|
break;
|
|
3998
4853
|
}
|
|
3999
4854
|
case "delete": {
|
|
4000
4855
|
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 }]);
|
|
4001
4856
|
if (!sure) break;
|
|
4002
4857
|
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
4003
|
-
console.log(
|
|
4858
|
+
console.log(import_chalk33.default.green(" App client deleted."));
|
|
4004
4859
|
return;
|
|
4005
4860
|
}
|
|
4006
4861
|
}
|
|
@@ -4027,9 +4882,9 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
4027
4882
|
const providers = Array.isArray(raw) ? raw : [];
|
|
4028
4883
|
console.log();
|
|
4029
4884
|
for (const p of providers) {
|
|
4030
|
-
console.log(` ${
|
|
4885
|
+
console.log(` ${import_chalk33.default.bold(p.type)} ${import_chalk33.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
|
|
4031
4886
|
}
|
|
4032
|
-
if (!providers.length) console.log(
|
|
4887
|
+
if (!providers.length) console.log(import_chalk33.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
4033
4888
|
const { act } = await inquirer3.prompt([{
|
|
4034
4889
|
type: "list",
|
|
4035
4890
|
name: "act",
|
|
@@ -4087,23 +4942,23 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
4087
4942
|
body.targetServerToken = routing;
|
|
4088
4943
|
}
|
|
4089
4944
|
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
4090
|
-
console.log(
|
|
4945
|
+
console.log(import_chalk33.default.green(` ${type} provider added.`));
|
|
4091
4946
|
} else {
|
|
4092
4947
|
const { p } = await inquirer3.prompt([{
|
|
4093
4948
|
type: "list",
|
|
4094
4949
|
name: "p",
|
|
4095
4950
|
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
4096
|
-
choices: [...providers.map((x) => ({ name: `${x.type} ${
|
|
4951
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk33.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
4097
4952
|
}]);
|
|
4098
4953
|
if (!p) continue;
|
|
4099
4954
|
if (act === "rm") {
|
|
4100
4955
|
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 }]);
|
|
4101
4956
|
if (!sure) continue;
|
|
4102
4957
|
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
4103
|
-
console.log(
|
|
4958
|
+
console.log(import_chalk33.default.green(` ${p.type} removed.`));
|
|
4104
4959
|
} else {
|
|
4105
4960
|
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
4106
|
-
console.log(` ${
|
|
4961
|
+
console.log(` ${import_chalk33.default.bold("client_secret")}: ${import_chalk33.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
4107
4962
|
}
|
|
4108
4963
|
}
|
|
4109
4964
|
}
|
|
@@ -4111,7 +4966,7 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
4111
4966
|
|
|
4112
4967
|
// src/commands/spec.ts
|
|
4113
4968
|
var fs7 = __toESM(require("fs"));
|
|
4114
|
-
var
|
|
4969
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
4115
4970
|
var import_ora14 = __toESM(require("ora"));
|
|
4116
4971
|
init_admin();
|
|
4117
4972
|
init_api();
|
|
@@ -4128,14 +4983,14 @@ async function runSpecGet(project, opts) {
|
|
|
4128
4983
|
}
|
|
4129
4984
|
async function runSpecSet(project, opts) {
|
|
4130
4985
|
if (!opts.file) {
|
|
4131
|
-
console.error(
|
|
4986
|
+
console.error(import_chalk34.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
4132
4987
|
process.exit(1);
|
|
4133
4988
|
}
|
|
4134
4989
|
let specContent;
|
|
4135
4990
|
try {
|
|
4136
4991
|
specContent = fs7.readFileSync(opts.file, "utf-8");
|
|
4137
4992
|
} catch {
|
|
4138
|
-
console.error(
|
|
4993
|
+
console.error(import_chalk34.default.red(`Cannot read file: ${opts.file}`));
|
|
4139
4994
|
process.exit(1);
|
|
4140
4995
|
}
|
|
4141
4996
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -4164,23 +5019,23 @@ async function runSpecSet(project, opts) {
|
|
|
4164
5019
|
function printSpecChangeReport(report, projectName) {
|
|
4165
5020
|
const r = report.routes;
|
|
4166
5021
|
if (r) {
|
|
4167
|
-
for (const a of r.added) console.log(
|
|
4168
|
-
for (const d of r.removed) console.log(
|
|
4169
|
-
for (const rn of r.renamed ?? []) console.log(
|
|
4170
|
-
for (const m of r.modified ?? []) console.log(
|
|
5022
|
+
for (const a of r.added) console.log(import_chalk34.default.green(` + ${a}`));
|
|
5023
|
+
for (const d of r.removed) console.log(import_chalk34.default.red(` \u2212 ${d}`));
|
|
5024
|
+
for (const rn of r.renamed ?? []) console.log(import_chalk34.default.yellow(` ~ ${rn.from} \u2192 ${rn.to}`));
|
|
5025
|
+
for (const m of r.modified ?? []) console.log(import_chalk34.default.dim(` \xB1 ${m} (params/schema changed)`));
|
|
4171
5026
|
}
|
|
4172
|
-
if (report.applied?.mcp_regenerated) console.log(
|
|
4173
|
-
if (report.applied?.candidates_cleared) console.log(
|
|
5027
|
+
if (report.applied?.mcp_regenerated) console.log(import_chalk34.default.dim(" MCP catalogue regenerated."));
|
|
5028
|
+
if (report.applied?.candidates_cleared) console.log(import_chalk34.default.dim(` ${report.applied.candidates_cleared} route candidate(s) now documented \u2014 cleared.`));
|
|
4174
5029
|
for (const item of report.attention ?? []) {
|
|
4175
|
-
console.log(
|
|
5030
|
+
console.log(import_chalk34.default.yellow(` \u26A0 ${item.detail}`));
|
|
4176
5031
|
if (item.suggestion?.carry_from && item.suggestion.carry_to) {
|
|
4177
|
-
console.log(
|
|
5032
|
+
console.log(import_chalk34.default.dim(` Fix: apiblaze spec carry ${projectName} --method ${item.suggestion.method} --from "${item.suggestion.carry_from}" --to "${item.suggestion.carry_to}"`));
|
|
4178
5033
|
}
|
|
4179
5034
|
}
|
|
4180
5035
|
}
|
|
4181
5036
|
async function runSpecCarry(project, opts) {
|
|
4182
5037
|
if (!opts.method || !opts.from || !opts.to) {
|
|
4183
|
-
console.error(
|
|
5038
|
+
console.error(import_chalk34.default.red("--method, --from and --to are required."));
|
|
4184
5039
|
process.exit(1);
|
|
4185
5040
|
}
|
|
4186
5041
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -4202,7 +5057,7 @@ async function runSpecCarry(project, opts) {
|
|
|
4202
5057
|
}
|
|
4203
5058
|
async function runSpecDeleteRule(project, opts) {
|
|
4204
5059
|
if (!opts.method || !opts.route) {
|
|
4205
|
-
console.error(
|
|
5060
|
+
console.error(import_chalk34.default.red("--method and --route are required."));
|
|
4206
5061
|
process.exit(1);
|
|
4207
5062
|
}
|
|
4208
5063
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -4222,14 +5077,14 @@ async function runSpecDeleteRule(project, opts) {
|
|
|
4222
5077
|
}
|
|
4223
5078
|
|
|
4224
5079
|
// src/commands/agent.ts
|
|
4225
|
-
var
|
|
5080
|
+
var import_chalk36 = __toESM(require("chalk"));
|
|
4226
5081
|
var import_ora15 = __toESM(require("ora"));
|
|
4227
5082
|
init_auth();
|
|
4228
5083
|
init_admin();
|
|
4229
5084
|
init_resolve();
|
|
4230
5085
|
|
|
4231
5086
|
// src/lib/tools.ts
|
|
4232
|
-
var
|
|
5087
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
4233
5088
|
init_admin();
|
|
4234
5089
|
init_api();
|
|
4235
5090
|
init_resolve();
|
|
@@ -4249,15 +5104,15 @@ var TOOLS = [
|
|
|
4249
5104
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
4250
5105
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
4251
5106
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
4252
|
-
const lines = [` ${
|
|
4253
|
-
if (res.devPortal) lines.push(` ${
|
|
5107
|
+
const lines = [` ${import_chalk35.default.dim("Proxy URL:")} ${import_chalk35.default.bold(url)}`];
|
|
5108
|
+
if (res.devPortal) lines.push(` ${import_chalk35.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
4254
5109
|
const envs = Object.keys(keys);
|
|
4255
5110
|
if (envs.length) {
|
|
4256
|
-
lines.push("", ` ${
|
|
5111
|
+
lines.push("", ` ${import_chalk35.default.bold("API keys")} ${import_chalk35.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
4257
5112
|
const w = Math.max(...envs.map((e) => e.length));
|
|
4258
|
-
for (const env of envs) lines.push(` ${
|
|
5113
|
+
for (const env of envs) lines.push(` ${import_chalk35.default.cyan(env.padEnd(w))} ${import_chalk35.default.green(keys[env])}`);
|
|
4259
5114
|
}
|
|
4260
|
-
if (tryIt) lines.push("", ` ${
|
|
5115
|
+
if (tryIt) lines.push("", ` ${import_chalk35.default.dim("Try it:")}`, ` ${import_chalk35.default.cyan(tryIt)}`);
|
|
4261
5116
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
4262
5117
|
}
|
|
4263
5118
|
},
|
|
@@ -4413,17 +5268,17 @@ function truncate(value, max = 1500) {
|
|
|
4413
5268
|
}
|
|
4414
5269
|
function printCost(resp) {
|
|
4415
5270
|
const line = formatBilling(resp.billing);
|
|
4416
|
-
if (line) console.log(
|
|
5271
|
+
if (line) console.log(import_chalk36.default.magenta(` ${line}`) + import_chalk36.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
|
|
4417
5272
|
}
|
|
4418
5273
|
async function runAgent(opts) {
|
|
4419
5274
|
requireAuth();
|
|
4420
5275
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
4421
5276
|
const { default: inquirer3 } = await import("inquirer");
|
|
4422
|
-
console.log(
|
|
4423
|
-
console.log(
|
|
5277
|
+
console.log(import_chalk36.default.bold("APIblaze agent") + import_chalk36.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
5278
|
+
console.log(import_chalk36.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
4424
5279
|
const history = [];
|
|
4425
5280
|
while (true) {
|
|
4426
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
5281
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk36.default.cyan("you") + " \u203A" }]);
|
|
4427
5282
|
const text = (input ?? "").trim();
|
|
4428
5283
|
if (!text) continue;
|
|
4429
5284
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
@@ -4437,14 +5292,14 @@ async function runAgent(opts) {
|
|
|
4437
5292
|
} catch (err) {
|
|
4438
5293
|
spinner.stop();
|
|
4439
5294
|
if (err instanceof ApiError && err.status === 402) {
|
|
4440
|
-
console.log(
|
|
5295
|
+
console.log(import_chalk36.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
4441
5296
|
break;
|
|
4442
5297
|
}
|
|
4443
5298
|
throw err;
|
|
4444
5299
|
}
|
|
4445
5300
|
history.push({ role: "assistant", content: resp.raw });
|
|
4446
5301
|
printCost(resp);
|
|
4447
|
-
if (resp.reply) console.log(
|
|
5302
|
+
if (resp.reply) console.log(import_chalk36.default.green("agent") + " \u203A " + resp.reply);
|
|
4448
5303
|
if (!resp.action) break;
|
|
4449
5304
|
const tool = findTool(resp.action.tool);
|
|
4450
5305
|
if (!tool) {
|
|
@@ -4469,11 +5324,11 @@ async function runAgent(opts) {
|
|
|
4469
5324
|
}
|
|
4470
5325
|
renderTrace();
|
|
4471
5326
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
4472
|
-
console.log(
|
|
5327
|
+
console.log(import_chalk36.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
4473
5328
|
}
|
|
4474
5329
|
}
|
|
4475
5330
|
}
|
|
4476
|
-
console.log(
|
|
5331
|
+
console.log(import_chalk36.default.dim("\nBye."));
|
|
4477
5332
|
}
|
|
4478
5333
|
|
|
4479
5334
|
// src/commands/config-browse.ts
|
|
@@ -4615,6 +5470,53 @@ var SETTINGS = [
|
|
|
4615
5470
|
desc: "Team-scoped tag grouping projects in the consumer portal",
|
|
4616
5471
|
toPatch: (v) => ({ product_slug: v })
|
|
4617
5472
|
},
|
|
5473
|
+
// Who funds AI chat here (specs/chat_funding.MD). A partial PATCH merges, so
|
|
5474
|
+
// setting one key never resurrects a switch the producer turned off.
|
|
5475
|
+
{
|
|
5476
|
+
key: "chat_sponsorship.enabled",
|
|
5477
|
+
label: "Pay for your users' AI chats",
|
|
5478
|
+
group: "Portal & MCP",
|
|
5479
|
+
type: "boolean",
|
|
5480
|
+
desc: "Every chat message costs money and your users can't pay for it here yet, so you cover it \u2014 for people who sign in, and for API keys you gave the use-premium permission. Off = everyone brings their own AI key, your own tools included",
|
|
5481
|
+
read: (cfg) => cfg.chat_sponsorship?.enabled !== false,
|
|
5482
|
+
toPatch: (v) => ({ chat_sponsorship: { enabled: v } })
|
|
5483
|
+
},
|
|
5484
|
+
{
|
|
5485
|
+
key: "chat_sponsorship.quota_enabled",
|
|
5486
|
+
label: "Limit AI chat per person per day",
|
|
5487
|
+
group: "Portal & MCP",
|
|
5488
|
+
type: "boolean",
|
|
5489
|
+
desc: "Give each person their own daily allowance, reset at midnight UTC. Off = your wallet running out is the only thing that stops it",
|
|
5490
|
+
read: (cfg) => cfg.chat_sponsorship?.quota_enabled !== false,
|
|
5491
|
+
toPatch: (v) => ({ chat_sponsorship: { quota_enabled: v } })
|
|
5492
|
+
},
|
|
5493
|
+
{
|
|
5494
|
+
key: "chat_sponsorship.cap_cents_per_day",
|
|
5495
|
+
label: "AI chat: cents per person per day",
|
|
5496
|
+
group: "Portal & MCP",
|
|
5497
|
+
type: "number",
|
|
5498
|
+
desc: "Most you'll spend on one person per day, in cents you're actually charged (not raw model price). 25 is roughly 25 short chats. Max 5000; 0 blocks everyone",
|
|
5499
|
+
read: (cfg) => cfg.chat_sponsorship?.cap_cents_per_day ?? 25,
|
|
5500
|
+
toPatch: (v) => ({ chat_sponsorship: { cap_cents_per_day: v } })
|
|
5501
|
+
},
|
|
5502
|
+
{
|
|
5503
|
+
key: "chat_sponsorship.max_chats_per_day",
|
|
5504
|
+
label: "AI chat: chats per person per day",
|
|
5505
|
+
group: "Portal & MCP",
|
|
5506
|
+
type: "number",
|
|
5507
|
+
desc: "Chats one person can have per day \u2014 counted even when a chat is cheap, so nobody runs thousands of tiny ones. Max 1000; 0 blocks everyone",
|
|
5508
|
+
read: (cfg) => cfg.chat_sponsorship?.max_chats_per_day ?? 25,
|
|
5509
|
+
toPatch: (v) => ({ chat_sponsorship: { max_chats_per_day: v } })
|
|
5510
|
+
},
|
|
5511
|
+
{
|
|
5512
|
+
key: "chat_sponsorship.max_chars_per_message",
|
|
5513
|
+
label: "AI chat: longest single message",
|
|
5514
|
+
group: "Portal & MCP",
|
|
5515
|
+
type: "number",
|
|
5516
|
+
desc: "2000 characters is about 300 words. Has its own limit because one very long message can cost more than a whole day of short ones. Max 64000",
|
|
5517
|
+
read: (cfg) => cfg.chat_sponsorship?.max_chars_per_message ?? 2e3,
|
|
5518
|
+
toPatch: (v) => ({ chat_sponsorship: { max_chars_per_message: v } })
|
|
5519
|
+
},
|
|
4618
5520
|
// ── Advanced (specs/missing.md): all optional, default-off; null clears ────
|
|
4619
5521
|
{
|
|
4620
5522
|
key: "ip_access.mode",
|
|
@@ -4712,11 +5614,11 @@ function dig(blob, dotted) {
|
|
|
4712
5614
|
}
|
|
4713
5615
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
4714
5616
|
function show(v) {
|
|
4715
|
-
if (v === void 0) return
|
|
4716
|
-
if (v === null) return
|
|
4717
|
-
if (typeof v === "object") return
|
|
4718
|
-
if (typeof v === "boolean") return v ?
|
|
4719
|
-
return
|
|
5617
|
+
if (v === void 0) return import_chalk37.default.dim("(unset)");
|
|
5618
|
+
if (v === null) return import_chalk37.default.dim("null");
|
|
5619
|
+
if (typeof v === "object") return import_chalk37.default.cyan(JSON.stringify(v));
|
|
5620
|
+
if (typeof v === "boolean") return v ? import_chalk37.default.green("on") : import_chalk37.default.red("off");
|
|
5621
|
+
return import_chalk37.default.cyan(String(v));
|
|
4720
5622
|
}
|
|
4721
5623
|
function parseValue(raw) {
|
|
4722
5624
|
if (raw === "true") return true;
|
|
@@ -4758,10 +5660,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
4758
5660
|
}
|
|
4759
5661
|
}
|
|
4760
5662
|
var loginFirst = (what) => {
|
|
4761
|
-
console.log(
|
|
5663
|
+
console.log(import_chalk37.default.yellow(`
|
|
4762
5664
|
Log in first to ${what}.`));
|
|
4763
|
-
console.log(
|
|
4764
|
-
console.log(
|
|
5665
|
+
console.log(import_chalk37.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
5666
|
+
console.log(import_chalk37.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
4765
5667
|
};
|
|
4766
5668
|
async function runConfig(project, key, value, opts) {
|
|
4767
5669
|
const creds = loadCredentials();
|
|
@@ -4778,9 +5680,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
4778
5680
|
}
|
|
4779
5681
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
4780
5682
|
if (!setting) {
|
|
4781
|
-
console.error(
|
|
4782
|
-
console.error(
|
|
4783
|
-
console.error(
|
|
5683
|
+
console.error(import_chalk37.default.red(`Unknown setting "${key}".`));
|
|
5684
|
+
console.error(import_chalk37.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
5685
|
+
console.error(import_chalk37.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
4784
5686
|
process.exit(1);
|
|
4785
5687
|
}
|
|
4786
5688
|
if (value === void 0) {
|
|
@@ -4795,7 +5697,7 @@ async function pickProject(teamId) {
|
|
|
4795
5697
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4796
5698
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
4797
5699
|
if (!projects.length) {
|
|
4798
|
-
console.error(
|
|
5700
|
+
console.error(import_chalk37.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
4799
5701
|
process.exit(1);
|
|
4800
5702
|
}
|
|
4801
5703
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -4803,7 +5705,7 @@ async function pickProject(teamId) {
|
|
|
4803
5705
|
type: "list",
|
|
4804
5706
|
name: "picked",
|
|
4805
5707
|
message: "Which project?",
|
|
4806
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
5708
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk37.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
4807
5709
|
}]);
|
|
4808
5710
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
4809
5711
|
}
|
|
@@ -4814,25 +5716,25 @@ function printAll(proj2, cfg, json) {
|
|
|
4814
5716
|
console.log(JSON.stringify(out, null, 2));
|
|
4815
5717
|
return;
|
|
4816
5718
|
}
|
|
4817
|
-
console.log(
|
|
5719
|
+
console.log(import_chalk37.default.bold(`
|
|
4818
5720
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
4819
5721
|
`));
|
|
4820
5722
|
for (const group2 of SETTING_GROUPS) {
|
|
4821
|
-
console.log(
|
|
5723
|
+
console.log(import_chalk37.default.bold(group2));
|
|
4822
5724
|
for (const s of SETTINGS.filter((x) => x.group === group2)) {
|
|
4823
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
5725
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk37.default.dim(s.desc)}`);
|
|
4824
5726
|
}
|
|
4825
5727
|
console.log();
|
|
4826
5728
|
}
|
|
4827
|
-
console.log(
|
|
5729
|
+
console.log(import_chalk37.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
4828
5730
|
}
|
|
4829
5731
|
async function discoveryMenu(project) {
|
|
4830
5732
|
const { default: inquirer3 } = await import("inquirer");
|
|
4831
|
-
console.log(
|
|
5733
|
+
console.log(import_chalk37.default.bold(`
|
|
4832
5734
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
4833
5735
|
`));
|
|
4834
|
-
console.log(
|
|
4835
|
-
console.log(
|
|
5736
|
+
console.log(import_chalk37.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
5737
|
+
console.log(import_chalk37.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
4836
5738
|
for (; ; ) {
|
|
4837
5739
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
4838
5740
|
type: "list",
|
|
@@ -4840,13 +5742,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4840
5742
|
message: "Explore:",
|
|
4841
5743
|
pageSize: 20,
|
|
4842
5744
|
choices: [
|
|
4843
|
-
new inquirer3.Separator(
|
|
5745
|
+
new inquirer3.Separator(import_chalk37.default.bold("\u2014 Settings \u2014")),
|
|
4844
5746
|
...SETTING_GROUPS.map((g) => ({
|
|
4845
|
-
name: `${g} ${
|
|
5747
|
+
name: `${g} ${import_chalk37.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
4846
5748
|
value: { kind: "settings", g }
|
|
4847
5749
|
})),
|
|
4848
|
-
new inquirer3.Separator(
|
|
4849
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
5750
|
+
new inquirer3.Separator(import_chalk37.default.bold("\u2014 Features \u2014")),
|
|
5751
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk37.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
4850
5752
|
new inquirer3.Separator(),
|
|
4851
5753
|
{ name: "Exit", value: { kind: "exit" } }
|
|
4852
5754
|
]
|
|
@@ -4855,24 +5757,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4855
5757
|
if (pick2.kind === "settings") {
|
|
4856
5758
|
console.log();
|
|
4857
5759
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
4858
|
-
console.log(` ${
|
|
4859
|
-
console.log(` ${
|
|
5760
|
+
console.log(` ${import_chalk37.default.bold(s.label.padEnd(28))} ${import_chalk37.default.dim(s.desc)}`);
|
|
5761
|
+
console.log(` ${import_chalk37.default.dim(" key: " + s.key)}`);
|
|
4860
5762
|
}
|
|
4861
5763
|
loginFirst("view or change these settings");
|
|
4862
5764
|
} else {
|
|
4863
5765
|
const f = pick2.f;
|
|
4864
5766
|
console.log(`
|
|
4865
|
-
${
|
|
5767
|
+
${import_chalk37.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
4866
5768
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
4867
5769
|
}
|
|
4868
5770
|
}
|
|
4869
5771
|
}
|
|
4870
5772
|
async function navigator(proj2, cfg, opts) {
|
|
4871
5773
|
const { default: inquirer3 } = await import("inquirer");
|
|
4872
|
-
console.log(
|
|
5774
|
+
console.log(import_chalk37.default.bold(`
|
|
4873
5775
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
4874
5776
|
`));
|
|
4875
|
-
console.log(
|
|
5777
|
+
console.log(import_chalk37.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
4876
5778
|
let blob = cfg;
|
|
4877
5779
|
for (; ; ) {
|
|
4878
5780
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
@@ -4881,10 +5783,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4881
5783
|
message: "Where to?",
|
|
4882
5784
|
pageSize: 20,
|
|
4883
5785
|
choices: [
|
|
4884
|
-
new inquirer3.Separator(
|
|
5786
|
+
new inquirer3.Separator(import_chalk37.default.bold("\u2014 Settings \u2014")),
|
|
4885
5787
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
4886
|
-
new inquirer3.Separator(
|
|
4887
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
5788
|
+
new inquirer3.Separator(import_chalk37.default.bold("\u2014 Features \u2014")),
|
|
5789
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk37.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
4888
5790
|
new inquirer3.Separator(),
|
|
4889
5791
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
4890
5792
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -4924,7 +5826,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4924
5826
|
}
|
|
4925
5827
|
}
|
|
4926
5828
|
} catch (err) {
|
|
4927
|
-
console.error(
|
|
5829
|
+
console.error(import_chalk37.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
4928
5830
|
}
|
|
4929
5831
|
}
|
|
4930
5832
|
}
|
|
@@ -4938,7 +5840,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
4938
5840
|
message: group2 + ":",
|
|
4939
5841
|
pageSize: 16,
|
|
4940
5842
|
choices: [
|
|
4941
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
5843
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk37.default.dim(s2.desc)}`, value: s2 })),
|
|
4942
5844
|
new inquirer3.Separator(),
|
|
4943
5845
|
{ name: "\u2190 Back", value: null }
|
|
4944
5846
|
]
|
|
@@ -4956,7 +5858,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
4956
5858
|
} else if (s.type === "number") {
|
|
4957
5859
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
4958
5860
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
4959
|
-
console.log(
|
|
5861
|
+
console.log(import_chalk37.default.yellow(" Not a number \u2014 unchanged."));
|
|
4960
5862
|
continue;
|
|
4961
5863
|
}
|
|
4962
5864
|
value = Number(v);
|
|
@@ -5037,7 +5939,7 @@ async function buildCondition(phase) {
|
|
|
5037
5939
|
const items = [];
|
|
5038
5940
|
for (; ; ) {
|
|
5039
5941
|
const a = await inquirer3.prompt([
|
|
5040
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
5942
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk37.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
5041
5943
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
5042
5944
|
"eq",
|
|
5043
5945
|
"neq",
|
|
@@ -5068,7 +5970,7 @@ async function buildCondition(phase) {
|
|
|
5068
5970
|
function showCondition(cond) {
|
|
5069
5971
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
5070
5972
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
5071
|
-
return
|
|
5973
|
+
return import_chalk37.default.dim(` when ${s}`);
|
|
5072
5974
|
}
|
|
5073
5975
|
async function transformsMenu(proj2) {
|
|
5074
5976
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -5077,12 +5979,12 @@ async function transformsMenu(proj2) {
|
|
|
5077
5979
|
const out = await admin({ method: "GET", path: base2, summary: "List transform rules" });
|
|
5078
5980
|
const rules = out?.rules ?? [];
|
|
5079
5981
|
console.log();
|
|
5080
|
-
if (!rules.length) console.log(
|
|
5982
|
+
if (!rules.length) console.log(import_chalk37.default.dim(" No transform rules yet."));
|
|
5081
5983
|
for (const r of rules) {
|
|
5082
5984
|
const a = r.action ?? {};
|
|
5083
5985
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
5084
|
-
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 ?
|
|
5085
|
-
console.log(` ${r.enabled ?
|
|
5986
|
+
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_chalk37.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
|
|
5987
|
+
console.log(` ${r.enabled ? import_chalk37.default.green("\u25CF") : import_chalk37.default.dim("\u25CB")} ${import_chalk37.default.bold(r.name)} ${import_chalk37.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
|
|
5086
5988
|
}
|
|
5087
5989
|
const { act } = await inquirer3.prompt([{
|
|
5088
5990
|
type: "list",
|
|
@@ -5094,7 +5996,7 @@ async function transformsMenu(proj2) {
|
|
|
5094
5996
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
5095
5997
|
{ name: "Delete a rule", value: "delete" }
|
|
5096
5998
|
] : [],
|
|
5097
|
-
{ name:
|
|
5999
|
+
{ name: import_chalk37.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
5098
6000
|
{ name: "\u2190 Back", value: "back" }
|
|
5099
6001
|
]
|
|
5100
6002
|
}]);
|
|
@@ -5107,11 +6009,11 @@ async function transformsMenu(proj2) {
|
|
|
5107
6009
|
}]);
|
|
5108
6010
|
const body = parseValue(raw);
|
|
5109
6011
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
5110
|
-
console.log(
|
|
6012
|
+
console.log(import_chalk37.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
5111
6013
|
continue;
|
|
5112
6014
|
}
|
|
5113
6015
|
await admin({ method: "POST", path: base2, body, summary: "Create transform rule (raw JSON)" });
|
|
5114
|
-
console.log(
|
|
6016
|
+
console.log(import_chalk37.default.green(" Rule created."));
|
|
5115
6017
|
continue;
|
|
5116
6018
|
}
|
|
5117
6019
|
if (act === "add") {
|
|
@@ -5127,7 +6029,7 @@ async function transformsMenu(proj2) {
|
|
|
5127
6029
|
{ name: "Remove a field", value: "remove" }
|
|
5128
6030
|
] }
|
|
5129
6031
|
]);
|
|
5130
|
-
const fieldHint =
|
|
6032
|
+
const fieldHint = import_chalk37.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
5131
6033
|
let action2;
|
|
5132
6034
|
if (ans.kind === "hardcode") {
|
|
5133
6035
|
const a = await inquirer3.prompt([
|
|
@@ -5174,16 +6076,16 @@ async function transformsMenu(proj2) {
|
|
|
5174
6076
|
type: "list",
|
|
5175
6077
|
name: "rule",
|
|
5176
6078
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
5177
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
6079
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk37.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
5178
6080
|
}]);
|
|
5179
6081
|
if (!rule) continue;
|
|
5180
6082
|
if (act === "toggle") {
|
|
5181
6083
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
5182
6084
|
await admin({ method: "PUT", path: `${base2}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
5183
|
-
console.log(
|
|
6085
|
+
console.log(import_chalk37.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
5184
6086
|
} else {
|
|
5185
6087
|
await admin({ method: "DELETE", path: `${base2}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
5186
|
-
console.log(
|
|
6088
|
+
console.log(import_chalk37.default.green(` ${rule.name} deleted.`));
|
|
5187
6089
|
}
|
|
5188
6090
|
}
|
|
5189
6091
|
}
|
|
@@ -5195,9 +6097,9 @@ async function mappingsMenu(proj2) {
|
|
|
5195
6097
|
const out = await admin({ method: "GET", path: base2, summary: "List mapping tables" });
|
|
5196
6098
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
5197
6099
|
console.log();
|
|
5198
|
-
if (!tables.length) console.log(
|
|
6100
|
+
if (!tables.length) console.log(import_chalk37.default.dim(" No mapping tables yet."));
|
|
5199
6101
|
for (const t of tables) {
|
|
5200
|
-
console.log(` ${
|
|
6102
|
+
console.log(` ${import_chalk37.default.bold(t.name)} ${import_chalk37.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
|
|
5201
6103
|
}
|
|
5202
6104
|
const { act } = await inquirer3.prompt([{
|
|
5203
6105
|
type: "list",
|
|
@@ -5217,11 +6119,11 @@ async function mappingsMenu(proj2) {
|
|
|
5217
6119
|
]);
|
|
5218
6120
|
const entries2 = parseValue(a.entries);
|
|
5219
6121
|
if (!Array.isArray(entries2)) {
|
|
5220
|
-
console.log(
|
|
6122
|
+
console.log(import_chalk37.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
5221
6123
|
continue;
|
|
5222
6124
|
}
|
|
5223
6125
|
await admin({ method: "POST", path: base2, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
5224
|
-
console.log(
|
|
6126
|
+
console.log(import_chalk37.default.green(` Table "${a.name}" created.`));
|
|
5225
6127
|
} else {
|
|
5226
6128
|
const { table } = await inquirer3.prompt([{
|
|
5227
6129
|
type: "list",
|
|
@@ -5231,7 +6133,7 @@ async function mappingsMenu(proj2) {
|
|
|
5231
6133
|
}]);
|
|
5232
6134
|
if (!table) continue;
|
|
5233
6135
|
await admin({ method: "DELETE", path: `${base2}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
5234
|
-
console.log(
|
|
6136
|
+
console.log(import_chalk37.default.green(` ${table.name} deleted.`));
|
|
5235
6137
|
}
|
|
5236
6138
|
}
|
|
5237
6139
|
}
|
|
@@ -5242,14 +6144,14 @@ async function tenantsMenu(proj2, opts) {
|
|
|
5242
6144
|
const out = await admin({ method: "GET", path: base2, summary: "List attached tenants" });
|
|
5243
6145
|
const tenants = out?.tenants ?? [];
|
|
5244
6146
|
console.log();
|
|
5245
|
-
if (!tenants.length) console.log(
|
|
5246
|
-
for (const t of tenants) console.log(` ${
|
|
6147
|
+
if (!tenants.length) console.log(import_chalk37.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
6148
|
+
for (const t of tenants) console.log(` ${import_chalk37.default.bold(t.tenant_name ?? t.name)} ${import_chalk37.default.dim(t.display_name ?? "")}`);
|
|
5247
6149
|
const { act } = await inquirer3.prompt([{
|
|
5248
6150
|
type: "list",
|
|
5249
6151
|
name: "act",
|
|
5250
6152
|
message: "Tenants:",
|
|
5251
6153
|
choices: [
|
|
5252
|
-
{ name: `Manage a tenant\u2026 ${
|
|
6154
|
+
{ name: `Manage a tenant\u2026 ${import_chalk37.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
5253
6155
|
{ name: "Attach a tenant to this project", value: "attach" },
|
|
5254
6156
|
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
5255
6157
|
{ name: "\u2190 Back", value: "back" }
|
|
@@ -5272,7 +6174,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
5272
6174
|
}]);
|
|
5273
6175
|
if (!t) continue;
|
|
5274
6176
|
await admin({ method: "DELETE", path: `${base2}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
5275
|
-
console.log(
|
|
6177
|
+
console.log(import_chalk37.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
5276
6178
|
}
|
|
5277
6179
|
}
|
|
5278
6180
|
}
|
|
@@ -5315,7 +6217,7 @@ async function specMenu(proj2, opts) {
|
|
|
5315
6217
|
choices: [
|
|
5316
6218
|
{ name: "Print the stored spec", value: "get" },
|
|
5317
6219
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
5318
|
-
{ name:
|
|
6220
|
+
{ name: import_chalk37.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
5319
6221
|
{ name: "\u2190 Back", value: "back" }
|
|
5320
6222
|
]
|
|
5321
6223
|
}]);
|
|
@@ -5323,7 +6225,7 @@ async function specMenu(proj2, opts) {
|
|
|
5323
6225
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
5324
6226
|
else if (act === "refresh") {
|
|
5325
6227
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
5326
|
-
console.log(
|
|
6228
|
+
console.log(import_chalk37.default.green(" Spec refresh triggered."));
|
|
5327
6229
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
5328
6230
|
}
|
|
5329
6231
|
async function agentsMenu(proj2, opts) {
|
|
@@ -5348,7 +6250,7 @@ async function agentsMenu(proj2, opts) {
|
|
|
5348
6250
|
}
|
|
5349
6251
|
|
|
5350
6252
|
// src/commands/group.ts
|
|
5351
|
-
var
|
|
6253
|
+
var import_chalk38 = __toESM(require("chalk"));
|
|
5352
6254
|
var import_ora17 = __toESM(require("ora"));
|
|
5353
6255
|
init_auth();
|
|
5354
6256
|
init_admin();
|
|
@@ -5442,7 +6344,7 @@ async function userByRefOrCreate(t, ref) {
|
|
|
5442
6344
|
if (!ref.includes("@")) throw e;
|
|
5443
6345
|
const created = await t.call("/v1/admin/users", "POST", { email: ref.trim().toLowerCase() });
|
|
5444
6346
|
if (!created?.abz_sub) throw e;
|
|
5445
|
-
console.log(
|
|
6347
|
+
console.log(import_chalk38.default.dim(` ${ref} wasn't a user yet \u2014 created.`));
|
|
5446
6348
|
return { abz_sub: created.abz_sub, email: created.email ?? ref, display_name: null };
|
|
5447
6349
|
}
|
|
5448
6350
|
}
|
|
@@ -5450,11 +6352,11 @@ async function runGroupList(opts) {
|
|
|
5450
6352
|
const t = await resolveTransport(opts);
|
|
5451
6353
|
const groups = await listGroups(t);
|
|
5452
6354
|
if (opts.json) return void console.log(JSON.stringify(groups));
|
|
5453
|
-
if (!groups.length) return void console.log(
|
|
5454
|
-
console.log(
|
|
6355
|
+
if (!groups.length) return void console.log(import_chalk38.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
|
|
6356
|
+
console.log(import_chalk38.default.dim(`Tenant ${t.tenant}:`));
|
|
5455
6357
|
for (const g of groups) {
|
|
5456
|
-
const sealed = g.name === "apiblaze_admins" ?
|
|
5457
|
-
console.log(` ${
|
|
6358
|
+
const sealed = g.name === "apiblaze_admins" ? import_chalk38.default.dim(" (admins \u2014 managed by allowlist)") : "";
|
|
6359
|
+
console.log(` ${import_chalk38.default.bold(g.name)} ${import_chalk38.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
|
|
5458
6360
|
}
|
|
5459
6361
|
}
|
|
5460
6362
|
async function runGroupCreate(name, opts) {
|
|
@@ -5482,7 +6384,7 @@ async function runGroupCreate(name, opts) {
|
|
|
5482
6384
|
const spinner = (0, import_ora17.default)(`Creating group ${name}...`).start();
|
|
5483
6385
|
try {
|
|
5484
6386
|
await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
|
|
5485
|
-
spinner.succeed(`Group ${
|
|
6387
|
+
spinner.succeed(`Group ${import_chalk38.default.bold(name)} created in ${t.tenant}.`);
|
|
5486
6388
|
} catch (e) {
|
|
5487
6389
|
spinner.fail("Create failed.");
|
|
5488
6390
|
throw e;
|
|
@@ -5494,7 +6396,7 @@ async function runGroupDelete(name, opts) {
|
|
|
5494
6396
|
const spinner = (0, import_ora17.default)(`Deleting group ${name}...`).start();
|
|
5495
6397
|
try {
|
|
5496
6398
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
|
|
5497
|
-
spinner.succeed(`Group ${
|
|
6399
|
+
spinner.succeed(`Group ${import_chalk38.default.bold(name)} deleted.`);
|
|
5498
6400
|
} catch (e) {
|
|
5499
6401
|
spinner.fail("Delete failed.");
|
|
5500
6402
|
throw e;
|
|
@@ -5507,11 +6409,11 @@ async function runGroupMembers(name, opts) {
|
|
|
5507
6409
|
const subs = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/subgroups`);
|
|
5508
6410
|
if (opts.json) return void console.log(JSON.stringify({ ...detail, subgroups: subs?.subgroups ?? [] }));
|
|
5509
6411
|
const members = detail?.members ?? [];
|
|
5510
|
-
console.log(
|
|
5511
|
-
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ?
|
|
5512
|
-
if (!members.length) console.log(
|
|
6412
|
+
console.log(import_chalk38.default.bold(`${g.name}`) + import_chalk38.default.dim(` (${t.tenant})`));
|
|
6413
|
+
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk38.default.cyan("group-admin") : import_chalk38.default.dim("member")}`);
|
|
6414
|
+
if (!members.length) console.log(import_chalk38.default.dim(" no members"));
|
|
5513
6415
|
const sg = subs?.subgroups ?? [];
|
|
5514
|
-
if (sg.length) console.log(
|
|
6416
|
+
if (sg.length) console.log(import_chalk38.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
|
|
5515
6417
|
}
|
|
5516
6418
|
async function runGroupAddUser(userRef, groupName, opts) {
|
|
5517
6419
|
const t = await resolveTransport(opts);
|
|
@@ -5521,7 +6423,7 @@ async function runGroupAddUser(userRef, groupName, opts) {
|
|
|
5521
6423
|
const spinner = (0, import_ora17.default)(`Adding ${label2(u)} to ${g.name}...`).start();
|
|
5522
6424
|
try {
|
|
5523
6425
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
|
|
5524
|
-
spinner.succeed(`${
|
|
6426
|
+
spinner.succeed(`${import_chalk38.default.bold(label2(u))} added to ${import_chalk38.default.bold(g.name)} as ${role}.`);
|
|
5525
6427
|
} catch (e) {
|
|
5526
6428
|
spinner.fail("Add failed.");
|
|
5527
6429
|
throw e;
|
|
@@ -5533,7 +6435,7 @@ async function runGroupRemoveUser(userRef, groupName, opts) {
|
|
|
5533
6435
|
const spinner = (0, import_ora17.default)(`Removing ${label2(u)} from ${g.name}...`).start();
|
|
5534
6436
|
try {
|
|
5535
6437
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
|
|
5536
|
-
spinner.succeed(`${
|
|
6438
|
+
spinner.succeed(`${import_chalk38.default.bold(label2(u))} removed from ${import_chalk38.default.bold(g.name)}.`);
|
|
5537
6439
|
} catch (e) {
|
|
5538
6440
|
spinner.fail("Remove failed.");
|
|
5539
6441
|
throw e;
|
|
@@ -5545,7 +6447,7 @@ async function runGroupAddGroup(childName, parentName, opts) {
|
|
|
5545
6447
|
const spinner = (0, import_ora17.default)(`Nesting ${child.name} under ${parent.name}...`).start();
|
|
5546
6448
|
try {
|
|
5547
6449
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
|
|
5548
|
-
spinner.succeed(`${
|
|
6450
|
+
spinner.succeed(`${import_chalk38.default.bold(child.name)} is now a subgroup of ${import_chalk38.default.bold(parent.name)}.`);
|
|
5549
6451
|
} catch (e) {
|
|
5550
6452
|
spinner.fail("Nest failed.");
|
|
5551
6453
|
throw e;
|
|
@@ -5557,7 +6459,7 @@ async function runGroupRemoveGroup(childName, parentName, opts) {
|
|
|
5557
6459
|
const spinner = (0, import_ora17.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
|
|
5558
6460
|
try {
|
|
5559
6461
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
|
|
5560
|
-
spinner.succeed(`${
|
|
6462
|
+
spinner.succeed(`${import_chalk38.default.bold(child.name)} un-nested from ${import_chalk38.default.bold(parent.name)}.`);
|
|
5561
6463
|
} catch (e) {
|
|
5562
6464
|
spinner.fail("Un-nest failed.");
|
|
5563
6465
|
throw e;
|
|
@@ -5568,18 +6470,18 @@ async function runGroupUsers(opts) {
|
|
|
5568
6470
|
const users = await listUsers(t);
|
|
5569
6471
|
const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
|
|
5570
6472
|
if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
|
|
5571
|
-
console.log(
|
|
5572
|
-
for (const u of users) console.log(` ${label2(u)} ${
|
|
5573
|
-
if (!users.length) console.log(
|
|
6473
|
+
console.log(import_chalk38.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
|
|
6474
|
+
for (const u of users) console.log(` ${label2(u)} ${import_chalk38.default.dim(u.abz_sub)}`);
|
|
6475
|
+
if (!users.length) console.log(import_chalk38.default.dim(" no users yet"));
|
|
5574
6476
|
const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
|
|
5575
6477
|
if (fresh.length) {
|
|
5576
|
-
console.log(
|
|
5577
|
-
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${
|
|
6478
|
+
console.log(import_chalk38.default.dim("Seen in traffic (not yet users):"));
|
|
6479
|
+
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk38.default.dim(o.source_label ?? "")}`);
|
|
5578
6480
|
}
|
|
5579
6481
|
}
|
|
5580
6482
|
|
|
5581
6483
|
// src/commands/key.ts
|
|
5582
|
-
var
|
|
6484
|
+
var import_chalk39 = __toESM(require("chalk"));
|
|
5583
6485
|
var import_ora18 = __toESM(require("ora"));
|
|
5584
6486
|
init_caller();
|
|
5585
6487
|
async function runApikeysMenu(opts) {
|
|
@@ -5609,11 +6511,11 @@ async function runKeyList(opts) {
|
|
|
5609
6511
|
return;
|
|
5610
6512
|
}
|
|
5611
6513
|
if (!keys.length) {
|
|
5612
|
-
console.log(
|
|
6514
|
+
console.log(import_chalk39.default.yellow("No developer keys."));
|
|
5613
6515
|
return;
|
|
5614
6516
|
}
|
|
5615
6517
|
for (const k of keys) {
|
|
5616
|
-
console.log(` ${
|
|
6518
|
+
console.log(` ${import_chalk39.default.bold(k.key_id ?? k.id)} ${import_chalk39.default.dim(k.description ?? "")} ${import_chalk39.default.dim(k.expires_at ?? "no expiry")}`);
|
|
5617
6519
|
}
|
|
5618
6520
|
}
|
|
5619
6521
|
async function runKeyMint(opts) {
|
|
@@ -5635,9 +6537,9 @@ async function runKeyMint(opts) {
|
|
|
5635
6537
|
console.log(JSON.stringify(out));
|
|
5636
6538
|
return;
|
|
5637
6539
|
}
|
|
5638
|
-
console.log(` ${
|
|
5639
|
-
console.log(` ${
|
|
5640
|
-
if (out?.expires_at) console.log(` ${
|
|
6540
|
+
console.log(` ${import_chalk39.default.bold("key_id")}: ${out?.key_id}`);
|
|
6541
|
+
console.log(` ${import_chalk39.default.bold("key")}: ${import_chalk39.default.green(out?.key)} ${import_chalk39.default.dim("(shown once \u2014 store it now)")}`);
|
|
6542
|
+
if (out?.expires_at) console.log(` ${import_chalk39.default.dim("expires:")} ${out.expires_at}`);
|
|
5641
6543
|
} catch (err) {
|
|
5642
6544
|
spinner.fail("Mint failed.");
|
|
5643
6545
|
throw err;
|
|
@@ -5664,7 +6566,7 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
5664
6566
|
init_iam();
|
|
5665
6567
|
|
|
5666
6568
|
// src/commands/admins.ts
|
|
5667
|
-
var
|
|
6569
|
+
var import_chalk40 = __toESM(require("chalk"));
|
|
5668
6570
|
var import_ora19 = __toESM(require("ora"));
|
|
5669
6571
|
init_caller();
|
|
5670
6572
|
function requireTenant(opts) {
|
|
@@ -5692,13 +6594,13 @@ async function runAdminsList(opts) {
|
|
|
5692
6594
|
return;
|
|
5693
6595
|
}
|
|
5694
6596
|
if (!admins2.length) {
|
|
5695
|
-
console.log(
|
|
6597
|
+
console.log(import_chalk40.default.yellow(`No tenant admins for ${tenant2} yet.`));
|
|
5696
6598
|
return;
|
|
5697
6599
|
}
|
|
5698
6600
|
for (const a of admins2) {
|
|
5699
6601
|
const email = typeof a === "string" ? a : a.email;
|
|
5700
6602
|
const flags = [a.active === false ? "pending" : null, a.pinned ? "pinned" : null].filter(Boolean).join(", ");
|
|
5701
|
-
console.log(` ${
|
|
6603
|
+
console.log(` ${import_chalk40.default.bold(email)}${flags ? import_chalk40.default.dim(` (${flags})`) : ""}`);
|
|
5702
6604
|
}
|
|
5703
6605
|
}
|
|
5704
6606
|
async function runAdminsAdd(email, opts) {
|
|
@@ -5713,9 +6615,9 @@ async function runAdminsAdd(email, opts) {
|
|
|
5713
6615
|
body: { email },
|
|
5714
6616
|
summary: `Add ${email} to tenant ${tenant2} admins`
|
|
5715
6617
|
});
|
|
5716
|
-
spinner?.succeed(`${
|
|
6618
|
+
spinner?.succeed(`${import_chalk40.default.bold(email)} is now a tenant admin of ${import_chalk40.default.bold(tenant2)}.`);
|
|
5717
6619
|
if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
|
|
5718
|
-
else console.log(
|
|
6620
|
+
else console.log(import_chalk40.default.dim(" Reload the Users & Groups widget \u2014 access flips from \u201Cpending\u201D to ready."));
|
|
5719
6621
|
} catch (err) {
|
|
5720
6622
|
spinner?.fail("Add failed.");
|
|
5721
6623
|
throw err;
|
|
@@ -5732,7 +6634,7 @@ async function runAdminsRemove(email, opts) {
|
|
|
5732
6634
|
path: `${base(teamId, tenant2)}/${encodeURIComponent(email)}`,
|
|
5733
6635
|
summary: `Remove ${email} from tenant ${tenant2} admins`
|
|
5734
6636
|
});
|
|
5735
|
-
spinner?.succeed(`Removed ${
|
|
6637
|
+
spinner?.succeed(`Removed ${import_chalk40.default.bold(email)} from the tenant admins of ${import_chalk40.default.bold(tenant2)}.`);
|
|
5736
6638
|
} catch (err) {
|
|
5737
6639
|
spinner?.fail("Remove failed.");
|
|
5738
6640
|
throw err;
|
|
@@ -5740,7 +6642,7 @@ async function runAdminsRemove(email, opts) {
|
|
|
5740
6642
|
}
|
|
5741
6643
|
|
|
5742
6644
|
// src/commands/preapprove.ts
|
|
5743
|
-
var
|
|
6645
|
+
var import_chalk41 = __toESM(require("chalk"));
|
|
5744
6646
|
var import_ora20 = __toESM(require("ora"));
|
|
5745
6647
|
|
|
5746
6648
|
// src/lib/preapproval.ts
|
|
@@ -5805,14 +6707,14 @@ async function runPreapprove(who, opts) {
|
|
|
5805
6707
|
const rules = await listPreapprovalRules(tenant2);
|
|
5806
6708
|
if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
|
|
5807
6709
|
if (!rules.length) {
|
|
5808
|
-
console.log(
|
|
5809
|
-
console.log(
|
|
6710
|
+
console.log(import_chalk41.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
|
|
6711
|
+
console.log(import_chalk41.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
|
|
5810
6712
|
return;
|
|
5811
6713
|
}
|
|
5812
|
-
console.log(
|
|
6714
|
+
console.log(import_chalk41.default.dim(`Pre-approved for ${import_chalk41.default.bold(tenant2)}:`));
|
|
5813
6715
|
for (const r of rules) {
|
|
5814
|
-
const tag = r.kind === "domain" ?
|
|
5815
|
-
const grp = r.groups?.length ?
|
|
6716
|
+
const tag = r.kind === "domain" ? import_chalk41.default.cyan("@" + r.value + " (whole domain)") : r.value;
|
|
6717
|
+
const grp = r.groups?.length ? import_chalk41.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
|
|
5816
6718
|
console.log(` ${tag}${grp}`);
|
|
5817
6719
|
}
|
|
5818
6720
|
return;
|
|
@@ -5823,17 +6725,17 @@ async function runPreapprove(who, opts) {
|
|
|
5823
6725
|
if (opts.remove) {
|
|
5824
6726
|
const spinner2 = (0, import_ora20.default)(`Removing ${who} from ${tenant2}\u2026`).start();
|
|
5825
6727
|
const { removed, value } = await removePreapprovalRule(tenant2, who);
|
|
5826
|
-
if (removed) spinner2.succeed(`${
|
|
5827
|
-
else spinner2.warn(`No pre-approval rule for ${
|
|
6728
|
+
if (removed) spinner2.succeed(`${import_chalk41.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
|
|
6729
|
+
else spinner2.warn(`No pre-approval rule for ${import_chalk41.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
|
|
5828
6730
|
return;
|
|
5829
6731
|
}
|
|
5830
6732
|
const spinner = (0, import_ora20.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
|
|
5831
6733
|
try {
|
|
5832
6734
|
const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
|
|
5833
6735
|
const what = kind === "domain" ? `Anyone @${value}` : value;
|
|
5834
|
-
spinner.succeed(`${
|
|
5835
|
-
if (opts.group?.length) console.log(
|
|
5836
|
-
console.log(
|
|
6736
|
+
spinner.succeed(`${import_chalk41.default.bold(what)} can now sign in to ${import_chalk41.default.bold(tenant2)}.`);
|
|
6737
|
+
if (opts.group?.length) console.log(import_chalk41.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
|
|
6738
|
+
console.log(import_chalk41.default.dim(` See the full list: apiblaze preapprove --list`));
|
|
5837
6739
|
} catch (err) {
|
|
5838
6740
|
spinner.fail("Could not add the rule.");
|
|
5839
6741
|
throw err;
|
|
@@ -5844,7 +6746,7 @@ async function runPreapprove(who, opts) {
|
|
|
5844
6746
|
var fs9 = __toESM(require("fs"));
|
|
5845
6747
|
var path6 = __toESM(require("path"));
|
|
5846
6748
|
var crypto2 = __toESM(require("crypto"));
|
|
5847
|
-
var
|
|
6749
|
+
var import_chalk43 = __toESM(require("chalk"));
|
|
5848
6750
|
var import_ora21 = __toESM(require("ora"));
|
|
5849
6751
|
var import_yaml2 = require("yaml");
|
|
5850
6752
|
init_auth();
|
|
@@ -5855,7 +6757,7 @@ init_admin();
|
|
|
5855
6757
|
// src/commands/llm.ts
|
|
5856
6758
|
var fs8 = __toESM(require("fs"));
|
|
5857
6759
|
var path5 = __toESM(require("path"));
|
|
5858
|
-
var
|
|
6760
|
+
var import_chalk42 = __toESM(require("chalk"));
|
|
5859
6761
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
5860
6762
|
init_auth();
|
|
5861
6763
|
var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
|
|
@@ -5899,27 +6801,27 @@ async function runLlmSetKey(keyArg, opts) {
|
|
|
5899
6801
|
}
|
|
5900
6802
|
const existing = loadLlmConfig();
|
|
5901
6803
|
saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
|
|
5902
|
-
console.log(`${
|
|
5903
|
-
console.log(
|
|
5904
|
-
if (opts.model) console.log(
|
|
6804
|
+
console.log(`${import_chalk42.default.green("\u2713")} Saved ${import_chalk42.default.bold(provider)} key ${maskSecret(key)}`);
|
|
6805
|
+
console.log(import_chalk42.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
|
|
6806
|
+
if (opts.model) console.log(import_chalk42.default.gray(` Model: ${opts.model}`));
|
|
5905
6807
|
}
|
|
5906
6808
|
async function runLlmShow() {
|
|
5907
6809
|
const cfg = loadLlmConfig();
|
|
5908
6810
|
if (!cfg) {
|
|
5909
|
-
console.log(
|
|
6811
|
+
console.log(import_chalk42.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
|
|
5910
6812
|
return;
|
|
5911
6813
|
}
|
|
5912
|
-
console.log(`Provider: ${
|
|
6814
|
+
console.log(`Provider: ${import_chalk42.default.bold(cfg.provider)}`);
|
|
5913
6815
|
console.log(`Key: ${maskSecret(cfg.key)}`);
|
|
5914
6816
|
if (cfg.model) console.log(`Model: ${cfg.model}`);
|
|
5915
|
-
console.log(
|
|
6817
|
+
console.log(import_chalk42.default.gray(`Stored at ${LLM_PATH}`));
|
|
5916
6818
|
}
|
|
5917
6819
|
async function runLlmClearKey() {
|
|
5918
6820
|
try {
|
|
5919
6821
|
fs8.unlinkSync(LLM_PATH);
|
|
5920
|
-
console.log(`${
|
|
6822
|
+
console.log(`${import_chalk42.default.green("\u2713")} Removed local LLM key.`);
|
|
5921
6823
|
} catch {
|
|
5922
|
-
console.log(
|
|
6824
|
+
console.log(import_chalk42.default.gray("No LLM key was set."));
|
|
5923
6825
|
}
|
|
5924
6826
|
}
|
|
5925
6827
|
|
|
@@ -5927,9 +6829,9 @@ async function runLlmClearKey() {
|
|
|
5927
6829
|
init_trace();
|
|
5928
6830
|
init_types();
|
|
5929
6831
|
function fail4(message, hint) {
|
|
5930
|
-
console.error(
|
|
6832
|
+
console.error(import_chalk43.default.red(`
|
|
5931
6833
|
Error: ${message}`));
|
|
5932
|
-
if (hint) console.error(
|
|
6834
|
+
if (hint) console.error(import_chalk43.default.dim(hint));
|
|
5933
6835
|
process.exit(1);
|
|
5934
6836
|
}
|
|
5935
6837
|
function normalizeName2(raw) {
|
|
@@ -6077,7 +6979,7 @@ async function resolveTargetAuth(spec2, opts) {
|
|
|
6077
6979
|
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
6078
6980
|
);
|
|
6079
6981
|
}
|
|
6080
|
-
if (sawOAuth) console.log(
|
|
6982
|
+
if (sawOAuth) console.log(import_chalk43.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
|
|
6081
6983
|
return null;
|
|
6082
6984
|
}
|
|
6083
6985
|
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
@@ -6150,7 +7052,7 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
|
|
|
6150
7052
|
const fresh = await validConsumerToken(existing);
|
|
6151
7053
|
if (fresh) {
|
|
6152
7054
|
if (fresh.accessToken !== existing.accessToken) saveConsumer({ ...fresh, resource });
|
|
6153
|
-
console.log(
|
|
7055
|
+
console.log(import_chalk43.default.dim(` Using your consumer session on ${import_chalk43.default.bold(tenant2)}${fresh.email ? ` (${fresh.email})` : ""}.`));
|
|
6154
7056
|
return fresh;
|
|
6155
7057
|
}
|
|
6156
7058
|
}
|
|
@@ -6169,13 +7071,13 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
|
|
|
6169
7071
|
);
|
|
6170
7072
|
}
|
|
6171
7073
|
const clientId = pick2.client_id ?? pick2.clientId;
|
|
6172
|
-
console.log(`${
|
|
7074
|
+
console.log(`${import_chalk43.default.cyan("\u2192")} This proxy signs consumers in with OAuth \u2014 logging you in to ${import_chalk43.default.bold(tenant2)}...`);
|
|
6173
7075
|
const result = await deviceLogin(clientId, "openid email profile offline_access", ({ verificationUri, userCode }) => {
|
|
6174
7076
|
console.log(`
|
|
6175
|
-
Open: ${
|
|
6176
|
-
console.log(` Code: ${
|
|
7077
|
+
Open: ${import_chalk43.default.underline(verificationUri)}`);
|
|
7078
|
+
console.log(` Code: ${import_chalk43.default.bold(userCode)}
|
|
6177
7079
|
`);
|
|
6178
|
-
console.log(
|
|
7080
|
+
console.log(import_chalk43.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
6179
7081
|
}, resource);
|
|
6180
7082
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
6181
7083
|
const creds = {
|
|
@@ -6191,7 +7093,7 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
|
|
|
6191
7093
|
resource
|
|
6192
7094
|
};
|
|
6193
7095
|
saveConsumer(creds);
|
|
6194
|
-
console.log(` ${
|
|
7096
|
+
console.log(` ${import_chalk43.default.green("\u2714")} Signed in as${creds.email ? ` ${import_chalk43.default.bold(creds.email)}` : " a consumer"} on ${tenant2}.`);
|
|
6195
7097
|
return creds;
|
|
6196
7098
|
}
|
|
6197
7099
|
async function cpPost(anon, path8, body, summary) {
|
|
@@ -6220,7 +7122,7 @@ async function provision(spec2, target, opts) {
|
|
|
6220
7122
|
let name = opts.name ? base2 : `${base2}${salt()}`;
|
|
6221
7123
|
const access = anon ? "open" : opts.access === "open" ? "open" : "invite";
|
|
6222
7124
|
if (anon && opts.access === "invite") {
|
|
6223
|
-
console.log(
|
|
7125
|
+
console.log(import_chalk43.default.dim(" Note: --access invite needs an account to pre-approve people. Staying open for this anonymous proxy \u2014 run `apiblaze login`, then `apiblaze apichat --access invite`."));
|
|
6224
7126
|
}
|
|
6225
7127
|
const DUAL_AUTH = {
|
|
6226
7128
|
mode: "authenticate",
|
|
@@ -6319,7 +7221,7 @@ async function provision(spec2, target, opts) {
|
|
|
6319
7221
|
try {
|
|
6320
7222
|
await addPreapprovalRule(tenant2, email);
|
|
6321
7223
|
} catch {
|
|
6322
|
-
console.log(
|
|
7224
|
+
console.log(import_chalk43.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
|
|
6323
7225
|
}
|
|
6324
7226
|
}
|
|
6325
7227
|
}
|
|
@@ -6363,7 +7265,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
6363
7265
|
throw err;
|
|
6364
7266
|
}
|
|
6365
7267
|
if (out && out.reused === true) {
|
|
6366
|
-
console.log(
|
|
7268
|
+
console.log(import_chalk43.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
|
|
6367
7269
|
} else if (out && out.changed === true && out.previous_spec_hash) {
|
|
6368
7270
|
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
6369
7271
|
if (interactive) {
|
|
@@ -6371,7 +7273,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
6371
7273
|
const { go } = await inquirer3.prompt([
|
|
6372
7274
|
{ type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
|
|
6373
7275
|
]);
|
|
6374
|
-
if (!go) console.log(
|
|
7276
|
+
if (!go) console.log(import_chalk43.default.dim(" Keeping the existing MCP catalogue."));
|
|
6375
7277
|
}
|
|
6376
7278
|
}
|
|
6377
7279
|
}
|
|
@@ -6412,15 +7314,15 @@ var revealAuth = false;
|
|
|
6412
7314
|
function renderToolEvents(events, cred) {
|
|
6413
7315
|
for (const e of events ?? []) {
|
|
6414
7316
|
const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
|
|
6415
|
-
const mark = ok ?
|
|
6416
|
-
console.log(` ${mark} ${
|
|
7317
|
+
const mark = ok ? import_chalk43.default.green("\u2713") : import_chalk43.default.red("\u2717");
|
|
7318
|
+
console.log(` ${mark} ${import_chalk43.default.cyan(e.name)} ${import_chalk43.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
|
|
6417
7319
|
if (isVerbose() && e.method && e.url) {
|
|
6418
|
-
console.log(
|
|
7320
|
+
console.log(import_chalk43.default.dim(` curl -sS -X ${e.method} '${e.url}'${cred ? " \\" : ""}`));
|
|
6419
7321
|
if (cred) {
|
|
6420
7322
|
const shown = revealAuth ? cred.value : maskKey(cred.value);
|
|
6421
7323
|
const keyLine = ` -H '${cred.header}: ${shown}'`;
|
|
6422
|
-
const hint = revealAuth ? "" :
|
|
6423
|
-
console.log(
|
|
7324
|
+
const hint = revealAuth ? "" : import_chalk43.default.yellow(" \u2190 /showauth will reveal this");
|
|
7325
|
+
console.log(import_chalk43.default.dim(keyLine) + hint);
|
|
6424
7326
|
}
|
|
6425
7327
|
}
|
|
6426
7328
|
}
|
|
@@ -6441,20 +7343,20 @@ function renderToolResults(delta, events) {
|
|
|
6441
7343
|
}
|
|
6442
7344
|
})();
|
|
6443
7345
|
const lines = pretty.split("\n");
|
|
6444
|
-
console.log(
|
|
7346
|
+
console.log(import_chalk43.default.dim(" response:"));
|
|
6445
7347
|
for (const line of lines.slice(0, cap)) {
|
|
6446
|
-
console.log(
|
|
7348
|
+
console.log(import_chalk43.default.dim(` ${line}`));
|
|
6447
7349
|
}
|
|
6448
|
-
if (lines.length > cap) console.log(
|
|
7350
|
+
if (lines.length > cap) console.log(import_chalk43.default.dim(` \u2026${lines.length - cap} more lines`));
|
|
6449
7351
|
}
|
|
6450
7352
|
}
|
|
6451
7353
|
function billingLine(billing) {
|
|
6452
7354
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
6453
7355
|
if (typeof billing.free_turns_remaining === "number") return null;
|
|
6454
7356
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
6455
|
-
let line =
|
|
7357
|
+
let line = import_chalk43.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk43.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
6456
7358
|
if (typeof billing.credits_remaining === "number") {
|
|
6457
|
-
line +=
|
|
7359
|
+
line += import_chalk43.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
6458
7360
|
}
|
|
6459
7361
|
return line;
|
|
6460
7362
|
}
|
|
@@ -6462,21 +7364,21 @@ function freeBudgetWarning(billing, anon) {
|
|
|
6462
7364
|
if (!anon || !billing) return null;
|
|
6463
7365
|
if (typeof billing.free_turns_remaining === "number") {
|
|
6464
7366
|
const left2 = billing.free_turns_remaining;
|
|
6465
|
-
if (left2 <= 0) return
|
|
6466
|
-
return
|
|
7367
|
+
if (left2 <= 0) return import_chalk43.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
|
|
7368
|
+
return import_chalk43.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
|
|
6467
7369
|
}
|
|
6468
7370
|
if (typeof billing.free_remaining_cents !== "number") return null;
|
|
6469
7371
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
6470
7372
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
6471
7373
|
if (left > 8) return null;
|
|
6472
|
-
if (left <= 0) return
|
|
6473
|
-
return
|
|
7374
|
+
if (left <= 0) return import_chalk43.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
7375
|
+
return import_chalk43.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
6474
7376
|
}
|
|
6475
7377
|
function printAssistant(delta) {
|
|
6476
7378
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
6477
7379
|
const m = delta[i];
|
|
6478
7380
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
6479
|
-
console.log("\n" +
|
|
7381
|
+
console.log("\n" + import_chalk43.default.green("assistant \u203A ") + m.content + "\n");
|
|
6480
7382
|
return;
|
|
6481
7383
|
}
|
|
6482
7384
|
}
|
|
@@ -6502,7 +7404,7 @@ async function replTurn(p, messages, userText) {
|
|
|
6502
7404
|
});
|
|
6503
7405
|
} catch (err) {
|
|
6504
7406
|
spinner.fail("Network error.");
|
|
6505
|
-
console.log(
|
|
7407
|
+
console.log(import_chalk43.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
6506
7408
|
return;
|
|
6507
7409
|
}
|
|
6508
7410
|
let data = null;
|
|
@@ -6522,9 +7424,9 @@ async function replTurn(p, messages, userText) {
|
|
|
6522
7424
|
if (res.status === 401) {
|
|
6523
7425
|
const reason = data && (data.error || data.message) || null;
|
|
6524
7426
|
const usingKey = !p.consumerAuth;
|
|
6525
|
-
console.log(
|
|
7427
|
+
console.log(import_chalk43.default.red(` The proxy rejected the request (401)${reason ? `: ${reason}` : "."}`));
|
|
6526
7428
|
console.log(
|
|
6527
|
-
|
|
7429
|
+
import_chalk43.default.dim(
|
|
6528
7430
|
usingKey ? " Sent an API key. If the key was revoked, re-run `apiblaze apichat` to re-provision." : " Sent your consumer OAuth token. Re-run `apiblaze apichat` to sign in again."
|
|
6529
7431
|
)
|
|
6530
7432
|
);
|
|
@@ -6532,7 +7434,7 @@ async function replTurn(p, messages, userText) {
|
|
|
6532
7434
|
}
|
|
6533
7435
|
if (!res.ok || !data) {
|
|
6534
7436
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
6535
|
-
console.log(
|
|
7437
|
+
console.log(import_chalk43.default.red(` Chat error: ${err}`));
|
|
6536
7438
|
return;
|
|
6537
7439
|
}
|
|
6538
7440
|
if (Array.isArray(data.delta)) {
|
|
@@ -6553,25 +7455,25 @@ async function replTurn(p, messages, userText) {
|
|
|
6553
7455
|
if (warn) console.log(warn);
|
|
6554
7456
|
if (!data.continue) return;
|
|
6555
7457
|
}
|
|
6556
|
-
console.log(
|
|
7458
|
+
console.log(import_chalk43.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
6557
7459
|
}
|
|
6558
7460
|
function renderUpsell(p, upsell) {
|
|
6559
7461
|
const loggedIn = !!loadCredentials();
|
|
6560
7462
|
if (upsell.reason === "CAPPED" && !loggedIn) {
|
|
6561
|
-
console.log("\n" +
|
|
6562
|
-
console.log(
|
|
7463
|
+
console.log("\n" + import_chalk43.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
|
|
7464
|
+
console.log(import_chalk43.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
|
|
6563
7465
|
console.log();
|
|
6564
7466
|
return;
|
|
6565
7467
|
}
|
|
6566
|
-
console.log("\n" +
|
|
6567
|
-
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
7468
|
+
console.log("\n" + import_chalk43.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
7469
|
+
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "QUOTA" || upsell.reason === "PROXY_CAP" || upsell.reason === "PAUSED" || upsell.reason === "BYO_REQUIRED") {
|
|
6568
7470
|
if (!loggedIn) {
|
|
6569
|
-
console.log(
|
|
7471
|
+
console.log(import_chalk43.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
|
|
6570
7472
|
} else {
|
|
6571
|
-
console.log(
|
|
7473
|
+
console.log(import_chalk43.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
|
|
6572
7474
|
}
|
|
6573
7475
|
} else if (upsell.reason === "INFLIGHT") {
|
|
6574
|
-
console.log(
|
|
7476
|
+
console.log(import_chalk43.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
6575
7477
|
}
|
|
6576
7478
|
console.log();
|
|
6577
7479
|
}
|
|
@@ -6664,9 +7566,9 @@ async function openServerProxy(project) {
|
|
|
6664
7566
|
let consumerAuth = false;
|
|
6665
7567
|
if (acceptsApiKey) {
|
|
6666
7568
|
if (!dpKey) {
|
|
6667
|
-
console.log(
|
|
7569
|
+
console.log(import_chalk43.default.dim(` Minting an API key for tenant ${import_chalk43.default.bold(tenant2)} to query project ${import_chalk43.default.bold(project.projectName)}\u2026`));
|
|
6668
7570
|
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
6669
|
-
console.log(` ${
|
|
7571
|
+
console.log(` ${import_chalk43.default.green("\u2714")} API key: ${import_chalk43.default.dim(maskKey(dpKey))}`);
|
|
6670
7572
|
}
|
|
6671
7573
|
} else {
|
|
6672
7574
|
consumerAuth = true;
|
|
@@ -6695,9 +7597,9 @@ async function openServerProxy(project) {
|
|
|
6695
7597
|
const spec2 = raw && (raw.spec ?? raw);
|
|
6696
7598
|
spinner.stop();
|
|
6697
7599
|
if (!spec2 || !(spec2.paths || spec2.openapi)) {
|
|
6698
|
-
console.log(
|
|
7600
|
+
console.log(import_chalk43.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
|
|
6699
7601
|
} else {
|
|
6700
|
-
console.log(
|
|
7602
|
+
console.log(import_chalk43.default.dim(" Using the proxy's existing MCP catalogue (`apiblaze mcp` to rebuild it)."));
|
|
6701
7603
|
}
|
|
6702
7604
|
} catch (err) {
|
|
6703
7605
|
spinner.fail("Could not open the proxy.");
|
|
@@ -6753,7 +7655,7 @@ async function noArgsMenu(opts) {
|
|
|
6753
7655
|
const me = loadCredentials()?.apiblazeUserId;
|
|
6754
7656
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
6755
7657
|
const choices = saved.map((a) => ({
|
|
6756
|
-
name: `Chat with ${
|
|
7658
|
+
name: `Chat with ${import_chalk43.default.bold(a.name)} ${import_chalk43.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
|
|
6757
7659
|
value: { type: "existing", a }
|
|
6758
7660
|
}));
|
|
6759
7661
|
const creds = loadCredentials();
|
|
@@ -6763,14 +7665,14 @@ async function noArgsMenu(opts) {
|
|
|
6763
7665
|
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
6764
7666
|
for (const pr of proxies) {
|
|
6765
7667
|
choices.push({
|
|
6766
|
-
name: `Chat with ${
|
|
7668
|
+
name: `Chat with ${import_chalk43.default.bold(pr.projectName)} ${import_chalk43.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
6767
7669
|
value: { type: "server", project: pr }
|
|
6768
7670
|
});
|
|
6769
7671
|
}
|
|
6770
7672
|
} catch {
|
|
6771
7673
|
}
|
|
6772
7674
|
}
|
|
6773
|
-
choices.push({ name:
|
|
7675
|
+
choices.push({ name: import_chalk43.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
6774
7676
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
6775
7677
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
6776
7678
|
]);
|
|
@@ -6873,36 +7775,36 @@ async function noArgsMenu(opts) {
|
|
|
6873
7775
|
async function runRepl(p, initialMessages) {
|
|
6874
7776
|
const { default: inquirer3 } = await import("inquirer");
|
|
6875
7777
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
6876
|
-
console.log("\n" +
|
|
6877
|
-
if (messages.length) console.log(
|
|
7778
|
+
console.log("\n" + import_chalk43.default.cyan.bold("Chat with your API") + import_chalk43.default.dim(` \xB7 ${p.mcpHost}`));
|
|
7779
|
+
if (messages.length) console.log(import_chalk43.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
6878
7780
|
const llm2 = loadLlmConfig();
|
|
6879
7781
|
console.log(
|
|
6880
|
-
|
|
7782
|
+
import_chalk43.default.dim(
|
|
6881
7783
|
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 more free chats \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
|
|
6882
7784
|
)
|
|
6883
7785
|
);
|
|
6884
7786
|
for (; ; ) {
|
|
6885
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
7787
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk43.default.green("you \u203A") }]);
|
|
6886
7788
|
const text = (input ?? "").trim();
|
|
6887
7789
|
if (!text) continue;
|
|
6888
7790
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
6889
7791
|
if (text === "/login") {
|
|
6890
7792
|
try {
|
|
6891
7793
|
await runLogin();
|
|
6892
|
-
console.log(
|
|
7794
|
+
console.log(import_chalk43.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
6893
7795
|
} catch (err) {
|
|
6894
|
-
console.log(
|
|
7796
|
+
console.log(import_chalk43.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6895
7797
|
}
|
|
6896
7798
|
continue;
|
|
6897
7799
|
}
|
|
6898
7800
|
if (text === "/claim") {
|
|
6899
7801
|
const justLoggedIn = !loadCredentials();
|
|
6900
7802
|
if (justLoggedIn) {
|
|
6901
|
-
console.log(
|
|
7803
|
+
console.log(import_chalk43.default.dim(" Logging in to claim your workspace\u2026"));
|
|
6902
7804
|
try {
|
|
6903
7805
|
await runLogin();
|
|
6904
7806
|
} catch (err) {
|
|
6905
|
-
console.log(
|
|
7807
|
+
console.log(import_chalk43.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6906
7808
|
continue;
|
|
6907
7809
|
}
|
|
6908
7810
|
if (!loadCredentials()) continue;
|
|
@@ -6913,30 +7815,30 @@ async function runRepl(p, initialMessages) {
|
|
|
6913
7815
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
6914
7816
|
p.anon = false;
|
|
6915
7817
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
6916
|
-
console.log(
|
|
7818
|
+
console.log(import_chalk43.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
6917
7819
|
}
|
|
6918
7820
|
} catch (err) {
|
|
6919
|
-
console.log(
|
|
7821
|
+
console.log(import_chalk43.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6920
7822
|
}
|
|
6921
7823
|
continue;
|
|
6922
7824
|
}
|
|
6923
7825
|
if (text === "/showauth") {
|
|
6924
7826
|
revealAuth = !revealAuth;
|
|
6925
|
-
console.log(
|
|
7827
|
+
console.log(import_chalk43.default.dim(revealAuth ? " The real API key will be shown in the next call's curl. /showauth again to re-mask." : " API key re-masked."));
|
|
6926
7828
|
continue;
|
|
6927
7829
|
}
|
|
6928
7830
|
if (text.startsWith("/")) {
|
|
6929
|
-
console.log(
|
|
7831
|
+
console.log(import_chalk43.default.dim(" Commands: /login /claim /showauth /exit"));
|
|
6930
7832
|
continue;
|
|
6931
7833
|
}
|
|
6932
7834
|
await replTurn(p, messages, text);
|
|
6933
7835
|
saveTranscript(p, messages);
|
|
6934
7836
|
}
|
|
6935
|
-
console.log(
|
|
7837
|
+
console.log(import_chalk43.default.dim("\nBye."));
|
|
6936
7838
|
}
|
|
6937
7839
|
async function runApichat(opts) {
|
|
6938
7840
|
setVerbose(opts.verbose !== false);
|
|
6939
|
-
console.log(
|
|
7841
|
+
console.log(import_chalk43.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
6940
7842
|
if (!opts.openapispec && !opts.target) {
|
|
6941
7843
|
if (!process.stdin.isTTY) {
|
|
6942
7844
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -6949,7 +7851,7 @@ async function runApichat(opts) {
|
|
|
6949
7851
|
}
|
|
6950
7852
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
6951
7853
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
6952
|
-
console.log(` ${
|
|
7854
|
+
console.log(` ${import_chalk43.default.dim("Target:")} ${import_chalk43.default.bold(target)}`);
|
|
6953
7855
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
6954
7856
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
6955
7857
|
fail4(
|
|
@@ -6958,7 +7860,7 @@ async function runApichat(opts) {
|
|
|
6958
7860
|
);
|
|
6959
7861
|
}
|
|
6960
7862
|
const p = await provision(spec2, target, opts);
|
|
6961
|
-
console.log(` ${
|
|
7863
|
+
console.log(` ${import_chalk43.default.dim("Proxy: ")} ${import_chalk43.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
6962
7864
|
upsertApichat({
|
|
6963
7865
|
name: p.projectId,
|
|
6964
7866
|
target,
|
|
@@ -6978,30 +7880,30 @@ async function runApichat(opts) {
|
|
|
6978
7880
|
const secret = await captureTargetSecret(auth, opts);
|
|
6979
7881
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
6980
7882
|
} else {
|
|
6981
|
-
console.log(
|
|
7883
|
+
console.log(import_chalk43.default.dim(" Target auth: none required."));
|
|
6982
7884
|
}
|
|
6983
7885
|
const specText = JSON.stringify(spec2);
|
|
6984
7886
|
await uploadSpec(p, specText, opts);
|
|
6985
7887
|
const mcpUrl = await publishMcp(p, spec2);
|
|
6986
7888
|
console.log();
|
|
6987
|
-
if (p.proxyUrl) console.log(` ${
|
|
7889
|
+
if (p.proxyUrl) console.log(` ${import_chalk43.default.green("\u2713")} proxy ${import_chalk43.default.bold(p.proxyUrl)}`);
|
|
6988
7890
|
if (mcpUrl) {
|
|
6989
|
-
console.log(` ${
|
|
7891
|
+
console.log(` ${import_chalk43.default.green("\u2713")} mcp ${import_chalk43.default.bold(mcpUrl)}`);
|
|
6990
7892
|
if (p.access === "invite") {
|
|
6991
|
-
console.log(
|
|
6992
|
-
console.log(
|
|
7893
|
+
console.log(import_chalk43.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
|
|
7894
|
+
console.log(import_chalk43.default.dim(` Let others in: apiblaze preapprove someone@company.com${p.tenant ? ` --tenant ${p.tenant}` : ""} (or re-run with --access open)`));
|
|
6993
7895
|
} else {
|
|
6994
|
-
console.log(
|
|
7896
|
+
console.log(import_chalk43.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
|
|
6995
7897
|
}
|
|
6996
7898
|
}
|
|
6997
7899
|
if (p.anon) {
|
|
6998
|
-
console.log(
|
|
7900
|
+
console.log(import_chalk43.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
|
|
6999
7901
|
}
|
|
7000
7902
|
await runRepl(p);
|
|
7001
7903
|
}
|
|
7002
7904
|
|
|
7003
7905
|
// src/commands/consumer.ts
|
|
7004
|
-
var
|
|
7906
|
+
var import_chalk44 = __toESM(require("chalk"));
|
|
7005
7907
|
var import_ora22 = __toESM(require("ora"));
|
|
7006
7908
|
init_admin();
|
|
7007
7909
|
init_resolve();
|
|
@@ -7020,10 +7922,10 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
7020
7922
|
}
|
|
7021
7923
|
return { status: res.status, data, creds: fresh };
|
|
7022
7924
|
}
|
|
7023
|
-
function
|
|
7925
|
+
function requireConsumer2() {
|
|
7024
7926
|
const c = loadConsumer();
|
|
7025
7927
|
if (!c) {
|
|
7026
|
-
console.error(
|
|
7928
|
+
console.error(import_chalk44.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
7027
7929
|
process.exit(1);
|
|
7028
7930
|
}
|
|
7029
7931
|
return c;
|
|
@@ -7034,7 +7936,7 @@ async function runConsumerLogin(opts) {
|
|
|
7034
7936
|
let clientId = opts.client;
|
|
7035
7937
|
if (clientId) {
|
|
7036
7938
|
if (!tenant2) {
|
|
7037
|
-
console.error(
|
|
7939
|
+
console.error(import_chalk44.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
7038
7940
|
process.exit(1);
|
|
7039
7941
|
}
|
|
7040
7942
|
} else {
|
|
@@ -7052,19 +7954,19 @@ async function runConsumerLogin(opts) {
|
|
|
7052
7954
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
7053
7955
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
7054
7956
|
if (!pick2) {
|
|
7055
|
-
console.error(
|
|
7957
|
+
console.error(import_chalk44.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
7056
7958
|
process.exit(1);
|
|
7057
7959
|
}
|
|
7058
7960
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
7059
7961
|
}
|
|
7060
7962
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
7061
|
-
console.log(`${
|
|
7963
|
+
console.log(`${import_chalk44.default.cyan("\u2192")} Logging in to ${import_chalk44.default.bold(tenant2)} as a consumer...`);
|
|
7062
7964
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
7063
7965
|
console.log(`
|
|
7064
|
-
Open: ${
|
|
7065
|
-
console.log(` Code: ${
|
|
7966
|
+
Open: ${import_chalk44.default.underline(verificationUri)}`);
|
|
7967
|
+
console.log(` Code: ${import_chalk44.default.bold(userCode)}
|
|
7066
7968
|
`);
|
|
7067
|
-
console.log(
|
|
7969
|
+
console.log(import_chalk44.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
7068
7970
|
}, portalResource);
|
|
7069
7971
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
7070
7972
|
const creds = {
|
|
@@ -7079,10 +7981,10 @@ async function runConsumerLogin(opts) {
|
|
|
7079
7981
|
obtainedAt: Date.now()
|
|
7080
7982
|
};
|
|
7081
7983
|
saveConsumer(creds);
|
|
7082
|
-
console.log(
|
|
7984
|
+
console.log(import_chalk44.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
7083
7985
|
}
|
|
7084
7986
|
async function runConsumerTokens(opts) {
|
|
7085
|
-
const creds =
|
|
7987
|
+
const creds = requireConsumer2();
|
|
7086
7988
|
const fresh = await validConsumerToken(creds) ?? creds;
|
|
7087
7989
|
const exp = (t) => {
|
|
7088
7990
|
const p = t ? decodeJwt2(t) : null;
|
|
@@ -7092,29 +7994,29 @@ async function runConsumerTokens(opts) {
|
|
|
7092
7994
|
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));
|
|
7093
7995
|
return;
|
|
7094
7996
|
}
|
|
7095
|
-
console.log(`${
|
|
7997
|
+
console.log(`${import_chalk44.default.cyan("Consumer")} ${import_chalk44.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk44.default.bold(fresh.tenant)}
|
|
7096
7998
|
`);
|
|
7097
|
-
console.log(`${
|
|
7999
|
+
console.log(`${import_chalk44.default.bold("access_token")} ${import_chalk44.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
7098
8000
|
${fresh.accessToken}
|
|
7099
8001
|
`);
|
|
7100
|
-
if (fresh.idToken) console.log(`${
|
|
8002
|
+
if (fresh.idToken) console.log(`${import_chalk44.default.bold("id_token")} ${import_chalk44.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
7101
8003
|
${fresh.idToken}
|
|
7102
8004
|
`);
|
|
7103
|
-
if (fresh.refreshToken) console.log(`${
|
|
8005
|
+
if (fresh.refreshToken) console.log(`${import_chalk44.default.bold("refresh_token")}
|
|
7104
8006
|
${fresh.refreshToken}
|
|
7105
8007
|
`);
|
|
7106
|
-
console.log(
|
|
8008
|
+
console.log(import_chalk44.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
7107
8009
|
}
|
|
7108
8010
|
async function runConsumerApikeys(opts) {
|
|
7109
|
-
const creds =
|
|
8011
|
+
const creds = requireConsumer2();
|
|
7110
8012
|
const { default: inquirer3 } = await import("inquirer");
|
|
7111
8013
|
const spinner = (0, import_ora22.default)("Loading your API keys...").start();
|
|
7112
8014
|
const list = await consumerFetch(creds, "/apikeys");
|
|
7113
8015
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
7114
8016
|
spinner.stop();
|
|
7115
8017
|
if (list.status >= 400) {
|
|
7116
|
-
console.error(
|
|
7117
|
-
if (list.status === 401) console.error(
|
|
8018
|
+
console.error(import_chalk44.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
8019
|
+
if (list.status === 401) console.error(import_chalk44.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
7118
8020
|
process.exit(1);
|
|
7119
8021
|
}
|
|
7120
8022
|
const keys = list.data?.keys ?? [];
|
|
@@ -7122,16 +8024,16 @@ async function runConsumerApikeys(opts) {
|
|
|
7122
8024
|
if (opts.json) {
|
|
7123
8025
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
7124
8026
|
} else if (!keys.length) {
|
|
7125
|
-
console.log(
|
|
8027
|
+
console.log(import_chalk44.default.yellow("No API keys yet."));
|
|
7126
8028
|
} else {
|
|
7127
8029
|
for (const k of keys) {
|
|
7128
8030
|
const clear = revealMap[k.environment]?.key;
|
|
7129
|
-
const shown = clear ?
|
|
7130
|
-
const exp = k.expires_at ?
|
|
7131
|
-
console.log(` ${
|
|
8031
|
+
const shown = clear ? import_chalk44.default.green(clear) : import_chalk44.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
8032
|
+
const exp = k.expires_at ? import_chalk44.default.dim(`exp ${k.expires_at}`) : import_chalk44.default.dim("no expiry");
|
|
8033
|
+
console.log(` ${import_chalk44.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk44.default.dim(k.description ?? "")}`);
|
|
7132
8034
|
}
|
|
7133
8035
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
7134
|
-
console.log(
|
|
8036
|
+
console.log(import_chalk44.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
7135
8037
|
}
|
|
7136
8038
|
}
|
|
7137
8039
|
if (opts.json) return;
|
|
@@ -7153,12 +8055,12 @@ async function runConsumerApikeys(opts) {
|
|
|
7153
8055
|
}
|
|
7154
8056
|
s2.succeed("Key created.");
|
|
7155
8057
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
7156
|
-
if (key) console.log(` ${
|
|
7157
|
-
else console.log(
|
|
8058
|
+
if (key) console.log(` ${import_chalk44.default.green(key)} ${import_chalk44.default.dim("(shown once \u2014 store it now)")}`);
|
|
8059
|
+
else console.log(import_chalk44.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
7158
8060
|
}
|
|
7159
8061
|
|
|
7160
8062
|
// src/commands/sidecar.ts
|
|
7161
|
-
var
|
|
8063
|
+
var import_chalk45 = __toESM(require("chalk"));
|
|
7162
8064
|
var import_ora23 = __toESM(require("ora"));
|
|
7163
8065
|
var fs10 = __toESM(require("fs"));
|
|
7164
8066
|
var path7 = __toESM(require("path"));
|
|
@@ -7201,7 +8103,7 @@ function upsertEnvLocal(root, token) {
|
|
|
7201
8103
|
}
|
|
7202
8104
|
function installSidecarPackage(root) {
|
|
7203
8105
|
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
7204
|
-
console.log(` ${
|
|
8106
|
+
console.log(` ${import_chalk45.default.green("\u2713")} apiblaze package already installed`);
|
|
7205
8107
|
return;
|
|
7206
8108
|
}
|
|
7207
8109
|
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
@@ -7212,7 +8114,7 @@ function installSidecarPackage(root) {
|
|
|
7212
8114
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
7213
8115
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
7214
8116
|
} catch {
|
|
7215
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
8117
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk45.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk45.default.cyan("npm run dev")}.`);
|
|
7216
8118
|
}
|
|
7217
8119
|
}
|
|
7218
8120
|
function readEnvKey(root) {
|
|
@@ -7363,29 +8265,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
7363
8265
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
7364
8266
|
const envState = upsertEnvLocal(root, out.token);
|
|
7365
8267
|
ensureGitignored(root);
|
|
7366
|
-
console.log(` ${
|
|
7367
|
-
console.log(` ${
|
|
8268
|
+
console.log(` ${import_chalk45.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
8269
|
+
console.log(` ${import_chalk45.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
7368
8270
|
installSidecarPackage(root);
|
|
7369
8271
|
let inspectorPath = null;
|
|
7370
8272
|
if (!opts.noInspector) {
|
|
7371
8273
|
inspectorPath = generateInspector(root, router);
|
|
7372
|
-
if (inspectorPath) console.log(` ${
|
|
8274
|
+
if (inspectorPath) console.log(` ${import_chalk45.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
7373
8275
|
}
|
|
7374
8276
|
console.log("");
|
|
7375
|
-
console.log(
|
|
7376
|
-
console.log(` 1. ${
|
|
8277
|
+
console.log(import_chalk45.default.bold("Done (no account needed). What happens next:"));
|
|
8278
|
+
console.log(` 1. ${import_chalk45.default.cyan("npm run dev")} and use your app.`);
|
|
7377
8279
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
7378
|
-
console.log(` ${
|
|
8280
|
+
console.log(` ${import_chalk45.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
7379
8281
|
console.log("");
|
|
7380
|
-
console.log(
|
|
7381
|
-
console.log(` ${
|
|
7382
|
-
console.log(
|
|
8282
|
+
console.log(import_chalk45.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
8283
|
+
console.log(` ${import_chalk45.default.cyan("apiblaze login")} then ${import_chalk45.default.cyan("apiblaze claim")} ${import_chalk45.default.dim("(no code needed here)")}`);
|
|
8284
|
+
console.log(import_chalk45.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
7383
8285
|
}
|
|
7384
8286
|
async function runSidecar(opts) {
|
|
7385
8287
|
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
7386
8288
|
const detected = detectNextProject(root);
|
|
7387
8289
|
if (!detected.found) {
|
|
7388
|
-
console.log(
|
|
8290
|
+
console.log(import_chalk45.default.yellow(`No Next.js project detected in ${root}.`));
|
|
7389
8291
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
7390
8292
|
return;
|
|
7391
8293
|
}
|
|
@@ -7396,10 +8298,10 @@ async function runSidecar(opts) {
|
|
|
7396
8298
|
if (!loadCredentials()) {
|
|
7397
8299
|
upsertEnvLocal(root, readEnvKey(root));
|
|
7398
8300
|
ensureGitignored(root);
|
|
7399
|
-
console.log(` ${
|
|
7400
|
-
console.log(` ${
|
|
8301
|
+
console.log(` ${import_chalk45.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
8302
|
+
console.log(` ${import_chalk45.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
7401
8303
|
installSidecarPackage(root);
|
|
7402
|
-
console.log(
|
|
8304
|
+
console.log(import_chalk45.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
7403
8305
|
return;
|
|
7404
8306
|
}
|
|
7405
8307
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -7422,38 +8324,38 @@ async function runSidecar(opts) {
|
|
|
7422
8324
|
throw err;
|
|
7423
8325
|
}
|
|
7424
8326
|
} else {
|
|
7425
|
-
console.log(
|
|
8327
|
+
console.log(import_chalk45.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
7426
8328
|
}
|
|
7427
8329
|
const envState = upsertEnvLocal(root, token);
|
|
7428
8330
|
ensureGitignored(root);
|
|
7429
|
-
console.log(` ${
|
|
8331
|
+
console.log(` ${import_chalk45.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
7430
8332
|
const wireState = wireInstrumentation(root);
|
|
7431
|
-
console.log(` ${
|
|
8333
|
+
console.log(` ${import_chalk45.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
7432
8334
|
installSidecarPackage(root);
|
|
7433
8335
|
let inspectorPath = null;
|
|
7434
8336
|
if (!opts.noInspector) {
|
|
7435
8337
|
inspectorPath = generateInspector(root, detected.router);
|
|
7436
|
-
if (inspectorPath) console.log(` ${
|
|
8338
|
+
if (inspectorPath) console.log(` ${import_chalk45.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
7437
8339
|
}
|
|
7438
8340
|
console.log("");
|
|
7439
|
-
console.log(
|
|
7440
|
-
console.log(` 1. ${
|
|
7441
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
7442
|
-
console.log(` 3. Approve the ones to route: ${
|
|
8341
|
+
console.log(import_chalk45.default.bold("Done. What happens next:"));
|
|
8342
|
+
console.log(` 1. ${import_chalk45.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
8343
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk45.default.bold("candidates")} \u2014 list them: ${import_chalk45.default.cyan("apiblaze sidecar")}`);
|
|
8344
|
+
console.log(` 3. Approve the ones to route: ${import_chalk45.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
7443
8345
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
7444
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
7445
|
-
if (switchingTeam) console.log(
|
|
8346
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk45.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
|
|
8347
|
+
if (switchingTeam) console.log(import_chalk45.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
7446
8348
|
console.log("");
|
|
7447
|
-
console.log(
|
|
7448
|
-
console.log(
|
|
7449
|
-
console.log(
|
|
8349
|
+
console.log(import_chalk45.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
8350
|
+
console.log(import_chalk45.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
8351
|
+
console.log(import_chalk45.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
7450
8352
|
console.log("");
|
|
7451
|
-
console.log(
|
|
7452
|
-
console.log(
|
|
8353
|
+
console.log(import_chalk45.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
8354
|
+
console.log(import_chalk45.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
7453
8355
|
}
|
|
7454
8356
|
|
|
7455
8357
|
// src/commands/origins.ts
|
|
7456
|
-
var
|
|
8358
|
+
var import_chalk46 = __toESM(require("chalk"));
|
|
7457
8359
|
var import_ora24 = __toESM(require("ora"));
|
|
7458
8360
|
init_admin();
|
|
7459
8361
|
init_resolve();
|
|
@@ -7464,7 +8366,7 @@ async function runOriginsList(opts) {
|
|
|
7464
8366
|
if (!loadCredentials()) {
|
|
7465
8367
|
const cred = loadAnonCred();
|
|
7466
8368
|
if (!cred) {
|
|
7467
|
-
console.log(
|
|
8369
|
+
console.log(import_chalk46.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
7468
8370
|
return;
|
|
7469
8371
|
}
|
|
7470
8372
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -7482,27 +8384,27 @@ async function runOriginsList(opts) {
|
|
|
7482
8384
|
}
|
|
7483
8385
|
const routed = out.routed ?? [];
|
|
7484
8386
|
const candidates = out.candidates ?? [];
|
|
7485
|
-
console.log(
|
|
8387
|
+
console.log(import_chalk46.default.bold(`
|
|
7486
8388
|
Routed through APIblaze (${routed.length})`));
|
|
7487
|
-
if (!routed.length) console.log(
|
|
7488
|
-
for (const r of routed) console.log(` ${
|
|
7489
|
-
console.log(
|
|
8389
|
+
if (!routed.length) console.log(import_chalk46.default.dim(" none yet"));
|
|
8390
|
+
for (const r of routed) console.log(` ${import_chalk46.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk46.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
8391
|
+
console.log(import_chalk46.default.bold(`
|
|
7490
8392
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
7491
|
-
if (!candidates.length) console.log(
|
|
8393
|
+
if (!candidates.length) console.log(import_chalk46.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
7492
8394
|
for (const c of candidates) {
|
|
7493
|
-
console.log(` ${
|
|
8395
|
+
console.log(` ${import_chalk46.default.yellow("\u25CB")} ${c.origin} ${import_chalk46.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
7494
8396
|
}
|
|
7495
8397
|
if (candidates.length) {
|
|
7496
|
-
console.log(
|
|
8398
|
+
console.log(import_chalk46.default.dim(`
|
|
7497
8399
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
7498
|
-
console.log(
|
|
8400
|
+
console.log(import_chalk46.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
7499
8401
|
}
|
|
7500
8402
|
}
|
|
7501
8403
|
async function runOriginsApprove(origin, opts) {
|
|
7502
8404
|
if (!loadCredentials()) {
|
|
7503
8405
|
const cred = loadAnonCred();
|
|
7504
8406
|
if (!cred) {
|
|
7505
|
-
console.error(
|
|
8407
|
+
console.error(import_chalk46.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
7506
8408
|
process.exit(1);
|
|
7507
8409
|
}
|
|
7508
8410
|
const spinner2 = (0, import_ora24.default)(`Approving ${origin} (anonymous)...`).start();
|
|
@@ -7555,7 +8457,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
7555
8457
|
}
|
|
7556
8458
|
|
|
7557
8459
|
// src/commands/op.ts
|
|
7558
|
-
var
|
|
8460
|
+
var import_chalk47 = __toESM(require("chalk"));
|
|
7559
8461
|
init_auth();
|
|
7560
8462
|
init_trace();
|
|
7561
8463
|
init_types();
|
|
@@ -7588,82 +8490,82 @@ function printResidue(report, applied) {
|
|
|
7588
8490
|
const up = report?.upstash ?? {};
|
|
7589
8491
|
const fga = report?.fga ?? {};
|
|
7590
8492
|
const ghosts = report?.ghosts ?? {};
|
|
7591
|
-
console.log(
|
|
7592
|
-
console.log(
|
|
8493
|
+
console.log(import_chalk47.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
8494
|
+
console.log(import_chalk47.default.bold("\n Upstash"));
|
|
7593
8495
|
const orphans = up.orphans ?? [];
|
|
7594
|
-
if (orphans.length === 0) console.log(
|
|
7595
|
-
for (const o of orphans) console.log(` ${
|
|
7596
|
-
console.log(
|
|
8496
|
+
if (orphans.length === 0) console.log(import_chalk47.default.green(" no orphaned keys"));
|
|
8497
|
+
for (const o of orphans) console.log(` ${import_chalk47.default.yellow(o.key)} ${import_chalk47.default.dim(`\u2014 ${o.reason}`)}`);
|
|
8498
|
+
console.log(import_chalk47.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
7597
8499
|
if (up.anon_wallet_detail) {
|
|
7598
8500
|
const d = up.anon_wallet_detail;
|
|
7599
|
-
console.log(
|
|
8501
|
+
console.log(import_chalk47.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)"}`));
|
|
7600
8502
|
}
|
|
7601
8503
|
if (up.keyspace_census) {
|
|
7602
8504
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
7603
|
-
console.log(
|
|
8505
|
+
console.log(import_chalk47.default.dim(` keyspace: ${census}`));
|
|
7604
8506
|
}
|
|
7605
|
-
if (up.unknown?.length) console.log(
|
|
7606
|
-
if (applied) console.log(` ${
|
|
7607
|
-
for (const e of up.errors ?? []) console.log(
|
|
7608
|
-
console.log(
|
|
8507
|
+
if (up.unknown?.length) console.log(import_chalk47.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
8508
|
+
if (applied) console.log(` ${import_chalk47.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
8509
|
+
for (const e of up.errors ?? []) console.log(import_chalk47.default.red(` error: ${e}`));
|
|
8510
|
+
console.log(import_chalk47.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
7609
8511
|
if (applied) {
|
|
7610
8512
|
const swept = fga?.swept ?? [];
|
|
7611
|
-
if (swept.length === 0) console.log(
|
|
8513
|
+
if (swept.length === 0) console.log(import_chalk47.default.green(" no orphaned stores"));
|
|
7612
8514
|
for (const s of swept) {
|
|
7613
8515
|
console.log(
|
|
7614
|
-
` ${
|
|
8516
|
+
` ${import_chalk47.default.yellow(s.store_id)} ${import_chalk47.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
7615
8517
|
);
|
|
7616
8518
|
}
|
|
7617
|
-
if (fga?.remaining) console.log(
|
|
8519
|
+
if (fga?.remaining) console.log(import_chalk47.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
7618
8520
|
const st = fga?.side_tables;
|
|
7619
|
-
if (st) console.log(
|
|
8521
|
+
if (st) console.log(import_chalk47.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})` : ""}`));
|
|
7620
8522
|
} else {
|
|
7621
8523
|
const fgaOrphans = fga?.orphans ?? [];
|
|
7622
|
-
if (fgaOrphans.length === 0) console.log(
|
|
8524
|
+
if (fgaOrphans.length === 0) console.log(import_chalk47.default.green(" no orphaned stores"));
|
|
7623
8525
|
for (const s of fgaOrphans) {
|
|
7624
8526
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
7625
|
-
console.log(` ${
|
|
8527
|
+
console.log(` ${import_chalk47.default.yellow(s.store_id)} ${import_chalk47.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
7626
8528
|
}
|
|
7627
|
-
console.log(
|
|
8529
|
+
console.log(import_chalk47.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
7628
8530
|
const st = fga?.side_tables;
|
|
7629
|
-
if (st) console.log(
|
|
8531
|
+
if (st) console.log(import_chalk47.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`));
|
|
7630
8532
|
}
|
|
7631
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
7632
|
-
console.log(
|
|
8533
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk47.default.red(` error: ${e}`));
|
|
8534
|
+
console.log(import_chalk47.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
7633
8535
|
if (applied) {
|
|
7634
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
7635
|
-
else console.log(` ${
|
|
8536
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk47.default.green(" no ghost tuples"));
|
|
8537
|
+
else console.log(` ${import_chalk47.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk47.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
7636
8538
|
} else {
|
|
7637
8539
|
const n = ghosts?.ghost_count ?? 0;
|
|
7638
|
-
if (n === 0) console.log(
|
|
8540
|
+
if (n === 0) console.log(import_chalk47.default.green(` no ghost tuples ${import_chalk47.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
7639
8541
|
else {
|
|
7640
|
-
console.log(
|
|
8542
|
+
console.log(import_chalk47.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
7641
8543
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
7642
|
-
console.log(
|
|
8544
|
+
console.log(import_chalk47.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
7643
8545
|
}
|
|
7644
|
-
if (n > 20) console.log(
|
|
8546
|
+
if (n > 20) console.log(import_chalk47.default.dim(` \u2026 and ${n - 20} more`));
|
|
7645
8547
|
}
|
|
7646
8548
|
}
|
|
7647
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
8549
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk47.default.red(` error: ${e}`));
|
|
7648
8550
|
console.log();
|
|
7649
8551
|
}
|
|
7650
8552
|
async function runOp(sub, opts = {}) {
|
|
7651
8553
|
if (!loadCredentials()) {
|
|
7652
|
-
console.log(
|
|
8554
|
+
console.log(import_chalk47.default.dim("Not logged in. Run `apiblaze login`."));
|
|
7653
8555
|
return;
|
|
7654
8556
|
}
|
|
7655
8557
|
if (!isOperatorLogin()) {
|
|
7656
|
-
console.log(
|
|
8558
|
+
console.log(import_chalk47.default.dim("`apiblaze op` is only available to platform operators."));
|
|
7657
8559
|
return;
|
|
7658
8560
|
}
|
|
7659
8561
|
switch (sub) {
|
|
7660
8562
|
case void 0:
|
|
7661
8563
|
case "menu": {
|
|
7662
|
-
console.log(
|
|
7663
|
-
console.log(` ${
|
|
7664
|
-
console.log(` ${
|
|
7665
|
-
console.log(` ${
|
|
7666
|
-
console.log(
|
|
8564
|
+
console.log(import_chalk47.default.bold("\nOperator menu"));
|
|
8565
|
+
console.log(` ${import_chalk47.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
8566
|
+
console.log(` ${import_chalk47.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk47.default.dim("-y to skip")})`);
|
|
8567
|
+
console.log(` ${import_chalk47.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
8568
|
+
console.log(import_chalk47.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
7667
8569
|
`));
|
|
7668
8570
|
return;
|
|
7669
8571
|
}
|
|
@@ -7682,17 +8584,17 @@ async function runOp(sub, opts = {}) {
|
|
|
7682
8584
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
7683
8585
|
printResidue(report, false);
|
|
7684
8586
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
7685
|
-
console.log(
|
|
8587
|
+
console.log(import_chalk47.default.green("Nothing to sweep."));
|
|
7686
8588
|
return;
|
|
7687
8589
|
}
|
|
7688
8590
|
if (!opts.yes) {
|
|
7689
|
-
const
|
|
7690
|
-
const rl =
|
|
8591
|
+
const readline3 = await import("readline/promises");
|
|
8592
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
7691
8593
|
const answer = await rl.question(
|
|
7692
|
-
|
|
8594
|
+
import_chalk47.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: `)
|
|
7693
8595
|
);
|
|
7694
8596
|
rl.close();
|
|
7695
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
8597
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk47.default.dim("Aborted."));
|
|
7696
8598
|
}
|
|
7697
8599
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
7698
8600
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -7703,15 +8605,15 @@ async function runOp(sub, opts = {}) {
|
|
|
7703
8605
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
7704
8606
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
7705
8607
|
const accounts = data?.accounts ?? [];
|
|
7706
|
-
if (accounts.length === 0) return void console.log(
|
|
8608
|
+
if (accounts.length === 0) return void console.log(import_chalk47.default.dim("No credit wallets."));
|
|
7707
8609
|
for (const a of accounts) {
|
|
7708
8610
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
7709
|
-
console.log(` ${
|
|
8611
|
+
console.log(` ${import_chalk47.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk47.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk47.default.dim(" \u2014 anon") : ""}`);
|
|
7710
8612
|
}
|
|
7711
8613
|
return;
|
|
7712
8614
|
}
|
|
7713
8615
|
default:
|
|
7714
|
-
console.log(
|
|
8616
|
+
console.log(import_chalk47.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
7715
8617
|
}
|
|
7716
8618
|
}
|
|
7717
8619
|
|
|
@@ -7766,15 +8668,15 @@ var runSetup = (opts) => runSidecar({ team: opts.team, dir: opts.dir, yes: opts.
|
|
|
7766
8668
|
var withSetupOptions = (cmd) => cmd.option("--team <id|name>", "Team to set up under (defaults to your active team)").option("--dir <path>", "Project directory (defaults to cwd)").option("--no-inspector", "Skip generating the dev-only /abz-inspector page").option("--rotate", "Mint a fresh invoke key even if one already exists").option("--new-session", "Start a fresh anonymous session (logged-out only)").option("-y, --yes", "Skip prompts");
|
|
7767
8669
|
withSetupOptions(program.command("init").description("Set up the APIblaze sidecar in a Next.js app (shortcut for `apiblaze sidecar setup`)")).action(action((opts) => runSetup(opts)));
|
|
7768
8670
|
var sidecar = program.command("sidecar").description("The APIblaze sidecar \u2014 set it up, then approve which origins route through APIblaze").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Machine-readable output").action(action((opts) => runOriginsList(opts)));
|
|
7769
|
-
withSetupOptions(sidecar.command("setup").description("Wire a Next.js app to route its external fetches through APIblaze")).action(action((opts) => runSetup(opts)));
|
|
7770
|
-
sidecar.command("approve").description("Route an origin through APIblaze (creates its proxy)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Machine-readable output").action(action((origin, opts) => runOriginsApprove(origin, opts)));
|
|
7771
|
-
sidecar.command("deny").description("Dismiss a candidate origin so it stops being suggested").argument("<origin>", "Origin, e.g. sentry.io").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts) => runOriginsDeny(origin, opts)));
|
|
7772
|
-
sidecar.command("remove").description("Un-route an approved origin (deletes its proxy; the app goes direct again)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts) => runOriginsRemove(origin, opts)));
|
|
8671
|
+
withSetupOptions(sidecar.command("setup").description("Wire a Next.js app to route its external fetches through APIblaze")).action(action((opts, cmd) => runSetup({ ...cmd.parent?.opts(), ...opts })));
|
|
8672
|
+
sidecar.command("approve").description("Route an origin through APIblaze (creates its proxy)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Machine-readable output").action(action((origin, opts, cmd) => runOriginsApprove(origin, { ...cmd.parent?.opts(), ...opts })));
|
|
8673
|
+
sidecar.command("deny").description("Dismiss a candidate origin so it stops being suggested").argument("<origin>", "Origin, e.g. sentry.io").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts, cmd) => runOriginsDeny(origin, { ...cmd.parent?.opts(), ...opts })));
|
|
8674
|
+
sidecar.command("remove").description("Un-route an approved origin (deletes its proxy; the app goes direct again)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts, cmd) => runOriginsRemove(origin, { ...cmd.parent?.opts(), ...opts })));
|
|
7773
8675
|
program.command("dev").description("Put your localhost behind a public URL (dev tunnel)").argument("[port]", "Local port to tunnel (positional; overrides --port)").option("-p, --port <number>", "Local port to tunnel", "3000").option("--project <nameOrId>", "Tunnel this specific project (skips the picker \u2014 for scripts)").option("-y, --yes", "Skip confirmation prompts (non-interactive)").option("-o, --capture-file <path>", "Stream full request/response traffic to a file (JSON lines)").option("--new-session", "Logged-out only: start a fresh anonymous workspace instead of reusing this machine's (each run = a throwaway proxy)").action(async (port, opts) => {
|
|
7774
8676
|
try {
|
|
7775
8677
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
7776
8678
|
if (Number.isNaN(resolved)) {
|
|
7777
|
-
console.error(
|
|
8679
|
+
console.error(import_chalk48.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
7778
8680
|
process.exit(1);
|
|
7779
8681
|
}
|
|
7780
8682
|
await runDev({ port: resolved, project: opts.project, yes: opts.yes, captureFile: opts.captureFile, newSession: opts.newSession });
|
|
@@ -7787,6 +8689,8 @@ var consumer = program.command("consumer").description("Act as a consumer of you
|
|
|
7787
8689
|
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)));
|
|
7788
8690
|
consumer.command("tokens").description("Show the logged-in consumer access / refresh / id tokens").option("--json", "Output machine-readable JSON").action(action((opts) => runConsumerTokens(opts)));
|
|
7789
8691
|
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)));
|
|
8692
|
+
var consumerLogs = consumer.command("logs").description("API Consumer view \u2014 stream YOUR OWN requests on this tenant live (see `consumer logs list` for past days)").option("--project <name>", "Only this project (when the tenant serves several)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit raw NDJSON rows (pipe into jq)").action(action((opts) => runConsumerLogsTail(opts)));
|
|
8693
|
+
consumerLogs.command("list").description("API Consumer view \u2014 browse YOUR OWN past requests by day, then open one for detail").option("--project <name>", "Only this project (when the tenant serves several)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit ALL matching rows as NDJSON, newest first (non-interactive)").option("--plain", "Print day-grouped rows instead of prompting (automatic when piped)").action(action((_o, cmd) => runConsumerLogsList(cmd.optsWithGlobals())));
|
|
7790
8694
|
program.command("flush").description("Wipe ALL local state (login, workspace keys, apichats, DP keys, secrets)").option("-y, --yes", "Skip the confirmation prompt").action(runFlush);
|
|
7791
8695
|
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)));
|
|
7792
8696
|
program.command("whoami").description("Show who you are \u2014 both API Producer and API Consumer").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
@@ -7823,6 +8727,8 @@ program.command("projects").description("List the projects in your team").action
|
|
|
7823
8727
|
});
|
|
7824
8728
|
program.command("delete").description("Delete a proxy and everything under it (asks first)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--team <id|name>", "Team the project is in (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON").action(action((project, version2, opts) => runDelete(project, version2, opts)));
|
|
7825
8729
|
program.command("export").description("Export config and data for migration out of APIblaze (Kong, ...)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--kong", "Produce a runnable Kong OSS bundle (decK config + plugins + docker-compose)").option("--data", "Plain data export (default)").option("--secrets", "Include decrypted producer-supplied secrets (member role; audit-logged)").option("--keys <mode>", "API-key export: hashes (default, consumers keep keys) | mint | none").option("--no-consumers", "Skip the end-user lane (users, groups, keys)").option("-o, --out <file>", "Output zip path").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, opts) => runExport(project, version2, { ...opts, noConsumers: opts.consumers === false })));
|
|
8730
|
+
var logs = program.command("logs").description("API Producer view \u2014 stream this proxy's requests from EVERY caller live (see `apiblaze logs list` to browse past days)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--tenant <slug>", "Tenant to tail (auto when one is attached; prompted otherwise)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit raw NDJSON rows (pipe into jq)").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, opts) => runLogsTail(project, version2, opts)));
|
|
8731
|
+
logs.command("list").description("API Producer view \u2014 browse EVERY caller's past requests by day, then open one to see its 4 views").argument("<project>", "Project name or id").argument("[version]", "API version (defaults to the first match)").option("--tenant <slug>", "Tenant to read (auto when one is attached; prompted otherwise)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit ALL matching rows as NDJSON, newest first (non-interactive)").option("--plain", "Print day-grouped rows instead of prompting (automatic when piped)").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, _o, cmd) => runLogsList(project, version2, cmd.optsWithGlobals())));
|
|
7826
8732
|
program.command("config").description("Browse and change every proxy setting & feature (interactive; git-config-style get/set)").argument("[project]", "Project name or id (omit to pick interactively)").argument("[key]", "Setting key, e.g. throttling.proxyQuota (omit for the menu)").argument("[value]", "New value (omit to read the current value)").option("--list", "Print every setting and its current value").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Machine-readable output (with --list or a key read)").action(action((project, key, value, opts) => runConfig(project, key, value, opts)));
|
|
7827
8733
|
program.command("target").description("Change where a proxy forwards requests").argument("<project>", "Project name or id").requiredOption("--url <url>", "Target URL to forward to").option("--env <env>", "Environment to scope the target to (e.g. prod, dev)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTargetSet(project, opts)));
|
|
7828
8734
|
program.command("throttle").description("Set rate limits and quotas for a proxy").argument("<project>", "Project name or id").option("--rate <n>", "User rate limit (requests/sec)").option("--end-user-rate <n>", "Per-end-user rate limit (requests/sec)").option("--quota <n>", "Proxy quota (requests/period)").option("--period <p>", "Quota period: daily | weekly | monthly").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runThrottleSet(project, opts)));
|
|
@@ -7874,10 +8780,11 @@ spec.command("delete-rule").description("Delete the saved rules for a route (e.g
|
|
|
7874
8780
|
var HELP_GROUPS = [
|
|
7875
8781
|
{ title: "Chat", commands: ["apichat", "agent"] },
|
|
7876
8782
|
{ title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
|
|
7877
|
-
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
8783
|
+
{ title: "Control plane commands", commands: ["config", "projects", "logs", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
7878
8784
|
{ title: "Data plane commands", commands: [
|
|
7879
8785
|
{ parent: "consumer", sub: "login" },
|
|
7880
|
-
{ parent: "consumer", sub: "apikeys" }
|
|
8786
|
+
{ parent: "consumer", sub: "apikeys" },
|
|
8787
|
+
{ parent: "consumer", sub: "logs" }
|
|
7881
8788
|
] }
|
|
7882
8789
|
];
|
|
7883
8790
|
var helpLabel = (e) => typeof e === "string" ? e : `${e.parent} ${e.sub}`;
|
|
@@ -7893,7 +8800,7 @@ function groupedCommandHelp() {
|
|
|
7893
8800
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
7894
8801
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
7895
8802
|
}).filter(Boolean).join("\n");
|
|
7896
|
-
return `${
|
|
8803
|
+
return `${import_chalk48.default.bold(g.title)}
|
|
7897
8804
|
${rows}`;
|
|
7898
8805
|
}).join("\n\n");
|
|
7899
8806
|
}
|
|
@@ -7928,14 +8835,14 @@ async function recoverStaleTeam() {
|
|
|
7928
8835
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
7929
8836
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
7930
8837
|
if (!linked) {
|
|
7931
|
-
console.error(
|
|
8838
|
+
console.error(import_chalk48.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
7932
8839
|
return;
|
|
7933
8840
|
}
|
|
7934
8841
|
if (linked.teamId === creds.teamId) return;
|
|
7935
8842
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
7936
8843
|
delete next.activeTenant;
|
|
7937
8844
|
saveCredentials(next);
|
|
7938
|
-
console.error(
|
|
8845
|
+
console.error(import_chalk48.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk48.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
7939
8846
|
} catch {
|
|
7940
8847
|
}
|
|
7941
8848
|
}
|
|
@@ -7943,16 +8850,16 @@ async function printError(err) {
|
|
|
7943
8850
|
if (err instanceof ApiError) {
|
|
7944
8851
|
const data = err.body;
|
|
7945
8852
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
7946
|
-
console.error(
|
|
8853
|
+
console.error(import_chalk48.default.red(`
|
|
7947
8854
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
7948
8855
|
if (err.status === 403 || err.status === 404) {
|
|
7949
8856
|
await recoverStaleTeam();
|
|
7950
8857
|
}
|
|
7951
8858
|
} else if (err instanceof Error) {
|
|
7952
|
-
console.error(
|
|
8859
|
+
console.error(import_chalk48.default.red(`
|
|
7953
8860
|
Error: ${err.message}`));
|
|
7954
8861
|
} else {
|
|
7955
|
-
console.error(
|
|
8862
|
+
console.error(import_chalk48.default.red("\nUnknown error"));
|
|
7956
8863
|
}
|
|
7957
8864
|
}
|
|
7958
8865
|
program.parse(process.argv);
|