Merge 142fa9c9198eea0ed6a9d022313a6faf61416738 into 9fb78453e088cd7b553d7779faa0de5c83708e70
This commit is contained in:
commit
81d3559a0a
112
src/gateway/server-startup.plugin-internal-hooks.test.ts
Normal file
112
src/gateway/server-startup.plugin-internal-hooks.test.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import { createDefaultDeps } from "../cli/deps.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
clearInternalHooks,
|
||||
createInternalHookEvent,
|
||||
triggerInternalHook,
|
||||
} from "../hooks/internal-hooks.js";
|
||||
import { loadOpenClawPlugins } from "../plugins/loader.js";
|
||||
import { startGatewaySidecars } from "./server-startup.js";
|
||||
|
||||
function createSilentLogger() {
|
||||
return {
|
||||
info: (_msg: string) => {},
|
||||
warn: (_msg: string) => {},
|
||||
error: (_msg: string) => {},
|
||||
debug: (_msg: string) => {},
|
||||
};
|
||||
}
|
||||
|
||||
async function writeTestPlugin(params: {
|
||||
dir: string;
|
||||
id: string;
|
||||
message: string;
|
||||
}): Promise<void> {
|
||||
await fs.mkdir(params.dir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(params.dir, "openclaw.plugin.json"),
|
||||
JSON.stringify({ id: params.id, configSchema: {} }, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
await fs.writeFile(
|
||||
path.join(params.dir, "index.ts"),
|
||||
[
|
||||
"export default function register(api) {",
|
||||
` api.registerHook("session:start", async (event) => { event.messages.push(${JSON.stringify(params.message)}); }, { name: ${JSON.stringify(`${params.id}:session-start`)} });`,
|
||||
"}",
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf-8",
|
||||
);
|
||||
}
|
||||
|
||||
describe("gateway startup internal hooks", () => {
|
||||
afterEach(() => {
|
||||
clearInternalHooks();
|
||||
});
|
||||
|
||||
test("does not clear plugin internal hooks when loading workspace hooks", async () => {
|
||||
vi.stubEnv("OPENCLAW_SKIP_BROWSER_CONTROL_SERVER", "1");
|
||||
vi.stubEnv("OPENCLAW_SKIP_GMAIL_WATCHER", "1");
|
||||
vi.stubEnv("OPENCLAW_SKIP_CHANNELS", "1");
|
||||
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
clearInternalHooks();
|
||||
|
||||
const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-hook-test-"));
|
||||
try {
|
||||
const workspaceDir = path.join(tmpRoot, "workspace");
|
||||
const pluginDir = path.join(tmpRoot, "plugin");
|
||||
const pluginId = "test-plugin-internal-hooks";
|
||||
const marker = "plugin-hook-ran";
|
||||
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await writeTestPlugin({ dir: pluginDir, id: pluginId, message: marker });
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
hooks: { internal: { enabled: true } },
|
||||
plugins: {
|
||||
enabled: true,
|
||||
allow: [pluginId],
|
||||
load: { paths: [pluginDir] },
|
||||
},
|
||||
};
|
||||
|
||||
// Plugin registration may register internal hooks immediately.
|
||||
const pluginRegistry = loadOpenClawPlugins({
|
||||
config: cfg,
|
||||
workspaceDir,
|
||||
cache: false,
|
||||
logger: createSilentLogger(),
|
||||
});
|
||||
|
||||
await startGatewaySidecars({
|
||||
cfg,
|
||||
pluginRegistry,
|
||||
defaultWorkspaceDir: workspaceDir,
|
||||
deps: createDefaultDeps(),
|
||||
startChannels: async () => {},
|
||||
log: { warn: () => {} },
|
||||
logHooks: createSilentLogger(),
|
||||
logChannels: { info: () => {}, error: () => {} },
|
||||
logBrowser: { error: () => {} },
|
||||
});
|
||||
|
||||
await vi.runAllTimersAsync();
|
||||
|
||||
const event = createInternalHookEvent("session", "start", "test-session", {});
|
||||
await triggerInternalHook(event);
|
||||
expect(event.messages).toContain(marker);
|
||||
} finally {
|
||||
await fs.rm(tmpRoot, { recursive: true, force: true });
|
||||
}
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -13,11 +13,7 @@ import type { CliDeps } from "../cli/deps.js";
|
||||
import type { loadConfig } from "../config/config.js";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
import { startGmailWatcherWithLogs } from "../hooks/gmail-watcher-lifecycle.js";
|
||||
import {
|
||||
clearInternalHooks,
|
||||
createInternalHookEvent,
|
||||
triggerInternalHook,
|
||||
} from "../hooks/internal-hooks.js";
|
||||
import { createInternalHookEvent, triggerInternalHook } from "../hooks/internal-hooks.js";
|
||||
import { loadInternalHooks } from "../hooks/loader.js";
|
||||
import { isTruthyEnvValue } from "../infra/env.js";
|
||||
import type { loadOpenClawPlugins } from "../plugins/loader.js";
|
||||
@ -110,8 +106,8 @@ export async function startGatewaySidecars(params: {
|
||||
|
||||
// Load internal hook handlers from configuration and directory discovery.
|
||||
try {
|
||||
// Clear any previously registered hooks to ensure fresh loading
|
||||
clearInternalHooks();
|
||||
// Internal hooks are cleared once at gateway startup before plugins load.
|
||||
// Do not clear here: plugins may register internal hooks during plugin registration.
|
||||
const loadedCount = await loadInternalHooks(params.cfg, params.defaultWorkspaceDir);
|
||||
if (loadedCount > 0) {
|
||||
params.logHooks.info(
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
import { formatConfigIssueLines } from "../config/issue-format.js";
|
||||
import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js";
|
||||
import { resolveMainSessionKey } from "../config/sessions.js";
|
||||
import { clearInternalHooks } from "../hooks/internal-hooks.js";
|
||||
import { clearAgentRunContext, onAgentEvent } from "../infra/agent-events.js";
|
||||
import {
|
||||
ensureControlUiAssetsBuilt,
|
||||
@ -557,6 +558,10 @@ export async function startGatewayServer(
|
||||
env: process.env,
|
||||
});
|
||||
const baseMethods = listGatewayMethods();
|
||||
// Reset internal hook registry before plugins register internal hooks.
|
||||
// Plugins may call `api.registerHook(...)` during registration, so clearing later
|
||||
// (e.g. during hook discovery) would wipe plugin-registered hooks like session:start.
|
||||
clearInternalHooks();
|
||||
const emptyPluginRegistry = createEmptyPluginRegistry();
|
||||
let pluginRegistry = emptyPluginRegistry;
|
||||
let baseGatewayMethods = baseMethods;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user