From ae475d025fb2c85468f999e17f4909a908607c1d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 9 May 2026 01:06:05 +0100 Subject: [PATCH] refactor: isolate plugin index legacy migration --- src/commands/doctor-sqlite-state.ts | 2 +- ...installed-plugin-index-legacy-migration.ts | 74 +++++++++++++++++++ .../installed-plugin-index-store.test.ts | 2 +- src/plugins/installed-plugin-index-store.ts | 55 +------------- 4 files changed, 77 insertions(+), 56 deletions(-) create mode 100644 src/plugins/installed-plugin-index-legacy-migration.ts diff --git a/src/commands/doctor-sqlite-state.ts b/src/commands/doctor-sqlite-state.ts index ecd6358d7b1..39f28f638ce 100644 --- a/src/commands/doctor-sqlite-state.ts +++ b/src/commands/doctor-sqlite-state.ts @@ -84,7 +84,7 @@ import { import { importLegacyInstalledPluginIndexFileToSqlite, legacyInstalledPluginIndexFileExists, -} from "../plugins/installed-plugin-index-store.js"; +} from "../plugins/installed-plugin-index-legacy-migration.js"; import { note } from "../terminal/note.js"; import { importLegacyTuiLastSessionStoreToSqlite, diff --git a/src/plugins/installed-plugin-index-legacy-migration.ts b/src/plugins/installed-plugin-index-legacy-migration.ts new file mode 100644 index 00000000000..ba57e9fc46c --- /dev/null +++ b/src/plugins/installed-plugin-index-legacy-migration.ts @@ -0,0 +1,74 @@ +import fs from "node:fs"; +import { tryReadJsonSync } from "../infra/json-files.js"; +import { + writeOpenClawStateKvJson, + type OpenClawStateJsonValue, +} from "../state/openclaw-state-kv.js"; +import { clearCurrentPluginMetadataSnapshotState } from "./current-plugin-metadata-state.js"; +import { + INSTALLED_PLUGIN_INDEX_KV_KEY, + INSTALLED_PLUGIN_INDEX_KV_SCOPE, + parseInstalledPluginIndex, + resolveInstalledPluginIndexStateDbOptions, +} from "./installed-plugin-index-persisted-read.js"; +import { + resolveInstalledPluginIndexStorePath, + type InstalledPluginIndexStoreOptions, +} from "./installed-plugin-index-store-path.js"; +import { + INSTALLED_PLUGIN_INDEX_WARNING, + type InstalledPluginIndex, +} from "./installed-plugin-index-types.js"; + +function withInstalledPluginIndexWarning(index: InstalledPluginIndex): InstalledPluginIndex & { + warning: string; +} { + return { ...index, warning: INSTALLED_PLUGIN_INDEX_WARNING }; +} + +export function legacyInstalledPluginIndexFileExists( + options: InstalledPluginIndexStoreOptions = {}, +): boolean { + try { + return fs.existsSync(resolveInstalledPluginIndexStorePath(options)); + } catch { + return false; + } +} + +export type ImportLegacyInstalledPluginIndexResult = { + imported: boolean; + plugins: number; + installRecords: number; + removedSource: boolean; +}; + +export function importLegacyInstalledPluginIndexFileToSqlite( + options: InstalledPluginIndexStoreOptions = {}, +): ImportLegacyInstalledPluginIndexResult { + const filePath = resolveInstalledPluginIndexStorePath(options); + const parsed = parseInstalledPluginIndex(tryReadJsonSync(filePath)); + if (!parsed) { + return { imported: false, plugins: 0, installRecords: 0, removedSource: false }; + } + writeOpenClawStateKvJson( + INSTALLED_PLUGIN_INDEX_KV_SCOPE, + INSTALLED_PLUGIN_INDEX_KV_KEY, + withInstalledPluginIndexWarning(parsed) as unknown as OpenClawStateJsonValue, + resolveInstalledPluginIndexStateDbOptions({ env: options.env, stateDir: options.stateDir }), + ); + let removedSource = false; + try { + fs.unlinkSync(filePath); + removedSource = true; + } catch { + removedSource = false; + } + clearCurrentPluginMetadataSnapshotState(); + return { + imported: true, + plugins: parsed.plugins.length, + installRecords: Object.keys(parsed.installRecords ?? {}).length, + removedSource, + }; +} diff --git a/src/plugins/installed-plugin-index-store.test.ts b/src/plugins/installed-plugin-index-store.test.ts index 44a5f33a11c..f426d098c8c 100644 --- a/src/plugins/installed-plugin-index-store.test.ts +++ b/src/plugins/installed-plugin-index-store.test.ts @@ -4,8 +4,8 @@ import { afterEach, describe, expect, it } from "vitest"; import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; import { readOpenClawStateKvJson } from "../state/openclaw-state-kv.js"; import type { PluginCandidate } from "./discovery.js"; +import { importLegacyInstalledPluginIndexFileToSqlite } from "./installed-plugin-index-legacy-migration.js"; import { - importLegacyInstalledPluginIndexFileToSqlite, inspectPersistedInstalledPluginIndex, readPersistedInstalledPluginIndex, refreshPersistedInstalledPluginIndex, diff --git a/src/plugins/installed-plugin-index-store.ts b/src/plugins/installed-plugin-index-store.ts index b6172257117..f1cc54023f7 100644 --- a/src/plugins/installed-plugin-index-store.ts +++ b/src/plugins/installed-plugin-index-store.ts @@ -1,5 +1,3 @@ -import fs from "node:fs"; -import { tryReadJsonSync } from "../infra/json-files.js"; import { deleteOpenClawStateKvJson, writeOpenClawStateKvJson, @@ -16,7 +14,6 @@ import { diffInstalledPluginIndexInvalidationReasons } from "./installed-plugin- import { INSTALLED_PLUGIN_INDEX_KV_KEY, INSTALLED_PLUGIN_INDEX_KV_SCOPE, - parseInstalledPluginIndex, readPersistedInstalledPluginIndex, readPersistedInstalledPluginIndexSync, resolveInstalledPluginIndexStateDbOptions, @@ -25,10 +22,7 @@ import { resolveCompatRegistryVersion, resolveInstalledPluginIndexPolicyHash, } from "./installed-plugin-index-policy.js"; -import { - resolveInstalledPluginIndexStorePath, - type InstalledPluginIndexStoreOptions, -} from "./installed-plugin-index-store-path.js"; +import type { InstalledPluginIndexStoreOptions } from "./installed-plugin-index-store-path.js"; import { INSTALLED_PLUGIN_INDEX_MIGRATION_VERSION, INSTALLED_PLUGIN_INDEX_VERSION, @@ -102,53 +96,6 @@ export function deletePersistedInstalledPluginIndexSync( return removed; } -export function legacyInstalledPluginIndexFileExists( - options: InstalledPluginIndexStoreOptions = {}, -): boolean { - try { - return fs.existsSync(resolveInstalledPluginIndexStorePath(options)); - } catch { - return false; - } -} - -export type ImportLegacyInstalledPluginIndexResult = { - imported: boolean; - plugins: number; - installRecords: number; - removedSource: boolean; -}; - -export function importLegacyInstalledPluginIndexFileToSqlite( - options: InstalledPluginIndexStoreOptions = {}, -): ImportLegacyInstalledPluginIndexResult { - const filePath = resolveInstalledPluginIndexStorePath(options); - const parsed = parseInstalledPluginIndex(tryReadJsonSync(filePath)); - if (!parsed) { - return { imported: false, plugins: 0, installRecords: 0, removedSource: false }; - } - writeOpenClawStateKvJson( - INSTALLED_PLUGIN_INDEX_KV_SCOPE, - INSTALLED_PLUGIN_INDEX_KV_KEY, - withInstalledPluginIndexWarning(parsed) as unknown as OpenClawStateJsonValue, - resolveInstalledPluginIndexStateDbOptions({ env: options.env, stateDir: options.stateDir }), - ); - let removedSource = false; - try { - fs.unlinkSync(filePath); - removedSource = true; - } catch { - removedSource = false; - } - clearCurrentPluginMetadataSnapshotState(); - return { - imported: true, - plugins: parsed.plugins.length, - installRecords: Object.keys(parsed.installRecords ?? {}).length, - removedSource, - }; -} - function hasPolicyRefreshTargets( persisted: InstalledPluginIndex, policyPluginIds: readonly string[] | undefined,