CLI: support package-manager installs from GitHub main (#47630)

* CLI: resolve package-manager main install specs

* CLI: skip registry resolution for raw package specs

* CLI: support main package target updates

* CLI: document package update specs in help

* Tests: cover package install spec resolution

* Tests: cover npm main-package updates

* Tests: cover update --tag main

* Installer: support main package targets

* Installer: support main package targets on Windows

* Docs: document package-manager main updates

* Docs: document installer main targets

* Docs: document npm and pnpm main installs

* Docs: document update --tag main

* Changelog: note package-manager main installs

* Update src/infra/update-global.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Vincent Koc
2026-03-15 14:18:12 -07:00
committed by GitHub
parent 3735156766
commit 5a7aba94a2
14 changed files with 320 additions and 63 deletions

View File

@@ -10,6 +10,7 @@ import { trimLogTail } from "../../infra/restart-sentinel.js";
import { parseSemver } from "../../infra/runtime-guard.js";
import { fetchNpmTagVersion } from "../../infra/update-check.js";
import {
canResolveRegistryVersionForPackageTarget,
detectGlobalInstallManagerByPresence,
detectGlobalInstallManagerForRoot,
type CommandRunner,
@@ -77,6 +78,9 @@ export async function resolveTargetVersion(
tag: string,
timeoutMs?: number,
): Promise<string | null> {
if (!canResolveRegistryVersionForPackageTarget(tag)) {
return null;
}
const direct = normalizeVersionTag(tag);
if (direct) {
return direct;

View File

@@ -24,6 +24,7 @@ import {
checkUpdateStatus,
} from "../../infra/update-check.js";
import {
canResolveRegistryVersionForPackageTarget,
createGlobalInstallEnv,
cleanupGlobalRenameDirs,
globalInstallArgs,
@@ -731,22 +732,31 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
let targetVersion: string | null = null;
let downgradeRisk = false;
let fallbackToLatest = false;
let packageInstallSpec: string | null = null;
if (updateInstallKind !== "git") {
currentVersion = switchToPackage ? null : await readPackageVersion(root);
targetVersion = explicitTag
? await resolveTargetVersion(tag, timeoutMs)
: await resolveNpmChannelTag({ channel, timeoutMs }).then((resolved) => {
tag = resolved.tag;
fallbackToLatest = channel === "beta" && resolved.tag === "latest";
return resolved.version;
});
if (explicitTag) {
targetVersion = await resolveTargetVersion(tag, timeoutMs);
} else {
targetVersion = await resolveNpmChannelTag({ channel, timeoutMs }).then((resolved) => {
tag = resolved.tag;
fallbackToLatest = channel === "beta" && resolved.tag === "latest";
return resolved.version;
});
}
const cmp =
currentVersion && targetVersion ? compareSemverStrings(currentVersion, targetVersion) : null;
downgradeRisk =
canResolveRegistryVersionForPackageTarget(tag) &&
!fallbackToLatest &&
currentVersion != null &&
(targetVersion == null || (cmp != null && cmp > 0));
packageInstallSpec = resolveGlobalInstallSpec({
packageName: DEFAULT_PACKAGE_NAME,
tag,
env: process.env,
});
}
if (opts.dryRun) {
@@ -772,7 +782,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
} else if (updateInstallKind === "git") {
actions.push(`Run git update flow on channel ${channel} (fetch/rebase/build/doctor)`);
} else {
actions.push(`Run global package manager update with spec openclaw@${tag}`);
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)");
@@ -789,6 +799,9 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
if (fallbackToLatest) {
notes.push("Beta channel resolves to latest for this run (fallback).");
}
if (explicitTag && !canResolveRegistryVersionForPackageTarget(tag)) {
notes.push("Non-registry package specs skip npm version lookup and downgrade previews.");
}
printDryRunPreview(
{
@@ -803,7 +816,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
requestedChannel,
storedChannel,
effectiveChannel: channel,
tag,
tag: packageInstallSpec ?? tag,
currentVersion,
targetVersion,
downgradeRisk,