Merge pull request #1429 from CREDO23/fix-desktop-redirects

[Fixes] Packaged desktop: connector redirect + linux launcher icon
This commit is contained in:
Rohan Verma 2026-05-23 15:51:59 -07:00 committed by GitHub
commit 69388fc710
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 78 additions and 9 deletions

View file

@ -33,7 +33,7 @@ Map outcomes to your `status`:
- Any other `"Error: …"``status=error` and relay the tool's message verbatim as `next_step`. - Any other `"Error: …"``status=error` and relay the tool's message verbatim as `next_step`.
- HITL rejection → `status=blocked` with `next_step="User declined this filesystem action. Do not retry."`. - HITL rejection → `status=blocked` with `next_step="User declined this filesystem action. Do not retry."`.
You construct the structured `evidence` fields from your own knowledge of what you called and what you observed — the tools do not return them. `chunk_ids` apply only to `<priority_documents>` hits; for local-file operations leave them `null`. Never report values you did not actually see. You construct the structured `evidence` fields from your own knowledge of what you called and what you observed — the tools do not return them. Never report values you did not actually see. (`chunk_ids` is always `null` in desktop mode — see "Chunk citations in your prose" below.)
## Chunk citations in your prose ## Chunk citations in your prose

View file

@ -5,6 +5,11 @@
# inside the desktop app. Set to your production frontend domain. # inside the desktop app. Set to your production frontend domain.
HOSTED_FRONTEND_URL=https://surfsense.net HOSTED_FRONTEND_URL=https://surfsense.net
# Runtime override for the above (read at app start, no rebuild required).
# Useful for self-hosters whose backend NEXT_FRONTEND_URL differs from the
# value baked into the official desktop builds. Leave empty to use HOSTED_FRONTEND_URL.
# SURFSENSE_HOSTED_FRONTEND_URL_OVERRIDE=
# PostHog analytics (leave empty to disable) # PostHog analytics (leave empty to disable)
POSTHOG_KEY= POSTHOG_KEY=
POSTHOG_HOST=https://assets.surfsense.com POSTHOG_HOST=https://assets.surfsense.com

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

View file

@ -55,6 +55,11 @@ mac:
NSAccessibilityUsageDescription: "SurfSense uses accessibility features to bring the app to the foreground and interact with the active application when you use desktop assists." NSAccessibilityUsageDescription: "SurfSense uses accessibility features to bring the app to the foreground and interact with the active application when you use desktop assists."
NSScreenCaptureUsageDescription: "SurfSense uses screen capture so you can attach a selected region to chat (Screenshot Assist) or capture the full screen from the composer." NSScreenCaptureUsageDescription: "SurfSense uses screen capture so you can attach a selected region to chat (Screenshot Assist) or capture the full screen from the composer."
NSAppleEventsUsageDescription: "SurfSense uses Apple Events to interact with the active application." NSAppleEventsUsageDescription: "SurfSense uses Apple Events to interact with the active application."
# `surfsense://` scheme — install-time registration for LaunchServices.
CFBundleURLTypes:
- CFBundleURLName: com.surfsense.desktop
CFBundleURLSchemes:
- surfsense
target: target:
- target: dmg - target: dmg
arch: [x64, arm64] arch: [x64, arm64]
@ -72,7 +77,7 @@ nsis:
createDesktopShortcut: true createDesktopShortcut: true
createStartMenuShortcut: true createStartMenuShortcut: true
linux: linux:
icon: assets/icon.png icon: assets/icons/
category: Utility category: Utility
artifactName: "${productName}-${version}-${arch}.${ext}" artifactName: "${productName}-${version}-${arch}.${ext}"
mimeTypes: mimeTypes:

View file

@ -60,6 +60,11 @@ export function setupDeepLinks(): boolean {
app.setAsDefaultProtocolClient(PROTOCOL); app.setAsDefaultProtocolClient(PROTOCOL);
} }
// Cold-start on Windows/Linux: protocol URL arrives via argv of the
// first instance, not via `second-instance` or `open-url`.
const cold = process.argv.find((arg) => arg.startsWith(`${PROTOCOL}://`));
if (cold) handleDeepLink(cold);
return true; return true;
} }

View file

@ -39,7 +39,8 @@ export async function startNextServer(): Promise<void> {
const serverScript = path.join(standalonePath, 'server.js'); const serverScript = path.join(standalonePath, 'server.js');
process.env.PORT = String(serverPort); process.env.PORT = String(serverPort);
process.env.HOSTNAME = '0.0.0.0'; // Loopback bind: 0.0.0.0 leaks into request.url and flips window origin via NextResponse.redirect.
process.env.HOSTNAME = 'localhost';
process.env.NODE_ENV = 'production'; process.env.NODE_ENV = 'production';
process.chdir(standalonePath); process.chdir(standalonePath);

View file

@ -6,9 +6,26 @@ import { getServerPort } from './server';
import { setActiveSearchSpaceId } from './active-search-space'; import { setActiveSearchSpaceId } from './active-search-space';
const isDev = !app.isPackaged; const isDev = !app.isPackaged;
const HOSTED_FRONTEND_URL = process.env.HOSTED_FRONTEND_URL as string;
const isMac = process.platform === 'darwin'; const isMac = process.platform === 'darwin';
function getHostedFrontendUrl(): string {
return (
process.env.SURFSENSE_HOSTED_FRONTEND_URL_OVERRIDE ||
process.env.HOSTED_FRONTEND_URL ||
'https://surfsense.net'
);
}
function getHostedFrontendHosts(): string[] {
try {
const host = new URL(getHostedFrontendUrl()).host;
const sibling = host.startsWith('www.') ? host.slice(4) : `www.${host}`;
return Array.from(new Set([host, sibling]));
} catch {
return [];
}
}
let mainWindow: BrowserWindow | null = null; let mainWindow: BrowserWindow | null = null;
let isQuitting = false; let isQuitting = false;
@ -58,11 +75,47 @@ export function createMainWindow(initialPath = '/dashboard'): BrowserWindow {
return { action: 'deny' }; return { action: 'deny' };
}); });
const filter = { urls: [`${HOSTED_FRONTEND_URL}/*`] }; const hostedHosts = getHostedFrontendHosts();
session.defaultSession.webRequest.onBeforeRequest(filter, (details, callback) => { const rewriteFilter = {
const rewritten = details.url.replace(HOSTED_FRONTEND_URL, `http://localhost:${getServerPort()}`); urls: hostedHosts.flatMap((h) => [`http://${h}/*`, `https://${h}/*`]),
callback({ redirectURL: rewritten }); };
if (rewriteFilter.urls.length > 0) {
session.defaultSession.webRequest.onBeforeRequest(rewriteFilter, (details, callback) => {
try {
const u = new URL(details.url);
const originalHost = u.host;
u.protocol = 'http:';
u.host = `localhost:${getServerPort()}`;
trackEvent('desktop_oauth_redirect_intercepted', {
host: originalHost,
path: u.pathname,
rewritten_to_port: getServerPort(),
}); });
callback({ redirectURL: u.toString() });
} catch {
callback({});
}
});
}
// Diagnostic: connector callback landing somewhere other than localhost
// means the rewrite missed and the user is stranded off-app.
session.defaultSession.webRequest.onCompleted(
{ urls: ['*://*/dashboard/*/connectors/callback*'] },
(details) => {
try {
const u = new URL(details.url);
if (u.hostname === 'localhost' || u.hostname === '127.0.0.1') return;
trackEvent('desktop_oauth_redirect_missed', {
host: u.host,
path: u.pathname,
status_code: details.statusCode,
});
} catch {
// ignore malformed URLs
}
}
);
mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDescription, validatedURL) => { mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDescription, validatedURL) => {
console.error(`Failed to load ${validatedURL}: ${errorDescription} (${errorCode})`); console.error(`Failed to load ${validatedURL}: ${errorDescription} (${errorCode})`);