apiblaze 0.18.3 → 0.18.7
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 +583 -507
- package/dist/react/index.js +366 -233
- package/dist/react/index.mjs +366 -233
- package/dist/server/index.d.mts +21 -0
- package/dist/server/index.d.ts +21 -0
- package/dist/server/index.js +29 -8
- package/dist/server/index.mjs +29 -8
- package/package.json +3 -3
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.7";
|
|
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,40 +5599,47 @@ 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
|
}
|
|
5545
5613
|
}
|
|
5546
5614
|
function billingLine(billing) {
|
|
5547
5615
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
5616
|
+
if (typeof billing.free_turns_remaining === "number") return null;
|
|
5548
5617
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
5549
|
-
let line =
|
|
5618
|
+
let line = import_chalk36.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk36.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
5550
5619
|
if (typeof billing.credits_remaining === "number") {
|
|
5551
|
-
line +=
|
|
5620
|
+
line += import_chalk36.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
5552
5621
|
}
|
|
5553
5622
|
return line;
|
|
5554
5623
|
}
|
|
5555
5624
|
function freeBudgetWarning(billing, anon) {
|
|
5556
|
-
if (!anon || !billing
|
|
5625
|
+
if (!anon || !billing) return null;
|
|
5626
|
+
if (typeof billing.free_turns_remaining === "number") {
|
|
5627
|
+
const left2 = billing.free_turns_remaining;
|
|
5628
|
+
if (left2 <= 0) return import_chalk36.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
|
|
5629
|
+
return import_chalk36.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left`);
|
|
5630
|
+
}
|
|
5631
|
+
if (typeof billing.free_remaining_cents !== "number") return null;
|
|
5557
5632
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
5558
5633
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
5559
5634
|
if (left > 8) return null;
|
|
5560
|
-
if (left <= 0) return
|
|
5561
|
-
return
|
|
5635
|
+
if (left <= 0) return import_chalk36.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
5636
|
+
return import_chalk36.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
5562
5637
|
}
|
|
5563
5638
|
function printAssistant(delta) {
|
|
5564
5639
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
5565
5640
|
const m = delta[i];
|
|
5566
5641
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
5567
|
-
console.log("\n" +
|
|
5642
|
+
console.log("\n" + import_chalk36.default.green("assistant \u203A ") + m.content + "\n");
|
|
5568
5643
|
return;
|
|
5569
5644
|
}
|
|
5570
5645
|
}
|
|
@@ -5574,7 +5649,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5574
5649
|
const llm2 = loadLlmConfig();
|
|
5575
5650
|
const turnId = crypto2.randomUUID();
|
|
5576
5651
|
for (let round = 0; round < CLIENT_ROUND_CAP; round++) {
|
|
5577
|
-
const spinner = (0,
|
|
5652
|
+
const spinner = (0, import_ora18.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
|
|
5578
5653
|
const body = {
|
|
5579
5654
|
turn_id: turnId,
|
|
5580
5655
|
messages,
|
|
@@ -5590,7 +5665,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5590
5665
|
});
|
|
5591
5666
|
} catch (err) {
|
|
5592
5667
|
spinner.fail("Network error.");
|
|
5593
|
-
console.log(
|
|
5668
|
+
console.log(import_chalk36.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
5594
5669
|
return;
|
|
5595
5670
|
}
|
|
5596
5671
|
let data = null;
|
|
@@ -5608,12 +5683,12 @@ async function replTurn(p, messages, userText) {
|
|
|
5608
5683
|
return;
|
|
5609
5684
|
}
|
|
5610
5685
|
if (res.status === 401) {
|
|
5611
|
-
console.log(
|
|
5686
|
+
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
5687
|
return;
|
|
5613
5688
|
}
|
|
5614
5689
|
if (!res.ok || !data) {
|
|
5615
5690
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
5616
|
-
console.log(
|
|
5691
|
+
console.log(import_chalk36.default.red(` Chat error: ${err}`));
|
|
5617
5692
|
return;
|
|
5618
5693
|
}
|
|
5619
5694
|
if (Array.isArray(data.delta)) {
|
|
@@ -5627,25 +5702,25 @@ async function replTurn(p, messages, userText) {
|
|
|
5627
5702
|
if (warn) console.log(warn);
|
|
5628
5703
|
if (!data.continue) return;
|
|
5629
5704
|
}
|
|
5630
|
-
console.log(
|
|
5705
|
+
console.log(import_chalk36.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
5631
5706
|
}
|
|
5632
5707
|
function renderUpsell(p, upsell) {
|
|
5633
5708
|
const loggedIn = !!loadCredentials();
|
|
5634
5709
|
if (upsell.reason === "CAPPED" && !loggedIn) {
|
|
5635
|
-
console.log("\n" +
|
|
5636
|
-
console.log(
|
|
5710
|
+
console.log("\n" + import_chalk36.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
|
|
5711
|
+
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
5712
|
console.log();
|
|
5638
5713
|
return;
|
|
5639
5714
|
}
|
|
5640
|
-
console.log("\n" +
|
|
5715
|
+
console.log("\n" + import_chalk36.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
5641
5716
|
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
5642
5717
|
if (!loggedIn) {
|
|
5643
|
-
console.log(
|
|
5718
|
+
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
5719
|
} else {
|
|
5645
|
-
console.log(
|
|
5720
|
+
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
5721
|
}
|
|
5647
5722
|
} else if (upsell.reason === "INFLIGHT") {
|
|
5648
|
-
console.log(
|
|
5723
|
+
console.log(import_chalk36.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
5649
5724
|
}
|
|
5650
5725
|
console.log();
|
|
5651
5726
|
}
|
|
@@ -5714,9 +5789,9 @@ async function openServerProxy(project) {
|
|
|
5714
5789
|
const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
|
|
5715
5790
|
let dpKey = prior?.dpKey;
|
|
5716
5791
|
if (!dpKey) {
|
|
5717
|
-
console.log(
|
|
5792
|
+
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
5793
|
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
5719
|
-
console.log(` ${
|
|
5794
|
+
console.log(` ${import_chalk36.default.green("\u2714")} API key: ${import_chalk36.default.dim(maskKey(dpKey))}`);
|
|
5720
5795
|
}
|
|
5721
5796
|
const p = {
|
|
5722
5797
|
projectId: project.projectId,
|
|
@@ -5727,7 +5802,7 @@ async function openServerProxy(project) {
|
|
|
5727
5802
|
proxyUrl: `https://${project.projectId}.abz.run/${version2}/${environment}`,
|
|
5728
5803
|
anon: false
|
|
5729
5804
|
};
|
|
5730
|
-
const spinner = (0,
|
|
5805
|
+
const spinner = (0, import_ora18.default)("Preparing the chat\u2026").start();
|
|
5731
5806
|
try {
|
|
5732
5807
|
const raw = await admin({
|
|
5733
5808
|
method: "GET",
|
|
@@ -5739,7 +5814,7 @@ async function openServerProxy(project) {
|
|
|
5739
5814
|
if (spec2 && (spec2.paths || spec2.openapi)) {
|
|
5740
5815
|
await publishMcp(p, spec2);
|
|
5741
5816
|
} else {
|
|
5742
|
-
console.log(
|
|
5817
|
+
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
5818
|
}
|
|
5744
5819
|
} catch (err) {
|
|
5745
5820
|
spinner.fail("Could not open the proxy.");
|
|
@@ -5792,7 +5867,7 @@ async function noArgsMenu(opts) {
|
|
|
5792
5867
|
const me = loadCredentials()?.apiblazeUserId;
|
|
5793
5868
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
5794
5869
|
const choices = saved.map((a) => ({
|
|
5795
|
-
name: `Chat with ${
|
|
5870
|
+
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
5871
|
value: { type: "existing", a }
|
|
5797
5872
|
}));
|
|
5798
5873
|
const creds = loadCredentials();
|
|
@@ -5802,14 +5877,14 @@ async function noArgsMenu(opts) {
|
|
|
5802
5877
|
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
5803
5878
|
for (const pr of proxies) {
|
|
5804
5879
|
choices.push({
|
|
5805
|
-
name: `Chat with ${
|
|
5880
|
+
name: `Chat with ${import_chalk36.default.bold(pr.projectName)} ${import_chalk36.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
5806
5881
|
value: { type: "server", project: pr }
|
|
5807
5882
|
});
|
|
5808
5883
|
}
|
|
5809
5884
|
} catch {
|
|
5810
5885
|
}
|
|
5811
5886
|
}
|
|
5812
|
-
choices.push({ name:
|
|
5887
|
+
choices.push({ name: import_chalk36.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
5813
5888
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
5814
5889
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
5815
5890
|
]);
|
|
@@ -5881,31 +5956,31 @@ async function noArgsMenu(opts) {
|
|
|
5881
5956
|
async function runRepl(p, initialMessages) {
|
|
5882
5957
|
const { default: inquirer3 } = await import("inquirer");
|
|
5883
5958
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
5884
|
-
console.log("\n" +
|
|
5885
|
-
if (messages.length) console.log(
|
|
5959
|
+
console.log("\n" + import_chalk36.default.cyan.bold("Chat with your API") + import_chalk36.default.dim(` \xB7 ${p.mcpHost}`));
|
|
5960
|
+
if (messages.length) console.log(import_chalk36.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
5886
5961
|
const llm2 = loadLlmConfig();
|
|
5887
5962
|
console.log(
|
|
5888
|
-
|
|
5963
|
+
import_chalk36.default.dim(
|
|
5889
5964
|
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
5965
|
)
|
|
5891
5966
|
);
|
|
5892
5967
|
for (; ; ) {
|
|
5893
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
5968
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk36.default.green("you \u203A") }]);
|
|
5894
5969
|
const text = (input ?? "").trim();
|
|
5895
5970
|
if (!text) continue;
|
|
5896
5971
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
5897
5972
|
if (text === "/login") {
|
|
5898
5973
|
try {
|
|
5899
5974
|
await runLogin();
|
|
5900
|
-
console.log(
|
|
5975
|
+
console.log(import_chalk36.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
5901
5976
|
} catch (err) {
|
|
5902
|
-
console.log(
|
|
5977
|
+
console.log(import_chalk36.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5903
5978
|
}
|
|
5904
5979
|
continue;
|
|
5905
5980
|
}
|
|
5906
5981
|
if (text === "/claim") {
|
|
5907
5982
|
if (!loadCredentials()) {
|
|
5908
|
-
console.log(
|
|
5983
|
+
console.log(import_chalk36.default.yellow(" Log in first: `/login`, then `/claim`."));
|
|
5909
5984
|
continue;
|
|
5910
5985
|
}
|
|
5911
5986
|
try {
|
|
@@ -5914,30 +5989,30 @@ async function runRepl(p, initialMessages) {
|
|
|
5914
5989
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
5915
5990
|
p.anon = false;
|
|
5916
5991
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
5917
|
-
console.log(
|
|
5992
|
+
console.log(import_chalk36.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
5918
5993
|
}
|
|
5919
5994
|
} catch (err) {
|
|
5920
|
-
console.log(
|
|
5995
|
+
console.log(import_chalk36.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5921
5996
|
}
|
|
5922
5997
|
continue;
|
|
5923
5998
|
}
|
|
5924
5999
|
if (text === "/showauth") {
|
|
5925
6000
|
revealAuth = !revealAuth;
|
|
5926
|
-
console.log(
|
|
6001
|
+
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
6002
|
continue;
|
|
5928
6003
|
}
|
|
5929
6004
|
if (text.startsWith("/")) {
|
|
5930
|
-
console.log(
|
|
6005
|
+
console.log(import_chalk36.default.dim(" Commands: /login /claim /showauth /exit"));
|
|
5931
6006
|
continue;
|
|
5932
6007
|
}
|
|
5933
6008
|
await replTurn(p, messages, text);
|
|
5934
6009
|
saveTranscript(p, messages);
|
|
5935
6010
|
}
|
|
5936
|
-
console.log(
|
|
6011
|
+
console.log(import_chalk36.default.dim("\nBye."));
|
|
5937
6012
|
}
|
|
5938
6013
|
async function runApichat(opts) {
|
|
5939
6014
|
setVerbose(opts.verbose !== false);
|
|
5940
|
-
console.log(
|
|
6015
|
+
console.log(import_chalk36.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
5941
6016
|
if (!opts.openapispec && !opts.target) {
|
|
5942
6017
|
if (!process.stdin.isTTY) {
|
|
5943
6018
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -5950,7 +6025,7 @@ async function runApichat(opts) {
|
|
|
5950
6025
|
}
|
|
5951
6026
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
5952
6027
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
5953
|
-
console.log(` ${
|
|
6028
|
+
console.log(` ${import_chalk36.default.dim("Target:")} ${import_chalk36.default.bold(target)}`);
|
|
5954
6029
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
5955
6030
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
5956
6031
|
fail4(
|
|
@@ -5959,7 +6034,7 @@ async function runApichat(opts) {
|
|
|
5959
6034
|
);
|
|
5960
6035
|
}
|
|
5961
6036
|
const p = await provision(spec2, target, opts);
|
|
5962
|
-
console.log(` ${
|
|
6037
|
+
console.log(` ${import_chalk36.default.dim("Proxy: ")} ${import_chalk36.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
5963
6038
|
upsertApichat({
|
|
5964
6039
|
name: p.projectId,
|
|
5965
6040
|
target,
|
|
@@ -5978,23 +6053,23 @@ async function runApichat(opts) {
|
|
|
5978
6053
|
const secret = await captureTargetSecret(auth, opts);
|
|
5979
6054
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
5980
6055
|
} else {
|
|
5981
|
-
console.log(
|
|
6056
|
+
console.log(import_chalk36.default.dim(" Target auth: none required."));
|
|
5982
6057
|
}
|
|
5983
6058
|
const specText = JSON.stringify(spec2);
|
|
5984
6059
|
await uploadSpec(p, specText, opts);
|
|
5985
6060
|
const mcpUrl = await publishMcp(p, spec2);
|
|
5986
6061
|
console.log();
|
|
5987
|
-
if (p.proxyUrl) console.log(` ${
|
|
5988
|
-
if (mcpUrl) console.log(` ${
|
|
6062
|
+
if (p.proxyUrl) console.log(` ${import_chalk36.default.green("\u2713")} proxy ${import_chalk36.default.bold(p.proxyUrl)}`);
|
|
6063
|
+
if (mcpUrl) console.log(` ${import_chalk36.default.green("\u2713")} mcp ${import_chalk36.default.bold(mcpUrl)}`);
|
|
5989
6064
|
if (p.anon) {
|
|
5990
|
-
console.log(
|
|
6065
|
+
console.log(import_chalk36.default.dim("\n Anonymous workspace \u2014 /claim inside the chat (after /login) to keep it beyond 30 days."));
|
|
5991
6066
|
}
|
|
5992
6067
|
await runRepl(p);
|
|
5993
6068
|
}
|
|
5994
6069
|
|
|
5995
6070
|
// src/commands/consumer.ts
|
|
5996
|
-
var
|
|
5997
|
-
var
|
|
6071
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
6072
|
+
var import_ora19 = __toESM(require("ora"));
|
|
5998
6073
|
init_admin();
|
|
5999
6074
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
6000
6075
|
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
@@ -6014,7 +6089,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
6014
6089
|
function requireConsumer() {
|
|
6015
6090
|
const c = loadConsumer();
|
|
6016
6091
|
if (!c) {
|
|
6017
|
-
console.error(
|
|
6092
|
+
console.error(import_chalk37.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
6018
6093
|
process.exit(1);
|
|
6019
6094
|
}
|
|
6020
6095
|
return c;
|
|
@@ -6025,7 +6100,7 @@ async function runConsumerLogin(opts) {
|
|
|
6025
6100
|
let clientId = opts.client;
|
|
6026
6101
|
if (clientId) {
|
|
6027
6102
|
if (!tenant2) {
|
|
6028
|
-
console.error(
|
|
6103
|
+
console.error(import_chalk37.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
6029
6104
|
process.exit(1);
|
|
6030
6105
|
}
|
|
6031
6106
|
} else {
|
|
@@ -6037,25 +6112,25 @@ async function runConsumerLogin(opts) {
|
|
|
6037
6112
|
if (!picked) process.exit(1);
|
|
6038
6113
|
tenant2 = picked;
|
|
6039
6114
|
}
|
|
6040
|
-
const s2 = (0,
|
|
6115
|
+
const s2 = (0, import_ora19.default)("Finding the login app...").start();
|
|
6041
6116
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
6042
6117
|
s2.stop();
|
|
6043
6118
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
6044
6119
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
6045
6120
|
if (!pick2) {
|
|
6046
|
-
console.error(
|
|
6121
|
+
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
6122
|
process.exit(1);
|
|
6048
6123
|
}
|
|
6049
6124
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
6050
6125
|
}
|
|
6051
6126
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
6052
|
-
console.log(`${
|
|
6127
|
+
console.log(`${import_chalk37.default.cyan("\u2192")} Logging in to ${import_chalk37.default.bold(tenant2)} as a consumer...`);
|
|
6053
6128
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
6054
6129
|
console.log(`
|
|
6055
|
-
Open: ${
|
|
6056
|
-
console.log(` Code: ${
|
|
6130
|
+
Open: ${import_chalk37.default.underline(verificationUri)}`);
|
|
6131
|
+
console.log(` Code: ${import_chalk37.default.bold(userCode)}
|
|
6057
6132
|
`);
|
|
6058
|
-
console.log(
|
|
6133
|
+
console.log(import_chalk37.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
6059
6134
|
}, portalResource);
|
|
6060
6135
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
6061
6136
|
const creds = {
|
|
@@ -6070,7 +6145,7 @@ async function runConsumerLogin(opts) {
|
|
|
6070
6145
|
obtainedAt: Date.now()
|
|
6071
6146
|
};
|
|
6072
6147
|
saveConsumer(creds);
|
|
6073
|
-
console.log(
|
|
6148
|
+
console.log(import_chalk37.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
6074
6149
|
}
|
|
6075
6150
|
async function runConsumerTokens(opts) {
|
|
6076
6151
|
const creds = requireConsumer();
|
|
@@ -6083,29 +6158,29 @@ async function runConsumerTokens(opts) {
|
|
|
6083
6158
|
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
6159
|
return;
|
|
6085
6160
|
}
|
|
6086
|
-
console.log(`${
|
|
6161
|
+
console.log(`${import_chalk37.default.cyan("Consumer")} ${import_chalk37.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk37.default.bold(fresh.tenant)}
|
|
6087
6162
|
`);
|
|
6088
|
-
console.log(`${
|
|
6163
|
+
console.log(`${import_chalk37.default.bold("access_token")} ${import_chalk37.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
6089
6164
|
${fresh.accessToken}
|
|
6090
6165
|
`);
|
|
6091
|
-
if (fresh.idToken) console.log(`${
|
|
6166
|
+
if (fresh.idToken) console.log(`${import_chalk37.default.bold("id_token")} ${import_chalk37.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
6092
6167
|
${fresh.idToken}
|
|
6093
6168
|
`);
|
|
6094
|
-
if (fresh.refreshToken) console.log(`${
|
|
6169
|
+
if (fresh.refreshToken) console.log(`${import_chalk37.default.bold("refresh_token")}
|
|
6095
6170
|
${fresh.refreshToken}
|
|
6096
6171
|
`);
|
|
6097
|
-
console.log(
|
|
6172
|
+
console.log(import_chalk37.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
6098
6173
|
}
|
|
6099
6174
|
async function runConsumerApikeys(opts) {
|
|
6100
6175
|
const creds = requireConsumer();
|
|
6101
6176
|
const { default: inquirer3 } = await import("inquirer");
|
|
6102
|
-
const spinner = (0,
|
|
6177
|
+
const spinner = (0, import_ora19.default)("Loading your API keys...").start();
|
|
6103
6178
|
const list = await consumerFetch(creds, "/apikeys");
|
|
6104
6179
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
6105
6180
|
spinner.stop();
|
|
6106
6181
|
if (list.status >= 400) {
|
|
6107
|
-
console.error(
|
|
6108
|
-
if (list.status === 401) console.error(
|
|
6182
|
+
console.error(import_chalk37.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
6183
|
+
if (list.status === 401) console.error(import_chalk37.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
6109
6184
|
process.exit(1);
|
|
6110
6185
|
}
|
|
6111
6186
|
const keys = list.data?.keys ?? [];
|
|
@@ -6113,16 +6188,16 @@ async function runConsumerApikeys(opts) {
|
|
|
6113
6188
|
if (opts.json) {
|
|
6114
6189
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
6115
6190
|
} else if (!keys.length) {
|
|
6116
|
-
console.log(
|
|
6191
|
+
console.log(import_chalk37.default.yellow("No API keys yet."));
|
|
6117
6192
|
} else {
|
|
6118
6193
|
for (const k of keys) {
|
|
6119
6194
|
const clear = revealMap[k.environment]?.key;
|
|
6120
|
-
const shown = clear ?
|
|
6121
|
-
const exp = k.expires_at ?
|
|
6122
|
-
console.log(` ${
|
|
6195
|
+
const shown = clear ? import_chalk37.default.green(clear) : import_chalk37.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
6196
|
+
const exp = k.expires_at ? import_chalk37.default.dim(`exp ${k.expires_at}`) : import_chalk37.default.dim("no expiry");
|
|
6197
|
+
console.log(` ${import_chalk37.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk37.default.dim(k.description ?? "")}`);
|
|
6123
6198
|
}
|
|
6124
6199
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
6125
|
-
console.log(
|
|
6200
|
+
console.log(import_chalk37.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
6126
6201
|
}
|
|
6127
6202
|
}
|
|
6128
6203
|
if (opts.json) return;
|
|
@@ -6136,7 +6211,7 @@ async function runConsumerApikeys(opts) {
|
|
|
6136
6211
|
const body = { environment: answers.environment };
|
|
6137
6212
|
if (answers.description) body.description = answers.description;
|
|
6138
6213
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
6139
|
-
const s2 = (0,
|
|
6214
|
+
const s2 = (0, import_ora19.default)("Creating key...").start();
|
|
6140
6215
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
6141
6216
|
if (created.status >= 400) {
|
|
6142
6217
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -6144,13 +6219,13 @@ async function runConsumerApikeys(opts) {
|
|
|
6144
6219
|
}
|
|
6145
6220
|
s2.succeed("Key created.");
|
|
6146
6221
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
6147
|
-
if (key) console.log(` ${
|
|
6148
|
-
else console.log(
|
|
6222
|
+
if (key) console.log(` ${import_chalk37.default.green(key)} ${import_chalk37.default.dim("(shown once \u2014 store it now)")}`);
|
|
6223
|
+
else console.log(import_chalk37.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
6149
6224
|
}
|
|
6150
6225
|
|
|
6151
6226
|
// src/commands/sidecar.ts
|
|
6152
|
-
var
|
|
6153
|
-
var
|
|
6227
|
+
var import_chalk38 = __toESM(require("chalk"));
|
|
6228
|
+
var import_ora20 = __toESM(require("ora"));
|
|
6154
6229
|
var fs10 = __toESM(require("fs"));
|
|
6155
6230
|
var path7 = __toESM(require("path"));
|
|
6156
6231
|
init_admin();
|
|
@@ -6191,18 +6266,18 @@ function upsertEnvLocal(root, token) {
|
|
|
6191
6266
|
}
|
|
6192
6267
|
function installSidecarPackage(root) {
|
|
6193
6268
|
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
6194
|
-
console.log(` ${
|
|
6269
|
+
console.log(` ${import_chalk38.default.green("\u2713")} apiblaze package already installed`);
|
|
6195
6270
|
return;
|
|
6196
6271
|
}
|
|
6197
6272
|
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
6198
6273
|
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,
|
|
6274
|
+
const spinner = (0, import_ora20.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
6200
6275
|
try {
|
|
6201
6276
|
const { execSync } = require("child_process");
|
|
6202
6277
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
6203
6278
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
6204
6279
|
} catch {
|
|
6205
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
6280
|
+
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
6281
|
}
|
|
6207
6282
|
}
|
|
6208
6283
|
function readEnvKey(root) {
|
|
@@ -6341,7 +6416,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6341
6416
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
6342
6417
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
6343
6418
|
if (opts.newSession) clearAnonCred2();
|
|
6344
|
-
const spinner = (0,
|
|
6419
|
+
const spinner = (0, import_ora20.default)("Setting up a sidecar (no login needed)...").start();
|
|
6345
6420
|
let out;
|
|
6346
6421
|
try {
|
|
6347
6422
|
out = await sidecarInitAnonymous2();
|
|
@@ -6353,29 +6428,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6353
6428
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
6354
6429
|
const envState = upsertEnvLocal(root, out.token);
|
|
6355
6430
|
ensureGitignored(root);
|
|
6356
|
-
console.log(` ${
|
|
6357
|
-
console.log(` ${
|
|
6431
|
+
console.log(` ${import_chalk38.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6432
|
+
console.log(` ${import_chalk38.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6358
6433
|
installSidecarPackage(root);
|
|
6359
6434
|
let inspectorPath = null;
|
|
6360
6435
|
if (!opts.noInspector) {
|
|
6361
6436
|
inspectorPath = generateInspector(root, router);
|
|
6362
|
-
if (inspectorPath) console.log(` ${
|
|
6437
|
+
if (inspectorPath) console.log(` ${import_chalk38.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6363
6438
|
}
|
|
6364
6439
|
console.log("");
|
|
6365
|
-
console.log(
|
|
6366
|
-
console.log(` 1. ${
|
|
6440
|
+
console.log(import_chalk38.default.bold("Done (no account needed). What happens next:"));
|
|
6441
|
+
console.log(` 1. ${import_chalk38.default.cyan("npm run dev")} and use your app.`);
|
|
6367
6442
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
6368
|
-
console.log(` ${
|
|
6443
|
+
console.log(` ${import_chalk38.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
6369
6444
|
console.log("");
|
|
6370
|
-
console.log(
|
|
6371
|
-
console.log(` ${
|
|
6372
|
-
console.log(
|
|
6445
|
+
console.log(import_chalk38.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
6446
|
+
console.log(` ${import_chalk38.default.cyan("apiblaze login")} then ${import_chalk38.default.cyan("apiblaze claim")} ${import_chalk38.default.dim("(no code needed here)")}`);
|
|
6447
|
+
console.log(import_chalk38.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
6373
6448
|
}
|
|
6374
6449
|
async function runSidecar(opts) {
|
|
6375
6450
|
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
6376
6451
|
const detected = detectNextProject(root);
|
|
6377
6452
|
if (!detected.found) {
|
|
6378
|
-
console.log(
|
|
6453
|
+
console.log(import_chalk38.default.yellow(`No Next.js project detected in ${root}.`));
|
|
6379
6454
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
6380
6455
|
return;
|
|
6381
6456
|
}
|
|
@@ -6386,10 +6461,10 @@ async function runSidecar(opts) {
|
|
|
6386
6461
|
if (!loadCredentials()) {
|
|
6387
6462
|
upsertEnvLocal(root, readEnvKey(root));
|
|
6388
6463
|
ensureGitignored(root);
|
|
6389
|
-
console.log(` ${
|
|
6390
|
-
console.log(` ${
|
|
6464
|
+
console.log(` ${import_chalk38.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
6465
|
+
console.log(` ${import_chalk38.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6391
6466
|
installSidecarPackage(root);
|
|
6392
|
-
console.log(
|
|
6467
|
+
console.log(import_chalk38.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
6393
6468
|
return;
|
|
6394
6469
|
}
|
|
6395
6470
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -6398,7 +6473,7 @@ async function runSidecar(opts) {
|
|
|
6398
6473
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
6399
6474
|
let token = existingKey ?? "";
|
|
6400
6475
|
if (mustMint) {
|
|
6401
|
-
const spinner = (0,
|
|
6476
|
+
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
6477
|
try {
|
|
6403
6478
|
const out = await admin({
|
|
6404
6479
|
method: "POST",
|
|
@@ -6412,39 +6487,39 @@ async function runSidecar(opts) {
|
|
|
6412
6487
|
throw err;
|
|
6413
6488
|
}
|
|
6414
6489
|
} else {
|
|
6415
|
-
console.log(
|
|
6490
|
+
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
6491
|
}
|
|
6417
6492
|
const envState = upsertEnvLocal(root, token);
|
|
6418
6493
|
ensureGitignored(root);
|
|
6419
|
-
console.log(` ${
|
|
6494
|
+
console.log(` ${import_chalk38.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6420
6495
|
const wireState = wireInstrumentation(root);
|
|
6421
|
-
console.log(` ${
|
|
6496
|
+
console.log(` ${import_chalk38.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
6422
6497
|
installSidecarPackage(root);
|
|
6423
6498
|
let inspectorPath = null;
|
|
6424
6499
|
if (!opts.noInspector) {
|
|
6425
6500
|
inspectorPath = generateInspector(root, detected.router);
|
|
6426
|
-
if (inspectorPath) console.log(` ${
|
|
6501
|
+
if (inspectorPath) console.log(` ${import_chalk38.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6427
6502
|
}
|
|
6428
6503
|
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: ${
|
|
6504
|
+
console.log(import_chalk38.default.bold("Done. What happens next:"));
|
|
6505
|
+
console.log(` 1. ${import_chalk38.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
6506
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk38.default.bold("candidates")} \u2014 list them: ${import_chalk38.default.cyan("apiblaze sidecar")}`);
|
|
6507
|
+
console.log(` 3. Approve the ones to route: ${import_chalk38.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
6433
6508
|
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(
|
|
6509
|
+
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)`);
|
|
6510
|
+
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
6511
|
console.log("");
|
|
6437
|
-
console.log(
|
|
6438
|
-
console.log(
|
|
6439
|
-
console.log(
|
|
6512
|
+
console.log(import_chalk38.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
6513
|
+
console.log(import_chalk38.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
6514
|
+
console.log(import_chalk38.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
6440
6515
|
console.log("");
|
|
6441
|
-
console.log(
|
|
6442
|
-
console.log(
|
|
6516
|
+
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."));
|
|
6517
|
+
console.log(import_chalk38.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
6443
6518
|
}
|
|
6444
6519
|
|
|
6445
6520
|
// src/commands/origins.ts
|
|
6446
|
-
var
|
|
6447
|
-
var
|
|
6521
|
+
var import_chalk39 = __toESM(require("chalk"));
|
|
6522
|
+
var import_ora21 = __toESM(require("ora"));
|
|
6448
6523
|
init_admin();
|
|
6449
6524
|
init_auth();
|
|
6450
6525
|
init_anon_cred();
|
|
@@ -6453,7 +6528,7 @@ async function runOriginsList(opts) {
|
|
|
6453
6528
|
if (!loadCredentials()) {
|
|
6454
6529
|
const cred = loadAnonCred();
|
|
6455
6530
|
if (!cred) {
|
|
6456
|
-
console.log(
|
|
6531
|
+
console.log(import_chalk39.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
6457
6532
|
return;
|
|
6458
6533
|
}
|
|
6459
6534
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -6471,30 +6546,30 @@ async function runOriginsList(opts) {
|
|
|
6471
6546
|
}
|
|
6472
6547
|
const routed = out.routed ?? [];
|
|
6473
6548
|
const candidates = out.candidates ?? [];
|
|
6474
|
-
console.log(
|
|
6549
|
+
console.log(import_chalk39.default.bold(`
|
|
6475
6550
|
Routed through APIblaze (${routed.length})`));
|
|
6476
|
-
if (!routed.length) console.log(
|
|
6477
|
-
for (const r of routed) console.log(` ${
|
|
6478
|
-
console.log(
|
|
6551
|
+
if (!routed.length) console.log(import_chalk39.default.dim(" none yet"));
|
|
6552
|
+
for (const r of routed) console.log(` ${import_chalk39.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk39.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
6553
|
+
console.log(import_chalk39.default.bold(`
|
|
6479
6554
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
6480
|
-
if (!candidates.length) console.log(
|
|
6555
|
+
if (!candidates.length) console.log(import_chalk39.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
6481
6556
|
for (const c of candidates) {
|
|
6482
|
-
console.log(` ${
|
|
6557
|
+
console.log(` ${import_chalk39.default.yellow("\u25CB")} ${c.origin} ${import_chalk39.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
6483
6558
|
}
|
|
6484
6559
|
if (candidates.length) {
|
|
6485
|
-
console.log(
|
|
6560
|
+
console.log(import_chalk39.default.dim(`
|
|
6486
6561
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
6487
|
-
console.log(
|
|
6562
|
+
console.log(import_chalk39.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
6488
6563
|
}
|
|
6489
6564
|
}
|
|
6490
6565
|
async function runOriginsApprove(origin, opts) {
|
|
6491
6566
|
if (!loadCredentials()) {
|
|
6492
6567
|
const cred = loadAnonCred();
|
|
6493
6568
|
if (!cred) {
|
|
6494
|
-
console.error(
|
|
6569
|
+
console.error(import_chalk39.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
6495
6570
|
process.exit(1);
|
|
6496
6571
|
}
|
|
6497
|
-
const spinner2 = (0,
|
|
6572
|
+
const spinner2 = (0, import_ora21.default)(`Approving ${origin} (anonymous)...`).start();
|
|
6498
6573
|
try {
|
|
6499
6574
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
6500
6575
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -6505,7 +6580,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
6505
6580
|
return;
|
|
6506
6581
|
}
|
|
6507
6582
|
const { teamId } = await resolveTeam(opts.team);
|
|
6508
|
-
const spinner = (0,
|
|
6583
|
+
const spinner = (0, import_ora21.default)(`Approving ${origin}...`).start();
|
|
6509
6584
|
try {
|
|
6510
6585
|
const out = await admin({
|
|
6511
6586
|
method: "POST",
|
|
@@ -6522,7 +6597,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
6522
6597
|
}
|
|
6523
6598
|
async function runOriginsDeny(origin, opts) {
|
|
6524
6599
|
const { teamId } = await resolveTeam(opts.team);
|
|
6525
|
-
const spinner = (0,
|
|
6600
|
+
const spinner = (0, import_ora21.default)(`Dismissing ${origin}...`).start();
|
|
6526
6601
|
try {
|
|
6527
6602
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
6528
6603
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -6533,7 +6608,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
6533
6608
|
}
|
|
6534
6609
|
async function runOriginsRemove(origin, opts) {
|
|
6535
6610
|
const { teamId } = await resolveTeam(opts.team);
|
|
6536
|
-
const spinner = (0,
|
|
6611
|
+
const spinner = (0, import_ora21.default)(`Removing the proxy for ${origin}...`).start();
|
|
6537
6612
|
try {
|
|
6538
6613
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
6539
6614
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -6544,7 +6619,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
6544
6619
|
}
|
|
6545
6620
|
|
|
6546
6621
|
// src/commands/op.ts
|
|
6547
|
-
var
|
|
6622
|
+
var import_chalk40 = __toESM(require("chalk"));
|
|
6548
6623
|
init_auth();
|
|
6549
6624
|
init_trace();
|
|
6550
6625
|
init_types();
|
|
@@ -6577,82 +6652,82 @@ function printResidue(report, applied) {
|
|
|
6577
6652
|
const up = report?.upstash ?? {};
|
|
6578
6653
|
const fga = report?.fga ?? {};
|
|
6579
6654
|
const ghosts = report?.ghosts ?? {};
|
|
6580
|
-
console.log(
|
|
6581
|
-
console.log(
|
|
6655
|
+
console.log(import_chalk40.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
6656
|
+
console.log(import_chalk40.default.bold("\n Upstash"));
|
|
6582
6657
|
const orphans = up.orphans ?? [];
|
|
6583
|
-
if (orphans.length === 0) console.log(
|
|
6584
|
-
for (const o of orphans) console.log(` ${
|
|
6585
|
-
console.log(
|
|
6658
|
+
if (orphans.length === 0) console.log(import_chalk40.default.green(" no orphaned keys"));
|
|
6659
|
+
for (const o of orphans) console.log(` ${import_chalk40.default.yellow(o.key)} ${import_chalk40.default.dim(`\u2014 ${o.reason}`)}`);
|
|
6660
|
+
console.log(import_chalk40.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
6586
6661
|
if (up.anon_wallet_detail) {
|
|
6587
6662
|
const d = up.anon_wallet_detail;
|
|
6588
|
-
console.log(
|
|
6663
|
+
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
6664
|
}
|
|
6590
6665
|
if (up.keyspace_census) {
|
|
6591
6666
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
6592
|
-
console.log(
|
|
6667
|
+
console.log(import_chalk40.default.dim(` keyspace: ${census}`));
|
|
6593
6668
|
}
|
|
6594
|
-
if (up.unknown?.length) console.log(
|
|
6595
|
-
if (applied) console.log(` ${
|
|
6596
|
-
for (const e of up.errors ?? []) console.log(
|
|
6597
|
-
console.log(
|
|
6669
|
+
if (up.unknown?.length) console.log(import_chalk40.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
6670
|
+
if (applied) console.log(` ${import_chalk40.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
6671
|
+
for (const e of up.errors ?? []) console.log(import_chalk40.default.red(` error: ${e}`));
|
|
6672
|
+
console.log(import_chalk40.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
6598
6673
|
if (applied) {
|
|
6599
6674
|
const swept = fga?.swept ?? [];
|
|
6600
|
-
if (swept.length === 0) console.log(
|
|
6675
|
+
if (swept.length === 0) console.log(import_chalk40.default.green(" no orphaned stores"));
|
|
6601
6676
|
for (const s of swept) {
|
|
6602
6677
|
console.log(
|
|
6603
|
-
` ${
|
|
6678
|
+
` ${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
6679
|
);
|
|
6605
6680
|
}
|
|
6606
|
-
if (fga?.remaining) console.log(
|
|
6681
|
+
if (fga?.remaining) console.log(import_chalk40.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
6607
6682
|
const st = fga?.side_tables;
|
|
6608
|
-
if (st) console.log(
|
|
6683
|
+
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
6684
|
} else {
|
|
6610
6685
|
const fgaOrphans = fga?.orphans ?? [];
|
|
6611
|
-
if (fgaOrphans.length === 0) console.log(
|
|
6686
|
+
if (fgaOrphans.length === 0) console.log(import_chalk40.default.green(" no orphaned stores"));
|
|
6612
6687
|
for (const s of fgaOrphans) {
|
|
6613
6688
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
6614
|
-
console.log(` ${
|
|
6689
|
+
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
6690
|
}
|
|
6616
|
-
console.log(
|
|
6691
|
+
console.log(import_chalk40.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
6617
6692
|
const st = fga?.side_tables;
|
|
6618
|
-
if (st) console.log(
|
|
6693
|
+
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
6694
|
}
|
|
6620
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
6621
|
-
console.log(
|
|
6695
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk40.default.red(` error: ${e}`));
|
|
6696
|
+
console.log(import_chalk40.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
6622
6697
|
if (applied) {
|
|
6623
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
6624
|
-
else console.log(` ${
|
|
6698
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk40.default.green(" no ghost tuples"));
|
|
6699
|
+
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
6700
|
} else {
|
|
6626
6701
|
const n = ghosts?.ghost_count ?? 0;
|
|
6627
|
-
if (n === 0) console.log(
|
|
6702
|
+
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
6703
|
else {
|
|
6629
|
-
console.log(
|
|
6704
|
+
console.log(import_chalk40.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
6630
6705
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
6631
|
-
console.log(
|
|
6706
|
+
console.log(import_chalk40.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
6632
6707
|
}
|
|
6633
|
-
if (n > 20) console.log(
|
|
6708
|
+
if (n > 20) console.log(import_chalk40.default.dim(` \u2026 and ${n - 20} more`));
|
|
6634
6709
|
}
|
|
6635
6710
|
}
|
|
6636
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
6711
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk40.default.red(` error: ${e}`));
|
|
6637
6712
|
console.log();
|
|
6638
6713
|
}
|
|
6639
6714
|
async function runOp(sub, opts = {}) {
|
|
6640
6715
|
if (!loadCredentials()) {
|
|
6641
|
-
console.log(
|
|
6716
|
+
console.log(import_chalk40.default.dim("Not logged in. Run `apiblaze login`."));
|
|
6642
6717
|
return;
|
|
6643
6718
|
}
|
|
6644
6719
|
if (!isOperatorLogin()) {
|
|
6645
|
-
console.log(
|
|
6720
|
+
console.log(import_chalk40.default.dim("`apiblaze op` is only available to platform operators."));
|
|
6646
6721
|
return;
|
|
6647
6722
|
}
|
|
6648
6723
|
switch (sub) {
|
|
6649
6724
|
case void 0:
|
|
6650
6725
|
case "menu": {
|
|
6651
|
-
console.log(
|
|
6652
|
-
console.log(` ${
|
|
6653
|
-
console.log(` ${
|
|
6654
|
-
console.log(` ${
|
|
6655
|
-
console.log(
|
|
6726
|
+
console.log(import_chalk40.default.bold("\nOperator menu"));
|
|
6727
|
+
console.log(` ${import_chalk40.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
6728
|
+
console.log(` ${import_chalk40.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk40.default.dim("-y to skip")})`);
|
|
6729
|
+
console.log(` ${import_chalk40.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
6730
|
+
console.log(import_chalk40.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
6656
6731
|
`));
|
|
6657
6732
|
return;
|
|
6658
6733
|
}
|
|
@@ -6671,17 +6746,17 @@ async function runOp(sub, opts = {}) {
|
|
|
6671
6746
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
6672
6747
|
printResidue(report, false);
|
|
6673
6748
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
6674
|
-
console.log(
|
|
6749
|
+
console.log(import_chalk40.default.green("Nothing to sweep."));
|
|
6675
6750
|
return;
|
|
6676
6751
|
}
|
|
6677
6752
|
if (!opts.yes) {
|
|
6678
6753
|
const readline2 = await import("readline/promises");
|
|
6679
6754
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
6680
6755
|
const answer = await rl.question(
|
|
6681
|
-
|
|
6756
|
+
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
6757
|
);
|
|
6683
6758
|
rl.close();
|
|
6684
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
6759
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk40.default.dim("Aborted."));
|
|
6685
6760
|
}
|
|
6686
6761
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
6687
6762
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -6692,15 +6767,15 @@ async function runOp(sub, opts = {}) {
|
|
|
6692
6767
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
6693
6768
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
6694
6769
|
const accounts = data?.accounts ?? [];
|
|
6695
|
-
if (accounts.length === 0) return void console.log(
|
|
6770
|
+
if (accounts.length === 0) return void console.log(import_chalk40.default.dim("No credit wallets."));
|
|
6696
6771
|
for (const a of accounts) {
|
|
6697
6772
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
6698
|
-
console.log(` ${
|
|
6773
|
+
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
6774
|
}
|
|
6700
6775
|
return;
|
|
6701
6776
|
}
|
|
6702
6777
|
default:
|
|
6703
|
-
console.log(
|
|
6778
|
+
console.log(import_chalk40.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
6704
6779
|
}
|
|
6705
6780
|
}
|
|
6706
6781
|
|
|
@@ -6743,6 +6818,7 @@ program.command("create").description("Create a new API proxy (no login needed \
|
|
|
6743
6818
|
});
|
|
6744
6819
|
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
6820
|
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)));
|
|
6821
|
+
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
6822
|
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
6823
|
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
6824
|
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 +6838,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
6762
6838
|
try {
|
|
6763
6839
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
6764
6840
|
if (Number.isNaN(resolved)) {
|
|
6765
|
-
console.error(
|
|
6841
|
+
console.error(import_chalk41.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
6766
6842
|
process.exit(1);
|
|
6767
6843
|
}
|
|
6768
6844
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -6852,7 +6928,7 @@ spec.command("set").description("Replace the stored OpenAPI spec from a local fi
|
|
|
6852
6928
|
var HELP_GROUPS = [
|
|
6853
6929
|
{ title: "Chat", commands: ["apichat", "agent"] },
|
|
6854
6930
|
{ 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"] },
|
|
6931
|
+
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
6856
6932
|
{ title: "Data plane commands", commands: [
|
|
6857
6933
|
{ parent: "consumer", sub: "login" },
|
|
6858
6934
|
{ parent: "consumer", sub: "apikeys" }
|
|
@@ -6871,7 +6947,7 @@ function groupedCommandHelp() {
|
|
|
6871
6947
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
6872
6948
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
6873
6949
|
}).filter(Boolean).join("\n");
|
|
6874
|
-
return `${
|
|
6950
|
+
return `${import_chalk41.default.bold(g.title)}
|
|
6875
6951
|
${rows}`;
|
|
6876
6952
|
}).join("\n\n");
|
|
6877
6953
|
}
|
|
@@ -6905,14 +6981,14 @@ async function recoverStaleTeam() {
|
|
|
6905
6981
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
6906
6982
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
6907
6983
|
if (!linked) {
|
|
6908
|
-
console.error(
|
|
6984
|
+
console.error(import_chalk41.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
6909
6985
|
return;
|
|
6910
6986
|
}
|
|
6911
6987
|
if (linked.teamId === creds.teamId) return;
|
|
6912
6988
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
6913
6989
|
delete next.activeTenant;
|
|
6914
6990
|
saveCredentials(next);
|
|
6915
|
-
console.error(
|
|
6991
|
+
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
6992
|
} catch {
|
|
6917
6993
|
}
|
|
6918
6994
|
}
|
|
@@ -6920,16 +6996,16 @@ async function printError(err) {
|
|
|
6920
6996
|
if (err instanceof ApiError) {
|
|
6921
6997
|
const data = err.body;
|
|
6922
6998
|
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(
|
|
6999
|
+
console.error(import_chalk41.default.red(`
|
|
6924
7000
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
6925
7001
|
if (err.status === 403 || err.status === 404) {
|
|
6926
7002
|
await recoverStaleTeam();
|
|
6927
7003
|
}
|
|
6928
7004
|
} else if (err instanceof Error) {
|
|
6929
|
-
console.error(
|
|
7005
|
+
console.error(import_chalk41.default.red(`
|
|
6930
7006
|
Error: ${err.message}`));
|
|
6931
7007
|
} else {
|
|
6932
|
-
console.error(
|
|
7008
|
+
console.error(import_chalk41.default.red("\nUnknown error"));
|
|
6933
7009
|
}
|
|
6934
7010
|
}
|
|
6935
7011
|
program.parse(process.argv);
|