openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
216 lines (215 loc) • 9.09 kB
JavaScript
import { i as formatErrorMessage } from "./errors-BXgSefBE.js";
import { p as resolveUserPath } from "./utils-CCC-BEJH.js";
import { t as WizardCancelledError } from "./prompts-E0FayNjJ.js";
import { n as t } from "./i18n-7g_f4oaD.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/wizard/setup.migration-import.ts
const MEANINGFUL_CONFIG_IGNORED_KEYS = new Set(["$schema", "meta"]);
const MEANINGFUL_WORKSPACE_ENTRIES = [
"AGENTS.md",
"SOUL.md",
"USER.md",
"IDENTITY.md",
"MEMORY.md",
"skills"
];
const MEANINGFUL_STATE_ENTRIES = [
"credentials",
"sessions",
"agents"
];
let migrationProviderRuntimeModulePromise = null;
let migrationContextModulePromise = null;
let configPathsModulePromise = null;
const loadMigrationProviderRuntimeModule = async () => {
migrationProviderRuntimeModulePromise ??= import("./migration-provider-runtime-DRWJd65t.js");
return await migrationProviderRuntimeModulePromise;
};
const loadMigrationContextModule = async () => {
migrationContextModulePromise ??= import("./context-lWhWMFgy.js");
return await migrationContextModulePromise;
};
const loadConfigPathsModule = async () => {
configPathsModulePromise ??= import("./paths-BiLl1Sm1.js");
return await configPathsModulePromise;
};
async function exists(candidate) {
try {
await fs.access(candidate);
return true;
} catch {
return false;
}
}
async function hasDirectoryEntries(candidate) {
try {
return (await fs.readdir(candidate)).length > 0;
} catch {
return false;
}
}
function hasMeaningfulConfig(config) {
return Object.keys(config).some((key) => !MEANINGFUL_CONFIG_IGNORED_KEYS.has(key));
}
async function inspectSetupMigrationFreshness(params) {
const reasons = [];
if (hasMeaningfulConfig(params.baseConfig)) reasons.push("existing config values are loaded");
for (const entry of MEANINGFUL_WORKSPACE_ENTRIES) if (await exists(path.join(params.workspaceDir, entry))) reasons.push(`workspace ${entry} exists`);
for (const entry of MEANINGFUL_STATE_ENTRIES) if (await hasDirectoryEntries(path.join(params.stateDir, entry))) reasons.push(`state ${entry}/ exists`);
return {
fresh: reasons.length === 0,
reasons
};
}
function assertFreshSetupMigrationTarget(freshness) {
if (freshness.fresh || process.env.OPENCLAW_MIGRATION_EXISTING_IMPORT === "1") return;
throw new Error([
"Migration import during onboarding requires a fresh OpenClaw setup.",
"Create a fresh setup or reset config, credentials, sessions, and workspace before importing.",
"Backup plus overwrite/merge imports are feature-gated for now.",
"Existing setup:",
...freshness.reasons.map((reason) => `- ${reason}`)
].join("\n"));
}
async function detectSetupMigrationSources(params) {
const [{ ensureStandaloneMigrationProviderRegistryLoaded, resolvePluginMigrationProviders }, { createMigrationLogger }, { resolveStateDir }] = await Promise.all([
loadMigrationProviderRuntimeModule(),
loadMigrationContextModule(),
loadConfigPathsModule()
]);
ensureStandaloneMigrationProviderRegistryLoaded({ cfg: params.config });
const stateDir = resolveStateDir();
const logger = createMigrationLogger(params.runtime);
const detections = [];
for (const provider of resolvePluginMigrationProviders({ cfg: params.config })) {
if (!provider.detect) continue;
try {
const detection = await provider.detect({
config: params.config,
stateDir,
logger
});
if (detection.found) detections.push({
providerId: provider.id,
label: detection.label ?? provider.label,
...detection.source ? { source: detection.source } : {},
...detection.message ? { message: detection.message } : {}
});
} catch (error) {
logger.debug?.(`Migration provider ${provider.id} detection failed: ${formatErrorMessage(error)}`);
}
}
return detections;
}
function resolveImportSourceDefault(params) {
const detected = params.detections.find((detection) => detection.providerId === params.providerId);
if (detected?.source) return detected.source;
return params.providerId === "hermes" ? "~/.hermes" : "";
}
async function selectSetupMigrationProvider(params) {
const { ensureStandaloneMigrationProviderRegistryLoaded, resolvePluginMigrationProvider, resolvePluginMigrationProviders } = await loadMigrationProviderRuntimeModule();
ensureStandaloneMigrationProviderRegistryLoaded({ cfg: params.baseConfig });
const providers = resolvePluginMigrationProviders({ cfg: params.baseConfig });
if (providers.length === 0) throw new Error("No migration providers found.");
const providerById = new Map(providers.map((provider) => [provider.id, provider]));
const providerId = params.opts.importFrom?.trim() || await params.prompter.select({
message: t("wizard.migration.source"),
options: [...params.detections.map((detection) => ({
value: detection.providerId,
label: detection.label,
...detection.source || detection.message ? { hint: detection.source ?? detection.message } : {}
})), ...providers.filter((provider) => !params.detections.some((detection) => detection.providerId === provider.id)).map((provider) => ({
value: provider.id,
label: provider.label,
hint: provider.description ?? t("wizard.migration.sourcePathHint")
}))],
initialValue: params.detections[0]?.providerId ?? providers[0]?.id
});
const provider = providerById.get(providerId) ?? resolvePluginMigrationProvider({
providerId,
cfg: params.baseConfig
});
if (!provider) throw new Error(`Unknown migration provider "${providerId}".`);
return {
provider,
providerId
};
}
async function runSetupMigrationImport(params) {
const [{ applyLocalSetupWorkspaceConfig, applySkipBootstrapConfig }, { createMigrationLogger, buildMigrationReportDir }, { createPreMigrationBackup }, { assertApplySucceeded, assertConflictFreePlan, formatMigrationPreview, formatMigrationResult }, { resolveStateDir }, onboardHelpers] = await Promise.all([
import("./onboard-config-DEBBvu7H.js"),
loadMigrationContextModule(),
import("./apply-BRLd9XB-.js"),
import("./output-D5hLOqNj.js"),
loadConfigPathsModule(),
import("./onboard-helpers-CfAsmV3U.js")
]);
const { provider, providerId } = await selectSetupMigrationProvider({
opts: params.opts,
baseConfig: params.baseConfig,
detections: params.detections,
prompter: params.prompter
});
const sourceDefault = resolveImportSourceDefault({
providerId,
detections: params.detections
});
const sourceDir = params.opts.importSource?.trim() || sourceDefault || (params.opts.nonInteractive ? (() => {
throw new Error("--import-source is required for non-interactive migration import.");
})() : await params.prompter.text({
message: t("wizard.migration.sourceAgentHome"),
initialValue: providerId === "hermes" ? "~/.hermes" : void 0
}));
const workspaceDir = resolveUserPath((params.opts.workspace ?? (params.opts.nonInteractive ? params.baseConfig.agents?.defaults?.workspace ?? onboardHelpers.DEFAULT_WORKSPACE : await params.prompter.text({
message: t("wizard.migration.targetWorkspace"),
initialValue: params.baseConfig.agents?.defaults?.workspace ?? onboardHelpers.DEFAULT_WORKSPACE
}))).trim() || onboardHelpers.DEFAULT_WORKSPACE);
let targetConfig = applyLocalSetupWorkspaceConfig(params.baseConfig, workspaceDir);
if (params.opts.skipBootstrap) targetConfig = applySkipBootstrapConfig(targetConfig);
const stateDir = resolveStateDir();
assertFreshSetupMigrationTarget(await inspectSetupMigrationFreshness({
baseConfig: params.baseConfig,
stateDir,
workspaceDir
}));
const ctx = {
config: targetConfig,
stateDir,
source: sourceDir,
includeSecrets: Boolean(params.opts.importSecrets),
overwrite: false,
logger: createMigrationLogger(params.runtime)
};
const plan = await provider.plan(ctx);
await params.prompter.note(formatMigrationPreview(plan).join("\n"), t("wizard.migration.previewTitle"));
assertConflictFreePlan(plan, providerId);
if (!(params.opts.nonInteractive === true ? true : await params.prompter.confirm({
message: t("wizard.migration.apply"),
initialValue: false
}))) throw new WizardCancelledError(t("wizard.migration.cancelled"));
const reportDir = buildMigrationReportDir(providerId, stateDir);
const backupPath = await createPreMigrationBackup({});
targetConfig = onboardHelpers.applyWizardMetadata(targetConfig, {
command: "onboard",
mode: "local"
});
targetConfig = await params.commitConfigFile(targetConfig);
const applyCtx = {
...ctx,
config: targetConfig,
...backupPath ? { backupPath } : {},
reportDir
};
const result = await provider.apply(applyCtx, plan);
const withReport = {
...result,
...result.backupPath ?? backupPath ? { backupPath: result.backupPath ?? backupPath } : {},
reportDir: result.reportDir ?? reportDir
};
assertApplySucceeded(withReport);
await params.prompter.note(formatMigrationResult(withReport).join("\n"), t("wizard.migration.appliedTitle"));
await params.prompter.outro(t("wizard.migration.complete"));
}
//#endregion
export { inspectSetupMigrationFreshness as n, runSetupMigrationImport as r, detectSetupMigrationSources as t };