mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-21 16:41:56 +00:00
refactor: unify plugin sdk primitives
This commit is contained in:
@@ -925,6 +925,12 @@ authoring plugins:
|
||||
|
||||
- `openclaw/plugin-sdk/plugin-entry` for plugin registration primitives.
|
||||
- `openclaw/plugin-sdk/core` for the generic shared plugin-facing contract.
|
||||
- Stable channel primitives such as `openclaw/plugin-sdk/channel-setup`,
|
||||
`openclaw/plugin-sdk/channel-pairing`,
|
||||
`openclaw/plugin-sdk/channel-reply-pipeline`,
|
||||
`openclaw/plugin-sdk/secret-input`, and
|
||||
`openclaw/plugin-sdk/webhook-ingress` for shared setup/auth/reply/webhook
|
||||
wiring.
|
||||
- Domain subpaths such as `openclaw/plugin-sdk/channel-config-helpers`,
|
||||
`openclaw/plugin-sdk/channel-config-schema`,
|
||||
`openclaw/plugin-sdk/channel-policy`,
|
||||
@@ -961,6 +967,9 @@ authoring plugins:
|
||||
Compatibility note:
|
||||
|
||||
- Avoid the root `openclaw/plugin-sdk` barrel for new code.
|
||||
- Prefer the narrow stable primitives first. The newer setup/pairing/reply/
|
||||
secret-input/webhook subpaths are the intended contract for new bundled and
|
||||
external plugin work.
|
||||
- Bundled extension-specific helper barrels are not stable by default. If a
|
||||
helper is only needed by a bundled extension, keep it behind the extension's
|
||||
local `api.js` or `runtime-api.js` seam instead of promoting it into
|
||||
|
||||
@@ -95,8 +95,10 @@ subpaths rather than the monolithic root:
|
||||
```typescript
|
||||
// Correct: focused subpaths
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
|
||||
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
||||
import { createOptionalChannelSetupSurface } from "openclaw/plugin-sdk/channel-setup";
|
||||
import { resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
||||
|
||||
// Wrong: monolithic root (lint will reject this)
|
||||
@@ -105,17 +107,24 @@ import { ... } from "openclaw/plugin-sdk";
|
||||
|
||||
Common subpaths:
|
||||
|
||||
| Subpath | Purpose |
|
||||
| ---------------------------------- | ------------------------------------ |
|
||||
| `plugin-sdk/core` | Plugin entry definitions, base types |
|
||||
| `plugin-sdk/channel-runtime` | Channel runtime helpers |
|
||||
| `plugin-sdk/channel-config-schema` | Config schema builders |
|
||||
| `plugin-sdk/channel-policy` | Group/DM policy helpers |
|
||||
| `plugin-sdk/setup` | Setup wizard adapters |
|
||||
| `plugin-sdk/runtime-store` | Persistent plugin storage |
|
||||
| `plugin-sdk/allow-from` | Allowlist resolution |
|
||||
| `plugin-sdk/reply-payload` | Message reply types |
|
||||
| `plugin-sdk/testing` | Test utilities |
|
||||
| Subpath | Purpose |
|
||||
| ----------------------------------- | ------------------------------------ |
|
||||
| `plugin-sdk/core` | Plugin entry definitions, base types |
|
||||
| `plugin-sdk/channel-setup` | Optional setup adapters/wizards |
|
||||
| `plugin-sdk/channel-pairing` | DM pairing primitives |
|
||||
| `plugin-sdk/channel-reply-pipeline` | Prefix + typing reply wiring |
|
||||
| `plugin-sdk/channel-config-schema` | Config schema builders |
|
||||
| `plugin-sdk/channel-policy` | Group/DM policy helpers |
|
||||
| `plugin-sdk/secret-input` | Secret input parsing/helpers |
|
||||
| `plugin-sdk/webhook-ingress` | Webhook request/target helpers |
|
||||
| `plugin-sdk/runtime-store` | Persistent plugin storage |
|
||||
| `plugin-sdk/allow-from` | Allowlist resolution |
|
||||
| `plugin-sdk/reply-payload` | Message reply types |
|
||||
| `plugin-sdk/provider-onboard` | Provider onboarding config patches |
|
||||
| `plugin-sdk/testing` | Test utilities |
|
||||
|
||||
Use the narrowest primitive that matches the job. Reach for `channel-runtime`
|
||||
or other larger helper barrels only when a dedicated subpath does not exist yet.
|
||||
|
||||
## Step 4: Use local barrels for internal imports
|
||||
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import {
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "openclaw/plugin-sdk/secret-input-runtime";
|
||||
import { buildSecretInputSchema } from "openclaw/plugin-sdk/secret-input-schema";
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
};
|
||||
} from "openclaw/plugin-sdk/secret-input";
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/provider-models";
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
applyProviderConfigWithModelCatalogPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
|
||||
@@ -17,24 +17,20 @@ export { CHUTES_DEFAULT_MODEL_REF };
|
||||
* Registers all catalog models and sets provider aliases (chutes-fast, etc.).
|
||||
*/
|
||||
export function applyChutesProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
for (const m of CHUTES_MODEL_CATALOG) {
|
||||
models[`chutes/${m.id}`] = {
|
||||
...models[`chutes/${m.id}`],
|
||||
};
|
||||
}
|
||||
|
||||
models["chutes-fast"] = { alias: "chutes/zai-org/GLM-4.7-FP8" };
|
||||
models["chutes-vision"] = { alias: "chutes/chutesai/Mistral-Small-3.2-24B-Instruct-2506" };
|
||||
models["chutes-pro"] = { alias: "chutes/deepseek-ai/DeepSeek-V3.2-TEE" };
|
||||
|
||||
const chutesModels = CHUTES_MODEL_CATALOG.map(buildChutesModelDefinition);
|
||||
return applyProviderConfigWithModelCatalog(cfg, {
|
||||
agentModels: models,
|
||||
return applyProviderConfigWithModelCatalogPreset(cfg, {
|
||||
providerId: "chutes",
|
||||
api: "openai-completions",
|
||||
baseUrl: CHUTES_BASE_URL,
|
||||
catalogModels: chutesModels,
|
||||
catalogModels: CHUTES_MODEL_CATALOG.map(buildChutesModelDefinition),
|
||||
aliases: [
|
||||
...CHUTES_MODEL_CATALOG.map((model) => `chutes/${model.id}`),
|
||||
{ modelRef: "chutes-fast", alias: "chutes/zai-org/GLM-4.7-FP8" },
|
||||
{
|
||||
modelRef: "chutes-vision",
|
||||
alias: "chutes/chutesai/Mistral-Small-3.2-24B-Instruct-2506",
|
||||
},
|
||||
{ modelRef: "chutes-pro", alias: "chutes/deepseek-ai/DeepSeek-V3.2-TEE" },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { ChannelType, type RequestClient } from "@buape/carbon";
|
||||
import { resolveAckReaction, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import { EmbeddedBlockChunker } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import { shouldAckReaction as shouldAckReactionGate } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { logTypingFailure, logAckFailure } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { createReplyPrefixOptions } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { recordInboundSession } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import {
|
||||
createStatusReactionController,
|
||||
DEFAULT_TIMING,
|
||||
type StatusReactionAdapter,
|
||||
} from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { createTypingCallbacks } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { resolveDiscordPreviewStreamMode } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/config-runtime";
|
||||
@@ -420,11 +419,24 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
||||
? deliverTarget.slice("channel:".length)
|
||||
: messageChannelId;
|
||||
|
||||
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
||||
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
|
||||
cfg,
|
||||
agentId: route.agentId,
|
||||
channel: "discord",
|
||||
accountId: route.accountId,
|
||||
typing: {
|
||||
start: () => sendTyping({ client, channelId: typingChannelId }),
|
||||
onStartError: (err) => {
|
||||
logTypingFailure({
|
||||
log: logVerbose,
|
||||
channel: "discord",
|
||||
target: typingChannelId,
|
||||
error: err,
|
||||
});
|
||||
},
|
||||
// Long tool-heavy runs are expected on Discord; keep heartbeats alive.
|
||||
maxDurationMs: DISCORD_TYPING_MAX_DURATION_MS,
|
||||
},
|
||||
});
|
||||
const tableMode = resolveMarkdownTableMode({
|
||||
cfg,
|
||||
@@ -438,20 +450,6 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
||||
});
|
||||
const chunkMode = resolveChunkMode(cfg, "discord", accountId);
|
||||
|
||||
const typingCallbacks = createTypingCallbacks({
|
||||
start: () => sendTyping({ client, channelId: typingChannelId }),
|
||||
onStartError: (err) => {
|
||||
logTypingFailure({
|
||||
log: logVerbose,
|
||||
channel: "discord",
|
||||
target: typingChannelId,
|
||||
error: err,
|
||||
});
|
||||
},
|
||||
// Long tool-heavy runs are expected on Discord; keep heartbeats alive.
|
||||
maxDurationMs: DISCORD_TYPING_MAX_DURATION_MS,
|
||||
});
|
||||
|
||||
// --- Discord draft stream (edit-based preview streaming) ---
|
||||
const discordStreamMode = resolveDiscordPreviewStreamMode(discordConfig);
|
||||
const draftMaxChars = Math.min(textLimit, 2000);
|
||||
@@ -597,9 +595,8 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
||||
|
||||
const { dispatcher, replyOptions, markDispatchIdle, markRunComplete } =
|
||||
createReplyDispatcherWithTyping({
|
||||
...prefixOptions,
|
||||
...replyPipeline,
|
||||
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
||||
typingCallbacks,
|
||||
deliver: async (payload: ReplyPayload, info) => {
|
||||
if (isProcessAborted(abortSignal)) {
|
||||
return;
|
||||
@@ -715,7 +712,7 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
||||
if (isProcessAborted(abortSignal)) {
|
||||
return;
|
||||
}
|
||||
await typingCallbacks.onReplyStart();
|
||||
await replyPipeline.typingCallbacks?.onReplyStart();
|
||||
await statusReactions.setThinking();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -10,10 +10,9 @@ import {
|
||||
buildAgentMediaPayload,
|
||||
buildPendingHistoryContextFromMap,
|
||||
clearHistoryEntriesIfEnabled,
|
||||
createScopedPairingAccess,
|
||||
createChannelPairingController,
|
||||
DEFAULT_GROUP_HISTORY_LIMIT,
|
||||
type HistoryEntry,
|
||||
issuePairingChallenge,
|
||||
normalizeAgentId,
|
||||
recordPendingHistoryEntryIfEnabled,
|
||||
resolveAgentOutboundIdentity,
|
||||
@@ -445,7 +444,7 @@ export async function handleFeishuMessage(params: {
|
||||
|
||||
try {
|
||||
const core = getFeishuRuntime();
|
||||
const pairing = createScopedPairingAccess({
|
||||
const pairing = createChannelPairingController({
|
||||
core,
|
||||
channel: "feishu",
|
||||
accountId: account.accountId,
|
||||
@@ -471,12 +470,10 @@ export async function handleFeishuMessage(params: {
|
||||
|
||||
if (isDirect && dmPolicy !== "open" && !dmAllowed) {
|
||||
if (dmPolicy === "pairing") {
|
||||
await issuePairingChallenge({
|
||||
channel: "feishu",
|
||||
await pairing.issueChallenge({
|
||||
senderId: ctx.senderOpenId,
|
||||
senderIdLine: `Your Feishu user id: ${ctx.senderOpenId}`,
|
||||
meta: { name: ctx.senderName },
|
||||
upsertPairingRequest: pairing.upsertPairingRequest,
|
||||
onCreated: () => {
|
||||
log(`feishu[${account.accountId}]: pairing request sender=${ctx.senderOpenId}`);
|
||||
},
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "../runtime-api.js";
|
||||
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
};
|
||||
} from "openclaw/plugin-sdk/secret-input";
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {
|
||||
GROUP_POLICY_BLOCKED_LABEL,
|
||||
createScopedPairingAccess,
|
||||
createChannelPairingController,
|
||||
evaluateGroupRouteAccessForPolicy,
|
||||
issuePairingChallenge,
|
||||
isDangerousNameMatchingEnabled,
|
||||
resolveAllowlistProviderRuntimeGroupPolicy,
|
||||
resolveDefaultGroupPolicy,
|
||||
@@ -166,7 +165,7 @@ export async function applyGoogleChatInboundAccessPolicy(params: {
|
||||
} = params;
|
||||
const allowNameMatching = isDangerousNameMatchingEnabled(account.config);
|
||||
const spaceId = space.name ?? "";
|
||||
const pairing = createScopedPairingAccess({
|
||||
const pairing = createChannelPairingController({
|
||||
core,
|
||||
channel: "googlechat",
|
||||
accountId: account.accountId,
|
||||
@@ -311,12 +310,10 @@ export async function applyGoogleChatInboundAccessPolicy(params: {
|
||||
|
||||
if (access.decision !== "allow") {
|
||||
if (access.decision === "pairing") {
|
||||
await issuePairingChallenge({
|
||||
channel: "googlechat",
|
||||
await pairing.issueChallenge({
|
||||
senderId,
|
||||
senderIdLine: `Your Google Chat user id: ${senderId}`,
|
||||
meta: { name: senderName || undefined, email: senderEmail },
|
||||
upsertPairingRequest: pairing.upsertPairingRequest,
|
||||
onCreated: () => {
|
||||
logVerbose(`googlechat pairing request sender=${senderId}`);
|
||||
},
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
} from "openclaw/plugin-sdk/reply-payload";
|
||||
import type { OpenClawConfig } from "../runtime-api.js";
|
||||
import {
|
||||
createChannelReplyPipeline,
|
||||
createWebhookInFlightLimiter,
|
||||
createReplyPrefixOptions,
|
||||
registerWebhookTargetWithPluginRoute,
|
||||
resolveInboundRouteEnvelopeBuilderWithRuntime,
|
||||
resolveWebhookPath,
|
||||
@@ -307,7 +307,7 @@ async function processMessageWithPipeline(params: {
|
||||
}
|
||||
}
|
||||
|
||||
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
||||
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
|
||||
cfg: config,
|
||||
agentId: route.agentId,
|
||||
channel: "googlechat",
|
||||
@@ -318,7 +318,7 @@ async function processMessageWithPipeline(params: {
|
||||
ctx: ctxPayload,
|
||||
cfg: config,
|
||||
dispatcherOptions: {
|
||||
...prefixOptions,
|
||||
...replyPipeline,
|
||||
deliver: async (payload) => {
|
||||
await deliverGoogleChatReply({
|
||||
payload,
|
||||
|
||||
@@ -4,32 +4,27 @@ import {
|
||||
HUGGINGFACE_MODEL_CATALOG,
|
||||
} from "openclaw/plugin-sdk/provider-models";
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
applyProviderConfigWithModelCatalogPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
|
||||
export const HUGGINGFACE_DEFAULT_MODEL_REF = "huggingface/deepseek-ai/DeepSeek-R1";
|
||||
|
||||
export function applyHuggingfaceProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[HUGGINGFACE_DEFAULT_MODEL_REF] = {
|
||||
...models[HUGGINGFACE_DEFAULT_MODEL_REF],
|
||||
alias: models[HUGGINGFACE_DEFAULT_MODEL_REF]?.alias ?? "Hugging Face",
|
||||
};
|
||||
|
||||
return applyProviderConfigWithModelCatalog(cfg, {
|
||||
agentModels: models,
|
||||
function applyHuggingfacePreset(cfg: OpenClawConfig, primaryModelRef?: string): OpenClawConfig {
|
||||
return applyProviderConfigWithModelCatalogPreset(cfg, {
|
||||
providerId: "huggingface",
|
||||
api: "openai-completions",
|
||||
baseUrl: HUGGINGFACE_BASE_URL,
|
||||
catalogModels: HUGGINGFACE_MODEL_CATALOG.map(buildHuggingfaceModelDefinition),
|
||||
aliases: [{ modelRef: HUGGINGFACE_DEFAULT_MODEL_REF, alias: "Hugging Face" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyHuggingfaceConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(
|
||||
applyHuggingfaceProviderConfig(cfg),
|
||||
HUGGINGFACE_DEFAULT_MODEL_REF,
|
||||
);
|
||||
export function applyHuggingfaceProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyHuggingfacePreset(cfg);
|
||||
}
|
||||
|
||||
export function applyHuggingfaceConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyHuggingfacePreset(cfg, HUGGINGFACE_DEFAULT_MODEL_REF);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,9 @@ import {
|
||||
} from "./policy.js";
|
||||
import {
|
||||
GROUP_POLICY_BLOCKED_LABEL,
|
||||
createScopedPairingAccess,
|
||||
createChannelPairingController,
|
||||
deliverFormattedTextWithAttachments,
|
||||
dispatchInboundReplyWithBase,
|
||||
issuePairingChallenge,
|
||||
logInboundDrop,
|
||||
isDangerousNameMatchingEnabled,
|
||||
readStoreAllowFromForDmPolicy,
|
||||
@@ -90,7 +89,7 @@ export async function handleIrcInbound(params: {
|
||||
}): Promise<void> {
|
||||
const { message, account, config, runtime, connectedNick, statusSink } = params;
|
||||
const core = getIrcRuntime();
|
||||
const pairing = createScopedPairingAccess({
|
||||
const pairing = createChannelPairingController({
|
||||
core,
|
||||
channel: CHANNEL_ID,
|
||||
accountId: account.accountId,
|
||||
@@ -208,12 +207,10 @@ export async function handleIrcInbound(params: {
|
||||
}).allowed;
|
||||
if (!dmAllowed) {
|
||||
if (dmPolicy === "pairing") {
|
||||
await issuePairingChallenge({
|
||||
channel: CHANNEL_ID,
|
||||
await pairing.issueChallenge({
|
||||
senderId: senderDisplay.toLowerCase(),
|
||||
senderIdLine: `Your IRC id: ${senderDisplay}`,
|
||||
meta: { name: message.senderNick || undefined },
|
||||
upsertPairingRequest: pairing.upsertPairingRequest,
|
||||
sendPairingReply: async (text) => {
|
||||
await deliverIrcReply({
|
||||
payload: { text },
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithDefaultModel,
|
||||
applyProviderConfigWithDefaultModelPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
import {
|
||||
@@ -12,28 +11,30 @@ import {
|
||||
export const KIMI_MODEL_REF = `kimi/${KIMI_CODING_DEFAULT_MODEL_ID}`;
|
||||
export const KIMI_CODING_MODEL_REF = KIMI_MODEL_REF;
|
||||
|
||||
export function applyKimiCodeProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[KIMI_MODEL_REF] = {
|
||||
...models[KIMI_MODEL_REF],
|
||||
alias: models[KIMI_MODEL_REF]?.alias ?? "Kimi",
|
||||
};
|
||||
function resolveKimiCodingDefaultModel() {
|
||||
return buildKimiCodingProvider().models[0];
|
||||
}
|
||||
|
||||
const defaultModel = buildKimiCodingProvider().models[0];
|
||||
function applyKimiCodingPreset(cfg: OpenClawConfig, primaryModelRef?: string): OpenClawConfig {
|
||||
const defaultModel = resolveKimiCodingDefaultModel();
|
||||
if (!defaultModel) {
|
||||
return cfg;
|
||||
}
|
||||
|
||||
return applyProviderConfigWithDefaultModel(cfg, {
|
||||
agentModels: models,
|
||||
return applyProviderConfigWithDefaultModelPreset(cfg, {
|
||||
providerId: "kimi",
|
||||
api: "anthropic-messages",
|
||||
baseUrl: KIMI_CODING_BASE_URL,
|
||||
defaultModel,
|
||||
defaultModelId: KIMI_CODING_DEFAULT_MODEL_ID,
|
||||
aliases: [{ modelRef: KIMI_MODEL_REF, alias: "Kimi" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyKimiCodeConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(applyKimiCodeProviderConfig(cfg), KIMI_MODEL_REF);
|
||||
export function applyKimiCodeProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyKimiCodingPreset(cfg);
|
||||
}
|
||||
|
||||
export function applyKimiCodeConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyKimiCodingPreset(cfg, KIMI_MODEL_REF);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "../runtime-api.js";
|
||||
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
};
|
||||
} from "openclaw/plugin-sdk/secret-input";
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "./runtime-api.js";
|
||||
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
};
|
||||
} from "openclaw/plugin-sdk/secret-input";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithDefaultModel,
|
||||
applyProviderConfigWithDefaultModelPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
import {
|
||||
@@ -11,23 +10,22 @@ import {
|
||||
|
||||
export const MISTRAL_DEFAULT_MODEL_REF = `mistral/${MISTRAL_DEFAULT_MODEL_ID}`;
|
||||
|
||||
export function applyMistralProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[MISTRAL_DEFAULT_MODEL_REF] = {
|
||||
...models[MISTRAL_DEFAULT_MODEL_REF],
|
||||
alias: models[MISTRAL_DEFAULT_MODEL_REF]?.alias ?? "Mistral",
|
||||
};
|
||||
|
||||
return applyProviderConfigWithDefaultModel(cfg, {
|
||||
agentModels: models,
|
||||
function applyMistralPreset(cfg: OpenClawConfig, primaryModelRef?: string): OpenClawConfig {
|
||||
return applyProviderConfigWithDefaultModelPreset(cfg, {
|
||||
providerId: "mistral",
|
||||
api: "openai-completions",
|
||||
baseUrl: MISTRAL_BASE_URL,
|
||||
defaultModel: buildMistralModelDefinition(),
|
||||
defaultModelId: MISTRAL_DEFAULT_MODEL_ID,
|
||||
aliases: [{ modelRef: MISTRAL_DEFAULT_MODEL_REF, alias: "Mistral" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyMistralConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(applyMistralProviderConfig(cfg), MISTRAL_DEFAULT_MODEL_REF);
|
||||
export function applyMistralProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyMistralPreset(cfg);
|
||||
}
|
||||
|
||||
export function applyMistralConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyMistralPreset(cfg, MISTRAL_DEFAULT_MODEL_REF);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
applyProviderConfigWithModelCatalogPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
import {
|
||||
@@ -15,26 +14,19 @@ export { MODELSTUDIO_CN_BASE_URL, MODELSTUDIO_DEFAULT_MODEL_REF, MODELSTUDIO_GLO
|
||||
function applyModelStudioProviderConfigWithBaseUrl(
|
||||
cfg: OpenClawConfig,
|
||||
baseUrl: string,
|
||||
primaryModelRef?: string,
|
||||
): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
const provider = buildModelStudioProvider();
|
||||
for (const model of provider.models ?? []) {
|
||||
const modelRef = `modelstudio/${model.id}`;
|
||||
if (!models[modelRef]) {
|
||||
models[modelRef] = {};
|
||||
}
|
||||
}
|
||||
models[MODELSTUDIO_DEFAULT_MODEL_REF] = {
|
||||
...models[MODELSTUDIO_DEFAULT_MODEL_REF],
|
||||
alias: models[MODELSTUDIO_DEFAULT_MODEL_REF]?.alias ?? "Qwen",
|
||||
};
|
||||
|
||||
return applyProviderConfigWithModelCatalog(cfg, {
|
||||
agentModels: models,
|
||||
return applyProviderConfigWithModelCatalogPreset(cfg, {
|
||||
providerId: "modelstudio",
|
||||
api: provider.api ?? "openai-completions",
|
||||
baseUrl,
|
||||
catalogModels: provider.models ?? [],
|
||||
aliases: [
|
||||
...(provider.models ?? []).map((model) => `modelstudio/${model.id}`),
|
||||
{ modelRef: MODELSTUDIO_DEFAULT_MODEL_REF, alias: "Qwen" },
|
||||
],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,15 +39,17 @@ export function applyModelStudioProviderConfigCn(cfg: OpenClawConfig): OpenClawC
|
||||
}
|
||||
|
||||
export function applyModelStudioConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(
|
||||
applyModelStudioProviderConfig(cfg),
|
||||
return applyModelStudioProviderConfigWithBaseUrl(
|
||||
cfg,
|
||||
MODELSTUDIO_GLOBAL_BASE_URL,
|
||||
MODELSTUDIO_DEFAULT_MODEL_REF,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyModelStudioConfigCn(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(
|
||||
applyModelStudioProviderConfigCn(cfg),
|
||||
return applyModelStudioProviderConfigWithBaseUrl(
|
||||
cfg,
|
||||
MODELSTUDIO_CN_BASE_URL,
|
||||
MODELSTUDIO_DEFAULT_MODEL_REF,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithDefaultModel,
|
||||
applyProviderConfigWithDefaultModelPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
import {
|
||||
@@ -23,38 +22,32 @@ export function applyMoonshotProviderConfigCn(cfg: OpenClawConfig): OpenClawConf
|
||||
function applyMoonshotProviderConfigWithBaseUrl(
|
||||
cfg: OpenClawConfig,
|
||||
baseUrl: string,
|
||||
primaryModelRef?: string,
|
||||
): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[MOONSHOT_DEFAULT_MODEL_REF] = {
|
||||
...models[MOONSHOT_DEFAULT_MODEL_REF],
|
||||
alias: models[MOONSHOT_DEFAULT_MODEL_REF]?.alias ?? "Kimi",
|
||||
};
|
||||
|
||||
const defaultModel = buildMoonshotProvider().models[0];
|
||||
if (!defaultModel) {
|
||||
return cfg;
|
||||
}
|
||||
|
||||
return applyProviderConfigWithDefaultModel(cfg, {
|
||||
agentModels: models,
|
||||
return applyProviderConfigWithDefaultModelPreset(cfg, {
|
||||
providerId: "moonshot",
|
||||
api: "openai-completions",
|
||||
baseUrl,
|
||||
defaultModel,
|
||||
defaultModelId: MOONSHOT_DEFAULT_MODEL_ID,
|
||||
aliases: [{ modelRef: MOONSHOT_DEFAULT_MODEL_REF, alias: "Kimi" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyMoonshotConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(
|
||||
applyMoonshotProviderConfig(cfg),
|
||||
MOONSHOT_DEFAULT_MODEL_REF,
|
||||
);
|
||||
return applyMoonshotProviderConfigWithBaseUrl(cfg, MOONSHOT_BASE_URL, MOONSHOT_DEFAULT_MODEL_REF);
|
||||
}
|
||||
|
||||
export function applyMoonshotConfigCn(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(
|
||||
applyMoonshotProviderConfigCn(cfg),
|
||||
return applyMoonshotProviderConfigWithBaseUrl(
|
||||
cfg,
|
||||
MOONSHOT_CN_BASE_URL,
|
||||
MOONSHOT_DEFAULT_MODEL_REF,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import {
|
||||
GROUP_POLICY_BLOCKED_LABEL,
|
||||
createScopedPairingAccess,
|
||||
createChannelPairingController,
|
||||
deliverFormattedTextWithAttachments,
|
||||
dispatchInboundReplyWithBase,
|
||||
issuePairingChallenge,
|
||||
logInboundDrop,
|
||||
readStoreAllowFromForDmPolicy,
|
||||
resolveDmGroupAccessWithCommandGate,
|
||||
@@ -58,7 +57,7 @@ export async function handleNextcloudTalkInbound(params: {
|
||||
}): Promise<void> {
|
||||
const { message, account, config, runtime, statusSink } = params;
|
||||
const core = getNextcloudTalkRuntime();
|
||||
const pairing = createScopedPairingAccess({
|
||||
const pairing = createChannelPairingController({
|
||||
core,
|
||||
channel: CHANNEL_ID,
|
||||
accountId: account.accountId,
|
||||
@@ -172,12 +171,10 @@ export async function handleNextcloudTalkInbound(params: {
|
||||
} else {
|
||||
if (access.decision !== "allow") {
|
||||
if (access.decision === "pairing") {
|
||||
await issuePairingChallenge({
|
||||
channel: CHANNEL_ID,
|
||||
await pairing.issueChallenge({
|
||||
senderId,
|
||||
senderIdLine: `Your Nextcloud user id: ${senderId}`,
|
||||
meta: { name: senderName || undefined },
|
||||
upsertPairingRequest: pairing.upsertPairingRequest,
|
||||
sendPairingReply: async (text) => {
|
||||
await sendMessageNextcloudTalk(roomToken, text, { accountId: account.accountId });
|
||||
statusSink?.({ lastOutboundAt: Date.now() });
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "../runtime-api.js";
|
||||
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
};
|
||||
} from "openclaw/plugin-sdk/secret-input";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { OPENCODE_GO_DEFAULT_MODEL_REF } from "openclaw/plugin-sdk/provider-models";
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
withAgentModelAliases,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
|
||||
@@ -13,21 +14,19 @@ const OPENCODE_GO_ALIAS_DEFAULTS: Record<string, string> = {
|
||||
};
|
||||
|
||||
export function applyOpencodeGoProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
for (const [modelRef, alias] of Object.entries(OPENCODE_GO_ALIAS_DEFAULTS)) {
|
||||
models[modelRef] = {
|
||||
...models[modelRef],
|
||||
alias: models[modelRef]?.alias ?? alias,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
models,
|
||||
models: withAgentModelAliases(
|
||||
cfg.agents?.defaults?.models,
|
||||
Object.entries(OPENCODE_GO_ALIAS_DEFAULTS).map(([modelRef, alias]) => ({
|
||||
modelRef,
|
||||
alias,
|
||||
})),
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
import { OPENCODE_ZEN_DEFAULT_MODEL_REF } from "openclaw/plugin-sdk/provider-models";
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
withAgentModelAliases,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
|
||||
export { OPENCODE_ZEN_DEFAULT_MODEL_REF };
|
||||
|
||||
export function applyOpencodeZenProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[OPENCODE_ZEN_DEFAULT_MODEL_REF] = {
|
||||
...models[OPENCODE_ZEN_DEFAULT_MODEL_REF],
|
||||
alias: models[OPENCODE_ZEN_DEFAULT_MODEL_REF]?.alias ?? "Opus",
|
||||
};
|
||||
|
||||
return {
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
defaults: {
|
||||
...cfg.agents?.defaults,
|
||||
models,
|
||||
models: withAgentModelAliases(cfg.agents?.defaults?.models, [
|
||||
{ modelRef: OPENCODE_ZEN_DEFAULT_MODEL_REF, alias: "Opus" },
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithDefaultModels,
|
||||
applyProviderConfigWithDefaultModelsPreset,
|
||||
type ModelApi,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
@@ -12,12 +11,11 @@ import {
|
||||
|
||||
export const QIANFAN_DEFAULT_MODEL_REF = `qianfan/${QIANFAN_DEFAULT_MODEL_ID}`;
|
||||
|
||||
export function applyQianfanProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[QIANFAN_DEFAULT_MODEL_REF] = {
|
||||
...models[QIANFAN_DEFAULT_MODEL_REF],
|
||||
alias: models[QIANFAN_DEFAULT_MODEL_REF]?.alias ?? "QIANFAN",
|
||||
};
|
||||
function resolveQianfanPreset(cfg: OpenClawConfig): {
|
||||
api: ModelApi;
|
||||
baseUrl: string;
|
||||
defaultModels: NonNullable<ReturnType<typeof buildQianfanProvider>["models"]>;
|
||||
} {
|
||||
const defaultProvider = buildQianfanProvider();
|
||||
const existingProvider = cfg.models?.providers?.qianfan as
|
||||
| {
|
||||
@@ -27,22 +25,35 @@ export function applyQianfanProviderConfig(cfg: OpenClawConfig): OpenClawConfig
|
||||
| undefined;
|
||||
const existingBaseUrl =
|
||||
typeof existingProvider?.baseUrl === "string" ? existingProvider.baseUrl.trim() : "";
|
||||
const resolvedBaseUrl = existingBaseUrl || QIANFAN_BASE_URL;
|
||||
const resolvedApi =
|
||||
const api =
|
||||
typeof existingProvider?.api === "string"
|
||||
? (existingProvider.api as ModelApi)
|
||||
: "openai-completions";
|
||||
|
||||
return applyProviderConfigWithDefaultModels(cfg, {
|
||||
agentModels: models,
|
||||
providerId: "qianfan",
|
||||
api: resolvedApi,
|
||||
baseUrl: resolvedBaseUrl,
|
||||
return {
|
||||
api,
|
||||
baseUrl: existingBaseUrl || QIANFAN_BASE_URL,
|
||||
defaultModels: defaultProvider.models ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
function applyQianfanPreset(cfg: OpenClawConfig, primaryModelRef?: string): OpenClawConfig {
|
||||
const preset = resolveQianfanPreset(cfg);
|
||||
return applyProviderConfigWithDefaultModelsPreset(cfg, {
|
||||
providerId: "qianfan",
|
||||
api: preset.api,
|
||||
baseUrl: preset.baseUrl,
|
||||
defaultModels: preset.defaultModels,
|
||||
defaultModelId: QIANFAN_DEFAULT_MODEL_ID,
|
||||
aliases: [{ modelRef: QIANFAN_DEFAULT_MODEL_REF, alias: "QIANFAN" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyQianfanConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(applyQianfanProviderConfig(cfg), QIANFAN_DEFAULT_MODEL_REF);
|
||||
export function applyQianfanProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyQianfanPreset(cfg);
|
||||
}
|
||||
|
||||
export function applyQianfanConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyQianfanPreset(cfg, QIANFAN_DEFAULT_MODEL_REF);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import { removeAckReactionAfterReply } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { logAckFailure, logTypingFailure } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { createReplyPrefixOptions } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { createTypingCallbacks } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { resolveStorePath, updateLastRoute } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { resolveAgentOutboundIdentity } from "openclaw/plugin-sdk/infra-runtime";
|
||||
import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
||||
@@ -147,63 +146,62 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
|
||||
const typingTarget = statusThreadTs ? `${message.channel}/${statusThreadTs}` : message.channel;
|
||||
const typingReaction = ctx.typingReaction;
|
||||
const typingCallbacks = createTypingCallbacks({
|
||||
start: async () => {
|
||||
didSetStatus = true;
|
||||
await ctx.setSlackThreadStatus({
|
||||
channelId: message.channel,
|
||||
threadTs: statusThreadTs,
|
||||
status: "is typing...",
|
||||
});
|
||||
if (typingReaction && message.ts) {
|
||||
await reactSlackMessage(message.channel, message.ts, typingReaction, {
|
||||
token: ctx.botToken,
|
||||
client: ctx.app.client,
|
||||
}).catch(() => {});
|
||||
}
|
||||
},
|
||||
stop: async () => {
|
||||
if (!didSetStatus) {
|
||||
return;
|
||||
}
|
||||
didSetStatus = false;
|
||||
await ctx.setSlackThreadStatus({
|
||||
channelId: message.channel,
|
||||
threadTs: statusThreadTs,
|
||||
status: "",
|
||||
});
|
||||
if (typingReaction && message.ts) {
|
||||
await removeSlackReaction(message.channel, message.ts, typingReaction, {
|
||||
token: ctx.botToken,
|
||||
client: ctx.app.client,
|
||||
}).catch(() => {});
|
||||
}
|
||||
},
|
||||
onStartError: (err) => {
|
||||
logTypingFailure({
|
||||
log: (message) => runtime.error?.(danger(message)),
|
||||
channel: "slack",
|
||||
action: "start",
|
||||
target: typingTarget,
|
||||
error: err,
|
||||
});
|
||||
},
|
||||
onStopError: (err) => {
|
||||
logTypingFailure({
|
||||
log: (message) => runtime.error?.(danger(message)),
|
||||
channel: "slack",
|
||||
action: "stop",
|
||||
target: typingTarget,
|
||||
error: err,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
||||
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
|
||||
cfg,
|
||||
agentId: route.agentId,
|
||||
channel: "slack",
|
||||
accountId: route.accountId,
|
||||
typing: {
|
||||
start: async () => {
|
||||
didSetStatus = true;
|
||||
await ctx.setSlackThreadStatus({
|
||||
channelId: message.channel,
|
||||
threadTs: statusThreadTs,
|
||||
status: "is typing...",
|
||||
});
|
||||
if (typingReaction && message.ts) {
|
||||
await reactSlackMessage(message.channel, message.ts, typingReaction, {
|
||||
token: ctx.botToken,
|
||||
client: ctx.app.client,
|
||||
}).catch(() => {});
|
||||
}
|
||||
},
|
||||
stop: async () => {
|
||||
if (!didSetStatus) {
|
||||
return;
|
||||
}
|
||||
didSetStatus = false;
|
||||
await ctx.setSlackThreadStatus({
|
||||
channelId: message.channel,
|
||||
threadTs: statusThreadTs,
|
||||
status: "",
|
||||
});
|
||||
if (typingReaction && message.ts) {
|
||||
await removeSlackReaction(message.channel, message.ts, typingReaction, {
|
||||
token: ctx.botToken,
|
||||
client: ctx.app.client,
|
||||
}).catch(() => {});
|
||||
}
|
||||
},
|
||||
onStartError: (err) => {
|
||||
logTypingFailure({
|
||||
log: (message) => runtime.error?.(danger(message)),
|
||||
channel: "slack",
|
||||
action: "start",
|
||||
target: typingTarget,
|
||||
error: err,
|
||||
});
|
||||
},
|
||||
onStopError: (err) => {
|
||||
logTypingFailure({
|
||||
log: (message) => runtime.error?.(danger(message)),
|
||||
channel: "slack",
|
||||
action: "stop",
|
||||
target: typingTarget,
|
||||
error: err,
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const slackStreaming = resolveSlackStreamingConfig({
|
||||
@@ -299,9 +297,8 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
};
|
||||
|
||||
const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
|
||||
...prefixOptions,
|
||||
...replyPipeline,
|
||||
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
||||
typingCallbacks,
|
||||
deliver: async (payload) => {
|
||||
if (useStreaming) {
|
||||
await deliverWithStreaming(payload);
|
||||
@@ -367,7 +364,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
},
|
||||
onError: (err, info) => {
|
||||
runtime.error?.(danger(`slack ${info.kind} reply failed: ${String(err)}`));
|
||||
typingCallbacks.onIdle?.();
|
||||
replyPipeline.typingCallbacks?.onIdle?.();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -5,32 +5,27 @@ import {
|
||||
SYNTHETIC_MODEL_CATALOG,
|
||||
} from "openclaw/plugin-sdk/provider-models";
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
applyProviderConfigWithModelCatalogPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
|
||||
export { SYNTHETIC_DEFAULT_MODEL_REF };
|
||||
|
||||
export function applySyntheticProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[SYNTHETIC_DEFAULT_MODEL_REF] = {
|
||||
...models[SYNTHETIC_DEFAULT_MODEL_REF],
|
||||
alias: models[SYNTHETIC_DEFAULT_MODEL_REF]?.alias ?? "MiniMax M2.5",
|
||||
};
|
||||
|
||||
return applyProviderConfigWithModelCatalog(cfg, {
|
||||
agentModels: models,
|
||||
function applySyntheticPreset(cfg: OpenClawConfig, primaryModelRef?: string): OpenClawConfig {
|
||||
return applyProviderConfigWithModelCatalogPreset(cfg, {
|
||||
providerId: "synthetic",
|
||||
api: "anthropic-messages",
|
||||
baseUrl: SYNTHETIC_BASE_URL,
|
||||
catalogModels: SYNTHETIC_MODEL_CATALOG.map(buildSyntheticModelDefinition),
|
||||
aliases: [{ modelRef: SYNTHETIC_DEFAULT_MODEL_REF, alias: "MiniMax M2.5" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
export function applySyntheticConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(
|
||||
applySyntheticProviderConfig(cfg),
|
||||
SYNTHETIC_DEFAULT_MODEL_REF,
|
||||
);
|
||||
export function applySyntheticProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applySyntheticPreset(cfg);
|
||||
}
|
||||
|
||||
export function applySyntheticConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applySyntheticPreset(cfg, SYNTHETIC_DEFAULT_MODEL_REF);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ import {
|
||||
modelSupportsVision,
|
||||
} from "openclaw/plugin-sdk/agent-runtime";
|
||||
import { resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import { removeAckReactionAfterReply } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { logAckFailure, logTypingFailure } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { createReplyPrefixOptions } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { createTypingCallbacks } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/config-runtime";
|
||||
import {
|
||||
loadSessionStore,
|
||||
@@ -381,12 +380,6 @@ export const dispatchTelegramMessage = async ({
|
||||
? true
|
||||
: undefined;
|
||||
|
||||
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
||||
cfg,
|
||||
agentId: route.agentId,
|
||||
channel: "telegram",
|
||||
accountId: route.accountId,
|
||||
});
|
||||
const chunkMode = resolveChunkMode(cfg, "telegram", route.accountId);
|
||||
|
||||
// Handle uncached stickers: get a dedicated vision description before dispatch
|
||||
@@ -524,15 +517,21 @@ export const dispatchTelegramMessage = async ({
|
||||
void statusReactionController.setThinking();
|
||||
}
|
||||
|
||||
const typingCallbacks = createTypingCallbacks({
|
||||
start: sendTyping,
|
||||
onStartError: (err) => {
|
||||
logTypingFailure({
|
||||
log: logVerbose,
|
||||
channel: "telegram",
|
||||
target: String(chatId),
|
||||
error: err,
|
||||
});
|
||||
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
|
||||
cfg,
|
||||
agentId: route.agentId,
|
||||
channel: "telegram",
|
||||
accountId: route.accountId,
|
||||
typing: {
|
||||
start: sendTyping,
|
||||
onStartError: (err) => {
|
||||
logTypingFailure({
|
||||
log: logVerbose,
|
||||
channel: "telegram",
|
||||
target: String(chatId),
|
||||
error: err,
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -542,8 +541,7 @@ export const dispatchTelegramMessage = async ({
|
||||
ctx: ctxPayload,
|
||||
cfg,
|
||||
dispatcherOptions: {
|
||||
...prefixOptions,
|
||||
typingCallbacks,
|
||||
...replyPipeline,
|
||||
deliver: async (payload, info) => {
|
||||
if (payload.isError === true) {
|
||||
hadErrorReplyFailureOrSkip = true;
|
||||
|
||||
@@ -4,32 +4,27 @@ import {
|
||||
TOGETHER_MODEL_CATALOG,
|
||||
} from "openclaw/plugin-sdk/provider-models";
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
applyProviderConfigWithModelCatalogPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
|
||||
export const TOGETHER_DEFAULT_MODEL_REF = "together/moonshotai/Kimi-K2.5";
|
||||
|
||||
export function applyTogetherProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[TOGETHER_DEFAULT_MODEL_REF] = {
|
||||
...models[TOGETHER_DEFAULT_MODEL_REF],
|
||||
alias: models[TOGETHER_DEFAULT_MODEL_REF]?.alias ?? "Together AI",
|
||||
};
|
||||
|
||||
return applyProviderConfigWithModelCatalog(cfg, {
|
||||
agentModels: models,
|
||||
function applyTogetherPreset(cfg: OpenClawConfig, primaryModelRef?: string): OpenClawConfig {
|
||||
return applyProviderConfigWithModelCatalogPreset(cfg, {
|
||||
providerId: "together",
|
||||
api: "openai-completions",
|
||||
baseUrl: TOGETHER_BASE_URL,
|
||||
catalogModels: TOGETHER_MODEL_CATALOG.map(buildTogetherModelDefinition),
|
||||
aliases: [{ modelRef: TOGETHER_DEFAULT_MODEL_REF, alias: "Together AI" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyTogetherConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(
|
||||
applyTogetherProviderConfig(cfg),
|
||||
TOGETHER_DEFAULT_MODEL_REF,
|
||||
);
|
||||
export function applyTogetherProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyTogetherPreset(cfg);
|
||||
}
|
||||
|
||||
export function applyTogetherConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyTogetherPreset(cfg, TOGETHER_DEFAULT_MODEL_REF);
|
||||
}
|
||||
|
||||
@@ -5,29 +5,27 @@ import {
|
||||
VENICE_MODEL_CATALOG,
|
||||
} from "openclaw/plugin-sdk/provider-models";
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
applyProviderConfigWithModelCatalogPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
|
||||
export { VENICE_DEFAULT_MODEL_REF };
|
||||
|
||||
export function applyVeniceProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[VENICE_DEFAULT_MODEL_REF] = {
|
||||
...models[VENICE_DEFAULT_MODEL_REF],
|
||||
alias: models[VENICE_DEFAULT_MODEL_REF]?.alias ?? "Kimi K2.5",
|
||||
};
|
||||
|
||||
return applyProviderConfigWithModelCatalog(cfg, {
|
||||
agentModels: models,
|
||||
function applyVenicePreset(cfg: OpenClawConfig, primaryModelRef?: string): OpenClawConfig {
|
||||
return applyProviderConfigWithModelCatalogPreset(cfg, {
|
||||
providerId: "venice",
|
||||
api: "openai-completions",
|
||||
baseUrl: VENICE_BASE_URL,
|
||||
catalogModels: VENICE_MODEL_CATALOG.map(buildVeniceModelDefinition),
|
||||
aliases: [{ modelRef: VENICE_DEFAULT_MODEL_REF, alias: "Kimi K2.5" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyVeniceConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(applyVeniceProviderConfig(cfg), VENICE_DEFAULT_MODEL_REF);
|
||||
export function applyVeniceProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyVenicePreset(cfg);
|
||||
}
|
||||
|
||||
export function applyVeniceConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyVenicePreset(cfg, VENICE_DEFAULT_MODEL_REF);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithDefaultModels,
|
||||
applyProviderConfigWithDefaultModelsPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
import { XAI_BASE_URL, XAI_DEFAULT_MODEL_ID } from "./model-definitions.js";
|
||||
@@ -11,20 +10,16 @@ export const XAI_DEFAULT_MODEL_REF = `xai/${XAI_DEFAULT_MODEL_ID}`;
|
||||
function applyXaiProviderConfigWithApi(
|
||||
cfg: OpenClawConfig,
|
||||
api: "openai-completions" | "openai-responses",
|
||||
primaryModelRef?: string,
|
||||
): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[XAI_DEFAULT_MODEL_REF] = {
|
||||
...models[XAI_DEFAULT_MODEL_REF],
|
||||
alias: models[XAI_DEFAULT_MODEL_REF]?.alias ?? "Grok",
|
||||
};
|
||||
|
||||
return applyProviderConfigWithDefaultModels(cfg, {
|
||||
agentModels: models,
|
||||
return applyProviderConfigWithDefaultModelsPreset(cfg, {
|
||||
providerId: "xai",
|
||||
api,
|
||||
baseUrl: XAI_BASE_URL,
|
||||
defaultModels: buildXaiCatalogModels(),
|
||||
defaultModelId: XAI_DEFAULT_MODEL_ID,
|
||||
aliases: [{ modelRef: XAI_DEFAULT_MODEL_REF, alias: "Grok" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,5 +32,5 @@ export function applyXaiResponsesApiConfig(cfg: OpenClawConfig): OpenClawConfig
|
||||
}
|
||||
|
||||
export function applyXaiConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(applyXaiProviderConfig(cfg), XAI_DEFAULT_MODEL_REF);
|
||||
return applyXaiProviderConfigWithApi(cfg, "openai-completions", XAI_DEFAULT_MODEL_REF);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
applyProviderConfigWithModelCatalogPreset,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
import {
|
||||
@@ -19,32 +18,35 @@ const ZAI_DEFAULT_MODELS = [
|
||||
buildZaiModelDefinition({ id: "glm-4.7-flashx" }),
|
||||
];
|
||||
|
||||
function resolveZaiPresetBaseUrl(cfg: OpenClawConfig, endpoint?: string): string {
|
||||
const existingProvider = cfg.models?.providers?.zai;
|
||||
const existingBaseUrl =
|
||||
typeof existingProvider?.baseUrl === "string" ? existingProvider.baseUrl.trim() : "";
|
||||
return endpoint ? resolveZaiBaseUrl(endpoint) : existingBaseUrl || resolveZaiBaseUrl();
|
||||
}
|
||||
|
||||
function applyZaiPreset(
|
||||
cfg: OpenClawConfig,
|
||||
params?: { endpoint?: string; modelId?: string },
|
||||
primaryModelRef?: string,
|
||||
): OpenClawConfig {
|
||||
const modelId = params?.modelId?.trim() || ZAI_DEFAULT_MODEL_ID;
|
||||
const modelRef = `zai/${modelId}`;
|
||||
return applyProviderConfigWithModelCatalogPreset(cfg, {
|
||||
providerId: "zai",
|
||||
api: "openai-completions",
|
||||
baseUrl: resolveZaiPresetBaseUrl(cfg, params?.endpoint),
|
||||
catalogModels: ZAI_DEFAULT_MODELS,
|
||||
aliases: [{ modelRef, alias: "GLM" }],
|
||||
primaryModelRef,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyZaiProviderConfig(
|
||||
cfg: OpenClawConfig,
|
||||
params?: { endpoint?: string; modelId?: string },
|
||||
): OpenClawConfig {
|
||||
const modelId = params?.modelId?.trim() || ZAI_DEFAULT_MODEL_ID;
|
||||
const modelRef = `zai/${modelId}`;
|
||||
const existingProvider = cfg.models?.providers?.zai;
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[modelRef] = {
|
||||
...models[modelRef],
|
||||
alias: models[modelRef]?.alias ?? "GLM",
|
||||
};
|
||||
|
||||
const existingBaseUrl =
|
||||
typeof existingProvider?.baseUrl === "string" ? existingProvider.baseUrl.trim() : "";
|
||||
const baseUrl = params?.endpoint
|
||||
? resolveZaiBaseUrl(params.endpoint)
|
||||
: existingBaseUrl || resolveZaiBaseUrl();
|
||||
|
||||
return applyProviderConfigWithModelCatalog(cfg, {
|
||||
agentModels: models,
|
||||
providerId: "zai",
|
||||
api: "openai-completions",
|
||||
baseUrl,
|
||||
catalogModels: ZAI_DEFAULT_MODELS,
|
||||
});
|
||||
return applyZaiPreset(cfg, params);
|
||||
}
|
||||
|
||||
export function applyZaiConfig(
|
||||
@@ -53,5 +55,5 @@ export function applyZaiConfig(
|
||||
): OpenClawConfig {
|
||||
const modelId = params?.modelId?.trim() || ZAI_DEFAULT_MODEL_ID;
|
||||
const modelRef = modelId === ZAI_DEFAULT_MODEL_ID ? ZAI_DEFAULT_MODEL_REF : `zai/${modelId}`;
|
||||
return applyAgentDefaultModelPrimary(applyZaiProviderConfig(cfg, params), modelRef);
|
||||
return applyZaiPreset(cfg, params, modelRef);
|
||||
}
|
||||
|
||||
@@ -30,11 +30,9 @@ import {
|
||||
import { resolveZaloProxyFetch } from "./proxy.js";
|
||||
import type { MarkdownTableMode, OpenClawConfig, OutboundReplyPayload } from "./runtime-api.js";
|
||||
import {
|
||||
createTypingCallbacks,
|
||||
createScopedPairingAccess,
|
||||
createReplyPrefixOptions,
|
||||
createChannelPairingController,
|
||||
createChannelReplyPipeline,
|
||||
deliverTextOrMediaReply,
|
||||
issuePairingChallenge,
|
||||
resolveWebhookPath,
|
||||
logTypingFailure,
|
||||
resolveDefaultGroupPolicy,
|
||||
@@ -330,7 +328,7 @@ async function processMessageWithPipeline(params: ZaloMessagePipelineParams): Pr
|
||||
statusSink,
|
||||
fetcher,
|
||||
} = params;
|
||||
const pairing = createScopedPairingAccess({
|
||||
const pairing = createChannelPairingController({
|
||||
core,
|
||||
channel: "zalo",
|
||||
accountId: account.accountId,
|
||||
@@ -406,12 +404,10 @@ async function processMessageWithPipeline(params: ZaloMessagePipelineParams): Pr
|
||||
}
|
||||
if (directDmOutcome === "unauthorized") {
|
||||
if (dmPolicy === "pairing") {
|
||||
await issuePairingChallenge({
|
||||
channel: "zalo",
|
||||
await pairing.issueChallenge({
|
||||
senderId,
|
||||
senderIdLine: `Your Zalo user id: ${senderId}`,
|
||||
meta: { name: senderName ?? undefined },
|
||||
upsertPairingRequest: pairing.upsertPairingRequest,
|
||||
onCreated: () => {
|
||||
logVerbose(core, runtime, `zalo pairing request sender=${senderId}`);
|
||||
},
|
||||
@@ -507,32 +503,32 @@ async function processMessageWithPipeline(params: ZaloMessagePipelineParams): Pr
|
||||
channel: "zalo",
|
||||
accountId: account.accountId,
|
||||
});
|
||||
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
||||
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
|
||||
cfg: config,
|
||||
agentId: route.agentId,
|
||||
channel: "zalo",
|
||||
accountId: account.accountId,
|
||||
});
|
||||
const typingCallbacks = createTypingCallbacks({
|
||||
start: async () => {
|
||||
await sendChatAction(
|
||||
token,
|
||||
{
|
||||
chat_id: chatId,
|
||||
action: "typing",
|
||||
},
|
||||
fetcher,
|
||||
ZALO_TYPING_TIMEOUT_MS,
|
||||
);
|
||||
},
|
||||
onStartError: (err) => {
|
||||
logTypingFailure({
|
||||
log: (message) => logVerbose(core, runtime, message),
|
||||
channel: "zalo",
|
||||
action: "start",
|
||||
target: chatId,
|
||||
error: err,
|
||||
});
|
||||
typing: {
|
||||
start: async () => {
|
||||
await sendChatAction(
|
||||
token,
|
||||
{
|
||||
chat_id: chatId,
|
||||
action: "typing",
|
||||
},
|
||||
fetcher,
|
||||
ZALO_TYPING_TIMEOUT_MS,
|
||||
);
|
||||
},
|
||||
onStartError: (err) => {
|
||||
logTypingFailure({
|
||||
log: (message) => logVerbose(core, runtime, message),
|
||||
channel: "zalo",
|
||||
action: "start",
|
||||
target: chatId,
|
||||
error: err,
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -540,8 +536,7 @@ async function processMessageWithPipeline(params: ZaloMessagePipelineParams): Pr
|
||||
ctx: ctxPayload,
|
||||
cfg: config,
|
||||
dispatcherOptions: {
|
||||
...prefixOptions,
|
||||
typingCallbacks,
|
||||
...replyPipeline,
|
||||
deliver: async (payload) => {
|
||||
await deliverZaloReply({
|
||||
payload,
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "./runtime-api.js";
|
||||
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
};
|
||||
} from "openclaw/plugin-sdk/secret-input";
|
||||
|
||||
@@ -18,13 +18,11 @@ import type {
|
||||
RuntimeEnv,
|
||||
} from "../runtime-api.js";
|
||||
import {
|
||||
createTypingCallbacks,
|
||||
createScopedPairingAccess,
|
||||
createReplyPrefixOptions,
|
||||
createChannelPairingController,
|
||||
createChannelReplyPipeline,
|
||||
deliverTextOrMediaReply,
|
||||
evaluateGroupRouteAccessForPolicy,
|
||||
isDangerousNameMatchingEnabled,
|
||||
issuePairingChallenge,
|
||||
mergeAllowlist,
|
||||
resolveMentionGatingWithBypass,
|
||||
resolveOpenProviderRuntimeGroupPolicy,
|
||||
@@ -252,7 +250,7 @@ async function processMessage(
|
||||
historyState: ZalouserGroupHistoryState,
|
||||
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void,
|
||||
): Promise<void> {
|
||||
const pairing = createScopedPairingAccess({
|
||||
const pairing = createChannelPairingController({
|
||||
core,
|
||||
channel: "zalouser",
|
||||
accountId: account.accountId,
|
||||
@@ -389,12 +387,10 @@ async function processMessage(
|
||||
|
||||
if (!isGroup && accessDecision.decision !== "allow") {
|
||||
if (accessDecision.decision === "pairing") {
|
||||
await issuePairingChallenge({
|
||||
channel: "zalouser",
|
||||
await pairing.issueChallenge({
|
||||
senderId,
|
||||
senderIdLine: `Your Zalo user id: ${senderId}`,
|
||||
meta: { name: senderName || undefined },
|
||||
upsertPairingRequest: pairing.upsertPairingRequest,
|
||||
onCreated: () => {
|
||||
logVerbose(core, runtime, `zalouser pairing request sender=${senderId}`);
|
||||
},
|
||||
@@ -630,24 +626,24 @@ async function processMessage(
|
||||
},
|
||||
});
|
||||
|
||||
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
||||
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
|
||||
cfg: config,
|
||||
agentId: route.agentId,
|
||||
channel: "zalouser",
|
||||
accountId: account.accountId,
|
||||
});
|
||||
const typingCallbacks = createTypingCallbacks({
|
||||
start: async () => {
|
||||
await sendTypingZalouser(chatId, {
|
||||
profile: account.profile,
|
||||
isGroup,
|
||||
});
|
||||
},
|
||||
onStartError: (err) => {
|
||||
runtime.error?.(
|
||||
`[${account.accountId}] zalouser typing start failed for ${chatId}: ${String(err)}`,
|
||||
);
|
||||
logVerbose(core, runtime, `zalouser typing failed for ${chatId}: ${String(err)}`);
|
||||
typing: {
|
||||
start: async () => {
|
||||
await sendTypingZalouser(chatId, {
|
||||
profile: account.profile,
|
||||
isGroup,
|
||||
});
|
||||
},
|
||||
onStartError: (err) => {
|
||||
runtime.error?.(
|
||||
`[${account.accountId}] zalouser typing start failed for ${chatId}: ${String(err)}`,
|
||||
);
|
||||
logVerbose(core, runtime, `zalouser typing failed for ${chatId}: ${String(err)}`);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -655,8 +651,7 @@ async function processMessage(
|
||||
ctx: ctxPayload,
|
||||
cfg: config,
|
||||
dispatcherOptions: {
|
||||
...prefixOptions,
|
||||
typingCallbacks,
|
||||
...replyPipeline,
|
||||
deliver: async (payload) => {
|
||||
await deliverZalouserReply({
|
||||
payload: payload as { text?: string; mediaUrls?: string[]; mediaUrl?: string },
|
||||
|
||||
20
package.json
20
package.json
@@ -78,6 +78,10 @@
|
||||
"types": "./dist/plugin-sdk/setup.d.ts",
|
||||
"default": "./dist/plugin-sdk/setup.js"
|
||||
},
|
||||
"./plugin-sdk/channel-setup": {
|
||||
"types": "./dist/plugin-sdk/channel-setup.d.ts",
|
||||
"default": "./dist/plugin-sdk/channel-setup.js"
|
||||
},
|
||||
"./plugin-sdk/setup-tools": {
|
||||
"types": "./dist/plugin-sdk/setup-tools.d.ts",
|
||||
"default": "./dist/plugin-sdk/setup-tools.js"
|
||||
@@ -94,6 +98,10 @@
|
||||
"types": "./dist/plugin-sdk/reply-payload.d.ts",
|
||||
"default": "./dist/plugin-sdk/reply-payload.js"
|
||||
},
|
||||
"./plugin-sdk/channel-reply-pipeline": {
|
||||
"types": "./dist/plugin-sdk/channel-reply-pipeline.d.ts",
|
||||
"default": "./dist/plugin-sdk/channel-reply-pipeline.js"
|
||||
},
|
||||
"./plugin-sdk/channel-runtime": {
|
||||
"types": "./dist/plugin-sdk/channel-runtime.d.ts",
|
||||
"default": "./dist/plugin-sdk/channel-runtime.js"
|
||||
@@ -254,6 +262,10 @@
|
||||
"types": "./dist/plugin-sdk/channel-lifecycle.d.ts",
|
||||
"default": "./dist/plugin-sdk/channel-lifecycle.js"
|
||||
},
|
||||
"./plugin-sdk/channel-pairing": {
|
||||
"types": "./dist/plugin-sdk/channel-pairing.d.ts",
|
||||
"default": "./dist/plugin-sdk/channel-pairing.js"
|
||||
},
|
||||
"./plugin-sdk/channel-policy": {
|
||||
"types": "./dist/plugin-sdk/channel-policy.d.ts",
|
||||
"default": "./dist/plugin-sdk/channel-policy.js"
|
||||
@@ -334,6 +346,10 @@
|
||||
"types": "./dist/plugin-sdk/request-url.d.ts",
|
||||
"default": "./dist/plugin-sdk/request-url.js"
|
||||
},
|
||||
"./plugin-sdk/webhook-ingress": {
|
||||
"types": "./dist/plugin-sdk/webhook-ingress.d.ts",
|
||||
"default": "./dist/plugin-sdk/webhook-ingress.js"
|
||||
},
|
||||
"./plugin-sdk/webhook-path": {
|
||||
"types": "./dist/plugin-sdk/webhook-path.d.ts",
|
||||
"default": "./dist/plugin-sdk/webhook-path.js"
|
||||
@@ -342,6 +358,10 @@
|
||||
"types": "./dist/plugin-sdk/runtime-store.d.ts",
|
||||
"default": "./dist/plugin-sdk/runtime-store.js"
|
||||
},
|
||||
"./plugin-sdk/secret-input": {
|
||||
"types": "./dist/plugin-sdk/secret-input.d.ts",
|
||||
"default": "./dist/plugin-sdk/secret-input.js"
|
||||
},
|
||||
"./plugin-sdk/web-media": {
|
||||
"types": "./dist/plugin-sdk/web-media.d.ts",
|
||||
"default": "./dist/plugin-sdk/web-media.js"
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
"runtime",
|
||||
"runtime-env",
|
||||
"setup",
|
||||
"channel-setup",
|
||||
"setup-tools",
|
||||
"config-runtime",
|
||||
"reply-runtime",
|
||||
"reply-payload",
|
||||
"channel-reply-pipeline",
|
||||
"channel-runtime",
|
||||
"interactive-runtime",
|
||||
"infra-runtime",
|
||||
@@ -53,6 +55,7 @@
|
||||
"channel-config-helpers",
|
||||
"channel-config-schema",
|
||||
"channel-lifecycle",
|
||||
"channel-pairing",
|
||||
"channel-policy",
|
||||
"channel-send-result",
|
||||
"group-access",
|
||||
@@ -73,8 +76,10 @@
|
||||
"reply-history",
|
||||
"media-understanding",
|
||||
"request-url",
|
||||
"webhook-ingress",
|
||||
"webhook-path",
|
||||
"runtime-store",
|
||||
"secret-input",
|
||||
"web-media",
|
||||
"speech",
|
||||
"state-paths",
|
||||
|
||||
@@ -3,9 +3,12 @@ import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { AgentModelEntryConfig } from "../config/types.agent-defaults.js";
|
||||
import type { ModelDefinitionConfig } from "../config/types.models.js";
|
||||
import {
|
||||
applyProviderConfigWithDefaultModelPreset,
|
||||
applyProviderConfigWithModelCatalogPreset,
|
||||
applyProviderConfigWithDefaultModel,
|
||||
applyProviderConfigWithDefaultModels,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
withAgentModelAliases,
|
||||
} from "../plugins/provider-onboarding-config.js";
|
||||
|
||||
function makeModel(id: string): ModelDefinitionConfig {
|
||||
@@ -97,4 +100,76 @@ describe("onboard auth provider config merges", () => {
|
||||
|
||||
expect(next.models?.providers?.custom?.models?.map((m) => m.id)).toEqual(["model-z"]);
|
||||
});
|
||||
|
||||
it("preserves explicit aliases when adding provider alias presets", () => {
|
||||
expect(
|
||||
withAgentModelAliases(
|
||||
{
|
||||
"custom/model-a": { alias: "Pinned" },
|
||||
},
|
||||
[{ modelRef: "custom/model-a", alias: "Preset" }, "custom/model-b"],
|
||||
),
|
||||
).toEqual({
|
||||
"custom/model-a": { alias: "Pinned" },
|
||||
"custom/model-b": {},
|
||||
});
|
||||
});
|
||||
|
||||
it("applies default-model presets with alias and primary model", () => {
|
||||
const next = applyProviderConfigWithDefaultModelPreset(
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"custom/model-z": { alias: "Pinned" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
providerId: "custom",
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://example.com/v1",
|
||||
defaultModel: makeModel("model-z"),
|
||||
aliases: [{ modelRef: "custom/model-z", alias: "Preset" }],
|
||||
primaryModelRef: "custom/model-z",
|
||||
},
|
||||
);
|
||||
|
||||
expect(next.agents?.defaults?.models?.["custom/model-z"]).toEqual({ alias: "Pinned" });
|
||||
expect(next.agents?.defaults?.model).toEqual({ primary: "custom/model-z" });
|
||||
});
|
||||
|
||||
it("applies catalog presets with alias and merged catalog models", () => {
|
||||
const next = applyProviderConfigWithModelCatalogPreset(
|
||||
{
|
||||
models: {
|
||||
providers: {
|
||||
custom: {
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://example.com/v1",
|
||||
models: [makeModel("model-a")],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
providerId: "custom",
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://example.com/v1",
|
||||
catalogModels: [makeModel("model-a"), makeModel("model-b")],
|
||||
aliases: [{ modelRef: "custom/model-b", alias: "Catalog Alias" }],
|
||||
primaryModelRef: "custom/model-b",
|
||||
},
|
||||
);
|
||||
|
||||
expect(next.models?.providers?.custom?.models?.map((model) => model.id)).toEqual([
|
||||
"model-a",
|
||||
"model-b",
|
||||
]);
|
||||
expect(next.agents?.defaults?.models?.["custom/model-b"]).toEqual({
|
||||
alias: "Catalog Alias",
|
||||
});
|
||||
expect(next.agents?.defaults?.model).toEqual({ primary: "custom/model-b" });
|
||||
});
|
||||
});
|
||||
|
||||
48
src/plugin-sdk/channel-pairing.test.ts
Normal file
48
src/plugin-sdk/channel-pairing.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { PluginRuntime } from "../plugins/runtime/types.js";
|
||||
import { createChannelPairingController } from "./channel-pairing.js";
|
||||
|
||||
describe("createChannelPairingController", () => {
|
||||
it("scopes store access and issues pairing challenges through the scoped store", async () => {
|
||||
const readAllowFromStore = vi.fn(async () => ["alice"]);
|
||||
const upsertPairingRequest = vi.fn(async () => ({ code: "123456", created: true }));
|
||||
const replies: string[] = [];
|
||||
const sendPairingReply = vi.fn(async (text: string) => {
|
||||
replies.push(text);
|
||||
});
|
||||
const runtime = {
|
||||
channel: {
|
||||
pairing: {
|
||||
readAllowFromStore,
|
||||
upsertPairingRequest,
|
||||
},
|
||||
},
|
||||
} as unknown as PluginRuntime;
|
||||
|
||||
const pairing = createChannelPairingController({
|
||||
core: runtime,
|
||||
channel: "googlechat",
|
||||
accountId: "Primary",
|
||||
});
|
||||
|
||||
await expect(pairing.readAllowFromStore()).resolves.toEqual(["alice"]);
|
||||
await pairing.issueChallenge({
|
||||
senderId: "user-1",
|
||||
senderIdLine: "Your id: user-1",
|
||||
sendPairingReply,
|
||||
});
|
||||
|
||||
expect(readAllowFromStore).toHaveBeenCalledWith({
|
||||
channel: "googlechat",
|
||||
accountId: "primary",
|
||||
});
|
||||
expect(upsertPairingRequest).toHaveBeenCalledWith({
|
||||
channel: "googlechat",
|
||||
accountId: "primary",
|
||||
id: "user-1",
|
||||
meta: undefined,
|
||||
});
|
||||
expect(sendPairingReply).toHaveBeenCalledTimes(1);
|
||||
expect(replies[0]).toContain("123456");
|
||||
});
|
||||
});
|
||||
31
src/plugin-sdk/channel-pairing.ts
Normal file
31
src/plugin-sdk/channel-pairing.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { ChannelId } from "../channels/plugins/types.js";
|
||||
import { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
import type { PluginRuntime } from "../plugins/runtime/types.js";
|
||||
import { createScopedPairingAccess } from "./pairing-access.js";
|
||||
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
|
||||
type ScopedPairingAccess = ReturnType<typeof createScopedPairingAccess>;
|
||||
|
||||
export type ChannelPairingController = ScopedPairingAccess & {
|
||||
issueChallenge: (
|
||||
params: Omit<Parameters<typeof issuePairingChallenge>[0], "channel" | "upsertPairingRequest">,
|
||||
) => ReturnType<typeof issuePairingChallenge>;
|
||||
};
|
||||
|
||||
export function createChannelPairingController(params: {
|
||||
core: PluginRuntime;
|
||||
channel: ChannelId;
|
||||
accountId: string;
|
||||
}): ChannelPairingController {
|
||||
const access = createScopedPairingAccess(params);
|
||||
return {
|
||||
...access,
|
||||
issueChallenge: (challenge) =>
|
||||
issuePairingChallenge({
|
||||
channel: params.channel,
|
||||
upsertPairingRequest: access.upsertPairingRequest,
|
||||
...challenge,
|
||||
}),
|
||||
};
|
||||
}
|
||||
39
src/plugin-sdk/channel-reply-pipeline.test.ts
Normal file
39
src/plugin-sdk/channel-reply-pipeline.test.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createChannelReplyPipeline } from "./channel-reply-pipeline.js";
|
||||
|
||||
describe("createChannelReplyPipeline", () => {
|
||||
it("builds prefix options without forcing typing support", () => {
|
||||
const pipeline = createChannelReplyPipeline({
|
||||
cfg: {},
|
||||
agentId: "main",
|
||||
channel: "telegram",
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
expect(typeof pipeline.onModelSelected).toBe("function");
|
||||
expect(typeof pipeline.responsePrefixContextProvider).toBe("function");
|
||||
expect(pipeline.typingCallbacks).toBeUndefined();
|
||||
});
|
||||
|
||||
it("builds typing callbacks when typing config is provided", async () => {
|
||||
const start = vi.fn(async () => {});
|
||||
const stop = vi.fn(async () => {});
|
||||
const pipeline = createChannelReplyPipeline({
|
||||
cfg: {},
|
||||
agentId: "main",
|
||||
channel: "discord",
|
||||
accountId: "default",
|
||||
typing: {
|
||||
start,
|
||||
stop,
|
||||
onStartError: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
await pipeline.typingCallbacks?.onReplyStart();
|
||||
pipeline.typingCallbacks?.onIdle?.();
|
||||
|
||||
expect(start).toHaveBeenCalled();
|
||||
expect(stop).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
38
src/plugin-sdk/channel-reply-pipeline.ts
Normal file
38
src/plugin-sdk/channel-reply-pipeline.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
createReplyPrefixContext,
|
||||
createReplyPrefixOptions,
|
||||
type ReplyPrefixContextBundle,
|
||||
type ReplyPrefixOptions,
|
||||
} from "../channels/reply-prefix.js";
|
||||
import {
|
||||
createTypingCallbacks,
|
||||
type CreateTypingCallbacksParams,
|
||||
type TypingCallbacks,
|
||||
} from "../channels/typing.js";
|
||||
|
||||
export type ReplyPrefixContext = ReplyPrefixContextBundle["prefixContext"];
|
||||
export type { ReplyPrefixContextBundle, ReplyPrefixOptions };
|
||||
export type { CreateTypingCallbacksParams, TypingCallbacks };
|
||||
export { createReplyPrefixContext, createReplyPrefixOptions, createTypingCallbacks };
|
||||
|
||||
export type ChannelReplyPipeline = ReplyPrefixOptions & {
|
||||
typingCallbacks?: TypingCallbacks;
|
||||
};
|
||||
|
||||
export function createChannelReplyPipeline(params: {
|
||||
cfg: Parameters<typeof createReplyPrefixOptions>[0]["cfg"];
|
||||
agentId: string;
|
||||
channel?: string;
|
||||
accountId?: string;
|
||||
typing?: CreateTypingCallbacksParams;
|
||||
}): ChannelReplyPipeline {
|
||||
return {
|
||||
...createReplyPrefixOptions({
|
||||
cfg: params.cfg,
|
||||
agentId: params.agentId,
|
||||
channel: params.channel,
|
||||
accountId: params.accountId,
|
||||
}),
|
||||
...(params.typing ? { typingCallbacks: createTypingCallbacks(params.typing) } : {}),
|
||||
};
|
||||
}
|
||||
38
src/plugin-sdk/channel-setup.test.ts
Normal file
38
src/plugin-sdk/channel-setup.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createOptionalChannelSetupSurface } from "./channel-setup.js";
|
||||
|
||||
describe("createOptionalChannelSetupSurface", () => {
|
||||
it("returns a matched adapter and wizard for optional plugins", async () => {
|
||||
const setup = createOptionalChannelSetupSurface({
|
||||
channel: "example",
|
||||
label: "Example",
|
||||
npmSpec: "@openclaw/example",
|
||||
docsPath: "/channels/example",
|
||||
});
|
||||
|
||||
expect(setup.setupAdapter.resolveAccountId?.({ cfg: {} })).toBe("default");
|
||||
expect(
|
||||
setup.setupAdapter.validateInput?.({
|
||||
cfg: {},
|
||||
accountId: "default",
|
||||
input: {},
|
||||
}),
|
||||
).toContain("@openclaw/example");
|
||||
expect(setup.setupWizard.channel).toBe("example");
|
||||
expect(setup.setupWizard.status.unconfiguredHint).toContain("/channels/example");
|
||||
await expect(
|
||||
setup.setupWizard.finalize?.({
|
||||
cfg: {},
|
||||
accountId: "default",
|
||||
credentialValues: {},
|
||||
runtime: {
|
||||
log: () => {},
|
||||
error: () => {},
|
||||
exit: async () => {},
|
||||
},
|
||||
prompter: {} as never,
|
||||
forceAllowFrom: false,
|
||||
}),
|
||||
).rejects.toThrow("@openclaw/example");
|
||||
});
|
||||
});
|
||||
42
src/plugin-sdk/channel-setup.ts
Normal file
42
src/plugin-sdk/channel-setup.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { ChannelSetupWizard } from "../channels/plugins/setup-wizard.js";
|
||||
import type { ChannelSetupAdapter } from "../channels/plugins/types.adapters.js";
|
||||
import {
|
||||
createOptionalChannelSetupAdapter,
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
|
||||
export type { ChannelSetupAdapter } from "../channels/plugins/types.adapters.js";
|
||||
export type { ChannelSetupDmPolicy, ChannelSetupWizard } from "./setup.js";
|
||||
export {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
createTopLevelChannelDmPolicy,
|
||||
formatDocsLink,
|
||||
setSetupChannelEnabled,
|
||||
splitSetupEntries,
|
||||
} from "./setup.js";
|
||||
|
||||
type OptionalChannelSetupParams = {
|
||||
channel: string;
|
||||
label: string;
|
||||
npmSpec?: string;
|
||||
docsPath?: string;
|
||||
};
|
||||
|
||||
export type OptionalChannelSetupSurface = {
|
||||
setupAdapter: ChannelSetupAdapter;
|
||||
setupWizard: ChannelSetupWizard;
|
||||
};
|
||||
|
||||
export {
|
||||
createOptionalChannelSetupAdapter,
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
|
||||
export function createOptionalChannelSetupSurface(
|
||||
params: OptionalChannelSetupParams,
|
||||
): OptionalChannelSetupSurface {
|
||||
return {
|
||||
setupAdapter: createOptionalChannelSetupAdapter(params),
|
||||
setupWizard: createOptionalChannelSetupWizard(params),
|
||||
};
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export type {
|
||||
} from "../channels/plugins/types.adapters.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { createReplyPrefixContext } from "../channels/reply-prefix.js";
|
||||
export { createTypingCallbacks } from "../channels/typing.js";
|
||||
export { createChannelReplyPipeline, createTypingCallbacks } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig as ClawdbotConfig, OpenClawConfig } from "../config/config.js";
|
||||
export {
|
||||
resolveAllowlistProviderRuntimeGroupPolicy,
|
||||
@@ -47,13 +47,13 @@ export {
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "../config/runtime-group-policy.js";
|
||||
export type { DmPolicy, GroupToolPolicyConfig } from "../config/types.js";
|
||||
export type { SecretInput } from "../config/types.secrets.js";
|
||||
export type { SecretInput } from "./secret-input.js";
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "../config/types.secrets.js";
|
||||
export { buildSecretInputSchema } from "./secret-input-schema.js";
|
||||
} from "./secret-input.js";
|
||||
export { createDedupeCache } from "../infra/dedupe.js";
|
||||
export { installRequestBodyLimitGuard, readJsonBodyWithLimit } from "../infra/http-body.js";
|
||||
export { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
|
||||
@@ -70,8 +70,7 @@ export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { feishuSetupWizard, feishuSetupAdapter } from "../../extensions/feishu/setup-api.js";
|
||||
export { buildAgentMediaPayload } from "./agent-media-payload.js";
|
||||
export { readJsonFileWithFallback } from "./json-store.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { createChannelPairingController, createScopedPairingAccess } from "./channel-pairing.js";
|
||||
export { createPersistentDedupe } from "./persistent-dedupe.js";
|
||||
export {
|
||||
buildBaseChannelStatusSummary,
|
||||
@@ -85,9 +84,9 @@ export {
|
||||
parseFeishuConversationId,
|
||||
} from "../../extensions/feishu/src/conversation-id.js";
|
||||
export {
|
||||
createFixedWindowRateLimiter,
|
||||
createWebhookAnomalyTracker,
|
||||
createFixedWindowRateLimiter,
|
||||
WEBHOOK_ANOMALY_COUNTER_DEFAULTS,
|
||||
WEBHOOK_RATE_LIMIT_DEFAULTS,
|
||||
} from "./webhook-memory-guards.js";
|
||||
export { applyBasicWebhookRequestGuards } from "./webhook-request-guards.js";
|
||||
} from "./webhook-ingress.js";
|
||||
export { applyBasicWebhookRequestGuards } from "./webhook-ingress.js";
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// Keep this list additive and scoped to symbols used under extensions/googlechat.
|
||||
|
||||
import { resolveChannelGroupRequireMention } from "./channel-policy.js";
|
||||
import {
|
||||
createOptionalChannelSetupAdapter,
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
import { createOptionalChannelSetupSurface } from "./channel-setup.js";
|
||||
|
||||
export {
|
||||
createActionGate,
|
||||
@@ -49,7 +46,7 @@ export type {
|
||||
} from "../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { getChatChannelMeta } from "../channels/registry.js";
|
||||
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
|
||||
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export { isDangerousNameMatchingEnabled } from "../config/dangerous-name-matching.js";
|
||||
export {
|
||||
@@ -71,26 +68,23 @@ export { resolveDmGroupAccessWithLists } from "../security/dm-policy-shared.js";
|
||||
export { formatDocsLink } from "../terminal/links.js";
|
||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { resolveInboundRouteEnvelopeBuilderWithRuntime } from "./inbound-envelope.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { createChannelPairingController, createScopedPairingAccess } from "./channel-pairing.js";
|
||||
export {
|
||||
evaluateGroupRouteAccessForPolicy,
|
||||
resolveSenderScopedGroupPolicy,
|
||||
} from "./group-access.js";
|
||||
export { extractToolSend } from "./tool-send.js";
|
||||
export { resolveWebhookPath } from "./webhook-path.js";
|
||||
export type { WebhookInFlightLimiter } from "./webhook-request-guards.js";
|
||||
export {
|
||||
beginWebhookRequestPipelineOrReject,
|
||||
createWebhookInFlightLimiter,
|
||||
readJsonWebhookBodyOrReject,
|
||||
} from "./webhook-request-guards.js";
|
||||
export {
|
||||
registerWebhookTargetWithPluginRoute,
|
||||
resolveWebhookTargets,
|
||||
resolveWebhookPath,
|
||||
resolveWebhookTargetWithAuthOrReject,
|
||||
resolveWebhookTargets,
|
||||
type WebhookInFlightLimiter,
|
||||
withResolvedWebhookRequestPipeline,
|
||||
} from "./webhook-targets.js";
|
||||
} from "./webhook-ingress.js";
|
||||
|
||||
type GoogleChatGroupContext = {
|
||||
cfg: import("../config/config.js").OpenClawConfig;
|
||||
@@ -107,16 +101,12 @@ export function resolveGoogleChatGroupRequireMention(params: GoogleChatGroupCont
|
||||
});
|
||||
}
|
||||
|
||||
export const googlechatSetupAdapter = createOptionalChannelSetupAdapter({
|
||||
const googlechatSetup = createOptionalChannelSetupSurface({
|
||||
channel: "googlechat",
|
||||
label: "Google Chat",
|
||||
npmSpec: "@openclaw/googlechat",
|
||||
docsPath: "/channels/googlechat",
|
||||
});
|
||||
|
||||
export const googlechatSetupWizard = createOptionalChannelSetupWizard({
|
||||
channel: "googlechat",
|
||||
label: "Google Chat",
|
||||
npmSpec: "@openclaw/googlechat",
|
||||
docsPath: "/channels/googlechat",
|
||||
});
|
||||
export const googlechatSetupAdapter = googlechatSetup.setupAdapter;
|
||||
export const googlechatSetupWizard = googlechatSetup.setupWizard;
|
||||
|
||||
@@ -23,7 +23,7 @@ export { patchScopedAccountConfig } from "../channels/plugins/setup-helpers.js";
|
||||
export type { BaseProbeResult } from "../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { getChatChannelMeta } from "../channels/registry.js";
|
||||
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
|
||||
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export { isDangerousNameMatchingEnabled } from "../config/dangerous-name-matching.js";
|
||||
export {
|
||||
@@ -69,8 +69,7 @@ export {
|
||||
} from "../security/dm-policy-shared.js";
|
||||
export { formatDocsLink } from "../terminal/links.js";
|
||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { createChannelPairingController, createScopedPairingAccess } from "./channel-pairing.js";
|
||||
export { dispatchInboundReplyWithBase } from "./inbound-reply-dispatch.js";
|
||||
export { ircSetupAdapter, ircSetupWizard } from "../../extensions/irc/api.js";
|
||||
export type { OutboundReplyPayload } from "./reply-payload.js";
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// Narrow plugin-sdk surface for the bundled matrix plugin.
|
||||
// Keep this list additive and scoped to symbols used under extensions/matrix.
|
||||
|
||||
import {
|
||||
createOptionalChannelSetupAdapter,
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
import { createOptionalChannelSetupSurface } from "./channel-setup.js";
|
||||
|
||||
export {
|
||||
createActionGate,
|
||||
@@ -60,8 +57,8 @@ export type {
|
||||
ChannelToolSend,
|
||||
} from "../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
|
||||
export { createTypingCallbacks } from "../channels/typing.js";
|
||||
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
|
||||
export { createTypingCallbacks } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export {
|
||||
GROUP_POLICY_BLOCKED_LABEL,
|
||||
@@ -75,13 +72,13 @@ export type {
|
||||
GroupToolPolicyConfig,
|
||||
MarkdownTableMode,
|
||||
} from "../config/types.js";
|
||||
export type { SecretInput } from "../config/types.secrets.js";
|
||||
export type { SecretInput } from "./secret-input.js";
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "../config/types.secrets.js";
|
||||
export { buildSecretInputSchema } from "./secret-input-schema.js";
|
||||
} from "./secret-input.js";
|
||||
export { ToolPolicySchema } from "../config/zod-schema.agent-runtime.js";
|
||||
export { MarkdownConfigSchema } from "../config/zod-schema.core.js";
|
||||
export { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
|
||||
@@ -103,7 +100,7 @@ export {
|
||||
evaluateGroupRouteAccessForPolicy,
|
||||
resolveSenderScopedGroupPolicy,
|
||||
} from "./group-access.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { createChannelPairingController, createScopedPairingAccess } from "./channel-pairing.js";
|
||||
export { formatResolvedUnresolvedNote } from "./resolution-notes.js";
|
||||
export { runPluginCommandWithTimeout } from "./run-command.js";
|
||||
export { dispatchReplyFromConfigWithSettledDispatcher } from "./inbound-reply-dispatch.js";
|
||||
@@ -114,16 +111,12 @@ export {
|
||||
collectStatusIssuesFromLastError,
|
||||
} from "./status-helpers.js";
|
||||
|
||||
export const matrixSetupWizard = createOptionalChannelSetupWizard({
|
||||
const matrixSetup = createOptionalChannelSetupSurface({
|
||||
channel: "matrix",
|
||||
label: "Matrix",
|
||||
npmSpec: "@openclaw/matrix",
|
||||
docsPath: "/channels/matrix",
|
||||
});
|
||||
|
||||
export const matrixSetupAdapter = createOptionalChannelSetupAdapter({
|
||||
channel: "matrix",
|
||||
label: "Matrix",
|
||||
npmSpec: "@openclaw/matrix",
|
||||
docsPath: "/channels/matrix",
|
||||
});
|
||||
export const matrixSetupWizard = matrixSetup.setupWizard;
|
||||
export const matrixSetupAdapter = matrixSetup.setupAdapter;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// Narrow plugin-sdk surface for the bundled msteams plugin.
|
||||
// Keep this list additive and scoped to symbols used under extensions/msteams.
|
||||
|
||||
import {
|
||||
createOptionalChannelSetupAdapter,
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
import { createOptionalChannelSetupSurface } from "./channel-setup.js";
|
||||
|
||||
export type { ChunkMode } from "../auto-reply/chunk.js";
|
||||
export type { HistoryEntry } from "../auto-reply/reply/history.js";
|
||||
@@ -55,8 +52,8 @@ export type {
|
||||
ChannelOutboundAdapter,
|
||||
} from "../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
|
||||
export { createTypingCallbacks } from "../channels/typing.js";
|
||||
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
|
||||
export { createTypingCallbacks } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export { isDangerousNameMatchingEnabled } from "../config/dangerous-name-matching.js";
|
||||
export { resolveToolsBySender } from "../config/group-policy.js";
|
||||
@@ -109,7 +106,7 @@ export { withFileLock } from "./file-lock.js";
|
||||
export { dispatchReplyFromConfigWithSettledDispatcher } from "./inbound-reply-dispatch.js";
|
||||
export { readJsonFileWithFallback, writeJsonFileAtomically } from "./json-store.js";
|
||||
export { loadOutboundMediaFromUrl } from "./outbound-media.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { createChannelPairingController, createScopedPairingAccess } from "./channel-pairing.js";
|
||||
export { resolveInboundSessionEnvelopeContext } from "../channels/session-envelope.js";
|
||||
export {
|
||||
buildHostnameAllowlistPolicyFromSuffixAllowlist,
|
||||
@@ -124,16 +121,12 @@ export {
|
||||
} from "./status-helpers.js";
|
||||
export { normalizeStringEntries } from "../shared/string-normalization.js";
|
||||
|
||||
export const msteamsSetupWizard = createOptionalChannelSetupWizard({
|
||||
const msteamsSetup = createOptionalChannelSetupSurface({
|
||||
channel: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
npmSpec: "@openclaw/msteams",
|
||||
docsPath: "/channels/msteams",
|
||||
});
|
||||
|
||||
export const msteamsSetupAdapter = createOptionalChannelSetupAdapter({
|
||||
channel: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
npmSpec: "@openclaw/msteams",
|
||||
docsPath: "/channels/msteams",
|
||||
});
|
||||
export const msteamsSetupWizard = msteamsSetup.setupWizard;
|
||||
export const msteamsSetupAdapter = msteamsSetup.setupAdapter;
|
||||
|
||||
@@ -32,7 +32,7 @@ export {
|
||||
export { createAccountListHelpers } from "../channels/plugins/account-helpers.js";
|
||||
export type { ChannelGroupContext, ChannelSetupInput } from "../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
|
||||
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export { mapAllowFromEntries } from "./channel-config-helpers.js";
|
||||
export { evaluateMatchedGroupAccessForPolicy } from "./group-access.js";
|
||||
@@ -49,13 +49,13 @@ export type {
|
||||
GroupPolicy,
|
||||
GroupToolPolicyConfig,
|
||||
} from "../config/types.js";
|
||||
export type { SecretInput } from "../config/types.secrets.js";
|
||||
export type { SecretInput } from "./secret-input.js";
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "../config/types.secrets.js";
|
||||
export { buildSecretInputSchema } from "./secret-input-schema.js";
|
||||
} from "./secret-input.js";
|
||||
export { ToolPolicySchema } from "../config/zod-schema.agent-runtime.js";
|
||||
export {
|
||||
BlockStreamingCoalesceSchema,
|
||||
@@ -88,8 +88,7 @@ export {
|
||||
listConfiguredAccountIds,
|
||||
resolveAccountWithDefaultFallback,
|
||||
} from "./account-resolution.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { createChannelPairingController, createScopedPairingAccess } from "./channel-pairing.js";
|
||||
export { createPersistentDedupe } from "./persistent-dedupe.js";
|
||||
export type { OutboundReplyPayload } from "./reply-payload.js";
|
||||
export {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// Narrow plugin-sdk surface for the bundled nostr plugin.
|
||||
// Keep this list additive and scoped to symbols used under extensions/nostr.
|
||||
|
||||
import {
|
||||
createOptionalChannelSetupAdapter,
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
import { createOptionalChannelSetupSurface } from "./channel-setup.js";
|
||||
|
||||
export { buildChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
export type { ChannelSetupAdapter } from "../channels/plugins/types.adapters.js";
|
||||
@@ -25,16 +22,12 @@ export {
|
||||
export { createFixedWindowRateLimiter } from "./webhook-memory-guards.js";
|
||||
export { mapAllowFromEntries } from "./channel-config-helpers.js";
|
||||
|
||||
export const nostrSetupAdapter = createOptionalChannelSetupAdapter({
|
||||
const nostrSetup = createOptionalChannelSetupSurface({
|
||||
channel: "nostr",
|
||||
label: "Nostr",
|
||||
npmSpec: "@openclaw/nostr",
|
||||
docsPath: "/channels/nostr",
|
||||
});
|
||||
|
||||
export const nostrSetupWizard = createOptionalChannelSetupWizard({
|
||||
channel: "nostr",
|
||||
label: "Nostr",
|
||||
npmSpec: "@openclaw/nostr",
|
||||
docsPath: "/channels/nostr",
|
||||
});
|
||||
export const nostrSetupAdapter = nostrSetup.setupAdapter;
|
||||
export const nostrSetupWizard = nostrSetup.setupWizard;
|
||||
|
||||
@@ -9,8 +9,13 @@ export type {
|
||||
export {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyOnboardAuthAgentModelsAndProviders,
|
||||
applyProviderConfigWithDefaultModelPreset,
|
||||
applyProviderConfigWithDefaultModelsPreset,
|
||||
applyProviderConfigWithDefaultModel,
|
||||
applyProviderConfigWithDefaultModels,
|
||||
applyProviderConfigWithModelCatalogPreset,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
withAgentModelAliases,
|
||||
} from "../plugins/provider-onboarding-config.js";
|
||||
export type { AgentModelAliasEntry } from "../plugins/provider-onboarding-config.js";
|
||||
export { ensureModelAllowlistEntry } from "../plugins/provider-model-allowlist.js";
|
||||
|
||||
24
src/plugin-sdk/secret-input.test.ts
Normal file
24
src/plugin-sdk/secret-input.test.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildOptionalSecretInputSchema,
|
||||
buildSecretInputArraySchema,
|
||||
normalizeSecretInputString,
|
||||
} from "./secret-input.js";
|
||||
|
||||
describe("plugin-sdk secret input helpers", () => {
|
||||
it("accepts undefined for optional secret input", () => {
|
||||
expect(buildOptionalSecretInputSchema().safeParse(undefined).success).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts arrays of secret inputs", () => {
|
||||
const result = buildSecretInputArraySchema().safeParse([
|
||||
"sk-plain",
|
||||
{ source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
]);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("normalizes plaintext secret strings", () => {
|
||||
expect(normalizeSecretInputString(" sk-test ")).toBe("sk-test");
|
||||
});
|
||||
});
|
||||
23
src/plugin-sdk/secret-input.ts
Normal file
23
src/plugin-sdk/secret-input.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "../config/types.secrets.js";
|
||||
import { buildSecretInputSchema } from "./secret-input-schema.js";
|
||||
|
||||
export type { SecretInput } from "../config/types.secrets.js";
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
};
|
||||
|
||||
export function buildOptionalSecretInputSchema() {
|
||||
return buildSecretInputSchema().optional();
|
||||
}
|
||||
|
||||
export function buildSecretInputArraySchema() {
|
||||
return z.array(buildSecretInputSchema());
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import * as bluebubblesSdk from "openclaw/plugin-sdk/bluebubbles";
|
||||
import * as channelPairingSdk from "openclaw/plugin-sdk/channel-pairing";
|
||||
import * as channelReplyPipelineSdk from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import * as channelRuntimeSdk from "openclaw/plugin-sdk/channel-runtime";
|
||||
import * as channelSendResultSdk from "openclaw/plugin-sdk/channel-send-result";
|
||||
import * as channelSetupSdk from "openclaw/plugin-sdk/channel-setup";
|
||||
import * as coreSdk from "openclaw/plugin-sdk/core";
|
||||
import type {
|
||||
ChannelMessageActionContext as CoreChannelMessageActionContext,
|
||||
@@ -18,11 +21,13 @@ import * as replyPayloadSdk from "openclaw/plugin-sdk/reply-payload";
|
||||
import * as routingSdk from "openclaw/plugin-sdk/routing";
|
||||
import * as runtimeSdk from "openclaw/plugin-sdk/runtime";
|
||||
import * as sandboxSdk from "openclaw/plugin-sdk/sandbox";
|
||||
import * as secretInputSdk from "openclaw/plugin-sdk/secret-input";
|
||||
import * as selfHostedProviderSetupSdk from "openclaw/plugin-sdk/self-hosted-provider-setup";
|
||||
import * as setupSdk from "openclaw/plugin-sdk/setup";
|
||||
import * as slackSdk from "openclaw/plugin-sdk/slack";
|
||||
import * as telegramSdk from "openclaw/plugin-sdk/telegram";
|
||||
import * as testingSdk from "openclaw/plugin-sdk/testing";
|
||||
import * as webhookIngressSdk from "openclaw/plugin-sdk/webhook-ingress";
|
||||
import * as whatsappSdk from "openclaw/plugin-sdk/whatsapp";
|
||||
import * as whatsappActionRuntimeSdk from "openclaw/plugin-sdk/whatsapp-action-runtime";
|
||||
import * as whatsappLoginQrSdk from "openclaw/plugin-sdk/whatsapp-login-qr";
|
||||
@@ -111,6 +116,21 @@ describe("plugin-sdk subpath exports", () => {
|
||||
expect(typeof channelRuntimeSdk.sendPayloadMediaSequenceOrFallback).toBe("function");
|
||||
});
|
||||
|
||||
it("exports channel setup helpers from the dedicated subpath", () => {
|
||||
expect(typeof channelSetupSdk.createOptionalChannelSetupSurface).toBe("function");
|
||||
expect(typeof channelSetupSdk.createTopLevelChannelDmPolicy).toBe("function");
|
||||
});
|
||||
|
||||
it("exports channel pairing helpers from the dedicated subpath", () => {
|
||||
expect(typeof channelPairingSdk.createChannelPairingController).toBe("function");
|
||||
expect(typeof channelPairingSdk.createScopedPairingAccess).toBe("function");
|
||||
});
|
||||
|
||||
it("exports channel reply pipeline helpers from the dedicated subpath", () => {
|
||||
expect(typeof channelReplyPipelineSdk.createChannelReplyPipeline).toBe("function");
|
||||
expect(typeof channelReplyPipelineSdk.createTypingCallbacks).toBe("function");
|
||||
});
|
||||
|
||||
it("exports channel send-result helpers from the dedicated subpath", () => {
|
||||
expect(typeof channelSendResultSdk.attachChannelToResult).toBe("function");
|
||||
expect(typeof channelSendResultSdk.buildChannelSendResult).toBe("function");
|
||||
@@ -162,6 +182,18 @@ describe("plugin-sdk subpath exports", () => {
|
||||
expect(typeof sandboxSdk.runPluginCommandWithTimeout).toBe("function");
|
||||
});
|
||||
|
||||
it("exports secret input helpers from the dedicated subpath", () => {
|
||||
expect(typeof secretInputSdk.buildSecretInputSchema).toBe("function");
|
||||
expect(typeof secretInputSdk.buildOptionalSecretInputSchema).toBe("function");
|
||||
expect(typeof secretInputSdk.normalizeSecretInputString).toBe("function");
|
||||
});
|
||||
|
||||
it("exports webhook ingress helpers from the dedicated subpath", () => {
|
||||
expect(typeof webhookIngressSdk.resolveWebhookPath).toBe("function");
|
||||
expect(typeof webhookIngressSdk.readJsonWebhookBodyOrReject).toBe("function");
|
||||
expect(typeof webhookIngressSdk.withResolvedWebhookRequestPipeline).toBe("function");
|
||||
});
|
||||
|
||||
it("exports shared core types used by bundled channels", () => {
|
||||
expectTypeOf<CoreOpenClawPluginApi>().toMatchTypeOf<OpenClawPluginApi>();
|
||||
expectTypeOf<CorePluginRuntime>().toMatchTypeOf<PluginRuntime>();
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// Narrow plugin-sdk surface for the bundled tlon plugin.
|
||||
// Keep this list additive and scoped to symbols used under extensions/tlon.
|
||||
|
||||
import {
|
||||
createOptionalChannelSetupAdapter,
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
import { createOptionalChannelSetupSurface } from "./channel-setup.js";
|
||||
|
||||
export type { ReplyPayload } from "../auto-reply/types.js";
|
||||
export { buildChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
@@ -18,7 +15,7 @@ export type {
|
||||
ChannelSetupInput,
|
||||
} from "../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
|
||||
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export { createDedupeCache } from "../infra/dedupe.js";
|
||||
export { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
|
||||
@@ -33,16 +30,12 @@ export { formatDocsLink } from "../terminal/links.js";
|
||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { createLoggerBackedRuntime } from "./runtime.js";
|
||||
|
||||
export const tlonSetupAdapter = createOptionalChannelSetupAdapter({
|
||||
const tlonSetup = createOptionalChannelSetupSurface({
|
||||
channel: "tlon",
|
||||
label: "Tlon",
|
||||
npmSpec: "@openclaw/tlon",
|
||||
docsPath: "/channels/tlon",
|
||||
});
|
||||
|
||||
export const tlonSetupWizard = createOptionalChannelSetupWizard({
|
||||
channel: "tlon",
|
||||
label: "Tlon",
|
||||
npmSpec: "@openclaw/tlon",
|
||||
docsPath: "/channels/tlon",
|
||||
});
|
||||
export const tlonSetupAdapter = tlonSetup.setupAdapter;
|
||||
export const tlonSetupWizard = tlonSetup.setupWizard;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// Narrow plugin-sdk surface for the bundled twitch plugin.
|
||||
// Keep this list additive and scoped to symbols used under extensions/twitch.
|
||||
|
||||
import {
|
||||
createOptionalChannelSetupAdapter,
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
import { createOptionalChannelSetupSurface } from "./channel-setup.js";
|
||||
|
||||
export type { ReplyPayload } from "../auto-reply/types.js";
|
||||
export { buildChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
@@ -27,7 +24,7 @@ export type {
|
||||
ChannelStatusIssue,
|
||||
} from "../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
|
||||
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export { MarkdownConfigSchema } from "../config/zod-schema.core.js";
|
||||
export type { OutboundDeliveryResult } from "../infra/outbound/deliver.js";
|
||||
@@ -39,14 +36,11 @@ export type { RuntimeEnv } from "../runtime.js";
|
||||
export { formatDocsLink } from "../terminal/links.js";
|
||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
|
||||
export const twitchSetupAdapter = createOptionalChannelSetupAdapter({
|
||||
const twitchSetup = createOptionalChannelSetupSurface({
|
||||
channel: "twitch",
|
||||
label: "Twitch",
|
||||
npmSpec: "@openclaw/twitch",
|
||||
});
|
||||
|
||||
export const twitchSetupWizard = createOptionalChannelSetupWizard({
|
||||
channel: "twitch",
|
||||
label: "Twitch",
|
||||
npmSpec: "@openclaw/twitch",
|
||||
});
|
||||
export const twitchSetupAdapter = twitchSetup.setupAdapter;
|
||||
export const twitchSetupWizard = twitchSetup.setupWizard;
|
||||
|
||||
38
src/plugin-sdk/webhook-ingress.ts
Normal file
38
src/plugin-sdk/webhook-ingress.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
export {
|
||||
createBoundedCounter,
|
||||
createFixedWindowRateLimiter,
|
||||
createWebhookAnomalyTracker,
|
||||
WEBHOOK_ANOMALY_COUNTER_DEFAULTS,
|
||||
WEBHOOK_ANOMALY_STATUS_CODES,
|
||||
WEBHOOK_RATE_LIMIT_DEFAULTS,
|
||||
type BoundedCounter,
|
||||
type FixedWindowRateLimiter,
|
||||
type WebhookAnomalyTracker,
|
||||
} from "./webhook-memory-guards.js";
|
||||
export {
|
||||
applyBasicWebhookRequestGuards,
|
||||
beginWebhookRequestPipelineOrReject,
|
||||
createWebhookInFlightLimiter,
|
||||
isJsonContentType,
|
||||
readJsonWebhookBodyOrReject,
|
||||
readWebhookBodyOrReject,
|
||||
WEBHOOK_BODY_READ_DEFAULTS,
|
||||
WEBHOOK_IN_FLIGHT_DEFAULTS,
|
||||
type WebhookBodyReadProfile,
|
||||
type WebhookInFlightLimiter,
|
||||
} from "./webhook-request-guards.js";
|
||||
export {
|
||||
registerWebhookTarget,
|
||||
registerWebhookTargetWithPluginRoute,
|
||||
resolveSingleWebhookTarget,
|
||||
resolveSingleWebhookTargetAsync,
|
||||
resolveWebhookTargetWithAuthOrReject,
|
||||
resolveWebhookTargetWithAuthOrRejectSync,
|
||||
resolveWebhookTargets,
|
||||
withResolvedWebhookRequestPipeline,
|
||||
type RegisterWebhookPluginRouteOptions,
|
||||
type RegisterWebhookTargetOptions,
|
||||
type RegisteredWebhookTarget,
|
||||
type WebhookTargetMatchResult,
|
||||
} from "./webhook-targets.js";
|
||||
export { normalizeWebhookPath, resolveWebhookPath } from "./webhook-path.js";
|
||||
@@ -34,9 +34,9 @@ export type {
|
||||
ChannelStatusIssue,
|
||||
} from "../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
|
||||
export { logTypingFailure } from "../channels/logging.js";
|
||||
export { createTypingCallbacks } from "../channels/typing.js";
|
||||
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
|
||||
export { createTypingCallbacks } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export {
|
||||
resolveDefaultGroupPolicy,
|
||||
@@ -44,13 +44,13 @@ export {
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "../config/runtime-group-policy.js";
|
||||
export type { GroupPolicy, MarkdownTableMode } from "../config/types.js";
|
||||
export type { SecretInput } from "../config/types.secrets.js";
|
||||
export type { SecretInput } from "./secret-input.js";
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "../config/types.secrets.js";
|
||||
export { buildSecretInputSchema } from "./secret-input-schema.js";
|
||||
} from "./secret-input.js";
|
||||
export { MarkdownConfigSchema } from "../config/zod-schema.core.js";
|
||||
export { waitForAbortSignal } from "../infra/abort-signal.js";
|
||||
export { createDedupeCache } from "../infra/dedupe.js";
|
||||
@@ -72,8 +72,7 @@ export { resolveChannelAccountConfigBasePath } from "./config-paths.js";
|
||||
export { evaluateSenderGroupAccess } from "./group-access.js";
|
||||
export type { SenderGroupAccessDecision } from "./group-access.js";
|
||||
export { resolveInboundRouteEnvelopeBuilderWithRuntime } from "./inbound-envelope.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { createChannelPairingController, createScopedPairingAccess } from "./channel-pairing.js";
|
||||
export { buildChannelSendResult } from "./channel-send-result.js";
|
||||
export type { OutboundReplyPayload } from "./reply-payload.js";
|
||||
export {
|
||||
@@ -90,25 +89,21 @@ export {
|
||||
export { chunkTextForOutbound } from "./text-chunking.js";
|
||||
export { extractToolSend } from "./tool-send.js";
|
||||
export {
|
||||
applyBasicWebhookRequestGuards,
|
||||
createFixedWindowRateLimiter,
|
||||
createWebhookAnomalyTracker,
|
||||
readJsonWebhookBodyOrReject,
|
||||
registerWebhookTarget,
|
||||
registerWebhookTargetWithPluginRoute,
|
||||
resolveSingleWebhookTarget,
|
||||
resolveWebhookPath,
|
||||
resolveWebhookTargetWithAuthOrRejectSync,
|
||||
resolveWebhookTargets,
|
||||
WEBHOOK_ANOMALY_COUNTER_DEFAULTS,
|
||||
WEBHOOK_RATE_LIMIT_DEFAULTS,
|
||||
} from "./webhook-memory-guards.js";
|
||||
export { resolveWebhookPath } from "./webhook-path.js";
|
||||
export {
|
||||
applyBasicWebhookRequestGuards,
|
||||
readJsonWebhookBodyOrReject,
|
||||
} from "./webhook-request-guards.js";
|
||||
withResolvedWebhookRequestPipeline,
|
||||
} from "./webhook-ingress.js";
|
||||
export type {
|
||||
RegisterWebhookPluginRouteOptions,
|
||||
RegisterWebhookTargetOptions,
|
||||
} from "./webhook-targets.js";
|
||||
export {
|
||||
registerWebhookTarget,
|
||||
registerWebhookTargetWithPluginRoute,
|
||||
resolveWebhookTargetWithAuthOrRejectSync,
|
||||
resolveSingleWebhookTarget,
|
||||
resolveWebhookTargets,
|
||||
withResolvedWebhookRequestPipeline,
|
||||
} from "./webhook-targets.js";
|
||||
} from "./webhook-ingress.js";
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// Narrow plugin-sdk surface for the bundled zalouser plugin.
|
||||
// Keep this list additive and scoped to symbols used under extensions/zalouser.
|
||||
|
||||
import {
|
||||
createOptionalChannelSetupAdapter,
|
||||
createOptionalChannelSetupWizard,
|
||||
} from "./optional-channel-setup.js";
|
||||
import { createOptionalChannelSetupSurface } from "./channel-setup.js";
|
||||
|
||||
export type { ReplyPayload } from "../auto-reply/types.js";
|
||||
export { mergeAllowlist, summarizeMapping } from "../channels/allowlists/resolve-utils.js";
|
||||
@@ -36,8 +33,8 @@ export type {
|
||||
ChannelStatusIssue,
|
||||
} from "../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
export { createReplyPrefixOptions } from "../channels/reply-prefix.js";
|
||||
export { createTypingCallbacks } from "../channels/typing.js";
|
||||
export { createChannelReplyPipeline, createReplyPrefixOptions } from "./channel-reply-pipeline.js";
|
||||
export { createTypingCallbacks } from "./channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
export { isDangerousNameMatchingEnabled } from "../config/dangerous-name-matching.js";
|
||||
export {
|
||||
@@ -63,8 +60,7 @@ export {
|
||||
resolveSenderScopedGroupPolicy,
|
||||
} from "./group-access.js";
|
||||
export { loadOutboundMediaFromUrl } from "./outbound-media.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { createChannelPairingController, createScopedPairingAccess } from "./channel-pairing.js";
|
||||
export { buildChannelSendResult } from "./channel-send-result.js";
|
||||
export type { OutboundReplyPayload } from "./reply-payload.js";
|
||||
export {
|
||||
@@ -79,16 +75,12 @@ export { formatResolvedUnresolvedNote } from "./resolution-notes.js";
|
||||
export { buildBaseAccountStatusSnapshot } from "./status-helpers.js";
|
||||
export { chunkTextForOutbound } from "./text-chunking.js";
|
||||
|
||||
export const zalouserSetupAdapter = createOptionalChannelSetupAdapter({
|
||||
const zalouserSetup = createOptionalChannelSetupSurface({
|
||||
channel: "zalouser",
|
||||
label: "Zalo Personal",
|
||||
npmSpec: "@openclaw/zalouser",
|
||||
docsPath: "/channels/zalouser",
|
||||
});
|
||||
|
||||
export const zalouserSetupWizard = createOptionalChannelSetupWizard({
|
||||
channel: "zalouser",
|
||||
label: "Zalo Personal",
|
||||
npmSpec: "@openclaw/zalouser",
|
||||
docsPath: "/channels/zalouser",
|
||||
});
|
||||
export const zalouserSetupAdapter = zalouserSetup.setupAdapter;
|
||||
export const zalouserSetupWizard = zalouserSetup.setupWizard;
|
||||
|
||||
@@ -18,6 +18,38 @@ function extractAgentDefaultModelFallbacks(model: unknown): string[] | undefined
|
||||
return Array.isArray(fallbacks) ? fallbacks.map((v) => String(v)) : undefined;
|
||||
}
|
||||
|
||||
export type AgentModelAliasEntry =
|
||||
| string
|
||||
| {
|
||||
modelRef: string;
|
||||
alias?: string;
|
||||
};
|
||||
|
||||
function normalizeAgentModelAliasEntry(entry: AgentModelAliasEntry): {
|
||||
modelRef: string;
|
||||
alias?: string;
|
||||
} {
|
||||
if (typeof entry === "string") {
|
||||
return { modelRef: entry };
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
export function withAgentModelAliases(
|
||||
existing: Record<string, AgentModelEntryConfig> | undefined,
|
||||
aliases: readonly AgentModelAliasEntry[],
|
||||
): Record<string, AgentModelEntryConfig> {
|
||||
const next = { ...existing };
|
||||
for (const entry of aliases) {
|
||||
const normalized = normalizeAgentModelAliasEntry(entry);
|
||||
next[normalized.modelRef] = {
|
||||
...next[normalized.modelRef],
|
||||
...(normalized.alias ? { alias: next[normalized.modelRef]?.alias ?? normalized.alias } : {}),
|
||||
};
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
export function applyOnboardAuthAgentModelsAndProviders(
|
||||
cfg: OpenClawConfig,
|
||||
params: {
|
||||
@@ -117,6 +149,56 @@ export function applyProviderConfigWithDefaultModel(
|
||||
});
|
||||
}
|
||||
|
||||
export function applyProviderConfigWithDefaultModelPreset(
|
||||
cfg: OpenClawConfig,
|
||||
params: {
|
||||
providerId: string;
|
||||
api: ModelApi;
|
||||
baseUrl: string;
|
||||
defaultModel: ModelDefinitionConfig;
|
||||
defaultModelId?: string;
|
||||
aliases?: readonly AgentModelAliasEntry[];
|
||||
primaryModelRef?: string;
|
||||
},
|
||||
): OpenClawConfig {
|
||||
const next = applyProviderConfigWithDefaultModel(cfg, {
|
||||
agentModels: withAgentModelAliases(cfg.agents?.defaults?.models, params.aliases ?? []),
|
||||
providerId: params.providerId,
|
||||
api: params.api,
|
||||
baseUrl: params.baseUrl,
|
||||
defaultModel: params.defaultModel,
|
||||
defaultModelId: params.defaultModelId,
|
||||
});
|
||||
return params.primaryModelRef
|
||||
? applyAgentDefaultModelPrimary(next, params.primaryModelRef)
|
||||
: next;
|
||||
}
|
||||
|
||||
export function applyProviderConfigWithDefaultModelsPreset(
|
||||
cfg: OpenClawConfig,
|
||||
params: {
|
||||
providerId: string;
|
||||
api: ModelApi;
|
||||
baseUrl: string;
|
||||
defaultModels: ModelDefinitionConfig[];
|
||||
defaultModelId?: string;
|
||||
aliases?: readonly AgentModelAliasEntry[];
|
||||
primaryModelRef?: string;
|
||||
},
|
||||
): OpenClawConfig {
|
||||
const next = applyProviderConfigWithDefaultModels(cfg, {
|
||||
agentModels: withAgentModelAliases(cfg.agents?.defaults?.models, params.aliases ?? []),
|
||||
providerId: params.providerId,
|
||||
api: params.api,
|
||||
baseUrl: params.baseUrl,
|
||||
defaultModels: params.defaultModels,
|
||||
defaultModelId: params.defaultModelId,
|
||||
});
|
||||
return params.primaryModelRef
|
||||
? applyAgentDefaultModelPrimary(next, params.primaryModelRef)
|
||||
: next;
|
||||
}
|
||||
|
||||
export function applyProviderConfigWithModelCatalog(
|
||||
cfg: OpenClawConfig,
|
||||
params: {
|
||||
@@ -149,6 +231,29 @@ export function applyProviderConfigWithModelCatalog(
|
||||
});
|
||||
}
|
||||
|
||||
export function applyProviderConfigWithModelCatalogPreset(
|
||||
cfg: OpenClawConfig,
|
||||
params: {
|
||||
providerId: string;
|
||||
api: ModelApi;
|
||||
baseUrl: string;
|
||||
catalogModels: ModelDefinitionConfig[];
|
||||
aliases?: readonly AgentModelAliasEntry[];
|
||||
primaryModelRef?: string;
|
||||
},
|
||||
): OpenClawConfig {
|
||||
const next = applyProviderConfigWithModelCatalog(cfg, {
|
||||
agentModels: withAgentModelAliases(cfg.agents?.defaults?.models, params.aliases ?? []),
|
||||
providerId: params.providerId,
|
||||
api: params.api,
|
||||
baseUrl: params.baseUrl,
|
||||
catalogModels: params.catalogModels,
|
||||
});
|
||||
return params.primaryModelRef
|
||||
? applyAgentDefaultModelPrimary(next, params.primaryModelRef)
|
||||
: next;
|
||||
}
|
||||
|
||||
type ProviderModelMergeState = {
|
||||
providers: Record<string, ModelProviderConfig>;
|
||||
existingProvider?: ModelProviderConfig;
|
||||
|
||||
Reference in New Issue
Block a user