mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-21 08:04:20 +01:00
Fix remaining typescript errors (#2741)
This commit is contained in:
1968
ui/desktop/package-lock.json
generated
1968
ui/desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@
|
|||||||
"test-e2e:report": "playwright show-report",
|
"test-e2e:report": "playwright show-report",
|
||||||
"test-e2e:single": "npm run generate-api && playwright test -g",
|
"test-e2e:single": "npm run generate-api && playwright test -g",
|
||||||
"lint": "eslint \"src/**/*.{ts,tsx}\" --fix --no-warn-ignored",
|
"lint": "eslint \"src/**/*.{ts,tsx}\" --fix --no-warn-ignored",
|
||||||
"lint:check": "eslint \"src/**/*.{ts,tsx}\" --max-warnings 0 --no-warn-ignored",
|
"lint:check": "npm run typecheck && eslint \"src/**/*.{ts,tsx}\" --max-warnings 0 --no-warn-ignored",
|
||||||
"format": "prettier --write \"src/**/*.{ts,tsx,css,json}\"",
|
"format": "prettier --write \"src/**/*.{ts,tsx,css,json}\"",
|
||||||
"format:check": "prettier --check \"src/**/*.{ts,tsx,css,json}\"",
|
"format:check": "prettier --check \"src/**/*.{ts,tsx,css,json}\"",
|
||||||
"prepare": "cd ../.. && husky install",
|
"prepare": "cd ../.. && husky install",
|
||||||
@@ -67,12 +67,14 @@
|
|||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
"tailwindcss": "^3.4.14",
|
"tailwindcss": "^3.4.14",
|
||||||
|
"typescript": "~5.5.0",
|
||||||
"vite": "^6.3.4"
|
"vite": "^6.3.4"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"src/**/*.{ts,tsx}": [
|
"src/**/*.{ts,tsx}": [
|
||||||
|
"bash -c 'npm run typecheck'",
|
||||||
"eslint --fix --max-warnings 0 --no-warn-ignored",
|
"eslint --fix --max-warnings 0 --no-warn-ignored",
|
||||||
"prettier --write"
|
"prettier --write"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { IpcRendererEvent } from 'electron';
|
import { IpcRendererEvent } from 'electron';
|
||||||
import { openSharedSessionFromDeepLink } from './sessionLinks';
|
import { openSharedSessionFromDeepLink, type SessionLinksViewOptions } from './sessionLinks';
|
||||||
|
import { type SharedSessionDetails } from './sharedSessions';
|
||||||
import { initializeSystem } from './utils/providerUtils';
|
import { initializeSystem } from './utils/providerUtils';
|
||||||
import { ErrorUI } from './components/ErrorBoundary';
|
import { ErrorUI } from './components/ErrorBoundary';
|
||||||
import { ConfirmationModal } from './components/ui/ConfirmationModal';
|
import { ConfirmationModal } from './components/ui/ConfirmationModal';
|
||||||
@@ -9,6 +10,7 @@ import { toastService } from './toasts';
|
|||||||
import { extractExtensionName } from './components/settings/extensions/utils';
|
import { extractExtensionName } from './components/settings/extensions/utils';
|
||||||
import { GoosehintsModal } from './components/GoosehintsModal';
|
import { GoosehintsModal } from './components/GoosehintsModal';
|
||||||
import { type ExtensionConfig } from './extensions';
|
import { type ExtensionConfig } from './extensions';
|
||||||
|
import { type Recipe } from './recipe';
|
||||||
|
|
||||||
import ChatView from './components/ChatView';
|
import ChatView from './components/ChatView';
|
||||||
import SuspenseLoader from './suspense-loader';
|
import SuspenseLoader from './suspense-loader';
|
||||||
@@ -237,12 +239,18 @@ export default function App() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleOpenSharedSession = async (_event: IpcRendererEvent, link: string) => {
|
const handleOpenSharedSession = async (_event: IpcRendererEvent, ...args: unknown[]) => {
|
||||||
|
const link = args[0] as string;
|
||||||
window.electron.logInfo(`Opening shared session from deep link ${link}`);
|
window.electron.logInfo(`Opening shared session from deep link ${link}`);
|
||||||
setIsLoadingSharedSession(true);
|
setIsLoadingSharedSession(true);
|
||||||
setSharedSessionError(null);
|
setSharedSessionError(null);
|
||||||
try {
|
try {
|
||||||
await openSharedSessionFromDeepLink(link, setView);
|
await openSharedSessionFromDeepLink(
|
||||||
|
link,
|
||||||
|
(view: View, options?: SessionLinksViewOptions) => {
|
||||||
|
setView(view, options as ViewOptions);
|
||||||
|
}
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Unexpected error opening shared session:', error);
|
console.error('Unexpected error opening shared session:', error);
|
||||||
setView('sessions');
|
setView('sessions');
|
||||||
@@ -279,7 +287,8 @@ export default function App() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('Setting up fatal error handler');
|
console.log('Setting up fatal error handler');
|
||||||
const handleFatalError = (_event: IpcRendererEvent, errorMessage: string) => {
|
const handleFatalError = (_event: IpcRendererEvent, ...args: unknown[]) => {
|
||||||
|
const errorMessage = args[0] as string;
|
||||||
console.error('Encountered a fatal error: ', errorMessage);
|
console.error('Encountered a fatal error: ', errorMessage);
|
||||||
console.error('Current view:', view);
|
console.error('Current view:', view);
|
||||||
console.error('Is loading session:', isLoadingSession);
|
console.error('Is loading session:', isLoadingSession);
|
||||||
@@ -293,7 +302,8 @@ export default function App() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('Setting up view change handler');
|
console.log('Setting up view change handler');
|
||||||
const handleSetView = (_event: IpcRendererEvent, newView: View) => {
|
const handleSetView = (_event: IpcRendererEvent, ...args: unknown[]) => {
|
||||||
|
const newView = args[0] as View;
|
||||||
console.log(`Received view change request to: ${newView}`);
|
console.log(`Received view change request to: ${newView}`);
|
||||||
setView(newView);
|
setView(newView);
|
||||||
};
|
};
|
||||||
@@ -328,7 +338,8 @@ export default function App() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('Setting up extension handler');
|
console.log('Setting up extension handler');
|
||||||
const handleAddExtension = async (_event: IpcRendererEvent, link: string) => {
|
const handleAddExtension = async (_event: IpcRendererEvent, ...args: unknown[]) => {
|
||||||
|
const link = args[0] as string;
|
||||||
try {
|
try {
|
||||||
console.log(`Received add-extension event with link: ${link}`);
|
console.log(`Received add-extension event with link: ${link}`);
|
||||||
const command = extractCommand(link);
|
const command = extractCommand(link);
|
||||||
@@ -401,7 +412,7 @@ export default function App() {
|
|||||||
}, [STRICT_ALLOWLIST]);
|
}, [STRICT_ALLOWLIST]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleFocusInput = (_event: IpcRendererEvent) => {
|
const handleFocusInput = (_event: IpcRendererEvent, ..._args: unknown[]) => {
|
||||||
const inputField = document.querySelector('input[type="text"], textarea') as HTMLInputElement;
|
const inputField = document.querySelector('input[type="text"], textarea') as HTMLInputElement;
|
||||||
if (inputField) {
|
if (inputField) {
|
||||||
inputField.focus();
|
inputField.focus();
|
||||||
@@ -418,7 +429,9 @@ export default function App() {
|
|||||||
console.log(`Confirming installation of extension from: ${pendingLink}`);
|
console.log(`Confirming installation of extension from: ${pendingLink}`);
|
||||||
setModalVisible(false);
|
setModalVisible(false);
|
||||||
try {
|
try {
|
||||||
await addExtensionFromDeepLinkV2(pendingLink, addExtension, setView);
|
await addExtensionFromDeepLinkV2(pendingLink, addExtension, (view: string, options) => {
|
||||||
|
setView(view as View, options as ViewOptions);
|
||||||
|
});
|
||||||
console.log('Extension installation successful');
|
console.log('Extension installation successful');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to add extension:', error);
|
console.error('Failed to add extension:', error);
|
||||||
@@ -522,7 +535,9 @@ export default function App() {
|
|||||||
{view === 'schedules' && <SchedulesView onClose={() => setView('chat')} />}
|
{view === 'schedules' && <SchedulesView onClose={() => setView('chat')} />}
|
||||||
{view === 'sharedSession' && (
|
{view === 'sharedSession' && (
|
||||||
<SharedSessionView
|
<SharedSessionView
|
||||||
session={viewOptions?.sessionDetails}
|
session={
|
||||||
|
(viewOptions?.sessionDetails as unknown as SharedSessionDetails | null) || null
|
||||||
|
}
|
||||||
isLoading={isLoadingSharedSession}
|
isLoading={isLoadingSharedSession}
|
||||||
error={viewOptions?.error || sharedSessionError}
|
error={viewOptions?.error || sharedSessionError}
|
||||||
onBack={() => setView('sessions')}
|
onBack={() => setView('sessions')}
|
||||||
@@ -532,7 +547,9 @@ export default function App() {
|
|||||||
try {
|
try {
|
||||||
await openSharedSessionFromDeepLink(
|
await openSharedSessionFromDeepLink(
|
||||||
`goose://sessions/${viewOptions.shareToken}`,
|
`goose://sessions/${viewOptions.shareToken}`,
|
||||||
setView,
|
(view: View, options?: SessionLinksViewOptions) => {
|
||||||
|
setView(view, options as ViewOptions);
|
||||||
|
},
|
||||||
viewOptions.baseUrl
|
viewOptions.baseUrl
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -546,7 +563,7 @@ export default function App() {
|
|||||||
)}
|
)}
|
||||||
{view === 'recipeEditor' && (
|
{view === 'recipeEditor' && (
|
||||||
<RecipeEditor
|
<RecipeEditor
|
||||||
config={viewOptions?.config || window.electron.getConfig().recipeConfig}
|
config={(viewOptions?.config as Recipe) || window.electron.getConfig().recipeConfig}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{view === 'permission' && (
|
{view === 'permission' && (
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
interface AgentHeaderProps {
|
interface AgentHeaderProps {
|
||||||
title: string;
|
title: string;
|
||||||
profileInfo?: string;
|
profileInfo?: string;
|
||||||
|
|||||||
@@ -94,11 +94,7 @@ export default function ChatInput({
|
|||||||
|
|
||||||
// Set the image to loading state
|
// Set the image to loading state
|
||||||
setPastedImages((prev) =>
|
setPastedImages((prev) =>
|
||||||
prev.map((img) =>
|
prev.map((img) => (img.id === imageId ? { ...img, isLoading: true, error: undefined } : img))
|
||||||
img.id === imageId
|
|
||||||
? { ...img, isLoading: true, error: undefined }
|
|
||||||
: img
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -149,7 +145,8 @@ export default function ChatInput({
|
|||||||
|
|
||||||
// Debounced function to update actual value
|
// Debounced function to update actual value
|
||||||
const debouncedSetValue = useMemo(
|
const debouncedSetValue = useMemo(
|
||||||
() => debounce((value: string) => {
|
() =>
|
||||||
|
debounce((value: string) => {
|
||||||
setValue(value);
|
setValue(value);
|
||||||
}, 150),
|
}, 150),
|
||||||
[setValue]
|
[setValue]
|
||||||
@@ -157,7 +154,8 @@ export default function ChatInput({
|
|||||||
|
|
||||||
// Debounced autosize function
|
// Debounced autosize function
|
||||||
const debouncedAutosize = useMemo(
|
const debouncedAutosize = useMemo(
|
||||||
() => debounce((element: HTMLTextAreaElement) => {
|
() =>
|
||||||
|
debounce((element: HTMLTextAreaElement) => {
|
||||||
element.style.height = '0px'; // Reset height
|
element.style.height = '0px'; // Reset height
|
||||||
const scrollHeight = element.scrollHeight;
|
const scrollHeight = element.scrollHeight;
|
||||||
element.style.height = Math.min(scrollHeight, maxHeight) + 'px';
|
element.style.height = Math.min(scrollHeight, maxHeight) + 'px';
|
||||||
@@ -179,7 +177,7 @@ export default function ChatInput({
|
|||||||
|
|
||||||
const handlePaste = async (evt: React.ClipboardEvent<HTMLTextAreaElement>) => {
|
const handlePaste = async (evt: React.ClipboardEvent<HTMLTextAreaElement>) => {
|
||||||
const files = Array.from(evt.clipboardData.files || []);
|
const files = Array.from(evt.clipboardData.files || []);
|
||||||
const imageFiles = files.filter(file => file.type.startsWith('image/'));
|
const imageFiles = files.filter((file) => file.type.startsWith('image/'));
|
||||||
|
|
||||||
if (imageFiles.length === 0) return;
|
if (imageFiles.length === 0) return;
|
||||||
|
|
||||||
@@ -192,13 +190,13 @@ export default function ChatInput({
|
|||||||
id: `error-${Date.now()}`,
|
id: `error-${Date.now()}`,
|
||||||
dataUrl: '',
|
dataUrl: '',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: `Cannot paste ${imageFiles.length} image(s). Maximum ${MAX_IMAGES_PER_MESSAGE} images per message allowed.`
|
error: `Cannot paste ${imageFiles.length} image(s). Maximum ${MAX_IMAGES_PER_MESSAGE} images per message allowed.`,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Remove the error message after 3 seconds
|
// Remove the error message after 3 seconds
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setPastedImages((prev) => prev.filter(img => !img.id.startsWith('error-')));
|
setPastedImages((prev) => prev.filter((img) => !img.id.startsWith('error-')));
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -216,13 +214,13 @@ export default function ChatInput({
|
|||||||
id: errorId,
|
id: errorId,
|
||||||
dataUrl: '',
|
dataUrl: '',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: `Image too large (${Math.round(file.size / (1024 * 1024))}MB). Maximum ${MAX_IMAGE_SIZE_MB}MB allowed.`
|
error: `Image too large (${Math.round(file.size / (1024 * 1024))}MB). Maximum ${MAX_IMAGE_SIZE_MB}MB allowed.`,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Remove the error message after 3 seconds
|
// Remove the error message after 3 seconds
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setPastedImages((prev) => prev.filter(img => img.id !== errorId));
|
setPastedImages((prev) => prev.filter((img) => img.id !== errorId));
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -365,7 +363,9 @@ export default function ChatInput({
|
|||||||
LocalMessageStorage.addMessage(validPastedImageFilesPaths.join(' '));
|
LocalMessageStorage.addMessage(validPastedImageFilesPaths.join(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(new CustomEvent('submit', { detail: { value: textToSend } }));
|
handleSubmit(
|
||||||
|
new CustomEvent('submit', { detail: { value: textToSend } }) as unknown as React.FormEvent
|
||||||
|
);
|
||||||
|
|
||||||
setDisplayValue('');
|
setDisplayValue('');
|
||||||
setValue('');
|
setValue('');
|
||||||
@@ -502,7 +502,7 @@ export default function ChatInput({
|
|||||||
className="absolute -top-1 -right-1 bg-gray-700 hover:bg-red-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs leading-none opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity z-10"
|
className="absolute -top-1 -right-1 bg-gray-700 hover:bg-red-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs leading-none opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity z-10"
|
||||||
aria-label="Remove image"
|
aria-label="Remove image"
|
||||||
>
|
>
|
||||||
<Close size={14} />
|
<Close className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import {
|
|||||||
ToolResponseMessageContent,
|
ToolResponseMessageContent,
|
||||||
ToolConfirmationRequestMessageContent,
|
ToolConfirmationRequestMessageContent,
|
||||||
getTextContent,
|
getTextContent,
|
||||||
|
TextContent,
|
||||||
} from '../types/message';
|
} from '../types/message';
|
||||||
|
|
||||||
export interface ChatType {
|
export interface ChatType {
|
||||||
@@ -245,12 +246,20 @@ function ChatContent({
|
|||||||
|
|
||||||
// Create a new window for the recipe editor
|
// Create a new window for the recipe editor
|
||||||
console.log('Opening recipe editor with config:', response.recipe);
|
console.log('Opening recipe editor with config:', response.recipe);
|
||||||
|
const recipeConfig = {
|
||||||
|
id: response.recipe.title || 'untitled',
|
||||||
|
name: response.recipe.title || 'Untitled Recipe',
|
||||||
|
description: response.recipe.description || '',
|
||||||
|
instructions: response.recipe.instructions || '',
|
||||||
|
activities: response.recipe.activities || [],
|
||||||
|
prompt: response.recipe.prompt || '',
|
||||||
|
};
|
||||||
window.electron.createChatWindow(
|
window.electron.createChatWindow(
|
||||||
undefined, // query
|
undefined, // query
|
||||||
undefined, // dir
|
undefined, // dir
|
||||||
undefined, // version
|
undefined, // version
|
||||||
undefined, // resumeSessionId
|
undefined, // resumeSessionId
|
||||||
response.recipe, // recipe config
|
recipeConfig, // recipe config
|
||||||
'recipeEditor' // view type
|
'recipeEditor' // view type
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -273,11 +282,8 @@ function ChatContent({
|
|||||||
|
|
||||||
// Update chat messages when they change and save to sessionStorage
|
// Update chat messages when they change and save to sessionStorage
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setChat((prevChat: ChatType) => {
|
setChat({ ...chat, messages });
|
||||||
const updatedChat = { ...prevChat, messages };
|
}, [messages, setChat, chat]);
|
||||||
return updatedChat;
|
|
||||||
});
|
|
||||||
}, [messages, setChat]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (messages.length > 0) {
|
if (messages.length > 0) {
|
||||||
@@ -354,10 +360,11 @@ function ChatContent({
|
|||||||
// check if the last message is a real user's message
|
// check if the last message is a real user's message
|
||||||
if (lastMessage && isUserMessage(lastMessage) && !isToolResponse) {
|
if (lastMessage && isUserMessage(lastMessage) && !isToolResponse) {
|
||||||
// Get the text content from the last message before removing it
|
// Get the text content from the last message before removing it
|
||||||
const textContent = lastMessage.content.find((c) => c.type === 'text')?.text || '';
|
const textContent = lastMessage.content.find((c): c is TextContent => c.type === 'text');
|
||||||
|
const textValue = textContent?.text || '';
|
||||||
|
|
||||||
// Set the text back to the input field
|
// Set the text back to the input field
|
||||||
_setInput(textContent);
|
_setInput(textValue);
|
||||||
|
|
||||||
// Remove the last user message if it's the most recent one
|
// Remove the last user message if it's the most recent one
|
||||||
if (messages.length > 1) {
|
if (messages.length > 1) {
|
||||||
@@ -453,7 +460,8 @@ function ChatContent({
|
|||||||
return filteredMessages
|
return filteredMessages
|
||||||
.reduce<string[]>((history, message) => {
|
.reduce<string[]>((history, message) => {
|
||||||
if (isUserMessage(message)) {
|
if (isUserMessage(message)) {
|
||||||
const text = message.content.find((c) => c.type === 'text')?.text?.trim();
|
const textContent = message.content.find((c): c is TextContent => c.type === 'text');
|
||||||
|
const text = textContent?.text?.trim();
|
||||||
if (text) {
|
if (text) {
|
||||||
history.push(text);
|
history.push(text);
|
||||||
}
|
}
|
||||||
@@ -468,7 +476,7 @@ function ChatContent({
|
|||||||
const fetchSessionTokens = async () => {
|
const fetchSessionTokens = async () => {
|
||||||
try {
|
try {
|
||||||
const sessionDetails = await fetchSessionDetails(chat.id);
|
const sessionDetails = await fetchSessionDetails(chat.id);
|
||||||
setSessionTokenCount(sessionDetails.metadata.total_tokens);
|
setSessionTokenCount(sessionDetails.metadata.total_tokens || 0);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching session token count:', err);
|
console.error('Error fetching session token count:', err);
|
||||||
}
|
}
|
||||||
@@ -535,7 +543,7 @@ function ChatContent({
|
|||||||
{messages.length === 0 ? (
|
{messages.length === 0 ? (
|
||||||
<Splash
|
<Splash
|
||||||
append={append}
|
append={append}
|
||||||
activities={Array.isArray(recipeConfig?.activities) ? recipeConfig.activities : null}
|
activities={Array.isArray(recipeConfig?.activities) ? recipeConfig!.activities : null}
|
||||||
title={recipeConfig?.title}
|
title={recipeConfig?.title}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
|
|||||||
return extensionsList;
|
return extensionsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
const extensionResponse: ExtensionResponse = result.data;
|
const extensionResponse: ExtensionResponse = result.data!;
|
||||||
setExtensionsList(extensionResponse.extensions);
|
setExtensionsList(extensionResponse.extensions);
|
||||||
return extensionResponse.extensions;
|
return extensionResponse.extensions;
|
||||||
}
|
}
|
||||||
@@ -173,8 +173,8 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
|
|||||||
async (forceRefresh = false): Promise<ProviderDetails[]> => {
|
async (forceRefresh = false): Promise<ProviderDetails[]> => {
|
||||||
if (forceRefresh || providersList.length === 0) {
|
if (forceRefresh || providersList.length === 0) {
|
||||||
const response = await providers();
|
const response = await providers();
|
||||||
setProvidersList(response.data);
|
setProvidersList(response.data || []);
|
||||||
return response.data;
|
return response.data || [];
|
||||||
}
|
}
|
||||||
return providersList;
|
return providersList;
|
||||||
},
|
},
|
||||||
@@ -191,7 +191,7 @@ export const ConfigProvider: React.FC<ConfigProviderProps> = ({ children }) => {
|
|||||||
// Load providers
|
// Load providers
|
||||||
try {
|
try {
|
||||||
const providersResponse = await providers();
|
const providersResponse = await providers();
|
||||||
setProvidersList(providersResponse.data);
|
setProvidersList(providersResponse.data || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load providers:', error);
|
console.error('Failed to load providers:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function ErrorUI({ error }: { error: Error }) {
|
|||||||
|
|
||||||
export class ErrorBoundary extends React.Component<
|
export class ErrorBoundary extends React.Component<
|
||||||
{ children: React.ReactNode },
|
{ children: React.ReactNode },
|
||||||
{ error: Error; hasError: boolean }
|
{ error: Error | null; hasError: boolean }
|
||||||
> {
|
> {
|
||||||
constructor(props: { children: React.ReactNode }) {
|
constructor(props: { children: React.ReactNode }) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -69,7 +69,7 @@ export class ErrorBoundary extends React.Component<
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.hasError) {
|
if (this.state.hasError) {
|
||||||
return <ErrorUI error={this.state.error} />;
|
return <ErrorUI error={this.state.error || new Error('Unknown error')} />;
|
||||||
}
|
}
|
||||||
return this.props.children;
|
return this.props.children;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||||
|
|
||||||
declare var requestAnimationFrame: (callback: FrameRequestCallback) => number;
|
|
||||||
declare class HTMLCanvasElement {}
|
|
||||||
declare class HTMLImageElement {}
|
|
||||||
declare class DOMHighResTimeStamp {}
|
|
||||||
declare class Image {}
|
|
||||||
declare type FrameRequestCallback = (time: DOMHighResTimeStamp) => void;
|
|
||||||
import svg1 from '../images/loading-goose/1.svg';
|
import svg1 from '../images/loading-goose/1.svg';
|
||||||
import svg7 from '../images/loading-goose/7.svg';
|
import svg7 from '../images/loading-goose/7.svg';
|
||||||
|
|
||||||
@@ -20,9 +14,11 @@ interface FlappyGooseProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
|
const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
// eslint-disable-next-line no-undef
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||||
const [gameOver, setGameOver] = useState(false);
|
const [gameOver, setGameOver] = useState(false);
|
||||||
const [displayScore, setDisplayScore] = useState(0);
|
const [displayScore, setDisplayScore] = useState(0);
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
const gooseImages = useRef<HTMLImageElement[]>([]);
|
const gooseImages = useRef<HTMLImageElement[]>([]);
|
||||||
const framesLoaded = useRef(0);
|
const framesLoaded = useRef(0);
|
||||||
const [imagesReady, setImagesReady] = useState(false);
|
const [imagesReady, setImagesReady] = useState(false);
|
||||||
@@ -51,7 +47,7 @@ const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
|
|||||||
const OBSTACLE_WIDTH = 40;
|
const OBSTACLE_WIDTH = 40;
|
||||||
const FLAP_DURATION = 150;
|
const FLAP_DURATION = 150;
|
||||||
|
|
||||||
const safeRequestAnimationFrame = useCallback((callback: FrameRequestCallback) => {
|
const safeRequestAnimationFrame = useCallback((callback: (time: number) => void) => {
|
||||||
if (typeof window !== 'undefined' && typeof requestAnimationFrame !== 'undefined') {
|
if (typeof window !== 'undefined' && typeof requestAnimationFrame !== 'undefined') {
|
||||||
requestAnimationFrame(callback);
|
requestAnimationFrame(callback);
|
||||||
}
|
}
|
||||||
@@ -216,6 +212,7 @@ const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const frames = [svg1, svg7];
|
const frames = [svg1, svg7];
|
||||||
frames.forEach((src, index) => {
|
frames.forEach((src, index) => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
const img = new Image() as HTMLImageElement;
|
const img = new Image() as HTMLImageElement;
|
||||||
img.src = src;
|
img.src = src;
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
@@ -272,7 +269,9 @@ const FlappyGoose: React.FC<FlappyGooseProps> = ({ onClose }) => {
|
|||||||
onClick={flap}
|
onClick={flap}
|
||||||
>
|
>
|
||||||
<canvas
|
<canvas
|
||||||
ref={canvasRef}
|
ref={(el) => {
|
||||||
|
canvasRef.current = el;
|
||||||
|
}}
|
||||||
style={{
|
style={{
|
||||||
border: '2px solid #333',
|
border: '2px solid #333',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ interface GooseLogoProps {
|
|||||||
hover?: boolean;
|
hover?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function GooseLogo({ className = '', size = 'default', hover = true }: GooseLogoProps) {
|
export default function GooseLogo({
|
||||||
|
className = '',
|
||||||
|
size = 'default',
|
||||||
|
hover = true,
|
||||||
|
}: GooseLogoProps) {
|
||||||
const sizes = {
|
const sizes = {
|
||||||
default: {
|
default: {
|
||||||
frame: 'w-16 h-16',
|
frame: 'w-16 h-16',
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ export default function GooseMessage({
|
|||||||
{/* NOTE from alexhancock on 1/14/2025 - disabling again temporarily due to non-determinism in when the forms show up */}
|
{/* NOTE from alexhancock on 1/14/2025 - disabling again temporarily due to non-determinism in when the forms show up */}
|
||||||
{false && metadata && (
|
{false && metadata && (
|
||||||
<div className="flex mt-[16px]">
|
<div className="flex mt-[16px]">
|
||||||
<GooseResponseForm message={textContent} metadata={metadata} append={append} />
|
<GooseResponseForm message={textContent} metadata={metadata || null} append={append} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -132,9 +132,14 @@ export default function GooseResponseForm({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isForm(f: DynamicForm) {
|
function isForm(f: DynamicForm | null): f is DynamicForm {
|
||||||
return (
|
return (
|
||||||
f && f.title && f.description && f.fields && Array.isArray(f.fields) && f.fields.length > 0
|
!!f &&
|
||||||
|
!!f.title &&
|
||||||
|
!!f.description &&
|
||||||
|
!!f.fields &&
|
||||||
|
Array.isArray(f.fields) &&
|
||||||
|
f.fields.length > 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,9 +96,9 @@ type GoosehintsModalProps = {
|
|||||||
|
|
||||||
export const GoosehintsModal = ({ directory, setIsGoosehintsModalOpen }: GoosehintsModalProps) => {
|
export const GoosehintsModal = ({ directory, setIsGoosehintsModalOpen }: GoosehintsModalProps) => {
|
||||||
const goosehintsFilePath = `${directory}/.goosehints`;
|
const goosehintsFilePath = `${directory}/.goosehints`;
|
||||||
const [goosehintsFile, setGoosehintsFile] = useState<string>(null);
|
const [goosehintsFile, setGoosehintsFile] = useState<string>('');
|
||||||
const [goosehintsFileFound, setGoosehintsFileFound] = useState<boolean>(false);
|
const [goosehintsFileFound, setGoosehintsFileFound] = useState<boolean>(false);
|
||||||
const [goosehintsFileReadError, setGoosehintsFileReadError] = useState<string>(null);
|
const [goosehintsFileReadError, setGoosehintsFileReadError] = useState<string>('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchGoosehintsFile = async () => {
|
const fetchGoosehintsFile = async () => {
|
||||||
@@ -106,7 +106,7 @@ export const GoosehintsModal = ({ directory, setIsGoosehintsModalOpen }: Goosehi
|
|||||||
const { file, error, found } = await getGoosehintsFile(goosehintsFilePath);
|
const { file, error, found } = await getGoosehintsFile(goosehintsFilePath);
|
||||||
setGoosehintsFile(file);
|
setGoosehintsFile(file);
|
||||||
setGoosehintsFileFound(found);
|
setGoosehintsFileFound(found);
|
||||||
setGoosehintsFileReadError(error);
|
setGoosehintsFileReadError(error || '');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching .goosehints file:', error);
|
console.error('Error fetching .goosehints file:', error);
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ export const GoosehintsModal = ({ directory, setIsGoosehintsModalOpen }: Goosehi
|
|||||||
<ModalHelpText />
|
<ModalHelpText />
|
||||||
<div className="flex flex-col flex-1">
|
<div className="flex flex-col flex-1">
|
||||||
{goosehintsFileReadError ? (
|
{goosehintsFileReadError ? (
|
||||||
<ModalError error={goosehintsFileReadError} />
|
<ModalError error={new Error(goosehintsFileReadError)} />
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col flex-1 space-y-2 h-full">
|
<div className="flex flex-col flex-1 space-y-2 h-full">
|
||||||
<ModalFileInfo filePath={goosehintsFilePath} found={goosehintsFileFound} />
|
<ModalFileInfo filePath={goosehintsFilePath} found={goosehintsFileFound} />
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ async function fetchMetadata(url: string): Promise<Metadata> {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
title: title || url,
|
title: title || url,
|
||||||
description,
|
description: description || undefined,
|
||||||
favicon,
|
favicon,
|
||||||
image,
|
image: image || undefined,
|
||||||
url,
|
url,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export function LoadingPlaceholder() {
|
export function LoadingPlaceholder() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -43,10 +43,15 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
|||||||
});
|
});
|
||||||
}, [activeKeys]);
|
}, [activeKeys]);
|
||||||
|
|
||||||
const handleConfigure = async (provider: { id: string; name: string; isConfigured: boolean; description: string }) => {
|
const handleConfigure = async (provider: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isConfigured: boolean;
|
||||||
|
description: string;
|
||||||
|
}) => {
|
||||||
const providerId = provider.id.toLowerCase();
|
const providerId = provider.id.toLowerCase();
|
||||||
|
|
||||||
const modelName = getDefaultModel(providerId);
|
const modelName = getDefaultModel(providerId) || 'default-model';
|
||||||
const model = createSelectedModel(providerId, modelName);
|
const model = createSelectedModel(providerId, modelName);
|
||||||
|
|
||||||
await initializeSystem(providerId, model.name);
|
await initializeSystem(providerId, model.name);
|
||||||
@@ -63,7 +68,12 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
|||||||
onSubmit?.();
|
onSubmit?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddKeys = (provider: { id: string; name: string; isConfigured: boolean; description: string }) => {
|
const handleAddKeys = (provider: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isConfigured: boolean;
|
||||||
|
description: string;
|
||||||
|
}) => {
|
||||||
setSelectedId(provider.id);
|
setSelectedId(provider.id);
|
||||||
setShowSetupModal(true);
|
setShowSetupModal(true);
|
||||||
};
|
};
|
||||||
@@ -189,9 +199,9 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
|||||||
{showSetupModal && selectedId && (
|
{showSetupModal && selectedId && (
|
||||||
<div className="relative z-[9999]">
|
<div className="relative z-[9999]">
|
||||||
<ProviderSetupModal
|
<ProviderSetupModal
|
||||||
provider={providers.find((p) => p.id === selectedId)?.name}
|
provider={providers.find((p) => p.id === selectedId)?.name || 'Unknown Provider'}
|
||||||
model="Example Model"
|
_model="Example Model"
|
||||||
endpoint="Example Endpoint"
|
_endpoint="Example Endpoint"
|
||||||
onSubmit={handleModalSubmit}
|
onSubmit={handleModalSubmit}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setShowSetupModal(false);
|
setShowSetupModal(false);
|
||||||
|
|||||||
@@ -123,13 +123,13 @@ export default function RecipeEditor({ config }: RecipeEditorProps) {
|
|||||||
if (!extension) return null;
|
if (!extension) return null;
|
||||||
|
|
||||||
// Create a clean copy of the extension configuration
|
// Create a clean copy of the extension configuration
|
||||||
const cleanExtension = { ...extension };
|
const { enabled: _enabled, ...cleanExtension } = extension;
|
||||||
delete cleanExtension.enabled;
|
|
||||||
// Remove legacy envs which could potentially include secrets
|
// Remove legacy envs which could potentially include secrets
|
||||||
// env_keys will work but rely on the end user having setup those keys themselves
|
// env_keys will work but rely on the end user having setup those keys themselves
|
||||||
if ('envs' in cleanExtension) {
|
if ('envs' in cleanExtension) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
delete (cleanExtension as any).envs;
|
const { envs: _envs, ...finalExtension } = cleanExtension as any;
|
||||||
|
return finalExtension;
|
||||||
}
|
}
|
||||||
return cleanExtension;
|
return cleanExtension;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ function ToolCallView({
|
|||||||
|
|
||||||
const toolResults: { result: Content; isExpandToolResults: boolean }[] =
|
const toolResults: { result: Content; isExpandToolResults: boolean }[] =
|
||||||
loadingStatus === 'success' && Array.isArray(toolResponse?.toolResult.value)
|
loadingStatus === 'success' && Array.isArray(toolResponse?.toolResult.value)
|
||||||
? toolResponse.toolResult.value
|
? toolResponse!.toolResult.value
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
const audience = item.annotations?.audience as string[] | undefined;
|
const audience = item.annotations?.audience as string[] | undefined;
|
||||||
return !audience || audience.includes('user');
|
return !audience || audience.includes('user');
|
||||||
@@ -322,7 +322,7 @@ function ToolLogsView({
|
|||||||
working: boolean;
|
working: boolean;
|
||||||
isStartExpanded?: boolean;
|
isStartExpanded?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const boxRef = useRef(null);
|
const boxRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Whenever logs update, jump to the newest entry
|
// Whenever logs update, jump to the newest entry
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -69,13 +69,13 @@ export const SearchBar: React.FC<SearchBarProps> = ({
|
|||||||
}
|
}
|
||||||
}, [initialSearchTerm, caseSensitive, debouncedSearchRef]);
|
}, [initialSearchTerm, caseSensitive, debouncedSearchRef]);
|
||||||
|
|
||||||
const [localSearchResults, setLocalSearchResults] = useState<typeof searchResults>(null);
|
const [localSearchResults, setLocalSearchResults] = useState<typeof searchResults>(undefined);
|
||||||
|
|
||||||
// Sync external search results with local state
|
// Sync external search results with local state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only set results if we have a search term
|
// Only set results if we have a search term
|
||||||
if (!searchTerm) {
|
if (!searchTerm) {
|
||||||
setLocalSearchResults(null);
|
setLocalSearchResults(undefined);
|
||||||
} else {
|
} else {
|
||||||
setLocalSearchResults(searchResults);
|
setLocalSearchResults(searchResults);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ export const SearchView: React.FC<PropsWithChildren<SearchViewProps>> = ({
|
|||||||
<div
|
<div
|
||||||
ref={(el) => {
|
ref={(el) => {
|
||||||
if (el) {
|
if (el) {
|
||||||
containerRef.current = el;
|
containerRef.current = el as SearchContainerElement;
|
||||||
// Expose the highlighter instance
|
// Expose the highlighter instance
|
||||||
containerRef.current._searchHighlighter = highlighterRef.current;
|
containerRef.current._searchHighlighter = highlighterRef.current;
|
||||||
}
|
}
|
||||||
@@ -326,7 +326,7 @@ export const SearchView: React.FC<PropsWithChildren<SearchViewProps>> = ({
|
|||||||
onSearch={handleSearch}
|
onSearch={handleSearch}
|
||||||
onClose={handleCloseSearch}
|
onClose={handleCloseSearch}
|
||||||
onNavigate={handleNavigate}
|
onNavigate={handleNavigate}
|
||||||
searchResults={searchResults || internalSearchResults}
|
searchResults={searchResults || internalSearchResults || undefined}
|
||||||
inputRef={searchInputRef}
|
inputRef={searchInputRef}
|
||||||
initialSearchTerm={initialSearchTerm}
|
initialSearchTerm={initialSearchTerm}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function ArrowDown({ className = '' }) {
|
export default function ArrowDown({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function ArrowUp({ className = '' }) {
|
export default function ArrowUp({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Attach({ className = '' }) {
|
export default function Attach({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Back({ className = '' }) {
|
export default function Back({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export function Bars() {
|
export function Bars() {
|
||||||
return (
|
return (
|
||||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function ChatSmart({ className = '' }) {
|
export default function ChatSmart({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Check({ className = '' }) {
|
export default function Check({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function ChevronDown({ className }: { className?: string }) {
|
export default function ChevronDown({ className }: { className?: string }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function ChevronUp({ className = '' }) {
|
export default function ChevronUp({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Close({ className }: { className?: string }) {
|
export default function Close({ className }: { className?: string }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Copy({ className = '' }) {
|
export default function Copy({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Document({ className = '' }) {
|
export default function Document({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Edit({ className = '' }) {
|
export default function Edit({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export function Gear({ className = '' }: { className?: string }) {
|
export function Gear({ className = '' }: { className?: string }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
[key: string]: any; // This will allow any other SVG props to pass through
|
[key: string]: any; // This will allow any other SVG props to pass through
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export function Goose({ className = '' }) {
|
export function Goose({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Idea({ className = '' }) {
|
export default function Idea({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function More({ className = '' }) {
|
export default function More({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Refresh({ className = '' }) {
|
export default function Refresh({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Send({ className = '' }) {
|
export default function Send({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function SensitiveHidden({ className = '' }) {
|
export default function SensitiveHidden({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function SensitiveVisible({ className = '' }) {
|
export default function SensitiveVisible({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Settings({ className = '' }) {
|
export default function Settings({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Time({ className = '' }) {
|
export default function Time({ className = '' }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ import { FolderOpen, Moon, Sliders, Sun } from 'lucide-react';
|
|||||||
import { useConfig } from '../ConfigContext';
|
import { useConfig } from '../ConfigContext';
|
||||||
import { ViewOptions, View } from '../../App';
|
import { ViewOptions, View } from '../../App';
|
||||||
|
|
||||||
|
interface RecipeConfig {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
instructions?: string;
|
||||||
|
activities?: string[];
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
interface MenuButtonProps {
|
interface MenuButtonProps {
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -187,7 +196,7 @@ export default function MoreMenu({
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
window.electron.createChatWindow(
|
window.electron.createChatWindow(
|
||||||
undefined,
|
undefined,
|
||||||
window.appConfig.get('GOOSE_WORKING_DIR')
|
window.appConfig.get('GOOSE_WORKING_DIR') as string | undefined
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
subtitle="Start a new session in the current directory"
|
subtitle="Start a new session in the current directory"
|
||||||
@@ -244,7 +253,7 @@ export default function MoreMenu({
|
|||||||
undefined, // dir
|
undefined, // dir
|
||||||
undefined, // version
|
undefined, // version
|
||||||
undefined, // resumeSessionId
|
undefined, // resumeSessionId
|
||||||
recipeConfig, // recipe config
|
recipeConfig as RecipeConfig, // recipe config
|
||||||
'recipeEditor' // view type
|
'recipeEditor' // view type
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export default function MoreMenuLayout({
|
|||||||
>
|
>
|
||||||
<Document className="mr-1" />
|
<Document className="mr-1" />
|
||||||
<div className="max-w-[200px] truncate [direction:rtl]">
|
<div className="max-w-[200px] truncate [direction:rtl]">
|
||||||
{window.appConfig.get('GOOSE_WORKING_DIR')}
|
{String(window.appConfig.get('GOOSE_WORKING_DIR'))}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
@@ -54,7 +54,10 @@ export default function MoreMenuLayout({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
|
|
||||||
<MoreMenu setView={setView} setIsGoosehintsModalOpen={setIsGoosehintsModalOpen} />
|
<MoreMenu
|
||||||
|
setView={setView || (() => {})}
|
||||||
|
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen || (() => {})}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -128,65 +128,63 @@ function recipeToYaml(recipe: Recipe): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (recipe.extensions && recipe.extensions.length > 0) {
|
if (recipe.extensions && recipe.extensions.length > 0) {
|
||||||
cleanRecipe.extensions = recipe.extensions.map(ext => {
|
cleanRecipe.extensions = recipe.extensions.map((ext) => {
|
||||||
const cleanExt: CleanExtension = {
|
const cleanExt: CleanExtension = {
|
||||||
name: ext.name,
|
name: ext.name,
|
||||||
type: 'builtin', // Default type, will be overridden below
|
type: 'builtin', // Default type, will be overridden below
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle different extension types
|
// Handle different extension types using type assertions
|
||||||
if ('type' in ext && ext.type) {
|
if ('type' in ext && ext.type) {
|
||||||
cleanExt.type = ext.type as CleanExtension['type'];
|
cleanExt.type = ext.type as CleanExtension['type'];
|
||||||
|
|
||||||
// Add type-specific fields based on the ExtensionConfig union types
|
// Use type assertions to access properties safely
|
||||||
switch (ext.type) {
|
const extAny = ext as Record<string, unknown>;
|
||||||
case 'sse':
|
|
||||||
if ('uri' in ext && ext.uri) {
|
if (ext.type === 'sse' && extAny.uri) {
|
||||||
cleanExt.uri = ext.uri as string;
|
cleanExt.uri = extAny.uri as string;
|
||||||
|
} else if (ext.type === 'stdio') {
|
||||||
|
if (extAny.cmd) {
|
||||||
|
cleanExt.cmd = extAny.cmd as string;
|
||||||
}
|
}
|
||||||
break;
|
if (extAny.args) {
|
||||||
case 'stdio':
|
cleanExt.args = extAny.args as string[];
|
||||||
if ('cmd' in ext && ext.cmd) {
|
|
||||||
cleanExt.cmd = ext.cmd as string;
|
|
||||||
}
|
}
|
||||||
if ('args' in ext && ext.args) {
|
} else if (ext.type === 'builtin' && extAny.display_name) {
|
||||||
cleanExt.args = ext.args as string[];
|
cleanExt.display_name = extAny.display_name as string;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 'builtin':
|
// Handle frontend type separately to avoid TypeScript narrowing issues
|
||||||
if ('display_name' in ext && ext.display_name) {
|
if ((ext.type as string) === 'frontend') {
|
||||||
cleanExt.display_name = ext.display_name as string;
|
if (extAny.tools) {
|
||||||
|
cleanExt.tools = extAny.tools as unknown[];
|
||||||
}
|
}
|
||||||
break;
|
if (extAny.instructions) {
|
||||||
case 'frontend':
|
cleanExt.instructions = extAny.instructions as string;
|
||||||
if ('tools' in ext && ext.tools) {
|
|
||||||
cleanExt.tools = ext.tools as unknown[];
|
|
||||||
}
|
}
|
||||||
if ('instructions' in ext && ext.instructions) {
|
|
||||||
cleanExt.instructions = ext.instructions as string;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback: try to infer type from available fields
|
// Fallback: try to infer type from available fields
|
||||||
if ('cmd' in ext && ext.cmd) {
|
const extAny = ext as Record<string, unknown>;
|
||||||
|
|
||||||
|
if (extAny.cmd) {
|
||||||
cleanExt.type = 'stdio';
|
cleanExt.type = 'stdio';
|
||||||
cleanExt.cmd = ext.cmd as string;
|
cleanExt.cmd = extAny.cmd as string;
|
||||||
if ('args' in ext && ext.args) {
|
if (extAny.args) {
|
||||||
cleanExt.args = ext.args as string[];
|
cleanExt.args = extAny.args as string[];
|
||||||
}
|
}
|
||||||
} else if ('command' in ext && ext.command) {
|
} else if (extAny.command) {
|
||||||
// Handle legacy 'command' field by converting to 'cmd'
|
// Handle legacy 'command' field by converting to 'cmd'
|
||||||
cleanExt.type = 'stdio';
|
cleanExt.type = 'stdio';
|
||||||
cleanExt.cmd = ext.command as string;
|
cleanExt.cmd = extAny.command as string;
|
||||||
} else if ('uri' in ext && ext.uri) {
|
} else if (extAny.uri) {
|
||||||
cleanExt.type = 'sse';
|
cleanExt.type = 'sse';
|
||||||
cleanExt.uri = ext.uri as string;
|
cleanExt.uri = extAny.uri as string;
|
||||||
} else if ('tools' in ext && ext.tools) {
|
} else if (extAny.tools) {
|
||||||
cleanExt.type = 'frontend';
|
cleanExt.type = 'frontend';
|
||||||
cleanExt.tools = ext.tools as unknown[];
|
cleanExt.tools = extAny.tools as unknown[];
|
||||||
if ('instructions' in ext && ext.instructions) {
|
if (extAny.instructions) {
|
||||||
cleanExt.instructions = ext.instructions as string;
|
cleanExt.instructions = extAny.instructions as string;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Default to builtin if we can't determine type
|
// Default to builtin if we can't determine type
|
||||||
@@ -258,7 +256,8 @@ export const CreateScheduleModal: React.FC<CreateScheduleModalProps> = ({
|
|||||||
const [readableCronExpression, setReadableCronExpression] = useState<string>('');
|
const [readableCronExpression, setReadableCronExpression] = useState<string>('');
|
||||||
const [internalValidationError, setInternalValidationError] = useState<string | null>(null);
|
const [internalValidationError, setInternalValidationError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleDeepLinkChange = useCallback((value: string) => {
|
const handleDeepLinkChange = useCallback(
|
||||||
|
(value: string) => {
|
||||||
setDeepLinkInput(value);
|
setDeepLinkInput(value);
|
||||||
setInternalValidationError(null);
|
setInternalValidationError(null);
|
||||||
|
|
||||||
@@ -268,17 +267,24 @@ export const CreateScheduleModal: React.FC<CreateScheduleModalProps> = ({
|
|||||||
setParsedRecipe(recipe);
|
setParsedRecipe(recipe);
|
||||||
// Auto-populate schedule ID from recipe title if available
|
// Auto-populate schedule ID from recipe title if available
|
||||||
if (recipe.title && !scheduleId) {
|
if (recipe.title && !scheduleId) {
|
||||||
const cleanId = recipe.title.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-');
|
const cleanId = recipe.title
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9-]/g, '-')
|
||||||
|
.replace(/-+/g, '-');
|
||||||
setScheduleId(cleanId);
|
setScheduleId(cleanId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setParsedRecipe(null);
|
setParsedRecipe(null);
|
||||||
setInternalValidationError('Invalid deep link format. Please use a goose://bot or goose://recipe link.');
|
setInternalValidationError(
|
||||||
|
'Invalid deep link format. Please use a goose://bot or goose://recipe link.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setParsedRecipe(null);
|
setParsedRecipe(null);
|
||||||
}
|
}
|
||||||
}, [scheduleId]);
|
},
|
||||||
|
[scheduleId]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Check for pending deep link when modal opens
|
// Check for pending deep link when modal opens
|
||||||
@@ -610,7 +616,8 @@ export const CreateScheduleModal: React.FC<CreateScheduleModalProps> = ({
|
|||||||
instanceId="frequency-select-modal"
|
instanceId="frequency-select-modal"
|
||||||
options={frequencies}
|
options={frequencies}
|
||||||
value={frequencies.find((f) => f.value === frequency)}
|
value={frequencies.find((f) => f.value === frequency)}
|
||||||
onChange={(selectedOption: FrequencyOption | null) => {
|
onChange={(newValue: unknown) => {
|
||||||
|
const selectedOption = newValue as FrequencyOption | null;
|
||||||
if (selectedOption) setFrequency(selectedOption.value);
|
if (selectedOption) setFrequency(selectedOption.value);
|
||||||
}}
|
}}
|
||||||
placeholder="Select frequency..."
|
placeholder="Select frequency..."
|
||||||
|
|||||||
@@ -58,16 +58,40 @@ const parseCronExpression = (cron: string) => {
|
|||||||
if (dayOfMonth !== '*' && month !== '*' && dayOfWeek === '*') {
|
if (dayOfMonth !== '*' && month !== '*' && dayOfWeek === '*') {
|
||||||
return { frequency: 'once' as FrequencyValue, minutes, hours, dayOfMonth, month };
|
return { frequency: 'once' as FrequencyValue, minutes, hours, dayOfMonth, month };
|
||||||
}
|
}
|
||||||
if (minutes !== '*' && hours === '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*') {
|
if (
|
||||||
|
minutes !== '*' &&
|
||||||
|
hours === '*' &&
|
||||||
|
dayOfMonth === '*' &&
|
||||||
|
month === '*' &&
|
||||||
|
dayOfWeek === '*'
|
||||||
|
) {
|
||||||
return { frequency: 'hourly' as FrequencyValue, minutes };
|
return { frequency: 'hourly' as FrequencyValue, minutes };
|
||||||
}
|
}
|
||||||
if (minutes !== '*' && hours !== '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*') {
|
if (
|
||||||
|
minutes !== '*' &&
|
||||||
|
hours !== '*' &&
|
||||||
|
dayOfMonth === '*' &&
|
||||||
|
month === '*' &&
|
||||||
|
dayOfWeek === '*'
|
||||||
|
) {
|
||||||
return { frequency: 'daily' as FrequencyValue, minutes, hours };
|
return { frequency: 'daily' as FrequencyValue, minutes, hours };
|
||||||
}
|
}
|
||||||
if (minutes !== '*' && hours !== '*' && dayOfMonth === '*' && month === '*' && dayOfWeek !== '*') {
|
if (
|
||||||
|
minutes !== '*' &&
|
||||||
|
hours !== '*' &&
|
||||||
|
dayOfMonth === '*' &&
|
||||||
|
month === '*' &&
|
||||||
|
dayOfWeek !== '*'
|
||||||
|
) {
|
||||||
return { frequency: 'weekly' as FrequencyValue, minutes, hours, dayOfWeek };
|
return { frequency: 'weekly' as FrequencyValue, minutes, hours, dayOfWeek };
|
||||||
}
|
}
|
||||||
if (minutes !== '*' && hours !== '*' && dayOfMonth !== '*' && month === '*' && dayOfWeek === '*') {
|
if (
|
||||||
|
minutes !== '*' &&
|
||||||
|
hours !== '*' &&
|
||||||
|
dayOfMonth !== '*' &&
|
||||||
|
month === '*' &&
|
||||||
|
dayOfWeek === '*'
|
||||||
|
) {
|
||||||
return { frequency: 'monthly' as FrequencyValue, minutes, hours, dayOfMonth };
|
return { frequency: 'monthly' as FrequencyValue, minutes, hours, dayOfMonth };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,23 +131,31 @@ export const EditScheduleModal: React.FC<EditScheduleModalProps> = ({
|
|||||||
// For 'once', we'd need to reconstruct the date from cron parts
|
// For 'once', we'd need to reconstruct the date from cron parts
|
||||||
// This is complex, so we'll default to current date/time for now
|
// This is complex, so we'll default to current date/time for now
|
||||||
setSelectedDate(new Date().toISOString().split('T')[0]);
|
setSelectedDate(new Date().toISOString().split('T')[0]);
|
||||||
setSelectedTime(`${parsed.hours?.padStart(2, '0')}:${parsed.minutes?.padStart(2, '0')}`);
|
setSelectedTime(
|
||||||
|
`${parsed.hours?.padStart(2, '0')}:${parsed.minutes?.padStart(2, '0')}`
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 'hourly':
|
case 'hourly':
|
||||||
setSelectedMinute(parsed.minutes || '0');
|
setSelectedMinute(parsed.minutes || '0');
|
||||||
break;
|
break;
|
||||||
case 'daily':
|
case 'daily':
|
||||||
setSelectedTime(`${parsed.hours?.padStart(2, '0')}:${parsed.minutes?.padStart(2, '0')}`);
|
setSelectedTime(
|
||||||
|
`${parsed.hours?.padStart(2, '0')}:${parsed.minutes?.padStart(2, '0')}`
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 'weekly':
|
case 'weekly':
|
||||||
setSelectedTime(`${parsed.hours?.padStart(2, '0')}:${parsed.minutes?.padStart(2, '0')}`);
|
setSelectedTime(
|
||||||
|
`${parsed.hours?.padStart(2, '0')}:${parsed.minutes?.padStart(2, '0')}`
|
||||||
|
);
|
||||||
if (parsed.dayOfWeek) {
|
if (parsed.dayOfWeek) {
|
||||||
const days = parsed.dayOfWeek.split(',').map(d => d.trim());
|
const days = parsed.dayOfWeek.split(',').map((d) => d.trim());
|
||||||
setSelectedDaysOfWeek(new Set(days));
|
setSelectedDaysOfWeek(new Set(days));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'monthly':
|
case 'monthly':
|
||||||
setSelectedTime(`${parsed.hours?.padStart(2, '0')}:${parsed.minutes?.padStart(2, '0')}`);
|
setSelectedTime(
|
||||||
|
`${parsed.hours?.padStart(2, '0')}:${parsed.minutes?.padStart(2, '0')}`
|
||||||
|
);
|
||||||
setSelectedDayOfMonth(parsed.dayOfMonth || '1');
|
setSelectedDayOfMonth(parsed.dayOfMonth || '1');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -287,7 +319,8 @@ export const EditScheduleModal: React.FC<EditScheduleModalProps> = ({
|
|||||||
instanceId="frequency-select-modal"
|
instanceId="frequency-select-modal"
|
||||||
options={frequencies}
|
options={frequencies}
|
||||||
value={frequencies.find((f) => f.value === frequency)}
|
value={frequencies.find((f) => f.value === frequency)}
|
||||||
onChange={(selectedOption: FrequencyOption | null) => {
|
onChange={(newValue: unknown) => {
|
||||||
|
const selectedOption = newValue as FrequencyOption | null;
|
||||||
if (selectedOption) setFrequency(selectedOption.value);
|
if (selectedOption) setFrequency(selectedOption.value);
|
||||||
}}
|
}}
|
||||||
placeholder="Select frequency..."
|
placeholder="Select frequency..."
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import { ScrollArea } from '../ui/scroll-area';
|
|||||||
import MarkdownContent from '../MarkdownContent';
|
import MarkdownContent from '../MarkdownContent';
|
||||||
import ToolCallWithResponse from '../ToolCallWithResponse';
|
import ToolCallWithResponse from '../ToolCallWithResponse';
|
||||||
import ImagePreview from '../ImagePreview';
|
import ImagePreview from '../ImagePreview';
|
||||||
import { ToolRequestMessageContent, ToolResponseMessageContent } from '../../types/message';
|
import {
|
||||||
|
ToolRequestMessageContent,
|
||||||
|
ToolResponseMessageContent,
|
||||||
|
TextContent,
|
||||||
|
} from '../../types/message';
|
||||||
import { type Message } from '../../types/message';
|
import { type Message } from '../../types/message';
|
||||||
import { formatMessageTimestamp } from '../../utils/timeUtils';
|
import { formatMessageTimestamp } from '../../utils/timeUtils';
|
||||||
import { extractImagePaths, removeImagePathsFromText } from '../../utils/imageUtils';
|
import { extractImagePaths, removeImagePathsFromText } from '../../utils/imageUtils';
|
||||||
@@ -109,7 +113,7 @@ export const SessionMessages: React.FC<SessionMessagesProps> = ({
|
|||||||
.map((message, index) => {
|
.map((message, index) => {
|
||||||
// Extract text content from the message
|
// Extract text content from the message
|
||||||
let textContent = message.content
|
let textContent = message.content
|
||||||
.filter((c) => c.type === 'text')
|
.filter((c): c is TextContent => c.type === 'text')
|
||||||
.map((c) => c.text)
|
.map((c) => c.text)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
|
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ const SharedSessionView: React.FC<SharedSessionViewProps> = ({
|
|||||||
<div className="flex items-center text-sm text-textSubtle mt-1 space-x-5">
|
<div className="flex items-center text-sm text-textSubtle mt-1 space-x-5">
|
||||||
<span className="flex items-center">
|
<span className="flex items-center">
|
||||||
<Calendar className="w-4 h-4 mr-1" />
|
<Calendar className="w-4 h-4 mr-1" />
|
||||||
{formatMessageTimestamp(session.messages[0]?.created)}
|
{session ? formatMessageTimestamp(session.messages[0]?.created) : 'Unknown'}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center">
|
<span className="flex items-center">
|
||||||
<MessageSquareText className="w-4 h-4 mr-1" />
|
<MessageSquareText className="w-4 h-4 mr-1" />
|
||||||
{session.message_count}
|
{session ? session.message_count : 0}
|
||||||
</span>
|
</span>
|
||||||
{session.total_tokens !== null && (
|
{session && session.total_tokens !== null && (
|
||||||
<span className="flex items-center">
|
<span className="flex items-center">
|
||||||
<Target className="w-4 h-4 mr-1" />
|
<Target className="w-4 h-4 mr-1" />
|
||||||
{session.total_tokens.toLocaleString()}
|
{session.total_tokens.toLocaleString()}
|
||||||
@@ -49,7 +49,7 @@ const SharedSessionView: React.FC<SharedSessionViewProps> = ({
|
|||||||
<div className="flex items-center text-sm text-textSubtle space-x-5">
|
<div className="flex items-center text-sm text-textSubtle space-x-5">
|
||||||
<span className="flex items-center">
|
<span className="flex items-center">
|
||||||
<Folder className="w-4 h-4 mr-1" />
|
<Folder className="w-4 h-4 mr-1" />
|
||||||
{session.working_dir}
|
{session ? session.working_dir : 'Unknown'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -404,8 +404,12 @@ export function OllamaBattleGame({ onComplete, requiredKeys: _ }: OllamaBattleGa
|
|||||||
!battleState.processingAction && (
|
!battleState.processingAction && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{(typeof battleSteps[battleState.currentStep].choices === 'function'
|
{(typeof battleSteps[battleState.currentStep].choices === 'function'
|
||||||
? (battleSteps[battleState.currentStep].choices as (choice: string) => string[])(battleState.lastChoice || '')
|
? (
|
||||||
: battleSteps[battleState.currentStep].choices as string[]
|
battleSteps[battleState.currentStep].choices as (
|
||||||
|
choice: string
|
||||||
|
) => string[]
|
||||||
|
)(battleState.lastChoice || '')
|
||||||
|
: (battleSteps[battleState.currentStep].choices as string[])
|
||||||
)?.map((choice: string) => (
|
)?.map((choice: string) => (
|
||||||
<button
|
<button
|
||||||
key={choice}
|
key={choice}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function ProviderSetupModal({
|
|||||||
const [configValues, setConfigValues] = React.useState<{ [key: string]: string }>(
|
const [configValues, setConfigValues] = React.useState<{ [key: string]: string }>(
|
||||||
default_key_value
|
default_key_value
|
||||||
);
|
);
|
||||||
const requiredKeys = required_keys[provider] || ['API Key'];
|
const requiredKeys = (required_keys as Record<string, string[]>)[provider] || ['API Key'];
|
||||||
const headerText = title || `Setup ${provider}`;
|
const headerText = title || `Setup ${provider}`;
|
||||||
|
|
||||||
const shouldShowBattle = React.useMemo(() => {
|
const shouldShowBattle = React.useMemo(() => {
|
||||||
@@ -59,7 +59,7 @@ export function ProviderSetupModal({
|
|||||||
) : (
|
) : (
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="mt-[24px] space-y-4">
|
<div className="mt-[24px] space-y-4">
|
||||||
{requiredKeys.map((keyName) => (
|
{requiredKeys.map((keyName: string) => (
|
||||||
<div key={keyName}>
|
<div key={keyName}>
|
||||||
<Input
|
<Input
|
||||||
type={isSecretKey(keyName) ? 'password' : 'text'}
|
type={isSecretKey(keyName) ? 'password' : 'text'}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ const DEFAULT_SETTINGS: SettingsType = {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// @ts-expect-error "we actually do always have all the properties required for builtins, but tsc cannot tell for some reason"
|
|
||||||
extensions: BUILT_IN_EXTENSIONS,
|
extensions: BUILT_IN_EXTENSIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ export async function getActiveProviders(): Promise<string[]> {
|
|||||||
const configStatus = provider.config_status ?? {};
|
const configStatus = provider.config_status ?? {};
|
||||||
|
|
||||||
// Skip if provider isn't in required_keys
|
// Skip if provider isn't in required_keys
|
||||||
if (!required_keys[providerName]) return false;
|
if (!required_keys[providerName as keyof typeof required_keys]) return false;
|
||||||
|
|
||||||
// Get all required keys for this provider
|
// Get all required keys for this provider
|
||||||
const providerRequiredKeys = required_keys[providerName];
|
const providerRequiredKeys = required_keys[providerName as keyof typeof required_keys];
|
||||||
|
|
||||||
// Special case: If a provider has exactly one required key and that key
|
// Special case: If a provider has exactly one required key and that key
|
||||||
// has a default value, check if it's explicitly set
|
// has a default value, check if it's explicitly set
|
||||||
@@ -103,14 +103,17 @@ export async function getConfigSettings(): Promise<Record<string, ProviderRespon
|
|||||||
supported: true,
|
supported: true,
|
||||||
description: provider.metadata.description,
|
description: provider.metadata.description,
|
||||||
models: provider.metadata.models,
|
models: provider.metadata.models,
|
||||||
config_status: providerRequiredKeys.reduce<Record<string, ConfigDetails>>((acc: Record<string, ConfigDetails>, key: string) => {
|
config_status: providerRequiredKeys.reduce<Record<string, ConfigDetails>>(
|
||||||
|
(acc: Record<string, ConfigDetails>, key: string) => {
|
||||||
acc[key] = {
|
acc[key] = {
|
||||||
key,
|
key,
|
||||||
is_set: provider.is_configured,
|
is_set: provider.is_configured,
|
||||||
location: provider.is_configured ? 'config' : undefined,
|
location: provider.is_configured ? 'config' : undefined,
|
||||||
};
|
};
|
||||||
return acc;
|
return acc;
|
||||||
}, {}),
|
},
|
||||||
|
{}
|
||||||
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function ConfigureApproveMode({
|
|||||||
|
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
try {
|
try {
|
||||||
handleModeChange(approveMode);
|
handleModeChange(approveMode || '');
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error configuring goose mode:', error);
|
console.error('Error configuring goose mode:', error);
|
||||||
@@ -71,7 +71,7 @@ export function ConfigureApproveMode({
|
|||||||
key={mode.key}
|
key={mode.key}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
showDescription={true}
|
showDescription={true}
|
||||||
currentMode={approveMode}
|
currentMode={approveMode || ''}
|
||||||
isApproveModeConfigure={true}
|
isApproveModeConfigure={true}
|
||||||
handleModeChange={(newMode) => {
|
handleModeChange={(newMode) => {
|
||||||
setApproveMode(newMode);
|
setApproveMode(newMode);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function ConfigureBuiltInExtensionModal({
|
|||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
try {
|
try {
|
||||||
// First store all environment variables
|
// First store all environment variables
|
||||||
if (extension.env_keys?.length > 0) {
|
if (extension.env_keys && extension.env_keys.length > 0) {
|
||||||
for (const envKey of extension.env_keys) {
|
for (const envKey of extension.env_keys) {
|
||||||
const value = envValues[envKey];
|
const value = envValues[envKey];
|
||||||
if (!value) continue;
|
if (!value) continue;
|
||||||
@@ -103,13 +103,13 @@ export function ConfigureBuiltInExtensionModal({
|
|||||||
{/* Form */}
|
{/* Form */}
|
||||||
<form onSubmit={handleExtensionConfigSubmit}>
|
<form onSubmit={handleExtensionConfigSubmit}>
|
||||||
<div className="mt-[24px]">
|
<div className="mt-[24px]">
|
||||||
{extension.env_keys?.length > 0 ? (
|
{extension.env_keys && extension.env_keys.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-6">
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-6">
|
||||||
Please provide the required environment variables for this extension:
|
Please provide the required environment variables for this extension:
|
||||||
</p>
|
</p>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{extension.env_keys?.map((envVarName) => (
|
{extension.env_keys.map((envVarName) => (
|
||||||
<div key={envVarName}>
|
<div key={envVarName}>
|
||||||
<label
|
<label
|
||||||
htmlFor={envVarName}
|
htmlFor={envVarName}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export function ConfigureExtensionModal({
|
|||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
try {
|
try {
|
||||||
// First store all environment variables
|
// First store all environment variables
|
||||||
if (extension.env_keys?.length > 0) {
|
if (extension.env_keys && extension.env_keys.length > 0) {
|
||||||
for (const envKey of extension.env_keys) {
|
for (const envKey of extension.env_keys) {
|
||||||
const value = envValues[envKey];
|
const value = envValues[envKey];
|
||||||
if (!value) continue;
|
if (!value) continue;
|
||||||
@@ -105,13 +105,13 @@ export function ConfigureExtensionModal({
|
|||||||
{/* Form */}
|
{/* Form */}
|
||||||
<form onSubmit={handleExtensionConfigSubmit}>
|
<form onSubmit={handleExtensionConfigSubmit}>
|
||||||
<div className="mt-[24px]">
|
<div className="mt-[24px]">
|
||||||
{extension.env_keys?.length > 0 ? (
|
{extension.env_keys && extension.env_keys.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-6">
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-6">
|
||||||
Please provide the required environment variables for this extension:
|
Please provide the required environment variables for this extension:
|
||||||
</p>
|
</p>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{extension.env_keys?.map((envVarName) => (
|
{extension.env_keys.map((envVarName) => (
|
||||||
<div key={envVarName}>
|
<div key={envVarName}>
|
||||||
<label
|
<label
|
||||||
htmlFor={envVarName}
|
htmlFor={envVarName}
|
||||||
|
|||||||
@@ -97,7 +97,10 @@ export function ManualExtensionModal({ isOpen, onClose, onSubmit }: ManualExtens
|
|||||||
resetForm();
|
resetForm();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error configuring extension:', error);
|
console.error('Error configuring extension:', error);
|
||||||
toastError({ title: 'Failed to configure extension', traceback: error.message });
|
toastError({
|
||||||
|
title: 'Failed to configure extension',
|
||||||
|
traceback: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -142,9 +145,13 @@ export function ManualExtensionModal({ isOpen, onClose, onSubmit }: ManualExtens
|
|||||||
<Select
|
<Select
|
||||||
options={typeOptions}
|
options={typeOptions}
|
||||||
value={typeOptions.find((option) => option.value === formData.type)}
|
value={typeOptions.find((option) => option.value === formData.type)}
|
||||||
onChange={(option: { value: string; label: string } | null) =>
|
onChange={(newValue: unknown) => {
|
||||||
setFormData({ ...formData, type: option?.value as FullExtensionConfig['type'] })
|
const option = newValue as { value: string; label: string } | null;
|
||||||
}
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
type: option?.value as FullExtensionConfig['type'],
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ export function AddModelInline() {
|
|||||||
|
|
||||||
const [selectedProvider, setSelectedProvider] = useState<string | null>(null);
|
const [selectedProvider, setSelectedProvider] = useState<string | null>(null);
|
||||||
const [modelName, setModelName] = useState<string>('');
|
const [modelName, setModelName] = useState<string>('');
|
||||||
const [filteredModels, setFilteredModels] = useState<{ id: string; name: string; provider: string }[]>([]);
|
const [filteredModels, setFilteredModels] = useState<
|
||||||
|
{ id: string; name: string; provider: string }[]
|
||||||
|
>([]);
|
||||||
const [showSuggestions, setShowSuggestions] = useState(false);
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||||
const handleModelSelection = useHandleModelSelection();
|
const handleModelSelection = useHandleModelSelection();
|
||||||
|
|
||||||
@@ -37,7 +39,12 @@ export function AddModelInline() {
|
|||||||
model.provider.toLowerCase() === selectedProvider &&
|
model.provider.toLowerCase() === selectedProvider &&
|
||||||
model.name.toLowerCase().includes(modelName.toLowerCase())
|
model.name.toLowerCase().includes(modelName.toLowerCase())
|
||||||
)
|
)
|
||||||
.slice(0, 5); // Limit suggestions to top 5
|
.slice(0, 5) // Limit suggestions to top 5
|
||||||
|
.map((model) => ({
|
||||||
|
id: String(model.id || ''),
|
||||||
|
name: model.name,
|
||||||
|
provider: model.provider,
|
||||||
|
}));
|
||||||
setFilteredModels(filtered);
|
setFilteredModels(filtered);
|
||||||
setShowSuggestions(filtered.length > 0);
|
setShowSuggestions(filtered.length > 0);
|
||||||
}, [modelName, selectedProvider]);
|
}, [modelName, selectedProvider]);
|
||||||
@@ -76,7 +83,8 @@ export function AddModelInline() {
|
|||||||
<Select
|
<Select
|
||||||
options={providerOptions}
|
options={providerOptions}
|
||||||
value={providerOptions.find((option) => option.value === selectedProvider) || null}
|
value={providerOptions.find((option) => option.value === selectedProvider) || null}
|
||||||
onChange={(option: { value: string | null }) => {
|
onChange={(newValue: unknown) => {
|
||||||
|
const option = newValue as { value: string | null } | null;
|
||||||
setSelectedProvider(option?.value || null);
|
setSelectedProvider(option?.value || null);
|
||||||
setModelName(''); // Clear model name when provider changes
|
setModelName(''); // Clear model name when provider changes
|
||||||
setFilteredModels([]);
|
setFilteredModels([]);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useRecentModels } from './RecentModels';
|
import { useRecentModels } from './RecentModels';
|
||||||
import { useModel, Model } from './ModelContext';
|
import { useModel, Model } from './ModelContext';
|
||||||
import { useHandleModelSelection } from './utils';
|
import { useHandleModelSelection } from './utils';
|
||||||
import type { View } from '@/src/App';
|
import type { View } from '../../../App';
|
||||||
|
|
||||||
interface ModelRadioListProps {
|
interface ModelRadioListProps {
|
||||||
renderItem: (props: {
|
renderItem: (props: {
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ import { useModel } from './ModelContext';
|
|||||||
import { useHandleModelSelection } from './utils';
|
import { useHandleModelSelection } from './utils';
|
||||||
|
|
||||||
// Create a mapping from provider name to href
|
// Create a mapping from provider name to href
|
||||||
const providerLinks = model_docs_link.reduce((acc, { name, href }) => {
|
const providerLinks: Record<string, string> = model_docs_link.reduce(
|
||||||
|
(acc, { name, href }) => {
|
||||||
acc[name] = href;
|
acc[name] = href;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
);
|
||||||
|
|
||||||
export function ProviderButtons() {
|
export function ProviderButtons() {
|
||||||
const { activeKeys } = useActiveKeys();
|
const { activeKeys } = useActiveKeys();
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function useHandleModelSelection() {
|
|||||||
toastError({
|
toastError({
|
||||||
title: model.name,
|
title: model.name,
|
||||||
msg: `Failed to switch to model`,
|
msg: `Failed to switch to model`,
|
||||||
traceback: error.message,
|
traceback: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ function BaseProviderCard({
|
|||||||
onTakeoff,
|
onTakeoff,
|
||||||
showTakeoff,
|
showTakeoff,
|
||||||
}: BaseProviderCardProps) {
|
}: BaseProviderCardProps) {
|
||||||
const numRequiredKeys = required_keys[name]?.length || 0;
|
const numRequiredKeys = (required_keys as Record<string, string[]>)[name]?.length || 0;
|
||||||
const tooltipText = numRequiredKeys === 1 ? `Add ${name} API Key` : `Add ${name} API Keys`;
|
const tooltipText = numRequiredKeys === 1 ? `Add ${name} API Key` : `Add ${name} API Keys`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -254,7 +254,8 @@ export function BaseProviderGrid({
|
|||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-[repeat(auto-fill,_minmax(140px,_1fr))] gap-3 [&_*]:z-20">
|
<div className="grid grid-cols-[repeat(auto-fill,_minmax(140px,_1fr))] gap-3 [&_*]:z-20">
|
||||||
{providers.map((provider) => {
|
{providers.map((provider) => {
|
||||||
const hasRequiredKeys = required_keys[provider.name]?.length > 0;
|
const hasRequiredKeys =
|
||||||
|
(required_keys as Record<string, string[]>)[provider.name]?.length > 0;
|
||||||
return (
|
return (
|
||||||
<BaseProviderCard
|
<BaseProviderCard
|
||||||
key={provider.id}
|
key={provider.id}
|
||||||
|
|||||||
@@ -9,7 +9,15 @@ import { useModel } from '../models/ModelContext';
|
|||||||
import { Button } from '../../ui/button';
|
import { Button } from '../../ui/button';
|
||||||
import { toastError, toastSuccess } from '../../../toasts';
|
import { toastError, toastSuccess } from '../../../toasts';
|
||||||
|
|
||||||
function ConfirmationModal({ message, onConfirm, onCancel }) {
|
function ConfirmationModal({
|
||||||
|
message,
|
||||||
|
onConfirm,
|
||||||
|
onCancel,
|
||||||
|
}: {
|
||||||
|
message: string;
|
||||||
|
onConfirm: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black/20 backdrop-blur-sm z-[9999]">
|
<div className="fixed inset-0 bg-black/20 backdrop-blur-sm z-[9999]">
|
||||||
<div className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[400px] bg-white dark:bg-gray-800 rounded-xl shadow-xl border border-gray-200 dark:border-gray-700">
|
<div className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[400px] bg-white dark:bg-gray-800 rounded-xl shadow-xl border border-gray-200 dark:border-gray-700">
|
||||||
@@ -43,7 +51,12 @@ export function ConfigureProvidersGrid() {
|
|||||||
const [selectedForSetup, setSelectedForSetup] = useState<string | null>(null);
|
const [selectedForSetup, setSelectedForSetup] = useState<string | null>(null);
|
||||||
const [modalMode, setModalMode] = useState<'edit' | 'setup' | 'battle'>('setup');
|
const [modalMode, setModalMode] = useState<'edit' | 'setup' | 'battle'>('setup');
|
||||||
const [isConfirmationOpen, setIsConfirmationOpen] = useState(false);
|
const [isConfirmationOpen, setIsConfirmationOpen] = useState(false);
|
||||||
const [providerToDelete, setProviderToDelete] = useState<{ name: string; id: string; isConfigured: boolean; description: string } | null>(null);
|
const [providerToDelete, setProviderToDelete] = useState<{
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
isConfigured: boolean;
|
||||||
|
description: string;
|
||||||
|
} | null>(null);
|
||||||
const { currentModel } = useModel();
|
const { currentModel } = useModel();
|
||||||
|
|
||||||
const providers = useMemo(() => {
|
const providers = useMemo(() => {
|
||||||
@@ -62,13 +75,23 @@ export function ConfigureProvidersGrid() {
|
|||||||
});
|
});
|
||||||
}, [activeKeys]);
|
}, [activeKeys]);
|
||||||
|
|
||||||
const handleAddKeys = (provider) => {
|
const handleAddKeys = (provider: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isConfigured: boolean;
|
||||||
|
description: string;
|
||||||
|
}) => {
|
||||||
setSelectedForSetup(provider.id);
|
setSelectedForSetup(provider.id);
|
||||||
setModalMode('setup');
|
setModalMode('setup');
|
||||||
setShowSetupModal(true);
|
setShowSetupModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfigure = (provider) => {
|
const handleConfigure = (provider: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isConfigured: boolean;
|
||||||
|
description: string;
|
||||||
|
}) => {
|
||||||
setSelectedForSetup(provider.id);
|
setSelectedForSetup(provider.id);
|
||||||
setModalMode('edit');
|
setModalMode('edit');
|
||||||
setShowSetupModal(true);
|
setShowSetupModal(true);
|
||||||
@@ -80,7 +103,7 @@ export function ConfigureProvidersGrid() {
|
|||||||
const provider = providers.find((p) => p.id === selectedForSetup)?.name;
|
const provider = providers.find((p) => p.id === selectedForSetup)?.name;
|
||||||
if (!provider) return;
|
if (!provider) return;
|
||||||
|
|
||||||
const requiredKeys = required_keys[provider];
|
const requiredKeys = (required_keys as Record<string, string[]>)[provider];
|
||||||
if (!requiredKeys || requiredKeys.length === 0) {
|
if (!requiredKeys || requiredKeys.length === 0) {
|
||||||
console.error(`No keys found for provider ${provider}`);
|
console.error(`No keys found for provider ${provider}`);
|
||||||
return;
|
return;
|
||||||
@@ -157,12 +180,17 @@ export function ConfigureProvidersGrid() {
|
|||||||
toastError({
|
toastError({
|
||||||
title: provider,
|
title: provider,
|
||||||
msg: `Failed to ${providers.find((p) => p.id === selectedForSetup)?.isConfigured ? 'update' : 'add'} configuration`,
|
msg: `Failed to ${providers.find((p) => p.id === selectedForSetup)?.isConfigured ? 'update' : 'add'} configuration`,
|
||||||
traceback: error.message,
|
traceback: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (provider) => {
|
const handleDelete = async (provider: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isConfigured: boolean;
|
||||||
|
description: string;
|
||||||
|
}) => {
|
||||||
setProviderToDelete(provider);
|
setProviderToDelete(provider);
|
||||||
setIsConfirmationOpen(true);
|
setIsConfirmationOpen(true);
|
||||||
};
|
};
|
||||||
@@ -220,7 +248,7 @@ export function ConfigureProvidersGrid() {
|
|||||||
toastError({
|
toastError({
|
||||||
title: providerToDelete.name,
|
title: providerToDelete.name,
|
||||||
msg: 'Failed to delete configuration',
|
msg: 'Failed to delete configuration',
|
||||||
traceback: error.message,
|
traceback: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setIsConfirmationOpen(false);
|
setIsConfirmationOpen(false);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Input } from '../../ui/input';
|
|||||||
import { Check, Lock } from 'lucide-react';
|
import { Check, Lock } from 'lucide-react';
|
||||||
|
|
||||||
export default function SessionSharingSection() {
|
export default function SessionSharingSection() {
|
||||||
const envBaseUrlShare = window.appConfig.get('GOOSE_BASE_URL_SHARE');
|
const envBaseUrlShare = window.appConfig.get('GOOSE_BASE_URL_SHARE') as string | undefined;
|
||||||
console.log('envBaseUrlShare', envBaseUrlShare);
|
console.log('envBaseUrlShare', envBaseUrlShare);
|
||||||
|
|
||||||
// If env is set, force sharing enabled and set the baseUrl accordingly.
|
// If env is set, force sharing enabled and set the baseUrl accordingly.
|
||||||
@@ -146,7 +146,7 @@ export default function SessionSharingSection() {
|
|||||||
placeholder="https://example.com/api"
|
placeholder="https://example.com/api"
|
||||||
value={sessionSharingConfig.baseUrl}
|
value={sessionSharingConfig.baseUrl}
|
||||||
disabled={!!envBaseUrlShare}
|
disabled={!!envBaseUrlShare}
|
||||||
onChange={envBaseUrlShare ? undefined : handleBaseUrlChange}
|
onChange={envBaseUrlShare ? () => {} : handleBaseUrlChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{urlError && <p className="text-red-500 text-sm">{urlError}</p>}
|
{urlError && <p className="text-red-500 text-sm">{urlError}</p>}
|
||||||
|
|||||||
@@ -250,7 +250,10 @@ export default function ExtensionsSection({
|
|||||||
{deepLinkConfigStateVar && showEnvVarsStateVar && (
|
{deepLinkConfigStateVar && showEnvVarsStateVar && (
|
||||||
<ExtensionModal
|
<ExtensionModal
|
||||||
title="Add custom extension"
|
title="Add custom extension"
|
||||||
initialData={extensionToFormData({ ...deepLinkConfig, enabled: true })}
|
initialData={extensionToFormData({
|
||||||
|
...deepLinkConfig,
|
||||||
|
enabled: true,
|
||||||
|
} as FixedExtensionEntry)}
|
||||||
onClose={handleModalClose}
|
onClose={handleModalClose}
|
||||||
onSubmit={handleAddExtension}
|
onSubmit={handleAddExtension}
|
||||||
submitLabel="Add Extension"
|
submitLabel="Add Extension"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export async function extensionApiCall(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// for adding the payload is an extensionConfig, for removing payload is just the name
|
// for adding the payload is an extensionConfig, for removing payload is just the name
|
||||||
const extensionName = isActivating ? (payload as ExtensionConfig).name : payload as string;
|
const extensionName = isActivating ? (payload as ExtensionConfig).name : (payload as string);
|
||||||
let toastId;
|
let toastId;
|
||||||
|
|
||||||
// Step 1: Show loading toast (only for activation of stdio)
|
// Step 1: Show loading toast (only for activation of stdio)
|
||||||
@@ -77,11 +77,13 @@ export async function extensionApiCall(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Final catch-all error handler
|
// Final catch-all error handler
|
||||||
toastService.dismiss(toastId);
|
toastService.dismiss(toastId);
|
||||||
const msg = error.length < 70 ? error : `Failed to ${action.presentTense} extension`;
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
const msg =
|
||||||
|
errorMessage.length < 70 ? errorMessage : `Failed to ${action.presentTense} extension`;
|
||||||
toastService.error({
|
toastService.error({
|
||||||
title: extensionName,
|
title: extensionName,
|
||||||
msg: msg,
|
msg: msg,
|
||||||
traceback: error,
|
traceback: errorMessage,
|
||||||
});
|
});
|
||||||
console.error(`Error in extensionApiCall for ${extensionName}:`, error);
|
console.error(`Error in extensionApiCall for ${extensionName}:`, error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -95,7 +97,7 @@ function handleErrorResponse(
|
|||||||
response: Response,
|
response: Response,
|
||||||
extensionName: string,
|
extensionName: string,
|
||||||
action: { type: string; verb: string },
|
action: { type: string; verb: string },
|
||||||
toastId: string
|
toastId: string | number | undefined
|
||||||
): never {
|
): never {
|
||||||
const errorMsg = `Server returned ${response.status}: ${response.statusText}`;
|
const errorMsg = `Server returned ${response.status}: ${response.statusText}`;
|
||||||
console.error(errorMsg);
|
console.error(errorMsg);
|
||||||
@@ -150,7 +152,7 @@ export async function addToAgent(
|
|||||||
return await extensionApiCall('/extensions/add', extension, options);
|
return await extensionApiCall('/extensions/add', extension, options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Check if this is a 428 error and make the message more descriptive
|
// Check if this is a 428 error and make the message more descriptive
|
||||||
if (error.message && error.message.includes('428')) {
|
if (error instanceof Error && error.message && error.message.includes('428')) {
|
||||||
const enhancedError = new Error(
|
const enhancedError = new Error(
|
||||||
'Failed to add extension. Goose Agent was still starting up. Please try again.'
|
'Failed to add extension. Goose Agent was still starting up. Please try again.'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ export async function syncBundledExtensions(
|
|||||||
description: bundledExt.description,
|
description: bundledExt.description,
|
||||||
type: bundledExt.type,
|
type: bundledExt.type,
|
||||||
timeout: bundledExt.timeout,
|
timeout: bundledExt.timeout,
|
||||||
cmd: bundledExt.cmd,
|
cmd: bundledExt.cmd || '',
|
||||||
args: bundledExt.args,
|
args: bundledExt.args || [],
|
||||||
envs: bundledExt.envs,
|
envs: bundledExt.envs,
|
||||||
env_keys: bundledExt.env_keys,
|
env_keys: bundledExt.env_keys || [],
|
||||||
bundled: true,
|
bundled: true,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@@ -76,7 +76,7 @@ export async function syncBundledExtensions(
|
|||||||
description: bundledExt.description,
|
description: bundledExt.description,
|
||||||
type: bundledExt.type,
|
type: bundledExt.type,
|
||||||
timeout: bundledExt.timeout,
|
timeout: bundledExt.timeout,
|
||||||
uri: bundledExt.uri,
|
uri: bundledExt.uri || '',
|
||||||
bundled: true,
|
bundled: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ export async function addExtensionFromDeepLink(
|
|||||||
const remoteUrl = parsedUrl.searchParams.get('url');
|
const remoteUrl = parsedUrl.searchParams.get('url');
|
||||||
|
|
||||||
const config = remoteUrl
|
const config = remoteUrl
|
||||||
? getSseConfig(remoteUrl, name, description, timeout)
|
? getSseConfig(remoteUrl, name, description || '', timeout)
|
||||||
: getStdioConfig(cmd!, parsedUrl, name, description, timeout);
|
: getStdioConfig(cmd!, parsedUrl, name, description || '', timeout);
|
||||||
|
|
||||||
// Check if extension requires env vars and go to settings if so
|
// Check if extension requires env vars and go to settings if so
|
||||||
if (config.envs && Object.keys(config.envs).length > 0) {
|
if (config.envs && Object.keys(config.envs).length > 0) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ async function retryWithBackoff<T>(fn: () => Promise<T>, options: RetryOptions =
|
|||||||
const { retries = 3, delayMs = 1000, backoffFactor = 1.5, shouldRetry = () => true } = options;
|
const { retries = 3, delayMs = 1000, backoffFactor = 1.5, shouldRetry = () => true } = options;
|
||||||
|
|
||||||
let attempt = 0;
|
let attempt = 0;
|
||||||
let lastError: ExtensionError;
|
let lastError: ExtensionError = new Error('Unknown error');
|
||||||
|
|
||||||
while (attempt <= retries) {
|
while (attempt <= retries) {
|
||||||
try {
|
try {
|
||||||
@@ -100,7 +100,7 @@ export async function addToAgentOnStartup({
|
|||||||
retries: 3,
|
retries: 3,
|
||||||
delayMs: 1000,
|
delayMs: 1000,
|
||||||
shouldRetry: (error: ExtensionError) =>
|
shouldRetry: (error: ExtensionError) =>
|
||||||
error.message &&
|
!!error.message &&
|
||||||
(error.message.includes('428') ||
|
(error.message.includes('428') ||
|
||||||
error.message.includes('Precondition Required') ||
|
error.message.includes('Precondition Required') ||
|
||||||
error.message.includes('Agent is not initialized')),
|
error.message.includes('Agent is not initialized')),
|
||||||
@@ -110,7 +110,7 @@ export async function addToAgentOnStartup({
|
|||||||
toastService.error({
|
toastService.error({
|
||||||
title: extensionConfig.name,
|
title: extensionConfig.name,
|
||||||
msg: 'Extension failed to start and will be disabled.',
|
msg: 'Extension failed to start and will be disabled.',
|
||||||
traceback: finalError as Error,
|
traceback: finalError instanceof Error ? finalError.message : String(finalError),
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ export default function ExtensionInfoFields({
|
|||||||
<label className="text-sm font-medium mb-2 block text-textStandard">Type</label>
|
<label className="text-sm font-medium mb-2 block text-textStandard">Type</label>
|
||||||
<Select
|
<Select
|
||||||
value={{ value: type, label: type.toUpperCase() }}
|
value={{ value: type, label: type.toUpperCase() }}
|
||||||
onChange={(option: { value: string; label: string } | null) => {
|
onChange={(newValue: unknown) => {
|
||||||
|
const option = newValue as { value: string; label: string } | null;
|
||||||
if (option) {
|
if (option) {
|
||||||
onChange('type', option.value);
|
onChange('type', option.value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ export default function ExtensionModal({
|
|||||||
|
|
||||||
const isConfigValid = () => {
|
const isConfigValid = () => {
|
||||||
return (
|
return (
|
||||||
(formData.type === 'stdio' && formData.cmd && formData.cmd.trim() !== '') ||
|
(formData.type === 'stdio' && !!formData.cmd && formData.cmd.trim() !== '') ||
|
||||||
(formData.type === 'sse' && formData.endpoint && formData.endpoint.trim() !== '')
|
(formData.type === 'sse' && !!formData.endpoint && formData.endpoint.trim() !== '')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ export default function ExtensionModal({
|
|||||||
/>
|
/>
|
||||||
<div className="mb-4" />
|
<div className="mb-4" />
|
||||||
<ExtensionTimeoutField
|
<ExtensionTimeoutField
|
||||||
timeout={formData.timeout}
|
timeout={formData.timeout || 300}
|
||||||
onChange={(key, value) => setFormData({ ...formData, [key]: value })}
|
onChange={(key, value) => setFormData({ ...formData, [key]: value })}
|
||||||
submitAttempted={submitAttempted}
|
submitAttempted={submitAttempted}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ interface ExtensionListProps {
|
|||||||
onToggle: (extension: FixedExtensionEntry) => Promise<boolean | void> | void;
|
onToggle: (extension: FixedExtensionEntry) => Promise<boolean | void> | void;
|
||||||
onConfigure?: (extension: FixedExtensionEntry) => void;
|
onConfigure?: (extension: FixedExtensionEntry) => void;
|
||||||
isStatic?: boolean;
|
isStatic?: boolean;
|
||||||
|
disableConfiguration?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ExtensionList({
|
export default function ExtensionList({
|
||||||
@@ -16,6 +17,7 @@ export default function ExtensionList({
|
|||||||
onToggle,
|
onToggle,
|
||||||
onConfigure,
|
onConfigure,
|
||||||
isStatic,
|
isStatic,
|
||||||
|
disableConfiguration: _disableConfiguration,
|
||||||
}: ExtensionListProps) {
|
}: ExtensionListProps) {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-2 gap-2 mb-2">
|
<div className="grid grid-cols-2 gap-2 mb-2">
|
||||||
|
|||||||
@@ -76,14 +76,14 @@ export function extensionToFormData(extension: FixedExtensionEntry): ExtensionFo
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: extension.name,
|
name: extension.name || '',
|
||||||
description:
|
description:
|
||||||
extension.type === 'stdio' || extension.type === 'sse' ? extension.description : undefined,
|
extension.type === 'stdio' || extension.type === 'sse' ? extension.description || '' : '',
|
||||||
type: extension.type,
|
type: extension.type === 'frontend' ? 'stdio' : extension.type,
|
||||||
cmd: extension.type === 'stdio' ? combineCmdAndArgs(extension.cmd, extension.args) : undefined,
|
cmd: extension.type === 'stdio' ? combineCmdAndArgs(extension.cmd, extension.args) : undefined,
|
||||||
endpoint: extension.type === 'sse' ? extension.uri : undefined,
|
endpoint: extension.type === 'sse' ? extension.uri : undefined,
|
||||||
enabled: extension.enabled,
|
enabled: extension.enabled,
|
||||||
timeout: 'timeout' in extension ? extension.timeout : undefined,
|
timeout: 'timeout' in extension ? (extension.timeout ?? undefined) : undefined,
|
||||||
envVars,
|
envVars,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ export function createExtensionConfig(formData: ExtensionFormData): ExtensionCon
|
|||||||
|
|
||||||
if (formData.type === 'stdio') {
|
if (formData.type === 'stdio') {
|
||||||
// we put the cmd + args all in the form cmd field but need to split out into cmd + args
|
// we put the cmd + args all in the form cmd field but need to split out into cmd + args
|
||||||
const { cmd, args } = splitCmdAndArgs(formData.cmd);
|
const { cmd, args } = splitCmdAndArgs(formData.cmd || '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'stdio',
|
type: 'stdio',
|
||||||
@@ -111,7 +111,7 @@ export function createExtensionConfig(formData: ExtensionFormData): ExtensionCon
|
|||||||
name: formData.name,
|
name: formData.name,
|
||||||
description: formData.description,
|
description: formData.description,
|
||||||
timeout: formData.timeout,
|
timeout: formData.timeout,
|
||||||
uri: formData.endpoint,
|
uri: formData.endpoint || '',
|
||||||
...(env_keys.length > 0 ? { env_keys } : {}),
|
...(env_keys.length > 0 ? { env_keys } : {}),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function ConfigureApproveMode({
|
|||||||
|
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
try {
|
try {
|
||||||
handleModeChange(approveMode);
|
handleModeChange(approveMode || '');
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error configuring goose mode:', error);
|
console.error('Error configuring goose mode:', error);
|
||||||
@@ -68,8 +68,10 @@ export function ConfigureApproveMode({
|
|||||||
key={mode.key}
|
key={mode.key}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
showDescription={true}
|
showDescription={true}
|
||||||
currentMode={approveMode}
|
currentMode={approveMode || ''}
|
||||||
isApproveModeConfigure={true}
|
isApproveModeConfigure={true}
|
||||||
|
parentView={'settings' as const}
|
||||||
|
setView={() => {}} // No-op since we're in configure mode
|
||||||
handleModeChange={(newMode) => {
|
handleModeChange={(newMode) => {
|
||||||
setApproveMode(newMode);
|
setApproveMode(newMode);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { initializeAgent } from '../../../agent';
|
import { initializeAgent } from '../../../agent';
|
||||||
import { toastError, toastSuccess } from '../../../toasts';
|
import { toastError, toastSuccess } from '../../../toasts';
|
||||||
import { ProviderDetails } from '@/src/api';
|
import { ProviderDetails } from '../../../api';
|
||||||
import Model, { getProviderMetadata } from './modelInterface';
|
import Model, { getProviderMetadata } from './modelInterface';
|
||||||
import { ProviderMetadata } from '../../../api';
|
import { ProviderMetadata } from '../../../api';
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export async function changeModel({ model, writeToConfig }: changeModelProps) {
|
|||||||
toastError({
|
toastError({
|
||||||
title: CHANGE_MODEL_ERROR_TITLE,
|
title: CHANGE_MODEL_ERROR_TITLE,
|
||||||
msg: SWITCH_MODEL_AGENT_ERROR_MSG,
|
msg: SWITCH_MODEL_AGENT_ERROR_MSG,
|
||||||
traceback: error,
|
traceback: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
// don't write to config
|
// don't write to config
|
||||||
return;
|
return;
|
||||||
@@ -51,7 +51,7 @@ export async function changeModel({ model, writeToConfig }: changeModelProps) {
|
|||||||
toastError({
|
toastError({
|
||||||
title: CHANGE_MODEL_ERROR_TITLE,
|
title: CHANGE_MODEL_ERROR_TITLE,
|
||||||
msg: CONFIG_UPDATE_ERROR_MSG,
|
msg: CONFIG_UPDATE_ERROR_MSG,
|
||||||
traceback: error,
|
traceback: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
// agent and config will be out of sync at this point
|
// agent and config will be out of sync at this point
|
||||||
// TODO: reset agent to use current config settings
|
// TODO: reset agent to use current config settings
|
||||||
@@ -92,7 +92,7 @@ export async function getCurrentModelAndProvider({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getFallbackModelAndProvider(
|
export async function getFallbackModelAndProvider(
|
||||||
writeToConfig: (key: string, value: unknown, is_secret: boolean) => Promise<void>
|
writeToConfig?: (key: string, value: unknown, is_secret: boolean) => Promise<void>
|
||||||
) {
|
) {
|
||||||
const provider = window.appConfig.get('GOOSE_DEFAULT_PROVIDER');
|
const provider = window.appConfig.get('GOOSE_DEFAULT_PROVIDER');
|
||||||
const model = window.appConfig.get('GOOSE_DEFAULT_MODEL');
|
const model = window.appConfig.get('GOOSE_DEFAULT_MODEL');
|
||||||
@@ -125,7 +125,7 @@ export async function getCurrentModelAndProviderForDisplay({
|
|||||||
let metadata: ProviderMetadata;
|
let metadata: ProviderMetadata;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
metadata = await getProviderMetadata(gooseProvider, getProviders);
|
metadata = await getProviderMetadata(String(gooseProvider), getProviders);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { model: gooseModel, provider: gooseProvider };
|
return { model: gooseModel, provider: gooseProvider };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export function BaseModelsList({
|
|||||||
);
|
);
|
||||||
// no matches so just create a model object (maybe user updated config.yaml from CLI usage, manual editing etc)
|
// no matches so just create a model object (maybe user updated config.yaml from CLI usage, manual editing etc)
|
||||||
if (!match) {
|
if (!match) {
|
||||||
currentModel = { name: result.model, provider: result.provider };
|
currentModel = { name: String(result.model), provider: String(result.provider) };
|
||||||
} else {
|
} else {
|
||||||
currentModel = match;
|
currentModel = match;
|
||||||
}
|
}
|
||||||
@@ -109,9 +109,9 @@ export function BaseModelsList({
|
|||||||
writeToConfig: upsert,
|
writeToConfig: upsert,
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentModel = modelList.find(
|
const currentModel =
|
||||||
(m) => m.name === result.model && m.provider === result.provider
|
modelList.find((m) => m.name === result.model && m.provider === result.provider) ||
|
||||||
) || { name: result.model, provider: result.provider };
|
({ name: String(result.model), provider: String(result.provider) } as Model);
|
||||||
|
|
||||||
setSelectedModel(currentModel);
|
setSelectedModel(currentModel);
|
||||||
} catch (secondError) {
|
} catch (secondError) {
|
||||||
@@ -136,10 +136,11 @@ export function BaseModelsList({
|
|||||||
{modelList.map((model) =>
|
{modelList.map((model) =>
|
||||||
renderItem({
|
renderItem({
|
||||||
model,
|
model,
|
||||||
isSelected:
|
isSelected: !!(
|
||||||
selectedModel &&
|
selectedModel &&
|
||||||
selectedModel.name === model.name &&
|
selectedModel.name === model.name &&
|
||||||
selectedModel.provider === model.provider,
|
selectedModel.provider === model.provider
|
||||||
|
),
|
||||||
onSelect: () => handleRadioChange(model),
|
onSelect: () => handleRadioChange(model),
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -12,7 +12,17 @@ import type { View } from '../../../../App';
|
|||||||
import Model, { getProviderMetadata } from '../modelInterface';
|
import Model, { getProviderMetadata } from '../modelInterface';
|
||||||
import { useModel } from '../../../settings/models/ModelContext';
|
import { useModel } from '../../../settings/models/ModelContext';
|
||||||
|
|
||||||
const ModalButtons = ({ onSubmit, onCancel, _isValid: _, _validationErrors: __ }) => (
|
const ModalButtons = ({
|
||||||
|
onSubmit,
|
||||||
|
onCancel,
|
||||||
|
_isValid: _,
|
||||||
|
_validationErrors: __,
|
||||||
|
}: {
|
||||||
|
onSubmit: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
_isValid: boolean;
|
||||||
|
_validationErrors: { provider: string; model: string };
|
||||||
|
}) => (
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -41,7 +51,9 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
const { getProviders, upsert } = useConfig();
|
const { getProviders, upsert } = useConfig();
|
||||||
const { switchModel } = useModel();
|
const { switchModel } = useModel();
|
||||||
const [providerOptions, setProviderOptions] = useState<{ value: string; label: string }[]>([]);
|
const [providerOptions, setProviderOptions] = useState<{ value: string; label: string }[]>([]);
|
||||||
const [modelOptions, setModelOptions] = useState<{ options: { value: string; label: string; provider: string }[] }[]>([]);
|
const [modelOptions, setModelOptions] = useState<
|
||||||
|
{ options: { value: string; label: string; provider: string }[] }[]
|
||||||
|
>([]);
|
||||||
const [provider, setProvider] = useState<string | null>(null);
|
const [provider, setProvider] = useState<string | null>(null);
|
||||||
const [model, setModel] = useState<string>('');
|
const [model, setModel] = useState<string>('');
|
||||||
const [isCustomModel, setIsCustomModel] = useState(false);
|
const [isCustomModel, setIsCustomModel] = useState(false);
|
||||||
@@ -80,7 +92,7 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
const isFormValid = validateForm();
|
const isFormValid = validateForm();
|
||||||
|
|
||||||
if (isFormValid) {
|
if (isFormValid) {
|
||||||
const providerMetaData = await getProviderMetadata(provider, getProviders);
|
const providerMetaData = await getProviderMetadata(provider || '', getProviders);
|
||||||
const providerDisplayName = providerMetaData.display_name;
|
const providerDisplayName = providerMetaData.display_name;
|
||||||
|
|
||||||
const modelObj = { name: model, provider: provider, subtext: providerDisplayName } as Model;
|
const modelObj = { name: model, provider: provider, subtext: providerDisplayName } as Model;
|
||||||
@@ -122,7 +134,9 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// Format model options by provider
|
// Format model options by provider
|
||||||
const formattedModelOptions = [];
|
const formattedModelOptions: {
|
||||||
|
options: { value: string; label: string; provider: string }[];
|
||||||
|
}[] = [];
|
||||||
activeProviders.forEach(({ metadata, name }) => {
|
activeProviders.forEach(({ metadata, name }) => {
|
||||||
if (metadata.known_models && metadata.known_models.length > 0) {
|
if (metadata.known_models && metadata.known_models.length > 0) {
|
||||||
formattedModelOptions.push({
|
formattedModelOptions.push({
|
||||||
@@ -158,7 +172,8 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
// Handle model selection change
|
// Handle model selection change
|
||||||
const handleModelChange = (selectedOption) => {
|
const handleModelChange = (newValue: unknown) => {
|
||||||
|
const selectedOption = newValue as { value: string; label: string; provider: string } | null;
|
||||||
if (selectedOption?.value === 'custom') {
|
if (selectedOption?.value === 'custom') {
|
||||||
setIsCustomModel(true);
|
setIsCustomModel(true);
|
||||||
setModel('');
|
setModel('');
|
||||||
@@ -169,7 +184,8 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Store the original model options in state, initialized from modelOptions
|
// Store the original model options in state, initialized from modelOptions
|
||||||
const [originalModelOptions, setOriginalModelOptions] = useState<{ options: { value: string; label: string; provider: string }[] }[]>(modelOptions);
|
const [originalModelOptions, setOriginalModelOptions] =
|
||||||
|
useState<{ options: { value: string; label: string; provider: string }[] }[]>(modelOptions);
|
||||||
|
|
||||||
const handleInputChange = (inputValue: string) => {
|
const handleInputChange = (inputValue: string) => {
|
||||||
if (!provider) return;
|
if (!provider) return;
|
||||||
@@ -221,8 +237,8 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
<ModalButtons
|
<ModalButtons
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onCancel={onClose}
|
onCancel={onClose}
|
||||||
isValid={isValid}
|
_isValid={isValid}
|
||||||
validationErrors={validationErrors}
|
_validationErrors={validationErrors}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -252,7 +268,8 @@ export const AddModelModal = ({ onClose, setView }: AddModelModalProps) => {
|
|||||||
<Select
|
<Select
|
||||||
options={providerOptions}
|
options={providerOptions}
|
||||||
value={providerOptions.find((option) => option.value === provider) || null}
|
value={providerOptions.find((option) => option.value === provider) || null}
|
||||||
onChange={(option) => {
|
onChange={(newValue: unknown) => {
|
||||||
|
const option = newValue as { value: string; label: string } | null;
|
||||||
if (option?.value === 'configure_providers') {
|
if (option?.value === 'configure_providers') {
|
||||||
// Navigate to ConfigureProviders view
|
// Navigate to ConfigureProviders view
|
||||||
setView('ConfigureProviders');
|
setView('ConfigureProviders');
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default memo(function ProviderGrid({
|
|||||||
providers={providers}
|
providers={providers}
|
||||||
isOnboarding={isOnboarding}
|
isOnboarding={isOnboarding}
|
||||||
refreshProviders={refreshProviders}
|
refreshProviders={refreshProviders}
|
||||||
onProviderLaunch={onProviderLaunch}
|
onProviderLaunch={onProviderLaunch || (() => {})}
|
||||||
/>
|
/>
|
||||||
<ProviderConfigurationModal />
|
<ProviderConfigurationModal />
|
||||||
</ProviderModalProvider>
|
</ProviderModalProvider>
|
||||||
|
|||||||
@@ -13,19 +13,23 @@ import { useConfig } from '../../../ConfigContext';
|
|||||||
import { AlertTriangle } from 'lucide-react';
|
import { AlertTriangle } from 'lucide-react';
|
||||||
import { getCurrentModelAndProvider } from '../../models'; // Import the utility
|
import { getCurrentModelAndProvider } from '../../models'; // Import the utility
|
||||||
|
|
||||||
const customSubmitHandlerMap = {
|
interface FormValues {
|
||||||
|
[key: string]: string | number | boolean | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const customSubmitHandlerMap: Record<string, unknown> = {
|
||||||
provider_name: OllamaSubmitHandler, // example
|
provider_name: OllamaSubmitHandler, // example
|
||||||
};
|
};
|
||||||
|
|
||||||
const customFormsMap = {
|
const customFormsMap: Record<string, unknown> = {
|
||||||
provider_name: OllamaForm, // example
|
provider_name: OllamaForm, // example
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ProviderConfigurationModal() {
|
export default function ProviderConfigurationModal() {
|
||||||
const [validationErrors, setValidationErrors] = useState({});
|
const [validationErrors, setValidationErrors] = useState<Record<string, string>>({});
|
||||||
const { upsert, remove, read } = useConfig(); // Add read to the destructured values
|
const { upsert, remove, read } = useConfig(); // Add read to the destructured values
|
||||||
const { isOpen, currentProvider, modalProps, closeModal } = useProviderModal();
|
const { isOpen, currentProvider, modalProps, closeModal } = useProviderModal();
|
||||||
const [configValues, setConfigValues] = useState({});
|
const [configValues, setConfigValues] = useState<Record<string, string>>({});
|
||||||
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
|
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
|
||||||
const [isActiveProvider, setIsActiveProvider] = useState(false); // New state for tracking active provider
|
const [isActiveProvider, setIsActiveProvider] = useState(false); // New state for tracking active provider
|
||||||
|
|
||||||
@@ -53,10 +57,14 @@ export default function ProviderConfigurationModal() {
|
|||||||
: 'This will permanently delete the current provider configuration.'
|
: 'This will permanently delete the current provider configuration.'
|
||||||
: `Add your API key(s) for this provider to integrate into Goose`;
|
: `Add your API key(s) for this provider to integrate into Goose`;
|
||||||
|
|
||||||
const SubmitHandler = customSubmitHandlerMap[currentProvider.name] || DefaultSubmitHandler;
|
const SubmitHandler =
|
||||||
const FormComponent = customFormsMap[currentProvider.name] || DefaultProviderSetupForm;
|
(customSubmitHandlerMap[currentProvider.name] as typeof DefaultSubmitHandler) ||
|
||||||
|
DefaultSubmitHandler;
|
||||||
|
const FormComponent =
|
||||||
|
(customFormsMap[currentProvider.name] as typeof DefaultProviderSetupForm) ||
|
||||||
|
DefaultProviderSetupForm;
|
||||||
|
|
||||||
const handleSubmitForm = async (e) => {
|
const handleSubmitForm = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log('Form submitted for:', currentProvider.name);
|
console.log('Form submitted for:', currentProvider.name);
|
||||||
|
|
||||||
@@ -65,7 +73,7 @@ export default function ProviderConfigurationModal() {
|
|||||||
|
|
||||||
// Validation logic
|
// Validation logic
|
||||||
const parameters = currentProvider.metadata.config_keys || [];
|
const parameters = currentProvider.metadata.config_keys || [];
|
||||||
const errors = {};
|
const errors: Record<string, string> = {};
|
||||||
|
|
||||||
// Check required fields
|
// Check required fields
|
||||||
parameters.forEach((parameter) => {
|
parameters.forEach((parameter) => {
|
||||||
@@ -94,7 +102,7 @@ export default function ProviderConfigurationModal() {
|
|||||||
|
|
||||||
// Call onSubmit callback if provided (from modal props)
|
// Call onSubmit callback if provided (from modal props)
|
||||||
if (modalProps.onSubmit) {
|
if (modalProps.onSubmit) {
|
||||||
modalProps.onSubmit(configValues);
|
modalProps.onSubmit(configValues as FormValues);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save configuration:', error);
|
console.error('Failed to save configuration:', error);
|
||||||
@@ -156,7 +164,7 @@ export default function ProviderConfigurationModal() {
|
|||||||
// Call onDelete callback if provided
|
// Call onDelete callback if provided
|
||||||
// This should trigger the refreshProviders function
|
// This should trigger the refreshProviders function
|
||||||
if (modalProps.onDelete) {
|
if (modalProps.onDelete) {
|
||||||
modalProps.onDelete(currentProvider.name);
|
modalProps.onDelete(currentProvider.name as unknown as FormValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the delete confirmation state before closing
|
// Reset the delete confirmation state before closing
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import OpenRouterLogo from './icons/openrouter@3x.png';
|
|||||||
import DefaultLogo from './icons/default@3x.png';
|
import DefaultLogo from './icons/default@3x.png';
|
||||||
|
|
||||||
// Map provider names to their logos
|
// Map provider names to their logos
|
||||||
const providerLogos = {
|
const providerLogos: Record<string, string> = {
|
||||||
openai: OpenAILogo,
|
openai: OpenAILogo,
|
||||||
anthropic: AnthropicLogo,
|
anthropic: AnthropicLogo,
|
||||||
google: GoogleLogo,
|
google: GoogleLogo,
|
||||||
|
|||||||
@@ -1,29 +1,7 @@
|
|||||||
import React, { useEffect, useMemo, useState, useCallback } from 'react';
|
import React, { useEffect, useMemo, useState, useCallback } from 'react';
|
||||||
import { Input } from '../../../../../ui/input';
|
import { Input } from '../../../../../ui/input';
|
||||||
import { useConfig } from '../../../../../ConfigContext'; // Adjust this import path as needed
|
import { useConfig } from '../../../../../ConfigContext'; // Adjust this import path as needed
|
||||||
|
import { ProviderDetails, ConfigKey } from '../../../../../../api';
|
||||||
interface ConfigParameter {
|
|
||||||
name: string;
|
|
||||||
required: boolean;
|
|
||||||
secret?: boolean;
|
|
||||||
default?: string | number | boolean | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProviderMetadata {
|
|
||||||
config_keys?: ConfigParameter[];
|
|
||||||
display_name?: string;
|
|
||||||
description?: string;
|
|
||||||
known_models?: string[];
|
|
||||||
default_model?: string;
|
|
||||||
[key: string]: string | string[] | ConfigParameter[] | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Provider {
|
|
||||||
metadata: ProviderMetadata;
|
|
||||||
name: string;
|
|
||||||
is_configured: boolean;
|
|
||||||
[key: string]: string | boolean | ProviderMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ValidationErrors {
|
interface ValidationErrors {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
@@ -32,7 +10,7 @@ interface ValidationErrors {
|
|||||||
interface DefaultProviderSetupFormProps {
|
interface DefaultProviderSetupFormProps {
|
||||||
configValues: Record<string, string>;
|
configValues: Record<string, string>;
|
||||||
setConfigValues: React.Dispatch<React.SetStateAction<Record<string, string>>>;
|
setConfigValues: React.Dispatch<React.SetStateAction<Record<string, string>>>;
|
||||||
provider: Provider;
|
provider: ProviderDetails;
|
||||||
validationErrors: ValidationErrors;
|
validationErrors: ValidationErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +86,7 @@ export default function DefaultProviderSetupForm({
|
|||||||
}, [parameters]);
|
}, [parameters]);
|
||||||
|
|
||||||
// Helper function to generate appropriate placeholder text
|
// Helper function to generate appropriate placeholder text
|
||||||
const getPlaceholder = (parameter: ConfigParameter): string => {
|
const getPlaceholder = (parameter: ConfigKey): string => {
|
||||||
// If default is defined and not null, show it
|
// If default is defined and not null, show it
|
||||||
if (parameter.default !== undefined && parameter.default !== null) {
|
if (parameter.default !== undefined && parameter.default !== null) {
|
||||||
return `Default: ${parameter.default}`;
|
return `Default: ${parameter.default}`;
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
import { PROVIDER_REGISTRY } from '../../../ProviderRegistry';
|
import { PROVIDER_REGISTRY } from '../../../ProviderRegistry';
|
||||||
import { Input } from '../../../../../ui/input';
|
import { Input } from '../../../../../ui/input';
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { RefreshCw } from 'lucide-react';
|
import { RefreshCw } from 'lucide-react';
|
||||||
import CustomRadio from '../../../../../ui/CustomRadio';
|
import CustomRadio from '../../../../../ui/CustomRadio';
|
||||||
|
|
||||||
export default function OllamaForm({ configValues, setConfigValues, provider }) {
|
export default function OllamaForm({
|
||||||
|
configValues,
|
||||||
|
setConfigValues,
|
||||||
|
provider,
|
||||||
|
}: {
|
||||||
|
configValues: Record<string, string>;
|
||||||
|
setConfigValues: React.Dispatch<React.SetStateAction<Record<string, string>>>;
|
||||||
|
provider: { name: string; [key: string]: unknown };
|
||||||
|
}) {
|
||||||
const providerEntry = PROVIDER_REGISTRY.find((p) => p.name === provider.name);
|
const providerEntry = PROVIDER_REGISTRY.find((p) => p.name === provider.name);
|
||||||
const parameters = providerEntry?.details?.parameters || [];
|
const parameters = providerEntry?.details?.parameters || [];
|
||||||
const [isCheckingLocal, setIsCheckingLocal] = useState(false);
|
const [isCheckingLocal, setIsCheckingLocal] = useState(false);
|
||||||
const [isLocalAvailable, setIsLocalAvailable] = useState(false);
|
const [isLocalAvailable, setIsLocalAvailable] = useState(false);
|
||||||
|
|
||||||
const handleConnectionTypeChange = useCallback(
|
const handleConnectionTypeChange = useCallback(
|
||||||
(value) => {
|
(value: string) => {
|
||||||
setConfigValues((prev) => ({
|
setConfigValues((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
connection_type: value,
|
connection_type: value,
|
||||||
@@ -22,7 +30,7 @@ export default function OllamaForm({ configValues, setConfigValues, provider })
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Function to handle input changes and auto-select/deselect the host radio
|
// Function to handle input changes and auto-select/deselect the host radio
|
||||||
const handleInputChange = (paramName, value) => {
|
const handleInputChange = (paramName: string, value: string) => {
|
||||||
// Update the parameter value
|
// Update the parameter value
|
||||||
setConfigValues((prev) => ({
|
setConfigValues((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
|
|||||||
@@ -2,10 +2,24 @@
|
|||||||
* Standalone function to submit provider configuration
|
* Standalone function to submit provider configuration
|
||||||
* Useful for components that don't want to use the hook
|
* Useful for components that don't want to use the hook
|
||||||
*/
|
*/
|
||||||
export const DefaultSubmitHandler = async (upsertFn, provider, configValues) => {
|
export const DefaultSubmitHandler = async (
|
||||||
|
upsertFn: (key: string, value: unknown, isSecret: boolean) => Promise<void>,
|
||||||
|
provider: {
|
||||||
|
metadata: {
|
||||||
|
config_keys?: Array<{
|
||||||
|
name: string;
|
||||||
|
required?: boolean;
|
||||||
|
default?: unknown;
|
||||||
|
secret?: boolean;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
configValues: Record<string, unknown>
|
||||||
|
) => {
|
||||||
const parameters = provider.metadata.config_keys || [];
|
const parameters = provider.metadata.config_keys || [];
|
||||||
|
|
||||||
const upsertPromises = parameters.map((parameter) => {
|
const upsertPromises = parameters.map(
|
||||||
|
(parameter: { name: string; required?: boolean; default?: unknown; secret?: boolean }) => {
|
||||||
// Skip parameters that don't have a value and aren't required
|
// Skip parameters that don't have a value and aren't required
|
||||||
if (!configValues[parameter.name] && !parameter.required) {
|
if (!configValues[parameter.name] && !parameter.required) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@@ -13,7 +27,9 @@ export const DefaultSubmitHandler = async (upsertFn, provider, configValues) =>
|
|||||||
|
|
||||||
// For required parameters with no value, use the default if available
|
// For required parameters with no value, use the default if available
|
||||||
const value =
|
const value =
|
||||||
configValues[parameter.name] !== undefined ? configValues[parameter.name] : parameter.default;
|
configValues[parameter.name] !== undefined
|
||||||
|
? configValues[parameter.name]
|
||||||
|
: parameter.default;
|
||||||
|
|
||||||
// Skip if there's still no value
|
// Skip if there's still no value
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
@@ -28,7 +44,8 @@ export const DefaultSubmitHandler = async (upsertFn, provider, configValues) =>
|
|||||||
|
|
||||||
// Pass the is_secret flag from the parameter definition
|
// Pass the is_secret flag from the parameter definition
|
||||||
return upsertFn(configKey, value, isSecret);
|
return upsertFn(configKey, value, isSecret);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Wait for all upsert operations to complete
|
// Wait for all upsert operations to complete
|
||||||
return Promise.all(upsertPromises);
|
return Promise.all(upsertPromises);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export default function OllamaSubmitHandler(configValues) {
|
export default function OllamaSubmitHandler(configValues: Record<string, unknown>) {
|
||||||
// Log each field value individually for clarity
|
// Log each field value individually for clarity
|
||||||
console.log('Ollama field values:');
|
console.log('Ollama field values:');
|
||||||
Object.entries(configValues).forEach(([key, value]) => {
|
Object.entries(configValues).forEach(([key, value]) => {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default function CardContainer({
|
|||||||
}`}
|
}`}
|
||||||
onClick={!grayedOut ? onClick : undefined}
|
onClick={!grayedOut ? onClick : undefined}
|
||||||
style={{
|
style={{
|
||||||
cursor: !grayedOut && onClick ? 'pointer' : 'default',
|
cursor: !grayedOut ? 'pointer' : 'default',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!grayedOut && <GlowingRing />}
|
{!grayedOut && <GlowingRing />}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
// Functions for string / string-based element creation (e.g. tooltips for each provider, descriptions, etc)
|
// Functions for string / string-based element creation (e.g. tooltips for each provider, descriptions, etc)
|
||||||
export function OllamaNotConfiguredTooltipMessage() {
|
export function OllamaNotConfiguredTooltipMessage() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ export default function SessionSharingSection() {
|
|||||||
// If env is set, force sharing enabled and set the baseUrl accordingly.
|
// If env is set, force sharing enabled and set the baseUrl accordingly.
|
||||||
const [sessionSharingConfig, setSessionSharingConfig] = useState({
|
const [sessionSharingConfig, setSessionSharingConfig] = useState({
|
||||||
enabled: envBaseUrlShare ? true : false,
|
enabled: envBaseUrlShare ? true : false,
|
||||||
baseUrl: envBaseUrlShare || '',
|
baseUrl: typeof envBaseUrlShare === 'string' ? envBaseUrlShare : '',
|
||||||
});
|
});
|
||||||
const [urlError, setUrlError] = useState('');
|
const [urlError, setUrlError] = useState('');
|
||||||
// isUrlConfigured is true if the user has configured a baseUrl and it is valid.
|
// isUrlConfigured is true if the user has configured a baseUrl and it is valid.
|
||||||
const isUrlConfigured =
|
const isUrlConfigured =
|
||||||
!envBaseUrlShare && sessionSharingConfig.enabled && isValidUrl(sessionSharingConfig.baseUrl);
|
!envBaseUrlShare &&
|
||||||
|
sessionSharingConfig.enabled &&
|
||||||
|
isValidUrl(String(sessionSharingConfig.baseUrl));
|
||||||
|
|
||||||
// Only load saved config from localStorage if the env variable is not provided.
|
// Only load saved config from localStorage if the env variable is not provided.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -23,7 +25,7 @@ export default function SessionSharingSection() {
|
|||||||
// If env variable is set, save the forced configuration to localStorage
|
// If env variable is set, save the forced configuration to localStorage
|
||||||
const forcedConfig = {
|
const forcedConfig = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
baseUrl: envBaseUrlShare,
|
baseUrl: typeof envBaseUrlShare === 'string' ? envBaseUrlShare : '',
|
||||||
};
|
};
|
||||||
localStorage.setItem('session_sharing_config', JSON.stringify(forcedConfig));
|
localStorage.setItem('session_sharing_config', JSON.stringify(forcedConfig));
|
||||||
} else {
|
} else {
|
||||||
@@ -113,7 +115,7 @@ export default function SessionSharingSection() {
|
|||||||
) : (
|
) : (
|
||||||
<Switch
|
<Switch
|
||||||
checked={sessionSharingConfig.enabled}
|
checked={sessionSharingConfig.enabled}
|
||||||
disabled={envBaseUrlShare}
|
disabled={!!envBaseUrlShare}
|
||||||
onCheckedChange={toggleSharing}
|
onCheckedChange={toggleSharing}
|
||||||
variant="mono"
|
variant="mono"
|
||||||
/>
|
/>
|
||||||
@@ -139,7 +141,7 @@ export default function SessionSharingSection() {
|
|||||||
placeholder="https://example.com/api"
|
placeholder="https://example.com/api"
|
||||||
value={sessionSharingConfig.baseUrl}
|
value={sessionSharingConfig.baseUrl}
|
||||||
disabled={!!envBaseUrlShare}
|
disabled={!!envBaseUrlShare}
|
||||||
onChange={envBaseUrlShare ? undefined : handleBaseUrlChange}
|
{...(envBaseUrlShare ? {} : { onChange: handleBaseUrlChange })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{urlError && <p className="text-red-500 text-sm">{urlError}</p>}
|
{urlError && <p className="text-red-500 text-sm">{urlError}</p>}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Box({ size }: { size: number }) {
|
export default function Box({ size }: { size: number }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export function ConfirmationModal({
|
|||||||
<BaseModal
|
<BaseModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
title={title}
|
title={title}
|
||||||
onClose={onCancel}
|
|
||||||
actions={
|
actions={
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* CustomRadio - A reusable radio button component with dark mode support
|
* CustomRadio - A reusable radio button component with dark mode support
|
||||||
* @param {Object} props - Component props
|
* @param {Object} props - Component props
|
||||||
@@ -25,6 +24,17 @@ const CustomRadio = ({
|
|||||||
secondaryLabel = null,
|
secondaryLabel = null,
|
||||||
rightContent = null,
|
rightContent = null,
|
||||||
className = '',
|
className = '',
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
checked: boolean;
|
||||||
|
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
label?: React.ReactNode;
|
||||||
|
secondaryLabel?: React.ReactNode;
|
||||||
|
rightContent?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
|
|||||||
@@ -136,6 +136,9 @@ export function DeepLinkModal({ recipeConfig: initialRecipeConfig, onClose }: De
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Open the deep link with the current bot config
|
// Open the deep link with the current bot config
|
||||||
const currentConfig = {
|
const currentConfig = {
|
||||||
|
id: 'deeplink-recipe',
|
||||||
|
name: 'DeepLink Recipe',
|
||||||
|
description: 'Recipe from deep link',
|
||||||
...recipeConfig,
|
...recipeConfig,
|
||||||
instructions,
|
instructions,
|
||||||
activities,
|
activities,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
import ReactSelect from 'react-select';
|
import ReactSelect from 'react-select';
|
||||||
|
|
||||||
export const Select = (props) => {
|
export const Select = (props: React.ComponentProps<typeof ReactSelect>) => {
|
||||||
return (
|
return (
|
||||||
<ReactSelect
|
<ReactSelect
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function Send({ size }: { size: number }) {
|
export default function Send({ size }: { size: number }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
@@ -12,7 +11,7 @@ export default function Send({ size }: { size: number }) {
|
|||||||
<path
|
<path
|
||||||
d="M22 12.5L2 4.5L4 12.5L2 20.5L22 12.5ZM5.81 13.5H14.11L4.88 17.19L5.81 13.5ZM14.11 11.5H5.81L4.89 7.81L14.11 11.5Z"
|
d="M22 12.5L2 4.5L4 12.5L2 20.5L22 12.5ZM5.81 13.5H14.11L4.88 17.19L5.81 13.5ZM14.11 11.5H5.81L4.89 7.81L14.11 11.5Z"
|
||||||
fill="#7A7EFB"
|
fill="#7A7EFB"
|
||||||
dark:fill="#4A56E2"
|
className="dark:fill-[#4A56E2]"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
interface StopProps {
|
interface StopProps {
|
||||||
size?: number;
|
size?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function VertDots({ size }: { size: number }) {
|
export default function VertDots({ size }: { size: number }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default function X({ size }: { size: number }) {
|
export default function X({ size }: { size: number }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
@@ -13,7 +12,7 @@ export default function X({ size }: { size: number }) {
|
|||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
d="M5.97237 6.00001L3.82593 3.85356L4.53303 3.14645L6.67948 5.2929L8.82593 3.14645L9.53303 3.85356L7.38659 6.00001L9.53303 8.14645L8.82593 8.85356L6.67948 6.70711L4.53303 8.85356L3.82593 8.14645L5.97237 6.00001Z"
|
d="M5.97237 6.00001L3.82593 3.85356L4.53303 3.14645L6.67948 5.2929L8.82593 3.14645L9.53303 3.85356L7.38659 6.00001L9.53303 8.14645L8.82593 8.85356L6.67948 6.70711L4.53303 8.85356L3.82593 8.14645L5.97237 6.00001Z"
|
||||||
fill="black"
|
fill="black"
|
||||||
dark:fill="white"
|
className="dark:fill-white"
|
||||||
fillOpacity="0.6"
|
fillOpacity="0.6"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export const BotIcon = () => {
|
export const BotIcon = () => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
// Helper to construct API endpoints
|
// Helper to construct API endpoints
|
||||||
export const getApiUrl = (endpoint: string): string => {
|
export const getApiUrl = (endpoint: string): string => {
|
||||||
const baseUrl = window.appConfig.get('GOOSE_API_HOST') + ':' + window.appConfig.get('GOOSE_PORT');
|
const baseUrl =
|
||||||
|
String(window.appConfig.get('GOOSE_API_HOST') || '') +
|
||||||
|
':' +
|
||||||
|
String(window.appConfig.get('GOOSE_PORT') || '');
|
||||||
const cleanEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
const cleanEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
||||||
return `${baseUrl}${cleanEndpoint}`;
|
return `${baseUrl}${cleanEndpoint}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSecretKey = (): string => {
|
export const getSecretKey = (): string => {
|
||||||
return window.appConfig.get('secretKey');
|
return String(window.appConfig.get('secretKey') || '');
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user