mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:10:45 +00:00
fix(gateway): defer channel runtime imports
This commit is contained in:
@@ -55,6 +55,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/startup: keep node session runtime on a lightweight JSON parser instead of importing gateway method validation helpers during boot. Thanks @vincentkoc.
|
||||
- Gateway/startup: read embedded-run activity from a lightweight shared state module so restart deferral no longer imports the embedded runner during Gateway boot. Thanks @vincentkoc.
|
||||
- Gateway/startup: defer MCP loopback server imports until Gateway shutdown so normal boot no longer loads the loopback HTTP/tool schema stack just to register close handlers. Thanks @vincentkoc.
|
||||
- Gateway/startup: resolve channel runtime helpers asynchronously only when an enabled/configured channel starts, so no-channel Gateway boot skips auto-reply, media, pairing, and outbound channel helper imports. Thanks @vincentkoc.
|
||||
- CLI/Gateway: use a parse-only config snapshot for plain `gateway status` reads and reuse same-path service config context so status no longer spends tens of seconds in full config validation before printing. Thanks @vincentkoc.
|
||||
- Lobster/Gateway: memoize repeated Ajv schema compilation before loading the embedded Lobster runtime so scheduled workflows and `llm.invoke` loops stop growing gateway heap on content-identical schemas. Fixes #71148. Thanks @cmi525, @vsolaz, and @vincentkoc.
|
||||
- Codex harness: normalize cached input tokens before session/context accounting so prompt cache reads are not double-counted in `/status`, `session_status`, or persisted `sessionEntry.totalTokens`. Fixes #69298. Thanks @richardmqq.
|
||||
|
||||
@@ -116,7 +116,7 @@ function installTestRegistry(...plugins: ChannelPlugin<TestAccount>[]) {
|
||||
|
||||
function createManager(options?: {
|
||||
channelRuntime?: PluginRuntime["channel"];
|
||||
resolveChannelRuntime?: () => PluginRuntime["channel"];
|
||||
resolveChannelRuntime?: () => PluginRuntime["channel"] | Promise<PluginRuntime["channel"]>;
|
||||
loadConfig?: () => Record<string, unknown>;
|
||||
channelIds?: ChannelId[];
|
||||
}) {
|
||||
@@ -375,6 +375,25 @@ describe("server-channels auto restart", () => {
|
||||
expect(ctx?.channelRuntime).not.toBe(channelRuntime);
|
||||
});
|
||||
|
||||
it("does not resolve channelRuntime for disabled accounts", async () => {
|
||||
const channelRuntime = createRuntimeChannel();
|
||||
const resolveChannelRuntime = vi.fn(() => channelRuntime);
|
||||
const startAccount = vi.fn(async (_ctx: ChannelGatewayContext<TestAccount>) => {});
|
||||
|
||||
installTestRegistry(
|
||||
createTestPlugin({
|
||||
startAccount,
|
||||
account: { enabled: false, configured: true },
|
||||
}),
|
||||
);
|
||||
const manager = createManager({ resolveChannelRuntime });
|
||||
|
||||
await manager.startChannels();
|
||||
|
||||
expect(resolveChannelRuntime).not.toHaveBeenCalled();
|
||||
expect(startAccount).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("fails fast when channelRuntime is not a full plugin runtime surface", async () => {
|
||||
installTestRegistry(createTestPlugin({ startAccount: vi.fn(async () => {}) }));
|
||||
const manager = createManager({
|
||||
|
||||
@@ -160,7 +160,7 @@ type ChannelManagerOptions = {
|
||||
* a channel account actually starts. The resolved value must be a real
|
||||
* `createPluginRuntime().channel` surface.
|
||||
*/
|
||||
resolveChannelRuntime?: () => ChannelRuntimeSurface;
|
||||
resolveChannelRuntime?: () => ChannelRuntimeSurface | Promise<ChannelRuntimeSurface>;
|
||||
};
|
||||
|
||||
type StartChannelOptions = {
|
||||
@@ -278,8 +278,8 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
|
||||
return next;
|
||||
};
|
||||
|
||||
const getChannelRuntime = (): ChannelRuntimeSurface | undefined => {
|
||||
return channelRuntime ?? resolveChannelRuntime?.();
|
||||
const getChannelRuntime = async (): Promise<ChannelRuntimeSurface | undefined> => {
|
||||
return channelRuntime ?? (await resolveChannelRuntime?.());
|
||||
};
|
||||
|
||||
const evictStaleChannelAccountState = (
|
||||
@@ -368,10 +368,6 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
|
||||
};
|
||||
|
||||
try {
|
||||
scopedChannelRuntime = createTaskScopedChannelRuntime({
|
||||
channelRuntime: getChannelRuntime(),
|
||||
});
|
||||
channelRuntimeForTask = scopedChannelRuntime.channelRuntime;
|
||||
const account = plugin.config.resolveAccount(cfg, id);
|
||||
const enabled = plugin.config.isEnabled
|
||||
? plugin.config.isEnabled(account, cfg)
|
||||
@@ -419,6 +415,11 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
|
||||
return;
|
||||
}
|
||||
|
||||
scopedChannelRuntime = createTaskScopedChannelRuntime({
|
||||
channelRuntime: await getChannelRuntime(),
|
||||
});
|
||||
channelRuntimeForTask = scopedChannelRuntime.channelRuntime;
|
||||
|
||||
if (!preserveRestartAttempts) {
|
||||
restartAttempts.delete(rKey);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import { startDiagnosticHeartbeat, stopDiagnosticHeartbeat } from "../logging/di
|
||||
import { createSubsystemLogger, runtimeForLogger } from "../logging/subsystem.js";
|
||||
import { getActiveBundledRuntimeDepsInstallCount } from "../plugins/bundled-runtime-deps-activity.js";
|
||||
import { runGlobalGatewayStopSafely } from "../plugins/hook-runner-global.js";
|
||||
import { createRuntimeChannel } from "../plugins/runtime/runtime-channel.js";
|
||||
import type { PluginRuntime } from "../plugins/runtime/types.js";
|
||||
import { getTotalQueueSize } from "../process/command-queue.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
@@ -117,11 +116,13 @@ const logDiscovery = log.child("discovery");
|
||||
const logTailscale = log.child("tailscale");
|
||||
const logChannels = log.child("channels");
|
||||
|
||||
let cachedChannelRuntime: PluginRuntime["channel"] | null = null;
|
||||
let cachedChannelRuntimePromise: Promise<PluginRuntime["channel"]> | null = null;
|
||||
|
||||
function getChannelRuntime() {
|
||||
cachedChannelRuntime ??= createRuntimeChannel();
|
||||
return cachedChannelRuntime;
|
||||
cachedChannelRuntimePromise ??= import("../plugins/runtime/runtime-channel.js").then(
|
||||
({ createRuntimeChannel }) => createRuntimeChannel(),
|
||||
);
|
||||
return cachedChannelRuntimePromise;
|
||||
}
|
||||
|
||||
async function closeMcpLoopbackServerOnDemand(): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user