apiblaze 0.19.1 → 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 +386 -305
- 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
|
}
|
|
@@ -5418,9 +5418,85 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
5418
5418
|
// src/index.ts
|
|
5419
5419
|
init_iam();
|
|
5420
5420
|
|
|
5421
|
-
// src/commands/
|
|
5421
|
+
// src/commands/admins.ts
|
|
5422
5422
|
var import_chalk36 = __toESM(require("chalk"));
|
|
5423
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"));
|
|
5424
5500
|
|
|
5425
5501
|
// src/lib/preapproval.ts
|
|
5426
5502
|
init_auth();
|
|
@@ -5484,14 +5560,14 @@ async function runPreapprove(who, opts) {
|
|
|
5484
5560
|
const rules = await listPreapprovalRules(tenant2);
|
|
5485
5561
|
if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
|
|
5486
5562
|
if (!rules.length) {
|
|
5487
|
-
console.log(
|
|
5488
|
-
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`));
|
|
5489
5565
|
return;
|
|
5490
5566
|
}
|
|
5491
|
-
console.log(
|
|
5567
|
+
console.log(import_chalk37.default.dim(`Pre-approved for ${import_chalk37.default.bold(tenant2)}:`));
|
|
5492
5568
|
for (const r of rules) {
|
|
5493
|
-
const tag = r.kind === "domain" ?
|
|
5494
|
-
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(", ")}`) : "";
|
|
5495
5571
|
console.log(` ${tag}${grp}`);
|
|
5496
5572
|
}
|
|
5497
5573
|
return;
|
|
@@ -5500,19 +5576,19 @@ async function runPreapprove(who, opts) {
|
|
|
5500
5576
|
throw new Error("Who? Pass an email or a domain: `apiblaze preapprove someone@acme.com` (or `apiblaze preapprove --list`).");
|
|
5501
5577
|
}
|
|
5502
5578
|
if (opts.remove) {
|
|
5503
|
-
const spinner2 = (0,
|
|
5579
|
+
const spinner2 = (0, import_ora20.default)(`Removing ${who} from ${tenant2}\u2026`).start();
|
|
5504
5580
|
const { removed, value } = await removePreapprovalRule(tenant2, who);
|
|
5505
|
-
if (removed) spinner2.succeed(`${
|
|
5506
|
-
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.`);
|
|
5507
5583
|
return;
|
|
5508
5584
|
}
|
|
5509
|
-
const spinner = (0,
|
|
5585
|
+
const spinner = (0, import_ora20.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
|
|
5510
5586
|
try {
|
|
5511
5587
|
const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
|
|
5512
5588
|
const what = kind === "domain" ? `Anyone @${value}` : value;
|
|
5513
|
-
spinner.succeed(`${
|
|
5514
|
-
if (opts.group?.length) console.log(
|
|
5515
|
-
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`));
|
|
5516
5592
|
} catch (err) {
|
|
5517
5593
|
spinner.fail("Could not add the rule.");
|
|
5518
5594
|
throw err;
|
|
@@ -5523,8 +5599,8 @@ async function runPreapprove(who, opts) {
|
|
|
5523
5599
|
var fs9 = __toESM(require("fs"));
|
|
5524
5600
|
var path6 = __toESM(require("path"));
|
|
5525
5601
|
var crypto2 = __toESM(require("crypto"));
|
|
5526
|
-
var
|
|
5527
|
-
var
|
|
5602
|
+
var import_chalk39 = __toESM(require("chalk"));
|
|
5603
|
+
var import_ora21 = __toESM(require("ora"));
|
|
5528
5604
|
var import_yaml = require("yaml");
|
|
5529
5605
|
init_auth();
|
|
5530
5606
|
init_anon_cred();
|
|
@@ -5534,7 +5610,7 @@ init_admin();
|
|
|
5534
5610
|
// src/commands/llm.ts
|
|
5535
5611
|
var fs8 = __toESM(require("fs"));
|
|
5536
5612
|
var path5 = __toESM(require("path"));
|
|
5537
|
-
var
|
|
5613
|
+
var import_chalk38 = __toESM(require("chalk"));
|
|
5538
5614
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
5539
5615
|
init_auth();
|
|
5540
5616
|
var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
|
|
@@ -5578,27 +5654,27 @@ async function runLlmSetKey(keyArg, opts) {
|
|
|
5578
5654
|
}
|
|
5579
5655
|
const existing = loadLlmConfig();
|
|
5580
5656
|
saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
|
|
5581
|
-
console.log(`${
|
|
5582
|
-
console.log(
|
|
5583
|
-
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}`));
|
|
5584
5660
|
}
|
|
5585
5661
|
async function runLlmShow() {
|
|
5586
5662
|
const cfg = loadLlmConfig();
|
|
5587
5663
|
if (!cfg) {
|
|
5588
|
-
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)."));
|
|
5589
5665
|
return;
|
|
5590
5666
|
}
|
|
5591
|
-
console.log(`Provider: ${
|
|
5667
|
+
console.log(`Provider: ${import_chalk38.default.bold(cfg.provider)}`);
|
|
5592
5668
|
console.log(`Key: ${maskSecret(cfg.key)}`);
|
|
5593
5669
|
if (cfg.model) console.log(`Model: ${cfg.model}`);
|
|
5594
|
-
console.log(
|
|
5670
|
+
console.log(import_chalk38.default.gray(`Stored at ${LLM_PATH}`));
|
|
5595
5671
|
}
|
|
5596
5672
|
async function runLlmClearKey() {
|
|
5597
5673
|
try {
|
|
5598
5674
|
fs8.unlinkSync(LLM_PATH);
|
|
5599
|
-
console.log(`${
|
|
5675
|
+
console.log(`${import_chalk38.default.green("\u2713")} Removed local LLM key.`);
|
|
5600
5676
|
} catch {
|
|
5601
|
-
console.log(
|
|
5677
|
+
console.log(import_chalk38.default.gray("No LLM key was set."));
|
|
5602
5678
|
}
|
|
5603
5679
|
}
|
|
5604
5680
|
|
|
@@ -5606,9 +5682,9 @@ async function runLlmClearKey() {
|
|
|
5606
5682
|
init_trace();
|
|
5607
5683
|
init_types();
|
|
5608
5684
|
function fail4(message, hint) {
|
|
5609
|
-
console.error(
|
|
5685
|
+
console.error(import_chalk39.default.red(`
|
|
5610
5686
|
Error: ${message}`));
|
|
5611
|
-
if (hint) console.error(
|
|
5687
|
+
if (hint) console.error(import_chalk39.default.dim(hint));
|
|
5612
5688
|
process.exit(1);
|
|
5613
5689
|
}
|
|
5614
5690
|
function normalizeName2(raw) {
|
|
@@ -5650,9 +5726,9 @@ async function fetchText(url) {
|
|
|
5650
5726
|
}
|
|
5651
5727
|
}
|
|
5652
5728
|
async function discoverSpec(target) {
|
|
5653
|
-
const
|
|
5729
|
+
const base2 = target.replace(/\/+$/, "");
|
|
5654
5730
|
for (const suffix of ["/openapi.json", "/openapi.yaml", "/swagger.json"]) {
|
|
5655
|
-
const url =
|
|
5731
|
+
const url = base2 + suffix;
|
|
5656
5732
|
const text = await fetchText(url);
|
|
5657
5733
|
if (text) {
|
|
5658
5734
|
try {
|
|
@@ -5756,7 +5832,7 @@ async function resolveTargetAuth(spec2, opts) {
|
|
|
5756
5832
|
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
5757
5833
|
);
|
|
5758
5834
|
}
|
|
5759
|
-
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`."));
|
|
5760
5836
|
return null;
|
|
5761
5837
|
}
|
|
5762
5838
|
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
@@ -5821,28 +5897,28 @@ async function provision(spec2, target, opts) {
|
|
|
5821
5897
|
const loggedIn = !!loadCredentials();
|
|
5822
5898
|
const anon = !loggedIn;
|
|
5823
5899
|
const salt = () => Math.random().toString(36).slice(2, 6);
|
|
5824
|
-
let
|
|
5825
|
-
if (!
|
|
5900
|
+
let base2 = opts.name ? normalizeName2(opts.name) : "";
|
|
5901
|
+
if (!base2) {
|
|
5826
5902
|
try {
|
|
5827
5903
|
const host = new URL(target).hostname;
|
|
5828
|
-
|
|
5829
|
-
if (
|
|
5904
|
+
base2 = normalizeName2(host.split(".")[0]);
|
|
5905
|
+
if (base2.length < 3) base2 = normalizeName2(host);
|
|
5830
5906
|
} catch {
|
|
5831
5907
|
}
|
|
5832
5908
|
}
|
|
5833
|
-
if (!
|
|
5834
|
-
if (!
|
|
5835
|
-
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()}`;
|
|
5836
5912
|
const access = anon ? "open" : opts.access === "open" ? "open" : "invite";
|
|
5837
5913
|
if (anon && opts.access === "invite") {
|
|
5838
|
-
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`."));
|
|
5839
5915
|
}
|
|
5840
5916
|
const DUAL_AUTH = {
|
|
5841
5917
|
mode: "authenticate",
|
|
5842
5918
|
methods: ["api_key", "jwt"],
|
|
5843
5919
|
...access === "invite" ? { preapproved_users_only: true } : {}
|
|
5844
5920
|
};
|
|
5845
|
-
const spinner = (0,
|
|
5921
|
+
const spinner = (0, import_ora21.default)("Provisioning an api_key proxy...").start();
|
|
5846
5922
|
let result;
|
|
5847
5923
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
5848
5924
|
try {
|
|
@@ -5874,13 +5950,13 @@ async function provision(spec2, target, opts) {
|
|
|
5874
5950
|
if (result.cp_key && result.team_id) saveAnonCred(result.cp_key, result.team_id, result.claim_code);
|
|
5875
5951
|
}
|
|
5876
5952
|
}
|
|
5877
|
-
spinner.succeed(`Proxy provisioned${name !==
|
|
5953
|
+
spinner.succeed(`Proxy provisioned${name !== base2 ? ` as "${name}"` : ""}.`);
|
|
5878
5954
|
break;
|
|
5879
5955
|
} catch (err) {
|
|
5880
5956
|
const status = err instanceof ApiError ? err.status : void 0;
|
|
5881
5957
|
const collision = err instanceof ApiError && (err.status === 409 || /exist|taken|available/i.test(err.message));
|
|
5882
5958
|
if (collision && attempt < 3) {
|
|
5883
|
-
name = `${
|
|
5959
|
+
name = `${base2}${salt()}`;
|
|
5884
5960
|
continue;
|
|
5885
5961
|
}
|
|
5886
5962
|
if (status === 401) {
|
|
@@ -5928,14 +6004,14 @@ async function provision(spec2, target, opts) {
|
|
|
5928
6004
|
try {
|
|
5929
6005
|
await addPreapprovalRule(tenant2, email);
|
|
5930
6006
|
} catch {
|
|
5931
|
-
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})`));
|
|
5932
6008
|
}
|
|
5933
6009
|
}
|
|
5934
6010
|
}
|
|
5935
6011
|
return { projectId, version: version2, environment, dpKey, mcpHost, proxyUrl, anon, access, tenant: tenant2 };
|
|
5936
6012
|
}
|
|
5937
6013
|
async function writeTargetAuth(p, auth, secret) {
|
|
5938
|
-
const spinner = (0,
|
|
6014
|
+
const spinner = (0, import_ora21.default)("Storing target credentials (encrypted)...").start();
|
|
5939
6015
|
try {
|
|
5940
6016
|
await cpPost(
|
|
5941
6017
|
p.anon,
|
|
@@ -5957,7 +6033,7 @@ async function writeTargetAuth(p, auth, secret) {
|
|
|
5957
6033
|
}
|
|
5958
6034
|
}
|
|
5959
6035
|
async function uploadSpec(p, specText, opts) {
|
|
5960
|
-
const spinner = (0,
|
|
6036
|
+
const spinner = (0, import_ora21.default)("Uploading the spec...").start();
|
|
5961
6037
|
let out;
|
|
5962
6038
|
try {
|
|
5963
6039
|
out = await cpPost(
|
|
@@ -5972,7 +6048,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5972
6048
|
throw err;
|
|
5973
6049
|
}
|
|
5974
6050
|
if (out && out.reused === true) {
|
|
5975
|
-
console.log(
|
|
6051
|
+
console.log(import_chalk39.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
|
|
5976
6052
|
} else if (out && out.changed === true && out.previous_spec_hash) {
|
|
5977
6053
|
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
5978
6054
|
if (interactive) {
|
|
@@ -5980,12 +6056,12 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5980
6056
|
const { go } = await inquirer3.prompt([
|
|
5981
6057
|
{ type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
|
|
5982
6058
|
]);
|
|
5983
|
-
if (!go) console.log(
|
|
6059
|
+
if (!go) console.log(import_chalk39.default.dim(" Keeping the existing MCP catalogue."));
|
|
5984
6060
|
}
|
|
5985
6061
|
}
|
|
5986
6062
|
}
|
|
5987
6063
|
async function publishMcp(p, spec2) {
|
|
5988
|
-
const spinner = (0,
|
|
6064
|
+
const spinner = (0, import_ora21.default)("Publishing the MCP catalogue...").start();
|
|
5989
6065
|
try {
|
|
5990
6066
|
const url = `https://${p.mcpHost}/${p.version}/${p.environment}/mcp/generate`;
|
|
5991
6067
|
const res = await fetch(url, {
|
|
@@ -6021,14 +6097,14 @@ var revealAuth = false;
|
|
|
6021
6097
|
function renderToolEvents(events, dpKey) {
|
|
6022
6098
|
for (const e of events ?? []) {
|
|
6023
6099
|
const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
|
|
6024
|
-
const mark = ok ?
|
|
6025
|
-
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)`)}`);
|
|
6026
6102
|
if (isVerbose() && e.method && e.url) {
|
|
6027
|
-
console.log(
|
|
6103
|
+
console.log(import_chalk39.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
|
|
6028
6104
|
if (dpKey) {
|
|
6029
6105
|
const keyLine = ` -H 'X-API-Key: ${revealAuth ? dpKey : maskKey(dpKey)}'`;
|
|
6030
|
-
const hint = revealAuth ? "" :
|
|
6031
|
-
console.log(
|
|
6106
|
+
const hint = revealAuth ? "" : import_chalk39.default.yellow(" \u2190 /showauth will reveal this");
|
|
6107
|
+
console.log(import_chalk39.default.dim(keyLine) + hint);
|
|
6032
6108
|
}
|
|
6033
6109
|
}
|
|
6034
6110
|
}
|
|
@@ -6037,9 +6113,9 @@ function billingLine(billing) {
|
|
|
6037
6113
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
6038
6114
|
if (typeof billing.free_turns_remaining === "number") return null;
|
|
6039
6115
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
6040
|
-
let line =
|
|
6116
|
+
let line = import_chalk39.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk39.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
6041
6117
|
if (typeof billing.credits_remaining === "number") {
|
|
6042
|
-
line +=
|
|
6118
|
+
line += import_chalk39.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
6043
6119
|
}
|
|
6044
6120
|
return line;
|
|
6045
6121
|
}
|
|
@@ -6047,21 +6123,21 @@ function freeBudgetWarning(billing, anon) {
|
|
|
6047
6123
|
if (!anon || !billing) return null;
|
|
6048
6124
|
if (typeof billing.free_turns_remaining === "number") {
|
|
6049
6125
|
const left2 = billing.free_turns_remaining;
|
|
6050
|
-
if (left2 <= 0) return
|
|
6051
|
-
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`);
|
|
6052
6128
|
}
|
|
6053
6129
|
if (typeof billing.free_remaining_cents !== "number") return null;
|
|
6054
6130
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
6055
6131
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
6056
6132
|
if (left > 8) return null;
|
|
6057
|
-
if (left <= 0) return
|
|
6058
|
-
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.`);
|
|
6059
6135
|
}
|
|
6060
6136
|
function printAssistant(delta) {
|
|
6061
6137
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
6062
6138
|
const m = delta[i];
|
|
6063
6139
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
6064
|
-
console.log("\n" +
|
|
6140
|
+
console.log("\n" + import_chalk39.default.green("assistant \u203A ") + m.content + "\n");
|
|
6065
6141
|
return;
|
|
6066
6142
|
}
|
|
6067
6143
|
}
|
|
@@ -6071,7 +6147,7 @@ async function replTurn(p, messages, userText) {
|
|
|
6071
6147
|
const llm2 = loadLlmConfig();
|
|
6072
6148
|
const turnId = crypto2.randomUUID();
|
|
6073
6149
|
for (let round = 0; round < CLIENT_ROUND_CAP; round++) {
|
|
6074
|
-
const spinner = (0,
|
|
6150
|
+
const spinner = (0, import_ora21.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
|
|
6075
6151
|
const body = {
|
|
6076
6152
|
turn_id: turnId,
|
|
6077
6153
|
messages,
|
|
@@ -6087,7 +6163,7 @@ async function replTurn(p, messages, userText) {
|
|
|
6087
6163
|
});
|
|
6088
6164
|
} catch (err) {
|
|
6089
6165
|
spinner.fail("Network error.");
|
|
6090
|
-
console.log(
|
|
6166
|
+
console.log(import_chalk39.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
6091
6167
|
return;
|
|
6092
6168
|
}
|
|
6093
6169
|
let data = null;
|
|
@@ -6105,12 +6181,12 @@ async function replTurn(p, messages, userText) {
|
|
|
6105
6181
|
return;
|
|
6106
6182
|
}
|
|
6107
6183
|
if (res.status === 401) {
|
|
6108
|
-
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."));
|
|
6109
6185
|
return;
|
|
6110
6186
|
}
|
|
6111
6187
|
if (!res.ok || !data) {
|
|
6112
6188
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
6113
|
-
console.log(
|
|
6189
|
+
console.log(import_chalk39.default.red(` Chat error: ${err}`));
|
|
6114
6190
|
return;
|
|
6115
6191
|
}
|
|
6116
6192
|
if (Array.isArray(data.delta)) {
|
|
@@ -6124,25 +6200,25 @@ async function replTurn(p, messages, userText) {
|
|
|
6124
6200
|
if (warn) console.log(warn);
|
|
6125
6201
|
if (!data.continue) return;
|
|
6126
6202
|
}
|
|
6127
|
-
console.log(
|
|
6203
|
+
console.log(import_chalk39.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
6128
6204
|
}
|
|
6129
6205
|
function renderUpsell(p, upsell) {
|
|
6130
6206
|
const loggedIn = !!loadCredentials();
|
|
6131
6207
|
if (upsell.reason === "CAPPED" && !loggedIn) {
|
|
6132
|
-
console.log("\n" +
|
|
6133
|
-
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.)"));
|
|
6134
6210
|
console.log();
|
|
6135
6211
|
return;
|
|
6136
6212
|
}
|
|
6137
|
-
console.log("\n" +
|
|
6213
|
+
console.log("\n" + import_chalk39.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
6138
6214
|
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
6139
6215
|
if (!loggedIn) {
|
|
6140
|
-
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."));
|
|
6141
6217
|
} else {
|
|
6142
|
-
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)."));
|
|
6143
6219
|
}
|
|
6144
6220
|
} else if (upsell.reason === "INFLIGHT") {
|
|
6145
|
-
console.log(
|
|
6221
|
+
console.log(import_chalk39.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
6146
6222
|
}
|
|
6147
6223
|
console.log();
|
|
6148
6224
|
}
|
|
@@ -6211,9 +6287,9 @@ async function openServerProxy(project) {
|
|
|
6211
6287
|
const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
|
|
6212
6288
|
let dpKey = prior?.dpKey;
|
|
6213
6289
|
if (!dpKey) {
|
|
6214
|
-
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`));
|
|
6215
6291
|
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
6216
|
-
console.log(` ${
|
|
6292
|
+
console.log(` ${import_chalk39.default.green("\u2714")} API key: ${import_chalk39.default.dim(maskKey(dpKey))}`);
|
|
6217
6293
|
}
|
|
6218
6294
|
const p = {
|
|
6219
6295
|
projectId: project.projectId,
|
|
@@ -6226,7 +6302,7 @@ async function openServerProxy(project) {
|
|
|
6226
6302
|
// Reusing an owned proxy: logged-in apichat doors default to invite-only.
|
|
6227
6303
|
access: "invite"
|
|
6228
6304
|
};
|
|
6229
|
-
const spinner = (0,
|
|
6305
|
+
const spinner = (0, import_ora21.default)("Preparing the chat\u2026").start();
|
|
6230
6306
|
try {
|
|
6231
6307
|
const raw = await admin({
|
|
6232
6308
|
method: "GET",
|
|
@@ -6238,7 +6314,7 @@ async function openServerProxy(project) {
|
|
|
6238
6314
|
if (spec2 && (spec2.paths || spec2.openapi)) {
|
|
6239
6315
|
await publishMcp(p, spec2);
|
|
6240
6316
|
} else {
|
|
6241
|
-
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`."));
|
|
6242
6318
|
}
|
|
6243
6319
|
} catch (err) {
|
|
6244
6320
|
spinner.fail("Could not open the proxy.");
|
|
@@ -6291,7 +6367,7 @@ async function noArgsMenu(opts) {
|
|
|
6291
6367
|
const me = loadCredentials()?.apiblazeUserId;
|
|
6292
6368
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
6293
6369
|
const choices = saved.map((a) => ({
|
|
6294
|
-
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` : ""}`)}`,
|
|
6295
6371
|
value: { type: "existing", a }
|
|
6296
6372
|
}));
|
|
6297
6373
|
const creds = loadCredentials();
|
|
@@ -6301,14 +6377,14 @@ async function noArgsMenu(opts) {
|
|
|
6301
6377
|
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
6302
6378
|
for (const pr of proxies) {
|
|
6303
6379
|
choices.push({
|
|
6304
|
-
name: `Chat with ${
|
|
6380
|
+
name: `Chat with ${import_chalk39.default.bold(pr.projectName)} ${import_chalk39.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
6305
6381
|
value: { type: "server", project: pr }
|
|
6306
6382
|
});
|
|
6307
6383
|
}
|
|
6308
6384
|
} catch {
|
|
6309
6385
|
}
|
|
6310
6386
|
}
|
|
6311
|
-
choices.push({ name:
|
|
6387
|
+
choices.push({ name: import_chalk39.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
6312
6388
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
6313
6389
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
6314
6390
|
]);
|
|
@@ -6382,36 +6458,36 @@ async function noArgsMenu(opts) {
|
|
|
6382
6458
|
async function runRepl(p, initialMessages) {
|
|
6383
6459
|
const { default: inquirer3 } = await import("inquirer");
|
|
6384
6460
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
6385
|
-
console.log("\n" +
|
|
6386
|
-
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.`));
|
|
6387
6463
|
const llm2 = loadLlmConfig();
|
|
6388
6464
|
console.log(
|
|
6389
|
-
|
|
6465
|
+
import_chalk39.default.dim(
|
|
6390
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."
|
|
6391
6467
|
)
|
|
6392
6468
|
);
|
|
6393
6469
|
for (; ; ) {
|
|
6394
|
-
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") }]);
|
|
6395
6471
|
const text = (input ?? "").trim();
|
|
6396
6472
|
if (!text) continue;
|
|
6397
6473
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
6398
6474
|
if (text === "/login") {
|
|
6399
6475
|
try {
|
|
6400
6476
|
await runLogin();
|
|
6401
|
-
console.log(
|
|
6477
|
+
console.log(import_chalk39.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
6402
6478
|
} catch (err) {
|
|
6403
|
-
console.log(
|
|
6479
|
+
console.log(import_chalk39.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6404
6480
|
}
|
|
6405
6481
|
continue;
|
|
6406
6482
|
}
|
|
6407
6483
|
if (text === "/claim") {
|
|
6408
6484
|
const justLoggedIn = !loadCredentials();
|
|
6409
6485
|
if (justLoggedIn) {
|
|
6410
|
-
console.log(
|
|
6486
|
+
console.log(import_chalk39.default.dim(" Logging in to claim your workspace\u2026"));
|
|
6411
6487
|
try {
|
|
6412
6488
|
await runLogin();
|
|
6413
6489
|
} catch (err) {
|
|
6414
|
-
console.log(
|
|
6490
|
+
console.log(import_chalk39.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6415
6491
|
continue;
|
|
6416
6492
|
}
|
|
6417
6493
|
if (!loadCredentials()) continue;
|
|
@@ -6422,30 +6498,30 @@ async function runRepl(p, initialMessages) {
|
|
|
6422
6498
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
6423
6499
|
p.anon = false;
|
|
6424
6500
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
6425
|
-
console.log(
|
|
6501
|
+
console.log(import_chalk39.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
6426
6502
|
}
|
|
6427
6503
|
} catch (err) {
|
|
6428
|
-
console.log(
|
|
6504
|
+
console.log(import_chalk39.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6429
6505
|
}
|
|
6430
6506
|
continue;
|
|
6431
6507
|
}
|
|
6432
6508
|
if (text === "/showauth") {
|
|
6433
6509
|
revealAuth = !revealAuth;
|
|
6434
|
-
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."));
|
|
6435
6511
|
continue;
|
|
6436
6512
|
}
|
|
6437
6513
|
if (text.startsWith("/")) {
|
|
6438
|
-
console.log(
|
|
6514
|
+
console.log(import_chalk39.default.dim(" Commands: /login /claim /showauth /exit"));
|
|
6439
6515
|
continue;
|
|
6440
6516
|
}
|
|
6441
6517
|
await replTurn(p, messages, text);
|
|
6442
6518
|
saveTranscript(p, messages);
|
|
6443
6519
|
}
|
|
6444
|
-
console.log(
|
|
6520
|
+
console.log(import_chalk39.default.dim("\nBye."));
|
|
6445
6521
|
}
|
|
6446
6522
|
async function runApichat(opts) {
|
|
6447
6523
|
setVerbose(opts.verbose !== false);
|
|
6448
|
-
console.log(
|
|
6524
|
+
console.log(import_chalk39.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
6449
6525
|
if (!opts.openapispec && !opts.target) {
|
|
6450
6526
|
if (!process.stdin.isTTY) {
|
|
6451
6527
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -6458,7 +6534,7 @@ async function runApichat(opts) {
|
|
|
6458
6534
|
}
|
|
6459
6535
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
6460
6536
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
6461
|
-
console.log(` ${
|
|
6537
|
+
console.log(` ${import_chalk39.default.dim("Target:")} ${import_chalk39.default.bold(target)}`);
|
|
6462
6538
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
6463
6539
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
6464
6540
|
fail4(
|
|
@@ -6467,7 +6543,7 @@ async function runApichat(opts) {
|
|
|
6467
6543
|
);
|
|
6468
6544
|
}
|
|
6469
6545
|
const p = await provision(spec2, target, opts);
|
|
6470
|
-
console.log(` ${
|
|
6546
|
+
console.log(` ${import_chalk39.default.dim("Proxy: ")} ${import_chalk39.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
6471
6547
|
upsertApichat({
|
|
6472
6548
|
name: p.projectId,
|
|
6473
6549
|
target,
|
|
@@ -6486,31 +6562,31 @@ async function runApichat(opts) {
|
|
|
6486
6562
|
const secret = await captureTargetSecret(auth, opts);
|
|
6487
6563
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
6488
6564
|
} else {
|
|
6489
|
-
console.log(
|
|
6565
|
+
console.log(import_chalk39.default.dim(" Target auth: none required."));
|
|
6490
6566
|
}
|
|
6491
6567
|
const specText = JSON.stringify(spec2);
|
|
6492
6568
|
await uploadSpec(p, specText, opts);
|
|
6493
6569
|
const mcpUrl = await publishMcp(p, spec2);
|
|
6494
6570
|
console.log();
|
|
6495
|
-
if (p.proxyUrl) console.log(` ${
|
|
6571
|
+
if (p.proxyUrl) console.log(` ${import_chalk39.default.green("\u2713")} proxy ${import_chalk39.default.bold(p.proxyUrl)}`);
|
|
6496
6572
|
if (mcpUrl) {
|
|
6497
|
-
console.log(` ${
|
|
6573
|
+
console.log(` ${import_chalk39.default.green("\u2713")} mcp ${import_chalk39.default.bold(mcpUrl)}`);
|
|
6498
6574
|
if (p.access === "invite") {
|
|
6499
|
-
console.log(
|
|
6500
|
-
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)`));
|
|
6501
6577
|
} else {
|
|
6502
|
-
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"));
|
|
6503
6579
|
}
|
|
6504
6580
|
}
|
|
6505
6581
|
if (p.anon) {
|
|
6506
|
-
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."));
|
|
6507
6583
|
}
|
|
6508
6584
|
await runRepl(p);
|
|
6509
6585
|
}
|
|
6510
6586
|
|
|
6511
6587
|
// src/commands/consumer.ts
|
|
6512
|
-
var
|
|
6513
|
-
var
|
|
6588
|
+
var import_chalk40 = __toESM(require("chalk"));
|
|
6589
|
+
var import_ora22 = __toESM(require("ora"));
|
|
6514
6590
|
init_admin();
|
|
6515
6591
|
init_resolve();
|
|
6516
6592
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
@@ -6531,7 +6607,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
6531
6607
|
function requireConsumer() {
|
|
6532
6608
|
const c = loadConsumer();
|
|
6533
6609
|
if (!c) {
|
|
6534
|
-
console.error(
|
|
6610
|
+
console.error(import_chalk40.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
6535
6611
|
process.exit(1);
|
|
6536
6612
|
}
|
|
6537
6613
|
return c;
|
|
@@ -6542,7 +6618,7 @@ async function runConsumerLogin(opts) {
|
|
|
6542
6618
|
let clientId = opts.client;
|
|
6543
6619
|
if (clientId) {
|
|
6544
6620
|
if (!tenant2) {
|
|
6545
|
-
console.error(
|
|
6621
|
+
console.error(import_chalk40.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
6546
6622
|
process.exit(1);
|
|
6547
6623
|
}
|
|
6548
6624
|
} else {
|
|
@@ -6554,25 +6630,25 @@ async function runConsumerLogin(opts) {
|
|
|
6554
6630
|
if (!picked) process.exit(1);
|
|
6555
6631
|
tenant2 = picked;
|
|
6556
6632
|
}
|
|
6557
|
-
const s2 = (0,
|
|
6633
|
+
const s2 = (0, import_ora22.default)("Finding the login app...").start();
|
|
6558
6634
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
6559
6635
|
s2.stop();
|
|
6560
6636
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
6561
6637
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
6562
6638
|
if (!pick2) {
|
|
6563
|
-
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).`));
|
|
6564
6640
|
process.exit(1);
|
|
6565
6641
|
}
|
|
6566
6642
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
6567
6643
|
}
|
|
6568
6644
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
6569
|
-
console.log(`${
|
|
6645
|
+
console.log(`${import_chalk40.default.cyan("\u2192")} Logging in to ${import_chalk40.default.bold(tenant2)} as a consumer...`);
|
|
6570
6646
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
6571
6647
|
console.log(`
|
|
6572
|
-
Open: ${
|
|
6573
|
-
console.log(` Code: ${
|
|
6648
|
+
Open: ${import_chalk40.default.underline(verificationUri)}`);
|
|
6649
|
+
console.log(` Code: ${import_chalk40.default.bold(userCode)}
|
|
6574
6650
|
`);
|
|
6575
|
-
console.log(
|
|
6651
|
+
console.log(import_chalk40.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
6576
6652
|
}, portalResource);
|
|
6577
6653
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
6578
6654
|
const creds = {
|
|
@@ -6587,7 +6663,7 @@ async function runConsumerLogin(opts) {
|
|
|
6587
6663
|
obtainedAt: Date.now()
|
|
6588
6664
|
};
|
|
6589
6665
|
saveConsumer(creds);
|
|
6590
|
-
console.log(
|
|
6666
|
+
console.log(import_chalk40.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
6591
6667
|
}
|
|
6592
6668
|
async function runConsumerTokens(opts) {
|
|
6593
6669
|
const creds = requireConsumer();
|
|
@@ -6600,29 +6676,29 @@ async function runConsumerTokens(opts) {
|
|
|
6600
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));
|
|
6601
6677
|
return;
|
|
6602
6678
|
}
|
|
6603
|
-
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)}
|
|
6604
6680
|
`);
|
|
6605
|
-
console.log(`${
|
|
6681
|
+
console.log(`${import_chalk40.default.bold("access_token")} ${import_chalk40.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
6606
6682
|
${fresh.accessToken}
|
|
6607
6683
|
`);
|
|
6608
|
-
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) ?? "?"))}
|
|
6609
6685
|
${fresh.idToken}
|
|
6610
6686
|
`);
|
|
6611
|
-
if (fresh.refreshToken) console.log(`${
|
|
6687
|
+
if (fresh.refreshToken) console.log(`${import_chalk40.default.bold("refresh_token")}
|
|
6612
6688
|
${fresh.refreshToken}
|
|
6613
6689
|
`);
|
|
6614
|
-
console.log(
|
|
6690
|
+
console.log(import_chalk40.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
6615
6691
|
}
|
|
6616
6692
|
async function runConsumerApikeys(opts) {
|
|
6617
6693
|
const creds = requireConsumer();
|
|
6618
6694
|
const { default: inquirer3 } = await import("inquirer");
|
|
6619
|
-
const spinner = (0,
|
|
6695
|
+
const spinner = (0, import_ora22.default)("Loading your API keys...").start();
|
|
6620
6696
|
const list = await consumerFetch(creds, "/apikeys");
|
|
6621
6697
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
6622
6698
|
spinner.stop();
|
|
6623
6699
|
if (list.status >= 400) {
|
|
6624
|
-
console.error(
|
|
6625
|
-
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."));
|
|
6626
6702
|
process.exit(1);
|
|
6627
6703
|
}
|
|
6628
6704
|
const keys = list.data?.keys ?? [];
|
|
@@ -6630,16 +6706,16 @@ async function runConsumerApikeys(opts) {
|
|
|
6630
6706
|
if (opts.json) {
|
|
6631
6707
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
6632
6708
|
} else if (!keys.length) {
|
|
6633
|
-
console.log(
|
|
6709
|
+
console.log(import_chalk40.default.yellow("No API keys yet."));
|
|
6634
6710
|
} else {
|
|
6635
6711
|
for (const k of keys) {
|
|
6636
6712
|
const clear = revealMap[k.environment]?.key;
|
|
6637
|
-
const shown = clear ?
|
|
6638
|
-
const exp = k.expires_at ?
|
|
6639
|
-
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 ?? "")}`);
|
|
6640
6716
|
}
|
|
6641
6717
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
6642
|
-
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.)"));
|
|
6643
6719
|
}
|
|
6644
6720
|
}
|
|
6645
6721
|
if (opts.json) return;
|
|
@@ -6653,7 +6729,7 @@ async function runConsumerApikeys(opts) {
|
|
|
6653
6729
|
const body = { environment: answers.environment };
|
|
6654
6730
|
if (answers.description) body.description = answers.description;
|
|
6655
6731
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
6656
|
-
const s2 = (0,
|
|
6732
|
+
const s2 = (0, import_ora22.default)("Creating key...").start();
|
|
6657
6733
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
6658
6734
|
if (created.status >= 400) {
|
|
6659
6735
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -6661,13 +6737,13 @@ async function runConsumerApikeys(opts) {
|
|
|
6661
6737
|
}
|
|
6662
6738
|
s2.succeed("Key created.");
|
|
6663
6739
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
6664
|
-
if (key) console.log(` ${
|
|
6665
|
-
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."));
|
|
6666
6742
|
}
|
|
6667
6743
|
|
|
6668
6744
|
// src/commands/sidecar.ts
|
|
6669
|
-
var
|
|
6670
|
-
var
|
|
6745
|
+
var import_chalk41 = __toESM(require("chalk"));
|
|
6746
|
+
var import_ora23 = __toESM(require("ora"));
|
|
6671
6747
|
var fs10 = __toESM(require("fs"));
|
|
6672
6748
|
var path7 = __toESM(require("path"));
|
|
6673
6749
|
init_admin();
|
|
@@ -6709,18 +6785,18 @@ function upsertEnvLocal(root, token) {
|
|
|
6709
6785
|
}
|
|
6710
6786
|
function installSidecarPackage(root) {
|
|
6711
6787
|
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
6712
|
-
console.log(` ${
|
|
6788
|
+
console.log(` ${import_chalk41.default.green("\u2713")} apiblaze package already installed`);
|
|
6713
6789
|
return;
|
|
6714
6790
|
}
|
|
6715
6791
|
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
6716
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" };
|
|
6717
|
-
const spinner = (0,
|
|
6793
|
+
const spinner = (0, import_ora23.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
6718
6794
|
try {
|
|
6719
6795
|
const { execSync } = require("child_process");
|
|
6720
6796
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
6721
6797
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
6722
6798
|
} catch {
|
|
6723
|
-
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")}.`);
|
|
6724
6800
|
}
|
|
6725
6801
|
}
|
|
6726
6802
|
function readEnvKey(root) {
|
|
@@ -6845,8 +6921,8 @@ function generateInspector(root, router) {
|
|
|
6845
6921
|
fs10.writeFileSync(f2, INSPECTOR_PAGE);
|
|
6846
6922
|
return path7.relative(root, f2);
|
|
6847
6923
|
}
|
|
6848
|
-
const
|
|
6849
|
-
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");
|
|
6850
6926
|
fs10.mkdirSync(dir, { recursive: true });
|
|
6851
6927
|
const f = path7.join(dir, "page.tsx");
|
|
6852
6928
|
fs10.writeFileSync(f, INSPECTOR_PAGE);
|
|
@@ -6859,7 +6935,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6859
6935
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
6860
6936
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
6861
6937
|
if (opts.newSession) clearAnonCred2();
|
|
6862
|
-
const spinner = (0,
|
|
6938
|
+
const spinner = (0, import_ora23.default)("Setting up a sidecar (no login needed)...").start();
|
|
6863
6939
|
let out;
|
|
6864
6940
|
try {
|
|
6865
6941
|
out = await sidecarInitAnonymous2();
|
|
@@ -6871,29 +6947,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6871
6947
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
6872
6948
|
const envState = upsertEnvLocal(root, out.token);
|
|
6873
6949
|
ensureGitignored(root);
|
|
6874
|
-
console.log(` ${
|
|
6875
|
-
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)}`);
|
|
6876
6952
|
installSidecarPackage(root);
|
|
6877
6953
|
let inspectorPath = null;
|
|
6878
6954
|
if (!opts.noInspector) {
|
|
6879
6955
|
inspectorPath = generateInspector(root, router);
|
|
6880
|
-
if (inspectorPath) console.log(` ${
|
|
6956
|
+
if (inspectorPath) console.log(` ${import_chalk41.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6881
6957
|
}
|
|
6882
6958
|
console.log("");
|
|
6883
|
-
console.log(
|
|
6884
|
-
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.`);
|
|
6885
6961
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
6886
|
-
console.log(` ${
|
|
6962
|
+
console.log(` ${import_chalk41.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
6887
6963
|
console.log("");
|
|
6888
|
-
console.log(
|
|
6889
|
-
console.log(` ${
|
|
6890
|
-
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`));
|
|
6891
6967
|
}
|
|
6892
6968
|
async function runSidecar(opts) {
|
|
6893
6969
|
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
6894
6970
|
const detected = detectNextProject(root);
|
|
6895
6971
|
if (!detected.found) {
|
|
6896
|
-
console.log(
|
|
6972
|
+
console.log(import_chalk41.default.yellow(`No Next.js project detected in ${root}.`));
|
|
6897
6973
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
6898
6974
|
return;
|
|
6899
6975
|
}
|
|
@@ -6904,10 +6980,10 @@ async function runSidecar(opts) {
|
|
|
6904
6980
|
if (!loadCredentials()) {
|
|
6905
6981
|
upsertEnvLocal(root, readEnvKey(root));
|
|
6906
6982
|
ensureGitignored(root);
|
|
6907
|
-
console.log(` ${
|
|
6908
|
-
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)}`);
|
|
6909
6985
|
installSidecarPackage(root);
|
|
6910
|
-
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."));
|
|
6911
6987
|
return;
|
|
6912
6988
|
}
|
|
6913
6989
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -6916,7 +6992,7 @@ async function runSidecar(opts) {
|
|
|
6916
6992
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
6917
6993
|
let token = existingKey ?? "";
|
|
6918
6994
|
if (mustMint) {
|
|
6919
|
-
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();
|
|
6920
6996
|
try {
|
|
6921
6997
|
const out = await admin({
|
|
6922
6998
|
method: "POST",
|
|
@@ -6930,39 +7006,39 @@ async function runSidecar(opts) {
|
|
|
6930
7006
|
throw err;
|
|
6931
7007
|
}
|
|
6932
7008
|
} else {
|
|
6933
|
-
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).`));
|
|
6934
7010
|
}
|
|
6935
7011
|
const envState = upsertEnvLocal(root, token);
|
|
6936
7012
|
ensureGitignored(root);
|
|
6937
|
-
console.log(` ${
|
|
7013
|
+
console.log(` ${import_chalk41.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6938
7014
|
const wireState = wireInstrumentation(root);
|
|
6939
|
-
console.log(` ${
|
|
7015
|
+
console.log(` ${import_chalk41.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
6940
7016
|
installSidecarPackage(root);
|
|
6941
7017
|
let inspectorPath = null;
|
|
6942
7018
|
if (!opts.noInspector) {
|
|
6943
7019
|
inspectorPath = generateInspector(root, detected.router);
|
|
6944
|
-
if (inspectorPath) console.log(` ${
|
|
7020
|
+
if (inspectorPath) console.log(` ${import_chalk41.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6945
7021
|
}
|
|
6946
7022
|
console.log("");
|
|
6947
|
-
console.log(
|
|
6948
|
-
console.log(` 1. ${
|
|
6949
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
6950
|
-
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)`);
|
|
6951
7027
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
6952
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
6953
|
-
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>\`.`));
|
|
6954
7030
|
console.log("");
|
|
6955
|
-
console.log(
|
|
6956
|
-
console.log(
|
|
6957
|
-
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)."));
|
|
6958
7034
|
console.log("");
|
|
6959
|
-
console.log(
|
|
6960
|
-
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."));
|
|
6961
7037
|
}
|
|
6962
7038
|
|
|
6963
7039
|
// src/commands/origins.ts
|
|
6964
|
-
var
|
|
6965
|
-
var
|
|
7040
|
+
var import_chalk42 = __toESM(require("chalk"));
|
|
7041
|
+
var import_ora24 = __toESM(require("ora"));
|
|
6966
7042
|
init_admin();
|
|
6967
7043
|
init_resolve();
|
|
6968
7044
|
init_auth();
|
|
@@ -6972,7 +7048,7 @@ async function runOriginsList(opts) {
|
|
|
6972
7048
|
if (!loadCredentials()) {
|
|
6973
7049
|
const cred = loadAnonCred();
|
|
6974
7050
|
if (!cred) {
|
|
6975
|
-
console.log(
|
|
7051
|
+
console.log(import_chalk42.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
6976
7052
|
return;
|
|
6977
7053
|
}
|
|
6978
7054
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -6990,30 +7066,30 @@ async function runOriginsList(opts) {
|
|
|
6990
7066
|
}
|
|
6991
7067
|
const routed = out.routed ?? [];
|
|
6992
7068
|
const candidates = out.candidates ?? [];
|
|
6993
|
-
console.log(
|
|
7069
|
+
console.log(import_chalk42.default.bold(`
|
|
6994
7070
|
Routed through APIblaze (${routed.length})`));
|
|
6995
|
-
if (!routed.length) console.log(
|
|
6996
|
-
for (const r of routed) console.log(` ${
|
|
6997
|
-
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(`
|
|
6998
7074
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
6999
|
-
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"));
|
|
7000
7076
|
for (const c of candidates) {
|
|
7001
|
-
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}`)}`);
|
|
7002
7078
|
}
|
|
7003
7079
|
if (candidates.length) {
|
|
7004
|
-
console.log(
|
|
7080
|
+
console.log(import_chalk42.default.dim(`
|
|
7005
7081
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
7006
|
-
console.log(
|
|
7082
|
+
console.log(import_chalk42.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
7007
7083
|
}
|
|
7008
7084
|
}
|
|
7009
7085
|
async function runOriginsApprove(origin, opts) {
|
|
7010
7086
|
if (!loadCredentials()) {
|
|
7011
7087
|
const cred = loadAnonCred();
|
|
7012
7088
|
if (!cred) {
|
|
7013
|
-
console.error(
|
|
7089
|
+
console.error(import_chalk42.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
7014
7090
|
process.exit(1);
|
|
7015
7091
|
}
|
|
7016
|
-
const spinner2 = (0,
|
|
7092
|
+
const spinner2 = (0, import_ora24.default)(`Approving ${origin} (anonymous)...`).start();
|
|
7017
7093
|
try {
|
|
7018
7094
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
7019
7095
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -7024,7 +7100,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
7024
7100
|
return;
|
|
7025
7101
|
}
|
|
7026
7102
|
const { teamId } = await resolveTeam(opts.team);
|
|
7027
|
-
const spinner = (0,
|
|
7103
|
+
const spinner = (0, import_ora24.default)(`Approving ${origin}...`).start();
|
|
7028
7104
|
try {
|
|
7029
7105
|
const out = await admin({
|
|
7030
7106
|
method: "POST",
|
|
@@ -7041,7 +7117,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
7041
7117
|
}
|
|
7042
7118
|
async function runOriginsDeny(origin, opts) {
|
|
7043
7119
|
const { teamId } = await resolveTeam(opts.team);
|
|
7044
|
-
const spinner = (0,
|
|
7120
|
+
const spinner = (0, import_ora24.default)(`Dismissing ${origin}...`).start();
|
|
7045
7121
|
try {
|
|
7046
7122
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
7047
7123
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -7052,7 +7128,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
7052
7128
|
}
|
|
7053
7129
|
async function runOriginsRemove(origin, opts) {
|
|
7054
7130
|
const { teamId } = await resolveTeam(opts.team);
|
|
7055
|
-
const spinner = (0,
|
|
7131
|
+
const spinner = (0, import_ora24.default)(`Removing the proxy for ${origin}...`).start();
|
|
7056
7132
|
try {
|
|
7057
7133
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
7058
7134
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -7063,7 +7139,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
7063
7139
|
}
|
|
7064
7140
|
|
|
7065
7141
|
// src/commands/op.ts
|
|
7066
|
-
var
|
|
7142
|
+
var import_chalk43 = __toESM(require("chalk"));
|
|
7067
7143
|
init_auth();
|
|
7068
7144
|
init_trace();
|
|
7069
7145
|
init_types();
|
|
@@ -7096,82 +7172,82 @@ function printResidue(report, applied) {
|
|
|
7096
7172
|
const up = report?.upstash ?? {};
|
|
7097
7173
|
const fga = report?.fga ?? {};
|
|
7098
7174
|
const ghosts = report?.ghosts ?? {};
|
|
7099
|
-
console.log(
|
|
7100
|
-
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"));
|
|
7101
7177
|
const orphans = up.orphans ?? [];
|
|
7102
|
-
if (orphans.length === 0) console.log(
|
|
7103
|
-
for (const o of orphans) console.log(` ${
|
|
7104
|
-
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}`));
|
|
7105
7181
|
if (up.anon_wallet_detail) {
|
|
7106
7182
|
const d = up.anon_wallet_detail;
|
|
7107
|
-
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)"}`));
|
|
7108
7184
|
}
|
|
7109
7185
|
if (up.keyspace_census) {
|
|
7110
7186
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
7111
|
-
console.log(
|
|
7187
|
+
console.log(import_chalk43.default.dim(` keyspace: ${census}`));
|
|
7112
7188
|
}
|
|
7113
|
-
if (up.unknown?.length) console.log(
|
|
7114
|
-
if (applied) console.log(` ${
|
|
7115
|
-
for (const e of up.errors ?? []) console.log(
|
|
7116
|
-
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"));
|
|
7117
7193
|
if (applied) {
|
|
7118
7194
|
const swept = fga?.swept ?? [];
|
|
7119
|
-
if (swept.length === 0) console.log(
|
|
7195
|
+
if (swept.length === 0) console.log(import_chalk43.default.green(" no orphaned stores"));
|
|
7120
7196
|
for (const s of swept) {
|
|
7121
7197
|
console.log(
|
|
7122
|
-
` ${
|
|
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`)}`
|
|
7123
7199
|
);
|
|
7124
7200
|
}
|
|
7125
|
-
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`));
|
|
7126
7202
|
const st = fga?.side_tables;
|
|
7127
|
-
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})` : ""}`));
|
|
7128
7204
|
} else {
|
|
7129
7205
|
const fgaOrphans = fga?.orphans ?? [];
|
|
7130
|
-
if (fgaOrphans.length === 0) console.log(
|
|
7206
|
+
if (fgaOrphans.length === 0) console.log(import_chalk43.default.green(" no orphaned stores"));
|
|
7131
7207
|
for (const s of fgaOrphans) {
|
|
7132
7208
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
7133
|
-
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)`)}`);
|
|
7134
7210
|
}
|
|
7135
|
-
console.log(
|
|
7211
|
+
console.log(import_chalk43.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
7136
7212
|
const st = fga?.side_tables;
|
|
7137
|
-
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`));
|
|
7138
7214
|
}
|
|
7139
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
7140
|
-
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"));
|
|
7141
7217
|
if (applied) {
|
|
7142
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
7143
|
-
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)`)}`);
|
|
7144
7220
|
} else {
|
|
7145
7221
|
const n = ghosts?.ghost_count ?? 0;
|
|
7146
|
-
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)`)}`));
|
|
7147
7223
|
else {
|
|
7148
|
-
console.log(
|
|
7224
|
+
console.log(import_chalk43.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
7149
7225
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
7150
|
-
console.log(
|
|
7226
|
+
console.log(import_chalk43.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
7151
7227
|
}
|
|
7152
|
-
if (n > 20) console.log(
|
|
7228
|
+
if (n > 20) console.log(import_chalk43.default.dim(` \u2026 and ${n - 20} more`));
|
|
7153
7229
|
}
|
|
7154
7230
|
}
|
|
7155
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
7231
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk43.default.red(` error: ${e}`));
|
|
7156
7232
|
console.log();
|
|
7157
7233
|
}
|
|
7158
7234
|
async function runOp(sub, opts = {}) {
|
|
7159
7235
|
if (!loadCredentials()) {
|
|
7160
|
-
console.log(
|
|
7236
|
+
console.log(import_chalk43.default.dim("Not logged in. Run `apiblaze login`."));
|
|
7161
7237
|
return;
|
|
7162
7238
|
}
|
|
7163
7239
|
if (!isOperatorLogin()) {
|
|
7164
|
-
console.log(
|
|
7240
|
+
console.log(import_chalk43.default.dim("`apiblaze op` is only available to platform operators."));
|
|
7165
7241
|
return;
|
|
7166
7242
|
}
|
|
7167
7243
|
switch (sub) {
|
|
7168
7244
|
case void 0:
|
|
7169
7245
|
case "menu": {
|
|
7170
|
-
console.log(
|
|
7171
|
-
console.log(` ${
|
|
7172
|
-
console.log(` ${
|
|
7173
|
-
console.log(` ${
|
|
7174
|
-
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)
|
|
7175
7251
|
`));
|
|
7176
7252
|
return;
|
|
7177
7253
|
}
|
|
@@ -7190,17 +7266,17 @@ async function runOp(sub, opts = {}) {
|
|
|
7190
7266
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
7191
7267
|
printResidue(report, false);
|
|
7192
7268
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
7193
|
-
console.log(
|
|
7269
|
+
console.log(import_chalk43.default.green("Nothing to sweep."));
|
|
7194
7270
|
return;
|
|
7195
7271
|
}
|
|
7196
7272
|
if (!opts.yes) {
|
|
7197
7273
|
const readline2 = await import("readline/promises");
|
|
7198
7274
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
7199
7275
|
const answer = await rl.question(
|
|
7200
|
-
|
|
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: `)
|
|
7201
7277
|
);
|
|
7202
7278
|
rl.close();
|
|
7203
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
7279
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk43.default.dim("Aborted."));
|
|
7204
7280
|
}
|
|
7205
7281
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
7206
7282
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -7211,15 +7287,15 @@ async function runOp(sub, opts = {}) {
|
|
|
7211
7287
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
7212
7288
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
7213
7289
|
const accounts = data?.accounts ?? [];
|
|
7214
|
-
if (accounts.length === 0) return void console.log(
|
|
7290
|
+
if (accounts.length === 0) return void console.log(import_chalk43.default.dim("No credit wallets."));
|
|
7215
7291
|
for (const a of accounts) {
|
|
7216
7292
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
7217
|
-
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") : ""}`);
|
|
7218
7294
|
}
|
|
7219
7295
|
return;
|
|
7220
7296
|
}
|
|
7221
7297
|
default:
|
|
7222
|
-
console.log(
|
|
7298
|
+
console.log(import_chalk43.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
7223
7299
|
}
|
|
7224
7300
|
}
|
|
7225
7301
|
|
|
@@ -7282,7 +7358,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
7282
7358
|
try {
|
|
7283
7359
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
7284
7360
|
if (Number.isNaN(resolved)) {
|
|
7285
|
-
console.error(
|
|
7361
|
+
console.error(import_chalk44.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
7286
7362
|
process.exit(1);
|
|
7287
7363
|
}
|
|
7288
7364
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -7360,6 +7436,11 @@ tenant.command("create").description("Create a tenant in your team (tenant names
|
|
|
7360
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)));
|
|
7361
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)));
|
|
7362
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);
|
|
7363
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)));
|
|
7364
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)));
|
|
7365
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)));
|
|
@@ -7377,7 +7458,7 @@ spec.command("delete-rule").description("Delete the saved rules for a route (e.g
|
|
|
7377
7458
|
var HELP_GROUPS = [
|
|
7378
7459
|
{ title: "Chat", commands: ["apichat", "agent"] },
|
|
7379
7460
|
{ title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
|
|
7380
|
-
{ 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"] },
|
|
7381
7462
|
{ title: "Data plane commands", commands: [
|
|
7382
7463
|
{ parent: "consumer", sub: "login" },
|
|
7383
7464
|
{ parent: "consumer", sub: "apikeys" }
|
|
@@ -7396,7 +7477,7 @@ function groupedCommandHelp() {
|
|
|
7396
7477
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
7397
7478
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
7398
7479
|
}).filter(Boolean).join("\n");
|
|
7399
|
-
return `${
|
|
7480
|
+
return `${import_chalk44.default.bold(g.title)}
|
|
7400
7481
|
${rows}`;
|
|
7401
7482
|
}).join("\n\n");
|
|
7402
7483
|
}
|
|
@@ -7430,14 +7511,14 @@ async function recoverStaleTeam() {
|
|
|
7430
7511
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
7431
7512
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
7432
7513
|
if (!linked) {
|
|
7433
|
-
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."));
|
|
7434
7515
|
return;
|
|
7435
7516
|
}
|
|
7436
7517
|
if (linked.teamId === creds.teamId) return;
|
|
7437
7518
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
7438
7519
|
delete next.activeTenant;
|
|
7439
7520
|
saveCredentials(next);
|
|
7440
|
-
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.`));
|
|
7441
7522
|
} catch {
|
|
7442
7523
|
}
|
|
7443
7524
|
}
|
|
@@ -7445,16 +7526,16 @@ async function printError(err) {
|
|
|
7445
7526
|
if (err instanceof ApiError) {
|
|
7446
7527
|
const data = err.body;
|
|
7447
7528
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
7448
|
-
console.error(
|
|
7529
|
+
console.error(import_chalk44.default.red(`
|
|
7449
7530
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
7450
7531
|
if (err.status === 403 || err.status === 404) {
|
|
7451
7532
|
await recoverStaleTeam();
|
|
7452
7533
|
}
|
|
7453
7534
|
} else if (err instanceof Error) {
|
|
7454
|
-
console.error(
|
|
7535
|
+
console.error(import_chalk44.default.red(`
|
|
7455
7536
|
Error: ${err.message}`));
|
|
7456
7537
|
} else {
|
|
7457
|
-
console.error(
|
|
7538
|
+
console.error(import_chalk44.default.red("\nUnknown error"));
|
|
7458
7539
|
}
|
|
7459
7540
|
}
|
|
7460
7541
|
program.parse(process.argv);
|