diff --git a/docs/cli/update.md b/docs/cli/update.md index ad80ba6f6cbf..14ee00afd1ca 100644 --- a/docs/cli/update.md +++ b/docs/cli/update.md @@ -26,7 +26,6 @@ openclaw update --channel extended-stable openclaw update --channel beta openclaw update --channel dev openclaw update --tag beta -openclaw update --tag main openclaw update --dry-run openclaw update --no-restart openclaw update --yes @@ -40,16 +39,16 @@ launcher scripts). ## Options -| Flag | Description | -| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--no-restart` | Skip restarting the Gateway service after a successful update. Package-manager updates that do restart verify the restarted service reports the expected version before the command succeeds. | -| `--channel ` | Set the update channel and persist it after core update success. Extended-stable is package-only. | -| `--tag ` | Override the package target for this update only. It cannot be combined with an effective `extended-stable` channel, whose verified exact target is mandatory. For other package installs, `main` maps to `github:openclaw/openclaw#main`; GitHub/git source specs are packed into a temporary tarball before the staged global npm install. | -| `--dry-run` | Preview planned actions (channel/tag/target/restart flow) without writing config, installing, syncing plugins, or restarting. | -| `--json` | Print machine-readable `UpdateRunResult` JSON. Includes `postUpdate.plugins.warnings` when a managed plugin needs repair, beta-channel plugin fallback details, and `postUpdate.plugins.integrityDrifts` when npm plugin artifact drift is detected during post-update sync. | -| `--timeout ` | Per-step timeout. Default `1800`. | -| `--yes` | Skip confirmation prompts (for example downgrade confirmation). | -| `--acknowledge-clawhub-risk` | Allow post-update plugin sync to continue past community ClawHub trust warnings without an interactive prompt. Without it, risky community releases are skipped and left unchanged when OpenClaw cannot prompt. Official ClawHub packages and bundled plugin sources bypass this prompt. | +| Flag | Description | +| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--no-restart` | Skip restarting the Gateway service after a successful update. Package-manager updates that do restart verify the restarted service reports the expected version before the command succeeds. | +| `--channel ` | Set the update channel and persist it after core update success. Extended-stable is package-only. | +| `--tag ` | Override the package target for this update only. It cannot be combined with an effective `extended-stable` channel, whose verified exact target is mandatory. Package installs reject the `main` shorthand; use `--channel dev` for the supported checkout and build flow. Other explicit package specs keep their package-manager behavior. | +| `--dry-run` | Preview planned actions (channel/tag/target/restart flow) without writing config, installing, syncing plugins, or restarting. | +| `--json` | Print machine-readable `UpdateRunResult` JSON. Includes `postUpdate.plugins.warnings` when a managed plugin needs repair, beta-channel plugin fallback details, and `postUpdate.plugins.integrityDrifts` when npm plugin artifact drift is detected during post-update sync. | +| `--timeout ` | Per-step timeout. Default `1800`. | +| `--yes` | Skip confirmation prompts (for example downgrade confirmation). | +| `--acknowledge-clawhub-risk` | Allow post-update plugin sync to continue past community ClawHub trust warnings without an interactive prompt. Without it, risky community releases are skipped and left unchanged when OpenClaw cannot prompt. Official ClawHub packages and bundled plugin sources bypass this prompt. | There is no `--verbose` flag. Use `--dry-run` to preview planned actions, `--json` for machine-readable results, and `openclaw update status --json` diff --git a/docs/install/development-channels.md b/docs/install/development-channels.md index def7a3f07c23..c3ccc33ba131 100644 --- a/docs/install/development-channels.md +++ b/docs/install/development-channels.md @@ -75,8 +75,6 @@ openclaw update --channel dev # Install a specific npm package spec openclaw update --tag openclaw@2026.4.1-beta.1 -# Install from GitHub main once without persisting the channel -openclaw update --tag main ``` Notes: @@ -84,13 +82,11 @@ Notes: - `--tag` applies to **package (npm) installs only**; git installs ignore it. - The tag is not persisted; the next `openclaw update` uses the configured channel. -- `--tag main` maps to the npm-compatible spec `github:openclaw/openclaw#main` - for that one run. For a persistent moving `main` install, use +- The `--tag main` shorthand is rejected for package installs because the + workspace checkout is not a self-contained package artifact. Use `openclaw update --channel dev` (package installs switch to a git checkout) or reinstall with the installer's git method: `curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method git --version main`. - The npm install path rejects GitHub/git source targets outright and points - you at the git method instead. - Downgrade protection: if the target version is older than the current version, OpenClaw prompts for confirmation (skip with `--yes`). - Extended-stable always uses its verified exact package target. It is not a diff --git a/docs/install/updating.md b/docs/install/updating.md index 5f26c33cd4f0..b2442e65f7ee 100644 --- a/docs/install/updating.md +++ b/docs/install/updating.md @@ -60,9 +60,11 @@ OpenClaw cannot safely distinguish an old automatic pin from a user pin; run `openclaw plugins update @openclaw/name` once on the extended-stable channel to opt that plugin back into exact-core tracking. -`--channel dev` gives a persistent moving GitHub `main` checkout. For a one-off -package update, `--tag main` maps to the `github:openclaw/openclaw#main` package -spec and installs it directly through the target package manager (npm/pnpm/bun). +`--channel dev` gives a persistent moving GitHub `main` checkout. Package +installs reject the `--tag main` shorthand because the workspace checkout is +not a self-contained package artifact. Use `openclaw update --channel dev` to +switch to the supported checkout and build flow. Other explicit package specs +keep their package-manager behavior. For managed plugins, a missing beta release is a warning, not a failure: the core update can still succeed while a plugin falls back to its recorded diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index f6ca33a0f2b4..471773212a7d 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -4355,12 +4355,6 @@ describe("update-cli", () => { packageSpec: undefined, expectedSpec: "openclaw@9999.0.0", }, - { - name: "main shorthand", - options: { yes: true, tag: "main" }, - packageSpec: undefined, - expectedSpec: "github:openclaw/openclaw#main", - }, { name: "explicit git package spec", options: { yes: true, tag: "github:openclaw/openclaw#main" }, @@ -4429,6 +4423,33 @@ describe("update-cli", () => { }, ); + it.each([ + { name: "real run", options: { yes: true, json: true, tag: "main" } }, + { name: "normalized alias", options: { yes: true, json: true, tag: "openclaw@main" } }, + { + name: "dry-run", + options: { dryRun: true, json: true, tag: "main", yes: true }, + }, + ] as const)("refuses --tag main before package resolution: $name", async ({ options }) => { + mockPackageInstallStatus(createCaseDir("openclaw-update-main-refusal")); + + await updateCommand(options); + + const result = lastWriteJsonCall() as UpdateRunResult | undefined; + expect(result).toMatchObject({ + status: "error", + reason: "unsupported-package-target", + steps: [], + }); + expect(packageInstallCommandCall()).toBeUndefined(); + expectNoSideEffects(resolveGlobalManager, replaceConfigFile, runGatewayUpdate); + if ("dryRun" in options && options.dryRun) { + expect(cleanupStaleManagedServiceUpdateHandoffs).not.toHaveBeenCalled(); + } + expect(defaultRuntime.exit).toHaveBeenCalledWith(1); + expect(getErrorOutput()).toContain("openclaw update --channel dev"); + }); + it("fails package updates when the installed correction version does not match the requested target", async () => { const tempDir = createCaseDir("openclaw-update"); const nodeModules = path.join(tempDir, "node_modules"); diff --git a/src/cli/update-cli.ts b/src/cli/update-cli.ts index ba0cc39647ce..a07563f6a23e 100644 --- a/src/cli/update-cli.ts +++ b/src/cli/update-cli.ts @@ -165,7 +165,6 @@ export function registerUpdateCli(program: Command) { ["openclaw update --channel beta", "Switch to beta channel (git + npm)"], ["openclaw update --channel dev", "Switch to dev channel (git + npm)"], ["openclaw update --tag beta", "One-off update to a dist-tag or version"], - ["openclaw update --tag main", "One-off package update from GitHub main"], ["openclaw update --dry-run", "Preview actions without changing anything"], ["openclaw update --no-restart", "Update without restarting the service"], ["openclaw update --json", "Output result as JSON"], @@ -187,7 +186,7 @@ ${theme.heading("Switch channels:")} - Use --channel stable|extended-stable|beta|dev to persist the update channel in config - Run openclaw update status to see the active channel and source - Use --tag for a one-off package update without persisting - - Use --tag main for a one-off package update from GitHub main + - Use --channel dev for the moving GitHub main checkout; package installs reject --tag main ${theme.heading("Non-interactive:")} - Use --yes to accept downgrade prompts diff --git a/src/cli/update-cli/update-command-post-core.ts b/src/cli/update-cli/update-command-post-core.ts index d7c6daf3a2a5..9addbd454eba 100644 --- a/src/cli/update-cli/update-command-post-core.ts +++ b/src/cli/update-cli/update-command-post-core.ts @@ -106,7 +106,8 @@ export async function reportPreMutationUpdateFailure(params: { reason: | ExtendedStableFailureReason | typeof EXTENDED_STABLE_TAG_UNSUPPORTED_REASON - | "npm lifecycle policy preflight"; + | "npm lifecycle policy preflight" + | "unsupported-package-target"; message?: string; opts: UpdateCommandOptions; controlPlaneUpdateSentinelMeta: ControlPlaneUpdateSentinelMetaFile["meta"] | null; diff --git a/src/cli/update-cli/update-command.ts b/src/cli/update-cli/update-command.ts index c2a79e9d198e..f66f4acf1a3c 100644 --- a/src/cli/update-cli/update-command.ts +++ b/src/cli/update-cli/update-command.ts @@ -289,11 +289,17 @@ async function updateCommandInternal( } const explicitTag = normalizeTag(opts.tag); - if (channel === "extended-stable" && explicitTag) { + const unsupportedMainTag = updateInstallKind === "package" && explicitTag === "main"; + if ((channel === "extended-stable" && explicitTag) || unsupportedMainTag) { await reportPreMutationUpdateFailure({ root, installKind: updateInstallKind, - reason: EXTENDED_STABLE_TAG_UNSUPPORTED_REASON, + reason: unsupportedMainTag + ? "unsupported-package-target" + : EXTENDED_STABLE_TAG_UNSUPPORTED_REASON, + message: unsupportedMainTag + ? "`--tag main` cannot update a package install. Run `openclaw update --channel dev` to switch to the supported Git checkout and build flow." + : undefined, opts, controlPlaneUpdateSentinelMeta, });