mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 23:56:07 +00:00
fix: drop stale managed plugin install records
This commit is contained in:
@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
|
||||
- OpenAI/Codex: point gateway missing-key recovery and wizard docs at the canonical `openai/gpt-5.5` plus Codex OAuth route, and fix trajectory export errors so they suggest the valid `openclaw sessions` command.
|
||||
- Google/Gemini: normalize retired `google/gemini-3-pro-preview` primary, fallback, and model-map refs during config load and unrelated config writes so saved config keeps targeting Gemini 3.1 Pro Preview.
|
||||
- Google/Gemini: normalize retired Gemini 3 Pro Preview ids inside emitted Google provider model config, so regenerated models.json rows test `google/gemini-3.1-pro-preview`.
|
||||
- Plugins/doctor: drop stale managed npm install records when `openclaw doctor --fix` removes npm packages that shadow bundled plugins, so the rebuilt registry no longer resurrects the removed package metadata.
|
||||
- Discord/voice: synthesize realtime playback timestamps from emitted Discord PCM so OpenAI realtime barge-in truncation no longer sees `audioEndMs=0` and skips legitimate interruptions.
|
||||
- Plugin SDK: keep activated linked plugin runtime facades loadable when bundled plugin fallback is disabled. Thanks @shakkernerd.
|
||||
- Feishu: auto-thread `message(action="send")` replies inside the topic when the active session is group_topic or group_topic_sender, and propagate `replyInThread` through text, card, and media outbound adapters so topic-scoped sessions no longer post at the group root. Fixes #74903. (#77151) Thanks @ai-hpc.
|
||||
|
||||
@@ -27,6 +27,17 @@ function makeTempDir() {
|
||||
return makeTrackedTempDir("openclaw-doctor-plugin-registry", tempDirs);
|
||||
}
|
||||
|
||||
async function readRequiredPersistedInstalledPluginIndex(
|
||||
stateDir: string,
|
||||
): Promise<InstalledPluginIndex> {
|
||||
const persisted = await readPersistedInstalledPluginIndex({ stateDir });
|
||||
expect(persisted).not.toBeNull();
|
||||
if (!persisted) {
|
||||
throw new Error("Expected persisted installed plugin index");
|
||||
}
|
||||
return persisted;
|
||||
}
|
||||
|
||||
function hermeticEnv(overrides: NodeJS.ProcessEnv = {}): NodeJS.ProcessEnv {
|
||||
return {
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: undefined,
|
||||
@@ -226,14 +237,10 @@ describe("maybeRepairPluginRegistryState", () => {
|
||||
});
|
||||
|
||||
expect(nextConfig).toStrictEqual({});
|
||||
await expect(readPersistedInstalledPluginIndex({ stateDir })).resolves.toMatchObject({
|
||||
refreshReason: "migration",
|
||||
plugins: [
|
||||
expect.objectContaining({
|
||||
pluginId: "demo",
|
||||
}),
|
||||
],
|
||||
});
|
||||
const persisted = await readRequiredPersistedInstalledPluginIndex(stateDir);
|
||||
expect(persisted.refreshReason).toBe("migration");
|
||||
expect(persisted.plugins).toHaveLength(1);
|
||||
expect(persisted.plugins[0]?.pluginId).toBe("demo");
|
||||
});
|
||||
|
||||
it("does not repair when registry migration is disabled", async () => {
|
||||
@@ -341,16 +348,12 @@ describe("maybeRepairPluginRegistryState", () => {
|
||||
expect(
|
||||
JSON.parse(fs.readFileSync(path.join(stateDir, "npm", "package.json"), "utf8")),
|
||||
).not.toHaveProperty("dependencies");
|
||||
await expect(readPersistedInstalledPluginIndex({ stateDir })).resolves.toMatchObject({
|
||||
refreshReason: "migration",
|
||||
plugins: [
|
||||
expect.objectContaining({
|
||||
pluginId: "google-meet",
|
||||
origin: "bundled",
|
||||
rootDir: bundledDir,
|
||||
}),
|
||||
],
|
||||
});
|
||||
const persisted = await readRequiredPersistedInstalledPluginIndex(stateDir);
|
||||
expect(persisted.refreshReason).toBe("migration");
|
||||
expect(persisted.plugins).toHaveLength(1);
|
||||
expect(persisted.plugins[0]?.pluginId).toBe("google-meet");
|
||||
expect(persisted.plugins[0]?.origin).toBe("bundled");
|
||||
expect(persisted.plugins[0]?.rootDir).toBe(bundledDir);
|
||||
expect(vi.mocked(note).mock.calls.join("\n")).toContain(
|
||||
"Removed stale managed npm plugin package",
|
||||
);
|
||||
@@ -402,17 +405,13 @@ describe("maybeRepairPluginRegistryState", () => {
|
||||
});
|
||||
|
||||
expect(fs.existsSync(managed.packageDir)).toBe(false);
|
||||
await expect(readPersistedInstalledPluginIndex({ stateDir })).resolves.toMatchObject({
|
||||
installRecords: {},
|
||||
refreshReason: "migration",
|
||||
plugins: [
|
||||
expect.objectContaining({
|
||||
pluginId: "google-meet",
|
||||
origin: "bundled",
|
||||
rootDir: bundledDir,
|
||||
}),
|
||||
],
|
||||
});
|
||||
const persisted = await readRequiredPersistedInstalledPluginIndex(stateDir);
|
||||
expect(persisted.installRecords).toStrictEqual({});
|
||||
expect(persisted.refreshReason).toBe("migration");
|
||||
expect(persisted.plugins).toHaveLength(1);
|
||||
expect(persisted.plugins[0]?.pluginId).toBe("google-meet");
|
||||
expect(persisted.plugins[0]?.origin).toBe("bundled");
|
||||
expect(persisted.plugins[0]?.rootDir).toBe(bundledDir);
|
||||
});
|
||||
|
||||
it("removes stale managed npm packages from the package lock during repair", async () => {
|
||||
|
||||
@@ -5,7 +5,10 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { saveJsonFile } from "../infra/json-file.js";
|
||||
import { tryReadJsonSync } from "../infra/json-files.js";
|
||||
import { resolveDefaultPluginNpmDir } from "../plugins/install-paths.js";
|
||||
import type { InstalledPluginIndexRecordStoreOptions } from "../plugins/installed-plugin-index-records.js";
|
||||
import {
|
||||
loadInstalledPluginIndexInstallRecords,
|
||||
type InstalledPluginIndexRecordStoreOptions,
|
||||
} from "../plugins/installed-plugin-index-records.js";
|
||||
import { loadInstalledPluginIndex } from "../plugins/installed-plugin-index.js";
|
||||
import { refreshPluginRegistry } from "../plugins/plugin-registry.js";
|
||||
import { note } from "../terminal/note.js";
|
||||
@@ -225,6 +228,17 @@ export function maybeRepairStaleManagedNpmBundledPlugins(
|
||||
return true;
|
||||
}
|
||||
|
||||
async function loadInstallRecordsWithoutPluginIds(
|
||||
params: PluginRegistryDoctorRepairParams,
|
||||
pluginIds: readonly string[],
|
||||
) {
|
||||
const records = await loadInstalledPluginIndexInstallRecords(params);
|
||||
for (const pluginId of pluginIds) {
|
||||
delete records[pluginId];
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
export async function maybeRepairPluginRegistryState(
|
||||
params: PluginRegistryDoctorRepairParams,
|
||||
): Promise<OpenClawConfig> {
|
||||
@@ -244,6 +258,9 @@ export async function maybeRepairPluginRegistryState(
|
||||
...params,
|
||||
config: params.config,
|
||||
};
|
||||
const staleManagedNpmBundledPluginIds = listStaleManagedNpmBundledPlugins(params).map(
|
||||
(plugin) => plugin.pluginId,
|
||||
);
|
||||
const removedStaleManagedNpmBundledPlugins = maybeRepairStaleManagedNpmBundledPlugins(params);
|
||||
if (!params.prompter.shouldRepair) {
|
||||
if (preflight.action === "migrate") {
|
||||
@@ -275,6 +292,14 @@ export async function maybeRepairPluginRegistryState(
|
||||
const index = await refreshPluginRegistry({
|
||||
...migrationParams,
|
||||
reason: "migration",
|
||||
...(removedStaleManagedNpmBundledPlugins
|
||||
? {
|
||||
installRecords: await loadInstallRecordsWithoutPluginIds(
|
||||
params,
|
||||
staleManagedNpmBundledPluginIds,
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
const total = index.plugins.length;
|
||||
const enabled = index.plugins.filter((plugin) => plugin.enabled).length;
|
||||
|
||||
Reference in New Issue
Block a user