apiblaze 0.18.2 → 0.18.6
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 +575 -506
- package/dist/react/index.js +377 -178
- package/dist/react/index.mjs +377 -178
- package/dist/server/index.d.mts +29 -0
- package/dist/server/index.d.ts +29 -0
- package/dist/server/index.js +38 -8
- package/dist/server/index.mjs +38 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -485,7 +485,7 @@ async function createTenantRow(teamId) {
|
|
|
485
485
|
const { name } = await inquirer3.prompt([{
|
|
486
486
|
type: "input",
|
|
487
487
|
name: "name",
|
|
488
|
-
message: `Tenant name ${
|
|
488
|
+
message: `Tenant name ${import_chalk25.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
|
|
489
489
|
validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
|
|
490
490
|
}]);
|
|
491
491
|
const slug = name.trim();
|
|
@@ -500,7 +500,7 @@ async function createTenantRow(teamId) {
|
|
|
500
500
|
} catch (err) {
|
|
501
501
|
const msg = err instanceof Error ? err.message : String(err);
|
|
502
502
|
if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
|
|
503
|
-
console.log(
|
|
503
|
+
console.log(import_chalk25.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
|
|
504
504
|
continue;
|
|
505
505
|
}
|
|
506
506
|
throw err;
|
|
@@ -517,11 +517,11 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
517
517
|
pageSize: 10,
|
|
518
518
|
choices: [
|
|
519
519
|
...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
|
|
520
|
-
...opts.allowSkip ? [new inquirer3.Separator(), { name:
|
|
520
|
+
...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk25.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
|
|
521
521
|
]
|
|
522
522
|
}]);
|
|
523
523
|
if (!provider) {
|
|
524
|
-
console.log(
|
|
524
|
+
console.log(import_chalk25.default.dim(" No login method yet \u2014 add one later under Login methods."));
|
|
525
525
|
return false;
|
|
526
526
|
}
|
|
527
527
|
const opt = PROVIDERS.find((p) => p.id === provider);
|
|
@@ -552,7 +552,7 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
552
552
|
if (a.callbacks.trim()) clientBody.authorizedCallbackUrls = a.callbacks.split(/\s*,\s*/).map((s) => s.trim()).filter(Boolean);
|
|
553
553
|
if (a.scopes.trim() && opt.own) providerBody.scopes = a.scopes.split(/\s+/).filter(Boolean);
|
|
554
554
|
}
|
|
555
|
-
const spinner = (0,
|
|
555
|
+
const spinner = (0, import_ora9.default)("Setting up login...").start();
|
|
556
556
|
try {
|
|
557
557
|
const client = await admin({ method: "POST", path: `${base}/app-clients`, body: clientBody, summary: `Create login for ${tenant2}` });
|
|
558
558
|
const clientId = client?.clientId ?? client?.client_id;
|
|
@@ -567,27 +567,27 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
567
567
|
async function createTenantInteractive(teamId) {
|
|
568
568
|
const { default: inquirer3 } = await import("inquirer");
|
|
569
569
|
const tenant2 = await createTenantRow(teamId);
|
|
570
|
-
console.log(
|
|
570
|
+
console.log(import_chalk25.default.green(` Tenant ${import_chalk25.default.bold(tenant2)} created.`));
|
|
571
571
|
const { users } = await inquirer3.prompt([{
|
|
572
572
|
type: "confirm",
|
|
573
573
|
name: "users",
|
|
574
574
|
default: false,
|
|
575
|
-
message: `Enable Users & groups? ${
|
|
575
|
+
message: `Enable Users & groups? ${import_chalk25.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
|
|
576
576
|
}]);
|
|
577
577
|
if (users) {
|
|
578
578
|
await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
|
|
579
579
|
});
|
|
580
|
-
console.log(
|
|
580
|
+
console.log(import_chalk25.default.green(" Users & groups enabled."));
|
|
581
581
|
}
|
|
582
582
|
await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
|
|
583
583
|
return tenant2;
|
|
584
584
|
}
|
|
585
|
-
var
|
|
585
|
+
var import_chalk25, import_ora9, PROVIDERS, DEFAULT_SCOPES;
|
|
586
586
|
var init_tenant_create = __esm({
|
|
587
587
|
"src/lib/tenant-create.ts"() {
|
|
588
588
|
"use strict";
|
|
589
|
-
|
|
590
|
-
|
|
589
|
+
import_chalk25 = __toESM(require("chalk"));
|
|
590
|
+
import_ora9 = __toESM(require("ora"));
|
|
591
591
|
init_admin();
|
|
592
592
|
PROVIDERS = [
|
|
593
593
|
{ id: "apiblaze", label: "APIblaze (via GitHub) \u2014 zero setup, recommended", own: false },
|
|
@@ -625,10 +625,10 @@ async function fetchPage(teamId, q) {
|
|
|
625
625
|
}
|
|
626
626
|
function label(t, defaultTenant, active) {
|
|
627
627
|
const tags = [
|
|
628
|
-
t.tenant_name === active ?
|
|
629
|
-
t.tenant_name === defaultTenant ?
|
|
628
|
+
t.tenant_name === active ? import_chalk26.default.cyan("active scope") : "",
|
|
629
|
+
t.tenant_name === defaultTenant ? import_chalk26.default.dim("team default") : ""
|
|
630
630
|
].filter(Boolean).join(", ");
|
|
631
|
-
const disp = t.display_name && t.display_name !== t.tenant_name ?
|
|
631
|
+
const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk26.default.dim(` ${t.display_name}`) : "";
|
|
632
632
|
return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
|
|
633
633
|
}
|
|
634
634
|
async function pickTenant(teamId, opts = {}) {
|
|
@@ -636,14 +636,14 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
636
636
|
const active = loadCredentials()?.activeTenant;
|
|
637
637
|
let q = opts.initialQuery ?? "";
|
|
638
638
|
for (; ; ) {
|
|
639
|
-
const spinner = (0,
|
|
639
|
+
const spinner = (0, import_ora10.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
|
|
640
640
|
const page = await fetchPage(teamId, q).finally(() => spinner.stop());
|
|
641
641
|
if (!page.total && !q) {
|
|
642
642
|
if (opts.allowCreate) {
|
|
643
643
|
const { make } = await inquirer3.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
|
|
644
644
|
if (make) return await createTenantInline(teamId);
|
|
645
645
|
}
|
|
646
|
-
console.error(
|
|
646
|
+
console.error(import_chalk26.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
647
647
|
return null;
|
|
648
648
|
}
|
|
649
649
|
const truncated = page.total > page.rows.length;
|
|
@@ -652,7 +652,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
652
652
|
value: t.tenant_name
|
|
653
653
|
}));
|
|
654
654
|
if (truncated || q) {
|
|
655
|
-
choices.push(new inquirer3.Separator(
|
|
655
|
+
choices.push(new inquirer3.Separator(import_chalk26.default.dim(
|
|
656
656
|
truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
|
|
657
657
|
)));
|
|
658
658
|
choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
|
|
@@ -686,12 +686,12 @@ async function createTenantInline(teamId) {
|
|
|
686
686
|
const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
687
687
|
return createTenantInteractive2(teamId);
|
|
688
688
|
}
|
|
689
|
-
var
|
|
689
|
+
var import_chalk26, import_ora10, PAGE;
|
|
690
690
|
var init_tenant_pick = __esm({
|
|
691
691
|
"src/lib/tenant-pick.ts"() {
|
|
692
692
|
"use strict";
|
|
693
|
-
|
|
694
|
-
|
|
693
|
+
import_chalk26 = __toESM(require("chalk"));
|
|
694
|
+
import_ora10 = __toESM(require("ora"));
|
|
695
695
|
init_admin();
|
|
696
696
|
init_auth();
|
|
697
697
|
PAGE = 15;
|
|
@@ -700,10 +700,10 @@ var init_tenant_pick = __esm({
|
|
|
700
700
|
|
|
701
701
|
// src/index.ts
|
|
702
702
|
var import_commander = require("commander");
|
|
703
|
-
var
|
|
703
|
+
var import_chalk41 = __toESM(require("chalk"));
|
|
704
704
|
|
|
705
705
|
// package.json
|
|
706
|
-
var version = "0.18.
|
|
706
|
+
var version = "0.18.6";
|
|
707
707
|
|
|
708
708
|
// src/index.ts
|
|
709
709
|
init_types();
|
|
@@ -2331,7 +2331,7 @@ async function runTeam(arg) {
|
|
|
2331
2331
|
}
|
|
2332
2332
|
|
|
2333
2333
|
// src/commands/authz.ts
|
|
2334
|
-
var
|
|
2334
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
2335
2335
|
init_auth();
|
|
2336
2336
|
init_api();
|
|
2337
2337
|
|
|
@@ -2424,6 +2424,56 @@ async function runAgentChatRepl(opts) {
|
|
|
2424
2424
|
rl.close();
|
|
2425
2425
|
}
|
|
2426
2426
|
|
|
2427
|
+
// src/lib/authz-apply.ts
|
|
2428
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
2429
|
+
init_api();
|
|
2430
|
+
async function applyProposal(opts) {
|
|
2431
|
+
const log = opts.log ?? ((s) => console.log(s));
|
|
2432
|
+
const { projectId, apiVersion, tenant: tenant2, proposal, enable } = opts;
|
|
2433
|
+
if (!enable) {
|
|
2434
|
+
const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant2)}`, "POST", proposal.model);
|
|
2435
|
+
if (m.status === 404) {
|
|
2436
|
+
log(import_chalk15.default.red(" Authorization store not provisioned yet. Open the dashboard Authorization tab for this project once (it auto-provisions the store), then re-run.\n"));
|
|
2437
|
+
return { ok: 0, fail: 0, enabled: false, storeMissing: true };
|
|
2438
|
+
}
|
|
2439
|
+
if (m.status >= 400) {
|
|
2440
|
+
log(import_chalk15.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
|
|
2441
|
+
`));
|
|
2442
|
+
return { ok: 0, fail: 0, enabled: false };
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
let ok = 0;
|
|
2446
|
+
let fail5 = 0;
|
|
2447
|
+
for (const r of proposal.routes) {
|
|
2448
|
+
const resource = "/" + String(r.resource || "").replace(/^\/+/, "");
|
|
2449
|
+
const policiesBody = {
|
|
2450
|
+
rule_mode: r.rule_mode === "list" ? "list" : "check-write",
|
|
2451
|
+
on_request_read: Array.isArray(r.on_request_read) ? r.on_request_read : [],
|
|
2452
|
+
post_response_write: Array.isArray(r.post_response_write) ? r.post_response_write : [],
|
|
2453
|
+
list_objects_read: r.list_objects_read ?? null,
|
|
2454
|
+
authentication_config: { require_authentication: true },
|
|
2455
|
+
authorization_enabled: enable
|
|
2456
|
+
};
|
|
2457
|
+
const encoded = resource.replace(/\{/g, "%7B").replace(/\}/g, "%7D");
|
|
2458
|
+
const putPath = `/projects/${projectId}/${apiVersion}/policies/route/${r.method.toUpperCase()}${encoded}`;
|
|
2459
|
+
let res = await agentCall(putPath, "PUT", { ...policiesBody, resource });
|
|
2460
|
+
if (res.status === 404) {
|
|
2461
|
+
res = await agentCall(`/projects/${projectId}/${apiVersion}/policies/route`, "POST", { method: r.method.toUpperCase(), resource, ...policiesBody });
|
|
2462
|
+
}
|
|
2463
|
+
if (res.status >= 200 && res.status < 300) ok++;
|
|
2464
|
+
else fail5++;
|
|
2465
|
+
}
|
|
2466
|
+
if (enable) {
|
|
2467
|
+
const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant: tenant2 });
|
|
2468
|
+
if (e.status >= 400) {
|
|
2469
|
+
log(import_chalk15.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
|
|
2470
|
+
`));
|
|
2471
|
+
return { ok, fail: fail5, enabled: false };
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
return { ok, fail: fail5, enabled: enable };
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2427
2477
|
// src/commands/authz.ts
|
|
2428
2478
|
async function runAuthz(projectArg, apiVersionArg) {
|
|
2429
2479
|
const creds = loadCredentials();
|
|
@@ -2439,50 +2489,13 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
2439
2489
|
const tenant2 = match.tenant || projectId;
|
|
2440
2490
|
async function apply(ctx, enable) {
|
|
2441
2491
|
const proposal = ctx.proposal;
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
return;
|
|
2447
|
-
}
|
|
2448
|
-
if (m.status >= 400) {
|
|
2449
|
-
ctx.log(import_chalk15.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
|
|
2450
|
-
`));
|
|
2451
|
-
return;
|
|
2452
|
-
}
|
|
2453
|
-
}
|
|
2454
|
-
let ok = 0;
|
|
2455
|
-
let fail5 = 0;
|
|
2456
|
-
for (const r of proposal.routes) {
|
|
2457
|
-
const resource = "/" + String(r.resource || "").replace(/^\/+/, "");
|
|
2458
|
-
const policiesBody = {
|
|
2459
|
-
rule_mode: r.rule_mode === "list" ? "list" : "check-write",
|
|
2460
|
-
on_request_read: Array.isArray(r.on_request_read) ? r.on_request_read : [],
|
|
2461
|
-
post_response_write: Array.isArray(r.post_response_write) ? r.post_response_write : [],
|
|
2462
|
-
list_objects_read: r.list_objects_read ?? null,
|
|
2463
|
-
authentication_config: { require_authentication: true },
|
|
2464
|
-
authorization_enabled: enable
|
|
2465
|
-
};
|
|
2466
|
-
const encoded = resource.replace(/\{/g, "%7B").replace(/\}/g, "%7D");
|
|
2467
|
-
const putPath = `/projects/${projectId}/${apiVersion}/policies/route/${r.method.toUpperCase()}${encoded}`;
|
|
2468
|
-
let res = await agentCall(putPath, "PUT", { ...policiesBody, resource });
|
|
2469
|
-
if (res.status === 404) {
|
|
2470
|
-
res = await agentCall(`/projects/${projectId}/${apiVersion}/policies/route`, "POST", { method: r.method.toUpperCase(), resource, ...policiesBody });
|
|
2471
|
-
}
|
|
2472
|
-
if (res.status >= 200 && res.status < 300) ok++;
|
|
2473
|
-
else fail5++;
|
|
2474
|
-
}
|
|
2475
|
-
if (enable) {
|
|
2476
|
-
const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant: tenant2 });
|
|
2477
|
-
if (e.status >= 400) {
|
|
2478
|
-
ctx.log(import_chalk15.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
|
|
2492
|
+
const r = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable, log: ctx.log });
|
|
2493
|
+
if (r.storeMissing) return;
|
|
2494
|
+
if (enable && r.enabled) {
|
|
2495
|
+
ctx.log(import_chalk16.default.green(` \u2713 Enforcement ON \u2014 ${r.ok} route(s) + project switch on${r.fail ? `, ${r.fail} failed` : ""}.
|
|
2479
2496
|
`));
|
|
2480
|
-
|
|
2481
|
-
}
|
|
2482
|
-
ctx.log(import_chalk15.default.green(` \u2713 Enforcement ON \u2014 ${ok} route(s) + project switch on${fail5 ? `, ${fail5} failed` : ""}.
|
|
2483
|
-
`));
|
|
2484
|
-
} else {
|
|
2485
|
-
ctx.log(import_chalk15.default.green(` \u2713 Published (shadow): model + ${ok} route(s)${fail5 ? `, ${fail5} failed` : ""}. Review, then /enable.
|
|
2497
|
+
} else if (!enable) {
|
|
2498
|
+
ctx.log(import_chalk16.default.green(` \u2713 Published (shadow): model + ${r.ok} route(s)${r.fail ? `, ${r.fail} failed` : ""}. Review, then /enable.
|
|
2486
2499
|
`));
|
|
2487
2500
|
}
|
|
2488
2501
|
}
|
|
@@ -2506,8 +2519,63 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
2506
2519
|
});
|
|
2507
2520
|
}
|
|
2508
2521
|
|
|
2522
|
+
// src/commands/rule.ts
|
|
2523
|
+
var import_chalk17 = __toESM(require("chalk"));
|
|
2524
|
+
var import_ora5 = __toESM(require("ora"));
|
|
2525
|
+
init_auth();
|
|
2526
|
+
init_api();
|
|
2527
|
+
async function runRule(ruleText, projectArg, opts) {
|
|
2528
|
+
const rule = (ruleText ?? "").trim();
|
|
2529
|
+
if (!rule) throw new Error('Provide the rule in quotes, e.g. apiblaze rule "users see only their own rows" myproject');
|
|
2530
|
+
const creds = loadCredentials();
|
|
2531
|
+
if (!creds) throw new Error("Not authenticated. Run `apiblaze login` first.");
|
|
2532
|
+
const teamId = creds.teamId ?? "";
|
|
2533
|
+
const projects = await getProjects(teamId);
|
|
2534
|
+
const match = projects.find((p) => p.projectId === projectArg || p.projectName === projectArg);
|
|
2535
|
+
if (!match) {
|
|
2536
|
+
throw new Error(`Project "${projectArg}" not found in your active team (${creds.teamName ?? teamId}). Run \`apiblaze projects\`.`);
|
|
2537
|
+
}
|
|
2538
|
+
const projectId = match.projectId;
|
|
2539
|
+
const apiVersion = opts.apiversion || match.apiVersion;
|
|
2540
|
+
const tenant2 = match.tenant || projectId;
|
|
2541
|
+
const spinner = (0, import_ora5.default)("Designing the rule\u2026").start();
|
|
2542
|
+
const messages = [{
|
|
2543
|
+
role: "user",
|
|
2544
|
+
content: `Author this authorization rule and generate the COMPLETE proposal (OpenFGA model + per-route rules) NOW for every affected route. Do not ask clarifying questions; make reasonable assumptions and state them briefly. Rule: "${rule}"`
|
|
2545
|
+
}];
|
|
2546
|
+
const { status, data } = await agentCall(
|
|
2547
|
+
`/projects/${projectId}/${apiVersion}/authz/chat`,
|
|
2548
|
+
"POST",
|
|
2549
|
+
{ messages, included_sample_ids: [], existing_model: null, existing_routes: [] }
|
|
2550
|
+
);
|
|
2551
|
+
if (status >= 400) {
|
|
2552
|
+
spinner.fail(`Authorization agent error (${status}): ${data?.error ?? ""}`);
|
|
2553
|
+
return;
|
|
2554
|
+
}
|
|
2555
|
+
const proposal = data?.proposal;
|
|
2556
|
+
if (!proposal || !Array.isArray(proposal.routes) || proposal.routes.length === 0) {
|
|
2557
|
+
spinner.fail("The agent couldn\u2019t turn that into a rule in one shot.");
|
|
2558
|
+
if (data?.reply) console.log(import_chalk17.default.dim("\n " + String(data.reply).slice(0, 500) + "\n"));
|
|
2559
|
+
console.log(import_chalk17.default.dim(" Try an interactive session for anything nuanced: ") + import_chalk17.default.cyan(`apiblaze agent authz ${projectArg}`));
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2562
|
+
spinner.succeed(`Rule designed \u2014 ${proposal.routes.length} route(s) affected.`);
|
|
2563
|
+
if (data?.reply) console.log(import_chalk17.default.dim(" " + String(data.reply).slice(0, 240)));
|
|
2564
|
+
const pub = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: false });
|
|
2565
|
+
if (pub.storeMissing) return;
|
|
2566
|
+
console.log(import_chalk17.default.green(` \u2713 Published in shadow mode \u2014 model + ${pub.ok} route(s)${pub.fail ? `, ${pub.fail} failed` : ""}.`));
|
|
2567
|
+
if (opts.enforce) {
|
|
2568
|
+
const enf = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: true });
|
|
2569
|
+
if (enf.enabled) {
|
|
2570
|
+
console.log(import_chalk17.default.green(` \u2713 Enforcement ON \u2014 ${enf.ok} route(s) + project switch on${enf.fail ? `, ${enf.fail} failed` : ""}.`));
|
|
2571
|
+
}
|
|
2572
|
+
} else {
|
|
2573
|
+
console.log(import_chalk17.default.dim(` Review it (shadow logs), then enforce: `) + import_chalk17.default.cyan(`apiblaze rule "${rule}" ${projectArg} --enforce`));
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2509
2577
|
// src/commands/openapi.ts
|
|
2510
|
-
var
|
|
2578
|
+
var import_chalk18 = __toESM(require("chalk"));
|
|
2511
2579
|
init_auth();
|
|
2512
2580
|
init_api();
|
|
2513
2581
|
async function runOpenapi(projectArg, apiVersionArg) {
|
|
@@ -2518,10 +2586,10 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
2518
2586
|
if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
|
|
2519
2587
|
const projectId = match.projectId;
|
|
2520
2588
|
const apiVersion = apiVersionArg || match.apiVersion;
|
|
2521
|
-
console.log(
|
|
2589
|
+
console.log(import_chalk18.default.dim("Gathering captured traffic samples\u2026"));
|
|
2522
2590
|
const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
|
|
2523
2591
|
if (routesRes.status >= 400) {
|
|
2524
|
-
console.log(
|
|
2592
|
+
console.log(import_chalk18.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
|
|
2525
2593
|
return;
|
|
2526
2594
|
}
|
|
2527
2595
|
const routes = routesRes.data?.routes ?? (Array.isArray(routesRes.data) ? routesRes.data : []);
|
|
@@ -2534,25 +2602,25 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
2534
2602
|
}
|
|
2535
2603
|
const sampleIds = [...ids];
|
|
2536
2604
|
if (sampleIds.length === 0) {
|
|
2537
|
-
console.log(
|
|
2605
|
+
console.log(import_chalk18.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
|
|
2538
2606
|
return;
|
|
2539
2607
|
}
|
|
2540
2608
|
async function publish(ctx) {
|
|
2541
2609
|
const patch = ctx.proposal.patch;
|
|
2542
2610
|
if (!Array.isArray(patch) || patch.length === 0) {
|
|
2543
|
-
ctx.log(
|
|
2611
|
+
ctx.log(import_chalk18.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
|
|
2544
2612
|
return;
|
|
2545
2613
|
}
|
|
2546
2614
|
const specSource = ctx.lastData?.spec_source ?? "unknown";
|
|
2547
2615
|
const endpoint = specSource === "github" ? "open-pr" : "publish-openapi";
|
|
2548
2616
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/samples/${endpoint}`, "POST", { patch, sample_ids_used: sampleIds });
|
|
2549
2617
|
if (pub.status >= 400) {
|
|
2550
|
-
ctx.log(
|
|
2618
|
+
ctx.log(import_chalk18.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
2551
2619
|
`));
|
|
2552
2620
|
return;
|
|
2553
2621
|
}
|
|
2554
2622
|
const prUrl = pub.data?.pr_url;
|
|
2555
|
-
ctx.log(
|
|
2623
|
+
ctx.log(import_chalk18.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
|
|
2556
2624
|
` : " \u2713 Published updated OpenAPI spec.\n"));
|
|
2557
2625
|
}
|
|
2558
2626
|
await runAgentChatRepl({
|
|
@@ -2573,7 +2641,7 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
2573
2641
|
}
|
|
2574
2642
|
|
|
2575
2643
|
// src/commands/mcp.ts
|
|
2576
|
-
var
|
|
2644
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
2577
2645
|
init_auth();
|
|
2578
2646
|
init_api();
|
|
2579
2647
|
async function runMcp(projectArg, apiVersionArg, opts) {
|
|
@@ -2590,11 +2658,11 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
2590
2658
|
const spec2 = ctx.proposal;
|
|
2591
2659
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec: spec2 });
|
|
2592
2660
|
if (pub.status >= 400) {
|
|
2593
|
-
ctx.log(
|
|
2661
|
+
ctx.log(import_chalk19.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
2594
2662
|
`));
|
|
2595
2663
|
return;
|
|
2596
2664
|
}
|
|
2597
|
-
ctx.log(
|
|
2665
|
+
ctx.log(import_chalk19.default.green(` \u2713 Published MCP server \u2014 ${mcpHost}.mcp.apiblaze.com/${apiVersion}/${environment}.
|
|
2598
2666
|
`));
|
|
2599
2667
|
}
|
|
2600
2668
|
await runAgentChatRepl({
|
|
@@ -2616,18 +2684,18 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
2616
2684
|
}
|
|
2617
2685
|
|
|
2618
2686
|
// src/commands/delete.ts
|
|
2619
|
-
var
|
|
2620
|
-
var
|
|
2687
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
2688
|
+
var import_ora6 = __toESM(require("ora"));
|
|
2621
2689
|
init_admin();
|
|
2622
2690
|
|
|
2623
2691
|
// src/lib/resolve.ts
|
|
2624
|
-
var
|
|
2692
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
2625
2693
|
init_auth();
|
|
2626
2694
|
init_api();
|
|
2627
2695
|
function requireAuth() {
|
|
2628
2696
|
const creds = loadCredentials();
|
|
2629
2697
|
if (!creds) {
|
|
2630
|
-
console.error(
|
|
2698
|
+
console.error(import_chalk20.default.red("Not logged in. Run `apiblaze login` first."));
|
|
2631
2699
|
process.exit(1);
|
|
2632
2700
|
}
|
|
2633
2701
|
return creds;
|
|
@@ -2636,7 +2704,7 @@ async function resolveTeam(opt) {
|
|
|
2636
2704
|
const creds = requireAuth();
|
|
2637
2705
|
if (!opt) {
|
|
2638
2706
|
if (!creds.teamId) {
|
|
2639
|
-
console.error(
|
|
2707
|
+
console.error(import_chalk20.default.red("No active team. Run `apiblaze login` or pass --team."));
|
|
2640
2708
|
process.exit(1);
|
|
2641
2709
|
}
|
|
2642
2710
|
return { teamId: creds.teamId, teamName: creds.teamName };
|
|
@@ -2645,7 +2713,7 @@ async function resolveTeam(opt) {
|
|
|
2645
2713
|
const teams = await getTeams().catch(() => []);
|
|
2646
2714
|
const match = teams.find((t) => t.name === opt || t.teamId === opt);
|
|
2647
2715
|
if (!match) {
|
|
2648
|
-
console.error(
|
|
2716
|
+
console.error(import_chalk20.default.red(`Team "${opt}" not found.${teams.length ? " Available: " + teams.map((t) => t.name).join(", ") : ""}`));
|
|
2649
2717
|
process.exit(1);
|
|
2650
2718
|
}
|
|
2651
2719
|
return { teamId: match.teamId, teamName: match.name };
|
|
@@ -2656,13 +2724,13 @@ async function resolveProject(teamId, nameOrId, version2) {
|
|
|
2656
2724
|
(p) => p.projectId === nameOrId || p.projectName === nameOrId
|
|
2657
2725
|
);
|
|
2658
2726
|
if (candidates.length === 0) {
|
|
2659
|
-
console.error(
|
|
2660
|
-
if (projects.length) console.error(
|
|
2727
|
+
console.error(import_chalk20.default.red(`Project "${nameOrId}" not found in this team.`));
|
|
2728
|
+
if (projects.length) console.error(import_chalk20.default.dim(" Known: " + projects.map((p) => p.projectName).join(", ")));
|
|
2661
2729
|
process.exit(1);
|
|
2662
2730
|
}
|
|
2663
2731
|
const chosen = version2 ? candidates.find((p) => p.apiVersion === version2) : candidates[0];
|
|
2664
2732
|
if (!chosen) {
|
|
2665
|
-
console.error(
|
|
2733
|
+
console.error(import_chalk20.default.red(`Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((p) => p.apiVersion).join(", ")}`));
|
|
2666
2734
|
process.exit(1);
|
|
2667
2735
|
}
|
|
2668
2736
|
return {
|
|
@@ -2678,7 +2746,7 @@ async function resolveProject(teamId, nameOrId, version2) {
|
|
|
2678
2746
|
async function runDelete(project, version2, opts) {
|
|
2679
2747
|
const { teamId } = await resolveTeam(opts.team);
|
|
2680
2748
|
const proj2 = await resolveProject(teamId, project, version2);
|
|
2681
|
-
const spinner = (0,
|
|
2749
|
+
const spinner = (0, import_ora6.default)("Checking delete impact...").start();
|
|
2682
2750
|
let impact;
|
|
2683
2751
|
try {
|
|
2684
2752
|
impact = await admin({
|
|
@@ -2697,10 +2765,10 @@ async function runDelete(project, version2, opts) {
|
|
|
2697
2765
|
return;
|
|
2698
2766
|
}
|
|
2699
2767
|
} else {
|
|
2700
|
-
console.log(`${
|
|
2768
|
+
console.log(`${import_chalk21.default.red.bold("Delete")} ${import_chalk21.default.bold(proj2.projectName)} ${import_chalk21.default.dim("v" + proj2.apiVersion)} ${import_chalk21.default.dim("(" + proj2.projectId + ")")}`);
|
|
2701
2769
|
if (impact && typeof impact === "object") {
|
|
2702
2770
|
const counts = impact.counts ?? impact.impact ?? impact;
|
|
2703
|
-
console.log(
|
|
2771
|
+
console.log(import_chalk21.default.dim(" This removes (cascade): ") + import_chalk21.default.yellow(JSON.stringify(counts)));
|
|
2704
2772
|
}
|
|
2705
2773
|
if (!opts.yes) {
|
|
2706
2774
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -2708,12 +2776,12 @@ async function runDelete(project, version2, opts) {
|
|
|
2708
2776
|
{ type: "confirm", name: "confirm", message: `Permanently delete ${proj2.projectName} v${proj2.apiVersion}? This cannot be undone.`, default: false }
|
|
2709
2777
|
]);
|
|
2710
2778
|
if (!confirm) {
|
|
2711
|
-
console.log(
|
|
2779
|
+
console.log(import_chalk21.default.dim("Aborted."));
|
|
2712
2780
|
return;
|
|
2713
2781
|
}
|
|
2714
2782
|
}
|
|
2715
2783
|
}
|
|
2716
|
-
const s2 = (0,
|
|
2784
|
+
const s2 = (0, import_ora6.default)("Deleting...").start();
|
|
2717
2785
|
try {
|
|
2718
2786
|
await admin({
|
|
2719
2787
|
method: "DELETE",
|
|
@@ -2729,7 +2797,7 @@ async function runDelete(project, version2, opts) {
|
|
|
2729
2797
|
}
|
|
2730
2798
|
|
|
2731
2799
|
// src/commands/export.ts
|
|
2732
|
-
var
|
|
2800
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
2733
2801
|
var import_fs3 = require("fs");
|
|
2734
2802
|
init_admin();
|
|
2735
2803
|
init_auth();
|
|
@@ -2747,7 +2815,7 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2747
2815
|
if (opts.noConsumers) q.set("include_consumers", "0");
|
|
2748
2816
|
if (opts.keys) q.set("keys", opts.keys);
|
|
2749
2817
|
const qs = q.toString() ? `?${q.toString()}` : "";
|
|
2750
|
-
console.log(
|
|
2818
|
+
console.log(import_chalk22.default.dim(`Queuing ${target} export of ${projectId}@${version2}\u2026`));
|
|
2751
2819
|
const job = await admin({
|
|
2752
2820
|
path: `/projects/${projectId}/${version2}/export/${target}${qs}`,
|
|
2753
2821
|
method: "GET",
|
|
@@ -2757,11 +2825,11 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2757
2825
|
const statusPath = `/projects/${projectId}/${version2}/export/jobs/${job.job_id}`;
|
|
2758
2826
|
let status = "queued";
|
|
2759
2827
|
const started = Date.now();
|
|
2760
|
-
process.stdout.write(
|
|
2828
|
+
process.stdout.write(import_chalk22.default.dim("Building bundle"));
|
|
2761
2829
|
while (status !== "ready" && status !== "failed") {
|
|
2762
2830
|
if (Date.now() - started > 10 * 6e4) throw new Error("Export timed out after 10 minutes.");
|
|
2763
2831
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
2764
|
-
process.stdout.write(
|
|
2832
|
+
process.stdout.write(import_chalk22.default.dim("."));
|
|
2765
2833
|
const st = await admin({ path: statusPath, method: "GET", summary: "poll export" });
|
|
2766
2834
|
status = st?.status ?? "queued";
|
|
2767
2835
|
if (status === "failed") {
|
|
@@ -2779,20 +2847,20 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2779
2847
|
if (!res.ok) throw new Error(`Download failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
2780
2848
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
2781
2849
|
(0, import_fs3.writeFileSync)(outFile, buf);
|
|
2782
|
-
console.log(
|
|
2783
|
-
if (job.decryption_key) console.log(
|
|
2784
|
-
console.log(
|
|
2785
|
-
if (target === "kong") console.log(
|
|
2850
|
+
console.log(import_chalk22.default.green(`\u2713 Saved ${outFile} (${(buf.length / 1024).toFixed(0)} KB)`));
|
|
2851
|
+
if (job.decryption_key) console.log(import_chalk22.default.yellow(`Decryption key (shown once): ${job.decryption_key}`));
|
|
2852
|
+
console.log(import_chalk22.default.dim("Read MIGRATION-REPORT.json inside the zip for the full fidelity ledger."));
|
|
2853
|
+
if (target === "kong") console.log(import_chalk22.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
|
|
2786
2854
|
}
|
|
2787
2855
|
|
|
2788
2856
|
// src/commands/config.ts
|
|
2789
|
-
var
|
|
2790
|
-
var
|
|
2857
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
2858
|
+
var import_ora7 = __toESM(require("ora"));
|
|
2791
2859
|
init_admin();
|
|
2792
2860
|
async function patchConfig(project, opts, body, summary) {
|
|
2793
2861
|
const { teamId } = await resolveTeam(opts.team);
|
|
2794
2862
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2795
|
-
const spinner = (0,
|
|
2863
|
+
const spinner = (0, import_ora7.default)(summary + "...").start();
|
|
2796
2864
|
try {
|
|
2797
2865
|
const out = await admin({
|
|
2798
2866
|
method: "PATCH",
|
|
@@ -2809,7 +2877,7 @@ async function patchConfig(project, opts, body, summary) {
|
|
|
2809
2877
|
}
|
|
2810
2878
|
async function runTargetSet(project, opts) {
|
|
2811
2879
|
if (!opts.url) {
|
|
2812
|
-
console.error(
|
|
2880
|
+
console.error(import_chalk23.default.red("--url is required."));
|
|
2813
2881
|
process.exit(1);
|
|
2814
2882
|
}
|
|
2815
2883
|
const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
|
|
@@ -2822,43 +2890,43 @@ async function runThrottleSet(project, opts) {
|
|
|
2822
2890
|
if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
|
|
2823
2891
|
if (opts.period !== void 0) {
|
|
2824
2892
|
if (!["daily", "weekly", "monthly"].includes(opts.period)) {
|
|
2825
|
-
console.error(
|
|
2893
|
+
console.error(import_chalk23.default.red("--period must be daily, weekly, or monthly."));
|
|
2826
2894
|
process.exit(1);
|
|
2827
2895
|
}
|
|
2828
2896
|
throttling.quotaPeriod = opts.period;
|
|
2829
2897
|
}
|
|
2830
2898
|
if (Object.keys(throttling).length === 0) {
|
|
2831
|
-
console.error(
|
|
2899
|
+
console.error(import_chalk23.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
|
|
2832
2900
|
process.exit(1);
|
|
2833
2901
|
}
|
|
2834
2902
|
await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
|
|
2835
2903
|
}
|
|
2836
2904
|
async function runRename(project, opts) {
|
|
2837
2905
|
if (!opts.displayName) {
|
|
2838
|
-
console.error(
|
|
2906
|
+
console.error(import_chalk23.default.red("--display-name is required."));
|
|
2839
2907
|
process.exit(1);
|
|
2840
2908
|
}
|
|
2841
2909
|
await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
|
|
2842
2910
|
}
|
|
2843
2911
|
|
|
2844
2912
|
// src/commands/config-browse.ts
|
|
2845
|
-
var
|
|
2846
|
-
var
|
|
2913
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
2914
|
+
var import_ora15 = __toESM(require("ora"));
|
|
2847
2915
|
init_admin();
|
|
2848
2916
|
init_auth();
|
|
2849
2917
|
|
|
2850
2918
|
// src/commands/domain.ts
|
|
2851
|
-
var
|
|
2852
|
-
var
|
|
2919
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
2920
|
+
var import_ora8 = __toESM(require("ora"));
|
|
2853
2921
|
init_admin();
|
|
2854
2922
|
async function runDomainAdd(project, opts) {
|
|
2855
2923
|
if (!opts.domain) {
|
|
2856
|
-
console.error(
|
|
2924
|
+
console.error(import_chalk24.default.red("--domain is required."));
|
|
2857
2925
|
process.exit(1);
|
|
2858
2926
|
}
|
|
2859
2927
|
const { teamId } = await resolveTeam(opts.team);
|
|
2860
2928
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2861
|
-
const spinner = (0,
|
|
2929
|
+
const spinner = (0, import_ora8.default)(`Registering ${opts.domain}...`).start();
|
|
2862
2930
|
try {
|
|
2863
2931
|
const out = await admin({
|
|
2864
2932
|
method: "POST",
|
|
@@ -2872,9 +2940,9 @@ async function runDomainAdd(project, opts) {
|
|
|
2872
2940
|
console.log(JSON.stringify(out));
|
|
2873
2941
|
return;
|
|
2874
2942
|
}
|
|
2875
|
-
if (out?.cnameRecord) console.log(` ${
|
|
2876
|
-
if (out?.dcv) console.log(` ${
|
|
2877
|
-
if (out?.id) console.log(
|
|
2943
|
+
if (out?.cnameRecord) console.log(` ${import_chalk24.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
|
|
2944
|
+
if (out?.dcv) console.log(` ${import_chalk24.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
|
|
2945
|
+
if (out?.id) console.log(import_chalk24.default.dim(` domain id: ${out.id}`));
|
|
2878
2946
|
} catch (err) {
|
|
2879
2947
|
spinner.fail("Domain registration failed.");
|
|
2880
2948
|
throw err;
|
|
@@ -2894,14 +2962,14 @@ async function runDomainList(project, opts) {
|
|
|
2894
2962
|
return;
|
|
2895
2963
|
}
|
|
2896
2964
|
if (!domains.length) {
|
|
2897
|
-
console.log(
|
|
2965
|
+
console.log(import_chalk24.default.yellow("No custom domains."));
|
|
2898
2966
|
return;
|
|
2899
2967
|
}
|
|
2900
|
-
for (const d of domains) console.log(` ${
|
|
2968
|
+
for (const d of domains) console.log(` ${import_chalk24.default.bold(d.hostname)} ${import_chalk24.default.dim(d.status ?? "")} ${import_chalk24.default.dim(d.id ?? "")}`);
|
|
2901
2969
|
}
|
|
2902
2970
|
async function runDomainStatus(project, opts) {
|
|
2903
2971
|
if (!opts.id) {
|
|
2904
|
-
console.error(
|
|
2972
|
+
console.error(import_chalk24.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
2905
2973
|
process.exit(1);
|
|
2906
2974
|
}
|
|
2907
2975
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -2911,16 +2979,16 @@ async function runDomainStatus(project, opts) {
|
|
|
2911
2979
|
path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
|
|
2912
2980
|
summary: `Check custom-domain status`
|
|
2913
2981
|
});
|
|
2914
|
-
console.log(opts.json ? JSON.stringify(out) : ` ${
|
|
2982
|
+
console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk24.default.bold(out?.hostname ?? opts.id)}: ${import_chalk24.default.cyan(out?.status ?? "unknown")}`);
|
|
2915
2983
|
}
|
|
2916
2984
|
async function runDomainRemove(project, opts) {
|
|
2917
2985
|
if (!opts.id) {
|
|
2918
|
-
console.error(
|
|
2986
|
+
console.error(import_chalk24.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
2919
2987
|
process.exit(1);
|
|
2920
2988
|
}
|
|
2921
2989
|
const { teamId } = await resolveTeam(opts.team);
|
|
2922
2990
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2923
|
-
const spinner = (0,
|
|
2991
|
+
const spinner = (0, import_ora8.default)("Removing domain...").start();
|
|
2924
2992
|
try {
|
|
2925
2993
|
await admin({
|
|
2926
2994
|
method: "DELETE",
|
|
@@ -2937,7 +3005,7 @@ async function runDomainSetBase(project, opts) {
|
|
|
2937
3005
|
const env = opts.env ?? "prod";
|
|
2938
3006
|
const { teamId } = await resolveTeam(opts.team);
|
|
2939
3007
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2940
|
-
const spinner = (0,
|
|
3008
|
+
const spinner = (0, import_ora8.default)("Setting base domain...").start();
|
|
2941
3009
|
try {
|
|
2942
3010
|
await admin({
|
|
2943
3011
|
method: "PUT",
|
|
@@ -2953,28 +3021,28 @@ async function runDomainSetBase(project, opts) {
|
|
|
2953
3021
|
}
|
|
2954
3022
|
|
|
2955
3023
|
// src/commands/tenant.ts
|
|
2956
|
-
var
|
|
2957
|
-
var
|
|
3024
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
3025
|
+
var import_ora11 = __toESM(require("ora"));
|
|
2958
3026
|
init_admin();
|
|
2959
3027
|
init_auth();
|
|
2960
3028
|
init_tenant_pick();
|
|
2961
3029
|
async function runTenantUse(query, opts) {
|
|
2962
3030
|
const creds = loadCredentials();
|
|
2963
3031
|
if (!creds) {
|
|
2964
|
-
console.error(
|
|
3032
|
+
console.error(import_chalk27.default.red("Not logged in. Run `apiblaze login` first."));
|
|
2965
3033
|
process.exit(1);
|
|
2966
3034
|
}
|
|
2967
3035
|
if (opts.clear) {
|
|
2968
3036
|
delete creds.activeTenant;
|
|
2969
3037
|
saveCredentials(creds);
|
|
2970
|
-
console.log(
|
|
3038
|
+
console.log(import_chalk27.default.green("Tenant scope cleared."));
|
|
2971
3039
|
return;
|
|
2972
3040
|
}
|
|
2973
3041
|
const { teamId } = await resolveTeam(opts.team);
|
|
2974
3042
|
const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
|
|
2975
3043
|
if (!slug) process.exit(1);
|
|
2976
3044
|
saveCredentials({ ...creds, activeTenant: slug });
|
|
2977
|
-
console.log(
|
|
3045
|
+
console.log(import_chalk27.default.green(`Tenant scope set to ${import_chalk27.default.bold(slug)}.`) + import_chalk27.default.dim(" (clear with `apiblaze tenant use --clear`)"));
|
|
2978
3046
|
}
|
|
2979
3047
|
async function runTenantList(opts) {
|
|
2980
3048
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -2991,26 +3059,26 @@ async function runTenantList(opts) {
|
|
|
2991
3059
|
return;
|
|
2992
3060
|
}
|
|
2993
3061
|
if (!tenants.length) {
|
|
2994
|
-
console.log(
|
|
3062
|
+
console.log(import_chalk27.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
2995
3063
|
return;
|
|
2996
3064
|
}
|
|
2997
3065
|
for (const t of tenants) {
|
|
2998
3066
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
2999
|
-
const display = typeof t === "string" ? "" :
|
|
3000
|
-
console.log(` ${
|
|
3067
|
+
const display = typeof t === "string" ? "" : import_chalk27.default.dim(` ${t.display_name ?? ""}`);
|
|
3068
|
+
console.log(` ${import_chalk27.default.bold(name)}${display}`);
|
|
3001
3069
|
}
|
|
3002
3070
|
const total = out?.total ?? tenants.length;
|
|
3003
3071
|
if (total > tenants.length) {
|
|
3004
|
-
console.log(
|
|
3072
|
+
console.log(import_chalk27.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
3005
3073
|
}
|
|
3006
3074
|
}
|
|
3007
3075
|
async function runTenantCreate(opts) {
|
|
3008
3076
|
if (!opts.name) {
|
|
3009
|
-
console.error(
|
|
3077
|
+
console.error(import_chalk27.default.red("--name (display name) is required."));
|
|
3010
3078
|
process.exit(1);
|
|
3011
3079
|
}
|
|
3012
3080
|
const { teamId } = await resolveTeam(opts.team);
|
|
3013
|
-
const spinner = (0,
|
|
3081
|
+
const spinner = (0, import_ora11.default)("Creating tenant...").start();
|
|
3014
3082
|
try {
|
|
3015
3083
|
const out = await admin({
|
|
3016
3084
|
method: "POST",
|
|
@@ -3018,7 +3086,7 @@ async function runTenantCreate(opts) {
|
|
|
3018
3086
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
3019
3087
|
summary: `Create tenant "${opts.name}"`
|
|
3020
3088
|
});
|
|
3021
|
-
spinner.succeed(`Created tenant ${
|
|
3089
|
+
spinner.succeed(`Created tenant ${import_chalk27.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
3022
3090
|
if (opts.json) console.log(JSON.stringify(out));
|
|
3023
3091
|
} catch (err) {
|
|
3024
3092
|
spinner.fail("Tenant create failed.");
|
|
@@ -3027,12 +3095,12 @@ async function runTenantCreate(opts) {
|
|
|
3027
3095
|
}
|
|
3028
3096
|
async function runTenantAttach(project, opts) {
|
|
3029
3097
|
if (!opts.tenant) {
|
|
3030
|
-
console.error(
|
|
3098
|
+
console.error(import_chalk27.default.red("--tenant <slug> is required."));
|
|
3031
3099
|
process.exit(1);
|
|
3032
3100
|
}
|
|
3033
3101
|
const { teamId } = await resolveTeam(opts.team);
|
|
3034
3102
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3035
|
-
const spinner = (0,
|
|
3103
|
+
const spinner = (0, import_ora11.default)("Attaching tenant...").start();
|
|
3036
3104
|
try {
|
|
3037
3105
|
const out = await admin({
|
|
3038
3106
|
method: "POST",
|
|
@@ -3055,11 +3123,11 @@ async function runTenantDelete(slug, opts) {
|
|
|
3055
3123
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
3056
3124
|
]);
|
|
3057
3125
|
if (!confirm) {
|
|
3058
|
-
console.log(
|
|
3126
|
+
console.log(import_chalk27.default.dim("Aborted."));
|
|
3059
3127
|
return;
|
|
3060
3128
|
}
|
|
3061
3129
|
}
|
|
3062
|
-
const spinner = (0,
|
|
3130
|
+
const spinner = (0, import_ora11.default)("Deleting tenant...").start();
|
|
3063
3131
|
try {
|
|
3064
3132
|
await admin({
|
|
3065
3133
|
method: "DELETE",
|
|
@@ -3074,13 +3142,13 @@ async function runTenantDelete(slug, opts) {
|
|
|
3074
3142
|
}
|
|
3075
3143
|
async function runTenantCors(opts) {
|
|
3076
3144
|
if (!opts.tenant) {
|
|
3077
|
-
console.error(
|
|
3145
|
+
console.error(import_chalk27.default.red("--tenant <slug> is required."));
|
|
3078
3146
|
process.exit(1);
|
|
3079
3147
|
}
|
|
3080
3148
|
const { teamId } = await resolveTeam(opts.team);
|
|
3081
3149
|
const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
3082
3150
|
const cors = origins.length ? { allowed_origins: origins } : null;
|
|
3083
|
-
const spinner = (0,
|
|
3151
|
+
const spinner = (0, import_ora11.default)("Updating CORS...").start();
|
|
3084
3152
|
try {
|
|
3085
3153
|
await admin({
|
|
3086
3154
|
method: "PUT",
|
|
@@ -3096,8 +3164,8 @@ async function runTenantCors(opts) {
|
|
|
3096
3164
|
}
|
|
3097
3165
|
|
|
3098
3166
|
// src/commands/tenant-drill.ts
|
|
3099
|
-
var
|
|
3100
|
-
var
|
|
3167
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
3168
|
+
var import_ora12 = __toESM(require("ora"));
|
|
3101
3169
|
var import_crypto = require("crypto");
|
|
3102
3170
|
init_admin();
|
|
3103
3171
|
init_auth();
|
|
@@ -3126,17 +3194,17 @@ async function validScopedTenant(teamId, query) {
|
|
|
3126
3194
|
const next = { ...creds };
|
|
3127
3195
|
delete next.activeTenant;
|
|
3128
3196
|
saveCredentials2(next);
|
|
3129
|
-
console.log(
|
|
3197
|
+
console.log(import_chalk28.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
3130
3198
|
return void 0;
|
|
3131
3199
|
}
|
|
3132
3200
|
async function tenantHome(teamId, tenant2) {
|
|
3133
3201
|
const { default: inquirer3 } = await import("inquirer");
|
|
3134
3202
|
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
3135
|
-
console.log(
|
|
3203
|
+
console.log(import_chalk28.default.bold(`
|
|
3136
3204
|
Tenant ${tenant2}`));
|
|
3137
|
-
console.log(
|
|
3205
|
+
console.log(import_chalk28.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
3138
3206
|
for (; ; ) {
|
|
3139
|
-
const spinner = (0,
|
|
3207
|
+
const spinner = (0, import_ora12.default)("Reading tenant state...").start();
|
|
3140
3208
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
3141
3209
|
admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
|
|
3142
3210
|
admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
|
|
@@ -3150,18 +3218,18 @@ Tenant ${tenant2}`));
|
|
|
3150
3218
|
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
3151
3219
|
const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
|
|
3152
3220
|
const nLogin = nClients + nIssuers + nOpaque;
|
|
3153
|
-
const onOff = (b) => b ?
|
|
3221
|
+
const onOff = (b) => b ? import_chalk28.default.green("on") : import_chalk28.default.dim("off");
|
|
3154
3222
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3155
3223
|
type: "list",
|
|
3156
3224
|
name: "pick",
|
|
3157
3225
|
message: `Tenant ${tenant2}:`,
|
|
3158
3226
|
pageSize: 12,
|
|
3159
3227
|
choices: [
|
|
3160
|
-
{ name: `Login methods (${nLogin}) ${
|
|
3161
|
-
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${
|
|
3162
|
-
{ name: `Portal admins (${nEmails}) ${
|
|
3163
|
-
new inquirer3.Separator(
|
|
3164
|
-
{ name: `CORS: ${cors?.cors ?
|
|
3228
|
+
{ name: `Login methods (${nLogin}) ${import_chalk28.default.dim("how your consumers sign in")}`, value: "login" },
|
|
3229
|
+
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk28.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
|
|
3230
|
+
{ name: `Portal admins (${nEmails}) ${import_chalk28.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
|
|
3231
|
+
new inquirer3.Separator(import_chalk28.default.dim(" Settings")),
|
|
3232
|
+
{ name: `CORS: ${cors?.cors ? import_chalk28.default.cyan(JSON.stringify(cors.cors)) : import_chalk28.default.dim("(unset)")} ${import_chalk28.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
|
|
3165
3233
|
{ name: "\u2190 Back", value: "back" }
|
|
3166
3234
|
]
|
|
3167
3235
|
}]);
|
|
@@ -3174,7 +3242,7 @@ Tenant ${tenant2}`));
|
|
|
3174
3242
|
case "iam": {
|
|
3175
3243
|
const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
|
|
3176
3244
|
await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
3177
|
-
console.log(
|
|
3245
|
+
console.log(import_chalk28.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
3178
3246
|
break;
|
|
3179
3247
|
}
|
|
3180
3248
|
case "cors": {
|
|
@@ -3187,11 +3255,11 @@ Tenant ${tenant2}`));
|
|
|
3187
3255
|
if (v === "") break;
|
|
3188
3256
|
const parsed = v === "null" ? null : safeJson(v);
|
|
3189
3257
|
if (parsed === void 0) {
|
|
3190
|
-
console.log(
|
|
3258
|
+
console.log(import_chalk28.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
3191
3259
|
break;
|
|
3192
3260
|
}
|
|
3193
3261
|
await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
3194
|
-
console.log(
|
|
3262
|
+
console.log(import_chalk28.default.green(" CORS updated."));
|
|
3195
3263
|
break;
|
|
3196
3264
|
}
|
|
3197
3265
|
case "emails":
|
|
@@ -3203,7 +3271,7 @@ Tenant ${tenant2}`));
|
|
|
3203
3271
|
async function loginMethodsMenu(teamId, tenant2, base) {
|
|
3204
3272
|
const { default: inquirer3 } = await import("inquirer");
|
|
3205
3273
|
for (; ; ) {
|
|
3206
|
-
const spinner = (0,
|
|
3274
|
+
const spinner = (0, import_ora12.default)("Loading login methods...").start();
|
|
3207
3275
|
const [rawClients, rawIssuers, rawOpaque] = await Promise.all([
|
|
3208
3276
|
admin({ method: "GET", path: `${base}/app-clients`, summary: "List APIblaze-hosted logins" }).catch(() => []),
|
|
3209
3277
|
admin({ method: "GET", path: `${base}/external-issuers`, summary: "List your-own-JWT logins" }).catch(() => null),
|
|
@@ -3215,15 +3283,15 @@ async function loginMethodsMenu(teamId, tenant2, base) {
|
|
|
3215
3283
|
const choices = [];
|
|
3216
3284
|
for (const c of appClients) {
|
|
3217
3285
|
const nP = (c.providers ?? []).length || c.providers_count || 0;
|
|
3218
|
-
choices.push({ name: `${
|
|
3286
|
+
choices.push({ name: `${import_chalk28.default.cyan("APIblaze-hosted")} ${import_chalk28.default.bold(c.name ?? c.clientId)} ${import_chalk28.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk28.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
|
|
3219
3287
|
}
|
|
3220
3288
|
for (const i of issuers) {
|
|
3221
|
-
choices.push({ name: `${
|
|
3289
|
+
choices.push({ name: `${import_chalk28.default.cyan("Your own \u2014 JWT ")} ${import_chalk28.default.bold(i.iss)} ${import_chalk28.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
|
|
3222
3290
|
}
|
|
3223
3291
|
if (opaque?.endpoint) {
|
|
3224
|
-
choices.push({ name: `${
|
|
3292
|
+
choices.push({ name: `${import_chalk28.default.cyan("Your own \u2014 opaque")} ${import_chalk28.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
|
|
3225
3293
|
}
|
|
3226
|
-
if (!choices.length) console.log(
|
|
3294
|
+
if (!choices.length) console.log(import_chalk28.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
|
|
3227
3295
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3228
3296
|
type: "list",
|
|
3229
3297
|
name: "pick",
|
|
@@ -3263,9 +3331,9 @@ async function addLoginMethod(teamId, tenant2, base) {
|
|
|
3263
3331
|
message: "How do people log in?",
|
|
3264
3332
|
pageSize: 8,
|
|
3265
3333
|
choices: [
|
|
3266
|
-
{ name: `APIblaze hosted login page ${
|
|
3267
|
-
{ name: `Your own hosted login \u2014 JWT ${
|
|
3268
|
-
{ name: `Your own login \u2014 opaque token ${
|
|
3334
|
+
{ name: `APIblaze hosted login page ${import_chalk28.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
|
|
3335
|
+
{ name: `Your own hosted login \u2014 JWT ${import_chalk28.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
|
|
3336
|
+
{ name: `Your own login \u2014 opaque token ${import_chalk28.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
|
|
3269
3337
|
{ name: "\u2190 Back", value: null }
|
|
3270
3338
|
]
|
|
3271
3339
|
}]);
|
|
@@ -3289,8 +3357,8 @@ function safeJson(s) {
|
|
|
3289
3357
|
async function emailsMenu(base, emails) {
|
|
3290
3358
|
const { default: inquirer3 } = await import("inquirer");
|
|
3291
3359
|
console.log();
|
|
3292
|
-
if (!emails.length) console.log(
|
|
3293
|
-
for (const e of emails) console.log(` ${
|
|
3360
|
+
if (!emails.length) console.log(import_chalk28.default.dim(" No consumer-admin emails."));
|
|
3361
|
+
for (const e of emails) console.log(` ${import_chalk28.default.bold(e.email ?? e)} ${import_chalk28.default.dim(e.status ?? "")}`);
|
|
3294
3362
|
const { act } = await inquirer3.prompt([{
|
|
3295
3363
|
type: "list",
|
|
3296
3364
|
name: "act",
|
|
@@ -3305,7 +3373,7 @@ async function emailsMenu(base, emails) {
|
|
|
3305
3373
|
if (act === "add") {
|
|
3306
3374
|
const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
3307
3375
|
await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
3308
|
-
console.log(
|
|
3376
|
+
console.log(import_chalk28.default.green(` ${email} added.`));
|
|
3309
3377
|
} else {
|
|
3310
3378
|
const { e } = await inquirer3.prompt([{
|
|
3311
3379
|
type: "list",
|
|
@@ -3315,7 +3383,7 @@ async function emailsMenu(base, emails) {
|
|
|
3315
3383
|
}]);
|
|
3316
3384
|
if (!e) return;
|
|
3317
3385
|
await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3318
|
-
console.log(
|
|
3386
|
+
console.log(import_chalk28.default.green(` ${e} removed.`));
|
|
3319
3387
|
}
|
|
3320
3388
|
}
|
|
3321
3389
|
async function addIssuer(base) {
|
|
@@ -3336,19 +3404,19 @@ async function addIssuer(base) {
|
|
|
3336
3404
|
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3337
3405
|
summary: `Add external issuer ${a.iss.trim()}`
|
|
3338
3406
|
});
|
|
3339
|
-
console.log(
|
|
3407
|
+
console.log(import_chalk28.default.green(" JWT login method saved."));
|
|
3340
3408
|
}
|
|
3341
3409
|
async function issuerHome(base, issuer) {
|
|
3342
3410
|
const { default: inquirer3 } = await import("inquirer");
|
|
3343
3411
|
console.log(`
|
|
3344
|
-
${
|
|
3412
|
+
${import_chalk28.default.bold(issuer.iss)} ${import_chalk28.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
|
|
3345
3413
|
const { act } = await inquirer3.prompt([{
|
|
3346
3414
|
type: "list",
|
|
3347
3415
|
name: "act",
|
|
3348
3416
|
message: "This JWT login method:",
|
|
3349
3417
|
choices: [
|
|
3350
3418
|
{ name: "Replace it (re-enter details)", value: "edit" },
|
|
3351
|
-
{ name:
|
|
3419
|
+
{ name: import_chalk28.default.red("Delete it"), value: "rm" },
|
|
3352
3420
|
{ name: "\u2190 Back", value: "back" }
|
|
3353
3421
|
]
|
|
3354
3422
|
}]);
|
|
@@ -3364,7 +3432,7 @@ async function issuerHome(base, issuer) {
|
|
|
3364
3432
|
path: `${base}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
|
|
3365
3433
|
summary: `Delete issuer ${issuer.iss}`
|
|
3366
3434
|
});
|
|
3367
|
-
console.log(
|
|
3435
|
+
console.log(import_chalk28.default.green(" Deleted."));
|
|
3368
3436
|
}
|
|
3369
3437
|
async function setOpaque(base, cur) {
|
|
3370
3438
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3373,19 +3441,19 @@ async function setOpaque(base, cur) {
|
|
|
3373
3441
|
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3374
3442
|
]);
|
|
3375
3443
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3376
|
-
console.log(
|
|
3444
|
+
console.log(import_chalk28.default.green(" Opaque login method set."));
|
|
3377
3445
|
}
|
|
3378
3446
|
async function opaqueHome(base, cur) {
|
|
3379
3447
|
const { default: inquirer3 } = await import("inquirer");
|
|
3380
3448
|
console.log(`
|
|
3381
|
-
${
|
|
3449
|
+
${import_chalk28.default.bold(cur.endpoint)} ${import_chalk28.default.dim(cur.method ?? "GET")}`);
|
|
3382
3450
|
const { act } = await inquirer3.prompt([{
|
|
3383
3451
|
type: "list",
|
|
3384
3452
|
name: "act",
|
|
3385
3453
|
message: "This opaque login method:",
|
|
3386
3454
|
choices: [
|
|
3387
3455
|
{ name: "Replace it", value: "edit" },
|
|
3388
|
-
{ name:
|
|
3456
|
+
{ name: import_chalk28.default.red("Delete it"), value: "rm" },
|
|
3389
3457
|
{ name: "\u2190 Back", value: "back" }
|
|
3390
3458
|
]
|
|
3391
3459
|
}]);
|
|
@@ -3395,14 +3463,14 @@ async function opaqueHome(base, cur) {
|
|
|
3395
3463
|
return;
|
|
3396
3464
|
}
|
|
3397
3465
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3398
|
-
console.log(
|
|
3466
|
+
console.log(import_chalk28.default.green(" Deleted."));
|
|
3399
3467
|
}
|
|
3400
3468
|
async function clientHome(base, summary) {
|
|
3401
3469
|
const { default: inquirer3 } = await import("inquirer");
|
|
3402
3470
|
const id = summary.clientId ?? summary.client_id;
|
|
3403
3471
|
const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
|
|
3404
3472
|
for (; ; ) {
|
|
3405
|
-
const spinner = (0,
|
|
3473
|
+
const spinner = (0, import_ora12.default)("Reading app client...").start();
|
|
3406
3474
|
const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
|
|
3407
3475
|
spinner.stop();
|
|
3408
3476
|
const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
|
|
@@ -3414,13 +3482,13 @@ async function clientHome(base, summary) {
|
|
|
3414
3482
|
message: `${c.name ?? id}:`,
|
|
3415
3483
|
pageSize: 12,
|
|
3416
3484
|
choices: [
|
|
3417
|
-
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${
|
|
3418
|
-
{ name: `Callback URLs: ${cb.length ?
|
|
3419
|
-
{ name: `Scopes: ${scopes.length ?
|
|
3485
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk28.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
3486
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk28.default.cyan(cb.join(", ")) : import_chalk28.default.dim("(none)")}`, value: "callbacks" },
|
|
3487
|
+
{ name: `Scopes: ${scopes.length ? import_chalk28.default.cyan(scopes.join(" ")) : import_chalk28.default.dim("(defaults)")}`, value: "scopes" },
|
|
3420
3488
|
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3421
3489
|
{ name: "Reveal client secret", value: "secret" },
|
|
3422
3490
|
{ name: "Rotate client secret", value: "rotate" },
|
|
3423
|
-
{ name:
|
|
3491
|
+
{ name: import_chalk28.default.red("Delete this app client"), value: "delete" },
|
|
3424
3492
|
{ name: "\u2190 Back", value: "back" }
|
|
3425
3493
|
]
|
|
3426
3494
|
}]);
|
|
@@ -3433,13 +3501,13 @@ async function clientHome(base, summary) {
|
|
|
3433
3501
|
case "callbacks": {
|
|
3434
3502
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3435
3503
|
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3436
|
-
console.log(
|
|
3504
|
+
console.log(import_chalk28.default.green(" Callbacks updated."));
|
|
3437
3505
|
break;
|
|
3438
3506
|
}
|
|
3439
3507
|
case "scopes": {
|
|
3440
3508
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3441
3509
|
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3442
|
-
console.log(
|
|
3510
|
+
console.log(import_chalk28.default.green(" Scopes updated."));
|
|
3443
3511
|
break;
|
|
3444
3512
|
}
|
|
3445
3513
|
case "expiries": {
|
|
@@ -3454,14 +3522,14 @@ async function clientHome(base, summary) {
|
|
|
3454
3522
|
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3455
3523
|
summary: "Update token expiries"
|
|
3456
3524
|
});
|
|
3457
|
-
console.log(
|
|
3525
|
+
console.log(import_chalk28.default.green(" Expiries updated."));
|
|
3458
3526
|
break;
|
|
3459
3527
|
}
|
|
3460
3528
|
case "secret": {
|
|
3461
3529
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3462
3530
|
if (!sure) break;
|
|
3463
3531
|
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3464
|
-
console.log(` ${
|
|
3532
|
+
console.log(` ${import_chalk28.default.bold("client_secret")}: ${import_chalk28.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3465
3533
|
break;
|
|
3466
3534
|
}
|
|
3467
3535
|
case "rotate": {
|
|
@@ -3469,14 +3537,14 @@ async function clientHome(base, summary) {
|
|
|
3469
3537
|
if (!sure) break;
|
|
3470
3538
|
const fresh = randomSecret();
|
|
3471
3539
|
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3472
|
-
console.log(` New ${
|
|
3540
|
+
console.log(` New ${import_chalk28.default.bold("client_secret")}: ${import_chalk28.default.green(fresh)} ${import_chalk28.default.dim("(store it now)")}`);
|
|
3473
3541
|
break;
|
|
3474
3542
|
}
|
|
3475
3543
|
case "delete": {
|
|
3476
3544
|
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 }]);
|
|
3477
3545
|
if (!sure) break;
|
|
3478
3546
|
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
3479
|
-
console.log(
|
|
3547
|
+
console.log(import_chalk28.default.green(" App client deleted."));
|
|
3480
3548
|
return;
|
|
3481
3549
|
}
|
|
3482
3550
|
}
|
|
@@ -3497,15 +3565,15 @@ var DEFAULT_SCOPES2 = {
|
|
|
3497
3565
|
async function providersMenu(cBase, clientLabel) {
|
|
3498
3566
|
const { default: inquirer3 } = await import("inquirer");
|
|
3499
3567
|
for (; ; ) {
|
|
3500
|
-
const spinner = (0,
|
|
3568
|
+
const spinner = (0, import_ora12.default)("Loading providers...").start();
|
|
3501
3569
|
const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
|
|
3502
3570
|
spinner.stop();
|
|
3503
3571
|
const providers = Array.isArray(raw) ? raw : [];
|
|
3504
3572
|
console.log();
|
|
3505
3573
|
for (const p of providers) {
|
|
3506
|
-
console.log(` ${
|
|
3574
|
+
console.log(` ${import_chalk28.default.bold(p.type)} ${import_chalk28.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
|
|
3507
3575
|
}
|
|
3508
|
-
if (!providers.length) console.log(
|
|
3576
|
+
if (!providers.length) console.log(import_chalk28.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
3509
3577
|
const { act } = await inquirer3.prompt([{
|
|
3510
3578
|
type: "list",
|
|
3511
3579
|
name: "act",
|
|
@@ -3563,23 +3631,23 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3563
3631
|
body.targetServerToken = routing;
|
|
3564
3632
|
}
|
|
3565
3633
|
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
3566
|
-
console.log(
|
|
3634
|
+
console.log(import_chalk28.default.green(` ${type} provider added.`));
|
|
3567
3635
|
} else {
|
|
3568
3636
|
const { p } = await inquirer3.prompt([{
|
|
3569
3637
|
type: "list",
|
|
3570
3638
|
name: "p",
|
|
3571
3639
|
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
3572
|
-
choices: [...providers.map((x) => ({ name: `${x.type} ${
|
|
3640
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk28.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3573
3641
|
}]);
|
|
3574
3642
|
if (!p) continue;
|
|
3575
3643
|
if (act === "rm") {
|
|
3576
3644
|
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 }]);
|
|
3577
3645
|
if (!sure) continue;
|
|
3578
3646
|
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
3579
|
-
console.log(
|
|
3647
|
+
console.log(import_chalk28.default.green(` ${p.type} removed.`));
|
|
3580
3648
|
} else {
|
|
3581
3649
|
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
3582
|
-
console.log(` ${
|
|
3650
|
+
console.log(` ${import_chalk28.default.bold("client_secret")}: ${import_chalk28.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3583
3651
|
}
|
|
3584
3652
|
}
|
|
3585
3653
|
}
|
|
@@ -3587,8 +3655,8 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3587
3655
|
|
|
3588
3656
|
// src/commands/spec.ts
|
|
3589
3657
|
var fs7 = __toESM(require("fs"));
|
|
3590
|
-
var
|
|
3591
|
-
var
|
|
3658
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
3659
|
+
var import_ora13 = __toESM(require("ora"));
|
|
3592
3660
|
init_admin();
|
|
3593
3661
|
async function runSpecGet(project, opts) {
|
|
3594
3662
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3602,19 +3670,19 @@ async function runSpecGet(project, opts) {
|
|
|
3602
3670
|
}
|
|
3603
3671
|
async function runSpecSet(project, opts) {
|
|
3604
3672
|
if (!opts.file) {
|
|
3605
|
-
console.error(
|
|
3673
|
+
console.error(import_chalk29.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
3606
3674
|
process.exit(1);
|
|
3607
3675
|
}
|
|
3608
3676
|
let specContent;
|
|
3609
3677
|
try {
|
|
3610
3678
|
specContent = fs7.readFileSync(opts.file, "utf-8");
|
|
3611
3679
|
} catch {
|
|
3612
|
-
console.error(
|
|
3680
|
+
console.error(import_chalk29.default.red(`Cannot read file: ${opts.file}`));
|
|
3613
3681
|
process.exit(1);
|
|
3614
3682
|
}
|
|
3615
3683
|
const { teamId } = await resolveTeam(opts.team);
|
|
3616
3684
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3617
|
-
const spinner = (0,
|
|
3685
|
+
const spinner = (0, import_ora13.default)("Uploading spec...").start();
|
|
3618
3686
|
try {
|
|
3619
3687
|
const out = await admin({
|
|
3620
3688
|
method: "POST",
|
|
@@ -3631,13 +3699,13 @@ async function runSpecSet(project, opts) {
|
|
|
3631
3699
|
}
|
|
3632
3700
|
|
|
3633
3701
|
// src/commands/agent.ts
|
|
3634
|
-
var
|
|
3635
|
-
var
|
|
3702
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
3703
|
+
var import_ora14 = __toESM(require("ora"));
|
|
3636
3704
|
init_auth();
|
|
3637
3705
|
init_admin();
|
|
3638
3706
|
|
|
3639
3707
|
// src/lib/tools.ts
|
|
3640
|
-
var
|
|
3708
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
3641
3709
|
init_admin();
|
|
3642
3710
|
init_api();
|
|
3643
3711
|
async function proj(teamId, name, version2) {
|
|
@@ -3656,15 +3724,15 @@ var TOOLS = [
|
|
|
3656
3724
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
3657
3725
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
3658
3726
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
3659
|
-
const lines = [` ${
|
|
3660
|
-
if (res.devPortal) lines.push(` ${
|
|
3727
|
+
const lines = [` ${import_chalk30.default.dim("Proxy URL:")} ${import_chalk30.default.bold(url)}`];
|
|
3728
|
+
if (res.devPortal) lines.push(` ${import_chalk30.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
3661
3729
|
const envs = Object.keys(keys);
|
|
3662
3730
|
if (envs.length) {
|
|
3663
|
-
lines.push("", ` ${
|
|
3731
|
+
lines.push("", ` ${import_chalk30.default.bold("API keys")} ${import_chalk30.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
3664
3732
|
const w = Math.max(...envs.map((e) => e.length));
|
|
3665
|
-
for (const env of envs) lines.push(` ${
|
|
3733
|
+
for (const env of envs) lines.push(` ${import_chalk30.default.cyan(env.padEnd(w))} ${import_chalk30.default.green(keys[env])}`);
|
|
3666
3734
|
}
|
|
3667
|
-
if (tryIt) lines.push("", ` ${
|
|
3735
|
+
if (tryIt) lines.push("", ` ${import_chalk30.default.dim("Try it:")}`, ` ${import_chalk30.default.cyan(tryIt)}`);
|
|
3668
3736
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
3669
3737
|
}
|
|
3670
3738
|
},
|
|
@@ -3820,23 +3888,23 @@ function truncate(value, max = 1500) {
|
|
|
3820
3888
|
}
|
|
3821
3889
|
function printCost(resp) {
|
|
3822
3890
|
const line = formatBilling(resp.billing);
|
|
3823
|
-
if (line) console.log(
|
|
3891
|
+
if (line) console.log(import_chalk31.default.magenta(` ${line}`) + import_chalk31.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
|
|
3824
3892
|
}
|
|
3825
3893
|
async function runAgent(opts) {
|
|
3826
3894
|
requireAuth();
|
|
3827
3895
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
3828
3896
|
const { default: inquirer3 } = await import("inquirer");
|
|
3829
|
-
console.log(
|
|
3830
|
-
console.log(
|
|
3897
|
+
console.log(import_chalk31.default.bold("APIblaze agent") + import_chalk31.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
3898
|
+
console.log(import_chalk31.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
3831
3899
|
const history = [];
|
|
3832
3900
|
while (true) {
|
|
3833
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
3901
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk31.default.cyan("you") + " \u203A" }]);
|
|
3834
3902
|
const text = (input ?? "").trim();
|
|
3835
3903
|
if (!text) continue;
|
|
3836
3904
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
3837
3905
|
history.push({ role: "user", content: text });
|
|
3838
3906
|
for (let step = 0; step < MAX_TOOL_STEPS; step++) {
|
|
3839
|
-
const spinner = (0,
|
|
3907
|
+
const spinner = (0, import_ora14.default)({ text: "thinking...", color: "magenta" }).start();
|
|
3840
3908
|
let resp;
|
|
3841
3909
|
try {
|
|
3842
3910
|
resp = await callAgent(history, teamId);
|
|
@@ -3844,21 +3912,21 @@ async function runAgent(opts) {
|
|
|
3844
3912
|
} catch (err) {
|
|
3845
3913
|
spinner.stop();
|
|
3846
3914
|
if (err instanceof ApiError && err.status === 402) {
|
|
3847
|
-
console.log(
|
|
3915
|
+
console.log(import_chalk31.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
3848
3916
|
break;
|
|
3849
3917
|
}
|
|
3850
3918
|
throw err;
|
|
3851
3919
|
}
|
|
3852
3920
|
history.push({ role: "assistant", content: resp.raw });
|
|
3853
3921
|
printCost(resp);
|
|
3854
|
-
if (resp.reply) console.log(
|
|
3922
|
+
if (resp.reply) console.log(import_chalk31.default.green("agent") + " \u203A " + resp.reply);
|
|
3855
3923
|
if (!resp.action) break;
|
|
3856
3924
|
const tool = findTool(resp.action.tool);
|
|
3857
3925
|
if (!tool) {
|
|
3858
3926
|
history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
|
|
3859
3927
|
continue;
|
|
3860
3928
|
}
|
|
3861
|
-
const runSpinner = (0,
|
|
3929
|
+
const runSpinner = (0, import_ora14.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
|
|
3862
3930
|
try {
|
|
3863
3931
|
const result = await tool.run(resp.action.args, { teamId });
|
|
3864
3932
|
runSpinner.succeed(`${tool.name} \u2713`);
|
|
@@ -3876,11 +3944,11 @@ async function runAgent(opts) {
|
|
|
3876
3944
|
}
|
|
3877
3945
|
renderTrace();
|
|
3878
3946
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
3879
|
-
console.log(
|
|
3947
|
+
console.log(import_chalk31.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
3880
3948
|
}
|
|
3881
3949
|
}
|
|
3882
3950
|
}
|
|
3883
|
-
console.log(
|
|
3951
|
+
console.log(import_chalk31.default.dim("\nBye."));
|
|
3884
3952
|
}
|
|
3885
3953
|
|
|
3886
3954
|
// src/commands/config-browse.ts
|
|
@@ -4119,11 +4187,11 @@ function dig(blob, dotted) {
|
|
|
4119
4187
|
}
|
|
4120
4188
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
4121
4189
|
function show(v) {
|
|
4122
|
-
if (v === void 0) return
|
|
4123
|
-
if (v === null) return
|
|
4124
|
-
if (typeof v === "object") return
|
|
4125
|
-
if (typeof v === "boolean") return v ?
|
|
4126
|
-
return
|
|
4190
|
+
if (v === void 0) return import_chalk32.default.dim("(unset)");
|
|
4191
|
+
if (v === null) return import_chalk32.default.dim("null");
|
|
4192
|
+
if (typeof v === "object") return import_chalk32.default.cyan(JSON.stringify(v));
|
|
4193
|
+
if (typeof v === "boolean") return v ? import_chalk32.default.green("on") : import_chalk32.default.red("off");
|
|
4194
|
+
return import_chalk32.default.cyan(String(v));
|
|
4127
4195
|
}
|
|
4128
4196
|
function parseValue(raw) {
|
|
4129
4197
|
if (raw === "true") return true;
|
|
@@ -4150,7 +4218,7 @@ async function fetchConfigBlob(proj2) {
|
|
|
4150
4218
|
}
|
|
4151
4219
|
async function patchSetting(proj2, s, value, cfg) {
|
|
4152
4220
|
const body = s.toPatch(value, cfg);
|
|
4153
|
-
const spinner = (0,
|
|
4221
|
+
const spinner = (0, import_ora15.default)(`Set ${s.key}...`).start();
|
|
4154
4222
|
try {
|
|
4155
4223
|
await admin({
|
|
4156
4224
|
method: "PATCH",
|
|
@@ -4165,10 +4233,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
4165
4233
|
}
|
|
4166
4234
|
}
|
|
4167
4235
|
var loginFirst = (what) => {
|
|
4168
|
-
console.log(
|
|
4236
|
+
console.log(import_chalk32.default.yellow(`
|
|
4169
4237
|
Log in first to ${what}.`));
|
|
4170
|
-
console.log(
|
|
4171
|
-
console.log(
|
|
4238
|
+
console.log(import_chalk32.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
4239
|
+
console.log(import_chalk32.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
4172
4240
|
};
|
|
4173
4241
|
async function runConfig(project, key, value, opts) {
|
|
4174
4242
|
const creds = loadCredentials();
|
|
@@ -4185,9 +4253,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
4185
4253
|
}
|
|
4186
4254
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
4187
4255
|
if (!setting) {
|
|
4188
|
-
console.error(
|
|
4189
|
-
console.error(
|
|
4190
|
-
console.error(
|
|
4256
|
+
console.error(import_chalk32.default.red(`Unknown setting "${key}".`));
|
|
4257
|
+
console.error(import_chalk32.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
4258
|
+
console.error(import_chalk32.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
4191
4259
|
process.exit(1);
|
|
4192
4260
|
}
|
|
4193
4261
|
if (value === void 0) {
|
|
@@ -4202,7 +4270,7 @@ async function pickProject(teamId) {
|
|
|
4202
4270
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4203
4271
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
4204
4272
|
if (!projects.length) {
|
|
4205
|
-
console.error(
|
|
4273
|
+
console.error(import_chalk32.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
4206
4274
|
process.exit(1);
|
|
4207
4275
|
}
|
|
4208
4276
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -4210,7 +4278,7 @@ async function pickProject(teamId) {
|
|
|
4210
4278
|
type: "list",
|
|
4211
4279
|
name: "picked",
|
|
4212
4280
|
message: "Which project?",
|
|
4213
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
4281
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk32.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
4214
4282
|
}]);
|
|
4215
4283
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
4216
4284
|
}
|
|
@@ -4221,25 +4289,25 @@ function printAll(proj2, cfg, json) {
|
|
|
4221
4289
|
console.log(JSON.stringify(out, null, 2));
|
|
4222
4290
|
return;
|
|
4223
4291
|
}
|
|
4224
|
-
console.log(
|
|
4292
|
+
console.log(import_chalk32.default.bold(`
|
|
4225
4293
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
4226
4294
|
`));
|
|
4227
4295
|
for (const group2 of SETTING_GROUPS) {
|
|
4228
|
-
console.log(
|
|
4296
|
+
console.log(import_chalk32.default.bold(group2));
|
|
4229
4297
|
for (const s of SETTINGS.filter((x) => x.group === group2)) {
|
|
4230
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
4298
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk32.default.dim(s.desc)}`);
|
|
4231
4299
|
}
|
|
4232
4300
|
console.log();
|
|
4233
4301
|
}
|
|
4234
|
-
console.log(
|
|
4302
|
+
console.log(import_chalk32.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
4235
4303
|
}
|
|
4236
4304
|
async function discoveryMenu(project) {
|
|
4237
4305
|
const { default: inquirer3 } = await import("inquirer");
|
|
4238
|
-
console.log(
|
|
4306
|
+
console.log(import_chalk32.default.bold(`
|
|
4239
4307
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
4240
4308
|
`));
|
|
4241
|
-
console.log(
|
|
4242
|
-
console.log(
|
|
4309
|
+
console.log(import_chalk32.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
4310
|
+
console.log(import_chalk32.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
4243
4311
|
for (; ; ) {
|
|
4244
4312
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
4245
4313
|
type: "list",
|
|
@@ -4247,13 +4315,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4247
4315
|
message: "Explore:",
|
|
4248
4316
|
pageSize: 20,
|
|
4249
4317
|
choices: [
|
|
4250
|
-
new inquirer3.Separator(
|
|
4318
|
+
new inquirer3.Separator(import_chalk32.default.bold("\u2014 Settings \u2014")),
|
|
4251
4319
|
...SETTING_GROUPS.map((g) => ({
|
|
4252
|
-
name: `${g} ${
|
|
4320
|
+
name: `${g} ${import_chalk32.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
4253
4321
|
value: { kind: "settings", g }
|
|
4254
4322
|
})),
|
|
4255
|
-
new inquirer3.Separator(
|
|
4256
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4323
|
+
new inquirer3.Separator(import_chalk32.default.bold("\u2014 Features \u2014")),
|
|
4324
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk32.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
4257
4325
|
new inquirer3.Separator(),
|
|
4258
4326
|
{ name: "Exit", value: { kind: "exit" } }
|
|
4259
4327
|
]
|
|
@@ -4262,24 +4330,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4262
4330
|
if (pick2.kind === "settings") {
|
|
4263
4331
|
console.log();
|
|
4264
4332
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
4265
|
-
console.log(` ${
|
|
4266
|
-
console.log(` ${
|
|
4333
|
+
console.log(` ${import_chalk32.default.bold(s.label.padEnd(28))} ${import_chalk32.default.dim(s.desc)}`);
|
|
4334
|
+
console.log(` ${import_chalk32.default.dim(" key: " + s.key)}`);
|
|
4267
4335
|
}
|
|
4268
4336
|
loginFirst("view or change these settings");
|
|
4269
4337
|
} else {
|
|
4270
4338
|
const f = pick2.f;
|
|
4271
4339
|
console.log(`
|
|
4272
|
-
${
|
|
4340
|
+
${import_chalk32.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
4273
4341
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
4274
4342
|
}
|
|
4275
4343
|
}
|
|
4276
4344
|
}
|
|
4277
4345
|
async function navigator(proj2, cfg, opts) {
|
|
4278
4346
|
const { default: inquirer3 } = await import("inquirer");
|
|
4279
|
-
console.log(
|
|
4347
|
+
console.log(import_chalk32.default.bold(`
|
|
4280
4348
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
4281
4349
|
`));
|
|
4282
|
-
console.log(
|
|
4350
|
+
console.log(import_chalk32.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
4283
4351
|
let blob = cfg;
|
|
4284
4352
|
for (; ; ) {
|
|
4285
4353
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
@@ -4288,10 +4356,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4288
4356
|
message: "Where to?",
|
|
4289
4357
|
pageSize: 20,
|
|
4290
4358
|
choices: [
|
|
4291
|
-
new inquirer3.Separator(
|
|
4359
|
+
new inquirer3.Separator(import_chalk32.default.bold("\u2014 Settings \u2014")),
|
|
4292
4360
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
4293
|
-
new inquirer3.Separator(
|
|
4294
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4361
|
+
new inquirer3.Separator(import_chalk32.default.bold("\u2014 Features \u2014")),
|
|
4362
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk32.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
4295
4363
|
new inquirer3.Separator(),
|
|
4296
4364
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
4297
4365
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -4331,7 +4399,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4331
4399
|
}
|
|
4332
4400
|
}
|
|
4333
4401
|
} catch (err) {
|
|
4334
|
-
console.error(
|
|
4402
|
+
console.error(import_chalk32.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
4335
4403
|
}
|
|
4336
4404
|
}
|
|
4337
4405
|
}
|
|
@@ -4345,7 +4413,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
4345
4413
|
message: group2 + ":",
|
|
4346
4414
|
pageSize: 16,
|
|
4347
4415
|
choices: [
|
|
4348
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
4416
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk32.default.dim(s2.desc)}`, value: s2 })),
|
|
4349
4417
|
new inquirer3.Separator(),
|
|
4350
4418
|
{ name: "\u2190 Back", value: null }
|
|
4351
4419
|
]
|
|
@@ -4363,7 +4431,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
4363
4431
|
} else if (s.type === "number") {
|
|
4364
4432
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
4365
4433
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
4366
|
-
console.log(
|
|
4434
|
+
console.log(import_chalk32.default.yellow(" Not a number \u2014 unchanged."));
|
|
4367
4435
|
continue;
|
|
4368
4436
|
}
|
|
4369
4437
|
value = Number(v);
|
|
@@ -4444,7 +4512,7 @@ async function buildCondition(phase) {
|
|
|
4444
4512
|
const items = [];
|
|
4445
4513
|
for (; ; ) {
|
|
4446
4514
|
const a = await inquirer3.prompt([
|
|
4447
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
4515
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk32.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
4448
4516
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
4449
4517
|
"eq",
|
|
4450
4518
|
"neq",
|
|
@@ -4475,7 +4543,7 @@ async function buildCondition(phase) {
|
|
|
4475
4543
|
function showCondition(cond) {
|
|
4476
4544
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
4477
4545
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
4478
|
-
return
|
|
4546
|
+
return import_chalk32.default.dim(` when ${s}`);
|
|
4479
4547
|
}
|
|
4480
4548
|
async function transformsMenu(proj2) {
|
|
4481
4549
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -4484,12 +4552,12 @@ async function transformsMenu(proj2) {
|
|
|
4484
4552
|
const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
|
|
4485
4553
|
const rules = out?.rules ?? [];
|
|
4486
4554
|
console.log();
|
|
4487
|
-
if (!rules.length) console.log(
|
|
4555
|
+
if (!rules.length) console.log(import_chalk32.default.dim(" No transform rules yet."));
|
|
4488
4556
|
for (const r of rules) {
|
|
4489
4557
|
const a = r.action ?? {};
|
|
4490
4558
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
4491
|
-
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 ?
|
|
4492
|
-
console.log(` ${r.enabled ?
|
|
4559
|
+
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_chalk32.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
|
|
4560
|
+
console.log(` ${r.enabled ? import_chalk32.default.green("\u25CF") : import_chalk32.default.dim("\u25CB")} ${import_chalk32.default.bold(r.name)} ${import_chalk32.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
|
|
4493
4561
|
}
|
|
4494
4562
|
const { act } = await inquirer3.prompt([{
|
|
4495
4563
|
type: "list",
|
|
@@ -4501,7 +4569,7 @@ async function transformsMenu(proj2) {
|
|
|
4501
4569
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
4502
4570
|
{ name: "Delete a rule", value: "delete" }
|
|
4503
4571
|
] : [],
|
|
4504
|
-
{ name:
|
|
4572
|
+
{ name: import_chalk32.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
4505
4573
|
{ name: "\u2190 Back", value: "back" }
|
|
4506
4574
|
]
|
|
4507
4575
|
}]);
|
|
@@ -4514,11 +4582,11 @@ async function transformsMenu(proj2) {
|
|
|
4514
4582
|
}]);
|
|
4515
4583
|
const body = parseValue(raw);
|
|
4516
4584
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
4517
|
-
console.log(
|
|
4585
|
+
console.log(import_chalk32.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
4518
4586
|
continue;
|
|
4519
4587
|
}
|
|
4520
4588
|
await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
|
|
4521
|
-
console.log(
|
|
4589
|
+
console.log(import_chalk32.default.green(" Rule created."));
|
|
4522
4590
|
continue;
|
|
4523
4591
|
}
|
|
4524
4592
|
if (act === "add") {
|
|
@@ -4534,7 +4602,7 @@ async function transformsMenu(proj2) {
|
|
|
4534
4602
|
{ name: "Remove a field", value: "remove" }
|
|
4535
4603
|
] }
|
|
4536
4604
|
]);
|
|
4537
|
-
const fieldHint =
|
|
4605
|
+
const fieldHint = import_chalk32.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
4538
4606
|
let action2;
|
|
4539
4607
|
if (ans.kind === "hardcode") {
|
|
4540
4608
|
const a = await inquirer3.prompt([
|
|
@@ -4563,7 +4631,7 @@ async function transformsMenu(proj2) {
|
|
|
4563
4631
|
};
|
|
4564
4632
|
}
|
|
4565
4633
|
const condition = await buildCondition(ans.phase);
|
|
4566
|
-
const spinner = (0,
|
|
4634
|
+
const spinner = (0, import_ora15.default)("Creating rule...").start();
|
|
4567
4635
|
try {
|
|
4568
4636
|
await admin({
|
|
4569
4637
|
method: "POST",
|
|
@@ -4581,16 +4649,16 @@ async function transformsMenu(proj2) {
|
|
|
4581
4649
|
type: "list",
|
|
4582
4650
|
name: "rule",
|
|
4583
4651
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
4584
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
4652
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk32.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
4585
4653
|
}]);
|
|
4586
4654
|
if (!rule) continue;
|
|
4587
4655
|
if (act === "toggle") {
|
|
4588
4656
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
4589
4657
|
await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
4590
|
-
console.log(
|
|
4658
|
+
console.log(import_chalk32.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
4591
4659
|
} else {
|
|
4592
4660
|
await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
4593
|
-
console.log(
|
|
4661
|
+
console.log(import_chalk32.default.green(` ${rule.name} deleted.`));
|
|
4594
4662
|
}
|
|
4595
4663
|
}
|
|
4596
4664
|
}
|
|
@@ -4602,9 +4670,9 @@ async function mappingsMenu(proj2) {
|
|
|
4602
4670
|
const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
|
|
4603
4671
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
4604
4672
|
console.log();
|
|
4605
|
-
if (!tables.length) console.log(
|
|
4673
|
+
if (!tables.length) console.log(import_chalk32.default.dim(" No mapping tables yet."));
|
|
4606
4674
|
for (const t of tables) {
|
|
4607
|
-
console.log(` ${
|
|
4675
|
+
console.log(` ${import_chalk32.default.bold(t.name)} ${import_chalk32.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
|
|
4608
4676
|
}
|
|
4609
4677
|
const { act } = await inquirer3.prompt([{
|
|
4610
4678
|
type: "list",
|
|
@@ -4624,11 +4692,11 @@ async function mappingsMenu(proj2) {
|
|
|
4624
4692
|
]);
|
|
4625
4693
|
const entries2 = parseValue(a.entries);
|
|
4626
4694
|
if (!Array.isArray(entries2)) {
|
|
4627
|
-
console.log(
|
|
4695
|
+
console.log(import_chalk32.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
4628
4696
|
continue;
|
|
4629
4697
|
}
|
|
4630
4698
|
await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
4631
|
-
console.log(
|
|
4699
|
+
console.log(import_chalk32.default.green(` Table "${a.name}" created.`));
|
|
4632
4700
|
} else {
|
|
4633
4701
|
const { table } = await inquirer3.prompt([{
|
|
4634
4702
|
type: "list",
|
|
@@ -4638,7 +4706,7 @@ async function mappingsMenu(proj2) {
|
|
|
4638
4706
|
}]);
|
|
4639
4707
|
if (!table) continue;
|
|
4640
4708
|
await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
4641
|
-
console.log(
|
|
4709
|
+
console.log(import_chalk32.default.green(` ${table.name} deleted.`));
|
|
4642
4710
|
}
|
|
4643
4711
|
}
|
|
4644
4712
|
}
|
|
@@ -4649,14 +4717,14 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4649
4717
|
const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
|
|
4650
4718
|
const tenants = out?.tenants ?? [];
|
|
4651
4719
|
console.log();
|
|
4652
|
-
if (!tenants.length) console.log(
|
|
4653
|
-
for (const t of tenants) console.log(` ${
|
|
4720
|
+
if (!tenants.length) console.log(import_chalk32.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
4721
|
+
for (const t of tenants) console.log(` ${import_chalk32.default.bold(t.tenant_name ?? t.name)} ${import_chalk32.default.dim(t.display_name ?? "")}`);
|
|
4654
4722
|
const { act } = await inquirer3.prompt([{
|
|
4655
4723
|
type: "list",
|
|
4656
4724
|
name: "act",
|
|
4657
4725
|
message: "Tenants:",
|
|
4658
4726
|
choices: [
|
|
4659
|
-
{ name: `Manage a tenant\u2026 ${
|
|
4727
|
+
{ name: `Manage a tenant\u2026 ${import_chalk32.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
4660
4728
|
{ name: "Attach a tenant to this project", value: "attach" },
|
|
4661
4729
|
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
4662
4730
|
{ name: "\u2190 Back", value: "back" }
|
|
@@ -4679,7 +4747,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4679
4747
|
}]);
|
|
4680
4748
|
if (!t) continue;
|
|
4681
4749
|
await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
4682
|
-
console.log(
|
|
4750
|
+
console.log(import_chalk32.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
4683
4751
|
}
|
|
4684
4752
|
}
|
|
4685
4753
|
}
|
|
@@ -4722,7 +4790,7 @@ async function specMenu(proj2, opts) {
|
|
|
4722
4790
|
choices: [
|
|
4723
4791
|
{ name: "Print the stored spec", value: "get" },
|
|
4724
4792
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
4725
|
-
{ name:
|
|
4793
|
+
{ name: import_chalk32.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
4726
4794
|
{ name: "\u2190 Back", value: "back" }
|
|
4727
4795
|
]
|
|
4728
4796
|
}]);
|
|
@@ -4730,7 +4798,7 @@ async function specMenu(proj2, opts) {
|
|
|
4730
4798
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
4731
4799
|
else if (act === "refresh") {
|
|
4732
4800
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
4733
|
-
console.log(
|
|
4801
|
+
console.log(import_chalk32.default.green(" Spec refresh triggered."));
|
|
4734
4802
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
4735
4803
|
}
|
|
4736
4804
|
async function agentsMenu(proj2, opts) {
|
|
@@ -4755,8 +4823,8 @@ async function agentsMenu(proj2, opts) {
|
|
|
4755
4823
|
}
|
|
4756
4824
|
|
|
4757
4825
|
// src/commands/group.ts
|
|
4758
|
-
var
|
|
4759
|
-
var
|
|
4826
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
4827
|
+
var import_ora16 = __toESM(require("ora"));
|
|
4760
4828
|
init_auth();
|
|
4761
4829
|
init_admin();
|
|
4762
4830
|
init_types();
|
|
@@ -4845,11 +4913,11 @@ async function runGroupList(opts) {
|
|
|
4845
4913
|
const t = await resolveTransport(opts);
|
|
4846
4914
|
const groups = await listGroups(t);
|
|
4847
4915
|
if (opts.json) return void console.log(JSON.stringify(groups));
|
|
4848
|
-
if (!groups.length) return void console.log(
|
|
4849
|
-
console.log(
|
|
4916
|
+
if (!groups.length) return void console.log(import_chalk33.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
|
|
4917
|
+
console.log(import_chalk33.default.dim(`Tenant ${t.tenant}:`));
|
|
4850
4918
|
for (const g of groups) {
|
|
4851
|
-
const sealed = g.name === "apiblaze_admins" ?
|
|
4852
|
-
console.log(` ${
|
|
4919
|
+
const sealed = g.name === "apiblaze_admins" ? import_chalk33.default.dim(" (admins \u2014 managed by allowlist)") : "";
|
|
4920
|
+
console.log(` ${import_chalk33.default.bold(g.name)} ${import_chalk33.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
|
|
4853
4921
|
}
|
|
4854
4922
|
}
|
|
4855
4923
|
async function runGroupCreate(name, opts) {
|
|
@@ -4874,10 +4942,10 @@ async function runGroupCreate(name, opts) {
|
|
|
4874
4942
|
}
|
|
4875
4943
|
}
|
|
4876
4944
|
}
|
|
4877
|
-
const spinner = (0,
|
|
4945
|
+
const spinner = (0, import_ora16.default)(`Creating group ${name}...`).start();
|
|
4878
4946
|
try {
|
|
4879
4947
|
await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
|
|
4880
|
-
spinner.succeed(`Group ${
|
|
4948
|
+
spinner.succeed(`Group ${import_chalk33.default.bold(name)} created in ${t.tenant}.`);
|
|
4881
4949
|
} catch (e) {
|
|
4882
4950
|
spinner.fail("Create failed.");
|
|
4883
4951
|
throw e;
|
|
@@ -4886,10 +4954,10 @@ async function runGroupCreate(name, opts) {
|
|
|
4886
4954
|
async function runGroupDelete(name, opts) {
|
|
4887
4955
|
const t = await resolveTransport(opts);
|
|
4888
4956
|
const g = await groupByName(t, name);
|
|
4889
|
-
const spinner = (0,
|
|
4957
|
+
const spinner = (0, import_ora16.default)(`Deleting group ${name}...`).start();
|
|
4890
4958
|
try {
|
|
4891
4959
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
|
|
4892
|
-
spinner.succeed(`Group ${
|
|
4960
|
+
spinner.succeed(`Group ${import_chalk33.default.bold(name)} deleted.`);
|
|
4893
4961
|
} catch (e) {
|
|
4894
4962
|
spinner.fail("Delete failed.");
|
|
4895
4963
|
throw e;
|
|
@@ -4902,20 +4970,20 @@ async function runGroupMembers(name, opts) {
|
|
|
4902
4970
|
const subs = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/subgroups`);
|
|
4903
4971
|
if (opts.json) return void console.log(JSON.stringify({ ...detail, subgroups: subs?.subgroups ?? [] }));
|
|
4904
4972
|
const members = detail?.members ?? [];
|
|
4905
|
-
console.log(
|
|
4906
|
-
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ?
|
|
4907
|
-
if (!members.length) console.log(
|
|
4973
|
+
console.log(import_chalk33.default.bold(`${g.name}`) + import_chalk33.default.dim(` (${t.tenant})`));
|
|
4974
|
+
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk33.default.cyan("group-admin") : import_chalk33.default.dim("member")}`);
|
|
4975
|
+
if (!members.length) console.log(import_chalk33.default.dim(" no members"));
|
|
4908
4976
|
const sg = subs?.subgroups ?? [];
|
|
4909
|
-
if (sg.length) console.log(
|
|
4977
|
+
if (sg.length) console.log(import_chalk33.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
|
|
4910
4978
|
}
|
|
4911
4979
|
async function runGroupAddUser(userRef, groupName, opts) {
|
|
4912
4980
|
const t = await resolveTransport(opts);
|
|
4913
4981
|
const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
|
|
4914
4982
|
const role = opts.role === "admin" ? "admin" : "member";
|
|
4915
|
-
const spinner = (0,
|
|
4983
|
+
const spinner = (0, import_ora16.default)(`Adding ${label2(u)} to ${g.name}...`).start();
|
|
4916
4984
|
try {
|
|
4917
4985
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
|
|
4918
|
-
spinner.succeed(`${
|
|
4986
|
+
spinner.succeed(`${import_chalk33.default.bold(label2(u))} added to ${import_chalk33.default.bold(g.name)} as ${role}.`);
|
|
4919
4987
|
} catch (e) {
|
|
4920
4988
|
spinner.fail("Add failed.");
|
|
4921
4989
|
throw e;
|
|
@@ -4924,10 +4992,10 @@ async function runGroupAddUser(userRef, groupName, opts) {
|
|
|
4924
4992
|
async function runGroupRemoveUser(userRef, groupName, opts) {
|
|
4925
4993
|
const t = await resolveTransport(opts);
|
|
4926
4994
|
const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
|
|
4927
|
-
const spinner = (0,
|
|
4995
|
+
const spinner = (0, import_ora16.default)(`Removing ${label2(u)} from ${g.name}...`).start();
|
|
4928
4996
|
try {
|
|
4929
4997
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
|
|
4930
|
-
spinner.succeed(`${
|
|
4998
|
+
spinner.succeed(`${import_chalk33.default.bold(label2(u))} removed from ${import_chalk33.default.bold(g.name)}.`);
|
|
4931
4999
|
} catch (e) {
|
|
4932
5000
|
spinner.fail("Remove failed.");
|
|
4933
5001
|
throw e;
|
|
@@ -4936,10 +5004,10 @@ async function runGroupRemoveUser(userRef, groupName, opts) {
|
|
|
4936
5004
|
async function runGroupAddGroup(childName, parentName, opts) {
|
|
4937
5005
|
const t = await resolveTransport(opts);
|
|
4938
5006
|
const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
|
|
4939
|
-
const spinner = (0,
|
|
5007
|
+
const spinner = (0, import_ora16.default)(`Nesting ${child.name} under ${parent.name}...`).start();
|
|
4940
5008
|
try {
|
|
4941
5009
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
|
|
4942
|
-
spinner.succeed(`${
|
|
5010
|
+
spinner.succeed(`${import_chalk33.default.bold(child.name)} is now a subgroup of ${import_chalk33.default.bold(parent.name)}.`);
|
|
4943
5011
|
} catch (e) {
|
|
4944
5012
|
spinner.fail("Nest failed.");
|
|
4945
5013
|
throw e;
|
|
@@ -4948,10 +5016,10 @@ async function runGroupAddGroup(childName, parentName, opts) {
|
|
|
4948
5016
|
async function runGroupRemoveGroup(childName, parentName, opts) {
|
|
4949
5017
|
const t = await resolveTransport(opts);
|
|
4950
5018
|
const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
|
|
4951
|
-
const spinner = (0,
|
|
5019
|
+
const spinner = (0, import_ora16.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
|
|
4952
5020
|
try {
|
|
4953
5021
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
|
|
4954
|
-
spinner.succeed(`${
|
|
5022
|
+
spinner.succeed(`${import_chalk33.default.bold(child.name)} un-nested from ${import_chalk33.default.bold(parent.name)}.`);
|
|
4955
5023
|
} catch (e) {
|
|
4956
5024
|
spinner.fail("Un-nest failed.");
|
|
4957
5025
|
throw e;
|
|
@@ -4962,19 +5030,19 @@ async function runGroupUsers(opts) {
|
|
|
4962
5030
|
const users = await listUsers(t);
|
|
4963
5031
|
const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
|
|
4964
5032
|
if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
|
|
4965
|
-
console.log(
|
|
4966
|
-
for (const u of users) console.log(` ${label2(u)} ${
|
|
4967
|
-
if (!users.length) console.log(
|
|
5033
|
+
console.log(import_chalk33.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
|
|
5034
|
+
for (const u of users) console.log(` ${label2(u)} ${import_chalk33.default.dim(u.abz_sub)}`);
|
|
5035
|
+
if (!users.length) console.log(import_chalk33.default.dim(" no users yet"));
|
|
4968
5036
|
const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
|
|
4969
5037
|
if (fresh.length) {
|
|
4970
|
-
console.log(
|
|
4971
|
-
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${
|
|
5038
|
+
console.log(import_chalk33.default.dim("Seen in traffic (not yet users):"));
|
|
5039
|
+
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk33.default.dim(o.source_label ?? "")}`);
|
|
4972
5040
|
}
|
|
4973
5041
|
}
|
|
4974
5042
|
|
|
4975
5043
|
// src/commands/key.ts
|
|
4976
|
-
var
|
|
4977
|
-
var
|
|
5044
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
5045
|
+
var import_ora17 = __toESM(require("ora"));
|
|
4978
5046
|
init_admin();
|
|
4979
5047
|
async function runApikeysMenu(opts) {
|
|
4980
5048
|
await runKeyList(opts);
|
|
@@ -5002,11 +5070,11 @@ async function runKeyList(opts) {
|
|
|
5002
5070
|
return;
|
|
5003
5071
|
}
|
|
5004
5072
|
if (!keys.length) {
|
|
5005
|
-
console.log(
|
|
5073
|
+
console.log(import_chalk34.default.yellow("No developer keys."));
|
|
5006
5074
|
return;
|
|
5007
5075
|
}
|
|
5008
5076
|
for (const k of keys) {
|
|
5009
|
-
console.log(` ${
|
|
5077
|
+
console.log(` ${import_chalk34.default.bold(k.key_id ?? k.id)} ${import_chalk34.default.dim(k.description ?? "")} ${import_chalk34.default.dim(k.expires_at ?? "no expiry")}`);
|
|
5010
5078
|
}
|
|
5011
5079
|
}
|
|
5012
5080
|
async function runKeyMint(opts) {
|
|
@@ -5014,7 +5082,7 @@ async function runKeyMint(opts) {
|
|
|
5014
5082
|
const body = { role: "consumer-admin" };
|
|
5015
5083
|
if (opts.desc) body.description = opts.desc;
|
|
5016
5084
|
if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
|
|
5017
|
-
const spinner = (0,
|
|
5085
|
+
const spinner = (0, import_ora17.default)("Minting key...").start();
|
|
5018
5086
|
try {
|
|
5019
5087
|
const out = await admin({
|
|
5020
5088
|
method: "POST",
|
|
@@ -5027,9 +5095,9 @@ async function runKeyMint(opts) {
|
|
|
5027
5095
|
console.log(JSON.stringify(out));
|
|
5028
5096
|
return;
|
|
5029
5097
|
}
|
|
5030
|
-
console.log(` ${
|
|
5031
|
-
console.log(` ${
|
|
5032
|
-
if (out?.expires_at) console.log(` ${
|
|
5098
|
+
console.log(` ${import_chalk34.default.bold("key_id")}: ${out?.key_id}`);
|
|
5099
|
+
console.log(` ${import_chalk34.default.bold("key")}: ${import_chalk34.default.green(out?.key)} ${import_chalk34.default.dim("(shown once \u2014 store it now)")}`);
|
|
5100
|
+
if (out?.expires_at) console.log(` ${import_chalk34.default.dim("expires:")} ${out.expires_at}`);
|
|
5033
5101
|
} catch (err) {
|
|
5034
5102
|
spinner.fail("Mint failed.");
|
|
5035
5103
|
throw err;
|
|
@@ -5037,7 +5105,7 @@ async function runKeyMint(opts) {
|
|
|
5037
5105
|
}
|
|
5038
5106
|
async function runKeyRevoke(keyId, opts) {
|
|
5039
5107
|
const { teamId } = await resolveTeam(opts.team);
|
|
5040
|
-
const spinner = (0,
|
|
5108
|
+
const spinner = (0, import_ora17.default)("Revoking key...").start();
|
|
5041
5109
|
try {
|
|
5042
5110
|
await admin({
|
|
5043
5111
|
method: "DELETE",
|
|
@@ -5055,8 +5123,8 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
5055
5123
|
var fs9 = __toESM(require("fs"));
|
|
5056
5124
|
var path6 = __toESM(require("path"));
|
|
5057
5125
|
var crypto2 = __toESM(require("crypto"));
|
|
5058
|
-
var
|
|
5059
|
-
var
|
|
5126
|
+
var import_chalk36 = __toESM(require("chalk"));
|
|
5127
|
+
var import_ora18 = __toESM(require("ora"));
|
|
5060
5128
|
var import_yaml = require("yaml");
|
|
5061
5129
|
init_auth();
|
|
5062
5130
|
init_anon_cred();
|
|
@@ -5066,7 +5134,7 @@ init_admin();
|
|
|
5066
5134
|
// src/commands/llm.ts
|
|
5067
5135
|
var fs8 = __toESM(require("fs"));
|
|
5068
5136
|
var path5 = __toESM(require("path"));
|
|
5069
|
-
var
|
|
5137
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
5070
5138
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
5071
5139
|
init_auth();
|
|
5072
5140
|
var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
|
|
@@ -5110,27 +5178,27 @@ async function runLlmSetKey(keyArg, opts) {
|
|
|
5110
5178
|
}
|
|
5111
5179
|
const existing = loadLlmConfig();
|
|
5112
5180
|
saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
|
|
5113
|
-
console.log(`${
|
|
5114
|
-
console.log(
|
|
5115
|
-
if (opts.model) console.log(
|
|
5181
|
+
console.log(`${import_chalk35.default.green("\u2713")} Saved ${import_chalk35.default.bold(provider)} key ${maskSecret(key)}`);
|
|
5182
|
+
console.log(import_chalk35.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
|
|
5183
|
+
if (opts.model) console.log(import_chalk35.default.gray(` Model: ${opts.model}`));
|
|
5116
5184
|
}
|
|
5117
5185
|
async function runLlmShow() {
|
|
5118
5186
|
const cfg = loadLlmConfig();
|
|
5119
5187
|
if (!cfg) {
|
|
5120
|
-
console.log(
|
|
5188
|
+
console.log(import_chalk35.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
|
|
5121
5189
|
return;
|
|
5122
5190
|
}
|
|
5123
|
-
console.log(`Provider: ${
|
|
5191
|
+
console.log(`Provider: ${import_chalk35.default.bold(cfg.provider)}`);
|
|
5124
5192
|
console.log(`Key: ${maskSecret(cfg.key)}`);
|
|
5125
5193
|
if (cfg.model) console.log(`Model: ${cfg.model}`);
|
|
5126
|
-
console.log(
|
|
5194
|
+
console.log(import_chalk35.default.gray(`Stored at ${LLM_PATH}`));
|
|
5127
5195
|
}
|
|
5128
5196
|
async function runLlmClearKey() {
|
|
5129
5197
|
try {
|
|
5130
5198
|
fs8.unlinkSync(LLM_PATH);
|
|
5131
|
-
console.log(`${
|
|
5199
|
+
console.log(`${import_chalk35.default.green("\u2713")} Removed local LLM key.`);
|
|
5132
5200
|
} catch {
|
|
5133
|
-
console.log(
|
|
5201
|
+
console.log(import_chalk35.default.gray("No LLM key was set."));
|
|
5134
5202
|
}
|
|
5135
5203
|
}
|
|
5136
5204
|
|
|
@@ -5138,9 +5206,9 @@ async function runLlmClearKey() {
|
|
|
5138
5206
|
init_trace();
|
|
5139
5207
|
init_types();
|
|
5140
5208
|
function fail4(message, hint) {
|
|
5141
|
-
console.error(
|
|
5209
|
+
console.error(import_chalk36.default.red(`
|
|
5142
5210
|
Error: ${message}`));
|
|
5143
|
-
if (hint) console.error(
|
|
5211
|
+
if (hint) console.error(import_chalk36.default.dim(hint));
|
|
5144
5212
|
process.exit(1);
|
|
5145
5213
|
}
|
|
5146
5214
|
function normalizeName2(raw) {
|
|
@@ -5288,7 +5356,7 @@ async function resolveTargetAuth(spec2, opts) {
|
|
|
5288
5356
|
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
5289
5357
|
);
|
|
5290
5358
|
}
|
|
5291
|
-
if (sawOAuth) console.log(
|
|
5359
|
+
if (sawOAuth) console.log(import_chalk36.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
|
|
5292
5360
|
return null;
|
|
5293
5361
|
}
|
|
5294
5362
|
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
@@ -5365,7 +5433,7 @@ async function provision(spec2, target, opts) {
|
|
|
5365
5433
|
if (!base && spec2.info && typeof spec2.info.title === "string") base = normalizeName2(spec2.info.title);
|
|
5366
5434
|
if (!base || base.length < 3) base = "apichat";
|
|
5367
5435
|
let name = opts.name ? base : `${base}${salt()}`;
|
|
5368
|
-
const spinner = (0,
|
|
5436
|
+
const spinner = (0, import_ora18.default)("Provisioning an api_key proxy...").start();
|
|
5369
5437
|
let result;
|
|
5370
5438
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
5371
5439
|
try {
|
|
@@ -5445,7 +5513,7 @@ async function provision(spec2, target, opts) {
|
|
|
5445
5513
|
return { projectId, version: version2, environment, dpKey, mcpHost, proxyUrl, anon };
|
|
5446
5514
|
}
|
|
5447
5515
|
async function writeTargetAuth(p, auth, secret) {
|
|
5448
|
-
const spinner = (0,
|
|
5516
|
+
const spinner = (0, import_ora18.default)("Storing target credentials (encrypted)...").start();
|
|
5449
5517
|
try {
|
|
5450
5518
|
await cpPost(
|
|
5451
5519
|
p.anon,
|
|
@@ -5467,7 +5535,7 @@ async function writeTargetAuth(p, auth, secret) {
|
|
|
5467
5535
|
}
|
|
5468
5536
|
}
|
|
5469
5537
|
async function uploadSpec(p, specText, opts) {
|
|
5470
|
-
const spinner = (0,
|
|
5538
|
+
const spinner = (0, import_ora18.default)("Uploading the spec...").start();
|
|
5471
5539
|
let out;
|
|
5472
5540
|
try {
|
|
5473
5541
|
out = await cpPost(
|
|
@@ -5482,7 +5550,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5482
5550
|
throw err;
|
|
5483
5551
|
}
|
|
5484
5552
|
if (out && out.reused === true) {
|
|
5485
|
-
console.log(
|
|
5553
|
+
console.log(import_chalk36.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
|
|
5486
5554
|
} else if (out && out.changed === true && out.previous_spec_hash) {
|
|
5487
5555
|
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
5488
5556
|
if (interactive) {
|
|
@@ -5490,12 +5558,12 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5490
5558
|
const { go } = await inquirer3.prompt([
|
|
5491
5559
|
{ type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
|
|
5492
5560
|
]);
|
|
5493
|
-
if (!go) console.log(
|
|
5561
|
+
if (!go) console.log(import_chalk36.default.dim(" Keeping the existing MCP catalogue."));
|
|
5494
5562
|
}
|
|
5495
5563
|
}
|
|
5496
5564
|
}
|
|
5497
5565
|
async function publishMcp(p, spec2) {
|
|
5498
|
-
const spinner = (0,
|
|
5566
|
+
const spinner = (0, import_ora18.default)("Publishing the MCP catalogue...").start();
|
|
5499
5567
|
try {
|
|
5500
5568
|
const url = `https://${p.mcpHost}/${p.version}/${p.environment}/mcp/generate`;
|
|
5501
5569
|
const res = await fetch(url, {
|
|
@@ -5531,14 +5599,14 @@ var revealAuth = false;
|
|
|
5531
5599
|
function renderToolEvents(events, dpKey) {
|
|
5532
5600
|
for (const e of events ?? []) {
|
|
5533
5601
|
const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
|
|
5534
|
-
const mark = ok ?
|
|
5535
|
-
console.log(` ${mark} ${
|
|
5602
|
+
const mark = ok ? import_chalk36.default.green("\u2713") : import_chalk36.default.red("\u2717");
|
|
5603
|
+
console.log(` ${mark} ${import_chalk36.default.cyan(e.name)} ${import_chalk36.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
|
|
5536
5604
|
if (isVerbose() && e.method && e.url) {
|
|
5537
|
-
console.log(
|
|
5605
|
+
console.log(import_chalk36.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
|
|
5538
5606
|
if (dpKey) {
|
|
5539
5607
|
const keyLine = ` -H 'X-API-Key: ${revealAuth ? dpKey : maskKey(dpKey)}'`;
|
|
5540
|
-
const hint = revealAuth ? "" :
|
|
5541
|
-
console.log(
|
|
5608
|
+
const hint = revealAuth ? "" : import_chalk36.default.yellow(" \u2190 /showauth will reveal this");
|
|
5609
|
+
console.log(import_chalk36.default.dim(keyLine) + hint);
|
|
5542
5610
|
}
|
|
5543
5611
|
}
|
|
5544
5612
|
}
|
|
@@ -5546,9 +5614,9 @@ function renderToolEvents(events, dpKey) {
|
|
|
5546
5614
|
function billingLine(billing) {
|
|
5547
5615
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
5548
5616
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
5549
|
-
let line =
|
|
5617
|
+
let line = import_chalk36.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk36.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
5550
5618
|
if (typeof billing.credits_remaining === "number") {
|
|
5551
|
-
line +=
|
|
5619
|
+
line += import_chalk36.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
5552
5620
|
}
|
|
5553
5621
|
return line;
|
|
5554
5622
|
}
|
|
@@ -5557,14 +5625,14 @@ function freeBudgetWarning(billing, anon) {
|
|
|
5557
5625
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
5558
5626
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
5559
5627
|
if (left > 8) return null;
|
|
5560
|
-
if (left <= 0) return
|
|
5561
|
-
return
|
|
5628
|
+
if (left <= 0) return import_chalk36.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
5629
|
+
return import_chalk36.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
5562
5630
|
}
|
|
5563
5631
|
function printAssistant(delta) {
|
|
5564
5632
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
5565
5633
|
const m = delta[i];
|
|
5566
5634
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
5567
|
-
console.log("\n" +
|
|
5635
|
+
console.log("\n" + import_chalk36.default.green("assistant \u203A ") + m.content + "\n");
|
|
5568
5636
|
return;
|
|
5569
5637
|
}
|
|
5570
5638
|
}
|
|
@@ -5574,7 +5642,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5574
5642
|
const llm2 = loadLlmConfig();
|
|
5575
5643
|
const turnId = crypto2.randomUUID();
|
|
5576
5644
|
for (let round = 0; round < CLIENT_ROUND_CAP; round++) {
|
|
5577
|
-
const spinner = (0,
|
|
5645
|
+
const spinner = (0, import_ora18.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
|
|
5578
5646
|
const body = {
|
|
5579
5647
|
turn_id: turnId,
|
|
5580
5648
|
messages,
|
|
@@ -5590,7 +5658,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5590
5658
|
});
|
|
5591
5659
|
} catch (err) {
|
|
5592
5660
|
spinner.fail("Network error.");
|
|
5593
|
-
console.log(
|
|
5661
|
+
console.log(import_chalk36.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
5594
5662
|
return;
|
|
5595
5663
|
}
|
|
5596
5664
|
let data = null;
|
|
@@ -5608,12 +5676,12 @@ async function replTurn(p, messages, userText) {
|
|
|
5608
5676
|
return;
|
|
5609
5677
|
}
|
|
5610
5678
|
if (res.status === 401) {
|
|
5611
|
-
console.log(
|
|
5679
|
+
console.log(import_chalk36.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
|
|
5612
5680
|
return;
|
|
5613
5681
|
}
|
|
5614
5682
|
if (!res.ok || !data) {
|
|
5615
5683
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
5616
|
-
console.log(
|
|
5684
|
+
console.log(import_chalk36.default.red(` Chat error: ${err}`));
|
|
5617
5685
|
return;
|
|
5618
5686
|
}
|
|
5619
5687
|
if (Array.isArray(data.delta)) {
|
|
@@ -5627,25 +5695,25 @@ async function replTurn(p, messages, userText) {
|
|
|
5627
5695
|
if (warn) console.log(warn);
|
|
5628
5696
|
if (!data.continue) return;
|
|
5629
5697
|
}
|
|
5630
|
-
console.log(
|
|
5698
|
+
console.log(import_chalk36.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
5631
5699
|
}
|
|
5632
5700
|
function renderUpsell(p, upsell) {
|
|
5633
5701
|
const loggedIn = !!loadCredentials();
|
|
5634
5702
|
if (upsell.reason === "CAPPED" && !loggedIn) {
|
|
5635
|
-
console.log("\n" +
|
|
5636
|
-
console.log(
|
|
5703
|
+
console.log("\n" + import_chalk36.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
|
|
5704
|
+
console.log(import_chalk36.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
|
|
5637
5705
|
console.log();
|
|
5638
5706
|
return;
|
|
5639
5707
|
}
|
|
5640
|
-
console.log("\n" +
|
|
5708
|
+
console.log("\n" + import_chalk36.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
5641
5709
|
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
5642
5710
|
if (!loggedIn) {
|
|
5643
|
-
console.log(
|
|
5711
|
+
console.log(import_chalk36.default.dim(" Options: `/login` for free login credit and higher limits, or `apiblaze llm set-key` to bring your own model key."));
|
|
5644
5712
|
} else {
|
|
5645
|
-
console.log(
|
|
5713
|
+
console.log(import_chalk36.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
|
|
5646
5714
|
}
|
|
5647
5715
|
} else if (upsell.reason === "INFLIGHT") {
|
|
5648
|
-
console.log(
|
|
5716
|
+
console.log(import_chalk36.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
5649
5717
|
}
|
|
5650
5718
|
console.log();
|
|
5651
5719
|
}
|
|
@@ -5714,9 +5782,9 @@ async function openServerProxy(project) {
|
|
|
5714
5782
|
const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
|
|
5715
5783
|
let dpKey = prior?.dpKey;
|
|
5716
5784
|
if (!dpKey) {
|
|
5717
|
-
console.log(
|
|
5785
|
+
console.log(import_chalk36.default.dim(` Minting an API key for tenant ${import_chalk36.default.bold(tenant2)} to query project ${import_chalk36.default.bold(project.projectName)}\u2026`));
|
|
5718
5786
|
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
5719
|
-
console.log(` ${
|
|
5787
|
+
console.log(` ${import_chalk36.default.green("\u2714")} API key: ${import_chalk36.default.dim(maskKey(dpKey))}`);
|
|
5720
5788
|
}
|
|
5721
5789
|
const p = {
|
|
5722
5790
|
projectId: project.projectId,
|
|
@@ -5727,7 +5795,7 @@ async function openServerProxy(project) {
|
|
|
5727
5795
|
proxyUrl: `https://${project.projectId}.abz.run/${version2}/${environment}`,
|
|
5728
5796
|
anon: false
|
|
5729
5797
|
};
|
|
5730
|
-
const spinner = (0,
|
|
5798
|
+
const spinner = (0, import_ora18.default)("Preparing the chat\u2026").start();
|
|
5731
5799
|
try {
|
|
5732
5800
|
const raw = await admin({
|
|
5733
5801
|
method: "GET",
|
|
@@ -5739,7 +5807,7 @@ async function openServerProxy(project) {
|
|
|
5739
5807
|
if (spec2 && (spec2.paths || spec2.openapi)) {
|
|
5740
5808
|
await publishMcp(p, spec2);
|
|
5741
5809
|
} else {
|
|
5742
|
-
console.log(
|
|
5810
|
+
console.log(import_chalk36.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
|
|
5743
5811
|
}
|
|
5744
5812
|
} catch (err) {
|
|
5745
5813
|
spinner.fail("Could not open the proxy.");
|
|
@@ -5792,7 +5860,7 @@ async function noArgsMenu(opts) {
|
|
|
5792
5860
|
const me = loadCredentials()?.apiblazeUserId;
|
|
5793
5861
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
5794
5862
|
const choices = saved.map((a) => ({
|
|
5795
|
-
name: `Chat with ${
|
|
5863
|
+
name: `Chat with ${import_chalk36.default.bold(a.name)} ${import_chalk36.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
|
|
5796
5864
|
value: { type: "existing", a }
|
|
5797
5865
|
}));
|
|
5798
5866
|
const creds = loadCredentials();
|
|
@@ -5802,14 +5870,14 @@ async function noArgsMenu(opts) {
|
|
|
5802
5870
|
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
5803
5871
|
for (const pr of proxies) {
|
|
5804
5872
|
choices.push({
|
|
5805
|
-
name: `Chat with ${
|
|
5873
|
+
name: `Chat with ${import_chalk36.default.bold(pr.projectName)} ${import_chalk36.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
5806
5874
|
value: { type: "server", project: pr }
|
|
5807
5875
|
});
|
|
5808
5876
|
}
|
|
5809
5877
|
} catch {
|
|
5810
5878
|
}
|
|
5811
5879
|
}
|
|
5812
|
-
choices.push({ name:
|
|
5880
|
+
choices.push({ name: import_chalk36.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
5813
5881
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
5814
5882
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
5815
5883
|
]);
|
|
@@ -5881,31 +5949,31 @@ async function noArgsMenu(opts) {
|
|
|
5881
5949
|
async function runRepl(p, initialMessages) {
|
|
5882
5950
|
const { default: inquirer3 } = await import("inquirer");
|
|
5883
5951
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
5884
|
-
console.log("\n" +
|
|
5885
|
-
if (messages.length) console.log(
|
|
5952
|
+
console.log("\n" + import_chalk36.default.cyan.bold("Chat with your API") + import_chalk36.default.dim(` \xB7 ${p.mcpHost}`));
|
|
5953
|
+
if (messages.length) console.log(import_chalk36.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
5886
5954
|
const llm2 = loadLlmConfig();
|
|
5887
5955
|
console.log(
|
|
5888
|
-
|
|
5956
|
+
import_chalk36.default.dim(
|
|
5889
5957
|
llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for credit \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
|
|
5890
5958
|
)
|
|
5891
5959
|
);
|
|
5892
5960
|
for (; ; ) {
|
|
5893
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
5961
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk36.default.green("you \u203A") }]);
|
|
5894
5962
|
const text = (input ?? "").trim();
|
|
5895
5963
|
if (!text) continue;
|
|
5896
5964
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
5897
5965
|
if (text === "/login") {
|
|
5898
5966
|
try {
|
|
5899
5967
|
await runLogin();
|
|
5900
|
-
console.log(
|
|
5968
|
+
console.log(import_chalk36.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
5901
5969
|
} catch (err) {
|
|
5902
|
-
console.log(
|
|
5970
|
+
console.log(import_chalk36.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5903
5971
|
}
|
|
5904
5972
|
continue;
|
|
5905
5973
|
}
|
|
5906
5974
|
if (text === "/claim") {
|
|
5907
5975
|
if (!loadCredentials()) {
|
|
5908
|
-
console.log(
|
|
5976
|
+
console.log(import_chalk36.default.yellow(" Log in first: `/login`, then `/claim`."));
|
|
5909
5977
|
continue;
|
|
5910
5978
|
}
|
|
5911
5979
|
try {
|
|
@@ -5914,30 +5982,30 @@ async function runRepl(p, initialMessages) {
|
|
|
5914
5982
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
5915
5983
|
p.anon = false;
|
|
5916
5984
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
5917
|
-
console.log(
|
|
5985
|
+
console.log(import_chalk36.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
5918
5986
|
}
|
|
5919
5987
|
} catch (err) {
|
|
5920
|
-
console.log(
|
|
5988
|
+
console.log(import_chalk36.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5921
5989
|
}
|
|
5922
5990
|
continue;
|
|
5923
5991
|
}
|
|
5924
5992
|
if (text === "/showauth") {
|
|
5925
5993
|
revealAuth = !revealAuth;
|
|
5926
|
-
console.log(
|
|
5994
|
+
console.log(import_chalk36.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."));
|
|
5927
5995
|
continue;
|
|
5928
5996
|
}
|
|
5929
5997
|
if (text.startsWith("/")) {
|
|
5930
|
-
console.log(
|
|
5998
|
+
console.log(import_chalk36.default.dim(" Commands: /login /claim /showauth /exit"));
|
|
5931
5999
|
continue;
|
|
5932
6000
|
}
|
|
5933
6001
|
await replTurn(p, messages, text);
|
|
5934
6002
|
saveTranscript(p, messages);
|
|
5935
6003
|
}
|
|
5936
|
-
console.log(
|
|
6004
|
+
console.log(import_chalk36.default.dim("\nBye."));
|
|
5937
6005
|
}
|
|
5938
6006
|
async function runApichat(opts) {
|
|
5939
6007
|
setVerbose(opts.verbose !== false);
|
|
5940
|
-
console.log(
|
|
6008
|
+
console.log(import_chalk36.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
5941
6009
|
if (!opts.openapispec && !opts.target) {
|
|
5942
6010
|
if (!process.stdin.isTTY) {
|
|
5943
6011
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -5950,7 +6018,7 @@ async function runApichat(opts) {
|
|
|
5950
6018
|
}
|
|
5951
6019
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
5952
6020
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
5953
|
-
console.log(` ${
|
|
6021
|
+
console.log(` ${import_chalk36.default.dim("Target:")} ${import_chalk36.default.bold(target)}`);
|
|
5954
6022
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
5955
6023
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
5956
6024
|
fail4(
|
|
@@ -5959,7 +6027,7 @@ async function runApichat(opts) {
|
|
|
5959
6027
|
);
|
|
5960
6028
|
}
|
|
5961
6029
|
const p = await provision(spec2, target, opts);
|
|
5962
|
-
console.log(` ${
|
|
6030
|
+
console.log(` ${import_chalk36.default.dim("Proxy: ")} ${import_chalk36.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
5963
6031
|
upsertApichat({
|
|
5964
6032
|
name: p.projectId,
|
|
5965
6033
|
target,
|
|
@@ -5978,23 +6046,23 @@ async function runApichat(opts) {
|
|
|
5978
6046
|
const secret = await captureTargetSecret(auth, opts);
|
|
5979
6047
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
5980
6048
|
} else {
|
|
5981
|
-
console.log(
|
|
6049
|
+
console.log(import_chalk36.default.dim(" Target auth: none required."));
|
|
5982
6050
|
}
|
|
5983
6051
|
const specText = JSON.stringify(spec2);
|
|
5984
6052
|
await uploadSpec(p, specText, opts);
|
|
5985
6053
|
const mcpUrl = await publishMcp(p, spec2);
|
|
5986
6054
|
console.log();
|
|
5987
|
-
if (p.proxyUrl) console.log(` ${
|
|
5988
|
-
if (mcpUrl) console.log(` ${
|
|
6055
|
+
if (p.proxyUrl) console.log(` ${import_chalk36.default.green("\u2713")} proxy ${import_chalk36.default.bold(p.proxyUrl)}`);
|
|
6056
|
+
if (mcpUrl) console.log(` ${import_chalk36.default.green("\u2713")} mcp ${import_chalk36.default.bold(mcpUrl)}`);
|
|
5989
6057
|
if (p.anon) {
|
|
5990
|
-
console.log(
|
|
6058
|
+
console.log(import_chalk36.default.dim("\n Anonymous workspace \u2014 /claim inside the chat (after /login) to keep it beyond 30 days."));
|
|
5991
6059
|
}
|
|
5992
6060
|
await runRepl(p);
|
|
5993
6061
|
}
|
|
5994
6062
|
|
|
5995
6063
|
// src/commands/consumer.ts
|
|
5996
|
-
var
|
|
5997
|
-
var
|
|
6064
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
6065
|
+
var import_ora19 = __toESM(require("ora"));
|
|
5998
6066
|
init_admin();
|
|
5999
6067
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
6000
6068
|
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
@@ -6014,7 +6082,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
6014
6082
|
function requireConsumer() {
|
|
6015
6083
|
const c = loadConsumer();
|
|
6016
6084
|
if (!c) {
|
|
6017
|
-
console.error(
|
|
6085
|
+
console.error(import_chalk37.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
6018
6086
|
process.exit(1);
|
|
6019
6087
|
}
|
|
6020
6088
|
return c;
|
|
@@ -6025,7 +6093,7 @@ async function runConsumerLogin(opts) {
|
|
|
6025
6093
|
let clientId = opts.client;
|
|
6026
6094
|
if (clientId) {
|
|
6027
6095
|
if (!tenant2) {
|
|
6028
|
-
console.error(
|
|
6096
|
+
console.error(import_chalk37.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
6029
6097
|
process.exit(1);
|
|
6030
6098
|
}
|
|
6031
6099
|
} else {
|
|
@@ -6037,25 +6105,25 @@ async function runConsumerLogin(opts) {
|
|
|
6037
6105
|
if (!picked) process.exit(1);
|
|
6038
6106
|
tenant2 = picked;
|
|
6039
6107
|
}
|
|
6040
|
-
const s2 = (0,
|
|
6108
|
+
const s2 = (0, import_ora19.default)("Finding the login app...").start();
|
|
6041
6109
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
6042
6110
|
s2.stop();
|
|
6043
6111
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
6044
6112
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
6045
6113
|
if (!pick2) {
|
|
6046
|
-
console.error(
|
|
6114
|
+
console.error(import_chalk37.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
6047
6115
|
process.exit(1);
|
|
6048
6116
|
}
|
|
6049
6117
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
6050
6118
|
}
|
|
6051
6119
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
6052
|
-
console.log(`${
|
|
6120
|
+
console.log(`${import_chalk37.default.cyan("\u2192")} Logging in to ${import_chalk37.default.bold(tenant2)} as a consumer...`);
|
|
6053
6121
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
6054
6122
|
console.log(`
|
|
6055
|
-
Open: ${
|
|
6056
|
-
console.log(` Code: ${
|
|
6123
|
+
Open: ${import_chalk37.default.underline(verificationUri)}`);
|
|
6124
|
+
console.log(` Code: ${import_chalk37.default.bold(userCode)}
|
|
6057
6125
|
`);
|
|
6058
|
-
console.log(
|
|
6126
|
+
console.log(import_chalk37.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
6059
6127
|
}, portalResource);
|
|
6060
6128
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
6061
6129
|
const creds = {
|
|
@@ -6070,7 +6138,7 @@ async function runConsumerLogin(opts) {
|
|
|
6070
6138
|
obtainedAt: Date.now()
|
|
6071
6139
|
};
|
|
6072
6140
|
saveConsumer(creds);
|
|
6073
|
-
console.log(
|
|
6141
|
+
console.log(import_chalk37.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
6074
6142
|
}
|
|
6075
6143
|
async function runConsumerTokens(opts) {
|
|
6076
6144
|
const creds = requireConsumer();
|
|
@@ -6083,29 +6151,29 @@ async function runConsumerTokens(opts) {
|
|
|
6083
6151
|
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));
|
|
6084
6152
|
return;
|
|
6085
6153
|
}
|
|
6086
|
-
console.log(`${
|
|
6154
|
+
console.log(`${import_chalk37.default.cyan("Consumer")} ${import_chalk37.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk37.default.bold(fresh.tenant)}
|
|
6087
6155
|
`);
|
|
6088
|
-
console.log(`${
|
|
6156
|
+
console.log(`${import_chalk37.default.bold("access_token")} ${import_chalk37.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
6089
6157
|
${fresh.accessToken}
|
|
6090
6158
|
`);
|
|
6091
|
-
if (fresh.idToken) console.log(`${
|
|
6159
|
+
if (fresh.idToken) console.log(`${import_chalk37.default.bold("id_token")} ${import_chalk37.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
6092
6160
|
${fresh.idToken}
|
|
6093
6161
|
`);
|
|
6094
|
-
if (fresh.refreshToken) console.log(`${
|
|
6162
|
+
if (fresh.refreshToken) console.log(`${import_chalk37.default.bold("refresh_token")}
|
|
6095
6163
|
${fresh.refreshToken}
|
|
6096
6164
|
`);
|
|
6097
|
-
console.log(
|
|
6165
|
+
console.log(import_chalk37.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
6098
6166
|
}
|
|
6099
6167
|
async function runConsumerApikeys(opts) {
|
|
6100
6168
|
const creds = requireConsumer();
|
|
6101
6169
|
const { default: inquirer3 } = await import("inquirer");
|
|
6102
|
-
const spinner = (0,
|
|
6170
|
+
const spinner = (0, import_ora19.default)("Loading your API keys...").start();
|
|
6103
6171
|
const list = await consumerFetch(creds, "/apikeys");
|
|
6104
6172
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
6105
6173
|
spinner.stop();
|
|
6106
6174
|
if (list.status >= 400) {
|
|
6107
|
-
console.error(
|
|
6108
|
-
if (list.status === 401) console.error(
|
|
6175
|
+
console.error(import_chalk37.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
6176
|
+
if (list.status === 401) console.error(import_chalk37.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
6109
6177
|
process.exit(1);
|
|
6110
6178
|
}
|
|
6111
6179
|
const keys = list.data?.keys ?? [];
|
|
@@ -6113,16 +6181,16 @@ async function runConsumerApikeys(opts) {
|
|
|
6113
6181
|
if (opts.json) {
|
|
6114
6182
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
6115
6183
|
} else if (!keys.length) {
|
|
6116
|
-
console.log(
|
|
6184
|
+
console.log(import_chalk37.default.yellow("No API keys yet."));
|
|
6117
6185
|
} else {
|
|
6118
6186
|
for (const k of keys) {
|
|
6119
6187
|
const clear = revealMap[k.environment]?.key;
|
|
6120
|
-
const shown = clear ?
|
|
6121
|
-
const exp = k.expires_at ?
|
|
6122
|
-
console.log(` ${
|
|
6188
|
+
const shown = clear ? import_chalk37.default.green(clear) : import_chalk37.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
6189
|
+
const exp = k.expires_at ? import_chalk37.default.dim(`exp ${k.expires_at}`) : import_chalk37.default.dim("no expiry");
|
|
6190
|
+
console.log(` ${import_chalk37.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk37.default.dim(k.description ?? "")}`);
|
|
6123
6191
|
}
|
|
6124
6192
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
6125
|
-
console.log(
|
|
6193
|
+
console.log(import_chalk37.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
6126
6194
|
}
|
|
6127
6195
|
}
|
|
6128
6196
|
if (opts.json) return;
|
|
@@ -6136,7 +6204,7 @@ async function runConsumerApikeys(opts) {
|
|
|
6136
6204
|
const body = { environment: answers.environment };
|
|
6137
6205
|
if (answers.description) body.description = answers.description;
|
|
6138
6206
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
6139
|
-
const s2 = (0,
|
|
6207
|
+
const s2 = (0, import_ora19.default)("Creating key...").start();
|
|
6140
6208
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
6141
6209
|
if (created.status >= 400) {
|
|
6142
6210
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -6144,13 +6212,13 @@ async function runConsumerApikeys(opts) {
|
|
|
6144
6212
|
}
|
|
6145
6213
|
s2.succeed("Key created.");
|
|
6146
6214
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
6147
|
-
if (key) console.log(` ${
|
|
6148
|
-
else console.log(
|
|
6215
|
+
if (key) console.log(` ${import_chalk37.default.green(key)} ${import_chalk37.default.dim("(shown once \u2014 store it now)")}`);
|
|
6216
|
+
else console.log(import_chalk37.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
6149
6217
|
}
|
|
6150
6218
|
|
|
6151
6219
|
// src/commands/sidecar.ts
|
|
6152
|
-
var
|
|
6153
|
-
var
|
|
6220
|
+
var import_chalk38 = __toESM(require("chalk"));
|
|
6221
|
+
var import_ora20 = __toESM(require("ora"));
|
|
6154
6222
|
var fs10 = __toESM(require("fs"));
|
|
6155
6223
|
var path7 = __toESM(require("path"));
|
|
6156
6224
|
init_admin();
|
|
@@ -6191,18 +6259,18 @@ function upsertEnvLocal(root, token) {
|
|
|
6191
6259
|
}
|
|
6192
6260
|
function installSidecarPackage(root) {
|
|
6193
6261
|
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
6194
|
-
console.log(` ${
|
|
6262
|
+
console.log(` ${import_chalk38.default.green("\u2713")} apiblaze package already installed`);
|
|
6195
6263
|
return;
|
|
6196
6264
|
}
|
|
6197
6265
|
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
6198
6266
|
const pm = has("bun.lockb") || has("bun.lock") ? { cmd: "bun", add: "add" } : has("pnpm-lock.yaml") ? { cmd: "pnpm", add: "add" } : has("yarn.lock") ? { cmd: "yarn", add: "add" } : { cmd: "npm", add: "install" };
|
|
6199
|
-
const spinner = (0,
|
|
6267
|
+
const spinner = (0, import_ora20.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
6200
6268
|
try {
|
|
6201
6269
|
const { execSync } = require("child_process");
|
|
6202
6270
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
6203
6271
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
6204
6272
|
} catch {
|
|
6205
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
6273
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk38.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk38.default.cyan("npm run dev")}.`);
|
|
6206
6274
|
}
|
|
6207
6275
|
}
|
|
6208
6276
|
function readEnvKey(root) {
|
|
@@ -6341,7 +6409,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6341
6409
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
6342
6410
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
6343
6411
|
if (opts.newSession) clearAnonCred2();
|
|
6344
|
-
const spinner = (0,
|
|
6412
|
+
const spinner = (0, import_ora20.default)("Setting up a sidecar (no login needed)...").start();
|
|
6345
6413
|
let out;
|
|
6346
6414
|
try {
|
|
6347
6415
|
out = await sidecarInitAnonymous2();
|
|
@@ -6353,29 +6421,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6353
6421
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
6354
6422
|
const envState = upsertEnvLocal(root, out.token);
|
|
6355
6423
|
ensureGitignored(root);
|
|
6356
|
-
console.log(` ${
|
|
6357
|
-
console.log(` ${
|
|
6424
|
+
console.log(` ${import_chalk38.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6425
|
+
console.log(` ${import_chalk38.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6358
6426
|
installSidecarPackage(root);
|
|
6359
6427
|
let inspectorPath = null;
|
|
6360
6428
|
if (!opts.noInspector) {
|
|
6361
6429
|
inspectorPath = generateInspector(root, router);
|
|
6362
|
-
if (inspectorPath) console.log(` ${
|
|
6430
|
+
if (inspectorPath) console.log(` ${import_chalk38.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6363
6431
|
}
|
|
6364
6432
|
console.log("");
|
|
6365
|
-
console.log(
|
|
6366
|
-
console.log(` 1. ${
|
|
6433
|
+
console.log(import_chalk38.default.bold("Done (no account needed). What happens next:"));
|
|
6434
|
+
console.log(` 1. ${import_chalk38.default.cyan("npm run dev")} and use your app.`);
|
|
6367
6435
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
6368
|
-
console.log(` ${
|
|
6436
|
+
console.log(` ${import_chalk38.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
6369
6437
|
console.log("");
|
|
6370
|
-
console.log(
|
|
6371
|
-
console.log(` ${
|
|
6372
|
-
console.log(
|
|
6438
|
+
console.log(import_chalk38.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
6439
|
+
console.log(` ${import_chalk38.default.cyan("apiblaze login")} then ${import_chalk38.default.cyan("apiblaze claim")} ${import_chalk38.default.dim("(no code needed here)")}`);
|
|
6440
|
+
console.log(import_chalk38.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
6373
6441
|
}
|
|
6374
6442
|
async function runSidecar(opts) {
|
|
6375
6443
|
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
6376
6444
|
const detected = detectNextProject(root);
|
|
6377
6445
|
if (!detected.found) {
|
|
6378
|
-
console.log(
|
|
6446
|
+
console.log(import_chalk38.default.yellow(`No Next.js project detected in ${root}.`));
|
|
6379
6447
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
6380
6448
|
return;
|
|
6381
6449
|
}
|
|
@@ -6386,10 +6454,10 @@ async function runSidecar(opts) {
|
|
|
6386
6454
|
if (!loadCredentials()) {
|
|
6387
6455
|
upsertEnvLocal(root, readEnvKey(root));
|
|
6388
6456
|
ensureGitignored(root);
|
|
6389
|
-
console.log(` ${
|
|
6390
|
-
console.log(` ${
|
|
6457
|
+
console.log(` ${import_chalk38.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
6458
|
+
console.log(` ${import_chalk38.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6391
6459
|
installSidecarPackage(root);
|
|
6392
|
-
console.log(
|
|
6460
|
+
console.log(import_chalk38.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
6393
6461
|
return;
|
|
6394
6462
|
}
|
|
6395
6463
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -6398,7 +6466,7 @@ async function runSidecar(opts) {
|
|
|
6398
6466
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
6399
6467
|
let token = existingKey ?? "";
|
|
6400
6468
|
if (mustMint) {
|
|
6401
|
-
const spinner = (0,
|
|
6469
|
+
const spinner = (0, import_ora20.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
|
|
6402
6470
|
try {
|
|
6403
6471
|
const out = await admin({
|
|
6404
6472
|
method: "POST",
|
|
@@ -6412,39 +6480,39 @@ async function runSidecar(opts) {
|
|
|
6412
6480
|
throw err;
|
|
6413
6481
|
}
|
|
6414
6482
|
} else {
|
|
6415
|
-
console.log(
|
|
6483
|
+
console.log(import_chalk38.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
6416
6484
|
}
|
|
6417
6485
|
const envState = upsertEnvLocal(root, token);
|
|
6418
6486
|
ensureGitignored(root);
|
|
6419
|
-
console.log(` ${
|
|
6487
|
+
console.log(` ${import_chalk38.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6420
6488
|
const wireState = wireInstrumentation(root);
|
|
6421
|
-
console.log(` ${
|
|
6489
|
+
console.log(` ${import_chalk38.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
6422
6490
|
installSidecarPackage(root);
|
|
6423
6491
|
let inspectorPath = null;
|
|
6424
6492
|
if (!opts.noInspector) {
|
|
6425
6493
|
inspectorPath = generateInspector(root, detected.router);
|
|
6426
|
-
if (inspectorPath) console.log(` ${
|
|
6494
|
+
if (inspectorPath) console.log(` ${import_chalk38.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6427
6495
|
}
|
|
6428
6496
|
console.log("");
|
|
6429
|
-
console.log(
|
|
6430
|
-
console.log(` 1. ${
|
|
6431
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
6432
|
-
console.log(` 3. Approve the ones to route: ${
|
|
6497
|
+
console.log(import_chalk38.default.bold("Done. What happens next:"));
|
|
6498
|
+
console.log(` 1. ${import_chalk38.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
6499
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk38.default.bold("candidates")} \u2014 list them: ${import_chalk38.default.cyan("apiblaze sidecar")}`);
|
|
6500
|
+
console.log(` 3. Approve the ones to route: ${import_chalk38.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
6433
6501
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
6434
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
6435
|
-
if (switchingTeam) console.log(
|
|
6502
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk38.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
|
|
6503
|
+
if (switchingTeam) console.log(import_chalk38.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
6436
6504
|
console.log("");
|
|
6437
|
-
console.log(
|
|
6438
|
-
console.log(
|
|
6439
|
-
console.log(
|
|
6505
|
+
console.log(import_chalk38.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
6506
|
+
console.log(import_chalk38.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
6507
|
+
console.log(import_chalk38.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
6440
6508
|
console.log("");
|
|
6441
|
-
console.log(
|
|
6442
|
-
console.log(
|
|
6509
|
+
console.log(import_chalk38.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
6510
|
+
console.log(import_chalk38.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
6443
6511
|
}
|
|
6444
6512
|
|
|
6445
6513
|
// src/commands/origins.ts
|
|
6446
|
-
var
|
|
6447
|
-
var
|
|
6514
|
+
var import_chalk39 = __toESM(require("chalk"));
|
|
6515
|
+
var import_ora21 = __toESM(require("ora"));
|
|
6448
6516
|
init_admin();
|
|
6449
6517
|
init_auth();
|
|
6450
6518
|
init_anon_cred();
|
|
@@ -6453,7 +6521,7 @@ async function runOriginsList(opts) {
|
|
|
6453
6521
|
if (!loadCredentials()) {
|
|
6454
6522
|
const cred = loadAnonCred();
|
|
6455
6523
|
if (!cred) {
|
|
6456
|
-
console.log(
|
|
6524
|
+
console.log(import_chalk39.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
6457
6525
|
return;
|
|
6458
6526
|
}
|
|
6459
6527
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -6471,30 +6539,30 @@ async function runOriginsList(opts) {
|
|
|
6471
6539
|
}
|
|
6472
6540
|
const routed = out.routed ?? [];
|
|
6473
6541
|
const candidates = out.candidates ?? [];
|
|
6474
|
-
console.log(
|
|
6542
|
+
console.log(import_chalk39.default.bold(`
|
|
6475
6543
|
Routed through APIblaze (${routed.length})`));
|
|
6476
|
-
if (!routed.length) console.log(
|
|
6477
|
-
for (const r of routed) console.log(` ${
|
|
6478
|
-
console.log(
|
|
6544
|
+
if (!routed.length) console.log(import_chalk39.default.dim(" none yet"));
|
|
6545
|
+
for (const r of routed) console.log(` ${import_chalk39.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk39.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
6546
|
+
console.log(import_chalk39.default.bold(`
|
|
6479
6547
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
6480
|
-
if (!candidates.length) console.log(
|
|
6548
|
+
if (!candidates.length) console.log(import_chalk39.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
6481
6549
|
for (const c of candidates) {
|
|
6482
|
-
console.log(` ${
|
|
6550
|
+
console.log(` ${import_chalk39.default.yellow("\u25CB")} ${c.origin} ${import_chalk39.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
6483
6551
|
}
|
|
6484
6552
|
if (candidates.length) {
|
|
6485
|
-
console.log(
|
|
6553
|
+
console.log(import_chalk39.default.dim(`
|
|
6486
6554
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
6487
|
-
console.log(
|
|
6555
|
+
console.log(import_chalk39.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
6488
6556
|
}
|
|
6489
6557
|
}
|
|
6490
6558
|
async function runOriginsApprove(origin, opts) {
|
|
6491
6559
|
if (!loadCredentials()) {
|
|
6492
6560
|
const cred = loadAnonCred();
|
|
6493
6561
|
if (!cred) {
|
|
6494
|
-
console.error(
|
|
6562
|
+
console.error(import_chalk39.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
6495
6563
|
process.exit(1);
|
|
6496
6564
|
}
|
|
6497
|
-
const spinner2 = (0,
|
|
6565
|
+
const spinner2 = (0, import_ora21.default)(`Approving ${origin} (anonymous)...`).start();
|
|
6498
6566
|
try {
|
|
6499
6567
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
6500
6568
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -6505,7 +6573,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
6505
6573
|
return;
|
|
6506
6574
|
}
|
|
6507
6575
|
const { teamId } = await resolveTeam(opts.team);
|
|
6508
|
-
const spinner = (0,
|
|
6576
|
+
const spinner = (0, import_ora21.default)(`Approving ${origin}...`).start();
|
|
6509
6577
|
try {
|
|
6510
6578
|
const out = await admin({
|
|
6511
6579
|
method: "POST",
|
|
@@ -6522,7 +6590,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
6522
6590
|
}
|
|
6523
6591
|
async function runOriginsDeny(origin, opts) {
|
|
6524
6592
|
const { teamId } = await resolveTeam(opts.team);
|
|
6525
|
-
const spinner = (0,
|
|
6593
|
+
const spinner = (0, import_ora21.default)(`Dismissing ${origin}...`).start();
|
|
6526
6594
|
try {
|
|
6527
6595
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
6528
6596
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -6533,7 +6601,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
6533
6601
|
}
|
|
6534
6602
|
async function runOriginsRemove(origin, opts) {
|
|
6535
6603
|
const { teamId } = await resolveTeam(opts.team);
|
|
6536
|
-
const spinner = (0,
|
|
6604
|
+
const spinner = (0, import_ora21.default)(`Removing the proxy for ${origin}...`).start();
|
|
6537
6605
|
try {
|
|
6538
6606
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
6539
6607
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -6544,7 +6612,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
6544
6612
|
}
|
|
6545
6613
|
|
|
6546
6614
|
// src/commands/op.ts
|
|
6547
|
-
var
|
|
6615
|
+
var import_chalk40 = __toESM(require("chalk"));
|
|
6548
6616
|
init_auth();
|
|
6549
6617
|
init_trace();
|
|
6550
6618
|
init_types();
|
|
@@ -6577,82 +6645,82 @@ function printResidue(report, applied) {
|
|
|
6577
6645
|
const up = report?.upstash ?? {};
|
|
6578
6646
|
const fga = report?.fga ?? {};
|
|
6579
6647
|
const ghosts = report?.ghosts ?? {};
|
|
6580
|
-
console.log(
|
|
6581
|
-
console.log(
|
|
6648
|
+
console.log(import_chalk40.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
6649
|
+
console.log(import_chalk40.default.bold("\n Upstash"));
|
|
6582
6650
|
const orphans = up.orphans ?? [];
|
|
6583
|
-
if (orphans.length === 0) console.log(
|
|
6584
|
-
for (const o of orphans) console.log(` ${
|
|
6585
|
-
console.log(
|
|
6651
|
+
if (orphans.length === 0) console.log(import_chalk40.default.green(" no orphaned keys"));
|
|
6652
|
+
for (const o of orphans) console.log(` ${import_chalk40.default.yellow(o.key)} ${import_chalk40.default.dim(`\u2014 ${o.reason}`)}`);
|
|
6653
|
+
console.log(import_chalk40.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
6586
6654
|
if (up.anon_wallet_detail) {
|
|
6587
6655
|
const d = up.anon_wallet_detail;
|
|
6588
|
-
console.log(
|
|
6656
|
+
console.log(import_chalk40.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)"}`));
|
|
6589
6657
|
}
|
|
6590
6658
|
if (up.keyspace_census) {
|
|
6591
6659
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
6592
|
-
console.log(
|
|
6660
|
+
console.log(import_chalk40.default.dim(` keyspace: ${census}`));
|
|
6593
6661
|
}
|
|
6594
|
-
if (up.unknown?.length) console.log(
|
|
6595
|
-
if (applied) console.log(` ${
|
|
6596
|
-
for (const e of up.errors ?? []) console.log(
|
|
6597
|
-
console.log(
|
|
6662
|
+
if (up.unknown?.length) console.log(import_chalk40.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
6663
|
+
if (applied) console.log(` ${import_chalk40.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
6664
|
+
for (const e of up.errors ?? []) console.log(import_chalk40.default.red(` error: ${e}`));
|
|
6665
|
+
console.log(import_chalk40.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
6598
6666
|
if (applied) {
|
|
6599
6667
|
const swept = fga?.swept ?? [];
|
|
6600
|
-
if (swept.length === 0) console.log(
|
|
6668
|
+
if (swept.length === 0) console.log(import_chalk40.default.green(" no orphaned stores"));
|
|
6601
6669
|
for (const s of swept) {
|
|
6602
6670
|
console.log(
|
|
6603
|
-
` ${
|
|
6671
|
+
` ${import_chalk40.default.yellow(s.store_id)} ${import_chalk40.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
6604
6672
|
);
|
|
6605
6673
|
}
|
|
6606
|
-
if (fga?.remaining) console.log(
|
|
6674
|
+
if (fga?.remaining) console.log(import_chalk40.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
6607
6675
|
const st = fga?.side_tables;
|
|
6608
|
-
if (st) console.log(
|
|
6676
|
+
if (st) console.log(import_chalk40.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})` : ""}`));
|
|
6609
6677
|
} else {
|
|
6610
6678
|
const fgaOrphans = fga?.orphans ?? [];
|
|
6611
|
-
if (fgaOrphans.length === 0) console.log(
|
|
6679
|
+
if (fgaOrphans.length === 0) console.log(import_chalk40.default.green(" no orphaned stores"));
|
|
6612
6680
|
for (const s of fgaOrphans) {
|
|
6613
6681
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
6614
|
-
console.log(` ${
|
|
6682
|
+
console.log(` ${import_chalk40.default.yellow(s.store_id)} ${import_chalk40.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
6615
6683
|
}
|
|
6616
|
-
console.log(
|
|
6684
|
+
console.log(import_chalk40.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
6617
6685
|
const st = fga?.side_tables;
|
|
6618
|
-
if (st) console.log(
|
|
6686
|
+
if (st) console.log(import_chalk40.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`));
|
|
6619
6687
|
}
|
|
6620
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
6621
|
-
console.log(
|
|
6688
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk40.default.red(` error: ${e}`));
|
|
6689
|
+
console.log(import_chalk40.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
6622
6690
|
if (applied) {
|
|
6623
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
6624
|
-
else console.log(` ${
|
|
6691
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk40.default.green(" no ghost tuples"));
|
|
6692
|
+
else console.log(` ${import_chalk40.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk40.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
6625
6693
|
} else {
|
|
6626
6694
|
const n = ghosts?.ghost_count ?? 0;
|
|
6627
|
-
if (n === 0) console.log(
|
|
6695
|
+
if (n === 0) console.log(import_chalk40.default.green(` no ghost tuples ${import_chalk40.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
6628
6696
|
else {
|
|
6629
|
-
console.log(
|
|
6697
|
+
console.log(import_chalk40.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
6630
6698
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
6631
|
-
console.log(
|
|
6699
|
+
console.log(import_chalk40.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
6632
6700
|
}
|
|
6633
|
-
if (n > 20) console.log(
|
|
6701
|
+
if (n > 20) console.log(import_chalk40.default.dim(` \u2026 and ${n - 20} more`));
|
|
6634
6702
|
}
|
|
6635
6703
|
}
|
|
6636
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
6704
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk40.default.red(` error: ${e}`));
|
|
6637
6705
|
console.log();
|
|
6638
6706
|
}
|
|
6639
6707
|
async function runOp(sub, opts = {}) {
|
|
6640
6708
|
if (!loadCredentials()) {
|
|
6641
|
-
console.log(
|
|
6709
|
+
console.log(import_chalk40.default.dim("Not logged in. Run `apiblaze login`."));
|
|
6642
6710
|
return;
|
|
6643
6711
|
}
|
|
6644
6712
|
if (!isOperatorLogin()) {
|
|
6645
|
-
console.log(
|
|
6713
|
+
console.log(import_chalk40.default.dim("`apiblaze op` is only available to platform operators."));
|
|
6646
6714
|
return;
|
|
6647
6715
|
}
|
|
6648
6716
|
switch (sub) {
|
|
6649
6717
|
case void 0:
|
|
6650
6718
|
case "menu": {
|
|
6651
|
-
console.log(
|
|
6652
|
-
console.log(` ${
|
|
6653
|
-
console.log(` ${
|
|
6654
|
-
console.log(` ${
|
|
6655
|
-
console.log(
|
|
6719
|
+
console.log(import_chalk40.default.bold("\nOperator menu"));
|
|
6720
|
+
console.log(` ${import_chalk40.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
6721
|
+
console.log(` ${import_chalk40.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk40.default.dim("-y to skip")})`);
|
|
6722
|
+
console.log(` ${import_chalk40.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
6723
|
+
console.log(import_chalk40.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
6656
6724
|
`));
|
|
6657
6725
|
return;
|
|
6658
6726
|
}
|
|
@@ -6671,17 +6739,17 @@ async function runOp(sub, opts = {}) {
|
|
|
6671
6739
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
6672
6740
|
printResidue(report, false);
|
|
6673
6741
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
6674
|
-
console.log(
|
|
6742
|
+
console.log(import_chalk40.default.green("Nothing to sweep."));
|
|
6675
6743
|
return;
|
|
6676
6744
|
}
|
|
6677
6745
|
if (!opts.yes) {
|
|
6678
6746
|
const readline2 = await import("readline/promises");
|
|
6679
6747
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
6680
6748
|
const answer = await rl.question(
|
|
6681
|
-
|
|
6749
|
+
import_chalk40.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: `)
|
|
6682
6750
|
);
|
|
6683
6751
|
rl.close();
|
|
6684
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
6752
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk40.default.dim("Aborted."));
|
|
6685
6753
|
}
|
|
6686
6754
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
6687
6755
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -6692,15 +6760,15 @@ async function runOp(sub, opts = {}) {
|
|
|
6692
6760
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
6693
6761
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
6694
6762
|
const accounts = data?.accounts ?? [];
|
|
6695
|
-
if (accounts.length === 0) return void console.log(
|
|
6763
|
+
if (accounts.length === 0) return void console.log(import_chalk40.default.dim("No credit wallets."));
|
|
6696
6764
|
for (const a of accounts) {
|
|
6697
6765
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
6698
|
-
console.log(` ${
|
|
6766
|
+
console.log(` ${import_chalk40.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk40.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk40.default.dim(" \u2014 anon") : ""}`);
|
|
6699
6767
|
}
|
|
6700
6768
|
return;
|
|
6701
6769
|
}
|
|
6702
6770
|
default:
|
|
6703
|
-
console.log(
|
|
6771
|
+
console.log(import_chalk40.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
6704
6772
|
}
|
|
6705
6773
|
}
|
|
6706
6774
|
|
|
@@ -6743,6 +6811,7 @@ program.command("create").description("Create a new API proxy (no login needed \
|
|
|
6743
6811
|
});
|
|
6744
6812
|
var agent = program.command("agent").description("Chat with an assistant that builds and runs your APIs (billed per turn)").option("--team <id|name>", "Team to work in (defaults to your active team)").action(action((opts) => runAgent(opts)));
|
|
6745
6813
|
agent.command("authz").description("Chat to design and turn on access rules for an API").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").action(action((project, apiVersion) => runAuthz(project, apiVersion)));
|
|
6814
|
+
program.command("rule").description("Author an object-level access rule in plain English, in one shot (billed per turn)").argument("<rule>", 'The rule in plain English, e.g. "users see only their own rows"').argument("<project>", "Project name or id").option("--enforce", "Turn enforcement on immediately (default: shadow-publish only)").option("--apiversion <version>", "API version (defaults to the project's)").action(action((rule, project, opts) => runRule(rule, project, opts)));
|
|
6746
6815
|
agent.command("openapi").description("Chat to build your API spec from real traffic").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").action(action((project, apiVersion) => runOpenapi(project, apiVersion)));
|
|
6747
6816
|
agent.command("mcp").description("Chat to build an MCP server for an API").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").option("--environment <env>", "Environment to publish (default: prod)").action(action((project, apiVersion, opts) => runMcp(project, apiVersion, opts)));
|
|
6748
6817
|
program.command("apichat").description("Turn any API into a chat: point at an OpenAPI spec and start asking questions (no login needed)").option("--openapispec <file|url>", "OpenAPI/Swagger spec to build from (JSON or YAML; file path or URL)").option("--target <url>", "Upstream base URL (overrides servers[0].url; enables spec discovery when --openapispec is omitted)").option("--name <name>", "Proxy name (defaults to the target host)").option("--apiversion <version>", "API version to create (e.g. 1.0.0)").option("--environment <env>", "Environment to chat against (default: prod anonymous / dev logged-in)").option("--target-auth-env <ENV_VAR>", "Read the upstream credential from this env var (CI-safe; required when there is no TTY and the API needs auth)").option("--force", "Proceed even if the API uses oauth2/openIdConnect target auth (you configure target auth yourself later)").option("-y, --yes", "Skip confirmation prompts").option("--no-verbose", "Hide the per-turn proxy curl trace (shown by default for apichat)").action(action((opts) => runApichat(opts)));
|
|
@@ -6762,7 +6831,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
6762
6831
|
try {
|
|
6763
6832
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
6764
6833
|
if (Number.isNaN(resolved)) {
|
|
6765
|
-
console.error(
|
|
6834
|
+
console.error(import_chalk41.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
6766
6835
|
process.exit(1);
|
|
6767
6836
|
}
|
|
6768
6837
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -6852,7 +6921,7 @@ spec.command("set").description("Replace the stored OpenAPI spec from a local fi
|
|
|
6852
6921
|
var HELP_GROUPS = [
|
|
6853
6922
|
{ title: "Chat", commands: ["apichat", "agent"] },
|
|
6854
6923
|
{ title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
|
|
6855
|
-
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
6924
|
+
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
6856
6925
|
{ title: "Data plane commands", commands: [
|
|
6857
6926
|
{ parent: "consumer", sub: "login" },
|
|
6858
6927
|
{ parent: "consumer", sub: "apikeys" }
|
|
@@ -6871,7 +6940,7 @@ function groupedCommandHelp() {
|
|
|
6871
6940
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
6872
6941
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
6873
6942
|
}).filter(Boolean).join("\n");
|
|
6874
|
-
return `${
|
|
6943
|
+
return `${import_chalk41.default.bold(g.title)}
|
|
6875
6944
|
${rows}`;
|
|
6876
6945
|
}).join("\n\n");
|
|
6877
6946
|
}
|
|
@@ -6905,14 +6974,14 @@ async function recoverStaleTeam() {
|
|
|
6905
6974
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
6906
6975
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
6907
6976
|
if (!linked) {
|
|
6908
|
-
console.error(
|
|
6977
|
+
console.error(import_chalk41.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
6909
6978
|
return;
|
|
6910
6979
|
}
|
|
6911
6980
|
if (linked.teamId === creds.teamId) return;
|
|
6912
6981
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
6913
6982
|
delete next.activeTenant;
|
|
6914
6983
|
saveCredentials(next);
|
|
6915
|
-
console.error(
|
|
6984
|
+
console.error(import_chalk41.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk41.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
6916
6985
|
} catch {
|
|
6917
6986
|
}
|
|
6918
6987
|
}
|
|
@@ -6920,16 +6989,16 @@ async function printError(err) {
|
|
|
6920
6989
|
if (err instanceof ApiError) {
|
|
6921
6990
|
const data = err.body;
|
|
6922
6991
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
6923
|
-
console.error(
|
|
6992
|
+
console.error(import_chalk41.default.red(`
|
|
6924
6993
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
6925
6994
|
if (err.status === 403 || err.status === 404) {
|
|
6926
6995
|
await recoverStaleTeam();
|
|
6927
6996
|
}
|
|
6928
6997
|
} else if (err instanceof Error) {
|
|
6929
|
-
console.error(
|
|
6998
|
+
console.error(import_chalk41.default.red(`
|
|
6930
6999
|
Error: ${err.message}`));
|
|
6931
7000
|
} else {
|
|
6932
|
-
console.error(
|
|
7001
|
+
console.error(import_chalk41.default.red("\nUnknown error"));
|
|
6933
7002
|
}
|
|
6934
7003
|
}
|
|
6935
7004
|
program.parse(process.argv);
|