@eudiplo/cli 8.0.2 → 8.1.0-main.21c667e
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 +7 -3
- package/dist/commands/completion/action.js +1 -1
- package/dist/commands/completion/action.js.map +1 -1
- package/dist/commands/completion/index.d.ts.map +1 -1
- package/dist/commands/completion/index.js +6 -1
- package/dist/commands/completion/index.js.map +1 -1
- package/dist/commands/config/action.d.ts.map +1 -1
- package/dist/commands/config/action.js.map +1 -1
- package/dist/commands/config/portability/migrations.spec.js +5 -3
- package/dist/commands/config/portability/migrations.spec.js.map +1 -1
- package/dist/commands/config/validate/schemas.js.map +1 -1
- package/dist/commands/config/validate/validator.js +7 -4
- package/dist/commands/config/validate/validator.js.map +1 -1
- package/dist/commands/deployment/action.d.ts +1 -1
- package/dist/commands/deployment/action.d.ts.map +1 -1
- package/dist/commands/deployment/action.js.map +1 -1
- package/dist/commands/deployment/index.d.ts.map +1 -1
- package/dist/commands/deployment/index.js +7 -4
- package/dist/commands/deployment/index.js.map +1 -1
- package/dist/commands/doctor/action.d.ts +2 -1
- package/dist/commands/doctor/action.d.ts.map +1 -1
- package/dist/commands/doctor/action.js +25 -7
- package/dist/commands/doctor/action.js.map +1 -1
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +2 -0
- package/dist/commands/doctor/index.js.map +1 -1
- package/dist/commands/instance/action.d.ts.map +1 -1
- package/dist/commands/instance/action.js.map +1 -1
- package/dist/commands/open/action.d.ts +9 -0
- package/dist/commands/open/action.d.ts.map +1 -0
- package/dist/commands/open/action.js +41 -0
- package/dist/commands/open/action.js.map +1 -0
- package/dist/commands/open/index.d.ts +5 -0
- package/dist/commands/open/index.d.ts.map +1 -0
- package/dist/commands/open/index.js +15 -0
- package/dist/commands/open/index.js.map +1 -0
- package/dist/commands/upgrade/action.d.ts +3 -0
- package/dist/commands/upgrade/action.d.ts.map +1 -0
- package/dist/commands/upgrade/action.js +95 -0
- package/dist/commands/upgrade/action.js.map +1 -0
- package/dist/commands/upgrade/index.d.ts +5 -0
- package/dist/commands/upgrade/index.d.ts.map +1 -0
- package/dist/commands/upgrade/index.js +15 -0
- package/dist/commands/upgrade/index.js.map +1 -0
- package/dist/index.js +3194 -595
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +5 -1
- package/dist/runtime.js.map +1 -1
- package/dist/services/api-auth.d.ts +31 -0
- package/dist/services/api-auth.d.ts.map +1 -0
- package/dist/services/api-auth.js +78 -0
- package/dist/services/api-auth.js.map +1 -0
- package/dist/services/backend-checks.d.ts +24 -0
- package/dist/services/backend-checks.d.ts.map +1 -0
- package/dist/services/backend-checks.js +105 -0
- package/dist/services/backend-checks.js.map +1 -0
- package/dist/services/browser.d.ts +30 -0
- package/dist/services/browser.d.ts.map +1 -0
- package/dist/services/browser.js +102 -0
- package/dist/services/browser.js.map +1 -0
- package/dist/services/cli-config.d.ts.map +1 -1
- package/dist/services/cli-config.js +5 -2
- package/dist/services/cli-config.js.map +1 -1
- package/dist/services/compose-args.d.ts +28 -0
- package/dist/services/compose-args.d.ts.map +1 -0
- package/dist/services/compose-args.js +117 -0
- package/dist/services/compose-args.js.map +1 -0
- package/dist/services/compose-project.d.ts.map +1 -1
- package/dist/services/compose-project.js +18 -13
- package/dist/services/compose-project.js.map +1 -1
- package/dist/services/compose-upgrade.d.ts +40 -0
- package/dist/services/compose-upgrade.d.ts.map +1 -0
- package/dist/services/compose-upgrade.js +102 -0
- package/dist/services/compose-upgrade.js.map +1 -0
- package/dist/services/container-state.d.ts +14 -0
- package/dist/services/container-state.d.ts.map +1 -0
- package/dist/services/container-state.js +70 -0
- package/dist/services/container-state.js.map +1 -0
- package/dist/services/deployment-drivers.d.ts +8 -1
- package/dist/services/deployment-drivers.d.ts.map +1 -1
- package/dist/services/deployment-drivers.js +162 -19
- package/dist/services/deployment-drivers.js.map +1 -1
- package/dist/services/diagnostics.d.ts +19 -2
- package/dist/services/diagnostics.d.ts.map +1 -1
- package/dist/services/diagnostics.js +219 -50
- package/dist/services/diagnostics.js.map +1 -1
- package/dist/services/kubectl.d.ts.map +1 -1
- package/dist/services/kubectl.js +1 -6
- package/dist/services/kubectl.js.map +1 -1
- package/dist/services/tls-expiry.d.ts +16 -0
- package/dist/services/tls-expiry.d.ts.map +1 -0
- package/dist/services/tls-expiry.js +80 -0
- package/dist/services/tls-expiry.js.map +1 -0
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -4
- package/templates/demo-config/trust-lists/trustlist-580831bc-ef11-43f4-a3be-a2b6bf1b29a3-config.json +1 -97
- package/templates/docker-compose.yml +35 -28
- package/templates/schemas/IssuanceConfig.schema.json +13 -0
- package/templates/schemas/IssuanceConfigFile.schema.json +663 -1
- package/templates/schemas/KeyChainConfigFile.schema.json +7 -3
- package/templates/schemas/KeyChainImportDto.schema.json +7 -3
- package/templates/schemas/PresentationConfigCreateDto.schema.json +3 -6
- package/templates/schemas/PresentationConfigFile.schema.json +723 -1
package/dist/index.js
CHANGED
|
@@ -2216,20 +2216,20 @@ var require_resolve = __commonJS({
|
|
|
2216
2216
|
return false;
|
|
2217
2217
|
}
|
|
2218
2218
|
function countKeys(schema) {
|
|
2219
|
-
let
|
|
2219
|
+
let count2 = 0;
|
|
2220
2220
|
for (const key in schema) {
|
|
2221
2221
|
if (key === "$ref")
|
|
2222
2222
|
return Infinity;
|
|
2223
|
-
|
|
2223
|
+
count2++;
|
|
2224
2224
|
if (SIMPLE_INLINED.has(key))
|
|
2225
2225
|
continue;
|
|
2226
2226
|
if (typeof schema[key] == "object") {
|
|
2227
|
-
(0, util_1.eachItem)(schema[key], (sch) =>
|
|
2227
|
+
(0, util_1.eachItem)(schema[key], (sch) => count2 += countKeys(sch));
|
|
2228
2228
|
}
|
|
2229
|
-
if (
|
|
2229
|
+
if (count2 === Infinity)
|
|
2230
2230
|
return Infinity;
|
|
2231
2231
|
}
|
|
2232
|
-
return
|
|
2232
|
+
return count2;
|
|
2233
2233
|
}
|
|
2234
2234
|
function getFullPath(resolver, id = "", normalize) {
|
|
2235
2235
|
if (normalize !== false)
|
|
@@ -2980,7 +2980,7 @@ var require_compile = __commonJS({
|
|
|
2980
2980
|
const schOrFunc = root.refs[ref];
|
|
2981
2981
|
if (schOrFunc)
|
|
2982
2982
|
return schOrFunc;
|
|
2983
|
-
let _sch =
|
|
2983
|
+
let _sch = resolve9.call(this, root, ref);
|
|
2984
2984
|
if (_sch === void 0) {
|
|
2985
2985
|
const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
2986
2986
|
const { schemaId } = this.opts;
|
|
@@ -3007,7 +3007,7 @@ var require_compile = __commonJS({
|
|
|
3007
3007
|
function sameSchemaEnv(s1, s2) {
|
|
3008
3008
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3009
3009
|
}
|
|
3010
|
-
function
|
|
3010
|
+
function resolve9(root, ref) {
|
|
3011
3011
|
let sch;
|
|
3012
3012
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3013
3013
|
ref = sch;
|
|
@@ -3828,11 +3828,11 @@ var require_fast_uri = __commonJS({
|
|
|
3828
3828
|
normalizeString(uri, options);
|
|
3829
3829
|
} else if (typeof uri === "object") {
|
|
3830
3830
|
uri = /** @type {T} */
|
|
3831
|
-
|
|
3831
|
+
parse5(serialize(uri, options), options);
|
|
3832
3832
|
}
|
|
3833
3833
|
return uri;
|
|
3834
3834
|
}
|
|
3835
|
-
function
|
|
3835
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
3836
3836
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3837
3837
|
const {
|
|
3838
3838
|
parsed: baseParsed,
|
|
@@ -3868,8 +3868,8 @@ var require_fast_uri = __commonJS({
|
|
|
3868
3868
|
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
3869
3869
|
const target = {};
|
|
3870
3870
|
if (!skipNormalization) {
|
|
3871
|
-
base =
|
|
3872
|
-
relative3 =
|
|
3871
|
+
base = parse5(serialize(base, options), options);
|
|
3872
|
+
relative3 = parse5(serialize(relative3, options), options);
|
|
3873
3873
|
}
|
|
3874
3874
|
options = options || {};
|
|
3875
3875
|
if (!options.tolerant && relative3.scheme) {
|
|
@@ -4161,7 +4161,7 @@ var require_fast_uri = __commonJS({
|
|
|
4161
4161
|
}
|
|
4162
4162
|
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
4163
4163
|
}
|
|
4164
|
-
function
|
|
4164
|
+
function parse5(uri, opts) {
|
|
4165
4165
|
return parseWithStatus(uri, opts).parsed;
|
|
4166
4166
|
}
|
|
4167
4167
|
function normalizeString(uri, opts) {
|
|
@@ -4194,11 +4194,11 @@ var require_fast_uri = __commonJS({
|
|
|
4194
4194
|
var fastUri = {
|
|
4195
4195
|
SCHEMES,
|
|
4196
4196
|
normalize,
|
|
4197
|
-
resolve:
|
|
4197
|
+
resolve: resolve9,
|
|
4198
4198
|
resolveComponent,
|
|
4199
4199
|
equal,
|
|
4200
4200
|
serialize,
|
|
4201
|
-
parse:
|
|
4201
|
+
parse: parse5
|
|
4202
4202
|
};
|
|
4203
4203
|
module.exports = fastUri;
|
|
4204
4204
|
module.exports.default = fastUri;
|
|
@@ -5699,8 +5699,8 @@ var require_contains = __commonJS({
|
|
|
5699
5699
|
cxt.result(valid, () => cxt.reset());
|
|
5700
5700
|
function validateItemsWithCount() {
|
|
5701
5701
|
const schValid = gen.name("_valid");
|
|
5702
|
-
const
|
|
5703
|
-
validateItems(schValid, () => gen.if(schValid, () => checkLimits(
|
|
5702
|
+
const count2 = gen.let("count", 0);
|
|
5703
|
+
validateItems(schValid, () => gen.if(schValid, () => checkLimits(count2)));
|
|
5704
5704
|
}
|
|
5705
5705
|
function validateItems(_valid, block) {
|
|
5706
5706
|
gen.forRange("i", 0, len, (i2) => {
|
|
@@ -5713,16 +5713,16 @@ var require_contains = __commonJS({
|
|
|
5713
5713
|
block();
|
|
5714
5714
|
});
|
|
5715
5715
|
}
|
|
5716
|
-
function checkLimits(
|
|
5717
|
-
gen.code((0, codegen_1._)`${
|
|
5716
|
+
function checkLimits(count2) {
|
|
5717
|
+
gen.code((0, codegen_1._)`${count2}++`);
|
|
5718
5718
|
if (max2 === void 0) {
|
|
5719
|
-
gen.if((0, codegen_1._)`${
|
|
5719
|
+
gen.if((0, codegen_1._)`${count2} >= ${min}`, () => gen.assign(valid, true).break());
|
|
5720
5720
|
} else {
|
|
5721
|
-
gen.if((0, codegen_1._)`${
|
|
5721
|
+
gen.if((0, codegen_1._)`${count2} > ${max2}`, () => gen.assign(valid, false).break());
|
|
5722
5722
|
if (min === 1)
|
|
5723
5723
|
gen.assign(valid, true);
|
|
5724
5724
|
else
|
|
5725
|
-
gen.if((0, codegen_1._)`${
|
|
5725
|
+
gen.if((0, codegen_1._)`${count2} >= ${min}`, () => gen.assign(valid, true));
|
|
5726
5726
|
}
|
|
5727
5727
|
}
|
|
5728
5728
|
}
|
|
@@ -11388,12 +11388,7 @@ import { homedir } from "node:os";
|
|
|
11388
11388
|
|
|
11389
11389
|
// src/services/kubectl.ts
|
|
11390
11390
|
function scopeArgs(scope) {
|
|
11391
|
-
return [
|
|
11392
|
-
"--context",
|
|
11393
|
-
scope.context,
|
|
11394
|
-
"--namespace",
|
|
11395
|
-
scope.namespace
|
|
11396
|
-
];
|
|
11391
|
+
return ["--context", scope.context, "--namespace", scope.namespace];
|
|
11397
11392
|
}
|
|
11398
11393
|
function resolveScope(instance, instanceName) {
|
|
11399
11394
|
if (!instance.context) {
|
|
@@ -11922,7 +11917,12 @@ function powershellCompletion() {
|
|
|
11922
11917
|
}
|
|
11923
11918
|
|
|
11924
11919
|
// src/commands/completion/index.ts
|
|
11925
|
-
var supportedShells = [
|
|
11920
|
+
var supportedShells = [
|
|
11921
|
+
"bash",
|
|
11922
|
+
"zsh",
|
|
11923
|
+
"fish",
|
|
11924
|
+
"powershell"
|
|
11925
|
+
];
|
|
11926
11926
|
function createCompletionCommand(context, setExitCode) {
|
|
11927
11927
|
return new Command("completion").description("Generate shell completion scripts").addArgument(
|
|
11928
11928
|
new Argument("<shell>", "shell to generate completion for").choices(
|
|
@@ -11933,14 +11933,22 @@ function createCompletionCommand(context, setExitCode) {
|
|
|
11933
11933
|
});
|
|
11934
11934
|
}
|
|
11935
11935
|
function createCompletionCandidatesCommand(context, setExitCode) {
|
|
11936
|
-
return new Command("_complete").argument("[words...]").allowUnknownOption(true).action(
|
|
11937
|
-
|
|
11938
|
-
|
|
11936
|
+
return new Command("_complete").argument("[words...]").allowUnknownOption(true).action(
|
|
11937
|
+
async (words, _options, command) => {
|
|
11938
|
+
if (!command.parent) {
|
|
11939
|
+
throw new Error(
|
|
11940
|
+
"Completion command is not attached to the CLI root."
|
|
11941
|
+
);
|
|
11942
|
+
}
|
|
11943
|
+
setExitCode(
|
|
11944
|
+
await runCompletionCandidates(
|
|
11945
|
+
command.parent,
|
|
11946
|
+
words,
|
|
11947
|
+
context
|
|
11948
|
+
)
|
|
11949
|
+
);
|
|
11939
11950
|
}
|
|
11940
|
-
|
|
11941
|
-
await runCompletionCandidates(command.parent, words, context)
|
|
11942
|
-
);
|
|
11943
|
-
});
|
|
11951
|
+
);
|
|
11944
11952
|
}
|
|
11945
11953
|
|
|
11946
11954
|
// src/commands/commands/render.ts
|
|
@@ -12028,8 +12036,10 @@ function runConfigShow(configPath, config, json, context) {
|
|
|
12028
12036
|
}
|
|
12029
12037
|
context.stdout.write(`Config: ${configPath}
|
|
12030
12038
|
`);
|
|
12031
|
-
context.stdout.write(
|
|
12032
|
-
`
|
|
12039
|
+
context.stdout.write(
|
|
12040
|
+
`Default instance: ${config.defaultInstance ?? "none"}
|
|
12041
|
+
`
|
|
12042
|
+
);
|
|
12033
12043
|
const instances = Object.entries(config.instances).sort(
|
|
12034
12044
|
([left], [right]) => left.localeCompare(right)
|
|
12035
12045
|
);
|
|
@@ -12065,10 +12075,10 @@ import { isAbsolute as isAbsolute2, join as join2, relative, resolve as resolve2
|
|
|
12065
12075
|
function createScanner(text, ignoreTrivia = false) {
|
|
12066
12076
|
const len = text.length;
|
|
12067
12077
|
let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
|
|
12068
|
-
function scanHexDigits(
|
|
12078
|
+
function scanHexDigits(count2, exact) {
|
|
12069
12079
|
let digits = 0;
|
|
12070
12080
|
let value2 = 0;
|
|
12071
|
-
while (digits <
|
|
12081
|
+
while (digits < count2 || !exact) {
|
|
12072
12082
|
let ch = text.charCodeAt(pos);
|
|
12073
12083
|
if (ch >= 48 && ch <= 57) {
|
|
12074
12084
|
value2 = value2 * 16 + ch - 48;
|
|
@@ -12082,7 +12092,7 @@ function createScanner(text, ignoreTrivia = false) {
|
|
|
12082
12092
|
pos++;
|
|
12083
12093
|
digits++;
|
|
12084
12094
|
}
|
|
12085
|
-
if (digits <
|
|
12095
|
+
if (digits < count2) {
|
|
12086
12096
|
value2 = -1;
|
|
12087
12097
|
}
|
|
12088
12098
|
return value2;
|
|
@@ -12707,9 +12717,9 @@ function format(documentText, range, options) {
|
|
|
12707
12717
|
}
|
|
12708
12718
|
return editOperations;
|
|
12709
12719
|
}
|
|
12710
|
-
function repeat(s,
|
|
12720
|
+
function repeat(s, count2) {
|
|
12711
12721
|
let result = "";
|
|
12712
|
-
for (let i2 = 0; i2 <
|
|
12722
|
+
for (let i2 = 0; i2 < count2; i2++) {
|
|
12713
12723
|
result += s;
|
|
12714
12724
|
}
|
|
12715
12725
|
return result;
|
|
@@ -14887,7 +14897,7 @@ var CONFIG_FORMATS = {
|
|
|
14887
14897
|
IssuanceConfig: {
|
|
14888
14898
|
slug: "issuance-config",
|
|
14889
14899
|
file: "IssuanceConfigFile",
|
|
14890
|
-
version:
|
|
14900
|
+
version: 2
|
|
14891
14901
|
},
|
|
14892
14902
|
CredentialConfig: {
|
|
14893
14903
|
slug: "credential-config",
|
|
@@ -14897,7 +14907,7 @@ var CONFIG_FORMATS = {
|
|
|
14897
14907
|
PresentationConfig: {
|
|
14898
14908
|
slug: "presentation-config",
|
|
14899
14909
|
file: "PresentationConfigFile",
|
|
14900
|
-
version:
|
|
14910
|
+
version: 2
|
|
14901
14911
|
},
|
|
14902
14912
|
AttributeProvider: {
|
|
14903
14913
|
slug: "attribute-provider",
|
|
@@ -14936,7 +14946,42 @@ function resourceId(input) {
|
|
|
14936
14946
|
throw new Error(`Configuration spec.${field} is required`);
|
|
14937
14947
|
return id;
|
|
14938
14948
|
}
|
|
14939
|
-
var
|
|
14949
|
+
var presentationConfigV1ToV2 = {
|
|
14950
|
+
id: "presentation-config-v2-provides-attestations",
|
|
14951
|
+
kind: "PresentationConfig",
|
|
14952
|
+
from: 1,
|
|
14953
|
+
to: 2,
|
|
14954
|
+
migrate(spec) {
|
|
14955
|
+
const registrationCert = spec.registration_cert;
|
|
14956
|
+
if (!object(registrationCert) || !object(registrationCert.body) || !("provided_attestations" in registrationCert.body)) {
|
|
14957
|
+
return { spec };
|
|
14958
|
+
}
|
|
14959
|
+
const { provided_attestations: _, ...body } = registrationCert.body;
|
|
14960
|
+
return {
|
|
14961
|
+
spec: { ...spec, registration_cert: { ...registrationCert, body } },
|
|
14962
|
+
issues: [
|
|
14963
|
+
{
|
|
14964
|
+
severity: "warning",
|
|
14965
|
+
code: "PROVIDED_ATTESTATIONS_REMOVED",
|
|
14966
|
+
path: "/spec/registration_cert/body/provided_attestations",
|
|
14967
|
+
message: "provided_attestations is not part of the registrar API and was removed. Set provides_attestations to the credential type identifiers (SD-JWT VC vct or mdoc doctype) the issuer provides."
|
|
14968
|
+
}
|
|
14969
|
+
]
|
|
14970
|
+
};
|
|
14971
|
+
}
|
|
14972
|
+
};
|
|
14973
|
+
var CONFIG_MIGRATIONS = [
|
|
14974
|
+
{
|
|
14975
|
+
id: "issuance-config-v2-offer-lifetime",
|
|
14976
|
+
kind: "IssuanceConfig",
|
|
14977
|
+
from: 1,
|
|
14978
|
+
to: 2,
|
|
14979
|
+
// v2 only adds the optional offerLifetimeSeconds: a v1 spec is a
|
|
14980
|
+
// valid v2 spec whose offers keep having no lifetime.
|
|
14981
|
+
migrate: (spec) => ({ spec })
|
|
14982
|
+
},
|
|
14983
|
+
presentationConfigV1ToV2
|
|
14984
|
+
];
|
|
14940
14985
|
function schemaUrl(kind, version = CONFIG_FORMATS[kind].version) {
|
|
14941
14986
|
if (!Number.isSafeInteger(version) || version < 1)
|
|
14942
14987
|
throw new Error("Invalid config version");
|
|
@@ -15260,12 +15305,43 @@ function assertConfigBundle(input) {
|
|
|
15260
15305
|
}
|
|
15261
15306
|
|
|
15262
15307
|
// ../../packages/eudiplo-config-format/dist/config-values.js
|
|
15308
|
+
var isVariableChar = (char) => char >= "A" && char <= "Z" || char >= "0" && char <= "9" || char === "_";
|
|
15309
|
+
function replacePlaceholders(input, replace) {
|
|
15310
|
+
let output = "";
|
|
15311
|
+
let copied = 0;
|
|
15312
|
+
let start = input.indexOf("${");
|
|
15313
|
+
while (start !== -1) {
|
|
15314
|
+
let cursor = start + 2;
|
|
15315
|
+
while (cursor < input.length && isVariableChar(input[cursor]))
|
|
15316
|
+
cursor++;
|
|
15317
|
+
const variable = input.slice(start + 2, cursor);
|
|
15318
|
+
let end = -1;
|
|
15319
|
+
let fallback;
|
|
15320
|
+
if (variable && input[cursor] === "}")
|
|
15321
|
+
end = cursor;
|
|
15322
|
+
else if (variable && input[cursor] === ":") {
|
|
15323
|
+
end = input.indexOf("}", cursor + 1);
|
|
15324
|
+
if (end === -1)
|
|
15325
|
+
break;
|
|
15326
|
+
fallback = input.slice(cursor + 1, end);
|
|
15327
|
+
}
|
|
15328
|
+
if (end === -1) {
|
|
15329
|
+
start = input.indexOf("${", start + 2);
|
|
15330
|
+
continue;
|
|
15331
|
+
}
|
|
15332
|
+
output += input.slice(copied, start);
|
|
15333
|
+
output += replace(input.slice(start, end + 1), variable, fallback);
|
|
15334
|
+
copied = end + 1;
|
|
15335
|
+
start = input.indexOf("${", copied);
|
|
15336
|
+
}
|
|
15337
|
+
return output + input.slice(copied);
|
|
15338
|
+
}
|
|
15263
15339
|
function resolveConfigVariables(input, env) {
|
|
15264
15340
|
const issues = [];
|
|
15265
15341
|
const seen = /* @__PURE__ */ new WeakMap();
|
|
15266
15342
|
function visit2(value, path2) {
|
|
15267
15343
|
if (typeof value === "string")
|
|
15268
|
-
return value
|
|
15344
|
+
return replacePlaceholders(value, (match, variable, fallback) => {
|
|
15269
15345
|
if (env[variable] !== void 0 && env[variable] !== "")
|
|
15270
15346
|
return env[variable];
|
|
15271
15347
|
if (fallback !== void 0)
|
|
@@ -19970,227 +20046,1621 @@ var CONFIG_SCHEMAS = [
|
|
|
19970
20046
|
"title": "CreateWebhookEndpointDto"
|
|
19971
20047
|
}
|
|
19972
20048
|
}
|
|
19973
|
-
}
|
|
19974
|
-
|
|
19975
|
-
|
|
19976
|
-
|
|
19977
|
-
|
|
19978
|
-
|
|
19979
|
-
|
|
19980
|
-
|
|
19981
|
-
|
|
19982
|
-
|
|
19983
|
-
|
|
19984
|
-
|
|
19985
|
-
|
|
19986
|
-
|
|
19987
|
-
|
|
19988
|
-
|
|
19989
|
-
|
|
19990
|
-
|
|
19991
|
-
|
|
19992
|
-
|
|
19993
|
-
|
|
19994
|
-
|
|
19995
|
-
|
|
19996
|
-
|
|
19997
|
-
|
|
19998
|
-
|
|
19999
|
-
|
|
20000
|
-
|
|
20001
|
-
|
|
20002
|
-
|
|
20003
|
-
|
|
20004
|
-
|
|
20005
|
-
|
|
20006
|
-
|
|
20007
|
-
|
|
20008
|
-
|
|
20009
|
-
|
|
20010
|
-
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
|
|
20017
|
-
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
}
|
|
20026
|
-
|
|
20027
|
-
|
|
20028
|
-
|
|
20029
|
-
|
|
20030
|
-
|
|
20031
|
-
|
|
20032
|
-
|
|
20033
|
-
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
|
|
20037
|
-
|
|
20038
|
-
|
|
20039
|
-
|
|
20040
|
-
|
|
20041
|
-
|
|
20042
|
-
|
|
20043
|
-
|
|
20044
|
-
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
|
|
20049
|
-
|
|
20050
|
-
|
|
20051
|
-
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
|
|
20062
|
-
|
|
20063
|
-
|
|
20064
|
-
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20068
|
-
|
|
20069
|
-
|
|
20070
|
-
|
|
20071
|
-
|
|
20072
|
-
|
|
20073
|
-
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
|
|
20093
|
-
|
|
20094
|
-
|
|
20095
|
-
|
|
20096
|
-
|
|
20097
|
-
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
20101
|
-
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20107
|
-
|
|
20108
|
-
|
|
20109
|
-
|
|
20110
|
-
|
|
20111
|
-
|
|
20112
|
-
|
|
20113
|
-
|
|
20114
|
-
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
|
|
20122
|
-
|
|
20123
|
-
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
|
|
20139
|
-
|
|
20140
|
-
|
|
20141
|
-
|
|
20142
|
-
|
|
20143
|
-
|
|
20144
|
-
|
|
20145
|
-
|
|
20146
|
-
|
|
20147
|
-
|
|
20148
|
-
|
|
20149
|
-
|
|
20150
|
-
|
|
20151
|
-
|
|
20152
|
-
|
|
20153
|
-
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
|
|
20158
|
-
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
|
|
20165
|
-
|
|
20166
|
-
|
|
20167
|
-
|
|
20168
|
-
|
|
20169
|
-
|
|
20170
|
-
|
|
20171
|
-
|
|
20172
|
-
|
|
20173
|
-
|
|
20174
|
-
|
|
20175
|
-
|
|
20176
|
-
|
|
20177
|
-
|
|
20178
|
-
|
|
20179
|
-
|
|
20180
|
-
|
|
20181
|
-
|
|
20182
|
-
|
|
20183
|
-
|
|
20184
|
-
|
|
20185
|
-
|
|
20186
|
-
|
|
20187
|
-
|
|
20188
|
-
|
|
20189
|
-
|
|
20190
|
-
|
|
20191
|
-
|
|
20192
|
-
|
|
20193
|
-
|
|
20049
|
+
},
|
|
20050
|
+
{
|
|
20051
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
20052
|
+
"$id": "https://eudiplo.dev/schemas/v2/IssuanceConfigFile.schema.json",
|
|
20053
|
+
"title": "IssuanceConfigFile",
|
|
20054
|
+
"type": "object",
|
|
20055
|
+
"properties": {
|
|
20056
|
+
"$schema": {
|
|
20057
|
+
"type": "string",
|
|
20058
|
+
"const": "https://eudiplo.dev/schemas/v2/IssuanceConfigFile.schema.json"
|
|
20059
|
+
},
|
|
20060
|
+
"metadata": {
|
|
20061
|
+
"type": "object",
|
|
20062
|
+
"properties": {
|
|
20063
|
+
"generation": {
|
|
20064
|
+
"type": "integer",
|
|
20065
|
+
"minimum": 1,
|
|
20066
|
+
"description": "Monotonically increasing configuration generation."
|
|
20067
|
+
},
|
|
20068
|
+
"ownership": {
|
|
20069
|
+
"type": "string",
|
|
20070
|
+
"enum": [
|
|
20071
|
+
"unmanaged",
|
|
20072
|
+
"file-managed"
|
|
20073
|
+
],
|
|
20074
|
+
"description": "Requested ownership recorded when the document is applied."
|
|
20075
|
+
}
|
|
20076
|
+
},
|
|
20077
|
+
"additionalProperties": false
|
|
20078
|
+
},
|
|
20079
|
+
"spec": {
|
|
20080
|
+
"$ref": "#/$defs/IssuanceConfig"
|
|
20081
|
+
}
|
|
20082
|
+
},
|
|
20083
|
+
"required": [
|
|
20084
|
+
"$schema",
|
|
20085
|
+
"spec"
|
|
20086
|
+
],
|
|
20087
|
+
"additionalProperties": false,
|
|
20088
|
+
"$defs": {
|
|
20089
|
+
"IssuanceConfig": {
|
|
20090
|
+
"type": "object",
|
|
20091
|
+
"properties": {
|
|
20092
|
+
"batchSize": {
|
|
20093
|
+
"description": "Optional issuance batch size.",
|
|
20094
|
+
"type": "integer",
|
|
20095
|
+
"minimum": 1,
|
|
20096
|
+
"maximum": 9007199254740991
|
|
20097
|
+
},
|
|
20098
|
+
"dPopRequired": {
|
|
20099
|
+
"description": "Require DPoP proofs for issuance endpoints.",
|
|
20100
|
+
"type": "boolean"
|
|
20101
|
+
},
|
|
20102
|
+
"walletAttestationRequired": {
|
|
20103
|
+
"description": "Default wallet attestation requirement for managed authorization servers.",
|
|
20104
|
+
"type": "boolean"
|
|
20105
|
+
},
|
|
20106
|
+
"walletProviderTrustLists": {
|
|
20107
|
+
"description": "Shared wallet provider trust lists for key attestations and default authorization server wallet authentication.",
|
|
20108
|
+
"type": "array",
|
|
20109
|
+
"items": {
|
|
20110
|
+
"type": "object",
|
|
20111
|
+
"properties": {
|
|
20112
|
+
"trustListId": {
|
|
20113
|
+
"type": "string",
|
|
20114
|
+
"minLength": 1
|
|
20115
|
+
},
|
|
20116
|
+
"url": {
|
|
20117
|
+
"description": "URL of the wallet provider trust list.",
|
|
20118
|
+
"type": "string",
|
|
20119
|
+
"format": "uri"
|
|
20120
|
+
},
|
|
20121
|
+
"verifierKey": {
|
|
20122
|
+
"description": "Optional verifier key material used for trust list verification.",
|
|
20123
|
+
"type": "object",
|
|
20124
|
+
"propertyNames": {
|
|
20125
|
+
"type": "string"
|
|
20126
|
+
},
|
|
20127
|
+
"additionalProperties": {}
|
|
20128
|
+
},
|
|
20129
|
+
"verifierX509Der": {
|
|
20130
|
+
"description": "Optional verifier certificate in DER/base64 form.",
|
|
20131
|
+
"type": "string"
|
|
20132
|
+
}
|
|
20133
|
+
},
|
|
20134
|
+
"additionalProperties": false
|
|
20135
|
+
}
|
|
20136
|
+
},
|
|
20137
|
+
"signingKeyId": {
|
|
20138
|
+
"description": "Default signing key chain id for credential issuance.",
|
|
20139
|
+
"type": "string",
|
|
20140
|
+
"minLength": 1
|
|
20141
|
+
},
|
|
20142
|
+
"authorizationServers": {
|
|
20143
|
+
"minItems": 1,
|
|
20144
|
+
"type": "array",
|
|
20145
|
+
"items": {
|
|
20146
|
+
"oneOf": [
|
|
20147
|
+
{
|
|
20148
|
+
"type": "object",
|
|
20149
|
+
"properties": {
|
|
20150
|
+
"type": {
|
|
20151
|
+
"type": "string",
|
|
20152
|
+
"const": "external",
|
|
20153
|
+
"description": "Use an externally managed authorization server."
|
|
20154
|
+
},
|
|
20155
|
+
"id": {
|
|
20156
|
+
"type": "string",
|
|
20157
|
+
"minLength": 1,
|
|
20158
|
+
"description": "Authorization server identifier."
|
|
20159
|
+
},
|
|
20160
|
+
"issuer": {
|
|
20161
|
+
"type": "string",
|
|
20162
|
+
"format": "uri",
|
|
20163
|
+
"description": "Issuer URL for the external authorization server."
|
|
20164
|
+
},
|
|
20165
|
+
"sessionBinding": {
|
|
20166
|
+
"description": "Explicit mapping from an external access-token claim to an existing issuance session.",
|
|
20167
|
+
"type": "object",
|
|
20168
|
+
"properties": {
|
|
20169
|
+
"method": {
|
|
20170
|
+
"type": "string",
|
|
20171
|
+
"const": "access_token_claim",
|
|
20172
|
+
"description": "Read the correlation value from a configured access-token claim."
|
|
20173
|
+
},
|
|
20174
|
+
"claim": {
|
|
20175
|
+
"type": "string",
|
|
20176
|
+
"minLength": 1,
|
|
20177
|
+
"description": "Claim name that contains the issuance-session correlation value."
|
|
20178
|
+
}
|
|
20179
|
+
},
|
|
20180
|
+
"required": [
|
|
20181
|
+
"method",
|
|
20182
|
+
"claim"
|
|
20183
|
+
],
|
|
20184
|
+
"additionalProperties": false
|
|
20185
|
+
},
|
|
20186
|
+
"label": {
|
|
20187
|
+
"description": "Optional display label for UI selection.",
|
|
20188
|
+
"type": "string"
|
|
20189
|
+
},
|
|
20190
|
+
"enabled": {
|
|
20191
|
+
"description": "Whether this authorization server entry is enabled.",
|
|
20192
|
+
"type": "boolean"
|
|
20193
|
+
}
|
|
20194
|
+
},
|
|
20195
|
+
"required": [
|
|
20196
|
+
"type",
|
|
20197
|
+
"id",
|
|
20198
|
+
"issuer"
|
|
20199
|
+
],
|
|
20200
|
+
"additionalProperties": false
|
|
20201
|
+
},
|
|
20202
|
+
{
|
|
20203
|
+
"type": "object",
|
|
20204
|
+
"properties": {
|
|
20205
|
+
"type": {
|
|
20206
|
+
"type": "string",
|
|
20207
|
+
"const": "oid4vp",
|
|
20208
|
+
"description": "Use OID4VP-based authorization server chaining."
|
|
20209
|
+
},
|
|
20210
|
+
"id": {
|
|
20211
|
+
"type": "string",
|
|
20212
|
+
"minLength": 1,
|
|
20213
|
+
"description": "Authorization server identifier."
|
|
20214
|
+
},
|
|
20215
|
+
"presentationConfigId": {
|
|
20216
|
+
"type": "string",
|
|
20217
|
+
"minLength": 1,
|
|
20218
|
+
"description": "Presentation configuration id used during authorization."
|
|
20219
|
+
},
|
|
20220
|
+
"immediateWalletRedirect": {
|
|
20221
|
+
"description": "Redirect wallets immediately after authorization response creation.",
|
|
20222
|
+
"type": "boolean"
|
|
20223
|
+
},
|
|
20224
|
+
"token": {
|
|
20225
|
+
"description": "Optional token issuance settings.",
|
|
20226
|
+
"type": "object",
|
|
20227
|
+
"properties": {
|
|
20228
|
+
"lifetimeSeconds": {
|
|
20229
|
+
"description": "Access token lifetime in seconds.",
|
|
20230
|
+
"type": "number",
|
|
20231
|
+
"minimum": 60
|
|
20232
|
+
},
|
|
20233
|
+
"signingKeyId": {
|
|
20234
|
+
"description": "Optional key chain id used to sign issued tokens.",
|
|
20235
|
+
"type": "string",
|
|
20236
|
+
"minLength": 1
|
|
20237
|
+
},
|
|
20238
|
+
"refreshTokenEnabled": {
|
|
20239
|
+
"description": "Enable issuing refresh tokens.",
|
|
20240
|
+
"type": "boolean"
|
|
20241
|
+
},
|
|
20242
|
+
"refreshTokenExpiresInSeconds": {
|
|
20243
|
+
"description": "Refresh token lifetime in seconds.",
|
|
20244
|
+
"type": "number",
|
|
20245
|
+
"minimum": 60
|
|
20246
|
+
}
|
|
20247
|
+
},
|
|
20248
|
+
"additionalProperties": false
|
|
20249
|
+
},
|
|
20250
|
+
"requireDPoP": {
|
|
20251
|
+
"description": "Require DPoP proofs for token/credential requests.",
|
|
20252
|
+
"type": "boolean"
|
|
20253
|
+
},
|
|
20254
|
+
"walletAttestationRequired": {
|
|
20255
|
+
"description": "Require wallet attestation for this authorization server.",
|
|
20256
|
+
"type": "boolean"
|
|
20257
|
+
},
|
|
20258
|
+
"walletProviderTrustLists": {
|
|
20259
|
+
"description": "Optional wallet provider trust list references for this authorization server.",
|
|
20260
|
+
"type": "array",
|
|
20261
|
+
"items": {
|
|
20262
|
+
"type": "object",
|
|
20263
|
+
"properties": {
|
|
20264
|
+
"trustListId": {
|
|
20265
|
+
"type": "string",
|
|
20266
|
+
"minLength": 1
|
|
20267
|
+
},
|
|
20268
|
+
"url": {
|
|
20269
|
+
"description": "URL of the wallet provider trust list.",
|
|
20270
|
+
"type": "string",
|
|
20271
|
+
"format": "uri"
|
|
20272
|
+
},
|
|
20273
|
+
"verifierKey": {
|
|
20274
|
+
"description": "Optional verifier key material used for trust list verification.",
|
|
20275
|
+
"type": "object",
|
|
20276
|
+
"propertyNames": {
|
|
20277
|
+
"type": "string"
|
|
20278
|
+
},
|
|
20279
|
+
"additionalProperties": {}
|
|
20280
|
+
},
|
|
20281
|
+
"verifierX509Der": {
|
|
20282
|
+
"description": "Optional verifier certificate in DER/base64 form.",
|
|
20283
|
+
"type": "string"
|
|
20284
|
+
}
|
|
20285
|
+
},
|
|
20286
|
+
"additionalProperties": false
|
|
20287
|
+
}
|
|
20288
|
+
},
|
|
20289
|
+
"label": {
|
|
20290
|
+
"description": "Optional display label for UI selection.",
|
|
20291
|
+
"type": "string"
|
|
20292
|
+
},
|
|
20293
|
+
"enabled": {
|
|
20294
|
+
"description": "Whether this authorization server entry is enabled.",
|
|
20295
|
+
"type": "boolean"
|
|
20296
|
+
}
|
|
20297
|
+
},
|
|
20298
|
+
"required": [
|
|
20299
|
+
"type",
|
|
20300
|
+
"id",
|
|
20301
|
+
"presentationConfigId"
|
|
20302
|
+
],
|
|
20303
|
+
"additionalProperties": false
|
|
20304
|
+
},
|
|
20305
|
+
{
|
|
20306
|
+
"type": "object",
|
|
20307
|
+
"properties": {
|
|
20308
|
+
"type": {
|
|
20309
|
+
"type": "string",
|
|
20310
|
+
"const": "chained",
|
|
20311
|
+
"description": "Use upstream OIDC as authorization source."
|
|
20312
|
+
},
|
|
20313
|
+
"id": {
|
|
20314
|
+
"type": "string",
|
|
20315
|
+
"minLength": 1,
|
|
20316
|
+
"description": "Authorization server identifier."
|
|
20317
|
+
},
|
|
20318
|
+
"upstream": {
|
|
20319
|
+
"type": "object",
|
|
20320
|
+
"properties": {
|
|
20321
|
+
"issuer": {
|
|
20322
|
+
"type": "string",
|
|
20323
|
+
"format": "uri",
|
|
20324
|
+
"description": "Upstream OIDC issuer URL."
|
|
20325
|
+
},
|
|
20326
|
+
"clientId": {
|
|
20327
|
+
"type": "string",
|
|
20328
|
+
"minLength": 1,
|
|
20329
|
+
"description": "Client id for upstream OIDC authentication."
|
|
20330
|
+
},
|
|
20331
|
+
"clientSecret": {
|
|
20332
|
+
"description": "Optional client secret for upstream OIDC authentication.",
|
|
20333
|
+
"type": "string",
|
|
20334
|
+
"minLength": 1
|
|
20335
|
+
},
|
|
20336
|
+
"scopes": {
|
|
20337
|
+
"description": "Optional scopes requested from the upstream issuer.",
|
|
20338
|
+
"type": "array",
|
|
20339
|
+
"items": {
|
|
20340
|
+
"type": "string",
|
|
20341
|
+
"minLength": 1
|
|
20342
|
+
}
|
|
20343
|
+
}
|
|
20344
|
+
},
|
|
20345
|
+
"required": [
|
|
20346
|
+
"issuer",
|
|
20347
|
+
"clientId"
|
|
20348
|
+
],
|
|
20349
|
+
"additionalProperties": false,
|
|
20350
|
+
"description": "Upstream OIDC connection settings."
|
|
20351
|
+
},
|
|
20352
|
+
"token": {
|
|
20353
|
+
"description": "Optional token issuance settings.",
|
|
20354
|
+
"type": "object",
|
|
20355
|
+
"properties": {
|
|
20356
|
+
"lifetimeSeconds": {
|
|
20357
|
+
"description": "Access token lifetime in seconds.",
|
|
20358
|
+
"type": "number",
|
|
20359
|
+
"minimum": 60
|
|
20360
|
+
},
|
|
20361
|
+
"signingKeyId": {
|
|
20362
|
+
"description": "Optional key chain id used to sign issued tokens.",
|
|
20363
|
+
"type": "string",
|
|
20364
|
+
"minLength": 1
|
|
20365
|
+
},
|
|
20366
|
+
"refreshTokenEnabled": {
|
|
20367
|
+
"description": "Enable issuing refresh tokens.",
|
|
20368
|
+
"type": "boolean"
|
|
20369
|
+
},
|
|
20370
|
+
"refreshTokenExpiresInSeconds": {
|
|
20371
|
+
"description": "Refresh token lifetime in seconds.",
|
|
20372
|
+
"type": "number",
|
|
20373
|
+
"minimum": 60
|
|
20374
|
+
}
|
|
20375
|
+
},
|
|
20376
|
+
"additionalProperties": false
|
|
20377
|
+
},
|
|
20378
|
+
"requireDPoP": {
|
|
20379
|
+
"description": "Require DPoP proofs for token/credential requests.",
|
|
20380
|
+
"type": "boolean"
|
|
20381
|
+
},
|
|
20382
|
+
"walletAttestationRequired": {
|
|
20383
|
+
"description": "Require wallet attestation for this authorization server.",
|
|
20384
|
+
"type": "boolean"
|
|
20385
|
+
},
|
|
20386
|
+
"walletProviderTrustLists": {
|
|
20387
|
+
"description": "Optional wallet provider trust list references for this authorization server.",
|
|
20388
|
+
"type": "array",
|
|
20389
|
+
"items": {
|
|
20390
|
+
"type": "object",
|
|
20391
|
+
"properties": {
|
|
20392
|
+
"trustListId": {
|
|
20393
|
+
"type": "string",
|
|
20394
|
+
"minLength": 1
|
|
20395
|
+
},
|
|
20396
|
+
"url": {
|
|
20397
|
+
"description": "URL of the wallet provider trust list.",
|
|
20398
|
+
"type": "string",
|
|
20399
|
+
"format": "uri"
|
|
20400
|
+
},
|
|
20401
|
+
"verifierKey": {
|
|
20402
|
+
"description": "Optional verifier key material used for trust list verification.",
|
|
20403
|
+
"type": "object",
|
|
20404
|
+
"propertyNames": {
|
|
20405
|
+
"type": "string"
|
|
20406
|
+
},
|
|
20407
|
+
"additionalProperties": {}
|
|
20408
|
+
},
|
|
20409
|
+
"verifierX509Der": {
|
|
20410
|
+
"description": "Optional verifier certificate in DER/base64 form.",
|
|
20411
|
+
"type": "string"
|
|
20412
|
+
}
|
|
20413
|
+
},
|
|
20414
|
+
"additionalProperties": false
|
|
20415
|
+
}
|
|
20416
|
+
},
|
|
20417
|
+
"label": {
|
|
20418
|
+
"description": "Optional display label for UI selection.",
|
|
20419
|
+
"type": "string"
|
|
20420
|
+
},
|
|
20421
|
+
"enabled": {
|
|
20422
|
+
"description": "Whether this authorization server entry is enabled.",
|
|
20423
|
+
"type": "boolean"
|
|
20424
|
+
}
|
|
20425
|
+
},
|
|
20426
|
+
"required": [
|
|
20427
|
+
"type",
|
|
20428
|
+
"id",
|
|
20429
|
+
"upstream"
|
|
20430
|
+
],
|
|
20431
|
+
"additionalProperties": false
|
|
20432
|
+
},
|
|
20433
|
+
{
|
|
20434
|
+
"type": "object",
|
|
20435
|
+
"properties": {
|
|
20436
|
+
"type": {
|
|
20437
|
+
"type": "string",
|
|
20438
|
+
"const": "built-in",
|
|
20439
|
+
"description": "Use EUDIPLO built-in authorization server."
|
|
20440
|
+
},
|
|
20441
|
+
"id": {
|
|
20442
|
+
"type": "string",
|
|
20443
|
+
"minLength": 1,
|
|
20444
|
+
"description": "Authorization server identifier."
|
|
20445
|
+
},
|
|
20446
|
+
"token": {
|
|
20447
|
+
"description": "Optional token issuance settings.",
|
|
20448
|
+
"type": "object",
|
|
20449
|
+
"properties": {
|
|
20450
|
+
"lifetimeSeconds": {
|
|
20451
|
+
"description": "Access token lifetime in seconds.",
|
|
20452
|
+
"type": "number",
|
|
20453
|
+
"minimum": 60
|
|
20454
|
+
},
|
|
20455
|
+
"signingKeyId": {
|
|
20456
|
+
"description": "Optional key chain id used to sign issued tokens.",
|
|
20457
|
+
"type": "string",
|
|
20458
|
+
"minLength": 1
|
|
20459
|
+
},
|
|
20460
|
+
"refreshTokenEnabled": {
|
|
20461
|
+
"description": "Enable issuing refresh tokens.",
|
|
20462
|
+
"type": "boolean"
|
|
20463
|
+
},
|
|
20464
|
+
"refreshTokenExpiresInSeconds": {
|
|
20465
|
+
"description": "Refresh token lifetime in seconds.",
|
|
20466
|
+
"type": "number",
|
|
20467
|
+
"minimum": 60
|
|
20468
|
+
}
|
|
20469
|
+
},
|
|
20470
|
+
"additionalProperties": false
|
|
20471
|
+
},
|
|
20472
|
+
"requireDPoP": {
|
|
20473
|
+
"description": "Require DPoP proofs for token/credential requests.",
|
|
20474
|
+
"type": "boolean"
|
|
20475
|
+
},
|
|
20476
|
+
"walletAttestationRequired": {
|
|
20477
|
+
"description": "Require wallet attestation for this authorization server.",
|
|
20478
|
+
"type": "boolean"
|
|
20479
|
+
},
|
|
20480
|
+
"walletProviderTrustLists": {
|
|
20481
|
+
"description": "Optional wallet provider trust list references for this authorization server.",
|
|
20482
|
+
"type": "array",
|
|
20483
|
+
"items": {
|
|
20484
|
+
"type": "object",
|
|
20485
|
+
"properties": {
|
|
20486
|
+
"trustListId": {
|
|
20487
|
+
"type": "string",
|
|
20488
|
+
"minLength": 1
|
|
20489
|
+
},
|
|
20490
|
+
"url": {
|
|
20491
|
+
"description": "URL of the wallet provider trust list.",
|
|
20492
|
+
"type": "string",
|
|
20493
|
+
"format": "uri"
|
|
20494
|
+
},
|
|
20495
|
+
"verifierKey": {
|
|
20496
|
+
"description": "Optional verifier key material used for trust list verification.",
|
|
20497
|
+
"type": "object",
|
|
20498
|
+
"propertyNames": {
|
|
20499
|
+
"type": "string"
|
|
20500
|
+
},
|
|
20501
|
+
"additionalProperties": {}
|
|
20502
|
+
},
|
|
20503
|
+
"verifierX509Der": {
|
|
20504
|
+
"description": "Optional verifier certificate in DER/base64 form.",
|
|
20505
|
+
"type": "string"
|
|
20506
|
+
}
|
|
20507
|
+
},
|
|
20508
|
+
"additionalProperties": false
|
|
20509
|
+
}
|
|
20510
|
+
},
|
|
20511
|
+
"label": {
|
|
20512
|
+
"description": "Optional display label for UI selection.",
|
|
20513
|
+
"type": "string"
|
|
20514
|
+
},
|
|
20515
|
+
"enabled": {
|
|
20516
|
+
"description": "Whether this authorization server entry is enabled.",
|
|
20517
|
+
"type": "boolean"
|
|
20518
|
+
}
|
|
20519
|
+
},
|
|
20520
|
+
"required": [
|
|
20521
|
+
"type",
|
|
20522
|
+
"id"
|
|
20523
|
+
],
|
|
20524
|
+
"additionalProperties": false
|
|
20525
|
+
}
|
|
20526
|
+
],
|
|
20527
|
+
"description": "Supported authorization server configurations."
|
|
20528
|
+
},
|
|
20529
|
+
"description": "Configured authorization server entries."
|
|
20530
|
+
},
|
|
20531
|
+
"federation": {
|
|
20532
|
+
"description": "Optional OpenID Federation settings.",
|
|
20533
|
+
"anyOf": [
|
|
20534
|
+
{
|
|
20535
|
+
"type": "object",
|
|
20536
|
+
"properties": {
|
|
20537
|
+
"role": {
|
|
20538
|
+
"description": "Federation role for this issuer.",
|
|
20539
|
+
"type": "string",
|
|
20540
|
+
"enum": [
|
|
20541
|
+
"trust_anchor",
|
|
20542
|
+
"intermediate",
|
|
20543
|
+
"leaf"
|
|
20544
|
+
]
|
|
20545
|
+
},
|
|
20546
|
+
"mode": {
|
|
20547
|
+
"description": "Federation operation mode.",
|
|
20548
|
+
"type": "string",
|
|
20549
|
+
"enum": [
|
|
20550
|
+
"federation-only",
|
|
20551
|
+
"hybrid"
|
|
20552
|
+
]
|
|
20553
|
+
},
|
|
20554
|
+
"entityId": {
|
|
20555
|
+
"description": "Optional local federation entity id.",
|
|
20556
|
+
"type": "string"
|
|
20557
|
+
},
|
|
20558
|
+
"enforceSigningPolicy": {
|
|
20559
|
+
"description": "Enforce strict signing policy checks.",
|
|
20560
|
+
"type": "boolean"
|
|
20561
|
+
},
|
|
20562
|
+
"cacheTtlSeconds": {
|
|
20563
|
+
"description": "Cache time-to-live for federation metadata in seconds.",
|
|
20564
|
+
"type": "integer",
|
|
20565
|
+
"minimum": 1,
|
|
20566
|
+
"maximum": 9007199254740991
|
|
20567
|
+
},
|
|
20568
|
+
"trustAnchors": {
|
|
20569
|
+
"type": "array",
|
|
20570
|
+
"items": {
|
|
20571
|
+
"type": "object",
|
|
20572
|
+
"properties": {
|
|
20573
|
+
"entityId": {
|
|
20574
|
+
"type": "string",
|
|
20575
|
+
"minLength": 1,
|
|
20576
|
+
"description": "Federation trust anchor entity id."
|
|
20577
|
+
},
|
|
20578
|
+
"entityConfigurationUri": {
|
|
20579
|
+
"type": "string",
|
|
20580
|
+
"format": "uri",
|
|
20581
|
+
"description": "Entity configuration URI for the trust anchor."
|
|
20582
|
+
}
|
|
20583
|
+
},
|
|
20584
|
+
"required": [
|
|
20585
|
+
"entityId",
|
|
20586
|
+
"entityConfigurationUri"
|
|
20587
|
+
],
|
|
20588
|
+
"additionalProperties": false
|
|
20589
|
+
},
|
|
20590
|
+
"description": "Federation trust anchors."
|
|
20591
|
+
}
|
|
20592
|
+
},
|
|
20593
|
+
"required": [
|
|
20594
|
+
"trustAnchors"
|
|
20595
|
+
],
|
|
20596
|
+
"additionalProperties": false
|
|
20597
|
+
},
|
|
20598
|
+
{
|
|
20599
|
+
"type": "null"
|
|
20600
|
+
}
|
|
20601
|
+
]
|
|
20602
|
+
},
|
|
20603
|
+
"registrationCertificate": {
|
|
20604
|
+
"description": "Optional registration certificate settings.",
|
|
20605
|
+
"anyOf": [
|
|
20606
|
+
{
|
|
20607
|
+
"type": "object",
|
|
20608
|
+
"properties": {
|
|
20609
|
+
"enabled": {
|
|
20610
|
+
"description": "Enable issuer registration certificate support.",
|
|
20611
|
+
"type": "boolean"
|
|
20612
|
+
},
|
|
20613
|
+
"mode": {
|
|
20614
|
+
"description": "How registration certificate data is provided.",
|
|
20615
|
+
"type": "string",
|
|
20616
|
+
"enum": [
|
|
20617
|
+
"import",
|
|
20618
|
+
"generate"
|
|
20619
|
+
]
|
|
20620
|
+
},
|
|
20621
|
+
"jwt": {
|
|
20622
|
+
"description": "Optional registration certificate JWT when using import mode.",
|
|
20623
|
+
"type": "string"
|
|
20624
|
+
},
|
|
20625
|
+
"privacyPolicy": {
|
|
20626
|
+
"description": "Optional privacy policy URI.",
|
|
20627
|
+
"type": "string"
|
|
20628
|
+
},
|
|
20629
|
+
"supportUri": {
|
|
20630
|
+
"description": "Optional support URI.",
|
|
20631
|
+
"type": "string"
|
|
20632
|
+
}
|
|
20633
|
+
},
|
|
20634
|
+
"additionalProperties": false
|
|
20635
|
+
},
|
|
20636
|
+
{
|
|
20637
|
+
"type": "null"
|
|
20638
|
+
}
|
|
20639
|
+
]
|
|
20640
|
+
},
|
|
20641
|
+
"display": {
|
|
20642
|
+
"description": "Localized issuer metadata shown to wallets.",
|
|
20643
|
+
"type": "array",
|
|
20644
|
+
"items": {
|
|
20645
|
+
"type": "object",
|
|
20646
|
+
"properties": {
|
|
20647
|
+
"name": {
|
|
20648
|
+
"description": "Issuer display name.",
|
|
20649
|
+
"type": "string"
|
|
20650
|
+
},
|
|
20651
|
+
"locale": {
|
|
20652
|
+
"description": "Locale tag for this display entry.",
|
|
20653
|
+
"type": "string"
|
|
20654
|
+
},
|
|
20655
|
+
"logo": {
|
|
20656
|
+
"description": "Optional issuer logo metadata.",
|
|
20657
|
+
"type": "object",
|
|
20658
|
+
"properties": {
|
|
20659
|
+
"uri": {
|
|
20660
|
+
"type": "string",
|
|
20661
|
+
"minLength": 1,
|
|
20662
|
+
"description": "Logo URI."
|
|
20663
|
+
},
|
|
20664
|
+
"alt_text": {
|
|
20665
|
+
"description": "Optional localized alternative text.",
|
|
20666
|
+
"type": "string"
|
|
20667
|
+
}
|
|
20668
|
+
},
|
|
20669
|
+
"required": [
|
|
20670
|
+
"uri"
|
|
20671
|
+
],
|
|
20672
|
+
"additionalProperties": {}
|
|
20673
|
+
}
|
|
20674
|
+
},
|
|
20675
|
+
"additionalProperties": {}
|
|
20676
|
+
}
|
|
20677
|
+
},
|
|
20678
|
+
"notificationEndpointEnabled": {
|
|
20679
|
+
"description": "Whether the OID4VCI notification endpoint is exposed for this issuance configuration.",
|
|
20680
|
+
"type": "boolean"
|
|
20681
|
+
},
|
|
20682
|
+
"credentialResponseEncryption": {
|
|
20683
|
+
"description": "Enable encrypted credential responses.",
|
|
20684
|
+
"type": "boolean"
|
|
20685
|
+
},
|
|
20686
|
+
"credentialRequestEncryption": {
|
|
20687
|
+
"description": "Require encrypted credential requests.",
|
|
20688
|
+
"type": "boolean"
|
|
20689
|
+
},
|
|
20690
|
+
"txCodeMaxAttempts": {
|
|
20691
|
+
"description": "Maximum verification attempts for transaction codes. Null resets to defaults.",
|
|
20692
|
+
"anyOf": [
|
|
20693
|
+
{
|
|
20694
|
+
"type": "integer",
|
|
20695
|
+
"minimum": 1,
|
|
20696
|
+
"maximum": 9007199254740991
|
|
20697
|
+
},
|
|
20698
|
+
{
|
|
20699
|
+
"type": "null"
|
|
20700
|
+
}
|
|
20701
|
+
]
|
|
20702
|
+
},
|
|
20703
|
+
"offerLifetimeSeconds": {
|
|
20704
|
+
"description": "Default lifetime of credential offers in seconds. Can be overridden per offer request. Null or unset: offers do not expire.",
|
|
20705
|
+
"anyOf": [
|
|
20706
|
+
{
|
|
20707
|
+
"type": "integer",
|
|
20708
|
+
"minimum": 1,
|
|
20709
|
+
"maximum": 9007199254740991
|
|
20710
|
+
},
|
|
20711
|
+
{
|
|
20712
|
+
"type": "null"
|
|
20713
|
+
}
|
|
20714
|
+
]
|
|
20715
|
+
}
|
|
20716
|
+
},
|
|
20717
|
+
"required": [
|
|
20718
|
+
"authorizationServers"
|
|
20719
|
+
],
|
|
20720
|
+
"additionalProperties": false,
|
|
20721
|
+
"title": "IssuanceConfig"
|
|
20722
|
+
}
|
|
20723
|
+
}
|
|
20724
|
+
},
|
|
20725
|
+
{
|
|
20726
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
20727
|
+
"$id": "https://eudiplo.dev/schemas/v2/PresentationConfigFile.schema.json",
|
|
20728
|
+
"title": "PresentationConfigFile",
|
|
20729
|
+
"type": "object",
|
|
20730
|
+
"properties": {
|
|
20731
|
+
"$schema": {
|
|
20732
|
+
"type": "string",
|
|
20733
|
+
"const": "https://eudiplo.dev/schemas/v2/PresentationConfigFile.schema.json"
|
|
20734
|
+
},
|
|
20735
|
+
"metadata": {
|
|
20736
|
+
"type": "object",
|
|
20737
|
+
"properties": {
|
|
20738
|
+
"generation": {
|
|
20739
|
+
"type": "integer",
|
|
20740
|
+
"minimum": 1,
|
|
20741
|
+
"description": "Monotonically increasing configuration generation."
|
|
20742
|
+
},
|
|
20743
|
+
"ownership": {
|
|
20744
|
+
"type": "string",
|
|
20745
|
+
"enum": [
|
|
20746
|
+
"unmanaged",
|
|
20747
|
+
"file-managed"
|
|
20748
|
+
],
|
|
20749
|
+
"description": "Requested ownership recorded when the document is applied."
|
|
20750
|
+
}
|
|
20751
|
+
},
|
|
20752
|
+
"additionalProperties": false
|
|
20753
|
+
},
|
|
20754
|
+
"spec": {
|
|
20755
|
+
"allOf": [
|
|
20756
|
+
{
|
|
20757
|
+
"$ref": "#/$defs/PresentationConfigCreateDto"
|
|
20758
|
+
},
|
|
20759
|
+
{
|
|
20760
|
+
"type": "object",
|
|
20761
|
+
"required": [
|
|
20762
|
+
"id"
|
|
20763
|
+
],
|
|
20764
|
+
"properties": {
|
|
20765
|
+
"id": {
|
|
20766
|
+
"type": "string",
|
|
20767
|
+
"minLength": 1
|
|
20768
|
+
}
|
|
20769
|
+
}
|
|
20770
|
+
}
|
|
20771
|
+
]
|
|
20772
|
+
}
|
|
20773
|
+
},
|
|
20774
|
+
"required": [
|
|
20775
|
+
"$schema",
|
|
20776
|
+
"spec"
|
|
20777
|
+
],
|
|
20778
|
+
"additionalProperties": false,
|
|
20779
|
+
"$defs": {
|
|
20780
|
+
"PresentationConfigCreateDto": {
|
|
20781
|
+
"type": "object",
|
|
20782
|
+
"properties": {
|
|
20783
|
+
"id": {
|
|
20784
|
+
"type": "string",
|
|
20785
|
+
"minLength": 1,
|
|
20786
|
+
"description": "Presentation configuration identifier."
|
|
20787
|
+
},
|
|
20788
|
+
"description": {
|
|
20789
|
+
"description": "Optional presentation configuration description.",
|
|
20790
|
+
"type": [
|
|
20791
|
+
"string",
|
|
20792
|
+
"null"
|
|
20793
|
+
]
|
|
20794
|
+
},
|
|
20795
|
+
"lifeTime": {
|
|
20796
|
+
"description": "Presentation request lifetime in seconds.",
|
|
20797
|
+
"type": "integer",
|
|
20798
|
+
"minimum": 1,
|
|
20799
|
+
"maximum": 9007199254740991
|
|
20800
|
+
},
|
|
20801
|
+
"skewSeconds": {
|
|
20802
|
+
"description": "Clock skew tolerance in seconds.",
|
|
20803
|
+
"type": "integer",
|
|
20804
|
+
"minimum": 0,
|
|
20805
|
+
"maximum": 9007199254740991
|
|
20806
|
+
},
|
|
20807
|
+
"statusCheckMode": {
|
|
20808
|
+
"description": "Revocation/status check mode.",
|
|
20809
|
+
"type": "string",
|
|
20810
|
+
"enum": [
|
|
20811
|
+
"strict",
|
|
20812
|
+
"best_effort",
|
|
20813
|
+
"disabled"
|
|
20814
|
+
]
|
|
20815
|
+
},
|
|
20816
|
+
"dcql_query": {
|
|
20817
|
+
"type": "object",
|
|
20818
|
+
"properties": {
|
|
20819
|
+
"credentials": {
|
|
20820
|
+
"minItems": 1,
|
|
20821
|
+
"type": "array",
|
|
20822
|
+
"items": {
|
|
20823
|
+
"oneOf": [
|
|
20824
|
+
{
|
|
20825
|
+
"type": "object",
|
|
20826
|
+
"properties": {
|
|
20827
|
+
"id": {
|
|
20828
|
+
"type": "string",
|
|
20829
|
+
"minLength": 1,
|
|
20830
|
+
"pattern": "^[A-Za-z0-9_-]+$",
|
|
20831
|
+
"description": "Credential query identifier."
|
|
20832
|
+
},
|
|
20833
|
+
"multiple": {
|
|
20834
|
+
"description": "Allow multiple matching credentials.",
|
|
20835
|
+
"type": "boolean"
|
|
20836
|
+
},
|
|
20837
|
+
"claim_sets": {
|
|
20838
|
+
"description": "Optional claim set constraints.",
|
|
20839
|
+
"type": "array",
|
|
20840
|
+
"items": {
|
|
20841
|
+
"type": "array",
|
|
20842
|
+
"items": {
|
|
20843
|
+
"type": "string"
|
|
20844
|
+
}
|
|
20845
|
+
}
|
|
20846
|
+
},
|
|
20847
|
+
"trusted_authorities": {
|
|
20848
|
+
"description": "Optional trusted authority constraints.",
|
|
20849
|
+
"type": "array",
|
|
20850
|
+
"items": {
|
|
20851
|
+
"oneOf": [
|
|
20852
|
+
{
|
|
20853
|
+
"type": "object",
|
|
20854
|
+
"properties": {
|
|
20855
|
+
"type": {
|
|
20856
|
+
"type": "string",
|
|
20857
|
+
"const": "etsi_tl",
|
|
20858
|
+
"description": "Trusted authority type discriminator for ETSI trust lists."
|
|
20859
|
+
},
|
|
20860
|
+
"values": {
|
|
20861
|
+
"type": "array",
|
|
20862
|
+
"items": {
|
|
20863
|
+
"type": "object",
|
|
20864
|
+
"properties": {
|
|
20865
|
+
"trustListId": {
|
|
20866
|
+
"description": "Optional trust list id reference.",
|
|
20867
|
+
"type": "string"
|
|
20868
|
+
},
|
|
20869
|
+
"url": {
|
|
20870
|
+
"description": "Optional trust list URL reference.",
|
|
20871
|
+
"anyOf": [
|
|
20872
|
+
{
|
|
20873
|
+
"type": "string",
|
|
20874
|
+
"format": "uri"
|
|
20875
|
+
},
|
|
20876
|
+
{
|
|
20877
|
+
"type": "string",
|
|
20878
|
+
"pattern": "^<TENANT_URL>(?:\\/.*)?$"
|
|
20879
|
+
}
|
|
20880
|
+
]
|
|
20881
|
+
},
|
|
20882
|
+
"verifierKey": {
|
|
20883
|
+
"description": "Optional verifier key material.",
|
|
20884
|
+
"type": "object",
|
|
20885
|
+
"propertyNames": {
|
|
20886
|
+
"type": "string"
|
|
20887
|
+
},
|
|
20888
|
+
"additionalProperties": {}
|
|
20889
|
+
},
|
|
20890
|
+
"verifierX509Der": {
|
|
20891
|
+
"description": "Optional verifier certificate in DER/base64 form.",
|
|
20892
|
+
"type": "string"
|
|
20893
|
+
}
|
|
20894
|
+
},
|
|
20895
|
+
"additionalProperties": false
|
|
20896
|
+
},
|
|
20897
|
+
"description": "Trust list references for ETSI TL verification."
|
|
20898
|
+
}
|
|
20899
|
+
},
|
|
20900
|
+
"required": [
|
|
20901
|
+
"type",
|
|
20902
|
+
"values"
|
|
20903
|
+
],
|
|
20904
|
+
"additionalProperties": false
|
|
20905
|
+
},
|
|
20906
|
+
{
|
|
20907
|
+
"type": "object",
|
|
20908
|
+
"properties": {
|
|
20909
|
+
"type": {
|
|
20910
|
+
"type": "string",
|
|
20911
|
+
"const": "openid_federation",
|
|
20912
|
+
"description": "Trusted authority type discriminator for OpenID Federation."
|
|
20913
|
+
},
|
|
20914
|
+
"values": {
|
|
20915
|
+
"type": "array",
|
|
20916
|
+
"items": {
|
|
20917
|
+
"type": "string"
|
|
20918
|
+
},
|
|
20919
|
+
"description": "OpenID Federation authority identifiers."
|
|
20920
|
+
}
|
|
20921
|
+
},
|
|
20922
|
+
"required": [
|
|
20923
|
+
"type",
|
|
20924
|
+
"values"
|
|
20925
|
+
],
|
|
20926
|
+
"additionalProperties": false
|
|
20927
|
+
}
|
|
20928
|
+
]
|
|
20929
|
+
}
|
|
20930
|
+
},
|
|
20931
|
+
"format": {
|
|
20932
|
+
"type": "string",
|
|
20933
|
+
"const": "dc+sd-jwt",
|
|
20934
|
+
"description": "Credential format discriminator."
|
|
20935
|
+
},
|
|
20936
|
+
"meta": {
|
|
20937
|
+
"type": "object",
|
|
20938
|
+
"properties": {
|
|
20939
|
+
"vct_values": {
|
|
20940
|
+
"minItems": 1,
|
|
20941
|
+
"type": "array",
|
|
20942
|
+
"items": {
|
|
20943
|
+
"type": "string"
|
|
20944
|
+
},
|
|
20945
|
+
"description": "Accepted VCT values."
|
|
20946
|
+
}
|
|
20947
|
+
},
|
|
20948
|
+
"required": [
|
|
20949
|
+
"vct_values"
|
|
20950
|
+
],
|
|
20951
|
+
"additionalProperties": false
|
|
20952
|
+
},
|
|
20953
|
+
"claims": {
|
|
20954
|
+
"description": "Optional claim-level constraints.",
|
|
20955
|
+
"type": "array",
|
|
20956
|
+
"items": {
|
|
20957
|
+
"type": "object",
|
|
20958
|
+
"properties": {
|
|
20959
|
+
"id": {
|
|
20960
|
+
"description": "Optional claim query id.",
|
|
20961
|
+
"type": "string"
|
|
20962
|
+
},
|
|
20963
|
+
"path": {
|
|
20964
|
+
"type": "array",
|
|
20965
|
+
"items": {
|
|
20966
|
+
"type": [
|
|
20967
|
+
"string",
|
|
20968
|
+
"number"
|
|
20969
|
+
]
|
|
20970
|
+
},
|
|
20971
|
+
"description": "Path to the claim value in presented credentials."
|
|
20972
|
+
},
|
|
20973
|
+
"values": {
|
|
20974
|
+
"description": "Optional allowed values for the claim.",
|
|
20975
|
+
"type": "array",
|
|
20976
|
+
"items": {
|
|
20977
|
+
"type": "string"
|
|
20978
|
+
}
|
|
20979
|
+
}
|
|
20980
|
+
},
|
|
20981
|
+
"required": [
|
|
20982
|
+
"path"
|
|
20983
|
+
],
|
|
20984
|
+
"additionalProperties": false
|
|
20985
|
+
}
|
|
20986
|
+
}
|
|
20987
|
+
},
|
|
20988
|
+
"required": [
|
|
20989
|
+
"id",
|
|
20990
|
+
"format",
|
|
20991
|
+
"meta"
|
|
20992
|
+
],
|
|
20993
|
+
"additionalProperties": false
|
|
20994
|
+
},
|
|
20995
|
+
{
|
|
20996
|
+
"type": "object",
|
|
20997
|
+
"properties": {
|
|
20998
|
+
"id": {
|
|
20999
|
+
"type": "string",
|
|
21000
|
+
"minLength": 1,
|
|
21001
|
+
"pattern": "^[A-Za-z0-9_-]+$",
|
|
21002
|
+
"description": "Credential query identifier."
|
|
21003
|
+
},
|
|
21004
|
+
"multiple": {
|
|
21005
|
+
"description": "Allow multiple matching credentials.",
|
|
21006
|
+
"type": "boolean"
|
|
21007
|
+
},
|
|
21008
|
+
"claim_sets": {
|
|
21009
|
+
"description": "Optional claim set constraints.",
|
|
21010
|
+
"type": "array",
|
|
21011
|
+
"items": {
|
|
21012
|
+
"type": "array",
|
|
21013
|
+
"items": {
|
|
21014
|
+
"type": "string"
|
|
21015
|
+
}
|
|
21016
|
+
}
|
|
21017
|
+
},
|
|
21018
|
+
"trusted_authorities": {
|
|
21019
|
+
"description": "Optional trusted authority constraints.",
|
|
21020
|
+
"type": "array",
|
|
21021
|
+
"items": {
|
|
21022
|
+
"oneOf": [
|
|
21023
|
+
{
|
|
21024
|
+
"type": "object",
|
|
21025
|
+
"properties": {
|
|
21026
|
+
"type": {
|
|
21027
|
+
"type": "string",
|
|
21028
|
+
"const": "etsi_tl",
|
|
21029
|
+
"description": "Trusted authority type discriminator for ETSI trust lists."
|
|
21030
|
+
},
|
|
21031
|
+
"values": {
|
|
21032
|
+
"type": "array",
|
|
21033
|
+
"items": {
|
|
21034
|
+
"type": "object",
|
|
21035
|
+
"properties": {
|
|
21036
|
+
"trustListId": {
|
|
21037
|
+
"description": "Optional trust list id reference.",
|
|
21038
|
+
"type": "string"
|
|
21039
|
+
},
|
|
21040
|
+
"url": {
|
|
21041
|
+
"description": "Optional trust list URL reference.",
|
|
21042
|
+
"anyOf": [
|
|
21043
|
+
{
|
|
21044
|
+
"type": "string",
|
|
21045
|
+
"format": "uri"
|
|
21046
|
+
},
|
|
21047
|
+
{
|
|
21048
|
+
"type": "string",
|
|
21049
|
+
"pattern": "^<TENANT_URL>(?:\\/.*)?$"
|
|
21050
|
+
}
|
|
21051
|
+
]
|
|
21052
|
+
},
|
|
21053
|
+
"verifierKey": {
|
|
21054
|
+
"description": "Optional verifier key material.",
|
|
21055
|
+
"type": "object",
|
|
21056
|
+
"propertyNames": {
|
|
21057
|
+
"type": "string"
|
|
21058
|
+
},
|
|
21059
|
+
"additionalProperties": {}
|
|
21060
|
+
},
|
|
21061
|
+
"verifierX509Der": {
|
|
21062
|
+
"description": "Optional verifier certificate in DER/base64 form.",
|
|
21063
|
+
"type": "string"
|
|
21064
|
+
}
|
|
21065
|
+
},
|
|
21066
|
+
"additionalProperties": false
|
|
21067
|
+
},
|
|
21068
|
+
"description": "Trust list references for ETSI TL verification."
|
|
21069
|
+
}
|
|
21070
|
+
},
|
|
21071
|
+
"required": [
|
|
21072
|
+
"type",
|
|
21073
|
+
"values"
|
|
21074
|
+
],
|
|
21075
|
+
"additionalProperties": false
|
|
21076
|
+
},
|
|
21077
|
+
{
|
|
21078
|
+
"type": "object",
|
|
21079
|
+
"properties": {
|
|
21080
|
+
"type": {
|
|
21081
|
+
"type": "string",
|
|
21082
|
+
"const": "openid_federation",
|
|
21083
|
+
"description": "Trusted authority type discriminator for OpenID Federation."
|
|
21084
|
+
},
|
|
21085
|
+
"values": {
|
|
21086
|
+
"type": "array",
|
|
21087
|
+
"items": {
|
|
21088
|
+
"type": "string"
|
|
21089
|
+
},
|
|
21090
|
+
"description": "OpenID Federation authority identifiers."
|
|
21091
|
+
}
|
|
21092
|
+
},
|
|
21093
|
+
"required": [
|
|
21094
|
+
"type",
|
|
21095
|
+
"values"
|
|
21096
|
+
],
|
|
21097
|
+
"additionalProperties": false
|
|
21098
|
+
}
|
|
21099
|
+
]
|
|
21100
|
+
}
|
|
21101
|
+
},
|
|
21102
|
+
"format": {
|
|
21103
|
+
"type": "string",
|
|
21104
|
+
"const": "mso_mdoc",
|
|
21105
|
+
"description": "Credential format discriminator."
|
|
21106
|
+
},
|
|
21107
|
+
"meta": {
|
|
21108
|
+
"type": "object",
|
|
21109
|
+
"properties": {
|
|
21110
|
+
"doctype_value": {
|
|
21111
|
+
"type": "string",
|
|
21112
|
+
"minLength": 1,
|
|
21113
|
+
"description": "Expected mDoc doctype value."
|
|
21114
|
+
}
|
|
21115
|
+
},
|
|
21116
|
+
"required": [
|
|
21117
|
+
"doctype_value"
|
|
21118
|
+
],
|
|
21119
|
+
"additionalProperties": false
|
|
21120
|
+
},
|
|
21121
|
+
"claims": {
|
|
21122
|
+
"description": "Optional mDoc claim-level constraints.",
|
|
21123
|
+
"type": "array",
|
|
21124
|
+
"items": {
|
|
21125
|
+
"type": "object",
|
|
21126
|
+
"properties": {
|
|
21127
|
+
"id": {
|
|
21128
|
+
"description": "Optional claim query id.",
|
|
21129
|
+
"type": "string"
|
|
21130
|
+
},
|
|
21131
|
+
"path": {
|
|
21132
|
+
"type": "array",
|
|
21133
|
+
"items": {
|
|
21134
|
+
"type": [
|
|
21135
|
+
"string",
|
|
21136
|
+
"number"
|
|
21137
|
+
]
|
|
21138
|
+
},
|
|
21139
|
+
"description": "Path to the claim value in presented credentials."
|
|
21140
|
+
},
|
|
21141
|
+
"values": {
|
|
21142
|
+
"description": "Optional allowed values for the claim.",
|
|
21143
|
+
"type": "array",
|
|
21144
|
+
"items": {
|
|
21145
|
+
"type": "string"
|
|
21146
|
+
}
|
|
21147
|
+
},
|
|
21148
|
+
"intent_to_retain": {
|
|
21149
|
+
"description": "Whether relying party intends to retain the claim.",
|
|
21150
|
+
"type": "boolean"
|
|
21151
|
+
}
|
|
21152
|
+
},
|
|
21153
|
+
"required": [
|
|
21154
|
+
"path"
|
|
21155
|
+
],
|
|
21156
|
+
"additionalProperties": false
|
|
21157
|
+
}
|
|
21158
|
+
}
|
|
21159
|
+
},
|
|
21160
|
+
"required": [
|
|
21161
|
+
"id",
|
|
21162
|
+
"format",
|
|
21163
|
+
"meta"
|
|
21164
|
+
],
|
|
21165
|
+
"additionalProperties": false
|
|
21166
|
+
}
|
|
21167
|
+
]
|
|
21168
|
+
},
|
|
21169
|
+
"description": "Credential queries requested by the verifier."
|
|
21170
|
+
},
|
|
21171
|
+
"credential_sets": {
|
|
21172
|
+
"description": "Optional higher-level credential set requirements.",
|
|
21173
|
+
"type": "array",
|
|
21174
|
+
"items": {
|
|
21175
|
+
"type": "object",
|
|
21176
|
+
"properties": {
|
|
21177
|
+
"options": {
|
|
21178
|
+
"minItems": 1,
|
|
21179
|
+
"type": "array",
|
|
21180
|
+
"items": {
|
|
21181
|
+
"minItems": 1,
|
|
21182
|
+
"type": "array",
|
|
21183
|
+
"items": {
|
|
21184
|
+
"type": "string"
|
|
21185
|
+
}
|
|
21186
|
+
},
|
|
21187
|
+
"description": "Alternative credential query id combinations."
|
|
21188
|
+
},
|
|
21189
|
+
"required": {
|
|
21190
|
+
"description": "Whether this credential set is mandatory.",
|
|
21191
|
+
"type": "boolean"
|
|
21192
|
+
}
|
|
21193
|
+
},
|
|
21194
|
+
"required": [
|
|
21195
|
+
"options"
|
|
21196
|
+
],
|
|
21197
|
+
"additionalProperties": false
|
|
21198
|
+
}
|
|
21199
|
+
}
|
|
21200
|
+
},
|
|
21201
|
+
"required": [
|
|
21202
|
+
"credentials"
|
|
21203
|
+
],
|
|
21204
|
+
"additionalProperties": false,
|
|
21205
|
+
"description": "DCQL query defining requested credentials and claims."
|
|
21206
|
+
},
|
|
21207
|
+
"transaction_data": {
|
|
21208
|
+
"description": "Optional transaction data descriptors.",
|
|
21209
|
+
"anyOf": [
|
|
21210
|
+
{
|
|
21211
|
+
"type": "array",
|
|
21212
|
+
"items": {
|
|
21213
|
+
"type": "object",
|
|
21214
|
+
"properties": {
|
|
21215
|
+
"type": {
|
|
21216
|
+
"type": "string",
|
|
21217
|
+
"description": "Transaction data type identifier."
|
|
21218
|
+
},
|
|
21219
|
+
"credential_ids": {
|
|
21220
|
+
"type": "array",
|
|
21221
|
+
"items": {
|
|
21222
|
+
"type": "string"
|
|
21223
|
+
},
|
|
21224
|
+
"description": "Credential query ids this transaction data applies to."
|
|
21225
|
+
},
|
|
21226
|
+
"payload": {
|
|
21227
|
+
"description": "Transaction details. Required for TS12 SCA transaction data."
|
|
21228
|
+
}
|
|
21229
|
+
},
|
|
21230
|
+
"required": [
|
|
21231
|
+
"type",
|
|
21232
|
+
"credential_ids"
|
|
21233
|
+
],
|
|
21234
|
+
"additionalProperties": {}
|
|
21235
|
+
}
|
|
21236
|
+
},
|
|
21237
|
+
{
|
|
21238
|
+
"type": "null"
|
|
21239
|
+
}
|
|
21240
|
+
]
|
|
21241
|
+
},
|
|
21242
|
+
"registration_cert": {
|
|
21243
|
+
"description": "Optional registration certificate request settings.",
|
|
21244
|
+
"anyOf": [
|
|
21245
|
+
{
|
|
21246
|
+
"type": "object",
|
|
21247
|
+
"properties": {
|
|
21248
|
+
"id": {
|
|
21249
|
+
"type": "string"
|
|
21250
|
+
},
|
|
21251
|
+
"body": {
|
|
21252
|
+
"type": "object",
|
|
21253
|
+
"properties": {
|
|
21254
|
+
"privacy_policy": {
|
|
21255
|
+
"type": "string"
|
|
21256
|
+
},
|
|
21257
|
+
"support_uri": {
|
|
21258
|
+
"type": "string"
|
|
21259
|
+
},
|
|
21260
|
+
"intermediary": {
|
|
21261
|
+
"type": "string"
|
|
21262
|
+
},
|
|
21263
|
+
"purpose": {
|
|
21264
|
+
"type": "array",
|
|
21265
|
+
"items": {
|
|
21266
|
+
"type": "object",
|
|
21267
|
+
"properties": {
|
|
21268
|
+
"lang": {
|
|
21269
|
+
"type": "string"
|
|
21270
|
+
},
|
|
21271
|
+
"content": {
|
|
21272
|
+
"type": "string"
|
|
21273
|
+
}
|
|
21274
|
+
},
|
|
21275
|
+
"required": [
|
|
21276
|
+
"lang",
|
|
21277
|
+
"content"
|
|
21278
|
+
],
|
|
21279
|
+
"additionalProperties": false
|
|
21280
|
+
}
|
|
21281
|
+
},
|
|
21282
|
+
"credentials": {
|
|
21283
|
+
"type": "array",
|
|
21284
|
+
"items": {
|
|
21285
|
+
"type": "object",
|
|
21286
|
+
"propertyNames": {
|
|
21287
|
+
"type": "string"
|
|
21288
|
+
},
|
|
21289
|
+
"additionalProperties": {}
|
|
21290
|
+
}
|
|
21291
|
+
},
|
|
21292
|
+
"provides_attestations": {
|
|
21293
|
+
"type": "array",
|
|
21294
|
+
"items": {
|
|
21295
|
+
"type": "string",
|
|
21296
|
+
"minLength": 1
|
|
21297
|
+
}
|
|
21298
|
+
}
|
|
21299
|
+
},
|
|
21300
|
+
"additionalProperties": false
|
|
21301
|
+
},
|
|
21302
|
+
"jwt": {
|
|
21303
|
+
"type": "string"
|
|
21304
|
+
}
|
|
21305
|
+
},
|
|
21306
|
+
"additionalProperties": false
|
|
21307
|
+
},
|
|
21308
|
+
{
|
|
21309
|
+
"type": "null"
|
|
21310
|
+
}
|
|
21311
|
+
]
|
|
21312
|
+
},
|
|
21313
|
+
"registrationCertImportJwt": {
|
|
21314
|
+
"description": "Optional imported registration certificate JWT.",
|
|
21315
|
+
"type": [
|
|
21316
|
+
"string",
|
|
21317
|
+
"null"
|
|
21318
|
+
]
|
|
21319
|
+
},
|
|
21320
|
+
"registrationCertImportId": {
|
|
21321
|
+
"description": "Optional registrar-side registration certificate id.",
|
|
21322
|
+
"type": [
|
|
21323
|
+
"string",
|
|
21324
|
+
"null"
|
|
21325
|
+
]
|
|
21326
|
+
},
|
|
21327
|
+
"registrationCertBodyPrivacyPolicy": {
|
|
21328
|
+
"description": "Optional registration certificate privacy policy URI.",
|
|
21329
|
+
"type": [
|
|
21330
|
+
"string",
|
|
21331
|
+
"null"
|
|
21332
|
+
]
|
|
21333
|
+
},
|
|
21334
|
+
"registrationCertBodySupportUri": {
|
|
21335
|
+
"description": "Optional registration certificate support URI.",
|
|
21336
|
+
"type": [
|
|
21337
|
+
"string",
|
|
21338
|
+
"null"
|
|
21339
|
+
]
|
|
21340
|
+
},
|
|
21341
|
+
"registrationCertBodyIntermediary": {
|
|
21342
|
+
"description": "Optional registration certificate intermediary.",
|
|
21343
|
+
"type": [
|
|
21344
|
+
"string",
|
|
21345
|
+
"null"
|
|
21346
|
+
]
|
|
21347
|
+
},
|
|
21348
|
+
"registrationCertBodyPurpose": {
|
|
21349
|
+
"description": "Optional registration certificate purpose entries.",
|
|
21350
|
+
"anyOf": [
|
|
21351
|
+
{
|
|
21352
|
+
"type": "array",
|
|
21353
|
+
"items": {
|
|
21354
|
+
"type": "object",
|
|
21355
|
+
"properties": {
|
|
21356
|
+
"lang": {
|
|
21357
|
+
"type": "string"
|
|
21358
|
+
},
|
|
21359
|
+
"content": {
|
|
21360
|
+
"type": "string"
|
|
21361
|
+
}
|
|
21362
|
+
},
|
|
21363
|
+
"additionalProperties": false
|
|
21364
|
+
}
|
|
21365
|
+
},
|
|
21366
|
+
{
|
|
21367
|
+
"type": "null"
|
|
21368
|
+
}
|
|
21369
|
+
]
|
|
21370
|
+
},
|
|
21371
|
+
"webhookEndpointId": {
|
|
21372
|
+
"description": "Optional webhook endpoint id for presentation callbacks.",
|
|
21373
|
+
"type": [
|
|
21374
|
+
"string",
|
|
21375
|
+
"null"
|
|
21376
|
+
]
|
|
21377
|
+
},
|
|
21378
|
+
"attached": {
|
|
21379
|
+
"description": "Optional attachments included with presentation requests.",
|
|
21380
|
+
"anyOf": [
|
|
21381
|
+
{
|
|
21382
|
+
"type": "array",
|
|
21383
|
+
"items": {
|
|
21384
|
+
"type": "object",
|
|
21385
|
+
"properties": {
|
|
21386
|
+
"format": {
|
|
21387
|
+
"type": "string",
|
|
21388
|
+
"description": "Attachment format identifier."
|
|
21389
|
+
},
|
|
21390
|
+
"data": {
|
|
21391
|
+
"description": "Attachment payload."
|
|
21392
|
+
},
|
|
21393
|
+
"credential_ids": {
|
|
21394
|
+
"description": "Optional credential query ids bound to this attachment.",
|
|
21395
|
+
"type": "array",
|
|
21396
|
+
"items": {
|
|
21397
|
+
"type": "string"
|
|
21398
|
+
}
|
|
21399
|
+
}
|
|
21400
|
+
},
|
|
21401
|
+
"required": [
|
|
21402
|
+
"format",
|
|
21403
|
+
"data"
|
|
21404
|
+
],
|
|
21405
|
+
"additionalProperties": false
|
|
21406
|
+
}
|
|
21407
|
+
},
|
|
21408
|
+
{
|
|
21409
|
+
"type": "null"
|
|
21410
|
+
}
|
|
21411
|
+
]
|
|
21412
|
+
},
|
|
21413
|
+
"redirectUri": {
|
|
21414
|
+
"description": "Optional redirect URI after presentation completion.",
|
|
21415
|
+
"type": [
|
|
21416
|
+
"string",
|
|
21417
|
+
"null"
|
|
21418
|
+
]
|
|
21419
|
+
},
|
|
21420
|
+
"accessKeyChainId": {
|
|
21421
|
+
"description": "Optional key chain id for access token/auth operations.",
|
|
21422
|
+
"type": [
|
|
21423
|
+
"string",
|
|
21424
|
+
"null"
|
|
21425
|
+
]
|
|
21426
|
+
},
|
|
21427
|
+
"readerAuth": {
|
|
21428
|
+
"description": "Whether reader authentication is required for mDoc requests.",
|
|
21429
|
+
"type": [
|
|
21430
|
+
"boolean",
|
|
21431
|
+
"null"
|
|
21432
|
+
]
|
|
21433
|
+
}
|
|
21434
|
+
},
|
|
21435
|
+
"required": [
|
|
21436
|
+
"id",
|
|
21437
|
+
"dcql_query"
|
|
21438
|
+
],
|
|
21439
|
+
"additionalProperties": false,
|
|
21440
|
+
"title": "PresentationConfigCreateDto"
|
|
21441
|
+
}
|
|
21442
|
+
}
|
|
21443
|
+
}
|
|
21444
|
+
];
|
|
21445
|
+
|
|
21446
|
+
// ../../packages/eudiplo-config-format/dist/config-validator.js
|
|
21447
|
+
var ajv = new import__.Ajv2020({
|
|
21448
|
+
allErrors: true,
|
|
21449
|
+
strict: false,
|
|
21450
|
+
validateFormats: false
|
|
21451
|
+
});
|
|
21452
|
+
for (const schema of CONFIG_SCHEMAS)
|
|
21453
|
+
ajv.addSchema(schema);
|
|
21454
|
+
function validateConfigDocument(document) {
|
|
21455
|
+
const file = serializeDocument(document);
|
|
21456
|
+
const validate = ajv.getSchema(file.$schema);
|
|
21457
|
+
if (!validate)
|
|
21458
|
+
throw new Error(`No bundled validator for ${file.$schema}`);
|
|
21459
|
+
const issues = [];
|
|
21460
|
+
if (!validate(file)) {
|
|
21461
|
+
for (const issue of validate.errors ?? [])
|
|
21462
|
+
issues.push({
|
|
21463
|
+
severity: "error",
|
|
21464
|
+
code: "CONFIG_SCHEMA_VALIDATION_FAILED",
|
|
21465
|
+
path: issue.instancePath || "/",
|
|
21466
|
+
message: issue.keyword === "additionalProperties" ? `Unknown property: ${String(issue.params.additionalProperty)}` : issue.message ?? "Invalid configuration",
|
|
21467
|
+
resource: { kind: document.kind, id: resourceId(document) }
|
|
21468
|
+
});
|
|
21469
|
+
}
|
|
21470
|
+
const id = document.kind === "Client" ? document.spec.clientId : document.spec.id;
|
|
21471
|
+
if (id !== void 0 && id !== resourceId(document))
|
|
21472
|
+
issues.push({
|
|
21473
|
+
severity: "error",
|
|
21474
|
+
code: "RESOURCE_ID_MISMATCH",
|
|
21475
|
+
path: document.kind === "Client" ? "/spec/clientId" : "/spec/id",
|
|
21476
|
+
message: "Resource identifier does not match metadata.id"
|
|
21477
|
+
});
|
|
21478
|
+
return issues;
|
|
21479
|
+
}
|
|
21480
|
+
|
|
21481
|
+
// src/commands/config/portability/migrations.ts
|
|
21482
|
+
function upgradeDocument(input) {
|
|
21483
|
+
const result = migrateDocument(input, validateConfigDocument);
|
|
21484
|
+
return { ...result, document: serializeDocument(result.document) };
|
|
21485
|
+
}
|
|
21486
|
+
|
|
21487
|
+
// src/commands/config/portability/upgrade.ts
|
|
21488
|
+
var codec = new ConfigBundleCodec();
|
|
21489
|
+
function parse3(text, path2) {
|
|
21490
|
+
try {
|
|
21491
|
+
return JSON.parse(text);
|
|
21492
|
+
} catch {
|
|
21493
|
+
throw new Error(`Invalid JSON in ${path2}`);
|
|
21494
|
+
}
|
|
21495
|
+
}
|
|
21496
|
+
function upgradeJson(input, file, context) {
|
|
21497
|
+
const bundle = Array.isArray(input?.documents);
|
|
21498
|
+
if (bundle) assertConfigBundle(input);
|
|
21499
|
+
const results = (bundle ? input.documents : [input]).map(
|
|
21500
|
+
(document) => upgradeDocument(document)
|
|
21501
|
+
);
|
|
21502
|
+
let blocked = false;
|
|
21503
|
+
for (const result of results) {
|
|
21504
|
+
for (const issue of result.issues) {
|
|
21505
|
+
context.stderr.write(
|
|
21506
|
+
`${file}${issue.path}: ${issue.severity} ${issue.code}: ${issue.message}
|
|
21507
|
+
`
|
|
21508
|
+
);
|
|
21509
|
+
if (issue.severity !== "warning") blocked = true;
|
|
21510
|
+
}
|
|
21511
|
+
for (const migration of result.migrations)
|
|
21512
|
+
context.stdout.write(`${file}: migration ${migration}
|
|
21513
|
+
`);
|
|
21514
|
+
}
|
|
21515
|
+
if (blocked) throw new Error(`${file}: upgrade blocked; no output written`);
|
|
21516
|
+
if (!bundle) return results[0].document;
|
|
21517
|
+
const documents = results.map(
|
|
21518
|
+
(result) => result.document
|
|
21519
|
+
);
|
|
21520
|
+
const resources = input.manifest.resources.map(
|
|
21521
|
+
(resource) => {
|
|
21522
|
+
const index = input.documents.findIndex(
|
|
21523
|
+
(candidate) => normalizeDocument(candidate).kind === resource.kind && resourceId(candidate) === resource.id
|
|
21524
|
+
);
|
|
21525
|
+
const document = documents[index];
|
|
21526
|
+
return {
|
|
21527
|
+
...resource,
|
|
21528
|
+
id: resourceId(document),
|
|
21529
|
+
$schema: document.$schema,
|
|
21530
|
+
sha256: createHash2("sha256").update(JSON.stringify(document)).digest("hex")
|
|
21531
|
+
};
|
|
21532
|
+
}
|
|
21533
|
+
);
|
|
21534
|
+
const output = {
|
|
21535
|
+
...input,
|
|
21536
|
+
manifest: { ...input.manifest, formatVersion: 2, resources },
|
|
21537
|
+
documents
|
|
21538
|
+
};
|
|
21539
|
+
assertConfigBundle(output);
|
|
21540
|
+
return output;
|
|
21541
|
+
}
|
|
21542
|
+
function report(input, output, file, parsed, context) {
|
|
21543
|
+
const changed = stableConfigJson(input) !== stableConfigJson(output);
|
|
21544
|
+
context.stdout.write(
|
|
21545
|
+
`${file}: ${changed ? "upgrade required" : "current"}
|
|
21546
|
+
`
|
|
21547
|
+
);
|
|
21548
|
+
if (parsed.flags.diff === true)
|
|
21549
|
+
for (const change of configChanges(input, output)) {
|
|
21550
|
+
context.stdout.write(
|
|
21551
|
+
` ${change.path}: ${JSON.stringify(change.before) ?? "(absent)"} -> ${JSON.stringify(change.after) ?? "(absent)"}
|
|
21552
|
+
`
|
|
21553
|
+
);
|
|
21554
|
+
}
|
|
21555
|
+
return changed;
|
|
21556
|
+
}
|
|
21557
|
+
function inferredKind(path2) {
|
|
21558
|
+
const normalized = path2.split(sep2).join("/");
|
|
21559
|
+
const match = TENANT_RESOURCE_REGISTRY.find(
|
|
21560
|
+
(entry) => entry.kind === "file" ? normalized === entry.file || normalized.endsWith(`/${entry.file}`) : normalized.endsWith(`/${entry.subfolder}/${basename2(path2)}`) || normalized === `${entry.subfolder}/${basename2(path2)}`
|
|
21561
|
+
);
|
|
21562
|
+
return CONFIG_RESOURCE_KINDS.find(
|
|
21563
|
+
(kind) => `${CONFIG_FORMATS[kind].file}.schema.json` === match?.schemaFile
|
|
21564
|
+
);
|
|
21565
|
+
}
|
|
21566
|
+
function wrapBare(kind, input, path2) {
|
|
21567
|
+
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
21568
|
+
throw new Error(`${path2}: configuration must be an object`);
|
|
21569
|
+
const singleton = {
|
|
21570
|
+
Tenant: "tenant",
|
|
21571
|
+
KmsConfig: "kms",
|
|
21572
|
+
RegistrarConfig: "registrar",
|
|
21573
|
+
IssuanceConfig: "issuance"
|
|
21574
|
+
};
|
|
21575
|
+
const spec = structuredClone(input);
|
|
21576
|
+
if (kind === "KeyChain" && spec.key && !spec.keySource) {
|
|
21577
|
+
spec.keySource = { type: "private-jwk", jwk: spec.key };
|
|
21578
|
+
delete spec.key;
|
|
21579
|
+
}
|
|
21580
|
+
const id = String(
|
|
21581
|
+
singleton[kind] ?? spec.id ?? spec.clientId ?? basename2(path2, ".json")
|
|
21582
|
+
);
|
|
21583
|
+
if (!singleton[kind]) spec[kind === "Client" ? "clientId" : "id"] = id;
|
|
21584
|
+
return { $schema: schemaUrl(kind, 1), metadata: { generation: 1 }, spec };
|
|
21585
|
+
}
|
|
21586
|
+
async function folderFiles(path2) {
|
|
21587
|
+
const files = [];
|
|
21588
|
+
for (const entry of await readdir(path2, { withFileTypes: true })) {
|
|
21589
|
+
if (entry.name.startsWith(".")) continue;
|
|
21590
|
+
const file = join4(path2, entry.name);
|
|
21591
|
+
if (entry.isSymbolicLink())
|
|
21592
|
+
throw new Error(`Config folder contains a symbolic link: ${file}`);
|
|
21593
|
+
if (entry.isDirectory()) files.push(...await folderFiles(file));
|
|
21594
|
+
else if (entry.isFile()) files.push(file);
|
|
21595
|
+
}
|
|
21596
|
+
return files.sort();
|
|
21597
|
+
}
|
|
21598
|
+
async function upgradeFolder(source, parsed, context) {
|
|
21599
|
+
const outputs = /* @__PURE__ */ new Map();
|
|
21600
|
+
let changed = 0;
|
|
21601
|
+
for (const file of await folderFiles(source)) {
|
|
21602
|
+
if (extname(file) !== ".json" || relative2(source, file).split(sep2).includes("images"))
|
|
21603
|
+
continue;
|
|
21604
|
+
const input = parse3(await readFile4(file, "utf8"), file);
|
|
21605
|
+
const kind = inferredKind(relative2(source, file));
|
|
21606
|
+
if (!kind && !isConfigDocument(input)) continue;
|
|
21607
|
+
const candidate = isConfigDocument(input) ? input : wrapBare(kind, input, file);
|
|
21608
|
+
const output = upgradeJson(candidate, file, context);
|
|
21609
|
+
if (kind && normalizeDocument(output).kind !== kind)
|
|
21610
|
+
throw new Error(`${file}: expected ${kind} configuration`);
|
|
21611
|
+
if (report(input, output, relative2(source, file), parsed, context))
|
|
21612
|
+
changed++;
|
|
21613
|
+
outputs.set(
|
|
21614
|
+
relative2(source, file),
|
|
21615
|
+
`${JSON.stringify(output, null, 2)}
|
|
21616
|
+
`
|
|
21617
|
+
);
|
|
21618
|
+
}
|
|
21619
|
+
if (!outputs.size)
|
|
21620
|
+
throw new Error(`No configuration documents found in ${source}`);
|
|
21621
|
+
context.stdout.write(
|
|
21622
|
+
`${outputs.size - changed} current, ${changed} require upgrading
|
|
21623
|
+
`
|
|
21624
|
+
);
|
|
21625
|
+
if (parsed.flags.check === true) return changed ? 1 : 0;
|
|
21626
|
+
if (parsed.flags["dry-run"] === true) return 0;
|
|
21627
|
+
const destination = resolve3(
|
|
21628
|
+
context.cwd,
|
|
21629
|
+
readStringFlag(parsed.flags, "output") ?? `${source}.upgraded`
|
|
21630
|
+
);
|
|
21631
|
+
const overlap = (parent, child) => {
|
|
21632
|
+
const path2 = relative2(parent, child);
|
|
21633
|
+
return path2 === "" || !path2.startsWith(`..${sep2}`) && path2 !== "..";
|
|
21634
|
+
};
|
|
21635
|
+
if (overlap(source, destination) || overlap(destination, source))
|
|
21636
|
+
throw new Error(
|
|
21637
|
+
"Folder output must be separate from the source directory"
|
|
21638
|
+
);
|
|
21639
|
+
await lstat(destination).then(
|
|
21640
|
+
() => {
|
|
21641
|
+
throw new Error(`Output already exists: ${destination}`);
|
|
21642
|
+
},
|
|
21643
|
+
(error) => {
|
|
21644
|
+
if (error.code !== "ENOENT") throw error;
|
|
21645
|
+
}
|
|
21646
|
+
);
|
|
21647
|
+
await mkdir3(dirname3(destination), { recursive: true });
|
|
21648
|
+
const staging = await mkdtemp(
|
|
21649
|
+
join4(dirname3(destination), ".eudiplo-upgrade-")
|
|
21650
|
+
);
|
|
21651
|
+
try {
|
|
21652
|
+
await cp(source, staging, {
|
|
21653
|
+
recursive: true,
|
|
21654
|
+
filter: (path2) => path2 === source || !relative2(source, path2).split(sep2).some((part) => part.startsWith("."))
|
|
21655
|
+
});
|
|
21656
|
+
for (const [path2, contents] of outputs)
|
|
21657
|
+
await atomicWriteFile(join4(staging, path2), contents);
|
|
21658
|
+
await rename2(staging, destination);
|
|
21659
|
+
context.stdout.write(
|
|
21660
|
+
`Wrote upgraded configuration folder to ${destination}
|
|
21661
|
+
`
|
|
21662
|
+
);
|
|
21663
|
+
} finally {
|
|
20194
21664
|
await rm(staging, { recursive: true, force: true });
|
|
20195
21665
|
}
|
|
20196
21666
|
return 0;
|
|
@@ -20537,14 +22007,11 @@ function createComposeEnv(options) {
|
|
|
20537
22007
|
`AUTH_CLIENT_ID=${escapeEnvValue(authClientId)}`,
|
|
20538
22008
|
`AUTH_CLIENT_SECRET=${escapeEnvValue(authClientSecret)}`
|
|
20539
22009
|
];
|
|
20540
|
-
lines.push(
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
"",
|
|
20544
|
-
|
|
20545
|
-
"",
|
|
20546
|
-
...kmsEnv(kms)
|
|
20547
|
-
);
|
|
22010
|
+
lines.push("", ...databaseEnv(database), "", ...storageEnv(storage));
|
|
22011
|
+
const kmsLines = kmsEnv(kms);
|
|
22012
|
+
if (kmsLines.length > 0) {
|
|
22013
|
+
lines.push("", ...kmsLines);
|
|
22014
|
+
}
|
|
20548
22015
|
return `${lines.join("\n")}
|
|
20549
22016
|
`;
|
|
20550
22017
|
}
|
|
@@ -20592,28 +22059,24 @@ function storageEnv(storage) {
|
|
|
20592
22059
|
if (storage === "local") {
|
|
20593
22060
|
return ["STORAGE_DRIVER=local", "LOCAL_STORAGE_DIR=/app/uploads"];
|
|
20594
22061
|
}
|
|
20595
|
-
const
|
|
22062
|
+
const rustfsPassword = randomSecret();
|
|
20596
22063
|
return [
|
|
20597
22064
|
"STORAGE_DRIVER=s3",
|
|
20598
|
-
"S3_ENDPOINT=http://
|
|
22065
|
+
"S3_ENDPOINT=http://rustfs:9000",
|
|
20599
22066
|
"S3_REGION=us-east-1",
|
|
20600
|
-
"S3_ACCESS_KEY_ID=
|
|
20601
|
-
`S3_SECRET_ACCESS_KEY=${
|
|
22067
|
+
"S3_ACCESS_KEY_ID=rustfsadmin",
|
|
22068
|
+
`S3_SECRET_ACCESS_KEY=${rustfsPassword}`,
|
|
20602
22069
|
"S3_BUCKET=uploads",
|
|
20603
22070
|
"S3_FORCE_PATH_STYLE=true",
|
|
20604
|
-
"
|
|
20605
|
-
`
|
|
22071
|
+
"RUSTFS_ACCESS_KEY=rustfsadmin",
|
|
22072
|
+
`RUSTFS_SECRET_KEY=${rustfsPassword}`
|
|
20606
22073
|
];
|
|
20607
22074
|
}
|
|
20608
22075
|
function kmsEnv(kms) {
|
|
20609
22076
|
if (kms === "db") {
|
|
20610
|
-
return [
|
|
22077
|
+
return [];
|
|
20611
22078
|
}
|
|
20612
|
-
return [
|
|
20613
|
-
"KM_TYPE=vault",
|
|
20614
|
-
"VAULT_ADDR=http://vault:8200",
|
|
20615
|
-
"VAULT_TOKEN=root"
|
|
20616
|
-
];
|
|
22079
|
+
return ["VAULT_ADDR=http://vault:8200", "VAULT_TOKEN=root"];
|
|
20617
22080
|
}
|
|
20618
22081
|
function randomSecret() {
|
|
20619
22082
|
return randomBytes(24).toString("base64url");
|
|
@@ -20755,10 +22218,10 @@ function formatTextReport(scope, rootPath, results) {
|
|
|
20755
22218
|
function formatTenantBlock(result) {
|
|
20756
22219
|
const lines = [`${result.valid ? "PASS" : "FAIL"} ${result.id}`];
|
|
20757
22220
|
if (result.valid) {
|
|
20758
|
-
for (const [resourceType,
|
|
22221
|
+
for (const [resourceType, count2] of Object.entries(
|
|
20759
22222
|
result.resourceCounts
|
|
20760
22223
|
)) {
|
|
20761
|
-
lines.push(` ${
|
|
22224
|
+
lines.push(` ${count2} ${pluralize(resourceType, count2)}`);
|
|
20762
22225
|
}
|
|
20763
22226
|
} else {
|
|
20764
22227
|
lines.push(...formatTenantErrors(result.errors));
|
|
@@ -20804,8 +22267,8 @@ function groupErrorsByFile(errors) {
|
|
|
20804
22267
|
}
|
|
20805
22268
|
return Array.from(grouped.entries());
|
|
20806
22269
|
}
|
|
20807
|
-
function pluralize(label,
|
|
20808
|
-
return
|
|
22270
|
+
function pluralize(label, count2) {
|
|
22271
|
+
return count2 === 1 ? label : `${label}s`;
|
|
20809
22272
|
}
|
|
20810
22273
|
|
|
20811
22274
|
// src/commands/config/validate/validator.ts
|
|
@@ -21000,7 +22463,10 @@ async function validateResourceFile(filePath, relativeFile, schemaFile, getValid
|
|
|
21000
22463
|
delete spec.key;
|
|
21001
22464
|
}
|
|
21002
22465
|
if (!CONFIG_SINGLETON_IDS[kind])
|
|
21003
|
-
spec[kind === "Client" ? "clientId" : "id"] ??= basename4(
|
|
22466
|
+
spec[kind === "Client" ? "clientId" : "id"] ??= basename4(
|
|
22467
|
+
relativeFile,
|
|
22468
|
+
".json"
|
|
22469
|
+
);
|
|
21004
22470
|
resolved = {
|
|
21005
22471
|
$schema: schemaUrl(kind),
|
|
21006
22472
|
metadata: { generation: 1 },
|
|
@@ -21550,8 +23016,169 @@ function createConfigCommand(context, setExitCode) {
|
|
|
21550
23016
|
|
|
21551
23017
|
// src/services/deployment-drivers.ts
|
|
21552
23018
|
import { access as access2, mkdir as mkdir6, rm as rm4, stat as stat2, unlink as unlink2, writeFile as writeFile5 } from "node:fs/promises";
|
|
21553
|
-
import { delimiter, join as join10, resolve as
|
|
23019
|
+
import { delimiter, join as join10, resolve as resolve6 } from "node:path";
|
|
21554
23020
|
import { spawn } from "node:child_process";
|
|
23021
|
+
|
|
23022
|
+
// src/services/compose-args.ts
|
|
23023
|
+
import { resolve as resolve5 } from "node:path";
|
|
23024
|
+
var SERVICE_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/;
|
|
23025
|
+
var DURATION = /^(\d+(\.\d+)?(ns|us|µs|ms|s|m|h))+$/;
|
|
23026
|
+
var TIMESTAMP = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})?)?$/;
|
|
23027
|
+
function assertServiceName(service) {
|
|
23028
|
+
if (!SERVICE_NAME.test(service)) {
|
|
23029
|
+
throw new Error(
|
|
23030
|
+
`Invalid service name ${JSON.stringify(service)}. Service names start with a letter or digit and contain only letters, digits, ".", "_" and "-".`
|
|
23031
|
+
);
|
|
23032
|
+
}
|
|
23033
|
+
}
|
|
23034
|
+
function assertSince(since) {
|
|
23035
|
+
const isDuration = DURATION.test(since);
|
|
23036
|
+
const isTimestamp = TIMESTAMP.test(since) && !Number.isNaN(Date.parse(since));
|
|
23037
|
+
if (!isDuration && !isTimestamp) {
|
|
23038
|
+
throw new Error(
|
|
23039
|
+
`Invalid --since value ${JSON.stringify(since)}. Use a duration such as 10m or 2h30m, or a timestamp such as 2026-09-01T12:00:00Z.`
|
|
23040
|
+
);
|
|
23041
|
+
}
|
|
23042
|
+
}
|
|
23043
|
+
function assertTail(tail) {
|
|
23044
|
+
if (!Number.isInteger(tail) || tail < 0) {
|
|
23045
|
+
throw new Error("--tail must be a whole number of lines, 0 or more.");
|
|
23046
|
+
}
|
|
23047
|
+
}
|
|
23048
|
+
function buildComposeBaseArgs(instance, projectDirectory, composeFiles) {
|
|
23049
|
+
const args = ["compose"];
|
|
23050
|
+
if (instance.envFile) {
|
|
23051
|
+
args.push("--env-file", resolve5(projectDirectory, instance.envFile));
|
|
23052
|
+
}
|
|
23053
|
+
for (const composeFile of composeFiles) {
|
|
23054
|
+
args.push("-f", resolve5(projectDirectory, composeFile));
|
|
23055
|
+
}
|
|
23056
|
+
for (const profile of instance.composeProfiles ?? []) {
|
|
23057
|
+
args.push("--profile", profile);
|
|
23058
|
+
}
|
|
23059
|
+
if (instance.projectName) {
|
|
23060
|
+
args.push("--project-name", instance.projectName);
|
|
23061
|
+
}
|
|
23062
|
+
return args;
|
|
23063
|
+
}
|
|
23064
|
+
function buildComposePsArgs() {
|
|
23065
|
+
return ["ps"];
|
|
23066
|
+
}
|
|
23067
|
+
function buildComposeListServicesArgs() {
|
|
23068
|
+
return ["config", "--services"];
|
|
23069
|
+
}
|
|
23070
|
+
function buildComposeLogsArgs(service, options = {}) {
|
|
23071
|
+
const args = ["logs"];
|
|
23072
|
+
if (options.follow === true) {
|
|
23073
|
+
args.push("--follow");
|
|
23074
|
+
}
|
|
23075
|
+
if (options.tail !== void 0) {
|
|
23076
|
+
assertTail(options.tail);
|
|
23077
|
+
args.push("--tail", String(options.tail));
|
|
23078
|
+
}
|
|
23079
|
+
if (options.since !== void 0) {
|
|
23080
|
+
assertSince(options.since);
|
|
23081
|
+
args.push("--since", options.since);
|
|
23082
|
+
}
|
|
23083
|
+
if (service !== void 0) {
|
|
23084
|
+
assertServiceName(service);
|
|
23085
|
+
args.push(service);
|
|
23086
|
+
}
|
|
23087
|
+
return args;
|
|
23088
|
+
}
|
|
23089
|
+
function buildComposePullArgs(service) {
|
|
23090
|
+
const args = ["pull"];
|
|
23091
|
+
if (service !== void 0) {
|
|
23092
|
+
assertServiceName(service);
|
|
23093
|
+
args.push(service);
|
|
23094
|
+
}
|
|
23095
|
+
return args;
|
|
23096
|
+
}
|
|
23097
|
+
function buildComposeUpArgs() {
|
|
23098
|
+
return ["up", "-d"];
|
|
23099
|
+
}
|
|
23100
|
+
function buildComposeRestartArgs(service) {
|
|
23101
|
+
const args = ["restart"];
|
|
23102
|
+
if (service !== void 0) {
|
|
23103
|
+
assertServiceName(service);
|
|
23104
|
+
args.push(service);
|
|
23105
|
+
}
|
|
23106
|
+
return args;
|
|
23107
|
+
}
|
|
23108
|
+
function parseServiceList(stdout) {
|
|
23109
|
+
return stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => SERVICE_NAME.test(line)).sort();
|
|
23110
|
+
}
|
|
23111
|
+
function assertKnownService(service, known) {
|
|
23112
|
+
if (!known.includes(service)) {
|
|
23113
|
+
throw new Error(
|
|
23114
|
+
`Unknown service ${service}. Services in this project: ${known.join(", ") || "none"}.`
|
|
23115
|
+
);
|
|
23116
|
+
}
|
|
23117
|
+
}
|
|
23118
|
+
|
|
23119
|
+
// src/services/container-state.ts
|
|
23120
|
+
function parseContainerStates(stdout) {
|
|
23121
|
+
const trimmed = stdout.trim();
|
|
23122
|
+
if (!trimmed) {
|
|
23123
|
+
return [];
|
|
23124
|
+
}
|
|
23125
|
+
const entries = trimmed.startsWith("[") ? asArray(JSON.parse(trimmed)) : trimmed.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("{")).map((line) => JSON.parse(line));
|
|
23126
|
+
return entries.map((entry) => {
|
|
23127
|
+
const record = entry;
|
|
23128
|
+
const names = record.Names;
|
|
23129
|
+
return {
|
|
23130
|
+
name: Array.isArray(names) ? String(names[0] ?? "") : String(names ?? record.Name ?? ""),
|
|
23131
|
+
state: String(record.State ?? ""),
|
|
23132
|
+
status: String(record.Status ?? "")
|
|
23133
|
+
};
|
|
23134
|
+
});
|
|
23135
|
+
}
|
|
23136
|
+
function summarizeContainerStates(containers, projectName) {
|
|
23137
|
+
const name = "service containers";
|
|
23138
|
+
if (containers.length === 0) {
|
|
23139
|
+
return {
|
|
23140
|
+
name,
|
|
23141
|
+
status: "warn",
|
|
23142
|
+
message: `No containers found for project ${projectName}. Start it with eudiplo up.`
|
|
23143
|
+
};
|
|
23144
|
+
}
|
|
23145
|
+
const unhealthy = containers.filter(
|
|
23146
|
+
(container) => container.status.toLowerCase().includes("unhealthy")
|
|
23147
|
+
);
|
|
23148
|
+
if (unhealthy.length > 0) {
|
|
23149
|
+
return {
|
|
23150
|
+
name,
|
|
23151
|
+
status: "fail",
|
|
23152
|
+
message: `Unhealthy: ${describe(unhealthy)}`
|
|
23153
|
+
};
|
|
23154
|
+
}
|
|
23155
|
+
const notRunning = containers.filter(
|
|
23156
|
+
(container) => container.state.toLowerCase() !== "running"
|
|
23157
|
+
);
|
|
23158
|
+
if (notRunning.length > 0) {
|
|
23159
|
+
return {
|
|
23160
|
+
name,
|
|
23161
|
+
status: "warn",
|
|
23162
|
+
message: `${containers.length - notRunning.length}/${containers.length} running. Not running: ${describe(notRunning)}`
|
|
23163
|
+
};
|
|
23164
|
+
}
|
|
23165
|
+
const starting = containers.filter(
|
|
23166
|
+
(container) => container.status.toLowerCase().includes("starting")
|
|
23167
|
+
).length;
|
|
23168
|
+
return {
|
|
23169
|
+
name,
|
|
23170
|
+
status: "pass",
|
|
23171
|
+
message: `${containers.length} container(s) running${starting > 0 ? `, ${starting} still starting` : ""}.`
|
|
23172
|
+
};
|
|
23173
|
+
}
|
|
23174
|
+
function asArray(value) {
|
|
23175
|
+
return Array.isArray(value) ? value : [];
|
|
23176
|
+
}
|
|
23177
|
+
function describe(containers) {
|
|
23178
|
+
return containers.map((container) => `${container.name} (${container.status})`).join(", ");
|
|
23179
|
+
}
|
|
23180
|
+
|
|
23181
|
+
// src/services/deployment-drivers.ts
|
|
21555
23182
|
var drivers = {
|
|
21556
23183
|
compose: {
|
|
21557
23184
|
target: "compose",
|
|
@@ -21564,10 +23191,16 @@ var drivers = {
|
|
|
21564
23191
|
status: runtime ? "pass" : "warn",
|
|
21565
23192
|
message: runtime ? `${runtime.name} found at ${runtime.command}` : "Docker or Podman was not found in a supported install location."
|
|
21566
23193
|
});
|
|
23194
|
+
if (runtime) {
|
|
23195
|
+
checks.push(await composeProviderCheck(runtime, context));
|
|
23196
|
+
checks.push(
|
|
23197
|
+
await composeServicesCheck(runtime, instance, context)
|
|
23198
|
+
);
|
|
23199
|
+
}
|
|
21567
23200
|
for (const composeFile of getComposeFiles(instance)) {
|
|
21568
23201
|
let found = true;
|
|
21569
23202
|
try {
|
|
21570
|
-
await access2(
|
|
23203
|
+
await access2(resolve6(projectDirectory, composeFile));
|
|
21571
23204
|
} catch {
|
|
21572
23205
|
found = false;
|
|
21573
23206
|
}
|
|
@@ -21585,8 +23218,41 @@ var drivers = {
|
|
|
21585
23218
|
down(options) {
|
|
21586
23219
|
return runCompose(["down", ...options.args], options);
|
|
21587
23220
|
},
|
|
21588
|
-
logs(options) {
|
|
21589
|
-
|
|
23221
|
+
async logs(options) {
|
|
23222
|
+
const service = await resolveComposeService(options);
|
|
23223
|
+
const { follow, tail } = readLogOptions(options.flags);
|
|
23224
|
+
const since = typeof options.flags.since === "string" ? options.flags.since : void 0;
|
|
23225
|
+
return runCompose(
|
|
23226
|
+
[
|
|
23227
|
+
...buildComposeLogsArgs(service, { follow, tail, since }),
|
|
23228
|
+
...options.args
|
|
23229
|
+
],
|
|
23230
|
+
options
|
|
23231
|
+
);
|
|
23232
|
+
},
|
|
23233
|
+
async ps(options) {
|
|
23234
|
+
return runCompose(
|
|
23235
|
+
[...buildComposePsArgs(), ...options.args],
|
|
23236
|
+
options
|
|
23237
|
+
);
|
|
23238
|
+
},
|
|
23239
|
+
async pull(options) {
|
|
23240
|
+
const service = await resolveComposeService(options);
|
|
23241
|
+
return runCompose(
|
|
23242
|
+
[...buildComposePullArgs(service), ...options.args],
|
|
23243
|
+
options
|
|
23244
|
+
);
|
|
23245
|
+
},
|
|
23246
|
+
async restart(options) {
|
|
23247
|
+
assertWritable(options);
|
|
23248
|
+
const service = await resolveComposeService(options);
|
|
23249
|
+
const restartArgs = buildComposeRestartArgs(service);
|
|
23250
|
+
const runtime = await resolveComposeRuntime(options.context.env);
|
|
23251
|
+
options.context.stdout.write(
|
|
23252
|
+
`Restarting ${service ?? "all services"} of ${options.instanceName}${runtime ? ` with ${runtime.name} compose` : ""}
|
|
23253
|
+
`
|
|
23254
|
+
);
|
|
23255
|
+
return runCompose([...restartArgs, ...options.args], options);
|
|
21590
23256
|
}
|
|
21591
23257
|
},
|
|
21592
23258
|
external: {
|
|
@@ -21606,7 +23272,11 @@ var drivers = {
|
|
|
21606
23272
|
);
|
|
21607
23273
|
return runKubectl(
|
|
21608
23274
|
[
|
|
21609
|
-
...buildLogsArgs(
|
|
23275
|
+
...buildLogsArgs(
|
|
23276
|
+
scope,
|
|
23277
|
+
workload,
|
|
23278
|
+
readLogOptions(options.flags)
|
|
23279
|
+
),
|
|
21610
23280
|
...options.args
|
|
21611
23281
|
],
|
|
21612
23282
|
options
|
|
@@ -21634,14 +23304,17 @@ var drivers = {
|
|
|
21634
23304
|
[...buildRestartArgs(scope, workload), ...options.args],
|
|
21635
23305
|
options
|
|
21636
23306
|
);
|
|
21637
|
-
if (restarted !== 0 || options.flags
|
|
23307
|
+
if (restarted !== 0 || options.flags["no-wait"] === true) {
|
|
21638
23308
|
return restarted;
|
|
21639
23309
|
}
|
|
21640
23310
|
return runKubectl(buildRolloutStatusArgs(scope, workload), options);
|
|
21641
23311
|
}
|
|
21642
23312
|
}
|
|
21643
23313
|
};
|
|
21644
|
-
function assertWritable({
|
|
23314
|
+
function assertWritable({
|
|
23315
|
+
instance,
|
|
23316
|
+
instanceName
|
|
23317
|
+
}) {
|
|
21645
23318
|
if (instance.readOnly === true) {
|
|
21646
23319
|
throw new Error(
|
|
21647
23320
|
`Instance ${instanceName} is registered read-only. Re-register it without --read-only to allow changes.`
|
|
@@ -21847,7 +23520,10 @@ function unreadyEndpoints(stdout) {
|
|
|
21847
23520
|
(endpoint) => isRecord3(endpoint) && // An omitted ready condition means ready, per the API contract.
|
|
21848
23521
|
(!isRecord3(endpoint.conditions) || endpoint.conditions.ready !== false)
|
|
21849
23522
|
);
|
|
21850
|
-
readyByService.set(
|
|
23523
|
+
readyByService.set(
|
|
23524
|
+
service,
|
|
23525
|
+
(readyByService.get(service) ?? false) || ready
|
|
23526
|
+
);
|
|
21851
23527
|
}
|
|
21852
23528
|
return [...readyByService.entries()].filter(([, ready]) => !ready).map(([service]) => service).sort();
|
|
21853
23529
|
}
|
|
@@ -21975,7 +23651,7 @@ async function ensureComposeProject(cwd, options) {
|
|
|
21975
23651
|
composeProfiles: composeProfiles(options),
|
|
21976
23652
|
envFile: envFileName,
|
|
21977
23653
|
projectName: mode === "demo" ? "eudiplo-demo" : "eudiplo",
|
|
21978
|
-
projectDirectory:
|
|
23654
|
+
projectDirectory: resolve6(cwd)
|
|
21979
23655
|
};
|
|
21980
23656
|
}
|
|
21981
23657
|
async function ensureProjectDirectory(directory) {
|
|
@@ -22029,23 +23705,14 @@ function unsupportedCommand(command, target) {
|
|
|
22029
23705
|
}
|
|
22030
23706
|
async function runCompose(args, { instance, context }) {
|
|
22031
23707
|
const projectDirectory = instance.projectDirectory ?? context.cwd;
|
|
22032
|
-
const composeArgs = [
|
|
22033
|
-
|
|
22034
|
-
|
|
22035
|
-
|
|
22036
|
-
|
|
22037
|
-
)
|
|
22038
|
-
|
|
22039
|
-
|
|
22040
|
-
composeArgs.push("-f", resolve5(projectDirectory, composeFile));
|
|
22041
|
-
}
|
|
22042
|
-
for (const profile of instance.composeProfiles ?? []) {
|
|
22043
|
-
composeArgs.push("--profile", profile);
|
|
22044
|
-
}
|
|
22045
|
-
if (instance.projectName) {
|
|
22046
|
-
composeArgs.push("--project-name", instance.projectName);
|
|
22047
|
-
}
|
|
22048
|
-
composeArgs.push(...args);
|
|
23708
|
+
const composeArgs = [
|
|
23709
|
+
...buildComposeBaseArgs(
|
|
23710
|
+
instance,
|
|
23711
|
+
projectDirectory,
|
|
23712
|
+
getComposeFiles(instance)
|
|
23713
|
+
),
|
|
23714
|
+
...args
|
|
23715
|
+
];
|
|
22049
23716
|
const composeRuntime = await resolveComposeRuntime(context.env);
|
|
22050
23717
|
if (!composeRuntime) {
|
|
22051
23718
|
context.stderr.write(
|
|
@@ -22054,17 +23721,158 @@ async function runCompose(args, { instance, context }) {
|
|
|
22054
23721
|
return 1;
|
|
22055
23722
|
}
|
|
22056
23723
|
return new Promise((resolveProcess) => {
|
|
22057
|
-
const child = spawn(composeRuntime.command, composeArgs, {
|
|
22058
|
-
cwd: projectDirectory,
|
|
23724
|
+
const child = spawn(composeRuntime.command, composeArgs, {
|
|
23725
|
+
cwd: projectDirectory,
|
|
23726
|
+
env: context.env,
|
|
23727
|
+
stdio: "inherit"
|
|
23728
|
+
});
|
|
23729
|
+
child.on("error", (error) => {
|
|
23730
|
+
context.stderr.write(`${error.message}
|
|
23731
|
+
`);
|
|
23732
|
+
resolveProcess(1);
|
|
23733
|
+
});
|
|
23734
|
+
child.on("close", (code) => resolveProcess(code ?? 1));
|
|
23735
|
+
});
|
|
23736
|
+
}
|
|
23737
|
+
async function captureCompose(args, { instance, context }) {
|
|
23738
|
+
const projectDirectory = instance.projectDirectory ?? context.cwd;
|
|
23739
|
+
const composeRuntime = await resolveComposeRuntime(context.env);
|
|
23740
|
+
if (!composeRuntime) {
|
|
23741
|
+
throw new Error(
|
|
23742
|
+
"Docker or Podman was not found in a supported install location."
|
|
23743
|
+
);
|
|
23744
|
+
}
|
|
23745
|
+
const composeArgs = [
|
|
23746
|
+
...buildComposeBaseArgs(
|
|
23747
|
+
instance,
|
|
23748
|
+
projectDirectory,
|
|
23749
|
+
getComposeFiles(instance)
|
|
23750
|
+
),
|
|
23751
|
+
...args
|
|
23752
|
+
];
|
|
23753
|
+
return new Promise((resolveProcess) => {
|
|
23754
|
+
const child = spawn(composeRuntime.command, composeArgs, {
|
|
23755
|
+
cwd: projectDirectory,
|
|
23756
|
+
env: context.env,
|
|
23757
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
23758
|
+
});
|
|
23759
|
+
let stdout = "";
|
|
23760
|
+
let stderr = "";
|
|
23761
|
+
child.stdout?.on("data", (chunk) => {
|
|
23762
|
+
stdout += String(chunk);
|
|
23763
|
+
});
|
|
23764
|
+
child.stderr?.on("data", (chunk) => {
|
|
23765
|
+
stderr += String(chunk);
|
|
23766
|
+
});
|
|
23767
|
+
child.on(
|
|
23768
|
+
"error",
|
|
23769
|
+
(error) => resolveProcess({ code: 1, stdout, stderr: error.message })
|
|
23770
|
+
);
|
|
23771
|
+
child.on(
|
|
23772
|
+
"close",
|
|
23773
|
+
(code) => resolveProcess({ code: code ?? 1, stdout, stderr })
|
|
23774
|
+
);
|
|
23775
|
+
});
|
|
23776
|
+
}
|
|
23777
|
+
async function resolveComposeService(options) {
|
|
23778
|
+
const service = readServiceFlag(options.flags);
|
|
23779
|
+
if (service === void 0) {
|
|
23780
|
+
return void 0;
|
|
23781
|
+
}
|
|
23782
|
+
assertServiceName(service);
|
|
23783
|
+
const listed = await captureCompose(
|
|
23784
|
+
buildComposeListServicesArgs(),
|
|
23785
|
+
options
|
|
23786
|
+
);
|
|
23787
|
+
if (listed.code !== 0) {
|
|
23788
|
+
throw new Error(
|
|
23789
|
+
`Could not list Compose services: ${listed.stderr.trim() || `exit code ${listed.code}`}`
|
|
23790
|
+
);
|
|
23791
|
+
}
|
|
23792
|
+
assertKnownService(service, parseServiceList(listed.stdout));
|
|
23793
|
+
return service;
|
|
23794
|
+
}
|
|
23795
|
+
async function composeProviderCheck(runtime, context) {
|
|
23796
|
+
const version = await captureRuntime(
|
|
23797
|
+
runtime.command,
|
|
23798
|
+
["compose", "version"],
|
|
23799
|
+
context
|
|
23800
|
+
);
|
|
23801
|
+
if (version.code !== 0) {
|
|
23802
|
+
return {
|
|
23803
|
+
name: "compose provider",
|
|
23804
|
+
status: "fail",
|
|
23805
|
+
message: `${runtime.name} compose is not usable: ${firstLine(version.stderr) || `exit code ${version.code}`}`
|
|
23806
|
+
};
|
|
23807
|
+
}
|
|
23808
|
+
return {
|
|
23809
|
+
name: "compose provider",
|
|
23810
|
+
status: "pass",
|
|
23811
|
+
message: firstLine(version.stdout) || `${runtime.name} compose works`
|
|
23812
|
+
};
|
|
23813
|
+
}
|
|
23814
|
+
async function composeServicesCheck(runtime, instance, context) {
|
|
23815
|
+
if (!instance.projectName) {
|
|
23816
|
+
return {
|
|
23817
|
+
name: "service containers",
|
|
23818
|
+
status: "skip",
|
|
23819
|
+
message: "This instance has no Compose project name, so its containers cannot be identified."
|
|
23820
|
+
};
|
|
23821
|
+
}
|
|
23822
|
+
const listed = await captureRuntime(
|
|
23823
|
+
runtime.command,
|
|
23824
|
+
[
|
|
23825
|
+
"ps",
|
|
23826
|
+
"--all",
|
|
23827
|
+
"--filter",
|
|
23828
|
+
`label=com.docker.compose.project=${instance.projectName}`,
|
|
23829
|
+
"--format",
|
|
23830
|
+
"json"
|
|
23831
|
+
],
|
|
23832
|
+
context
|
|
23833
|
+
);
|
|
23834
|
+
if (listed.code !== 0) {
|
|
23835
|
+
return {
|
|
23836
|
+
name: "service containers",
|
|
23837
|
+
status: "warn",
|
|
23838
|
+
message: `Containers could not be listed: ${firstLine(listed.stderr) || `exit code ${listed.code}`}`
|
|
23839
|
+
};
|
|
23840
|
+
}
|
|
23841
|
+
try {
|
|
23842
|
+
return summarizeContainerStates(
|
|
23843
|
+
parseContainerStates(listed.stdout),
|
|
23844
|
+
instance.projectName
|
|
23845
|
+
);
|
|
23846
|
+
} catch {
|
|
23847
|
+
return {
|
|
23848
|
+
name: "service containers",
|
|
23849
|
+
status: "warn",
|
|
23850
|
+
message: `The container list returned by ${runtime.name} could not be read.`
|
|
23851
|
+
};
|
|
23852
|
+
}
|
|
23853
|
+
}
|
|
23854
|
+
function captureRuntime(command, args, context) {
|
|
23855
|
+
return new Promise((resolveProcess) => {
|
|
23856
|
+
const child = spawn(command, args, {
|
|
22059
23857
|
env: context.env,
|
|
22060
|
-
stdio: "
|
|
23858
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
22061
23859
|
});
|
|
22062
|
-
|
|
22063
|
-
|
|
22064
|
-
|
|
22065
|
-
|
|
23860
|
+
let stdout = "";
|
|
23861
|
+
let stderr = "";
|
|
23862
|
+
child.stdout?.on("data", (chunk) => {
|
|
23863
|
+
stdout += String(chunk);
|
|
22066
23864
|
});
|
|
22067
|
-
child.on("
|
|
23865
|
+
child.stderr?.on("data", (chunk) => {
|
|
23866
|
+
stderr += String(chunk);
|
|
23867
|
+
});
|
|
23868
|
+
child.on(
|
|
23869
|
+
"error",
|
|
23870
|
+
(error) => resolveProcess({ code: 1, stdout, stderr: error.message })
|
|
23871
|
+
);
|
|
23872
|
+
child.on(
|
|
23873
|
+
"close",
|
|
23874
|
+
(code) => resolveProcess({ code: code ?? 1, stdout, stderr })
|
|
23875
|
+
);
|
|
22068
23876
|
});
|
|
22069
23877
|
}
|
|
22070
23878
|
async function resolveComposeRuntime(env) {
|
|
@@ -22164,7 +23972,7 @@ async function removeIfExists(path2) {
|
|
|
22164
23972
|
}
|
|
22165
23973
|
|
|
22166
23974
|
// src/services/project-directory.ts
|
|
22167
|
-
import { resolve as
|
|
23975
|
+
import { resolve as resolve7 } from "node:path";
|
|
22168
23976
|
function resolveProjectDirectory(parsed, context, promptedDirectory) {
|
|
22169
23977
|
const positionalDirectory = parsed.subject;
|
|
22170
23978
|
const flagDirectory = readStringFlag(parsed.flags, "directory");
|
|
@@ -22176,7 +23984,7 @@ function resolveProjectDirectory(parsed, context, promptedDirectory) {
|
|
|
22176
23984
|
"Specify the project directory either positionally or with --directory."
|
|
22177
23985
|
);
|
|
22178
23986
|
}
|
|
22179
|
-
return
|
|
23987
|
+
return resolve7(
|
|
22180
23988
|
context.cwd,
|
|
22181
23989
|
positionalDirectory ?? flagDirectory ?? promptedDirectory ?? "."
|
|
22182
23990
|
);
|
|
@@ -22197,211 +24005,704 @@ async function runDemo(configPath, config, parsed, context) {
|
|
|
22197
24005
|
} finally {
|
|
22198
24006
|
prompter.close();
|
|
22199
24007
|
}
|
|
22200
|
-
const force = parsed.flags.force === true;
|
|
22201
|
-
const reset = parsed.flags.reset === true;
|
|
22202
|
-
const imageTagOverride = readStringFlag(parsed.flags, "image-tag") ?? context.env.EUDIPLO_IMAGE_TAG;
|
|
22203
|
-
if (reset && !force) {
|
|
22204
|
-
throw new Error(
|
|
22205
|
-
"demo --reset requires --force to remove managed demo data."
|
|
22206
|
-
);
|
|
24008
|
+
const force = parsed.flags.force === true;
|
|
24009
|
+
const reset = parsed.flags.reset === true;
|
|
24010
|
+
const imageTagOverride = readStringFlag(parsed.flags, "image-tag") ?? context.env.EUDIPLO_IMAGE_TAG;
|
|
24011
|
+
if (reset && !force) {
|
|
24012
|
+
throw new Error(
|
|
24013
|
+
"demo --reset requires --force to remove managed demo data."
|
|
24014
|
+
);
|
|
24015
|
+
}
|
|
24016
|
+
if (reset && await demoProjectExists(projectDirectory)) {
|
|
24017
|
+
context.stdout.write(
|
|
24018
|
+
"Resetting demo deployment and removing managed demo volumes...\n"
|
|
24019
|
+
);
|
|
24020
|
+
await drivers.compose.down?.({
|
|
24021
|
+
instanceName: "local",
|
|
24022
|
+
instance: {
|
|
24023
|
+
target: "compose",
|
|
24024
|
+
url: "http://localhost:3000",
|
|
24025
|
+
clientUrl: "http://localhost:4200",
|
|
24026
|
+
composeFiles: ["eudiplo.demo.compose.yaml"],
|
|
24027
|
+
envFile: ".eudiplo.demo.env",
|
|
24028
|
+
projectName: "eudiplo-demo",
|
|
24029
|
+
projectDirectory
|
|
24030
|
+
},
|
|
24031
|
+
args: ["--volumes", "--remove-orphans"],
|
|
24032
|
+
flags: {},
|
|
24033
|
+
context
|
|
24034
|
+
});
|
|
24035
|
+
}
|
|
24036
|
+
const instance = await ensureComposeProject(projectDirectory, {
|
|
24037
|
+
mode: "demo",
|
|
24038
|
+
force,
|
|
24039
|
+
reset,
|
|
24040
|
+
imageTagOverride
|
|
24041
|
+
});
|
|
24042
|
+
const nextConfig = upsertInstance(config, "local", instance);
|
|
24043
|
+
await saveConfig(configPath, nextConfig);
|
|
24044
|
+
context.stdout.write("Starting EUDIPLO demo with the Compose runtime...\n");
|
|
24045
|
+
const exitCode2 = await drivers.compose.up?.({
|
|
24046
|
+
instanceName: "local",
|
|
24047
|
+
instance,
|
|
24048
|
+
args: [],
|
|
24049
|
+
flags: {},
|
|
24050
|
+
context
|
|
24051
|
+
}) ?? 1;
|
|
24052
|
+
if (exitCode2 === 0) {
|
|
24053
|
+
context.stdout.write("\nDemo mode - not for production.\n");
|
|
24054
|
+
context.stdout.write("API URL: http://localhost:3000\n");
|
|
24055
|
+
context.stdout.write("Client URL: http://localhost:4200\n");
|
|
24056
|
+
context.stdout.write("Demo credentials:\n");
|
|
24057
|
+
context.stdout.write(" Client ID: root\n");
|
|
24058
|
+
context.stdout.write(" Client Secret: root\n");
|
|
24059
|
+
context.stdout.write(
|
|
24060
|
+
`Editable demo config: ${projectDirectory}/config/demo
|
|
24061
|
+
`
|
|
24062
|
+
);
|
|
24063
|
+
}
|
|
24064
|
+
return exitCode2;
|
|
24065
|
+
}
|
|
24066
|
+
|
|
24067
|
+
// src/commands/demo/index.ts
|
|
24068
|
+
function createDemoCommand(context, setExitCode) {
|
|
24069
|
+
return new Command("demo").description("Start the minimal local demo").argument("[directory]", "project directory").option("--directory <path>", "set the project directory").option("--reset", "recreate managed demo data and configuration").option("--force", "allow replacement of managed demo files").option("--yes", "accept the default directory without prompting").option("--no-interactive", "disable the directory prompt").option(
|
|
24070
|
+
"--image-tag <tag>",
|
|
24071
|
+
"override the backend and client image tag"
|
|
24072
|
+
).action(async (directory, options) => {
|
|
24073
|
+
const { configPath, config } = await loadCliState(context);
|
|
24074
|
+
setExitCode(
|
|
24075
|
+
await runDemo(
|
|
24076
|
+
configPath,
|
|
24077
|
+
config,
|
|
24078
|
+
parsedArgs("demo", directory, [], options),
|
|
24079
|
+
context
|
|
24080
|
+
)
|
|
24081
|
+
);
|
|
24082
|
+
});
|
|
24083
|
+
}
|
|
24084
|
+
|
|
24085
|
+
// src/commands/deployment/action.ts
|
|
24086
|
+
async function runDriverCommand(config, command, parsed, context) {
|
|
24087
|
+
const [instanceName, instance] = resolveInstance(config, parsed);
|
|
24088
|
+
const driver = drivers[instance.target];
|
|
24089
|
+
const handler = driver[command];
|
|
24090
|
+
if (!handler) {
|
|
24091
|
+
throw new Error(unsupportedCommand(command, instance.target));
|
|
24092
|
+
}
|
|
24093
|
+
if (instance.target === "compose") {
|
|
24094
|
+
const clientMode = instance.clientUrl ? "client enabled" : "client disabled";
|
|
24095
|
+
context.stdout.write(`${command} ${instanceName} (${clientMode})
|
|
24096
|
+
`);
|
|
24097
|
+
}
|
|
24098
|
+
return handler({
|
|
24099
|
+
instanceName,
|
|
24100
|
+
instance,
|
|
24101
|
+
args: parsed.positionals,
|
|
24102
|
+
flags: parsed.flags,
|
|
24103
|
+
context
|
|
24104
|
+
});
|
|
24105
|
+
}
|
|
24106
|
+
|
|
24107
|
+
// src/commands/deployment/index.ts
|
|
24108
|
+
var descriptions = {
|
|
24109
|
+
up: "Start the selected Compose deployment",
|
|
24110
|
+
down: "Stop the selected Compose deployment",
|
|
24111
|
+
logs: "Stream logs for the selected deployment",
|
|
24112
|
+
ps: "List the running containers or pods for the selected deployment",
|
|
24113
|
+
restart: "Restart the workloads of the selected deployment",
|
|
24114
|
+
pull: "Download the configured images without changing configuration"
|
|
24115
|
+
};
|
|
24116
|
+
var serviceScoped = /* @__PURE__ */ new Set([
|
|
24117
|
+
"logs",
|
|
24118
|
+
"restart",
|
|
24119
|
+
"pull"
|
|
24120
|
+
]);
|
|
24121
|
+
function createDeploymentCommands(context, setExitCode) {
|
|
24122
|
+
return ["up", "down", "logs", "ps", "restart", "pull"].map(
|
|
24123
|
+
(name) => {
|
|
24124
|
+
const command = new Command(name).description(descriptions[name]).argument(
|
|
24125
|
+
"[args...]",
|
|
24126
|
+
"additional arguments passed to the deployment runtime"
|
|
24127
|
+
).option("--instance <name>", "select a configured instance");
|
|
24128
|
+
if (serviceScoped.has(name)) {
|
|
24129
|
+
command.option(
|
|
24130
|
+
"--service <name>",
|
|
24131
|
+
"select a single service (Compose) or configured workload (Kubernetes)"
|
|
24132
|
+
);
|
|
24133
|
+
}
|
|
24134
|
+
if (name === "logs") {
|
|
24135
|
+
command.option("--follow", "stream new output as it arrives").option("--tail <lines>", "number of recent lines to show").option(
|
|
24136
|
+
"--since <time>",
|
|
24137
|
+
"show logs since a duration (10m, 2h) or timestamp (Compose instances)"
|
|
24138
|
+
);
|
|
24139
|
+
}
|
|
24140
|
+
if (name === "restart") {
|
|
24141
|
+
command.option(
|
|
24142
|
+
"--no-wait",
|
|
24143
|
+
"return without waiting for the rollout to finish (Kubernetes instances)"
|
|
24144
|
+
);
|
|
24145
|
+
}
|
|
24146
|
+
return command.action(async (args, options) => {
|
|
24147
|
+
const { config } = await loadCliState(context);
|
|
24148
|
+
setExitCode(
|
|
24149
|
+
await runDriverCommand(
|
|
24150
|
+
config,
|
|
24151
|
+
name,
|
|
24152
|
+
parsedArgs(name, void 0, args, options),
|
|
24153
|
+
context
|
|
24154
|
+
)
|
|
24155
|
+
);
|
|
24156
|
+
});
|
|
24157
|
+
}
|
|
24158
|
+
);
|
|
24159
|
+
}
|
|
24160
|
+
|
|
24161
|
+
// package.json
|
|
24162
|
+
var package_default = {
|
|
24163
|
+
name: "@eudiplo/cli",
|
|
24164
|
+
version: "8.1.0-main.21c667e",
|
|
24165
|
+
description: "Deployment-aware command line tools for EUDIPLO",
|
|
24166
|
+
private: false,
|
|
24167
|
+
type: "module",
|
|
24168
|
+
bin: {
|
|
24169
|
+
eudiplo: "dist/index.js"
|
|
24170
|
+
},
|
|
24171
|
+
files: [
|
|
24172
|
+
"dist",
|
|
24173
|
+
"templates",
|
|
24174
|
+
"README.md"
|
|
24175
|
+
],
|
|
24176
|
+
scripts: {
|
|
24177
|
+
"assets:sync": "node scripts/sync-cli-assets.cjs",
|
|
24178
|
+
"assets:check": "node scripts/check-cli-assets.cjs",
|
|
24179
|
+
build: "pnpm assets:sync && pnpm assets:check && tsc -p tsconfig.json",
|
|
24180
|
+
"build:publish": "pnpm build && esbuild src/index.ts --bundle --platform=node --format=esm --target=node22.12 --alias:jsonc-parser=jsonc-parser/lib/esm/main.js --outfile=dist/index.js",
|
|
24181
|
+
"build:sea:bundle": "pnpm assets:sync && pnpm assets:check && esbuild src/index.ts --bundle --platform=node --format=esm --target=node22.12 --alias:jsonc-parser=jsonc-parser/lib/esm/main.js --outfile=dist-sea/index.js",
|
|
24182
|
+
"build:sea": "node scripts/build-cli-sea.mjs",
|
|
24183
|
+
dev: "tsx src/index.ts",
|
|
24184
|
+
test: "vitest run --root ../.. --project cli",
|
|
24185
|
+
lint: "biome lint .",
|
|
24186
|
+
format: "biome format --write .",
|
|
24187
|
+
"format:check": "biome format .",
|
|
24188
|
+
prepublishOnly: "pnpm build:publish"
|
|
24189
|
+
},
|
|
24190
|
+
keywords: [
|
|
24191
|
+
"eudiplo",
|
|
24192
|
+
"cli",
|
|
24193
|
+
"eudi",
|
|
24194
|
+
"wallet",
|
|
24195
|
+
"oid4vci",
|
|
24196
|
+
"oid4vp"
|
|
24197
|
+
],
|
|
24198
|
+
repository: {
|
|
24199
|
+
type: "git",
|
|
24200
|
+
url: "git+https://github.com/openwallet-foundation/eudiplo.git",
|
|
24201
|
+
directory: "apps/cli"
|
|
24202
|
+
},
|
|
24203
|
+
homepage: "https://github.com/openwallet-foundation/eudiplo/tree/main/apps/cli#readme",
|
|
24204
|
+
bugs: {
|
|
24205
|
+
url: "https://github.com/openwallet-foundation/eudiplo/issues"
|
|
24206
|
+
},
|
|
24207
|
+
author: "OpenWallet Foundation",
|
|
24208
|
+
license: "Apache-2.0",
|
|
24209
|
+
engines: {
|
|
24210
|
+
node: ">=22.12.0"
|
|
24211
|
+
},
|
|
24212
|
+
publishConfig: {
|
|
24213
|
+
access: "public"
|
|
24214
|
+
},
|
|
24215
|
+
dependencies: {
|
|
24216
|
+
"@eudiplo/config-format": "workspace:*",
|
|
24217
|
+
ajv: "^8.20.0",
|
|
24218
|
+
"ajv-formats": "^3.0.1",
|
|
24219
|
+
commander: "^15.0.0",
|
|
24220
|
+
fflate: "^0.8.3",
|
|
24221
|
+
"jsonc-parser": "^3.3.1"
|
|
24222
|
+
},
|
|
24223
|
+
devDependencies: {
|
|
24224
|
+
"@biomejs/biome": "2.5.14",
|
|
24225
|
+
"@types/node": "^26.4.0",
|
|
24226
|
+
esbuild: "^0.28.2",
|
|
24227
|
+
tsx: "^4.23.13",
|
|
24228
|
+
typescript: "^7.0.2",
|
|
24229
|
+
vitest: "5.0.2"
|
|
24230
|
+
}
|
|
24231
|
+
};
|
|
24232
|
+
|
|
24233
|
+
// src/services/api-auth.ts
|
|
24234
|
+
async function requestAccessToken(baseUrl, context) {
|
|
24235
|
+
const clientId = context.env.EUDIPLO_CLIENT_ID;
|
|
24236
|
+
const clientSecret = context.env.EUDIPLO_CLIENT_SECRET;
|
|
24237
|
+
if (!clientId || !clientSecret) {
|
|
24238
|
+
return {
|
|
24239
|
+
kind: "unavailable",
|
|
24240
|
+
reason: "EUDIPLO_CLIENT_ID and EUDIPLO_CLIENT_SECRET are not set"
|
|
24241
|
+
};
|
|
24242
|
+
}
|
|
24243
|
+
const tokenUrl = new URL("api/oauth2/token", withTrailingSlash(baseUrl));
|
|
24244
|
+
try {
|
|
24245
|
+
const response = await context.fetch(tokenUrl, {
|
|
24246
|
+
method: "POST",
|
|
24247
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
24248
|
+
body: new URLSearchParams({
|
|
24249
|
+
grant_type: "client_credentials",
|
|
24250
|
+
client_id: clientId,
|
|
24251
|
+
client_secret: clientSecret
|
|
24252
|
+
}).toString()
|
|
24253
|
+
});
|
|
24254
|
+
if (!response.ok) {
|
|
24255
|
+
return {
|
|
24256
|
+
kind: "error",
|
|
24257
|
+
reason: `the token endpoint returned HTTP ${response.status}`
|
|
24258
|
+
};
|
|
24259
|
+
}
|
|
24260
|
+
const payload = await response.json();
|
|
24261
|
+
const accessToken = typeof payload === "object" && payload !== null && "access_token" in payload ? String(payload.access_token) : "";
|
|
24262
|
+
if (!accessToken) {
|
|
24263
|
+
return {
|
|
24264
|
+
kind: "error",
|
|
24265
|
+
reason: "the token response did not contain an access token"
|
|
24266
|
+
};
|
|
24267
|
+
}
|
|
24268
|
+
return { kind: "token", accessToken };
|
|
24269
|
+
} catch (error) {
|
|
24270
|
+
return {
|
|
24271
|
+
kind: "error",
|
|
24272
|
+
reason: `the token endpoint could not be reached: ${error instanceof Error ? error.message : String(error)}`
|
|
24273
|
+
};
|
|
24274
|
+
}
|
|
24275
|
+
}
|
|
24276
|
+
async function fetchAuthenticated(url, accessToken, context) {
|
|
24277
|
+
try {
|
|
24278
|
+
const response = await context.fetch(url, {
|
|
24279
|
+
method: "GET",
|
|
24280
|
+
headers: { authorization: `Bearer ${accessToken}` }
|
|
24281
|
+
});
|
|
24282
|
+
if (!response.ok) {
|
|
24283
|
+
return {
|
|
24284
|
+
ok: false,
|
|
24285
|
+
status: response.status,
|
|
24286
|
+
reason: `${url.pathname} returned HTTP ${response.status}`
|
|
24287
|
+
};
|
|
24288
|
+
}
|
|
24289
|
+
return { ok: true, body: await response.json() };
|
|
24290
|
+
} catch (error) {
|
|
24291
|
+
return {
|
|
24292
|
+
ok: false,
|
|
24293
|
+
reason: `${url.pathname} could not be reached: ${error instanceof Error ? error.message : String(error)}`
|
|
24294
|
+
};
|
|
24295
|
+
}
|
|
24296
|
+
}
|
|
24297
|
+
function withTrailingSlash(url) {
|
|
24298
|
+
return url.href.endsWith("/") ? url : new URL(`${url.href}/`);
|
|
24299
|
+
}
|
|
24300
|
+
|
|
24301
|
+
// src/services/backend-checks.ts
|
|
24302
|
+
function compareVersions(cliVersion, backendVersion) {
|
|
24303
|
+
const name = "version compatibility";
|
|
24304
|
+
const cli = parse4(cliVersion);
|
|
24305
|
+
const backend = parse4(backendVersion);
|
|
24306
|
+
if (!cli || !backend) {
|
|
24307
|
+
return {
|
|
24308
|
+
name,
|
|
24309
|
+
status: "warn",
|
|
24310
|
+
message: `CLI ${cliVersion} and backend ${backendVersion} cannot be compared as versions.`
|
|
24311
|
+
};
|
|
24312
|
+
}
|
|
24313
|
+
if (cli.major !== backend.major) {
|
|
24314
|
+
return {
|
|
24315
|
+
name,
|
|
24316
|
+
status: "fail",
|
|
24317
|
+
message: `CLI ${cliVersion} and backend ${backendVersion} are different major versions. Install a matching CLI with npm install -g @eudiplo/cli@${backend.major}.`
|
|
24318
|
+
};
|
|
24319
|
+
}
|
|
24320
|
+
if (cli.minor !== backend.minor) {
|
|
24321
|
+
return {
|
|
24322
|
+
name,
|
|
24323
|
+
status: "warn",
|
|
24324
|
+
message: `CLI ${cliVersion} and backend ${backendVersion} differ in minor version; some commands may not be supported.`
|
|
24325
|
+
};
|
|
24326
|
+
}
|
|
24327
|
+
return {
|
|
24328
|
+
name,
|
|
24329
|
+
status: "pass",
|
|
24330
|
+
message: `CLI ${cliVersion} matches backend ${backendVersion}.`
|
|
24331
|
+
};
|
|
24332
|
+
}
|
|
24333
|
+
function readBackendVersion(body) {
|
|
24334
|
+
if (typeof body === "object" && body !== null && "version" in body) {
|
|
24335
|
+
const version = body.version;
|
|
24336
|
+
return typeof version === "string" && version ? version : void 0;
|
|
22207
24337
|
}
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
|
|
22212
|
-
|
|
22213
|
-
instanceName: "local",
|
|
22214
|
-
instance: {
|
|
22215
|
-
target: "compose",
|
|
22216
|
-
url: "http://localhost:3000",
|
|
22217
|
-
clientUrl: "http://localhost:4200",
|
|
22218
|
-
composeFiles: ["eudiplo.demo.compose.yaml"],
|
|
22219
|
-
envFile: ".eudiplo.demo.env",
|
|
22220
|
-
projectName: "eudiplo-demo",
|
|
22221
|
-
projectDirectory
|
|
22222
|
-
},
|
|
22223
|
-
args: ["--volumes", "--remove-orphans"],
|
|
22224
|
-
flags: {},
|
|
22225
|
-
context
|
|
22226
|
-
});
|
|
24338
|
+
return void 0;
|
|
24339
|
+
}
|
|
24340
|
+
function readProviderHealth(body) {
|
|
24341
|
+
if (!Array.isArray(body)) {
|
|
24342
|
+
throw new Error("the provider health response was not a list");
|
|
22227
24343
|
}
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
|
|
22231
|
-
|
|
22232
|
-
|
|
24344
|
+
return body.map((entry) => {
|
|
24345
|
+
const record = entry;
|
|
24346
|
+
return {
|
|
24347
|
+
providerId: String(record.providerId ?? "unknown"),
|
|
24348
|
+
type: String(record.type ?? "unknown"),
|
|
24349
|
+
ok: record.ok === true,
|
|
24350
|
+
latencyMs: typeof record.latencyMs === "number" ? record.latencyMs : void 0,
|
|
24351
|
+
error: typeof record.error === "string" ? record.error : void 0
|
|
24352
|
+
};
|
|
22233
24353
|
});
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
}) ?? 1;
|
|
22244
|
-
if (exitCode2 === 0) {
|
|
22245
|
-
context.stdout.write("\nDemo mode - not for production.\n");
|
|
22246
|
-
context.stdout.write("API URL: http://localhost:3000\n");
|
|
22247
|
-
context.stdout.write("Client URL: http://localhost:4200\n");
|
|
22248
|
-
context.stdout.write("Demo credentials:\n");
|
|
22249
|
-
context.stdout.write(" Client ID: root\n");
|
|
22250
|
-
context.stdout.write(" Client Secret: root\n");
|
|
22251
|
-
context.stdout.write(
|
|
22252
|
-
`Editable demo config: ${projectDirectory}/config/demo
|
|
22253
|
-
`
|
|
22254
|
-
);
|
|
24354
|
+
}
|
|
24355
|
+
function summarizeProviderHealth(providers) {
|
|
24356
|
+
const name = "KMS providers";
|
|
24357
|
+
if (providers.length === 0) {
|
|
24358
|
+
return {
|
|
24359
|
+
name,
|
|
24360
|
+
status: "warn",
|
|
24361
|
+
message: "No KMS providers are registered for this tenant."
|
|
24362
|
+
};
|
|
22255
24363
|
}
|
|
22256
|
-
|
|
24364
|
+
const failing = providers.filter((provider) => !provider.ok);
|
|
24365
|
+
if (failing.length > 0) {
|
|
24366
|
+
return {
|
|
24367
|
+
name,
|
|
24368
|
+
status: "fail",
|
|
24369
|
+
message: `${failing.length}/${providers.length} provider(s) unhealthy: ${failing.map(
|
|
24370
|
+
(provider) => `${provider.providerId} (${provider.type})${provider.error ? ", health check reported an error" : ""}`
|
|
24371
|
+
).join(", ")}`
|
|
24372
|
+
};
|
|
24373
|
+
}
|
|
24374
|
+
return {
|
|
24375
|
+
name,
|
|
24376
|
+
status: "pass",
|
|
24377
|
+
message: `${providers.length} provider(s) healthy: ${providers.map(
|
|
24378
|
+
(provider) => `${provider.providerId} (${provider.type}${provider.latencyMs === void 0 ? "" : `, ${provider.latencyMs}ms`})`
|
|
24379
|
+
).join(", ")}`
|
|
24380
|
+
};
|
|
22257
24381
|
}
|
|
22258
|
-
|
|
22259
|
-
|
|
22260
|
-
|
|
22261
|
-
|
|
22262
|
-
|
|
22263
|
-
|
|
22264
|
-
|
|
22265
|
-
|
|
22266
|
-
|
|
22267
|
-
|
|
22268
|
-
configPath,
|
|
22269
|
-
config,
|
|
22270
|
-
parsedArgs("demo", directory, [], options),
|
|
22271
|
-
context
|
|
22272
|
-
)
|
|
22273
|
-
);
|
|
22274
|
-
});
|
|
24382
|
+
function parse4(version) {
|
|
24383
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
|
|
24384
|
+
if (!match) {
|
|
24385
|
+
return void 0;
|
|
24386
|
+
}
|
|
24387
|
+
return {
|
|
24388
|
+
major: Number(match[1]),
|
|
24389
|
+
minor: Number(match[2]),
|
|
24390
|
+
patch: Number(match[3])
|
|
24391
|
+
};
|
|
22275
24392
|
}
|
|
22276
24393
|
|
|
22277
|
-
// src/
|
|
22278
|
-
|
|
22279
|
-
|
|
22280
|
-
|
|
22281
|
-
|
|
22282
|
-
|
|
22283
|
-
|
|
24394
|
+
// src/services/tls-expiry.ts
|
|
24395
|
+
import { connect } from "node:tls";
|
|
24396
|
+
var defaultExpiryWarningDays = 30;
|
|
24397
|
+
var dayInMs = 24 * 60 * 60 * 1e3;
|
|
24398
|
+
function evaluateCertificate(certificate, now, warningDays = defaultExpiryWarningDays) {
|
|
24399
|
+
const remainingDays = Math.floor(
|
|
24400
|
+
(certificate.validTo.getTime() - now.getTime()) / dayInMs
|
|
24401
|
+
);
|
|
24402
|
+
const expiresOn = certificate.validTo.toISOString().slice(0, 10);
|
|
24403
|
+
if (remainingDays < 0) {
|
|
24404
|
+
return {
|
|
24405
|
+
name: "TLS certificate",
|
|
24406
|
+
status: "fail",
|
|
24407
|
+
message: `The certificate issued by ${certificate.issuer} expired on ${expiresOn}.`
|
|
24408
|
+
};
|
|
22284
24409
|
}
|
|
22285
|
-
if (
|
|
22286
|
-
|
|
22287
|
-
|
|
22288
|
-
|
|
24410
|
+
if (remainingDays <= warningDays) {
|
|
24411
|
+
return {
|
|
24412
|
+
name: "TLS certificate",
|
|
24413
|
+
status: "warn",
|
|
24414
|
+
message: `The certificate expires on ${expiresOn}, in ${remainingDays} day(s). Renew it before then.`
|
|
24415
|
+
};
|
|
22289
24416
|
}
|
|
22290
|
-
return
|
|
22291
|
-
|
|
22292
|
-
|
|
22293
|
-
|
|
22294
|
-
|
|
22295
|
-
context
|
|
22296
|
-
});
|
|
24417
|
+
return {
|
|
24418
|
+
name: "TLS certificate",
|
|
24419
|
+
status: "pass",
|
|
24420
|
+
message: `Valid until ${expiresOn} (${remainingDays} day(s) left), issued by ${certificate.issuer}.`
|
|
24421
|
+
};
|
|
22297
24422
|
}
|
|
22298
|
-
|
|
22299
|
-
|
|
22300
|
-
|
|
22301
|
-
|
|
22302
|
-
|
|
22303
|
-
|
|
22304
|
-
|
|
22305
|
-
|
|
22306
|
-
|
|
22307
|
-
|
|
22308
|
-
|
|
22309
|
-
|
|
22310
|
-
|
|
22311
|
-
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
|
|
22317
|
-
|
|
22318
|
-
command.option(
|
|
22319
|
-
"--service <name>",
|
|
22320
|
-
"select a configured workload (Kubernetes instances)"
|
|
22321
|
-
);
|
|
22322
|
-
}
|
|
22323
|
-
if (name === "logs") {
|
|
22324
|
-
command.option("--follow", "stream new output as it arrives").option("--tail <lines>", "number of recent lines to show");
|
|
22325
|
-
}
|
|
22326
|
-
if (name === "restart") {
|
|
22327
|
-
command.option(
|
|
22328
|
-
"--no-wait",
|
|
22329
|
-
"return without waiting for the rollout to finish"
|
|
22330
|
-
);
|
|
24423
|
+
var readPeerCertificate = (url) => new Promise((resolvePeer, rejectPeer) => {
|
|
24424
|
+
const socket = connect(
|
|
24425
|
+
{
|
|
24426
|
+
host: url.hostname,
|
|
24427
|
+
port: Number(url.port || 443),
|
|
24428
|
+
servername: url.hostname,
|
|
24429
|
+
rejectUnauthorized: true
|
|
24430
|
+
},
|
|
24431
|
+
() => {
|
|
24432
|
+
const certificate = socket.getPeerCertificate();
|
|
24433
|
+
socket.end();
|
|
24434
|
+
if (!certificate || !certificate.valid_to) {
|
|
24435
|
+
rejectPeer(new Error("no peer certificate was presented"));
|
|
24436
|
+
return;
|
|
24437
|
+
}
|
|
24438
|
+
resolvePeer({
|
|
24439
|
+
subject: String(certificate.subject?.CN ?? url.hostname),
|
|
24440
|
+
issuer: String(certificate.issuer?.CN ?? "unknown issuer"),
|
|
24441
|
+
validTo: new Date(certificate.valid_to)
|
|
24442
|
+
});
|
|
22331
24443
|
}
|
|
22332
|
-
|
|
22333
|
-
|
|
22334
|
-
|
|
22335
|
-
|
|
22336
|
-
|
|
22337
|
-
|
|
22338
|
-
|
|
22339
|
-
|
|
22340
|
-
)
|
|
22341
|
-
);
|
|
22342
|
-
});
|
|
24444
|
+
);
|
|
24445
|
+
socket.setTimeout(1e4, () => {
|
|
24446
|
+
socket.destroy();
|
|
24447
|
+
rejectPeer(new Error("the TLS handshake timed out"));
|
|
24448
|
+
});
|
|
24449
|
+
socket.once("error", (error) => {
|
|
24450
|
+
socket.destroy();
|
|
24451
|
+
rejectPeer(error);
|
|
22343
24452
|
});
|
|
24453
|
+
});
|
|
24454
|
+
async function checkTlsCertificate(baseUrl, now, read = readPeerCertificate, warningDays = defaultExpiryWarningDays) {
|
|
24455
|
+
if (baseUrl.protocol !== "https:") {
|
|
24456
|
+
return {
|
|
24457
|
+
name: "TLS certificate",
|
|
24458
|
+
status: "skip",
|
|
24459
|
+
message: `${baseUrl.origin} does not use HTTPS, so there is no certificate to check.`
|
|
24460
|
+
};
|
|
24461
|
+
}
|
|
24462
|
+
try {
|
|
24463
|
+
return evaluateCertificate(await read(baseUrl), now, warningDays);
|
|
24464
|
+
} catch (error) {
|
|
24465
|
+
return {
|
|
24466
|
+
name: "TLS certificate",
|
|
24467
|
+
status: "fail",
|
|
24468
|
+
message: `The certificate of ${baseUrl.origin} could not be verified: ${error instanceof Error ? error.message : String(error)}`
|
|
24469
|
+
};
|
|
24470
|
+
}
|
|
22344
24471
|
}
|
|
22345
24472
|
|
|
22346
24473
|
// src/services/diagnostics.ts
|
|
22347
|
-
|
|
22348
|
-
|
|
22349
|
-
|
|
22350
|
-
|
|
22351
|
-
|
|
22352
|
-
{
|
|
24474
|
+
var publicUrlCheck = {
|
|
24475
|
+
id: "public-url",
|
|
24476
|
+
async run({ instance, baseUrl }) {
|
|
24477
|
+
if (!baseUrl) {
|
|
24478
|
+
return {
|
|
22353
24479
|
name: "public URL",
|
|
22354
24480
|
status: "fail",
|
|
22355
24481
|
message: `${instance.url} is not a valid absolute URL`
|
|
22356
|
-
}
|
|
22357
|
-
|
|
24482
|
+
};
|
|
24483
|
+
}
|
|
24484
|
+
return {
|
|
24485
|
+
name: "public URL",
|
|
24486
|
+
status: baseUrl.protocol === "https:" ? "pass" : "warn",
|
|
24487
|
+
message: baseUrl.protocol === "https:" ? `${baseUrl.href} uses HTTPS` : `${baseUrl.href} does not use HTTPS`
|
|
24488
|
+
};
|
|
22358
24489
|
}
|
|
22359
|
-
|
|
22360
|
-
|
|
22361
|
-
|
|
22362
|
-
|
|
22363
|
-
|
|
22364
|
-
|
|
22365
|
-
|
|
22366
|
-
)
|
|
22367
|
-
|
|
22368
|
-
|
|
22369
|
-
|
|
22370
|
-
|
|
22371
|
-
|
|
24490
|
+
};
|
|
24491
|
+
var apiReachabilityCheck = {
|
|
24492
|
+
id: "api-reachability",
|
|
24493
|
+
run: ({ baseUrl, context }) => requireBaseUrl(
|
|
24494
|
+
"API reachability",
|
|
24495
|
+
baseUrl,
|
|
24496
|
+
(url) => checkEndpoint("API reachability", url, "api/docs", context)
|
|
24497
|
+
)
|
|
24498
|
+
};
|
|
24499
|
+
var healthEndpointCheck = {
|
|
24500
|
+
id: "health-endpoint",
|
|
24501
|
+
run: ({ baseUrl, context }) => requireBaseUrl(
|
|
24502
|
+
"health endpoint",
|
|
24503
|
+
baseUrl,
|
|
24504
|
+
(url) => checkEndpoint("health endpoint", url, "health", context)
|
|
24505
|
+
)
|
|
24506
|
+
};
|
|
24507
|
+
var tlsCertificateCheck = {
|
|
24508
|
+
id: "tls-certificate",
|
|
24509
|
+
run: ({ baseUrl, now, readCertificate }) => requireBaseUrl(
|
|
24510
|
+
"TLS certificate",
|
|
24511
|
+
baseUrl,
|
|
24512
|
+
(url) => checkTlsCertificate(url, now, readCertificate)
|
|
24513
|
+
)
|
|
24514
|
+
};
|
|
24515
|
+
var authenticationCheck = {
|
|
24516
|
+
id: "authentication",
|
|
24517
|
+
async run({ context }) {
|
|
24518
|
+
const configured = Boolean(context.env.EUDIPLO_CLIENT_ID) && Boolean(context.env.EUDIPLO_CLIENT_SECRET);
|
|
24519
|
+
return {
|
|
24520
|
+
name: "authentication configuration",
|
|
24521
|
+
status: configured ? "pass" : "warn",
|
|
24522
|
+
message: configured ? "Client credentials are available in environment variables." : "Set EUDIPLO_CLIENT_ID and EUDIPLO_CLIENT_SECRET when commands need authenticated API access."
|
|
24523
|
+
};
|
|
24524
|
+
}
|
|
24525
|
+
};
|
|
24526
|
+
var clientConnectivityCheck = {
|
|
24527
|
+
id: "client-connectivity",
|
|
24528
|
+
async run({ instance, context }) {
|
|
24529
|
+
if (!instance.clientUrl) {
|
|
24530
|
+
return {
|
|
24531
|
+
name: "client connectivity",
|
|
24532
|
+
status: "skip",
|
|
24533
|
+
message: "No client URL configured for this instance."
|
|
24534
|
+
};
|
|
24535
|
+
}
|
|
22372
24536
|
const clientUrl = parseUrl(instance.clientUrl);
|
|
22373
|
-
|
|
22374
|
-
|
|
22375
|
-
"client connectivity",
|
|
22376
|
-
clientUrl,
|
|
22377
|
-
"/",
|
|
22378
|
-
context
|
|
22379
|
-
) : {
|
|
24537
|
+
if (!clientUrl) {
|
|
24538
|
+
return {
|
|
22380
24539
|
name: "client connectivity",
|
|
22381
24540
|
status: "fail",
|
|
22382
24541
|
message: `${instance.clientUrl} is not a valid absolute URL`
|
|
24542
|
+
};
|
|
24543
|
+
}
|
|
24544
|
+
return checkEndpoint("client connectivity", clientUrl, "", context);
|
|
24545
|
+
}
|
|
24546
|
+
};
|
|
24547
|
+
var versionCompatibilityCheck = {
|
|
24548
|
+
id: "version-compatibility",
|
|
24549
|
+
run: ({ baseUrl, context, accessToken }) => requireBaseUrl("version compatibility", baseUrl, async (url) => {
|
|
24550
|
+
const token = await accessToken();
|
|
24551
|
+
if (token.kind !== "token") {
|
|
24552
|
+
return authUnavailable("version compatibility", token);
|
|
24553
|
+
}
|
|
24554
|
+
const response = await fetchAuthenticated(
|
|
24555
|
+
new URL("api/version", withTrailingSlash(url)),
|
|
24556
|
+
token.accessToken,
|
|
24557
|
+
context
|
|
24558
|
+
);
|
|
24559
|
+
if (!response.ok) {
|
|
24560
|
+
if (response.status === 404) {
|
|
24561
|
+
return {
|
|
24562
|
+
name: "version compatibility",
|
|
24563
|
+
status: "skip",
|
|
24564
|
+
message: "This backend does not expose /api/version, so compatibility cannot be checked."
|
|
24565
|
+
};
|
|
22383
24566
|
}
|
|
24567
|
+
return {
|
|
24568
|
+
name: "version compatibility",
|
|
24569
|
+
status: "fail",
|
|
24570
|
+
message: `The backend version could not be read: ${response.reason}`
|
|
24571
|
+
};
|
|
24572
|
+
}
|
|
24573
|
+
const backendVersion = readBackendVersion(response.body);
|
|
24574
|
+
if (!backendVersion) {
|
|
24575
|
+
return {
|
|
24576
|
+
name: "version compatibility",
|
|
24577
|
+
status: "warn",
|
|
24578
|
+
message: "The backend did not report a version."
|
|
24579
|
+
};
|
|
24580
|
+
}
|
|
24581
|
+
return compareVersions(package_default.version, backendVersion);
|
|
24582
|
+
})
|
|
24583
|
+
};
|
|
24584
|
+
var kmsHealthCheck = {
|
|
24585
|
+
id: "kms-health",
|
|
24586
|
+
run: ({ baseUrl, context, accessToken }) => requireBaseUrl("KMS providers", baseUrl, async (url) => {
|
|
24587
|
+
const token = await accessToken();
|
|
24588
|
+
if (token.kind !== "token") {
|
|
24589
|
+
return authUnavailable("KMS providers", token);
|
|
24590
|
+
}
|
|
24591
|
+
const response = await fetchAuthenticated(
|
|
24592
|
+
new URL(
|
|
24593
|
+
"api/key-chain/providers/health",
|
|
24594
|
+
withTrailingSlash(url)
|
|
24595
|
+
),
|
|
24596
|
+
token.accessToken,
|
|
24597
|
+
context
|
|
22384
24598
|
);
|
|
22385
|
-
|
|
22386
|
-
|
|
22387
|
-
|
|
22388
|
-
|
|
22389
|
-
|
|
22390
|
-
|
|
24599
|
+
if (!response.ok) {
|
|
24600
|
+
if (response.status === 404) {
|
|
24601
|
+
return {
|
|
24602
|
+
name: "KMS providers",
|
|
24603
|
+
status: "skip",
|
|
24604
|
+
message: "This backend does not expose the KMS provider health endpoint."
|
|
24605
|
+
};
|
|
24606
|
+
}
|
|
24607
|
+
return {
|
|
24608
|
+
name: "KMS providers",
|
|
24609
|
+
status: "fail",
|
|
24610
|
+
message: `Provider health could not be read: ${response.reason}`
|
|
24611
|
+
};
|
|
24612
|
+
}
|
|
24613
|
+
try {
|
|
24614
|
+
return summarizeProviderHealth(
|
|
24615
|
+
readProviderHealth(response.body)
|
|
24616
|
+
);
|
|
24617
|
+
} catch {
|
|
24618
|
+
return {
|
|
24619
|
+
name: "KMS providers",
|
|
24620
|
+
status: "warn",
|
|
24621
|
+
message: "The provider health response could not be read."
|
|
24622
|
+
};
|
|
24623
|
+
}
|
|
24624
|
+
})
|
|
24625
|
+
};
|
|
24626
|
+
function authUnavailable(name, token) {
|
|
24627
|
+
return {
|
|
24628
|
+
name,
|
|
24629
|
+
status: token.kind === "unavailable" ? "skip" : "fail",
|
|
24630
|
+
message: token.kind === "unavailable" ? `Needs authentication: ${token.reason}.` : `Could not authenticate: ${token.reason}.`
|
|
24631
|
+
};
|
|
24632
|
+
}
|
|
24633
|
+
var coreChecks = [
|
|
24634
|
+
publicUrlCheck,
|
|
24635
|
+
apiReachabilityCheck,
|
|
24636
|
+
healthEndpointCheck,
|
|
24637
|
+
tlsCertificateCheck,
|
|
24638
|
+
authenticationCheck,
|
|
24639
|
+
versionCompatibilityCheck,
|
|
24640
|
+
kmsHealthCheck,
|
|
24641
|
+
clientConnectivityCheck
|
|
24642
|
+
];
|
|
24643
|
+
async function runDoctor(instance, context, driverDiagnostics, options = {}) {
|
|
24644
|
+
const baseUrl = parseUrl(instance.url);
|
|
24645
|
+
let token;
|
|
24646
|
+
const checkContext = {
|
|
24647
|
+
instance,
|
|
24648
|
+
context,
|
|
24649
|
+
baseUrl,
|
|
24650
|
+
now: options.now ?? /* @__PURE__ */ new Date(),
|
|
24651
|
+
readCertificate: options.readCertificate ?? context.readCertificate ?? readPeerCertificate,
|
|
24652
|
+
accessToken: () => {
|
|
24653
|
+
if (!baseUrl) {
|
|
24654
|
+
return Promise.resolve({
|
|
24655
|
+
kind: "unavailable",
|
|
24656
|
+
reason: "the instance URL could not be parsed"
|
|
24657
|
+
});
|
|
24658
|
+
}
|
|
24659
|
+
token ??= requestAccessToken(baseUrl, context);
|
|
24660
|
+
return token;
|
|
24661
|
+
}
|
|
24662
|
+
};
|
|
24663
|
+
const checks = [];
|
|
24664
|
+
for (const check of coreChecks) {
|
|
24665
|
+
checks.push(await check.run(checkContext));
|
|
22391
24666
|
}
|
|
22392
24667
|
checks.push(...driverDiagnostics);
|
|
22393
24668
|
return checks;
|
|
22394
24669
|
}
|
|
22395
|
-
function
|
|
22396
|
-
return
|
|
24670
|
+
function summarizeChecks(checks) {
|
|
24671
|
+
return {
|
|
24672
|
+
pass: count(checks, "pass"),
|
|
24673
|
+
warn: count(checks, "warn"),
|
|
24674
|
+
fail: count(checks, "fail"),
|
|
24675
|
+
skip: count(checks, "skip")
|
|
24676
|
+
};
|
|
24677
|
+
}
|
|
24678
|
+
function hasFailedChecks(checks, strict = false) {
|
|
24679
|
+
return checks.some(
|
|
24680
|
+
(check) => check.status === "fail" || strict && check.status === "warn"
|
|
24681
|
+
);
|
|
22397
24682
|
}
|
|
22398
24683
|
function formatChecks(checks) {
|
|
22399
24684
|
return checks.map(
|
|
22400
24685
|
(check) => `${formatStatus(check.status)} ${check.name}: ${check.message}`
|
|
22401
24686
|
).join("\n");
|
|
22402
24687
|
}
|
|
24688
|
+
function formatSummary(summary) {
|
|
24689
|
+
return `${summary.pass} passed, ${summary.warn} warning(s), ${summary.fail} failure(s), ${summary.skip} skipped`;
|
|
24690
|
+
}
|
|
24691
|
+
function count(checks, status) {
|
|
24692
|
+
return checks.filter((check) => check.status === status).length;
|
|
24693
|
+
}
|
|
24694
|
+
async function requireBaseUrl(name, baseUrl, run) {
|
|
24695
|
+
if (!baseUrl) {
|
|
24696
|
+
return {
|
|
24697
|
+
name,
|
|
24698
|
+
status: "skip",
|
|
24699
|
+
message: "The instance URL could not be parsed."
|
|
24700
|
+
};
|
|
24701
|
+
}
|
|
24702
|
+
return run(baseUrl);
|
|
24703
|
+
}
|
|
22403
24704
|
async function checkEndpoint(name, baseUrl, path2, context) {
|
|
22404
|
-
const url = new URL(path2, baseUrl);
|
|
24705
|
+
const url = new URL(path2, withTrailingSlash(baseUrl));
|
|
22405
24706
|
try {
|
|
22406
24707
|
const response = await context.fetch(url, { method: "GET" });
|
|
22407
24708
|
if (response.ok) {
|
|
@@ -22424,22 +24725,6 @@ async function checkEndpoint(name, baseUrl, path2, context) {
|
|
|
22424
24725
|
};
|
|
22425
24726
|
}
|
|
22426
24727
|
}
|
|
22427
|
-
function checkAuthentication(context) {
|
|
22428
|
-
const hasClientId = Boolean(context.env.EUDIPLO_CLIENT_ID);
|
|
22429
|
-
const hasClientSecret = Boolean(context.env.EUDIPLO_CLIENT_SECRET);
|
|
22430
|
-
if (hasClientId && hasClientSecret) {
|
|
22431
|
-
return {
|
|
22432
|
-
name: "authentication configuration",
|
|
22433
|
-
status: "pass",
|
|
22434
|
-
message: "Client credentials are available in environment variables."
|
|
22435
|
-
};
|
|
22436
|
-
}
|
|
22437
|
-
return {
|
|
22438
|
-
name: "authentication configuration",
|
|
22439
|
-
status: "warn",
|
|
22440
|
-
message: "Set EUDIPLO_CLIENT_ID and EUDIPLO_CLIENT_SECRET when commands need authenticated API access."
|
|
22441
|
-
};
|
|
22442
|
-
}
|
|
22443
24728
|
function parseUrl(value) {
|
|
22444
24729
|
try {
|
|
22445
24730
|
const url = new URL(value);
|
|
@@ -22455,28 +24740,54 @@ function formatStatus(status) {
|
|
|
22455
24740
|
if (status === "warn") {
|
|
22456
24741
|
return "WARN";
|
|
22457
24742
|
}
|
|
24743
|
+
if (status === "skip") {
|
|
24744
|
+
return "SKIP";
|
|
24745
|
+
}
|
|
22458
24746
|
return "FAIL";
|
|
22459
24747
|
}
|
|
22460
24748
|
|
|
22461
24749
|
// src/commands/doctor/action.ts
|
|
22462
|
-
async function runDoctorCommand(config, parsed, context) {
|
|
22463
|
-
const
|
|
24750
|
+
async function runDoctorCommand(config, parsed, context, options = {}) {
|
|
24751
|
+
const strict = parsed.flags.strict === true;
|
|
24752
|
+
const instances = parsed.flags.all === true ? Object.keys(config.instances) : [resolveInstance(config, parsed)[0]];
|
|
24753
|
+
if (instances.length === 0) {
|
|
24754
|
+
throw new Error("No instances are configured.");
|
|
24755
|
+
}
|
|
24756
|
+
let failed = false;
|
|
24757
|
+
for (const [index, instanceName] of instances.entries()) {
|
|
24758
|
+
const instance = config.instances[instanceName];
|
|
24759
|
+
if (index > 0) {
|
|
24760
|
+
context.stdout.write("\n");
|
|
24761
|
+
}
|
|
24762
|
+
const checks = await runDiagnostics(instance, context, options);
|
|
24763
|
+
context.stdout.write(
|
|
24764
|
+
`Doctor for ${instanceName} (${instance.target})
|
|
24765
|
+
`
|
|
24766
|
+
);
|
|
24767
|
+
context.stdout.write(`${formatChecks(checks)}
|
|
24768
|
+
`);
|
|
24769
|
+
context.stdout.write(`${formatSummary(summarizeChecks(checks))}
|
|
24770
|
+
`);
|
|
24771
|
+
failed = hasFailedChecks(checks, strict) || failed;
|
|
24772
|
+
}
|
|
24773
|
+
return failed ? 1 : 0;
|
|
24774
|
+
}
|
|
24775
|
+
async function runDiagnostics(instance, context, options) {
|
|
22464
24776
|
const driver = drivers[instance.target];
|
|
22465
|
-
|
|
24777
|
+
return runDoctor(
|
|
22466
24778
|
instance,
|
|
22467
24779
|
context,
|
|
22468
|
-
await driver.diagnostics(instance, context)
|
|
24780
|
+
await driver.diagnostics(instance, context),
|
|
24781
|
+
options
|
|
22469
24782
|
);
|
|
22470
|
-
context.stdout.write(`Doctor for ${instanceName} (${instance.target})
|
|
22471
|
-
`);
|
|
22472
|
-
context.stdout.write(`${formatChecks(checks)}
|
|
22473
|
-
`);
|
|
22474
|
-
return hasFailedChecks(checks) ? 1 : 0;
|
|
22475
24783
|
}
|
|
22476
24784
|
|
|
22477
24785
|
// src/commands/doctor/index.ts
|
|
22478
24786
|
function createDoctorCommand(context, setExitCode) {
|
|
22479
|
-
return new Command("doctor").description("Run deployment diagnostics").option("--instance <name>", "select a configured instance").
|
|
24787
|
+
return new Command("doctor").description("Run deployment diagnostics").option("--instance <name>", "select a configured instance").option("--all", "run the checks for every configured instance").option(
|
|
24788
|
+
"--strict",
|
|
24789
|
+
"treat warnings as failures (skipped checks never fail)"
|
|
24790
|
+
).action(async (options) => {
|
|
22480
24791
|
const { config } = await loadCliState(context);
|
|
22481
24792
|
setExitCode(
|
|
22482
24793
|
await runDoctorCommand(
|
|
@@ -22872,7 +25183,9 @@ function runInstanceList(config, context) {
|
|
|
22872
25183
|
function runInstanceShow(config, parsed, context) {
|
|
22873
25184
|
const name = parsed.positionals[0] ?? config.defaultInstance;
|
|
22874
25185
|
if (!name) {
|
|
22875
|
-
throw new Error(
|
|
25186
|
+
throw new Error(
|
|
25187
|
+
"No instance selected. Specify a name or add an instance first."
|
|
25188
|
+
);
|
|
22876
25189
|
}
|
|
22877
25190
|
const instance = config.instances[name];
|
|
22878
25191
|
if (!instance) {
|
|
@@ -23020,6 +25333,146 @@ function createInstanceCommand(context, setExitCode) {
|
|
|
23020
25333
|
return instance;
|
|
23021
25334
|
}
|
|
23022
25335
|
|
|
25336
|
+
// src/services/browser.ts
|
|
25337
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
25338
|
+
import { access as access3 } from "node:fs/promises";
|
|
25339
|
+
import { delimiter as delimiter2, join as join11 } from "node:path";
|
|
25340
|
+
function assertWebUrl(value) {
|
|
25341
|
+
let url;
|
|
25342
|
+
try {
|
|
25343
|
+
url = new URL(value);
|
|
25344
|
+
} catch {
|
|
25345
|
+
throw new Error(`${value} is not a valid absolute URL.`);
|
|
25346
|
+
}
|
|
25347
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
25348
|
+
throw new Error(
|
|
25349
|
+
`Refusing to open ${value}: only http and https URLs are supported.`
|
|
25350
|
+
);
|
|
25351
|
+
}
|
|
25352
|
+
return url;
|
|
25353
|
+
}
|
|
25354
|
+
async function chooseLauncher(url, { platform, env, which }) {
|
|
25355
|
+
const browser = env.BROWSER?.trim();
|
|
25356
|
+
if (browser && !/\s|%s/.test(browser)) {
|
|
25357
|
+
const command = await which(browser);
|
|
25358
|
+
if (command) {
|
|
25359
|
+
return { command, args: [url] };
|
|
25360
|
+
}
|
|
25361
|
+
}
|
|
25362
|
+
if (platform === "darwin") {
|
|
25363
|
+
return { command: "open", args: [url] };
|
|
25364
|
+
}
|
|
25365
|
+
if (platform === "win32") {
|
|
25366
|
+
return {
|
|
25367
|
+
command: "rundll32.exe",
|
|
25368
|
+
args: ["url.dll,FileProtocolHandler", url]
|
|
25369
|
+
};
|
|
25370
|
+
}
|
|
25371
|
+
if (env.WSL_DISTRO_NAME) {
|
|
25372
|
+
const wslview = await which("wslview");
|
|
25373
|
+
if (wslview) {
|
|
25374
|
+
return { command: wslview, args: [url] };
|
|
25375
|
+
}
|
|
25376
|
+
}
|
|
25377
|
+
if (env.DISPLAY || env.WAYLAND_DISPLAY) {
|
|
25378
|
+
const xdgOpen = await which("xdg-open");
|
|
25379
|
+
if (xdgOpen) {
|
|
25380
|
+
return { command: xdgOpen, args: [url] };
|
|
25381
|
+
}
|
|
25382
|
+
}
|
|
25383
|
+
return void 0;
|
|
25384
|
+
}
|
|
25385
|
+
function launchBrowser(launcher) {
|
|
25386
|
+
return new Promise((resolveLaunch) => {
|
|
25387
|
+
const child = spawn2(launcher.command, launcher.args, {
|
|
25388
|
+
detached: true,
|
|
25389
|
+
stdio: "ignore"
|
|
25390
|
+
});
|
|
25391
|
+
child.once("error", () => resolveLaunch(false));
|
|
25392
|
+
child.once("spawn", () => {
|
|
25393
|
+
child.unref();
|
|
25394
|
+
resolveLaunch(true);
|
|
25395
|
+
});
|
|
25396
|
+
});
|
|
25397
|
+
}
|
|
25398
|
+
function pathLookup(env, platform = process.platform) {
|
|
25399
|
+
const extensions = platform === "win32" ? ["", ...(env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";")] : [""];
|
|
25400
|
+
return async (name) => {
|
|
25401
|
+
const entries = (env.PATH ?? "").split(delimiter2).filter(Boolean);
|
|
25402
|
+
for (const entry of entries) {
|
|
25403
|
+
for (const extension of extensions) {
|
|
25404
|
+
const candidate = join11(entry, `${name}${extension}`);
|
|
25405
|
+
try {
|
|
25406
|
+
await access3(candidate);
|
|
25407
|
+
return candidate;
|
|
25408
|
+
} catch {
|
|
25409
|
+
}
|
|
25410
|
+
}
|
|
25411
|
+
}
|
|
25412
|
+
return void 0;
|
|
25413
|
+
};
|
|
25414
|
+
}
|
|
25415
|
+
|
|
25416
|
+
// src/commands/open/action.ts
|
|
25417
|
+
var defaultDependencies = {
|
|
25418
|
+
platform: process.platform,
|
|
25419
|
+
launch: launchBrowser
|
|
25420
|
+
};
|
|
25421
|
+
function resolveOpenTarget(instanceName, instance, docs) {
|
|
25422
|
+
if (docs) {
|
|
25423
|
+
const base = instance.url.endsWith("/") ? instance.url : `${instance.url}/`;
|
|
25424
|
+
return assertWebUrl(new URL("api/docs", base).href).href;
|
|
25425
|
+
}
|
|
25426
|
+
if (!instance.clientUrl) {
|
|
25427
|
+
throw new Error(
|
|
25428
|
+
`Instance ${instanceName} has no web client URL. Use --docs to open the API documentation instead.`
|
|
25429
|
+
);
|
|
25430
|
+
}
|
|
25431
|
+
return assertWebUrl(instance.clientUrl).href;
|
|
25432
|
+
}
|
|
25433
|
+
async function runOpenCommand(config, parsed, context, dependencies = defaultDependencies) {
|
|
25434
|
+
const [instanceName, instance] = resolveInstance(config, parsed);
|
|
25435
|
+
const url = resolveOpenTarget(
|
|
25436
|
+
instanceName,
|
|
25437
|
+
instance,
|
|
25438
|
+
parsed.flags.docs === true
|
|
25439
|
+
);
|
|
25440
|
+
if (parsed.flags.print === true || context.interactive !== true) {
|
|
25441
|
+
context.stdout.write(`${url}
|
|
25442
|
+
`);
|
|
25443
|
+
return 0;
|
|
25444
|
+
}
|
|
25445
|
+
const launcher = await chooseLauncher(url, {
|
|
25446
|
+
platform: dependencies.platform,
|
|
25447
|
+
env: context.env,
|
|
25448
|
+
which: pathLookup(context.env, dependencies.platform)
|
|
25449
|
+
});
|
|
25450
|
+
if (!launcher || !await dependencies.launch(launcher)) {
|
|
25451
|
+
context.stdout.write(`No browser is available here. Open: ${url}
|
|
25452
|
+
`);
|
|
25453
|
+
return 0;
|
|
25454
|
+
}
|
|
25455
|
+
context.stdout.write(`Opening ${url}
|
|
25456
|
+
`);
|
|
25457
|
+
return 0;
|
|
25458
|
+
}
|
|
25459
|
+
|
|
25460
|
+
// src/commands/open/index.ts
|
|
25461
|
+
function createOpenCommand(context, setExitCode) {
|
|
25462
|
+
return new Command("open").description(
|
|
25463
|
+
"Open the web client of the selected instance in a browser"
|
|
25464
|
+
).option("--instance <name>", "select a configured instance").option("--docs", "open the management API documentation instead").option("--print", "print the URL instead of opening a browser").action(async (options) => {
|
|
25465
|
+
const { config } = await loadCliState(context);
|
|
25466
|
+
setExitCode(
|
|
25467
|
+
await runOpenCommand(
|
|
25468
|
+
config,
|
|
25469
|
+
parsedArgs("open", void 0, [], options),
|
|
25470
|
+
context
|
|
25471
|
+
)
|
|
25472
|
+
);
|
|
25473
|
+
});
|
|
25474
|
+
}
|
|
25475
|
+
|
|
23023
25476
|
// src/commands/status/action.ts
|
|
23024
25477
|
async function runStatusCommand(config, parsed, context) {
|
|
23025
25478
|
const [instanceName, instance] = resolveInstance(config, parsed);
|
|
@@ -23052,80 +25505,224 @@ function createStatusCommand(context, setExitCode) {
|
|
|
23052
25505
|
});
|
|
23053
25506
|
}
|
|
23054
25507
|
|
|
23055
|
-
// src/commands/
|
|
23056
|
-
import {
|
|
25508
|
+
// src/commands/upgrade/action.ts
|
|
25509
|
+
import { readFile as readFile9, writeFile as writeFile6 } from "node:fs/promises";
|
|
25510
|
+
import { resolve as resolve8 } from "node:path";
|
|
23057
25511
|
|
|
23058
|
-
//
|
|
23059
|
-
var
|
|
23060
|
-
|
|
23061
|
-
|
|
23062
|
-
description: "Deployment-aware command line tools for EUDIPLO",
|
|
23063
|
-
private: false,
|
|
23064
|
-
type: "module",
|
|
23065
|
-
bin: {
|
|
23066
|
-
eudiplo: "dist/index.js"
|
|
23067
|
-
},
|
|
23068
|
-
files: [
|
|
23069
|
-
"dist",
|
|
23070
|
-
"templates",
|
|
23071
|
-
"README.md"
|
|
23072
|
-
],
|
|
23073
|
-
scripts: {
|
|
23074
|
-
"assets:sync": "node scripts/sync-cli-assets.cjs",
|
|
23075
|
-
"assets:check": "node scripts/check-cli-assets.cjs",
|
|
23076
|
-
build: "pnpm assets:sync && pnpm assets:check && tsc -p tsconfig.json",
|
|
23077
|
-
"build:publish": "pnpm build && esbuild src/index.ts --bundle --platform=node --format=esm --target=node22.12 --alias:jsonc-parser=jsonc-parser/lib/esm/main.js --outfile=dist/index.js",
|
|
23078
|
-
"build:sea:bundle": "pnpm assets:sync && pnpm assets:check && esbuild src/index.ts --bundle --platform=node --format=esm --target=node22.12 --alias:jsonc-parser=jsonc-parser/lib/esm/main.js --outfile=dist-sea/index.js",
|
|
23079
|
-
"build:sea": "node scripts/build-cli-sea.mjs",
|
|
23080
|
-
dev: "tsx src/index.ts",
|
|
23081
|
-
test: "vitest run --project cli",
|
|
23082
|
-
lint: "biome lint .",
|
|
23083
|
-
prepublishOnly: "pnpm build:publish"
|
|
23084
|
-
},
|
|
23085
|
-
keywords: [
|
|
23086
|
-
"eudiplo",
|
|
23087
|
-
"cli",
|
|
23088
|
-
"eudi",
|
|
23089
|
-
"wallet",
|
|
23090
|
-
"oid4vci",
|
|
23091
|
-
"oid4vp"
|
|
23092
|
-
],
|
|
23093
|
-
repository: {
|
|
23094
|
-
type: "git",
|
|
23095
|
-
url: "git+https://github.com/openwallet-foundation/eudiplo.git",
|
|
23096
|
-
directory: "apps/cli"
|
|
23097
|
-
},
|
|
23098
|
-
homepage: "https://github.com/openwallet-foundation/eudiplo/tree/main/apps/cli#readme",
|
|
23099
|
-
bugs: {
|
|
23100
|
-
url: "https://github.com/openwallet-foundation/eudiplo/issues"
|
|
23101
|
-
},
|
|
23102
|
-
author: "OpenWallet Foundation",
|
|
23103
|
-
license: "Apache-2.0",
|
|
23104
|
-
engines: {
|
|
23105
|
-
node: ">=22.12.0"
|
|
23106
|
-
},
|
|
23107
|
-
publishConfig: {
|
|
23108
|
-
access: "public"
|
|
23109
|
-
},
|
|
23110
|
-
dependencies: {
|
|
23111
|
-
"@eudiplo/config-format": "workspace:*",
|
|
23112
|
-
ajv: "^8.20.0",
|
|
23113
|
-
"ajv-formats": "^3.0.1",
|
|
23114
|
-
commander: "^15.0.0",
|
|
23115
|
-
fflate: "^0.8.3",
|
|
23116
|
-
"jsonc-parser": "^3.3.1"
|
|
23117
|
-
},
|
|
23118
|
-
devDependencies: {
|
|
23119
|
-
"@biomejs/biome": "2.5.13",
|
|
23120
|
-
"@types/node": "^26.4.0",
|
|
23121
|
-
esbuild: "^0.28.2",
|
|
23122
|
-
tsx: "^4.23.13",
|
|
23123
|
-
typescript: "^7.0.2",
|
|
23124
|
-
vitest: "4.1.11"
|
|
23125
|
-
}
|
|
25512
|
+
// src/services/compose-upgrade.ts
|
|
25513
|
+
var managedImages = {
|
|
25514
|
+
EUDIPLO_IMAGE: "ghcr.io/openwallet-foundation/eudiplo",
|
|
25515
|
+
EUDIPLO_CLIENT_IMAGE: "ghcr.io/openwallet-foundation/eudiplo-client"
|
|
23126
25516
|
};
|
|
25517
|
+
var TAG = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
|
|
25518
|
+
function assertImageTag(tag) {
|
|
25519
|
+
if (!TAG.test(tag)) {
|
|
25520
|
+
throw new Error(
|
|
25521
|
+
`Invalid image tag ${JSON.stringify(tag)}. Tags contain letters, digits, "_", "." and "-", and cannot start with "." or "-".`
|
|
25522
|
+
);
|
|
25523
|
+
}
|
|
25524
|
+
}
|
|
25525
|
+
function planImageUpgrade(content, tag) {
|
|
25526
|
+
assertImageTag(tag);
|
|
25527
|
+
const found = /* @__PURE__ */ new Set();
|
|
25528
|
+
const changes = [];
|
|
25529
|
+
const lines = content.split("\n").map((line) => {
|
|
25530
|
+
const match = /^(EUDIPLO_IMAGE|EUDIPLO_CLIENT_IMAGE)=(.*?)(\r?)$/.exec(
|
|
25531
|
+
line
|
|
25532
|
+
);
|
|
25533
|
+
if (!match) {
|
|
25534
|
+
return line;
|
|
25535
|
+
}
|
|
25536
|
+
const key = match[1];
|
|
25537
|
+
const value = match[2];
|
|
25538
|
+
const lineEnding = match[3];
|
|
25539
|
+
const repository = managedImages[key];
|
|
25540
|
+
if (found.has(key)) {
|
|
25541
|
+
throw new Error(
|
|
25542
|
+
`${key} is defined more than once in the env file.`
|
|
25543
|
+
);
|
|
25544
|
+
}
|
|
25545
|
+
found.add(key);
|
|
25546
|
+
const prefix = `${repository}:`;
|
|
25547
|
+
const current = value.startsWith(prefix) ? value.slice(prefix.length) : void 0;
|
|
25548
|
+
if (current === void 0 || !TAG.test(current)) {
|
|
25549
|
+
throw new Error(
|
|
25550
|
+
`${key} is set to ${value}, which is not a CLI-managed image. Update it yourself, or restore ${prefix}<tag> to let the CLI manage it.`
|
|
25551
|
+
);
|
|
25552
|
+
}
|
|
25553
|
+
if (current !== tag) {
|
|
25554
|
+
changes.push({ key, repository, from: current, to: tag });
|
|
25555
|
+
}
|
|
25556
|
+
return `${key}=${prefix}${tag}${lineEnding}`;
|
|
25557
|
+
});
|
|
25558
|
+
if (!found.has("EUDIPLO_IMAGE")) {
|
|
25559
|
+
throw new Error(
|
|
25560
|
+
"The env file does not define EUDIPLO_IMAGE, so there is no CLI-managed image to upgrade."
|
|
25561
|
+
);
|
|
25562
|
+
}
|
|
25563
|
+
return { changes, content: lines.join("\n") };
|
|
25564
|
+
}
|
|
25565
|
+
function parseVersion(tag) {
|
|
25566
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(tag);
|
|
25567
|
+
if (!match) {
|
|
25568
|
+
return void 0;
|
|
25569
|
+
}
|
|
25570
|
+
return {
|
|
25571
|
+
major: Number(match[1]),
|
|
25572
|
+
minor: Number(match[2]),
|
|
25573
|
+
patch: Number(match[3])
|
|
25574
|
+
};
|
|
25575
|
+
}
|
|
25576
|
+
function compareVersions2(a, b) {
|
|
25577
|
+
return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
|
|
25578
|
+
}
|
|
25579
|
+
var migrationBaseUrl = "https://docs.eudiplo.dev/upgrade";
|
|
25580
|
+
function migrationNotes(from, to) {
|
|
25581
|
+
const fromVersion = parseVersion(from);
|
|
25582
|
+
const toVersion = parseVersion(to);
|
|
25583
|
+
if (!fromVersion || !toVersion) {
|
|
25584
|
+
return [
|
|
25585
|
+
`Cannot compare ${from} and ${to} as versions. Check the release notes and ${migrationBaseUrl} before continuing.`
|
|
25586
|
+
];
|
|
25587
|
+
}
|
|
25588
|
+
const order = compareVersions2(fromVersion, toVersion);
|
|
25589
|
+
if (order > 0) {
|
|
25590
|
+
return [
|
|
25591
|
+
`${to} is older than ${from}. Downgrades are not supported once database migrations have run; restore a backup instead if you need to go back.`
|
|
25592
|
+
];
|
|
25593
|
+
}
|
|
25594
|
+
const notes = [];
|
|
25595
|
+
for (let major = fromVersion.major + 1; major <= toVersion.major; major++) {
|
|
25596
|
+
notes.push(
|
|
25597
|
+
`Major version ${major}: read ${migrationBaseUrl}/${major - 1}.x-to-${major}.0 first.`
|
|
25598
|
+
);
|
|
25599
|
+
}
|
|
25600
|
+
return notes;
|
|
25601
|
+
}
|
|
25602
|
+
|
|
25603
|
+
// src/commands/upgrade/action.ts
|
|
25604
|
+
async function runUpgradeCommand(config, parsed, context) {
|
|
25605
|
+
const [instanceName, instance] = resolveInstance(config, parsed);
|
|
25606
|
+
if (instance.target !== "compose") {
|
|
25607
|
+
throw new Error(unsupportedCommand("upgrade", instance.target));
|
|
25608
|
+
}
|
|
25609
|
+
const options = {
|
|
25610
|
+
instanceName,
|
|
25611
|
+
instance,
|
|
25612
|
+
args: [],
|
|
25613
|
+
flags: parsed.flags,
|
|
25614
|
+
context
|
|
25615
|
+
};
|
|
25616
|
+
assertWritable(options);
|
|
25617
|
+
const tag = readStringFlag(parsed.flags, "image-tag");
|
|
25618
|
+
if (!tag) {
|
|
25619
|
+
throw new Error("upgrade requires --image-tag <tag>.");
|
|
25620
|
+
}
|
|
25621
|
+
if (!instance.envFile) {
|
|
25622
|
+
throw new Error(
|
|
25623
|
+
`Instance ${instanceName} has no env file, so its images are not managed by the CLI.`
|
|
25624
|
+
);
|
|
25625
|
+
}
|
|
25626
|
+
const envPath = resolve8(
|
|
25627
|
+
instance.projectDirectory ?? context.cwd,
|
|
25628
|
+
instance.envFile
|
|
25629
|
+
);
|
|
25630
|
+
const original = await readEnvFile(envPath);
|
|
25631
|
+
const plan = planImageUpgrade(original, tag);
|
|
25632
|
+
if (plan.changes.length === 0) {
|
|
25633
|
+
context.stdout.write(
|
|
25634
|
+
`${instanceName} already uses image tag ${tag}.
|
|
25635
|
+
`
|
|
25636
|
+
);
|
|
25637
|
+
return 0;
|
|
25638
|
+
}
|
|
25639
|
+
context.stdout.write(`Upgrade ${instanceName}:
|
|
25640
|
+
`);
|
|
25641
|
+
for (const change of plan.changes) {
|
|
25642
|
+
context.stdout.write(
|
|
25643
|
+
` ${change.repository}: ${change.from} -> ${change.to}
|
|
25644
|
+
`
|
|
25645
|
+
);
|
|
25646
|
+
}
|
|
25647
|
+
const notes = new Set(
|
|
25648
|
+
plan.changes.flatMap(
|
|
25649
|
+
(change) => migrationNotes(change.from, change.to)
|
|
25650
|
+
)
|
|
25651
|
+
);
|
|
25652
|
+
for (const note of notes) {
|
|
25653
|
+
context.stdout.write(`Note: ${note}
|
|
25654
|
+
`);
|
|
25655
|
+
}
|
|
25656
|
+
if (!await confirmUpgrade(parsed, context)) {
|
|
25657
|
+
context.stdout.write("Upgrade cancelled. Nothing was changed.\n");
|
|
25658
|
+
return 0;
|
|
25659
|
+
}
|
|
25660
|
+
await writeFile6(envPath, plan.content, "utf8");
|
|
25661
|
+
const pulled = await runCompose(buildComposePullArgs(void 0), options);
|
|
25662
|
+
if (pulled !== 0) {
|
|
25663
|
+
await writeFile6(envPath, original, "utf8");
|
|
25664
|
+
context.stderr.write(
|
|
25665
|
+
`Pulling the new images failed. Restored the previous image tags in ${envPath}.
|
|
25666
|
+
`
|
|
25667
|
+
);
|
|
25668
|
+
return pulled;
|
|
25669
|
+
}
|
|
25670
|
+
const recreated = await runCompose(buildComposeUpArgs(), options);
|
|
25671
|
+
if (recreated !== 0) {
|
|
25672
|
+
context.stderr.write(
|
|
25673
|
+
`The new images were pulled and ${envPath} now uses ${tag}, but recreating the services failed. Fix the error above and run eudiplo up.
|
|
25674
|
+
`
|
|
25675
|
+
);
|
|
25676
|
+
return recreated;
|
|
25677
|
+
}
|
|
25678
|
+
context.stdout.write(`Upgraded ${instanceName} to ${tag}.
|
|
25679
|
+
`);
|
|
25680
|
+
return 0;
|
|
25681
|
+
}
|
|
25682
|
+
async function readEnvFile(path2) {
|
|
25683
|
+
try {
|
|
25684
|
+
return await readFile9(path2, "utf8");
|
|
25685
|
+
} catch (error) {
|
|
25686
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
25687
|
+
throw new Error(`Env file not found: ${path2}`);
|
|
25688
|
+
}
|
|
25689
|
+
throw error;
|
|
25690
|
+
}
|
|
25691
|
+
}
|
|
25692
|
+
async function confirmUpgrade(parsed, context) {
|
|
25693
|
+
if (parsed.flags.yes === true) {
|
|
25694
|
+
return true;
|
|
25695
|
+
}
|
|
25696
|
+
if (context.interactive !== true) {
|
|
25697
|
+
throw new Error("upgrade requires --yes in non-interactive mode.");
|
|
25698
|
+
}
|
|
25699
|
+
const prompter = createPrompter(context);
|
|
25700
|
+
try {
|
|
25701
|
+
const answer = (await prompter.ask("Continue? [y/N]: ")).trim().toLowerCase();
|
|
25702
|
+
return answer === "y" || answer === "yes";
|
|
25703
|
+
} finally {
|
|
25704
|
+
prompter.close();
|
|
25705
|
+
}
|
|
25706
|
+
}
|
|
25707
|
+
|
|
25708
|
+
// src/commands/upgrade/index.ts
|
|
25709
|
+
function createUpgradeCommand(context, setExitCode) {
|
|
25710
|
+
return new Command("upgrade").description(
|
|
25711
|
+
"Upgrade the EUDIPLO application images of a Compose deployment"
|
|
25712
|
+
).requiredOption("--image-tag <tag>", "image tag to upgrade to").option("--instance <name>", "select a configured instance").option("--yes", "upgrade without asking for confirmation").action(async (options) => {
|
|
25713
|
+
const { config } = await loadCliState(context);
|
|
25714
|
+
setExitCode(
|
|
25715
|
+
await runUpgradeCommand(
|
|
25716
|
+
config,
|
|
25717
|
+
parsedArgs("upgrade", void 0, [], options),
|
|
25718
|
+
context
|
|
25719
|
+
)
|
|
25720
|
+
);
|
|
25721
|
+
});
|
|
25722
|
+
}
|
|
23127
25723
|
|
|
23128
25724
|
// src/commands/version/action.ts
|
|
25725
|
+
import { isSea as isSea2 } from "node:sea";
|
|
23129
25726
|
function versionText() {
|
|
23130
25727
|
return `${package_default.name} ${package_default.version}`;
|
|
23131
25728
|
}
|
|
@@ -23225,13 +25822,15 @@ function createProgram(context, setExitCode) {
|
|
|
23225
25822
|
"Print the installed CLI version"
|
|
23226
25823
|
).helpCommand(true).addHelpText(
|
|
23227
25824
|
"afterAll",
|
|
23228
|
-
"\nFor more information, see https://docs.eudiplo.dev/
|
|
25825
|
+
"\nFor more information, see https://docs.eudiplo.dev/cookbooks/foundation"
|
|
23229
25826
|
);
|
|
23230
25827
|
program2.addCommand(createDemoCommand(context, setExitCode));
|
|
23231
25828
|
program2.addCommand(createInitCommand(context, setExitCode));
|
|
23232
25829
|
for (const command of createDeploymentCommands(context, setExitCode)) {
|
|
23233
25830
|
program2.addCommand(command);
|
|
23234
25831
|
}
|
|
25832
|
+
program2.addCommand(createUpgradeCommand(context, setExitCode));
|
|
25833
|
+
program2.addCommand(createOpenCommand(context, setExitCode));
|
|
23235
25834
|
program2.addCommand(createInstanceCommand(context, setExitCode));
|
|
23236
25835
|
program2.addCommand(createConfigCommand(context, setExitCode));
|
|
23237
25836
|
program2.addCommand(createDoctorCommand(context, setExitCode));
|