@@ -32,6 +32,7 @@ import {
3232 uninstallLegacySystemdUnits ,
3333 type SystemdUnitScope ,
3434} from "../daemon/systemd.js" ;
35+ import { isTruthyEnvValue } from "../infra/env.js" ;
3536import type { RuntimeEnv } from "../runtime.js" ;
3637import { VERSION } from "../version.js" ;
3738import { buildGatewayInstallPlan } from "./daemon-install-helpers.js" ;
@@ -45,6 +46,25 @@ import {
4546 isServiceRepairExternallyManaged ,
4647 resolveServiceRepairPolicy ,
4748} from "./doctor-service-repair-policy.js" ;
49+ import {
50+ UPDATE_IN_PROGRESS_ENV ,
51+ UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE_ENV ,
52+ } from "./doctor/shared/update-phase.js" ;
53+
54+ type GatewayServiceConfigRepairOptions = {
55+ allowConfigSizeDrop ?: boolean ;
56+ allowExecSecretRefs ?: boolean ;
57+ lastTouchedVersionOverride ?: string ;
58+ preservedLegacyRootKeys ?: readonly string [ ] ;
59+ skipPluginValidation ?: boolean ;
60+ } ;
61+
62+ function shouldSkipLegacyUpdateRepairConfigWrite ( env : NodeJS . ProcessEnv ) : boolean {
63+ return (
64+ isTruthyEnvValue ( env [ UPDATE_IN_PROGRESS_ENV ] ) &&
65+ ! isTruthyEnvValue ( env [ UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE_ENV ] )
66+ ) ;
67+ }
4868
4969const execFileAsync = promisify ( execFile ) ;
5070const EXECSTART_REPAIR_CODES = new Set < string > ( [
@@ -366,16 +386,16 @@ export async function maybeRepairGatewayServiceConfig(
366386 mode : "local" | "remote" ,
367387 runtime : RuntimeEnv ,
368388 prompter : DoctorPrompter ,
369- options : { allowExecSecretRefs ?: boolean ; lastTouchedVersionOverride ?: string } = { } ,
370- ) {
389+ options : GatewayServiceConfigRepairOptions = { } ,
390+ ) : Promise < OpenClawConfig > {
371391 if ( resolveIsNixMode ( process . env ) ) {
372392 note ( "Nix mode detected; skip service updates." , "Gateway" ) ;
373- return ;
393+ return cfg ;
374394 }
375395
376396 if ( mode === "remote" ) {
377397 note ( "Gateway mode is remote; skipped local service audit." , "Gateway" ) ;
378- return ;
398+ return cfg ;
379399 }
380400
381401 const service = resolveGatewayService ( ) ;
@@ -386,7 +406,7 @@ export async function maybeRepairGatewayServiceConfig(
386406 command = null ;
387407 }
388408 if ( ! command ) {
389- return ;
409+ return cfg ;
390410 }
391411 const serviceInstallEnv = buildGatewayServiceRepairEnv ( command ) ;
392412 const serviceWrapperPath = resolveGatewayServiceWrapperPath ( command ) ;
@@ -514,7 +534,7 @@ export async function maybeRepairGatewayServiceConfig(
514534 if ( sourceCheckoutWarning !== null && ! hasEntrypointMismatch ) {
515535 note ( sourceCheckoutWarning , "Gateway service config" ) ;
516536 }
517- return ;
537+ return cfg ;
518538 }
519539
520540 const serviceRepairPolicy = resolveServiceRepairPolicy ( ) ;
@@ -546,15 +566,15 @@ export async function maybeRepairGatewayServiceConfig(
546566
547567 if ( serviceRepairExternal ) {
548568 note ( EXTERNAL_SERVICE_REPAIR_NOTE , "Gateway service config" ) ;
549- return ;
569+ return cfg ;
550570 }
551571
552572 if ( serviceRewriteBlocked ) {
553573 note (
554574 "Gateway service is running; leaving supervisor metadata unchanged. Stop the service first or use `openclaw gateway install --force` when you want to replace the active launcher." ,
555575 "Gateway service config" ,
556576 ) ;
557- return ;
577+ return cfg ;
558578 }
559579
560580 const updateRepairMode = isDoctorUpdateRepairMode ( prompter . repairMode ) ;
@@ -568,7 +588,7 @@ export async function maybeRepairGatewayServiceConfig(
568588 "Update-mode doctor detected gateway service drift but left the live systemd unit unchanged. Review the service file and run `openclaw gateway install --force` when you want OpenClaw to replace operator-owned systemd directives." ,
569589 "Gateway service config" ,
570590 ) ;
571- return ;
591+ return cfg ;
572592 }
573593
574594 const repairMessage = needsAggressive
@@ -596,7 +616,7 @@ export async function maybeRepairGatewayServiceConfig(
596616 "Gateway service config" ,
597617 ) ;
598618 }
599- return ;
619+ return cfg ;
600620 }
601621 const serviceEmbeddedToken = readEmbeddedGatewayToken ( command ) ;
602622 const gatewayTokenForRepair = expectedGatewayToken ?? serviceEmbeddedToken ;
@@ -614,6 +634,16 @@ export async function maybeRepairGatewayServiceConfig(
614634 ! configuredGatewayToken &&
615635 gatewayTokenForRepair
616636 ) {
637+ if (
638+ updateRepairWillRewriteWindowsTask &&
639+ shouldSkipLegacyUpdateRepairConfigWrite ( process . env )
640+ ) {
641+ note (
642+ "Legacy update parent cannot persist gateway.auth.token before service repair; leaving the existing gateway service unchanged." ,
643+ "Gateway" ,
644+ ) ;
645+ return cfg ;
646+ }
617647 const nextCfg : OpenClawConfig = {
618648 ...cfg ,
619649 gateway : {
@@ -629,13 +659,14 @@ export async function maybeRepairGatewayServiceConfig(
629659 await replaceConfigFile ( {
630660 nextConfig : nextCfg ,
631661 afterWrite : { mode : "auto" } ,
632- ...( options . lastTouchedVersionOverride
633- ? {
634- writeOptions : {
635- lastTouchedVersionOverride : options . lastTouchedVersionOverride ,
636- } ,
637- }
638- : { } ) ,
662+ writeOptions : {
663+ allowConfigSizeDrop : options . allowConfigSizeDrop === true || updateRepairMode ,
664+ skipPluginValidation : options . skipPluginValidation === true || updateRepairMode ,
665+ preservedLegacyRootKeys : options . preservedLegacyRootKeys ,
666+ ...( options . lastTouchedVersionOverride
667+ ? { lastTouchedVersionOverride : options . lastTouchedVersionOverride }
668+ : { } ) ,
669+ } ,
639670 } ) ;
640671 cfgForServiceInstall = nextCfg ;
641672 note (
@@ -646,7 +677,7 @@ export async function maybeRepairGatewayServiceConfig(
646677 ) ;
647678 } catch ( err ) {
648679 runtime . error ( `Failed to persist gateway.auth.token before service repair: ${ String ( err ) } ` ) ;
649- return ;
680+ return cfg ;
650681 }
651682 }
652683
@@ -681,6 +712,7 @@ export async function maybeRepairGatewayServiceConfig(
681712 } catch ( err ) {
682713 runtime . error ( `Gateway service update failed: ${ String ( err ) } ` ) ;
683714 }
715+ return cfgForServiceInstall ;
684716}
685717
686718export async function maybeScanExtraGatewayServices (
0 commit comments