diff --git a/Dockerfile b/Dockerfile index 5f137b9aeeb..8a5c22f08d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,15 +5,16 @@ # # Multi-stage build produces a minimal runtime image without build tools, # source code, or Bun. Works with Docker, Buildx, and Podman. -# The ext-deps stage extracts only the package.json files we need from -# extensions/, so the main build layer is not invalidated by unrelated -# extension source changes. +# The ext-deps stage extracts only the package.json files we need from the +# bundled plugin workspace tree, so the main build layer is not invalidated by +# unrelated plugin source changes. # # Two runtime variants: # Default (bookworm): docker build . # Slim (bookworm-slim): docker build --build-arg OPENCLAW_VARIANT=slim . ARG OPENCLAW_EXTENSIONS="" ARG OPENCLAW_VARIANT=default +ARG OPENCLAW_BUNDLED_PLUGIN_DIR=extensions ARG OPENCLAW_DOCKER_APT_UPGRADE=1 ARG OPENCLAW_NODE_BOOKWORM_IMAGE="node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b" ARG OPENCLAW_NODE_BOOKWORM_DIGEST="sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b" @@ -27,18 +28,20 @@ ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST="sha256:e8e2e91b1378f83c5b2dd15f0247f3411 FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS ext-deps ARG OPENCLAW_EXTENSIONS -COPY extensions /tmp/extensions +ARG OPENCLAW_BUNDLED_PLUGIN_DIR +COPY ${OPENCLAW_BUNDLED_PLUGIN_DIR} /tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR} # Copy package.json for opted-in extensions so pnpm resolves their deps. RUN mkdir -p /out && \ for ext in $OPENCLAW_EXTENSIONS; do \ - if [ -f "/tmp/extensions/$ext/package.json" ]; then \ + if [ -f "/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}/$ext/package.json" ]; then \ mkdir -p "/out/$ext" && \ - cp "/tmp/extensions/$ext/package.json" "/out/$ext/package.json"; \ + cp "/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}/$ext/package.json" "/out/$ext/package.json"; \ fi; \ done # ── Stage 2: Build ────────────────────────────────────────────── FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS build +ARG OPENCLAW_BUNDLED_PLUGIN_DIR # Install Bun (required for build scripts). Retry the whole bootstrap flow to # tolerate transient 5xx failures from bun.sh/GitHub during CI image builds. @@ -62,7 +65,7 @@ COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ COPY ui/package.json ./ui/package.json COPY patches ./patches -COPY --from=ext-deps /out/ ./extensions/ +COPY --from=ext-deps /out/ ./${OPENCLAW_BUNDLED_PLUGIN_DIR}/ # Reduce OOM risk on low-memory hosts during dependency installation. # Docker builds on small VMs may otherwise fail with "Killed" (exit 137). @@ -73,7 +76,7 @@ COPY . . # Normalize extension paths now so runtime COPY preserves safe modes # without adding a second full extensions layer. -RUN for dir in /app/extensions /app/.agent /app/.agents; do \ +RUN for dir in /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} /app/.agent /app/.agents; do \ if [ -d "$dir" ]; then \ find "$dir" -type d -exec chmod 755 {} +; \ find "$dir" -type f -exec chmod 644 {} +; \ @@ -114,6 +117,7 @@ LABEL org.opencontainers.image.base.name="docker.io/library/node:24-bookworm-sli # ── Stage 3: Runtime ──────────────────────────────────────────── FROM base-${OPENCLAW_VARIANT} ARG OPENCLAW_VARIANT +ARG OPENCLAW_BUNDLED_PLUGIN_DIR ARG OPENCLAW_DOCKER_APT_UPGRADE # OCI base-image metadata for downstream image consumers. @@ -148,13 +152,13 @@ COPY --from=runtime-assets --chown=node:node /app/dist ./dist COPY --from=runtime-assets --chown=node:node /app/node_modules ./node_modules COPY --from=runtime-assets --chown=node:node /app/package.json . COPY --from=runtime-assets --chown=node:node /app/openclaw.mjs . -COPY --from=runtime-assets --chown=node:node /app/extensions ./extensions +COPY --from=runtime-assets --chown=node:node /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} ./${OPENCLAW_BUNDLED_PLUGIN_DIR} COPY --from=runtime-assets --chown=node:node /app/skills ./skills COPY --from=runtime-assets --chown=node:node /app/docs ./docs # In npm-installed Docker images, prefer the copied source extension tree for # bundled discovery so package metadata that points at source entries stays valid. -ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/extensions +ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/${OPENCLAW_BUNDLED_PLUGIN_DIR} # Keep pnpm available in the runtime image for container-local workflows. # Use a shared Corepack home so the non-root `node` user does not need a diff --git a/extensions/acpx/skills/acp-router/SKILL.md b/extensions/acpx/skills/acp-router/SKILL.md index 3b59f63346f..fa2e1e76801 100644 --- a/extensions/acpx/skills/acp-router/SKILL.md +++ b/extensions/acpx/skills/acp-router/SKILL.md @@ -102,7 +102,7 @@ Required behavior when ACP backend is unavailable: 1. Do not immediately ask the user to pick an alternate path. 2. First attempt automatic local repair: - - ensure plugin-local pinned acpx is installed in `extensions/acpx` + - ensure plugin-local pinned acpx is installed in the bundled ACPX plugin package - verify `${ACPX_CMD} --version` 3. After reinstall/repair, restart the gateway and explicitly offer to run that restart for the user. 4. Retry ACP thread spawn once after repair. @@ -120,20 +120,21 @@ Do not default to subagent runtime for these requests. For this repo, direct `acpx` calls must follow the same pinned policy as the `@openclaw/acpx` extension package. 1. Prefer plugin-local binary, not global PATH: - - `./extensions/acpx/node_modules/.bin/acpx` + - `${ACPX_PLUGIN_ROOT}/node_modules/.bin/acpx` 2. Resolve pinned version from extension dependency: - - `node -e "console.log(require('./extensions/acpx/package.json').dependencies.acpx)"` + - `node -e "console.log(require(process.env.ACPX_PLUGIN_ROOT + '/package.json').dependencies.acpx)"` 3. If binary is missing or version mismatched, install plugin-local pinned version: - - `cd extensions/acpx && npm install --omit=dev --no-save acpx@` + - `cd "$ACPX_PLUGIN_ROOT" && npm install --omit=dev --no-save acpx@` 4. Verify before use: - - `./extensions/acpx/node_modules/.bin/acpx --version` + - `${ACPX_PLUGIN_ROOT}/node_modules/.bin/acpx --version` 5. If install/repair changed ACPX artifacts, restart the gateway and offer to run the restart. 6. Do not run `npm install -g acpx` unless the user explicitly asks for global install. Set and reuse: ```bash -ACPX_CMD="./extensions/acpx/node_modules/.bin/acpx" +ACPX_PLUGIN_ROOT="" +ACPX_CMD="$ACPX_PLUGIN_ROOT/node_modules/.bin/acpx" ``` ## Direct acpx path ("telephone game") @@ -227,7 +228,7 @@ If your local Cursor install still exposes ACP as `agent acp`, set that as the ` ### Failure handling - `acpx: command not found`: - - for thread-spawn ACP requests, install plugin-local pinned acpx in `extensions/acpx` immediately + - for thread-spawn ACP requests, install plugin-local pinned acpx in the bundled ACPX plugin package immediately - restart gateway after install and offer to run the restart automatically - then retry once - do not ask for install permission first unless policy explicitly requires it diff --git a/extensions/acpx/src/config.test.ts b/extensions/acpx/src/config.test.ts index 19dfc9d0bb4..e3c79711084 100644 --- a/extensions/acpx/src/config.test.ts +++ b/extensions/acpx/src/config.test.ts @@ -3,6 +3,10 @@ import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { describe, expect, it } from "vitest"; +import { + bundledDistPluginRootAt, + bundledPluginRootAt, +} from "../../../test/helpers/bundled-plugin-paths.js"; import { ACPX_BUNDLED_BIN, ACPX_PINNED_VERSION, @@ -39,10 +43,10 @@ describe("acpx plugin config parsing", () => { } }); - it("prefers the workspace plugin root for dist/extensions/acpx bundles", () => { + it("prefers the workspace plugin root for dist plugin bundles", () => { const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "acpx-root-workspace-")); - const workspacePluginRoot = path.join(repoRoot, "extensions", "acpx"); - const bundledPluginRoot = path.join(repoRoot, "dist", "extensions", "acpx"); + const workspacePluginRoot = bundledPluginRootAt(repoRoot, "acpx"); + const bundledPluginRoot = bundledDistPluginRootAt(repoRoot, "acpx"); try { fs.mkdirSync(workspacePluginRoot, { recursive: true }); fs.mkdirSync(bundledPluginRoot, { recursive: true }); diff --git a/extensions/acpx/src/runtime-internals/mcp-proxy.test.ts b/extensions/acpx/src/runtime-internals/mcp-proxy.test.ts index cb0357a3581..3e64dd5a36c 100644 --- a/extensions/acpx/src/runtime-internals/mcp-proxy.test.ts +++ b/extensions/acpx/src/runtime-internals/mcp-proxy.test.ts @@ -3,9 +3,10 @@ import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; +import { bundledPluginFile } from "../../../../test/helpers/bundled-plugin-paths.js"; const tempDirs: string[] = []; -const proxyPath = path.resolve("extensions/acpx/src/runtime-internals/mcp-proxy.mjs"); +const proxyPath = path.resolve(bundledPluginFile("acpx", "src/runtime-internals/mcp-proxy.mjs")); async function makeTempScript(name: string, content: string): Promise { const dir = await mkdtemp(path.join(os.tmpdir(), "openclaw-acpx-mcp-proxy-")); diff --git a/extensions/amazon-bedrock/index.test.ts b/extensions/amazon-bedrock/index.test.ts index 049ebc45810..59fdc059334 100644 --- a/extensions/amazon-bedrock/index.test.ts +++ b/extensions/amazon-bedrock/index.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { registerSingleProviderPlugin } from "../../test/helpers/extensions/plugin-registration.js"; +import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js"; import amazonBedrockPlugin from "./index.js"; describe("amazon-bedrock provider plugin", () => { diff --git a/extensions/amazon-bedrock/provider.contract.test.ts b/extensions/amazon-bedrock/provider.contract.test.ts index 8535108143b..1aebdcd62ac 100644 --- a/extensions/amazon-bedrock/provider.contract.test.ts +++ b/extensions/amazon-bedrock/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("amazon-bedrock"); diff --git a/extensions/anthropic/plugin-registration.contract.test.ts b/extensions/anthropic/plugin-registration.contract.test.ts index 5d526538798..df21b2205ac 100644 --- a/extensions/anthropic/plugin-registration.contract.test.ts +++ b/extensions/anthropic/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "anthropic", diff --git a/extensions/anthropic/provider-runtime.contract.test.ts b/extensions/anthropic/provider-runtime.contract.test.ts index 9c67fbb6561..31975c6c25d 100644 --- a/extensions/anthropic/provider-runtime.contract.test.ts +++ b/extensions/anthropic/provider-runtime.contract.test.ts @@ -1,3 +1,3 @@ -import { describeAnthropicProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js"; +import { describeAnthropicProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js"; describeAnthropicProviderRuntimeContract(); diff --git a/extensions/anthropic/provider.contract.test.ts b/extensions/anthropic/provider.contract.test.ts index da253adb244..b5f87aef589 100644 --- a/extensions/anthropic/provider.contract.test.ts +++ b/extensions/anthropic/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("anthropic"); diff --git a/extensions/bluebubbles/README.md b/extensions/bluebubbles/README.md index dbd16b95cfe..927fd9c748e 100644 --- a/extensions/bluebubbles/README.md +++ b/extensions/bluebubbles/README.md @@ -1,6 +1,6 @@ # BlueBubbles extension (developer reference) -This directory contains the **BlueBubbles external channel plugin** for OpenClaw. +This package contains the **BlueBubbles external channel plugin** for OpenClaw. If you’re looking for **how to use BlueBubbles as an agent/tool user**, see: @@ -8,22 +8,22 @@ If you’re looking for **how to use BlueBubbles as an agent/tool user**, see: ## Layout -- Extension package: `extensions/bluebubbles/` (entry: `index.ts`). -- Channel implementation: `extensions/bluebubbles/src/channel.ts`. -- Webhook handling: `extensions/bluebubbles/src/monitor.ts` (register per-account route via `registerPluginHttpRoute`). -- REST helpers: `extensions/bluebubbles/src/send.ts` + `extensions/bluebubbles/src/probe.ts`. -- Runtime bridge: `extensions/bluebubbles/src/runtime.ts` (set via `api.runtime`). +- Package entry: `index.ts`. +- Channel implementation: `src/channel.ts`. +- Webhook handling: `src/monitor.ts` (register per-account route via `registerPluginHttpRoute`). +- REST helpers: `src/send.ts` + `src/probe.ts`. +- Runtime bridge: `src/runtime.ts` (set via `api.runtime`). - Catalog entry for setup selection: `src/channels/plugins/catalog.ts`. ## Internal helpers (use these, not raw API calls) -- `probeBlueBubbles` in `extensions/bluebubbles/src/probe.ts` for health checks. -- `sendMessageBlueBubbles` in `extensions/bluebubbles/src/send.ts` for text delivery. -- `resolveChatGuidForTarget` in `extensions/bluebubbles/src/send.ts` for chat lookup. -- `sendBlueBubblesReaction` in `extensions/bluebubbles/src/reactions.ts` for tapbacks. -- `sendBlueBubblesTyping` + `markBlueBubblesChatRead` in `extensions/bluebubbles/src/chat.ts`. -- `downloadBlueBubblesAttachment` in `extensions/bluebubbles/src/attachments.ts` for inbound media. -- `buildBlueBubblesApiUrl` + `blueBubblesFetchWithTimeout` in `extensions/bluebubbles/src/types.ts` for shared REST plumbing. +- `probeBlueBubbles` in `src/probe.ts` for health checks. +- `sendMessageBlueBubbles` in `src/send.ts` for text delivery. +- `resolveChatGuidForTarget` in `src/send.ts` for chat lookup. +- `sendBlueBubblesReaction` in `src/reactions.ts` for tapbacks. +- `sendBlueBubblesTyping` + `markBlueBubblesChatRead` in `src/chat.ts`. +- `downloadBlueBubblesAttachment` in `src/attachments.ts` for inbound media. +- `buildBlueBubblesApiUrl` + `blueBubblesFetchWithTimeout` in `src/types.ts` for shared REST plumbing. ## Webhooks diff --git a/extensions/bluebubbles/package-manifest.contract.test.ts b/extensions/bluebubbles/package-manifest.contract.test.ts index 12fc9bf7eac..8bb7f472622 100644 --- a/extensions/bluebubbles/package-manifest.contract.test.ts +++ b/extensions/bluebubbles/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "bluebubbles", diff --git a/extensions/bluebubbles/package.json b/extensions/bluebubbles/package.json index f5b9668cb87..677877a4dee 100644 --- a/extensions/bluebubbles/package.json +++ b/extensions/bluebubbles/package.json @@ -38,7 +38,6 @@ }, "install": { "npmSpec": "@openclaw/bluebubbles", - "localPath": "extensions/bluebubbles", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/bluebubbles/src/monitor.test.ts b/extensions/bluebubbles/src/monitor.test.ts index 64649f52acd..199a14315e2 100644 --- a/extensions/bluebubbles/src/monitor.test.ts +++ b/extensions/bluebubbles/src/monitor.test.ts @@ -5,7 +5,7 @@ import { EMPTY_DISPATCH_RESULT, resetBlueBubblesMonitorTestState, type DispatchReplyParams, -} from "../../../test/helpers/extensions/bluebubbles-monitor.js"; +} from "../../../test/helpers/plugins/bluebubbles-monitor.js"; import type { ResolvedBlueBubblesAccount } from "./accounts.js"; import { fetchBlueBubblesHistory } from "./history.js"; import { createBlueBubblesDebounceRegistry } from "./monitor-debounce.js"; diff --git a/extensions/bluebubbles/src/monitor.webhook-auth.test.ts b/extensions/bluebubbles/src/monitor.webhook-auth.test.ts index d4de405c10f..d6d97885591 100644 --- a/extensions/bluebubbles/src/monitor.webhook-auth.test.ts +++ b/extensions/bluebubbles/src/monitor.webhook-auth.test.ts @@ -4,7 +4,7 @@ import { EMPTY_DISPATCH_RESULT, resetBlueBubblesMonitorTestState, type DispatchReplyParams, -} from "../../../test/helpers/extensions/bluebubbles-monitor.js"; +} from "../../../test/helpers/plugins/bluebubbles-monitor.js"; import type { ResolvedBlueBubblesAccount } from "./accounts.js"; import { fetchBlueBubblesHistory } from "./history.js"; import { handleBlueBubblesWebhookRequest, resolveBlueBubblesMessageId } from "./monitor.js"; diff --git a/extensions/bluebubbles/src/setup-surface.test.ts b/extensions/bluebubbles/src/setup-surface.test.ts index 077b3a410fb..31029b211e5 100644 --- a/extensions/bluebubbles/src/setup-surface.test.ts +++ b/extensions/bluebubbles/src/setup-surface.test.ts @@ -6,7 +6,7 @@ import { createTestWizardPrompter, runSetupWizardConfigure, type WizardPrompter, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import { resolveBlueBubblesAccount } from "./accounts.js"; import { BlueBubblesConfigSchema } from "./config-schema.js"; import { diff --git a/extensions/brave/bundled-web-search.contract.test.ts b/extensions/brave/bundled-web-search.contract.test.ts index 710beee50b2..1d93e09276f 100644 --- a/extensions/brave/bundled-web-search.contract.test.ts +++ b/extensions/brave/bundled-web-search.contract.test.ts @@ -1,3 +1,3 @@ -import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js"; +import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js"; describeBundledWebSearchFastPathContract("brave"); diff --git a/extensions/brave/plugin-registration.contract.test.ts b/extensions/brave/plugin-registration.contract.test.ts index ae7408ad619..c5a11f53e32 100644 --- a/extensions/brave/plugin-registration.contract.test.ts +++ b/extensions/brave/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "brave", diff --git a/extensions/brave/web-search-provider.contract.test.ts b/extensions/brave/web-search-provider.contract.test.ts index 759a197d079..a48db3f4c35 100644 --- a/extensions/brave/web-search-provider.contract.test.ts +++ b/extensions/brave/web-search-provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js"; +import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js"; describeWebSearchProviderContracts("brave"); diff --git a/extensions/browser/index.test.ts b/extensions/browser/index.test.ts index 0e857236281..8b6a989c01a 100644 --- a/extensions/browser/index.test.ts +++ b/extensions/browser/index.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js"; +import { createTestPluginApi } from "../../test/helpers/plugins/plugin-api.js"; import type { OpenClawPluginApi } from "./runtime-api.js"; const runtimeApiMocks = vi.hoisted(() => ({ diff --git a/extensions/browser/test-support.ts b/extensions/browser/test-support.ts index 1d8076b1fb8..9d4c8dd65c6 100644 --- a/extensions/browser/test-support.ts +++ b/extensions/browser/test-support.ts @@ -5,7 +5,7 @@ export { type CliRuntimeCapture, } from "../../src/cli/test-runtime-capture.js"; export type { OpenClawConfig } from "openclaw/plugin-sdk/browser-support"; -export { expectGeneratedTokenPersistedToGatewayAuth } from "../../test/helpers/extensions/auth-token-assertions.ts"; -export { withEnv, withEnvAsync } from "../../test/helpers/extensions/env.ts"; -export { withFetchPreconnect, type FetchMock } from "../../test/helpers/extensions/fetch-mock.ts"; -export { createTempHomeEnv, type TempHomeEnv } from "../../test/helpers/extensions/temp-home.ts"; +export { expectGeneratedTokenPersistedToGatewayAuth } from "../../test/helpers/plugins/auth-token-assertions.ts"; +export { withEnv, withEnvAsync } from "../../test/helpers/plugins/env.ts"; +export { withFetchPreconnect, type FetchMock } from "../../test/helpers/plugins/fetch-mock.ts"; +export { createTempHomeEnv, type TempHomeEnv } from "../../test/helpers/plugins/temp-home.ts"; diff --git a/extensions/byteplus/provider.contract.test.ts b/extensions/byteplus/provider.contract.test.ts index 7a18ee2c159..5eb162dab52 100644 --- a/extensions/byteplus/provider.contract.test.ts +++ b/extensions/byteplus/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("byteplus"); diff --git a/extensions/chutes/provider.contract.test.ts b/extensions/chutes/provider.contract.test.ts index 6ecced04b87..ecdd538ab24 100644 --- a/extensions/chutes/provider.contract.test.ts +++ b/extensions/chutes/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("chutes"); diff --git a/extensions/cloudflare-ai-gateway/provider-discovery.contract.test.ts b/extensions/cloudflare-ai-gateway/provider-discovery.contract.test.ts index f6c4afe2ae1..b29e98d756b 100644 --- a/extensions/cloudflare-ai-gateway/provider-discovery.contract.test.ts +++ b/extensions/cloudflare-ai-gateway/provider-discovery.contract.test.ts @@ -1,3 +1,3 @@ -import { describeCloudflareAiGatewayProviderDiscoveryContract } from "../../test/helpers/extensions/provider-discovery-contract.js"; +import { describeCloudflareAiGatewayProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js"; describeCloudflareAiGatewayProviderDiscoveryContract(); diff --git a/extensions/cloudflare-ai-gateway/provider.contract.test.ts b/extensions/cloudflare-ai-gateway/provider.contract.test.ts index b0aa83bcf5c..d235be48fd3 100644 --- a/extensions/cloudflare-ai-gateway/provider.contract.test.ts +++ b/extensions/cloudflare-ai-gateway/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("cloudflare-ai-gateway"); diff --git a/extensions/copilot-proxy/provider.contract.test.ts b/extensions/copilot-proxy/provider.contract.test.ts index 2d94ce8e85d..a63e27ce9f9 100644 --- a/extensions/copilot-proxy/provider.contract.test.ts +++ b/extensions/copilot-proxy/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("copilot-proxy"); diff --git a/extensions/deepgram/plugin-registration.contract.test.ts b/extensions/deepgram/plugin-registration.contract.test.ts index 3c541d8a334..bef37c16e1a 100644 --- a/extensions/deepgram/plugin-registration.contract.test.ts +++ b/extensions/deepgram/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "deepgram", diff --git a/extensions/deepseek/index.test.ts b/extensions/deepseek/index.test.ts index ff2152ae4ec..92bc661ed0a 100644 --- a/extensions/deepseek/index.test.ts +++ b/extensions/deepseek/index.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { resolveProviderPluginChoice } from "../../src/plugins/provider-wizard.js"; -import { registerSingleProviderPlugin } from "../../test/helpers/extensions/plugin-registration.js"; +import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js"; import deepseekPlugin from "./index.js"; describe("deepseek provider plugin", () => { diff --git a/extensions/deepseek/provider.contract.test.ts b/extensions/deepseek/provider.contract.test.ts index 750deed15c7..e110d3fab75 100644 --- a/extensions/deepseek/provider.contract.test.ts +++ b/extensions/deepseek/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("deepseek"); diff --git a/extensions/device-pair/index.test.ts b/extensions/device-pair/index.test.ts index 5272fd49ddf..8ade752170f 100644 --- a/extensions/device-pair/index.test.ts +++ b/extensions/device-pair/index.test.ts @@ -6,7 +6,7 @@ import type { PluginCommandContext, } from "openclaw/plugin-sdk/core"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js"; +import { createTestPluginApi } from "../../test/helpers/plugins/plugin-api.js"; import type { OpenClawPluginApi } from "./api.js"; import type { PendingPairingRequest } from "./notify.ts"; diff --git a/extensions/diffs/src/browser.test.ts b/extensions/diffs/src/browser.test.ts index cec8f07161f..0c59a63ec86 100644 --- a/extensions/diffs/src/browser.test.ts +++ b/extensions/diffs/src/browser.test.ts @@ -2,8 +2,8 @@ import fs from "node:fs/promises"; import type { IncomingMessage } from "node:http"; import path from "node:path"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { createMockServerResponse } from "../../../test/helpers/extensions/mock-http-response.js"; -import { createTestPluginApi } from "../../../test/helpers/extensions/plugin-api.js"; +import { createMockServerResponse } from "../../../test/helpers/plugins/mock-http-response.js"; +import { createTestPluginApi } from "../../../test/helpers/plugins/plugin-api.js"; import type { OpenClawConfig } from "../api.js"; import type { OpenClawPluginApi, OpenClawPluginToolContext } from "../api.js"; import plugin from "../index.js"; diff --git a/extensions/diffs/src/store.test.ts b/extensions/diffs/src/store.test.ts index 7e07fcc6e4b..8a09a4585fb 100644 --- a/extensions/diffs/src/store.test.ts +++ b/extensions/diffs/src/store.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import type { IncomingMessage } from "node:http"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { createMockServerResponse } from "../../../test/helpers/extensions/mock-http-response.js"; +import { createMockServerResponse } from "../../../test/helpers/plugins/mock-http-response.js"; import { createDiffsHttpHandler } from "./http.js"; import { DiffArtifactStore } from "./store.js"; import { createDiffStoreHarness } from "./test-helpers.js"; diff --git a/extensions/diffs/src/tool.test.ts b/extensions/diffs/src/tool.test.ts index 949113b9be5..3da73b51443 100644 --- a/extensions/diffs/src/tool.test.ts +++ b/extensions/diffs/src/tool.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../../test/helpers/extensions/plugin-api.js"; +import { createTestPluginApi } from "../../../test/helpers/plugins/plugin-api.js"; import type { OpenClawPluginApi, OpenClawPluginToolContext } from "../api.js"; import type { DiffScreenshotter } from "./browser.js"; import { DEFAULT_DIFFS_TOOL_DEFAULTS } from "./config.js"; diff --git a/extensions/discord/package-manifest.contract.test.ts b/extensions/discord/package-manifest.contract.test.ts index be1d32becdb..7f7fdaa9ad2 100644 --- a/extensions/discord/package-manifest.contract.test.ts +++ b/extensions/discord/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "discord", diff --git a/extensions/discord/package.json b/extensions/discord/package.json index a219f8f0b6e..8264729a13b 100644 --- a/extensions/discord/package.json +++ b/extensions/discord/package.json @@ -39,7 +39,6 @@ }, "install": { "npmSpec": "@openclaw/discord", - "localPath": "extensions/discord", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/discord/src/api.test.ts b/extensions/discord/src/api.test.ts index f2a6d557769..6e2453dec7c 100644 --- a/extensions/discord/src/api.test.ts +++ b/extensions/discord/src/api.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js"; +import { withFetchPreconnect } from "../../../test/helpers/plugins/fetch-mock.js"; import { fetchDiscord } from "./api.js"; import { jsonResponse } from "./test-http-helpers.js"; diff --git a/extensions/discord/src/channel.test.ts b/extensions/discord/src/channel.test.ts index 93a7fdc838f..b59ffd3e71e 100644 --- a/extensions/discord/src/channel.test.ts +++ b/extensions/discord/src/channel.test.ts @@ -4,7 +4,7 @@ import type { PluginApprovalResolved, } from "../../../src/infra/plugin-approvals.js"; import type { PluginRuntime } from "../../../src/plugins/runtime/types.js"; -import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js"; +import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js"; import type { ResolvedDiscordAccount } from "./accounts.js"; import type { OpenClawConfig } from "./runtime-api.js"; let discordPlugin: typeof import("./channel.js").discordPlugin; diff --git a/extensions/discord/src/chunk.test.ts b/extensions/discord/src/chunk.test.ts index 228871fe5d6..7081d3165f7 100644 --- a/extensions/discord/src/chunk.test.ts +++ b/extensions/discord/src/chunk.test.ts @@ -1,8 +1,5 @@ import { describe, expect, it } from "vitest"; -import { - countLines, - hasBalancedFences, -} from "../../../test/helpers/extensions/chunk-test-helpers.js"; +import { countLines, hasBalancedFences } from "../../../test/helpers/plugins/chunk-test-helpers.js"; import { chunkDiscordText, chunkDiscordTextWithMode } from "./chunk.js"; describe("chunkDiscordText", () => { diff --git a/extensions/discord/src/monitor.test.ts b/extensions/discord/src/monitor.test.ts index c1edba55d16..ba62bfe6370 100644 --- a/extensions/discord/src/monitor.test.ts +++ b/extensions/discord/src/monitor.test.ts @@ -1,6 +1,6 @@ import { ChannelType, type Guild } from "@buape/carbon"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { typedCases } from "../../../test/helpers/extensions/typed-cases.js"; +import { typedCases } from "../../../test/helpers/plugins/typed-cases.js"; import { allowListMatches, buildDiscordMediaPayload, diff --git a/extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts b/extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts index 112abfb3eb0..33751e209b7 100644 --- a/extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts +++ b/extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts @@ -1,6 +1,6 @@ import * as conversationRuntime from "openclaw/plugin-sdk/conversation-runtime"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createConfiguredBindingConversationRuntimeModuleMock } from "../../../../test/helpers/extensions/configured-binding-runtime.js"; +import { createConfiguredBindingConversationRuntimeModuleMock } from "../../../../test/helpers/plugins/configured-binding-runtime.js"; const ensureConfiguredBindingRouteReadyMock = vi.hoisted(() => vi.fn()); const resolveConfiguredBindingRouteMock = vi.hoisted(() => vi.fn()); diff --git a/extensions/discord/src/monitor/monitor.agent-components.test.ts b/extensions/discord/src/monitor/monitor.agent-components.test.ts index d55b7887abf..34c60257db4 100644 --- a/extensions/discord/src/monitor/monitor.agent-components.test.ts +++ b/extensions/discord/src/monitor/monitor.agent-components.test.ts @@ -6,12 +6,12 @@ import { buildAgentSessionKey } from "openclaw/plugin-sdk/routing"; import * as securityRuntime from "openclaw/plugin-sdk/security-runtime"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { peekSystemEvents, resetSystemEventsForTest } from "../../../../src/infra/system-events.ts"; +import { expectPairingReplyText } from "../../../../test/helpers/pairing-reply.js"; import { readAllowFromStoreMock, resetDiscordComponentRuntimeMocks, upsertPairingRequestMock, -} from "../../../../test/helpers/extensions/discord-component-runtime.js"; -import { expectPairingReplyText } from "../../../../test/helpers/pairing-reply.js"; +} from "../../../../test/helpers/plugins/discord-component-runtime.js"; import { createAgentComponentButton, createAgentSelectMenu } from "./agent-components.js"; describe("agent components", () => { diff --git a/extensions/discord/src/monitor/monitor.test.ts b/extensions/discord/src/monitor/monitor.test.ts index da35d3c334a..c81b94c2617 100644 --- a/extensions/discord/src/monitor/monitor.test.ts +++ b/extensions/discord/src/monitor/monitor.test.ts @@ -18,7 +18,7 @@ import { resetDiscordComponentRuntimeMocks, resolvePluginConversationBindingApprovalMock, upsertPairingRequestMock, -} from "../../../../test/helpers/extensions/discord-component-runtime.js"; +} from "../../../../test/helpers/plugins/discord-component-runtime.js"; import { type DiscordComponentEntry, type DiscordModalEntry } from "../components.js"; import type { DiscordChannelConfigResolved } from "./allow-list.js"; import { diff --git a/extensions/discord/src/monitor/native-command.think-autocomplete.test.ts b/extensions/discord/src/monitor/native-command.think-autocomplete.test.ts index a0cc6e45703..b9327613adf 100644 --- a/extensions/discord/src/monitor/native-command.think-autocomplete.test.ts +++ b/extensions/discord/src/monitor/native-command.think-autocomplete.test.ts @@ -9,7 +9,7 @@ import { } from "../../../../src/auto-reply/commands-registry.js"; import type { OpenClawConfig, loadConfig } from "../../../../src/config/config.js"; import { clearSessionStoreCacheForTest } from "../../../../src/config/sessions/store.js"; -import { createConfiguredBindingConversationRuntimeModuleMock } from "../../../../test/helpers/extensions/configured-binding-runtime.js"; +import { createConfiguredBindingConversationRuntimeModuleMock } from "../../../../test/helpers/plugins/configured-binding-runtime.js"; import { createNoopThreadBindingManager } from "./thread-bindings.js"; const ensureConfiguredBindingRouteReadyMock = vi.hoisted(() => diff --git a/extensions/discord/src/monitor/provider.allowlist.test.ts b/extensions/discord/src/monitor/provider.allowlist.test.ts index a40933dfc90..4fc96b6dd15 100644 --- a/extensions/discord/src/monitor/provider.allowlist.test.ts +++ b/extensions/discord/src/monitor/provider.allowlist.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { RuntimeEnv } from "../../../../src/runtime.js"; -import { createNonExitingTypedRuntimeEnv } from "../../../../test/helpers/extensions/runtime-env.js"; +import { createNonExitingTypedRuntimeEnv } from "../../../../test/helpers/plugins/runtime-env.js"; import * as resolveChannelsModule from "../resolve-channels.js"; import * as resolveUsersModule from "../resolve-users.js"; import { resolveDiscordAllowlistConfig } from "./provider.allowlist.js"; diff --git a/extensions/discord/src/monitor/provider.test.ts b/extensions/discord/src/monitor/provider.test.ts index 155d92202e7..5a0e3ebc8d4 100644 --- a/extensions/discord/src/monitor/provider.test.ts +++ b/extensions/discord/src/monitor/provider.test.ts @@ -9,7 +9,7 @@ import { getFirstDiscordMessageHandlerParams, getProviderMonitorTestMocks, resetDiscordProviderMonitorMocks, -} from "../../../../test/helpers/extensions/discord-provider.test-support.js"; +} from "../../../../test/helpers/plugins/discord-provider.test-support.js"; const { clientConstructorOptionsMock, diff --git a/extensions/discord/src/resolve-channels.test.ts b/extensions/discord/src/resolve-channels.test.ts index 8fd06593923..c18a5935bc2 100644 --- a/extensions/discord/src/resolve-channels.test.ts +++ b/extensions/discord/src/resolve-channels.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js"; +import { withFetchPreconnect } from "../../../test/helpers/plugins/fetch-mock.js"; import { resolveDiscordChannelAllowlist } from "./resolve-channels.js"; import { jsonResponse, urlToString } from "./test-http-helpers.js"; diff --git a/extensions/discord/src/resolve-users.test.ts b/extensions/discord/src/resolve-users.test.ts index 080c312b856..8ffd0762d94 100644 --- a/extensions/discord/src/resolve-users.test.ts +++ b/extensions/discord/src/resolve-users.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js"; +import { withFetchPreconnect } from "../../../test/helpers/plugins/fetch-mock.js"; import { resolveDiscordUserAllowlist } from "./resolve-users.js"; import { jsonResponse, urlToString } from "./test-http-helpers.js"; diff --git a/extensions/discord/src/subagent-hooks.test.ts b/extensions/discord/src/subagent-hooks.test.ts index 890be595f69..070d608ac87 100644 --- a/extensions/discord/src/subagent-hooks.test.ts +++ b/extensions/discord/src/subagent-hooks.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getRequiredHookHandler, registerHookHandlersForTest, -} from "../../../test/helpers/extensions/subagent-hooks.js"; +} from "../../../test/helpers/plugins/subagent-hooks.js"; type ThreadBindingRecord = { accountId: string; diff --git a/extensions/discord/test-api.ts b/extensions/discord/test-api.ts index 79467537a4e..df136732857 100644 --- a/extensions/discord/test-api.ts +++ b/extensions/discord/test-api.ts @@ -1,2 +1,3 @@ export { buildFinalizedDiscordDirectInboundContext } from "./src/monitor/inbound-context.test-helpers.js"; +export { __testing as discordThreadBindingTesting } from "./src/monitor/thread-bindings.manager.js"; export { discordOutbound } from "./src/outbound-adapter.js"; diff --git a/extensions/duckduckgo/bundled-web-search.contract.test.ts b/extensions/duckduckgo/bundled-web-search.contract.test.ts index 66102c8e556..a4e29319aa2 100644 --- a/extensions/duckduckgo/bundled-web-search.contract.test.ts +++ b/extensions/duckduckgo/bundled-web-search.contract.test.ts @@ -1,3 +1,3 @@ -import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js"; +import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js"; describeBundledWebSearchFastPathContract("duckduckgo"); diff --git a/extensions/duckduckgo/plugin-registration.contract.test.ts b/extensions/duckduckgo/plugin-registration.contract.test.ts index 23ba150fbb1..8865c48a8e2 100644 --- a/extensions/duckduckgo/plugin-registration.contract.test.ts +++ b/extensions/duckduckgo/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "duckduckgo", diff --git a/extensions/duckduckgo/web-search-provider.contract.test.ts b/extensions/duckduckgo/web-search-provider.contract.test.ts index 8e54ab2c5a4..16383ff8c77 100644 --- a/extensions/duckduckgo/web-search-provider.contract.test.ts +++ b/extensions/duckduckgo/web-search-provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js"; +import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js"; describeWebSearchProviderContracts("duckduckgo"); diff --git a/extensions/elevenlabs/plugin-registration.contract.test.ts b/extensions/elevenlabs/plugin-registration.contract.test.ts index 7464c71f847..553ebd428db 100644 --- a/extensions/elevenlabs/plugin-registration.contract.test.ts +++ b/extensions/elevenlabs/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "elevenlabs", diff --git a/extensions/exa/bundled-web-search.contract.test.ts b/extensions/exa/bundled-web-search.contract.test.ts index a6dafde9876..e1719519a7b 100644 --- a/extensions/exa/bundled-web-search.contract.test.ts +++ b/extensions/exa/bundled-web-search.contract.test.ts @@ -1,3 +1,3 @@ -import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js"; +import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js"; describeBundledWebSearchFastPathContract("exa"); diff --git a/extensions/exa/plugin-registration.contract.test.ts b/extensions/exa/plugin-registration.contract.test.ts index 60c67675442..6e07f09a6d8 100644 --- a/extensions/exa/plugin-registration.contract.test.ts +++ b/extensions/exa/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "exa", diff --git a/extensions/exa/web-search-provider.contract.test.ts b/extensions/exa/web-search-provider.contract.test.ts index 65a4aed16ad..3d0de251fbf 100644 --- a/extensions/exa/web-search-provider.contract.test.ts +++ b/extensions/exa/web-search-provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js"; +import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js"; describeWebSearchProviderContracts("exa"); diff --git a/extensions/fal/plugin-registration.contract.test.ts b/extensions/fal/plugin-registration.contract.test.ts index 1721f0e457b..0eb31e1fa47 100644 --- a/extensions/fal/plugin-registration.contract.test.ts +++ b/extensions/fal/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "fal", diff --git a/extensions/fal/provider.contract.test.ts b/extensions/fal/provider.contract.test.ts index 09f7829b8f3..a8d94c2db69 100644 --- a/extensions/fal/provider.contract.test.ts +++ b/extensions/fal/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("fal"); diff --git a/extensions/feishu/package-manifest.contract.test.ts b/extensions/feishu/package-manifest.contract.test.ts index 8239226d7e6..ca5e03009be 100644 --- a/extensions/feishu/package-manifest.contract.test.ts +++ b/extensions/feishu/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "feishu", diff --git a/extensions/feishu/package.json b/extensions/feishu/package.json index 68c1fe5f87a..ab84fa522e1 100644 --- a/extensions/feishu/package.json +++ b/extensions/feishu/package.json @@ -39,7 +39,6 @@ }, "install": { "npmSpec": "@openclaw/feishu", - "localPath": "extensions/feishu", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/feishu/src/bot.broadcast.test.ts b/extensions/feishu/src/bot.broadcast.test.ts index 10c388a342c..2e1acdaac66 100644 --- a/extensions/feishu/src/bot.broadcast.test.ts +++ b/extensions/feishu/src/bot.broadcast.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { EnvelopeFormatOptions } from "../../../src/auto-reply/envelope.js"; -import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, PluginRuntime } from "../runtime-api.js"; import type { FeishuMessageEvent } from "./bot.js"; import { handleFeishuMessage } from "./bot.js"; diff --git a/extensions/feishu/src/bot.card-action.test.ts b/extensions/feishu/src/bot.card-action.test.ts index 9613aefacaa..25658b309c9 100644 --- a/extensions/feishu/src/bot.card-action.test.ts +++ b/extensions/feishu/src/bot.card-action.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { handleFeishuCardAction, diff --git a/extensions/feishu/src/bot.test.ts b/extensions/feishu/src/bot.test.ts index bb9c2a8e43f..a9ac447c06c 100644 --- a/extensions/feishu/src/bot.test.ts +++ b/extensions/feishu/src/bot.test.ts @@ -1,8 +1,8 @@ import type * as ConversationRuntime from "openclaw/plugin-sdk/conversation-runtime"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { ResolvedAgentRoute } from "../../../src/routing/resolve-route.js"; -import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, PluginRuntime, RuntimeEnv } from "../runtime-api.js"; import type { FeishuMessageEvent } from "./bot.js"; import { diff --git a/extensions/feishu/src/card-ux-launcher.test.ts b/extensions/feishu/src/card-ux-launcher.test.ts index 40efeb02d0f..74fe72514f3 100644 --- a/extensions/feishu/src/card-ux-launcher.test.ts +++ b/extensions/feishu/src/card-ux-launcher.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { createQuickActionLauncherCard, diff --git a/extensions/feishu/src/chat.test.ts b/extensions/feishu/src/chat.test.ts index 956c4c2f3b1..d2d80e48110 100644 --- a/extensions/feishu/src/chat.test.ts +++ b/extensions/feishu/src/chat.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../../test/helpers/extensions/plugin-api.js"; -import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js"; +import { createTestPluginApi } from "../../../test/helpers/plugins/plugin-api.js"; +import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; import type { OpenClawPluginApi } from "../runtime-api.js"; const createFeishuClientMock = vi.hoisted(() => vi.fn()); diff --git a/extensions/feishu/src/client.test.ts b/extensions/feishu/src/client.test.ts index d95787bdae9..e8c9ee6957f 100644 --- a/extensions/feishu/src/client.test.ts +++ b/extensions/feishu/src/client.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../../test/helpers/extensions/plugin-api.js"; -import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js"; +import { createTestPluginApi } from "../../../test/helpers/plugins/plugin-api.js"; +import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; import type { OpenClawPluginApi } from "../runtime-api.js"; import { FeishuConfigSchema } from "./config-schema.js"; import type { FeishuConfig, ResolvedFeishuAccount } from "./types.js"; diff --git a/extensions/feishu/src/monitor.acp-init-failure.lifecycle.test.ts b/extensions/feishu/src/monitor.acp-init-failure.lifecycle.test.ts index 20076282f9f..29986919558 100644 --- a/extensions/feishu/src/monitor.acp-init-failure.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.acp-init-failure.lifecycle.test.ts @@ -8,8 +8,8 @@ import { restoreFeishuLifecycleStateDir, setFeishuLifecycleStateDir, setupFeishuLifecycleHandler, -} from "../../../test/helpers/extensions/feishu-lifecycle.js"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +} from "../../../test/helpers/plugins/feishu-lifecycle.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { getFeishuLifecycleTestMocks } from "./lifecycle.test-support.js"; import type { ResolvedFeishuAccount } from "./types.js"; diff --git a/extensions/feishu/src/monitor.bot-menu.lifecycle.test.ts b/extensions/feishu/src/monitor.bot-menu.lifecycle.test.ts index 17a9e77f2f2..4a4b4127535 100644 --- a/extensions/feishu/src/monitor.bot-menu.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.bot-menu.lifecycle.test.ts @@ -12,8 +12,8 @@ import { restoreFeishuLifecycleStateDir, setFeishuLifecycleStateDir, setupFeishuLifecycleHandler, -} from "../../../test/helpers/extensions/feishu-lifecycle.js"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +} from "../../../test/helpers/plugins/feishu-lifecycle.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { getFeishuLifecycleTestMocks } from "./lifecycle.test-support.js"; import type { ResolvedFeishuAccount } from "./types.js"; diff --git a/extensions/feishu/src/monitor.bot-menu.test.ts b/extensions/feishu/src/monitor.bot-menu.test.ts index d3170757647..eca1dac76aa 100644 --- a/extensions/feishu/src/monitor.bot-menu.test.ts +++ b/extensions/feishu/src/monitor.bot-menu.test.ts @@ -4,7 +4,7 @@ import { createInboundDebouncer, resolveInboundDebounceMs, } from "../../../src/auto-reply/inbound-debounce.js"; -import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js"; +import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { monitorSingleAccount } from "./monitor.account.js"; import { setFeishuRuntime } from "./runtime.js"; diff --git a/extensions/feishu/src/monitor.broadcast.reply-once.lifecycle.test.ts b/extensions/feishu/src/monitor.broadcast.reply-once.lifecycle.test.ts index 54cc5e70dab..b9a9353c684 100644 --- a/extensions/feishu/src/monitor.broadcast.reply-once.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.broadcast.reply-once.lifecycle.test.ts @@ -9,8 +9,8 @@ import { runFeishuLifecycleSequence, setFeishuLifecycleStateDir, setupFeishuLifecycleHandler, -} from "../../../test/helpers/extensions/feishu-lifecycle.js"; -import { createNonExitingRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +} from "../../../test/helpers/plugins/feishu-lifecycle.js"; +import { createNonExitingRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { FeishuConfigSchema } from "./config-schema.js"; import { getFeishuLifecycleTestMocks } from "./lifecycle.test-support.js"; diff --git a/extensions/feishu/src/monitor.card-action.lifecycle.test.ts b/extensions/feishu/src/monitor.card-action.lifecycle.test.ts index d0af83d4707..71fdbaecce3 100644 --- a/extensions/feishu/src/monitor.card-action.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.card-action.lifecycle.test.ts @@ -12,8 +12,8 @@ import { restoreFeishuLifecycleStateDir, setFeishuLifecycleStateDir, setupFeishuLifecycleHandler, -} from "../../../test/helpers/extensions/feishu-lifecycle.js"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +} from "../../../test/helpers/plugins/feishu-lifecycle.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { resetProcessedFeishuCardActionTokensForTests } from "./card-action.js"; import { createFeishuCardInteractionEnvelope } from "./card-interaction.js"; diff --git a/extensions/feishu/src/monitor.reaction.test.ts b/extensions/feishu/src/monitor.reaction.test.ts index 238d8a060db..17608a27731 100644 --- a/extensions/feishu/src/monitor.reaction.test.ts +++ b/extensions/feishu/src/monitor.reaction.test.ts @@ -4,11 +4,11 @@ import { createInboundDebouncer, resolveInboundDebounceMs, } from "../../../src/auto-reply/inbound-debounce.js"; -import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js"; +import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; import { createNonExitingTypedRuntimeEnv, createRuntimeEnv, -} from "../../../test/helpers/extensions/runtime-env.js"; +} from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { parseFeishuMessageEvent, type FeishuMessageEvent } from "./bot.js"; import * as dedup from "./dedup.js"; diff --git a/extensions/feishu/src/monitor.reply-once.lifecycle.test.ts b/extensions/feishu/src/monitor.reply-once.lifecycle.test.ts index 0bf63fb5e8b..5e9f6ab35da 100644 --- a/extensions/feishu/src/monitor.reply-once.lifecycle.test.ts +++ b/extensions/feishu/src/monitor.reply-once.lifecycle.test.ts @@ -13,8 +13,8 @@ import { restoreFeishuLifecycleStateDir, setFeishuLifecycleStateDir, setupFeishuLifecycleHandler, -} from "../../../test/helpers/extensions/feishu-lifecycle.js"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +} from "../../../test/helpers/plugins/feishu-lifecycle.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { getFeishuLifecycleTestMocks } from "./lifecycle.test-support.js"; import type { ResolvedFeishuAccount } from "./types.js"; diff --git a/extensions/feishu/src/monitor.startup.test.ts b/extensions/feishu/src/monitor.startup.test.ts index cb85ad859ad..f70e898755d 100644 --- a/extensions/feishu/src/monitor.startup.test.ts +++ b/extensions/feishu/src/monitor.startup.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { createNonExitingRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createNonExitingRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { ClawdbotConfig } from "../runtime-api.js"; import { monitorFeishuProvider, stopFeishuMonitor } from "./monitor.js"; diff --git a/extensions/feishu/src/setup-surface.test.ts b/extensions/feishu/src/setup-surface.test.ts index 91583c235f3..082441138fb 100644 --- a/extensions/feishu/src/setup-surface.test.ts +++ b/extensions/feishu/src/setup-surface.test.ts @@ -1,11 +1,11 @@ import { describe, expect, it, vi } from "vitest"; -import { createNonExitingTypedRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createNonExitingTypedRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import { createPluginSetupWizardConfigure, createPluginSetupWizardStatus, createTestWizardPrompter, runSetupWizardConfigure, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; vi.mock("./probe.js", () => ({ probeFeishu: vi.fn(async () => ({ ok: false, error: "mocked" })), diff --git a/extensions/feishu/src/subagent-hooks.test.ts b/extensions/feishu/src/subagent-hooks.test.ts index d8c47bda71a..796bf67403e 100644 --- a/extensions/feishu/src/subagent-hooks.test.ts +++ b/extensions/feishu/src/subagent-hooks.test.ts @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getRequiredHookHandler, registerHookHandlersForTest, -} from "../../../test/helpers/extensions/subagent-hooks.js"; +} from "../../../test/helpers/plugins/subagent-hooks.js"; import type { ClawdbotConfig, OpenClawPluginApi } from "../runtime-api.js"; import { registerFeishuSubagentHooks } from "./subagent-hooks.js"; import { diff --git a/extensions/firecrawl/bundled-web-search.contract.test.ts b/extensions/firecrawl/bundled-web-search.contract.test.ts index 8c97d44304c..c3587b7e85d 100644 --- a/extensions/firecrawl/bundled-web-search.contract.test.ts +++ b/extensions/firecrawl/bundled-web-search.contract.test.ts @@ -1,3 +1,3 @@ -import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js"; +import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js"; describeBundledWebSearchFastPathContract("firecrawl"); diff --git a/extensions/firecrawl/plugin-registration.contract.test.ts b/extensions/firecrawl/plugin-registration.contract.test.ts index 2e2c225cf54..69c5d24b029 100644 --- a/extensions/firecrawl/plugin-registration.contract.test.ts +++ b/extensions/firecrawl/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "firecrawl", diff --git a/extensions/firecrawl/web-search-provider.contract.test.ts b/extensions/firecrawl/web-search-provider.contract.test.ts index 8f4bd036afd..2171f946a2b 100644 --- a/extensions/firecrawl/web-search-provider.contract.test.ts +++ b/extensions/firecrawl/web-search-provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js"; +import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js"; describeWebSearchProviderContracts("firecrawl"); diff --git a/extensions/github-copilot/models.test.ts b/extensions/github-copilot/models.test.ts index 960236ae636..67803eb5488 100644 --- a/extensions/github-copilot/models.test.ts +++ b/extensions/github-copilot/models.test.ts @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { createProviderUsageFetch, makeResponse, -} from "../../test/helpers/extensions/provider-usage-fetch.js"; +} from "../../test/helpers/plugins/provider-usage-fetch.js"; import { buildCopilotModelDefinition, getDefaultCopilotModelIds } from "./models-defaults.js"; import { fetchCopilotUsage } from "./usage.js"; diff --git a/extensions/github-copilot/provider-auth.contract.test.ts b/extensions/github-copilot/provider-auth.contract.test.ts index fcbc7e82111..589086a6471 100644 --- a/extensions/github-copilot/provider-auth.contract.test.ts +++ b/extensions/github-copilot/provider-auth.contract.test.ts @@ -1,3 +1,3 @@ -import { describeGithubCopilotProviderAuthContract } from "../../test/helpers/extensions/provider-auth-contract.js"; +import { describeGithubCopilotProviderAuthContract } from "../../test/helpers/plugins/provider-auth-contract.js"; describeGithubCopilotProviderAuthContract(); diff --git a/extensions/github-copilot/provider-discovery.contract.test.ts b/extensions/github-copilot/provider-discovery.contract.test.ts index e681fe0befe..ccdb30f5266 100644 --- a/extensions/github-copilot/provider-discovery.contract.test.ts +++ b/extensions/github-copilot/provider-discovery.contract.test.ts @@ -1,3 +1,3 @@ -import { describeGithubCopilotProviderDiscoveryContract } from "../../test/helpers/extensions/provider-discovery-contract.js"; +import { describeGithubCopilotProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js"; describeGithubCopilotProviderDiscoveryContract(); diff --git a/extensions/github-copilot/provider-runtime.contract.test.ts b/extensions/github-copilot/provider-runtime.contract.test.ts index 5014b4b5d30..9bf9f022271 100644 --- a/extensions/github-copilot/provider-runtime.contract.test.ts +++ b/extensions/github-copilot/provider-runtime.contract.test.ts @@ -1,3 +1,3 @@ -import { describeGithubCopilotProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js"; +import { describeGithubCopilotProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js"; describeGithubCopilotProviderRuntimeContract(); diff --git a/extensions/github-copilot/provider.contract.test.ts b/extensions/github-copilot/provider.contract.test.ts index 54b89e0a296..61befe3e4d6 100644 --- a/extensions/github-copilot/provider.contract.test.ts +++ b/extensions/github-copilot/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("github-copilot"); diff --git a/extensions/google/bundled-web-search.contract.test.ts b/extensions/google/bundled-web-search.contract.test.ts index 6e39bcc5606..86b6009e85f 100644 --- a/extensions/google/bundled-web-search.contract.test.ts +++ b/extensions/google/bundled-web-search.contract.test.ts @@ -1,3 +1,3 @@ -import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js"; +import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js"; describeBundledWebSearchFastPathContract("google"); diff --git a/extensions/google/media-understanding-provider.video.test.ts b/extensions/google/media-understanding-provider.video.test.ts index 99b3f7db769..41742d8f5d7 100644 --- a/extensions/google/media-understanding-provider.video.test.ts +++ b/extensions/google/media-understanding-provider.video.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import * as ssrf from "../../src/infra/net/ssrf.js"; -import { withFetchPreconnect } from "../../test/helpers/extensions/fetch-mock.js"; -import { createRequestCaptureJsonFetch } from "../../test/helpers/extensions/media-understanding.js"; +import { withFetchPreconnect } from "../../test/helpers/plugins/fetch-mock.js"; +import { createRequestCaptureJsonFetch } from "../../test/helpers/plugins/media-understanding.js"; import { describeGeminiVideo } from "./media-understanding-provider.js"; const TEST_NET_IP = "203.0.113.10"; diff --git a/extensions/google/plugin-registration.contract.test.ts b/extensions/google/plugin-registration.contract.test.ts index 25510d2a782..9824641f26e 100644 --- a/extensions/google/plugin-registration.contract.test.ts +++ b/extensions/google/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "google", diff --git a/extensions/google/provider-runtime.contract.test.ts b/extensions/google/provider-runtime.contract.test.ts index f981a75c1d3..b402248a47e 100644 --- a/extensions/google/provider-runtime.contract.test.ts +++ b/extensions/google/provider-runtime.contract.test.ts @@ -1,3 +1,3 @@ -import { describeGoogleProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js"; +import { describeGoogleProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js"; describeGoogleProviderRuntimeContract(); diff --git a/extensions/google/provider.contract.test.ts b/extensions/google/provider.contract.test.ts index 5d2e92e748c..d9c8167340d 100644 --- a/extensions/google/provider.contract.test.ts +++ b/extensions/google/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("google"); diff --git a/extensions/google/web-search-provider.contract.test.ts b/extensions/google/web-search-provider.contract.test.ts index 311f50a3e41..68d1c6222cc 100644 --- a/extensions/google/web-search-provider.contract.test.ts +++ b/extensions/google/web-search-provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js"; +import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js"; describeWebSearchProviderContracts("google"); diff --git a/extensions/googlechat/package-manifest.contract.test.ts b/extensions/googlechat/package-manifest.contract.test.ts index 14224ffb7ca..a92134528ec 100644 --- a/extensions/googlechat/package-manifest.contract.test.ts +++ b/extensions/googlechat/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "googlechat", diff --git a/extensions/googlechat/package.json b/extensions/googlechat/package.json index 9cf0af5ba9a..714596e7b20 100644 --- a/extensions/googlechat/package.json +++ b/extensions/googlechat/package.json @@ -41,7 +41,6 @@ }, "install": { "npmSpec": "@openclaw/googlechat", - "localPath": "extensions/googlechat", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" } diff --git a/extensions/googlechat/src/channel.test.ts b/extensions/googlechat/src/channel.test.ts index 46d1cd75f13..58dcba8d537 100644 --- a/extensions/googlechat/src/channel.test.ts +++ b/extensions/googlechat/src/channel.test.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { createDirectoryTestRuntime, expectDirectorySurface, -} from "../../../test/helpers/extensions/directory.ts"; +} from "../../../test/helpers/plugins/directory.ts"; import type { OpenClawConfig, PluginRuntime } from "../runtime-api.js"; const uploadGoogleChatAttachmentMock = vi.hoisted(() => vi.fn()); diff --git a/extensions/googlechat/src/monitor.webhook-routing.test.ts b/extensions/googlechat/src/monitor.webhook-routing.test.ts index 3f1800919a7..f0509c28254 100644 --- a/extensions/googlechat/src/monitor.webhook-routing.test.ts +++ b/extensions/googlechat/src/monitor.webhook-routing.test.ts @@ -3,7 +3,7 @@ import type { IncomingMessage } from "node:http"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createEmptyPluginRegistry } from "../../../src/plugins/registry.js"; import { setActivePluginRegistry } from "../../../src/plugins/runtime.js"; -import { createMockServerResponse } from "../../../test/helpers/extensions/mock-http-response.js"; +import { createMockServerResponse } from "../../../test/helpers/plugins/mock-http-response.js"; import type { OpenClawConfig, PluginRuntime } from "../runtime-api.js"; import type { ResolvedGoogleChatAccount } from "./accounts.js"; import { verifyGoogleChatRequest } from "./auth.js"; diff --git a/extensions/googlechat/src/setup.test.ts b/extensions/googlechat/src/setup.test.ts index a5ed29858a9..3fb330fa34e 100644 --- a/extensions/googlechat/src/setup.test.ts +++ b/extensions/googlechat/src/setup.test.ts @@ -5,13 +5,13 @@ import { createTestWizardPrompter, runSetupWizardConfigure, type WizardPrompter, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import { expectLifecyclePatch, expectPendingUntilAbort, startAccountAndTrackLifecycle, waitForStartedMocks, -} from "../../../test/helpers/extensions/start-account-lifecycle.js"; +} from "../../../test/helpers/plugins/start-account-lifecycle.js"; import type { OpenClawConfig } from "../runtime-api.js"; import { resolveGoogleChatAccount, type ResolvedGoogleChatAccount } from "./accounts.js"; import { googlechatPlugin } from "./channel.js"; diff --git a/extensions/groq/plugin-registration.contract.test.ts b/extensions/groq/plugin-registration.contract.test.ts index ed05341a7e2..ef33db84827 100644 --- a/extensions/groq/plugin-registration.contract.test.ts +++ b/extensions/groq/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "groq", diff --git a/extensions/huggingface/provider.contract.test.ts b/extensions/huggingface/provider.contract.test.ts index 4bbb0c0d92b..adab2999430 100644 --- a/extensions/huggingface/provider.contract.test.ts +++ b/extensions/huggingface/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("huggingface"); diff --git a/extensions/irc/package-manifest.contract.test.ts b/extensions/irc/package-manifest.contract.test.ts index 492e009c7b3..bef5f20587b 100644 --- a/extensions/irc/package-manifest.contract.test.ts +++ b/extensions/irc/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "irc", diff --git a/extensions/irc/src/send.test.ts b/extensions/irc/src/send.test.ts index a8902f6efb3..3474d34b602 100644 --- a/extensions/irc/src/send.test.ts +++ b/extensions/irc/src/send.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createSendCfgThreadingRuntime } from "../../../test/helpers/extensions/send-config.js"; +import { createSendCfgThreadingRuntime } from "../../../test/helpers/plugins/send-config.js"; import type { IrcClient } from "./client.js"; import { setIrcRuntime } from "./runtime.js"; import type { CoreConfig } from "./types.js"; diff --git a/extensions/irc/src/setup.test.ts b/extensions/irc/src/setup.test.ts index 2ce877e6d44..eaa317fa8b9 100644 --- a/extensions/irc/src/setup.test.ts +++ b/extensions/irc/src/setup.test.ts @@ -5,12 +5,12 @@ import { promptSetupWizardAllowFrom, runSetupWizardConfigure, type WizardPrompter, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import { expectStopPendingUntilAbort, startAccountAndTrackLifecycle, waitForStartedMocks, -} from "../../../test/helpers/extensions/start-account-lifecycle.js"; +} from "../../../test/helpers/plugins/start-account-lifecycle.js"; import type { ResolvedIrcAccount } from "./accounts.js"; import { ircPlugin } from "./channel.js"; import { setIrcRuntime } from "./runtime.js"; @@ -351,9 +351,6 @@ describe("irc setup", () => { it("keeps startAccount pending until abort, then stops the monitor", async () => { const stop = vi.fn(); vi.resetModules(); - vi.doMock("../../../src/generated/bundled-channel-entries.generated.ts", () => ({ - GENERATED_BUNDLED_CHANNEL_ENTRIES: [], - })); hoisted.monitorIrcProvider.mockResolvedValue({ stop }); installIrcRuntime(); const { ircPlugin: runtimeMockedPlugin } = await import("./channel.js"); diff --git a/extensions/kilocode/onboard.test.ts b/extensions/kilocode/onboard.test.ts index f0be8a60e1a..f0ffa4795fe 100644 --- a/extensions/kilocode/onboard.test.ts +++ b/extensions/kilocode/onboard.test.ts @@ -12,7 +12,7 @@ import { KILOCODE_DEFAULT_COST, KILOCODE_DEFAULT_MODEL_ID, } from "../../src/plugin-sdk/kilocode.js"; -import { captureEnv } from "../../test/helpers/extensions/env.js"; +import { captureEnv } from "../../test/helpers/plugins/env.js"; import { applyKilocodeProviderConfig, applyKilocodeConfig, diff --git a/extensions/kilocode/provider.contract.test.ts b/extensions/kilocode/provider.contract.test.ts index a42c9002a46..cf82aff90d1 100644 --- a/extensions/kilocode/provider.contract.test.ts +++ b/extensions/kilocode/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("kilocode"); diff --git a/extensions/line/package-manifest.contract.test.ts b/extensions/line/package-manifest.contract.test.ts index 03c7fcfbd2b..9cca21fff09 100644 --- a/extensions/line/package-manifest.contract.test.ts +++ b/extensions/line/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "line", diff --git a/extensions/line/package.json b/extensions/line/package.json index 0ad278da1d8..11887a5fcdf 100644 --- a/extensions/line/package.json +++ b/extensions/line/package.json @@ -34,7 +34,6 @@ }, "install": { "npmSpec": "@openclaw/line", - "localPath": "extensions/line", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" } diff --git a/extensions/line/src/channel.logout.test.ts b/extensions/line/src/channel.logout.test.ts index 0b3dd9a9517..ea739819651 100644 --- a/extensions/line/src/channel.logout.test.ts +++ b/extensions/line/src/channel.logout.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { OpenClawConfig, PluginRuntime, ResolvedLineAccount } from "../api.js"; import { linePlugin } from "./channel.js"; import { setLineRuntime } from "./runtime.js"; diff --git a/extensions/line/src/setup-surface.test.ts b/extensions/line/src/setup-surface.test.ts index e85611d5c45..f94530b6612 100644 --- a/extensions/line/src/setup-surface.test.ts +++ b/extensions/line/src/setup-surface.test.ts @@ -2,14 +2,15 @@ import { readFileSync } from "node:fs"; import path from "node:path"; import ts from "typescript"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { loadRuntimeApiExportTypesViaJiti } from "../../../test/helpers/extensions/jiti-runtime-api.ts"; +import { bundledPluginRoot } from "../../../test/helpers/bundled-plugin-paths.js"; +import { loadRuntimeApiExportTypesViaJiti } from "../../../test/helpers/plugins/jiti-runtime-api.ts"; import { createPluginSetupWizardConfigure, createTestWizardPrompter, runSetupWizardConfigure, type WizardPrompter, -} from "../../../test/helpers/extensions/setup-wizard.js"; -import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; +import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js"; import type { OpenClawConfig, PluginRuntime, ResolvedLineAccount } from "../api.js"; import { linePlugin } from "./channel.js"; import { clearLineRuntime, setLineRuntime } from "./runtime.js"; @@ -28,13 +29,14 @@ vi.mock("@line/bot-sdk", () => ({ const lineConfigure = createPluginSetupWizardConfigure(linePlugin); let probeLineBot: typeof import("./probe.js").probeLineBot; +const LINE_SRC_PREFIX = `../../${bundledPluginRoot("line")}/src/`; function normalizeModuleSpecifier(specifier: string): string | null { if (specifier.startsWith("./src/")) { return specifier; } - if (specifier.startsWith("../../extensions/line/src/")) { - return `./src/${specifier.slice("../../extensions/line/src/".length)}`; + if (specifier.startsWith(LINE_SRC_PREFIX)) { + return `./src/${specifier.slice(LINE_SRC_PREFIX.length)}`; } return null; } diff --git a/extensions/litellm/onboard.test.ts b/extensions/litellm/onboard.test.ts index 4db4097c08c..d0283a7ea03 100644 --- a/extensions/litellm/onboard.test.ts +++ b/extensions/litellm/onboard.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { createLegacyProviderConfig } from "../../test/helpers/extensions/onboard-config.js"; +import { createLegacyProviderConfig } from "../../test/helpers/plugins/onboard-config.js"; import { applyLitellmProviderConfig } from "./onboard.js"; describe("litellm onboard", () => { diff --git a/extensions/litellm/provider.contract.test.ts b/extensions/litellm/provider.contract.test.ts index cad6aa56eb0..32075890565 100644 --- a/extensions/litellm/provider.contract.test.ts +++ b/extensions/litellm/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("litellm"); diff --git a/extensions/matrix/package-manifest.contract.test.ts b/extensions/matrix/package-manifest.contract.test.ts index e4a92c61229..caba0465e2c 100644 --- a/extensions/matrix/package-manifest.contract.test.ts +++ b/extensions/matrix/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "matrix", diff --git a/extensions/matrix/package.json b/extensions/matrix/package.json index 287eb04860d..214828e9380 100644 --- a/extensions/matrix/package.json +++ b/extensions/matrix/package.json @@ -38,7 +38,6 @@ }, "install": { "npmSpec": "@openclaw/matrix", - "localPath": "extensions/matrix", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/matrix/src/channel.directory.test.ts b/extensions/matrix/src/channel.directory.test.ts index 57fc1e64e6b..917687879f5 100644 --- a/extensions/matrix/src/channel.directory.test.ts +++ b/extensions/matrix/src/channel.directory.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { RuntimeEnv } from "../runtime-api.js"; import { matrixPlugin } from "./channel.js"; import { resolveMatrixAccount } from "./matrix/accounts.js"; diff --git a/extensions/matrix/src/channel.resolve.test.ts b/extensions/matrix/src/channel.resolve.test.ts index 3d8e5a24154..43c8d4d0987 100644 --- a/extensions/matrix/src/channel.resolve.test.ts +++ b/extensions/matrix/src/channel.resolve.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createNonExitingRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createNonExitingRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; const resolveMatrixTargetsMock = vi.hoisted(() => vi.fn(async () => [])); diff --git a/extensions/matrix/src/matrix/monitor/index.test.ts b/extensions/matrix/src/matrix/monitor/index.test.ts index 7cd28046365..3c18c6103ab 100644 --- a/extensions/matrix/src/matrix/monitor/index.test.ts +++ b/extensions/matrix/src/matrix/monitor/index.test.ts @@ -1,7 +1,7 @@ import path from "node:path"; import { z } from "openclaw/plugin-sdk/zod"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { loadRuntimeApiExportTypesViaJiti } from "../../../../../test/helpers/extensions/jiti-runtime-api.ts"; +import { loadRuntimeApiExportTypesViaJiti } from "../../../../../test/helpers/plugins/jiti-runtime-api.ts"; const hoisted = vi.hoisted(() => { const callOrder: string[] = []; @@ -112,10 +112,6 @@ vi.mock("../../resolve-targets.js", () => ({ resolveMatrixTargets: vi.fn(async () => []), })); -vi.mock("../../../../../src/generated/bundled-channel-entries.generated.ts", () => ({ - GENERATED_BUNDLED_CHANNEL_ENTRIES: [], -})); - vi.mock("../../runtime.js", () => ({ getMatrixRuntime: () => ({ config: { diff --git a/extensions/matrix/src/matrix/monitor/route.test.ts b/extensions/matrix/src/matrix/monitor/route.test.ts index 97a5d4fe0fe..fd034a43b8b 100644 --- a/extensions/matrix/src/matrix/monitor/route.test.ts +++ b/extensions/matrix/src/matrix/monitor/route.test.ts @@ -6,7 +6,7 @@ import { resolveAgentRoute, setActivePluginRegistry, type OpenClawConfig, -} from "../../../../../test/helpers/extensions/matrix-monitor-route.js"; +} from "../../../../../test/helpers/plugins/matrix-monitor-route.js"; import { matrixPlugin } from "../../channel.js"; import { resolveMatrixInboundRoute } from "./route.js"; diff --git a/extensions/mattermost/package-manifest.contract.test.ts b/extensions/mattermost/package-manifest.contract.test.ts index 41d6aede6be..db4d0ccbfc3 100644 --- a/extensions/mattermost/package-manifest.contract.test.ts +++ b/extensions/mattermost/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "mattermost", diff --git a/extensions/mattermost/package.json b/extensions/mattermost/package.json index 20f4d89b429..89f4a455bff 100644 --- a/extensions/mattermost/package.json +++ b/extensions/mattermost/package.json @@ -34,7 +34,6 @@ }, "install": { "npmSpec": "@openclaw/mattermost", - "localPath": "extensions/mattermost", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" } diff --git a/extensions/mattermost/src/mattermost/interactions.test.ts b/extensions/mattermost/src/mattermost/interactions.test.ts index 841f1c94390..ee77058eb78 100644 --- a/extensions/mattermost/src/mattermost/interactions.test.ts +++ b/extensions/mattermost/src/mattermost/interactions.test.ts @@ -1,6 +1,6 @@ import { type IncomingMessage, type ServerResponse } from "node:http"; import { describe, expect, it, beforeEach, afterEach, vi } from "vitest"; -import { createPluginRuntimeMock } from "../../../../test/helpers/extensions/plugin-runtime-mock.js"; +import { createPluginRuntimeMock } from "../../../../test/helpers/plugins/plugin-runtime-mock.js"; import { setMattermostRuntime } from "../runtime.js"; import { resolveMattermostAccount } from "./accounts.js"; import type { MattermostClient, MattermostPost } from "./client.js"; diff --git a/extensions/mattermost/src/mattermost/reply-delivery.test.ts b/extensions/mattermost/src/mattermost/reply-delivery.test.ts index 91d948291b0..80fe5db0c3a 100644 --- a/extensions/mattermost/src/mattermost/reply-delivery.test.ts +++ b/extensions/mattermost/src/mattermost/reply-delivery.test.ts @@ -3,7 +3,7 @@ import os from "node:os"; import path from "node:path"; import type { ChunkMode } from "openclaw/plugin-sdk/reply-runtime"; import { describe, expect, it, vi } from "vitest"; -import { createPluginRuntimeMock } from "../../../../test/helpers/extensions/plugin-runtime-mock.js"; +import { createPluginRuntimeMock } from "../../../../test/helpers/plugins/plugin-runtime-mock.js"; import type { OpenClawConfig } from "../../runtime-api.js"; import { deliverMattermostReplyPayload } from "./reply-delivery.js"; diff --git a/extensions/mattermost/src/mattermost/send.test.ts b/extensions/mattermost/src/mattermost/send.test.ts index 6c59d0d5b88..dfc0a6dfc94 100644 --- a/extensions/mattermost/src/mattermost/send.test.ts +++ b/extensions/mattermost/src/mattermost/send.test.ts @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { expectProvidedCfgSkipsRuntimeLoad, expectRuntimeCfgFallback, -} from "../../../../test/helpers/extensions/send-config.js"; +} from "../../../../test/helpers/plugins/send-config.js"; let parseMattermostTarget: typeof import("./send.js").parseMattermostTarget; let sendMessageMattermost: typeof import("./send.js").sendMessageMattermost; diff --git a/extensions/mattermost/src/setup.test.ts b/extensions/mattermost/src/setup.test.ts index 87f20cb1c37..ea40ca89678 100644 --- a/extensions/mattermost/src/setup.test.ts +++ b/extensions/mattermost/src/setup.test.ts @@ -1,6 +1,6 @@ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../../test/helpers/extensions/plugin-api.js"; +import { createTestPluginApi } from "../../../test/helpers/plugins/plugin-api.js"; import type { OpenClawConfig, OpenClawPluginApi } from "../runtime-api.js"; vi.mock("../../../src/config/bundled-channel-config-runtime.js", () => ({ diff --git a/extensions/memory-lancedb/package-manifest.contract.test.ts b/extensions/memory-lancedb/package-manifest.contract.test.ts index 15d6d2a5d4a..de45046c0c3 100644 --- a/extensions/memory-lancedb/package-manifest.contract.test.ts +++ b/extensions/memory-lancedb/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "memory-lancedb", diff --git a/extensions/memory-lancedb/package.json b/extensions/memory-lancedb/package.json index 5c942fec32c..6d295a2590e 100644 --- a/extensions/memory-lancedb/package.json +++ b/extensions/memory-lancedb/package.json @@ -14,7 +14,6 @@ ], "install": { "npmSpec": "@openclaw/memory-lancedb", - "localPath": "extensions/memory-lancedb", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/microsoft-foundry/index.test.ts b/extensions/microsoft-foundry/index.test.ts index 371309cb990..588786af00b 100644 --- a/extensions/microsoft-foundry/index.test.ts +++ b/extensions/microsoft-foundry/index.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../src/config/types.openclaw.js"; -import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js"; +import { createTestPluginApi } from "../../test/helpers/plugins/plugin-api.js"; import { getAccessTokenResultAsync } from "./cli.js"; import plugin from "./index.js"; import { buildFoundryConnectionTest, isValidTenantIdentifier } from "./onboard.js"; diff --git a/extensions/microsoft-foundry/provider.contract.test.ts b/extensions/microsoft-foundry/provider.contract.test.ts index 5c1c4495444..49f354e49ae 100644 --- a/extensions/microsoft-foundry/provider.contract.test.ts +++ b/extensions/microsoft-foundry/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("microsoft-foundry"); diff --git a/extensions/microsoft/plugin-registration.contract.test.ts b/extensions/microsoft/plugin-registration.contract.test.ts index 995e159ed8b..a4897c3da01 100644 --- a/extensions/microsoft/plugin-registration.contract.test.ts +++ b/extensions/microsoft/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "microsoft", diff --git a/extensions/minimax/onboard.test.ts b/extensions/minimax/onboard.test.ts index 331533f0481..95384149b87 100644 --- a/extensions/minimax/onboard.test.ts +++ b/extensions/minimax/onboard.test.ts @@ -7,7 +7,7 @@ import { createConfigWithFallbacks, createLegacyProviderConfig, EXPECTED_FALLBACKS, -} from "../../test/helpers/extensions/onboard-config.js"; +} from "../../test/helpers/plugins/onboard-config.js"; import { applyMinimaxApiConfig, applyMinimaxApiProviderConfig } from "./onboard.js"; describe("minimax onboard", () => { diff --git a/extensions/minimax/plugin-registration.contract.test.ts b/extensions/minimax/plugin-registration.contract.test.ts index a8d659c3762..9951b8e93c4 100644 --- a/extensions/minimax/plugin-registration.contract.test.ts +++ b/extensions/minimax/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "minimax", diff --git a/extensions/minimax/provider-discovery.contract.test.ts b/extensions/minimax/provider-discovery.contract.test.ts index c1f6e2d5609..11efcf29e94 100644 --- a/extensions/minimax/provider-discovery.contract.test.ts +++ b/extensions/minimax/provider-discovery.contract.test.ts @@ -1,3 +1,3 @@ -import { describeMinimaxProviderDiscoveryContract } from "../../test/helpers/extensions/provider-discovery-contract.js"; +import { describeMinimaxProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js"; describeMinimaxProviderDiscoveryContract(); diff --git a/extensions/minimax/provider.contract.test.ts b/extensions/minimax/provider.contract.test.ts index e8dfee30a32..49e06f14079 100644 --- a/extensions/minimax/provider.contract.test.ts +++ b/extensions/minimax/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("minimax"); diff --git a/extensions/mistral/onboard.test.ts b/extensions/mistral/onboard.test.ts index f32eca3a1d5..24fee3e8006 100644 --- a/extensions/mistral/onboard.test.ts +++ b/extensions/mistral/onboard.test.ts @@ -7,7 +7,7 @@ import { createConfigWithFallbacks, createLegacyProviderConfig, EXPECTED_FALLBACKS, -} from "../../test/helpers/extensions/onboard-config.js"; +} from "../../test/helpers/plugins/onboard-config.js"; import { buildMistralModelDefinition as buildBundledMistralModelDefinition } from "./model-definitions.js"; import { applyMistralConfig, diff --git a/extensions/mistral/plugin-registration.contract.test.ts b/extensions/mistral/plugin-registration.contract.test.ts index ae1184ae860..02f23acd773 100644 --- a/extensions/mistral/plugin-registration.contract.test.ts +++ b/extensions/mistral/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "mistral", diff --git a/extensions/mistral/provider.contract.test.ts b/extensions/mistral/provider.contract.test.ts index d72c32b076e..e7f5e4eb701 100644 --- a/extensions/mistral/provider.contract.test.ts +++ b/extensions/mistral/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("mistral"); diff --git a/extensions/modelstudio/provider-discovery.contract.test.ts b/extensions/modelstudio/provider-discovery.contract.test.ts index da900d6d514..9fddf4f80cb 100644 --- a/extensions/modelstudio/provider-discovery.contract.test.ts +++ b/extensions/modelstudio/provider-discovery.contract.test.ts @@ -1,3 +1,3 @@ -import { describeModelStudioProviderDiscoveryContract } from "../../test/helpers/extensions/provider-discovery-contract.js"; +import { describeModelStudioProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js"; describeModelStudioProviderDiscoveryContract(); diff --git a/extensions/modelstudio/provider.contract.test.ts b/extensions/modelstudio/provider.contract.test.ts index 4da569bd8f9..f2775a22c98 100644 --- a/extensions/modelstudio/provider.contract.test.ts +++ b/extensions/modelstudio/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("modelstudio"); diff --git a/extensions/moonshot/bundled-web-search.contract.test.ts b/extensions/moonshot/bundled-web-search.contract.test.ts index 3710d1863b5..ae518a3cc01 100644 --- a/extensions/moonshot/bundled-web-search.contract.test.ts +++ b/extensions/moonshot/bundled-web-search.contract.test.ts @@ -1,3 +1,3 @@ -import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js"; +import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js"; describeBundledWebSearchFastPathContract("moonshot"); diff --git a/extensions/moonshot/plugin-registration.contract.test.ts b/extensions/moonshot/plugin-registration.contract.test.ts index 8f4d8aa09fc..8a7f822f8db 100644 --- a/extensions/moonshot/plugin-registration.contract.test.ts +++ b/extensions/moonshot/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "moonshot", diff --git a/extensions/moonshot/provider.contract.test.ts b/extensions/moonshot/provider.contract.test.ts index 6b92074f8e1..1687cb1a3f7 100644 --- a/extensions/moonshot/provider.contract.test.ts +++ b/extensions/moonshot/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("moonshot"); diff --git a/extensions/moonshot/src/kimi-web-search-provider.test.ts b/extensions/moonshot/src/kimi-web-search-provider.test.ts index daf4664ffb0..cb748366a71 100644 --- a/extensions/moonshot/src/kimi-web-search-provider.test.ts +++ b/extensions/moonshot/src/kimi-web-search-provider.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { withEnv } from "../../../test/helpers/extensions/env.js"; +import { withEnv } from "../../../test/helpers/plugins/env.js"; import { __testing } from "./kimi-web-search-provider.js"; const kimiApiKeyEnv = ["KIMI_API", "KEY"].join("_"); diff --git a/extensions/moonshot/web-search-provider.contract.test.ts b/extensions/moonshot/web-search-provider.contract.test.ts index eff40a5fcb2..e0111555afe 100644 --- a/extensions/moonshot/web-search-provider.contract.test.ts +++ b/extensions/moonshot/web-search-provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js"; +import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js"; describeWebSearchProviderContracts("moonshot"); diff --git a/extensions/msteams/package-manifest.contract.test.ts b/extensions/msteams/package-manifest.contract.test.ts index 52ca75461dc..d571f090c0d 100644 --- a/extensions/msteams/package-manifest.contract.test.ts +++ b/extensions/msteams/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "msteams", diff --git a/extensions/msteams/package.json b/extensions/msteams/package.json index 77536b73d2b..6a7c279af03 100644 --- a/extensions/msteams/package.json +++ b/extensions/msteams/package.json @@ -38,7 +38,6 @@ }, "install": { "npmSpec": "@openclaw/msteams", - "localPath": "extensions/msteams", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/msteams/src/attachments.helpers.test.ts b/extensions/msteams/src/attachments.helpers.test.ts index 8e4dce89170..83c90ff052d 100644 --- a/extensions/msteams/src/attachments.helpers.test.ts +++ b/extensions/msteams/src/attachments.helpers.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it } from "vitest"; -import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js"; +import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; import type { PluginRuntime } from "../runtime-api.js"; import { buildMSTeamsAttachmentPlaceholder, diff --git a/extensions/msteams/src/attachments.test.ts b/extensions/msteams/src/attachments.test.ts index 88e644fbb3a..b1d35c3bd2f 100644 --- a/extensions/msteams/src/attachments.test.ts +++ b/extensions/msteams/src/attachments.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js"; +import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; import type { PluginRuntime, SsrFPolicy } from "../runtime-api.js"; import { downloadMSTeamsAttachments, downloadMSTeamsGraphMedia } from "./attachments.js"; import { setMSTeamsRuntime } from "./runtime.js"; diff --git a/extensions/msteams/src/channel.directory.test.ts b/extensions/msteams/src/channel.directory.test.ts index 160d9d3dc99..cb95316c830 100644 --- a/extensions/msteams/src/channel.directory.test.ts +++ b/extensions/msteams/src/channel.directory.test.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { createDirectoryTestRuntime, expectDirectorySurface, -} from "../../../test/helpers/extensions/directory.js"; +} from "../../../test/helpers/plugins/directory.js"; import type { OpenClawConfig, RuntimeEnv } from "../runtime-api.js"; import { msteamsPlugin } from "./channel.js"; import { resolveMSTeamsOutboundSessionRoute } from "./session-route.js"; diff --git a/extensions/msteams/src/graph-upload.test.ts b/extensions/msteams/src/graph-upload.test.ts index 572e5a0321d..af7b6eeb39f 100644 --- a/extensions/msteams/src/graph-upload.test.ts +++ b/extensions/msteams/src/graph-upload.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js"; +import { withFetchPreconnect } from "../../../test/helpers/plugins/fetch-mock.js"; import { buildTeamsFileInfoCard } from "./graph-chat.js"; import { resolveGraphChatId, uploadToOneDrive, uploadToSharePoint } from "./graph-upload.js"; diff --git a/extensions/msteams/src/messenger.test.ts b/extensions/msteams/src/messenger.test.ts index 2fa822aae94..85f3b0ae042 100644 --- a/extensions/msteams/src/messenger.test.ts +++ b/extensions/msteams/src/messenger.test.ts @@ -2,7 +2,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js"; +import { createPluginRuntimeMock } from "../../../test/helpers/plugins/plugin-runtime-mock.js"; import { SILENT_REPLY_TOKEN, type PluginRuntime } from "../runtime-api.js"; import type { StoredConversationReference } from "./conversation-store.js"; const graphUploadMockState = vi.hoisted(() => ({ diff --git a/extensions/nextcloud-talk/package-manifest.contract.test.ts b/extensions/nextcloud-talk/package-manifest.contract.test.ts index eb5aa782e7c..bae3db52861 100644 --- a/extensions/nextcloud-talk/package-manifest.contract.test.ts +++ b/extensions/nextcloud-talk/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "nextcloud-talk", diff --git a/extensions/nextcloud-talk/package.json b/extensions/nextcloud-talk/package.json index 77f38142f19..232eeb6fa9d 100644 --- a/extensions/nextcloud-talk/package.json +++ b/extensions/nextcloud-talk/package.json @@ -35,7 +35,6 @@ }, "install": { "npmSpec": "@openclaw/nextcloud-talk", - "localPath": "extensions/nextcloud-talk", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/nextcloud-talk/src/setup.test.ts b/extensions/nextcloud-talk/src/setup.test.ts index 263bee90b4c..5487acb78e4 100644 --- a/extensions/nextcloud-talk/src/setup.test.ts +++ b/extensions/nextcloud-talk/src/setup.test.ts @@ -7,13 +7,13 @@ import { createSendCfgThreadingRuntime, expectProvidedCfgSkipsRuntimeLoad, expectRuntimeCfgFallback, -} from "../../../test/helpers/extensions/send-config.js"; -import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js"; +} from "../../../test/helpers/plugins/send-config.js"; +import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js"; import { expectStopPendingUntilAbort, startAccountAndTrackLifecycle, waitForStartedMocks, -} from "../../../test/helpers/extensions/start-account-lifecycle.js"; +} from "../../../test/helpers/plugins/start-account-lifecycle.js"; import type { ResolvedNextcloudTalkAccount } from "./accounts.js"; import type { CoreConfig } from "./types.js"; diff --git a/extensions/nostr/package-manifest.contract.test.ts b/extensions/nostr/package-manifest.contract.test.ts index 4b47bf908c8..ace7dfdbf80 100644 --- a/extensions/nostr/package-manifest.contract.test.ts +++ b/extensions/nostr/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "nostr", diff --git a/extensions/nostr/package.json b/extensions/nostr/package.json index 39284b40729..08f90f9e4de 100644 --- a/extensions/nostr/package.json +++ b/extensions/nostr/package.json @@ -34,7 +34,6 @@ }, "install": { "npmSpec": "@openclaw/nostr", - "localPath": "extensions/nostr", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/nostr/src/channel.inbound.test.ts b/extensions/nostr/src/channel.inbound.test.ts index 0090253782e..2b9d06c328a 100644 --- a/extensions/nostr/src/channel.inbound.test.ts +++ b/extensions/nostr/src/channel.inbound.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js"; +import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js"; import type { PluginRuntime } from "../runtime-api.js"; import { nostrPlugin } from "./channel.js"; import { setNostrRuntime } from "./runtime.js"; diff --git a/extensions/nostr/src/channel.outbound.test.ts b/extensions/nostr/src/channel.outbound.test.ts index dd9f06af272..ad5d6000e91 100644 --- a/extensions/nostr/src/channel.outbound.test.ts +++ b/extensions/nostr/src/channel.outbound.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js"; +import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js"; import type { PluginRuntime } from "../runtime-api.js"; import { nostrPlugin } from "./channel.js"; import { setNostrRuntime } from "./runtime.js"; diff --git a/extensions/nostr/src/channel.test.ts b/extensions/nostr/src/channel.test.ts index d99fd5aa60d..7204b5c4c96 100644 --- a/extensions/nostr/src/channel.test.ts +++ b/extensions/nostr/src/channel.test.ts @@ -4,7 +4,7 @@ import { createTestWizardPrompter, runSetupWizardConfigure, type WizardPrompter, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import type { OpenClawConfig } from "../runtime-api.js"; import { nostrPlugin } from "./channel.js"; import { diff --git a/extensions/nvidia/provider.contract.test.ts b/extensions/nvidia/provider.contract.test.ts index 3fac55560a6..e59507abde5 100644 --- a/extensions/nvidia/provider.contract.test.ts +++ b/extensions/nvidia/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("nvidia"); diff --git a/extensions/ollama/index.test.ts b/extensions/ollama/index.test.ts index 4e56156d3c0..942838e71f9 100644 --- a/extensions/ollama/index.test.ts +++ b/extensions/ollama/index.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js"; +import { createTestPluginApi } from "../../test/helpers/plugins/plugin-api.js"; import plugin from "./index.js"; const promptAndConfigureOllamaMock = vi.hoisted(() => diff --git a/extensions/ollama/provider-discovery.contract.test.ts b/extensions/ollama/provider-discovery.contract.test.ts index ddbbd4d089d..8d64efee731 100644 --- a/extensions/ollama/provider-discovery.contract.test.ts +++ b/extensions/ollama/provider-discovery.contract.test.ts @@ -1,3 +1,3 @@ -import { describeOllamaProviderDiscoveryContract } from "../../test/helpers/extensions/provider-discovery-contract.js"; +import { describeOllamaProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js"; describeOllamaProviderDiscoveryContract(); diff --git a/extensions/ollama/provider.contract.test.ts b/extensions/ollama/provider.contract.test.ts index 16908e6c086..3bf03039256 100644 --- a/extensions/ollama/provider.contract.test.ts +++ b/extensions/ollama/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("ollama"); diff --git a/extensions/ollama/runtime-api.ts b/extensions/ollama/runtime-api.ts index 10c6b31fc03..05156fab574 100644 --- a/extensions/ollama/runtime-api.ts +++ b/extensions/ollama/runtime-api.ts @@ -14,3 +14,9 @@ export { shouldInjectOllamaCompatNumCtx, wrapOllamaCompatNumCtx, } from "./src/stream.js"; +export { + createOllamaEmbeddingProvider, + DEFAULT_OLLAMA_EMBEDDING_MODEL, + type OllamaEmbeddingClient, + type OllamaEmbeddingProvider, +} from "./src/embedding-provider.js"; diff --git a/extensions/openai/index.test.ts b/extensions/openai/index.test.ts index 80fd25584ca..bc57932eaae 100644 --- a/extensions/openai/index.test.ts +++ b/extensions/openai/index.test.ts @@ -13,7 +13,7 @@ import type { ResolvedTtsConfig } from "../../src/tts/tts.js"; import { registerProviderPlugin, requireRegisteredProvider, -} from "../../test/helpers/extensions/provider-registration.js"; +} from "../../test/helpers/plugins/provider-registration.js"; import { buildOpenAIImageGenerationProvider } from "./image-generation-provider.js"; import plugin from "./index.js"; diff --git a/extensions/openai/plugin-registration.contract.test.ts b/extensions/openai/plugin-registration.contract.test.ts index 10e62535d1c..09239f3e827 100644 --- a/extensions/openai/plugin-registration.contract.test.ts +++ b/extensions/openai/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "openai", diff --git a/extensions/openai/provider-auth.contract.test.ts b/extensions/openai/provider-auth.contract.test.ts index 7956297223a..85c81e216ec 100644 --- a/extensions/openai/provider-auth.contract.test.ts +++ b/extensions/openai/provider-auth.contract.test.ts @@ -1,3 +1,3 @@ -import { describeOpenAICodexProviderAuthContract } from "../../test/helpers/extensions/provider-auth-contract.js"; +import { describeOpenAICodexProviderAuthContract } from "../../test/helpers/plugins/provider-auth-contract.js"; describeOpenAICodexProviderAuthContract(); diff --git a/extensions/openai/provider-catalog.contract.test.ts b/extensions/openai/provider-catalog.contract.test.ts index 26269eaaf7f..ed033acce84 100644 --- a/extensions/openai/provider-catalog.contract.test.ts +++ b/extensions/openai/provider-catalog.contract.test.ts @@ -1,3 +1,3 @@ -import { describeOpenAIProviderCatalogContract } from "../../test/helpers/extensions/provider-catalog-contract.js"; +import { describeOpenAIProviderCatalogContract } from "../../test/helpers/plugins/provider-catalog-contract.js"; describeOpenAIProviderCatalogContract(); diff --git a/extensions/openai/provider-runtime.contract.test.ts b/extensions/openai/provider-runtime.contract.test.ts index 6a52f4626d5..20ea550e0c1 100644 --- a/extensions/openai/provider-runtime.contract.test.ts +++ b/extensions/openai/provider-runtime.contract.test.ts @@ -1,3 +1,3 @@ -import { describeOpenAIProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js"; +import { describeOpenAIProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js"; describeOpenAIProviderRuntimeContract(); diff --git a/extensions/openai/provider.contract.test.ts b/extensions/openai/provider.contract.test.ts index 1da70496c2f..d123356c1d5 100644 --- a/extensions/openai/provider.contract.test.ts +++ b/extensions/openai/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("openai"); diff --git a/extensions/opencode-go/onboard.test.ts b/extensions/opencode-go/onboard.test.ts index 0273d09f6d3..ee02c748562 100644 --- a/extensions/opencode-go/onboard.test.ts +++ b/extensions/opencode-go/onboard.test.ts @@ -6,7 +6,7 @@ import { import { createConfigWithFallbacks, EXPECTED_FALLBACKS, -} from "../../test/helpers/extensions/onboard-config.js"; +} from "../../test/helpers/plugins/onboard-config.js"; import { applyOpencodeGoConfig, applyOpencodeGoProviderConfig } from "./onboard.js"; const MODEL_REF = "opencode-go/kimi-k2.5"; diff --git a/extensions/opencode-go/provider.contract.test.ts b/extensions/opencode-go/provider.contract.test.ts index 88d7ea494b9..773fbd5a4df 100644 --- a/extensions/opencode-go/provider.contract.test.ts +++ b/extensions/opencode-go/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("opencode-go"); diff --git a/extensions/opencode/onboard.test.ts b/extensions/opencode/onboard.test.ts index 7172e9831ad..07c6ba7d4a9 100644 --- a/extensions/opencode/onboard.test.ts +++ b/extensions/opencode/onboard.test.ts @@ -6,7 +6,7 @@ import { import { createConfigWithFallbacks, EXPECTED_FALLBACKS, -} from "../../test/helpers/extensions/onboard-config.js"; +} from "../../test/helpers/plugins/onboard-config.js"; import { applyOpencodeZenConfig, applyOpencodeZenProviderConfig } from "./onboard.js"; const MODEL_REF = "opencode/claude-opus-4-6"; diff --git a/extensions/opencode/provider.contract.test.ts b/extensions/opencode/provider.contract.test.ts index d0663081b41..a174babc8eb 100644 --- a/extensions/opencode/provider.contract.test.ts +++ b/extensions/opencode/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("opencode"); diff --git a/extensions/openrouter/index.test.ts b/extensions/openrouter/index.test.ts index 248f728bf87..3de534486bb 100644 --- a/extensions/openrouter/index.test.ts +++ b/extensions/openrouter/index.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest"; import { registerProviderPlugin, requireRegisteredProvider, -} from "../../test/helpers/extensions/provider-registration.js"; +} from "../../test/helpers/plugins/provider-registration.js"; import plugin from "./index.js"; const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY ?? ""; diff --git a/extensions/openrouter/onboard.test.ts b/extensions/openrouter/onboard.test.ts index 493aa37d62a..b5a5574dec9 100644 --- a/extensions/openrouter/onboard.test.ts +++ b/extensions/openrouter/onboard.test.ts @@ -6,7 +6,7 @@ import { import { createConfigWithFallbacks, EXPECTED_FALLBACKS, -} from "../../test/helpers/extensions/onboard-config.js"; +} from "../../test/helpers/plugins/onboard-config.js"; import { applyOpenrouterConfig, applyOpenrouterProviderConfig, diff --git a/extensions/openrouter/plugin-registration.contract.test.ts b/extensions/openrouter/plugin-registration.contract.test.ts index 029ea7a91d2..855820e9278 100644 --- a/extensions/openrouter/plugin-registration.contract.test.ts +++ b/extensions/openrouter/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "openrouter", diff --git a/extensions/openrouter/provider-runtime.contract.test.ts b/extensions/openrouter/provider-runtime.contract.test.ts index 20bd4789c78..7062f4fb09c 100644 --- a/extensions/openrouter/provider-runtime.contract.test.ts +++ b/extensions/openrouter/provider-runtime.contract.test.ts @@ -1,3 +1,3 @@ -import { describeOpenRouterProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js"; +import { describeOpenRouterProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js"; describeOpenRouterProviderRuntimeContract(); diff --git a/extensions/openrouter/provider.contract.test.ts b/extensions/openrouter/provider.contract.test.ts index 8aad4caf208..c814cccc194 100644 --- a/extensions/openrouter/provider.contract.test.ts +++ b/extensions/openrouter/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("openrouter"); diff --git a/extensions/perplexity/bundled-web-search.contract.test.ts b/extensions/perplexity/bundled-web-search.contract.test.ts index b6d4f3e6842..f4b147f1556 100644 --- a/extensions/perplexity/bundled-web-search.contract.test.ts +++ b/extensions/perplexity/bundled-web-search.contract.test.ts @@ -1,3 +1,3 @@ -import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js"; +import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js"; describeBundledWebSearchFastPathContract("perplexity"); diff --git a/extensions/perplexity/plugin-registration.contract.test.ts b/extensions/perplexity/plugin-registration.contract.test.ts index 50fb424537a..03fbdda199a 100644 --- a/extensions/perplexity/plugin-registration.contract.test.ts +++ b/extensions/perplexity/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "perplexity", diff --git a/extensions/perplexity/src/perplexity-web-search-provider.test.ts b/extensions/perplexity/src/perplexity-web-search-provider.test.ts index b4d5290288e..35ff72302dc 100644 --- a/extensions/perplexity/src/perplexity-web-search-provider.test.ts +++ b/extensions/perplexity/src/perplexity-web-search-provider.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { withEnv } from "../../../test/helpers/extensions/env.js"; +import { withEnv } from "../../../test/helpers/plugins/env.js"; import { __testing } from "./perplexity-web-search-provider.js"; const openRouterApiKeyEnv = ["OPENROUTER_API", "KEY"].join("_"); diff --git a/extensions/perplexity/web-search-provider.contract.test.ts b/extensions/perplexity/web-search-provider.contract.test.ts index 8616f446d19..3dedbaa1638 100644 --- a/extensions/perplexity/web-search-provider.contract.test.ts +++ b/extensions/perplexity/web-search-provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js"; +import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js"; describeWebSearchProviderContracts("perplexity"); diff --git a/extensions/phone-control/index.test.ts b/extensions/phone-control/index.test.ts index bee1069977a..51bc7e283ab 100644 --- a/extensions/phone-control/index.test.ts +++ b/extensions/phone-control/index.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; -import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js"; +import { createTestPluginApi } from "../../test/helpers/plugins/plugin-api.js"; import registerPhoneControl from "./index.js"; import type { OpenClawPluginApi, diff --git a/extensions/qianfan/provider.contract.test.ts b/extensions/qianfan/provider.contract.test.ts index 1e64f6d5b1d..1b385b5b20f 100644 --- a/extensions/qianfan/provider.contract.test.ts +++ b/extensions/qianfan/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("qianfan"); diff --git a/extensions/sglang/provider-discovery.contract.test.ts b/extensions/sglang/provider-discovery.contract.test.ts index c6521b125b9..03d78730ab9 100644 --- a/extensions/sglang/provider-discovery.contract.test.ts +++ b/extensions/sglang/provider-discovery.contract.test.ts @@ -1,3 +1,3 @@ -import { describeSglangProviderDiscoveryContract } from "../../test/helpers/extensions/provider-discovery-contract.js"; +import { describeSglangProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js"; describeSglangProviderDiscoveryContract(); diff --git a/extensions/sglang/provider.contract.test.ts b/extensions/sglang/provider.contract.test.ts index fcffdba5631..4d3dae82ec5 100644 --- a/extensions/sglang/provider.contract.test.ts +++ b/extensions/sglang/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("sglang"); diff --git a/extensions/slack/package-manifest.contract.test.ts b/extensions/slack/package-manifest.contract.test.ts index 5f44025fab4..d548fe4e753 100644 --- a/extensions/slack/package-manifest.contract.test.ts +++ b/extensions/slack/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "slack", diff --git a/extensions/slack/src/channel.test.ts b/extensions/slack/src/channel.test.ts index b05c2db6772..aa5b64cc7bb 100644 --- a/extensions/slack/src/channel.test.ts +++ b/extensions/slack/src/channel.test.ts @@ -1,6 +1,6 @@ import { Type } from "@sinclair/typebox"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import { slackPlugin } from "./channel.js"; import { slackOutbound } from "./outbound-adapter.js"; import * as probeModule from "./probe.js"; diff --git a/extensions/slack/src/monitor/media.test.ts b/extensions/slack/src/monitor/media.test.ts index 9ac0bc0eeb1..e434195c732 100644 --- a/extensions/slack/src/monitor/media.test.ts +++ b/extensions/slack/src/monitor/media.test.ts @@ -7,7 +7,7 @@ import { mockPinnedHostnameResolution } from "../../../../src/test-helpers/ssrf. import { type FetchMock, withFetchPreconnect, -} from "../../../../test/helpers/extensions/fetch-mock.js"; +} from "../../../../test/helpers/plugins/fetch-mock.js"; import { fetchWithSlackAuth, resolveSlackAttachmentContent, diff --git a/extensions/slack/src/setup-surface.test.ts b/extensions/slack/src/setup-surface.test.ts index acd8fcf4004..0707099e338 100644 --- a/extensions/slack/src/setup-surface.test.ts +++ b/extensions/slack/src/setup-surface.test.ts @@ -4,7 +4,7 @@ import { createTestWizardPrompter, runSetupWizardFinalize, type WizardPrompter, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import { slackSetupWizard } from "./setup-surface.js"; describe("slackSetupWizard.finalize", () => { diff --git a/extensions/synology-chat/package-manifest.contract.test.ts b/extensions/synology-chat/package-manifest.contract.test.ts index bbfc5b8971d..19ef76c1777 100644 --- a/extensions/synology-chat/package-manifest.contract.test.ts +++ b/extensions/synology-chat/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "synology-chat", diff --git a/extensions/synology-chat/package.json b/extensions/synology-chat/package.json index 2aec5272762..b873d94d01b 100644 --- a/extensions/synology-chat/package.json +++ b/extensions/synology-chat/package.json @@ -19,7 +19,6 @@ }, "install": { "npmSpec": "@openclaw/synology-chat", - "localPath": "extensions/synology-chat", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" } diff --git a/extensions/synology-chat/src/core.test.ts b/extensions/synology-chat/src/core.test.ts index e206a0a8142..43efd2e6b5a 100644 --- a/extensions/synology-chat/src/core.test.ts +++ b/extensions/synology-chat/src/core.test.ts @@ -5,7 +5,7 @@ import { createTestWizardPrompter, runSetupWizardConfigure, type WizardPrompter, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import { listAccountIds, resolveAccount } from "./accounts.js"; import { synologyChatPlugin } from "./channel.js"; import { SynologyChatChannelConfigSchema } from "./config-schema.js"; diff --git a/extensions/synthetic/onboard.test.ts b/extensions/synthetic/onboard.test.ts index 9b1e07f269a..befda764141 100644 --- a/extensions/synthetic/onboard.test.ts +++ b/extensions/synthetic/onboard.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { SYNTHETIC_DEFAULT_MODEL_ID } from "../../src/agents/synthetic-models.js"; import { resolveAgentModelPrimaryValue } from "../../src/config/model-input.js"; -import { createLegacyProviderConfig } from "../../test/helpers/extensions/onboard-config.js"; +import { createLegacyProviderConfig } from "../../test/helpers/plugins/onboard-config.js"; import { applySyntheticConfig, applySyntheticProviderConfig, diff --git a/extensions/synthetic/provider.contract.test.ts b/extensions/synthetic/provider.contract.test.ts index 27f1688cc89..e2d6d76c3d2 100644 --- a/extensions/synthetic/provider.contract.test.ts +++ b/extensions/synthetic/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("synthetic"); diff --git a/extensions/talk-voice/index.test.ts b/extensions/talk-voice/index.test.ts index 050a6efd158..cca3b055691 100644 --- a/extensions/talk-voice/index.test.ts +++ b/extensions/talk-voice/index.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import type { OpenClawPluginCommandDefinition } from "../../test/helpers/extensions/plugin-command.js"; -import { createPluginRuntimeMock } from "../../test/helpers/extensions/plugin-runtime-mock.js"; +import type { OpenClawPluginCommandDefinition } from "../../test/helpers/plugins/plugin-command.js"; +import { createPluginRuntimeMock } from "../../test/helpers/plugins/plugin-runtime-mock.js"; import register from "./index.js"; function createHarness(config: Record) { diff --git a/extensions/tavily/bundled-web-search.contract.test.ts b/extensions/tavily/bundled-web-search.contract.test.ts index 2fdcf4f44f6..3785494363f 100644 --- a/extensions/tavily/bundled-web-search.contract.test.ts +++ b/extensions/tavily/bundled-web-search.contract.test.ts @@ -1,3 +1,3 @@ -import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js"; +import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js"; describeBundledWebSearchFastPathContract("tavily"); diff --git a/extensions/tavily/plugin-registration.contract.test.ts b/extensions/tavily/plugin-registration.contract.test.ts index 0f7ba946a0d..91e89305e92 100644 --- a/extensions/tavily/plugin-registration.contract.test.ts +++ b/extensions/tavily/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "tavily", diff --git a/extensions/tavily/web-search-provider.contract.test.ts b/extensions/tavily/web-search-provider.contract.test.ts index 5b69dabaca9..d0b37e73d4a 100644 --- a/extensions/tavily/web-search-provider.contract.test.ts +++ b/extensions/tavily/web-search-provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js"; +import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js"; describeWebSearchProviderContracts("tavily"); diff --git a/extensions/telegram/package-manifest.contract.test.ts b/extensions/telegram/package-manifest.contract.test.ts index 5dbb3892a34..df267e3e26b 100644 --- a/extensions/telegram/package-manifest.contract.test.ts +++ b/extensions/telegram/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "telegram", diff --git a/extensions/telegram/src/account-inspect.test.ts b/extensions/telegram/src/account-inspect.test.ts index 735cf4e53bb..f4f0e7ef6e9 100644 --- a/extensions/telegram/src/account-inspect.test.ts +++ b/extensions/telegram/src/account-inspect.test.ts @@ -3,7 +3,7 @@ import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../../../src/config/config.js"; -import { withEnv } from "../../../test/helpers/extensions/env.js"; +import { withEnv } from "../../../test/helpers/plugins/env.js"; import { inspectTelegramAccount } from "./account-inspect.js"; describe("inspectTelegramAccount SecretRef resolution", () => { diff --git a/extensions/telegram/src/accounts.test.ts b/extensions/telegram/src/accounts.test.ts index ae8a56c66cf..b53d2088eed 100644 --- a/extensions/telegram/src/accounts.test.ts +++ b/extensions/telegram/src/accounts.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../../src/config/config.js"; import * as subsystemModule from "../../../src/logging/subsystem.js"; -import { withEnv } from "../../../test/helpers/extensions/env.js"; +import { withEnv } from "../../../test/helpers/plugins/env.js"; import { listTelegramAccountIds, resetMissingDefaultWarnFlag, diff --git a/extensions/telegram/src/action-runtime.test.ts b/extensions/telegram/src/action-runtime.test.ts index ddcf7c2854a..797da2c5cbe 100644 --- a/extensions/telegram/src/action-runtime.test.ts +++ b/extensions/telegram/src/action-runtime.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../../src/config/config.js"; -import { captureEnv } from "../../../test/helpers/extensions/env.js"; +import { captureEnv } from "../../../test/helpers/plugins/env.js"; import { handleTelegramAction, readTelegramButtons, diff --git a/extensions/telegram/src/bot-native-commands.skills-allowlist.test.ts b/extensions/telegram/src/bot-native-commands.skills-allowlist.test.ts index 10f0e95bdb8..bbff5ec85bb 100644 --- a/extensions/telegram/src/bot-native-commands.skills-allowlist.test.ts +++ b/extensions/telegram/src/bot-native-commands.skills-allowlist.test.ts @@ -7,7 +7,7 @@ import type { OpenClawConfig } from "../../../src/config/config.js"; import { pluginCommandMocks, resetPluginCommandMocks, -} from "../../../test/helpers/extensions/telegram-plugin-command.js"; +} from "../../../test/helpers/plugins/telegram-plugin-command.js"; import { registerTelegramNativeCommands } from "./bot-native-commands.js"; import { createNativeCommandTestParams, diff --git a/extensions/telegram/src/bot-native-commands.test.ts b/extensions/telegram/src/bot-native-commands.test.ts index fab8976cff1..abaed79a54d 100644 --- a/extensions/telegram/src/bot-native-commands.test.ts +++ b/extensions/telegram/src/bot-native-commands.test.ts @@ -7,7 +7,7 @@ import type { RuntimeEnv } from "../../../src/runtime.js"; import { pluginCommandMocks, resetPluginCommandMocks, -} from "../../../test/helpers/extensions/telegram-plugin-command.js"; +} from "../../../test/helpers/plugins/telegram-plugin-command.js"; let registerTelegramNativeCommands: typeof import("./bot-native-commands.js").registerTelegramNativeCommands; import { diff --git a/extensions/telegram/src/bot.create-telegram-bot.channel-post-media.test.ts b/extensions/telegram/src/bot.create-telegram-bot.channel-post-media.test.ts index 8520254da70..6234ed811ef 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.channel-post-media.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.channel-post-media.test.ts @@ -1,5 +1,5 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { useFrozenTime, useRealTime } from "../../../test/helpers/extensions/frozen-time.js"; +import { useFrozenTime, useRealTime } from "../../../test/helpers/plugins/frozen-time.js"; const harness = await import("./bot.create-telegram-bot.test-harness.js"); const { diff --git a/extensions/telegram/src/bot.create-telegram-bot.test.ts b/extensions/telegram/src/bot.create-telegram-bot.test.ts index 8709453bb60..5fbdc50a9be 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test.ts @@ -4,8 +4,8 @@ import path from "node:path"; import type { GetReplyOptions, MsgContext } from "openclaw/plugin-sdk/reply-runtime"; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js"; -import { withEnvAsync } from "../../../test/helpers/extensions/env.js"; -import { useFrozenTime, useRealTime } from "../../../test/helpers/extensions/frozen-time.js"; +import { withEnvAsync } from "../../../test/helpers/plugins/env.js"; +import { useFrozenTime, useRealTime } from "../../../test/helpers/plugins/frozen-time.js"; const harness = await import("./bot.create-telegram-bot.test-harness.js"); const { answerCallbackQuerySpy, diff --git a/extensions/telegram/src/channel.test.ts b/extensions/telegram/src/channel.test.ts index 8186557a3b9..2fe7ac10dc8 100644 --- a/extensions/telegram/src/channel.test.ts +++ b/extensions/telegram/src/channel.test.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../../src/config/config.js"; import type { PluginApprovalRequest } from "../../../src/infra/plugin-approvals.js"; import type { PluginRuntime } from "../../../src/plugins/runtime/types.js"; -import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js"; +import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js"; import type { ResolvedTelegramAccount } from "./accounts.js"; import * as auditModule from "./audit.js"; import { telegramPlugin } from "./channel.js"; diff --git a/extensions/telegram/src/probe.test.ts b/extensions/telegram/src/probe.test.ts index 4b39f36f0ec..aeb82998a7a 100644 --- a/extensions/telegram/src/probe.test.ts +++ b/extensions/telegram/src/probe.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeAll, beforeEach, type Mock, describe, expect, it, vi } from "vitest"; -import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js"; +import { withFetchPreconnect } from "../../../test/helpers/plugins/fetch-mock.js"; const resolveTelegramFetch = vi.hoisted(() => vi.fn()); const makeProxyFetch = vi.hoisted(() => vi.fn()); diff --git a/extensions/telegram/src/setup-surface.test.ts b/extensions/telegram/src/setup-surface.test.ts index a218af37ceb..db668937a86 100644 --- a/extensions/telegram/src/setup-surface.test.ts +++ b/extensions/telegram/src/setup-surface.test.ts @@ -5,7 +5,7 @@ import { createTestWizardPrompter, runSetupWizardFinalize, runSetupWizardPrepare, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import { resolveTelegramAllowFromEntries } from "./setup-core.js"; import { telegramSetupWizard } from "./setup-surface.js"; diff --git a/extensions/tlon/package-manifest.contract.test.ts b/extensions/tlon/package-manifest.contract.test.ts index 97ebf33f84c..f3083796ec4 100644 --- a/extensions/tlon/package-manifest.contract.test.ts +++ b/extensions/tlon/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "tlon", diff --git a/extensions/tlon/package.json b/extensions/tlon/package.json index ca167ad253c..cdd11e1c7a3 100644 --- a/extensions/tlon/package.json +++ b/extensions/tlon/package.json @@ -37,7 +37,6 @@ }, "install": { "npmSpec": "@openclaw/tlon", - "localPath": "extensions/tlon", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" } diff --git a/extensions/tlon/src/core.test.ts b/extensions/tlon/src/core.test.ts index 29360d220be..3ce4d10da1d 100644 --- a/extensions/tlon/src/core.test.ts +++ b/extensions/tlon/src/core.test.ts @@ -4,7 +4,7 @@ import { createTestWizardPrompter, runSetupWizardConfigure, type WizardPrompter, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import type { OpenClawConfig } from "../api.js"; import { tlonPlugin } from "./channel.js"; import { TlonAuthorizationSchema, TlonConfigSchema } from "./config-schema.js"; diff --git a/extensions/together/provider.contract.test.ts b/extensions/together/provider.contract.test.ts index 0064291d7a8..9a8605ba0a1 100644 --- a/extensions/together/provider.contract.test.ts +++ b/extensions/together/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("together"); diff --git a/extensions/twitch/README.md b/extensions/twitch/README.md index 2043094f760..0d5aad8fcf6 100644 --- a/extensions/twitch/README.md +++ b/extensions/twitch/README.md @@ -5,7 +5,7 @@ Twitch channel plugin for OpenClaw. ## Install (local checkout) ```bash -openclaw plugins install ./extensions/twitch +openclaw plugins install ./path/to/local/twitch-plugin ``` ## Install (npm) diff --git a/extensions/twitch/package-manifest.contract.test.ts b/extensions/twitch/package-manifest.contract.test.ts index b862514614c..8bf9120c232 100644 --- a/extensions/twitch/package-manifest.contract.test.ts +++ b/extensions/twitch/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "twitch", diff --git a/extensions/venice/provider-runtime.contract.test.ts b/extensions/venice/provider-runtime.contract.test.ts index 54e30c2cf51..5f4e6abccbf 100644 --- a/extensions/venice/provider-runtime.contract.test.ts +++ b/extensions/venice/provider-runtime.contract.test.ts @@ -1,3 +1,3 @@ -import { describeVeniceProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js"; +import { describeVeniceProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js"; describeVeniceProviderRuntimeContract(); diff --git a/extensions/venice/provider.contract.test.ts b/extensions/venice/provider.contract.test.ts index 8a6ec60fa52..d28ceab9bcd 100644 --- a/extensions/venice/provider.contract.test.ts +++ b/extensions/venice/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("venice"); diff --git a/extensions/vercel-ai-gateway/provider.contract.test.ts b/extensions/vercel-ai-gateway/provider.contract.test.ts index 7359a8960d2..ac8612f50c1 100644 --- a/extensions/vercel-ai-gateway/provider.contract.test.ts +++ b/extensions/vercel-ai-gateway/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("vercel-ai-gateway"); diff --git a/extensions/vllm/provider-discovery.contract.test.ts b/extensions/vllm/provider-discovery.contract.test.ts index a3cf06ca5ba..f0169d54eb4 100644 --- a/extensions/vllm/provider-discovery.contract.test.ts +++ b/extensions/vllm/provider-discovery.contract.test.ts @@ -1,3 +1,3 @@ -import { describeVllmProviderDiscoveryContract } from "../../test/helpers/extensions/provider-discovery-contract.js"; +import { describeVllmProviderDiscoveryContract } from "../../test/helpers/plugins/provider-discovery-contract.js"; describeVllmProviderDiscoveryContract(); diff --git a/extensions/vllm/provider.contract.test.ts b/extensions/vllm/provider.contract.test.ts index 980ae364f0b..15a9a0e84a6 100644 --- a/extensions/vllm/provider.contract.test.ts +++ b/extensions/vllm/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("vllm"); diff --git a/extensions/voice-call/README.md b/extensions/voice-call/README.md index d885daca5f3..1bffa9539cd 100644 --- a/extensions/voice-call/README.md +++ b/extensions/voice-call/README.md @@ -25,9 +25,10 @@ Restart the Gateway afterwards. ### Option B: copy into your global extensions folder (dev) ```bash -mkdir -p ~/.openclaw/extensions -cp -R extensions/voice-call ~/.openclaw/extensions/voice-call -cd ~/.openclaw/extensions/voice-call && pnpm install +PLUGIN_HOME=~/.openclaw/extensions +mkdir -p "$PLUGIN_HOME" +cp -R "$PLUGIN_HOME/voice-call" +cd "$PLUGIN_HOME/voice-call" && pnpm install ``` ## Config diff --git a/extensions/voice-call/package-manifest.contract.test.ts b/extensions/voice-call/package-manifest.contract.test.ts index d932244267a..ddd11c6920f 100644 --- a/extensions/voice-call/package-manifest.contract.test.ts +++ b/extensions/voice-call/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "voice-call", diff --git a/extensions/volcengine/provider.contract.test.ts b/extensions/volcengine/provider.contract.test.ts index 7923b1d1eeb..b3da1a812d4 100644 --- a/extensions/volcengine/provider.contract.test.ts +++ b/extensions/volcengine/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("volcengine"); diff --git a/extensions/whatsapp/constants.ts b/extensions/whatsapp/constants.ts new file mode 100644 index 00000000000..58128e15e6e --- /dev/null +++ b/extensions/whatsapp/constants.ts @@ -0,0 +1 @@ +export { DEFAULT_WEB_MEDIA_BYTES } from "./src/auto-reply/constants.js"; diff --git a/extensions/whatsapp/package-manifest.contract.test.ts b/extensions/whatsapp/package-manifest.contract.test.ts index 77b44d61b19..f9635ed52c2 100644 --- a/extensions/whatsapp/package-manifest.contract.test.ts +++ b/extensions/whatsapp/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "whatsapp", diff --git a/extensions/whatsapp/package.json b/extensions/whatsapp/package.json index fd3a1f10daa..ca678c62534 100644 --- a/extensions/whatsapp/package.json +++ b/extensions/whatsapp/package.json @@ -35,7 +35,6 @@ }, "install": { "npmSpec": "@openclaw/whatsapp", - "localPath": "extensions/whatsapp", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/whatsapp/src/accounts.whatsapp-auth.test.ts b/extensions/whatsapp/src/accounts.whatsapp-auth.test.ts index 9926b3c5324..b82ad57985c 100644 --- a/extensions/whatsapp/src/accounts.whatsapp-auth.test.ts +++ b/extensions/whatsapp/src/accounts.whatsapp-auth.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { captureEnv } from "../../../test/helpers/extensions/env.js"; +import { captureEnv } from "../../../test/helpers/plugins/env.js"; import { hasAnyWhatsAppAuth, listWhatsAppAuthDirs } from "./accounts.js"; describe("hasAnyWhatsAppAuth", () => { diff --git a/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts b/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts index 5ec6f9145fd..a2285391f43 100644 --- a/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts +++ b/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts @@ -5,7 +5,7 @@ import { beforeAll, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../../src/config/config.js"; import { setLoggerOverride } from "../../../src/logging.js"; import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js"; -import { withEnvAsync } from "../../../test/helpers/extensions/env.js"; +import { withEnvAsync } from "../../../test/helpers/plugins/env.js"; import { createWebInboundDeliverySpies, createMockWebListener, diff --git a/extensions/whatsapp/src/auto-reply/web-auto-reply-utils.test.ts b/extensions/whatsapp/src/auto-reply/web-auto-reply-utils.test.ts index 7432196d182..b2cf0a5b6a2 100644 --- a/extensions/whatsapp/src/auto-reply/web-auto-reply-utils.test.ts +++ b/extensions/whatsapp/src/auto-reply/web-auto-reply-utils.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { saveSessionStore } from "../../../../src/config/sessions.js"; -import { withTempDir } from "../../../../test/helpers/extensions/temp-dir.js"; +import { withTempDir } from "../../../../test/helpers/plugins/temp-dir.js"; import { debugMention, isBotMentionedFromTargets, diff --git a/extensions/whatsapp/src/channel.test.ts b/extensions/whatsapp/src/channel.test.ts index 3ca720a5b77..8bea552b46b 100644 --- a/extensions/whatsapp/src/channel.test.ts +++ b/extensions/whatsapp/src/channel.test.ts @@ -8,12 +8,12 @@ import { import { createDirectoryTestRuntime, expectDirectorySurface, -} from "../../../test/helpers/extensions/directory.ts"; +} from "../../../test/helpers/plugins/directory.ts"; import { createPluginSetupWizardConfigure, createQueuedWizardPrompter, runSetupWizardConfigure, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import { whatsappPlugin } from "./channel.js"; import { resolveWhatsAppGroupRequireMention, diff --git a/extensions/whatsapp/src/media.test.ts b/extensions/whatsapp/src/media.test.ts index 3325ed70370..66acdc60050 100644 --- a/extensions/whatsapp/src/media.test.ts +++ b/extensions/whatsapp/src/media.test.ts @@ -7,7 +7,7 @@ import { resolveStateDir } from "../../../src/config/paths.js"; import { resolvePreferredOpenClawTmpDir } from "../../../src/infra/tmp-openclaw-dir.js"; import { optimizeImageToPng } from "../../../src/media/image-ops.js"; import { mockPinnedHostnameResolution } from "../../../src/test-helpers/ssrf.js"; -import { captureEnv } from "../../../test/helpers/extensions/env.js"; +import { captureEnv } from "../../../test/helpers/plugins/env.js"; let LocalMediaAccessError: typeof import("./media.js").LocalMediaAccessError; let loadWebMedia: typeof import("./media.js").loadWebMedia; diff --git a/extensions/xai/bundled-web-search.contract.test.ts b/extensions/xai/bundled-web-search.contract.test.ts index 87096e3fd6a..b60775ee57d 100644 --- a/extensions/xai/bundled-web-search.contract.test.ts +++ b/extensions/xai/bundled-web-search.contract.test.ts @@ -1,3 +1,3 @@ -import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js"; +import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js"; describeBundledWebSearchFastPathContract("xai"); diff --git a/extensions/xai/code-execution.test.ts b/extensions/xai/code-execution.test.ts index d26f7542ad7..e621e551377 100644 --- a/extensions/xai/code-execution.test.ts +++ b/extensions/xai/code-execution.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { withFetchPreconnect } from "../../test/helpers/extensions/fetch-mock.js"; +import { withFetchPreconnect } from "../../test/helpers/plugins/fetch-mock.js"; import { createCodeExecutionTool } from "./code-execution.js"; function installCodeExecutionFetch(payload?: Record) { diff --git a/extensions/xai/onboard.test.ts b/extensions/xai/onboard.test.ts index 450ed7fb06e..8163fce500d 100644 --- a/extensions/xai/onboard.test.ts +++ b/extensions/xai/onboard.test.ts @@ -7,7 +7,7 @@ import { createConfigWithFallbacks, createLegacyProviderConfig, EXPECTED_FALLBACKS, -} from "../../test/helpers/extensions/onboard-config.js"; +} from "../../test/helpers/plugins/onboard-config.js"; import { applyXaiConfig, applyXaiProviderConfig, XAI_DEFAULT_MODEL_REF } from "./onboard.js"; describe("xai onboard", () => { diff --git a/extensions/xai/plugin-registration.contract.test.ts b/extensions/xai/plugin-registration.contract.test.ts index eb17d5aeaf7..183b081990e 100644 --- a/extensions/xai/plugin-registration.contract.test.ts +++ b/extensions/xai/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "xai", diff --git a/extensions/xai/provider-runtime.contract.test.ts b/extensions/xai/provider-runtime.contract.test.ts index 81df0b7a4fe..70ff851088c 100644 --- a/extensions/xai/provider-runtime.contract.test.ts +++ b/extensions/xai/provider-runtime.contract.test.ts @@ -1,3 +1,3 @@ -import { describeXAIProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js"; +import { describeXAIProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js"; describeXAIProviderRuntimeContract(); diff --git a/extensions/xai/provider.contract.test.ts b/extensions/xai/provider.contract.test.ts index 265876ce5e7..b8005c0171b 100644 --- a/extensions/xai/provider.contract.test.ts +++ b/extensions/xai/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("xai"); diff --git a/extensions/xai/web-search-provider.contract.test.ts b/extensions/xai/web-search-provider.contract.test.ts index 78ca60244fd..2ea0b63ed59 100644 --- a/extensions/xai/web-search-provider.contract.test.ts +++ b/extensions/xai/web-search-provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js"; +import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js"; describeWebSearchProviderContracts("xai"); diff --git a/extensions/xai/web-search.test.ts b/extensions/xai/web-search.test.ts index 8abed3413de..59636d92a22 100644 --- a/extensions/xai/web-search.test.ts +++ b/extensions/xai/web-search.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest"; import { NON_ENV_SECRETREF_MARKER } from "../../src/agents/model-auth-markers.js"; import { capturePluginRegistration } from "../../src/plugins/captured-registration.js"; import { createNonExitingRuntime } from "../../src/runtime.js"; -import { withEnv } from "../../test/helpers/extensions/env.js"; +import { withEnv } from "../../test/helpers/plugins/env.js"; import { createWizardPrompter } from "../../test/helpers/wizard-prompter.js"; import xaiPlugin from "./index.js"; import { resolveXaiCatalogEntry } from "./model-definitions.js"; diff --git a/extensions/xai/x-search.test.ts b/extensions/xai/x-search.test.ts index d06a7d835be..c74bdde96d8 100644 --- a/extensions/xai/x-search.test.ts +++ b/extensions/xai/x-search.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { withFetchPreconnect } from "../../test/helpers/extensions/fetch-mock.js"; +import { withFetchPreconnect } from "../../test/helpers/plugins/fetch-mock.js"; import { createXSearchTool } from "./x-search.js"; function installXSearchFetch(payload?: Record) { diff --git a/extensions/xiaomi/onboard.test.ts b/extensions/xiaomi/onboard.test.ts index d7e39810e3b..0af93da77b9 100644 --- a/extensions/xiaomi/onboard.test.ts +++ b/extensions/xiaomi/onboard.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { resolveAgentModelPrimaryValue } from "../../src/config/model-input.js"; -import { createLegacyProviderConfig } from "../../test/helpers/extensions/onboard-config.js"; +import { createLegacyProviderConfig } from "../../test/helpers/plugins/onboard-config.js"; import { applyXiaomiConfig, applyXiaomiProviderConfig } from "./onboard.js"; describe("xiaomi onboard", () => { diff --git a/extensions/xiaomi/provider.contract.test.ts b/extensions/xiaomi/provider.contract.test.ts index f36675c83cc..bcba5f42cf0 100644 --- a/extensions/xiaomi/provider.contract.test.ts +++ b/extensions/xiaomi/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("xiaomi"); diff --git a/extensions/zai/plugin-registration.contract.test.ts b/extensions/zai/plugin-registration.contract.test.ts index 8991f6efd6d..d23601e662a 100644 --- a/extensions/zai/plugin-registration.contract.test.ts +++ b/extensions/zai/plugin-registration.contract.test.ts @@ -1,4 +1,4 @@ -import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js"; +import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js"; describePluginRegistrationContract({ pluginId: "zai", diff --git a/extensions/zai/provider-runtime.contract.test.ts b/extensions/zai/provider-runtime.contract.test.ts index 18f99d627e6..990c81e8d7a 100644 --- a/extensions/zai/provider-runtime.contract.test.ts +++ b/extensions/zai/provider-runtime.contract.test.ts @@ -1,3 +1,3 @@ -import { describeZAIProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js"; +import { describeZAIProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js"; describeZAIProviderRuntimeContract(); diff --git a/extensions/zai/provider.contract.test.ts b/extensions/zai/provider.contract.test.ts index e381dd6f4a1..d71302cfb33 100644 --- a/extensions/zai/provider.contract.test.ts +++ b/extensions/zai/provider.contract.test.ts @@ -1,3 +1,3 @@ -import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js"; +import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js"; describeProviderContracts("zai"); diff --git a/extensions/zalo/README.md b/extensions/zalo/README.md index 309569fa330..1d97c74d0a2 100644 --- a/extensions/zalo/README.md +++ b/extensions/zalo/README.md @@ -5,7 +5,7 @@ Zalo channel plugin for OpenClaw (Bot API). ## Install (local checkout) ```bash -openclaw plugins install ./extensions/zalo +openclaw plugins install ./path/to/local/zalo-plugin ``` ## Install (npm) diff --git a/extensions/zalo/package-manifest.contract.test.ts b/extensions/zalo/package-manifest.contract.test.ts index 39c69dbdc2d..6ad9bdcffce 100644 --- a/extensions/zalo/package-manifest.contract.test.ts +++ b/extensions/zalo/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "zalo", diff --git a/extensions/zalo/package.json b/extensions/zalo/package.json index fa6915837d5..8c584e66c88 100644 --- a/extensions/zalo/package.json +++ b/extensions/zalo/package.json @@ -37,7 +37,6 @@ }, "install": { "npmSpec": "@openclaw/zalo", - "localPath": "extensions/zalo", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/zalo/src/channel.directory.test.ts b/extensions/zalo/src/channel.directory.test.ts index e6254dd6be7..c461a350813 100644 --- a/extensions/zalo/src/channel.directory.test.ts +++ b/extensions/zalo/src/channel.directory.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import { createDirectoryTestRuntime, expectDirectorySurface, -} from "../../../test/helpers/extensions/directory.js"; +} from "../../../test/helpers/plugins/directory.js"; import type { OpenClawConfig, RuntimeEnv } from "../runtime-api.js"; import { zaloPlugin } from "./channel.js"; diff --git a/extensions/zalo/src/channel.startup.test.ts b/extensions/zalo/src/channel.startup.test.ts index e4d9bfb3ebc..617d08cf833 100644 --- a/extensions/zalo/src/channel.startup.test.ts +++ b/extensions/zalo/src/channel.startup.test.ts @@ -4,7 +4,7 @@ import { expectPendingUntilAbort, startAccountAndTrackLifecycle, waitForStartedMocks, -} from "../../../test/helpers/extensions/start-account-lifecycle.js"; +} from "../../../test/helpers/plugins/start-account-lifecycle.js"; import type { ResolvedZaloAccount } from "./accounts.js"; const hoisted = vi.hoisted(() => ({ diff --git a/extensions/zalo/src/monitor.image.polling.test.ts b/extensions/zalo/src/monitor.image.polling.test.ts index 9a3d5594f2f..867f969ebf1 100644 --- a/extensions/zalo/src/monitor.image.polling.test.ts +++ b/extensions/zalo/src/monitor.image.polling.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import { createImageLifecycleCore, createImageUpdate, @@ -9,7 +9,7 @@ import { getZaloRuntimeMock, resetLifecycleTestState, sendMessageMock, -} from "../../../test/helpers/extensions/zalo-lifecycle.js"; +} from "../../../test/helpers/plugins/zalo-lifecycle.js"; describe("Zalo polling image handling", () => { const { diff --git a/extensions/zalo/src/monitor.lifecycle.test.ts b/extensions/zalo/src/monitor.lifecycle.test.ts index 96ffc0daad3..6e22fbe9dd3 100644 --- a/extensions/zalo/src/monitor.lifecycle.test.ts +++ b/extensions/zalo/src/monitor.lifecycle.test.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { createEmptyPluginRegistry } from "../../../src/plugins/registry.js"; import { setActivePluginRegistry } from "../../../src/plugins/runtime.js"; -import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js"; +import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js"; import type { OpenClawConfig } from "../runtime-api.js"; import type { ResolvedZaloAccount } from "./accounts.js"; diff --git a/extensions/zalo/src/monitor.pairing.lifecycle.test.ts b/extensions/zalo/src/monitor.pairing.lifecycle.test.ts index 8a82e232687..5e7bea10037 100644 --- a/extensions/zalo/src/monitor.pairing.lifecycle.test.ts +++ b/extensions/zalo/src/monitor.pairing.lifecycle.test.ts @@ -1,4 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { withServer } from "../../../test/helpers/http-test-server.js"; import { createLifecycleMonitorSetup, createTextUpdate, @@ -8,8 +9,7 @@ import { sendMessageMock, settleAsyncWork, startWebhookLifecycleMonitor, -} from "../../../test/helpers/extensions/zalo-lifecycle.js"; -import { withServer } from "../../../test/helpers/http-test-server.js"; +} from "../../../test/helpers/plugins/zalo-lifecycle.js"; describe("Zalo pairing lifecycle", () => { const readAllowFromStoreMock = vi.fn(async () => [] as string[]); diff --git a/extensions/zalo/src/monitor.reply-once.lifecycle.test.ts b/extensions/zalo/src/monitor.reply-once.lifecycle.test.ts index ec59241f07c..d05a35bce6b 100644 --- a/extensions/zalo/src/monitor.reply-once.lifecycle.test.ts +++ b/extensions/zalo/src/monitor.reply-once.lifecycle.test.ts @@ -1,4 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { withServer } from "../../../test/helpers/http-test-server.js"; import { createLifecycleMonitorSetup, createTextUpdate, @@ -8,8 +9,7 @@ import { sendMessageMock, settleAsyncWork, startWebhookLifecycleMonitor, -} from "../../../test/helpers/extensions/zalo-lifecycle.js"; -import { withServer } from "../../../test/helpers/http-test-server.js"; +} from "../../../test/helpers/plugins/zalo-lifecycle.js"; import type { PluginRuntime } from "../runtime-api.js"; describe("Zalo reply-once lifecycle", () => { diff --git a/extensions/zalo/src/monitor.webhook.test.ts b/extensions/zalo/src/monitor.webhook.test.ts index 15b1cd96b7b..1ba94d5c142 100644 --- a/extensions/zalo/src/monitor.webhook.test.ts +++ b/extensions/zalo/src/monitor.webhook.test.ts @@ -2,14 +2,14 @@ import type { RequestListener } from "node:http"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createEmptyPluginRegistry } from "../../../src/plugins/registry.js"; import { setActivePluginRegistry } from "../../../src/plugins/runtime.js"; +import { withServer } from "../../../test/helpers/http-test-server.js"; import { createImageLifecycleCore, createImageUpdate, createTextUpdate, expectImageLifecycleDelivery, postWebhookReplay, -} from "../../../test/helpers/extensions/zalo-lifecycle.js"; -import { withServer } from "../../../test/helpers/http-test-server.js"; +} from "../../../test/helpers/plugins/zalo-lifecycle.js"; import type { OpenClawConfig, PluginRuntime } from "../runtime-api.js"; import { clearZaloWebhookSecurityStateForTest, diff --git a/extensions/zalo/src/setup-status.test.ts b/extensions/zalo/src/setup-status.test.ts index 93dd835dd7f..11db4432c7a 100644 --- a/extensions/zalo/src/setup-status.test.ts +++ b/extensions/zalo/src/setup-status.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { createPluginSetupWizardStatus } from "../../../test/helpers/extensions/setup-wizard.js"; +import { createPluginSetupWizardStatus } from "../../../test/helpers/plugins/setup-wizard.js"; import type { OpenClawConfig } from "../runtime-api.js"; import { zaloPlugin } from "./channel.js"; diff --git a/extensions/zalo/src/setup-surface.test.ts b/extensions/zalo/src/setup-surface.test.ts index ab5c1572e6c..b71f13ee690 100644 --- a/extensions/zalo/src/setup-surface.test.ts +++ b/extensions/zalo/src/setup-surface.test.ts @@ -4,7 +4,7 @@ import { createTestWizardPrompter, runSetupWizardConfigure, type WizardPrompter, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import type { OpenClawConfig } from "../runtime-api.js"; import { zaloPlugin } from "./channel.js"; diff --git a/extensions/zalo/src/status-issues.test.ts b/extensions/zalo/src/status-issues.test.ts index 1187d45a298..31767f40715 100644 --- a/extensions/zalo/src/status-issues.test.ts +++ b/extensions/zalo/src/status-issues.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { expectOpenDmPolicyConfigIssue } from "../../../test/helpers/extensions/status-issues.js"; +import { expectOpenDmPolicyConfigIssue } from "../../../test/helpers/plugins/status-issues.js"; import { collectZaloStatusIssues } from "./status-issues.js"; describe("collectZaloStatusIssues", () => { diff --git a/extensions/zalouser/README.md b/extensions/zalouser/README.md index ea55c343edb..59b3b254f98 100644 --- a/extensions/zalouser/README.md +++ b/extensions/zalouser/README.md @@ -30,8 +30,9 @@ openclaw plugins install @openclaw/zalouser ### Option B: local source checkout ```bash -openclaw plugins install ./extensions/zalouser -cd ./extensions/zalouser && pnpm install +PLUGIN_SRC=./path/to/local/zalouser-plugin +openclaw plugins install "$PLUGIN_SRC" +cd "$PLUGIN_SRC" && pnpm install ``` Restart the Gateway after install. diff --git a/extensions/zalouser/package-manifest.contract.test.ts b/extensions/zalouser/package-manifest.contract.test.ts index 867700f142c..ad3b2ebded0 100644 --- a/extensions/zalouser/package-manifest.contract.test.ts +++ b/extensions/zalouser/package-manifest.contract.test.ts @@ -1,4 +1,4 @@ -import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js"; +import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js"; describePackageManifestContract({ pluginId: "zalouser", diff --git a/extensions/zalouser/package.json b/extensions/zalouser/package.json index 4089d88fe26..e0c2cd2229b 100644 --- a/extensions/zalouser/package.json +++ b/extensions/zalouser/package.json @@ -38,7 +38,6 @@ }, "install": { "npmSpec": "@openclaw/zalouser", - "localPath": "extensions/zalouser", "defaultChoice": "npm", "minHostVersion": ">=2026.3.28" }, diff --git a/extensions/zalouser/src/channel.setup.test.ts b/extensions/zalouser/src/channel.setup.test.ts index 915b236dcb2..453c0aa63da 100644 --- a/extensions/zalouser/src/channel.setup.test.ts +++ b/extensions/zalouser/src/channel.setup.test.ts @@ -2,8 +2,8 @@ import { mkdtemp, rm } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withEnvAsync } from "../../../test/helpers/extensions/env.js"; -import { createPluginSetupWizardStatus } from "../../../test/helpers/extensions/setup-wizard.js"; +import { withEnvAsync } from "../../../test/helpers/plugins/env.js"; +import { createPluginSetupWizardStatus } from "../../../test/helpers/plugins/setup-wizard.js"; import "./zalo-js.test-mocks.js"; import { zalouserSetupPlugin } from "./channel.setup.js"; diff --git a/extensions/zalouser/src/setup-surface.test.ts b/extensions/zalouser/src/setup-surface.test.ts index 8666f4aa7f6..c707e64bb4e 100644 --- a/extensions/zalouser/src/setup-surface.test.ts +++ b/extensions/zalouser/src/setup-surface.test.ts @@ -3,7 +3,7 @@ import { createPluginSetupWizardConfigure, createTestWizardPrompter, runSetupWizardConfigure, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import type { OpenClawConfig } from "../runtime-api.js"; import "./zalo-js.test-mocks.js"; import { zalouserPlugin } from "./channel.js"; diff --git a/extensions/zalouser/src/status-issues.test.ts b/extensions/zalouser/src/status-issues.test.ts index bd1ae4d4cd4..70ca3e23e54 100644 --- a/extensions/zalouser/src/status-issues.test.ts +++ b/extensions/zalouser/src/status-issues.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { expectOpenDmPolicyConfigIssue } from "../../../test/helpers/extensions/status-issues.js"; +import { expectOpenDmPolicyConfigIssue } from "../../../test/helpers/plugins/status-issues.js"; import { collectZalouserStatusIssues } from "./status-issues.js"; describe("collectZalouserStatusIssues", () => { diff --git a/knip.config.ts b/knip.config.ts index 0d2db0e3715..3ce95ef39cb 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -1,11 +1,17 @@ +const BUNDLED_PLUGIN_ROOT_DIR = "extensions"; + +function bundledPluginFile(pluginId: string, relativePath: string, suffix = ""): string { + return `${BUNDLED_PLUGIN_ROOT_DIR}/${pluginId}/${relativePath}${suffix}`; +} + const rootEntries = [ "openclaw.mjs!", "src/index.ts!", "src/entry.ts!", "src/cli/daemon-cli.ts!", "src/infra/warning-filter.ts!", - "extensions/telegram/src/audit.ts!", - "extensions/telegram/src/token.ts!", + bundledPluginFile("telegram", "src/audit.ts", "!"), + bundledPluginFile("telegram", "src/token.ts", "!"), "src/hooks/bundled/*/handler.ts!", "src/hooks/llm-slug-generator.ts!", "src/plugin-sdk/*.ts!", @@ -61,12 +67,12 @@ const config = { "src/gateway/live-tool-probe-utils.ts", "src/gateway/server.auth.shared.ts", "src/shared/text/assistant-visible-text.ts", - "extensions/telegram/src/bot/reply-threading.ts", - "extensions/telegram/src/draft-chunking.ts", - "extensions/msteams/src/conversation-store-memory.ts", - "extensions/msteams/src/polls-store-memory.ts", - "extensions/voice-call/src/providers/index.ts", - "extensions/voice-call/src/providers/tts-openai.ts", + bundledPluginFile("telegram", "src/bot/reply-threading.ts"), + bundledPluginFile("telegram", "src/draft-chunking.ts"), + bundledPluginFile("msteams", "src/conversation-store-memory.ts"), + bundledPluginFile("msteams", "src/polls-store-memory.ts"), + bundledPluginFile("voice-call", "src/providers/index.ts"), + bundledPluginFile("voice-call", "src/providers/tts-openai.ts"), ], workspaces: { ".": { @@ -86,7 +92,7 @@ const config = { entry: ["index.js!", "scripts/postinstall.js!"], project: ["index.js!", "scripts/**/*.js!"], }, - "extensions/*": { + [`${BUNDLED_PLUGIN_ROOT_DIR}/*`]: { entry: ["index.ts!"], project: ["index.ts!", "src/**/*.ts!"], ignoreDependencies: ["openclaw"], diff --git a/package.json b/package.json index 943f1a0e52e..c5c1300d254 100644 --- a/package.json +++ b/package.json @@ -1017,7 +1017,6 @@ "check": "pnpm check:no-conflict-markers && pnpm check:host-env-policy:swift && pnpm tsgo && pnpm lint && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope", "check:base-config-schema": "node --import tsx scripts/generate-base-config-schema.ts --check", "check:bundled-channel-config-metadata": "node --import tsx scripts/generate-bundled-channel-config-metadata.ts --check", - "check:bundled-plugin-metadata": "node scripts/generate-bundled-plugin-metadata.mjs --check", "check:bundled-provider-auth-env-vars": "node scripts/generate-bundled-provider-auth-env-vars.mjs --check", "check:docs": "pnpm format:docs:check && pnpm lint:docs && pnpm docs:check-i18n-glossary && pnpm docs:check-links", "check:host-env-policy:swift": "node scripts/generate-host-env-security-policy-swift.mjs --check", @@ -1111,7 +1110,7 @@ "protocol:check": "pnpm protocol:gen && pnpm protocol:gen:swift && git diff --exit-code -- dist/protocol.schema.json apps/macos/Sources/OpenClawProtocol/GatewayModels.swift apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift", "protocol:gen": "node --import tsx scripts/protocol-gen.ts", "protocol:gen:swift": "node --import tsx scripts/protocol-gen-swift.ts", - "release:check": "pnpm check:base-config-schema && pnpm check:bundled-channel-config-metadata && pnpm check:bundled-plugin-metadata && pnpm check:bundled-provider-auth-env-vars && pnpm config:docs:check && pnpm plugin-sdk:check-exports && pnpm plugin-sdk:facades:check && pnpm plugin-sdk:api:check && node scripts/stage-bundled-plugin-runtime-deps.mjs && pnpm ui:build && node --import tsx scripts/release-check.ts", + "release:check": "pnpm check:base-config-schema && pnpm check:bundled-channel-config-metadata && pnpm check:bundled-provider-auth-env-vars && pnpm config:docs:check && pnpm plugin-sdk:check-exports && pnpm plugin-sdk:facades:check && pnpm plugin-sdk:api:check && node scripts/stage-bundled-plugin-runtime-deps.mjs && pnpm ui:build && node --import tsx scripts/release-check.ts", "release:openclaw:npm:check": "node --import tsx scripts/openclaw-npm-release-check.ts", "release:openclaw:npm:verify-published": "node --import tsx scripts/openclaw-npm-postpublish-verify.ts", "release:plugins:npm:check": "node --import tsx scripts/plugin-npm-release-check.ts", @@ -1175,7 +1174,7 @@ "test:serial": "node scripts/test-parallel.mjs --profile serial", "test:startup:memory": "node scripts/check-cli-startup-memory.mjs", "test:ui": "pnpm lint:ui:no-raw-window-open && pnpm --dir ui test", - "test:voicecall:closedloop": "vitest run extensions/voice-call/src/manager.test.ts extensions/voice-call/src/media-stream.test.ts src/plugins/voice-call.plugin.test.ts --maxWorkers=1", + "test:voicecall:closedloop": "node scripts/test-voicecall-closedloop.mjs", "test:watch": "vitest", "ts-topology": "node --import tsx scripts/ts-topology.ts", "tui": "node scripts/run-node.mjs tui", diff --git a/packages/memory-host-sdk/src/host/embeddings-ollama.ts b/packages/memory-host-sdk/src/host/embeddings-ollama.ts index 6c657b11844..d88c2b2c5b7 100644 --- a/packages/memory-host-sdk/src/host/embeddings-ollama.ts +++ b/packages/memory-host-sdk/src/host/embeddings-ollama.ts @@ -1,5 +1,5 @@ -export type { OllamaEmbeddingClient } from "../../../../extensions/ollama/src/embedding-provider.js"; +export type { OllamaEmbeddingClient } from "../../../../src/plugin-sdk/ollama.js"; export { createOllamaEmbeddingProvider, DEFAULT_OLLAMA_EMBEDDING_MODEL, -} from "../../../../extensions/ollama/src/embedding-provider.js"; +} from "../../../../src/plugin-sdk/ollama.js"; diff --git a/scripts/audit-seams.mjs b/scripts/audit-seams.mjs index 3e5bbaa17c6..c1147f26d66 100644 --- a/scripts/audit-seams.mjs +++ b/scripts/audit-seams.mjs @@ -4,11 +4,15 @@ import { promises as fs } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import ts from "typescript"; +import { + BUNDLED_PLUGIN_PATH_PREFIX, + BUNDLED_PLUGIN_ROOT_DIR, +} from "./lib/bundled-plugin-paths.mjs"; import { optionalBundledClusterSet } from "./lib/optional-bundled-clusters.mjs"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const srcRoot = path.join(repoRoot, "src"); -const extensionsRoot = path.join(repoRoot, "extensions"); +const extensionsRoot = path.join(repoRoot, BUNDLED_PLUGIN_ROOT_DIR); const testRoot = path.join(repoRoot, "test"); const workspacePackagePaths = ["ui/package.json"]; const MAX_SCAN_BYTES = 2 * 1024 * 1024; @@ -168,7 +172,7 @@ function normalizePluginSdkFamily(resolvedPath) { } function resolveOptionalClusterFromPath(resolvedPath) { - if (resolvedPath.startsWith("extensions/")) { + if (resolvedPath.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { const cluster = resolvedPath.split("/")[1]; return optionalBundledClusterSet.has(cluster) ? cluster : null; } @@ -426,7 +430,7 @@ function packageClusterMeta(relativePackagePath) { cluster, packageName: null, packagePath: relativePackagePath, - reachability: relativePackagePath.startsWith("extensions/") + reachability: relativePackagePath.startsWith(BUNDLED_PLUGIN_PATH_PREFIX) ? "extension-workspace" : "workspace", }; @@ -777,7 +781,7 @@ export function describeSeamKinds(relativePath, source) { relativePath, ); const isChannelMediaAdapterPath = - (relativePath.startsWith("extensions/") && + (relativePath.startsWith(BUNDLED_PLUGIN_PATH_PREFIX) && /(outbound|outbound-adapter|reply-delivery|send|delivery|messenger|channel(?:\.runtime)?)\.ts$/.test( relativePath, )) || diff --git a/scripts/check-architecture-smells.mjs b/scripts/check-architecture-smells.mjs index 3ca4df4d049..c586be45ed1 100644 --- a/scripts/check-architecture-smells.mjs +++ b/scripts/check-architecture-smells.mjs @@ -3,6 +3,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import ts from "typescript"; +import { BUNDLED_PLUGIN_PATH_PREFIX } from "./lib/bundled-plugin-paths.mjs"; import { collectTypeScriptInventory, normalizeRepoPath, @@ -52,7 +53,7 @@ function scanPluginSdkExtensionFacadeSmells(sourceFile, filePath) { return; } const resolvedPath = resolveRepoSpecifier(repoRoot, specifier, filePath); - if (!resolvedPath?.startsWith("extensions/")) { + if (!resolvedPath?.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { return; } pushEntry(entries, { diff --git a/scripts/check-extension-plugin-sdk-boundary.mjs b/scripts/check-extension-plugin-sdk-boundary.mjs index c235c7a9823..2c34dda33ff 100644 --- a/scripts/check-extension-plugin-sdk-boundary.mjs +++ b/scripts/check-extension-plugin-sdk-boundary.mjs @@ -4,6 +4,10 @@ import { promises as fs } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import ts from "typescript"; +import { + BUNDLED_PLUGIN_PATH_PREFIX, + BUNDLED_PLUGIN_ROOT_DIR, +} from "./lib/bundled-plugin-paths.mjs"; import { diffInventoryEntries, normalizeRepoPath, @@ -14,7 +18,7 @@ import { import { toLine } from "./lib/ts-guard-utils.mjs"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); -const extensionsRoot = path.join(repoRoot, "extensions"); +const extensionsRoot = path.join(repoRoot, BUNDLED_PLUGIN_ROOT_DIR); const MODES = new Set([ "src-outside-plugin-sdk", @@ -48,11 +52,11 @@ let parsedExtensionSourceFilesPromise; const ruleTextByMode = { "src-outside-plugin-sdk": - "Rule: production extensions/** must not import src/** outside src/plugin-sdk/**", + "Rule: production bundled plugins must not import src/** outside src/plugin-sdk/**", "plugin-sdk-internal": - "Rule: production extensions/** must not import src/plugin-sdk-internal/**", + "Rule: production bundled plugins must not import src/plugin-sdk-internal/**", "relative-outside-package": - "Rule: production extensions/** must not use relative imports that escape their own extension package root", + "Rule: production bundled plugins must not use relative imports that escape their own package root", }; function isCodeFile(fileName) { @@ -127,7 +131,7 @@ async function collectParsedExtensionSourceFiles() { function resolveExtensionRoot(filePath) { const relativePath = normalizeRepoPath(repoRoot, filePath); const segments = relativePath.split("/"); - if (segments[0] !== "extensions" || !segments[1]) { + if (segments[0] !== BUNDLED_PLUGIN_ROOT_DIR || !segments[1]) { return null; } return `${segments[0]}/${segments[1]}`; @@ -147,8 +151,8 @@ function classifyReason(mode, kind, resolvedPath, specifier) { if (resolvedPath?.startsWith("src/")) { return `${verb} core src path via relative path outside the extension package`; } - if (resolvedPath?.startsWith("extensions/")) { - return `${verb} another extension via relative path outside the extension package`; + if (resolvedPath?.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { + return `${verb} another bundled plugin via relative path outside the extension package`; } return `${verb} relative path ${specifier} outside the extension package`; } diff --git a/scripts/check-ingress-agent-owner-context.mjs b/scripts/check-ingress-agent-owner-context.mjs index d01026ad105..c7b923f62b5 100644 --- a/scripts/check-ingress-agent-owner-context.mjs +++ b/scripts/check-ingress-agent-owner-context.mjs @@ -2,6 +2,7 @@ import path from "node:path"; import ts from "typescript"; +import { bundledPluginFile } from "./lib/bundled-plugin-paths.mjs"; import { runCallsiteGuard } from "./lib/callsite-guard.mjs"; import { collectCallExpressionLines, @@ -9,9 +10,9 @@ import { unwrapExpression, } from "./lib/ts-guard-utils.mjs"; -const sourceRoots = ["src/gateway", "extensions/discord/src/voice"]; +const sourceRoots = ["src/gateway", bundledPluginFile("discord", "src/voice")]; const enforcedFiles = new Set([ - "extensions/discord/src/voice/manager.ts", + bundledPluginFile("discord", "src/voice/manager.ts"), "src/gateway/openai-http.ts", "src/gateway/openresponses-http.ts", "src/gateway/server-methods/agent.ts", diff --git a/scripts/check-no-extension-test-core-imports.ts b/scripts/check-no-extension-test-core-imports.ts index 252d356479d..1556083f65a 100644 --- a/scripts/check-no-extension-test-core-imports.ts +++ b/scripts/check-no-extension-test-core-imports.ts @@ -17,15 +17,15 @@ const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; hint: string }> = [ }, { pattern: /["'](?:\.\.\/)+(?:test-utils\/)[^"']+["']/, - hint: "Use test/helpers/extensions/* for repo-only bundled extension test helpers.", + hint: "Use test/helpers/plugins/* for repo-only bundled extension test helpers.", }, { pattern: /["'](?:\.\.\/)+(?:src\/test-utils\/)[^"']+["']/, - hint: "Use test/helpers/extensions/* for repo-only helpers, or openclaw/plugin-sdk/testing for public surfaces.", + hint: "Use test/helpers/plugins/* for repo-only helpers, or openclaw/plugin-sdk/testing for public surfaces.", }, { pattern: /["'](?:\.\.\/)+(?:src\/plugins\/types\.js)["']/, - hint: "Use public plugin-sdk/core types or test/helpers/extensions/* instead.", + hint: "Use public plugin-sdk/core types or test/helpers/plugins/* instead.", }, ]; diff --git a/scripts/check-no-random-messaging-tmp.mjs b/scripts/check-no-random-messaging-tmp.mjs index 30cf1509f29..e472082b5fc 100644 --- a/scripts/check-no-random-messaging-tmp.mjs +++ b/scripts/check-no-random-messaging-tmp.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node import ts from "typescript"; +import { bundledPluginFile } from "./lib/bundled-plugin-paths.mjs"; import { runCallsiteGuard } from "./lib/callsite-guard.mjs"; import { collectCallExpressionLines, @@ -15,7 +16,7 @@ const sourceRoots = [ "src/media-understanding", "extensions", ]; -const allowedRelativePaths = new Set(["extensions/feishu/src/dedup.ts"]); +const allowedRelativePaths = new Set([bundledPluginFile("feishu", "src/dedup.ts")]); function collectOsTmpdirImports(sourceFile) { const osModuleSpecifiers = new Set(["node:os", "os"]); diff --git a/scripts/check-no-raw-channel-fetch.mjs b/scripts/check-no-raw-channel-fetch.mjs index 1d3df8f0923..9e4128fad30 100644 --- a/scripts/check-no-raw-channel-fetch.mjs +++ b/scripts/check-no-raw-channel-fetch.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node import ts from "typescript"; +import { bundledPluginCallsite } from "./lib/bundled-plugin-paths.mjs"; import { runCallsiteGuard } from "./lib/callsite-guard.mjs"; import { collectCallExpressionLines, @@ -13,41 +14,41 @@ const sourceRoots = ["src/channels", "src/routing", "src/line", "extensions"]; // Temporary allowlist for legacy callsites. New raw fetch callsites in channel/plugin runtime // code should be rejected and migrated to fetchWithSsrFGuard/shared channel helpers. const allowedRawFetchCallsites = new Set([ - "extensions/bluebubbles/src/types.ts:133", - "extensions/feishu/src/streaming-card.ts:31", - "extensions/feishu/src/streaming-card.ts:101", - "extensions/feishu/src/streaming-card.ts:143", - "extensions/feishu/src/streaming-card.ts:199", - "extensions/googlechat/src/api.ts:22", - "extensions/googlechat/src/api.ts:43", - "extensions/googlechat/src/api.ts:63", - "extensions/googlechat/src/api.ts:188", - "extensions/googlechat/src/auth.ts:82", - "extensions/matrix/src/directory-live.ts:41", - "extensions/matrix/src/matrix/client/config.ts:171", - "extensions/mattermost/src/mattermost/client.ts:211", - "extensions/mattermost/src/mattermost/monitor.ts:230", - "extensions/mattermost/src/mattermost/probe.ts:27", - "extensions/minimax/oauth.ts:62", - "extensions/minimax/oauth.ts:93", - "extensions/msteams/src/graph.ts:39", - "extensions/nextcloud-talk/src/room-info.ts:92", - "extensions/nextcloud-talk/src/send.ts:107", - "extensions/nextcloud-talk/src/send.ts:198", - "extensions/talk-voice/index.ts:27", - "extensions/thread-ownership/index.ts:105", - "extensions/voice-call/src/providers/plivo.ts:95", - "extensions/voice-call/src/providers/telnyx.ts:61", - "extensions/voice-call/src/providers/tts-openai.ts:111", - "extensions/voice-call/src/providers/twilio/api.ts:23", - "extensions/telegram/src/api-fetch.ts:8", - "extensions/discord/src/send.outbound.ts:363", - "extensions/discord/src/voice-message.ts:268", - "extensions/discord/src/voice-message.ts:312", - "extensions/slack/src/monitor/media.ts:55", - "extensions/slack/src/monitor/media.ts:59", - "extensions/slack/src/monitor/media.ts:73", - "extensions/slack/src/monitor/media.ts:99", + bundledPluginCallsite("bluebubbles", "src/types.ts", 133), + bundledPluginCallsite("feishu", "src/streaming-card.ts", 31), + bundledPluginCallsite("feishu", "src/streaming-card.ts", 101), + bundledPluginCallsite("feishu", "src/streaming-card.ts", 143), + bundledPluginCallsite("feishu", "src/streaming-card.ts", 199), + bundledPluginCallsite("googlechat", "src/api.ts", 22), + bundledPluginCallsite("googlechat", "src/api.ts", 43), + bundledPluginCallsite("googlechat", "src/api.ts", 63), + bundledPluginCallsite("googlechat", "src/api.ts", 188), + bundledPluginCallsite("googlechat", "src/auth.ts", 82), + bundledPluginCallsite("matrix", "src/directory-live.ts", 41), + bundledPluginCallsite("matrix", "src/matrix/client/config.ts", 171), + bundledPluginCallsite("mattermost", "src/mattermost/client.ts", 211), + bundledPluginCallsite("mattermost", "src/mattermost/monitor.ts", 230), + bundledPluginCallsite("mattermost", "src/mattermost/probe.ts", 27), + bundledPluginCallsite("minimax", "oauth.ts", 62), + bundledPluginCallsite("minimax", "oauth.ts", 93), + bundledPluginCallsite("msteams", "src/graph.ts", 39), + bundledPluginCallsite("nextcloud-talk", "src/room-info.ts", 92), + bundledPluginCallsite("nextcloud-talk", "src/send.ts", 107), + bundledPluginCallsite("nextcloud-talk", "src/send.ts", 198), + bundledPluginCallsite("talk-voice", "index.ts", 27), + bundledPluginCallsite("thread-ownership", "index.ts", 105), + bundledPluginCallsite("voice-call", "src/providers/plivo.ts", 95), + bundledPluginCallsite("voice-call", "src/providers/telnyx.ts", 61), + bundledPluginCallsite("voice-call", "src/providers/tts-openai.ts", 111), + bundledPluginCallsite("voice-call", "src/providers/twilio/api.ts", 23), + bundledPluginCallsite("telegram", "src/api-fetch.ts", 8), + bundledPluginCallsite("discord", "src/send.outbound.ts", 363), + bundledPluginCallsite("discord", "src/voice-message.ts", 268), + bundledPluginCallsite("discord", "src/voice-message.ts", 312), + bundledPluginCallsite("slack", "src/monitor/media.ts", 55), + bundledPluginCallsite("slack", "src/monitor/media.ts", 59), + bundledPluginCallsite("slack", "src/monitor/media.ts", 73), + bundledPluginCallsite("slack", "src/monitor/media.ts", 99), ]); function isRawFetchCall(expression) { diff --git a/scripts/check-plugin-extension-import-boundary.mjs b/scripts/check-plugin-extension-import-boundary.mjs index 9bd802287a1..41b11572dfd 100644 --- a/scripts/check-plugin-extension-import-boundary.mjs +++ b/scripts/check-plugin-extension-import-boundary.mjs @@ -4,6 +4,7 @@ import { promises as fs } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import ts from "typescript"; +import { BUNDLED_PLUGIN_PATH_PREFIX } from "./lib/bundled-plugin-paths.mjs"; import { collectTypeScriptInventory, diffInventoryEntries, @@ -83,7 +84,7 @@ function scanImportBoundaryViolations(sourceFile, filePath) { visitModuleSpecifiers(ts, sourceFile, ({ kind, specifier, specifierNode }) => { const resolvedPath = resolveRepoSpecifier(repoRoot, specifier, filePath); - if (!resolvedPath?.startsWith("extensions/")) { + if (!resolvedPath?.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { return; } pushEntry(entries, { @@ -211,11 +212,11 @@ export function diffInventory(expected, actual) { function formatInventoryHuman(inventory) { if (inventory.length === 0) { - return "Rule: src/plugins/** must not import extensions/**\nNo plugin import boundary violations found."; + return "Rule: src/plugins/** must not import bundled plugin files\nNo plugin import boundary violations found."; } const lines = [ - "Rule: src/plugins/** must not import extensions/**", + "Rule: src/plugins/** must not import bundled plugin files", "Plugin extension import boundary inventory:", ]; let activeFile = ""; diff --git a/scripts/check-webhook-auth-body-order.mjs b/scripts/check-webhook-auth-body-order.mjs index 8a9cd507cbc..5e958d4c362 100644 --- a/scripts/check-webhook-auth-body-order.mjs +++ b/scripts/check-webhook-auth-body-order.mjs @@ -2,20 +2,21 @@ import path from "node:path"; import ts from "typescript"; +import { bundledPluginCallsite, bundledPluginFile } from "./lib/bundled-plugin-paths.mjs"; import { runCallsiteGuard } from "./lib/callsite-guard.mjs"; import { runAsScript, toLine, unwrapExpression } from "./lib/ts-guard-utils.mjs"; const sourceRoots = ["extensions"]; const enforcedFiles = new Set([ - "extensions/bluebubbles/src/monitor.ts", - "extensions/feishu/src/monitor.transport.ts", - "extensions/googlechat/src/monitor.ts", - "extensions/zalo/src/monitor.webhook.ts", + bundledPluginFile("bluebubbles", "src/monitor.ts"), + bundledPluginFile("feishu", "src/monitor.transport.ts"), + bundledPluginFile("googlechat", "src/monitor.ts"), + bundledPluginFile("zalo", "src/monitor.webhook.ts"), ]); const blockedCallees = new Set(["readJsonBodyWithLimit", "readRequestBodyWithLimit"]); const allowedCallsites = new Set([ // Feishu signs the exact wire body, so this handler must read raw bytes before parsing JSON. - "extensions/feishu/src/monitor.transport.ts:199", + bundledPluginCallsite("feishu", "src/monitor.transport.ts", 199), ]); function getCalleeName(expression) { diff --git a/scripts/dev/test-device-pair-telegram.ts b/scripts/dev/test-device-pair-telegram.ts index fecb5593635..00a71c63982 100644 --- a/scripts/dev/test-device-pair-telegram.ts +++ b/scripts/dev/test-device-pair-telegram.ts @@ -1,5 +1,5 @@ -import { sendMessageTelegram } from "../../extensions/telegram/src/send.js"; import { loadConfig } from "../../src/config/config.js"; +import { sendMessageTelegram } from "../../src/plugin-sdk/telegram-runtime.js"; import { matchPluginCommand, executePluginCommand } from "../../src/plugins/commands.js"; import { loadOpenClawPlugins } from "../../src/plugins/loader.js"; diff --git a/scripts/e2e/plugins-docker.sh b/scripts/e2e/plugins-docker.sh index 2feece5c880..6c974755144 100755 --- a/scripts/e2e/plugins-docker.sh +++ b/scripts/e2e/plugins-docker.sh @@ -24,6 +24,8 @@ export OPENCLAW_ENTRY home_dir=$(mktemp -d "/tmp/openclaw-plugins-e2e.XXXXXX") export HOME="$home_dir" +BUNDLED_PLUGIN_ROOT_DIR="extensions" +OPENCLAW_PLUGIN_HOME="$HOME/.openclaw/$BUNDLED_PLUGIN_ROOT_DIR" gateway_pid="" @@ -252,9 +254,11 @@ fs.writeFileSync(file, `${JSON.stringify(parsed, null, 2)}\n`); NODE } -mkdir -p "$HOME/.openclaw/extensions/demo-plugin" +demo_plugin_id="demo-plugin" +demo_plugin_root="$OPENCLAW_PLUGIN_HOME/$demo_plugin_id" +mkdir -p "$demo_plugin_root" -cat > "$HOME/.openclaw/extensions/demo-plugin/index.js" <<'JS' +cat > "$demo_plugin_root/index.js" <<'JS' module.exports = { id: "demo-plugin", name: "Demo Plugin", @@ -267,7 +271,7 @@ module.exports = { }, }; JS -cat > "$HOME/.openclaw/extensions/demo-plugin/openclaw.plugin.json" <<'JSON' +cat > "$demo_plugin_root/openclaw.plugin.json" <<'JSON' { "id": "demo-plugin", "configSchema": { @@ -450,7 +454,8 @@ console.log("ok"); NODE echo "Testing /plugin alias with Claude bundle restart semantics..." -bundle_root="$HOME/.openclaw/extensions/claude-bundle-e2e" +bundle_plugin_id="claude-bundle-e2e" +bundle_root="$OPENCLAW_PLUGIN_HOME/$bundle_plugin_id" mkdir -p "$bundle_root/.claude-plugin" "$bundle_root/commands" cat > "$bundle_root/.claude-plugin/plugin.json" <<'JSON' { diff --git a/scripts/generate-bundled-plugin-metadata.d.mts b/scripts/generate-bundled-plugin-metadata.d.mts deleted file mode 100644 index b4fbfd4a232..00000000000 --- a/scripts/generate-bundled-plugin-metadata.d.mts +++ /dev/null @@ -1,33 +0,0 @@ -export type BundledPluginPathPair = { - source: string; - built: string; -}; - -export type BundledPluginMetadataEntry = { - dirName: string; - idHint: string; - source: BundledPluginPathPair; - setupSource?: BundledPluginPathPair; - packageName?: string; - packageVersion?: string; - packageDescription?: string; - packageManifest?: Record; - manifest: Record; -}; - -export function collectBundledPluginMetadata(params?: { - repoRoot?: string; -}): Promise; - -export function renderBundledPluginMetadataModule(entries: BundledPluginMetadataEntry[]): string; - -export function writeBundledPluginMetadataModule(params?: { - repoRoot?: string; - outputPath?: string; - entriesOutputPath?: string; - check?: boolean; -}): Promise<{ - changed: boolean; - wrote: boolean; - outputPaths: string[]; -}>; diff --git a/scripts/generate-bundled-plugin-metadata.mjs b/scripts/generate-bundled-plugin-metadata.mjs deleted file mode 100644 index db200ba4f3d..00000000000 --- a/scripts/generate-bundled-plugin-metadata.mjs +++ /dev/null @@ -1,620 +0,0 @@ -import { execFileSync } from "node:child_process"; -import fs from "node:fs"; -import path from "node:path"; -import { collectBundledPluginSources } from "./lib/bundled-plugin-source-utils.mjs"; -import { formatGeneratedModule } from "./lib/format-generated-module.mjs"; -import { writeGeneratedOutput } from "./lib/generated-output-utils.mjs"; - -const GENERATED_BY = "scripts/generate-bundled-plugin-metadata.mjs"; -const DEFAULT_OUTPUT_PATH = "src/plugins/bundled-plugin-metadata.generated.ts"; -const DEFAULT_ENTRIES_OUTPUT_PATH = "src/generated/bundled-plugin-entries.generated.ts"; -const DEFAULT_CHANNEL_ENTRIES_OUTPUT_PATH = "src/generated/bundled-channel-entries.generated.ts"; -const DEFAULT_BUNDLED_CHANNEL_ENTRY_IDS = [ - "bluebubbles", - "discord", - "feishu", - "imessage", - "irc", - "line", - "mattermost", - "nextcloud-talk", - "signal", - "slack", - "synology-chat", - "telegram", - "zalo", -]; -const MANIFEST_KEY = "openclaw"; -const FORMATTER_CWD = path.resolve(import.meta.dirname, ".."); -const PUBLIC_SURFACE_SOURCE_EXTENSIONS = new Set([".ts", ".mts", ".js", ".mjs", ".cts", ".cjs"]); -const RUNTIME_SIDECAR_ARTIFACTS = new Set([ - "helper-api.js", - "light-runtime-api.js", - "runtime-api.js", - "thread-bindings-runtime.js", -]); - -function rewriteEntryToBuiltPath(entry) { - if (typeof entry !== "string" || entry.trim().length === 0) { - return undefined; - } - const normalized = entry.replace(/^\.\//u, ""); - return normalized.replace(/\.[^.]+$/u, ".js"); -} - -function isTopLevelPublicSurfaceSource(name) { - if (!PUBLIC_SURFACE_SOURCE_EXTENSIONS.has(path.extname(name))) { - return false; - } - if (name.startsWith(".")) { - return false; - } - if (name.startsWith("test-")) { - return false; - } - if (name.includes(".test-")) { - return false; - } - if (name.endsWith(".d.ts")) { - return false; - } - return !/(\.test|\.spec)(\.[cm]?[jt]s)$/u.test(name); -} - -function collectTopLevelPublicSurfaceArtifacts(params) { - const excluded = new Set( - [params.sourceEntry, params.setupEntry] - .filter((entry) => typeof entry === "string" && entry.trim().length > 0) - .map((entry) => path.basename(entry)), - ); - const artifacts = fs - .readdirSync(params.pluginDir, { withFileTypes: true }) - .filter((entry) => entry.isFile()) - .map((entry) => entry.name) - .filter(isTopLevelPublicSurfaceSource) - .filter((entry) => !excluded.has(entry)) - .map(rewriteEntryToBuiltPath) - .filter((entry) => typeof entry === "string" && entry.length > 0) - .toSorted((left, right) => left.localeCompare(right)); - return artifacts.length > 0 ? artifacts : undefined; -} - -function collectRuntimeSidecarArtifacts(publicSurfaceArtifacts) { - if (!publicSurfaceArtifacts) { - return undefined; - } - const runtimeSidecarArtifacts = publicSurfaceArtifacts.filter((artifact) => - RUNTIME_SIDECAR_ARTIFACTS.has(artifact), - ); - return runtimeSidecarArtifacts.length > 0 ? runtimeSidecarArtifacts : undefined; -} - -function deriveIdHint({ filePath, manifestId, packageName, hasMultipleExtensions }) { - const base = path.basename(filePath, path.extname(filePath)); - const normalizedManifestId = manifestId?.trim(); - if (normalizedManifestId) { - return hasMultipleExtensions ? `${normalizedManifestId}/${base}` : normalizedManifestId; - } - const rawPackageName = packageName?.trim(); - if (!rawPackageName) { - return base; - } - - const unscoped = rawPackageName.includes("/") - ? (rawPackageName.split("/").pop() ?? rawPackageName) - : rawPackageName; - const normalizedPackageId = - unscoped.endsWith("-provider") && unscoped.length > "-provider".length - ? unscoped.slice(0, -"-provider".length) - : unscoped; - - if (!hasMultipleExtensions) { - return normalizedPackageId; - } - return `${normalizedPackageId}/${base}`; -} - -function normalizeStringList(values) { - if (!Array.isArray(values)) { - return undefined; - } - const normalized = values.map((value) => String(value).trim()).filter(Boolean); - return normalized.length > 0 ? normalized : undefined; -} - -function normalizeManifestContracts(raw) { - const contracts = normalizeObject(raw); - if (!contracts) { - return undefined; - } - const speechProviders = normalizeStringList(contracts.speechProviders); - const mediaUnderstandingProviders = normalizeStringList(contracts.mediaUnderstandingProviders); - const imageGenerationProviders = normalizeStringList(contracts.imageGenerationProviders); - const webSearchProviders = normalizeStringList(contracts.webSearchProviders); - const tools = normalizeStringList(contracts.tools); - const normalized = { - ...(speechProviders?.length ? { speechProviders } : {}), - ...(mediaUnderstandingProviders?.length ? { mediaUnderstandingProviders } : {}), - ...(imageGenerationProviders?.length ? { imageGenerationProviders } : {}), - ...(webSearchProviders?.length ? { webSearchProviders } : {}), - ...(tools?.length ? { tools } : {}), - }; - return Object.keys(normalized).length > 0 ? normalized : undefined; -} - -function normalizeObject(value) { - if (!value || typeof value !== "object" || Array.isArray(value)) { - return undefined; - } - return value; -} - -function normalizePackageManifest(raw) { - const packageManifest = normalizeObject(raw?.[MANIFEST_KEY]); - if (!packageManifest) { - return undefined; - } - const normalized = { - ...(Array.isArray(packageManifest.extensions) - ? { extensions: packageManifest.extensions.map((entry) => String(entry).trim()) } - : {}), - ...(typeof packageManifest.setupEntry === "string" - ? { setupEntry: packageManifest.setupEntry.trim() } - : {}), - ...(normalizeObject(packageManifest.channel) ? { channel: packageManifest.channel } : {}), - ...(normalizeObject(packageManifest.install) ? { install: packageManifest.install } : {}), - ...(normalizeObject(packageManifest.startup) ? { startup: packageManifest.startup } : {}), - }; - return Object.keys(normalized).length > 0 ? normalized : undefined; -} - -function normalizePluginManifest(raw) { - if (!raw || typeof raw !== "object" || Array.isArray(raw)) { - return null; - } - if (typeof raw.id !== "string" || !raw.id.trim()) { - return null; - } - if ( - !raw.configSchema || - typeof raw.configSchema !== "object" || - Array.isArray(raw.configSchema) - ) { - return null; - } - - return { - id: raw.id.trim(), - configSchema: raw.configSchema, - ...(raw.enabledByDefault === true ? { enabledByDefault: true } : {}), - ...(typeof raw.kind === "string" ? { kind: raw.kind.trim() } : {}), - ...(normalizeStringList(raw.channels) ? { channels: normalizeStringList(raw.channels) } : {}), - ...(normalizeStringList(raw.providers) - ? { providers: normalizeStringList(raw.providers) } - : {}), - ...(normalizeStringList(raw.autoEnableWhenConfiguredProviders) - ? { - autoEnableWhenConfiguredProviders: normalizeStringList( - raw.autoEnableWhenConfiguredProviders, - ), - } - : {}), - ...(normalizeStringList(raw.cliBackends) - ? { cliBackends: normalizeStringList(raw.cliBackends) } - : {}), - ...(normalizeStringList(raw.legacyPluginIds) - ? { legacyPluginIds: normalizeStringList(raw.legacyPluginIds) } - : {}), - ...(normalizeObject(raw.providerAuthEnvVars) - ? { providerAuthEnvVars: raw.providerAuthEnvVars } - : {}), - ...(Array.isArray(raw.providerAuthChoices) - ? { providerAuthChoices: raw.providerAuthChoices } - : {}), - ...(normalizeStringList(raw.skills) ? { skills: normalizeStringList(raw.skills) } : {}), - ...(typeof raw.name === "string" ? { name: raw.name.trim() } : {}), - ...(typeof raw.description === "string" ? { description: raw.description.trim() } : {}), - ...(typeof raw.version === "string" ? { version: raw.version.trim() } : {}), - ...(normalizeObject(raw.uiHints) ? { uiHints: raw.uiHints } : {}), - ...(normalizeObject(raw.channelConfigs) ? { channelConfigs: raw.channelConfigs } : {}), - ...(normalizeManifestContracts(raw.contracts) - ? { contracts: normalizeManifestContracts(raw.contracts) } - : {}), - }; -} - -function resolvePackageChannelMeta(packageJson) { - const openclawMeta = - packageJson && - typeof packageJson === "object" && - !Array.isArray(packageJson) && - "openclaw" in packageJson - ? packageJson.openclaw - : undefined; - if (!openclawMeta || typeof openclawMeta !== "object" || Array.isArray(openclawMeta)) { - return undefined; - } - const channelMeta = openclawMeta.channel; - if (!channelMeta || typeof channelMeta !== "object" || Array.isArray(channelMeta)) { - return undefined; - } - return channelMeta; -} - -function resolveChannelConfigSchemaModulePath(rootDir) { - const candidates = [ - path.join(rootDir, "src", "config-schema.ts"), - path.join(rootDir, "src", "config-schema.js"), - path.join(rootDir, "src", "config-schema.mts"), - path.join(rootDir, "src", "config-schema.mjs"), - ]; - for (const candidate of candidates) { - if (fs.existsSync(candidate)) { - return candidate; - } - } - return null; -} - -function resolveRootLabel(source, channelId) { - const channelMeta = resolvePackageChannelMeta(source.packageJson); - if (channelMeta?.id === channelId && typeof channelMeta.label === "string") { - return channelMeta.label.trim(); - } - if (typeof source.manifest?.name === "string" && source.manifest.name.trim()) { - return source.manifest.name.trim(); - } - return undefined; -} - -function resolveRootDescription(source, channelId) { - const channelMeta = resolvePackageChannelMeta(source.packageJson); - if (channelMeta?.id === channelId && typeof channelMeta.blurb === "string") { - return channelMeta.blurb.trim(); - } - if (typeof source.manifest?.description === "string" && source.manifest.description.trim()) { - return source.manifest.description.trim(); - } - return undefined; -} - -function resolveRootPreferOver(source, channelId) { - const channelMeta = resolvePackageChannelMeta(source.packageJson); - if (channelMeta?.id !== channelId || !Array.isArray(channelMeta.preferOver)) { - return undefined; - } - const preferOver = channelMeta.preferOver - .map((entry) => (typeof entry === "string" ? entry.trim() : "")) - .filter(Boolean); - return preferOver.length > 0 ? preferOver : undefined; -} - -async function collectBundledChannelConfigsForSource({ source, manifest }) { - const channelIds = Array.isArray(manifest.channels) - ? manifest.channels.filter((entry) => typeof entry === "string" && entry.trim()) - : []; - const existingChannelConfigs = normalizeObject(manifest.channelConfigs) - ? { ...manifest.channelConfigs } - : {}; - if (channelIds.length === 0) { - return Object.keys(existingChannelConfigs).length > 0 ? existingChannelConfigs : undefined; - } - - const modulePath = resolveChannelConfigSchemaModulePath(source.pluginDir); - if (!modulePath || !fs.existsSync(modulePath)) { - return Object.keys(existingChannelConfigs).length > 0 ? existingChannelConfigs : undefined; - } - - const runSurfaceLoader = (command, args) => - execFileSync(command, args, { - // Run from the host repo so the generator always resolves its own loader/tooling, - // even when inspecting a temporary or alternate repo root. - cwd: FORMATTER_CWD, - encoding: "utf8", - }); - - let surfaceJson; - try { - surfaceJson = runSurfaceLoader("bun", ["scripts/load-channel-config-surface.ts", modulePath]); - } catch (error) { - if (!error || typeof error !== "object" || error.code !== "ENOENT") { - throw error; - } - surfaceJson = runSurfaceLoader(process.execPath, [ - "--import", - "tsx", - "scripts/load-channel-config-surface.ts", - modulePath, - ]); - } - const surface = JSON.parse(surfaceJson); - if (!surface?.schema) { - return Object.keys(existingChannelConfigs).length > 0 ? existingChannelConfigs : undefined; - } - - for (const channelId of channelIds) { - const existing = - existingChannelConfigs[channelId] && - typeof existingChannelConfigs[channelId] === "object" && - !Array.isArray(existingChannelConfigs[channelId]) - ? existingChannelConfigs[channelId] - : undefined; - const label = existing?.label ?? resolveRootLabel(source, channelId); - const description = existing?.description ?? resolveRootDescription(source, channelId); - const preferOver = existing?.preferOver ?? resolveRootPreferOver(source, channelId); - const uiHints = - surface.uiHints || existing?.uiHints - ? { - ...(surface.uiHints && Object.keys(surface.uiHints).length > 0 - ? { ...surface.uiHints } - : {}), - ...(existing?.uiHints && Object.keys(existing.uiHints).length > 0 - ? { ...existing.uiHints } - : {}), - } - : undefined; - - existingChannelConfigs[channelId] = { - schema: surface.schema, - ...(uiHints && Object.keys(uiHints).length > 0 ? { uiHints } : {}), - ...(label ? { label } : {}), - ...(description ? { description } : {}), - ...(preferOver?.length ? { preferOver } : {}), - }; - } - - return Object.keys(existingChannelConfigs).length > 0 ? existingChannelConfigs : undefined; -} - -function formatTypeScriptModule(source, { outputPath }) { - return formatGeneratedModule(source, { - repoRoot: FORMATTER_CWD, - outputPath, - errorLabel: "bundled plugin metadata", - }); -} - -function toIdentifier(dirName) { - const cleaned = String(dirName) - .replace(/[^a-zA-Z0-9]+(.)/g, (_match, next) => next.toUpperCase()) - .replace(/[^a-zA-Z0-9]/g, "") - .replace(/^[^a-zA-Z]+/g, ""); - const base = cleaned || "plugin"; - return `${base[0].toLowerCase()}${base.slice(1)}Plugin`; -} - -function normalizeGeneratedImportPath(dirName, builtPath) { - return `../../extensions/${dirName}/${String(builtPath).replace(/^\.\//u, "")}`; -} - -function resolveBundledChannelEntries(entries) { - const orderById = new Map(DEFAULT_BUNDLED_CHANNEL_ENTRY_IDS.map((id, index) => [id, index])); - return entries - .filter( - (entry) => - Array.isArray(entry.manifest?.channels) && - entry.manifest.channels.length > 0 && - orderById.has(entry.manifest.id), - ) - .toSorted( - (left, right) => - (orderById.get(left.manifest.id) ?? Number.MAX_SAFE_INTEGER) - - (orderById.get(right.manifest.id) ?? Number.MAX_SAFE_INTEGER), - ); -} - -export async function collectBundledPluginMetadata(params = {}) { - const repoRoot = path.resolve(params.repoRoot ?? process.cwd()); - const entries = []; - for (const source of collectBundledPluginSources({ repoRoot, requirePackageJson: true })) { - const manifest = normalizePluginManifest(source.manifest); - if (!manifest) { - continue; - } - - const packageJson = source.packageJson; - const packageManifest = normalizePackageManifest(packageJson); - const extensions = Array.isArray(packageManifest?.extensions) - ? packageManifest.extensions.filter((entry) => typeof entry === "string" && entry.trim()) - : []; - if (extensions.length === 0) { - continue; - } - - const sourceEntry = extensions[0]; - const builtEntry = rewriteEntryToBuiltPath(sourceEntry); - if (!builtEntry) { - continue; - } - const setupEntry = - typeof packageManifest?.setupEntry === "string" && - packageManifest.setupEntry.trim().length > 0 - ? { - source: packageManifest.setupEntry.trim(), - built: rewriteEntryToBuiltPath(packageManifest.setupEntry.trim()), - } - : undefined; - const publicSurfaceArtifacts = collectTopLevelPublicSurfaceArtifacts({ - pluginDir: source.pluginDir, - sourceEntry, - setupEntry: setupEntry?.source, - }); - const runtimeSidecarArtifacts = collectRuntimeSidecarArtifacts(publicSurfaceArtifacts); - const channelConfigs = await collectBundledChannelConfigsForSource({ source, manifest }); - if (channelConfigs) { - manifest.channelConfigs = channelConfigs; - } - - entries.push({ - dirName: source.dirName, - idHint: deriveIdHint({ - filePath: sourceEntry, - manifestId: manifest.id, - packageName: typeof packageJson.name === "string" ? packageJson.name : undefined, - hasMultipleExtensions: extensions.length > 1, - }), - source: { - source: sourceEntry, - built: builtEntry, - }, - ...(setupEntry?.built - ? { setupSource: { source: setupEntry.source, built: setupEntry.built } } - : {}), - ...(publicSurfaceArtifacts ? { publicSurfaceArtifacts } : {}), - ...(runtimeSidecarArtifacts ? { runtimeSidecarArtifacts } : {}), - ...(typeof packageJson.name === "string" ? { packageName: packageJson.name.trim() } : {}), - ...(typeof packageJson.version === "string" - ? { packageVersion: packageJson.version.trim() } - : {}), - ...(typeof packageJson.description === "string" - ? { packageDescription: packageJson.description.trim() } - : {}), - ...(packageManifest ? { packageManifest } : {}), - manifest, - }); - } - - return entries.toSorted((left, right) => left.dirName.localeCompare(right.dirName)); -} - -export function renderBundledPluginMetadataModule(entries) { - return `// Auto-generated by ${GENERATED_BY}. Do not edit directly. - -export const GENERATED_BUNDLED_PLUGIN_METADATA = ${JSON.stringify(entries, null, 2)} as const; -`; -} - -export function renderBundledPluginEntriesModule(entries) { - const imports = entries - .map((entry) => { - const importPath = normalizeGeneratedImportPath(entry.dirName, entry.source.built); - return ` import("${importPath}")`; - }) - .join(",\n"); - const bindings = entries - .map((entry) => { - const identifier = toIdentifier(entry.dirName); - return `${identifier}Module`; - }) - .join(",\n "); - const identifiers = entries - .map((entry) => { - const identifier = toIdentifier(entry.dirName); - return `${identifier}Module.default`; - }) - .join(",\n "); - return `// Auto-generated by ${GENERATED_BY}. Do not edit directly. - -export async function loadGeneratedBundledPluginEntries() { - const [ - ${bindings} - ] = await Promise.all([ -${imports} - ]); - return [ - ${identifiers} - ] as const; -} -`; -} - -export function renderBundledChannelEntriesModule(entries) { - const channelEntries = resolveBundledChannelEntries(entries); - const importLines = []; - const entryRecords = []; - for (const entry of channelEntries) { - const identifierBase = toIdentifier(entry.dirName).replace(/Plugin$/u, ""); - const entryIdentifier = `${identifierBase}ChannelEntry`; - importLines.push( - `import ${entryIdentifier} from "${normalizeGeneratedImportPath(entry.dirName, entry.source.built)}";`, - ); - let setupEntryIdentifier = null; - if (entry.setupSource?.built) { - setupEntryIdentifier = `${identifierBase}ChannelSetupEntry`; - importLines.push( - `import ${setupEntryIdentifier} from "${normalizeGeneratedImportPath(entry.dirName, entry.setupSource.built)}";`, - ); - } - entryRecords.push(` { - id: ${JSON.stringify(entry.manifest.id)}, - entry: ${entryIdentifier}, -${setupEntryIdentifier ? ` setupEntry: ${setupEntryIdentifier},\n` : ""} }`); - } - return `// Auto-generated by ${GENERATED_BY}. Do not edit directly. - -${importLines.join("\n")} - -export const GENERATED_BUNDLED_CHANNEL_ENTRIES = [ -${entryRecords.join(",\n")} -] as const; -`; -} - -export async function writeBundledPluginMetadataModule(params = {}) { - const repoRoot = path.resolve(params.repoRoot ?? process.cwd()); - const entries = await collectBundledPluginMetadata({ repoRoot }); - const outputPath = path.resolve(repoRoot, params.outputPath ?? DEFAULT_OUTPUT_PATH); - const entriesOutputPath = path.resolve( - repoRoot, - params.entriesOutputPath ?? DEFAULT_ENTRIES_OUTPUT_PATH, - ); - const channelEntriesOutputPath = path.resolve( - repoRoot, - params.channelEntriesOutputPath ?? DEFAULT_CHANNEL_ENTRIES_OUTPUT_PATH, - ); - const metadataNext = formatTypeScriptModule(renderBundledPluginMetadataModule(entries), { - outputPath, - }); - const registryNext = formatTypeScriptModule(renderBundledPluginEntriesModule(entries), { - outputPath: entriesOutputPath, - }); - const channelEntriesNext = formatTypeScriptModule(renderBundledChannelEntriesModule(entries), { - outputPath: channelEntriesOutputPath, - }); - const metadataResult = writeGeneratedOutput({ - repoRoot, - outputPath: params.outputPath ?? DEFAULT_OUTPUT_PATH, - next: metadataNext, - check: params.check, - }); - const entriesResult = writeGeneratedOutput({ - repoRoot, - outputPath: params.entriesOutputPath ?? DEFAULT_ENTRIES_OUTPUT_PATH, - next: registryNext, - check: params.check, - }); - const channelEntriesResult = writeGeneratedOutput({ - repoRoot, - outputPath: params.channelEntriesOutputPath ?? DEFAULT_CHANNEL_ENTRIES_OUTPUT_PATH, - next: channelEntriesNext, - check: params.check, - }); - return { - changed: metadataResult.changed || entriesResult.changed || channelEntriesResult.changed, - wrote: metadataResult.wrote || entriesResult.wrote || channelEntriesResult.wrote, - outputPaths: [ - metadataResult.outputPath, - entriesResult.outputPath, - channelEntriesResult.outputPath, - ], - }; -} - -if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) { - const check = process.argv.includes("--check"); - const result = await writeBundledPluginMetadataModule({ check }); - if (!result.changed) { - process.exitCode = 0; - } else if (check) { - for (const outputPath of result.outputPaths) { - const relativeOutputPath = path.relative(process.cwd(), outputPath); - console.error(`[bundled-plugin-metadata] stale generated output at ${relativeOutputPath}`); - } - process.exitCode = 1; - } else { - for (const outputPath of result.outputPaths) { - const relativeOutputPath = path.relative(process.cwd(), outputPath); - console.log(`[bundled-plugin-metadata] wrote ${relativeOutputPath}`); - } - } -} diff --git a/scripts/generate-plugin-sdk-facades.mjs b/scripts/generate-plugin-sdk-facades.mjs index c335e8ea5a8..4832382fedb 100644 --- a/scripts/generate-plugin-sdk-facades.mjs +++ b/scripts/generate-plugin-sdk-facades.mjs @@ -7,6 +7,8 @@ import { writeGeneratedOutput } from "./lib/generated-output-utils.mjs"; import { GENERATED_PLUGIN_SDK_FACADES, GENERATED_PLUGIN_SDK_FACADES_LABEL, + GENERATED_PLUGIN_SDK_FACADE_TYPES_OUTPUT, + buildPluginSdkFacadeTypeMapModule, buildPluginSdkFacadeModule, } from "./lib/plugin-sdk-facades.mjs"; @@ -21,6 +23,23 @@ function parseArgs(argv) { export function generatePluginSdkFacades(params) { const results = []; + const typeMapOutputPath = GENERATED_PLUGIN_SDK_FACADE_TYPES_OUTPUT; + const typeMapNext = formatGeneratedModule( + buildPluginSdkFacadeTypeMapModule(GENERATED_PLUGIN_SDK_FACADES), + { + repoRoot: params.repoRoot, + outputPath: typeMapOutputPath, + errorLabel: `${GENERATED_PLUGIN_SDK_FACADES_LABEL}:type-map`, + }, + ); + results.push( + writeGeneratedOutput({ + repoRoot: params.repoRoot, + outputPath: typeMapOutputPath, + next: typeMapNext, + check: params.check, + }), + ); for (const entry of GENERATED_PLUGIN_SDK_FACADES) { const outputPath = `src/plugin-sdk/${entry.subpath}.ts`; const next = formatGeneratedModule( diff --git a/scripts/lib/bundled-plugin-build-entries.mjs b/scripts/lib/bundled-plugin-build-entries.mjs index e8a567b87a7..4453353ffdd 100644 --- a/scripts/lib/bundled-plugin-build-entries.mjs +++ b/scripts/lib/bundled-plugin-build-entries.mjs @@ -1,5 +1,10 @@ import fs from "node:fs"; import path from "node:path"; +import { + BUNDLED_PLUGIN_ROOT_DIR, + bundledDistPluginFile, + bundledPluginFile, +} from "./bundled-plugin-paths.mjs"; import { shouldBuildBundledCluster } from "./optional-bundled-clusters.mjs"; const TOP_LEVEL_PUBLIC_SURFACE_EXTENSIONS = new Set([".ts", ".js", ".mts", ".cts", ".mjs", ".cjs"]); @@ -68,7 +73,7 @@ function collectTopLevelPublicSurfaceEntries(pluginDir) { export function collectBundledPluginBuildEntries(params = {}) { const cwd = params.cwd ?? process.cwd(); const env = params.env ?? process.env; - const extensionsRoot = path.join(cwd, "extensions"); + const extensionsRoot = path.join(cwd, BUNDLED_PLUGIN_ROOT_DIR); const entries = []; for (const dirent of fs.readdirSync(extensionsRoot, { withFileTypes: true })) { @@ -109,8 +114,8 @@ export function listBundledPluginBuildEntries(params = {}) { collectBundledPluginBuildEntries(params).flatMap(({ id, sourceEntries }) => sourceEntries.map((entry) => { const normalizedEntry = entry.replace(/^\.\//, ""); - const entryKey = `extensions/${id}/${normalizedEntry.replace(/\.[^.]+$/u, "")}`; - return [entryKey, path.join("extensions", id, normalizedEntry)]; + const entryKey = bundledPluginFile(id, normalizedEntry.replace(/\.[^.]+$/u, "")); + return [entryKey, path.join(BUNDLED_PLUGIN_ROOT_DIR, id, normalizedEntry)]; }), ), ); @@ -121,13 +126,13 @@ export function listBundledPluginPackArtifacts(params = {}) { const artifacts = new Set(); for (const { id, hasPackageJson, sourceEntries } of entries) { - artifacts.add(`dist/extensions/${id}/openclaw.plugin.json`); + artifacts.add(bundledDistPluginFile(id, "openclaw.plugin.json")); if (hasPackageJson) { - artifacts.add(`dist/extensions/${id}/package.json`); + artifacts.add(bundledDistPluginFile(id, "package.json")); } for (const entry of sourceEntries) { const normalizedEntry = entry.replace(/^\.\//, "").replace(/\.[^.]+$/u, ""); - artifacts.add(`dist/extensions/${id}/${normalizedEntry}.js`); + artifacts.add(bundledDistPluginFile(id, `${normalizedEntry}.js`)); } } diff --git a/scripts/lib/bundled-plugin-paths.mjs b/scripts/lib/bundled-plugin-paths.mjs new file mode 100644 index 00000000000..b5f612ec67a --- /dev/null +++ b/scripts/lib/bundled-plugin-paths.mjs @@ -0,0 +1,25 @@ +export const BUNDLED_PLUGIN_ROOT_DIR = "extensions"; +export const BUNDLED_PLUGIN_PATH_PREFIX = `${BUNDLED_PLUGIN_ROOT_DIR}/`; +export const BUNDLED_PLUGIN_TEST_GLOB = `${BUNDLED_PLUGIN_ROOT_DIR}/**/*.test.ts`; +export const BUNDLED_PLUGIN_E2E_TEST_GLOB = `${BUNDLED_PLUGIN_ROOT_DIR}/**/*.e2e.test.ts`; +export const BUNDLED_PLUGIN_LIVE_TEST_GLOB = `${BUNDLED_PLUGIN_ROOT_DIR}/**/*.live.test.ts`; + +export function bundledPluginRoot(pluginId) { + return `${BUNDLED_PLUGIN_PATH_PREFIX}${pluginId}`; +} + +export function bundledPluginFile(pluginId, relativePath) { + return `${bundledPluginRoot(pluginId)}/${relativePath}`; +} + +export function bundledDistPluginRoot(pluginId) { + return `dist/${bundledPluginRoot(pluginId)}`; +} + +export function bundledDistPluginFile(pluginId, relativePath) { + return `${bundledDistPluginRoot(pluginId)}/${relativePath}`; +} + +export function bundledPluginCallsite(pluginId, relativePath, line) { + return `${bundledPluginFile(pluginId, relativePath)}:${line}`; +} diff --git a/scripts/lib/plugin-sdk-facades.mjs b/scripts/lib/plugin-sdk-facades.mjs index 5ec7f953564..55011b1a673 100644 --- a/scripts/lib/plugin-sdk-facades.mjs +++ b/scripts/lib/plugin-sdk-facades.mjs @@ -1,11 +1,22 @@ import fs from "node:fs"; import path from "node:path"; import ts from "typescript"; +import { BUNDLED_PLUGIN_PATH_PREFIX, bundledPluginFile } from "./bundled-plugin-paths.mjs"; + +function pluginSource(dirName, artifactBasename = "api.js") { + return `openclaw/plugin-source/${dirName}/${artifactBasename}`; +} + +function runtimeApiSourcePath(dirName) { + return bundledPluginFile(dirName, "runtime-api.ts"); +} + +const BUNDLED_PLUGIN_SOURCE_RELATIVE_PREFIX = `../../${BUNDLED_PLUGIN_PATH_PREFIX}`; export const GENERATED_PLUGIN_SDK_FACADES = [ { subpath: "amazon-bedrock", - source: "../../extensions/amazon-bedrock/api.js", + source: pluginSource("amazon-bedrock", "api.js"), exports: [ "discoverBedrockModels", "mergeImplicitBedrockProvider", @@ -16,7 +27,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "anthropic-vertex", - source: "../../extensions/anthropic-vertex/api.js", + source: pluginSource("anthropic-vertex", "api.js"), exports: [ "ANTHROPIC_VERTEX_DEFAULT_MODEL_ID", "buildAnthropicVertexProvider", @@ -33,13 +44,13 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "discord-account", - source: "../../extensions/discord/api.js", + source: pluginSource("discord", "api.js"), exports: ["resolveDiscordAccount", "ResolvedDiscordAccount"], typeExports: ["ResolvedDiscordAccount"], }, { subpath: "discord-runtime-surface", - source: "../../extensions/discord/runtime-api.js", + source: pluginSource("discord", "runtime-api.js"), exports: [ "addRoleDiscord", "auditDiscordChannelPermissions", @@ -102,12 +113,12 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "discord-session-key", - source: "../../extensions/discord/session-key-api.js", + source: pluginSource("discord", "session-key-api.js"), exports: ["normalizeExplicitDiscordSessionKey"], }, { subpath: "discord-surface", - source: "../../extensions/discord/api.js", + source: pluginSource("discord", "api.js"), exports: [ "buildDiscordComponentMessage", "collectDiscordStatusIssues", @@ -149,9 +160,8 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "discord-thread-bindings", - source: "../../extensions/discord/runtime-api.js", + source: pluginSource("discord", "runtime-api.js"), exports: [ - "__testing", "autoBindSpawnedDiscordSubagent", "createThreadBindingManager", "getThreadBindingManager", @@ -171,17 +181,17 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "discord-timeouts", - source: "../../extensions/discord/api.js", + source: pluginSource("discord", "timeouts.js"), exports: ["DISCORD_DEFAULT_INBOUND_WORKER_TIMEOUT_MS", "DISCORD_DEFAULT_LISTENER_TIMEOUT_MS"], }, { subpath: "anthropic-cli", - source: "../../extensions/anthropic/api.js", + source: pluginSource("anthropic", "api.js"), exports: ["CLAUDE_CLI_BACKEND_ID", "isClaudeCliProvider"], }, { subpath: "bluebubbles-policy", - source: "../../extensions/bluebubbles/api.js", + source: pluginSource("bluebubbles", "api.js"), exports: [ "isAllowedBlueBubblesSender", "resolveBlueBubblesGroupRequireMention", @@ -190,7 +200,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "browser", - source: "../../extensions/browser/runtime-api.js", + source: pluginSource("browser", "runtime-api.js"), exports: [ "browserHandlers", "createBrowserPluginService", @@ -201,12 +211,117 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "browser-runtime", - source: "../../extensions/browser/runtime-api.js", - exportAll: true, + source: pluginSource("browser", "runtime-api.js"), + exports: [ + "BrowserBridge", + "BrowserCreateProfileResult", + "BrowserDeleteProfileResult", + "BrowserExecutable", + "BrowserFormField", + "BrowserResetProfileResult", + "BrowserRouteRegistrar", + "BrowserServerState", + "BrowserStatus", + "BrowserTab", + "BrowserTransport", + "DEFAULT_AI_SNAPSHOT_MAX_CHARS", + "DEFAULT_BROWSER_EVALUATE_ENABLED", + "DEFAULT_OPENCLAW_BROWSER_COLOR", + "DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME", + "DEFAULT_UPLOAD_DIR", + "OpenClawPluginApi", + "OpenClawPluginToolContext", + "OpenClawPluginToolFactory", + "ProfileStatus", + "ResolvedBrowserConfig", + "ResolvedBrowserProfile", + "SnapshotResult", + "applyBrowserProxyPaths", + "browserAct", + "browserArmDialog", + "browserArmFileChooser", + "browserCloseTab", + "browserConsoleMessages", + "browserCreateProfile", + "browserDeleteProfile", + "browserFocusTab", + "browserHandlers", + "browserNavigate", + "browserOpenTab", + "browserPdfSave", + "browserProfiles", + "browserResetProfile", + "browserScreenshotAction", + "browserSnapshot", + "browserStart", + "browserStatus", + "browserStop", + "browserTabAction", + "browserTabs", + "closeTrackedBrowserTabsForSessions", + "createBrowserControlContext", + "createBrowserPluginService", + "createBrowserRouteContext", + "createBrowserRouteDispatcher", + "createBrowserRuntimeState", + "createBrowserTool", + "definePluginEntry", + "ensureBrowserControlAuth", + "getBrowserControlState", + "getBrowserProfileCapabilities", + "handleBrowserGatewayRequest", + "installBrowserAuthMiddleware", + "installBrowserCommonMiddleware", + "isPersistentBrowserProfileMutation", + "movePathToTrash", + "normalizeBrowserFormField", + "normalizeBrowserFormFieldValue", + "normalizeBrowserRequestPath", + "parseBrowserMajorVersion", + "persistBrowserProxyFiles", + "readBrowserVersion", + "redactCdpUrl", + "registerBrowserCli", + "registerBrowserRoutes", + "resolveBrowserConfig", + "resolveBrowserControlAuth", + "resolveExistingPathsWithinRoot", + "resolveGoogleChromeExecutableForPlatform", + "resolveProfile", + "resolveRequestedBrowserProfile", + "runBrowserProxyCommand", + "startBrowserBridgeServer", + "startBrowserControlServiceFromConfig", + "stopBrowserBridgeServer", + "stopBrowserControlService", + "stopBrowserRuntime", + "trackSessionBrowserTab", + "untrackSessionBrowserTab", + ], + typeExports: [ + "BrowserBridge", + "BrowserCreateProfileResult", + "BrowserDeleteProfileResult", + "BrowserExecutable", + "BrowserFormField", + "BrowserResetProfileResult", + "BrowserRouteRegistrar", + "BrowserServerState", + "BrowserStatus", + "BrowserTab", + "BrowserTransport", + "OpenClawPluginApi", + "OpenClawPluginToolContext", + "OpenClawPluginToolFactory", + "ProfileStatus", + "ResolvedBrowserConfig", + "ResolvedBrowserProfile", + "SnapshotResult", + ], }, { subpath: "cloudflare-ai-gateway", - source: "../../extensions/cloudflare-ai-gateway/api.js", + source: pluginSource("cloudflare-ai-gateway", "api.js"), exports: [ "applyCloudflareAiGatewayConfig", "applyCloudflareAiGatewayProviderConfig", @@ -220,7 +335,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "byteplus", - source: "../../extensions/byteplus/api.js", + source: pluginSource("byteplus", "api.js"), exports: [ "buildBytePlusCodingProvider", "buildBytePlusModelDefinition", @@ -233,7 +348,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "chutes", - source: "../../extensions/chutes/api.js", + source: pluginSource("chutes", "api.js"), exports: [ "applyChutesApiKeyConfig", "applyChutesConfig", @@ -249,7 +364,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "deepseek", - source: "../../extensions/deepseek/api.js", + source: pluginSource("deepseek", "api.js"), exports: [ "buildDeepSeekModelDefinition", "buildDeepSeekProvider", @@ -259,7 +374,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "feishu-conversation", - source: "../../extensions/feishu/api.js", + source: pluginSource("feishu", "api.js"), exports: [ "buildFeishuConversationId", "createFeishuThreadBindingManager", @@ -272,7 +387,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "google", - source: "../../extensions/google/api.js", + source: pluginSource("google", "api.js"), exports: [ "applyGoogleGeminiModelDefault", "DEFAULT_GOOGLE_API_BASE_URL", @@ -292,17 +407,17 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "feishu-setup", - source: "../../extensions/feishu/api.js", + source: pluginSource("feishu", "api.js"), exports: ["feishuSetupAdapter", "feishuSetupWizard"], }, { subpath: "github-copilot-login", - source: "../../extensions/github-copilot/api.js", + source: pluginSource("github-copilot", "api.js"), exports: ["githubCopilotLoginCommand"], }, { subpath: "huggingface", - source: "../../extensions/huggingface/api.js", + source: pluginSource("huggingface", "api.js"), exports: [ "buildHuggingfaceModelDefinition", "buildHuggingfaceProvider", @@ -316,7 +431,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "imessage-targets", - source: "../../extensions/imessage/api.js", + source: pluginSource("imessage", "api.js"), exports: [ "normalizeIMessageHandle", "parseChatAllowTargetPrefixes", @@ -329,17 +444,23 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "image-generation-runtime", - source: "../../extensions/image-generation-core/runtime-api.js", - exportAll: true, + source: pluginSource("image-generation-core", "runtime-api.js"), + exports: [ + "generateImage", + "listRuntimeImageGenerationProviders", + "GenerateImageParams", + "GenerateImageRuntimeResult", + ], + typeExports: ["GenerateImageParams", "GenerateImageRuntimeResult"], }, { subpath: "kimi-coding", - source: "../../extensions/kimi-coding/api.js", + source: pluginSource("kimi-coding", "api.js"), exports: ["buildKimiCodingProvider"], }, { subpath: "kilocode", - source: "../../extensions/kilocode/api.js", + source: pluginSource("kilocode", "api.js"), exports: [ "buildKilocodeProvider", "buildKilocodeProviderWithDiscovery", @@ -358,7 +479,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "imessage-policy", - source: "../../extensions/imessage/api.js", + source: pluginSource("imessage", "api.js"), exports: [ "normalizeIMessageHandle", "resolveIMessageRuntimeGroupPolicy", @@ -368,13 +489,13 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "imessage-runtime", - source: "../../extensions/imessage/runtime-api.js", + source: pluginSource("imessage", "runtime-api.js"), exports: ["monitorIMessageProvider", "probeIMessage", "sendMessageIMessage"], typeExports: ["IMessageProbe"], }, { subpath: "irc-surface", - source: "../../extensions/irc/api.js", + source: pluginSource("irc", "api.js"), exports: [ "ircSetupAdapter", "ircSetupWizard", @@ -385,22 +506,38 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "media-understanding-runtime", - source: "../../extensions/media-understanding-core/runtime-api.js", - exportAll: true, + source: pluginSource("media-understanding-core", "runtime-api.js"), + exports: [ + "describeImageFile", + "describeImageFileWithModel", + "describeVideoFile", + "runMediaUnderstandingFile", + "transcribeAudioFile", + "RunMediaUnderstandingFileParams", + "RunMediaUnderstandingFileResult", + ], + typeExports: ["RunMediaUnderstandingFileParams", "RunMediaUnderstandingFileResult"], }, { subpath: "memory-core-engine-runtime", - source: "../../extensions/memory-core/runtime-api.js", - exportAll: true, + source: pluginSource("memory-core", "runtime-api.js"), + exports: [ + "BuiltinMemoryEmbeddingProviderDoctorMetadata", + "getBuiltinMemoryEmbeddingProviderDoctorMetadata", + "getMemorySearchManager", + "listBuiltinAutoSelectMemoryEmbeddingProviderDoctorMetadata", + "MemoryIndexManager", + ], + typeExports: ["BuiltinMemoryEmbeddingProviderDoctorMetadata"], }, { subpath: "mattermost-policy", - source: "../../extensions/mattermost/api.js", + source: pluginSource("mattermost", "api.js"), exports: ["isMattermostSenderAllowed"], }, { subpath: "litellm", - source: "../../extensions/litellm/api.js", + source: pluginSource("litellm", "api.js"), exports: [ "applyLitellmConfig", "applyLitellmProviderConfig", @@ -412,8 +549,8 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "line-runtime", - source: "../../extensions/line/runtime-api.js", - runtimeApiPreExportsPath: "extensions/line/runtime-api.ts", + source: pluginSource("line", "runtime-api.js"), + runtimeApiPreExportsPath: runtimeApiSourcePath("line"), typeExports: [ "Action", "CardAction", @@ -440,7 +577,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "line-surface", - source: "../../extensions/line/runtime-api.js", + source: pluginSource("line", "runtime-api.js"), exports: [ "CardAction", "createActionCard", @@ -477,7 +614,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "matrix-helper", - source: "../../extensions/matrix/api.js", + source: pluginSource("matrix", "api.js"), exports: [ "findMatrixAccountEntry", "getMatrixScopedEnvVarNames", @@ -493,12 +630,12 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "matrix-runtime-surface", - source: "../../extensions/matrix/runtime-api.js", + source: pluginSource("matrix", "runtime-api.js"), exports: ["resolveMatrixAccountStringValues", "setMatrixRuntime"], }, { subpath: "matrix-surface", - source: "../../extensions/matrix/api.js", + source: pluginSource("matrix", "api.js"), exports: [ "createMatrixThreadBindingManager", "matrixSessionBindingAdapterChannels", @@ -507,7 +644,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "matrix-thread-bindings", - source: "../../extensions/matrix/api.js", + source: pluginSource("matrix", "api.js"), exports: [ "setMatrixThreadBindingIdleTimeoutBySessionKey", "setMatrixThreadBindingMaxAgeBySessionKey", @@ -515,7 +652,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "openrouter", - source: "../../extensions/openrouter/api.js", + source: pluginSource("openrouter", "api.js"), exports: [ "applyOpenrouterConfig", "applyOpenrouterProviderConfig", @@ -525,7 +662,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "minimax", - source: "../../extensions/minimax/api.js", + source: pluginSource("minimax", "api.js"), exports: [ "applyMinimaxApiConfig", "applyMinimaxApiConfigCn", @@ -545,7 +682,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "modelstudio", - source: "../../extensions/modelstudio/api.js", + source: pluginSource("modelstudio", "api.js"), exports: [ "applyModelStudioNativeStreamingUsageCompat", "buildModelStudioDefaultModelDefinition", @@ -565,7 +702,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "modelstudio-definitions", - source: "../../extensions/modelstudio/api.js", + source: pluginSource("modelstudio", "api.js"), exports: [ "buildModelStudioDefaultModelDefinition", "buildModelStudioModelDefinition", @@ -580,7 +717,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "moonshot", - source: "../../extensions/moonshot/api.js", + source: pluginSource("moonshot", "api.js"), exports: [ "applyMoonshotNativeStreamingUsageCompat", "buildMoonshotProvider", @@ -593,7 +730,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "mistral", - source: "../../extensions/mistral/api.js", + source: pluginSource("mistral", "api.js"), exports: [ "applyMistralConfig", "applyMistralProviderConfig", @@ -606,17 +743,35 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "nvidia", - source: "../../extensions/nvidia/api.js", + source: pluginSource("nvidia", "api.js"), exports: ["buildNvidiaProvider"], }, { subpath: "ollama", - source: "../../extensions/ollama/runtime-api.js", - exportAll: true, + source: pluginSource("ollama", "runtime-api.js"), + exports: [ + "buildAssistantMessage", + "buildOllamaChatRequest", + "convertToOllamaMessages", + "createOllamaEmbeddingProvider", + "createConfiguredOllamaCompatNumCtxWrapper", + "createConfiguredOllamaCompatStreamWrapper", + "createConfiguredOllamaStreamFn", + "createOllamaStreamFn", + "DEFAULT_OLLAMA_EMBEDDING_MODEL", + "isOllamaCompatProvider", + "OLLAMA_NATIVE_BASE_URL", + "parseNdjsonStream", + "resolveOllamaBaseUrlForRun", + "resolveOllamaCompatNumCtxEnabled", + "shouldInjectOllamaCompatNumCtx", + "wrapOllamaCompatNumCtx", + ], + typeExports: ["OllamaEmbeddingClient", "OllamaEmbeddingProvider"], }, { subpath: "ollama-surface", - source: "../../extensions/ollama/api.js", + source: pluginSource("ollama", "api.js"), exports: [ "buildOllamaModelDefinition", "buildOllamaProvider", @@ -640,7 +795,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "openai", - source: "../../extensions/openai/api.js", + source: pluginSource("openai", "api.js"), exports: [ "applyOpenAIConfig", "applyOpenAIProviderConfig", @@ -657,7 +812,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "opencode", - source: "../../extensions/opencode/api.js", + source: pluginSource("opencode", "api.js"), exports: [ "applyOpencodeZenConfig", "applyOpencodeZenModelDefault", @@ -668,7 +823,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "opencode-go", - source: "../../extensions/opencode-go/api.js", + source: pluginSource("opencode-go", "api.js"), exports: [ "applyOpencodeGoConfig", "applyOpencodeGoModelDefault", @@ -678,18 +833,18 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "qianfan", - source: "../../extensions/qianfan/api.js", + source: pluginSource("qianfan", "api.js"), exports: ["QIANFAN_BASE_URL", "QIANFAN_DEFAULT_MODEL_ID", "buildQianfanProvider"], }, { subpath: "signal-account", - source: "../../extensions/signal/api.js", + source: pluginSource("signal", "api.js"), exports: ["resolveSignalAccount", "ResolvedSignalAccount"], typeExports: ["ResolvedSignalAccount"], }, { subpath: "signal-surface", - source: "../../extensions/signal/api.js", + source: pluginSource("signal", "api.js"), exports: [ "isSignalSenderAllowed", "listEnabledSignalAccounts", @@ -709,17 +864,58 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "provider-reasoning", - source: "../../extensions/ollama/api.js", + source: pluginSource("ollama", "api.js"), exports: ["isReasoningModelHeuristic"], }, { subpath: "speech-runtime", - source: "../../extensions/speech-core/runtime-api.js", - exportAll: true, + source: pluginSource("speech-core", "runtime-api.js"), + exports: [ + "_test", + "buildTtsSystemPromptHint", + "getLastTtsAttempt", + "getResolvedSpeechProviderConfig", + "getTtsMaxLength", + "getTtsProvider", + "isSummarizationEnabled", + "isTtsEnabled", + "isTtsProviderConfigured", + "listSpeechVoices", + "maybeApplyTtsToPayload", + "ResolvedTtsConfig", + "ResolvedTtsModelOverrides", + "resolveTtsAutoMode", + "resolveTtsConfig", + "resolveTtsPrefsPath", + "resolveTtsProviderOrder", + "setLastTtsAttempt", + "setSummarizationEnabled", + "setTtsAutoMode", + "setTtsEnabled", + "setTtsMaxLength", + "setTtsProvider", + "synthesizeSpeech", + "textToSpeech", + "textToSpeechTelephony", + "TtsDirectiveOverrides", + "TtsDirectiveParseResult", + "TtsResult", + "TtsSynthesisResult", + "TtsTelephonyResult", + ], + typeExports: [ + "ResolvedTtsConfig", + "ResolvedTtsModelOverrides", + "TtsDirectiveOverrides", + "TtsDirectiveParseResult", + "TtsResult", + "TtsSynthesisResult", + "TtsTelephonyResult", + ], }, { subpath: "sglang", - source: "../../extensions/sglang/api.js", + source: pluginSource("sglang", "api.js"), exports: [ "buildSglangProvider", "SGLANG_DEFAULT_API_KEY_ENV_VAR", @@ -730,7 +926,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "synthetic", - source: "../../extensions/synthetic/api.js", + source: pluginSource("synthetic", "api.js"), exports: [ "applySyntheticConfig", "applySyntheticProviderConfig", @@ -743,18 +939,18 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "slack-target-parser", - source: "../../extensions/slack/api.js", + source: pluginSource("slack", "api.js"), exports: ["parseSlackTarget", "resolveSlackChannelId"], }, { subpath: "slack-account", - source: "../../extensions/slack/api.js", + source: pluginSource("slack", "api.js"), exports: ["resolveSlackAccount", "ResolvedSlackAccount"], typeExports: ["ResolvedSlackAccount"], }, { subpath: "slack-runtime-surface", - source: "../../extensions/slack/runtime-api.js", + source: pluginSource("slack", "runtime-api.js"), exports: [ "handleSlackAction", "listSlackDirectoryGroupsLive", @@ -770,7 +966,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "slack-surface", - source: "../../extensions/slack/api.js", + source: pluginSource("slack", "api.js"), exports: [ "buildSlackThreadingToolContext", "createSlackWebClient", @@ -813,7 +1009,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "together", - source: "../../extensions/together/api.js", + source: pluginSource("together", "api.js"), exports: [ "applyTogetherConfig", "buildTogetherModelDefinition", @@ -825,7 +1021,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "venice", - source: "../../extensions/venice/api.js", + source: pluginSource("venice", "api.js"), exports: [ "buildVeniceModelDefinition", "buildVeniceProvider", @@ -837,18 +1033,18 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "telegram-account", - source: "../../extensions/telegram/api.js", + source: pluginSource("telegram", "api.js"), exports: ["resolveTelegramAccount", "ResolvedTelegramAccount"], typeExports: ["ResolvedTelegramAccount"], }, { subpath: "telegram-allow-from", - source: "../../extensions/telegram/api.js", + source: pluginSource("telegram", "api.js"), exports: ["isNumericTelegramUserId", "normalizeTelegramAllowFromEntry"], }, { subpath: "telegram-runtime-surface", - source: "../../extensions/telegram/runtime-api.js", + source: pluginSource("telegram", "runtime-api.js"), exports: [ "auditTelegramGroupMembership", "buildTelegramExecApprovalPendingPayload", @@ -883,7 +1079,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "telegram-surface", - source: "../../extensions/telegram/api.js", + source: pluginSource("telegram", "api.js"), exports: [ "buildBrowseProvidersButton", "buildModelsKeyboard", @@ -937,7 +1133,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "vercel-ai-gateway", - source: "../../extensions/vercel-ai-gateway/api.js", + source: pluginSource("vercel-ai-gateway", "api.js"), exports: [ "buildVercelAiGatewayProvider", "discoverVercelAiGatewayModels", @@ -953,7 +1149,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "volcengine", - source: "../../extensions/volcengine/api.js", + source: pluginSource("volcengine", "api.js"), exports: [ "buildDoubaoCodingProvider", "buildDoubaoModelDefinition", @@ -966,7 +1162,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "vllm", - source: "../../extensions/vllm/api.js", + source: pluginSource("vllm", "api.js"), exports: [ "buildVllmProvider", "VLLM_DEFAULT_API_KEY_ENV_VAR", @@ -977,7 +1173,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "xai", - source: "../../extensions/xai/api.js", + source: pluginSource("xai", "api.js"), exports: [ "applyXaiConfig", "applyXaiProviderConfig", @@ -1000,7 +1196,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "xiaomi", - source: "../../extensions/xiaomi/api.js", + source: pluginSource("xiaomi", "api.js"), exports: [ "applyXiaomiConfig", "applyXiaomiProviderConfig", @@ -1011,7 +1207,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "zai", - source: "../../extensions/zai/api.js", + source: pluginSource("zai", "api.js"), exports: [ "applyZaiConfig", "applyZaiProviderConfig", @@ -1025,12 +1221,15 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "whatsapp-targets", - source: "../../extensions/whatsapp/api.js", + source: pluginSource("whatsapp", "api.js"), exports: ["isWhatsAppGroupJid", "isWhatsAppUserTarget", "normalizeWhatsAppTarget"], }, { subpath: "whatsapp-surface", - source: "../../extensions/whatsapp/api.js", + source: pluginSource("whatsapp", "api.js"), + exportSources: { + DEFAULT_WEB_MEDIA_BYTES: pluginSource("whatsapp", "constants.js"), + }, exports: [ "DEFAULT_WEB_MEDIA_BYTES", "hasAnyWhatsAppAuth", @@ -1052,7 +1251,7 @@ export const GENERATED_PLUGIN_SDK_FACADES = [ }, { subpath: "zalo-setup", - source: "../../extensions/zalo/api.js", + source: pluginSource("zalo", "api.js"), exports: [ "evaluateZaloGroupAccess", "resolveZaloRuntimeGroupPolicy", @@ -1068,6 +1267,19 @@ export const GENERATED_PLUGIN_SDK_FACADES_BY_SUBPATH = Object.fromEntries( export const GENERATED_PLUGIN_SDK_FACADES_LABEL = "plugin-sdk-facades"; export const GENERATED_PLUGIN_SDK_FACADES_SCRIPT = "scripts/generate-plugin-sdk-facades.mjs"; +export const GENERATED_PLUGIN_SDK_FACADE_TYPES_OUTPUT = + "src/generated/plugin-sdk-facade-type-map.generated.ts"; + +function rewriteFacadeTypeImportSpecifier(sourcePath) { + if (sourcePath.startsWith("openclaw/plugin-source/")) { + const { dirName, artifactBasename } = normalizeFacadeSourceParts(sourcePath); + return `${BUNDLED_PLUGIN_SOURCE_RELATIVE_PREFIX}${dirName}/${artifactBasename}`; + } + if (sourcePath.startsWith(BUNDLED_PLUGIN_SOURCE_RELATIVE_PREFIX)) { + return sourcePath; + } + return sourcePath; +} const MODULE_RESOLUTION_OPTIONS = { allowJs: true, @@ -1081,6 +1293,55 @@ const MODULE_RESOLUTION_OPTIONS = { const MODULE_RESOLUTION_HOST = ts.createCompilerHost(MODULE_RESOLUTION_OPTIONS, true); const sourceExportKindsCache = new Map(); +function listFacadeEntrySourcePaths(entry) { + return Array.from(new Set([entry.source, ...Object.values(entry.exportSources ?? {})])); +} + +function buildFacadeSourceModuleKey(sourceIndex) { + return `source${sourceIndex + 1}`; +} + +function isPrimitiveTypeLike(type) { + if (type.isUnion()) { + return type.types.every((member) => isPrimitiveTypeLike(member)); + } + const primitiveFlags = + ts.TypeFlags.StringLike | + ts.TypeFlags.NumberLike | + ts.TypeFlags.BooleanLike | + ts.TypeFlags.BigIntLike | + ts.TypeFlags.ESSymbolLike | + ts.TypeFlags.Null | + ts.TypeFlags.Undefined | + ts.TypeFlags.Void; + return Boolean(type.flags & primitiveFlags); +} + +function isArrayTypeLike(checker, type) { + if (type.isUnion()) { + return type.types.every((member) => isArrayTypeLike(checker, member)); + } + return checker.isArrayType(type) || checker.isTupleType(type); +} + +function normalizeFacadeSourceParts(sourcePath) { + const pluginSourceMatch = /^openclaw\/plugin-source\/([^/]+)\/([^/]+)$/u.exec(sourcePath); + if (pluginSourceMatch) { + return { + dirName: pluginSourceMatch[1], + artifactBasename: pluginSourceMatch[2], + }; + } + const match = /^\.\.\/\.\.\/extensions\/([^/]+)\/([^/]+)$/u.exec(sourcePath); + if (!match) { + throw new Error(`Unsupported plugin-sdk facade source: ${sourcePath}`); + } + return { + dirName: match[1], + artifactBasename: match[2], + }; +} + function collectRuntimeApiPreExports(repoRoot, runtimeApiPath) { const absolutePath = path.join(repoRoot, runtimeApiPath); const sourceText = fs.readFileSync(absolutePath, "utf8"); @@ -1121,7 +1382,9 @@ function collectRuntimeApiPreExports(repoRoot, runtimeApiPath) { } function resolveFacadeSourceTypescriptPath(repoRoot, sourcePath) { - const absolutePath = path.join(repoRoot, sourcePath); + const absolutePath = sourcePath.startsWith("openclaw/plugin-source/") + ? path.resolve(repoRoot, "extensions", sourcePath.slice("openclaw/plugin-source/".length)) + : path.resolve(repoRoot, "src/plugin-sdk", sourcePath); const candidates = [absolutePath.replace(/\.js$/, ".ts"), absolutePath.replace(/\.js$/, ".tsx")]; return candidates.find((candidate) => fs.existsSync(candidate)); } @@ -1160,9 +1423,18 @@ function resolveFacadeSourceExportKinds(repoRoot, sourcePath) { const symbol = exported.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(exported) : exported; const flags = symbol.flags; + const declaration = + symbol.valueDeclaration ?? exported.valueDeclaration ?? exported.declarations?.[0]; + const typeAtLocation = declaration + ? checker.getTypeOfSymbolAtLocation(symbol, declaration) + : checker.getDeclaredTypeOfSymbol(symbol); exportKinds.set(exported.getName(), { type: Boolean(flags & ts.SymbolFlags.Type), value: Boolean(flags & ts.SymbolFlags.Value), + functionLike: Boolean(flags & (ts.SymbolFlags.Function | ts.SymbolFlags.Method)), + callable: typeAtLocation.getCallSignatures().length > 0, + arrayLike: isArrayTypeLike(checker, typeAtLocation), + primitiveLike: isPrimitiveTypeLike(typeAtLocation), }); } } @@ -1172,33 +1444,39 @@ function resolveFacadeSourceExportKinds(repoRoot, sourcePath) { } export function buildPluginSdkFacadeModule(entry, params = {}) { - if (entry.exportAll) { - return [ - `// Generated by ${GENERATED_PLUGIN_SDK_FACADES_SCRIPT}. Do not edit manually.`, - `export * from "${entry.source}";`, - "", - ].join("\n"); - } - const exportNames = entry.runtimeApiPreExportsPath - ? collectRuntimeApiPreExports(params.repoRoot, entry.runtimeApiPreExportsPath) - : entry.exports; + const sourceExportKinds = params.repoRoot + ? resolveFacadeSourceExportKinds(params.repoRoot, entry.source) + : new Map(); + const exportNames = entry.exportAll + ? Array.from(sourceExportKinds.keys()).toSorted((left, right) => left.localeCompare(right)) + : entry.runtimeApiPreExportsPath + ? collectRuntimeApiPreExports(params.repoRoot, entry.runtimeApiPreExportsPath) + : entry.exports; const explicitTypeExports = new Set(entry.typeExports ?? []); const valueExports = []; const typeExports = []; - const exportKinds = - params.repoRoot && exportNames?.length - ? resolveFacadeSourceExportKinds(params.repoRoot, entry.source) - : new Map(); + const valueExportsBySource = new Map(); + let needsLazyArrayHelper = false; + let needsLazyObjectHelper = false; for (const exportName of exportNames ?? []) { if (explicitTypeExports.has(exportName)) { continue; } - const kind = exportKinds.get(exportName); + const kind = sourceExportKinds.get(exportName); if (kind?.type && !kind.value) { typeExports.push(exportName); continue; } valueExports.push(exportName); + if (kind?.arrayLike) { + needsLazyArrayHelper = true; + } else if (!kind?.functionLike && !kind?.callable && !kind?.primitiveLike) { + needsLazyObjectHelper = true; + } + const sourcePath = entry.exportSources?.[exportName] ?? entry.source; + const exportsForSource = valueExportsBySource.get(sourcePath) ?? []; + exportsForSource.push(exportName); + valueExportsBySource.set(sourcePath, exportsForSource); } for (const typeExport of entry.typeExports ?? []) { if (!typeExports.includes(typeExport)) { @@ -1206,14 +1484,120 @@ export function buildPluginSdkFacadeModule(entry, params = {}) { } } const lines = [`// Generated by ${GENERATED_PLUGIN_SDK_FACADES_SCRIPT}. Do not edit manually.`]; + if (valueExports.length || typeExports.length) { + lines.push( + 'import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js";', + ); + lines.push(`type FacadeEntry = PluginSdkFacadeTypeMap[${JSON.stringify(entry.subpath)}];`); + lines.push('type FacadeModule = FacadeEntry["module"];'); + for (const [sourceIndex] of listFacadeEntrySourcePaths(entry).entries()) { + if (sourceIndex === 0) { + continue; + } + lines.push( + `type FacadeModule${sourceIndex + 1} = FacadeEntry["sourceModules"][${JSON.stringify(buildFacadeSourceModuleKey(sourceIndex))}]["module"];`, + ); + } + } if (valueExports.length) { - const exports = valueExports.join(",\n "); - lines.push("export {", ` ${exports},`, `} from "${entry.source}";`); + const runtimeImports = ["loadBundledPluginPublicSurfaceModuleSync"]; + if (needsLazyArrayHelper) { + runtimeImports.unshift("createLazyFacadeArrayValue"); + } + if (needsLazyObjectHelper) { + runtimeImports.unshift("createLazyFacadeObjectValue"); + } + lines.push(`import { ${runtimeImports.join(", ")} } from "./facade-runtime.js";`); + for (const [sourceIndex, sourcePath] of listFacadeEntrySourcePaths(entry).entries()) { + if (!valueExportsBySource.has(sourcePath)) { + continue; + } + const { dirName: sourceDirName, artifactBasename: sourceArtifactBasename } = + normalizeFacadeSourceParts(sourcePath); + const loaderSuffix = sourceIndex === 0 ? "" : String(sourceIndex + 1); + const moduleTypeName = sourceIndex === 0 ? "FacadeModule" : `FacadeModule${sourceIndex + 1}`; + lines.push(""); + lines.push(`function loadFacadeModule${loaderSuffix}(): ${moduleTypeName} {`); + lines.push(` return loadBundledPluginPublicSurfaceModuleSync<${moduleTypeName}>({`); + lines.push(` dirName: ${JSON.stringify(sourceDirName)},`); + lines.push(` artifactBasename: ${JSON.stringify(sourceArtifactBasename)},`); + lines.push(" });"); + lines.push("}"); + } + } + if (valueExports.length) { + const sourceIndexByPath = new Map( + listFacadeEntrySourcePaths(entry).map((sourcePath, index) => [sourcePath, index]), + ); + for (const exportName of valueExports) { + const kind = sourceExportKinds.get(exportName); + const sourcePath = entry.exportSources?.[exportName] ?? entry.source; + const sourceIndex = sourceIndexByPath.get(sourcePath) ?? 0; + const loaderSuffix = sourceIndex === 0 ? "" : String(sourceIndex + 1); + const moduleTypeName = sourceIndex === 0 ? "FacadeModule" : `FacadeModule${sourceIndex + 1}`; + if (kind?.functionLike || kind?.callable) { + lines.push( + `export const ${exportName}: ${moduleTypeName}[${JSON.stringify(exportName)}] = ((...args) =>`, + ); + lines.push( + ` loadFacadeModule${loaderSuffix}()[${JSON.stringify(exportName)}](...args)) as ${moduleTypeName}[${JSON.stringify(exportName)}];`, + ); + continue; + } + if (kind?.arrayLike) { + lines.push( + `export const ${exportName}: ${moduleTypeName}[${JSON.stringify(exportName)}] = createLazyFacadeArrayValue(() => loadFacadeModule${loaderSuffix}()[${JSON.stringify(exportName)}] as unknown as readonly unknown[]) as ${moduleTypeName}[${JSON.stringify(exportName)}];`, + ); + continue; + } + if (!kind?.primitiveLike) { + lines.push( + `export const ${exportName}: ${moduleTypeName}[${JSON.stringify(exportName)}] = createLazyFacadeObjectValue(() => loadFacadeModule${loaderSuffix}()[${JSON.stringify(exportName)}] as object) as ${moduleTypeName}[${JSON.stringify(exportName)}];`, + ); + continue; + } + lines.push( + `export const ${exportName}: ${moduleTypeName}[${JSON.stringify(exportName)}] = loadFacadeModule${loaderSuffix}()[${JSON.stringify(exportName)}];`, + ); + } } if (typeExports.length) { - const exportedTypes = typeExports.join(",\n "); - lines.push("export type {", ` ${exportedTypes},`, `} from "${entry.source}";`); + for (const exportedType of typeExports) { + lines.push( + `export type ${exportedType} = FacadeEntry["types"][${JSON.stringify(exportedType)}];`, + ); + } } lines.push(""); return lines.join("\n"); } + +export function buildPluginSdkFacadeTypeMapModule(entries) { + const lines = [`// Generated by ${GENERATED_PLUGIN_SDK_FACADES_SCRIPT}. Do not edit manually.`]; + lines.push("export interface PluginSdkFacadeTypeMap {"); + for (const entry of entries) { + const moduleImportPath = rewriteFacadeTypeImportSpecifier(entry.source); + lines.push(` ${JSON.stringify(entry.subpath)}: {`); + lines.push(` module: typeof import(${JSON.stringify(moduleImportPath)});`); + lines.push(" sourceModules: {"); + for (const [sourceIndex, sourcePath] of listFacadeEntrySourcePaths(entry).entries()) { + const rewrittenSourcePath = rewriteFacadeTypeImportSpecifier(sourcePath); + lines.push(` ${JSON.stringify(buildFacadeSourceModuleKey(sourceIndex))}: {`); + lines.push(` module: typeof import(${JSON.stringify(rewrittenSourcePath)});`); + lines.push(" };"); + } + lines.push(" };"); + lines.push(" types: {"); + for (const exportedType of entry.typeExports ?? []) { + const typeImportPath = rewriteFacadeTypeImportSpecifier(entry.source); + lines.push( + ` ${JSON.stringify(exportedType)}: import(${JSON.stringify(typeImportPath)}).${exportedType};`, + ); + } + lines.push(" };"); + lines.push(" };"); + } + lines.push("}"); + lines.push(""); + return lines.join("\n"); +} diff --git a/scripts/lib/ts-topology/scope.ts b/scripts/lib/ts-topology/scope.ts index 23f401cc944..dee2ef82320 100644 --- a/scripts/lib/ts-topology/scope.ts +++ b/scripts/lib/ts-topology/scope.ts @@ -1,5 +1,6 @@ import fs from "node:fs"; import path from "node:path"; +import { BUNDLED_PLUGIN_PATH_PREFIX } from "../bundled-plugin-paths.mjs"; import { pluginSdkEntrypoints } from "../plugin-sdk-entries.mjs"; import type { ConsumerScope, PublicEntrypoint, TopologyScope, UsageBucket } from "./types.js"; @@ -19,7 +20,7 @@ function isTestFile(relPath: string): boolean { } function classifyScope(relPath: string): ConsumerScope { - if (relPath.startsWith("extensions/")) { + if (relPath.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { return "extension"; } if (relPath.startsWith("packages/")) { @@ -74,7 +75,7 @@ function extractOwner(relPath: string): string | null { } function extractExtensionId(relPath: string): string | null { - if (!relPath.startsWith("extensions/")) { + if (!relPath.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { return null; } const parts = relPath.split("/"); diff --git a/scripts/profile-extension-memory.mjs b/scripts/profile-extension-memory.mjs index 0145ed832a4..3c7c1b50562 100644 --- a/scripts/profile-extension-memory.mjs +++ b/scripts/profile-extension-memory.mjs @@ -14,7 +14,7 @@ const RSS_MARKER = "__OPENCLAW_MAX_RSS_KB__="; function printHelp() { console.log(`Usage: node scripts/profile-extension-memory.mjs [options] -Profiles peak RSS for built extension entrypoints in dist/extensions/*/index.js. +Profiles peak RSS for built bundled plugin entrypoints. Run pnpm build first if you want stats for the latest source changes. Options: @@ -179,7 +179,7 @@ function findExtensionEntries(repoRoot) { .toSorted((a, b) => a.dir.localeCompare(b.dir)); if (entries.length === 0) { - throw new Error("No built extension entrypoints found under dist/extensions/*/index.js"); + throw new Error("No built bundled plugin entrypoints found in the dist plugin tree"); } return entries; } diff --git a/scripts/run-node.mjs b/scripts/run-node.mjs index 04c6f8fb2d4..aa85c55518b 100644 --- a/scripts/run-node.mjs +++ b/scripts/run-node.mjs @@ -5,12 +5,16 @@ import path from "node:path"; import process from "node:process"; import { pathToFileURL } from "node:url"; import { resolveGitHead, writeBuildStamp as writeDistBuildStamp } from "./build-stamp.mjs"; +import { + BUNDLED_PLUGIN_PATH_PREFIX, + BUNDLED_PLUGIN_ROOT_DIR, +} from "./lib/bundled-plugin-paths.mjs"; import { runRuntimePostBuild } from "./runtime-postbuild.mjs"; const buildScript = "scripts/tsdown-build.mjs"; const compilerArgs = [buildScript, "--no-clean"]; -const runNodeSourceRoots = ["src", "extensions"]; +const runNodeSourceRoots = ["src", BUNDLED_PLUGIN_ROOT_DIR]; const runNodeConfigFiles = ["tsconfig.json", "package.json", "tsdown.config.ts"]; export const runNodeWatchedPaths = [...runNodeSourceRoots, ...runNodeConfigFiles]; const extensionSourceFilePattern = /\.(?:[cm]?[jt]sx?)$/; @@ -40,8 +44,8 @@ export const isBuildRelevantRunNodePath = (repoPath) => { if (normalizedPath.startsWith("src/")) { return !isIgnoredSourcePath(normalizedPath.slice("src/".length)); } - if (normalizedPath.startsWith("extensions/")) { - return isBuildRelevantSourcePath(normalizedPath.slice("extensions/".length)); + if (normalizedPath.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { + return isBuildRelevantSourcePath(normalizedPath.slice(BUNDLED_PLUGIN_PATH_PREFIX.length)); } return false; }; @@ -62,8 +66,8 @@ export const isRestartRelevantRunNodePath = (repoPath) => { if (normalizedPath.startsWith("src/")) { return !isIgnoredSourcePath(normalizedPath.slice("src/".length)); } - if (normalizedPath.startsWith("extensions/")) { - return isRestartRelevantExtensionPath(normalizedPath.slice("extensions/".length)); + if (normalizedPath.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { + return isRestartRelevantExtensionPath(normalizedPath.slice(BUNDLED_PLUGIN_PATH_PREFIX.length)); } return false; }; diff --git a/scripts/test-extension.mjs b/scripts/test-extension.mjs index d11bcae1901..75aa30231b2 100644 --- a/scripts/test-extension.mjs +++ b/scripts/test-extension.mjs @@ -5,6 +5,10 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import { channelTestRoots } from "../vitest.channel-paths.mjs"; +import { + BUNDLED_PLUGIN_PATH_PREFIX, + BUNDLED_PLUGIN_ROOT_DIR, +} from "./lib/bundled-plugin-paths.mjs"; import { loadTestRunnerBehavior } from "./test-runner-manifest.mjs"; const __filename = fileURLToPath(import.meta.url); @@ -110,11 +114,11 @@ function listChangedPaths(base, head = "HEAD") { } function hasExtensionPackage(extensionId) { - return fs.existsSync(path.join(repoRoot, "extensions", extensionId, "package.json")); + return fs.existsSync(path.join(repoRoot, BUNDLED_PLUGIN_ROOT_DIR, extensionId, "package.json")); } export function listAvailableExtensionIds() { - const extensionsDir = path.join(repoRoot, "extensions"); + const extensionsDir = path.join(repoRoot, BUNDLED_PLUGIN_ROOT_DIR); if (!fs.existsSync(extensionsDir)) { return []; } @@ -136,7 +140,9 @@ export function detectChangedExtensionIds(changedPaths) { continue; } - const extensionMatch = relativePath.match(/^extensions\/([^/]+)(?:\/|$)/); + const extensionMatch = relativePath.match( + new RegExp(`^${BUNDLED_PLUGIN_PATH_PREFIX.replace("/", "\\/")}([^/]+)(?:/|$)`), + ); if (extensionMatch) { const extensionId = extensionMatch[1]; if (hasExtensionPackage(extensionId)) { @@ -179,20 +185,20 @@ function resolveExtensionDirectory(targetArg, cwd = process.cwd()) { return asGiven; } - const byName = path.join(repoRoot, "extensions", targetArg); + const byName = path.join(repoRoot, BUNDLED_PLUGIN_ROOT_DIR, targetArg); if (fs.existsSync(path.join(byName, "package.json"))) { return byName; } throw new Error( - `Unknown extension target "${targetArg}". Use an extension name like "slack" or a path under extensions/.`, + `Unknown extension target "${targetArg}". Use a plugin name like "slack" or a path inside the bundled plugin workspace tree.`, ); } let current = cwd; while (true) { if ( - normalizeRelative(path.relative(repoRoot, current)).startsWith("extensions/") && + normalizeRelative(path.relative(repoRoot, current)).startsWith(BUNDLED_PLUGIN_PATH_PREFIX) && fs.existsSync(path.join(current, "package.json")) ) { return current; @@ -205,7 +211,7 @@ function resolveExtensionDirectory(targetArg, cwd = process.cwd()) { } throw new Error( - "No extension target provided, and current working directory is not inside extensions/.", + "No extension target provided, and current working directory is not inside the bundled plugin workspace tree.", ); } diff --git a/scripts/test-live-acp-bind-docker.sh b/scripts/test-live-acp-bind-docker.sh index 1ce80a7f7af..fd9cba16de9 100644 --- a/scripts/test-live-acp-bind-docker.sh +++ b/scripts/test-live-acp-bind-docker.sh @@ -10,7 +10,7 @@ WORKSPACE_DIR="${OPENCLAW_WORKSPACE_DIR:-$HOME/.openclaw/workspace}" PROFILE_FILE="${OPENCLAW_PROFILE_FILE:-$HOME/.profile}" CLI_TOOLS_DIR="${OPENCLAW_DOCKER_CLI_TOOLS_DIR:-$HOME/.cache/openclaw/docker-cli-tools}" ACP_AGENT="${OPENCLAW_LIVE_ACP_BIND_AGENT:-claude}" -# Keep in sync with extensions/acpx/src/config.ts ACPX_PINNED_VERSION. +# Keep in sync with the pinned ACPX version used by the bundled ACP runtime. ACPX_VERSION="${OPENCLAW_DOCKER_ACPX_VERSION:-0.3.1}" case "$ACP_AGENT" in diff --git a/scripts/test-planner/catalog.mjs b/scripts/test-planner/catalog.mjs index ff105c52224..94cdceed779 100644 --- a/scripts/test-planner/catalog.mjs +++ b/scripts/test-planner/catalog.mjs @@ -2,6 +2,10 @@ import fs from "node:fs"; import path from "node:path"; import { channelTestPrefixes } from "../../vitest.channel-paths.mjs"; import { isUnitConfigTestFile } from "../../vitest.unit-paths.mjs"; +import { + BUNDLED_PLUGIN_PATH_PREFIX, + BUNDLED_PLUGIN_ROOT_DIR, +} from "../lib/bundled-plugin-paths.mjs"; import { dedupeFilesPreserveOrder, loadTestRunnerBehavior } from "../test-runner-manifest.mjs"; const baseConfigPrefixes = ["src/agents/", "src/auto-reply/", "src/commands/", "test/", "ui/"]; @@ -55,7 +59,7 @@ export function loadTestCatalog() { const allKnownTestFiles = [ ...new Set([ ...walkTestFiles("src"), - ...walkTestFiles("extensions"), + ...walkTestFiles(BUNDLED_PLUGIN_ROOT_DIR), ...walkTestFiles("packages"), ...walkTestFiles("test"), ...walkTestFiles(path.join("ui", "src", "ui")), @@ -105,7 +109,7 @@ export function loadTestCatalog() { surface = "e2e"; } else if (channelTestPrefixes.some((prefix) => normalizedFile.startsWith(prefix))) { surface = "channels"; - } else if (normalizedFile.startsWith("extensions/")) { + } else if (normalizedFile.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { surface = "extensions"; } else if (normalizedFile.startsWith("src/gateway/")) { surface = "gateway"; diff --git a/scripts/test-planner/planner.mjs b/scripts/test-planner/planner.mjs index 8145b0cefc8..80a22365d66 100644 --- a/scripts/test-planner/planner.mjs +++ b/scripts/test-planner/planner.mjs @@ -1,5 +1,6 @@ import path from "node:path"; import { isUnitConfigTestFile } from "../../vitest.unit-paths.mjs"; +import { BUNDLED_PLUGIN_PATH_PREFIX } from "../lib/bundled-plugin-paths.mjs"; import { loadChannelTimingManifest, loadExtensionTimingManifest, @@ -464,7 +465,9 @@ const buildDefaultUnits = (context, request) => { (file) => !new Set(unitFastExcludedFiles).has(file), ); const extensionSharedCandidateFiles = catalog.allKnownTestFiles.filter( - (file) => file.startsWith("extensions/") && !catalog.extensionForkIsolatedFileSet.has(file), + (file) => + file.startsWith(BUNDLED_PLUGIN_PATH_PREFIX) && + !catalog.extensionForkIsolatedFileSet.has(file), ); const channelSharedCandidateFiles = catalog.allKnownTestFiles.filter( (file) => @@ -1091,7 +1094,7 @@ const estimateTopLevelEntryDurationMs = (unit, context) => { if (context.catalog.channelTestPrefixes.some((prefix) => file.startsWith(prefix))) { return totalMs + 3_000; } - if (file.startsWith("extensions/")) { + if (file.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)) { return totalMs + 2_000; } return totalMs + 1_000; diff --git a/scripts/test-voicecall-closedloop.mjs b/scripts/test-voicecall-closedloop.mjs new file mode 100644 index 00000000000..b054ed059aa --- /dev/null +++ b/scripts/test-voicecall-closedloop.mjs @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +import { execFileSync } from "node:child_process"; +import { bundledPluginFile } from "./lib/bundled-plugin-paths.mjs"; + +const args = [ + "run", + bundledPluginFile("voice-call", "src/manager.test.ts"), + bundledPluginFile("voice-call", "src/media-stream.test.ts"), + "src/plugins/voice-call.plugin.test.ts", + "--maxWorkers=1", +]; + +execFileSync("vitest", args, { + stdio: "inherit", +}); diff --git a/scripts/tsdown-build.mjs b/scripts/tsdown-build.mjs index a86a1a01e8c..28bd5d9f242 100644 --- a/scripts/tsdown-build.mjs +++ b/scripts/tsdown-build.mjs @@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; +import { BUNDLED_PLUGIN_PATH_PREFIX } from "./lib/bundled-plugin-paths.mjs"; const logLevel = process.env.OPENCLAW_BUILD_VERBOSE ? "info" : "warn"; const extraArgs = process.argv.slice(2); @@ -49,7 +50,10 @@ function findFatalUnresolvedImport(lines) { } const normalizedLine = line.replace(ANSI_ESCAPE_RE, ""); - if (!normalizedLine.includes("extensions/") && !normalizedLine.includes("node_modules/")) { + if ( + !normalizedLine.includes(BUNDLED_PLUGIN_PATH_PREFIX) && + !normalizedLine.includes("node_modules/") + ) { return normalizedLine; } } diff --git a/scripts/write-official-channel-catalog.mjs b/scripts/write-official-channel-catalog.mjs index a913a86b212..3020949fe61 100644 --- a/scripts/write-official-channel-catalog.mjs +++ b/scripts/write-official-channel-catalog.mjs @@ -19,11 +19,9 @@ function toCatalogInstall(value, packageName) { if (!npmSpec) { return null; } - const localPath = trimString(install.localPath); const defaultChoice = trimString(install.defaultChoice); return { npmSpec, - ...(localPath ? { localPath } : {}), ...(defaultChoice === "npm" || defaultChoice === "local" ? { defaultChoice } : {}), }; } diff --git a/src/agents/cli-runner.test-support.ts b/src/agents/cli-runner.test-support.ts index c39506093d3..d38f23fbde5 100644 --- a/src/agents/cli-runner.test-support.ts +++ b/src/agents/cli-runner.test-support.ts @@ -1,15 +1,24 @@ import fs from "node:fs/promises"; import { beforeEach, vi } from "vitest"; -import { buildAnthropicCliBackend } from "../../extensions/anthropic/test-api.js"; -import { buildGoogleGeminiCliBackend } from "../../extensions/google/test-api.js"; -import { buildOpenAICodexCliBackend } from "../../extensions/openai/test-api.js"; import type { OpenClawConfig } from "../config/config.js"; import { createEmptyPluginRegistry } from "../plugins/registry.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; +import type { CliBackendPlugin } from "../plugins/types.js"; +import { loadBundledPluginTestApiSync } from "../test-utils/bundled-plugin-public-surface.js"; import { mergeMockedModule } from "../test-utils/vitest-module-mocks.js"; import type { EmbeddedContextFile } from "./pi-embedded-helpers.js"; import type { WorkspaceBootstrapFile } from "./workspace.js"; +const { buildAnthropicCliBackend } = loadBundledPluginTestApiSync<{ + buildAnthropicCliBackend: () => CliBackendPlugin; +}>("anthropic"); +const { buildGoogleGeminiCliBackend } = loadBundledPluginTestApiSync<{ + buildGoogleGeminiCliBackend: () => CliBackendPlugin; +}>("google"); +const { buildOpenAICodexCliBackend } = loadBundledPluginTestApiSync<{ + buildOpenAICodexCliBackend: () => CliBackendPlugin; +}>("openai"); + export const supervisorSpawnMock = vi.fn(); export const enqueueSystemEventMock = vi.fn(); export const requestHeartbeatNowMock = vi.fn(); diff --git a/src/agents/context.test.ts b/src/agents/context.test.ts index 98eb99d7295..3fdc3affdfc 100644 --- a/src/agents/context.test.ts +++ b/src/agents/context.test.ts @@ -5,7 +5,7 @@ import { applyDiscoveredContextWindows, resolveContextTokensForModel, } from "./context.js"; -import { createSessionManagerRuntimeRegistry } from "./pi-extensions/session-manager-runtime-registry.js"; +import { createSessionManagerRuntimeRegistry } from "./pi-hooks/session-manager-runtime-registry.js"; describe("applyDiscoveredContextWindows", () => { it("keeps the smallest context window when the same bare model id appears under multiple providers", () => { diff --git a/src/agents/models-config.providers.static.ts b/src/agents/models-config.providers.static.ts index 9d590bff14a..66637c6d504 100644 --- a/src/agents/models-config.providers.static.ts +++ b/src/agents/models-config.providers.static.ts @@ -1,7 +1,7 @@ import path from "node:path"; import { pathToFileURL } from "node:url"; import { - BUNDLED_PLUGIN_METADATA, + listBundledPluginMetadata, resolveBundledPluginPublicSurfacePath, } from "../plugins/bundled-plugin-metadata.js"; @@ -32,7 +32,7 @@ export function resolveBundledProviderCatalogEntries(params?: { } const entries: BundledProviderCatalogEntry[] = []; - for (const entry of BUNDLED_PLUGIN_METADATA) { + for (const entry of listBundledPluginMetadata({ rootDir })) { if (!entry.publicSurfaceArtifacts?.includes(PROVIDER_CATALOG_ARTIFACT_BASENAME)) { continue; } diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index d25e88ba0c1..fd197579db9 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -61,7 +61,7 @@ import { import { consumeCompactionSafeguardCancelReason, setCompactionSafeguardCancelReason, -} from "../pi-extensions/compaction-safeguard-runtime.js"; +} from "../pi-hooks/compaction-safeguard-runtime.js"; import { createPreparedEmbeddedPiSettingsManager } from "../pi-project-settings.js"; import { createOpenClawCodingTools } from "../pi-tools.js"; import { registerProviderStreamForModel } from "../provider-stream.js"; diff --git a/src/agents/pi-embedded-runner/extensions.test.ts b/src/agents/pi-embedded-runner/extensions.test.ts index e3f412cafd0..605816ae56b 100644 --- a/src/agents/pi-embedded-runner/extensions.test.ts +++ b/src/agents/pi-embedded-runner/extensions.test.ts @@ -2,8 +2,8 @@ import type { Api, Model } from "@mariozechner/pi-ai"; import type { SessionManager } from "@mariozechner/pi-coding-agent"; import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; -import { getCompactionSafeguardRuntime } from "../pi-extensions/compaction-safeguard-runtime.js"; -import compactionSafeguardExtension from "../pi-extensions/compaction-safeguard.js"; +import { getCompactionSafeguardRuntime } from "../pi-hooks/compaction-safeguard-runtime.js"; +import compactionSafeguardExtension from "../pi-hooks/compaction-safeguard.js"; import { buildEmbeddedExtensionFactories } from "./extensions.js"; function buildSafeguardFactories(cfg: OpenClawConfig) { diff --git a/src/agents/pi-embedded-runner/extensions.ts b/src/agents/pi-embedded-runner/extensions.ts index 08c1b0a3f70..dc33d3bf3a3 100644 --- a/src/agents/pi-embedded-runner/extensions.ts +++ b/src/agents/pi-embedded-runner/extensions.ts @@ -3,12 +3,12 @@ import type { ExtensionFactory, SessionManager } from "@mariozechner/pi-coding-a import type { OpenClawConfig } from "../../config/config.js"; import { resolveContextWindowInfo } from "../context-window-guard.js"; import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js"; -import { setCompactionSafeguardRuntime } from "../pi-extensions/compaction-safeguard-runtime.js"; -import compactionSafeguardExtension from "../pi-extensions/compaction-safeguard.js"; -import contextPruningExtension from "../pi-extensions/context-pruning.js"; -import { setContextPruningRuntime } from "../pi-extensions/context-pruning/runtime.js"; -import { computeEffectiveSettings } from "../pi-extensions/context-pruning/settings.js"; -import { makeToolPrunablePredicate } from "../pi-extensions/context-pruning/tools.js"; +import { setCompactionSafeguardRuntime } from "../pi-hooks/compaction-safeguard-runtime.js"; +import compactionSafeguardExtension from "../pi-hooks/compaction-safeguard.js"; +import contextPruningExtension from "../pi-hooks/context-pruning.js"; +import { setContextPruningRuntime } from "../pi-hooks/context-pruning/runtime.js"; +import { computeEffectiveSettings } from "../pi-hooks/context-pruning/settings.js"; +import { makeToolPrunablePredicate } from "../pi-hooks/context-pruning/tools.js"; import { ensurePiCompactionReserveTokens } from "../pi-settings.js"; import { isCacheTtlEligibleProvider, readLastCacheTtlTimestamp } from "./cache-ttl.js"; diff --git a/src/agents/pi-extensions/compaction-instructions.test.ts b/src/agents/pi-hooks/compaction-instructions.test.ts similarity index 100% rename from src/agents/pi-extensions/compaction-instructions.test.ts rename to src/agents/pi-hooks/compaction-instructions.test.ts diff --git a/src/agents/pi-extensions/compaction-instructions.ts b/src/agents/pi-hooks/compaction-instructions.ts similarity index 100% rename from src/agents/pi-extensions/compaction-instructions.ts rename to src/agents/pi-hooks/compaction-instructions.ts diff --git a/src/agents/pi-extensions/compaction-safeguard-quality.ts b/src/agents/pi-hooks/compaction-safeguard-quality.ts similarity index 100% rename from src/agents/pi-extensions/compaction-safeguard-quality.ts rename to src/agents/pi-hooks/compaction-safeguard-quality.ts diff --git a/src/agents/pi-extensions/compaction-safeguard-runtime.ts b/src/agents/pi-hooks/compaction-safeguard-runtime.ts similarity index 100% rename from src/agents/pi-extensions/compaction-safeguard-runtime.ts rename to src/agents/pi-hooks/compaction-safeguard-runtime.ts diff --git a/src/agents/pi-extensions/compaction-safeguard.test.ts b/src/agents/pi-hooks/compaction-safeguard.test.ts similarity index 100% rename from src/agents/pi-extensions/compaction-safeguard.test.ts rename to src/agents/pi-hooks/compaction-safeguard.test.ts diff --git a/src/agents/pi-extensions/compaction-safeguard.ts b/src/agents/pi-hooks/compaction-safeguard.ts similarity index 100% rename from src/agents/pi-extensions/compaction-safeguard.ts rename to src/agents/pi-hooks/compaction-safeguard.ts diff --git a/src/agents/pi-extensions/context-pruning.test.ts b/src/agents/pi-hooks/context-pruning.test.ts similarity index 100% rename from src/agents/pi-extensions/context-pruning.test.ts rename to src/agents/pi-hooks/context-pruning.test.ts diff --git a/src/agents/pi-extensions/context-pruning.ts b/src/agents/pi-hooks/context-pruning.ts similarity index 100% rename from src/agents/pi-extensions/context-pruning.ts rename to src/agents/pi-hooks/context-pruning.ts diff --git a/src/agents/pi-extensions/context-pruning/extension.ts b/src/agents/pi-hooks/context-pruning/extension.ts similarity index 100% rename from src/agents/pi-extensions/context-pruning/extension.ts rename to src/agents/pi-hooks/context-pruning/extension.ts diff --git a/src/agents/pi-extensions/context-pruning/pruner.test.ts b/src/agents/pi-hooks/context-pruning/pruner.test.ts similarity index 100% rename from src/agents/pi-extensions/context-pruning/pruner.test.ts rename to src/agents/pi-hooks/context-pruning/pruner.test.ts diff --git a/src/agents/pi-extensions/context-pruning/pruner.ts b/src/agents/pi-hooks/context-pruning/pruner.ts similarity index 100% rename from src/agents/pi-extensions/context-pruning/pruner.ts rename to src/agents/pi-hooks/context-pruning/pruner.ts diff --git a/src/agents/pi-extensions/context-pruning/runtime.ts b/src/agents/pi-hooks/context-pruning/runtime.ts similarity index 100% rename from src/agents/pi-extensions/context-pruning/runtime.ts rename to src/agents/pi-hooks/context-pruning/runtime.ts diff --git a/src/agents/pi-extensions/context-pruning/settings.ts b/src/agents/pi-hooks/context-pruning/settings.ts similarity index 100% rename from src/agents/pi-extensions/context-pruning/settings.ts rename to src/agents/pi-hooks/context-pruning/settings.ts diff --git a/src/agents/pi-extensions/context-pruning/tools.ts b/src/agents/pi-hooks/context-pruning/tools.ts similarity index 100% rename from src/agents/pi-extensions/context-pruning/tools.ts rename to src/agents/pi-hooks/context-pruning/tools.ts diff --git a/src/agents/pi-extensions/session-manager-runtime-registry.ts b/src/agents/pi-hooks/session-manager-runtime-registry.ts similarity index 100% rename from src/agents/pi-extensions/session-manager-runtime-registry.ts rename to src/agents/pi-hooks/session-manager-runtime-registry.ts diff --git a/src/agents/skills.test.ts b/src/agents/skills.test.ts index 91319dd612a..1a75d1e3841 100644 --- a/src/agents/skills.test.ts +++ b/src/agents/skills.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { installedPluginRoot } from "../../test/helpers/bundled-plugin-paths.js"; import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot, @@ -210,7 +211,13 @@ describe("buildWorkspaceSkillCommandSpecs", () => { ); expect( commands.find((entry) => entry.skillName === "workflows:review")?.sourceFilePath, - ).toContain("/.openclaw/extensions/compound-bundle/commands/workflows-review.md"); + ).toContain( + path.join( + installedPluginRoot(path.join(workspaceDir, ".openclaw"), "compound-bundle"), + "commands", + "workflows-review.md", + ), + ); }); }); diff --git a/src/auto-reply/reply.triggers.trigger-handling.test-harness.ts b/src/auto-reply/reply.triggers.trigger-handling.test-harness.ts index 7a0c670648e..f570d802af2 100644 --- a/src/auto-reply/reply.triggers.trigger-handling.test-harness.ts +++ b/src/auto-reply/reply.triggers.trigger-handling.test-harness.ts @@ -7,6 +7,7 @@ import { clearRuntimeAuthProfileStoreSnapshots } from "../agents/auth-profiles.j import { resetCliCredentialCachesForTest } from "../agents/cli-credentials.js"; import type { OpenClawConfig } from "../config/config.js"; import { resetProviderRuntimeHookCacheForTest } from "../plugins/provider-runtime.js"; +import { resolveRelativeBundledPluginPublicModuleId } from "../test-utils/bundled-plugin-public-surface.js"; // Avoid exporting vitest mock types (TS2742 under pnpm + d.ts emit). // oxlint-disable-next-line typescript/no-explicit-any @@ -158,12 +159,17 @@ const webSessionMocks = getSharedMocks("openclaw.trigger-handling.web-session-mo readWebSelfId: vi.fn().mockReturnValue({ e164: "+1999" }), })); +const whatsappRuntimeApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "whatsapp", + artifactBasename: "runtime-api.js", +}); + export function getWebSessionMocks(): AnyMocks { return webSessionMocks; } -const installWebSessionMock = () => - vi.doMock("../../extensions/whatsapp/runtime-api.js", () => webSessionMocks); +const installWebSessionMock = () => vi.doMock(whatsappRuntimeApiModuleId, () => webSessionMocks); installWebSessionMock(); diff --git a/src/auto-reply/reply/commands-acp/install-hints.test.ts b/src/auto-reply/reply/commands-acp/install-hints.test.ts index 46354f7f080..ceb931c07af 100644 --- a/src/auto-reply/reply/commands-acp/install-hints.test.ts +++ b/src/auto-reply/reply/commands-acp/install-hints.test.ts @@ -34,8 +34,7 @@ describe("ACP install hints", () => { const cfg = withAcpConfig({ backend: "acpx" }); const hint = resolveAcpInstallCommandHint(cfg); - expect(hint).toContain("openclaw plugins install "); - expect(hint).toContain(path.join("extensions", "acpx")); + expect(hint).toBe(`openclaw plugins install ${path.join(tempRoot, "extensions", "acpx")}`); }); it("falls back to scoped install hint for acpx when local extension is absent", () => { diff --git a/src/auto-reply/reply/commands-acp/install-hints.ts b/src/auto-reply/reply/commands-acp/install-hints.ts index 58b4b387c74..ac2fa68d83a 100644 --- a/src/auto-reply/reply/commands-acp/install-hints.ts +++ b/src/auto-reply/reply/commands-acp/install-hints.ts @@ -1,6 +1,8 @@ import { existsSync } from "node:fs"; import path from "node:path"; import type { OpenClawConfig } from "../../../config/config.js"; +import { resolveBundledPluginWorkspaceSourcePath } from "../../../plugins/bundled-plugin-metadata.js"; +import { resolveBundledPluginInstallCommandHint } from "../../../plugins/bundled-sources.js"; export function resolveConfiguredAcpBackendId(cfg: OpenClawConfig): string { return cfg.acp?.backend?.trim() || "acpx"; @@ -11,11 +13,30 @@ export function resolveAcpInstallCommandHint(cfg: OpenClawConfig): string { if (configured) { return configured; } + const workspaceDir = process.cwd(); const backendId = resolveConfiguredAcpBackendId(cfg).toLowerCase(); if (backendId === "acpx") { - const localPath = path.resolve(process.cwd(), "extensions/acpx"); - if (existsSync(localPath)) { - return `openclaw plugins install ${localPath}`; + const workspaceLocalPath = resolveBundledPluginWorkspaceSourcePath({ + rootDir: workspaceDir, + pluginId: backendId, + }); + if (workspaceLocalPath && existsSync(workspaceLocalPath)) { + return `openclaw plugins install ${workspaceLocalPath}`; + } + const bundledInstallHint = resolveBundledPluginInstallCommandHint({ + pluginId: backendId, + workspaceDir, + }); + if (bundledInstallHint) { + const localPath = bundledInstallHint.replace(/^openclaw plugins install /u, ""); + const resolvedLocalPath = path.resolve(localPath); + const relativeToWorkspace = path.relative(workspaceDir, resolvedLocalPath); + const belongsToWorkspace = + relativeToWorkspace.length === 0 || + (!relativeToWorkspace.startsWith("..") && !path.isAbsolute(relativeToWorkspace)); + if (belongsToWorkspace && existsSync(resolvedLocalPath)) { + return bundledInstallHint; + } } return "openclaw plugins install acpx"; } diff --git a/src/auto-reply/reply/commands-plugins.toggle.test.ts b/src/auto-reply/reply/commands-plugins.toggle.test.ts index 17702e2e911..cf6711cb765 100644 --- a/src/auto-reply/reply/commands-plugins.toggle.test.ts +++ b/src/auto-reply/reply/commands-plugins.toggle.test.ts @@ -1,6 +1,9 @@ import { afterEach, describe, expect, it, vi } from "vitest"; +import { installedPluginRoot } from "../../../test/helpers/bundled-plugin-paths.js"; import { createPluginRecord, createPluginStatusReport } from "../../plugins/status.test-helpers.js"; +const WORKSPACE_PLUGIN_ROOT = installedPluginRoot("/tmp/workspace/.openclaw", "superpowers"); + const { readConfigFileSnapshotMock, validateConfigObjectWithPluginsMock, @@ -70,7 +73,7 @@ describe("handleCommands /plugins toggle", () => { createPluginRecord({ id: "superpowers", format: "bundle", - source: "/tmp/workspace/.openclaw/extensions/superpowers", + source: WORKSPACE_PLUGIN_ROOT, enabled: false, status: "disabled", }), @@ -110,7 +113,7 @@ describe("handleCommands /plugins toggle", () => { createPluginRecord({ id: "superpowers", format: "bundle", - source: "/tmp/workspace/.openclaw/extensions/superpowers", + source: WORKSPACE_PLUGIN_ROOT, enabled: true, }), ], diff --git a/src/channels/chat-meta.ts b/src/channels/chat-meta.ts index eaf42510e1e..70a22fea1d4 100644 --- a/src/channels/chat-meta.ts +++ b/src/channels/chat-meta.ts @@ -1,4 +1,4 @@ -import { GENERATED_BUNDLED_PLUGIN_METADATA } from "../plugins/bundled-plugin-metadata.generated.js"; +import { listBundledPluginMetadata } from "../plugins/bundled-plugin-metadata.js"; import type { PluginPackageChannel } from "../plugins/manifest.js"; import { CHAT_CHANNEL_ORDER, type ChatChannelId } from "./ids.js"; import type { ChannelMeta } from "./plugins/types.js"; @@ -64,7 +64,7 @@ function toChatChannelMeta(params: { function buildChatChannelMetaById(): Record { const entries = new Map(); - for (const entry of GENERATED_BUNDLED_PLUGIN_METADATA) { + for (const entry of listBundledPluginMetadata()) { const channel = entry.packageManifest && "channel" in entry.packageManifest ? entry.packageManifest.channel diff --git a/src/channels/plugins/bundled.shape-guard.test.ts b/src/channels/plugins/bundled.shape-guard.test.ts index edfe417aa50..90bc4bcbd8b 100644 --- a/src/channels/plugins/bundled.shape-guard.test.ts +++ b/src/channels/plugins/bundled.shape-guard.test.ts @@ -1,20 +1,30 @@ import { afterEach, describe, expect, it, vi } from "vitest"; afterEach(() => { - vi.doUnmock("../../generated/bundled-channel-entries.generated.js"); + vi.doUnmock("../../plugins/discovery.js"); + vi.doUnmock("../../plugins/manifest-registry.js"); vi.resetModules(); }); describe("bundled channel entry shape guards", () => { - it("treats a missing generated bundled entry export as empty", async () => { + it("treats missing bundled discovery results as empty", async () => { vi.resetModules(); - vi.doMock("../../generated/bundled-channel-entries.generated.js", () => ({ - GENERATED_BUNDLED_CHANNEL_ENTRIES: undefined, + vi.doMock("../../plugins/discovery.js", () => ({ + discoverOpenClawPlugins: () => ({ + candidates: [], + diagnostics: [], + }), + })); + vi.doMock("../../plugins/manifest-registry.js", () => ({ + loadPluginManifestRegistry: () => ({ + plugins: [], + diagnostics: [], + }), })); const bundled = await import("./bundled.js"); - expect(bundled.bundledChannelPlugins).toEqual([]); - expect(bundled.bundledChannelSetupPlugins).toEqual([]); + expect(bundled.listBundledChannelPlugins()).toEqual([]); + expect(bundled.listBundledChannelSetupPlugins()).toEqual([]); }); }); diff --git a/src/channels/plugins/bundled.ts b/src/channels/plugins/bundled.ts index 661ee77b845..f7a8e97b17d 100644 --- a/src/channels/plugins/bundled.ts +++ b/src/channels/plugins/bundled.ts @@ -1,5 +1,15 @@ -import { GENERATED_BUNDLED_CHANNEL_ENTRIES } from "../../generated/bundled-channel-entries.generated.js"; +import fs from "node:fs"; +import { createJiti } from "jiti"; +import { openBoundaryFileSync } from "../../infra/boundary-file-read.js"; +import { createSubsystemLogger } from "../../logging/subsystem.js"; +import { discoverOpenClawPlugins } from "../../plugins/discovery.js"; +import { loadPluginManifestRegistry } from "../../plugins/manifest-registry.js"; import type { PluginRuntime } from "../../plugins/runtime/types.js"; +import { + buildPluginLoaderAliasMap, + buildPluginLoaderJitiOptions, + shouldPreferNativeJiti, +} from "../../plugins/sdk-alias.js"; import type { ChannelId, ChannelPlugin } from "./types.js"; type GeneratedBundledChannelEntry = { @@ -13,37 +23,150 @@ type GeneratedBundledChannelEntry = { }; }; -function isGeneratedBundledChannelEntry(value: unknown): value is GeneratedBundledChannelEntry { - if (!value || typeof value !== "object") { - return false; +const log = createSubsystemLogger("channels"); + +function resolveChannelPluginModuleEntry( + moduleExport: unknown, +): GeneratedBundledChannelEntry["entry"] | null { + const resolved = + moduleExport && + typeof moduleExport === "object" && + "default" in (moduleExport as Record) + ? (moduleExport as { default: unknown }).default + : moduleExport; + if (!resolved || typeof resolved !== "object") { + return null; } - const record = value as { - id?: unknown; - entry?: { - channelPlugin?: { id?: unknown }; - setChannelRuntime?: unknown; - }; - setupEntry?: { plugin?: { id?: unknown } }; + const record = resolved as { + channelPlugin?: unknown; + setChannelRuntime?: unknown; + }; + if (!record.channelPlugin || typeof record.channelPlugin !== "object") { + return null; + } + return { + channelPlugin: record.channelPlugin as ChannelPlugin, + ...(typeof record.setChannelRuntime === "function" + ? { setChannelRuntime: record.setChannelRuntime as (runtime: PluginRuntime) => void } + : {}), }; - return typeof record.id === "string" && typeof record.entry?.channelPlugin?.id === "string"; } -const generatedBundledChannelEntries = ( - Array.isArray(GENERATED_BUNDLED_CHANNEL_ENTRIES) - ? GENERATED_BUNDLED_CHANNEL_ENTRIES.filter(isGeneratedBundledChannelEntry) - : [] -) as readonly GeneratedBundledChannelEntry[]; +function resolveChannelSetupModuleEntry( + moduleExport: unknown, +): GeneratedBundledChannelEntry["setupEntry"] | null { + const resolved = + moduleExport && + typeof moduleExport === "object" && + "default" in (moduleExport as Record) + ? (moduleExport as { default: unknown }).default + : moduleExport; + if (!resolved || typeof resolved !== "object") { + return null; + } + const record = resolved as { + plugin?: unknown; + }; + if (!record.plugin || typeof record.plugin !== "object") { + return null; + } + return { + plugin: record.plugin as ChannelPlugin, + }; +} -export const bundledChannelPlugins = generatedBundledChannelEntries.map( - ({ entry }) => entry.channelPlugin, -); +function createModuleLoader() { + const jitiLoaders = new Map>(); -export const bundledChannelSetupPlugins = generatedBundledChannelEntries.flatMap( - ({ setupEntry }) => { - const plugin = setupEntry?.plugin; - return plugin ? [plugin] : []; - }, -); + return (modulePath: string) => { + const tryNative = shouldPreferNativeJiti(modulePath); + const aliasMap = buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url); + const cacheKey = JSON.stringify({ + tryNative, + aliasMap: Object.entries(aliasMap).toSorted(([left], [right]) => left.localeCompare(right)), + }); + const cached = jitiLoaders.get(cacheKey); + if (cached) { + return cached; + } + const loader = createJiti(import.meta.url, { + ...buildPluginLoaderJitiOptions(aliasMap), + tryNative, + }); + jitiLoaders.set(cacheKey, loader); + return loader; + }; +} + +const loadModule = createModuleLoader(); + +function loadBundledModule(modulePath: string, rootDir: string): unknown { + const opened = openBoundaryFileSync({ + absolutePath: modulePath, + rootPath: rootDir, + boundaryLabel: "plugin root", + rejectHardlinks: false, + skipLexicalRootCheck: true, + }); + if (!opened.ok) { + throw new Error("plugin entry path escapes plugin root or fails alias checks"); + } + const safePath = opened.path; + fs.closeSync(opened.fd); + return loadModule(safePath)(safePath); +} + +function loadGeneratedBundledChannelEntries(): readonly GeneratedBundledChannelEntry[] { + const discovery = discoverOpenClawPlugins({ cache: false }); + const manifestRegistry = loadPluginManifestRegistry({ + cache: false, + config: {}, + candidates: discovery.candidates, + diagnostics: discovery.diagnostics, + }); + const manifestByRoot = new Map( + manifestRegistry.plugins.map((plugin) => [plugin.rootDir, plugin] as const), + ); + const seenIds = new Set(); + const entries: GeneratedBundledChannelEntry[] = []; + + for (const candidate of discovery.candidates) { + const manifest = manifestByRoot.get(candidate.rootDir); + if (!manifest || manifest.origin !== "bundled" || manifest.channels.length === 0) { + continue; + } + if (seenIds.has(manifest.id)) { + continue; + } + seenIds.add(manifest.id); + + try { + const entry = resolveChannelPluginModuleEntry( + loadBundledModule(candidate.source, candidate.rootDir), + ); + if (!entry) { + log.warn( + `[channels] bundled channel entry ${manifest.id} missing channelPlugin export; skipping`, + ); + continue; + } + const setupEntry = manifest.setupSource + ? resolveChannelSetupModuleEntry(loadBundledModule(manifest.setupSource, candidate.rootDir)) + : null; + entries.push({ + id: manifest.id, + entry, + ...(setupEntry ? { setupEntry } : {}), + }); + } catch (error) { + log.warn( + `[channels] failed to load bundled channel ${manifest.id} from ${candidate.source}: ${String(error)}`, + ); + } + } + + return entries; +} function buildBundledChannelPluginsById(plugins: readonly ChannelPlugin[]) { const byId = new Map(); @@ -56,20 +179,60 @@ function buildBundledChannelPluginsById(plugins: readonly ChannelPlugin[]) { return byId; } -const bundledChannelPluginsById = buildBundledChannelPluginsById(bundledChannelPlugins); +type BundledChannelState = { + entries: readonly GeneratedBundledChannelEntry[]; + plugins: readonly ChannelPlugin[]; + setupPlugins: readonly ChannelPlugin[]; + pluginsById: Map; + runtimeSettersById: Map< + ChannelId, + NonNullable + >; +}; -const bundledChannelRuntimeSettersById = new Map< - ChannelId, - NonNullable ->(); -for (const { entry } of generatedBundledChannelEntries) { - if (entry.setChannelRuntime) { - bundledChannelRuntimeSettersById.set(entry.channelPlugin.id, entry.setChannelRuntime); +let cachedBundledChannelState: BundledChannelState | null = null; + +function getBundledChannelState(): BundledChannelState { + if (cachedBundledChannelState) { + return cachedBundledChannelState; } + + const entries = loadGeneratedBundledChannelEntries(); + const plugins = entries.map(({ entry }) => entry.channelPlugin); + const setupPlugins = entries.flatMap(({ setupEntry }) => { + const plugin = setupEntry?.plugin; + return plugin ? [plugin] : []; + }); + const runtimeSettersById = new Map< + ChannelId, + NonNullable + >(); + for (const { entry } of entries) { + if (entry.setChannelRuntime) { + runtimeSettersById.set(entry.channelPlugin.id, entry.setChannelRuntime); + } + } + + cachedBundledChannelState = { + entries, + plugins, + setupPlugins, + pluginsById: buildBundledChannelPluginsById(plugins), + runtimeSettersById, + }; + return cachedBundledChannelState; +} + +export function listBundledChannelPlugins(): readonly ChannelPlugin[] { + return getBundledChannelState().plugins; +} + +export function listBundledChannelSetupPlugins(): readonly ChannelPlugin[] { + return getBundledChannelState().setupPlugins; } export function getBundledChannelPlugin(id: ChannelId): ChannelPlugin | undefined { - return bundledChannelPluginsById.get(id); + return getBundledChannelState().pluginsById.get(id); } export function requireBundledChannelPlugin(id: ChannelId): ChannelPlugin { @@ -81,7 +244,7 @@ export function requireBundledChannelPlugin(id: ChannelId): ChannelPlugin { } export function setBundledChannelRuntime(id: ChannelId, runtime: PluginRuntime): void { - const setter = bundledChannelRuntimeSettersById.get(id); + const setter = getBundledChannelState().runtimeSettersById.get(id); if (!setter) { throw new Error(`missing bundled channel runtime setter: ${id}`); } diff --git a/src/channels/plugins/contracts/registry.ts b/src/channels/plugins/contracts/registry.ts index 7a440e8b357..0392898b2cf 100644 --- a/src/channels/plugins/contracts/registry.ts +++ b/src/channels/plugins/contracts/registry.ts @@ -2,19 +2,16 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { expect, vi } from "vitest"; -import { createBlueBubblesConversationBindingManager } from "../../../../extensions/bluebubbles/api.js"; -import { createIMessageConversationBindingManager } from "../../../../extensions/imessage/api.js"; import type { OpenClawConfig } from "../../../config/config.js"; import { getSessionBindingService, type SessionBindingCapabilities, type SessionBindingRecord, } from "../../../infra/outbound/session-binding-service.js"; -import { - discordThreadBindingTesting, - createDiscordThreadBindingManager, -} from "../../../plugin-sdk/discord.js"; +import { createBlueBubblesConversationBindingManager } from "../../../plugin-sdk/bluebubbles.js"; +import { createDiscordThreadBindingManager } from "../../../plugin-sdk/discord.js"; import { createFeishuThreadBindingManager } from "../../../plugin-sdk/feishu.js"; +import { createIMessageConversationBindingManager } from "../../../plugin-sdk/imessage.js"; import { listLineAccountIds, resolveDefaultLineAccountId, @@ -26,8 +23,9 @@ import { setMatrixRuntime, } from "../../../plugin-sdk/matrix.js"; import { createTelegramThreadBindingManager } from "../../../plugin-sdk/telegram-runtime.js"; +import { loadBundledPluginTestApiSync } from "../../../test-utils/bundled-plugin-public-surface.js"; import { - bundledChannelPlugins, + listBundledChannelPlugins, requireBundledChannelPlugin, setBundledChannelRuntime, } from "../bundled.js"; @@ -39,6 +37,16 @@ import { type SessionBindingContractChannelId, } from "./manifest.js"; +const { discordThreadBindingTesting } = loadBundledPluginTestApiSync<{ + discordThreadBindingTesting: { + resetThreadBindingsForTests: () => void; + }; +}>("discord"); + +function buildBundledPluginModuleId(pluginId: string, artifactBasename: string): string { + return ["..", "..", "..", "..", "extensions", pluginId, artifactBasename].join("/"); +} + type PluginContractEntry = { id: string; plugin: Pick; @@ -210,10 +218,9 @@ setBundledChannelRuntime("line", { }, } as never); -vi.mock("../../../../extensions/matrix/runtime-api.js", async () => { - const actual = await vi.importActual< - typeof import("../../../../extensions/matrix/runtime-api.js") - >("../../../../extensions/matrix/runtime-api.js"); +vi.mock(buildBundledPluginModuleId("matrix", "runtime-api.js"), async () => { + const matrixRuntimeApiModuleId = buildBundledPluginModuleId("matrix", "runtime-api.js"); + const actual = await vi.importActual(matrixRuntimeApiModuleId); return { ...actual, sendMessageMatrix: sendMessageMatrixMock, @@ -252,7 +259,7 @@ async function createContractMatrixThreadBindingManager() { }); } -export const pluginContractRegistry: PluginContractEntry[] = bundledChannelPlugins.map( +export const pluginContractRegistry: PluginContractEntry[] = listBundledChannelPlugins().map( (plugin) => ({ id: plugin.id, plugin, @@ -607,7 +614,7 @@ export const statusContractRegistry: StatusContractEntry[] = [ }, ]; -export const surfaceContractRegistry: SurfaceContractEntry[] = bundledChannelPlugins.map( +export const surfaceContractRegistry: SurfaceContractEntry[] = listBundledChannelPlugins().map( (plugin) => ({ id: plugin.id, plugin, diff --git a/src/channels/plugins/setup-registry.ts b/src/channels/plugins/setup-registry.ts index bbb82022da9..00ccea0765a 100644 --- a/src/channels/plugins/setup-registry.ts +++ b/src/channels/plugins/setup-registry.ts @@ -3,7 +3,7 @@ import { requireActivePluginRegistry, } from "../../plugins/runtime.js"; import { CHAT_CHANNEL_ORDER, type ChatChannelId } from "../registry.js"; -import { bundledChannelSetupPlugins } from "./bundled.js"; +import { listBundledChannelSetupPlugins } from "./bundled.js"; import type { ChannelId, ChannelPlugin } from "./types.js"; type CachedChannelSetupPlugins = { @@ -20,7 +20,7 @@ const EMPTY_CHANNEL_SETUP_CACHE: CachedChannelSetupPlugins = { let cachedChannelSetupPlugins = EMPTY_CHANNEL_SETUP_CACHE; -function dedupeSetupPlugins(plugins: ChannelPlugin[]): ChannelPlugin[] { +function dedupeSetupPlugins(plugins: readonly ChannelPlugin[]): ChannelPlugin[] { const seen = new Set(); const resolved: ChannelPlugin[] = []; for (const plugin of plugins) { @@ -34,7 +34,7 @@ function dedupeSetupPlugins(plugins: ChannelPlugin[]): ChannelPlugin[] { return resolved; } -function sortChannelSetupPlugins(plugins: ChannelPlugin[]): ChannelPlugin[] { +function sortChannelSetupPlugins(plugins: readonly ChannelPlugin[]): ChannelPlugin[] { return dedupeSetupPlugins(plugins).toSorted((a, b) => { const indexA = CHAT_CHANNEL_ORDER.indexOf(a.id as ChatChannelId); const indexB = CHAT_CHANNEL_ORDER.indexOf(b.id as ChatChannelId); @@ -57,7 +57,7 @@ function resolveCachedChannelSetupPlugins(): CachedChannelSetupPlugins { const registryPlugins = (registry.channelSetups ?? []).map((entry) => entry.plugin); const sorted = sortChannelSetupPlugins( - registryPlugins.length > 0 ? registryPlugins : bundledChannelSetupPlugins, + registryPlugins.length > 0 ? registryPlugins : listBundledChannelSetupPlugins(), ); const byId = new Map(); for (const plugin of sorted) { diff --git a/src/channels/plugins/setup-wizard-helpers.test.ts b/src/channels/plugins/setup-wizard-helpers.test.ts index 4a2eb56b204..8c9d1131e77 100644 --- a/src/channels/plugins/setup-wizard-helpers.test.ts +++ b/src/channels/plugins/setup-wizard-helpers.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest"; import { resolveSetupWizardAllowFromEntries, resolveSetupWizardGroupAllowlist, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import type { OpenClawConfig } from "../../config/config.js"; import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js"; import { diff --git a/src/channels/plugins/setup-wizard-proxy.test.ts b/src/channels/plugins/setup-wizard-proxy.test.ts index 480ecf5ceb3..701fb4e5296 100644 --- a/src/channels/plugins/setup-wizard-proxy.test.ts +++ b/src/channels/plugins/setup-wizard-proxy.test.ts @@ -5,7 +5,7 @@ import { resolveSetupWizardGroupAllowlist, runSetupWizardFinalize, runSetupWizardPrepare, -} from "../../../test/helpers/extensions/setup-wizard.js"; +} from "../../../test/helpers/plugins/setup-wizard.js"; import { createAllowlistSetupWizardProxy, createDelegatedFinalize, diff --git a/src/channels/plugins/types.core.ts b/src/channels/plugins/types.core.ts index a941f5f2ec2..c4126f1053a 100644 --- a/src/channels/plugins/types.core.ts +++ b/src/channels/plugins/types.core.ts @@ -334,7 +334,7 @@ export type ChannelThreadingAdapter = { allowExplicitReplyTagsWhenOff?: boolean; /** * Deprecated alias for allowExplicitReplyTagsWhenOff. - * Kept for compatibility with older extensions/docks. + * Kept for compatibility with older plugin surfaces. */ allowTagsWhenOff?: boolean; buildToolContext?: (params: { diff --git a/src/cli/command-secret-resolution.coverage.test.ts b/src/cli/command-secret-resolution.coverage.test.ts index e31d10df28a..476c7b1698d 100644 --- a/src/cli/command-secret-resolution.coverage.test.ts +++ b/src/cli/command-secret-resolution.coverage.test.ts @@ -1,8 +1,9 @@ import { describe, expect, it } from "vitest"; +import { bundledPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; import { readCommandSource } from "./command-source.test-helpers.js"; const SECRET_TARGET_CALLSITES = [ - "extensions/memory-core/src/cli.runtime.ts", + bundledPluginFile("memory-core", "src/cli.runtime.ts"), "src/cli/qr-cli.ts", "src/commands/agent.ts", "src/commands/channels/resolve.ts", diff --git a/src/cli/npm-resolution.test.ts b/src/cli/npm-resolution.test.ts index e33e897c61b..90650834aa0 100644 --- a/src/cli/npm-resolution.test.ts +++ b/src/cli/npm-resolution.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import { installedPluginRoot } from "../../test/helpers/bundled-plugin-paths.js"; import { buildNpmInstallRecordFields, logPinnedNpmSpecMessages, @@ -8,6 +9,9 @@ import { resolvePinnedNpmSpec, } from "./npm-resolution.js"; +const CLI_STATE_ROOT = "/tmp/openclaw"; +const ALPHA_INSTALL_PATH = installedPluginRoot(CLI_STATE_ROOT, "alpha"); + describe("npm-resolution helpers", () => { it("keeps original spec when pin is disabled", () => { const result = resolvePinnedNpmSpec({ @@ -67,7 +71,7 @@ describe("npm-resolution helpers", () => { expect( buildNpmInstallRecordFields({ spec: "@openclaw/plugin-alpha@1.2.3", - installPath: "/tmp/openclaw/extensions/alpha", + installPath: ALPHA_INSTALL_PATH, version: "1.2.3", resolution: { name: "@openclaw/plugin-alpha", @@ -79,7 +83,7 @@ describe("npm-resolution helpers", () => { ).toEqual({ source: "npm", spec: "@openclaw/plugin-alpha@1.2.3", - installPath: "/tmp/openclaw/extensions/alpha", + installPath: ALPHA_INSTALL_PATH, version: "1.2.3", resolvedName: "@openclaw/plugin-alpha", resolvedVersion: "1.2.3", @@ -112,7 +116,7 @@ describe("npm-resolution helpers", () => { const record = resolvePinnedNpmInstallRecord({ rawSpec: "@openclaw/plugin-alpha@latest", pin: true, - installPath: "/tmp/openclaw/extensions/alpha", + installPath: ALPHA_INSTALL_PATH, version: "1.2.3", resolution: { name: "@openclaw/plugin-alpha", @@ -126,7 +130,7 @@ describe("npm-resolution helpers", () => { expect(record).toEqual({ source: "npm", spec: "@openclaw/plugin-alpha@1.2.3", - installPath: "/tmp/openclaw/extensions/alpha", + installPath: ALPHA_INSTALL_PATH, version: "1.2.3", resolvedName: "@openclaw/plugin-alpha", resolvedVersion: "1.2.3", @@ -144,7 +148,7 @@ describe("npm-resolution helpers", () => { const record = resolvePinnedNpmInstallRecordForCli( "@openclaw/plugin-alpha@latest", true, - "/tmp/openclaw/extensions/alpha", + ALPHA_INSTALL_PATH, "1.2.3", undefined, (message) => logs.push(message), @@ -154,7 +158,7 @@ describe("npm-resolution helpers", () => { expect(record).toEqual({ source: "npm", spec: "@openclaw/plugin-alpha@latest", - installPath: "/tmp/openclaw/extensions/alpha", + installPath: ALPHA_INSTALL_PATH, version: "1.2.3", resolvedName: undefined, resolvedVersion: undefined, diff --git a/src/cli/plugin-install-plan.test.ts b/src/cli/plugin-install-plan.test.ts index 7d0396dc199..2ec5e196f3e 100644 --- a/src/cli/plugin-install-plan.test.ts +++ b/src/cli/plugin-install-plan.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from "vitest"; +import { installedPluginRoot } from "../../test/helpers/bundled-plugin-paths.js"; import { PLUGIN_INSTALL_ERROR_CODE } from "../plugins/install.js"; import { resolveBundledInstallPlanForCatalogEntry, @@ -10,7 +11,7 @@ describe("plugin install plan helpers", () => { it("prefers bundled plugin for bare plugin-id specs", () => { const findBundledSource = vi.fn().mockReturnValue({ pluginId: "voice-call", - localPath: "/tmp/extensions/voice-call", + localPath: installedPluginRoot("/tmp", "voice-call"), npmSpec: "@openclaw/voice-call", }); @@ -42,7 +43,7 @@ describe("plugin install plan helpers", () => { if (kind === "pluginId" && value === "voice-call") { return { pluginId: "voice-call", - localPath: "/tmp/extensions/voice-call", + localPath: installedPluginRoot("/tmp", "voice-call"), npmSpec: "@openclaw/voice-call", }; } @@ -56,7 +57,7 @@ describe("plugin install plan helpers", () => { }); expect(findBundledSource).toHaveBeenCalledWith({ kind: "pluginId", value: "voice-call" }); - expect(result?.bundledSource.localPath).toBe("/tmp/extensions/voice-call"); + expect(result?.bundledSource.localPath).toBe(installedPluginRoot("/tmp", "voice-call")); }); it("rejects npm-spec matches that resolve to a different plugin id", () => { @@ -66,7 +67,7 @@ describe("plugin install plan helpers", () => { if (kind === "npmSpec") { return { pluginId: "not-voice-call", - localPath: "/tmp/extensions/not-voice-call", + localPath: installedPluginRoot("/tmp", "not-voice-call"), npmSpec: "@openclaw/voice-call", }; } @@ -89,7 +90,7 @@ describe("plugin install plan helpers", () => { if (kind === "pluginId") { return { pluginId: "whatsapp", - localPath: "/tmp/extensions/whatsapp", + localPath: installedPluginRoot("/tmp", "whatsapp"), npmSpec: "@openclaw/whatsapp", }; } @@ -108,7 +109,7 @@ describe("plugin install plan helpers", () => { it("uses npm-spec bundled fallback only for package-not-found", () => { const findBundledSource = vi.fn().mockReturnValue({ pluginId: "voice-call", - localPath: "/tmp/extensions/voice-call", + localPath: installedPluginRoot("/tmp", "voice-call"), npmSpec: "@openclaw/voice-call", }); const result = resolveBundledInstallPlanForNpmFailure({ diff --git a/src/cli/plugins-cli.install.test.ts b/src/cli/plugins-cli.install.test.ts index 3ec242ec1e8..1be254e0b78 100644 --- a/src/cli/plugins-cli.install.test.ts +++ b/src/cli/plugins-cli.install.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it } from "vitest"; +import { installedPluginRoot } from "../../test/helpers/bundled-plugin-paths.js"; import type { OpenClawConfig } from "../config/config.js"; import { applyExclusiveSlotSelection, @@ -21,6 +22,12 @@ import { writeConfigFile, } from "./plugins-cli-test-helpers.js"; +const CLI_STATE_ROOT = "/tmp/openclaw-state"; + +function cliInstallPath(pluginId: string): string { + return installedPluginRoot(CLI_STATE_ROOT, pluginId); +} + function createEnabledPluginConfig(pluginId: string): OpenClawConfig { return { plugins: { @@ -58,7 +65,7 @@ function createClawHubInstallResult(params: { return { ok: true, pluginId: params.pluginId, - targetDir: `/tmp/openclaw-state/extensions/${params.pluginId}`, + targetDir: cliInstallPath(params.pluginId), version: params.version, packageName: params.packageName, clawhub: { @@ -154,7 +161,7 @@ describe("plugins cli install", () => { installs: { alpha: { source: "marketplace", - installPath: "/tmp/openclaw-state/extensions/alpha", + installPath: cliInstallPath("alpha"), }, }, }, @@ -164,7 +171,7 @@ describe("plugins cli install", () => { installPluginFromMarketplace.mockResolvedValue({ ok: true, pluginId: "alpha", - targetDir: "/tmp/openclaw-state/extensions/alpha", + targetDir: cliInstallPath("alpha"), version: "1.2.3", marketplaceName: "Claude", marketplaceSource: "local/repo", @@ -201,7 +208,7 @@ describe("plugins cli install", () => { install: { source: "clawhub", spec: "clawhub:demo@1.2.3", - installPath: "/tmp/openclaw-state/extensions/demo", + installPath: cliInstallPath("demo"), clawhubPackage: "demo", clawhubFamily: "code-plugin", clawhubChannel: "official", @@ -260,7 +267,7 @@ describe("plugins cli install", () => { install: { source: "clawhub", spec: "clawhub:demo@1.2.3", - installPath: "/tmp/openclaw-state/extensions/demo", + installPath: cliInstallPath("demo"), clawhubPackage: "demo", }, }); @@ -317,7 +324,7 @@ describe("plugins cli install", () => { installPluginFromNpmSpec.mockResolvedValue({ ok: true, pluginId: "demo", - targetDir: "/tmp/openclaw-state/extensions/demo", + targetDir: cliInstallPath("demo"), version: "1.2.3", npmResolution: { packageName: "demo", diff --git a/src/cli/plugins-cli.uninstall.test.ts b/src/cli/plugins-cli.uninstall.test.ts index 4dfeb02d922..99866a89d58 100644 --- a/src/cli/plugins-cli.uninstall.test.ts +++ b/src/cli/plugins-cli.uninstall.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it } from "vitest"; +import { installedPluginRoot } from "../../test/helpers/bundled-plugin-paths.js"; import type { OpenClawConfig } from "../config/config.js"; import { buildPluginStatusReport, @@ -13,6 +14,9 @@ import { writeConfigFile, } from "./plugins-cli-test-helpers.js"; +const CLI_STATE_ROOT = "/tmp/openclaw-state"; +const ALPHA_INSTALL_PATH = installedPluginRoot(CLI_STATE_ROOT, "alpha"); + describe("plugins cli uninstall", () => { beforeEach(() => { resetPluginsCliTestState(); @@ -29,8 +33,8 @@ describe("plugins cli uninstall", () => { installs: { alpha: { source: "path", - sourcePath: "/tmp/openclaw-state/extensions/alpha", - installPath: "/tmp/openclaw-state/extensions/alpha", + sourcePath: ALPHA_INSTALL_PATH, + installPath: ALPHA_INSTALL_PATH, }, }, }, @@ -56,8 +60,8 @@ describe("plugins cli uninstall", () => { installs: { alpha: { source: "path", - sourcePath: "/tmp/openclaw-state/extensions/alpha", - installPath: "/tmp/openclaw-state/extensions/alpha", + sourcePath: ALPHA_INSTALL_PATH, + installPath: ALPHA_INSTALL_PATH, }, }, }, diff --git a/src/cli/plugins-install-config.test.ts b/src/cli/plugins-install-config.test.ts index 39e5650aec1..d98134526d6 100644 --- a/src/cli/plugins-install-config.test.ts +++ b/src/cli/plugins-install-config.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { bundledPluginRootAt, repoInstallSpec } from "../../test/helpers/bundled-plugin-paths.js"; import type { OpenClawConfig } from "../config/config.js"; import type { ConfigFileSnapshot } from "../config/types.openclaw.js"; @@ -16,6 +17,7 @@ vi.mock("../commands/doctor/providers/matrix.js", () => ({ })); const { loadConfigForInstall } = await import("./plugins-install-command.js"); +const MATRIX_REPO_INSTALL_SPEC = repoInstallSpec("matrix"); function makeSnapshot(overrides: Partial = {}): ConfigFileSnapshot { return { @@ -112,9 +114,9 @@ describe("loadConfigForInstall", () => { ); const result = await loadConfigForInstall({ - rawSpec: "./extensions/matrix", - normalizedSpec: "./extensions/matrix", - resolvedPath: "/tmp/repo/extensions/matrix", + rawSpec: MATRIX_REPO_INSTALL_SPEC, + normalizedSpec: MATRIX_REPO_INSTALL_SPEC, + resolvedPath: bundledPluginRootAt("/tmp/repo", "matrix"), }); expect(result).toBe(snapshotCfg); }); diff --git a/src/cli/program/preaction.test.ts b/src/cli/program/preaction.test.ts index f813f25067c..bf218d8a2e7 100644 --- a/src/cli/program/preaction.test.ts +++ b/src/cli/program/preaction.test.ts @@ -1,8 +1,11 @@ import { Command } from "commander"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { repoInstallSpec } from "../../../test/helpers/bundled-plugin-paths.js"; import { loggingState } from "../../logging/state.js"; import { setCommandJsonMode } from "./json-mode.js"; +const MATRIX_REPO_INSTALL_SPEC = repoInstallSpec("matrix"); + const setVerboseMock = vi.fn(); const emitCliBannerMock = vi.fn(); const ensureConfigReadyMock = vi.fn(async () => {}); @@ -286,8 +289,8 @@ describe("registerPreActionHooks", () => { vi.clearAllMocks(); await runPreAction({ - parseArgv: ["plugins", "install", "./extensions/matrix"], - processArgv: ["node", "openclaw", "plugins", "install", "./extensions/matrix"], + parseArgv: ["plugins", "install", MATRIX_REPO_INSTALL_SPEC], + processArgv: ["node", "openclaw", "plugins", "install", MATRIX_REPO_INSTALL_SPEC], }); expect(ensureConfigReadyMock).toHaveBeenCalledWith({ diff --git a/src/commands/channel-setup/plugin-install.test.ts b/src/commands/channel-setup/plugin-install.test.ts index 879566fba02..d0b716b15a6 100644 --- a/src/commands/channel-setup/plugin-install.test.ts +++ b/src/commands/channel-setup/plugin-install.test.ts @@ -1,5 +1,9 @@ import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import { + bundledPluginRoot, + bundledPluginRootAt, +} from "../../../test/helpers/bundled-plugin-paths.js"; vi.mock("node:fs", async (importOriginal) => { const actual = await importOriginal(); @@ -92,7 +96,7 @@ const baseEntry: ChannelPluginCatalogEntry = { }, install: { npmSpec: "@openclaw/zalo", - localPath: "extensions/zalo", + localPath: bundledPluginRoot("zalo"), }, }; @@ -134,7 +138,7 @@ async function runInitialValueForChannel(channel: "dev" | "beta") { function expectPluginLoadedFromLocalPath( result: Awaited>, ) { - const expectedPath = path.resolve(process.cwd(), "extensions/zalo"); + const expectedPath = path.resolve(process.cwd(), bundledPluginRoot("zalo")); expect(result.installed).toBe(true); expect(result.cfg.plugins?.load?.paths).toContain(expectedPath); } @@ -235,7 +239,7 @@ describe("ensureChannelSetupPluginInstalled", () => { "zalo", { pluginId: "zalo", - localPath: "/opt/openclaw/extensions/zalo", + localPath: bundledPluginRootAt("/opt/openclaw", "zalo"), npmSpec: "@openclaw/zalo", }, ], @@ -255,7 +259,7 @@ describe("ensureChannelSetupPluginInstalled", () => { options: expect.arrayContaining([ expect.objectContaining({ value: "local", - hint: "/opt/openclaw/extensions/zalo", + hint: bundledPluginRootAt("/opt/openclaw", "zalo"), }), ]), }), @@ -274,7 +278,7 @@ describe("ensureChannelSetupPluginInstalled", () => { "whatsapp", { pluginId: "whatsapp", - localPath: "/opt/openclaw/extensions/whatsapp", + localPath: bundledPluginRootAt("/opt/openclaw", "whatsapp"), npmSpec: "@openclaw/whatsapp", }, ], diff --git a/src/commands/channel-test-helpers.ts b/src/commands/channel-test-helpers.ts index f5acb4d074e..5ce3bf4eb00 100644 --- a/src/commands/channel-test-helpers.ts +++ b/src/commands/channel-test-helpers.ts @@ -1,16 +1,33 @@ -import { googlechatPlugin } from "../../extensions/googlechat/test-api.js"; -import { matrixPlugin, setMatrixRuntime } from "../../extensions/matrix/test-api.js"; -import { msteamsPlugin } from "../../extensions/msteams/test-api.js"; -import { nostrPlugin } from "../../extensions/nostr/test-api.js"; -import { tlonPlugin } from "../../extensions/tlon/test-api.js"; -import { whatsappPlugin } from "../../extensions/whatsapp/test-api.js"; -import { bundledChannelPlugins } from "../channels/plugins/bundled.js"; +import { listBundledChannelPlugins } from "../channels/plugins/bundled.js"; +import type { ChannelPlugin } from "../channels/plugins/types.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; +import type { PluginRuntime } from "../plugins/runtime/index.js"; +import { loadBundledPluginTestApiSync } from "../test-utils/bundled-plugin-public-surface.js"; import { createTestRegistry } from "../test-utils/channel-plugins.js"; import { getChannelSetupWizardAdapter } from "./channel-setup/registry.js"; import type { ChannelSetupWizardAdapter } from "./channel-setup/types.js"; import type { ChannelChoice } from "./onboard-types.js"; +const { googlechatPlugin } = loadBundledPluginTestApiSync<{ + googlechatPlugin: ChannelPlugin; +}>("googlechat"); +const { matrixPlugin, setMatrixRuntime } = loadBundledPluginTestApiSync<{ + matrixPlugin: ChannelPlugin; + setMatrixRuntime: (runtime: PluginRuntime) => void; +}>("matrix"); +const { msteamsPlugin } = loadBundledPluginTestApiSync<{ + msteamsPlugin: ChannelPlugin; +}>("msteams"); +const { nostrPlugin } = loadBundledPluginTestApiSync<{ + nostrPlugin: ChannelPlugin; +}>("nostr"); +const { tlonPlugin } = loadBundledPluginTestApiSync<{ + tlonPlugin: ChannelPlugin; +}>("tlon"); +const { whatsappPlugin } = loadBundledPluginTestApiSync<{ + whatsappPlugin: ChannelPlugin; +}>("whatsapp"); + type ChannelSetupWizardAdapterPatch = Partial< Pick< ChannelSetupWizardAdapter, @@ -37,7 +54,7 @@ export function setDefaultChannelPluginRegistryForTests(): void { }, } as Parameters[0]); const channels = [ - ...bundledChannelPlugins, + ...listBundledChannelPlugins(), matrixPlugin, msteamsPlugin, nostrPlugin, diff --git a/src/commands/channels.mock-harness.ts b/src/commands/channels.mock-harness.ts index 1a1b64eeb3f..a069f61c855 100644 --- a/src/commands/channels.mock-harness.ts +++ b/src/commands/channels.mock-harness.ts @@ -1,6 +1,10 @@ import { vi } from "vitest"; import type { MockFn } from "../test-utils/vitest-mock-fn.js"; +function buildBundledPluginModuleId(pluginId: string, artifactBasename: string): string { + return ["..", "..", "extensions", pluginId, artifactBasename].join("/"); +} + export const configMocks: { readConfigFileSnapshot: MockFn; writeConfigFile: MockFn; @@ -24,11 +28,13 @@ vi.mock("../config/config.js", async (importOriginal) => { }; }); -vi.mock("../../extensions/telegram/update-offset-runtime-api.js", async (importOriginal) => { - const actual = - await importOriginal(); - return { - ...actual, - deleteTelegramUpdateOffset: offsetMocks.deleteTelegramUpdateOffset, - }; -}); +vi.mock( + buildBundledPluginModuleId("telegram", "update-offset-runtime-api.js"), + async (importOriginal) => { + const actual = (await importOriginal()) as Record; + return { + ...actual, + deleteTelegramUpdateOffset: offsetMocks.deleteTelegramUpdateOffset, + }; + }, +); diff --git a/src/commands/doctor.e2e-harness.ts b/src/commands/doctor.e2e-harness.ts index e1cee060139..9d63cbaa4c3 100644 --- a/src/commands/doctor.e2e-harness.ts +++ b/src/commands/doctor.e2e-harness.ts @@ -15,6 +15,10 @@ let originalStateDir: string | undefined; let originalUpdateInProgress: string | undefined; let tempStateDir: string | undefined; +function buildBundledPluginModuleId(pluginId: string, artifactBasename: string): string { + return ["..", "..", "extensions", pluginId, artifactBasename].join("/"); +} + function setStdinTty(value: boolean | undefined) { try { Object.defineProperty(process.stdin, "isTTY", { @@ -294,7 +298,7 @@ vi.mock("../pairing/pairing-store.js", () => ({ upsertChannelPairingRequest: vi.fn().mockResolvedValue({ code: "000000", created: false }), })); -vi.mock("../../extensions/telegram/api.js", () => ({ +vi.mock(buildBundledPluginModuleId("telegram", "api.js"), () => ({ resolveTelegramToken: vi.fn(() => ({ token: "", source: "none" })), })); diff --git a/src/commands/doctor/providers/matrix.test.ts b/src/commands/doctor/providers/matrix.test.ts index 6dde90ca24a..e4bc2e4ce7c 100644 --- a/src/commands/doctor/providers/matrix.test.ts +++ b/src/commands/doctor/providers/matrix.test.ts @@ -102,7 +102,8 @@ describe("doctor matrix provider helpers", () => { expect(warnings[0]).toContain("custom path that no longer exists"); expect(warnings[0]).toContain(missingPath); expect(warnings[1]).toContain("openclaw plugins install @openclaw/matrix"); - expect(warnings[2]).toContain("openclaw plugins install ./extensions/matrix"); + expect(warnings[2]).toContain("openclaw plugins install "); + expect(warnings[2]).toContain(path.join("extensions", "matrix")); }); it("summarizes matrix repair messaging", async () => { diff --git a/src/commands/doctor/providers/matrix.ts b/src/commands/doctor/providers/matrix.ts index 8fd907700d7..0f13843160e 100644 --- a/src/commands/doctor/providers/matrix.ts +++ b/src/commands/doctor/providers/matrix.ts @@ -17,6 +17,7 @@ import { detectPluginInstallPathIssue, formatPluginInstallPathIssue, } from "../../../infra/plugin-install-path-warnings.js"; +import { resolveBundledPluginInstallCommandHint } from "../../../plugins/bundled-sources.js"; import { removePluginFromConfig } from "../../../plugins/uninstall.js"; import type { DoctorConfigMutationResult } from "../shared/config-mutation-state.js"; @@ -65,7 +66,10 @@ export async function collectMatrixInstallPathWarnings(cfg: OpenClawConfig): Pro issue, pluginLabel: "Matrix", defaultInstallCommand: "openclaw plugins install @openclaw/matrix", - repoInstallCommand: "openclaw plugins install ./extensions/matrix", + repoInstallCommand: resolveBundledPluginInstallCommandHint({ + pluginId: "matrix", + workspaceDir: process.cwd(), + }), formatCommand: formatCliCommand, }).map((entry) => `- ${entry}`); } diff --git a/src/commands/doctor/shared/bundled-plugin-load-paths.test.ts b/src/commands/doctor/shared/bundled-plugin-load-paths.test.ts index 24ab5b5a919..7c9e1a346f9 100644 --- a/src/commands/doctor/shared/bundled-plugin-load-paths.test.ts +++ b/src/commands/doctor/shared/bundled-plugin-load-paths.test.ts @@ -1,5 +1,9 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + bundledDistPluginRootAt, + bundledPluginRootAt, +} from "../../../../test/helpers/bundled-plugin-paths.js"; import type { BundledPluginSource } from "../../../plugins/bundled-sources.js"; import * as bundledSources from "../../../plugins/bundled-sources.js"; import { @@ -18,8 +22,9 @@ function bundled(pluginId: string, localPath: string): BundledPluginSource { describe("bundled plugin load path repair", () => { beforeEach(() => { + const packageRoot = "/app/node_modules/openclaw"; vi.spyOn(bundledSources, "resolveBundledPluginSources").mockReturnValue( - new Map([["feishu", bundled("feishu", "/app/node_modules/openclaw/dist/extensions/feishu")]]), + new Map([["feishu", bundled("feishu", bundledDistPluginRootAt(packageRoot, "feishu"))]]), ); }); @@ -29,8 +34,8 @@ describe("bundled plugin load path repair", () => { it("detects legacy bundled plugin paths that still point at source extensions", () => { const packageRoot = path.resolve("app-node-modules", "openclaw"); - const legacyPath = path.join(packageRoot, "extensions", "feishu"); - const bundledPath = path.join(packageRoot, "dist", "extensions", "feishu"); + const legacyPath = bundledPluginRootAt(packageRoot, "feishu"); + const bundledPath = bundledDistPluginRootAt(packageRoot, "feishu"); vi.spyOn(bundledSources, "resolveBundledPluginSources").mockReturnValue( new Map([["feishu", bundled("feishu", bundledPath)]]), ); @@ -55,8 +60,8 @@ describe("bundled plugin load path repair", () => { it("rewrites legacy bundled paths during doctor repair", () => { const packageRoot = path.resolve("app-node-modules", "openclaw"); - const legacyPath = path.join(packageRoot, "extensions", "feishu"); - const bundledPath = path.join(packageRoot, "dist", "extensions", "feishu"); + const legacyPath = bundledPluginRootAt(packageRoot, "feishu"); + const bundledPath = bundledDistPluginRootAt(packageRoot, "feishu"); vi.spyOn(bundledSources, "resolveBundledPluginSources").mockReturnValue( new Map([["feishu", bundled("feishu", bundledPath)]]), ); @@ -77,8 +82,8 @@ describe("bundled plugin load path repair", () => { it("derives legacy paths from the bundled directory name instead of plugin id", () => { const packageRoot = path.resolve("app-node-modules", "openclaw"); - const legacyPath = path.join(packageRoot, "extensions", "kimi-coding"); - const bundledPath = path.join(packageRoot, "dist", "extensions", "kimi-coding"); + const legacyPath = bundledPluginRootAt(packageRoot, "kimi-coding"); + const bundledPath = bundledDistPluginRootAt(packageRoot, "kimi-coding"); vi.spyOn(bundledSources, "resolveBundledPluginSources").mockReturnValue( new Map([["kimi", bundled("kimi", bundledPath)]]), ); @@ -103,8 +108,8 @@ describe("bundled plugin load path repair", () => { it("matches legacy bundled paths with a trailing slash", () => { const packageRoot = path.resolve("app-node-modules", "openclaw"); - const legacyPath = `${path.join(packageRoot, "extensions", "feishu")}${path.sep}`; - const bundledPath = path.join(packageRoot, "dist", "extensions", "feishu"); + const legacyPath = `${bundledPluginRootAt(packageRoot, "feishu")}${path.sep}`; + const bundledPath = bundledDistPluginRootAt(packageRoot, "feishu"); vi.spyOn(bundledSources, "resolveBundledPluginSources").mockReturnValue( new Map([["feishu", bundled("feishu", bundledPath)]]), ); diff --git a/src/commands/onboard-auth.test.ts b/src/commands/onboard-auth.test.ts index 02eb230d280..84c61df0d29 100644 --- a/src/commands/onboard-auth.test.ts +++ b/src/commands/onboard-auth.test.ts @@ -7,7 +7,7 @@ import { createConfigWithFallbacks, createLegacyProviderConfig, EXPECTED_FALLBACKS, -} from "../../test/helpers/extensions/onboard-config.js"; +} from "../../test/helpers/plugins/onboard-config.js"; import { SYNTHETIC_DEFAULT_MODEL_ID } from "../agents/synthetic-models.js"; import type { OpenClawConfig } from "../config/config.js"; import { diff --git a/src/config/bundled-channel-config-runtime.test.ts b/src/config/bundled-channel-config-runtime.test.ts index b719eff2a60..9f4ca6e741e 100644 --- a/src/config/bundled-channel-config-runtime.test.ts +++ b/src/config/bundled-channel-config-runtime.test.ts @@ -8,9 +8,7 @@ describe("bundled channel config runtime", () => { it("tolerates an unavailable bundled channel list during import", async () => { vi.doMock("../channels/plugins/bundled.js", () => ({ - get bundledChannelPlugins() { - return undefined; - }, + listBundledChannelPlugins: () => undefined, })); const runtimeModule = await import("./bundled-channel-config-runtime.js"); @@ -22,14 +20,11 @@ describe("bundled channel config runtime", () => { it("falls back to static channel schemas when bundled plugin access hits a TDZ-style ReferenceError", async () => { vi.resetModules(); vi.doMock("../channels/plugins/bundled.js", () => { - const mockModule = {} as { bundledChannelPlugins?: unknown }; - Object.defineProperty(mockModule, "bundledChannelPlugins", { - enumerable: true, - get() { + return { + listBundledChannelPlugins() { throw new ReferenceError("Cannot access 'bundledChannelPlugins' before initialization."); }, - }); - return mockModule; + }; }); const runtime = await import("./bundled-channel-config-runtime.js"); diff --git a/src/config/bundled-channel-config-runtime.ts b/src/config/bundled-channel-config-runtime.ts index 537df0848d8..0966fd733a6 100644 --- a/src/config/bundled-channel-config-runtime.ts +++ b/src/config/bundled-channel-config-runtime.ts @@ -4,7 +4,7 @@ import type { ChannelConfigRuntimeSchema, ChannelConfigSchema, } from "../channels/plugins/types.plugin.js"; -import { BUNDLED_PLUGIN_METADATA } from "../plugins/bundled-plugin-metadata.js"; +import { listBundledPluginMetadata } from "../plugins/bundled-plugin-metadata.js"; import { MSTeamsConfigSchema } from "./zod-schema.providers-core.js"; import { WhatsAppConfigSchema } from "./zod-schema.providers-whatsapp.js"; @@ -42,7 +42,7 @@ function buildBundledChannelMaps( } } - for (const entry of BUNDLED_PLUGIN_METADATA) { + for (const entry of listBundledPluginMetadata()) { const channelConfigs = entry.manifest.channelConfigs; if (!channelConfigs) { continue; @@ -72,9 +72,11 @@ function buildBundledChannelMaps( function readBundledChannelPlugins(): readonly BundledChannelPluginShape[] | undefined { try { - return Array.isArray(bundledChannelModule.bundledChannelPlugins) - ? (bundledChannelModule.bundledChannelPlugins as readonly BundledChannelPluginShape[]) - : undefined; + if (typeof bundledChannelModule.listBundledChannelPlugins !== "function") { + return undefined; + } + const plugins = bundledChannelModule.listBundledChannelPlugins(); + return Array.isArray(plugins) ? (plugins as readonly BundledChannelPluginShape[]) : undefined; } catch (error) { // Circular bundled channel imports can transiently hit TDZ during test/bootstrap // initialization. Fall back to metadata/static schemas until the registry is ready. diff --git a/src/config/schema.base.generated.ts b/src/config/schema.base.generated.ts index 026b8521d4e..be0d5fff155 100644 --- a/src/config/schema.base.generated.ts +++ b/src/config/schema.base.generated.ts @@ -15387,7 +15387,7 @@ export const GENERATED_BASE_CONFIG_SCHEMA = { }, "plugins.installs.*.installPath": { label: "Plugin Install Path", - help: "Resolved install directory (usually ~/.openclaw/extensions/).", + help: "Resolved install directory for the installed plugin bundle.", tags: ["storage"], }, "plugins.installs.*.version": { diff --git a/src/config/schema.help.ts b/src/config/schema.help.ts index 1363900c595..79013bd90c9 100644 --- a/src/config/schema.help.ts +++ b/src/config/schema.help.ts @@ -1001,8 +1001,7 @@ export const FIELD_HELP: Record = { "plugins.installs.*.source": 'Install source ("npm", "archive", or "path").', "plugins.installs.*.spec": "Original npm spec used for install (if source is npm).", "plugins.installs.*.sourcePath": "Original archive/path used for install (if any).", - "plugins.installs.*.installPath": - "Resolved install directory (usually ~/.openclaw/extensions/).", + "plugins.installs.*.installPath": "Resolved install directory for the installed plugin bundle.", "plugins.installs.*.version": "Version recorded at install time (if available).", "plugins.installs.*.resolvedName": "Resolved npm package name from the fetched artifact.", "plugins.installs.*.resolvedVersion": diff --git a/src/dockerfile.test.ts b/src/dockerfile.test.ts index 2570a8ed9dc..f23c06fe8bb 100644 --- a/src/dockerfile.test.ts +++ b/src/dockerfile.test.ts @@ -2,6 +2,7 @@ import { readFile } from "node:fs/promises"; import { join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; +import { BUNDLED_PLUGIN_ROOT_DIR } from "../test/helpers/bundled-plugin-paths.js"; const repoRoot = resolve(fileURLToPath(new URL(".", import.meta.url)), ".."); const dockerfilePath = join(repoRoot, "Dockerfile"); @@ -41,7 +42,9 @@ describe("Dockerfile", () => { const dockerfile = await readFile(dockerfilePath, "utf8"); expect(dockerfile).toContain("FROM build AS runtime-assets"); expect(dockerfile).toContain("CI=true pnpm prune --prod"); - expect(dockerfile).not.toContain('npm install --prefix "extensions/$ext" --omit=dev --silent'); + expect(dockerfile).not.toContain( + `npm install --prefix "${BUNDLED_PLUGIN_ROOT_DIR}/$ext" --omit=dev --silent`, + ); expect(dockerfile).toContain( "COPY --from=runtime-assets --chown=node:node /app/node_modules ./node_modules", ); @@ -49,12 +52,17 @@ describe("Dockerfile", () => { it("pins bundled plugin discovery to copied source extensions in runtime images", async () => { const dockerfile = await readFile(dockerfilePath, "utf8"); - expect(dockerfile).toContain("ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/extensions"); + expect(dockerfile).toContain(`ARG OPENCLAW_BUNDLED_PLUGIN_DIR=${BUNDLED_PLUGIN_ROOT_DIR}`); + expect(dockerfile).toContain( + "ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/${OPENCLAW_BUNDLED_PLUGIN_DIR}", + ); }); it("normalizes plugin and agent paths permissions in image layers", async () => { const dockerfile = await readFile(dockerfilePath, "utf8"); - expect(dockerfile).toContain("for dir in /app/extensions /app/.agent /app/.agents"); + expect(dockerfile).toContain( + "RUN for dir in /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} /app/.agent /app/.agents; do \\", + ); expect(dockerfile).toContain('find "$dir" -type d -exec chmod 755 {} +'); expect(dockerfile).toContain('find "$dir" -type f -exec chmod 644 {} +'); }); diff --git a/src/extensions/public-artifacts.test.ts b/src/extensions/public-artifacts.test.ts deleted file mode 100644 index 906bc951d23..00000000000 --- a/src/extensions/public-artifacts.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - BUNDLED_RUNTIME_SIDECAR_BASENAMES, - BUNDLED_RUNTIME_SIDECAR_PATHS, - GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES, - getPublicArtifactBasename, -} from "./public-artifacts.js"; - -describe("public artifact manifests", () => { - it("derives bundled sidecar basenames from the runtime sidecar paths", () => { - expect(BUNDLED_RUNTIME_SIDECAR_BASENAMES).toEqual([ - ...new Set(BUNDLED_RUNTIME_SIDECAR_PATHS.map(getPublicArtifactBasename)), - ]); - }); - - it("keeps every bundled sidecar basename in the guarded public surface list", () => { - const guardedBasenames = new Set(GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES); - for (const basename of BUNDLED_RUNTIME_SIDECAR_BASENAMES) { - expect(guardedBasenames.has(basename), basename).toBe(true); - } - }); -}); diff --git a/src/extensions/public-artifacts.ts b/src/extensions/public-artifacts.ts deleted file mode 100644 index 0f9a7385bfd..00000000000 --- a/src/extensions/public-artifacts.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { - BUNDLED_RUNTIME_SIDECAR_BASENAMES, - BUNDLED_RUNTIME_SIDECAR_PATHS, - GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES, - getPublicArtifactBasename, -} from "../plugins/public-artifacts.js"; diff --git a/src/gateway/server.impl.ts b/src/gateway/server.impl.ts index 047a7c2807f..637f5fadf0b 100644 --- a/src/gateway/server.impl.ts +++ b/src/gateway/server.impl.ts @@ -50,6 +50,7 @@ import { enqueueSystemEvent } from "../infra/system-events.js"; import { scheduleGatewayUpdateCheck } from "../infra/update-startup.js"; import { startDiagnosticHeartbeat, stopDiagnosticHeartbeat } from "../logging/diagnostic.js"; import { createSubsystemLogger, runtimeForLogger } from "../logging/subsystem.js"; +import { resolveBundledPluginInstallCommandHint } from "../plugins/bundled-sources.js"; import { resolveConfiguredDeferredChannelPluginIds } from "../plugins/channel-plugin-ids.js"; import { getGlobalHookRunner, runGlobalGatewayStopSafely } from "../plugins/hook-runner-global.js"; import { createEmptyPluginRegistry } from "../plugins/registry.js"; @@ -542,7 +543,10 @@ export async function startGatewayServer( issue: matrixInstallPathIssue, pluginLabel: "Matrix", defaultInstallCommand: "openclaw plugins install @openclaw/matrix", - repoInstallCommand: "openclaw plugins install ./extensions/matrix", + repoInstallCommand: resolveBundledPluginInstallCommandHint({ + pluginId: "matrix", + workspaceDir: process.cwd(), + }), formatCommand: formatCliCommand, }); log.warn( diff --git a/src/gateway/test-helpers.mocks.ts b/src/gateway/test-helpers.mocks.ts index e30f26aaf4a..c9ddfc66c9d 100644 --- a/src/gateway/test-helpers.mocks.ts +++ b/src/gateway/test-helpers.mocks.ts @@ -4,8 +4,6 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { Mock, vi } from "vitest"; -import { buildElevenLabsSpeechProvider } from "../../extensions/elevenlabs/test-api.ts"; -import { buildOpenAISpeechProvider } from "../../extensions/openai/test-api.ts"; import type { MsgContext } from "../auto-reply/templating.js"; import type { GetReplyOptions, ReplyPayload } from "../auto-reply/types.js"; import type { ChannelPlugin, ChannelOutboundAdapter } from "../channels/plugins/types.js"; @@ -16,8 +14,21 @@ import type { HooksConfig } from "../config/types.hooks.js"; import type { TailscaleWhoisIdentity } from "../infra/tailscale.js"; import type { PluginRegistry } from "../plugins/registry.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; +import type { SpeechProviderPlugin } from "../plugins/types.js"; import { DEFAULT_ACCOUNT_ID } from "../routing/session-key.js"; import { resolveGlobalSingleton } from "../shared/global-singleton.js"; +import { loadBundledPluginTestApiSync } from "../test-utils/bundled-plugin-public-surface.js"; + +const { buildElevenLabsSpeechProvider } = loadBundledPluginTestApiSync<{ + buildElevenLabsSpeechProvider: () => SpeechProviderPlugin; +}>("elevenlabs"); +const { buildOpenAISpeechProvider } = loadBundledPluginTestApiSync<{ + buildOpenAISpeechProvider: () => SpeechProviderPlugin; +}>("openai"); + +function buildBundledPluginModuleId(pluginId: string, artifactBasename: string): string { + return ["..", "..", "extensions", pluginId, artifactBasename].join("/"); +} type StubChannelOptions = { id: ChannelPlugin["id"]; @@ -692,7 +703,7 @@ vi.mock("../commands/health.js", () => ({ vi.mock("../commands/status.js", () => ({ getStatusSummary: vi.fn().mockResolvedValue({ ok: true }), })); -vi.mock("../../extensions/whatsapp/runtime-api.js", () => ({ +vi.mock(buildBundledPluginModuleId("whatsapp", "runtime-api.js"), () => ({ sendMessageWhatsApp: (...args: unknown[]) => (hoisted.sendWhatsAppMock as (...args: unknown[]) => unknown)(...args), sendPollWhatsApp: (...args: unknown[]) => diff --git a/src/generated/bundled-channel-entries.generated.ts b/src/generated/bundled-channel-entries.generated.ts deleted file mode 100644 index 0b73e743c3f..00000000000 --- a/src/generated/bundled-channel-entries.generated.ts +++ /dev/null @@ -1,96 +0,0 @@ -// Auto-generated by scripts/generate-bundled-plugin-metadata.mjs. Do not edit directly. - -import bluebubblesChannelEntry from "../../extensions/bluebubbles/index.js"; -import bluebubblesChannelSetupEntry from "../../extensions/bluebubbles/setup-entry.js"; -import discordChannelEntry from "../../extensions/discord/index.js"; -import discordChannelSetupEntry from "../../extensions/discord/setup-entry.js"; -import feishuChannelEntry from "../../extensions/feishu/index.js"; -import feishuChannelSetupEntry from "../../extensions/feishu/setup-entry.js"; -import imessageChannelEntry from "../../extensions/imessage/index.js"; -import imessageChannelSetupEntry from "../../extensions/imessage/setup-entry.js"; -import ircChannelEntry from "../../extensions/irc/index.js"; -import ircChannelSetupEntry from "../../extensions/irc/setup-entry.js"; -import lineChannelEntry from "../../extensions/line/index.js"; -import lineChannelSetupEntry from "../../extensions/line/setup-entry.js"; -import mattermostChannelEntry from "../../extensions/mattermost/index.js"; -import mattermostChannelSetupEntry from "../../extensions/mattermost/setup-entry.js"; -import nextcloudTalkChannelEntry from "../../extensions/nextcloud-talk/index.js"; -import nextcloudTalkChannelSetupEntry from "../../extensions/nextcloud-talk/setup-entry.js"; -import signalChannelEntry from "../../extensions/signal/index.js"; -import signalChannelSetupEntry from "../../extensions/signal/setup-entry.js"; -import slackChannelEntry from "../../extensions/slack/index.js"; -import slackChannelSetupEntry from "../../extensions/slack/setup-entry.js"; -import synologyChatChannelEntry from "../../extensions/synology-chat/index.js"; -import synologyChatChannelSetupEntry from "../../extensions/synology-chat/setup-entry.js"; -import telegramChannelEntry from "../../extensions/telegram/index.js"; -import telegramChannelSetupEntry from "../../extensions/telegram/setup-entry.js"; -import zaloChannelEntry from "../../extensions/zalo/index.js"; -import zaloChannelSetupEntry from "../../extensions/zalo/setup-entry.js"; - -export const GENERATED_BUNDLED_CHANNEL_ENTRIES = [ - { - id: "bluebubbles", - entry: bluebubblesChannelEntry, - setupEntry: bluebubblesChannelSetupEntry, - }, - { - id: "discord", - entry: discordChannelEntry, - setupEntry: discordChannelSetupEntry, - }, - { - id: "feishu", - entry: feishuChannelEntry, - setupEntry: feishuChannelSetupEntry, - }, - { - id: "imessage", - entry: imessageChannelEntry, - setupEntry: imessageChannelSetupEntry, - }, - { - id: "irc", - entry: ircChannelEntry, - setupEntry: ircChannelSetupEntry, - }, - { - id: "line", - entry: lineChannelEntry, - setupEntry: lineChannelSetupEntry, - }, - { - id: "mattermost", - entry: mattermostChannelEntry, - setupEntry: mattermostChannelSetupEntry, - }, - { - id: "nextcloud-talk", - entry: nextcloudTalkChannelEntry, - setupEntry: nextcloudTalkChannelSetupEntry, - }, - { - id: "signal", - entry: signalChannelEntry, - setupEntry: signalChannelSetupEntry, - }, - { - id: "slack", - entry: slackChannelEntry, - setupEntry: slackChannelSetupEntry, - }, - { - id: "synology-chat", - entry: synologyChatChannelEntry, - setupEntry: synologyChatChannelSetupEntry, - }, - { - id: "telegram", - entry: telegramChannelEntry, - setupEntry: telegramChannelSetupEntry, - }, - { - id: "zalo", - entry: zaloChannelEntry, - setupEntry: zaloChannelSetupEntry, - }, -] as const; diff --git a/src/generated/bundled-plugin-entries.generated.ts b/src/generated/bundled-plugin-entries.generated.ts deleted file mode 100644 index 34737f83082..00000000000 --- a/src/generated/bundled-plugin-entries.generated.ts +++ /dev/null @@ -1,243 +0,0 @@ -// Auto-generated by scripts/generate-bundled-plugin-metadata.mjs. Do not edit directly. - -export async function loadGeneratedBundledPluginEntries() { - const [ - acpxPluginModule, - amazonBedrockPluginModule, - anthropicPluginModule, - bluebubblesPluginModule, - bravePluginModule, - browserPluginModule, - byteplusPluginModule, - chutesPluginModule, - cloudflareAiGatewayPluginModule, - copilotProxyPluginModule, - deepgramPluginModule, - deepseekPluginModule, - diagnosticsOtelPluginModule, - diffsPluginModule, - discordPluginModule, - duckduckgoPluginModule, - elevenlabsPluginModule, - exaPluginModule, - falPluginModule, - feishuPluginModule, - firecrawlPluginModule, - githubCopilotPluginModule, - googlePluginModule, - googlechatPluginModule, - groqPluginModule, - huggingfacePluginModule, - imessagePluginModule, - ircPluginModule, - kilocodePluginModule, - kimiCodingPluginModule, - linePluginModule, - litellmPluginModule, - llmTaskPluginModule, - lobsterPluginModule, - matrixPluginModule, - mattermostPluginModule, - memoryCorePluginModule, - memoryLancedbPluginModule, - microsoftPluginModule, - microsoftFoundryPluginModule, - minimaxPluginModule, - mistralPluginModule, - modelstudioPluginModule, - moonshotPluginModule, - msteamsPluginModule, - nextcloudTalkPluginModule, - nostrPluginModule, - nvidiaPluginModule, - ollamaPluginModule, - openProsePluginModule, - openaiPluginModule, - opencodePluginModule, - opencodeGoPluginModule, - openrouterPluginModule, - openshellPluginModule, - perplexityPluginModule, - qianfanPluginModule, - sglangPluginModule, - signalPluginModule, - slackPluginModule, - synologyChatPluginModule, - syntheticPluginModule, - tavilyPluginModule, - telegramPluginModule, - tlonPluginModule, - togetherPluginModule, - twitchPluginModule, - venicePluginModule, - vercelAiGatewayPluginModule, - vllmPluginModule, - voiceCallPluginModule, - volcenginePluginModule, - whatsappPluginModule, - xaiPluginModule, - xiaomiPluginModule, - zaiPluginModule, - zaloPluginModule, - zalouserPluginModule, - ] = await Promise.all([ - import("../../extensions/acpx/index.js"), - import("../../extensions/amazon-bedrock/index.js"), - import("../../extensions/anthropic/index.js"), - import("../../extensions/bluebubbles/index.js"), - import("../../extensions/brave/index.js"), - import("../../extensions/browser/index.js"), - import("../../extensions/byteplus/index.js"), - import("../../extensions/chutes/index.js"), - import("../../extensions/cloudflare-ai-gateway/index.js"), - import("../../extensions/copilot-proxy/index.js"), - import("../../extensions/deepgram/index.js"), - import("../../extensions/deepseek/index.js"), - import("../../extensions/diagnostics-otel/index.js"), - import("../../extensions/diffs/index.js"), - import("../../extensions/discord/index.js"), - import("../../extensions/duckduckgo/index.js"), - import("../../extensions/elevenlabs/index.js"), - import("../../extensions/exa/index.js"), - import("../../extensions/fal/index.js"), - import("../../extensions/feishu/index.js"), - import("../../extensions/firecrawl/index.js"), - import("../../extensions/github-copilot/index.js"), - import("../../extensions/google/index.js"), - import("../../extensions/googlechat/index.js"), - import("../../extensions/groq/index.js"), - import("../../extensions/huggingface/index.js"), - import("../../extensions/imessage/index.js"), - import("../../extensions/irc/index.js"), - import("../../extensions/kilocode/index.js"), - import("../../extensions/kimi-coding/index.js"), - import("../../extensions/line/index.js"), - import("../../extensions/litellm/index.js"), - import("../../extensions/llm-task/index.js"), - import("../../extensions/lobster/index.js"), - import("../../extensions/matrix/index.js"), - import("../../extensions/mattermost/index.js"), - import("../../extensions/memory-core/index.js"), - import("../../extensions/memory-lancedb/index.js"), - import("../../extensions/microsoft/index.js"), - import("../../extensions/microsoft-foundry/index.js"), - import("../../extensions/minimax/index.js"), - import("../../extensions/mistral/index.js"), - import("../../extensions/modelstudio/index.js"), - import("../../extensions/moonshot/index.js"), - import("../../extensions/msteams/index.js"), - import("../../extensions/nextcloud-talk/index.js"), - import("../../extensions/nostr/index.js"), - import("../../extensions/nvidia/index.js"), - import("../../extensions/ollama/index.js"), - import("../../extensions/open-prose/index.js"), - import("../../extensions/openai/index.js"), - import("../../extensions/opencode/index.js"), - import("../../extensions/opencode-go/index.js"), - import("../../extensions/openrouter/index.js"), - import("../../extensions/openshell/index.js"), - import("../../extensions/perplexity/index.js"), - import("../../extensions/qianfan/index.js"), - import("../../extensions/sglang/index.js"), - import("../../extensions/signal/index.js"), - import("../../extensions/slack/index.js"), - import("../../extensions/synology-chat/index.js"), - import("../../extensions/synthetic/index.js"), - import("../../extensions/tavily/index.js"), - import("../../extensions/telegram/index.js"), - import("../../extensions/tlon/index.js"), - import("../../extensions/together/index.js"), - import("../../extensions/twitch/index.js"), - import("../../extensions/venice/index.js"), - import("../../extensions/vercel-ai-gateway/index.js"), - import("../../extensions/vllm/index.js"), - import("../../extensions/voice-call/index.js"), - import("../../extensions/volcengine/index.js"), - import("../../extensions/whatsapp/index.js"), - import("../../extensions/xai/index.js"), - import("../../extensions/xiaomi/index.js"), - import("../../extensions/zai/index.js"), - import("../../extensions/zalo/index.js"), - import("../../extensions/zalouser/index.js"), - ]); - return [ - acpxPluginModule.default, - amazonBedrockPluginModule.default, - anthropicPluginModule.default, - bluebubblesPluginModule.default, - bravePluginModule.default, - browserPluginModule.default, - byteplusPluginModule.default, - chutesPluginModule.default, - cloudflareAiGatewayPluginModule.default, - copilotProxyPluginModule.default, - deepgramPluginModule.default, - deepseekPluginModule.default, - diagnosticsOtelPluginModule.default, - diffsPluginModule.default, - discordPluginModule.default, - duckduckgoPluginModule.default, - elevenlabsPluginModule.default, - exaPluginModule.default, - falPluginModule.default, - feishuPluginModule.default, - firecrawlPluginModule.default, - githubCopilotPluginModule.default, - googlePluginModule.default, - googlechatPluginModule.default, - groqPluginModule.default, - huggingfacePluginModule.default, - imessagePluginModule.default, - ircPluginModule.default, - kilocodePluginModule.default, - kimiCodingPluginModule.default, - linePluginModule.default, - litellmPluginModule.default, - llmTaskPluginModule.default, - lobsterPluginModule.default, - matrixPluginModule.default, - mattermostPluginModule.default, - memoryCorePluginModule.default, - memoryLancedbPluginModule.default, - microsoftPluginModule.default, - microsoftFoundryPluginModule.default, - minimaxPluginModule.default, - mistralPluginModule.default, - modelstudioPluginModule.default, - moonshotPluginModule.default, - msteamsPluginModule.default, - nextcloudTalkPluginModule.default, - nostrPluginModule.default, - nvidiaPluginModule.default, - ollamaPluginModule.default, - openProsePluginModule.default, - openaiPluginModule.default, - opencodePluginModule.default, - opencodeGoPluginModule.default, - openrouterPluginModule.default, - openshellPluginModule.default, - perplexityPluginModule.default, - qianfanPluginModule.default, - sglangPluginModule.default, - signalPluginModule.default, - slackPluginModule.default, - synologyChatPluginModule.default, - syntheticPluginModule.default, - tavilyPluginModule.default, - telegramPluginModule.default, - tlonPluginModule.default, - togetherPluginModule.default, - twitchPluginModule.default, - venicePluginModule.default, - vercelAiGatewayPluginModule.default, - vllmPluginModule.default, - voiceCallPluginModule.default, - volcenginePluginModule.default, - whatsappPluginModule.default, - xaiPluginModule.default, - xiaomiPluginModule.default, - zaiPluginModule.default, - zaloPluginModule.default, - zalouserPluginModule.default, - ] as const; -} diff --git a/src/generated/plugin-sdk-facade-type-map.generated.ts b/src/generated/plugin-sdk-facade-type-map.generated.ts new file mode 100644 index 00000000000..f3909087f39 --- /dev/null +++ b/src/generated/plugin-sdk-facade-type-map.generated.ts @@ -0,0 +1,813 @@ +// Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. +export interface PluginSdkFacadeTypeMap { + "amazon-bedrock": { + module: typeof import("../../extensions/amazon-bedrock/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/amazon-bedrock/api.js"); + }; + }; + types: {}; + }; + "anthropic-vertex": { + module: typeof import("../../extensions/anthropic-vertex/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/anthropic-vertex/api.js"); + }; + }; + types: {}; + }; + "discord-account": { + module: typeof import("../../extensions/discord/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/discord/api.js"); + }; + }; + types: { + ResolvedDiscordAccount: import("../../extensions/discord/api.js").ResolvedDiscordAccount; + }; + }; + "discord-runtime-surface": { + module: typeof import("../../extensions/discord/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/discord/runtime-api.js"); + }; + }; + types: {}; + }; + "discord-session-key": { + module: typeof import("../../extensions/discord/session-key-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/discord/session-key-api.js"); + }; + }; + types: {}; + }; + "discord-surface": { + module: typeof import("../../extensions/discord/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/discord/api.js"); + }; + }; + types: { + DiscordComponentMessageSpec: import("../../extensions/discord/api.js").DiscordComponentMessageSpec; + DiscordProbe: import("../../extensions/discord/api.js").DiscordProbe; + DiscordSendComponents: import("../../extensions/discord/api.js").DiscordSendComponents; + DiscordSendEmbeds: import("../../extensions/discord/api.js").DiscordSendEmbeds; + DiscordSendResult: import("../../extensions/discord/api.js").DiscordSendResult; + DiscordTokenResolution: import("../../extensions/discord/api.js").DiscordTokenResolution; + InspectedDiscordAccount: import("../../extensions/discord/api.js").InspectedDiscordAccount; + ResolvedDiscordAccount: import("../../extensions/discord/api.js").ResolvedDiscordAccount; + }; + }; + "discord-thread-bindings": { + module: typeof import("../../extensions/discord/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/discord/runtime-api.js"); + }; + }; + types: { + ThreadBindingManager: import("../../extensions/discord/runtime-api.js").ThreadBindingManager; + ThreadBindingRecord: import("../../extensions/discord/runtime-api.js").ThreadBindingRecord; + ThreadBindingTargetKind: import("../../extensions/discord/runtime-api.js").ThreadBindingTargetKind; + }; + }; + "discord-timeouts": { + module: typeof import("../../extensions/discord/timeouts.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/discord/timeouts.js"); + }; + }; + types: {}; + }; + "anthropic-cli": { + module: typeof import("../../extensions/anthropic/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/anthropic/api.js"); + }; + }; + types: {}; + }; + "bluebubbles-policy": { + module: typeof import("../../extensions/bluebubbles/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/bluebubbles/api.js"); + }; + }; + types: {}; + }; + browser: { + module: typeof import("../../extensions/browser/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/browser/runtime-api.js"); + }; + }; + types: {}; + }; + "browser-runtime": { + module: typeof import("../../extensions/browser/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/browser/runtime-api.js"); + }; + }; + types: { + BrowserBridge: import("../../extensions/browser/runtime-api.js").BrowserBridge; + BrowserCreateProfileResult: import("../../extensions/browser/runtime-api.js").BrowserCreateProfileResult; + BrowserDeleteProfileResult: import("../../extensions/browser/runtime-api.js").BrowserDeleteProfileResult; + BrowserExecutable: import("../../extensions/browser/runtime-api.js").BrowserExecutable; + BrowserFormField: import("../../extensions/browser/runtime-api.js").BrowserFormField; + BrowserResetProfileResult: import("../../extensions/browser/runtime-api.js").BrowserResetProfileResult; + BrowserRouteRegistrar: import("../../extensions/browser/runtime-api.js").BrowserRouteRegistrar; + BrowserServerState: import("../../extensions/browser/runtime-api.js").BrowserServerState; + BrowserStatus: import("../../extensions/browser/runtime-api.js").BrowserStatus; + BrowserTab: import("../../extensions/browser/runtime-api.js").BrowserTab; + BrowserTransport: import("../../extensions/browser/runtime-api.js").BrowserTransport; + OpenClawPluginApi: import("../../extensions/browser/runtime-api.js").OpenClawPluginApi; + OpenClawPluginToolContext: import("../../extensions/browser/runtime-api.js").OpenClawPluginToolContext; + OpenClawPluginToolFactory: import("../../extensions/browser/runtime-api.js").OpenClawPluginToolFactory; + ProfileStatus: import("../../extensions/browser/runtime-api.js").ProfileStatus; + ResolvedBrowserConfig: import("../../extensions/browser/runtime-api.js").ResolvedBrowserConfig; + ResolvedBrowserProfile: import("../../extensions/browser/runtime-api.js").ResolvedBrowserProfile; + SnapshotResult: import("../../extensions/browser/runtime-api.js").SnapshotResult; + }; + }; + "cloudflare-ai-gateway": { + module: typeof import("../../extensions/cloudflare-ai-gateway/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/cloudflare-ai-gateway/api.js"); + }; + }; + types: {}; + }; + byteplus: { + module: typeof import("../../extensions/byteplus/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/byteplus/api.js"); + }; + }; + types: {}; + }; + chutes: { + module: typeof import("../../extensions/chutes/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/chutes/api.js"); + }; + }; + types: {}; + }; + deepseek: { + module: typeof import("../../extensions/deepseek/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/deepseek/api.js"); + }; + }; + types: {}; + }; + "feishu-conversation": { + module: typeof import("../../extensions/feishu/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/feishu/api.js"); + }; + }; + types: {}; + }; + google: { + module: typeof import("../../extensions/google/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/google/api.js"); + }; + }; + types: {}; + }; + "feishu-setup": { + module: typeof import("../../extensions/feishu/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/feishu/api.js"); + }; + }; + types: {}; + }; + "github-copilot-login": { + module: typeof import("../../extensions/github-copilot/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/github-copilot/api.js"); + }; + }; + types: {}; + }; + huggingface: { + module: typeof import("../../extensions/huggingface/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/huggingface/api.js"); + }; + }; + types: {}; + }; + "imessage-targets": { + module: typeof import("../../extensions/imessage/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/imessage/api.js"); + }; + }; + types: { + ParsedChatTarget: import("../../extensions/imessage/api.js").ParsedChatTarget; + }; + }; + "image-generation-runtime": { + module: typeof import("../../extensions/image-generation-core/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/image-generation-core/runtime-api.js"); + }; + }; + types: { + GenerateImageParams: import("../../extensions/image-generation-core/runtime-api.js").GenerateImageParams; + GenerateImageRuntimeResult: import("../../extensions/image-generation-core/runtime-api.js").GenerateImageRuntimeResult; + }; + }; + "kimi-coding": { + module: typeof import("../../extensions/kimi-coding/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/kimi-coding/api.js"); + }; + }; + types: {}; + }; + kilocode: { + module: typeof import("../../extensions/kilocode/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/kilocode/api.js"); + }; + }; + types: {}; + }; + "imessage-policy": { + module: typeof import("../../extensions/imessage/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/imessage/api.js"); + }; + }; + types: {}; + }; + "imessage-runtime": { + module: typeof import("../../extensions/imessage/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/imessage/runtime-api.js"); + }; + }; + types: { + IMessageProbe: import("../../extensions/imessage/runtime-api.js").IMessageProbe; + }; + }; + "irc-surface": { + module: typeof import("../../extensions/irc/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/irc/api.js"); + }; + }; + types: {}; + }; + "media-understanding-runtime": { + module: typeof import("../../extensions/media-understanding-core/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/media-understanding-core/runtime-api.js"); + }; + }; + types: { + RunMediaUnderstandingFileParams: import("../../extensions/media-understanding-core/runtime-api.js").RunMediaUnderstandingFileParams; + RunMediaUnderstandingFileResult: import("../../extensions/media-understanding-core/runtime-api.js").RunMediaUnderstandingFileResult; + }; + }; + "memory-core-engine-runtime": { + module: typeof import("../../extensions/memory-core/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/memory-core/runtime-api.js"); + }; + }; + types: { + BuiltinMemoryEmbeddingProviderDoctorMetadata: import("../../extensions/memory-core/runtime-api.js").BuiltinMemoryEmbeddingProviderDoctorMetadata; + }; + }; + "mattermost-policy": { + module: typeof import("../../extensions/mattermost/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/mattermost/api.js"); + }; + }; + types: {}; + }; + litellm: { + module: typeof import("../../extensions/litellm/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/litellm/api.js"); + }; + }; + types: {}; + }; + "line-runtime": { + module: typeof import("../../extensions/line/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/line/runtime-api.js"); + }; + }; + types: { + Action: import("../../extensions/line/runtime-api.js").Action; + CardAction: import("../../extensions/line/runtime-api.js").CardAction; + CreateRichMenuParams: import("../../extensions/line/runtime-api.js").CreateRichMenuParams; + FlexBox: import("../../extensions/line/runtime-api.js").FlexBox; + FlexBubble: import("../../extensions/line/runtime-api.js").FlexBubble; + FlexButton: import("../../extensions/line/runtime-api.js").FlexButton; + FlexCarousel: import("../../extensions/line/runtime-api.js").FlexCarousel; + FlexComponent: import("../../extensions/line/runtime-api.js").FlexComponent; + FlexContainer: import("../../extensions/line/runtime-api.js").FlexContainer; + FlexImage: import("../../extensions/line/runtime-api.js").FlexImage; + FlexText: import("../../extensions/line/runtime-api.js").FlexText; + LineChannelData: import("../../extensions/line/runtime-api.js").LineChannelData; + LineConfig: import("../../extensions/line/runtime-api.js").LineConfig; + LineProbeResult: import("../../extensions/line/runtime-api.js").LineProbeResult; + ListItem: import("../../extensions/line/runtime-api.js").ListItem; + ResolvedLineAccount: import("../../extensions/line/runtime-api.js").ResolvedLineAccount; + RichMenuArea: import("../../extensions/line/runtime-api.js").RichMenuArea; + RichMenuAreaRequest: import("../../extensions/line/runtime-api.js").RichMenuAreaRequest; + RichMenuRequest: import("../../extensions/line/runtime-api.js").RichMenuRequest; + RichMenuResponse: import("../../extensions/line/runtime-api.js").RichMenuResponse; + RichMenuSize: import("../../extensions/line/runtime-api.js").RichMenuSize; + }; + }; + "line-surface": { + module: typeof import("../../extensions/line/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/line/runtime-api.js"); + }; + }; + types: { + CardAction: import("../../extensions/line/runtime-api.js").CardAction; + LineChannelData: import("../../extensions/line/runtime-api.js").LineChannelData; + LineConfig: import("../../extensions/line/runtime-api.js").LineConfig; + LineProbeResult: import("../../extensions/line/runtime-api.js").LineProbeResult; + ListItem: import("../../extensions/line/runtime-api.js").ListItem; + ResolvedLineAccount: import("../../extensions/line/runtime-api.js").ResolvedLineAccount; + }; + }; + "matrix-helper": { + module: typeof import("../../extensions/matrix/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/matrix/api.js"); + }; + }; + types: {}; + }; + "matrix-runtime-surface": { + module: typeof import("../../extensions/matrix/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/matrix/runtime-api.js"); + }; + }; + types: {}; + }; + "matrix-surface": { + module: typeof import("../../extensions/matrix/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/matrix/api.js"); + }; + }; + types: {}; + }; + "matrix-thread-bindings": { + module: typeof import("../../extensions/matrix/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/matrix/api.js"); + }; + }; + types: {}; + }; + openrouter: { + module: typeof import("../../extensions/openrouter/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/openrouter/api.js"); + }; + }; + types: {}; + }; + minimax: { + module: typeof import("../../extensions/minimax/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/minimax/api.js"); + }; + }; + types: {}; + }; + modelstudio: { + module: typeof import("../../extensions/modelstudio/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/modelstudio/api.js"); + }; + }; + types: {}; + }; + "modelstudio-definitions": { + module: typeof import("../../extensions/modelstudio/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/modelstudio/api.js"); + }; + }; + types: {}; + }; + moonshot: { + module: typeof import("../../extensions/moonshot/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/moonshot/api.js"); + }; + }; + types: {}; + }; + mistral: { + module: typeof import("../../extensions/mistral/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/mistral/api.js"); + }; + }; + types: {}; + }; + nvidia: { + module: typeof import("../../extensions/nvidia/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/nvidia/api.js"); + }; + }; + types: {}; + }; + ollama: { + module: typeof import("../../extensions/ollama/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/ollama/runtime-api.js"); + }; + }; + types: { + OllamaEmbeddingClient: import("../../extensions/ollama/runtime-api.js").OllamaEmbeddingClient; + OllamaEmbeddingProvider: import("../../extensions/ollama/runtime-api.js").OllamaEmbeddingProvider; + }; + }; + "ollama-surface": { + module: typeof import("../../extensions/ollama/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/ollama/api.js"); + }; + }; + types: { + OllamaModelWithContext: import("../../extensions/ollama/api.js").OllamaModelWithContext; + OllamaTagModel: import("../../extensions/ollama/api.js").OllamaTagModel; + OllamaTagsResponse: import("../../extensions/ollama/api.js").OllamaTagsResponse; + }; + }; + openai: { + module: typeof import("../../extensions/openai/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/openai/api.js"); + }; + }; + types: {}; + }; + opencode: { + module: typeof import("../../extensions/opencode/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/opencode/api.js"); + }; + }; + types: {}; + }; + "opencode-go": { + module: typeof import("../../extensions/opencode-go/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/opencode-go/api.js"); + }; + }; + types: {}; + }; + qianfan: { + module: typeof import("../../extensions/qianfan/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/qianfan/api.js"); + }; + }; + types: {}; + }; + "signal-account": { + module: typeof import("../../extensions/signal/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/signal/api.js"); + }; + }; + types: { + ResolvedSignalAccount: import("../../extensions/signal/api.js").ResolvedSignalAccount; + }; + }; + "signal-surface": { + module: typeof import("../../extensions/signal/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/signal/api.js"); + }; + }; + types: { + ResolvedSignalAccount: import("../../extensions/signal/api.js").ResolvedSignalAccount; + SignalProbe: import("../../extensions/signal/api.js").SignalProbe; + SignalSender: import("../../extensions/signal/api.js").SignalSender; + }; + }; + "provider-reasoning": { + module: typeof import("../../extensions/ollama/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/ollama/api.js"); + }; + }; + types: {}; + }; + "speech-runtime": { + module: typeof import("../../extensions/speech-core/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/speech-core/runtime-api.js"); + }; + }; + types: { + ResolvedTtsConfig: import("../../extensions/speech-core/runtime-api.js").ResolvedTtsConfig; + ResolvedTtsModelOverrides: import("../../extensions/speech-core/runtime-api.js").ResolvedTtsModelOverrides; + TtsDirectiveOverrides: import("../../extensions/speech-core/runtime-api.js").TtsDirectiveOverrides; + TtsDirectiveParseResult: import("../../extensions/speech-core/runtime-api.js").TtsDirectiveParseResult; + TtsResult: import("../../extensions/speech-core/runtime-api.js").TtsResult; + TtsSynthesisResult: import("../../extensions/speech-core/runtime-api.js").TtsSynthesisResult; + TtsTelephonyResult: import("../../extensions/speech-core/runtime-api.js").TtsTelephonyResult; + }; + }; + sglang: { + module: typeof import("../../extensions/sglang/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/sglang/api.js"); + }; + }; + types: {}; + }; + synthetic: { + module: typeof import("../../extensions/synthetic/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/synthetic/api.js"); + }; + }; + types: {}; + }; + "slack-target-parser": { + module: typeof import("../../extensions/slack/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/slack/api.js"); + }; + }; + types: {}; + }; + "slack-account": { + module: typeof import("../../extensions/slack/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/slack/api.js"); + }; + }; + types: { + ResolvedSlackAccount: import("../../extensions/slack/api.js").ResolvedSlackAccount; + }; + }; + "slack-runtime-surface": { + module: typeof import("../../extensions/slack/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/slack/runtime-api.js"); + }; + }; + types: { + SlackActionContext: import("../../extensions/slack/runtime-api.js").SlackActionContext; + }; + }; + "slack-surface": { + module: typeof import("../../extensions/slack/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/slack/api.js"); + }; + }; + types: { + InspectedSlackAccount: import("../../extensions/slack/api.js").InspectedSlackAccount; + ResolvedSlackAccount: import("../../extensions/slack/api.js").ResolvedSlackAccount; + SlackProbe: import("../../extensions/slack/api.js").SlackProbe; + }; + }; + together: { + module: typeof import("../../extensions/together/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/together/api.js"); + }; + }; + types: {}; + }; + venice: { + module: typeof import("../../extensions/venice/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/venice/api.js"); + }; + }; + types: {}; + }; + "telegram-account": { + module: typeof import("../../extensions/telegram/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/telegram/api.js"); + }; + }; + types: { + ResolvedTelegramAccount: import("../../extensions/telegram/api.js").ResolvedTelegramAccount; + }; + }; + "telegram-allow-from": { + module: typeof import("../../extensions/telegram/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/telegram/api.js"); + }; + }; + types: {}; + }; + "telegram-runtime-surface": { + module: typeof import("../../extensions/telegram/runtime-api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/telegram/runtime-api.js"); + }; + }; + types: { + TelegramApiOverride: import("../../extensions/telegram/runtime-api.js").TelegramApiOverride; + TelegramProbe: import("../../extensions/telegram/runtime-api.js").TelegramProbe; + }; + }; + "telegram-surface": { + module: typeof import("../../extensions/telegram/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/telegram/api.js"); + }; + }; + types: { + InspectedTelegramAccount: import("../../extensions/telegram/api.js").InspectedTelegramAccount; + ProviderInfo: import("../../extensions/telegram/api.js").ProviderInfo; + ResolvedTelegramAccount: import("../../extensions/telegram/api.js").ResolvedTelegramAccount; + StickerMetadata: import("../../extensions/telegram/api.js").StickerMetadata; + TelegramButtonStyle: import("../../extensions/telegram/api.js").TelegramButtonStyle; + TelegramInlineButtons: import("../../extensions/telegram/api.js").TelegramInlineButtons; + TelegramProbe: import("../../extensions/telegram/api.js").TelegramProbe; + TelegramTokenResolution: import("../../extensions/telegram/api.js").TelegramTokenResolution; + }; + }; + "vercel-ai-gateway": { + module: typeof import("../../extensions/vercel-ai-gateway/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/vercel-ai-gateway/api.js"); + }; + }; + types: {}; + }; + volcengine: { + module: typeof import("../../extensions/volcengine/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/volcengine/api.js"); + }; + }; + types: {}; + }; + vllm: { + module: typeof import("../../extensions/vllm/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/vllm/api.js"); + }; + }; + types: {}; + }; + xai: { + module: typeof import("../../extensions/xai/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/xai/api.js"); + }; + }; + types: {}; + }; + xiaomi: { + module: typeof import("../../extensions/xiaomi/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/xiaomi/api.js"); + }; + }; + types: {}; + }; + zai: { + module: typeof import("../../extensions/zai/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/zai/api.js"); + }; + }; + types: {}; + }; + "whatsapp-targets": { + module: typeof import("../../extensions/whatsapp/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/whatsapp/api.js"); + }; + }; + types: {}; + }; + "whatsapp-surface": { + module: typeof import("../../extensions/whatsapp/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/whatsapp/api.js"); + }; + source2: { + module: typeof import("../../extensions/whatsapp/constants.js"); + }; + }; + types: { + WebChannelStatus: import("../../extensions/whatsapp/api.js").WebChannelStatus; + WebInboundMessage: import("../../extensions/whatsapp/api.js").WebInboundMessage; + WebListenerCloseReason: import("../../extensions/whatsapp/api.js").WebListenerCloseReason; + WebMonitorTuning: import("../../extensions/whatsapp/api.js").WebMonitorTuning; + }; + }; + "zalo-setup": { + module: typeof import("../../extensions/zalo/api.js"); + sourceModules: { + source1: { + module: typeof import("../../extensions/zalo/api.js"); + }; + }; + types: {}; + }; +} diff --git a/src/infra/heartbeat-runner.test-harness.ts b/src/infra/heartbeat-runner.test-harness.ts index a31f1f48292..682b65f3b37 100644 --- a/src/infra/heartbeat-runner.test-harness.ts +++ b/src/infra/heartbeat-runner.test-harness.ts @@ -1,12 +1,23 @@ import { beforeEach } from "vitest"; -import { slackPlugin, setSlackRuntime } from "../../extensions/slack/test-api.js"; -import { telegramPlugin, setTelegramRuntime } from "../../extensions/telegram/test-api.js"; -import { whatsappPlugin, setWhatsAppRuntime } from "../../extensions/whatsapp/test-api.js"; import type { ChannelPlugin } from "../channels/plugins/types.plugin.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; -import { createPluginRuntime } from "../plugins/runtime/index.js"; +import { createPluginRuntime, type PluginRuntime } from "../plugins/runtime/index.js"; +import { loadBundledPluginTestApiSync } from "../test-utils/bundled-plugin-public-surface.js"; import { createTestRegistry } from "../test-utils/channel-plugins.js"; +const { slackPlugin, setSlackRuntime } = loadBundledPluginTestApiSync<{ + slackPlugin: ChannelPlugin; + setSlackRuntime: (runtime: PluginRuntime) => void; +}>("slack"); +const { telegramPlugin, setTelegramRuntime } = loadBundledPluginTestApiSync<{ + telegramPlugin: ChannelPlugin; + setTelegramRuntime: (runtime: PluginRuntime) => void; +}>("telegram"); +const { whatsappPlugin, setWhatsAppRuntime } = loadBundledPluginTestApiSync<{ + whatsappPlugin: ChannelPlugin; + setWhatsAppRuntime: (runtime: PluginRuntime) => void; +}>("whatsapp"); + const slackChannelPlugin = slackPlugin as unknown as ChannelPlugin; const telegramChannelPlugin = telegramPlugin as unknown as ChannelPlugin; const whatsappChannelPlugin = whatsappPlugin as unknown as ChannelPlugin; diff --git a/src/infra/heartbeat-runner.test-utils.ts b/src/infra/heartbeat-runner.test-utils.ts index 073f3f2505b..626c803f091 100644 --- a/src/infra/heartbeat-runner.test-utils.ts +++ b/src/infra/heartbeat-runner.test-utils.ts @@ -2,14 +2,20 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { vi } from "vitest"; -import { telegramPlugin, setTelegramRuntime } from "../../extensions/telegram/test-api.js"; import * as replyModule from "../auto-reply/reply.js"; +import type { ChannelPlugin } from "../channels/plugins/types.plugin.js"; import type { OpenClawConfig } from "../config/config.js"; import { resolveMainSessionKey } from "../config/sessions.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; -import { createPluginRuntime } from "../plugins/runtime/index.js"; +import { createPluginRuntime, type PluginRuntime } from "../plugins/runtime/index.js"; +import { loadBundledPluginTestApiSync } from "../test-utils/bundled-plugin-public-surface.js"; import { createTestRegistry } from "../test-utils/channel-plugins.js"; +const { telegramPlugin, setTelegramRuntime } = loadBundledPluginTestApiSync<{ + telegramPlugin: ChannelPlugin; + setTelegramRuntime: (runtime: PluginRuntime) => void; +}>("telegram"); + export type HeartbeatSessionSeed = { sessionId?: string; updatedAt?: number; diff --git a/src/infra/outbound/cfg-threading.guard.test.ts b/src/infra/outbound/cfg-threading.guard.test.ts index 7dfc8ca5e4f..2f8a33604bb 100644 --- a/src/infra/outbound/cfg-threading.guard.test.ts +++ b/src/infra/outbound/cfg-threading.guard.test.ts @@ -2,6 +2,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; +import { bundledPluginFile } from "../../../test/helpers/bundled-plugin-paths.js"; const thisFilePath = fileURLToPath(import.meta.url); const thisDir = path.dirname(thisFilePath); @@ -61,10 +62,10 @@ function listExtensionFiles(): { function listHighRiskRuntimeCfgFiles(): string[] { return [ - "extensions/telegram/src/action-runtime.ts", - "extensions/discord/src/monitor/reply-delivery.ts", - "extensions/discord/src/monitor/thread-bindings.discord-api.ts", - "extensions/discord/src/monitor/thread-bindings.manager.ts", + bundledPluginFile("telegram", "src/action-runtime.ts"), + bundledPluginFile("discord", "src/monitor/reply-delivery.ts"), + bundledPluginFile("discord", "src/monitor/thread-bindings.discord-api.ts"), + bundledPluginFile("discord", "src/monitor/thread-bindings.manager.ts"), ]; } diff --git a/src/infra/outbound/current-conversation-bindings.ts b/src/infra/outbound/current-conversation-bindings.ts index e8117e2894f..d636651f720 100644 --- a/src/infra/outbound/current-conversation-bindings.ts +++ b/src/infra/outbound/current-conversation-bindings.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import path from "node:path"; import { normalizeConversationText } from "../../acp/conversation-id.js"; -import { bundledChannelPlugins } from "../../channels/plugins/bundled.js"; +import { listBundledChannelPlugins } from "../../channels/plugins/bundled.js"; import { normalizeAnyChannelId } from "../../channels/registry.js"; import { resolveStateDir } from "../../config/paths.js"; import { loadJsonFile } from "../../infra/json-file.js"; @@ -130,7 +130,7 @@ function resolveChannelSupportsCurrentConversationBinding(channel: string): bool (plugin.meta?.aliases ?? []).some((alias) => alias.trim().toLowerCase() === normalized); const plugin = getActivePluginChannelRegistry()?.channels.find((entry) => matchesPluginId(entry.plugin)) - ?.plugin ?? bundledChannelPlugins.find((entry) => matchesPluginId(entry)); + ?.plugin ?? listBundledChannelPlugins().find((entry) => matchesPluginId(entry)); return plugin?.conversationBindings?.supportsCurrentConversationBinding === true; } diff --git a/src/infra/outbound/message-action-runner.test-helpers.ts b/src/infra/outbound/message-action-runner.test-helpers.ts index 65a1cd3a163..f038f496dc1 100644 --- a/src/infra/outbound/message-action-runner.test-helpers.ts +++ b/src/infra/outbound/message-action-runner.test-helpers.ts @@ -1,10 +1,19 @@ -import { slackPlugin, setSlackRuntime } from "../../../extensions/slack/test-api.js"; -import { telegramPlugin, setTelegramRuntime } from "../../../extensions/telegram/test-api.js"; +import type { ChannelPlugin } from "../../channels/plugins/types.plugin.js"; import type { OpenClawConfig } from "../../config/config.js"; import { setActivePluginRegistry } from "../../plugins/runtime.js"; -import { createPluginRuntime } from "../../plugins/runtime/index.js"; +import { createPluginRuntime, type PluginRuntime } from "../../plugins/runtime/index.js"; +import { loadBundledPluginTestApiSync } from "../../test-utils/bundled-plugin-public-surface.js"; import { createTestRegistry } from "../../test-utils/channel-plugins.js"; +const { slackPlugin, setSlackRuntime } = loadBundledPluginTestApiSync<{ + slackPlugin: ChannelPlugin; + setSlackRuntime: (runtime: PluginRuntime) => void; +}>("slack"); +const { telegramPlugin, setTelegramRuntime } = loadBundledPluginTestApiSync<{ + telegramPlugin: ChannelPlugin; + setTelegramRuntime: (runtime: PluginRuntime) => void; +}>("telegram"); + export const slackConfig = { channels: { slack: { diff --git a/src/infra/plugin-install-path-warnings.test.ts b/src/infra/plugin-install-path-warnings.test.ts index eb7ba108df1..43e6a9e55b7 100644 --- a/src/infra/plugin-install-path-warnings.test.ts +++ b/src/infra/plugin-install-path-warnings.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; +import { repoInstallSpec } from "../../test/helpers/bundled-plugin-paths.js"; import { withTempHome } from "../../test/helpers/temp-home.js"; import { detectPluginInstallPathIssue, @@ -26,6 +27,8 @@ async function detectMatrixCustomPathIssue(sourcePath: string | ((pluginPath: st }); } +const MATRIX_REPO_INSTALL_COMMAND = `openclaw plugins install ${repoInstallSpec("matrix")}`; + describe("plugin install path warnings", () => { it("ignores non-path installs and blank path candidates", async () => { expect( @@ -66,12 +69,12 @@ describe("plugin install path warnings", () => { issue: issue!, pluginLabel: "Matrix", defaultInstallCommand: "openclaw plugins install @openclaw/matrix", - repoInstallCommand: "openclaw plugins install ./extensions/matrix", + repoInstallCommand: MATRIX_REPO_INSTALL_COMMAND, }), ).toEqual([ "Matrix is installed from a custom path that no longer exists: /tmp/openclaw-matrix-missing", 'Reinstall with "openclaw plugins install @openclaw/matrix".', - 'If you are running from a repo checkout, you can also use "openclaw plugins install ./extensions/matrix".', + `If you are running from a repo checkout, you can also use "${MATRIX_REPO_INSTALL_COMMAND}".`, ]); }); @@ -105,14 +108,32 @@ describe("plugin install path warnings", () => { }, pluginLabel: "Matrix", defaultInstallCommand: "openclaw plugins install @openclaw/matrix", - repoInstallCommand: "openclaw plugins install ./extensions/matrix", + repoInstallCommand: MATRIX_REPO_INSTALL_COMMAND, formatCommand: (command) => `<${command}>`, }), ).toEqual([ "Matrix is installed from a custom path: /tmp/matrix-plugin", "Main updates will not automatically replace that plugin with the repo's default Matrix package.", 'Reinstall with "" when you want to return to the standard Matrix plugin.', - 'If you are intentionally running from a repo checkout, reinstall that checkout explicitly with "" after updates.', + `If you are intentionally running from a repo checkout, reinstall that checkout explicitly with "<${MATRIX_REPO_INSTALL_COMMAND}>" after updates.`, + ]); + }); + + it("omits repo checkout guidance when no bundled source hint exists", () => { + expect( + formatPluginInstallPathIssue({ + issue: { + kind: "missing-path", + pluginId: "matrix", + path: "/tmp/openclaw-matrix-missing", + }, + pluginLabel: "Matrix", + defaultInstallCommand: "openclaw plugins install @openclaw/matrix", + repoInstallCommand: null, + }), + ).toEqual([ + "Matrix is installed from a custom path that no longer exists: /tmp/openclaw-matrix-missing", + 'Reinstall with "openclaw plugins install @openclaw/matrix".', ]); }); }); diff --git a/src/infra/plugin-install-path-warnings.ts b/src/infra/plugin-install-path-warnings.ts index 8435a93e33f..43cae1f36e4 100644 --- a/src/infra/plugin-install-path-warnings.ts +++ b/src/infra/plugin-install-path-warnings.ts @@ -53,7 +53,7 @@ export function formatPluginInstallPathIssue(params: { issue: PluginInstallPathIssue; pluginLabel: string; defaultInstallCommand: string; - repoInstallCommand: string; + repoInstallCommand?: string | null; formatCommand?: (command: string) => string; }): string[] { const formatCommand = params.formatCommand ?? ((command: string) => command); @@ -62,12 +62,20 @@ export function formatPluginInstallPathIssue(params: { `${params.pluginLabel} is installed from a custom path: ${params.issue.path}`, `Main updates will not automatically replace that plugin with the repo's default ${params.pluginLabel} package.`, `Reinstall with "${formatCommand(params.defaultInstallCommand)}" when you want to return to the standard ${params.pluginLabel} plugin.`, - `If you are intentionally running from a repo checkout, reinstall that checkout explicitly with "${formatCommand(params.repoInstallCommand)}" after updates.`, + ...(params.repoInstallCommand + ? [ + `If you are intentionally running from a repo checkout, reinstall that checkout explicitly with "${formatCommand(params.repoInstallCommand)}" after updates.`, + ] + : []), ]; } return [ `${params.pluginLabel} is installed from a custom path that no longer exists: ${params.issue.path}`, `Reinstall with "${formatCommand(params.defaultInstallCommand)}".`, - `If you are running from a repo checkout, you can also use "${formatCommand(params.repoInstallCommand)}".`, + ...(params.repoInstallCommand + ? [ + `If you are running from a repo checkout, you can also use "${formatCommand(params.repoInstallCommand)}".`, + ] + : []), ]; } diff --git a/src/infra/run-node.test.ts b/src/infra/run-node.test.ts index 7dee99a2135..f8a73224922 100644 --- a/src/infra/run-node.test.ts +++ b/src/infra/run-node.test.ts @@ -4,6 +4,11 @@ import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { runNodeMain } from "../../scripts/run-node.mjs"; +import { + bundledDistPluginFile, + bundledPluginFile, + bundledPluginRoot, +} from "../../test/helpers/bundled-plugin-paths.js"; const ROOT_SRC = "src/index.ts"; const ROOT_TSCONFIG = "tsconfig.json"; @@ -11,12 +16,12 @@ const ROOT_PACKAGE = "package.json"; const ROOT_TSDOWN = "tsdown.config.ts"; const DIST_ENTRY = "dist/entry.js"; const BUILD_STAMP = "dist/.buildstamp"; -const EXTENSION_SRC = "extensions/demo/src/index.ts"; -const EXTENSION_MANIFEST = "extensions/demo/openclaw.plugin.json"; -const EXTENSION_PACKAGE = "extensions/demo/package.json"; -const EXTENSION_README = "extensions/demo/README.md"; -const DIST_EXTENSION_MANIFEST = "dist/extensions/demo/openclaw.plugin.json"; -const DIST_EXTENSION_PACKAGE = "dist/extensions/demo/package.json"; +const EXTENSION_SRC = bundledPluginFile("demo", "src/index.ts"); +const EXTENSION_MANIFEST = bundledPluginFile("demo", "openclaw.plugin.json"); +const EXTENSION_PACKAGE = bundledPluginFile("demo", "package.json"); +const EXTENSION_README = bundledPluginFile("demo", "README.md"); +const DIST_EXTENSION_MANIFEST = bundledDistPluginFile("demo", "openclaw.plugin.json"); +const DIST_EXTENSION_PACKAGE = bundledDistPluginFile("demo", "package.json"); const OLD_TIME = new Date("2026-03-13T10:00:00.000Z"); const BUILD_TIME = new Date("2026-03-13T12:00:00.000Z"); @@ -386,7 +391,7 @@ describe("run-node script", () => { const { spawnCalls, spawn, spawnSync } = createSpawnRecorder({ gitHead: "abc123\n", - gitStatus: " M extensions/demo/README.md\n", + gitStatus: ` M ${EXTENSION_README}\n`, }); const exitCode = await runStatusCommand({ tmp, spawn, spawnSync }); @@ -418,7 +423,7 @@ describe("run-node script", () => { const { spawnCalls, spawn, spawnSync } = createSpawnRecorder({ gitHead: "abc123\n", - gitStatus: " M extensions/demo/openclaw.plugin.json\n", + gitStatus: ` M ${EXTENSION_MANIFEST}\n`, }); const exitCode = await runStatusCommand({ tmp, spawn, spawnSync }); @@ -480,7 +485,7 @@ describe("run-node script", () => { ], }); - await fs.mkdir(resolvePath(tmp, "extensions/demo"), { recursive: true }); + await fs.mkdir(resolvePath(tmp, bundledPluginRoot("demo")), { recursive: true }); const { spawnCalls, spawn, spawnSync } = createSpawnRecorder({ gitHead: "abc123\n", diff --git a/src/infra/tsdown-config.test.ts b/src/infra/tsdown-config.test.ts index 1e83a7e83c7..f0c79345e92 100644 --- a/src/infra/tsdown-config.test.ts +++ b/src/infra/tsdown-config.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import { bundledPluginRoot } from "../../test/helpers/bundled-plugin-paths.js"; import tsdownConfig from "../../tsdown.config.ts"; type TsdownConfigEntry = { @@ -17,6 +18,10 @@ function entryKeys(config: TsdownConfigEntry): string[] { return Object.keys(config.entry); } +function bundledEntry(pluginId: string): string { + return `${bundledPluginRoot(pluginId)}/index`; +} + describe("tsdown config", () => { it("keeps core, plugin runtime, plugin-sdk, bundled plugins, and bundled hooks in one dist graph", () => { const configs = asConfigArray(tsdownConfig); @@ -26,7 +31,7 @@ describe("tsdown config", () => { keys.includes("index") || keys.includes("plugins/runtime/index") || keys.includes("plugin-sdk/index") || - keys.includes("extensions/openai/index") || + keys.includes(bundledEntry("openai")) || keys.includes("bundled/boot-md/handler") ); }); @@ -39,13 +44,15 @@ describe("tsdown config", () => { "index", "commands/status.summary.runtime", "plugins/provider-runtime.runtime", + "plugins/runtime/runtime-image-generation.runtime", + "plugins/runtime/runtime-line.contract", "plugins/runtime/index", "plugin-sdk/compat", "plugin-sdk/index", - "extensions/openai/index", - "extensions/matrix/index", - "extensions/msteams/index", - "extensions/whatsapp/index", + bundledEntry("openai"), + bundledEntry("matrix"), + bundledEntry("msteams"), + bundledEntry("whatsapp"), "bundled/boot-md/handler", ]), ); diff --git a/src/infra/update-global.test.ts b/src/infra/update-global.test.ts index bfd01260666..58fa99a577d 100644 --- a/src/infra/update-global.test.ts +++ b/src/infra/update-global.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; +import { bundledDistPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/public-artifacts.js"; import { captureEnv } from "../test-utils/env.js"; import { @@ -21,6 +22,8 @@ import { type CommandRunner, } from "./update-global.js"; +const MATRIX_HELPER_API = bundledDistPluginFile("matrix", "helper-api.js"); + describe("update global helpers", () => { let envSnapshot: ReturnType | undefined; @@ -203,9 +206,9 @@ describe("update global helpers", () => { await expect(collectInstalledGlobalPackageErrors({ packageRoot })).resolves.toEqual([]); - await fs.rm(path.join(packageRoot, "dist/extensions/matrix/helper-api.js")); + await fs.rm(path.join(packageRoot, MATRIX_HELPER_API)); await expect(collectInstalledGlobalPackageErrors({ packageRoot })).resolves.toContain( - "missing bundled runtime sidecar dist/extensions/matrix/helper-api.js", + `missing bundled runtime sidecar ${MATRIX_HELPER_API}`, ); }); }); diff --git a/src/infra/update-runner.test.ts b/src/infra/update-runner.test.ts index be6fa9d8c94..11f591126f8 100644 --- a/src/infra/update-runner.test.ts +++ b/src/infra/update-runner.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { bundledDistPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/public-artifacts.js"; import { withEnvAsync } from "../test-utils/env.js"; import { pathExists } from "../utils.js"; @@ -10,6 +11,7 @@ import { runGatewayUpdate } from "./update-runner.js"; type CommandResponse = { stdout?: string; stderr?: string; code?: number | null }; type CommandResult = { stdout: string; stderr: string; code: number | null }; +const WHATSAPP_LIGHT_RUNTIME_API = bundledDistPluginFile("whatsapp", "light-runtime-api.js"); function toCommandResult(response?: CommandResponse): CommandResult { return { @@ -688,7 +690,7 @@ describe("runGatewayUpdate", () => { expect(result.status).toBe("error"); expect(result.reason).toBe("global install verify"); expect(result.steps.at(-1)?.stderrTail).toContain( - "missing bundled runtime sidecar dist/extensions/whatsapp/light-runtime-api.js", + `missing bundled runtime sidecar ${WHATSAPP_LIGHT_RUNTIME_API}`, ); }); diff --git a/src/infra/watch-node.test.ts b/src/infra/watch-node.test.ts index 8fa92bae1df..81ff76f8de3 100644 --- a/src/infra/watch-node.test.ts +++ b/src/infra/watch-node.test.ts @@ -2,6 +2,13 @@ import { EventEmitter } from "node:events"; import { describe, expect, it, vi } from "vitest"; import { runNodeWatchedPaths } from "../../scripts/run-node.mjs"; import { runWatchMain } from "../../scripts/watch-node.mjs"; +import { bundledPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; + +const VOICE_CALL_README = bundledPluginFile("voice-call", "README.md"); +const VOICE_CALL_MANIFEST = bundledPluginFile("voice-call", "openclaw.plugin.json"); +const VOICE_CALL_PACKAGE = bundledPluginFile("voice-call", "package.json"); +const VOICE_CALL_INDEX = bundledPluginFile("voice-call", "index.ts"); +const VOICE_CALL_RUNTIME = bundledPluginFile("voice-call", "src/runtime.ts"); const createFakeProcess = () => Object.assign(new EventEmitter(), { @@ -50,11 +57,11 @@ describe("watch-node script", () => { expect(watchOptions.ignored("src/infra/watch-node.test.ts")).toBe(true); expect(watchOptions.ignored("src/infra/watch-node.test.tsx")).toBe(true); expect(watchOptions.ignored("src/infra/watch-node-test-helpers.ts")).toBe(true); - expect(watchOptions.ignored("extensions/voice-call/README.md")).toBe(true); - expect(watchOptions.ignored("extensions/voice-call/openclaw.plugin.json")).toBe(false); - expect(watchOptions.ignored("extensions/voice-call/package.json")).toBe(false); - expect(watchOptions.ignored("extensions/voice-call/index.ts")).toBe(false); - expect(watchOptions.ignored("extensions/voice-call/src/runtime.ts")).toBe(false); + expect(watchOptions.ignored(VOICE_CALL_README)).toBe(true); + expect(watchOptions.ignored(VOICE_CALL_MANIFEST)).toBe(false); + expect(watchOptions.ignored(VOICE_CALL_PACKAGE)).toBe(false); + expect(watchOptions.ignored(VOICE_CALL_INDEX)).toBe(false); + expect(watchOptions.ignored(VOICE_CALL_RUNTIME)).toBe(false); expect(watchOptions.ignored("src/infra/watch-node.ts")).toBe(false); expect(watchOptions.ignored("tsconfig.json")).toBe(false); @@ -173,17 +180,17 @@ describe("watch-node script", () => { expect(spawn).toHaveBeenCalledTimes(1); expect(childA.kill).not.toHaveBeenCalled(); - watcher.emit("change", "extensions/voice-call/README.md"); + watcher.emit("change", VOICE_CALL_README); await new Promise((resolve) => setImmediate(resolve)); expect(spawn).toHaveBeenCalledTimes(1); expect(childA.kill).not.toHaveBeenCalled(); - watcher.emit("change", "extensions/voice-call/openclaw.plugin.json"); + watcher.emit("change", VOICE_CALL_MANIFEST); await new Promise((resolve) => setImmediate(resolve)); expect(childA.kill).toHaveBeenCalledWith("SIGTERM"); expect(spawn).toHaveBeenCalledTimes(2); - watcher.emit("change", "extensions/voice-call/package.json"); + watcher.emit("change", VOICE_CALL_PACKAGE); await new Promise((resolve) => setImmediate(resolve)); expect(childB.kill).toHaveBeenCalledWith("SIGTERM"); expect(spawn).toHaveBeenCalledTimes(3); diff --git a/src/plugin-sdk/AGENTS.md b/src/plugin-sdk/AGENTS.md index cd10f32a304..e0e80a7c2b7 100644 --- a/src/plugin-sdk/AGENTS.md +++ b/src/plugin-sdk/AGENTS.md @@ -29,8 +29,8 @@ can affect bundled plugins and third-party plugins. - Prefer `api.runtime` or a focused SDK facade over telling extensions to reach into host internals directly. - When core or tests need bundled plugin helpers, expose them through - `extensions//api.ts` and a matching `src/plugin-sdk/.ts` facade - instead of importing `extensions//src/**` or `extensions//onboard.js` + the plugin package `api.ts` and a matching `src/plugin-sdk/.ts` facade + instead of importing plugin-private `src/**` files or `onboard.js` directly. ## Expanding The Boundary diff --git a/src/plugin-sdk/amazon-bedrock.ts b/src/plugin-sdk/amazon-bedrock.ts index 019dae56428..89cf282cef8 100644 --- a/src/plugin-sdk/amazon-bedrock.ts +++ b/src/plugin-sdk/amazon-bedrock.ts @@ -1,8 +1,35 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - discoverBedrockModels, - mergeImplicitBedrockProvider, - resetBedrockDiscoveryCacheForTest, - resolveBedrockConfigApiKey, - resolveImplicitBedrockProvider, -} from "../../extensions/amazon-bedrock/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["amazon-bedrock"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "amazon-bedrock", + artifactBasename: "api.js", + }); +} +export const discoverBedrockModels: FacadeModule["discoverBedrockModels"] = ((...args) => + loadFacadeModule()["discoverBedrockModels"](...args)) as FacadeModule["discoverBedrockModels"]; +export const mergeImplicitBedrockProvider: FacadeModule["mergeImplicitBedrockProvider"] = (( + ...args +) => + loadFacadeModule()["mergeImplicitBedrockProvider"]( + ...args, + )) as FacadeModule["mergeImplicitBedrockProvider"]; +export const resetBedrockDiscoveryCacheForTest: FacadeModule["resetBedrockDiscoveryCacheForTest"] = + ((...args) => + loadFacadeModule()["resetBedrockDiscoveryCacheForTest"]( + ...args, + )) as FacadeModule["resetBedrockDiscoveryCacheForTest"]; +export const resolveBedrockConfigApiKey: FacadeModule["resolveBedrockConfigApiKey"] = ((...args) => + loadFacadeModule()["resolveBedrockConfigApiKey"]( + ...args, + )) as FacadeModule["resolveBedrockConfigApiKey"]; +export const resolveImplicitBedrockProvider: FacadeModule["resolveImplicitBedrockProvider"] = (( + ...args +) => + loadFacadeModule()["resolveImplicitBedrockProvider"]( + ...args, + )) as FacadeModule["resolveImplicitBedrockProvider"]; diff --git a/src/plugin-sdk/anthropic-cli.ts b/src/plugin-sdk/anthropic-cli.ts index 0896b3f78c9..f2e233fc9c2 100644 --- a/src/plugin-sdk/anthropic-cli.ts +++ b/src/plugin-sdk/anthropic-cli.ts @@ -1,2 +1,16 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { CLAUDE_CLI_BACKEND_ID, isClaudeCliProvider } from "../../extensions/anthropic/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["anthropic-cli"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "anthropic", + artifactBasename: "api.js", + }); +} +export const CLAUDE_CLI_BACKEND_ID: FacadeModule["CLAUDE_CLI_BACKEND_ID"] = + loadFacadeModule()["CLAUDE_CLI_BACKEND_ID"]; +export const isClaudeCliProvider: FacadeModule["isClaudeCliProvider"] = ((...args) => + loadFacadeModule()["isClaudeCliProvider"](...args)) as FacadeModule["isClaudeCliProvider"]; diff --git a/src/plugin-sdk/anthropic-vertex.ts b/src/plugin-sdk/anthropic-vertex.ts index 9ac0752f75d..d0eb1666d1e 100644 --- a/src/plugin-sdk/anthropic-vertex.ts +++ b/src/plugin-sdk/anthropic-vertex.ts @@ -1,14 +1,69 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - ANTHROPIC_VERTEX_DEFAULT_MODEL_ID, - buildAnthropicVertexProvider, - hasAnthropicVertexAvailableAuth, - hasAnthropicVertexCredentials, - mergeImplicitAnthropicVertexProvider, - resolveAnthropicVertexClientRegion, - resolveAnthropicVertexConfigApiKey, - resolveImplicitAnthropicVertexProvider, - resolveAnthropicVertexProjectId, - resolveAnthropicVertexRegion, - resolveAnthropicVertexRegionFromBaseUrl, -} from "../../extensions/anthropic-vertex/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["anthropic-vertex"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "anthropic-vertex", + artifactBasename: "api.js", + }); +} +export const ANTHROPIC_VERTEX_DEFAULT_MODEL_ID: FacadeModule["ANTHROPIC_VERTEX_DEFAULT_MODEL_ID"] = + loadFacadeModule()["ANTHROPIC_VERTEX_DEFAULT_MODEL_ID"]; +export const buildAnthropicVertexProvider: FacadeModule["buildAnthropicVertexProvider"] = (( + ...args +) => + loadFacadeModule()["buildAnthropicVertexProvider"]( + ...args, + )) as FacadeModule["buildAnthropicVertexProvider"]; +export const hasAnthropicVertexAvailableAuth: FacadeModule["hasAnthropicVertexAvailableAuth"] = (( + ...args +) => + loadFacadeModule()["hasAnthropicVertexAvailableAuth"]( + ...args, + )) as FacadeModule["hasAnthropicVertexAvailableAuth"]; +export const hasAnthropicVertexCredentials: FacadeModule["hasAnthropicVertexCredentials"] = (( + ...args +) => + loadFacadeModule()["hasAnthropicVertexCredentials"]( + ...args, + )) as FacadeModule["hasAnthropicVertexCredentials"]; +export const mergeImplicitAnthropicVertexProvider: FacadeModule["mergeImplicitAnthropicVertexProvider"] = + ((...args) => + loadFacadeModule()["mergeImplicitAnthropicVertexProvider"]( + ...args, + )) as FacadeModule["mergeImplicitAnthropicVertexProvider"]; +export const resolveAnthropicVertexClientRegion: FacadeModule["resolveAnthropicVertexClientRegion"] = + ((...args) => + loadFacadeModule()["resolveAnthropicVertexClientRegion"]( + ...args, + )) as FacadeModule["resolveAnthropicVertexClientRegion"]; +export const resolveAnthropicVertexConfigApiKey: FacadeModule["resolveAnthropicVertexConfigApiKey"] = + ((...args) => + loadFacadeModule()["resolveAnthropicVertexConfigApiKey"]( + ...args, + )) as FacadeModule["resolveAnthropicVertexConfigApiKey"]; +export const resolveImplicitAnthropicVertexProvider: FacadeModule["resolveImplicitAnthropicVertexProvider"] = + ((...args) => + loadFacadeModule()["resolveImplicitAnthropicVertexProvider"]( + ...args, + )) as FacadeModule["resolveImplicitAnthropicVertexProvider"]; +export const resolveAnthropicVertexProjectId: FacadeModule["resolveAnthropicVertexProjectId"] = (( + ...args +) => + loadFacadeModule()["resolveAnthropicVertexProjectId"]( + ...args, + )) as FacadeModule["resolveAnthropicVertexProjectId"]; +export const resolveAnthropicVertexRegion: FacadeModule["resolveAnthropicVertexRegion"] = (( + ...args +) => + loadFacadeModule()["resolveAnthropicVertexRegion"]( + ...args, + )) as FacadeModule["resolveAnthropicVertexRegion"]; +export const resolveAnthropicVertexRegionFromBaseUrl: FacadeModule["resolveAnthropicVertexRegionFromBaseUrl"] = + ((...args) => + loadFacadeModule()["resolveAnthropicVertexRegionFromBaseUrl"]( + ...args, + )) as FacadeModule["resolveAnthropicVertexRegionFromBaseUrl"]; diff --git a/src/plugin-sdk/bluebubbles-policy.ts b/src/plugin-sdk/bluebubbles-policy.ts index f3b4a7114ec..abf70120dc4 100644 --- a/src/plugin-sdk/bluebubbles-policy.ts +++ b/src/plugin-sdk/bluebubbles-policy.ts @@ -1,6 +1,26 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - isAllowedBlueBubblesSender, - resolveBlueBubblesGroupRequireMention, - resolveBlueBubblesGroupToolPolicy, -} from "../../extensions/bluebubbles/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["bluebubbles-policy"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "bluebubbles", + artifactBasename: "api.js", + }); +} +export const isAllowedBlueBubblesSender: FacadeModule["isAllowedBlueBubblesSender"] = ((...args) => + loadFacadeModule()["isAllowedBlueBubblesSender"]( + ...args, + )) as FacadeModule["isAllowedBlueBubblesSender"]; +export const resolveBlueBubblesGroupRequireMention: FacadeModule["resolveBlueBubblesGroupRequireMention"] = + ((...args) => + loadFacadeModule()["resolveBlueBubblesGroupRequireMention"]( + ...args, + )) as FacadeModule["resolveBlueBubblesGroupRequireMention"]; +export const resolveBlueBubblesGroupToolPolicy: FacadeModule["resolveBlueBubblesGroupToolPolicy"] = + ((...args) => + loadFacadeModule()["resolveBlueBubblesGroupToolPolicy"]( + ...args, + )) as FacadeModule["resolveBlueBubblesGroupToolPolicy"]; diff --git a/src/plugin-sdk/bluebubbles.ts b/src/plugin-sdk/bluebubbles.ts index b693e8bac95..abe6ff80bef 100644 --- a/src/plugin-sdk/bluebubbles.ts +++ b/src/plugin-sdk/bluebubbles.ts @@ -1,11 +1,13 @@ +import type { OpenClawConfig } from "../config/config.js"; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; import { parseChatTargetPrefixesOrThrow, resolveServicePrefixedTarget, type ParsedChatTarget, } from "./imessage-targets.js"; -// Narrow plugin-sdk surface for the bundled bluebubbles plugin. -// Keep this list additive and scoped to symbols used under extensions/bluebubbles. +// Narrow plugin-sdk surface for the bundled BlueBubbles plugin. +// Keep this list additive and scoped to the conversation-binding seam only. type BlueBubblesService = "imessage" | "sms" | "auto"; @@ -13,6 +15,31 @@ type BlueBubblesTarget = | ParsedChatTarget | { kind: "handle"; to: string; service: BlueBubblesService }; +export type BlueBubblesConversationBindingManager = { + stop: () => void; +}; + +type BlueBubblesFacadeModule = { + createBlueBubblesConversationBindingManager: (params: { + accountId?: string; + cfg: OpenClawConfig; + }) => BlueBubblesConversationBindingManager; +}; + +function loadBlueBubblesFacadeModule(): BlueBubblesFacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "bluebubbles", + artifactBasename: "api.js", + }); +} + +export function createBlueBubblesConversationBindingManager(params: { + accountId?: string; + cfg: OpenClawConfig; +}): BlueBubblesConversationBindingManager { + return loadBlueBubblesFacadeModule().createBlueBubblesConversationBindingManager(params); +} + const CHAT_ID_PREFIXES = ["chat_id:", "chatid:", "chat:"]; const CHAT_GUID_PREFIXES = ["chat_guid:", "chatguid:", "guid:"]; const CHAT_IDENTIFIER_PREFIXES = ["chat_identifier:", "chatidentifier:", "chatident:"]; diff --git a/src/plugin-sdk/browser-runtime.ts b/src/plugin-sdk/browser-runtime.ts index 663c50db9c7..1c41e395def 100644 --- a/src/plugin-sdk/browser-runtime.ts +++ b/src/plugin-sdk/browser-runtime.ts @@ -1,2 +1,245 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export * from "../../extensions/browser/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["browser-runtime"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "browser", + artifactBasename: "runtime-api.js", + }); +} +export const DEFAULT_AI_SNAPSHOT_MAX_CHARS: FacadeModule["DEFAULT_AI_SNAPSHOT_MAX_CHARS"] = + loadFacadeModule()["DEFAULT_AI_SNAPSHOT_MAX_CHARS"]; +export const DEFAULT_BROWSER_EVALUATE_ENABLED: FacadeModule["DEFAULT_BROWSER_EVALUATE_ENABLED"] = + loadFacadeModule()["DEFAULT_BROWSER_EVALUATE_ENABLED"]; +export const DEFAULT_OPENCLAW_BROWSER_COLOR: FacadeModule["DEFAULT_OPENCLAW_BROWSER_COLOR"] = + loadFacadeModule()["DEFAULT_OPENCLAW_BROWSER_COLOR"]; +export const DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME: FacadeModule["DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME"] = + loadFacadeModule()["DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME"]; +export const DEFAULT_UPLOAD_DIR: FacadeModule["DEFAULT_UPLOAD_DIR"] = + loadFacadeModule()["DEFAULT_UPLOAD_DIR"]; +export const applyBrowserProxyPaths: FacadeModule["applyBrowserProxyPaths"] = ((...args) => + loadFacadeModule()["applyBrowserProxyPaths"](...args)) as FacadeModule["applyBrowserProxyPaths"]; +export const browserAct: FacadeModule["browserAct"] = ((...args) => + loadFacadeModule()["browserAct"](...args)) as FacadeModule["browserAct"]; +export const browserArmDialog: FacadeModule["browserArmDialog"] = ((...args) => + loadFacadeModule()["browserArmDialog"](...args)) as FacadeModule["browserArmDialog"]; +export const browserArmFileChooser: FacadeModule["browserArmFileChooser"] = ((...args) => + loadFacadeModule()["browserArmFileChooser"](...args)) as FacadeModule["browserArmFileChooser"]; +export const browserCloseTab: FacadeModule["browserCloseTab"] = ((...args) => + loadFacadeModule()["browserCloseTab"](...args)) as FacadeModule["browserCloseTab"]; +export const browserConsoleMessages: FacadeModule["browserConsoleMessages"] = ((...args) => + loadFacadeModule()["browserConsoleMessages"](...args)) as FacadeModule["browserConsoleMessages"]; +export const browserCreateProfile: FacadeModule["browserCreateProfile"] = ((...args) => + loadFacadeModule()["browserCreateProfile"](...args)) as FacadeModule["browserCreateProfile"]; +export const browserDeleteProfile: FacadeModule["browserDeleteProfile"] = ((...args) => + loadFacadeModule()["browserDeleteProfile"](...args)) as FacadeModule["browserDeleteProfile"]; +export const browserFocusTab: FacadeModule["browserFocusTab"] = ((...args) => + loadFacadeModule()["browserFocusTab"](...args)) as FacadeModule["browserFocusTab"]; +export const browserHandlers: FacadeModule["browserHandlers"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["browserHandlers"] as object, +) as FacadeModule["browserHandlers"]; +export const browserNavigate: FacadeModule["browserNavigate"] = ((...args) => + loadFacadeModule()["browserNavigate"](...args)) as FacadeModule["browserNavigate"]; +export const browserOpenTab: FacadeModule["browserOpenTab"] = ((...args) => + loadFacadeModule()["browserOpenTab"](...args)) as FacadeModule["browserOpenTab"]; +export const browserPdfSave: FacadeModule["browserPdfSave"] = ((...args) => + loadFacadeModule()["browserPdfSave"](...args)) as FacadeModule["browserPdfSave"]; +export const browserProfiles: FacadeModule["browserProfiles"] = ((...args) => + loadFacadeModule()["browserProfiles"](...args)) as FacadeModule["browserProfiles"]; +export const browserResetProfile: FacadeModule["browserResetProfile"] = ((...args) => + loadFacadeModule()["browserResetProfile"](...args)) as FacadeModule["browserResetProfile"]; +export const browserScreenshotAction: FacadeModule["browserScreenshotAction"] = ((...args) => + loadFacadeModule()["browserScreenshotAction"]( + ...args, + )) as FacadeModule["browserScreenshotAction"]; +export const browserSnapshot: FacadeModule["browserSnapshot"] = ((...args) => + loadFacadeModule()["browserSnapshot"](...args)) as FacadeModule["browserSnapshot"]; +export const browserStart: FacadeModule["browserStart"] = ((...args) => + loadFacadeModule()["browserStart"](...args)) as FacadeModule["browserStart"]; +export const browserStatus: FacadeModule["browserStatus"] = ((...args) => + loadFacadeModule()["browserStatus"](...args)) as FacadeModule["browserStatus"]; +export const browserStop: FacadeModule["browserStop"] = ((...args) => + loadFacadeModule()["browserStop"](...args)) as FacadeModule["browserStop"]; +export const browserTabAction: FacadeModule["browserTabAction"] = ((...args) => + loadFacadeModule()["browserTabAction"](...args)) as FacadeModule["browserTabAction"]; +export const browserTabs: FacadeModule["browserTabs"] = ((...args) => + loadFacadeModule()["browserTabs"](...args)) as FacadeModule["browserTabs"]; +export const closeTrackedBrowserTabsForSessions: FacadeModule["closeTrackedBrowserTabsForSessions"] = + ((...args) => + loadFacadeModule()["closeTrackedBrowserTabsForSessions"]( + ...args, + )) as FacadeModule["closeTrackedBrowserTabsForSessions"]; +export const createBrowserControlContext: FacadeModule["createBrowserControlContext"] = (( + ...args +) => + loadFacadeModule()["createBrowserControlContext"]( + ...args, + )) as FacadeModule["createBrowserControlContext"]; +export const createBrowserPluginService: FacadeModule["createBrowserPluginService"] = ((...args) => + loadFacadeModule()["createBrowserPluginService"]( + ...args, + )) as FacadeModule["createBrowserPluginService"]; +export const createBrowserRouteContext: FacadeModule["createBrowserRouteContext"] = ((...args) => + loadFacadeModule()["createBrowserRouteContext"]( + ...args, + )) as FacadeModule["createBrowserRouteContext"]; +export const createBrowserRouteDispatcher: FacadeModule["createBrowserRouteDispatcher"] = (( + ...args +) => + loadFacadeModule()["createBrowserRouteDispatcher"]( + ...args, + )) as FacadeModule["createBrowserRouteDispatcher"]; +export const createBrowserRuntimeState: FacadeModule["createBrowserRuntimeState"] = ((...args) => + loadFacadeModule()["createBrowserRuntimeState"]( + ...args, + )) as FacadeModule["createBrowserRuntimeState"]; +export const createBrowserTool: FacadeModule["createBrowserTool"] = ((...args) => + loadFacadeModule()["createBrowserTool"](...args)) as FacadeModule["createBrowserTool"]; +export const definePluginEntry: FacadeModule["definePluginEntry"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["definePluginEntry"] as object, +) as FacadeModule["definePluginEntry"]; +export const ensureBrowserControlAuth: FacadeModule["ensureBrowserControlAuth"] = ((...args) => + loadFacadeModule()["ensureBrowserControlAuth"]( + ...args, + )) as FacadeModule["ensureBrowserControlAuth"]; +export const getBrowserControlState: FacadeModule["getBrowserControlState"] = ((...args) => + loadFacadeModule()["getBrowserControlState"](...args)) as FacadeModule["getBrowserControlState"]; +export const getBrowserProfileCapabilities: FacadeModule["getBrowserProfileCapabilities"] = (( + ...args +) => + loadFacadeModule()["getBrowserProfileCapabilities"]( + ...args, + )) as FacadeModule["getBrowserProfileCapabilities"]; +export const handleBrowserGatewayRequest: FacadeModule["handleBrowserGatewayRequest"] = (( + ...args +) => + loadFacadeModule()["handleBrowserGatewayRequest"]( + ...args, + )) as FacadeModule["handleBrowserGatewayRequest"]; +export const installBrowserAuthMiddleware: FacadeModule["installBrowserAuthMiddleware"] = (( + ...args +) => + loadFacadeModule()["installBrowserAuthMiddleware"]( + ...args, + )) as FacadeModule["installBrowserAuthMiddleware"]; +export const installBrowserCommonMiddleware: FacadeModule["installBrowserCommonMiddleware"] = (( + ...args +) => + loadFacadeModule()["installBrowserCommonMiddleware"]( + ...args, + )) as FacadeModule["installBrowserCommonMiddleware"]; +export const isPersistentBrowserProfileMutation: FacadeModule["isPersistentBrowserProfileMutation"] = + ((...args) => + loadFacadeModule()["isPersistentBrowserProfileMutation"]( + ...args, + )) as FacadeModule["isPersistentBrowserProfileMutation"]; +export const movePathToTrash: FacadeModule["movePathToTrash"] = ((...args) => + loadFacadeModule()["movePathToTrash"](...args)) as FacadeModule["movePathToTrash"]; +export const normalizeBrowserFormField: FacadeModule["normalizeBrowserFormField"] = ((...args) => + loadFacadeModule()["normalizeBrowserFormField"]( + ...args, + )) as FacadeModule["normalizeBrowserFormField"]; +export const normalizeBrowserFormFieldValue: FacadeModule["normalizeBrowserFormFieldValue"] = (( + ...args +) => + loadFacadeModule()["normalizeBrowserFormFieldValue"]( + ...args, + )) as FacadeModule["normalizeBrowserFormFieldValue"]; +export const normalizeBrowserRequestPath: FacadeModule["normalizeBrowserRequestPath"] = (( + ...args +) => + loadFacadeModule()["normalizeBrowserRequestPath"]( + ...args, + )) as FacadeModule["normalizeBrowserRequestPath"]; +export const parseBrowserMajorVersion: FacadeModule["parseBrowserMajorVersion"] = ((...args) => + loadFacadeModule()["parseBrowserMajorVersion"]( + ...args, + )) as FacadeModule["parseBrowserMajorVersion"]; +export const persistBrowserProxyFiles: FacadeModule["persistBrowserProxyFiles"] = ((...args) => + loadFacadeModule()["persistBrowserProxyFiles"]( + ...args, + )) as FacadeModule["persistBrowserProxyFiles"]; +export const readBrowserVersion: FacadeModule["readBrowserVersion"] = ((...args) => + loadFacadeModule()["readBrowserVersion"](...args)) as FacadeModule["readBrowserVersion"]; +export const redactCdpUrl: FacadeModule["redactCdpUrl"] = ((...args) => + loadFacadeModule()["redactCdpUrl"](...args)) as FacadeModule["redactCdpUrl"]; +export const registerBrowserCli: FacadeModule["registerBrowserCli"] = ((...args) => + loadFacadeModule()["registerBrowserCli"](...args)) as FacadeModule["registerBrowserCli"]; +export const registerBrowserRoutes: FacadeModule["registerBrowserRoutes"] = ((...args) => + loadFacadeModule()["registerBrowserRoutes"](...args)) as FacadeModule["registerBrowserRoutes"]; +export const resolveBrowserConfig: FacadeModule["resolveBrowserConfig"] = ((...args) => + loadFacadeModule()["resolveBrowserConfig"](...args)) as FacadeModule["resolveBrowserConfig"]; +export const resolveBrowserControlAuth: FacadeModule["resolveBrowserControlAuth"] = ((...args) => + loadFacadeModule()["resolveBrowserControlAuth"]( + ...args, + )) as FacadeModule["resolveBrowserControlAuth"]; +export const resolveExistingPathsWithinRoot: FacadeModule["resolveExistingPathsWithinRoot"] = (( + ...args +) => + loadFacadeModule()["resolveExistingPathsWithinRoot"]( + ...args, + )) as FacadeModule["resolveExistingPathsWithinRoot"]; +export const resolveGoogleChromeExecutableForPlatform: FacadeModule["resolveGoogleChromeExecutableForPlatform"] = + ((...args) => + loadFacadeModule()["resolveGoogleChromeExecutableForPlatform"]( + ...args, + )) as FacadeModule["resolveGoogleChromeExecutableForPlatform"]; +export const resolveProfile: FacadeModule["resolveProfile"] = ((...args) => + loadFacadeModule()["resolveProfile"](...args)) as FacadeModule["resolveProfile"]; +export const resolveRequestedBrowserProfile: FacadeModule["resolveRequestedBrowserProfile"] = (( + ...args +) => + loadFacadeModule()["resolveRequestedBrowserProfile"]( + ...args, + )) as FacadeModule["resolveRequestedBrowserProfile"]; +export const runBrowserProxyCommand: FacadeModule["runBrowserProxyCommand"] = ((...args) => + loadFacadeModule()["runBrowserProxyCommand"](...args)) as FacadeModule["runBrowserProxyCommand"]; +export const startBrowserBridgeServer: FacadeModule["startBrowserBridgeServer"] = ((...args) => + loadFacadeModule()["startBrowserBridgeServer"]( + ...args, + )) as FacadeModule["startBrowserBridgeServer"]; +export const startBrowserControlServiceFromConfig: FacadeModule["startBrowserControlServiceFromConfig"] = + ((...args) => + loadFacadeModule()["startBrowserControlServiceFromConfig"]( + ...args, + )) as FacadeModule["startBrowserControlServiceFromConfig"]; +export const stopBrowserBridgeServer: FacadeModule["stopBrowserBridgeServer"] = ((...args) => + loadFacadeModule()["stopBrowserBridgeServer"]( + ...args, + )) as FacadeModule["stopBrowserBridgeServer"]; +export const stopBrowserControlService: FacadeModule["stopBrowserControlService"] = ((...args) => + loadFacadeModule()["stopBrowserControlService"]( + ...args, + )) as FacadeModule["stopBrowserControlService"]; +export const stopBrowserRuntime: FacadeModule["stopBrowserRuntime"] = ((...args) => + loadFacadeModule()["stopBrowserRuntime"](...args)) as FacadeModule["stopBrowserRuntime"]; +export const trackSessionBrowserTab: FacadeModule["trackSessionBrowserTab"] = ((...args) => + loadFacadeModule()["trackSessionBrowserTab"](...args)) as FacadeModule["trackSessionBrowserTab"]; +export const untrackSessionBrowserTab: FacadeModule["untrackSessionBrowserTab"] = ((...args) => + loadFacadeModule()["untrackSessionBrowserTab"]( + ...args, + )) as FacadeModule["untrackSessionBrowserTab"]; +export type BrowserBridge = FacadeEntry["types"]["BrowserBridge"]; +export type BrowserCreateProfileResult = FacadeEntry["types"]["BrowserCreateProfileResult"]; +export type BrowserDeleteProfileResult = FacadeEntry["types"]["BrowserDeleteProfileResult"]; +export type BrowserExecutable = FacadeEntry["types"]["BrowserExecutable"]; +export type BrowserFormField = FacadeEntry["types"]["BrowserFormField"]; +export type BrowserResetProfileResult = FacadeEntry["types"]["BrowserResetProfileResult"]; +export type BrowserRouteRegistrar = FacadeEntry["types"]["BrowserRouteRegistrar"]; +export type BrowserServerState = FacadeEntry["types"]["BrowserServerState"]; +export type BrowserStatus = FacadeEntry["types"]["BrowserStatus"]; +export type BrowserTab = FacadeEntry["types"]["BrowserTab"]; +export type BrowserTransport = FacadeEntry["types"]["BrowserTransport"]; +export type OpenClawPluginApi = FacadeEntry["types"]["OpenClawPluginApi"]; +export type OpenClawPluginToolContext = FacadeEntry["types"]["OpenClawPluginToolContext"]; +export type OpenClawPluginToolFactory = FacadeEntry["types"]["OpenClawPluginToolFactory"]; +export type ProfileStatus = FacadeEntry["types"]["ProfileStatus"]; +export type ResolvedBrowserConfig = FacadeEntry["types"]["ResolvedBrowserConfig"]; +export type ResolvedBrowserProfile = FacadeEntry["types"]["ResolvedBrowserProfile"]; +export type SnapshotResult = FacadeEntry["types"]["SnapshotResult"]; diff --git a/src/plugin-sdk/browser.ts b/src/plugin-sdk/browser.ts index 68875e91922..8728af181ba 100644 --- a/src/plugin-sdk/browser.ts +++ b/src/plugin-sdk/browser.ts @@ -1,8 +1,32 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - browserHandlers, - createBrowserPluginService, - createBrowserTool, - handleBrowserGatewayRequest, - registerBrowserCli, -} from "../../extensions/browser/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["browser"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "browser", + artifactBasename: "runtime-api.js", + }); +} +export const browserHandlers: FacadeModule["browserHandlers"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["browserHandlers"] as object, +) as FacadeModule["browserHandlers"]; +export const createBrowserPluginService: FacadeModule["createBrowserPluginService"] = ((...args) => + loadFacadeModule()["createBrowserPluginService"]( + ...args, + )) as FacadeModule["createBrowserPluginService"]; +export const createBrowserTool: FacadeModule["createBrowserTool"] = ((...args) => + loadFacadeModule()["createBrowserTool"](...args)) as FacadeModule["createBrowserTool"]; +export const handleBrowserGatewayRequest: FacadeModule["handleBrowserGatewayRequest"] = (( + ...args +) => + loadFacadeModule()["handleBrowserGatewayRequest"]( + ...args, + )) as FacadeModule["handleBrowserGatewayRequest"]; +export const registerBrowserCli: FacadeModule["registerBrowserCli"] = ((...args) => + loadFacadeModule()["registerBrowserCli"](...args)) as FacadeModule["registerBrowserCli"]; diff --git a/src/plugin-sdk/byteplus.ts b/src/plugin-sdk/byteplus.ts index d5e51e2dfd0..3d63eb89fbf 100644 --- a/src/plugin-sdk/byteplus.ts +++ b/src/plugin-sdk/byteplus.ts @@ -1,10 +1,41 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildBytePlusCodingProvider, - buildBytePlusModelDefinition, - buildBytePlusProvider, - BYTEPLUS_BASE_URL, - BYTEPLUS_CODING_BASE_URL, - BYTEPLUS_CODING_MODEL_CATALOG, - BYTEPLUS_MODEL_CATALOG, -} from "../../extensions/byteplus/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["byteplus"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "byteplus", + artifactBasename: "api.js", + }); +} +export const buildBytePlusCodingProvider: FacadeModule["buildBytePlusCodingProvider"] = (( + ...args +) => + loadFacadeModule()["buildBytePlusCodingProvider"]( + ...args, + )) as FacadeModule["buildBytePlusCodingProvider"]; +export const buildBytePlusModelDefinition: FacadeModule["buildBytePlusModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildBytePlusModelDefinition"]( + ...args, + )) as FacadeModule["buildBytePlusModelDefinition"]; +export const buildBytePlusProvider: FacadeModule["buildBytePlusProvider"] = ((...args) => + loadFacadeModule()["buildBytePlusProvider"](...args)) as FacadeModule["buildBytePlusProvider"]; +export const BYTEPLUS_BASE_URL: FacadeModule["BYTEPLUS_BASE_URL"] = + loadFacadeModule()["BYTEPLUS_BASE_URL"]; +export const BYTEPLUS_CODING_BASE_URL: FacadeModule["BYTEPLUS_CODING_BASE_URL"] = + loadFacadeModule()["BYTEPLUS_CODING_BASE_URL"]; +export const BYTEPLUS_CODING_MODEL_CATALOG: FacadeModule["BYTEPLUS_CODING_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["BYTEPLUS_CODING_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["BYTEPLUS_CODING_MODEL_CATALOG"]; +export const BYTEPLUS_MODEL_CATALOG: FacadeModule["BYTEPLUS_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["BYTEPLUS_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["BYTEPLUS_MODEL_CATALOG"]; diff --git a/src/plugin-sdk/channel-import-guardrails.test.ts b/src/plugin-sdk/channel-import-guardrails.test.ts index b8c5a540a5a..279eb6b8b15 100644 --- a/src/plugin-sdk/channel-import-guardrails.test.ts +++ b/src/plugin-sdk/channel-import-guardrails.test.ts @@ -2,6 +2,11 @@ import { readdirSync, readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; +import { + BUNDLED_PLUGIN_PATH_PREFIX, + BUNDLED_PLUGIN_ROOT_DIR, + bundledPluginFile, +} from "../../test/helpers/bundled-plugin-paths.js"; import { GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES } from "../plugins/public-artifacts.js"; const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), ".."); @@ -39,42 +44,42 @@ type GuardedSource = { const SAME_CHANNEL_SDK_GUARDS: GuardedSource[] = [ { - path: "extensions/discord/src/shared.ts", + path: bundledPluginFile("discord", "src/shared.ts"), forbiddenPatterns: [/["']openclaw\/plugin-sdk\/discord["']/, /plugin-sdk-internal\/discord/], }, { - path: "extensions/slack/src/shared.ts", + path: bundledPluginFile("slack", "src/shared.ts"), forbiddenPatterns: [/["']openclaw\/plugin-sdk\/slack["']/, /plugin-sdk-internal\/slack/], }, { - path: "extensions/telegram/src/shared.ts", + path: bundledPluginFile("telegram", "src/shared.ts"), forbiddenPatterns: [/["']openclaw\/plugin-sdk\/telegram["']/, /plugin-sdk-internal\/telegram/], }, { - path: "extensions/imessage/src/shared.ts", + path: bundledPluginFile("imessage", "src/shared.ts"), forbiddenPatterns: [/["']openclaw\/plugin-sdk\/imessage["']/, /plugin-sdk-internal\/imessage/], }, { - path: "extensions/whatsapp/src/shared.ts", + path: bundledPluginFile("whatsapp", "src/shared.ts"), forbiddenPatterns: [/["']openclaw\/plugin-sdk\/whatsapp["']/, /plugin-sdk-internal\/whatsapp/], }, { - path: "extensions/signal/src/shared.ts", + path: bundledPluginFile("signal", "src/shared.ts"), forbiddenPatterns: [/["']openclaw\/plugin-sdk\/signal["']/, /plugin-sdk-internal\/signal/], }, { - path: "extensions/signal/src/runtime-api.ts", + path: bundledPluginFile("signal", "src/runtime-api.ts"), forbiddenPatterns: [/["']openclaw\/plugin-sdk\/signal["']/, /plugin-sdk-internal\/signal/], }, ]; const SETUP_BARREL_GUARDS: GuardedSource[] = [ { - path: "extensions/signal/src/setup-core.ts", + path: bundledPluginFile("signal", "src/setup-core.ts"), forbiddenPatterns: [/\bformatCliCommand\b/, /\bformatDocsLink\b/], }, { - path: "extensions/signal/src/setup-surface.ts", + path: bundledPluginFile("signal", "src/setup-surface.ts"), forbiddenPatterns: [ /\bdetectBinary\b/, /\binstallSignalCli\b/, @@ -83,35 +88,35 @@ const SETUP_BARREL_GUARDS: GuardedSource[] = [ ], }, { - path: "extensions/slack/src/setup-core.ts", + path: bundledPluginFile("slack", "src/setup-core.ts"), forbiddenPatterns: [/\bformatDocsLink\b/], }, { - path: "extensions/slack/src/setup-surface.ts", + path: bundledPluginFile("slack", "src/setup-surface.ts"), forbiddenPatterns: [/\bformatDocsLink\b/], }, { - path: "extensions/discord/src/setup-core.ts", + path: bundledPluginFile("discord", "src/setup-core.ts"), forbiddenPatterns: [/\bformatDocsLink\b/], }, { - path: "extensions/discord/src/setup-surface.ts", + path: bundledPluginFile("discord", "src/setup-surface.ts"), forbiddenPatterns: [/\bformatDocsLink\b/], }, { - path: "extensions/imessage/src/setup-core.ts", + path: bundledPluginFile("imessage", "src/setup-core.ts"), forbiddenPatterns: [/\bformatDocsLink\b/], }, { - path: "extensions/imessage/src/setup-surface.ts", + path: bundledPluginFile("imessage", "src/setup-surface.ts"), forbiddenPatterns: [/\bdetectBinary\b/, /\bformatDocsLink\b/], }, { - path: "extensions/telegram/src/setup-core.ts", + path: bundledPluginFile("telegram", "src/setup-core.ts"), forbiddenPatterns: [/\bformatCliCommand\b/, /\bformatDocsLink\b/], }, { - path: "extensions/whatsapp/src/setup-surface.ts", + path: bundledPluginFile("whatsapp", "src/setup-surface.ts"), forbiddenPatterns: [/\bformatCliCommand\b/, /\bformatDocsLink\b/], }, ]; @@ -154,8 +159,8 @@ const LOCAL_EXTENSION_API_BARREL_GUARDS = [ const LOCAL_EXTENSION_API_BARREL_EXCEPTIONS = [ // Direct import avoids a circular init path: - // accounts.ts -> runtime-api.ts -> src/plugin-sdk/matrix -> extensions/matrix/api.ts -> accounts.ts - "extensions/matrix/src/matrix/accounts.ts", + // accounts.ts -> runtime-api.ts -> src/plugin-sdk/matrix -> plugin api barrel -> accounts.ts + bundledPluginFile("matrix", "src/matrix/accounts.ts"), ] as const; const sourceTextCache = new Map(); @@ -344,7 +349,9 @@ function getSourceAnalysis(path: string): SourceAnalysis { const analysis = { text, importSpecifiers, - extensionImports: importSpecifiers.filter((specifier) => specifier.includes("extensions/")), + extensionImports: importSpecifiers.filter((specifier) => + specifier.includes(BUNDLED_PLUGIN_PATH_PREFIX), + ), } satisfies SourceAnalysis; sourceAnalysisCache.set(fullPath, analysis); return analysis; @@ -356,7 +363,8 @@ function expectOnlyApprovedExtensionSeams(file: string, imports: string[]): void const resolved = specifier.startsWith(".") ? resolve(dirname(file), specifier).replaceAll("\\", "/") : normalized; - const extensionId = resolved.match(/extensions\/([^/]+)\//)?.[1] ?? null; + const extensionId = + resolved.match(new RegExp(`${BUNDLED_PLUGIN_ROOT_DIR}/([^/]+)/`))?.[1] ?? null; if (!extensionId || !GUARDED_CHANNEL_EXTENSIONS.has(extensionId)) { continue; } @@ -370,7 +378,8 @@ function expectOnlyApprovedExtensionSeams(file: string, imports: string[]): void function expectNoSiblingExtensionPrivateSrcImports(file: string, imports: string[]): void { const normalizedFile = file.replaceAll("\\", "/"); - const currentExtensionId = normalizedFile.match(/\/extensions\/([^/]+)\//)?.[1] ?? null; + const currentExtensionId = + normalizedFile.match(new RegExp(`/${BUNDLED_PLUGIN_ROOT_DIR}/([^/]+)/`))?.[1] ?? null; if (!currentExtensionId) { return; } @@ -430,10 +439,10 @@ describe("channel import guardrails", () => { } }); - it("keeps core production files off extension private src imports", () => { + it("keeps core production files off plugin-private src imports", () => { for (const file of collectCoreSourceFiles()) { const analysis = getSourceAnalysis(file); - expect(analysis.text, `${file} should not import extensions/*/src`).not.toMatch( + expect(analysis.text, `${file} should not import plugin-private src paths`).not.toMatch( /["'][^"']*extensions\/[^/"']+\/src\//, ); } diff --git a/src/plugin-sdk/channel-setup.test.ts b/src/plugin-sdk/channel-setup.test.ts index 546077fa1f5..4b4e58809a1 100644 --- a/src/plugin-sdk/channel-setup.test.ts +++ b/src/plugin-sdk/channel-setup.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { runSetupWizardFinalize } from "../../test/helpers/extensions/setup-wizard.js"; +import { runSetupWizardFinalize } from "../../test/helpers/plugins/setup-wizard.js"; import { createOptionalChannelSetupSurface } from "./channel-setup.js"; describe("createOptionalChannelSetupSurface", () => { diff --git a/src/plugin-sdk/chutes.ts b/src/plugin-sdk/chutes.ts index 4fc5b158ef4..06feb2cda10 100644 --- a/src/plugin-sdk/chutes.ts +++ b/src/plugin-sdk/chutes.ts @@ -1,13 +1,43 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyChutesApiKeyConfig, - applyChutesConfig, - applyChutesProviderConfig, - buildChutesModelDefinition, - buildChutesProvider, - CHUTES_BASE_URL, - CHUTES_DEFAULT_MODEL_ID, - CHUTES_DEFAULT_MODEL_REF, - CHUTES_MODEL_CATALOG, - discoverChutesModels, -} from "../../extensions/chutes/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["chutes"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "chutes", + artifactBasename: "api.js", + }); +} +export const applyChutesApiKeyConfig: FacadeModule["applyChutesApiKeyConfig"] = ((...args) => + loadFacadeModule()["applyChutesApiKeyConfig"]( + ...args, + )) as FacadeModule["applyChutesApiKeyConfig"]; +export const applyChutesConfig: FacadeModule["applyChutesConfig"] = ((...args) => + loadFacadeModule()["applyChutesConfig"](...args)) as FacadeModule["applyChutesConfig"]; +export const applyChutesProviderConfig: FacadeModule["applyChutesProviderConfig"] = ((...args) => + loadFacadeModule()["applyChutesProviderConfig"]( + ...args, + )) as FacadeModule["applyChutesProviderConfig"]; +export const buildChutesModelDefinition: FacadeModule["buildChutesModelDefinition"] = ((...args) => + loadFacadeModule()["buildChutesModelDefinition"]( + ...args, + )) as FacadeModule["buildChutesModelDefinition"]; +export const buildChutesProvider: FacadeModule["buildChutesProvider"] = ((...args) => + loadFacadeModule()["buildChutesProvider"](...args)) as FacadeModule["buildChutesProvider"]; +export const CHUTES_BASE_URL: FacadeModule["CHUTES_BASE_URL"] = + loadFacadeModule()["CHUTES_BASE_URL"]; +export const CHUTES_DEFAULT_MODEL_ID: FacadeModule["CHUTES_DEFAULT_MODEL_ID"] = + loadFacadeModule()["CHUTES_DEFAULT_MODEL_ID"]; +export const CHUTES_DEFAULT_MODEL_REF: FacadeModule["CHUTES_DEFAULT_MODEL_REF"] = + loadFacadeModule()["CHUTES_DEFAULT_MODEL_REF"]; +export const CHUTES_MODEL_CATALOG: FacadeModule["CHUTES_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["CHUTES_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["CHUTES_MODEL_CATALOG"]; +export const discoverChutesModels: FacadeModule["discoverChutesModels"] = ((...args) => + loadFacadeModule()["discoverChutesModels"](...args)) as FacadeModule["discoverChutesModels"]; diff --git a/src/plugin-sdk/cloudflare-ai-gateway.ts b/src/plugin-sdk/cloudflare-ai-gateway.ts index b65f92b1d1b..9334b3d0ada 100644 --- a/src/plugin-sdk/cloudflare-ai-gateway.ts +++ b/src/plugin-sdk/cloudflare-ai-gateway.ts @@ -1,11 +1,44 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyCloudflareAiGatewayConfig, - applyCloudflareAiGatewayProviderConfig, - buildCloudflareAiGatewayConfigPatch, - buildCloudflareAiGatewayModelDefinition, - CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_ID, - CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF, - CLOUDFLARE_AI_GATEWAY_PROVIDER_ID, - resolveCloudflareAiGatewayBaseUrl, -} from "../../extensions/cloudflare-ai-gateway/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["cloudflare-ai-gateway"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "cloudflare-ai-gateway", + artifactBasename: "api.js", + }); +} +export const applyCloudflareAiGatewayConfig: FacadeModule["applyCloudflareAiGatewayConfig"] = (( + ...args +) => + loadFacadeModule()["applyCloudflareAiGatewayConfig"]( + ...args, + )) as FacadeModule["applyCloudflareAiGatewayConfig"]; +export const applyCloudflareAiGatewayProviderConfig: FacadeModule["applyCloudflareAiGatewayProviderConfig"] = + ((...args) => + loadFacadeModule()["applyCloudflareAiGatewayProviderConfig"]( + ...args, + )) as FacadeModule["applyCloudflareAiGatewayProviderConfig"]; +export const buildCloudflareAiGatewayConfigPatch: FacadeModule["buildCloudflareAiGatewayConfigPatch"] = + ((...args) => + loadFacadeModule()["buildCloudflareAiGatewayConfigPatch"]( + ...args, + )) as FacadeModule["buildCloudflareAiGatewayConfigPatch"]; +export const buildCloudflareAiGatewayModelDefinition: FacadeModule["buildCloudflareAiGatewayModelDefinition"] = + ((...args) => + loadFacadeModule()["buildCloudflareAiGatewayModelDefinition"]( + ...args, + )) as FacadeModule["buildCloudflareAiGatewayModelDefinition"]; +export const CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_ID: FacadeModule["CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_ID"] = + loadFacadeModule()["CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_ID"]; +export const CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF: FacadeModule["CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF"] = + loadFacadeModule()["CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF"]; +export const CLOUDFLARE_AI_GATEWAY_PROVIDER_ID: FacadeModule["CLOUDFLARE_AI_GATEWAY_PROVIDER_ID"] = + loadFacadeModule()["CLOUDFLARE_AI_GATEWAY_PROVIDER_ID"]; +export const resolveCloudflareAiGatewayBaseUrl: FacadeModule["resolveCloudflareAiGatewayBaseUrl"] = + ((...args) => + loadFacadeModule()["resolveCloudflareAiGatewayBaseUrl"]( + ...args, + )) as FacadeModule["resolveCloudflareAiGatewayBaseUrl"]; diff --git a/src/plugin-sdk/copilot-proxy.ts b/src/plugin-sdk/copilot-proxy.ts index 808cad1dbf7..96a15137788 100644 --- a/src/plugin-sdk/copilot-proxy.ts +++ b/src/plugin-sdk/copilot-proxy.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled copilot-proxy plugin. -// Keep this list additive and scoped to symbols used under extensions/copilot-proxy. +// Keep this list additive and scoped to the bundled Copilot proxy surface. export { definePluginEntry } from "./plugin-entry.js"; export type { diff --git a/src/plugin-sdk/deepseek.ts b/src/plugin-sdk/deepseek.ts index 7bf128cc2bf..9a7204362cc 100644 --- a/src/plugin-sdk/deepseek.ts +++ b/src/plugin-sdk/deepseek.ts @@ -1,7 +1,29 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildDeepSeekModelDefinition, - buildDeepSeekProvider, - DEEPSEEK_BASE_URL, - DEEPSEEK_MODEL_CATALOG, -} from "../../extensions/deepseek/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["deepseek"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "deepseek", + artifactBasename: "api.js", + }); +} +export const buildDeepSeekModelDefinition: FacadeModule["buildDeepSeekModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildDeepSeekModelDefinition"]( + ...args, + )) as FacadeModule["buildDeepSeekModelDefinition"]; +export const buildDeepSeekProvider: FacadeModule["buildDeepSeekProvider"] = ((...args) => + loadFacadeModule()["buildDeepSeekProvider"](...args)) as FacadeModule["buildDeepSeekProvider"]; +export const DEEPSEEK_BASE_URL: FacadeModule["DEEPSEEK_BASE_URL"] = + loadFacadeModule()["DEEPSEEK_BASE_URL"]; +export const DEEPSEEK_MODEL_CATALOG: FacadeModule["DEEPSEEK_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["DEEPSEEK_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["DEEPSEEK_MODEL_CATALOG"]; diff --git a/src/plugin-sdk/diagnostics-otel.ts b/src/plugin-sdk/diagnostics-otel.ts index cb5038f4c42..93428b6ee2e 100644 --- a/src/plugin-sdk/diagnostics-otel.ts +++ b/src/plugin-sdk/diagnostics-otel.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled diagnostics-otel plugin. -// Keep this list additive and scoped to symbols used under extensions/diagnostics-otel. +// Keep this list additive and scoped to the bundled diagnostics-otel surface. export type { DiagnosticEventPayload } from "../infra/diagnostic-events.js"; export { emitDiagnosticEvent, onDiagnosticEvent } from "../infra/diagnostic-events.js"; diff --git a/src/plugin-sdk/diffs.ts b/src/plugin-sdk/diffs.ts index 9a7b50bdbaa..24ef1e0b3cb 100644 --- a/src/plugin-sdk/diffs.ts +++ b/src/plugin-sdk/diffs.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled diffs plugin. -// Keep this list additive and scoped to symbols used under extensions/diffs. +// Keep this list additive and scoped to the bundled diffs surface. export { definePluginEntry } from "./plugin-entry.js"; export type { OpenClawConfig } from "../config/config.js"; diff --git a/src/plugin-sdk/discord-account.ts b/src/plugin-sdk/discord-account.ts index 1e591d233af..b770fe59a6a 100644 --- a/src/plugin-sdk/discord-account.ts +++ b/src/plugin-sdk/discord-account.ts @@ -1,3 +1,15 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { resolveDiscordAccount } from "../../extensions/discord/api.js"; -export type { ResolvedDiscordAccount } from "../../extensions/discord/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["discord-account"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "discord", + artifactBasename: "api.js", + }); +} +export const resolveDiscordAccount: FacadeModule["resolveDiscordAccount"] = ((...args) => + loadFacadeModule()["resolveDiscordAccount"](...args)) as FacadeModule["resolveDiscordAccount"]; +export type ResolvedDiscordAccount = FacadeEntry["types"]["ResolvedDiscordAccount"]; diff --git a/src/plugin-sdk/discord-runtime-surface.ts b/src/plugin-sdk/discord-runtime-surface.ts index a0ce0ad88b7..1cbb29c1e71 100644 --- a/src/plugin-sdk/discord-runtime-surface.ts +++ b/src/plugin-sdk/discord-runtime-surface.ts @@ -1,60 +1,201 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - addRoleDiscord, - auditDiscordChannelPermissions, - banMemberDiscord, - collectDiscordAuditChannelIds, - createChannelDiscord, - createScheduledEventDiscord, - createThreadDiscord, - deleteChannelDiscord, - deleteMessageDiscord, - discordMessageActions, - editChannelDiscord, - editDiscordComponentMessage, - editMessageDiscord, - fetchChannelInfoDiscord, - fetchChannelPermissionsDiscord, - fetchMemberInfoDiscord, - fetchMessageDiscord, - fetchReactionsDiscord, - fetchRoleInfoDiscord, - fetchVoiceStatusDiscord, - getGateway, - getPresence, - hasAnyGuildPermissionDiscord, - kickMemberDiscord, - listDiscordDirectoryGroupsLive, - listDiscordDirectoryPeersLive, - listGuildChannelsDiscord, - listGuildEmojisDiscord, - listPinsDiscord, - listScheduledEventsDiscord, - listThreadsDiscord, - monitorDiscordProvider, - moveChannelDiscord, - pinMessageDiscord, - probeDiscord, - reactMessageDiscord, - readMessagesDiscord, - registerBuiltDiscordComponentMessage, - removeChannelPermissionDiscord, - removeOwnReactionsDiscord, - removeReactionDiscord, - removeRoleDiscord, - resolveDiscordChannelAllowlist, - resolveDiscordOutboundSessionRoute, - resolveDiscordUserAllowlist, - searchMessagesDiscord, - sendDiscordComponentMessage, - sendMessageDiscord, - sendPollDiscord, - sendStickerDiscord, - sendTypingDiscord, - sendVoiceMessageDiscord, - setChannelPermissionDiscord, - timeoutMemberDiscord, - unpinMessageDiscord, - uploadEmojiDiscord, - uploadStickerDiscord, -} from "../../extensions/discord/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["discord-runtime-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "discord", + artifactBasename: "runtime-api.js", + }); +} +export const addRoleDiscord: FacadeModule["addRoleDiscord"] = ((...args) => + loadFacadeModule()["addRoleDiscord"](...args)) as FacadeModule["addRoleDiscord"]; +export const auditDiscordChannelPermissions: FacadeModule["auditDiscordChannelPermissions"] = (( + ...args +) => + loadFacadeModule()["auditDiscordChannelPermissions"]( + ...args, + )) as FacadeModule["auditDiscordChannelPermissions"]; +export const banMemberDiscord: FacadeModule["banMemberDiscord"] = ((...args) => + loadFacadeModule()["banMemberDiscord"](...args)) as FacadeModule["banMemberDiscord"]; +export const collectDiscordAuditChannelIds: FacadeModule["collectDiscordAuditChannelIds"] = (( + ...args +) => + loadFacadeModule()["collectDiscordAuditChannelIds"]( + ...args, + )) as FacadeModule["collectDiscordAuditChannelIds"]; +export const createChannelDiscord: FacadeModule["createChannelDiscord"] = ((...args) => + loadFacadeModule()["createChannelDiscord"](...args)) as FacadeModule["createChannelDiscord"]; +export const createScheduledEventDiscord: FacadeModule["createScheduledEventDiscord"] = (( + ...args +) => + loadFacadeModule()["createScheduledEventDiscord"]( + ...args, + )) as FacadeModule["createScheduledEventDiscord"]; +export const createThreadDiscord: FacadeModule["createThreadDiscord"] = ((...args) => + loadFacadeModule()["createThreadDiscord"](...args)) as FacadeModule["createThreadDiscord"]; +export const deleteChannelDiscord: FacadeModule["deleteChannelDiscord"] = ((...args) => + loadFacadeModule()["deleteChannelDiscord"](...args)) as FacadeModule["deleteChannelDiscord"]; +export const deleteMessageDiscord: FacadeModule["deleteMessageDiscord"] = ((...args) => + loadFacadeModule()["deleteMessageDiscord"](...args)) as FacadeModule["deleteMessageDiscord"]; +export const discordMessageActions: FacadeModule["discordMessageActions"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["discordMessageActions"] as object, + ) as FacadeModule["discordMessageActions"]; +export const editChannelDiscord: FacadeModule["editChannelDiscord"] = ((...args) => + loadFacadeModule()["editChannelDiscord"](...args)) as FacadeModule["editChannelDiscord"]; +export const editDiscordComponentMessage: FacadeModule["editDiscordComponentMessage"] = (( + ...args +) => + loadFacadeModule()["editDiscordComponentMessage"]( + ...args, + )) as FacadeModule["editDiscordComponentMessage"]; +export const editMessageDiscord: FacadeModule["editMessageDiscord"] = ((...args) => + loadFacadeModule()["editMessageDiscord"](...args)) as FacadeModule["editMessageDiscord"]; +export const fetchChannelInfoDiscord: FacadeModule["fetchChannelInfoDiscord"] = ((...args) => + loadFacadeModule()["fetchChannelInfoDiscord"]( + ...args, + )) as FacadeModule["fetchChannelInfoDiscord"]; +export const fetchChannelPermissionsDiscord: FacadeModule["fetchChannelPermissionsDiscord"] = (( + ...args +) => + loadFacadeModule()["fetchChannelPermissionsDiscord"]( + ...args, + )) as FacadeModule["fetchChannelPermissionsDiscord"]; +export const fetchMemberInfoDiscord: FacadeModule["fetchMemberInfoDiscord"] = ((...args) => + loadFacadeModule()["fetchMemberInfoDiscord"](...args)) as FacadeModule["fetchMemberInfoDiscord"]; +export const fetchMessageDiscord: FacadeModule["fetchMessageDiscord"] = ((...args) => + loadFacadeModule()["fetchMessageDiscord"](...args)) as FacadeModule["fetchMessageDiscord"]; +export const fetchReactionsDiscord: FacadeModule["fetchReactionsDiscord"] = ((...args) => + loadFacadeModule()["fetchReactionsDiscord"](...args)) as FacadeModule["fetchReactionsDiscord"]; +export const fetchRoleInfoDiscord: FacadeModule["fetchRoleInfoDiscord"] = ((...args) => + loadFacadeModule()["fetchRoleInfoDiscord"](...args)) as FacadeModule["fetchRoleInfoDiscord"]; +export const fetchVoiceStatusDiscord: FacadeModule["fetchVoiceStatusDiscord"] = ((...args) => + loadFacadeModule()["fetchVoiceStatusDiscord"]( + ...args, + )) as FacadeModule["fetchVoiceStatusDiscord"]; +export const getGateway: FacadeModule["getGateway"] = ((...args) => + loadFacadeModule()["getGateway"](...args)) as FacadeModule["getGateway"]; +export const getPresence: FacadeModule["getPresence"] = ((...args) => + loadFacadeModule()["getPresence"](...args)) as FacadeModule["getPresence"]; +export const hasAnyGuildPermissionDiscord: FacadeModule["hasAnyGuildPermissionDiscord"] = (( + ...args +) => + loadFacadeModule()["hasAnyGuildPermissionDiscord"]( + ...args, + )) as FacadeModule["hasAnyGuildPermissionDiscord"]; +export const kickMemberDiscord: FacadeModule["kickMemberDiscord"] = ((...args) => + loadFacadeModule()["kickMemberDiscord"](...args)) as FacadeModule["kickMemberDiscord"]; +export const listDiscordDirectoryGroupsLive: FacadeModule["listDiscordDirectoryGroupsLive"] = (( + ...args +) => + loadFacadeModule()["listDiscordDirectoryGroupsLive"]( + ...args, + )) as FacadeModule["listDiscordDirectoryGroupsLive"]; +export const listDiscordDirectoryPeersLive: FacadeModule["listDiscordDirectoryPeersLive"] = (( + ...args +) => + loadFacadeModule()["listDiscordDirectoryPeersLive"]( + ...args, + )) as FacadeModule["listDiscordDirectoryPeersLive"]; +export const listGuildChannelsDiscord: FacadeModule["listGuildChannelsDiscord"] = ((...args) => + loadFacadeModule()["listGuildChannelsDiscord"]( + ...args, + )) as FacadeModule["listGuildChannelsDiscord"]; +export const listGuildEmojisDiscord: FacadeModule["listGuildEmojisDiscord"] = ((...args) => + loadFacadeModule()["listGuildEmojisDiscord"](...args)) as FacadeModule["listGuildEmojisDiscord"]; +export const listPinsDiscord: FacadeModule["listPinsDiscord"] = ((...args) => + loadFacadeModule()["listPinsDiscord"](...args)) as FacadeModule["listPinsDiscord"]; +export const listScheduledEventsDiscord: FacadeModule["listScheduledEventsDiscord"] = ((...args) => + loadFacadeModule()["listScheduledEventsDiscord"]( + ...args, + )) as FacadeModule["listScheduledEventsDiscord"]; +export const listThreadsDiscord: FacadeModule["listThreadsDiscord"] = ((...args) => + loadFacadeModule()["listThreadsDiscord"](...args)) as FacadeModule["listThreadsDiscord"]; +export const monitorDiscordProvider: FacadeModule["monitorDiscordProvider"] = ((...args) => + loadFacadeModule()["monitorDiscordProvider"](...args)) as FacadeModule["monitorDiscordProvider"]; +export const moveChannelDiscord: FacadeModule["moveChannelDiscord"] = ((...args) => + loadFacadeModule()["moveChannelDiscord"](...args)) as FacadeModule["moveChannelDiscord"]; +export const pinMessageDiscord: FacadeModule["pinMessageDiscord"] = ((...args) => + loadFacadeModule()["pinMessageDiscord"](...args)) as FacadeModule["pinMessageDiscord"]; +export const probeDiscord: FacadeModule["probeDiscord"] = ((...args) => + loadFacadeModule()["probeDiscord"](...args)) as FacadeModule["probeDiscord"]; +export const reactMessageDiscord: FacadeModule["reactMessageDiscord"] = ((...args) => + loadFacadeModule()["reactMessageDiscord"](...args)) as FacadeModule["reactMessageDiscord"]; +export const readMessagesDiscord: FacadeModule["readMessagesDiscord"] = ((...args) => + loadFacadeModule()["readMessagesDiscord"](...args)) as FacadeModule["readMessagesDiscord"]; +export const registerBuiltDiscordComponentMessage: FacadeModule["registerBuiltDiscordComponentMessage"] = + ((...args) => + loadFacadeModule()["registerBuiltDiscordComponentMessage"]( + ...args, + )) as FacadeModule["registerBuiltDiscordComponentMessage"]; +export const removeChannelPermissionDiscord: FacadeModule["removeChannelPermissionDiscord"] = (( + ...args +) => + loadFacadeModule()["removeChannelPermissionDiscord"]( + ...args, + )) as FacadeModule["removeChannelPermissionDiscord"]; +export const removeOwnReactionsDiscord: FacadeModule["removeOwnReactionsDiscord"] = ((...args) => + loadFacadeModule()["removeOwnReactionsDiscord"]( + ...args, + )) as FacadeModule["removeOwnReactionsDiscord"]; +export const removeReactionDiscord: FacadeModule["removeReactionDiscord"] = ((...args) => + loadFacadeModule()["removeReactionDiscord"](...args)) as FacadeModule["removeReactionDiscord"]; +export const removeRoleDiscord: FacadeModule["removeRoleDiscord"] = ((...args) => + loadFacadeModule()["removeRoleDiscord"](...args)) as FacadeModule["removeRoleDiscord"]; +export const resolveDiscordChannelAllowlist: FacadeModule["resolveDiscordChannelAllowlist"] = (( + ...args +) => + loadFacadeModule()["resolveDiscordChannelAllowlist"]( + ...args, + )) as FacadeModule["resolveDiscordChannelAllowlist"]; +export const resolveDiscordOutboundSessionRoute: FacadeModule["resolveDiscordOutboundSessionRoute"] = + ((...args) => + loadFacadeModule()["resolveDiscordOutboundSessionRoute"]( + ...args, + )) as FacadeModule["resolveDiscordOutboundSessionRoute"]; +export const resolveDiscordUserAllowlist: FacadeModule["resolveDiscordUserAllowlist"] = (( + ...args +) => + loadFacadeModule()["resolveDiscordUserAllowlist"]( + ...args, + )) as FacadeModule["resolveDiscordUserAllowlist"]; +export const searchMessagesDiscord: FacadeModule["searchMessagesDiscord"] = ((...args) => + loadFacadeModule()["searchMessagesDiscord"](...args)) as FacadeModule["searchMessagesDiscord"]; +export const sendDiscordComponentMessage: FacadeModule["sendDiscordComponentMessage"] = (( + ...args +) => + loadFacadeModule()["sendDiscordComponentMessage"]( + ...args, + )) as FacadeModule["sendDiscordComponentMessage"]; +export const sendMessageDiscord: FacadeModule["sendMessageDiscord"] = ((...args) => + loadFacadeModule()["sendMessageDiscord"](...args)) as FacadeModule["sendMessageDiscord"]; +export const sendPollDiscord: FacadeModule["sendPollDiscord"] = ((...args) => + loadFacadeModule()["sendPollDiscord"](...args)) as FacadeModule["sendPollDiscord"]; +export const sendStickerDiscord: FacadeModule["sendStickerDiscord"] = ((...args) => + loadFacadeModule()["sendStickerDiscord"](...args)) as FacadeModule["sendStickerDiscord"]; +export const sendTypingDiscord: FacadeModule["sendTypingDiscord"] = ((...args) => + loadFacadeModule()["sendTypingDiscord"](...args)) as FacadeModule["sendTypingDiscord"]; +export const sendVoiceMessageDiscord: FacadeModule["sendVoiceMessageDiscord"] = ((...args) => + loadFacadeModule()["sendVoiceMessageDiscord"]( + ...args, + )) as FacadeModule["sendVoiceMessageDiscord"]; +export const setChannelPermissionDiscord: FacadeModule["setChannelPermissionDiscord"] = (( + ...args +) => + loadFacadeModule()["setChannelPermissionDiscord"]( + ...args, + )) as FacadeModule["setChannelPermissionDiscord"]; +export const timeoutMemberDiscord: FacadeModule["timeoutMemberDiscord"] = ((...args) => + loadFacadeModule()["timeoutMemberDiscord"](...args)) as FacadeModule["timeoutMemberDiscord"]; +export const unpinMessageDiscord: FacadeModule["unpinMessageDiscord"] = ((...args) => + loadFacadeModule()["unpinMessageDiscord"](...args)) as FacadeModule["unpinMessageDiscord"]; +export const uploadEmojiDiscord: FacadeModule["uploadEmojiDiscord"] = ((...args) => + loadFacadeModule()["uploadEmojiDiscord"](...args)) as FacadeModule["uploadEmojiDiscord"]; +export const uploadStickerDiscord: FacadeModule["uploadStickerDiscord"] = ((...args) => + loadFacadeModule()["uploadStickerDiscord"](...args)) as FacadeModule["uploadStickerDiscord"]; diff --git a/src/plugin-sdk/discord-session-key.ts b/src/plugin-sdk/discord-session-key.ts index 6253e0dde61..19129bdc4a3 100644 --- a/src/plugin-sdk/discord-session-key.ts +++ b/src/plugin-sdk/discord-session-key.ts @@ -1,2 +1,17 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { normalizeExplicitDiscordSessionKey } from "../../extensions/discord/session-key-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["discord-session-key"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "discord", + artifactBasename: "session-key-api.js", + }); +} +export const normalizeExplicitDiscordSessionKey: FacadeModule["normalizeExplicitDiscordSessionKey"] = + ((...args) => + loadFacadeModule()["normalizeExplicitDiscordSessionKey"]( + ...args, + )) as FacadeModule["normalizeExplicitDiscordSessionKey"]; diff --git a/src/plugin-sdk/discord-surface.ts b/src/plugin-sdk/discord-surface.ts index a386400c1f8..6a63842ca71 100644 --- a/src/plugin-sdk/discord-surface.ts +++ b/src/plugin-sdk/discord-surface.ts @@ -1,33 +1,112 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildDiscordComponentMessage, - collectDiscordStatusIssues, - createDiscordActionGate, - handleDiscordMessageAction, - inspectDiscordAccount, - isDiscordExecApprovalApprover, - isDiscordExecApprovalClientEnabled, - listDiscordAccountIds, - listDiscordDirectoryGroupsFromConfig, - listDiscordDirectoryPeersFromConfig, - looksLikeDiscordTargetId, - normalizeDiscordMessagingTarget, - normalizeDiscordOutboundTarget, - readDiscordComponentSpec, - resolveDefaultDiscordAccountId, - resolveDiscordAccount, - resolveDiscordChannelId, - resolveDiscordRuntimeGroupPolicy, - resolveDiscordGroupRequireMention, - resolveDiscordGroupToolPolicy, -} from "../../extensions/discord/api.js"; -export type { - DiscordComponentMessageSpec, - DiscordProbe, - DiscordSendComponents, - DiscordSendEmbeds, - DiscordSendResult, - DiscordTokenResolution, - InspectedDiscordAccount, - ResolvedDiscordAccount, -} from "../../extensions/discord/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["discord-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "discord", + artifactBasename: "api.js", + }); +} +export const buildDiscordComponentMessage: FacadeModule["buildDiscordComponentMessage"] = (( + ...args +) => + loadFacadeModule()["buildDiscordComponentMessage"]( + ...args, + )) as FacadeModule["buildDiscordComponentMessage"]; +export const collectDiscordStatusIssues: FacadeModule["collectDiscordStatusIssues"] = ((...args) => + loadFacadeModule()["collectDiscordStatusIssues"]( + ...args, + )) as FacadeModule["collectDiscordStatusIssues"]; +export const createDiscordActionGate: FacadeModule["createDiscordActionGate"] = ((...args) => + loadFacadeModule()["createDiscordActionGate"]( + ...args, + )) as FacadeModule["createDiscordActionGate"]; +export const handleDiscordMessageAction: FacadeModule["handleDiscordMessageAction"] = ((...args) => + loadFacadeModule()["handleDiscordMessageAction"]( + ...args, + )) as FacadeModule["handleDiscordMessageAction"]; +export const inspectDiscordAccount: FacadeModule["inspectDiscordAccount"] = ((...args) => + loadFacadeModule()["inspectDiscordAccount"](...args)) as FacadeModule["inspectDiscordAccount"]; +export const isDiscordExecApprovalApprover: FacadeModule["isDiscordExecApprovalApprover"] = (( + ...args +) => + loadFacadeModule()["isDiscordExecApprovalApprover"]( + ...args, + )) as FacadeModule["isDiscordExecApprovalApprover"]; +export const isDiscordExecApprovalClientEnabled: FacadeModule["isDiscordExecApprovalClientEnabled"] = + ((...args) => + loadFacadeModule()["isDiscordExecApprovalClientEnabled"]( + ...args, + )) as FacadeModule["isDiscordExecApprovalClientEnabled"]; +export const listDiscordAccountIds: FacadeModule["listDiscordAccountIds"] = ((...args) => + loadFacadeModule()["listDiscordAccountIds"](...args)) as FacadeModule["listDiscordAccountIds"]; +export const listDiscordDirectoryGroupsFromConfig: FacadeModule["listDiscordDirectoryGroupsFromConfig"] = + ((...args) => + loadFacadeModule()["listDiscordDirectoryGroupsFromConfig"]( + ...args, + )) as FacadeModule["listDiscordDirectoryGroupsFromConfig"]; +export const listDiscordDirectoryPeersFromConfig: FacadeModule["listDiscordDirectoryPeersFromConfig"] = + ((...args) => + loadFacadeModule()["listDiscordDirectoryPeersFromConfig"]( + ...args, + )) as FacadeModule["listDiscordDirectoryPeersFromConfig"]; +export const looksLikeDiscordTargetId: FacadeModule["looksLikeDiscordTargetId"] = ((...args) => + loadFacadeModule()["looksLikeDiscordTargetId"]( + ...args, + )) as FacadeModule["looksLikeDiscordTargetId"]; +export const normalizeDiscordMessagingTarget: FacadeModule["normalizeDiscordMessagingTarget"] = (( + ...args +) => + loadFacadeModule()["normalizeDiscordMessagingTarget"]( + ...args, + )) as FacadeModule["normalizeDiscordMessagingTarget"]; +export const normalizeDiscordOutboundTarget: FacadeModule["normalizeDiscordOutboundTarget"] = (( + ...args +) => + loadFacadeModule()["normalizeDiscordOutboundTarget"]( + ...args, + )) as FacadeModule["normalizeDiscordOutboundTarget"]; +export const readDiscordComponentSpec: FacadeModule["readDiscordComponentSpec"] = ((...args) => + loadFacadeModule()["readDiscordComponentSpec"]( + ...args, + )) as FacadeModule["readDiscordComponentSpec"]; +export const resolveDefaultDiscordAccountId: FacadeModule["resolveDefaultDiscordAccountId"] = (( + ...args +) => + loadFacadeModule()["resolveDefaultDiscordAccountId"]( + ...args, + )) as FacadeModule["resolveDefaultDiscordAccountId"]; +export const resolveDiscordAccount: FacadeModule["resolveDiscordAccount"] = ((...args) => + loadFacadeModule()["resolveDiscordAccount"](...args)) as FacadeModule["resolveDiscordAccount"]; +export const resolveDiscordChannelId: FacadeModule["resolveDiscordChannelId"] = ((...args) => + loadFacadeModule()["resolveDiscordChannelId"]( + ...args, + )) as FacadeModule["resolveDiscordChannelId"]; +export const resolveDiscordRuntimeGroupPolicy: FacadeModule["resolveDiscordRuntimeGroupPolicy"] = (( + ...args +) => + loadFacadeModule()["resolveDiscordRuntimeGroupPolicy"]( + ...args, + )) as FacadeModule["resolveDiscordRuntimeGroupPolicy"]; +export const resolveDiscordGroupRequireMention: FacadeModule["resolveDiscordGroupRequireMention"] = + ((...args) => + loadFacadeModule()["resolveDiscordGroupRequireMention"]( + ...args, + )) as FacadeModule["resolveDiscordGroupRequireMention"]; +export const resolveDiscordGroupToolPolicy: FacadeModule["resolveDiscordGroupToolPolicy"] = (( + ...args +) => + loadFacadeModule()["resolveDiscordGroupToolPolicy"]( + ...args, + )) as FacadeModule["resolveDiscordGroupToolPolicy"]; +export type DiscordComponentMessageSpec = FacadeEntry["types"]["DiscordComponentMessageSpec"]; +export type DiscordProbe = FacadeEntry["types"]["DiscordProbe"]; +export type DiscordSendComponents = FacadeEntry["types"]["DiscordSendComponents"]; +export type DiscordSendEmbeds = FacadeEntry["types"]["DiscordSendEmbeds"]; +export type DiscordSendResult = FacadeEntry["types"]["DiscordSendResult"]; +export type DiscordTokenResolution = FacadeEntry["types"]["DiscordTokenResolution"]; +export type InspectedDiscordAccount = FacadeEntry["types"]["InspectedDiscordAccount"]; +export type ResolvedDiscordAccount = FacadeEntry["types"]["ResolvedDiscordAccount"]; diff --git a/src/plugin-sdk/discord-thread-bindings.ts b/src/plugin-sdk/discord-thread-bindings.ts index 59018589fd8..032cc7bd2d5 100644 --- a/src/plugin-sdk/discord-thread-bindings.ts +++ b/src/plugin-sdk/discord-thread-bindings.ts @@ -1,20 +1,72 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - __testing, - autoBindSpawnedDiscordSubagent, - createThreadBindingManager, - getThreadBindingManager, - listThreadBindingsBySessionKey, - resolveThreadBindingIdleTimeoutMs, - resolveThreadBindingInactivityExpiresAt, - resolveThreadBindingMaxAgeExpiresAt, - resolveThreadBindingMaxAgeMs, - setThreadBindingIdleTimeoutBySessionKey, - setThreadBindingMaxAgeBySessionKey, - unbindThreadBindingsBySessionKey, -} from "../../extensions/discord/runtime-api.js"; -export type { - ThreadBindingManager, - ThreadBindingRecord, - ThreadBindingTargetKind, -} from "../../extensions/discord/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["discord-thread-bindings"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "discord", + artifactBasename: "runtime-api.js", + }); +} +export const autoBindSpawnedDiscordSubagent: FacadeModule["autoBindSpawnedDiscordSubagent"] = (( + ...args +) => + loadFacadeModule()["autoBindSpawnedDiscordSubagent"]( + ...args, + )) as FacadeModule["autoBindSpawnedDiscordSubagent"]; +export const createThreadBindingManager: FacadeModule["createThreadBindingManager"] = ((...args) => + loadFacadeModule()["createThreadBindingManager"]( + ...args, + )) as FacadeModule["createThreadBindingManager"]; +export const getThreadBindingManager: FacadeModule["getThreadBindingManager"] = ((...args) => + loadFacadeModule()["getThreadBindingManager"]( + ...args, + )) as FacadeModule["getThreadBindingManager"]; +export const listThreadBindingsBySessionKey: FacadeModule["listThreadBindingsBySessionKey"] = (( + ...args +) => + loadFacadeModule()["listThreadBindingsBySessionKey"]( + ...args, + )) as FacadeModule["listThreadBindingsBySessionKey"]; +export const resolveThreadBindingIdleTimeoutMs: FacadeModule["resolveThreadBindingIdleTimeoutMs"] = + ((...args) => + loadFacadeModule()["resolveThreadBindingIdleTimeoutMs"]( + ...args, + )) as FacadeModule["resolveThreadBindingIdleTimeoutMs"]; +export const resolveThreadBindingInactivityExpiresAt: FacadeModule["resolveThreadBindingInactivityExpiresAt"] = + ((...args) => + loadFacadeModule()["resolveThreadBindingInactivityExpiresAt"]( + ...args, + )) as FacadeModule["resolveThreadBindingInactivityExpiresAt"]; +export const resolveThreadBindingMaxAgeExpiresAt: FacadeModule["resolveThreadBindingMaxAgeExpiresAt"] = + ((...args) => + loadFacadeModule()["resolveThreadBindingMaxAgeExpiresAt"]( + ...args, + )) as FacadeModule["resolveThreadBindingMaxAgeExpiresAt"]; +export const resolveThreadBindingMaxAgeMs: FacadeModule["resolveThreadBindingMaxAgeMs"] = (( + ...args +) => + loadFacadeModule()["resolveThreadBindingMaxAgeMs"]( + ...args, + )) as FacadeModule["resolveThreadBindingMaxAgeMs"]; +export const setThreadBindingIdleTimeoutBySessionKey: FacadeModule["setThreadBindingIdleTimeoutBySessionKey"] = + ((...args) => + loadFacadeModule()["setThreadBindingIdleTimeoutBySessionKey"]( + ...args, + )) as FacadeModule["setThreadBindingIdleTimeoutBySessionKey"]; +export const setThreadBindingMaxAgeBySessionKey: FacadeModule["setThreadBindingMaxAgeBySessionKey"] = + ((...args) => + loadFacadeModule()["setThreadBindingMaxAgeBySessionKey"]( + ...args, + )) as FacadeModule["setThreadBindingMaxAgeBySessionKey"]; +export const unbindThreadBindingsBySessionKey: FacadeModule["unbindThreadBindingsBySessionKey"] = (( + ...args +) => + loadFacadeModule()["unbindThreadBindingsBySessionKey"]( + ...args, + )) as FacadeModule["unbindThreadBindingsBySessionKey"]; +export type ThreadBindingManager = FacadeEntry["types"]["ThreadBindingManager"]; +export type ThreadBindingRecord = FacadeEntry["types"]["ThreadBindingRecord"]; +export type ThreadBindingTargetKind = FacadeEntry["types"]["ThreadBindingTargetKind"]; diff --git a/src/plugin-sdk/discord-timeouts.ts b/src/plugin-sdk/discord-timeouts.ts index 529b8dadb26..660f9c5350e 100644 --- a/src/plugin-sdk/discord-timeouts.ts +++ b/src/plugin-sdk/discord-timeouts.ts @@ -1,5 +1,16 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - DISCORD_DEFAULT_INBOUND_WORKER_TIMEOUT_MS, - DISCORD_DEFAULT_LISTENER_TIMEOUT_MS, -} from "../../extensions/discord/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["discord-timeouts"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "discord", + artifactBasename: "timeouts.js", + }); +} +export const DISCORD_DEFAULT_INBOUND_WORKER_TIMEOUT_MS: FacadeModule["DISCORD_DEFAULT_INBOUND_WORKER_TIMEOUT_MS"] = + loadFacadeModule()["DISCORD_DEFAULT_INBOUND_WORKER_TIMEOUT_MS"]; +export const DISCORD_DEFAULT_LISTENER_TIMEOUT_MS: FacadeModule["DISCORD_DEFAULT_LISTENER_TIMEOUT_MS"] = + loadFacadeModule()["DISCORD_DEFAULT_LISTENER_TIMEOUT_MS"]; diff --git a/src/plugin-sdk/discord.ts b/src/plugin-sdk/discord.ts index ee8c2ed4782..f0abd125f92 100644 --- a/src/plugin-sdk/discord.ts +++ b/src/plugin-sdk/discord.ts @@ -101,10 +101,7 @@ export { setThreadBindingMaxAgeBySessionKey, unbindThreadBindingsBySessionKey, } from "./discord-thread-bindings.js"; -export { - __testing as discordThreadBindingTesting, - createThreadBindingManager as createDiscordThreadBindingManager, -} from "./discord-thread-bindings.js"; +export { createThreadBindingManager as createDiscordThreadBindingManager } from "./discord-thread-bindings.js"; export { getGateway } from "./discord-runtime-surface.js"; export { getPresence } from "./discord-runtime-surface.js"; export { readDiscordComponentSpec } from "./discord-surface.js"; diff --git a/src/plugin-sdk/facade-runtime.test.ts b/src/plugin-sdk/facade-runtime.test.ts new file mode 100644 index 00000000000..d4350aadc06 --- /dev/null +++ b/src/plugin-sdk/facade-runtime.test.ts @@ -0,0 +1,53 @@ +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +const tempDirs: string[] = []; +const originalBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR; + +function createBundledPluginDir(prefix: string, marker: string): string { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), prefix)); + tempDirs.push(rootDir); + fs.mkdirSync(path.join(rootDir, "demo"), { recursive: true }); + fs.writeFileSync( + path.join(rootDir, "demo", "api.js"), + `export const marker = ${JSON.stringify(marker)};\n`, + "utf8", + ); + return rootDir; +} + +afterEach(() => { + vi.restoreAllMocks(); + if (originalBundledPluginsDir === undefined) { + delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR; + } else { + process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = originalBundledPluginsDir; + } + for (const dir of tempDirs.splice(0, tempDirs.length)) { + fs.rmSync(dir, { recursive: true, force: true }); + } +}); + +describe("plugin-sdk facade runtime", () => { + it("honors bundled plugin dir overrides outside the package root", () => { + const overrideA = createBundledPluginDir("openclaw-facade-runtime-a-", "override-a"); + const overrideB = createBundledPluginDir("openclaw-facade-runtime-b-", "override-b"); + + process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = overrideA; + const fromA = loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({ + dirName: "demo", + artifactBasename: "api.js", + }); + expect(fromA.marker).toBe("override-a"); + + process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = overrideB; + const fromB = loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({ + dirName: "demo", + artifactBasename: "api.js", + }); + expect(fromB.marker).toBe("override-b"); + }); +}); diff --git a/src/plugin-sdk/facade-runtime.ts b/src/plugin-sdk/facade-runtime.ts new file mode 100644 index 00000000000..3425eec1408 --- /dev/null +++ b/src/plugin-sdk/facade-runtime.ts @@ -0,0 +1,206 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { createJiti } from "jiti"; +import { openBoundaryFileSync } from "../infra/boundary-file-read.js"; +import { resolveBundledPluginsDir } from "../plugins/bundled-dir.js"; +import { resolveBundledPluginPublicSurfacePath } from "../plugins/bundled-plugin-metadata.js"; +import { + buildPluginLoaderAliasMap, + buildPluginLoaderJitiOptions, + resolveLoaderPackageRoot, + shouldPreferNativeJiti, +} from "../plugins/sdk-alias.js"; + +const OPENCLAW_PACKAGE_ROOT = + resolveLoaderPackageRoot({ + modulePath: fileURLToPath(import.meta.url), + moduleUrl: import.meta.url, + }) ?? fileURLToPath(new URL("../..", import.meta.url)); +const CURRENT_MODULE_PATH = fileURLToPath(import.meta.url); +const PUBLIC_SURFACE_SOURCE_EXTENSIONS = [".ts", ".mts", ".js", ".mjs", ".cts", ".cjs"] as const; +const jitiLoaders = new Map>(); +const loadedFacadeModules = new Map(); + +function resolveSourceFirstPublicSurfacePath(params: { + bundledPluginsDir?: string; + dirName: string; + artifactBasename: string; +}): string | null { + const sourceBaseName = params.artifactBasename.replace(/\.js$/u, ""); + const sourceRoot = params.bundledPluginsDir ?? path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions"); + for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) { + const candidate = path.resolve(sourceRoot, params.dirName, `${sourceBaseName}${ext}`); + if (fs.existsSync(candidate)) { + return candidate; + } + } + return null; +} + +function resolveFacadeModuleLocation(params: { + dirName: string; + artifactBasename: string; +}): { modulePath: string; boundaryRoot: string } | null { + const bundledPluginsDir = resolveBundledPluginsDir(); + const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`); + if (preferSource) { + const modulePath = + resolveSourceFirstPublicSurfacePath({ + ...params, + ...(bundledPluginsDir ? { bundledPluginsDir } : {}), + }) ?? + resolveSourceFirstPublicSurfacePath(params) ?? + resolveBundledPluginPublicSurfacePath({ + rootDir: OPENCLAW_PACKAGE_ROOT, + ...(bundledPluginsDir ? { bundledPluginsDir } : {}), + dirName: params.dirName, + artifactBasename: params.artifactBasename, + }); + if (!modulePath) { + return null; + } + return { + modulePath, + boundaryRoot: + bundledPluginsDir && modulePath.startsWith(path.resolve(bundledPluginsDir) + path.sep) + ? path.resolve(bundledPluginsDir) + : OPENCLAW_PACKAGE_ROOT, + }; + } + const modulePath = resolveBundledPluginPublicSurfacePath({ + rootDir: OPENCLAW_PACKAGE_ROOT, + ...(bundledPluginsDir ? { bundledPluginsDir } : {}), + dirName: params.dirName, + artifactBasename: params.artifactBasename, + }); + if (!modulePath) { + return null; + } + return { + modulePath, + boundaryRoot: + bundledPluginsDir && modulePath.startsWith(path.resolve(bundledPluginsDir) + path.sep) + ? path.resolve(bundledPluginsDir) + : OPENCLAW_PACKAGE_ROOT, + }; +} + +function getJiti(modulePath: string) { + const tryNative = + shouldPreferNativeJiti(modulePath) || modulePath.includes(`${path.sep}dist${path.sep}`); + const aliasMap = buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url); + const cacheKey = JSON.stringify({ + tryNative, + aliasMap: Object.entries(aliasMap).toSorted(([left], [right]) => left.localeCompare(right)), + }); + const cached = jitiLoaders.get(cacheKey); + if (cached) { + return cached; + } + const loader = createJiti(import.meta.url, { + ...buildPluginLoaderJitiOptions(aliasMap), + tryNative, + }); + jitiLoaders.set(cacheKey, loader); + return loader; +} + +function createLazyFacadeValueLoader(load: () => T): () => T { + let loaded = false; + let value: T; + return () => { + if (!loaded) { + value = load(); + loaded = true; + } + return value; + }; +} + +function createLazyFacadeProxyValue(params: { + load: () => T; + target: object; +}): T { + const resolve = createLazyFacadeValueLoader(params.load); + return new Proxy(params.target, { + defineProperty(_target, property, descriptor) { + return Reflect.defineProperty(resolve(), property, descriptor); + }, + deleteProperty(_target, property) { + return Reflect.deleteProperty(resolve(), property); + }, + get(_target, property, receiver) { + return Reflect.get(resolve(), property, receiver); + }, + getOwnPropertyDescriptor(_target, property) { + return Reflect.getOwnPropertyDescriptor(resolve(), property); + }, + getPrototypeOf() { + return Reflect.getPrototypeOf(resolve()); + }, + has(_target, property) { + return Reflect.has(resolve(), property); + }, + isExtensible() { + return Reflect.isExtensible(resolve()); + }, + ownKeys() { + return Reflect.ownKeys(resolve()); + }, + preventExtensions() { + return Reflect.preventExtensions(resolve()); + }, + set(_target, property, value, receiver) { + return Reflect.set(resolve(), property, value, receiver); + }, + setPrototypeOf(_target, prototype) { + return Reflect.setPrototypeOf(resolve(), prototype); + }, + }) as T; +} + +export function createLazyFacadeObjectValue(load: () => T): T { + return createLazyFacadeProxyValue({ load, target: {} }); +} + +export function createLazyFacadeArrayValue(load: () => T): T { + return createLazyFacadeProxyValue({ load, target: [] }); +} + +export function loadBundledPluginPublicSurfaceModuleSync(params: { + dirName: string; + artifactBasename: string; +}): T { + const location = resolveFacadeModuleLocation(params); + if (!location) { + throw new Error( + `Unable to resolve bundled plugin public surface ${params.dirName}/${params.artifactBasename}`, + ); + } + const cached = loadedFacadeModules.get(location.modulePath); + if (cached) { + return cached as T; + } + + const opened = openBoundaryFileSync({ + absolutePath: location.modulePath, + rootPath: location.boundaryRoot, + boundaryLabel: + location.boundaryRoot === OPENCLAW_PACKAGE_ROOT + ? "OpenClaw package root" + : "bundled plugin directory", + rejectHardlinks: false, + }); + if (!opened.ok) { + throw new Error( + `Unable to open bundled plugin public surface ${params.dirName}/${params.artifactBasename}`, + { cause: opened.error }, + ); + } + fs.closeSync(opened.fd); + + const loaded = getJiti(location.modulePath)(location.modulePath) as T; + loadedFacadeModules.set(location.modulePath, loaded); + return loaded; +} diff --git a/src/plugin-sdk/feishu-conversation.ts b/src/plugin-sdk/feishu-conversation.ts index cd046ce3477..78e19ee6da7 100644 --- a/src/plugin-sdk/feishu-conversation.ts +++ b/src/plugin-sdk/feishu-conversation.ts @@ -1,10 +1,47 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildFeishuConversationId, - createFeishuThreadBindingManager, - feishuSessionBindingAdapterChannels, - feishuThreadBindingTesting, - parseFeishuDirectConversationId, - parseFeishuConversationId, - parseFeishuTargetId, -} from "../../extensions/feishu/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["feishu-conversation"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "feishu", + artifactBasename: "api.js", + }); +} +export const buildFeishuConversationId: FacadeModule["buildFeishuConversationId"] = ((...args) => + loadFacadeModule()["buildFeishuConversationId"]( + ...args, + )) as FacadeModule["buildFeishuConversationId"]; +export const createFeishuThreadBindingManager: FacadeModule["createFeishuThreadBindingManager"] = (( + ...args +) => + loadFacadeModule()["createFeishuThreadBindingManager"]( + ...args, + )) as FacadeModule["createFeishuThreadBindingManager"]; +export const feishuSessionBindingAdapterChannels: FacadeModule["feishuSessionBindingAdapterChannels"] = + createLazyFacadeArrayValue( + () => + loadFacadeModule()["feishuSessionBindingAdapterChannels"] as unknown as readonly unknown[], + ) as FacadeModule["feishuSessionBindingAdapterChannels"]; +export const feishuThreadBindingTesting: FacadeModule["feishuThreadBindingTesting"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["feishuThreadBindingTesting"] as object, + ) as FacadeModule["feishuThreadBindingTesting"]; +export const parseFeishuDirectConversationId: FacadeModule["parseFeishuDirectConversationId"] = (( + ...args +) => + loadFacadeModule()["parseFeishuDirectConversationId"]( + ...args, + )) as FacadeModule["parseFeishuDirectConversationId"]; +export const parseFeishuConversationId: FacadeModule["parseFeishuConversationId"] = ((...args) => + loadFacadeModule()["parseFeishuConversationId"]( + ...args, + )) as FacadeModule["parseFeishuConversationId"]; +export const parseFeishuTargetId: FacadeModule["parseFeishuTargetId"] = ((...args) => + loadFacadeModule()["parseFeishuTargetId"](...args)) as FacadeModule["parseFeishuTargetId"]; diff --git a/src/plugin-sdk/feishu-setup.ts b/src/plugin-sdk/feishu-setup.ts index 013d381b945..0ec695a786e 100644 --- a/src/plugin-sdk/feishu-setup.ts +++ b/src/plugin-sdk/feishu-setup.ts @@ -1,2 +1,21 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { feishuSetupAdapter, feishuSetupWizard } from "../../extensions/feishu/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["feishu-setup"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "feishu", + artifactBasename: "api.js", + }); +} +export const feishuSetupAdapter: FacadeModule["feishuSetupAdapter"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["feishuSetupAdapter"] as object, +) as FacadeModule["feishuSetupAdapter"]; +export const feishuSetupWizard: FacadeModule["feishuSetupWizard"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["feishuSetupWizard"] as object, +) as FacadeModule["feishuSetupWizard"]; diff --git a/src/plugin-sdk/feishu.ts b/src/plugin-sdk/feishu.ts index c7f03dd2a20..79a89a234d5 100644 --- a/src/plugin-sdk/feishu.ts +++ b/src/plugin-sdk/feishu.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled feishu plugin. -// Keep this list additive and scoped to symbols used under extensions/feishu. +// Keep this list additive and scoped to the bundled Feishu surface. export type { HistoryEntry } from "../auto-reply/reply/history.js"; export { diff --git a/src/plugin-sdk/github-copilot-login.ts b/src/plugin-sdk/github-copilot-login.ts index 903ed9759a4..c0bbbe7ee2b 100644 --- a/src/plugin-sdk/github-copilot-login.ts +++ b/src/plugin-sdk/github-copilot-login.ts @@ -1,2 +1,16 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { githubCopilotLoginCommand } from "../../extensions/github-copilot/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["github-copilot-login"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "github-copilot", + artifactBasename: "api.js", + }); +} +export const githubCopilotLoginCommand: FacadeModule["githubCopilotLoginCommand"] = ((...args) => + loadFacadeModule()["githubCopilotLoginCommand"]( + ...args, + )) as FacadeModule["githubCopilotLoginCommand"]; diff --git a/src/plugin-sdk/google.ts b/src/plugin-sdk/google.ts index 87629f2a84d..1c8e7a4620a 100644 --- a/src/plugin-sdk/google.ts +++ b/src/plugin-sdk/google.ts @@ -1,17 +1,71 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyGoogleGeminiModelDefault, - DEFAULT_GOOGLE_API_BASE_URL, - GOOGLE_GEMINI_DEFAULT_MODEL, - isGoogleGenerativeAiApi, - normalizeAntigravityModelId, - normalizeGoogleApiBaseUrl, - normalizeGoogleGenerativeAiBaseUrl, - normalizeGoogleModelId, - normalizeGoogleProviderConfig, - parseGeminiAuth, - resolveGoogleGenerativeAiApiOrigin, - resolveGoogleGenerativeAiTransport, - shouldNormalizeGoogleProviderConfig, - shouldNormalizeGoogleGenerativeAiProviderConfig, -} from "../../extensions/google/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["google"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "google", + artifactBasename: "api.js", + }); +} +export const applyGoogleGeminiModelDefault: FacadeModule["applyGoogleGeminiModelDefault"] = (( + ...args +) => + loadFacadeModule()["applyGoogleGeminiModelDefault"]( + ...args, + )) as FacadeModule["applyGoogleGeminiModelDefault"]; +export const DEFAULT_GOOGLE_API_BASE_URL: FacadeModule["DEFAULT_GOOGLE_API_BASE_URL"] = + loadFacadeModule()["DEFAULT_GOOGLE_API_BASE_URL"]; +export const GOOGLE_GEMINI_DEFAULT_MODEL: FacadeModule["GOOGLE_GEMINI_DEFAULT_MODEL"] = + loadFacadeModule()["GOOGLE_GEMINI_DEFAULT_MODEL"]; +export const isGoogleGenerativeAiApi: FacadeModule["isGoogleGenerativeAiApi"] = ((...args) => + loadFacadeModule()["isGoogleGenerativeAiApi"]( + ...args, + )) as FacadeModule["isGoogleGenerativeAiApi"]; +export const normalizeAntigravityModelId: FacadeModule["normalizeAntigravityModelId"] = (( + ...args +) => + loadFacadeModule()["normalizeAntigravityModelId"]( + ...args, + )) as FacadeModule["normalizeAntigravityModelId"]; +export const normalizeGoogleApiBaseUrl: FacadeModule["normalizeGoogleApiBaseUrl"] = ((...args) => + loadFacadeModule()["normalizeGoogleApiBaseUrl"]( + ...args, + )) as FacadeModule["normalizeGoogleApiBaseUrl"]; +export const normalizeGoogleGenerativeAiBaseUrl: FacadeModule["normalizeGoogleGenerativeAiBaseUrl"] = + ((...args) => + loadFacadeModule()["normalizeGoogleGenerativeAiBaseUrl"]( + ...args, + )) as FacadeModule["normalizeGoogleGenerativeAiBaseUrl"]; +export const normalizeGoogleModelId: FacadeModule["normalizeGoogleModelId"] = ((...args) => + loadFacadeModule()["normalizeGoogleModelId"](...args)) as FacadeModule["normalizeGoogleModelId"]; +export const normalizeGoogleProviderConfig: FacadeModule["normalizeGoogleProviderConfig"] = (( + ...args +) => + loadFacadeModule()["normalizeGoogleProviderConfig"]( + ...args, + )) as FacadeModule["normalizeGoogleProviderConfig"]; +export const parseGeminiAuth: FacadeModule["parseGeminiAuth"] = ((...args) => + loadFacadeModule()["parseGeminiAuth"](...args)) as FacadeModule["parseGeminiAuth"]; +export const resolveGoogleGenerativeAiApiOrigin: FacadeModule["resolveGoogleGenerativeAiApiOrigin"] = + ((...args) => + loadFacadeModule()["resolveGoogleGenerativeAiApiOrigin"]( + ...args, + )) as FacadeModule["resolveGoogleGenerativeAiApiOrigin"]; +export const resolveGoogleGenerativeAiTransport: FacadeModule["resolveGoogleGenerativeAiTransport"] = + ((...args) => + loadFacadeModule()["resolveGoogleGenerativeAiTransport"]( + ...args, + )) as FacadeModule["resolveGoogleGenerativeAiTransport"]; +export const shouldNormalizeGoogleProviderConfig: FacadeModule["shouldNormalizeGoogleProviderConfig"] = + ((...args) => + loadFacadeModule()["shouldNormalizeGoogleProviderConfig"]( + ...args, + )) as FacadeModule["shouldNormalizeGoogleProviderConfig"]; +export const shouldNormalizeGoogleGenerativeAiProviderConfig: FacadeModule["shouldNormalizeGoogleGenerativeAiProviderConfig"] = + ((...args) => + loadFacadeModule()["shouldNormalizeGoogleGenerativeAiProviderConfig"]( + ...args, + )) as FacadeModule["shouldNormalizeGoogleGenerativeAiProviderConfig"]; diff --git a/src/plugin-sdk/googlechat.ts b/src/plugin-sdk/googlechat.ts index 3416ef742cd..19e71e5f6fe 100644 --- a/src/plugin-sdk/googlechat.ts +++ b/src/plugin-sdk/googlechat.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled googlechat plugin. -// Keep this list additive and scoped to symbols used under extensions/googlechat. +// Keep this list additive and scoped to the bundled Google Chat surface. import { resolveChannelGroupRequireMention } from "./channel-policy.js"; import { createOptionalChannelSetupSurface } from "./channel-setup.js"; diff --git a/src/plugin-sdk/huggingface.ts b/src/plugin-sdk/huggingface.ts index b3f1892cb70..0239582bf4a 100644 --- a/src/plugin-sdk/huggingface.ts +++ b/src/plugin-sdk/huggingface.ts @@ -1,11 +1,45 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildHuggingfaceModelDefinition, - buildHuggingfaceProvider, - discoverHuggingfaceModels, - HUGGINGFACE_BASE_URL, - HUGGINGFACE_DEFAULT_MODEL_REF, - HUGGINGFACE_MODEL_CATALOG, - HUGGINGFACE_POLICY_SUFFIXES, - isHuggingfacePolicyLocked, -} from "../../extensions/huggingface/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["huggingface"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "huggingface", + artifactBasename: "api.js", + }); +} +export const buildHuggingfaceModelDefinition: FacadeModule["buildHuggingfaceModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildHuggingfaceModelDefinition"]( + ...args, + )) as FacadeModule["buildHuggingfaceModelDefinition"]; +export const buildHuggingfaceProvider: FacadeModule["buildHuggingfaceProvider"] = ((...args) => + loadFacadeModule()["buildHuggingfaceProvider"]( + ...args, + )) as FacadeModule["buildHuggingfaceProvider"]; +export const discoverHuggingfaceModels: FacadeModule["discoverHuggingfaceModels"] = ((...args) => + loadFacadeModule()["discoverHuggingfaceModels"]( + ...args, + )) as FacadeModule["discoverHuggingfaceModels"]; +export const HUGGINGFACE_BASE_URL: FacadeModule["HUGGINGFACE_BASE_URL"] = + loadFacadeModule()["HUGGINGFACE_BASE_URL"]; +export const HUGGINGFACE_DEFAULT_MODEL_REF: FacadeModule["HUGGINGFACE_DEFAULT_MODEL_REF"] = + loadFacadeModule()["HUGGINGFACE_DEFAULT_MODEL_REF"]; +export const HUGGINGFACE_MODEL_CATALOG: FacadeModule["HUGGINGFACE_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["HUGGINGFACE_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["HUGGINGFACE_MODEL_CATALOG"]; +export const HUGGINGFACE_POLICY_SUFFIXES: FacadeModule["HUGGINGFACE_POLICY_SUFFIXES"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["HUGGINGFACE_POLICY_SUFFIXES"] as unknown as readonly unknown[], + ) as FacadeModule["HUGGINGFACE_POLICY_SUFFIXES"]; +export const isHuggingfacePolicyLocked: FacadeModule["isHuggingfacePolicyLocked"] = ((...args) => + loadFacadeModule()["isHuggingfacePolicyLocked"]( + ...args, + )) as FacadeModule["isHuggingfacePolicyLocked"]; diff --git a/src/plugin-sdk/image-generation-runtime.ts b/src/plugin-sdk/image-generation-runtime.ts index ac8bd874c3d..13c547de234 100644 --- a/src/plugin-sdk/image-generation-runtime.ts +++ b/src/plugin-sdk/image-generation-runtime.ts @@ -1,2 +1,21 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export * from "../../extensions/image-generation-core/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["image-generation-runtime"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "image-generation-core", + artifactBasename: "runtime-api.js", + }); +} +export const generateImage: FacadeModule["generateImage"] = ((...args) => + loadFacadeModule()["generateImage"](...args)) as FacadeModule["generateImage"]; +export const listRuntimeImageGenerationProviders: FacadeModule["listRuntimeImageGenerationProviders"] = + ((...args) => + loadFacadeModule()["listRuntimeImageGenerationProviders"]( + ...args, + )) as FacadeModule["listRuntimeImageGenerationProviders"]; +export type GenerateImageParams = FacadeEntry["types"]["GenerateImageParams"]; +export type GenerateImageRuntimeResult = FacadeEntry["types"]["GenerateImageRuntimeResult"]; diff --git a/src/plugin-sdk/imessage-policy.ts b/src/plugin-sdk/imessage-policy.ts index 7bc5b8c7e1b..cfd445a7a33 100644 --- a/src/plugin-sdk/imessage-policy.ts +++ b/src/plugin-sdk/imessage-policy.ts @@ -1,7 +1,32 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - normalizeIMessageHandle, - resolveIMessageRuntimeGroupPolicy, - resolveIMessageGroupRequireMention, - resolveIMessageGroupToolPolicy, -} from "../../extensions/imessage/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["imessage-policy"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "imessage", + artifactBasename: "api.js", + }); +} +export const normalizeIMessageHandle: FacadeModule["normalizeIMessageHandle"] = ((...args) => + loadFacadeModule()["normalizeIMessageHandle"]( + ...args, + )) as FacadeModule["normalizeIMessageHandle"]; +export const resolveIMessageRuntimeGroupPolicy: FacadeModule["resolveIMessageRuntimeGroupPolicy"] = + ((...args) => + loadFacadeModule()["resolveIMessageRuntimeGroupPolicy"]( + ...args, + )) as FacadeModule["resolveIMessageRuntimeGroupPolicy"]; +export const resolveIMessageGroupRequireMention: FacadeModule["resolveIMessageGroupRequireMention"] = + ((...args) => + loadFacadeModule()["resolveIMessageGroupRequireMention"]( + ...args, + )) as FacadeModule["resolveIMessageGroupRequireMention"]; +export const resolveIMessageGroupToolPolicy: FacadeModule["resolveIMessageGroupToolPolicy"] = (( + ...args +) => + loadFacadeModule()["resolveIMessageGroupToolPolicy"]( + ...args, + )) as FacadeModule["resolveIMessageGroupToolPolicy"]; diff --git a/src/plugin-sdk/imessage-runtime.ts b/src/plugin-sdk/imessage-runtime.ts index 9e66147ae13..31fed42dc5f 100644 --- a/src/plugin-sdk/imessage-runtime.ts +++ b/src/plugin-sdk/imessage-runtime.ts @@ -1,7 +1,21 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - monitorIMessageProvider, - probeIMessage, - sendMessageIMessage, -} from "../../extensions/imessage/runtime-api.js"; -export type { IMessageProbe } from "../../extensions/imessage/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["imessage-runtime"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "imessage", + artifactBasename: "runtime-api.js", + }); +} +export const monitorIMessageProvider: FacadeModule["monitorIMessageProvider"] = ((...args) => + loadFacadeModule()["monitorIMessageProvider"]( + ...args, + )) as FacadeModule["monitorIMessageProvider"]; +export const probeIMessage: FacadeModule["probeIMessage"] = ((...args) => + loadFacadeModule()["probeIMessage"](...args)) as FacadeModule["probeIMessage"]; +export const sendMessageIMessage: FacadeModule["sendMessageIMessage"] = ((...args) => + loadFacadeModule()["sendMessageIMessage"](...args)) as FacadeModule["sendMessageIMessage"]; +export type IMessageProbe = FacadeEntry["types"]["IMessageProbe"]; diff --git a/src/plugin-sdk/imessage-targets.ts b/src/plugin-sdk/imessage-targets.ts index f91dc99190d..9d5bc5f2eff 100644 --- a/src/plugin-sdk/imessage-targets.ts +++ b/src/plugin-sdk/imessage-targets.ts @@ -1,9 +1,40 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - normalizeIMessageHandle, - parseChatAllowTargetPrefixes, - parseChatTargetPrefixesOrThrow, - resolveServicePrefixedAllowTarget, - resolveServicePrefixedTarget, -} from "../../extensions/imessage/api.js"; -export type { ParsedChatTarget } from "../../extensions/imessage/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["imessage-targets"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "imessage", + artifactBasename: "api.js", + }); +} +export const normalizeIMessageHandle: FacadeModule["normalizeIMessageHandle"] = ((...args) => + loadFacadeModule()["normalizeIMessageHandle"]( + ...args, + )) as FacadeModule["normalizeIMessageHandle"]; +export const parseChatAllowTargetPrefixes: FacadeModule["parseChatAllowTargetPrefixes"] = (( + ...args +) => + loadFacadeModule()["parseChatAllowTargetPrefixes"]( + ...args, + )) as FacadeModule["parseChatAllowTargetPrefixes"]; +export const parseChatTargetPrefixesOrThrow: FacadeModule["parseChatTargetPrefixesOrThrow"] = (( + ...args +) => + loadFacadeModule()["parseChatTargetPrefixesOrThrow"]( + ...args, + )) as FacadeModule["parseChatTargetPrefixesOrThrow"]; +export const resolveServicePrefixedAllowTarget: FacadeModule["resolveServicePrefixedAllowTarget"] = + ((...args) => + loadFacadeModule()["resolveServicePrefixedAllowTarget"]( + ...args, + )) as FacadeModule["resolveServicePrefixedAllowTarget"]; +export const resolveServicePrefixedTarget: FacadeModule["resolveServicePrefixedTarget"] = (( + ...args +) => + loadFacadeModule()["resolveServicePrefixedTarget"]( + ...args, + )) as FacadeModule["resolveServicePrefixedTarget"]; +export type ParsedChatTarget = FacadeEntry["types"]["ParsedChatTarget"]; diff --git a/src/plugin-sdk/imessage.ts b/src/plugin-sdk/imessage.ts index 8be5af3c82d..7732e8b9bc3 100644 --- a/src/plugin-sdk/imessage.ts +++ b/src/plugin-sdk/imessage.ts @@ -1,3 +1,6 @@ +import type { OpenClawConfig } from "../config/config.js"; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + export type { IMessageAccountConfig } from "../config/types.js"; export type { IMessageProbe } from "./imessage-runtime.js"; export type { OpenClawConfig } from "../config/config.js"; @@ -57,3 +60,28 @@ export { collectStatusIssuesFromLastError, } from "./status-helpers.js"; export { monitorIMessageProvider, probeIMessage, sendMessageIMessage } from "./imessage-runtime.js"; + +export type IMessageConversationBindingManager = { + stop: () => void; +}; + +type IMessageFacadeModule = { + createIMessageConversationBindingManager: (params: { + accountId?: string; + cfg: OpenClawConfig; + }) => IMessageConversationBindingManager; +}; + +function loadIMessageFacadeModule(): IMessageFacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "imessage", + artifactBasename: "api.js", + }); +} + +export function createIMessageConversationBindingManager(params: { + accountId?: string; + cfg: OpenClawConfig; +}): IMessageConversationBindingManager { + return loadIMessageFacadeModule().createIMessageConversationBindingManager(params); +} diff --git a/src/plugin-sdk/index.bundle.test.ts b/src/plugin-sdk/index.bundle.test.ts index 77955017c41..0679472d420 100644 --- a/src/plugin-sdk/index.bundle.test.ts +++ b/src/plugin-sdk/index.bundle.test.ts @@ -3,13 +3,14 @@ import { createRequire } from "node:module"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { describe, expect, it } from "vitest"; +import { bundledPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; import { buildPluginSdkEntrySources, pluginSdkEntrypoints } from "./entrypoints.js"; const require = createRequire(import.meta.url); const tsdownModuleUrl = pathToFileURL(require.resolve("tsdown")).href; const bundledRepresentativeEntrypoints = ["matrix-runtime-heavy"] as const; const matrixRuntimeCoverageEntries = { - "matrix-runtime-sdk": "extensions/matrix/src/matrix/sdk.ts", + "matrix-runtime-sdk": bundledPluginFile("matrix", "src/matrix/sdk.ts"), } as const; const bundledCoverageEntrySources = { ...buildPluginSdkEntrySources(bundledRepresentativeEntrypoints), diff --git a/src/plugin-sdk/irc-surface.ts b/src/plugin-sdk/irc-surface.ts index 7bff0429ae5..b7173c32128 100644 --- a/src/plugin-sdk/irc-surface.ts +++ b/src/plugin-sdk/irc-surface.ts @@ -1,8 +1,29 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - ircSetupAdapter, - ircSetupWizard, - listIrcAccountIds, - resolveDefaultIrcAccountId, - resolveIrcAccount, -} from "../../extensions/irc/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["irc-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "irc", + artifactBasename: "api.js", + }); +} +export const ircSetupAdapter: FacadeModule["ircSetupAdapter"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["ircSetupAdapter"] as object, +) as FacadeModule["ircSetupAdapter"]; +export const ircSetupWizard: FacadeModule["ircSetupWizard"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["ircSetupWizard"] as object, +) as FacadeModule["ircSetupWizard"]; +export const listIrcAccountIds: FacadeModule["listIrcAccountIds"] = ((...args) => + loadFacadeModule()["listIrcAccountIds"](...args)) as FacadeModule["listIrcAccountIds"]; +export const resolveDefaultIrcAccountId: FacadeModule["resolveDefaultIrcAccountId"] = ((...args) => + loadFacadeModule()["resolveDefaultIrcAccountId"]( + ...args, + )) as FacadeModule["resolveDefaultIrcAccountId"]; +export const resolveIrcAccount: FacadeModule["resolveIrcAccount"] = ((...args) => + loadFacadeModule()["resolveIrcAccount"](...args)) as FacadeModule["resolveIrcAccount"]; diff --git a/src/plugin-sdk/irc.ts b/src/plugin-sdk/irc.ts index 037408bb456..4d4105c8bc7 100644 --- a/src/plugin-sdk/irc.ts +++ b/src/plugin-sdk/irc.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled irc plugin. -// Keep this list additive and scoped to symbols used under extensions/irc. +// Keep this list additive and scoped to the bundled IRC surface. export { resolveControlCommandGate } from "../channels/command-gating.js"; export { logInboundDrop } from "../channels/logging.js"; diff --git a/src/plugin-sdk/kilocode.ts b/src/plugin-sdk/kilocode.ts index 059e8e842b6..872d9ba9665 100644 --- a/src/plugin-sdk/kilocode.ts +++ b/src/plugin-sdk/kilocode.ts @@ -1,16 +1,53 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildKilocodeProvider, - buildKilocodeProviderWithDiscovery, - buildKilocodeModelDefinition, - discoverKilocodeModels, - KILOCODE_BASE_URL, - KILOCODE_DEFAULT_CONTEXT_WINDOW, - KILOCODE_DEFAULT_COST, - KILOCODE_DEFAULT_MAX_TOKENS, - KILOCODE_DEFAULT_MODEL_ID, - KILOCODE_DEFAULT_MODEL_NAME, - KILOCODE_DEFAULT_MODEL_REF, - KILOCODE_MODELS_URL, - KILOCODE_MODEL_CATALOG, -} from "../../extensions/kilocode/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["kilocode"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "kilocode", + artifactBasename: "api.js", + }); +} +export const buildKilocodeProvider: FacadeModule["buildKilocodeProvider"] = ((...args) => + loadFacadeModule()["buildKilocodeProvider"](...args)) as FacadeModule["buildKilocodeProvider"]; +export const buildKilocodeProviderWithDiscovery: FacadeModule["buildKilocodeProviderWithDiscovery"] = + ((...args) => + loadFacadeModule()["buildKilocodeProviderWithDiscovery"]( + ...args, + )) as FacadeModule["buildKilocodeProviderWithDiscovery"]; +export const buildKilocodeModelDefinition: FacadeModule["buildKilocodeModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildKilocodeModelDefinition"]( + ...args, + )) as FacadeModule["buildKilocodeModelDefinition"]; +export const discoverKilocodeModels: FacadeModule["discoverKilocodeModels"] = ((...args) => + loadFacadeModule()["discoverKilocodeModels"](...args)) as FacadeModule["discoverKilocodeModels"]; +export const KILOCODE_BASE_URL: FacadeModule["KILOCODE_BASE_URL"] = + loadFacadeModule()["KILOCODE_BASE_URL"]; +export const KILOCODE_DEFAULT_CONTEXT_WINDOW: FacadeModule["KILOCODE_DEFAULT_CONTEXT_WINDOW"] = + loadFacadeModule()["KILOCODE_DEFAULT_CONTEXT_WINDOW"]; +export const KILOCODE_DEFAULT_COST: FacadeModule["KILOCODE_DEFAULT_COST"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["KILOCODE_DEFAULT_COST"] as object, + ) as FacadeModule["KILOCODE_DEFAULT_COST"]; +export const KILOCODE_DEFAULT_MAX_TOKENS: FacadeModule["KILOCODE_DEFAULT_MAX_TOKENS"] = + loadFacadeModule()["KILOCODE_DEFAULT_MAX_TOKENS"]; +export const KILOCODE_DEFAULT_MODEL_ID: FacadeModule["KILOCODE_DEFAULT_MODEL_ID"] = + loadFacadeModule()["KILOCODE_DEFAULT_MODEL_ID"]; +export const KILOCODE_DEFAULT_MODEL_NAME: FacadeModule["KILOCODE_DEFAULT_MODEL_NAME"] = + loadFacadeModule()["KILOCODE_DEFAULT_MODEL_NAME"]; +export const KILOCODE_DEFAULT_MODEL_REF: FacadeModule["KILOCODE_DEFAULT_MODEL_REF"] = + loadFacadeModule()["KILOCODE_DEFAULT_MODEL_REF"]; +export const KILOCODE_MODELS_URL: FacadeModule["KILOCODE_MODELS_URL"] = + loadFacadeModule()["KILOCODE_MODELS_URL"]; +export const KILOCODE_MODEL_CATALOG: FacadeModule["KILOCODE_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["KILOCODE_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["KILOCODE_MODEL_CATALOG"]; diff --git a/src/plugin-sdk/kimi-coding.ts b/src/plugin-sdk/kimi-coding.ts index 59127aedf99..0bb15590ed2 100644 --- a/src/plugin-sdk/kimi-coding.ts +++ b/src/plugin-sdk/kimi-coding.ts @@ -1,2 +1,16 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { buildKimiCodingProvider } from "../../extensions/kimi-coding/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["kimi-coding"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "kimi-coding", + artifactBasename: "api.js", + }); +} +export const buildKimiCodingProvider: FacadeModule["buildKimiCodingProvider"] = ((...args) => + loadFacadeModule()["buildKimiCodingProvider"]( + ...args, + )) as FacadeModule["buildKimiCodingProvider"]; diff --git a/src/plugin-sdk/line-runtime.ts b/src/plugin-sdk/line-runtime.ts index a4ed9e8f19a..8fc50a213db 100644 --- a/src/plugin-sdk/line-runtime.ts +++ b/src/plugin-sdk/line-runtime.ts @@ -1,76 +1,155 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildTemplateMessageFromPayload, - cancelDefaultRichMenu, - createActionCard, - createAgendaCard, - createAppleTvRemoteCard, - createCarousel, - createDefaultMenuConfig, - createDeviceControlCard, - createEventCard, - createGridLayout, - createImageCard, - createInfoCard, - createListCard, - createMediaPlayerCard, - createNotificationBubble, - createQuickReplyItems, - createReceiptCard, - createRichMenu, - createRichMenuAlias, - datetimePickerAction, - deleteRichMenu, - deleteRichMenuAlias, - downloadLineMedia, - firstDefined, - getDefaultRichMenuId, - getRichMenu, - getRichMenuIdOfUser, - getRichMenuList, - isSenderAllowed, - linkRichMenuToUser, - linkRichMenuToUsers, - messageAction, - monitorLineProvider, - normalizeAllowFrom, - normalizeDmAllowFromWithStore, - postbackAction, - probeLineBot, - pushFlexMessage, - pushLocationMessage, - pushMessageLine, - pushMessagesLine, - pushTemplateMessage, - pushTextMessageWithQuickReplies, - sendMessageLine, - setDefaultRichMenu, - toFlexMessage, - unlinkRichMenuFromUser, - unlinkRichMenuFromUsers, - uploadRichMenuImage, - uriAction, -} from "../../extensions/line/runtime-api.js"; -export type { - Action, - CardAction, - CreateRichMenuParams, - FlexBox, - FlexBubble, - FlexButton, - FlexCarousel, - FlexComponent, - FlexContainer, - FlexImage, - FlexText, - LineChannelData, - LineConfig, - LineProbeResult, - ListItem, - ResolvedLineAccount, - RichMenuArea, - RichMenuAreaRequest, - RichMenuRequest, - RichMenuResponse, - RichMenuSize, -} from "../../extensions/line/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["line-runtime"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "line", + artifactBasename: "runtime-api.js", + }); +} +export const buildTemplateMessageFromPayload: FacadeModule["buildTemplateMessageFromPayload"] = (( + ...args +) => + loadFacadeModule()["buildTemplateMessageFromPayload"]( + ...args, + )) as FacadeModule["buildTemplateMessageFromPayload"]; +export const cancelDefaultRichMenu: FacadeModule["cancelDefaultRichMenu"] = ((...args) => + loadFacadeModule()["cancelDefaultRichMenu"](...args)) as FacadeModule["cancelDefaultRichMenu"]; +export const createActionCard: FacadeModule["createActionCard"] = ((...args) => + loadFacadeModule()["createActionCard"](...args)) as FacadeModule["createActionCard"]; +export const createAgendaCard: FacadeModule["createAgendaCard"] = ((...args) => + loadFacadeModule()["createAgendaCard"](...args)) as FacadeModule["createAgendaCard"]; +export const createAppleTvRemoteCard: FacadeModule["createAppleTvRemoteCard"] = ((...args) => + loadFacadeModule()["createAppleTvRemoteCard"]( + ...args, + )) as FacadeModule["createAppleTvRemoteCard"]; +export const createCarousel: FacadeModule["createCarousel"] = ((...args) => + loadFacadeModule()["createCarousel"](...args)) as FacadeModule["createCarousel"]; +export const createDefaultMenuConfig: FacadeModule["createDefaultMenuConfig"] = ((...args) => + loadFacadeModule()["createDefaultMenuConfig"]( + ...args, + )) as FacadeModule["createDefaultMenuConfig"]; +export const createDeviceControlCard: FacadeModule["createDeviceControlCard"] = ((...args) => + loadFacadeModule()["createDeviceControlCard"]( + ...args, + )) as FacadeModule["createDeviceControlCard"]; +export const createEventCard: FacadeModule["createEventCard"] = ((...args) => + loadFacadeModule()["createEventCard"](...args)) as FacadeModule["createEventCard"]; +export const createGridLayout: FacadeModule["createGridLayout"] = ((...args) => + loadFacadeModule()["createGridLayout"](...args)) as FacadeModule["createGridLayout"]; +export const createImageCard: FacadeModule["createImageCard"] = ((...args) => + loadFacadeModule()["createImageCard"](...args)) as FacadeModule["createImageCard"]; +export const createInfoCard: FacadeModule["createInfoCard"] = ((...args) => + loadFacadeModule()["createInfoCard"](...args)) as FacadeModule["createInfoCard"]; +export const createListCard: FacadeModule["createListCard"] = ((...args) => + loadFacadeModule()["createListCard"](...args)) as FacadeModule["createListCard"]; +export const createMediaPlayerCard: FacadeModule["createMediaPlayerCard"] = ((...args) => + loadFacadeModule()["createMediaPlayerCard"](...args)) as FacadeModule["createMediaPlayerCard"]; +export const createNotificationBubble: FacadeModule["createNotificationBubble"] = ((...args) => + loadFacadeModule()["createNotificationBubble"]( + ...args, + )) as FacadeModule["createNotificationBubble"]; +export const createQuickReplyItems: FacadeModule["createQuickReplyItems"] = ((...args) => + loadFacadeModule()["createQuickReplyItems"](...args)) as FacadeModule["createQuickReplyItems"]; +export const createReceiptCard: FacadeModule["createReceiptCard"] = ((...args) => + loadFacadeModule()["createReceiptCard"](...args)) as FacadeModule["createReceiptCard"]; +export const createRichMenu: FacadeModule["createRichMenu"] = ((...args) => + loadFacadeModule()["createRichMenu"](...args)) as FacadeModule["createRichMenu"]; +export const createRichMenuAlias: FacadeModule["createRichMenuAlias"] = ((...args) => + loadFacadeModule()["createRichMenuAlias"](...args)) as FacadeModule["createRichMenuAlias"]; +export const datetimePickerAction: FacadeModule["datetimePickerAction"] = ((...args) => + loadFacadeModule()["datetimePickerAction"](...args)) as FacadeModule["datetimePickerAction"]; +export const deleteRichMenu: FacadeModule["deleteRichMenu"] = ((...args) => + loadFacadeModule()["deleteRichMenu"](...args)) as FacadeModule["deleteRichMenu"]; +export const deleteRichMenuAlias: FacadeModule["deleteRichMenuAlias"] = ((...args) => + loadFacadeModule()["deleteRichMenuAlias"](...args)) as FacadeModule["deleteRichMenuAlias"]; +export const downloadLineMedia: FacadeModule["downloadLineMedia"] = ((...args) => + loadFacadeModule()["downloadLineMedia"](...args)) as FacadeModule["downloadLineMedia"]; +export const firstDefined: FacadeModule["firstDefined"] = ((...args) => + loadFacadeModule()["firstDefined"](...args)) as FacadeModule["firstDefined"]; +export const getDefaultRichMenuId: FacadeModule["getDefaultRichMenuId"] = ((...args) => + loadFacadeModule()["getDefaultRichMenuId"](...args)) as FacadeModule["getDefaultRichMenuId"]; +export const getRichMenu: FacadeModule["getRichMenu"] = ((...args) => + loadFacadeModule()["getRichMenu"](...args)) as FacadeModule["getRichMenu"]; +export const getRichMenuIdOfUser: FacadeModule["getRichMenuIdOfUser"] = ((...args) => + loadFacadeModule()["getRichMenuIdOfUser"](...args)) as FacadeModule["getRichMenuIdOfUser"]; +export const getRichMenuList: FacadeModule["getRichMenuList"] = ((...args) => + loadFacadeModule()["getRichMenuList"](...args)) as FacadeModule["getRichMenuList"]; +export const isSenderAllowed: FacadeModule["isSenderAllowed"] = ((...args) => + loadFacadeModule()["isSenderAllowed"](...args)) as FacadeModule["isSenderAllowed"]; +export const linkRichMenuToUser: FacadeModule["linkRichMenuToUser"] = ((...args) => + loadFacadeModule()["linkRichMenuToUser"](...args)) as FacadeModule["linkRichMenuToUser"]; +export const linkRichMenuToUsers: FacadeModule["linkRichMenuToUsers"] = ((...args) => + loadFacadeModule()["linkRichMenuToUsers"](...args)) as FacadeModule["linkRichMenuToUsers"]; +export const messageAction: FacadeModule["messageAction"] = ((...args) => + loadFacadeModule()["messageAction"](...args)) as FacadeModule["messageAction"]; +export const monitorLineProvider: FacadeModule["monitorLineProvider"] = ((...args) => + loadFacadeModule()["monitorLineProvider"](...args)) as FacadeModule["monitorLineProvider"]; +export const normalizeAllowFrom: FacadeModule["normalizeAllowFrom"] = ((...args) => + loadFacadeModule()["normalizeAllowFrom"](...args)) as FacadeModule["normalizeAllowFrom"]; +export const normalizeDmAllowFromWithStore: FacadeModule["normalizeDmAllowFromWithStore"] = (( + ...args +) => + loadFacadeModule()["normalizeDmAllowFromWithStore"]( + ...args, + )) as FacadeModule["normalizeDmAllowFromWithStore"]; +export const postbackAction: FacadeModule["postbackAction"] = ((...args) => + loadFacadeModule()["postbackAction"](...args)) as FacadeModule["postbackAction"]; +export const probeLineBot: FacadeModule["probeLineBot"] = ((...args) => + loadFacadeModule()["probeLineBot"](...args)) as FacadeModule["probeLineBot"]; +export const pushFlexMessage: FacadeModule["pushFlexMessage"] = ((...args) => + loadFacadeModule()["pushFlexMessage"](...args)) as FacadeModule["pushFlexMessage"]; +export const pushLocationMessage: FacadeModule["pushLocationMessage"] = ((...args) => + loadFacadeModule()["pushLocationMessage"](...args)) as FacadeModule["pushLocationMessage"]; +export const pushMessageLine: FacadeModule["pushMessageLine"] = ((...args) => + loadFacadeModule()["pushMessageLine"](...args)) as FacadeModule["pushMessageLine"]; +export const pushMessagesLine: FacadeModule["pushMessagesLine"] = ((...args) => + loadFacadeModule()["pushMessagesLine"](...args)) as FacadeModule["pushMessagesLine"]; +export const pushTemplateMessage: FacadeModule["pushTemplateMessage"] = ((...args) => + loadFacadeModule()["pushTemplateMessage"](...args)) as FacadeModule["pushTemplateMessage"]; +export const pushTextMessageWithQuickReplies: FacadeModule["pushTextMessageWithQuickReplies"] = (( + ...args +) => + loadFacadeModule()["pushTextMessageWithQuickReplies"]( + ...args, + )) as FacadeModule["pushTextMessageWithQuickReplies"]; +export const sendMessageLine: FacadeModule["sendMessageLine"] = ((...args) => + loadFacadeModule()["sendMessageLine"](...args)) as FacadeModule["sendMessageLine"]; +export const setDefaultRichMenu: FacadeModule["setDefaultRichMenu"] = ((...args) => + loadFacadeModule()["setDefaultRichMenu"](...args)) as FacadeModule["setDefaultRichMenu"]; +export const toFlexMessage: FacadeModule["toFlexMessage"] = ((...args) => + loadFacadeModule()["toFlexMessage"](...args)) as FacadeModule["toFlexMessage"]; +export const unlinkRichMenuFromUser: FacadeModule["unlinkRichMenuFromUser"] = ((...args) => + loadFacadeModule()["unlinkRichMenuFromUser"](...args)) as FacadeModule["unlinkRichMenuFromUser"]; +export const unlinkRichMenuFromUsers: FacadeModule["unlinkRichMenuFromUsers"] = ((...args) => + loadFacadeModule()["unlinkRichMenuFromUsers"]( + ...args, + )) as FacadeModule["unlinkRichMenuFromUsers"]; +export const uploadRichMenuImage: FacadeModule["uploadRichMenuImage"] = ((...args) => + loadFacadeModule()["uploadRichMenuImage"](...args)) as FacadeModule["uploadRichMenuImage"]; +export const uriAction: FacadeModule["uriAction"] = ((...args) => + loadFacadeModule()["uriAction"](...args)) as FacadeModule["uriAction"]; +export type Action = FacadeEntry["types"]["Action"]; +export type CardAction = FacadeEntry["types"]["CardAction"]; +export type CreateRichMenuParams = FacadeEntry["types"]["CreateRichMenuParams"]; +export type FlexBox = FacadeEntry["types"]["FlexBox"]; +export type FlexBubble = FacadeEntry["types"]["FlexBubble"]; +export type FlexButton = FacadeEntry["types"]["FlexButton"]; +export type FlexCarousel = FacadeEntry["types"]["FlexCarousel"]; +export type FlexComponent = FacadeEntry["types"]["FlexComponent"]; +export type FlexContainer = FacadeEntry["types"]["FlexContainer"]; +export type FlexImage = FacadeEntry["types"]["FlexImage"]; +export type FlexText = FacadeEntry["types"]["FlexText"]; +export type LineChannelData = FacadeEntry["types"]["LineChannelData"]; +export type LineConfig = FacadeEntry["types"]["LineConfig"]; +export type LineProbeResult = FacadeEntry["types"]["LineProbeResult"]; +export type ListItem = FacadeEntry["types"]["ListItem"]; +export type ResolvedLineAccount = FacadeEntry["types"]["ResolvedLineAccount"]; +export type RichMenuArea = FacadeEntry["types"]["RichMenuArea"]; +export type RichMenuAreaRequest = FacadeEntry["types"]["RichMenuAreaRequest"]; +export type RichMenuRequest = FacadeEntry["types"]["RichMenuRequest"]; +export type RichMenuResponse = FacadeEntry["types"]["RichMenuResponse"]; +export type RichMenuSize = FacadeEntry["types"]["RichMenuSize"]; diff --git a/src/plugin-sdk/line-surface.ts b/src/plugin-sdk/line-surface.ts index 0cd27492d9d..88f62d2abd7 100644 --- a/src/plugin-sdk/line-surface.ts +++ b/src/plugin-sdk/line-surface.ts @@ -1,28 +1,68 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - createActionCard, - createAgendaCard, - createAppleTvRemoteCard, - createDeviceControlCard, - createEventCard, - createImageCard, - createInfoCard, - createListCard, - createMediaPlayerCard, - createReceiptCard, - LineConfigSchema, - listLineAccountIds, - normalizeAccountId, - processLineMessage, - resolveDefaultLineAccountId, - resolveExactLineGroupConfigKey, - resolveLineAccount, -} from "../../extensions/line/runtime-api.js"; -export type { - CardAction, - LineChannelData, - LineConfig, - LineProbeResult, - ListItem, - ResolvedLineAccount, -} from "../../extensions/line/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["line-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "line", + artifactBasename: "runtime-api.js", + }); +} +export const createActionCard: FacadeModule["createActionCard"] = ((...args) => + loadFacadeModule()["createActionCard"](...args)) as FacadeModule["createActionCard"]; +export const createAgendaCard: FacadeModule["createAgendaCard"] = ((...args) => + loadFacadeModule()["createAgendaCard"](...args)) as FacadeModule["createAgendaCard"]; +export const createAppleTvRemoteCard: FacadeModule["createAppleTvRemoteCard"] = ((...args) => + loadFacadeModule()["createAppleTvRemoteCard"]( + ...args, + )) as FacadeModule["createAppleTvRemoteCard"]; +export const createDeviceControlCard: FacadeModule["createDeviceControlCard"] = ((...args) => + loadFacadeModule()["createDeviceControlCard"]( + ...args, + )) as FacadeModule["createDeviceControlCard"]; +export const createEventCard: FacadeModule["createEventCard"] = ((...args) => + loadFacadeModule()["createEventCard"](...args)) as FacadeModule["createEventCard"]; +export const createImageCard: FacadeModule["createImageCard"] = ((...args) => + loadFacadeModule()["createImageCard"](...args)) as FacadeModule["createImageCard"]; +export const createInfoCard: FacadeModule["createInfoCard"] = ((...args) => + loadFacadeModule()["createInfoCard"](...args)) as FacadeModule["createInfoCard"]; +export const createListCard: FacadeModule["createListCard"] = ((...args) => + loadFacadeModule()["createListCard"](...args)) as FacadeModule["createListCard"]; +export const createMediaPlayerCard: FacadeModule["createMediaPlayerCard"] = ((...args) => + loadFacadeModule()["createMediaPlayerCard"](...args)) as FacadeModule["createMediaPlayerCard"]; +export const createReceiptCard: FacadeModule["createReceiptCard"] = ((...args) => + loadFacadeModule()["createReceiptCard"](...args)) as FacadeModule["createReceiptCard"]; +export const LineConfigSchema: FacadeModule["LineConfigSchema"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["LineConfigSchema"] as object, +) as FacadeModule["LineConfigSchema"]; +export const listLineAccountIds: FacadeModule["listLineAccountIds"] = ((...args) => + loadFacadeModule()["listLineAccountIds"](...args)) as FacadeModule["listLineAccountIds"]; +export const normalizeAccountId: FacadeModule["normalizeAccountId"] = ((...args) => + loadFacadeModule()["normalizeAccountId"](...args)) as FacadeModule["normalizeAccountId"]; +export const processLineMessage: FacadeModule["processLineMessage"] = ((...args) => + loadFacadeModule()["processLineMessage"](...args)) as FacadeModule["processLineMessage"]; +export const resolveDefaultLineAccountId: FacadeModule["resolveDefaultLineAccountId"] = (( + ...args +) => + loadFacadeModule()["resolveDefaultLineAccountId"]( + ...args, + )) as FacadeModule["resolveDefaultLineAccountId"]; +export const resolveExactLineGroupConfigKey: FacadeModule["resolveExactLineGroupConfigKey"] = (( + ...args +) => + loadFacadeModule()["resolveExactLineGroupConfigKey"]( + ...args, + )) as FacadeModule["resolveExactLineGroupConfigKey"]; +export const resolveLineAccount: FacadeModule["resolveLineAccount"] = ((...args) => + loadFacadeModule()["resolveLineAccount"](...args)) as FacadeModule["resolveLineAccount"]; +export type CardAction = FacadeEntry["types"]["CardAction"]; +export type LineChannelData = FacadeEntry["types"]["LineChannelData"]; +export type LineConfig = FacadeEntry["types"]["LineConfig"]; +export type LineProbeResult = FacadeEntry["types"]["LineProbeResult"]; +export type ListItem = FacadeEntry["types"]["ListItem"]; +export type ResolvedLineAccount = FacadeEntry["types"]["ResolvedLineAccount"]; diff --git a/src/plugin-sdk/litellm.ts b/src/plugin-sdk/litellm.ts index 0255b827b30..6040cafcbda 100644 --- a/src/plugin-sdk/litellm.ts +++ b/src/plugin-sdk/litellm.ts @@ -1,9 +1,30 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyLitellmConfig, - applyLitellmProviderConfig, - buildLitellmModelDefinition, - LITELLM_BASE_URL, - LITELLM_DEFAULT_MODEL_ID, - LITELLM_DEFAULT_MODEL_REF, -} from "../../extensions/litellm/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["litellm"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "litellm", + artifactBasename: "api.js", + }); +} +export const applyLitellmConfig: FacadeModule["applyLitellmConfig"] = ((...args) => + loadFacadeModule()["applyLitellmConfig"](...args)) as FacadeModule["applyLitellmConfig"]; +export const applyLitellmProviderConfig: FacadeModule["applyLitellmProviderConfig"] = ((...args) => + loadFacadeModule()["applyLitellmProviderConfig"]( + ...args, + )) as FacadeModule["applyLitellmProviderConfig"]; +export const buildLitellmModelDefinition: FacadeModule["buildLitellmModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildLitellmModelDefinition"]( + ...args, + )) as FacadeModule["buildLitellmModelDefinition"]; +export const LITELLM_BASE_URL: FacadeModule["LITELLM_BASE_URL"] = + loadFacadeModule()["LITELLM_BASE_URL"]; +export const LITELLM_DEFAULT_MODEL_ID: FacadeModule["LITELLM_DEFAULT_MODEL_ID"] = + loadFacadeModule()["LITELLM_DEFAULT_MODEL_ID"]; +export const LITELLM_DEFAULT_MODEL_REF: FacadeModule["LITELLM_DEFAULT_MODEL_REF"] = + loadFacadeModule()["LITELLM_DEFAULT_MODEL_REF"]; diff --git a/src/plugin-sdk/llm-task.ts b/src/plugin-sdk/llm-task.ts index 5b7a7a0a9e5..01cd7e2342e 100644 --- a/src/plugin-sdk/llm-task.ts +++ b/src/plugin-sdk/llm-task.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled llm-task plugin. -// Keep this list additive and scoped to symbols used under extensions/llm-task. +// Keep this list additive and scoped to the bundled LLM task surface. export { definePluginEntry } from "./plugin-entry.js"; export { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js"; diff --git a/src/plugin-sdk/matrix-helper.ts b/src/plugin-sdk/matrix-helper.ts index 69a8ce60f48..7f2bdc7ff6c 100644 --- a/src/plugin-sdk/matrix-helper.ts +++ b/src/plugin-sdk/matrix-helper.ts @@ -1,13 +1,60 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - findMatrixAccountEntry, - getMatrixScopedEnvVarNames, - requiresExplicitMatrixDefaultAccount, - resolveConfiguredMatrixAccountIds, - resolveMatrixAccountStorageRoot, - resolveMatrixChannelConfig, - resolveMatrixCredentialsDir, - resolveMatrixCredentialsPath, - resolveMatrixDefaultOrOnlyAccountId, - resolveMatrixLegacyFlatStoragePaths, -} from "../../extensions/matrix/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["matrix-helper"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "matrix", + artifactBasename: "api.js", + }); +} +export const findMatrixAccountEntry: FacadeModule["findMatrixAccountEntry"] = ((...args) => + loadFacadeModule()["findMatrixAccountEntry"](...args)) as FacadeModule["findMatrixAccountEntry"]; +export const getMatrixScopedEnvVarNames: FacadeModule["getMatrixScopedEnvVarNames"] = ((...args) => + loadFacadeModule()["getMatrixScopedEnvVarNames"]( + ...args, + )) as FacadeModule["getMatrixScopedEnvVarNames"]; +export const requiresExplicitMatrixDefaultAccount: FacadeModule["requiresExplicitMatrixDefaultAccount"] = + ((...args) => + loadFacadeModule()["requiresExplicitMatrixDefaultAccount"]( + ...args, + )) as FacadeModule["requiresExplicitMatrixDefaultAccount"]; +export const resolveConfiguredMatrixAccountIds: FacadeModule["resolveConfiguredMatrixAccountIds"] = + ((...args) => + loadFacadeModule()["resolveConfiguredMatrixAccountIds"]( + ...args, + )) as FacadeModule["resolveConfiguredMatrixAccountIds"]; +export const resolveMatrixAccountStorageRoot: FacadeModule["resolveMatrixAccountStorageRoot"] = (( + ...args +) => + loadFacadeModule()["resolveMatrixAccountStorageRoot"]( + ...args, + )) as FacadeModule["resolveMatrixAccountStorageRoot"]; +export const resolveMatrixChannelConfig: FacadeModule["resolveMatrixChannelConfig"] = ((...args) => + loadFacadeModule()["resolveMatrixChannelConfig"]( + ...args, + )) as FacadeModule["resolveMatrixChannelConfig"]; +export const resolveMatrixCredentialsDir: FacadeModule["resolveMatrixCredentialsDir"] = (( + ...args +) => + loadFacadeModule()["resolveMatrixCredentialsDir"]( + ...args, + )) as FacadeModule["resolveMatrixCredentialsDir"]; +export const resolveMatrixCredentialsPath: FacadeModule["resolveMatrixCredentialsPath"] = (( + ...args +) => + loadFacadeModule()["resolveMatrixCredentialsPath"]( + ...args, + )) as FacadeModule["resolveMatrixCredentialsPath"]; +export const resolveMatrixDefaultOrOnlyAccountId: FacadeModule["resolveMatrixDefaultOrOnlyAccountId"] = + ((...args) => + loadFacadeModule()["resolveMatrixDefaultOrOnlyAccountId"]( + ...args, + )) as FacadeModule["resolveMatrixDefaultOrOnlyAccountId"]; +export const resolveMatrixLegacyFlatStoragePaths: FacadeModule["resolveMatrixLegacyFlatStoragePaths"] = + ((...args) => + loadFacadeModule()["resolveMatrixLegacyFlatStoragePaths"]( + ...args, + )) as FacadeModule["resolveMatrixLegacyFlatStoragePaths"]; diff --git a/src/plugin-sdk/matrix-runtime-surface.ts b/src/plugin-sdk/matrix-runtime-surface.ts index 811c6076112..86125c6a033 100644 --- a/src/plugin-sdk/matrix-runtime-surface.ts +++ b/src/plugin-sdk/matrix-runtime-surface.ts @@ -1,5 +1,20 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - resolveMatrixAccountStringValues, - setMatrixRuntime, -} from "../../extensions/matrix/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["matrix-runtime-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "matrix", + artifactBasename: "runtime-api.js", + }); +} +export const resolveMatrixAccountStringValues: FacadeModule["resolveMatrixAccountStringValues"] = (( + ...args +) => + loadFacadeModule()["resolveMatrixAccountStringValues"]( + ...args, + )) as FacadeModule["resolveMatrixAccountStringValues"]; +export const setMatrixRuntime: FacadeModule["setMatrixRuntime"] = ((...args) => + loadFacadeModule()["setMatrixRuntime"](...args)) as FacadeModule["setMatrixRuntime"]; diff --git a/src/plugin-sdk/matrix-surface.ts b/src/plugin-sdk/matrix-surface.ts index d63120398b3..9dfc1f86fdf 100644 --- a/src/plugin-sdk/matrix-surface.ts +++ b/src/plugin-sdk/matrix-surface.ts @@ -1,6 +1,31 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - createMatrixThreadBindingManager, - matrixSessionBindingAdapterChannels, - resetMatrixThreadBindingsForTests, -} from "../../extensions/matrix/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["matrix-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "matrix", + artifactBasename: "api.js", + }); +} +export const createMatrixThreadBindingManager: FacadeModule["createMatrixThreadBindingManager"] = (( + ...args +) => + loadFacadeModule()["createMatrixThreadBindingManager"]( + ...args, + )) as FacadeModule["createMatrixThreadBindingManager"]; +export const matrixSessionBindingAdapterChannels: FacadeModule["matrixSessionBindingAdapterChannels"] = + createLazyFacadeArrayValue( + () => + loadFacadeModule()["matrixSessionBindingAdapterChannels"] as unknown as readonly unknown[], + ) as FacadeModule["matrixSessionBindingAdapterChannels"]; +export const resetMatrixThreadBindingsForTests: FacadeModule["resetMatrixThreadBindingsForTests"] = + ((...args) => + loadFacadeModule()["resetMatrixThreadBindingsForTests"]( + ...args, + )) as FacadeModule["resetMatrixThreadBindingsForTests"]; diff --git a/src/plugin-sdk/matrix-thread-bindings.ts b/src/plugin-sdk/matrix-thread-bindings.ts index 4da938055d3..6930ab7aad0 100644 --- a/src/plugin-sdk/matrix-thread-bindings.ts +++ b/src/plugin-sdk/matrix-thread-bindings.ts @@ -1,5 +1,22 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - setMatrixThreadBindingIdleTimeoutBySessionKey, - setMatrixThreadBindingMaxAgeBySessionKey, -} from "../../extensions/matrix/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["matrix-thread-bindings"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "matrix", + artifactBasename: "api.js", + }); +} +export const setMatrixThreadBindingIdleTimeoutBySessionKey: FacadeModule["setMatrixThreadBindingIdleTimeoutBySessionKey"] = + ((...args) => + loadFacadeModule()["setMatrixThreadBindingIdleTimeoutBySessionKey"]( + ...args, + )) as FacadeModule["setMatrixThreadBindingIdleTimeoutBySessionKey"]; +export const setMatrixThreadBindingMaxAgeBySessionKey: FacadeModule["setMatrixThreadBindingMaxAgeBySessionKey"] = + ((...args) => + loadFacadeModule()["setMatrixThreadBindingMaxAgeBySessionKey"]( + ...args, + )) as FacadeModule["setMatrixThreadBindingMaxAgeBySessionKey"]; diff --git a/src/plugin-sdk/matrix.ts b/src/plugin-sdk/matrix.ts index cfdfc9f59d8..13031180647 100644 --- a/src/plugin-sdk/matrix.ts +++ b/src/plugin-sdk/matrix.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled matrix plugin. -// Keep this list additive and scoped to symbols used under extensions/matrix. +// Keep this list additive and scoped to the bundled Matrix surface. import { createOptionalChannelSetupSurface } from "./channel-setup.js"; diff --git a/src/plugin-sdk/mattermost-policy.ts b/src/plugin-sdk/mattermost-policy.ts index ccc71f12bb8..103c78b5d2b 100644 --- a/src/plugin-sdk/mattermost-policy.ts +++ b/src/plugin-sdk/mattermost-policy.ts @@ -1,2 +1,16 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { isMattermostSenderAllowed } from "../../extensions/mattermost/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["mattermost-policy"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "mattermost", + artifactBasename: "api.js", + }); +} +export const isMattermostSenderAllowed: FacadeModule["isMattermostSenderAllowed"] = ((...args) => + loadFacadeModule()["isMattermostSenderAllowed"]( + ...args, + )) as FacadeModule["isMattermostSenderAllowed"]; diff --git a/src/plugin-sdk/mattermost.ts b/src/plugin-sdk/mattermost.ts index 2dd1717a4a9..e9df483ad53 100644 --- a/src/plugin-sdk/mattermost.ts +++ b/src/plugin-sdk/mattermost.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled mattermost plugin. -// Keep this list additive and scoped to symbols used under extensions/mattermost. +// Keep this list additive and scoped to the bundled Mattermost surface. export { formatInboundFromLabel } from "../auto-reply/envelope.js"; export type { HistoryEntry } from "../auto-reply/reply/history.js"; diff --git a/src/plugin-sdk/media-understanding-runtime.ts b/src/plugin-sdk/media-understanding-runtime.ts index e40b9732046..3639a4f861e 100644 --- a/src/plugin-sdk/media-understanding-runtime.ts +++ b/src/plugin-sdk/media-understanding-runtime.ts @@ -1,2 +1,30 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export * from "../../extensions/media-understanding-core/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["media-understanding-runtime"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "media-understanding-core", + artifactBasename: "runtime-api.js", + }); +} +export const describeImageFile: FacadeModule["describeImageFile"] = ((...args) => + loadFacadeModule()["describeImageFile"](...args)) as FacadeModule["describeImageFile"]; +export const describeImageFileWithModel: FacadeModule["describeImageFileWithModel"] = ((...args) => + loadFacadeModule()["describeImageFileWithModel"]( + ...args, + )) as FacadeModule["describeImageFileWithModel"]; +export const describeVideoFile: FacadeModule["describeVideoFile"] = ((...args) => + loadFacadeModule()["describeVideoFile"](...args)) as FacadeModule["describeVideoFile"]; +export const runMediaUnderstandingFile: FacadeModule["runMediaUnderstandingFile"] = ((...args) => + loadFacadeModule()["runMediaUnderstandingFile"]( + ...args, + )) as FacadeModule["runMediaUnderstandingFile"]; +export const transcribeAudioFile: FacadeModule["transcribeAudioFile"] = ((...args) => + loadFacadeModule()["transcribeAudioFile"](...args)) as FacadeModule["transcribeAudioFile"]; +export type RunMediaUnderstandingFileParams = + FacadeEntry["types"]["RunMediaUnderstandingFileParams"]; +export type RunMediaUnderstandingFileResult = + FacadeEntry["types"]["RunMediaUnderstandingFileResult"]; diff --git a/src/plugin-sdk/memory-core-engine-runtime.ts b/src/plugin-sdk/memory-core-engine-runtime.ts index 325e6fa388d..ae7ee2f8295 100644 --- a/src/plugin-sdk/memory-core-engine-runtime.ts +++ b/src/plugin-sdk/memory-core-engine-runtime.ts @@ -1,2 +1,32 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export * from "../../extensions/memory-core/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["memory-core-engine-runtime"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "memory-core", + artifactBasename: "runtime-api.js", + }); +} +export const getBuiltinMemoryEmbeddingProviderDoctorMetadata: FacadeModule["getBuiltinMemoryEmbeddingProviderDoctorMetadata"] = + ((...args) => + loadFacadeModule()["getBuiltinMemoryEmbeddingProviderDoctorMetadata"]( + ...args, + )) as FacadeModule["getBuiltinMemoryEmbeddingProviderDoctorMetadata"]; +export const getMemorySearchManager: FacadeModule["getMemorySearchManager"] = ((...args) => + loadFacadeModule()["getMemorySearchManager"](...args)) as FacadeModule["getMemorySearchManager"]; +export const listBuiltinAutoSelectMemoryEmbeddingProviderDoctorMetadata: FacadeModule["listBuiltinAutoSelectMemoryEmbeddingProviderDoctorMetadata"] = + ((...args) => + loadFacadeModule()["listBuiltinAutoSelectMemoryEmbeddingProviderDoctorMetadata"]( + ...args, + )) as FacadeModule["listBuiltinAutoSelectMemoryEmbeddingProviderDoctorMetadata"]; +export const MemoryIndexManager: FacadeModule["MemoryIndexManager"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["MemoryIndexManager"] as object, +) as FacadeModule["MemoryIndexManager"]; +export type BuiltinMemoryEmbeddingProviderDoctorMetadata = + FacadeEntry["types"]["BuiltinMemoryEmbeddingProviderDoctorMetadata"]; diff --git a/src/plugin-sdk/memory-core.ts b/src/plugin-sdk/memory-core.ts index bbc17f8323f..038bbcf233f 100644 --- a/src/plugin-sdk/memory-core.ts +++ b/src/plugin-sdk/memory-core.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled memory-core plugin. -// Keep this list additive and scoped to symbols used under extensions/memory-core. +// Keep this list additive and scoped to the bundled memory-core surface. export { getMemorySearchManager, MemoryIndexManager } from "./memory-core-engine-runtime.js"; export { diff --git a/src/plugin-sdk/memory-lancedb.ts b/src/plugin-sdk/memory-lancedb.ts index 2f49fb9371f..c2617b42da5 100644 --- a/src/plugin-sdk/memory-lancedb.ts +++ b/src/plugin-sdk/memory-lancedb.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled memory-lancedb plugin. -// Keep this list additive and scoped to symbols used under extensions/memory-lancedb. +// Keep this list additive and scoped to the bundled memory-lancedb surface. export { definePluginEntry } from "./plugin-entry.js"; export { resolveStateDir } from "./state-paths.js"; diff --git a/src/plugin-sdk/minimax.ts b/src/plugin-sdk/minimax.ts index 2032a3fffcc..a50da86782e 100644 --- a/src/plugin-sdk/minimax.ts +++ b/src/plugin-sdk/minimax.ts @@ -1,17 +1,62 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyMinimaxApiConfig, - applyMinimaxApiConfigCn, - applyMinimaxApiProviderConfig, - applyMinimaxApiProviderConfigCn, - buildMinimaxPortalProvider, - buildMinimaxProvider, - isMiniMaxModernModelId, - MINIMAX_API_BASE_URL, - MINIMAX_CN_API_BASE_URL, - MINIMAX_DEFAULT_MODEL_ID, - MINIMAX_DEFAULT_MODEL_REF, - MINIMAX_TEXT_MODEL_CATALOG, - MINIMAX_TEXT_MODEL_ORDER, - MINIMAX_TEXT_MODEL_REFS, -} from "../../extensions/minimax/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["minimax"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "minimax", + artifactBasename: "api.js", + }); +} +export const applyMinimaxApiConfig: FacadeModule["applyMinimaxApiConfig"] = ((...args) => + loadFacadeModule()["applyMinimaxApiConfig"](...args)) as FacadeModule["applyMinimaxApiConfig"]; +export const applyMinimaxApiConfigCn: FacadeModule["applyMinimaxApiConfigCn"] = ((...args) => + loadFacadeModule()["applyMinimaxApiConfigCn"]( + ...args, + )) as FacadeModule["applyMinimaxApiConfigCn"]; +export const applyMinimaxApiProviderConfig: FacadeModule["applyMinimaxApiProviderConfig"] = (( + ...args +) => + loadFacadeModule()["applyMinimaxApiProviderConfig"]( + ...args, + )) as FacadeModule["applyMinimaxApiProviderConfig"]; +export const applyMinimaxApiProviderConfigCn: FacadeModule["applyMinimaxApiProviderConfigCn"] = (( + ...args +) => + loadFacadeModule()["applyMinimaxApiProviderConfigCn"]( + ...args, + )) as FacadeModule["applyMinimaxApiProviderConfigCn"]; +export const buildMinimaxPortalProvider: FacadeModule["buildMinimaxPortalProvider"] = ((...args) => + loadFacadeModule()["buildMinimaxPortalProvider"]( + ...args, + )) as FacadeModule["buildMinimaxPortalProvider"]; +export const buildMinimaxProvider: FacadeModule["buildMinimaxProvider"] = ((...args) => + loadFacadeModule()["buildMinimaxProvider"](...args)) as FacadeModule["buildMinimaxProvider"]; +export const isMiniMaxModernModelId: FacadeModule["isMiniMaxModernModelId"] = ((...args) => + loadFacadeModule()["isMiniMaxModernModelId"](...args)) as FacadeModule["isMiniMaxModernModelId"]; +export const MINIMAX_API_BASE_URL: FacadeModule["MINIMAX_API_BASE_URL"] = + loadFacadeModule()["MINIMAX_API_BASE_URL"]; +export const MINIMAX_CN_API_BASE_URL: FacadeModule["MINIMAX_CN_API_BASE_URL"] = + loadFacadeModule()["MINIMAX_CN_API_BASE_URL"]; +export const MINIMAX_DEFAULT_MODEL_ID: FacadeModule["MINIMAX_DEFAULT_MODEL_ID"] = + loadFacadeModule()["MINIMAX_DEFAULT_MODEL_ID"]; +export const MINIMAX_DEFAULT_MODEL_REF: FacadeModule["MINIMAX_DEFAULT_MODEL_REF"] = + loadFacadeModule()["MINIMAX_DEFAULT_MODEL_REF"]; +export const MINIMAX_TEXT_MODEL_CATALOG: FacadeModule["MINIMAX_TEXT_MODEL_CATALOG"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["MINIMAX_TEXT_MODEL_CATALOG"] as object, + ) as FacadeModule["MINIMAX_TEXT_MODEL_CATALOG"]; +export const MINIMAX_TEXT_MODEL_ORDER: FacadeModule["MINIMAX_TEXT_MODEL_ORDER"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["MINIMAX_TEXT_MODEL_ORDER"] as unknown as readonly unknown[], + ) as FacadeModule["MINIMAX_TEXT_MODEL_ORDER"]; +export const MINIMAX_TEXT_MODEL_REFS: FacadeModule["MINIMAX_TEXT_MODEL_REFS"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["MINIMAX_TEXT_MODEL_REFS"] as unknown as readonly unknown[], + ) as FacadeModule["MINIMAX_TEXT_MODEL_REFS"]; diff --git a/src/plugin-sdk/mistral.ts b/src/plugin-sdk/mistral.ts index 288ad9b4850..b9af9880dd9 100644 --- a/src/plugin-sdk/mistral.ts +++ b/src/plugin-sdk/mistral.ts @@ -1,10 +1,32 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyMistralConfig, - applyMistralProviderConfig, - buildMistralModelDefinition, - buildMistralProvider, - MISTRAL_BASE_URL, - MISTRAL_DEFAULT_MODEL_ID, - MISTRAL_DEFAULT_MODEL_REF, -} from "../../extensions/mistral/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["mistral"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "mistral", + artifactBasename: "api.js", + }); +} +export const applyMistralConfig: FacadeModule["applyMistralConfig"] = ((...args) => + loadFacadeModule()["applyMistralConfig"](...args)) as FacadeModule["applyMistralConfig"]; +export const applyMistralProviderConfig: FacadeModule["applyMistralProviderConfig"] = ((...args) => + loadFacadeModule()["applyMistralProviderConfig"]( + ...args, + )) as FacadeModule["applyMistralProviderConfig"]; +export const buildMistralModelDefinition: FacadeModule["buildMistralModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildMistralModelDefinition"]( + ...args, + )) as FacadeModule["buildMistralModelDefinition"]; +export const buildMistralProvider: FacadeModule["buildMistralProvider"] = ((...args) => + loadFacadeModule()["buildMistralProvider"](...args)) as FacadeModule["buildMistralProvider"]; +export const MISTRAL_BASE_URL: FacadeModule["MISTRAL_BASE_URL"] = + loadFacadeModule()["MISTRAL_BASE_URL"]; +export const MISTRAL_DEFAULT_MODEL_ID: FacadeModule["MISTRAL_DEFAULT_MODEL_ID"] = + loadFacadeModule()["MISTRAL_DEFAULT_MODEL_ID"]; +export const MISTRAL_DEFAULT_MODEL_REF: FacadeModule["MISTRAL_DEFAULT_MODEL_REF"] = + loadFacadeModule()["MISTRAL_DEFAULT_MODEL_REF"]; diff --git a/src/plugin-sdk/modelstudio-definitions.ts b/src/plugin-sdk/modelstudio-definitions.ts index e60af60dcf4..52955b235d0 100644 --- a/src/plugin-sdk/modelstudio-definitions.ts +++ b/src/plugin-sdk/modelstudio-definitions.ts @@ -1,12 +1,42 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildModelStudioDefaultModelDefinition, - buildModelStudioModelDefinition, - MODELSTUDIO_CN_BASE_URL, - MODELSTUDIO_DEFAULT_COST, - MODELSTUDIO_DEFAULT_MODEL_ID, - MODELSTUDIO_DEFAULT_MODEL_REF, - MODELSTUDIO_GLOBAL_BASE_URL, - MODELSTUDIO_STANDARD_CN_BASE_URL, - MODELSTUDIO_STANDARD_GLOBAL_BASE_URL, -} from "../../extensions/modelstudio/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["modelstudio-definitions"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "modelstudio", + artifactBasename: "api.js", + }); +} +export const buildModelStudioDefaultModelDefinition: FacadeModule["buildModelStudioDefaultModelDefinition"] = + ((...args) => + loadFacadeModule()["buildModelStudioDefaultModelDefinition"]( + ...args, + )) as FacadeModule["buildModelStudioDefaultModelDefinition"]; +export const buildModelStudioModelDefinition: FacadeModule["buildModelStudioModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildModelStudioModelDefinition"]( + ...args, + )) as FacadeModule["buildModelStudioModelDefinition"]; +export const MODELSTUDIO_CN_BASE_URL: FacadeModule["MODELSTUDIO_CN_BASE_URL"] = + loadFacadeModule()["MODELSTUDIO_CN_BASE_URL"]; +export const MODELSTUDIO_DEFAULT_COST: FacadeModule["MODELSTUDIO_DEFAULT_COST"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["MODELSTUDIO_DEFAULT_COST"] as object, + ) as FacadeModule["MODELSTUDIO_DEFAULT_COST"]; +export const MODELSTUDIO_DEFAULT_MODEL_ID: FacadeModule["MODELSTUDIO_DEFAULT_MODEL_ID"] = + loadFacadeModule()["MODELSTUDIO_DEFAULT_MODEL_ID"]; +export const MODELSTUDIO_DEFAULT_MODEL_REF: FacadeModule["MODELSTUDIO_DEFAULT_MODEL_REF"] = + loadFacadeModule()["MODELSTUDIO_DEFAULT_MODEL_REF"]; +export const MODELSTUDIO_GLOBAL_BASE_URL: FacadeModule["MODELSTUDIO_GLOBAL_BASE_URL"] = + loadFacadeModule()["MODELSTUDIO_GLOBAL_BASE_URL"]; +export const MODELSTUDIO_STANDARD_CN_BASE_URL: FacadeModule["MODELSTUDIO_STANDARD_CN_BASE_URL"] = + loadFacadeModule()["MODELSTUDIO_STANDARD_CN_BASE_URL"]; +export const MODELSTUDIO_STANDARD_GLOBAL_BASE_URL: FacadeModule["MODELSTUDIO_STANDARD_GLOBAL_BASE_URL"] = + loadFacadeModule()["MODELSTUDIO_STANDARD_GLOBAL_BASE_URL"]; diff --git a/src/plugin-sdk/modelstudio.ts b/src/plugin-sdk/modelstudio.ts index 15a47ce0897..7ea37e56339 100644 --- a/src/plugin-sdk/modelstudio.ts +++ b/src/plugin-sdk/modelstudio.ts @@ -1,17 +1,62 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyModelStudioNativeStreamingUsageCompat, - buildModelStudioDefaultModelDefinition, - buildModelStudioModelDefinition, - MODELSTUDIO_BASE_URL, - MODELSTUDIO_CN_BASE_URL, - MODELSTUDIO_DEFAULT_COST, - MODELSTUDIO_DEFAULT_MODEL_ID, - MODELSTUDIO_DEFAULT_MODEL_REF, - MODELSTUDIO_GLOBAL_BASE_URL, - MODELSTUDIO_STANDARD_CN_BASE_URL, - MODELSTUDIO_STANDARD_GLOBAL_BASE_URL, - MODELSTUDIO_MODEL_CATALOG, - isNativeModelStudioBaseUrl, - buildModelStudioProvider, -} from "../../extensions/modelstudio/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["modelstudio"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "modelstudio", + artifactBasename: "api.js", + }); +} +export const applyModelStudioNativeStreamingUsageCompat: FacadeModule["applyModelStudioNativeStreamingUsageCompat"] = + ((...args) => + loadFacadeModule()["applyModelStudioNativeStreamingUsageCompat"]( + ...args, + )) as FacadeModule["applyModelStudioNativeStreamingUsageCompat"]; +export const buildModelStudioDefaultModelDefinition: FacadeModule["buildModelStudioDefaultModelDefinition"] = + ((...args) => + loadFacadeModule()["buildModelStudioDefaultModelDefinition"]( + ...args, + )) as FacadeModule["buildModelStudioDefaultModelDefinition"]; +export const buildModelStudioModelDefinition: FacadeModule["buildModelStudioModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildModelStudioModelDefinition"]( + ...args, + )) as FacadeModule["buildModelStudioModelDefinition"]; +export const MODELSTUDIO_BASE_URL: FacadeModule["MODELSTUDIO_BASE_URL"] = + loadFacadeModule()["MODELSTUDIO_BASE_URL"]; +export const MODELSTUDIO_CN_BASE_URL: FacadeModule["MODELSTUDIO_CN_BASE_URL"] = + loadFacadeModule()["MODELSTUDIO_CN_BASE_URL"]; +export const MODELSTUDIO_DEFAULT_COST: FacadeModule["MODELSTUDIO_DEFAULT_COST"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["MODELSTUDIO_DEFAULT_COST"] as object, + ) as FacadeModule["MODELSTUDIO_DEFAULT_COST"]; +export const MODELSTUDIO_DEFAULT_MODEL_ID: FacadeModule["MODELSTUDIO_DEFAULT_MODEL_ID"] = + loadFacadeModule()["MODELSTUDIO_DEFAULT_MODEL_ID"]; +export const MODELSTUDIO_DEFAULT_MODEL_REF: FacadeModule["MODELSTUDIO_DEFAULT_MODEL_REF"] = + loadFacadeModule()["MODELSTUDIO_DEFAULT_MODEL_REF"]; +export const MODELSTUDIO_GLOBAL_BASE_URL: FacadeModule["MODELSTUDIO_GLOBAL_BASE_URL"] = + loadFacadeModule()["MODELSTUDIO_GLOBAL_BASE_URL"]; +export const MODELSTUDIO_STANDARD_CN_BASE_URL: FacadeModule["MODELSTUDIO_STANDARD_CN_BASE_URL"] = + loadFacadeModule()["MODELSTUDIO_STANDARD_CN_BASE_URL"]; +export const MODELSTUDIO_STANDARD_GLOBAL_BASE_URL: FacadeModule["MODELSTUDIO_STANDARD_GLOBAL_BASE_URL"] = + loadFacadeModule()["MODELSTUDIO_STANDARD_GLOBAL_BASE_URL"]; +export const MODELSTUDIO_MODEL_CATALOG: FacadeModule["MODELSTUDIO_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["MODELSTUDIO_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["MODELSTUDIO_MODEL_CATALOG"]; +export const isNativeModelStudioBaseUrl: FacadeModule["isNativeModelStudioBaseUrl"] = ((...args) => + loadFacadeModule()["isNativeModelStudioBaseUrl"]( + ...args, + )) as FacadeModule["isNativeModelStudioBaseUrl"]; +export const buildModelStudioProvider: FacadeModule["buildModelStudioProvider"] = ((...args) => + loadFacadeModule()["buildModelStudioProvider"]( + ...args, + )) as FacadeModule["buildModelStudioProvider"]; diff --git a/src/plugin-sdk/moonshot.ts b/src/plugin-sdk/moonshot.ts index 6794eb71376..7203d0e9d06 100644 --- a/src/plugin-sdk/moonshot.ts +++ b/src/plugin-sdk/moonshot.ts @@ -1,10 +1,31 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyMoonshotNativeStreamingUsageCompat, - buildMoonshotProvider, - isNativeMoonshotBaseUrl, - MOONSHOT_BASE_URL, - MOONSHOT_CN_BASE_URL, - MOONSHOT_DEFAULT_MODEL_ID, - MOONSHOT_DEFAULT_MODEL_REF, -} from "../../extensions/moonshot/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["moonshot"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "moonshot", + artifactBasename: "api.js", + }); +} +export const applyMoonshotNativeStreamingUsageCompat: FacadeModule["applyMoonshotNativeStreamingUsageCompat"] = + ((...args) => + loadFacadeModule()["applyMoonshotNativeStreamingUsageCompat"]( + ...args, + )) as FacadeModule["applyMoonshotNativeStreamingUsageCompat"]; +export const buildMoonshotProvider: FacadeModule["buildMoonshotProvider"] = ((...args) => + loadFacadeModule()["buildMoonshotProvider"](...args)) as FacadeModule["buildMoonshotProvider"]; +export const isNativeMoonshotBaseUrl: FacadeModule["isNativeMoonshotBaseUrl"] = ((...args) => + loadFacadeModule()["isNativeMoonshotBaseUrl"]( + ...args, + )) as FacadeModule["isNativeMoonshotBaseUrl"]; +export const MOONSHOT_BASE_URL: FacadeModule["MOONSHOT_BASE_URL"] = + loadFacadeModule()["MOONSHOT_BASE_URL"]; +export const MOONSHOT_CN_BASE_URL: FacadeModule["MOONSHOT_CN_BASE_URL"] = + loadFacadeModule()["MOONSHOT_CN_BASE_URL"]; +export const MOONSHOT_DEFAULT_MODEL_ID: FacadeModule["MOONSHOT_DEFAULT_MODEL_ID"] = + loadFacadeModule()["MOONSHOT_DEFAULT_MODEL_ID"]; +export const MOONSHOT_DEFAULT_MODEL_REF: FacadeModule["MOONSHOT_DEFAULT_MODEL_REF"] = + loadFacadeModule()["MOONSHOT_DEFAULT_MODEL_REF"]; diff --git a/src/plugin-sdk/msteams.ts b/src/plugin-sdk/msteams.ts index 441ee7e2ed8..043f4f378db 100644 --- a/src/plugin-sdk/msteams.ts +++ b/src/plugin-sdk/msteams.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled msteams plugin. -// Keep this list additive and scoped to symbols used under extensions/msteams. +// Keep this list additive and scoped to the bundled Teams surface. import { createOptionalChannelSetupSurface } from "./channel-setup.js"; diff --git a/src/plugin-sdk/nextcloud-talk.ts b/src/plugin-sdk/nextcloud-talk.ts index b189411aa7d..19fe60a9d4a 100644 --- a/src/plugin-sdk/nextcloud-talk.ts +++ b/src/plugin-sdk/nextcloud-talk.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled nextcloud-talk plugin. -// Keep this list additive and scoped to symbols used under extensions/nextcloud-talk. +// Keep this list additive and scoped to the bundled Nextcloud Talk surface. export { logInboundDrop } from "../channels/logging.js"; export { createAuthRateLimiter } from "../gateway/auth-rate-limit.js"; diff --git a/src/plugin-sdk/nostr.ts b/src/plugin-sdk/nostr.ts index 493304ed3d3..12378ca8a03 100644 --- a/src/plugin-sdk/nostr.ts +++ b/src/plugin-sdk/nostr.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled nostr plugin. -// Keep this list additive and scoped to symbols used under extensions/nostr. +// Keep this list additive and scoped to the bundled Nostr surface. import { createOptionalChannelSetupSurface } from "./channel-setup.js"; diff --git a/src/plugin-sdk/nvidia.ts b/src/plugin-sdk/nvidia.ts index 54117b97768..c939625d279 100644 --- a/src/plugin-sdk/nvidia.ts +++ b/src/plugin-sdk/nvidia.ts @@ -1,2 +1,14 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { buildNvidiaProvider } from "../../extensions/nvidia/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["nvidia"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "nvidia", + artifactBasename: "api.js", + }); +} +export const buildNvidiaProvider: FacadeModule["buildNvidiaProvider"] = ((...args) => + loadFacadeModule()["buildNvidiaProvider"](...args)) as FacadeModule["buildNvidiaProvider"]; diff --git a/src/plugin-sdk/ollama-surface.ts b/src/plugin-sdk/ollama-surface.ts index 21d4a689d67..bdaa55ec945 100644 --- a/src/plugin-sdk/ollama-surface.ts +++ b/src/plugin-sdk/ollama-surface.ts @@ -1,22 +1,63 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildOllamaModelDefinition, - buildOllamaProvider, - configureOllamaNonInteractive, - ensureOllamaModelPulled, - enrichOllamaModelsWithContext, - fetchOllamaModels, - OLLAMA_DEFAULT_BASE_URL, - OLLAMA_DEFAULT_CONTEXT_WINDOW, - OLLAMA_DEFAULT_COST, - OLLAMA_DEFAULT_MAX_TOKENS, - OLLAMA_DEFAULT_MODEL, - promptAndConfigureOllama, - queryOllamaContextWindow, - resolveOllamaApiBase, -} from "../../extensions/ollama/api.js"; -export type { - OllamaModelWithContext, - OllamaTagModel, - OllamaTagsResponse, -} from "../../extensions/ollama/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["ollama-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "ollama", + artifactBasename: "api.js", + }); +} +export const buildOllamaModelDefinition: FacadeModule["buildOllamaModelDefinition"] = ((...args) => + loadFacadeModule()["buildOllamaModelDefinition"]( + ...args, + )) as FacadeModule["buildOllamaModelDefinition"]; +export const buildOllamaProvider: FacadeModule["buildOllamaProvider"] = ((...args) => + loadFacadeModule()["buildOllamaProvider"](...args)) as FacadeModule["buildOllamaProvider"]; +export const configureOllamaNonInteractive: FacadeModule["configureOllamaNonInteractive"] = (( + ...args +) => + loadFacadeModule()["configureOllamaNonInteractive"]( + ...args, + )) as FacadeModule["configureOllamaNonInteractive"]; +export const ensureOllamaModelPulled: FacadeModule["ensureOllamaModelPulled"] = ((...args) => + loadFacadeModule()["ensureOllamaModelPulled"]( + ...args, + )) as FacadeModule["ensureOllamaModelPulled"]; +export const enrichOllamaModelsWithContext: FacadeModule["enrichOllamaModelsWithContext"] = (( + ...args +) => + loadFacadeModule()["enrichOllamaModelsWithContext"]( + ...args, + )) as FacadeModule["enrichOllamaModelsWithContext"]; +export const fetchOllamaModels: FacadeModule["fetchOllamaModels"] = ((...args) => + loadFacadeModule()["fetchOllamaModels"](...args)) as FacadeModule["fetchOllamaModels"]; +export const OLLAMA_DEFAULT_BASE_URL: FacadeModule["OLLAMA_DEFAULT_BASE_URL"] = + loadFacadeModule()["OLLAMA_DEFAULT_BASE_URL"]; +export const OLLAMA_DEFAULT_CONTEXT_WINDOW: FacadeModule["OLLAMA_DEFAULT_CONTEXT_WINDOW"] = + loadFacadeModule()["OLLAMA_DEFAULT_CONTEXT_WINDOW"]; +export const OLLAMA_DEFAULT_COST: FacadeModule["OLLAMA_DEFAULT_COST"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["OLLAMA_DEFAULT_COST"] as object, +) as FacadeModule["OLLAMA_DEFAULT_COST"]; +export const OLLAMA_DEFAULT_MAX_TOKENS: FacadeModule["OLLAMA_DEFAULT_MAX_TOKENS"] = + loadFacadeModule()["OLLAMA_DEFAULT_MAX_TOKENS"]; +export const OLLAMA_DEFAULT_MODEL: FacadeModule["OLLAMA_DEFAULT_MODEL"] = + loadFacadeModule()["OLLAMA_DEFAULT_MODEL"]; +export const promptAndConfigureOllama: FacadeModule["promptAndConfigureOllama"] = ((...args) => + loadFacadeModule()["promptAndConfigureOllama"]( + ...args, + )) as FacadeModule["promptAndConfigureOllama"]; +export const queryOllamaContextWindow: FacadeModule["queryOllamaContextWindow"] = ((...args) => + loadFacadeModule()["queryOllamaContextWindow"]( + ...args, + )) as FacadeModule["queryOllamaContextWindow"]; +export const resolveOllamaApiBase: FacadeModule["resolveOllamaApiBase"] = ((...args) => + loadFacadeModule()["resolveOllamaApiBase"](...args)) as FacadeModule["resolveOllamaApiBase"]; +export type OllamaModelWithContext = FacadeEntry["types"]["OllamaModelWithContext"]; +export type OllamaTagModel = FacadeEntry["types"]["OllamaTagModel"]; +export type OllamaTagsResponse = FacadeEntry["types"]["OllamaTagsResponse"]; diff --git a/src/plugin-sdk/ollama.ts b/src/plugin-sdk/ollama.ts index 97edea0a748..cb4bae38cc7 100644 --- a/src/plugin-sdk/ollama.ts +++ b/src/plugin-sdk/ollama.ts @@ -1,2 +1,72 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export * from "../../extensions/ollama/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["ollama"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "ollama", + artifactBasename: "runtime-api.js", + }); +} +export const buildAssistantMessage: FacadeModule["buildAssistantMessage"] = ((...args) => + loadFacadeModule()["buildAssistantMessage"](...args)) as FacadeModule["buildAssistantMessage"]; +export const buildOllamaChatRequest: FacadeModule["buildOllamaChatRequest"] = ((...args) => + loadFacadeModule()["buildOllamaChatRequest"](...args)) as FacadeModule["buildOllamaChatRequest"]; +export const convertToOllamaMessages: FacadeModule["convertToOllamaMessages"] = ((...args) => + loadFacadeModule()["convertToOllamaMessages"]( + ...args, + )) as FacadeModule["convertToOllamaMessages"]; +export const createOllamaEmbeddingProvider: FacadeModule["createOllamaEmbeddingProvider"] = (( + ...args +) => + loadFacadeModule()["createOllamaEmbeddingProvider"]( + ...args, + )) as FacadeModule["createOllamaEmbeddingProvider"]; +export const createConfiguredOllamaCompatNumCtxWrapper: FacadeModule["createConfiguredOllamaCompatNumCtxWrapper"] = + ((...args) => + loadFacadeModule()["createConfiguredOllamaCompatNumCtxWrapper"]( + ...args, + )) as FacadeModule["createConfiguredOllamaCompatNumCtxWrapper"]; +export const createConfiguredOllamaCompatStreamWrapper: FacadeModule["createConfiguredOllamaCompatStreamWrapper"] = + ((...args) => + loadFacadeModule()["createConfiguredOllamaCompatStreamWrapper"]( + ...args, + )) as FacadeModule["createConfiguredOllamaCompatStreamWrapper"]; +export const createConfiguredOllamaStreamFn: FacadeModule["createConfiguredOllamaStreamFn"] = (( + ...args +) => + loadFacadeModule()["createConfiguredOllamaStreamFn"]( + ...args, + )) as FacadeModule["createConfiguredOllamaStreamFn"]; +export const createOllamaStreamFn: FacadeModule["createOllamaStreamFn"] = ((...args) => + loadFacadeModule()["createOllamaStreamFn"](...args)) as FacadeModule["createOllamaStreamFn"]; +export const DEFAULT_OLLAMA_EMBEDDING_MODEL: FacadeModule["DEFAULT_OLLAMA_EMBEDDING_MODEL"] = + loadFacadeModule()["DEFAULT_OLLAMA_EMBEDDING_MODEL"]; +export const isOllamaCompatProvider: FacadeModule["isOllamaCompatProvider"] = ((...args) => + loadFacadeModule()["isOllamaCompatProvider"](...args)) as FacadeModule["isOllamaCompatProvider"]; +export const OLLAMA_NATIVE_BASE_URL: FacadeModule["OLLAMA_NATIVE_BASE_URL"] = + loadFacadeModule()["OLLAMA_NATIVE_BASE_URL"]; +export const parseNdjsonStream: FacadeModule["parseNdjsonStream"] = ((...args) => + loadFacadeModule()["parseNdjsonStream"](...args)) as FacadeModule["parseNdjsonStream"]; +export const resolveOllamaBaseUrlForRun: FacadeModule["resolveOllamaBaseUrlForRun"] = ((...args) => + loadFacadeModule()["resolveOllamaBaseUrlForRun"]( + ...args, + )) as FacadeModule["resolveOllamaBaseUrlForRun"]; +export const resolveOllamaCompatNumCtxEnabled: FacadeModule["resolveOllamaCompatNumCtxEnabled"] = (( + ...args +) => + loadFacadeModule()["resolveOllamaCompatNumCtxEnabled"]( + ...args, + )) as FacadeModule["resolveOllamaCompatNumCtxEnabled"]; +export const shouldInjectOllamaCompatNumCtx: FacadeModule["shouldInjectOllamaCompatNumCtx"] = (( + ...args +) => + loadFacadeModule()["shouldInjectOllamaCompatNumCtx"]( + ...args, + )) as FacadeModule["shouldInjectOllamaCompatNumCtx"]; +export const wrapOllamaCompatNumCtx: FacadeModule["wrapOllamaCompatNumCtx"] = ((...args) => + loadFacadeModule()["wrapOllamaCompatNumCtx"](...args)) as FacadeModule["wrapOllamaCompatNumCtx"]; +export type OllamaEmbeddingClient = FacadeEntry["types"]["OllamaEmbeddingClient"]; +export type OllamaEmbeddingProvider = FacadeEntry["types"]["OllamaEmbeddingProvider"]; diff --git a/src/plugin-sdk/open-prose.ts b/src/plugin-sdk/open-prose.ts index c361b702120..210291b9094 100644 --- a/src/plugin-sdk/open-prose.ts +++ b/src/plugin-sdk/open-prose.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled open-prose plugin. -// Keep this list additive and scoped to symbols used under extensions/open-prose. +// Keep this list additive and scoped to the bundled open-prose surface. export { definePluginEntry } from "./plugin-entry.js"; export type { OpenClawPluginApi } from "../plugins/types.js"; diff --git a/src/plugin-sdk/openai.ts b/src/plugin-sdk/openai.ts index afb9393ebf4..c1be9b05d7d 100644 --- a/src/plugin-sdk/openai.ts +++ b/src/plugin-sdk/openai.ts @@ -1,14 +1,38 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyOpenAIConfig, - applyOpenAIProviderConfig, - buildOpenAICodexProvider, - buildOpenAIProvider, - OPENAI_CODEX_DEFAULT_MODEL, - OPENAI_DEFAULT_AUDIO_TRANSCRIPTION_MODEL, - OPENAI_DEFAULT_EMBEDDING_MODEL, - OPENAI_DEFAULT_IMAGE_MODEL, - OPENAI_DEFAULT_MODEL, - OPENAI_DEFAULT_TTS_MODEL, - OPENAI_DEFAULT_TTS_VOICE, -} from "../../extensions/openai/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["openai"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "openai", + artifactBasename: "api.js", + }); +} +export const applyOpenAIConfig: FacadeModule["applyOpenAIConfig"] = ((...args) => + loadFacadeModule()["applyOpenAIConfig"](...args)) as FacadeModule["applyOpenAIConfig"]; +export const applyOpenAIProviderConfig: FacadeModule["applyOpenAIProviderConfig"] = ((...args) => + loadFacadeModule()["applyOpenAIProviderConfig"]( + ...args, + )) as FacadeModule["applyOpenAIProviderConfig"]; +export const buildOpenAICodexProvider: FacadeModule["buildOpenAICodexProvider"] = ((...args) => + loadFacadeModule()["buildOpenAICodexProvider"]( + ...args, + )) as FacadeModule["buildOpenAICodexProvider"]; +export const buildOpenAIProvider: FacadeModule["buildOpenAIProvider"] = ((...args) => + loadFacadeModule()["buildOpenAIProvider"](...args)) as FacadeModule["buildOpenAIProvider"]; +export const OPENAI_CODEX_DEFAULT_MODEL: FacadeModule["OPENAI_CODEX_DEFAULT_MODEL"] = + loadFacadeModule()["OPENAI_CODEX_DEFAULT_MODEL"]; +export const OPENAI_DEFAULT_AUDIO_TRANSCRIPTION_MODEL: FacadeModule["OPENAI_DEFAULT_AUDIO_TRANSCRIPTION_MODEL"] = + loadFacadeModule()["OPENAI_DEFAULT_AUDIO_TRANSCRIPTION_MODEL"]; +export const OPENAI_DEFAULT_EMBEDDING_MODEL: FacadeModule["OPENAI_DEFAULT_EMBEDDING_MODEL"] = + loadFacadeModule()["OPENAI_DEFAULT_EMBEDDING_MODEL"]; +export const OPENAI_DEFAULT_IMAGE_MODEL: FacadeModule["OPENAI_DEFAULT_IMAGE_MODEL"] = + loadFacadeModule()["OPENAI_DEFAULT_IMAGE_MODEL"]; +export const OPENAI_DEFAULT_MODEL: FacadeModule["OPENAI_DEFAULT_MODEL"] = + loadFacadeModule()["OPENAI_DEFAULT_MODEL"]; +export const OPENAI_DEFAULT_TTS_MODEL: FacadeModule["OPENAI_DEFAULT_TTS_MODEL"] = + loadFacadeModule()["OPENAI_DEFAULT_TTS_MODEL"]; +export const OPENAI_DEFAULT_TTS_VOICE: FacadeModule["OPENAI_DEFAULT_TTS_VOICE"] = + loadFacadeModule()["OPENAI_DEFAULT_TTS_VOICE"]; diff --git a/src/plugin-sdk/opencode-go.ts b/src/plugin-sdk/opencode-go.ts index a3cb895a858..328388a08bd 100644 --- a/src/plugin-sdk/opencode-go.ts +++ b/src/plugin-sdk/opencode-go.ts @@ -1,7 +1,28 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyOpencodeGoConfig, - applyOpencodeGoModelDefault, - applyOpencodeGoProviderConfig, - OPENCODE_GO_DEFAULT_MODEL_REF, -} from "../../extensions/opencode-go/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["opencode-go"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "opencode-go", + artifactBasename: "api.js", + }); +} +export const applyOpencodeGoConfig: FacadeModule["applyOpencodeGoConfig"] = ((...args) => + loadFacadeModule()["applyOpencodeGoConfig"](...args)) as FacadeModule["applyOpencodeGoConfig"]; +export const applyOpencodeGoModelDefault: FacadeModule["applyOpencodeGoModelDefault"] = (( + ...args +) => + loadFacadeModule()["applyOpencodeGoModelDefault"]( + ...args, + )) as FacadeModule["applyOpencodeGoModelDefault"]; +export const applyOpencodeGoProviderConfig: FacadeModule["applyOpencodeGoProviderConfig"] = (( + ...args +) => + loadFacadeModule()["applyOpencodeGoProviderConfig"]( + ...args, + )) as FacadeModule["applyOpencodeGoProviderConfig"]; +export const OPENCODE_GO_DEFAULT_MODEL_REF: FacadeModule["OPENCODE_GO_DEFAULT_MODEL_REF"] = + loadFacadeModule()["OPENCODE_GO_DEFAULT_MODEL_REF"]; diff --git a/src/plugin-sdk/opencode.ts b/src/plugin-sdk/opencode.ts index 20942e9f86f..670ad621c02 100644 --- a/src/plugin-sdk/opencode.ts +++ b/src/plugin-sdk/opencode.ts @@ -1,8 +1,30 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyOpencodeZenConfig, - applyOpencodeZenModelDefault, - applyOpencodeZenProviderConfig, - OPENCODE_ZEN_DEFAULT_MODEL, - OPENCODE_ZEN_DEFAULT_MODEL_REF, -} from "../../extensions/opencode/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["opencode"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "opencode", + artifactBasename: "api.js", + }); +} +export const applyOpencodeZenConfig: FacadeModule["applyOpencodeZenConfig"] = ((...args) => + loadFacadeModule()["applyOpencodeZenConfig"](...args)) as FacadeModule["applyOpencodeZenConfig"]; +export const applyOpencodeZenModelDefault: FacadeModule["applyOpencodeZenModelDefault"] = (( + ...args +) => + loadFacadeModule()["applyOpencodeZenModelDefault"]( + ...args, + )) as FacadeModule["applyOpencodeZenModelDefault"]; +export const applyOpencodeZenProviderConfig: FacadeModule["applyOpencodeZenProviderConfig"] = (( + ...args +) => + loadFacadeModule()["applyOpencodeZenProviderConfig"]( + ...args, + )) as FacadeModule["applyOpencodeZenProviderConfig"]; +export const OPENCODE_ZEN_DEFAULT_MODEL: FacadeModule["OPENCODE_ZEN_DEFAULT_MODEL"] = + loadFacadeModule()["OPENCODE_ZEN_DEFAULT_MODEL"]; +export const OPENCODE_ZEN_DEFAULT_MODEL_REF: FacadeModule["OPENCODE_ZEN_DEFAULT_MODEL_REF"] = + loadFacadeModule()["OPENCODE_ZEN_DEFAULT_MODEL_REF"]; diff --git a/src/plugin-sdk/openrouter.ts b/src/plugin-sdk/openrouter.ts index 4263bd833b8..63cf74fb8f0 100644 --- a/src/plugin-sdk/openrouter.ts +++ b/src/plugin-sdk/openrouter.ts @@ -1,7 +1,26 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyOpenrouterConfig, - applyOpenrouterProviderConfig, - buildOpenrouterProvider, - OPENROUTER_DEFAULT_MODEL_REF, -} from "../../extensions/openrouter/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["openrouter"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "openrouter", + artifactBasename: "api.js", + }); +} +export const applyOpenrouterConfig: FacadeModule["applyOpenrouterConfig"] = ((...args) => + loadFacadeModule()["applyOpenrouterConfig"](...args)) as FacadeModule["applyOpenrouterConfig"]; +export const applyOpenrouterProviderConfig: FacadeModule["applyOpenrouterProviderConfig"] = (( + ...args +) => + loadFacadeModule()["applyOpenrouterProviderConfig"]( + ...args, + )) as FacadeModule["applyOpenrouterProviderConfig"]; +export const buildOpenrouterProvider: FacadeModule["buildOpenrouterProvider"] = ((...args) => + loadFacadeModule()["buildOpenrouterProvider"]( + ...args, + )) as FacadeModule["buildOpenrouterProvider"]; +export const OPENROUTER_DEFAULT_MODEL_REF: FacadeModule["OPENROUTER_DEFAULT_MODEL_REF"] = + loadFacadeModule()["OPENROUTER_DEFAULT_MODEL_REF"]; diff --git a/src/plugin-sdk/package-contract-guardrails.test.ts b/src/plugin-sdk/package-contract-guardrails.test.ts index e390e1bbf62..d4d0f6cc568 100644 --- a/src/plugin-sdk/package-contract-guardrails.test.ts +++ b/src/plugin-sdk/package-contract-guardrails.test.ts @@ -56,6 +56,13 @@ function readRootPackageJson(): { }; } +function readGeneratedFacadeTypeMap(): string { + return readFileSync( + resolve(REPO_ROOT, "src/generated/plugin-sdk-facade-type-map.generated.ts"), + "utf8", + ); +} + describe("plugin-sdk package contract guardrails", () => { it("keeps package.json exports aligned with built plugin-sdk entrypoints", () => { expect(collectPluginSdkPackageExports()).toEqual([...pluginSdkEntrypoints].toSorted()); @@ -91,4 +98,8 @@ describe("plugin-sdk package contract guardrails", () => { expect(dependencies["matrix-js-sdk"]).toBe("41.2.0"); expect(optionalDependencies["@matrix-org/matrix-sdk-crypto-nodejs"]).toBe("^0.4.0"); }); + + it("keeps generated facade types on package-valid module specifiers", () => { + expect(readGeneratedFacadeTypeMap()).not.toContain("openclaw/plugin-source/"); + }); }); diff --git a/src/plugin-sdk/phone-control.ts b/src/plugin-sdk/phone-control.ts index 27a7124af32..d3139fb4102 100644 --- a/src/plugin-sdk/phone-control.ts +++ b/src/plugin-sdk/phone-control.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled phone-control plugin. -// Keep this list additive and scoped to symbols used under extensions/phone-control. +// Keep this list additive and scoped to the bundled phone-control surface. export { definePluginEntry } from "./plugin-entry.js"; export type { diff --git a/src/plugin-sdk/plugin-entry-guardrails.test.ts b/src/plugin-sdk/plugin-entry-guardrails.test.ts index 036c362a791..bb7d7ee3128 100644 --- a/src/plugin-sdk/plugin-entry-guardrails.test.ts +++ b/src/plugin-sdk/plugin-entry-guardrails.test.ts @@ -2,10 +2,14 @@ import { readdirSync, readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; +import { + BUNDLED_PLUGIN_ROOT_DIR, + bundledPluginFile, +} from "../../test/helpers/bundled-plugin-paths.js"; const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const REPO_ROOT = resolve(ROOT_DIR, ".."); -const EXTENSIONS_DIR = resolve(REPO_ROOT, "extensions"); +const EXTENSIONS_DIR = resolve(REPO_ROOT, BUNDLED_PLUGIN_ROOT_DIR); const CORE_PLUGIN_ENTRY_IMPORT_RE = /import\s*\{[^}]*\bdefinePluginEntry\b[^}]*\}\s*from\s*"openclaw\/plugin-sdk\/core"/; @@ -21,7 +25,7 @@ describe("plugin entry guardrails", () => { try { const source = readFileSync(indexPath, "utf8"); if (CORE_PLUGIN_ENTRY_IMPORT_RE.test(source)) { - failures.push(`extensions/${entry.name}/index.ts`); + failures.push(bundledPluginFile(entry.name, "index.ts")); } } catch { // Skip extensions without index.ts entry modules. diff --git a/src/plugin-sdk/provider-reasoning.ts b/src/plugin-sdk/provider-reasoning.ts index d7ab5ae4499..d2c9dcb0634 100644 --- a/src/plugin-sdk/provider-reasoning.ts +++ b/src/plugin-sdk/provider-reasoning.ts @@ -1,2 +1,16 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { isReasoningModelHeuristic } from "../../extensions/ollama/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["provider-reasoning"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "ollama", + artifactBasename: "api.js", + }); +} +export const isReasoningModelHeuristic: FacadeModule["isReasoningModelHeuristic"] = ((...args) => + loadFacadeModule()["isReasoningModelHeuristic"]( + ...args, + )) as FacadeModule["isReasoningModelHeuristic"]; diff --git a/src/plugin-sdk/qianfan.ts b/src/plugin-sdk/qianfan.ts index 7af95b4b618..30b4b4636e4 100644 --- a/src/plugin-sdk/qianfan.ts +++ b/src/plugin-sdk/qianfan.ts @@ -1,6 +1,18 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - QIANFAN_BASE_URL, - QIANFAN_DEFAULT_MODEL_ID, - buildQianfanProvider, -} from "../../extensions/qianfan/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["qianfan"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "qianfan", + artifactBasename: "api.js", + }); +} +export const QIANFAN_BASE_URL: FacadeModule["QIANFAN_BASE_URL"] = + loadFacadeModule()["QIANFAN_BASE_URL"]; +export const QIANFAN_DEFAULT_MODEL_ID: FacadeModule["QIANFAN_DEFAULT_MODEL_ID"] = + loadFacadeModule()["QIANFAN_DEFAULT_MODEL_ID"]; +export const buildQianfanProvider: FacadeModule["buildQianfanProvider"] = ((...args) => + loadFacadeModule()["buildQianfanProvider"](...args)) as FacadeModule["buildQianfanProvider"]; diff --git a/src/plugin-sdk/runtime-api-guardrails.test.ts b/src/plugin-sdk/runtime-api-guardrails.test.ts index a293ffacb9c..d87aec8fefa 100644 --- a/src/plugin-sdk/runtime-api-guardrails.test.ts +++ b/src/plugin-sdk/runtime-api-guardrails.test.ts @@ -3,11 +3,12 @@ import { dirname, relative, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import ts from "typescript"; import { describe, expect, it } from "vitest"; +import { bundledPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const RUNTIME_API_EXPORT_GUARDS: Record = { - "extensions/discord/runtime-api.ts": [ + [bundledPluginFile("discord", "runtime-api.ts")]: [ 'export * from "./src/audit.js";', 'export * from "./src/actions/runtime.js";', 'export * from "./src/actions/runtime.moderation-shared.js";', @@ -27,7 +28,7 @@ const RUNTIME_API_EXPORT_GUARDS: Record = { 'export * from "./src/outbound-session-route.js";', 'export * from "./src/send.js";', ], - "extensions/imessage/runtime-api.ts": [ + [bundledPluginFile("imessage", "runtime-api.ts")]: [ 'export { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE, buildComputedAccountStatusSnapshot, buildChannelConfigSchema, chunkTextForOutbound, collectStatusIssuesFromLastError, formatTrimmedAllowFromEntries, getChatChannelMeta, looksLikeIMessageTargetId, normalizeIMessageMessagingTarget, resolveChannelMediaMaxBytes, resolveIMessageConfigAllowFrom, resolveIMessageConfigDefaultTo, IMessageConfigSchema, type ChannelPlugin, type IMessageAccountConfig } from "openclaw/plugin-sdk/imessage";', 'export { resolveIMessageGroupRequireMention, resolveIMessageGroupToolPolicy } from "./src/group-policy.js";', 'export { monitorIMessageProvider } from "./src/monitor.js";', @@ -36,8 +37,10 @@ const RUNTIME_API_EXPORT_GUARDS: Record = { 'export type { IMessageProbe } from "./src/probe.js";', 'export { sendMessageIMessage } from "./src/send.js";', ], - "extensions/googlechat/runtime-api.ts": ['export * from "openclaw/plugin-sdk/googlechat";'], - "extensions/matrix/runtime-api.ts": [ + [bundledPluginFile("googlechat", "runtime-api.ts")]: [ + 'export * from "openclaw/plugin-sdk/googlechat";', + ], + [bundledPluginFile("matrix", "runtime-api.ts")]: [ 'export * from "./src/auth-precedence.js";', 'export { requiresExplicitMatrixDefaultAccount, resolveMatrixDefaultOrOnlyAccountId } from "./src/account-selection.js";', 'export * from "./src/account-selection.js";', @@ -51,18 +54,18 @@ const RUNTIME_API_EXPORT_GUARDS: Record = { 'export { formatZonedTimestamp } from "openclaw/plugin-sdk/matrix-runtime-shared";', 'export function chunkTextForOutbound(text: string, limit: number): string[] { const chunks: string[] = []; let remaining = text; while (remaining.length > limit) { const window = remaining.slice(0, limit); const splitAt = Math.max(window.lastIndexOf("\\n"), window.lastIndexOf(" ")); const breakAt = splitAt > 0 ? splitAt : limit; chunks.push(remaining.slice(0, breakAt).trimEnd()); remaining = remaining.slice(breakAt).trimStart(); } if (remaining.length > 0 || text.length === 0) { chunks.push(remaining); } return chunks; }', ], - "extensions/nextcloud-talk/runtime-api.ts": [ + [bundledPluginFile("nextcloud-talk", "runtime-api.ts")]: [ 'export * from "openclaw/plugin-sdk/nextcloud-talk";', ], - "extensions/signal/runtime-api.ts": ['export * from "./src/runtime-api.js";'], - "extensions/slack/runtime-api.ts": [ + [bundledPluginFile("signal", "runtime-api.ts")]: ['export * from "./src/runtime-api.js";'], + [bundledPluginFile("slack", "runtime-api.ts")]: [ 'export * from "./src/action-runtime.js";', 'export * from "./src/directory-live.js";', 'export * from "./src/index.js";', 'export * from "./src/resolve-channels.js";', 'export * from "./src/resolve-users.js";', ], - "extensions/telegram/runtime-api.ts": [ + [bundledPluginFile("telegram", "runtime-api.ts")]: [ 'export type { ChannelMessageActionAdapter, ChannelPlugin, OpenClawConfig, OpenClawPluginApi, PluginRuntime, TelegramAccountConfig, TelegramActionConfig, TelegramNetworkConfig } from "openclaw/plugin-sdk/telegram-core";', 'export type { TelegramApiOverride } from "./src/send.js";', 'export type { OpenClawPluginService, OpenClawPluginServiceContext, PluginLogger } from "openclaw/plugin-sdk/core";', @@ -83,7 +86,7 @@ const RUNTIME_API_EXPORT_GUARDS: Record = { 'export { createTelegramThreadBindingManager, getTelegramThreadBindingManager, resetTelegramThreadBindingsForTests, setTelegramThreadBindingIdleTimeoutBySessionKey, setTelegramThreadBindingMaxAgeBySessionKey } from "./src/thread-bindings.js";', 'export { resolveTelegramToken } from "./src/token.js";', ], - "extensions/whatsapp/runtime-api.ts": [ + [bundledPluginFile("whatsapp", "runtime-api.ts")]: [ 'export * from "./src/active-listener.js";', 'export * from "./src/action-runtime.js";', 'export * from "./src/agent-tools-login.js";', diff --git a/src/plugin-sdk/sglang.ts b/src/plugin-sdk/sglang.ts index 53ec7fa2c99..da984b794cc 100644 --- a/src/plugin-sdk/sglang.ts +++ b/src/plugin-sdk/sglang.ts @@ -1,8 +1,22 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildSglangProvider, - SGLANG_DEFAULT_API_KEY_ENV_VAR, - SGLANG_DEFAULT_BASE_URL, - SGLANG_MODEL_PLACEHOLDER, - SGLANG_PROVIDER_LABEL, -} from "../../extensions/sglang/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["sglang"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "sglang", + artifactBasename: "api.js", + }); +} +export const buildSglangProvider: FacadeModule["buildSglangProvider"] = ((...args) => + loadFacadeModule()["buildSglangProvider"](...args)) as FacadeModule["buildSglangProvider"]; +export const SGLANG_DEFAULT_API_KEY_ENV_VAR: FacadeModule["SGLANG_DEFAULT_API_KEY_ENV_VAR"] = + loadFacadeModule()["SGLANG_DEFAULT_API_KEY_ENV_VAR"]; +export const SGLANG_DEFAULT_BASE_URL: FacadeModule["SGLANG_DEFAULT_BASE_URL"] = + loadFacadeModule()["SGLANG_DEFAULT_BASE_URL"]; +export const SGLANG_MODEL_PLACEHOLDER: FacadeModule["SGLANG_MODEL_PLACEHOLDER"] = + loadFacadeModule()["SGLANG_MODEL_PLACEHOLDER"]; +export const SGLANG_PROVIDER_LABEL: FacadeModule["SGLANG_PROVIDER_LABEL"] = + loadFacadeModule()["SGLANG_PROVIDER_LABEL"]; diff --git a/src/plugin-sdk/signal-account.ts b/src/plugin-sdk/signal-account.ts index 14b5b88bb43..2f5ec234e56 100644 --- a/src/plugin-sdk/signal-account.ts +++ b/src/plugin-sdk/signal-account.ts @@ -1,3 +1,15 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { resolveSignalAccount } from "../../extensions/signal/api.js"; -export type { ResolvedSignalAccount } from "../../extensions/signal/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["signal-account"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "signal", + artifactBasename: "api.js", + }); +} +export const resolveSignalAccount: FacadeModule["resolveSignalAccount"] = ((...args) => + loadFacadeModule()["resolveSignalAccount"](...args)) as FacadeModule["resolveSignalAccount"]; +export type ResolvedSignalAccount = FacadeEntry["types"]["ResolvedSignalAccount"]; diff --git a/src/plugin-sdk/signal-core.ts b/src/plugin-sdk/signal-core.ts index d7e5277d1ab..c7b72e5e9fa 100644 --- a/src/plugin-sdk/signal-core.ts +++ b/src/plugin-sdk/signal-core.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled signal plugin. -// Keep this list additive and scoped to symbols used under extensions/signal. +// Keep this list additive and scoped to the bundled Signal surface. export type { SignalAccountConfig } from "../config/types.js"; export type { ChannelPlugin } from "./channel-plugin-common.js"; diff --git a/src/plugin-sdk/signal-surface.ts b/src/plugin-sdk/signal-surface.ts index cdf3d65f107..ebe27e06299 100644 --- a/src/plugin-sdk/signal-surface.ts +++ b/src/plugin-sdk/signal-surface.ts @@ -1,19 +1,50 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - isSignalSenderAllowed, - listEnabledSignalAccounts, - listSignalAccountIds, - monitorSignalProvider, - probeSignal, - removeReactionSignal, - resolveDefaultSignalAccountId, - resolveSignalReactionLevel, - sendMessageSignal, - sendReactionSignal, - signalMessageActions, -} from "../../extensions/signal/api.js"; -export type { - ResolvedSignalAccount, - SignalProbe, - SignalSender, -} from "../../extensions/signal/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["signal-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "signal", + artifactBasename: "api.js", + }); +} +export const isSignalSenderAllowed: FacadeModule["isSignalSenderAllowed"] = ((...args) => + loadFacadeModule()["isSignalSenderAllowed"](...args)) as FacadeModule["isSignalSenderAllowed"]; +export const listEnabledSignalAccounts: FacadeModule["listEnabledSignalAccounts"] = ((...args) => + loadFacadeModule()["listEnabledSignalAccounts"]( + ...args, + )) as FacadeModule["listEnabledSignalAccounts"]; +export const listSignalAccountIds: FacadeModule["listSignalAccountIds"] = ((...args) => + loadFacadeModule()["listSignalAccountIds"](...args)) as FacadeModule["listSignalAccountIds"]; +export const monitorSignalProvider: FacadeModule["monitorSignalProvider"] = ((...args) => + loadFacadeModule()["monitorSignalProvider"](...args)) as FacadeModule["monitorSignalProvider"]; +export const probeSignal: FacadeModule["probeSignal"] = ((...args) => + loadFacadeModule()["probeSignal"](...args)) as FacadeModule["probeSignal"]; +export const removeReactionSignal: FacadeModule["removeReactionSignal"] = ((...args) => + loadFacadeModule()["removeReactionSignal"](...args)) as FacadeModule["removeReactionSignal"]; +export const resolveDefaultSignalAccountId: FacadeModule["resolveDefaultSignalAccountId"] = (( + ...args +) => + loadFacadeModule()["resolveDefaultSignalAccountId"]( + ...args, + )) as FacadeModule["resolveDefaultSignalAccountId"]; +export const resolveSignalReactionLevel: FacadeModule["resolveSignalReactionLevel"] = ((...args) => + loadFacadeModule()["resolveSignalReactionLevel"]( + ...args, + )) as FacadeModule["resolveSignalReactionLevel"]; +export const sendMessageSignal: FacadeModule["sendMessageSignal"] = ((...args) => + loadFacadeModule()["sendMessageSignal"](...args)) as FacadeModule["sendMessageSignal"]; +export const sendReactionSignal: FacadeModule["sendReactionSignal"] = ((...args) => + loadFacadeModule()["sendReactionSignal"](...args)) as FacadeModule["sendReactionSignal"]; +export const signalMessageActions: FacadeModule["signalMessageActions"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["signalMessageActions"] as object, + ) as FacadeModule["signalMessageActions"]; +export type ResolvedSignalAccount = FacadeEntry["types"]["ResolvedSignalAccount"]; +export type SignalProbe = FacadeEntry["types"]["SignalProbe"]; +export type SignalSender = FacadeEntry["types"]["SignalSender"]; diff --git a/src/plugin-sdk/signal.ts b/src/plugin-sdk/signal.ts index ea62016d5f4..87508d8e382 100644 --- a/src/plugin-sdk/signal.ts +++ b/src/plugin-sdk/signal.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled signal plugin. -// Keep this list additive and scoped to symbols used under extensions/signal. +// Keep this list additive and scoped to the bundled Signal surface. export type { ChannelMessageActionAdapter } from "../channels/plugins/types.js"; export type { OpenClawConfig } from "../config/config.js"; diff --git a/src/plugin-sdk/slack-account.ts b/src/plugin-sdk/slack-account.ts index fefe6786b03..87b2beed6f4 100644 --- a/src/plugin-sdk/slack-account.ts +++ b/src/plugin-sdk/slack-account.ts @@ -1,3 +1,15 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { resolveSlackAccount } from "../../extensions/slack/api.js"; -export type { ResolvedSlackAccount } from "../../extensions/slack/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["slack-account"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "slack", + artifactBasename: "api.js", + }); +} +export const resolveSlackAccount: FacadeModule["resolveSlackAccount"] = ((...args) => + loadFacadeModule()["resolveSlackAccount"](...args)) as FacadeModule["resolveSlackAccount"]; +export type ResolvedSlackAccount = FacadeEntry["types"]["ResolvedSlackAccount"]; diff --git a/src/plugin-sdk/slack-runtime-surface.ts b/src/plugin-sdk/slack-runtime-surface.ts index 1a9589bc4a9..e14b175c0bf 100644 --- a/src/plugin-sdk/slack-runtime-surface.ts +++ b/src/plugin-sdk/slack-runtime-surface.ts @@ -1,12 +1,43 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - handleSlackAction, - listSlackDirectoryGroupsLive, - listSlackDirectoryPeersLive, - monitorSlackProvider, - probeSlack, - resolveSlackChannelAllowlist, - resolveSlackUserAllowlist, - sendMessageSlack, -} from "../../extensions/slack/runtime-api.js"; -export type { SlackActionContext } from "../../extensions/slack/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["slack-runtime-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "slack", + artifactBasename: "runtime-api.js", + }); +} +export const handleSlackAction: FacadeModule["handleSlackAction"] = ((...args) => + loadFacadeModule()["handleSlackAction"](...args)) as FacadeModule["handleSlackAction"]; +export const listSlackDirectoryGroupsLive: FacadeModule["listSlackDirectoryGroupsLive"] = (( + ...args +) => + loadFacadeModule()["listSlackDirectoryGroupsLive"]( + ...args, + )) as FacadeModule["listSlackDirectoryGroupsLive"]; +export const listSlackDirectoryPeersLive: FacadeModule["listSlackDirectoryPeersLive"] = (( + ...args +) => + loadFacadeModule()["listSlackDirectoryPeersLive"]( + ...args, + )) as FacadeModule["listSlackDirectoryPeersLive"]; +export const monitorSlackProvider: FacadeModule["monitorSlackProvider"] = ((...args) => + loadFacadeModule()["monitorSlackProvider"](...args)) as FacadeModule["monitorSlackProvider"]; +export const probeSlack: FacadeModule["probeSlack"] = ((...args) => + loadFacadeModule()["probeSlack"](...args)) as FacadeModule["probeSlack"]; +export const resolveSlackChannelAllowlist: FacadeModule["resolveSlackChannelAllowlist"] = (( + ...args +) => + loadFacadeModule()["resolveSlackChannelAllowlist"]( + ...args, + )) as FacadeModule["resolveSlackChannelAllowlist"]; +export const resolveSlackUserAllowlist: FacadeModule["resolveSlackUserAllowlist"] = ((...args) => + loadFacadeModule()["resolveSlackUserAllowlist"]( + ...args, + )) as FacadeModule["resolveSlackUserAllowlist"]; +export const sendMessageSlack: FacadeModule["sendMessageSlack"] = ((...args) => + loadFacadeModule()["sendMessageSlack"](...args)) as FacadeModule["sendMessageSlack"]; +export type SlackActionContext = FacadeEntry["types"]["SlackActionContext"]; diff --git a/src/plugin-sdk/slack-surface.ts b/src/plugin-sdk/slack-surface.ts index 82ba31a3db2..caea30c06e2 100644 --- a/src/plugin-sdk/slack-surface.ts +++ b/src/plugin-sdk/slack-surface.ts @@ -1,42 +1,129 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildSlackThreadingToolContext, - createSlackWebClient, - deleteSlackMessage, - downloadSlackFile, - editSlackMessage, - extractSlackToolSend, - getSlackMemberInfo, - handleSlackHttpRequest, - inspectSlackAccount, - isSlackInteractiveRepliesEnabled, - listEnabledSlackAccounts, - listSlackAccountIds, - listSlackDirectoryGroupsFromConfig, - listSlackDirectoryPeersFromConfig, - listSlackEmojis, - listSlackMessageActions, - listSlackPins, - listSlackReactions, - normalizeAllowListLower, - parseSlackBlocksInput, - recordSlackThreadParticipation, - resolveDefaultSlackAccountId, - resolveSlackAutoThreadId, - resolveSlackGroupRequireMention, - resolveSlackRuntimeGroupPolicy, - resolveSlackGroupToolPolicy, - resolveSlackReplyToMode, - sendSlackMessage, - pinSlackMessage, - reactSlackMessage, - readSlackMessages, - removeOwnSlackReactions, - removeSlackReaction, - unpinSlackMessage, -} from "../../extensions/slack/api.js"; -export type { - InspectedSlackAccount, - ResolvedSlackAccount, - SlackProbe, -} from "../../extensions/slack/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["slack-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "slack", + artifactBasename: "api.js", + }); +} +export const buildSlackThreadingToolContext: FacadeModule["buildSlackThreadingToolContext"] = (( + ...args +) => + loadFacadeModule()["buildSlackThreadingToolContext"]( + ...args, + )) as FacadeModule["buildSlackThreadingToolContext"]; +export const createSlackWebClient: FacadeModule["createSlackWebClient"] = ((...args) => + loadFacadeModule()["createSlackWebClient"](...args)) as FacadeModule["createSlackWebClient"]; +export const deleteSlackMessage: FacadeModule["deleteSlackMessage"] = ((...args) => + loadFacadeModule()["deleteSlackMessage"](...args)) as FacadeModule["deleteSlackMessage"]; +export const downloadSlackFile: FacadeModule["downloadSlackFile"] = ((...args) => + loadFacadeModule()["downloadSlackFile"](...args)) as FacadeModule["downloadSlackFile"]; +export const editSlackMessage: FacadeModule["editSlackMessage"] = ((...args) => + loadFacadeModule()["editSlackMessage"](...args)) as FacadeModule["editSlackMessage"]; +export const extractSlackToolSend: FacadeModule["extractSlackToolSend"] = ((...args) => + loadFacadeModule()["extractSlackToolSend"](...args)) as FacadeModule["extractSlackToolSend"]; +export const getSlackMemberInfo: FacadeModule["getSlackMemberInfo"] = ((...args) => + loadFacadeModule()["getSlackMemberInfo"](...args)) as FacadeModule["getSlackMemberInfo"]; +export const handleSlackHttpRequest: FacadeModule["handleSlackHttpRequest"] = ((...args) => + loadFacadeModule()["handleSlackHttpRequest"](...args)) as FacadeModule["handleSlackHttpRequest"]; +export const inspectSlackAccount: FacadeModule["inspectSlackAccount"] = ((...args) => + loadFacadeModule()["inspectSlackAccount"](...args)) as FacadeModule["inspectSlackAccount"]; +export const isSlackInteractiveRepliesEnabled: FacadeModule["isSlackInteractiveRepliesEnabled"] = (( + ...args +) => + loadFacadeModule()["isSlackInteractiveRepliesEnabled"]( + ...args, + )) as FacadeModule["isSlackInteractiveRepliesEnabled"]; +export const listEnabledSlackAccounts: FacadeModule["listEnabledSlackAccounts"] = ((...args) => + loadFacadeModule()["listEnabledSlackAccounts"]( + ...args, + )) as FacadeModule["listEnabledSlackAccounts"]; +export const listSlackAccountIds: FacadeModule["listSlackAccountIds"] = ((...args) => + loadFacadeModule()["listSlackAccountIds"](...args)) as FacadeModule["listSlackAccountIds"]; +export const listSlackDirectoryGroupsFromConfig: FacadeModule["listSlackDirectoryGroupsFromConfig"] = + ((...args) => + loadFacadeModule()["listSlackDirectoryGroupsFromConfig"]( + ...args, + )) as FacadeModule["listSlackDirectoryGroupsFromConfig"]; +export const listSlackDirectoryPeersFromConfig: FacadeModule["listSlackDirectoryPeersFromConfig"] = + ((...args) => + loadFacadeModule()["listSlackDirectoryPeersFromConfig"]( + ...args, + )) as FacadeModule["listSlackDirectoryPeersFromConfig"]; +export const listSlackEmojis: FacadeModule["listSlackEmojis"] = ((...args) => + loadFacadeModule()["listSlackEmojis"](...args)) as FacadeModule["listSlackEmojis"]; +export const listSlackMessageActions: FacadeModule["listSlackMessageActions"] = ((...args) => + loadFacadeModule()["listSlackMessageActions"]( + ...args, + )) as FacadeModule["listSlackMessageActions"]; +export const listSlackPins: FacadeModule["listSlackPins"] = ((...args) => + loadFacadeModule()["listSlackPins"](...args)) as FacadeModule["listSlackPins"]; +export const listSlackReactions: FacadeModule["listSlackReactions"] = ((...args) => + loadFacadeModule()["listSlackReactions"](...args)) as FacadeModule["listSlackReactions"]; +export const normalizeAllowListLower: FacadeModule["normalizeAllowListLower"] = ((...args) => + loadFacadeModule()["normalizeAllowListLower"]( + ...args, + )) as FacadeModule["normalizeAllowListLower"]; +export const parseSlackBlocksInput: FacadeModule["parseSlackBlocksInput"] = ((...args) => + loadFacadeModule()["parseSlackBlocksInput"](...args)) as FacadeModule["parseSlackBlocksInput"]; +export const recordSlackThreadParticipation: FacadeModule["recordSlackThreadParticipation"] = (( + ...args +) => + loadFacadeModule()["recordSlackThreadParticipation"]( + ...args, + )) as FacadeModule["recordSlackThreadParticipation"]; +export const resolveDefaultSlackAccountId: FacadeModule["resolveDefaultSlackAccountId"] = (( + ...args +) => + loadFacadeModule()["resolveDefaultSlackAccountId"]( + ...args, + )) as FacadeModule["resolveDefaultSlackAccountId"]; +export const resolveSlackAutoThreadId: FacadeModule["resolveSlackAutoThreadId"] = ((...args) => + loadFacadeModule()["resolveSlackAutoThreadId"]( + ...args, + )) as FacadeModule["resolveSlackAutoThreadId"]; +export const resolveSlackGroupRequireMention: FacadeModule["resolveSlackGroupRequireMention"] = (( + ...args +) => + loadFacadeModule()["resolveSlackGroupRequireMention"]( + ...args, + )) as FacadeModule["resolveSlackGroupRequireMention"]; +export const resolveSlackRuntimeGroupPolicy: FacadeModule["resolveSlackRuntimeGroupPolicy"] = (( + ...args +) => + loadFacadeModule()["resolveSlackRuntimeGroupPolicy"]( + ...args, + )) as FacadeModule["resolveSlackRuntimeGroupPolicy"]; +export const resolveSlackGroupToolPolicy: FacadeModule["resolveSlackGroupToolPolicy"] = (( + ...args +) => + loadFacadeModule()["resolveSlackGroupToolPolicy"]( + ...args, + )) as FacadeModule["resolveSlackGroupToolPolicy"]; +export const resolveSlackReplyToMode: FacadeModule["resolveSlackReplyToMode"] = ((...args) => + loadFacadeModule()["resolveSlackReplyToMode"]( + ...args, + )) as FacadeModule["resolveSlackReplyToMode"]; +export const sendSlackMessage: FacadeModule["sendSlackMessage"] = ((...args) => + loadFacadeModule()["sendSlackMessage"](...args)) as FacadeModule["sendSlackMessage"]; +export const pinSlackMessage: FacadeModule["pinSlackMessage"] = ((...args) => + loadFacadeModule()["pinSlackMessage"](...args)) as FacadeModule["pinSlackMessage"]; +export const reactSlackMessage: FacadeModule["reactSlackMessage"] = ((...args) => + loadFacadeModule()["reactSlackMessage"](...args)) as FacadeModule["reactSlackMessage"]; +export const readSlackMessages: FacadeModule["readSlackMessages"] = ((...args) => + loadFacadeModule()["readSlackMessages"](...args)) as FacadeModule["readSlackMessages"]; +export const removeOwnSlackReactions: FacadeModule["removeOwnSlackReactions"] = ((...args) => + loadFacadeModule()["removeOwnSlackReactions"]( + ...args, + )) as FacadeModule["removeOwnSlackReactions"]; +export const removeSlackReaction: FacadeModule["removeSlackReaction"] = ((...args) => + loadFacadeModule()["removeSlackReaction"](...args)) as FacadeModule["removeSlackReaction"]; +export const unpinSlackMessage: FacadeModule["unpinSlackMessage"] = ((...args) => + loadFacadeModule()["unpinSlackMessage"](...args)) as FacadeModule["unpinSlackMessage"]; +export type InspectedSlackAccount = FacadeEntry["types"]["InspectedSlackAccount"]; +export type ResolvedSlackAccount = FacadeEntry["types"]["ResolvedSlackAccount"]; +export type SlackProbe = FacadeEntry["types"]["SlackProbe"]; diff --git a/src/plugin-sdk/slack-target-parser.ts b/src/plugin-sdk/slack-target-parser.ts index 092da9bf621..00eea01bad3 100644 --- a/src/plugin-sdk/slack-target-parser.ts +++ b/src/plugin-sdk/slack-target-parser.ts @@ -1,2 +1,16 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { parseSlackTarget, resolveSlackChannelId } from "../../extensions/slack/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["slack-target-parser"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "slack", + artifactBasename: "api.js", + }); +} +export const parseSlackTarget: FacadeModule["parseSlackTarget"] = ((...args) => + loadFacadeModule()["parseSlackTarget"](...args)) as FacadeModule["parseSlackTarget"]; +export const resolveSlackChannelId: FacadeModule["resolveSlackChannelId"] = ((...args) => + loadFacadeModule()["resolveSlackChannelId"](...args)) as FacadeModule["resolveSlackChannelId"]; diff --git a/src/plugin-sdk/speech-runtime.ts b/src/plugin-sdk/speech-runtime.ts index 102c0330f00..8b8c1c55e44 100644 --- a/src/plugin-sdk/speech-runtime.ts +++ b/src/plugin-sdk/speech-runtime.ts @@ -1,2 +1,83 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export * from "../../extensions/speech-core/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["speech-runtime"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "speech-core", + artifactBasename: "runtime-api.js", + }); +} +export const _test: FacadeModule["_test"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["_test"] as object, +) as FacadeModule["_test"]; +export const buildTtsSystemPromptHint: FacadeModule["buildTtsSystemPromptHint"] = ((...args) => + loadFacadeModule()["buildTtsSystemPromptHint"]( + ...args, + )) as FacadeModule["buildTtsSystemPromptHint"]; +export const getLastTtsAttempt: FacadeModule["getLastTtsAttempt"] = ((...args) => + loadFacadeModule()["getLastTtsAttempt"](...args)) as FacadeModule["getLastTtsAttempt"]; +export const getResolvedSpeechProviderConfig: FacadeModule["getResolvedSpeechProviderConfig"] = (( + ...args +) => + loadFacadeModule()["getResolvedSpeechProviderConfig"]( + ...args, + )) as FacadeModule["getResolvedSpeechProviderConfig"]; +export const getTtsMaxLength: FacadeModule["getTtsMaxLength"] = ((...args) => + loadFacadeModule()["getTtsMaxLength"](...args)) as FacadeModule["getTtsMaxLength"]; +export const getTtsProvider: FacadeModule["getTtsProvider"] = ((...args) => + loadFacadeModule()["getTtsProvider"](...args)) as FacadeModule["getTtsProvider"]; +export const isSummarizationEnabled: FacadeModule["isSummarizationEnabled"] = ((...args) => + loadFacadeModule()["isSummarizationEnabled"](...args)) as FacadeModule["isSummarizationEnabled"]; +export const isTtsEnabled: FacadeModule["isTtsEnabled"] = ((...args) => + loadFacadeModule()["isTtsEnabled"](...args)) as FacadeModule["isTtsEnabled"]; +export const isTtsProviderConfigured: FacadeModule["isTtsProviderConfigured"] = ((...args) => + loadFacadeModule()["isTtsProviderConfigured"]( + ...args, + )) as FacadeModule["isTtsProviderConfigured"]; +export const listSpeechVoices: FacadeModule["listSpeechVoices"] = ((...args) => + loadFacadeModule()["listSpeechVoices"](...args)) as FacadeModule["listSpeechVoices"]; +export const maybeApplyTtsToPayload: FacadeModule["maybeApplyTtsToPayload"] = ((...args) => + loadFacadeModule()["maybeApplyTtsToPayload"](...args)) as FacadeModule["maybeApplyTtsToPayload"]; +export const resolveTtsAutoMode: FacadeModule["resolveTtsAutoMode"] = ((...args) => + loadFacadeModule()["resolveTtsAutoMode"](...args)) as FacadeModule["resolveTtsAutoMode"]; +export const resolveTtsConfig: FacadeModule["resolveTtsConfig"] = ((...args) => + loadFacadeModule()["resolveTtsConfig"](...args)) as FacadeModule["resolveTtsConfig"]; +export const resolveTtsPrefsPath: FacadeModule["resolveTtsPrefsPath"] = ((...args) => + loadFacadeModule()["resolveTtsPrefsPath"](...args)) as FacadeModule["resolveTtsPrefsPath"]; +export const resolveTtsProviderOrder: FacadeModule["resolveTtsProviderOrder"] = ((...args) => + loadFacadeModule()["resolveTtsProviderOrder"]( + ...args, + )) as FacadeModule["resolveTtsProviderOrder"]; +export const setLastTtsAttempt: FacadeModule["setLastTtsAttempt"] = ((...args) => + loadFacadeModule()["setLastTtsAttempt"](...args)) as FacadeModule["setLastTtsAttempt"]; +export const setSummarizationEnabled: FacadeModule["setSummarizationEnabled"] = ((...args) => + loadFacadeModule()["setSummarizationEnabled"]( + ...args, + )) as FacadeModule["setSummarizationEnabled"]; +export const setTtsAutoMode: FacadeModule["setTtsAutoMode"] = ((...args) => + loadFacadeModule()["setTtsAutoMode"](...args)) as FacadeModule["setTtsAutoMode"]; +export const setTtsEnabled: FacadeModule["setTtsEnabled"] = ((...args) => + loadFacadeModule()["setTtsEnabled"](...args)) as FacadeModule["setTtsEnabled"]; +export const setTtsMaxLength: FacadeModule["setTtsMaxLength"] = ((...args) => + loadFacadeModule()["setTtsMaxLength"](...args)) as FacadeModule["setTtsMaxLength"]; +export const setTtsProvider: FacadeModule["setTtsProvider"] = ((...args) => + loadFacadeModule()["setTtsProvider"](...args)) as FacadeModule["setTtsProvider"]; +export const synthesizeSpeech: FacadeModule["synthesizeSpeech"] = ((...args) => + loadFacadeModule()["synthesizeSpeech"](...args)) as FacadeModule["synthesizeSpeech"]; +export const textToSpeech: FacadeModule["textToSpeech"] = ((...args) => + loadFacadeModule()["textToSpeech"](...args)) as FacadeModule["textToSpeech"]; +export const textToSpeechTelephony: FacadeModule["textToSpeechTelephony"] = ((...args) => + loadFacadeModule()["textToSpeechTelephony"](...args)) as FacadeModule["textToSpeechTelephony"]; +export type ResolvedTtsConfig = FacadeEntry["types"]["ResolvedTtsConfig"]; +export type ResolvedTtsModelOverrides = FacadeEntry["types"]["ResolvedTtsModelOverrides"]; +export type TtsDirectiveOverrides = FacadeEntry["types"]["TtsDirectiveOverrides"]; +export type TtsDirectiveParseResult = FacadeEntry["types"]["TtsDirectiveParseResult"]; +export type TtsResult = FacadeEntry["types"]["TtsResult"]; +export type TtsSynthesisResult = FacadeEntry["types"]["TtsSynthesisResult"]; +export type TtsTelephonyResult = FacadeEntry["types"]["TtsTelephonyResult"]; diff --git a/src/plugin-sdk/synthetic.ts b/src/plugin-sdk/synthetic.ts index 84c902ca9b5..e0ea717eb71 100644 --- a/src/plugin-sdk/synthetic.ts +++ b/src/plugin-sdk/synthetic.ts @@ -1,10 +1,39 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applySyntheticConfig, - applySyntheticProviderConfig, - buildSyntheticModelDefinition, - buildSyntheticProvider, - SYNTHETIC_BASE_URL, - SYNTHETIC_DEFAULT_MODEL_REF, - SYNTHETIC_MODEL_CATALOG, -} from "../../extensions/synthetic/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["synthetic"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "synthetic", + artifactBasename: "api.js", + }); +} +export const applySyntheticConfig: FacadeModule["applySyntheticConfig"] = ((...args) => + loadFacadeModule()["applySyntheticConfig"](...args)) as FacadeModule["applySyntheticConfig"]; +export const applySyntheticProviderConfig: FacadeModule["applySyntheticProviderConfig"] = (( + ...args +) => + loadFacadeModule()["applySyntheticProviderConfig"]( + ...args, + )) as FacadeModule["applySyntheticProviderConfig"]; +export const buildSyntheticModelDefinition: FacadeModule["buildSyntheticModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildSyntheticModelDefinition"]( + ...args, + )) as FacadeModule["buildSyntheticModelDefinition"]; +export const buildSyntheticProvider: FacadeModule["buildSyntheticProvider"] = ((...args) => + loadFacadeModule()["buildSyntheticProvider"](...args)) as FacadeModule["buildSyntheticProvider"]; +export const SYNTHETIC_BASE_URL: FacadeModule["SYNTHETIC_BASE_URL"] = + loadFacadeModule()["SYNTHETIC_BASE_URL"]; +export const SYNTHETIC_DEFAULT_MODEL_REF: FacadeModule["SYNTHETIC_DEFAULT_MODEL_REF"] = + loadFacadeModule()["SYNTHETIC_DEFAULT_MODEL_REF"]; +export const SYNTHETIC_MODEL_CATALOG: FacadeModule["SYNTHETIC_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["SYNTHETIC_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["SYNTHETIC_MODEL_CATALOG"]; diff --git a/src/plugin-sdk/talk-voice.ts b/src/plugin-sdk/talk-voice.ts index 10f4096da03..56943e6abd9 100644 --- a/src/plugin-sdk/talk-voice.ts +++ b/src/plugin-sdk/talk-voice.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled talk-voice plugin. -// Keep this list additive and scoped to symbols used under extensions/talk-voice. +// Keep this list additive and scoped to the bundled talk-voice surface. export { definePluginEntry } from "./plugin-entry.js"; export type { OpenClawPluginApi } from "../plugins/types.js"; diff --git a/src/plugin-sdk/telegram-account.ts b/src/plugin-sdk/telegram-account.ts index f6ceb2e8fab..6592d139819 100644 --- a/src/plugin-sdk/telegram-account.ts +++ b/src/plugin-sdk/telegram-account.ts @@ -1,3 +1,15 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { resolveTelegramAccount } from "../../extensions/telegram/api.js"; -export type { ResolvedTelegramAccount } from "../../extensions/telegram/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["telegram-account"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "telegram", + artifactBasename: "api.js", + }); +} +export const resolveTelegramAccount: FacadeModule["resolveTelegramAccount"] = ((...args) => + loadFacadeModule()["resolveTelegramAccount"](...args)) as FacadeModule["resolveTelegramAccount"]; +export type ResolvedTelegramAccount = FacadeEntry["types"]["ResolvedTelegramAccount"]; diff --git a/src/plugin-sdk/telegram-allow-from.ts b/src/plugin-sdk/telegram-allow-from.ts index d0671d1a97a..615f70c78b9 100644 --- a/src/plugin-sdk/telegram-allow-from.ts +++ b/src/plugin-sdk/telegram-allow-from.ts @@ -1,5 +1,22 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - isNumericTelegramUserId, - normalizeTelegramAllowFromEntry, -} from "../../extensions/telegram/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["telegram-allow-from"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "telegram", + artifactBasename: "api.js", + }); +} +export const isNumericTelegramUserId: FacadeModule["isNumericTelegramUserId"] = ((...args) => + loadFacadeModule()["isNumericTelegramUserId"]( + ...args, + )) as FacadeModule["isNumericTelegramUserId"]; +export const normalizeTelegramAllowFromEntry: FacadeModule["normalizeTelegramAllowFromEntry"] = (( + ...args +) => + loadFacadeModule()["normalizeTelegramAllowFromEntry"]( + ...args, + )) as FacadeModule["normalizeTelegramAllowFromEntry"]; diff --git a/src/plugin-sdk/telegram-runtime-surface.ts b/src/plugin-sdk/telegram-runtime-surface.ts index 8fe0e31d45c..69af061ce34 100644 --- a/src/plugin-sdk/telegram-runtime-surface.ts +++ b/src/plugin-sdk/telegram-runtime-surface.ts @@ -1,30 +1,109 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - auditTelegramGroupMembership, - buildTelegramExecApprovalPendingPayload, - collectTelegramUnmentionedGroupIds, - createTelegramThreadBindingManager, - createForumTopicTelegram, - deleteMessageTelegram, - editForumTopicTelegram, - editMessageReplyMarkupTelegram, - editMessageTelegram, - monitorTelegramProvider, - pinMessageTelegram, - probeTelegram, - reactMessageTelegram, - renameForumTopicTelegram, - resetTelegramThreadBindingsForTests, - resolveTelegramRuntimeGroupPolicy, - resolveTelegramToken, - sendMessageTelegram, - sendPollTelegram, - sendStickerTelegram, - sendTypingTelegram, - setTelegramThreadBindingIdleTimeoutBySessionKey, - setTelegramThreadBindingMaxAgeBySessionKey, - shouldSuppressTelegramExecApprovalForwardingFallback, - telegramMessageActions, - unpinMessageTelegram, -} from "../../extensions/telegram/runtime-api.js"; -export type { TelegramApiOverride, TelegramProbe } from "../../extensions/telegram/runtime-api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["telegram-runtime-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "telegram", + artifactBasename: "runtime-api.js", + }); +} +export const auditTelegramGroupMembership: FacadeModule["auditTelegramGroupMembership"] = (( + ...args +) => + loadFacadeModule()["auditTelegramGroupMembership"]( + ...args, + )) as FacadeModule["auditTelegramGroupMembership"]; +export const buildTelegramExecApprovalPendingPayload: FacadeModule["buildTelegramExecApprovalPendingPayload"] = + ((...args) => + loadFacadeModule()["buildTelegramExecApprovalPendingPayload"]( + ...args, + )) as FacadeModule["buildTelegramExecApprovalPendingPayload"]; +export const collectTelegramUnmentionedGroupIds: FacadeModule["collectTelegramUnmentionedGroupIds"] = + ((...args) => + loadFacadeModule()["collectTelegramUnmentionedGroupIds"]( + ...args, + )) as FacadeModule["collectTelegramUnmentionedGroupIds"]; +export const createTelegramThreadBindingManager: FacadeModule["createTelegramThreadBindingManager"] = + ((...args) => + loadFacadeModule()["createTelegramThreadBindingManager"]( + ...args, + )) as FacadeModule["createTelegramThreadBindingManager"]; +export const createForumTopicTelegram: FacadeModule["createForumTopicTelegram"] = ((...args) => + loadFacadeModule()["createForumTopicTelegram"]( + ...args, + )) as FacadeModule["createForumTopicTelegram"]; +export const deleteMessageTelegram: FacadeModule["deleteMessageTelegram"] = ((...args) => + loadFacadeModule()["deleteMessageTelegram"](...args)) as FacadeModule["deleteMessageTelegram"]; +export const editForumTopicTelegram: FacadeModule["editForumTopicTelegram"] = ((...args) => + loadFacadeModule()["editForumTopicTelegram"](...args)) as FacadeModule["editForumTopicTelegram"]; +export const editMessageReplyMarkupTelegram: FacadeModule["editMessageReplyMarkupTelegram"] = (( + ...args +) => + loadFacadeModule()["editMessageReplyMarkupTelegram"]( + ...args, + )) as FacadeModule["editMessageReplyMarkupTelegram"]; +export const editMessageTelegram: FacadeModule["editMessageTelegram"] = ((...args) => + loadFacadeModule()["editMessageTelegram"](...args)) as FacadeModule["editMessageTelegram"]; +export const monitorTelegramProvider: FacadeModule["monitorTelegramProvider"] = ((...args) => + loadFacadeModule()["monitorTelegramProvider"]( + ...args, + )) as FacadeModule["monitorTelegramProvider"]; +export const pinMessageTelegram: FacadeModule["pinMessageTelegram"] = ((...args) => + loadFacadeModule()["pinMessageTelegram"](...args)) as FacadeModule["pinMessageTelegram"]; +export const probeTelegram: FacadeModule["probeTelegram"] = ((...args) => + loadFacadeModule()["probeTelegram"](...args)) as FacadeModule["probeTelegram"]; +export const reactMessageTelegram: FacadeModule["reactMessageTelegram"] = ((...args) => + loadFacadeModule()["reactMessageTelegram"](...args)) as FacadeModule["reactMessageTelegram"]; +export const renameForumTopicTelegram: FacadeModule["renameForumTopicTelegram"] = ((...args) => + loadFacadeModule()["renameForumTopicTelegram"]( + ...args, + )) as FacadeModule["renameForumTopicTelegram"]; +export const resetTelegramThreadBindingsForTests: FacadeModule["resetTelegramThreadBindingsForTests"] = + ((...args) => + loadFacadeModule()["resetTelegramThreadBindingsForTests"]( + ...args, + )) as FacadeModule["resetTelegramThreadBindingsForTests"]; +export const resolveTelegramRuntimeGroupPolicy: FacadeModule["resolveTelegramRuntimeGroupPolicy"] = + ((...args) => + loadFacadeModule()["resolveTelegramRuntimeGroupPolicy"]( + ...args, + )) as FacadeModule["resolveTelegramRuntimeGroupPolicy"]; +export const resolveTelegramToken: FacadeModule["resolveTelegramToken"] = ((...args) => + loadFacadeModule()["resolveTelegramToken"](...args)) as FacadeModule["resolveTelegramToken"]; +export const sendMessageTelegram: FacadeModule["sendMessageTelegram"] = ((...args) => + loadFacadeModule()["sendMessageTelegram"](...args)) as FacadeModule["sendMessageTelegram"]; +export const sendPollTelegram: FacadeModule["sendPollTelegram"] = ((...args) => + loadFacadeModule()["sendPollTelegram"](...args)) as FacadeModule["sendPollTelegram"]; +export const sendStickerTelegram: FacadeModule["sendStickerTelegram"] = ((...args) => + loadFacadeModule()["sendStickerTelegram"](...args)) as FacadeModule["sendStickerTelegram"]; +export const sendTypingTelegram: FacadeModule["sendTypingTelegram"] = ((...args) => + loadFacadeModule()["sendTypingTelegram"](...args)) as FacadeModule["sendTypingTelegram"]; +export const setTelegramThreadBindingIdleTimeoutBySessionKey: FacadeModule["setTelegramThreadBindingIdleTimeoutBySessionKey"] = + ((...args) => + loadFacadeModule()["setTelegramThreadBindingIdleTimeoutBySessionKey"]( + ...args, + )) as FacadeModule["setTelegramThreadBindingIdleTimeoutBySessionKey"]; +export const setTelegramThreadBindingMaxAgeBySessionKey: FacadeModule["setTelegramThreadBindingMaxAgeBySessionKey"] = + ((...args) => + loadFacadeModule()["setTelegramThreadBindingMaxAgeBySessionKey"]( + ...args, + )) as FacadeModule["setTelegramThreadBindingMaxAgeBySessionKey"]; +export const shouldSuppressTelegramExecApprovalForwardingFallback: FacadeModule["shouldSuppressTelegramExecApprovalForwardingFallback"] = + ((...args) => + loadFacadeModule()["shouldSuppressTelegramExecApprovalForwardingFallback"]( + ...args, + )) as FacadeModule["shouldSuppressTelegramExecApprovalForwardingFallback"]; +export const telegramMessageActions: FacadeModule["telegramMessageActions"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["telegramMessageActions"] as object, + ) as FacadeModule["telegramMessageActions"]; +export const unpinMessageTelegram: FacadeModule["unpinMessageTelegram"] = ((...args) => + loadFacadeModule()["unpinMessageTelegram"](...args)) as FacadeModule["unpinMessageTelegram"]; +export type TelegramApiOverride = FacadeEntry["types"]["TelegramApiOverride"]; +export type TelegramProbe = FacadeEntry["types"]["TelegramProbe"]; diff --git a/src/plugin-sdk/telegram-surface.ts b/src/plugin-sdk/telegram-surface.ts index 9228f3a227e..0db7850582e 100644 --- a/src/plugin-sdk/telegram-surface.ts +++ b/src/plugin-sdk/telegram-surface.ts @@ -1,45 +1,148 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildBrowseProvidersButton, - buildModelsKeyboard, - buildProviderKeyboard, - buildTelegramGroupPeerId, - calculateTotalPages, - createTelegramActionGate, - fetchTelegramChatId, - getCacheStats, - getModelsPageSize, - inspectTelegramAccount, - isTelegramExecApprovalApprover, - isTelegramExecApprovalAuthorizedSender, - isTelegramExecApprovalClientEnabled, - isTelegramExecApprovalTargetRecipient, - listTelegramAccountIds, - listTelegramDirectoryGroupsFromConfig, - listTelegramDirectoryPeersFromConfig, - looksLikeTelegramTargetId, - lookupTelegramChatId, - normalizeTelegramMessagingTarget, - parseTelegramReplyToMessageId, - parseTelegramTarget, - parseTelegramThreadId, - resolveTelegramAutoThreadId, - resolveTelegramGroupRequireMention, - resolveTelegramGroupToolPolicy, - resolveTelegramInlineButtonsScope, - resolveTelegramPollActionGateState, - resolveTelegramReactionLevel, - resolveTelegramTargetChatType, - searchStickers, - sendTelegramPayloadMessages, -} from "../../extensions/telegram/api.js"; -export type { - InspectedTelegramAccount, - ProviderInfo, - ResolvedTelegramAccount, - StickerMetadata, - TelegramButtonStyle, - TelegramInlineButtons, - TelegramProbe, - TelegramTokenResolution, -} from "../../extensions/telegram/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["telegram-surface"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "telegram", + artifactBasename: "api.js", + }); +} +export const buildBrowseProvidersButton: FacadeModule["buildBrowseProvidersButton"] = ((...args) => + loadFacadeModule()["buildBrowseProvidersButton"]( + ...args, + )) as FacadeModule["buildBrowseProvidersButton"]; +export const buildModelsKeyboard: FacadeModule["buildModelsKeyboard"] = ((...args) => + loadFacadeModule()["buildModelsKeyboard"](...args)) as FacadeModule["buildModelsKeyboard"]; +export const buildProviderKeyboard: FacadeModule["buildProviderKeyboard"] = ((...args) => + loadFacadeModule()["buildProviderKeyboard"](...args)) as FacadeModule["buildProviderKeyboard"]; +export const buildTelegramGroupPeerId: FacadeModule["buildTelegramGroupPeerId"] = ((...args) => + loadFacadeModule()["buildTelegramGroupPeerId"]( + ...args, + )) as FacadeModule["buildTelegramGroupPeerId"]; +export const calculateTotalPages: FacadeModule["calculateTotalPages"] = ((...args) => + loadFacadeModule()["calculateTotalPages"](...args)) as FacadeModule["calculateTotalPages"]; +export const createTelegramActionGate: FacadeModule["createTelegramActionGate"] = ((...args) => + loadFacadeModule()["createTelegramActionGate"]( + ...args, + )) as FacadeModule["createTelegramActionGate"]; +export const fetchTelegramChatId: FacadeModule["fetchTelegramChatId"] = ((...args) => + loadFacadeModule()["fetchTelegramChatId"](...args)) as FacadeModule["fetchTelegramChatId"]; +export const getCacheStats: FacadeModule["getCacheStats"] = ((...args) => + loadFacadeModule()["getCacheStats"](...args)) as FacadeModule["getCacheStats"]; +export const getModelsPageSize: FacadeModule["getModelsPageSize"] = ((...args) => + loadFacadeModule()["getModelsPageSize"](...args)) as FacadeModule["getModelsPageSize"]; +export const inspectTelegramAccount: FacadeModule["inspectTelegramAccount"] = ((...args) => + loadFacadeModule()["inspectTelegramAccount"](...args)) as FacadeModule["inspectTelegramAccount"]; +export const isTelegramExecApprovalApprover: FacadeModule["isTelegramExecApprovalApprover"] = (( + ...args +) => + loadFacadeModule()["isTelegramExecApprovalApprover"]( + ...args, + )) as FacadeModule["isTelegramExecApprovalApprover"]; +export const isTelegramExecApprovalAuthorizedSender: FacadeModule["isTelegramExecApprovalAuthorizedSender"] = + ((...args) => + loadFacadeModule()["isTelegramExecApprovalAuthorizedSender"]( + ...args, + )) as FacadeModule["isTelegramExecApprovalAuthorizedSender"]; +export const isTelegramExecApprovalClientEnabled: FacadeModule["isTelegramExecApprovalClientEnabled"] = + ((...args) => + loadFacadeModule()["isTelegramExecApprovalClientEnabled"]( + ...args, + )) as FacadeModule["isTelegramExecApprovalClientEnabled"]; +export const isTelegramExecApprovalTargetRecipient: FacadeModule["isTelegramExecApprovalTargetRecipient"] = + ((...args) => + loadFacadeModule()["isTelegramExecApprovalTargetRecipient"]( + ...args, + )) as FacadeModule["isTelegramExecApprovalTargetRecipient"]; +export const listTelegramAccountIds: FacadeModule["listTelegramAccountIds"] = ((...args) => + loadFacadeModule()["listTelegramAccountIds"](...args)) as FacadeModule["listTelegramAccountIds"]; +export const listTelegramDirectoryGroupsFromConfig: FacadeModule["listTelegramDirectoryGroupsFromConfig"] = + ((...args) => + loadFacadeModule()["listTelegramDirectoryGroupsFromConfig"]( + ...args, + )) as FacadeModule["listTelegramDirectoryGroupsFromConfig"]; +export const listTelegramDirectoryPeersFromConfig: FacadeModule["listTelegramDirectoryPeersFromConfig"] = + ((...args) => + loadFacadeModule()["listTelegramDirectoryPeersFromConfig"]( + ...args, + )) as FacadeModule["listTelegramDirectoryPeersFromConfig"]; +export const looksLikeTelegramTargetId: FacadeModule["looksLikeTelegramTargetId"] = ((...args) => + loadFacadeModule()["looksLikeTelegramTargetId"]( + ...args, + )) as FacadeModule["looksLikeTelegramTargetId"]; +export const lookupTelegramChatId: FacadeModule["lookupTelegramChatId"] = ((...args) => + loadFacadeModule()["lookupTelegramChatId"](...args)) as FacadeModule["lookupTelegramChatId"]; +export const normalizeTelegramMessagingTarget: FacadeModule["normalizeTelegramMessagingTarget"] = (( + ...args +) => + loadFacadeModule()["normalizeTelegramMessagingTarget"]( + ...args, + )) as FacadeModule["normalizeTelegramMessagingTarget"]; +export const parseTelegramReplyToMessageId: FacadeModule["parseTelegramReplyToMessageId"] = (( + ...args +) => + loadFacadeModule()["parseTelegramReplyToMessageId"]( + ...args, + )) as FacadeModule["parseTelegramReplyToMessageId"]; +export const parseTelegramTarget: FacadeModule["parseTelegramTarget"] = ((...args) => + loadFacadeModule()["parseTelegramTarget"](...args)) as FacadeModule["parseTelegramTarget"]; +export const parseTelegramThreadId: FacadeModule["parseTelegramThreadId"] = ((...args) => + loadFacadeModule()["parseTelegramThreadId"](...args)) as FacadeModule["parseTelegramThreadId"]; +export const resolveTelegramAutoThreadId: FacadeModule["resolveTelegramAutoThreadId"] = (( + ...args +) => + loadFacadeModule()["resolveTelegramAutoThreadId"]( + ...args, + )) as FacadeModule["resolveTelegramAutoThreadId"]; +export const resolveTelegramGroupRequireMention: FacadeModule["resolveTelegramGroupRequireMention"] = + ((...args) => + loadFacadeModule()["resolveTelegramGroupRequireMention"]( + ...args, + )) as FacadeModule["resolveTelegramGroupRequireMention"]; +export const resolveTelegramGroupToolPolicy: FacadeModule["resolveTelegramGroupToolPolicy"] = (( + ...args +) => + loadFacadeModule()["resolveTelegramGroupToolPolicy"]( + ...args, + )) as FacadeModule["resolveTelegramGroupToolPolicy"]; +export const resolveTelegramInlineButtonsScope: FacadeModule["resolveTelegramInlineButtonsScope"] = + ((...args) => + loadFacadeModule()["resolveTelegramInlineButtonsScope"]( + ...args, + )) as FacadeModule["resolveTelegramInlineButtonsScope"]; +export const resolveTelegramPollActionGateState: FacadeModule["resolveTelegramPollActionGateState"] = + ((...args) => + loadFacadeModule()["resolveTelegramPollActionGateState"]( + ...args, + )) as FacadeModule["resolveTelegramPollActionGateState"]; +export const resolveTelegramReactionLevel: FacadeModule["resolveTelegramReactionLevel"] = (( + ...args +) => + loadFacadeModule()["resolveTelegramReactionLevel"]( + ...args, + )) as FacadeModule["resolveTelegramReactionLevel"]; +export const resolveTelegramTargetChatType: FacadeModule["resolveTelegramTargetChatType"] = (( + ...args +) => + loadFacadeModule()["resolveTelegramTargetChatType"]( + ...args, + )) as FacadeModule["resolveTelegramTargetChatType"]; +export const searchStickers: FacadeModule["searchStickers"] = ((...args) => + loadFacadeModule()["searchStickers"](...args)) as FacadeModule["searchStickers"]; +export const sendTelegramPayloadMessages: FacadeModule["sendTelegramPayloadMessages"] = (( + ...args +) => + loadFacadeModule()["sendTelegramPayloadMessages"]( + ...args, + )) as FacadeModule["sendTelegramPayloadMessages"]; +export type InspectedTelegramAccount = FacadeEntry["types"]["InspectedTelegramAccount"]; +export type ProviderInfo = FacadeEntry["types"]["ProviderInfo"]; +export type ResolvedTelegramAccount = FacadeEntry["types"]["ResolvedTelegramAccount"]; +export type StickerMetadata = FacadeEntry["types"]["StickerMetadata"]; +export type TelegramButtonStyle = FacadeEntry["types"]["TelegramButtonStyle"]; +export type TelegramInlineButtons = FacadeEntry["types"]["TelegramInlineButtons"]; +export type TelegramProbe = FacadeEntry["types"]["TelegramProbe"]; +export type TelegramTokenResolution = FacadeEntry["types"]["TelegramTokenResolution"]; diff --git a/src/plugin-sdk/thread-ownership.ts b/src/plugin-sdk/thread-ownership.ts index a0245ab22b1..363075ed02c 100644 --- a/src/plugin-sdk/thread-ownership.ts +++ b/src/plugin-sdk/thread-ownership.ts @@ -1,5 +1,5 @@ // Narrow plugin-sdk surface for the bundled thread-ownership plugin. -// Keep this list additive and scoped to symbols used under extensions/thread-ownership. +// Keep this list additive and scoped to the bundled thread-ownership surface. export { definePluginEntry } from "./plugin-entry.js"; export type { OpenClawConfig } from "../config/config.js"; diff --git a/src/plugin-sdk/tlon.ts b/src/plugin-sdk/tlon.ts index 46374813abc..32431e03e7d 100644 --- a/src/plugin-sdk/tlon.ts +++ b/src/plugin-sdk/tlon.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled tlon plugin. -// Keep this list additive and scoped to symbols used under extensions/tlon. +// Keep this list additive and scoped to the bundled Tlon surface. import { createOptionalChannelSetupSurface } from "./channel-setup.js"; diff --git a/src/plugin-sdk/together.ts b/src/plugin-sdk/together.ts index a3fb3f0fddc..ac70be7586f 100644 --- a/src/plugin-sdk/together.ts +++ b/src/plugin-sdk/together.ts @@ -1,9 +1,33 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyTogetherConfig, - buildTogetherModelDefinition, - buildTogetherProvider, - TOGETHER_BASE_URL, - TOGETHER_DEFAULT_MODEL_REF, - TOGETHER_MODEL_CATALOG, -} from "../../extensions/together/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["together"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "together", + artifactBasename: "api.js", + }); +} +export const applyTogetherConfig: FacadeModule["applyTogetherConfig"] = ((...args) => + loadFacadeModule()["applyTogetherConfig"](...args)) as FacadeModule["applyTogetherConfig"]; +export const buildTogetherModelDefinition: FacadeModule["buildTogetherModelDefinition"] = (( + ...args +) => + loadFacadeModule()["buildTogetherModelDefinition"]( + ...args, + )) as FacadeModule["buildTogetherModelDefinition"]; +export const buildTogetherProvider: FacadeModule["buildTogetherProvider"] = ((...args) => + loadFacadeModule()["buildTogetherProvider"](...args)) as FacadeModule["buildTogetherProvider"]; +export const TOGETHER_BASE_URL: FacadeModule["TOGETHER_BASE_URL"] = + loadFacadeModule()["TOGETHER_BASE_URL"]; +export const TOGETHER_DEFAULT_MODEL_REF: FacadeModule["TOGETHER_DEFAULT_MODEL_REF"] = + loadFacadeModule()["TOGETHER_DEFAULT_MODEL_REF"]; +export const TOGETHER_MODEL_CATALOG: FacadeModule["TOGETHER_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["TOGETHER_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["TOGETHER_MODEL_CATALOG"]; diff --git a/src/plugin-sdk/twitch.ts b/src/plugin-sdk/twitch.ts index 440f33d15dc..c2bd255badb 100644 --- a/src/plugin-sdk/twitch.ts +++ b/src/plugin-sdk/twitch.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled twitch plugin. -// Keep this list additive and scoped to symbols used under extensions/twitch. +// Keep this list additive and scoped to the bundled Twitch surface. import { createOptionalChannelSetupSurface } from "./channel-setup.js"; diff --git a/src/plugin-sdk/venice.ts b/src/plugin-sdk/venice.ts index dfe1b5862d5..9d0b10ddd58 100644 --- a/src/plugin-sdk/venice.ts +++ b/src/plugin-sdk/venice.ts @@ -1,9 +1,31 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildVeniceModelDefinition, - buildVeniceProvider, - discoverVeniceModels, - VENICE_BASE_URL, - VENICE_DEFAULT_MODEL_REF, - VENICE_MODEL_CATALOG, -} from "../../extensions/venice/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["venice"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "venice", + artifactBasename: "api.js", + }); +} +export const buildVeniceModelDefinition: FacadeModule["buildVeniceModelDefinition"] = ((...args) => + loadFacadeModule()["buildVeniceModelDefinition"]( + ...args, + )) as FacadeModule["buildVeniceModelDefinition"]; +export const buildVeniceProvider: FacadeModule["buildVeniceProvider"] = ((...args) => + loadFacadeModule()["buildVeniceProvider"](...args)) as FacadeModule["buildVeniceProvider"]; +export const discoverVeniceModels: FacadeModule["discoverVeniceModels"] = ((...args) => + loadFacadeModule()["discoverVeniceModels"](...args)) as FacadeModule["discoverVeniceModels"]; +export const VENICE_BASE_URL: FacadeModule["VENICE_BASE_URL"] = + loadFacadeModule()["VENICE_BASE_URL"]; +export const VENICE_DEFAULT_MODEL_REF: FacadeModule["VENICE_DEFAULT_MODEL_REF"] = + loadFacadeModule()["VENICE_DEFAULT_MODEL_REF"]; +export const VENICE_MODEL_CATALOG: FacadeModule["VENICE_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["VENICE_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["VENICE_MODEL_CATALOG"]; diff --git a/src/plugin-sdk/vercel-ai-gateway.ts b/src/plugin-sdk/vercel-ai-gateway.ts index b838b95538d..2ec8df5e07e 100644 --- a/src/plugin-sdk/vercel-ai-gateway.ts +++ b/src/plugin-sdk/vercel-ai-gateway.ts @@ -1,13 +1,48 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildVercelAiGatewayProvider, - discoverVercelAiGatewayModels, - getStaticVercelAiGatewayModelCatalog, - VERCEL_AI_GATEWAY_BASE_URL, - VERCEL_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW, - VERCEL_AI_GATEWAY_DEFAULT_COST, - VERCEL_AI_GATEWAY_DEFAULT_MAX_TOKENS, - VERCEL_AI_GATEWAY_DEFAULT_MODEL_ID, - VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, - VERCEL_AI_GATEWAY_PROVIDER_ID, -} from "../../extensions/vercel-ai-gateway/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["vercel-ai-gateway"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "vercel-ai-gateway", + artifactBasename: "api.js", + }); +} +export const buildVercelAiGatewayProvider: FacadeModule["buildVercelAiGatewayProvider"] = (( + ...args +) => + loadFacadeModule()["buildVercelAiGatewayProvider"]( + ...args, + )) as FacadeModule["buildVercelAiGatewayProvider"]; +export const discoverVercelAiGatewayModels: FacadeModule["discoverVercelAiGatewayModels"] = (( + ...args +) => + loadFacadeModule()["discoverVercelAiGatewayModels"]( + ...args, + )) as FacadeModule["discoverVercelAiGatewayModels"]; +export const getStaticVercelAiGatewayModelCatalog: FacadeModule["getStaticVercelAiGatewayModelCatalog"] = + ((...args) => + loadFacadeModule()["getStaticVercelAiGatewayModelCatalog"]( + ...args, + )) as FacadeModule["getStaticVercelAiGatewayModelCatalog"]; +export const VERCEL_AI_GATEWAY_BASE_URL: FacadeModule["VERCEL_AI_GATEWAY_BASE_URL"] = + loadFacadeModule()["VERCEL_AI_GATEWAY_BASE_URL"]; +export const VERCEL_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW: FacadeModule["VERCEL_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW"] = + loadFacadeModule()["VERCEL_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW"]; +export const VERCEL_AI_GATEWAY_DEFAULT_COST: FacadeModule["VERCEL_AI_GATEWAY_DEFAULT_COST"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["VERCEL_AI_GATEWAY_DEFAULT_COST"] as object, + ) as FacadeModule["VERCEL_AI_GATEWAY_DEFAULT_COST"]; +export const VERCEL_AI_GATEWAY_DEFAULT_MAX_TOKENS: FacadeModule["VERCEL_AI_GATEWAY_DEFAULT_MAX_TOKENS"] = + loadFacadeModule()["VERCEL_AI_GATEWAY_DEFAULT_MAX_TOKENS"]; +export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_ID: FacadeModule["VERCEL_AI_GATEWAY_DEFAULT_MODEL_ID"] = + loadFacadeModule()["VERCEL_AI_GATEWAY_DEFAULT_MODEL_ID"]; +export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF: FacadeModule["VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF"] = + loadFacadeModule()["VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF"]; +export const VERCEL_AI_GATEWAY_PROVIDER_ID: FacadeModule["VERCEL_AI_GATEWAY_PROVIDER_ID"] = + loadFacadeModule()["VERCEL_AI_GATEWAY_PROVIDER_ID"]; diff --git a/src/plugin-sdk/vllm.ts b/src/plugin-sdk/vllm.ts index 4d21740bc93..cee1f570593 100644 --- a/src/plugin-sdk/vllm.ts +++ b/src/plugin-sdk/vllm.ts @@ -1,8 +1,22 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildVllmProvider, - VLLM_DEFAULT_API_KEY_ENV_VAR, - VLLM_DEFAULT_BASE_URL, - VLLM_MODEL_PLACEHOLDER, - VLLM_PROVIDER_LABEL, -} from "../../extensions/vllm/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["vllm"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "vllm", + artifactBasename: "api.js", + }); +} +export const buildVllmProvider: FacadeModule["buildVllmProvider"] = ((...args) => + loadFacadeModule()["buildVllmProvider"](...args)) as FacadeModule["buildVllmProvider"]; +export const VLLM_DEFAULT_API_KEY_ENV_VAR: FacadeModule["VLLM_DEFAULT_API_KEY_ENV_VAR"] = + loadFacadeModule()["VLLM_DEFAULT_API_KEY_ENV_VAR"]; +export const VLLM_DEFAULT_BASE_URL: FacadeModule["VLLM_DEFAULT_BASE_URL"] = + loadFacadeModule()["VLLM_DEFAULT_BASE_URL"]; +export const VLLM_MODEL_PLACEHOLDER: FacadeModule["VLLM_MODEL_PLACEHOLDER"] = + loadFacadeModule()["VLLM_MODEL_PLACEHOLDER"]; +export const VLLM_PROVIDER_LABEL: FacadeModule["VLLM_PROVIDER_LABEL"] = + loadFacadeModule()["VLLM_PROVIDER_LABEL"]; diff --git a/src/plugin-sdk/volcengine.ts b/src/plugin-sdk/volcengine.ts index 365b2aa361c..4572f39df8d 100644 --- a/src/plugin-sdk/volcengine.ts +++ b/src/plugin-sdk/volcengine.ts @@ -1,10 +1,37 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - buildDoubaoCodingProvider, - buildDoubaoModelDefinition, - buildDoubaoProvider, - DOUBAO_BASE_URL, - DOUBAO_CODING_BASE_URL, - DOUBAO_CODING_MODEL_CATALOG, - DOUBAO_MODEL_CATALOG, -} from "../../extensions/volcengine/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["volcengine"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeArrayValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "volcengine", + artifactBasename: "api.js", + }); +} +export const buildDoubaoCodingProvider: FacadeModule["buildDoubaoCodingProvider"] = ((...args) => + loadFacadeModule()["buildDoubaoCodingProvider"]( + ...args, + )) as FacadeModule["buildDoubaoCodingProvider"]; +export const buildDoubaoModelDefinition: FacadeModule["buildDoubaoModelDefinition"] = ((...args) => + loadFacadeModule()["buildDoubaoModelDefinition"]( + ...args, + )) as FacadeModule["buildDoubaoModelDefinition"]; +export const buildDoubaoProvider: FacadeModule["buildDoubaoProvider"] = ((...args) => + loadFacadeModule()["buildDoubaoProvider"](...args)) as FacadeModule["buildDoubaoProvider"]; +export const DOUBAO_BASE_URL: FacadeModule["DOUBAO_BASE_URL"] = + loadFacadeModule()["DOUBAO_BASE_URL"]; +export const DOUBAO_CODING_BASE_URL: FacadeModule["DOUBAO_CODING_BASE_URL"] = + loadFacadeModule()["DOUBAO_CODING_BASE_URL"]; +export const DOUBAO_CODING_MODEL_CATALOG: FacadeModule["DOUBAO_CODING_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["DOUBAO_CODING_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["DOUBAO_CODING_MODEL_CATALOG"]; +export const DOUBAO_MODEL_CATALOG: FacadeModule["DOUBAO_MODEL_CATALOG"] = + createLazyFacadeArrayValue( + () => loadFacadeModule()["DOUBAO_MODEL_CATALOG"] as unknown as readonly unknown[], + ) as FacadeModule["DOUBAO_MODEL_CATALOG"]; diff --git a/src/plugin-sdk/whatsapp-surface.ts b/src/plugin-sdk/whatsapp-surface.ts index 6f024052898..93d197467e0 100644 --- a/src/plugin-sdk/whatsapp-surface.ts +++ b/src/plugin-sdk/whatsapp-surface.ts @@ -1,19 +1,70 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - DEFAULT_WEB_MEDIA_BYTES, - hasAnyWhatsAppAuth, - listEnabledWhatsAppAccounts, - listWhatsAppDirectoryGroupsFromConfig, - listWhatsAppDirectoryPeersFromConfig, - resolveWhatsAppAccount, - resolveWhatsAppGroupRequireMention, - resolveWhatsAppGroupToolPolicy, - resolveWhatsAppOutboundTarget, - whatsappAccessControlTesting, -} from "../../extensions/whatsapp/api.js"; -export type { - WebChannelStatus, - WebInboundMessage, - WebListenerCloseReason, - WebMonitorTuning, -} from "../../extensions/whatsapp/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["whatsapp-surface"]; +type FacadeModule = FacadeEntry["module"]; +type FacadeModule2 = FacadeEntry["sourceModules"]["source2"]["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "whatsapp", + artifactBasename: "api.js", + }); +} + +function loadFacadeModule2(): FacadeModule2 { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "whatsapp", + artifactBasename: "constants.js", + }); +} +export const DEFAULT_WEB_MEDIA_BYTES: FacadeModule2["DEFAULT_WEB_MEDIA_BYTES"] = + loadFacadeModule2()["DEFAULT_WEB_MEDIA_BYTES"]; +export const hasAnyWhatsAppAuth: FacadeModule["hasAnyWhatsAppAuth"] = ((...args) => + loadFacadeModule()["hasAnyWhatsAppAuth"](...args)) as FacadeModule["hasAnyWhatsAppAuth"]; +export const listEnabledWhatsAppAccounts: FacadeModule["listEnabledWhatsAppAccounts"] = (( + ...args +) => + loadFacadeModule()["listEnabledWhatsAppAccounts"]( + ...args, + )) as FacadeModule["listEnabledWhatsAppAccounts"]; +export const listWhatsAppDirectoryGroupsFromConfig: FacadeModule["listWhatsAppDirectoryGroupsFromConfig"] = + ((...args) => + loadFacadeModule()["listWhatsAppDirectoryGroupsFromConfig"]( + ...args, + )) as FacadeModule["listWhatsAppDirectoryGroupsFromConfig"]; +export const listWhatsAppDirectoryPeersFromConfig: FacadeModule["listWhatsAppDirectoryPeersFromConfig"] = + ((...args) => + loadFacadeModule()["listWhatsAppDirectoryPeersFromConfig"]( + ...args, + )) as FacadeModule["listWhatsAppDirectoryPeersFromConfig"]; +export const resolveWhatsAppAccount: FacadeModule["resolveWhatsAppAccount"] = ((...args) => + loadFacadeModule()["resolveWhatsAppAccount"](...args)) as FacadeModule["resolveWhatsAppAccount"]; +export const resolveWhatsAppGroupRequireMention: FacadeModule["resolveWhatsAppGroupRequireMention"] = + ((...args) => + loadFacadeModule()["resolveWhatsAppGroupRequireMention"]( + ...args, + )) as FacadeModule["resolveWhatsAppGroupRequireMention"]; +export const resolveWhatsAppGroupToolPolicy: FacadeModule["resolveWhatsAppGroupToolPolicy"] = (( + ...args +) => + loadFacadeModule()["resolveWhatsAppGroupToolPolicy"]( + ...args, + )) as FacadeModule["resolveWhatsAppGroupToolPolicy"]; +export const resolveWhatsAppOutboundTarget: FacadeModule["resolveWhatsAppOutboundTarget"] = (( + ...args +) => + loadFacadeModule()["resolveWhatsAppOutboundTarget"]( + ...args, + )) as FacadeModule["resolveWhatsAppOutboundTarget"]; +export const whatsappAccessControlTesting: FacadeModule["whatsappAccessControlTesting"] = + createLazyFacadeObjectValue( + () => loadFacadeModule()["whatsappAccessControlTesting"] as object, + ) as FacadeModule["whatsappAccessControlTesting"]; +export type WebChannelStatus = FacadeEntry["types"]["WebChannelStatus"]; +export type WebInboundMessage = FacadeEntry["types"]["WebInboundMessage"]; +export type WebListenerCloseReason = FacadeEntry["types"]["WebListenerCloseReason"]; +export type WebMonitorTuning = FacadeEntry["types"]["WebMonitorTuning"]; diff --git a/src/plugin-sdk/whatsapp-targets.ts b/src/plugin-sdk/whatsapp-targets.ts index 15f2ab7ad11..08c116ab8ff 100644 --- a/src/plugin-sdk/whatsapp-targets.ts +++ b/src/plugin-sdk/whatsapp-targets.ts @@ -1,6 +1,20 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - isWhatsAppGroupJid, - isWhatsAppUserTarget, - normalizeWhatsAppTarget, -} from "../../extensions/whatsapp/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["whatsapp-targets"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "whatsapp", + artifactBasename: "api.js", + }); +} +export const isWhatsAppGroupJid: FacadeModule["isWhatsAppGroupJid"] = ((...args) => + loadFacadeModule()["isWhatsAppGroupJid"](...args)) as FacadeModule["isWhatsAppGroupJid"]; +export const isWhatsAppUserTarget: FacadeModule["isWhatsAppUserTarget"] = ((...args) => + loadFacadeModule()["isWhatsAppUserTarget"](...args)) as FacadeModule["isWhatsAppUserTarget"]; +export const normalizeWhatsAppTarget: FacadeModule["normalizeWhatsAppTarget"] = ((...args) => + loadFacadeModule()["normalizeWhatsAppTarget"]( + ...args, + )) as FacadeModule["normalizeWhatsAppTarget"]; diff --git a/src/plugin-sdk/xai.ts b/src/plugin-sdk/xai.ts index de68c43a70f..8555e50f3ba 100644 --- a/src/plugin-sdk/xai.ts +++ b/src/plugin-sdk/xai.ts @@ -1,20 +1,51 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyXaiConfig, - applyXaiProviderConfig, - applyXaiModelCompat, - buildXaiCatalogModels, - buildXaiModelDefinition, - buildXaiProvider, - HTML_ENTITY_TOOL_CALL_ARGUMENTS_ENCODING, - isModernXaiModel, - normalizeXaiModelId, - resolveXaiCatalogEntry, - resolveXaiForwardCompatModel, - XAI_BASE_URL, - XAI_DEFAULT_CONTEXT_WINDOW, - XAI_DEFAULT_MODEL_ID, - XAI_DEFAULT_MODEL_REF, - XAI_DEFAULT_MAX_TOKENS, - XAI_TOOL_SCHEMA_PROFILE, -} from "../../extensions/xai/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["xai"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "xai", + artifactBasename: "api.js", + }); +} +export const applyXaiConfig: FacadeModule["applyXaiConfig"] = ((...args) => + loadFacadeModule()["applyXaiConfig"](...args)) as FacadeModule["applyXaiConfig"]; +export const applyXaiProviderConfig: FacadeModule["applyXaiProviderConfig"] = ((...args) => + loadFacadeModule()["applyXaiProviderConfig"](...args)) as FacadeModule["applyXaiProviderConfig"]; +export const applyXaiModelCompat: FacadeModule["applyXaiModelCompat"] = ((...args) => + loadFacadeModule()["applyXaiModelCompat"](...args)) as FacadeModule["applyXaiModelCompat"]; +export const buildXaiCatalogModels: FacadeModule["buildXaiCatalogModels"] = ((...args) => + loadFacadeModule()["buildXaiCatalogModels"](...args)) as FacadeModule["buildXaiCatalogModels"]; +export const buildXaiModelDefinition: FacadeModule["buildXaiModelDefinition"] = ((...args) => + loadFacadeModule()["buildXaiModelDefinition"]( + ...args, + )) as FacadeModule["buildXaiModelDefinition"]; +export const buildXaiProvider: FacadeModule["buildXaiProvider"] = ((...args) => + loadFacadeModule()["buildXaiProvider"](...args)) as FacadeModule["buildXaiProvider"]; +export const HTML_ENTITY_TOOL_CALL_ARGUMENTS_ENCODING: FacadeModule["HTML_ENTITY_TOOL_CALL_ARGUMENTS_ENCODING"] = + loadFacadeModule()["HTML_ENTITY_TOOL_CALL_ARGUMENTS_ENCODING"]; +export const isModernXaiModel: FacadeModule["isModernXaiModel"] = ((...args) => + loadFacadeModule()["isModernXaiModel"](...args)) as FacadeModule["isModernXaiModel"]; +export const normalizeXaiModelId: FacadeModule["normalizeXaiModelId"] = ((...args) => + loadFacadeModule()["normalizeXaiModelId"](...args)) as FacadeModule["normalizeXaiModelId"]; +export const resolveXaiCatalogEntry: FacadeModule["resolveXaiCatalogEntry"] = ((...args) => + loadFacadeModule()["resolveXaiCatalogEntry"](...args)) as FacadeModule["resolveXaiCatalogEntry"]; +export const resolveXaiForwardCompatModel: FacadeModule["resolveXaiForwardCompatModel"] = (( + ...args +) => + loadFacadeModule()["resolveXaiForwardCompatModel"]( + ...args, + )) as FacadeModule["resolveXaiForwardCompatModel"]; +export const XAI_BASE_URL: FacadeModule["XAI_BASE_URL"] = loadFacadeModule()["XAI_BASE_URL"]; +export const XAI_DEFAULT_CONTEXT_WINDOW: FacadeModule["XAI_DEFAULT_CONTEXT_WINDOW"] = + loadFacadeModule()["XAI_DEFAULT_CONTEXT_WINDOW"]; +export const XAI_DEFAULT_MODEL_ID: FacadeModule["XAI_DEFAULT_MODEL_ID"] = + loadFacadeModule()["XAI_DEFAULT_MODEL_ID"]; +export const XAI_DEFAULT_MODEL_REF: FacadeModule["XAI_DEFAULT_MODEL_REF"] = + loadFacadeModule()["XAI_DEFAULT_MODEL_REF"]; +export const XAI_DEFAULT_MAX_TOKENS: FacadeModule["XAI_DEFAULT_MAX_TOKENS"] = + loadFacadeModule()["XAI_DEFAULT_MAX_TOKENS"]; +export const XAI_TOOL_SCHEMA_PROFILE: FacadeModule["XAI_TOOL_SCHEMA_PROFILE"] = + loadFacadeModule()["XAI_TOOL_SCHEMA_PROFILE"]; diff --git a/src/plugin-sdk/xiaomi.ts b/src/plugin-sdk/xiaomi.ts index 0b712bd184a..6a331dd451a 100644 --- a/src/plugin-sdk/xiaomi.ts +++ b/src/plugin-sdk/xiaomi.ts @@ -1,8 +1,24 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyXiaomiConfig, - applyXiaomiProviderConfig, - buildXiaomiProvider, - XIAOMI_DEFAULT_MODEL_ID, - XIAOMI_DEFAULT_MODEL_REF, -} from "../../extensions/xiaomi/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["xiaomi"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "xiaomi", + artifactBasename: "api.js", + }); +} +export const applyXiaomiConfig: FacadeModule["applyXiaomiConfig"] = ((...args) => + loadFacadeModule()["applyXiaomiConfig"](...args)) as FacadeModule["applyXiaomiConfig"]; +export const applyXiaomiProviderConfig: FacadeModule["applyXiaomiProviderConfig"] = ((...args) => + loadFacadeModule()["applyXiaomiProviderConfig"]( + ...args, + )) as FacadeModule["applyXiaomiProviderConfig"]; +export const buildXiaomiProvider: FacadeModule["buildXiaomiProvider"] = ((...args) => + loadFacadeModule()["buildXiaomiProvider"](...args)) as FacadeModule["buildXiaomiProvider"]; +export const XIAOMI_DEFAULT_MODEL_ID: FacadeModule["XIAOMI_DEFAULT_MODEL_ID"] = + loadFacadeModule()["XIAOMI_DEFAULT_MODEL_ID"]; +export const XIAOMI_DEFAULT_MODEL_REF: FacadeModule["XIAOMI_DEFAULT_MODEL_REF"] = + loadFacadeModule()["XIAOMI_DEFAULT_MODEL_REF"]; diff --git a/src/plugin-sdk/zai.ts b/src/plugin-sdk/zai.ts index 2358fafd396..6d5f34778f0 100644 --- a/src/plugin-sdk/zai.ts +++ b/src/plugin-sdk/zai.ts @@ -1,11 +1,28 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - applyZaiConfig, - applyZaiProviderConfig, - ZAI_CN_BASE_URL, - ZAI_CODING_CN_BASE_URL, - ZAI_CODING_GLOBAL_BASE_URL, - ZAI_DEFAULT_MODEL_ID, - ZAI_DEFAULT_MODEL_REF, - ZAI_GLOBAL_BASE_URL, -} from "../../extensions/zai/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["zai"]; +type FacadeModule = FacadeEntry["module"]; +import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "zai", + artifactBasename: "api.js", + }); +} +export const applyZaiConfig: FacadeModule["applyZaiConfig"] = ((...args) => + loadFacadeModule()["applyZaiConfig"](...args)) as FacadeModule["applyZaiConfig"]; +export const applyZaiProviderConfig: FacadeModule["applyZaiProviderConfig"] = ((...args) => + loadFacadeModule()["applyZaiProviderConfig"](...args)) as FacadeModule["applyZaiProviderConfig"]; +export const ZAI_CN_BASE_URL: FacadeModule["ZAI_CN_BASE_URL"] = + loadFacadeModule()["ZAI_CN_BASE_URL"]; +export const ZAI_CODING_CN_BASE_URL: FacadeModule["ZAI_CODING_CN_BASE_URL"] = + loadFacadeModule()["ZAI_CODING_CN_BASE_URL"]; +export const ZAI_CODING_GLOBAL_BASE_URL: FacadeModule["ZAI_CODING_GLOBAL_BASE_URL"] = + loadFacadeModule()["ZAI_CODING_GLOBAL_BASE_URL"]; +export const ZAI_DEFAULT_MODEL_ID: FacadeModule["ZAI_DEFAULT_MODEL_ID"] = + loadFacadeModule()["ZAI_DEFAULT_MODEL_ID"]; +export const ZAI_DEFAULT_MODEL_REF: FacadeModule["ZAI_DEFAULT_MODEL_REF"] = + loadFacadeModule()["ZAI_DEFAULT_MODEL_REF"]; +export const ZAI_GLOBAL_BASE_URL: FacadeModule["ZAI_GLOBAL_BASE_URL"] = + loadFacadeModule()["ZAI_GLOBAL_BASE_URL"]; diff --git a/src/plugin-sdk/zalo-setup.ts b/src/plugin-sdk/zalo-setup.ts index ec2f5813338..d2c8c6945e9 100644 --- a/src/plugin-sdk/zalo-setup.ts +++ b/src/plugin-sdk/zalo-setup.ts @@ -1,7 +1,31 @@ // Generated by scripts/generate-plugin-sdk-facades.mjs. Do not edit manually. -export { - evaluateZaloGroupAccess, - resolveZaloRuntimeGroupPolicy, - zaloSetupAdapter, - zaloSetupWizard, -} from "../../extensions/zalo/api.js"; +import type { PluginSdkFacadeTypeMap } from "../generated/plugin-sdk-facade-type-map.generated.js"; +type FacadeEntry = PluginSdkFacadeTypeMap["zalo-setup"]; +type FacadeModule = FacadeEntry["module"]; +import { + createLazyFacadeObjectValue, + loadBundledPluginPublicSurfaceModuleSync, +} from "./facade-runtime.js"; + +function loadFacadeModule(): FacadeModule { + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: "zalo", + artifactBasename: "api.js", + }); +} +export const evaluateZaloGroupAccess: FacadeModule["evaluateZaloGroupAccess"] = ((...args) => + loadFacadeModule()["evaluateZaloGroupAccess"]( + ...args, + )) as FacadeModule["evaluateZaloGroupAccess"]; +export const resolveZaloRuntimeGroupPolicy: FacadeModule["resolveZaloRuntimeGroupPolicy"] = (( + ...args +) => + loadFacadeModule()["resolveZaloRuntimeGroupPolicy"]( + ...args, + )) as FacadeModule["resolveZaloRuntimeGroupPolicy"]; +export const zaloSetupAdapter: FacadeModule["zaloSetupAdapter"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["zaloSetupAdapter"] as object, +) as FacadeModule["zaloSetupAdapter"]; +export const zaloSetupWizard: FacadeModule["zaloSetupWizard"] = createLazyFacadeObjectValue( + () => loadFacadeModule()["zaloSetupWizard"] as object, +) as FacadeModule["zaloSetupWizard"]; diff --git a/src/plugin-sdk/zalo.ts b/src/plugin-sdk/zalo.ts index a1526fbc215..66607d74f97 100644 --- a/src/plugin-sdk/zalo.ts +++ b/src/plugin-sdk/zalo.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled zalo plugin. -// Keep this list additive and scoped to symbols used under extensions/zalo. +// Keep this list additive and scoped to the bundled Zalo surface. export { jsonResult, readStringParam } from "../agents/tools/common.js"; export type { ReplyPayload } from "../auto-reply/types.js"; diff --git a/src/plugin-sdk/zalouser.ts b/src/plugin-sdk/zalouser.ts index 4babb35baf1..7460fb1facc 100644 --- a/src/plugin-sdk/zalouser.ts +++ b/src/plugin-sdk/zalouser.ts @@ -1,5 +1,5 @@ // Private helper surface for the bundled zalouser plugin. -// Keep this list additive and scoped to symbols used under extensions/zalouser. +// Keep this list additive and scoped to the bundled Zalo user surface. import { createOptionalChannelSetupSurface } from "./channel-setup.js"; diff --git a/src/plugins/AGENTS.md b/src/plugins/AGENTS.md index 04373075ee4..c916638f272 100644 --- a/src/plugins/AGENTS.md +++ b/src/plugins/AGENTS.md @@ -14,7 +14,7 @@ assembly, and contract enforcement. - `src/plugins/types.ts` - `src/plugins/runtime/types.ts` - `src/plugins/contracts/registry.ts` - - `src/extensions/public-artifacts.ts` + - `src/plugins/public-artifacts.ts` ## Boundary Rules diff --git a/src/plugins/bundled-capability-metadata.ts b/src/plugins/bundled-capability-metadata.ts index 42ad2a4a973..e994cabcd53 100644 --- a/src/plugins/bundled-capability-metadata.ts +++ b/src/plugins/bundled-capability-metadata.ts @@ -1,4 +1,4 @@ -import { BUNDLED_PLUGIN_METADATA } from "./bundled-plugin-metadata.js"; +import { listBundledPluginMetadata } from "./bundled-plugin-metadata.js"; export type BundledPluginContractSnapshot = { pluginId: string; @@ -26,16 +26,17 @@ function uniqueStrings(values: readonly string[] | undefined): string[] { } export const BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS: readonly BundledPluginContractSnapshot[] = - BUNDLED_PLUGIN_METADATA.map(({ manifest }) => ({ - pluginId: manifest.id, - cliBackendIds: uniqueStrings(manifest.cliBackends), - providerIds: uniqueStrings(manifest.providers), - speechProviderIds: uniqueStrings(manifest.contracts?.speechProviders), - mediaUnderstandingProviderIds: uniqueStrings(manifest.contracts?.mediaUnderstandingProviders), - imageGenerationProviderIds: uniqueStrings(manifest.contracts?.imageGenerationProviders), - webSearchProviderIds: uniqueStrings(manifest.contracts?.webSearchProviders), - toolNames: uniqueStrings(manifest.contracts?.tools), - })) + listBundledPluginMetadata() + .map(({ manifest }) => ({ + pluginId: manifest.id, + cliBackendIds: uniqueStrings(manifest.cliBackends), + providerIds: uniqueStrings(manifest.providers), + speechProviderIds: uniqueStrings(manifest.contracts?.speechProviders), + mediaUnderstandingProviderIds: uniqueStrings(manifest.contracts?.mediaUnderstandingProviders), + imageGenerationProviderIds: uniqueStrings(manifest.contracts?.imageGenerationProviders), + webSearchProviderIds: uniqueStrings(manifest.contracts?.webSearchProviders), + toolNames: uniqueStrings(manifest.contracts?.tools), + })) .filter( (entry) => entry.cliBackendIds.length > 0 || @@ -100,18 +101,22 @@ export const BUNDLED_PROVIDER_PLUGIN_ID_ALIASES = Object.fromEntries( ) as Readonly>; export const BUNDLED_LEGACY_PLUGIN_ID_ALIASES = Object.fromEntries( - BUNDLED_PLUGIN_METADATA.flatMap(({ manifest }) => - (manifest.legacyPluginIds ?? []).map( - (legacyPluginId) => [legacyPluginId, manifest.id] as const, - ), - ).toSorted(([left], [right]) => left.localeCompare(right)), + listBundledPluginMetadata() + .flatMap(({ manifest }) => + (manifest.legacyPluginIds ?? []).map( + (legacyPluginId) => [legacyPluginId, manifest.id] as const, + ), + ) + .toSorted(([left], [right]) => left.localeCompare(right)), ) as Readonly>; export const BUNDLED_AUTO_ENABLE_PROVIDER_PLUGIN_IDS = Object.fromEntries( - BUNDLED_PLUGIN_METADATA.flatMap(({ manifest }) => - (manifest.autoEnableWhenConfiguredProviders ?? []).map((providerId) => [ - providerId, - manifest.id, - ]), - ).toSorted(([left], [right]) => left.localeCompare(right)), + listBundledPluginMetadata() + .flatMap(({ manifest }) => + (manifest.autoEnableWhenConfiguredProviders ?? []).map((providerId) => [ + providerId, + manifest.id, + ]), + ) + .toSorted(([left], [right]) => left.localeCompare(right)), ) as Readonly>; diff --git a/src/plugins/bundled-dir.ts b/src/plugins/bundled-dir.ts index ef0758209af..ed7fc4d1db9 100644 --- a/src/plugins/bundled-dir.ts +++ b/src/plugins/bundled-dir.ts @@ -81,7 +81,7 @@ export function resolveBundledPluginsDir(env: NodeJS.ProcessEnv = process.env): // ignore } - // bun --compile: ship a sibling `extensions/` next to the executable. + // bun --compile: ship a sibling bundled plugin tree next to the executable. try { const execDir = path.dirname(process.execPath); const siblingBuilt = path.join(execDir, "dist", "extensions"); @@ -96,7 +96,7 @@ export function resolveBundledPluginsDir(env: NodeJS.ProcessEnv = process.env): // ignore } - // npm/dev: walk up from this module to find `extensions/` at the package root. + // npm/dev: walk up from this module to find the bundled plugin tree at the package root. try { let cursor = path.dirname(fileURLToPath(import.meta.url)); for (let i = 0; i < 6; i += 1) { diff --git a/src/plugins/bundled-plugin-entries.ts b/src/plugins/bundled-plugin-entries.ts deleted file mode 100644 index 771a960ee20..00000000000 --- a/src/plugins/bundled-plugin-entries.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { loadGeneratedBundledPluginEntries } from "../generated/bundled-plugin-entries.generated.js"; -import type { OpenClawPluginDefinition } from "./types.js"; - -type BundledRegistrablePlugin = OpenClawPluginDefinition & { - id: string; - register: NonNullable; -}; - -export const BUNDLED_PLUGIN_ENTRIES = - (await loadGeneratedBundledPluginEntries()) as unknown as readonly BundledRegistrablePlugin[]; diff --git a/src/plugins/bundled-plugin-metadata.generated.ts b/src/plugins/bundled-plugin-metadata.generated.ts deleted file mode 100644 index 35711b3c902..00000000000 --- a/src/plugins/bundled-plugin-metadata.generated.ts +++ /dev/null @@ -1,19618 +0,0 @@ -// Auto-generated by scripts/generate-bundled-plugin-metadata.mjs. Do not edit directly. - -export const GENERATED_BUNDLED_PLUGIN_METADATA = [ - { - dirName: "acpx", - idHint: "acpx", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/acpx", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw ACP runtime backend via acpx", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "acpx", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - command: { - type: "string", - minLength: 1, - }, - expectedVersion: { - type: "string", - minLength: 1, - }, - cwd: { - type: "string", - minLength: 1, - }, - permissionMode: { - type: "string", - enum: ["approve-all", "approve-reads", "deny-all"], - }, - nonInteractivePermissions: { - type: "string", - enum: ["deny", "fail"], - }, - strictWindowsCmdWrapper: { - type: "boolean", - }, - timeoutSeconds: { - type: "number", - minimum: 0.001, - }, - queueOwnerTtlSeconds: { - type: "number", - minimum: 0, - }, - mcpServers: { - type: "object", - additionalProperties: { - type: "object", - properties: { - command: { - type: "string", - minLength: 1, - description: "Command to run the MCP server", - }, - args: { - type: "array", - items: { - type: "string", - }, - description: "Arguments to pass to the command", - }, - env: { - type: "object", - additionalProperties: { - type: "string", - }, - description: "Environment variables for the MCP server", - }, - }, - required: ["command"], - }, - }, - }, - }, - skills: ["./skills"], - name: "ACPX Runtime", - description: - "ACP runtime backend powered by acpx with configurable command path and version policy.", - uiHints: { - command: { - label: "acpx Command", - help: "Optional path/command override for acpx (for example /home/user/repos/acpx/dist/cli.js). Leave unset to use plugin-local bundled acpx.", - }, - expectedVersion: { - label: "Expected acpx Version", - help: 'Exact version to enforce or "any" to skip strict version matching.', - }, - cwd: { - label: "Default Working Directory", - help: "Default cwd for ACP session operations when not set per session.", - }, - permissionMode: { - label: "Permission Mode", - help: "Default acpx permission policy for runtime prompts.", - }, - nonInteractivePermissions: { - label: "Non-Interactive Permission Policy", - help: "acpx policy when interactive permission prompts are unavailable.", - }, - strictWindowsCmdWrapper: { - label: "Strict Windows cmd Wrapper", - help: "Enabled by default. On Windows, reject unresolved .cmd/.bat wrappers instead of shell fallback. Disable only for compatibility with non-standard wrappers.", - advanced: true, - }, - timeoutSeconds: { - label: "Prompt Timeout Seconds", - help: "Optional acpx timeout for each runtime turn.", - advanced: true, - }, - queueOwnerTtlSeconds: { - label: "Queue Owner TTL Seconds", - help: "Idle queue-owner TTL for acpx prompt turns. Keep this short in OpenClaw to avoid delayed completion after each turn.", - advanced: true, - }, - mcpServers: { - label: "MCP Servers", - help: "Named MCP server definitions to inject into ACPX-backed session bootstrap. Each entry needs a command and can include args and env.", - advanced: true, - }, - }, - }, - }, - { - dirName: "amazon-bedrock", - idHint: "amazon-bedrock", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "discovery.js"], - packageName: "@openclaw/amazon-bedrock-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Amazon Bedrock provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "amazon-bedrock", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["amazon-bedrock"], - }, - }, - { - dirName: "anthropic", - idHint: "anthropic", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "cli-backend.js", - "cli-migration.js", - "cli-shared.js", - "media-understanding-provider.js", - ], - packageName: "@openclaw/anthropic-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Anthropic provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "anthropic", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["anthropic"], - cliBackends: ["claude-cli"], - providerAuthEnvVars: { - anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "anthropic", - method: "cli", - choiceId: "anthropic-cli", - deprecatedChoiceIds: ["claude-cli"], - choiceLabel: "Anthropic Claude CLI", - choiceHint: "Reuse a local Claude CLI login on this host", - groupId: "anthropic", - groupLabel: "Anthropic", - groupHint: "Claude CLI + setup-token + API key", - }, - { - provider: "anthropic", - method: "setup-token", - choiceId: "token", - choiceLabel: "Anthropic token (paste setup-token)", - choiceHint: "Run `claude setup-token` elsewhere, then paste the token here", - groupId: "anthropic", - groupLabel: "Anthropic", - groupHint: "Claude CLI + setup-token + API key", - }, - { - provider: "anthropic", - method: "api-key", - choiceId: "apiKey", - choiceLabel: "Anthropic API key", - groupId: "anthropic", - groupLabel: "Anthropic", - groupHint: "Claude CLI + setup-token + API key", - optionKey: "anthropicApiKey", - cliFlag: "--anthropic-api-key", - cliOption: "--anthropic-api-key ", - cliDescription: "Anthropic API key", - }, - ], - contracts: { - mediaUnderstandingProviders: ["anthropic"], - }, - }, - }, - { - dirName: "bluebubbles", - idHint: "bluebubbles", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "channel-config-api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/bluebubbles", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw BlueBubbles channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "bluebubbles", - label: "BlueBubbles", - selectionLabel: "BlueBubbles (macOS app)", - detailLabel: "BlueBubbles", - docsPath: "/channels/bluebubbles", - docsLabel: "bluebubbles", - blurb: "iMessage via the BlueBubbles mac app + REST API.", - aliases: ["bb"], - preferOver: ["imessage"], - systemImage: "bubble.left.and.text.bubble.right", - order: 75, - }, - install: { - npmSpec: "@openclaw/bluebubbles", - localPath: "extensions/bluebubbles", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "bluebubbles", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["bluebubbles"], - channelConfigs: { - bluebubbles: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - serverUrl: { - type: "string", - }, - password: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - webhookPath: { - type: "string", - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - enrichGroupParticipantsFromContacts: { - default: true, - type: "boolean", - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - mediaMaxMb: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - mediaLocalRoots: { - type: "array", - items: { - type: "string", - }, - }, - sendReadReceipts: { - type: "boolean", - }, - allowPrivateNetwork: { - type: "boolean", - }, - blockStreaming: { - type: "boolean", - }, - groups: { - type: "object", - properties: {}, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - }, - accounts: { - type: "object", - properties: {}, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - serverUrl: { - type: "string", - }, - password: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - webhookPath: { - type: "string", - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - enrichGroupParticipantsFromContacts: { - default: true, - type: "boolean", - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - mediaMaxMb: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - mediaLocalRoots: { - type: "array", - items: { - type: "string", - }, - }, - sendReadReceipts: { - type: "boolean", - }, - allowPrivateNetwork: { - type: "boolean", - }, - blockStreaming: { - type: "boolean", - }, - groups: { - type: "object", - properties: {}, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - }, - }, - required: ["enrichGroupParticipantsFromContacts"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - actions: { - type: "object", - properties: { - reactions: { - default: true, - type: "boolean", - }, - edit: { - default: true, - type: "boolean", - }, - unsend: { - default: true, - type: "boolean", - }, - reply: { - default: true, - type: "boolean", - }, - sendWithEffect: { - default: true, - type: "boolean", - }, - renameGroup: { - default: true, - type: "boolean", - }, - setGroupIcon: { - default: true, - type: "boolean", - }, - addParticipant: { - default: true, - type: "boolean", - }, - removeParticipant: { - default: true, - type: "boolean", - }, - leaveGroup: { - default: true, - type: "boolean", - }, - sendAttachment: { - default: true, - type: "boolean", - }, - }, - required: [ - "reactions", - "edit", - "unsend", - "reply", - "sendWithEffect", - "renameGroup", - "setGroupIcon", - "addParticipant", - "removeParticipant", - "leaveGroup", - "sendAttachment", - ], - additionalProperties: false, - }, - }, - required: ["enrichGroupParticipantsFromContacts"], - additionalProperties: false, - }, - uiHints: { - "": { - label: "BlueBubbles", - help: "BlueBubbles channel provider configuration used for Apple messaging bridge integrations. Keep DM policy aligned with your trusted sender model in shared deployments.", - }, - dmPolicy: { - label: "BlueBubbles DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.bluebubbles.allowFrom=["*"].', - }, - }, - label: "BlueBubbles", - description: "iMessage via the BlueBubbles mac app + REST API.", - preferOver: ["imessage"], - }, - }, - }, - }, - { - dirName: "brave", - idHint: "brave", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["web-search-provider.js"], - packageName: "@openclaw/brave-plugin", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Brave plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "brave", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - webSearch: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: ["string", "object"], - }, - mode: { - type: "string", - enum: ["web", "llm-context"], - }, - }, - }, - }, - }, - providerAuthEnvVars: { - brave: ["BRAVE_API_KEY"], - }, - uiHints: { - "webSearch.apiKey": { - label: "Brave Search API Key", - help: "Brave Search API key (fallback: BRAVE_API_KEY env var).", - sensitive: true, - placeholder: "BSA...", - }, - "webSearch.mode": { - label: "Brave Search Mode", - help: "Brave Search mode: web or llm-context.", - }, - }, - contracts: { - webSearchProviders: ["brave"], - }, - }, - }, - { - dirName: "browser", - idHint: "browser", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["browser-runtime-api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/browser-plugin", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw browser tool plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "browser", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - }, - }, - { - dirName: "byteplus", - idHint: "byteplus", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "provider-catalog.js"], - packageName: "@openclaw/byteplus-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw BytePlus provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "byteplus", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["byteplus", "byteplus-plan"], - providerAuthEnvVars: { - byteplus: ["BYTEPLUS_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "byteplus", - method: "api-key", - choiceId: "byteplus-api-key", - choiceLabel: "BytePlus API key", - groupId: "byteplus", - groupLabel: "BytePlus", - groupHint: "API key", - optionKey: "byteplusApiKey", - cliFlag: "--byteplus-api-key", - cliOption: "--byteplus-api-key ", - cliDescription: "BytePlus API key", - }, - ], - }, - }, - { - dirName: "chutes", - idHint: "chutes", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/chutes-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Chutes.ai provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "chutes", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["chutes"], - providerAuthEnvVars: { - chutes: ["CHUTES_API_KEY", "CHUTES_OAUTH_TOKEN"], - }, - providerAuthChoices: [ - { - provider: "chutes", - method: "oauth", - choiceId: "chutes", - choiceLabel: "Chutes (OAuth)", - choiceHint: "Browser sign-in", - groupId: "chutes", - groupLabel: "Chutes", - groupHint: "OAuth + API key", - }, - { - provider: "chutes", - method: "api-key", - choiceId: "chutes-api-key", - choiceLabel: "Chutes API key", - choiceHint: "Open-source models including Llama, DeepSeek, and more", - groupId: "chutes", - groupLabel: "Chutes", - groupHint: "OAuth + API key", - optionKey: "chutesApiKey", - cliFlag: "--chutes-api-key", - cliOption: "--chutes-api-key ", - cliDescription: "Chutes API key", - }, - ], - }, - }, - { - dirName: "cloudflare-ai-gateway", - idHint: "cloudflare-ai-gateway", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "onboard.js"], - packageName: "@openclaw/cloudflare-ai-gateway-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Cloudflare AI Gateway provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "cloudflare-ai-gateway", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["cloudflare-ai-gateway"], - providerAuthEnvVars: { - "cloudflare-ai-gateway": ["CLOUDFLARE_AI_GATEWAY_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "cloudflare-ai-gateway", - method: "api-key", - choiceId: "cloudflare-ai-gateway-api-key", - choiceLabel: "Cloudflare AI Gateway", - choiceHint: "Account ID + Gateway ID + API key", - groupId: "cloudflare-ai-gateway", - groupLabel: "Cloudflare AI Gateway", - groupHint: "Account ID + Gateway ID + API key", - optionKey: "cloudflareAiGatewayApiKey", - cliFlag: "--cloudflare-ai-gateway-api-key", - cliOption: "--cloudflare-ai-gateway-api-key ", - cliDescription: "Cloudflare AI Gateway API key", - }, - ], - }, - }, - { - dirName: "copilot-proxy", - idHint: "copilot-proxy", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/copilot-proxy", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Copilot Proxy provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "copilot-proxy", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["copilot-proxy"], - autoEnableWhenConfiguredProviders: ["copilot-proxy"], - providerAuthChoices: [ - { - provider: "copilot-proxy", - method: "local", - choiceId: "copilot-proxy", - choiceLabel: "Copilot Proxy", - choiceHint: "Configure base URL + model ids", - groupId: "copilot", - groupLabel: "Copilot", - groupHint: "GitHub + local proxy", - }, - ], - }, - }, - { - dirName: "deepgram", - idHint: "deepgram", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["audio.js", "media-understanding-provider.js"], - packageName: "@openclaw/deepgram-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Deepgram media-understanding provider", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "deepgram", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - contracts: { - mediaUnderstandingProviders: ["deepgram"], - }, - }, - }, - { - dirName: "deepseek", - idHint: "deepseek", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/deepseek-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw DeepSeek provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "deepseek", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["deepseek"], - providerAuthEnvVars: { - deepseek: ["DEEPSEEK_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "deepseek", - method: "api-key", - choiceId: "deepseek-api-key", - choiceLabel: "DeepSeek API key", - groupId: "deepseek", - groupLabel: "DeepSeek", - groupHint: "API key", - optionKey: "deepseekApiKey", - cliFlag: "--deepseek-api-key", - cliOption: "--deepseek-api-key ", - cliDescription: "DeepSeek API key", - }, - ], - }, - }, - { - dirName: "diagnostics-otel", - idHint: "diagnostics-otel", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js"], - packageName: "@openclaw/diagnostics-otel", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw diagnostics OpenTelemetry exporter", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "diagnostics-otel", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - }, - }, - { - dirName: "diffs", - idHint: "diffs", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js"], - packageName: "@openclaw/diffs", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw diff viewer plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "diffs", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - defaults: { - type: "object", - additionalProperties: false, - properties: { - fontFamily: { - type: "string", - default: "Fira Code", - }, - fontSize: { - type: "number", - minimum: 10, - maximum: 24, - default: 15, - }, - lineSpacing: { - type: "number", - minimum: 1, - maximum: 3, - default: 1.6, - }, - layout: { - type: "string", - enum: ["unified", "split"], - default: "unified", - }, - showLineNumbers: { - type: "boolean", - default: true, - }, - diffIndicators: { - type: "string", - enum: ["bars", "classic", "none"], - default: "bars", - }, - wordWrap: { - type: "boolean", - default: true, - }, - background: { - type: "boolean", - default: true, - }, - theme: { - type: "string", - enum: ["light", "dark"], - default: "dark", - }, - fileFormat: { - type: "string", - enum: ["png", "pdf"], - default: "png", - }, - format: { - type: "string", - enum: ["png", "pdf"], - }, - fileQuality: { - type: "string", - enum: ["standard", "hq", "print"], - default: "standard", - }, - fileScale: { - type: "number", - minimum: 1, - maximum: 4, - default: 2, - }, - fileMaxWidth: { - type: "number", - minimum: 640, - maximum: 2400, - default: 960, - }, - imageFormat: { - type: "string", - enum: ["png", "pdf"], - }, - imageQuality: { - type: "string", - enum: ["standard", "hq", "print"], - }, - imageScale: { - type: "number", - minimum: 1, - maximum: 4, - }, - imageMaxWidth: { - type: "number", - minimum: 640, - maximum: 2400, - }, - mode: { - type: "string", - enum: ["view", "image", "file", "both"], - default: "both", - }, - }, - }, - security: { - type: "object", - additionalProperties: false, - properties: { - allowRemoteViewer: { - type: "boolean", - default: false, - }, - }, - }, - }, - }, - skills: ["./skills"], - name: "Diffs", - description: "Read-only diff viewer and file renderer for agents.", - uiHints: { - "defaults.fontFamily": { - label: "Default Font", - help: "Preferred font family name for diff content and headers.", - }, - "defaults.fontSize": { - label: "Default Font Size", - help: "Base diff font size in pixels.", - }, - "defaults.lineSpacing": { - label: "Default Line Spacing", - help: "Line-height multiplier applied to diff rows.", - }, - "defaults.layout": { - label: "Default Layout", - help: "Initial diff layout shown in the viewer.", - }, - "defaults.showLineNumbers": { - label: "Show Line Numbers", - help: "Show line numbers by default.", - }, - "defaults.diffIndicators": { - label: "Diff Indicator Style", - help: "Choose added/removed indicators style.", - }, - "defaults.wordWrap": { - label: "Default Word Wrap", - help: "Wrap long lines by default.", - }, - "defaults.background": { - label: "Default Background Highlights", - help: "Show added/removed background highlights by default.", - }, - "defaults.theme": { - label: "Default Theme", - help: "Initial viewer theme.", - }, - "defaults.fileFormat": { - label: "Default File Format", - help: "Rendered file format for file mode (PNG or PDF).", - }, - "defaults.fileQuality": { - label: "Default File Quality", - help: "Quality preset for PNG/PDF rendering.", - }, - "defaults.fileScale": { - label: "Default File Scale", - help: "Device scale factor used while rendering file artifacts.", - }, - "defaults.fileMaxWidth": { - label: "Default File Max Width", - help: "Maximum file render width in CSS pixels.", - }, - "defaults.mode": { - label: "Default Output Mode", - help: "Tool default when mode is omitted. Use view for canvas/gateway viewer, file for PNG/PDF, or both.", - }, - "security.allowRemoteViewer": { - label: "Allow Remote Viewer", - help: "Allow non-loopback access to diff viewer URLs when the token path is known.", - }, - }, - }, - }, - { - dirName: "discord", - idHint: "discord", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: [ - "action-runtime-api.js", - "api.js", - "channel-config-api.js", - "runtime-api.js", - "session-key-api.js", - "timeouts.js", - ], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/discord", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Discord channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "discord", - label: "Discord", - selectionLabel: "Discord (Bot API)", - detailLabel: "Discord Bot", - docsPath: "/channels/discord", - docsLabel: "discord", - blurb: "very well supported right now.", - systemImage: "bubble.left.and.bubble.right", - markdownCapable: true, - }, - install: { - npmSpec: "@openclaw/discord", - localPath: "extensions/discord", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "discord", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["discord"], - channelConfigs: { - discord: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - commands: { - type: "object", - properties: { - native: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - nativeSkills: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - }, - additionalProperties: false, - }, - configWrites: { - type: "boolean", - }, - token: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - proxy: { - type: "string", - }, - allowBots: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "mentions", - }, - ], - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - streaming: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - enum: ["off", "partial", "block", "progress"], - }, - ], - }, - streamMode: { - type: "string", - enum: ["partial", "block", "off"], - }, - draftChunk: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - breakPreference: { - anyOf: [ - { - type: "string", - const: "paragraph", - }, - { - type: "string", - const: "newline", - }, - { - type: "string", - const: "sentence", - }, - ], - }, - }, - additionalProperties: false, - }, - maxLinesPerMessage: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - retry: { - type: "object", - properties: { - attempts: { - type: "integer", - minimum: 1, - maximum: 9007199254740991, - }, - minDelayMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - maxDelayMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - jitter: { - type: "number", - minimum: 0, - maximum: 1, - }, - }, - additionalProperties: false, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - stickers: { - type: "boolean", - }, - emojiUploads: { - type: "boolean", - }, - stickerUploads: { - type: "boolean", - }, - polls: { - type: "boolean", - }, - permissions: { - type: "boolean", - }, - messages: { - type: "boolean", - }, - threads: { - type: "boolean", - }, - pins: { - type: "boolean", - }, - search: { - type: "boolean", - }, - memberInfo: { - type: "boolean", - }, - roleInfo: { - type: "boolean", - }, - roles: { - type: "boolean", - }, - channelInfo: { - type: "boolean", - }, - voiceStatus: { - type: "boolean", - }, - events: { - type: "boolean", - }, - moderation: { - type: "boolean", - }, - channels: { - type: "boolean", - }, - presence: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - type: "string", - }, - dm: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - policy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupEnabled: { - type: "boolean", - }, - groupChannels: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - }, - additionalProperties: false, - }, - guilds: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - slug: { - type: "string", - }, - requireMention: { - type: "boolean", - }, - ignoreOtherMentions: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - reactionNotifications: { - type: "string", - enum: ["off", "own", "all", "allowlist"], - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - roles: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - channels: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - ignoreOtherMentions: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - roles: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - includeThreadStarter: { - type: "boolean", - }, - autoThread: { - type: "boolean", - }, - autoThreadName: { - type: "string", - enum: ["message", "generated"], - }, - autoArchiveDuration: { - anyOf: [ - { - type: "string", - enum: ["60", "1440", "4320", "10080"], - }, - { - type: "number", - const: 60, - }, - { - type: "number", - const: 1440, - }, - { - type: "number", - const: 4320, - }, - { - type: "number", - const: 10080, - }, - ], - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - execApprovals: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - approvers: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - agentFilter: { - type: "array", - items: { - type: "string", - }, - }, - sessionFilter: { - type: "array", - items: { - type: "string", - }, - }, - cleanupAfterResolve: { - type: "boolean", - }, - target: { - type: "string", - enum: ["dm", "channel", "both"], - }, - }, - additionalProperties: false, - }, - agentComponents: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - ui: { - type: "object", - properties: { - components: { - type: "object", - properties: { - accentColor: { - type: "string", - pattern: "^#?[0-9a-fA-F]{6}$", - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - slashCommand: { - type: "object", - properties: { - ephemeral: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - threadBindings: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - idleHours: { - type: "number", - minimum: 0, - }, - maxAgeHours: { - type: "number", - minimum: 0, - }, - spawnSubagentSessions: { - type: "boolean", - }, - spawnAcpSessions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - intents: { - type: "object", - properties: { - presence: { - type: "boolean", - }, - guildMembers: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - voice: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - autoJoin: { - type: "array", - items: { - type: "object", - properties: { - guildId: { - type: "string", - minLength: 1, - }, - channelId: { - type: "string", - minLength: 1, - }, - }, - required: ["guildId", "channelId"], - additionalProperties: false, - }, - }, - daveEncryption: { - type: "boolean", - }, - decryptionFailureTolerance: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - tts: { - type: "object", - properties: { - auto: { - type: "string", - enum: ["off", "always", "inbound", "tagged"], - }, - enabled: { - type: "boolean", - }, - mode: { - type: "string", - enum: ["final", "all"], - }, - provider: { - type: "string", - minLength: 1, - }, - summaryModel: { - type: "string", - }, - modelOverrides: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allowText: { - type: "boolean", - }, - allowProvider: { - type: "boolean", - }, - allowVoice: { - type: "boolean", - }, - allowModelId: { - type: "boolean", - }, - allowVoiceSettings: { - type: "boolean", - }, - allowNormalization: { - type: "boolean", - }, - allowSeed: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - providers: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - apiKey: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - }, - additionalProperties: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - { - type: "boolean", - }, - { - type: "null", - }, - { - type: "array", - items: {}, - }, - { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: {}, - }, - ], - }, - }, - }, - prefsPath: { - type: "string", - }, - maxTextLength: { - type: "integer", - minimum: 1, - maximum: 9007199254740991, - }, - timeoutMs: { - type: "integer", - minimum: 1000, - maximum: 120000, - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - pluralkit: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - token: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - ackReaction: { - type: "string", - }, - ackReactionScope: { - type: "string", - enum: ["group-mentions", "group-all", "direct", "all", "off", "none"], - }, - activity: { - type: "string", - }, - status: { - type: "string", - enum: ["online", "dnd", "idle", "invisible"], - }, - autoPresence: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - intervalMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - minUpdateIntervalMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - healthyText: { - type: "string", - }, - degradedText: { - type: "string", - }, - exhaustedText: { - type: "string", - }, - }, - additionalProperties: false, - }, - activityType: { - anyOf: [ - { - type: "number", - const: 0, - }, - { - type: "number", - const: 1, - }, - { - type: "number", - const: 2, - }, - { - type: "number", - const: 3, - }, - { - type: "number", - const: 4, - }, - { - type: "number", - const: 5, - }, - ], - }, - activityUrl: { - type: "string", - format: "uri", - }, - inboundWorker: { - type: "object", - properties: { - runTimeoutMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - eventQueue: { - type: "object", - properties: { - listenerTimeout: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxQueueSize: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxConcurrency: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - commands: { - type: "object", - properties: { - native: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - nativeSkills: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - }, - additionalProperties: false, - }, - configWrites: { - type: "boolean", - }, - token: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - proxy: { - type: "string", - }, - allowBots: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "mentions", - }, - ], - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - streaming: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - enum: ["off", "partial", "block", "progress"], - }, - ], - }, - streamMode: { - type: "string", - enum: ["partial", "block", "off"], - }, - draftChunk: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - breakPreference: { - anyOf: [ - { - type: "string", - const: "paragraph", - }, - { - type: "string", - const: "newline", - }, - { - type: "string", - const: "sentence", - }, - ], - }, - }, - additionalProperties: false, - }, - maxLinesPerMessage: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - retry: { - type: "object", - properties: { - attempts: { - type: "integer", - minimum: 1, - maximum: 9007199254740991, - }, - minDelayMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - maxDelayMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - jitter: { - type: "number", - minimum: 0, - maximum: 1, - }, - }, - additionalProperties: false, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - stickers: { - type: "boolean", - }, - emojiUploads: { - type: "boolean", - }, - stickerUploads: { - type: "boolean", - }, - polls: { - type: "boolean", - }, - permissions: { - type: "boolean", - }, - messages: { - type: "boolean", - }, - threads: { - type: "boolean", - }, - pins: { - type: "boolean", - }, - search: { - type: "boolean", - }, - memberInfo: { - type: "boolean", - }, - roleInfo: { - type: "boolean", - }, - roles: { - type: "boolean", - }, - channelInfo: { - type: "boolean", - }, - voiceStatus: { - type: "boolean", - }, - events: { - type: "boolean", - }, - moderation: { - type: "boolean", - }, - channels: { - type: "boolean", - }, - presence: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - type: "string", - }, - dm: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - policy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupEnabled: { - type: "boolean", - }, - groupChannels: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - }, - additionalProperties: false, - }, - guilds: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - slug: { - type: "string", - }, - requireMention: { - type: "boolean", - }, - ignoreOtherMentions: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - reactionNotifications: { - type: "string", - enum: ["off", "own", "all", "allowlist"], - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - roles: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - channels: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - ignoreOtherMentions: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - roles: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - includeThreadStarter: { - type: "boolean", - }, - autoThread: { - type: "boolean", - }, - autoThreadName: { - type: "string", - enum: ["message", "generated"], - }, - autoArchiveDuration: { - anyOf: [ - { - type: "string", - enum: ["60", "1440", "4320", "10080"], - }, - { - type: "number", - const: 60, - }, - { - type: "number", - const: 1440, - }, - { - type: "number", - const: 4320, - }, - { - type: "number", - const: 10080, - }, - ], - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - execApprovals: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - approvers: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - agentFilter: { - type: "array", - items: { - type: "string", - }, - }, - sessionFilter: { - type: "array", - items: { - type: "string", - }, - }, - cleanupAfterResolve: { - type: "boolean", - }, - target: { - type: "string", - enum: ["dm", "channel", "both"], - }, - }, - additionalProperties: false, - }, - agentComponents: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - ui: { - type: "object", - properties: { - components: { - type: "object", - properties: { - accentColor: { - type: "string", - pattern: "^#?[0-9a-fA-F]{6}$", - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - slashCommand: { - type: "object", - properties: { - ephemeral: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - threadBindings: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - idleHours: { - type: "number", - minimum: 0, - }, - maxAgeHours: { - type: "number", - minimum: 0, - }, - spawnSubagentSessions: { - type: "boolean", - }, - spawnAcpSessions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - intents: { - type: "object", - properties: { - presence: { - type: "boolean", - }, - guildMembers: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - voice: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - autoJoin: { - type: "array", - items: { - type: "object", - properties: { - guildId: { - type: "string", - minLength: 1, - }, - channelId: { - type: "string", - minLength: 1, - }, - }, - required: ["guildId", "channelId"], - additionalProperties: false, - }, - }, - daveEncryption: { - type: "boolean", - }, - decryptionFailureTolerance: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - tts: { - type: "object", - properties: { - auto: { - type: "string", - enum: ["off", "always", "inbound", "tagged"], - }, - enabled: { - type: "boolean", - }, - mode: { - type: "string", - enum: ["final", "all"], - }, - provider: { - type: "string", - minLength: 1, - }, - summaryModel: { - type: "string", - }, - modelOverrides: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allowText: { - type: "boolean", - }, - allowProvider: { - type: "boolean", - }, - allowVoice: { - type: "boolean", - }, - allowModelId: { - type: "boolean", - }, - allowVoiceSettings: { - type: "boolean", - }, - allowNormalization: { - type: "boolean", - }, - allowSeed: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - providers: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - apiKey: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - }, - additionalProperties: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - { - type: "boolean", - }, - { - type: "null", - }, - { - type: "array", - items: {}, - }, - { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: {}, - }, - ], - }, - }, - }, - prefsPath: { - type: "string", - }, - maxTextLength: { - type: "integer", - minimum: 1, - maximum: 9007199254740991, - }, - timeoutMs: { - type: "integer", - minimum: 1000, - maximum: 120000, - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - pluralkit: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - token: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - ackReaction: { - type: "string", - }, - ackReactionScope: { - type: "string", - enum: ["group-mentions", "group-all", "direct", "all", "off", "none"], - }, - activity: { - type: "string", - }, - status: { - type: "string", - enum: ["online", "dnd", "idle", "invisible"], - }, - autoPresence: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - intervalMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - minUpdateIntervalMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - healthyText: { - type: "string", - }, - degradedText: { - type: "string", - }, - exhaustedText: { - type: "string", - }, - }, - additionalProperties: false, - }, - activityType: { - anyOf: [ - { - type: "number", - const: 0, - }, - { - type: "number", - const: 1, - }, - { - type: "number", - const: 2, - }, - { - type: "number", - const: 3, - }, - { - type: "number", - const: 4, - }, - { - type: "number", - const: 5, - }, - ], - }, - activityUrl: { - type: "string", - format: "uri", - }, - inboundWorker: { - type: "object", - properties: { - runTimeoutMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - eventQueue: { - type: "object", - properties: { - listenerTimeout: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxQueueSize: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxConcurrency: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - required: ["groupPolicy"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["groupPolicy"], - additionalProperties: false, - }, - uiHints: { - "": { - label: "Discord", - help: "Discord channel provider configuration for bot auth, retry policy, streaming, thread bindings, and optional voice capabilities. Keep privileged intents and advanced features disabled unless needed.", - }, - dmPolicy: { - label: "Discord DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.discord.allowFrom=["*"].', - }, - "dm.policy": { - label: "Discord DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.discord.allowFrom=["*"] (legacy: channels.discord.dm.allowFrom).', - }, - configWrites: { - label: "Discord Config Writes", - help: "Allow Discord to write config in response to channel events/commands (default: true).", - }, - proxy: { - label: "Discord Proxy URL", - help: "Proxy URL for Discord gateway + API requests (app-id lookup and allowlist resolution). Set per account via channels.discord.accounts..proxy.", - }, - "commands.native": { - label: "Discord Native Commands", - help: 'Override native commands for Discord (bool or "auto").', - }, - "commands.nativeSkills": { - label: "Discord Native Skill Commands", - help: 'Override native skill commands for Discord (bool or "auto").', - }, - streaming: { - label: "Discord Streaming Mode", - help: 'Unified Discord stream preview mode: "off" | "partial" | "block" | "progress". "progress" maps to "partial" on Discord. Legacy boolean/streamMode keys are auto-mapped.', - }, - streamMode: { - label: "Discord Stream Mode (Legacy)", - help: "Legacy Discord preview mode alias (off | partial | block); auto-migrated to channels.discord.streaming.", - }, - "draftChunk.minChars": { - label: "Discord Draft Chunk Min Chars", - help: 'Minimum chars before emitting a Discord stream preview update when channels.discord.streaming="block" (default: 200).', - }, - "draftChunk.maxChars": { - label: "Discord Draft Chunk Max Chars", - help: 'Target max size for a Discord stream preview chunk when channels.discord.streaming="block" (default: 800; clamped to channels.discord.textChunkLimit).', - }, - "draftChunk.breakPreference": { - label: "Discord Draft Chunk Break Preference", - help: "Preferred breakpoints for Discord draft chunks (paragraph | newline | sentence). Default: paragraph.", - }, - "retry.attempts": { - label: "Discord Retry Attempts", - help: "Max retry attempts for outbound Discord API calls (default: 3).", - }, - "retry.minDelayMs": { - label: "Discord Retry Min Delay (ms)", - help: "Minimum retry delay in ms for Discord outbound calls.", - }, - "retry.maxDelayMs": { - label: "Discord Retry Max Delay (ms)", - help: "Maximum retry delay cap in ms for Discord outbound calls.", - }, - "retry.jitter": { - label: "Discord Retry Jitter", - help: "Jitter factor (0-1) applied to Discord retry delays.", - }, - maxLinesPerMessage: { - label: "Discord Max Lines Per Message", - help: "Soft max line count per Discord message (default: 17).", - }, - "inboundWorker.runTimeoutMs": { - label: "Discord Inbound Worker Timeout (ms)", - help: "Optional queued Discord inbound worker timeout in ms. This is separate from Carbon listener timeouts; defaults to 1800000 and can be disabled with 0. Set per account via channels.discord.accounts..inboundWorker.runTimeoutMs.", - }, - "eventQueue.listenerTimeout": { - label: "Discord EventQueue Listener Timeout (ms)", - help: "Canonical Discord listener timeout control in ms for gateway normalization/enqueue handlers. Default is 120000 in OpenClaw; set per account via channels.discord.accounts..eventQueue.listenerTimeout.", - }, - "eventQueue.maxQueueSize": { - label: "Discord EventQueue Max Queue Size", - help: "Optional Discord EventQueue capacity override (max queued events before backpressure). Set per account via channels.discord.accounts..eventQueue.maxQueueSize.", - }, - "eventQueue.maxConcurrency": { - label: "Discord EventQueue Max Concurrency", - help: "Optional Discord EventQueue concurrency override (max concurrent handler executions). Set per account via channels.discord.accounts..eventQueue.maxConcurrency.", - }, - "threadBindings.enabled": { - label: "Discord Thread Binding Enabled", - help: "Enable Discord thread binding features (/focus, bound-thread routing/delivery, and thread-bound subagent sessions). Overrides session.threadBindings.enabled when set.", - }, - "threadBindings.idleHours": { - label: "Discord Thread Binding Idle Timeout (hours)", - help: "Inactivity window in hours for Discord thread-bound sessions (/focus and spawned thread sessions). Set 0 to disable idle auto-unfocus (default: 24). Overrides session.threadBindings.idleHours when set.", - }, - "threadBindings.maxAgeHours": { - label: "Discord Thread Binding Max Age (hours)", - help: "Optional hard max age in hours for Discord thread-bound sessions. Set 0 to disable hard cap (default: 0). Overrides session.threadBindings.maxAgeHours when set.", - }, - "threadBindings.spawnSubagentSessions": { - label: "Discord Thread-Bound Subagent Spawn", - help: "Allow subagent spawns with thread=true to auto-create and bind Discord threads (default: false; opt-in). Set true to enable thread-bound subagent spawns for this account/channel.", - }, - "threadBindings.spawnAcpSessions": { - label: "Discord Thread-Bound ACP Spawn", - help: "Allow /acp spawn to auto-create and bind Discord threads for ACP sessions (default: false; opt-in). Set true to enable thread-bound ACP spawns for this account/channel.", - }, - "ui.components.accentColor": { - label: "Discord Component Accent Color", - help: "Accent color for Discord component containers (hex). Set per account via channels.discord.accounts..ui.components.accentColor.", - }, - "intents.presence": { - label: "Discord Presence Intent", - help: "Enable the Guild Presences privileged intent. Must also be enabled in the Discord Developer Portal. Allows tracking user activities (e.g. Spotify). Default: false.", - }, - "intents.guildMembers": { - label: "Discord Guild Members Intent", - help: "Enable the Guild Members privileged intent. Must also be enabled in the Discord Developer Portal. Default: false.", - }, - "voice.enabled": { - label: "Discord Voice Enabled", - help: "Enable Discord voice channel conversations (default: true). Omit channels.discord.voice to keep voice support disabled for the account.", - }, - "voice.autoJoin": { - label: "Discord Voice Auto-Join", - help: "Voice channels to auto-join on startup (list of guildId/channelId entries).", - }, - "voice.daveEncryption": { - label: "Discord Voice DAVE Encryption", - help: "Toggle DAVE end-to-end encryption for Discord voice joins (default: true in @discordjs/voice; Discord may require this).", - }, - "voice.decryptionFailureTolerance": { - label: "Discord Voice Decrypt Failure Tolerance", - help: "Consecutive decrypt failures before DAVE attempts session recovery (passed to @discordjs/voice; default: 24).", - }, - "voice.tts": { - label: "Discord Voice Text-to-Speech", - help: "Optional TTS overrides for Discord voice playback (merged with messages.tts).", - }, - "pluralkit.enabled": { - label: "Discord PluralKit Enabled", - help: "Resolve PluralKit proxied messages and treat system members as distinct senders.", - }, - "pluralkit.token": { - label: "Discord PluralKit Token", - help: "Optional PluralKit token for resolving private systems or members.", - }, - activity: { - label: "Discord Presence Activity", - help: "Discord presence activity text (defaults to custom status).", - }, - status: { - label: "Discord Presence Status", - help: "Discord presence status (online, dnd, idle, invisible).", - }, - "autoPresence.enabled": { - label: "Discord Auto Presence Enabled", - help: "Enable automatic Discord bot presence updates based on runtime/model availability signals. When enabled: healthy=>online, degraded/unknown=>idle, exhausted/unavailable=>dnd.", - }, - "autoPresence.intervalMs": { - label: "Discord Auto Presence Check Interval (ms)", - help: "How often to evaluate Discord auto-presence state in milliseconds (default: 30000).", - }, - "autoPresence.minUpdateIntervalMs": { - label: "Discord Auto Presence Min Update Interval (ms)", - help: "Minimum time between actual Discord presence update calls in milliseconds (default: 15000). Prevents status spam on noisy state changes.", - }, - "autoPresence.healthyText": { - label: "Discord Auto Presence Healthy Text", - help: "Optional custom status text while runtime is healthy (online). If omitted, falls back to static channels.discord.activity when set.", - }, - "autoPresence.degradedText": { - label: "Discord Auto Presence Degraded Text", - help: "Optional custom status text while runtime/model availability is degraded or unknown (idle).", - }, - "autoPresence.exhaustedText": { - label: "Discord Auto Presence Exhausted Text", - help: "Optional custom status text while runtime detects exhausted/unavailable model quota (dnd). Supports {reason} template placeholder.", - }, - activityType: { - label: "Discord Presence Activity Type", - help: "Discord presence activity type (0=Playing,1=Streaming,2=Listening,3=Watching,4=Custom,5=Competing).", - }, - activityUrl: { - label: "Discord Presence Activity URL", - help: "Discord presence streaming URL (required for activityType=1).", - }, - allowBots: { - label: "Discord Allow Bot Messages", - help: 'Allow bot-authored messages to trigger Discord replies (default: false). Set "mentions" to only accept bot messages that mention the bot.', - }, - token: { - label: "Discord Bot Token", - help: "Discord bot token used for gateway and REST API authentication for this provider account. Keep this secret out of committed config and rotate immediately after any leak.", - }, - }, - label: "Discord", - description: "very well supported right now.", - }, - }, - }, - }, - { - dirName: "duckduckgo", - idHint: "duckduckgo", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["web-search-provider.js"], - packageName: "@openclaw/duckduckgo-plugin", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw DuckDuckGo plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "duckduckgo", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - webSearch: { - type: "object", - additionalProperties: false, - properties: { - region: { - type: "string", - }, - safeSearch: { - type: "string", - enum: ["strict", "moderate", "off"], - }, - }, - }, - }, - }, - uiHints: { - "webSearch.region": { - label: "DuckDuckGo Region", - help: "Optional DuckDuckGo region code such as us-en, uk-en, or de-de.", - }, - "webSearch.safeSearch": { - label: "DuckDuckGo SafeSearch", - help: "SafeSearch level for DuckDuckGo results.", - }, - }, - contracts: { - webSearchProviders: ["duckduckgo"], - }, - }, - }, - { - dirName: "elevenlabs", - idHint: "elevenlabs", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["speech-provider.js", "tts.js"], - packageName: "@openclaw/elevenlabs-speech", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw ElevenLabs speech plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "elevenlabs", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - contracts: { - speechProviders: ["elevenlabs"], - }, - }, - }, - { - dirName: "exa", - idHint: "exa", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["web-search-provider.js"], - packageName: "@openclaw/exa-plugin", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Exa plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "exa", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - webSearch: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: ["string", "object"], - }, - }, - }, - }, - }, - providerAuthEnvVars: { - exa: ["EXA_API_KEY"], - }, - uiHints: { - "webSearch.apiKey": { - label: "Exa API Key", - help: "Exa Search API key (fallback: EXA_API_KEY env var).", - sensitive: true, - placeholder: "exa-...", - }, - }, - contracts: { - webSearchProviders: ["exa"], - }, - }, - }, - { - dirName: "fal", - idHint: "fal", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["image-generation-provider.js", "onboard.js"], - packageName: "@openclaw/fal-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw fal provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "fal", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["fal"], - providerAuthEnvVars: { - fal: ["FAL_KEY"], - }, - providerAuthChoices: [ - { - provider: "fal", - method: "api-key", - choiceId: "fal-api-key", - choiceLabel: "fal API key", - groupId: "fal", - groupLabel: "fal", - groupHint: "Image generation", - onboardingScopes: ["image-generation"], - optionKey: "falApiKey", - cliFlag: "--fal-api-key", - cliOption: "--fal-api-key ", - cliDescription: "fal API key", - }, - ], - contracts: { - imageGenerationProviders: ["fal"], - }, - }, - }, - { - dirName: "feishu", - idHint: "feishu", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js", "setup-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/feishu", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Feishu/Lark channel plugin (community maintained by @m1heng)", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "feishu", - label: "Feishu", - selectionLabel: "Feishu/Lark (飞书)", - docsPath: "/channels/feishu", - docsLabel: "feishu", - blurb: "飞书/Lark enterprise messaging with doc/wiki/drive tools.", - aliases: ["lark"], - order: 35, - quickstartAllowFrom: true, - }, - install: { - npmSpec: "@openclaw/feishu", - localPath: "extensions/feishu", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "feishu", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["feishu"], - skills: ["./skills"], - channelConfigs: { - feishu: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - enabled: { - type: "boolean", - }, - defaultAccount: { - type: "string", - }, - appId: { - type: "string", - }, - appSecret: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - encryptKey: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - verificationToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - domain: { - default: "feishu", - anyOf: [ - { - type: "string", - enum: ["feishu", "lark"], - }, - { - type: "string", - format: "uri", - pattern: "^https:\\/\\/.*", - }, - ], - }, - connectionMode: { - default: "websocket", - type: "string", - enum: ["websocket", "webhook"], - }, - webhookPath: { - default: "/feishu/events", - type: "string", - }, - webhookHost: { - type: "string", - }, - webhookPort: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - mode: { - type: "string", - enum: ["native", "escape", "strip"], - }, - tableMode: { - type: "string", - enum: ["native", "ascii", "simple"], - }, - }, - additionalProperties: false, - }, - configWrites: { - type: "boolean", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["open", "pairing", "allowlist"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - anyOf: [ - { - type: "string", - enum: ["open", "allowlist", "disabled"], - }, - {}, - ], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupSenderAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - requireMention: { - type: "boolean", - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - groupSessionScope: { - type: "string", - enum: ["group", "group_sender", "group_topic", "group_topic_sender"], - }, - topicSessionMode: { - type: "string", - enum: ["disabled", "enabled"], - }, - replyInThread: { - type: "string", - enum: ["disabled", "enabled"], - }, - }, - additionalProperties: false, - }, - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreamingCoalesce: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - minDelayMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxDelayMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - httpTimeoutMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 300000, - }, - heartbeat: { - type: "object", - properties: { - visibility: { - type: "string", - enum: ["visible", "hidden"], - }, - intervalMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - renderMode: { - type: "string", - enum: ["auto", "raw", "card"], - }, - streaming: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - doc: { - type: "boolean", - }, - chat: { - type: "boolean", - }, - wiki: { - type: "boolean", - }, - drive: { - type: "boolean", - }, - perm: { - type: "boolean", - }, - scopes: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - replyInThread: { - type: "string", - enum: ["disabled", "enabled"], - }, - reactionNotifications: { - default: "own", - type: "string", - enum: ["off", "own", "all"], - }, - typingIndicator: { - default: true, - type: "boolean", - }, - resolveSenderNames: { - default: true, - type: "boolean", - }, - groupSessionScope: { - type: "string", - enum: ["group", "group_sender", "group_topic", "group_topic_sender"], - }, - topicSessionMode: { - type: "string", - enum: ["disabled", "enabled"], - }, - dynamicAgentCreation: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - workspaceTemplate: { - type: "string", - }, - agentDirTemplate: { - type: "string", - }, - maxAgents: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - name: { - type: "string", - }, - appId: { - type: "string", - }, - appSecret: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - encryptKey: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - verificationToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - domain: { - anyOf: [ - { - type: "string", - enum: ["feishu", "lark"], - }, - { - type: "string", - format: "uri", - pattern: "^https:\\/\\/.*", - }, - ], - }, - connectionMode: { - type: "string", - enum: ["websocket", "webhook"], - }, - webhookPath: { - type: "string", - }, - webhookHost: { - type: "string", - }, - webhookPort: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - mode: { - type: "string", - enum: ["native", "escape", "strip"], - }, - tableMode: { - type: "string", - enum: ["native", "ascii", "simple"], - }, - }, - additionalProperties: false, - }, - configWrites: { - type: "boolean", - }, - dmPolicy: { - type: "string", - enum: ["open", "pairing", "allowlist"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - anyOf: [ - { - type: "string", - enum: ["open", "allowlist", "disabled"], - }, - {}, - ], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupSenderAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - requireMention: { - type: "boolean", - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - groupSessionScope: { - type: "string", - enum: ["group", "group_sender", "group_topic", "group_topic_sender"], - }, - topicSessionMode: { - type: "string", - enum: ["disabled", "enabled"], - }, - replyInThread: { - type: "string", - enum: ["disabled", "enabled"], - }, - }, - additionalProperties: false, - }, - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreamingCoalesce: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - minDelayMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxDelayMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - httpTimeoutMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 300000, - }, - heartbeat: { - type: "object", - properties: { - visibility: { - type: "string", - enum: ["visible", "hidden"], - }, - intervalMs: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - renderMode: { - type: "string", - enum: ["auto", "raw", "card"], - }, - streaming: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - doc: { - type: "boolean", - }, - chat: { - type: "boolean", - }, - wiki: { - type: "boolean", - }, - drive: { - type: "boolean", - }, - perm: { - type: "boolean", - }, - scopes: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - replyInThread: { - type: "string", - enum: ["disabled", "enabled"], - }, - reactionNotifications: { - type: "string", - enum: ["off", "own", "all"], - }, - typingIndicator: { - type: "boolean", - }, - resolveSenderNames: { - type: "boolean", - }, - groupSessionScope: { - type: "string", - enum: ["group", "group_sender", "group_topic", "group_topic_sender"], - }, - topicSessionMode: { - type: "string", - enum: ["disabled", "enabled"], - }, - }, - additionalProperties: false, - }, - }, - }, - required: [ - "domain", - "connectionMode", - "webhookPath", - "dmPolicy", - "groupPolicy", - "reactionNotifications", - "typingIndicator", - "resolveSenderNames", - ], - additionalProperties: false, - }, - label: "Feishu", - description: "飞书/Lark enterprise messaging with doc/wiki/drive tools.", - }, - }, - }, - }, - { - dirName: "firecrawl", - idHint: "firecrawl", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["web-search-provider.js"], - packageName: "@openclaw/firecrawl-plugin", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Firecrawl plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "firecrawl", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - webSearch: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: ["string", "object"], - }, - baseUrl: { - type: "string", - }, - }, - }, - }, - }, - providerAuthEnvVars: { - firecrawl: ["FIRECRAWL_API_KEY"], - }, - uiHints: { - "webSearch.apiKey": { - label: "Firecrawl Search API Key", - help: "Firecrawl API key for web search (fallback: FIRECRAWL_API_KEY env var).", - sensitive: true, - placeholder: "fc-...", - }, - "webSearch.baseUrl": { - label: "Firecrawl Search Base URL", - help: "Firecrawl Search base URL override.", - }, - }, - contracts: { - webSearchProviders: ["firecrawl"], - tools: ["firecrawl_search", "firecrawl_scrape"], - }, - }, - }, - { - dirName: "github-copilot", - idHint: "github-copilot", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "login.js", - "models-defaults.js", - "models.js", - "token.js", - "usage.js", - ], - packageName: "@openclaw/github-copilot-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw GitHub Copilot provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "github-copilot", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["github-copilot"], - providerAuthEnvVars: { - "github-copilot": ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"], - }, - providerAuthChoices: [ - { - provider: "github-copilot", - method: "device", - choiceId: "github-copilot", - choiceLabel: "GitHub Copilot", - choiceHint: "Device login with your GitHub account", - groupId: "copilot", - groupLabel: "Copilot", - groupHint: "GitHub + local proxy", - }, - ], - }, - }, - { - dirName: "google", - idHint: "google", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "cli-backend.js", - "gemini-cli-provider.js", - "image-generation-provider.js", - "media-understanding-provider.js", - "model-id.js", - "oauth.credentials.js", - "oauth.flow.js", - "oauth.http.js", - "oauth.js", - "oauth.project.js", - "oauth.runtime.js", - "oauth.shared.js", - "oauth.token.js", - "provider-models.js", - "runtime-api.js", - "web-search-provider.js", - ], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/google-plugin", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Google plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "google", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - webSearch: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: ["string", "object"], - }, - model: { - type: "string", - }, - }, - }, - }, - }, - enabledByDefault: true, - providers: ["google", "google-gemini-cli"], - autoEnableWhenConfiguredProviders: ["google-gemini-cli"], - cliBackends: ["google-gemini-cli"], - providerAuthEnvVars: { - google: ["GEMINI_API_KEY", "GOOGLE_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "google", - method: "api-key", - choiceId: "gemini-api-key", - choiceLabel: "Google Gemini API key", - groupId: "google", - groupLabel: "Google", - groupHint: "Gemini API key + OAuth", - optionKey: "geminiApiKey", - cliFlag: "--gemini-api-key", - cliOption: "--gemini-api-key ", - cliDescription: "Gemini API key", - }, - { - provider: "google-gemini-cli", - method: "oauth", - choiceId: "google-gemini-cli", - choiceLabel: "Gemini CLI OAuth", - choiceHint: "Google OAuth with project-aware token payload", - groupId: "google", - groupLabel: "Google", - groupHint: "Gemini API key + OAuth", - }, - ], - uiHints: { - "webSearch.apiKey": { - label: "Gemini Search API Key", - help: "Gemini API key for Google Search grounding (fallback: GEMINI_API_KEY env var).", - sensitive: true, - placeholder: "AIza...", - }, - "webSearch.model": { - label: "Gemini Search Model", - help: "Gemini model override for web search grounding.", - }, - }, - contracts: { - mediaUnderstandingProviders: ["google"], - imageGenerationProviders: ["google"], - webSearchProviders: ["gemini"], - }, - }, - }, - { - dirName: "googlechat", - idHint: "googlechat", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "channel-config-api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/googlechat", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Google Chat channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "googlechat", - label: "Google Chat", - selectionLabel: "Google Chat (Chat API)", - detailLabel: "Google Chat", - docsPath: "/channels/googlechat", - docsLabel: "googlechat", - blurb: "Google Workspace Chat app with HTTP webhook.", - aliases: ["gchat", "google-chat"], - order: 55, - systemImage: "message.badge", - markdownCapable: true, - }, - install: { - npmSpec: "@openclaw/googlechat", - localPath: "extensions/googlechat", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "googlechat", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["googlechat"], - channelConfigs: { - googlechat: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - configWrites: { - type: "boolean", - }, - allowBots: { - type: "boolean", - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allow: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - defaultTo: { - type: "string", - }, - serviceAccount: { - anyOf: [ - { - type: "string", - }, - { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: {}, - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - serviceAccountRef: { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - serviceAccountFile: { - type: "string", - }, - audienceType: { - type: "string", - enum: ["app-url", "project-number"], - }, - audience: { - type: "string", - }, - appPrincipal: { - type: "string", - }, - webhookPath: { - type: "string", - }, - webhookUrl: { - type: "string", - }, - botUser: { - type: "string", - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - streamMode: { - default: "replace", - type: "string", - enum: ["replace", "status_final", "append"], - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - dm: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - policy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - }, - required: ["policy"], - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - typingIndicator: { - type: "string", - enum: ["none", "message", "reaction"], - }, - responsePrefix: { - type: "string", - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - configWrites: { - type: "boolean", - }, - allowBots: { - type: "boolean", - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allow: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - defaultTo: { - type: "string", - }, - serviceAccount: { - anyOf: [ - { - type: "string", - }, - { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: {}, - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - serviceAccountRef: { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - serviceAccountFile: { - type: "string", - }, - audienceType: { - type: "string", - enum: ["app-url", "project-number"], - }, - audience: { - type: "string", - }, - appPrincipal: { - type: "string", - }, - webhookPath: { - type: "string", - }, - webhookUrl: { - type: "string", - }, - botUser: { - type: "string", - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - streamMode: { - default: "replace", - type: "string", - enum: ["replace", "status_final", "append"], - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - dm: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - policy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - }, - required: ["policy"], - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - typingIndicator: { - type: "string", - enum: ["none", "message", "reaction"], - }, - responsePrefix: { - type: "string", - }, - }, - required: ["groupPolicy", "streamMode"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["groupPolicy", "streamMode"], - additionalProperties: false, - }, - label: "Google Chat", - description: "Google Workspace Chat app with HTTP webhook.", - }, - }, - }, - }, - { - dirName: "groq", - idHint: "groq", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["media-understanding-provider.js"], - packageName: "@openclaw/groq-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Groq media-understanding provider", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "groq", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - contracts: { - mediaUnderstandingProviders: ["groq"], - }, - }, - }, - { - dirName: "huggingface", - idHint: "huggingface", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/huggingface-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Hugging Face provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "huggingface", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["huggingface"], - providerAuthEnvVars: { - huggingface: ["HUGGINGFACE_HUB_TOKEN", "HF_TOKEN"], - }, - providerAuthChoices: [ - { - provider: "huggingface", - method: "api-key", - choiceId: "huggingface-api-key", - choiceLabel: "Hugging Face API key", - choiceHint: "Inference API (HF token)", - groupId: "huggingface", - groupLabel: "Hugging Face", - groupHint: "Inference API (HF token)", - optionKey: "huggingfaceApiKey", - cliFlag: "--huggingface-api-key", - cliOption: "--huggingface-api-key ", - cliDescription: "Hugging Face API key (HF token)", - }, - ], - }, - }, - { - dirName: "imessage", - idHint: "imessage", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "channel-config-api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/imessage", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw iMessage channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "imessage", - label: "iMessage", - selectionLabel: "iMessage (imsg)", - detailLabel: "iMessage", - docsPath: "/channels/imessage", - docsLabel: "imessage", - blurb: "this is still a work in progress.", - aliases: ["imsg"], - systemImage: "message.fill", - }, - }, - manifest: { - id: "imessage", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["imessage"], - channelConfigs: { - imessage: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - configWrites: { - type: "boolean", - }, - cliPath: { - type: "string", - }, - dbPath: { - type: "string", - }, - remoteHost: { - type: "string", - }, - service: { - anyOf: [ - { - type: "string", - const: "imessage", - }, - { - type: "string", - const: "sms", - }, - { - type: "string", - const: "auto", - }, - ], - }, - region: { - type: "string", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - type: "string", - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - includeAttachments: { - type: "boolean", - }, - attachmentRoots: { - type: "array", - items: { - type: "string", - }, - }, - remoteAttachmentRoots: { - type: "array", - items: { - type: "string", - }, - }, - mediaMaxMb: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - configWrites: { - type: "boolean", - }, - cliPath: { - type: "string", - }, - dbPath: { - type: "string", - }, - remoteHost: { - type: "string", - }, - service: { - anyOf: [ - { - type: "string", - const: "imessage", - }, - { - type: "string", - const: "sms", - }, - { - type: "string", - const: "auto", - }, - ], - }, - region: { - type: "string", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - type: "string", - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - includeAttachments: { - type: "boolean", - }, - attachmentRoots: { - type: "array", - items: { - type: "string", - }, - }, - remoteAttachmentRoots: { - type: "array", - items: { - type: "string", - }, - }, - mediaMaxMb: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - uiHints: { - "": { - label: "iMessage", - help: "iMessage channel provider configuration for CLI integration and DM access policy handling. Use explicit CLI paths when runtime environments have non-standard binary locations.", - }, - dmPolicy: { - label: "iMessage DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.imessage.allowFrom=["*"].', - }, - configWrites: { - label: "iMessage Config Writes", - help: "Allow iMessage to write config in response to channel events/commands (default: true).", - }, - cliPath: { - label: "iMessage CLI Path", - help: "Filesystem path to the iMessage bridge CLI binary used for send/receive operations. Set explicitly when the binary is not on PATH in service runtime environments.", - }, - }, - label: "iMessage", - description: "this is still a work in progress.", - }, - }, - }, - }, - { - dirName: "irc", - idHint: "irc", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "channel-config-api.js"], - packageName: "@openclaw/irc", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw IRC channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "irc", - label: "IRC", - selectionLabel: "IRC (Server + Nick)", - detailLabel: "IRC", - docsPath: "/channels/irc", - docsLabel: "irc", - blurb: "classic IRC networks with DM/channel routing and pairing controls.", - aliases: ["internet-relay-chat"], - systemImage: "network", - }, - install: { - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "irc", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["irc"], - channelConfigs: { - irc: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - host: { - type: "string", - }, - port: { - type: "integer", - minimum: 1, - maximum: 65535, - }, - tls: { - type: "boolean", - }, - nick: { - type: "string", - }, - username: { - type: "string", - }, - realname: { - type: "string", - }, - password: { - type: "string", - }, - passwordFile: { - type: "string", - }, - nickserv: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - service: { - type: "string", - }, - password: { - type: "string", - }, - passwordFile: { - type: "string", - }, - register: { - type: "boolean", - }, - registerEmail: { - type: "string", - }, - }, - additionalProperties: false, - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - channels: { - type: "array", - items: { - type: "string", - }, - }, - mentionPatterns: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - host: { - type: "string", - }, - port: { - type: "integer", - minimum: 1, - maximum: 65535, - }, - tls: { - type: "boolean", - }, - nick: { - type: "string", - }, - username: { - type: "string", - }, - realname: { - type: "string", - }, - password: { - type: "string", - }, - passwordFile: { - type: "string", - }, - nickserv: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - service: { - type: "string", - }, - password: { - type: "string", - }, - passwordFile: { - type: "string", - }, - register: { - type: "boolean", - }, - registerEmail: { - type: "string", - }, - }, - additionalProperties: false, - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - channels: { - type: "array", - items: { - type: "string", - }, - }, - mentionPatterns: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - uiHints: { - "": { - label: "IRC", - help: "IRC channel provider configuration and compatibility settings for classic IRC transport workflows. Use this section when bridging legacy chat infrastructure into OpenClaw.", - }, - dmPolicy: { - label: "IRC DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.irc.allowFrom=["*"].', - }, - "nickserv.enabled": { - label: "IRC NickServ Enabled", - help: "Enable NickServ identify/register after connect (defaults to enabled when password is configured).", - }, - "nickserv.service": { - label: "IRC NickServ Service", - help: "NickServ service nick (default: NickServ).", - }, - "nickserv.password": { - label: "IRC NickServ Password", - help: "NickServ password used for IDENTIFY/REGISTER (sensitive).", - }, - "nickserv.passwordFile": { - label: "IRC NickServ Password File", - help: "Optional file path containing NickServ password.", - }, - "nickserv.register": { - label: "IRC NickServ Register", - help: "If true, send NickServ REGISTER on every connect. Use once for initial registration, then disable.", - }, - "nickserv.registerEmail": { - label: "IRC NickServ Register Email", - help: "Email used with NickServ REGISTER (required when register=true).", - }, - configWrites: { - label: "IRC Config Writes", - help: "Allow IRC to write config in response to channel events/commands (default: true).", - }, - }, - label: "IRC", - description: "classic IRC networks with DM/channel routing and pairing controls.", - }, - }, - }, - }, - { - dirName: "kilocode", - idHint: "kilocode", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "onboard.js", - "provider-catalog.js", - "provider-models.js", - "shared.js", - ], - packageName: "@openclaw/kilocode-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Kilo Gateway provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "kilocode", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["kilocode"], - providerAuthEnvVars: { - kilocode: ["KILOCODE_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "kilocode", - method: "api-key", - choiceId: "kilocode-api-key", - choiceLabel: "Kilo Gateway API key", - choiceHint: "API key (OpenRouter-compatible)", - groupId: "kilocode", - groupLabel: "Kilo Gateway", - groupHint: "API key (OpenRouter-compatible)", - optionKey: "kilocodeApiKey", - cliFlag: "--kilocode-api-key", - cliOption: "--kilocode-api-key ", - cliDescription: "Kilo Gateway API key", - }, - ], - }, - }, - { - dirName: "kimi-coding", - idHint: "kimi", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/kimi-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Kimi provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "kimi", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["kimi", "kimi-coding"], - providerAuthEnvVars: { - kimi: ["KIMI_API_KEY", "KIMICODE_API_KEY"], - "kimi-coding": ["KIMI_API_KEY", "KIMICODE_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "kimi", - method: "api-key", - choiceId: "kimi-code-api-key", - choiceLabel: "Kimi Code API key (subscription)", - groupId: "moonshot", - groupLabel: "Moonshot AI (Kimi K2.5)", - groupHint: "Kimi K2.5", - optionKey: "kimiCodeApiKey", - cliFlag: "--kimi-code-api-key", - cliOption: "--kimi-code-api-key ", - cliDescription: "Kimi Code API key (subscription)", - }, - ], - }, - }, - { - dirName: "line", - idHint: "line", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js", "setup-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/line", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw LINE channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "line", - label: "LINE", - selectionLabel: "LINE (Messaging API)", - detailLabel: "LINE Bot", - docsPath: "/channels/line", - docsLabel: "line", - blurb: "LINE Messaging API webhook bot.", - systemImage: "message", - order: 75, - quickstartAllowFrom: true, - }, - install: { - npmSpec: "@openclaw/line", - localPath: "extensions/line", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "line", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["line"], - channelConfigs: { - line: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - enabled: { - type: "boolean", - }, - channelAccessToken: { - type: "string", - }, - channelSecret: { - type: "string", - }, - tokenFile: { - type: "string", - }, - secretFile: { - type: "string", - }, - name: { - type: "string", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["open", "allowlist", "pairing", "disabled"], - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "allowlist", "disabled"], - }, - responsePrefix: { - type: "string", - }, - mediaMaxMb: { - type: "number", - }, - webhookPath: { - type: "string", - }, - threadBindings: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - idleHours: { - type: "number", - }, - maxAgeHours: { - type: "number", - }, - spawnSubagentSessions: { - type: "boolean", - }, - spawnAcpSessions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - channelAccessToken: { - type: "string", - }, - channelSecret: { - type: "string", - }, - tokenFile: { - type: "string", - }, - secretFile: { - type: "string", - }, - name: { - type: "string", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["open", "allowlist", "pairing", "disabled"], - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "allowlist", "disabled"], - }, - responsePrefix: { - type: "string", - }, - mediaMaxMb: { - type: "number", - }, - webhookPath: { - type: "string", - }, - threadBindings: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - idleHours: { - type: "number", - }, - maxAgeHours: { - type: "number", - }, - spawnSubagentSessions: { - type: "boolean", - }, - spawnAcpSessions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - requireMention: { - type: "boolean", - }, - systemPrompt: { - type: "string", - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - requireMention: { - type: "boolean", - }, - systemPrompt: { - type: "string", - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - label: "LINE", - description: "LINE Messaging API webhook bot.", - }, - }, - }, - }, - { - dirName: "litellm", - idHint: "litellm", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/litellm-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw LiteLLM provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "litellm", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["litellm"], - providerAuthEnvVars: { - litellm: ["LITELLM_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "litellm", - method: "api-key", - choiceId: "litellm-api-key", - choiceLabel: "LiteLLM API key", - choiceHint: "Unified gateway for 100+ LLM providers", - groupId: "litellm", - groupLabel: "LiteLLM", - groupHint: "Unified LLM gateway (100+ providers)", - optionKey: "litellmApiKey", - cliFlag: "--litellm-api-key", - cliOption: "--litellm-api-key ", - cliDescription: "LiteLLM API key", - }, - ], - }, - }, - { - dirName: "llm-task", - idHint: "llm-task", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js"], - packageName: "@openclaw/llm-task", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw JSON-only LLM task plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "llm-task", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - defaultProvider: { - type: "string", - }, - defaultModel: { - type: "string", - }, - defaultAuthProfileId: { - type: "string", - }, - allowedModels: { - type: "array", - items: { - type: "string", - }, - description: "Allowlist of provider/model keys like openai-codex/gpt-5.2.", - }, - maxTokens: { - type: "number", - }, - timeoutMs: { - type: "number", - }, - }, - }, - name: "LLM Task", - description: "Generic JSON-only LLM tool for structured tasks callable from workflows.", - }, - }, - { - dirName: "lobster", - idHint: "lobster", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/lobster", - packageVersion: "2026.3.28", - packageDescription: "Lobster workflow tool plugin (typed pipelines + resumable approvals)", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "lobster", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - name: "Lobster", - description: "Typed workflow tool with resumable approvals.", - }, - }, - { - dirName: "matrix", - idHint: "matrix", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "helper-api.js", - "legacy-crypto-inspector.js", - "runtime-api.js", - "thread-bindings-runtime.js", - ], - runtimeSidecarArtifacts: ["helper-api.js", "runtime-api.js", "thread-bindings-runtime.js"], - packageName: "@openclaw/matrix", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Matrix channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "matrix", - label: "Matrix", - selectionLabel: "Matrix (plugin)", - docsPath: "/channels/matrix", - docsLabel: "matrix", - blurb: "open protocol; install the plugin to enable.", - order: 70, - quickstartAllowFrom: true, - }, - install: { - npmSpec: "@openclaw/matrix", - localPath: "extensions/matrix", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "matrix", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["matrix"], - channelConfigs: { - matrix: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - defaultAccount: { - type: "string", - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: {}, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - homeserver: { - type: "string", - }, - allowPrivateNetwork: { - type: "boolean", - }, - userId: { - type: "string", - }, - accessToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - password: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - deviceId: { - type: "string", - }, - deviceName: { - type: "string", - }, - avatarUrl: { - type: "string", - }, - initialSyncLimit: { - type: "number", - }, - encryption: { - type: "boolean", - }, - allowlistOnly: { - type: "boolean", - }, - allowBots: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "mentions", - }, - ], - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - streaming: { - anyOf: [ - { - type: "string", - enum: ["partial", "off"], - }, - { - type: "boolean", - }, - ], - }, - replyToMode: { - type: "string", - enum: ["off", "first", "all"], - }, - threadReplies: { - type: "string", - enum: ["off", "inbound", "always"], - }, - textChunkLimit: { - type: "number", - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - responsePrefix: { - type: "string", - }, - ackReaction: { - type: "string", - }, - ackReactionScope: { - type: "string", - enum: ["group-mentions", "group-all", "direct", "all", "none", "off"], - }, - reactionNotifications: { - type: "string", - enum: ["off", "own"], - }, - threadBindings: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - idleHours: { - type: "number", - minimum: 0, - }, - maxAgeHours: { - type: "number", - minimum: 0, - }, - spawnSubagentSessions: { - type: "boolean", - }, - spawnAcpSessions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - startupVerification: { - type: "string", - enum: ["off", "if-unverified"], - }, - startupVerificationCooldownHours: { - type: "number", - }, - mediaMaxMb: { - type: "number", - }, - autoJoin: { - type: "string", - enum: ["always", "allowlist", "off"], - }, - autoJoinAllowlist: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - dm: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - policy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - }, - additionalProperties: false, - }, - groups: { - type: "object", - properties: {}, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allow: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - allowBots: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "mentions", - }, - ], - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - autoReply: { - type: "boolean", - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - rooms: { - type: "object", - properties: {}, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allow: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - allowBots: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "mentions", - }, - ], - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - autoReply: { - type: "boolean", - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - messages: { - type: "boolean", - }, - pins: { - type: "boolean", - }, - profile: { - type: "boolean", - }, - memberInfo: { - type: "boolean", - }, - channelInfo: { - type: "boolean", - }, - verification: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - label: "Matrix", - description: "open protocol; install the plugin to enable.", - }, - }, - }, - }, - { - dirName: "mattermost", - idHint: "mattermost", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/mattermost", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Mattermost channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "mattermost", - label: "Mattermost", - selectionLabel: "Mattermost (plugin)", - docsPath: "/channels/mattermost", - docsLabel: "mattermost", - blurb: "self-hosted Slack-style chat; install the plugin to enable.", - order: 65, - }, - install: { - npmSpec: "@openclaw/mattermost", - localPath: "extensions/mattermost", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "mattermost", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["mattermost"], - channelConfigs: { - mattermost: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - configWrites: { - type: "boolean", - }, - botToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - baseUrl: { - type: "string", - }, - chatmode: { - type: "string", - enum: ["oncall", "onmessage", "onchar"], - }, - oncharPrefixes: { - type: "array", - items: { - type: "string", - }, - }, - requireMention: { - type: "boolean", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - replyToMode: { - type: "string", - enum: ["off", "first", "all"], - }, - responsePrefix: { - type: "string", - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - commands: { - type: "object", - properties: { - native: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - nativeSkills: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - callbackPath: { - type: "string", - }, - callbackUrl: { - type: "string", - }, - }, - additionalProperties: false, - }, - interactions: { - type: "object", - properties: { - callbackBaseUrl: { - type: "string", - }, - allowedSourceIps: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - allowPrivateNetwork: { - type: "boolean", - }, - dmChannelRetry: { - type: "object", - properties: { - maxRetries: { - type: "integer", - minimum: 0, - maximum: 10, - }, - initialDelayMs: { - type: "integer", - minimum: 100, - maximum: 60000, - }, - maxDelayMs: { - type: "integer", - minimum: 1000, - maximum: 60000, - }, - timeoutMs: { - type: "integer", - minimum: 5000, - maximum: 120000, - }, - }, - additionalProperties: false, - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - configWrites: { - type: "boolean", - }, - botToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - baseUrl: { - type: "string", - }, - chatmode: { - type: "string", - enum: ["oncall", "onmessage", "onchar"], - }, - oncharPrefixes: { - type: "array", - items: { - type: "string", - }, - }, - requireMention: { - type: "boolean", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - replyToMode: { - type: "string", - enum: ["off", "first", "all"], - }, - responsePrefix: { - type: "string", - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - commands: { - type: "object", - properties: { - native: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - nativeSkills: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - callbackPath: { - type: "string", - }, - callbackUrl: { - type: "string", - }, - }, - additionalProperties: false, - }, - interactions: { - type: "object", - properties: { - callbackBaseUrl: { - type: "string", - }, - allowedSourceIps: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - allowPrivateNetwork: { - type: "boolean", - }, - dmChannelRetry: { - type: "object", - properties: { - maxRetries: { - type: "integer", - minimum: 0, - maximum: 10, - }, - initialDelayMs: { - type: "integer", - minimum: 100, - maximum: 60000, - }, - maxDelayMs: { - type: "integer", - minimum: 1000, - maximum: 60000, - }, - timeoutMs: { - type: "integer", - minimum: 5000, - maximum: 120000, - }, - }, - additionalProperties: false, - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - label: "Mattermost", - description: "self-hosted Slack-style chat; install the plugin to enable.", - }, - }, - }, - }, - { - dirName: "memory-core", - idHint: "memory-core", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/memory-core", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw core memory search plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "memory-core", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - kind: "memory", - }, - }, - { - dirName: "memory-lancedb", - idHint: "memory-lancedb", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "config.js", "lancedb-runtime.js"], - packageName: "@openclaw/memory-lancedb", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw LanceDB-backed long-term memory plugin with auto-recall/capture", - packageManifest: { - extensions: ["./index.ts"], - install: { - npmSpec: "@openclaw/memory-lancedb", - localPath: "extensions/memory-lancedb", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "memory-lancedb", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - embedding: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: "string", - }, - model: { - type: "string", - }, - baseUrl: { - type: "string", - }, - dimensions: { - type: "number", - }, - }, - required: ["apiKey"], - }, - dbPath: { - type: "string", - }, - autoCapture: { - type: "boolean", - }, - autoRecall: { - type: "boolean", - }, - captureMaxChars: { - type: "number", - minimum: 100, - maximum: 10000, - }, - }, - required: ["embedding"], - }, - kind: "memory", - uiHints: { - "embedding.apiKey": { - label: "OpenAI API Key", - sensitive: true, - placeholder: "sk-proj-...", - help: "API key for OpenAI embeddings (or use ${OPENAI_API_KEY})", - }, - "embedding.model": { - label: "Embedding Model", - placeholder: "text-embedding-3-small", - help: "OpenAI embedding model to use", - }, - "embedding.baseUrl": { - label: "Base URL", - placeholder: "https://api.openai.com/v1", - help: "Base URL for compatible providers (e.g. http://localhost:11434/v1)", - advanced: true, - }, - "embedding.dimensions": { - label: "Dimensions", - placeholder: "1536", - help: "Vector dimensions for custom models (required for non-standard models)", - advanced: true, - }, - dbPath: { - label: "Database Path", - placeholder: "~/.openclaw/memory/lancedb", - advanced: true, - }, - autoCapture: { - label: "Auto-Capture", - help: "Automatically capture important information from conversations", - }, - autoRecall: { - label: "Auto-Recall", - help: "Automatically inject relevant memories into context", - }, - captureMaxChars: { - label: "Capture Max Chars", - help: "Maximum message length eligible for auto-capture", - advanced: true, - placeholder: "500", - }, - }, - }, - }, - { - dirName: "microsoft", - idHint: "microsoft", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["speech-provider.js", "tts.js"], - packageName: "@openclaw/microsoft-speech", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Microsoft speech plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "microsoft", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - contracts: { - speechProviders: ["microsoft"], - }, - }, - }, - { - dirName: "microsoft-foundry", - idHint: "microsoft-foundry", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "auth.js", - "cli.js", - "onboard.js", - "provider.js", - "runtime.js", - "shared-runtime.js", - "shared.js", - ], - packageName: "@openclaw/microsoft-foundry", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Microsoft Foundry provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "microsoft-foundry", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["microsoft-foundry"], - providerAuthEnvVars: { - "microsoft-foundry": ["AZURE_OPENAI_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "microsoft-foundry", - method: "entra-id", - choiceId: "microsoft-foundry-entra", - choiceLabel: "Microsoft Foundry (Entra ID / az login)", - choiceHint: "Use your Azure login — no API key needed", - groupId: "microsoft-foundry", - groupLabel: "Microsoft Foundry", - groupHint: "Entra ID + API key", - }, - { - provider: "microsoft-foundry", - method: "api-key", - choiceId: "microsoft-foundry-apikey", - choiceLabel: "Microsoft Foundry (API key)", - choiceHint: "Use an Azure OpenAI API key directly", - groupId: "microsoft-foundry", - groupLabel: "Microsoft Foundry", - groupHint: "Entra ID + API key", - }, - ], - }, - }, - { - dirName: "minimax", - idHint: "minimax", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "image-generation-provider.js", - "media-understanding-provider.js", - "model-definitions.js", - "oauth.js", - "oauth.runtime.js", - "onboard.js", - "provider-catalog.js", - "provider-models.js", - ], - packageName: "@openclaw/minimax-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw MiniMax provider and OAuth plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "minimax", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["minimax", "minimax-portal"], - autoEnableWhenConfiguredProviders: ["minimax-portal"], - legacyPluginIds: ["minimax-portal-auth"], - providerAuthEnvVars: { - minimax: ["MINIMAX_API_KEY"], - "minimax-portal": ["MINIMAX_OAUTH_TOKEN", "MINIMAX_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "minimax-portal", - method: "oauth", - choiceId: "minimax-global-oauth", - choiceLabel: "MiniMax OAuth (Global)", - choiceHint: "Global endpoint - api.minimax.io", - groupId: "minimax", - groupLabel: "MiniMax", - groupHint: "M2.7 (recommended)", - }, - { - provider: "minimax", - method: "api-global", - choiceId: "minimax-global-api", - deprecatedChoiceIds: ["minimax", "minimax-api", "minimax-cloud", "minimax-api-lightning"], - choiceLabel: "MiniMax API key (Global)", - choiceHint: "Global endpoint - api.minimax.io", - groupId: "minimax", - groupLabel: "MiniMax", - groupHint: "M2.7 (recommended)", - optionKey: "minimaxApiKey", - cliFlag: "--minimax-api-key", - cliOption: "--minimax-api-key ", - cliDescription: "MiniMax API key", - }, - { - provider: "minimax-portal", - method: "oauth-cn", - choiceId: "minimax-cn-oauth", - choiceLabel: "MiniMax OAuth (CN)", - choiceHint: "CN endpoint - api.minimaxi.com", - groupId: "minimax", - groupLabel: "MiniMax", - groupHint: "M2.7 (recommended)", - }, - { - provider: "minimax", - method: "api-cn", - choiceId: "minimax-cn-api", - deprecatedChoiceIds: ["minimax-api-key-cn"], - choiceLabel: "MiniMax API key (CN)", - choiceHint: "CN endpoint - api.minimaxi.com", - groupId: "minimax", - groupLabel: "MiniMax", - groupHint: "M2.7 (recommended)", - optionKey: "minimaxApiKey", - cliFlag: "--minimax-api-key", - cliOption: "--minimax-api-key ", - cliDescription: "MiniMax API key", - }, - ], - contracts: { - mediaUnderstandingProviders: ["minimax", "minimax-portal"], - imageGenerationProviders: ["minimax", "minimax-portal"], - }, - }, - }, - { - dirName: "mistral", - idHint: "mistral", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "media-understanding-provider.js", - "model-definitions.js", - "onboard.js", - "provider-catalog.js", - ], - packageName: "@openclaw/mistral-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Mistral provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "mistral", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["mistral"], - providerAuthEnvVars: { - mistral: ["MISTRAL_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "mistral", - method: "api-key", - choiceId: "mistral-api-key", - choiceLabel: "Mistral API key", - groupId: "mistral", - groupLabel: "Mistral AI", - groupHint: "API key", - optionKey: "mistralApiKey", - cliFlag: "--mistral-api-key", - cliOption: "--mistral-api-key ", - cliDescription: "Mistral API key", - }, - ], - contracts: { - mediaUnderstandingProviders: ["mistral"], - }, - }, - }, - { - dirName: "modelstudio", - idHint: "modelstudio", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "model-definitions.js", - "models.js", - "onboard.js", - "provider-catalog.js", - ], - packageName: "@openclaw/modelstudio-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Model Studio provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "modelstudio", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["modelstudio"], - providerAuthEnvVars: { - modelstudio: ["MODELSTUDIO_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "modelstudio", - method: "standard-api-key-cn", - choiceId: "modelstudio-standard-api-key-cn", - choiceLabel: "Standard API Key for China (pay-as-you-go)", - choiceHint: "Endpoint: dashscope.aliyuncs.com", - groupId: "modelstudio", - groupLabel: "Qwen (Alibaba Cloud Model Studio)", - groupHint: "Standard / Coding Plan (CN / Global)", - optionKey: "modelstudioStandardApiKeyCn", - cliFlag: "--modelstudio-standard-api-key-cn", - cliOption: "--modelstudio-standard-api-key-cn ", - cliDescription: "Alibaba Cloud Model Studio Standard API key (China)", - }, - { - provider: "modelstudio", - method: "standard-api-key", - choiceId: "modelstudio-standard-api-key", - choiceLabel: "Standard API Key for Global/Intl (pay-as-you-go)", - choiceHint: "Endpoint: dashscope-intl.aliyuncs.com", - groupId: "modelstudio", - groupLabel: "Qwen (Alibaba Cloud Model Studio)", - groupHint: "Standard / Coding Plan (CN / Global)", - optionKey: "modelstudioStandardApiKey", - cliFlag: "--modelstudio-standard-api-key", - cliOption: "--modelstudio-standard-api-key ", - cliDescription: "Alibaba Cloud Model Studio Standard API key (Global/Intl)", - }, - { - provider: "modelstudio", - method: "api-key-cn", - choiceId: "modelstudio-api-key-cn", - choiceLabel: "Coding Plan API Key for China (subscription)", - choiceHint: "Endpoint: coding.dashscope.aliyuncs.com", - groupId: "modelstudio", - groupLabel: "Qwen (Alibaba Cloud Model Studio)", - groupHint: "Standard / Coding Plan (CN / Global)", - optionKey: "modelstudioApiKeyCn", - cliFlag: "--modelstudio-api-key-cn", - cliOption: "--modelstudio-api-key-cn ", - cliDescription: "Alibaba Cloud Model Studio Coding Plan API key (China)", - }, - { - provider: "modelstudio", - method: "api-key", - choiceId: "modelstudio-api-key", - choiceLabel: "Coding Plan API Key for Global/Intl (subscription)", - choiceHint: "Endpoint: coding-intl.dashscope.aliyuncs.com", - groupId: "modelstudio", - groupLabel: "Qwen (Alibaba Cloud Model Studio)", - groupHint: "Standard / Coding Plan (CN / Global)", - optionKey: "modelstudioApiKey", - cliFlag: "--modelstudio-api-key", - cliOption: "--modelstudio-api-key ", - cliDescription: "Alibaba Cloud Model Studio Coding Plan API key (Global/Intl)", - }, - ], - }, - }, - { - dirName: "moonshot", - idHint: "moonshot", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "media-understanding-provider.js", - "onboard.js", - "provider-catalog.js", - "web-search-provider.js", - ], - packageName: "@openclaw/moonshot-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Moonshot provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "moonshot", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - webSearch: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: ["string", "object"], - }, - baseUrl: { - type: "string", - }, - model: { - type: "string", - }, - }, - }, - }, - }, - enabledByDefault: true, - providers: ["moonshot"], - providerAuthEnvVars: { - moonshot: ["MOONSHOT_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "moonshot", - method: "api-key", - choiceId: "moonshot-api-key", - choiceLabel: "Moonshot API key (.ai)", - groupId: "moonshot", - groupLabel: "Moonshot AI (Kimi K2.5)", - groupHint: "Kimi K2.5", - optionKey: "moonshotApiKey", - cliFlag: "--moonshot-api-key", - cliOption: "--moonshot-api-key ", - cliDescription: "Moonshot API key", - }, - { - provider: "moonshot", - method: "api-key-cn", - choiceId: "moonshot-api-key-cn", - choiceLabel: "Moonshot API key (.cn)", - groupId: "moonshot", - groupLabel: "Moonshot AI (Kimi K2.5)", - groupHint: "Kimi K2.5", - optionKey: "moonshotApiKey", - cliFlag: "--moonshot-api-key", - cliOption: "--moonshot-api-key ", - cliDescription: "Moonshot API key", - }, - ], - uiHints: { - "webSearch.apiKey": { - label: "Kimi Search API Key", - help: "Moonshot/Kimi API key (fallback: KIMI_API_KEY or MOONSHOT_API_KEY env var).", - sensitive: true, - }, - "webSearch.baseUrl": { - label: "Kimi Search Base URL", - help: "Kimi base URL override.", - }, - "webSearch.model": { - label: "Kimi Search Model", - help: "Kimi model override.", - }, - }, - contracts: { - mediaUnderstandingProviders: ["moonshot"], - webSearchProviders: ["kimi"], - }, - }, - }, - { - dirName: "msteams", - idHint: "msteams", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "channel-config-api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/msteams", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Microsoft Teams channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "msteams", - label: "Microsoft Teams", - selectionLabel: "Microsoft Teams (Teams SDK)", - docsPath: "/channels/msteams", - docsLabel: "msteams", - blurb: "Teams SDK; enterprise support.", - aliases: ["teams"], - order: 60, - }, - install: { - npmSpec: "@openclaw/msteams", - localPath: "extensions/msteams", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "msteams", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["msteams"], - channelConfigs: { - msteams: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - enabled: { - type: "boolean", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - configWrites: { - type: "boolean", - }, - appId: { - type: "string", - }, - appPassword: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - tenantId: { - type: "string", - }, - webhook: { - type: "object", - properties: { - port: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - path: { - type: "string", - }, - }, - additionalProperties: false, - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - type: "string", - }, - }, - defaultTo: { - type: "string", - }, - groupAllowFrom: { - type: "array", - items: { - type: "string", - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - mediaAllowHosts: { - type: "array", - items: { - type: "string", - }, - }, - mediaAuthAllowHosts: { - type: "array", - items: { - type: "string", - }, - }, - requireMention: { - type: "boolean", - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - replyStyle: { - type: "string", - enum: ["thread", "top-level"], - }, - teams: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - replyStyle: { - type: "string", - enum: ["thread", "top-level"], - }, - channels: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - replyStyle: { - type: "string", - enum: ["thread", "top-level"], - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - sharePointSiteId: { - type: "string", - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - welcomeCard: { - type: "boolean", - }, - promptStarters: { - type: "array", - items: { - type: "string", - }, - }, - groupWelcomeCard: { - type: "boolean", - }, - feedbackEnabled: { - type: "boolean", - }, - feedbackReflection: { - type: "boolean", - }, - feedbackReflectionCooldownMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - uiHints: { - "": { - label: "MS Teams", - help: "Microsoft Teams channel provider configuration and provider-specific policy toggles. Use this section to isolate Teams behavior from other enterprise chat providers.", - }, - configWrites: { - label: "MS Teams Config Writes", - help: "Allow Microsoft Teams to write config in response to channel events/commands (default: true).", - }, - }, - label: "Microsoft Teams", - description: "Teams SDK; enterprise support.", - }, - }, - }, - }, - { - dirName: "nextcloud-talk", - idHint: "nextcloud-talk", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/nextcloud-talk", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Nextcloud Talk channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "nextcloud-talk", - label: "Nextcloud Talk", - selectionLabel: "Nextcloud Talk (self-hosted)", - docsPath: "/channels/nextcloud-talk", - docsLabel: "nextcloud-talk", - blurb: "Self-hosted chat via Nextcloud Talk webhook bots.", - aliases: ["nc-talk", "nc"], - order: 65, - quickstartAllowFrom: true, - }, - install: { - npmSpec: "@openclaw/nextcloud-talk", - localPath: "extensions/nextcloud-talk", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "nextcloud-talk", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["nextcloud-talk"], - channelConfigs: { - "nextcloud-talk": { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - baseUrl: { - type: "string", - }, - botSecret: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - botSecretFile: { - type: "string", - }, - apiUser: { - type: "string", - }, - apiPassword: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - apiPasswordFile: { - type: "string", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - webhookPort: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - webhookHost: { - type: "string", - }, - webhookPath: { - type: "string", - }, - webhookPublicUrl: { - type: "string", - }, - allowFrom: { - type: "array", - items: { - type: "string", - }, - }, - groupAllowFrom: { - type: "array", - items: { - type: "string", - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - rooms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - type: "string", - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - allowPrivateNetwork: { - type: "boolean", - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - baseUrl: { - type: "string", - }, - botSecret: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - botSecretFile: { - type: "string", - }, - apiUser: { - type: "string", - }, - apiPassword: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - apiPasswordFile: { - type: "string", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - webhookPort: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - webhookHost: { - type: "string", - }, - webhookPath: { - type: "string", - }, - webhookPublicUrl: { - type: "string", - }, - allowFrom: { - type: "array", - items: { - type: "string", - }, - }, - groupAllowFrom: { - type: "array", - items: { - type: "string", - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - rooms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - type: "string", - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - allowPrivateNetwork: { - type: "boolean", - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - label: "Nextcloud Talk", - description: "Self-hosted chat via Nextcloud Talk webhook bots.", - }, - }, - }, - }, - { - dirName: "nostr", - idHint: "nostr", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js", "setup-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/nostr", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Nostr channel plugin for NIP-04 encrypted DMs", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "nostr", - label: "Nostr", - selectionLabel: "Nostr (NIP-04 DMs)", - docsPath: "/channels/nostr", - docsLabel: "nostr", - blurb: "Decentralized protocol; encrypted DMs via NIP-04.", - order: 55, - quickstartAllowFrom: true, - }, - install: { - npmSpec: "@openclaw/nostr", - localPath: "extensions/nostr", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "nostr", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["nostr"], - channelConfigs: { - nostr: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - defaultAccount: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - privateKey: { - type: "string", - }, - relays: { - type: "array", - items: { - type: "string", - }, - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - profile: { - type: "object", - properties: { - name: { - type: "string", - maxLength: 256, - }, - displayName: { - type: "string", - maxLength: 256, - }, - about: { - type: "string", - maxLength: 2000, - }, - picture: { - type: "string", - format: "uri", - }, - banner: { - type: "string", - format: "uri", - }, - website: { - type: "string", - format: "uri", - }, - nip05: { - type: "string", - }, - lud16: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - label: "Nostr", - description: "Decentralized protocol; encrypted DMs via NIP-04.", - }, - }, - }, - }, - { - dirName: "nvidia", - idHint: "nvidia", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "provider-catalog.js"], - packageName: "@openclaw/nvidia-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw NVIDIA provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "nvidia", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["nvidia"], - providerAuthEnvVars: { - nvidia: ["NVIDIA_API_KEY"], - }, - }, - }, - { - dirName: "ollama", - idHint: "ollama", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/ollama-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Ollama provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "ollama", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["ollama"], - providerAuthEnvVars: { - ollama: ["OLLAMA_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "ollama", - method: "local", - choiceId: "ollama", - choiceLabel: "Ollama", - choiceHint: "Cloud and local open models", - groupId: "ollama", - groupLabel: "Ollama", - groupHint: "Cloud and local open models", - }, - ], - }, - }, - { - dirName: "open-prose", - idHint: "open-prose", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/open-prose", - packageVersion: "2026.3.28", - packageDescription: "OpenProse VM skill pack plugin (slash command + telemetry).", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "open-prose", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - skills: ["./skills"], - name: "OpenProse", - description: "OpenProse VM skill pack with a /prose slash command.", - }, - }, - { - dirName: "openai", - idHint: "openai", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "cli-backend.js", - "default-models.js", - "image-generation-provider.js", - "media-understanding-provider.js", - "openai-codex-auth-identity.js", - "openai-codex-catalog.js", - "openai-codex-provider.js", - "openai-codex-provider.runtime.js", - "openai-provider.js", - "shared.js", - "speech-provider.js", - "tts.js", - ], - packageName: "@openclaw/openai-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw OpenAI provider plugins", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "openai", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["openai", "openai-codex"], - cliBackends: ["codex-cli"], - providerAuthEnvVars: { - openai: ["OPENAI_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "openai-codex", - method: "oauth", - choiceId: "openai-codex", - deprecatedChoiceIds: ["codex-cli"], - choiceLabel: "OpenAI Codex (ChatGPT OAuth)", - choiceHint: "Browser sign-in", - groupId: "openai", - groupLabel: "OpenAI", - groupHint: "Codex OAuth + API key", - }, - { - provider: "openai", - method: "api-key", - choiceId: "openai-api-key", - choiceLabel: "OpenAI API key", - groupId: "openai", - groupLabel: "OpenAI", - groupHint: "Codex OAuth + API key", - optionKey: "openaiApiKey", - cliFlag: "--openai-api-key", - cliOption: "--openai-api-key ", - cliDescription: "OpenAI API key", - }, - ], - contracts: { - speechProviders: ["openai"], - mediaUnderstandingProviders: ["openai", "openai-codex"], - imageGenerationProviders: ["openai"], - }, - }, - }, - { - dirName: "opencode", - idHint: "opencode", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "onboard.js"], - packageName: "@openclaw/opencode-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw OpenCode Zen provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "opencode", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["opencode"], - providerAuthEnvVars: { - opencode: ["OPENCODE_API_KEY", "OPENCODE_ZEN_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "opencode", - method: "api-key", - choiceId: "opencode-zen", - choiceLabel: "OpenCode Zen catalog", - groupId: "opencode", - groupLabel: "OpenCode", - groupHint: "Shared API key for Zen + Go catalogs", - optionKey: "opencodeZenApiKey", - cliFlag: "--opencode-zen-api-key", - cliOption: "--opencode-zen-api-key ", - cliDescription: "OpenCode API key (Zen catalog)", - }, - ], - }, - }, - { - dirName: "opencode-go", - idHint: "opencode-go", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "onboard.js"], - packageName: "@openclaw/opencode-go-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw OpenCode Go provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "opencode-go", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["opencode-go"], - providerAuthEnvVars: { - "opencode-go": ["OPENCODE_API_KEY", "OPENCODE_ZEN_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "opencode-go", - method: "api-key", - choiceId: "opencode-go", - choiceLabel: "OpenCode Go catalog", - groupId: "opencode", - groupLabel: "OpenCode", - groupHint: "Shared API key for Zen + Go catalogs", - optionKey: "opencodeGoApiKey", - cliFlag: "--opencode-go-api-key", - cliOption: "--opencode-go-api-key ", - cliDescription: "OpenCode API key (Go catalog)", - }, - ], - }, - }, - { - dirName: "openrouter", - idHint: "openrouter", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "media-understanding-provider.js", - "onboard.js", - "provider-catalog.js", - ], - packageName: "@openclaw/openrouter-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw OpenRouter provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "openrouter", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["openrouter"], - providerAuthEnvVars: { - openrouter: ["OPENROUTER_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "openrouter", - method: "api-key", - choiceId: "openrouter-api-key", - choiceLabel: "OpenRouter API key", - groupId: "openrouter", - groupLabel: "OpenRouter", - groupHint: "API key", - optionKey: "openrouterApiKey", - cliFlag: "--openrouter-api-key", - cliOption: "--openrouter-api-key ", - cliDescription: "OpenRouter API key", - }, - ], - contracts: { - mediaUnderstandingProviders: ["openrouter"], - }, - }, - }, - { - dirName: "openshell", - idHint: "openshell", - source: { - source: "./index.ts", - built: "index.js", - }, - packageName: "@openclaw/openshell-sandbox", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw OpenShell sandbox backend", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "openshell", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - mode: { - type: "string", - enum: ["mirror", "remote"], - }, - command: { - type: "string", - minLength: 1, - }, - gateway: { - type: "string", - minLength: 1, - }, - gatewayEndpoint: { - type: "string", - minLength: 1, - }, - from: { - type: "string", - minLength: 1, - }, - policy: { - type: "string", - minLength: 1, - }, - providers: { - type: "array", - items: { - type: "string", - minLength: 1, - }, - }, - gpu: { - type: "boolean", - }, - autoProviders: { - type: "boolean", - }, - remoteWorkspaceDir: { - type: "string", - minLength: 1, - }, - remoteAgentWorkspaceDir: { - type: "string", - minLength: 1, - }, - timeoutSeconds: { - type: "number", - minimum: 1, - }, - }, - }, - name: "OpenShell Sandbox", - description: - "Sandbox backend powered by OpenShell with mirrored local workspaces and SSH-based command execution.", - uiHints: { - mode: { - label: "Mode", - help: "Sandbox mode. Use mirror for the default local-workspace flow or remote for a fully remote workspace.", - }, - command: { - label: "OpenShell Command", - help: "Path or command name for the openshell CLI.", - }, - gateway: { - label: "Gateway Name", - help: "Optional OpenShell gateway name passed as --gateway.", - }, - gatewayEndpoint: { - label: "Gateway Endpoint", - help: "Optional OpenShell gateway endpoint passed as --gateway-endpoint.", - }, - from: { - label: "Sandbox Source", - help: "OpenShell sandbox source for first-time create. Defaults to openclaw.", - }, - policy: { - label: "Policy File", - help: "Optional path to a custom OpenShell sandbox policy YAML.", - }, - providers: { - label: "Providers", - help: "Provider names to attach when a sandbox is created.", - }, - gpu: { - label: "GPU", - help: "Request GPU resources when creating the sandbox.", - advanced: true, - }, - autoProviders: { - label: "Auto-create Providers", - help: "When enabled, pass --auto-providers during sandbox create.", - advanced: true, - }, - remoteWorkspaceDir: { - label: "Remote Workspace Dir", - help: "Primary writable workspace inside the OpenShell sandbox.", - advanced: true, - }, - remoteAgentWorkspaceDir: { - label: "Remote Agent Dir", - help: "Mirror path for the real agent workspace when workspaceAccess is read-only.", - advanced: true, - }, - timeoutSeconds: { - label: "Command Timeout Seconds", - help: "Timeout for openshell CLI operations such as create/upload/download.", - advanced: true, - }, - }, - }, - }, - { - dirName: "perplexity", - idHint: "perplexity", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["web-search-provider.js"], - packageName: "@openclaw/perplexity-plugin", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Perplexity plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "perplexity", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - webSearch: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: ["string", "object"], - }, - baseUrl: { - type: "string", - }, - model: { - type: "string", - }, - }, - }, - }, - }, - providerAuthEnvVars: { - perplexity: ["PERPLEXITY_API_KEY", "OPENROUTER_API_KEY"], - }, - uiHints: { - "webSearch.apiKey": { - label: "Perplexity API Key", - help: "Perplexity or OpenRouter API key for web search.", - sensitive: true, - placeholder: "pplx-...", - }, - "webSearch.baseUrl": { - label: "Perplexity Base URL", - help: "Optional Perplexity/OpenRouter chat-completions base URL override.", - }, - "webSearch.model": { - label: "Perplexity Model", - help: "Optional Sonar/OpenRouter model override.", - }, - }, - contracts: { - webSearchProviders: ["perplexity"], - }, - }, - }, - { - dirName: "qianfan", - idHint: "qianfan", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/qianfan-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Qianfan provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "qianfan", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["qianfan"], - providerAuthEnvVars: { - qianfan: ["QIANFAN_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "qianfan", - method: "api-key", - choiceId: "qianfan-api-key", - choiceLabel: "Qianfan API key", - groupId: "qianfan", - groupLabel: "Qianfan", - groupHint: "API key", - optionKey: "qianfanApiKey", - cliFlag: "--qianfan-api-key", - cliOption: "--qianfan-api-key ", - cliDescription: "QIANFAN API key", - }, - ], - }, - }, - { - dirName: "sglang", - idHint: "sglang", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "defaults.js", "models.js"], - packageName: "@openclaw/sglang-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw SGLang provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "sglang", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["sglang"], - providerAuthEnvVars: { - sglang: ["SGLANG_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "sglang", - method: "custom", - choiceId: "sglang", - choiceLabel: "SGLang", - choiceHint: "Fast self-hosted OpenAI-compatible server", - groupId: "sglang", - groupLabel: "SGLang", - groupHint: "Fast self-hosted server", - }, - ], - }, - }, - { - dirName: "signal", - idHint: "signal", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "channel-config-api.js", - "reaction-runtime-api.js", - "runtime-api.js", - ], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/signal", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Signal channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "signal", - label: "Signal", - selectionLabel: "Signal (signal-cli)", - detailLabel: "Signal REST", - docsPath: "/channels/signal", - docsLabel: "signal", - blurb: 'signal-cli linked device; more setup (David Reagans: "Hop on Discord.").', - systemImage: "antenna.radiowaves.left.and.right", - markdownCapable: true, - }, - }, - manifest: { - id: "signal", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["signal"], - channelConfigs: { - signal: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - configWrites: { - type: "boolean", - }, - account: { - type: "string", - }, - accountUuid: { - type: "string", - }, - httpUrl: { - type: "string", - }, - httpHost: { - type: "string", - }, - httpPort: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - cliPath: { - type: "string", - }, - autoStart: { - type: "boolean", - }, - startupTimeoutMs: { - type: "integer", - minimum: 1000, - maximum: 120000, - }, - receiveMode: { - anyOf: [ - { - type: "string", - const: "on-start", - }, - { - type: "string", - const: "manual", - }, - ], - }, - ignoreAttachments: { - type: "boolean", - }, - ignoreStories: { - type: "boolean", - }, - sendReadReceipts: { - type: "boolean", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - type: "string", - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - mediaMaxMb: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - reactionNotifications: { - type: "string", - enum: ["off", "own", "all", "allowlist"], - }, - reactionAllowlist: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - reactionLevel: { - type: "string", - enum: ["off", "ack", "minimal", "extensive"], - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - configWrites: { - type: "boolean", - }, - account: { - type: "string", - }, - accountUuid: { - type: "string", - }, - httpUrl: { - type: "string", - }, - httpHost: { - type: "string", - }, - httpPort: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - cliPath: { - type: "string", - }, - autoStart: { - type: "boolean", - }, - startupTimeoutMs: { - type: "integer", - minimum: 1000, - maximum: 120000, - }, - receiveMode: { - anyOf: [ - { - type: "string", - const: "on-start", - }, - { - type: "string", - const: "manual", - }, - ], - }, - ignoreAttachments: { - type: "boolean", - }, - ignoreStories: { - type: "boolean", - }, - sendReadReceipts: { - type: "boolean", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - type: "string", - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - mediaMaxMb: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - reactionNotifications: { - type: "string", - enum: ["off", "own", "all", "allowlist"], - }, - reactionAllowlist: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - reactionLevel: { - type: "string", - enum: ["off", "ack", "minimal", "extensive"], - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - uiHints: { - "": { - label: "Signal", - help: "Signal channel provider configuration including account identity and DM policy behavior. Keep account mapping explicit so routing remains stable across multi-device setups.", - }, - dmPolicy: { - label: "Signal DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.signal.allowFrom=["*"].', - }, - configWrites: { - label: "Signal Config Writes", - help: "Allow Signal to write config in response to channel events/commands (default: true).", - }, - account: { - label: "Signal Account", - help: "Signal account identifier (phone/number handle) used to bind this channel config to a specific Signal identity. Keep this aligned with your linked device/session state.", - }, - }, - label: "Signal", - description: 'signal-cli linked device; more setup (David Reagans: "Hop on Discord.").', - }, - }, - }, - }, - { - dirName: "slack", - idHint: "slack", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "channel-config-api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/slack", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Slack channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "slack", - label: "Slack", - selectionLabel: "Slack (Socket Mode)", - detailLabel: "Slack Bot", - docsPath: "/channels/slack", - docsLabel: "slack", - blurb: "supported (Socket Mode).", - systemImage: "number", - markdownCapable: true, - }, - }, - manifest: { - id: "slack", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["slack"], - channelConfigs: { - slack: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - mode: { - default: "socket", - type: "string", - enum: ["socket", "http"], - }, - signingSecret: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - webhookPath: { - default: "/slack/events", - type: "string", - }, - capabilities: { - anyOf: [ - { - type: "array", - items: { - type: "string", - }, - }, - { - type: "object", - properties: { - interactiveReplies: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - ], - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - commands: { - type: "object", - properties: { - native: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - nativeSkills: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - }, - additionalProperties: false, - }, - configWrites: { - type: "boolean", - }, - botToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - appToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - userToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - userTokenReadOnly: { - default: true, - type: "boolean", - }, - allowBots: { - type: "boolean", - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - streaming: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - enum: ["off", "partial", "block", "progress"], - }, - ], - }, - nativeStreaming: { - type: "boolean", - }, - streamMode: { - type: "string", - enum: ["replace", "status_final", "append"], - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - reactionNotifications: { - type: "string", - enum: ["off", "own", "all", "allowlist"], - }, - reactionAllowlist: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - replyToModeByChatType: { - type: "object", - properties: { - direct: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - group: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - channel: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - }, - additionalProperties: false, - }, - thread: { - type: "object", - properties: { - historyScope: { - type: "string", - enum: ["thread", "channel"], - }, - inheritParent: { - type: "boolean", - }, - initialHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - messages: { - type: "boolean", - }, - pins: { - type: "boolean", - }, - search: { - type: "boolean", - }, - permissions: { - type: "boolean", - }, - memberInfo: { - type: "boolean", - }, - channelInfo: { - type: "boolean", - }, - emojiList: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - slashCommand: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - name: { - type: "string", - }, - sessionPrefix: { - type: "string", - }, - ephemeral: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - type: "string", - }, - dm: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - policy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupEnabled: { - type: "boolean", - }, - groupChannels: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - }, - additionalProperties: false, - }, - channels: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allow: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - allowBots: { - type: "boolean", - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - ackReaction: { - type: "string", - }, - typingReaction: { - type: "string", - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - mode: { - type: "string", - enum: ["socket", "http"], - }, - signingSecret: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - webhookPath: { - type: "string", - }, - capabilities: { - anyOf: [ - { - type: "array", - items: { - type: "string", - }, - }, - { - type: "object", - properties: { - interactiveReplies: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - ], - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - commands: { - type: "object", - properties: { - native: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - nativeSkills: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - }, - additionalProperties: false, - }, - configWrites: { - type: "boolean", - }, - botToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - appToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - userToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - userTokenReadOnly: { - default: true, - type: "boolean", - }, - allowBots: { - type: "boolean", - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - streaming: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - enum: ["off", "partial", "block", "progress"], - }, - ], - }, - nativeStreaming: { - type: "boolean", - }, - streamMode: { - type: "string", - enum: ["replace", "status_final", "append"], - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - reactionNotifications: { - type: "string", - enum: ["off", "own", "all", "allowlist"], - }, - reactionAllowlist: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - replyToModeByChatType: { - type: "object", - properties: { - direct: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - group: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - channel: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - }, - additionalProperties: false, - }, - thread: { - type: "object", - properties: { - historyScope: { - type: "string", - enum: ["thread", "channel"], - }, - inheritParent: { - type: "boolean", - }, - initialHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - messages: { - type: "boolean", - }, - pins: { - type: "boolean", - }, - search: { - type: "boolean", - }, - permissions: { - type: "boolean", - }, - memberInfo: { - type: "boolean", - }, - channelInfo: { - type: "boolean", - }, - emojiList: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - slashCommand: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - name: { - type: "string", - }, - sessionPrefix: { - type: "string", - }, - ephemeral: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - type: "string", - }, - dm: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - policy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupEnabled: { - type: "boolean", - }, - groupChannels: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - }, - additionalProperties: false, - }, - channels: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - allow: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - allowBots: { - type: "boolean", - }, - users: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - systemPrompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - responsePrefix: { - type: "string", - }, - ackReaction: { - type: "string", - }, - typingReaction: { - type: "string", - }, - }, - required: ["userTokenReadOnly"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["mode", "webhookPath", "userTokenReadOnly", "groupPolicy"], - additionalProperties: false, - }, - uiHints: { - "": { - label: "Slack", - help: "Slack channel provider configuration for bot/app tokens, streaming behavior, and DM policy controls. Keep token handling and thread behavior explicit to avoid noisy workspace interactions.", - }, - "dm.policy": { - label: "Slack DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.slack.allowFrom=["*"] (legacy: channels.slack.dm.allowFrom).', - }, - dmPolicy: { - label: "Slack DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.slack.allowFrom=["*"].', - }, - configWrites: { - label: "Slack Config Writes", - help: "Allow Slack to write config in response to channel events/commands (default: true).", - }, - "commands.native": { - label: "Slack Native Commands", - help: 'Override native commands for Slack (bool or "auto").', - }, - "commands.nativeSkills": { - label: "Slack Native Skill Commands", - help: 'Override native skill commands for Slack (bool or "auto").', - }, - allowBots: { - label: "Slack Allow Bot Messages", - help: "Allow bot-authored messages to trigger Slack replies (default: false).", - }, - botToken: { - label: "Slack Bot Token", - help: "Slack bot token used for standard chat actions in the configured workspace. Keep this credential scoped and rotate if workspace app permissions change.", - }, - appToken: { - label: "Slack App Token", - help: "Slack app-level token used for Socket Mode connections and event transport when enabled. Use least-privilege app scopes and store this token as a secret.", - }, - userToken: { - label: "Slack User Token", - help: "Optional Slack user token for workflows requiring user-context API access beyond bot permissions. Use sparingly and audit scopes because this token can carry broader authority.", - }, - userTokenReadOnly: { - label: "Slack User Token Read Only", - help: "When true, treat configured Slack user token usage as read-only helper behavior where possible. Keep enabled if you only need supplemental reads without user-context writes.", - }, - "capabilities.interactiveReplies": { - label: "Slack Interactive Replies", - help: "Enable agent-authored Slack interactive reply directives (`[[slack_buttons: ...]]`, `[[slack_select: ...]]`). Default: false.", - }, - streaming: { - label: "Slack Streaming Mode", - help: 'Unified Slack stream preview mode: "off" | "partial" | "block" | "progress". Legacy boolean/streamMode keys are auto-mapped.', - }, - nativeStreaming: { - label: "Slack Native Streaming", - help: "Enable native Slack text streaming (chat.startStream/chat.appendStream/chat.stopStream) when channels.slack.streaming is partial (default: true).", - }, - streamMode: { - label: "Slack Stream Mode (Legacy)", - help: "Legacy Slack preview mode alias (replace | status_final | append); auto-migrated to channels.slack.streaming.", - }, - "thread.historyScope": { - label: "Slack Thread History Scope", - help: 'Scope for Slack thread history context ("thread" isolates per thread; "channel" reuses channel history).', - }, - "thread.inheritParent": { - label: "Slack Thread Parent Inheritance", - help: "If true, Slack thread sessions inherit the parent channel transcript (default: false).", - }, - "thread.initialHistoryLimit": { - label: "Slack Thread Initial History Limit", - help: "Maximum number of existing Slack thread messages to fetch when starting a new thread session (default: 20, set to 0 to disable).", - }, - }, - label: "Slack", - description: "supported (Socket Mode).", - }, - }, - }, - }, - { - dirName: "synology-chat", - idHint: "synology-chat", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["setup-api.js"], - packageName: "@openclaw/synology-chat", - packageVersion: "2026.3.28", - packageDescription: "Synology Chat channel plugin for OpenClaw", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "synology-chat", - label: "Synology Chat", - selectionLabel: "Synology Chat (Webhook)", - docsPath: "/channels/synology-chat", - docsLabel: "synology-chat", - blurb: "Connect your Synology NAS Chat to OpenClaw with full agent capabilities.", - order: 90, - }, - install: { - npmSpec: "@openclaw/synology-chat", - localPath: "extensions/synology-chat", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "synology-chat", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["synology-chat"], - channelConfigs: { - "synology-chat": { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - dangerouslyAllowNameMatching: { - type: "boolean", - }, - dangerouslyAllowInheritedWebhookPath: { - type: "boolean", - }, - }, - additionalProperties: {}, - }, - label: "Synology Chat", - description: "Connect your Synology NAS Chat to OpenClaw with full agent capabilities.", - }, - }, - }, - }, - { - dirName: "synthetic", - idHint: "synthetic", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/synthetic-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Synthetic provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "synthetic", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["synthetic"], - providerAuthEnvVars: { - synthetic: ["SYNTHETIC_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "synthetic", - method: "api-key", - choiceId: "synthetic-api-key", - choiceLabel: "Synthetic API key", - groupId: "synthetic", - groupLabel: "Synthetic", - groupHint: "Anthropic-compatible (multi-model)", - optionKey: "syntheticApiKey", - cliFlag: "--synthetic-api-key", - cliOption: "--synthetic-api-key ", - cliDescription: "Synthetic API key", - }, - ], - }, - }, - { - dirName: "tavily", - idHint: "tavily", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["web-search-provider.js"], - packageName: "@openclaw/tavily-plugin", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Tavily plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "tavily", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - webSearch: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: ["string", "object"], - }, - baseUrl: { - type: "string", - }, - }, - }, - }, - }, - providerAuthEnvVars: { - tavily: ["TAVILY_API_KEY"], - }, - skills: ["./skills"], - uiHints: { - "webSearch.apiKey": { - label: "Tavily API Key", - help: "Tavily API key for web search and extraction (fallback: TAVILY_API_KEY env var).", - sensitive: true, - placeholder: "tvly-...", - }, - "webSearch.baseUrl": { - label: "Tavily Base URL", - help: "Tavily API base URL override.", - }, - }, - contracts: { - webSearchProviders: ["tavily"], - tools: ["tavily_search", "tavily_extract"], - }, - }, - }, - { - dirName: "telegram", - idHint: "telegram", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: [ - "allow-from.js", - "api.js", - "channel-config-api.js", - "runtime-api.js", - "update-offset-runtime-api.js", - ], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/telegram", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Telegram channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "telegram", - label: "Telegram", - selectionLabel: "Telegram (Bot API)", - detailLabel: "Telegram Bot", - docsPath: "/channels/telegram", - docsLabel: "telegram", - blurb: "simplest way to get started — register a bot with @BotFather and get going.", - systemImage: "paperplane", - selectionDocsPrefix: "", - selectionDocsOmitLabel: true, - selectionExtras: ["https://openclaw.ai"], - markdownCapable: true, - }, - }, - manifest: { - id: "telegram", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["telegram"], - channelConfigs: { - telegram: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - anyOf: [ - { - type: "array", - items: { - type: "string", - }, - }, - { - type: "object", - properties: { - inlineButtons: { - type: "string", - enum: ["off", "dm", "group", "all", "allowlist"], - }, - }, - additionalProperties: false, - }, - ], - }, - execApprovals: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - approvers: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - agentFilter: { - type: "array", - items: { - type: "string", - }, - }, - sessionFilter: { - type: "array", - items: { - type: "string", - }, - }, - target: { - type: "string", - enum: ["dm", "channel", "both"], - }, - }, - additionalProperties: false, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - commands: { - type: "object", - properties: { - native: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - nativeSkills: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - }, - additionalProperties: false, - }, - customCommands: { - type: "array", - items: { - type: "object", - properties: { - command: { - type: "string", - }, - description: { - type: "string", - }, - }, - required: ["command", "description"], - additionalProperties: false, - }, - }, - configWrites: { - type: "boolean", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - botToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - tokenFile: { - type: "string", - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - disableAudioPreflight: { - type: "boolean", - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - topics: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - disableAudioPreflight: { - type: "boolean", - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - agentId: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - direct: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - topics: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - disableAudioPreflight: { - type: "boolean", - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - agentId: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - requireTopic: { - type: "boolean", - }, - autoTopicLabel: { - anyOf: [ - { - type: "boolean", - }, - { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - prompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - ], - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - streaming: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - enum: ["off", "partial", "block", "progress"], - }, - ], - }, - blockStreaming: { - type: "boolean", - }, - draftChunk: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - breakPreference: { - anyOf: [ - { - type: "string", - const: "paragraph", - }, - { - type: "string", - const: "newline", - }, - { - type: "string", - const: "sentence", - }, - ], - }, - }, - additionalProperties: false, - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - streamMode: { - type: "string", - enum: ["off", "partial", "block"], - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - timeoutSeconds: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - retry: { - type: "object", - properties: { - attempts: { - type: "integer", - minimum: 1, - maximum: 9007199254740991, - }, - minDelayMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - maxDelayMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - jitter: { - type: "number", - minimum: 0, - maximum: 1, - }, - }, - additionalProperties: false, - }, - network: { - type: "object", - properties: { - autoSelectFamily: { - type: "boolean", - }, - dnsResultOrder: { - type: "string", - enum: ["ipv4first", "verbatim"], - }, - }, - additionalProperties: false, - }, - proxy: { - type: "string", - }, - webhookUrl: { - description: - "Public HTTPS webhook URL registered with Telegram for inbound updates. This must be internet-reachable and requires channels.telegram.webhookSecret.", - type: "string", - }, - webhookSecret: { - description: - "Secret token sent to Telegram during webhook registration and verified on inbound webhook requests. Telegram returns this value for verification; this is not the gateway auth token and not the bot token.", - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - webhookPath: { - description: - "Local webhook route path served by the gateway listener. Defaults to /telegram-webhook.", - type: "string", - }, - webhookHost: { - description: - "Local bind host for the webhook listener. Defaults to 127.0.0.1; keep loopback unless you intentionally expose direct ingress.", - type: "string", - }, - webhookPort: { - description: - "Local bind port for the webhook listener. Defaults to 8787; set to 0 to let the OS assign an ephemeral port.", - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - webhookCertPath: { - description: - "Path to the self-signed certificate (PEM) to upload to Telegram during webhook registration. Required for self-signed certs (direct IP or no domain).", - type: "string", - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - sendMessage: { - type: "boolean", - }, - poll: { - type: "boolean", - }, - deleteMessage: { - type: "boolean", - }, - editMessage: { - type: "boolean", - }, - sticker: { - type: "boolean", - }, - createForumTopic: { - type: "boolean", - }, - editForumTopic: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - threadBindings: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - idleHours: { - type: "number", - minimum: 0, - }, - maxAgeHours: { - type: "number", - minimum: 0, - }, - spawnSubagentSessions: { - type: "boolean", - }, - spawnAcpSessions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - reactionNotifications: { - type: "string", - enum: ["off", "own", "all"], - }, - reactionLevel: { - type: "string", - enum: ["off", "ack", "minimal", "extensive"], - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - linkPreview: { - type: "boolean", - }, - silentErrorReplies: { - type: "boolean", - }, - responsePrefix: { - type: "string", - }, - ackReaction: { - type: "string", - }, - apiRoot: { - type: "string", - format: "uri", - }, - autoTopicLabel: { - anyOf: [ - { - type: "boolean", - }, - { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - prompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - ], - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - capabilities: { - anyOf: [ - { - type: "array", - items: { - type: "string", - }, - }, - { - type: "object", - properties: { - inlineButtons: { - type: "string", - enum: ["off", "dm", "group", "all", "allowlist"], - }, - }, - additionalProperties: false, - }, - ], - }, - execApprovals: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - approvers: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - agentFilter: { - type: "array", - items: { - type: "string", - }, - }, - sessionFilter: { - type: "array", - items: { - type: "string", - }, - }, - target: { - type: "string", - enum: ["dm", "channel", "both"], - }, - }, - additionalProperties: false, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - enabled: { - type: "boolean", - }, - commands: { - type: "object", - properties: { - native: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - nativeSkills: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - const: "auto", - }, - ], - }, - }, - additionalProperties: false, - }, - customCommands: { - type: "array", - items: { - type: "object", - properties: { - command: { - type: "string", - }, - description: { - type: "string", - }, - }, - required: ["command", "description"], - additionalProperties: false, - }, - }, - configWrites: { - type: "boolean", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - botToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - tokenFile: { - type: "string", - }, - replyToMode: { - anyOf: [ - { - type: "string", - const: "off", - }, - { - type: "string", - const: "first", - }, - { - type: "string", - const: "all", - }, - ], - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - disableAudioPreflight: { - type: "boolean", - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - topics: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - disableAudioPreflight: { - type: "boolean", - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - agentId: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - defaultTo: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - direct: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - topics: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - disableAudioPreflight: { - type: "boolean", - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - skills: { - type: "array", - items: { - type: "string", - }, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - systemPrompt: { - type: "string", - }, - agentId: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - requireTopic: { - type: "boolean", - }, - autoTopicLabel: { - anyOf: [ - { - type: "boolean", - }, - { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - prompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - ], - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - streaming: { - anyOf: [ - { - type: "boolean", - }, - { - type: "string", - enum: ["off", "partial", "block", "progress"], - }, - ], - }, - blockStreaming: { - type: "boolean", - }, - draftChunk: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - breakPreference: { - anyOf: [ - { - type: "string", - const: "paragraph", - }, - { - type: "string", - const: "newline", - }, - { - type: "string", - const: "sentence", - }, - ], - }, - }, - additionalProperties: false, - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - streamMode: { - type: "string", - enum: ["off", "partial", "block"], - }, - mediaMaxMb: { - type: "number", - exclusiveMinimum: 0, - }, - timeoutSeconds: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - retry: { - type: "object", - properties: { - attempts: { - type: "integer", - minimum: 1, - maximum: 9007199254740991, - }, - minDelayMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - maxDelayMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - jitter: { - type: "number", - minimum: 0, - maximum: 1, - }, - }, - additionalProperties: false, - }, - network: { - type: "object", - properties: { - autoSelectFamily: { - type: "boolean", - }, - dnsResultOrder: { - type: "string", - enum: ["ipv4first", "verbatim"], - }, - }, - additionalProperties: false, - }, - proxy: { - type: "string", - }, - webhookUrl: { - description: - "Public HTTPS webhook URL registered with Telegram for inbound updates. This must be internet-reachable and requires channels.telegram.webhookSecret.", - type: "string", - }, - webhookSecret: { - description: - "Secret token sent to Telegram during webhook registration and verified on inbound webhook requests. Telegram returns this value for verification; this is not the gateway auth token and not the bot token.", - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - webhookPath: { - description: - "Local webhook route path served by the gateway listener. Defaults to /telegram-webhook.", - type: "string", - }, - webhookHost: { - description: - "Local bind host for the webhook listener. Defaults to 127.0.0.1; keep loopback unless you intentionally expose direct ingress.", - type: "string", - }, - webhookPort: { - description: - "Local bind port for the webhook listener. Defaults to 8787; set to 0 to let the OS assign an ephemeral port.", - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - webhookCertPath: { - description: - "Path to the self-signed certificate (PEM) to upload to Telegram during webhook registration. Required for self-signed certs (direct IP or no domain).", - type: "string", - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - sendMessage: { - type: "boolean", - }, - poll: { - type: "boolean", - }, - deleteMessage: { - type: "boolean", - }, - editMessage: { - type: "boolean", - }, - sticker: { - type: "boolean", - }, - createForumTopic: { - type: "boolean", - }, - editForumTopic: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - threadBindings: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - idleHours: { - type: "number", - minimum: 0, - }, - maxAgeHours: { - type: "number", - minimum: 0, - }, - spawnSubagentSessions: { - type: "boolean", - }, - spawnAcpSessions: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - reactionNotifications: { - type: "string", - enum: ["off", "own", "all"], - }, - reactionLevel: { - type: "string", - enum: ["off", "ack", "minimal", "extensive"], - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - linkPreview: { - type: "boolean", - }, - silentErrorReplies: { - type: "boolean", - }, - responsePrefix: { - type: "string", - }, - ackReaction: { - type: "string", - }, - apiRoot: { - type: "string", - format: "uri", - }, - autoTopicLabel: { - anyOf: [ - { - type: "boolean", - }, - { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - prompt: { - type: "string", - }, - }, - additionalProperties: false, - }, - ], - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["dmPolicy", "groupPolicy"], - additionalProperties: false, - }, - uiHints: { - "": { - label: "Telegram", - help: "Telegram channel provider configuration including auth tokens, retry behavior, and message rendering controls. Use this section to tune bot behavior for Telegram-specific API semantics.", - }, - customCommands: { - label: "Telegram Custom Commands", - help: "Additional Telegram bot menu commands (merged with native; conflicts ignored).", - }, - botToken: { - label: "Telegram Bot Token", - help: "Telegram bot token used to authenticate Bot API requests for this account/provider config. Use secret/env substitution and rotate tokens if exposure is suspected.", - }, - dmPolicy: { - label: "Telegram DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.telegram.allowFrom=["*"].', - }, - configWrites: { - label: "Telegram Config Writes", - help: "Allow Telegram to write config in response to channel events/commands (default: true).", - }, - "commands.native": { - label: "Telegram Native Commands", - help: 'Override native commands for Telegram (bool or "auto").', - }, - "commands.nativeSkills": { - label: "Telegram Native Skill Commands", - help: 'Override native skill commands for Telegram (bool or "auto").', - }, - streaming: { - label: "Telegram Streaming Mode", - help: 'Unified Telegram stream preview mode: "off" | "partial" | "block" | "progress" (default: "partial"). "progress" maps to "partial" on Telegram. Legacy boolean/streamMode keys are auto-mapped.', - }, - "retry.attempts": { - label: "Telegram Retry Attempts", - help: "Max retry attempts for outbound Telegram API calls (default: 3).", - }, - "retry.minDelayMs": { - label: "Telegram Retry Min Delay (ms)", - help: "Minimum retry delay in ms for Telegram outbound calls.", - }, - "retry.maxDelayMs": { - label: "Telegram Retry Max Delay (ms)", - help: "Maximum retry delay cap in ms for Telegram outbound calls.", - }, - "retry.jitter": { - label: "Telegram Retry Jitter", - help: "Jitter factor (0-1) applied to Telegram retry delays.", - }, - "network.autoSelectFamily": { - label: "Telegram autoSelectFamily", - help: "Override Node autoSelectFamily for Telegram (true=enable, false=disable).", - }, - timeoutSeconds: { - label: "Telegram API Timeout (seconds)", - help: "Max seconds before Telegram API requests are aborted (default: 500 per grammY).", - }, - silentErrorReplies: { - label: "Telegram Silent Error Replies", - help: "When true, Telegram bot replies marked as errors are sent silently (no notification sound). Default: false.", - }, - apiRoot: { - label: "Telegram API Root URL", - help: "Custom Telegram Bot API root URL. Use for self-hosted Bot API servers (https://github.com/tdlib/telegram-bot-api) or reverse proxies in regions where api.telegram.org is blocked.", - }, - autoTopicLabel: { - label: "Telegram Auto Topic Label", - help: "Auto-rename DM forum topics on first message using LLM. Default: true. Set to false to disable, or use object form { enabled: true, prompt: '...' } for custom prompt.", - }, - "autoTopicLabel.enabled": { - label: "Telegram Auto Topic Label Enabled", - help: "Whether auto topic labeling is enabled. Default: true.", - }, - "autoTopicLabel.prompt": { - label: "Telegram Auto Topic Label Prompt", - help: "Custom prompt for LLM-based topic naming. The user message is appended after the prompt.", - }, - "capabilities.inlineButtons": { - label: "Telegram Inline Buttons", - help: "Enable Telegram inline button components for supported command and interaction surfaces. Disable if your deployment needs plain-text-only compatibility behavior.", - }, - execApprovals: { - label: "Telegram Exec Approvals", - help: "Telegram-native exec approval routing and approver authorization. Enable this only when Telegram should act as an explicit exec-approval client for the selected bot account.", - }, - "execApprovals.enabled": { - label: "Telegram Exec Approvals Enabled", - help: "Enable Telegram exec approvals for this account. When false or unset, Telegram messages/buttons cannot approve exec requests.", - }, - "execApprovals.approvers": { - label: "Telegram Exec Approval Approvers", - help: "Telegram user IDs allowed to approve exec requests for this bot account. Use numeric Telegram user IDs; prompts are only delivered to these approvers when target includes dm.", - }, - "execApprovals.agentFilter": { - label: "Telegram Exec Approval Agent Filter", - help: 'Optional allowlist of agent IDs eligible for Telegram exec approvals, for example `["main", "ops-agent"]`. Use this to keep approval prompts scoped to the agents you actually operate from Telegram.', - }, - "execApprovals.sessionFilter": { - label: "Telegram Exec Approval Session Filter", - help: "Optional session-key filters matched as substring or regex-style patterns before Telegram approval routing is used. Use narrow patterns so Telegram approvals only appear for intended sessions.", - }, - "execApprovals.target": { - label: "Telegram Exec Approval Target", - help: 'Controls where Telegram approval prompts are sent: "dm" sends to approver DMs (default), "channel" sends to the originating Telegram chat/topic, and "both" sends to both. Channel delivery exposes the command text to the chat, so only use it in trusted groups/topics.', - }, - "threadBindings.enabled": { - label: "Telegram Thread Binding Enabled", - help: "Enable Telegram conversation binding features (/focus, /unfocus, /agents, and /session idle|max-age). Overrides session.threadBindings.enabled when set.", - }, - "threadBindings.idleHours": { - label: "Telegram Thread Binding Idle Timeout (hours)", - help: "Inactivity window in hours for Telegram bound sessions. Set 0 to disable idle auto-unfocus (default: 24). Overrides session.threadBindings.idleHours when set.", - }, - "threadBindings.maxAgeHours": { - label: "Telegram Thread Binding Max Age (hours)", - help: "Optional hard max age in hours for Telegram bound sessions. Set 0 to disable hard cap (default: 0). Overrides session.threadBindings.maxAgeHours when set.", - }, - "threadBindings.spawnSubagentSessions": { - label: "Telegram Thread-Bound Subagent Spawn", - help: "Allow subagent spawns with thread=true to auto-bind Telegram current conversations when supported.", - }, - "threadBindings.spawnAcpSessions": { - label: "Telegram Thread-Bound ACP Spawn", - help: "Allow ACP spawns with thread=true to auto-bind Telegram current conversations when supported.", - }, - }, - label: "Telegram", - description: - "simplest way to get started — register a bot with @BotFather and get going.", - }, - }, - }, - }, - { - dirName: "tlon", - idHint: "tlon", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js", "setup-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/tlon", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Tlon/Urbit channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "tlon", - label: "Tlon", - selectionLabel: "Tlon (Urbit)", - docsPath: "/channels/tlon", - docsLabel: "tlon", - blurb: "decentralized messaging on Urbit; install the plugin to enable.", - order: 90, - quickstartAllowFrom: true, - }, - install: { - npmSpec: "@openclaw/tlon", - localPath: "extensions/tlon", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "tlon", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["tlon"], - skills: ["node_modules/@tloncorp/tlon-skill"], - channelConfigs: { - tlon: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - ship: { - type: "string", - minLength: 1, - }, - url: { - type: "string", - }, - code: { - type: "string", - }, - allowPrivateNetwork: { - type: "boolean", - }, - groupChannels: { - type: "array", - items: { - type: "string", - minLength: 1, - }, - }, - dmAllowlist: { - type: "array", - items: { - type: "string", - minLength: 1, - }, - }, - autoDiscoverChannels: { - type: "boolean", - }, - showModelSignature: { - type: "boolean", - }, - responsePrefix: { - type: "string", - }, - autoAcceptDmInvites: { - type: "boolean", - }, - autoAcceptGroupInvites: { - type: "boolean", - }, - ownerShip: { - type: "string", - minLength: 1, - }, - authorization: { - type: "object", - properties: { - channelRules: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - mode: { - type: "string", - enum: ["restricted", "open"], - }, - allowedShips: { - type: "array", - items: { - type: "string", - minLength: 1, - }, - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - defaultAuthorizedShips: { - type: "array", - items: { - type: "string", - minLength: 1, - }, - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - ship: { - type: "string", - minLength: 1, - }, - url: { - type: "string", - }, - code: { - type: "string", - }, - allowPrivateNetwork: { - type: "boolean", - }, - groupChannels: { - type: "array", - items: { - type: "string", - minLength: 1, - }, - }, - dmAllowlist: { - type: "array", - items: { - type: "string", - minLength: 1, - }, - }, - autoDiscoverChannels: { - type: "boolean", - }, - showModelSignature: { - type: "boolean", - }, - responsePrefix: { - type: "string", - }, - autoAcceptDmInvites: { - type: "boolean", - }, - autoAcceptGroupInvites: { - type: "boolean", - }, - ownerShip: { - type: "string", - minLength: 1, - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - label: "Tlon", - description: "decentralized messaging on Urbit; install the plugin to enable.", - }, - }, - }, - }, - { - dirName: "together", - idHint: "together", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/together-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Together provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "together", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["together"], - providerAuthEnvVars: { - together: ["TOGETHER_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "together", - method: "api-key", - choiceId: "together-api-key", - choiceLabel: "Together AI API key", - groupId: "together", - groupLabel: "Together AI", - groupHint: "API key", - optionKey: "togetherApiKey", - cliFlag: "--together-api-key", - cliOption: "--together-api-key ", - cliDescription: "Together AI API key", - }, - ], - }, - }, - { - dirName: "twitch", - idHint: "twitch", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/twitch", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Twitch channel plugin", - packageManifest: { - extensions: ["./index.ts"], - channel: { - id: "twitch", - label: "Twitch", - selectionLabel: "Twitch (Chat)", - docsPath: "/channels/twitch", - blurb: "Twitch chat integration", - aliases: ["twitch-chat"], - }, - install: { - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "twitch", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["twitch"], - channelConfigs: { - twitch: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - anyOf: [ - { - allOf: [ - { - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - { - type: "object", - properties: { - username: { - type: "string", - }, - accessToken: { - type: "string", - }, - clientId: { - type: "string", - }, - channel: { - type: "string", - minLength: 1, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - type: "string", - }, - }, - allowedRoles: { - type: "array", - items: { - type: "string", - enum: ["moderator", "owner", "vip", "subscriber", "all"], - }, - }, - requireMention: { - type: "boolean", - }, - responsePrefix: { - type: "string", - }, - clientSecret: { - type: "string", - }, - refreshToken: { - type: "string", - }, - expiresIn: { - anyOf: [ - { - type: "number", - }, - { - type: "null", - }, - ], - }, - obtainmentTimestamp: { - type: "number", - }, - }, - required: ["username", "accessToken", "channel"], - additionalProperties: false, - }, - ], - }, - { - allOf: [ - { - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - { - type: "object", - properties: { - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - username: { - type: "string", - }, - accessToken: { - type: "string", - }, - clientId: { - type: "string", - }, - channel: { - type: "string", - minLength: 1, - }, - enabled: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - type: "string", - }, - }, - allowedRoles: { - type: "array", - items: { - type: "string", - enum: ["moderator", "owner", "vip", "subscriber", "all"], - }, - }, - requireMention: { - type: "boolean", - }, - responsePrefix: { - type: "string", - }, - clientSecret: { - type: "string", - }, - refreshToken: { - type: "string", - }, - expiresIn: { - anyOf: [ - { - type: "number", - }, - { - type: "null", - }, - ], - }, - obtainmentTimestamp: { - type: "number", - }, - }, - required: ["username", "accessToken", "channel"], - additionalProperties: false, - }, - }, - }, - required: ["accounts"], - additionalProperties: false, - }, - ], - }, - ], - }, - label: "Twitch", - description: "Twitch chat integration", - }, - }, - }, - }, - { - dirName: "venice", - idHint: "venice", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/venice-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Venice provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "venice", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["venice"], - providerAuthEnvVars: { - venice: ["VENICE_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "venice", - method: "api-key", - choiceId: "venice-api-key", - choiceLabel: "Venice AI API key", - groupId: "venice", - groupLabel: "Venice AI", - groupHint: "Privacy-focused (uncensored models)", - optionKey: "veniceApiKey", - cliFlag: "--venice-api-key", - cliOption: "--venice-api-key ", - cliDescription: "Venice API key", - }, - ], - }, - }, - { - dirName: "vercel-ai-gateway", - idHint: "vercel-ai-gateway", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/vercel-ai-gateway-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Vercel AI Gateway provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "vercel-ai-gateway", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["vercel-ai-gateway"], - providerAuthEnvVars: { - "vercel-ai-gateway": ["AI_GATEWAY_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "vercel-ai-gateway", - method: "api-key", - choiceId: "ai-gateway-api-key", - choiceLabel: "Vercel AI Gateway API key", - groupId: "ai-gateway", - groupLabel: "Vercel AI Gateway", - groupHint: "API key", - optionKey: "aiGatewayApiKey", - cliFlag: "--ai-gateway-api-key", - cliOption: "--ai-gateway-api-key ", - cliDescription: "Vercel AI Gateway API key", - }, - ], - }, - }, - { - dirName: "vllm", - idHint: "vllm", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "defaults.js", "models.js"], - packageName: "@openclaw/vllm-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw vLLM provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "vllm", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["vllm"], - providerAuthEnvVars: { - vllm: ["VLLM_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "vllm", - method: "custom", - choiceId: "vllm", - choiceLabel: "vLLM", - choiceHint: "Local/self-hosted OpenAI-compatible server", - groupId: "vllm", - groupLabel: "vLLM", - groupHint: "Local/self-hosted OpenAI-compatible", - }, - ], - }, - }, - { - dirName: "voice-call", - idHint: "voice-call", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js", "runtime-entry.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/voice-call", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw voice-call plugin", - packageManifest: { - extensions: ["./index.ts"], - install: { - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "voice-call", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - enabled: { - type: "boolean", - }, - provider: { - type: "string", - enum: ["telnyx", "twilio", "plivo", "mock"], - }, - telnyx: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: "string", - }, - connectionId: { - type: "string", - }, - publicKey: { - type: "string", - }, - }, - }, - twilio: { - type: "object", - additionalProperties: false, - properties: { - accountSid: { - type: "string", - }, - authToken: { - type: "string", - }, - }, - }, - plivo: { - type: "object", - additionalProperties: false, - properties: { - authId: { - type: "string", - }, - authToken: { - type: "string", - }, - }, - }, - fromNumber: { - type: "string", - pattern: "^\\+[1-9]\\d{1,14}$", - }, - toNumber: { - type: "string", - pattern: "^\\+[1-9]\\d{1,14}$", - }, - inboundPolicy: { - type: "string", - enum: ["disabled", "allowlist", "pairing", "open"], - }, - allowFrom: { - type: "array", - items: { - type: "string", - pattern: "^\\+[1-9]\\d{1,14}$", - }, - }, - inboundGreeting: { - type: "string", - }, - outbound: { - type: "object", - additionalProperties: false, - properties: { - defaultMode: { - type: "string", - enum: ["notify", "conversation"], - }, - notifyHangupDelaySec: { - type: "integer", - minimum: 0, - }, - }, - }, - maxDurationSeconds: { - type: "integer", - minimum: 1, - }, - staleCallReaperSeconds: { - type: "integer", - minimum: 0, - }, - silenceTimeoutMs: { - type: "integer", - minimum: 1, - }, - transcriptTimeoutMs: { - type: "integer", - minimum: 1, - }, - ringTimeoutMs: { - type: "integer", - minimum: 1, - }, - maxConcurrentCalls: { - type: "integer", - minimum: 1, - }, - serve: { - type: "object", - additionalProperties: false, - properties: { - port: { - type: "integer", - minimum: 1, - }, - bind: { - type: "string", - }, - path: { - type: "string", - }, - }, - }, - tailscale: { - type: "object", - additionalProperties: false, - properties: { - mode: { - type: "string", - enum: ["off", "serve", "funnel"], - }, - path: { - type: "string", - }, - }, - }, - tunnel: { - type: "object", - additionalProperties: false, - properties: { - provider: { - type: "string", - enum: ["none", "ngrok", "tailscale-serve", "tailscale-funnel"], - }, - ngrokAuthToken: { - type: "string", - }, - ngrokDomain: { - type: "string", - }, - allowNgrokFreeTierLoopbackBypass: { - type: "boolean", - }, - }, - }, - webhookSecurity: { - type: "object", - additionalProperties: false, - properties: { - allowedHosts: { - type: "array", - items: { - type: "string", - }, - }, - trustForwardingHeaders: { - type: "boolean", - }, - trustedProxyIPs: { - type: "array", - items: { - type: "string", - }, - }, - }, - }, - streaming: { - type: "object", - additionalProperties: false, - properties: { - enabled: { - type: "boolean", - }, - sttProvider: { - type: "string", - enum: ["openai-realtime"], - }, - openaiApiKey: { - type: "string", - }, - sttModel: { - type: "string", - }, - silenceDurationMs: { - type: "integer", - minimum: 1, - }, - vadThreshold: { - type: "number", - minimum: 0, - maximum: 1, - }, - streamPath: { - type: "string", - }, - preStartTimeoutMs: { - type: "integer", - minimum: 1, - }, - maxPendingConnections: { - type: "integer", - minimum: 1, - }, - maxPendingConnectionsPerIp: { - type: "integer", - minimum: 1, - }, - maxConnections: { - type: "integer", - minimum: 1, - }, - }, - }, - publicUrl: { - type: "string", - }, - skipSignatureVerification: { - type: "boolean", - }, - stt: { - type: "object", - additionalProperties: false, - properties: { - provider: { - type: "string", - enum: ["openai"], - }, - model: { - type: "string", - }, - }, - }, - tts: { - type: "object", - additionalProperties: false, - properties: { - auto: { - type: "string", - enum: ["off", "always", "inbound", "tagged"], - }, - enabled: { - type: "boolean", - }, - mode: { - type: "string", - enum: ["final", "all"], - }, - provider: { - type: "string", - }, - summaryModel: { - type: "string", - }, - modelOverrides: { - type: "object", - additionalProperties: false, - properties: { - enabled: { - type: "boolean", - }, - allowText: { - type: "boolean", - }, - allowProvider: { - type: "boolean", - }, - allowVoice: { - type: "boolean", - }, - allowModelId: { - type: "boolean", - }, - allowVoiceSettings: { - type: "boolean", - }, - allowNormalization: { - type: "boolean", - }, - allowSeed: { - type: "boolean", - }, - }, - }, - elevenlabs: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: "string", - }, - baseUrl: { - type: "string", - }, - voiceId: { - type: "string", - }, - modelId: { - type: "string", - }, - seed: { - type: "integer", - minimum: 0, - maximum: 4294967295, - }, - applyTextNormalization: { - type: "string", - enum: ["auto", "on", "off"], - }, - languageCode: { - type: "string", - }, - voiceSettings: { - type: "object", - additionalProperties: false, - properties: { - stability: { - type: "number", - minimum: 0, - maximum: 1, - }, - similarityBoost: { - type: "number", - minimum: 0, - maximum: 1, - }, - style: { - type: "number", - minimum: 0, - maximum: 1, - }, - useSpeakerBoost: { - type: "boolean", - }, - speed: { - type: "number", - minimum: 0.5, - maximum: 2, - }, - }, - }, - }, - }, - openai: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: "string", - }, - baseUrl: { - type: "string", - }, - model: { - type: "string", - }, - voice: { - type: "string", - }, - speed: { - type: "number", - minimum: 0.25, - maximum: 4, - }, - instructions: { - type: "string", - }, - }, - }, - edge: { - type: "object", - additionalProperties: false, - properties: { - enabled: { - type: "boolean", - }, - voice: { - type: "string", - }, - lang: { - type: "string", - }, - outputFormat: { - type: "string", - }, - pitch: { - type: "string", - }, - rate: { - type: "string", - }, - volume: { - type: "string", - }, - saveSubtitles: { - type: "boolean", - }, - proxy: { - type: "string", - }, - timeoutMs: { - type: "integer", - minimum: 1000, - maximum: 120000, - }, - }, - }, - prefsPath: { - type: "string", - }, - maxTextLength: { - type: "integer", - minimum: 1, - }, - timeoutMs: { - type: "integer", - minimum: 1000, - maximum: 120000, - }, - }, - }, - store: { - type: "string", - }, - responseModel: { - type: "string", - }, - responseSystemPrompt: { - type: "string", - }, - responseTimeoutMs: { - type: "integer", - minimum: 1, - }, - }, - }, - uiHints: { - provider: { - label: "Provider", - help: "Use twilio, telnyx, or mock for dev/no-network.", - }, - fromNumber: { - label: "From Number", - placeholder: "+15550001234", - }, - toNumber: { - label: "Default To Number", - placeholder: "+15550001234", - }, - inboundPolicy: { - label: "Inbound Policy", - }, - allowFrom: { - label: "Inbound Allowlist", - }, - inboundGreeting: { - label: "Inbound Greeting", - advanced: true, - }, - "telnyx.apiKey": { - label: "Telnyx API Key", - sensitive: true, - }, - "telnyx.connectionId": { - label: "Telnyx Connection ID", - }, - "telnyx.publicKey": { - label: "Telnyx Public Key", - sensitive: true, - }, - "twilio.accountSid": { - label: "Twilio Account SID", - }, - "twilio.authToken": { - label: "Twilio Auth Token", - sensitive: true, - }, - "outbound.defaultMode": { - label: "Default Call Mode", - }, - "outbound.notifyHangupDelaySec": { - label: "Notify Hangup Delay (sec)", - advanced: true, - }, - "serve.port": { - label: "Webhook Port", - }, - "serve.bind": { - label: "Webhook Bind", - }, - "serve.path": { - label: "Webhook Path", - }, - "tailscale.mode": { - label: "Tailscale Mode", - advanced: true, - }, - "tailscale.path": { - label: "Tailscale Path", - advanced: true, - }, - "tunnel.provider": { - label: "Tunnel Provider", - advanced: true, - }, - "tunnel.ngrokAuthToken": { - label: "ngrok Auth Token", - sensitive: true, - advanced: true, - }, - "tunnel.ngrokDomain": { - label: "ngrok Domain", - advanced: true, - }, - "tunnel.allowNgrokFreeTierLoopbackBypass": { - label: "Allow ngrok Free Tier (Loopback Bypass)", - advanced: true, - }, - "streaming.enabled": { - label: "Enable Streaming", - advanced: true, - }, - "streaming.openaiApiKey": { - label: "OpenAI Realtime API Key", - sensitive: true, - advanced: true, - }, - "streaming.sttModel": { - label: "Realtime STT Model", - advanced: true, - }, - "streaming.streamPath": { - label: "Media Stream Path", - advanced: true, - }, - "tts.provider": { - label: "TTS Provider Override", - help: "Deep-merges with messages.tts (Microsoft is ignored for calls).", - advanced: true, - }, - "tts.openai.model": { - label: "OpenAI TTS Model", - advanced: true, - }, - "tts.openai.voice": { - label: "OpenAI TTS Voice", - advanced: true, - }, - "tts.openai.apiKey": { - label: "OpenAI API Key", - sensitive: true, - advanced: true, - }, - "tts.elevenlabs.modelId": { - label: "ElevenLabs Model ID", - advanced: true, - }, - "tts.elevenlabs.voiceId": { - label: "ElevenLabs Voice ID", - advanced: true, - }, - "tts.elevenlabs.apiKey": { - label: "ElevenLabs API Key", - sensitive: true, - advanced: true, - }, - "tts.elevenlabs.baseUrl": { - label: "ElevenLabs Base URL", - advanced: true, - }, - publicUrl: { - label: "Public Webhook URL", - advanced: true, - }, - skipSignatureVerification: { - label: "Skip Signature Verification", - advanced: true, - }, - store: { - label: "Call Log Store Path", - advanced: true, - }, - responseModel: { - label: "Response Model", - advanced: true, - }, - responseSystemPrompt: { - label: "Response System Prompt", - advanced: true, - }, - responseTimeoutMs: { - label: "Response Timeout (ms)", - advanced: true, - }, - }, - }, - }, - { - dirName: "volcengine", - idHint: "volcengine", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "models.js", "provider-catalog.js"], - packageName: "@openclaw/volcengine-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Volcengine provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "volcengine", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["volcengine", "volcengine-plan"], - providerAuthEnvVars: { - volcengine: ["VOLCANO_ENGINE_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "volcengine", - method: "api-key", - choiceId: "volcengine-api-key", - choiceLabel: "Volcano Engine API key", - groupId: "volcengine", - groupLabel: "Volcano Engine", - groupHint: "API key", - optionKey: "volcengineApiKey", - cliFlag: "--volcengine-api-key", - cliOption: "--volcengine-api-key ", - cliDescription: "Volcano Engine API key", - }, - ], - }, - }, - { - dirName: "whatsapp", - idHint: "whatsapp", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: [ - "action-runtime-api.js", - "action-runtime.runtime.js", - "api.js", - "auth-presence.js", - "channel-config-api.js", - "light-runtime-api.js", - "login-qr-api.js", - "runtime-api.js", - ], - runtimeSidecarArtifacts: ["light-runtime-api.js", "runtime-api.js"], - packageName: "@openclaw/whatsapp", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw WhatsApp channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "whatsapp", - label: "WhatsApp", - selectionLabel: "WhatsApp (QR link)", - detailLabel: "WhatsApp Web", - docsPath: "/channels/whatsapp", - docsLabel: "whatsapp", - blurb: "works with your own number; recommend a separate phone + eSIM.", - systemImage: "message", - }, - install: { - npmSpec: "@openclaw/whatsapp", - localPath: "extensions/whatsapp", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "whatsapp", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["whatsapp"], - channelConfigs: { - whatsapp: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - enabled: { - type: "boolean", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - configWrites: { - type: "boolean", - }, - sendReadReceipts: { - type: "boolean", - }, - messagePrefix: { - type: "string", - }, - responsePrefix: { - type: "string", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - selfChatMode: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - type: "string", - }, - }, - defaultTo: { - type: "string", - }, - groupAllowFrom: { - type: "array", - items: { - type: "string", - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - ackReaction: { - type: "object", - properties: { - emoji: { - type: "string", - }, - direct: { - default: true, - type: "boolean", - }, - group: { - default: "mentions", - type: "string", - enum: ["always", "mentions", "never"], - }, - }, - required: ["direct", "group"], - additionalProperties: false, - }, - debounceMs: { - default: 0, - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - accounts: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - capabilities: { - type: "array", - items: { - type: "string", - }, - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - configWrites: { - type: "boolean", - }, - sendReadReceipts: { - type: "boolean", - }, - messagePrefix: { - type: "string", - }, - responsePrefix: { - type: "string", - }, - dmPolicy: { - default: "pairing", - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - selfChatMode: { - type: "boolean", - }, - allowFrom: { - type: "array", - items: { - type: "string", - }, - }, - defaultTo: { - type: "string", - }, - groupAllowFrom: { - type: "array", - items: { - type: "string", - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dmHistoryLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - dms: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - }, - textChunkLimit: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - chunkMode: { - type: "string", - enum: ["length", "newline"], - }, - blockStreaming: { - type: "boolean", - }, - blockStreamingCoalesce: { - type: "object", - properties: { - minChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - maxChars: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - idleMs: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - }, - additionalProperties: false, - }, - groups: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - toolsBySender: { - type: "object", - propertyNames: { - type: "string", - }, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: false, - }, - }, - ackReaction: { - type: "object", - properties: { - emoji: { - type: "string", - }, - direct: { - default: true, - type: "boolean", - }, - group: { - default: "mentions", - type: "string", - enum: ["always", "mentions", "never"], - }, - }, - required: ["direct", "group"], - additionalProperties: false, - }, - debounceMs: { - default: 0, - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - heartbeat: { - type: "object", - properties: { - showOk: { - type: "boolean", - }, - showAlerts: { - type: "boolean", - }, - useIndicator: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - healthMonitor: { - type: "object", - properties: { - enabled: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - name: { - type: "string", - }, - authDir: { - type: "string", - }, - mediaMaxMb: { - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - }, - required: ["dmPolicy", "groupPolicy", "debounceMs"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - mediaMaxMb: { - default: 50, - type: "integer", - exclusiveMinimum: 0, - maximum: 9007199254740991, - }, - actions: { - type: "object", - properties: { - reactions: { - type: "boolean", - }, - sendMessage: { - type: "boolean", - }, - polls: { - type: "boolean", - }, - }, - additionalProperties: false, - }, - }, - required: ["dmPolicy", "groupPolicy", "debounceMs", "mediaMaxMb"], - additionalProperties: false, - }, - uiHints: { - "": { - label: "WhatsApp", - help: "WhatsApp channel provider configuration for access policy and message batching behavior. Use this section to tune responsiveness and direct-message routing safety for WhatsApp chats.", - }, - dmPolicy: { - label: "WhatsApp DM Policy", - help: 'Direct message access control ("pairing" recommended). "open" requires channels.whatsapp.allowFrom=["*"].', - }, - selfChatMode: { - label: "WhatsApp Self-Phone Mode", - help: "Same-phone setup (bot uses your personal WhatsApp number).", - }, - debounceMs: { - label: "WhatsApp Message Debounce (ms)", - help: "Debounce window (ms) for batching rapid consecutive messages from the same sender (0 to disable).", - }, - configWrites: { - label: "WhatsApp Config Writes", - help: "Allow WhatsApp to write config in response to channel events/commands (default: true).", - }, - }, - label: "WhatsApp", - description: "works with your own number; recommend a separate phone + eSIM.", - }, - }, - }, - }, - { - dirName: "xai", - idHint: "xai", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "code-execution.js", - "model-definitions.js", - "model-id.js", - "onboard.js", - "provider-catalog.js", - "provider-models.js", - "stream.js", - "web-search.js", - "x-search.js", - ], - packageName: "@openclaw/xai-plugin", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw xAI plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "xai", - configSchema: { - type: "object", - additionalProperties: false, - properties: { - webSearch: { - type: "object", - additionalProperties: false, - properties: { - apiKey: { - type: ["string", "object"], - }, - model: { - type: "string", - }, - inlineCitations: { - type: "boolean", - }, - }, - }, - codeExecution: { - type: "object", - additionalProperties: false, - properties: { - enabled: { - type: "boolean", - }, - model: { - type: "string", - }, - maxTurns: { - type: "number", - }, - timeoutSeconds: { - type: "number", - }, - }, - }, - }, - }, - enabledByDefault: true, - providers: ["xai"], - providerAuthEnvVars: { - xai: ["XAI_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "xai", - method: "api-key", - choiceId: "xai-api-key", - choiceLabel: "xAI API key", - groupId: "xai", - groupLabel: "xAI (Grok)", - groupHint: "API key", - optionKey: "xaiApiKey", - cliFlag: "--xai-api-key", - cliOption: "--xai-api-key ", - cliDescription: "xAI API key", - }, - ], - uiHints: { - "webSearch.apiKey": { - label: "Grok Search API Key", - help: "xAI API key for Grok web search (fallback: XAI_API_KEY env var).", - sensitive: true, - }, - "webSearch.model": { - label: "Grok Search Model", - help: "Grok model override for web search.", - }, - "webSearch.inlineCitations": { - label: "Inline Citations", - help: "Include inline markdown citations in Grok responses.", - }, - "codeExecution.enabled": { - label: "Enable Code Execution", - help: "Enable the code_execution tool for remote xAI sandbox analysis.", - }, - "codeExecution.model": { - label: "Code Execution Model", - help: "xAI model override for code_execution.", - }, - "codeExecution.maxTurns": { - label: "Code Execution Max Turns", - help: "Optional max internal tool turns xAI may use for code_execution.", - }, - "codeExecution.timeoutSeconds": { - label: "Code Execution Timeout", - help: "Timeout in seconds for code_execution requests.", - }, - }, - contracts: { - webSearchProviders: ["grok"], - tools: ["code_execution", "x_search"], - }, - }, - }, - { - dirName: "xiaomi", - idHint: "xiaomi", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: ["api.js", "onboard.js", "provider-catalog.js"], - packageName: "@openclaw/xiaomi-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Xiaomi provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "xiaomi", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["xiaomi"], - providerAuthEnvVars: { - xiaomi: ["XIAOMI_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "xiaomi", - method: "api-key", - choiceId: "xiaomi-api-key", - choiceLabel: "Xiaomi API key", - groupId: "xiaomi", - groupLabel: "Xiaomi", - groupHint: "API key", - optionKey: "xiaomiApiKey", - cliFlag: "--xiaomi-api-key", - cliOption: "--xiaomi-api-key ", - cliDescription: "Xiaomi API key", - }, - ], - }, - }, - { - dirName: "zai", - idHint: "zai", - source: { - source: "./index.ts", - built: "index.js", - }, - publicSurfaceArtifacts: [ - "api.js", - "detect.js", - "media-understanding-provider.js", - "model-definitions.js", - "onboard.js", - "runtime-api.js", - ], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/zai-provider", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Z.AI provider plugin", - packageManifest: { - extensions: ["./index.ts"], - }, - manifest: { - id: "zai", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - enabledByDefault: true, - providers: ["zai"], - providerAuthEnvVars: { - zai: ["ZAI_API_KEY", "Z_AI_API_KEY"], - }, - providerAuthChoices: [ - { - provider: "zai", - method: "api-key", - choiceId: "zai-api-key", - choiceLabel: "Z.AI API key", - groupId: "zai", - groupLabel: "Z.AI", - groupHint: "GLM Coding Plan / Global / CN", - optionKey: "zaiApiKey", - cliFlag: "--zai-api-key", - cliOption: "--zai-api-key ", - cliDescription: "Z.AI API key", - }, - { - provider: "zai", - method: "coding-global", - choiceId: "zai-coding-global", - choiceLabel: "Coding-Plan-Global", - choiceHint: "GLM Coding Plan Global (api.z.ai)", - groupId: "zai", - groupLabel: "Z.AI", - groupHint: "GLM Coding Plan / Global / CN", - optionKey: "zaiApiKey", - cliFlag: "--zai-api-key", - cliOption: "--zai-api-key ", - cliDescription: "Z.AI API key", - }, - { - provider: "zai", - method: "coding-cn", - choiceId: "zai-coding-cn", - choiceLabel: "Coding-Plan-CN", - choiceHint: "GLM Coding Plan CN (open.bigmodel.cn)", - groupId: "zai", - groupLabel: "Z.AI", - groupHint: "GLM Coding Plan / Global / CN", - optionKey: "zaiApiKey", - cliFlag: "--zai-api-key", - cliOption: "--zai-api-key ", - cliDescription: "Z.AI API key", - }, - { - provider: "zai", - method: "global", - choiceId: "zai-global", - choiceLabel: "Global", - choiceHint: "Z.AI Global (api.z.ai)", - groupId: "zai", - groupLabel: "Z.AI", - groupHint: "GLM Coding Plan / Global / CN", - optionKey: "zaiApiKey", - cliFlag: "--zai-api-key", - cliOption: "--zai-api-key ", - cliDescription: "Z.AI API key", - }, - { - provider: "zai", - method: "cn", - choiceId: "zai-cn", - choiceLabel: "CN", - choiceHint: "Z.AI CN (open.bigmodel.cn)", - groupId: "zai", - groupLabel: "Z.AI", - groupHint: "GLM Coding Plan / Global / CN", - optionKey: "zaiApiKey", - cliFlag: "--zai-api-key", - cliOption: "--zai-api-key ", - cliDescription: "Z.AI API key", - }, - ], - contracts: { - mediaUnderstandingProviders: ["zai"], - }, - }, - }, - { - dirName: "zalo", - idHint: "zalo", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/zalo", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Zalo channel plugin", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "zalo", - label: "Zalo", - selectionLabel: "Zalo (Bot API)", - docsPath: "/channels/zalo", - docsLabel: "zalo", - blurb: "Vietnam-focused messaging platform with Bot API.", - aliases: ["zl"], - order: 80, - quickstartAllowFrom: true, - }, - install: { - npmSpec: "@openclaw/zalo", - localPath: "extensions/zalo", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "zalo", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["zalo"], - channelConfigs: { - zalo: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - botToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - tokenFile: { - type: "string", - }, - webhookUrl: { - type: "string", - }, - webhookSecret: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - webhookPath: { - type: "string", - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - mediaMaxMb: { - type: "number", - }, - proxy: { - type: "string", - }, - responsePrefix: { - type: "string", - }, - accounts: { - type: "object", - properties: {}, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - botToken: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - tokenFile: { - type: "string", - }, - webhookUrl: { - type: "string", - }, - webhookSecret: { - anyOf: [ - { - type: "string", - }, - { - oneOf: [ - { - type: "object", - properties: { - source: { - type: "string", - const: "env", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - pattern: "^[A-Z][A-Z0-9_]{0,127}$", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "file", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - { - type: "object", - properties: { - source: { - type: "string", - const: "exec", - }, - provider: { - type: "string", - pattern: "^[a-z][a-z0-9_-]{0,63}$", - }, - id: { - type: "string", - }, - }, - required: ["source", "provider", "id"], - additionalProperties: false, - }, - ], - }, - ], - }, - webhookPath: { - type: "string", - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - mediaMaxMb: { - type: "number", - }, - proxy: { - type: "string", - }, - responsePrefix: { - type: "string", - }, - }, - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - additionalProperties: false, - }, - label: "Zalo", - description: "Vietnam-focused messaging platform with Bot API.", - }, - }, - }, - }, - { - dirName: "zalouser", - idHint: "zalouser", - source: { - source: "./index.ts", - built: "index.js", - }, - setupSource: { - source: "./setup-entry.ts", - built: "setup-entry.js", - }, - publicSurfaceArtifacts: ["api.js", "runtime-api.js"], - runtimeSidecarArtifacts: ["runtime-api.js"], - packageName: "@openclaw/zalouser", - packageVersion: "2026.3.28", - packageDescription: "OpenClaw Zalo Personal Account plugin via native zca-js integration", - packageManifest: { - extensions: ["./index.ts"], - setupEntry: "./setup-entry.ts", - channel: { - id: "zalouser", - label: "Zalo Personal", - selectionLabel: "Zalo (Personal Account)", - docsPath: "/channels/zalouser", - docsLabel: "zalouser", - blurb: "Zalo personal account via QR code login.", - aliases: ["zlu"], - order: 85, - quickstartAllowFrom: false, - }, - install: { - npmSpec: "@openclaw/zalouser", - localPath: "extensions/zalouser", - defaultChoice: "npm", - minHostVersion: ">=2026.3.28", - }, - }, - manifest: { - id: "zalouser", - configSchema: { - type: "object", - additionalProperties: false, - properties: {}, - }, - channels: ["zalouser"], - channelConfigs: { - zalouser: { - schema: { - $schema: "http://json-schema.org/draft-07/schema#", - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - profile: { - type: "string", - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groups: { - type: "object", - properties: {}, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "boolean", - }, - enabled: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - }, - messagePrefix: { - type: "string", - }, - responsePrefix: { - type: "string", - }, - accounts: { - type: "object", - properties: {}, - additionalProperties: { - type: "object", - properties: { - name: { - type: "string", - }, - enabled: { - type: "boolean", - }, - markdown: { - type: "object", - properties: { - tables: { - type: "string", - enum: ["off", "bullets", "code"], - }, - }, - additionalProperties: false, - }, - profile: { - type: "string", - }, - dangerouslyAllowNameMatching: { - type: "boolean", - }, - dmPolicy: { - type: "string", - enum: ["pairing", "allowlist", "open", "disabled"], - }, - allowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - historyLimit: { - type: "integer", - minimum: 0, - maximum: 9007199254740991, - }, - groupAllowFrom: { - type: "array", - items: { - anyOf: [ - { - type: "string", - }, - { - type: "number", - }, - ], - }, - }, - groupPolicy: { - default: "allowlist", - type: "string", - enum: ["open", "disabled", "allowlist"], - }, - groups: { - type: "object", - properties: {}, - additionalProperties: { - type: "object", - properties: { - allow: { - type: "boolean", - }, - enabled: { - type: "boolean", - }, - requireMention: { - type: "boolean", - }, - tools: { - type: "object", - properties: { - allow: { - type: "array", - items: { - type: "string", - }, - }, - alsoAllow: { - type: "array", - items: { - type: "string", - }, - }, - deny: { - type: "array", - items: { - type: "string", - }, - }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - }, - messagePrefix: { - type: "string", - }, - responsePrefix: { - type: "string", - }, - }, - required: ["groupPolicy"], - additionalProperties: false, - }, - }, - defaultAccount: { - type: "string", - }, - }, - required: ["groupPolicy"], - additionalProperties: false, - }, - label: "Zalo Personal", - description: "Zalo personal account via QR code login.", - }, - }, - }, - }, -] as const; diff --git a/src/plugins/bundled-plugin-metadata.test.ts b/src/plugins/bundled-plugin-metadata.test.ts index f47dabfe46e..74fc308f407 100644 --- a/src/plugins/bundled-plugin-metadata.test.ts +++ b/src/plugins/bundled-plugin-metadata.test.ts @@ -2,11 +2,8 @@ import fs from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { - collectBundledPluginMetadata, - writeBundledPluginMetadataModule, -} from "../../scripts/generate-bundled-plugin-metadata.mjs"; -import { - BUNDLED_PLUGIN_METADATA, + clearBundledPluginMetadataCache, + listBundledPluginMetadata, resolveBundledPluginGeneratedPath, } from "./bundled-plugin-metadata.js"; import { @@ -53,44 +50,17 @@ function expectArtifactPresence( } } -async function writeGeneratedMetadataModule(params: { - repoRoot: string; - outputPath?: string; - check?: boolean; -}) { - return writeBundledPluginMetadataModule({ - repoRoot: params.repoRoot, - outputPath: params.outputPath ?? "src/plugins/bundled-plugin-metadata.generated.ts", - ...(params.check ? { check: true } : {}), - }); -} - -async function expectGeneratedMetadataModuleState(params: { - repoRoot: string; - check?: boolean; - expected: { changed?: boolean; wrote?: boolean }; -}) { - const result = await writeGeneratedMetadataModule({ - repoRoot: params.repoRoot, - ...(params.check ? { check: true } : {}), - }); - expect(result).toEqual(expect.objectContaining(params.expected)); - return result; -} - describe("bundled plugin metadata", () => { it( - "matches the generated metadata snapshot", + "matches the runtime metadata snapshot", { timeout: BUNDLED_PLUGIN_METADATA_TEST_TIMEOUT_MS }, - async () => { - await expect(collectBundledPluginMetadata({ repoRoot })).resolves.toEqual( - BUNDLED_PLUGIN_METADATA, - ); + () => { + expect(listBundledPluginMetadata({ rootDir: repoRoot })).toEqual(listBundledPluginMetadata()); }, ); it("captures setup-entry metadata for bundled channel plugins", () => { - const discord = BUNDLED_PLUGIN_METADATA.find((entry) => entry.dirName === "discord"); + const discord = listBundledPluginMetadata().find((entry) => entry.dirName === "discord"); expect(discord?.source).toEqual({ source: "./index.ts", built: "index.js" }); expect(discord?.setupSource).toEqual({ source: "./setup-entry.ts", built: "setup-entry.js" }); expectArtifactPresence(discord?.publicSurfaceArtifacts, { @@ -109,7 +79,7 @@ describe("bundled plugin metadata", () => { }); it("excludes test-only public surface artifacts", () => { - BUNDLED_PLUGIN_METADATA.forEach((entry) => + listBundledPluginMetadata().forEach((entry) => expectTestOnlyArtifactsExcluded(entry.publicSurfaceArtifacts ?? []), ); }); @@ -125,46 +95,7 @@ describe("bundled plugin metadata", () => { expectGeneratedPathResolution(tempRoot, path.join("plugin", "index.js")); }); - it("supports check mode for stale generated artifacts", async () => { - const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-generated-"); - - writeJson(path.join(tempRoot, "extensions", "alpha", "package.json"), { - name: "@openclaw/alpha", - version: "0.0.1", - openclaw: { - extensions: ["./index.ts"], - }, - }); - writeJson(path.join(tempRoot, "extensions", "alpha", "openclaw.plugin.json"), { - id: "alpha", - configSchema: { type: "object" }, - }); - - await expectGeneratedMetadataModuleState({ - repoRoot: tempRoot, - expected: { wrote: true }, - }); - - await expectGeneratedMetadataModuleState({ - repoRoot: tempRoot, - check: true, - expected: { changed: false, wrote: false }, - }); - - fs.writeFileSync( - path.join(tempRoot, "src/plugins/bundled-plugin-metadata.generated.ts"), - "// stale\n", - "utf8", - ); - - await expectGeneratedMetadataModuleState({ - repoRoot: tempRoot, - check: true, - expected: { changed: true, wrote: false }, - }); - }); - - it("merges generated channel schema metadata with manifest-owned channel config fields", async () => { + it("merges runtime channel schema metadata with manifest-owned channel config fields", () => { const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-channel-configs-"); writeJson(path.join(tempRoot, "extensions", "alpha", "package.json"), { @@ -219,7 +150,8 @@ describe("bundled plugin metadata", () => { "utf8", ); - const entries = await collectBundledPluginMetadata({ repoRoot: tempRoot }); + clearBundledPluginMetadataCache(); + const entries = listBundledPluginMetadata({ rootDir: tempRoot }); const channelConfigs = entries[0]?.manifest.channelConfigs as | Record | undefined; @@ -240,7 +172,7 @@ describe("bundled plugin metadata", () => { }); }); - it("captures top-level public surface artifacts without duplicating the primary entrypoints", async () => { + it("captures top-level public surface artifacts without duplicating the primary entrypoints", () => { const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-public-artifacts-"); writeJson(path.join(tempRoot, "extensions", "alpha", "package.json"), { @@ -272,7 +204,8 @@ describe("bundled plugin metadata", () => { "utf8", ); - const entries = await collectBundledPluginMetadata({ repoRoot: tempRoot }); + clearBundledPluginMetadataCache(); + const entries = listBundledPluginMetadata({ rootDir: tempRoot }); const firstEntry = entries[0] as | { publicSurfaceArtifacts?: string[]; @@ -282,4 +215,77 @@ describe("bundled plugin metadata", () => { expect(firstEntry?.publicSurfaceArtifacts).toEqual(["api.js", "runtime-api.js"]); expect(firstEntry?.runtimeSidecarArtifacts).toEqual(["runtime-api.js"]); }); + + it("loads channel config metadata from built public surfaces in dist-only roots", () => { + const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-dist-config-"); + const distRoot = path.join(tempRoot, "dist"); + + writeJson(path.join(distRoot, "extensions", "alpha", "package.json"), { + name: "@openclaw/alpha", + version: "0.0.1", + openclaw: { + extensions: ["./index.ts"], + channel: { + id: "alpha", + label: "Alpha Root Label", + blurb: "Alpha Root Description", + }, + }, + }); + writeJson(path.join(distRoot, "extensions", "alpha", "openclaw.plugin.json"), { + id: "alpha", + channels: ["alpha"], + channelConfigs: { + alpha: { + schema: { type: "object", properties: { stale: { type: "boolean" } } }, + uiHints: { + "channels.alpha.explicitOnly": { + help: "manifest hint", + }, + }, + }, + }, + }); + fs.writeFileSync( + path.join(distRoot, "extensions", "alpha", "index.js"), + "export {};\n", + "utf8", + ); + fs.writeFileSync( + path.join(distRoot, "extensions", "alpha", "channel-config-api.js"), + [ + "export const AlphaChannelConfigSchema = {", + " schema: {", + " type: 'object',", + " properties: { built: { type: 'string' } },", + " },", + " uiHints: {", + " 'channels.alpha.generatedOnly': { help: 'built hint' },", + " },", + "};", + "", + ].join("\n"), + "utf8", + ); + + clearBundledPluginMetadataCache(); + const entries = listBundledPluginMetadata({ rootDir: distRoot }); + const channelConfigs = entries[0]?.manifest.channelConfigs as + | Record + | undefined; + expect(channelConfigs?.alpha).toEqual({ + schema: { + type: "object", + properties: { + built: { type: "string" }, + }, + }, + label: "Alpha Root Label", + description: "Alpha Root Description", + uiHints: { + "channels.alpha.generatedOnly": { help: "built hint" }, + "channels.alpha.explicitOnly": { help: "manifest hint" }, + }, + }); + }); }); diff --git a/src/plugins/bundled-plugin-metadata.ts b/src/plugins/bundled-plugin-metadata.ts index da083d657ce..91fb082e1c8 100644 --- a/src/plugins/bundled-plugin-metadata.ts +++ b/src/plugins/bundled-plugin-metadata.ts @@ -1,20 +1,57 @@ import fs from "node:fs"; import path from "node:path"; -import { GENERATED_BUNDLED_PLUGIN_METADATA } from "./bundled-plugin-metadata.generated.js"; -import type { PluginManifest, OpenClawPackageManifest } from "./manifest.js"; +import { fileURLToPath } from "node:url"; +import { createJiti } from "jiti"; +import { buildChannelConfigSchema } from "../channels/plugins/config-schema.js"; +import { resolveBundledPluginsDir } from "./bundled-dir.js"; +import { + getPackageManifestMetadata, + loadPluginManifest, + type OpenClawPackageManifest, + type PackageManifest, + type PluginManifest, + type PluginManifestChannelConfig, +} from "./manifest.js"; +import { + buildPluginLoaderAliasMap, + buildPluginLoaderJitiOptions, + resolveLoaderPackageRoot, + shouldPreferNativeJiti, +} from "./sdk-alias.js"; +import type { PluginConfigUiHint } from "./types.js"; +const OPENCLAW_PACKAGE_ROOT = + resolveLoaderPackageRoot({ + modulePath: fileURLToPath(import.meta.url), + moduleUrl: import.meta.url, + }) ?? fileURLToPath(new URL("../..", import.meta.url)); const PUBLIC_SURFACE_SOURCE_EXTENSIONS = [".ts", ".mts", ".js", ".mjs", ".cts", ".cjs"] as const; +const RUNTIME_SIDECAR_ARTIFACTS = new Set([ + "helper-api.js", + "light-runtime-api.js", + "runtime-api.js", + "thread-bindings-runtime.js", +]); +const SOURCE_CONFIG_SCHEMA_CANDIDATES = [ + path.join("src", "config-schema.ts"), + path.join("src", "config-schema.js"), + path.join("src", "config-schema.mts"), + path.join("src", "config-schema.mjs"), + path.join("src", "config-schema.cts"), + path.join("src", "config-schema.cjs"), +] as const; +const PUBLIC_CONFIG_SURFACE_BASENAMES = ["channel-config-api", "runtime-api", "api"] as const; -type GeneratedBundledPluginPathPair = { +type BundledPluginPathPair = { source: string; built: string; }; -export type GeneratedBundledPluginMetadata = { +export type BundledPluginMetadata = { dirName: string; idHint: string; - source: GeneratedBundledPluginPathPair; - setupSource?: GeneratedBundledPluginPathPair; + source: BundledPluginPathPair; + setupSource?: BundledPluginPathPair; publicSurfaceArtifacts?: readonly string[]; runtimeSidecarArtifacts?: readonly string[]; packageName?: string; @@ -24,12 +61,409 @@ export type GeneratedBundledPluginMetadata = { manifest: PluginManifest; }; -export const BUNDLED_PLUGIN_METADATA = - GENERATED_BUNDLED_PLUGIN_METADATA as unknown as readonly GeneratedBundledPluginMetadata[]; +type ChannelConfigSurface = { + schema: Record; + uiHints?: Record; +}; + +const bundledPluginMetadataCache = new Map(); +const jitiLoaders = new Map>(); + +export function clearBundledPluginMetadataCache(): void { + bundledPluginMetadataCache.clear(); +} + +function trimString(value: unknown): string | undefined { + return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined; +} + +function normalizeStringList(value: unknown): string[] { + if (!Array.isArray(value)) { + return []; + } + return value.map((entry) => trimString(entry) ?? "").filter(Boolean); +} + +function rewriteEntryToBuiltPath(entry: string | undefined): string | undefined { + if (!entry) { + return undefined; + } + const normalized = entry.replace(/^\.\//u, ""); + return normalized.replace(/\.[^.]+$/u, ".js"); +} + +function readPackageManifest(pluginDir: string): PackageManifest | undefined { + const packagePath = path.join(pluginDir, "package.json"); + if (!fs.existsSync(packagePath)) { + return undefined; + } + try { + return JSON.parse(fs.readFileSync(packagePath, "utf-8")) as PackageManifest; + } catch { + return undefined; + } +} + +function deriveIdHint(params: { + entryPath: string; + manifestId: string; + packageName?: string; + hasMultipleExtensions: boolean; +}): string { + const base = path.basename(params.entryPath, path.extname(params.entryPath)); + if (!params.hasMultipleExtensions) { + return params.manifestId; + } + const packageName = trimString(params.packageName); + if (!packageName) { + return `${params.manifestId}/${base}`; + } + const unscoped = packageName.includes("/") + ? (packageName.split("/").pop() ?? packageName) + : packageName; + return `${unscoped}/${base}`; +} + +function isTopLevelPublicSurfaceSource(name: string): boolean { + if ( + !PUBLIC_SURFACE_SOURCE_EXTENSIONS.includes( + path.extname(name) as (typeof PUBLIC_SURFACE_SOURCE_EXTENSIONS)[number], + ) + ) { + return false; + } + if (name.startsWith(".")) { + return false; + } + if (name.startsWith("test-")) { + return false; + } + if (name.includes(".test-")) { + return false; + } + if (name.endsWith(".d.ts")) { + return false; + } + return !/(\.test|\.spec)(\.[cm]?[jt]s)$/u.test(name); +} + +function collectTopLevelPublicSurfaceArtifacts(params: { + pluginDir: string; + sourceEntry: string; + setupEntry?: string; +}): readonly string[] | undefined { + const excluded = new Set( + [params.sourceEntry, params.setupEntry] + .filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0) + .map((entry) => path.basename(entry)), + ); + const artifacts = fs + .readdirSync(params.pluginDir, { withFileTypes: true }) + .filter((entry) => entry.isFile()) + .map((entry) => entry.name) + .filter(isTopLevelPublicSurfaceSource) + .filter((entry) => !excluded.has(entry)) + .map((entry) => rewriteEntryToBuiltPath(entry)) + .filter((entry): entry is string => typeof entry === "string" && entry.length > 0) + .toSorted((left, right) => left.localeCompare(right)); + return artifacts.length > 0 ? artifacts : undefined; +} + +function collectRuntimeSidecarArtifacts( + publicSurfaceArtifacts: readonly string[] | undefined, +): readonly string[] | undefined { + if (!publicSurfaceArtifacts) { + return undefined; + } + const artifacts = publicSurfaceArtifacts.filter((artifact) => + RUNTIME_SIDECAR_ARTIFACTS.has(artifact), + ); + return artifacts.length > 0 ? artifacts : undefined; +} + +function resolveBundledPluginScanDir(packageRoot: string): string | undefined { + const sourceDir = path.join(packageRoot, "extensions"); + const runtimeDir = path.join(packageRoot, "dist-runtime", "extensions"); + const builtDir = path.join(packageRoot, "dist", "extensions"); + if (fs.existsSync(sourceDir)) { + return sourceDir; + } + if (fs.existsSync(runtimeDir) && fs.existsSync(builtDir)) { + return runtimeDir; + } + if (fs.existsSync(builtDir)) { + return builtDir; + } + return undefined; +} + +function isBuiltChannelConfigSchema(value: unknown): value is ChannelConfigSurface { + if (!value || typeof value !== "object") { + return false; + } + const candidate = value as { schema?: unknown }; + return Boolean(candidate.schema && typeof candidate.schema === "object"); +} + +function resolveConfigSchemaExport(imported: Record): ChannelConfigSurface | null { + for (const [name, value] of Object.entries(imported)) { + if (name.endsWith("ChannelConfigSchema") && isBuiltChannelConfigSchema(value)) { + return value; + } + } + + for (const [name, value] of Object.entries(imported)) { + if (!name.endsWith("ConfigSchema") || name.endsWith("AccountConfigSchema")) { + continue; + } + if (isBuiltChannelConfigSchema(value)) { + return value; + } + if (value && typeof value === "object") { + return buildChannelConfigSchema(value as never); + } + } + + for (const value of Object.values(imported)) { + if (isBuiltChannelConfigSchema(value)) { + return value; + } + } + + return null; +} + +function getJiti(modulePath: string) { + const tryNative = + shouldPreferNativeJiti(modulePath) || modulePath.includes(`${path.sep}dist${path.sep}`); + const aliasMap = buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url); + const cacheKey = JSON.stringify({ + tryNative, + aliasMap: Object.entries(aliasMap).toSorted(([left], [right]) => left.localeCompare(right)), + }); + const cached = jitiLoaders.get(cacheKey); + if (cached) { + return cached; + } + const loader = createJiti(import.meta.url, { + ...buildPluginLoaderJitiOptions(aliasMap), + tryNative, + }); + jitiLoaders.set(cacheKey, loader); + return loader; +} + +function resolveChannelConfigSchemaModulePath(pluginDir: string): string | undefined { + for (const relativePath of SOURCE_CONFIG_SCHEMA_CANDIDATES) { + const candidate = path.join(pluginDir, relativePath); + if (fs.existsSync(candidate)) { + return candidate; + } + } + for (const basename of PUBLIC_CONFIG_SURFACE_BASENAMES) { + for (const extension of PUBLIC_SURFACE_SOURCE_EXTENSIONS) { + const candidate = path.join(pluginDir, `${basename}${extension}`); + if (fs.existsSync(candidate)) { + return candidate; + } + } + } + return undefined; +} + +function loadChannelConfigSurfaceModuleSync(modulePath: string): ChannelConfigSurface | null { + try { + const imported = getJiti(modulePath)(modulePath) as Record; + return resolveConfigSchemaExport(imported); + } catch { + return null; + } +} + +function resolvePackageChannelMeta( + packageManifest: OpenClawPackageManifest | undefined, + channelId: string, +): OpenClawPackageManifest["channel"] | undefined { + const channelMeta = packageManifest?.channel; + return channelMeta?.id?.trim() === channelId ? channelMeta : undefined; +} + +function collectBundledChannelConfigs(params: { + pluginDir: string; + manifest: PluginManifest; + packageManifest?: OpenClawPackageManifest; +}): Record | undefined { + const channelIds = normalizeStringList(params.manifest.channels); + const existingChannelConfigs: Record = + params.manifest.channelConfigs && Object.keys(params.manifest.channelConfigs).length > 0 + ? { ...params.manifest.channelConfigs } + : {}; + if (channelIds.length === 0) { + return Object.keys(existingChannelConfigs).length > 0 ? existingChannelConfigs : undefined; + } + + const surfaceModulePath = resolveChannelConfigSchemaModulePath(params.pluginDir); + const surface = surfaceModulePath ? loadChannelConfigSurfaceModuleSync(surfaceModulePath) : null; + + for (const channelId of channelIds) { + const existing = existingChannelConfigs[channelId]; + const channelMeta = resolvePackageChannelMeta(params.packageManifest, channelId); + const preferOver = normalizeStringList(channelMeta?.preferOver); + const uiHints: Record | undefined = + surface?.uiHints || existing?.uiHints + ? { + ...(surface?.uiHints && Object.keys(surface.uiHints).length > 0 ? surface.uiHints : {}), + ...(existing?.uiHints && Object.keys(existing.uiHints).length > 0 + ? existing.uiHints + : {}), + } + : undefined; + + if (!surface?.schema && !existing?.schema) { + continue; + } + + existingChannelConfigs[channelId] = { + schema: surface?.schema ?? existing?.schema ?? {}, + ...(uiHints && Object.keys(uiHints).length > 0 ? { uiHints } : {}), + ...((trimString(existing?.label) ?? trimString(channelMeta?.label)) + ? { label: trimString(existing?.label) ?? trimString(channelMeta?.label)! } + : {}), + ...((trimString(existing?.description) ?? trimString(channelMeta?.blurb)) + ? { + description: trimString(existing?.description) ?? trimString(channelMeta?.blurb)!, + } + : {}), + ...(existing?.preferOver?.length + ? { preferOver: existing.preferOver } + : preferOver.length > 0 + ? { preferOver } + : {}), + }; + } + + return Object.keys(existingChannelConfigs).length > 0 ? existingChannelConfigs : undefined; +} + +function collectBundledPluginMetadataForPackageRoot( + packageRoot: string, +): readonly BundledPluginMetadata[] { + const scanDir = resolveBundledPluginScanDir(packageRoot); + if (!scanDir || !fs.existsSync(scanDir)) { + return []; + } + + const entries: BundledPluginMetadata[] = []; + for (const dirName of fs + .readdirSync(scanDir, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name) + .toSorted((left, right) => left.localeCompare(right))) { + const pluginDir = path.join(scanDir, dirName); + const manifestResult = loadPluginManifest(pluginDir, false); + if (!manifestResult.ok) { + continue; + } + + const packageJson = readPackageManifest(pluginDir); + const packageManifest = getPackageManifestMetadata(packageJson); + const extensions = normalizeStringList(packageManifest?.extensions); + if (extensions.length === 0) { + continue; + } + const sourceEntry = trimString(extensions[0]); + const builtEntry = rewriteEntryToBuiltPath(sourceEntry); + if (!sourceEntry || !builtEntry) { + continue; + } + + const setupSourcePath = trimString(packageManifest?.setupEntry); + const setupSource = + setupSourcePath && rewriteEntryToBuiltPath(setupSourcePath) + ? { + source: setupSourcePath, + built: rewriteEntryToBuiltPath(setupSourcePath)!, + } + : undefined; + const publicSurfaceArtifacts = collectTopLevelPublicSurfaceArtifacts({ + pluginDir, + sourceEntry, + ...(setupSourcePath ? { setupEntry: setupSourcePath } : {}), + }); + const runtimeSidecarArtifacts = collectRuntimeSidecarArtifacts(publicSurfaceArtifacts); + const channelConfigs = collectBundledChannelConfigs({ + pluginDir, + manifest: manifestResult.manifest, + packageManifest, + }); + + entries.push({ + dirName, + idHint: deriveIdHint({ + entryPath: sourceEntry, + manifestId: manifestResult.manifest.id, + packageName: trimString(packageJson?.name), + hasMultipleExtensions: extensions.length > 1, + }), + source: { + source: sourceEntry, + built: builtEntry, + }, + ...(setupSource ? { setupSource } : {}), + ...(publicSurfaceArtifacts ? { publicSurfaceArtifacts } : {}), + ...(runtimeSidecarArtifacts ? { runtimeSidecarArtifacts } : {}), + ...(trimString(packageJson?.name) ? { packageName: trimString(packageJson?.name) } : {}), + ...(trimString(packageJson?.version) + ? { packageVersion: trimString(packageJson?.version) } + : {}), + ...(trimString(packageJson?.description) + ? { packageDescription: trimString(packageJson?.description) } + : {}), + ...(packageManifest ? { packageManifest } : {}), + manifest: { + ...manifestResult.manifest, + ...(channelConfigs ? { channelConfigs } : {}), + }, + }); + } + + return entries; +} + +export function listBundledPluginMetadata(params?: { + rootDir?: string; +}): readonly BundledPluginMetadata[] { + const rootDir = path.resolve(params?.rootDir ?? OPENCLAW_PACKAGE_ROOT); + const cached = bundledPluginMetadataCache.get(rootDir); + if (cached) { + return cached; + } + const entries = Object.freeze(collectBundledPluginMetadataForPackageRoot(rootDir)); + bundledPluginMetadataCache.set(rootDir, entries); + return entries; +} + +export function findBundledPluginMetadataById( + pluginId: string, + params?: { rootDir?: string }, +): BundledPluginMetadata | undefined { + return listBundledPluginMetadata(params).find((entry) => entry.manifest.id === pluginId); +} + +export function resolveBundledPluginWorkspaceSourcePath(params: { + rootDir: string; + pluginId: string; +}): string | null { + const metadata = findBundledPluginMetadataById(params.pluginId, { rootDir: params.rootDir }); + if (!metadata) { + return null; + } + return path.resolve(params.rootDir, "extensions", metadata.dirName); +} export function resolveBundledPluginGeneratedPath( rootDir: string, - entry: GeneratedBundledPluginPathPair | undefined, + entry: BundledPluginPathPair | undefined, ): string | null { if (!entry) { return null; @@ -51,21 +485,39 @@ export function resolveBundledPluginPublicSurfacePath(params: { rootDir: string; dirName: string; artifactBasename: string; + env?: NodeJS.ProcessEnv; + bundledPluginsDir?: string; }): string | null { const artifactBasename = params.artifactBasename.replace(/^\.\//u, ""); if (!artifactBasename) { return null; } - const builtCandidate = path.resolve( - params.rootDir, - "dist", - "extensions", - params.dirName, - artifactBasename, - ); - if (fs.existsSync(builtCandidate)) { - return builtCandidate; + const explicitBundledPluginsDir = + params.bundledPluginsDir ?? resolveBundledPluginsDir(params.env ?? process.env); + if (explicitBundledPluginsDir) { + const explicitPluginDir = path.resolve(explicitBundledPluginsDir, params.dirName); + const explicitBuiltCandidate = path.join(explicitPluginDir, artifactBasename); + if (fs.existsSync(explicitBuiltCandidate)) { + return explicitBuiltCandidate; + } + + const sourceBaseName = artifactBasename.replace(/\.js$/u, ""); + for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) { + const sourceCandidate = path.join(explicitPluginDir, `${sourceBaseName}${ext}`); + if (fs.existsSync(sourceCandidate)) { + return sourceCandidate; + } + } + } + + for (const candidate of [ + path.resolve(params.rootDir, "dist", "extensions", params.dirName, artifactBasename), + path.resolve(params.rootDir, "dist-runtime", "extensions", params.dirName, artifactBasename), + ]) { + if (fs.existsSync(candidate)) { + return candidate; + } } const sourceBaseName = artifactBasename.replace(/\.js$/u, ""); diff --git a/src/plugins/bundled-sources.test.ts b/src/plugins/bundled-sources.test.ts index 75f3c284223..f0ab77a7019 100644 --- a/src/plugins/bundled-sources.test.ts +++ b/src/plugins/bundled-sources.test.ts @@ -1,10 +1,17 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { bundledPluginRootAt } from "../../test/helpers/bundled-plugin-paths.js"; import { findBundledPluginSource, findBundledPluginSourceInMap, resolveBundledPluginSources, } from "./bundled-sources.js"; +const APP_ROOT = "/app"; + +function appBundledPluginRoot(pluginId: string): string { + return bundledPluginRootAt(APP_ROOT, pluginId); +} + const discoverOpenClawPluginsMock = vi.fn(); const loadPluginManifestMock = vi.fn(); @@ -56,17 +63,17 @@ function setBundledManifestIdsByRoot(manifestIds: Record) { function setBundledLookupFixture() { setBundledDiscoveryCandidates([ createBundledCandidate({ - rootDir: "/app/extensions/feishu", + rootDir: appBundledPluginRoot("feishu"), packageName: "@openclaw/feishu", }), createBundledCandidate({ - rootDir: "/app/extensions/diffs", + rootDir: appBundledPluginRoot("diffs"), packageName: "@openclaw/diffs", }), ]); setBundledManifestIdsByRoot({ - "/app/extensions/feishu": "feishu", - "/app/extensions/diffs": "diffs", + [appBundledPluginRoot("feishu")]: "feishu", + [appBundledPluginRoot("diffs")]: "diffs", }); } @@ -127,21 +134,21 @@ describe("bundled plugin sources", () => { packageName: "@openclaw/feishu", }), createBundledCandidate({ - rootDir: "/app/extensions/feishu", + rootDir: appBundledPluginRoot("feishu"), packageName: "@openclaw/feishu", }), createBundledCandidate({ - rootDir: "/app/extensions/feishu-dup", + rootDir: appBundledPluginRoot("feishu-dup"), packageName: "@openclaw/feishu", }), createBundledCandidate({ - rootDir: "/app/extensions/msteams", + rootDir: appBundledPluginRoot("msteams"), packageName: "@openclaw/msteams", }), ]); setBundledManifestIdsByRoot({ - "/app/extensions/feishu": "feishu", - "/app/extensions/msteams": "msteams", + [appBundledPluginRoot("feishu")]: "feishu", + [appBundledPluginRoot("msteams")]: "msteams", }); const map = resolveBundledPluginSources({}); @@ -150,7 +157,7 @@ describe("bundled plugin sources", () => { expect(map.get("feishu")).toEqual( createResolvedBundledSource({ pluginId: "feishu", - localPath: "/app/extensions/feishu", + localPath: appBundledPluginRoot("feishu"), }), ); }); @@ -159,7 +166,7 @@ describe("bundled plugin sources", () => { [ "finds bundled source by npm spec", { kind: "npmSpec", value: "@openclaw/feishu" } as const, - { pluginId: "feishu", localPath: "/app/extensions/feishu" }, + { pluginId: "feishu", localPath: appBundledPluginRoot("feishu") }, ], [ "returns undefined for missing npm spec", @@ -169,7 +176,7 @@ describe("bundled plugin sources", () => { [ "finds bundled source by plugin id", { kind: "pluginId", value: "diffs" } as const, - { pluginId: "diffs", localPath: "/app/extensions/diffs" }, + { pluginId: "diffs", localPath: appBundledPluginRoot("diffs") }, ], [ "returns undefined for missing plugin id", @@ -211,7 +218,7 @@ describe("bundled plugin sources", () => { "feishu", createResolvedBundledSource({ pluginId: "feishu", - localPath: "/app/extensions/feishu", + localPath: appBundledPluginRoot("feishu"), }), ], ]); @@ -224,7 +231,7 @@ describe("bundled plugin sources", () => { ).toEqual( createResolvedBundledSource({ pluginId: "feishu", - localPath: "/app/extensions/feishu", + localPath: appBundledPluginRoot("feishu"), }), ); expect( diff --git a/src/plugins/bundled-sources.ts b/src/plugins/bundled-sources.ts index 57745c58388..e604c17dcc6 100644 --- a/src/plugins/bundled-sources.ts +++ b/src/plugins/bundled-sources.ts @@ -84,3 +84,20 @@ export function findBundledPluginSource(params: { lookup: params.lookup, }); } + +export function resolveBundledPluginInstallCommandHint(params: { + pluginId: string; + workspaceDir?: string; + /** Use an explicit env when bundled roots should resolve independently from process.env. */ + env?: NodeJS.ProcessEnv; +}): string | null { + const bundledSource = findBundledPluginSource({ + lookup: { kind: "pluginId", value: params.pluginId }, + workspaceDir: params.workspaceDir, + env: params.env, + }); + if (!bundledSource?.localPath) { + return null; + } + return `openclaw plugins install ${bundledSource.localPath}`; +} diff --git a/src/plugins/contracts/discovery.contract.test.ts b/src/plugins/contracts/discovery.contract.test.ts index ea6c98f8f6d..5a2a6258dba 100644 --- a/src/plugins/contracts/discovery.contract.test.ts +++ b/src/plugins/contracts/discovery.contract.test.ts @@ -1,6 +1,10 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { AuthProfileStore } from "../../agents/auth-profiles/types.js"; import type { ModelDefinitionConfig } from "../../config/types.models.js"; +import { + loadBundledPluginPublicSurfaceSync, + resolveRelativeBundledPluginPublicModuleId, +} from "../../test-utils/bundled-plugin-public-surface.js"; import { registerProviders, requireProvider } from "./testkit.js"; const resolveCopilotApiTokenMock = vi.hoisted(() => vi.fn()); @@ -136,6 +140,21 @@ function runCatalog(params: { describe("provider discovery contract", () => { beforeEach(async () => { + const githubCopilotTokenModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "github-copilot", + artifactBasename: "token.js", + }); + const vllmApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "vllm", + artifactBasename: "api.js", + }); + const sglangApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "sglang", + artifactBasename: "api.js", + }); vi.resetModules(); vi.doMock("openclaw/plugin-sdk/agent-runtime", async () => { // Import the direct source module, not the mocked subpath, so bundled @@ -155,8 +174,8 @@ describe("provider discovery contract", () => { listProfilesForProvider: listProfilesForProviderMock, }; }); - vi.doMock("../../../extensions/github-copilot/token.js", async () => { - const actual = await vi.importActual("../../../extensions/github-copilot/token.js"); + vi.doMock(githubCopilotTokenModuleId, async () => { + const actual = await vi.importActual(githubCopilotTokenModuleId); return { ...actual, resolveCopilotApiToken: resolveCopilotApiTokenMock, @@ -181,15 +200,15 @@ describe("provider discovery contract", () => { buildSglangProvider: (...args: unknown[]) => buildSglangProviderMock(...args), }; }); - vi.doMock("../../../extensions/vllm/api.js", async () => { - const actual = await vi.importActual("../../../extensions/vllm/api.js"); + vi.doMock(vllmApiModuleId, async () => { + const actual = await vi.importActual(vllmApiModuleId); return { ...actual, buildVllmProvider: (...args: unknown[]) => buildVllmProviderMock(...args), }; }); - vi.doMock("../../../extensions/sglang/api.js", async () => { - const actual = await vi.importActual("../../../extensions/sglang/api.js"); + vi.doMock(sglangApiModuleId, async () => { + const actual = await vi.importActual(sglangApiModuleId); return { ...actual, buildSglangProvider: (...args: unknown[]) => buildSglangProviderMock(...args), @@ -205,13 +224,27 @@ describe("provider discovery contract", () => { { default: modelStudioPlugin }, { default: cloudflareAiGatewayPlugin }, ] = await Promise.all([ - import("../../../extensions/github-copilot/index.js"), - import("../../../extensions/ollama/index.js"), - import("../../../extensions/vllm/index.js"), - import("../../../extensions/sglang/index.js"), - import("../../../extensions/minimax/index.js"), - import("../../../extensions/modelstudio/index.js"), - import("../../../extensions/cloudflare-ai-gateway/index.js"), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "github-copilot", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "ollama", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "vllm", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "sglang", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "minimax", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "modelstudio", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "cloudflare-ai-gateway", artifactBasename: "index.js" }), ]); githubCopilotProvider = requireProvider( registerProviders(githubCopilotPlugin), diff --git a/src/plugins/discovery.test.ts b/src/plugins/discovery.test.ts index 1f325d0c4d0..aa0782e8ead 100644 --- a/src/plugins/discovery.test.ts +++ b/src/plugins/discovery.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; +import { bundledDistPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; import { clearPluginDiscoveryCache, discoverOpenClawPlugins } from "./discovery.js"; import { cleanupTrackedTempDirs, @@ -371,7 +372,7 @@ describe("discoverOpenClawPlugins", () => { writePluginPackageManifest({ packageDir: path.join(pluginDir, "node_modules", "openclaw"), packageName: "openclaw", - extensions: ["./dist/extensions/diffs/index.js"], + extensions: [`./${bundledDistPluginFile("diffs", "index.js")}`], }); writePluginManifest({ pluginDir: nestedDiffsDir, id: "diffs" }); fs.writeFileSync( diff --git a/src/plugins/discovery.ts b/src/plugins/discovery.ts index 910f33ccf32..307ae359306 100644 --- a/src/plugins/discovery.ts +++ b/src/plugins/discovery.ts @@ -3,10 +3,6 @@ import path from "node:path"; import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js"; import { resolveUserPath } from "../utils.js"; import { detectBundleManifestFormat, loadBundleManifest } from "./bundle-manifest.js"; -import { - BUNDLED_PLUGIN_METADATA, - resolveBundledPluginGeneratedPath, -} from "./bundled-plugin-metadata.js"; import { DEFAULT_PLUGIN_ENTRY_CANDIDATES, getPackageManifestMetadata, @@ -471,11 +467,56 @@ function resolvePackageEntrySource(params: { rejectHardlinks?: boolean; }): string | null { const source = path.resolve(params.packageDir, params.entryPath); + const rejectHardlinks = params.rejectHardlinks ?? true; + const candidates = [source]; + if (!rejectHardlinks) { + const builtCandidate = source.replace(/\.[^.]+$/u, ".js"); + if (builtCandidate !== source) { + candidates.push(builtCandidate); + } + } + + for (const candidate of new Set(candidates)) { + if (!fs.existsSync(candidate)) { + continue; + } + const opened = openBoundaryFileSync({ + absolutePath: candidate, + rootPath: params.packageDir, + boundaryLabel: "plugin package directory", + rejectHardlinks, + }); + if (!opened.ok) { + return matchBoundaryFileOpenFailure(opened, { + path: () => null, + io: () => { + params.diagnostics.push({ + level: "warn", + message: `extension entry unreadable (I/O error): ${params.entryPath}`, + source: params.sourceLabel, + }); + return null; + }, + fallback: () => { + params.diagnostics.push({ + level: "error", + message: `extension entry escapes package directory: ${params.entryPath}`, + source: params.sourceLabel, + }); + return null; + }, + }); + } + const safeSource = opened.path; + fs.closeSync(opened.fd); + return safeSource; + } + const opened = openBoundaryFileSync({ absolutePath: source, rootPath: params.packageDir, boundaryLabel: "plugin package directory", - rejectHardlinks: params.rejectHardlinks ?? true, + rejectHardlinks, }); if (!opened.ok) { return matchBoundaryFileOpenFailure(opened, { @@ -788,62 +829,6 @@ function discoverFromPath(params: { } } -function discoverBundledMetadataInDirectory(params: { - dir: string; - ownershipUid?: number | null; - candidates: PluginCandidate[]; - diagnostics: PluginDiagnostic[]; - seen: Set; -}) { - if (!fs.existsSync(params.dir)) { - return null; - } - - const coveredDirectories = new Set(); - for (const entry of BUNDLED_PLUGIN_METADATA) { - const rootDir = path.join(params.dir, entry.dirName); - if (!fs.existsSync(rootDir)) { - continue; - } - coveredDirectories.add(entry.dirName); - const source = resolveBundledPluginGeneratedPath(rootDir, entry.source); - if (!source) { - continue; - } - const setupSource = resolveBundledPluginGeneratedPath(rootDir, entry.setupSource); - const packageManifest = readPackageManifest(rootDir, false); - addCandidate({ - candidates: params.candidates, - diagnostics: params.diagnostics, - seen: params.seen, - idHint: entry.idHint, - source, - ...(setupSource ? { setupSource } : {}), - rootDir, - origin: "bundled", - ownershipUid: params.ownershipUid, - manifest: { - ...packageManifest, - ...(!packageManifest?.name && entry.packageName ? { name: entry.packageName } : {}), - ...(!packageManifest?.version && entry.packageVersion - ? { version: entry.packageVersion } - : {}), - ...(!packageManifest?.description && entry.packageDescription - ? { description: entry.packageDescription } - : {}), - ...(!packageManifest?.openclaw && entry.packageManifest - ? { openclaw: entry.packageManifest } - : {}), - }, - packageDir: rootDir, - bundledManifest: entry.manifest, - bundledManifestPath: path.join(rootDir, "openclaw.plugin.json"), - }); - } - - return coveredDirectories; -} - export function discoverOpenClawPlugins(params: { workspaceDir?: string; extraPaths?: string[]; @@ -906,13 +891,6 @@ export function discoverOpenClawPlugins(params: { } if (roots.stock) { - const coveredBundledDirectories = discoverBundledMetadataInDirectory({ - dir: roots.stock, - ownershipUid: params.ownershipUid, - candidates, - diagnostics, - seen, - }); discoverInDirectory({ dir: roots.stock, origin: "bundled", @@ -920,7 +898,6 @@ export function discoverOpenClawPlugins(params: { candidates, diagnostics, seen, - ...(coveredBundledDirectories ? { skipDirectories: coveredBundledDirectories } : {}), }); } diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index feece504123..2c0c188747a 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -830,7 +830,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi const getJiti = (modulePath: string) => { const tryNative = shouldPreferNativeJiti(modulePath); // Pass loader's moduleUrl so the openclaw root can always be resolved even when - // loading external plugins from outside the installation directory (e.g. ~/.openclaw/extensions/). + // loading external plugins from outside the managed install directory. const aliasMap = buildPluginLoaderAliasMap( modulePath, process.argv[1], diff --git a/src/plugins/public-artifacts.ts b/src/plugins/public-artifacts.ts index 4c6ecfad77f..c7cdf06ec2b 100644 --- a/src/plugins/public-artifacts.ts +++ b/src/plugins/public-artifacts.ts @@ -1,4 +1,4 @@ -import { BUNDLED_PLUGIN_METADATA } from "./bundled-plugin-metadata.js"; +import { listBundledPluginMetadata } from "./bundled-plugin-metadata.js"; function assertUniqueValues(values: readonly T[], label: string): readonly T[] { const seen = new Set(); @@ -20,12 +20,18 @@ export function getPublicArtifactBasename(relativePath: string): string { return relativePath.split("/").at(-1) ?? relativePath; } +function buildBundledDistArtifactPath(dirName: string, artifact: string): string { + return ["dist", "extensions", dirName, artifact].join("/"); +} + export const BUNDLED_RUNTIME_SIDECAR_PATHS = assertUniqueValues( - BUNDLED_PLUGIN_METADATA.flatMap((entry) => - (entry.runtimeSidecarArtifacts ?? []).map( - (artifact) => `dist/extensions/${entry.dirName}/${artifact}`, - ), - ).toSorted((left, right) => left.localeCompare(right)), + listBundledPluginMetadata() + .flatMap((entry) => + (entry.runtimeSidecarArtifacts ?? []).map((artifact) => + buildBundledDistArtifactPath(entry.dirName, artifact), + ), + ) + .toSorted((left, right) => left.localeCompare(right)), "bundled runtime sidecar path", ); diff --git a/src/plugins/runtime-live-state-guardrails.test.ts b/src/plugins/runtime-live-state-guardrails.test.ts index 0538df424e2..c239bd022da 100644 --- a/src/plugins/runtime-live-state-guardrails.test.ts +++ b/src/plugins/runtime-live-state-guardrails.test.ts @@ -2,6 +2,7 @@ import { readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; +import { bundledPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); @@ -12,7 +13,7 @@ const LIVE_RUNTIME_STATE_GUARDS: Record< forbidden: readonly string[]; } > = { - "extensions/whatsapp/src/active-listener.ts": { + [bundledPluginFile("whatsapp", "src/active-listener.ts")]: { required: ["globalThis", 'Symbol.for("openclaw.whatsapp.activeListenerState")'], forbidden: ["resolveGlobalSingleton"], }, diff --git a/src/plugins/runtime-plugin-boundary.whatsapp.test.ts b/src/plugins/runtime-plugin-boundary.whatsapp.test.ts index 7d92cf58f1b..0a44ace6ac3 100644 --- a/src/plugins/runtime-plugin-boundary.whatsapp.test.ts +++ b/src/plugins/runtime-plugin-boundary.whatsapp.test.ts @@ -3,6 +3,7 @@ import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { stageBundledPluginRuntime } from "../../scripts/stage-bundled-plugin-runtime.mjs"; +import { bundledDistPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; import { loadPluginBoundaryModuleWithJiti } from "./runtime/runtime-plugin-boundary.js"; type LightModule = { @@ -44,10 +45,10 @@ function createBundledWhatsAppRuntimeFixture() { 2, ), "openclaw.mjs": "export {};\n", - "dist/extensions/whatsapp/index.js": "export default {};\n", - "dist/extensions/whatsapp/light-runtime-api.js": + [bundledDistPluginFile("whatsapp", "index.js")]: "export default {};\n", + [bundledDistPluginFile("whatsapp", "light-runtime-api.js")]: 'export { getActiveWebListener } from "../../active-listener.js";\n', - "dist/extensions/whatsapp/runtime-api.js": + [bundledDistPluginFile("whatsapp", "runtime-api.js")]: 'export { getActiveWebListener, setActiveWebListener } from "../../active-listener.js";\n', "dist/active-listener.js": [ 'const key = Symbol.for("openclaw.whatsapp.activeListenerState");', diff --git a/src/plugins/runtime/index.ts b/src/plugins/runtime/index.ts index 759cfd58f50..60815d26659 100644 --- a/src/plugins/runtime/index.ts +++ b/src/plugins/runtime/index.ts @@ -1,8 +1,4 @@ import { resolveStateDir } from "../../config/paths.js"; -import { - listRuntimeImageGenerationProviders, - generateImage, -} from "../../plugin-sdk/image-generation-runtime.js"; import { resolveGlobalSingleton } from "../../shared/global-singleton.js"; import { createLazyRuntimeMethod, @@ -11,6 +7,7 @@ import { } from "../../shared/lazy-runtime.js"; import { VERSION } from "../../version.js"; import { listWebSearchProviders, runWebSearch } from "../../web-search/runtime.js"; +import { loadSiblingRuntimeModuleSync } from "./local-runtime-module.js"; import { createRuntimeAgent } from "./runtime-agent.js"; import { defineCachedValue } from "./runtime-cache.js"; import { createRuntimeChannel } from "./runtime-channel.js"; @@ -53,6 +50,27 @@ function createRuntimeMediaUnderstandingFacade(): PluginRuntime["mediaUnderstand }; } +type RuntimeImageGenerationModule = typeof import("./runtime-image-generation.runtime.js"); +let cachedRuntimeImageGenerationModule: RuntimeImageGenerationModule | null = null; + +function loadRuntimeImageGenerationModule(): RuntimeImageGenerationModule { + cachedRuntimeImageGenerationModule ??= loadSiblingRuntimeModuleSync( + { + moduleUrl: import.meta.url, + relativeBase: "./runtime-image-generation.runtime", + }, + ); + return cachedRuntimeImageGenerationModule; +} + +function createRuntimeImageGeneration(): PluginRuntime["imageGeneration"] { + return { + generate: (params) => loadRuntimeImageGenerationModule().generateImage(params), + listProviders: (params) => + loadRuntimeImageGenerationModule().listRuntimeImageGenerationProviders(params), + }; +} + function createRuntimeModelAuth(): PluginRuntime["modelAuth"] { const getApiKeyForModel = createLazyRuntimeMethod( loadModelAuthRuntime, @@ -175,10 +193,6 @@ export function createPluginRuntime(_options: CreatePluginRuntimeOptions = {}): ), system: createRuntimeSystem(), media: createRuntimeMedia(), - imageGeneration: { - generate: generateImage, - listProviders: listRuntimeImageGenerationProviders, - }, webSearch: { listProviders: listWebSearchProviders, search: runWebSearch, @@ -187,8 +201,13 @@ export function createPluginRuntime(_options: CreatePluginRuntimeOptions = {}): events: createRuntimeEvents(), logging: createRuntimeLogging(), state: { resolveStateDir }, - } satisfies Omit & - Partial>; + } satisfies Omit< + PluginRuntime, + "tts" | "mediaUnderstanding" | "stt" | "modelAuth" | "imageGeneration" + > & + Partial< + Pick + >; defineCachedValue(runtime, "tts", createRuntimeTts); defineCachedValue(runtime, "mediaUnderstanding", () => mediaUnderstanding); @@ -196,6 +215,7 @@ export function createPluginRuntime(_options: CreatePluginRuntimeOptions = {}): transcribeAudioFile: mediaUnderstanding.transcribeAudioFile, })); defineCachedValue(runtime, "modelAuth", createRuntimeModelAuth); + defineCachedValue(runtime, "imageGeneration", createRuntimeImageGeneration); return runtime as PluginRuntime; } diff --git a/src/plugins/runtime/local-runtime-module.ts b/src/plugins/runtime/local-runtime-module.ts new file mode 100644 index 00000000000..db52a20403c --- /dev/null +++ b/src/plugins/runtime/local-runtime-module.ts @@ -0,0 +1,51 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { createJiti } from "jiti"; +import { + buildPluginLoaderAliasMap, + buildPluginLoaderJitiOptions, + shouldPreferNativeJiti, +} from "../sdk-alias.js"; + +const RUNTIME_MODULE_EXTENSIONS = [".js", ".ts", ".mjs", ".mts", ".cjs", ".cts"] as const; +const jitiLoaders = new Map>(); + +function resolveSiblingRuntimeModulePath(moduleUrl: string, relativeBase: string): string { + const baseDir = path.dirname(fileURLToPath(moduleUrl)); + for (const ext of RUNTIME_MODULE_EXTENSIONS) { + const candidate = path.resolve(baseDir, `${relativeBase}${ext}`); + if (fs.existsSync(candidate)) { + return candidate; + } + } + throw new Error(`Unable to resolve runtime module ${relativeBase} from ${moduleUrl}`); +} + +function getJiti(modulePath: string, moduleUrl: string) { + const tryNative = shouldPreferNativeJiti(modulePath); + const aliasMap = buildPluginLoaderAliasMap(modulePath, process.argv[1], moduleUrl); + const cacheKey = JSON.stringify({ + tryNative, + aliasMap: Object.entries(aliasMap).toSorted(([left], [right]) => left.localeCompare(right)), + moduleUrl, + }); + const cached = jitiLoaders.get(cacheKey); + if (cached) { + return cached; + } + const loader = createJiti(moduleUrl, { + ...buildPluginLoaderJitiOptions(aliasMap), + tryNative, + }); + jitiLoaders.set(cacheKey, loader); + return loader; +} + +export function loadSiblingRuntimeModuleSync(params: { + moduleUrl: string; + relativeBase: string; +}): T { + const modulePath = resolveSiblingRuntimeModulePath(params.moduleUrl, params.relativeBase); + return getJiti(modulePath, params.moduleUrl)(modulePath) as T; +} diff --git a/src/plugins/runtime/runtime-channel.ts b/src/plugins/runtime/runtime-channel.ts index ba961768645..ac0d55405c4 100644 --- a/src/plugins/runtime/runtime-channel.ts +++ b/src/plugins/runtime/runtime-channel.ts @@ -56,29 +56,11 @@ import { readChannelAllowFromStore, upsertChannelPairingRequest, } from "../../pairing/pairing-store.js"; -import { - buildTemplateMessageFromPayload, - createQuickReplyItems, - monitorLineProvider, - probeLineBot, - pushFlexMessage, - pushLocationMessage, - pushMessageLine, - pushMessagesLine, - pushTemplateMessage, - pushTextMessageWithQuickReplies, - sendMessageLine, -} from "../../plugin-sdk/line-runtime.js"; -import { - listLineAccountIds, - normalizeAccountId as normalizeLineAccountId, - resolveDefaultLineAccountId, - resolveLineAccount, -} from "../../plugin-sdk/line.js"; import { buildAgentSessionKey, resolveAgentRoute } from "../../routing/resolve-route.js"; import { defineCachedValue } from "./runtime-cache.js"; import { createRuntimeDiscord } from "./runtime-discord.js"; import { createRuntimeIMessage } from "./runtime-imessage.js"; +import { createRuntimeLine } from "./runtime-line.js"; import { createRuntimeMatrix } from "./runtime-matrix.js"; import { createRuntimeSignal } from "./runtime-signal.js"; import { createRuntimeSlack } from "./runtime-slack.js"; @@ -169,31 +151,14 @@ export function createRuntimeChannel(): PluginRuntime["channel"] { shouldComputeCommandAuthorized, shouldHandleTextCommands, }, - line: { - listLineAccountIds, - resolveDefaultLineAccountId, - resolveLineAccount, - normalizeAccountId: normalizeLineAccountId, - probeLineBot, - sendMessageLine, - pushMessageLine, - pushMessagesLine, - pushFlexMessage, - pushTemplateMessage, - pushLocationMessage, - pushTextMessageWithQuickReplies, - createQuickReplyItems, - buildTemplateMessageFromPayload, - monitorLineProvider, - }, } satisfies Omit< PluginRuntime["channel"], - "discord" | "slack" | "telegram" | "matrix" | "signal" | "imessage" | "whatsapp" + "discord" | "slack" | "telegram" | "matrix" | "signal" | "imessage" | "whatsapp" | "line" > & Partial< Pick< PluginRuntime["channel"], - "discord" | "slack" | "telegram" | "matrix" | "signal" | "imessage" | "whatsapp" + "discord" | "slack" | "telegram" | "matrix" | "signal" | "imessage" | "whatsapp" | "line" > >; @@ -204,6 +169,7 @@ export function createRuntimeChannel(): PluginRuntime["channel"] { defineCachedValue(channelRuntime, "signal", createRuntimeSignal); defineCachedValue(channelRuntime, "imessage", createRuntimeIMessage); defineCachedValue(channelRuntime, "whatsapp", createRuntimeWhatsApp); + defineCachedValue(channelRuntime, "line", createRuntimeLine); return channelRuntime as PluginRuntime["channel"]; } diff --git a/src/plugins/runtime/runtime-image-generation.runtime.ts b/src/plugins/runtime/runtime-image-generation.runtime.ts new file mode 100644 index 00000000000..96214975b64 --- /dev/null +++ b/src/plugins/runtime/runtime-image-generation.runtime.ts @@ -0,0 +1,4 @@ +export { + generateImage, + listRuntimeImageGenerationProviders, +} from "../../plugin-sdk/image-generation-runtime.js"; diff --git a/src/plugins/runtime/runtime-line.contract.ts b/src/plugins/runtime/runtime-line.contract.ts new file mode 100644 index 00000000000..3043ad88cb2 --- /dev/null +++ b/src/plugins/runtime/runtime-line.contract.ts @@ -0,0 +1,38 @@ +import { + buildTemplateMessageFromPayload, + createQuickReplyItems, + monitorLineProvider, + probeLineBot, + pushFlexMessage, + pushLocationMessage, + pushMessageLine, + pushMessagesLine, + pushTemplateMessage, + pushTextMessageWithQuickReplies, + sendMessageLine, +} from "../../plugin-sdk/line-runtime.js"; +import { + listLineAccountIds, + normalizeAccountId, + resolveDefaultLineAccountId, + resolveLineAccount, +} from "../../plugin-sdk/line.js"; +import type { PluginRuntimeChannel } from "./types-channel.js"; + +export const runtimeLine = { + listLineAccountIds, + resolveDefaultLineAccountId, + resolveLineAccount, + normalizeAccountId, + probeLineBot, + sendMessageLine, + pushMessageLine, + pushMessagesLine, + pushFlexMessage, + pushTemplateMessage, + pushLocationMessage, + pushTextMessageWithQuickReplies, + createQuickReplyItems, + buildTemplateMessageFromPayload, + monitorLineProvider, +} satisfies PluginRuntimeChannel["line"]; diff --git a/src/plugins/runtime/runtime-line.ts b/src/plugins/runtime/runtime-line.ts new file mode 100644 index 00000000000..3231494f50d --- /dev/null +++ b/src/plugins/runtime/runtime-line.ts @@ -0,0 +1,46 @@ +import { loadSiblingRuntimeModuleSync } from "./local-runtime-module.js"; +import type { PluginRuntimeChannel } from "./types-channel.js"; + +type RuntimeLineModule = { + runtimeLine: PluginRuntimeChannel["line"]; +}; + +let cachedRuntimeLineModule: RuntimeLineModule | null = null; + +function loadRuntimeLineModule(): RuntimeLineModule { + cachedRuntimeLineModule ??= loadSiblingRuntimeModuleSync({ + moduleUrl: import.meta.url, + relativeBase: "./runtime-line.contract", + }); + return cachedRuntimeLineModule; +} + +export function createRuntimeLine(): PluginRuntimeChannel["line"] { + return { + listLineAccountIds: (...args) => + loadRuntimeLineModule().runtimeLine.listLineAccountIds(...args), + resolveDefaultLineAccountId: (...args) => + loadRuntimeLineModule().runtimeLine.resolveDefaultLineAccountId(...args), + resolveLineAccount: (...args) => + loadRuntimeLineModule().runtimeLine.resolveLineAccount(...args), + normalizeAccountId: (...args) => + loadRuntimeLineModule().runtimeLine.normalizeAccountId(...args), + probeLineBot: (...args) => loadRuntimeLineModule().runtimeLine.probeLineBot(...args), + sendMessageLine: (...args) => loadRuntimeLineModule().runtimeLine.sendMessageLine(...args), + pushMessageLine: (...args) => loadRuntimeLineModule().runtimeLine.pushMessageLine(...args), + pushMessagesLine: (...args) => loadRuntimeLineModule().runtimeLine.pushMessagesLine(...args), + pushFlexMessage: (...args) => loadRuntimeLineModule().runtimeLine.pushFlexMessage(...args), + pushTemplateMessage: (...args) => + loadRuntimeLineModule().runtimeLine.pushTemplateMessage(...args), + pushLocationMessage: (...args) => + loadRuntimeLineModule().runtimeLine.pushLocationMessage(...args), + pushTextMessageWithQuickReplies: (...args) => + loadRuntimeLineModule().runtimeLine.pushTextMessageWithQuickReplies(...args), + createQuickReplyItems: (...args) => + loadRuntimeLineModule().runtimeLine.createQuickReplyItems(...args), + buildTemplateMessageFromPayload: (...args) => + loadRuntimeLineModule().runtimeLine.buildTemplateMessageFromPayload(...args), + monitorLineProvider: (...args) => + loadRuntimeLineModule().runtimeLine.monitorLineProvider(...args), + }; +} diff --git a/src/plugins/runtime/runtime-matrix-contract.ts b/src/plugins/runtime/runtime-matrix-contract.ts index 8ed09882891..703502965da 100644 --- a/src/plugins/runtime/runtime-matrix-contract.ts +++ b/src/plugins/runtime/runtime-matrix-contract.ts @@ -1,5 +1,5 @@ -// Narrow plugin-sdk surface for the bundled matrix plugin. -// Keep this list additive and scoped to symbols used under extensions/matrix. +// Narrow plugin-sdk surface for the bundled Matrix plugin. +// Keep this list additive and scoped to the runtime contract only. import { createOptionalChannelSetupSurface } from "../../plugin-sdk/channel-setup.js"; diff --git a/src/plugins/sdk-alias.test.ts b/src/plugins/sdk-alias.test.ts index bc3a8c79b01..47ec4bb4c58 100644 --- a/src/plugins/sdk-alias.test.ts +++ b/src/plugins/sdk-alias.test.ts @@ -2,6 +2,11 @@ import fs from "node:fs"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { afterAll, describe, expect, it, vi } from "vitest"; +import { + bundledDistPluginFile, + bundledPluginFile, + bundledPluginRoot, +} from "../../test/helpers/bundled-plugin-paths.js"; import { withEnv } from "../test-utils/env.js"; import { buildPluginLoaderAliasMap, @@ -530,7 +535,10 @@ describe("plugin sdk alias helpers", () => { it("builds plugin-sdk aliases from the module being loaded, not the loader location", () => { const { fixture, sourceRootAlias, distRootAlias } = createPluginSdkAliasTargetFixture(); - const sourcePluginEntry = writePluginEntry(fixture.root, "extensions/demo/src/index.ts"); + const sourcePluginEntry = writePluginEntry( + fixture.root, + bundledPluginFile("demo", "src/index.ts"), + ); const sourceAliases = withEnv({ NODE_ENV: undefined }, () => buildPluginLoaderAliasMap(sourcePluginEntry), @@ -540,7 +548,10 @@ describe("plugin sdk alias helpers", () => { channelRuntimePath: path.join(fixture.root, "src", "plugin-sdk", "channel-runtime.ts"), }); - const distPluginEntry = writePluginEntry(fixture.root, "dist/extensions/demo/index.js"); + const distPluginEntry = writePluginEntry( + fixture.root, + bundledDistPluginFile("demo", "index.js"), + ); const distAliases = withEnv({ NODE_ENV: undefined }, () => buildPluginLoaderAliasMap(distPluginEntry), @@ -553,7 +564,10 @@ describe("plugin sdk alias helpers", () => { it("applies explicit dist resolution to plugin-sdk subpath aliases too", () => { const { fixture, distRootAlias } = createPluginSdkAliasTargetFixture(); - const sourcePluginEntry = writePluginEntry(fixture.root, "extensions/demo/src/index.ts"); + const sourcePluginEntry = writePluginEntry( + fixture.root, + bundledPluginFile("demo", "src/index.ts"), + ); const distAliases = withEnv({ NODE_ENV: undefined }, () => buildPluginLoaderAliasMap(sourcePluginEntry, undefined, undefined, "dist"), @@ -663,7 +677,9 @@ describe("plugin sdk alias helpers", () => { it("uses transpiled Jiti loads for source TypeScript plugin entries", () => { expect(shouldPreferNativeJiti("/repo/dist/plugins/runtime/index.js")).toBe(true); - expect(shouldPreferNativeJiti("/repo/extensions/discord/src/channel.runtime.ts")).toBe(false); + expect( + shouldPreferNativeJiti(`/repo/${bundledPluginFile("discord", "src/channel.runtime.ts")}`), + ).toBe(false); }); it("disables native Jiti loads under Bun even for built JavaScript entries", () => { @@ -678,7 +694,9 @@ describe("plugin sdk alias helpers", () => { try { expect(shouldPreferNativeJiti("/repo/dist/plugins/runtime/index.js")).toBe(false); - expect(shouldPreferNativeJiti("/repo/dist/extensions/browser/index.js")).toBe(false); + expect(shouldPreferNativeJiti(`/repo/${bundledDistPluginFile("browser", "index.js")}`)).toBe( + false, + ); } finally { Object.defineProperty(process, "versions", { configurable: true, @@ -688,7 +706,7 @@ describe("plugin sdk alias helpers", () => { }); it("loads source runtime shims through the non-native Jiti boundary", async () => { - const copiedExtensionRoot = path.join(makeTempDir(), "extensions", "discord"); + const copiedExtensionRoot = path.join(makeTempDir(), bundledPluginRoot("discord")); const copiedSourceDir = path.join(copiedExtensionRoot, "src"); const copiedPluginSdkDir = path.join(copiedExtensionRoot, "plugin-sdk"); mkdirSafeDir(copiedSourceDir); diff --git a/src/plugins/stage-bundled-plugin-runtime.test.ts b/src/plugins/stage-bundled-plugin-runtime.test.ts index 54b643ab140..866480bc58b 100644 --- a/src/plugins/stage-bundled-plugin-runtime.test.ts +++ b/src/plugins/stage-bundled-plugin-runtime.test.ts @@ -4,6 +4,7 @@ import path from "node:path"; import { pathToFileURL } from "node:url"; import { afterEach, describe, expect, it, vi } from "vitest"; import { stageBundledPluginRuntime } from "../../scripts/stage-bundled-plugin-runtime.mjs"; +import { bundledDistPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; import { discoverOpenClawPlugins } from "./discovery.js"; import { loadPluginManifestRegistry } from "./manifest-registry.js"; @@ -33,6 +34,10 @@ function setupRepoFiles(repoRoot: string, files: Readonly } } +function distRuntimeImportPath(pluginId: string, relativePath = "index.js"): string { + return `../../../${bundledDistPluginFile(pluginId, relativePath)}`; +} + function expectRuntimePluginWrapperContains(params: { repoRoot: string; pluginId: string; @@ -83,8 +88,9 @@ describe("stageBundledPluginRuntime", () => { recursive: true, }); setupRepoFiles(repoRoot, { - "dist/extensions/diffs/index.js": "export default {}\n", - "dist/extensions/diffs/node_modules/@pierre/diffs/index.js": "export default {}\n", + [bundledDistPluginFile("diffs", "index.js")]: "export default {}\n", + [bundledDistPluginFile("diffs", "node_modules/@pierre/diffs/index.js")]: + "export default {}\n", }); stageBundledPluginRuntime({ repoRoot }); @@ -93,7 +99,7 @@ describe("stageBundledPluginRuntime", () => { expectRuntimePluginWrapperContains({ repoRoot, pluginId: "diffs", - expectedImport: "../../../dist/extensions/diffs/index.js", + expectedImport: distRuntimeImportPath("diffs"), }); expect(fs.lstatSync(path.join(runtimePluginDir, "node_modules")).isSymbolicLink()).toBe(true); expect(fs.realpathSync(path.join(runtimePluginDir, "node_modules"))).toBe( @@ -107,7 +113,7 @@ describe("stageBundledPluginRuntime", () => { createDistPluginDir(repoRoot, "diffs"); setupRepoFiles(repoRoot, { "dist/chunk-abc.js": "export const value = 1;\n", - "dist/extensions/diffs/index.js": "export { value } from '../../chunk-abc.js';\n", + [bundledDistPluginFile("diffs", "index.js")]: "export { value } from '../../chunk-abc.js';\n", }); stageBundledPluginRuntime({ repoRoot }); @@ -116,7 +122,7 @@ describe("stageBundledPluginRuntime", () => { expectRuntimePluginWrapperContains({ repoRoot, pluginId: "diffs", - expectedImport: "../../../dist/extensions/diffs/index.js", + expectedImport: distRuntimeImportPath("diffs"), }); expect(fs.existsSync(path.join(repoRoot, "dist-runtime", "chunk-abc.js"))).toBe(false); @@ -128,9 +134,9 @@ describe("stageBundledPluginRuntime", () => { const repoRoot = makeRepoRoot("openclaw-stage-bundled-runtime-sidecars-"); createDistPluginDir(repoRoot, "whatsapp"); setupRepoFiles(repoRoot, { - "dist/extensions/whatsapp/index.js": "export default {};\n", - "dist/extensions/whatsapp/light-runtime-api.js": "export const light = true;\n", - "dist/extensions/whatsapp/runtime-api.js": "export const heavy = true;\n", + [bundledDistPluginFile("whatsapp", "index.js")]: "export default {};\n", + [bundledDistPluginFile("whatsapp", "light-runtime-api.js")]: "export const light = true;\n", + [bundledDistPluginFile("whatsapp", "runtime-api.js")]: "export const heavy = true;\n", }); stageBundledPluginRuntime({ repoRoot }); @@ -139,13 +145,13 @@ describe("stageBundledPluginRuntime", () => { repoRoot, pluginId: "whatsapp", relativePath: "light-runtime-api.js", - expectedImport: "../../../dist/extensions/whatsapp/light-runtime-api.js", + expectedImport: distRuntimeImportPath("whatsapp", "light-runtime-api.js"), }); expectRuntimePluginWrapperContains({ repoRoot, pluginId: "whatsapp", relativePath: "runtime-api.js", - expectedImport: "../../../dist/extensions/whatsapp/runtime-api.js", + expectedImport: distRuntimeImportPath("whatsapp", "runtime-api.js"), }); }); @@ -260,13 +266,13 @@ describe("stageBundledPluginRuntime", () => { const repoRoot = makeRepoRoot("openclaw-stage-bundled-runtime-assets-"); createDistPluginDir(repoRoot, "diffs"); setupRepoFiles(repoRoot, { - "dist/extensions/diffs/package.json": JSON.stringify( + [bundledDistPluginFile("diffs", "package.json")]: JSON.stringify( { name: "@openclaw/diffs", openclaw: { extensions: ["./index.js"] } }, null, 2, ), - "dist/extensions/diffs/openclaw.plugin.json": "{}\n", - "dist/extensions/diffs/assets/info.txt": "ok\n", + [bundledDistPluginFile("diffs", "openclaw.plugin.json")]: "{}\n", + [bundledDistPluginFile("diffs", "assets/info.txt")]: "ok\n", }); stageBundledPluginRuntime({ repoRoot }); @@ -301,7 +307,7 @@ describe("stageBundledPluginRuntime", () => { const runtimeExtensionsDir = path.join(repoRoot, "dist-runtime", "extensions"); createDistPluginDir(repoRoot, "demo"); setupRepoFiles(repoRoot, { - "dist/extensions/demo/package.json": JSON.stringify( + [bundledDistPluginFile("demo", "package.json")]: JSON.stringify( { name: "@openclaw/demo", openclaw: { @@ -315,7 +321,7 @@ describe("stageBundledPluginRuntime", () => { null, 2, ), - "dist/extensions/demo/openclaw.plugin.json": JSON.stringify( + [bundledDistPluginFile("demo", "openclaw.plugin.json")]: JSON.stringify( { id: "demo", channels: ["demo"], @@ -324,8 +330,8 @@ describe("stageBundledPluginRuntime", () => { null, 2, ), - "dist/extensions/demo/main.js": "export default {};\n", - "dist/extensions/demo/setup.js": "export default {};\n", + [bundledDistPluginFile("demo", "main.js")]: "export default {};\n", + [bundledDistPluginFile("demo", "setup.js")]: "export default {};\n", }); stageBundledPluginRuntime({ repoRoot }); @@ -390,8 +396,8 @@ describe("stageBundledPluginRuntime", () => { const repoRoot = makeRepoRoot("openclaw-stage-bundled-runtime-eexist-"); createDistPluginDir(repoRoot, "feishu"); setupRepoFiles(repoRoot, { - "dist/extensions/feishu/index.js": "export default {}\n", - "dist/extensions/feishu/skills/feishu-doc/SKILL.md": "# Feishu Doc\n", + [bundledDistPluginFile("feishu", "index.js")]: "export default {}\n", + [bundledDistPluginFile("feishu", "skills/feishu-doc/SKILL.md")]: "# Feishu Doc\n", }); const realSymlinkSync = fs.symlinkSync.bind(fs); diff --git a/src/plugins/uninstall.test.ts b/src/plugins/uninstall.test.ts index dc17e77a5c9..733bfbb54db 100644 --- a/src/plugins/uninstall.test.ts +++ b/src/plugins/uninstall.test.ts @@ -777,7 +777,7 @@ describe("resolveUninstallDirectoryTarget", () => { installRecord: { source: "npm", spec: "my-plugin@1.0.0", - installPath: "/tmp/not-openclaw-extensions/my-plugin", + installPath: "/tmp/not-openclaw-plugin-install/my-plugin", }, extensionsDir, }); diff --git a/src/plugins/update.test.ts b/src/plugins/update.test.ts index 0dea60cef76..05a91e5c63b 100644 --- a/src/plugins/update.test.ts +++ b/src/plugins/update.test.ts @@ -1,6 +1,13 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { bundledPluginRootAt } from "../../test/helpers/bundled-plugin-paths.js"; import type { OpenClawConfig } from "../config/config.js"; +const APP_ROOT = "/app"; + +function appBundledPluginRoot(pluginId: string): string { + return bundledPluginRootAt(APP_ROOT, pluginId); +} + const installPluginFromNpmSpecMock = vi.fn(); const installPluginFromMarketplaceMock = vi.fn(); const installPluginFromClawHubMock = vi.fn(); @@ -131,7 +138,7 @@ function createBundledPathInstallConfig(params: { installs: { feishu: { source: "path", - sourcePath: params.sourcePath ?? "/app/extensions/feishu", + sourcePath: params.sourcePath ?? appBundledPluginRoot("feishu"), installPath: params.installPath, ...(params.spec ? { spec: params.spec } : {}), }, @@ -178,7 +185,7 @@ function createBundledSource(params?: { pluginId?: string; localPath?: string; n const pluginId = params?.pluginId ?? "feishu"; return { pluginId, - localPath: params?.localPath ?? `/app/extensions/${pluginId}`, + localPath: params?.localPath ?? appBundledPluginRoot(pluginId), npmSpec: params?.npmSpec ?? `@openclaw/${pluginId}`, }; } @@ -610,13 +617,13 @@ describe("syncPluginsForUpdateChannel", () => { { name: "keeps bundled path installs on beta without reinstalling from npm", config: createBundledPathInstallConfig({ - loadPaths: ["/app/extensions/feishu"], - installPath: "/app/extensions/feishu", + loadPaths: [appBundledPluginRoot("feishu")], + installPath: appBundledPluginRoot("feishu"), spec: "@openclaw/feishu", }), expectedChanged: false, - expectedLoadPaths: ["/app/extensions/feishu"], - expectedInstallPath: "/app/extensions/feishu", + expectedLoadPaths: [appBundledPluginRoot("feishu")], + expectedInstallPath: appBundledPluginRoot("feishu"), }, { name: "repairs bundled install metadata when the load path is re-added", @@ -626,8 +633,8 @@ describe("syncPluginsForUpdateChannel", () => { spec: "@openclaw/feishu", }), expectedChanged: true, - expectedLoadPaths: ["/app/extensions/feishu"], - expectedInstallPath: "/app/extensions/feishu", + expectedLoadPaths: [appBundledPluginRoot("feishu")], + expectedInstallPath: appBundledPluginRoot("feishu"), }, ] as const)( "$name", @@ -645,7 +652,7 @@ describe("syncPluginsForUpdateChannel", () => { expect(result.config.plugins?.load?.paths).toEqual(expectedLoadPaths); expectBundledPathInstall({ install: result.config.plugins?.installs?.feishu, - sourcePath: "/app/extensions/feishu", + sourcePath: appBundledPluginRoot("feishu"), installPath: expectedInstallPath, spec: "@openclaw/feishu", }); diff --git a/src/scripts/ci-changed-scope.test.ts b/src/scripts/ci-changed-scope.test.ts index f5d3136f747..d16ea6d15d7 100644 --- a/src/scripts/ci-changed-scope.test.ts +++ b/src/scripts/ci-changed-scope.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; +import { bundledPluginFile } from "../../test/helpers/bundled-plugin-paths.js"; const { detectChangedScope, listChangedPaths } = (await import("../../scripts/ci-changed-scope.mjs")) as unknown as { @@ -166,7 +167,7 @@ describe("detectChangedScope", () => { runSkillsPython: false, runChangedSmoke: true, }); - expect(detectChangedScope(["extensions/matrix/package.json"])).toEqual({ + expect(detectChangedScope([bundledPluginFile("matrix", "package.json")])).toEqual({ runNode: true, runMacos: false, runAndroid: false, diff --git a/src/test-utils/bundled-plugin-public-surface.ts b/src/test-utils/bundled-plugin-public-surface.ts new file mode 100644 index 00000000000..6f4d9f34776 --- /dev/null +++ b/src/test-utils/bundled-plugin-public-surface.ts @@ -0,0 +1,59 @@ +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { loadBundledPluginPublicSurfaceModuleSync } from "../plugin-sdk/facade-runtime.js"; +import { + findBundledPluginMetadataById, + type BundledPluginMetadata, +} from "../plugins/bundled-plugin-metadata.js"; +import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js"; + +const OPENCLAW_PACKAGE_ROOT = + resolveLoaderPackageRoot({ + modulePath: fileURLToPath(import.meta.url), + moduleUrl: import.meta.url, + }) ?? fileURLToPath(new URL("../..", import.meta.url)); + +function findBundledPluginMetadata(pluginId: string): BundledPluginMetadata { + const metadata = findBundledPluginMetadataById(pluginId); + if (!metadata) { + throw new Error(`Unknown bundled plugin id: ${pluginId}`); + } + return metadata; +} + +export function loadBundledPluginPublicSurfaceSync(params: { + pluginId: string; + artifactBasename: string; +}): T { + const metadata = findBundledPluginMetadata(params.pluginId); + return loadBundledPluginPublicSurfaceModuleSync({ + dirName: metadata.dirName, + artifactBasename: params.artifactBasename, + }); +} + +export function loadBundledPluginTestApiSync(pluginId: string): T { + return loadBundledPluginPublicSurfaceSync({ + pluginId, + artifactBasename: "test-api.js", + }); +} + +export function resolveRelativeBundledPluginPublicModuleId(params: { + fromModuleUrl: string; + pluginId: string; + artifactBasename: string; +}): string { + const metadata = findBundledPluginMetadata(params.pluginId); + const fromFilePath = fileURLToPath(params.fromModuleUrl); + const targetPath = path.resolve( + OPENCLAW_PACKAGE_ROOT, + "extensions", + metadata.dirName, + params.artifactBasename, + ); + const relativePath = path + .relative(path.dirname(fromFilePath), targetPath) + .replaceAll(path.sep, "/"); + return relativePath.startsWith(".") ? relativePath : `./${relativePath}`; +} diff --git a/src/test-utils/repo-scan.ts b/src/test-utils/repo-scan.ts index 9dbf67fed22..f43ec7171a5 100644 --- a/src/test-utils/repo-scan.ts +++ b/src/test-utils/repo-scan.ts @@ -73,7 +73,7 @@ export async function listRepoFiles( pending.push({ absolutePath }); } } catch { - // Skip missing roots. Useful when extensions/ is absent. + // Skip missing roots. Useful when the bundled plugin tree is absent. } } diff --git a/test/channel-outbounds.ts b/test/channel-outbounds.ts index 3cc86859338..e262177e3a9 100644 --- a/test/channel-outbounds.ts +++ b/test/channel-outbounds.ts @@ -1,6 +1,27 @@ -export { discordOutbound } from "../extensions/discord/test-api.js"; -export { imessageOutbound } from "../extensions/imessage/src/outbound-adapter.js"; -export { signalOutbound } from "../extensions/signal/test-api.js"; -export { slackOutbound } from "../extensions/slack/test-api.js"; -export { telegramOutbound } from "../extensions/telegram/test-api.js"; -export { whatsappOutbound } from "../extensions/whatsapp/test-api.js"; +import type { ChannelOutboundAdapter } from "../src/channels/plugins/types.js"; +import { + loadBundledPluginPublicSurfaceSync, + loadBundledPluginTestApiSync, +} from "../src/test-utils/bundled-plugin-public-surface.js"; + +export const { discordOutbound } = loadBundledPluginTestApiSync<{ + discordOutbound: ChannelOutboundAdapter; +}>("discord"); +export const { imessageOutbound } = loadBundledPluginPublicSurfaceSync<{ + imessageOutbound: ChannelOutboundAdapter; +}>({ + pluginId: "imessage", + artifactBasename: "src/outbound-adapter.js", +}); +export const { signalOutbound } = loadBundledPluginTestApiSync<{ + signalOutbound: ChannelOutboundAdapter; +}>("signal"); +export const { slackOutbound } = loadBundledPluginTestApiSync<{ + slackOutbound: ChannelOutboundAdapter; +}>("slack"); +export const { telegramOutbound } = loadBundledPluginTestApiSync<{ + telegramOutbound: ChannelOutboundAdapter; +}>("telegram"); +export const { whatsappOutbound } = loadBundledPluginTestApiSync<{ + whatsappOutbound: ChannelOutboundAdapter; +}>("whatsapp"); diff --git a/test/extension-test-boundary.test.ts b/test/extension-test-boundary.test.ts index cacf5b67df0..91c758502d5 100644 --- a/test/extension-test-boundary.test.ts +++ b/test/extension-test-boundary.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; +import { BUNDLED_PLUGIN_PATH_PREFIX } from "./helpers/bundled-plugin-paths.js"; const repoRoot = path.resolve(import.meta.dirname, ".."); @@ -35,14 +36,14 @@ function findExtensionImports(source: string): string[] { } describe("non-extension test boundaries", () => { - it("keeps extension-owned behavior suites under extensions/", () => { + it("keeps plugin-owned behavior suites under the bundled plugin tree", () => { const testFiles = [ ...walk(path.join(repoRoot, "src")), ...walk(path.join(repoRoot, "test")), ...walk(path.join(repoRoot, "packages")), ].filter( (file) => - !file.startsWith("extensions/") && + !file.startsWith(BUNDLED_PLUGIN_PATH_PREFIX) && !file.startsWith("test/helpers/") && !file.startsWith("ui/"), ); diff --git a/test/fixtures/test-parallel.behavior.json b/test/fixtures/test-parallel.behavior.json index c0fa529ca42..8d858a4656f 100644 --- a/test/fixtures/test-parallel.behavior.json +++ b/test/fixtures/test-parallel.behavior.json @@ -10,7 +10,7 @@ "reason": "Measured ~35% faster under threads than forks on base config after removing process.chdir() from the test." }, { - "file": "src/agents/pi-extensions/compaction-safeguard.test.ts", + "file": "src/agents/pi-hooks/compaction-safeguard.test.ts", "reason": "Measured ~27% faster under threads than forks on base config after removing process.chdir() from the test." }, { diff --git a/test/helpers/bundled-plugin-paths.ts b/test/helpers/bundled-plugin-paths.ts new file mode 100644 index 00000000000..504efea0986 --- /dev/null +++ b/test/helpers/bundled-plugin-paths.ts @@ -0,0 +1,59 @@ +export const BUNDLED_PLUGIN_ROOT_DIR = "extensions"; +export const BUNDLED_PLUGIN_PATH_PREFIX = `${BUNDLED_PLUGIN_ROOT_DIR}/`; +export const BUNDLED_PLUGIN_TEST_GLOB = `${BUNDLED_PLUGIN_ROOT_DIR}/**/*.test.ts`; + +export function bundledPluginRoot(pluginId: string): string { + return `${BUNDLED_PLUGIN_PATH_PREFIX}${pluginId}`; +} + +export function bundledPluginFile(pluginId: string, relativePath: string): string { + return `${bundledPluginRoot(pluginId)}/${relativePath}`; +} + +function joinRoot(baseDir: string, relativePath: string): string { + return `${baseDir.replace(/\/$/, "")}/${relativePath}`; +} + +export function bundledPluginDirPrefix(pluginId: string, relativeDir: string): string { + return `${bundledPluginRoot(pluginId)}/${relativeDir.replace(/\/$/, "")}/`; +} + +export function bundledPluginRootAt(baseDir: string, pluginId: string): string { + return joinRoot(baseDir, bundledPluginRoot(pluginId)); +} + +export function bundledPluginFileAt( + baseDir: string, + pluginId: string, + relativePath: string, +): string { + return joinRoot(baseDir, bundledPluginFile(pluginId, relativePath)); +} + +export function bundledDistPluginRoot(pluginId: string): string { + return `dist/${bundledPluginRoot(pluginId)}`; +} + +export function bundledDistPluginFile(pluginId: string, relativePath: string): string { + return `${bundledDistPluginRoot(pluginId)}/${relativePath}`; +} + +export function bundledDistPluginRootAt(baseDir: string, pluginId: string): string { + return joinRoot(baseDir, bundledDistPluginRoot(pluginId)); +} + +export function bundledDistPluginFileAt( + baseDir: string, + pluginId: string, + relativePath: string, +): string { + return joinRoot(baseDir, bundledDistPluginFile(pluginId, relativePath)); +} + +export function installedPluginRoot(baseDir: string, pluginId: string): string { + return bundledPluginRootAt(baseDir, pluginId); +} + +export function repoInstallSpec(pluginId: string): string { + return `./${bundledPluginRoot(pluginId)}`; +} diff --git a/test/helpers/channels/dm-policy-contract.ts b/test/helpers/channels/dm-policy-contract.ts index 64bff7156c8..498c9ee210b 100644 --- a/test/helpers/channels/dm-policy-contract.ts +++ b/test/helpers/channels/dm-policy-contract.ts @@ -1,7 +1,10 @@ import { expect, it } from "vitest"; -import { isAllowedBlueBubblesSender } from "../../../extensions/bluebubbles/api.js"; -import { isMattermostSenderAllowed } from "../../../extensions/mattermost/api.js"; -import { isSignalSenderAllowed, type SignalSender } from "../../../extensions/signal/api.js"; +import { isAllowedBlueBubblesSender } from "../../../src/plugin-sdk/bluebubbles-policy.js"; +import { isMattermostSenderAllowed } from "../../../src/plugin-sdk/mattermost-policy.js"; +import { + isSignalSenderAllowed, + type SignalSender, +} from "../../../src/plugin-sdk/signal-surface.js"; import { DM_GROUP_ACCESS_REASON, resolveDmGroupAccessWithLists, diff --git a/test/helpers/channels/group-policy-contract.ts b/test/helpers/channels/group-policy-contract.ts index 799a7256038..e3fa6b26c71 100644 --- a/test/helpers/channels/group-policy-contract.ts +++ b/test/helpers/channels/group-policy-contract.ts @@ -1,18 +1,18 @@ import { expect, it } from "vitest"; -import { __testing as discordMonitorTesting } from "../../../extensions/discord/src/monitor/provider.js"; -import { __testing as imessageMonitorTesting } from "../../../extensions/imessage/src/monitor/monitor-provider.js"; -import { __testing as slackMonitorTesting } from "../../../extensions/slack/src/monitor/provider.js"; -import { resolveTelegramRuntimeGroupPolicy } from "../../../extensions/telegram/runtime-api.js"; -import { whatsappAccessControlTesting } from "../../../extensions/whatsapp/api.js"; +import { installChannelRuntimeGroupPolicyFallbackSuite } from "../../../src/channels/plugins/contracts/suites.js"; +import { resolveDiscordRuntimeGroupPolicy } from "../../../src/plugin-sdk/discord-surface.js"; +import { resolveIMessageRuntimeGroupPolicy } from "../../../src/plugin-sdk/imessage-policy.js"; +import { resolveSlackRuntimeGroupPolicy } from "../../../src/plugin-sdk/slack-surface.js"; +import { resolveTelegramRuntimeGroupPolicy } from "../../../src/plugin-sdk/telegram-runtime-surface.js"; +import { whatsappAccessControlTesting } from "../../../src/plugin-sdk/whatsapp-surface.js"; import { evaluateZaloGroupAccess, resolveZaloRuntimeGroupPolicy, -} from "../../../extensions/zalo/api.js"; -import { installChannelRuntimeGroupPolicyFallbackSuite } from "../../../src/channels/plugins/contracts/suites.js"; +} from "../../../src/plugin-sdk/zalo-setup.js"; export function installSlackGroupPolicyContractSuite() { installChannelRuntimeGroupPolicyFallbackSuite({ - resolve: slackMonitorTesting.resolveSlackRuntimeGroupPolicy, + resolve: resolveSlackRuntimeGroupPolicy, configuredLabel: "keeps open default when channels.slack is configured", defaultGroupPolicyUnderTest: "open", missingConfigLabel: "fails closed when channels.slack is missing and no defaults are set", @@ -42,7 +42,7 @@ export function installWhatsAppGroupPolicyContractSuite() { export function installIMessageGroupPolicyContractSuite() { installChannelRuntimeGroupPolicyFallbackSuite({ - resolve: imessageMonitorTesting.resolveIMessageRuntimeGroupPolicy, + resolve: resolveIMessageRuntimeGroupPolicy, configuredLabel: "keeps open fallback when channels.imessage is configured", defaultGroupPolicyUnderTest: "disabled", missingConfigLabel: "fails closed when channels.imessage is missing and no defaults are set", @@ -52,7 +52,7 @@ export function installIMessageGroupPolicyContractSuite() { export function installDiscordGroupPolicyContractSuite() { installChannelRuntimeGroupPolicyFallbackSuite({ - resolve: discordMonitorTesting.resolveDiscordRuntimeGroupPolicy, + resolve: resolveDiscordRuntimeGroupPolicy, configuredLabel: "keeps open default when channels.discord is configured", defaultGroupPolicyUnderTest: "open", missingConfigLabel: "fails closed when channels.discord is missing and no defaults are set", @@ -60,7 +60,7 @@ export function installDiscordGroupPolicyContractSuite() { }); it("respects explicit provider policy", () => { - const resolved = discordMonitorTesting.resolveDiscordRuntimeGroupPolicy({ + const resolved = resolveDiscordRuntimeGroupPolicy({ providerConfigPresent: false, groupPolicy: "disabled", }); diff --git a/test/helpers/channels/inbound-contract.ts b/test/helpers/channels/inbound-contract.ts index f01980d2e3b..479583168b8 100644 --- a/test/helpers/channels/inbound-contract.ts +++ b/test/helpers/channels/inbound-contract.ts @@ -1,18 +1,59 @@ import { expect, it, vi } from "vitest"; -import { buildFinalizedDiscordDirectInboundContext } from "../../../extensions/discord/test-api.js"; -import { - createInboundSlackTestContext, - prepareSlackMessage, - type ResolvedSlackAccount, - type SlackMessageEvent, -} from "../../../extensions/slack/test-api.js"; -import { buildTelegramMessageContextForTest } from "../../../extensions/telegram/test-api.js"; import type { MsgContext } from "../../../src/auto-reply/templating.js"; import { inboundCtxCapture } from "../../../src/channels/plugins/contracts/inbound-testkit.js"; import { expectChannelInboundContextContract } from "../../../src/channels/plugins/contracts/suites.js"; import type { OpenClawConfig } from "../../../src/config/config.js"; +import type { ResolvedSlackAccount } from "../../../src/plugin-sdk/slack.js"; +import { + loadBundledPluginTestApiSync, + resolveRelativeBundledPluginPublicModuleId, +} from "../../../src/test-utils/bundled-plugin-public-surface.js"; import { withTempHome } from "../temp-home.js"; +type SlackMessageEvent = { + channel: string; + channel_type?: string; + user?: string; + text?: string; + ts: string; +}; + +type SlackPrepareResult = { ctxPayload: MsgContext } | null | undefined; + +const { buildFinalizedDiscordDirectInboundContext } = loadBundledPluginTestApiSync<{ + buildFinalizedDiscordDirectInboundContext: () => MsgContext; +}>("discord"); +const { createInboundSlackTestContext, prepareSlackMessage } = loadBundledPluginTestApiSync<{ + createInboundSlackTestContext: (params: { cfg: OpenClawConfig }) => { + resolveUserName?: () => Promise; + }; + prepareSlackMessage: (params: { + ctx: { + resolveUserName?: () => Promise; + }; + account: ResolvedSlackAccount; + message: SlackMessageEvent; + opts: { source: string }; + }) => Promise; +}>("slack"); +const { buildTelegramMessageContextForTest } = loadBundledPluginTestApiSync<{ + buildTelegramMessageContextForTest: (params: { + cfg: OpenClawConfig; + message: Record; + }) => Promise<{ ctxPayload: MsgContext } | null | undefined>; +}>("telegram"); + +const signalApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "signal", + artifactBasename: "api.js", +}); +const whatsAppTestApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "whatsapp", + artifactBasename: "test-api.js", +}); + const dispatchInboundMessageMock = vi.hoisted(() => vi.fn( async (params: { @@ -54,7 +95,7 @@ vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => { }; }); -vi.mock("../../../extensions/signal/api.js", () => ({ +vi.mock(signalApiModuleId, () => ({ sendMessageSignal: vi.fn(), sendTypingSignal: vi.fn(async () => true), sendReadReceiptSignal: vi.fn(async () => true), @@ -65,8 +106,8 @@ vi.mock("../../../src/pairing/pairing-store.js", () => ({ upsertChannelPairingRequest: vi.fn(), })); -vi.mock("../../../extensions/whatsapp/test-api.js", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock(whatsAppTestApiModuleId, async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, trackBackgroundTask: (tasks: Set>, task: Promise) => { diff --git a/test/helpers/channels/outbound-payload-contract.ts b/test/helpers/channels/outbound-payload-contract.ts index 2b3192282c9..9927d7d16a4 100644 --- a/test/helpers/channels/outbound-payload-contract.ts +++ b/test/helpers/channels/outbound-payload-contract.ts @@ -1,11 +1,4 @@ import { vi } from "vitest"; -import { discordOutbound } from "../../../extensions/discord/test-api.js"; -import { whatsappOutbound } from "../../../extensions/whatsapp/test-api.js"; -import { sendMessageZalo } from "../../../extensions/zalo/test-api.js"; -import { - sendMessageZalouser, - parseZalouserOutboundTarget, -} from "../../../extensions/zalouser/test-api.js"; import type { ReplyPayload } from "../../../src/auto-reply/types.js"; import { createSlackOutboundPayloadHarness, @@ -13,22 +6,65 @@ import { primeChannelOutboundSendMock, } from "../../../src/channels/plugins/contracts/suites.js"; import { createDirectTextMediaOutbound } from "../../../src/channels/plugins/outbound/direct-text-media.js"; +import type { ChannelOutboundAdapter } from "../../../src/channels/plugins/types.js"; import { chunkTextForOutbound as chunkZaloTextForOutbound, sendPayloadWithChunkedTextAndMedia as sendZaloPayloadWithChunkedTextAndMedia, } from "../../../src/plugin-sdk/zalo.js"; import { sendPayloadWithChunkedTextAndMedia as sendZalouserPayloadWithChunkedTextAndMedia } from "../../../src/plugin-sdk/zalouser.js"; +import { + loadBundledPluginTestApiSync, + resolveRelativeBundledPluginPublicModuleId, +} from "../../../src/test-utils/bundled-plugin-public-surface.js"; -vi.mock("../../../extensions/zalo/test-api.js", async (importOriginal) => { - const actual = await importOriginal(); +type ChannelSendResponse = { ok?: boolean; messageId?: string }; +type SendMessageZalo = ( + to: string, + text: string, + options: Record, +) => Promise; +type SendMessageZalouser = ( + threadId: string, + text: string, + options: Record, +) => Promise; +type ParseZalouserOutboundTarget = (raw: string) => { threadId: string; isGroup: boolean }; + +const { discordOutbound } = loadBundledPluginTestApiSync<{ + discordOutbound: ChannelOutboundAdapter; +}>("discord"); +const { whatsappOutbound } = loadBundledPluginTestApiSync<{ + whatsappOutbound: ChannelOutboundAdapter; +}>("whatsapp"); +const { sendMessageZalo } = loadBundledPluginTestApiSync<{ + sendMessageZalo: SendMessageZalo; +}>("zalo"); +const { sendMessageZalouser, parseZalouserOutboundTarget } = loadBundledPluginTestApiSync<{ + sendMessageZalouser: SendMessageZalouser; + parseZalouserOutboundTarget: ParseZalouserOutboundTarget; +}>("zalouser"); + +const zaloTestApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "zalo", + artifactBasename: "test-api.js", +}); +const zalouserTestApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "zalouser", + artifactBasename: "test-api.js", +}); + +vi.mock(zaloTestApiModuleId, async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, sendMessageZalo: vi.fn().mockResolvedValue({ ok: true, messageId: "zl-1" }), }; }); -vi.mock("../../../extensions/zalouser/test-api.js", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock(zalouserTestApiModuleId, async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, listZalouserAccountIds: vi.fn(() => ["default"]), diff --git a/test/helpers/channels/plugins-core-extension-contract.ts b/test/helpers/channels/plugins-core-extension-contract.ts index b6189c6963f..1115c406fc1 100644 --- a/test/helpers/channels/plugins-core-extension-contract.ts +++ b/test/helpers/channels/plugins-core-extension-contract.ts @@ -1,34 +1,34 @@ import { describe, expect, expectTypeOf, it } from "vitest"; -import { - listDiscordDirectoryGroupsFromConfig, - listDiscordDirectoryPeersFromConfig, - type DiscordProbe, - type DiscordTokenResolution, -} from "../../../extensions/discord/api.js"; -import type { IMessageProbe } from "../../../extensions/imessage/api.js"; -import type { SignalProbe } from "../../../extensions/signal/api.js"; -import { - listSlackDirectoryGroupsFromConfig, - listSlackDirectoryPeersFromConfig, - type SlackProbe, -} from "../../../extensions/slack/api.js"; -import { - listTelegramDirectoryGroupsFromConfig, - listTelegramDirectoryPeersFromConfig, - type TelegramProbe, - type TelegramTokenResolution, -} from "../../../extensions/telegram/api.js"; -import { - listWhatsAppDirectoryGroupsFromConfig, - listWhatsAppDirectoryPeersFromConfig, -} from "../../../extensions/whatsapp/api.js"; import type { BaseProbeResult, BaseTokenResolution, ChannelDirectoryEntry, } from "../../../src/channels/plugins/types.js"; import type { OpenClawConfig } from "../../../src/config/config.js"; +import { + listDiscordDirectoryGroupsFromConfig, + listDiscordDirectoryPeersFromConfig, + type DiscordProbe, + type DiscordTokenResolution, +} from "../../../src/plugin-sdk/discord-surface.js"; +import type { IMessageProbe } from "../../../src/plugin-sdk/imessage.js"; import type { LineProbeResult } from "../../../src/plugin-sdk/line.js"; +import type { SignalProbe } from "../../../src/plugin-sdk/signal-surface.js"; +import { + listSlackDirectoryGroupsFromConfig, + listSlackDirectoryPeersFromConfig, + type SlackProbe, +} from "../../../src/plugin-sdk/slack-surface.js"; +import { + listTelegramDirectoryGroupsFromConfig, + listTelegramDirectoryPeersFromConfig, + type TelegramProbe, + type TelegramTokenResolution, +} from "../../../src/plugin-sdk/telegram-surface.js"; +import { + listWhatsAppDirectoryGroupsFromConfig, + listWhatsAppDirectoryPeersFromConfig, +} from "../../../src/plugin-sdk/whatsapp-surface.js"; import { withEnvAsync } from "../../../src/test-utils/env.js"; type DirectoryListFn = (params: { diff --git a/test/helpers/channels/registry-backed-contract.ts b/test/helpers/channels/registry-backed-contract.ts index bae8e254e61..7e63c7720dd 100644 --- a/test/helpers/channels/registry-backed-contract.ts +++ b/test/helpers/channels/registry-backed-contract.ts @@ -1,8 +1,4 @@ import { beforeEach, describe } from "vitest"; -import { __testing as discordThreadBindingTesting } from "../../../extensions/discord/runtime-api.js"; -import { feishuThreadBindingTesting } from "../../../extensions/feishu/api.js"; -import { resetMatrixThreadBindingsForTests } from "../../../extensions/matrix/api.js"; -import { __testing as telegramThreadBindingTesting } from "../../../extensions/telegram/src/thread-bindings.js"; import { actionContractRegistry, directoryContractRegistry, @@ -24,6 +20,16 @@ import { installSessionBindingContractSuite, } from "../../../src/channels/plugins/contracts/suites.js"; import { __testing as sessionBindingTesting } from "../../../src/infra/outbound/session-binding-service.js"; +import { feishuThreadBindingTesting } from "../../../src/plugin-sdk/feishu-conversation.js"; +import { resetMatrixThreadBindingsForTests } from "../../../src/plugin-sdk/matrix.js"; +import { resetTelegramThreadBindingsForTests } from "../../../src/plugin-sdk/telegram-runtime-surface.js"; +import { loadBundledPluginTestApiSync } from "../../../src/test-utils/bundled-plugin-public-surface.js"; + +const { discordThreadBindingTesting } = loadBundledPluginTestApiSync<{ + discordThreadBindingTesting: { + resetThreadBindingsForTests: () => void; + }; +}>("discord"); function hasEntries( entries: readonly T[], @@ -118,7 +124,7 @@ export function describeSessionBindingRegistryBackedContract(id: string) { discordThreadBindingTesting.resetThreadBindingsForTests(); feishuThreadBindingTesting.resetFeishuThreadBindingsForTests(); resetMatrixThreadBindingsForTests(); - await telegramThreadBindingTesting.resetTelegramThreadBindingsForTests(); + await resetTelegramThreadBindingsForTests(); }); installSessionBindingContractSuite({ diff --git a/test/helpers/extensions/directory.ts b/test/helpers/extensions/directory.ts deleted file mode 100644 index b4edaa12ded..00000000000 --- a/test/helpers/extensions/directory.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { ChannelDirectoryAdapter } from "openclaw/plugin-sdk/channel-runtime"; - -export function createDirectoryTestRuntime() { - return { - log: () => {}, - error: () => {}, - exit: (code: number): never => { - throw new Error(`exit ${code}`); - }, - }; -} - -export function expectDirectorySurface(directory: ChannelDirectoryAdapter | null | undefined) { - if (!directory) { - throw new Error("expected directory"); - } - if (!directory.listPeers) { - throw new Error("expected listPeers"); - } - if (!directory.listGroups) { - throw new Error("expected listGroups"); - } - return directory as { - listPeers: NonNullable; - listGroups: NonNullable; - }; -} diff --git a/test/helpers/memory-tool-manager-mock.ts b/test/helpers/memory-tool-manager-mock.ts index 454e8937b6f..29001ad88c4 100644 --- a/test/helpers/memory-tool-manager-mock.ts +++ b/test/helpers/memory-tool-manager-mock.ts @@ -1,4 +1,5 @@ import { vi } from "vitest"; +import { resolveRelativeBundledPluginPublicModuleId } from "../../src/test-utils/bundled-plugin-public-surface.js"; export type SearchImpl = () => Promise; export type MemoryReadParams = { relPath: string; from?: number; lines?: number }; @@ -38,7 +39,18 @@ const readAgentMemoryFileMock = vi.fn( async (params: MemoryReadParams) => await readFileImpl(params), ); -vi.mock("../../extensions/memory-core/src/memory/index.js", () => ({ +const memoryIndexModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "memory-core", + artifactBasename: "src/memory/index.js", +}); +const memoryToolsRuntimeModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "memory-core", + artifactBasename: "src/tools.runtime.js", +}); + +vi.mock(memoryIndexModuleId, () => ({ getMemorySearchManager: getMemorySearchManagerMock, })); @@ -46,7 +58,7 @@ vi.mock("../../packages/memory-host-sdk/src/host/read-file.js", () => ({ readAgentMemoryFile: readAgentMemoryFileMock, })); -vi.mock("../../extensions/memory-core/src/tools.runtime.js", () => ({ +vi.mock(memoryToolsRuntimeModuleId, () => ({ resolveMemoryBackendConfig: ({ cfg, }: { diff --git a/test/helpers/extensions/auth-token-assertions.ts b/test/helpers/plugins/auth-token-assertions.ts similarity index 100% rename from test/helpers/extensions/auth-token-assertions.ts rename to test/helpers/plugins/auth-token-assertions.ts diff --git a/test/helpers/extensions/bluebubbles-monitor.ts b/test/helpers/plugins/bluebubbles-monitor.ts similarity index 88% rename from test/helpers/extensions/bluebubbles-monitor.ts rename to test/helpers/plugins/bluebubbles-monitor.ts index f9252bda6fd..807b02ea849 100644 --- a/test/helpers/extensions/bluebubbles-monitor.ts +++ b/test/helpers/plugins/bluebubbles-monitor.ts @@ -1,13 +1,28 @@ +import type { HistoryEntry, PluginRuntime } from "openclaw/plugin-sdk/bluebubbles"; import { vi } from "vitest"; -import type { BlueBubblesHistoryFetchResult } from "../../../extensions/bluebubbles/src/history.js"; -import { - _resetBlueBubblesShortIdState, - clearBlueBubblesWebhookSecurityStateForTest, -} from "../../../extensions/bluebubbles/src/monitor.js"; -import type { PluginRuntime } from "../../../extensions/bluebubbles/src/runtime-api.js"; -import { setBlueBubblesRuntime } from "../../../extensions/bluebubbles/src/runtime.js"; +import { loadBundledPluginPublicSurfaceSync } from "../../../src/test-utils/bundled-plugin-public-surface.js"; import { createPluginRuntimeMock } from "./plugin-runtime-mock.js"; +type BlueBubblesHistoryFetchResult = { + entries: HistoryEntry[]; + resolved: boolean; +}; + +const { _resetBlueBubblesShortIdState, clearBlueBubblesWebhookSecurityStateForTest } = + loadBundledPluginPublicSurfaceSync<{ + _resetBlueBubblesShortIdState: () => void; + clearBlueBubblesWebhookSecurityStateForTest: () => void; + }>({ + pluginId: "bluebubbles", + artifactBasename: "src/monitor.js", + }); +const { setBlueBubblesRuntime } = loadBundledPluginPublicSurfaceSync<{ + setBlueBubblesRuntime: (runtime: PluginRuntime) => void; +}>({ + pluginId: "bluebubbles", + artifactBasename: "src/runtime.js", +}); + export type DispatchReplyParams = Parameters< PluginRuntime["channel"]["reply"]["dispatchReplyWithBufferedBlockDispatcher"] >[0]; diff --git a/test/helpers/extensions/bundled-web-search-fast-path-contract.ts b/test/helpers/plugins/bundled-web-search-fast-path-contract.ts similarity index 100% rename from test/helpers/extensions/bundled-web-search-fast-path-contract.ts rename to test/helpers/plugins/bundled-web-search-fast-path-contract.ts diff --git a/test/helpers/extensions/chunk-test-helpers.ts b/test/helpers/plugins/chunk-test-helpers.ts similarity index 100% rename from test/helpers/extensions/chunk-test-helpers.ts rename to test/helpers/plugins/chunk-test-helpers.ts diff --git a/test/helpers/extensions/configured-binding-runtime.ts b/test/helpers/plugins/configured-binding-runtime.ts similarity index 100% rename from test/helpers/extensions/configured-binding-runtime.ts rename to test/helpers/plugins/configured-binding-runtime.ts diff --git a/test/helpers/plugins/directory.ts b/test/helpers/plugins/directory.ts new file mode 100644 index 00000000000..31f46be63ad --- /dev/null +++ b/test/helpers/plugins/directory.ts @@ -0,0 +1,33 @@ +import type { ChannelDirectoryAdapter } from "openclaw/plugin-sdk/channel-runtime"; + +type DirectorySurface = { + listPeers: NonNullable; + listGroups: NonNullable; +}; + +export function createDirectoryTestRuntime() { + return { + log: () => {}, + error: () => {}, + exit: (code: number): never => { + throw new Error(`exit ${code}`); + }, + }; +} + +export function expectDirectorySurface(directory: unknown): DirectorySurface { + if (!directory || typeof directory !== "object") { + throw new Error("expected directory"); + } + const { listPeers, listGroups } = directory as ChannelDirectoryAdapter; + if (!listPeers) { + throw new Error("expected listPeers"); + } + if (!listGroups) { + throw new Error("expected listGroups"); + } + return { + listPeers, + listGroups, + }; +} diff --git a/test/helpers/extensions/discord-component-runtime.ts b/test/helpers/plugins/discord-component-runtime.ts similarity index 100% rename from test/helpers/extensions/discord-component-runtime.ts rename to test/helpers/plugins/discord-component-runtime.ts diff --git a/test/helpers/extensions/discord-provider.test-support.ts b/test/helpers/plugins/discord-provider.test-support.ts similarity index 90% rename from test/helpers/extensions/discord-provider.test-support.ts rename to test/helpers/plugins/discord-provider.test-support.ts index ae60abfc93b..7a32b3bd855 100644 --- a/test/helpers/extensions/discord-provider.test-support.ts +++ b/test/helpers/plugins/discord-provider.test-support.ts @@ -1,7 +1,8 @@ import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env"; import type { Mock } from "vitest"; import { expect, vi } from "vitest"; -import type { OpenClawConfig } from "../../../extensions/discord/src/runtime-api.js"; +import type { OpenClawConfig } from "../../../src/plugin-sdk/discord.js"; +import { resolveRelativeBundledPluginPublicModuleId } from "../../../src/test-utils/bundled-plugin-public-surface.js"; export type NativeCommandSpecMock = { name: string; @@ -144,6 +145,14 @@ const providerMonitorTestMocks: ProviderMonitorTestMocks = vi.hoisted(() => { }; }); +function buildDiscordSourceModuleId(artifactBasename: string): string { + return resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "discord", + artifactBasename: `src/${artifactBasename}`, + }); +} + const { clientHandleDeployRequestMock, clientFetchUserMock, @@ -401,23 +410,23 @@ vi.mock("openclaw/plugin-sdk/infra-runtime", async () => { }; }); -vi.mock("../../../extensions/discord/src/accounts.js", () => ({ +vi.mock(buildDiscordSourceModuleId("accounts.js"), () => ({ resolveDiscordAccount: resolveDiscordAccountMock, })); -vi.mock("../../../extensions/discord/src/probe.js", () => ({ +vi.mock(buildDiscordSourceModuleId("probe.js"), () => ({ fetchDiscordApplicationId: async () => "app-1", })); -vi.mock("../../../extensions/discord/src/token.js", () => ({ +vi.mock(buildDiscordSourceModuleId("token.js"), () => ({ normalizeDiscordToken: (value?: string) => value, })); -vi.mock("../../../extensions/discord/src/voice/command.js", () => ({ +vi.mock(buildDiscordSourceModuleId("voice/command.js"), () => ({ createDiscordVoiceCommand: () => ({ name: "voice-command" }), })); -vi.mock("../../../extensions/discord/src/monitor/agent-components.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/agent-components.js"), () => ({ createAgentComponentButton: () => ({ id: "btn" }), createAgentSelectMenu: () => ({ id: "menu" }), createDiscordComponentButton: () => ({ id: "btn2" }), @@ -429,15 +438,15 @@ vi.mock("../../../extensions/discord/src/monitor/agent-components.js", () => ({ createDiscordComponentUserSelect: () => ({ id: "user" }), })); -vi.mock("../../../extensions/discord/src/monitor/auto-presence.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/auto-presence.js"), () => ({ createDiscordAutoPresenceController: createDiscordAutoPresenceControllerMock, })); -vi.mock("../../../extensions/discord/src/monitor/commands.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/commands.js"), () => ({ resolveDiscordSlashCommandConfig: () => ({ ephemeral: false }), })); -vi.mock("../../../extensions/discord/src/monitor/exec-approvals.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/exec-approvals.js"), () => ({ createExecApprovalButton: () => ({ id: "exec-approval" }), DiscordExecApprovalHandler: class DiscordExecApprovalHandler { async start() { @@ -449,11 +458,11 @@ vi.mock("../../../extensions/discord/src/monitor/exec-approvals.js", () => ({ }, })); -vi.mock("../../../extensions/discord/src/monitor/gateway-plugin.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/gateway-plugin.js"), () => ({ createDiscordGatewayPlugin: () => ({ id: "gateway-plugin" }), })); -vi.mock("../../../extensions/discord/src/monitor/listeners.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/listeners.js"), () => ({ DiscordMessageListener: class DiscordMessageListener {}, DiscordPresenceListener: class DiscordPresenceListener {}, DiscordReactionListener: class DiscordReactionListener {}, @@ -462,36 +471,36 @@ vi.mock("../../../extensions/discord/src/monitor/listeners.js", () => ({ registerDiscordListener: vi.fn(), })); -vi.mock("../../../extensions/discord/src/monitor/message-handler.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/message-handler.js"), () => ({ createDiscordMessageHandler: createDiscordMessageHandlerMock, })); -vi.mock("../../../extensions/discord/src/monitor/native-command.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/native-command.js"), () => ({ createDiscordCommandArgFallbackButton: () => ({ id: "arg-fallback" }), createDiscordModelPickerFallbackButton: () => ({ id: "model-fallback-btn" }), createDiscordModelPickerFallbackSelect: () => ({ id: "model-fallback-select" }), createDiscordNativeCommand: createDiscordNativeCommandMock, })); -vi.mock("../../../extensions/discord/src/monitor/presence.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/presence.js"), () => ({ resolveDiscordPresenceUpdate: () => undefined, })); -vi.mock("../../../extensions/discord/src/monitor/provider.allowlist.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/provider.allowlist.js"), () => ({ resolveDiscordAllowlistConfig: resolveDiscordAllowlistConfigMock, })); -vi.mock("../../../extensions/discord/src/monitor/provider.lifecycle.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/provider.lifecycle.js"), () => ({ runDiscordGatewayLifecycle: monitorLifecycleMock, })); -vi.mock("../../../extensions/discord/src/monitor/rest-fetch.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/rest-fetch.js"), () => ({ resolveDiscordRestFetch: () => async () => { throw new Error("offline"); }, })); -vi.mock("../../../extensions/discord/src/monitor/thread-bindings.js", () => ({ +vi.mock(buildDiscordSourceModuleId("monitor/thread-bindings.js"), () => ({ createNoopThreadBindingManager: createNoopThreadBindingManagerMock, createThreadBindingManager: createThreadBindingManagerMock, reconcileAcpThreadBindingsOnStartup: reconcileAcpThreadBindingsOnStartupMock, diff --git a/test/helpers/extensions/env.ts b/test/helpers/plugins/env.ts similarity index 100% rename from test/helpers/extensions/env.ts rename to test/helpers/plugins/env.ts diff --git a/test/helpers/extensions/feishu-lifecycle.ts b/test/helpers/plugins/feishu-lifecycle.ts similarity index 91% rename from test/helpers/extensions/feishu-lifecycle.ts rename to test/helpers/plugins/feishu-lifecycle.ts index e07b83c3199..6db8f5168c6 100644 --- a/test/helpers/extensions/feishu-lifecycle.ts +++ b/test/helpers/plugins/feishu-lifecycle.ts @@ -1,14 +1,40 @@ +import type { ClawdbotConfig, PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/feishu"; import { expect, vi } from "vitest"; -import type { - ClawdbotConfig, - PluginRuntime, - RuntimeEnv, -} from "../../../extensions/feishu/runtime-api.js"; -import { monitorSingleAccount } from "../../../extensions/feishu/src/monitor.account.js"; -import { setFeishuRuntime } from "../../../extensions/feishu/src/runtime.js"; -import type { ResolvedFeishuAccount } from "../../../extensions/feishu/src/types.js"; +import { loadBundledPluginPublicSurfaceSync } from "../../../src/test-utils/bundled-plugin-public-surface.js"; import { createPluginRuntimeMock } from "./plugin-runtime-mock.js"; +type ResolvedFeishuAccount = { + accountId: string; + selectionSource: string; + enabled: boolean; + configured: boolean; + name?: string; + appId?: string; + appSecret?: string; + encryptKey?: string; + verificationToken?: string; + domain: string; + config: Record; +}; + +const { monitorSingleAccount } = loadBundledPluginPublicSurfaceSync<{ + monitorSingleAccount: (params: { + cfg: ClawdbotConfig; + account: ResolvedFeishuAccount; + runtime: RuntimeEnv; + botOpenIdSource: typeof FEISHU_PREFETCHED_BOT_OPEN_ID_SOURCE; + }) => Promise; +}>({ + pluginId: "feishu", + artifactBasename: "src/monitor.account.js", +}); +const { setFeishuRuntime } = loadBundledPluginPublicSurfaceSync<{ + setFeishuRuntime: (runtime: PluginRuntime) => void; +}>({ + pluginId: "feishu", + artifactBasename: "src/runtime.js", +}); + type InboundDebouncerParams = { onFlush?: (items: T[]) => Promise; onError?: (err: unknown, items: T[]) => void; @@ -175,12 +201,12 @@ export function createFeishuLifecycleConfig(params: { return { ...extraConfig, channels: { - ...((extraConfig.channels as Record | undefined) ?? {}), + ...(extraConfig.channels as Record | undefined), feishu: { enabled: true, requireMention: false, resolveSenderNames: false, - ...(params.channelConfig ?? {}), + ...params.channelConfig, accounts: { [params.accountId]: { enabled: true, @@ -189,7 +215,7 @@ export function createFeishuLifecycleConfig(params: { connectionMode: "websocket", requireMention: false, resolveSenderNames: false, - ...(params.accountConfig ?? {}), + ...params.accountConfig, }, }, }, @@ -220,8 +246,8 @@ export function createFeishuLifecycleFixture(params: { appId: params.appId, appSecret: params.appSecret, config: { - ...((params.channelConfig as Record | undefined) ?? {}), - ...((params.accountConfig as Record | undefined) ?? {}), + ...params.channelConfig, + ...params.accountConfig, }, }), }; diff --git a/test/helpers/extensions/fetch-mock.ts b/test/helpers/plugins/fetch-mock.ts similarity index 100% rename from test/helpers/extensions/fetch-mock.ts rename to test/helpers/plugins/fetch-mock.ts diff --git a/test/helpers/extensions/frozen-time.ts b/test/helpers/plugins/frozen-time.ts similarity index 100% rename from test/helpers/extensions/frozen-time.ts rename to test/helpers/plugins/frozen-time.ts diff --git a/test/helpers/extensions/jiti-runtime-api.ts b/test/helpers/plugins/jiti-runtime-api.ts similarity index 100% rename from test/helpers/extensions/jiti-runtime-api.ts rename to test/helpers/plugins/jiti-runtime-api.ts diff --git a/test/helpers/extensions/matrix-monitor-route.ts b/test/helpers/plugins/matrix-monitor-route.ts similarity index 100% rename from test/helpers/extensions/matrix-monitor-route.ts rename to test/helpers/plugins/matrix-monitor-route.ts diff --git a/test/helpers/extensions/matrix-route-test.ts b/test/helpers/plugins/matrix-route-test.ts similarity index 100% rename from test/helpers/extensions/matrix-route-test.ts rename to test/helpers/plugins/matrix-route-test.ts diff --git a/test/helpers/extensions/media-understanding.ts b/test/helpers/plugins/media-understanding.ts similarity index 100% rename from test/helpers/extensions/media-understanding.ts rename to test/helpers/plugins/media-understanding.ts diff --git a/test/helpers/extensions/mock-http-response.ts b/test/helpers/plugins/mock-http-response.ts similarity index 100% rename from test/helpers/extensions/mock-http-response.ts rename to test/helpers/plugins/mock-http-response.ts diff --git a/test/helpers/extensions/onboard-config.ts b/test/helpers/plugins/onboard-config.ts similarity index 100% rename from test/helpers/extensions/onboard-config.ts rename to test/helpers/plugins/onboard-config.ts diff --git a/test/helpers/extensions/package-manifest-contract.ts b/test/helpers/plugins/package-manifest-contract.ts similarity index 94% rename from test/helpers/extensions/package-manifest-contract.ts rename to test/helpers/plugins/package-manifest-contract.ts index e21cdf1eb65..41e6c20aa61 100644 --- a/test/helpers/extensions/package-manifest-contract.ts +++ b/test/helpers/plugins/package-manifest-contract.ts @@ -3,6 +3,7 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; import { isAtLeast, parseSemver } from "../../../src/infra/runtime-guard.js"; import { parseMinHostVersionRequirement } from "../../../src/plugins/min-host-version.js"; +import { bundledPluginFile } from "../bundled-plugin-paths.js"; type PackageManifest = { dependencies?: Record; @@ -25,7 +26,7 @@ function readJson(relativePath: string): T { } export function describePackageManifestContract(params: PackageManifestContractParams) { - const packagePath = `extensions/${params.pluginId}/package.json`; + const packagePath = bundledPluginFile(params.pluginId, "package.json"); describe(`${params.pluginId} package manifest contract`, () => { if (params.runtimeDeps?.length) { diff --git a/test/helpers/extensions/plugin-api.ts b/test/helpers/plugins/plugin-api.ts similarity index 90% rename from test/helpers/extensions/plugin-api.ts rename to test/helpers/plugins/plugin-api.ts index 329747698ec..d89aca74b01 100644 --- a/test/helpers/extensions/plugin-api.ts +++ b/test/helpers/plugins/plugin-api.ts @@ -1,6 +1,9 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime"; -type TestPluginApiInput = Partial & +type TestPluginApiInput = Omit< + Partial, + "id" | "name" | "source" | "config" | "runtime" +> & Pick; export function createTestPluginApi(api: TestPluginApiInput): OpenClawPluginApi { diff --git a/test/helpers/extensions/plugin-command.ts b/test/helpers/plugins/plugin-command.ts similarity index 100% rename from test/helpers/extensions/plugin-command.ts rename to test/helpers/plugins/plugin-command.ts diff --git a/test/helpers/extensions/plugin-registration-contract.ts b/test/helpers/plugins/plugin-registration-contract.ts similarity index 100% rename from test/helpers/extensions/plugin-registration-contract.ts rename to test/helpers/plugins/plugin-registration-contract.ts diff --git a/test/helpers/extensions/plugin-registration.ts b/test/helpers/plugins/plugin-registration.ts similarity index 100% rename from test/helpers/extensions/plugin-registration.ts rename to test/helpers/plugins/plugin-registration.ts diff --git a/test/helpers/extensions/plugin-routing.ts b/test/helpers/plugins/plugin-routing.ts similarity index 100% rename from test/helpers/extensions/plugin-routing.ts rename to test/helpers/plugins/plugin-routing.ts diff --git a/test/helpers/extensions/plugin-runtime-mock.ts b/test/helpers/plugins/plugin-runtime-mock.ts similarity index 100% rename from test/helpers/extensions/plugin-runtime-mock.ts rename to test/helpers/plugins/plugin-runtime-mock.ts diff --git a/test/helpers/extensions/plugin-sdk-stub.cjs b/test/helpers/plugins/plugin-sdk-stub.cjs similarity index 100% rename from test/helpers/extensions/plugin-sdk-stub.cjs rename to test/helpers/plugins/plugin-sdk-stub.cjs diff --git a/test/helpers/extensions/provider-auth-contract.ts b/test/helpers/plugins/provider-auth-contract.ts similarity index 96% rename from test/helpers/extensions/provider-auth-contract.ts rename to test/helpers/plugins/provider-auth-contract.ts index 4e99cb5d8a8..d377594d193 100644 --- a/test/helpers/extensions/provider-auth-contract.ts +++ b/test/helpers/plugins/provider-auth-contract.ts @@ -3,6 +3,7 @@ import { clearRuntimeAuthProfileStoreSnapshots } from "../../../src/agents/auth- import type { AuthProfileStore } from "../../../src/agents/auth-profiles/types.js"; import { registerProviders, requireProvider } from "../../../src/plugins/contracts/testkit.js"; import { createNonExitingRuntime } from "../../../src/runtime.js"; +import { loadBundledPluginPublicSurfaceSync } from "../../../src/test-utils/bundled-plugin-public-surface.js"; import type { WizardMultiSelectParams, WizardPrompter, @@ -44,8 +45,18 @@ vi.mock("openclaw/plugin-sdk/provider-auth", async (importOriginal) => { }; }); -import githubCopilotPlugin from "../../../extensions/github-copilot/index.js"; -import openAIPlugin from "../../../extensions/openai/index.js"; +const { default: githubCopilotPlugin } = loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; +}>({ + pluginId: "github-copilot", + artifactBasename: "index.js", +}); +const { default: openAIPlugin } = loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; +}>({ + pluginId: "openai", + artifactBasename: "index.js", +}); function buildPrompter(): WizardPrompter { const progress: WizardProgress = { diff --git a/test/helpers/extensions/provider-catalog-contract.ts b/test/helpers/plugins/provider-catalog-contract.ts similarity index 92% rename from test/helpers/extensions/provider-catalog-contract.ts rename to test/helpers/plugins/provider-catalog-contract.ts index d9e6ccbfd06..51c17a2ff7c 100644 --- a/test/helpers/extensions/provider-catalog-contract.ts +++ b/test/helpers/plugins/provider-catalog-contract.ts @@ -5,6 +5,7 @@ import { expectCodexMissingAuthHint, } from "../../../src/plugins/provider-runtime.test-support.js"; import type { ProviderPlugin } from "../../../src/plugins/types.js"; +import { loadBundledPluginPublicSurfaceSync } from "../../../src/test-utils/bundled-plugin-public-surface.js"; import { registerProviderPlugin, requireRegisteredProvider } from "./provider-registration.js"; const PROVIDER_CATALOG_CONTRACT_TIMEOUT_MS = 300_000; @@ -48,7 +49,12 @@ export function describeOpenAIProviderCatalogContract() { () => { beforeAll(async () => { vi.resetModules(); - const openaiPlugin = await import("../../../extensions/openai/index.ts"); + const openaiPlugin = loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]["plugin"]; + }>({ + pluginId: "openai", + artifactBasename: "index.js", + }); openaiProviders = registerProviderPlugin({ plugin: openaiPlugin.default, id: "openai", diff --git a/test/helpers/extensions/provider-contract.ts b/test/helpers/plugins/provider-contract.ts similarity index 100% rename from test/helpers/extensions/provider-contract.ts rename to test/helpers/plugins/provider-contract.ts diff --git a/test/helpers/extensions/provider-discovery-contract.ts b/test/helpers/plugins/provider-discovery-contract.ts similarity index 90% rename from test/helpers/extensions/provider-discovery-contract.ts rename to test/helpers/plugins/provider-discovery-contract.ts index 48cd854ebe6..dcfa40ad883 100644 --- a/test/helpers/extensions/provider-discovery-contract.ts +++ b/test/helpers/plugins/provider-discovery-contract.ts @@ -3,6 +3,10 @@ import type { AuthProfileStore } from "../../../src/agents/auth-profiles/types.j import type { OpenClawConfig } from "../../../src/config/config.js"; import type { ModelDefinitionConfig } from "../../../src/config/types.models.js"; import { registerProviders, requireProvider } from "../../../src/plugins/contracts/testkit.js"; +import { + loadBundledPluginPublicSurfaceSync, + resolveRelativeBundledPluginPublicModuleId, +} from "../../../src/test-utils/bundled-plugin-public-surface.js"; const resolveCopilotApiTokenMock = vi.hoisted(() => vi.fn()); const buildOllamaProviderMock = vi.hoisted(() => vi.fn()); @@ -106,6 +110,21 @@ function runCatalog( function installDiscoveryHooks(state: DiscoveryState) { beforeEach(async () => { + const githubCopilotTokenModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "github-copilot", + artifactBasename: "token.js", + }); + const vllmApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "vllm", + artifactBasename: "api.js", + }); + const sglangApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "sglang", + artifactBasename: "api.js", + }); vi.resetModules(); vi.doMock("openclaw/plugin-sdk/agent-runtime", async () => { const actual = await import("../../../src/plugin-sdk/agent-runtime.ts"); @@ -123,8 +142,8 @@ function installDiscoveryHooks(state: DiscoveryState) { listProfilesForProvider: listProfilesForProviderMock, }; }); - vi.doMock("../../../extensions/github-copilot/token.js", async () => { - const actual = await vi.importActual("../../../extensions/github-copilot/token.js"); + vi.doMock(githubCopilotTokenModuleId, async () => { + const actual = await vi.importActual(githubCopilotTokenModuleId); return { ...actual, resolveCopilotApiToken: resolveCopilotApiTokenMock, @@ -149,15 +168,15 @@ function installDiscoveryHooks(state: DiscoveryState) { buildSglangProvider: (...args: unknown[]) => buildSglangProviderMock(...args), }; }); - vi.doMock("../../../extensions/vllm/api.js", async () => { - const actual = await vi.importActual("../../../extensions/vllm/api.js"); + vi.doMock(vllmApiModuleId, async () => { + const actual = await vi.importActual(vllmApiModuleId); return { ...actual, buildVllmProvider: (...args: unknown[]) => buildVllmProviderMock(...args), }; }); - vi.doMock("../../../extensions/sglang/api.js", async () => { - const actual = await vi.importActual("../../../extensions/sglang/api.js"); + vi.doMock(sglangApiModuleId, async () => { + const actual = await vi.importActual(sglangApiModuleId); return { ...actual, buildSglangProvider: (...args: unknown[]) => buildSglangProviderMock(...args), @@ -174,13 +193,27 @@ function installDiscoveryHooks(state: DiscoveryState) { { default: modelStudioPlugin }, { default: cloudflareAiGatewayPlugin }, ] = await Promise.all([ - import("../../../extensions/github-copilot/index.js"), - import("../../../extensions/ollama/index.js"), - import("../../../extensions/vllm/index.js"), - import("../../../extensions/sglang/index.js"), - import("../../../extensions/minimax/index.js"), - import("../../../extensions/modelstudio/index.js"), - import("../../../extensions/cloudflare-ai-gateway/index.js"), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "github-copilot", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "ollama", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "vllm", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "sglang", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "minimax", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "modelstudio", artifactBasename: "index.js" }), + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]; + }>({ pluginId: "cloudflare-ai-gateway", artifactBasename: "index.js" }), ]); state.githubCopilotProvider = requireProvider( registerProviders(githubCopilotPlugin), diff --git a/test/helpers/extensions/provider-registration.ts b/test/helpers/plugins/provider-registration.ts similarity index 100% rename from test/helpers/extensions/provider-registration.ts rename to test/helpers/plugins/provider-registration.ts diff --git a/test/helpers/extensions/provider-runtime-contract.ts b/test/helpers/plugins/provider-runtime-contract.ts similarity index 92% rename from test/helpers/extensions/provider-runtime-contract.ts rename to test/helpers/plugins/provider-runtime-contract.ts index 77c77ec02f9..c82670e1e49 100644 --- a/test/helpers/extensions/provider-runtime-contract.ts +++ b/test/helpers/plugins/provider-runtime-contract.ts @@ -4,6 +4,10 @@ import path from "node:path"; import type { StreamFn } from "@mariozechner/pi-agent-core"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ProviderPlugin, ProviderRuntimeModel } from "../../../src/plugins/types.js"; +import { + loadBundledPluginPublicSurfaceSync, + resolveRelativeBundledPluginPublicModuleId, +} from "../../../src/test-utils/bundled-plugin-public-surface.js"; import { createProviderUsageFetch, makeResponse, @@ -32,7 +36,13 @@ vi.mock("@mariozechner/pi-ai/oauth", async () => { }; }); -vi.mock("../../../extensions/openai/openai-codex-provider.runtime.js", () => ({ +const openAICodexProviderRuntimeModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "openai", + artifactBasename: "openai-codex-provider.runtime.js", +}); + +vi.mock(openAICodexProviderRuntimeModuleId, () => ({ refreshOpenAICodexToken: refreshOpenAICodexTokenMock, })); @@ -63,49 +73,97 @@ const PROVIDER_RUNTIME_CONTRACT_FIXTURES: readonly ProviderRuntimeContractFixtur providerIds: ["anthropic"], pluginId: "anthropic", name: "Anthropic", - load: async () => await import("../../../extensions/anthropic/index.ts"), + load: async () => + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]["plugin"]; + }>({ + pluginId: "anthropic", + artifactBasename: "index.js", + }), }, { providerIds: ["github-copilot"], pluginId: "github-copilot", name: "GitHub Copilot", - load: async () => await import("../../../extensions/github-copilot/index.ts"), + load: async () => + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]["plugin"]; + }>({ + pluginId: "github-copilot", + artifactBasename: "index.js", + }), }, { providerIds: ["google", "google-gemini-cli"], pluginId: "google", name: "Google", - load: async () => await import("../../../extensions/google/index.ts"), + load: async () => + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]["plugin"]; + }>({ + pluginId: "google", + artifactBasename: "index.js", + }), }, { providerIds: ["openai", "openai-codex"], pluginId: "openai", name: "OpenAI", - load: async () => await import("../../../extensions/openai/index.ts"), + load: async () => + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]["plugin"]; + }>({ + pluginId: "openai", + artifactBasename: "index.js", + }), }, { providerIds: ["openrouter"], pluginId: "openrouter", name: "OpenRouter", - load: async () => await import("../../../extensions/openrouter/index.ts"), + load: async () => + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]["plugin"]; + }>({ + pluginId: "openrouter", + artifactBasename: "index.js", + }), }, { providerIds: ["venice"], pluginId: "venice", name: "Venice", - load: async () => await import("../../../extensions/venice/index.ts"), + load: async () => + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]["plugin"]; + }>({ + pluginId: "venice", + artifactBasename: "index.js", + }), }, { providerIds: ["xai"], pluginId: "xai", name: "xAI", - load: async () => await import("../../../extensions/xai/index.ts"), + load: async () => + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]["plugin"]; + }>({ + pluginId: "xai", + artifactBasename: "index.js", + }), }, { providerIds: ["zai"], pluginId: "zai", name: "Z.AI", - load: async () => await import("../../../extensions/zai/index.ts"), + load: async () => + loadBundledPluginPublicSurfaceSync<{ + default: Parameters[0]["plugin"]; + }>({ + pluginId: "zai", + artifactBasename: "index.js", + }), }, ] as const; diff --git a/test/helpers/extensions/provider-usage-fetch.ts b/test/helpers/plugins/provider-usage-fetch.ts similarity index 100% rename from test/helpers/extensions/provider-usage-fetch.ts rename to test/helpers/plugins/provider-usage-fetch.ts diff --git a/test/helpers/extensions/runtime-env.ts b/test/helpers/plugins/runtime-env.ts similarity index 86% rename from test/helpers/extensions/runtime-env.ts rename to test/helpers/plugins/runtime-env.ts index 46a136a5d67..ec59326b588 100644 --- a/test/helpers/extensions/runtime-env.ts +++ b/test/helpers/plugins/runtime-env.ts @@ -1,9 +1,7 @@ import type { OutputRuntimeEnv } from "openclaw/plugin-sdk/runtime"; import { vi } from "vitest"; -export function createRuntimeEnv(options?: { - throwOnExit?: boolean; -}): OutputRuntimeEnv { +export function createRuntimeEnv(options?: { throwOnExit?: boolean }): OutputRuntimeEnv { const throwOnExit = options?.throwOnExit ?? true; return { log: vi.fn(), diff --git a/test/helpers/extensions/send-config.ts b/test/helpers/plugins/send-config.ts similarity index 100% rename from test/helpers/extensions/send-config.ts rename to test/helpers/plugins/send-config.ts diff --git a/test/helpers/extensions/setup-wizard.ts b/test/helpers/plugins/setup-wizard.ts similarity index 100% rename from test/helpers/extensions/setup-wizard.ts rename to test/helpers/plugins/setup-wizard.ts diff --git a/test/helpers/extensions/start-account-context.ts b/test/helpers/plugins/start-account-context.ts similarity index 100% rename from test/helpers/extensions/start-account-context.ts rename to test/helpers/plugins/start-account-context.ts diff --git a/test/helpers/extensions/start-account-lifecycle.ts b/test/helpers/plugins/start-account-lifecycle.ts similarity index 100% rename from test/helpers/extensions/start-account-lifecycle.ts rename to test/helpers/plugins/start-account-lifecycle.ts diff --git a/test/helpers/extensions/status-issues.ts b/test/helpers/plugins/status-issues.ts similarity index 100% rename from test/helpers/extensions/status-issues.ts rename to test/helpers/plugins/status-issues.ts diff --git a/test/helpers/extensions/subagent-hooks.ts b/test/helpers/plugins/subagent-hooks.ts similarity index 100% rename from test/helpers/extensions/subagent-hooks.ts rename to test/helpers/plugins/subagent-hooks.ts diff --git a/test/helpers/extensions/telegram-plugin-command.ts b/test/helpers/plugins/telegram-plugin-command.ts similarity index 100% rename from test/helpers/extensions/telegram-plugin-command.ts rename to test/helpers/plugins/telegram-plugin-command.ts diff --git a/test/helpers/extensions/temp-dir.ts b/test/helpers/plugins/temp-dir.ts similarity index 100% rename from test/helpers/extensions/temp-dir.ts rename to test/helpers/plugins/temp-dir.ts diff --git a/test/helpers/extensions/temp-home.ts b/test/helpers/plugins/temp-home.ts similarity index 100% rename from test/helpers/extensions/temp-home.ts rename to test/helpers/plugins/temp-home.ts diff --git a/test/helpers/extensions/typed-cases.ts b/test/helpers/plugins/typed-cases.ts similarity index 100% rename from test/helpers/extensions/typed-cases.ts rename to test/helpers/plugins/typed-cases.ts diff --git a/test/helpers/extensions/web-search-provider-contract.ts b/test/helpers/plugins/web-search-provider-contract.ts similarity index 100% rename from test/helpers/extensions/web-search-provider-contract.ts rename to test/helpers/plugins/web-search-provider-contract.ts diff --git a/test/helpers/extensions/zalo-lifecycle.ts b/test/helpers/plugins/zalo-lifecycle.ts similarity index 85% rename from test/helpers/extensions/zalo-lifecycle.ts rename to test/helpers/plugins/zalo-lifecycle.ts index 31c132d0a32..e8b26d32774 100644 --- a/test/helpers/extensions/zalo-lifecycle.ts +++ b/test/helpers/plugins/zalo-lifecycle.ts @@ -1,21 +1,61 @@ import { request as httpRequest } from "node:http"; +import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/zalo"; import { expect, vi } from "vitest"; -import type { ResolvedZaloAccount } from "../../../extensions/zalo/src/accounts.js"; -import { - clearZaloWebhookSecurityStateForTest, - monitorZaloProvider, -} from "../../../extensions/zalo/src/monitor.js"; -import type { PluginRuntime } from "../../../extensions/zalo/src/runtime-api.js"; -import type { OpenClawConfig } from "../../../extensions/zalo/src/runtime-api.js"; -import { normalizeSecretInputString } from "../../../extensions/zalo/src/secret-input.js"; import { createEmptyPluginRegistry } from "../../../src/plugins/registry.js"; import { setActivePluginRegistry } from "../../../src/plugins/runtime.js"; +import { + loadBundledPluginPublicSurfaceSync, + resolveRelativeBundledPluginPublicModuleId, +} from "../../../src/test-utils/bundled-plugin-public-surface.js"; import { withServer } from "../http-test-server.js"; import { createPluginRuntimeMock } from "./plugin-runtime-mock.js"; import { createRuntimeEnv } from "./runtime-env.js"; export { withServer }; +type ResolvedZaloAccount = { + accountId: string; + enabled: boolean; + token: string; + tokenSource: "env" | "config" | "configFile" | "none"; + config: Record; +}; + +const { clearZaloWebhookSecurityStateForTest, monitorZaloProvider } = + loadBundledPluginPublicSurfaceSync<{ + clearZaloWebhookSecurityStateForTest: () => void; + monitorZaloProvider: (params: { + token: string; + account: ResolvedZaloAccount; + config: OpenClawConfig; + runtime: ReturnType; + abortSignal: AbortSignal; + useWebhook?: boolean; + webhookUrl?: string; + webhookSecret?: string; + }) => Promise; + }>({ + pluginId: "zalo", + artifactBasename: "src/monitor.js", + }); +const { normalizeSecretInputString } = loadBundledPluginPublicSurfaceSync<{ + normalizeSecretInputString: (value: unknown) => string | undefined; +}>({ + pluginId: "zalo", + artifactBasename: "src/secret-input.js", +}); + +const zaloApiModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "zalo", + artifactBasename: "src/api.js", +}); +const zaloRuntimeModuleId = resolveRelativeBundledPluginPublicModuleId({ + fromModuleUrl: import.meta.url, + pluginId: "zalo", + artifactBasename: "src/runtime.js", +}); + const lifecycleMocks = vi.hoisted(() => ({ setWebhookMock: vi.fn(async () => ({ ok: true, result: { url: "" } })), deleteWebhookMock: vi.fn(async () => ({ ok: true, result: { url: "" } })), @@ -39,8 +79,8 @@ export const sendMessageMock = lifecycleMocks.sendMessageMock; export const sendPhotoMock = lifecycleMocks.sendPhotoMock; export const getZaloRuntimeMock = lifecycleMocks.getZaloRuntimeMock; -vi.mock("../../../extensions/zalo/src/api.js", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock(zaloApiModuleId, async (importOriginal) => { + const actual = await importOriginal(); return { ...actual, deleteWebhook: lifecycleMocks.deleteWebhookMock, @@ -53,7 +93,7 @@ vi.mock("../../../extensions/zalo/src/api.js", async (importOriginal) => { }; }); -vi.mock("../../../extensions/zalo/src/runtime.js", () => ({ +vi.mock(zaloRuntimeModuleId, () => ({ getZaloRuntime: lifecycleMocks.getZaloRuntimeMock, })); @@ -341,7 +381,11 @@ export async function startWebhookLifecycleMonitor(params: { setActivePluginRegistry(registry); const abort = new AbortController(); const runtime = createRuntimeEnv(); - const webhookUrl = params.webhookUrl ?? params.account.config?.webhookUrl; + const accountWebhookUrl = + typeof params.account.config?.webhookUrl === "string" + ? params.account.config.webhookUrl + : undefined; + const webhookUrl = params.webhookUrl ?? accountWebhookUrl; const webhookSecret = params.webhookSecret ?? normalizeSecretInputString(params.account.config?.webhookSecret); const run = monitorZaloProvider({ diff --git a/test/official-channel-catalog.test.ts b/test/official-channel-catalog.test.ts index 23bd52c8336..a4eb9cbc5a0 100644 --- a/test/official-channel-catalog.test.ts +++ b/test/official-channel-catalog.test.ts @@ -6,6 +6,7 @@ import { OFFICIAL_CHANNEL_CATALOG_RELATIVE_PATH, writeOfficialChannelCatalog, } from "../scripts/write-official-channel-catalog.mjs"; +import { bundledPluginRoot } from "./helpers/bundled-plugin-paths.js"; import { cleanupTempDirs, makeTempRepoRoot, writeJsonFile } from "./helpers/temp-repo.js"; const tempDirs: string[] = []; @@ -40,7 +41,7 @@ describe("buildOfficialChannelCatalog", () => { }, install: { npmSpec: "@openclaw/whatsapp", - localPath: "extensions/whatsapp", + localPath: bundledPluginRoot("whatsapp"), defaultChoice: "npm", }, release: { @@ -59,7 +60,7 @@ describe("buildOfficialChannelCatalog", () => { blurb: "dev only", }, install: { - localPath: "extensions/local-only", + localPath: bundledPluginRoot("local-only"), }, release: { publishToNpm: false, @@ -84,7 +85,7 @@ describe("buildOfficialChannelCatalog", () => { }, install: { npmSpec: "@openclaw/whatsapp", - localPath: "extensions/whatsapp", + localPath: bundledPluginRoot("whatsapp"), defaultChoice: "npm", }, }, diff --git a/test/plugin-npm-release.test.ts b/test/plugin-npm-release.test.ts index af718c53d75..7ffc034809a 100644 --- a/test/plugin-npm-release.test.ts +++ b/test/plugin-npm-release.test.ts @@ -9,6 +9,7 @@ import { resolveSelectedPublishablePluginPackages, type PublishablePluginPackage, } from "../scripts/lib/plugin-npm-release.ts"; +import { bundledPluginFile, bundledPluginRoot } from "./helpers/bundled-plugin-paths.js"; describe("parsePluginReleaseSelection", () => { it("returns an empty list for blank input", () => { @@ -75,7 +76,7 @@ describe("collectPublishablePluginPackageErrors", () => { expect( collectPublishablePluginPackageErrors({ extensionId: "zalo", - packageDir: "extensions/zalo", + packageDir: bundledPluginRoot("zalo"), packageJson: { name: "@openclaw/zalo", version: "2026.3.15", @@ -94,7 +95,7 @@ describe("collectPublishablePluginPackageErrors", () => { expect( collectPublishablePluginPackageErrors({ extensionId: "broken", - packageDir: "extensions/broken", + packageDir: bundledPluginRoot("broken"), packageJson: { name: "broken", version: "latest", @@ -120,7 +121,7 @@ describe("resolveSelectedPublishablePluginPackages", () => { const publishablePlugins: PublishablePluginPackage[] = [ { extensionId: "feishu", - packageDir: "extensions/feishu", + packageDir: bundledPluginRoot("feishu"), packageName: "@openclaw/feishu", version: "2026.3.15", channel: "stable", @@ -128,7 +129,7 @@ describe("resolveSelectedPublishablePluginPackages", () => { }, { extensionId: "zalo", - packageDir: "extensions/zalo", + packageDir: bundledPluginRoot("zalo"), packageName: "@openclaw/zalo", version: "2026.3.15-beta.1", channel: "beta", @@ -168,9 +169,9 @@ describe("collectChangedExtensionIdsFromPaths", () => { it("extracts unique extension ids from changed extension paths", () => { expect( collectChangedExtensionIdsFromPaths([ - "extensions/zalo/index.ts", - "extensions/zalo/package.json", - "extensions/feishu/src/client.ts", + bundledPluginFile("zalo", "index.ts"), + bundledPluginFile("zalo", "package.json"), + bundledPluginFile("feishu", "src/client.ts"), "docs/reference/RELEASING.md", ]), ).toEqual(["feishu", "zalo"]); @@ -181,7 +182,7 @@ describe("resolveChangedPublishablePluginPackages", () => { const publishablePlugins: PublishablePluginPackage[] = [ { extensionId: "feishu", - packageDir: "extensions/feishu", + packageDir: bundledPluginRoot("feishu"), packageName: "@openclaw/feishu", version: "2026.3.15", channel: "stable", @@ -189,7 +190,7 @@ describe("resolveChangedPublishablePluginPackages", () => { }, { extensionId: "zalo", - packageDir: "extensions/zalo", + packageDir: bundledPluginRoot("zalo"), packageName: "@openclaw/zalo", version: "2026.3.15-beta.1", channel: "beta", diff --git a/test/release-check.test.ts b/test/release-check.test.ts index 406ee67f43a..e0e6bd17b06 100644 --- a/test/release-check.test.ts +++ b/test/release-check.test.ts @@ -8,6 +8,7 @@ import { collectMissingPackPaths, collectPackUnpackedSizeErrors, } from "../scripts/release-check.ts"; +import { bundledDistPluginFile, bundledPluginFile } from "./helpers/bundled-plugin-paths.js"; function makeItem(shortVersion: string, sparkleVersion: string): string { return `${shortVersion}${shortVersion}${sparkleVersion}`; @@ -113,11 +114,11 @@ describe("collectForbiddenPackPaths", () => { expect( collectForbiddenPackPaths([ "dist/index.js", - "dist/extensions/discord/node_modules/@buape/carbon/index.js", - "extensions/tlon/node_modules/.bin/tlon", + bundledDistPluginFile("discord", "node_modules/@buape/carbon/index.js"), + bundledPluginFile("tlon", "node_modules/.bin/tlon"), "node_modules/.bin/openclaw", ]), - ).toEqual(["extensions/tlon/node_modules/.bin/tlon", "node_modules/.bin/openclaw"]); + ).toEqual([bundledPluginFile("tlon", "node_modules/.bin/tlon"), "node_modules/.bin/openclaw"]); }); }); @@ -137,15 +138,15 @@ describe("collectMissingPackPaths", () => { expect.arrayContaining([ "dist/channel-catalog.json", "dist/control-ui/index.html", - "dist/extensions/matrix/helper-api.js", - "dist/extensions/matrix/runtime-api.js", - "dist/extensions/matrix/thread-bindings-runtime.js", - "dist/extensions/matrix/openclaw.plugin.json", - "dist/extensions/matrix/package.json", - "dist/extensions/whatsapp/light-runtime-api.js", - "dist/extensions/whatsapp/runtime-api.js", - "dist/extensions/whatsapp/openclaw.plugin.json", - "dist/extensions/whatsapp/package.json", + bundledDistPluginFile("matrix", "helper-api.js"), + bundledDistPluginFile("matrix", "runtime-api.js"), + bundledDistPluginFile("matrix", "thread-bindings-runtime.js"), + bundledDistPluginFile("matrix", "openclaw.plugin.json"), + bundledDistPluginFile("matrix", "package.json"), + bundledDistPluginFile("whatsapp", "light-runtime-api.js"), + bundledDistPluginFile("whatsapp", "runtime-api.js"), + bundledDistPluginFile("whatsapp", "openclaw.plugin.json"), + bundledDistPluginFile("whatsapp", "package.json"), ]), ); }); @@ -168,11 +169,11 @@ describe("collectMissingPackPaths", () => { it("requires bundled plugin runtime sidecars that dynamic plugin boundaries resolve at runtime", () => { expect(requiredBundledPluginPackPaths).toEqual( expect.arrayContaining([ - "dist/extensions/matrix/helper-api.js", - "dist/extensions/matrix/runtime-api.js", - "dist/extensions/matrix/thread-bindings-runtime.js", - "dist/extensions/whatsapp/light-runtime-api.js", - "dist/extensions/whatsapp/runtime-api.js", + bundledDistPluginFile("matrix", "helper-api.js"), + bundledDistPluginFile("matrix", "runtime-api.js"), + bundledDistPluginFile("matrix", "thread-bindings-runtime.js"), + bundledDistPluginFile("whatsapp", "light-runtime-api.js"), + bundledDistPluginFile("whatsapp", "runtime-api.js"), ]), ); }); diff --git a/test/scripts/check-no-conflict-markers.test.ts b/test/scripts/check-no-conflict-markers.test.ts index db8be9d8e99..9bb656eb1c3 100644 --- a/test/scripts/check-no-conflict-markers.test.ts +++ b/test/scripts/check-no-conflict-markers.test.ts @@ -77,7 +77,7 @@ describe("check-no-conflict-markers", () => { git(rootDir, "config", "user.email", "test@example.com"); git(rootDir, "config", "user.name", "Test User"); - const scriptFile = path.join(rootDir, "scripts", "generate-bundled-plugin-metadata.mjs"); + const scriptFile = path.join(rootDir, "scripts", "bundled-plugin-metadata-runtime.mjs"); fs.mkdirSync(path.dirname(scriptFile), { recursive: true }); fs.writeFileSync( scriptFile, @@ -89,7 +89,7 @@ describe("check-no-conflict-markers", () => { ">>>>>>> branch", ].join("\n"), ); - git(rootDir, "add", "scripts/generate-bundled-plugin-metadata.mjs"); + git(rootDir, "add", "scripts/bundled-plugin-metadata-runtime.mjs"); const violations = findConflictMarkersInFiles(listTrackedFiles(rootDir)); diff --git a/test/scripts/test-extension.test.ts b/test/scripts/test-extension.test.ts index f04377e26c1..5208cc23f3a 100644 --- a/test/scripts/test-extension.test.ts +++ b/test/scripts/test-extension.test.ts @@ -8,6 +8,7 @@ import { partitionExtensionTestFiles, resolveExtensionTestPlan, } from "../../scripts/test-extension.mjs"; +import { bundledPluginFile, bundledPluginRoot } from "../helpers/bundled-plugin-paths.js"; const scriptPath = path.join(process.cwd(), "scripts", "test-extension.mjs"); @@ -41,18 +42,24 @@ describe("scripts/test-extension.mjs", () => { const plan = resolveExtensionTestPlan({ targetArg: "slack", cwd: process.cwd() }); expect(plan.extensionId).toBe("slack"); - expect(plan.extensionDir).toBe("extensions/slack"); + expect(plan.extensionDir).toBe(bundledPluginRoot("slack")); expect(plan.config).toBe("vitest.channels.config.ts"); - expect(plan.testFiles.some((file) => file.startsWith("extensions/slack/"))).toBe(true); + expect(plan.testFiles.some((file) => file.startsWith(`${bundledPluginRoot("slack")}/`))).toBe( + true, + ); }); it("splits channel monitor files into isolated runs", () => { const plan = resolveExtensionTestPlan({ targetArg: "discord", cwd: process.cwd() }); expect(plan.config).toBe("vitest.channels.config.ts"); - expect(plan.isolatedTestFiles).toContain("extensions/discord/src/monitor/provider.test.ts"); - expect(plan.sharedTestFiles).toContain("extensions/discord/src/channel.test.ts"); - expect(plan.sharedTestFiles).not.toContain("extensions/discord/src/monitor/provider.test.ts"); + expect(plan.isolatedTestFiles).toContain( + bundledPluginFile("discord", "src/monitor/provider.test.ts"), + ); + expect(plan.sharedTestFiles).toContain(bundledPluginFile("discord", "src/channel.test.ts")); + expect(plan.sharedTestFiles).not.toContain( + bundledPluginFile("discord", "src/monitor/provider.test.ts"), + ); }); it("resolves provider extensions onto the extensions vitest config", () => { @@ -60,28 +67,34 @@ describe("scripts/test-extension.mjs", () => { expect(plan.extensionId).toBe("firecrawl"); expect(plan.config).toBe("vitest.extensions.config.ts"); - expect(plan.testFiles.some((file) => file.startsWith("extensions/firecrawl/"))).toBe(true); + expect( + plan.testFiles.some((file) => file.startsWith(`${bundledPluginRoot("firecrawl")}/`)), + ).toBe(true); }); it("applies exact isolated files for non-channel extensions", () => { const { isolatedTestFiles, sharedTestFiles } = partitionExtensionTestFiles({ config: "vitest.extensions.config.ts", testFiles: [ - "extensions/firecrawl/src/firecrawl-scrape-tool.test.ts", - "extensions/firecrawl/src/index.test.ts", + bundledPluginFile("firecrawl", "src/firecrawl-scrape-tool.test.ts"), + bundledPluginFile("firecrawl", "src/index.test.ts"), ], }); - expect(isolatedTestFiles).toEqual(["extensions/firecrawl/src/firecrawl-scrape-tool.test.ts"]); - expect(sharedTestFiles).toEqual(["extensions/firecrawl/src/index.test.ts"]); + expect(isolatedTestFiles).toEqual([ + bundledPluginFile("firecrawl", "src/firecrawl-scrape-tool.test.ts"), + ]); + expect(sharedTestFiles).toEqual([bundledPluginFile("firecrawl", "src/index.test.ts")]); }); it("includes paired src roots when they contain tests", () => { const plan = resolveExtensionTestPlan({ targetArg: "line", cwd: process.cwd() }); - expect(plan.roots).toContain("extensions/line"); + expect(plan.roots).toContain(bundledPluginRoot("line")); expect(plan.config).toBe("vitest.extensions.config.ts"); - expect(plan.testFiles.some((file) => file.startsWith("extensions/line/"))).toBe(true); + expect(plan.testFiles.some((file) => file.startsWith(`${bundledPluginRoot("line")}/`))).toBe( + true, + ); }); it("infers the extension from the current working directory", () => { @@ -89,14 +102,14 @@ describe("scripts/test-extension.mjs", () => { const plan = readPlan([], cwd); expect(plan.extensionId).toBe("slack"); - expect(plan.extensionDir).toBe("extensions/slack"); + expect(plan.extensionDir).toBe(bundledPluginRoot("slack")); }); it("maps changed paths back to extension ids", () => { const extensionIds = detectChangedExtensionIds([ - "extensions/slack/src/channel.ts", + bundledPluginFile("slack", "src/channel.ts"), "src/line/message.test.ts", - "extensions/firecrawl/package.json", + bundledPluginFile("firecrawl", "package.json"), "src/not-a-plugin/file.ts", ]); @@ -134,7 +147,7 @@ describe("scripts/test-extension.mjs", () => { const extensionId = findExtensionWithoutTests(); const stdout = runScript([extensionId]); - expect(stdout).toContain(`No tests found for extensions/${extensionId}.`); + expect(stdout).toContain(`No tests found for ${bundledPluginRoot(extensionId)}.`); expect(stdout).toContain("Skipping."); }); }); diff --git a/test/scripts/test-parallel.test.ts b/test/scripts/test-parallel.test.ts index d97080171bc..dfc2e2ff8f8 100644 --- a/test/scripts/test-parallel.test.ts +++ b/test/scripts/test-parallel.test.ts @@ -14,6 +14,7 @@ import { resolveTestRunExitCode, } from "../../scripts/test-parallel-utils.mjs"; import { loadTestCatalog } from "../../scripts/test-planner/catalog.mjs"; +import { bundledPluginFile } from "../helpers/bundled-plugin-paths.js"; const clearPlannerShardEnv = (env) => { const nextEnv = { ...env }; @@ -54,10 +55,10 @@ const sharedTargetedUnitProxyFiles = (() => { const targetedChannelProxyFiles = [ ...sharedTargetedChannelProxyFiles, - "extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts", - "extensions/discord/src/monitor/monitor.agent-components.test.ts", - "extensions/telegram/src/bot.create-telegram-bot.test.ts", - "extensions/whatsapp/src/monitor-inbox.streams-inbound-messages.test.ts", + bundledPluginFile("discord", "src/monitor/message-handler.preflight.acp-bindings.test.ts"), + bundledPluginFile("discord", "src/monitor/monitor.agent-components.test.ts"), + bundledPluginFile("telegram", "src/bot.create-telegram-bot.test.ts"), + bundledPluginFile("whatsapp", "src/monitor-inbox.streams-inbound-messages.test.ts"), ]; const targetedUnitProxyFiles = [ diff --git a/test/scripts/test-planner.test.ts b/test/scripts/test-planner.test.ts index b28f6bf2f6f..5665d86da94 100644 --- a/test/scripts/test-planner.test.ts +++ b/test/scripts/test-planner.test.ts @@ -12,6 +12,7 @@ import { buildExecutionPlan, explainExecutionTarget, } from "../../scripts/test-planner/planner.mjs"; +import { bundledPluginFile } from "../helpers/bundled-plugin-paths.js"; describe("test planner", () => { it("builds a capability-aware plan for mid-memory local runs", () => { @@ -195,7 +196,10 @@ describe("test planner", () => { surfaces: [], passthroughArgs: [ "src/auto-reply/reply/followup-runner.test.ts", - "extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts", + bundledPluginFile( + "discord", + "src/monitor/message-handler.preflight.acp-bindings.test.ts", + ), ], }, { diff --git a/test/test-runner-manifest.test.ts b/test/test-runner-manifest.test.ts index ebad441811f..aa4e0f09327 100644 --- a/test/test-runner-manifest.test.ts +++ b/test/test-runner-manifest.test.ts @@ -3,6 +3,7 @@ import { loadChannelTimingManifest, loadTestRunnerBehavior, } from "../scripts/test-runner-manifest.mjs"; +import { bundledPluginDirPrefix, bundledPluginFile } from "./helpers/bundled-plugin-paths.js"; describe("loadTestRunnerBehavior", () => { it("loads channel isolated entries from the behavior manifest", () => { @@ -10,14 +11,16 @@ describe("loadTestRunnerBehavior", () => { const files = behavior.channels.isolated.map((entry) => entry.file); expect(files).toContain( - "extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts", + bundledPluginFile("discord", "src/monitor/message-handler.preflight.acp-bindings.test.ts"), ); }); it("loads channel isolated prefixes from the behavior manifest", () => { const behavior = loadTestRunnerBehavior(); - expect(behavior.channels.isolatedPrefixes).toContain("extensions/discord/src/monitor/"); + expect(behavior.channels.isolatedPrefixes).toContain( + bundledPluginDirPrefix("discord", "src/monitor"), + ); }); it("loads channel timing metadata from the timing manifest", () => { diff --git a/test/vitest-extensions-config.test.ts b/test/vitest-extensions-config.test.ts index f9228f04466..391e2f7f9d4 100644 --- a/test/vitest-extensions-config.test.ts +++ b/test/vitest-extensions-config.test.ts @@ -1,5 +1,6 @@ import { afterEach, describe, expect, it } from "vitest"; import { loadIncludePatternsFromEnv } from "../vitest.extensions.config.ts"; +import { bundledPluginFile } from "./helpers/bundled-plugin-paths.js"; import { createPatternFileHelper } from "./helpers/pattern-file.js"; const patternFiles = createPatternFileHelper("openclaw-vitest-extensions-config-"); @@ -15,22 +16,25 @@ describe("extensions vitest include patterns", () => { it("loads include patterns from a JSON file", () => { const filePath = patternFiles.writePatternFile("include.json", [ - "extensions/feishu/index.test.ts", + bundledPluginFile("feishu", "index.test.ts"), 42, "", - "extensions/msteams/src/monitor.test.ts", + bundledPluginFile("msteams", "src/monitor.test.ts"), ]); expect( loadIncludePatternsFromEnv({ OPENCLAW_VITEST_INCLUDE_FILE: filePath, }), - ).toEqual(["extensions/feishu/index.test.ts", "extensions/msteams/src/monitor.test.ts"]); + ).toEqual([ + bundledPluginFile("feishu", "index.test.ts"), + bundledPluginFile("msteams", "src/monitor.test.ts"), + ]); }); it("throws when the configured file is not a JSON array", () => { const filePath = patternFiles.writePatternFile("include.json", { - include: ["extensions/feishu/index.test.ts"], + include: [bundledPluginFile("feishu", "index.test.ts")], }); expect(() => diff --git a/test/vitest-scoped-config.test.ts b/test/vitest-scoped-config.test.ts index 7691a4174d6..11bc9816a99 100644 --- a/test/vitest-scoped-config.test.ts +++ b/test/vitest-scoped-config.test.ts @@ -6,6 +6,9 @@ import { createChannelsVitestConfig } from "../vitest.channels.config.ts"; import { createExtensionsVitestConfig } from "../vitest.extensions.config.ts"; import { createGatewayVitestConfig } from "../vitest.gateway.config.ts"; import { createScopedVitestConfig, resolveVitestIsolation } from "../vitest.scoped-config.ts"; +import { BUNDLED_PLUGIN_TEST_GLOB, bundledPluginFile } from "./helpers/bundled-plugin-paths.js"; + +const EXTENSIONS_CHANNEL_GLOB = ["extensions", "channel", "**"].join("/"); describe("resolveVitestIsolation", () => { it("defaults shared scoped configs to non-isolated workers", () => { @@ -36,10 +39,10 @@ describe("createScopedVitestConfig", () => { }); it("relativizes scoped include and exclude patterns to the configured dir", () => { - const config = createScopedVitestConfig(["extensions/**/*.test.ts"], { + const config = createScopedVitestConfig([BUNDLED_PLUGIN_TEST_GLOB], { dir: "extensions", env: {}, - exclude: ["extensions/channel/**", "dist/**"], + exclude: [EXTENSIONS_CHANNEL_GLOB, "dist/**"], }); expect(config.test?.include).toEqual(["**/*.test.ts"]); @@ -64,7 +67,10 @@ describe("scoped vitest configs", () => { fs.writeFileSync( includeFile, JSON.stringify([ - "extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts", + bundledPluginFile( + "discord", + "src/monitor/message-handler.preflight.acp-bindings.test.ts", + ), ]), "utf8", ); @@ -74,7 +80,7 @@ describe("scoped vitest configs", () => { }); expect(config.test?.include).toEqual([ - "extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts", + bundledPluginFile("discord", "src/monitor/message-handler.preflight.acp-bindings.test.ts"), ]); } finally { fs.rmSync(tempDir, { recursive: true, force: true }); diff --git a/test/vitest-unit-paths.test.ts b/test/vitest-unit-paths.test.ts index e9e6b360258..b25bff3b78c 100644 --- a/test/vitest-unit-paths.test.ts +++ b/test/vitest-unit-paths.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { isUnitConfigTestFile } from "../vitest.unit-paths.mjs"; +import { bundledPluginFile } from "./helpers/bundled-plugin-paths.js"; describe("isUnitConfigTestFile", () => { it("accepts unit-config src, test, and whitelisted ui tests", () => { @@ -11,7 +12,9 @@ describe("isUnitConfigTestFile", () => { it("rejects files excluded from the unit config", () => { expect( - isUnitConfigTestFile("extensions/imessage/src/monitor.shutdown.unhandled-rejection.test.ts"), + isUnitConfigTestFile( + bundledPluginFile("imessage", "src/monitor.shutdown.unhandled-rejection.test.ts"), + ), ).toBe(false); expect(isUnitConfigTestFile("src/agents/pi-embedded-runner.test.ts")).toBe(false); expect(isUnitConfigTestFile("src/commands/onboard.test.ts")).toBe(false); diff --git a/test/web-search-provider-boundary.test.ts b/test/web-search-provider-boundary.test.ts index 6aa0a1bbb8a..e114dc3f83f 100644 --- a/test/web-search-provider-boundary.test.ts +++ b/test/web-search-provider-boundary.test.ts @@ -3,6 +3,7 @@ import { collectWebSearchProviderBoundaryInventory, main, } from "../scripts/check-web-search-provider-boundaries.mjs"; +import { BUNDLED_PLUGIN_PATH_PREFIX } from "./helpers/bundled-plugin-paths.js"; import { createCapturedIo } from "./helpers/captured-io.js"; const inventoryPromise = collectWebSearchProviderBoundaryInventory(); @@ -24,7 +25,9 @@ describe("web search provider boundary inventory", () => { const jsonOutput = await jsonOutputPromise; expect(inventory).toEqual([]); - expect(inventory.some((entry) => entry.file.startsWith("extensions/"))).toBe(false); + expect(inventory.some((entry) => entry.file.startsWith(BUNDLED_PLUGIN_PATH_PREFIX))).toBe( + false, + ); expect( [...inventory].toSorted( (left, right) => diff --git a/tsconfig.json b/tsconfig.json index 84aec875cee..ba6e6e186be 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,8 @@ "openclaw/extension-api": ["./src/extensionAPI.ts"], "openclaw/plugin-sdk": ["./src/plugin-sdk/index.ts"], "openclaw/plugin-sdk/*": ["./src/plugin-sdk/*.ts"], - "openclaw/plugin-sdk/account-id": ["./src/plugin-sdk/account-id.ts"] + "openclaw/plugin-sdk/account-id": ["./src/plugin-sdk/account-id.ts"], + "openclaw/plugin-source/*": ["./extensions/*"] } }, "include": ["src/**/*", "ui/**/*", "extensions/**/*", "packages/**/*"], diff --git a/tsdown.config.ts b/tsdown.config.ts index 70e6bf41c40..c20b1cf7701 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -108,6 +108,9 @@ function buildBundledHookEntries(): Record { } const bundledHookEntries = buildBundledHookEntries(); +const bundledPluginRoot = (pluginId: string) => ["extensions", pluginId].join("/"); +const bundledPluginFile = (pluginId: string, relativePath: string) => + `${bundledPluginRoot(pluginId)}/${relativePath}`; function buildCoreDistEntries(): Record { return { @@ -121,10 +124,13 @@ function buildCoreDistEntries(): Record { "agents/pi-model-discovery-runtime": "src/agents/pi-model-discovery-runtime.ts", "commands/status.summary.runtime": "src/commands/status.summary.runtime.ts", "plugins/provider-runtime.runtime": "src/plugins/provider-runtime.runtime.ts", + "plugins/runtime/runtime-image-generation.runtime": + "src/plugins/runtime/runtime-image-generation.runtime.ts", + "plugins/runtime/runtime-line.contract": "src/plugins/runtime/runtime-line.contract.ts", extensionAPI: "src/extensionAPI.ts", "infra/warning-filter": "src/infra/warning-filter.ts", - "telegram/audit": "extensions/telegram/src/audit.ts", - "telegram/token": "extensions/telegram/src/token.ts", + "telegram/audit": bundledPluginFile("telegram", "src/audit.ts"), + "telegram/token": bundledPluginFile("telegram", "src/token.ts"), "plugins/build-smoke-entry": "src/plugins/build-smoke-entry.ts", "plugins/runtime/index": "src/plugins/runtime/index.ts", "llm-slug-generator": "src/hooks/llm-slug-generator.ts", diff --git a/vitest.config.ts b/vitest.config.ts index a1282906bc2..edcf49fa269 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,6 +1,10 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import { defineConfig } from "vitest/config"; +import { + BUNDLED_PLUGIN_ROOT_DIR, + BUNDLED_PLUGIN_TEST_GLOB, +} from "./scripts/lib/bundled-plugin-paths.mjs"; import { pluginSdkSubpaths } from "./scripts/lib/plugin-sdk-entries.mjs"; import { resolveLocalVitestMaxWorkers } from "./scripts/test-planner/runtime-profile.mjs"; import { @@ -73,7 +77,7 @@ export default defineConfig({ ], include: [ "src/**/*.test.ts", - "extensions/**/*.test.ts", + BUNDLED_PLUGIN_TEST_GLOB, "packages/**/*.test.ts", "test/**/*.test.ts", "ui/src/ui/app-chat.test.ts", @@ -119,7 +123,7 @@ export default defineConfig({ include: ["./src/**/*.ts"], exclude: [ // Never count workspace packages/apps toward core coverage thresholds. - "extensions/**", + `${BUNDLED_PLUGIN_ROOT_DIR}/**`, "apps/**", "ui/**", "test/**", diff --git a/vitest.e2e.config.ts b/vitest.e2e.config.ts index c9656cc2936..9887e22eeaa 100644 --- a/vitest.e2e.config.ts +++ b/vitest.e2e.config.ts @@ -1,5 +1,6 @@ import os from "node:os"; import { defineConfig } from "vitest/config"; +import { BUNDLED_PLUGIN_E2E_TEST_GLOB } from "./scripts/lib/bundled-plugin-paths.mjs"; import baseConfig from "./vitest.config.ts"; const base = baseConfig as unknown as Record; @@ -25,7 +26,7 @@ export default defineConfig({ pool: "forks", maxWorkers: e2eWorkers, silent: !verboseE2E, - include: ["test/**/*.e2e.test.ts", "src/**/*.e2e.test.ts", "extensions/**/*.e2e.test.ts"], + include: ["test/**/*.e2e.test.ts", "src/**/*.e2e.test.ts", BUNDLED_PLUGIN_E2E_TEST_GLOB], exclude, }, }); diff --git a/vitest.extensions.config.ts b/vitest.extensions.config.ts index 28b9cbed691..c5ec6238887 100644 --- a/vitest.extensions.config.ts +++ b/vitest.extensions.config.ts @@ -1,3 +1,7 @@ +import { + BUNDLED_PLUGIN_PATH_PREFIX, + BUNDLED_PLUGIN_TEST_GLOB, +} from "./scripts/lib/bundled-plugin-paths.mjs"; import { channelTestExclude } from "./vitest.channel-paths.mjs"; import { loadPatternListFromEnv } from "./vitest.pattern-file.ts"; import { createScopedVitestConfig } from "./vitest.scoped-config.ts"; @@ -11,14 +15,14 @@ export function loadIncludePatternsFromEnv( export function createExtensionsVitestConfig( env: Record = process.env, ) { - return createScopedVitestConfig(loadIncludePatternsFromEnv(env) ?? ["extensions/**/*.test.ts"], { + return createScopedVitestConfig(loadIncludePatternsFromEnv(env) ?? [BUNDLED_PLUGIN_TEST_GLOB], { dir: "extensions", env, passWithNoTests: true, - // Channel implementations live under extensions/ but are tested by + // Channel implementations live under the bundled plugin tree but are tested by // vitest.channels.config.ts (pnpm test:channels) which provides // the heavier mock scaffolding they need. - exclude: channelTestExclude.filter((pattern) => pattern.startsWith("extensions/")), + exclude: channelTestExclude.filter((pattern) => pattern.startsWith(BUNDLED_PLUGIN_PATH_PREFIX)), }); } diff --git a/vitest.live.config.ts b/vitest.live.config.ts index b01cd8f279b..63ae4116476 100644 --- a/vitest.live.config.ts +++ b/vitest.live.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from "vitest/config"; +import { BUNDLED_PLUGIN_LIVE_TEST_GLOB } from "./scripts/lib/bundled-plugin-paths.mjs"; import baseConfig from "./vitest.config.ts"; const base = baseConfig as unknown as Record; @@ -13,7 +14,7 @@ export default defineConfig({ // Vitest's buffered per-test console capture. disableConsoleIntercept: true, maxWorkers: 1, - include: ["src/**/*.live.test.ts", "extensions/**/*.live.test.ts"], + include: ["src/**/*.live.test.ts", BUNDLED_PLUGIN_LIVE_TEST_GLOB], exclude, }, }); diff --git a/vitest.unit-paths.mjs b/vitest.unit-paths.mjs index fe5ada29866..6870d70af9d 100644 --- a/vitest.unit-paths.mjs +++ b/vitest.unit-paths.mjs @@ -1,4 +1,5 @@ import path from "node:path"; +import { BUNDLED_PLUGIN_ROOT_DIR } from "./scripts/lib/bundled-plugin-paths.mjs"; export const unitTestIncludePatterns = [ "src/**/*.test.ts", @@ -16,7 +17,7 @@ export const unitTestIncludePatterns = [ export const unitTestAdditionalExcludePatterns = [ "src/gateway/**", - "extensions/**", + `${BUNDLED_PLUGIN_ROOT_DIR}/**`, "src/browser/**", "src/line/**", "src/agents/**",