apiblaze 0.20.2 → 0.20.3

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 +66 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -932,7 +932,7 @@ var import_commander = require("commander");
932
932
  var import_chalk51 = __toESM(require("chalk"));
933
933
 
934
934
  // package.json
935
- var version = "0.20.2";
935
+ var version = "0.20.3";
936
936
 
937
937
  // src/index.ts
938
938
  init_types();
@@ -3666,7 +3666,7 @@ async function runRecipeInstall(nameArg, opts) {
3666
3666
  const assumeYes = !!opts.yes;
3667
3667
  const interactive = !!process.stdin.isTTY && !assumeYes;
3668
3668
  const caller = requireCallerForInstall();
3669
- const recipe = await fetchRecipeFile(ref, caller);
3669
+ let recipe = await fetchRecipeFile(ref, caller);
3670
3670
  const pinned = `${recipe.name}@${recipe.revision}`;
3671
3671
  const picked = await chooseTeam(caller, opts.team, interactive);
3672
3672
  const teamId = picked.teamId;
@@ -3677,6 +3677,7 @@ async function runRecipeInstall(nameArg, opts) {
3677
3677
  if (recipe.summary) console.log(` ${recipe.summary}`);
3678
3678
  console.log();
3679
3679
  let project;
3680
+ let reuseTenant = false;
3680
3681
  let apiVersion;
3681
3682
  let tenant2;
3682
3683
  let reinstall = false;
@@ -3691,15 +3692,19 @@ async function runRecipeInstall(nameArg, opts) {
3691
3692
  } else {
3692
3693
  project = await chooseProxyName(ref.slug, opts.as, teamId, interactive);
3693
3694
  apiVersion = recipe.api_version || "1.0.0";
3694
- tenant2 = `${project}users`;
3695
- await assertTenantFree(tenant2, teamId);
3696
- printSummary(recipe, project, tenant2);
3695
+ const chosenTenant = await chooseTenant(caller, project, teamId, opts.tenant, interactive);
3696
+ tenant2 = chosenTenant.tenant;
3697
+ reuseTenant = chosenTenant.existing;
3698
+ printSummary(recipe, project, tenant2, reuseTenant);
3697
3699
  if (!assumeYes && !await confirmYesNo(" continue?", interactive)) {
3698
3700
  console.log(import_chalk26.default.yellow(" Cancelled \u2014 nothing was created."));
3699
3701
  return;
3700
3702
  }
3701
3703
  }
3702
3704
  const ctx = { project, tenant: tenant2 };
3705
+ if (reuseTenant) {
3706
+ recipe = { ...recipe, auth: { app_client: null, provider: null, key_types: [] }, questions: [] };
3707
+ }
3703
3708
  const declined = assumeYes ? declineAll(recipe.requires_confirmation ?? []) : await askConsent(recipe.requires_confirmation ?? [], interactive);
3704
3709
  const answers = await askQuestions(recipe.questions ?? [], ctx, interactive);
3705
3710
  const started = Date.now();
@@ -4102,7 +4107,7 @@ async function assertTenantFree(tenant2, teamId) {
4102
4107
  );
4103
4108
  }
4104
4109
  }
4105
- function printSummary(recipe, project, tenant2) {
4110
+ function printSummary(recipe, project, tenant2, reuseTenant = false) {
4106
4111
  const hosts = upstreamHosts(recipe);
4107
4112
  const routes = countRoutes(recipe.openapi);
4108
4113
  const keyTypes = recipe.auth?.key_types?.length ?? 0;
@@ -4110,7 +4115,11 @@ function printSummary(recipe, project, tenant2) {
4110
4115
  console.log();
4111
4116
  if (hosts.length) console.log(` ${import_chalk26.default.dim("sends requests to")} ${hosts.join(", ")}`);
4112
4117
  console.log(` ${import_chalk26.default.dim("creates ")} ${describeCreates(keyTypes, routes, recipe.settings.mcp_enabled === true)}`);
4113
- console.log(` ${import_chalk26.default.dim("asks you for ")} ${questions} value${questions === 1 ? "" : "s"}`);
4118
+ console.log(` ${import_chalk26.default.dim("asks you for ")} ${reuseTenant ? "nothing" : `${questions} value${questions === 1 ? "" : "s"}`}`);
4119
+ if (reuseTenant) {
4120
+ console.log(` ${import_chalk26.default.dim("users live in ")} ${tenant2} ${import_chalk26.default.dim("(existing \u2014 its login is used as it is)")}`);
4121
+ console.log(import_chalk26.default.dim(` ${" ".repeat(17)} the recipe's own login setup is not applied`));
4122
+ }
4114
4123
  if (recipe.settings.publish_openapi === true) {
4115
4124
  console.log(` ${import_chalk26.default.dim("makes public ")} this API's spec, at ${project}.abz.run/${recipe.api_version}/openapi.json`);
4116
4125
  }
@@ -4300,6 +4309,55 @@ async function chooseTeam(caller, optTeam, interactive) {
4300
4309
  const match = teams.find((t) => t.teamId === picked);
4301
4310
  return { teamId: picked, teamName: match?.name, chosen: true };
4302
4311
  }
4312
+ async function chooseTenant(caller, project, teamId, optTenant, interactive) {
4313
+ const fresh = `${project}users`;
4314
+ const owned = await teamTenants(caller, teamId);
4315
+ const isOwned = (t2) => owned.some((o) => o === t2);
4316
+ if (optTenant) {
4317
+ const t2 = normalizeName2(optTenant);
4318
+ if (isOwned(t2)) return { tenant: t2, existing: true };
4319
+ await assertTenantFree(t2, teamId);
4320
+ return { tenant: t2, existing: false };
4321
+ }
4322
+ if (!interactive || !owned.length) {
4323
+ await assertTenantFree(fresh, teamId);
4324
+ return { tenant: fresh, existing: false };
4325
+ }
4326
+ const { default: inquirer3 } = await import("inquirer");
4327
+ const { picked } = await inquirer3.prompt([{
4328
+ type: "list",
4329
+ name: "picked",
4330
+ message: " where should this proxy's users live?",
4331
+ default: "__new__",
4332
+ choices: [
4333
+ { name: `create ${fresh} ${import_chalk26.default.dim("(a fresh tenant, set up by the recipe)")}`, value: "__new__" },
4334
+ new inquirer3.Separator(import_chalk26.default.dim(" \u2014 or reuse one you already have \u2014")),
4335
+ ...owned.map((t2) => ({
4336
+ name: `${t2} ${import_chalk26.default.dim("(existing \u2014 its own login is kept)")}`,
4337
+ value: t2
4338
+ }))
4339
+ ]
4340
+ }]);
4341
+ if (picked !== "__new__") return { tenant: picked, existing: true };
4342
+ const { name } = await inquirer3.prompt([{
4343
+ type: "input",
4344
+ name: "name",
4345
+ message: " new tenant name",
4346
+ default: fresh,
4347
+ filter: (v) => normalizeName2(v)
4348
+ }]);
4349
+ const t = normalizeName2(String(name || fresh));
4350
+ await assertTenantFree(t, teamId);
4351
+ return { tenant: t, existing: false };
4352
+ }
4353
+ async function teamTenants(caller, teamId) {
4354
+ const out = await producer(caller, {
4355
+ method: "GET",
4356
+ path: `/teams/${encodeURIComponent(teamId)}/tenants`,
4357
+ summary: "List the team's tenants"
4358
+ }).catch(() => ({ tenants: [] }));
4359
+ return (out?.tenants ?? []).map((t) => typeof t === "string" ? t : t.disabled ? "" : t.tenant_name ?? t.name ?? "").filter(Boolean);
4360
+ }
4303
4361
 
4304
4362
  // src/commands/recipe-publish.ts
4305
4363
  var import_chalk27 = __toESM(require("chalk"));
@@ -10008,7 +10066,7 @@ program.command("projects").description("List the projects in your team").action
10008
10066
  });
10009
10067
  program.command("search").description("Find a published recipe \u2014 a whole working proxy someone else set up").argument("[query]", "Words to match against a recipe name or summary").option("--limit <n>", "Maximum results (default 25)").option("--json", "Output machine-readable JSON").action(action((query, opts) => runRecipeSearch(query, opts)));
10010
10068
  program.command("show").description("Read a recipe before you install it (upstreams, questions, transforms, spec)").argument("<recipe>", "Recipe name, e.g. @julien/gmail or @julien/gmail@2").option("--settings", "Print the settings section").option("--auth", "Print the auth section (login provider, key types)").option("--transforms", "Print the transform rules and mapping tables").option("--openapi", "Print the OpenAPI document").option("--json", "Output machine-readable JSON").action(action((recipe, opts) => runRecipeShow(recipe, opts)));
10011
- program.command("install").description("Create a proxy from a recipe, with your own credentials").argument("<recipe>", "Recipe name, e.g. @julien/gmail or @julien/gmail@2 to pin a revision").option("--as <name>", "Proxy name (skips the name prompt)").option("--into <proxy>", "Reinstall over an existing proxy \u2014 replaces its settings, transforms and spec").option("--team <id|name>", "Team the proxy is created in (you are asked when you belong to several)").option("--apiversion <v>", "API version of the proxy named by --into").option("-y, --yes", "Skip the routine prompts (installs WITHOUT anything needing consent)").action(action((recipe, opts) => runRecipeInstall(recipe, opts)));
10069
+ program.command("install").description("Create a proxy from a recipe, with your own credentials").argument("<recipe>", "Recipe name, e.g. @julien/gmail or @julien/gmail@2 to pin a revision").option("--as <name>", "Proxy name (skips the name prompt)").option("--into <proxy>", "Reinstall over an existing proxy \u2014 replaces its settings, transforms and spec").option("--team <id|name>", "Team the proxy is created in (you are asked when you belong to several)").option("--apiversion <v>", "API version of the proxy named by --into").option("--tenant <slug>", "Where this proxy's users live. One of yours is reused as it is; a new name is created").option("-y, --yes", "Skip the routine prompts (installs WITHOUT anything needing consent)").action(action((recipe, opts) => runRecipeInstall(recipe, opts)));
10012
10070
  program.command("publish").description("Publish one of your proxies as a recipe others can install").argument("<project>", "Project name or id (see `apiblaze projects`)").option("--as <name>", "Recipe name. Defaults to your GitHub handle + the proxy name; pass `pokedex` for just the name, or `@acme/pokedex` to set both").option("--private", "Only your team can see or install it (default is public)").option("--tenant <slug>", "Which tenant's auth setup the recipe carries (asked when a proxy has several)").option("--display-name <text>", "Human-readable title").option("--summary <text>", "One-line description shown in search").option("--license <id>", "Licence identifier, e.g. MIT").option("--team <id|name>", "Team the project is in (defaults to active team)").option("--apiversion <v>", "API version to publish (defaults to the first match)").option("-y, --yes", "Skip routine confirmations (never accepts scanner findings)").option("--json", "Output machine-readable JSON").action(action((project, opts) => runRecipePublish(project, opts)));
10013
10071
  program.command("withdraw").description("Permanently delete one published recipe revision").argument("<recipe>", "Recipe revision, e.g. @julien/gmail@3").option("--team <id|name>", "Team that published it (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((recipe, opts) => runRecipeWithdraw(recipe, opts)));
10014
10072
  program.command("delete").description("Delete a proxy and everything under it (asks first)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--team <id|name>", "Team the project is in (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON").action(action((project, version2, opts) => runDelete(project, version2, opts)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.20.2",
3
+ "version": "0.20.3",
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",