mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-03 20:01:36 +00:00
refactor: mechanical dedup batch (protocol types, update-cli bridge, talk fallback) (#114432)
* refactor(onboarding): remove search setup barrel * refactor(plugins): reuse detected package manifest * test(update): replace global helper bridges * refactor(talk): remove dead legacy response fallback * refactor(protocol): derive root types from schema The maintainer approved broadening the additive schema-backed type surface without a protocol version bump. * fix(plugins): drop stale package path import * test(protocol): type dynamic registry lookups * docs(talk): explain canonical response boundary * refactor(talk): enforce canonical response input * fix(protocol): keep root type exports registry-free * refactor(update): expose helpers through test facades * fix(protocol): keep result types on leaf schema modules
This commit is contained in:
committed by
GitHub
parent
0f879595d4
commit
c6b2ec28c8
@@ -1,7 +1,5 @@
|
||||
// Plugin synchronization and convergence after the core update.
|
||||
import path from "node:path";
|
||||
import { confirm, isCancel, text } from "@clack/prompts";
|
||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||
import { stripAnsi } from "../../../packages/terminal-core/src/ansi.js";
|
||||
import { stylePromptMessage } from "../../../packages/terminal-core/src/prompt-style.js";
|
||||
import { sanitizeTerminalText } from "../../../packages/terminal-core/src/safe-text.js";
|
||||
@@ -10,20 +8,13 @@ import { readConfigFileSnapshot } from "../../config/config.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { PluginInstallRecord } from "../../config/types.plugins.js";
|
||||
import type { ClawHubRiskAcknowledgementRequest } from "../../infra/clawhub-install-trust.js";
|
||||
import { pathExists } from "../../infra/fs-safe.js";
|
||||
import type { UpdateChannel } from "../../infra/update-channels.js";
|
||||
import type { UpdateRunResult } from "../../infra/update-runner.js";
|
||||
import { normalizePluginsConfig, resolveEffectiveEnableState } from "../../plugins/config-state.js";
|
||||
import { commitPluginInstallRecordsWithConfig } from "../../plugins/install-record-commit.js";
|
||||
import {
|
||||
loadInstalledPluginIndexInstallRecords,
|
||||
withoutPluginInstallRecords,
|
||||
withPluginInstallRecords,
|
||||
} from "../../plugins/installed-plugin-index-records.js";
|
||||
import {
|
||||
resolveTrustedSourceLinkedOfficialClawHubSpec,
|
||||
resolveTrustedSourceLinkedOfficialNpmSpec,
|
||||
} from "../../plugins/official-external-install-records.js";
|
||||
import { refreshPluginRegistryAfterConfigMutation } from "../../plugins/registry-refresh.js";
|
||||
import {
|
||||
isClawHubTrustSkippedOutcome,
|
||||
@@ -33,46 +24,27 @@ import {
|
||||
type PluginUpdateOutcome,
|
||||
} from "../../plugins/update.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { resolveUserPath } from "../../utils.js";
|
||||
import { listPersistedBundledPluginLocationBridges } from "../plugins-location-bridges.js";
|
||||
import {
|
||||
hasNativePackageInstallPayload,
|
||||
resolveBundleInstallRecordPayload,
|
||||
validateBundleInstallRecordPayload,
|
||||
} from "./plugin-payload-validation.js";
|
||||
import {
|
||||
convergenceWarningsToOutcomes,
|
||||
runPostCorePluginConvergence,
|
||||
} from "./post-core-plugin-convergence.js";
|
||||
import { readPackageVersion, type UpdateCommandOptions } from "./shared.js";
|
||||
import {
|
||||
buildInvalidConfigPostCoreUpdateResult,
|
||||
collectMissingPluginInstallPayloads,
|
||||
resolvePostSyncPluginUpdateSkipIds,
|
||||
type MissingPluginInstallPayload,
|
||||
type PostCorePluginUpdateResult,
|
||||
} from "./update-command-plugins-internals.js";
|
||||
|
||||
export type { PostCorePluginUpdateResult } from "./update-command-plugins-internals.js";
|
||||
|
||||
const POST_UPDATE_PLUGIN_REPAIR_GUIDANCE =
|
||||
"Run openclaw update repair to retry post-update plugin repair.";
|
||||
|
||||
export type PostCorePluginUpdateResult = NonNullable<
|
||||
NonNullable<UpdateRunResult["postUpdate"]>["plugins"]
|
||||
>;
|
||||
|
||||
type MissingPluginInstallPayload = {
|
||||
pluginId: string;
|
||||
installPath?: string;
|
||||
reason: "missing-install-path" | "missing-package-dir" | "missing-package-json";
|
||||
};
|
||||
|
||||
type PostUpdatePluginWarning = NonNullable<PostCorePluginUpdateResult["warnings"]>[number];
|
||||
|
||||
function resolvePostSyncPluginUpdateSkipIds(params: {
|
||||
switchedToClawHub: readonly string[];
|
||||
switchedToNpm: readonly string[];
|
||||
repairedMissingPayloadIds: ReadonlySet<string>;
|
||||
}): Set<string> {
|
||||
return new Set([
|
||||
...params.switchedToClawHub,
|
||||
...params.switchedToNpm,
|
||||
...params.repairedMissingPayloadIds,
|
||||
]);
|
||||
}
|
||||
|
||||
function isClawHubTrustNotice(message: string): boolean {
|
||||
const trimmed = stripAnsi(message).trimStart();
|
||||
return (
|
||||
@@ -133,85 +105,6 @@ function resolveUpdateClawHubRiskAcknowledgementOptions(
|
||||
};
|
||||
}
|
||||
|
||||
function isTrackedPackageInstallRecord(record: PluginInstallRecord): boolean {
|
||||
return (
|
||||
record.source === "npm" ||
|
||||
record.source === "clawhub" ||
|
||||
record.source === "git" ||
|
||||
record.source === "marketplace"
|
||||
);
|
||||
}
|
||||
|
||||
async function collectMissingPluginInstallPayloads(params: {
|
||||
records: Record<string, PluginInstallRecord>;
|
||||
config?: OpenClawConfig;
|
||||
skipDisabledPlugins?: boolean;
|
||||
syncOfficialPluginInstalls?: boolean;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): Promise<MissingPluginInstallPayload[]> {
|
||||
const env = params.env ?? process.env;
|
||||
const normalizedPluginConfig =
|
||||
params.skipDisabledPlugins && params.config
|
||||
? normalizePluginsConfig(params.config.plugins)
|
||||
: undefined;
|
||||
const missing: MissingPluginInstallPayload[] = [];
|
||||
for (const [pluginId, record] of Object.entries(params.records).toSorted(([left], [right]) =>
|
||||
left.localeCompare(right),
|
||||
)) {
|
||||
if (!isTrackedPackageInstallRecord(record)) {
|
||||
continue;
|
||||
}
|
||||
const officialNpmSpec = params.syncOfficialPluginInstalls
|
||||
? resolveTrustedSourceLinkedOfficialNpmSpec({ pluginId, record })
|
||||
: undefined;
|
||||
const officialClawHubSpec = params.syncOfficialPluginInstalls
|
||||
? resolveTrustedSourceLinkedOfficialClawHubSpec({ pluginId, record })
|
||||
: undefined;
|
||||
if (normalizedPluginConfig && params.config) {
|
||||
const enableState = resolveEffectiveEnableState({
|
||||
id: pluginId,
|
||||
origin: "global",
|
||||
config: normalizedPluginConfig,
|
||||
rootConfig: params.config,
|
||||
});
|
||||
if (!enableState.enabled && !officialNpmSpec && !officialClawHubSpec) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const rawInstallPath = normalizeOptionalString(record.installPath);
|
||||
if (!rawInstallPath) {
|
||||
missing.push({ pluginId, reason: "missing-install-path" });
|
||||
continue;
|
||||
}
|
||||
const installPath = resolveUserPath(rawInstallPath, env);
|
||||
if (!(await pathExists(installPath))) {
|
||||
missing.push({ pluginId, installPath, reason: "missing-package-dir" });
|
||||
continue;
|
||||
}
|
||||
const bundlePayload = resolveBundleInstallRecordPayload({ record, installPath });
|
||||
if (bundlePayload.isBundlePayload) {
|
||||
if (await hasNativePackageInstallPayload(installPath)) {
|
||||
continue;
|
||||
}
|
||||
const bundleFailure = validateBundleInstallRecordPayload({
|
||||
pluginId,
|
||||
installPath,
|
||||
record,
|
||||
bundleFormat: bundlePayload.bundleFormat,
|
||||
});
|
||||
if (bundleFailure) {
|
||||
missing.push({ pluginId, installPath, reason: "missing-package-json" });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const packageJsonPath = path.join(installPath, "package.json");
|
||||
if (!(await pathExists(packageJsonPath))) {
|
||||
missing.push({ pluginId, installPath, reason: "missing-package-json" });
|
||||
}
|
||||
}
|
||||
return missing;
|
||||
}
|
||||
|
||||
function formatMissingPluginPayloadReason(entry: MissingPluginInstallPayload): string {
|
||||
if (entry.reason === "missing-install-path") {
|
||||
return "installPath is missing";
|
||||
@@ -295,58 +188,6 @@ function isActionableSkippedPostUpdateOutcome(outcome: PluginUpdateOutcome): boo
|
||||
return isDisabledAfterFailureOutcome(outcome) || isClawHubTrustSkippedOutcome(outcome);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the post-core-update result we return when the active config cannot
|
||||
* even be parsed. Mandatory post-core convergence requires a parseable
|
||||
* config to know which plugins are configured; if one isn't available, we
|
||||
* refuse to restart the gateway and surface this as a hard error so the
|
||||
* existing `status === "error"` ⇒ `exit 1` pre-restart gate fires.
|
||||
*
|
||||
*/
|
||||
function buildInvalidConfigPostCoreUpdateResult(): {
|
||||
message: string;
|
||||
guidance: string[];
|
||||
result: PostCorePluginUpdateResult;
|
||||
} {
|
||||
const guidance = [
|
||||
"Run `openclaw doctor` to inspect the config validation errors.",
|
||||
"Once the config parses, rerun `openclaw update repair`.",
|
||||
];
|
||||
const message =
|
||||
"Plugin post-update convergence skipped because the config is invalid; refusing to restart the gateway with an unverified plugin set.";
|
||||
return {
|
||||
message,
|
||||
guidance,
|
||||
result: {
|
||||
status: "error",
|
||||
reason: "invalid-config",
|
||||
changed: false,
|
||||
sync: {
|
||||
changed: false,
|
||||
switchedToBundled: [],
|
||||
switchedToNpm: [],
|
||||
warnings: [],
|
||||
errors: [],
|
||||
},
|
||||
npm: {
|
||||
changed: false,
|
||||
outcomes: [],
|
||||
},
|
||||
integrityDrifts: [],
|
||||
warnings: [{ reason: "invalid-config", message, guidance }],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
||||
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.updateCommandPluginsTestApi")] =
|
||||
{
|
||||
buildInvalidConfigPostCoreUpdateResult,
|
||||
collectMissingPluginInstallPayloads,
|
||||
resolvePostSyncPluginUpdateSkipIds,
|
||||
};
|
||||
}
|
||||
|
||||
export async function updatePluginsAfterCoreUpdate(params: {
|
||||
root: string;
|
||||
channel: UpdateChannel;
|
||||
|
||||
Reference in New Issue
Block a user