apiblaze 0.21.9 → 0.21.11

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.
Files changed (2) hide show
  1. package/dist/index.js +109 -27
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1342,7 +1342,7 @@ var import_commander = require("commander");
1342
1342
  var import_chalk63 = __toESM(require("chalk"));
1343
1343
 
1344
1344
  // package.json
1345
- var version = "0.21.9";
1345
+ var version = "0.21.11";
1346
1346
 
1347
1347
  // src/index.ts
1348
1348
  init_types();
@@ -4848,10 +4848,30 @@ async function mintConsumerKey(o) {
4848
4848
  raw: out
4849
4849
  };
4850
4850
  }
4851
+ async function tenantKeyEndUserMode(caller, teamId, tenant2) {
4852
+ try {
4853
+ const p = await producer(caller, {
4854
+ method: "GET",
4855
+ path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/key-policy`,
4856
+ summary: "Read the tenant key policy (default holder)"
4857
+ });
4858
+ return p?.key_end_user_mode === "one" ? "one" : "many";
4859
+ } catch {
4860
+ return "many";
4861
+ }
4862
+ }
4863
+ function defaultHolderRefusal(tenant2) {
4864
+ return `Tenant ${tenant2} issues one key per end user (1 API key = 1 end user): add --for <email|id>. To create a different key anyway, pass --backend (a server key that names each caller) or --client.`;
4865
+ }
4851
4866
  async function runConsumerKeyMint(opts) {
4852
4867
  const kind = resolveHolder(opts);
4853
4868
  const who = (opts.for ?? "").trim();
4854
4869
  const tenant2 = (opts.tenant ?? "").trim();
4870
+ if (tenant2 && !who && !opts.backend && !opts.client) {
4871
+ const caller = requireCaller();
4872
+ const { teamId } = await resolveActingTeam(caller, opts.team);
4873
+ if (await tenantKeyEndUserMode(caller, teamId, tenant2) === "one") throw new Error(defaultHolderRefusal(tenant2));
4874
+ }
4855
4875
  const spinner = (0, import_ora3.default)(who ? `Minting a key for ${who} in ${tenant2}...` : `Minting a ${kind} key in ${tenant2}...`).start();
4856
4876
  let out;
4857
4877
  try {
@@ -10156,29 +10176,74 @@ var import_chalk44 = __toESM(require("chalk"));
10156
10176
  var import_ora16 = __toESM(require("ora"));
10157
10177
  init_admin();
10158
10178
  init_resolve();
10179
+ var KINDS = ["api", "mcp", "portal", "auth"];
10180
+ var TENANT_KINDS = ["portal", "auth"];
10181
+ async function scopeFor(project, opts) {
10182
+ const { teamId } = await resolveTeam(opts.team);
10183
+ if (project) {
10184
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
10185
+ return { kind: "project", base: `/projects/${proj2.projectId}/domains`, label: `${proj2.projectName} v${proj2.apiVersion}`, apiVersion: proj2.apiVersion };
10186
+ }
10187
+ if (!opts.tenant) {
10188
+ console.error(import_chalk44.default.red("Give a <project> (api / mcp domains) or --tenant <slug> (portal / auth domains)."));
10189
+ process.exit(1);
10190
+ }
10191
+ const tenant2 = opts.tenant.toLowerCase();
10192
+ return {
10193
+ kind: "tenant",
10194
+ base: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/domains`,
10195
+ label: `tenant ${tenant2}`
10196
+ };
10197
+ }
10198
+ function describeKind(d) {
10199
+ return d?.target ? `${d.kind ?? "api"} \u2192 stands in for ${d.target}` : d?.kind ?? "api";
10200
+ }
10201
+ function statusColor2(s) {
10202
+ if (s === "active") return import_chalk44.default.green(s);
10203
+ if (s === "failed" || s === "missing" || s === "suspended") return import_chalk44.default.red(s);
10204
+ return import_chalk44.default.yellow(s ?? "unknown");
10205
+ }
10159
10206
  async function runDomainAdd(project, opts) {
10160
10207
  if (!opts.domain) {
10161
10208
  console.error(import_chalk44.default.red("--domain is required."));
10162
10209
  process.exit(1);
10163
10210
  }
10164
- const { teamId } = await resolveTeam(opts.team);
10165
- const proj2 = await resolveProject(teamId, project, opts.apiversion);
10211
+ const kind = (opts.kind ?? (project ? "api" : "")).toLowerCase();
10212
+ if (!KINDS.includes(kind)) {
10213
+ console.error(import_chalk44.default.red(`--kind must be one of ${KINDS.join(", ")}${project ? "" : " (portal or auth for a --tenant)"}.`));
10214
+ process.exit(1);
10215
+ }
10216
+ const tenantKind = TENANT_KINDS.includes(kind);
10217
+ if (tenantKind && project) {
10218
+ console.error(import_chalk44.default.red(`A ${kind} domain belongs to a tenant, not a proxy: apiblaze domain add --tenant <slug> --kind ${kind} --domain ${opts.domain}`));
10219
+ process.exit(1);
10220
+ }
10221
+ if (tenantKind && !opts.tenant) {
10222
+ console.error(import_chalk44.default.red(`--tenant <slug> is required for a ${kind} domain.`));
10223
+ process.exit(1);
10224
+ }
10225
+ if (!tenantKind && !project) {
10226
+ console.error(import_chalk44.default.red(`A ${kind} domain belongs to a proxy: apiblaze domain add <project> --kind ${kind} --domain ${opts.domain}`));
10227
+ process.exit(1);
10228
+ }
10229
+ const scope = await scopeFor(project, opts);
10166
10230
  const spinner = (0, import_ora16.default)(`Registering ${opts.domain}...`).start();
10167
10231
  try {
10232
+ const body = scope.kind === "project" ? { hostname: opts.domain, api_version: scope.apiVersion, kind, ...opts.tenant ? { tenant_name: opts.tenant.toLowerCase() } : {} } : { hostname: opts.domain, kind };
10168
10233
  const out = await admin({
10169
10234
  method: "POST",
10170
- path: `/projects/${proj2.projectId}/domains`,
10171
- // --tenant binds the domain to one of the project's tenants (per-tenant scope).
10172
- body: { hostname: opts.domain, api_version: proj2.apiVersion, ...opts.tenant ? { tenant_name: opts.tenant } : {} },
10173
- summary: `Add custom domain ${opts.domain} to ${proj2.projectName} v${proj2.apiVersion}${opts.tenant ? ` (tenant ${opts.tenant})` : ""}`
10235
+ path: scope.base,
10236
+ body,
10237
+ summary: `Add custom ${kind} domain ${opts.domain} to ${scope.label}${scope.kind === "project" && opts.tenant ? ` (tenant ${opts.tenant})` : ""}`
10174
10238
  });
10175
- spinner.succeed(`Registered ${opts.domain}. Add these DNS records, then run \`apiblaze domain status\`:`);
10239
+ spinner.succeed(`Registered ${out?.hostname ?? opts.domain} (${describeKind(out)}). Add these DNS records, then run \`apiblaze domain status\`:`);
10176
10240
  if (opts.json) {
10177
10241
  console.log(JSON.stringify(out));
10178
10242
  return;
10179
10243
  }
10180
- if (out?.cnameRecord) console.log(` ${import_chalk44.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
10244
+ if (out?.cnameRecord) console.log(` ${import_chalk44.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value}`);
10181
10245
  if (out?.dcv) console.log(` ${import_chalk44.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
10246
+ if (typeof out?.monthlyCents === "number") console.log(import_chalk44.default.dim(` billed $${(out.monthlyCents / 100).toFixed(2)}/month (first month pro rata)`));
10182
10247
  if (out?.id) console.log(import_chalk44.default.dim(` domain id: ${out.id}`));
10183
10248
  } catch (err) {
10184
10249
  spinner.fail("Domain registration failed.");
@@ -10186,12 +10251,11 @@ async function runDomainAdd(project, opts) {
10186
10251
  }
10187
10252
  }
10188
10253
  async function runDomainList(project, opts) {
10189
- const { teamId } = await resolveTeam(opts.team);
10190
- const proj2 = await resolveProject(teamId, project, opts.apiversion);
10254
+ const scope = await scopeFor(project, opts);
10191
10255
  const out = await admin({
10192
10256
  method: "GET",
10193
- path: `/projects/${proj2.projectId}/domains?api_version=${encodeURIComponent(proj2.apiVersion)}`,
10194
- summary: `List custom domains for ${proj2.projectName} v${proj2.apiVersion}`
10257
+ path: scope.kind === "project" ? `${scope.base}?api_version=${encodeURIComponent(scope.apiVersion)}` : scope.base,
10258
+ summary: `List custom domains for ${scope.label}`
10195
10259
  });
10196
10260
  const domains = out?.domains ?? [];
10197
10261
  if (opts.json) {
@@ -10202,37 +10266,50 @@ async function runDomainList(project, opts) {
10202
10266
  console.log(import_chalk44.default.yellow("No custom domains."));
10203
10267
  return;
10204
10268
  }
10205
- for (const d of domains) console.log(` ${import_chalk44.default.bold(d.hostname)} ${import_chalk44.default.dim(d.status ?? "")} ${import_chalk44.default.dim(d.id ?? "")}`);
10269
+ for (const d of domains) {
10270
+ const pin = scope.kind === "project" && d.tenant ? import_chalk44.default.dim(` tenant ${d.tenant}`) : "";
10271
+ console.log(` ${import_chalk44.default.bold(d.hostname)} ${statusColor2(d.status)} ${import_chalk44.default.dim(describeKind(d))}${pin} ${import_chalk44.default.dim(d.id ?? "")}`);
10272
+ }
10206
10273
  }
10207
10274
  async function runDomainStatus(project, opts) {
10208
10275
  if (!opts.id) {
10209
10276
  console.error(import_chalk44.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
10210
10277
  process.exit(1);
10211
10278
  }
10212
- const { teamId } = await resolveTeam(opts.team);
10213
- const proj2 = await resolveProject(teamId, project, opts.apiversion);
10279
+ const scope = await scopeFor(project, opts);
10214
10280
  const out = await admin({
10215
10281
  method: "GET",
10216
- path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
10282
+ path: `${scope.base}/${encodeURIComponent(opts.id)}/status`,
10217
10283
  summary: `Check custom-domain status`
10218
10284
  });
10219
- console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk44.default.bold(out?.hostname ?? opts.id)}: ${import_chalk44.default.cyan(out?.status ?? "unknown")}`);
10285
+ if (opts.json) {
10286
+ console.log(JSON.stringify(out));
10287
+ return;
10288
+ }
10289
+ console.log(` ${import_chalk44.default.bold(out?.hostname ?? opts.id)}: ${statusColor2(out?.status)} ${import_chalk44.default.dim(describeKind(out))}`);
10290
+ if (out?.status !== "active") {
10291
+ if (out?.status === "suspended") console.log(import_chalk44.default.red(" Suspended for non-payment \u2014 add credit to restore it."));
10292
+ else if (out?.status === "missing") console.log(import_chalk44.default.red(" No longer registered at Cloudflare \u2014 remove it and add it again."));
10293
+ else {
10294
+ if (out?.cnameRecord) console.log(` ${import_chalk44.default.cyan("CNAME")} ${out.cnameRecord.name} \u2192 ${out.cnameRecord.value}`);
10295
+ if (out?.dcv) console.log(` ${import_chalk44.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
10296
+ }
10297
+ }
10220
10298
  }
10221
10299
  async function runDomainRemove(project, opts) {
10222
10300
  if (!opts.id) {
10223
10301
  console.error(import_chalk44.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
10224
10302
  process.exit(1);
10225
10303
  }
10226
- const { teamId } = await resolveTeam(opts.team);
10227
- const proj2 = await resolveProject(teamId, project, opts.apiversion);
10304
+ const scope = await scopeFor(project, opts);
10228
10305
  const spinner = (0, import_ora16.default)("Removing domain...").start();
10229
10306
  try {
10230
10307
  await admin({
10231
10308
  method: "DELETE",
10232
- path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}`,
10309
+ path: `${scope.base}/${encodeURIComponent(opts.id)}`,
10233
10310
  summary: `Remove custom domain ${opts.id}`
10234
10311
  });
10235
- spinner.succeed("Removed.");
10312
+ spinner.succeed("Removed. Billing for it stops now.");
10236
10313
  } catch (err) {
10237
10314
  spinner.fail("Remove failed.");
10238
10315
  throw err;
@@ -14446,11 +14523,16 @@ program.command("config").description("Browse and change every proxy setting & f
14446
14523
  program.command("target").description("Change where a proxy forwards requests").argument("<project>", "Project name or id").requiredOption("--url <url>", "Target URL to forward to").option("--env <env>", "Environment to scope the target to (e.g. prod, dev)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTargetSet(project, opts)));
14447
14524
  program.command("throttle").description("Set rate limits and quotas for a proxy").argument("<project>", "Project name or id").option("--rate <n>", "User rate limit (requests/sec)").option("--end-user-rate <n>", "Per-end-user rate limit (requests/sec)").option("--quota <n>", "Proxy quota (requests/period)").option("--period <p>", "Quota period: daily | weekly | monthly").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runThrottleSet(project, opts)));
14448
14525
  program.command("rename").description("Change a proxy's display name").argument("<project>", "Project name or id").requiredOption("--display-name <name>", "New human-friendly display name").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runRename(project, opts)));
14449
- var domain = program.command("domain").description("Use your own domain for a proxy");
14450
- domain.command("add").description("Add your own domain (shows the DNS records to set)").argument("<project>", "Project name or id").requiredOption("--domain <host>", "Custom hostname to add").option("--tenant <slug>", "Bind the domain to one of the project's tenants (per-tenant scope)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainAdd(project, opts)));
14451
- domain.command("list").description("List custom domains for a proxy").argument("<project>", "Project name or id").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainList(project, opts)));
14452
- 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)));
14453
- 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)));
14526
+ var domain = program.command("domain").description("Use your own domain for your API, MCP server, developer portal or login pages (billed monthly)");
14527
+ domain.command("add").description("Add your own domain (shows the DNS records to set)").argument("[project]", "Project name or id (for api / mcp domains; omit with --tenant for portal / auth)").requiredOption("--domain <host>", "Custom hostname to add, e.g. api.example.com").option("--kind <kind>", "api (default) | mcp \u2014 on a project; portal | auth \u2014 on a --tenant").option("--tenant <slug>", "portal/auth: the tenant the domain belongs to. api/mcp: pin the domain to one of the project's tenants").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").addHelpText("after", `
14528
+ Examples:
14529
+ apiblaze domain add shop --domain api.acme.com # the API
14530
+ apiblaze domain add shop --domain mcp.acme.com --kind mcp # the MCP server
14531
+ apiblaze domain add --tenant acme --kind portal --domain developers.acme.com
14532
+ apiblaze domain add --tenant acme --kind auth --domain login.acme.com`).action(action((project, opts) => runDomainAdd(project, opts)));
14533
+ domain.command("list").description("List a project's custom domains, or a tenant's portal/login domains").argument("[project]", "Project name or id (omit with --tenant)").option("--tenant <slug>", "List the tenant's portal / auth domains").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainList(project, opts)));
14534
+ domain.command("status").description("Check a custom domain's validation status (and show the DNS records still needed)").argument("[project]", "Project name or id (omit with --tenant)").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--tenant <slug>", "The tenant, for a portal / auth domain").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)));
14535
+ domain.command("rm").description("Remove a custom domain (billing for it stops)").argument("[project]", "Project name or id (omit with --tenant)").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--tenant <slug>", "The tenant, for a portal / auth domain").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainRemove(project, opts)));
14454
14536
  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)));
14455
14537
  var group = program.command("group").description("Manage a tenant's users & groups \u2014 bare command lists groups").action(action(() => runGroupList({})));
14456
14538
  group.command("list").description("List groups").option("--tenant <slug>", "Tenant").option("--json", "Machine-readable output").action(action((opts) => runGroupList(opts)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.21.9",
3
+ "version": "0.21.11",
4
4
  "description": "APIblaze CLI — Chat with your APIs, Manage your API keys, users and groups with the APIblaze serverless proxy",
5
5
  "keywords": [
6
6
  "apiblaze",