@varde-flyt/vfac 0.15.0 → 0.15.2

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 (3) hide show
  1. package/README.md +23 -13
  2. package/dist/vfac.mjs +245 -29
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -26,10 +26,10 @@ vfac guide
26
26
  vfac whoami
27
27
  ```
28
28
 
29
- `doctor` checks the endpoint, which Project this terminal has selected, the API,
30
- whether this client is new enough for the platform it is pointed at, how this
31
- terminal is signed in and what that credential reaches, in that order, and reports
32
- the first thing that is actually wrong. Both ways of signing in count: a Context
29
+ `doctor` checks the endpoint, which Project this terminal has selected and which
30
+ one this command will act in, the API, whether this client is new enough for the
31
+ platform it is pointed at, how this terminal is signed in and what that credential
32
+ reaches, in that order, and reports the first thing that is actually wrong. Both ways of signing in count: a Context
33
33
  Gate exchanged from `VFAC_CREDENTIAL_ID` and `VFAC_CREDENTIAL_SECRET`, or a
34
34
  sign-in from `vfac login` that is still valid. A platform declares the oldest client it supports;
35
35
  below that, `doctor` fails and says so before sending your credential anywhere.
@@ -37,7 +37,10 @@ below that, `doctor` fails and says so before sending your credential anywhere.
37
37
  The selected Project and the Projects your credential reaches are two different
38
38
  lines, and reading the second as the first is the mistake worth naming: a command
39
39
  with no `--project` uses the SELECTED one, and having access to a Project is not
40
- the same as being in it. `doctor` also says so when it has just started a fresh
40
+ the same as being in it. `--project` overrides the selection for one command and
41
+ writes nothing, so `vfac doctor --project prj_…` reports the Project it would act
42
+ in, says the flag is where that came from, and still names what your context has
43
+ selected. `doctor` also says so when it has just started a fresh
41
44
  config because the old one could not be read — the endpoint and Project you had
42
45
  set are gone in that case, and the unreadable file is kept beside it rather than
43
46
  discarded.
@@ -68,7 +71,7 @@ vfac context set --project <projectId>
68
71
 
69
72
  ```sh
70
73
  vfac product list
71
- vfac product show <productId>
74
+ vfac product show <productId> # every setting, and when you must answer it
72
75
  vfac manifest init --product <productId> --name "My resource" > resource.yaml
73
76
  ```
74
77
 
@@ -97,9 +100,12 @@ the plan describes a different request.
97
100
 
98
101
  `--secret` names a root setting by its name, and a setting inside a group by its
99
102
  path — `--secret /upstreamAuth/clientSecret=env:UPSTREAM_CLIENT_SECRET` for the
100
- `clientSecret` under `upstreamAuth`. `vfac product show` and `vfac manifest init`
101
- print each required secret in that form. A path never names an item of a list:
102
- no secret is declared there.
103
+ `clientSecret` under `upstreamAuth`. `vfac product show` prints that form beside
104
+ EVERY credential the Product declares, with a line saying whether it is required
105
+ of everyone, required when another setting holds a particular value, or required
106
+ because another setting was answered — `dependentRequired` is directional, and
107
+ the line says which way it runs; `vfac manifest init` names the ones required
108
+ outright. A path never names an item of a list: no secret is declared there.
103
109
 
104
110
  `requiredSecrets` in a plan is what the Product requires for the configuration in
105
111
  your document — not a list of what the platform is missing. It names a credential
@@ -109,7 +115,9 @@ act on. `secretsToWrite` is the field that says what this apply would write.
109
115
  A credential the Product requires for your document — outright, or because of
110
116
  what a setting is set to — that the run neither supplies nor the resource already
111
117
  holds is refused by `plan` and `apply` alike, before anything is written: the
112
- error names every one, by the path `--secret` takes, and the command exits 1.
118
+ error names every one, by the path `--secret` takes, and the command exits 1. So
119
+ is a credential the run DOES supply whose value the Product's own declaration
120
+ refuses: the error names the setting and the bound it missed, never the value.
113
121
  `vfac validate` carries no secrets and refuses none; it lists them.
114
122
 
115
123
  ### `metadata.key` is the name you choose
@@ -229,9 +237,11 @@ it.
229
237
  ## For an agent or a pipeline
230
238
 
231
239
  Every command takes `--output json`, and it is the same data rather than a
232
- prettier subset — the human format of `vfac product show` prints a readable
233
- selection on purpose and says so at the foot of its output, and the JSON carries
234
- the whole Product contract.
240
+ prettier subset. The human format of `vfac product show` lists every setting the
241
+ Product declares and says when each must be answered; what it leaves to the JSON
242
+ form is the prose and the value constraints — descriptions, patterns, bounds —
243
+ and it says so at the foot of its output. The JSON carries the whole Product
244
+ contract.
235
245
 
236
246
  ```sh
237
247
  vfac product show <productId> --output json
package/dist/vfac.mjs CHANGED
@@ -13207,6 +13207,14 @@ function canonicalJson(value) {
13207
13207
  function immediateContainer(node) {
13208
13208
  return node.containers[node.containers.length - 1] ?? null;
13209
13209
  }
13210
+ function resolvedGroup(node) {
13211
+ for (const container of node.containers) {
13212
+ const declared = container.property.group;
13213
+ if (declared !== void 0)
13214
+ return declared;
13215
+ }
13216
+ return node.property.group;
13217
+ }
13210
13218
  function underList(node) {
13211
13219
  return node.containers.some((container) => container.kind === "array");
13212
13220
  }
@@ -14210,6 +14218,116 @@ function exampleResourceKey(name) {
14210
14218
  }
14211
14219
  var DEPLOYMENT_EXAMPLE_GUARANTEE = "Product-owned configuration is complete and E2E-proven; tenant-specific secrets and Resource/provider selections are supplied by the tenant.";
14212
14220
 
14221
+ // ../product-configuration/dist/configuration-requirements.js
14222
+ function configurationRequirements(configuration) {
14223
+ if (configuration === void 0 || configuration === null)
14224
+ return [];
14225
+ const schema = configuration;
14226
+ const rootRequired = new Set(schema.required ?? []);
14227
+ const carriers = new Map(conditionCarriers(schema).map((carrier) => [carrier.path, carrier]));
14228
+ const secretPaths = new Set(configurationLeaves(schema).filter((leaf) => isSecretProperty(leaf.property)).map((leaf) => String(leaf.path)));
14229
+ return configurationLeaves(schema).map((leaf) => {
14230
+ const parent = immediateContainer(leaf);
14231
+ const immediatelyRequired = parent === null ? rootRequired.has(leaf.name) : parent.required.includes(leaf.name);
14232
+ const chainRequired = containersRequiredToRoot(schema, leaf);
14233
+ const always = requiredToRoot(schema, leaf);
14234
+ const secret = isSecretProperty(leaf.property);
14235
+ const condition = conditionFor(schema, leaf.path, leaf.containers, carriers);
14236
+ const clause = clauseEdges(schema, leaf, secretPaths);
14237
+ const requiredness = always ? "always" : condition !== null && condition.carrier === "setting" ? "when-value" : immediatelyRequired && !chainRequired ? "when-group" : clause.requiredBy.length > 0 ? "with-siblings" : "optional";
14238
+ const containers = leaf.containers.map((container, index) => settingContainer(container, index === 0 ? rootRequired : new Set(leaf.containers[index - 1]?.required ?? [])));
14239
+ const list = [...containers].reverse().find((container) => container.kind === "array") ?? null;
14240
+ return {
14241
+ path: leaf.path,
14242
+ printablePath: printableConfigurationPath(leaf.path),
14243
+ title: leaf.property.title,
14244
+ description: leaf.property.description,
14245
+ type: leaf.property.type,
14246
+ secret,
14247
+ requiredness,
14248
+ // NEVER A SECRET'S, even though the declaration rules already refuse one.
14249
+ // Being wrong in that direction prints a credential, which is the same
14250
+ // paranoia `product-instance-example.ts` documents for its own reader.
14251
+ defaultValue: secret ? void 0 : declaredDefault(leaf.property),
14252
+ allowed: secret ? [] : allowedValues(leaf.property),
14253
+ condition,
14254
+ requires: clause.requires,
14255
+ requiredBy: clause.requiredBy,
14256
+ containers,
14257
+ perRow: underList(leaf),
14258
+ list,
14259
+ group: resolvedGroup(leaf)
14260
+ };
14261
+ });
14262
+ }
14263
+ function allowedValues(property) {
14264
+ const leaf = property;
14265
+ return [...leaf.enum ?? leaf.items?.enum ?? []];
14266
+ }
14267
+ function conditionFor(schema, path, containers, carriers) {
14268
+ const own = carriers.get(path);
14269
+ const ancestor = [...containers].reverse().map((container) => carriers.get(container.path)).find((carrier2) => carrier2 !== void 0 && carrier2.kind === "container");
14270
+ const carrier = own?.kind === "leaf" ? own : ancestor;
14271
+ if (carrier === void 0)
14272
+ return null;
14273
+ const setting = parseConfigurationPath(conditionSetting(carrier.condition));
14274
+ if (setting === null)
14275
+ return null;
14276
+ return {
14277
+ carrier: carrier.kind === "leaf" ? "setting" : "container",
14278
+ carrierPath: carrier.path,
14279
+ setting,
14280
+ printableSetting: printableConfigurationPath(setting),
14281
+ settingLabel: labelOfPath(schema, setting),
14282
+ anyOf: [...carrier.condition.anyOf],
14283
+ anyRow: "path" in carrier.condition && carrier.condition.anyRow === true
14284
+ };
14285
+ }
14286
+ function clauseEdges(schema, leaf, secretPaths) {
14287
+ const parent = immediateContainer(leaf);
14288
+ const scope = parent === null ? schema : parent.kind === "array" ? parent.property.items ?? parent.property : parent.property;
14289
+ const requires = /* @__PURE__ */ new Set();
14290
+ const requiredBy = /* @__PURE__ */ new Set();
14291
+ for (const [trigger, dependents] of Object.entries(scope.dependentRequired ?? {})) {
14292
+ if (trigger === leaf.name)
14293
+ for (const dependent of dependents)
14294
+ requires.add(dependent);
14295
+ else if (dependents.includes(leaf.name))
14296
+ requiredBy.add(trigger);
14297
+ }
14298
+ const order = Object.keys(scope.properties ?? {});
14299
+ const members = (names) => [...names].sort((a, b) => order.indexOf(a) - order.indexOf(b)).flatMap((name) => {
14300
+ const path = memberPath(leaf, name);
14301
+ return path === null ? [] : [
14302
+ {
14303
+ path,
14304
+ printablePath: printableConfigurationPath(path),
14305
+ secret: secretPaths.has(String(path))
14306
+ }
14307
+ ];
14308
+ });
14309
+ return { requires: members(requires), requiredBy: members(requiredBy) };
14310
+ }
14311
+ function memberPath(leaf, name) {
14312
+ const parent = immediateContainer(leaf);
14313
+ return parent === null ? configurationPath(name) : configurationPath(...pathSegments(parent.path), name);
14314
+ }
14315
+ function settingContainer(container, requiredByParent) {
14316
+ return {
14317
+ path: container.path,
14318
+ printablePath: printableConfigurationPath(container.path),
14319
+ title: container.property.title,
14320
+ kind: container.kind,
14321
+ required: requiredByParent.has(container.name)
14322
+ };
14323
+ }
14324
+
14325
+ // ../product-configuration/dist/manifest-yaml.js
14326
+ var import_yaml = __toESM(require_dist(), 1);
14327
+ function manifestYaml(document) {
14328
+ return (0, import_yaml.stringify)(document, { compat: "yaml-1.1" });
14329
+ }
14330
+
14213
14331
  // ../platform-schemas/dist/index.js
14214
14332
  var TENANT_KEY_REGEX = /^[a-z][a-z0-9-]{2,23}[a-z0-9]$/;
14215
14333
  var RESERVED_TENANT_KEYS = /* @__PURE__ */ new Set([
@@ -17868,7 +17986,7 @@ withJsonSchemaAnnex(ProductSubmissionSchema, PUBLISH_RULE_ANNEX);
17868
17986
  withJsonSchemaAnnex(ProductSubmissionV2Schema, PUBLISH_RULE_ANNEX);
17869
17987
 
17870
17988
  // src/manifest.ts
17871
- var import_yaml = __toESM(require_dist(), 1);
17989
+ var import_yaml2 = __toESM(require_dist(), 1);
17872
17990
  function readManifest(path) {
17873
17991
  let text;
17874
17992
  try {
@@ -17878,7 +17996,7 @@ function readManifest(path) {
17878
17996
  }
17879
17997
  let parsed;
17880
17998
  try {
17881
- parsed = (0, import_yaml.parse)(text, { prettyErrors: false });
17999
+ parsed = (0, import_yaml2.parse)(text, { prettyErrors: false });
17882
18000
  } catch (error) {
17883
18001
  return {
17884
18002
  ok: false,
@@ -18041,8 +18159,14 @@ async function ensureSession(args) {
18041
18159
  }
18042
18160
  };
18043
18161
  }
18162
+ function resolveProjectWithSource(flag, stored) {
18163
+ if (flag !== void 0) return { value: flag, source: "--project" };
18164
+ const selected = stored.context?.projectId;
18165
+ if (selected !== void 0) return { value: selected, source: "the stored context" };
18166
+ return null;
18167
+ }
18044
18168
  function resolveProject(flag, stored) {
18045
- return flag ?? stored.context?.projectId ?? null;
18169
+ return resolveProjectWithSource(flag, stored)?.value ?? null;
18046
18170
  }
18047
18171
 
18048
18172
  // src/operations.ts
@@ -18692,13 +18816,18 @@ async function runDoctor(parsed) {
18692
18816
  detail: `${endpoint} (from ${resolved.source})`,
18693
18817
  facts: { endpoint, source: resolved.source }
18694
18818
  });
18695
- const selected = stored.context?.projectId ?? null;
18819
+ const project = resolveProjectWithSource(parsed.flags["project"], stored);
18820
+ const storedProjectId = stored.context?.projectId ?? null;
18696
18821
  checks.push({
18697
18822
  name: "Project",
18698
18823
  ok: true,
18699
- note: selected === null,
18700
- detail: selected === null ? "none selected. Commands need `--project prj_\u2026`, or run `vfac context set --project prj_\u2026` once. This is not a fault \u2014 it is what a pipeline that names the Project per command looks like \u2014 but it is NOT the same thing as which Projects your credential reaches." : `${selected} (selected in this context; which Projects the credential reaches is a separate question)`,
18701
- facts: { selectedProjectId: selected }
18824
+ note: storedProjectId === null,
18825
+ detail: project === null ? "none selected. Commands need `--project prj_\u2026`, or run `vfac context set --project prj_\u2026` once. This is not a fault \u2014 it is what a pipeline that names the Project per command looks like \u2014 but it is NOT the same thing as which Projects your credential reaches." : project.source === "--project" ? `${project.value} (from --project, for this command only). ` + (storedProjectId === null ? "Nothing is selected in this context, so every other command needs the flag too. " : `The stored context still selects ${storedProjectId}, which is what a command with no --project uses. `) + "Which Projects the credential reaches is a separate question." : `${project.value} (from the stored context; a command with no --project uses it, and which Projects the credential reaches is a separate question)`,
18826
+ facts: {
18827
+ selectedProjectId: project?.value ?? null,
18828
+ source: project?.source ?? null,
18829
+ storedProjectId
18830
+ }
18702
18831
  });
18703
18832
  const probe = await probeMachineApi({ endpoint });
18704
18833
  if (!probe.ok) {
@@ -18733,13 +18862,13 @@ async function runDoctor(parsed) {
18733
18862
  return report2(checks, json);
18734
18863
  }
18735
18864
  const access = who.data.access;
18736
- const projects = access.projects.map((project) => project.projectId);
18865
+ const projects = access.projects.map((project2) => project2.projectId);
18737
18866
  const mutating = access.projects.filter(
18738
- (project) => project.permissions.some((permission) => !permission.endsWith(".read"))
18867
+ (project2) => project2.permissions.some((permission) => !permission.endsWith(".read"))
18739
18868
  );
18740
18869
  const organizationWide = access.tenantPermissions.includes("project.read");
18741
18870
  const alsoOrganizationWide = access.tenantPermissions.length > 0 ? `Organization-wide it holds ${access.tenantPermissions.join(", ")}, which reaches no Project. ` : "";
18742
- const perProject = projects.length === 0 ? "" : `. Granted per Project: ${projects.join(", ")}` + (mutating.length > 0 ? ` \u2014 you can deploy in ${mutating.map((project) => project.projectId).join(", ")}` : " \u2014 read-only there");
18871
+ const perProject = projects.length === 0 ? "" : `. Granted per Project: ${projects.join(", ")}` + (mutating.length > 0 ? ` \u2014 you can deploy in ${mutating.map((project2) => project2.projectId).join(", ")}` : " \u2014 read-only there");
18743
18872
  checks.push(
18744
18873
  organizationWide ? {
18745
18874
  name: "Access",
@@ -18898,7 +19027,6 @@ function report2(checks, json) {
18898
19027
  }
18899
19028
 
18900
19029
  // src/commands/export.ts
18901
- var import_yaml2 = __toESM(require_dist(), 1);
18902
19030
  async function runExport(parsed) {
18903
19031
  if (parsed.command[1] !== "product-instance" && parsed.command[1] !== "resource") {
18904
19032
  process.stderr.write("Usage: vfac export product-instance pri_\u2026\n");
@@ -18926,7 +19054,7 @@ async function runExport(parsed) {
18926
19054
  if (!result.ok) return printError(result.error, parsed.flags["output"] === "json");
18927
19055
  process.stdout.write(
18928
19056
  parsed.flags["output"] === "json" ? `${JSON.stringify(result.data, null, 2)}
18929
- ` : (0, import_yaml2.stringify)(result.data)
19057
+ ` : manifestYaml(result.data)
18930
19058
  );
18931
19059
  const metadata = result.data["metadata"];
18932
19060
  const hasKey = typeof metadata === "object" && metadata !== null && "key" in metadata;
@@ -19338,16 +19466,44 @@ function renderProduct(product) {
19338
19466
  (profile) => `${profile.id}${profile.default ? " (default)" : ""}`
19339
19467
  );
19340
19468
  lines.push(` profile ${profiles.join(", ") || "(none declared)"}`);
19341
- const seed = seedConfiguration(product.configuration);
19342
- if (seed.open.length > 0 || seed.requiredSecrets.length > 0) {
19343
- lines.push("", " Required configuration");
19344
- for (const name of seed.requiredSecrets) {
19345
- lines.push(` ${name} (secret \u2014 pass --secret ${name}=env:VAR)`);
19346
- }
19347
- for (const setting of seed.open) {
19348
- lines.push(` ${setting.label} ${setting.type ?? ""}`.trimEnd());
19469
+ const settings = configurationRequirements(product.configuration);
19470
+ if (settings.length > 0) {
19471
+ lines.push("", ` Settings (${settings.length})`);
19472
+ for (const setting of settings) {
19473
+ lines.push(
19474
+ ` ${identityOf(setting)} ${setting.secret ? "secret" : setting.type} ${TOKEN[setting.requiredness]}`
19475
+ );
19476
+ const requirement = requirementOf(setting);
19477
+ if (requirement !== null) lines.push(...continuation(requirement));
19478
+ if (setting.requires.length > 0 && !mutualClause(setting)) {
19479
+ lines.push(
19480
+ ...continuation(
19481
+ `answering it also requires ${listOf(
19482
+ setting.requires.map((member) => identityOf(member)),
19483
+ "and"
19484
+ )}`
19485
+ )
19486
+ );
19487
+ }
19488
+ if (setting.condition?.carrier === "container") {
19489
+ lines.push(
19490
+ ...continuation(
19491
+ `${printableConfigurationPath(setting.condition.carrierPath)} is required when ${setting.condition.printableSetting} is ${valueList(setting.condition.anyOf)}`
19492
+ )
19493
+ );
19494
+ }
19495
+ if (setting.secret) {
19496
+ lines.push(...continuation(`--secret ${String(setting.path)}=env:VAR`));
19497
+ }
19498
+ if (setting.allowed.length > 0) {
19499
+ lines.push(...continuation(`one of ${valueList(setting.allowed)}`));
19500
+ }
19501
+ if (setting.defaultValue !== void 0) {
19502
+ lines.push(...continuation(`default ${JSON.stringify(setting.defaultValue)}`));
19503
+ }
19349
19504
  }
19350
19505
  }
19506
+ const seed = seedConfiguration(product.configuration);
19351
19507
  const together = seed.groups.filter((group) => group.within !== null);
19352
19508
  if (together.length > 0) {
19353
19509
  lines.push("", " Settings that go together");
@@ -19397,9 +19553,66 @@ function renderProduct(product) {
19397
19553
  lines.push(` ${condition.key ?? ""} ${condition.displayName ?? ""}`.trimEnd());
19398
19554
  }
19399
19555
  }
19400
- lines.push("", " `--output json` carries the whole declaration, this is a readable subset.");
19556
+ lines.push(
19557
+ "",
19558
+ " `--output json` carries the whole declaration. Every setting is listed above;",
19559
+ " descriptions and value constraints are in the JSON form."
19560
+ );
19561
+ if (settings.some((setting) => setting.secret)) {
19562
+ lines.push(" A credential is supplied per apply and never written into a manifest.");
19563
+ }
19401
19564
  return lines.join("\n");
19402
19565
  }
19566
+ var TOKEN = {
19567
+ always: "required",
19568
+ "when-value": "required-when",
19569
+ "when-group": "required-if",
19570
+ "with-siblings": "required-with",
19571
+ optional: "optional"
19572
+ };
19573
+ var MAX_LISTED_VALUES = 8;
19574
+ var identityOf = (setting) => setting.secret ? String(setting.path) : setting.printablePath;
19575
+ function valueList(values) {
19576
+ const shown = values.slice(0, MAX_LISTED_VALUES).map((value) => JSON.stringify(value));
19577
+ const rest = values.length - shown.length;
19578
+ const joined = shown.length === 2 ? shown.join(" or ") : shown.join(", ");
19579
+ return rest > 0 ? `${joined} and ${rest} more` : joined;
19580
+ }
19581
+ function requirementOf(setting) {
19582
+ const list = setting.list === null ? "" : ` ${setting.list.printablePath}`;
19583
+ switch (setting.requiredness) {
19584
+ case "always":
19585
+ return setting.perRow ? `in every${list} entry` : null;
19586
+ case "when-value": {
19587
+ const condition = setting.condition;
19588
+ if (condition === null) return null;
19589
+ const rows = condition.anyRow ? " in any entry" : "";
19590
+ return `when ${condition.printableSetting} is ${valueList(condition.anyOf)}${rows}`;
19591
+ }
19592
+ case "when-group": {
19593
+ const owner = setting.containers[setting.containers.length - 1];
19594
+ const base = `once ${owner?.printablePath ?? ""} is configured`;
19595
+ return setting.perRow ? `${base}, in every${list} entry` : base;
19596
+ }
19597
+ case "with-siblings": {
19598
+ const names = (members) => members.map((member) => identityOf(member));
19599
+ const triggers = names(setting.requiredBy);
19600
+ return mutualClause(setting) ? `with ${names(setting.requires).join(", ")}` : `once ${listOf(triggers, "or")} is set`;
19601
+ }
19602
+ case "optional":
19603
+ return setting.perRow ? `one per${list} entry` : null;
19604
+ }
19605
+ }
19606
+ function mutualClause(setting) {
19607
+ const requires = setting.requires.map((member) => String(member.path)).sort();
19608
+ const requiredBy = setting.requiredBy.map((member) => String(member.path)).sort();
19609
+ return requires.length > 0 && requires.join("\0") === requiredBy.join("\0");
19610
+ }
19611
+ function listOf(items, conjunction) {
19612
+ if (items.length <= 1) return items[0] ?? "";
19613
+ return `${items.slice(0, -1).join(", ")} ${conjunction} ${items[items.length - 1]}`;
19614
+ }
19615
+ var continuation = (sentence) => wrap(sentence, 72).map((line) => ` ${line}`);
19403
19616
  function wrap(text, width) {
19404
19617
  const lines = [];
19405
19618
  for (const paragraph of text.split(/\n\s*\n/)) {
@@ -19582,7 +19795,6 @@ Only approve a code you are looking at right now. Waiting\u2026
19582
19795
  }
19583
19796
 
19584
19797
  // src/commands/manifest-init.ts
19585
- var import_yaml3 = __toESM(require_dist(), 1);
19586
19798
  async function runManifestInit(parsed) {
19587
19799
  const verb = parsed.command[1];
19588
19800
  if (verb !== "init") {
@@ -19687,7 +19899,7 @@ ${where}`);
19687
19899
  }
19688
19900
  process.stdout.write(
19689
19901
  parsed.flags["output"] === "json" ? `${JSON.stringify(document, null, 2)}
19690
- ` : (0, import_yaml3.stringify)(document)
19902
+ ` : manifestYaml(document)
19691
19903
  );
19692
19904
  if (notes.length > 0) {
19693
19905
  process.stderr.write("\nStill yours to fill in:\n");
@@ -19901,12 +20113,16 @@ Options
19901
20113
  --output json, for a pipeline that reads the answer
19902
20114
 
19903
20115
  Diagnosing
19904
- vfac doctor checks the endpoint, the machine API, whether this client is new
19905
- enough for that platform, the credential and what that credential
19906
- can reach \u2014 in that order, so the first thing that is actually
19907
- wrong is the thing it reports. It ends by asking npm whether a
19908
- newer vfac has been published, which is a note and never a
19909
- failure. Set VFAC_NO_UPDATE_CHECK to skip that one request
20116
+ vfac doctor checks the config it could read, the endpoint, which Project this
20117
+ terminal has selected and which one the command will act in, the
20118
+ machine API, whether this client is new enough for that platform,
20119
+ how this terminal is signed in and what that credential can reach
20120
+ \u2014 in that order, so the first thing that is actually wrong is the
20121
+ thing it reports. Pass --project to ask about the Project a
20122
+ command would name, which changes nothing on disk. It ends by
20123
+ asking npm whether a newer vfac has been published, which is a
20124
+ note and never a failure. Set VFAC_NO_UPDATE_CHECK to skip that
20125
+ one request
19910
20126
  vfac whoami what this credential is, and which Project scopes it has been
19911
20127
  GRANTED. A credential scoped to one Project deploys fine and
19912
20128
  cannot make organization-wide reads \u2014 this is where that is said.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varde-flyt/vfac",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "description": "Deploy and manage Varde Flyt Products from a manifest.",
5
5
  "repository": {
6
6
  "type": "git",