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
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Map outcomes to your `status`:
- 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."`.

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

Expand Down
5 changes: 5 additions & 0 deletions surfsense_desktop/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
# inside the desktop app. Set to your production frontend domain.
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_KEY=
POSTHOG_HOST=https://assets.surfsense.com
Binary file added surfsense_desktop/assets/icons/1024x1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added surfsense_desktop/assets/icons/128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added surfsense_desktop/assets/icons/16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added surfsense_desktop/assets/icons/256x256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added surfsense_desktop/assets/icons/32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added surfsense_desktop/assets/icons/48x48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added surfsense_desktop/assets/icons/512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added surfsense_desktop/assets/icons/64x64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion surfsense_desktop/electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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."
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."
# `surfsense://` scheme — install-time registration for LaunchServices.
CFBundleURLTypes:
- CFBundleURLName: com.surfsense.desktop
CFBundleURLSchemes:
- surfsense
target:
- target: dmg
arch: [x64, arm64]
Expand All @@ -72,7 +77,7 @@ nsis:
createDesktopShortcut: true
createStartMenuShortcut: true
linux:
icon: assets/icon.png
icon: assets/icons/
category: Utility
artifactName: "${productName}-${version}-${arch}.${ext}"
mimeTypes:
Expand Down
5 changes: 5 additions & 0 deletions surfsense_desktop/src/modules/deep-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export function setupDeepLinks(): boolean {
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;
}

Expand Down
3 changes: 2 additions & 1 deletion surfsense_desktop/src/modules/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export async function startNextServer(): Promise<void> {
const serverScript = path.join(standalonePath, 'server.js');

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.chdir(standalonePath);

Expand Down
65 changes: 59 additions & 6 deletions surfsense_desktop/src/modules/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,26 @@ import { getServerPort } from './server';
import { setActiveSearchSpaceId } from './active-search-space';

const isDev = !app.isPackaged;
const HOSTED_FRONTEND_URL = process.env.HOSTED_FRONTEND_URL as string;
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 isQuitting = false;

Expand Down Expand Up @@ -58,11 +75,47 @@ export function createMainWindow(initialPath = '/dashboard'): BrowserWindow {
return { action: 'deny' };
});

const filter = { urls: [`${HOSTED_FRONTEND_URL}/*`] };
session.defaultSession.webRequest.onBeforeRequest(filter, (details, callback) => {
const rewritten = details.url.replace(HOSTED_FRONTEND_URL, `http://localhost:${getServerPort()}`);
callback({ redirectURL: rewritten });
});
const hostedHosts = getHostedFrontendHosts();
const rewriteFilter = {
urls: hostedHosts.flatMap((h) => [`http://${h}/*`, `https://${h}/*`]),
};
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) => {
console.error(`Failed to load ${validatedURL}: ${errorDescription} (${errorCode})`);
Expand Down
Loading