




















1+import { execFileSync } from "node:child_process";
2+import { readFileSync } from "node:fs";
3+import { fileURLToPath } from "node:url";
4+import { parseReleaseVersion } from "./npm-publish-plan.mjs";
5+6+function parseVersion(version) {
7+return parseReleaseVersion(String(version ?? "").trim()) ?? undefined;
8+}
9+10+export function compareOpenClawVersions(leftVersion, rightVersion) {
11+const left = parseVersion(leftVersion);
12+const right = parseVersion(rightVersion);
13+if (!left || !right) {
14+throw new Error(`cannot compare OpenClaw versions: ${leftVersion} ${rightVersion}`);
15+}
16+for (const key of ["year", "month", "patch"]) {
17+const delta = left[key] - right[key];
18+if (delta !== 0) {
19+return delta;
20+}
21+}
22+const channelRank = { alpha: 0, beta: 1, stable: 2 };
23+const channelDelta = channelRank[left.channel] - channelRank[right.channel];
24+if (channelDelta !== 0) {
25+return channelDelta;
26+}
27+if (left.channel === "alpha") {
28+return (left.alphaNumber ?? 0) - (right.alphaNumber ?? 0);
29+}
30+if (left.channel === "beta") {
31+return (left.betaNumber ?? 0) - (right.betaNumber ?? 0);
32+}
33+return (left.correctionNumber ?? 0) - (right.correctionNumber ?? 0);
34+}
35+36+function normalizePublishedVersions(publishedVersions) {
37+return [...new Set(publishedVersions.map((version) => String(version).trim()).filter(Boolean))]
38+.filter((version) => parseVersion(version))
39+.toSorted((left, right) => compareOpenClawVersions(right, left));
40+}
41+42+export function resolveDefaultReleaseUpgradeBaseline(candidateVersion, publishedVersions) {
43+const candidate = parseVersion(candidateVersion);
44+if (!candidate) {
45+throw new Error(`invalid candidate OpenClaw version: ${candidateVersion}`);
46+}
47+48+const versions = normalizePublishedVersions(publishedVersions);
49+const older = versions.find((version) => compareOpenClawVersions(version, candidate.version) < 0);
50+if (older) {
51+return `openclaw@${older}`;
52+}
53+54+const same = versions.find(
55+(version) => compareOpenClawVersions(version, candidate.version) === 0,
56+);
57+if (same) {
58+return `openclaw@${same}`;
59+}
60+61+throw new Error(`no published OpenClaw baseline is <= candidate ${candidate.version}`);
62+}
63+64+function parseArgs(argv) {
65+const args = new Map();
66+for (let index = 0; index < argv.length; index += 1) {
67+const arg = argv[index];
68+if (!arg.startsWith("--")) {
69+throw new Error(`unexpected argument: ${arg}`);
70+}
71+const key = arg.slice(2);
72+const value = argv[index + 1];
73+if (value === undefined || value.startsWith("--")) {
74+throw new Error(`missing value for --${key}`);
75+}
76+args.set(key, value);
77+index += 1;
78+}
79+return args;
80+}
81+82+function readPublishedVersions(args) {
83+const versionsJson = args.get("versions-json");
84+if (versionsJson) {
85+const parsed = JSON.parse(readFileSync(versionsJson, "utf8"));
86+if (!Array.isArray(parsed)) {
87+throw new Error(`npm versions list must be a JSON array: ${versionsJson}`);
88+}
89+return parsed;
90+}
91+const raw = execFileSync("npm", ["view", "openclaw", "versions", "--json", "--silent"], {
92+encoding: "utf8",
93+stdio: ["ignore", "pipe", "inherit"],
94+});
95+const parsed = JSON.parse(raw);
96+if (!Array.isArray(parsed)) {
97+throw new Error("npm returned a non-array openclaw versions payload");
98+}
99+return parsed;
100+}
101+102+const isMain = process.argv[1] ? fileURLToPath(import.meta.url) === process.argv[1] : false;
103+104+if (isMain) {
105+const args = parseArgs(process.argv.slice(2));
106+const candidateVersion = args.get("candidate-version");
107+if (!candidateVersion) {
108+throw new Error("--candidate-version is required");
109+}
110+const baseline = resolveDefaultReleaseUpgradeBaseline(
111+candidateVersion,
112+readPublishedVersions(args),
113+);
114+process.stdout.write(`${baseline}\n`);
115+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。