mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:50:42 +00:00
perf(migrations): trim legacy migration and bind cold paths
This commit is contained in:
@@ -29,6 +29,10 @@ type BundledChannelSetupEntryRuntimeContract = {
|
||||
kind: "bundled-channel-setup-entry";
|
||||
loadSetupPlugin: () => ChannelPlugin;
|
||||
loadSetupSecrets?: () => ChannelPlugin["secrets"] | undefined;
|
||||
features?: {
|
||||
legacyStateMigrations?: boolean;
|
||||
legacySessionSurfaces?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
type GeneratedBundledChannelEntry = {
|
||||
@@ -88,6 +92,13 @@ function resolveChannelSetupModuleEntry(
|
||||
return record as BundledChannelSetupEntryRuntimeContract;
|
||||
}
|
||||
|
||||
function hasSetupEntryFeature(
|
||||
entry: BundledChannelSetupEntryRuntimeContract | undefined,
|
||||
feature: keyof NonNullable<BundledChannelSetupEntryRuntimeContract["features"]>,
|
||||
): boolean {
|
||||
return entry?.features?.[feature] === true;
|
||||
}
|
||||
|
||||
function resolveBundledChannelBoundaryRoot(params: {
|
||||
metadata: BundledChannelPluginMetadata;
|
||||
modulePath: string;
|
||||
@@ -263,6 +274,19 @@ export function listBundledChannelSetupPlugins(): readonly ChannelPlugin[] {
|
||||
});
|
||||
}
|
||||
|
||||
export function listBundledChannelSetupPluginsByFeature(
|
||||
feature: keyof NonNullable<BundledChannelSetupEntryRuntimeContract["features"]>,
|
||||
): readonly ChannelPlugin[] {
|
||||
return listBundledChannelPluginIds().flatMap((id) => {
|
||||
const setupEntry = getLazyGeneratedBundledChannelEntry(id, { includeSetup: true })?.setupEntry;
|
||||
if (!hasSetupEntryFeature(setupEntry, feature)) {
|
||||
return [];
|
||||
}
|
||||
const plugin = getBundledChannelSetupPlugin(id);
|
||||
return plugin ? [plugin] : [];
|
||||
});
|
||||
}
|
||||
|
||||
export function getBundledChannelPlugin(id: ChannelId): ChannelPlugin | undefined {
|
||||
const cached = lazyPluginsById.get(id);
|
||||
if (cached) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
createTestRegistry,
|
||||
} from "../test-utils/channel-plugins.js";
|
||||
import {
|
||||
loadFreshAgentsCommandModuleForTest,
|
||||
loadFreshAgentsBindCommandModuleForTest,
|
||||
readConfigFileSnapshotMock,
|
||||
resetAgentsBindTestHarness,
|
||||
runtime,
|
||||
@@ -25,11 +25,11 @@ const matrixBindingPlugin = createBindingResolverTestPlugin({
|
||||
},
|
||||
});
|
||||
|
||||
let agentsBindCommand: typeof import("./agents.js").agentsBindCommand;
|
||||
let agentsBindCommand: typeof import("./agents.commands.bind.js").agentsBindCommand;
|
||||
|
||||
describe("agents bind matrix integration", () => {
|
||||
beforeEach(async () => {
|
||||
({ agentsBindCommand } = await loadFreshAgentsCommandModuleForTest());
|
||||
({ agentsBindCommand } = await loadFreshAgentsBindCommandModuleForTest());
|
||||
resetAgentsBindTestHarness();
|
||||
|
||||
setActivePluginRegistry(
|
||||
|
||||
@@ -39,12 +39,18 @@ vi.mock("../config/config.js", async () => {
|
||||
export const runtime = createTestRuntime();
|
||||
|
||||
let agentsCommandModulePromise: Promise<typeof import("./agents.js")> | undefined;
|
||||
let agentsBindCommandModulePromise: Promise<typeof import("./agents.commands.bind.js")> | undefined;
|
||||
|
||||
export async function loadFreshAgentsCommandModuleForTest() {
|
||||
agentsCommandModulePromise ??= import("./agents.js");
|
||||
return await agentsCommandModulePromise;
|
||||
}
|
||||
|
||||
export async function loadFreshAgentsBindCommandModuleForTest() {
|
||||
agentsBindCommandModulePromise ??= import("./agents.commands.bind.js");
|
||||
return await agentsBindCommandModulePromise;
|
||||
}
|
||||
|
||||
export function resetAgentsBindTestHarness(): void {
|
||||
readConfigFileSnapshotMock.mockClear();
|
||||
writeConfigFileMock.mockClear();
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
resetSessionStoreLockRuntimeForTests,
|
||||
setSessionWriteLockAcquirerForTests,
|
||||
} from "../config/sessions/store.js";
|
||||
import {
|
||||
autoMigrateLegacyStateDir,
|
||||
autoMigrateLegacyState,
|
||||
@@ -14,6 +18,30 @@ import {
|
||||
|
||||
let tempRoot: string | null = null;
|
||||
|
||||
vi.mock("../infra/json-files.js", async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import("../infra/json-files.js")>("../infra/json-files.js");
|
||||
return {
|
||||
...actual,
|
||||
writeTextAtomic: async (
|
||||
filePath: string,
|
||||
content: string,
|
||||
options?: { mode?: number; ensureDirMode?: number; appendTrailingNewline?: boolean },
|
||||
) => {
|
||||
const payload =
|
||||
options?.appendTrailingNewline && !content.endsWith("\n") ? `${content}\n` : content;
|
||||
await fs.promises.mkdir(path.dirname(filePath), {
|
||||
recursive: true,
|
||||
...(typeof options?.ensureDirMode === "number" ? { mode: options.ensureDirMode } : {}),
|
||||
});
|
||||
await fs.promises.writeFile(filePath, payload, {
|
||||
encoding: "utf8",
|
||||
mode: options?.mode ?? 0o600,
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
async function makeTempRoot() {
|
||||
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "openclaw-doctor-"));
|
||||
tempRoot = root;
|
||||
@@ -52,9 +80,16 @@ async function runTelegramAllowFromMigration(params: { root: string; cfg: OpenCl
|
||||
return { oauthDir, detected, result };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
setSessionWriteLockAcquirerForTests(async () => ({
|
||||
release: async () => undefined,
|
||||
}));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
resetAutoMigrateLegacyStateForTest();
|
||||
resetAutoMigrateLegacyStateDirForTest();
|
||||
resetSessionStoreLockRuntimeForTests();
|
||||
if (!tempRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||
import { iterateBootstrapChannelPlugins } from "../channels/plugins/bootstrap-registry.js";
|
||||
import { listBundledChannelPlugins } from "../channels/plugins/bundled.js";
|
||||
import { listBundledChannelSetupPluginsByFeature } from "../channels/plugins/bundled.js";
|
||||
import type { ChannelLegacyStateMigrationPlan } from "../channels/plugins/types.core.js";
|
||||
import {
|
||||
resolveLegacyStateDirs,
|
||||
@@ -73,6 +72,7 @@ type MigrationLogger = {
|
||||
|
||||
let autoMigrateChecked = false;
|
||||
let autoMigrateStateDirChecked = false;
|
||||
let cachedLegacySessionSurfaces: LegacySessionSurface[] | null = null;
|
||||
|
||||
type LegacySessionSurface = {
|
||||
isLegacyGroupSessionKey?: (key: string) => boolean;
|
||||
@@ -83,14 +83,16 @@ type LegacySessionSurface = {
|
||||
};
|
||||
|
||||
function getLegacySessionSurfaces(): LegacySessionSurface[] {
|
||||
const surfaces: LegacySessionSurface[] = [];
|
||||
for (const plugin of iterateBootstrapChannelPlugins()) {
|
||||
// Legacy migrations run on cold doctor/startup paths. Prefer the narrower
|
||||
// setup plugin surface here so session-key cleanup does not materialize full
|
||||
// bundled channel runtimes.
|
||||
cachedLegacySessionSurfaces ??= listBundledChannelSetupPluginsByFeature(
|
||||
"legacySessionSurfaces",
|
||||
).flatMap((plugin) => {
|
||||
const surface = plugin.messaging;
|
||||
if (surface && typeof surface === "object") {
|
||||
surfaces.push(surface);
|
||||
}
|
||||
}
|
||||
return surfaces;
|
||||
return surface && typeof surface === "object" ? [surface] : [];
|
||||
});
|
||||
return cachedLegacySessionSurfaces;
|
||||
}
|
||||
|
||||
function isSurfaceGroupKey(key: string): boolean {
|
||||
@@ -419,6 +421,7 @@ function removeDirIfEmpty(dir: string) {
|
||||
|
||||
export function resetAutoMigrateLegacyStateForTest() {
|
||||
autoMigrateChecked = false;
|
||||
cachedLegacySessionSurfaces = null;
|
||||
}
|
||||
|
||||
export function resetAutoMigrateLegacyAgentDirForTest() {
|
||||
@@ -667,7 +670,9 @@ async function collectChannelLegacyStateMigrationPlans(params: {
|
||||
oauthDir: string;
|
||||
}): Promise<ChannelLegacyStateMigrationPlan[]> {
|
||||
const plans: ChannelLegacyStateMigrationPlan[] = [];
|
||||
for (const plugin of listBundledChannelPlugins()) {
|
||||
// Legacy state detection belongs on the lightweight setup surface so doctor
|
||||
// does not cold-load unrelated runtime channel code.
|
||||
for (const plugin of listBundledChannelSetupPluginsByFeature("legacyStateMigrations")) {
|
||||
const detected = await plugin.lifecycle?.detectLegacyStateMigrations?.({
|
||||
cfg: params.cfg,
|
||||
env: params.env,
|
||||
|
||||
@@ -44,6 +44,12 @@ type DefineBundledChannelSetupEntryOptions = {
|
||||
importMetaUrl: string;
|
||||
plugin: BundledEntryModuleRef;
|
||||
secrets?: BundledEntryModuleRef;
|
||||
features?: BundledChannelSetupEntryFeatures;
|
||||
};
|
||||
|
||||
export type BundledChannelSetupEntryFeatures = {
|
||||
legacyStateMigrations?: boolean;
|
||||
legacySessionSurfaces?: boolean;
|
||||
};
|
||||
|
||||
export type BundledChannelEntryContract<TPlugin = ChannelPlugin> = {
|
||||
@@ -62,6 +68,7 @@ export type BundledChannelSetupEntryContract<TPlugin = ChannelPlugin> = {
|
||||
kind: "bundled-channel-setup-entry";
|
||||
loadSetupPlugin: () => TPlugin;
|
||||
loadSetupSecrets?: () => ChannelPlugin["secrets"] | undefined;
|
||||
features?: BundledChannelSetupEntryFeatures;
|
||||
};
|
||||
|
||||
const nodeRequire = createRequire(import.meta.url);
|
||||
@@ -373,6 +380,7 @@ export function defineBundledChannelSetupEntry<TPlugin = ChannelPlugin>({
|
||||
importMetaUrl,
|
||||
plugin,
|
||||
secrets,
|
||||
features,
|
||||
}: DefineBundledChannelSetupEntryOptions): BundledChannelSetupEntryContract<TPlugin> {
|
||||
return {
|
||||
kind: "bundled-channel-setup-entry",
|
||||
@@ -386,5 +394,6 @@ export function defineBundledChannelSetupEntry<TPlugin = ChannelPlugin>({
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
...(features ? { features } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user