mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 03:30:24 +00:00
fix(runtime): lazy-load setup shims and align contracts
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import fs from "node:fs/promises";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { expect, vi } from "vitest";
|
||||
@@ -7,7 +7,11 @@ import {
|
||||
createThreadBindingManager as createDiscordThreadBindingManager,
|
||||
} from "../../../../extensions/discord/runtime-api.js";
|
||||
import { createFeishuThreadBindingManager } from "../../../../extensions/feishu/api.js";
|
||||
import { createMatrixThreadBindingManager } from "../../../../extensions/matrix/api.js";
|
||||
import {
|
||||
createMatrixThreadBindingManager,
|
||||
resetMatrixThreadBindingsForTests,
|
||||
} from "../../../../extensions/matrix/api.js";
|
||||
import { setMatrixRuntime } from "../../../../extensions/matrix/index.js";
|
||||
import { createTelegramThreadBindingManager } from "../../../../extensions/telegram/runtime-api.js";
|
||||
import type { OpenClawConfig } from "../../../config/config.js";
|
||||
import {
|
||||
@@ -181,6 +185,12 @@ function expectClearedSessionBinding(params: {
|
||||
|
||||
const telegramDescribeMessageToolMock = vi.fn();
|
||||
const discordDescribeMessageToolMock = vi.fn();
|
||||
const sendMessageMatrixMock = vi.hoisted(() =>
|
||||
vi.fn(async (to: string, _message: string, opts?: { threadId?: string }) => ({
|
||||
messageId: opts?.threadId ? "$matrix-thread" : "$matrix-root",
|
||||
roomId: to.replace(/^room:/, ""),
|
||||
})),
|
||||
);
|
||||
|
||||
bundledChannelRuntimeSetters.setTelegramRuntime({
|
||||
channel: {
|
||||
@@ -213,6 +223,48 @@ bundledChannelRuntimeSetters.setLineRuntime({
|
||||
},
|
||||
} as never);
|
||||
|
||||
vi.mock("../../../../extensions/matrix/src/matrix/send.js", async () => {
|
||||
const actual = await vi.importActual<
|
||||
typeof import("../../../../extensions/matrix/src/matrix/send.js")
|
||||
>("../../../../extensions/matrix/src/matrix/send.js");
|
||||
return {
|
||||
...actual,
|
||||
sendMessageMatrix: sendMessageMatrixMock,
|
||||
};
|
||||
});
|
||||
|
||||
const matrixSessionBindingStateDir = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "openclaw-matrix-session-binding-contract-"),
|
||||
);
|
||||
const matrixSessionBindingAuth = {
|
||||
accountId: "ops",
|
||||
homeserver: "https://matrix.example.org",
|
||||
userId: "@bot:example.org",
|
||||
accessToken: "token",
|
||||
} as const;
|
||||
|
||||
function resetMatrixSessionBindingStateDir() {
|
||||
fs.rmSync(matrixSessionBindingStateDir, { recursive: true, force: true });
|
||||
fs.mkdirSync(matrixSessionBindingStateDir, { recursive: true });
|
||||
}
|
||||
|
||||
async function createContractMatrixThreadBindingManager() {
|
||||
resetMatrixSessionBindingStateDir();
|
||||
setMatrixRuntime({
|
||||
state: {
|
||||
resolveStateDir: () => matrixSessionBindingStateDir,
|
||||
},
|
||||
} as never);
|
||||
return await createMatrixThreadBindingManager({
|
||||
accountId: matrixSessionBindingAuth.accountId,
|
||||
auth: matrixSessionBindingAuth,
|
||||
client: {} as never,
|
||||
idleTimeoutMs: 24 * 60 * 60 * 1000,
|
||||
maxAgeMs: 0,
|
||||
enableSweeper: false,
|
||||
});
|
||||
}
|
||||
|
||||
export const pluginContractRegistry: PluginContractEntry[] = bundledChannelPlugins.map(
|
||||
(plugin) => ({
|
||||
id: plugin.id,
|
||||
@@ -595,24 +647,6 @@ const baseSessionBindingCfg = {
|
||||
session: { mainKey: "main", scope: "per-sender" },
|
||||
} satisfies OpenClawConfig;
|
||||
|
||||
async function createContractMatrixThreadBindingManager() {
|
||||
const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "matrix-contract-thread-bindings-"));
|
||||
return await createMatrixThreadBindingManager({
|
||||
accountId: "ops",
|
||||
auth: {
|
||||
accountId: "ops",
|
||||
homeserver: "https://matrix.example.org",
|
||||
userId: "@bot:example.org",
|
||||
accessToken: "token",
|
||||
},
|
||||
client: {} as never,
|
||||
stateDir,
|
||||
idleTimeoutMs: 24 * 60 * 60 * 1000,
|
||||
maxAgeMs: 0,
|
||||
enableSweeper: false,
|
||||
});
|
||||
}
|
||||
|
||||
export const sessionBindingContractRegistry: SessionBindingContractEntry[] = [
|
||||
{
|
||||
id: "discord",
|
||||
@@ -744,47 +778,43 @@ export const sessionBindingContractRegistry: SessionBindingContractEntry[] = [
|
||||
await createContractMatrixThreadBindingManager();
|
||||
return getSessionBindingService().getCapabilities({
|
||||
channel: "matrix",
|
||||
accountId: "ops",
|
||||
accountId: matrixSessionBindingAuth.accountId,
|
||||
});
|
||||
},
|
||||
bindAndResolve: async () => {
|
||||
await createContractMatrixThreadBindingManager();
|
||||
const service = getSessionBindingService();
|
||||
const binding = await service.bind({
|
||||
targetSessionKey: "agent:matrix:subagent:child-1",
|
||||
targetSessionKey: "agent:matrix:child:thread-1",
|
||||
targetKind: "subagent",
|
||||
conversation: {
|
||||
channel: "matrix",
|
||||
accountId: "ops",
|
||||
conversationId: "!room:example",
|
||||
accountId: matrixSessionBindingAuth.accountId,
|
||||
conversationId: "$thread",
|
||||
parentConversationId: "!room:example",
|
||||
},
|
||||
placement: "child",
|
||||
placement: "current",
|
||||
metadata: {
|
||||
label: "codex-matrix",
|
||||
introText: "intro root",
|
||||
},
|
||||
});
|
||||
expectResolvedSessionBinding({
|
||||
channel: "matrix",
|
||||
accountId: "ops",
|
||||
conversationId: "$root",
|
||||
parentConversationId: "!room:example",
|
||||
targetSessionKey: "agent:matrix:subagent:child-1",
|
||||
accountId: matrixSessionBindingAuth.accountId,
|
||||
conversationId: "$thread",
|
||||
targetSessionKey: "agent:matrix:child:thread-1",
|
||||
});
|
||||
return binding;
|
||||
},
|
||||
unbindAndVerify: unbindAndExpectClearedSessionBinding,
|
||||
cleanup: async () => {
|
||||
const manager = await createContractMatrixThreadBindingManager();
|
||||
manager.stop();
|
||||
expect(
|
||||
getSessionBindingService().resolveByConversation({
|
||||
channel: "matrix",
|
||||
accountId: "ops",
|
||||
conversationId: "$root",
|
||||
parentConversationId: "!room:example",
|
||||
}),
|
||||
).toBeNull();
|
||||
resetMatrixThreadBindingsForTests();
|
||||
resetMatrixSessionBindingStateDir();
|
||||
expectClearedSessionBinding({
|
||||
channel: "matrix",
|
||||
accountId: matrixSessionBindingAuth.accountId,
|
||||
conversationId: "$thread",
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -485,7 +485,7 @@ export function installSessionBindingContractSuite(params: {
|
||||
expectedCapabilities: SessionBindingCapabilities;
|
||||
}) {
|
||||
it("registers the expected session binding capabilities", async () => {
|
||||
expect(await params.getCapabilities()).toEqual(params.expectedCapabilities);
|
||||
expect(await Promise.resolve(params.getCapabilities())).toEqual(params.expectedCapabilities);
|
||||
});
|
||||
|
||||
it("binds and resolves a session binding through the shared service", async () => {
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { DmPolicy, GroupPolicy } from "../../config/types.js";
|
||||
import type { SecretInput } from "../../config/types.secrets.js";
|
||||
import {
|
||||
promptSecretRefForSetup,
|
||||
resolveSecretInputModeForEnvSelection,
|
||||
} from "../../plugins/provider-auth-input.js";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
|
||||
import type { WizardPrompter } from "../../wizard/prompts.js";
|
||||
import {
|
||||
@@ -18,6 +14,15 @@ import type {
|
||||
} from "./setup-wizard-types.js";
|
||||
import type { ChannelSetupWizard, ChannelSetupWizardAllowFromEntry } from "./setup-wizard.js";
|
||||
|
||||
let providerAuthInputPromise:
|
||||
| Promise<typeof import("../../plugins/provider-auth-input.js")>
|
||||
| undefined;
|
||||
|
||||
function loadProviderAuthInput() {
|
||||
providerAuthInputPromise ??= import("../../plugins/provider-auth-input.js");
|
||||
return providerAuthInputPromise;
|
||||
}
|
||||
|
||||
export const promptAccountId: PromptAccountId = async (params: PromptAccountIdParams) => {
|
||||
const existingIds = params.listAccountIds(params.cfg);
|
||||
const initial = params.currentId?.trim() || params.defaultAccountId || DEFAULT_ACCOUNT_ID;
|
||||
@@ -994,6 +999,8 @@ export async function promptSingleChannelSecretInput(params: {
|
||||
inputPrompt: string;
|
||||
preferredEnvVar?: string;
|
||||
}): Promise<SingleChannelSecretInputPromptResult> {
|
||||
const { promptSecretRefForSetup, resolveSecretInputModeForEnvSelection } =
|
||||
await loadProviderAuthInput();
|
||||
const selectedMode = await resolveSecretInputModeForEnvSelection({
|
||||
prompter: params.prompter as WizardPrompter,
|
||||
explicitMode: params.secretInputMode,
|
||||
|
||||
Reference in New Issue
Block a user