@eudiplo/cli 8.0.2-main.e4bc4d4 → 8.1.0-main.a6a67b3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/doctor/action.d.ts +2 -1
  3. package/dist/commands/doctor/action.d.ts.map +1 -1
  4. package/dist/commands/doctor/action.js +25 -7
  5. package/dist/commands/doctor/action.js.map +1 -1
  6. package/dist/commands/doctor/index.d.ts.map +1 -1
  7. package/dist/commands/doctor/index.js +2 -0
  8. package/dist/commands/doctor/index.js.map +1 -1
  9. package/dist/index.js +770 -169
  10. package/dist/services/api-auth.d.ts +31 -0
  11. package/dist/services/api-auth.d.ts.map +1 -0
  12. package/dist/services/api-auth.js +78 -0
  13. package/dist/services/api-auth.js.map +1 -0
  14. package/dist/services/backend-checks.d.ts +24 -0
  15. package/dist/services/backend-checks.d.ts.map +1 -0
  16. package/dist/services/backend-checks.js +105 -0
  17. package/dist/services/backend-checks.js.map +1 -0
  18. package/dist/services/compose-project.js +6 -6
  19. package/dist/services/compose-project.js.map +1 -1
  20. package/dist/services/container-state.d.ts +14 -0
  21. package/dist/services/container-state.d.ts.map +1 -0
  22. package/dist/services/container-state.js +70 -0
  23. package/dist/services/container-state.js.map +1 -0
  24. package/dist/services/deployment-drivers.d.ts.map +1 -1
  25. package/dist/services/deployment-drivers.js +77 -0
  26. package/dist/services/deployment-drivers.js.map +1 -1
  27. package/dist/services/diagnostics.d.ts +19 -2
  28. package/dist/services/diagnostics.d.ts.map +1 -1
  29. package/dist/services/diagnostics.js +213 -49
  30. package/dist/services/diagnostics.js.map +1 -1
  31. package/dist/services/tls-expiry.d.ts +16 -0
  32. package/dist/services/tls-expiry.d.ts.map +1 -0
  33. package/dist/services/tls-expiry.js +80 -0
  34. package/dist/services/tls-expiry.js.map +1 -0
  35. package/dist/types.d.ts +4 -1
  36. package/dist/types.d.ts.map +1 -1
  37. package/package.json +1 -1
  38. package/templates/docker-compose.yml +34 -27
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 count = 0;
2219
+ let count2 = 0;
2220
2220
  for (const key in schema) {
2221
2221
  if (key === "$ref")
2222
2222
  return Infinity;
2223
- count++;
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) => count += countKeys(sch));
2227
+ (0, util_1.eachItem)(schema[key], (sch) => count2 += countKeys(sch));
2228
2228
  }
2229
- if (count === Infinity)
2229
+ if (count2 === Infinity)
2230
2230
  return Infinity;
2231
2231
  }
2232
- return count;
2232
+ return count2;
2233
2233
  }
2234
2234
  function getFullPath(resolver, id = "", normalize) {
2235
2235
  if (normalize !== false)
@@ -3828,7 +3828,7 @@ var require_fast_uri = __commonJS({
3828
3828
  normalizeString(uri, options);
3829
3829
  } else if (typeof uri === "object") {
3830
3830
  uri = /** @type {T} */
3831
- parse4(serialize(uri, options), options);
3831
+ parse5(serialize(uri, options), options);
3832
3832
  }
3833
3833
  return uri;
3834
3834
  }
@@ -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 = parse4(serialize(base, options), options);
3872
- relative3 = parse4(serialize(relative3, options), options);
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 parse4(uri, opts) {
4164
+ function parse5(uri, opts) {
4165
4165
  return parseWithStatus(uri, opts).parsed;
4166
4166
  }
4167
4167
  function normalizeString(uri, opts) {
@@ -4198,7 +4198,7 @@ var require_fast_uri = __commonJS({
4198
4198
  resolveComponent,
4199
4199
  equal,
4200
4200
  serialize,
4201
- parse: parse4
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 count = gen.let("count", 0);
5703
- validateItems(schValid, () => gen.if(schValid, () => checkLimits(count)));
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(count) {
5717
- gen.code((0, codegen_1._)`${count}++`);
5716
+ function checkLimits(count2) {
5717
+ gen.code((0, codegen_1._)`${count2}++`);
5718
5718
  if (max2 === void 0) {
5719
- gen.if((0, codegen_1._)`${count} >= ${min}`, () => gen.assign(valid, true).break());
5719
+ gen.if((0, codegen_1._)`${count2} >= ${min}`, () => gen.assign(valid, true).break());
5720
5720
  } else {
5721
- gen.if((0, codegen_1._)`${count} > ${max2}`, () => gen.assign(valid, false).break());
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._)`${count} >= ${min}`, () => gen.assign(valid, true));
5725
+ gen.if((0, codegen_1._)`${count2} >= ${min}`, () => gen.assign(valid, true));
5726
5726
  }
5727
5727
  }
5728
5728
  }
@@ -12065,10 +12065,10 @@ import { isAbsolute as isAbsolute2, join as join2, relative, resolve as resolve2
12065
12065
  function createScanner(text, ignoreTrivia = false) {
12066
12066
  const len = text.length;
12067
12067
  let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
12068
- function scanHexDigits(count, exact) {
12068
+ function scanHexDigits(count2, exact) {
12069
12069
  let digits = 0;
12070
12070
  let value2 = 0;
12071
- while (digits < count || !exact) {
12071
+ while (digits < count2 || !exact) {
12072
12072
  let ch = text.charCodeAt(pos);
12073
12073
  if (ch >= 48 && ch <= 57) {
12074
12074
  value2 = value2 * 16 + ch - 48;
@@ -12082,7 +12082,7 @@ function createScanner(text, ignoreTrivia = false) {
12082
12082
  pos++;
12083
12083
  digits++;
12084
12084
  }
12085
- if (digits < count) {
12085
+ if (digits < count2) {
12086
12086
  value2 = -1;
12087
12087
  }
12088
12088
  return value2;
@@ -12707,9 +12707,9 @@ function format(documentText, range, options) {
12707
12707
  }
12708
12708
  return editOperations;
12709
12709
  }
12710
- function repeat(s, count) {
12710
+ function repeat(s, count2) {
12711
12711
  let result = "";
12712
- for (let i2 = 0; i2 < count; i2++) {
12712
+ for (let i2 = 0; i2 < count2; i2++) {
12713
12713
  result += s;
12714
12714
  }
12715
12715
  return result;
@@ -15260,12 +15260,43 @@ function assertConfigBundle(input) {
15260
15260
  }
15261
15261
 
15262
15262
  // ../../packages/eudiplo-config-format/dist/config-values.js
15263
+ var isVariableChar = (char) => char >= "A" && char <= "Z" || char >= "0" && char <= "9" || char === "_";
15264
+ function replacePlaceholders(input, replace) {
15265
+ let output = "";
15266
+ let copied = 0;
15267
+ let start = input.indexOf("${");
15268
+ while (start !== -1) {
15269
+ let cursor = start + 2;
15270
+ while (cursor < input.length && isVariableChar(input[cursor]))
15271
+ cursor++;
15272
+ const variable = input.slice(start + 2, cursor);
15273
+ let end = -1;
15274
+ let fallback;
15275
+ if (variable && input[cursor] === "}")
15276
+ end = cursor;
15277
+ else if (variable && input[cursor] === ":") {
15278
+ end = input.indexOf("}", cursor + 1);
15279
+ if (end === -1)
15280
+ break;
15281
+ fallback = input.slice(cursor + 1, end);
15282
+ }
15283
+ if (end === -1) {
15284
+ start = input.indexOf("${", start + 2);
15285
+ continue;
15286
+ }
15287
+ output += input.slice(copied, start);
15288
+ output += replace(input.slice(start, end + 1), variable, fallback);
15289
+ copied = end + 1;
15290
+ start = input.indexOf("${", copied);
15291
+ }
15292
+ return output + input.slice(copied);
15293
+ }
15263
15294
  function resolveConfigVariables(input, env) {
15264
15295
  const issues = [];
15265
15296
  const seen = /* @__PURE__ */ new WeakMap();
15266
15297
  function visit2(value, path2) {
15267
15298
  if (typeof value === "string")
15268
- return value.replace(/\$\{([A-Z0-9_]+)(?::([^}]*))?\}/g, (match, variable, fallback) => {
15299
+ return replacePlaceholders(value, (match, variable, fallback) => {
15269
15300
  if (env[variable] !== void 0 && env[variable] !== "")
15270
15301
  return env[variable];
15271
15302
  if (fallback !== void 0)
@@ -20592,17 +20623,17 @@ function storageEnv(storage) {
20592
20623
  if (storage === "local") {
20593
20624
  return ["STORAGE_DRIVER=local", "LOCAL_STORAGE_DIR=/app/uploads"];
20594
20625
  }
20595
- const minioPassword = randomSecret();
20626
+ const rustfsPassword = randomSecret();
20596
20627
  return [
20597
20628
  "STORAGE_DRIVER=s3",
20598
- "S3_ENDPOINT=http://minio:9000",
20629
+ "S3_ENDPOINT=http://rustfs:9000",
20599
20630
  "S3_REGION=us-east-1",
20600
- "S3_ACCESS_KEY_ID=minioadmin",
20601
- `S3_SECRET_ACCESS_KEY=${minioPassword}`,
20631
+ "S3_ACCESS_KEY_ID=rustfsadmin",
20632
+ `S3_SECRET_ACCESS_KEY=${rustfsPassword}`,
20602
20633
  "S3_BUCKET=uploads",
20603
20634
  "S3_FORCE_PATH_STYLE=true",
20604
- "MINIO_ROOT_USER=minioadmin",
20605
- `MINIO_ROOT_PASSWORD=${minioPassword}`
20635
+ "RUSTFS_ACCESS_KEY=rustfsadmin",
20636
+ `RUSTFS_SECRET_KEY=${rustfsPassword}`
20606
20637
  ];
20607
20638
  }
20608
20639
  function kmsEnv(kms) {
@@ -20755,10 +20786,10 @@ function formatTextReport(scope, rootPath, results) {
20755
20786
  function formatTenantBlock(result) {
20756
20787
  const lines = [`${result.valid ? "PASS" : "FAIL"} ${result.id}`];
20757
20788
  if (result.valid) {
20758
- for (const [resourceType, count] of Object.entries(
20789
+ for (const [resourceType, count2] of Object.entries(
20759
20790
  result.resourceCounts
20760
20791
  )) {
20761
- lines.push(` ${count} ${pluralize(resourceType, count)}`);
20792
+ lines.push(` ${count2} ${pluralize(resourceType, count2)}`);
20762
20793
  }
20763
20794
  } else {
20764
20795
  lines.push(...formatTenantErrors(result.errors));
@@ -20804,8 +20835,8 @@ function groupErrorsByFile(errors) {
20804
20835
  }
20805
20836
  return Array.from(grouped.entries());
20806
20837
  }
20807
- function pluralize(label, count) {
20808
- return count === 1 ? label : `${label}s`;
20838
+ function pluralize(label, count2) {
20839
+ return count2 === 1 ? label : `${label}s`;
20809
20840
  }
20810
20841
 
20811
20842
  // src/commands/config/validate/validator.ts
@@ -21650,6 +21681,68 @@ function assertKnownService(service, known) {
21650
21681
  }
21651
21682
  }
21652
21683
 
21684
+ // src/services/container-state.ts
21685
+ function parseContainerStates(stdout) {
21686
+ const trimmed = stdout.trim();
21687
+ if (!trimmed) {
21688
+ return [];
21689
+ }
21690
+ const entries = trimmed.startsWith("[") ? asArray(JSON.parse(trimmed)) : trimmed.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("{")).map((line) => JSON.parse(line));
21691
+ return entries.map((entry) => {
21692
+ const record = entry;
21693
+ const names = record.Names;
21694
+ return {
21695
+ name: Array.isArray(names) ? String(names[0] ?? "") : String(names ?? record.Name ?? ""),
21696
+ state: String(record.State ?? ""),
21697
+ status: String(record.Status ?? "")
21698
+ };
21699
+ });
21700
+ }
21701
+ function summarizeContainerStates(containers, projectName) {
21702
+ const name = "service containers";
21703
+ if (containers.length === 0) {
21704
+ return {
21705
+ name,
21706
+ status: "warn",
21707
+ message: `No containers found for project ${projectName}. Start it with eudiplo up.`
21708
+ };
21709
+ }
21710
+ const unhealthy = containers.filter(
21711
+ (container) => container.status.toLowerCase().includes("unhealthy")
21712
+ );
21713
+ if (unhealthy.length > 0) {
21714
+ return {
21715
+ name,
21716
+ status: "fail",
21717
+ message: `Unhealthy: ${describe(unhealthy)}`
21718
+ };
21719
+ }
21720
+ const notRunning = containers.filter(
21721
+ (container) => container.state.toLowerCase() !== "running"
21722
+ );
21723
+ if (notRunning.length > 0) {
21724
+ return {
21725
+ name,
21726
+ status: "warn",
21727
+ message: `${containers.length - notRunning.length}/${containers.length} running. Not running: ${describe(notRunning)}`
21728
+ };
21729
+ }
21730
+ const starting = containers.filter(
21731
+ (container) => container.status.toLowerCase().includes("starting")
21732
+ ).length;
21733
+ return {
21734
+ name,
21735
+ status: "pass",
21736
+ message: `${containers.length} container(s) running${starting > 0 ? `, ${starting} still starting` : ""}.`
21737
+ };
21738
+ }
21739
+ function asArray(value) {
21740
+ return Array.isArray(value) ? value : [];
21741
+ }
21742
+ function describe(containers) {
21743
+ return containers.map((container) => `${container.name} (${container.status})`).join(", ");
21744
+ }
21745
+
21653
21746
  // src/services/deployment-drivers.ts
21654
21747
  var drivers = {
21655
21748
  compose: {
@@ -21663,6 +21756,12 @@ var drivers = {
21663
21756
  status: runtime ? "pass" : "warn",
21664
21757
  message: runtime ? `${runtime.name} found at ${runtime.command}` : "Docker or Podman was not found in a supported install location."
21665
21758
  });
21759
+ if (runtime) {
21760
+ checks.push(await composeProviderCheck(runtime, context));
21761
+ checks.push(
21762
+ await composeServicesCheck(runtime, instance, context)
21763
+ );
21764
+ }
21666
21765
  for (const composeFile of getComposeFiles(instance)) {
21667
21766
  let found = true;
21668
21767
  try {
@@ -22258,6 +22357,89 @@ async function resolveComposeService(options) {
22258
22357
  assertKnownService(service, parseServiceList(listed.stdout));
22259
22358
  return service;
22260
22359
  }
22360
+ async function composeProviderCheck(runtime, context) {
22361
+ const version = await captureRuntime(
22362
+ runtime.command,
22363
+ ["compose", "version"],
22364
+ context
22365
+ );
22366
+ if (version.code !== 0) {
22367
+ return {
22368
+ name: "compose provider",
22369
+ status: "fail",
22370
+ message: `${runtime.name} compose is not usable: ${firstLine(version.stderr) || `exit code ${version.code}`}`
22371
+ };
22372
+ }
22373
+ return {
22374
+ name: "compose provider",
22375
+ status: "pass",
22376
+ message: firstLine(version.stdout) || `${runtime.name} compose works`
22377
+ };
22378
+ }
22379
+ async function composeServicesCheck(runtime, instance, context) {
22380
+ if (!instance.projectName) {
22381
+ return {
22382
+ name: "service containers",
22383
+ status: "skip",
22384
+ message: "This instance has no Compose project name, so its containers cannot be identified."
22385
+ };
22386
+ }
22387
+ const listed = await captureRuntime(
22388
+ runtime.command,
22389
+ [
22390
+ "ps",
22391
+ "--all",
22392
+ "--filter",
22393
+ `label=com.docker.compose.project=${instance.projectName}`,
22394
+ "--format",
22395
+ "json"
22396
+ ],
22397
+ context
22398
+ );
22399
+ if (listed.code !== 0) {
22400
+ return {
22401
+ name: "service containers",
22402
+ status: "warn",
22403
+ message: `Containers could not be listed: ${firstLine(listed.stderr) || `exit code ${listed.code}`}`
22404
+ };
22405
+ }
22406
+ try {
22407
+ return summarizeContainerStates(
22408
+ parseContainerStates(listed.stdout),
22409
+ instance.projectName
22410
+ );
22411
+ } catch {
22412
+ return {
22413
+ name: "service containers",
22414
+ status: "warn",
22415
+ message: `The container list returned by ${runtime.name} could not be read.`
22416
+ };
22417
+ }
22418
+ }
22419
+ function captureRuntime(command, args, context) {
22420
+ return new Promise((resolveProcess) => {
22421
+ const child = spawn(command, args, {
22422
+ env: context.env,
22423
+ stdio: ["ignore", "pipe", "pipe"]
22424
+ });
22425
+ let stdout = "";
22426
+ let stderr = "";
22427
+ child.stdout?.on("data", (chunk) => {
22428
+ stdout += String(chunk);
22429
+ });
22430
+ child.stderr?.on("data", (chunk) => {
22431
+ stderr += String(chunk);
22432
+ });
22433
+ child.on(
22434
+ "error",
22435
+ (error) => resolveProcess({ code: 1, stdout, stderr: error.message })
22436
+ );
22437
+ child.on(
22438
+ "close",
22439
+ (code) => resolveProcess({ code: code ?? 1, stdout, stderr })
22440
+ );
22441
+ });
22442
+ }
22261
22443
  async function resolveComposeRuntime(env) {
22262
22444
  const preferredRuntime = parsePreferredRuntime(
22263
22445
  env.EUDIPLO_CONTAINER_RUNTIME
@@ -22541,63 +22723,544 @@ function createDeploymentCommands(context, setExitCode) {
22541
22723
  );
22542
22724
  }
22543
22725
 
22726
+ // package.json
22727
+ var package_default = {
22728
+ name: "@eudiplo/cli",
22729
+ version: "8.1.0-main.a6a67b3",
22730
+ description: "Deployment-aware command line tools for EUDIPLO",
22731
+ private: false,
22732
+ type: "module",
22733
+ bin: {
22734
+ eudiplo: "dist/index.js"
22735
+ },
22736
+ files: [
22737
+ "dist",
22738
+ "templates",
22739
+ "README.md"
22740
+ ],
22741
+ scripts: {
22742
+ "assets:sync": "node scripts/sync-cli-assets.cjs",
22743
+ "assets:check": "node scripts/check-cli-assets.cjs",
22744
+ build: "pnpm assets:sync && pnpm assets:check && tsc -p tsconfig.json",
22745
+ "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",
22746
+ "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",
22747
+ "build:sea": "node scripts/build-cli-sea.mjs",
22748
+ dev: "tsx src/index.ts",
22749
+ test: "vitest run --project cli",
22750
+ lint: "biome lint .",
22751
+ prepublishOnly: "pnpm build:publish"
22752
+ },
22753
+ keywords: [
22754
+ "eudiplo",
22755
+ "cli",
22756
+ "eudi",
22757
+ "wallet",
22758
+ "oid4vci",
22759
+ "oid4vp"
22760
+ ],
22761
+ repository: {
22762
+ type: "git",
22763
+ url: "git+https://github.com/openwallet-foundation/eudiplo.git",
22764
+ directory: "apps/cli"
22765
+ },
22766
+ homepage: "https://github.com/openwallet-foundation/eudiplo/tree/main/apps/cli#readme",
22767
+ bugs: {
22768
+ url: "https://github.com/openwallet-foundation/eudiplo/issues"
22769
+ },
22770
+ author: "OpenWallet Foundation",
22771
+ license: "Apache-2.0",
22772
+ engines: {
22773
+ node: ">=22.12.0"
22774
+ },
22775
+ publishConfig: {
22776
+ access: "public"
22777
+ },
22778
+ dependencies: {
22779
+ "@eudiplo/config-format": "workspace:*",
22780
+ ajv: "^8.20.0",
22781
+ "ajv-formats": "^3.0.1",
22782
+ commander: "^15.0.0",
22783
+ fflate: "^0.8.3",
22784
+ "jsonc-parser": "^3.3.1"
22785
+ },
22786
+ devDependencies: {
22787
+ "@biomejs/biome": "2.5.14",
22788
+ "@types/node": "^26.4.0",
22789
+ esbuild: "^0.28.2",
22790
+ tsx: "^4.23.13",
22791
+ typescript: "^7.0.2",
22792
+ vitest: "4.1.11"
22793
+ }
22794
+ };
22795
+
22796
+ // src/services/api-auth.ts
22797
+ async function requestAccessToken(baseUrl, context) {
22798
+ const clientId = context.env.EUDIPLO_CLIENT_ID;
22799
+ const clientSecret = context.env.EUDIPLO_CLIENT_SECRET;
22800
+ if (!clientId || !clientSecret) {
22801
+ return {
22802
+ kind: "unavailable",
22803
+ reason: "EUDIPLO_CLIENT_ID and EUDIPLO_CLIENT_SECRET are not set"
22804
+ };
22805
+ }
22806
+ const tokenUrl = new URL("api/oauth2/token", withTrailingSlash(baseUrl));
22807
+ try {
22808
+ const response = await context.fetch(tokenUrl, {
22809
+ method: "POST",
22810
+ headers: { "content-type": "application/x-www-form-urlencoded" },
22811
+ body: new URLSearchParams({
22812
+ grant_type: "client_credentials",
22813
+ client_id: clientId,
22814
+ client_secret: clientSecret
22815
+ }).toString()
22816
+ });
22817
+ if (!response.ok) {
22818
+ return {
22819
+ kind: "error",
22820
+ reason: `the token endpoint returned HTTP ${response.status}`
22821
+ };
22822
+ }
22823
+ const payload = await response.json();
22824
+ const accessToken = typeof payload === "object" && payload !== null && "access_token" in payload ? String(payload.access_token) : "";
22825
+ if (!accessToken) {
22826
+ return {
22827
+ kind: "error",
22828
+ reason: "the token response did not contain an access token"
22829
+ };
22830
+ }
22831
+ return { kind: "token", accessToken };
22832
+ } catch (error) {
22833
+ return {
22834
+ kind: "error",
22835
+ reason: `the token endpoint could not be reached: ${error instanceof Error ? error.message : String(error)}`
22836
+ };
22837
+ }
22838
+ }
22839
+ async function fetchAuthenticated(url, accessToken, context) {
22840
+ try {
22841
+ const response = await context.fetch(url, {
22842
+ method: "GET",
22843
+ headers: { authorization: `Bearer ${accessToken}` }
22844
+ });
22845
+ if (!response.ok) {
22846
+ return {
22847
+ ok: false,
22848
+ status: response.status,
22849
+ reason: `${url.pathname} returned HTTP ${response.status}`
22850
+ };
22851
+ }
22852
+ return { ok: true, body: await response.json() };
22853
+ } catch (error) {
22854
+ return {
22855
+ ok: false,
22856
+ reason: `${url.pathname} could not be reached: ${error instanceof Error ? error.message : String(error)}`
22857
+ };
22858
+ }
22859
+ }
22860
+ function withTrailingSlash(url) {
22861
+ return url.href.endsWith("/") ? url : new URL(`${url.href}/`);
22862
+ }
22863
+
22864
+ // src/services/backend-checks.ts
22865
+ function compareVersions(cliVersion, backendVersion) {
22866
+ const name = "version compatibility";
22867
+ const cli = parse4(cliVersion);
22868
+ const backend = parse4(backendVersion);
22869
+ if (!cli || !backend) {
22870
+ return {
22871
+ name,
22872
+ status: "warn",
22873
+ message: `CLI ${cliVersion} and backend ${backendVersion} cannot be compared as versions.`
22874
+ };
22875
+ }
22876
+ if (cli.major !== backend.major) {
22877
+ return {
22878
+ name,
22879
+ status: "fail",
22880
+ message: `CLI ${cliVersion} and backend ${backendVersion} are different major versions. Install a matching CLI with npm install -g @eudiplo/cli@${backend.major}.`
22881
+ };
22882
+ }
22883
+ if (cli.minor !== backend.minor) {
22884
+ return {
22885
+ name,
22886
+ status: "warn",
22887
+ message: `CLI ${cliVersion} and backend ${backendVersion} differ in minor version; some commands may not be supported.`
22888
+ };
22889
+ }
22890
+ return {
22891
+ name,
22892
+ status: "pass",
22893
+ message: `CLI ${cliVersion} matches backend ${backendVersion}.`
22894
+ };
22895
+ }
22896
+ function readBackendVersion(body) {
22897
+ if (typeof body === "object" && body !== null && "version" in body) {
22898
+ const version = body.version;
22899
+ return typeof version === "string" && version ? version : void 0;
22900
+ }
22901
+ return void 0;
22902
+ }
22903
+ function readProviderHealth(body) {
22904
+ if (!Array.isArray(body)) {
22905
+ throw new Error("the provider health response was not a list");
22906
+ }
22907
+ return body.map((entry) => {
22908
+ const record = entry;
22909
+ return {
22910
+ providerId: String(record.providerId ?? "unknown"),
22911
+ type: String(record.type ?? "unknown"),
22912
+ ok: record.ok === true,
22913
+ latencyMs: typeof record.latencyMs === "number" ? record.latencyMs : void 0,
22914
+ error: typeof record.error === "string" ? record.error : void 0
22915
+ };
22916
+ });
22917
+ }
22918
+ function summarizeProviderHealth(providers) {
22919
+ const name = "KMS providers";
22920
+ if (providers.length === 0) {
22921
+ return {
22922
+ name,
22923
+ status: "warn",
22924
+ message: "No KMS providers are registered for this tenant."
22925
+ };
22926
+ }
22927
+ const failing = providers.filter((provider) => !provider.ok);
22928
+ if (failing.length > 0) {
22929
+ return {
22930
+ name,
22931
+ status: "fail",
22932
+ message: `${failing.length}/${providers.length} provider(s) unhealthy: ${failing.map(
22933
+ (provider) => `${provider.providerId} (${provider.type})${provider.error ? ", health check reported an error" : ""}`
22934
+ ).join(", ")}`
22935
+ };
22936
+ }
22937
+ return {
22938
+ name,
22939
+ status: "pass",
22940
+ message: `${providers.length} provider(s) healthy: ${providers.map(
22941
+ (provider) => `${provider.providerId} (${provider.type}${provider.latencyMs === void 0 ? "" : `, ${provider.latencyMs}ms`})`
22942
+ ).join(", ")}`
22943
+ };
22944
+ }
22945
+ function parse4(version) {
22946
+ const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
22947
+ if (!match) {
22948
+ return void 0;
22949
+ }
22950
+ return {
22951
+ major: Number(match[1]),
22952
+ minor: Number(match[2]),
22953
+ patch: Number(match[3])
22954
+ };
22955
+ }
22956
+
22957
+ // src/services/tls-expiry.ts
22958
+ import { connect } from "node:tls";
22959
+ var defaultExpiryWarningDays = 30;
22960
+ var dayInMs = 24 * 60 * 60 * 1e3;
22961
+ function evaluateCertificate(certificate, now, warningDays = defaultExpiryWarningDays) {
22962
+ const remainingDays = Math.floor(
22963
+ (certificate.validTo.getTime() - now.getTime()) / dayInMs
22964
+ );
22965
+ const expiresOn = certificate.validTo.toISOString().slice(0, 10);
22966
+ if (remainingDays < 0) {
22967
+ return {
22968
+ name: "TLS certificate",
22969
+ status: "fail",
22970
+ message: `The certificate issued by ${certificate.issuer} expired on ${expiresOn}.`
22971
+ };
22972
+ }
22973
+ if (remainingDays <= warningDays) {
22974
+ return {
22975
+ name: "TLS certificate",
22976
+ status: "warn",
22977
+ message: `The certificate expires on ${expiresOn}, in ${remainingDays} day(s). Renew it before then.`
22978
+ };
22979
+ }
22980
+ return {
22981
+ name: "TLS certificate",
22982
+ status: "pass",
22983
+ message: `Valid until ${expiresOn} (${remainingDays} day(s) left), issued by ${certificate.issuer}.`
22984
+ };
22985
+ }
22986
+ var readPeerCertificate = (url) => new Promise((resolvePeer, rejectPeer) => {
22987
+ const socket = connect(
22988
+ {
22989
+ host: url.hostname,
22990
+ port: Number(url.port || 443),
22991
+ servername: url.hostname,
22992
+ rejectUnauthorized: true
22993
+ },
22994
+ () => {
22995
+ const certificate = socket.getPeerCertificate();
22996
+ socket.end();
22997
+ if (!certificate || !certificate.valid_to) {
22998
+ rejectPeer(new Error("no peer certificate was presented"));
22999
+ return;
23000
+ }
23001
+ resolvePeer({
23002
+ subject: String(certificate.subject?.CN ?? url.hostname),
23003
+ issuer: String(certificate.issuer?.CN ?? "unknown issuer"),
23004
+ validTo: new Date(certificate.valid_to)
23005
+ });
23006
+ }
23007
+ );
23008
+ socket.setTimeout(1e4, () => {
23009
+ socket.destroy();
23010
+ rejectPeer(new Error("the TLS handshake timed out"));
23011
+ });
23012
+ socket.once("error", (error) => {
23013
+ socket.destroy();
23014
+ rejectPeer(error);
23015
+ });
23016
+ });
23017
+ async function checkTlsCertificate(baseUrl, now, read = readPeerCertificate, warningDays = defaultExpiryWarningDays) {
23018
+ if (baseUrl.protocol !== "https:") {
23019
+ return {
23020
+ name: "TLS certificate",
23021
+ status: "skip",
23022
+ message: `${baseUrl.origin} does not use HTTPS, so there is no certificate to check.`
23023
+ };
23024
+ }
23025
+ try {
23026
+ return evaluateCertificate(await read(baseUrl), now, warningDays);
23027
+ } catch (error) {
23028
+ return {
23029
+ name: "TLS certificate",
23030
+ status: "fail",
23031
+ message: `The certificate of ${baseUrl.origin} could not be verified: ${error instanceof Error ? error.message : String(error)}`
23032
+ };
23033
+ }
23034
+ }
23035
+
22544
23036
  // src/services/diagnostics.ts
22545
- async function runDoctor(instance, context, driverDiagnostics) {
22546
- const checks = [];
22547
- const baseUrl = parseUrl(instance.url);
22548
- if (!baseUrl) {
22549
- return [
22550
- {
23037
+ var publicUrlCheck = {
23038
+ id: "public-url",
23039
+ async run({ instance, baseUrl }) {
23040
+ if (!baseUrl) {
23041
+ return {
22551
23042
  name: "public URL",
22552
23043
  status: "fail",
22553
23044
  message: `${instance.url} is not a valid absolute URL`
22554
- }
22555
- ];
23045
+ };
23046
+ }
23047
+ return {
23048
+ name: "public URL",
23049
+ status: baseUrl.protocol === "https:" ? "pass" : "warn",
23050
+ message: baseUrl.protocol === "https:" ? `${baseUrl.href} uses HTTPS` : `${baseUrl.href} does not use HTTPS`
23051
+ };
22556
23052
  }
22557
- checks.push({
22558
- name: "public URL",
22559
- status: baseUrl.protocol === "https:" ? "pass" : "warn",
22560
- message: baseUrl.protocol === "https:" ? `${baseUrl.href} uses HTTPS` : `${baseUrl.href} does not use HTTPS`
22561
- });
22562
- checks.push(
22563
- await checkEndpoint("API reachability", baseUrl, "/api/docs", context)
22564
- );
22565
- checks.push(
22566
- await checkEndpoint("health endpoint", baseUrl, "/health", context)
22567
- );
22568
- checks.push(checkAuthentication(context));
22569
- if (instance.clientUrl) {
23053
+ };
23054
+ var apiReachabilityCheck = {
23055
+ id: "api-reachability",
23056
+ run: ({ baseUrl, context }) => requireBaseUrl(
23057
+ "API reachability",
23058
+ baseUrl,
23059
+ (url) => checkEndpoint("API reachability", url, "/api/docs", context)
23060
+ )
23061
+ };
23062
+ var healthEndpointCheck = {
23063
+ id: "health-endpoint",
23064
+ run: ({ baseUrl, context }) => requireBaseUrl(
23065
+ "health endpoint",
23066
+ baseUrl,
23067
+ (url) => checkEndpoint("health endpoint", url, "/health", context)
23068
+ )
23069
+ };
23070
+ var tlsCertificateCheck = {
23071
+ id: "tls-certificate",
23072
+ run: ({ baseUrl, now, readCertificate }) => requireBaseUrl(
23073
+ "TLS certificate",
23074
+ baseUrl,
23075
+ (url) => checkTlsCertificate(url, now, readCertificate)
23076
+ )
23077
+ };
23078
+ var authenticationCheck = {
23079
+ id: "authentication",
23080
+ async run({ context }) {
23081
+ const configured = Boolean(context.env.EUDIPLO_CLIENT_ID) && Boolean(context.env.EUDIPLO_CLIENT_SECRET);
23082
+ return {
23083
+ name: "authentication configuration",
23084
+ status: configured ? "pass" : "warn",
23085
+ message: configured ? "Client credentials are available in environment variables." : "Set EUDIPLO_CLIENT_ID and EUDIPLO_CLIENT_SECRET when commands need authenticated API access."
23086
+ };
23087
+ }
23088
+ };
23089
+ var clientConnectivityCheck = {
23090
+ id: "client-connectivity",
23091
+ async run({ instance, context }) {
23092
+ if (!instance.clientUrl) {
23093
+ return {
23094
+ name: "client connectivity",
23095
+ status: "skip",
23096
+ message: "No client URL configured for this instance."
23097
+ };
23098
+ }
22570
23099
  const clientUrl = parseUrl(instance.clientUrl);
22571
- checks.push(
22572
- clientUrl ? await checkEndpoint(
22573
- "client connectivity",
22574
- clientUrl,
22575
- "/",
22576
- context
22577
- ) : {
23100
+ if (!clientUrl) {
23101
+ return {
22578
23102
  name: "client connectivity",
22579
23103
  status: "fail",
22580
23104
  message: `${instance.clientUrl} is not a valid absolute URL`
23105
+ };
23106
+ }
23107
+ return checkEndpoint("client connectivity", clientUrl, "/", context);
23108
+ }
23109
+ };
23110
+ var versionCompatibilityCheck = {
23111
+ id: "version-compatibility",
23112
+ run: ({ baseUrl, context, accessToken }) => requireBaseUrl("version compatibility", baseUrl, async (url) => {
23113
+ const token = await accessToken();
23114
+ if (token.kind !== "token") {
23115
+ return authUnavailable("version compatibility", token);
23116
+ }
23117
+ const response = await fetchAuthenticated(
23118
+ new URL("version", withTrailingSlash(url)),
23119
+ token.accessToken,
23120
+ context
23121
+ );
23122
+ if (!response.ok) {
23123
+ if (response.status === 404) {
23124
+ return {
23125
+ name: "version compatibility",
23126
+ status: "skip",
23127
+ message: "This backend does not expose /version, so compatibility cannot be checked."
23128
+ };
22581
23129
  }
23130
+ return {
23131
+ name: "version compatibility",
23132
+ status: "fail",
23133
+ message: `The backend version could not be read: ${response.reason}`
23134
+ };
23135
+ }
23136
+ const backendVersion = readBackendVersion(response.body);
23137
+ if (!backendVersion) {
23138
+ return {
23139
+ name: "version compatibility",
23140
+ status: "warn",
23141
+ message: "The backend did not report a version."
23142
+ };
23143
+ }
23144
+ return compareVersions(package_default.version, backendVersion);
23145
+ })
23146
+ };
23147
+ var kmsHealthCheck = {
23148
+ id: "kms-health",
23149
+ run: ({ baseUrl, context, accessToken }) => requireBaseUrl("KMS providers", baseUrl, async (url) => {
23150
+ const token = await accessToken();
23151
+ if (token.kind !== "token") {
23152
+ return authUnavailable("KMS providers", token);
23153
+ }
23154
+ const response = await fetchAuthenticated(
23155
+ new URL("key-chain/providers/health", withTrailingSlash(url)),
23156
+ token.accessToken,
23157
+ context
22582
23158
  );
22583
- } else {
22584
- checks.push({
22585
- name: "client connectivity",
22586
- status: "warn",
22587
- message: "No client URL configured for this instance."
22588
- });
23159
+ if (!response.ok) {
23160
+ if (response.status === 404) {
23161
+ return {
23162
+ name: "KMS providers",
23163
+ status: "skip",
23164
+ message: "This backend does not expose the KMS provider health endpoint."
23165
+ };
23166
+ }
23167
+ return {
23168
+ name: "KMS providers",
23169
+ status: "fail",
23170
+ message: `Provider health could not be read: ${response.reason}`
23171
+ };
23172
+ }
23173
+ try {
23174
+ return summarizeProviderHealth(
23175
+ readProviderHealth(response.body)
23176
+ );
23177
+ } catch {
23178
+ return {
23179
+ name: "KMS providers",
23180
+ status: "warn",
23181
+ message: "The provider health response could not be read."
23182
+ };
23183
+ }
23184
+ })
23185
+ };
23186
+ function authUnavailable(name, token) {
23187
+ return {
23188
+ name,
23189
+ status: token.kind === "unavailable" ? "skip" : "fail",
23190
+ message: token.kind === "unavailable" ? `Needs authentication: ${token.reason}.` : `Could not authenticate: ${token.reason}.`
23191
+ };
23192
+ }
23193
+ var coreChecks = [
23194
+ publicUrlCheck,
23195
+ apiReachabilityCheck,
23196
+ healthEndpointCheck,
23197
+ tlsCertificateCheck,
23198
+ authenticationCheck,
23199
+ versionCompatibilityCheck,
23200
+ kmsHealthCheck,
23201
+ clientConnectivityCheck
23202
+ ];
23203
+ async function runDoctor(instance, context, driverDiagnostics, options = {}) {
23204
+ const baseUrl = parseUrl(instance.url);
23205
+ let token;
23206
+ const checkContext = {
23207
+ instance,
23208
+ context,
23209
+ baseUrl,
23210
+ now: options.now ?? /* @__PURE__ */ new Date(),
23211
+ readCertificate: options.readCertificate ?? context.readCertificate ?? readPeerCertificate,
23212
+ accessToken: () => {
23213
+ if (!baseUrl) {
23214
+ return Promise.resolve({
23215
+ kind: "unavailable",
23216
+ reason: "the instance URL could not be parsed"
23217
+ });
23218
+ }
23219
+ token ??= requestAccessToken(baseUrl, context);
23220
+ return token;
23221
+ }
23222
+ };
23223
+ const checks = [];
23224
+ for (const check of coreChecks) {
23225
+ checks.push(await check.run(checkContext));
22589
23226
  }
22590
23227
  checks.push(...driverDiagnostics);
22591
23228
  return checks;
22592
23229
  }
22593
- function hasFailedChecks(checks) {
22594
- return checks.some((check) => check.status === "fail");
23230
+ function summarizeChecks(checks) {
23231
+ return {
23232
+ pass: count(checks, "pass"),
23233
+ warn: count(checks, "warn"),
23234
+ fail: count(checks, "fail"),
23235
+ skip: count(checks, "skip")
23236
+ };
23237
+ }
23238
+ function hasFailedChecks(checks, strict = false) {
23239
+ return checks.some(
23240
+ (check) => check.status === "fail" || strict && check.status === "warn"
23241
+ );
22595
23242
  }
22596
23243
  function formatChecks(checks) {
22597
23244
  return checks.map(
22598
23245
  (check) => `${formatStatus(check.status)} ${check.name}: ${check.message}`
22599
23246
  ).join("\n");
22600
23247
  }
23248
+ function formatSummary(summary) {
23249
+ return `${summary.pass} passed, ${summary.warn} warning(s), ${summary.fail} failure(s), ${summary.skip} skipped`;
23250
+ }
23251
+ function count(checks, status) {
23252
+ return checks.filter((check) => check.status === status).length;
23253
+ }
23254
+ async function requireBaseUrl(name, baseUrl, run) {
23255
+ if (!baseUrl) {
23256
+ return {
23257
+ name,
23258
+ status: "skip",
23259
+ message: "The instance URL could not be parsed."
23260
+ };
23261
+ }
23262
+ return run(baseUrl);
23263
+ }
22601
23264
  async function checkEndpoint(name, baseUrl, path2, context) {
22602
23265
  const url = new URL(path2, baseUrl);
22603
23266
  try {
@@ -22622,22 +23285,6 @@ async function checkEndpoint(name, baseUrl, path2, context) {
22622
23285
  };
22623
23286
  }
22624
23287
  }
22625
- function checkAuthentication(context) {
22626
- const hasClientId = Boolean(context.env.EUDIPLO_CLIENT_ID);
22627
- const hasClientSecret = Boolean(context.env.EUDIPLO_CLIENT_SECRET);
22628
- if (hasClientId && hasClientSecret) {
22629
- return {
22630
- name: "authentication configuration",
22631
- status: "pass",
22632
- message: "Client credentials are available in environment variables."
22633
- };
22634
- }
22635
- return {
22636
- name: "authentication configuration",
22637
- status: "warn",
22638
- message: "Set EUDIPLO_CLIENT_ID and EUDIPLO_CLIENT_SECRET when commands need authenticated API access."
22639
- };
22640
- }
22641
23288
  function parseUrl(value) {
22642
23289
  try {
22643
23290
  const url = new URL(value);
@@ -22653,28 +23300,54 @@ function formatStatus(status) {
22653
23300
  if (status === "warn") {
22654
23301
  return "WARN";
22655
23302
  }
23303
+ if (status === "skip") {
23304
+ return "SKIP";
23305
+ }
22656
23306
  return "FAIL";
22657
23307
  }
22658
23308
 
22659
23309
  // src/commands/doctor/action.ts
22660
- async function runDoctorCommand(config, parsed, context) {
22661
- const [instanceName, instance] = resolveInstance(config, parsed);
23310
+ async function runDoctorCommand(config, parsed, context, options = {}) {
23311
+ const strict = parsed.flags.strict === true;
23312
+ const instances = parsed.flags.all === true ? Object.keys(config.instances) : [resolveInstance(config, parsed)[0]];
23313
+ if (instances.length === 0) {
23314
+ throw new Error("No instances are configured.");
23315
+ }
23316
+ let failed = false;
23317
+ for (const [index, instanceName] of instances.entries()) {
23318
+ const instance = config.instances[instanceName];
23319
+ if (index > 0) {
23320
+ context.stdout.write("\n");
23321
+ }
23322
+ const checks = await runDiagnostics(instance, context, options);
23323
+ context.stdout.write(
23324
+ `Doctor for ${instanceName} (${instance.target})
23325
+ `
23326
+ );
23327
+ context.stdout.write(`${formatChecks(checks)}
23328
+ `);
23329
+ context.stdout.write(`${formatSummary(summarizeChecks(checks))}
23330
+ `);
23331
+ failed = hasFailedChecks(checks, strict) || failed;
23332
+ }
23333
+ return failed ? 1 : 0;
23334
+ }
23335
+ async function runDiagnostics(instance, context, options) {
22662
23336
  const driver = drivers[instance.target];
22663
- const checks = await runDoctor(
23337
+ return runDoctor(
22664
23338
  instance,
22665
23339
  context,
22666
- await driver.diagnostics(instance, context)
23340
+ await driver.diagnostics(instance, context),
23341
+ options
22667
23342
  );
22668
- context.stdout.write(`Doctor for ${instanceName} (${instance.target})
22669
- `);
22670
- context.stdout.write(`${formatChecks(checks)}
22671
- `);
22672
- return hasFailedChecks(checks) ? 1 : 0;
22673
23343
  }
22674
23344
 
22675
23345
  // src/commands/doctor/index.ts
22676
23346
  function createDoctorCommand(context, setExitCode) {
22677
- return new Command("doctor").description("Run deployment diagnostics").option("--instance <name>", "select a configured instance").action(async (options) => {
23347
+ 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(
23348
+ "--strict",
23349
+ "treat warnings as failures (skipped checks never fail)"
23350
+ ).action(async (options) => {
22678
23351
  const { config } = await loadCliState(context);
22679
23352
  setExitCode(
22680
23353
  await runDoctorCommand(
@@ -23458,7 +24131,7 @@ function parseVersion(tag) {
23458
24131
  patch: Number(match[3])
23459
24132
  };
23460
24133
  }
23461
- function compareVersions(a, b) {
24134
+ function compareVersions2(a, b) {
23462
24135
  return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
23463
24136
  }
23464
24137
  var migrationBaseUrl = "https://docs.eudiplo.dev/migration";
@@ -23470,7 +24143,7 @@ function migrationNotes(from, to) {
23470
24143
  `Cannot compare ${from} and ${to} as versions. Check the release notes and ${migrationBaseUrl} before continuing.`
23471
24144
  ];
23472
24145
  }
23473
- const order = compareVersions(fromVersion, toVersion);
24146
+ const order = compareVersions2(fromVersion, toVersion);
23474
24147
  if (order > 0) {
23475
24148
  return [
23476
24149
  `${to} is older than ${from}. Downgrades are not supported once database migrations have run; restore a backup instead if you need to go back.`
@@ -23608,78 +24281,6 @@ function createUpgradeCommand(context, setExitCode) {
23608
24281
 
23609
24282
  // src/commands/version/action.ts
23610
24283
  import { isSea as isSea2 } from "node:sea";
23611
-
23612
- // package.json
23613
- var package_default = {
23614
- name: "@eudiplo/cli",
23615
- version: "8.0.2-main.e4bc4d4",
23616
- description: "Deployment-aware command line tools for EUDIPLO",
23617
- private: false,
23618
- type: "module",
23619
- bin: {
23620
- eudiplo: "dist/index.js"
23621
- },
23622
- files: [
23623
- "dist",
23624
- "templates",
23625
- "README.md"
23626
- ],
23627
- scripts: {
23628
- "assets:sync": "node scripts/sync-cli-assets.cjs",
23629
- "assets:check": "node scripts/check-cli-assets.cjs",
23630
- build: "pnpm assets:sync && pnpm assets:check && tsc -p tsconfig.json",
23631
- "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",
23632
- "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",
23633
- "build:sea": "node scripts/build-cli-sea.mjs",
23634
- dev: "tsx src/index.ts",
23635
- test: "vitest run --project cli",
23636
- lint: "biome lint .",
23637
- prepublishOnly: "pnpm build:publish"
23638
- },
23639
- keywords: [
23640
- "eudiplo",
23641
- "cli",
23642
- "eudi",
23643
- "wallet",
23644
- "oid4vci",
23645
- "oid4vp"
23646
- ],
23647
- repository: {
23648
- type: "git",
23649
- url: "git+https://github.com/openwallet-foundation/eudiplo.git",
23650
- directory: "apps/cli"
23651
- },
23652
- homepage: "https://github.com/openwallet-foundation/eudiplo/tree/main/apps/cli#readme",
23653
- bugs: {
23654
- url: "https://github.com/openwallet-foundation/eudiplo/issues"
23655
- },
23656
- author: "OpenWallet Foundation",
23657
- license: "Apache-2.0",
23658
- engines: {
23659
- node: ">=22.12.0"
23660
- },
23661
- publishConfig: {
23662
- access: "public"
23663
- },
23664
- dependencies: {
23665
- "@eudiplo/config-format": "workspace:*",
23666
- ajv: "^8.20.0",
23667
- "ajv-formats": "^3.0.1",
23668
- commander: "^15.0.0",
23669
- fflate: "^0.8.3",
23670
- "jsonc-parser": "^3.3.1"
23671
- },
23672
- devDependencies: {
23673
- "@biomejs/biome": "2.5.14",
23674
- "@types/node": "^26.4.0",
23675
- esbuild: "^0.28.2",
23676
- tsx: "^4.23.13",
23677
- typescript: "^7.0.2",
23678
- vitest: "4.1.11"
23679
- }
23680
- };
23681
-
23682
- // src/commands/version/action.ts
23683
24284
  function versionText() {
23684
24285
  return `${package_default.name} ${package_default.version}`;
23685
24286
  }