mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-13 11:00:50 +00:00
152 lines
4.2 KiB
TypeScript
152 lines
4.2 KiB
TypeScript
import { confirm, isCancel } from "@clack/prompts";
|
|
import { readConfigFileSnapshot } from "../../config/config.js";
|
|
import {
|
|
formatUpdateChannelLabel,
|
|
normalizeUpdateChannel,
|
|
resolveEffectiveUpdateChannel,
|
|
} from "../../infra/update-channels.js";
|
|
import { checkUpdateStatus } from "../../infra/update-check.js";
|
|
import { defaultRuntime } from "../../runtime.js";
|
|
import { selectStyled } from "../../terminal/prompt-select-styled.js";
|
|
import { stylePromptMessage } from "../../terminal/prompt-style.js";
|
|
import { theme } from "../../terminal/theme.js";
|
|
import { pathExists } from "../../utils.js";
|
|
import {
|
|
isEmptyDir,
|
|
isGitCheckout,
|
|
parseTimeoutMsOrExit,
|
|
resolveGitInstallDir,
|
|
resolveUpdateRoot,
|
|
type UpdateWizardOptions,
|
|
} from "./shared.js";
|
|
import { updateCommand } from "./update-command.js";
|
|
|
|
export async function updateWizardCommand(opts: UpdateWizardOptions = {}): Promise<void> {
|
|
if (!process.stdin.isTTY) {
|
|
defaultRuntime.error(
|
|
"Update wizard requires a TTY. Use `openclaw update --channel <stable|beta|dev>` instead.",
|
|
);
|
|
defaultRuntime.exit(1);
|
|
return;
|
|
}
|
|
|
|
const timeoutMs = parseTimeoutMsOrExit(opts.timeout);
|
|
if (timeoutMs === null) {
|
|
return;
|
|
}
|
|
|
|
const root = await resolveUpdateRoot();
|
|
const [updateStatus, configSnapshot] = await Promise.all([
|
|
checkUpdateStatus({
|
|
root,
|
|
timeoutMs: timeoutMs ?? 3500,
|
|
fetchGit: false,
|
|
includeRegistry: false,
|
|
}),
|
|
readConfigFileSnapshot(),
|
|
]);
|
|
|
|
const configChannel = configSnapshot.valid
|
|
? normalizeUpdateChannel(configSnapshot.config.update?.channel)
|
|
: null;
|
|
const channelInfo = resolveEffectiveUpdateChannel({
|
|
configChannel,
|
|
installKind: updateStatus.installKind,
|
|
git: updateStatus.git
|
|
? { tag: updateStatus.git.tag, branch: updateStatus.git.branch }
|
|
: undefined,
|
|
});
|
|
const channelLabel = formatUpdateChannelLabel({
|
|
channel: channelInfo.channel,
|
|
source: channelInfo.source,
|
|
gitTag: updateStatus.git?.tag ?? null,
|
|
gitBranch: updateStatus.git?.branch ?? null,
|
|
});
|
|
|
|
const pickedChannel = await selectStyled({
|
|
message: "Update channel",
|
|
options: [
|
|
{
|
|
value: "keep",
|
|
label: `Keep current (${channelInfo.channel})`,
|
|
hint: channelLabel,
|
|
},
|
|
{
|
|
value: "stable",
|
|
label: "Stable",
|
|
hint: "Tagged releases (npm latest)",
|
|
},
|
|
{
|
|
value: "beta",
|
|
label: "Beta",
|
|
hint: "Prereleases (npm beta)",
|
|
},
|
|
{
|
|
value: "dev",
|
|
label: "Dev",
|
|
hint: "Git main",
|
|
},
|
|
],
|
|
initialValue: "keep",
|
|
});
|
|
|
|
if (isCancel(pickedChannel)) {
|
|
defaultRuntime.log(theme.muted("Update cancelled."));
|
|
defaultRuntime.exit(0);
|
|
return;
|
|
}
|
|
|
|
const requestedChannel = pickedChannel === "keep" ? null : pickedChannel;
|
|
|
|
if (requestedChannel === "dev" && updateStatus.installKind !== "git") {
|
|
const gitDir = resolveGitInstallDir();
|
|
const hasGit = await isGitCheckout(gitDir);
|
|
if (!hasGit) {
|
|
const dirExists = await pathExists(gitDir);
|
|
if (dirExists) {
|
|
const empty = await isEmptyDir(gitDir);
|
|
if (!empty) {
|
|
defaultRuntime.error(
|
|
`OPENCLAW_GIT_DIR points at a non-git directory: ${gitDir}. Set OPENCLAW_GIT_DIR to an empty folder or an openclaw checkout.`,
|
|
);
|
|
defaultRuntime.exit(1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const ok = await confirm({
|
|
message: stylePromptMessage(
|
|
`Create a git checkout at ${gitDir}? (override via OPENCLAW_GIT_DIR)`,
|
|
),
|
|
initialValue: true,
|
|
});
|
|
if (isCancel(ok) || !ok) {
|
|
defaultRuntime.log(theme.muted("Update cancelled."));
|
|
defaultRuntime.exit(0);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
const restart = await confirm({
|
|
message: stylePromptMessage("Restart the gateway service after update?"),
|
|
initialValue: true,
|
|
});
|
|
if (isCancel(restart)) {
|
|
defaultRuntime.log(theme.muted("Update cancelled."));
|
|
defaultRuntime.exit(0);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await updateCommand({
|
|
channel: requestedChannel ?? undefined,
|
|
restart: Boolean(restart),
|
|
timeout: opts.timeout,
|
|
});
|
|
} catch (err) {
|
|
defaultRuntime.error(String(err));
|
|
defaultRuntime.exit(1);
|
|
}
|
|
}
|