apiblaze 0.19.0 → 0.19.2
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 +389 -309
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -577,10 +577,10 @@ async function resolveProjectVia(caller, teamId, nameOrId, version2) {
|
|
|
577
577
|
summary: `List projects for ${teamId}`
|
|
578
578
|
});
|
|
579
579
|
const rows = out?.projects ?? [];
|
|
580
|
-
const candidates = rows.filter((r) => r.project_id === nameOrId || r.
|
|
580
|
+
const candidates = rows.filter((r) => r.project_id === nameOrId || r.project_display_name === nameOrId || r.display_name === nameOrId);
|
|
581
581
|
if (!candidates.length) {
|
|
582
582
|
throw new Error(
|
|
583
|
-
`Project "${nameOrId}" not found in this team.` + (rows.length ? ` Known: ${rows.map((r) => r.
|
|
583
|
+
`Project "${nameOrId}" not found in this team.` + (rows.length ? ` Known: ${rows.map((r) => r.project_id).join(", ")}` : "")
|
|
584
584
|
);
|
|
585
585
|
}
|
|
586
586
|
const chosen = version2 ? candidates.find((r) => r.api_version === version2) : candidates[0];
|
|
@@ -590,11 +590,12 @@ async function resolveProjectVia(caller, teamId, nameOrId, version2) {
|
|
|
590
590
|
);
|
|
591
591
|
}
|
|
592
592
|
return {
|
|
593
|
+
// A proxy is addressed by its project_id (that IS its name); display_name is cosmetic.
|
|
593
594
|
projectId: chosen.project_id,
|
|
594
|
-
projectName: chosen.
|
|
595
|
+
projectName: chosen.project_display_name ?? chosen.display_name ?? chosen.project_id,
|
|
595
596
|
apiVersion: chosen.api_version,
|
|
596
597
|
teamId,
|
|
597
|
-
tenant: chosen.tenant ?? chosen.config?.
|
|
598
|
+
tenant: chosen.config?.tenant ?? chosen.config?.default_tenant
|
|
598
599
|
};
|
|
599
600
|
}
|
|
600
601
|
var init_caller = __esm({
|
|
@@ -626,15 +627,16 @@ async function runIamToggle(project, state, opts) {
|
|
|
626
627
|
if (!tenant2) {
|
|
627
628
|
throw new Error("Could not resolve the tenant for this project. Pass --tenant <name>.");
|
|
628
629
|
}
|
|
629
|
-
const spinner = (0, import_ora4.default)(`Turning IAM ${state} for tenant ${tenant2}...`).start();
|
|
630
|
+
const spinner = opts.quiet ? null : (0, import_ora4.default)(`Turning IAM ${state} for tenant ${tenant2}...`).start();
|
|
630
631
|
try {
|
|
631
632
|
const out = await producer(caller, {
|
|
632
633
|
method: "PATCH",
|
|
633
634
|
path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`,
|
|
634
|
-
body: {
|
|
635
|
+
body: { enabled },
|
|
635
636
|
summary: `IAM ${state} for tenant ${tenant2}`
|
|
636
637
|
});
|
|
637
|
-
|
|
638
|
+
if (opts.quiet) return;
|
|
639
|
+
spinner?.succeed(`IAM is ${import_chalk9.default.bold(state)} for tenant ${tenant2}.`);
|
|
638
640
|
if (opts.json) console.log(JSON.stringify(out ?? { iam_enabled: enabled }));
|
|
639
641
|
else if (enabled) {
|
|
640
642
|
console.log(import_chalk9.default.dim(" Groups now apply to identified calls. Identify callers with"));
|
|
@@ -642,7 +644,7 @@ async function runIamToggle(project, state, opts) {
|
|
|
642
644
|
console.log(import_chalk9.default.dim(` \`apiblaze identified ${project} require\` to reject unattributed traffic.`));
|
|
643
645
|
}
|
|
644
646
|
} catch (err) {
|
|
645
|
-
spinner
|
|
647
|
+
spinner?.fail(`IAM ${state} failed.`);
|
|
646
648
|
throw err;
|
|
647
649
|
}
|
|
648
650
|
}
|
|
@@ -654,7 +656,7 @@ async function runIdentifiedToggle(project, mode, opts) {
|
|
|
654
656
|
const caller = requireCaller();
|
|
655
657
|
const { teamId } = await resolveActingTeam(caller, opts.team);
|
|
656
658
|
const proj2 = await resolveProjectVia(caller, teamId, project, opts.apiversion);
|
|
657
|
-
const spinner = (0, import_ora4.default)(
|
|
659
|
+
const spinner = opts.quiet ? null : (0, import_ora4.default)(
|
|
658
660
|
require2 ? "Requiring identified traffic..." : "Allowing unattributed traffic..."
|
|
659
661
|
).start();
|
|
660
662
|
try {
|
|
@@ -681,12 +683,13 @@ async function runIdentifiedToggle(project, mode, opts) {
|
|
|
681
683
|
body: { requests_policy: merged },
|
|
682
684
|
summary: `Set identified-traffic policy (${mode}) on ${proj2.projectName}`
|
|
683
685
|
});
|
|
684
|
-
|
|
686
|
+
if (opts.quiet) return;
|
|
687
|
+
spinner?.succeed(
|
|
685
688
|
require2 ? `${proj2.projectName} now rejects calls that don't identify an end user.` : `${proj2.projectName} accepts unattributed calls again (they pass with no groups).`
|
|
686
689
|
);
|
|
687
690
|
if (opts.json) console.log(JSON.stringify(out ?? { identified_traffic_only: require2 }));
|
|
688
691
|
} catch (err) {
|
|
689
|
-
spinner
|
|
692
|
+
spinner?.fail("Policy update failed.");
|
|
690
693
|
throw err;
|
|
691
694
|
}
|
|
692
695
|
}
|
|
@@ -736,7 +739,7 @@ async function createTenantRow(teamId) {
|
|
|
736
739
|
}
|
|
737
740
|
async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
738
741
|
const { default: inquirer3 } = await import("inquirer");
|
|
739
|
-
const
|
|
742
|
+
const base2 = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
740
743
|
const { provider } = await inquirer3.prompt([{
|
|
741
744
|
type: "list",
|
|
742
745
|
name: "provider",
|
|
@@ -781,9 +784,9 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
781
784
|
}
|
|
782
785
|
const spinner = (0, import_ora10.default)("Setting up login...").start();
|
|
783
786
|
try {
|
|
784
|
-
const client = await admin({ method: "POST", path: `${
|
|
787
|
+
const client = await admin({ method: "POST", path: `${base2}/app-clients`, body: clientBody, summary: `Create login for ${tenant2}` });
|
|
785
788
|
const clientId = client?.clientId ?? client?.client_id;
|
|
786
|
-
await admin({ method: "POST", path: `${
|
|
789
|
+
await admin({ method: "POST", path: `${base2}/app-clients/${encodeURIComponent(clientId)}/providers`, body: providerBody, summary: `Add ${provider} provider` });
|
|
787
790
|
spinner.succeed(opt.own ? `${opt.label} login is ready.` : "APIblaze-hosted GitHub login is ready.");
|
|
788
791
|
return true;
|
|
789
792
|
} catch (err) {
|
|
@@ -927,10 +930,10 @@ var init_tenant_pick = __esm({
|
|
|
927
930
|
|
|
928
931
|
// src/index.ts
|
|
929
932
|
var import_commander = require("commander");
|
|
930
|
-
var
|
|
933
|
+
var import_chalk44 = __toESM(require("chalk"));
|
|
931
934
|
|
|
932
935
|
// package.json
|
|
933
|
-
var version = "0.19.
|
|
936
|
+
var version = "0.19.2";
|
|
934
937
|
|
|
935
938
|
// src/index.ts
|
|
936
939
|
init_types();
|
|
@@ -1183,7 +1186,7 @@ function decodeJwt(token) {
|
|
|
1183
1186
|
function maskPath(path8) {
|
|
1184
1187
|
const q = path8.indexOf("?");
|
|
1185
1188
|
if (q < 0) return path8;
|
|
1186
|
-
const
|
|
1189
|
+
const base2 = path8.slice(0, q);
|
|
1187
1190
|
const query = path8.slice(q + 1);
|
|
1188
1191
|
const masked = query.split("&").map((pair) => {
|
|
1189
1192
|
const eq = pair.indexOf("=");
|
|
@@ -1195,7 +1198,7 @@ function maskPath(path8) {
|
|
|
1195
1198
|
}
|
|
1196
1199
|
return pair;
|
|
1197
1200
|
}).join("&");
|
|
1198
|
-
return `${
|
|
1201
|
+
return `${base2}?${masked}`;
|
|
1199
1202
|
}
|
|
1200
1203
|
function formatHeaderLines(name, value) {
|
|
1201
1204
|
const lower = name.toLowerCase();
|
|
@@ -2010,6 +2013,7 @@ async function runCreate(opts = {}) {
|
|
|
2010
2013
|
const adminKey = keys.dev ?? Object.values(keys)[0];
|
|
2011
2014
|
const proxyUrl = `https://${name}.abz.run/${version2}/dev`;
|
|
2012
2015
|
const devPortal = result.devPortal ? stripTenantFromPortal(result.devPortal) : void 0;
|
|
2016
|
+
await applyCreateToggles(name, opts);
|
|
2013
2017
|
if (opts.json) {
|
|
2014
2018
|
process.stdout.write(JSON.stringify({
|
|
2015
2019
|
project_id: result.project_id,
|
|
@@ -2037,17 +2041,13 @@ async function runCreate(opts = {}) {
|
|
|
2037
2041
|
}
|
|
2038
2042
|
printCurlExample(proxyUrl, auth, adminKey, devPortal);
|
|
2039
2043
|
console.log();
|
|
2040
|
-
await applyCreateToggles(name, opts);
|
|
2041
2044
|
}
|
|
2042
2045
|
async function applyCreateToggles(name, opts) {
|
|
2043
2046
|
if (!opts.identified && !opts.iam) return;
|
|
2044
2047
|
const { runIamToggle: runIamToggle2, runIdentifiedToggle: runIdentifiedToggle2 } = await Promise.resolve().then(() => (init_iam(), iam_exports));
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
if (opts.iam) {
|
|
2049
|
-
await runIamToggle2(name, "on", { team: opts.team, json: opts.json });
|
|
2050
|
-
}
|
|
2048
|
+
const toggleOpts = { team: opts.team, json: opts.json, quiet: opts.json };
|
|
2049
|
+
if (opts.identified) await runIdentifiedToggle2(name, "require", toggleOpts);
|
|
2050
|
+
if (opts.iam) await runIamToggle2(name, "on", toggleOpts);
|
|
2051
2051
|
}
|
|
2052
2052
|
async function runAnonymousCreate(opts) {
|
|
2053
2053
|
const interactive = !!process.stdin.isTTY && !opts.json;
|
|
@@ -2149,6 +2149,7 @@ async function runAnonymousCreate(opts) {
|
|
|
2149
2149
|
const keys = result.api_keys ?? {};
|
|
2150
2150
|
const apiKey = result.apiKey ?? keys.prod ?? Object.values(keys)[0];
|
|
2151
2151
|
const prodEndpoint = (result.endpoints || []).find((e) => e.endsWith("/prod")) || (result.endpoints || [])[0];
|
|
2152
|
+
if (name) await applyCreateToggles(name, opts);
|
|
2152
2153
|
if (opts.json) {
|
|
2153
2154
|
process.stdout.write(JSON.stringify({
|
|
2154
2155
|
project_id: result.project_id,
|
|
@@ -2182,7 +2183,6 @@ async function runAnonymousCreate(opts) {
|
|
|
2182
2183
|
console.log(` ${import_chalk10.default.bold(result.claim_url)}`);
|
|
2183
2184
|
}
|
|
2184
2185
|
console.log();
|
|
2185
|
-
if (name) await applyCreateToggles(name, opts);
|
|
2186
2186
|
}
|
|
2187
2187
|
|
|
2188
2188
|
// src/commands/claim.ts
|
|
@@ -3422,19 +3422,19 @@ async function validScopedTenant(teamId, query) {
|
|
|
3422
3422
|
}
|
|
3423
3423
|
async function tenantHome(teamId, tenant2) {
|
|
3424
3424
|
const { default: inquirer3 } = await import("inquirer");
|
|
3425
|
-
const
|
|
3425
|
+
const base2 = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
3426
3426
|
console.log(import_chalk29.default.bold(`
|
|
3427
3427
|
Tenant ${tenant2}`));
|
|
3428
3428
|
console.log(import_chalk29.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
3429
3429
|
for (; ; ) {
|
|
3430
3430
|
const spinner = (0, import_ora13.default)("Reading tenant state...").start();
|
|
3431
3431
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
3432
|
-
admin({ method: "GET", path: `${
|
|
3433
|
-
admin({ method: "GET", path: `${
|
|
3434
|
-
admin({ method: "GET", path: `${
|
|
3435
|
-
admin({ method: "GET", path: `${
|
|
3436
|
-
admin({ method: "GET", path: `${
|
|
3437
|
-
admin({ method: "GET", path: `${
|
|
3432
|
+
admin({ method: "GET", path: `${base2}/iam`, summary: "Read IAM toggle" }).catch(() => null),
|
|
3433
|
+
admin({ method: "GET", path: `${base2}/cors`, summary: "Read tenant CORS" }).catch(() => null),
|
|
3434
|
+
admin({ method: "GET", path: `${base2}/admin-emails`, summary: "List consumer-admin emails" }).catch(() => null),
|
|
3435
|
+
admin({ method: "GET", path: `${base2}/external-issuers`, summary: "List external issuers" }).catch(() => null),
|
|
3436
|
+
admin({ method: "GET", path: `${base2}/opaque`, summary: "Read opaque validator" }).catch(() => null),
|
|
3437
|
+
admin({ method: "GET", path: `${base2}/app-clients`, summary: "List app clients" }).catch(() => [])
|
|
3438
3438
|
]).finally(() => spinner.stop());
|
|
3439
3439
|
const nEmails = (emails?.admin_emails ?? []).length;
|
|
3440
3440
|
const nIssuers = (issuers?.external_issuers ?? []).length;
|
|
@@ -3460,11 +3460,11 @@ Tenant ${tenant2}`));
|
|
|
3460
3460
|
case "back":
|
|
3461
3461
|
return;
|
|
3462
3462
|
case "login":
|
|
3463
|
-
await loginMethodsMenu(teamId, tenant2,
|
|
3463
|
+
await loginMethodsMenu(teamId, tenant2, base2);
|
|
3464
3464
|
break;
|
|
3465
3465
|
case "iam": {
|
|
3466
3466
|
const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
|
|
3467
|
-
await admin({ method: "PATCH", path: `${
|
|
3467
|
+
await admin({ method: "PATCH", path: `${base2}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
3468
3468
|
console.log(import_chalk29.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
3469
3469
|
break;
|
|
3470
3470
|
}
|
|
@@ -3481,24 +3481,24 @@ Tenant ${tenant2}`));
|
|
|
3481
3481
|
console.log(import_chalk29.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
3482
3482
|
break;
|
|
3483
3483
|
}
|
|
3484
|
-
await admin({ method: "PUT", path: `${
|
|
3484
|
+
await admin({ method: "PUT", path: `${base2}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
3485
3485
|
console.log(import_chalk29.default.green(" CORS updated."));
|
|
3486
3486
|
break;
|
|
3487
3487
|
}
|
|
3488
3488
|
case "emails":
|
|
3489
|
-
await emailsMenu(
|
|
3489
|
+
await emailsMenu(base2, emails?.admin_emails ?? []);
|
|
3490
3490
|
break;
|
|
3491
3491
|
}
|
|
3492
3492
|
}
|
|
3493
3493
|
}
|
|
3494
|
-
async function loginMethodsMenu(teamId, tenant2,
|
|
3494
|
+
async function loginMethodsMenu(teamId, tenant2, base2) {
|
|
3495
3495
|
const { default: inquirer3 } = await import("inquirer");
|
|
3496
3496
|
for (; ; ) {
|
|
3497
3497
|
const spinner = (0, import_ora13.default)("Loading login methods...").start();
|
|
3498
3498
|
const [rawClients, rawIssuers, rawOpaque] = await Promise.all([
|
|
3499
|
-
admin({ method: "GET", path: `${
|
|
3500
|
-
admin({ method: "GET", path: `${
|
|
3501
|
-
admin({ method: "GET", path: `${
|
|
3499
|
+
admin({ method: "GET", path: `${base2}/app-clients`, summary: "List APIblaze-hosted logins" }).catch(() => []),
|
|
3500
|
+
admin({ method: "GET", path: `${base2}/external-issuers`, summary: "List your-own-JWT logins" }).catch(() => null),
|
|
3501
|
+
admin({ method: "GET", path: `${base2}/opaque`, summary: "Read opaque login" }).catch(() => null)
|
|
3502
3502
|
]).finally(() => spinner.stop());
|
|
3503
3503
|
const appClients = Array.isArray(rawClients) ? rawClients : [];
|
|
3504
3504
|
const issuers = rawIssuers?.external_issuers ?? [];
|
|
@@ -3529,24 +3529,24 @@ async function loginMethodsMenu(teamId, tenant2, base) {
|
|
|
3529
3529
|
}]);
|
|
3530
3530
|
if (pick2.kind === "back") return;
|
|
3531
3531
|
if (pick2.kind === "add") {
|
|
3532
|
-
await addLoginMethod(teamId, tenant2,
|
|
3532
|
+
await addLoginMethod(teamId, tenant2, base2);
|
|
3533
3533
|
continue;
|
|
3534
3534
|
}
|
|
3535
3535
|
if (pick2.kind === "client") {
|
|
3536
|
-
await clientHome(
|
|
3536
|
+
await clientHome(base2, pick2.item);
|
|
3537
3537
|
continue;
|
|
3538
3538
|
}
|
|
3539
3539
|
if (pick2.kind === "issuer") {
|
|
3540
|
-
await issuerHome(
|
|
3540
|
+
await issuerHome(base2, pick2.item);
|
|
3541
3541
|
continue;
|
|
3542
3542
|
}
|
|
3543
3543
|
if (pick2.kind === "opaque") {
|
|
3544
|
-
await opaqueHome(
|
|
3544
|
+
await opaqueHome(base2, pick2.item);
|
|
3545
3545
|
continue;
|
|
3546
3546
|
}
|
|
3547
3547
|
}
|
|
3548
3548
|
}
|
|
3549
|
-
async function addLoginMethod(teamId, tenant2,
|
|
3549
|
+
async function addLoginMethod(teamId, tenant2, base2) {
|
|
3550
3550
|
const { default: inquirer3 } = await import("inquirer");
|
|
3551
3551
|
const { kind } = await inquirer3.prompt([{
|
|
3552
3552
|
type: "list",
|
|
@@ -3565,9 +3565,9 @@ async function addLoginMethod(teamId, tenant2, base) {
|
|
|
3565
3565
|
const { addApiblazeHostedLogin: addApiblazeHostedLogin2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
3566
3566
|
await addApiblazeHostedLogin2(teamId, tenant2);
|
|
3567
3567
|
} else if (kind === "jwt") {
|
|
3568
|
-
await addIssuer(
|
|
3568
|
+
await addIssuer(base2);
|
|
3569
3569
|
} else {
|
|
3570
|
-
await setOpaque(
|
|
3570
|
+
await setOpaque(base2, null);
|
|
3571
3571
|
}
|
|
3572
3572
|
}
|
|
3573
3573
|
function safeJson(s) {
|
|
@@ -3577,7 +3577,7 @@ function safeJson(s) {
|
|
|
3577
3577
|
return void 0;
|
|
3578
3578
|
}
|
|
3579
3579
|
}
|
|
3580
|
-
async function emailsMenu(
|
|
3580
|
+
async function emailsMenu(base2, emails) {
|
|
3581
3581
|
const { default: inquirer3 } = await import("inquirer");
|
|
3582
3582
|
console.log();
|
|
3583
3583
|
if (!emails.length) console.log(import_chalk29.default.dim(" No consumer-admin emails."));
|
|
@@ -3595,7 +3595,7 @@ async function emailsMenu(base, emails) {
|
|
|
3595
3595
|
if (act === "back") return;
|
|
3596
3596
|
if (act === "add") {
|
|
3597
3597
|
const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
3598
|
-
await admin({ method: "POST", path: `${
|
|
3598
|
+
await admin({ method: "POST", path: `${base2}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
3599
3599
|
console.log(import_chalk29.default.green(` ${email} added.`));
|
|
3600
3600
|
} else {
|
|
3601
3601
|
const { e } = await inquirer3.prompt([{
|
|
@@ -3605,11 +3605,11 @@ async function emailsMenu(base, emails) {
|
|
|
3605
3605
|
choices: [...emails.map((x) => ({ name: x.email ?? String(x), value: x.email ?? String(x) })), { name: "\u2190 Back", value: null }]
|
|
3606
3606
|
}]);
|
|
3607
3607
|
if (!e) return;
|
|
3608
|
-
await admin({ method: "DELETE", path: `${
|
|
3608
|
+
await admin({ method: "DELETE", path: `${base2}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3609
3609
|
console.log(import_chalk29.default.green(` ${e} removed.`));
|
|
3610
3610
|
}
|
|
3611
3611
|
}
|
|
3612
|
-
async function addIssuer(
|
|
3612
|
+
async function addIssuer(base2) {
|
|
3613
3613
|
const { default: inquirer3 } = await import("inquirer");
|
|
3614
3614
|
const a = await inquirer3.prompt([
|
|
3615
3615
|
{ type: "input", name: "iss", message: "Issuer URL (iss):", validate: (s) => !!s.trim() || "required" },
|
|
@@ -3623,13 +3623,13 @@ async function addIssuer(base) {
|
|
|
3623
3623
|
const claim = a.sem === "extract_from_claim" ? (await inquirer3.prompt([{ type: "input", name: "c", message: "Claim name:", validate: (s) => !!s.trim() || "required" }])).c : void 0;
|
|
3624
3624
|
await admin({
|
|
3625
3625
|
method: "POST",
|
|
3626
|
-
path: `${
|
|
3626
|
+
path: `${base2}/external-issuers`,
|
|
3627
3627
|
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3628
3628
|
summary: `Add external issuer ${a.iss.trim()}`
|
|
3629
3629
|
});
|
|
3630
3630
|
console.log(import_chalk29.default.green(" JWT login method saved."));
|
|
3631
3631
|
}
|
|
3632
|
-
async function issuerHome(
|
|
3632
|
+
async function issuerHome(base2, issuer) {
|
|
3633
3633
|
const { default: inquirer3 } = await import("inquirer");
|
|
3634
3634
|
console.log(`
|
|
3635
3635
|
${import_chalk29.default.bold(issuer.iss)} ${import_chalk29.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
|
|
@@ -3645,28 +3645,28 @@ async function issuerHome(base, issuer) {
|
|
|
3645
3645
|
}]);
|
|
3646
3646
|
if (act === "back") return;
|
|
3647
3647
|
if (act === "edit") {
|
|
3648
|
-
await addIssuer(
|
|
3648
|
+
await addIssuer(base2);
|
|
3649
3649
|
return;
|
|
3650
3650
|
}
|
|
3651
3651
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Delete the JWT login method for ${issuer.iss}? Consumers using it can no longer sign in.`, default: false }]);
|
|
3652
3652
|
if (!sure) return;
|
|
3653
3653
|
await admin({
|
|
3654
3654
|
method: "DELETE",
|
|
3655
|
-
path: `${
|
|
3655
|
+
path: `${base2}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
|
|
3656
3656
|
summary: `Delete issuer ${issuer.iss}`
|
|
3657
3657
|
});
|
|
3658
3658
|
console.log(import_chalk29.default.green(" Deleted."));
|
|
3659
3659
|
}
|
|
3660
|
-
async function setOpaque(
|
|
3660
|
+
async function setOpaque(base2, cur) {
|
|
3661
3661
|
const { default: inquirer3 } = await import("inquirer");
|
|
3662
3662
|
const a = await inquirer3.prompt([
|
|
3663
3663
|
{ type: "input", name: "endpoint", message: "Introspection endpoint (https):", default: cur?.endpoint, validate: (s) => s.startsWith("https://") || "must be https" },
|
|
3664
3664
|
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3665
3665
|
]);
|
|
3666
|
-
await admin({ method: "PUT", path: `${
|
|
3666
|
+
await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3667
3667
|
console.log(import_chalk29.default.green(" Opaque login method set."));
|
|
3668
3668
|
}
|
|
3669
|
-
async function opaqueHome(
|
|
3669
|
+
async function opaqueHome(base2, cur) {
|
|
3670
3670
|
const { default: inquirer3 } = await import("inquirer");
|
|
3671
3671
|
console.log(`
|
|
3672
3672
|
${import_chalk29.default.bold(cur.endpoint)} ${import_chalk29.default.dim(cur.method ?? "GET")}`);
|
|
@@ -3682,16 +3682,16 @@ async function opaqueHome(base, cur) {
|
|
|
3682
3682
|
}]);
|
|
3683
3683
|
if (act === "back") return;
|
|
3684
3684
|
if (act === "edit") {
|
|
3685
|
-
await setOpaque(
|
|
3685
|
+
await setOpaque(base2, cur);
|
|
3686
3686
|
return;
|
|
3687
3687
|
}
|
|
3688
|
-
await admin({ method: "PUT", path: `${
|
|
3688
|
+
await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3689
3689
|
console.log(import_chalk29.default.green(" Deleted."));
|
|
3690
3690
|
}
|
|
3691
|
-
async function clientHome(
|
|
3691
|
+
async function clientHome(base2, summary) {
|
|
3692
3692
|
const { default: inquirer3 } = await import("inquirer");
|
|
3693
3693
|
const id = summary.clientId ?? summary.client_id;
|
|
3694
|
-
const cBase = `${
|
|
3694
|
+
const cBase = `${base2}/app-clients/${encodeURIComponent(id)}`;
|
|
3695
3695
|
for (; ; ) {
|
|
3696
3696
|
const spinner = (0, import_ora13.default)("Reading app client...").start();
|
|
3697
3697
|
const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
|
|
@@ -4839,9 +4839,9 @@ function showCondition(cond) {
|
|
|
4839
4839
|
}
|
|
4840
4840
|
async function transformsMenu(proj2) {
|
|
4841
4841
|
const { default: inquirer3 } = await import("inquirer");
|
|
4842
|
-
const
|
|
4842
|
+
const base2 = `/projects/${proj2.projectId}/${proj2.apiVersion}/transforms`;
|
|
4843
4843
|
for (; ; ) {
|
|
4844
|
-
const out = await admin({ method: "GET", path:
|
|
4844
|
+
const out = await admin({ method: "GET", path: base2, summary: "List transform rules" });
|
|
4845
4845
|
const rules = out?.rules ?? [];
|
|
4846
4846
|
console.log();
|
|
4847
4847
|
if (!rules.length) console.log(import_chalk33.default.dim(" No transform rules yet."));
|
|
@@ -4877,7 +4877,7 @@ async function transformsMenu(proj2) {
|
|
|
4877
4877
|
console.log(import_chalk33.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
4878
4878
|
continue;
|
|
4879
4879
|
}
|
|
4880
|
-
await admin({ method: "POST", path:
|
|
4880
|
+
await admin({ method: "POST", path: base2, body, summary: "Create transform rule (raw JSON)" });
|
|
4881
4881
|
console.log(import_chalk33.default.green(" Rule created."));
|
|
4882
4882
|
continue;
|
|
4883
4883
|
}
|
|
@@ -4927,7 +4927,7 @@ async function transformsMenu(proj2) {
|
|
|
4927
4927
|
try {
|
|
4928
4928
|
await admin({
|
|
4929
4929
|
method: "POST",
|
|
4930
|
-
path:
|
|
4930
|
+
path: base2,
|
|
4931
4931
|
body: { name: ans.name, phase: ans.phase, enabled: true, action: action2, ...condition ? { condition } : {} },
|
|
4932
4932
|
summary: `Create transform "${ans.name}"`
|
|
4933
4933
|
});
|
|
@@ -4946,10 +4946,10 @@ async function transformsMenu(proj2) {
|
|
|
4946
4946
|
if (!rule) continue;
|
|
4947
4947
|
if (act === "toggle") {
|
|
4948
4948
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
4949
|
-
await admin({ method: "PUT", path: `${
|
|
4949
|
+
await admin({ method: "PUT", path: `${base2}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
4950
4950
|
console.log(import_chalk33.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
4951
4951
|
} else {
|
|
4952
|
-
await admin({ method: "DELETE", path: `${
|
|
4952
|
+
await admin({ method: "DELETE", path: `${base2}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
4953
4953
|
console.log(import_chalk33.default.green(` ${rule.name} deleted.`));
|
|
4954
4954
|
}
|
|
4955
4955
|
}
|
|
@@ -4957,9 +4957,9 @@ async function transformsMenu(proj2) {
|
|
|
4957
4957
|
}
|
|
4958
4958
|
async function mappingsMenu(proj2) {
|
|
4959
4959
|
const { default: inquirer3 } = await import("inquirer");
|
|
4960
|
-
const
|
|
4960
|
+
const base2 = `/projects/${proj2.projectId}/${proj2.apiVersion}/mappings`;
|
|
4961
4961
|
for (; ; ) {
|
|
4962
|
-
const out = await admin({ method: "GET", path:
|
|
4962
|
+
const out = await admin({ method: "GET", path: base2, summary: "List mapping tables" });
|
|
4963
4963
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
4964
4964
|
console.log();
|
|
4965
4965
|
if (!tables.length) console.log(import_chalk33.default.dim(" No mapping tables yet."));
|
|
@@ -4987,7 +4987,7 @@ async function mappingsMenu(proj2) {
|
|
|
4987
4987
|
console.log(import_chalk33.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
4988
4988
|
continue;
|
|
4989
4989
|
}
|
|
4990
|
-
await admin({ method: "POST", path:
|
|
4990
|
+
await admin({ method: "POST", path: base2, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
4991
4991
|
console.log(import_chalk33.default.green(` Table "${a.name}" created.`));
|
|
4992
4992
|
} else {
|
|
4993
4993
|
const { table } = await inquirer3.prompt([{
|
|
@@ -4997,16 +4997,16 @@ async function mappingsMenu(proj2) {
|
|
|
4997
4997
|
choices: [...tables.map((t) => ({ name: t.name, value: t })), { name: "\u2190 Back", value: null }]
|
|
4998
4998
|
}]);
|
|
4999
4999
|
if (!table) continue;
|
|
5000
|
-
await admin({ method: "DELETE", path: `${
|
|
5000
|
+
await admin({ method: "DELETE", path: `${base2}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
5001
5001
|
console.log(import_chalk33.default.green(` ${table.name} deleted.`));
|
|
5002
5002
|
}
|
|
5003
5003
|
}
|
|
5004
5004
|
}
|
|
5005
5005
|
async function tenantsMenu(proj2, opts) {
|
|
5006
5006
|
const { default: inquirer3 } = await import("inquirer");
|
|
5007
|
-
const
|
|
5007
|
+
const base2 = `/projects/${proj2.projectId}/${proj2.apiVersion}/tenants`;
|
|
5008
5008
|
for (; ; ) {
|
|
5009
|
-
const out = await admin({ method: "GET", path:
|
|
5009
|
+
const out = await admin({ method: "GET", path: base2, summary: "List attached tenants" });
|
|
5010
5010
|
const tenants = out?.tenants ?? [];
|
|
5011
5011
|
console.log();
|
|
5012
5012
|
if (!tenants.length) console.log(import_chalk33.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
@@ -5038,7 +5038,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
5038
5038
|
choices: [...tenants.map((x) => ({ name: x.tenant_name ?? x.name, value: x })), { name: "\u2190 Back", value: null }]
|
|
5039
5039
|
}]);
|
|
5040
5040
|
if (!t) continue;
|
|
5041
|
-
await admin({ method: "DELETE", path: `${
|
|
5041
|
+
await admin({ method: "DELETE", path: `${base2}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
5042
5042
|
console.log(import_chalk33.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
5043
5043
|
}
|
|
5044
5044
|
}
|
|
@@ -5372,7 +5372,6 @@ async function runKeyList(opts) {
|
|
|
5372
5372
|
}
|
|
5373
5373
|
}
|
|
5374
5374
|
async function runKeyMint(opts) {
|
|
5375
|
-
if (process.env.ABZ_DEBUG_OPTS) console.error("DEBUG opts:", JSON.stringify(opts));
|
|
5376
5375
|
const caller = requireCaller();
|
|
5377
5376
|
const { teamId } = await resolveActingTeam(caller, opts.team);
|
|
5378
5377
|
const body = { role: "consumer-admin" };
|
|
@@ -5419,9 +5418,85 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
5419
5418
|
// src/index.ts
|
|
5420
5419
|
init_iam();
|
|
5421
5420
|
|
|
5422
|
-
// src/commands/
|
|
5421
|
+
// src/commands/admins.ts
|
|
5423
5422
|
var import_chalk36 = __toESM(require("chalk"));
|
|
5424
5423
|
var import_ora19 = __toESM(require("ora"));
|
|
5424
|
+
init_caller();
|
|
5425
|
+
function requireTenant(opts) {
|
|
5426
|
+
const t = (opts.tenant ?? "").trim();
|
|
5427
|
+
if (!t) {
|
|
5428
|
+
throw new Error("--tenant <slug> is required (e.g. --tenant nino).");
|
|
5429
|
+
}
|
|
5430
|
+
return t;
|
|
5431
|
+
}
|
|
5432
|
+
function base(teamId, tenant2) {
|
|
5433
|
+
return `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/admin-emails`;
|
|
5434
|
+
}
|
|
5435
|
+
async function runAdminsList(opts) {
|
|
5436
|
+
const caller = requireCaller();
|
|
5437
|
+
const { teamId } = await resolveActingTeam(caller, opts.team);
|
|
5438
|
+
const tenant2 = requireTenant(opts);
|
|
5439
|
+
const out = await producer(caller, {
|
|
5440
|
+
method: "GET",
|
|
5441
|
+
path: base(teamId, tenant2),
|
|
5442
|
+
summary: `List admins for tenant ${tenant2}`
|
|
5443
|
+
});
|
|
5444
|
+
const admins2 = out?.admin_emails ?? out?.admins ?? [];
|
|
5445
|
+
if (opts.json) {
|
|
5446
|
+
console.log(JSON.stringify(admins2));
|
|
5447
|
+
return;
|
|
5448
|
+
}
|
|
5449
|
+
if (!admins2.length) {
|
|
5450
|
+
console.log(import_chalk36.default.yellow(`No admins for tenant ${tenant2} yet.`));
|
|
5451
|
+
return;
|
|
5452
|
+
}
|
|
5453
|
+
for (const a of admins2) {
|
|
5454
|
+
const email = typeof a === "string" ? a : a.email;
|
|
5455
|
+
const flags = [a.active === false ? "pending" : null, a.pinned ? "pinned" : null].filter(Boolean).join(", ");
|
|
5456
|
+
console.log(` ${import_chalk36.default.bold(email)}${flags ? import_chalk36.default.dim(` (${flags})`) : ""}`);
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
async function runAdminsAdd(email, opts) {
|
|
5460
|
+
const caller = requireCaller();
|
|
5461
|
+
const { teamId } = await resolveActingTeam(caller, opts.team);
|
|
5462
|
+
const tenant2 = requireTenant(opts);
|
|
5463
|
+
const spinner = opts.json ? null : (0, import_ora19.default)(`Adding ${email} as admin of ${tenant2}...`).start();
|
|
5464
|
+
try {
|
|
5465
|
+
const out = await producer(caller, {
|
|
5466
|
+
method: "POST",
|
|
5467
|
+
path: base(teamId, tenant2),
|
|
5468
|
+
body: { email },
|
|
5469
|
+
summary: `Add ${email} to tenant ${tenant2} admins`
|
|
5470
|
+
});
|
|
5471
|
+
spinner?.succeed(`${import_chalk36.default.bold(email)} is now an admin of ${import_chalk36.default.bold(tenant2)}.`);
|
|
5472
|
+
if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
|
|
5473
|
+
else console.log(import_chalk36.default.dim(" Reload the Users & Groups widget \u2014 access flips from \u201Cpending\u201D to ready."));
|
|
5474
|
+
} catch (err) {
|
|
5475
|
+
spinner?.fail("Add failed.");
|
|
5476
|
+
throw err;
|
|
5477
|
+
}
|
|
5478
|
+
}
|
|
5479
|
+
async function runAdminsRemove(email, opts) {
|
|
5480
|
+
const caller = requireCaller();
|
|
5481
|
+
const { teamId } = await resolveActingTeam(caller, opts.team);
|
|
5482
|
+
const tenant2 = requireTenant(opts);
|
|
5483
|
+
const spinner = opts.json ? null : (0, import_ora19.default)(`Removing ${email} from ${tenant2} admins...`).start();
|
|
5484
|
+
try {
|
|
5485
|
+
await producer(caller, {
|
|
5486
|
+
method: "DELETE",
|
|
5487
|
+
path: `${base(teamId, tenant2)}/${encodeURIComponent(email)}`,
|
|
5488
|
+
summary: `Remove ${email} from tenant ${tenant2} admins`
|
|
5489
|
+
});
|
|
5490
|
+
spinner?.succeed(`Removed ${import_chalk36.default.bold(email)} from ${import_chalk36.default.bold(tenant2)} admins.`);
|
|
5491
|
+
} catch (err) {
|
|
5492
|
+
spinner?.fail("Remove failed.");
|
|
5493
|
+
throw err;
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
|
|
5497
|
+
// src/commands/preapprove.ts
|
|
5498
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
5499
|
+
var import_ora20 = __toESM(require("ora"));
|
|
5425
5500
|
|
|
5426
5501
|
// src/lib/preapproval.ts
|
|
5427
5502
|
init_auth();
|
|
@@ -5485,14 +5560,14 @@ async function runPreapprove(who, opts) {
|
|
|
5485
5560
|
const rules = await listPreapprovalRules(tenant2);
|
|
5486
5561
|
if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
|
|
5487
5562
|
if (!rules.length) {
|
|
5488
|
-
console.log(
|
|
5489
|
-
console.log(
|
|
5563
|
+
console.log(import_chalk37.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
|
|
5564
|
+
console.log(import_chalk37.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
|
|
5490
5565
|
return;
|
|
5491
5566
|
}
|
|
5492
|
-
console.log(
|
|
5567
|
+
console.log(import_chalk37.default.dim(`Pre-approved for ${import_chalk37.default.bold(tenant2)}:`));
|
|
5493
5568
|
for (const r of rules) {
|
|
5494
|
-
const tag = r.kind === "domain" ?
|
|
5495
|
-
const grp = r.groups?.length ?
|
|
5569
|
+
const tag = r.kind === "domain" ? import_chalk37.default.cyan("@" + r.value + " (whole domain)") : r.value;
|
|
5570
|
+
const grp = r.groups?.length ? import_chalk37.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
|
|
5496
5571
|
console.log(` ${tag}${grp}`);
|
|
5497
5572
|
}
|
|
5498
5573
|
return;
|
|
@@ -5501,19 +5576,19 @@ async function runPreapprove(who, opts) {
|
|
|
5501
5576
|
throw new Error("Who? Pass an email or a domain: `apiblaze preapprove someone@acme.com` (or `apiblaze preapprove --list`).");
|
|
5502
5577
|
}
|
|
5503
5578
|
if (opts.remove) {
|
|
5504
|
-
const spinner2 = (0,
|
|
5579
|
+
const spinner2 = (0, import_ora20.default)(`Removing ${who} from ${tenant2}\u2026`).start();
|
|
5505
5580
|
const { removed, value } = await removePreapprovalRule(tenant2, who);
|
|
5506
|
-
if (removed) spinner2.succeed(`${
|
|
5507
|
-
else spinner2.warn(`No pre-approval rule for ${
|
|
5581
|
+
if (removed) spinner2.succeed(`${import_chalk37.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
|
|
5582
|
+
else spinner2.warn(`No pre-approval rule for ${import_chalk37.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
|
|
5508
5583
|
return;
|
|
5509
5584
|
}
|
|
5510
|
-
const spinner = (0,
|
|
5585
|
+
const spinner = (0, import_ora20.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
|
|
5511
5586
|
try {
|
|
5512
5587
|
const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
|
|
5513
5588
|
const what = kind === "domain" ? `Anyone @${value}` : value;
|
|
5514
|
-
spinner.succeed(`${
|
|
5515
|
-
if (opts.group?.length) console.log(
|
|
5516
|
-
console.log(
|
|
5589
|
+
spinner.succeed(`${import_chalk37.default.bold(what)} can now sign in to ${import_chalk37.default.bold(tenant2)}.`);
|
|
5590
|
+
if (opts.group?.length) console.log(import_chalk37.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
|
|
5591
|
+
console.log(import_chalk37.default.dim(` See the full list: apiblaze preapprove --list`));
|
|
5517
5592
|
} catch (err) {
|
|
5518
5593
|
spinner.fail("Could not add the rule.");
|
|
5519
5594
|
throw err;
|
|
@@ -5524,8 +5599,8 @@ async function runPreapprove(who, opts) {
|
|
|
5524
5599
|
var fs9 = __toESM(require("fs"));
|
|
5525
5600
|
var path6 = __toESM(require("path"));
|
|
5526
5601
|
var crypto2 = __toESM(require("crypto"));
|
|
5527
|
-
var
|
|
5528
|
-
var
|
|
5602
|
+
var import_chalk39 = __toESM(require("chalk"));
|
|
5603
|
+
var import_ora21 = __toESM(require("ora"));
|
|
5529
5604
|
var import_yaml = require("yaml");
|
|
5530
5605
|
init_auth();
|
|
5531
5606
|
init_anon_cred();
|
|
@@ -5535,7 +5610,7 @@ init_admin();
|
|
|
5535
5610
|
// src/commands/llm.ts
|
|
5536
5611
|
var fs8 = __toESM(require("fs"));
|
|
5537
5612
|
var path5 = __toESM(require("path"));
|
|
5538
|
-
var
|
|
5613
|
+
var import_chalk38 = __toESM(require("chalk"));
|
|
5539
5614
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
5540
5615
|
init_auth();
|
|
5541
5616
|
var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
|
|
@@ -5579,27 +5654,27 @@ async function runLlmSetKey(keyArg, opts) {
|
|
|
5579
5654
|
}
|
|
5580
5655
|
const existing = loadLlmConfig();
|
|
5581
5656
|
saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
|
|
5582
|
-
console.log(`${
|
|
5583
|
-
console.log(
|
|
5584
|
-
if (opts.model) console.log(
|
|
5657
|
+
console.log(`${import_chalk38.default.green("\u2713")} Saved ${import_chalk38.default.bold(provider)} key ${maskSecret(key)}`);
|
|
5658
|
+
console.log(import_chalk38.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
|
|
5659
|
+
if (opts.model) console.log(import_chalk38.default.gray(` Model: ${opts.model}`));
|
|
5585
5660
|
}
|
|
5586
5661
|
async function runLlmShow() {
|
|
5587
5662
|
const cfg = loadLlmConfig();
|
|
5588
5663
|
if (!cfg) {
|
|
5589
|
-
console.log(
|
|
5664
|
+
console.log(import_chalk38.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
|
|
5590
5665
|
return;
|
|
5591
5666
|
}
|
|
5592
|
-
console.log(`Provider: ${
|
|
5667
|
+
console.log(`Provider: ${import_chalk38.default.bold(cfg.provider)}`);
|
|
5593
5668
|
console.log(`Key: ${maskSecret(cfg.key)}`);
|
|
5594
5669
|
if (cfg.model) console.log(`Model: ${cfg.model}`);
|
|
5595
|
-
console.log(
|
|
5670
|
+
console.log(import_chalk38.default.gray(`Stored at ${LLM_PATH}`));
|
|
5596
5671
|
}
|
|
5597
5672
|
async function runLlmClearKey() {
|
|
5598
5673
|
try {
|
|
5599
5674
|
fs8.unlinkSync(LLM_PATH);
|
|
5600
|
-
console.log(`${
|
|
5675
|
+
console.log(`${import_chalk38.default.green("\u2713")} Removed local LLM key.`);
|
|
5601
5676
|
} catch {
|
|
5602
|
-
console.log(
|
|
5677
|
+
console.log(import_chalk38.default.gray("No LLM key was set."));
|
|
5603
5678
|
}
|
|
5604
5679
|
}
|
|
5605
5680
|
|
|
@@ -5607,9 +5682,9 @@ async function runLlmClearKey() {
|
|
|
5607
5682
|
init_trace();
|
|
5608
5683
|
init_types();
|
|
5609
5684
|
function fail4(message, hint) {
|
|
5610
|
-
console.error(
|
|
5685
|
+
console.error(import_chalk39.default.red(`
|
|
5611
5686
|
Error: ${message}`));
|
|
5612
|
-
if (hint) console.error(
|
|
5687
|
+
if (hint) console.error(import_chalk39.default.dim(hint));
|
|
5613
5688
|
process.exit(1);
|
|
5614
5689
|
}
|
|
5615
5690
|
function normalizeName2(raw) {
|
|
@@ -5651,9 +5726,9 @@ async function fetchText(url) {
|
|
|
5651
5726
|
}
|
|
5652
5727
|
}
|
|
5653
5728
|
async function discoverSpec(target) {
|
|
5654
|
-
const
|
|
5729
|
+
const base2 = target.replace(/\/+$/, "");
|
|
5655
5730
|
for (const suffix of ["/openapi.json", "/openapi.yaml", "/swagger.json"]) {
|
|
5656
|
-
const url =
|
|
5731
|
+
const url = base2 + suffix;
|
|
5657
5732
|
const text = await fetchText(url);
|
|
5658
5733
|
if (text) {
|
|
5659
5734
|
try {
|
|
@@ -5757,7 +5832,7 @@ async function resolveTargetAuth(spec2, opts) {
|
|
|
5757
5832
|
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
5758
5833
|
);
|
|
5759
5834
|
}
|
|
5760
|
-
if (sawOAuth) console.log(
|
|
5835
|
+
if (sawOAuth) console.log(import_chalk39.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
|
|
5761
5836
|
return null;
|
|
5762
5837
|
}
|
|
5763
5838
|
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
@@ -5822,28 +5897,28 @@ async function provision(spec2, target, opts) {
|
|
|
5822
5897
|
const loggedIn = !!loadCredentials();
|
|
5823
5898
|
const anon = !loggedIn;
|
|
5824
5899
|
const salt = () => Math.random().toString(36).slice(2, 6);
|
|
5825
|
-
let
|
|
5826
|
-
if (!
|
|
5900
|
+
let base2 = opts.name ? normalizeName2(opts.name) : "";
|
|
5901
|
+
if (!base2) {
|
|
5827
5902
|
try {
|
|
5828
5903
|
const host = new URL(target).hostname;
|
|
5829
|
-
|
|
5830
|
-
if (
|
|
5904
|
+
base2 = normalizeName2(host.split(".")[0]);
|
|
5905
|
+
if (base2.length < 3) base2 = normalizeName2(host);
|
|
5831
5906
|
} catch {
|
|
5832
5907
|
}
|
|
5833
5908
|
}
|
|
5834
|
-
if (!
|
|
5835
|
-
if (!
|
|
5836
|
-
let name = opts.name ?
|
|
5909
|
+
if (!base2 && spec2.info && typeof spec2.info.title === "string") base2 = normalizeName2(spec2.info.title);
|
|
5910
|
+
if (!base2 || base2.length < 3) base2 = "apichat";
|
|
5911
|
+
let name = opts.name ? base2 : `${base2}${salt()}`;
|
|
5837
5912
|
const access = anon ? "open" : opts.access === "open" ? "open" : "invite";
|
|
5838
5913
|
if (anon && opts.access === "invite") {
|
|
5839
|
-
console.log(
|
|
5914
|
+
console.log(import_chalk39.default.dim(" Note: --access invite needs an account to pre-approve people. Staying open for this anonymous proxy \u2014 run `apiblaze login`, then `apiblaze apichat --access invite`."));
|
|
5840
5915
|
}
|
|
5841
5916
|
const DUAL_AUTH = {
|
|
5842
5917
|
mode: "authenticate",
|
|
5843
5918
|
methods: ["api_key", "jwt"],
|
|
5844
5919
|
...access === "invite" ? { preapproved_users_only: true } : {}
|
|
5845
5920
|
};
|
|
5846
|
-
const spinner = (0,
|
|
5921
|
+
const spinner = (0, import_ora21.default)("Provisioning an api_key proxy...").start();
|
|
5847
5922
|
let result;
|
|
5848
5923
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
5849
5924
|
try {
|
|
@@ -5875,13 +5950,13 @@ async function provision(spec2, target, opts) {
|
|
|
5875
5950
|
if (result.cp_key && result.team_id) saveAnonCred(result.cp_key, result.team_id, result.claim_code);
|
|
5876
5951
|
}
|
|
5877
5952
|
}
|
|
5878
|
-
spinner.succeed(`Proxy provisioned${name !==
|
|
5953
|
+
spinner.succeed(`Proxy provisioned${name !== base2 ? ` as "${name}"` : ""}.`);
|
|
5879
5954
|
break;
|
|
5880
5955
|
} catch (err) {
|
|
5881
5956
|
const status = err instanceof ApiError ? err.status : void 0;
|
|
5882
5957
|
const collision = err instanceof ApiError && (err.status === 409 || /exist|taken|available/i.test(err.message));
|
|
5883
5958
|
if (collision && attempt < 3) {
|
|
5884
|
-
name = `${
|
|
5959
|
+
name = `${base2}${salt()}`;
|
|
5885
5960
|
continue;
|
|
5886
5961
|
}
|
|
5887
5962
|
if (status === 401) {
|
|
@@ -5929,14 +6004,14 @@ async function provision(spec2, target, opts) {
|
|
|
5929
6004
|
try {
|
|
5930
6005
|
await addPreapprovalRule(tenant2, email);
|
|
5931
6006
|
} catch {
|
|
5932
|
-
console.log(
|
|
6007
|
+
console.log(import_chalk39.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
|
|
5933
6008
|
}
|
|
5934
6009
|
}
|
|
5935
6010
|
}
|
|
5936
6011
|
return { projectId, version: version2, environment, dpKey, mcpHost, proxyUrl, anon, access, tenant: tenant2 };
|
|
5937
6012
|
}
|
|
5938
6013
|
async function writeTargetAuth(p, auth, secret) {
|
|
5939
|
-
const spinner = (0,
|
|
6014
|
+
const spinner = (0, import_ora21.default)("Storing target credentials (encrypted)...").start();
|
|
5940
6015
|
try {
|
|
5941
6016
|
await cpPost(
|
|
5942
6017
|
p.anon,
|
|
@@ -5958,7 +6033,7 @@ async function writeTargetAuth(p, auth, secret) {
|
|
|
5958
6033
|
}
|
|
5959
6034
|
}
|
|
5960
6035
|
async function uploadSpec(p, specText, opts) {
|
|
5961
|
-
const spinner = (0,
|
|
6036
|
+
const spinner = (0, import_ora21.default)("Uploading the spec...").start();
|
|
5962
6037
|
let out;
|
|
5963
6038
|
try {
|
|
5964
6039
|
out = await cpPost(
|
|
@@ -5973,7 +6048,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5973
6048
|
throw err;
|
|
5974
6049
|
}
|
|
5975
6050
|
if (out && out.reused === true) {
|
|
5976
|
-
console.log(
|
|
6051
|
+
console.log(import_chalk39.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
|
|
5977
6052
|
} else if (out && out.changed === true && out.previous_spec_hash) {
|
|
5978
6053
|
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
5979
6054
|
if (interactive) {
|
|
@@ -5981,12 +6056,12 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5981
6056
|
const { go } = await inquirer3.prompt([
|
|
5982
6057
|
{ type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
|
|
5983
6058
|
]);
|
|
5984
|
-
if (!go) console.log(
|
|
6059
|
+
if (!go) console.log(import_chalk39.default.dim(" Keeping the existing MCP catalogue."));
|
|
5985
6060
|
}
|
|
5986
6061
|
}
|
|
5987
6062
|
}
|
|
5988
6063
|
async function publishMcp(p, spec2) {
|
|
5989
|
-
const spinner = (0,
|
|
6064
|
+
const spinner = (0, import_ora21.default)("Publishing the MCP catalogue...").start();
|
|
5990
6065
|
try {
|
|
5991
6066
|
const url = `https://${p.mcpHost}/${p.version}/${p.environment}/mcp/generate`;
|
|
5992
6067
|
const res = await fetch(url, {
|
|
@@ -6022,14 +6097,14 @@ var revealAuth = false;
|
|
|
6022
6097
|
function renderToolEvents(events, dpKey) {
|
|
6023
6098
|
for (const e of events ?? []) {
|
|
6024
6099
|
const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
|
|
6025
|
-
const mark = ok ?
|
|
6026
|
-
console.log(` ${mark} ${
|
|
6100
|
+
const mark = ok ? import_chalk39.default.green("\u2713") : import_chalk39.default.red("\u2717");
|
|
6101
|
+
console.log(` ${mark} ${import_chalk39.default.cyan(e.name)} ${import_chalk39.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
|
|
6027
6102
|
if (isVerbose() && e.method && e.url) {
|
|
6028
|
-
console.log(
|
|
6103
|
+
console.log(import_chalk39.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
|
|
6029
6104
|
if (dpKey) {
|
|
6030
6105
|
const keyLine = ` -H 'X-API-Key: ${revealAuth ? dpKey : maskKey(dpKey)}'`;
|
|
6031
|
-
const hint = revealAuth ? "" :
|
|
6032
|
-
console.log(
|
|
6106
|
+
const hint = revealAuth ? "" : import_chalk39.default.yellow(" \u2190 /showauth will reveal this");
|
|
6107
|
+
console.log(import_chalk39.default.dim(keyLine) + hint);
|
|
6033
6108
|
}
|
|
6034
6109
|
}
|
|
6035
6110
|
}
|
|
@@ -6038,9 +6113,9 @@ function billingLine(billing) {
|
|
|
6038
6113
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
6039
6114
|
if (typeof billing.free_turns_remaining === "number") return null;
|
|
6040
6115
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
6041
|
-
let line =
|
|
6116
|
+
let line = import_chalk39.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk39.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
6042
6117
|
if (typeof billing.credits_remaining === "number") {
|
|
6043
|
-
line +=
|
|
6118
|
+
line += import_chalk39.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
6044
6119
|
}
|
|
6045
6120
|
return line;
|
|
6046
6121
|
}
|
|
@@ -6048,21 +6123,21 @@ function freeBudgetWarning(billing, anon) {
|
|
|
6048
6123
|
if (!anon || !billing) return null;
|
|
6049
6124
|
if (typeof billing.free_turns_remaining === "number") {
|
|
6050
6125
|
const left2 = billing.free_turns_remaining;
|
|
6051
|
-
if (left2 <= 0) return
|
|
6052
|
-
return
|
|
6126
|
+
if (left2 <= 0) return import_chalk39.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
|
|
6127
|
+
return import_chalk39.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
|
|
6053
6128
|
}
|
|
6054
6129
|
if (typeof billing.free_remaining_cents !== "number") return null;
|
|
6055
6130
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
6056
6131
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
6057
6132
|
if (left > 8) return null;
|
|
6058
|
-
if (left <= 0) return
|
|
6059
|
-
return
|
|
6133
|
+
if (left <= 0) return import_chalk39.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
6134
|
+
return import_chalk39.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
6060
6135
|
}
|
|
6061
6136
|
function printAssistant(delta) {
|
|
6062
6137
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
6063
6138
|
const m = delta[i];
|
|
6064
6139
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
6065
|
-
console.log("\n" +
|
|
6140
|
+
console.log("\n" + import_chalk39.default.green("assistant \u203A ") + m.content + "\n");
|
|
6066
6141
|
return;
|
|
6067
6142
|
}
|
|
6068
6143
|
}
|
|
@@ -6072,7 +6147,7 @@ async function replTurn(p, messages, userText) {
|
|
|
6072
6147
|
const llm2 = loadLlmConfig();
|
|
6073
6148
|
const turnId = crypto2.randomUUID();
|
|
6074
6149
|
for (let round = 0; round < CLIENT_ROUND_CAP; round++) {
|
|
6075
|
-
const spinner = (0,
|
|
6150
|
+
const spinner = (0, import_ora21.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
|
|
6076
6151
|
const body = {
|
|
6077
6152
|
turn_id: turnId,
|
|
6078
6153
|
messages,
|
|
@@ -6088,7 +6163,7 @@ async function replTurn(p, messages, userText) {
|
|
|
6088
6163
|
});
|
|
6089
6164
|
} catch (err) {
|
|
6090
6165
|
spinner.fail("Network error.");
|
|
6091
|
-
console.log(
|
|
6166
|
+
console.log(import_chalk39.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
6092
6167
|
return;
|
|
6093
6168
|
}
|
|
6094
6169
|
let data = null;
|
|
@@ -6106,12 +6181,12 @@ async function replTurn(p, messages, userText) {
|
|
|
6106
6181
|
return;
|
|
6107
6182
|
}
|
|
6108
6183
|
if (res.status === 401) {
|
|
6109
|
-
console.log(
|
|
6184
|
+
console.log(import_chalk39.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
|
|
6110
6185
|
return;
|
|
6111
6186
|
}
|
|
6112
6187
|
if (!res.ok || !data) {
|
|
6113
6188
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
6114
|
-
console.log(
|
|
6189
|
+
console.log(import_chalk39.default.red(` Chat error: ${err}`));
|
|
6115
6190
|
return;
|
|
6116
6191
|
}
|
|
6117
6192
|
if (Array.isArray(data.delta)) {
|
|
@@ -6125,25 +6200,25 @@ async function replTurn(p, messages, userText) {
|
|
|
6125
6200
|
if (warn) console.log(warn);
|
|
6126
6201
|
if (!data.continue) return;
|
|
6127
6202
|
}
|
|
6128
|
-
console.log(
|
|
6203
|
+
console.log(import_chalk39.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
6129
6204
|
}
|
|
6130
6205
|
function renderUpsell(p, upsell) {
|
|
6131
6206
|
const loggedIn = !!loadCredentials();
|
|
6132
6207
|
if (upsell.reason === "CAPPED" && !loggedIn) {
|
|
6133
|
-
console.log("\n" +
|
|
6134
|
-
console.log(
|
|
6208
|
+
console.log("\n" + import_chalk39.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
|
|
6209
|
+
console.log(import_chalk39.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
|
|
6135
6210
|
console.log();
|
|
6136
6211
|
return;
|
|
6137
6212
|
}
|
|
6138
|
-
console.log("\n" +
|
|
6213
|
+
console.log("\n" + import_chalk39.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
6139
6214
|
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
6140
6215
|
if (!loggedIn) {
|
|
6141
|
-
console.log(
|
|
6216
|
+
console.log(import_chalk39.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
|
|
6142
6217
|
} else {
|
|
6143
|
-
console.log(
|
|
6218
|
+
console.log(import_chalk39.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
|
|
6144
6219
|
}
|
|
6145
6220
|
} else if (upsell.reason === "INFLIGHT") {
|
|
6146
|
-
console.log(
|
|
6221
|
+
console.log(import_chalk39.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
6147
6222
|
}
|
|
6148
6223
|
console.log();
|
|
6149
6224
|
}
|
|
@@ -6212,9 +6287,9 @@ async function openServerProxy(project) {
|
|
|
6212
6287
|
const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
|
|
6213
6288
|
let dpKey = prior?.dpKey;
|
|
6214
6289
|
if (!dpKey) {
|
|
6215
|
-
console.log(
|
|
6290
|
+
console.log(import_chalk39.default.dim(` Minting an API key for tenant ${import_chalk39.default.bold(tenant2)} to query project ${import_chalk39.default.bold(project.projectName)}\u2026`));
|
|
6216
6291
|
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
6217
|
-
console.log(` ${
|
|
6292
|
+
console.log(` ${import_chalk39.default.green("\u2714")} API key: ${import_chalk39.default.dim(maskKey(dpKey))}`);
|
|
6218
6293
|
}
|
|
6219
6294
|
const p = {
|
|
6220
6295
|
projectId: project.projectId,
|
|
@@ -6227,7 +6302,7 @@ async function openServerProxy(project) {
|
|
|
6227
6302
|
// Reusing an owned proxy: logged-in apichat doors default to invite-only.
|
|
6228
6303
|
access: "invite"
|
|
6229
6304
|
};
|
|
6230
|
-
const spinner = (0,
|
|
6305
|
+
const spinner = (0, import_ora21.default)("Preparing the chat\u2026").start();
|
|
6231
6306
|
try {
|
|
6232
6307
|
const raw = await admin({
|
|
6233
6308
|
method: "GET",
|
|
@@ -6239,7 +6314,7 @@ async function openServerProxy(project) {
|
|
|
6239
6314
|
if (spec2 && (spec2.paths || spec2.openapi)) {
|
|
6240
6315
|
await publishMcp(p, spec2);
|
|
6241
6316
|
} else {
|
|
6242
|
-
console.log(
|
|
6317
|
+
console.log(import_chalk39.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
|
|
6243
6318
|
}
|
|
6244
6319
|
} catch (err) {
|
|
6245
6320
|
spinner.fail("Could not open the proxy.");
|
|
@@ -6292,7 +6367,7 @@ async function noArgsMenu(opts) {
|
|
|
6292
6367
|
const me = loadCredentials()?.apiblazeUserId;
|
|
6293
6368
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
6294
6369
|
const choices = saved.map((a) => ({
|
|
6295
|
-
name: `Chat with ${
|
|
6370
|
+
name: `Chat with ${import_chalk39.default.bold(a.name)} ${import_chalk39.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
|
|
6296
6371
|
value: { type: "existing", a }
|
|
6297
6372
|
}));
|
|
6298
6373
|
const creds = loadCredentials();
|
|
@@ -6302,14 +6377,14 @@ async function noArgsMenu(opts) {
|
|
|
6302
6377
|
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
6303
6378
|
for (const pr of proxies) {
|
|
6304
6379
|
choices.push({
|
|
6305
|
-
name: `Chat with ${
|
|
6380
|
+
name: `Chat with ${import_chalk39.default.bold(pr.projectName)} ${import_chalk39.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
6306
6381
|
value: { type: "server", project: pr }
|
|
6307
6382
|
});
|
|
6308
6383
|
}
|
|
6309
6384
|
} catch {
|
|
6310
6385
|
}
|
|
6311
6386
|
}
|
|
6312
|
-
choices.push({ name:
|
|
6387
|
+
choices.push({ name: import_chalk39.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
6313
6388
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
6314
6389
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
6315
6390
|
]);
|
|
@@ -6383,36 +6458,36 @@ async function noArgsMenu(opts) {
|
|
|
6383
6458
|
async function runRepl(p, initialMessages) {
|
|
6384
6459
|
const { default: inquirer3 } = await import("inquirer");
|
|
6385
6460
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
6386
|
-
console.log("\n" +
|
|
6387
|
-
if (messages.length) console.log(
|
|
6461
|
+
console.log("\n" + import_chalk39.default.cyan.bold("Chat with your API") + import_chalk39.default.dim(` \xB7 ${p.mcpHost}`));
|
|
6462
|
+
if (messages.length) console.log(import_chalk39.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
6388
6463
|
const llm2 = loadLlmConfig();
|
|
6389
6464
|
console.log(
|
|
6390
|
-
|
|
6465
|
+
import_chalk39.default.dim(
|
|
6391
6466
|
llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for more free chats \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
|
|
6392
6467
|
)
|
|
6393
6468
|
);
|
|
6394
6469
|
for (; ; ) {
|
|
6395
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
6470
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk39.default.green("you \u203A") }]);
|
|
6396
6471
|
const text = (input ?? "").trim();
|
|
6397
6472
|
if (!text) continue;
|
|
6398
6473
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
6399
6474
|
if (text === "/login") {
|
|
6400
6475
|
try {
|
|
6401
6476
|
await runLogin();
|
|
6402
|
-
console.log(
|
|
6477
|
+
console.log(import_chalk39.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
6403
6478
|
} catch (err) {
|
|
6404
|
-
console.log(
|
|
6479
|
+
console.log(import_chalk39.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6405
6480
|
}
|
|
6406
6481
|
continue;
|
|
6407
6482
|
}
|
|
6408
6483
|
if (text === "/claim") {
|
|
6409
6484
|
const justLoggedIn = !loadCredentials();
|
|
6410
6485
|
if (justLoggedIn) {
|
|
6411
|
-
console.log(
|
|
6486
|
+
console.log(import_chalk39.default.dim(" Logging in to claim your workspace\u2026"));
|
|
6412
6487
|
try {
|
|
6413
6488
|
await runLogin();
|
|
6414
6489
|
} catch (err) {
|
|
6415
|
-
console.log(
|
|
6490
|
+
console.log(import_chalk39.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6416
6491
|
continue;
|
|
6417
6492
|
}
|
|
6418
6493
|
if (!loadCredentials()) continue;
|
|
@@ -6423,30 +6498,30 @@ async function runRepl(p, initialMessages) {
|
|
|
6423
6498
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
6424
6499
|
p.anon = false;
|
|
6425
6500
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
6426
|
-
console.log(
|
|
6501
|
+
console.log(import_chalk39.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
6427
6502
|
}
|
|
6428
6503
|
} catch (err) {
|
|
6429
|
-
console.log(
|
|
6504
|
+
console.log(import_chalk39.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6430
6505
|
}
|
|
6431
6506
|
continue;
|
|
6432
6507
|
}
|
|
6433
6508
|
if (text === "/showauth") {
|
|
6434
6509
|
revealAuth = !revealAuth;
|
|
6435
|
-
console.log(
|
|
6510
|
+
console.log(import_chalk39.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."));
|
|
6436
6511
|
continue;
|
|
6437
6512
|
}
|
|
6438
6513
|
if (text.startsWith("/")) {
|
|
6439
|
-
console.log(
|
|
6514
|
+
console.log(import_chalk39.default.dim(" Commands: /login /claim /showauth /exit"));
|
|
6440
6515
|
continue;
|
|
6441
6516
|
}
|
|
6442
6517
|
await replTurn(p, messages, text);
|
|
6443
6518
|
saveTranscript(p, messages);
|
|
6444
6519
|
}
|
|
6445
|
-
console.log(
|
|
6520
|
+
console.log(import_chalk39.default.dim("\nBye."));
|
|
6446
6521
|
}
|
|
6447
6522
|
async function runApichat(opts) {
|
|
6448
6523
|
setVerbose(opts.verbose !== false);
|
|
6449
|
-
console.log(
|
|
6524
|
+
console.log(import_chalk39.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
6450
6525
|
if (!opts.openapispec && !opts.target) {
|
|
6451
6526
|
if (!process.stdin.isTTY) {
|
|
6452
6527
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -6459,7 +6534,7 @@ async function runApichat(opts) {
|
|
|
6459
6534
|
}
|
|
6460
6535
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
6461
6536
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
6462
|
-
console.log(` ${
|
|
6537
|
+
console.log(` ${import_chalk39.default.dim("Target:")} ${import_chalk39.default.bold(target)}`);
|
|
6463
6538
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
6464
6539
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
6465
6540
|
fail4(
|
|
@@ -6468,7 +6543,7 @@ async function runApichat(opts) {
|
|
|
6468
6543
|
);
|
|
6469
6544
|
}
|
|
6470
6545
|
const p = await provision(spec2, target, opts);
|
|
6471
|
-
console.log(` ${
|
|
6546
|
+
console.log(` ${import_chalk39.default.dim("Proxy: ")} ${import_chalk39.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
6472
6547
|
upsertApichat({
|
|
6473
6548
|
name: p.projectId,
|
|
6474
6549
|
target,
|
|
@@ -6487,31 +6562,31 @@ async function runApichat(opts) {
|
|
|
6487
6562
|
const secret = await captureTargetSecret(auth, opts);
|
|
6488
6563
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
6489
6564
|
} else {
|
|
6490
|
-
console.log(
|
|
6565
|
+
console.log(import_chalk39.default.dim(" Target auth: none required."));
|
|
6491
6566
|
}
|
|
6492
6567
|
const specText = JSON.stringify(spec2);
|
|
6493
6568
|
await uploadSpec(p, specText, opts);
|
|
6494
6569
|
const mcpUrl = await publishMcp(p, spec2);
|
|
6495
6570
|
console.log();
|
|
6496
|
-
if (p.proxyUrl) console.log(` ${
|
|
6571
|
+
if (p.proxyUrl) console.log(` ${import_chalk39.default.green("\u2713")} proxy ${import_chalk39.default.bold(p.proxyUrl)}`);
|
|
6497
6572
|
if (mcpUrl) {
|
|
6498
|
-
console.log(` ${
|
|
6573
|
+
console.log(` ${import_chalk39.default.green("\u2713")} mcp ${import_chalk39.default.bold(mcpUrl)}`);
|
|
6499
6574
|
if (p.access === "invite") {
|
|
6500
|
-
console.log(
|
|
6501
|
-
console.log(
|
|
6575
|
+
console.log(import_chalk39.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
|
|
6576
|
+
console.log(import_chalk39.default.dim(` Let others in: apiblaze preapprove someone@company.com${p.tenant ? ` --tenant ${p.tenant}` : ""} (or re-run with --access open)`));
|
|
6502
6577
|
} else {
|
|
6503
|
-
console.log(
|
|
6578
|
+
console.log(import_chalk39.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
|
|
6504
6579
|
}
|
|
6505
6580
|
}
|
|
6506
6581
|
if (p.anon) {
|
|
6507
|
-
console.log(
|
|
6582
|
+
console.log(import_chalk39.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
|
|
6508
6583
|
}
|
|
6509
6584
|
await runRepl(p);
|
|
6510
6585
|
}
|
|
6511
6586
|
|
|
6512
6587
|
// src/commands/consumer.ts
|
|
6513
|
-
var
|
|
6514
|
-
var
|
|
6588
|
+
var import_chalk40 = __toESM(require("chalk"));
|
|
6589
|
+
var import_ora22 = __toESM(require("ora"));
|
|
6515
6590
|
init_admin();
|
|
6516
6591
|
init_resolve();
|
|
6517
6592
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
@@ -6532,7 +6607,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
6532
6607
|
function requireConsumer() {
|
|
6533
6608
|
const c = loadConsumer();
|
|
6534
6609
|
if (!c) {
|
|
6535
|
-
console.error(
|
|
6610
|
+
console.error(import_chalk40.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
6536
6611
|
process.exit(1);
|
|
6537
6612
|
}
|
|
6538
6613
|
return c;
|
|
@@ -6543,7 +6618,7 @@ async function runConsumerLogin(opts) {
|
|
|
6543
6618
|
let clientId = opts.client;
|
|
6544
6619
|
if (clientId) {
|
|
6545
6620
|
if (!tenant2) {
|
|
6546
|
-
console.error(
|
|
6621
|
+
console.error(import_chalk40.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
6547
6622
|
process.exit(1);
|
|
6548
6623
|
}
|
|
6549
6624
|
} else {
|
|
@@ -6555,25 +6630,25 @@ async function runConsumerLogin(opts) {
|
|
|
6555
6630
|
if (!picked) process.exit(1);
|
|
6556
6631
|
tenant2 = picked;
|
|
6557
6632
|
}
|
|
6558
|
-
const s2 = (0,
|
|
6633
|
+
const s2 = (0, import_ora22.default)("Finding the login app...").start();
|
|
6559
6634
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
6560
6635
|
s2.stop();
|
|
6561
6636
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
6562
6637
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
6563
6638
|
if (!pick2) {
|
|
6564
|
-
console.error(
|
|
6639
|
+
console.error(import_chalk40.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
6565
6640
|
process.exit(1);
|
|
6566
6641
|
}
|
|
6567
6642
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
6568
6643
|
}
|
|
6569
6644
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
6570
|
-
console.log(`${
|
|
6645
|
+
console.log(`${import_chalk40.default.cyan("\u2192")} Logging in to ${import_chalk40.default.bold(tenant2)} as a consumer...`);
|
|
6571
6646
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
6572
6647
|
console.log(`
|
|
6573
|
-
Open: ${
|
|
6574
|
-
console.log(` Code: ${
|
|
6648
|
+
Open: ${import_chalk40.default.underline(verificationUri)}`);
|
|
6649
|
+
console.log(` Code: ${import_chalk40.default.bold(userCode)}
|
|
6575
6650
|
`);
|
|
6576
|
-
console.log(
|
|
6651
|
+
console.log(import_chalk40.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
6577
6652
|
}, portalResource);
|
|
6578
6653
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
6579
6654
|
const creds = {
|
|
@@ -6588,7 +6663,7 @@ async function runConsumerLogin(opts) {
|
|
|
6588
6663
|
obtainedAt: Date.now()
|
|
6589
6664
|
};
|
|
6590
6665
|
saveConsumer(creds);
|
|
6591
|
-
console.log(
|
|
6666
|
+
console.log(import_chalk40.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
6592
6667
|
}
|
|
6593
6668
|
async function runConsumerTokens(opts) {
|
|
6594
6669
|
const creds = requireConsumer();
|
|
@@ -6601,29 +6676,29 @@ async function runConsumerTokens(opts) {
|
|
|
6601
6676
|
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));
|
|
6602
6677
|
return;
|
|
6603
6678
|
}
|
|
6604
|
-
console.log(`${
|
|
6679
|
+
console.log(`${import_chalk40.default.cyan("Consumer")} ${import_chalk40.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk40.default.bold(fresh.tenant)}
|
|
6605
6680
|
`);
|
|
6606
|
-
console.log(`${
|
|
6681
|
+
console.log(`${import_chalk40.default.bold("access_token")} ${import_chalk40.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
6607
6682
|
${fresh.accessToken}
|
|
6608
6683
|
`);
|
|
6609
|
-
if (fresh.idToken) console.log(`${
|
|
6684
|
+
if (fresh.idToken) console.log(`${import_chalk40.default.bold("id_token")} ${import_chalk40.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
6610
6685
|
${fresh.idToken}
|
|
6611
6686
|
`);
|
|
6612
|
-
if (fresh.refreshToken) console.log(`${
|
|
6687
|
+
if (fresh.refreshToken) console.log(`${import_chalk40.default.bold("refresh_token")}
|
|
6613
6688
|
${fresh.refreshToken}
|
|
6614
6689
|
`);
|
|
6615
|
-
console.log(
|
|
6690
|
+
console.log(import_chalk40.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
6616
6691
|
}
|
|
6617
6692
|
async function runConsumerApikeys(opts) {
|
|
6618
6693
|
const creds = requireConsumer();
|
|
6619
6694
|
const { default: inquirer3 } = await import("inquirer");
|
|
6620
|
-
const spinner = (0,
|
|
6695
|
+
const spinner = (0, import_ora22.default)("Loading your API keys...").start();
|
|
6621
6696
|
const list = await consumerFetch(creds, "/apikeys");
|
|
6622
6697
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
6623
6698
|
spinner.stop();
|
|
6624
6699
|
if (list.status >= 400) {
|
|
6625
|
-
console.error(
|
|
6626
|
-
if (list.status === 401) console.error(
|
|
6700
|
+
console.error(import_chalk40.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
6701
|
+
if (list.status === 401) console.error(import_chalk40.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
6627
6702
|
process.exit(1);
|
|
6628
6703
|
}
|
|
6629
6704
|
const keys = list.data?.keys ?? [];
|
|
@@ -6631,16 +6706,16 @@ async function runConsumerApikeys(opts) {
|
|
|
6631
6706
|
if (opts.json) {
|
|
6632
6707
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
6633
6708
|
} else if (!keys.length) {
|
|
6634
|
-
console.log(
|
|
6709
|
+
console.log(import_chalk40.default.yellow("No API keys yet."));
|
|
6635
6710
|
} else {
|
|
6636
6711
|
for (const k of keys) {
|
|
6637
6712
|
const clear = revealMap[k.environment]?.key;
|
|
6638
|
-
const shown = clear ?
|
|
6639
|
-
const exp = k.expires_at ?
|
|
6640
|
-
console.log(` ${
|
|
6713
|
+
const shown = clear ? import_chalk40.default.green(clear) : import_chalk40.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
6714
|
+
const exp = k.expires_at ? import_chalk40.default.dim(`exp ${k.expires_at}`) : import_chalk40.default.dim("no expiry");
|
|
6715
|
+
console.log(` ${import_chalk40.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk40.default.dim(k.description ?? "")}`);
|
|
6641
6716
|
}
|
|
6642
6717
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
6643
|
-
console.log(
|
|
6718
|
+
console.log(import_chalk40.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
6644
6719
|
}
|
|
6645
6720
|
}
|
|
6646
6721
|
if (opts.json) return;
|
|
@@ -6654,7 +6729,7 @@ async function runConsumerApikeys(opts) {
|
|
|
6654
6729
|
const body = { environment: answers.environment };
|
|
6655
6730
|
if (answers.description) body.description = answers.description;
|
|
6656
6731
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
6657
|
-
const s2 = (0,
|
|
6732
|
+
const s2 = (0, import_ora22.default)("Creating key...").start();
|
|
6658
6733
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
6659
6734
|
if (created.status >= 400) {
|
|
6660
6735
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -6662,13 +6737,13 @@ async function runConsumerApikeys(opts) {
|
|
|
6662
6737
|
}
|
|
6663
6738
|
s2.succeed("Key created.");
|
|
6664
6739
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
6665
|
-
if (key) console.log(` ${
|
|
6666
|
-
else console.log(
|
|
6740
|
+
if (key) console.log(` ${import_chalk40.default.green(key)} ${import_chalk40.default.dim("(shown once \u2014 store it now)")}`);
|
|
6741
|
+
else console.log(import_chalk40.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
6667
6742
|
}
|
|
6668
6743
|
|
|
6669
6744
|
// src/commands/sidecar.ts
|
|
6670
|
-
var
|
|
6671
|
-
var
|
|
6745
|
+
var import_chalk41 = __toESM(require("chalk"));
|
|
6746
|
+
var import_ora23 = __toESM(require("ora"));
|
|
6672
6747
|
var fs10 = __toESM(require("fs"));
|
|
6673
6748
|
var path7 = __toESM(require("path"));
|
|
6674
6749
|
init_admin();
|
|
@@ -6710,18 +6785,18 @@ function upsertEnvLocal(root, token) {
|
|
|
6710
6785
|
}
|
|
6711
6786
|
function installSidecarPackage(root) {
|
|
6712
6787
|
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
6713
|
-
console.log(` ${
|
|
6788
|
+
console.log(` ${import_chalk41.default.green("\u2713")} apiblaze package already installed`);
|
|
6714
6789
|
return;
|
|
6715
6790
|
}
|
|
6716
6791
|
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
6717
6792
|
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" };
|
|
6718
|
-
const spinner = (0,
|
|
6793
|
+
const spinner = (0, import_ora23.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
6719
6794
|
try {
|
|
6720
6795
|
const { execSync } = require("child_process");
|
|
6721
6796
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
6722
6797
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
6723
6798
|
} catch {
|
|
6724
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
6799
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk41.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk41.default.cyan("npm run dev")}.`);
|
|
6725
6800
|
}
|
|
6726
6801
|
}
|
|
6727
6802
|
function readEnvKey(root) {
|
|
@@ -6846,8 +6921,8 @@ function generateInspector(root, router) {
|
|
|
6846
6921
|
fs10.writeFileSync(f2, INSPECTOR_PAGE);
|
|
6847
6922
|
return path7.relative(root, f2);
|
|
6848
6923
|
}
|
|
6849
|
-
const
|
|
6850
|
-
const dir = path7.join(
|
|
6924
|
+
const base2 = fs10.existsSync(path7.join(root, "src", "app")) ? path7.join(root, "src", "app") : path7.join(root, "app");
|
|
6925
|
+
const dir = path7.join(base2, "abz-inspector");
|
|
6851
6926
|
fs10.mkdirSync(dir, { recursive: true });
|
|
6852
6927
|
const f = path7.join(dir, "page.tsx");
|
|
6853
6928
|
fs10.writeFileSync(f, INSPECTOR_PAGE);
|
|
@@ -6860,7 +6935,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6860
6935
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
6861
6936
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
6862
6937
|
if (opts.newSession) clearAnonCred2();
|
|
6863
|
-
const spinner = (0,
|
|
6938
|
+
const spinner = (0, import_ora23.default)("Setting up a sidecar (no login needed)...").start();
|
|
6864
6939
|
let out;
|
|
6865
6940
|
try {
|
|
6866
6941
|
out = await sidecarInitAnonymous2();
|
|
@@ -6872,29 +6947,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6872
6947
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
6873
6948
|
const envState = upsertEnvLocal(root, out.token);
|
|
6874
6949
|
ensureGitignored(root);
|
|
6875
|
-
console.log(` ${
|
|
6876
|
-
console.log(` ${
|
|
6950
|
+
console.log(` ${import_chalk41.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6951
|
+
console.log(` ${import_chalk41.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6877
6952
|
installSidecarPackage(root);
|
|
6878
6953
|
let inspectorPath = null;
|
|
6879
6954
|
if (!opts.noInspector) {
|
|
6880
6955
|
inspectorPath = generateInspector(root, router);
|
|
6881
|
-
if (inspectorPath) console.log(` ${
|
|
6956
|
+
if (inspectorPath) console.log(` ${import_chalk41.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6882
6957
|
}
|
|
6883
6958
|
console.log("");
|
|
6884
|
-
console.log(
|
|
6885
|
-
console.log(` 1. ${
|
|
6959
|
+
console.log(import_chalk41.default.bold("Done (no account needed). What happens next:"));
|
|
6960
|
+
console.log(` 1. ${import_chalk41.default.cyan("npm run dev")} and use your app.`);
|
|
6886
6961
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
6887
|
-
console.log(` ${
|
|
6962
|
+
console.log(` ${import_chalk41.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
6888
6963
|
console.log("");
|
|
6889
|
-
console.log(
|
|
6890
|
-
console.log(` ${
|
|
6891
|
-
console.log(
|
|
6964
|
+
console.log(import_chalk41.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
6965
|
+
console.log(` ${import_chalk41.default.cyan("apiblaze login")} then ${import_chalk41.default.cyan("apiblaze claim")} ${import_chalk41.default.dim("(no code needed here)")}`);
|
|
6966
|
+
console.log(import_chalk41.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
6892
6967
|
}
|
|
6893
6968
|
async function runSidecar(opts) {
|
|
6894
6969
|
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
6895
6970
|
const detected = detectNextProject(root);
|
|
6896
6971
|
if (!detected.found) {
|
|
6897
|
-
console.log(
|
|
6972
|
+
console.log(import_chalk41.default.yellow(`No Next.js project detected in ${root}.`));
|
|
6898
6973
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
6899
6974
|
return;
|
|
6900
6975
|
}
|
|
@@ -6905,10 +6980,10 @@ async function runSidecar(opts) {
|
|
|
6905
6980
|
if (!loadCredentials()) {
|
|
6906
6981
|
upsertEnvLocal(root, readEnvKey(root));
|
|
6907
6982
|
ensureGitignored(root);
|
|
6908
|
-
console.log(` ${
|
|
6909
|
-
console.log(` ${
|
|
6983
|
+
console.log(` ${import_chalk41.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
6984
|
+
console.log(` ${import_chalk41.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6910
6985
|
installSidecarPackage(root);
|
|
6911
|
-
console.log(
|
|
6986
|
+
console.log(import_chalk41.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
6912
6987
|
return;
|
|
6913
6988
|
}
|
|
6914
6989
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -6917,7 +6992,7 @@ async function runSidecar(opts) {
|
|
|
6917
6992
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
6918
6993
|
let token = existingKey ?? "";
|
|
6919
6994
|
if (mustMint) {
|
|
6920
|
-
const spinner = (0,
|
|
6995
|
+
const spinner = (0, import_ora23.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
|
|
6921
6996
|
try {
|
|
6922
6997
|
const out = await admin({
|
|
6923
6998
|
method: "POST",
|
|
@@ -6931,39 +7006,39 @@ async function runSidecar(opts) {
|
|
|
6931
7006
|
throw err;
|
|
6932
7007
|
}
|
|
6933
7008
|
} else {
|
|
6934
|
-
console.log(
|
|
7009
|
+
console.log(import_chalk41.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
6935
7010
|
}
|
|
6936
7011
|
const envState = upsertEnvLocal(root, token);
|
|
6937
7012
|
ensureGitignored(root);
|
|
6938
|
-
console.log(` ${
|
|
7013
|
+
console.log(` ${import_chalk41.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6939
7014
|
const wireState = wireInstrumentation(root);
|
|
6940
|
-
console.log(` ${
|
|
7015
|
+
console.log(` ${import_chalk41.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
6941
7016
|
installSidecarPackage(root);
|
|
6942
7017
|
let inspectorPath = null;
|
|
6943
7018
|
if (!opts.noInspector) {
|
|
6944
7019
|
inspectorPath = generateInspector(root, detected.router);
|
|
6945
|
-
if (inspectorPath) console.log(` ${
|
|
7020
|
+
if (inspectorPath) console.log(` ${import_chalk41.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6946
7021
|
}
|
|
6947
7022
|
console.log("");
|
|
6948
|
-
console.log(
|
|
6949
|
-
console.log(` 1. ${
|
|
6950
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
6951
|
-
console.log(` 3. Approve the ones to route: ${
|
|
7023
|
+
console.log(import_chalk41.default.bold("Done. What happens next:"));
|
|
7024
|
+
console.log(` 1. ${import_chalk41.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
7025
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk41.default.bold("candidates")} \u2014 list them: ${import_chalk41.default.cyan("apiblaze sidecar")}`);
|
|
7026
|
+
console.log(` 3. Approve the ones to route: ${import_chalk41.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
6952
7027
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
6953
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
6954
|
-
if (switchingTeam) console.log(
|
|
7028
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk41.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
|
|
7029
|
+
if (switchingTeam) console.log(import_chalk41.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
6955
7030
|
console.log("");
|
|
6956
|
-
console.log(
|
|
6957
|
-
console.log(
|
|
6958
|
-
console.log(
|
|
7031
|
+
console.log(import_chalk41.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
7032
|
+
console.log(import_chalk41.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
7033
|
+
console.log(import_chalk41.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
6959
7034
|
console.log("");
|
|
6960
|
-
console.log(
|
|
6961
|
-
console.log(
|
|
7035
|
+
console.log(import_chalk41.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
7036
|
+
console.log(import_chalk41.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
6962
7037
|
}
|
|
6963
7038
|
|
|
6964
7039
|
// src/commands/origins.ts
|
|
6965
|
-
var
|
|
6966
|
-
var
|
|
7040
|
+
var import_chalk42 = __toESM(require("chalk"));
|
|
7041
|
+
var import_ora24 = __toESM(require("ora"));
|
|
6967
7042
|
init_admin();
|
|
6968
7043
|
init_resolve();
|
|
6969
7044
|
init_auth();
|
|
@@ -6973,7 +7048,7 @@ async function runOriginsList(opts) {
|
|
|
6973
7048
|
if (!loadCredentials()) {
|
|
6974
7049
|
const cred = loadAnonCred();
|
|
6975
7050
|
if (!cred) {
|
|
6976
|
-
console.log(
|
|
7051
|
+
console.log(import_chalk42.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
6977
7052
|
return;
|
|
6978
7053
|
}
|
|
6979
7054
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -6991,30 +7066,30 @@ async function runOriginsList(opts) {
|
|
|
6991
7066
|
}
|
|
6992
7067
|
const routed = out.routed ?? [];
|
|
6993
7068
|
const candidates = out.candidates ?? [];
|
|
6994
|
-
console.log(
|
|
7069
|
+
console.log(import_chalk42.default.bold(`
|
|
6995
7070
|
Routed through APIblaze (${routed.length})`));
|
|
6996
|
-
if (!routed.length) console.log(
|
|
6997
|
-
for (const r of routed) console.log(` ${
|
|
6998
|
-
console.log(
|
|
7071
|
+
if (!routed.length) console.log(import_chalk42.default.dim(" none yet"));
|
|
7072
|
+
for (const r of routed) console.log(` ${import_chalk42.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk42.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
7073
|
+
console.log(import_chalk42.default.bold(`
|
|
6999
7074
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
7000
|
-
if (!candidates.length) console.log(
|
|
7075
|
+
if (!candidates.length) console.log(import_chalk42.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
7001
7076
|
for (const c of candidates) {
|
|
7002
|
-
console.log(` ${
|
|
7077
|
+
console.log(` ${import_chalk42.default.yellow("\u25CB")} ${c.origin} ${import_chalk42.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
7003
7078
|
}
|
|
7004
7079
|
if (candidates.length) {
|
|
7005
|
-
console.log(
|
|
7080
|
+
console.log(import_chalk42.default.dim(`
|
|
7006
7081
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
7007
|
-
console.log(
|
|
7082
|
+
console.log(import_chalk42.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
7008
7083
|
}
|
|
7009
7084
|
}
|
|
7010
7085
|
async function runOriginsApprove(origin, opts) {
|
|
7011
7086
|
if (!loadCredentials()) {
|
|
7012
7087
|
const cred = loadAnonCred();
|
|
7013
7088
|
if (!cred) {
|
|
7014
|
-
console.error(
|
|
7089
|
+
console.error(import_chalk42.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
7015
7090
|
process.exit(1);
|
|
7016
7091
|
}
|
|
7017
|
-
const spinner2 = (0,
|
|
7092
|
+
const spinner2 = (0, import_ora24.default)(`Approving ${origin} (anonymous)...`).start();
|
|
7018
7093
|
try {
|
|
7019
7094
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
7020
7095
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -7025,7 +7100,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
7025
7100
|
return;
|
|
7026
7101
|
}
|
|
7027
7102
|
const { teamId } = await resolveTeam(opts.team);
|
|
7028
|
-
const spinner = (0,
|
|
7103
|
+
const spinner = (0, import_ora24.default)(`Approving ${origin}...`).start();
|
|
7029
7104
|
try {
|
|
7030
7105
|
const out = await admin({
|
|
7031
7106
|
method: "POST",
|
|
@@ -7042,7 +7117,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
7042
7117
|
}
|
|
7043
7118
|
async function runOriginsDeny(origin, opts) {
|
|
7044
7119
|
const { teamId } = await resolveTeam(opts.team);
|
|
7045
|
-
const spinner = (0,
|
|
7120
|
+
const spinner = (0, import_ora24.default)(`Dismissing ${origin}...`).start();
|
|
7046
7121
|
try {
|
|
7047
7122
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
7048
7123
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -7053,7 +7128,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
7053
7128
|
}
|
|
7054
7129
|
async function runOriginsRemove(origin, opts) {
|
|
7055
7130
|
const { teamId } = await resolveTeam(opts.team);
|
|
7056
|
-
const spinner = (0,
|
|
7131
|
+
const spinner = (0, import_ora24.default)(`Removing the proxy for ${origin}...`).start();
|
|
7057
7132
|
try {
|
|
7058
7133
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
7059
7134
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -7064,7 +7139,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
7064
7139
|
}
|
|
7065
7140
|
|
|
7066
7141
|
// src/commands/op.ts
|
|
7067
|
-
var
|
|
7142
|
+
var import_chalk43 = __toESM(require("chalk"));
|
|
7068
7143
|
init_auth();
|
|
7069
7144
|
init_trace();
|
|
7070
7145
|
init_types();
|
|
@@ -7097,82 +7172,82 @@ function printResidue(report, applied) {
|
|
|
7097
7172
|
const up = report?.upstash ?? {};
|
|
7098
7173
|
const fga = report?.fga ?? {};
|
|
7099
7174
|
const ghosts = report?.ghosts ?? {};
|
|
7100
|
-
console.log(
|
|
7101
|
-
console.log(
|
|
7175
|
+
console.log(import_chalk43.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
7176
|
+
console.log(import_chalk43.default.bold("\n Upstash"));
|
|
7102
7177
|
const orphans = up.orphans ?? [];
|
|
7103
|
-
if (orphans.length === 0) console.log(
|
|
7104
|
-
for (const o of orphans) console.log(` ${
|
|
7105
|
-
console.log(
|
|
7178
|
+
if (orphans.length === 0) console.log(import_chalk43.default.green(" no orphaned keys"));
|
|
7179
|
+
for (const o of orphans) console.log(` ${import_chalk43.default.yellow(o.key)} ${import_chalk43.default.dim(`\u2014 ${o.reason}`)}`);
|
|
7180
|
+
console.log(import_chalk43.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
7106
7181
|
if (up.anon_wallet_detail) {
|
|
7107
7182
|
const d = up.anon_wallet_detail;
|
|
7108
|
-
console.log(
|
|
7183
|
+
console.log(import_chalk43.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)"}`));
|
|
7109
7184
|
}
|
|
7110
7185
|
if (up.keyspace_census) {
|
|
7111
7186
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
7112
|
-
console.log(
|
|
7187
|
+
console.log(import_chalk43.default.dim(` keyspace: ${census}`));
|
|
7113
7188
|
}
|
|
7114
|
-
if (up.unknown?.length) console.log(
|
|
7115
|
-
if (applied) console.log(` ${
|
|
7116
|
-
for (const e of up.errors ?? []) console.log(
|
|
7117
|
-
console.log(
|
|
7189
|
+
if (up.unknown?.length) console.log(import_chalk43.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
7190
|
+
if (applied) console.log(` ${import_chalk43.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
7191
|
+
for (const e of up.errors ?? []) console.log(import_chalk43.default.red(` error: ${e}`));
|
|
7192
|
+
console.log(import_chalk43.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
7118
7193
|
if (applied) {
|
|
7119
7194
|
const swept = fga?.swept ?? [];
|
|
7120
|
-
if (swept.length === 0) console.log(
|
|
7195
|
+
if (swept.length === 0) console.log(import_chalk43.default.green(" no orphaned stores"));
|
|
7121
7196
|
for (const s of swept) {
|
|
7122
7197
|
console.log(
|
|
7123
|
-
` ${
|
|
7198
|
+
` ${import_chalk43.default.yellow(s.store_id)} ${import_chalk43.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
7124
7199
|
);
|
|
7125
7200
|
}
|
|
7126
|
-
if (fga?.remaining) console.log(
|
|
7201
|
+
if (fga?.remaining) console.log(import_chalk43.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
7127
7202
|
const st = fga?.side_tables;
|
|
7128
|
-
if (st) console.log(
|
|
7203
|
+
if (st) console.log(import_chalk43.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})` : ""}`));
|
|
7129
7204
|
} else {
|
|
7130
7205
|
const fgaOrphans = fga?.orphans ?? [];
|
|
7131
|
-
if (fgaOrphans.length === 0) console.log(
|
|
7206
|
+
if (fgaOrphans.length === 0) console.log(import_chalk43.default.green(" no orphaned stores"));
|
|
7132
7207
|
for (const s of fgaOrphans) {
|
|
7133
7208
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
7134
|
-
console.log(` ${
|
|
7209
|
+
console.log(` ${import_chalk43.default.yellow(s.store_id)} ${import_chalk43.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
7135
7210
|
}
|
|
7136
|
-
console.log(
|
|
7211
|
+
console.log(import_chalk43.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
7137
7212
|
const st = fga?.side_tables;
|
|
7138
|
-
if (st) console.log(
|
|
7213
|
+
if (st) console.log(import_chalk43.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`));
|
|
7139
7214
|
}
|
|
7140
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
7141
|
-
console.log(
|
|
7215
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk43.default.red(` error: ${e}`));
|
|
7216
|
+
console.log(import_chalk43.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
7142
7217
|
if (applied) {
|
|
7143
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
7144
|
-
else console.log(` ${
|
|
7218
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk43.default.green(" no ghost tuples"));
|
|
7219
|
+
else console.log(` ${import_chalk43.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk43.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
7145
7220
|
} else {
|
|
7146
7221
|
const n = ghosts?.ghost_count ?? 0;
|
|
7147
|
-
if (n === 0) console.log(
|
|
7222
|
+
if (n === 0) console.log(import_chalk43.default.green(` no ghost tuples ${import_chalk43.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
7148
7223
|
else {
|
|
7149
|
-
console.log(
|
|
7224
|
+
console.log(import_chalk43.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
7150
7225
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
7151
|
-
console.log(
|
|
7226
|
+
console.log(import_chalk43.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
7152
7227
|
}
|
|
7153
|
-
if (n > 20) console.log(
|
|
7228
|
+
if (n > 20) console.log(import_chalk43.default.dim(` \u2026 and ${n - 20} more`));
|
|
7154
7229
|
}
|
|
7155
7230
|
}
|
|
7156
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
7231
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk43.default.red(` error: ${e}`));
|
|
7157
7232
|
console.log();
|
|
7158
7233
|
}
|
|
7159
7234
|
async function runOp(sub, opts = {}) {
|
|
7160
7235
|
if (!loadCredentials()) {
|
|
7161
|
-
console.log(
|
|
7236
|
+
console.log(import_chalk43.default.dim("Not logged in. Run `apiblaze login`."));
|
|
7162
7237
|
return;
|
|
7163
7238
|
}
|
|
7164
7239
|
if (!isOperatorLogin()) {
|
|
7165
|
-
console.log(
|
|
7240
|
+
console.log(import_chalk43.default.dim("`apiblaze op` is only available to platform operators."));
|
|
7166
7241
|
return;
|
|
7167
7242
|
}
|
|
7168
7243
|
switch (sub) {
|
|
7169
7244
|
case void 0:
|
|
7170
7245
|
case "menu": {
|
|
7171
|
-
console.log(
|
|
7172
|
-
console.log(` ${
|
|
7173
|
-
console.log(` ${
|
|
7174
|
-
console.log(` ${
|
|
7175
|
-
console.log(
|
|
7246
|
+
console.log(import_chalk43.default.bold("\nOperator menu"));
|
|
7247
|
+
console.log(` ${import_chalk43.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
7248
|
+
console.log(` ${import_chalk43.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk43.default.dim("-y to skip")})`);
|
|
7249
|
+
console.log(` ${import_chalk43.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
7250
|
+
console.log(import_chalk43.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
7176
7251
|
`));
|
|
7177
7252
|
return;
|
|
7178
7253
|
}
|
|
@@ -7191,17 +7266,17 @@ async function runOp(sub, opts = {}) {
|
|
|
7191
7266
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
7192
7267
|
printResidue(report, false);
|
|
7193
7268
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
7194
|
-
console.log(
|
|
7269
|
+
console.log(import_chalk43.default.green("Nothing to sweep."));
|
|
7195
7270
|
return;
|
|
7196
7271
|
}
|
|
7197
7272
|
if (!opts.yes) {
|
|
7198
7273
|
const readline2 = await import("readline/promises");
|
|
7199
7274
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
7200
7275
|
const answer = await rl.question(
|
|
7201
|
-
|
|
7276
|
+
import_chalk43.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: `)
|
|
7202
7277
|
);
|
|
7203
7278
|
rl.close();
|
|
7204
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
7279
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk43.default.dim("Aborted."));
|
|
7205
7280
|
}
|
|
7206
7281
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
7207
7282
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -7212,15 +7287,15 @@ async function runOp(sub, opts = {}) {
|
|
|
7212
7287
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
7213
7288
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
7214
7289
|
const accounts = data?.accounts ?? [];
|
|
7215
|
-
if (accounts.length === 0) return void console.log(
|
|
7290
|
+
if (accounts.length === 0) return void console.log(import_chalk43.default.dim("No credit wallets."));
|
|
7216
7291
|
for (const a of accounts) {
|
|
7217
7292
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
7218
|
-
console.log(` ${
|
|
7293
|
+
console.log(` ${import_chalk43.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk43.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk43.default.dim(" \u2014 anon") : ""}`);
|
|
7219
7294
|
}
|
|
7220
7295
|
return;
|
|
7221
7296
|
}
|
|
7222
7297
|
default:
|
|
7223
|
-
console.log(
|
|
7298
|
+
console.log(import_chalk43.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
7224
7299
|
}
|
|
7225
7300
|
}
|
|
7226
7301
|
|
|
@@ -7283,7 +7358,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
7283
7358
|
try {
|
|
7284
7359
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
7285
7360
|
if (Number.isNaN(resolved)) {
|
|
7286
|
-
console.error(
|
|
7361
|
+
console.error(import_chalk44.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
7287
7362
|
process.exit(1);
|
|
7288
7363
|
}
|
|
7289
7364
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -7361,12 +7436,17 @@ tenant.command("create").description("Create a tenant in your team (tenant names
|
|
|
7361
7436
|
tenant.command("attach").description("Attach a tenant to a proxy").argument("<project>", "Project name or id").requiredOption("--tenant <slug>", "Tenant slug to attach").option("--auth-config <id>", "Auth config id to bind").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTenantAttach(project, opts)));
|
|
7362
7437
|
tenant.command("delete").description("Delete a tenant (full cascade)").argument("<slug>", "Tenant slug to delete").option("--team <id|name>", "Team (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").action(action((slug, opts) => runTenantDelete(slug, opts)));
|
|
7363
7438
|
tenant.command("cors").description("Set the CORS allow-list for a tenant").requiredOption("--tenant <slug>", "Tenant slug").option("--origins <list>", 'Comma-separated origins (or "*"); empty clears').option("--team <id|name>", "Team (defaults to active team)").action(action((opts) => runTenantCors(opts)));
|
|
7439
|
+
var admins = new import_commander.Command("admins").description("Manage who can administer a tenant's users & groups (the first-admin bootstrap the widget needs)").option("--tenant <slug>", "Tenant slug (e.g. nino)").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--json", "Output machine-readable JSON").action(action((opts) => runAdminsList(opts)));
|
|
7440
|
+
admins.command("list").description("List a tenant's admins").option("--tenant <slug>", "Tenant slug").option("--team <id|name>", "Team").option("--json", "JSON").action(action((opts, cmd) => runAdminsList({ ...cmd.parent?.opts(), ...opts })));
|
|
7441
|
+
admins.command("add").description("Add an email as a tenant admin (e.g. yourself, to start)").argument("<email>", "Email to grant admin").option("--tenant <slug>", "Tenant slug").option("--team <id|name>", "Team").option("--json", "JSON").action(action((email, opts, cmd) => runAdminsAdd(email, { ...cmd.parent?.opts(), ...opts })));
|
|
7442
|
+
admins.command("remove").description("Remove a tenant admin").argument("<email>", "Email to remove").option("--tenant <slug>", "Tenant slug").option("--team <id|name>", "Team").option("--json", "JSON").action(action((email, opts, cmd) => runAdminsRemove(email, { ...cmd.parent?.opts(), ...opts })));
|
|
7443
|
+
program.addCommand(admins);
|
|
7364
7444
|
program.command("iam").description("Turn users & groups on/off for a proxy's tenant (identified calls get their user's groups applied)").argument("<project>", "Project name or id").argument("<state>", "on | off").option("--tenant <name>", "Tenant to toggle (defaults to the project's tenant)").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--apiversion <version>", "API version (defaults to the first)").option("--json", "Output machine-readable JSON").action(action((project, state, opts) => runIamToggle(project, state, opts)));
|
|
7365
7445
|
program.command("identified").description("Require calls to identify their end user (X-End-User-Id or a login token) \u2014 or allow unattributed calls again").argument("<project>", "Project name or id").argument("<mode>", "require | allow-anon").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--apiversion <version>", "API version (defaults to the first)").option("--json", "Output machine-readable JSON").action(action((project, mode, opts) => runIdentifiedToggle(project, mode, opts)));
|
|
7366
7446
|
var apikeys = new import_commander.Command("apikeys").description("Producer control-plane API keys (list, then offer to create one)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runApikeysMenu(opts)));
|
|
7367
|
-
apikeys.command("list").description("List control-plane API keys in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runKeyList(opts)));
|
|
7368
|
-
apikeys.command("mint").description("Create a control-plane API key (secret shown once)").option("--desc <text>", "Description").option("--expires-days <n>", "Expiry in days (default 90 server-side)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runKeyMint(opts)));
|
|
7369
|
-
apikeys.command("revoke").description("Revoke a control-plane API key").argument("<keyId>", "Key id (see `apikeys list`)").option("--team <id|name>", "Team (defaults to active team)").action(action((keyId, opts) => runKeyRevoke(keyId, opts)));
|
|
7447
|
+
apikeys.command("list").description("List control-plane API keys in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts, cmd) => runKeyList({ ...cmd.parent?.opts(), ...opts })));
|
|
7448
|
+
apikeys.command("mint").description("Create a control-plane API key (secret shown once)").option("--desc <text>", "Description").option("--expires-days <n>", "Expiry in days (default 90 server-side)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts, cmd) => runKeyMint({ ...cmd.parent?.opts(), ...opts })));
|
|
7449
|
+
apikeys.command("revoke").description("Revoke a control-plane API key").argument("<keyId>", "Key id (see `apikeys list`)").option("--team <id|name>", "Team (defaults to active team)").action(action((keyId, opts, cmd) => runKeyRevoke(keyId, { ...cmd.parent?.opts(), ...opts })));
|
|
7370
7450
|
program.addCommand(apikeys, { hidden: true });
|
|
7371
7451
|
var op = new import_commander.Command("op").description("Operator menu (platform operators only)").argument("[action]", "residue | sweep | credits (omit for the menu)").option("-y, --yes", "Skip the sweep confirmation prompt").option("--json", "Output machine-readable JSON").action(action((sub, opts) => runOp(sub, opts)));
|
|
7372
7452
|
program.addCommand(op, { hidden: true });
|
|
@@ -7378,7 +7458,7 @@ spec.command("delete-rule").description("Delete the saved rules for a route (e.g
|
|
|
7378
7458
|
var HELP_GROUPS = [
|
|
7379
7459
|
{ title: "Chat", commands: ["apichat", "agent"] },
|
|
7380
7460
|
{ title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
|
|
7381
|
-
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
7461
|
+
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
7382
7462
|
{ title: "Data plane commands", commands: [
|
|
7383
7463
|
{ parent: "consumer", sub: "login" },
|
|
7384
7464
|
{ parent: "consumer", sub: "apikeys" }
|
|
@@ -7397,7 +7477,7 @@ function groupedCommandHelp() {
|
|
|
7397
7477
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
7398
7478
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
7399
7479
|
}).filter(Boolean).join("\n");
|
|
7400
|
-
return `${
|
|
7480
|
+
return `${import_chalk44.default.bold(g.title)}
|
|
7401
7481
|
${rows}`;
|
|
7402
7482
|
}).join("\n\n");
|
|
7403
7483
|
}
|
|
@@ -7431,14 +7511,14 @@ async function recoverStaleTeam() {
|
|
|
7431
7511
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
7432
7512
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
7433
7513
|
if (!linked) {
|
|
7434
|
-
console.error(
|
|
7514
|
+
console.error(import_chalk44.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
7435
7515
|
return;
|
|
7436
7516
|
}
|
|
7437
7517
|
if (linked.teamId === creds.teamId) return;
|
|
7438
7518
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
7439
7519
|
delete next.activeTenant;
|
|
7440
7520
|
saveCredentials(next);
|
|
7441
|
-
console.error(
|
|
7521
|
+
console.error(import_chalk44.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk44.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
7442
7522
|
} catch {
|
|
7443
7523
|
}
|
|
7444
7524
|
}
|
|
@@ -7446,16 +7526,16 @@ async function printError(err) {
|
|
|
7446
7526
|
if (err instanceof ApiError) {
|
|
7447
7527
|
const data = err.body;
|
|
7448
7528
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
7449
|
-
console.error(
|
|
7529
|
+
console.error(import_chalk44.default.red(`
|
|
7450
7530
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
7451
7531
|
if (err.status === 403 || err.status === 404) {
|
|
7452
7532
|
await recoverStaleTeam();
|
|
7453
7533
|
}
|
|
7454
7534
|
} else if (err instanceof Error) {
|
|
7455
|
-
console.error(
|
|
7535
|
+
console.error(import_chalk44.default.red(`
|
|
7456
7536
|
Error: ${err.message}`));
|
|
7457
7537
|
} else {
|
|
7458
|
-
console.error(
|
|
7538
|
+
console.error(import_chalk44.default.red("\nUnknown error"));
|
|
7459
7539
|
}
|
|
7460
7540
|
}
|
|
7461
7541
|
program.parse(process.argv);
|