diff --git a/src/service/launchd.ts b/src/service/launchd.ts
index 0fdf5e3..f477e7b 100644
--- a/src/service/launchd.ts
+++ b/src/service/launchd.ts
@@ -66,7 +66,9 @@ export function buildPlist(): string {
PATH
${escapeXml(pathEnv)}
-
+${process.env.CODEX_BIN ? ` CODEX_BIN
+ ${escapeXml(process.env.CODEX_BIN)}
+` : ''}
`;
diff --git a/src/service/systemd.ts b/src/service/systemd.ts
index 10d8b46..3b26039 100644
--- a/src/service/systemd.ts
+++ b/src/service/systemd.ts
@@ -42,6 +42,7 @@ export function buildUnit(): string {
const nodePath = process.execPath;
const cliBinPath = resolveCliBinPath();
const pathEnv = process.env.PATH ?? '';
+ const codexBinEnv = process.env.CODEX_BIN ? `Environment="CODEX_BIN=${esc(process.env.CODEX_BIN)}"\n` : '';
return `[Unit]
Description=feishu-codex-bridge bot
After=network-online.target
@@ -55,6 +56,7 @@ RestartSec=5
StandardOutput=append:${serviceStdoutPath()}
StandardError=append:${serviceStderrPath()}
Environment="PATH=${esc(pathEnv)}"
+${codexBinEnv}
[Install]
WantedBy=default.target
diff --git a/src/service/win-startup.ts b/src/service/win-startup.ts
index 23adf5f..721f3e5 100644
--- a/src/service/win-startup.ts
+++ b/src/service/win-startup.ts
@@ -62,6 +62,7 @@ export function buildLauncherCmd(): string {
return [
'@echo off',
`set "PATH=${process.env.PATH ?? ''}"`,
+ ...(process.env.CODEX_BIN ? [`set "CODEX_BIN=${process.env.CODEX_BIN}"`] : []),
`set "${SERVICE_ENV_FLAG}=1"`,
`"${process.execPath}" "${resolveCliBinPath()}" run >> "${serviceStdoutPath()}" 2>> "${serviceStderrPath()}"`,
'',
diff --git a/test/service-definitions.test.ts b/test/service-definitions.test.ts
index cf51f0b..48e1c5c 100644
--- a/test/service-definitions.test.ts
+++ b/test/service-definitions.test.ts
@@ -1,4 +1,5 @@
import { describe, expect, it } from 'vitest';
+import { buildPlist } from '../src/service/launchd';
import { buildUnit, SYSTEMD_UNIT_NAME } from '../src/service/systemd';
import { buildLauncherCmd, buildLauncherVbs } from '../src/service/win-startup';
@@ -28,6 +29,22 @@ describe('systemd unit (buildUnit)', () => {
it('unit name is a .service', () => {
expect(SYSTEMD_UNIT_NAME).toMatch(/\.service$/);
});
+
+ it('preserves an explicit CODEX_BIN override for the background service', () => {
+ withCodexBin('/opt/codex/bin/codex', () => {
+ expect(buildUnit()).toContain('Environment="CODEX_BIN=/opt/codex/bin/codex"');
+ });
+ });
+});
+
+describe('launchd plist (buildPlist)', () => {
+ it('preserves an explicit CODEX_BIN override for the background service', () => {
+ withCodexBin('/Applications/Codex & Friends.app/Contents/Resources/codex', () => {
+ const plist = buildPlist();
+ expect(plist).toContain('CODEX_BIN');
+ expect(plist).toContain('/Applications/Codex & Friends.app/Contents/Resources/codex');
+ });
+ });
});
describe('Windows hidden launcher (.cmd)', () => {
@@ -45,6 +62,12 @@ describe('Windows hidden launcher (.cmd)', () => {
expect(cmd).toContain('>> "');
expect(cmd).toContain('2>> "');
});
+
+ it('preserves an explicit CODEX_BIN override for the background service', () => {
+ withCodexBin('C:\\Tools\\codex.exe', () => {
+ expect(buildLauncherCmd()).toContain('set "CODEX_BIN=C:\\Tools\\codex.exe"');
+ });
+ });
});
describe('Windows hidden launcher (.vbs)', () => {
@@ -55,3 +78,14 @@ describe('Windows hidden launcher (.vbs)', () => {
expect(vbs).toMatch(/sh\.Run "cmd \/c "".+\.cmd""", 0, False/);
});
});
+
+function withCodexBin(value: string, fn: () => void): void {
+ const prev = process.env.CODEX_BIN;
+ process.env.CODEX_BIN = value;
+ try {
+ fn();
+ } finally {
+ if (prev === undefined) delete process.env.CODEX_BIN;
+ else process.env.CODEX_BIN = prev;
+ }
+}