From 8109195ad89e5095f2dfeb0b8fd72d4465da1050 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 29 Mar 2026 14:59:32 +0100 Subject: [PATCH] fix(plugin-sdk): avoid recursive bundled facade loads --- extensions/telegram/src/account-inspect.ts | 2 +- extensions/telegram/src/accounts.ts | 5 ++- extensions/telegram/src/api-fetch.ts | 2 +- extensions/telegram/src/channel.setup.ts | 2 +- extensions/telegram/src/probe.ts | 2 +- extensions/telegram/src/setup-core.ts | 2 +- extensions/telegram/src/token.ts | 2 +- extensions/xai/src/code-execution-shared.ts | 2 +- extensions/xai/src/web-search-shared.ts | 2 +- extensions/xai/src/x-search-shared.ts | 2 +- src/plugin-sdk/agent-runtime.ts | 4 +-- .../channel-import-guardrails.test.ts | 32 +++++++++++++++++++ src/plugin-sdk/provider-models.ts | 10 +++--- src/plugin-sdk/provider-setup.ts | 11 ++++--- src/plugin-sdk/self-hosted-provider-setup.ts | 4 +-- src/plugin-sdk/subpaths.test.ts | 14 ++++++++ src/plugin-sdk/xai-model-id.ts | 2 +- 17 files changed, 76 insertions(+), 24 deletions(-) diff --git a/extensions/telegram/src/account-inspect.ts b/extensions/telegram/src/account-inspect.ts index 2c529a09662..665a15bb4dc 100644 --- a/extensions/telegram/src/account-inspect.ts +++ b/extensions/telegram/src/account-inspect.ts @@ -8,7 +8,7 @@ import { hasConfiguredSecretInput, normalizeSecretInputString, } from "openclaw/plugin-sdk/secret-input"; -import type { TelegramAccountConfig } from "../runtime-api.js"; +import type { TelegramAccountConfig } from "openclaw/plugin-sdk/telegram-core"; import { mergeTelegramAccountConfig, resolveDefaultTelegramAccountId, diff --git a/extensions/telegram/src/accounts.ts b/extensions/telegram/src/accounts.ts index 6c7a9a25f97..c9312bb02fa 100644 --- a/extensions/telegram/src/accounts.ts +++ b/extensions/telegram/src/accounts.ts @@ -16,7 +16,10 @@ import { } from "openclaw/plugin-sdk/routing"; import { formatSetExplicitDefaultInstruction } from "openclaw/plugin-sdk/routing"; import { createSubsystemLogger, isTruthyEnvValue } from "openclaw/plugin-sdk/runtime-env"; -import type { TelegramAccountConfig, TelegramActionConfig } from "../runtime-api.js"; +import type { + TelegramAccountConfig, + TelegramActionConfig, +} from "openclaw/plugin-sdk/telegram-core"; import { resolveTelegramToken } from "./token.js"; let log: ReturnType | null = null; diff --git a/extensions/telegram/src/api-fetch.ts b/extensions/telegram/src/api-fetch.ts index 21dd8fd64e5..4768e080b46 100644 --- a/extensions/telegram/src/api-fetch.ts +++ b/extensions/telegram/src/api-fetch.ts @@ -1,4 +1,4 @@ -import type { TelegramNetworkConfig } from "../runtime-api.js"; +import type { TelegramNetworkConfig } from "openclaw/plugin-sdk/telegram-core"; import { resolveTelegramApiBase, resolveTelegramFetch } from "./fetch.js"; import { makeProxyFetch } from "./proxy.js"; diff --git a/extensions/telegram/src/channel.setup.ts b/extensions/telegram/src/channel.setup.ts index 10067a34378..3b22cf4aa09 100644 --- a/extensions/telegram/src/channel.setup.ts +++ b/extensions/telegram/src/channel.setup.ts @@ -1,4 +1,4 @@ -import { type ChannelPlugin } from "../runtime-api.js"; +import type { ChannelPlugin } from "openclaw/plugin-sdk/telegram-core"; import { type ResolvedTelegramAccount } from "./accounts.js"; import type { TelegramProbe } from "./probe.js"; import { telegramSetupAdapter } from "./setup-core.js"; diff --git a/extensions/telegram/src/probe.ts b/extensions/telegram/src/probe.ts index bec56269927..b18886d6c50 100644 --- a/extensions/telegram/src/probe.ts +++ b/extensions/telegram/src/probe.ts @@ -1,6 +1,6 @@ import type { BaseProbeResult } from "openclaw/plugin-sdk/channel-contract"; +import type { TelegramNetworkConfig } from "openclaw/plugin-sdk/telegram-core"; import { fetchWithTimeout } from "openclaw/plugin-sdk/text-runtime"; -import type { TelegramNetworkConfig } from "../runtime-api.js"; import { resolveTelegramApiBase, resolveTelegramFetch } from "./fetch.js"; import { makeProxyFetch } from "./proxy.js"; diff --git a/extensions/telegram/src/setup-core.ts b/extensions/telegram/src/setup-core.ts index 6e24563a9c9..5ea70065755 100644 --- a/extensions/telegram/src/setup-core.ts +++ b/extensions/telegram/src/setup-core.ts @@ -9,7 +9,7 @@ import { } from "openclaw/plugin-sdk/setup"; import type { ChannelSetupAdapter, ChannelSetupDmPolicy } from "openclaw/plugin-sdk/setup"; import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools"; -import type { TelegramNetworkConfig } from "../runtime-api.js"; +import type { TelegramNetworkConfig } from "openclaw/plugin-sdk/telegram-core"; import { resolveDefaultTelegramAccountId, resolveTelegramAccount } from "./accounts.js"; import { lookupTelegramChatId } from "./api-fetch.js"; diff --git a/extensions/telegram/src/token.ts b/extensions/telegram/src/token.ts index 3bb9c40e4f8..e1503e0d36b 100644 --- a/extensions/telegram/src/token.ts +++ b/extensions/telegram/src/token.ts @@ -4,7 +4,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; import { tryReadSecretFileSync } from "openclaw/plugin-sdk/core"; import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/routing"; import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input"; -import type { TelegramAccountConfig } from "../runtime-api.js"; +import type { TelegramAccountConfig } from "openclaw/plugin-sdk/telegram-core"; export type TelegramTokenSource = "env" | "tokenFile" | "config" | "none"; diff --git a/extensions/xai/src/code-execution-shared.ts b/extensions/xai/src/code-execution-shared.ts index dfd658b1944..1c6f2d69926 100644 --- a/extensions/xai/src/code-execution-shared.ts +++ b/extensions/xai/src/code-execution-shared.ts @@ -1,5 +1,5 @@ -import { normalizeXaiModelId } from "openclaw/plugin-sdk/provider-models"; import { postTrustedWebToolsJson } from "openclaw/plugin-sdk/provider-web-search"; +import { normalizeXaiModelId } from "../model-id.js"; import { extractXaiWebSearchContent, type XaiWebSearchResponse } from "./web-search-shared.js"; export const XAI_CODE_EXECUTION_ENDPOINT = "https://api.x.ai/v1/responses"; diff --git a/extensions/xai/src/web-search-shared.ts b/extensions/xai/src/web-search-shared.ts index 06e6ceee5de..b93b0fcaaaf 100644 --- a/extensions/xai/src/web-search-shared.ts +++ b/extensions/xai/src/web-search-shared.ts @@ -1,5 +1,5 @@ import { postTrustedWebToolsJson, wrapWebContent } from "openclaw/plugin-sdk/provider-web-search"; -import { normalizeXaiModelId } from "../api.js"; +import { normalizeXaiModelId } from "../model-id.js"; export const XAI_WEB_SEARCH_ENDPOINT = "https://api.x.ai/v1/responses"; export const XAI_DEFAULT_WEB_SEARCH_MODEL = "grok-4-1-fast"; diff --git a/extensions/xai/src/x-search-shared.ts b/extensions/xai/src/x-search-shared.ts index b7d52643799..7f997a7360e 100644 --- a/extensions/xai/src/x-search-shared.ts +++ b/extensions/xai/src/x-search-shared.ts @@ -1,5 +1,5 @@ -import { normalizeXaiModelId } from "openclaw/plugin-sdk/provider-models"; import { postTrustedWebToolsJson, wrapWebContent } from "openclaw/plugin-sdk/provider-web-search"; +import { normalizeXaiModelId } from "../model-id.js"; import { extractXaiWebSearchContent, type XaiWebSearchResponse } from "./web-search-shared.js"; export const XAI_X_SEARCH_ENDPOINT = "https://api.x.ai/v1/responses"; diff --git a/src/plugin-sdk/agent-runtime.ts b/src/plugin-sdk/agent-runtime.ts index e17c48b8c32..1cfc9d09069 100644 --- a/src/plugin-sdk/agent-runtime.ts +++ b/src/plugin-sdk/agent-runtime.ts @@ -16,12 +16,12 @@ export * from "../agents/pi-embedded-utils.js"; export * from "../agents/provider-id.js"; export * from "../agents/sandbox-paths.js"; export * from "../agents/schema/typebox.js"; -export * from "./sglang.js"; +export * from "../../extensions/sglang/api.js"; export * from "../agents/tools/common.js"; export * from "../agents/tools/web-guarded-fetch.js"; export * from "../agents/tools/web-shared.js"; export * from "../agents/tools/web-fetch-utils.js"; -export * from "./vllm.js"; +export * from "../../extensions/vllm/api.js"; // Intentional public runtime surface: channel plugins use ingress agent helpers directly. export * from "../agents/agent-command.js"; export * from "../tts/tts.js"; diff --git a/src/plugin-sdk/channel-import-guardrails.test.ts b/src/plugin-sdk/channel-import-guardrails.test.ts index cd43d509c53..9bd25ee78b7 100644 --- a/src/plugin-sdk/channel-import-guardrails.test.ts +++ b/src/plugin-sdk/channel-import-guardrails.test.ts @@ -75,6 +75,34 @@ const SAME_CHANNEL_SDK_GUARDS: GuardedSource[] = [ path: bundledPluginFile("telegram", "src/action-runtime.ts"), forbiddenPatterns: [/["']\.\.\/runtime-api\.js["']/], }, + { + path: bundledPluginFile("telegram", "src/accounts.ts"), + forbiddenPatterns: [/["']\.\.\/runtime-api\.js["']/], + }, + { + path: bundledPluginFile("telegram", "src/account-inspect.ts"), + forbiddenPatterns: [/["']\.\.\/runtime-api\.js["']/], + }, + { + path: bundledPluginFile("telegram", "src/api-fetch.ts"), + forbiddenPatterns: [/["']\.\.\/runtime-api\.js["']/], + }, + { + path: bundledPluginFile("telegram", "src/channel.setup.ts"), + forbiddenPatterns: [/["']\.\.\/runtime-api\.js["']/], + }, + { + path: bundledPluginFile("telegram", "src/probe.ts"), + forbiddenPatterns: [/["']\.\.\/runtime-api\.js["']/], + }, + { + path: bundledPluginFile("telegram", "src/setup-core.ts"), + forbiddenPatterns: [/["']\.\.\/runtime-api\.js["']/], + }, + { + path: bundledPluginFile("telegram", "src/token.ts"), + forbiddenPatterns: [/["']\.\.\/runtime-api\.js["']/], + }, { path: bundledPluginFile("imessage", "src/shared.ts"), forbiddenPatterns: [/["']openclaw\/plugin-sdk\/imessage["']/, /plugin-sdk-internal\/imessage/], @@ -160,9 +188,11 @@ const LOCAL_EXTENSION_API_BARREL_GUARDS = [ "msteams", "nextcloud-talk", "nostr", + "ollama", "open-prose", "phone-control", "copilot-proxy", + "sglang", "zai", "signal", "synology-chat", @@ -171,8 +201,10 @@ const LOCAL_EXTENSION_API_BARREL_GUARDS = [ "thread-ownership", "tlon", "voice-call", + "vllm", "whatsapp", "twitch", + "xai", "zalo", "zalouser", ] as const; diff --git a/src/plugin-sdk/provider-models.ts b/src/plugin-sdk/provider-models.ts index 40a8dc15788..fafa037b024 100644 --- a/src/plugin-sdk/provider-models.ts +++ b/src/plugin-sdk/provider-models.ts @@ -28,7 +28,7 @@ export { HTML_ENTITY_TOOL_CALL_ARGUMENTS_ENCODING, normalizeXaiModelId, XAI_TOOL_SCHEMA_PROFILE, -} from "./xai.js"; +} from "../../extensions/xai/api.js"; export { isMiniMaxModernModelId, MINIMAX_DEFAULT_MODEL_ID, @@ -87,7 +87,7 @@ export { type OllamaModelWithContext, type OllamaTagModel, type OllamaTagsResponse, -} from "./ollama-surface.js"; +} from "../../extensions/ollama/api.js"; export { buildSyntheticModelDefinition, SYNTHETIC_BASE_URL, @@ -130,9 +130,9 @@ export { OLLAMA_DEFAULT_CONTEXT_WINDOW, OLLAMA_DEFAULT_COST, OLLAMA_DEFAULT_MAX_TOKENS, -} from "./ollama-surface.js"; -export { VLLM_DEFAULT_BASE_URL } from "./vllm.js"; -export { SGLANG_DEFAULT_BASE_URL } from "./sglang.js"; +} from "../../extensions/ollama/api.js"; +export { VLLM_DEFAULT_BASE_URL } from "../../extensions/vllm/api.js"; +export { SGLANG_DEFAULT_BASE_URL } from "../../extensions/sglang/api.js"; export { buildKilocodeModelDefinition, KILOCODE_BASE_URL, diff --git a/src/plugin-sdk/provider-setup.ts b/src/plugin-sdk/provider-setup.ts index e574f7f8423..ff087e76af4 100644 --- a/src/plugin-sdk/provider-setup.ts +++ b/src/plugin-sdk/provider-setup.ts @@ -18,13 +18,16 @@ export { SELF_HOSTED_DEFAULT_COST, SELF_HOSTED_DEFAULT_MAX_TOKENS, } from "../plugins/provider-self-hosted-setup.js"; -export { OLLAMA_DEFAULT_BASE_URL, OLLAMA_DEFAULT_MODEL } from "./ollama-surface.js"; +// Keep shared setup barrels off the generated plugin facades. Source-first +// facade loading can otherwise recurse back into the same plugin while its +// public surface is still evaluating. +export { OLLAMA_DEFAULT_BASE_URL, OLLAMA_DEFAULT_MODEL } from "../../extensions/ollama/api.js"; export { buildOllamaProvider, configureOllamaNonInteractive, ensureOllamaModelPulled, promptAndConfigureOllama, -} from "./ollama-surface.js"; +} from "../../extensions/ollama/api.js"; export { VLLM_DEFAULT_BASE_URL, VLLM_DEFAULT_CONTEXT_WINDOW, @@ -32,5 +35,5 @@ export { VLLM_DEFAULT_MAX_TOKENS, promptAndConfigureVllm, } from "../plugins/provider-vllm-setup.js"; -export { buildVllmProvider } from "./vllm.js"; -export { buildSglangProvider } from "./sglang.js"; +export { buildVllmProvider } from "../../extensions/vllm/api.js"; +export { buildSglangProvider } from "../../extensions/sglang/api.js"; diff --git a/src/plugin-sdk/self-hosted-provider-setup.ts b/src/plugin-sdk/self-hosted-provider-setup.ts index cae33e74580..5cf4cec1cdc 100644 --- a/src/plugin-sdk/self-hosted-provider-setup.ts +++ b/src/plugin-sdk/self-hosted-provider-setup.ts @@ -19,5 +19,5 @@ export { SELF_HOSTED_DEFAULT_MAX_TOKENS, } from "../plugins/provider-self-hosted-setup.js"; -export { buildVllmProvider } from "./vllm.js"; -export { buildSglangProvider } from "./sglang.js"; +export { buildVllmProvider } from "../../extensions/vllm/api.js"; +export { buildSglangProvider } from "../../extensions/sglang/api.js"; diff --git a/src/plugin-sdk/subpaths.test.ts b/src/plugin-sdk/subpaths.test.ts index c5af8c1f920..74c01756d3c 100644 --- a/src/plugin-sdk/subpaths.test.ts +++ b/src/plugin-sdk/subpaths.test.ts @@ -117,6 +117,10 @@ function expectSourceContains(subpath: string, snippet: string) { expect(readPluginSdkSource(subpath)).toContain(snippet); } +function expectSourceOmitsSnippet(subpath: string, snippet: string) { + expect(readPluginSdkSource(subpath)).not.toContain(snippet); +} + describe("plugin-sdk subpath exports", () => { it("keeps the curated public list free of internal implementation subpaths", () => { for (const deniedSubpath of [ @@ -593,6 +597,9 @@ describe("plugin-sdk subpath exports", () => { "buildVllmProvider", "discoverOpenAICompatibleSelfHostedProvider", ]); + expectSourceOmitsSnippet("provider-setup", "./ollama-surface.js"); + expectSourceOmitsSnippet("provider-setup", "./vllm.js"); + expectSourceOmitsSnippet("provider-setup", "./sglang.js"); expectSourceMentions("provider-auth", [ "buildOauthProviderAuthResult", "generatePkceVerifierChallenge", @@ -609,6 +616,8 @@ describe("plugin-sdk subpath exports", () => { "resolveZaiBaseUrl", ], }); + expectSourceOmitsSnippet("provider-models", "./xai.js"); + expectSourceOmitsSnippet("provider-models", "./ollama-surface.js"); expectSourceContract("provider-model-shared", { mentions: ["DEFAULT_CONTEXT_TOKENS", "normalizeModelCompat", "cloneFirstTemplateModel"], omits: ["applyOpenAIConfig", "buildKilocodeModelDefinition", "discoverHuggingfaceModels"], @@ -637,6 +646,11 @@ describe("plugin-sdk subpath exports", () => { "buildSglangProvider", "configureOpenAICompatibleSelfHostedProviderNonInteractive", ]); + expectSourceOmitsSnippet("self-hosted-provider-setup", "./vllm.js"); + expectSourceOmitsSnippet("self-hosted-provider-setup", "./sglang.js"); + expectSourceOmitsSnippet("agent-runtime", "./sglang.js"); + expectSourceOmitsSnippet("agent-runtime", "./vllm.js"); + expectSourceOmitsSnippet("xai-model-id", "./xai.js"); expectSourceMentions("sandbox", ["registerSandboxBackend", "runPluginCommandWithTimeout"]); expectSourceMentions("secret-input", [ diff --git a/src/plugin-sdk/xai-model-id.ts b/src/plugin-sdk/xai-model-id.ts index 16493ac436c..463481c66f3 100644 --- a/src/plugin-sdk/xai-model-id.ts +++ b/src/plugin-sdk/xai-model-id.ts @@ -1 +1 @@ -export { normalizeXaiModelId } from "./xai.js"; +export { normalizeXaiModelId } from "../../extensions/xai/model-id.js";