Download src/hooks/mutation/use-save-fields-as-secrets.ts from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 3.08 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/src/hooks/mutation/use-save-fields-as-secrets.ts
- Command line
-
hf download hf://SaylorTwift/openhands/src/hooks/mutation/use-save-fields-as-secrets.ts
-
curl -L -o use-save-fields-as-secrets.ts https://huggingface.co/SaylorTwift/openhands/resolve/main/src/hooks/mutation/use-save-fields-as-secrets.ts
3.08 kB
| import { useCallback } from "react"; | |
| import { useTranslation } from "react-i18next"; | |
| import { useQueryClient } from "@tanstack/react-query"; | |
| import type { MarketplaceField } from "@openhands/extensions/integrations"; | |
| import { SecretsService } from "#/api/secrets-service"; | |
| import { I18nKey } from "#/i18n/declaration"; | |
| import { | |
| displayErrorToast, | |
| displaySuccessToast, | |
| } from "#/utils/custom-toast-handlers"; | |
| /** Truncates a list of key names for display in toasts. */ | |
| function formatKeyList(keys: string[]): string { | |
| const MAX = 3; | |
| if (keys.length <= MAX) return keys.join(", "); | |
| return `${keys.slice(0, MAX).join(", ")} … (+${keys.length - MAX})`; | |
| } | |
| /** | |
| * Returns a stable function that upserts checked envFields into the Secrets | |
| * store. Callers may ignore the returned promise for background saves or await | |
| * it when later work depends on the secret being present. MCP server config | |
| * and the Secrets store are separate — this bridges the gap so Automation | |
| * Server can access credentials without a separate manual step. Internally, | |
| * `SecretsService.createSecret` is an upsert, so existing secrets with the | |
| * same name are overwritten safely. | |
| */ | |
| export function useSaveFieldsAsSecrets() { | |
| const { t } = useTranslation("openhands"); | |
| const queryClient = useQueryClient(); | |
| return useCallback( | |
| ( | |
| envFields: MarketplaceField[], | |
| values: Record<string, string>, | |
| savedAsSecret: Record<string, boolean>, | |
| ): Promise<void> => { | |
| const fieldsToSave = envFields.filter( | |
| (field) => savedAsSecret[field.key] && (values[field.key] ?? "").trim(), | |
| ); | |
| if (fieldsToSave.length === 0) return Promise.resolve(); | |
| return Promise.allSettled( | |
| fieldsToSave.map((field) => | |
| SecretsService.createSecret( | |
| field.key, | |
| values[field.key].trim(), | |
| field.label, | |
| ), | |
| ), | |
| ).then((results) => { | |
| const saved = fieldsToSave | |
| .filter((_, i) => results[i].status === "fulfilled") | |
| .map((f) => f.key); | |
| const failed = fieldsToSave | |
| .filter((_, i) => results[i].status === "rejected") | |
| .map((f) => f.key); | |
| if (saved.length > 0) { | |
| // Refresh any cached secrets lists so a newly-saved secret shows up | |
| // in Settings → Secrets immediately. Without this, the 5-minute | |
| // staleTime on the secrets query (use-get-secrets.ts) can keep a | |
| // previously-loaded list stale and hide the new secret. Mirrors the | |
| // invalidation done by secret-form.tsx and secrets-settings.tsx. | |
| queryClient.invalidateQueries({ queryKey: ["secrets-search"] }); | |
| queryClient.invalidateQueries({ queryKey: ["secrets"] }); | |
| displaySuccessToast( | |
| t(I18nKey.MCP$SECRETS_SAVED, { keys: formatKeyList(saved) }), | |
| ); | |
| } | |
| if (failed.length > 0) { | |
| displayErrorToast( | |
| t(I18nKey.MCP$SECRETS_SAVE_FAILED, { keys: formatKeyList(failed) }), | |
| ); | |
| } | |
| }); | |
| }, | |
| [t, queryClient], | |
| ); | |
| } | |