mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-10 04:33:00 +00:00
Reapply "refactor: move runtime state to SQLite"
This reverts commit 694ca50e97.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -21,9 +20,7 @@ import type { UpdateStepProgress, UpdateStepResult } from "../../infra/update-ru
|
||||
import { runCommandWithTimeout } from "../../process/exec.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { pathExists } from "../../utils.js";
|
||||
import { COMPLETION_SKIP_PLUGIN_COMMANDS_ENV } from "../completion-runtime.js";
|
||||
|
||||
export type UpdateCommandOptions = {
|
||||
json?: boolean;
|
||||
@@ -279,53 +276,6 @@ export async function resolveGlobalManager(params: {
|
||||
return byPresence ?? "npm";
|
||||
}
|
||||
|
||||
const COMPLETION_CACHE_WRITE_TIMEOUT_MS = 30_000;
|
||||
const COMPLETION_CACHE_MANUAL_REFRESH_HINT =
|
||||
"Shell tab-completion may be stale; refresh manually with: openclaw completion --write-state";
|
||||
|
||||
export async function tryWriteCompletionCache(root: string, jsonMode: boolean): Promise<void> {
|
||||
const binPath = path.join(root, "openclaw.mjs");
|
||||
if (!(await pathExists(binPath))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = spawnSync(resolveNodeRunner(), [binPath, "completion", "--write-state"], {
|
||||
cwd: root,
|
||||
env: {
|
||||
...process.env,
|
||||
[COMPLETION_SKIP_PLUGIN_COMMANDS_ENV]: "1",
|
||||
},
|
||||
encoding: "utf-8",
|
||||
timeout: COMPLETION_CACHE_WRITE_TIMEOUT_MS,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
if (!jsonMode) {
|
||||
const err = result.error as NodeJS.ErrnoException;
|
||||
const reason =
|
||||
err.code === "ETIMEDOUT"
|
||||
? `timed out after ${COMPLETION_CACHE_WRITE_TIMEOUT_MS / 1000}s`
|
||||
: String(result.error);
|
||||
defaultRuntime.log(
|
||||
theme.warn(
|
||||
`Completion cache update failed: ${reason}. ${COMPLETION_CACHE_MANUAL_REFRESH_HINT}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.status !== 0 && !jsonMode) {
|
||||
const stderr = (result.stderr ?? "").trim();
|
||||
const detail = stderr ? ` (${stderr})` : "";
|
||||
defaultRuntime.log(
|
||||
theme.warn(
|
||||
`Completion cache update failed${detail}. ${COMPLETION_CACHE_MANUAL_REFRESH_HINT}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function createGlobalCommandRunner(): CommandRunner {
|
||||
return async (argv, options) => {
|
||||
const res = await runCommandWithTimeout(argv, options);
|
||||
|
||||
@@ -5,10 +5,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { Writable } from "node:stream";
|
||||
import { confirm, isCancel } from "@clack/prompts";
|
||||
import {
|
||||
checkShellCompletionStatus,
|
||||
ensureCompletionCacheExists,
|
||||
} from "../../commands/doctor-completion.js";
|
||||
import { checkShellCompletionStatus } from "../../commands/doctor-completion.js";
|
||||
import { doctorCommand } from "../../commands/doctor.js";
|
||||
import {
|
||||
UPDATE_DEFER_CONFIGURED_PLUGIN_INSTALL_REPAIR_ENV,
|
||||
@@ -38,7 +35,6 @@ import { resolveGatewayInstallEntrypoint } from "../../daemon/gateway-entrypoint
|
||||
import { disableCurrentOpenClawUpdateLaunchdJob } from "../../daemon/launchd.js";
|
||||
import { resolveGatewayRestartLogPath } from "../../daemon/restart-logs.js";
|
||||
import { summarizeGatewayServiceLayout } from "../../daemon/service-layout.js";
|
||||
import type { GatewayServiceCommandConfig } from "../../daemon/service-types.js";
|
||||
import {
|
||||
readGatewayServiceState,
|
||||
resolveGatewayService,
|
||||
@@ -101,7 +97,6 @@ import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { stylePromptMessage } from "../../terminal/prompt-style.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { resolveUserPath } from "../../utils.js";
|
||||
import { VERSION } from "../../version.js";
|
||||
import { replaceCliName, resolveCliName } from "../cli-name.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import { installCompletion } from "../completion-runtime.js";
|
||||
@@ -136,7 +131,6 @@ import {
|
||||
resolveTargetVersion,
|
||||
resolveUpdateRoot,
|
||||
runUpdateStep,
|
||||
tryWriteCompletionCache,
|
||||
type UpdateCommandOptions,
|
||||
type UpdateFinalizeOptions,
|
||||
} from "./shared.js";
|
||||
@@ -758,12 +752,6 @@ type PrePackageServiceStop = {
|
||||
serviceEnv?: NodeJS.ProcessEnv;
|
||||
};
|
||||
|
||||
type ManagedServiceRootRedirect = {
|
||||
root: string;
|
||||
previousRoot: string;
|
||||
nodeRunner?: string;
|
||||
};
|
||||
|
||||
function formatGatewayAncestryBlockMessage(pid: number): string {
|
||||
return `openclaw update detected it is running inside the gateway process tree.
|
||||
Gateway PID ${pid} is an ancestor of this process, so this updater cannot safely stop or restart the gateway that owns it.
|
||||
@@ -983,7 +971,6 @@ function tryResolveInvocationCwd(): string | undefined {
|
||||
async function resolvePackageRuntimePreflightError(params: {
|
||||
tag: string;
|
||||
timeoutMs?: number;
|
||||
nodeRunner?: string;
|
||||
}): Promise<string | null> {
|
||||
if (!canResolveRegistryVersionForPackageTarget(params.tag)) {
|
||||
return null;
|
||||
@@ -999,45 +986,20 @@ async function resolvePackageRuntimePreflightError(params: {
|
||||
if (status.error) {
|
||||
return null;
|
||||
}
|
||||
const runtime = await resolvePackageRuntimeForPreflight({
|
||||
nodeRunner: params.nodeRunner,
|
||||
timeoutMs: params.timeoutMs,
|
||||
});
|
||||
const satisfies = nodeVersionSatisfiesEngine(runtime.version, status.nodeEngine);
|
||||
const satisfies = nodeVersionSatisfiesEngine(process.versions.node ?? null, status.nodeEngine);
|
||||
if (satisfies !== false) {
|
||||
return null;
|
||||
}
|
||||
const targetLabel = status.version ?? target;
|
||||
const runtimeLabel = runtime.nodeRunner
|
||||
? `Node ${runtime.version ?? "unknown"} at ${runtime.nodeRunner}`
|
||||
: `Node ${runtime.version ?? "unknown"}`;
|
||||
return [
|
||||
`${runtimeLabel} is too old for openclaw@${targetLabel}.`,
|
||||
`Node ${process.versions.node ?? "unknown"} is too old for openclaw@${targetLabel}.`,
|
||||
`The requested package requires ${status.nodeEngine}.`,
|
||||
runtime.nodeRunner
|
||||
? "Upgrade the Node runtime that owns the managed Gateway service, then rerun `openclaw update`."
|
||||
: "Upgrade Node to 22.19+ or Node 24, then rerun `openclaw update`.",
|
||||
"Upgrade Node to 24 or newer, then rerun `openclaw update`.",
|
||||
"Bare `npm i -g openclaw` can silently install an older compatible release.",
|
||||
"After upgrading Node, use `npm i -g openclaw@latest`.",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
async function resolvePackageRuntimeForPreflight(params: {
|
||||
nodeRunner?: string;
|
||||
timeoutMs?: number;
|
||||
}): Promise<{ version: string | null; nodeRunner?: string }> {
|
||||
const nodeRunner = normalizeOptionalString(params.nodeRunner);
|
||||
if (!nodeRunner) {
|
||||
return { version: process.versions.node ?? null };
|
||||
}
|
||||
const res = await runCommandWithTimeout([nodeRunner, "--version"], {
|
||||
timeoutMs: Math.min(params.timeoutMs ?? 10_000, 10_000),
|
||||
}).catch(() => null);
|
||||
const rawVersion = res?.code === 0 ? res.stdout.trim() : "";
|
||||
const version = rawVersion.replace(/^v/u, "") || null;
|
||||
return { version, nodeRunner };
|
||||
}
|
||||
|
||||
function resolveServiceRefreshEnv(
|
||||
env: NodeJS.ProcessEnv,
|
||||
invocationCwd?: string,
|
||||
@@ -1186,7 +1148,6 @@ async function refreshGatewayServiceEnv(params: {
|
||||
jsonMode: boolean;
|
||||
invocationCwd?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
nodeRunner?: string;
|
||||
}): Promise<void> {
|
||||
const args = ["gateway", "install", "--force"];
|
||||
if (params.jsonMode) {
|
||||
@@ -1195,14 +1156,11 @@ async function refreshGatewayServiceEnv(params: {
|
||||
|
||||
const entrypoint = await resolveGatewayInstallEntrypoint(params.result.root);
|
||||
if (entrypoint) {
|
||||
const res = await runCommandWithTimeout(
|
||||
[params.nodeRunner ?? resolveNodeRunner(), entrypoint, ...args],
|
||||
{
|
||||
cwd: params.result.root,
|
||||
env: resolveUpdatedInstallCommandEnv(params.env ?? process.env, params.invocationCwd),
|
||||
timeoutMs: SERVICE_REFRESH_TIMEOUT_MS,
|
||||
},
|
||||
);
|
||||
const res = await runCommandWithTimeout([resolveNodeRunner(), entrypoint, ...args], {
|
||||
cwd: params.result.root,
|
||||
env: resolveUpdatedInstallCommandEnv(params.env ?? process.env, params.invocationCwd),
|
||||
timeoutMs: SERVICE_REFRESH_TIMEOUT_MS,
|
||||
});
|
||||
if (res.code === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -1225,7 +1183,6 @@ async function runUpdatedInstallGatewayRestart(params: {
|
||||
jsonMode: boolean;
|
||||
invocationCwd?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
nodeRunner?: string;
|
||||
}): Promise<boolean> {
|
||||
const entrypoint = await resolveGatewayInstallEntrypoint(params.result.root);
|
||||
if (!entrypoint) {
|
||||
@@ -1238,14 +1195,11 @@ async function runUpdatedInstallGatewayRestart(params: {
|
||||
if (params.jsonMode) {
|
||||
args.push("--json");
|
||||
}
|
||||
const res = await runCommandWithTimeout(
|
||||
[params.nodeRunner ?? resolveNodeRunner(), entrypoint, ...args],
|
||||
{
|
||||
cwd: params.result.root,
|
||||
env: resolveUpdatedInstallCommandEnv(params.env ?? process.env, params.invocationCwd),
|
||||
timeoutMs: SERVICE_REFRESH_TIMEOUT_MS,
|
||||
},
|
||||
);
|
||||
const res = await runCommandWithTimeout([resolveNodeRunner(), entrypoint, ...args], {
|
||||
cwd: params.result.root,
|
||||
env: resolveUpdatedInstallCommandEnv(params.env ?? process.env, params.invocationCwd),
|
||||
timeoutMs: SERVICE_REFRESH_TIMEOUT_MS,
|
||||
});
|
||||
if (res.code === 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -1264,18 +1218,11 @@ async function tryInstallShellCompletion(opts: {
|
||||
|
||||
const status = await checkShellCompletionStatus(CLI_NAME);
|
||||
|
||||
if (status.usesSlowPattern) {
|
||||
defaultRuntime.log(theme.muted("Upgrading shell completion to cached version..."));
|
||||
const cacheGenerated = await ensureCompletionCacheExists(CLI_NAME);
|
||||
if (cacheGenerated) {
|
||||
await installShellCompletionForUpdate(status.shell, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (status.profileInstalled && !status.cacheExists) {
|
||||
defaultRuntime.log(theme.muted("Regenerating shell completion cache..."));
|
||||
await ensureCompletionCacheExists(CLI_NAME);
|
||||
if (status.usesRetiredCache) {
|
||||
defaultRuntime.log(theme.muted("Updating shell completion profile..."));
|
||||
await installCompletion(status.shell, true, CLI_NAME, {
|
||||
retiredCachePath: status.retiredCachePath,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1299,22 +1246,7 @@ async function tryInstallShellCompletion(opts: {
|
||||
return;
|
||||
}
|
||||
|
||||
const cacheGenerated = await ensureCompletionCacheExists(CLI_NAME);
|
||||
if (!cacheGenerated) {
|
||||
defaultRuntime.log(theme.warn("Failed to generate completion cache."));
|
||||
return;
|
||||
}
|
||||
|
||||
await installShellCompletionForUpdate(status.shell, opts.skipPrompt);
|
||||
}
|
||||
}
|
||||
|
||||
async function installShellCompletionForUpdate(shell: string, yes: boolean): Promise<void> {
|
||||
try {
|
||||
await installCompletion(shell, yes, CLI_NAME);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
defaultRuntime.log(theme.warn(`Shell completion refresh failed: ${message}`));
|
||||
await installCompletion(status.shell, opts.skipPrompt, CLI_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1326,54 +1258,9 @@ async function tryRealpathOrResolve(value: string): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
function isNodeExecutable(value: string | undefined): boolean {
|
||||
const base = normalizeOptionalString(value ? path.basename(value) : undefined)?.toLowerCase();
|
||||
return base === "node" || base === "node.exe";
|
||||
}
|
||||
|
||||
function resolveManagedServiceNodeRunner(
|
||||
command: GatewayServiceCommandConfig | null,
|
||||
): string | undefined {
|
||||
const args = command?.programArguments;
|
||||
if (!args?.length) {
|
||||
return undefined;
|
||||
}
|
||||
const gatewayIndex = args.indexOf("gateway");
|
||||
if (gatewayIndex <= 1) {
|
||||
return undefined;
|
||||
}
|
||||
const runner = args[gatewayIndex - 2];
|
||||
return isNodeExecutable(runner) ? runner : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the node binary baked into the managed gateway service unit,
|
||||
* independent of any package root redirect. This detects when the user's
|
||||
* current PATH-resolved node differs from the service's baked node even
|
||||
* when the package root is the same.
|
||||
*/
|
||||
async function resolveManagedServiceNodeRunnerOverride(): Promise<string | undefined> {
|
||||
const command = await resolveGatewayService()
|
||||
.readCommand(process.env)
|
||||
.catch(() => null);
|
||||
const serviceNode = resolveManagedServiceNodeRunner(command);
|
||||
if (!serviceNode) {
|
||||
return undefined;
|
||||
}
|
||||
const currentNode = resolveNodeRunner();
|
||||
const [serviceNodeReal, currentNodeReal] = await Promise.all([
|
||||
tryRealpathOrResolve(serviceNode),
|
||||
tryRealpathOrResolve(currentNode),
|
||||
]);
|
||||
if (serviceNodeReal === currentNodeReal) {
|
||||
return undefined;
|
||||
}
|
||||
return serviceNode;
|
||||
}
|
||||
|
||||
async function resolveManagedServicePackageUpdateRoot(params: {
|
||||
root: string;
|
||||
}): Promise<ManagedServiceRootRedirect | null> {
|
||||
}): Promise<{ root: string; previousRoot: string } | null> {
|
||||
const command = await resolveGatewayService()
|
||||
.readCommand(process.env)
|
||||
.catch(() => null);
|
||||
@@ -1389,12 +1276,7 @@ async function resolveManagedServicePackageUpdateRoot(params: {
|
||||
if (currentRootReal === serviceRootReal) {
|
||||
return null;
|
||||
}
|
||||
const nodeRunner = resolveManagedServiceNodeRunner(command);
|
||||
return {
|
||||
root: serviceRoot,
|
||||
previousRoot: params.root,
|
||||
...(nodeRunner ? { nodeRunner } : {}),
|
||||
};
|
||||
return { root: serviceRoot, previousRoot: params.root };
|
||||
}
|
||||
|
||||
async function runPackageInstallUpdate(params: {
|
||||
@@ -1408,7 +1290,6 @@ async function runPackageInstallUpdate(params: {
|
||||
managedServiceEnv?: NodeJS.ProcessEnv;
|
||||
invocationCwd?: string;
|
||||
honorPackageRoot?: boolean;
|
||||
nodeRunner?: string;
|
||||
}): Promise<UpdateRunResult> {
|
||||
const manager = await resolveGlobalManager({
|
||||
root: params.root,
|
||||
@@ -1474,13 +1355,7 @@ async function runPackageInstallUpdate(params: {
|
||||
const candidateHostVersion = await readPackageVersion(verifiedPackageRoot);
|
||||
return await runUpdateStep({
|
||||
name: `${CLI_NAME} doctor`,
|
||||
argv: [
|
||||
params.nodeRunner ?? resolveNodeRunner(),
|
||||
entryPath,
|
||||
"doctor",
|
||||
"--non-interactive",
|
||||
"--fix",
|
||||
],
|
||||
argv: [resolveNodeRunner(), entryPath, "doctor", "--non-interactive", "--fix"],
|
||||
cwd: verifiedPackageRoot,
|
||||
env: {
|
||||
...resolvePostInstallDoctorEnv({
|
||||
@@ -1955,7 +1830,6 @@ async function maybeRestartService(params: {
|
||||
gatewayPort: number;
|
||||
restartScriptPath?: string | null;
|
||||
invocationCwd?: string;
|
||||
nodeRunner?: string;
|
||||
}): Promise<boolean> {
|
||||
const verifyRestartedGateway = async (expectedGatewayVersion: string | undefined) => {
|
||||
const restartAfterStaleCleanup = async () => {
|
||||
@@ -1965,7 +1839,6 @@ async function maybeRestartService(params: {
|
||||
jsonMode: Boolean(params.opts.json),
|
||||
invocationCwd: params.invocationCwd,
|
||||
env: params.serviceEnv,
|
||||
nodeRunner: params.nodeRunner,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -2079,7 +1952,6 @@ async function maybeRestartService(params: {
|
||||
jsonMode: Boolean(params.opts.json),
|
||||
invocationCwd: params.invocationCwd,
|
||||
env: params.serviceEnv,
|
||||
nodeRunner: params.nodeRunner,
|
||||
});
|
||||
} catch (err) {
|
||||
// Always log the refresh failure so callers can detect it (issue #56772).
|
||||
@@ -2128,7 +2000,6 @@ async function maybeRestartService(params: {
|
||||
jsonMode: Boolean(params.opts.json),
|
||||
invocationCwd: params.invocationCwd,
|
||||
env: params.serviceEnv,
|
||||
nodeRunner: params.nodeRunner,
|
||||
});
|
||||
} else if (
|
||||
!refreshedGatewayAlreadyHealthy &&
|
||||
@@ -2431,23 +2302,6 @@ function createUpdatedConfigSnapshot(
|
||||
};
|
||||
}
|
||||
|
||||
async function maybeRepairLegacyConfigForUpdateChannel(params: {
|
||||
configSnapshot: Awaited<ReturnType<typeof readConfigFileSnapshot>>;
|
||||
jsonMode: boolean;
|
||||
}): Promise<Awaited<ReturnType<typeof readConfigFileSnapshot>>> {
|
||||
if (params.configSnapshot.valid || params.configSnapshot.legacyIssues.length === 0) {
|
||||
return params.configSnapshot;
|
||||
}
|
||||
|
||||
const { repairLegacyConfigForUpdateChannel } =
|
||||
await import("../../commands/doctor/legacy-config-repair.js");
|
||||
const { snapshot, repaired } = await repairLegacyConfigForUpdateChannel(params);
|
||||
if (!params.jsonMode && repaired) {
|
||||
defaultRuntime.log(theme.muted("Migrated legacy config before changing update channel."));
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
async function writePostCorePluginUpdateResultFile(
|
||||
filePath: string | undefined,
|
||||
result: PostCorePluginUpdateResult,
|
||||
@@ -2722,7 +2576,6 @@ async function continuePostCoreUpdateInFreshProcess(params: {
|
||||
pluginInstallRecords: Record<string, PluginInstallRecord>;
|
||||
preUpdateConfig?: PreUpdateConfigRestoreInput;
|
||||
updateStartedAtMs: number;
|
||||
nodeRunner?: string;
|
||||
}): Promise<{ resumed: boolean; pluginUpdate?: PostCorePluginUpdateResult }> {
|
||||
const entryPath = await resolveGatewayInstallEntrypoint(params.root);
|
||||
if (!entryPath) {
|
||||
@@ -2746,13 +2599,12 @@ async function continuePostCoreUpdateInFreshProcess(params: {
|
||||
const resultPath = path.join(resultDir, "plugins.json");
|
||||
const installRecordsPath = path.join(resultDir, "plugin-install-records.json");
|
||||
const sourceConfigPath = path.join(resultDir, "source-config.json");
|
||||
const postCoreHostVersion = await readPackageVersion(params.root);
|
||||
|
||||
try {
|
||||
await writePostCorePluginInstallRecordsFile(installRecordsPath, params.pluginInstallRecords);
|
||||
await writePostCoreSourceConfigFile(sourceConfigPath, params.preUpdateConfig);
|
||||
const childStdio = resolvePostCoreUpdateChildStdio();
|
||||
const child = spawn(params.nodeRunner ?? resolveNodeRunner(), argv, {
|
||||
const child = spawn(resolveNodeRunner(), argv, {
|
||||
stdio: childStdio,
|
||||
env: {
|
||||
...stripGatewayServiceMarkerEnv(disableUpdatedPackageCompileCacheEnv(process.env)),
|
||||
@@ -2765,9 +2617,6 @@ async function continuePostCoreUpdateInFreshProcess(params: {
|
||||
[POST_CORE_UPDATE_RESULT_PATH_ENV]: resultPath,
|
||||
[POST_CORE_UPDATE_INSTALL_RECORDS_PATH_ENV]: installRecordsPath,
|
||||
[POST_CORE_UPDATE_STARTED_AT_ENV]: String(params.updateStartedAtMs),
|
||||
...(postCoreHostVersion === null
|
||||
? {}
|
||||
: { OPENCLAW_COMPATIBILITY_HOST_VERSION: postCoreHostVersion }),
|
||||
...(params.preUpdateConfig
|
||||
? { [POST_CORE_UPDATE_SOURCE_CONFIG_PATH_ENV]: sourceConfigPath }
|
||||
: {}),
|
||||
@@ -2973,12 +2822,7 @@ async function updateCommandInternal(opts: UpdateCommandOptions): Promise<void>
|
||||
return;
|
||||
}
|
||||
|
||||
process.env.OPENCLAW_COMPATIBILITY_HOST_VERSION = (await readPackageVersion(root)) ?? VERSION;
|
||||
|
||||
let postCoreConfigSnapshot = await readConfigFileSnapshot({
|
||||
skipPluginValidation: true,
|
||||
suppressFutureVersionWarning: true,
|
||||
});
|
||||
let postCoreConfigSnapshot = await readConfigFileSnapshot({ skipPluginValidation: true });
|
||||
const preUpdateSourceConfig = await readPostCorePreUpdateSourceConfig({
|
||||
sourceConfigPath: process.env[POST_CORE_UPDATE_SOURCE_CONFIG_PATH_ENV],
|
||||
currentSnapshot: postCoreConfigSnapshot,
|
||||
@@ -3087,54 +2931,18 @@ async function updateCommandInternal(opts: UpdateCommandOptions): Promise<void>
|
||||
let fallbackToLatest = false;
|
||||
let packageInstallSpec: string | null = null;
|
||||
let packageAlreadyCurrent = false;
|
||||
let managedServiceRootRedirect: ManagedServiceRootRedirect | null = null;
|
||||
// Resolved independently of the root redirect so it covers the common case
|
||||
// where the package root is the same but the user's PATH-resolved node
|
||||
// differs from the node baked into the managed gateway service unit.
|
||||
let managedServiceNodeRunner: string | undefined;
|
||||
let managedServiceRootRedirect: { root: string; previousRoot: string } | null = null;
|
||||
|
||||
if (updateInstallKind === "package") {
|
||||
managedServiceRootRedirect = await resolveManagedServicePackageUpdateRoot({ root });
|
||||
if (managedServiceRootRedirect) {
|
||||
root = managedServiceRootRedirect.root;
|
||||
managedServiceNodeRunner = managedServiceRootRedirect.nodeRunner;
|
||||
if (!opts.json) {
|
||||
defaultRuntime.log(
|
||||
theme.muted(
|
||||
`Targeting managed gateway service package root: ${managedServiceRootRedirect.root}`,
|
||||
),
|
||||
);
|
||||
defaultRuntime.log(
|
||||
theme.warn(
|
||||
`Shell OpenClaw root differs from the managed gateway service root: ${managedServiceRootRedirect.previousRoot}`,
|
||||
),
|
||||
);
|
||||
defaultRuntime.log(
|
||||
theme.muted(
|
||||
`After the update, make sure \`${CLI_NAME}\` on PATH resolves to the managed service root or reinstall the gateway service from the shell install you want to use.`,
|
||||
),
|
||||
);
|
||||
if (managedServiceNodeRunner) {
|
||||
defaultRuntime.log(
|
||||
theme.muted(`Managed gateway service Node: ${managedServiceNodeRunner}`),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Roots match but the node binary may still differ (e.g. user switched
|
||||
// nvm/fnm/brew node after gateway install).
|
||||
managedServiceNodeRunner = await resolveManagedServiceNodeRunnerOverride();
|
||||
if (managedServiceNodeRunner && !opts.json) {
|
||||
defaultRuntime.log(
|
||||
theme.warn(
|
||||
`Current Node (${resolveNodeRunner()}) differs from the managed gateway service Node (${managedServiceNodeRunner}).`,
|
||||
),
|
||||
);
|
||||
defaultRuntime.log(
|
||||
theme.muted(
|
||||
`Using the managed service Node for this update so the gateway can start after the upgrade.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3201,7 +3009,7 @@ async function updateCommandInternal(opts: UpdateCommandOptions): Promise<void>
|
||||
actions.push(`Run global package manager update with spec ${packageInstallSpec ?? tag}`);
|
||||
}
|
||||
actions.push("Run plugin update sync after core update");
|
||||
actions.push("Refresh shell completion cache (if needed)");
|
||||
actions.push("Refresh shell completion profile (if needed)");
|
||||
actions.push(
|
||||
shouldRestart
|
||||
? "Restart gateway service and run doctor checks"
|
||||
@@ -3286,7 +3094,6 @@ async function updateCommandInternal(opts: UpdateCommandOptions): Promise<void>
|
||||
const runtimePreflightError = await resolvePackageRuntimePreflightError({
|
||||
tag,
|
||||
timeoutMs,
|
||||
nodeRunner: managedServiceNodeRunner,
|
||||
});
|
||||
if (runtimePreflightError) {
|
||||
defaultRuntime.error(runtimePreflightError);
|
||||
@@ -3354,9 +3161,7 @@ async function updateCommandInternal(opts: UpdateCommandOptions): Promise<void>
|
||||
jsonMode: Boolean(opts.json),
|
||||
managedServiceEnv: prePackageServiceStop?.serviceEnv,
|
||||
invocationCwd,
|
||||
honorPackageRoot:
|
||||
managedServiceRootRedirect !== null || managedServiceNodeRunner !== undefined,
|
||||
nodeRunner: managedServiceNodeRunner,
|
||||
honorPackageRoot: managedServiceRootRedirect !== null,
|
||||
})
|
||||
: await runGitUpdate({
|
||||
root,
|
||||
@@ -3485,7 +3290,6 @@ async function updateCommandInternal(opts: UpdateCommandOptions): Promise<void>
|
||||
opts,
|
||||
pluginInstallRecords: preUpdatePluginInstallRecords,
|
||||
updateStartedAtMs: startedAt,
|
||||
nodeRunner: managedServiceNodeRunner,
|
||||
preUpdateConfig: configSnapshot.valid
|
||||
? {
|
||||
sourceConfig: configSnapshot.sourceConfig,
|
||||
@@ -3594,7 +3398,6 @@ async function updateCommandInternal(opts: UpdateCommandOptions): Promise<void>
|
||||
}
|
||||
}
|
||||
|
||||
await tryWriteCompletionCache(postUpdateRoot, Boolean(opts.json));
|
||||
await tryInstallShellCompletion({
|
||||
jsonMode: Boolean(opts.json),
|
||||
skipPrompt: Boolean(opts.yes),
|
||||
@@ -3615,7 +3418,6 @@ async function updateCommandInternal(opts: UpdateCommandOptions): Promise<void>
|
||||
gatewayPort,
|
||||
restartScriptPath,
|
||||
invocationCwd,
|
||||
nodeRunner: managedServiceNodeRunner,
|
||||
});
|
||||
if (!restartOk) {
|
||||
await markControlPlaneUpdateRestartSentinelFailureBestEffort({
|
||||
|
||||
Reference in New Issue
Block a user