apiblaze 0.21.54 → 0.21.56

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 +34 -19
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__
34
34
  var version;
35
35
  var init_package = __esm({
36
36
  "package.json"() {
37
- version = "0.21.54";
37
+ version = "0.21.56";
38
38
  }
39
39
  });
40
40
 
@@ -1124,7 +1124,7 @@ async function resolveProject(teamId, nameOrId, version2) {
1124
1124
  projectName: chosen.projectName,
1125
1125
  apiVersion: chosen.apiVersion,
1126
1126
  teamId,
1127
- tenant: chosen.tenant
1127
+ tenant: chosen.tenant ?? void 0
1128
1128
  };
1129
1129
  }
1130
1130
  var import_chalk6;
@@ -5309,8 +5309,8 @@ __export(directory_exports, {
5309
5309
  fetchDirectoryReport: () => fetchDirectoryReport,
5310
5310
  isApiError: () => isApiError,
5311
5311
  isReady: () => isReady,
5312
+ parseCompliant: () => parseCompliant,
5312
5313
  parsePlatforms: () => parsePlatforms,
5313
- parsePolicy: () => parsePolicy,
5314
5314
  printReport: () => printReport
5315
5315
  });
5316
5316
  function attestationId(flag) {
@@ -5343,7 +5343,7 @@ function directoryBodyFromFlags(f) {
5343
5343
  }
5344
5344
  return out;
5345
5345
  }
5346
- function parsePolicy(policy, platform) {
5346
+ function parseCompliant(policy, platform) {
5347
5347
  if (platform && (policy === void 0 || policy === true)) return parsePlatforms(platform) ?? ["claude"];
5348
5348
  if (policy === void 0 || policy === false || policy === true) return ["claude"];
5349
5349
  const raw = (Array.isArray(policy) ? policy : [policy]).flatMap((s) => String(s).split(",")).map((s) => s.trim().toLowerCase()).filter(Boolean);
@@ -5354,7 +5354,7 @@ function parsePolicy(policy, platform) {
5354
5354
  PLATFORMS.forEach((x) => out.add(x));
5355
5355
  continue;
5356
5356
  }
5357
- if (!PLATFORMS.includes(p)) throw new Error(`Unknown policy "${p}". Use claude, chatgpt or all.`);
5357
+ if (!PLATFORMS.includes(p)) throw new Error(`Unknown directory "${p}" for --compliant. Use claude, chatgpt or all.`);
5358
5358
  out.add(p);
5359
5359
  }
5360
5360
  return PLATFORMS.filter((p) => out.has(p));
@@ -10604,7 +10604,7 @@ async function runCheck(project, opts) {
10604
10604
  }
10605
10605
  const name = project;
10606
10606
  if (!name) throw new Error(`Which ${B.thingShort}? ${B.cli} check --directory-ready <project>`);
10607
- const platforms = parsePolicy(opts.policy, opts.platform);
10607
+ const platforms = parseCompliant(opts.compliant ?? opts.policy, opts.platform);
10608
10608
  const stage = opts.stage === "create" ? "create" : opts.stage === "ready" || !opts.stage ? "ready" : void 0;
10609
10609
  if (!stage) throw new Error("--stage must be create or ready");
10610
10610
  const caller = requireCaller();
@@ -11123,7 +11123,7 @@ function seedMessage(proj2) {
11123
11123
  async function runConnector(target2, opts) {
11124
11124
  const caller = requireCaller();
11125
11125
  const { teamId } = await resolveActingTeam(caller, opts.team);
11126
- const platforms = parsePolicy(opts.policy, opts.platform);
11126
+ const platforms = parseCompliant(opts.compliant ?? opts.policy, opts.platform);
11127
11127
  const json = !!opts.json;
11128
11128
  const interactive2 = !!process.stdin.isTTY && !json && !opts.yes;
11129
11129
  const say = (s) => {
@@ -11221,7 +11221,12 @@ async function runConnector(target2, opts) {
11221
11221
  process.stdout.write(JSON.stringify({ ready: true, project: proj2.projectId, headline: r.rendered.headline, report: r.report, packet: { markdown: mdPath, json: jsonPath }, portals: PORTALS, ...reviewer ? { reviewer } : {} }) + "\n");
11222
11222
  return;
11223
11223
  }
11224
- printReport(r, false);
11224
+ if (r.rendered.compliance) {
11225
+ console.log();
11226
+ console.log(r.rendered.compliance.split("\n").map((l, i) => i === 0 ? import_chalk28.default.bold(l) : l.replace(/^(\s+)✓ /, (_m, sp2) => `${sp2}${import_chalk28.default.green("\u2713")} `).replace(/^(\s+)! /, (_m, sp2) => `${sp2}${import_chalk28.default.yellow("!")} `)).join("\n"));
11227
+ } else {
11228
+ printReport(r, false);
11229
+ }
11225
11230
  console.log();
11226
11231
  console.log(import_chalk28.default.green.bold(` ${r.rendered.headline}`));
11227
11232
  console.log(` Submission packet: ${import_chalk28.default.cyan(mdPath)} and ${import_chalk28.default.cyan(jsonPath)}`);
@@ -12007,9 +12012,9 @@ the header are not owned by ana@example.com's key.
12007
12012
 
12008
12013
  ## Listing it in the Claude or ChatGPT directory
12009
12014
 
12010
- \`npx ${cli} create --policy --target https://yourdomain.com/openapi.yaml --domain mcp.yourdomain.com\` \u2014 directory-ready
12011
- in one command, to the letter of the Claude directory's policies (\`--policy chatgpt\` or \`--policy all\` for OpenAI's too;
12012
- \`npx ${cli} connector <name> --policy \u2026\` runs the same flow on an existing ${B.thingShort}): it creates the ${B.thingShort} from the spec, checks what the
12015
+ \`npx ${cli} create --compliant --target https://yourdomain.com/openapi.yaml --domain mcp.yourdomain.com\` \u2014 directory-ready
12016
+ in one command, to the letter of the Claude directory's policies (\`--compliant chatgpt\` or \`--compliant all\` for OpenAI's too;
12017
+ \`npx ${cli} connector <name> --compliant \u2026\` runs the same flow on an existing ${B.thingShort}): it creates the ${B.thingShort} from the spec, checks what the
12013
12018
  directories require, applies the safe fixes, registers the MCP hostname and waits for its ONE CNAME
12014
12019
  (\u2192 proxy.abz.run), hides utility endpoints and fills thin tool descriptions, asks for the rest with
12015
12020
  every answer pre-filled from the spec and cut to the portal's limits (Enter accepts), creates the reviewer account, and writes the
@@ -12752,6 +12757,9 @@ async function runAuthz(projectArg, apiVersionArg) {
12752
12757
  if (!match) {
12753
12758
  throw new Error(`Project "${projectArg}" not found in your active team (${creds.teamName ?? teamId}). Run \`apiblaze projects\`.`);
12754
12759
  }
12760
+ if (match.configMissing) {
12761
+ throw new Error(`Project "${projectArg}": its settings could not be loaded, so its tenant is unknown and no rule can be published to it. Open it in the dashboard (Refresh, or delete and re-create it) first.`);
12762
+ }
12755
12763
  const projectId = match.projectId;
12756
12764
  const apiVersion = apiVersionArg || match.apiVersion;
12757
12765
  const tenant2 = match.tenant || projectId;
@@ -12813,6 +12821,9 @@ async function runRule(projectArg, ruleText, opts) {
12813
12821
  if (!match) {
12814
12822
  throw new Error(`Project "${projectArg}" not found in your active team (${creds.teamName ?? teamId}). Run \`${B.cli} projects\`.`);
12815
12823
  }
12824
+ if (match.configMissing) {
12825
+ throw new Error(`Project "${projectArg}": its settings could not be loaded, so its tenant is unknown and no rule can be published to it. Open it in the dashboard (Refresh, or delete and re-create it) first.`);
12826
+ }
12816
12827
  const projectId = match.projectId;
12817
12828
  const apiVersion = opts.apiversion || match.apiVersion;
12818
12829
  const tenant2 = match.tenant || projectId;
@@ -14068,7 +14079,10 @@ function declineAll(items) {
14068
14079
  console.log(import_chalk48.default.yellow(` --yes: installing without ${skippable.length} setting${skippable.length === 1 ? "" : "s"} that need consent.`));
14069
14080
  for (const i of skippable) console.log(import_chalk48.default.dim(` skipped ${i.kind} ${i.value}`));
14070
14081
  }
14071
- return new Set(skippable.map((i) => i.kind));
14082
+ return new Set(skippable.map(declineKey));
14083
+ }
14084
+ function declineKey(item) {
14085
+ return item.kind === "transform" ? `transform:${item.value}` : `kind:${item.kind}`;
14072
14086
  }
14073
14087
  async function confirmYesNo(message, interactive2) {
14074
14088
  if (!interactive2) {
@@ -14101,7 +14115,7 @@ async function askConsent(items, interactive2, reinstalling) {
14101
14115
  }
14102
14116
  if (typed.toLowerCase() === "yes") return declined;
14103
14117
  for (const item of items) {
14104
- declined.add(item.kind === "transform" ? `transform:${item.value}` : `kind:${item.kind}`);
14118
+ declined.add(declineKey(item));
14105
14119
  }
14106
14120
  console.log(import_chalk48.default.dim(` Installing without ${count} setting${count === 1 ? "" : "s"} \u2014 everything else is applied.`));
14107
14121
  return declined;
@@ -20094,13 +20108,14 @@ program.command("login").description(`Authenticate with ${B.product}`).option("-
20094
20108
  process.exit(1);
20095
20109
  }
20096
20110
  });
20097
- program.command("create").description(`Put an app that is already DEPLOYED behind ${B.product}: a public URL and an MCP address for AI agents in front of your URL or OpenAPI spec. Included: API keys, sign-in, ownership rules, rate limits. No login needed. (App on this machine? Use \`dev\`.)`).option("--name <name>", B.cli === "mcpblaze" ? "MCP server name (becomes <name>-<tenant>.mcpblaze.com)" : "Proxy name (becomes <name>.abz.run)").option("--target <url|file>", "What to proxy \u2014 pass ANY of: a target server base URL (https://httpbin.org), a local OpenAPI file (./openapi.yaml), or a remote OpenAPI URL (https://acme.com/openapi.yaml). Spec inputs are detected automatically; routes, API version and environments then come from the spec").addOption(new import_commander.Option("--openapi <file|url>", "Deprecated alias \u2014 --target now detects spec files/URLs itself").hideHelp()).addOption(new import_commander.Option("--openapispec <file|url>", "Deprecated alias for --openapi").hideHelp()).option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Open ONE door only: api_key | oauth (sign in with GitHub, APIblaze-hosted) | none. Omitted = both doors.").option("--apikey", "API-key door only (no sign-in): the bootstrap keys are minted and printed; callers send X-API-Key, agents connect with a key.").option("--oauth [config]", `Sign-in door only (no API key). Bare = APIblaze-hosted GitHub sign-in. '{"iss","aud","jwks"}' = trust YOUR hosted login's JWTs. '{"provider","clientId","clientSecret"?}' = APIblaze-hosted login page with YOUR OAuth app (github \xB7 google \xB7 microsoft \xB7 facebook \xB7 auth0 \xB7 oidc); the secret is optional except for github and facebook. '{"provider":"oidc","issuer":"https://acme.okta.com/oauth2/default","clientId":"\u2026"}' = any OpenID Connect IdP by issuer (Okta \xB7 Entra \xB7 Keycloak \xB7 Cognito \xB7 \u2026).`).option("--identified", "Require every call to identify its end user (X-End-User-Id from a backend key, or a login token \u2014 a client key alone cannot); unattributed calls are rejected").option("--iam", `Turn IAM on for the ${B.thingShort}'s tenant so users & groups apply to identified calls`).option("--apiversion <version>", `API version to create (e.g. 2.0.0). Creating a new version of a ${B.thingShort} you own adds a version to the existing project.`).option("--tenant <slug>", `Tenant to attach the ${B.thingShort} to (created if new). Omitted \u2192 your team's most-recently-used tenant.`).option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--auto", `Zero prompts, start to finish: generates the ${B.thingShort} name when --name is omitted (from the target host, salted), takes every confirmation as yes, auto-picks the tenant. Implies --yes.`).option("--new-session", "Start a fresh anonymous session (do not group with prior anonymous creates)").option("--json", "Output machine-readable JSON (non-interactive)").option("--policy [directories...]", `Directory-ready in one command, to the letter of that directory's policies: create the ${B.thingShort} from --target (an OpenAPI URL/file), apply the safe fixes, set up the custom MCP domain (--domain, one CNAME), ask for every listing answer with the spec's proposal pre-filled (cut to the portal's limits), create the reviewer account, write the submission packet. Bare = claude; or claude \xB7 chatgpt \xB7 all. Same as \`${B.cli} connector <spec> --policy \u2026\`.`).option("--domain <host>", "With --policy: the MCP server's custom hostname (mcp.<your domain>); asked for when missing, suggested from the spec").option("--wait <minutes>", "With --policy: how long to wait for the custom domain to activate once its CNAME is in place (default 15; 0 = do not wait)").option("--directory-ready", "Make it directory-ready for the Claude and ChatGPT directories: OAuth door, custom-domain checks, listing fields; the readiness report runs after the create and a blocker exits 1").option("--privacy-url <url>", "Directory listing: public https privacy policy").option("--support <email|url>", "Directory listing: support contact").option("--docs-url <url>", "Directory listing: public documentation URL (the developer portal by default)").option("--prompt <text>", "Directory listing: an example prompt (repeat the flag; three are needed)", collect, []).option("--server-name <name>", 'Directory listing: the name (2\u201340 chars, no "MCP"/"Plugin" suffix)').option("--tagline <text>", "Directory listing: one-line tagline").option("--description <text>", "Directory listing: description").option("--icon-url <url>", "Directory listing: square icon (https)").option("--vuln-contact <email|url>", "Directory listing: vulnerability-disclosure contact").option("--attest <id>", "Attest (repeatable): guidelines \xB7 own-backend \xB7 no-financial \xB7 no-genai-media \xB7 no-prompt-injection \xB7 no-conversation-data \xB7 public-docs (Claude's seven) \xB7 no-digital-goods \xB7 content-13-no-ads \xB7 no-restricted-data (ChatGPT)", collect, []).option("--allowed-origin <origin>", "Directory mode: an extra browser origin allowed on the MCP host (repeatable)", collect, []).option("--openai-challenge <token>", "ChatGPT: the domain-verification token the OpenAI portal shows under Plugins \u2192 Connect MCP server").action(async (opts) => {
20111
+ program.command("create").description(`Put an app that is already DEPLOYED behind ${B.product}: a public URL and an MCP address for AI agents in front of your URL or OpenAPI spec. Included: API keys, sign-in, ownership rules, rate limits. No login needed. (App on this machine? Use \`dev\`.)`).option("--name <name>", B.cli === "mcpblaze" ? "MCP server name (becomes <name>-<tenant>.mcpblaze.com)" : "Proxy name (becomes <name>.abz.run)").option("--target <url|file>", "What to proxy \u2014 pass ANY of: a target server base URL (https://httpbin.org), a local OpenAPI file (./openapi.yaml), or a remote OpenAPI URL (https://acme.com/openapi.yaml). Spec inputs are detected automatically; routes, API version and environments then come from the spec").addOption(new import_commander.Option("--openapi <file|url>", "Deprecated alias \u2014 --target now detects spec files/URLs itself").hideHelp()).addOption(new import_commander.Option("--openapispec <file|url>", "Deprecated alias for --openapi").hideHelp()).option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Open ONE door only: api_key | oauth (sign in with GitHub, APIblaze-hosted) | none. Omitted = both doors.").option("--apikey", "API-key door only (no sign-in): the bootstrap keys are minted and printed; callers send X-API-Key, agents connect with a key.").option("--oauth [config]", `Sign-in door only (no API key). Bare = APIblaze-hosted GitHub sign-in. '{"iss","aud","jwks"}' = trust YOUR hosted login's JWTs. '{"provider","clientId","clientSecret"?}' = APIblaze-hosted login page with YOUR OAuth app (github \xB7 google \xB7 microsoft \xB7 facebook \xB7 auth0 \xB7 oidc); the secret is optional except for github and facebook. '{"provider":"oidc","issuer":"https://acme.okta.com/oauth2/default","clientId":"\u2026"}' = any OpenID Connect IdP by issuer (Okta \xB7 Entra \xB7 Keycloak \xB7 Cognito \xB7 \u2026).`).option("--identified", "Require every call to identify its end user (X-End-User-Id from a backend key, or a login token \u2014 a client key alone cannot); unattributed calls are rejected").option("--iam", `Turn IAM on for the ${B.thingShort}'s tenant so users & groups apply to identified calls`).option("--apiversion <version>", `API version to create (e.g. 2.0.0). Creating a new version of a ${B.thingShort} you own adds a version to the existing project.`).option("--tenant <slug>", `Tenant to attach the ${B.thingShort} to (created if new). Omitted \u2192 your team's most-recently-used tenant.`).option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--auto", `Zero prompts, start to finish: generates the ${B.thingShort} name when --name is omitted (from the target host, salted), takes every confirmation as yes, auto-picks the tenant. Implies --yes.`).option("--new-session", "Start a fresh anonymous session (do not group with prior anonymous creates)").option("--json", "Output machine-readable JSON (non-interactive)").option("--compliant [directories...]", `Directory-ready in one command, to the letter of that directory's policies: create the ${B.thingShort} from --target (an OpenAPI URL/file), apply the safe fixes, set up the custom MCP domain (--domain, one CNAME), ask for every listing answer with the spec's proposal pre-filled (cut to the portal's limits), create the reviewer account, write the submission packet. Bare = claude; or claude \xB7 chatgpt \xB7 all. Ends with the compliance table: every rule the listing satisfies and the policy page it comes from. Same as \`${B.cli} connector <spec> --compliant \u2026\`.`).addOption(new import_commander.Option("--policy [directories...]", "Older spelling of --compliant").hideHelp()).option("--domain <host>", "With --compliant: the MCP server's custom hostname (mcp.<your domain>); asked for when missing, suggested from the spec").option("--wait <minutes>", "With --compliant: how long to wait for the custom domain to activate once its CNAME is in place (default 15; 0 = do not wait)").option("--directory-ready", "Make it directory-ready for the Claude and ChatGPT directories: OAuth door, custom-domain checks, listing fields; the readiness report runs after the create and a blocker exits 1").option("--privacy-url <url>", "Directory listing: public https privacy policy").option("--support <email|url>", "Directory listing: support contact").option("--docs-url <url>", "Directory listing: public documentation URL (the developer portal by default)").option("--prompt <text>", "Directory listing: an example prompt (repeat the flag; three are needed)", collect, []).option("--server-name <name>", 'Directory listing: the name (2\u201340 chars, no "MCP"/"Plugin" suffix)').option("--tagline <text>", "Directory listing: one-line tagline").option("--description <text>", "Directory listing: description").option("--icon-url <url>", "Directory listing: square icon (https)").option("--vuln-contact <email|url>", "Directory listing: vulnerability-disclosure contact").option("--attest <id>", "Attest (repeatable): guidelines \xB7 own-backend \xB7 no-financial \xB7 no-genai-media \xB7 no-prompt-injection \xB7 no-conversation-data \xB7 public-docs (Claude's seven) \xB7 no-digital-goods \xB7 content-13-no-ads \xB7 no-restricted-data (ChatGPT)", collect, []).option("--allowed-origin <origin>", "Directory mode: an extra browser origin allowed on the MCP host (repeatable)", collect, []).option("--openai-challenge <token>", "ChatGPT: the domain-verification token the OpenAI portal shows under Plugins \u2192 Connect MCP server").action(async (opts) => {
20098
20112
  try {
20099
- if (opts.policy !== void 0 && opts.policy !== false) {
20113
+ const compliant = opts.compliant ?? opts.policy;
20114
+ if (compliant !== void 0 && compliant !== false) {
20100
20115
  const spec2 = opts.target ?? opts.openapi ?? opts.openapispec;
20101
- if (!spec2) throw new Error(`--policy needs the OpenAPI document: ${B.cli} create --policy --target https://yourdomain.com/openapi.yaml --domain mcp.yourdomain.com`);
20116
+ if (!spec2) throw new Error(`--compliant needs the OpenAPI document: ${B.cli} create --compliant --target https://yourdomain.com/openapi.yaml --domain mcp.yourdomain.com`);
20102
20117
  const { auth: _auth, target: _target, openapi: _openapi, openapispec: _spec, directoryReady: _dr, ...rest } = opts;
20103
- await runConnector(spec2, { ...rest, full: true });
20118
+ await runConnector(spec2, { ...rest, compliant, full: true });
20104
20119
  return;
20105
20120
  }
20106
20121
  await runCreate({ ...opts, openapi: opts.openapi ?? opts.openapispec });
@@ -20112,8 +20127,8 @@ program.command("create").description(`Put an app that is already DEPLOYED behin
20112
20127
  function collect(value, previous) {
20113
20128
  return [...previous ?? [], value];
20114
20129
  }
20115
- program.command("check").description(`Readiness report for the Claude and ChatGPT directories (--directory-ready): what passes, every blocker with its fix, and the ${B.thingShort}'s published URL. Exit 1 while a blocker remains.`).argument("[project]", `The ${B.thingShort} (project name)`).option("--directory-ready", "The directory readiness checks (the only mode today)").option("--policy <directory>", "Which directory's policies to check against: claude \xB7 chatgpt \xB7 all (default claude; repeat or comma-separate)", collect, []).addOption(new import_commander.Option("--platform <list>", "Older spelling of --policy").hideHelp()).option("--stage <stage>", "create (relaxed: docs URL is a warning) \xB7 ready (the submission gate, default)").option("--refresh-snapshot", "Re-take the listing snapshot (after you edited the listing in the portals)").option("--no-probe", "Skip the live probes from this machine (report what the server can see)").option("--team <id|name>", "Team the project is in (defaults to your active team)").option("--apiversion <version>", "API version (defaults to the project's)").option("--json", "Print the raw report JSON (report, rendered text, target)").action(action((project, opts) => runCheck(project, { ...opts, noProbe: opts.probe === false })));
20116
- var connector = program.command("connector").description(`Directory-ready in one command: from a project or straight from an OpenAPI URL \u2014 creates the ${B.thingShort} if needed, applies the safe fixes, sets up the custom MCP domain (one CNAME), pre-fills every listing answer from the spec, creates the reviewer account, and writes the submission packet when every check passes (you submit it to Anthropic and OpenAI yourself)`).argument("[project-or-spec]", `The ${B.thingShort} (project name), or an OpenAPI URL/file to create a directory-ready ${B.thingShort} from first`).option("--domain <host>", "Custom hostname for the MCP server (mcp.<your domain>); asked for when missing, suggested from the spec").option("--wait <minutes>", "How long to wait for the custom domain to activate once its CNAME is in place (default 15; 0 = do not wait)").option("--name <name>", `When creating from a spec: the ${B.thingShort} name (default: derived from the spec host)`).option("--privacy-url <url>", "Public https privacy policy").option("--support <email|url>", "Support contact").option("--docs-url <url>", "Public documentation URL").option("--prompt <text>", "An example prompt (repeatable; three are needed)", collect, []).option("--server-name <name>", 'Listing name (2\u201340 chars, no "MCP"/"Plugin" suffix)').option("--tagline <text>", "One-line tagline").option("--description <text>", "Description").option("--icon-url <url>", "Square icon (https)").option("--vuln-contact <email|url>", "Vulnerability-disclosure contact").option("--attest <id>", "Attest (repeatable): guidelines \xB7 own-backend \xB7 no-financial \xB7 no-genai-media \xB7 no-prompt-injection \xB7 no-conversation-data \xB7 public-docs (Claude's seven) \xB7 no-digital-goods \xB7 content-13-no-ads \xB7 no-restricted-data (ChatGPT). Alone, this only records the attestation.", collect, []).option("--allowed-origin <origin>", "An extra browser origin allowed on the MCP host (repeatable)", collect, []).option("--openai-challenge <token>", "ChatGPT: the domain-verification token the OpenAI portal shows under Plugins \u2192 Connect MCP server; served verbatim at /.well-known/openai-apps-challenge on your MCP domain").option("--full", "Run the full flow even when only --attest was given").option("--policy <directory>", "Which directory's policies this run must satisfy: claude \xB7 chatgpt \xB7 all (default claude; repeat or comma-separate)", collect, []).addOption(new import_commander.Option("--platform <list>", "Older spelling of --policy").hideHelp()).option("--out <dir>", "Where to write connector-packet-<project>.md/.json (default: current directory)").option("-y, --yes", "No questions: apply the flags, report, exit 1 on a blocker").option("--team <id|name>", "Team the project is in (defaults to your active team)").option("--apiversion <version>", "API version (defaults to the project's)").option("--json", "Output machine-readable JSON").action(action(async (project, opts) => {
20130
+ program.command("check").description(`Readiness report for the Claude and ChatGPT directories (--directory-ready): what passes, every blocker with its fix, and the ${B.thingShort}'s published URL. Exit 1 while a blocker remains.`).argument("[project]", `The ${B.thingShort} (project name)`).option("--directory-ready", "The directory readiness checks (the only mode today)").option("--compliant <directory>", "Which directory's policies to check against: claude \xB7 chatgpt \xB7 all (default claude; repeat or comma-separate)", collect, []).addOption(new import_commander.Option("--policy <directory>", "Older spelling of --compliant").hideHelp()).addOption(new import_commander.Option("--platform <list>", "Older spelling of --compliant").hideHelp()).option("--stage <stage>", "create (relaxed: docs URL is a warning) \xB7 ready (the submission gate, default)").option("--refresh-snapshot", "Re-take the listing snapshot (after you edited the listing in the portals)").option("--no-probe", "Skip the live probes from this machine (report what the server can see)").option("--team <id|name>", "Team the project is in (defaults to your active team)").option("--apiversion <version>", "API version (defaults to the project's)").option("--json", "Print the raw report JSON (report, rendered text, target)").action(action((project, opts) => runCheck(project, { ...opts, noProbe: opts.probe === false })));
20131
+ var connector = program.command("connector").description(`Directory-ready in one command: from a project or straight from an OpenAPI URL \u2014 creates the ${B.thingShort} if needed, applies the safe fixes, sets up the custom MCP domain (one CNAME), pre-fills every listing answer from the spec, creates the reviewer account, and writes the submission packet when every check passes (you submit it to Anthropic and OpenAI yourself)`).argument("[project-or-spec]", `The ${B.thingShort} (project name), or an OpenAPI URL/file to create a directory-ready ${B.thingShort} from first`).option("--domain <host>", "Custom hostname for the MCP server (mcp.<your domain>); asked for when missing, suggested from the spec").option("--wait <minutes>", "How long to wait for the custom domain to activate once its CNAME is in place (default 15; 0 = do not wait)").option("--name <name>", `When creating from a spec: the ${B.thingShort} name (default: derived from the spec host)`).option("--privacy-url <url>", "Public https privacy policy").option("--support <email|url>", "Support contact").option("--docs-url <url>", "Public documentation URL").option("--prompt <text>", "An example prompt (repeatable; three are needed)", collect, []).option("--server-name <name>", 'Listing name (2\u201340 chars, no "MCP"/"Plugin" suffix)').option("--tagline <text>", "One-line tagline").option("--description <text>", "Description").option("--icon-url <url>", "Square icon (https)").option("--vuln-contact <email|url>", "Vulnerability-disclosure contact").option("--attest <id>", "Attest (repeatable): guidelines \xB7 own-backend \xB7 no-financial \xB7 no-genai-media \xB7 no-prompt-injection \xB7 no-conversation-data \xB7 public-docs (Claude's seven) \xB7 no-digital-goods \xB7 content-13-no-ads \xB7 no-restricted-data (ChatGPT). Alone, this only records the attestation.", collect, []).option("--allowed-origin <origin>", "An extra browser origin allowed on the MCP host (repeatable)", collect, []).option("--openai-challenge <token>", "ChatGPT: the domain-verification token the OpenAI portal shows under Plugins \u2192 Connect MCP server; served verbatim at /.well-known/openai-apps-challenge on your MCP domain").option("--full", "Run the full flow even when only --attest was given").option("--compliant <directory>", "Which directory's policies this run must satisfy: claude \xB7 chatgpt \xB7 all (default claude; repeat or comma-separate)", collect, []).addOption(new import_commander.Option("--policy <directory>", "Older spelling of --compliant").hideHelp()).addOption(new import_commander.Option("--platform <list>", "Older spelling of --compliant").hideHelp()).option("--out <dir>", "Where to write connector-packet-<project>.md/.json (default: current directory)").option("-y, --yes", "No questions: apply the flags, report, exit 1 on a blocker").option("--team <id|name>", "Team the project is in (defaults to your active team)").option("--apiversion <version>", "API version (defaults to the project's)").option("--json", "Output machine-readable JSON").action(action(async (project, opts) => {
20117
20132
  if (!project) throw new Error(`Which ${B.thingShort}? ${B.cli} connector <project> \xB7 ${B.cli} connector https://\u2026/openapi.yaml \xB7 ${B.cli} connector reviewer <project>`);
20118
20133
  await runConnector(project, opts);
20119
20134
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.21.54",
3
+ "version": "0.21.56",
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",
@@ -65,7 +65,7 @@
65
65
  "typecheck": "tsc --noEmit",
66
66
  "prepublishOnly": "npm run build && npm run typecheck",
67
67
  "release": "bash release.sh",
68
- "test": "node test/tail-inspector.test.js"
68
+ "test": "node test/tail-inspector.test.js && node test/config-missing.test.js"
69
69
  },
70
70
  "dependencies": {
71
71
  "chalk": "^4.1.2",