Main recovery: restore formatter and contract checks (#49570)
* Extensions: fix oxfmt drift on main * Plugins: restore runtime barrel exports on main * Config: restore web search compatibility types * Telegram: align test harness with reply runtime * Plugin SDK: fix channel config accessor generics * CLI: remove redundant search provider casts * Tests: restore main typecheck coverage * Lobster: fix test import formatting * Extensions: route bundled seams through plugin-sdk * Tests: use extension env helper for xai * Image generation: fix main oxfmt drift * Config: restore latest main compatibility checks * Plugin SDK: align guardrail tests with lint * Telegram: type native command skill mock
This commit is contained in:
parent
e6c6aaa11b
commit
fbd88e2c8f
@ -1,4 +1,4 @@
|
||||
import type { AcpRuntimeEvent, AcpSessionUpdateTag } from "../runtime-api.js";
|
||||
import type { AcpRuntimeEvent, AcpSessionUpdateTag } from "../../runtime-api.js";
|
||||
import {
|
||||
asOptionalBoolean,
|
||||
asOptionalString,
|
||||
|
||||
@ -25,7 +25,7 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
const wrapped = provider.wrapStreamFn?.({
|
||||
provider: "amazon-bedrock",
|
||||
modelId: "amazon.nova-micro-v1:0",
|
||||
streamFn: (_model, _context, options) => options,
|
||||
streamFn: (_model: unknown, _context: unknown, options: Record<string, unknown>) => options,
|
||||
} as never);
|
||||
|
||||
expect(
|
||||
|
||||
@ -132,8 +132,8 @@ function resolveBraveConfig(
|
||||
: ({ apiKey: (searchConfig as Record<string, unknown> | undefined)?.apiKey } as BraveConfig);
|
||||
}
|
||||
|
||||
function resolveBraveMode(brave: BraveConfig): "web" | "llm-context" {
|
||||
return brave.mode === "llm-context" ? "llm-context" : "web";
|
||||
function resolveBraveMode(brave?: BraveConfig): "web" | "llm-context" {
|
||||
return brave?.mode === "llm-context" ? "llm-context" : "web";
|
||||
}
|
||||
|
||||
function resolveBraveApiKey(
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/copilot-proxy.js";
|
||||
export * from "openclaw/plugin-sdk/copilot-proxy";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/device-pair.js";
|
||||
export * from "openclaw/plugin-sdk/device-pair";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/diagnostics-otel.js";
|
||||
export * from "openclaw/plugin-sdk/diagnostics-otel";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/diffs.js";
|
||||
export * from "openclaw/plugin-sdk/diffs";
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
import {
|
||||
applyDirectoryQueryAndLimit,
|
||||
collectNormalizedDirectoryIds,
|
||||
inspectReadOnlyChannelAccount,
|
||||
toDirectoryEntries,
|
||||
type DirectoryConfigParams,
|
||||
} from "openclaw/plugin-sdk/directory-runtime";
|
||||
import type { InspectedDiscordAccount } from "../api.js";
|
||||
import { inspectDiscordAccount, type InspectedDiscordAccount } from "../api.js";
|
||||
|
||||
export async function listDiscordDirectoryPeersFromConfig(params: DirectoryConfigParams) {
|
||||
const account = (await inspectReadOnlyChannelAccount({
|
||||
channelId: "discord",
|
||||
const account = inspectDiscordAccount({
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
})) as InspectedDiscordAccount | null;
|
||||
}) as InspectedDiscordAccount | null;
|
||||
if (!account || !("config" in account)) {
|
||||
return [];
|
||||
}
|
||||
@ -34,11 +32,10 @@ export async function listDiscordDirectoryPeersFromConfig(params: DirectoryConfi
|
||||
}
|
||||
|
||||
export async function listDiscordDirectoryGroupsFromConfig(params: DirectoryConfigParams) {
|
||||
const account = (await inspectReadOnlyChannelAccount({
|
||||
channelId: "discord",
|
||||
const account = inspectDiscordAccount({
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
})) as InspectedDiscordAccount | null;
|
||||
}) as InspectedDiscordAccount | null;
|
||||
if (!account || !("config" in account)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ export type {
|
||||
ChannelMessageActionAdapter,
|
||||
ChannelMessageActionName,
|
||||
} from "openclaw/plugin-sdk/channel-runtime";
|
||||
export type { DiscordConfig } from "openclaw/plugin-sdk/discord";
|
||||
export {
|
||||
assertMediaNotDataUrl,
|
||||
parseAvailableTags,
|
||||
|
||||
@ -1 +1 @@
|
||||
export { normalizeGoogleModelId, parseGeminiAuth } from "openclaw/plugin-sdk/google";
|
||||
export * from "openclaw/plugin-sdk/google";
|
||||
|
||||
@ -1,23 +1,19 @@
|
||||
export type { IMessageAccountConfig } from "../../src/config/types.imessage.js";
|
||||
export type { ChannelPlugin } from "../../src/channels/plugins/types.plugin.js";
|
||||
export {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
buildChannelConfigSchema,
|
||||
getChatChannelMeta,
|
||||
} from "../../src/plugin-sdk/channel-plugin-common.js";
|
||||
export {
|
||||
collectStatusIssuesFromLastError,
|
||||
formatTrimmedAllowFromEntries,
|
||||
resolveIMessageConfigAllowFrom,
|
||||
resolveIMessageConfigDefaultTo,
|
||||
} from "../../src/plugin-sdk/channel-config-helpers.js";
|
||||
export { collectStatusIssuesFromLastError } from "../../src/plugin-sdk/status-helpers.js";
|
||||
export { resolveChannelMediaMaxBytes } from "../../src/channels/plugins/media-limits.js";
|
||||
export {
|
||||
getChatChannelMeta,
|
||||
looksLikeIMessageTargetId,
|
||||
normalizeIMessageMessagingTarget,
|
||||
} from "../../src/channels/plugins/normalize/imessage.js";
|
||||
export { IMessageConfigSchema } from "../../src/config/zod-schema.providers-core.js";
|
||||
resolveChannelMediaMaxBytes,
|
||||
resolveIMessageConfigAllowFrom,
|
||||
resolveIMessageConfigDefaultTo,
|
||||
IMessageConfigSchema,
|
||||
type ChannelPlugin,
|
||||
type IMessageAccountConfig,
|
||||
} from "openclaw/plugin-sdk/imessage";
|
||||
export {
|
||||
resolveIMessageGroupRequireMention,
|
||||
resolveIMessageGroupToolPolicy,
|
||||
|
||||
@ -1,53 +1 @@
|
||||
export {
|
||||
addWildcardAllowFrom,
|
||||
buildBaseAccountStatusSnapshot,
|
||||
buildBaseChannelStatusSummary,
|
||||
buildChannelConfigSchema,
|
||||
createAccountListHelpers,
|
||||
createAccountStatusSink,
|
||||
createLoggerBackedRuntime,
|
||||
createNormalizedOutboundDeliverer,
|
||||
createReplyPrefixOptions,
|
||||
createScopedPairingAccess,
|
||||
dispatchInboundReplyWithBase,
|
||||
emptyPluginConfigSchema,
|
||||
formatDocsLink,
|
||||
formatPairingApproveHint,
|
||||
formatTextWithAttachmentLinks,
|
||||
getChatChannelMeta,
|
||||
GROUP_POLICY_BLOCKED_LABEL,
|
||||
isDangerousNameMatchingEnabled,
|
||||
issuePairingChallenge,
|
||||
logInboundDrop,
|
||||
normalizeResolvedSecretInputString,
|
||||
parseOptionalDelimitedEntries,
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
patchScopedAccountConfig,
|
||||
readStoreAllowFromForDmPolicy,
|
||||
resolveAllowlistProviderRuntimeGroupPolicy,
|
||||
resolveControlCommandGate,
|
||||
resolveDefaultGroupPolicy,
|
||||
resolveEffectiveAllowFromLists,
|
||||
resolveOutboundMediaUrls,
|
||||
runPassiveAccountLifecycle,
|
||||
setAccountEnabledInConfigSection,
|
||||
setTopLevelChannelAllowFrom,
|
||||
setTopLevelChannelDmPolicyWithAllowFrom,
|
||||
ToolPolicySchema,
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
type BaseProbeResult,
|
||||
type BlockStreamingCoalesceConfig,
|
||||
type ChannelPlugin,
|
||||
type DmConfig,
|
||||
type DmPolicy,
|
||||
type GroupPolicy,
|
||||
type GroupToolPolicyBySenderConfig,
|
||||
type GroupToolPolicyConfig,
|
||||
type MarkdownConfig,
|
||||
type OpenClawConfig,
|
||||
type OpenClawPluginApi,
|
||||
type OutboundReplyPayload,
|
||||
type PluginRuntime,
|
||||
type RuntimeEnv,
|
||||
type WizardPrompter,
|
||||
} from "openclaw/plugin-sdk/irc";
|
||||
export * from "openclaw/plugin-sdk/irc";
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
type ChannelStatusIssue,
|
||||
type LineConfig,
|
||||
type LineChannelData,
|
||||
type OpenClawConfig,
|
||||
type ResolvedLineAccount,
|
||||
} from "../api.js";
|
||||
import { lineConfigAdapter } from "./config-adapter.js";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/llm-task.js";
|
||||
export * from "openclaw/plugin-sdk/llm-task";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/lobster.js";
|
||||
export * from "openclaw/plugin-sdk/lobster";
|
||||
|
||||
@ -3,8 +3,8 @@ import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { PassThrough } from "node:stream";
|
||||
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "../runtime-api.js";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "../runtime-api.js";
|
||||
import {
|
||||
createWindowsCmdShimFixture,
|
||||
restorePlatformPathEnv,
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/memory-lancedb.js";
|
||||
export * from "openclaw/plugin-sdk/memory-lancedb";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/open-prose.js";
|
||||
export * from "openclaw/plugin-sdk/open-prose";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/phone-control.js";
|
||||
export * from "openclaw/plugin-sdk/phone-control";
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { ensureAuthProfileStore, listProfilesForProvider } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import { QWEN_OAUTH_MARKER } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import { loginQwenPortalOAuth } from "./oauth.js";
|
||||
import { buildQwenPortalProvider, QWEN_PORTAL_BASE_URL } from "./provider-catalog.js";
|
||||
import {
|
||||
buildOauthProviderAuthResult,
|
||||
definePluginEntry,
|
||||
@ -7,8 +9,6 @@ import {
|
||||
type ProviderAuthContext,
|
||||
type ProviderCatalogContext,
|
||||
} from "./runtime-api.js";
|
||||
import { loginQwenPortalOAuth } from "./oauth.js";
|
||||
import { buildQwenPortalProvider, QWEN_PORTAL_BASE_URL } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "qwen-portal";
|
||||
const PROVIDER_LABEL = "Qwen";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/qwen-portal-auth.js";
|
||||
export * from "openclaw/plugin-sdk/qwen-portal-auth";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { type ResolvedSignalAccount } from "./accounts.js";
|
||||
import { signalSetupAdapter } from "./setup-core.js";
|
||||
import { type ChannelPlugin } from "./runtime-api.js";
|
||||
import { signalSetupAdapter } from "./setup-core.js";
|
||||
import { createSignalPluginBase, signalSetupWizard } from "./shared.js";
|
||||
|
||||
export const signalSetupPlugin: ChannelPlugin<ResolvedSignalAccount> = {
|
||||
|
||||
@ -4,6 +4,16 @@ import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { buildOutboundBaseSessionKey } from "openclaw/plugin-sdk/core";
|
||||
import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import { type RoutePeer } from "openclaw/plugin-sdk/routing";
|
||||
import { resolveSignalAccount, type ResolvedSignalAccount } from "./accounts.js";
|
||||
import { markdownToSignalTextChunks } from "./format.js";
|
||||
import {
|
||||
looksLikeUuid,
|
||||
resolveSignalPeerId,
|
||||
resolveSignalRecipient,
|
||||
resolveSignalSender,
|
||||
} from "./identity.js";
|
||||
import { signalMessageActions } from "./message-actions.js";
|
||||
import type { SignalProbe } from "./probe.js";
|
||||
import {
|
||||
buildBaseAccountStatusSnapshot,
|
||||
buildBaseChannelStatusSummary,
|
||||
@ -17,16 +27,6 @@ import {
|
||||
resolveChannelMediaMaxBytes,
|
||||
type ChannelPlugin,
|
||||
} from "./runtime-api.js";
|
||||
import { resolveSignalAccount, type ResolvedSignalAccount } from "./accounts.js";
|
||||
import { markdownToSignalTextChunks } from "./format.js";
|
||||
import {
|
||||
looksLikeUuid,
|
||||
resolveSignalPeerId,
|
||||
resolveSignalRecipient,
|
||||
resolveSignalSender,
|
||||
} from "./identity.js";
|
||||
import { signalMessageActions } from "./message-actions.js";
|
||||
import type { SignalProbe } from "./probe.js";
|
||||
import { getSignalRuntime } from "./runtime.js";
|
||||
import { signalSetupAdapter } from "./setup-core.js";
|
||||
import {
|
||||
|
||||
@ -4,6 +4,12 @@ import {
|
||||
createScopedDmSecurityResolver,
|
||||
} from "openclaw/plugin-sdk/channel-config-helpers";
|
||||
import { createChannelPluginBase } from "openclaw/plugin-sdk/core";
|
||||
import {
|
||||
listSignalAccountIds,
|
||||
resolveDefaultSignalAccountId,
|
||||
resolveSignalAccount,
|
||||
type ResolvedSignalAccount,
|
||||
} from "./accounts.js";
|
||||
import {
|
||||
buildChannelConfigSchema,
|
||||
getChatChannelMeta,
|
||||
@ -11,12 +17,6 @@ import {
|
||||
SignalConfigSchema,
|
||||
type ChannelPlugin,
|
||||
} from "./runtime-api.js";
|
||||
import {
|
||||
listSignalAccountIds,
|
||||
resolveDefaultSignalAccountId,
|
||||
resolveSignalAccount,
|
||||
type ResolvedSignalAccount,
|
||||
} from "./accounts.js";
|
||||
import { createSignalSetupWizardProxy } from "./setup-core.js";
|
||||
|
||||
export const SIGNAL_CHANNEL = "signal" as const;
|
||||
|
||||
@ -29,6 +29,8 @@ import { resolveSlackUserAllowlist } from "./resolve-users.js";
|
||||
import {
|
||||
buildComputedAccountStatusSnapshot,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
listSlackDirectoryGroupsFromConfig,
|
||||
listSlackDirectoryPeersFromConfig,
|
||||
looksLikeSlackTargetId,
|
||||
normalizeSlackMessagingTarget,
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
|
||||
@ -1,20 +1,18 @@
|
||||
import {
|
||||
applyDirectoryQueryAndLimit,
|
||||
collectNormalizedDirectoryIds,
|
||||
inspectReadOnlyChannelAccount,
|
||||
listDirectoryGroupEntriesFromMapKeys,
|
||||
toDirectoryEntries,
|
||||
type DirectoryConfigParams,
|
||||
} from "openclaw/plugin-sdk/directory-runtime";
|
||||
import type { InspectedSlackAccount } from "../api.js";
|
||||
import { inspectSlackAccount, type InspectedSlackAccount } from "../api.js";
|
||||
import { parseSlackTarget } from "./targets.js";
|
||||
|
||||
export async function listSlackDirectoryPeersFromConfig(params: DirectoryConfigParams) {
|
||||
const account = (await inspectReadOnlyChannelAccount({
|
||||
channelId: "slack",
|
||||
const account = inspectSlackAccount({
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
})) as InspectedSlackAccount | null;
|
||||
}) as InspectedSlackAccount | null;
|
||||
if (!account || !("config" in account)) {
|
||||
return [];
|
||||
}
|
||||
@ -40,11 +38,10 @@ export async function listSlackDirectoryPeersFromConfig(params: DirectoryConfigP
|
||||
}
|
||||
|
||||
export async function listSlackDirectoryGroupsFromConfig(params: DirectoryConfigParams) {
|
||||
const account = (await inspectReadOnlyChannelAccount({
|
||||
channelId: "slack",
|
||||
const account = inspectSlackAccount({
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
})) as InspectedSlackAccount | null;
|
||||
}) as InspectedSlackAccount | null;
|
||||
if (!account || !("config" in account)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -1,34 +1,29 @@
|
||||
export type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
export type { SlackAccountConfig } from "../../../src/config/types.slack.js";
|
||||
export type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
|
||||
|
||||
export {
|
||||
buildComputedAccountStatusSnapshot,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
buildChannelConfigSchema,
|
||||
getChatChannelMeta,
|
||||
looksLikeSlackTargetId,
|
||||
normalizeSlackMessagingTarget,
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
} from "../../../src/plugin-sdk/channel-plugin-common.js";
|
||||
export { buildComputedAccountStatusSnapshot } from "../../../src/plugin-sdk/status-helpers.js";
|
||||
projectCredentialSnapshotFields,
|
||||
resolveConfiguredFromRequiredCredentialStatuses,
|
||||
type ChannelPlugin,
|
||||
type OpenClawConfig,
|
||||
type SlackAccountConfig,
|
||||
} from "openclaw/plugin-sdk/slack";
|
||||
export {
|
||||
listSlackDirectoryGroupsFromConfig,
|
||||
listSlackDirectoryPeersFromConfig,
|
||||
} from "./directory-config.js";
|
||||
export {
|
||||
looksLikeSlackTargetId,
|
||||
normalizeSlackMessagingTarget,
|
||||
} from "../../../src/channels/plugins/normalize/slack.js";
|
||||
export {
|
||||
projectCredentialSnapshotFields,
|
||||
resolveConfiguredFromRequiredCredentialStatuses,
|
||||
} from "../../../src/channels/account-snapshot-fields.js";
|
||||
export { SlackConfigSchema } from "../../../src/config/zod-schema.providers-core.js";
|
||||
export {
|
||||
buildChannelConfigSchema,
|
||||
getChatChannelMeta,
|
||||
createActionGate,
|
||||
imageResultFromFile,
|
||||
jsonResult,
|
||||
readNumberParam,
|
||||
readReactionParams,
|
||||
readStringParam,
|
||||
} from "../../../src/agents/tools/common.js";
|
||||
export { withNormalizedTimestamp } from "../../../src/agents/date-time.js";
|
||||
SlackConfigSchema,
|
||||
withNormalizedTimestamp,
|
||||
} from "openclaw/plugin-sdk/slack-core";
|
||||
export { isSlackInteractiveRepliesEnabled } from "./interactive-replies.js";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/talk-voice.js";
|
||||
export * from "openclaw/plugin-sdk/talk-voice";
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
export type {
|
||||
ChannelMessageActionAdapter,
|
||||
ChannelPlugin,
|
||||
OpenClawConfig,
|
||||
TelegramActionConfig,
|
||||
} from "../../src/plugin-sdk/telegram-core.js";
|
||||
export type { ChannelMessageActionAdapter } from "../../src/channels/plugins/types.js";
|
||||
export type { TelegramAccountConfig, TelegramNetworkConfig } from "../../src/config/types.js";
|
||||
export type {
|
||||
OpenClawPluginApi,
|
||||
PluginRuntime,
|
||||
TelegramAccountConfig,
|
||||
TelegramActionConfig,
|
||||
TelegramNetworkConfig,
|
||||
} from "openclaw/plugin-sdk/telegram";
|
||||
export type {
|
||||
OpenClawPluginService,
|
||||
OpenClawPluginServiceContext,
|
||||
PluginLogger,
|
||||
} from "../../src/plugins/types.js";
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
export type {
|
||||
AcpRuntime,
|
||||
AcpRuntimeCapabilities,
|
||||
@ -20,12 +22,22 @@ export type {
|
||||
AcpRuntimeHandle,
|
||||
AcpRuntimeStatus,
|
||||
AcpRuntimeTurnInput,
|
||||
AcpRuntimeErrorCode,
|
||||
AcpSessionUpdateTag,
|
||||
} from "../../src/acp/runtime/types.js";
|
||||
export type { AcpRuntimeErrorCode } from "../../src/acp/runtime/errors.js";
|
||||
export { AcpRuntimeError } from "../../src/acp/runtime/errors.js";
|
||||
} from "openclaw/plugin-sdk/acp-runtime";
|
||||
export { AcpRuntimeError } from "openclaw/plugin-sdk/acp-runtime";
|
||||
|
||||
export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../src/routing/session-key.js";
|
||||
export {
|
||||
buildTokenChannelStatusSummary,
|
||||
clearAccountEntryFields,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
normalizeAccountId,
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
parseTelegramTopicConversation,
|
||||
projectCredentialSnapshotFields,
|
||||
resolveConfiguredFromCredentialStatuses,
|
||||
resolveTelegramPollVisibility,
|
||||
} from "openclaw/plugin-sdk/telegram";
|
||||
export {
|
||||
buildChannelConfigSchema,
|
||||
getChatChannelMeta,
|
||||
@ -37,13 +49,31 @@ export {
|
||||
readStringParam,
|
||||
resolvePollMaxSelections,
|
||||
TelegramConfigSchema,
|
||||
} from "../../src/plugin-sdk/telegram-core.js";
|
||||
export { parseTelegramTopicConversation } from "../../src/acp/conversation-id.js";
|
||||
export { clearAccountEntryFields } from "../../src/channels/plugins/config-helpers.js";
|
||||
export { buildTokenChannelStatusSummary } from "../../src/plugin-sdk/status-helpers.js";
|
||||
} from "openclaw/plugin-sdk/telegram-core";
|
||||
export type { TelegramProbe } from "./src/probe.js";
|
||||
export { auditTelegramGroupMembership, collectTelegramUnmentionedGroupIds } from "./src/audit.js";
|
||||
export { telegramMessageActions } from "./src/channel-actions.js";
|
||||
export { monitorTelegramProvider } from "./src/monitor.js";
|
||||
export { probeTelegram } from "./src/probe.js";
|
||||
export {
|
||||
projectCredentialSnapshotFields,
|
||||
resolveConfiguredFromCredentialStatuses,
|
||||
} from "../../src/channels/account-snapshot-fields.js";
|
||||
export { resolveTelegramPollVisibility } from "../../src/poll-params.js";
|
||||
export { PAIRING_APPROVED_MESSAGE } from "../../src/channels/plugins/pairing-message.js";
|
||||
createForumTopicTelegram,
|
||||
deleteMessageTelegram,
|
||||
editForumTopicTelegram,
|
||||
editMessageReplyMarkupTelegram,
|
||||
editMessageTelegram,
|
||||
pinMessageTelegram,
|
||||
reactMessageTelegram,
|
||||
renameForumTopicTelegram,
|
||||
sendMessageTelegram,
|
||||
sendPollTelegram,
|
||||
sendStickerTelegram,
|
||||
sendTypingTelegram,
|
||||
unpinMessageTelegram,
|
||||
} from "./src/send.js";
|
||||
export {
|
||||
createTelegramThreadBindingManager,
|
||||
getTelegramThreadBindingManager,
|
||||
setTelegramThreadBindingIdleTimeoutBySessionKey,
|
||||
setTelegramThreadBindingMaxAgeBySessionKey,
|
||||
} from "./src/thread-bindings.js";
|
||||
export { resolveTelegramToken } from "./src/token.js";
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { expect, vi } from "vitest";
|
||||
import type { SkillCommandSpec } from "../../../src/agents/skills.js";
|
||||
import type { OpenClawConfig } from "../runtime-api.js";
|
||||
import type { TelegramBotDeps } from "./bot-deps.js";
|
||||
import {
|
||||
@ -8,6 +9,12 @@ import {
|
||||
type NativeCommandTestParams as RegisterTelegramNativeCommandsParams,
|
||||
} from "./bot-native-commands.fixture-test-support.js";
|
||||
|
||||
const EMPTY_REPLY_COUNTS = {
|
||||
block: 0,
|
||||
final: 0,
|
||||
tool: 0,
|
||||
} as const;
|
||||
|
||||
type RegisteredCommand = {
|
||||
command: string;
|
||||
description: string;
|
||||
@ -21,7 +28,9 @@ type CreateCommandBotResult = {
|
||||
};
|
||||
|
||||
const skillCommandMocks = vi.hoisted(() => ({
|
||||
listSkillCommandsForAgents: vi.fn(() => []),
|
||||
listSkillCommandsForAgents: vi.fn<
|
||||
(params: { cfg: OpenClawConfig; agentIds?: string[] }) => SkillCommandSpec[]
|
||||
>(() => []),
|
||||
}));
|
||||
|
||||
const deliveryMocks = vi.hoisted(() => ({
|
||||
@ -86,7 +95,7 @@ export function createNativeCommandTestParams(
|
||||
enqueueSystemEvent: vi.fn(),
|
||||
dispatchReplyWithBufferedBlockDispatcher: vi.fn(async () => ({
|
||||
queuedFinal: false,
|
||||
counts: {},
|
||||
counts: EMPTY_REPLY_COUNTS,
|
||||
})),
|
||||
listSkillCommandsForAgents,
|
||||
wasSentByBot: vi.fn(() => false),
|
||||
|
||||
@ -37,6 +37,12 @@ import {
|
||||
waitForRegisteredCommands,
|
||||
} from "./bot-native-commands.menu-test-support.js";
|
||||
|
||||
const EMPTY_REPLY_COUNTS = {
|
||||
block: 0,
|
||||
final: 0,
|
||||
tool: 0,
|
||||
} as const;
|
||||
|
||||
function createNativeCommandTestParams(
|
||||
cfg: OpenClawConfig,
|
||||
params: Partial<Parameters<typeof registerTelegramNativeCommands>[0]> = {},
|
||||
@ -48,7 +54,7 @@ function createNativeCommandTestParams(
|
||||
enqueueSystemEvent: vi.fn(),
|
||||
dispatchReplyWithBufferedBlockDispatcher: vi.fn(async () => ({
|
||||
queuedFinal: false,
|
||||
counts: {},
|
||||
counts: EMPTY_REPLY_COUNTS,
|
||||
})),
|
||||
listSkillCommandsForAgents: skillCommandMocks.listSkillCommandsForAgents,
|
||||
wasSentByBot: vi.fn(() => false),
|
||||
|
||||
@ -4,23 +4,21 @@ import type { MsgContext } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import type { GetReplyOptions, ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import type { MockFn } from "openclaw/plugin-sdk/testing";
|
||||
import { beforeEach, vi } from "vitest";
|
||||
import type { TelegramBotDeps } from "./bot-deps.js";
|
||||
|
||||
type AnyMock = MockFn<(...args: unknown[]) => unknown>;
|
||||
type AnyAsyncMock = MockFn<(...args: unknown[]) => Promise<unknown>>;
|
||||
type AnyMock = ReturnType<typeof vi.fn>;
|
||||
type AnyAsyncMock = ReturnType<typeof vi.fn>;
|
||||
type DispatchReplyWithBufferedBlockDispatcherFn =
|
||||
typeof import("openclaw/plugin-sdk/reply-runtime").dispatchReplyWithBufferedBlockDispatcher;
|
||||
type DispatchReplyWithBufferedBlockDispatcherResult = Awaited<
|
||||
ReturnType<DispatchReplyWithBufferedBlockDispatcherFn>
|
||||
>;
|
||||
type DispatchReplyHarnessParams = {
|
||||
ctx: MsgContext;
|
||||
replyOptions?: GetReplyOptions;
|
||||
dispatcherOptions?: {
|
||||
typingCallbacks?: {
|
||||
start?: () => void | Promise<void>;
|
||||
};
|
||||
deliver?: (payload: ReplyPayload, info: { kind: "final" }) => void | Promise<void>;
|
||||
};
|
||||
type DispatchReplyHarnessParams = Parameters<DispatchReplyWithBufferedBlockDispatcherFn>[0];
|
||||
|
||||
const EMPTY_REPLY_COUNTS: DispatchReplyWithBufferedBlockDispatcherResult["counts"] = {
|
||||
block: 0,
|
||||
final: 0,
|
||||
tool: 0,
|
||||
};
|
||||
|
||||
const { sessionStorePath } = vi.hoisted(() => ({
|
||||
@ -39,12 +37,14 @@ vi.doMock("openclaw/plugin-sdk/web-media", () => ({
|
||||
loadWebMedia,
|
||||
}));
|
||||
|
||||
const { loadConfig } = vi.hoisted((): { loadConfig: AnyMock } => ({
|
||||
loadConfig: vi.fn(() => ({})),
|
||||
}));
|
||||
const { resolveStorePathMock } = vi.hoisted((): { resolveStorePathMock: AnyMock } => ({
|
||||
resolveStorePathMock: vi.fn((storePath?: string) => storePath ?? sessionStorePath),
|
||||
const { loadConfig } = vi.hoisted((): { loadConfig: MockFn<() => OpenClawConfig> } => ({
|
||||
loadConfig: vi.fn(() => ({}) as OpenClawConfig),
|
||||
}));
|
||||
const { resolveStorePathMock } = vi.hoisted(
|
||||
(): { resolveStorePathMock: MockFn<TelegramBotDeps["resolveStorePath"]> } => ({
|
||||
resolveStorePathMock: vi.fn((storePath?: string) => storePath ?? sessionStorePath),
|
||||
}),
|
||||
);
|
||||
|
||||
export function getLoadConfigMock(): AnyMock {
|
||||
return loadConfig;
|
||||
@ -67,7 +67,7 @@ vi.doMock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
|
||||
|
||||
const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(
|
||||
(): {
|
||||
readChannelAllowFromStore: AnyAsyncMock;
|
||||
readChannelAllowFromStore: MockFn<TelegramBotDeps["readChannelAllowFromStore"]>;
|
||||
upsertChannelPairingRequest: AnyAsyncMock;
|
||||
} => ({
|
||||
readChannelAllowFromStore: vi.fn(async () => [] as string[]),
|
||||
@ -111,9 +111,9 @@ const skillCommandsHoisted = vi.hoisted(() => ({
|
||||
async (params: DispatchReplyHarnessParams) => {
|
||||
const result: DispatchReplyWithBufferedBlockDispatcherResult = {
|
||||
queuedFinal: false,
|
||||
counts: {} as DispatchReplyWithBufferedBlockDispatcherResult["counts"],
|
||||
counts: EMPTY_REPLY_COUNTS,
|
||||
};
|
||||
await params.dispatcherOptions?.typingCallbacks?.start?.();
|
||||
await params.dispatcherOptions?.typingCallbacks?.onReplyStart?.();
|
||||
const reply = await skillCommandsHoisted.replySpy(params.ctx, params.replyOptions);
|
||||
const payloads = reply === undefined ? [] : Array.isArray(reply) ? reply : [reply];
|
||||
for (const payload of payloads) {
|
||||
@ -141,9 +141,10 @@ vi.doMock("openclaw/plugin-sdk/reply-runtime", async (importOriginal) => {
|
||||
});
|
||||
|
||||
const systemEventsHoisted = vi.hoisted(() => ({
|
||||
enqueueSystemEventSpy: vi.fn(),
|
||||
enqueueSystemEventSpy: vi.fn<TelegramBotDeps["enqueueSystemEvent"]>(() => false),
|
||||
}));
|
||||
export const enqueueSystemEventSpy: AnyMock = systemEventsHoisted.enqueueSystemEventSpy;
|
||||
export const enqueueSystemEventSpy: MockFn<TelegramBotDeps["enqueueSystemEvent"]> =
|
||||
systemEventsHoisted.enqueueSystemEventSpy;
|
||||
|
||||
vi.doMock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/infra-runtime")>();
|
||||
@ -173,7 +174,7 @@ const grammySpies = vi.hoisted(() => ({
|
||||
onSpy: vi.fn() as AnyMock,
|
||||
stopSpy: vi.fn() as AnyMock,
|
||||
commandSpy: vi.fn() as AnyMock,
|
||||
botCtorSpy: vi.fn() as AnyMock,
|
||||
botCtorSpy: vi.fn((_: string, __?: { client?: { fetch?: typeof fetch } }) => undefined),
|
||||
answerCallbackQuerySpy: vi.fn(async () => undefined) as AnyAsyncMock,
|
||||
sendChatActionSpy: vi.fn() as AnyMock,
|
||||
editMessageTextSpy: vi.fn(async () => ({ message_id: 88 })) as AnyAsyncMock,
|
||||
@ -191,26 +192,26 @@ const grammySpies = vi.hoisted(() => ({
|
||||
getFileSpy: vi.fn(async () => ({ file_path: "media/file.jpg" })) as AnyAsyncMock,
|
||||
}));
|
||||
|
||||
export const {
|
||||
useSpy,
|
||||
middlewareUseSpy,
|
||||
onSpy,
|
||||
stopSpy,
|
||||
commandSpy,
|
||||
botCtorSpy,
|
||||
answerCallbackQuerySpy,
|
||||
sendChatActionSpy,
|
||||
editMessageTextSpy,
|
||||
editMessageReplyMarkupSpy,
|
||||
sendMessageDraftSpy,
|
||||
setMessageReactionSpy,
|
||||
setMyCommandsSpy,
|
||||
getMeSpy,
|
||||
sendMessageSpy,
|
||||
sendAnimationSpy,
|
||||
sendPhotoSpy,
|
||||
getFileSpy,
|
||||
} = grammySpies;
|
||||
export const useSpy: MockFn<(arg: unknown) => void> = grammySpies.useSpy;
|
||||
export const middlewareUseSpy: AnyMock = grammySpies.middlewareUseSpy;
|
||||
export const onSpy: AnyMock = grammySpies.onSpy;
|
||||
export const stopSpy: AnyMock = grammySpies.stopSpy;
|
||||
export const commandSpy: AnyMock = grammySpies.commandSpy;
|
||||
export const botCtorSpy: MockFn<
|
||||
(token: string, options?: { client?: { fetch?: typeof fetch } }) => void
|
||||
> = grammySpies.botCtorSpy;
|
||||
export const answerCallbackQuerySpy: AnyAsyncMock = grammySpies.answerCallbackQuerySpy;
|
||||
export const sendChatActionSpy: AnyMock = grammySpies.sendChatActionSpy;
|
||||
export const editMessageTextSpy: AnyAsyncMock = grammySpies.editMessageTextSpy;
|
||||
export const editMessageReplyMarkupSpy: AnyAsyncMock = grammySpies.editMessageReplyMarkupSpy;
|
||||
export const sendMessageDraftSpy: AnyAsyncMock = grammySpies.sendMessageDraftSpy;
|
||||
export const setMessageReactionSpy: AnyAsyncMock = grammySpies.setMessageReactionSpy;
|
||||
export const setMyCommandsSpy: AnyAsyncMock = grammySpies.setMyCommandsSpy;
|
||||
export const getMeSpy: AnyAsyncMock = grammySpies.getMeSpy;
|
||||
export const sendMessageSpy: AnyAsyncMock = grammySpies.sendMessageSpy;
|
||||
export const sendAnimationSpy: AnyAsyncMock = grammySpies.sendAnimationSpy;
|
||||
export const sendPhotoSpy: AnyAsyncMock = grammySpies.sendPhotoSpy;
|
||||
export const getFileSpy: AnyAsyncMock = grammySpies.getFileSpy;
|
||||
|
||||
const runnerHoisted = vi.hoisted(() => ({
|
||||
sequentializeMiddleware: vi.fn(async (_ctx: unknown, next?: () => Promise<void>) => {
|
||||
@ -224,7 +225,11 @@ const runnerHoisted = vi.hoisted(() => ({
|
||||
export const sequentializeSpy: AnyMock = runnerHoisted.sequentializeSpy;
|
||||
export let sequentializeKey: ((ctx: unknown) => string) | undefined;
|
||||
export const throttlerSpy: AnyMock = runnerHoisted.throttlerSpy;
|
||||
export const telegramBotRuntimeForTest = {
|
||||
export const telegramBotRuntimeForTest: {
|
||||
Bot: new (token: string, options?: { client?: { fetch?: typeof fetch } }) => unknown;
|
||||
sequentialize: (keyFn: (ctx: unknown) => string) => unknown;
|
||||
apiThrottler: () => unknown;
|
||||
} = {
|
||||
Bot: class {
|
||||
api = {
|
||||
config: { use: grammySpies.useSpy },
|
||||
@ -259,7 +264,7 @@ export const telegramBotRuntimeForTest = {
|
||||
},
|
||||
apiThrottler: () => runnerHoisted.throttlerSpy(),
|
||||
};
|
||||
export const telegramBotDepsForTest = {
|
||||
export const telegramBotDepsForTest: TelegramBotDeps = {
|
||||
loadConfig,
|
||||
resolveStorePath: resolveStorePathMock,
|
||||
readChannelAllowFromStore,
|
||||
@ -365,9 +370,9 @@ beforeEach(() => {
|
||||
async (params: DispatchReplyHarnessParams) => {
|
||||
const result: DispatchReplyWithBufferedBlockDispatcherResult = {
|
||||
queuedFinal: false,
|
||||
counts: {} as DispatchReplyWithBufferedBlockDispatcherResult["counts"],
|
||||
counts: EMPTY_REPLY_COUNTS,
|
||||
};
|
||||
await params.dispatcherOptions?.typingCallbacks?.start?.();
|
||||
await params.dispatcherOptions?.typingCallbacks?.onReplyStart?.();
|
||||
const reply = await replySpy(params.ctx, params.replyOptions);
|
||||
const payloads = reply === undefined ? [] : Array.isArray(reply) ? reply : [reply];
|
||||
for (const payload of payloads) {
|
||||
|
||||
@ -39,7 +39,9 @@ const {
|
||||
getTelegramSequentialKey,
|
||||
setTelegramBotRuntimeForTest,
|
||||
} = await import("./bot.js");
|
||||
setTelegramBotRuntimeForTest(telegramBotRuntimeForTest);
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
const createTelegramBot = (opts: Parameters<typeof createTelegramBotBase>[0]) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
|
||||
@ -6,7 +6,9 @@ const { botCtorSpy, telegramBotDepsForTest } =
|
||||
const { telegramBotRuntimeForTest } = await import("./bot.create-telegram-bot.test-harness.js");
|
||||
const { createTelegramBot: createTelegramBotBase, setTelegramBotRuntimeForTest } =
|
||||
await import("./bot.js");
|
||||
setTelegramBotRuntimeForTest(telegramBotRuntimeForTest);
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
const createTelegramBot = (opts: Parameters<typeof createTelegramBotBase>[0]) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
import { resetInboundDedupe } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import { beforeEach, vi, type Mock } from "vitest";
|
||||
import type { TelegramBotDeps } from "./bot-deps.js";
|
||||
|
||||
const EMPTY_REPLY_COUNTS = {
|
||||
block: 0,
|
||||
final: 0,
|
||||
tool: 0,
|
||||
} as const;
|
||||
|
||||
export const useSpy: Mock = vi.fn();
|
||||
export const middlewareUseSpy: Mock = vi.fn();
|
||||
@ -56,7 +63,11 @@ const apiStub: ApiStub = {
|
||||
setMyCommands: vi.fn(async () => undefined),
|
||||
};
|
||||
|
||||
export const telegramBotRuntimeForTest = {
|
||||
export const telegramBotRuntimeForTest: {
|
||||
Bot: new (token: string) => unknown;
|
||||
sequentialize: () => unknown;
|
||||
apiThrottler: () => unknown;
|
||||
} = {
|
||||
Bot: class {
|
||||
api = apiStub;
|
||||
use = middlewareUseSpy;
|
||||
@ -84,12 +95,12 @@ const mediaHarnessDispatchReplyWithBufferedBlockDispatcher = vi.hoisted(() =>
|
||||
for (const payload of payloads) {
|
||||
await params.dispatcherOptions?.deliver?.(payload, { kind: "final" });
|
||||
}
|
||||
return { queuedFinal: false, counts: {} };
|
||||
return { queuedFinal: false, counts: EMPTY_REPLY_COUNTS };
|
||||
}),
|
||||
);
|
||||
export const telegramBotDepsForTest = {
|
||||
export const telegramBotDepsForTest: TelegramBotDeps = {
|
||||
loadConfig: () => ({
|
||||
channels: { telegram: { dmPolicy: "open", allowFrom: ["*"] } },
|
||||
channels: { telegram: { dmPolicy: "open" as const, allowFrom: ["*"] } },
|
||||
}),
|
||||
resolveStorePath: vi.fn((storePath?: string) => storePath ?? "/tmp/telegram-media-sessions.json"),
|
||||
readChannelAllowFromStore: vi.fn(async () => [] as string[]),
|
||||
|
||||
@ -107,7 +107,11 @@ beforeAll(async () => {
|
||||
onSpyRef = harness.onSpy;
|
||||
sendChatActionSpyRef = harness.sendChatActionSpy;
|
||||
const botModule = await import("./bot.js");
|
||||
botModule.setTelegramBotRuntimeForTest(harness.telegramBotRuntimeForTest);
|
||||
botModule.setTelegramBotRuntimeForTest(
|
||||
harness.telegramBotRuntimeForTest as unknown as Parameters<
|
||||
typeof botModule.setTelegramBotRuntimeForTest
|
||||
>[0],
|
||||
);
|
||||
createTelegramBotRef = (opts) =>
|
||||
botModule.createTelegramBot({
|
||||
...opts,
|
||||
|
||||
@ -35,7 +35,9 @@ const { normalizeTelegramCommandName } =
|
||||
await import("../../../src/config/telegram-custom-commands.js");
|
||||
const { createTelegramBot: createTelegramBotBase, setTelegramBotRuntimeForTest } =
|
||||
await import("./bot.js");
|
||||
setTelegramBotRuntimeForTest(telegramBotRuntimeForTest);
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
const createTelegramBot = (opts: Parameters<typeof createTelegramBotBase>[0]) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
|
||||
@ -2,19 +2,17 @@ import { mapAllowFromEntries } from "openclaw/plugin-sdk/channel-config-helpers"
|
||||
import {
|
||||
applyDirectoryQueryAndLimit,
|
||||
collectNormalizedDirectoryIds,
|
||||
inspectReadOnlyChannelAccount,
|
||||
listDirectoryGroupEntriesFromMapKeys,
|
||||
toDirectoryEntries,
|
||||
type DirectoryConfigParams,
|
||||
} from "openclaw/plugin-sdk/directory-runtime";
|
||||
import type { InspectedTelegramAccount } from "../api.js";
|
||||
import { inspectTelegramAccount, type InspectedTelegramAccount } from "../api.js";
|
||||
|
||||
export async function listTelegramDirectoryPeersFromConfig(params: DirectoryConfigParams) {
|
||||
const account = (await inspectReadOnlyChannelAccount({
|
||||
channelId: "telegram",
|
||||
const account = inspectTelegramAccount({
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
})) as InspectedTelegramAccount | null;
|
||||
}) as InspectedTelegramAccount | null;
|
||||
if (!account || !("config" in account)) {
|
||||
return [];
|
||||
}
|
||||
@ -36,11 +34,10 @@ export async function listTelegramDirectoryPeersFromConfig(params: DirectoryConf
|
||||
}
|
||||
|
||||
export async function listTelegramDirectoryGroupsFromConfig(params: DirectoryConfigParams) {
|
||||
const account = (await inspectReadOnlyChannelAccount({
|
||||
channelId: "telegram",
|
||||
const account = inspectTelegramAccount({
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
})) as InspectedTelegramAccount | null;
|
||||
}) as InspectedTelegramAccount | null;
|
||||
if (!account || !("config" in account)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/thread-ownership.js";
|
||||
export * from "openclaw/plugin-sdk/thread-ownership";
|
||||
|
||||
@ -1,2 +1 @@
|
||||
export * from "openclaw/plugin-sdk/twitch";
|
||||
export * from "./src/setup-surface.js";
|
||||
|
||||
@ -1 +1 @@
|
||||
export * from "../../src/plugin-sdk/voice-call.js";
|
||||
export * from "openclaw/plugin-sdk/voice-call";
|
||||
|
||||
@ -1,22 +1,30 @@
|
||||
export {
|
||||
buildChannelConfigSchema,
|
||||
createActionGate,
|
||||
createWhatsAppOutboundBase,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
formatWhatsAppConfigAllowFromEntries,
|
||||
isWhatsAppGroupJid,
|
||||
getChatChannelMeta,
|
||||
jsonResult,
|
||||
normalizeWhatsAppTarget,
|
||||
normalizeE164,
|
||||
readReactionParams,
|
||||
readStringParam,
|
||||
resolveWhatsAppHeartbeatRecipients,
|
||||
resolveWhatsAppMentionStripRegexes,
|
||||
resolveWhatsAppGroupIntroHint,
|
||||
resolveWhatsAppOutboundTarget,
|
||||
ToolAuthorizationError,
|
||||
WhatsAppConfigSchema,
|
||||
type ChannelPlugin,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/whatsapp-core";
|
||||
|
||||
export {
|
||||
createWhatsAppOutboundBase,
|
||||
isWhatsAppGroupJid,
|
||||
normalizeWhatsAppTarget,
|
||||
resolveWhatsAppHeartbeatRecipients,
|
||||
resolveWhatsAppMentionStripRegexes,
|
||||
type ChannelMessageActionName,
|
||||
type DmPolicy,
|
||||
type GroupPolicy,
|
||||
type OpenClawConfig,
|
||||
type WhatsAppAccountConfig,
|
||||
} from "openclaw/plugin-sdk/whatsapp";
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import {
|
||||
resolveWebSearchProviderCredential,
|
||||
} from "openclaw/plugin-sdk/provider-web-search";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { withEnv } from "../../src/test-utils/env.js";
|
||||
import { withEnv } from "../../test/helpers/extensions/env.js";
|
||||
import { __testing } from "./web-search.js";
|
||||
|
||||
const { extractXaiWebSearchContent, resolveXaiInlineCitations, resolveXaiWebSearchModel } =
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
||||
import { listEnabledZaloAccounts } from "./accounts.js";
|
||||
import type {
|
||||
ChannelMessageActionAdapter,
|
||||
ChannelMessageActionName,
|
||||
OpenClawConfig,
|
||||
} from "./runtime-api.js";
|
||||
import { extractToolSend, jsonResult, readStringParam } from "./runtime-api.js";
|
||||
import { listEnabledZaloAccounts } from "./accounts.js";
|
||||
|
||||
const loadZaloActionsRuntime = createLazyRuntimeNamedExport(
|
||||
() => import("./actions.runtime.js"),
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-lifecycle";
|
||||
import { probeZalo } from "./probe.js";
|
||||
import { resolveZaloProxyFetch } from "./proxy.js";
|
||||
import { normalizeSecretInputString } from "./secret-input.js";
|
||||
import { sendMessageZalo } from "./send.js";
|
||||
import {
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
type ChannelPlugin,
|
||||
type OpenClawConfig,
|
||||
} from "./runtime-api.js";
|
||||
import { normalizeSecretInputString } from "./secret-input.js";
|
||||
import { sendMessageZalo } from "./send.js";
|
||||
|
||||
export async function notifyZaloPairingApproval(params: {
|
||||
cfg: OpenClawConfig;
|
||||
id: string;
|
||||
}) {
|
||||
export async function notifyZaloPairingApproval(params: { cfg: OpenClawConfig; id: string }) {
|
||||
const { resolveZaloAccount } = await import("./accounts.js");
|
||||
const account = resolveZaloAccount({ cfg: params.cfg });
|
||||
if (!account.token) {
|
||||
@ -44,11 +41,7 @@ export async function probeZaloAccount(params: {
|
||||
}
|
||||
|
||||
export async function startZaloGatewayAccount(
|
||||
ctx: Parameters<
|
||||
NonNullable<
|
||||
NonNullable<ChannelPlugin["gateway"]>["startAccount"]
|
||||
>
|
||||
>[0],
|
||||
ctx: Parameters<NonNullable<NonNullable<ChannelPlugin["gateway"]>["startAccount"]>>[0],
|
||||
) {
|
||||
const account = ctx.account;
|
||||
const token = account.token.trim();
|
||||
|
||||
@ -9,6 +9,14 @@ import {
|
||||
collectOpenProviderGroupPolicyWarnings,
|
||||
} from "openclaw/plugin-sdk/channel-policy";
|
||||
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
||||
import {
|
||||
listZaloAccountIds,
|
||||
resolveDefaultZaloAccountId,
|
||||
resolveZaloAccount,
|
||||
type ResolvedZaloAccount,
|
||||
} from "./accounts.js";
|
||||
import { zaloMessageActions } from "./actions.js";
|
||||
import { ZaloConfigSchema } from "./config-schema.js";
|
||||
import {
|
||||
buildBaseAccountStatusSnapshot,
|
||||
buildChannelConfigSchema,
|
||||
@ -24,14 +32,6 @@ import {
|
||||
type ChannelPlugin,
|
||||
type OpenClawConfig,
|
||||
} from "./runtime-api.js";
|
||||
import {
|
||||
listZaloAccountIds,
|
||||
resolveDefaultZaloAccountId,
|
||||
resolveZaloAccount,
|
||||
type ResolvedZaloAccount,
|
||||
} from "./accounts.js";
|
||||
import { zaloMessageActions } from "./actions.js";
|
||||
import { ZaloConfigSchema } from "./config-schema.js";
|
||||
import { resolveZaloOutboundSessionRoute } from "./session-route.js";
|
||||
import { zaloSetupAdapter } from "./setup-core.js";
|
||||
import { zaloSetupWizard } from "./setup-surface.js";
|
||||
|
||||
@ -5,8 +5,8 @@ import {
|
||||
GroupPolicySchema,
|
||||
} from "openclaw/plugin-sdk/channel-config-schema";
|
||||
import { z } from "zod";
|
||||
import { buildSecretInputSchema } from "./secret-input.js";
|
||||
import { MarkdownConfigSchema } from "./runtime-api.js";
|
||||
import { buildSecretInputSchema } from "./secret-input.js";
|
||||
|
||||
const zaloAccountSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
|
||||
@ -1,25 +1,4 @@
|
||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
import type {
|
||||
MarkdownTableMode,
|
||||
OpenClawConfig,
|
||||
OutboundReplyPayload,
|
||||
} from "./runtime-api.js";
|
||||
import {
|
||||
createTypingCallbacks,
|
||||
createScopedPairingAccess,
|
||||
createReplyPrefixOptions,
|
||||
issuePairingChallenge,
|
||||
logTypingFailure,
|
||||
resolveDirectDmAuthorizationOutcome,
|
||||
resolveSenderCommandAuthorizationWithRuntime,
|
||||
resolveOutboundMediaUrls,
|
||||
resolveDefaultGroupPolicy,
|
||||
resolveInboundRouteEnvelopeBuilderWithRuntime,
|
||||
sendMediaWithLeadingCaption,
|
||||
resolveWebhookPath,
|
||||
waitForAbortSignal,
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "./runtime-api.js";
|
||||
import type { ResolvedZaloAccount } from "./accounts.js";
|
||||
import {
|
||||
ZaloApiError,
|
||||
@ -48,6 +27,23 @@ import {
|
||||
type ZaloWebhookTarget,
|
||||
} from "./monitor.webhook.js";
|
||||
import { resolveZaloProxyFetch } from "./proxy.js";
|
||||
import type { MarkdownTableMode, OpenClawConfig, OutboundReplyPayload } from "./runtime-api.js";
|
||||
import {
|
||||
createTypingCallbacks,
|
||||
createScopedPairingAccess,
|
||||
createReplyPrefixOptions,
|
||||
issuePairingChallenge,
|
||||
logTypingFailure,
|
||||
resolveDirectDmAuthorizationOutcome,
|
||||
resolveSenderCommandAuthorizationWithRuntime,
|
||||
resolveOutboundMediaUrls,
|
||||
resolveDefaultGroupPolicy,
|
||||
resolveInboundRouteEnvelopeBuilderWithRuntime,
|
||||
sendMediaWithLeadingCaption,
|
||||
resolveWebhookPath,
|
||||
waitForAbortSignal,
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "./runtime-api.js";
|
||||
import { getZaloRuntime } from "./runtime.js";
|
||||
|
||||
export type ZaloRuntimeEnv = {
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import { timingSafeEqual } from "node:crypto";
|
||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
import type { ResolvedZaloAccount } from "./accounts.js";
|
||||
import type { ZaloFetch, ZaloUpdate } from "./api.js";
|
||||
import type { ZaloRuntimeEnv } from "./monitor.js";
|
||||
import {
|
||||
createDedupeCache,
|
||||
createFixedWindowRateLimiter,
|
||||
@ -17,9 +20,6 @@ import {
|
||||
resolveClientIp,
|
||||
type OpenClawConfig,
|
||||
} from "./runtime-api.js";
|
||||
import type { ResolvedZaloAccount } from "./accounts.js";
|
||||
import type { ZaloFetch, ZaloUpdate } from "./api.js";
|
||||
import type { ZaloRuntimeEnv } from "./monitor.js";
|
||||
|
||||
const ZALO_WEBHOOK_REPLAY_WINDOW_MS = 5 * 60_000;
|
||||
|
||||
|
||||
@ -2,8 +2,8 @@ import { resolveZaloAccount } from "./accounts.js";
|
||||
import type { ZaloFetch } from "./api.js";
|
||||
import { sendMessage, sendPhoto } from "./api.js";
|
||||
import { resolveZaloProxyFetch } from "./proxy.js";
|
||||
import { resolveZaloToken } from "./token.js";
|
||||
import type { OpenClawConfig } from "./runtime-api.js";
|
||||
import { resolveZaloToken } from "./token.js";
|
||||
|
||||
export type ZaloSendOptions = {
|
||||
token?: string;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
||||
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/infra-runtime";
|
||||
import type { BaseTokenResolution } from "./runtime-api.js";
|
||||
import { normalizeResolvedSecretInputString, normalizeSecretInputString } from "./secret-input.js";
|
||||
import type { ZaloConfig } from "./types.js";
|
||||
import type { BaseTokenResolution } from "./runtime-api.js";
|
||||
|
||||
export type ZaloTokenResolution = BaseTokenResolution & {
|
||||
source: "env" | "config" | "configFile" | "none";
|
||||
|
||||
@ -17,7 +17,17 @@ function stubImageGenerationProviders() {
|
||||
id: "openai",
|
||||
defaultModel: "gpt-image-1",
|
||||
models: ["gpt-image-1"],
|
||||
supportedSizes: ["1024x1024"],
|
||||
capabilities: {
|
||||
generate: {
|
||||
supportsSize: true,
|
||||
},
|
||||
edit: {
|
||||
enabled: false,
|
||||
},
|
||||
geometry: {
|
||||
sizes: ["1024x1024"],
|
||||
},
|
||||
},
|
||||
generateImage: vi.fn(async () => {
|
||||
throw new Error("not used");
|
||||
}),
|
||||
|
||||
@ -18,7 +18,7 @@ describe("extra-params: Google thinking payload compatibility", () => {
|
||||
api: "google-generative-ai",
|
||||
provider: "google",
|
||||
id: "gemini-3.1-pro-preview",
|
||||
} as Model<"openai-completions">,
|
||||
} as unknown as Model<"openai-completions">,
|
||||
thinkingLevel: "high",
|
||||
payload: {
|
||||
contents: [],
|
||||
|
||||
@ -457,7 +457,7 @@ describe("createOpenClawCodingTools", () => {
|
||||
it("applies xai model compat for direct Grok tool cleanup", () => {
|
||||
const xaiTools = createOpenClawCodingTools({
|
||||
modelProvider: "xai",
|
||||
modelCompat: applyXaiModelCompat({}).compat,
|
||||
modelCompat: applyXaiModelCompat({ compat: {} }).compat,
|
||||
senderIsOwner: true,
|
||||
});
|
||||
|
||||
|
||||
@ -18,10 +18,7 @@ function toolNames(tools: AnyAgentTool[]): string[] {
|
||||
|
||||
describe("applyModelProviderToolPolicy", () => {
|
||||
it("keeps web_search for non-xAI models", () => {
|
||||
const filtered = __testing.applyModelProviderToolPolicy(baseTools, {
|
||||
modelProvider: "openai",
|
||||
modelId: "gpt-4o-mini",
|
||||
});
|
||||
const filtered = __testing.applyModelProviderToolPolicy(baseTools);
|
||||
|
||||
expect(toolNames(filtered)).toEqual(["read", "web_search", "exec"]);
|
||||
});
|
||||
|
||||
@ -392,10 +392,11 @@ describe("createImageGenerateTool", () => {
|
||||
throw new Error("expected image_generate tool");
|
||||
}
|
||||
|
||||
await expect(tool.execute("call-bad-aspect", { prompt: "portrait", aspectRatio: "7:5" }))
|
||||
.rejects.toThrow(
|
||||
"aspectRatio must be one of 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, or 21:9",
|
||||
);
|
||||
await expect(
|
||||
tool.execute("call-bad-aspect", { prompt: "portrait", aspectRatio: "7:5" }),
|
||||
).rejects.toThrow(
|
||||
"aspectRatio must be one of 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, or 21:9",
|
||||
);
|
||||
});
|
||||
|
||||
it("lists registered provider and model options", async () => {
|
||||
|
||||
@ -230,7 +230,9 @@ function normalizeReferenceImages(args: Record<string, unknown>): string[] {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function parseImageGenerationModelRef(raw: string | undefined): { provider: string; model: string } | null {
|
||||
function parseImageGenerationModelRef(
|
||||
raw: string | undefined,
|
||||
): { provider: string; model: string } | null {
|
||||
const trimmed = raw?.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
@ -258,7 +260,8 @@ function resolveSelectedImageGenerationProvider(params: {
|
||||
}
|
||||
return listRuntimeImageGenerationProviders({ config: params.config }).find(
|
||||
(provider) =>
|
||||
provider.id === selectedRef.provider || (provider.aliases ?? []).includes(selectedRef.provider),
|
||||
provider.id === selectedRef.provider ||
|
||||
(provider.aliases ?? []).includes(selectedRef.provider),
|
||||
);
|
||||
}
|
||||
|
||||
@ -298,7 +301,9 @@ function validateImageGenerationCapabilities(params: {
|
||||
|
||||
if (params.size) {
|
||||
if (!modeCaps.supportsSize) {
|
||||
throw new ToolInputError(`${provider.id} ${isEdit ? "edit" : "generate"} does not support size overrides.`);
|
||||
throw new ToolInputError(
|
||||
`${provider.id} ${isEdit ? "edit" : "generate"} does not support size overrides.`,
|
||||
);
|
||||
}
|
||||
if ((geometry?.sizes?.length ?? 0) > 0 && !geometry?.sizes?.includes(params.size)) {
|
||||
throw new ToolInputError(
|
||||
@ -309,7 +314,9 @@ function validateImageGenerationCapabilities(params: {
|
||||
|
||||
if (params.aspectRatio) {
|
||||
if (!modeCaps.supportsAspectRatio) {
|
||||
throw new ToolInputError(`${provider.id} ${isEdit ? "edit" : "generate"} does not support aspectRatio overrides.`);
|
||||
throw new ToolInputError(
|
||||
`${provider.id} ${isEdit ? "edit" : "generate"} does not support aspectRatio overrides.`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
(geometry?.aspectRatios?.length ?? 0) > 0 &&
|
||||
@ -323,7 +330,9 @@ function validateImageGenerationCapabilities(params: {
|
||||
|
||||
if (params.resolution) {
|
||||
if (!modeCaps.supportsResolution) {
|
||||
throw new ToolInputError(`${provider.id} ${isEdit ? "edit" : "generate"} does not support resolution overrides.`);
|
||||
throw new ToolInputError(
|
||||
`${provider.id} ${isEdit ? "edit" : "generate"} does not support resolution overrides.`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
(geometry?.resolutions?.length ?? 0) > 0 &&
|
||||
|
||||
@ -26,7 +26,7 @@ type AssistantLikeMessage = {
|
||||
};
|
||||
|
||||
function resolveLiveXaiModel() {
|
||||
return getModel("xai", "grok-4-1-fast-reasoning") ?? getModel("xai", "grok-4");
|
||||
return getModel("xai", "grok-4");
|
||||
}
|
||||
|
||||
async function collectDoneMessage(
|
||||
|
||||
@ -722,7 +722,14 @@ export function createAccountScopedGroupAccessSection<TResolved>(params: {
|
||||
};
|
||||
}
|
||||
|
||||
type AccountScopedChannel = "discord" | "slack" | "telegram" | "imessage" | "signal";
|
||||
type AccountScopedChannel =
|
||||
| "bluebubbles"
|
||||
| "discord"
|
||||
| "imessage"
|
||||
| "line"
|
||||
| "signal"
|
||||
| "slack"
|
||||
| "telegram";
|
||||
type LegacyDmChannel = "discord" | "slack";
|
||||
|
||||
export function patchLegacyDmChannelConfig(params: {
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { PluginCompatibilityNotice } from "../plugins/status.js";
|
||||
|
||||
const readConfigFileSnapshot = vi.fn();
|
||||
const buildPluginCompatibilityNotices = vi.fn(() => []);
|
||||
const buildPluginCompatibilityNotices = vi.fn((): PluginCompatibilityNotice[] => []);
|
||||
|
||||
vi.mock("../config/config.js", () => ({
|
||||
readConfigFileSnapshot,
|
||||
|
||||
@ -184,13 +184,13 @@ async function promptWebToolsConfig(
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
return hasExistingKey(nextConfig, provider as SP) || hasKeyInEnv(entry);
|
||||
return hasExistingKey(nextConfig, provider) || hasKeyInEnv(entry);
|
||||
};
|
||||
|
||||
const existingProvider: SP = (() => {
|
||||
const stored = existingSearch?.provider;
|
||||
if (stored && SEARCH_PROVIDER_OPTIONS.some((e) => e.value === stored)) {
|
||||
return stored as SP;
|
||||
return stored;
|
||||
}
|
||||
return (
|
||||
SEARCH_PROVIDER_OPTIONS.find((e) => hasKeyForProvider(e.value))?.value ?? defaultProvider
|
||||
@ -242,8 +242,8 @@ async function promptWebToolsConfig(
|
||||
nextSearch = { ...nextSearch, provider: providerChoice };
|
||||
|
||||
const entry = SEARCH_PROVIDER_OPTIONS.find((e) => e.value === providerChoice)!;
|
||||
const existingKey = resolveExistingKey(nextConfig, providerChoice as SP);
|
||||
const keyConfigured = hasExistingKey(nextConfig, providerChoice as SP);
|
||||
const existingKey = resolveExistingKey(nextConfig, providerChoice);
|
||||
const keyConfigured = hasExistingKey(nextConfig, providerChoice);
|
||||
const envAvailable = entry.envKeys.some((k) => Boolean(process.env[k]?.trim()));
|
||||
const envVarNames = entry.envKeys.join(" / ");
|
||||
|
||||
@ -263,7 +263,7 @@ async function promptWebToolsConfig(
|
||||
const key = String(keyInput ?? "").trim();
|
||||
|
||||
if (key || existingKey) {
|
||||
const applied = applySearchKey(nextConfig, providerChoice as SP, (key || existingKey)!);
|
||||
const applied = applySearchKey(nextConfig, providerChoice, (key || existingKey)!);
|
||||
nextSearch = { ...applied.tools?.web?.search };
|
||||
} else if (keyConfigured || envAvailable) {
|
||||
nextSearch = { ...nextSearch };
|
||||
|
||||
@ -359,6 +359,8 @@ describe("normalizeCompatibilityConfigValues", () => {
|
||||
providers: {
|
||||
google: {
|
||||
apiKey: "existing-google-key",
|
||||
baseUrl: "https://generativelanguage.googleapis.com",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -474,6 +474,11 @@ export function normalizeCompatibilityConfigValues(cfg: OpenClawConfig): {
|
||||
};
|
||||
|
||||
const normalizeLegacyNanoBananaSkill = () => {
|
||||
type ModelProviderEntry = Partial<
|
||||
NonNullable<NonNullable<OpenClawConfig["models"]>["providers"]>[string]
|
||||
>;
|
||||
type ModelsConfigPatch = Partial<NonNullable<OpenClawConfig["models"]>>;
|
||||
|
||||
const rawSkills = next.skills;
|
||||
if (!isRecord(rawSkills)) {
|
||||
return;
|
||||
@ -544,14 +549,20 @@ export function normalizeCompatibilityConfigValues(cfg: OpenClawConfig): {
|
||||
? structuredClone(rawLegacyEntry.apiKey)
|
||||
: undefined);
|
||||
|
||||
const rawModels = isRecord(next.models) ? structuredClone(next.models) : {};
|
||||
const rawProviders = isRecord(rawModels.providers) ? { ...rawModels.providers } : {};
|
||||
const rawGoogle = isRecord(rawProviders.google) ? { ...rawProviders.google } : {};
|
||||
const rawModels = (
|
||||
isRecord(next.models) ? structuredClone(next.models) : {}
|
||||
) as ModelsConfigPatch;
|
||||
const rawProviders = (
|
||||
isRecord(rawModels.providers) ? { ...rawModels.providers } : {}
|
||||
) as Record<string, ModelProviderEntry>;
|
||||
const rawGoogle = (
|
||||
isRecord(rawProviders.google) ? { ...rawProviders.google } : {}
|
||||
) as ModelProviderEntry;
|
||||
const hasGoogleApiKey = rawGoogle.apiKey !== undefined;
|
||||
if (!hasGoogleApiKey && legacyApiKey) {
|
||||
rawGoogle.apiKey = legacyApiKey;
|
||||
rawProviders.google = rawGoogle;
|
||||
rawModels.providers = rawProviders;
|
||||
rawModels.providers = rawProviders as NonNullable<OpenClawConfig["models"]>["providers"];
|
||||
next = {
|
||||
...next,
|
||||
models: rawModels as OpenClawConfig["models"],
|
||||
|
||||
@ -444,6 +444,14 @@ export type MemorySearchConfig = {
|
||||
};
|
||||
};
|
||||
|
||||
type WebSearchLegacyProviderConfig = {
|
||||
apiKey?: SecretInput;
|
||||
baseUrl?: string;
|
||||
model?: string;
|
||||
mode?: string;
|
||||
inlineCitations?: boolean;
|
||||
};
|
||||
|
||||
export type ToolsConfig = {
|
||||
/** Base tool profile applied before allow/deny lists. */
|
||||
profile?: ToolProfileId;
|
||||
@ -465,6 +473,20 @@ export type ToolsConfig = {
|
||||
timeoutSeconds?: number;
|
||||
/** Cache TTL in minutes for search results. */
|
||||
cacheTtlMinutes?: number;
|
||||
/** @deprecated Legacy Brave credential path. */
|
||||
apiKey?: SecretInput;
|
||||
/** @deprecated Legacy Brave scoped config. */
|
||||
brave?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Firecrawl scoped config. */
|
||||
firecrawl?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Gemini scoped config. */
|
||||
gemini?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Grok scoped config. */
|
||||
grok?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Kimi scoped config. */
|
||||
kimi?: WebSearchLegacyProviderConfig;
|
||||
/** @deprecated Legacy Perplexity scoped config. */
|
||||
perplexity?: WebSearchLegacyProviderConfig;
|
||||
};
|
||||
fetch?: {
|
||||
/** Enable web fetch tool (default: true). */
|
||||
|
||||
@ -267,6 +267,57 @@ export const ToolsWebSearchSchema = z
|
||||
maxResults: z.number().int().positive().optional(),
|
||||
timeoutSeconds: z.number().int().positive().optional(),
|
||||
cacheTtlMinutes: z.number().nonnegative().optional(),
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
brave: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
mode: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
firecrawl: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
gemini: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
grok: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
inlineCitations: z.boolean().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
kimi: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
perplexity: z
|
||||
.object({
|
||||
apiKey: SecretInputSchema.optional().register(sensitive),
|
||||
baseUrl: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
@ -94,14 +94,22 @@ function aspectRatioToEnum(aspectRatio: string | undefined): string | undefined
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function aspectRatioToDimensions(aspectRatio: string, edge: number): { width: number; height: number } {
|
||||
function aspectRatioToDimensions(
|
||||
aspectRatio: string,
|
||||
edge: number,
|
||||
): { width: number; height: number } {
|
||||
const match = /^(\d+):(\d+)$/u.exec(aspectRatio.trim());
|
||||
if (!match) {
|
||||
throw new Error(`Invalid fal aspect ratio: ${aspectRatio}`);
|
||||
}
|
||||
const widthRatio = Number.parseInt(match[1] ?? "", 10);
|
||||
const heightRatio = Number.parseInt(match[2] ?? "", 10);
|
||||
if (!Number.isFinite(widthRatio) || !Number.isFinite(heightRatio) || widthRatio <= 0 || heightRatio <= 0) {
|
||||
if (
|
||||
!Number.isFinite(widthRatio) ||
|
||||
!Number.isFinite(heightRatio) ||
|
||||
widthRatio <= 0 ||
|
||||
heightRatio <= 0
|
||||
) {
|
||||
throw new Error(`Invalid fal aspect ratio: ${aspectRatio}`);
|
||||
}
|
||||
if (widthRatio >= heightRatio) {
|
||||
@ -140,7 +148,10 @@ function resolveFalImageSize(params: {
|
||||
return { width: edge, height: edge };
|
||||
}
|
||||
if (normalizedAspectRatio) {
|
||||
return aspectRatioToEnum(normalizedAspectRatio) ?? aspectRatioToDimensions(normalizedAspectRatio, 1024);
|
||||
return (
|
||||
aspectRatioToEnum(normalizedAspectRatio) ??
|
||||
aspectRatioToDimensions(normalizedAspectRatio, 1024)
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ describe("resolveOutboundSessionRoute", () => {
|
||||
from?: string;
|
||||
to?: string;
|
||||
threadId?: string | number;
|
||||
chatType?: "direct" | "group";
|
||||
chatType?: "channel" | "direct" | "group";
|
||||
};
|
||||
}> = [
|
||||
{
|
||||
|
||||
@ -972,7 +972,7 @@ describe("resolveOutboundSessionRoute", () => {
|
||||
from?: string;
|
||||
to?: string;
|
||||
threadId?: string | number;
|
||||
chatType?: "direct" | "group";
|
||||
chatType?: "channel" | "direct" | "group";
|
||||
};
|
||||
}> = [
|
||||
{
|
||||
|
||||
@ -1,6 +1,18 @@
|
||||
// Public ACP runtime helpers for plugins that integrate with ACP control/session state.
|
||||
|
||||
export { getAcpSessionManager } from "../acp/control-plane/manager.js";
|
||||
export { isAcpRuntimeError } from "../acp/runtime/errors.js";
|
||||
export { AcpRuntimeError, isAcpRuntimeError } from "../acp/runtime/errors.js";
|
||||
export type { AcpRuntimeErrorCode } from "../acp/runtime/errors.js";
|
||||
export type {
|
||||
AcpRuntime,
|
||||
AcpRuntimeCapabilities,
|
||||
AcpRuntimeDoctorReport,
|
||||
AcpRuntimeEnsureInput,
|
||||
AcpRuntimeEvent,
|
||||
AcpRuntimeHandle,
|
||||
AcpRuntimeStatus,
|
||||
AcpRuntimeTurnInput,
|
||||
AcpSessionUpdateTag,
|
||||
} from "../acp/runtime/types.js";
|
||||
export { readAcpSessionEntry } from "../acp/runtime/session-meta.js";
|
||||
export type { AcpSessionStoreEntry } from "../acp/runtime/session-meta.js";
|
||||
|
||||
@ -41,8 +41,11 @@ export function resolveOptionalConfigString(
|
||||
}
|
||||
|
||||
/** Build the shared allowlist/default target adapter surface for account-scoped channel configs. */
|
||||
export function createScopedAccountConfigAccessors<ResolvedAccount>(params: {
|
||||
resolveAccount: (params: { cfg: OpenClawConfig; accountId?: string | null }) => ResolvedAccount;
|
||||
export function createScopedAccountConfigAccessors<
|
||||
ResolvedAccount,
|
||||
Config extends OpenClawConfig = OpenClawConfig,
|
||||
>(params: {
|
||||
resolveAccount: (params: { cfg: Config; accountId?: string | null }) => ResolvedAccount;
|
||||
resolveAllowFrom: (account: ResolvedAccount) => Array<string | number> | null | undefined;
|
||||
formatAllowFrom: (allowFrom: Array<string | number>) => string[];
|
||||
resolveDefaultTo?: (account: ResolvedAccount) => string | number | null | undefined;
|
||||
@ -52,7 +55,9 @@ export function createScopedAccountConfigAccessors<ResolvedAccount>(params: {
|
||||
> {
|
||||
const base = {
|
||||
resolveAllowFrom: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string | null }) =>
|
||||
mapAllowFromEntries(params.resolveAllowFrom(params.resolveAccount({ cfg, accountId }))),
|
||||
mapAllowFromEntries(
|
||||
params.resolveAllowFrom(params.resolveAccount({ cfg: cfg as Config, accountId })),
|
||||
),
|
||||
formatAllowFrom: ({ allowFrom }: { allowFrom: Array<string | number> }) =>
|
||||
params.formatAllowFrom(allowFrom),
|
||||
};
|
||||
@ -65,7 +70,7 @@ export function createScopedAccountConfigAccessors<ResolvedAccount>(params: {
|
||||
...base,
|
||||
resolveDefaultTo: ({ cfg, accountId }) =>
|
||||
resolveOptionalConfigString(
|
||||
params.resolveDefaultTo?.(params.resolveAccount({ cfg, accountId })),
|
||||
params.resolveDefaultTo?.(params.resolveAccount({ cfg: cfg as Config, accountId })),
|
||||
),
|
||||
};
|
||||
}
|
||||
@ -160,7 +165,7 @@ export function createScopedChannelConfigAdapter<
|
||||
clearBaseFields: params.clearBaseFields,
|
||||
allowTopLevel: params.allowTopLevel,
|
||||
}),
|
||||
...createScopedAccountConfigAccessors<AccessorAccount>({
|
||||
...createScopedAccountConfigAccessors<AccessorAccount, Config>({
|
||||
resolveAccount: resolveAccessorAccount,
|
||||
resolveAllowFrom: params.resolveAllowFrom,
|
||||
formatAllowFrom: params.formatAllowFrom,
|
||||
@ -316,7 +321,7 @@ export function createTopLevelChannelConfigAdapter<
|
||||
deleteMode: params.deleteMode,
|
||||
clearBaseFields: params.clearBaseFields,
|
||||
}),
|
||||
...createScopedAccountConfigAccessors<AccessorAccount>({
|
||||
...createScopedAccountConfigAccessors<AccessorAccount, Config>({
|
||||
resolveAccount: resolveAccessorAccount,
|
||||
resolveAllowFrom: params.resolveAllowFrom,
|
||||
formatAllowFrom: params.formatAllowFrom,
|
||||
@ -438,7 +443,7 @@ export function createHybridChannelConfigAdapter<
|
||||
clearBaseFields: params.clearBaseFields,
|
||||
preserveSectionOnDefaultDelete: params.preserveSectionOnDefaultDelete,
|
||||
}),
|
||||
...createScopedAccountConfigAccessors<AccessorAccount>({
|
||||
...createScopedAccountConfigAccessors<AccessorAccount, Config>({
|
||||
resolveAccount: resolveAccessorAccount,
|
||||
resolveAllowFrom: params.resolveAllowFrom,
|
||||
formatAllowFrom: params.formatAllowFrom,
|
||||
|
||||
@ -44,6 +44,7 @@ export type {
|
||||
ProviderThinkingPolicyContext,
|
||||
ProviderWrapStreamFnContext,
|
||||
OpenClawPluginService,
|
||||
OpenClawPluginServiceContext,
|
||||
ProviderAuthContext,
|
||||
ProviderAuthDoctorHintContext,
|
||||
ProviderAuthMethodNonInteractiveContext,
|
||||
@ -51,6 +52,7 @@ export type {
|
||||
ProviderAuthResult,
|
||||
OpenClawPluginCommandDefinition,
|
||||
OpenClawPluginDefinition,
|
||||
PluginLogger,
|
||||
PluginInteractiveTelegramHandlerContext,
|
||||
} from "../plugins/types.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
|
||||
@ -25,7 +25,7 @@ function collectPluginSdkPackageExports(): string[] {
|
||||
}
|
||||
subpaths.push(key.slice("./plugin-sdk/".length));
|
||||
}
|
||||
return subpaths.sort();
|
||||
return subpaths.toSorted();
|
||||
}
|
||||
|
||||
function collectPluginSdkSourceNames(): string[] {
|
||||
@ -35,7 +35,7 @@ function collectPluginSdkSourceNames(): string[] {
|
||||
(entry) => entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".test.ts"),
|
||||
)
|
||||
.map((entry) => entry.name.slice(0, -".ts".length))
|
||||
.sort();
|
||||
.toSorted();
|
||||
}
|
||||
|
||||
function collectTextFiles(rootRelativeDir: string): string[] {
|
||||
@ -92,7 +92,7 @@ function collectPluginSdkSubpathReferences() {
|
||||
|
||||
describe("plugin-sdk package contract guardrails", () => {
|
||||
it("keeps package.json exports aligned with built plugin-sdk entrypoints", () => {
|
||||
expect(collectPluginSdkPackageExports()).toEqual([...pluginSdkEntrypoints].sort());
|
||||
expect(collectPluginSdkPackageExports()).toEqual([...pluginSdkEntrypoints].toSorted());
|
||||
});
|
||||
|
||||
it("keeps repo openclaw/plugin-sdk/<name> references on exported built subpaths", () => {
|
||||
@ -135,7 +135,7 @@ describe("plugin-sdk package contract guardrails", () => {
|
||||
failures.push(
|
||||
`src/plugin-sdk/${sourceName}.ts is referenced as openclaw/plugin-sdk/${sourceName} in ${matchingRefs
|
||||
.map((reference) => reference.file)
|
||||
.sort()
|
||||
.toSorted()
|
||||
.join(", ")}, but ${sourceName} is not exported as a public plugin-sdk subpath`,
|
||||
);
|
||||
}
|
||||
|
||||
@ -26,6 +26,8 @@ export type { StickerMetadata } from "../../extensions/telegram/api.js";
|
||||
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||
export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
|
||||
export { parseTelegramTopicConversation } from "../acp/conversation-id.js";
|
||||
export { clearAccountEntryFields } from "../channels/plugins/config-helpers.js";
|
||||
export { resolveTelegramPollVisibility } from "../poll-params.js";
|
||||
|
||||
export {
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
@ -38,9 +40,6 @@ export {
|
||||
setAccountEnabledInConfigSection,
|
||||
} from "./channel-plugin-common.js";
|
||||
|
||||
export { clearAccountEntryFields } from "../channels/plugins/config-helpers.js";
|
||||
export { resolveTelegramPollVisibility } from "../poll-params.js";
|
||||
|
||||
export {
|
||||
projectCredentialSnapshotFields,
|
||||
resolveConfiguredFromCredentialStatuses,
|
||||
|
||||
@ -99,6 +99,7 @@ describe("plugin shape compatibility matrix", () => {
|
||||
envVars: ["HYBRID_SEARCH_KEY"],
|
||||
placeholder: "hsk_...",
|
||||
signupUrl: "https://example.com/signup",
|
||||
credentialPath: "tools.web.search.hybrid-search.apiKey",
|
||||
getCredentialValue: () => "hsk-test",
|
||||
setCredentialValue(searchConfigTarget, value) {
|
||||
searchConfigTarget.apiKey = value;
|
||||
|
||||
@ -68,7 +68,10 @@ function createProviderSecretRefConfig(
|
||||
}
|
||||
|
||||
function readProviderKey(config: OpenClawConfig, provider: ProviderUnderTest): unknown {
|
||||
return config.plugins?.entries?.[providerPluginId(provider)]?.config?.webSearch?.apiKey;
|
||||
const pluginConfig = config.plugins?.entries?.[providerPluginId(provider)]?.config as
|
||||
| { webSearch?: { apiKey?: unknown } }
|
||||
| undefined;
|
||||
return pluginConfig?.webSearch?.apiKey;
|
||||
}
|
||||
|
||||
function expectInactiveFirecrawlSecretRef(params: {
|
||||
|
||||
@ -21,6 +21,7 @@ describe("web search runtime", () => {
|
||||
placeholder: "custom-...",
|
||||
signupUrl: "https://example.com/signup",
|
||||
autoDetectOrder: 1,
|
||||
credentialPath: "tools.web.search.custom.apiKey",
|
||||
getCredentialValue: () => "configured",
|
||||
setCredentialValue: () => {},
|
||||
createTool: () => ({
|
||||
|
||||
@ -199,5 +199,6 @@ export async function runWebSearch(
|
||||
|
||||
export const __testing = {
|
||||
resolveSearchConfig,
|
||||
resolveSearchProvider: resolveWebSearchProviderId,
|
||||
resolveWebSearchProviderId,
|
||||
};
|
||||
|
||||
@ -42,6 +42,8 @@ describe("config view", () => {
|
||||
themeMode: "system" as ThemeMode,
|
||||
setTheme: vi.fn(),
|
||||
setThemeMode: vi.fn(),
|
||||
borderRadius: 50,
|
||||
setBorderRadius: vi.fn(),
|
||||
gatewayUrl: "",
|
||||
assistantName: "OpenClaw",
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user