@saasicat/cli 0.18.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -53,7 +53,7 @@ var CliContextService = class {
53
53
  const fromEnv = process.env[this.config.adminEmailEnvVar] ?? "";
54
54
  const email = (asFlag ?? fromEnv).trim().toLowerCase();
55
55
  if (!email) {
56
- throw new CliError("NO_IDENTITY", `Keine Admin-Identit\xE4t gesetzt. Bitte $${this.config.adminEmailEnvVar} setzen oder --as <email> \xFCbergeben.`, 2);
56
+ throw new CliError("NO_IDENTITY", `No admin identity set. Please set $${this.config.adminEmailEnvVar} or pass --as <email>.`, 2);
57
57
  }
58
58
  const host = os.hostname();
59
59
  return {
@@ -72,10 +72,10 @@ var CliContextService = class {
72
72
  async ensureSuperAdmin(identity) {
73
73
  const user = await this.users.findByEmail(identity.email);
74
74
  if (!user || user.deletedAt || !user.isActive) {
75
- throw new CliError("USER_NOT_FOUND", `SUPER_ADMIN-User ${identity.email} nicht gefunden oder inaktiv.`, 2);
75
+ throw new CliError("USER_NOT_FOUND", `SUPER_ADMIN-User ${identity.email} not found or inactive.`, 2);
76
76
  }
77
77
  if (user.platformRole !== "SUPER_ADMIN") {
78
- throw new CliError("NOT_SUPER_ADMIN", `User ${identity.email} hat Rolle ${user.platformRole} \u2014 nur SUPER_ADMIN darf das CLI nutzen.`, 2);
78
+ throw new CliError("NOT_SUPER_ADMIN", `User ${identity.email} has role ${user.platformRole} \u2014 only SUPER_ADMIN may use this CLI.`, 2);
79
79
  }
80
80
  return user;
81
81
  }
@@ -94,7 +94,7 @@ var CliContextService = class {
94
94
  }
95
95
  const enabled = await this.mfa.isEnabled(userId);
96
96
  if (!enabled) {
97
- throw new CliError("MFA_NOT_SET_UP", "MFA ist nicht konfiguriert. Bitte zuerst 'admin mfa-setup' ausf\xFChren.", 3);
97
+ throw new CliError("MFA_NOT_SET_UP", "MFA is not configured. Run 'admin mfa-setup' first.", 3);
98
98
  }
99
99
  const code = await this.prompt("TOTP-Code: ");
100
100
  const ok2 = await this.mfa.verify({
@@ -102,7 +102,7 @@ var CliContextService = class {
102
102
  code
103
103
  });
104
104
  if (!ok2) {
105
- throw new CliError("MFA_FAILED", "TOTP-Code ung\xFCltig.", 3);
105
+ throw new CliError("MFA_FAILED", "Invalid TOTP code.", 3);
106
106
  }
107
107
  }
108
108
  // ---------------------------------------------------------------------
@@ -111,9 +111,9 @@ var CliContextService = class {
111
111
  async ensureProductionConfirmation(opts = {}) {
112
112
  if (!this.config.isProductionEnvironment()) return;
113
113
  if (opts.yes) return;
114
- const answer = await this.prompt("Tippe production zur Best\xE4tigung: ");
114
+ const answer = await this.prompt("Type production to confirm: ");
115
115
  if (answer.trim().toLowerCase() !== "production") {
116
- throw new CliError("PRODUCTION_CONFIRM_ABORTED", "Production-Confirmation abgebrochen.", 1);
116
+ throw new CliError("PRODUCTION_CONFIRM_ABORTED", "Production confirmation aborted.", 1);
117
117
  }
118
118
  }
119
119
  // ---------------------------------------------------------------------
@@ -141,7 +141,7 @@ var CliContextService = class {
141
141
  // ---------------------------------------------------------------------
142
142
  table(rows) {
143
143
  if (rows.length === 0) {
144
- console.log("\u2014 keine Eintr\xE4ge \u2014");
144
+ console.log("\u2014 no entries \u2014");
145
145
  return;
146
146
  }
147
147
  console.table(rows);
@@ -223,9 +223,9 @@ var MfaSetupFlow = class {
223
223
  const user = await this.ctx.ensureSuperAdmin(identity);
224
224
  const alreadyEnabled = await this.mfa.isEnabled(user.id);
225
225
  if (alreadyEnabled && !options.force) {
226
- const answer = await this.ctx.prompt("MFA ist bereits konfiguriert. Tippe `yes`, um das Secret zu \xFCberschreiben: ");
226
+ const answer = await this.ctx.prompt("MFA is already configured. Type `yes` to overwrite the secret: ");
227
227
  if (answer.trim().toLowerCase() !== "yes") {
228
- throw new CliError("MFA_SETUP_ABORTED", "Re-Setup nicht best\xE4tigt \u2014 bestehendes Secret bleibt aktiv.", 1);
228
+ throw new CliError("MFA_SETUP_ABORTED", "Re-setup not confirmed \u2014 the existing secret stays active.", 1);
229
229
  }
230
230
  }
231
231
  const setup = await this.mfa.setup(user.id, user.email, options.issuer);
@@ -253,15 +253,15 @@ var MfaSetupFlow = class {
253
253
  */
254
254
  formatSetupResult(result) {
255
255
  return [
256
- `MFA-Setup f\xFCr ${result.userEmail} abgeschlossen.`,
256
+ `MFA setup for ${result.userEmail} completed.`,
257
257
  "",
258
258
  `Secret (Base32): ${result.secret}`,
259
259
  `otpauth-URI: ${result.otpauthUri}`,
260
260
  "",
261
- "Bitte den otpauth-URI in den Authenticator (Google Authenticator,",
262
- "1Password, \u2026) importieren oder als QR-Code in einem QR-Generator",
263
- "rendern. Danach mit dem ersten TOTP-Code testen, dass der Login",
264
- "funktioniert \u2014 sonst kommst du nicht mehr ans CLI."
261
+ "Import the otpauth URI into your authenticator (Google Authenticator,",
262
+ "1Password, \u2026), or render it as a QR code in a QR generator",
263
+ ". Then verify with the first TOTP code that login",
264
+ "works \u2014 otherwise you will lock yourself out of the CLI."
265
265
  ].join("\n");
266
266
  }
267
267
  };
@@ -333,16 +333,16 @@ var WhoAmIFlow = class {
333
333
  }
334
334
  formatResult(r) {
335
335
  const lines = [
336
- `Identit\xE4t: ${r.email}`,
336
+ `Identity: ${r.email}`,
337
337
  `Host: ${r.host}`,
338
338
  `Actor-Tag: ${r.actor}`,
339
- `User-ID: ${r.userId ?? "\u2014 (User nicht gefunden)"}`,
340
- `Plattform-Rolle: ${r.isSuperAdmin ? "SUPER_ADMIN \u2713" : "\u2014 (kein SUPER_ADMIN!)"}`,
341
- `MFA konfiguriert: ${r.mfaEnabled ? "\u2713" : "\u2717 \u2014 bitte `admin mfa-setup` ausf\xFChren"}`,
339
+ `User ID: ${r.userId ?? "\u2014 (user not found)"}`,
340
+ `Platform role: ${r.isSuperAdmin ? "SUPER_ADMIN \u2713" : "\u2014 (not a SUPER_ADMIN!)"}`,
341
+ `MFA configured: ${r.mfaEnabled ? "\u2713" : "\u2717 \u2014 run `admin mfa-setup`"}`,
342
342
  `Environment: ${r.isProduction ? "PRODUCTION" : "non-production"}`
343
343
  ];
344
344
  if (r.mfaSkipActive) {
345
- lines.push("\u26A0 MFA-Bypass aktiv (SKIP-Env-Var gesetzt, non-prod)");
345
+ lines.push("\u26A0 MFA bypass active (SKIP env var set, non-prod)");
346
346
  }
347
347
  return lines.join("\n");
348
348
  }
@@ -478,7 +478,7 @@ var DoctorFlow = class {
478
478
  }
479
479
  formatReport(report) {
480
480
  const lines = [
481
- `Doctor-Check (Gesamtstatus: ${report.overall.toUpperCase()})`,
481
+ `Doctor check (overall status: ${report.overall.toUpperCase()})`,
482
482
  ""
483
483
  ];
484
484
  for (const c of report.checks) {
@@ -551,62 +551,62 @@ __name(allCapabilities, "allCapabilities");
551
551
  var DEFAULT_MANIFEST_CHECKS = [
552
552
  {
553
553
  id: "manifest.schema-version",
554
- label: "schemaVersion ist 1",
555
- run: /* @__PURE__ */ __name((m) => m.schemaVersion === 1 ? ok("schemaVersion=1") : err(`Unerwartete schemaVersion ${m.schemaVersion}`), "run")
554
+ label: "schemaVersion is 1",
555
+ run: /* @__PURE__ */ __name((m) => m.schemaVersion === 1 ? ok("schemaVersion=1") : err(`Unexpected schemaVersion ${m.schemaVersion}`), "run")
556
556
  },
557
557
  {
558
558
  id: "manifest.hash-format",
559
- label: "manifestHash folgt sha256-<base64url>-Pattern",
559
+ label: "manifestHash follows the sha256-<base64url> pattern",
560
560
  run: /* @__PURE__ */ __name((m) => {
561
561
  const hash = m.build?.manifestHash;
562
- if (!hash) return err("manifestHash fehlt");
563
- return /^sha256-[A-Za-z0-9_-]+$/.test(hash) ? ok(hash) : err(`manifestHash hat falsches Format: ${hash}`);
562
+ if (!hash) return err("manifestHash is missing");
563
+ return /^sha256-[A-Za-z0-9_-]+$/.test(hash) ? ok(hash) : err(`manifestHash has the wrong format: ${hash}`);
564
564
  }, "run")
565
565
  },
566
566
  {
567
567
  id: "manifest.project-page-component-keys",
568
- label: "ProjectPage.componentKey-Format (lowercase-hyphenated ODER namespace.dot)",
568
+ label: "ProjectPage.componentKey format (lowercase-hyphenated OR namespace.dot)",
569
569
  run: /* @__PURE__ */ __name((m) => {
570
570
  const bad = [];
571
571
  for (const p of allProjectPages(m)) {
572
572
  if (!COMPONENT_KEY_PATTERN.test(p.componentKey)) bad.push(p.componentKey);
573
573
  }
574
- return bad.length === 0 ? ok(`${allProjectPages(m).length} ProjectPage(s) ok`) : err(`${bad.length} componentKey(s) verletzen das Pattern`, bad);
574
+ return bad.length === 0 ? ok(`${allProjectPages(m).length} ProjectPage(s) ok`) : err(`${bad.length} componentKey(s) violate the pattern`, bad);
575
575
  }, "run")
576
576
  },
577
577
  {
578
578
  id: "manifest.tenant-action-keys",
579
- label: "TenantAction.actionKey-Format (domain.action \u2014 SPEC \xA74.2.1)",
579
+ label: "TenantAction.actionKey format (domain.action \u2014 SPEC \xA74.2.1)",
580
580
  run: /* @__PURE__ */ __name((m) => {
581
581
  const bad = [];
582
582
  for (const a of allTenantActions(m)) {
583
583
  if (!TENANT_ACTION_KEY_PATTERN.test(a.actionKey)) bad.push(a.actionKey);
584
584
  }
585
- return bad.length === 0 ? ok("alle actionKeys folgen domain.action") : err(`${bad.length} actionKey(s) verletzen das Pattern`, bad);
585
+ return bad.length === 0 ? ok("every actionKey follows domain.action") : err(`${bad.length} actionKey(s) violate the pattern`, bad);
586
586
  }, "run")
587
587
  },
588
588
  {
589
589
  id: "manifest.audit-action-keys",
590
- label: "AuditAction.key-Format (SCREAMING_SNAKE_CASE)",
590
+ label: "AuditAction.key format (SCREAMING_SNAKE_CASE)",
591
591
  run: /* @__PURE__ */ __name((m) => {
592
592
  const bad = [];
593
593
  for (const a of allAuditActions(m)) {
594
594
  if (!ACTION_KEY_PATTERN.test(a.key)) bad.push(a.key);
595
595
  }
596
- return bad.length === 0 ? ok("alle AuditAction.keys SCREAMING_SNAKE_CASE") : err(`${bad.length} AuditAction.key(s) verletzen das Pattern`, bad);
596
+ return bad.length === 0 ? ok("every AuditAction.key is SCREAMING_SNAKE_CASE") : err(`${bad.length} AuditAction.key(s) violate the pattern`, bad);
597
597
  }, "run")
598
598
  },
599
599
  {
600
600
  id: "manifest.capabilities-pattern",
601
- label: "Capability-Pattern <domain>.<action> (SPEC \xA74.2.1)",
601
+ label: "Capability pattern <domain>.<action> (SPEC \xA74.2.1)",
602
602
  run: /* @__PURE__ */ __name((m) => {
603
603
  const bad = allCapabilities(m).filter((c) => !CAPABILITY_PATTERN.test(c));
604
- return bad.length === 0 ? ok(`${allCapabilities(m).length} Capabilities ok`) : err(`${bad.length} Capability/-ies verletzen das Pattern`, bad);
604
+ return bad.length === 0 ? ok(`${allCapabilities(m).length} Capabilities ok`) : err(`${bad.length} Capability/-ies violate the pattern`, bad);
605
605
  }, "run")
606
606
  },
607
607
  {
608
608
  id: "manifest.required-capabilities-known",
609
- label: "requiredCapability-Referenzen existieren in capabilities-Map",
609
+ label: "requiredCapability references resolve against the capabilities map",
610
610
  run: /* @__PURE__ */ __name((m) => {
611
611
  const known = new Set(allCapabilities(m));
612
612
  const bad = [];
@@ -617,23 +617,23 @@ var DEFAULT_MANIFEST_CHECKS = [
617
617
  for (const k of allKpiCards(m)) visit(k.requiredCapability, k.id);
618
618
  for (const a of allTenantActions(m)) visit(a.requiredCapability, a.id);
619
619
  for (const c of allTenantColumns(m)) visit(c.requiredCapability, c.key);
620
- return bad.length === 0 ? ok("alle requiredCapability-Refs aufgel\xF6st") : err(`${bad.length} unbekannte Capability-Ref(s)`, bad);
620
+ return bad.length === 0 ? ok("every requiredCapability ref resolves") : err(`${bad.length} unknown capability ref(s)`, bad);
621
621
  }, "run")
622
622
  },
623
623
  {
624
624
  id: "manifest.route-prefix",
625
- label: "ProjectPage.route beginnt mit /admin",
625
+ label: "ProjectPage.route starts with /admin",
626
626
  run: /* @__PURE__ */ __name((m) => {
627
627
  const bad = [];
628
628
  for (const p of allProjectPages(m)) {
629
629
  if (!p.route.startsWith(ROUTE_PREFIX)) bad.push(p.route);
630
630
  }
631
- return bad.length === 0 ? ok("alle ProjectPage-Routes unter /admin") : err(`${bad.length} Route(s) ohne /admin-Prefix`, bad);
631
+ return bad.length === 0 ? ok("every ProjectPage route is under /admin") : err(`${bad.length} route(s) without the /admin prefix`, bad);
632
632
  }, "run")
633
633
  },
634
634
  {
635
635
  id: "manifest.kpi-slot-priority",
636
- label: "KpiCard.slotPriority (falls gesetzt) ist endliche Zahl",
636
+ label: "KpiCard.slotPriority (if set) is a finite number",
637
637
  run: /* @__PURE__ */ __name((m) => {
638
638
  const bad = [];
639
639
  for (const k of allKpiCards(m)) {
@@ -641,12 +641,12 @@ var DEFAULT_MANIFEST_CHECKS = [
641
641
  bad.push(k.id);
642
642
  }
643
643
  }
644
- return bad.length === 0 ? ok("alle slotPriority-Werte endlich") : err(`${bad.length} KpiCard(s) mit ung\xFCltiger slotPriority`, bad);
644
+ return bad.length === 0 ? ok("every slotPriority value is finite") : err(`${bad.length} KpiCard(s) with an invalid slotPriority`, bad);
645
645
  }, "run")
646
646
  },
647
647
  {
648
648
  id: "manifest.unique-project-page-ids",
649
- label: "ProjectPage.id ist unique",
649
+ label: "ProjectPage.id is unique",
650
650
  run: /* @__PURE__ */ __name((m) => {
651
651
  const seen = /* @__PURE__ */ new Map();
652
652
  for (const p of allProjectPages(m)) {
@@ -655,12 +655,12 @@ var DEFAULT_MANIFEST_CHECKS = [
655
655
  const dups = [
656
656
  ...seen.entries()
657
657
  ].filter(([, n]) => n > 1).map(([k]) => k);
658
- return dups.length === 0 ? ok("alle ProjectPage.id eindeutig") : err(`${dups.length} doppelte ProjectPage.id`, dups);
658
+ return dups.length === 0 ? ok("every ProjectPage.id is unique") : err(`${dups.length} duplicate ProjectPage.id(s)`, dups);
659
659
  }, "run")
660
660
  },
661
661
  {
662
662
  id: "manifest.unique-action-keys",
663
- label: "actionKeys sind je Namespace (TenantAction / AuditAction) eindeutig",
663
+ label: "actionKeys are unique per namespace (TenantAction / AuditAction)",
664
664
  run: /* @__PURE__ */ __name((m) => {
665
665
  const dups = [];
666
666
  const tenantSeen = /* @__PURE__ */ new Map();
@@ -677,21 +677,21 @@ var DEFAULT_MANIFEST_CHECKS = [
677
677
  for (const [k, n] of auditSeen) {
678
678
  if (n > 1) dups.push(`AuditAction:${k}`);
679
679
  }
680
- return dups.length === 0 ? ok("alle actionKeys je Namespace eindeutig") : err(`${dups.length} doppelte actionKey(s)`, dups);
680
+ return dups.length === 0 ? ok("every actionKey is unique per namespace") : err(`${dups.length} duplicate actionKey(s)`, dups);
681
681
  }, "run")
682
682
  },
683
683
  {
684
684
  id: "manifest.tenant-columns-batchable",
685
- label: "TenantColumns haben endpoint-Pfad f\xFCr Batch-Fetch",
685
+ label: "tenant columns carry an endpoint path for batch fetching",
686
686
  run: /* @__PURE__ */ __name((m) => {
687
687
  const bad = [];
688
688
  for (const c of allTenantColumns(m)) {
689
689
  if (!c.endpoint || c.endpoint.trim().length === 0) bad.push(c.key);
690
690
  else if (c.endpoint.includes("{slug}") || c.endpoint.includes("{tenantId}")) {
691
- bad.push(`${c.key} (per-Tenant statt batch)`);
691
+ bad.push(`${c.key} (per-tenant instead of batch)`);
692
692
  }
693
693
  }
694
- return bad.length === 0 ? ok("alle TenantColumns batch-f\xE4hig") : err(`${bad.length} TenantColumn(s) verletzen Batch-Pflicht`, bad);
694
+ return bad.length === 0 ? ok("every tenant column supports batch fetching") : err(`${bad.length} TenantColumn(s) violate the batch requirement`, bad);
695
695
  }, "run")
696
696
  }
697
697
  ];
@@ -737,7 +737,7 @@ var ManifestCliFlow = class {
737
737
  hash() {
738
738
  const m = this.access.getManifest();
739
739
  const h = m.build?.manifestHash;
740
- if (!h) throw new Error("manifestHash fehlt im Manifest \u2014 Boot-Zeit-Bug?");
740
+ if (!h) throw new Error("manifestHash is missing from the manifest \u2014 a boot-time bug?");
741
741
  return h;
742
742
  }
743
743
  /**
@@ -751,19 +751,19 @@ var ManifestCliFlow = class {
751
751
  if (m.schemaVersion !== 1) {
752
752
  return {
753
753
  ok: false,
754
- reason: `Unerwartete schemaVersion ${m.schemaVersion}`
754
+ reason: `Unexpected schemaVersion ${m.schemaVersion}`
755
755
  };
756
756
  }
757
757
  if (!m.project?.key) {
758
758
  return {
759
759
  ok: false,
760
- reason: "Kein `project.key` im Manifest"
760
+ reason: "No `project.key` in the manifest"
761
761
  };
762
762
  }
763
763
  if (!m.build?.manifestHash) {
764
764
  return {
765
765
  ok: false,
766
- reason: "manifestHash fehlt"
766
+ reason: "manifestHash is missing"
767
767
  };
768
768
  }
769
769
  return {
@@ -815,7 +815,7 @@ var ManifestCliFlow = class {
815
815
  id: check.id,
816
816
  label: check.label,
817
817
  severity: "error",
818
- message: `Check warf eine Exception: ${message}`
818
+ message: `The check threw an exception: ${message}`
819
819
  });
820
820
  overall = "error";
821
821
  }
@@ -831,7 +831,7 @@ var ManifestCliFlow = class {
831
831
  }
832
832
  formatReport(report) {
833
833
  const lines = [
834
- `Manifest-Check (Gesamtstatus: ${report.overall.toUpperCase()})`,
834
+ `Manifest check (overall status: ${report.overall.toUpperCase()})`,
835
835
  ""
836
836
  ];
837
837
  for (const c of report.checks) {
@@ -893,7 +893,7 @@ var PlanCatalogDoctorCheck = class {
893
893
  }
894
894
  catalog;
895
895
  id = "platform.plan-catalog";
896
- label = "Plan-Catalog im DI";
896
+ label = "Plan catalog in DI";
897
897
  constructor(catalog) {
898
898
  this.catalog = catalog;
899
899
  }
@@ -902,12 +902,12 @@ var PlanCatalogDoctorCheck = class {
902
902
  if (plans.length === 0) {
903
903
  return {
904
904
  severity: "error",
905
- message: "PlanCatalog enth\xE4lt keine Pl\xE4ne \u2014 Onboarding-Pricing-Page wird leer."
905
+ message: "The plan catalog contains no plans \u2014 the onboarding pricing page will be empty."
906
906
  };
907
907
  }
908
908
  return {
909
909
  severity: "ok",
910
- message: `${plans.length} Plan(s), ${this.catalog.features?.length ?? 0} Feature(s) geladen.`,
910
+ message: `${plans.length} plan(s), ${this.catalog.features?.length ?? 0} feature(s) loaded.`,
911
911
  details: {
912
912
  projectKey: this.catalog.projectKey,
913
913
  planIds: plans.map((p) => p.id)
@@ -929,7 +929,7 @@ var DiscoverySnapshotDoctorCheck = class {
929
929
  }
930
930
  snapshot;
931
931
  id = "platform.discovery-snapshot";
932
- label = "Discovery-Snapshot beim Boot";
932
+ label = "Discovery snapshot on boot";
933
933
  constructor(snapshot) {
934
934
  this.snapshot = snapshot;
935
935
  }
@@ -938,7 +938,7 @@ var DiscoverySnapshotDoctorCheck = class {
938
938
  if (caps.length === 0) {
939
939
  return {
940
940
  severity: "warning",
941
- message: "Keine Capabilities entdeckt \u2014 Decorator-tragende Module evtl. nicht in AppModule.imports[]."
941
+ message: "No capabilities discovered \u2014 decorator-carrying modules may be missing from AppModule.imports[]."
942
942
  };
943
943
  }
944
944
  return {
@@ -961,7 +961,7 @@ var UserPortDoctorCheck = class {
961
961
  }
962
962
  users;
963
963
  id = "platform.user-port";
964
- label = "UserPort.findByEmail erreichbar";
964
+ label = "UserPort.findByEmail reachable";
965
965
  constructor(users) {
966
966
  this.users = users;
967
967
  }
@@ -970,12 +970,12 @@ var UserPortDoctorCheck = class {
970
970
  await this.users.findByEmail("__doctor-check__@invalid.local");
971
971
  return {
972
972
  severity: "ok",
973
- message: "UserPort antwortet."
973
+ message: "UserPort responds."
974
974
  };
975
975
  } catch (err2) {
976
976
  return {
977
977
  severity: "error",
978
- message: `UserPort wirft: ${err2 instanceof Error ? err2.message : String(err2)}`
978
+ message: `UserPort throws: ${err2 instanceof Error ? err2.message : String(err2)}`
979
979
  };
980
980
  }
981
981
  }
@@ -994,7 +994,7 @@ var AdminManifestDoctorCheck = class {
994
994
  }
995
995
  manifest;
996
996
  id = "platform.admin-manifest";
997
- label = "AdminManifestService liefert Manifest";
997
+ label = "AdminManifestService returns a manifest";
998
998
  constructor(manifest) {
999
999
  this.manifest = manifest;
1000
1000
  }
@@ -1004,12 +1004,12 @@ var AdminManifestDoctorCheck = class {
1004
1004
  const pageCount = Object.keys(m.navigation?.standardPages ?? {}).length;
1005
1005
  return {
1006
1006
  severity: "ok",
1007
- message: `Manifest mit ${pageCount} Standard-Pages, Hash ${m.build?.manifestHash?.slice(0, 12) ?? "???"}\u2026`
1007
+ message: `Manifest with ${pageCount} standard pages, hash ${m.build?.manifestHash?.slice(0, 12) ?? "???"}\u2026`
1008
1008
  };
1009
1009
  } catch (err2) {
1010
1010
  return {
1011
1011
  severity: "error",
1012
- message: `Manifest-Build wirft: ${err2 instanceof Error ? err2.message : String(err2)}`
1012
+ message: `Manifest build throws: ${err2 instanceof Error ? err2.message : String(err2)}`
1013
1013
  };
1014
1014
  }
1015
1015
  }
@@ -1152,11 +1152,11 @@ function applyFragmentBlocks(schema, fragmentBlocks, options = {}) {
1152
1152
  const header = options.fragmentLabel ? `
1153
1153
 
1154
1154
  // ============================================================
1155
- // Eingef\xFCgt durch \`saasicat schema apply\` aus ${options.fragmentLabel}
1155
+ // Inserted by \`saasicat schema apply\` from ${options.fragmentLabel}
1156
1156
  // ============================================================
1157
1157
  ` : `
1158
1158
 
1159
- // Eingef\xFCgt durch \`saasicat schema apply\`
1159
+ // Inserted by \`saasicat schema apply\`
1160
1160
  `;
1161
1161
  const trimmedSchema = schema.endsWith("\n") ? schema : schema + "\n";
1162
1162
  return {
@@ -1470,7 +1470,7 @@ ManifestDumpCommand = _ts_decorate9([
1470
1470
  Injectable8(),
1471
1471
  SubCommand({
1472
1472
  name: "dump",
1473
- description: "Manifest als JSON ausgeben"
1473
+ description: "Print the manifest as JSON"
1474
1474
  }),
1475
1475
  _ts_metadata8("design:type", Function),
1476
1476
  _ts_metadata8("design:paramtypes", [
@@ -1510,7 +1510,7 @@ ManifestHashCommand = _ts_decorate9([
1510
1510
  Injectable8(),
1511
1511
  SubCommand({
1512
1512
  name: "hash",
1513
- description: "manifestHash ausgeben (CI-Pinning)"
1513
+ description: "Print the manifestHash (CI pinning)"
1514
1514
  }),
1515
1515
  _ts_metadata8("design:type", Function),
1516
1516
  _ts_metadata8("design:paramtypes", [
@@ -1532,7 +1532,7 @@ var ManifestValidateCommand = class extends CommandRunner {
1532
1532
  await this.ctx.ensureSuperAdmin(identity);
1533
1533
  const result = this.flow.validate();
1534
1534
  if (result.ok) {
1535
- process.stdout.write("Manifest validiert \u2713\n");
1535
+ process.stdout.write("Manifest is valid \u2713\n");
1536
1536
  return;
1537
1537
  }
1538
1538
  process.stderr.write(`Manifest invalid: ${result.reason}
@@ -1557,7 +1557,7 @@ ManifestValidateCommand = _ts_decorate9([
1557
1557
  Injectable8(),
1558
1558
  SubCommand({
1559
1559
  name: "validate",
1560
- description: "Schnell-Sanity (schemaVersion + project.key + manifestHash)"
1560
+ description: "Quick sanity check (schemaVersion + project.key + manifestHash)"
1561
1561
  }),
1562
1562
  _ts_metadata8("design:type", Function),
1563
1563
  _ts_metadata8("design:paramtypes", [
@@ -1600,7 +1600,7 @@ ManifestCheckCommand = _ts_decorate9([
1600
1600
  Injectable8(),
1601
1601
  SubCommand({
1602
1602
  name: "check",
1603
- description: "Alle Manifest-Checks (Exit-Code 7 bei error/Drift)"
1603
+ description: "All manifest checks (exit code 7 on error/drift)"
1604
1604
  }),
1605
1605
  _ts_metadata8("design:type", Function),
1606
1606
  _ts_metadata8("design:paramtypes", [
@@ -1613,7 +1613,7 @@ var ManifestCommands = class extends CommandRunner {
1613
1613
  __name(this, "ManifestCommands");
1614
1614
  }
1615
1615
  async run() {
1616
- process.stderr.write("Bitte Sub-Command angeben: dump, hash, validate, check.\n");
1616
+ process.stderr.write("Specify a sub-command: dump, hash, validate, check.\n");
1617
1617
  process.exit(2);
1618
1618
  }
1619
1619
  };
@@ -1621,7 +1621,7 @@ ManifestCommands = _ts_decorate9([
1621
1621
  Injectable8(),
1622
1622
  Command({
1623
1623
  name: "manifest",
1624
- description: "Manifest-Operations (dump, hash, validate, check)",
1624
+ description: "Manifest operations (dump, hash, validate, check)",
1625
1625
  subCommands: [
1626
1626
  ManifestDumpCommand,
1627
1627
  ManifestHashCommand,
@@ -1682,7 +1682,7 @@ AdminWhoamiCommand = _ts_decorate10([
1682
1682
  Injectable9(),
1683
1683
  SubCommand2({
1684
1684
  name: "whoami",
1685
- description: "Aktive CLI-Identit\xE4t + MFA-/Production-Status"
1685
+ description: "Active CLI identity plus MFA and production status"
1686
1686
  }),
1687
1687
  _ts_metadata9("design:type", Function),
1688
1688
  _ts_metadata9("design:paramtypes", [
@@ -1734,7 +1734,7 @@ _ts_decorate10([
1734
1734
  _ts_decorate10([
1735
1735
  Option2({
1736
1736
  flags: "--force",
1737
- description: "bestehendes Secret ohne R\xFCckfrage \xFCberschreiben"
1737
+ description: "overwrite an existing secret without asking"
1738
1738
  }),
1739
1739
  _ts_metadata9("design:type", Function),
1740
1740
  _ts_metadata9("design:paramtypes", []),
@@ -1744,7 +1744,7 @@ AdminMfaSetupCommand = _ts_decorate10([
1744
1744
  Injectable9(),
1745
1745
  SubCommand2({
1746
1746
  name: "mfa-setup",
1747
- description: "TOTP-MFA f\xFCr den eigenen SuperAdmin einrichten"
1747
+ description: "Set up TOTP MFA for your own super-admin account"
1748
1748
  }),
1749
1749
  _ts_param7(0, Inject7(CLI_CONTEXT_CONFIG_TOKEN)),
1750
1750
  _ts_metadata9("design:type", Function),
@@ -1758,7 +1758,7 @@ var AdminCommands = class extends CommandRunner2 {
1758
1758
  __name(this, "AdminCommands");
1759
1759
  }
1760
1760
  async run() {
1761
- process.stderr.write("Bitte Sub-Command angeben: whoami, mfa-setup.\n");
1761
+ process.stderr.write("Specify a sub-command: whoami, mfa-setup.\n");
1762
1762
  process.exit(2);
1763
1763
  }
1764
1764
  };
@@ -1892,7 +1892,7 @@ AuditTailCommand = _ts_decorate11([
1892
1892
  Injectable10(),
1893
1893
  SubCommand3({
1894
1894
  name: "tail",
1895
- description: "Letzte Audit-Log-Eintr\xE4ge (--actor/--action/--entity/--since/--limit)"
1895
+ description: "Most recent audit-log entries (--actor/--action/--entity/--since/--limit)"
1896
1896
  }),
1897
1897
  _ts_metadata10("design:type", Function),
1898
1898
  _ts_metadata10("design:paramtypes", [
@@ -1905,7 +1905,7 @@ var AuditCommands = class extends CommandRunner3 {
1905
1905
  __name(this, "AuditCommands");
1906
1906
  }
1907
1907
  async run() {
1908
- process.stderr.write("Bitte Sub-Command angeben: tail.\n");
1908
+ process.stderr.write("Specify a sub-command: tail.\n");
1909
1909
  process.exit(2);
1910
1910
  }
1911
1911
  };
@@ -1969,7 +1969,7 @@ DoctorCommands = _ts_decorate12([
1969
1969
  Injectable11(),
1970
1970
  Command4({
1971
1971
  name: "doctor",
1972
- description: "Health-/Drift-Checks (Exit-Code 4 bei error)"
1972
+ description: "Health/drift checks (exit code 4 on error)"
1973
1973
  }),
1974
1974
  _ts_metadata11("design:type", Function),
1975
1975
  _ts_metadata11("design:paramtypes", [
@@ -2012,7 +2012,7 @@ var DiscoveryScanCommand = class extends CommandRunner5 {
2012
2012
  }
2013
2013
  async run(_args, flags) {
2014
2014
  if (!this.scanner) {
2015
- this.fail("DiscoveryScanner nicht registriert \u2014 DiscoveryModule.forRoot() im CLI-Modul importieren.", flags);
2015
+ this.fail("DiscoveryScanner is not registered \u2014 import DiscoveryModule.forRoot() in the CLI module.", flags);
2016
2016
  return;
2017
2017
  }
2018
2018
  try {
@@ -2028,10 +2028,10 @@ var DiscoveryScanCommand = class extends CommandRunner5 {
2028
2028
  process.stdout.write(`Discovery-Scan (${snapshot.app.key} v${snapshot.app.version}): ${snapshot.capabilities.length} Capabilities \xB7 ${snapshot.features.length} Features \xB7 ${snapshot.quotas.length} Quotas \xB7 hash ${snapshot.hash.slice(0, 19)}\u2026
2029
2029
  `);
2030
2030
  if (target) {
2031
- process.stdout.write(`Snapshot persistiert: ${resolvePath(target)}
2031
+ process.stdout.write(`Snapshot persisted: ${resolvePath(target)}
2032
2032
  `);
2033
2033
  } else {
2034
- process.stderr.write("WARNUNG: Snapshot wurde nicht persistiert \u2014 weder snapshotPath (DiscoveryModule.forRoot) konfiguriert noch --out angegeben. Das Seed-Gate findet so keinen Snapshot.\n");
2034
+ process.stderr.write("WARNING: the snapshot was not persisted \u2014 neither snapshotPath (DiscoveryModule.forRoot) configured nor --out given. The seed gate will find no snapshot this way.\n");
2035
2035
  }
2036
2036
  } catch (err2) {
2037
2037
  this.fail(err2 instanceof Error ? err2.message : String(err2), flags);
@@ -2044,7 +2044,7 @@ var DiscoveryScanCommand = class extends CommandRunner5 {
2044
2044
  `);
2045
2045
  return;
2046
2046
  }
2047
- process.stderr.write(`[discovery scan] FEHLER: ${message}
2047
+ process.stderr.write(`[discovery scan] ERROR: ${message}
2048
2048
  `);
2049
2049
  process.exit(4);
2050
2050
  }
@@ -2058,7 +2058,7 @@ var DiscoveryScanCommand = class extends CommandRunner5 {
2058
2058
  _ts_decorate13([
2059
2059
  Option5({
2060
2060
  flags: "--out <path>",
2061
- description: "Snapshot zus\xE4tzlich an diesen Pfad schreiben"
2061
+ description: "Additionally write the snapshot to this path"
2062
2062
  }),
2063
2063
  _ts_metadata12("design:type", Function),
2064
2064
  _ts_metadata12("design:paramtypes", [
@@ -2069,7 +2069,7 @@ _ts_decorate13([
2069
2069
  _ts_decorate13([
2070
2070
  Option5({
2071
2071
  flags: "--non-fatal",
2072
- description: "Scan-Fehler nur als Warnung melden (Exit 0) \u2014 gestufter Rollout"
2072
+ description: "Report scan errors as a warning only (exit 0) \u2014 staged rollout"
2073
2073
  }),
2074
2074
  _ts_metadata12("design:type", Function),
2075
2075
  _ts_metadata12("design:paramtypes", []),
@@ -2079,7 +2079,7 @@ DiscoveryScanCommand = _ts_decorate13([
2079
2079
  Injectable12(),
2080
2080
  SubCommand4({
2081
2081
  name: "scan",
2082
- description: "Discovery-Snapshot headless erzeugen + persistieren (Seed-Gate, #23)"
2082
+ description: "Produce and persist a discovery snapshot headlessly (seed gate, #23)"
2083
2083
  }),
2084
2084
  _ts_param8(0, Optional()),
2085
2085
  _ts_param8(0, Inject8(DiscoveryScanner)),
@@ -2096,7 +2096,7 @@ var DiscoveryCommands = class extends CommandRunner5 {
2096
2096
  __name(this, "DiscoveryCommands");
2097
2097
  }
2098
2098
  async run() {
2099
- process.stderr.write("Bitte Sub-Command angeben: scan.\n");
2099
+ process.stderr.write("Specify a sub-command: scan.\n");
2100
2100
  process.exit(2);
2101
2101
  }
2102
2102
  };
@@ -2162,12 +2162,12 @@ var UserCommands = class extends CommandRunner6 {
2162
2162
  case "deactivate":
2163
2163
  return this.deactivate(args[1], flags, identity, me.id);
2164
2164
  default:
2165
- throw new CliError("UNKNOWN_SUBCOMMAND", `Unbekannter Subbefehl: user ${sub ?? "(leer)"}. Verf\xFCgbar: create-super-admin <email>, reassign-admin <slug>, list <slug>, reset-password <email>, deactivate <email>.`, 1);
2165
+ throw new CliError("UNKNOWN_SUBCOMMAND", `Unknown sub-command: user ${sub ?? "(empty)"}. Available: create-super-admin <email>, reassign-admin <slug>, list <slug>, reset-password <email>, deactivate <email>.`, 1);
2166
2166
  }
2167
2167
  }
2168
2168
  async createSuperAdmin(email, flags, identity, meId, meEmail) {
2169
2169
  if (!email) {
2170
- throw new CliError("MISSING_ARG", "user create-super-admin <email> erwartet eine E-Mail.", 1);
2170
+ throw new CliError("MISSING_ARG", "user create-super-admin <email> expects an email address.", 1);
2171
2171
  }
2172
2172
  await this.ctx.requireMfa(meId);
2173
2173
  await this.ctx.ensureProductionConfirmation({
@@ -2192,20 +2192,20 @@ var UserCommands = class extends CommandRunner6 {
2192
2192
  createdBy: meEmail
2193
2193
  }
2194
2194
  });
2195
- console.log(`\u2714 SUPER_ADMIN ${created.email} angelegt (durch ${meEmail}).`);
2195
+ console.log(`\u2714 SUPER_ADMIN ${created.email} created (by ${meEmail}).`);
2196
2196
  console.log(` User-ID: ${created.id}`);
2197
2197
  if (generated) {
2198
- console.log(` Passwort: ${password}`);
2199
- console.log(" \u2192 Sicher \xFCbermitteln. Beim ersten Login \xE4ndern.");
2198
+ console.log(` Password: ${password}`);
2199
+ console.log(" \u2192 Share it securely. Change it on first login.");
2200
2200
  }
2201
- console.log(` N\xE4chster Schritt: admin mfa-setup f\xFCr ${created.email}.`);
2201
+ console.log(` Next step: admin mfa-setup for ${created.email}.`);
2202
2202
  }
2203
2203
  async reassignAdmin(slug, flags, identity, meId) {
2204
2204
  if (!slug) {
2205
- throw new CliError("MISSING_ARG", "user reassign-admin <tenant-slug> erwartet einen Slug.", 1);
2205
+ throw new CliError("MISSING_ARG", "user reassign-admin <tenant-slug> expects a slug.", 1);
2206
2206
  }
2207
- if (!flags.to) throw new CliError("MISSING_FLAG", "--to=<email> ist Pflicht.", 1);
2208
- if (!flags.reason) throw new CliError("MISSING_FLAG", '--reason="\u2026" ist Pflicht.', 1);
2207
+ if (!flags.to) throw new CliError("MISSING_FLAG", "--to=<email> is required.", 1);
2208
+ if (!flags.reason) throw new CliError("MISSING_FLAG", '--reason="\u2026" is required.', 1);
2209
2209
  await this.ctx.requireMfa(meId);
2210
2210
  const result = await this.users.reassignTenantAdmin(slug, flags.to.toLowerCase());
2211
2211
  await this.ctx.log({
@@ -2223,18 +2223,18 @@ var UserCommands = class extends CommandRunner6 {
2223
2223
  }
2224
2224
  });
2225
2225
  if (result.created) {
2226
- console.log(`\u2714 Notfall-Admin ${result.user.email} f\xFCr ${slug} angelegt.`);
2226
+ console.log(`\u2714 Emergency admin ${result.user.email} created for ${slug}.`);
2227
2227
  if (result.oneTimePassword) {
2228
- console.log(` Initial-Passwort: ${result.oneTimePassword}`);
2229
- console.log(" \u2192 Sicher \xFCbermitteln. Beim ersten Login \xE4ndern.");
2228
+ console.log(` Initial password: ${result.oneTimePassword}`);
2229
+ console.log(" \u2192 Share it securely. Change it on first login.");
2230
2230
  }
2231
2231
  } else {
2232
- console.log(`\u2714 ${result.user.email} ist jetzt TENANT_ADMIN von ${slug}.`);
2232
+ console.log(`\u2714 ${result.user.email} is now TENANT_ADMIN of ${slug}.`);
2233
2233
  }
2234
2234
  }
2235
2235
  async list(slug) {
2236
2236
  if (!slug) {
2237
- throw new CliError("MISSING_ARG", "user list <tenant-slug> erwartet einen Slug.", 1);
2237
+ throw new CliError("MISSING_ARG", "user list <tenant-slug> expects a slug.", 1);
2238
2238
  }
2239
2239
  const rows = await this.users.listTenantUsers(slug);
2240
2240
  this.ctx.table(rows.map((u) => ({
@@ -2246,9 +2246,9 @@ var UserCommands = class extends CommandRunner6 {
2246
2246
  }
2247
2247
  async resetPassword(email, flags, identity, meId) {
2248
2248
  if (!email) {
2249
- throw new CliError("MISSING_ARG", "user reset-password <email> erwartet eine E-Mail.", 1);
2249
+ throw new CliError("MISSING_ARG", "user reset-password <email> expects an email address.", 1);
2250
2250
  }
2251
- if (!flags.reason) throw new CliError("MISSING_FLAG", '--reason="\u2026" ist Pflicht.', 1);
2251
+ if (!flags.reason) throw new CliError("MISSING_FLAG", '--reason="\u2026" is required.', 1);
2252
2252
  const result = await this.users.triggerPasswordReset(email.toLowerCase());
2253
2253
  await this.ctx.log({
2254
2254
  identity,
@@ -2261,18 +2261,18 @@ var UserCommands = class extends CommandRunner6 {
2261
2261
  }
2262
2262
  });
2263
2263
  if (result.oneTimePassword) {
2264
- console.log(`\u2714 Einmal-Passwort f\xFCr ${result.user.email} gesetzt.`);
2265
- console.log(` Passwort: ${result.oneTimePassword}`);
2266
- console.log(" \u2192 Sicher \xFCbermitteln. Beim ersten Login \xE4ndern.");
2264
+ console.log(`\u2714 One-time password set for ${result.user.email}.`);
2265
+ console.log(` Password: ${result.oneTimePassword}`);
2266
+ console.log(" \u2192 Share it securely. Change it on first login.");
2267
2267
  } else {
2268
- console.log(`\u2714 Passwort-Reset f\xFCr ${result.user.email} ausgel\xF6st.`);
2268
+ console.log(`\u2714 Password reset triggered for ${result.user.email}.`);
2269
2269
  }
2270
2270
  }
2271
2271
  async deactivate(email, flags, identity, meId) {
2272
2272
  if (!email) {
2273
- throw new CliError("MISSING_ARG", "user deactivate <email> erwartet eine E-Mail.", 1);
2273
+ throw new CliError("MISSING_ARG", "user deactivate <email> expects an email address.", 1);
2274
2274
  }
2275
- if (!flags.reason) throw new CliError("MISSING_FLAG", '--reason="\u2026" ist Pflicht.', 1);
2275
+ if (!flags.reason) throw new CliError("MISSING_FLAG", '--reason="\u2026" is required.', 1);
2276
2276
  await this.ctx.requireMfa(meId);
2277
2277
  await this.ctx.ensureProductionConfirmation({
2278
2278
  yes: flags.yes
@@ -2289,7 +2289,7 @@ var UserCommands = class extends CommandRunner6 {
2289
2289
  emergency: true
2290
2290
  }
2291
2291
  });
2292
- console.log(`\u2714 ${user.email} deaktiviert.`);
2292
+ console.log(`\u2714 ${user.email} deactivated.`);
2293
2293
  }
2294
2294
  parseAs(val) {
2295
2295
  return val;
@@ -2316,7 +2316,7 @@ var UserCommands = class extends CommandRunner6 {
2316
2316
  _ts_decorate14([
2317
2317
  Option6({
2318
2318
  flags: "--as <email>",
2319
- description: "CLI-Identit\xE4t (sonst <APP>_ADMIN_EMAIL)"
2319
+ description: "CLI identity (otherwise <APP>_ADMIN_EMAIL)"
2320
2320
  }),
2321
2321
  _ts_metadata13("design:type", Function),
2322
2322
  _ts_metadata13("design:paramtypes", [
@@ -2327,7 +2327,7 @@ _ts_decorate14([
2327
2327
  _ts_decorate14([
2328
2328
  Option6({
2329
2329
  flags: "--to <email>",
2330
- description: "Ziel-User (reassign-admin)"
2330
+ description: "Target user (reassign-admin)"
2331
2331
  }),
2332
2332
  _ts_metadata13("design:type", Function),
2333
2333
  _ts_metadata13("design:paramtypes", [
@@ -2338,7 +2338,7 @@ _ts_decorate14([
2338
2338
  _ts_decorate14([
2339
2339
  Option6({
2340
2340
  flags: "--reason <text>",
2341
- description: "Begr\xFCndung (Audit)"
2341
+ description: "Reason (audit)"
2342
2342
  }),
2343
2343
  _ts_metadata13("design:type", Function),
2344
2344
  _ts_metadata13("design:paramtypes", [
@@ -2349,7 +2349,7 @@ _ts_decorate14([
2349
2349
  _ts_decorate14([
2350
2350
  Option6({
2351
2351
  flags: "-y, --yes",
2352
- description: "Production-Confirmation \xFCberspringen"
2352
+ description: "Skip the production confirmation"
2353
2353
  }),
2354
2354
  _ts_metadata13("design:type", Function),
2355
2355
  _ts_metadata13("design:paramtypes", []),
@@ -2358,7 +2358,7 @@ _ts_decorate14([
2358
2358
  _ts_decorate14([
2359
2359
  Option6({
2360
2360
  flags: "--first <name>",
2361
- description: "Vorname (create-super-admin)"
2361
+ description: "First name (create-super-admin)"
2362
2362
  }),
2363
2363
  _ts_metadata13("design:type", Function),
2364
2364
  _ts_metadata13("design:paramtypes", [
@@ -2369,7 +2369,7 @@ _ts_decorate14([
2369
2369
  _ts_decorate14([
2370
2370
  Option6({
2371
2371
  flags: "--last <name>",
2372
- description: "Nachname (create-super-admin)"
2372
+ description: "Last name (create-super-admin)"
2373
2373
  }),
2374
2374
  _ts_metadata13("design:type", Function),
2375
2375
  _ts_metadata13("design:paramtypes", [
@@ -2380,7 +2380,7 @@ _ts_decorate14([
2380
2380
  _ts_decorate14([
2381
2381
  Option6({
2382
2382
  flags: "--password <pwd>",
2383
- description: "Passwort (create-super-admin; ohne Angabe generiert)"
2383
+ description: "Password (create-super-admin; generated when omitted)"
2384
2384
  }),
2385
2385
  _ts_metadata13("design:type", Function),
2386
2386
  _ts_metadata13("design:paramtypes", [
@@ -2392,7 +2392,7 @@ UserCommands = _ts_decorate14([
2392
2392
  Injectable13(),
2393
2393
  Command6({
2394
2394
  name: "user",
2395
- description: "User-Operationen (create-super-admin, reassign-admin, list, reset-password, deactivate)"
2395
+ description: "User operations (create-super-admin, reassign-admin, list, reset-password, deactivate)"
2396
2396
  }),
2397
2397
  _ts_param9(1, Inject9(USER_MANAGEMENT_PORT_TOKEN)),
2398
2398
  _ts_metadata13("design:type", Function),