From 344ad4aadc2c7de5f21c0e17a47abb52f006a08a Mon Sep 17 00:00:00 2001 From: jarek Date: Fri, 14 Aug 2026 10:39:08 +0200 Subject: [PATCH] external vault providers - live probing secrets when editing stack --- src/lib/components/StackEnvVarsPanel.svelte | 21 +++++++++++++++------ src/lib/utils/invault-markers.ts | 13 +++++++++++++ src/routes/stacks/StackModal.svelte | 1 + 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/lib/components/StackEnvVarsPanel.svelte b/src/lib/components/StackEnvVarsPanel.svelte index 3e2d05c..17ffb0c 100644 --- a/src/lib/components/StackEnvVarsPanel.svelte +++ b/src/lib/components/StackEnvVarsPanel.svelte @@ -8,6 +8,7 @@ import * as Tooltip from '$lib/components/ui/tooltip'; import { getProviderIcon } from '$lib/components/provider-icons'; import { providerTypeLabel } from '../../routes/settings/secrets/ProviderModal.svelte'; + import { effectiveMissing } from '$lib/utils/invault-markers'; interface Props { variables: EnvVar[]; // Bindable - ALL variables (secrets + non-secrets) @@ -27,6 +28,9 @@ providerName?: string | null; /** Set when the live provider probe failed - shown as an amber line. */ probeError?: string | null; + /** Key NAMES the live probe found in the bound provider (present RIGHT NOW). + * These are not "missing" even without a local value. Empty on probe failure. */ + providerKeySet?: Set; showInterpolationHint?: boolean; theme?: 'light' | 'dark'; class?: string; @@ -49,6 +53,7 @@ providerType = null, providerName = null, probeError = null, + providerKeySet = new Set(), showInterpolationHint = false, theme = 'dark', class: className = '', @@ -57,13 +62,17 @@ }: Props = $props(); - // Provider-injected keys are supplied at deploy from the bound secret provider, so - // they are NOT "missing" even when a ${VAR} in compose has no local value. Drop them - // from the missing set (and expose them separately so the editor can mark them). - const injectedSet = $derived(new Set(injectedSecretKeys)); + // A ${VAR} the bound provider currently supplies (LIVE probe) is NOT "missing" even + // without a local value. Drop those from the panel's missing count / "Add missing" + // list so the panel matches the editor's IN VAULT markers - same source, one truth. + // This is purely live: the last-deploy injected names drive only the banner, never + // this set. A failed probe leaves providerKeySet empty, so those keys stay missing. const effectiveValidation = $derived.by(() => { - if (!validation || injectedSet.size === 0) return validation; - return { ...validation, missing: validation.missing.filter((k) => !injectedSet.has(k)) }; + if (!validation || providerKeySet.size === 0) return validation; + return { + ...validation, + missing: effectiveMissing(validation.missing, providerKeySet) + }; }); const STORAGE_KEY_VIEW_MODE = 'dockhand-env-vars-view-mode'; diff --git a/src/lib/utils/invault-markers.ts b/src/lib/utils/invault-markers.ts index 87d251f..17a506c 100644 --- a/src/lib/utils/invault-markers.ts +++ b/src/lib/utils/invault-markers.ts @@ -25,6 +25,19 @@ export function classifyMarker( return 'missing'; } +/** + * The panel's "missing" set: compose-required vars with no local value, MINUS the + * ones the bound provider currently supplies (the LIVE probe set). This is the SAME + * source the editor's IN VAULT markers use, so panel and editor always agree. The + * last-deploy injected names are deliberately NOT consulted here - a var is dropped + * from "missing" only because the provider has it RIGHT NOW, never because it was + * injected at a past deploy (a failed probe leaves the set empty -> stays missing, + * never a false green). + */ +export function effectiveMissing(missing: string[], providerKeys: Set): string[] { + return missing.filter((k) => !providerKeys.has(k)); +} + /** * Given the inline op:// (or other provider) references the client submitted as * `{ varName, ref }` pairs and the SUBSET of ref STRINGS the provider resolved, diff --git a/src/routes/stacks/StackModal.svelte b/src/routes/stacks/StackModal.svelte index ea9eaeb..709cb34 100644 --- a/src/routes/stacks/StackModal.svelte +++ b/src/routes/stacks/StackModal.svelte @@ -1862,6 +1862,7 @@ providerType={selectedProviderType} providerName={selectedProviderName} {probeError} + {providerKeySet} {readonly} onchange={() => { markDirty(); debouncedValidate(); }} theme={editorTheme}