Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getDefaultFormData,
} from './utils';
import { useAgent } from '../../../agent/UpdateAgent';
import { activateExtension } from '.';

export default function ExtensionsSection() {
const { toggleExtension, getExtensions, addExtension, removeExtension } = useConfig();
Expand Down Expand Up @@ -61,7 +62,7 @@ export default function ExtensionsSection() {
const extensionConfig = createExtensionConfig(formData);

try {
await addExtension(formData.name, extensionConfig, formData.enabled);
await activateExtension(formData.name, extensionConfig, addExtension);
console.log('attempting to add extension');
await updateAgent(extensionConfig);
handleModalClose();
Expand All @@ -75,7 +76,7 @@ export default function ExtensionsSection() {
const extensionConfig = createExtensionConfig(formData);

try {
await addExtension(formData.name, extensionConfig, formData.enabled);
await activateExtension(formData.name, extensionConfig, addExtension);
handleModalClose();
fetchExtensions(); // Refresh the list after updating
} catch (error) {
Expand Down
103 changes: 67 additions & 36 deletions ui/desktop/src/components/settings_v2/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,72 @@ function handleError(message: string, shouldThrow = false): void {
}
}

// Update the path to the binary based on the command
async function replaceWithShims(cmd: string) {
const binaryPathMap: Record<string, string> = {
goosed: await window.electron.getBinaryPath('goosed'),
npx: await window.electron.getBinaryPath('npx'),
uvx: await window.electron.getBinaryPath('uvx'),
};

if (binaryPathMap[cmd]) {
console.log('--------> Replacing command with shim ------>', cmd, binaryPathMap[cmd]);
cmd = binaryPathMap[cmd];
}

return cmd;
}

/**
* Activates an extension by adding it to both the config system and the API.
* @param name The extension name
* @param config The extension configuration
* @param addExtensionFn Function to add extension to config
* @returns Promise that resolves when activation is complete
*/
export async function activateExtension(
name: string,
config: ExtensionConfig,
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>
): Promise<void> {
try {
// First add to the config system
await addExtensionFn(nameToKey(name), config, true);

// Then call the API endpoint
const response = await fetch(getApiUrl('/extensions/add'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': getSecretKey(),
},
body: JSON.stringify({
type: config.type,
name: nameToKey(name),
cmd: await replaceWithShims(config.cmd),
args: config.args || [],
env_keys: config.envs ? Object.keys(config.envs) : undefined,
timeout: config.timeout,
}),
});

const data = await response.json();

if (!data.error) {
toast.success(`Extension "${name}" has been successfully enabled`);
} else {
const errorMessage = `Error adding ${name} extension${data.message ? `. ${data.message}` : ''}`;
console.error(errorMessage);
toast.error(errorMessage, { autoClose: false });
}
} catch (error) {
const errorMessage = `Failed to add ${name} extension: ${error instanceof Error ? error.message : 'Unknown error'}`;
console.error(errorMessage);
toast.error(errorMessage, { autoClose: false });
throw error;
}
}

export async function addExtensionFromDeepLink(
url: string,
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>,
Expand Down Expand Up @@ -116,42 +182,7 @@ export async function addExtensionFromDeepLink(
}

// If no env vars are required, proceed with adding the extension
try {
// First add to the config system
await addExtensionFn(nameToKey(name), config, true);

// Then call the API endpoint
const response = await fetch(getApiUrl('/extensions/add'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': getSecretKey(),
},
body: JSON.stringify({
type: config.type,
name: nameToKey(name),
cmd: config.cmd,
args: config.args || [],
env_keys: config.envs ? Object.keys(config.envs) : undefined,
timeout: config.timeout,
}),
});

const data = await response.json();

if (!data.error) {
toast.success(`Extension "${name}" has been successfully enabled`);
} else {
const errorMessage = `Error adding ${name} extension${data.message ? `. ${data.message}` : ''}`;
console.error(errorMessage);
toast.error(errorMessage, { autoClose: false });
}
} catch (error) {
const errorMessage = `Failed to add ${name} extension: ${error instanceof Error ? error.message : 'Unknown error'}`;
console.error(errorMessage);
toast.error(errorMessage, { autoClose: false });
throw error;
}
await activateExtension(name, config, addExtensionFn);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions ui/desktop/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ app.on('open-url', async (event, url) => {
}
}

// Handle different types of deep links
if (parsedUrl.pathname === '/extension') {
// Handle extension install links
if (parsedUrl.hostname === 'extension') {
firstOpenWindow.webContents.send('add-extension', pendingDeepLink);
}
});
Expand Down Expand Up @@ -360,7 +360,7 @@ ipcMain.on('react-ready', (event) => {
console.log('Processing pending deep link:', pendingDeepLink);
const parsedUrl = new URL(pendingDeepLink);

if (parsedUrl.pathname === '/extension') {
if (parsedUrl.hostname === 'extension') {
console.log('Sending add-extension event');
firstOpenWindow.webContents.send('add-extension', pendingDeepLink);
}
Expand Down
Loading