fix(update): use service env for doctor

This commit is contained in:
Vincent Koc
2026-05-01 05:00:04 -07:00
parent debb8ac76c
commit cad2cef0fb
3 changed files with 76 additions and 1 deletions

View File

@@ -38,6 +38,7 @@ Docs: https://docs.openclaw.ai
- Auto-reply/docking: require `/dock-*` route switches to start from direct chats, so group or channel participants cannot reroute a shared session's future replies into a linked DM. Thanks @vincentkoc.
- Discord: keep text-DM main-session route updates pinned to the configured DM owner, matching component interactions so another direct-message sender cannot redirect future main-session replies. Thanks @vincentkoc.
- Mattermost/Matrix: keep direct-message main-session route updates pinned to the configured DM owner so paired or temporarily allowed senders cannot redirect future shared-session replies. Thanks @vincentkoc.
- Updates: run package post-install doctor repair with the managed Gateway service profile and state paths when a daemon is installed, so shell/profile mismatches no longer repair the caller state while the restarted Gateway keeps stale config. Thanks @vincentkoc.
- CLI/update: verify managed gateway restarts against the installed service port instead of the caller shell port, so package updates do not report a healthy daemon as failed when profiles use different gateway ports. Thanks @vincentkoc.
- Gateway/agent: reject strict `openclaw agent --deliver` requests with missing delivery targets before starting the agent run, so users do not wait for a completed turn that cannot send anywhere. Thanks @vincentkoc.
- Setup/import: honor non-interactive `--import-from` onboarding flags by running the migration import path instead of silently completing normal setup without importing anything. Thanks @vincentkoc.

View File

@@ -5,6 +5,7 @@ import {
resolveGatewayInstallEntrypoint,
} from "../../daemon/gateway-entrypoint.js";
import {
resolvePostInstallDoctorEnv,
shouldPrepareUpdatedInstallRestart,
resolveUpdatedGatewayRestartPort,
shouldUseLegacyProcessRestartAfterUpdate,
@@ -106,6 +107,48 @@ describe("resolveUpdatedGatewayRestartPort", () => {
});
});
describe("resolvePostInstallDoctorEnv", () => {
it("uses the managed service profile paths for post-install doctor", () => {
const env = resolvePostInstallDoctorEnv({
invocationCwd: "/srv/openclaw",
baseEnv: {
PATH: "/bin",
OPENCLAW_STATE_DIR: "/wrong/state",
OPENCLAW_CONFIG_PATH: "/wrong/openclaw.json",
OPENCLAW_PROFILE: "wrong",
},
serviceEnv: {
OPENCLAW_STATE_DIR: "daemon-state",
OPENCLAW_CONFIG_PATH: "daemon-state/openclaw.json",
OPENCLAW_PROFILE: "work",
},
});
expect(env.PATH).toBe("/bin");
expect(env.NODE_DISABLE_COMPILE_CACHE).toBe("1");
expect(env.OPENCLAW_STATE_DIR).toBe(path.join("/srv/openclaw", "daemon-state"));
expect(env.OPENCLAW_CONFIG_PATH).toBe(
path.join("/srv/openclaw", "daemon-state", "openclaw.json"),
);
expect(env.OPENCLAW_PROFILE).toBe("work");
});
it("keeps the caller env when no managed service env is available", () => {
const env = resolvePostInstallDoctorEnv({
baseEnv: {
PATH: "/bin",
OPENCLAW_STATE_DIR: "/caller/state",
OPENCLAW_PROFILE: "caller",
},
});
expect(env.PATH).toBe("/bin");
expect(env.NODE_DISABLE_COMPILE_CACHE).toBe("1");
expect(env.OPENCLAW_STATE_DIR).toBe("/caller/state");
expect(env.OPENCLAW_PROFILE).toBe("caller");
});
});
describe("shouldUseLegacyProcessRestartAfterUpdate", () => {
it("never restarts package updates through the pre-update process", () => {
expect(shouldUseLegacyProcessRestartAfterUpdate({ updateMode: "npm" })).toBe(false);

View File

@@ -103,6 +103,10 @@ const SERVICE_REFRESH_PATH_ENV_KEYS = [
"OPENCLAW_STATE_DIR",
"OPENCLAW_CONFIG_PATH",
] as const;
const POST_INSTALL_DOCTOR_SERVICE_ENV_KEYS = [
...SERVICE_REFRESH_PATH_ENV_KEYS,
"OPENCLAW_PROFILE",
] as const;
const UPDATE_QUIPS = [
"Leveled up! New skills unlocked. You're welcome.",
@@ -320,6 +324,26 @@ function resolveUpdatedInstallCommandEnv(
return disableUpdatedPackageCompileCacheEnv(resolveServiceRefreshEnv(env, invocationCwd));
}
export function resolvePostInstallDoctorEnv(params?: {
baseEnv?: NodeJS.ProcessEnv;
serviceEnv?: NodeJS.ProcessEnv;
invocationCwd?: string;
}): NodeJS.ProcessEnv {
const resolvedEnv = disableUpdatedPackageCompileCacheEnv(params?.baseEnv ?? process.env);
if (!params?.serviceEnv) {
return resolvedEnv;
}
const serviceEnv = resolveServiceRefreshEnv(params.serviceEnv, params.invocationCwd);
for (const key of POST_INSTALL_DOCTOR_SERVICE_ENV_KEYS) {
const value = serviceEnv[key]?.trim();
if (value) {
resolvedEnv[key] = serviceEnv[key];
}
}
return resolvedEnv;
}
export function resolveUpdatedGatewayRestartPort(params: {
config?: OpenClawConfig;
processEnv?: NodeJS.ProcessEnv;
@@ -515,6 +539,8 @@ async function runPackageInstallUpdate(params: {
startedAt: number;
progress: ReturnType<typeof createUpdateProgress>["progress"];
jsonMode: boolean;
managedServiceEnv?: NodeJS.ProcessEnv;
invocationCwd?: string;
}): Promise<UpdateRunResult> {
const manager = await resolveGlobalManager({
root: params.root,
@@ -579,7 +605,10 @@ async function runPackageInstallUpdate(params: {
name: `${CLI_NAME} doctor`,
argv: [resolveNodeRunner(), entryPath, "doctor", "--non-interactive", "--fix"],
env: {
...disableUpdatedPackageCompileCacheEnv(process.env),
...resolvePostInstallDoctorEnv({
serviceEnv: params.managedServiceEnv,
invocationCwd: params.invocationCwd,
}),
OPENCLAW_UPDATE_IN_PROGRESS: "1",
[UPDATE_PARENT_SUPPORTS_DOCTOR_CONFIG_WRITE_ENV]: "1",
},
@@ -1619,6 +1648,8 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
startedAt,
progress,
jsonMode: Boolean(opts.json),
managedServiceEnv: prePackageServiceStop?.serviceEnv,
invocationCwd,
})
: await runGitUpdate({
root,