apiblaze 0.17.21 → 0.17.23
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/README.md +63 -0
- package/dist/index.js +451 -217
- package/dist/react/index.d.mts +67 -7
- package/dist/react/index.d.ts +67 -7
- package/dist/react/index.js +651 -69
- package/dist/react/index.mjs +651 -69
- package/dist/server/index.d.mts +165 -25
- package/dist/server/index.d.ts +165 -25
- package/dist/server/index.js +207 -35
- package/dist/server/index.mjs +204 -36
- package/package.json +28 -16
package/dist/index.js
CHANGED
|
@@ -572,7 +572,7 @@ async function createTenantInteractive(teamId) {
|
|
|
572
572
|
type: "confirm",
|
|
573
573
|
name: "users",
|
|
574
574
|
default: false,
|
|
575
|
-
message: `Enable Users & groups? ${import_chalk23.default.dim(`(identity/key management at
|
|
575
|
+
message: `Enable Users & groups? ${import_chalk23.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
|
|
576
576
|
}]);
|
|
577
577
|
if (users) {
|
|
578
578
|
await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
|
|
@@ -700,10 +700,10 @@ var init_tenant_pick = __esm({
|
|
|
700
700
|
|
|
701
701
|
// src/index.ts
|
|
702
702
|
var import_commander = require("commander");
|
|
703
|
-
var
|
|
703
|
+
var import_chalk39 = __toESM(require("chalk"));
|
|
704
704
|
|
|
705
705
|
// package.json
|
|
706
|
-
var version = "0.17.
|
|
706
|
+
var version = "0.17.23";
|
|
707
707
|
|
|
708
708
|
// src/index.ts
|
|
709
709
|
init_types();
|
|
@@ -1385,9 +1385,9 @@ function printTunnelEndpoints(restore, targets) {
|
|
|
1385
1385
|
if (restore.length === 0) return;
|
|
1386
1386
|
console.log(import_chalk4.default.bold("\nYour proxy is live at:"));
|
|
1387
1387
|
for (const r of restore) {
|
|
1388
|
-
const
|
|
1388
|
+
const label3 = targets.find((t) => t.projectId === r.projectId)?.projectName ?? r.projectId;
|
|
1389
1389
|
console.log(`
|
|
1390
|
-
${import_chalk4.default.bold(
|
|
1390
|
+
${import_chalk4.default.bold(label3)}`);
|
|
1391
1391
|
const internalEnvs = Object.keys(r.environments ?? {}).filter((e) => isInternalTarget(r.environments[e]?.target));
|
|
1392
1392
|
const envs = internalEnvs.includes("dev") ? ["dev"] : internalEnvs.length ? internalEnvs : ["dev"];
|
|
1393
1393
|
for (const env of envs) {
|
|
@@ -2013,8 +2013,8 @@ async function runClaim(claimCodeArg, opts) {
|
|
|
2013
2013
|
if (res.merged) {
|
|
2014
2014
|
console.log(` Merged ${import_chalk7.default.bold(String(res.proxy_count ?? ""))} prox${res.proxy_count === 1 ? "y" : "ies"} into team ${import_chalk7.default.bold(res.team_id)}.`);
|
|
2015
2015
|
} else {
|
|
2016
|
-
const
|
|
2017
|
-
console.log(` It's now your team ${import_chalk7.default.bold(
|
|
2016
|
+
const label3 = res.team_name ? `\u201C${res.team_name}\u201D` : res.team_id;
|
|
2017
|
+
console.log(` It's now your team ${import_chalk7.default.bold(label3)}. Switch to it with ${import_chalk7.default.cyan(`apiblaze team ${res.team_id}`)}.`);
|
|
2018
2018
|
console.log(import_chalk7.default.dim(` (Tip: name it with --name, or --team <name> to merge into an existing team instead.)`));
|
|
2019
2019
|
}
|
|
2020
2020
|
console.log(`
|
|
@@ -3158,7 +3158,7 @@ Tenant ${tenant2}`));
|
|
|
3158
3158
|
pageSize: 12,
|
|
3159
3159
|
choices: [
|
|
3160
3160
|
{ name: `Login methods (${nLogin}) ${import_chalk26.default.dim("how your consumers sign in")}`, value: "login" },
|
|
3161
|
-
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk26.default.dim(`identity & key management (
|
|
3161
|
+
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk26.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
|
|
3162
3162
|
{ name: `Portal admins (${nEmails}) ${import_chalk26.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
|
|
3163
3163
|
new inquirer3.Separator(import_chalk26.default.dim(" Settings")),
|
|
3164
3164
|
{ name: `CORS: ${cors?.cors ? import_chalk26.default.cyan(JSON.stringify(cors.cors)) : import_chalk26.default.dim("(unset)")} ${import_chalk26.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
|
|
@@ -4224,9 +4224,9 @@ function printAll(proj2, cfg, json) {
|
|
|
4224
4224
|
console.log(import_chalk30.default.bold(`
|
|
4225
4225
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
4226
4226
|
`));
|
|
4227
|
-
for (const
|
|
4228
|
-
console.log(import_chalk30.default.bold(
|
|
4229
|
-
for (const s of SETTINGS.filter((x) => x.group ===
|
|
4227
|
+
for (const group2 of SETTING_GROUPS) {
|
|
4228
|
+
console.log(import_chalk30.default.bold(group2));
|
|
4229
|
+
for (const s of SETTINGS.filter((x) => x.group === group2)) {
|
|
4230
4230
|
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk30.default.dim(s.desc)}`);
|
|
4231
4231
|
}
|
|
4232
4232
|
console.log();
|
|
@@ -4335,14 +4335,14 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4335
4335
|
}
|
|
4336
4336
|
}
|
|
4337
4337
|
}
|
|
4338
|
-
async function settingsGroup(proj2, cfg,
|
|
4338
|
+
async function settingsGroup(proj2, cfg, group2) {
|
|
4339
4339
|
const { default: inquirer3 } = await import("inquirer");
|
|
4340
4340
|
for (; ; ) {
|
|
4341
|
-
const items = SETTINGS.filter((s2) => s2.group ===
|
|
4341
|
+
const items = SETTINGS.filter((s2) => s2.group === group2);
|
|
4342
4342
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
4343
4343
|
type: "list",
|
|
4344
4344
|
name: "pick",
|
|
4345
|
-
message:
|
|
4345
|
+
message: group2 + ":",
|
|
4346
4346
|
pageSize: 16,
|
|
4347
4347
|
choices: [
|
|
4348
4348
|
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk30.default.dim(s2.desc)}`, value: s2 })),
|
|
@@ -4754,9 +4754,227 @@ async function agentsMenu(proj2, opts) {
|
|
|
4754
4754
|
else await runMcp(proj2.projectName, proj2.apiVersion, {});
|
|
4755
4755
|
}
|
|
4756
4756
|
|
|
4757
|
-
// src/commands/
|
|
4757
|
+
// src/commands/group.ts
|
|
4758
4758
|
var import_chalk31 = __toESM(require("chalk"));
|
|
4759
4759
|
var import_ora15 = __toESM(require("ora"));
|
|
4760
|
+
init_auth();
|
|
4761
|
+
init_admin();
|
|
4762
|
+
init_types();
|
|
4763
|
+
var DASHBOARD_BASE6 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
4764
|
+
var IAM_BASE = process.env.APIBLAZE_IAM_BASE || "https://iam.apiblaze.com";
|
|
4765
|
+
async function resolveTransport(opts) {
|
|
4766
|
+
const producer = loadCredentials();
|
|
4767
|
+
if (producer) {
|
|
4768
|
+
const { teamId } = await resolveTeam(void 0);
|
|
4769
|
+
let tenant2 = (opts.tenant ?? "").trim().toLowerCase();
|
|
4770
|
+
if (!tenant2) {
|
|
4771
|
+
const out = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants`, summary: "List tenants" });
|
|
4772
|
+
const tenants = (out?.tenants ?? []).map((t) => String(t.tenant_name ?? t.name ?? t)).filter((t) => !t.startsWith("op"));
|
|
4773
|
+
if (tenants.length === 0) throw new Error("No tenants in your team yet. Create a proxy first, or pass --tenant.");
|
|
4774
|
+
if (tenants.length === 1) tenant2 = tenants[0];
|
|
4775
|
+
else {
|
|
4776
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
4777
|
+
({ tenant: tenant2 } = await inquirer3.prompt([{ type: "list", name: "tenant", message: "Which tenant?", choices: tenants }]));
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
const token = getAccessToken();
|
|
4781
|
+
return {
|
|
4782
|
+
tenant: tenant2,
|
|
4783
|
+
call: async (path8, method = "GET", body) => {
|
|
4784
|
+
const res = await fetch(`${DASHBOARD_BASE6}/api/cli/iam`, {
|
|
4785
|
+
method: "POST",
|
|
4786
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
4787
|
+
body: JSON.stringify({ tenant: tenant2, path: path8, method, body })
|
|
4788
|
+
});
|
|
4789
|
+
const data = await res.json().catch(() => ({}));
|
|
4790
|
+
if (!res.ok) throw new ApiError(res.status, data?.details ?? data?.error ?? res.statusText, data);
|
|
4791
|
+
return data;
|
|
4792
|
+
}
|
|
4793
|
+
};
|
|
4794
|
+
}
|
|
4795
|
+
const consumer2 = loadConsumer();
|
|
4796
|
+
if (consumer2) {
|
|
4797
|
+
const fresh = await validConsumerToken(consumer2) ?? consumer2;
|
|
4798
|
+
if (fresh !== consumer2) saveConsumer(fresh);
|
|
4799
|
+
if (opts.tenant && opts.tenant.toLowerCase() !== fresh.tenant) {
|
|
4800
|
+
throw new Error(`Your consumer login is for tenant "${fresh.tenant}". Log in to "${opts.tenant}" first (apiblaze consumer login --tenant ${opts.tenant}).`);
|
|
4801
|
+
}
|
|
4802
|
+
return {
|
|
4803
|
+
tenant: fresh.tenant,
|
|
4804
|
+
selfEmail: fresh.email,
|
|
4805
|
+
call: async (path8, method = "GET", body) => {
|
|
4806
|
+
const res = await fetch(`${IAM_BASE}${path8}`, {
|
|
4807
|
+
method,
|
|
4808
|
+
headers: {
|
|
4809
|
+
"Content-Type": "application/json",
|
|
4810
|
+
Authorization: `Bearer ${fresh.accessToken}`
|
|
4811
|
+
},
|
|
4812
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
4813
|
+
});
|
|
4814
|
+
if (res.status === 204) return {};
|
|
4815
|
+
const data = await res.json().catch(() => ({}));
|
|
4816
|
+
if (!res.ok) throw new ApiError(res.status, data?.error?.message ?? data?.details ?? data?.error ?? res.statusText, data);
|
|
4817
|
+
return data;
|
|
4818
|
+
}
|
|
4819
|
+
};
|
|
4820
|
+
}
|
|
4821
|
+
throw new Error("Not authenticated. Producers: `apiblaze login`. Tenant admins: `apiblaze consumer login`.");
|
|
4822
|
+
}
|
|
4823
|
+
async function listGroups(t) {
|
|
4824
|
+
const out = await t.call("/v1/admin/groups");
|
|
4825
|
+
return out?.groups ?? [];
|
|
4826
|
+
}
|
|
4827
|
+
async function groupByName(t, name) {
|
|
4828
|
+
const g = (await listGroups(t)).find((x) => x.name === name || x.id === name);
|
|
4829
|
+
if (!g) throw new Error(`Group "${name}" not found. Run \`apiblaze group list\`.`);
|
|
4830
|
+
return g;
|
|
4831
|
+
}
|
|
4832
|
+
async function listUsers(t) {
|
|
4833
|
+
const out = await t.call("/v1/admin/users");
|
|
4834
|
+
return out?.users ?? [];
|
|
4835
|
+
}
|
|
4836
|
+
async function userByRef(t, ref) {
|
|
4837
|
+
const users = await listUsers(t);
|
|
4838
|
+
const r = ref.toLowerCase();
|
|
4839
|
+
const u = users.find((x) => x.abz_sub === ref || (x.email ?? "").toLowerCase() === r || (x.display_name ?? "").toLowerCase() === r);
|
|
4840
|
+
if (!u) throw new Error(`User "${ref}" not found in tenant. Run \`apiblaze group users\` to see the directory (or provision them from Seen-in-traffic in the dashboard).`);
|
|
4841
|
+
return u;
|
|
4842
|
+
}
|
|
4843
|
+
var label2 = (u) => u.display_name || u.email || u.abz_sub;
|
|
4844
|
+
async function runGroupList(opts) {
|
|
4845
|
+
const t = await resolveTransport(opts);
|
|
4846
|
+
const groups = await listGroups(t);
|
|
4847
|
+
if (opts.json) return void console.log(JSON.stringify(groups));
|
|
4848
|
+
if (!groups.length) return void console.log(import_chalk31.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
|
|
4849
|
+
console.log(import_chalk31.default.dim(`Tenant ${t.tenant}:`));
|
|
4850
|
+
for (const g of groups) {
|
|
4851
|
+
const sealed = g.name === "apiblaze_admins" ? import_chalk31.default.dim(" (admins \u2014 managed by allowlist)") : "";
|
|
4852
|
+
console.log(` ${import_chalk31.default.bold(g.name)} ${import_chalk31.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
async function runGroupCreate(name, opts) {
|
|
4856
|
+
const t = await resolveTransport(opts);
|
|
4857
|
+
let adminSub;
|
|
4858
|
+
if (opts.admin) adminSub = (await userByRef(t, opts.admin)).abz_sub;
|
|
4859
|
+
else {
|
|
4860
|
+
const users = await listUsers(t);
|
|
4861
|
+
if (t.selfEmail) adminSub = users.find((u) => (u.email ?? "").toLowerCase() === t.selfEmail.toLowerCase())?.abz_sub;
|
|
4862
|
+
if (!adminSub) {
|
|
4863
|
+
if (!users.length) throw new Error("The tenant has no users yet, and a group needs an initial group-admin. Add a user first (widget/portal login or Seen-in-traffic), or pass --admin <user>.");
|
|
4864
|
+
if (users.length === 1) adminSub = users[0].abz_sub;
|
|
4865
|
+
else {
|
|
4866
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
4867
|
+
const { pick: pick2 } = await inquirer3.prompt([{
|
|
4868
|
+
type: "list",
|
|
4869
|
+
name: "pick",
|
|
4870
|
+
message: `Initial group-admin for "${name}"?`,
|
|
4871
|
+
choices: users.map((u) => ({ name: label2(u), value: u.abz_sub }))
|
|
4872
|
+
}]);
|
|
4873
|
+
adminSub = pick2;
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
}
|
|
4877
|
+
const spinner = (0, import_ora15.default)(`Creating group ${name}...`).start();
|
|
4878
|
+
try {
|
|
4879
|
+
await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
|
|
4880
|
+
spinner.succeed(`Group ${import_chalk31.default.bold(name)} created in ${t.tenant}.`);
|
|
4881
|
+
} catch (e) {
|
|
4882
|
+
spinner.fail("Create failed.");
|
|
4883
|
+
throw e;
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
async function runGroupDelete(name, opts) {
|
|
4887
|
+
const t = await resolveTransport(opts);
|
|
4888
|
+
const g = await groupByName(t, name);
|
|
4889
|
+
const spinner = (0, import_ora15.default)(`Deleting group ${name}...`).start();
|
|
4890
|
+
try {
|
|
4891
|
+
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
|
|
4892
|
+
spinner.succeed(`Group ${import_chalk31.default.bold(name)} deleted.`);
|
|
4893
|
+
} catch (e) {
|
|
4894
|
+
spinner.fail("Delete failed.");
|
|
4895
|
+
throw e;
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
async function runGroupMembers(name, opts) {
|
|
4899
|
+
const t = await resolveTransport(opts);
|
|
4900
|
+
const g = await groupByName(t, name);
|
|
4901
|
+
const detail = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`);
|
|
4902
|
+
const subs = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/subgroups`);
|
|
4903
|
+
if (opts.json) return void console.log(JSON.stringify({ ...detail, subgroups: subs?.subgroups ?? [] }));
|
|
4904
|
+
const members = detail?.members ?? [];
|
|
4905
|
+
console.log(import_chalk31.default.bold(`${g.name}`) + import_chalk31.default.dim(` (${t.tenant})`));
|
|
4906
|
+
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk31.default.cyan("group-admin") : import_chalk31.default.dim("member")}`);
|
|
4907
|
+
if (!members.length) console.log(import_chalk31.default.dim(" no members"));
|
|
4908
|
+
const sg = subs?.subgroups ?? [];
|
|
4909
|
+
if (sg.length) console.log(import_chalk31.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
|
|
4910
|
+
}
|
|
4911
|
+
async function runGroupAddUser(userRef, groupName, opts) {
|
|
4912
|
+
const t = await resolveTransport(opts);
|
|
4913
|
+
const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
|
|
4914
|
+
const role = opts.role === "admin" ? "admin" : "member";
|
|
4915
|
+
const spinner = (0, import_ora15.default)(`Adding ${label2(u)} to ${g.name}...`).start();
|
|
4916
|
+
try {
|
|
4917
|
+
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
|
|
4918
|
+
spinner.succeed(`${import_chalk31.default.bold(label2(u))} added to ${import_chalk31.default.bold(g.name)} as ${role}.`);
|
|
4919
|
+
} catch (e) {
|
|
4920
|
+
spinner.fail("Add failed.");
|
|
4921
|
+
throw e;
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
async function runGroupRemoveUser(userRef, groupName, opts) {
|
|
4925
|
+
const t = await resolveTransport(opts);
|
|
4926
|
+
const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
|
|
4927
|
+
const spinner = (0, import_ora15.default)(`Removing ${label2(u)} from ${g.name}...`).start();
|
|
4928
|
+
try {
|
|
4929
|
+
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
|
|
4930
|
+
spinner.succeed(`${import_chalk31.default.bold(label2(u))} removed from ${import_chalk31.default.bold(g.name)}.`);
|
|
4931
|
+
} catch (e) {
|
|
4932
|
+
spinner.fail("Remove failed.");
|
|
4933
|
+
throw e;
|
|
4934
|
+
}
|
|
4935
|
+
}
|
|
4936
|
+
async function runGroupAddGroup(childName, parentName, opts) {
|
|
4937
|
+
const t = await resolveTransport(opts);
|
|
4938
|
+
const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
|
|
4939
|
+
const spinner = (0, import_ora15.default)(`Nesting ${child.name} under ${parent.name}...`).start();
|
|
4940
|
+
try {
|
|
4941
|
+
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
|
|
4942
|
+
spinner.succeed(`${import_chalk31.default.bold(child.name)} is now a subgroup of ${import_chalk31.default.bold(parent.name)}.`);
|
|
4943
|
+
} catch (e) {
|
|
4944
|
+
spinner.fail("Nest failed.");
|
|
4945
|
+
throw e;
|
|
4946
|
+
}
|
|
4947
|
+
}
|
|
4948
|
+
async function runGroupRemoveGroup(childName, parentName, opts) {
|
|
4949
|
+
const t = await resolveTransport(opts);
|
|
4950
|
+
const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
|
|
4951
|
+
const spinner = (0, import_ora15.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
|
|
4952
|
+
try {
|
|
4953
|
+
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
|
|
4954
|
+
spinner.succeed(`${import_chalk31.default.bold(child.name)} un-nested from ${import_chalk31.default.bold(parent.name)}.`);
|
|
4955
|
+
} catch (e) {
|
|
4956
|
+
spinner.fail("Un-nest failed.");
|
|
4957
|
+
throw e;
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
async function runGroupUsers(opts) {
|
|
4961
|
+
const t = await resolveTransport(opts);
|
|
4962
|
+
const users = await listUsers(t);
|
|
4963
|
+
const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
|
|
4964
|
+
if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
|
|
4965
|
+
console.log(import_chalk31.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
|
|
4966
|
+
for (const u of users) console.log(` ${label2(u)} ${import_chalk31.default.dim(u.abz_sub)}`);
|
|
4967
|
+
if (!users.length) console.log(import_chalk31.default.dim(" no users yet"));
|
|
4968
|
+
const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
|
|
4969
|
+
if (fresh.length) {
|
|
4970
|
+
console.log(import_chalk31.default.dim("Seen in traffic (not yet users):"));
|
|
4971
|
+
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk31.default.dim(o.source_label ?? "")}`);
|
|
4972
|
+
}
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4975
|
+
// src/commands/key.ts
|
|
4976
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
4977
|
+
var import_ora16 = __toESM(require("ora"));
|
|
4760
4978
|
init_admin();
|
|
4761
4979
|
async function runApikeysMenu(opts) {
|
|
4762
4980
|
await runKeyList(opts);
|
|
@@ -4784,11 +5002,11 @@ async function runKeyList(opts) {
|
|
|
4784
5002
|
return;
|
|
4785
5003
|
}
|
|
4786
5004
|
if (!keys.length) {
|
|
4787
|
-
console.log(
|
|
5005
|
+
console.log(import_chalk32.default.yellow("No developer keys."));
|
|
4788
5006
|
return;
|
|
4789
5007
|
}
|
|
4790
5008
|
for (const k of keys) {
|
|
4791
|
-
console.log(` ${
|
|
5009
|
+
console.log(` ${import_chalk32.default.bold(k.key_id ?? k.id)} ${import_chalk32.default.dim(k.description ?? "")} ${import_chalk32.default.dim(k.expires_at ?? "no expiry")}`);
|
|
4792
5010
|
}
|
|
4793
5011
|
}
|
|
4794
5012
|
async function runKeyMint(opts) {
|
|
@@ -4796,7 +5014,7 @@ async function runKeyMint(opts) {
|
|
|
4796
5014
|
const body = { role: "consumer-admin" };
|
|
4797
5015
|
if (opts.desc) body.description = opts.desc;
|
|
4798
5016
|
if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
|
|
4799
|
-
const spinner = (0,
|
|
5017
|
+
const spinner = (0, import_ora16.default)("Minting key...").start();
|
|
4800
5018
|
try {
|
|
4801
5019
|
const out = await admin({
|
|
4802
5020
|
method: "POST",
|
|
@@ -4809,9 +5027,9 @@ async function runKeyMint(opts) {
|
|
|
4809
5027
|
console.log(JSON.stringify(out));
|
|
4810
5028
|
return;
|
|
4811
5029
|
}
|
|
4812
|
-
console.log(` ${
|
|
4813
|
-
console.log(` ${
|
|
4814
|
-
if (out?.expires_at) console.log(` ${
|
|
5030
|
+
console.log(` ${import_chalk32.default.bold("key_id")}: ${out?.key_id}`);
|
|
5031
|
+
console.log(` ${import_chalk32.default.bold("key")}: ${import_chalk32.default.green(out?.key)} ${import_chalk32.default.dim("(shown once \u2014 store it now)")}`);
|
|
5032
|
+
if (out?.expires_at) console.log(` ${import_chalk32.default.dim("expires:")} ${out.expires_at}`);
|
|
4815
5033
|
} catch (err) {
|
|
4816
5034
|
spinner.fail("Mint failed.");
|
|
4817
5035
|
throw err;
|
|
@@ -4819,7 +5037,7 @@ async function runKeyMint(opts) {
|
|
|
4819
5037
|
}
|
|
4820
5038
|
async function runKeyRevoke(keyId, opts) {
|
|
4821
5039
|
const { teamId } = await resolveTeam(opts.team);
|
|
4822
|
-
const spinner = (0,
|
|
5040
|
+
const spinner = (0, import_ora16.default)("Revoking key...").start();
|
|
4823
5041
|
try {
|
|
4824
5042
|
await admin({
|
|
4825
5043
|
method: "DELETE",
|
|
@@ -4837,8 +5055,8 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
4837
5055
|
var fs9 = __toESM(require("fs"));
|
|
4838
5056
|
var path6 = __toESM(require("path"));
|
|
4839
5057
|
var crypto2 = __toESM(require("crypto"));
|
|
4840
|
-
var
|
|
4841
|
-
var
|
|
5058
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
5059
|
+
var import_ora17 = __toESM(require("ora"));
|
|
4842
5060
|
var import_yaml = require("yaml");
|
|
4843
5061
|
init_auth();
|
|
4844
5062
|
init_anon_cred();
|
|
@@ -4848,7 +5066,7 @@ init_admin();
|
|
|
4848
5066
|
// src/commands/llm.ts
|
|
4849
5067
|
var fs8 = __toESM(require("fs"));
|
|
4850
5068
|
var path5 = __toESM(require("path"));
|
|
4851
|
-
var
|
|
5069
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
4852
5070
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
4853
5071
|
init_auth();
|
|
4854
5072
|
var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
|
|
@@ -4892,27 +5110,27 @@ async function runLlmSetKey(keyArg, opts) {
|
|
|
4892
5110
|
}
|
|
4893
5111
|
const existing = loadLlmConfig();
|
|
4894
5112
|
saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
|
|
4895
|
-
console.log(`${
|
|
4896
|
-
console.log(
|
|
4897
|
-
if (opts.model) console.log(
|
|
5113
|
+
console.log(`${import_chalk33.default.green("\u2713")} Saved ${import_chalk33.default.bold(provider)} key ${maskSecret(key)}`);
|
|
5114
|
+
console.log(import_chalk33.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
|
|
5115
|
+
if (opts.model) console.log(import_chalk33.default.gray(` Model: ${opts.model}`));
|
|
4898
5116
|
}
|
|
4899
5117
|
async function runLlmShow() {
|
|
4900
5118
|
const cfg = loadLlmConfig();
|
|
4901
5119
|
if (!cfg) {
|
|
4902
|
-
console.log(
|
|
5120
|
+
console.log(import_chalk33.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
|
|
4903
5121
|
return;
|
|
4904
5122
|
}
|
|
4905
|
-
console.log(`Provider: ${
|
|
5123
|
+
console.log(`Provider: ${import_chalk33.default.bold(cfg.provider)}`);
|
|
4906
5124
|
console.log(`Key: ${maskSecret(cfg.key)}`);
|
|
4907
5125
|
if (cfg.model) console.log(`Model: ${cfg.model}`);
|
|
4908
|
-
console.log(
|
|
5126
|
+
console.log(import_chalk33.default.gray(`Stored at ${LLM_PATH}`));
|
|
4909
5127
|
}
|
|
4910
5128
|
async function runLlmClearKey() {
|
|
4911
5129
|
try {
|
|
4912
5130
|
fs8.unlinkSync(LLM_PATH);
|
|
4913
|
-
console.log(`${
|
|
5131
|
+
console.log(`${import_chalk33.default.green("\u2713")} Removed local LLM key.`);
|
|
4914
5132
|
} catch {
|
|
4915
|
-
console.log(
|
|
5133
|
+
console.log(import_chalk33.default.gray("No LLM key was set."));
|
|
4916
5134
|
}
|
|
4917
5135
|
}
|
|
4918
5136
|
|
|
@@ -4920,9 +5138,9 @@ async function runLlmClearKey() {
|
|
|
4920
5138
|
init_trace();
|
|
4921
5139
|
init_types();
|
|
4922
5140
|
function fail4(message, hint) {
|
|
4923
|
-
console.error(
|
|
5141
|
+
console.error(import_chalk34.default.red(`
|
|
4924
5142
|
Error: ${message}`));
|
|
4925
|
-
if (hint) console.error(
|
|
5143
|
+
if (hint) console.error(import_chalk34.default.dim(hint));
|
|
4926
5144
|
process.exit(1);
|
|
4927
5145
|
}
|
|
4928
5146
|
function normalizeName2(raw) {
|
|
@@ -5070,7 +5288,7 @@ async function resolveTargetAuth(spec2, opts) {
|
|
|
5070
5288
|
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
5071
5289
|
);
|
|
5072
5290
|
}
|
|
5073
|
-
if (sawOAuth) console.log(
|
|
5291
|
+
if (sawOAuth) console.log(import_chalk34.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
|
|
5074
5292
|
return null;
|
|
5075
5293
|
}
|
|
5076
5294
|
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
@@ -5147,7 +5365,7 @@ async function provision(spec2, target, opts) {
|
|
|
5147
5365
|
if (!base && spec2.info && typeof spec2.info.title === "string") base = normalizeName2(spec2.info.title);
|
|
5148
5366
|
if (!base || base.length < 3) base = "apichat";
|
|
5149
5367
|
let name = opts.name ? base : `${base}${salt()}`;
|
|
5150
|
-
const spinner = (0,
|
|
5368
|
+
const spinner = (0, import_ora17.default)("Provisioning an api_key proxy...").start();
|
|
5151
5369
|
let result;
|
|
5152
5370
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
5153
5371
|
try {
|
|
@@ -5227,7 +5445,7 @@ async function provision(spec2, target, opts) {
|
|
|
5227
5445
|
return { projectId, version: version2, environment, dpKey, mcpHost, proxyUrl, anon };
|
|
5228
5446
|
}
|
|
5229
5447
|
async function writeTargetAuth(p, auth, secret) {
|
|
5230
|
-
const spinner = (0,
|
|
5448
|
+
const spinner = (0, import_ora17.default)("Storing target credentials (encrypted)...").start();
|
|
5231
5449
|
try {
|
|
5232
5450
|
await cpPost(
|
|
5233
5451
|
p.anon,
|
|
@@ -5249,7 +5467,7 @@ async function writeTargetAuth(p, auth, secret) {
|
|
|
5249
5467
|
}
|
|
5250
5468
|
}
|
|
5251
5469
|
async function uploadSpec(p, specText, opts) {
|
|
5252
|
-
const spinner = (0,
|
|
5470
|
+
const spinner = (0, import_ora17.default)("Uploading the spec...").start();
|
|
5253
5471
|
let out;
|
|
5254
5472
|
try {
|
|
5255
5473
|
out = await cpPost(
|
|
@@ -5264,7 +5482,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5264
5482
|
throw err;
|
|
5265
5483
|
}
|
|
5266
5484
|
if (out && out.reused === true) {
|
|
5267
|
-
console.log(
|
|
5485
|
+
console.log(import_chalk34.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
|
|
5268
5486
|
} else if (out && out.changed === true && out.previous_spec_hash) {
|
|
5269
5487
|
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
5270
5488
|
if (interactive) {
|
|
@@ -5272,12 +5490,12 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5272
5490
|
const { go } = await inquirer3.prompt([
|
|
5273
5491
|
{ type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
|
|
5274
5492
|
]);
|
|
5275
|
-
if (!go) console.log(
|
|
5493
|
+
if (!go) console.log(import_chalk34.default.dim(" Keeping the existing MCP catalogue."));
|
|
5276
5494
|
}
|
|
5277
5495
|
}
|
|
5278
5496
|
}
|
|
5279
5497
|
async function publishMcp(p, spec2) {
|
|
5280
|
-
const spinner = (0,
|
|
5498
|
+
const spinner = (0, import_ora17.default)("Publishing the MCP catalogue...").start();
|
|
5281
5499
|
try {
|
|
5282
5500
|
const url = `https://${p.mcpHost}/${p.version}/${p.environment}/mcp/generate`;
|
|
5283
5501
|
const res = await fetch(url, {
|
|
@@ -5313,14 +5531,14 @@ var revealAuth = false;
|
|
|
5313
5531
|
function renderToolEvents(events, dpKey) {
|
|
5314
5532
|
for (const e of events ?? []) {
|
|
5315
5533
|
const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
|
|
5316
|
-
const mark = ok ?
|
|
5317
|
-
console.log(` ${mark} ${
|
|
5534
|
+
const mark = ok ? import_chalk34.default.green("\u2713") : import_chalk34.default.red("\u2717");
|
|
5535
|
+
console.log(` ${mark} ${import_chalk34.default.cyan(e.name)} ${import_chalk34.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
|
|
5318
5536
|
if (isVerbose() && e.method && e.url) {
|
|
5319
|
-
console.log(
|
|
5537
|
+
console.log(import_chalk34.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
|
|
5320
5538
|
if (dpKey) {
|
|
5321
5539
|
const keyLine = ` -H 'X-API-Key: ${revealAuth ? dpKey : maskKey(dpKey)}'`;
|
|
5322
|
-
const hint = revealAuth ? "" :
|
|
5323
|
-
console.log(
|
|
5540
|
+
const hint = revealAuth ? "" : import_chalk34.default.yellow(" \u2190 /showauth will reveal this");
|
|
5541
|
+
console.log(import_chalk34.default.dim(keyLine) + hint);
|
|
5324
5542
|
}
|
|
5325
5543
|
}
|
|
5326
5544
|
}
|
|
@@ -5328,9 +5546,9 @@ function renderToolEvents(events, dpKey) {
|
|
|
5328
5546
|
function billingLine(billing) {
|
|
5329
5547
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
5330
5548
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
5331
|
-
let line =
|
|
5549
|
+
let line = import_chalk34.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk34.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
5332
5550
|
if (typeof billing.credits_remaining === "number") {
|
|
5333
|
-
line +=
|
|
5551
|
+
line += import_chalk34.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
5334
5552
|
}
|
|
5335
5553
|
return line;
|
|
5336
5554
|
}
|
|
@@ -5339,14 +5557,14 @@ function freeBudgetWarning(billing, anon) {
|
|
|
5339
5557
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
5340
5558
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
5341
5559
|
if (left > 8) return null;
|
|
5342
|
-
if (left <= 0) return
|
|
5343
|
-
return
|
|
5560
|
+
if (left <= 0) return import_chalk34.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
5561
|
+
return import_chalk34.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
5344
5562
|
}
|
|
5345
5563
|
function printAssistant(delta) {
|
|
5346
5564
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
5347
5565
|
const m = delta[i];
|
|
5348
5566
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
5349
|
-
console.log("\n" +
|
|
5567
|
+
console.log("\n" + import_chalk34.default.green("assistant \u203A ") + m.content + "\n");
|
|
5350
5568
|
return;
|
|
5351
5569
|
}
|
|
5352
5570
|
}
|
|
@@ -5356,7 +5574,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5356
5574
|
const llm2 = loadLlmConfig();
|
|
5357
5575
|
const turnId = crypto2.randomUUID();
|
|
5358
5576
|
for (let round = 0; round < CLIENT_ROUND_CAP; round++) {
|
|
5359
|
-
const spinner = (0,
|
|
5577
|
+
const spinner = (0, import_ora17.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
|
|
5360
5578
|
const body = {
|
|
5361
5579
|
turn_id: turnId,
|
|
5362
5580
|
messages,
|
|
@@ -5372,7 +5590,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5372
5590
|
});
|
|
5373
5591
|
} catch (err) {
|
|
5374
5592
|
spinner.fail("Network error.");
|
|
5375
|
-
console.log(
|
|
5593
|
+
console.log(import_chalk34.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
5376
5594
|
return;
|
|
5377
5595
|
}
|
|
5378
5596
|
let data = null;
|
|
@@ -5390,12 +5608,12 @@ async function replTurn(p, messages, userText) {
|
|
|
5390
5608
|
return;
|
|
5391
5609
|
}
|
|
5392
5610
|
if (res.status === 401) {
|
|
5393
|
-
console.log(
|
|
5611
|
+
console.log(import_chalk34.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
|
|
5394
5612
|
return;
|
|
5395
5613
|
}
|
|
5396
5614
|
if (!res.ok || !data) {
|
|
5397
5615
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
5398
|
-
console.log(
|
|
5616
|
+
console.log(import_chalk34.default.red(` Chat error: ${err}`));
|
|
5399
5617
|
return;
|
|
5400
5618
|
}
|
|
5401
5619
|
if (Array.isArray(data.delta)) {
|
|
@@ -5409,19 +5627,25 @@ async function replTurn(p, messages, userText) {
|
|
|
5409
5627
|
if (warn) console.log(warn);
|
|
5410
5628
|
if (!data.continue) return;
|
|
5411
5629
|
}
|
|
5412
|
-
console.log(
|
|
5630
|
+
console.log(import_chalk34.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
5413
5631
|
}
|
|
5414
5632
|
function renderUpsell(p, upsell) {
|
|
5415
5633
|
const loggedIn = !!loadCredentials();
|
|
5416
|
-
|
|
5634
|
+
if (upsell.reason === "CAPPED" && !loggedIn) {
|
|
5635
|
+
console.log("\n" + import_chalk34.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
|
|
5636
|
+
console.log(import_chalk34.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
|
|
5637
|
+
console.log();
|
|
5638
|
+
return;
|
|
5639
|
+
}
|
|
5640
|
+
console.log("\n" + import_chalk34.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
5417
5641
|
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
5418
5642
|
if (!loggedIn) {
|
|
5419
|
-
console.log(
|
|
5643
|
+
console.log(import_chalk34.default.dim(" Options: `/login` for free login credit and higher limits, or `apiblaze llm set-key` to bring your own model key."));
|
|
5420
5644
|
} else {
|
|
5421
|
-
console.log(
|
|
5645
|
+
console.log(import_chalk34.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
|
|
5422
5646
|
}
|
|
5423
5647
|
} else if (upsell.reason === "INFLIGHT") {
|
|
5424
|
-
console.log(
|
|
5648
|
+
console.log(import_chalk34.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
5425
5649
|
}
|
|
5426
5650
|
console.log();
|
|
5427
5651
|
}
|
|
@@ -5490,9 +5714,9 @@ async function openServerProxy(project) {
|
|
|
5490
5714
|
const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
|
|
5491
5715
|
let dpKey = prior?.dpKey;
|
|
5492
5716
|
if (!dpKey) {
|
|
5493
|
-
console.log(
|
|
5717
|
+
console.log(import_chalk34.default.dim(` Minting an API key for tenant ${import_chalk34.default.bold(tenant2)} to query project ${import_chalk34.default.bold(project.projectName)}\u2026`));
|
|
5494
5718
|
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
5495
|
-
console.log(` ${
|
|
5719
|
+
console.log(` ${import_chalk34.default.green("\u2714")} API key: ${import_chalk34.default.dim(maskKey(dpKey))}`);
|
|
5496
5720
|
}
|
|
5497
5721
|
const p = {
|
|
5498
5722
|
projectId: project.projectId,
|
|
@@ -5503,7 +5727,7 @@ async function openServerProxy(project) {
|
|
|
5503
5727
|
proxyUrl: `https://${project.projectId}.abz.run/${version2}/${environment}`,
|
|
5504
5728
|
anon: false
|
|
5505
5729
|
};
|
|
5506
|
-
const spinner = (0,
|
|
5730
|
+
const spinner = (0, import_ora17.default)("Preparing the chat\u2026").start();
|
|
5507
5731
|
try {
|
|
5508
5732
|
const raw = await admin({
|
|
5509
5733
|
method: "GET",
|
|
@@ -5515,7 +5739,7 @@ async function openServerProxy(project) {
|
|
|
5515
5739
|
if (spec2 && (spec2.paths || spec2.openapi)) {
|
|
5516
5740
|
await publishMcp(p, spec2);
|
|
5517
5741
|
} else {
|
|
5518
|
-
console.log(
|
|
5742
|
+
console.log(import_chalk34.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
|
|
5519
5743
|
}
|
|
5520
5744
|
} catch (err) {
|
|
5521
5745
|
spinner.fail("Could not open the proxy.");
|
|
@@ -5568,7 +5792,7 @@ async function noArgsMenu(opts) {
|
|
|
5568
5792
|
const me = loadCredentials()?.apiblazeUserId;
|
|
5569
5793
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
5570
5794
|
const choices = saved.map((a) => ({
|
|
5571
|
-
name: `Chat with ${
|
|
5795
|
+
name: `Chat with ${import_chalk34.default.bold(a.name)} ${import_chalk34.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
|
|
5572
5796
|
value: { type: "existing", a }
|
|
5573
5797
|
}));
|
|
5574
5798
|
const creds = loadCredentials();
|
|
@@ -5578,14 +5802,14 @@ async function noArgsMenu(opts) {
|
|
|
5578
5802
|
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
5579
5803
|
for (const pr of proxies) {
|
|
5580
5804
|
choices.push({
|
|
5581
|
-
name: `Chat with ${
|
|
5805
|
+
name: `Chat with ${import_chalk34.default.bold(pr.projectName)} ${import_chalk34.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
5582
5806
|
value: { type: "server", project: pr }
|
|
5583
5807
|
});
|
|
5584
5808
|
}
|
|
5585
5809
|
} catch {
|
|
5586
5810
|
}
|
|
5587
5811
|
}
|
|
5588
|
-
choices.push({ name:
|
|
5812
|
+
choices.push({ name: import_chalk34.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
5589
5813
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
5590
5814
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
5591
5815
|
]);
|
|
@@ -5657,31 +5881,31 @@ async function noArgsMenu(opts) {
|
|
|
5657
5881
|
async function runRepl(p, initialMessages) {
|
|
5658
5882
|
const { default: inquirer3 } = await import("inquirer");
|
|
5659
5883
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
5660
|
-
console.log("\n" +
|
|
5661
|
-
if (messages.length) console.log(
|
|
5884
|
+
console.log("\n" + import_chalk34.default.cyan.bold("Chat with your API") + import_chalk34.default.dim(` \xB7 ${p.mcpHost}`));
|
|
5885
|
+
if (messages.length) console.log(import_chalk34.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
5662
5886
|
const llm2 = loadLlmConfig();
|
|
5663
5887
|
console.log(
|
|
5664
|
-
|
|
5888
|
+
import_chalk34.default.dim(
|
|
5665
5889
|
llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for credit \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
|
|
5666
5890
|
)
|
|
5667
5891
|
);
|
|
5668
5892
|
for (; ; ) {
|
|
5669
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
5893
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk34.default.green("you \u203A") }]);
|
|
5670
5894
|
const text = (input ?? "").trim();
|
|
5671
5895
|
if (!text) continue;
|
|
5672
5896
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
5673
5897
|
if (text === "/login") {
|
|
5674
5898
|
try {
|
|
5675
5899
|
await runLogin();
|
|
5676
|
-
console.log(
|
|
5900
|
+
console.log(import_chalk34.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
5677
5901
|
} catch (err) {
|
|
5678
|
-
console.log(
|
|
5902
|
+
console.log(import_chalk34.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5679
5903
|
}
|
|
5680
5904
|
continue;
|
|
5681
5905
|
}
|
|
5682
5906
|
if (text === "/claim") {
|
|
5683
5907
|
if (!loadCredentials()) {
|
|
5684
|
-
console.log(
|
|
5908
|
+
console.log(import_chalk34.default.yellow(" Log in first: `/login`, then `/claim`."));
|
|
5685
5909
|
continue;
|
|
5686
5910
|
}
|
|
5687
5911
|
try {
|
|
@@ -5690,30 +5914,30 @@ async function runRepl(p, initialMessages) {
|
|
|
5690
5914
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
5691
5915
|
p.anon = false;
|
|
5692
5916
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
5693
|
-
console.log(
|
|
5917
|
+
console.log(import_chalk34.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
5694
5918
|
}
|
|
5695
5919
|
} catch (err) {
|
|
5696
|
-
console.log(
|
|
5920
|
+
console.log(import_chalk34.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5697
5921
|
}
|
|
5698
5922
|
continue;
|
|
5699
5923
|
}
|
|
5700
5924
|
if (text === "/showauth") {
|
|
5701
5925
|
revealAuth = !revealAuth;
|
|
5702
|
-
console.log(
|
|
5926
|
+
console.log(import_chalk34.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."));
|
|
5703
5927
|
continue;
|
|
5704
5928
|
}
|
|
5705
5929
|
if (text.startsWith("/")) {
|
|
5706
|
-
console.log(
|
|
5930
|
+
console.log(import_chalk34.default.dim(" Commands: /login /claim /showauth /exit"));
|
|
5707
5931
|
continue;
|
|
5708
5932
|
}
|
|
5709
5933
|
await replTurn(p, messages, text);
|
|
5710
5934
|
saveTranscript(p, messages);
|
|
5711
5935
|
}
|
|
5712
|
-
console.log(
|
|
5936
|
+
console.log(import_chalk34.default.dim("\nBye."));
|
|
5713
5937
|
}
|
|
5714
5938
|
async function runApichat(opts) {
|
|
5715
5939
|
setVerbose(opts.verbose !== false);
|
|
5716
|
-
console.log(
|
|
5940
|
+
console.log(import_chalk34.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
5717
5941
|
if (!opts.openapispec && !opts.target) {
|
|
5718
5942
|
if (!process.stdin.isTTY) {
|
|
5719
5943
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -5726,7 +5950,7 @@ async function runApichat(opts) {
|
|
|
5726
5950
|
}
|
|
5727
5951
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
5728
5952
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
5729
|
-
console.log(` ${
|
|
5953
|
+
console.log(` ${import_chalk34.default.dim("Target:")} ${import_chalk34.default.bold(target)}`);
|
|
5730
5954
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
5731
5955
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
5732
5956
|
fail4(
|
|
@@ -5735,7 +5959,7 @@ async function runApichat(opts) {
|
|
|
5735
5959
|
);
|
|
5736
5960
|
}
|
|
5737
5961
|
const p = await provision(spec2, target, opts);
|
|
5738
|
-
console.log(` ${
|
|
5962
|
+
console.log(` ${import_chalk34.default.dim("Proxy: ")} ${import_chalk34.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
5739
5963
|
upsertApichat({
|
|
5740
5964
|
name: p.projectId,
|
|
5741
5965
|
target,
|
|
@@ -5754,23 +5978,23 @@ async function runApichat(opts) {
|
|
|
5754
5978
|
const secret = await captureTargetSecret(auth, opts);
|
|
5755
5979
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
5756
5980
|
} else {
|
|
5757
|
-
console.log(
|
|
5981
|
+
console.log(import_chalk34.default.dim(" Target auth: none required."));
|
|
5758
5982
|
}
|
|
5759
5983
|
const specText = JSON.stringify(spec2);
|
|
5760
5984
|
await uploadSpec(p, specText, opts);
|
|
5761
5985
|
const mcpUrl = await publishMcp(p, spec2);
|
|
5762
5986
|
console.log();
|
|
5763
|
-
if (p.proxyUrl) console.log(` ${
|
|
5764
|
-
if (mcpUrl) console.log(` ${
|
|
5987
|
+
if (p.proxyUrl) console.log(` ${import_chalk34.default.green("\u2713")} proxy ${import_chalk34.default.bold(p.proxyUrl)}`);
|
|
5988
|
+
if (mcpUrl) console.log(` ${import_chalk34.default.green("\u2713")} mcp ${import_chalk34.default.bold(mcpUrl)}`);
|
|
5765
5989
|
if (p.anon) {
|
|
5766
|
-
console.log(
|
|
5990
|
+
console.log(import_chalk34.default.dim("\n Anonymous workspace \u2014 /claim inside the chat (after /login) to keep it beyond 30 days."));
|
|
5767
5991
|
}
|
|
5768
5992
|
await runRepl(p);
|
|
5769
5993
|
}
|
|
5770
5994
|
|
|
5771
5995
|
// src/commands/consumer.ts
|
|
5772
|
-
var
|
|
5773
|
-
var
|
|
5996
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
5997
|
+
var import_ora18 = __toESM(require("ora"));
|
|
5774
5998
|
init_admin();
|
|
5775
5999
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
5776
6000
|
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
@@ -5790,7 +6014,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
5790
6014
|
function requireConsumer() {
|
|
5791
6015
|
const c = loadConsumer();
|
|
5792
6016
|
if (!c) {
|
|
5793
|
-
console.error(
|
|
6017
|
+
console.error(import_chalk35.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
5794
6018
|
process.exit(1);
|
|
5795
6019
|
}
|
|
5796
6020
|
return c;
|
|
@@ -5801,7 +6025,7 @@ async function runConsumerLogin(opts) {
|
|
|
5801
6025
|
let clientId = opts.client;
|
|
5802
6026
|
if (clientId) {
|
|
5803
6027
|
if (!tenant2) {
|
|
5804
|
-
console.error(
|
|
6028
|
+
console.error(import_chalk35.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
5805
6029
|
process.exit(1);
|
|
5806
6030
|
}
|
|
5807
6031
|
} else {
|
|
@@ -5813,25 +6037,25 @@ async function runConsumerLogin(opts) {
|
|
|
5813
6037
|
if (!picked) process.exit(1);
|
|
5814
6038
|
tenant2 = picked;
|
|
5815
6039
|
}
|
|
5816
|
-
const s2 = (0,
|
|
6040
|
+
const s2 = (0, import_ora18.default)("Finding the login app...").start();
|
|
5817
6041
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
5818
6042
|
s2.stop();
|
|
5819
6043
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
5820
6044
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
5821
6045
|
if (!pick2) {
|
|
5822
|
-
console.error(
|
|
6046
|
+
console.error(import_chalk35.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
5823
6047
|
process.exit(1);
|
|
5824
6048
|
}
|
|
5825
6049
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
5826
6050
|
}
|
|
5827
6051
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
5828
|
-
console.log(`${
|
|
6052
|
+
console.log(`${import_chalk35.default.cyan("\u2192")} Logging in to ${import_chalk35.default.bold(tenant2)} as a consumer...`);
|
|
5829
6053
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
5830
6054
|
console.log(`
|
|
5831
|
-
Open: ${
|
|
5832
|
-
console.log(` Code: ${
|
|
6055
|
+
Open: ${import_chalk35.default.underline(verificationUri)}`);
|
|
6056
|
+
console.log(` Code: ${import_chalk35.default.bold(userCode)}
|
|
5833
6057
|
`);
|
|
5834
|
-
console.log(
|
|
6058
|
+
console.log(import_chalk35.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
5835
6059
|
}, portalResource);
|
|
5836
6060
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
5837
6061
|
const creds = {
|
|
@@ -5846,7 +6070,7 @@ async function runConsumerLogin(opts) {
|
|
|
5846
6070
|
obtainedAt: Date.now()
|
|
5847
6071
|
};
|
|
5848
6072
|
saveConsumer(creds);
|
|
5849
|
-
console.log(
|
|
6073
|
+
console.log(import_chalk35.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
5850
6074
|
}
|
|
5851
6075
|
async function runConsumerTokens(opts) {
|
|
5852
6076
|
const creds = requireConsumer();
|
|
@@ -5859,29 +6083,29 @@ async function runConsumerTokens(opts) {
|
|
|
5859
6083
|
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));
|
|
5860
6084
|
return;
|
|
5861
6085
|
}
|
|
5862
|
-
console.log(`${
|
|
6086
|
+
console.log(`${import_chalk35.default.cyan("Consumer")} ${import_chalk35.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk35.default.bold(fresh.tenant)}
|
|
5863
6087
|
`);
|
|
5864
|
-
console.log(`${
|
|
6088
|
+
console.log(`${import_chalk35.default.bold("access_token")} ${import_chalk35.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
5865
6089
|
${fresh.accessToken}
|
|
5866
6090
|
`);
|
|
5867
|
-
if (fresh.idToken) console.log(`${
|
|
6091
|
+
if (fresh.idToken) console.log(`${import_chalk35.default.bold("id_token")} ${import_chalk35.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
5868
6092
|
${fresh.idToken}
|
|
5869
6093
|
`);
|
|
5870
|
-
if (fresh.refreshToken) console.log(`${
|
|
6094
|
+
if (fresh.refreshToken) console.log(`${import_chalk35.default.bold("refresh_token")}
|
|
5871
6095
|
${fresh.refreshToken}
|
|
5872
6096
|
`);
|
|
5873
|
-
console.log(
|
|
6097
|
+
console.log(import_chalk35.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
5874
6098
|
}
|
|
5875
6099
|
async function runConsumerApikeys(opts) {
|
|
5876
6100
|
const creds = requireConsumer();
|
|
5877
6101
|
const { default: inquirer3 } = await import("inquirer");
|
|
5878
|
-
const spinner = (0,
|
|
6102
|
+
const spinner = (0, import_ora18.default)("Loading your API keys...").start();
|
|
5879
6103
|
const list = await consumerFetch(creds, "/apikeys");
|
|
5880
6104
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
5881
6105
|
spinner.stop();
|
|
5882
6106
|
if (list.status >= 400) {
|
|
5883
|
-
console.error(
|
|
5884
|
-
if (list.status === 401) console.error(
|
|
6107
|
+
console.error(import_chalk35.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
6108
|
+
if (list.status === 401) console.error(import_chalk35.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
5885
6109
|
process.exit(1);
|
|
5886
6110
|
}
|
|
5887
6111
|
const keys = list.data?.keys ?? [];
|
|
@@ -5889,16 +6113,16 @@ async function runConsumerApikeys(opts) {
|
|
|
5889
6113
|
if (opts.json) {
|
|
5890
6114
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
5891
6115
|
} else if (!keys.length) {
|
|
5892
|
-
console.log(
|
|
6116
|
+
console.log(import_chalk35.default.yellow("No API keys yet."));
|
|
5893
6117
|
} else {
|
|
5894
6118
|
for (const k of keys) {
|
|
5895
6119
|
const clear = revealMap[k.environment]?.key;
|
|
5896
|
-
const shown = clear ?
|
|
5897
|
-
const exp = k.expires_at ?
|
|
5898
|
-
console.log(` ${
|
|
6120
|
+
const shown = clear ? import_chalk35.default.green(clear) : import_chalk35.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
6121
|
+
const exp = k.expires_at ? import_chalk35.default.dim(`exp ${k.expires_at}`) : import_chalk35.default.dim("no expiry");
|
|
6122
|
+
console.log(` ${import_chalk35.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk35.default.dim(k.description ?? "")}`);
|
|
5899
6123
|
}
|
|
5900
6124
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
5901
|
-
console.log(
|
|
6125
|
+
console.log(import_chalk35.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
5902
6126
|
}
|
|
5903
6127
|
}
|
|
5904
6128
|
if (opts.json) return;
|
|
@@ -5912,7 +6136,7 @@ async function runConsumerApikeys(opts) {
|
|
|
5912
6136
|
const body = { environment: answers.environment };
|
|
5913
6137
|
if (answers.description) body.description = answers.description;
|
|
5914
6138
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
5915
|
-
const s2 = (0,
|
|
6139
|
+
const s2 = (0, import_ora18.default)("Creating key...").start();
|
|
5916
6140
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
5917
6141
|
if (created.status >= 400) {
|
|
5918
6142
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -5920,13 +6144,13 @@ async function runConsumerApikeys(opts) {
|
|
|
5920
6144
|
}
|
|
5921
6145
|
s2.succeed("Key created.");
|
|
5922
6146
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
5923
|
-
if (key) console.log(` ${
|
|
5924
|
-
else console.log(
|
|
6147
|
+
if (key) console.log(` ${import_chalk35.default.green(key)} ${import_chalk35.default.dim("(shown once \u2014 store it now)")}`);
|
|
6148
|
+
else console.log(import_chalk35.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
5925
6149
|
}
|
|
5926
6150
|
|
|
5927
6151
|
// src/commands/sidecar.ts
|
|
5928
|
-
var
|
|
5929
|
-
var
|
|
6152
|
+
var import_chalk36 = __toESM(require("chalk"));
|
|
6153
|
+
var import_ora19 = __toESM(require("ora"));
|
|
5930
6154
|
var fs10 = __toESM(require("fs"));
|
|
5931
6155
|
var path7 = __toESM(require("path"));
|
|
5932
6156
|
init_admin();
|
|
@@ -5967,18 +6191,18 @@ function upsertEnvLocal(root, token) {
|
|
|
5967
6191
|
}
|
|
5968
6192
|
function installSidecarPackage(root) {
|
|
5969
6193
|
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
5970
|
-
console.log(` ${
|
|
6194
|
+
console.log(` ${import_chalk36.default.green("\u2713")} apiblaze package already installed`);
|
|
5971
6195
|
return;
|
|
5972
6196
|
}
|
|
5973
6197
|
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
5974
6198
|
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" };
|
|
5975
|
-
const spinner = (0,
|
|
6199
|
+
const spinner = (0, import_ora19.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
5976
6200
|
try {
|
|
5977
6201
|
const { execSync } = require("child_process");
|
|
5978
6202
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
5979
6203
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
5980
6204
|
} catch {
|
|
5981
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
6205
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk36.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk36.default.cyan("npm run dev")}.`);
|
|
5982
6206
|
}
|
|
5983
6207
|
}
|
|
5984
6208
|
function readEnvKey(root) {
|
|
@@ -6117,7 +6341,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6117
6341
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
6118
6342
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
6119
6343
|
if (opts.newSession) clearAnonCred2();
|
|
6120
|
-
const spinner = (0,
|
|
6344
|
+
const spinner = (0, import_ora19.default)("Setting up a sidecar (no login needed)...").start();
|
|
6121
6345
|
let out;
|
|
6122
6346
|
try {
|
|
6123
6347
|
out = await sidecarInitAnonymous2();
|
|
@@ -6129,29 +6353,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6129
6353
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
6130
6354
|
const envState = upsertEnvLocal(root, out.token);
|
|
6131
6355
|
ensureGitignored(root);
|
|
6132
|
-
console.log(` ${
|
|
6133
|
-
console.log(` ${
|
|
6356
|
+
console.log(` ${import_chalk36.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6357
|
+
console.log(` ${import_chalk36.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6134
6358
|
installSidecarPackage(root);
|
|
6135
6359
|
let inspectorPath = null;
|
|
6136
6360
|
if (!opts.noInspector) {
|
|
6137
6361
|
inspectorPath = generateInspector(root, router);
|
|
6138
|
-
if (inspectorPath) console.log(` ${
|
|
6362
|
+
if (inspectorPath) console.log(` ${import_chalk36.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6139
6363
|
}
|
|
6140
6364
|
console.log("");
|
|
6141
|
-
console.log(
|
|
6142
|
-
console.log(` 1. ${
|
|
6365
|
+
console.log(import_chalk36.default.bold("Done (no account needed). What happens next:"));
|
|
6366
|
+
console.log(` 1. ${import_chalk36.default.cyan("npm run dev")} and use your app.`);
|
|
6143
6367
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
6144
|
-
console.log(` ${
|
|
6368
|
+
console.log(` ${import_chalk36.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
6145
6369
|
console.log("");
|
|
6146
|
-
console.log(
|
|
6147
|
-
console.log(` ${
|
|
6148
|
-
console.log(
|
|
6370
|
+
console.log(import_chalk36.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
6371
|
+
console.log(` ${import_chalk36.default.cyan("apiblaze login")} then ${import_chalk36.default.cyan("apiblaze claim")} ${import_chalk36.default.dim("(no code needed here)")}`);
|
|
6372
|
+
console.log(import_chalk36.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
6149
6373
|
}
|
|
6150
6374
|
async function runSidecar(opts) {
|
|
6151
6375
|
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
6152
6376
|
const detected = detectNextProject(root);
|
|
6153
6377
|
if (!detected.found) {
|
|
6154
|
-
console.log(
|
|
6378
|
+
console.log(import_chalk36.default.yellow(`No Next.js project detected in ${root}.`));
|
|
6155
6379
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
6156
6380
|
return;
|
|
6157
6381
|
}
|
|
@@ -6162,10 +6386,10 @@ async function runSidecar(opts) {
|
|
|
6162
6386
|
if (!loadCredentials()) {
|
|
6163
6387
|
upsertEnvLocal(root, readEnvKey(root));
|
|
6164
6388
|
ensureGitignored(root);
|
|
6165
|
-
console.log(` ${
|
|
6166
|
-
console.log(` ${
|
|
6389
|
+
console.log(` ${import_chalk36.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
6390
|
+
console.log(` ${import_chalk36.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6167
6391
|
installSidecarPackage(root);
|
|
6168
|
-
console.log(
|
|
6392
|
+
console.log(import_chalk36.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
6169
6393
|
return;
|
|
6170
6394
|
}
|
|
6171
6395
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -6174,7 +6398,7 @@ async function runSidecar(opts) {
|
|
|
6174
6398
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
6175
6399
|
let token = existingKey ?? "";
|
|
6176
6400
|
if (mustMint) {
|
|
6177
|
-
const spinner = (0,
|
|
6401
|
+
const spinner = (0, import_ora19.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
|
|
6178
6402
|
try {
|
|
6179
6403
|
const out = await admin({
|
|
6180
6404
|
method: "POST",
|
|
@@ -6188,39 +6412,39 @@ async function runSidecar(opts) {
|
|
|
6188
6412
|
throw err;
|
|
6189
6413
|
}
|
|
6190
6414
|
} else {
|
|
6191
|
-
console.log(
|
|
6415
|
+
console.log(import_chalk36.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
6192
6416
|
}
|
|
6193
6417
|
const envState = upsertEnvLocal(root, token);
|
|
6194
6418
|
ensureGitignored(root);
|
|
6195
|
-
console.log(` ${
|
|
6419
|
+
console.log(` ${import_chalk36.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6196
6420
|
const wireState = wireInstrumentation(root);
|
|
6197
|
-
console.log(` ${
|
|
6421
|
+
console.log(` ${import_chalk36.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
6198
6422
|
installSidecarPackage(root);
|
|
6199
6423
|
let inspectorPath = null;
|
|
6200
6424
|
if (!opts.noInspector) {
|
|
6201
6425
|
inspectorPath = generateInspector(root, detected.router);
|
|
6202
|
-
if (inspectorPath) console.log(` ${
|
|
6426
|
+
if (inspectorPath) console.log(` ${import_chalk36.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6203
6427
|
}
|
|
6204
6428
|
console.log("");
|
|
6205
|
-
console.log(
|
|
6206
|
-
console.log(` 1. ${
|
|
6207
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
6208
|
-
console.log(` 3. Approve the ones to route: ${
|
|
6429
|
+
console.log(import_chalk36.default.bold("Done. What happens next:"));
|
|
6430
|
+
console.log(` 1. ${import_chalk36.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
6431
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk36.default.bold("candidates")} \u2014 list them: ${import_chalk36.default.cyan("apiblaze sidecar")}`);
|
|
6432
|
+
console.log(` 3. Approve the ones to route: ${import_chalk36.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
6209
6433
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
6210
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
6211
|
-
if (switchingTeam) console.log(
|
|
6434
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk36.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
|
|
6435
|
+
if (switchingTeam) console.log(import_chalk36.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
6212
6436
|
console.log("");
|
|
6213
|
-
console.log(
|
|
6214
|
-
console.log(
|
|
6215
|
-
console.log(
|
|
6437
|
+
console.log(import_chalk36.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
6438
|
+
console.log(import_chalk36.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
6439
|
+
console.log(import_chalk36.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
6216
6440
|
console.log("");
|
|
6217
|
-
console.log(
|
|
6218
|
-
console.log(
|
|
6441
|
+
console.log(import_chalk36.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
6442
|
+
console.log(import_chalk36.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
6219
6443
|
}
|
|
6220
6444
|
|
|
6221
6445
|
// src/commands/origins.ts
|
|
6222
|
-
var
|
|
6223
|
-
var
|
|
6446
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
6447
|
+
var import_ora20 = __toESM(require("ora"));
|
|
6224
6448
|
init_admin();
|
|
6225
6449
|
init_auth();
|
|
6226
6450
|
init_anon_cred();
|
|
@@ -6229,7 +6453,7 @@ async function runOriginsList(opts) {
|
|
|
6229
6453
|
if (!loadCredentials()) {
|
|
6230
6454
|
const cred = loadAnonCred();
|
|
6231
6455
|
if (!cred) {
|
|
6232
|
-
console.log(
|
|
6456
|
+
console.log(import_chalk37.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
6233
6457
|
return;
|
|
6234
6458
|
}
|
|
6235
6459
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -6247,30 +6471,30 @@ async function runOriginsList(opts) {
|
|
|
6247
6471
|
}
|
|
6248
6472
|
const routed = out.routed ?? [];
|
|
6249
6473
|
const candidates = out.candidates ?? [];
|
|
6250
|
-
console.log(
|
|
6474
|
+
console.log(import_chalk37.default.bold(`
|
|
6251
6475
|
Routed through APIblaze (${routed.length})`));
|
|
6252
|
-
if (!routed.length) console.log(
|
|
6253
|
-
for (const r of routed) console.log(` ${
|
|
6254
|
-
console.log(
|
|
6476
|
+
if (!routed.length) console.log(import_chalk37.default.dim(" none yet"));
|
|
6477
|
+
for (const r of routed) console.log(` ${import_chalk37.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk37.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
6478
|
+
console.log(import_chalk37.default.bold(`
|
|
6255
6479
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
6256
|
-
if (!candidates.length) console.log(
|
|
6480
|
+
if (!candidates.length) console.log(import_chalk37.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
6257
6481
|
for (const c of candidates) {
|
|
6258
|
-
console.log(` ${
|
|
6482
|
+
console.log(` ${import_chalk37.default.yellow("\u25CB")} ${c.origin} ${import_chalk37.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
6259
6483
|
}
|
|
6260
6484
|
if (candidates.length) {
|
|
6261
|
-
console.log(
|
|
6485
|
+
console.log(import_chalk37.default.dim(`
|
|
6262
6486
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
6263
|
-
console.log(
|
|
6487
|
+
console.log(import_chalk37.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
6264
6488
|
}
|
|
6265
6489
|
}
|
|
6266
6490
|
async function runOriginsApprove(origin, opts) {
|
|
6267
6491
|
if (!loadCredentials()) {
|
|
6268
6492
|
const cred = loadAnonCred();
|
|
6269
6493
|
if (!cred) {
|
|
6270
|
-
console.error(
|
|
6494
|
+
console.error(import_chalk37.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
6271
6495
|
process.exit(1);
|
|
6272
6496
|
}
|
|
6273
|
-
const spinner2 = (0,
|
|
6497
|
+
const spinner2 = (0, import_ora20.default)(`Approving ${origin} (anonymous)...`).start();
|
|
6274
6498
|
try {
|
|
6275
6499
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
6276
6500
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -6281,7 +6505,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
6281
6505
|
return;
|
|
6282
6506
|
}
|
|
6283
6507
|
const { teamId } = await resolveTeam(opts.team);
|
|
6284
|
-
const spinner = (0,
|
|
6508
|
+
const spinner = (0, import_ora20.default)(`Approving ${origin}...`).start();
|
|
6285
6509
|
try {
|
|
6286
6510
|
const out = await admin({
|
|
6287
6511
|
method: "POST",
|
|
@@ -6298,7 +6522,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
6298
6522
|
}
|
|
6299
6523
|
async function runOriginsDeny(origin, opts) {
|
|
6300
6524
|
const { teamId } = await resolveTeam(opts.team);
|
|
6301
|
-
const spinner = (0,
|
|
6525
|
+
const spinner = (0, import_ora20.default)(`Dismissing ${origin}...`).start();
|
|
6302
6526
|
try {
|
|
6303
6527
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
6304
6528
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -6309,7 +6533,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
6309
6533
|
}
|
|
6310
6534
|
async function runOriginsRemove(origin, opts) {
|
|
6311
6535
|
const { teamId } = await resolveTeam(opts.team);
|
|
6312
|
-
const spinner = (0,
|
|
6536
|
+
const spinner = (0, import_ora20.default)(`Removing the proxy for ${origin}...`).start();
|
|
6313
6537
|
try {
|
|
6314
6538
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
6315
6539
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -6320,19 +6544,19 @@ async function runOriginsRemove(origin, opts) {
|
|
|
6320
6544
|
}
|
|
6321
6545
|
|
|
6322
6546
|
// src/commands/op.ts
|
|
6323
|
-
var
|
|
6547
|
+
var import_chalk38 = __toESM(require("chalk"));
|
|
6324
6548
|
init_auth();
|
|
6325
6549
|
init_trace();
|
|
6326
6550
|
init_types();
|
|
6327
6551
|
var OPERATOR_EMAILS = /* @__PURE__ */ new Set(["julienpmjacquet@gmail.com", "chkev@umich.edu"]);
|
|
6328
|
-
var
|
|
6552
|
+
var DASHBOARD_BASE7 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
6329
6553
|
function isOperatorLogin() {
|
|
6330
6554
|
const email = loadCredentials()?.email?.toLowerCase();
|
|
6331
6555
|
return !!email && OPERATOR_EMAILS.has(email);
|
|
6332
6556
|
}
|
|
6333
6557
|
async function opCall(call) {
|
|
6334
6558
|
const token = getAccessToken();
|
|
6335
|
-
const res = await fetch(`${
|
|
6559
|
+
const res = await fetch(`${DASHBOARD_BASE7}/api/cli/op`, {
|
|
6336
6560
|
method: "POST",
|
|
6337
6561
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
6338
6562
|
body: JSON.stringify({ path: call.path, method: call.method, body: call.body })
|
|
@@ -6353,82 +6577,82 @@ function printResidue(report, applied) {
|
|
|
6353
6577
|
const up = report?.upstash ?? {};
|
|
6354
6578
|
const fga = report?.fga ?? {};
|
|
6355
6579
|
const ghosts = report?.ghosts ?? {};
|
|
6356
|
-
console.log(
|
|
6357
|
-
console.log(
|
|
6580
|
+
console.log(import_chalk38.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
6581
|
+
console.log(import_chalk38.default.bold("\n Upstash"));
|
|
6358
6582
|
const orphans = up.orphans ?? [];
|
|
6359
|
-
if (orphans.length === 0) console.log(
|
|
6360
|
-
for (const o of orphans) console.log(` ${
|
|
6361
|
-
console.log(
|
|
6583
|
+
if (orphans.length === 0) console.log(import_chalk38.default.green(" no orphaned keys"));
|
|
6584
|
+
for (const o of orphans) console.log(` ${import_chalk38.default.yellow(o.key)} ${import_chalk38.default.dim(`\u2014 ${o.reason}`)}`);
|
|
6585
|
+
console.log(import_chalk38.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
6362
6586
|
if (up.anon_wallet_detail) {
|
|
6363
6587
|
const d = up.anon_wallet_detail;
|
|
6364
|
-
console.log(
|
|
6588
|
+
console.log(import_chalk38.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)"}`));
|
|
6365
6589
|
}
|
|
6366
6590
|
if (up.keyspace_census) {
|
|
6367
6591
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
6368
|
-
console.log(
|
|
6592
|
+
console.log(import_chalk38.default.dim(` keyspace: ${census}`));
|
|
6369
6593
|
}
|
|
6370
|
-
if (up.unknown?.length) console.log(
|
|
6371
|
-
if (applied) console.log(` ${
|
|
6372
|
-
for (const e of up.errors ?? []) console.log(
|
|
6373
|
-
console.log(
|
|
6594
|
+
if (up.unknown?.length) console.log(import_chalk38.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
6595
|
+
if (applied) console.log(` ${import_chalk38.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
6596
|
+
for (const e of up.errors ?? []) console.log(import_chalk38.default.red(` error: ${e}`));
|
|
6597
|
+
console.log(import_chalk38.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
6374
6598
|
if (applied) {
|
|
6375
6599
|
const swept = fga?.swept ?? [];
|
|
6376
|
-
if (swept.length === 0) console.log(
|
|
6600
|
+
if (swept.length === 0) console.log(import_chalk38.default.green(" no orphaned stores"));
|
|
6377
6601
|
for (const s of swept) {
|
|
6378
6602
|
console.log(
|
|
6379
|
-
` ${
|
|
6603
|
+
` ${import_chalk38.default.yellow(s.store_id)} ${import_chalk38.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
6380
6604
|
);
|
|
6381
6605
|
}
|
|
6382
|
-
if (fga?.remaining) console.log(
|
|
6606
|
+
if (fga?.remaining) console.log(import_chalk38.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
6383
6607
|
const st = fga?.side_tables;
|
|
6384
|
-
if (st) console.log(
|
|
6608
|
+
if (st) console.log(import_chalk38.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})` : ""}`));
|
|
6385
6609
|
} else {
|
|
6386
6610
|
const fgaOrphans = fga?.orphans ?? [];
|
|
6387
|
-
if (fgaOrphans.length === 0) console.log(
|
|
6611
|
+
if (fgaOrphans.length === 0) console.log(import_chalk38.default.green(" no orphaned stores"));
|
|
6388
6612
|
for (const s of fgaOrphans) {
|
|
6389
6613
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
6390
|
-
console.log(` ${
|
|
6614
|
+
console.log(` ${import_chalk38.default.yellow(s.store_id)} ${import_chalk38.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
6391
6615
|
}
|
|
6392
|
-
console.log(
|
|
6616
|
+
console.log(import_chalk38.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
6393
6617
|
const st = fga?.side_tables;
|
|
6394
|
-
if (st) console.log(
|
|
6618
|
+
if (st) console.log(import_chalk38.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`));
|
|
6395
6619
|
}
|
|
6396
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
6397
|
-
console.log(
|
|
6620
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk38.default.red(` error: ${e}`));
|
|
6621
|
+
console.log(import_chalk38.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
6398
6622
|
if (applied) {
|
|
6399
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
6400
|
-
else console.log(` ${
|
|
6623
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk38.default.green(" no ghost tuples"));
|
|
6624
|
+
else console.log(` ${import_chalk38.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk38.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
6401
6625
|
} else {
|
|
6402
6626
|
const n = ghosts?.ghost_count ?? 0;
|
|
6403
|
-
if (n === 0) console.log(
|
|
6627
|
+
if (n === 0) console.log(import_chalk38.default.green(` no ghost tuples ${import_chalk38.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
6404
6628
|
else {
|
|
6405
|
-
console.log(
|
|
6629
|
+
console.log(import_chalk38.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
6406
6630
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
6407
|
-
console.log(
|
|
6631
|
+
console.log(import_chalk38.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
6408
6632
|
}
|
|
6409
|
-
if (n > 20) console.log(
|
|
6633
|
+
if (n > 20) console.log(import_chalk38.default.dim(` \u2026 and ${n - 20} more`));
|
|
6410
6634
|
}
|
|
6411
6635
|
}
|
|
6412
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
6636
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk38.default.red(` error: ${e}`));
|
|
6413
6637
|
console.log();
|
|
6414
6638
|
}
|
|
6415
6639
|
async function runOp(sub, opts = {}) {
|
|
6416
6640
|
if (!loadCredentials()) {
|
|
6417
|
-
console.log(
|
|
6641
|
+
console.log(import_chalk38.default.dim("Not logged in. Run `apiblaze login`."));
|
|
6418
6642
|
return;
|
|
6419
6643
|
}
|
|
6420
6644
|
if (!isOperatorLogin()) {
|
|
6421
|
-
console.log(
|
|
6645
|
+
console.log(import_chalk38.default.dim("`apiblaze op` is only available to platform operators."));
|
|
6422
6646
|
return;
|
|
6423
6647
|
}
|
|
6424
6648
|
switch (sub) {
|
|
6425
6649
|
case void 0:
|
|
6426
6650
|
case "menu": {
|
|
6427
|
-
console.log(
|
|
6428
|
-
console.log(` ${
|
|
6429
|
-
console.log(` ${
|
|
6430
|
-
console.log(` ${
|
|
6431
|
-
console.log(
|
|
6651
|
+
console.log(import_chalk38.default.bold("\nOperator menu"));
|
|
6652
|
+
console.log(` ${import_chalk38.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
6653
|
+
console.log(` ${import_chalk38.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk38.default.dim("-y to skip")})`);
|
|
6654
|
+
console.log(` ${import_chalk38.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
6655
|
+
console.log(import_chalk38.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
6432
6656
|
`));
|
|
6433
6657
|
return;
|
|
6434
6658
|
}
|
|
@@ -6447,17 +6671,17 @@ async function runOp(sub, opts = {}) {
|
|
|
6447
6671
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
6448
6672
|
printResidue(report, false);
|
|
6449
6673
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
6450
|
-
console.log(
|
|
6674
|
+
console.log(import_chalk38.default.green("Nothing to sweep."));
|
|
6451
6675
|
return;
|
|
6452
6676
|
}
|
|
6453
6677
|
if (!opts.yes) {
|
|
6454
6678
|
const readline2 = await import("readline/promises");
|
|
6455
6679
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
6456
6680
|
const answer = await rl.question(
|
|
6457
|
-
|
|
6681
|
+
import_chalk38.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: `)
|
|
6458
6682
|
);
|
|
6459
6683
|
rl.close();
|
|
6460
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
6684
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk38.default.dim("Aborted."));
|
|
6461
6685
|
}
|
|
6462
6686
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
6463
6687
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -6468,15 +6692,15 @@ async function runOp(sub, opts = {}) {
|
|
|
6468
6692
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
6469
6693
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
6470
6694
|
const accounts = data?.accounts ?? [];
|
|
6471
|
-
if (accounts.length === 0) return void console.log(
|
|
6695
|
+
if (accounts.length === 0) return void console.log(import_chalk38.default.dim("No credit wallets."));
|
|
6472
6696
|
for (const a of accounts) {
|
|
6473
6697
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
6474
|
-
console.log(` ${
|
|
6698
|
+
console.log(` ${import_chalk38.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk38.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk38.default.dim(" \u2014 anon") : ""}`);
|
|
6475
6699
|
}
|
|
6476
6700
|
return;
|
|
6477
6701
|
}
|
|
6478
6702
|
default:
|
|
6479
|
-
console.log(
|
|
6703
|
+
console.log(import_chalk38.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
6480
6704
|
}
|
|
6481
6705
|
}
|
|
6482
6706
|
|
|
@@ -6538,7 +6762,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
6538
6762
|
try {
|
|
6539
6763
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
6540
6764
|
if (Number.isNaN(resolved)) {
|
|
6541
|
-
console.error(
|
|
6765
|
+
console.error(import_chalk39.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
6542
6766
|
process.exit(1);
|
|
6543
6767
|
}
|
|
6544
6768
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -6597,6 +6821,16 @@ domain.command("list").description("List custom domains for a proxy").argument("
|
|
|
6597
6821
|
domain.command("status").description("Check a custom domain's validation status").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainStatus(project, opts)));
|
|
6598
6822
|
domain.command("rm").description("Remove a custom domain").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainRemove(project, opts)));
|
|
6599
6823
|
domain.command("set-base").description("Choose which version/environment your main URL serves").argument("<project>", "Project name or id").option("--env <env>", "Environment (default: prod)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainSetBase(project, opts)));
|
|
6824
|
+
var group = program.command("group").description("Manage a tenant's users & groups \u2014 bare command lists groups").action(action(() => runGroupList({})));
|
|
6825
|
+
group.command("list").description("List groups").option("--tenant <slug>", "Tenant").option("--json", "Machine-readable output").action(action((opts) => runGroupList(opts)));
|
|
6826
|
+
group.command("create").description("Create a group (e.g. `group create admin`)").argument("<name>", "Group name").option("--admin <user>", "Initial group-admin (email, name, or user id)").option("--tenant <slug>", "Tenant").action(action((name, opts) => runGroupCreate(name, opts)));
|
|
6827
|
+
group.command("delete").description("Delete a group").argument("<name>", "Group name").option("--tenant <slug>", "Tenant").action(action((name, opts) => runGroupDelete(name, opts)));
|
|
6828
|
+
group.command("members").description("Show a group's members and subgroups").argument("<name>", "Group name").option("--tenant <slug>", "Tenant").option("--json", "Machine-readable output").action(action((name, opts) => runGroupMembers(name, opts)));
|
|
6829
|
+
group.command("add-user").description("Add a user to a group (e.g. `group add-user john admin`)").argument("<user>", "User (email, name, or user id)").argument("<group>", "Group name").option("--role <role>", "member | admin (group-admin)", "member").option("--tenant <slug>", "Tenant").action(action((user, grp, opts) => runGroupAddUser(user, grp, opts)));
|
|
6830
|
+
group.command("remove-user").description("Remove a user from a group").argument("<user>", "User (email, name, or user id)").argument("<group>", "Group name").option("--tenant <slug>", "Tenant").action(action((user, grp, opts) => runGroupRemoveUser(user, grp, opts)));
|
|
6831
|
+
group.command("add-group").description("Nest a group inside another (e.g. `group add-group reservationists admin`)").argument("<child>", "Child group name").argument("<parent>", "Parent group name").option("--tenant <slug>", "Tenant").action(action((child, parent, opts) => runGroupAddGroup(child, parent, opts)));
|
|
6832
|
+
group.command("remove-group").description("Un-nest a subgroup from its parent").argument("<child>", "Child group name").argument("<parent>", "Parent group name").option("--tenant <slug>", "Tenant").action(action((child, parent, opts) => runGroupRemoveGroup(child, parent, opts)));
|
|
6833
|
+
group.command("users").description("List the tenant user directory + identities seen in traffic").option("--tenant <slug>", "Tenant").option("--json", "Machine-readable output").action(action((opts) => runGroupUsers(opts)));
|
|
6600
6834
|
var tenant = program.command("tenant").description("Manage tenants \u2014 bare command opens the interactive picker (settings, app clients, providers)").action(action(() => runTenantManage(void 0, {})));
|
|
6601
6835
|
tenant.command("manage").description("Browse & edit one tenant: settings, login app clients, providers, issuers (search-first picker)").argument("[query]", "Search by tenant name/display name (omit to use your tenant scope or pick)").option("--tenant <slug>", "Exact tenant slug (skips the picker)").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantManage(query, opts)));
|
|
6602
6836
|
tenant.command("use").description("Set the sticky tenant scope for future commands (search-first; --clear to unset)").argument("[query]", "Search by tenant name/display name").option("--clear", "Clear the tenant scope").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantUse(query, opts)));
|
|
@@ -6618,7 +6852,7 @@ spec.command("set").description("Replace the stored OpenAPI spec from a local fi
|
|
|
6618
6852
|
var HELP_GROUPS = [
|
|
6619
6853
|
{ title: "Chat", commands: ["apichat", "agent"] },
|
|
6620
6854
|
{ title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
|
|
6621
|
-
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
6855
|
+
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
6622
6856
|
{ title: "Data plane commands", commands: [
|
|
6623
6857
|
{ parent: "consumer", sub: "login" },
|
|
6624
6858
|
{ parent: "consumer", sub: "apikeys" }
|
|
@@ -6637,7 +6871,7 @@ function groupedCommandHelp() {
|
|
|
6637
6871
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
6638
6872
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
6639
6873
|
}).filter(Boolean).join("\n");
|
|
6640
|
-
return `${
|
|
6874
|
+
return `${import_chalk39.default.bold(g.title)}
|
|
6641
6875
|
${rows}`;
|
|
6642
6876
|
}).join("\n\n");
|
|
6643
6877
|
}
|
|
@@ -6671,14 +6905,14 @@ async function recoverStaleTeam() {
|
|
|
6671
6905
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
6672
6906
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
6673
6907
|
if (!linked) {
|
|
6674
|
-
console.error(
|
|
6908
|
+
console.error(import_chalk39.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
6675
6909
|
return;
|
|
6676
6910
|
}
|
|
6677
6911
|
if (linked.teamId === creds.teamId) return;
|
|
6678
6912
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
6679
6913
|
delete next.activeTenant;
|
|
6680
6914
|
saveCredentials(next);
|
|
6681
|
-
console.error(
|
|
6915
|
+
console.error(import_chalk39.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk39.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
6682
6916
|
} catch {
|
|
6683
6917
|
}
|
|
6684
6918
|
}
|
|
@@ -6686,16 +6920,16 @@ async function printError(err) {
|
|
|
6686
6920
|
if (err instanceof ApiError) {
|
|
6687
6921
|
const data = err.body;
|
|
6688
6922
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
6689
|
-
console.error(
|
|
6923
|
+
console.error(import_chalk39.default.red(`
|
|
6690
6924
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
6691
6925
|
if (err.status === 403 || err.status === 404) {
|
|
6692
6926
|
await recoverStaleTeam();
|
|
6693
6927
|
}
|
|
6694
6928
|
} else if (err instanceof Error) {
|
|
6695
|
-
console.error(
|
|
6929
|
+
console.error(import_chalk39.default.red(`
|
|
6696
6930
|
Error: ${err.message}`));
|
|
6697
6931
|
} else {
|
|
6698
|
-
console.error(
|
|
6932
|
+
console.error(import_chalk39.default.red("\nUnknown error"));
|
|
6699
6933
|
}
|
|
6700
6934
|
}
|
|
6701
6935
|
program.parse(process.argv);
|