consolidate credential parameters into unified entry point with guided type/source flow & tooltips (#4497)
This commit is contained in:
@@ -50,6 +50,36 @@ const workflowParameterTypeOptions = [
|
|||||||
{ label: "JSON", value: WorkflowParameterValueType.JSON },
|
{ label: "JSON", value: WorkflowParameterValueType.JSON },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
type CredentialDataType = "password" | "secret" | "creditCard";
|
||||||
|
type CredentialSource = "bitwarden" | "skyvern" | "onepassword" | "azurevault";
|
||||||
|
|
||||||
|
// Determine available sources based on credential data type
|
||||||
|
function getAvailableSourcesForDataType(
|
||||||
|
dataType: CredentialDataType,
|
||||||
|
isCloud: boolean,
|
||||||
|
): Array<{ value: CredentialSource; label: string }> {
|
||||||
|
switch (dataType) {
|
||||||
|
case "password":
|
||||||
|
return [
|
||||||
|
...(isCloud ? [{ value: "skyvern" as const, label: "Skyvern" }] : []),
|
||||||
|
{ value: "bitwarden" as const, label: "Bitwarden" },
|
||||||
|
{ value: "onepassword" as const, label: "1Password" },
|
||||||
|
{ value: "azurevault" as const, label: "Azure Key Vault" },
|
||||||
|
];
|
||||||
|
case "secret":
|
||||||
|
return [
|
||||||
|
...(isCloud ? [{ value: "skyvern" as const, label: "Skyvern" }] : []),
|
||||||
|
{ value: "bitwarden" as const, label: "Bitwarden" },
|
||||||
|
];
|
||||||
|
case "creditCard":
|
||||||
|
return [
|
||||||
|
...(isCloud ? [{ value: "skyvern" as const, label: "Skyvern" }] : []),
|
||||||
|
{ value: "bitwarden" as const, label: "Bitwarden" },
|
||||||
|
{ value: "onepassword" as const, label: "1Password" },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function header(type: WorkflowEditorParameterType, isEdit: boolean) {
|
function header(type: WorkflowEditorParameterType, isEdit: boolean) {
|
||||||
const prefix = isEdit ? "Edit" : "Add";
|
const prefix = isEdit ? "Edit" : "Add";
|
||||||
if (type === "workflow") {
|
if (type === "workflow") {
|
||||||
@@ -58,15 +88,38 @@ function header(type: WorkflowEditorParameterType, isEdit: boolean) {
|
|||||||
if (type === "credential") {
|
if (type === "credential") {
|
||||||
return `${prefix} Credential Parameter`;
|
return `${prefix} Credential Parameter`;
|
||||||
}
|
}
|
||||||
if (type === "secret") {
|
|
||||||
return `${prefix} Secret Parameter`;
|
|
||||||
}
|
|
||||||
if (type === "creditCardData") {
|
|
||||||
return `${prefix} Credit Card Parameter`;
|
|
||||||
}
|
|
||||||
return `${prefix} Context Parameter`;
|
return `${prefix} Context Parameter`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to detect initial credential data type from existing parameter
|
||||||
|
function detectInitialCredentialDataType(
|
||||||
|
initialValues: ParametersState[number] | undefined,
|
||||||
|
): CredentialDataType {
|
||||||
|
if (!initialValues) return "password";
|
||||||
|
if (initialValues.parameterType === "secret") return "secret";
|
||||||
|
if (initialValues.parameterType === "creditCardData") return "creditCard";
|
||||||
|
return "password";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to detect initial credential source from existing parameter
|
||||||
|
function detectInitialCredentialSource(
|
||||||
|
initialValues: ParametersState[number] | undefined,
|
||||||
|
): CredentialSource {
|
||||||
|
if (!initialValues) return "bitwarden";
|
||||||
|
|
||||||
|
if (initialValues.parameterType === "secret") return "bitwarden";
|
||||||
|
if (initialValues.parameterType === "creditCardData") return "bitwarden";
|
||||||
|
if (initialValues.parameterType === "onepassword") return "onepassword";
|
||||||
|
|
||||||
|
if (initialValues.parameterType === "credential") {
|
||||||
|
if (parameterIsSkyvernCredential(initialValues)) return "skyvern";
|
||||||
|
if (parameterIsBitwardenCredential(initialValues)) return "bitwarden";
|
||||||
|
if (parameterIsAzureVaultCredential(initialValues)) return "azurevault";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "bitwarden";
|
||||||
|
}
|
||||||
|
|
||||||
function WorkflowParameterEditPanel({
|
function WorkflowParameterEditPanel({
|
||||||
type,
|
type,
|
||||||
onClose,
|
onClose,
|
||||||
@@ -83,6 +136,8 @@ function WorkflowParameterEditPanel({
|
|||||||
const isEditMode = !!initialValues;
|
const isEditMode = !!initialValues;
|
||||||
const [key, setKey] = useState(initialValues?.key ?? "");
|
const [key, setKey] = useState(initialValues?.key ?? "");
|
||||||
const hasWhitespace = /\s/.test(key);
|
const hasWhitespace = /\s/.test(key);
|
||||||
|
|
||||||
|
// Detect initial values for backward compatibility
|
||||||
const isBitwardenCredential =
|
const isBitwardenCredential =
|
||||||
initialValues?.parameterType === "credential" &&
|
initialValues?.parameterType === "credential" &&
|
||||||
parameterIsBitwardenCredential(initialValues);
|
parameterIsBitwardenCredential(initialValues);
|
||||||
@@ -95,17 +150,16 @@ function WorkflowParameterEditPanel({
|
|||||||
const isAzureVaultCredential =
|
const isAzureVaultCredential =
|
||||||
initialValues?.parameterType === "credential" &&
|
initialValues?.parameterType === "credential" &&
|
||||||
parameterIsAzureVaultCredential(initialValues);
|
parameterIsAzureVaultCredential(initialValues);
|
||||||
const [credentialType, setCredentialType] = useState<
|
|
||||||
"bitwarden" | "skyvern" | "onepassword" | "azurevault"
|
// New unified credential state
|
||||||
>(
|
const [credentialDataType, setCredentialDataType] =
|
||||||
isBitwardenCredential
|
useState<CredentialDataType>(
|
||||||
? "bitwarden"
|
detectInitialCredentialDataType(initialValues),
|
||||||
: isOnePasswordCredential
|
);
|
||||||
? "onepassword"
|
const [credentialSource, setCredentialSource] = useState<CredentialSource>(
|
||||||
: isAzureVaultCredential
|
detectInitialCredentialSource(initialValues),
|
||||||
? "azurevault"
|
|
||||||
: "skyvern",
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const [urlParameterKey, setUrlParameterKey] = useState(
|
const [urlParameterKey, setUrlParameterKey] = useState(
|
||||||
isBitwardenCredential ? initialValues?.urlParameterKey ?? "" : "",
|
isBitwardenCredential ? initialValues?.urlParameterKey ?? "" : "",
|
||||||
);
|
);
|
||||||
@@ -191,6 +245,43 @@ function WorkflowParameterEditPanel({
|
|||||||
isAzureVaultCredential ? initialValues.totpSecretKey ?? "" : "",
|
isAzureVaultCredential ? initialValues.totpSecretKey ?? "" : "",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Handle credential data type change - reset source to first available
|
||||||
|
const handleCredentialDataTypeChange = (newDataType: CredentialDataType) => {
|
||||||
|
setCredentialDataType(newDataType);
|
||||||
|
const availableSources = getAvailableSourcesForDataType(
|
||||||
|
newDataType,
|
||||||
|
isCloud,
|
||||||
|
);
|
||||||
|
if (!availableSources.find((s) => s.value === credentialSource)) {
|
||||||
|
setCredentialSource(availableSources[0]?.value ?? "bitwarden");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const availableSources = getAvailableSourcesForDataType(
|
||||||
|
credentialDataType,
|
||||||
|
isCloud,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Determine what fields to show based on credential data type and source
|
||||||
|
const showBitwardenPasswordFields =
|
||||||
|
type === "credential" &&
|
||||||
|
credentialDataType === "password" &&
|
||||||
|
credentialSource === "bitwarden";
|
||||||
|
const showBitwardenSecretFields =
|
||||||
|
type === "credential" &&
|
||||||
|
credentialDataType === "secret" &&
|
||||||
|
credentialSource === "bitwarden";
|
||||||
|
const showBitwardenCreditCardFields =
|
||||||
|
type === "credential" &&
|
||||||
|
credentialDataType === "creditCard" &&
|
||||||
|
credentialSource === "bitwarden";
|
||||||
|
const showOnePasswordFields =
|
||||||
|
type === "credential" && credentialSource === "onepassword";
|
||||||
|
const showAzureVaultFields =
|
||||||
|
type === "credential" && credentialSource === "azurevault";
|
||||||
|
const showSkyvernCredentialSelector =
|
||||||
|
type === "credential" && credentialSource === "skyvern" && isCloud;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea>
|
<ScrollArea>
|
||||||
<ScrollAreaViewport className="max-h-[500px]">
|
<ScrollAreaViewport className="max-h-[500px]">
|
||||||
@@ -301,40 +392,68 @@ function WorkflowParameterEditPanel({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Credential Parameter - Unified Flow */}
|
||||||
{type === "credential" && (
|
{type === "credential" && (
|
||||||
<>
|
<>
|
||||||
|
{/* Step 1: Credential Type */}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label className="text-xs text-slate-300">
|
<div className="flex gap-2">
|
||||||
Credential Type
|
<Label className="text-xs text-slate-300">
|
||||||
</Label>
|
Credential Type
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="Select the type of credential you want to use. Password for login credentials, Secret for sensitive data fields, Credit Card for payment information." />
|
||||||
|
</div>
|
||||||
<Select
|
<Select
|
||||||
value={credentialType}
|
value={credentialDataType}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) =>
|
||||||
setCredentialType(
|
handleCredentialDataTypeChange(value as CredentialDataType)
|
||||||
value as
|
}
|
||||||
| "bitwarden"
|
|
||||||
| "skyvern"
|
|
||||||
| "onepassword"
|
|
||||||
| "azurevault",
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
<SelectValue placeholder="Select a type" />
|
<SelectValue placeholder="Select credential type" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectItem value="skyvern">Skyvern</SelectItem>
|
<SelectItem value="password">Password</SelectItem>
|
||||||
<SelectItem value="bitwarden">Bitwarden</SelectItem>
|
<SelectItem value="secret">Secret</SelectItem>
|
||||||
<SelectItem value="onepassword">1Password</SelectItem>
|
<SelectItem value="creditCard">Credit Card</SelectItem>
|
||||||
<SelectItem value="azurevault">Azure Vault</SelectItem>
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Step 2: Source */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">Source</Label>
|
||||||
|
<HelpTooltip content="Select where your credentials are stored. Skyvern uses managed credentials, while Bitwarden, 1Password, and Azure Key Vault connect directly to your vault." />
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
value={credentialSource}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setCredentialSource(value as CredentialSource)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder="Select source" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
{availableSources.map((source) => (
|
||||||
|
<SelectItem key={source.value} value={source.value}>
|
||||||
|
{source.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{type === "credential" && credentialType === "bitwarden" && (
|
|
||||||
|
{/* Bitwarden Password Fields */}
|
||||||
|
{showBitwardenPasswordFields && (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
@@ -351,9 +470,9 @@ function WorkflowParameterEditPanel({
|
|||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Label className="text-xs text-slate-300">
|
<Label className="text-xs text-slate-300">
|
||||||
Collection ID
|
Bitwarden Collection ID
|
||||||
</Label>
|
</Label>
|
||||||
<HelpTooltip content="The Bitwarden collection ID. You can find this in the URL when viewing a collection in Bitwarden." />
|
<HelpTooltip content="The Bitwarden collection ID. You can find this in the URL when viewing a collection in Bitwarden (e.g., https://vault.bitwarden.com/#/organizations/.../collections/[COLLECTION_ID])." />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
value={bitwardenCollectionId}
|
value={bitwardenCollectionId}
|
||||||
@@ -362,8 +481,10 @@ function WorkflowParameterEditPanel({
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Label className="text-xs text-slate-300">Item ID</Label>
|
<Label className="text-xs text-slate-300">
|
||||||
<HelpTooltip content="The Bitwarden item ID. You can find this in the URL when viewing the item in Bitwarden." />
|
Bitwarden Item ID
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="The Bitwarden item ID. You can find this in the URL when viewing the item in Bitwarden (e.g., https://vault.bitwarden.com/#/vault?itemId=[ITEM_ID])." />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
value={bitwardenLoginCredentialItemId}
|
value={bitwardenLoginCredentialItemId}
|
||||||
@@ -374,12 +495,89 @@ function WorkflowParameterEditPanel({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{type === "credential" && credentialType === "onepassword" && (
|
|
||||||
|
{/* Bitwarden Secret Fields */}
|
||||||
|
{showBitwardenSecretFields && (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Label className="text-xs text-slate-300">Vault ID</Label>
|
<Label className="text-xs text-slate-300">
|
||||||
<HelpTooltip content="You can find the Vault ID and Item ID in the URL when viewing the item in 1Password on the web." />
|
Bitwarden Collection ID
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="Required. The Bitwarden collection ID containing the identity item. You can find this in the URL when viewing a collection in Bitwarden." />
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
value={bitwardenCollectionId}
|
||||||
|
onChange={(e) => setBitwardenCollectionId(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">Identity Key</Label>
|
||||||
|
<HelpTooltip content="The key used to identify which identity to use from Bitwarden (e.g., the identity name or a custom identifier)." />
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
value={identityKey}
|
||||||
|
onChange={(e) => setIdentityKey(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">
|
||||||
|
Identity Fields
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="Comma-separated list of field names to extract from the Bitwarden identity (e.g., 'ssn, address, phone')." />
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
value={identityFields}
|
||||||
|
onChange={(e) => setIdentityFields(e.target.value)}
|
||||||
|
placeholder="field1, field2, field3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Bitwarden Credit Card Fields */}
|
||||||
|
{showBitwardenCreditCardFields && (
|
||||||
|
<>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">
|
||||||
|
Bitwarden Collection ID
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="Required. The Bitwarden collection ID containing the credit card. You can find this in the URL when viewing a collection in Bitwarden." />
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
value={bitwardenCollectionId}
|
||||||
|
onChange={(e) => setBitwardenCollectionId(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">
|
||||||
|
Bitwarden Item ID
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="Required. The Bitwarden item ID of the credit card. You can find this in the URL when viewing the item in Bitwarden." />
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
value={sensitiveInformationItemId}
|
||||||
|
onChange={(e) =>
|
||||||
|
setSensitiveInformationItemId(e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 1Password Fields */}
|
||||||
|
{showOnePasswordFields && (
|
||||||
|
<>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">
|
||||||
|
1Password Vault ID
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="You can find the Vault ID in the URL when viewing the vault in 1Password on the web (e.g., https://my.1password.com/vaults/[VAULT_ID])." />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
value={opVaultId}
|
value={opVaultId}
|
||||||
@@ -388,29 +586,37 @@ function WorkflowParameterEditPanel({
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Label className="text-xs text-slate-300">Item ID</Label>
|
<Label className="text-xs text-slate-300">
|
||||||
<HelpTooltip content="Supports all 1Password item types: Logins, Passwords, Credit Cards, Secure Notes, and more." />
|
1Password Item ID
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="You can find the Item ID in the URL when viewing the item in 1Password on the web. Supports all item types: Logins, Passwords, Credit Cards, Secure Notes, and more." />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
value={opItemId}
|
value={opItemId}
|
||||||
onChange={(e) => setOpItemId(e.target.value)}
|
onChange={(e) => setOpItemId(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-md bg-slate-800 p-2">
|
{credentialDataType === "creditCard" && (
|
||||||
<div className="space-y-1 text-xs text-slate-400">
|
<div className="rounded-md bg-slate-800 p-2">
|
||||||
Credit Cards: Due to a 1Password limitation, add the
|
<div className="space-y-1 text-xs text-slate-400">
|
||||||
expiration date as a separate text field named “Expire Date”
|
Credit Cards: Due to a 1Password limitation, add the
|
||||||
in the format MM/YYYY (e.g. 09/2027).
|
expiration date as a separate text field named "Expire Date"
|
||||||
|
in the format MM/YYYY (e.g. 09/2027).
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{type === "credential" && credentialType === "azurevault" && (
|
|
||||||
|
{/* Azure Key Vault Fields */}
|
||||||
|
{showAzureVaultFields && (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Label className="text-xs text-slate-300">Vault Name</Label>
|
<Label className="text-xs text-slate-300">
|
||||||
<HelpTooltip content="The name of your Azure Key Vault instance." />
|
Azure Key Vault Name
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="The name of your Azure Key Vault instance (e.g., 'my-company-vault'). This is the name you see in the Azure portal." />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
value={azureVaultName}
|
value={azureVaultName}
|
||||||
@@ -419,8 +625,10 @@ function WorkflowParameterEditPanel({
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Label className="text-xs text-slate-300">Username Key</Label>
|
<Label className="text-xs text-slate-300">
|
||||||
<HelpTooltip content="The secret name in Azure Key Vault that stores the username." />
|
Azure Username Secret Key
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="The secret name in Azure Key Vault that stores the username (e.g., 'my-app-username')." />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
@@ -430,8 +638,10 @@ function WorkflowParameterEditPanel({
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Label className="text-xs text-slate-300">Password Key</Label>
|
<Label className="text-xs text-slate-300">
|
||||||
<HelpTooltip content="The secret name in Azure Key Vault that stores the password." />
|
Azure Password Secret Key
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="The secret name in Azure Key Vault that stores the password (e.g., 'my-app-password')." />
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
value={azurePasswordKey}
|
value={azurePasswordKey}
|
||||||
@@ -441,7 +651,7 @@ function WorkflowParameterEditPanel({
|
|||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Label className="text-xs text-slate-300">
|
<Label className="text-xs text-slate-300">
|
||||||
TOTP Secret Key
|
Azure TOTP Secret Key
|
||||||
</Label>
|
</Label>
|
||||||
<HelpTooltip content="Optional. The secret name in Azure Key Vault that stores the TOTP secret for two-factor authentication." />
|
<HelpTooltip content="Optional. The secret name in Azure Key Vault that stores the TOTP secret for two-factor authentication." />
|
||||||
</div>
|
</div>
|
||||||
@@ -452,6 +662,23 @@ function WorkflowParameterEditPanel({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Skyvern Managed Credential Selector */}
|
||||||
|
{showSkyvernCredentialSelector && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">
|
||||||
|
Skyvern Credential
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content="Select a credential from your Skyvern credential store. These are managed credentials you've previously added to Skyvern." />
|
||||||
|
</div>
|
||||||
|
<CredentialParameterSourceSelector
|
||||||
|
value={credentialId}
|
||||||
|
onChange={(value) => setCredentialId(value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{type === "context" && (
|
{type === "context" && (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label className="text-xs text-slate-300">Source Parameter</Label>
|
<Label className="text-xs text-slate-300">Source Parameter</Label>
|
||||||
@@ -461,67 +688,7 @@ function WorkflowParameterEditPanel({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{type === "secret" && (
|
|
||||||
<>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<Label className="text-xs text-slate-300">Identity Key</Label>
|
|
||||||
<Input
|
|
||||||
value={identityKey}
|
|
||||||
onChange={(e) => setIdentityKey(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<Label className="text-xs text-slate-300">
|
|
||||||
Identity Fields
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
value={identityFields}
|
|
||||||
onChange={(e) => setIdentityFields(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<Label className="text-xs text-slate-300">Collection ID</Label>
|
|
||||||
<Input
|
|
||||||
value={bitwardenCollectionId}
|
|
||||||
onChange={(e) => setBitwardenCollectionId(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{type === "creditCardData" && (
|
|
||||||
<>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<Label className="text-xs text-slate-300">Collection ID</Label>
|
|
||||||
<Input
|
|
||||||
value={bitwardenCollectionId}
|
|
||||||
onChange={(e) => setBitwardenCollectionId(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<Label className="text-xs text-slate-300">Item ID</Label>
|
|
||||||
<Input
|
|
||||||
value={sensitiveInformationItemId}
|
|
||||||
onChange={(e) =>
|
|
||||||
setSensitiveInformationItemId(e.target.value)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{
|
|
||||||
// temporarily cloud only
|
|
||||||
type === "credential" &&
|
|
||||||
credentialType === "skyvern" &&
|
|
||||||
isCloud && (
|
|
||||||
<div className="space-y-1">
|
|
||||||
<Label className="text-xs text-slate-300">Credential</Label>
|
|
||||||
<CredentialParameterSourceSelector
|
|
||||||
value={credentialId}
|
|
||||||
onChange={(value) => setCredentialId(value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -550,6 +717,8 @@ function WorkflowParameterEditPanel({
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle workflow parameters
|
||||||
if (type === "workflow") {
|
if (type === "workflow") {
|
||||||
if (
|
if (
|
||||||
parameterType === "json" &&
|
parameterType === "json" &&
|
||||||
@@ -601,111 +770,10 @@ function WorkflowParameterEditPanel({
|
|||||||
? defaultValue
|
? defaultValue
|
||||||
: null,
|
: null,
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (type === "credential" && credentialType === "bitwarden") {
|
|
||||||
const errorMessage = validateBitwardenLoginCredential(
|
// Handle context parameters
|
||||||
bitwardenCollectionId,
|
|
||||||
bitwardenLoginCredentialItemId,
|
|
||||||
urlParameterKey,
|
|
||||||
);
|
|
||||||
if (errorMessage) {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: "Failed to save parameter",
|
|
||||||
description: errorMessage,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSave({
|
|
||||||
key,
|
|
||||||
parameterType: "credential",
|
|
||||||
itemId:
|
|
||||||
bitwardenLoginCredentialItemId === ""
|
|
||||||
? null
|
|
||||||
: bitwardenLoginCredentialItemId,
|
|
||||||
urlParameterKey:
|
|
||||||
urlParameterKey === "" ? null : urlParameterKey,
|
|
||||||
collectionId:
|
|
||||||
bitwardenCollectionId === ""
|
|
||||||
? null
|
|
||||||
: bitwardenCollectionId,
|
|
||||||
description,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (type === "credential" && credentialType === "onepassword") {
|
|
||||||
if (opVaultId.trim() === "" || opItemId.trim() === "") {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: "Failed to save parameter",
|
|
||||||
description: "Vault ID and Item ID are required",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSave({
|
|
||||||
key,
|
|
||||||
parameterType: "onepassword",
|
|
||||||
vaultId: opVaultId,
|
|
||||||
itemId: opItemId,
|
|
||||||
description,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (type === "credential" && credentialType === "azurevault") {
|
|
||||||
if (
|
|
||||||
azureVaultName.trim() === "" ||
|
|
||||||
azureUsernameKey.trim() === "" ||
|
|
||||||
azurePasswordKey.trim() === ""
|
|
||||||
) {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: "Failed to add parameter",
|
|
||||||
description:
|
|
||||||
"Azure Vault Name, Username Key and Password Key are required",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSave({
|
|
||||||
key,
|
|
||||||
parameterType: "credential",
|
|
||||||
vaultName: azureVaultName,
|
|
||||||
usernameKey: azureUsernameKey,
|
|
||||||
passwordKey: azurePasswordKey,
|
|
||||||
totpSecretKey:
|
|
||||||
azureTotpSecretKey === "" ? null : azureTotpSecretKey,
|
|
||||||
description: description,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (type === "secret" || type === "creditCardData") {
|
|
||||||
if (!bitwardenCollectionId) {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: "Failed to save parameter",
|
|
||||||
description: "Collection ID is required",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (type === "secret") {
|
|
||||||
onSave({
|
|
||||||
key,
|
|
||||||
parameterType: "secret",
|
|
||||||
collectionId: bitwardenCollectionId,
|
|
||||||
identityFields: identityFields
|
|
||||||
.split(",")
|
|
||||||
.filter((s) => s.length > 0)
|
|
||||||
.map((field) => field.trim()),
|
|
||||||
identityKey,
|
|
||||||
description,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (type === "creditCardData") {
|
|
||||||
onSave({
|
|
||||||
key,
|
|
||||||
parameterType: "creditCardData",
|
|
||||||
collectionId: bitwardenCollectionId,
|
|
||||||
itemId: sensitiveInformationItemId,
|
|
||||||
description,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (type === "context") {
|
if (type === "context") {
|
||||||
if (!sourceParameterKey) {
|
if (!sourceParameterKey) {
|
||||||
toast({
|
toast({
|
||||||
@@ -721,22 +789,166 @@ function WorkflowParameterEditPanel({
|
|||||||
sourceParameterKey,
|
sourceParameterKey,
|
||||||
description,
|
description,
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (type === "credential" && credentialType === "skyvern") {
|
|
||||||
if (!credentialId) {
|
// Handle credential parameters based on type + source combination
|
||||||
toast({
|
if (type === "credential") {
|
||||||
variant: "destructive",
|
// Skyvern managed credentials
|
||||||
title: "Failed to save parameter",
|
if (credentialSource === "skyvern") {
|
||||||
description: "Credential is required",
|
if (!credentialId) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Failed to save parameter",
|
||||||
|
description: "Credential is required",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSave({
|
||||||
|
key,
|
||||||
|
parameterType: "credential",
|
||||||
|
credentialId,
|
||||||
|
description,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitwarden credentials
|
||||||
|
if (credentialSource === "bitwarden") {
|
||||||
|
// Password type
|
||||||
|
if (credentialDataType === "password") {
|
||||||
|
const errorMessage = validateBitwardenLoginCredential(
|
||||||
|
bitwardenCollectionId,
|
||||||
|
bitwardenLoginCredentialItemId,
|
||||||
|
urlParameterKey,
|
||||||
|
);
|
||||||
|
if (errorMessage) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Failed to save parameter",
|
||||||
|
description: errorMessage,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSave({
|
||||||
|
key,
|
||||||
|
parameterType: "credential",
|
||||||
|
itemId:
|
||||||
|
bitwardenLoginCredentialItemId === ""
|
||||||
|
? null
|
||||||
|
: bitwardenLoginCredentialItemId,
|
||||||
|
urlParameterKey:
|
||||||
|
urlParameterKey === "" ? null : urlParameterKey,
|
||||||
|
collectionId:
|
||||||
|
bitwardenCollectionId === ""
|
||||||
|
? null
|
||||||
|
: bitwardenCollectionId,
|
||||||
|
description,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secret type
|
||||||
|
if (credentialDataType === "secret") {
|
||||||
|
if (!bitwardenCollectionId) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Failed to save parameter",
|
||||||
|
description: "Bitwarden Collection ID is required",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSave({
|
||||||
|
key,
|
||||||
|
parameterType: "secret",
|
||||||
|
collectionId: bitwardenCollectionId,
|
||||||
|
identityFields: identityFields
|
||||||
|
.split(",")
|
||||||
|
.filter((s) => s.length > 0)
|
||||||
|
.map((field) => field.trim()),
|
||||||
|
identityKey,
|
||||||
|
description,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Credit Card type
|
||||||
|
if (credentialDataType === "creditCard") {
|
||||||
|
if (!bitwardenCollectionId) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Failed to save parameter",
|
||||||
|
description: "Bitwarden Collection ID is required",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!sensitiveInformationItemId) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Failed to save parameter",
|
||||||
|
description: "Bitwarden Item ID is required",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSave({
|
||||||
|
key,
|
||||||
|
parameterType: "creditCardData",
|
||||||
|
collectionId: bitwardenCollectionId,
|
||||||
|
itemId: sensitiveInformationItemId,
|
||||||
|
description,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1Password credentials
|
||||||
|
if (credentialSource === "onepassword") {
|
||||||
|
if (opVaultId.trim() === "" || opItemId.trim() === "") {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Failed to save parameter",
|
||||||
|
description:
|
||||||
|
"1Password Vault ID and Item ID are required",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSave({
|
||||||
|
key,
|
||||||
|
parameterType: "onepassword",
|
||||||
|
vaultId: opVaultId,
|
||||||
|
itemId: opItemId,
|
||||||
|
description,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Azure Key Vault credentials
|
||||||
|
if (credentialSource === "azurevault") {
|
||||||
|
if (
|
||||||
|
azureVaultName.trim() === "" ||
|
||||||
|
azureUsernameKey.trim() === "" ||
|
||||||
|
azurePasswordKey.trim() === ""
|
||||||
|
) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Failed to add parameter",
|
||||||
|
description:
|
||||||
|
"Azure Vault Name, Username Key and Password Key are required",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onSave({
|
||||||
|
key,
|
||||||
|
parameterType: "credential",
|
||||||
|
vaultName: azureVaultName,
|
||||||
|
usernameKey: azureUsernameKey,
|
||||||
|
passwordKey: azurePasswordKey,
|
||||||
|
totpSecretKey:
|
||||||
|
azureTotpSecretKey === "" ? null : azureTotpSecretKey,
|
||||||
|
description: description,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onSave({
|
|
||||||
key,
|
|
||||||
parameterType: "credential",
|
|
||||||
credentialId,
|
|
||||||
description,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -105,28 +105,6 @@ function WorkflowParametersPanel({ onMouseDownCapture }: Props) {
|
|||||||
>
|
>
|
||||||
Credential Parameter
|
Credential Parameter
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => {
|
|
||||||
setOperationPanelState({
|
|
||||||
active: true,
|
|
||||||
operation: "add",
|
|
||||||
type: WorkflowEditorParameterTypes.Secret,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Secret Parameter
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => {
|
|
||||||
setOperationPanelState({
|
|
||||||
active: true,
|
|
||||||
operation: "add",
|
|
||||||
type: WorkflowEditorParameterTypes.CreditCardData,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Credit Card Parameter
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
||||||
@@ -149,7 +127,9 @@ function WorkflowParametersPanel({ onMouseDownCapture }: Props) {
|
|||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-sm text-slate-400">
|
<span className="text-sm text-slate-400">
|
||||||
{parameter.parameterType === "onepassword"
|
{parameter.parameterType === "onepassword" ||
|
||||||
|
parameter.parameterType === "secret" ||
|
||||||
|
parameter.parameterType === "creditCardData"
|
||||||
? "credential"
|
? "credential"
|
||||||
: parameter.parameterType}
|
: parameter.parameterType}
|
||||||
</span>
|
</span>
|
||||||
@@ -164,7 +144,9 @@ function WorkflowParametersPanel({ onMouseDownCapture }: Props) {
|
|||||||
operation: "edit",
|
operation: "edit",
|
||||||
parameter: parameter,
|
parameter: parameter,
|
||||||
type:
|
type:
|
||||||
parameter.parameterType === "onepassword"
|
parameter.parameterType === "onepassword" ||
|
||||||
|
parameter.parameterType === "secret" ||
|
||||||
|
parameter.parameterType === "creditCardData"
|
||||||
? WorkflowEditorParameterTypes.Credential
|
? WorkflowEditorParameterTypes.Credential
|
||||||
: parameter.parameterType,
|
: parameter.parameterType,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user