mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-19 15:14:21 +01:00
add copy button and make extension builtins any time (#1961)
This commit is contained in:
@@ -63,7 +63,7 @@ export default function App() {
|
||||
view: 'welcome',
|
||||
viewOptions: {},
|
||||
});
|
||||
const { getExtensions, addExtension, read } = useConfig();
|
||||
const { getExtensions, addExtension, read, upsert } = useConfig();
|
||||
const initAttemptedRef = useRef(false);
|
||||
|
||||
// Utility function to extract the command from the link
|
||||
@@ -91,6 +91,7 @@ export default function App() {
|
||||
const initializeApp = async () => {
|
||||
try {
|
||||
const config = window.electron.getConfig();
|
||||
|
||||
const provider = config.GOOSE_PROVIDER ?? (await read('GOOSE_PROVIDER', false));
|
||||
const model = config.GOOSE_MODEL ?? (await read('GOOSE_MODEL', false));
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ChatSmart, Idea, More, Refresh, Time, Send } from '../icons';
|
||||
import { FolderOpen, Moon, Sliders, Sun } from 'lucide-react';
|
||||
import { View } from '../../App';
|
||||
import { useConfig } from '../ConfigContext';
|
||||
import { toastService } from '../../toasts';
|
||||
|
||||
interface VersionInfo {
|
||||
current_version: string;
|
||||
@@ -262,7 +263,7 @@ export default function MoreMenu({
|
||||
await remove('GOOSE_PROVIDER', false);
|
||||
await remove('GOOSE_MODEL', false);
|
||||
setOpen(false);
|
||||
window.electron.createChatWindow();
|
||||
setView('welcome');
|
||||
}}
|
||||
danger
|
||||
subtitle="Clear selected model and restart (alpha)"
|
||||
|
||||
@@ -76,7 +76,7 @@ export async function extensionApiCall(
|
||||
} catch (error) {
|
||||
// Final catch-all error handler
|
||||
toastService.dismiss(toastId);
|
||||
const msg = error.length < 100 ? error : `Failed to ${action.presentTense} extension`;
|
||||
const msg = error.length < 70 ? error : `Failed to ${action.presentTense} extension`;
|
||||
toastService.error({
|
||||
title: extensionName,
|
||||
msg: msg,
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import type { ExtensionConfig } from '../../../api/types.gen';
|
||||
import { FixedExtensionEntry } from '../../ConfigContext';
|
||||
import builtInExtensionsData from './built-in-extensions.json';
|
||||
import bundledExtensionsData from './bundled-extensions.json';
|
||||
import { nameToKey } from './utils';
|
||||
|
||||
// Type definition for built-in extensions from JSON
|
||||
type BuiltinExtension = {
|
||||
type BundledExtension = {
|
||||
id: string;
|
||||
name: string;
|
||||
display_name: string;
|
||||
description: string;
|
||||
display_name?: string;
|
||||
description?: string;
|
||||
enabled: boolean;
|
||||
type: 'builtin';
|
||||
type: 'builtin' | 'stdio' | 'sse';
|
||||
cmd?: string;
|
||||
args?: string[];
|
||||
uri?: string;
|
||||
envs?: { [key: string]: string };
|
||||
timeout?: number;
|
||||
allow_configure?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -24,43 +28,61 @@ type BuiltinExtension = {
|
||||
* @param addExtensionFn Function to add a new extension to the config
|
||||
* @returns Promise that resolves when sync is complete
|
||||
*/
|
||||
export async function syncBuiltInExtensions(
|
||||
export async function syncBundledExtensions(
|
||||
existingExtensions: FixedExtensionEntry[],
|
||||
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>
|
||||
): Promise<void> {
|
||||
try {
|
||||
console.log('Setting up built-in extensions... in syncBuiltinExtensions');
|
||||
|
||||
// Create a set of existing extension IDs for quick lookup
|
||||
const existingExtensionKeys = new Set(existingExtensions.map((ext) => nameToKey(ext.name)));
|
||||
console.log('existing extension ids', existingExtensionKeys);
|
||||
|
||||
// Cast the imported JSON data to the expected type
|
||||
const builtinExtensions = builtInExtensionsData as BuiltinExtension[];
|
||||
const bundledExtensions = bundledExtensionsData as BundledExtension[];
|
||||
|
||||
// Track how many extensions were added
|
||||
let addedCount = 0;
|
||||
|
||||
// Check each built-in extension
|
||||
for (const builtinExt of builtinExtensions) {
|
||||
for (const bundledExt of bundledExtensions) {
|
||||
// Only add if the extension doesn't already exist -- use the id
|
||||
if (!existingExtensionKeys.has(builtinExt.id)) {
|
||||
console.log(`Adding built-in extension: ${builtinExt.id}`);
|
||||
|
||||
// Convert to the ExtensionConfig format
|
||||
const extConfig: ExtensionConfig = {
|
||||
name: builtinExt.name,
|
||||
display_name: builtinExt.display_name,
|
||||
type: 'builtin',
|
||||
timeout: builtinExt.timeout ?? 300,
|
||||
if (!existingExtensionKeys.has(bundledExt.id)) {
|
||||
console.log(`Adding built-in extension: ${bundledExt.id}`);
|
||||
let extConfig: ExtensionConfig;
|
||||
switch (bundledExt.type) {
|
||||
case 'builtin':
|
||||
extConfig = {
|
||||
name: bundledExt.name,
|
||||
display_name: bundledExt.display_name,
|
||||
type: bundledExt.type,
|
||||
timeout: bundledExt.timeout ?? 300,
|
||||
};
|
||||
|
||||
break;
|
||||
case 'stdio':
|
||||
extConfig = {
|
||||
name: bundledExt.name,
|
||||
description: bundledExt.description,
|
||||
type: bundledExt.type,
|
||||
timeout: bundledExt.timeout,
|
||||
cmd: bundledExt.cmd,
|
||||
args: bundledExt.args,
|
||||
envs: bundledExt.envs,
|
||||
};
|
||||
break;
|
||||
case 'sse':
|
||||
extConfig = {
|
||||
name: bundledExt.name,
|
||||
description: bundledExt.description,
|
||||
type: bundledExt.type,
|
||||
timeout: bundledExt.timeout,
|
||||
uri: bundledExt.uri,
|
||||
};
|
||||
}
|
||||
// Add the extension with its default enabled state
|
||||
try {
|
||||
await addExtensionFn(nameToKey(builtinExt.name), extConfig, builtinExt.enabled);
|
||||
await addExtensionFn(bundledExt.name, extConfig, bundledExt.enabled);
|
||||
addedCount++;
|
||||
} catch (error) {
|
||||
console.error(`Failed to add built-in extension ${builtinExt.name}:`, error);
|
||||
console.error(`Failed to add built-in extension ${bundledExt.name}:`, error);
|
||||
// Continue with other extensions even if one fails
|
||||
}
|
||||
}
|
||||
@@ -81,9 +103,9 @@ export async function syncBuiltInExtensions(
|
||||
* Function to initialize all built-in extensions for a first-time user.
|
||||
* This can be called when the application is first installed.
|
||||
*/
|
||||
export async function initializeBuiltInExtensions(
|
||||
export async function initializeBundledExtensions(
|
||||
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>
|
||||
): Promise<void> {
|
||||
// Call with an empty list to ensure all built-ins are added
|
||||
await syncBuiltInExtensions([], addExtensionFn);
|
||||
await syncBundledExtensions([], addExtensionFn);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ export {
|
||||
} from './extension-manager';
|
||||
|
||||
// Export built-in extension functions
|
||||
export { syncBuiltInExtensions, initializeBuiltInExtensions } from './built-in';
|
||||
export { syncBundledExtensions, initializeBundledExtensions } from './bundled-extensions';
|
||||
|
||||
// Export deeplink handling
|
||||
export { addExtensionFromDeepLink } from './deeplink';
|
||||
|
||||
@@ -4,13 +4,12 @@ import ModelSettingsButtons from './subcomponents/ModelSettingsButtons';
|
||||
import { useConfig } from '../../ConfigContext';
|
||||
import { toastError } from '../../../toasts';
|
||||
|
||||
import { UNKNOWN_PROVIDER_MSG, UNKNOWN_PROVIDER_TITLE } from './index';
|
||||
|
||||
interface ModelsSectionProps {
|
||||
setView: (view: View) => void;
|
||||
}
|
||||
|
||||
const UNKNOWN_PROVIDER_TITLE = 'Provider name error';
|
||||
const UNKNOWN_PROVIDER_MSG = 'Unknown provider in config -- please inspect your config.yaml';
|
||||
|
||||
export default function ModelsSection({ setView }: ModelsSectionProps) {
|
||||
const [provider, setProvider] = useState<string | null>(null);
|
||||
const [model, setModel] = useState<string>('');
|
||||
|
||||
@@ -8,13 +8,13 @@ import type { ExtensionConfig, FixedExtensionEntry } from '../../ConfigContext';
|
||||
// titles
|
||||
const CHANGE_MODEL_TOAST_TITLE = 'Model selected';
|
||||
const START_AGENT_TITLE = 'Initialize agent';
|
||||
const UNKNOWN_PROVIDER_TITLE = 'Provider name lookup';
|
||||
export const UNKNOWN_PROVIDER_TITLE = 'Provider name lookup';
|
||||
|
||||
// errors
|
||||
const SWITCH_MODEL_AGENT_ERROR_MSG = 'Failed to start agent with selected model';
|
||||
const CONFIG_UPDATE_ERROR_MSG = 'Failed to update configuration settings';
|
||||
const CONFIG_READ_MODEL_ERROR_MSG = 'Failed to read GOOSE_MODEL or GOOSE_PROVIDER from config';
|
||||
const UNKNOWN_PROVIDER_MSG = 'Unknown provider in config -- please inspect your config.yaml';
|
||||
export const UNKNOWN_PROVIDER_MSG = 'Unknown provider in config -- please inspect your config.yaml';
|
||||
|
||||
// success
|
||||
const SWITCH_MODEL_SUCCESS_MSG = 'Successfully switched models';
|
||||
@@ -114,11 +114,6 @@ export async function getCurrentModelAndProviderForDisplay({
|
||||
try {
|
||||
metadata = await getProviderMetadata(gooseProvider, getProviders);
|
||||
} catch (error) {
|
||||
toastError({
|
||||
title: UNKNOWN_PROVIDER_TITLE,
|
||||
msg: UNKNOWN_PROVIDER_MSG,
|
||||
traceback: error,
|
||||
});
|
||||
return { model: gooseModel, provider: gooseProvider };
|
||||
}
|
||||
const providerDisplayName = metadata.display_name;
|
||||
|
||||
@@ -6,6 +6,8 @@ import { useConfig } from '../../ConfigContext';
|
||||
import { ProviderDetails } from '../../../api/types.gen';
|
||||
import { initializeSystem } from '../../../utils/providerUtils';
|
||||
import WelcomeGooseLogo from '../../WelcomeGooseLogo';
|
||||
import { toastService } from '../../../toasts';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
interface ProviderSettingsProps {
|
||||
onClose: () => void;
|
||||
@@ -56,7 +58,6 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
|
||||
const provider_name = provider.name;
|
||||
const model = provider.metadata.default_model;
|
||||
|
||||
console.log(`Launching with provider: ${provider.name}`);
|
||||
try {
|
||||
// update the config
|
||||
// set GOOSE_PROVIDER in the config file
|
||||
@@ -76,6 +77,11 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
|
||||
} catch (error) {
|
||||
console.error(`Failed to initialize with provider ${provider_name}:`, error);
|
||||
}
|
||||
toastService.configure({ silent: false });
|
||||
toastService.success({
|
||||
title: 'Success!',
|
||||
msg: `Started goose with ${model} by ${provider.metadata.display_name}. You can change the model via the lower right corner.`,
|
||||
});
|
||||
onClose();
|
||||
},
|
||||
[onClose, upsert]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { toast, ToastOptions } from 'react-toastify';
|
||||
import React from 'react';
|
||||
import { Button } from './components/ui/button';
|
||||
|
||||
export interface ToastServiceOptions {
|
||||
silent?: boolean;
|
||||
@@ -123,12 +124,12 @@ export function toastError({ title, msg, traceback, toastOptions }: ToastErrorPr
|
||||
</div>
|
||||
<div className="flex-none flex items-center">
|
||||
{traceback ? (
|
||||
<button
|
||||
className="text-textProminentInverse font-medium"
|
||||
<Button
|
||||
className="text-textProminentInverse font-medium rt-variant-outline dark:bg-gray-300 bg-slate"
|
||||
onClick={() => navigator.clipboard.writeText(traceback)}
|
||||
>
|
||||
Copy error
|
||||
</button>
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>,
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Model } from '../components/settings/models/ModelContext';
|
||||
import { gooseModels } from '../components/settings/models/GooseModels';
|
||||
import { initializeAgent } from '../agent';
|
||||
import {
|
||||
initializeBuiltInExtensions,
|
||||
syncBuiltInExtensions,
|
||||
initializeBundledExtensions,
|
||||
syncBundledExtensions,
|
||||
addToAgentOnStartup,
|
||||
} from '../components/settings_v2/extensions';
|
||||
import { extractExtensionConfig } from '../components/settings_v2/extensions/utils';
|
||||
@@ -128,10 +128,10 @@ export const initializeSystem = async (
|
||||
let refreshedExtensions = await options.getExtensions(false);
|
||||
|
||||
if (refreshedExtensions.length === 0) {
|
||||
await initializeBuiltInExtensions(options.addExtension);
|
||||
await initializeBundledExtensions(options.addExtension);
|
||||
refreshedExtensions = await options.getExtensions(false);
|
||||
} else {
|
||||
await syncBuiltInExtensions(refreshedExtensions, options.addExtension);
|
||||
await syncBundledExtensions(refreshedExtensions, options.addExtension);
|
||||
}
|
||||
|
||||
// Add enabled extensions to agent
|
||||
|
||||
Reference in New Issue
Block a user