


























@@ -15,6 +15,7 @@ import { loadInstalledPluginIndexInstallRecords } from "../../../plugins/install
1515import { writePersistedInstalledPluginIndexInstallRecords } from "../../../plugins/installed-plugin-index-records.js";
1616import { buildNpmResolutionInstallFields } from "../../../plugins/installs.js";
1717import { loadManifestMetadataSnapshot } from "../../../plugins/manifest-contract-eligibility.js";
18+import type { PluginManifestRecord } from "../../../plugins/manifest-registry.js";
1819import type { PluginPackageInstall } from "../../../plugins/manifest.js";
1920import {
2021listOfficialExternalPluginCatalogEntries,
@@ -53,6 +54,7 @@ const RUNTIME_PLUGIN_INSTALL_CANDIDATES: readonly DownloadableInstallCandidate[]
5354];
54555556const MISSING_CHANNEL_CONFIG_DESCRIPTOR_DIAGNOSTIC = "without channelConfigs metadata";
57+const UPDATE_IN_PROGRESS_ENV = "OPENCLAW_UPDATE_IN_PROGRESS";
56585759function shouldFallbackClawHubToNpm(result: { ok: false; code?: string }): boolean {
5860return (
@@ -172,6 +174,9 @@ function collectDownloadableInstallCandidates(params: {
172174env: params.env,
173175excludeWorkspace: true,
174176})) {
177+if (entry.origin === "bundled") {
178+continue;
179+}
175180const pluginId = entry.pluginId ?? entry.id;
176181if (params.blockedPluginIds?.has(pluginId)) {
177182continue;
@@ -305,6 +310,31 @@ function isInstalledRecordMissingOnDisk(
305310return !existsSync(path.join(resolved, "package.json"));
306311}
307312313+function isUpdatePackageDoctorPass(env: NodeJS.ProcessEnv): boolean {
314+return env[UPDATE_IN_PROGRESS_ENV] === "1";
315+}
316+317+function recordMatchesBundledPackage(
318+record: PluginInstallRecord,
319+bundled: PluginManifestRecord,
320+): boolean {
321+const packageName = bundled.packageName?.trim() || bundled.name?.trim();
322+if (!packageName) {
323+return false;
324+}
325+if (record.source === "npm") {
326+return [record.spec, record.resolvedName, record.resolvedSpec].some((value) =>
327+value?.trim().startsWith(packageName),
328+);
329+}
330+if (record.source === "clawhub") {
331+return [record.clawhubPackage, record.spec].some((value) =>
332+value?.trim().includes(packageName),
333+);
334+}
335+return false;
336+}
337+308338async function installCandidate(params: {
309339candidate: DownloadableInstallCandidate;
310340records: Record<string, PluginInstallRecord>;
@@ -451,30 +481,72 @@ async function repairMissingPluginInstalls(params: {
451481 env,
452482});
453483const knownIds = new Set(snapshot.plugins.map((plugin) => plugin.id));
484+const bundledPluginsById = new Map(
485+snapshot.plugins
486+.filter((plugin) => plugin.origin === "bundled")
487+.map((plugin) => [plugin.id, plugin]),
488+);
454489const configuredPluginIdsWithStaleDescriptors =
455490collectConfiguredPluginIdsWithMissingChannelConfigDescriptors({
456491 snapshot,
457492configuredPluginIds: params.pluginIds,
458493configuredChannelIds: params.channelIds,
459494});
460495const records = await loadInstalledPluginIndexInstallRecords({ env });
461-const missingRecordedPluginIds = Object.keys(records).filter(
462-(pluginId) =>
463-(params.pluginIds.has(pluginId) &&
464-(!knownIds.has(pluginId) || isInstalledRecordMissingOnDisk(records[pluginId], env))) ||
465-configuredPluginIdsWithStaleDescriptors.has(pluginId),
466-);
467496const changes: string[] = [];
468497const warnings: string[] = [];
498+const deferredPluginIds = new Set<string>();
469499let nextRecords = records;
470500501+for (const [pluginId, record] of Object.entries(records)) {
502+const bundled = bundledPluginsById.get(pluginId);
503+if (
504+!bundled ||
505+!params.pluginIds.has(pluginId) ||
506+!recordMatchesBundledPackage(record, bundled)
507+) {
508+continue;
509+}
510+if (nextRecords === records) {
511+nextRecords = { ...records };
512+}
513+delete nextRecords[pluginId];
514+changes.push(`Removed stale managed install record for bundled plugin "${pluginId}".`);
515+}
516+517+if (isUpdatePackageDoctorPass(env)) {
518+for (const pluginId of params.pluginIds) {
519+const record = nextRecords[pluginId];
520+if (!record || !isInstalledRecordMissingOnDisk(record, env)) {
521+continue;
522+}
523+if (nextRecords === records) {
524+nextRecords = { ...records };
525+}
526+delete nextRecords[pluginId];
527+deferredPluginIds.add(pluginId);
528+changes.push(
529+`Deferred missing configured plugin "${pluginId}" install repair until post-update doctor.`,
530+);
531+}
532+}
533+534+const missingRecordedPluginIds = Object.keys(records).filter(
535+(pluginId) =>
536+Object.hasOwn(nextRecords, pluginId) &&
537+!bundledPluginsById.has(pluginId) &&
538+((params.pluginIds.has(pluginId) &&
539+(!knownIds.has(pluginId) || isInstalledRecordMissingOnDisk(nextRecords[pluginId], env))) ||
540+configuredPluginIdsWithStaleDescriptors.has(pluginId)),
541+);
542+471543if (missingRecordedPluginIds.length > 0) {
472544const updateResult = await updateNpmInstalledPlugins({
473545config: {
474546 ...params.cfg,
475547plugins: {
476548 ...params.cfg.plugins,
477-installs: records,
549+installs: nextRecords,
478550},
479551},
480552pluginIds: missingRecordedPluginIds,
@@ -496,10 +568,15 @@ async function repairMissingPluginInstalls(params: {
496568497569const missingPluginIds = new Set(
498570[...params.pluginIds].filter((pluginId) => {
571+if (deferredPluginIds.has(pluginId)) {
572+return false;
573+}
499574const hasRecord = Object.hasOwn(nextRecords, pluginId);
500575return (
501-(!knownIds.has(pluginId) && !hasRecord) ||
502-(hasRecord && isInstalledRecordMissingOnDisk(nextRecords[pluginId], env))
576+(!knownIds.has(pluginId) && !hasRecord && !bundledPluginsById.has(pluginId)) ||
577+(hasRecord &&
578+!bundledPluginsById.has(pluginId) &&
579+isInstalledRecordMissingOnDisk(nextRecords[pluginId], env))
503580);
504581}),
505582);
@@ -509,7 +586,10 @@ async function repairMissingPluginInstalls(params: {
509586 missingPluginIds,
510587configuredPluginIds: params.pluginIds,
511588configuredChannelIds: params.channelIds,
512-blockedPluginIds: params.blockedPluginIds,
589+blockedPluginIds:
590+deferredPluginIds.size > 0
591+ ? new Set([...(params.blockedPluginIds ?? []), ...deferredPluginIds])
592+ : params.blockedPluginIds,
513593})) {
514594const hasUsableRecord =
515595Object.hasOwn(nextRecords, candidate.pluginId) &&
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。