Skip to content

Commit aa904af

Browse files
authored
fix: Add isDevBuild() util and namespace mock node dir per instance (#1177)
1. Add isDevBuild() utility to env.ts to separate dev/prod feature gates from ASAR packaging checks 2. Replace !app.isPackaged dev/prod checks across with isDevBuild() 3. Namespace the mock node symlink dir per instance (/tmp/agent-node-dev vs /tmp/agent-node-prod) to avoid cross-contamination between simultaneous dev and prod builds 4. Leave app.isPackaged in place for the 4 sites that genuinely need ASAR .unpacked path resolution
1 parent 6c18861 commit aa904af

File tree

7 files changed

+36
-15
lines changed

7 files changed

+36
-15
lines changed

apps/code/src/main/menu.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { MAIN_TOKENS } from "./di/tokens.js";
1313
import type { AgentService } from "./services/agent/service.js";
1414
import type { UIService } from "./services/ui/service.js";
1515
import type { UpdatesService } from "./services/updates/service.js";
16+
import { isDevBuild } from "./utils/env.js";
1617
import { getLogFilePath } from "./utils/logger.js";
1718

1819
function getSystemInfo(): string {
@@ -68,7 +69,7 @@ function buildAppMenu(): MenuItemConstructorOptions {
6869
},
6970
},
7071
{ type: "separator" },
71-
...(app.isPackaged
72+
...(!isDevBuild()
7273
? [
7374
{
7475
label: "Check for Updates...",

apps/code/src/main/services/agent/service.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import type { AcpMessage } from "@shared/types/session-events.js";
2626
import { app } from "electron";
2727
import { inject, injectable, preDestroy } from "inversify";
2828
import { MAIN_TOKENS } from "../../di/tokens.js";
29+
import { isDevBuild } from "../../utils/env.js";
2930
import { logger } from "../../utils/logger.js";
3031
import { TypedEventEmitter } from "../../utils/typed-event-emitter.js";
3132
import type { FsService } from "../fs/service.js";
@@ -47,9 +48,14 @@ export type { InterruptReason };
4748

4849
const log = logger.scope("agent-service");
4950

50-
const SHARED_MOCK_NODE_DIR = join(tmpdir(), "agent-node-shared");
51+
const MOCK_NODE_DIR_PREFIX = "agent-node";
5152

52-
/** Mark all content blocks as hidden so the renderer doesn't show a duplicate user message on retry. */
53+
function getMockNodeDir(): string {
54+
const suffix = isDevBuild() ? "dev" : "prod";
55+
return join(tmpdir(), `${MOCK_NODE_DIR_PREFIX}-${suffix}`);
56+
}
57+
58+
/** Mark all content blocks as hidden so the renderer doesn't show a duplicate user message on retry */
5359
function hidePromptBlocks(prompt: ContentBlock[]): ContentBlock[] {
5460
return prompt.map((block) => {
5561
const existing = (
@@ -607,7 +613,7 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
607613
},
608614
skipLogPersistence: isPreview,
609615
localCachePath: join(app.getPath("home"), ".posthog-code"),
610-
debug: !app.isPackaged,
616+
debug: isDevBuild(),
611617
onLog: onAgentLog,
612618
});
613619

@@ -1146,10 +1152,11 @@ For git operations while detached:
11461152
}
11471153

11481154
private setupMockNodeEnvironment(): string {
1155+
const mockNodeDir = getMockNodeDir();
11491156
if (!this.mockNodeReady) {
11501157
try {
1151-
mkdirSync(SHARED_MOCK_NODE_DIR, { recursive: true });
1152-
const nodeSymlinkPath = join(SHARED_MOCK_NODE_DIR, "node");
1158+
mkdirSync(mockNodeDir, { recursive: true });
1159+
const nodeSymlinkPath = join(mockNodeDir, "node");
11531160
try {
11541161
symlinkSync(process.execPath, nodeSymlinkPath);
11551162
} catch (err) {
@@ -1166,7 +1173,7 @@ For git operations while detached:
11661173
log.warn("Failed to setup mock node environment", err);
11671174
}
11681175
}
1169-
return SHARED_MOCK_NODE_DIR;
1176+
return mockNodeDir;
11701177
}
11711178

11721179
private async cleanupSession(taskRunId: string): Promise<void> {

apps/code/src/main/services/posthog-plugin/service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { homedir, tmpdir } from "node:os";
44
import { join } from "node:path";
55
import { app, net } from "electron";
66
import { injectable, postConstruct, preDestroy } from "inversify";
7+
import { isDevBuild } from "../../utils/env.js";
78
import { logger } from "../../utils/logger.js";
89
import { TypedEventEmitter } from "../../utils/typed-event-emitter.js";
910
import { captureException } from "../posthog-analytics.js";
@@ -93,7 +94,7 @@ export class PosthogPluginService extends TypedEventEmitter<PosthogPluginEvents>
9394
* - Fallback: bundled plugin path.
9495
*/
9596
getPluginPath(): string {
96-
if (!app.isPackaged) {
97+
if (isDevBuild()) {
9798
return this.bundledPluginDir;
9899
}
99100

apps/code/src/main/services/settingsStore.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ import * as path from "node:path";
44
import { LEGACY_DATA_DIRS, WORKTREES_DIR } from "@shared/constants";
55
import { app } from "electron";
66
import Store from "electron-store";
7+
import { isDevBuild } from "../utils/env.js";
78

89
interface SettingsSchema {
910
worktreeLocation: string;
1011
preventSleepWhileRunning: boolean;
1112
}
1213

1314
function getDefaultWorktreeLocation(): string {
14-
const isDev = !app.isPackaged;
15+
const isDev = isDevBuild();
1516
const dir = isDev ? `${WORKTREES_DIR}-dev` : WORKTREES_DIR;
1617
return path.join(os.homedir(), dir);
1718
}
1819

1920
function getLegacyWorktreeLocations(): string[] {
20-
const isDev = !app.isPackaged;
21+
const isDev = isDevBuild();
2122
const locations: string[] = [];
2223
for (const dir of LEGACY_DATA_DIRS) {
2324
if (isDev) {

apps/code/src/main/services/updates/service.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { app, autoUpdater } from "electron";
22
import { inject, injectable, postConstruct, preDestroy } from "inversify";
33
import { MAIN_TOKENS } from "../../di/tokens.js";
4+
import { isDevBuild } from "../../utils/env.js";
45
import { logger } from "../../utils/logger.js";
56
import { TypedEventEmitter } from "../../utils/typed-event-emitter.js";
67
import type { AppLifecycleService } from "../app-lifecycle/service.js";
@@ -43,7 +44,7 @@ export class UpdatesService extends TypedEventEmitter<UpdatesEvents> {
4344

4445
get isEnabled(): boolean {
4546
return (
46-
app.isPackaged &&
47+
!isDevBuild() &&
4748
!process.env[UpdatesService.DISABLE_ENV_FLAG] &&
4849
UpdatesService.SUPPORTED_PLATFORMS.includes(process.platform)
4950
);
@@ -63,7 +64,7 @@ export class UpdatesService extends TypedEventEmitter<UpdatesEvents> {
6364
!UpdatesService.SUPPORTED_PLATFORMS.includes(process.platform)
6465
) {
6566
log.info("Auto updates only supported on macOS and Windows");
66-
} else if (!app.isPackaged) {
67+
} else if (isDevBuild()) {
6768
log.info("Auto updates only available in packaged builds");
6869
}
6970
return;
@@ -79,7 +80,7 @@ export class UpdatesService extends TypedEventEmitter<UpdatesEvents> {
7980

8081
checkForUpdates(source: CheckSource = "user"): CheckForUpdatesOutput {
8182
if (!this.isEnabled) {
82-
const reason = !app.isPackaged
83+
const reason = isDevBuild()
8384
? "Updates only available in packaged builds"
8485
: "Auto updates only supported on macOS and Windows";
8586
return { success: false, errorMessage: reason, errorCode: "disabled" };

apps/code/src/main/utils/env.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ import { mkdirSync } from "node:fs";
22
import path from "node:path";
33
import { app } from "electron";
44

5+
/**
6+
* Whether this is a development build (running via electron-forge start).
7+
* Use this for dev/prod feature gates. Use `app.isPackaged` directly only
8+
* when you need to know about ASAR packaging (e.g. resolving .unpacked paths).
9+
*/
10+
export function isDevBuild(): boolean {
11+
return !app.isPackaged;
12+
}
13+
514
export function ensureClaudeConfigDir(): void {
615
const existing = process.env.CLAUDE_CONFIG_DIR;
716
if (existing) return;

apps/code/src/main/window.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import path from "node:path";
22
import { fileURLToPath } from "node:url";
33
import { createIPCHandler } from "@posthog/electron-trpc/main";
4-
import { app, BrowserWindow, screen, shell } from "electron";
4+
import { BrowserWindow, screen, shell } from "electron";
55
import { buildApplicationMenu } from "./menu.js";
66
import { setMainWindowGetter } from "./trpc/context.js";
77
import { trpcRouter } from "./trpc/router.js";
8+
import { isDevBuild } from "./utils/env.js";
89
import { type WindowStateSchema, windowStateStore } from "./utils/store.js";
910

1011
declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string | undefined;
@@ -85,7 +86,7 @@ function setupExternalLinkHandlers(window: BrowserWindow): void {
8586
}
8687

8788
export function createWindow(): void {
88-
const isDev = !app.isPackaged;
89+
const isDev = isDevBuild();
8990
const savedState = getSavedWindowState();
9091
let saveTimeout: ReturnType<typeof setTimeout> | null = null;
9192

0 commit comments

Comments
 (0)