@rulebricks/cli 1.9.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 (93) hide show
  1. package/README.md +62 -0
  2. package/dist/commands/clone.d.ts +6 -0
  3. package/dist/commands/clone.js +60 -0
  4. package/dist/commands/deploy.d.ts +8 -0
  5. package/dist/commands/deploy.js +409 -0
  6. package/dist/commands/destroy.d.ts +8 -0
  7. package/dist/commands/destroy.js +298 -0
  8. package/dist/commands/init.d.ts +7 -0
  9. package/dist/commands/init.js +201 -0
  10. package/dist/commands/logs.d.ts +9 -0
  11. package/dist/commands/logs.js +222 -0
  12. package/dist/commands/open.d.ts +7 -0
  13. package/dist/commands/open.js +139 -0
  14. package/dist/commands/status.d.ts +5 -0
  15. package/dist/commands/status.js +125 -0
  16. package/dist/commands/upgrade.d.ts +7 -0
  17. package/dist/commands/upgrade.js +239 -0
  18. package/dist/components/DNSWaitScreen.d.ts +9 -0
  19. package/dist/components/DNSWaitScreen.js +73 -0
  20. package/dist/components/Wizard/WizardContext.d.ts +176 -0
  21. package/dist/components/Wizard/WizardContext.js +346 -0
  22. package/dist/components/Wizard/index.d.ts +2 -0
  23. package/dist/components/Wizard/index.js +2 -0
  24. package/dist/components/Wizard/steps/CloudProviderStep.d.ts +6 -0
  25. package/dist/components/Wizard/steps/CloudProviderStep.js +210 -0
  26. package/dist/components/Wizard/steps/CredentialsStep.d.ts +6 -0
  27. package/dist/components/Wizard/steps/CredentialsStep.js +22 -0
  28. package/dist/components/Wizard/steps/DatabaseStep.d.ts +6 -0
  29. package/dist/components/Wizard/steps/DatabaseStep.js +80 -0
  30. package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +5 -0
  31. package/dist/components/Wizard/steps/DeploymentModeStep.js +26 -0
  32. package/dist/components/Wizard/steps/DomainStep.d.ts +6 -0
  33. package/dist/components/Wizard/steps/DomainStep.js +126 -0
  34. package/dist/components/Wizard/steps/FeatureConfigStep.d.ts +6 -0
  35. package/dist/components/Wizard/steps/FeatureConfigStep.js +765 -0
  36. package/dist/components/Wizard/steps/FeaturesStep.d.ts +6 -0
  37. package/dist/components/Wizard/steps/FeaturesStep.js +119 -0
  38. package/dist/components/Wizard/steps/ReviewStep.d.ts +6 -0
  39. package/dist/components/Wizard/steps/ReviewStep.js +56 -0
  40. package/dist/components/Wizard/steps/SMTPStep.d.ts +6 -0
  41. package/dist/components/Wizard/steps/SMTPStep.js +191 -0
  42. package/dist/components/Wizard/steps/SupabaseCredentialsStep.d.ts +6 -0
  43. package/dist/components/Wizard/steps/SupabaseCredentialsStep.js +76 -0
  44. package/dist/components/Wizard/steps/TierStep.d.ts +6 -0
  45. package/dist/components/Wizard/steps/TierStep.js +29 -0
  46. package/dist/components/Wizard/steps/VersionStep.d.ts +6 -0
  47. package/dist/components/Wizard/steps/VersionStep.js +113 -0
  48. package/dist/components/Wizard/steps/index.d.ts +12 -0
  49. package/dist/components/Wizard/steps/index.js +12 -0
  50. package/dist/components/common/AppShell.d.ts +31 -0
  51. package/dist/components/common/AppShell.js +31 -0
  52. package/dist/components/common/Box.d.ts +20 -0
  53. package/dist/components/common/Box.js +20 -0
  54. package/dist/components/common/Logo.d.ts +7 -0
  55. package/dist/components/common/Logo.js +22 -0
  56. package/dist/components/common/Spinner.d.ts +12 -0
  57. package/dist/components/common/Spinner.js +28 -0
  58. package/dist/components/common/index.d.ts +6 -0
  59. package/dist/components/common/index.js +5 -0
  60. package/dist/index.d.ts +2 -0
  61. package/dist/index.js +202 -0
  62. package/dist/lib/cloudCli.d.ts +156 -0
  63. package/dist/lib/cloudCli.js +691 -0
  64. package/dist/lib/config.d.ts +91 -0
  65. package/dist/lib/config.js +278 -0
  66. package/dist/lib/dns.d.ts +41 -0
  67. package/dist/lib/dns.js +235 -0
  68. package/dist/lib/dockerHub.d.ts +57 -0
  69. package/dist/lib/dockerHub.js +128 -0
  70. package/dist/lib/helm.d.ts +53 -0
  71. package/dist/lib/helm.js +209 -0
  72. package/dist/lib/helmValues.d.ts +17 -0
  73. package/dist/lib/helmValues.js +693 -0
  74. package/dist/lib/kubernetes.d.ts +161 -0
  75. package/dist/lib/kubernetes.js +755 -0
  76. package/dist/lib/terraform.d.ts +44 -0
  77. package/dist/lib/terraform.js +230 -0
  78. package/dist/lib/theme.d.ts +81 -0
  79. package/dist/lib/theme.js +115 -0
  80. package/dist/lib/validation.d.ts +47 -0
  81. package/dist/lib/validation.js +164 -0
  82. package/dist/lib/versions.d.ts +69 -0
  83. package/dist/lib/versions.js +139 -0
  84. package/dist/types/index.d.ts +718 -0
  85. package/dist/types/index.js +556 -0
  86. package/email-templates/email_change.html +325 -0
  87. package/email-templates/invite.html +383 -0
  88. package/email-templates/password_change.html +414 -0
  89. package/email-templates/verify.html +396 -0
  90. package/package.json +78 -0
  91. package/terraform/aws/main.tf +327 -0
  92. package/terraform/azure/main.tf +326 -0
  93. package/terraform/gcp/main.tf +369 -0
@@ -0,0 +1,765 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState, useEffect } 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
+ import { Spinner } from "../../common/Spinner.js";
9
+ import { LOGGING_SINK_INFO, CLOUD_REGIONS, DEFAULT_EMAIL_SUBJECTS, } from "../../../types/index.js";
10
+ import { listBucketsInRegion, listRegions } from "../../../lib/cloudCli.js";
11
+ const SSO_PROVIDERS = [
12
+ { label: "Microsoft Azure AD", value: "azure" },
13
+ { label: "Google Workspace", value: "google" },
14
+ { label: "Okta", value: "okta" },
15
+ { label: "Keycloak", value: "keycloak" },
16
+ { label: "Ory", value: "ory" },
17
+ { label: "Other OIDC Provider", value: "other" },
18
+ ];
19
+ const LOGGING_CATEGORIES = [
20
+ { label: "Cloud Storage (S3, Azure Blob, GCS)", value: "cloud-storage" },
21
+ {
22
+ label: "Logging Platform (Datadog, Splunk, etc.)",
23
+ value: "logging-platform",
24
+ },
25
+ ];
26
+ const CLOUD_STORAGE_SINKS = [
27
+ { label: "AWS S3", value: "s3" },
28
+ { label: "Azure Blob Storage", value: "azure-blob" },
29
+ { label: "Google Cloud Storage", value: "gcs" },
30
+ ];
31
+ const LOGGING_PLATFORM_SINKS = [
32
+ { label: "Datadog", value: "datadog" },
33
+ { label: "Splunk (HEC)", value: "splunk" },
34
+ { label: "Elasticsearch", value: "elasticsearch" },
35
+ { label: "Grafana Loki", value: "loki" },
36
+ { label: "New Relic", value: "newrelic" },
37
+ { label: "Axiom", value: "axiom" },
38
+ ];
39
+ // Datadog sites
40
+ const DATADOG_SITES = [
41
+ { label: "US1 (datadoghq.com)", value: "datadoghq.com" },
42
+ { label: "US3 (us3.datadoghq.com)", value: "us3.datadoghq.com" },
43
+ { label: "US5 (us5.datadoghq.com)", value: "us5.datadoghq.com" },
44
+ { label: "EU1 (datadoghq.eu)", value: "datadoghq.eu" },
45
+ { label: "AP1 (ap1.datadoghq.com)", value: "ap1.datadoghq.com" },
46
+ ];
47
+ function BucketSelector({ loggingSink, loggingRegion, availableBuckets, isRefreshing, onSelect, onRefresh, colors, }) {
48
+ useInput((input) => {
49
+ if (input.toLowerCase() === "r") {
50
+ onRefresh();
51
+ }
52
+ });
53
+ const bucketItems = availableBuckets.map((b) => ({ label: b, value: b }));
54
+ const hasBuckets = availableBuckets.length > 0;
55
+ return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsxs(Text, { bold: true, children: [loggingSink === "s3" && "Select S3 Bucket", loggingSink === "azure-blob" && "Select Azure Storage Account", loggingSink === "gcs" && "Select GCS Bucket"] }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Select an existing bucket in ", loggingRegion] }), isRefreshing ? (_jsx(Box, { marginTop: 1, children: _jsx(Spinner, { label: "Refreshing bucket list..." }) })) : hasBuckets ? (_jsx(Box, { marginTop: 1, height: 10, flexDirection: "column", overflowY: "hidden", children: _jsx(SelectInput, { items: bucketItems, onSelect: onSelect, limit: 8, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => (_jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [isSelected ? "❯ " : " ", label] })) }) })) : (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { color: "yellow", children: ["No buckets found in ", loggingRegion, "."] }), _jsx(Text, { color: "gray", dimColor: true, children: "Create a bucket in your cloud console, then press R to refresh." })] })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" ", "Sink: ", LOGGING_SINK_INFO[loggingSink]?.name] })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Region: ", loggingRegion] })] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "R to refresh list \u2022 \u2191/\u2193 to navigate \u2022 Enter to select" }) })] }));
56
+ }
57
+ const YES_NO_OPTIONS = [
58
+ { label: "No, just collect metrics locally", value: false },
59
+ { label: "Yes, send metrics to external system", value: true },
60
+ ];
61
+ export function FeatureConfigStep({ onComplete, onBack, }) {
62
+ const { state, dispatch } = useWizard();
63
+ const { colors } = useTheme();
64
+ // Determine what needs to be configured
65
+ const needsAI = state.aiEnabled && !state.openaiApiKey;
66
+ const needsSSO = state.ssoEnabled;
67
+ const needsMonitoring = state.monitoringEnabled;
68
+ const needsLogging = state.loggingSink !== "console";
69
+ const needsCustomEmails = state.customEmailsEnabled;
70
+ // Configuration order: AI -> SSO -> Monitoring -> Logging -> Custom Emails
71
+ const getInitialStep = () => {
72
+ if (needsAI)
73
+ return "openai-key";
74
+ if (needsSSO)
75
+ return "sso-provider";
76
+ if (needsMonitoring)
77
+ return "monitoring-remote-write-ask";
78
+ if (needsLogging)
79
+ return "logging-category";
80
+ if (needsCustomEmails)
81
+ return "email-subject-invite";
82
+ return "done";
83
+ };
84
+ const [subStep, setSubStep] = useState(getInitialStep);
85
+ const [openaiKey, setOpenaiKey] = useState(state.openaiApiKey || "");
86
+ const [ssoProvider, setSsoProvider] = useState(state.ssoProvider);
87
+ const [ssoUrl, setSsoUrl] = useState(state.ssoUrl || "");
88
+ const [ssoClientId, setSsoClientId] = useState(state.ssoClientId || "");
89
+ const [ssoClientSecret, setSsoClientSecret] = useState(state.ssoClientSecret || "");
90
+ const [remoteWriteUrl, setRemoteWriteUrl] = useState(state.prometheusRemoteWriteUrl || "");
91
+ const [loggingSink, setLoggingSink] = useState(state.loggingSink);
92
+ const [loggingBucket, setLoggingBucket] = useState(state.loggingBucket || "");
93
+ const [loggingRegion, setLoggingRegion] = useState(state.loggingRegion || "");
94
+ const [loggingCategory, setLoggingCategory] = useState(null);
95
+ const [error, setError] = useState(null);
96
+ // Logging platform config
97
+ const [datadogApiKey, setDatadogApiKey] = useState("");
98
+ const [datadogSite, setDatadogSite] = useState("datadoghq.com");
99
+ const [splunkHecToken, setSplunkHecToken] = useState("");
100
+ const [splunkUrl, setSplunkUrl] = useState("");
101
+ const [elasticsearchUrl, setElasticsearchUrl] = useState("");
102
+ const [elasticsearchUser, setElasticsearchUser] = useState("");
103
+ const [elasticsearchPass, setElasticsearchPass] = useState("");
104
+ const [elasticsearchIndex, setElasticsearchIndex] = useState("rulebricks-logs");
105
+ const [lokiUrl, setLokiUrl] = useState("");
106
+ const [newrelicLicenseKey, setNewrelicLicenseKey] = useState("");
107
+ const [newrelicAccountId, setNewrelicAccountId] = useState("");
108
+ const [axiomApiToken, setAxiomApiToken] = useState("");
109
+ const [axiomDataset, setAxiomDataset] = useState("rulebricks");
110
+ // Dynamic bucket/region lists
111
+ const [availableBuckets, setAvailableBuckets] = useState([]);
112
+ const [availableRegions, setAvailableRegions] = useState([]);
113
+ const [isRefreshing, setIsRefreshing] = useState(false);
114
+ // Custom email templates
115
+ const [emailSubjectInvite, setEmailSubjectInvite] = useState(state.emailSubjects?.invite || DEFAULT_EMAIL_SUBJECTS.invite);
116
+ const [emailSubjectConfirm, setEmailSubjectConfirm] = useState(state.emailSubjects?.confirmation || DEFAULT_EMAIL_SUBJECTS.confirmation);
117
+ const [emailSubjectRecovery, setEmailSubjectRecovery] = useState(state.emailSubjects?.recovery || DEFAULT_EMAIL_SUBJECTS.recovery);
118
+ const [emailSubjectChange, setEmailSubjectChange] = useState(state.emailSubjects?.emailChange || DEFAULT_EMAIL_SUBJECTS.emailChange);
119
+ const [emailTemplateInvite, setEmailTemplateInvite] = useState(state.emailTemplates?.invite || "");
120
+ const [emailTemplateConfirm, setEmailTemplateConfirm] = useState(state.emailTemplates?.confirmation || "");
121
+ const [emailTemplateRecovery, setEmailTemplateRecovery] = useState(state.emailTemplates?.recovery || "");
122
+ const [emailTemplateChange, setEmailTemplateChange] = useState(state.emailTemplates?.emailChange || "");
123
+ // If nothing needs configuration, complete immediately
124
+ useEffect(() => {
125
+ if (!needsAI &&
126
+ !needsSSO &&
127
+ !needsMonitoring &&
128
+ !needsLogging &&
129
+ !needsCustomEmails) {
130
+ onComplete();
131
+ }
132
+ }, []);
133
+ useInput((input, key) => {
134
+ if (key.escape) {
135
+ setError(null);
136
+ handleBack();
137
+ }
138
+ });
139
+ const handleBack = () => {
140
+ switch (subStep) {
141
+ case "openai-key":
142
+ onBack();
143
+ break;
144
+ case "sso-provider":
145
+ if (needsAI)
146
+ setSubStep("openai-key");
147
+ else
148
+ onBack();
149
+ break;
150
+ case "sso-url":
151
+ setSubStep("sso-provider");
152
+ break;
153
+ case "sso-client-id":
154
+ if (ssoProvider === "google")
155
+ setSubStep("sso-provider");
156
+ else
157
+ setSubStep("sso-url");
158
+ break;
159
+ case "sso-client-secret":
160
+ setSubStep("sso-client-id");
161
+ break;
162
+ case "monitoring-remote-write-ask":
163
+ if (needsSSO)
164
+ setSubStep("sso-client-secret");
165
+ else if (needsAI)
166
+ setSubStep("openai-key");
167
+ else
168
+ onBack();
169
+ break;
170
+ case "monitoring-remote-write-url":
171
+ setSubStep("monitoring-remote-write-ask");
172
+ break;
173
+ case "logging-category":
174
+ if (needsMonitoring)
175
+ setSubStep("monitoring-remote-write-ask");
176
+ else if (needsSSO)
177
+ setSubStep("sso-client-secret");
178
+ else if (needsAI)
179
+ setSubStep("openai-key");
180
+ else
181
+ onBack();
182
+ break;
183
+ case "logging-sink":
184
+ setSubStep("logging-category");
185
+ break;
186
+ case "logging-region":
187
+ case "logging-region-loading":
188
+ setSubStep("logging-sink");
189
+ break;
190
+ case "logging-bucket":
191
+ case "logging-bucket-loading":
192
+ setSubStep("logging-region");
193
+ break;
194
+ // Logging platform config steps
195
+ case "logging-datadog-config":
196
+ case "logging-splunk-config":
197
+ case "logging-elasticsearch-config":
198
+ case "logging-loki-config":
199
+ case "logging-newrelic-config":
200
+ case "logging-axiom-config":
201
+ setSubStep("logging-sink");
202
+ break;
203
+ // Email template steps
204
+ case "email-subject-invite":
205
+ if (needsLogging)
206
+ setSubStep("logging-category");
207
+ else if (needsMonitoring)
208
+ setSubStep("monitoring-remote-write-ask");
209
+ else if (needsSSO)
210
+ setSubStep("sso-client-secret");
211
+ else if (needsAI)
212
+ setSubStep("openai-key");
213
+ else
214
+ onBack();
215
+ break;
216
+ case "email-subject-confirm":
217
+ setSubStep("email-subject-invite");
218
+ break;
219
+ case "email-subject-recovery":
220
+ setSubStep("email-subject-confirm");
221
+ break;
222
+ case "email-subject-change":
223
+ setSubStep("email-subject-recovery");
224
+ break;
225
+ case "email-template-invite":
226
+ setSubStep("email-subject-change");
227
+ break;
228
+ case "email-template-confirm":
229
+ setSubStep("email-template-invite");
230
+ break;
231
+ case "email-template-recovery":
232
+ setSubStep("email-template-confirm");
233
+ break;
234
+ case "email-template-change":
235
+ setSubStep("email-template-recovery");
236
+ break;
237
+ }
238
+ };
239
+ const advanceToNext = (from) => {
240
+ switch (from) {
241
+ case "openai-key":
242
+ if (needsSSO)
243
+ setSubStep("sso-provider");
244
+ else if (needsMonitoring)
245
+ setSubStep("monitoring-remote-write-ask");
246
+ else if (needsLogging)
247
+ setSubStep("logging-category");
248
+ else if (needsCustomEmails)
249
+ setSubStep("email-subject-invite");
250
+ else
251
+ onComplete();
252
+ break;
253
+ case "sso-client-secret":
254
+ if (needsMonitoring)
255
+ setSubStep("monitoring-remote-write-ask");
256
+ else if (needsLogging)
257
+ setSubStep("logging-category");
258
+ else if (needsCustomEmails)
259
+ setSubStep("email-subject-invite");
260
+ else
261
+ onComplete();
262
+ break;
263
+ case "monitoring-remote-write-ask":
264
+ case "monitoring-remote-write-url":
265
+ if (needsLogging)
266
+ setSubStep("logging-category");
267
+ else if (needsCustomEmails)
268
+ setSubStep("email-subject-invite");
269
+ else
270
+ onComplete();
271
+ break;
272
+ case "logging-bucket":
273
+ // Cloud storage config complete, check for custom emails
274
+ if (needsCustomEmails)
275
+ setSubStep("email-subject-invite");
276
+ else
277
+ onComplete();
278
+ break;
279
+ case "logging-datadog-config":
280
+ case "logging-splunk-config":
281
+ case "logging-elasticsearch-config":
282
+ case "logging-loki-config":
283
+ case "logging-newrelic-config":
284
+ case "logging-axiom-config":
285
+ // Platform config complete, check for custom emails
286
+ if (needsCustomEmails)
287
+ setSubStep("email-subject-invite");
288
+ else
289
+ onComplete();
290
+ break;
291
+ case "email-template-change":
292
+ // All email config complete
293
+ onComplete();
294
+ break;
295
+ }
296
+ };
297
+ // === AI Configuration ===
298
+ const handleOpenAIKeySubmit = () => {
299
+ if (!openaiKey) {
300
+ setError("OpenAI API key is required for AI features");
301
+ return;
302
+ }
303
+ if (!openaiKey.startsWith("sk-")) {
304
+ setError('OpenAI API key should start with "sk-"');
305
+ return;
306
+ }
307
+ setError(null);
308
+ dispatch({ type: "SET_OPENAI_KEY", key: openaiKey });
309
+ advanceToNext("openai-key");
310
+ };
311
+ // === SSO Configuration ===
312
+ const handleSsoProviderSelect = (item) => {
313
+ const provider = item.value;
314
+ setSsoProvider(provider);
315
+ dispatch({ type: "SET_SSO_CONFIG", config: { ssoProvider: provider } });
316
+ if (provider === "google") {
317
+ setSubStep("sso-client-id");
318
+ }
319
+ else {
320
+ setSubStep("sso-url");
321
+ }
322
+ };
323
+ const handleSsoUrlSubmit = () => {
324
+ if (!ssoUrl) {
325
+ setError("SSO provider URL is required");
326
+ return;
327
+ }
328
+ try {
329
+ new URL(ssoUrl);
330
+ }
331
+ catch {
332
+ setError("Invalid URL format");
333
+ return;
334
+ }
335
+ setError(null);
336
+ setSubStep("sso-client-id");
337
+ };
338
+ const handleSsoClientIdSubmit = () => {
339
+ if (!ssoClientId) {
340
+ setError("Client ID is required");
341
+ return;
342
+ }
343
+ setError(null);
344
+ setSubStep("sso-client-secret");
345
+ };
346
+ const handleSsoClientSecretSubmit = () => {
347
+ if (!ssoClientSecret) {
348
+ setError("Client secret is required");
349
+ return;
350
+ }
351
+ setError(null);
352
+ dispatch({
353
+ type: "SET_SSO_CONFIG",
354
+ config: {
355
+ ssoProvider,
356
+ ssoUrl,
357
+ ssoClientId,
358
+ ssoClientSecret,
359
+ },
360
+ });
361
+ advanceToNext("sso-client-secret");
362
+ };
363
+ // === Monitoring Configuration ===
364
+ const handleRemoteWriteAsk = (item) => {
365
+ if (item.value) {
366
+ setSubStep("monitoring-remote-write-url");
367
+ }
368
+ else {
369
+ dispatch({ type: "SET_PROMETHEUS_REMOTE_WRITE", url: "" });
370
+ advanceToNext("monitoring-remote-write-ask");
371
+ }
372
+ };
373
+ const handleRemoteWriteUrlSubmit = () => {
374
+ if (remoteWriteUrl) {
375
+ try {
376
+ new URL(remoteWriteUrl);
377
+ }
378
+ catch {
379
+ setError("Invalid URL format");
380
+ return;
381
+ }
382
+ }
383
+ setError(null);
384
+ dispatch({ type: "SET_PROMETHEUS_REMOTE_WRITE", url: remoteWriteUrl });
385
+ advanceToNext("monitoring-remote-write-url");
386
+ };
387
+ // === Logging Configuration ===
388
+ // Map logging sink to cloud provider
389
+ const sinkToProvider = (sink) => {
390
+ if (sink === "s3")
391
+ return "aws";
392
+ if (sink === "azure-blob")
393
+ return "azure";
394
+ if (sink === "gcs")
395
+ return "gcp";
396
+ return null;
397
+ };
398
+ // Is sink a cloud storage type?
399
+ const isCloudStorageSink = (sink) => {
400
+ return sink === "s3" || sink === "azure-blob" || sink === "gcs";
401
+ };
402
+ // Step 1: Select logging category
403
+ const handleLoggingCategorySelect = (item) => {
404
+ const category = item.value;
405
+ setLoggingCategory(category);
406
+ setSubStep("logging-sink");
407
+ };
408
+ // Step 2: Select logging sink based on category
409
+ const handleLoggingSinkSelect = async (item) => {
410
+ const sink = item.value;
411
+ setLoggingSink(sink);
412
+ dispatch({ type: "SET_LOGGING_SINK", sink });
413
+ if (isCloudStorageSink(sink)) {
414
+ // Cloud storage: go to region selection
415
+ loadRegionsForLogging(sink);
416
+ }
417
+ else {
418
+ // Logging platform: go to platform-specific config
419
+ switch (sink) {
420
+ case "datadog":
421
+ setSubStep("logging-datadog-config");
422
+ break;
423
+ case "splunk":
424
+ setSubStep("logging-splunk-config");
425
+ break;
426
+ case "elasticsearch":
427
+ setSubStep("logging-elasticsearch-config");
428
+ break;
429
+ case "loki":
430
+ setSubStep("logging-loki-config");
431
+ break;
432
+ case "newrelic":
433
+ setSubStep("logging-newrelic-config");
434
+ break;
435
+ case "axiom":
436
+ setSubStep("logging-axiom-config");
437
+ break;
438
+ }
439
+ }
440
+ };
441
+ // Load regions for cloud storage
442
+ const loadRegionsForLogging = async (sink) => {
443
+ const provider = sinkToProvider(sink);
444
+ if (!provider) {
445
+ setSubStep("logging-region");
446
+ return;
447
+ }
448
+ setSubStep("logging-region-loading");
449
+ try {
450
+ const regions = await listRegions(provider);
451
+ if (regions.length > 0) {
452
+ setAvailableRegions(regions);
453
+ }
454
+ else {
455
+ setAvailableRegions(CLOUD_REGIONS[provider]);
456
+ }
457
+ }
458
+ catch {
459
+ setAvailableRegions(CLOUD_REGIONS[provider]);
460
+ }
461
+ setSubStep("logging-region");
462
+ };
463
+ // After region is selected, load buckets in that region
464
+ const handleLoggingRegionSelect = async (item) => {
465
+ setLoggingRegion(item.value);
466
+ dispatch({
467
+ type: "SET_LOGGING_CONFIG",
468
+ config: { loggingRegion: item.value },
469
+ });
470
+ // Now load buckets in this region
471
+ const provider = sinkToProvider(loggingSink);
472
+ if (provider) {
473
+ setSubStep("logging-bucket-loading");
474
+ try {
475
+ const buckets = await listBucketsInRegion(provider, item.value);
476
+ setAvailableBuckets(buckets);
477
+ }
478
+ catch {
479
+ setAvailableBuckets([]);
480
+ }
481
+ }
482
+ setSubStep("logging-bucket");
483
+ };
484
+ // Refresh bucket list
485
+ const refreshBuckets = async () => {
486
+ if (isRefreshing)
487
+ return;
488
+ const provider = sinkToProvider(loggingSink);
489
+ if (!provider || !loggingRegion)
490
+ return;
491
+ setIsRefreshing(true);
492
+ try {
493
+ const buckets = await listBucketsInRegion(provider, loggingRegion);
494
+ setAvailableBuckets(buckets);
495
+ }
496
+ catch {
497
+ // Keep existing list on error
498
+ }
499
+ setIsRefreshing(false);
500
+ };
501
+ // Select bucket (no create option)
502
+ const handleLoggingBucketSelect = (item) => {
503
+ setLoggingBucket(item.value);
504
+ dispatch({
505
+ type: "SET_LOGGING_CONFIG",
506
+ config: { loggingBucket: item.value },
507
+ });
508
+ advanceToNext("logging-bucket");
509
+ };
510
+ // === Logging Platform Config Handlers ===
511
+ const handleDatadogConfigSubmit = () => {
512
+ if (!datadogApiKey) {
513
+ setError("Datadog API key is required");
514
+ return;
515
+ }
516
+ setError(null);
517
+ dispatch({
518
+ type: "SET_LOGGING_CONFIG",
519
+ config: {
520
+ loggingBucket: datadogApiKey, // Repurpose bucket field for API key
521
+ loggingRegion: datadogSite, // Repurpose region field for site
522
+ },
523
+ });
524
+ advanceToNext("logging-datadog-config");
525
+ };
526
+ const handleSplunkConfigSubmit = () => {
527
+ if (!splunkUrl) {
528
+ setError("Splunk HEC URL is required");
529
+ return;
530
+ }
531
+ if (!splunkHecToken) {
532
+ setError("Splunk HEC token is required");
533
+ return;
534
+ }
535
+ try {
536
+ new URL(splunkUrl);
537
+ }
538
+ catch {
539
+ setError("Invalid URL format");
540
+ return;
541
+ }
542
+ setError(null);
543
+ dispatch({
544
+ type: "SET_LOGGING_CONFIG",
545
+ config: {
546
+ loggingBucket: splunkHecToken,
547
+ loggingRegion: splunkUrl,
548
+ },
549
+ });
550
+ advanceToNext("logging-splunk-config");
551
+ };
552
+ const handleElasticsearchConfigSubmit = () => {
553
+ if (!elasticsearchUrl) {
554
+ setError("Elasticsearch URL is required");
555
+ return;
556
+ }
557
+ try {
558
+ new URL(elasticsearchUrl);
559
+ }
560
+ catch {
561
+ setError("Invalid URL format");
562
+ return;
563
+ }
564
+ setError(null);
565
+ // Store as JSON in bucket field for complex config
566
+ dispatch({
567
+ type: "SET_LOGGING_CONFIG",
568
+ config: {
569
+ loggingBucket: JSON.stringify({
570
+ url: elasticsearchUrl,
571
+ user: elasticsearchUser,
572
+ password: elasticsearchPass,
573
+ index: elasticsearchIndex,
574
+ }),
575
+ loggingRegion: elasticsearchIndex,
576
+ },
577
+ });
578
+ advanceToNext("logging-elasticsearch-config");
579
+ };
580
+ const handleLokiConfigSubmit = () => {
581
+ if (!lokiUrl) {
582
+ setError("Loki URL is required");
583
+ return;
584
+ }
585
+ try {
586
+ new URL(lokiUrl);
587
+ }
588
+ catch {
589
+ setError("Invalid URL format");
590
+ return;
591
+ }
592
+ setError(null);
593
+ dispatch({
594
+ type: "SET_LOGGING_CONFIG",
595
+ config: {
596
+ loggingBucket: lokiUrl,
597
+ loggingRegion: "",
598
+ },
599
+ });
600
+ advanceToNext("logging-loki-config");
601
+ };
602
+ const handleNewrelicConfigSubmit = () => {
603
+ if (!newrelicLicenseKey) {
604
+ setError("New Relic License Key is required");
605
+ return;
606
+ }
607
+ if (!newrelicAccountId) {
608
+ setError("New Relic Account ID is required");
609
+ return;
610
+ }
611
+ setError(null);
612
+ dispatch({
613
+ type: "SET_LOGGING_CONFIG",
614
+ config: {
615
+ loggingBucket: newrelicLicenseKey,
616
+ loggingRegion: newrelicAccountId,
617
+ },
618
+ });
619
+ advanceToNext("logging-newrelic-config");
620
+ };
621
+ const handleAxiomConfigSubmit = () => {
622
+ if (!axiomApiToken) {
623
+ setError("Axiom API token is required");
624
+ return;
625
+ }
626
+ if (!axiomDataset) {
627
+ setError("Axiom dataset is required");
628
+ return;
629
+ }
630
+ setError(null);
631
+ dispatch({
632
+ type: "SET_LOGGING_CONFIG",
633
+ config: {
634
+ loggingBucket: axiomApiToken,
635
+ loggingRegion: axiomDataset,
636
+ },
637
+ });
638
+ advanceToNext("logging-axiom-config");
639
+ };
640
+ // === Custom Email Configuration Handlers ===
641
+ const handleEmailSubjectInviteSubmit = () => {
642
+ dispatch({
643
+ type: "SET_EMAIL_SUBJECTS",
644
+ subjects: { invite: emailSubjectInvite },
645
+ });
646
+ setSubStep("email-subject-confirm");
647
+ };
648
+ const handleEmailSubjectConfirmSubmit = () => {
649
+ dispatch({
650
+ type: "SET_EMAIL_SUBJECTS",
651
+ subjects: { confirmation: emailSubjectConfirm },
652
+ });
653
+ setSubStep("email-subject-recovery");
654
+ };
655
+ const handleEmailSubjectRecoverySubmit = () => {
656
+ dispatch({
657
+ type: "SET_EMAIL_SUBJECTS",
658
+ subjects: { recovery: emailSubjectRecovery },
659
+ });
660
+ setSubStep("email-subject-change");
661
+ };
662
+ const handleEmailSubjectChangeSubmit = () => {
663
+ dispatch({
664
+ type: "SET_EMAIL_SUBJECTS",
665
+ subjects: { emailChange: emailSubjectChange },
666
+ });
667
+ setSubStep("email-template-invite");
668
+ };
669
+ const validateUrl = (url) => {
670
+ if (!url)
671
+ return false;
672
+ try {
673
+ new URL(url);
674
+ return true;
675
+ }
676
+ catch {
677
+ return false;
678
+ }
679
+ };
680
+ const handleEmailTemplateInviteSubmit = () => {
681
+ if (!validateUrl(emailTemplateInvite)) {
682
+ setError("Please enter a valid URL for the invite template");
683
+ return;
684
+ }
685
+ setError(null);
686
+ dispatch({
687
+ type: "SET_EMAIL_TEMPLATES",
688
+ templates: { invite: emailTemplateInvite },
689
+ });
690
+ setSubStep("email-template-confirm");
691
+ };
692
+ const handleEmailTemplateConfirmSubmit = () => {
693
+ if (!validateUrl(emailTemplateConfirm)) {
694
+ setError("Please enter a valid URL for the confirmation template");
695
+ return;
696
+ }
697
+ setError(null);
698
+ dispatch({
699
+ type: "SET_EMAIL_TEMPLATES",
700
+ templates: { confirmation: emailTemplateConfirm },
701
+ });
702
+ setSubStep("email-template-recovery");
703
+ };
704
+ const handleEmailTemplateRecoverySubmit = () => {
705
+ if (!validateUrl(emailTemplateRecovery)) {
706
+ setError("Please enter a valid URL for the recovery template");
707
+ return;
708
+ }
709
+ setError(null);
710
+ dispatch({
711
+ type: "SET_EMAIL_TEMPLATES",
712
+ templates: { recovery: emailTemplateRecovery },
713
+ });
714
+ setSubStep("email-template-change");
715
+ };
716
+ const handleEmailTemplateChangeSubmit = () => {
717
+ if (!validateUrl(emailTemplateChange)) {
718
+ setError("Please enter a valid URL for the email change template");
719
+ return;
720
+ }
721
+ setError(null);
722
+ dispatch({
723
+ type: "SET_EMAIL_TEMPLATES",
724
+ templates: { emailChange: emailTemplateChange },
725
+ });
726
+ advanceToNext("email-template-change");
727
+ };
728
+ // Build bucket items for selection (no create option - just existing buckets)
729
+ const getBucketItems = () => {
730
+ return availableBuckets.map((b) => ({ label: b, value: b }));
731
+ };
732
+ // Get regions based on logging sink (use dynamic regions if available)
733
+ const getLoggingRegions = () => {
734
+ if (availableRegions.length > 0) {
735
+ return availableRegions.map((r) => ({ label: r, value: r }));
736
+ }
737
+ const provider = sinkToProvider(loggingSink);
738
+ if (!provider)
739
+ return [];
740
+ return CLOUD_REGIONS[provider].map((r) => ({ label: r, value: r }));
741
+ };
742
+ // If nothing to configure, don't render
743
+ if (!needsAI &&
744
+ !needsSSO &&
745
+ !needsMonitoring &&
746
+ !needsLogging &&
747
+ !needsCustomEmails) {
748
+ return null;
749
+ }
750
+ // Progress summary
751
+ const ProgressSummary = () => (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [state.openaiApiKey && (_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsx(Text, { color: "gray", children: " OpenAI API key configured" })] })), state.ssoProvider && (_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" SSO: ", state.ssoProvider] })] })), state.prometheusRemoteWriteUrl && (_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsx(Text, { color: "gray", children: " Metrics remote write configured" })] }))] }));
752
+ return (_jsxs(BorderBox, { title: "Feature Configuration", children: [subStep === "openai-key" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "OpenAI API Key" }), _jsx(Text, { color: "gray", dimColor: true, children: "Required for AI-powered rule generation features" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: openaiKey, onChange: setOpenaiKey, onSubmit: handleOpenAIKeySubmit, placeholder: "sk-...", mask: "*" }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Get your API key at: https://platform.openai.com/api-keys" }) })] })), subStep === "sso-provider" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "SSO Provider" }), _jsx(Text, { color: "gray", dimColor: true, children: "Select your identity provider" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: SSO_PROVIDERS, onSelect: handleSsoProviderSelect, itemComponent: ({ isSelected, label }) => (_jsx(Text, { color: isSelected ? colors.accent : undefined, children: label })) }) }), _jsx(ProgressSummary, {})] })), subStep === "sso-url" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsxs(Text, { bold: true, children: [ssoProvider?.toUpperCase(), " Provider URL"] }), _jsxs(Text, { color: "gray", dimColor: true, children: [ssoProvider === "azure" &&
753
+ "e.g., https://login.microsoftonline.com/your-tenant-id", ssoProvider === "okta" && "e.g., https://your-org.okta.com", ssoProvider === "keycloak" &&
754
+ "e.g., https://keycloak.example.com/realms/your-realm", ssoProvider === "ory" &&
755
+ "e.g., https://your-project.projects.oryapis.com", ssoProvider === "other" && "The base URL of your OIDC provider"] }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: ssoUrl, onChange: setSsoUrl, onSubmit: handleSsoUrlSubmit, placeholder: "https://..." }) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Provider: ", ssoProvider] })] })] })), subStep === "sso-client-id" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "OAuth Client ID" }), _jsx(Text, { color: "gray", dimColor: true, children: "The client/application ID from your identity provider" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: ssoClientId, onChange: setSsoClientId, onSubmit: handleSsoClientIdSubmit, placeholder: "your-client-id" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Provider: ", ssoProvider] })] }), ssoUrl && (_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" URL: ", ssoUrl] })] }))] })] })), subStep === "sso-client-secret" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "OAuth Client Secret" }), _jsx(Text, { color: "gray", dimColor: true, children: "The client secret from your identity provider" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: ssoClientSecret, onChange: setSsoClientSecret, onSubmit: handleSsoClientSecretSubmit, placeholder: "your-client-secret", mask: "*" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Provider: ", ssoProvider] })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsx(Text, { color: "gray", children: " Client ID configured" })] })] })] })), subStep === "monitoring-remote-write-ask" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Prometheus Remote Write" }), _jsx(Text, { color: "gray", dimColor: true, children: "Do you want to send metrics to an external monitoring system?" }), _jsx(Text, { color: "gray", dimColor: true, children: "(e.g., Datadog, Grafana Cloud, Chronosphere)" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: YES_NO_OPTIONS, onSelect: handleRemoteWriteAsk, itemComponent: ({ isSelected, label }) => (_jsx(Text, { color: isSelected ? colors.accent : undefined, children: label })) }) }), _jsx(ProgressSummary, {})] })), subStep === "monitoring-remote-write-url" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Remote Write URL" }), _jsx(Text, { color: "gray", dimColor: true, children: "Prometheus remote_write endpoint URL" }), _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: remoteWriteUrl, onChange: setRemoteWriteUrl, onSubmit: handleRemoteWriteUrlSubmit, placeholder: "https://metrics.example.com/api/v1/write" }) }), _jsx(ProgressSummary, {})] })), subStep === "logging-category" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "External Logging Destination" }), _jsx(Text, { color: "gray", dimColor: true, children: "Choose how you want to store decision logs (Console logging is always included)" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: LOGGING_CATEGORIES, onSelect: handleLoggingCategorySelect, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => (_jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [isSelected ? "❯ " : " ", label] })) }) }), _jsx(ProgressSummary, {})] })), subStep === "logging-sink" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: loggingCategory === "cloud-storage"
756
+ ? "Select Cloud Storage"
757
+ : "Select Logging Platform" }), _jsx(Text, { color: "gray", dimColor: true, children: loggingCategory === "cloud-storage"
758
+ ? "Store logs in cloud object storage"
759
+ : "Send logs to a centralized logging platform" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: loggingCategory === "cloud-storage"
760
+ ? CLOUD_STORAGE_SINKS
761
+ : LOGGING_PLATFORM_SINKS, onSelect: handleLoggingSinkSelect, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => (_jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [isSelected ? "❯ " : " ", label] })) }) }), state.infrastructureMode === "existing" &&
762
+ loggingCategory === "cloud-storage" && (_jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, children: _jsx(Text, { color: "yellow", children: "Note: Cloud storage requires IRSA/Workload Identity in your cluster." }) })), _jsx(ProgressSummary, {})] })), subStep === "logging-region-loading" && (_jsx(Box, { flexDirection: "column", marginY: 1, children: _jsx(Spinner, { label: "Loading available regions..." }) })), subStep === "logging-region" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsxs(Text, { bold: true, children: [loggingSink === "s3" && "Select AWS Region", loggingSink === "azure-blob" && "Select Azure Region", loggingSink === "gcs" && "Select GCP Region"] }), _jsx(Text, { color: "gray", dimColor: true, children: "Select the region where logs will be stored" }), _jsx(Box, { marginTop: 1, height: 10, flexDirection: "column", overflowY: "hidden", children: _jsx(SelectInput, { items: getLoggingRegions(), onSelect: handleLoggingRegionSelect, limit: 8, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => (_jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [isSelected ? "❯ " : " ", label] })) }) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" ", "Sink: ", LOGGING_SINK_INFO[loggingSink]?.name] })] })] })), subStep === "logging-bucket-loading" && (_jsx(Box, { flexDirection: "column", marginY: 1, children: _jsx(Spinner, { label: `Loading buckets in ${loggingRegion}...` }) })), subStep === "logging-bucket" && (_jsx(BucketSelector, { loggingSink: loggingSink, loggingRegion: loggingRegion, availableBuckets: availableBuckets, isRefreshing: isRefreshing, onSelect: handleLoggingBucketSelect, onRefresh: refreshBuckets, colors: colors })), subStep === "logging-datadog-config" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Datadog Configuration" }), _jsx(Text, { color: "gray", dimColor: true, children: "Configure Datadog Logs integration" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "API Key:" }), _jsx(Box, { children: _jsx(TextInput, { value: datadogApiKey, onChange: setDatadogApiKey, placeholder: "your-api-key", mask: "*" }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Datadog Site:" }), _jsx(SelectInput, { items: DATADOG_SITES, initialIndex: DATADOG_SITES.findIndex((s) => s.value === datadogSite), onSelect: (item) => setDatadogSite(item.value), indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => (_jsxs(Text, { color: isSelected ? colors.accent : undefined, children: [isSelected ? "❯ " : " ", label] })) })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Press Enter after selecting site to continue" }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: datadogApiKey ? colors.accent : colors.muted, bold: !!datadogApiKey, children: datadogApiKey
763
+ ? "→ Press Enter to continue"
764
+ : "Enter API key to continue" }) }), datadogApiKey && (_jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: "", onChange: () => { }, onSubmit: handleDatadogConfigSubmit, placeholder: "" }) }))] })), subStep === "logging-splunk-config" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Splunk HEC Configuration" }), _jsx(Text, { color: "gray", dimColor: true, children: "Configure Splunk HTTP Event Collector" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "HEC URL:" }), _jsx(Box, { children: _jsx(TextInput, { value: splunkUrl, onChange: setSplunkUrl, placeholder: "https://splunk.example.com:8088" }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "HEC Token:" }), _jsx(Box, { children: _jsx(TextInput, { value: splunkHecToken, onChange: setSplunkHecToken, onSubmit: handleSplunkConfigSubmit, placeholder: "your-hec-token", mask: "*" }) })] })] })), subStep === "logging-elasticsearch-config" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Elasticsearch Configuration" }), _jsx(Text, { color: "gray", dimColor: true, children: "Configure Elasticsearch logging destination" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Elasticsearch URL:" }), _jsx(Box, { children: _jsx(TextInput, { value: elasticsearchUrl, onChange: setElasticsearchUrl, placeholder: "https://elasticsearch.example.com:9200" }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Username (optional):" }), _jsx(Box, { children: _jsx(TextInput, { value: elasticsearchUser, onChange: setElasticsearchUser, placeholder: "elastic" }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Password (optional):" }), _jsx(Box, { children: _jsx(TextInput, { value: elasticsearchPass, onChange: setElasticsearchPass, placeholder: "", mask: "*" }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Index name:" }), _jsx(Box, { children: _jsx(TextInput, { value: elasticsearchIndex, onChange: setElasticsearchIndex, onSubmit: handleElasticsearchConfigSubmit, placeholder: "rulebricks-logs" }) })] })] })), subStep === "logging-loki-config" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Grafana Loki Configuration" }), _jsx(Text, { color: "gray", dimColor: true, children: "Configure Loki logging destination" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Loki URL:" }), _jsx(Text, { color: "gray", dimColor: true, children: "Include /loki/api/v1/push endpoint" }), _jsx(Box, { children: _jsx(TextInput, { value: lokiUrl, onChange: setLokiUrl, onSubmit: handleLokiConfigSubmit, placeholder: "https://loki.example.com/loki/api/v1/push" }) })] })] })), subStep === "logging-newrelic-config" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "New Relic Configuration" }), _jsx(Text, { color: "gray", dimColor: true, children: "Configure New Relic Logs integration" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "License Key:" }), _jsx(Box, { children: _jsx(TextInput, { value: newrelicLicenseKey, onChange: setNewrelicLicenseKey, placeholder: "your-license-key", mask: "*" }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Account ID:" }), _jsx(Box, { children: _jsx(TextInput, { value: newrelicAccountId, onChange: setNewrelicAccountId, onSubmit: handleNewrelicConfigSubmit, placeholder: "1234567" }) })] })] })), subStep === "logging-axiom-config" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Axiom Configuration" }), _jsx(Text, { color: "gray", dimColor: true, children: "Configure Axiom logging destination" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "API Token:" }), _jsx(Box, { children: _jsx(TextInput, { value: axiomApiToken, onChange: setAxiomApiToken, placeholder: "xaat-...", mask: "*" }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Dataset:" }), _jsx(Box, { children: _jsx(TextInput, { value: axiomDataset, onChange: setAxiomDataset, onSubmit: handleAxiomConfigSubmit, placeholder: "rulebricks" }) })] })] })), subStep === "email-subject-invite" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Custom Email Templates" }), _jsx(Text, { color: "gray", dimColor: true, children: "Customize Supabase auth email subjects and templates." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "First, let's customize the subject lines. Press Enter to use defaults." }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Invite Email Subject:" }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Default: \"", DEFAULT_EMAIL_SUBJECTS.invite, "\""] }), _jsx(Box, { children: _jsx(TextInput, { value: emailSubjectInvite, onChange: setEmailSubjectInvite, onSubmit: handleEmailSubjectInviteSubmit, placeholder: DEFAULT_EMAIL_SUBJECTS.invite }) })] })] })), subStep === "email-subject-confirm" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Custom Email Templates - Subject Lines" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Confirmation Email Subject:" }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Default: \"", DEFAULT_EMAIL_SUBJECTS.confirmation, "\""] }), _jsx(Box, { children: _jsx(TextInput, { value: emailSubjectConfirm, onChange: setEmailSubjectConfirm, onSubmit: handleEmailSubjectConfirmSubmit, placeholder: DEFAULT_EMAIL_SUBJECTS.confirmation }) })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Invite: ", emailSubjectInvite] })] })] })), subStep === "email-subject-recovery" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Custom Email Templates - Subject Lines" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Password Recovery Email Subject:" }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Default: \"", DEFAULT_EMAIL_SUBJECTS.recovery, "\""] }), _jsx(Box, { children: _jsx(TextInput, { value: emailSubjectRecovery, onChange: setEmailSubjectRecovery, onSubmit: handleEmailSubjectRecoverySubmit, placeholder: DEFAULT_EMAIL_SUBJECTS.recovery }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Invite: ", emailSubjectInvite] })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Confirmation: ", emailSubjectConfirm] })] })] })] })), subStep === "email-subject-change" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Custom Email Templates - Subject Lines" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Email Change Subject:" }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Default: \"", DEFAULT_EMAIL_SUBJECTS.emailChange, "\""] }), _jsx(Box, { children: _jsx(TextInput, { value: emailSubjectChange, onChange: setEmailSubjectChange, onSubmit: handleEmailSubjectChangeSubmit, placeholder: DEFAULT_EMAIL_SUBJECTS.emailChange }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Invite: ", emailSubjectInvite] })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Confirmation: ", emailSubjectConfirm] })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Recovery: ", emailSubjectRecovery] })] })] })] })), subStep === "email-template-invite" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Custom Email Templates - Template URLs" }), _jsx(Text, { color: "gray", dimColor: true, children: "Provide URLs to your custom HTML email templates." }), _jsx(Text, { color: "gray", dimColor: true, children: "Templates must be publicly accessible (S3, GCS, or any HTTPS URL)." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Invite Template URL:" }), _jsx(Box, { children: _jsx(TextInput, { value: emailTemplateInvite, onChange: setEmailTemplateInvite, onSubmit: handleEmailTemplateInviteSubmit, placeholder: "https://bucket.s3.amazonaws.com/templates/invite.html" }) })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsx(Text, { color: "gray", children: " All subject lines configured" })] })] })), subStep === "email-template-confirm" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Custom Email Templates - Template URLs" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Confirmation Template URL:" }), _jsx(Box, { children: _jsx(TextInput, { value: emailTemplateConfirm, onChange: setEmailTemplateConfirm, onSubmit: handleEmailTemplateConfirmSubmit, placeholder: "https://bucket.s3.amazonaws.com/templates/verify.html" }) })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Invite: ", emailTemplateInvite] })] })] })), subStep === "email-template-recovery" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Custom Email Templates - Template URLs" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Recovery Template URL:" }), _jsx(Box, { children: _jsx(TextInput, { value: emailTemplateRecovery, onChange: setEmailTemplateRecovery, onSubmit: handleEmailTemplateRecoverySubmit, placeholder: "https://bucket.s3.amazonaws.com/templates/password_change.html" }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Invite: ", emailTemplateInvite] })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Confirmation: ", emailTemplateConfirm] })] })] })] })), subStep === "email-template-change" && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Custom Email Templates - Template URLs" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Email Change Template URL:" }), _jsx(Box, { children: _jsx(TextInput, { value: emailTemplateChange, onChange: setEmailTemplateChange, onSubmit: handleEmailTemplateChangeSubmit, placeholder: "https://bucket.s3.amazonaws.com/templates/email_change.html" }) })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Invite: ", emailTemplateInvite] })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Confirmation: ", emailTemplateConfirm] })] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.success, children: "\u2713" }), _jsxs(Text, { color: "gray", children: [" Recovery: ", emailTemplateRecovery] })] })] })] })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["\u2717 ", error] }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Esc to go back \u2022 Enter to continue" }) })] }));
765
+ }