@rulebricks/cli 2.1.7 → 2.3.2

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 (117) hide show
  1. package/README.md +51 -16
  2. package/cluster-setup/aws/README.md +96 -47
  3. package/cluster-setup/aws/check-aws-access.sh +216 -52
  4. package/cluster-setup/aws/parameters.json +13 -0
  5. package/cluster-setup/aws/rulebricks-cluster.cfn.yaml +355 -0
  6. package/cluster-setup/azure/README.md +103 -55
  7. package/cluster-setup/azure/check-aks-prereqs.sh +236 -56
  8. package/cluster-setup/azure/parameters.json +30 -0
  9. package/cluster-setup/azure/rulebricks-cluster.bicep +546 -0
  10. package/cluster-setup/gcp/README.md +51 -34
  11. package/cluster-setup/gcp/check-gke-prereqs.sh +222 -60
  12. package/dist/commands/backup.d.ts +5 -0
  13. package/dist/commands/backup.js +104 -0
  14. package/dist/commands/deploy.d.ts +3 -1
  15. package/dist/commands/deploy.js +226 -326
  16. package/dist/commands/destroy.d.ts +1 -1
  17. package/dist/commands/destroy.js +73 -123
  18. package/dist/commands/init.d.ts +5 -1
  19. package/dist/commands/init.js +78 -54
  20. package/dist/commands/list.d.ts +1 -0
  21. package/dist/commands/list.js +74 -0
  22. package/dist/commands/open.d.ts +1 -1
  23. package/dist/commands/open.js +4 -12
  24. package/dist/commands/redeploy.d.ts +6 -0
  25. package/dist/commands/redeploy.js +310 -0
  26. package/dist/commands/restore.d.ts +5 -0
  27. package/dist/commands/restore.js +338 -0
  28. package/dist/commands/status.js +62 -49
  29. package/dist/commands/upgrade.js +74 -51
  30. package/dist/components/DNSWaitScreen.d.ts +5 -1
  31. package/dist/components/DNSWaitScreen.js +47 -41
  32. package/dist/components/Wizard/WizardContext.d.ts +157 -36
  33. package/dist/components/Wizard/WizardContext.js +872 -160
  34. package/dist/components/Wizard/steps/CloudProviderStep.js +192 -107
  35. package/dist/components/Wizard/steps/DomainStep.js +5 -24
  36. package/dist/components/Wizard/steps/ExternalServicesStep.d.ts +6 -0
  37. package/dist/components/Wizard/steps/ExternalServicesStep.js +645 -0
  38. package/dist/components/Wizard/steps/FeatureConfigStep.d.ts +2 -1
  39. package/dist/components/Wizard/steps/FeatureConfigStep.js +739 -425
  40. package/dist/components/Wizard/steps/FeaturesStep.js +31 -35
  41. package/dist/components/Wizard/steps/ObservabilityStep.d.ts +6 -0
  42. package/dist/components/Wizard/steps/ObservabilityStep.js +137 -0
  43. package/dist/components/Wizard/steps/ReviewStep.d.ts +2 -1
  44. package/dist/components/Wizard/steps/ReviewStep.js +56 -12
  45. package/dist/components/Wizard/steps/StorageStep.d.ts +9 -0
  46. package/dist/components/Wizard/steps/StorageStep.js +592 -0
  47. package/dist/components/Wizard/steps/SupabaseCredentialsStep.js +20 -21
  48. package/dist/components/Wizard/steps/VersionStep.js +45 -23
  49. package/dist/components/Wizard/steps/index.d.ts +3 -3
  50. package/dist/components/Wizard/steps/index.js +3 -3
  51. package/dist/components/common/CommandApproval.d.ts +12 -0
  52. package/dist/components/common/CommandApproval.js +91 -0
  53. package/dist/components/common/DeploymentPicker.d.ts +14 -0
  54. package/dist/components/common/DeploymentPicker.js +16 -0
  55. package/dist/components/common/index.d.ts +2 -0
  56. package/dist/components/common/index.js +2 -0
  57. package/dist/index.js +94 -62
  58. package/dist/lib/cloudCli.d.ts +134 -63
  59. package/dist/lib/cloudCli.js +512 -220
  60. package/dist/lib/clusterSetupDefaults.d.ts +30 -0
  61. package/dist/lib/clusterSetupDefaults.js +64 -0
  62. package/dist/lib/commandApproval.d.ts +26 -0
  63. package/dist/lib/commandApproval.js +114 -0
  64. package/dist/lib/config.d.ts +12 -10
  65. package/dist/lib/config.js +91 -33
  66. package/dist/lib/configFixtures.d.ts +5 -0
  67. package/dist/lib/configFixtures.js +513 -0
  68. package/dist/lib/deploymentHealth.d.ts +32 -0
  69. package/dist/lib/deploymentHealth.js +157 -0
  70. package/dist/lib/dns.d.ts +1 -1
  71. package/dist/lib/dns.js +19 -1
  72. package/dist/lib/dns.test.d.ts +1 -0
  73. package/dist/lib/dns.test.js +27 -0
  74. package/dist/lib/dockerHub.d.ts +12 -1
  75. package/dist/lib/dockerHub.js +18 -8
  76. package/dist/lib/helm.d.ts +4 -0
  77. package/dist/lib/helm.js +16 -0
  78. package/dist/lib/helmValues.d.ts +25 -0
  79. package/dist/lib/helmValues.js +1841 -289
  80. package/dist/lib/helmValues.test.d.ts +1 -0
  81. package/dist/lib/helmValues.test.js +1012 -0
  82. package/dist/lib/htpasswd.d.ts +1 -0
  83. package/dist/lib/htpasswd.js +15 -0
  84. package/dist/lib/kubernetes.d.ts +124 -17
  85. package/dist/lib/kubernetes.js +576 -145
  86. package/dist/lib/secrets.d.ts +23 -0
  87. package/dist/lib/secrets.js +158 -0
  88. package/dist/lib/validateValues.d.ts +31 -0
  89. package/dist/lib/validateValues.js +253 -0
  90. package/dist/lib/versions.d.ts +82 -11
  91. package/dist/lib/versions.js +131 -31
  92. package/dist/lib/versions.test.d.ts +1 -0
  93. package/dist/lib/versions.test.js +81 -0
  94. package/dist/lib/wizardSteps.d.ts +14 -0
  95. package/dist/lib/wizardSteps.js +23 -0
  96. package/dist/lib/workloadIdentity.d.ts +26 -0
  97. package/dist/lib/workloadIdentity.js +323 -0
  98. package/dist/lib/workloadIdentity.test.d.ts +1 -0
  99. package/dist/lib/workloadIdentity.test.js +57 -0
  100. package/dist/types/index.d.ts +1860 -164
  101. package/dist/types/index.js +518 -295
  102. package/package.json +9 -4
  103. package/schema/values.schema.json +1934 -0
  104. package/cluster-setup/aws/cluster.yaml +0 -33
  105. package/cluster-setup/azure/main.bicep +0 -282
  106. package/cluster-setup/azure/main.parameters.json +0 -21
  107. package/dist/components/Wizard/steps/CredentialsStep.d.ts +0 -6
  108. package/dist/components/Wizard/steps/CredentialsStep.js +0 -22
  109. package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +0 -5
  110. package/dist/components/Wizard/steps/DeploymentModeStep.js +0 -26
  111. package/dist/components/Wizard/steps/TierStep.d.ts +0 -6
  112. package/dist/components/Wizard/steps/TierStep.js +0 -29
  113. package/dist/lib/terraform.d.ts +0 -66
  114. package/dist/lib/terraform.js +0 -754
  115. package/terraform/aws/main.tf +0 -355
  116. package/terraform/azure/main.tf +0 -371
  117. package/terraform/gcp/main.tf +0 -407
@@ -3,8 +3,7 @@ import { useState } from 'react';
3
3
  import { Box, Text, useInput } from 'ink';
4
4
  import { useWizard } from '../WizardContext.js';
5
5
  import { BorderBox, useTheme } from '../../common/index.js';
6
- import { LOGGING_SINK_INFO } from '../../../types/index.js';
7
- // Five features: AI, SSO, Monitoring, External Logging, Custom Emails
6
+ // Features: AI, SSO, External Logging, Custom Emails
8
7
  // External DNS is handled in Domain step, not here
9
8
  const FEATURES = [
10
9
  {
@@ -20,17 +19,16 @@ const FEATURES = [
20
19
  requiresConfig: true
21
20
  },
22
21
  {
23
- id: 'monitoring',
24
- label: 'Monitoring',
25
- description: 'Enable Prometheus metrics collection. Optionally send to external system.',
26
- requiresConfig: true
27
- },
28
- {
29
- id: 'logging',
30
- label: 'External Logging',
31
- description: 'Forward logs to cloud storage or logging platforms (Datadog, Splunk, etc.)',
32
- requiresConfig: true
22
+ id: 'valkeyObservability',
23
+ label: 'Valkey Admin + Cache Metrics',
24
+ description: 'Deploy the official Apache-2.0 Valkey Admin console (internal by default, optionally public via Traefik BasicAuth) and export Valkey/Kafka lag metrics to Prometheus.',
25
+ requiresConfig: false
33
26
  },
27
+ // NOTE: Forwarding a copy of decision logs to a third-party platform
28
+ // (Datadog, Splunk, Elasticsearch, Loki, New Relic, Axiom) is no longer
29
+ // offered in the wizard - it was confusing alongside the always-on
30
+ // decision-log archive to object storage. The capability still exists for
31
+ // config-file/redeploy users (features.logging.sink + generateVectorSinks).
34
32
  {
35
33
  id: 'customEmails',
36
34
  label: 'Custom Email Templates',
@@ -45,8 +43,7 @@ export function FeaturesStep({ onComplete, onBack }) {
45
43
  const enabledFeatures = {
46
44
  ai: state.aiEnabled,
47
45
  sso: state.ssoEnabled,
48
- monitoring: state.monitoringEnabled,
49
- logging: state.loggingSink !== 'console', // External logging is "enabled" if not console-only (includes 'pending')
46
+ valkeyObservability: state.valkeyAdminEnabled,
50
47
  customEmails: state.customEmailsEnabled
51
48
  };
52
49
  useInput((input, key) => {
@@ -86,34 +83,33 @@ export function FeaturesStep({ onComplete, onBack }) {
86
83
  case 'sso':
87
84
  dispatch({ type: 'SET_SSO_ENABLED', enabled: !state.ssoEnabled });
88
85
  break;
89
- case 'monitoring':
90
- dispatch({ type: 'SET_MONITORING', enabled: !state.monitoringEnabled });
91
- break;
92
- case 'logging':
93
- // Toggle between console-only and needing to pick a sink
94
- // If currently console, mark as pending - FeatureConfigStep will handle sink selection
95
- if (state.loggingSink === 'console') {
96
- dispatch({ type: 'SET_LOGGING_SINK', sink: 'pending' });
97
- }
98
- else {
99
- dispatch({ type: 'SET_LOGGING_SINK', sink: 'console' });
100
- }
86
+ case 'valkeyObservability': {
87
+ const enabled = !state.valkeyAdminEnabled;
88
+ dispatch({
89
+ type: 'SET_EXTERNAL_SERVICES',
90
+ config: {
91
+ valkeyAdminEnabled: enabled,
92
+ redisExporterEnabled: enabled,
93
+ kafkaExporterEnabled: enabled,
94
+ valkeyAdminExposure: enabled ? 'ingress' : 'internal',
95
+ valkeyAdminHostname: '',
96
+ ...(enabled ? {} : { valkeyAdminBasicAuthUsers: [], valkeyAdminAllowedIPs: [] })
97
+ }
98
+ });
101
99
  break;
100
+ }
102
101
  case 'customEmails':
103
102
  dispatch({ type: 'SET_CUSTOM_EMAILS_ENABLED', enabled: !state.customEmailsEnabled });
104
103
  break;
105
104
  }
106
105
  };
107
- // Get current logging sink description
108
- const getLoggingStatusText = () => {
109
- if (state.loggingSink === 'console') {
110
- return 'Console only (default)';
111
- }
112
- return LOGGING_SINK_INFO[state.loggingSink]?.name || state.loggingSink;
113
- };
114
106
  return (_jsxs(BorderBox, { title: "Optional Features", children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Select features to enable:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Use arrows to navigate, space/enter to toggle" })] }), _jsxs(Box, { flexDirection: "column", marginY: 1, children: [FEATURES.map((feature, index) => {
115
107
  const isSelected = index === currentIndex;
116
108
  const isEnabled = enabledFeatures[feature.id];
117
- return (_jsxs(Box, { flexDirection: "column", marginBottom: isSelected ? 1 : 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? colors.accent : undefined, children: isSelected ? '❯ ' : ' ' }), _jsx(Text, { color: isEnabled ? colors.success : colors.muted, children: isEnabled ? '[✓]' : '[ ]' }), _jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [' ', feature.label] }), feature.id === 'logging' && state.loggingSink !== 'console' && state.loggingSink !== 'pending' && (_jsxs(Text, { color: colors.accent, children: [" \u2192 ", getLoggingStatusText()] }))] }), isSelected && (_jsx(Box, { marginLeft: 6, children: _jsx(Text, { color: "gray", dimColor: true, children: feature.description }) }))] }, feature.id));
118
- }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: currentIndex === FEATURES.length ? colors.accent : colors.muted, children: currentIndex === FEATURES.length ? '❯ ' : ' ' }), _jsx(Text, { color: currentIndex === FEATURES.length ? colors.success : colors.muted, bold: currentIndex === FEATURES.length, children: "[Continue \u2192]" })] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", dimColor: true, children: "Space/Enter to toggle \u2022 \u2191/\u2193 to navigate \u2022 Esc to go back" }), (state.aiEnabled || state.ssoEnabled || state.monitoringEnabled || state.loggingSink !== 'console') && (_jsx(Text, { color: "yellow", dimColor: true, children: "Note: Enabled features will be configured in the next step" }))] })] }));
109
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: isSelected ? 1 : 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? colors.accent : undefined, children: isSelected ? '❯ ' : ' ' }), _jsx(Text, { color: isEnabled ? colors.success : colors.muted, children: isEnabled ? '[✓]' : '[ ]' }), _jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [' ', feature.label] })] }), isSelected && (_jsx(Box, { marginLeft: 6, children: _jsx(Text, { color: "gray", dimColor: true, children: feature.description }) }))] }, feature.id));
110
+ }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: currentIndex === FEATURES.length ? colors.accent : colors.muted, children: currentIndex === FEATURES.length ? '❯ ' : ' ' }), _jsx(Text, { color: currentIndex === FEATURES.length ? colors.success : colors.muted, bold: currentIndex === FEATURES.length, children: "[Continue \u2192]" })] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", dimColor: true, children: "Space/Enter to toggle \u2022 \u2191/\u2193 to navigate \u2022 Esc to go back" }), (state.aiEnabled ||
111
+ state.ssoEnabled ||
112
+ state.valkeyAdminEnabled ||
113
+ state.loggingSink !== 'console' ||
114
+ state.customEmailsEnabled) && (_jsx(Text, { color: "yellow", dimColor: true, children: "Note: Enabled features will be configured in the next step" }))] })] }));
119
115
  }
@@ -0,0 +1,6 @@
1
+ interface ObservabilityStepProps {
2
+ onComplete: () => void;
3
+ onBack: () => void;
4
+ }
5
+ export declare function ObservabilityStep({ onComplete, onBack, }: ObservabilityStepProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,137 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Box, Text, useInput } from "ink";
4
+ import SelectInput from "ink-select-input";
5
+ import TextInput from "ink-text-input";
6
+ import { useWizard } from "../WizardContext.js";
7
+ import { BorderBox, useTheme } from "../../common/index.js";
8
+ const MODE_OPTIONS = [
9
+ {
10
+ label: "Use Rulebricks built-in observability (ClickStack + HyperDX)",
11
+ value: "built-in",
12
+ },
13
+ {
14
+ label: "Export to my own observability systems",
15
+ value: "byo",
16
+ },
17
+ ];
18
+ const SIGNALS = [
19
+ {
20
+ id: "metrics",
21
+ label: "Metrics export",
22
+ description: "Prometheus remote_write to your managed metrics backend.",
23
+ },
24
+ {
25
+ id: "traces",
26
+ label: "Distributed tracing",
27
+ description: "OTLP traces to Elastic, Azure Monitor, or another backend.",
28
+ },
29
+ {
30
+ id: "logs",
31
+ label: "Application log shipping",
32
+ description: "Pod/app logs to Elasticsearch, Loki, or generic HTTP.",
33
+ },
34
+ ];
35
+ function parsePositiveInt(value, fallback) {
36
+ const parsed = Number.parseInt(value.trim(), 10);
37
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
38
+ }
39
+ function normalizeSize(value, fallback) {
40
+ const trimmed = value.trim();
41
+ return trimmed || fallback;
42
+ }
43
+ export function ObservabilityStep({ onComplete, onBack, }) {
44
+ const { state, dispatch } = useWizard();
45
+ const { colors } = useTheme();
46
+ const [subStep, setSubStep] = useState("mode");
47
+ const [telemetryRetention, setTelemetryRetention] = useState(String(state.clickStackTelemetryRetentionDays || 7));
48
+ const [clickHouseStorage, setClickHouseStorage] = useState(state.clickHouseStorageSize || "100Gi");
49
+ const [signalIndex, setSignalIndex] = useState(0);
50
+ const requestedStorageGi = Number.parseInt(clickHouseStorage, 10) + 10;
51
+ const reportedStorageGi = state.totalPersistentStorageGi || 0;
52
+ const storageWarning = reportedStorageGi > 0 && requestedStorageGi > reportedStorageGi * 0.75;
53
+ useInput((input, key) => {
54
+ if (key.escape) {
55
+ if (subStep === "mode") {
56
+ onBack();
57
+ }
58
+ else {
59
+ setSubStep("mode");
60
+ }
61
+ return;
62
+ }
63
+ if (subStep !== "byo-signals")
64
+ return;
65
+ if (key.upArrow) {
66
+ setSignalIndex((idx) => Math.max(0, idx - 1));
67
+ }
68
+ else if (key.downArrow) {
69
+ setSignalIndex((idx) => Math.min(SIGNALS.length, idx + 1));
70
+ }
71
+ else if (input === " " || input === "x" || key.return) {
72
+ if (signalIndex === SIGNALS.length) {
73
+ onComplete();
74
+ return;
75
+ }
76
+ const signal = SIGNALS[signalIndex];
77
+ if (signal.id === "metrics") {
78
+ dispatch({
79
+ type: "SET_METRICS_EXPORT",
80
+ enabled: !state.metricsExportEnabled,
81
+ });
82
+ }
83
+ else if (signal.id === "traces") {
84
+ dispatch({
85
+ type: "SET_TRACING_ENABLED",
86
+ enabled: !state.tracingEnabled,
87
+ });
88
+ }
89
+ else {
90
+ dispatch({
91
+ type: "SET_APP_LOGS_ENABLED",
92
+ enabled: !state.appLogsEnabled,
93
+ });
94
+ }
95
+ }
96
+ });
97
+ const chooseMode = (item) => {
98
+ if (item.value === "built-in") {
99
+ dispatch({ type: "SET_CLICKSTACK_ENABLED", enabled: true });
100
+ setSubStep("builtin-telemetry-retention");
101
+ return;
102
+ }
103
+ dispatch({ type: "SET_CLICKSTACK_ENABLED", enabled: false });
104
+ setSubStep("byo-signals");
105
+ };
106
+ const saveBuiltInSettings = () => {
107
+ dispatch({
108
+ type: "SET_CLICKSTACK_CONFIG",
109
+ config: {
110
+ clickStackTelemetryRetentionDays: parsePositiveInt(telemetryRetention, 7),
111
+ clickHouseStorageSize: normalizeSize(clickHouseStorage, "100Gi"),
112
+ },
113
+ });
114
+ onComplete();
115
+ };
116
+ const renderCapacitySummary = () => (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: "gray", dimColor: true, children: ["Storage class: ", state.storageClass || "not detected"] }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Reported persistent storage:", " ", reportedStorageGi > 0
117
+ ? `${Math.ceil(reportedStorageGi)} Gi`
118
+ : "unknown / dynamic provisioning"] }), _jsxs(Text, { color: storageWarning ? colors.warning : "gray", dimColor: true, children: ["Requested ClickStack PVCs: ", Number.isFinite(requestedStorageGi) ? requestedStorageGi : 0, " Gi", " ", "(", clickHouseStorage || "100Gi", " ClickHouse + 10Gi HyperDX metadata)", storageWarning ? " (high relative to reported capacity)" : ""] })] }));
119
+ if (subStep === "mode") {
120
+ return (_jsxs(BorderBox, { title: "Observability", children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "How should Rulebricks observability be set up?" }), _jsx(Text, { color: "gray", dimColor: true, children: "Built-in ClickStack gives you logs, traces, mirrored metrics, and operational dashboards. Decision logs stay in object storage and are queried directly when needed." })] }), _jsx(SelectInput, { items: MODE_OPTIONS, onSelect: chooseMode }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Esc to go back" }) })] }));
121
+ }
122
+ if (subStep === "builtin-telemetry-retention") {
123
+ return (_jsx(BorderBox, { title: "Telemetry Retention", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "How many days of ClickStack logs/traces/metrics should be retained?" }), _jsx(Text, { color: "gray", dimColor: true, children: "This controls operational telemetry TTL. Decision logs are archived only to object storage." }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Days: " }), _jsx(TextInput, { value: telemetryRetention, onChange: setTelemetryRetention, onSubmit: () => setSubStep("builtin-clickhouse-storage"), placeholder: "7" })] })] }) }));
124
+ }
125
+ if (subStep === "builtin-clickhouse-storage") {
126
+ return (_jsx(BorderBox, { title: "ClickHouse Storage", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "How large should the ClickHouse PVC be?" }), _jsx(Text, { color: "gray", dimColor: true, children: "Stores ClickStack operational telemetry. Example: 100Gi" }), renderCapacitySummary(), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Size: " }), _jsx(TextInput, { value: clickHouseStorage, onChange: setClickHouseStorage, onSubmit: saveBuiltInSettings, placeholder: "100Gi" })] })] }) }));
127
+ }
128
+ return (_jsxs(BorderBox, { title: "BYO Observability Signals", children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Select the signals you want to export to your own systems:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Space/Enter to toggle, then Continue. Connection details come later in Feature Settings." })] }), _jsxs(Box, { flexDirection: "column", marginY: 1, children: [SIGNALS.map((signal, index) => {
129
+ const enabled = signal.id === "metrics"
130
+ ? state.metricsExportEnabled
131
+ : signal.id === "traces"
132
+ ? state.tracingEnabled
133
+ : state.appLogsEnabled;
134
+ const selected = signalIndex === index;
135
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: selected ? 1 : 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: selected ? colors.accent : undefined, children: selected ? "❯ " : " " }), _jsx(Text, { color: enabled ? colors.success : colors.muted, children: enabled ? "[✓]" : "[ ]" }), _jsxs(Text, { color: selected ? colors.accent : undefined, children: [" ", signal.label] })] }), selected && (_jsx(Box, { marginLeft: 6, children: _jsx(Text, { color: "gray", dimColor: true, children: signal.description }) }))] }, signal.id));
136
+ }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: signalIndex === SIGNALS.length ? colors.accent : colors.muted, children: signalIndex === SIGNALS.length ? "❯ " : " " }), _jsx(Text, { color: signalIndex === SIGNALS.length ? colors.success : colors.muted, bold: signalIndex === SIGNALS.length, children: "[Continue \u2192]" })] })] })] }));
137
+ }
@@ -1,6 +1,7 @@
1
1
  interface ReviewStepProps {
2
2
  onComplete: () => void;
3
3
  onBack: () => void;
4
+ allowEditName?: boolean;
4
5
  }
5
- export declare function ReviewStep({ onComplete, onBack }: ReviewStepProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function ReviewStep({ onComplete, onBack, allowEditName, }: ReviewStepProps): import("react/jsx-runtime").JSX.Element;
6
7
  export {};
@@ -1,14 +1,28 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { Box, Text, useInput } from 'ink';
4
4
  import TextInput from 'ink-text-input';
5
5
  import { useWizard } from '../WizardContext.js';
6
6
  import { BorderBox, useTheme } from '../../common/index.js';
7
- import { TIER_CONFIGS, DNS_PROVIDER_NAMES, isSupportedDnsProvider } from '../../../types/index.js';
8
- export function ReviewStep({ onComplete, onBack }) {
7
+ import { DNS_PROVIDER_NAMES, CLOUD_PROVIDER_NAMES, LOGGING_SINK_INFO, isSupportedDnsProvider } from '../../../types/index.js';
8
+ function kafkaPresetLabel(preset) {
9
+ switch (preset) {
10
+ case 'aws-msk-iam':
11
+ return 'AWS MSK IAM';
12
+ case 'azure-event-hubs':
13
+ return 'Azure Event Hubs';
14
+ case 'gcp-managed':
15
+ return 'GCP Managed Kafka';
16
+ case 'custom':
17
+ return 'custom';
18
+ default:
19
+ return 'not configured';
20
+ }
21
+ }
22
+ export function ReviewStep({ onComplete, onBack, allowEditName = true, }) {
9
23
  const { state, dispatch } = useWizard();
10
24
  const { colors } = useTheme();
11
- const [editingName, setEditingName] = useState(!state.name);
25
+ const [editingName, setEditingName] = useState(allowEditName && !state.name);
12
26
  const [name, setName] = useState(state.name || '');
13
27
  const [error, setError] = useState(null);
14
28
  useInput((input, key) => {
@@ -22,7 +36,7 @@ export function ReviewStep({ onComplete, onBack }) {
22
36
  onComplete();
23
37
  }
24
38
  }
25
- else if (input === 'e') {
39
+ else if (allowEditName && input === 'e') {
26
40
  setEditingName(true);
27
41
  }
28
42
  });
@@ -43,13 +57,30 @@ export function ReviewStep({ onComplete, onBack }) {
43
57
  dispatch({ type: 'SET_NAME', name });
44
58
  setEditingName(false);
45
59
  };
46
- const tierConfig = state.infrastructureMode !== 'existing' && state.tier ? TIER_CONFIGS[state.tier] : null;
47
- const tierLabel = state.infrastructureMode === 'existing'
48
- ? 'Inferred from cluster'
49
- : state.tier
50
- ? `${state.tier.charAt(0).toUpperCase()}${state.tier.slice(1)}`
51
- : 'Not selected';
52
60
  const externalDnsEnabled = state.dnsAutoManage && isSupportedDnsProvider(state.dnsProvider);
61
+ const clusterCpuCores = state.eligibleCpuCores || Math.ceil(state.totalCpuCores);
62
+ const clusterMemoryGi = state.eligibleMemoryGi || Math.ceil(state.totalMemoryGi);
63
+ const storageValue = state.storageClass
64
+ ? state.totalPersistentStorageGi > 0
65
+ ? `${state.storageClass} (${Math.ceil(state.totalPersistentStorageGi)} Gi reported available)`
66
+ : `${state.storageClass} (dynamic PVC provisioning)`
67
+ : '';
68
+ const monitoringDestination = state.clickStackEnabled
69
+ ? 'Prometheus + in-cluster ClickStack metrics mirror'
70
+ : state.metricsExportEnabled && state.prometheusRemoteWriteDestination
71
+ ? `Remote write: ${state.prometheusRemoteWriteDestination}`
72
+ : 'In-cluster Prometheus (no remote write)';
73
+ const storageAuthValue = state.storageProvider === 's3'
74
+ ? state.storageAwsIamRoleArn || 'not configured'
75
+ : state.storageProvider === 'gcs'
76
+ ? state.storageGcpServiceAccountEmail || 'not configured'
77
+ : state.storageCloudAuthMode === 'secret'
78
+ ? `secret: ${state.storageAzureBlobConnectionStringSecretRef || 'not configured'}`
79
+ : state.storageAzureBlobClientId
80
+ ? `workload identity (${state.storageAzureBlobClientId})`
81
+ : 'workload identity';
82
+ const clickStackStorageGi = Number.parseInt(state.clickHouseStorageSize || "0", 10) + 10;
83
+ const usesInClusterPostgres = state.databaseType === 'self-hosted' && state.postgresMode !== 'external';
53
84
  if (editingName) {
54
85
  return (_jsx(BorderBox, { title: "Deployment Name", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Enter a name for this deployment:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Lowercase letters, numbers, and hyphens only" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: name, onChange: setName, onSubmit: handleNameSubmit, placeholder: "my-deployment" }) }), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["\u2717 ", error] }) }))] }) }));
55
86
  }
@@ -57,5 +88,18 @@ export function ReviewStep({ onComplete, onBack }) {
57
88
  const ConfigRow = ({ label, value, valueColor }) => (_jsxs(Box, { children: [_jsx(Box, { width: 16, children: _jsx(Text, { color: colors.muted, children: label }) }), _jsx(Text, { color: valueColor || colors.accent, children: value })] }));
58
89
  // Helper to render a section header
59
90
  const SectionHeader = ({ title }) => (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { bold: true, color: colors.accent, children: ["\u2500\u2500 ", title, " \u2500\u2500"] }) }));
60
- return (_jsxs(BorderBox, { title: "Review Configuration", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { title: "Deployment" }), _jsx(ConfigRow, { label: "Name", value: state.name }), state.appVersion && (_jsx(ConfigRow, { label: "App Version", value: state.appVersion })), _jsx(SectionHeader, { title: "Infrastructure" }), _jsx(ConfigRow, { label: "Mode", value: state.infrastructureMode === 'provision' ? 'Provision new cluster' : 'Use existing cluster' }), state.provider && (_jsx(ConfigRow, { label: "Provider", value: state.provider.toUpperCase() })), state.region && (_jsx(ConfigRow, { label: "Region", value: state.region })), _jsx(SectionHeader, { title: "Domain & DNS" }), _jsx(ConfigRow, { label: "Domain", value: state.domain }), _jsx(ConfigRow, { label: "Admin Email", value: state.adminEmail }), _jsx(ConfigRow, { label: "TLS Email", value: state.tlsEmail }), _jsxs(Box, { children: [_jsx(Box, { width: 16, children: _jsx(Text, { color: colors.muted, children: "DNS" }) }), _jsx(Text, { color: colors.accent, children: DNS_PROVIDER_NAMES[state.dnsProvider] }), externalDnsEnabled && _jsx(Text, { color: colors.success, children: " (auto)" })] }), _jsx(SectionHeader, { title: "SMTP" }), _jsx(ConfigRow, { label: "Host", value: `${state.smtpHost}:${state.smtpPort}` }), _jsx(ConfigRow, { label: "From", value: `${state.smtpFromName} <${state.smtpFrom}>` }), _jsx(SectionHeader, { title: "Database" }), _jsx(ConfigRow, { label: "Type", value: state.databaseType === 'supabase-cloud' ? 'Supabase Cloud' : 'Self-hosted' }), _jsx(SectionHeader, { title: "Performance" }), _jsxs(Box, { children: [_jsx(Box, { width: 16, children: _jsx(Text, { color: colors.muted, children: "Tier" }) }), _jsx(Text, { color: colors.accent, bold: true, children: tierLabel }), tierConfig && _jsxs(Text, { color: colors.muted, children: [" (", tierConfig.throughput, ")"] }), state.infrastructureMode === 'existing' && (_jsx(Text, { color: colors.muted, children: " (used for app sizing)" }))] }), _jsx(SectionHeader, { title: "Features" }), _jsxs(Box, { children: [_jsxs(Text, { color: state.aiEnabled ? colors.success : colors.muted, children: [state.aiEnabled ? '✓' : '○', " AI"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: state.ssoEnabled ? colors.success : colors.muted, children: [state.ssoEnabled ? '✓' : '○', " SSO"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: state.monitoringEnabled ? colors.success : colors.muted, children: [state.monitoringEnabled ? '✓' : '○', " Monitoring"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: state.loggingSink !== 'console' ? colors.success : colors.muted, children: [state.loggingSink !== 'console' ? '✓' : '○', " Logging"] })] }), _jsx(SectionHeader, { title: "License" }), _jsx(ConfigRow, { label: "Key", value: `${state.licenseKey?.substring(0, 12)}...` })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.success, bold: true, children: "Press Enter to save this configuration" }), _jsx(Text, { color: colors.muted, dimColor: true, children: "e to edit name \u2022 Esc to go back" })] })] }));
91
+ return (_jsxs(BorderBox, { title: "Review Configuration", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { title: "Deployment" }), _jsx(ConfigRow, { label: "Name", value: state.name }), state.version && (_jsx(ConfigRow, { label: "Version", value: state.version })), _jsx(SectionHeader, { title: "Infrastructure" }), state.provider && (_jsx(ConfigRow, { label: "Provider", value: CLOUD_PROVIDER_NAMES[state.provider] })), state.clusterName && (_jsx(ConfigRow, { label: "Cluster", value: state.clusterName })), state.region && (_jsx(ConfigRow, { label: "Region", value: state.region })), state.azureResourceGroup && (_jsx(ConfigRow, { label: "Resource Group", value: state.azureResourceGroup })), state.gcpProjectId && (_jsx(ConfigRow, { label: "GCP Project", value: state.gcpProjectId })), _jsx(SectionHeader, { title: "Domain & DNS" }), _jsx(ConfigRow, { label: "Domain", value: state.domain }), _jsx(ConfigRow, { label: "Admin Email", value: state.adminEmail }), state.tlsEmail && state.tlsEmail !== state.adminEmail && (_jsx(ConfigRow, { label: "TLS Email", value: state.tlsEmail })), _jsxs(Box, { children: [_jsx(Box, { width: 16, children: _jsx(Text, { color: colors.muted, children: "DNS" }) }), _jsx(Text, { color: colors.accent, children: DNS_PROVIDER_NAMES[state.dnsProvider] }), externalDnsEnabled && _jsx(Text, { color: colors.success, children: " (auto)" })] }), _jsx(SectionHeader, { title: "SMTP" }), _jsx(ConfigRow, { label: "Host", value: `${state.smtpHost}:${state.smtpPort}` }), _jsx(ConfigRow, { label: "From", value: `${state.smtpFromName} <${state.smtpFrom}>` }), _jsx(SectionHeader, { title: "Database" }), _jsx(ConfigRow, { label: "Type", value: state.databaseType === 'supabase-cloud' ? 'Supabase Cloud' : 'Self-hosted' }), _jsx(SectionHeader, { title: "Cluster" }), state.totalCpuCores > 0 && (_jsx(ConfigRow, { label: "Capacity", value: `${state.schedulableNodeCount} nodes, ${clusterCpuCores} vCPU, ${clusterMemoryGi} Gi` })), state.storageClass && (_jsx(ConfigRow, { label: "Storage", value: storageValue })), state.storageProvider && (_jsxs(_Fragment, { children: [_jsx(SectionHeader, { title: "Object Storage" }), _jsx(ConfigRow, { label: "Bucket", value: `${state.storageProvider} / ${state.storageBucket || 'not configured'}` }), state.storageRegion && (_jsx(ConfigRow, { label: "Region", value: state.storageRegion })), state.storageProvider === 'azure-blob' &&
92
+ state.storageAzureBlobContainer && (_jsx(ConfigRow, { label: "Container", value: state.storageAzureBlobContainer })), _jsx(ConfigRow, { label: "Auth", value: storageAuthValue }), usesInClusterPostgres && state.backupEnabled && (_jsx(ConfigRow, { label: "DB backups", value: `${state.storageBucket || 'not configured'} / db-backups/` }))] })), usesInClusterPostgres && (_jsxs(_Fragment, { children: [_jsx(SectionHeader, { title: "Backups" }), _jsx(ConfigRow, { label: "Database", value: state.backupEnabled
93
+ ? `${state.backupSchedule} / ${state.backupRetentionDays} days`
94
+ : 'Disabled', valueColor: state.backupEnabled ? colors.success : colors.muted })] })), (state.redisMode === 'external' || state.kafkaMode === 'external') && (_jsxs(_Fragment, { children: [_jsx(SectionHeader, { title: "External Services" }), _jsx(ConfigRow, { label: "Redis", value: state.redisMode === 'external'
95
+ ? `external (${state.redisHost || 'not configured'}${state.redisTls ? ', TLS' : ''})`
96
+ : 'in-cluster', valueColor: state.redisMode === 'external' ? colors.success : colors.muted }), _jsx(ConfigRow, { label: "Kafka", value: state.kafkaMode === 'external'
97
+ ? `external (${kafkaPresetLabel(state.kafkaPreset)})`
98
+ : 'in-cluster', valueColor: state.kafkaMode === 'external' ? colors.success : colors.muted }), state.kafkaMode === 'external' && (_jsx(ConfigRow, { label: "Topic prefix", value: state.kafkaTopicPrefix || '(none)' })), state.kafkaMode === 'external' &&
99
+ state.kafkaSaslMechanism === 'aws-iam' && (_jsx(ConfigRow, { label: "Vector", value: "kafka-proxy bridge sidecar (MSK IAM)", valueColor: colors.muted })), state.kafkaMode === 'external' &&
100
+ state.kafkaSaslMechanism === 'aws-iam' && (_jsx(ConfigRow, { label: "Topics", value: state.kafkaProvisionTopics
101
+ ? 'auto-provisioned by the chart'
102
+ : 'self-managed (no CreateTopic)', valueColor: colors.muted }))] })), _jsx(SectionHeader, { title: "Features" }), _jsxs(Box, { children: [_jsxs(Text, { color: state.aiEnabled ? colors.success : colors.muted, children: [state.aiEnabled ? '✓' : '○', " AI"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: state.ssoEnabled ? colors.success : colors.muted, children: [state.ssoEnabled ? '✓' : '○', " SSO"] }), _jsx(Text, { children: " " }), _jsx(Text, { color: colors.success, children: "\u2713 Monitoring" }), _jsx(Text, { children: " " }), _jsxs(Text, { color: state.clickStackEnabled ? colors.success : colors.muted, children: [state.clickStackEnabled ? '✓' : '○', " ClickStack"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: !state.clickStackEnabled && state.tracingEnabled ? colors.success : colors.muted, children: [!state.clickStackEnabled && state.tracingEnabled ? '✓' : '○', " Tracing"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: !state.clickStackEnabled && state.appLogsEnabled ? colors.success : colors.muted, children: [!state.clickStackEnabled && state.appLogsEnabled ? '✓' : '○', " App Logs"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: state.loggingSink !== 'console' ? colors.success : colors.muted, children: [state.loggingSink !== 'console' ? '✓' : '○', " Logging"] })] }), _jsx(ConfigRow, { label: "Observability", value: state.clickStackEnabled
103
+ ? 'Built-in ClickStack + HyperDX'
104
+ : 'BYO/export mode', valueColor: state.clickStackEnabled ? colors.success : colors.muted }), state.clickStackEnabled && (_jsxs(_Fragment, { children: [_jsx(ConfigRow, { label: "Retention", value: `telemetry ${state.clickStackTelemetryRetentionDays}d` }), _jsx(ConfigRow, { label: "Storage", value: `ClickHouse ${state.clickHouseStorageSize}, HyperDX metadata 10Gi (${clickStackStorageGi} Gi requested)` })] })), _jsx(ConfigRow, { label: "Monitoring", value: monitoringDestination }), !state.clickStackEnabled && state.tracingEnabled && (_jsx(ConfigRow, { label: "Tracing", value: state.tracingElasticEndpoint || 'enabled' })), !state.clickStackEnabled && state.appLogsEnabled && (_jsx(ConfigRow, { label: "App logs", value: state.appLogsElasticEndpoint || 'enabled' })), state.loggingSink !== 'console' && (_jsx(ConfigRow, { label: "Logging", value: LOGGING_SINK_INFO[state.loggingSink]?.name || state.loggingSink })), _jsx(SectionHeader, { title: "License" }), _jsx(ConfigRow, { label: "Key", value: `${state.licenseKey?.substring(0, 12)}...` })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.success, bold: true, children: "Press Enter to save this configuration" }), _jsxs(Text, { color: colors.muted, dimColor: true, children: [allowEditName ? 'e to edit name • ' : '', "Esc to go back"] })] })] }));
61
105
  }
@@ -0,0 +1,9 @@
1
+ import { ObjectStorageProvider } from "../../../types/index.js";
2
+ interface StorageStepProps {
3
+ onComplete: () => void;
4
+ onBack: () => void;
5
+ }
6
+ export declare function storageProviderForCloud(provider: string | null): ObjectStorageProvider;
7
+ export declare function storageRegionForCloud(cloudProvider: string | null, clusterRegion: string, savedStorageProvider?: ObjectStorageProvider | null, savedStorageRegion?: string): string;
8
+ export declare function StorageStep({ onComplete, onBack }: StorageStepProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};