mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-06 06:41:08 +00:00
fix: restore channel sdk schema typing
This commit is contained in:
@@ -43,17 +43,17 @@ let resolveExecHostApprovalContext: typeof import("./bash-tools.exec-host-shared
|
||||
let sendExecApprovalFollowup: typeof import("./bash-tools.exec-approval-followup.js").sendExecApprovalFollowup;
|
||||
let logWarn: typeof import("../logger.js").logWarn;
|
||||
|
||||
describe("sendExecApprovalFollowupResult", () => {
|
||||
beforeAll(async () => {
|
||||
({
|
||||
sendExecApprovalFollowupResult,
|
||||
MAX_EXEC_APPROVAL_FOLLOWUP_FAILURE_LOG_KEYS: maxExecApprovalFollowupFailureLogKeys,
|
||||
resolveExecHostApprovalContext,
|
||||
} = await import("./bash-tools.exec-host-shared.js"));
|
||||
({ sendExecApprovalFollowup } = await import("./bash-tools.exec-approval-followup.js"));
|
||||
({ logWarn } = await import("../logger.js"));
|
||||
});
|
||||
beforeAll(async () => {
|
||||
({
|
||||
sendExecApprovalFollowupResult,
|
||||
MAX_EXEC_APPROVAL_FOLLOWUP_FAILURE_LOG_KEYS: maxExecApprovalFollowupFailureLogKeys,
|
||||
resolveExecHostApprovalContext,
|
||||
} = await import("./bash-tools.exec-host-shared.js"));
|
||||
({ sendExecApprovalFollowup } = await import("./bash-tools.exec-approval-followup.js"));
|
||||
({ logWarn } = await import("../logger.js"));
|
||||
});
|
||||
|
||||
describe("sendExecApprovalFollowupResult", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(sendExecApprovalFollowup).mockReset();
|
||||
vi.mocked(logWarn).mockReset();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
import { buildChannelConfigSchema } from "./config-schema.js";
|
||||
import { buildChannelConfigSchema, emptyChannelConfigSchema } from "./config-schema.js";
|
||||
|
||||
describe("buildChannelConfigSchema", () => {
|
||||
it("builds json schema when toJSONSchema is available", () => {
|
||||
@@ -46,3 +46,22 @@ describe("buildChannelConfigSchema", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("emptyChannelConfigSchema", () => {
|
||||
it("accepts undefined and empty objects only", () => {
|
||||
const result = emptyChannelConfigSchema();
|
||||
|
||||
expect(result.runtime?.safeParse(undefined)).toEqual({
|
||||
success: true,
|
||||
data: undefined,
|
||||
});
|
||||
expect(result.runtime?.safeParse({})).toEqual({
|
||||
success: true,
|
||||
data: {},
|
||||
});
|
||||
expect(result.runtime?.safeParse({ enabled: true })).toEqual({
|
||||
success: false,
|
||||
issues: [{ path: [], message: "config must be empty" }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -104,3 +104,33 @@ export function buildChannelConfigSchema(
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function emptyChannelConfigSchema(): ChannelConfigSchema {
|
||||
return {
|
||||
schema: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {},
|
||||
},
|
||||
runtime: {
|
||||
safeParse(value) {
|
||||
if (value === undefined) {
|
||||
return { success: true, data: undefined };
|
||||
}
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
return {
|
||||
success: false,
|
||||
issues: [{ path: [], message: "expected config object" }],
|
||||
};
|
||||
}
|
||||
if (Object.keys(value as Record<string, unknown>).length > 0) {
|
||||
return {
|
||||
success: false,
|
||||
issues: [{ path: [], message: "config must be empty" }],
|
||||
};
|
||||
}
|
||||
return { success: true, data: value };
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { emptyChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
import { buildAccountScopedDmSecurityPolicy } from "../channels/plugins/helpers.js";
|
||||
import {
|
||||
createScopedAccountReplyToModeResolver,
|
||||
@@ -14,14 +15,17 @@ import type {
|
||||
ChannelPollResult,
|
||||
ChannelThreadingAdapter,
|
||||
} from "../channels/plugins/types.core.js";
|
||||
import type { ChannelConfigUiHint, ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
import type {
|
||||
ChannelConfigSchema,
|
||||
ChannelConfigUiHint,
|
||||
ChannelPlugin,
|
||||
} from "../channels/plugins/types.plugin.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { ReplyToMode } from "../config/types.base.js";
|
||||
import { buildOutboundBaseSessionKey } from "../infra/outbound/base-session-key.js";
|
||||
import type { OutboundDeliveryResult } from "../infra/outbound/deliver.js";
|
||||
import { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||
import type { PluginRuntime } from "../plugins/runtime/types.js";
|
||||
import type { OpenClawPluginApi, OpenClawPluginConfigSchema } from "../plugins/types.js";
|
||||
import type { OpenClawPluginApi } from "../plugins/types.js";
|
||||
|
||||
export type { ChannelConfigUiHint, ChannelPlugin };
|
||||
export type { OpenClawConfig };
|
||||
@@ -31,12 +35,17 @@ export type ChannelOutboundSessionRouteParams = Parameters<
|
||||
NonNullable<ChannelMessagingAdapter["resolveOutboundSessionRoute"]>
|
||||
>[0];
|
||||
|
||||
type ChannelEntryConfigSchema<TPlugin> =
|
||||
TPlugin extends ChannelPlugin<unknown>
|
||||
? NonNullable<TPlugin["configSchema"]>
|
||||
: ChannelConfigSchema;
|
||||
|
||||
type DefineChannelPluginEntryOptions<TPlugin = ChannelPlugin> = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
plugin: TPlugin;
|
||||
configSchema?: OpenClawPluginConfigSchema | (() => OpenClawPluginConfigSchema);
|
||||
configSchema?: ChannelEntryConfigSchema<TPlugin> | (() => ChannelEntryConfigSchema<TPlugin>);
|
||||
setRuntime?: (runtime: PluginRuntime) => void;
|
||||
registerCliMetadata?: (api: OpenClawPluginApi) => void;
|
||||
registerFull?: (api: OpenClawPluginApi) => void;
|
||||
@@ -46,7 +55,7 @@ type DefinedChannelPluginEntry<TPlugin> = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
configSchema: OpenClawPluginConfigSchema;
|
||||
configSchema: ChannelEntryConfigSchema<TPlugin>;
|
||||
register: (api: OpenClawPluginApi) => void;
|
||||
channelPlugin: TPlugin;
|
||||
setChannelRuntime?: (runtime: PluginRuntime) => void;
|
||||
@@ -270,12 +279,15 @@ export function defineChannelPluginEntry<TPlugin>({
|
||||
name,
|
||||
description,
|
||||
plugin,
|
||||
configSchema = emptyPluginConfigSchema,
|
||||
configSchema,
|
||||
setRuntime,
|
||||
registerCliMetadata,
|
||||
registerFull,
|
||||
}: DefineChannelPluginEntryOptions<TPlugin>): DefinedChannelPluginEntry<TPlugin> {
|
||||
const resolvedConfigSchema = typeof configSchema === "function" ? configSchema() : configSchema;
|
||||
const resolvedConfigSchema: ChannelEntryConfigSchema<TPlugin> =
|
||||
typeof configSchema === "function"
|
||||
? configSchema()
|
||||
: ((configSchema ?? emptyChannelConfigSchema()) as ChannelEntryConfigSchema<TPlugin>);
|
||||
const entry = {
|
||||
id,
|
||||
name,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CHAT_CHANNEL_ORDER, type ChatChannelId } from "../channels/ids.js";
|
||||
import { emptyChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
import { buildAccountScopedDmSecurityPolicy } from "../channels/plugins/helpers.js";
|
||||
import {
|
||||
createScopedAccountReplyToModeResolver,
|
||||
@@ -22,7 +23,6 @@ import type { ReplyToMode } from "../config/types.base.js";
|
||||
import { buildOutboundBaseSessionKey } from "../infra/outbound/base-session-key.js";
|
||||
import type { OutboundDeliveryResult } from "../infra/outbound/deliver.js";
|
||||
import { listBundledPluginMetadata } from "../plugins/bundled-plugin-metadata.js";
|
||||
import { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||
import type { PluginPackageChannel } from "../plugins/manifest.js";
|
||||
import type { PluginRuntime } from "../plugins/runtime/types.js";
|
||||
import type { OpenClawPluginApi } from "../plugins/types.js";
|
||||
@@ -144,7 +144,10 @@ export { createDedupeCache, resolveGlobalDedupeCache } from "../infra/dedupe.js"
|
||||
export { generateSecureToken, generateSecureUuid } from "../infra/secure-random.js";
|
||||
export { delegateCompactionToRuntime } from "../context-engine/delegate.js";
|
||||
export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
|
||||
export { buildChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
export {
|
||||
buildChannelConfigSchema,
|
||||
emptyChannelConfigSchema,
|
||||
} from "../channels/plugins/config-schema.js";
|
||||
export {
|
||||
applyAccountNameToChannelSection,
|
||||
migrateBaseNameToDefaultAccount,
|
||||
@@ -356,35 +359,18 @@ export function buildChannelOutboundSessionRoute(params: {
|
||||
};
|
||||
}
|
||||
|
||||
const emptyChannelConfigSchema: ChannelConfigSchema = {
|
||||
schema: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {},
|
||||
},
|
||||
runtime: {
|
||||
safeParse(value: unknown) {
|
||||
if (value === undefined) {
|
||||
return { success: true, data: undefined };
|
||||
}
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
return { success: false, issues: [{ path: [], message: "expected config object" }] };
|
||||
}
|
||||
if (Object.keys(value as Record<string, unknown>).length > 0) {
|
||||
return { success: false, issues: [{ path: [], message: "config must be empty" }] };
|
||||
}
|
||||
return { success: true, data: value };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/** Options for a channel plugin entry that should register a channel capability. */
|
||||
type ChannelEntryConfigSchema<TPlugin> =
|
||||
TPlugin extends ChannelPlugin<unknown>
|
||||
? NonNullable<TPlugin["configSchema"]>
|
||||
: ChannelConfigSchema;
|
||||
|
||||
type DefineChannelPluginEntryOptions<TPlugin = ChannelPlugin> = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
plugin: TPlugin;
|
||||
configSchema?: ChannelConfigSchema | (() => ChannelConfigSchema);
|
||||
configSchema?: ChannelEntryConfigSchema<TPlugin> | (() => ChannelEntryConfigSchema<TPlugin>);
|
||||
setRuntime?: (runtime: PluginRuntime) => void;
|
||||
registerCliMetadata?: (api: OpenClawPluginApi) => void;
|
||||
registerFull?: (api: OpenClawPluginApi) => void;
|
||||
@@ -394,7 +380,7 @@ type DefinedChannelPluginEntry<TPlugin> = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
configSchema: ChannelConfigSchema;
|
||||
configSchema: ChannelEntryConfigSchema<TPlugin>;
|
||||
register: (api: OpenClawPluginApi) => void;
|
||||
channelPlugin: TPlugin;
|
||||
setChannelRuntime?: (runtime: PluginRuntime) => void;
|
||||
@@ -452,16 +438,17 @@ export function defineChannelPluginEntry<TPlugin>({
|
||||
name,
|
||||
description,
|
||||
plugin,
|
||||
configSchema = emptyChannelConfigSchema,
|
||||
configSchema,
|
||||
setRuntime,
|
||||
registerCliMetadata,
|
||||
registerFull,
|
||||
}: DefineChannelPluginEntryOptions<TPlugin>): DefinedChannelPluginEntry<TPlugin> {
|
||||
let resolvedConfigSchema: ChannelConfigSchema | undefined;
|
||||
const getConfigSchema = (): ChannelConfigSchema => {
|
||||
let resolvedConfigSchema: ChannelEntryConfigSchema<TPlugin> | undefined;
|
||||
const getConfigSchema = (): ChannelEntryConfigSchema<TPlugin> => {
|
||||
resolvedConfigSchema ??=
|
||||
(typeof configSchema === "function" ? configSchema() : configSchema) ??
|
||||
emptyChannelConfigSchema;
|
||||
typeof configSchema === "function"
|
||||
? configSchema()
|
||||
: ((configSchema ?? emptyChannelConfigSchema()) as ChannelEntryConfigSchema<TPlugin>);
|
||||
return resolvedConfigSchema;
|
||||
};
|
||||
const entry = {
|
||||
|
||||
Reference in New Issue
Block a user