@everfur/cli 0.2.0 → 0.4.1

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { VERIFY_IDENTITY_MODES, SURFACES, ENVIRONMENTS, BASE_URL_ENV_VAR, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, ALL_CAPABILITIES, expandCapabilities, errorOutcome, describeFailure, failureDetails, countsAsNotOn, diagnoseVerdict, isJsonObject, succeed, CAPABILITY_QUESTION, CAPABILITIES_TREE, WEB_EMBEDDING_QUESTION, PLATFORM_TREE, loadDecisionSpace, fetchContract, contractUnavailableOutcome, loadDecisionSpaceFromContract, parseEnvironment, detectProject, planIntegration, fail, ExitCode, DOCS_KEY, resolveContractPath, validateContractFile, buildSupportBundle, renderSupportBundle, chooseWidgetAuth, DIAGNOSTICS_PATH, diagnoseFailure, parseDiagnostics, buildDiagnosisDocument, PARTNER_KEY, SESSION_TOKEN, resolveCredential, ADMIN_TOKEN, withPrefix, parseOrigin, parseLoaderUrl, parseSurfaces, parseIdentityMode, runVerification, PARTNER_SECRET_KEY, resolveOffline, offlineSuggestions, loadErrorRegistryFromContract, loadErrorRegistry, resolveAfterRegistryMiss, knownCodes, buildExplanationDocument, renderExplanation, resolveEndpoint, isCorrelatableRequestId, EMPTY_REGISTRY, createApiClient, buildSdkExplanationDocument, renderSdkExplanation, buildContractTableExplanationDocument, renderContractTableExplanation, IDENTITY_QUESTION, IDENTITY_TREE, BACKEND_LANGUAGE_QUESTION, SESSION_MINT_TREE, CREDENTIAL_SPECS } from './chunk-CEWWBPPT.js';
2
+ import { VERIFY_IDENTITY_MODES, SURFACES, ENVIRONMENTS, BASE_URL_ENV_VAR, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, ALL_CAPABILITIES, expandCapabilities, errorOutcome, describeFailure, failureDetails, countsAsNotOn, diagnoseVerdict, isJsonObject, succeed, CAPABILITY_QUESTION, CAPABILITIES_TREE, WEB_EMBEDDING_QUESTION, PLATFORM_TREE, loadDecisionSpace, fetchContract, contractUnavailableOutcome, loadDecisionSpaceFromContract, parseEnvironment, checkTenantMode, detectProject, planIntegration, fail, ExitCode, DOCS_KEY, resolveContractPath, validateContractFile, buildSupportBundle, renderSupportBundle, chooseWidgetAuth, DIAGNOSTICS_PATH, diagnoseFailure, parseDiagnostics, buildDiagnosisDocument, PARTNER_KEY, SESSION_TOKEN, resolveCredential, ADMIN_TOKEN, withPrefix, parseOrigin, parseLoaderUrl, parseSurfaces, parseIdentityMode, runVerification, PARTNER_SECRET_KEY, resolveOffline, resolveWithoutRegistry, knownCodes, loadErrorRegistryFromContract, loadErrorRegistry, resolveAfterRegistryMiss, registryAlsoIn, buildExplanationDocument, renderExplanation, alsoInLines, resolveEndpoint, isCorrelatableRequestId, EMPTY_REGISTRY, createApiClient, offlineDocument, renderOffline, IDENTITY_QUESTION, IDENTITY_TREE, BACKEND_LANGUAGE_QUESTION, SESSION_MINT_TREE, CREDENTIAL_SPECS } from './chunk-EBVP6FAD.js';
3
3
  import { isAbsolute, resolve } from 'path';
4
4
 
5
5
  // src/cli/format.ts
@@ -186,6 +186,9 @@ function renderRefusal(document) {
186
186
  lines.push(` ${fact.fact}`);
187
187
  lines.push(` why: ${fact.reason}`);
188
188
  lines.push(` supply: ${fact.supplyWith}`);
189
+ if (fact.requiredWhen !== void 0) {
190
+ lines.push(` only when: ${fact.requiredWhen.fact} is ${fact.requiredWhen.oneOf.join(" or ")}; answer it with that fact`);
191
+ }
189
192
  }
190
193
  lines.push("");
191
194
  lines.push(...renderDetected(document));
@@ -523,6 +526,14 @@ var CONTRACT_FLAG = Object.freeze({
523
526
  summary: "Path to a contract-public projection, used to explain any error code returned.",
524
527
  placeholder: "file"
525
528
  });
529
+ var PLAN_CONTRACT_FLAG = Object.freeze({
530
+ ...CONTRACT_FLAG,
531
+ summary: "Path to a contract-public projection to plan from (its decision trees), read instead of the contract fetched with EVERFUR_DOCS_KEY."
532
+ });
533
+ var EXPLAIN_CONTRACT_FLAG = Object.freeze({
534
+ ...CONTRACT_FLAG,
535
+ summary: "Path to a contract-public projection whose error registry answers the code, read instead of the contract fetched with EVERFUR_DOCS_KEY."
536
+ });
526
537
  var CONTRACT_ENV_VAR = "EVERFUR_CONTRACT_PUBLIC";
527
538
  var DEFAULT_CONTRACT_PATHS = Object.freeze([
528
539
  "contract-public/contract-public.json",
@@ -613,6 +624,7 @@ var POLL_DELAY_MS = 500;
613
624
  var MAX_ATTEMPTS = 20;
614
625
  var VERIFICATION_SCHEMA_VERSION = 1;
615
626
  var VERIFICATION_DOCUMENT_KIND = "everfur.verification";
627
+ var WEB_EMBEDDINGS = Object.freeze(["in-page", "frame"]);
616
628
  var FLAGS = Object.freeze([
617
629
  BASE_URL_FLAG,
618
630
  {
@@ -630,9 +642,16 @@ var FLAGS = Object.freeze([
630
642
  {
631
643
  name: "origin",
632
644
  kind: "string",
633
- summary: "For a web integration: the page origin (https://shop.example.com) the surface is embedded on. Adds the embed fence, CORS exposure, session policy and loader checks.",
645
+ summary: "For a framed web page (the loader and an iframe): the page origin (https://shop.example.com) the frame renders on. Adds the CORS preflight, embed fence, CORS exposure, session policy and loader checks. An in-page integration needs no --origin.",
634
646
  placeholder: "origin"
635
647
  },
648
+ {
649
+ // The same flag name `plan` takes for the same fact (the argv parser knows it as a value flag).
650
+ name: "embedding",
651
+ kind: "string",
652
+ summary: `With --origin: how the page embeds the surface, ${WEB_EMBEDDINGS.join(" or ")}. frame (the default without --project-root) runs every check; in-page (the SDK's React components in the page) reports the embed fence and the loader N/A, since only the frame uses them. Read from --project-root when not given.`,
653
+ placeholder: "mode"
654
+ },
636
655
  {
637
656
  name: "loader-url",
638
657
  kind: "string",
@@ -665,10 +684,12 @@ var FLAGS = Object.freeze([
665
684
  function buildVerificationDocument(verification, baseUrl, cliVersion) {
666
685
  const counts = { passed: 0, failed: 0, skipped: 0 };
667
686
  let gated = 0;
687
+ let notApplicable = 0;
668
688
  for (const entry of verification.steps) {
669
689
  if (entry.result === "PASS") counts.passed += 1;
670
690
  else if (entry.result === "FAIL") counts.failed += 1;
671
691
  else if (entry.result === "GATED") gated += 1;
692
+ else if (entry.result === "N/A") notApplicable += 1;
672
693
  else counts.skipped += 1;
673
694
  }
674
695
  return {
@@ -677,8 +698,9 @@ function buildVerificationDocument(verification, baseUrl, cliVersion) {
677
698
  generatedBy: { name: "@everfur/cli", version: cliVersion },
678
699
  status: verification.status,
679
700
  baseUrl,
680
- // `gated` appears only when a step was GATED, so a document from a run with no surface checks is unchanged.
681
- summary: gated === 0 ? counts : { ...counts, gated },
701
+ // `gated` appears only when a step was GATED, and `notApplicable` only when one was N/A, so a document from a run
702
+ // with neither is unchanged.
703
+ summary: { ...counts, ...gated === 0 ? {} : { gated }, ...notApplicable === 0 ? {} : { notApplicable } },
682
704
  steps: verification.steps.map((entry) => ({
683
705
  id: entry.id,
684
706
  title: entry.title,
@@ -709,6 +731,8 @@ function renderVerification(verification, baseUrl) {
709
731
  lines.push("Nothing failed, but a step was skipped, so the wire was not fully exercised.");
710
732
  } else if (verification.steps.some((entry) => entry.result === "GATED")) {
711
733
  lines.push("Nothing failed. A GATED step is a surface Everfur has not switched on for this tenant, not a fault in the integration.");
734
+ } else if (verification.steps.some((entry) => entry.result === "N/A")) {
735
+ lines.push("Nothing failed. An N/A step is a check that does not apply to this integration, and it was not run.");
712
736
  } else {
713
737
  lines.push(
714
738
  verification.steps.some((entry) => entry.id === "anonymous-identity") ? "Every step passed. The anonymous identity, the verdict read, and the verdict itself are all healthy." : "Every step passed. The session mint, the verdict read, and the verdict itself are all healthy."
@@ -716,6 +740,12 @@ function renderVerification(verification, baseUrl) {
716
740
  }
717
741
  return lines.join("\n");
718
742
  }
743
+ function parseEmbedding2(raw) {
744
+ if (raw === null) return { value: null, errors: [] };
745
+ const value = raw.trim();
746
+ const known2 = WEB_EMBEDDINGS.find((embedding) => embedding === value);
747
+ return known2 === void 0 ? { value: null, errors: [`--embedding must be ${WEB_EMBEDDINGS.join(" or ")}, not "${raw}"`] } : { value: known2, errors: [] };
748
+ }
719
749
  function parseAttempts(raw) {
720
750
  if (raw === null) return { value: DEFAULT_ATTEMPTS, errors: [] };
721
751
  const parsed = Number(raw.trim());
@@ -730,7 +760,7 @@ function parseAttempts(raw) {
730
760
  var verifyCommand = {
731
761
  path: ["verify"],
732
762
  summary: "Exercise the wire: mint a session, read the verdict, assert it is not empty.",
733
- usage: "verify [--base-url <url>] [--user-ref <ref>] [--attempts <n>] [--identity <mode>] [--origin <origin>] [--loader-url <url>] [--surfaces <list>] [--project-root <path>] [--json]",
763
+ usage: "verify [--base-url <url>] [--user-ref <ref>] [--attempts <n>] [--identity <mode>] [--origin <origin>] [--embedding <mode>] [--loader-url <url>] [--surfaces <list>] [--project-root <path>] [--json]",
734
764
  flags: FLAGS,
735
765
  /**
736
766
  * Exactly what `mintSession` resolves: the session token first, else the key pair it mints with locally. Without
@@ -741,6 +771,7 @@ var verifyCommand = {
741
771
  const attempts = parseAttempts(flags.string("attempts"));
742
772
  const originFlag = flags.string("origin");
743
773
  const origin = originFlag === null ? { origin: null, error: null } : parseOrigin(originFlag);
774
+ const embedding = parseEmbedding2(flags.string("embedding"));
744
775
  const loaderFlag = flags.string("loader-url");
745
776
  const loader = loaderFlag === null ? { url: null, error: null } : parseLoaderUrl(loaderFlag);
746
777
  const surfaces = parseSurfaces(flags.string("surfaces"));
@@ -751,6 +782,7 @@ var verifyCommand = {
751
782
  const flagErrors = [
752
783
  ...attempts.errors,
753
784
  ...origin.error === null ? [] : [origin.error],
785
+ ...embedding.errors,
754
786
  ...loader.error === null ? [] : [loader.error],
755
787
  ...surfaces.errors,
756
788
  ...identityMode.errors,
@@ -771,6 +803,7 @@ var verifyCommand = {
771
803
  attempts: attempts.value,
772
804
  pollDelayMs: POLL_DELAY_MS,
773
805
  origin: origin.origin,
806
+ embedding: embedding.value,
774
807
  loaderUrl: loader.url,
775
808
  transport: context.transport,
776
809
  surfaces: surfaces.value ?? [],
@@ -826,7 +859,7 @@ var FLAGS2 = Object.freeze([
826
859
  summary: `The Everfur deployment the generated configuration targets: ${ENVIRONMENTS.join(", ")}. Required for a plan that writes configuration; never assumed.`,
827
860
  placeholder: "name"
828
861
  },
829
- CONTRACT_FLAG
862
+ PLAN_CONTRACT_FLAG
830
863
  ]);
831
864
  function asJsonObject(document) {
832
865
  return JSON.parse(JSON.stringify(document));
@@ -912,8 +945,10 @@ var planCommand = {
912
945
  ...environment.errors
913
946
  ]);
914
947
  }
948
+ const mode = await checkTenantMode({ env: context.env, environment: environment.value, transport: context.transport, clock: context.clock });
949
+ if (mode.kind === "mismatch") return errorOutcome("usage_error", mode.message, mode.details);
915
950
  const detection = detectProject(context.fs, root);
916
- const document = planIntegration({
951
+ const planned = planIntegration({
917
952
  detection,
918
953
  capabilities: supplied.capabilities,
919
954
  allCapabilities: supplied.allCapabilities,
@@ -924,6 +959,7 @@ var planCommand = {
924
959
  space,
925
960
  cliVersion: context.version
926
961
  });
962
+ const document = mode.kind === "not_checked" && planned.status === "ready" ? Object.freeze({ ...planned, warnings: Object.freeze([...planned.warnings, mode.warning]) }) : planned;
927
963
  const json = asJsonObject(document);
928
964
  const text = renderPlan(document);
929
965
  if (document.status === "underspecified") return fail(ExitCode.UNDERSPECIFIED, json, text);
@@ -1115,7 +1151,7 @@ var diagnoseCommand = {
1115
1151
  };
1116
1152
 
1117
1153
  // src/commands/explain-error/index.ts
1118
- var FLAGS5 = Object.freeze([CONTRACT_FLAG]);
1154
+ var FLAGS5 = Object.freeze([EXPLAIN_CONTRACT_FLAG]);
1119
1155
  var POSITIONALS = Object.freeze([
1120
1156
  {
1121
1157
  name: "code",
@@ -1136,7 +1172,7 @@ var explainErrorCommand = {
1136
1172
  return errorOutcome("usage_error", "no error code was given", ["usage: everfur explain-error <code>"]);
1137
1173
  }
1138
1174
  const offline = resolveOffline(code);
1139
- if (offline?.kind === "sdk_member") return offlineOutcome(offline, context.version);
1175
+ if (offline?.kind === "sdk_member") return offlineOutcome(offline, context.version, "no_registry");
1140
1176
  const contractFlag = flags.string("contract");
1141
1177
  const contractPath = findContractPath(context, contractFlag);
1142
1178
  let load;
@@ -1148,10 +1184,13 @@ var explainErrorCommand = {
1148
1184
  ...context.contractCache === void 0 ? {} : { cache: context.contractCache }
1149
1185
  });
1150
1186
  if (fetched.kind === "unavailable") {
1151
- if (offline !== null) return offlineOutcome(offline, context.version);
1152
- const suggestions = offlineSuggestions(code);
1153
- return contractUnavailableOutcome(fetched, [
1154
- ...suggestions.length > 0 ? [`did you mean: ${suggestions.join(", ")}`, "codes are case sensitive; a client branches on the literal"] : [`no code similar to "${code}" exists in the SDK taxonomy or the SDK contract table this CLI carries`]
1187
+ const resolved = resolveWithoutRegistry(code);
1188
+ if (resolved.kind !== "unknown") return offlineOutcome(resolved, context.version, "no_registry");
1189
+ return errorOutcome("unknown_error_code", `"${code}" is not a code this CLI knows`, [
1190
+ ...resolved.suggestions.length > 0 ? [`did you mean: ${resolved.suggestions.join(", ")}`, "codes are case sensitive; a client branches on the literal"] : [`no similar code exists in the ${knownCodes(null).length} codes this CLI carries`],
1191
+ "searched: the SDK taxonomy, the SDK contract table, the MCP tools' codes, the diagnostics reason codes, this CLI's error codes and the SDK's sub-enums",
1192
+ `the live error registry was not read (${fetched.message}); it can list a code these tables predate:`,
1193
+ ...fetched.details.filter((detail) => detail.startsWith("export "))
1155
1194
  ]);
1156
1195
  }
1157
1196
  load = loadErrorRegistryFromContract(fetched.contract);
@@ -1168,11 +1207,11 @@ var explainErrorCommand = {
1168
1207
  const entry = load.registry.lookup(code);
1169
1208
  if (entry === null) {
1170
1209
  const fallback = resolveAfterRegistryMiss(code, load.registry);
1171
- if (fallback.kind !== "unknown") return offlineOutcome(fallback, context.version);
1210
+ if (fallback.kind !== "unknown") return offlineOutcome(fallback, context.version, "registry_miss");
1172
1211
  return errorOutcome("unknown_error_code", `"${code}" is not in the error registry`, [
1173
1212
  ...fallback.suggestions.length > 0 ? [`did you mean: ${fallback.suggestions.join(", ")}`, "codes are case sensitive; a client branches on the literal"] : ["no similar code exists in the registry"],
1174
1213
  `the registry loaded from ${load.path} lists ${load.registry.codes.length} codes`,
1175
- `neither the SDK taxonomy nor the SDK contract table this CLI carries knows it either (${knownCodes(load.registry).length} codes searched in all)`
1214
+ `no other table this CLI carries knows it either: the SDK taxonomy, the SDK contract table, the MCP tools' codes, the diagnostics reason codes, this CLI's error codes and the SDK's sub-enums (${knownCodes(load.registry).length} codes searched in all)`
1176
1215
  ]);
1177
1216
  }
1178
1217
  const input = {
@@ -1181,11 +1220,13 @@ var explainErrorCommand = {
1181
1220
  digest: load.digest,
1182
1221
  cliVersion: context.version
1183
1222
  };
1184
- return succeed(buildExplanationDocument(input), renderExplanation(input));
1223
+ const document = { ...buildExplanationDocument(input), ...registryAlsoIn(code) };
1224
+ return succeed(document, [renderExplanation(input), ...alsoInLines(document)].join("\n"));
1185
1225
  }
1186
1226
  };
1187
- function offlineOutcome(resolution, cliVersion) {
1188
- return resolution.kind === "sdk_member" ? succeed(buildSdkExplanationDocument(resolution.code, cliVersion), renderSdkExplanation(resolution.code)) : succeed(buildContractTableExplanationDocument(resolution.row, cliVersion), renderContractTableExplanation(resolution.row));
1227
+ function offlineOutcome(resolution, cliVersion, context) {
1228
+ const document = offlineDocument(resolution, cliVersion, context);
1229
+ return succeed(document, renderOffline(resolution, document, context));
1189
1230
  }
1190
1231
  function contractOutcome2(outcome) {
1191
1232
  switch (outcome.kind) {
@@ -1233,7 +1274,11 @@ var FLAGS6 = Object.freeze([
1233
1274
  ]);
1234
1275
  var sandboxCreateCommand = {
1235
1276
  path: ["sandbox", "create"],
1236
- summary: "Provision a partner secret key and return a claim URL. Never prints the secret.",
1277
+ /**
1278
+ * What the command does TODAY (ROADMAP.md), not the designed claim flow: the live route returns the secret inline and
1279
+ * no claim URL, so the key is revoked unread and the command exits 8 (SECRET_UNCLAIMABLE).
1280
+ */
1281
+ summary: "Everfur-internal. Mints a partner secret key and, because no claim flow exists to hand it over safely, revokes it again and exits 8. Never prints the secret.",
1237
1282
  usage: "sandbox create --client <client-id> [--label <text>] [--base-url <url>] [--json]",
1238
1283
  flags: FLAGS6,
1239
1284
  /** The one credential this command resolves. It never reads a partner key of either kind. */
@@ -1357,20 +1402,14 @@ function parseArgv(argv) {
1357
1402
  const path = [];
1358
1403
  const flags = /* @__PURE__ */ new Map();
1359
1404
  const errors = [];
1360
- let sawFlag = false;
1361
1405
  for (let index = 0; index < argv.length; index += 1) {
1362
1406
  const token = argv[index];
1363
1407
  if (token === void 0) continue;
1364
1408
  if (token === "--") break;
1365
1409
  if (!isFlagToken(token)) {
1366
- if (sawFlag) {
1367
- errors.push(`unexpected argument "${token}" after flags began`);
1368
- } else {
1369
- path.push(token);
1370
- }
1410
+ path.push(token);
1371
1411
  continue;
1372
1412
  }
1373
- sawFlag = true;
1374
1413
  const equalsAt = token.indexOf("=");
1375
1414
  const rawName = equalsAt === -1 ? token : token.slice(0, equalsAt);
1376
1415
  const inlineValue = equalsAt === -1 ? null : token.slice(equalsAt + 1);
@@ -1477,7 +1516,7 @@ function renderRootHelp(commands, version) {
1477
1516
  lines.push("", "Global flags");
1478
1517
  for (const spec of GLOBAL_FLAGS) lines.push(renderFlag(spec));
1479
1518
  lines.push(" --version".padEnd(FLAG_COLUMN) + "Print the CLI version.");
1480
- lines.push(...environmentBlock(CREDENTIAL_SPECS));
1519
+ lines.push(...environmentBlock(CREDENTIAL_SPECS, "all"));
1481
1520
  lines.push(
1482
1521
  "",
1483
1522
  "Not in this release",
@@ -1486,9 +1525,10 @@ function renderRootHelp(commands, version) {
1486
1525
  );
1487
1526
  return lines.join("\n");
1488
1527
  }
1489
- function environmentBlock(specs) {
1528
+ function environmentBlock(specs, baseUrlFlag) {
1490
1529
  const lines = ["", "Environment"];
1491
- lines.push(` ${BASE_URL_ENV_VAR}`.padEnd(FLAG_COLUMN) + "API base URL. Overridden by --base-url.");
1530
+ const override = baseUrlFlag === "all" ? " Overridden by --base-url on the commands that take it." : baseUrlFlag ? " Overridden by --base-url." : "";
1531
+ lines.push(` ${BASE_URL_ENV_VAR}`.padEnd(FLAG_COLUMN) + `API base URL.${override}`);
1492
1532
  for (const spec of specs) {
1493
1533
  lines.push(` ${spec.envVar}`.padEnd(FLAG_COLUMN) + spec.summary);
1494
1534
  }
@@ -1521,7 +1561,9 @@ function renderCommandHelp(command) {
1521
1561
  lines.push("", "Global flags");
1522
1562
  for (const spec of GLOBAL_FLAGS) lines.push(renderFlag(spec));
1523
1563
  const credentials = command.credentials ?? [];
1524
- if (credentials.length > 0) lines.push(...environmentBlock(credentials));
1564
+ if (credentials.length > 0) {
1565
+ lines.push(...environmentBlock(credentials, command.flags.some((spec) => spec.name === "base-url")));
1566
+ }
1525
1567
  return lines.join("\n");
1526
1568
  }
1527
1569
 
@@ -1561,8 +1603,10 @@ async function runCli(options) {
1561
1603
  const specs = match.command.positionals ?? [];
1562
1604
  if (positionals.length > specs.length) {
1563
1605
  const surplus = positionals[specs.length] ?? "";
1606
+ const flagErrors = bindFlags(match.command.flags, parsed);
1564
1607
  return formatOutcome(
1565
1608
  errorOutcome("usage_error", `unexpected argument "${surplus}" after "${commandName(match.command)}"`, [
1609
+ ...flagErrors.ok ? [] : flagErrors.errors,
1566
1610
  `usage: everfur ${match.command.usage}`
1567
1611
  ]),
1568
1612
  format
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { buildServer } from './chunk-WCH6AI6K.js';
2
+ import { buildServer } from './chunk-QCXZJW5C.js';
3
3
  import { createServer } from 'http';
4
4
  import { timingSafeEqual } from 'crypto';
5
5
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';