Skip to content

Commit dfe53bc

Browse files
authored
Better server build (#50)
* env.dev and env.prod separate * build-script for releease of server
1 parent 540573d commit dfe53bc

File tree

4 files changed

+253
-18
lines changed

4 files changed

+253
-18
lines changed

bun.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"commander": "^14.0.1",
3131
"core-js": "3.45.1",
3232
"debug": "4.4.3",
33+
"dotenv": "^17.2.3",
3334
"eslint": "^9.35.0",
3435
"eslint-config-prettier": "^9.1.2",
3536
"eslint-import-resolver-typescript": "^4.4.4",
@@ -902,6 +903,8 @@
902903

903904
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
904905

906+
"dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
907+
905908
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
906909

907910
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],

package.json

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
22
"name": "browseros-server",
3-
"version": "0.0.7",
3+
"version": "0.0.8",
44
"description": "Unified BrowserOS server with MCP and Agent support",
55
"private": true,
66
"type": "module",
77
"workspaces": [
88
"packages/*"
99
],
1010
"scripts": {
11-
"start": "bun run build:codex-sdk-ts && CODEX_BINARY_PATH=third_party/bin/codex bun --env-file=.env packages/server/src/index.ts",
12-
"start:debug": "bun run build:codex-sdk-ts && CODEX_BINARY_PATH=third_party/bin/codex bun --inspect-brk --env-file=.env packages/server/src/index.ts",
11+
"start": "bun run build:codex-sdk-ts && CODEX_BINARY_PATH=third_party/bin/codex bun --env-file=.env.dev packages/server/src/index.ts",
12+
"start:debug": "bun run build:codex-sdk-ts && CODEX_BINARY_PATH=third_party/bin/codex bun --inspect-brk --env-file=.env.dev packages/server/src/index.ts",
1313
"build:codex-sdk-ts": "bun run --filter @browseros/codex-sdk-ts prepare",
1414
"test": "bun test; bun run test:cleanup",
1515
"test:all": "bun test --workspace",
@@ -26,12 +26,12 @@
2626
"dev:server:windows": "mkdir -p dist/server && bun build --compile packages/server/src/index.ts --outfile dist/server/browseros-server.exe --minify --target bun-windows-x64 --env inline && bun scripts/patch-windows-exe.ts dist/server/browseros-server.exe",
2727
"dev:ext": "rimraf dist/ext && bun run --filter browseros-controller build:dev && mkdir -p dist/ext && cp -r packages/controller-ext/dist/* dist/ext/",
2828
"dist:ext": "rimraf dist/ext && mkdir -p dist/ext && bun run --filter browseros-controller build && cp -r packages/controller-ext/dist/* dist/ext/",
29-
"dist:server": "bun run build:codex-sdk-ts && rimraf dist/server && mkdir -p dist/server && bun run dist:server:linux-x64 && bun run dist:server:linux-arm64 && bun run dist:server:windows-x64 && bun run dist:server:darwin-arm64 && bun run dist:server:darwin-x64",
30-
"dist:server:linux-x64": "bun build --compile packages/server/src/index.ts --outfile dist/server/browseros-server-linux-x64 --minify --sourcemap --target=bun-linux-x64-modern --env inline",
31-
"dist:server:linux-arm64": "bun build --compile packages/server/src/index.ts --outfile dist/server/browseros-server-linux-arm64 --minify --sourcemap --target=bun-linux-arm64 --env inline",
32-
"dist:server:windows-x64": "bun build --compile packages/server/src/index.ts --outfile dist/server/browseros-server-windows-x64.exe --minify --sourcemap --target=bun-windows-x64-modern --env inline && bun scripts/patch-windows-exe.ts dist/server/browseros-server-windows-x64.exe",
33-
"dist:server:darwin-arm64": "bun build --compile packages/server/src/index.ts --outfile dist/server/browseros-server-darwin-arm64 --minify --sourcemap --target=bun-darwin-arm64 --env inline",
34-
"dist:server:darwin-x64": "bun build --compile packages/server/src/index.ts --outfile dist/server/browseros-server-darwin-x64 --minify --sourcemap --target=bun-darwin-x64 --env inline",
29+
"dist:server": "bun run build:codex-sdk-ts && rimraf dist/server && bun scripts/build_server.ts --mode=prod --target=all",
30+
"dist:server:linux-x64": "bun run build:codex-sdk-ts && bun scripts/build_server.ts --mode=prod --target=linux-x64",
31+
"dist:server:linux-arm64": "bun run build:codex-sdk-ts && bun scripts/build_server.ts --mode=prod --target=linux-arm64",
32+
"dist:server:windows-x64": "bun run build:codex-sdk-ts && bun scripts/build_server.ts --mode=prod --target=windows-x64",
33+
"dist:server:darwin-arm64": "bun run build:codex-sdk-ts && bun scripts/build_server.ts --mode=prod --target=darwin-arm64",
34+
"dist:server:darwin-x64": "bun run build:codex-sdk-ts && bun scripts/build_server.ts --mode=prod --target=darwin-x64",
3535
"format": "prettier --write --cache . || true ; eslint --cache --fix . || true",
3636
"check-format": "prettier --check --cache . || true ; eslint --cache || true",
3737
"docs": "npm run docs:generate && npm run format",
@@ -72,6 +72,7 @@
7272
"commander": "^14.0.1",
7373
"core-js": "3.45.1",
7474
"debug": "4.4.3",
75+
"dotenv": "^17.2.3",
7576
"eslint": "^9.35.0",
7677
"eslint-config-prettier": "^9.1.2",
7778
"eslint-import-resolver-typescript": "^4.4.4",

packages/agent/src/agent/CodexSDKAgent.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,31 @@ export class CodexSDKAgent extends BaseAgent {
155155
});
156156
}
157157

158+
private isExecutableFile(path: string): boolean {
159+
try {
160+
accessSync(path, fsConstants.X_OK);
161+
return true;
162+
} catch {
163+
return false;
164+
}
165+
}
166+
158167
private resolveCodexExecutablePath(): string {
159168
const codexBinaryName =
160169
process.platform === 'win32' ? 'codex.exe' : 'codex';
161170

162171
// Check CODEX_BINARY_PATH env var first
163172
if (process.env.CODEX_BINARY_PATH) {
164-
return process.env.CODEX_BINARY_PATH;
173+
const envPath = process.env.CODEX_BINARY_PATH;
174+
if (this.isExecutableFile(envPath)) {
175+
return envPath;
176+
}
177+
logger.warn(
178+
'CODEX_BINARY_PATH set but file not found or not executable',
179+
{
180+
path: envPath,
181+
},
182+
);
165183
}
166184

167185
// Check resourcesDir if provided
@@ -171,22 +189,16 @@ export class CodexSDKAgent extends BaseAgent {
171189
'bin',
172190
codexBinaryName,
173191
);
174-
try {
175-
accessSync(resourcesCodexPath, fsConstants.X_OK);
192+
if (this.isExecutableFile(resourcesCodexPath)) {
176193
return resourcesCodexPath;
177-
} catch {
178-
// Ignore failures; fall back to next option
179194
}
180195
}
181196

182197
// Check bundled codex in current binary directory
183198
const currentBinaryDirectory = dirname(process.execPath);
184199
const bundledCodexPath = join(currentBinaryDirectory, codexBinaryName);
185-
try {
186-
accessSync(bundledCodexPath, fsConstants.X_OK);
200+
if (this.isExecutableFile(bundledCodexPath)) {
187201
return bundledCodexPath;
188-
} catch {
189-
// Ignore failures; fall through to error
190202
}
191203

192204
throw new Error(

scripts/build_server.ts

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
#!/usr/bin/env bun
2+
/**
3+
* Build script for BrowserOS server binaries
4+
*
5+
* Usage:
6+
* bun scripts/build_server.ts --mode=prod [--target=darwin-arm64]
7+
* bun scripts/build_server.ts --mode=dev [--target=all]
8+
*
9+
* Modes:
10+
* prod - Clean environment build using only .env.prod
11+
* dev - Normal build using shell environment + .env.dev
12+
*
13+
* Targets:
14+
* linux-x64, linux-arm64, windows-x64, darwin-arm64, darwin-x64, all
15+
*/
16+
17+
import { spawn } from "child_process";
18+
import { readFileSync, mkdirSync } from "fs";
19+
import { resolve, join } from "path";
20+
import { parse } from "dotenv";
21+
22+
interface BuildTarget {
23+
name: string;
24+
bunTarget: string;
25+
outfile: string;
26+
}
27+
28+
const TARGETS: Record<string, BuildTarget> = {
29+
"linux-x64": {
30+
name: "Linux x64",
31+
bunTarget: "bun-linux-x64-modern",
32+
outfile: "dist/server/browseros-server-linux-x64",
33+
},
34+
"linux-arm64": {
35+
name: "Linux ARM64",
36+
bunTarget: "bun-linux-arm64",
37+
outfile: "dist/server/browseros-server-linux-arm64",
38+
},
39+
"windows-x64": {
40+
name: "Windows x64",
41+
bunTarget: "bun-windows-x64-modern",
42+
outfile: "dist/server/browseros-server-windows-x64.exe",
43+
},
44+
"darwin-arm64": {
45+
name: "macOS ARM64",
46+
bunTarget: "bun-darwin-arm64",
47+
outfile: "dist/server/browseros-server-darwin-arm64",
48+
},
49+
"darwin-x64": {
50+
name: "macOS x64",
51+
bunTarget: "bun-darwin-x64",
52+
outfile: "dist/server/browseros-server-darwin-x64",
53+
},
54+
};
55+
56+
const MINIMAL_SYSTEM_VARS = ["PATH"];
57+
58+
function parseArgs(): { mode: "prod" | "dev"; targets: string[] } {
59+
const args = process.argv.slice(2);
60+
let mode: "prod" | "dev" = "prod";
61+
let targetArg = "all";
62+
63+
for (const arg of args) {
64+
if (arg.startsWith("--mode=")) {
65+
const modeValue = arg.split("=")[1];
66+
if (modeValue !== "prod" && modeValue !== "dev") {
67+
console.error(`Invalid mode: ${modeValue}. Must be 'prod' or 'dev'`);
68+
process.exit(1);
69+
}
70+
mode = modeValue;
71+
} else if (arg.startsWith("--target=")) {
72+
targetArg = arg.split("=")[1];
73+
}
74+
}
75+
76+
const targets =
77+
targetArg === "all"
78+
? Object.keys(TARGETS)
79+
: targetArg.split(",").map((t) => t.trim());
80+
81+
for (const target of targets) {
82+
if (!TARGETS[target]) {
83+
console.error(`Invalid target: ${target}`);
84+
console.error(`Available targets: ${Object.keys(TARGETS).join(", ")}, all`);
85+
process.exit(1);
86+
}
87+
}
88+
89+
return { mode, targets };
90+
}
91+
92+
function loadEnvFile(path: string): Record<string, string> {
93+
try {
94+
const content = readFileSync(path, "utf-8");
95+
const parsed = parse(content);
96+
return parsed;
97+
} catch (error) {
98+
console.error(`Failed to load ${path}:`, error);
99+
process.exit(1);
100+
}
101+
}
102+
103+
function createCleanEnv(envVars: Record<string, string>): Record<string, string> {
104+
const cleanEnv: Record<string, string> = {};
105+
106+
for (const varName of MINIMAL_SYSTEM_VARS) {
107+
const value = process.env[varName];
108+
if (value) {
109+
cleanEnv[varName] = value;
110+
}
111+
}
112+
113+
Object.assign(cleanEnv, envVars);
114+
115+
return cleanEnv;
116+
}
117+
118+
function runCommand(
119+
command: string,
120+
args: string[],
121+
env: NodeJS.ProcessEnv
122+
): Promise<void> {
123+
return new Promise((resolve, reject) => {
124+
const child = spawn(command, args, {
125+
env,
126+
stdio: "inherit",
127+
});
128+
129+
child.on("close", (code) => {
130+
if (code === 0) {
131+
resolve();
132+
} else {
133+
reject(new Error(`Command exited with code ${code}`));
134+
}
135+
});
136+
137+
child.on("error", (error) => {
138+
reject(error);
139+
});
140+
});
141+
}
142+
143+
async function buildTarget(
144+
target: BuildTarget,
145+
mode: "prod" | "dev",
146+
envVars: Record<string, string>
147+
): Promise<void> {
148+
console.log(`\n📦 Building ${target.name}...`);
149+
150+
const args = [
151+
"build",
152+
"--compile",
153+
"packages/server/src/index.ts",
154+
"--outfile",
155+
target.outfile,
156+
"--minify",
157+
"--sourcemap",
158+
`--target=${target.bunTarget}`,
159+
"--env",
160+
"inline",
161+
];
162+
163+
const buildEnv = mode === "prod" ? createCleanEnv(envVars) : { ...process.env, ...envVars };
164+
165+
try {
166+
await runCommand("bun", args, buildEnv);
167+
console.log(`✅ ${target.name} built successfully`);
168+
169+
if (target.outfile.endsWith(".exe")) {
170+
console.log(`🔧 Patching Windows executable...`);
171+
await runCommand("bun", ["scripts/patch-windows-exe.ts", target.outfile], process.env);
172+
}
173+
} catch (error) {
174+
console.error(`❌ Failed to build ${target.name}:`, error);
175+
throw error;
176+
}
177+
}
178+
179+
async function main() {
180+
const { mode, targets } = parseArgs();
181+
const rootDir = resolve(import.meta.dir, "..");
182+
process.chdir(rootDir);
183+
184+
console.log(`🚀 Building BrowserOS server binaries`);
185+
console.log(` Mode: ${mode}`);
186+
console.log(` Targets: ${targets.join(", ")}`);
187+
188+
const envFile = mode === "prod" ? ".env.prod" : ".env.dev";
189+
const envPath = join(rootDir, envFile);
190+
191+
console.log(`\n📄 Loading environment from ${envFile}...`);
192+
const envVars = loadEnvFile(envPath);
193+
console.log(` Loaded ${Object.keys(envVars).length} variables`);
194+
195+
if (mode === "prod") {
196+
console.log(`\n🔒 Production mode: Using CLEAN environment (only ${envFile} + minimal system vars)`);
197+
console.log(` System vars: ${MINIMAL_SYSTEM_VARS.join(", ")}`);
198+
} else {
199+
console.log(`\n🔓 Development mode: Using shell environment + ${envFile}`);
200+
}
201+
202+
mkdirSync("dist/server", { recursive: true });
203+
204+
for (const targetKey of targets) {
205+
const target = TARGETS[targetKey];
206+
await buildTarget(target, mode, envVars);
207+
}
208+
209+
console.log(`\n✨ All builds completed successfully!`);
210+
console.log(`\n📦 Output files:`);
211+
for (const targetKey of targets) {
212+
console.log(` ${TARGETS[targetKey].outfile}`);
213+
}
214+
}
215+
216+
main().catch((error) => {
217+
console.error("\n💥 Build failed:", error);
218+
process.exit(1);
219+
});

0 commit comments

Comments
 (0)