fix redirect to extensions page after deeplink install and show toast with success message (#4863)

Co-authored-by: Amed Rodriguez <amed@squareup.com>
This commit is contained in:
Zane
2025-10-02 10:50:43 -07:00
committed by GitHub
parent 610967a55b
commit 031b8b7270
8 changed files with 128 additions and 52 deletions
@@ -24,6 +24,7 @@ interface ExtensionSectionProps {
disableConfiguration?: boolean;
customToggle?: (extension: FixedExtensionEntry) => Promise<boolean | void>;
selectedExtensions?: string[]; // Add controlled state
onModalClose?: (extensionName: string) => void;
}
export default function ExtensionsSection({
@@ -34,6 +35,7 @@ export default function ExtensionsSection({
disableConfiguration,
customToggle,
selectedExtensions = [],
onModalClose,
}: ExtensionSectionProps) {
const { getExtensions, addExtension, removeExtension, extensionsList } = useConfig();
const [selectedExtension, setSelectedExtension] = useState<FixedExtensionEntry | null>(null);
@@ -127,11 +129,15 @@ export default function ExtensionsSection({
extensionConfig: extensionConfig,
sessionId: sessionId,
});
// Immediately refresh the extensions list after successful activation
await fetchExtensions();
} catch (error) {
console.error('Failed to activate extension:', error);
} finally {
await fetchExtensions();
if (onModalClose) {
setTimeout(() => {
onModalClose(formData.name);
}, 200);
}
}
};
@@ -88,7 +88,8 @@ function getStreamableHttpConfig(
name: string,
description: string,
timeout: number,
headers?: { [key: string]: string }
headers?: { [key: string]: string },
envs?: { [key: string]: string }
) {
const config: ExtensionConfig = {
name,
@@ -97,6 +98,7 @@ function getStreamableHttpConfig(
description,
timeout: timeout,
headers: headers,
envs: envs,
};
return config;
@@ -114,14 +116,9 @@ export async function addExtensionFromDeepLink(
) => Promise<void>,
setView: (
view: string,
options:
| { extensionId: string; showEnvVars: boolean }
| { deepLinkConfig: ExtensionConfig; showEnvVars: boolean }
options: { showEnvVars: boolean; deepLinkConfig?: ExtensionConfig }
) => void
) {
console.log('=== addExtensionFromDeepLink Debug ===');
console.log('URL:', url);
const parsedUrl = new URL(url);
if (parsedUrl.protocol !== 'goose:') {
@@ -168,9 +165,21 @@ export async function addExtensionFromDeepLink(
)
: undefined;
// Parse env vars for remote extensions (same logic as stdio)
const envList = parsedUrl.searchParams.getAll('env');
const envs =
envList.length > 0
? Object.fromEntries(
envList.map((env) => {
const [key] = env.split('=');
return [key, ''];
})
)
: undefined;
const config = remoteUrl
? transportType === 'streamable_http'
? getStreamableHttpConfig(remoteUrl, name, description || '', timeout, headers)
? getStreamableHttpConfig(remoteUrl, name, description || '', timeout, headers, envs)
: getSseConfig(remoteUrl, name, description || '', timeout)
: getStdioConfig(cmd!, parsedUrl, name, description || '', timeout);
@@ -180,21 +189,25 @@ export async function addExtensionFromDeepLink(
config.type === 'streamable_http' && config.headers && Object.keys(config.headers).length > 0;
if (hasEnvVars || hasHeaders) {
console.log('Environment variables or headers required, redirecting to settings');
console.log('Calling setView with:', { deepLinkConfig: config, showEnvVars: true });
setView('settings', { deepLinkConfig: config, showEnvVars: true });
console.log(
'Environment variables or headers required, redirecting to extensions with env variables modal showing'
);
setView('extensions', { deepLinkConfig: config, showEnvVars: true });
return;
}
try {
console.log('No env vars required, activating extension directly');
// Note: deeplink activation doesn't have access to sessionId
// The extension will be added to config but not activated in the current session
// It will be activated when the next session starts
console.warn('Extension will be added to config but requires a session to activate');
await addExtensionFn(config.name, config, true);
} catch (error) {
console.error('Failed to activate extension from deeplink:', error);
throw error;
}
console.log('No env vars required, activating extension directly');
// Note: deeplink activation doesn't have access to sessionId
// The extension will be added to config but not activated in the current session
// It will be activated when the next session starts
await addExtensionFn(config.name, config, true);
// Show success toast and navigate to extensions page
toastService.success({
title: 'Extension Installed',
msg: `${config.name} extension has been installed successfully. Start a new chat session to use it.`,
});
// Navigate to extensions page to show the newly installed extension
setView('extensions', { deepLinkConfig: config, showEnvVars: false });
}
@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react';
import kebabCase from 'lodash/kebabCase';
import { Switch } from '../../../ui/switch';
import { Gear } from '../../../icons/Gear';
import { Gear } from '../../../icons';
import { FixedExtensionEntry } from '../../../ConfigContext';
import { getSubtitle, getFriendlyTitle } from './ExtensionList';
import { Card, CardHeader, CardTitle, CardContent, CardAction } from '../../../ui/card';
@@ -73,6 +74,7 @@ export default function ExtensionItem({
return (
<Card
id={`extension-${kebabCase(extension.name)}`}
className="transition-all duration-200 hover:shadow-default hover:cursor-pointer min-h-[120px] overflow-hidden"
onClick={() => handleToggle(extension)}
>