Skip to content

Commit 7386fe8

Browse files
fix(gateway): preserve restart warnings in json
1 parent e3ec88d commit 7386fe8

4 files changed

Lines changed: 30 additions & 3 deletions

File tree

src/cli/daemon-cli/lifecycle-core.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
33
import type { OpenClawConfig } from "../../config/config.js";
44
import type { GatewayService } from "../../daemon/service.js";
5+
import type { GatewayServiceControlArgs } from "../../daemon/service-types.js";
56
import {
67
defaultRuntime,
78
resetLifecycleRuntimeLogs,
@@ -445,6 +446,25 @@ describe("runServiceRestart token drift", () => {
445446
expect(service.restart).toHaveBeenCalledTimes(1);
446447
});
447448

449+
it("captures service restart warnings in json restart output", async () => {
450+
service.restart.mockImplementationOnce(async (args?: GatewayServiceControlArgs) => {
451+
args?.warn?.(
452+
"Existing generated LaunchAgent env wrapper contains custom behavior and will be overwritten.",
453+
);
454+
return { outcome: "completed" };
455+
});
456+
457+
await runServiceRestart(createServiceRunArgs());
458+
459+
const payload = readJsonLog<{ warnings?: string[] }>();
460+
expect(payload.warnings).toContain(
461+
"Existing generated LaunchAgent env wrapper contains custom behavior and will be overwritten.",
462+
);
463+
expect(service.restart).toHaveBeenCalledWith(
464+
expect.objectContaining({ warn: expect.any(Function) }),
465+
);
466+
});
467+
448468
it("writes restart force and wait options into the service-manager intent", async () => {
449469
service.readRuntime.mockResolvedValue({ status: "running", pid: 1234 });
450470

src/cli/daemon-cli/lifecycle-core.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,8 @@ export async function runServiceRestart(params: {
470470
onNotLoaded?: (ctx: ServiceRecoveryContext) => Promise<ServiceRecoveryResult | null>;
471471
}): Promise<boolean> {
472472
const json = Boolean(params.opts?.json);
473-
const { stdout, emit, fail } = createDaemonActionContext({ action: "restart", json });
474-
const warnings: string[] = [];
473+
const { stdout, warnings, emit, fail } = createDaemonActionContext({ action: "restart", json });
474+
const warn = json ? (message: string) => warnings.push(message) : undefined;
475475
const restartIntent = params.opts?.restartIntent;
476476
let handledRecovery: ServiceRecoveryResult | null = null;
477477
let recoveredLoadedState: boolean | null = null;
@@ -590,7 +590,7 @@ export async function runServiceRestart(params: {
590590
});
591591
}
592592
try {
593-
restartResult = await params.service.restart({ env: process.env, stdout });
593+
restartResult = await params.service.restart({ env: process.env, stdout, warn });
594594
} catch (err) {
595595
if (wroteRestartIntent) {
596596
clearGatewayRestartIntentSync();

src/daemon/launchd.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,11 +1035,13 @@ async function rewriteLaunchAgentPlistForRestart({
10351035
label,
10361036
plistPath,
10371037
stdout,
1038+
warn,
10381039
}: {
10391040
env: GatewayServiceEnv;
10401041
label: string;
10411042
plistPath: string;
10421043
stdout?: NodeJS.WritableStream;
1044+
warn?: (message: string) => void;
10431045
}): Promise<boolean> {
10441046
const existing = await readLaunchAgentProgramArgumentsFromFile(
10451047
plistPath,
@@ -1062,6 +1064,7 @@ async function rewriteLaunchAgentPlistForRestart({
10621064
programArguments: existing.programArguments,
10631065
environment: existing.environment,
10641066
stdout,
1067+
warn,
10651068
});
10661069
const plist = buildLaunchAgentPlist({
10671070
label,
@@ -1105,6 +1108,7 @@ async function ensureLaunchAgentLoadedAfterFailure(params: {
11051108
export async function restartLaunchAgent({
11061109
stdout,
11071110
env,
1111+
warn,
11081112
}: GatewayServiceControlArgs): Promise<GatewayServiceRestartResult> {
11091113
const serviceEnv = env ?? (process.env as GatewayServiceEnv);
11101114
const domain = resolveGuiDomain();
@@ -1121,6 +1125,7 @@ export async function restartLaunchAgent({
11211125
label,
11221126
plistPath,
11231127
stdout,
1128+
warn,
11241129
});
11251130
const handoff = scheduleDetachedLaunchdRestartHandoff({
11261131
env: serviceEnv,
@@ -1152,6 +1157,7 @@ export async function restartLaunchAgent({
11521157
label,
11531158
plistPath,
11541159
stdout,
1160+
warn,
11551161
});
11561162

11571163
// `openclaw gateway restart` is an explicit operator request to bring the

src/daemon/service-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type GatewayServiceControlArgs = {
2727
stdout: NodeJS.WritableStream;
2828
env?: GatewayServiceEnv;
2929
disable?: boolean;
30+
warn?: (message: string) => void;
3031
};
3132

3233
export type GatewayServiceRestartResult = { outcome: "completed" } | { outcome: "scheduled" };

0 commit comments

Comments
 (0)