@@ -15,6 +15,7 @@ import {
1515} from "../daemon/legacy.js" ;
1616import { resolveGatewayProgramArguments } from "../daemon/program-args.js" ;
1717import { resolveGatewayService } from "../daemon/service.js" ;
18+ import { auditGatewayServiceConfig } from "../daemon/service-audit.js" ;
1819import type { RuntimeEnv } from "../runtime.js" ;
1920import {
2021 DEFAULT_GATEWAY_DAEMON_RUNTIME ,
@@ -23,6 +24,18 @@ import {
2324} from "./daemon-runtime.js" ;
2425import type { DoctorOptions , DoctorPrompter } from "./doctor-prompter.js" ;
2526
27+ function detectGatewayRuntime (
28+ programArguments : string [ ] | undefined ,
29+ ) : GatewayDaemonRuntime {
30+ const first = programArguments ?. [ 0 ] ;
31+ if ( first ) {
32+ const base = path . basename ( first ) . toLowerCase ( ) ;
33+ if ( base === "bun" || base === "bun.exe" ) return "bun" ;
34+ if ( base === "node" || base === "node.exe" ) return "node" ;
35+ }
36+ return DEFAULT_GATEWAY_DAEMON_RUNTIME ;
37+ }
38+
2639export async function maybeMigrateLegacyGatewayService (
2740 cfg : ClawdbotConfig ,
2841 mode : "local" | "remote" ,
@@ -112,6 +125,83 @@ export async function maybeMigrateLegacyGatewayService(
112125 } ) ;
113126}
114127
128+ export async function maybeRepairGatewayServiceConfig (
129+ cfg : ClawdbotConfig ,
130+ mode : "local" | "remote" ,
131+ runtime : RuntimeEnv ,
132+ prompter : DoctorPrompter ,
133+ ) {
134+ if ( resolveIsNixMode ( process . env ) ) {
135+ note ( "Nix mode detected; skip service updates." , "Gateway" ) ;
136+ return ;
137+ }
138+
139+ if ( mode === "remote" ) {
140+ note ( "Gateway mode is remote; skipped local service audit." , "Gateway" ) ;
141+ return ;
142+ }
143+
144+ const service = resolveGatewayService ( ) ;
145+ const command = await service . readCommand ( process . env ) . catch ( ( ) => null ) ;
146+ if ( ! command ) return ;
147+
148+ const audit = await auditGatewayServiceConfig ( {
149+ env : process . env ,
150+ command,
151+ } ) ;
152+ if ( audit . issues . length === 0 ) return ;
153+
154+ note (
155+ audit . issues
156+ . map ( ( issue ) =>
157+ issue . detail ? `- ${ issue . message } (${ issue . detail } )` : `- ${ issue . message } ` ,
158+ )
159+ . join ( "\n" ) ,
160+ "Gateway service config" ,
161+ ) ;
162+
163+ const repair = await prompter . confirmSkipInNonInteractive ( {
164+ message : "Update gateway service config to the recommended defaults now?" ,
165+ initialValue : true ,
166+ } ) ;
167+ if ( ! repair ) return ;
168+
169+ const devMode =
170+ process . argv [ 1 ] ?. includes ( `${ path . sep } src${ path . sep } ` ) &&
171+ process . argv [ 1 ] ?. endsWith ( ".ts" ) ;
172+ const port = resolveGatewayPort ( cfg , process . env ) ;
173+ const runtimeChoice = detectGatewayRuntime ( command . programArguments ) ;
174+ const { programArguments, workingDirectory } =
175+ await resolveGatewayProgramArguments ( {
176+ port,
177+ dev : devMode ,
178+ runtime : runtimeChoice ,
179+ } ) ;
180+ const environment : Record < string , string | undefined > = {
181+ PATH : process . env . PATH ,
182+ CLAWDBOT_PROFILE : process . env . CLAWDBOT_PROFILE ,
183+ CLAWDBOT_STATE_DIR : process . env . CLAWDBOT_STATE_DIR ,
184+ CLAWDBOT_CONFIG_PATH : process . env . CLAWDBOT_CONFIG_PATH ,
185+ CLAWDBOT_GATEWAY_PORT : String ( port ) ,
186+ CLAWDBOT_GATEWAY_TOKEN :
187+ cfg . gateway ?. auth ?. token ?? process . env . CLAWDBOT_GATEWAY_TOKEN ,
188+ CLAWDBOT_LAUNCHD_LABEL :
189+ process . platform === "darwin" ? GATEWAY_LAUNCH_AGENT_LABEL : undefined ,
190+ } ;
191+
192+ try {
193+ await service . install ( {
194+ env : process . env ,
195+ stdout : process . stdout ,
196+ programArguments,
197+ workingDirectory,
198+ environment,
199+ } ) ;
200+ } catch ( err ) {
201+ runtime . error ( `Gateway service update failed: ${ String ( err ) } ` ) ;
202+ }
203+ }
204+
115205export async function maybeScanExtraGatewayServices ( options : DoctorOptions ) {
116206 const extraServices = await findExtraGatewayServices ( process . env , {
117207 deep : options . deep ,
0 commit comments