-
-
-
+
+
+
+
+
+
+
+
+
-
@@ -232,6 +300,11 @@ export function SecretsPage(props: {
addSecretDescription?: string;
showProviderInfo?: boolean;
storageOptions?: readonly SecretStorageOption[];
+ /** Pre-fill values for the add-secret modal and auto-open it. Set by
+ * the route when the URL carries `?openAdd=1&name=…&secretId=…`,
+ * which is how the agent-facing `secrets.create` tool hands a user
+ * off to this page. */
+ prefill?: SecretPrefill;
}) {
const storageOptions = props.storageOptions ?? defaultStorageOptions;
const showProviderInfo = props.showProviderInfo ?? true;
@@ -239,8 +312,9 @@ export function SecretsPage(props: {
props.addSecretDescription ??
"Store a credential or API key. Values are kept in your system keychain when available, with a local encrypted file fallback.";
const secretProviderPlugins = useSecretProviderPlugins();
- const [addOpen, setAddOpen] = useState(false);
+ const [addOpen, setAddOpen] = useState(props.prefill != null);
const scopeId = useScope();
+ const formScopeId = secretFormScopeId(scopeId, props.prefill);
const scopeStack = useScopeStack();
const secrets = useAtomValue(secretsOptimisticAtom(scopeId));
const scopeLabel = (secretScopeId: ScopeId): string => {
@@ -252,12 +326,21 @@ export function SecretsPage(props: {
const existingSecretIds = useMemo(
() =>
AsyncResult.match(secrets, {
- onInitial: () => [] as string[],
- onFailure: () => [] as string[],
- onSuccess: ({ value }) => value.map((secret) => secret.id),
+ onInitial: () => [] as { readonly id: string; readonly scopeId: ScopeId }[],
+ onFailure: () => [] as { readonly id: string; readonly scopeId: ScopeId }[],
+ onSuccess: ({ value }) =>
+ value.map((secret) => ({ id: secret.id, scopeId: secret.scopeId })),
}),
[secrets],
);
+ const scopeOptions = useMemo(
+ () =>
+ scopeStack.map((entry, index) => ({
+ value: entry.id,
+ label: index === 0 ? "Personal" : entry.name || "Organization",
+ })),
+ [scopeStack],
+ );
const doRemove = useAtomSet(removeSecretOptimistic(scopeId), {
mode: "promiseExit",
});
@@ -393,8 +476,10 @@ export function SecretsPage(props: {
onOpenChange={setAddOpen}
description={addSecretDescription}
storageOptions={storageOptions}
- existingSecretIds={existingSecretIds}
- scopeId={scopeId}
+ existingSecrets={existingSecretIds}
+ scopeId={formScopeId}
+ scopeOptions={scopeOptions}
+ prefill={props.prefill}
/>
diff --git a/packages/react/src/plugins/secret-form.tsx b/packages/react/src/plugins/secret-form.tsx
index 27256453d..89f40c292 100644
--- a/packages/react/src/plugins/secret-form.tsx
+++ b/packages/react/src/plugins/secret-form.tsx
@@ -90,6 +90,10 @@ interface SecretFormProviderProps {
readonly existingSecretIds: readonly string[];
readonly suggestedName?: string;
readonly fallbackId?: string;
+ /** Force the secret id to a pre-allocated value (e.g. when the agent's
+ * `secrets.create` tool generated a UUID up front and the user is
+ * just here to provide the value). */
+ readonly initialIdOverride?: string;
readonly initialProvider?: string;
readonly scopeId: ScopeId;
readonly onCreated: (secretId: string) => void;
@@ -101,6 +105,7 @@ function SecretFormProvider(props: SecretFormProviderProps) {
existingSecretIds,
suggestedName = "",
fallbackId = "secret",
+ initialIdOverride,
initialProvider = "auto",
scopeId,
onCreated,
@@ -112,7 +117,7 @@ function SecretFormProvider(props: SecretFormProviderProps) {
const [state, setState] = useState