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/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' http://127.0.0.1:* https://api.github.com https://github.com https://objects.githubusercontent.com; object-src 'none'; frame-src 'none'; font-src 'self' data: https:; media-src 'self' mediastream:; form-action 'none'; base-uri 'self'; manifest-src 'self'; worker-src 'self';" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' http://127.0.0.1:* https:; object-src 'none'; frame-src 'none'; font-src 'self' data: https:; media-src 'self' mediastream:; form-action 'none'; base-uri 'self'; manifest-src 'self'; worker-src 'self';" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what? were did github come from?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! It's in case our app needs to reach out to github but I guess its not needed now so will remove 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh thats right we use it in the updater code

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just matching what we already do for content security policy for server responses

'Content-Security-Policy':

<title>Goose</title>
<script>
// Initialize theme before any content loads
Expand Down
124 changes: 108 additions & 16 deletions ui/desktop/src/components/settings/sessions/SessionSharingSection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { Input } from '../../ui/input';
import { Check, Lock } from 'lucide-react';
import { Check, Lock, Loader2, AlertCircle } from 'lucide-react';
import { Switch } from '../../ui/switch';
import { Button } from '../../ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../ui/card';
Expand All @@ -15,6 +15,11 @@ export default function SessionSharingSection() {
baseUrl: typeof envBaseUrlShare === 'string' ? envBaseUrlShare : '',
});
const [urlError, setUrlError] = useState('');
const [testResult, setTestResult] = useState<{
status: 'success' | 'error' | 'testing' | null;
message: string;
}>({ status: null, message: '' });

// isUrlConfigured is true if the user has configured a baseUrl and it is valid.
const isUrlConfigured =
!envBaseUrlShare &&
Expand Down Expand Up @@ -74,6 +79,9 @@ export default function SessionSharingSection() {
baseUrl: newBaseUrl,
}));

// Clear previous test results when URL changes
setTestResult({ status: null, message: '' });

if (isValidUrl(newBaseUrl)) {
setUrlError('');
const updated = { ...sessionSharingConfig, baseUrl: newBaseUrl };
Expand All @@ -83,13 +91,72 @@ export default function SessionSharingSection() {
}
};

// Test connection to the configured URL
const testConnection = async () => {
const baseUrl = sessionSharingConfig.baseUrl;
if (!baseUrl) return;

setTestResult({ status: 'testing', message: 'Testing connection...' });

try {
// Create an AbortController for timeout
const controller = new AbortController();
const timeoutId = window.setTimeout(() => controller.abort(), 10000); // 10 second timeout

const response = await fetch(baseUrl, {
method: 'GET',
headers: {
Accept: 'application/json, text/plain, */*',
},
signal: controller.signal,
});

window.clearTimeout(timeoutId);

// Consider any response (even 404) as a successful connection
// since it means we can reach the server
if (response.status < 500) {
setTestResult({
status: 'success',
message: `Connection successful! Server responded with status ${response.status}.`,
});
} else {
setTestResult({
status: 'error',
message: `Server error: HTTP ${response.status}. The server may not be configured correctly.`,
});
}
} catch (error) {
console.error('Connection test failed:', error);
let errorMessage = 'Connection failed. ';

if (error instanceof TypeError && error.message.includes('fetch')) {
errorMessage +=
'Unable to reach the server. Please check the URL and your network connection.';
} else if (error instanceof Error) {
if (error.name === 'AbortError') {
errorMessage += 'Connection timed out. The server may be slow or unreachable.';
} else {
errorMessage += error.message;
}
} else {
errorMessage += 'Unknown error occurred.';
}

setTestResult({
status: 'error',
message: errorMessage,
});
}
};

return (
<section id="session-sharing" className="space-y-4 pr-4 mt-1">
<Card className="pb-2">
<CardHeader className="pb-0">
<CardTitle>Session Sharing</CardTitle>
<CardDescription>
{envBaseUrlShare
{(envBaseUrlShare as string)
? 'Session sharing is configured but fully opt-in — your sessions are only shared when you explicitly click the share button.'
: 'You can enable session sharing to share your sessions with others.'}
</CardDescription>
Expand All @@ -99,7 +166,7 @@ export default function SessionSharingSection() {
{/* Toggle for enabling session sharing */}
<div className="flex items-center gap-3">
<label className="text-sm cursor-pointer">
{envBaseUrlShare
{(envBaseUrlShare as string)
? 'Session sharing has already been configured'
: 'Enable session sharing'}
</label>
Expand Down Expand Up @@ -136,19 +203,44 @@ export default function SessionSharingSection() {
/>
</div>
{urlError && <p className="text-red-500 text-sm">{urlError}</p>}
{isUrlConfigured && (
<Button
variant="outline"
size="sm"
className="mt-2"
onClick={() => {
// Test the connection to the configured URL
console.log('Testing connection to:', sessionSharingConfig.baseUrl);
// TODO: Implement actual connection test
}}
>
Test Connection
</Button>

{(isUrlConfigured || (envBaseUrlShare as string)) && (
<div className="space-y-2">
<Button
variant="outline"
size="sm"
onClick={testConnection}
disabled={testResult.status === 'testing'}
className="flex items-center gap-2"
>
{testResult.status === 'testing' ? (
<>
<Loader2 className="w-4 h-4 animate-spin" />
Testing...
</>
) : (
'Test Connection'
)}
</Button>

{/* Test Results */}
{testResult.status && testResult.status !== 'testing' && (
<div
className={`flex items-start gap-2 p-3 rounded-md text-sm ${
testResult.status === 'success'
? 'bg-green-50 text-green-800 border border-green-200'
: 'bg-red-50 text-red-800 border border-red-200'
}`}
>
{testResult.status === 'success' ? (
<Check className="w-4 h-4 mt-0.5 flex-shrink-0" />
) : (
<AlertCircle className="w-4 h-4 mt-0.5 flex-shrink-0" />
)}
<span>{testResult.message}</span>
</div>
)}
</div>
)}
</div>
)}
Expand Down
36 changes: 35 additions & 1 deletion ui/desktop/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -874,9 +874,43 @@ const openDirectoryDialog = async (

addRecentDir(dirToAdd);
const currentWindow = BrowserWindow.getFocusedWindow();
await createChat(app, undefined, dirToAdd);

if (replaceWindow && currentWindow) {
// Replace current window with new one
await createChat(app, undefined, dirToAdd);
currentWindow.close();
} else {
// Update the working directory in the current window's localStorage
if (currentWindow) {
try {
const updateConfigScript = `
try {
const currentConfig = JSON.parse(localStorage.getItem('gooseConfig') || '{}');
const updatedConfig = {
...currentConfig,
GOOSE_WORKING_DIR: '${dirToAdd.replace(/'/g, "\\'")}',
};
localStorage.setItem('gooseConfig', JSON.stringify(updatedConfig));

// Trigger a config update event so the UI can refresh
window.dispatchEvent(new CustomEvent('goose-config-updated', {
detail: { GOOSE_WORKING_DIR: '${dirToAdd.replace(/'/g, "\\'")}' }
}));
} catch (e) {
console.error('Failed to update working directory in localStorage:', e);
}
`;
await currentWindow.webContents.executeJavaScript(updateConfigScript);
console.log(`Updated working directory to: ${dirToAdd}`);
} catch (error) {
console.error('Failed to update working directory:', error);
// Fallback: create new window
await createChat(app, undefined, dirToAdd);
}
} else {
// No current window, create new one
await createChat(app, undefined, dirToAdd);
}
}
}
return result;
Expand Down
Loading