@@ -5,9 +5,19 @@ import { fileURLToPath } from 'url';
55const __dirname = dirname ( fileURLToPath ( import . meta. url ) ) ;
66const ROOT = resolve ( __dirname , '..' ) ;
77
8+ // When packaged as a single .exe (pkg), __dirname/ROOT point INSIDE the
9+ // read-only snapshot (C:\snapshot\...), so .env can't be read from there and
10+ // DATA_DIR would try to mkdir in the snapshot (fails). The real writable
11+ // location is the folder the .exe sits in. Use that as the base for .env and
12+ // the default data dir so a double-clicked exe keeps its state in a `data/`
13+ // folder next to itself — cleanly isolated from the bundled Linux snapshot.
14+ const IS_PACKAGED = ! ! process . pkg ;
15+ const EXE_DIR = IS_PACKAGED ? dirname ( process . execPath ) : ROOT ;
16+
817// Load .env file manually (zero dependencies)
918function loadEnv ( ) {
10- const envPath = resolve ( ROOT , '.env' ) ;
19+ // Packaged: read the .env sitting next to the .exe, not the snapshot copy.
20+ const envPath = resolve ( EXE_DIR , '.env' ) ;
1121 if ( ! existsSync ( envPath ) ) return ;
1222 const content = readFileSync ( envPath , 'utf-8' ) ;
1323 for ( const line of content . split ( '\n' ) ) {
@@ -34,14 +44,32 @@ if (process.env.WINDSURFAPI_SKIP_DOTENV !== '1') {
3444 loadEnv ( ) ;
3545}
3646
47+ // Packaged-exe sane defaults (only when the user hasn't set them). The Windows
48+ // exe has no bundled Language Server (Linux-only) and is meant for local
49+ // single-user use, so: DEVIN_CONNECT=1 (pure-HTTP path, skip the LS entirely),
50+ // HOST=127.0.0.1 (loopback — don't expose an unauthenticated gateway to the
51+ // LAN on first run). Both stay overridable via .env / env.
52+ if ( IS_PACKAGED ) {
53+ if ( ! process . env . DEVIN_CONNECT && ! process . env . DEVIN_ONLY ) process . env . DEVIN_CONNECT = '1' ;
54+ if ( ! process . env . HOST && ! process . env . BIND_HOST ) process . env . HOST = '127.0.0.1' ;
55+ }
56+
3757// `sharedDataDir` is the cluster-shared root: a single accounts.json lives
3858// here so add-account writes from any replica are visible to every replica
3959// after restart. `dataDir` is replica-local under REPLICA_ISOLATE=1 and is
4060// safe to use for telemetry that does not need cross-replica visibility.
4161// See issue #67 — when the two were collapsed into one path, every
4262// docker-compose upgrade orphaned the user's accounts.json under a stale
4363// `replica-${HOSTNAME}` subdir.
44- const sharedDataDir = process . env . DATA_DIR ? resolve ( ROOT , process . env . DATA_DIR ) : ROOT ;
64+ // Base that relative DATA_DIR / default data dir resolve against. Packaged:
65+ // the .exe's folder (writable) — NEVER the snapshot ROOT. Default (DATA_DIR
66+ // unset) for a packaged exe is `<exe-dir>/Windsurf_data` so a double-click
67+ // drops all state (accounts/stats/logs) into one tidy folder beside the
68+ // program, isolated from the bundled Linux snapshot.
69+ const DATA_BASE = IS_PACKAGED ? EXE_DIR : ROOT ;
70+ const sharedDataDir = process . env . DATA_DIR
71+ ? resolve ( DATA_BASE , process . env . DATA_DIR )
72+ : ( IS_PACKAGED ? join ( EXE_DIR , 'Windsurf_data' ) : ROOT ) ;
4573const dataDir = ( ( ) => {
4674 let base = sharedDataDir ;
4775 if ( process . env . REPLICA_ISOLATE === '1' && process . env . HOSTNAME ) {
@@ -50,6 +78,11 @@ const dataDir = (() => {
5078 return base ;
5179} ) ( ) ;
5280
81+ // First-run detection (packaged only): capture whether the data folder exists
82+ // BEFORE we mkdir it, so index.js can decide to auto-open the dashboard only on
83+ // the very first launch (no Windsurf_data folder yet = fresh deploy).
84+ const isFirstRun = IS_PACKAGED && ! existsSync ( sharedDataDir ) ;
85+
5386try {
5487 mkdirSync ( sharedDataDir , { recursive : true } ) ;
5588 mkdirSync ( dataDir , { recursive : true } ) ;
@@ -106,6 +139,13 @@ export const config = {
106139
107140 // Proxy testing
108141 allowPrivateProxyHosts : process . env . ALLOW_PRIVATE_PROXY_HOSTS === '1' ,
142+
143+ // True when running as a packaged single-exe (pkg). Used to enable the
144+ // double-click desktop UX: data folder beside the exe, auto-open dashboard.
145+ isPackaged : IS_PACKAGED ,
146+ // True on the very first packaged launch (Windsurf_data didn't exist yet).
147+ // index.js opens the dashboard in the browser only on this first deploy.
148+ isFirstRun,
109149} ;
110150
111151const levels = { debug : 0 , info : 1 , warn : 2 , error : 3 } ;
0 commit comments