mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +00:00
test(ci): reduce channel contract import cost
This commit is contained in:
1
extensions/feishu/channel-plugin-api.ts
Normal file
1
extensions/feishu/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { feishuPlugin } from "./src/channel.js";
|
||||
@@ -67,7 +67,7 @@ export default defineBundledChannelEntry({
|
||||
description: "Feishu/Lark channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "feishuPlugin",
|
||||
},
|
||||
secrets: {
|
||||
|
||||
@@ -49,7 +49,6 @@ import {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
} from "./channel-runtime-api.js";
|
||||
import { createFeishuClient } from "./client.js";
|
||||
import { isRecord } from "./comment-shared.js";
|
||||
import { FeishuConfigSchema } from "./config-schema.js";
|
||||
import {
|
||||
@@ -119,6 +118,11 @@ const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(
|
||||
"feishuChannelRuntime",
|
||||
);
|
||||
|
||||
async function createFeishuActionClient(account: ResolvedFeishuAccount) {
|
||||
const { createFeishuClient } = await import("./client.js");
|
||||
return createFeishuClient(account);
|
||||
}
|
||||
|
||||
const collectFeishuSecurityWarnings = createAllowlistProviderGroupPolicyWarningCollector<{
|
||||
cfg: ClawdbotConfig;
|
||||
accountId?: string | null;
|
||||
@@ -841,7 +845,7 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount, FeishuProbeResul
|
||||
throw new Error("Feishu channel-info requires chatId or channelId.");
|
||||
}
|
||||
const runtime = await loadFeishuChannelRuntime();
|
||||
const client = createFeishuClient(account);
|
||||
const client = await createFeishuActionClient(account);
|
||||
const channel = await runtime.getChatInfo(client, chatId);
|
||||
const includeMembers =
|
||||
ctx.params.includeMembers === true || ctx.params.members === true;
|
||||
@@ -871,7 +875,7 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount, FeishuProbeResul
|
||||
|
||||
if (ctx.action === "member-info") {
|
||||
const runtime = await loadFeishuChannelRuntime();
|
||||
const client = createFeishuClient(account);
|
||||
const client = await createFeishuActionClient(account);
|
||||
const memberId = resolveFeishuMemberId(ctx.params);
|
||||
if (memberId) {
|
||||
const member = await runtime.getFeishuMemberInfo(
|
||||
|
||||
@@ -13,15 +13,7 @@ import {
|
||||
type SecretInput,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
import { inspectFeishuCredentials, resolveDefaultFeishuAccountId } from "./accounts.js";
|
||||
import {
|
||||
beginAppRegistration,
|
||||
getAppOwnerOpenId,
|
||||
initAppRegistration,
|
||||
pollAppRegistration,
|
||||
printQrCode,
|
||||
type AppRegistrationResult,
|
||||
} from "./app-registration.js";
|
||||
import { probeFeishu } from "./probe.js";
|
||||
import type { AppRegistrationResult } from "./app-registration.js";
|
||||
import type { FeishuConfig, FeishuDomain } from "./types.js";
|
||||
|
||||
const channel = "feishu" as const;
|
||||
@@ -254,6 +246,8 @@ function applyNewAppSecurityPolicy(
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function runScanToCreate(prompter: WizardPrompter): Promise<AppRegistrationResult | null> {
|
||||
const { beginAppRegistration, initAppRegistration, pollAppRegistration, printQrCode } =
|
||||
await import("./app-registration.js");
|
||||
try {
|
||||
await initAppRegistration("feishu");
|
||||
} catch {
|
||||
@@ -371,6 +365,7 @@ async function runNewAppFlow(params: {
|
||||
|
||||
// Fetch openId via API for manual flow.
|
||||
if (appId && appSecretProbeValue) {
|
||||
const { getAppOwnerOpenId } = await import("./app-registration.js");
|
||||
scanOpenId = await getAppOwnerOpenId({
|
||||
appId,
|
||||
appSecret: appSecretProbeValue,
|
||||
@@ -528,6 +523,7 @@ export const feishuSetupWizard: ChannelSetupWizard = {
|
||||
let probeResult = null;
|
||||
if (configured && resolvedCredentials) {
|
||||
try {
|
||||
const { probeFeishu } = await import("./probe.js");
|
||||
probeResult = await probeFeishu(resolvedCredentials);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
1
extensions/googlechat/channel-plugin-api.ts
Normal file
1
extensions/googlechat/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { googlechatPlugin } from "./src/channel.js";
|
||||
@@ -6,7 +6,7 @@ export default defineBundledChannelEntry({
|
||||
description: "OpenClaw Google Chat channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "googlechatPlugin",
|
||||
},
|
||||
secrets: {
|
||||
|
||||
1
extensions/imessage/channel-plugin-api.ts
Normal file
1
extensions/imessage/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { imessagePlugin } from "./src/channel.js";
|
||||
@@ -6,7 +6,7 @@ export default defineBundledChannelEntry({
|
||||
description: "iMessage channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "imessagePlugin",
|
||||
},
|
||||
runtime: {
|
||||
|
||||
1
extensions/line/channel-plugin-api.ts
Normal file
1
extensions/line/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { linePlugin } from "./src/channel.js";
|
||||
@@ -31,7 +31,7 @@ export default defineBundledChannelEntry({
|
||||
description: "LINE Messaging API channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "linePlugin",
|
||||
},
|
||||
runtime: {
|
||||
|
||||
1
extensions/msteams/channel-plugin-api.ts
Normal file
1
extensions/msteams/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { msteamsPlugin } from "./src/channel.js";
|
||||
@@ -6,7 +6,7 @@ export default defineBundledChannelEntry({
|
||||
description: "Microsoft Teams channel plugin (Bot Framework)",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "msteamsPlugin",
|
||||
},
|
||||
secrets: {
|
||||
|
||||
1
extensions/nextcloud-talk/channel-plugin-api.ts
Normal file
1
extensions/nextcloud-talk/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { nextcloudTalkPlugin } from "./src/channel.js";
|
||||
@@ -6,7 +6,7 @@ export default defineBundledChannelEntry({
|
||||
description: "Nextcloud Talk channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "nextcloudTalkPlugin",
|
||||
},
|
||||
secrets: {
|
||||
|
||||
1
extensions/nostr/channel-plugin-api.ts
Normal file
1
extensions/nostr/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { nostrPlugin } from "./src/channel.js";
|
||||
@@ -35,7 +35,7 @@ export default defineBundledChannelEntry({
|
||||
description: "Nostr DM channel plugin via NIP-04",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "nostrPlugin",
|
||||
},
|
||||
runtime: {
|
||||
|
||||
1
extensions/qa-channel/channel-plugin-api.ts
Normal file
1
extensions/qa-channel/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { qaChannelPlugin } from "./src/channel.js";
|
||||
@@ -6,7 +6,7 @@ export default defineBundledChannelEntry({
|
||||
description: "Synthetic QA channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "qaChannelPlugin",
|
||||
},
|
||||
runtime: {
|
||||
|
||||
1
extensions/qqbot/channel-plugin-api.ts
Normal file
1
extensions/qqbot/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { qqbotPlugin } from "./src/channel.js";
|
||||
@@ -95,7 +95,7 @@ export default defineBundledChannelEntry({
|
||||
description: "QQ Bot channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "qqbotPlugin",
|
||||
},
|
||||
runtime: {
|
||||
|
||||
1
extensions/synology-chat/channel-plugin-api.ts
Normal file
1
extensions/synology-chat/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { synologyChatPlugin } from "./src/channel.js";
|
||||
@@ -6,7 +6,7 @@ export default defineBundledChannelEntry({
|
||||
description: "Native Synology Chat channel plugin for OpenClaw",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "synologyChatPlugin",
|
||||
},
|
||||
runtime: {
|
||||
|
||||
1
extensions/tlon/channel-plugin-api.ts
Normal file
1
extensions/tlon/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { tlonPlugin } from "./src/channel.js";
|
||||
@@ -119,7 +119,7 @@ export default defineBundledChannelEntry({
|
||||
description: "Tlon/Urbit channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "tlonPlugin",
|
||||
},
|
||||
runtime: {
|
||||
|
||||
1
extensions/twitch/channel-plugin-api.ts
Normal file
1
extensions/twitch/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { twitchPlugin } from "./src/plugin.js";
|
||||
@@ -6,7 +6,7 @@ export default defineBundledChannelEntry({
|
||||
description: "Twitch IRC chat channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "twitchPlugin",
|
||||
},
|
||||
runtime: {
|
||||
|
||||
1
extensions/zalo/channel-plugin-api.ts
Normal file
1
extensions/zalo/channel-plugin-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { zaloPlugin } from "./src/channel.js";
|
||||
@@ -6,7 +6,7 @@ export default defineBundledChannelEntry({
|
||||
description: "Zalo channel plugin",
|
||||
importMetaUrl: import.meta.url,
|
||||
plugin: {
|
||||
specifier: "./api.js",
|
||||
specifier: "./channel-plugin-api.js",
|
||||
exportName: "zaloPlugin",
|
||||
},
|
||||
secrets: {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { expect } from "vitest";
|
||||
import { ensureAuthProfileStore, type AuthProfileStore } from "../agents/auth-profiles.js";
|
||||
import type { AuthProfileStore } from "../agents/auth-profiles.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { PluginOrigin } from "../plugins/plugin-origin.types.js";
|
||||
import type { captureEnv } from "../test-utils/env.js";
|
||||
import { getActiveSecretsRuntimeSnapshot } from "./runtime.js";
|
||||
|
||||
export const OPENAI_ENV_KEY_REF = {
|
||||
source: "env",
|
||||
@@ -102,7 +103,10 @@ export function createOpenAIFileRuntimeConfig(secretFile: string): OpenClawConfi
|
||||
|
||||
export function expectResolvedOpenAIRuntime(agentDir: string) {
|
||||
expect(loadConfig().models?.providers?.openai?.apiKey).toBe("sk-file-runtime");
|
||||
expect(ensureAuthProfileStore(agentDir).profiles["openai:default"]).toMatchObject({
|
||||
const activeAuthStore = getActiveSecretsRuntimeSnapshot()?.authStores.find(
|
||||
(entry) => entry.agentDir === agentDir,
|
||||
)?.store;
|
||||
expect(activeAuthStore?.profiles["openai:default"]).toMatchObject({
|
||||
type: "api_key",
|
||||
key: "sk-file-runtime",
|
||||
});
|
||||
|
||||
55
test/helpers/channels/bundled-channel-plugin-loader.ts
Normal file
55
test/helpers/channels/bundled-channel-plugin-loader.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { listBundledChannelPluginIds as listCatalogBundledChannelPluginIds } from "../../../src/channels/plugins/bundled-ids.js";
|
||||
import type { ChannelId } from "../../../src/channels/plugins/channel-id.types.js";
|
||||
import type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
|
||||
import {
|
||||
listChannelCatalogEntries,
|
||||
type PluginChannelCatalogEntry,
|
||||
} from "../../../src/plugins/channel-catalog-registry.js";
|
||||
import { loadBundledPluginPublicSurfaceSync } from "../../../src/test-utils/bundled-plugin-public-surface.js";
|
||||
|
||||
type ChannelPluginApiModule = Record<string, unknown>;
|
||||
|
||||
const channelPluginCache = new Map<ChannelId, ChannelPlugin | null>();
|
||||
let channelCatalogEntries: PluginChannelCatalogEntry[] | undefined;
|
||||
|
||||
function isChannelPlugin(value: unknown): value is ChannelPlugin {
|
||||
return (
|
||||
Boolean(value) &&
|
||||
typeof value === "object" &&
|
||||
typeof (value as Partial<ChannelPlugin>).id === "string" &&
|
||||
Boolean((value as Partial<ChannelPlugin>).meta) &&
|
||||
Boolean((value as Partial<ChannelPlugin>).config)
|
||||
);
|
||||
}
|
||||
|
||||
export function listBundledChannelPluginIds(): readonly ChannelId[] {
|
||||
return listCatalogBundledChannelPluginIds() as ChannelId[];
|
||||
}
|
||||
|
||||
export function getBundledChannelCatalogEntry(
|
||||
id: ChannelId,
|
||||
): PluginChannelCatalogEntry | undefined {
|
||||
channelCatalogEntries ??= listChannelCatalogEntries({ origin: "bundled" });
|
||||
return channelCatalogEntries.find((entry) => entry.pluginId === id || entry.channel.id === id);
|
||||
}
|
||||
|
||||
export function getBundledChannelPlugin(id: ChannelId): ChannelPlugin | undefined {
|
||||
if (channelPluginCache.has(id)) {
|
||||
return channelPluginCache.get(id) ?? undefined;
|
||||
}
|
||||
|
||||
const loaded = loadBundledPluginPublicSurfaceSync<ChannelPluginApiModule>({
|
||||
pluginId: id,
|
||||
artifactBasename: "channel-plugin-api.js",
|
||||
});
|
||||
const plugin = Object.values(loaded).find(isChannelPlugin) ?? null;
|
||||
channelPluginCache.set(id, plugin);
|
||||
return plugin ?? undefined;
|
||||
}
|
||||
|
||||
export function listBundledChannelPlugins(): readonly ChannelPlugin[] {
|
||||
return listBundledChannelPluginIds().flatMap((id) => {
|
||||
const plugin = getBundledChannelPlugin(id);
|
||||
return plugin ? [plugin] : [];
|
||||
});
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import {
|
||||
getBundledChannelPlugin,
|
||||
listBundledChannelPluginIds,
|
||||
listBundledChannelPlugins,
|
||||
} from "../../../src/channels/plugins/bundled.js";
|
||||
import type { ChannelId } from "../../../src/channels/plugins/channel-id.types.js";
|
||||
import { normalizeChannelMeta } from "../../../src/channels/plugins/meta-normalization.js";
|
||||
import type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
|
||||
import {
|
||||
getBundledChannelCatalogEntry,
|
||||
getBundledChannelPlugin,
|
||||
listBundledChannelPluginIds,
|
||||
listBundledChannelPlugins,
|
||||
} from "./bundled-channel-plugin-loader.js";
|
||||
|
||||
type PluginContractEntry = {
|
||||
id: string;
|
||||
@@ -13,11 +14,12 @@ type PluginContractEntry = {
|
||||
};
|
||||
|
||||
function toPluginContractEntry(plugin: ChannelPlugin): PluginContractEntry {
|
||||
const existingMeta = getBundledChannelCatalogEntry(plugin.id)?.channel;
|
||||
return {
|
||||
id: plugin.id,
|
||||
plugin: {
|
||||
...plugin,
|
||||
meta: normalizeChannelMeta({ id: plugin.id, meta: plugin.meta }),
|
||||
meta: normalizeChannelMeta({ id: plugin.id, meta: plugin.meta, existing: existingMeta }),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import {
|
||||
getBundledChannelPlugin,
|
||||
listBundledChannelPluginIds,
|
||||
listBundledChannelPlugins,
|
||||
setBundledChannelRuntime,
|
||||
} from "../../../src/channels/plugins/bundled.js";
|
||||
import type { ChannelId } from "../../../src/channels/plugins/channel-id.types.js";
|
||||
import type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import {
|
||||
listLineAccountIds,
|
||||
resolveDefaultLineAccountId,
|
||||
resolveLineAccount,
|
||||
} from "../../../src/plugin-sdk/line.js";
|
||||
getBundledChannelPlugin,
|
||||
listBundledChannelPluginIds,
|
||||
listBundledChannelPlugins,
|
||||
} from "./bundled-channel-plugin-loader.js";
|
||||
import { channelPluginSurfaceKeys, type ChannelPluginSurface } from "./manifest.js";
|
||||
|
||||
type SurfaceContractEntry = {
|
||||
@@ -44,17 +38,6 @@ type DirectoryContractEntry = {
|
||||
accountId?: string;
|
||||
};
|
||||
|
||||
setBundledChannelRuntime("line", {
|
||||
channel: {
|
||||
line: {
|
||||
listLineAccountIds,
|
||||
resolveDefaultLineAccountId,
|
||||
resolveLineAccount: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string }) =>
|
||||
resolveLineAccount({ cfg, accountId }),
|
||||
},
|
||||
},
|
||||
} as never);
|
||||
|
||||
let surfaceContractRegistryCache: SurfaceContractEntry[] | undefined;
|
||||
const surfaceContractEntryCache = new Map<ChannelId, SurfaceContractEntry | null>();
|
||||
let threadingContractRegistryCache: ThreadingContractEntry[] | undefined;
|
||||
|
||||
@@ -7,9 +7,18 @@ import type {
|
||||
} from "../../../src/channels/plugins/types.core.js";
|
||||
import type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import { createNonExitingRuntime } from "../../../src/runtime.js";
|
||||
import type { RuntimeEnv } from "../../../src/runtime.js";
|
||||
|
||||
const contractRuntime = createNonExitingRuntime();
|
||||
let contractRuntime: RuntimeEnv | undefined;
|
||||
|
||||
async function getDirectoryContractRuntime(): Promise<RuntimeEnv> {
|
||||
if (contractRuntime) {
|
||||
return contractRuntime;
|
||||
}
|
||||
const { createNonExitingRuntime } = await import("../../../src/runtime.js");
|
||||
contractRuntime = createNonExitingRuntime();
|
||||
return contractRuntime;
|
||||
}
|
||||
|
||||
function expectDirectoryEntryShape(entry: ChannelDirectoryEntry) {
|
||||
expect(["user", "group", "channel"]).toContain(entry.kind);
|
||||
@@ -177,10 +186,11 @@ export function installChannelDirectoryContractSuite(params: {
|
||||
if (params.coverage === "presence") {
|
||||
return;
|
||||
}
|
||||
const runtime = await getDirectoryContractRuntime();
|
||||
const self = await directory?.self?.({
|
||||
cfg: params.cfg ?? ({} as OpenClawConfig),
|
||||
accountId: params.accountId ?? "default",
|
||||
runtime: contractRuntime,
|
||||
runtime,
|
||||
});
|
||||
if (self) {
|
||||
expectDirectoryEntryShape(self);
|
||||
@@ -192,7 +202,7 @@ export function installChannelDirectoryContractSuite(params: {
|
||||
accountId: params.accountId ?? "default",
|
||||
query: "",
|
||||
limit: 5,
|
||||
runtime: contractRuntime,
|
||||
runtime,
|
||||
})) ?? [];
|
||||
expect(Array.isArray(peers)).toBe(true);
|
||||
for (const peer of peers) {
|
||||
@@ -205,7 +215,7 @@ export function installChannelDirectoryContractSuite(params: {
|
||||
accountId: params.accountId ?? "default",
|
||||
query: "",
|
||||
limit: 5,
|
||||
runtime: contractRuntime,
|
||||
runtime,
|
||||
})) ?? [];
|
||||
expect(Array.isArray(groups)).toBe(true);
|
||||
for (const group of groups) {
|
||||
@@ -218,7 +228,7 @@ export function installChannelDirectoryContractSuite(params: {
|
||||
accountId: params.accountId ?? "default",
|
||||
groupId: groups[0].id,
|
||||
limit: 5,
|
||||
runtime: contractRuntime,
|
||||
runtime,
|
||||
});
|
||||
expect(Array.isArray(members)).toBe(true);
|
||||
for (const member of members) {
|
||||
|
||||
Reference in New Issue
Block a user