Files
Moltbot/src/version.ts
Liu Xiaopai ae29842158 Gateway: fix stale self version in status output (#32655)
Merged via squash.

Prepared head SHA: b9675d1f90ef0eabb7e68c24a72d4b2fb27def22
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-03 02:41:52 -05:00

129 lines
3.5 KiB
TypeScript

import { createRequire } from "node:module";
declare const __OPENCLAW_VERSION__: string | undefined;
const CORE_PACKAGE_NAME = "openclaw";
const PACKAGE_JSON_CANDIDATES = [
"../package.json",
"../../package.json",
"../../../package.json",
"./package.json",
] as const;
const BUILD_INFO_CANDIDATES = [
"../build-info.json",
"../../build-info.json",
"./build-info.json",
] as const;
function readVersionFromJsonCandidates(
moduleUrl: string,
candidates: readonly string[],
opts: { requirePackageName?: boolean } = {},
): string | null {
try {
const require = createRequire(moduleUrl);
for (const candidate of candidates) {
try {
const parsed = require(candidate) as { name?: string; version?: string };
const version = parsed.version?.trim();
if (!version) {
continue;
}
if (opts.requirePackageName && parsed.name !== CORE_PACKAGE_NAME) {
continue;
}
return version;
} catch {
// ignore missing or unreadable candidate
}
}
return null;
} catch {
return null;
}
}
function firstNonEmpty(...values: Array<string | undefined>): string | undefined {
for (const value of values) {
const trimmed = value?.trim();
if (trimmed) {
return trimmed;
}
}
return undefined;
}
export function readVersionFromPackageJsonForModuleUrl(moduleUrl: string): string | null {
return readVersionFromJsonCandidates(moduleUrl, PACKAGE_JSON_CANDIDATES, {
requirePackageName: true,
});
}
export function readVersionFromBuildInfoForModuleUrl(moduleUrl: string): string | null {
return readVersionFromJsonCandidates(moduleUrl, BUILD_INFO_CANDIDATES);
}
export function resolveVersionFromModuleUrl(moduleUrl: string): string | null {
return (
readVersionFromPackageJsonForModuleUrl(moduleUrl) ||
readVersionFromBuildInfoForModuleUrl(moduleUrl)
);
}
export function resolveBinaryVersion(params: {
moduleUrl: string;
injectedVersion?: string;
bundledVersion?: string;
fallback?: string;
}): string {
return (
firstNonEmpty(params.injectedVersion) ||
resolveVersionFromModuleUrl(params.moduleUrl) ||
firstNonEmpty(params.bundledVersion) ||
params.fallback ||
"0.0.0"
);
}
export type RuntimeVersionEnv = {
[key: string]: string | undefined;
};
export const RUNTIME_SERVICE_VERSION_FALLBACK = "unknown";
export function resolveUsableRuntimeVersion(version: string | undefined): string | undefined {
const trimmed = version?.trim();
// "0.0.0" is the resolver's hard fallback when module metadata cannot be read.
// Prefer explicit service/package markers in that edge case.
if (!trimmed || trimmed === "0.0.0") {
return undefined;
}
return trimmed;
}
export function resolveRuntimeServiceVersion(
env: RuntimeVersionEnv = process.env as RuntimeVersionEnv,
fallback = RUNTIME_SERVICE_VERSION_FALLBACK,
): string {
const runtimeVersion = resolveUsableRuntimeVersion(VERSION);
return (
firstNonEmpty(
env["OPENCLAW_VERSION"],
runtimeVersion,
env["OPENCLAW_SERVICE_VERSION"],
env["npm_package_version"],
) ?? fallback
);
}
// Single source of truth for the current OpenClaw version.
// - Embedded/bundled builds: injected define or env var.
// - Dev/npm builds: package.json.
export const VERSION = resolveBinaryVersion({
moduleUrl: import.meta.url,
injectedVersion: typeof __OPENCLAW_VERSION__ === "string" ? __OPENCLAW_VERSION__ : undefined,
bundledVersion: process.env.OPENCLAW_BUNDLED_VERSION,
});