Skip to content

Commit c11df88

Browse files
committed
feat: bundle agent-server into a single CJS file for production to resolve ESM/asar pathing issues
1 parent 533da32 commit c11df88

8 files changed

Lines changed: 30 additions & 43 deletions

File tree

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ jobs:
3232
- name: Install dependencies
3333
run: npm ci
3434

35+
- name: Bundle agent server
36+
run: npm run bundle:agent
37+
3538
- name: Build and Release
3639
uses: samuelmeuli/action-electron-builder@v1
3740
env:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ node_modules
1111
dist
1212
dist-ssr
1313
dist-electron
14+
agent-server.bundle.cjs
1415
*.local
1516

1617
# Editor directories and files

electron-builder.json

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,10 @@
1010
"dist/**/*",
1111
"electron/**/*",
1212
"package.json",
13-
"agent-server.js",
14-
"wizard-server.js",
15-
"src/wizard/**/*",
16-
"src/services/**/*",
17-
"src/utils/**/*",
18-
"src/ai/palettes.js",
19-
"src/constants.js",
20-
"src/core/**/*"
13+
"agent-server.bundle.cjs"
2114
],
2215
"asarUnpack": [
23-
"package.json",
24-
"agent-server.js",
25-
"wizard-server.js",
26-
"src/wizard/**/*",
27-
"src/services/**/*",
28-
"src/utils/**/*",
29-
"src/ai/palettes.js",
30-
"src/constants.js",
31-
"src/core/**/*",
32-
"node_modules/**/*"
16+
"agent-server.bundle.cjs"
3317
],
3418
"mac": {
3519
"entitlements": "entitlements.mac.plist",

electron/main.cjs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,17 @@ function startAgentServer() {
6161
return;
6262
}
6363

64-
// agent-server.js uses ESM imports, which don't work from inside asar archives.
65-
// Both script and cwd must point to app.asar.unpacked/ where all files
66-
// (including node_modules) are extracted as real files on disk.
67-
let agentServerPath = path.join(__dirname, '..', 'agent-server.js');
64+
// In production: use the pre-bundled CJS file from app.asar.unpacked/
65+
// (single file, no ESM/asar issues, no node_modules needed)
66+
// In dev: use the original ESM source (system Node.js handles ESM fine)
67+
let agentServerPath;
6868
let agentCwd = path.join(__dirname, '..');
6969
if (app.isPackaged) {
70-
agentServerPath = agentServerPath.replace('app.asar', 'app.asar.unpacked');
70+
agentServerPath = path.join(__dirname, '..', 'agent-server.bundle.cjs')
71+
.replace('app.asar', 'app.asar.unpacked');
7172
agentCwd = agentCwd.replace('app.asar', 'app.asar.unpacked');
73+
} else {
74+
agentServerPath = path.join(__dirname, '..', 'agent-server.js');
7275
}
7376

7477
if (!fsSync.existsSync(agentServerPath)) {

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525
"bump-version": "node scripts/bump-version.js",
2626
"dev": "vite",
2727
"electron:dev": "concurrently -k \"npm run oauth\" \"npm run dev\" \"wait-on http://localhost:4001 && cross-env NODE_ENV=development electron .\"",
28-
"electron:build": "npm run build && electron-builder",
29-
"electron:build:mac": "npm run build && electron-builder --mac",
30-
"electron:build:win": "npm run build && electron-builder --win",
31-
"electron:build:linux": "npm run build && electron-builder --linux",
28+
"bundle:agent": "esbuild agent-server.js --bundle --platform=node --format=cjs --outfile=agent-server.bundle.cjs",
29+
"electron:build": "npm run build && npm run bundle:agent && electron-builder",
30+
"electron:build:mac": "npm run build && npm run bundle:agent && electron-builder --mac",
31+
"electron:build:win": "npm run build && npm run bundle:agent && electron-builder --win",
32+
"electron:build:linux": "npm run build && npm run bundle:agent && electron-builder --linux",
3233
"free:4000": "node scripts/free-port-4000.js",
3334
"build": "vite build",
3435
"lint": "eslint .",

src/services/queue/Queue.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import fs from 'fs';
55
import path from 'path';
66
import { fileURLToPath } from 'url';
77

8-
const __filename = fileURLToPath(import.meta.url);
9-
const __dirname = path.dirname(__filename);
8+
// Safe for both ESM and CJS (esbuild bundle) contexts
9+
const __filename = import.meta.url ? fileURLToPath(import.meta.url) : '';
10+
const __dirname = __filename ? path.dirname(__filename) : '';
1011

1112
function ensureDir(dirPath) {
1213
if (!fs.existsSync(dirPath)) {
@@ -23,7 +24,9 @@ function randomId(prefix = 'q') {
2324
}
2425

2526
class QueueManager {
26-
constructor(journalRoot = path.resolve(__dirname, '../../../data/queues')) {
27+
constructor(journalRoot = __dirname
28+
? path.resolve(__dirname, '../../../data/queues')
29+
: path.join(process.cwd(), 'data', 'queues')) {
2730
this.journalRoot = journalRoot;
2831
ensureDir(this.journalRoot);
2932
// Map<string, { items: Array, inflight: Map<leaseId,itemId>, byId: Map, metrics: {} }>

src/wizard/AgentLoop.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import { selectToolsForTurn } from './tools/schemas.js';
1313
import { WIZARD_SYSTEM_PROMPT } from '../services/agent/WizardPrompt.js';
1414
import { NODE_DEFAULT_COLOR } from '../constants.js';
1515

16-
const __filename = fileURLToPath(import.meta.url);
17-
const __dirname = path.dirname(__filename);
16+
// Safe for both ESM and CJS (esbuild bundle) contexts
17+
const __filename = import.meta.url ? fileURLToPath(import.meta.url) : '';
18+
const __dirname = __filename ? path.dirname(__filename) : process.cwd();
1819

1920
// Load system prompt
2021
let SYSTEM_PROMPT = WIZARD_SYSTEM_PROMPT;

wizard-server.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { getToolDefinitions, executeTool } from './src/wizard/tools/index.js';
1616
import { callLLM } from './src/wizard/LLMClient.js';
1717
import { debugLogSync } from './src/utils/debugLogger.js';
1818
import { enrichBatch, enrichSingle } from './src/wizard/services/wikipediaEnrichment.js';
19+
import SchedulerModule from './src/services/orchestrator/Scheduler.js';
1920

2021
const app = express();
2122

@@ -72,18 +73,8 @@ let scheduler = null;
7273
async function ensureSchedulerStarted() {
7374
if (!scheduler) {
7475
try {
75-
// #region agent log
76-
debugLogSync('wizard-server.js:scheduler:IMPORT_START', 'Importing scheduler', {}, 'debug-session', 'E');
77-
// #endregion
78-
const mod = await import('./src/services/orchestrator/Scheduler.js');
79-
scheduler = mod.default;
80-
// #region agent log
81-
debugLogSync('wizard-server.js:scheduler:IMPORT_OK', 'Scheduler imported', { hasDefault: !!mod.default }, 'debug-session', 'E');
82-
// #endregion
76+
scheduler = SchedulerModule;
8377
} catch (e) {
84-
// #region agent log
85-
debugLogSync('wizard-server.js:scheduler:IMPORT_FAIL', 'Scheduler import failed', { error: e.message }, 'debug-session', 'E');
86-
// #endregion
8778
console.warn('[Wizard] Failed to load scheduler:', e.message);
8879
return;
8980
}

0 commit comments

Comments
 (0)