@flui-cloud/cli 0.0.1 → 0.2.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.
Files changed (108) hide show
  1. package/lib/cli/src/commands/app/list.d.ts +3 -0
  2. package/lib/cli/src/commands/app/list.js +72 -18
  3. package/lib/cli/src/commands/app/status.d.ts +1 -0
  4. package/lib/cli/src/commands/app/status.js +27 -2
  5. package/lib/cli/src/commands/cluster/destroy.d.ts +1 -1
  6. package/lib/cli/src/commands/cluster/destroy.js +2 -2
  7. package/lib/cli/src/commands/deploy.d.ts +3 -0
  8. package/lib/cli/src/commands/deploy.js +19 -0
  9. package/lib/cli/src/commands/dev/creds.d.ts +0 -1
  10. package/lib/cli/src/commands/dev/creds.js +6 -27
  11. package/lib/cli/src/commands/dev/tunnel.js +8 -8
  12. package/lib/cli/src/commands/env/capacity.js +4 -4
  13. package/lib/cli/src/commands/env/create.d.ts +4 -1
  14. package/lib/cli/src/commands/env/create.js +78 -52
  15. package/lib/cli/src/commands/env/credentials.js +12 -12
  16. package/lib/cli/src/commands/env/destroy.d.ts +2 -1
  17. package/lib/cli/src/commands/env/destroy.js +45 -28
  18. package/lib/cli/src/commands/env/diag-ca.js +5 -5
  19. package/lib/cli/src/commands/env/export-config.d.ts +0 -17
  20. package/lib/cli/src/commands/env/export-config.js +50 -47
  21. package/lib/cli/src/commands/env/force-ready.d.ts +1 -1
  22. package/lib/cli/src/commands/env/force-ready.js +8 -8
  23. package/lib/cli/src/commands/env/inspect.js +5 -5
  24. package/lib/cli/src/commands/env/refresh-kubeconfig.js +4 -4
  25. package/lib/cli/src/commands/env/repair-ssh-ca.js +4 -4
  26. package/lib/cli/src/commands/env/repair-storage.d.ts +9 -0
  27. package/lib/cli/src/commands/env/repair-storage.js +82 -0
  28. package/lib/cli/src/commands/env/restart.d.ts +1 -1
  29. package/lib/cli/src/commands/env/restart.js +9 -9
  30. package/lib/cli/src/commands/env/scale-master.js +4 -4
  31. package/lib/cli/src/commands/env/scale-node.js +4 -4
  32. package/lib/cli/src/commands/env/set-master-protection.d.ts +16 -0
  33. package/lib/cli/src/commands/env/set-master-protection.js +120 -0
  34. package/lib/cli/src/commands/env/status.d.ts +1 -1
  35. package/lib/cli/src/commands/env/status.js +10 -10
  36. package/lib/cli/src/commands/env/stop.d.ts +1 -1
  37. package/lib/cli/src/commands/env/stop.js +8 -8
  38. package/lib/cli/src/commands/env/storage-expand.js +4 -4
  39. package/lib/cli/src/commands/env/storage.d.ts +1 -1
  40. package/lib/cli/src/commands/env/storage.js +5 -5
  41. package/lib/cli/src/commands/env/sync.js +5 -5
  42. package/lib/cli/src/commands/env/uncordon.js +4 -4
  43. package/lib/cli/src/commands/env/update-firewall.d.ts +13 -1
  44. package/lib/cli/src/commands/env/update-firewall.js +232 -126
  45. package/lib/cli/src/commands/integration/connect.d.ts +1 -0
  46. package/lib/cli/src/commands/integration/connect.js +19 -1
  47. package/lib/cli/src/commands/integration/reset.d.ts +13 -0
  48. package/lib/cli/src/commands/integration/reset.js +95 -0
  49. package/lib/cli/src/commands/integration/setup.d.ts +18 -0
  50. package/lib/cli/src/commands/integration/setup.js +320 -0
  51. package/lib/cli/src/commands/integration/status.d.ts +9 -0
  52. package/lib/cli/src/commands/integration/status.js +117 -0
  53. package/lib/cli/src/commands/node/list.d.ts +1 -0
  54. package/lib/cli/src/commands/node/list.js +19 -2
  55. package/lib/cli/src/commands/server-types/list.d.ts +3 -0
  56. package/lib/cli/src/commands/server-types/list.js +84 -0
  57. package/lib/cli/src/commands/ssh.js +5 -5
  58. package/lib/cli/src/commands/version.d.ts +18 -0
  59. package/lib/cli/src/commands/version.js +100 -0
  60. package/lib/cli/src/config/bootstrap.config.d.ts +10 -1
  61. package/lib/cli/src/config/bootstrap.config.js +24 -4
  62. package/lib/cli/src/config/preferences-schema.js +5 -5
  63. package/lib/cli/src/config/release-override.d.ts +43 -0
  64. package/lib/cli/src/config/release-override.js +203 -0
  65. package/lib/cli/src/config/release.config.d.ts +31 -0
  66. package/lib/cli/src/config/release.config.js +38 -0
  67. package/lib/cli/src/lib/prompts.d.ts +1 -6
  68. package/lib/cli/src/lib/prompts.js +33 -13
  69. package/lib/cli/src/lib/services/cli-app.service.d.ts +33 -0
  70. package/lib/cli/src/lib/services/cli-app.service.js +9 -0
  71. package/lib/cli/src/lib/services/reconciliation.service.js +1 -1
  72. package/lib/cli/src/lib/templates/firewall-rules.d.ts +2 -2
  73. package/lib/cli/src/lib/templates/firewall-rules.js +3 -3
  74. package/lib/cli/src/modules/cli-infrastructure.module.js +3 -3
  75. package/lib/cli/src/services/cli-cluster-creator.service.js +31 -6
  76. package/lib/cli/src/services/cli-clusters.service.d.ts +3 -3
  77. package/lib/cli/src/services/cli-clusters.service.js +57 -34
  78. package/lib/cli/src/services/cli-control-cluster.service.d.ts +129 -0
  79. package/lib/cli/src/services/cli-control-cluster.service.js +545 -0
  80. package/lib/cli/src/services/cli-endpoint-resolver.service.d.ts +1 -0
  81. package/lib/cli/src/services/cli-endpoint-resolver.service.js +25 -11
  82. package/lib/cli/src/services/cli-k3s-script.service.d.ts +8 -1
  83. package/lib/cli/src/services/cli-k3s-script.service.js +14 -6
  84. package/lib/src/config/release.config.d.ts +28 -0
  85. package/lib/src/config/release.config.js +35 -0
  86. package/lib/src/modules/applications/entities/application.entity.d.ts +13 -20
  87. package/lib/src/modules/applications/entities/application.entity.js +12 -0
  88. package/lib/src/modules/applications/enums/application-exposure.enum.d.ts +2 -1
  89. package/lib/src/modules/applications/enums/application-exposure.enum.js +1 -0
  90. package/lib/src/modules/applications/interfaces/source-config.interface.d.ts +1 -0
  91. package/lib/src/modules/infrastructure/clusters/entities/cluster.entity.d.ts +8 -2
  92. package/lib/src/modules/infrastructure/clusters/entities/cluster.entity.js +16 -1
  93. package/lib/src/modules/infrastructure/clusters/services/cluster-node-scaling.service.js +2 -2
  94. package/lib/src/modules/infrastructure/firewalls/templates/firewall-rules.template.d.ts +3 -2
  95. package/lib/src/modules/infrastructure/firewalls/templates/firewall-rules.template.js +11 -4
  96. package/lib/src/modules/infrastructure/shared/services/kubernetes.service.d.ts +26 -0
  97. package/lib/src/modules/infrastructure/shared/services/kubernetes.service.js +105 -8
  98. package/lib/src/modules/management/entities/provider-capabilities.entity.d.ts +2 -0
  99. package/lib/src/modules/providers/implementations/contabo/contabo-capabilities.service.js +2 -0
  100. package/lib/src/modules/providers/implementations/hetzner/hetzner-capabilities.service.js +3 -6
  101. package/lib/src/modules/providers/implementations/scaleway/scaleway-capabilities.service.js +2 -1
  102. package/lib/src/modules/providers/implementations/scaleway/scaleway-firewall.service.js +3 -1
  103. package/lib/src/modules/providers/implementations/scaleway/scaleway-provider.service.js +3 -1
  104. package/lib/src/modules/providers/interfaces/provider-capabilities.interface.d.ts +0 -2
  105. package/lib/src/modules/providers/services/hetzner-firewall.service.d.ts +1 -1
  106. package/lib/src/modules/providers/services/hetzner-firewall.service.js +2 -1
  107. package/oclif.manifest.json +1201 -854
  108. package/package.json +2 -2
@@ -4,8 +4,11 @@ export default class AppList extends Command {
4
4
  static readonly examples: string[];
5
5
  static readonly flags: {
6
6
  cluster: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ expanded: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
8
  output: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
9
  };
9
10
  run(): Promise<void>;
11
+ private printGroup;
12
+ private printRow;
10
13
  private colorStatus;
11
14
  }
@@ -15,34 +15,30 @@ class AppList extends core_1.Command {
15
15
  try {
16
16
  const { id: clusterId } = await (0, resolve_cluster_1.resolveCluster)(flags.cluster);
17
17
  const service = await cli_app_service_1.CliAppService.create(clusterId);
18
- const apps = await service.listApps();
18
+ const groups = await service.listAppGroups();
19
19
  spinner.stop();
20
20
  if (flags.output === 'json') {
21
- console.log(JSON.stringify(apps, null, 2));
21
+ console.log(JSON.stringify(groups, null, 2));
22
22
  return;
23
23
  }
24
- if (apps.length === 0) {
24
+ if (groups.length === 0) {
25
25
  console.log(chalk_1.default.yellow('\n No applications found in this cluster.\n'));
26
26
  return;
27
27
  }
28
28
  console.log(chalk_1.default.cyan('\n Applications\n'));
29
- console.log(chalk_1.default.dim(` ${'NAME'.padEnd(28)} ${'STATUS'.padEnd(14)} ${'REPLICAS'.padEnd(10)} ${'KIND'.padEnd(14)} ${'EXPOSURE'.padEnd(10)} LAST DEPLOY`));
30
- console.log(chalk_1.default.dim(' ' + '─'.repeat(90)));
31
- for (const app of apps) {
32
- const name = app.name.length > 26 ? app.name.slice(0, 25) + '…' : app.name;
33
- const kind = (app.kind || '').toLowerCase();
34
- const exposure = (app.exposure || '').toLowerCase();
35
- const replicas = String(app.replicas ?? '-');
36
- const lastDeploy = app.lastDeployedAt
37
- ? new Date(app.lastDeployedAt).toLocaleString()
38
- : chalk_1.default.dim('never');
39
- // Pad before coloring so escape codes don't break column widths
40
- const statusPadded = app.status.padEnd(14);
41
- const coloredStatus = this.colorStatus(statusPadded);
42
- console.log(` ${name.padEnd(28)} ${coloredStatus} ${replicas.padEnd(10)} ${kind.padEnd(14)} ${exposure.padEnd(10)} ${lastDeploy}`);
29
+ console.log(chalk_1.default.dim(` ${'NAME'.padEnd(30)} ${'STATUS'.padEnd(14)} ${'REPLICAS'.padEnd(10)} ${'KIND'.padEnd(12)} ${'EXPOSURE'.padEnd(10)} LAST DEPLOY`));
30
+ console.log(chalk_1.default.dim(' ' + '─'.repeat(92)));
31
+ for (const group of groups) {
32
+ this.printGroup(group, flags.expanded);
43
33
  }
34
+ const appCount = groups.reduce((n, g) => n + g.componentCount, 0);
35
+ const composed = groups.filter((g) => g.type === 'composed').length;
44
36
  console.log('');
45
- console.log(chalk_1.default.dim(` ${apps.length} app${apps.length === 1 ? '' : 's'} total`));
37
+ console.log(chalk_1.default.dim(` ${groups.length} app${groups.length === 1 ? '' : 's'}` +
38
+ (composed > 0 ? ` (${appCount} components)` : '') +
39
+ (composed > 0 && !flags.expanded
40
+ ? chalk_1.default.dim(' · use --expanded to list components')
41
+ : '')));
46
42
  console.log('');
47
43
  }
48
44
  catch (error) {
@@ -51,6 +47,58 @@ class AppList extends core_1.Command {
51
47
  this.exit(1);
52
48
  }
53
49
  }
50
+ printGroup(group, expanded) {
51
+ if (group.type === 'standalone') {
52
+ const app = group.components[0];
53
+ this.printRow({
54
+ name: group.name,
55
+ status: group.status,
56
+ replicas: String(app?.replicas ?? '-'),
57
+ kind: (app?.kind || '').toLowerCase(),
58
+ exposure: (app?.exposure || '').toLowerCase(),
59
+ lastDeploy: app?.lastDeployedAt,
60
+ });
61
+ return;
62
+ }
63
+ const marker = expanded ? '▾' : '▸';
64
+ const primary = group.components.find((c) => c.isPrimary);
65
+ this.printRow({
66
+ name: `${marker} ${group.name} (${group.componentCount})`,
67
+ status: group.status,
68
+ replicas: '-',
69
+ kind: 'composed',
70
+ exposure: (primary?.exposure || '').toLowerCase(),
71
+ lastDeploy: undefined,
72
+ });
73
+ if (!expanded)
74
+ return;
75
+ const children = [...group.components].sort((a, b) => Number(b.isPrimary ?? false) - Number(a.isPrimary ?? false));
76
+ children.forEach((c, i) => {
77
+ const branch = i === children.length - 1 ? '└' : '├';
78
+ const short = c.slug.startsWith(`${group.slug}-`)
79
+ ? c.slug.slice(group.slug.length + 1)
80
+ : c.slug;
81
+ this.printRow({
82
+ name: ` ${branch} ${short}${c.isPrimary ? chalk_1.default.dim(' (primary)') : ''}`,
83
+ status: c.status,
84
+ replicas: String(c.replicas ?? '-'),
85
+ kind: (c.kind || '').toLowerCase(),
86
+ exposure: (c.exposure || '').toLowerCase(),
87
+ lastDeploy: c.lastDeployedAt,
88
+ dim: true,
89
+ });
90
+ });
91
+ }
92
+ printRow(r) {
93
+ const visibleLen = r.name.replace(/\x1b\[[0-9;]*m/g, '').length;
94
+ const namePad = r.name + ' '.repeat(Math.max(0, 30 - visibleLen));
95
+ const status = this.colorStatus(r.status.padEnd(14));
96
+ const last = r.lastDeploy
97
+ ? new Date(r.lastDeploy).toLocaleString()
98
+ : chalk_1.default.dim('—');
99
+ const line = ` ${namePad} ${status} ${r.replicas.padEnd(10)} ${r.kind.padEnd(12)} ${r.exposure.padEnd(10)} ${last}`;
100
+ console.log(r.dim ? chalk_1.default.dim(line) : line);
101
+ }
54
102
  colorStatus(status) {
55
103
  const s = status.trim().toLowerCase();
56
104
  if (s === 'running')
@@ -67,6 +115,7 @@ class AppList extends core_1.Command {
67
115
  AppList.description = 'List all applications in the cluster';
68
116
  AppList.examples = [
69
117
  '<%= config.bin %> <%= command.id %>',
118
+ '<%= config.bin %> <%= command.id %> --expanded',
70
119
  '<%= config.bin %> <%= command.id %> --output json',
71
120
  ];
72
121
  AppList.flags = {
@@ -74,6 +123,11 @@ AppList.flags = {
74
123
  char: 'c',
75
124
  description: 'Cluster name or ID (default: auto-detect when only one cluster exists)',
76
125
  }),
126
+ expanded: core_1.Flags.boolean({
127
+ char: 'x',
128
+ description: 'Show the individual components of composed apps',
129
+ default: false,
130
+ }),
77
131
  output: core_1.Flags.string({
78
132
  char: 'o',
79
133
  description: 'Output format',
@@ -10,6 +10,7 @@ export default class AppStatus extends Command {
10
10
  output: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
11
11
  };
12
12
  run(): Promise<void>;
13
+ private printEndpoint;
13
14
  private printContainer;
14
15
  private colorStatus;
15
16
  }
@@ -16,10 +16,13 @@ class AppStatus extends core_1.Command {
16
16
  const { id: clusterId } = await (0, resolve_cluster_1.resolveCluster)(flags.cluster);
17
17
  const service = await cli_app_service_1.CliAppService.create(clusterId);
18
18
  const app = await service.getAppByName(args.name);
19
- const runtime = await service.getRuntime(app.id);
19
+ const [runtime, endpoints] = await Promise.all([
20
+ service.getRuntime(app.id),
21
+ service.listEndpoints(app.id).catch(() => []),
22
+ ]);
20
23
  spinner.stop();
21
24
  if (flags.output === 'json') {
22
- console.log(JSON.stringify({ app, runtime }, null, 2));
25
+ console.log(JSON.stringify({ app, runtime, endpoints }, null, 2));
23
26
  return;
24
27
  }
25
28
  console.log(chalk_1.default.cyan(`\n ${app.name}\n`));
@@ -45,6 +48,11 @@ class AppStatus extends core_1.Command {
45
48
  for (const c of runtime.containers)
46
49
  this.printContainer(c);
47
50
  }
51
+ if (endpoints.length > 0) {
52
+ console.log(chalk_1.default.cyan('\n Endpoints\n'));
53
+ for (const e of endpoints)
54
+ this.printEndpoint(e);
55
+ }
48
56
  console.log('');
49
57
  }
50
58
  catch (error) {
@@ -53,6 +61,23 @@ class AppStatus extends core_1.Command {
53
61
  this.exit(1);
54
62
  }
55
63
  }
64
+ printEndpoint(e) {
65
+ const scheme = e.tlsEnabled ? 'https' : 'http';
66
+ const url = `${scheme}://${e.fqdn}`;
67
+ console.log(` ${chalk_1.default.bold('URL:')} ${chalk_1.default.cyan(url)}`);
68
+ const meta = [
69
+ e.endpointType,
70
+ e.hostnameMode,
71
+ e.tlsEnabled ? 'tls' : 'no-tls',
72
+ ]
73
+ .filter(Boolean)
74
+ .join(' · ');
75
+ console.log(` ${chalk_1.default.dim(meta)}`);
76
+ if (e.certificateStatus && e.certificateStatus !== 'ISSUED') {
77
+ const color = e.certificateStatus === 'FAILED' ? chalk_1.default.red : chalk_1.default.yellow;
78
+ console.log(` ${chalk_1.default.bold('Cert:')} ${color(e.certificateStatus)}${e.certificateMessage ? ` — ${e.certificateMessage}` : ''}`);
79
+ }
80
+ }
56
81
  printContainer(c) {
57
82
  console.log(` ${chalk_1.default.bold(c.name)}`);
58
83
  console.log(` ${chalk_1.default.dim('image:')} ${c.image}`);
@@ -1,6 +1,6 @@
1
1
  import { Command } from '@oclif/core';
2
2
  export default class ClusterDestroy extends Command {
3
- static readonly description = "Permanently destroy a workload cluster and all its nodes. For the observability cluster use `flui env destroy`.";
3
+ static readonly description = "Permanently destroy a workload cluster and all its nodes. For the control cluster use `flui env destroy`.";
4
4
  static readonly examples: string[];
5
5
  static readonly args: {
6
6
  cluster: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
@@ -30,7 +30,7 @@ class ClusterDestroy extends core_1.Command {
30
30
  }
31
31
  const { id: clusterId, name: clusterName, entity } = resolved;
32
32
  if (entity.metadata?.isObservabilityCluster) {
33
- this.error(`"${clusterName}" is the observability cluster. Use \`flui env destroy\` instead.`, { exit: 1 });
33
+ this.error(`"${clusterName}" is the control cluster. Use \`flui env destroy\` instead.`, { exit: 1 });
34
34
  }
35
35
  console.log(chalk_1.default.red('\n⚠️ DESTROY Workload Cluster\n'));
36
36
  console.log(` ${chalk_1.default.bold('Name:')} ${clusterName}`);
@@ -111,7 +111,7 @@ class ClusterDestroy extends core_1.Command {
111
111
  return false;
112
112
  }
113
113
  }
114
- ClusterDestroy.description = 'Permanently destroy a workload cluster and all its nodes. For the observability cluster use `flui env destroy`.';
114
+ ClusterDestroy.description = 'Permanently destroy a workload cluster and all its nodes. For the control cluster use `flui env destroy`.';
115
115
  ClusterDestroy.examples = [
116
116
  '<%= config.bin %> <%= command.id %> my-workload-cluster',
117
117
  '<%= config.bin %> <%= command.id %> my-workload-cluster --force',
@@ -16,6 +16,9 @@ export default class Deploy extends Command {
16
16
  'no-build': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
17
17
  image: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
18
18
  'skip-endpoint': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
19
+ 'cert-challenge': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
20
+ 'cert-provider': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
21
+ hostname: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
19
22
  'no-wait': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
23
  'validate-only': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
24
  'skip-checks': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -258,6 +258,13 @@ class Deploy extends core_1.Command {
258
258
  clusterId,
259
259
  ...(flags.name ? { displayName: flags.name } : {}),
260
260
  ...(flags.domain ? { domain: flags.domain } : {}),
261
+ ...(flags['cert-challenge']
262
+ ? { certChallenge: flags['cert-challenge'] }
263
+ : {}),
264
+ ...(flags['cert-provider']
265
+ ? { certificateProvider: flags['cert-provider'] }
266
+ : {}),
267
+ ...(flags.hostname ? { hostnameMode: flags.hostname } : {}),
261
268
  ...(flags['skip-endpoint'] ? { skipEndpoint: true } : {}),
262
269
  ...(Object.keys(envOverrides).length > 0
263
270
  ? { envOverrides, userInputs: envOverrides }
@@ -525,6 +532,18 @@ Deploy.flags = {
525
532
  description: 'Skip DNS and TLS provisioning (kind:CatalogApp only)',
526
533
  default: false,
527
534
  }),
535
+ 'cert-challenge': core_1.Flags.string({
536
+ description: 'ACME challenge for the app endpoint (kind:CatalogApp). http-01 works without a DNS zone and forces a per-host cert; dns-01 needs a cluster DNS zone with a wildcard issuer. Default: derived from cluster config.',
537
+ options: ['http-01', 'dns-01'],
538
+ }),
539
+ 'cert-provider': core_1.Flags.string({
540
+ description: 'Certificate issuer for the app endpoint (kind:CatalogApp). Default: cluster default.',
541
+ options: ['lets-encrypt', 'lets-encrypt-staging'],
542
+ }),
543
+ hostname: core_1.Flags.string({
544
+ description: 'How the app is exposed (kind:CatalogApp): ip (nip.io) or domain (cluster DNS zone). Default: derived from manifest/cluster.',
545
+ options: ['ip', 'domain'],
546
+ }),
528
547
  'no-wait': core_1.Flags.boolean({
529
548
  description: 'Alias for --detach (kind:CatalogApp compat)',
530
549
  default: false,
@@ -13,7 +13,6 @@ export default class DevCreds extends Command {
13
13
  private buildEnvVars;
14
14
  private writeEnvFile;
15
15
  private readFluiSecrets;
16
- private readOidcIssuer;
17
16
  private resolveApiPath;
18
17
  private printSummary;
19
18
  }
@@ -43,7 +43,7 @@ const fs = __importStar(require("node:fs"));
43
43
  const path = __importStar(require("node:path"));
44
44
  const os = __importStar(require("node:os"));
45
45
  const nest_app_1 = require("../../lib/nest-app");
46
- const cli_observability_cluster_service_1 = require("../../services/cli-observability-cluster.service");
46
+ const cli_control_cluster_service_1 = require("../../services/cli-control-cluster.service");
47
47
  const cli_ssh_service_1 = require("../../services/cli-ssh.service");
48
48
  const config_storage_1 = require("../../lib/config-storage");
49
49
  const encryption_service_1 = require("../../../../src/modules/shared/encryption/services/encryption.service");
@@ -58,12 +58,12 @@ class DevCreds extends core_1.Command {
58
58
  let spinner = (0, ora_1.default)('Reading cluster configuration...').start();
59
59
  try {
60
60
  const app = await (0, nest_app_1.getNestApp)();
61
- const observabilityService = app.get(cli_observability_cluster_service_1.CliObservabilityClusterService);
61
+ const controlService = app.get(cli_control_cluster_service_1.CliControlClusterService);
62
62
  const encryptionService = app.get(encryption_service_1.EncryptionService);
63
63
  const sshService = app.get(cli_ssh_service_1.CliSshService);
64
- const cluster = await observabilityService.getObservabilityCluster();
64
+ const cluster = await controlService.getControlCluster();
65
65
  if (!cluster) {
66
- spinner.fail('No observability cluster found');
66
+ spinner.fail('No control cluster found');
67
67
  return;
68
68
  }
69
69
  if (cluster.status !== cluster_entity_1.ClusterStatus.READY) {
@@ -89,14 +89,6 @@ class DevCreds extends core_1.Command {
89
89
  spinner = (0, ora_1.default)('Reading flui-secrets via SSH...').start();
90
90
  const fluiSecrets = await this.readFluiSecrets(sshService, masterIp);
91
91
  spinner.succeed('flui-secrets read');
92
- // OIDC issuer from flui-api-config — used to point local API at the
93
- // public Zitadel admin URL instead of the in-cluster service DNS.
94
- spinner = (0, ora_1.default)('Reading OIDC issuer from flui-api-config...').start();
95
- const oidcIssuer = await this.readOidcIssuer(sshService, masterIp);
96
- if (oidcIssuer)
97
- spinner.succeed(`OIDC issuer: ${oidcIssuer}`);
98
- else
99
- spinner.warn('OIDC issuer not found in flui-api-config');
100
92
  // Local encryption key — shared with API via ~/.flui/encryption.key.
101
93
  const encryptionKeyPath = path.join(os.homedir(), '.flui', 'encryption.key');
102
94
  const encryptionKey = fs.existsSync(encryptionKeyPath)
@@ -111,7 +103,6 @@ class DevCreds extends core_1.Command {
111
103
  passwords,
112
104
  encryptionKey,
113
105
  fluiSecrets,
114
- oidcIssuer,
115
106
  });
116
107
  this.printSummary(envLocalPath, envVars);
117
108
  if (fluiSecrets.fluiApiKey) {
@@ -160,7 +151,7 @@ class DevCreds extends core_1.Command {
160
151
  return null;
161
152
  }
162
153
  buildEnvVars(opts) {
163
- const { passwords, encryptionKey, fluiSecrets, oidcIssuer } = opts;
154
+ const { passwords, encryptionKey, fluiSecrets } = opts;
164
155
  const envVars = {
165
156
  DB_PASSWORD: passwords.postgres,
166
157
  REDIS_PASSWORD: passwords.redis,
@@ -180,8 +171,6 @@ class DevCreds extends core_1.Command {
180
171
  envVars.SSH_CA_PUBLIC_KEY = fluiSecrets.sshCaPublicKey;
181
172
  if (fluiSecrets.zitadelPat)
182
173
  envVars.ZITADEL_SERVICE_ACCOUNT_PAT = fluiSecrets.zitadelPat;
183
- if (oidcIssuer)
184
- envVars.OIDC_PROVIDER_ADMIN_URL = oidcIssuer;
185
174
  return envVars;
186
175
  }
187
176
  writeEnvFile(envLocalPath, apiDir, envVars, backup) {
@@ -228,16 +217,6 @@ class DevCreds extends core_1.Command {
228
217
  return {};
229
218
  }
230
219
  }
231
- async readOidcIssuer(sshService, masterIp) {
232
- try {
233
- const raw = await sshService.sshExec(masterIp, "kubectl -n flui-system get configmap flui-api-config -o jsonpath='{.data.OIDC_ISSUER}'");
234
- const trimmed = raw.trim();
235
- return trimmed || undefined;
236
- }
237
- catch {
238
- return undefined;
239
- }
240
- }
241
220
  async resolveApiPath(explicit, save) {
242
221
  const storage = new config_storage_1.ConfigStorage();
243
222
  const resolver = new preferences_resolver_1.PreferencesResolver(storage);
@@ -267,7 +246,7 @@ DevCreds.description = 'Developer-only: write cluster secrets (DB/Redis password
267
246
  DevCreds.examples = [
268
247
  '<%= config.bin %> <%= command.id %>',
269
248
  '<%= config.bin %> <%= command.id %> --dry-run',
270
- '<%= config.bin %> <%= command.id %> --api-path ../flui.api',
249
+ '<%= config.bin %> <%= command.id %> --api-path ../flui-core',
271
250
  ];
272
251
  DevCreds.flags = {
273
252
  'dry-run': core_1.Flags.boolean({
@@ -10,7 +10,7 @@ const node_child_process_1 = require("node:child_process");
10
10
  const node_util_1 = require("node:util");
11
11
  const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
12
12
  const nest_app_1 = require("../../lib/nest-app");
13
- const cli_observability_cluster_service_1 = require("../../services/cli-observability-cluster.service");
13
+ const cli_control_cluster_service_1 = require("../../services/cli-control-cluster.service");
14
14
  const cli_ssh_service_1 = require("../../services/cli-ssh.service");
15
15
  const cluster_entity_1 = require("../../../../src/modules/infrastructure/clusters/entities/cluster.entity");
16
16
  const FORWARDS = {
@@ -41,7 +41,7 @@ const FORWARDS = {
41
41
  localPort: 3001,
42
42
  remotePort: 3000,
43
43
  service: 'svc/grafana',
44
- namespace: 'flui-observability',
44
+ namespace: 'flui-control',
45
45
  needsKubectl: true,
46
46
  },
47
47
  vmsingle: {
@@ -49,7 +49,7 @@ const FORWARDS = {
49
49
  localPort: 9090,
50
50
  remotePort: 8428,
51
51
  service: 'svc/vmsingle',
52
- namespace: 'flui-observability',
52
+ namespace: 'flui-control',
53
53
  needsKubectl: true,
54
54
  },
55
55
  loki: {
@@ -57,7 +57,7 @@ const FORWARDS = {
57
57
  localPort: 3100,
58
58
  remotePort: 3100,
59
59
  service: 'svc/loki',
60
- namespace: 'flui-observability',
60
+ namespace: 'flui-control',
61
61
  needsKubectl: true,
62
62
  },
63
63
  };
@@ -68,10 +68,10 @@ class DevTunnel extends core_1.Command {
68
68
  let masterIp;
69
69
  try {
70
70
  const app = await (0, nest_app_1.getNestApp)();
71
- const observabilityService = app.get(cli_observability_cluster_service_1.CliObservabilityClusterService);
72
- const cluster = await observabilityService.getObservabilityCluster();
71
+ const controlService = app.get(cli_control_cluster_service_1.CliControlClusterService);
72
+ const cluster = await controlService.getControlCluster();
73
73
  if (!cluster) {
74
- spinner.fail('No observability cluster found');
74
+ spinner.fail('No control cluster found');
75
75
  console.log(chalk_1.default.yellow('\n⚠️ Run `flui env create` first.\n'));
76
76
  return;
77
77
  }
@@ -239,7 +239,7 @@ class DevTunnel extends core_1.Command {
239
239
  return `sh -c '${lines.join('\n')}'`;
240
240
  }
241
241
  }
242
- DevTunnel.description = 'Open SSH tunnels from localhost to the observability cluster services.\n' +
242
+ DevTunnel.description = 'Open SSH tunnels from localhost to the control cluster services.\n' +
243
243
  'On the remote side runs kubectl port-forward against the in-cluster Services,\n' +
244
244
  'so no NodePort or kube-API exposure is required. Stay in foreground; CTRL-C to close.';
245
245
  DevTunnel.examples = [
@@ -7,7 +7,7 @@ const core_1 = require("@oclif/core");
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const ora_1 = __importDefault(require("ora"));
9
9
  const nest_app_1 = require("../../lib/nest-app");
10
- const cli_observability_cluster_service_1 = require("../../services/cli-observability-cluster.service");
10
+ const cli_control_cluster_service_1 = require("../../services/cli-control-cluster.service");
11
11
  const cluster_capacity_service_1 = require("../../../../src/modules/infrastructure/clusters/services/cluster-capacity.service");
12
12
  const context_banner_1 = require("../../lib/context-banner");
13
13
  class EnvCapacity extends core_1.Command {
@@ -17,11 +17,11 @@ class EnvCapacity extends core_1.Command {
17
17
  const spinner = (0, ora_1.default)('Computing capacity plan...').start();
18
18
  try {
19
19
  const app = await (0, nest_app_1.getNestApp)();
20
- const observabilityService = app.get(cli_observability_cluster_service_1.CliObservabilityClusterService);
20
+ const controlService = app.get(cli_control_cluster_service_1.CliControlClusterService);
21
21
  const capacityService = app.get(cluster_capacity_service_1.ClusterCapacityService);
22
- const cluster = await observabilityService.getObservabilityCluster();
22
+ const cluster = await controlService.getControlCluster();
23
23
  if (!cluster) {
24
- spinner.fail('No observability cluster found');
24
+ spinner.fail('No control cluster found');
25
25
  console.log(chalk_1.default.yellow('\n⚠️ Create a cluster first: ' + chalk_1.default.cyan('flui env create\n')));
26
26
  return;
27
27
  }
@@ -1,6 +1,6 @@
1
1
  import { Command } from '@oclif/core';
2
2
  export default class EnvCreate extends Command {
3
- static readonly description = "Create observability cluster infrastructure on K3s";
3
+ static readonly description = "Create control cluster infrastructure on K3s";
4
4
  static readonly examples: string[];
5
5
  static readonly flags: {
6
6
  provider: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -14,8 +14,11 @@ export default class EnvCreate extends Command {
14
14
  'firewall-ip': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
15
15
  'auth-mode': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
16
16
  'acme-staging': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
17
+ latest: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
17
18
  'no-shared-storage': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
18
19
  'shared-storage-size': import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
19
20
  };
21
+ /** The sole provider with configured credentials, or undefined if none/both. */
22
+ private detectConfiguredProvider;
20
23
  run(): Promise<void>;
21
24
  }