From 514a6c4112300d895b1bb1205263bad76ab90ab9 Mon Sep 17 00:00:00 2001 From: Lingxuan Zuo Date: Sat, 28 Mar 2026 15:34:01 +0800 Subject: [PATCH] fix: avoid systemd-supervised gateway retry loop in owned launcher (#700) --- electron/gateway/config-sync-env.ts | 22 ++++++++++++++ electron/gateway/config-sync.ts | 4 ++- tests/unit/config-sync.test.ts | 45 +++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 electron/gateway/config-sync-env.ts create mode 100644 tests/unit/config-sync.test.ts diff --git a/electron/gateway/config-sync-env.ts b/electron/gateway/config-sync-env.ts new file mode 100644 index 000000000..24944b23a --- /dev/null +++ b/electron/gateway/config-sync-env.ts @@ -0,0 +1,22 @@ +export const SUPERVISED_SYSTEMD_ENV_KEYS = [ + 'OPENCLAW_SYSTEMD_UNIT', + 'INVOCATION_ID', + 'SYSTEMD_EXEC_PID', + 'JOURNAL_STREAM', +] as const; + +export type GatewayEnv = Record; + +/** + * OpenClaw CLI treats certain environment variables as systemd supervisor hints. + * When present in ClawX-owned child-process launches, it can mistakenly enter + * a supervised process retry loop. Strip those variables so startup follows + * ClawX lifecycle. + */ +export function stripSystemdSupervisorEnv(env: GatewayEnv): GatewayEnv { + const next = { ...env }; + for (const key of SUPERVISED_SYSTEMD_ENV_KEYS) { + delete next[key]; + } + return next; +} diff --git a/electron/gateway/config-sync.ts b/electron/gateway/config-sync.ts index 2abdde4fb..932095424 100644 --- a/electron/gateway/config-sync.ts +++ b/electron/gateway/config-sync.ts @@ -27,6 +27,8 @@ import { syncProxyConfigToOpenClaw } from '../utils/openclaw-proxy'; import { logger } from '../utils/logger'; import { prependPathEntry } from '../utils/env-path'; import { copyPluginFromNodeModules, fixupPluginManifest, cpSyncSafe } from '../utils/plugin-install'; +import { stripSystemdSupervisorEnv } from './config-sync-env'; + export interface GatewayLaunchContext { appSettings: Awaited>; @@ -317,7 +319,7 @@ export async function prepareGatewayLaunchContext(port: number): Promise = { - ...baseEnvPatched, + ...stripSystemdSupervisorEnv(baseEnvPatched), ...providerEnv, ...uvEnv, ...proxyEnv, diff --git a/tests/unit/config-sync.test.ts b/tests/unit/config-sync.test.ts new file mode 100644 index 000000000..143dbaa9e --- /dev/null +++ b/tests/unit/config-sync.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from 'vitest'; +import { stripSystemdSupervisorEnv } from '@electron/gateway/config-sync-env'; + +describe('stripSystemdSupervisorEnv', () => { + it('removes systemd supervisor marker env vars', () => { + const env = { + PATH: '/usr/bin:/bin', + OPENCLAW_SYSTEMD_UNIT: 'openclaw-gateway.service', + INVOCATION_ID: 'abc123', + SYSTEMD_EXEC_PID: '777', + JOURNAL_STREAM: '8:12345', + OTHER: 'keep-me', + }; + + const result = stripSystemdSupervisorEnv(env); + + expect(result).toEqual({ + PATH: '/usr/bin:/bin', + OTHER: 'keep-me', + }); + }); + + it('keeps unrelated variables unchanged', () => { + const env = { + NODE_ENV: 'production', + OPENCLAW_GATEWAY_TOKEN: 'token', + CLAWDBOT_SKIP_CHANNELS: '0', + }; + + expect(stripSystemdSupervisorEnv(env)).toEqual(env); + }); + + it('does not mutate source env object', () => { + const env = { + OPENCLAW_SYSTEMD_UNIT: 'openclaw-gateway.service', + VALUE: '1', + }; + const before = { ...env }; + + const result = stripSystemdSupervisorEnv(env); + + expect(env).toEqual(before); + expect(result).toEqual({ VALUE: '1' }); + }); +});