UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

216 lines (215 loc) 9.09 kB
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 };