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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ui/desktop/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"license": {
"name": "Apache-2.0"
},
"version": "1.0.14"
"version": "1.0.15"
},
"paths": {
"/config": {
Expand Down
195 changes: 149 additions & 46 deletions ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { ToastContainer } from 'react-toastify';
import { extractExtensionName } from './components/settings/extensions/utils';
import { GoosehintsModal } from './components/GoosehintsModal';
import { SessionDetails, fetchSessionDetails } from './sessions';

Check warning on line 14 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

'fetchSessionDetails' is defined but never used. Allowed unused vars must match /^_/u

import WelcomeView from './components/WelcomeView';
import ChatView from './components/ChatView';
Expand Down Expand Up @@ -49,7 +49,7 @@
| {
resumedSession?: SessionDetails;
}
| Record<string, any>;

Check warning on line 52 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
};

export default function App() {
Expand All @@ -73,77 +73,137 @@
return `${cmd} ${args.join(' ')}`.trim();
}

// this is all settings v2 stuff
useEffect(() => {
// Skip if feature flag is not enabled
if (!process.env.ALPHA) {
return;
}

console.log('Alpha flow initializing...');

const setupExtensions = async () => {
try {
console.log('Setting up extensions...');

// Set the ref immediately to prevent duplicate runs
initAttemptedRef.current = true;
console.log('Set initAttemptedRef to prevent duplicate runs');

// Force refresh extensions from the backend to ensure we have the latest
console.log('Getting extensions from backend...');
const refreshedExtensions = await getExtensions(true);
console.log(`Retrieved ${refreshedExtensions.length} extensions`);

if (refreshedExtensions.length === 0) {
// If we still have no extensions, this is truly a first-time setup
console.log('First-time setup: Adding all built-in extensions...');
await initializeBuiltInExtensions(addExtension);
console.log('Built-in extensions initialization complete');
} else {
// Extensions exist, check for any missing built-ins
console.log('Checking for missing built-in extensions...');
console.log(refreshedExtensions);
console.log('Current extensions:', refreshedExtensions);
await syncBuiltInExtensions(refreshedExtensions, addExtension);
console.log('Built-in extensions sync complete');
}
} catch (error) {
console.error('Error setting up extensions:', error);
console.error('Extension setup error details:', {
message: error.message,
stack: error.stack,
name: error.name,
});
// We don't set fatal error here since the app might still work without extensions
}
};

const initializeApp = async () => {
try {
console.log('Initializing alpha app...');

// Check if we have the required configuration
console.log('Reading GOOSE_PROVIDER from config...');
const provider = (await read('GOOSE_PROVIDER', false)) as string;
console.log('Provider from config:', provider);

console.log('Reading GOOSE_MODEL from config...');
const model = (await read('GOOSE_MODEL', false)) as string;
console.log('Model from config:', model);

if (provider && model) {
// We have all needed configuration, initialize the system
console.log('Initializing system with stored GOOSE_MODEL and GOOSE_PROVIDER');
console.log(`Initializing system with provider: ${provider}, model: ${model}`);
await initializeSystem(provider, model);
console.log('System initialization successful');
setView('chat');
} else {
// Missing configuration, show onboarding
console.log('Missing configuration, showing onboarding');
if (!provider) console.log('Missing provider');
if (!model) console.log('Missing model');
setView('welcome');
}
} catch (error) {
console.error('Error initializing app:', error);
console.error('App initialization error details:', {
message: error.message,
stack: error.stack,
name: error.name,
});
setFatalError(`Alpha initialization error: ${error.message || 'Unknown error'}`);
setView('welcome');
}
};

initializeApp().then();
setupExtensions().then();
// Execute with better promise handling
initializeApp()
.then(() => console.log('Alpha app initialization complete'))
.catch((error) => {
console.error('Unhandled error in initializeApp:', error);
setFatalError(`Unhandled alpha app error: ${error.message || 'Unknown error'}`);
});

setupExtensions()
.then(() => console.log('Extensions setup complete'))
.catch((error) => {
console.error('Unhandled error in setupExtensions:', error);
// Not setting fatal error here since extensions are optional
});
}, []); // Empty dependency array since we're using initAttemptedRef

Check warning on line 173 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

React Hook useEffect has missing dependencies: 'addExtension', 'getExtensions', and 'read'. Either include them or remove the dependency array

const setView = (view: View, viewOptions: Record<any, any> = {}) => {

Check warning on line 175 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type

Check warning on line 175 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
console.log(`Setting view to: ${view}`, viewOptions);
setInternalView({ view, viewOptions });
};

const [isGoosehintsModalOpen, setIsGoosehintsModalOpen] = useState(false);
const [isLoadingSession, setIsLoadingSession] = useState(false);
const { chat, setChat } = useChat({ setView, setIsLoadingSession });

useEffect(() => window.electron.reactReady(), []);
useEffect(() => {
console.log('Sending reactReady signal to Electron');
try {
window.electron.reactReady();
} catch (error) {
console.error('Error sending reactReady:', error);
setFatalError(`React ready notification failed: ${error.message}`);
}
}, []);

// Keyboard shortcut handler
useEffect(() => {
console.log('Setting up keyboard shortcuts');
const handleKeyDown = (event: KeyboardEvent) => {
if ((event.metaKey || event.ctrlKey) && event.key === 'n') {
event.preventDefault();
window.electron.createChatWindow(undefined, window.appConfig.get('GOOSE_WORKING_DIR'));
try {
const workingDir = window.appConfig.get('GOOSE_WORKING_DIR');
console.log(`Creating new chat window with working dir: ${workingDir}`);
window.electron.createChatWindow(undefined, workingDir);
} catch (error) {
console.error('Error creating new window:', error);
}
}
};

Expand All @@ -154,40 +214,58 @@
}, []);

useEffect(() => {
console.log('Setting up fatal error handler');
const handleFatalError = (_: any, errorMessage: string) => {

Check warning on line 218 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
console.error('Encountered a fatal error: ', errorMessage);
// Log additional context that might help diagnose the issue
console.error('Current view:', view);
console.error('Is loading session:', isLoadingSession);
setFatalError(errorMessage);
};

window.electron.on('fatal-error', handleFatalError);
return () => {
window.electron.off('fatal-error', handleFatalError);
};
}, []);
}, [view, isLoadingSession]); // Add dependencies to provide context in error logs

useEffect(() => {
const handleSetView = (_, view) => setView(view);
console.log('Setting up view change handler');
const handleSetView = (_, newView) => {
console.log(`Received view change request to: ${newView}`);
setView(newView);
};

window.electron.on('set-view', handleSetView);
return () => window.electron.off('set-view', handleSetView);
}, []);

// Add cleanup for session states when view changes
useEffect(() => {
console.log(`View changed to: ${view}`);
if (view !== 'chat') {
console.log('Not in chat view, clearing loading session state');
setIsLoadingSession(false);
}
}, [view]);

// TODO: modify
useEffect(() => {
console.log('Setting up extension handler');
const handleAddExtension = (_: any, link: string) => {

Check warning on line 255 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

Unexpected any. Specify a different type
const command = extractCommand(link);
const extName = extractExtensionName(link);
window.electron.logInfo(`Adding extension from deep link ${link}`);
setPendingLink(link);
setModalMessage(
`Are you sure you want to install the ${extName} extension?\n\nCommand: ${command}`
);
setModalVisible(true);
try {
console.log(`Received add-extension event with link: ${link}`);
const command = extractCommand(link);
const extName = extractExtensionName(link);
window.electron.logInfo(`Adding extension from deep link ${link}`);
setPendingLink(link);
setModalMessage(
`Are you sure you want to install the ${extName} extension?\n\nCommand: ${command}`
);
setModalVisible(true);
} catch (error) {
console.error('Error handling add-extension event:', error);
}
};

window.electron.on('add-extension', handleAddExtension);
Expand All @@ -199,11 +277,14 @@
// TODO: modify
const handleConfirm = async () => {
if (pendingLink && !isInstalling) {
console.log(`Confirming installation of extension from: ${pendingLink}`);
setIsInstalling(true);
try {
await addExtensionFromDeepLink(pendingLink, setView);
console.log('Extension installation successful');
} catch (error) {
console.error('Failed to add extension:', error);
// Consider showing a user-visible error notification here
} finally {
setModalVisible(false);
setPendingLink(null);
Expand All @@ -219,6 +300,7 @@
setPendingLink(null);
};

// TODO: remove
const { switchModel } = useModel(); // TODO: remove
const { addRecentModel } = useRecentModels(); // TODO: remove

Expand All @@ -227,51 +309,72 @@
return;
}

// TODO: remove
console.log('Non-alpha flow initializing...');

// Attempt to detect config for a stored provider
const detectStoredProvider = () => {
const config = window.electron.getConfig();
const storedProvider = getStoredProvider(config);
if (storedProvider) {
setView('chat');
} else {
setView('welcome');
try {
const config = window.electron.getConfig();
console.log('Loaded config:', JSON.stringify(config));

const storedProvider = getStoredProvider(config);
console.log('Stored provider:', storedProvider);

if (storedProvider) {
setView('chat');
} else {
setView('welcome');
}
} catch (err) {
console.error('DETECTION ERROR:', err);
setFatalError(`Config detection error: ${err.message || 'Unknown error'}`);
}
};

// TODO: remove
// Initialize system if we have a stored provider
const setupStoredProvider = async () => {
const config = window.electron.getConfig();
try {
const config = window.electron.getConfig();

if (config.GOOSE_PROVIDER && config.GOOSE_MODEL) {
window.electron.logInfo(
'Initializing system with environment: GOOSE_MODEL and GOOSE_PROVIDER as priority.'
);
await initializeSystem(config.GOOSE_PROVIDER, config.GOOSE_MODEL);
return;
}
const storedProvider = getStoredProvider(config);
const storedModel = getStoredModel();
if (storedProvider) {
try {
await initializeSystem(storedProvider, storedModel);
if (config.GOOSE_PROVIDER && config.GOOSE_MODEL) {
console.log('using GOOSE_PROVIDER and GOOSE_MODEL from config');
await initializeSystem(config.GOOSE_PROVIDER, config.GOOSE_MODEL);
return;
}

if (!storedModel) {
const modelName = getDefaultModel(storedProvider.toLowerCase());
const model = createSelectedModel(storedProvider.toLowerCase(), modelName);
switchModel(model);
addRecentModel(model);
const storedProvider = getStoredProvider(config);
const storedModel = getStoredModel();

if (storedProvider) {
try {
await initializeSystem(storedProvider, storedModel);
console.log('Setup using locally stored provider:', storedProvider);
console.log('Setup using locally stored model:', storedModel);

if (!storedModel) {
const modelName = getDefaultModel(storedProvider.toLowerCase());
const model = createSelectedModel(storedProvider.toLowerCase(), modelName);
switchModel(model);
addRecentModel(model);
}
} catch (error) {
console.error('Failed to initialize with stored provider:', error);
setFatalError(`Initialization failed: ${error.message || 'Unknown error'}`);
}
} catch (error) {
// TODO: add sessionError state and show error screen with option to start fresh
console.error('Failed to initialize with stored provider:', error);
}
} catch (err) {
console.error('SETUP ERROR:', err);
setFatalError(`Setup error: ${err.message || 'Unknown error'}`);
}
};

// Execute the functions with better error handling
detectStoredProvider();
setupStoredProvider();
setupStoredProvider().catch((err) => {
console.error('ASYNC SETUP ERROR:', err);
setFatalError(`Async setup error: ${err.message || 'Unknown error'}`);
});
}, []);

Check warning on line 377 in ui/desktop/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint Electron Desktop App

React Hook useEffect has missing dependencies: 'addRecentModel' and 'switchModel'. Either include them or remove the dependency array

// keep
if (fatalError) {
Expand Down
Loading