This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 459ea9f59b0d CAMEL-24259: camel-tui - Add Secrets tab for vault 
providers
459ea9f59b0d is described below

commit 459ea9f59b0d0f1bc233577ecb57da45fcce387c
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Jul 28 11:50:51 2026 +0200

    CAMEL-24259: camel-tui - Add Secrets tab for vault providers
    
    Add a Secrets tab to the TUI that shows secrets from all configured
    vault providers (AWS, Azure, GCP, HashiCorp, IBM, Kubernetes) in a
    single view. Press f to force reload secrets from the vault providers.
    
    Also add PeriodTaskScheduler.getTasks() API to enumerate scheduled
    tasks, and a vault-refresh action in LocalCliConnector that triggers
    all vault reload tasks using the @PeriodicTask annotation.
    
    CAMEL-24259: camel-tui - Fix SecretsTab import and ConsumersTab render test 
width
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../org/apache/camel/spi/PeriodTaskScheduler.java  |  13 ++
 .../impl/engine/DefaultPeriodTaskScheduler.java    |  14 ++
 .../camel/cli/connector/LocalCliConnector.java     |  17 ++
 .../jbang/core/commands/tui/IntegrationInfo.java   |   1 +
 .../dsl/jbang/core/commands/tui/SecretsTab.java    | 235 +++++++++++++++++++++
 .../dsl/jbang/core/commands/tui/StatusParser.java  |  71 +++++++
 .../dsl/jbang/core/commands/tui/TabRegistry.java   |   7 +
 .../dsl/jbang/core/commands/tui/TuiIcons.java      |   1 +
 .../jbang/core/commands/tui/VaultSecretInfo.java   |  26 +++
 .../core/commands/tui/ConsumersTabRenderTest.java  |   2 +-
 10 files changed, 386 insertions(+), 1 deletion(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/PeriodTaskScheduler.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/PeriodTaskScheduler.java
index e0b3eb6577b2..82123d1b7981 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/PeriodTaskScheduler.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/PeriodTaskScheduler.java
@@ -16,6 +16,9 @@
  */
 package org.apache.camel.spi;
 
+import java.util.Collection;
+import java.util.Collections;
+
 import org.jspecify.annotations.Nullable;
 
 /**
@@ -55,4 +58,14 @@ public interface PeriodTaskScheduler {
      */
     <T> @Nullable T getTaskByType(Class<T> type);
 
+    /**
+     * Gets all scheduled tasks.
+     *
+     * @return the tasks, or an empty collection if no tasks are scheduled
+     * @since  4.22
+     */
+    default Collection<Runnable> getTasks() {
+        return Collections.emptyList();
+    }
+
 }
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultPeriodTaskScheduler.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultPeriodTaskScheduler.java
index 24d5b26c3a75..d2f8ffb9a0fa 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultPeriodTaskScheduler.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultPeriodTaskScheduler.java
@@ -16,6 +16,9 @@
  */
 package org.apache.camel.impl.engine;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 
@@ -57,6 +60,17 @@ public final class DefaultPeriodTaskScheduler extends 
TimerListenerManager imple
         return null;
     }
 
+    @Override
+    public Collection<Runnable> getTasks() {
+        List<Runnable> answer = new ArrayList<>();
+        for (TimerListener listener : getListeners()) {
+            if (listener instanceof TaskWrapper wrapper) {
+                answer.add(wrapper.getTask());
+            }
+        }
+        return answer;
+    }
+
     @Override
     public void addTimerListener(TimerListener listener) {
         if (listener instanceof TaskWrapper) {
diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index f25af3e88f69..c3c25b790d5b 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -75,12 +75,14 @@ import org.apache.camel.spi.CliConnectorFactory;
 import org.apache.camel.spi.ContextReloadStrategy;
 import org.apache.camel.spi.EndpointUriFactory;
 import org.apache.camel.spi.Language;
+import org.apache.camel.spi.PeriodTaskScheduler;
 import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.spi.ResourceReloadStrategy;
 import org.apache.camel.spi.RoutesLoader;
 import org.apache.camel.spi.RuntimeEndpointRegistry;
 import org.apache.camel.spi.ShutdownPrepared;
+import org.apache.camel.spi.annotations.PeriodicTask;
 import org.apache.camel.support.LoadOnDemandReloadStrategy;
 import org.apache.camel.support.MessageHelper;
 import org.apache.camel.support.PatternHelper;
@@ -396,6 +398,8 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
                 doActionTypeConvertersTask();
             } else if ("transformers".equals(action)) {
                 doActionTransformersTask();
+            } else if ("vault-refresh".equals(action)) {
+                doActionVaultRefreshTask();
             }
         } catch (Exception e) {
             LOG.warn("Error executing action: {} due to: {}. This exception is 
ignored.", action != null ? action : af,
@@ -1921,6 +1925,19 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
         return null;
     }
 
+    private void doActionVaultRefreshTask() {
+        PeriodTaskScheduler scheduler = 
PluginHelper.getPeriodTaskScheduler(camelContext);
+        if (scheduler == null) {
+            return;
+        }
+        for (Runnable task : scheduler.getTasks()) {
+            PeriodicTask pt = 
task.getClass().getAnnotation(PeriodicTask.class);
+            if (pt != null && pt.value().endsWith("-refresh")) {
+                task.run();
+            }
+        }
+    }
+
     private JsonObject collectVaults() {
         JsonObject root = new JsonObject();
         // aws-secrets is optional
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
index 177dc100cb88..9fcc784d5dad 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
@@ -114,4 +114,5 @@ class IntegrationInfo {
     Set<String> devConsoles = Set.of();
     boolean hasBrowseableEndpoints;
     int transformerCount;
+    final List<VaultSecretInfo> vaultSecrets = new ArrayList<>();
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SecretsTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SecretsTab.java
new file mode 100644
index 000000000000..bbb8b9de6604
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SecretsTab.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import dev.tamboui.layout.Constraint;
+import dev.tamboui.layout.Rect;
+import dev.tamboui.style.Style;
+import dev.tamboui.terminal.Frame;
+import dev.tamboui.text.Span;
+import dev.tamboui.tui.event.KeyEvent;
+import dev.tamboui.widgets.block.Block;
+import dev.tamboui.widgets.block.BorderType;
+import dev.tamboui.widgets.block.Borders;
+import dev.tamboui.widgets.table.Cell;
+import dev.tamboui.widgets.table.Row;
+import dev.tamboui.widgets.table.Table;
+import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+
+import static org.apache.camel.dsl.jbang.core.commands.tui.TuiHelper.*;
+
+class SecretsTab extends AbstractTableTab {
+
+    SecretsTab(MonitorContext ctx) {
+        super(ctx, "vault", "secret");
+    }
+
+    @Override
+    protected int getRowCount() {
+        IntegrationInfo info = ctx.findSelectedIntegration();
+        return info != null ? info.vaultSecrets.size() : 0;
+    }
+
+    @Override
+    protected void renderContent(Frame frame, Rect area, IntegrationInfo info) 
{
+        List<VaultSecretInfo> sorted = new ArrayList<>(info.vaultSecrets);
+        sorted.sort(this::sortSecret);
+
+        List<Row> rows = new ArrayList<>();
+        for (VaultSecretInfo vi : sorted) {
+            rows.add(Row.from(
+                    Cell.from(Span.styled(" " + (vi.vault != null ? vi.vault : 
""), Style.EMPTY.fg(Theme.accent()))),
+                    Cell.from(vi.region != null ? vi.region : ""),
+                    Cell.from(vi.secret != null ? vi.secret : ""),
+                    Cell.from(printSince(vi.timestamp)),
+                    Cell.from(printSince(vi.lastReload)),
+                    Cell.from(printSince(vi.lastCheck))));
+        }
+
+        if (rows.isEmpty()) {
+            rows.add(emptyRow("No secrets", 6));
+        }
+
+        Table table = Table.builder()
+                .rows(rows)
+                .header(Row.from(
+                        Cell.from(Span.styled(" " + sortLabel("VAULT", 
"vault"), sortStyle("vault"))),
+                        Cell.from(Span.styled("REGION", Style.EMPTY.bold())),
+                        Cell.from(Span.styled(sortLabel("SECRET", "secret"), 
sortStyle("secret"))),
+                        Cell.from(Span.styled("AGE", Style.EMPTY.bold())),
+                        Cell.from(Span.styled("UPDATE", Style.EMPTY.bold())),
+                        Cell.from(Span.styled("CHECK", Style.EMPTY.bold()))))
+                .widths(
+                        Constraint.length(16),
+                        Constraint.length(16),
+                        Constraint.fill(),
+                        Constraint.length(10),
+                        Constraint.length(10),
+                        Constraint.length(10))
+                .highlightStyle(Theme.selectionBg())
+                .highlightSpacing(Table.HighlightSpacing.ALWAYS)
+                
.block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL)
+                        .title(" Secrets ").build())
+                .build();
+
+        lastTableArea = area;
+        frame.renderStatefulWidget(table, area, tableState);
+        renderScrollbar(frame, sorted.size());
+    }
+
+    @Override
+    protected boolean handleTabKeyEvent(KeyEvent ke) {
+        if (ke.isChar('f')) {
+            IntegrationInfo info = ctx.findSelectedIntegration();
+            if (info != null && info.pid != null) {
+                JsonObject root = new JsonObject();
+                root.put("action", "vault-refresh");
+                ctx.fireAction(info.pid, root);
+                if (ctx.notificationCallback != null) {
+                    ctx.notificationCallback.accept("Secrets reload in 
progress", false);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private int sortSecret(VaultSecretInfo a, VaultSecretInfo b) {
+        int result = switch (sort) {
+            case "secret" -> {
+                String sa = a.secret != null ? a.secret : "";
+                String sb = b.secret != null ? b.secret : "";
+                yield sa.compareToIgnoreCase(sb);
+            }
+            default -> { // "vault"
+                String va = a.vault != null ? a.vault : "";
+                String vb = b.vault != null ? b.vault : "";
+                int c = va.compareToIgnoreCase(vb);
+                if (c == 0) {
+                    String sa = a.secret != null ? a.secret : "";
+                    String sb = b.secret != null ? b.secret : "";
+                    c = sa.compareToIgnoreCase(sb);
+                }
+                yield c;
+            }
+        };
+        return sortReversed ? -result : result;
+    }
+
+    private static String printSince(long timestamp) {
+        if (timestamp == 0) {
+            return "";
+        }
+        return TimeUtils.printSince(timestamp);
+    }
+
+    @Override
+    public SelectionContext getSelectionContext() {
+        IntegrationInfo info = ctx.findSelectedIntegration();
+        if (info == null || info.vaultSecrets.isEmpty()) {
+            return null;
+        }
+        List<VaultSecretInfo> sorted = new ArrayList<>(info.vaultSecrets);
+        sorted.sort(this::sortSecret);
+        List<String> items = sorted.stream()
+                .map(v -> (v.vault != null ? v.vault : "") + "/" + (v.secret 
!= null ? v.secret : ""))
+                .toList();
+        Integer sel = tableState.selected();
+        return new SelectionContext("table", items, sel != null ? sel : -1, 
items.size(), "Secrets");
+    }
+
+    @Override
+    public String description() {
+        return "Secrets from cloud vault providers (AWS, Azure, GCP, 
Kubernetes, HashiCorp, IBM)";
+    }
+
+    @Override
+    public String getHelpText() {
+        return """
+                # Secrets
+
+                Shows secrets resolved from cloud vault providers. Camel can 
load
+                configuration values from external secret managers instead of
+                hardcoding them in properties files.
+
+                Supported providers: AWS Secrets Manager, Azure Key Vault,
+                GCP Secret Manager, HashiCorp Vault, IBM Secrets Manager,
+                Kubernetes Secrets, and Kubernetes ConfigMaps.
+
+                ## Table Columns
+
+                - **VAULT** — The cloud provider name (AWS, Azure, GCP, 
Hashicorp, IBM, Kubernetes, Kubernetes-cm)
+                - **REGION** — Cloud region (AWS only, blank for other 
providers)
+                - **SECRET** — The secret name/key as referenced in the route 
configuration
+                - **AGE** — How long ago the secret value was last updated
+                - **UPDATE** — How long ago the last automatic reload of 
secrets occurred
+                - **CHECK** — How long ago the last check for secret changes 
was performed
+
+                ## Automatic Refresh
+
+                Camel can automatically detect secret changes and reload them:
+                - **AWS** — monitors via CloudTrail events
+                - **Azure** — monitors via Event Hubs
+                - **GCP** — monitors via Pub/Sub
+                - **IBM** — monitors via Event Streams (Kafka)
+                - **Kubernetes** — watches the Kubernetes API
+
+                When refresh is enabled, the UPDATE and CHECK columns show the
+                timing of these automatic operations.
+
+                ## Keys
+
+                - `Up/Down` — select secret
+                - `f` — force reload secrets from vault providers
+                - `s` — cycle sort column
+                - `S` — reverse sort order
+                """;
+    }
+
+    @Override
+    public JsonObject getTableDataAsJson() {
+        IntegrationInfo info = ctx.findSelectedIntegration();
+        if (info == null) {
+            return null;
+        }
+        JsonObject result = new JsonObject();
+        result.put("tab", "Secrets");
+        JsonArray rows = new JsonArray();
+        List<VaultSecretInfo> sorted = new ArrayList<>(info.vaultSecrets);
+        sorted.sort(this::sortSecret);
+        for (VaultSecretInfo vi : sorted) {
+            JsonObject row = new JsonObject();
+            row.put("vault", vi.vault);
+            row.put("region", vi.region);
+            row.put("secret", vi.secret);
+            row.put("age", printSince(vi.timestamp));
+            row.put("update", printSince(vi.lastReload));
+            row.put("check", printSince(vi.lastCheck));
+            rows.add(row);
+        }
+        result.put("rows", rows);
+        result.put("totalRows", info.vaultSecrets.size());
+        Integer sel = tableState.selected();
+        result.put("selectedIndex", sel != null ? sel : -1);
+        return result;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
index 493a8f556f58..eed18d345032 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
@@ -719,6 +719,18 @@ final class StatusParser {
             info.transformerCount = trObj.getIntegerOrDefault("size", 0);
         }
 
+        // Parse vaults / secrets
+        JsonObject vaults = (JsonObject) root.get("vaults");
+        if (vaults != null) {
+            parseVaultSecrets(vaults, "aws-secrets", "AWS", info, true);
+            parseVaultSecrets(vaults, "gcp-secrets", "GCP", info, false);
+            parseVaultSecrets(vaults, "azure-secrets", "Azure", info, false);
+            parseVaultSecrets(vaults, "ibm-secrets", "IBM", info, false);
+            parseVaultSecrets(vaults, "kubernetes-secrets", "Kubernetes", 
info, false);
+            parseVaultConfigmaps(vaults, info);
+            parseVaultHashicorp(vaults, info);
+        }
+
         return info;
     }
 
@@ -1367,6 +1379,65 @@ final class StatusParser {
         return TuiHelper.objToLong(o);
     }
 
+    private static void parseVaultSecrets(
+            JsonObject vaults, String key, String vaultName, IntegrationInfo 
info, boolean hasRegion) {
+        JsonObject vo = (JsonObject) vaults.get(key);
+        if (vo == null) {
+            return;
+        }
+        long lastCheck = vo.getLongOrDefault("lastCheckTimestamp", 0);
+        long lastReload = vo.getLongOrDefault("lastReloadTimestamp", 0);
+        String region = hasRegion ? vo.getString("region") : null;
+        JsonArray arr = (JsonArray) vo.get("secrets");
+        if (arr != null) {
+            for (int i = 0; i < arr.size(); i++) {
+                JsonObject jo = (JsonObject) arr.get(i);
+                VaultSecretInfo vi = new VaultSecretInfo();
+                vi.vault = vaultName;
+                vi.region = region;
+                vi.secret = jo.getString("name");
+                vi.timestamp = jo.getLongOrDefault("timestamp", 0);
+                vi.lastCheck = lastCheck;
+                vi.lastReload = lastReload;
+                info.vaultSecrets.add(vi);
+            }
+        }
+    }
+
+    private static void parseVaultConfigmaps(JsonObject vaults, 
IntegrationInfo info) {
+        JsonObject vo = (JsonObject) vaults.get("kubernetes-configmaps");
+        if (vo == null) {
+            return;
+        }
+        long lastCheck = vo.getLongOrDefault("startCheckTimestamp", 0);
+        long lastReload = vo.getLongOrDefault("lastReloadTimestamp", 0);
+        JsonArray arr = (JsonArray) vo.get("configmap");
+        if (arr != null) {
+            for (int i = 0; i < arr.size(); i++) {
+                JsonObject jo = (JsonObject) arr.get(i);
+                VaultSecretInfo vi = new VaultSecretInfo();
+                vi.vault = "Kubernetes-cm";
+                vi.secret = jo.getString("name");
+                vi.timestamp = jo.getLongOrDefault("timestamp", 0);
+                vi.lastCheck = lastCheck;
+                vi.lastReload = lastReload;
+                info.vaultSecrets.add(vi);
+            }
+        }
+    }
+
+    private static void parseVaultHashicorp(JsonObject vaults, IntegrationInfo 
info) {
+        JsonObject vo = (JsonObject) vaults.get("hashicorp-secrets");
+        if (vo == null) {
+            return;
+        }
+        VaultSecretInfo vi = new VaultSecretInfo();
+        vi.vault = "Hashicorp";
+        vi.lastCheck = vo.getLongOrDefault("startCheckTimestamp", 0);
+        vi.lastReload = vo.getLongOrDefault("lastReloadTimestamp", 0);
+        info.vaultSecrets.add(vi);
+    }
+
     private static void parseEventArray(JsonObject eventsObj, String arrayKey, 
String category, IntegrationInfo info) {
         JsonArray arr = (JsonArray) eventsObj.get(arrayKey);
         if (arr != null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
index 0e930a47ef7a..c7a53b6c8d7a 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
@@ -107,6 +107,7 @@ class TabRegistry {
     private SqlTraceTab sqlTraceTab;
     private TypeConvertersTab typeConvertersTab;
     private TransformersTab transformersTab;
+    private SecretsTab secretsTab;
 
     private MonitorTab activeMoreTab;
     private List<MoreTab> moreTabs;
@@ -136,6 +137,7 @@ class TabRegistry {
         sqlTraceTab = new SqlTraceTab(ctx);
         typeConvertersTab = new TypeConvertersTab(ctx);
         transformersTab = new TransformersTab(ctx);
+        secretsTab = new SecretsTab(ctx);
         endpointsTab = new EndpointsTab(ctx, dataService.metrics());
         networkTab = new NetworkTab(ctx, dataService.metrics());
         httpTab = new HttpTab(ctx);
@@ -215,6 +217,11 @@ class TabRegistry {
                 new MoreTab(
                         TuiIcons.TAB_KAFKA, "Kafka", "&Kafka", kafkaTab, 
"Data",
                         List.of("kafka")),
+                new MoreTab(
+                        TuiIcons.TAB_SECRETS, "Secrets", "&Secrets", 
secretsTab, "Data",
+                        List.of("aws-secrets", "azure-secrets", "gcp-secrets", 
"hashicorp-secrets",
+                                "ibm-secrets", "kubernetes-secrets", 
"kubernetes-configmaps"),
+                        info -> !info.vaultSecrets.isEmpty()),
                 new MoreTab(
                         TuiIcons.TAB_SQL_QUERY, "SQL Query", "S&QL Query", 
sqlQueryTab, "Data",
                         List.of(), info -> !info.dataSources.isEmpty()),
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java
index 4da9429eebb7..a57aacbae276 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java
@@ -153,6 +153,7 @@ final class TuiIcons {
     static final String TAB_RECOVERY_TASKS = "🔁";
     static final String TAB_ROUTE_CONTROLLER = "🚦";
     static final String TAB_TYPE_CONVERTERS = "🔄";
+    static final String TAB_SECRETS = "🔑";
     static final String TAB_TRANSFORMERS = "🔀";
 
     /** Icons for {@link TabRegistry#TAB_OVERVIEW}..{@link 
TabRegistry#TAB_MORE} (in order). */
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/VaultSecretInfo.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/VaultSecretInfo.java
new file mode 100644
index 000000000000..ec093801eb2f
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/VaultSecretInfo.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+class VaultSecretInfo {
+    String vault;
+    String region;
+    String secret;
+    long timestamp;
+    long lastCheck;
+    long lastReload;
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTabRenderTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTabRenderTest.java
index c5bfc716773a..64d7836740d2 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTabRenderTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ConsumersTabRenderTest.java
@@ -59,7 +59,7 @@ class ConsumersTabRenderTest {
         addConsumer("route1", "timer://hello", "Started", 
"org.apache.camel.component.timer.TimerConsumer");
 
         ConsumersTab tab = new ConsumersTab(ctx);
-        String rendered = TuiTestHelper.renderToString(tab, 120, 20);
+        String rendered = TuiTestHelper.renderToString(tab, 160, 20);
 
         assertTrue(rendered.contains("ROUTE"), "Should show ROUTE header");
         assertTrue(rendered.contains("STATUS"), "Should show STATUS header");

Reply via email to