feat(autofill): consolidated tool-params & sub-block store for one unified store that handles sub-block values (#237)

This commit is contained in:
Waleed Latif
2025-04-09 21:41:54 -07:00
committed by GitHub
parent 4f9c70b9d4
commit 23abb29b88
8 changed files with 275 additions and 228 deletions

View File

@@ -13,7 +13,7 @@ import { OAuthProvider } from '@/lib/oauth'
import { cn } from '@/lib/utils'
import { useCustomToolsStore } from '@/stores/custom-tools/store'
import { useGeneralStore } from '@/stores/settings/general/store'
import { useToolParamsStore } from '@/stores/tool-params/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import { getAllBlocks } from '@/blocks'
import { getTool } from '@/tools'
@@ -122,8 +122,12 @@ const getOperationOptions = (blockType: string): { label: string; id: string }[]
const initializeToolParams = (
toolId: string,
params: ToolParam[],
toolParamsStore: {
resolveParamValue: (toolId: string, paramId: string, instanceId?: string) => string | undefined
subBlockStore: {
resolveToolParamValue: (
toolId: string,
paramId: string,
instanceId?: string
) => string | undefined
},
isAutoFillEnabled: boolean,
instanceId?: string
@@ -134,7 +138,7 @@ const initializeToolParams = (
if (isAutoFillEnabled) {
// For each parameter, check if we have a stored/resolved value
params.forEach((param) => {
const resolvedValue = toolParamsStore.resolveParamValue(toolId, param.id, instanceId)
const resolvedValue = subBlockStore.resolveToolParamValue(toolId, param.id, instanceId)
if (resolvedValue) {
initialParams[param.id] = resolvedValue
}
@@ -152,7 +156,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
const [searchQuery, setSearchQuery] = useState('')
const isWide = useWorkflowStore((state) => state.blocks[blockId]?.isWide)
const customTools = useCustomToolsStore((state) => state.getAllTools())
const toolParamsStore = useToolParamsStore()
const subBlockStore = useSubBlockStore()
const isAutoFillEnvVarsEnabled = useGeneralStore((state) => state.isAutoFillEnvVarsEnabled)
const toolBlocks = getAllBlocks().filter((block) => block.category === 'tools')
@@ -194,7 +198,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
const initialParams = initializeToolParams(
toolId,
requiredParams,
toolParamsStore,
subBlockStore,
isAutoFillEnvVarsEnabled,
blockId
)
@@ -247,7 +251,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
const initialParams = initializeToolParams(
toolId,
toolParams,
toolParamsStore,
subBlockStore,
isAutoFillEnvVarsEnabled,
blockId
)
@@ -327,7 +331,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
// Only store non-empty values
if (paramValue.trim()) {
toolParamsStore.setParam(toolId, paramId, paramValue)
subBlockStore.setToolParam(toolId, paramId, paramValue)
}
// Update the value in the workflow

View File

@@ -1,7 +1,6 @@
import { useCallback, useEffect, useRef } from 'react'
import { isEqual } from 'lodash'
import { useGeneralStore } from '@/stores/settings/general/store'
import { useToolParamsStore } from '@/stores/tool-params/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import { getProviderFromModel } from '@/providers/utils'
@@ -24,20 +23,19 @@ function handleAgentBlockApiKey(
// Skip if we couldn't determine a provider
if (!provider || provider === 'ollama') return
const toolParamsStore = useToolParamsStore.getState()
const subBlockStore = useSubBlockStore.getState()
// Try to get a saved API key for this provider
const savedValue = toolParamsStore.resolveParamValue(provider, 'apiKey', blockId)
const savedValue = subBlockStore.resolveToolParamValue(provider, 'apiKey', blockId)
// If we have a valid API key, use it
if (savedValue && savedValue !== '') {
// Only update if different from current value
if (savedValue !== storeValue) {
subBlockStore.setValue(blockId, subBlockId, savedValue)
}
// Always update the value when switching models, even if it appears the same
// This handles cases where the field shows masked values but needs to update
subBlockStore.setValue(blockId, subBlockId, savedValue)
} else {
// No API key found for this provider - ALWAYS clear the field
// Always clear the field when switching to a model with no API key
// Don't wait for user interaction to clear it
subBlockStore.setValue(blockId, subBlockId, '')
}
}
@@ -53,16 +51,16 @@ function handleStandardBlockApiKey(
) {
if (!blockType) return
const toolParamsStore = useToolParamsStore.getState()
const subBlockStore = useSubBlockStore.getState()
// Only auto-fill if the field is empty
if (!storeValue || storeValue === '') {
// Pass the blockId as instanceId to check if this specific instance has been cleared
const savedValue = toolParamsStore.resolveParamValue(blockType, 'apiKey', blockId)
const savedValue = subBlockStore.resolveToolParamValue(blockType, 'apiKey', blockId)
if (savedValue && savedValue !== '' && savedValue !== storeValue) {
// Auto-fill the API key from the param store
useSubBlockStore.getState().setValue(blockId, subBlockId, savedValue)
subBlockStore.setValue(blockId, subBlockId, savedValue)
}
}
// Handle environment variable references
@@ -73,13 +71,13 @@ function handleStandardBlockApiKey(
storeValue.endsWith('}}')
) {
// Pass the blockId as instanceId
const currentValue = toolParamsStore.resolveParamValue(blockType, 'apiKey', blockId)
const currentValue = subBlockStore.resolveToolParamValue(blockType, 'apiKey', blockId)
if (currentValue !== storeValue) {
// If we got a replacement or null, update the field
if (currentValue) {
// Replacement found - update to new reference
useSubBlockStore.getState().setValue(blockId, subBlockId, currentValue)
subBlockStore.setValue(blockId, subBlockId, currentValue)
}
}
}
@@ -97,32 +95,39 @@ function storeApiKeyValue(
) {
if (!blockType) return
const toolParamsStore = useToolParamsStore.getState()
const subBlockStore = useSubBlockStore.getState()
// Check if this is an empty value for an API key field that previously had a value
// This indicates the user has deliberately cleared the field
// Check if this is user explicitly clearing a field that had a value
// We only want to mark it as cleared if it's a user action, not an automatic
// clearing from model switching
if (
storeValue &&
storeValue !== '' &&
(newValue === null || newValue === '' || String(newValue).trim() === '')
) {
// Mark this specific instance as cleared so we don't auto-fill it
toolParamsStore.markParamAsCleared(blockId, 'apiKey')
subBlockStore.markParamAsCleared(blockId, 'apiKey')
return
}
// Only store non-empty values
if (!newValue || String(newValue).trim() === '') return
// If user enters a value, we should clear any "cleared" flag
// to ensure auto-fill will work in the future
if (subBlockStore.isParamCleared(blockId, 'apiKey')) {
subBlockStore.unmarkParamAsCleared(blockId, 'apiKey')
}
// For agent blocks, store the API key under the provider name
if (blockType === 'agent' && modelValue) {
const provider = getProviderFromModel(modelValue)
if (provider && provider !== 'ollama') {
toolParamsStore.setParam(provider, 'apiKey', String(newValue))
subBlockStore.setToolParam(provider, 'apiKey', String(newValue))
}
} else {
// For other blocks, store under the block type
toolParamsStore.setParam(blockType, 'apiKey', String(newValue))
subBlockStore.setToolParam(blockType, 'apiKey', String(newValue))
}
}
@@ -184,9 +189,27 @@ export function useSubBlockValue<T = any>(
// Update the previous model reference
prevModelRef.current = modelValue
// Handle API key autofill for the new model
if (isAutoFillEnvVarsEnabled && modelValue) {
handleAgentBlockApiKey(blockId, subBlockId, modelValue, storeValue)
// For agent blocks, always clear the field if needed
// But only fill with saved values if auto-fill is enabled
if (modelValue) {
const provider = getProviderFromModel(modelValue)
// Skip if we couldn't determine a provider
if (!provider || provider === 'ollama') return
const subBlockStore = useSubBlockStore.getState()
// Check if there's a saved value for this provider
const savedValue = subBlockStore.resolveToolParamValue(provider, 'apiKey', blockId)
if (savedValue && savedValue !== '' && isAutoFillEnvVarsEnabled) {
// Only auto-fill if the feature is enabled
subBlockStore.setValue(blockId, subBlockId, savedValue)
} else {
// Always clear immediately when switching to a model with no saved key
// or when auto-fill is disabled
subBlockStore.setValue(blockId, subBlockId, '')
}
}
}
}, [blockId, subBlockId, blockType, isApiKey, modelValue, isAutoFillEnvVarsEnabled, storeValue])

View File

@@ -8,7 +8,6 @@ import { useConsoleStore } from './panel/console/store'
import { useVariablesStore } from './panel/variables/store'
import { useEnvironmentStore } from './settings/environment/store'
import { getSyncManagers, initializeSyncManagers, resetSyncManagers } from './sync-registry'
import { useToolParamsStore } from './tool-params/store'
import {
loadRegistry,
loadSubblockValues,
@@ -18,7 +17,6 @@ import {
} from './workflows/persistence'
import { useWorkflowRegistry } from './workflows/registry/store'
import { useSubBlockStore } from './workflows/subblock/store'
import { workflowSync } from './workflows/sync'
import { useWorkflowStore } from './workflows/workflow/store'
const logger = createLogger('Stores')
@@ -232,7 +230,6 @@ export {
useChatStore,
useCustomToolsStore,
useVariablesStore,
useToolParamsStore,
}
// Helper function to reset all stores
@@ -246,6 +243,7 @@ export const resetAllStores = () => {
})
useWorkflowStore.getState().clear()
useSubBlockStore.getState().clear()
useSubBlockStore.getState().clearToolParams()
useNotificationStore.setState({ notifications: [] })
useEnvironmentStore.setState({
variables: {},
@@ -257,7 +255,6 @@ export const resetAllStores = () => {
useChatStore.setState({ messages: [], isProcessing: false, error: null })
useCustomToolsStore.setState({ tools: {} })
useVariablesStore.getState().resetLoaded() // Reset variables store tracking
useToolParamsStore.getState().clear()
}
// Helper function to log all store states
@@ -273,7 +270,6 @@ export const logAllStores = () => {
customTools: useCustomToolsStore.getState(),
subBlock: useSubBlockStore.getState(),
variables: useVariablesStore.getState(),
toolParams: useToolParamsStore.getState(),
}
return state

View File

@@ -1,179 +0,0 @@
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
import { useEnvironmentStore } from '../settings/environment/store'
import { useGeneralStore } from '../settings/general/store'
import { ToolParamsStore } from './types'
import { extractEnvVarName, findMatchingEnvVar, isEnvVarReference } from './utils'
export const useToolParamsStore = create<ToolParamsStore>()(
devtools(
persist(
(set, get) => ({
params: {},
clearedParams: {},
setParam: (toolId: string, paramId: string, value: string) => {
// If setting a non-empty value, we should remove it from clearedParams if it exists
if (value.trim() !== '') {
set((state) => {
const newClearedParams = { ...state.clearedParams }
if (newClearedParams[toolId] && newClearedParams[toolId][paramId]) {
delete newClearedParams[toolId][paramId]
// Clean up empty objects
if (Object.keys(newClearedParams[toolId]).length === 0) {
delete newClearedParams[toolId]
}
}
return { clearedParams: newClearedParams }
})
}
// Set the parameter value
set((state) => ({
params: {
...state.params,
[toolId]: {
...(state.params[toolId] || {}),
[paramId]: value,
},
},
}))
// For API keys, also store under a normalized tool name for cross-referencing
// This allows both blocks and tools to share the same parameters
if (paramId.toLowerCase() === 'apikey' || paramId.toLowerCase() === 'api_key') {
// Extract the tool name part (e.g., "exa" from "exa-search")
const baseTool = toolId.split('-')[0].toLowerCase()
if (baseTool !== toolId) {
// Set the same value for the base tool to enable cross-referencing
set((state) => ({
params: {
...state.params,
[baseTool]: {
...(state.params[baseTool] || {}),
[paramId]: value,
},
},
}))
}
}
},
markParamAsCleared: (instanceId: string, paramId: string) => {
// Mark this specific instance as cleared
set((state) => ({
clearedParams: {
...state.clearedParams,
[instanceId]: {
...(state.clearedParams[instanceId] || {}),
[paramId]: true,
},
},
}))
},
isParamCleared: (instanceId: string, paramId: string) => {
// Only check this specific instance
return !!get().clearedParams[instanceId]?.[paramId]
},
getParam: (toolId: string, paramId: string) => {
// Check for direct match first
const directValue = get().params[toolId]?.[paramId]
if (directValue) return directValue
// Try base tool name if it's a compound tool ID
if (toolId.includes('-')) {
const baseTool = toolId.split('-')[0].toLowerCase()
return get().params[baseTool]?.[paramId]
}
// Try matching against any stored tool that starts with this ID
// This helps match "exa" with "exa-search" etc.
const matchingToolIds = Object.keys(get().params).filter(
(id) => id.startsWith(toolId) || id.split('-')[0] === toolId
)
for (const id of matchingToolIds) {
const value = get().params[id]?.[paramId]
if (value) return value
}
return undefined
},
getToolParams: (toolId: string) => {
return get().params[toolId] || {}
},
isEnvVarReference,
resolveParamValue: (toolId: string, paramId: string, instanceId?: string) => {
// If this is a specific instance that has been deliberately cleared, don't auto-fill it
if (instanceId && get().isParamCleared(instanceId, paramId)) {
return undefined
}
// Check if auto-fill environment variables is enabled
const isAutoFillEnvVarsEnabled = useGeneralStore.getState().isAutoFillEnvVarsEnabled
if (!isAutoFillEnvVarsEnabled) {
// When auto-fill is disabled, we still return existing stored values, but don't
// attempt to resolve environment variables or set new values
return get().params[toolId]?.[paramId]
}
const envStore = useEnvironmentStore.getState()
// First check params store for previously entered value
const storedValue = get().getParam(toolId, paramId)
if (storedValue) {
// If the stored value is an environment variable reference like {{EXA_API_KEY}}
if (isEnvVarReference(storedValue)) {
// Extract variable name from {{VAR_NAME}}
const envVarName = extractEnvVarName(storedValue)
if (!envVarName) return undefined
// Check if this environment variable still exists
const envValue = envStore.getVariable(envVarName)
if (envValue) {
// Environment variable exists, return the reference
return storedValue
} else {
// Environment variable no longer exists
return undefined
}
}
// Return the stored value directly if it's not an env var reference
return storedValue
}
// If no stored value, try to guess based on parameter name
// This handles cases where the user hasn't entered a value yet
if (paramId.toLowerCase() === 'apikey' || paramId.toLowerCase() === 'api_key') {
const matchingVar = findMatchingEnvVar(toolId)
if (matchingVar) {
const envReference = `{{${matchingVar}}}`
get().setParam(toolId, paramId, envReference)
return envReference
}
}
// No value found
return undefined
},
clear: () => {
set({ params: {}, clearedParams: {} })
},
}),
{
name: 'tool-params-store',
}
)
)
)

View File

@@ -1,12 +0,0 @@
export interface ToolParamsStore {
params: Record<string, Record<string, string>>
clearedParams: Record<string, Record<string, boolean>>
setParam: (toolId: string, paramId: string, value: string) => void
markParamAsCleared: (instanceId: string, paramId: string) => void
isParamCleared: (instanceId: string, paramId: string) => boolean
getParam: (toolId: string, paramId: string) => string | undefined
getToolParams: (toolId: string) => Record<string, string>
isEnvVarReference: (value: string) => boolean
resolveParamValue: (toolId: string, paramId: string, instanceId?: string) => string | undefined
clear: () => void
}

View File

@@ -1,10 +1,13 @@
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
import { SubBlockConfig } from '@/blocks/types'
import { useEnvironmentStore } from '../../settings/environment/store'
import { useGeneralStore } from '../../settings/general/store'
import { loadSubblockValues, saveSubblockValues } from '../persistence'
import { useWorkflowRegistry } from '../registry/store'
import { workflowSync } from '../sync'
import { SubBlockStore } from './types'
import { extractEnvVarName, findMatchingEnvVar, isEnvVarReference } from './utils'
// Add debounce utility for syncing
let syncDebounceTimer: NodeJS.Timeout | null = null
@@ -27,6 +30,9 @@ export const useSubBlockStore = create<SubBlockStore>()(
persist(
(set, get) => ({
workflowValues: {},
// Initialize tool params-related state
toolParams: {},
clearedParams: {},
setValue: (blockId: string, subBlockId: string, value: any) => {
const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId
@@ -124,10 +130,189 @@ export const useSubBlockStore = create<SubBlockStore>()(
workflowSync.sync()
}, DEBOUNCE_DELAY)
},
// Tool params related functionality
setToolParam: (toolId: string, paramId: string, value: string) => {
// If setting a non-empty value, we should remove it from clearedParams if it exists
if (value.trim() !== '') {
set((state) => {
const newClearedParams = { ...state.clearedParams }
if (newClearedParams[toolId] && newClearedParams[toolId][paramId]) {
delete newClearedParams[toolId][paramId]
// Clean up empty objects
if (Object.keys(newClearedParams[toolId]).length === 0) {
delete newClearedParams[toolId]
}
}
return { clearedParams: newClearedParams }
})
}
// Set the parameter value
set((state) => ({
toolParams: {
...state.toolParams,
[toolId]: {
...(state.toolParams[toolId] || {}),
[paramId]: value,
},
},
}))
// For API keys, also store under a normalized tool name for cross-referencing
// This allows both blocks and tools to share the same parameters
if (paramId.toLowerCase() === 'apikey' || paramId.toLowerCase() === 'api_key') {
// Extract the tool name part (e.g., "exa" from "exa-search")
const baseTool = toolId.split('-')[0].toLowerCase()
if (baseTool !== toolId) {
// Set the same value for the base tool to enable cross-referencing
set((state) => ({
toolParams: {
...state.toolParams,
[baseTool]: {
...(state.toolParams[baseTool] || {}),
[paramId]: value,
},
},
}))
}
}
},
markParamAsCleared: (instanceId: string, paramId: string) => {
// Mark this specific instance as cleared
set((state) => ({
clearedParams: {
...state.clearedParams,
[instanceId]: {
...(state.clearedParams[instanceId] || {}),
[paramId]: true,
},
},
}))
},
unmarkParamAsCleared: (instanceId: string, paramId: string) => {
// Remove the cleared flag for this parameter
set((state) => {
const newClearedParams = { ...state.clearedParams }
if (newClearedParams[instanceId] && newClearedParams[instanceId][paramId]) {
delete newClearedParams[instanceId][paramId]
// Clean up empty objects
if (Object.keys(newClearedParams[instanceId]).length === 0) {
delete newClearedParams[instanceId]
}
}
return { clearedParams: newClearedParams }
})
},
isParamCleared: (instanceId: string, paramId: string) => {
// Only check this specific instance
return !!get().clearedParams[instanceId]?.[paramId]
},
getToolParam: (toolId: string, paramId: string) => {
// Check for direct match first
const directValue = get().toolParams[toolId]?.[paramId]
if (directValue) return directValue
// Try base tool name if it's a compound tool ID
if (toolId.includes('-')) {
const baseTool = toolId.split('-')[0].toLowerCase()
return get().toolParams[baseTool]?.[paramId]
}
// Try matching against any stored tool that starts with this ID
// This helps match "exa" with "exa-search" etc.
const matchingToolIds = Object.keys(get().toolParams).filter(
(id) => id.startsWith(toolId) || id.split('-')[0] === toolId
)
for (const id of matchingToolIds) {
const value = get().toolParams[id]?.[paramId]
if (value) return value
}
return undefined
},
getToolParams: (toolId: string) => {
return get().toolParams[toolId] || {}
},
isEnvVarReference,
resolveToolParamValue: (toolId: string, paramId: string, instanceId?: string) => {
// If this is a specific instance that has been deliberately cleared, don't auto-fill it
if (instanceId && get().isParamCleared(instanceId, paramId)) {
return undefined
}
// Check if auto-fill environment variables is enabled
const isAutoFillEnvVarsEnabled = useGeneralStore.getState().isAutoFillEnvVarsEnabled
if (!isAutoFillEnvVarsEnabled) {
// When auto-fill is disabled, we still return existing stored values, but don't
// attempt to resolve environment variables or set new values
return get().toolParams[toolId]?.[paramId]
}
const envStore = useEnvironmentStore.getState()
// First check params store for previously entered value
const storedValue = get().getToolParam(toolId, paramId)
if (storedValue) {
// If the stored value is an environment variable reference like {{EXA_API_KEY}}
if (isEnvVarReference(storedValue)) {
// Extract variable name from {{VAR_NAME}}
const envVarName = extractEnvVarName(storedValue)
if (!envVarName) return undefined
// Check if this environment variable still exists
const envValue = envStore.getVariable(envVarName)
if (envValue) {
// Environment variable exists, return the reference
return storedValue
} else {
// Environment variable no longer exists
return undefined
}
}
// Return the stored value directly if it's not an env var reference
return storedValue
}
// If no stored value, try to guess based on parameter name
// This handles cases where the user hasn't entered a value yet
if (paramId.toLowerCase() === 'apikey' || paramId.toLowerCase() === 'api_key') {
const matchingVar = findMatchingEnvVar(toolId)
if (matchingVar) {
const envReference = `{{${matchingVar}}}`
get().setToolParam(toolId, paramId, envReference)
return envReference
}
}
// No value found
return undefined
},
clearToolParams: () => {
set({ toolParams: {}, clearedParams: {} })
},
}),
{
name: 'subblock-store',
partialize: (state) => ({ workflowValues: state.workflowValues }),
partialize: (state) => ({
workflowValues: state.workflowValues,
toolParams: state.toolParams,
clearedParams: state.clearedParams,
}),
// Use default storage
storage: {
getItem: (name) => {

View File

@@ -1,5 +1,7 @@
export interface SubBlockState {
workflowValues: Record<string, Record<string, Record<string, any>>> // Store values per workflow ID
toolParams: Record<string, Record<string, string>>
clearedParams: Record<string, Record<string, boolean>>
}
export interface SubBlockStore extends SubBlockState {
@@ -9,4 +11,19 @@ export interface SubBlockStore extends SubBlockState {
initializeFromWorkflow: (workflowId: string, blocks: Record<string, any>) => void
// Add debounced sync function
syncWithDB: () => void
// Tool params related functions
setToolParam: (toolId: string, paramId: string, value: string) => void
markParamAsCleared: (instanceId: string, paramId: string) => void
unmarkParamAsCleared: (instanceId: string, paramId: string) => void
isParamCleared: (instanceId: string, paramId: string) => boolean
getToolParam: (toolId: string, paramId: string) => string | undefined
getToolParams: (toolId: string) => Record<string, string>
isEnvVarReference: (value: string) => boolean
resolveToolParamValue: (
toolId: string,
paramId: string,
instanceId?: string
) => string | undefined
clearToolParams: () => void
}

View File

@@ -1,15 +1,25 @@
import { useEnvironmentStore } from '../settings/environment/store'
import { useEnvironmentStore } from '../../settings/environment/store'
/**
* Checks if a value is an environment variable reference in the format {{ENV_VAR}}
*/
export const isEnvVarReference = (value: string): boolean => {
// Check if the value looks like {{ENV_VAR}}
return /^\{\{[a-zA-Z0-9_-]+\}\}$/.test(value)
}
/**
* Extracts the environment variable name from a reference like {{ENV_VAR}}
*/
export const extractEnvVarName = (value: string): string | null => {
if (!isEnvVarReference(value)) return null
return value.slice(2, -2)
}
/**
* Generates possible environment variable names for a tool ID
* For example, "exa-search" could map to "EXA_API_KEY", "EXA_KEY", etc.
*/
export const generatePossibleEnvVarNames = (toolId: string): string[] => {
// Extract base tool name if it's a compound ID
const baseTool = toolId.includes('-') ? toolId.split('-')[0] : toolId
@@ -24,6 +34,9 @@ export const generatePossibleEnvVarNames = (toolId: string): string[] => {
]
}
/**
* Finds a matching environment variable for a tool ID
*/
export const findMatchingEnvVar = (toolId: string): string | null => {
const envStore = useEnvironmentStore.getState()
const possibleVars = generatePossibleEnvVarNames(toolId)