@varde-flyt/vfac 0.9.0 → 0.10.0
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.
- package/README.md +5 -3
- package/dist/vfac.mjs +241 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,9 +27,11 @@ vfac whoami
|
|
|
27
27
|
```
|
|
28
28
|
|
|
29
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,
|
|
31
|
-
|
|
32
|
-
thing that is actually wrong.
|
|
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
|
|
33
|
+
Gate exchanged from `VFAC_CREDENTIAL_ID` and `VFAC_CREDENTIAL_SECRET`, or a
|
|
34
|
+
sign-in from `vfac login` that is still valid. A platform declares the oldest client it supports;
|
|
33
35
|
below that, `doctor` fails and says so before sending your credential anywhere.
|
|
34
36
|
|
|
35
37
|
The selected Project and the Projects your credential reaches are two different
|
package/dist/vfac.mjs
CHANGED
|
@@ -7793,7 +7793,7 @@ async function ensureSession(args) {
|
|
|
7793
7793
|
const endpoint = checked.endpoint;
|
|
7794
7794
|
const credential = credentialFromEnvironment();
|
|
7795
7795
|
if (args.forceExchange !== true && sessionUsable(stored.session, endpoint, args.now ?? /* @__PURE__ */ new Date(), credential?.keyId)) {
|
|
7796
|
-
return { ok: true, ready: { endpoint, token: stored.session.token, stored } };
|
|
7796
|
+
return { ok: true, ready: { endpoint, token: stored.session.token, stored, via: "stored" } };
|
|
7797
7797
|
}
|
|
7798
7798
|
const signedIn = await signIn({
|
|
7799
7799
|
endpoint,
|
|
@@ -7802,7 +7802,16 @@ async function ensureSession(args) {
|
|
|
7802
7802
|
if (!signedIn.ok) return { ok: false, message: signedIn.message };
|
|
7803
7803
|
const next = { ...stored, session: signedIn.session };
|
|
7804
7804
|
writeStored(next);
|
|
7805
|
-
return {
|
|
7805
|
+
return {
|
|
7806
|
+
ok: true,
|
|
7807
|
+
ready: {
|
|
7808
|
+
endpoint,
|
|
7809
|
+
token: signedIn.session.token,
|
|
7810
|
+
stored: next,
|
|
7811
|
+
via: "exchanged",
|
|
7812
|
+
...signedIn.gate ? { gate: signedIn.gate } : {}
|
|
7813
|
+
}
|
|
7814
|
+
};
|
|
7806
7815
|
}
|
|
7807
7816
|
function resolveProject(flag, stored) {
|
|
7808
7817
|
return flag ?? stored.context?.projectId ?? null;
|
|
@@ -7922,8 +7931,9 @@ function renderOperation(operationId, data) {
|
|
|
7922
7931
|
const retryable = failure["retryable"];
|
|
7923
7932
|
if (typeof retryable === "boolean") {
|
|
7924
7933
|
const again = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "`vfac lifecycle upgrade <pri_\u2026>`" : "`vfac lifecycle reconcile <pri_\u2026>`";
|
|
7934
|
+
const changeIt = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "" : ", or `vfac apply` a corrected manifest";
|
|
7925
7935
|
lines.push(
|
|
7926
|
-
retryable ? ` Another attempt can succeed: ${again}.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}.`
|
|
7936
|
+
retryable ? ` Another attempt can succeed: ${again}.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}${changeIt}.`
|
|
7927
7937
|
);
|
|
7928
7938
|
}
|
|
7929
7939
|
}
|
|
@@ -12224,6 +12234,16 @@ var ConfigurationGroupSchema = external_exports.enum([
|
|
|
12224
12234
|
]);
|
|
12225
12235
|
var CONFIGURATION_PROPERTY_NAME_REGEX = /^[a-z][a-zA-Z0-9]{1,47}$/;
|
|
12226
12236
|
var PropertyNameSchema = external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX, "Configuration property names must be camelCase, start with a lowercase letter, and be 2\u201348 characters.");
|
|
12237
|
+
var ValueConditionSchema = external_exports.object({
|
|
12238
|
+
/**
|
|
12239
|
+
* The setting, as a canonical path — or a bare root key, which is the same
|
|
12240
|
+
* string at depth 1. With `anyRow`, a DECLARATION path: `/servers/kind`
|
|
12241
|
+
* names the setting each row answers, never one row's answer.
|
|
12242
|
+
*/
|
|
12243
|
+
path: external_exports.string().min(1).max(400),
|
|
12244
|
+
anyOf: external_exports.array(external_exports.string().min(1).max(200)).min(1).max(20),
|
|
12245
|
+
anyRow: external_exports.boolean().optional()
|
|
12246
|
+
}).strict();
|
|
12227
12247
|
var TitleSchema = external_exports.string().min(2).max(80).describe("Customer-facing field label.");
|
|
12228
12248
|
var V1_LEAF_BOUNDS = {
|
|
12229
12249
|
help: 400,
|
|
@@ -12280,7 +12300,24 @@ function leafShapes(bounds) {
|
|
|
12280
12300
|
* without the other a validation error.
|
|
12281
12301
|
*/
|
|
12282
12302
|
writeOnly: external_exports.boolean().optional(),
|
|
12283
|
-
"x-secret": external_exports.boolean().optional()
|
|
12303
|
+
"x-secret": external_exports.boolean().optional(),
|
|
12304
|
+
/**
|
|
12305
|
+
* The setting VALUE that makes this credential necessary.
|
|
12306
|
+
*
|
|
12307
|
+
* WHY IT CANNOT BE `dependentRequired`. That mechanism is keyed on
|
|
12308
|
+
* PRESENCE — "if the author answered X, they must also answer Y" — and
|
|
12309
|
+
* says so in as many words: "PRESENCE, NEVER A VALUE. A caller passes
|
|
12310
|
+
* NAMES." A Product whose credential is needed only when a setting has one
|
|
12311
|
+
* particular value could not say so, so `vfac plan`'s `requiredSecrets`
|
|
12312
|
+
* could not warn before a deploy, and the refusal arrived at apply.
|
|
12313
|
+
*
|
|
12314
|
+
* ON A SECRET ONLY, and that is narrower than the mechanism could be. A
|
|
12315
|
+
* value-conditioned requirement for an ORDINARY setting is a real gap too,
|
|
12316
|
+
* but it changes what every door accepts for every property; this changes
|
|
12317
|
+
* what `requiredSecrets` reports, which is one answer with one reader.
|
|
12318
|
+
* Opening it wider is a separate decision with its own blast radius.
|
|
12319
|
+
*/
|
|
12320
|
+
"x-required-when": ValueConditionSchema.optional()
|
|
12284
12321
|
}).strict();
|
|
12285
12322
|
withJsonSchemaAnnex(string, SECRET_COUPLING_ANNEX);
|
|
12286
12323
|
const number = external_exports.object({
|
|
@@ -12332,7 +12369,7 @@ var ConfigurationPropertySchema = external_exports.discriminatedUnion("type", [
|
|
|
12332
12369
|
BooleanPropertySchema,
|
|
12333
12370
|
ArrayPropertySchema
|
|
12334
12371
|
]);
|
|
12335
|
-
var NestedStringPropertySchema = V2_LEAVES.string.omit({ "x-secret": true, writeOnly: true }).extend({ group: ConfigurationGroupSchema.optional() });
|
|
12372
|
+
var NestedStringPropertySchema = V2_LEAVES.string.omit({ "x-secret": true, writeOnly: true, "x-required-when": true }).extend({ group: ConfigurationGroupSchema.optional() });
|
|
12336
12373
|
var V2_NESTED_LEAVES = {
|
|
12337
12374
|
string: NestedStringPropertySchema,
|
|
12338
12375
|
number: V2_LEAVES.number.extend({ group: ConfigurationGroupSchema.optional() }),
|
|
@@ -13038,6 +13075,9 @@ function pathSegments(path) {
|
|
|
13038
13075
|
const raw = path.startsWith("/") ? path.slice(1).split("/") : [path];
|
|
13039
13076
|
return raw.map((segment) => isIndexSegment(segment) ? Number(segment) : segment);
|
|
13040
13077
|
}
|
|
13078
|
+
function rootSegment(path) {
|
|
13079
|
+
return String(pathSegments(path)[0]);
|
|
13080
|
+
}
|
|
13041
13081
|
function declarationPathOf(path) {
|
|
13042
13082
|
const segments = pathSegments(path).filter((segment) => typeof segment !== "number");
|
|
13043
13083
|
return configurationPath(...segments) ?? path;
|
|
@@ -13151,11 +13191,71 @@ function declaresPath(schema, path) {
|
|
|
13151
13191
|
return true;
|
|
13152
13192
|
return configurationContainers(schema).some((container) => container.path === declarationPathOf(path));
|
|
13153
13193
|
}
|
|
13194
|
+
function isPlainObject(value) {
|
|
13195
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13196
|
+
}
|
|
13197
|
+
function valueAt(configuration, path) {
|
|
13198
|
+
let cursor = configuration;
|
|
13199
|
+
for (const segment of segmentsOf(path)) {
|
|
13200
|
+
if (typeof segment === "number") {
|
|
13201
|
+
if (!Array.isArray(cursor))
|
|
13202
|
+
return void 0;
|
|
13203
|
+
cursor = cursor[segment];
|
|
13204
|
+
continue;
|
|
13205
|
+
}
|
|
13206
|
+
if (!isPlainObject(cursor))
|
|
13207
|
+
return void 0;
|
|
13208
|
+
cursor = cursor[segment];
|
|
13209
|
+
}
|
|
13210
|
+
return cursor;
|
|
13211
|
+
}
|
|
13212
|
+
function valuesAt(configuration, path) {
|
|
13213
|
+
let cursors = [configuration];
|
|
13214
|
+
for (const segment of segmentsOf(path)) {
|
|
13215
|
+
const next = [];
|
|
13216
|
+
for (const cursor of cursors) {
|
|
13217
|
+
if (typeof segment === "number") {
|
|
13218
|
+
if (Array.isArray(cursor))
|
|
13219
|
+
next.push(cursor[segment]);
|
|
13220
|
+
continue;
|
|
13221
|
+
}
|
|
13222
|
+
if (!isPlainObject(cursor))
|
|
13223
|
+
continue;
|
|
13224
|
+
const value = cursor[segment];
|
|
13225
|
+
if (Array.isArray(value))
|
|
13226
|
+
next.push(...value);
|
|
13227
|
+
else
|
|
13228
|
+
next.push(value);
|
|
13229
|
+
}
|
|
13230
|
+
cursors = next;
|
|
13231
|
+
}
|
|
13232
|
+
return cursors.filter((value) => value !== void 0);
|
|
13233
|
+
}
|
|
13234
|
+
function segmentsOf(path) {
|
|
13235
|
+
const raw = path.startsWith("/") ? path.slice(1).split("/") : [path];
|
|
13236
|
+
return raw.map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
|
|
13237
|
+
}
|
|
13238
|
+
|
|
13239
|
+
// ../product-configuration/dist/value-condition.js
|
|
13240
|
+
function conditionSetting(condition) {
|
|
13241
|
+
return "path" in condition ? condition.path : condition.property;
|
|
13242
|
+
}
|
|
13243
|
+
function conditionRoot(condition) {
|
|
13244
|
+
const path = parseConfigurationPath(conditionSetting(condition));
|
|
13245
|
+
return path === null ? conditionSetting(condition) : rootSegment(path);
|
|
13246
|
+
}
|
|
13247
|
+
function conditionHolds(condition, configuration) {
|
|
13248
|
+
const path = parseConfigurationPath(conditionSetting(condition));
|
|
13249
|
+
if (path === null)
|
|
13250
|
+
return false;
|
|
13251
|
+
const matches2 = (held) => typeof held === "string" && condition.anyOf.includes(held);
|
|
13252
|
+
return "anyRow" in condition && condition.anyRow === true ? valuesAt(configuration, path).some(matches2) : matches2(valueAt(configuration, path));
|
|
13253
|
+
}
|
|
13154
13254
|
|
|
13155
13255
|
// ../product-configuration/dist/customer-configuration-input.js
|
|
13156
13256
|
var ABSOLUTE_MAX_STRING = MAX_DECLARABLE_STRING_LENGTH;
|
|
13157
13257
|
var MAX_REPORTED_UNDECLARED_KEYS = 10;
|
|
13158
|
-
function
|
|
13258
|
+
function isPlainObject2(value) {
|
|
13159
13259
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13160
13260
|
}
|
|
13161
13261
|
function report(walk, field, message) {
|
|
@@ -13177,17 +13277,17 @@ function validateSubmittedConfiguration(schema, submitted, presence = {}) {
|
|
|
13177
13277
|
referenced: new Set(presence.referenced ?? [])
|
|
13178
13278
|
};
|
|
13179
13279
|
const supplied = /* @__PURE__ */ new Set();
|
|
13180
|
-
if (submitted !== void 0 && submitted !== null && !
|
|
13280
|
+
if (submitted !== void 0 && submitted !== null && !isPlainObject2(submitted)) {
|
|
13181
13281
|
return {
|
|
13182
13282
|
ok: false,
|
|
13183
13283
|
issues: [{ field: "", message: "Configuration must be an object." }],
|
|
13184
13284
|
partial: {}
|
|
13185
13285
|
};
|
|
13186
13286
|
}
|
|
13187
|
-
const input =
|
|
13287
|
+
const input = isPlainObject2(submitted) ? submitted : {};
|
|
13188
13288
|
const secrets = {};
|
|
13189
13289
|
const configuration = validateObject({ properties: schema.properties, required: schema.required }, input, null, walk, { secrets, supplied });
|
|
13190
|
-
drain(walk, crossFieldIssues(schema, supplied, presence));
|
|
13290
|
+
drain(walk, crossFieldIssues(schema, supplied, presence, configuration));
|
|
13191
13291
|
if (walk.suppressed > 0) {
|
|
13192
13292
|
walk.issues.push({
|
|
13193
13293
|
field: "",
|
|
@@ -13342,7 +13442,7 @@ function validateContainer(name, property, children, value, field, walk) {
|
|
|
13342
13442
|
if (children.kind === "object") {
|
|
13343
13443
|
if (value === void 0)
|
|
13344
13444
|
return void 0;
|
|
13345
|
-
if (!
|
|
13445
|
+
if (!isPlainObject2(value)) {
|
|
13346
13446
|
report(walk, field, "Expected a group of settings.");
|
|
13347
13447
|
return void 0;
|
|
13348
13448
|
}
|
|
@@ -13369,7 +13469,7 @@ function validateContainer(name, property, children, value, field, walk) {
|
|
|
13369
13469
|
report(walk, field, "This configuration has too many values.");
|
|
13370
13470
|
break;
|
|
13371
13471
|
}
|
|
13372
|
-
if (!
|
|
13472
|
+
if (!isPlainObject2(element)) {
|
|
13373
13473
|
report(walk, `${field}`, `Item ${index + 1} is not a group of settings.`);
|
|
13374
13474
|
continue;
|
|
13375
13475
|
}
|
|
@@ -13385,9 +13485,10 @@ function validateContainer(name, property, children, value, field, walk) {
|
|
|
13385
13485
|
}
|
|
13386
13486
|
return out;
|
|
13387
13487
|
}
|
|
13388
|
-
function crossFieldIssues(schema, supplied, presence) {
|
|
13488
|
+
function crossFieldIssues(schema, supplied, presence, configuration = {}) {
|
|
13389
13489
|
const clauses = Object.entries(schema.dependentRequired ?? {});
|
|
13390
|
-
|
|
13490
|
+
const conditional = Object.entries(schema.properties).filter(([, property]) => conditionOf(property) !== void 0);
|
|
13491
|
+
if (clauses.length === 0 && conditional.length === 0)
|
|
13391
13492
|
return [];
|
|
13392
13493
|
const alsoPresent = /* @__PURE__ */ new Set([...presence.present ?? [], ...presence.referenced ?? []]);
|
|
13393
13494
|
const unknown = new Set(presence.unknown ?? []);
|
|
@@ -13410,13 +13511,42 @@ function crossFieldIssues(schema, supplied, presence) {
|
|
|
13410
13511
|
});
|
|
13411
13512
|
}
|
|
13412
13513
|
}
|
|
13514
|
+
for (const [name, property] of conditional) {
|
|
13515
|
+
const condition = conditionOf(property);
|
|
13516
|
+
if (condition === void 0 || !known(name) || held(name))
|
|
13517
|
+
continue;
|
|
13518
|
+
if (!conditionHolds(condition, configuration))
|
|
13519
|
+
continue;
|
|
13520
|
+
if (reported.has(name))
|
|
13521
|
+
continue;
|
|
13522
|
+
reported.add(name);
|
|
13523
|
+
issues.push({
|
|
13524
|
+
field: name,
|
|
13525
|
+
// THE SETTING THAT MADE IT NECESSARY, never the value it holds. A message
|
|
13526
|
+
// naming the value would be fine here and wrong one refactor later, when
|
|
13527
|
+
// somebody conditions a secret on another secret — which the publish rules
|
|
13528
|
+
// refuse precisely because nothing may read one.
|
|
13529
|
+
message: `This setting is required because of what ${labelOf(schema, conditionRoot(condition))} is set to.`
|
|
13530
|
+
});
|
|
13531
|
+
}
|
|
13413
13532
|
return issues;
|
|
13414
13533
|
}
|
|
13534
|
+
function conditionOf(property) {
|
|
13535
|
+
return property?.["x-required-when"];
|
|
13536
|
+
}
|
|
13415
13537
|
function labelOf(schema, name) {
|
|
13416
13538
|
const property = schema.properties[name];
|
|
13417
13539
|
return property?.title === void 0 ? `"${name}"` : `"${property.title}"`;
|
|
13418
13540
|
}
|
|
13419
13541
|
function withoutCrossFieldRules(schema) {
|
|
13542
|
+
const conditioned = Object.entries(schema.properties).some(([, property]) => property["x-required-when"] !== void 0);
|
|
13543
|
+
if (conditioned) {
|
|
13544
|
+
const properties = Object.fromEntries(Object.entries(schema.properties).map(([name, property]) => {
|
|
13545
|
+
const { "x-required-when": _dropped, ...bare } = property;
|
|
13546
|
+
return [name, bare];
|
|
13547
|
+
}));
|
|
13548
|
+
return withoutCrossFieldRules({ ...schema, properties });
|
|
13549
|
+
}
|
|
13420
13550
|
if (schema.dependentRequired === void 0)
|
|
13421
13551
|
return schema;
|
|
13422
13552
|
const { dependentRequired: _removed, ...rest } = schema;
|
|
@@ -14619,6 +14749,91 @@ function checkProductDefinition(definition, ctx) {
|
|
|
14619
14749
|
});
|
|
14620
14750
|
}
|
|
14621
14751
|
});
|
|
14752
|
+
for (const [name, property] of Object.entries(definition.configuration.properties ?? {})) {
|
|
14753
|
+
const condition = property["x-required-when"];
|
|
14754
|
+
if (condition === void 0)
|
|
14755
|
+
continue;
|
|
14756
|
+
const at = ["configuration", "properties", name, "x-required-when"];
|
|
14757
|
+
if (!isSecretProperty(property)) {
|
|
14758
|
+
ctx.addIssue({
|
|
14759
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14760
|
+
path: [...at],
|
|
14761
|
+
message: `"${name}" is not a secret, so "x-required-when" has nothing to report through. Declare it "x-secret": true with "writeOnly": true, or use "dependentRequired" to require one ordinary setting alongside another.`
|
|
14762
|
+
});
|
|
14763
|
+
continue;
|
|
14764
|
+
}
|
|
14765
|
+
if (new Set(condition.anyOf).size !== condition.anyOf.length) {
|
|
14766
|
+
ctx.addIssue({
|
|
14767
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14768
|
+
path: [...at, "anyOf"],
|
|
14769
|
+
message: "Repeated values in `anyOf`."
|
|
14770
|
+
});
|
|
14771
|
+
}
|
|
14772
|
+
if (condition.path === name) {
|
|
14773
|
+
ctx.addIssue({
|
|
14774
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14775
|
+
path: [...at, "path"],
|
|
14776
|
+
message: `"${name}" cannot be conditioned on its own value.`
|
|
14777
|
+
});
|
|
14778
|
+
continue;
|
|
14779
|
+
}
|
|
14780
|
+
const parsed = parseConfigurationPath(condition.path);
|
|
14781
|
+
const leaf = parsed === null ? null : leafAt(definition.configuration, parsed);
|
|
14782
|
+
if (leaf === null) {
|
|
14783
|
+
const isGroup = parsed !== null && declaresPath(definition.configuration, parsed);
|
|
14784
|
+
ctx.addIssue({
|
|
14785
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14786
|
+
path: [...at, "path"],
|
|
14787
|
+
message: isGroup ? `"${condition.path}" is a group of settings, which holds no single value to compare. Name one setting inside it.` : `"${condition.path}" is not a declared customer configuration property.`
|
|
14788
|
+
});
|
|
14789
|
+
continue;
|
|
14790
|
+
}
|
|
14791
|
+
if (underList(leaf) && condition.anyRow !== true) {
|
|
14792
|
+
ctx.addIssue({
|
|
14793
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14794
|
+
path: [...at, "path"],
|
|
14795
|
+
message: `"${condition.path}" is inside a list, so it names one value per item rather than one value. Add "anyRow": true to require the secret when ANY row chose one of these values, or name a setting the instance holds exactly once.`
|
|
14796
|
+
});
|
|
14797
|
+
continue;
|
|
14798
|
+
}
|
|
14799
|
+
if (!underList(leaf) && condition.anyRow === true) {
|
|
14800
|
+
ctx.addIssue({
|
|
14801
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14802
|
+
path: [...at, "anyRow"],
|
|
14803
|
+
message: `"${condition.path}" is not inside a list, so there are no rows to quantify over.`
|
|
14804
|
+
});
|
|
14805
|
+
}
|
|
14806
|
+
const target = leaf.property;
|
|
14807
|
+
if (target.type !== "string") {
|
|
14808
|
+
ctx.addIssue({
|
|
14809
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14810
|
+
path: [...at, "path"],
|
|
14811
|
+
message: `"${condition.path}" is a ${target.type} setting; a condition compares string values.`
|
|
14812
|
+
});
|
|
14813
|
+
continue;
|
|
14814
|
+
}
|
|
14815
|
+
if (isSecretProperty(target)) {
|
|
14816
|
+
ctx.addIssue({
|
|
14817
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14818
|
+
path: [...at, "path"],
|
|
14819
|
+
message: `"${condition.path}" is a secret. The platform never reads a secret's value, so a condition on one could never be evaluated.`
|
|
14820
|
+
});
|
|
14821
|
+
continue;
|
|
14822
|
+
}
|
|
14823
|
+
const single = withoutCrossFieldRules({
|
|
14824
|
+
...definition.configuration,
|
|
14825
|
+
properties: { [leaf.name]: target },
|
|
14826
|
+
required: [leaf.name]
|
|
14827
|
+
});
|
|
14828
|
+
const outside = condition.anyOf.filter((value) => !validateSubmittedConfiguration(single, { [leaf.name]: value }).ok);
|
|
14829
|
+
if (outside.length > 0) {
|
|
14830
|
+
ctx.addIssue({
|
|
14831
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14832
|
+
path: [...at, "anyOf"],
|
|
14833
|
+
message: `"${condition.path}" never accepts ${outside.map((v) => `"${v}"`).join(", ")}, so this condition could never make the secret required.`
|
|
14834
|
+
});
|
|
14835
|
+
}
|
|
14836
|
+
}
|
|
14622
14837
|
const provides = definition.serviceInterfaces?.provides ?? [];
|
|
14623
14838
|
const provideKeys = provides.map((provide) => provide.key);
|
|
14624
14839
|
const duplicateProvideKeys = provideKeys.filter((key, i) => provideKeys.indexOf(key) !== i);
|
|
@@ -17002,7 +17217,7 @@ async function runApply(verb, parsed, deps = {}) {
|
|
|
17002
17217
|
if (!planned.ok) return printError(planned.error, json);
|
|
17003
17218
|
if (verb === "plan") {
|
|
17004
17219
|
printPlan(planned.data, json);
|
|
17005
|
-
if (isUnconverged(planned.data)) {
|
|
17220
|
+
if (isUnconverged(planned.data) && planned.data.action === "NO_CHANGE") {
|
|
17006
17221
|
if (!json) {
|
|
17007
17222
|
process.stderr.write(
|
|
17008
17223
|
`${planned.data.manifestKey ?? "This resource"} is ${planned.data.health?.status ?? "not serving"}. Its desired state is applied; it is not running. \`vfac get operation <id>\` says why.
|
|
@@ -17011,6 +17226,12 @@ async function runApply(verb, parsed, deps = {}) {
|
|
|
17011
17226
|
}
|
|
17012
17227
|
return 1;
|
|
17013
17228
|
}
|
|
17229
|
+
if (isUnconverged(planned.data) && !json) {
|
|
17230
|
+
process.stderr.write(
|
|
17231
|
+
`${planned.data.manifestKey ?? "This resource"} is ${planned.data.health?.status ?? "not serving"}. This plan changes it.
|
|
17232
|
+
`
|
|
17233
|
+
);
|
|
17234
|
+
}
|
|
17014
17235
|
return 0;
|
|
17015
17236
|
}
|
|
17016
17237
|
const parsedAction = ManifestActionSchema.safeParse(planned.data.action);
|
|
@@ -17310,23 +17531,20 @@ async function runDoctor(parsed) {
|
|
|
17310
17531
|
const compatible = compatibility(cliVersion(), probe.minimumCliVersion);
|
|
17311
17532
|
checks.push(compatible);
|
|
17312
17533
|
if (!compatible.ok) return report2(checks, json);
|
|
17313
|
-
const
|
|
17314
|
-
if (!
|
|
17315
|
-
checks.push({ name: "
|
|
17534
|
+
const session = await ensureSession({ endpointFlag: parsed.flags["endpoint"] });
|
|
17535
|
+
if (!session.ok) {
|
|
17536
|
+
checks.push({ name: "Sign-in", ok: false, detail: session.message });
|
|
17316
17537
|
return report2(checks, json);
|
|
17317
17538
|
}
|
|
17318
17539
|
checks.push({
|
|
17319
|
-
name: "
|
|
17540
|
+
name: "Sign-in",
|
|
17320
17541
|
ok: true,
|
|
17321
|
-
detail:
|
|
17542
|
+
detail: session.ready.via === "stored" ? "a stored sign-in is still valid, so no credential was spent" : session.ready.gate ? `authenticated as "${session.ready.gate.name}" (${session.ready.gate.id})` : "authenticated"
|
|
17322
17543
|
});
|
|
17323
|
-
if (!sessionUsable(stored.session, endpoint, /* @__PURE__ */ new Date())) {
|
|
17324
|
-
writeStored({ ...stored, session: signedIn.session });
|
|
17325
|
-
}
|
|
17326
17544
|
const who = await call({
|
|
17327
17545
|
endpoint,
|
|
17328
17546
|
path: "/api/v1/whoami",
|
|
17329
|
-
token:
|
|
17547
|
+
token: session.ready.token
|
|
17330
17548
|
});
|
|
17331
17549
|
if (!who.ok) {
|
|
17332
17550
|
checks.push({
|