diff --git a/CHANGELOG.md b/CHANGELOG.md index 87586e9f767..9a9a1716e86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -767,6 +767,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Gateway/macOS restart: remove self-issued `launchctl kickstart -k` from launchd supervised restart path to prevent race with launchd's async bootout state machine that permanently unloads the LaunchAgent. With `ThrottleInterval=1` (current default), `exit(0)` + `KeepAlive=true` restarts the service within ~1s without the race condition. (#39760) Landed from contributor PR #39763 by @daymade. Thanks @daymade. +- Plugin SDK/bundled subpath contracts: add regression coverage for newly routed bundled-plugin SDK exports so BlueBubbles, Mattermost, Nextcloud Talk, and Twitch subpath symbols stay pinned during future plugin-sdk cleanup. (#39638) - Exec/system.run env sanitization: block dangerous override-only env pivots such as `GIT_SSH_COMMAND`, editor/pager hooks, and `GIT_CONFIG_` / `NPM_CONFIG_` override prefixes so allowlisted tools cannot smuggle helper command execution through subprocess environment overrides. Thanks @tdjackey and @SnailSploit for reporting. - Network/fetch guard redirect auth stripping: switch cross-origin redirect handling in `fetchWithSsrFGuard` from a narrow sensitive-header denylist to a safe-header allowlist so custom auth headers like `X-Api-Key` and `Private-Token` no longer leak on origin changes. Thanks @Rickidevs for reporting. - Security/Sandbox media reads: eliminate sandbox media TOCTOU symlink-retarget escapes by enforcing root-scoped boundary-safe reads at attachment/image load time and consolidating shared safe-read helpers across sandbox media callsites. This ships in the next npm release. Thanks @tdjackey for reporting. diff --git a/extensions/bluebubbles/src/monitor-normalize.ts b/extensions/bluebubbles/src/monitor-normalize.ts index 22705e6b12c..173ea9c24a6 100644 --- a/extensions/bluebubbles/src/monitor-normalize.ts +++ b/extensions/bluebubbles/src/monitor-normalize.ts @@ -1,4 +1,4 @@ -import { parseFiniteNumber } from "../../../src/infra/parse-finite-number.js"; +import { parseFiniteNumber } from "openclaw/plugin-sdk/bluebubbles"; import { extractHandleFromChatGuid, normalizeBlueBubblesHandle } from "./targets.js"; import type { BlueBubblesAttachment } from "./types.js"; diff --git a/extensions/mattermost/src/mattermost/monitor.ts b/extensions/mattermost/src/mattermost/monitor.ts index d6f4bd9543c..93d4ce1cfcb 100644 --- a/extensions/mattermost/src/mattermost/monitor.ts +++ b/extensions/mattermost/src/mattermost/monitor.ts @@ -19,6 +19,7 @@ import { DEFAULT_GROUP_HISTORY_LIMIT, recordPendingHistoryEntryIfEnabled, isDangerousNameMatchingEnabled, + parseStrictPositiveInteger, registerPluginHttpRoute, resolveControlCommandGate, readStoreAllowFromForDmPolicy, @@ -30,7 +31,6 @@ import { listSkillCommandsForAgents, type HistoryEntry, } from "openclaw/plugin-sdk/mattermost"; -import { parseStrictPositiveInteger } from "../../../../src/infra/parse-finite-number.js"; import { getMattermostRuntime } from "../runtime.js"; import { resolveMattermostAccount } from "./accounts.js"; import { diff --git a/extensions/nextcloud-talk/src/channel.ts b/extensions/nextcloud-talk/src/channel.ts index 711ac34cb52..6fdf36e9f8c 100644 --- a/extensions/nextcloud-talk/src/channel.ts +++ b/extensions/nextcloud-talk/src/channel.ts @@ -15,11 +15,11 @@ import { deleteAccountFromConfigSection, normalizeAccountId, setAccountEnabledInConfigSection, + waitForAbortSignal, type ChannelPlugin, type OpenClawConfig, type ChannelSetupInput, } from "openclaw/plugin-sdk/nextcloud-talk"; -import { waitForAbortSignal } from "../../../src/infra/abort-signal.js"; import { listNextcloudTalkAccountIds, resolveDefaultNextcloudTalkAccountId, diff --git a/extensions/twitch/src/token.ts b/extensions/twitch/src/token.ts index 4c3eae6a28a..76f0c2007aa 100644 --- a/extensions/twitch/src/token.ts +++ b/extensions/twitch/src/token.ts @@ -9,8 +9,11 @@ * 2. Environment variable: OPENCLAW_TWITCH_ACCESS_TOKEN (default account only) */ -import type { OpenClawConfig } from "../../../src/config/config.js"; -import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../src/routing/session-key.js"; +import { + DEFAULT_ACCOUNT_ID, + normalizeAccountId, + type OpenClawConfig, +} from "openclaw/plugin-sdk/twitch"; export type TwitchTokenSource = "env" | "config" | "none"; diff --git a/extensions/twitch/src/types.ts b/extensions/twitch/src/types.ts index 25aaf3bd80e..8bb677bdc3e 100644 --- a/extensions/twitch/src/types.ts +++ b/extensions/twitch/src/types.ts @@ -5,26 +5,24 @@ * from OpenClaw core. */ -import type { - ChannelGatewayContext, - ChannelOutboundAdapter, - ChannelOutboundContext, - ChannelResolveKind, - ChannelResolveResult, - ChannelStatusAdapter, -} from "../../../src/channels/plugins/types.adapters.js"; import type { ChannelAccountSnapshot, ChannelCapabilities, + ChannelGatewayContext, ChannelLogSink, ChannelMessageActionAdapter, ChannelMessageActionContext, ChannelMeta, -} from "../../../src/channels/plugins/types.core.js"; -import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js"; -import type { OpenClawConfig } from "../../../src/config/config.js"; -import type { OutboundDeliveryResult } from "../../../src/infra/outbound/deliver.js"; -import type { RuntimeEnv } from "../../../src/runtime.js"; + ChannelOutboundAdapter, + ChannelOutboundContext, + ChannelPlugin, + ChannelResolveKind, + ChannelResolveResult, + ChannelStatusAdapter, + OpenClawConfig, + OutboundDeliveryResult, + RuntimeEnv, +} from "openclaw/plugin-sdk/twitch"; // ============================================================================ // Twitch-Specific Types diff --git a/src/plugin-sdk/bluebubbles.ts b/src/plugin-sdk/bluebubbles.ts index 736640b5a29..19f74c30c28 100644 --- a/src/plugin-sdk/bluebubbles.ts +++ b/src/plugin-sdk/bluebubbles.ts @@ -75,6 +75,7 @@ export { resolveServicePrefixedTarget, } from "../imessage/target-parsing-helpers.js"; export { stripMarkdown } from "../line/markdown-to-line.js"; +export { parseFiniteNumber } from "../infra/parse-finite-number.js"; export { emptyPluginConfigSchema } from "../plugins/config-schema.js"; export type { PluginRuntime } from "../plugins/runtime/types.js"; export type { OpenClawPluginApi } from "../plugins/types.js"; diff --git a/src/plugin-sdk/mattermost.ts b/src/plugin-sdk/mattermost.ts index c680b6606c8..7b574dd3eeb 100644 --- a/src/plugin-sdk/mattermost.ts +++ b/src/plugin-sdk/mattermost.ts @@ -77,6 +77,7 @@ export { requireOpenAllowFrom, } from "../config/zod-schema.core.js"; export { createDedupeCache } from "../infra/dedupe.js"; +export { parseStrictPositiveInteger } from "../infra/parse-finite-number.js"; export { rawDataToString } from "../infra/ws.js"; export { isLoopbackHost, isTrustedProxyAddress, resolveClientIp } from "../gateway/net.js"; export { registerPluginHttpRoute } from "../plugins/http-registry.js"; diff --git a/src/plugin-sdk/nextcloud-talk.ts b/src/plugin-sdk/nextcloud-talk.ts index ff22f937cf7..3f534a0ab5d 100644 --- a/src/plugin-sdk/nextcloud-talk.ts +++ b/src/plugin-sdk/nextcloud-talk.ts @@ -73,6 +73,7 @@ export { readRequestBodyWithLimit, requestBodyErrorToText, } from "../infra/http-body.js"; +export { waitForAbortSignal } from "../infra/abort-signal.js"; export { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js"; export { emptyPluginConfigSchema } from "../plugins/config-schema.js"; export type { PluginRuntime } from "../plugins/runtime/types.js"; diff --git a/src/plugin-sdk/subpaths.test.ts b/src/plugin-sdk/subpaths.test.ts index 7d9e76ec6bc..aff93389421 100644 --- a/src/plugin-sdk/subpaths.test.ts +++ b/src/plugin-sdk/subpaths.test.ts @@ -105,4 +105,19 @@ describe("plugin-sdk subpath exports", () => { expect(mod, `subpath ${id} should resolve`).toBeTruthy(); } }); + + it("keeps the newly added bundled plugin-sdk contracts available", async () => { + const bluebubbles = await import("openclaw/plugin-sdk/bluebubbles"); + expect(typeof bluebubbles.parseFiniteNumber).toBe("function"); + + const mattermost = await import("openclaw/plugin-sdk/mattermost"); + expect(typeof mattermost.parseStrictPositiveInteger).toBe("function"); + + const nextcloudTalk = await import("openclaw/plugin-sdk/nextcloud-talk"); + expect(typeof nextcloudTalk.waitForAbortSignal).toBe("function"); + + const twitch = await import("openclaw/plugin-sdk/twitch"); + expect(typeof twitch.DEFAULT_ACCOUNT_ID).toBe("string"); + expect(typeof twitch.normalizeAccountId).toBe("function"); + }); }); diff --git a/src/plugin-sdk/twitch.ts b/src/plugin-sdk/twitch.ts index bd315b02c9a..7ea8a9f5f4b 100644 --- a/src/plugin-sdk/twitch.ts +++ b/src/plugin-sdk/twitch.ts @@ -3,17 +3,38 @@ export type { ReplyPayload } from "../auto-reply/types.js"; export { buildChannelConfigSchema } from "../channels/plugins/config-schema.js"; +export type { + ChannelGatewayContext, + ChannelOutboundAdapter, + ChannelOutboundContext, + ChannelResolveKind, + ChannelResolveResult, + ChannelStatusAdapter, +} from "../channels/plugins/types.adapters.js"; +export type { + BaseProbeResult, + ChannelAccountSnapshot, + ChannelCapabilities, + ChannelLogSink, + ChannelMessageActionAdapter, + ChannelMessageActionContext, + ChannelMeta, + ChannelStatusIssue, +} from "../channels/plugins/types.js"; +export type { ChannelPlugin } from "../channels/plugins/types.plugin.js"; export type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy, } from "../channels/plugins/onboarding-types.js"; export { promptChannelAccessConfig } from "../channels/plugins/onboarding/channel-access.js"; -export type { BaseProbeResult, ChannelStatusIssue } from "../channels/plugins/types.js"; export { createReplyPrefixOptions } from "../channels/reply-prefix.js"; export type { OpenClawConfig } from "../config/config.js"; export { MarkdownConfigSchema } from "../config/zod-schema.core.js"; +export type { OutboundDeliveryResult } from "../infra/outbound/deliver.js"; +export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "./account-id.js"; export { emptyPluginConfigSchema } from "../plugins/config-schema.js"; export type { PluginRuntime } from "../plugins/runtime/types.js"; export type { OpenClawPluginApi } from "../plugins/types.js"; +export type { RuntimeEnv } from "../runtime.js"; export { formatDocsLink } from "../terminal/links.js"; export type { WizardPrompter } from "../wizard/prompts.js";