mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 03:45:46 -06:00
fix: allow gateway service commands for named profiles (#116314)
* fix: gateway service commands refuse a named profile or relocated OPENCLAW_HOME - Resolve the default install identity against the canonical state directory for the active OpenClaw home and profile instead of the unprofiled OS account default. - `--profile <name>` / `--dev` project `.openclaw-<profile>` state and config paths, so every named profile was classified as isolated state and refused `install`, `start`, `stop`, `restart`, `uninstall`, Doctor service repair, and self-update service handling. - `OPENCLAW_HOME` relocates all OpenClaw path defaults and is documented for running as a dedicated service user; a relocated home is now an install identity. `HOME` alone still is not. - An `OPENCLAW_STATE_DIR` or `OPENCLAW_CONFIG_PATH` pointing outside those canonical paths is still treated as isolated state. - Recovery guidance in the refusal message now names the paths that must match. Verified: focused vitest shards for the changed suites plus the daemon, CLI, and doctor suites that consume the identity check; tsgo core and core-test lanes; oxlint; docs format, MDX, link, and map checks. * fix(gateway): keep relocated homes isolated * fix(config): validate service profile identity * fix(daemon): enforce named-profile service ownership * fix(update): reject drifted service selectors before probes * test(windows): prove scheduled task lifecycle * test(windows): harden scheduled task proof cleanup * test(windows): bind lifecycle proof to checkout * test(windows): normalize cleanup exit status * test(windows): verify effective task privilege * test(windows): protect scheduled task proof roots * test(windows): prove listener-owned task lifecycle * test(windows): fix scheduled task proof contracts * test(windows): remove redundant mock coercions * test(windows): measure fallback before task probes * test(windows): prove scheduled task process origin * fix(gateway): preserve unmanaged restart fallback * test(gateway): cover denied restart ownership * test(gateway): keep restart helper types private * test(gateway): classify lifecycle helpers as test code --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -38,9 +38,13 @@ import {
|
||||
} from "./update-command-post-core.js";
|
||||
import { POST_PLUGIN_DOCTOR_EXECUTION_FAILED_REASON } from "./update-command-post-plugin-validation.js";
|
||||
import {
|
||||
assertGatewayServiceManagementAllowedForUpdate,
|
||||
GatewayServiceUpdateOwnershipError,
|
||||
gatewayServiceCommandUsesRoot,
|
||||
isGatewayServiceManagementAllowedForUpdate,
|
||||
maybeRestartService,
|
||||
maybeRestartServiceAfterFailedMutableUpdate,
|
||||
resolveGatewayServiceManagementBlockMessageForUpdate,
|
||||
resolvePostUpdateServiceStateReadEnv,
|
||||
resolveUpdatedGatewayRestartPort,
|
||||
restoreWindowsTaskAutoStartOrExit,
|
||||
@@ -340,18 +344,30 @@ export async function finishUpdate(params: {
|
||||
let refreshGatewayServiceEnv = false;
|
||||
let gatewayServiceEnv: NodeJS.ProcessEnv | undefined;
|
||||
let skipLegacyServiceRestart = false;
|
||||
const serviceStateReadEnv = resolvePostUpdateServiceStateReadEnv({
|
||||
updateMode: resultWithPostUpdate.mode,
|
||||
processEnv: process.env,
|
||||
preManagedServiceEnv: params.preManagedServiceStop?.serviceEnv,
|
||||
});
|
||||
const serviceMutationAllowed =
|
||||
params.preManagedServiceStop?.serviceMutationAllowed !== false &&
|
||||
isGatewayServiceManagementAllowedForUpdate(process.env) &&
|
||||
isGatewayServiceManagementAllowedForUpdate(serviceStateReadEnv);
|
||||
const serviceMutationSkipMessage =
|
||||
params.shouldRestart && !serviceMutationAllowed
|
||||
? (params.preManagedServiceStop?.serviceMutationSkipMessage ??
|
||||
resolveGatewayServiceManagementBlockMessageForUpdate(process.env) ??
|
||||
resolveGatewayServiceManagementBlockMessageForUpdate(serviceStateReadEnv))
|
||||
: undefined;
|
||||
let gatewayPort = resolveUpdatedGatewayRestartPort({
|
||||
config: restartConfigSnapshot.valid ? restartConfigSnapshot.config : undefined,
|
||||
processEnv: process.env,
|
||||
});
|
||||
if (params.shouldRestart) {
|
||||
if (params.shouldRestart && serviceMutationAllowed) {
|
||||
try {
|
||||
const serviceState = await readGatewayServiceState(resolveGatewayService(), {
|
||||
env: resolvePostUpdateServiceStateReadEnv({
|
||||
updateMode: resultWithPostUpdate.mode,
|
||||
processEnv: process.env,
|
||||
preManagedServiceEnv: params.preManagedServiceStop?.serviceEnv,
|
||||
}),
|
||||
env: serviceStateReadEnv,
|
||||
validateEnvBeforeStatusRead: assertGatewayServiceManagementAllowedForUpdate,
|
||||
});
|
||||
const serviceMatchesUpdateRoot =
|
||||
(await gatewayServiceCommandUsesRoot({
|
||||
@@ -399,7 +415,12 @@ export async function finishUpdate(params: {
|
||||
// ownership authorizes rewriting the service definition.
|
||||
refreshGatewayServiceEnv = serviceOwnershipConfirmed;
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
if (err instanceof GatewayServiceUpdateOwnershipError) {
|
||||
defaultRuntime.error(err.message);
|
||||
defaultRuntime.exit(1);
|
||||
return;
|
||||
}
|
||||
// Ignore errors during pre-check; fallback to standard restart
|
||||
}
|
||||
}
|
||||
@@ -420,7 +441,7 @@ export async function finishUpdate(params: {
|
||||
return;
|
||||
}
|
||||
const restartOk = await maybeRestartService({
|
||||
shouldRestart: params.shouldRestart,
|
||||
shouldRestart: params.shouldRestart && serviceMutationAllowed,
|
||||
result: resultWithPostUpdate,
|
||||
opts: params.opts,
|
||||
refreshServiceEnv: refreshGatewayServiceEnv,
|
||||
@@ -432,6 +453,7 @@ export async function finishUpdate(params: {
|
||||
skipLegacyServiceRestart,
|
||||
requireRunningServiceAfterRestart:
|
||||
resultWithPostUpdate.mode === "git" && params.preManagedServiceStop?.stopped === true,
|
||||
serviceMutationSkipMessage,
|
||||
timeoutMs: params.updateStepTimeoutMs,
|
||||
});
|
||||
if (!restartOk) {
|
||||
|
||||
Reference in New Issue
Block a user