Merge branch 'main' into memory-chunking-phase1

This commit is contained in:
ly-wang19 2026-03-17 16:39:34 +08:00 committed by GitHub
commit 7068cf5b59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
209 changed files with 3022 additions and 1718 deletions

View File

@ -273,7 +273,9 @@ jobs:
use-sticky-disk: "false"
- name: Run changed extension tests
run: pnpm test:extension ${{ matrix.extension }}
env:
EXTENSION_ID: ${{ matrix.extension }}
run: pnpm test:extension "$EXTENSION_ID"
# Types, lint, and format check.
check:
@ -458,30 +460,30 @@ jobs:
run: pre-commit run --all-files detect-private-key
- name: Audit changed GitHub workflows with zizmor
env:
BASE_SHA: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
run: |
set -euo pipefail
BASE="$(
python - <<'PY'
import json
import os
if [ -z "${BASE_SHA:-}" ] || [ "${BASE_SHA}" = "0000000000000000000000000000000000000000" ]; then
echo "No usable base SHA detected; skipping zizmor."
exit 0
fi
with open(os.environ["GITHUB_EVENT_PATH"], "r", encoding="utf-8") as fh:
event = json.load(fh)
if ! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
echo "Base SHA ${BASE_SHA} is unavailable; skipping zizmor."
exit 0
fi
if os.environ["GITHUB_EVENT_NAME"] == "push":
print(event["before"])
else:
print(event["pull_request"]["base"]["sha"])
PY
)"
mapfile -t workflow_files < <(git diff --name-only "$BASE" HEAD -- '.github/workflows/*.yml' '.github/workflows/*.yaml')
mapfile -t workflow_files < <(
git diff --name-only "${BASE_SHA}" HEAD -- '.github/workflows/*.yml' '.github/workflows/*.yaml'
)
if [ "${#workflow_files[@]}" -eq 0 ]; then
echo "No workflow changes detected; skipping zizmor."
exit 0
fi
printf 'Auditing workflow files:\n%s\n' "${workflow_files[@]}"
pre-commit run zizmor --files "${workflow_files[@]}"
- name: Audit production dependencies

View File

@ -34,11 +34,13 @@ Docs: https://docs.openclaw.ai
- Skills/prompt budget: preserve all registered skills via a compact catalog fallback before dropping entries when the full prompt format exceeds `maxSkillsPromptChars`. (#47553) Thanks @snese.
- Plugins/bundles: make enabled bundle MCP servers expose runnable tools in embedded Pi, and default relative bundle MCP launches to the bundle root so marketplace bundles like Context7 work through Pi instead of stopping at config import.
- Scope message SecretRef resolution and harden doctor/status paths. (#48728) Thanks @joshavant.
- Plugins/testing: add a public `openclaw/plugin-sdk/testing` seam for plugin-author test helpers, and move bundled-extension-only test bridges out of `extensions/` into private repo test helpers.
### Breaking
- Browser/Chrome MCP: remove the legacy Chrome extension relay path, bundled extension assets, `driver: "extension"`, and `browser.relayBindHost`. Run `openclaw doctor --fix` to migrate host-local browser config to `existing-session` / `user`; Docker, headless, sandbox, and remote browser flows still use raw CDP. (#47893) Thanks @vincentkoc.
- Plugins/runtime: remove the public `openclaw/extension-api` surface with no compatibility shim. Bundled plugins must use injected runtime for host-side operations (for example `api.runtime.agent.runEmbeddedPiAgent`) and any remaining direct imports must come from narrow `openclaw/plugin-sdk/*` subpaths instead of the monolithic SDK root.
- Tools/image generation: standardize the stock image create/edit path on the core `image_generate` tool. The old `nano-banana-pro` docs/examples are gone; if you previously copied that sample-skill config, switch to `agents.defaults.imageGenerationModel` for built-in image generation or install a separate third-party skill explicitly.
### Fixes
@ -119,6 +121,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Agents/bootstrap warnings: move bootstrap truncation warnings out of the system prompt and into the per-turn prompt body so prompt-cache reuse stays stable when truncation warnings appear or disappear. (#48753) Thanks @scoootscooob and @obviyus.
- Telegram/DM topic session keys: route named-account DM topics through the same per-account base session key across inbound messages, native commands, and session-state lookups so `/status` and thread recovery stop creating phantom `agent:main:main:thread:...` sessions. (#48204) Thanks @vincentkoc.
## 2026.3.13

View File

@ -434,7 +434,7 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
nodeManager: "npm",
},
entries: {
"nano-banana-pro": {
"image-lab": {
enabled: true,
apiKey: "GEMINI_KEY_HERE",
env: { GEMINI_API_KEY: "GEMINI_KEY_HERE" },

View File

@ -2371,7 +2371,7 @@ See [Local Models](/gateway/local-models). TL;DR: run MiniMax M2.5 via LM Studio
nodeManager: "npm", // npm | pnpm | yarn
},
entries: {
"nano-banana-pro": {
"image-lab": {
apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY" }, // or plaintext string
env: { GEMINI_API_KEY: "GEMINI_KEY_HERE" },
},

View File

@ -597,11 +597,11 @@ Rules:
},
skills: {
entries: {
"nano-banana-pro": {
"image-lab": {
apiKey: {
source: "file",
provider: "filemain",
id: "/skills/entries/nano-banana-pro/apiKey",
id: "/skills/entries/image-lab/apiKey",
},
},
},

View File

@ -360,14 +360,29 @@ If you want to rely on env keys (e.g. exported in your `~/.profile`), run local
- Enable: `BYTEPLUS_API_KEY=... BYTEPLUS_LIVE_TEST=1 pnpm test:live src/agents/byteplus.live.test.ts`
- Optional model override: `BYTEPLUS_CODING_MODEL=ark-code-latest`
## Google image generation live
## Image generation live
- Test: `src/image-generation/providers/google.live.test.ts`
- Enable: `GOOGLE_LIVE_TEST=1 pnpm test:live src/image-generation/providers/google.live.test.ts`
- Key source: `GEMINI_API_KEY` or `GOOGLE_API_KEY`
- Optional overrides:
- `GOOGLE_IMAGE_GENERATION_MODEL=gemini-3.1-flash-image-preview`
- `GOOGLE_IMAGE_BASE_URL=https://generativelanguage.googleapis.com/v1beta`
- Test: `src/image-generation/runtime.live.test.ts`
- Command: `pnpm test:live src/image-generation/runtime.live.test.ts`
- Scope:
- Enumerates every registered image-generation provider plugin
- Loads missing provider env vars from your login shell (`~/.profile`) before probing
- Uses live/env API keys ahead of stored auth profiles by default, so stale test keys in `auth-profiles.json` do not mask real shell credentials
- Skips providers with no usable auth/profile/model
- Runs the stock image-generation variants through the shared runtime capability:
- `google:flash-generate`
- `google:pro-generate`
- `google:pro-edit`
- `openai:default-generate`
- Current bundled providers covered:
- `openai`
- `google`
- Optional narrowing:
- `OPENCLAW_LIVE_IMAGE_GENERATION_PROVIDERS="openai,google"`
- `OPENCLAW_LIVE_IMAGE_GENERATION_MODELS="openai/gpt-image-1,google/gemini-3.1-flash-image-preview"`
- `OPENCLAW_LIVE_IMAGE_GENERATION_CASES="google:flash-generate,google:pro-edit"`
- Optional auth behavior:
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` to force profile-store auth and ignore env-only overrides
## Docker runners (optional “works in Linux” checks)

View File

@ -400,6 +400,30 @@ Notes:
- Only available when `agents.defaults.imageModel` is configured (primary or fallbacks), or when an implicit image model can be inferred from your default model + configured auth (best-effort pairing).
- Uses the image model directly (independent of the main chat model).
### `image_generate`
Generate one or more images with the configured image-generation model.
Core parameters:
- `action` (optional: `generate` or `list`; default `generate`)
- `prompt` (required)
- `image` or `images` (optional reference image path/URL for edit mode)
- `model` (optional provider/model override)
- `size` (optional size hint)
- `resolution` (optional `1K|2K|4K` hint)
- `count` (optional, `1-4`, default `1`)
Notes:
- Only available when `agents.defaults.imageGenerationModel` is configured.
- Use `action: "list"` to inspect registered providers, default models, supported model ids, sizes, resolutions, and edit support.
- Returns local `MEDIA:<path>` lines so channels can deliver the generated files directly.
- Uses the image-generation model directly (independent of the main chat model).
- Google-backed flows support reference-image edits plus explicit `1K|2K|4K` resolution hints.
- When editing and `resolution` is omitted, OpenClaw infers a draft/final resolution from the input image size.
- This is the built-in replacement for the old sample `nano-banana-pro` skill workflow. Use `agents.defaults.imageGenerationModel`, not `skills.entries`, for stock image generation.
### `pdf`
Analyze one or more PDF documents.

View File

@ -24,7 +24,7 @@ All skills-related configuration lives under `skills` in `~/.openclaw/openclaw.j
nodeManager: "npm", // npm | pnpm | yarn | bun (Gateway runtime still Node; bun not recommended)
},
entries: {
"nano-banana-pro": {
"image-lab": {
enabled: true,
apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY" }, // or plaintext string
env: {
@ -38,6 +38,10 @@ All skills-related configuration lives under `skills` in `~/.openclaw/openclaw.j
}
```
For built-in image generation/editing, prefer `agents.defaults.imageGenerationModel`
plus the core `image_generate` tool. `skills.entries.*` is only for custom or
third-party skill workflows.
## Fields
- `allowBundled`: optional allowlist for **bundled** skills only. When set, only

View File

@ -81,8 +81,8 @@ that up as `<workspace>/skills` on the next session.
```markdown
---
name: nano-banana-pro
description: Generate or edit images via Gemini 3 Pro Image
name: image-lab
description: Generate or edit images via a provider-backed image workflow
---
```
@ -109,8 +109,8 @@ OpenClaw **filters skills at load time** using `metadata` (single-line JSON):
```markdown
---
name: nano-banana-pro
description: Generate or edit images via Gemini 3 Pro Image
name: image-lab
description: Generate or edit images via a provider-backed image workflow
metadata:
{
"openclaw":
@ -194,7 +194,7 @@ Bundled/managed skills can be toggled and supplied with env values:
{
skills: {
entries: {
"nano-banana-pro": {
"image-lab": {
enabled: true,
apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY" }, // or plaintext string
env: {
@ -214,6 +214,10 @@ Bundled/managed skills can be toggled and supplied with env values:
Note: if the skill name contains hyphens, quote the key (JSON5 allows quoted keys).
If you want stock image generation/editing inside OpenClaw itself, use the core
`image_generate` tool with `agents.defaults.imageGenerationModel` instead of a
bundled skill. Skill examples here are for custom or third-party workflows.
Config keys match the **skill name** by default. If a skill defines
`metadata.openclaw.skillKey`, use that key under `skills.entries`.

View File

@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { registerSingleProviderPlugin } from "../test-utils/plugin-registration.js";
import { registerSingleProviderPlugin } from "../../test/helpers/extensions/plugin-registration.js";
import amazonBedrockPlugin from "./index.js";
describe("amazon-bedrock provider plugin", () => {

View File

@ -15,87 +15,17 @@ import {
sendMessageBlueBubbles as sendMessageBlueBubblesImpl,
} from "./send.js";
type SendBlueBubblesAttachment = typeof import("./attachments.js").sendBlueBubblesAttachment;
type AddBlueBubblesParticipant = typeof import("./chat.js").addBlueBubblesParticipant;
type EditBlueBubblesMessage = typeof import("./chat.js").editBlueBubblesMessage;
type LeaveBlueBubblesChat = typeof import("./chat.js").leaveBlueBubblesChat;
type RemoveBlueBubblesParticipant = typeof import("./chat.js").removeBlueBubblesParticipant;
type RenameBlueBubblesChat = typeof import("./chat.js").renameBlueBubblesChat;
type SetGroupIconBlueBubbles = typeof import("./chat.js").setGroupIconBlueBubbles;
type UnsendBlueBubblesMessage = typeof import("./chat.js").unsendBlueBubblesMessage;
type ResolveBlueBubblesMessageId = typeof import("./monitor.js").resolveBlueBubblesMessageId;
type SendBlueBubblesReaction = typeof import("./reactions.js").sendBlueBubblesReaction;
type ResolveChatGuidForTarget = typeof import("./send.js").resolveChatGuidForTarget;
type SendMessageBlueBubbles = typeof import("./send.js").sendMessageBlueBubbles;
export function sendBlueBubblesAttachment(
...args: Parameters<SendBlueBubblesAttachment>
): ReturnType<SendBlueBubblesAttachment> {
return sendBlueBubblesAttachmentImpl(...args);
}
export function addBlueBubblesParticipant(
...args: Parameters<AddBlueBubblesParticipant>
): ReturnType<AddBlueBubblesParticipant> {
return addBlueBubblesParticipantImpl(...args);
}
export function editBlueBubblesMessage(
...args: Parameters<EditBlueBubblesMessage>
): ReturnType<EditBlueBubblesMessage> {
return editBlueBubblesMessageImpl(...args);
}
export function leaveBlueBubblesChat(
...args: Parameters<LeaveBlueBubblesChat>
): ReturnType<LeaveBlueBubblesChat> {
return leaveBlueBubblesChatImpl(...args);
}
export function removeBlueBubblesParticipant(
...args: Parameters<RemoveBlueBubblesParticipant>
): ReturnType<RemoveBlueBubblesParticipant> {
return removeBlueBubblesParticipantImpl(...args);
}
export function renameBlueBubblesChat(
...args: Parameters<RenameBlueBubblesChat>
): ReturnType<RenameBlueBubblesChat> {
return renameBlueBubblesChatImpl(...args);
}
export function setGroupIconBlueBubbles(
...args: Parameters<SetGroupIconBlueBubbles>
): ReturnType<SetGroupIconBlueBubbles> {
return setGroupIconBlueBubblesImpl(...args);
}
export function unsendBlueBubblesMessage(
...args: Parameters<UnsendBlueBubblesMessage>
): ReturnType<UnsendBlueBubblesMessage> {
return unsendBlueBubblesMessageImpl(...args);
}
export function resolveBlueBubblesMessageId(
...args: Parameters<ResolveBlueBubblesMessageId>
): ReturnType<ResolveBlueBubblesMessageId> {
return resolveBlueBubblesMessageIdImpl(...args);
}
export function sendBlueBubblesReaction(
...args: Parameters<SendBlueBubblesReaction>
): ReturnType<SendBlueBubblesReaction> {
return sendBlueBubblesReactionImpl(...args);
}
export function resolveChatGuidForTarget(
...args: Parameters<ResolveChatGuidForTarget>
): ReturnType<ResolveChatGuidForTarget> {
return resolveChatGuidForTargetImpl(...args);
}
export function sendMessageBlueBubbles(
...args: Parameters<SendMessageBlueBubbles>
): ReturnType<SendMessageBlueBubbles> {
return sendMessageBlueBubblesImpl(...args);
}
export const blueBubblesActionsRuntime = {
sendBlueBubblesAttachment: sendBlueBubblesAttachmentImpl,
addBlueBubblesParticipant: addBlueBubblesParticipantImpl,
editBlueBubblesMessage: editBlueBubblesMessageImpl,
leaveBlueBubblesChat: leaveBlueBubblesChatImpl,
removeBlueBubblesParticipant: removeBlueBubblesParticipantImpl,
renameBlueBubblesChat: renameBlueBubblesChatImpl,
setGroupIconBlueBubbles: setGroupIconBlueBubblesImpl,
unsendBlueBubblesMessage: unsendBlueBubblesMessageImpl,
resolveBlueBubblesMessageId: resolveBlueBubblesMessageIdImpl,
sendBlueBubblesReaction: sendBlueBubblesReactionImpl,
resolveChatGuidForTarget: resolveChatGuidForTargetImpl,
sendMessageBlueBubbles: sendMessageBlueBubblesImpl,
};

View File

@ -11,18 +11,19 @@ import {
type ChannelMessageActionAdapter,
type ChannelMessageActionName,
} from "openclaw/plugin-sdk/bluebubbles";
import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js";
import { resolveBlueBubblesAccount } from "./accounts.js";
import { getCachedBlueBubblesPrivateApiStatus, isMacOS26OrHigher } from "./probe.js";
import { normalizeSecretInputString } from "./secret-input.js";
import { normalizeBlueBubblesHandle, parseBlueBubblesTarget } from "./targets.js";
import type { BlueBubblesSendTarget } from "./types.js";
let actionsRuntimePromise: Promise<typeof import("./actions.runtime.js")> | null = null;
type BlueBubblesActionsRuntime = typeof import("./actions.runtime.js").blueBubblesActionsRuntime;
function loadBlueBubblesActionsRuntime() {
actionsRuntimePromise ??= import("./actions.runtime.js");
return actionsRuntimePromise;
}
const loadBlueBubblesActionsRuntime = createLazyRuntimeSurface(
() => import("./actions.runtime.js"),
({ blueBubblesActionsRuntime }) => blueBubblesActionsRuntime,
);
const providerId = "bluebubbles";

View File

@ -6,52 +6,14 @@ import {
} from "./monitor.js";
import { probeBlueBubbles as probeBlueBubblesImpl } from "./probe.js";
import { sendMessageBlueBubbles as sendMessageBlueBubblesImpl } from "./send.js";
import { blueBubblesSetupWizard as blueBubblesSetupWizardImpl } from "./setup-surface.js";
export type { BlueBubblesProbe } from "./probe.js";
type SendBlueBubblesMedia = typeof import("./media-send.js").sendBlueBubblesMedia;
type ResolveBlueBubblesMessageId = typeof import("./monitor.js").resolveBlueBubblesMessageId;
type MonitorBlueBubblesProvider = typeof import("./monitor.js").monitorBlueBubblesProvider;
type ResolveWebhookPathFromConfig = typeof import("./monitor.js").resolveWebhookPathFromConfig;
type ProbeBlueBubbles = typeof import("./probe.js").probeBlueBubbles;
type SendMessageBlueBubbles = typeof import("./send.js").sendMessageBlueBubbles;
type BlueBubblesSetupWizard = typeof import("./setup-surface.js").blueBubblesSetupWizard;
export function sendBlueBubblesMedia(
...args: Parameters<SendBlueBubblesMedia>
): ReturnType<SendBlueBubblesMedia> {
return sendBlueBubblesMediaImpl(...args);
}
export function resolveBlueBubblesMessageId(
...args: Parameters<ResolveBlueBubblesMessageId>
): ReturnType<ResolveBlueBubblesMessageId> {
return resolveBlueBubblesMessageIdImpl(...args);
}
export function monitorBlueBubblesProvider(
...args: Parameters<MonitorBlueBubblesProvider>
): ReturnType<MonitorBlueBubblesProvider> {
return monitorBlueBubblesProviderImpl(...args);
}
export function resolveWebhookPathFromConfig(
...args: Parameters<ResolveWebhookPathFromConfig>
): ReturnType<ResolveWebhookPathFromConfig> {
return resolveWebhookPathFromConfigImpl(...args);
}
export function probeBlueBubbles(
...args: Parameters<ProbeBlueBubbles>
): ReturnType<ProbeBlueBubbles> {
return probeBlueBubblesImpl(...args);
}
export function sendMessageBlueBubbles(
...args: Parameters<SendMessageBlueBubbles>
): ReturnType<SendMessageBlueBubbles> {
return sendMessageBlueBubblesImpl(...args);
}
export const blueBubblesSetupWizard: BlueBubblesSetupWizard = { ...blueBubblesSetupWizardImpl };
export const blueBubblesChannelRuntime = {
sendBlueBubblesMedia: sendBlueBubblesMediaImpl,
resolveBlueBubblesMessageId: resolveBlueBubblesMessageIdImpl,
monitorBlueBubblesProvider: monitorBlueBubblesProviderImpl,
resolveWebhookPathFromConfig: resolveWebhookPathFromConfigImpl,
probeBlueBubbles: probeBlueBubblesImpl,
sendMessageBlueBubbles: sendMessageBlueBubblesImpl,
};

View File

@ -18,6 +18,7 @@ import {
buildAccountScopedDmSecurityPolicy,
collectOpenGroupPolicyRestrictSendersWarnings,
} from "openclaw/plugin-sdk/channel-policy";
import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js";
import {
listBlueBubblesAccountIds,
type ResolvedBlueBubblesAccount,
@ -37,12 +38,12 @@ import {
parseBlueBubblesTarget,
} from "./targets.js";
let blueBubblesChannelRuntimePromise: Promise<typeof import("./channel.runtime.js")> | null = null;
type BlueBubblesChannelRuntime = typeof import("./channel.runtime.js").blueBubblesChannelRuntime;
function loadBlueBubblesChannelRuntime() {
blueBubblesChannelRuntimePromise ??= import("./channel.runtime.js");
return blueBubblesChannelRuntimePromise;
}
const loadBlueBubblesChannelRuntime = createLazyRuntimeSurface(
() => import("./channel.runtime.js"),
({ blueBubblesChannelRuntime }) => blueBubblesChannelRuntime,
);
const meta = {
id: "bluebubbles",

View File

@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
import type { IncomingMessage, ServerResponse } from "node:http";
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/bluebubbles";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
import { fetchBlueBubblesHistory } from "./history.js";
import { resetBlueBubblesSelfChatCache } from "./monitor-self-chat-cache.js";

View File

@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
import type { IncomingMessage, ServerResponse } from "node:http";
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/bluebubbles";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
import { fetchBlueBubblesHistory } from "./history.js";
import {

View File

@ -1,8 +1,8 @@
import type { IncomingMessage } from "node:http";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/diffs";
import { describe, expect, it, vi } from "vitest";
import { createMockServerResponse } from "../test-utils/mock-http-response.js";
import { createTestPluginApi } from "../test-utils/plugin-api.js";
import { createMockServerResponse } from "../../test/helpers/extensions/mock-http-response.js";
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
import plugin from "./index.js";
describe("diffs plugin registration", () => {

View File

@ -1,6 +1,6 @@
import type { IncomingMessage } from "node:http";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { createMockServerResponse } from "../../test-utils/mock-http-response.js";
import { createMockServerResponse } from "../../../test/helpers/extensions/mock-http-response.js";
import { createDiffsHttpHandler } from "./http.js";
import { DiffArtifactStore } from "./store.js";
import { createDiffStoreHarness } from "./test-helpers.js";

View File

@ -2,7 +2,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/diffs";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../../test-utils/plugin-api.js";
import { createTestPluginApi } from "../../../test/helpers/extensions/plugin-api.js";
import type { DiffScreenshotter } from "./browser.js";
import { DEFAULT_DIFFS_TOOL_DEFAULTS } from "./config.js";
import { DiffArtifactStore } from "./store.js";

View File

@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { fetchDiscord } from "./api.js";
import { jsonResponse } from "./test-http-helpers.js";

View File

@ -1,5 +1,8 @@
import { describe, expect, it } from "vitest";
import { countLines, hasBalancedFences } from "../../test-utils/chunk-test-helpers.js";
import {
countLines,
hasBalancedFences,
} from "../../../test/helpers/extensions/chunk-test-helpers.js";
import { chunkDiscordText, chunkDiscordTextWithMode } from "./chunk.js";
describe("chunkDiscordText", () => {

View File

@ -1,6 +1,6 @@
import { ChannelType, type Guild } from "@buape/carbon";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { typedCases } from "../../test-utils/typed-cases.js";
import { typedCases } from "../../../test/helpers/extensions/typed-cases.js";
import {
allowListMatches,
buildDiscordMediaPayload,

View File

@ -1,4 +1,4 @@
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
export const sendMock: MockFn = vi.fn();

View File

@ -1,4 +1,4 @@
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
export const preflightDiscordMessageMock: MockFn = vi.fn();

View File

@ -5,7 +5,7 @@ import {
baseRuntime,
getProviderMonitorTestMocks,
resetDiscordProviderMonitorMocks,
} from "./provider.test-support.js";
} from "../../../../test/helpers/extensions/discord-provider.test-support.js";
const { createDiscordNativeCommandMock, clientHandleDeployRequestMock, monitorLifecycleMock } =
getProviderMonitorTestMocks();

View File

@ -1,459 +1 @@
import type { Mock } from "vitest";
import { expect, vi } from "vitest";
import type { OpenClawConfig } from "../../../../src/config/config.js";
import type { RuntimeEnv } from "../../../../src/runtime.js";
export type NativeCommandSpecMock = {
name: string;
description: string;
acceptsArgs: boolean;
};
export type PluginCommandSpecMock = {
name: string;
description: string;
acceptsArgs: boolean;
};
type ProviderMonitorTestMocks = {
clientHandleDeployRequestMock: Mock<() => Promise<void>>;
clientFetchUserMock: Mock<(target: string) => Promise<{ id: string }>>;
clientGetPluginMock: Mock<(name: string) => unknown>;
clientConstructorOptionsMock: Mock<(options?: unknown) => void>;
createDiscordAutoPresenceControllerMock: Mock<() => unknown>;
createDiscordNativeCommandMock: Mock<(params?: { command?: { name?: string } }) => unknown>;
createDiscordMessageHandlerMock: Mock<() => unknown>;
createNoopThreadBindingManagerMock: Mock<() => { stop: ReturnType<typeof vi.fn> }>;
createThreadBindingManagerMock: Mock<() => { stop: ReturnType<typeof vi.fn> }>;
reconcileAcpThreadBindingsOnStartupMock: Mock<() => unknown>;
createdBindingManagers: Array<{ stop: ReturnType<typeof vi.fn> }>;
getAcpSessionStatusMock: Mock<
(params: {
cfg: OpenClawConfig;
sessionKey: string;
signal?: AbortSignal;
}) => Promise<{ state: string }>
>;
getPluginCommandSpecsMock: Mock<() => PluginCommandSpecMock[]>;
listNativeCommandSpecsForConfigMock: Mock<() => NativeCommandSpecMock[]>;
listSkillCommandsForAgentsMock: Mock<() => unknown[]>;
monitorLifecycleMock: Mock<(params: { threadBindings: { stop: () => void } }) => Promise<void>>;
resolveDiscordAccountMock: Mock<() => unknown>;
resolveDiscordAllowlistConfigMock: Mock<() => Promise<unknown>>;
resolveNativeCommandsEnabledMock: Mock<() => boolean>;
resolveNativeSkillsEnabledMock: Mock<() => boolean>;
isVerboseMock: Mock<() => boolean>;
shouldLogVerboseMock: Mock<() => boolean>;
voiceRuntimeModuleLoadedMock: Mock<() => void>;
};
export function baseDiscordAccountConfig() {
return {
commands: { native: true, nativeSkills: false },
voice: { enabled: false },
agentComponents: { enabled: false },
execApprovals: { enabled: false },
};
}
const providerMonitorTestMocks: ProviderMonitorTestMocks = vi.hoisted(() => {
const createdBindingManagers: Array<{ stop: ReturnType<typeof vi.fn> }> = [];
const isVerboseMock = vi.fn(() => false);
const shouldLogVerboseMock = vi.fn(() => false);
return {
clientHandleDeployRequestMock: vi.fn(async () => undefined),
clientFetchUserMock: vi.fn(async (_target: string) => ({ id: "bot-1" })),
clientGetPluginMock: vi.fn<(_name: string) => unknown>(() => undefined),
clientConstructorOptionsMock: vi.fn(),
createDiscordAutoPresenceControllerMock: vi.fn(() => ({
enabled: false,
start: vi.fn(),
stop: vi.fn(),
refresh: vi.fn(),
runNow: vi.fn(),
})),
createDiscordNativeCommandMock: vi.fn((params?: { command?: { name?: string } }) => ({
name: params?.command?.name ?? "mock-command",
})),
createDiscordMessageHandlerMock: vi.fn(() =>
Object.assign(
vi.fn(async () => undefined),
{
deactivate: vi.fn(),
},
),
),
createNoopThreadBindingManagerMock: vi.fn(() => {
const manager = { stop: vi.fn() };
createdBindingManagers.push(manager);
return manager;
}),
createThreadBindingManagerMock: vi.fn(() => {
const manager = { stop: vi.fn() };
createdBindingManagers.push(manager);
return manager;
}),
reconcileAcpThreadBindingsOnStartupMock: vi.fn(() => ({
checked: 0,
removed: 0,
staleSessionKeys: [],
})),
createdBindingManagers,
getAcpSessionStatusMock: vi.fn(
async (_params: { cfg: OpenClawConfig; sessionKey: string; signal?: AbortSignal }) => ({
state: "idle",
}),
),
getPluginCommandSpecsMock: vi.fn<() => PluginCommandSpecMock[]>(() => []),
listNativeCommandSpecsForConfigMock: vi.fn<() => NativeCommandSpecMock[]>(() => [
{ name: "cmd", description: "built-in", acceptsArgs: false },
]),
listSkillCommandsForAgentsMock: vi.fn(() => []),
monitorLifecycleMock: vi.fn(async (params: { threadBindings: { stop: () => void } }) => {
params.threadBindings.stop();
}),
resolveDiscordAccountMock: vi.fn(() => ({
accountId: "default",
token: "cfg-token",
config: baseDiscordAccountConfig(),
})),
resolveDiscordAllowlistConfigMock: vi.fn(async () => ({
guildEntries: undefined,
allowFrom: undefined,
})),
resolveNativeCommandsEnabledMock: vi.fn(() => true),
resolveNativeSkillsEnabledMock: vi.fn(() => false),
isVerboseMock,
shouldLogVerboseMock,
voiceRuntimeModuleLoadedMock: vi.fn(),
};
});
const {
clientHandleDeployRequestMock,
clientFetchUserMock,
clientGetPluginMock,
clientConstructorOptionsMock,
createDiscordAutoPresenceControllerMock,
createDiscordNativeCommandMock,
createDiscordMessageHandlerMock,
createNoopThreadBindingManagerMock,
createThreadBindingManagerMock,
reconcileAcpThreadBindingsOnStartupMock,
createdBindingManagers,
getAcpSessionStatusMock,
getPluginCommandSpecsMock,
listNativeCommandSpecsForConfigMock,
listSkillCommandsForAgentsMock,
monitorLifecycleMock,
resolveDiscordAccountMock,
resolveDiscordAllowlistConfigMock,
resolveNativeCommandsEnabledMock,
resolveNativeSkillsEnabledMock,
isVerboseMock,
shouldLogVerboseMock,
voiceRuntimeModuleLoadedMock,
} = providerMonitorTestMocks;
export function getProviderMonitorTestMocks(): typeof providerMonitorTestMocks {
return providerMonitorTestMocks;
}
export function mockResolvedDiscordAccountConfig(overrides: Record<string, unknown>) {
resolveDiscordAccountMock.mockImplementation(() => ({
accountId: "default",
token: "cfg-token",
config: {
...baseDiscordAccountConfig(),
...overrides,
},
}));
}
export function getFirstDiscordMessageHandlerParams<T extends object>() {
expect(createDiscordMessageHandlerMock).toHaveBeenCalledTimes(1);
const firstCall = createDiscordMessageHandlerMock.mock.calls.at(0) as [T] | undefined;
return firstCall?.[0];
}
export function resetDiscordProviderMonitorMocks(params?: {
nativeCommands?: NativeCommandSpecMock[];
}) {
clientHandleDeployRequestMock.mockClear().mockResolvedValue(undefined);
clientFetchUserMock.mockClear().mockResolvedValue({ id: "bot-1" });
clientGetPluginMock.mockClear().mockReturnValue(undefined);
clientConstructorOptionsMock.mockClear();
createDiscordAutoPresenceControllerMock.mockClear().mockImplementation(() => ({
enabled: false,
start: vi.fn(),
stop: vi.fn(),
refresh: vi.fn(),
runNow: vi.fn(),
}));
createDiscordNativeCommandMock.mockClear().mockImplementation((input) => ({
name: input?.command?.name ?? "mock-command",
}));
createDiscordMessageHandlerMock.mockClear().mockImplementation(() =>
Object.assign(
vi.fn(async () => undefined),
{
deactivate: vi.fn(),
},
),
);
createNoopThreadBindingManagerMock.mockClear();
createThreadBindingManagerMock.mockClear();
reconcileAcpThreadBindingsOnStartupMock.mockClear().mockReturnValue({
checked: 0,
removed: 0,
staleSessionKeys: [],
});
createdBindingManagers.length = 0;
getAcpSessionStatusMock.mockClear().mockResolvedValue({ state: "idle" });
getPluginCommandSpecsMock.mockClear().mockReturnValue([]);
listNativeCommandSpecsForConfigMock
.mockClear()
.mockReturnValue(
params?.nativeCommands ?? [{ name: "cmd", description: "built-in", acceptsArgs: false }],
);
listSkillCommandsForAgentsMock.mockClear().mockReturnValue([]);
monitorLifecycleMock.mockClear().mockImplementation(async (monitorParams) => {
monitorParams.threadBindings.stop();
});
resolveDiscordAccountMock.mockClear().mockReturnValue({
accountId: "default",
token: "cfg-token",
config: baseDiscordAccountConfig(),
});
resolveDiscordAllowlistConfigMock.mockClear().mockResolvedValue({
guildEntries: undefined,
allowFrom: undefined,
});
resolveNativeCommandsEnabledMock.mockClear().mockReturnValue(true);
resolveNativeSkillsEnabledMock.mockClear().mockReturnValue(false);
isVerboseMock.mockClear().mockReturnValue(false);
shouldLogVerboseMock.mockClear().mockReturnValue(false);
voiceRuntimeModuleLoadedMock.mockClear();
}
export const baseRuntime = (): RuntimeEnv => ({
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
});
export const baseConfig = (): OpenClawConfig =>
({
channels: {
discord: {
accounts: {
default: {},
},
},
},
}) as OpenClawConfig;
vi.mock("@buape/carbon", () => {
class ReadyListener {}
class RateLimitError extends Error {
status = 429;
discordCode?: number;
retryAfter: number;
scope: string | null;
bucket: string | null;
constructor(
response: Response,
body: { message: string; retry_after: number; global: boolean },
) {
super(body.message);
this.retryAfter = body.retry_after;
this.scope = body.global ? "global" : response.headers.get("X-RateLimit-Scope");
this.bucket = response.headers.get("X-RateLimit-Bucket");
}
}
class Client {
listeners: unknown[];
rest: { put: ReturnType<typeof vi.fn> };
options: unknown;
constructor(options: unknown, handlers: { listeners?: unknown[] }) {
this.options = options;
this.listeners = handlers.listeners ?? [];
this.rest = { put: vi.fn(async () => undefined) };
clientConstructorOptionsMock(options);
}
async handleDeployRequest() {
return await clientHandleDeployRequestMock();
}
async fetchUser(target: string) {
return await clientFetchUserMock(target);
}
getPlugin(name: string) {
return clientGetPluginMock(name);
}
}
return { Client, RateLimitError, ReadyListener };
});
vi.mock("@buape/carbon/gateway", () => ({
GatewayCloseCodes: { DisallowedIntents: 4014 },
}));
vi.mock("@buape/carbon/voice", () => ({
VoicePlugin: class VoicePlugin {},
}));
vi.mock("../../../../src/acp/control-plane/manager.js", () => ({
getAcpSessionManager: () => ({
getSessionStatus: getAcpSessionStatusMock,
}),
}));
vi.mock("../../../../src/auto-reply/chunk.js", () => ({
resolveTextChunkLimit: () => 2000,
}));
vi.mock("../../../../src/auto-reply/commands-registry.js", () => ({
listNativeCommandSpecsForConfig: listNativeCommandSpecsForConfigMock,
}));
vi.mock("../../../../src/auto-reply/skill-commands.js", () => ({
listSkillCommandsForAgents: listSkillCommandsForAgentsMock,
}));
vi.mock("../../../../src/config/commands.js", () => ({
isNativeCommandsExplicitlyDisabled: () => false,
resolveNativeCommandsEnabled: resolveNativeCommandsEnabledMock,
resolveNativeSkillsEnabled: resolveNativeSkillsEnabledMock,
}));
vi.mock("../../../../src/config/config.js", () => ({
loadConfig: () => ({}),
}));
vi.mock("../../../../src/globals.js", () => ({
danger: (value: string) => value,
isVerbose: isVerboseMock,
logVerbose: vi.fn(),
shouldLogVerbose: shouldLogVerboseMock,
warn: (value: string) => value,
}));
vi.mock("../../../../src/infra/errors.js", () => ({
formatErrorMessage: (error: unknown) => String(error),
}));
vi.mock("../../../../src/infra/retry-policy.js", () => ({
createDiscordRetryRunner: () => async (run: () => Promise<unknown>) => run(),
}));
vi.mock("../../../../src/logging/subsystem.js", () => ({
createSubsystemLogger: () => {
const logger = {
child: vi.fn(() => logger),
info: vi.fn(),
error: vi.fn(),
warn: vi.fn(),
debug: vi.fn(),
};
return logger;
},
}));
vi.mock("../../../../src/runtime.js", () => ({
createNonExitingRuntime: () => ({ log: vi.fn(), error: vi.fn(), exit: vi.fn() }),
}));
vi.mock("../accounts.js", () => ({
resolveDiscordAccount: resolveDiscordAccountMock,
}));
vi.mock("../probe.js", () => ({
fetchDiscordApplicationId: async () => "app-1",
}));
vi.mock("../token.js", () => ({
normalizeDiscordToken: (value?: string) => value,
}));
vi.mock("../voice/command.js", () => ({
createDiscordVoiceCommand: () => ({ name: "voice-command" }),
}));
vi.mock("./agent-components.js", () => ({
createAgentComponentButton: () => ({ id: "btn" }),
createAgentSelectMenu: () => ({ id: "menu" }),
createDiscordComponentButton: () => ({ id: "btn2" }),
createDiscordComponentChannelSelect: () => ({ id: "channel" }),
createDiscordComponentMentionableSelect: () => ({ id: "mentionable" }),
createDiscordComponentModal: () => ({ id: "modal" }),
createDiscordComponentRoleSelect: () => ({ id: "role" }),
createDiscordComponentStringSelect: () => ({ id: "string" }),
createDiscordComponentUserSelect: () => ({ id: "user" }),
}));
vi.mock("./auto-presence.js", () => ({
createDiscordAutoPresenceController: createDiscordAutoPresenceControllerMock,
}));
vi.mock("./commands.js", () => ({
resolveDiscordSlashCommandConfig: () => ({ ephemeral: false }),
}));
vi.mock("./exec-approvals.js", () => ({
createExecApprovalButton: () => ({ id: "exec-approval" }),
DiscordExecApprovalHandler: class DiscordExecApprovalHandler {
async start() {
return undefined;
}
async stop() {
return undefined;
}
},
}));
vi.mock("./gateway-plugin.js", () => ({
createDiscordGatewayPlugin: () => ({ id: "gateway-plugin" }),
}));
vi.mock("./listeners.js", () => ({
DiscordMessageListener: class DiscordMessageListener {},
DiscordPresenceListener: class DiscordPresenceListener {},
DiscordReactionListener: class DiscordReactionListener {},
DiscordReactionRemoveListener: class DiscordReactionRemoveListener {},
DiscordThreadUpdateListener: class DiscordThreadUpdateListener {},
registerDiscordListener: vi.fn(),
}));
vi.mock("./message-handler.js", () => ({
createDiscordMessageHandler: createDiscordMessageHandlerMock,
}));
vi.mock("./native-command.js", () => ({
createDiscordCommandArgFallbackButton: () => ({ id: "arg-fallback" }),
createDiscordModelPickerFallbackButton: () => ({ id: "model-fallback-btn" }),
createDiscordModelPickerFallbackSelect: () => ({ id: "model-fallback-select" }),
createDiscordNativeCommand: createDiscordNativeCommandMock,
}));
vi.mock("./presence.js", () => ({
resolveDiscordPresenceUpdate: () => undefined,
}));
vi.mock("./provider.allowlist.js", () => ({
resolveDiscordAllowlistConfig: resolveDiscordAllowlistConfigMock,
}));
vi.mock("./provider.lifecycle.js", () => ({
runDiscordGatewayLifecycle: monitorLifecycleMock,
}));
vi.mock("./rest-fetch.js", () => ({
resolveDiscordRestFetch: () => async () => undefined,
}));
vi.mock("./thread-bindings.js", () => ({
createNoopThreadBindingManager: createNoopThreadBindingManagerMock,
createThreadBindingManager: createThreadBindingManagerMock,
reconcileAcpThreadBindingsOnStartup: reconcileAcpThreadBindingsOnStartupMock,
}));
export * from "../../../../test/helpers/extensions/discord-provider.test-support.js";

View File

@ -9,7 +9,7 @@ import {
getProviderMonitorTestMocks,
mockResolvedDiscordAccountConfig,
resetDiscordProviderMonitorMocks,
} from "./provider.test-support.js";
} from "../../../../test/helpers/extensions/discord-provider.test-support.js";
const {
clientConstructorOptionsMock,
@ -37,9 +37,15 @@ const {
voiceRuntimeModuleLoadedMock,
} = getProviderMonitorTestMocks();
vi.mock("../../../../src/plugins/commands.js", () => ({
getPluginCommandSpecs: getPluginCommandSpecsMock,
}));
vi.mock("openclaw/plugin-sdk/plugin-runtime", async () => {
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/plugin-runtime")>(
"openclaw/plugin-sdk/plugin-runtime",
);
return {
...actual,
getPluginCommandSpecs: getPluginCommandSpecsMock,
};
});
vi.mock("../voice/manager.runtime.js", () => {
voiceRuntimeModuleLoadedMock();

View File

@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { resolveDiscordChannelAllowlist } from "./resolve-channels.js";
import { jsonResponse, urlToString } from "./test-http-helpers.js";

View File

@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { resolveDiscordUserAllowlist } from "./resolve-users.js";
import { jsonResponse, urlToString } from "./test-http-helpers.js";

View File

@ -1,4 +1,4 @@
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
type DiscordWebMediaMockFactoryResult = {

View File

@ -2,6 +2,7 @@ import type { DiscordGuildEntry } from "openclaw/plugin-sdk/config-runtime";
import {
DEFAULT_ACCOUNT_ID,
createEnvPatchedAccountSetupAdapter,
formatDocsLink,
noteChannelLookupFailure,
noteChannelLookupSummary,
parseMentionOrPrefixedId,
@ -16,7 +17,6 @@ import {
type ChannelSetupDmPolicy,
type ChannelSetupWizard,
} from "openclaw/plugin-sdk/setup";
import { formatDocsLink } from "../../../src/terminal/links.js";
import { inspectDiscordAccount } from "./account-inspect.js";
import { listDiscordAccountIds, resolveDiscordAccount } from "./accounts.js";

View File

@ -1,11 +1,11 @@
import {
formatDocsLink,
type OpenClawConfig,
promptLegacyChannelAllowFrom,
resolveSetupAccountId,
type WizardPrompter,
} from "openclaw/plugin-sdk/setup";
import { type ChannelSetupWizard } from "openclaw/plugin-sdk/setup";
import { formatDocsLink } from "../../../src/terminal/links.js";
import { resolveDefaultDiscordAccountId, resolveDiscordAccount } from "./accounts.js";
import { normalizeDiscordSlug } from "./monitor/allow-list.js";
import {

View File

@ -3,10 +3,12 @@ import {
createScopedAccountConfigAccessors,
createScopedChannelConfigBase,
} from "openclaw/plugin-sdk/channel-config-helpers";
import { buildChannelConfigSchema } from "../../../src/channels/plugins/config-schema.js";
import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js";
import { getChatChannelMeta } from "../../../src/channels/registry.js";
import { DiscordConfigSchema } from "../../../src/config/zod-schema.providers-core.js";
import {
buildChannelConfigSchema,
DiscordConfigSchema,
getChatChannelMeta,
type ChannelPlugin,
} from "openclaw/plugin-sdk/discord";
import { inspectDiscordAccount } from "./account-inspect.js";
import {
listDiscordAccountIds,

View File

@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import {
getRequiredHookHandler,
registerHookHandlersForTest,
} from "../../test-utils/subagent-hooks.js";
} from "../../../test/helpers/extensions/subagent-hooks.js";
import { registerDiscordSubagentHooks } from "./subagent-hooks.js";
type ThreadBindingRecord = {

View File

@ -1,6 +1,6 @@
import type { ClawdbotConfig, PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/feishu";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import type { FeishuMessageEvent } from "./bot.js";
import {
buildBroadcastSessionKey,

View File

@ -26,104 +26,22 @@ import {
sendMessageFeishu as sendMessageFeishuImpl,
} from "./send.js";
type ListFeishuDirectoryGroupsLive = typeof import("./directory.js").listFeishuDirectoryGroupsLive;
type ListFeishuDirectoryPeersLive = typeof import("./directory.js").listFeishuDirectoryPeersLive;
type FeishuOutbound = typeof import("./outbound.js").feishuOutbound;
type CreatePinFeishu = typeof import("./pins.js").createPinFeishu;
type ListPinsFeishu = typeof import("./pins.js").listPinsFeishu;
type RemovePinFeishu = typeof import("./pins.js").removePinFeishu;
type ProbeFeishu = typeof import("./probe.js").probeFeishu;
type AddReactionFeishu = typeof import("./reactions.js").addReactionFeishu;
type ListReactionsFeishu = typeof import("./reactions.js").listReactionsFeishu;
type RemoveReactionFeishu = typeof import("./reactions.js").removeReactionFeishu;
type GetChatInfo = typeof import("./chat.js").getChatInfo;
type GetChatMembers = typeof import("./chat.js").getChatMembers;
type GetFeishuMemberInfo = typeof import("./chat.js").getFeishuMemberInfo;
type EditMessageFeishu = typeof import("./send.js").editMessageFeishu;
type GetMessageFeishu = typeof import("./send.js").getMessageFeishu;
type SendCardFeishu = typeof import("./send.js").sendCardFeishu;
type SendMessageFeishu = typeof import("./send.js").sendMessageFeishu;
export function listFeishuDirectoryGroupsLive(
...args: Parameters<ListFeishuDirectoryGroupsLive>
): ReturnType<ListFeishuDirectoryGroupsLive> {
return listFeishuDirectoryGroupsLiveImpl(...args);
}
export function listFeishuDirectoryPeersLive(
...args: Parameters<ListFeishuDirectoryPeersLive>
): ReturnType<ListFeishuDirectoryPeersLive> {
return listFeishuDirectoryPeersLiveImpl(...args);
}
export const feishuOutbound: FeishuOutbound = { ...feishuOutboundImpl };
export function createPinFeishu(...args: Parameters<CreatePinFeishu>): ReturnType<CreatePinFeishu> {
return createPinFeishuImpl(...args);
}
export function listPinsFeishu(...args: Parameters<ListPinsFeishu>): ReturnType<ListPinsFeishu> {
return listPinsFeishuImpl(...args);
}
export function removePinFeishu(...args: Parameters<RemovePinFeishu>): ReturnType<RemovePinFeishu> {
return removePinFeishuImpl(...args);
}
export function probeFeishu(...args: Parameters<ProbeFeishu>): ReturnType<ProbeFeishu> {
return probeFeishuImpl(...args);
}
export function addReactionFeishu(
...args: Parameters<AddReactionFeishu>
): ReturnType<AddReactionFeishu> {
return addReactionFeishuImpl(...args);
}
export function listReactionsFeishu(
...args: Parameters<ListReactionsFeishu>
): ReturnType<ListReactionsFeishu> {
return listReactionsFeishuImpl(...args);
}
export function removeReactionFeishu(
...args: Parameters<RemoveReactionFeishu>
): ReturnType<RemoveReactionFeishu> {
return removeReactionFeishuImpl(...args);
}
export function getChatInfo(...args: Parameters<GetChatInfo>): ReturnType<GetChatInfo> {
return getChatInfoImpl(...args);
}
export function getChatMembers(...args: Parameters<GetChatMembers>): ReturnType<GetChatMembers> {
return getChatMembersImpl(...args);
}
export function getFeishuMemberInfo(
...args: Parameters<GetFeishuMemberInfo>
): ReturnType<GetFeishuMemberInfo> {
return getFeishuMemberInfoImpl(...args);
}
export function editMessageFeishu(
...args: Parameters<EditMessageFeishu>
): ReturnType<EditMessageFeishu> {
return editMessageFeishuImpl(...args);
}
export function getMessageFeishu(
...args: Parameters<GetMessageFeishu>
): ReturnType<GetMessageFeishu> {
return getMessageFeishuImpl(...args);
}
export function sendCardFeishu(...args: Parameters<SendCardFeishu>): ReturnType<SendCardFeishu> {
return sendCardFeishuImpl(...args);
}
export function sendMessageFeishu(
...args: Parameters<SendMessageFeishu>
): ReturnType<SendMessageFeishu> {
return sendMessageFeishuImpl(...args);
}
export const feishuChannelRuntime = {
listFeishuDirectoryGroupsLive: listFeishuDirectoryGroupsLiveImpl,
listFeishuDirectoryPeersLive: listFeishuDirectoryPeersLiveImpl,
feishuOutbound: { ...feishuOutboundImpl },
createPinFeishu: createPinFeishuImpl,
listPinsFeishu: listPinsFeishuImpl,
removePinFeishu: removePinFeishuImpl,
probeFeishu: probeFeishuImpl,
addReactionFeishu: addReactionFeishuImpl,
listReactionsFeishu: listReactionsFeishuImpl,
removeReactionFeishu: removeReactionFeishuImpl,
getChatInfo: getChatInfoImpl,
getChatMembers: getChatMembersImpl,
getFeishuMemberInfo: getFeishuMemberInfoImpl,
editMessageFeishu: editMessageFeishuImpl,
getMessageFeishu: getMessageFeishuImpl,
sendCardFeishu: sendCardFeishuImpl,
sendMessageFeishu: sendMessageFeishuImpl,
};

View File

@ -28,22 +28,28 @@ vi.mock("./client.js", () => ({
}));
vi.mock("./channel.runtime.js", () => ({
addReactionFeishu: addReactionFeishuMock,
createPinFeishu: createPinFeishuMock,
editMessageFeishu: editMessageFeishuMock,
getChatInfo: getChatInfoMock,
getChatMembers: getChatMembersMock,
getFeishuMemberInfo: getFeishuMemberInfoMock,
getMessageFeishu: getMessageFeishuMock,
listFeishuDirectoryGroupsLive: listFeishuDirectoryGroupsLiveMock,
listFeishuDirectoryPeersLive: listFeishuDirectoryPeersLiveMock,
listPinsFeishu: listPinsFeishuMock,
listReactionsFeishu: listReactionsFeishuMock,
probeFeishu: probeFeishuMock,
removePinFeishu: removePinFeishuMock,
removeReactionFeishu: removeReactionFeishuMock,
sendCardFeishu: sendCardFeishuMock,
sendMessageFeishu: sendMessageFeishuMock,
feishuChannelRuntime: {
addReactionFeishu: addReactionFeishuMock,
createPinFeishu: createPinFeishuMock,
editMessageFeishu: editMessageFeishuMock,
getChatInfo: getChatInfoMock,
getChatMembers: getChatMembersMock,
getFeishuMemberInfo: getFeishuMemberInfoMock,
getMessageFeishu: getMessageFeishuMock,
listFeishuDirectoryGroupsLive: listFeishuDirectoryGroupsLiveMock,
listFeishuDirectoryPeersLive: listFeishuDirectoryPeersLiveMock,
listPinsFeishu: listPinsFeishuMock,
listReactionsFeishu: listReactionsFeishuMock,
probeFeishu: probeFeishuMock,
removePinFeishu: removePinFeishuMock,
removeReactionFeishu: removeReactionFeishuMock,
sendCardFeishu: sendCardFeishuMock,
sendMessageFeishu: sendMessageFeishuMock,
feishuOutbound: {
sendText: vi.fn(),
sendMedia: vi.fn(),
},
},
}));
import { feishuPlugin } from "./channel.js";

View File

@ -12,6 +12,7 @@ import {
PAIRING_APPROVED_MESSAGE,
} from "openclaw/plugin-sdk/feishu";
import type { ChannelMessageActionName } from "openclaw/plugin-sdk/feishu";
import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js";
import {
resolveFeishuAccount,
resolveFeishuCredentials,
@ -41,9 +42,12 @@ const meta: ChannelMeta = {
order: 70,
};
async function loadFeishuChannelRuntime() {
return await import("./channel.runtime.js");
}
type FeishuChannelRuntime = typeof import("./channel.runtime.js").feishuChannelRuntime;
const loadFeishuChannelRuntime = createLazyRuntimeSurface(
() => import("./channel.runtime.js"),
({ feishuChannelRuntime }) => feishuChannelRuntime,
);
function setFeishuNamedAccountEnabled(
cfg: ClawdbotConfig,

View File

@ -5,7 +5,7 @@ import {
createInboundDebouncer,
resolveInboundDebounceMs,
} from "../../../src/auto-reply/inbound-debounce.js";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import { monitorSingleAccount } from "./monitor.account.js";
import { setFeishuRuntime } from "./runtime.js";
import type { ResolvedFeishuAccount } from "./types.js";

View File

@ -5,7 +5,7 @@ import {
createInboundDebouncer,
resolveInboundDebounceMs,
} from "../../../src/auto-reply/inbound-debounce.js";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import { parseFeishuMessageEvent, type FeishuMessageEvent } from "./bot.js";
import * as dedup from "./dedup.js";
import { monitorSingleAccount } from "./monitor.account.js";

View File

@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import {
getRequiredHookHandler,
registerHookHandlersForTest,
} from "../../test-utils/subagent-hooks.js";
} from "../../../test/helpers/extensions/subagent-hooks.js";
import { registerFeishuSubagentHooks } from "./subagent-hooks.js";
import {
__testing as threadBindingTesting,

View File

@ -1,5 +1,8 @@
import { describe, expect, it } from "vitest";
import { createProviderUsageFetch, makeResponse } from "../test-utils/provider-usage-fetch.js";
import {
createProviderUsageFetch,
makeResponse,
} from "../../test/helpers/extensions/provider-usage-fetch.js";
import { fetchCopilotUsage } from "./usage.js";
describe("fetchCopilotUsage", () => {

View File

@ -8,36 +8,10 @@ import {
startGoogleChatMonitor as startGoogleChatMonitorImpl,
} from "./monitor.js";
type ProbeGoogleChat = typeof import("./api.js").probeGoogleChat;
type SendGoogleChatMessage = typeof import("./api.js").sendGoogleChatMessage;
type UploadGoogleChatAttachment = typeof import("./api.js").uploadGoogleChatAttachment;
type ResolveGoogleChatWebhookPath = typeof import("./monitor.js").resolveGoogleChatWebhookPath;
type StartGoogleChatMonitor = typeof import("./monitor.js").startGoogleChatMonitor;
export function probeGoogleChat(...args: Parameters<ProbeGoogleChat>): ReturnType<ProbeGoogleChat> {
return probeGoogleChatImpl(...args);
}
export function sendGoogleChatMessage(
...args: Parameters<SendGoogleChatMessage>
): ReturnType<SendGoogleChatMessage> {
return sendGoogleChatMessageImpl(...args);
}
export function uploadGoogleChatAttachment(
...args: Parameters<UploadGoogleChatAttachment>
): ReturnType<UploadGoogleChatAttachment> {
return uploadGoogleChatAttachmentImpl(...args);
}
export function resolveGoogleChatWebhookPath(
...args: Parameters<ResolveGoogleChatWebhookPath>
): ReturnType<ResolveGoogleChatWebhookPath> {
return resolveGoogleChatWebhookPathImpl(...args);
}
export function startGoogleChatMonitor(
...args: Parameters<StartGoogleChatMonitor>
): ReturnType<StartGoogleChatMonitor> {
return startGoogleChatMonitorImpl(...args);
}
export const googleChatChannelRuntime = {
probeGoogleChat: probeGoogleChatImpl,
sendGoogleChatMessage: sendGoogleChatMessageImpl,
uploadGoogleChatAttachment: uploadGoogleChatAttachmentImpl,
resolveGoogleChatWebhookPath: resolveGoogleChatWebhookPathImpl,
startGoogleChatMonitor: startGoogleChatMonitorImpl,
};

View File

@ -4,7 +4,7 @@ import {
abortStartedAccount,
expectPendingUntilAbort,
startAccountAndTrackLifecycle,
} from "../../test-utils/start-account-lifecycle.js";
} from "../../../test/helpers/extensions/start-account-lifecycle.js";
import type { ResolvedGoogleChatAccount } from "./accounts.js";
const hoisted = vi.hoisted(() => ({

View File

@ -27,6 +27,7 @@ import {
type OpenClawConfig,
} from "openclaw/plugin-sdk/googlechat";
import { GoogleChatConfigSchema } from "openclaw/plugin-sdk/googlechat";
import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js";
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
import {
listGoogleChatAccountIds,
@ -47,9 +48,12 @@ import {
const meta = getChatChannelMeta("googlechat");
async function loadGoogleChatChannelRuntime() {
return await import("./channel.runtime.js");
}
type GoogleChatChannelRuntime = typeof import("./channel.runtime.js").googleChatChannelRuntime;
const loadGoogleChatChannelRuntime = createLazyRuntimeSurface(
() => import("./channel.runtime.js"),
({ googleChatChannelRuntime }) => googleChatChannelRuntime,
);
const formatAllowFromEntry = (entry: string) =>
entry

View File

@ -4,7 +4,7 @@ import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/googlech
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-utils/mock-http-response.js";
import { createMockServerResponse } from "../../../test/helpers/extensions/mock-http-response.js";
import type { ResolvedGoogleChatAccount } from "./accounts.js";
import { verifyGoogleChatRequest } from "./auth.js";
import { handleGoogleChatWebhookRequest, registerGoogleChatWebhookTarget } from "./monitor.js";

View File

@ -1,8 +1,11 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/googlechat";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import {
createTestWizardPrompter,
type WizardPrompter,
} from "../../../test/helpers/extensions/setup-wizard.js";
import { googlechatPlugin } from "./channel.js";
const googlechatConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@ -1,5 +1,6 @@
import {
createPatchedAccountSetupAdapter,
formatDocsLink,
parseSetupEntriesAllowingWildcard,
promptParsedAllowFromForScopedChannel,
setChannelDmPolicyWithAllowFrom,
@ -13,7 +14,6 @@ import type {
ChannelSetupWizard,
ChannelSetupWizardTextInput,
} from "openclaw/plugin-sdk/setup";
import { formatDocsLink } from "../../../src/terminal/links.js";
import {
listIMessageAccountIds,
resolveDefaultIMessageAccountId,

View File

@ -1,5 +1,8 @@
import { setSetupChannelEnabled, type ChannelSetupWizard } from "openclaw/plugin-sdk/setup";
import { detectBinary } from "../../../src/plugins/setup-binary.js";
import {
detectBinary,
setSetupChannelEnabled,
type ChannelSetupWizard,
} from "openclaw/plugin-sdk/setup";
import { listIMessageAccountIds, resolveIMessageAccount } from "./accounts.js";
import {
createIMessageCliPathTextInput,

View File

@ -3,19 +3,17 @@ import {
collectAllowlistProviderRestrictSendersWarnings,
} from "openclaw/plugin-sdk/channel-policy";
import {
buildChannelConfigSchema,
DEFAULT_ACCOUNT_ID,
deleteAccountFromConfigSection,
setAccountEnabledInConfigSection,
} from "../../../src/channels/plugins/config-helpers.js";
import { buildChannelConfigSchema } from "../../../src/channels/plugins/config-schema.js";
import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js";
import { getChatChannelMeta } from "../../../src/channels/registry.js";
import { IMessageConfigSchema } from "../../../src/config/zod-schema.providers-core.js";
import {
formatTrimmedAllowFromEntries,
getChatChannelMeta,
IMessageConfigSchema,
resolveIMessageConfigAllowFrom,
resolveIMessageConfigDefaultTo,
} from "../../../src/plugin-sdk/channel-config-helpers.js";
import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js";
setAccountEnabledInConfigSection,
type ChannelPlugin,
} from "openclaw/plugin-sdk/imessage";
import {
listIMessageAccountIds,
resolveDefaultIMessageAccountId,

View File

@ -2,7 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import {
expectStopPendingUntilAbort,
startAccountAndTrackLifecycle,
} from "../../test-utils/start-account-lifecycle.js";
} from "../../../test/helpers/extensions/start-account-lifecycle.js";
import type { ResolvedIrcAccount } from "./accounts.js";
const hoisted = vi.hoisted(() => ({

View File

@ -3,7 +3,7 @@ import {
createSendCfgThreadingRuntime,
expectProvidedCfgSkipsRuntimeLoad,
expectRuntimeCfgFallback,
} from "../../test-utils/send-config.js";
} from "../../../test/helpers/extensions/send-config.js";
import type { IrcClient } from "./client.js";
import type { CoreConfig } from "./types.js";

View File

@ -1,8 +1,11 @@
import type { RuntimeEnv } from "openclaw/plugin-sdk/irc";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import {
createTestWizardPrompter,
type WizardPrompter,
} from "../../../test/helpers/extensions/setup-wizard.js";
import { ircPlugin } from "./channel.js";
import type { CoreConfig } from "./types.js";

View File

@ -1,6 +1,6 @@
import type { OpenClawConfig, PluginRuntime, ResolvedLineAccount } from "openclaw/plugin-sdk/line";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { linePlugin } from "./channel.js";
import { setLineRuntime } from "./runtime.js";

View File

@ -6,7 +6,7 @@ import type {
ResolvedLineAccount,
} from "openclaw/plugin-sdk/line";
import { describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { linePlugin } from "./channel.js";
import { setLineRuntime } from "./runtime.js";

View File

@ -6,8 +6,11 @@ import {
resolveDefaultLineAccountId,
resolveLineAccount,
} from "../../../src/line/accounts.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import {
createTestWizardPrompter,
type WizardPrompter,
} from "../../../test/helpers/extensions/setup-wizard.js";
import { lineSetupAdapter, lineSetupWizard } from "./setup-surface.js";
const lineConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@ -1,6 +1,6 @@
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/matrix";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { matrixPlugin } from "./channel.js";
import { setMatrixRuntime } from "./runtime.js";
import { createMatrixBotSdkMock } from "./test-mocks.js";

View File

@ -7,49 +7,12 @@ import { probeMatrix as probeMatrixImpl } from "./matrix/probe.js";
import { sendMessageMatrix as sendMessageMatrixImpl } from "./matrix/send.js";
import { matrixOutbound as matrixOutboundImpl } from "./outbound.js";
import { resolveMatrixTargets as resolveMatrixTargetsImpl } from "./resolve-targets.js";
type ListMatrixDirectoryGroupsLive =
typeof import("./directory-live.js").listMatrixDirectoryGroupsLive;
type ListMatrixDirectoryPeersLive =
typeof import("./directory-live.js").listMatrixDirectoryPeersLive;
type ResolveMatrixAuth = typeof import("./matrix/client.js").resolveMatrixAuth;
type ProbeMatrix = typeof import("./matrix/probe.js").probeMatrix;
type SendMessageMatrix = typeof import("./matrix/send.js").sendMessageMatrix;
type ResolveMatrixTargets = typeof import("./resolve-targets.js").resolveMatrixTargets;
type MatrixOutbound = typeof import("./outbound.js").matrixOutbound;
export function listMatrixDirectoryGroupsLive(
...args: Parameters<ListMatrixDirectoryGroupsLive>
): ReturnType<ListMatrixDirectoryGroupsLive> {
return listMatrixDirectoryGroupsLiveImpl(...args);
}
export function listMatrixDirectoryPeersLive(
...args: Parameters<ListMatrixDirectoryPeersLive>
): ReturnType<ListMatrixDirectoryPeersLive> {
return listMatrixDirectoryPeersLiveImpl(...args);
}
export function resolveMatrixAuth(
...args: Parameters<ResolveMatrixAuth>
): ReturnType<ResolveMatrixAuth> {
return resolveMatrixAuthImpl(...args);
}
export function probeMatrix(...args: Parameters<ProbeMatrix>): ReturnType<ProbeMatrix> {
return probeMatrixImpl(...args);
}
export function sendMessageMatrix(
...args: Parameters<SendMessageMatrix>
): ReturnType<SendMessageMatrix> {
return sendMessageMatrixImpl(...args);
}
export function resolveMatrixTargets(
...args: Parameters<ResolveMatrixTargets>
): ReturnType<ResolveMatrixTargets> {
return resolveMatrixTargetsImpl(...args);
}
export const matrixOutbound: MatrixOutbound = { ...matrixOutboundImpl };
export const matrixChannelRuntime = {
listMatrixDirectoryGroupsLive: listMatrixDirectoryGroupsLiveImpl,
listMatrixDirectoryPeersLive: listMatrixDirectoryPeersLiveImpl,
resolveMatrixAuth: resolveMatrixAuthImpl,
probeMatrix: probeMatrixImpl,
sendMessageMatrix: sendMessageMatrixImpl,
resolveMatrixTargets: resolveMatrixTargetsImpl,
matrixOutbound: { ...matrixOutboundImpl },
};

View File

@ -15,6 +15,7 @@ import {
PAIRING_APPROVED_MESSAGE,
type ChannelPlugin,
} from "openclaw/plugin-sdk/matrix";
import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js";
import { buildTrafficStatusSummary } from "../../shared/channel-status-summary.js";
import { matrixMessageActions } from "./actions.js";
import { MatrixConfigSchema } from "./config-schema.js";
@ -38,9 +39,12 @@ import type { CoreConfig } from "./types.js";
// Mutex for serializing account startup (workaround for concurrent dynamic import race condition)
let matrixStartupLock: Promise<void> = Promise.resolve();
async function loadMatrixChannelRuntime() {
return await import("./channel.runtime.js");
}
type MatrixChannelRuntime = typeof import("./channel.runtime.js").matrixChannelRuntime;
const loadMatrixChannelRuntime = createLazyRuntimeSurface(
() => import("./channel.runtime.js"),
({ matrixChannelRuntime }) => matrixChannelRuntime,
);
const meta = {
id: "matrix",

View File

@ -1,6 +1,6 @@
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/mattermost";
import { describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../test-utils/plugin-api.js";
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
import plugin from "./index.js";
function createApi(

View File

@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import {
expectProvidedCfgSkipsRuntimeLoad,
expectRuntimeCfgFallback,
} from "../../../test-utils/send-config.js";
} from "../../../../test/helpers/extensions/send-config.js";
import { parseMattermostTarget, sendMessageMattermost } from "./send.js";
import { resetMattermostOpaqueTargetCacheForTests } from "./target-resolution.js";

View File

@ -1,6 +1,6 @@
import type { PluginRuntime, SsrFPolicy } from "openclaw/plugin-sdk/msteams";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import {
buildMSTeamsAttachmentPlaceholder,
buildMSTeamsGraphMessageUrls,

View File

@ -1,6 +1,9 @@
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk/msteams";
import { describe, expect, it } from "vitest";
import { createDirectoryTestRuntime, expectDirectorySurface } from "../../test-utils/directory.js";
import {
createDirectoryTestRuntime,
expectDirectorySurface,
} from "../../../test/helpers/extensions/directory.js";
import { msteamsPlugin } from "./channel.js";
describe("msteams directory", () => {

View File

@ -8,42 +8,11 @@ import {
sendAdaptiveCardMSTeams as sendAdaptiveCardMSTeamsImpl,
sendMessageMSTeams as sendMessageMSTeamsImpl,
} from "./send.js";
type ListMSTeamsDirectoryGroupsLive =
typeof import("./directory-live.js").listMSTeamsDirectoryGroupsLive;
type ListMSTeamsDirectoryPeersLive =
typeof import("./directory-live.js").listMSTeamsDirectoryPeersLive;
type MSTeamsOutbound = typeof import("./outbound.js").msteamsOutbound;
type ProbeMSTeams = typeof import("./probe.js").probeMSTeams;
type SendAdaptiveCardMSTeams = typeof import("./send.js").sendAdaptiveCardMSTeams;
type SendMessageMSTeams = typeof import("./send.js").sendMessageMSTeams;
export function listMSTeamsDirectoryGroupsLive(
...args: Parameters<ListMSTeamsDirectoryGroupsLive>
): ReturnType<ListMSTeamsDirectoryGroupsLive> {
return listMSTeamsDirectoryGroupsLiveImpl(...args);
}
export function listMSTeamsDirectoryPeersLive(
...args: Parameters<ListMSTeamsDirectoryPeersLive>
): ReturnType<ListMSTeamsDirectoryPeersLive> {
return listMSTeamsDirectoryPeersLiveImpl(...args);
}
export const msteamsOutbound: MSTeamsOutbound = { ...msteamsOutboundImpl };
export function probeMSTeams(...args: Parameters<ProbeMSTeams>): ReturnType<ProbeMSTeams> {
return probeMSTeamsImpl(...args);
}
export function sendAdaptiveCardMSTeams(
...args: Parameters<SendAdaptiveCardMSTeams>
): ReturnType<SendAdaptiveCardMSTeams> {
return sendAdaptiveCardMSTeamsImpl(...args);
}
export function sendMessageMSTeams(
...args: Parameters<SendMessageMSTeams>
): ReturnType<SendMessageMSTeams> {
return sendMessageMSTeamsImpl(...args);
}
export const msTeamsChannelRuntime = {
listMSTeamsDirectoryGroupsLive: listMSTeamsDirectoryGroupsLiveImpl,
listMSTeamsDirectoryPeersLive: listMSTeamsDirectoryPeersLiveImpl,
msteamsOutbound: { ...msteamsOutboundImpl },
probeMSTeams: probeMSTeamsImpl,
sendAdaptiveCardMSTeams: sendAdaptiveCardMSTeamsImpl,
sendMessageMSTeams: sendMessageMSTeamsImpl,
};

View File

@ -14,6 +14,7 @@ import {
MSTeamsConfigSchema,
PAIRING_APPROVED_MESSAGE,
} from "openclaw/plugin-sdk/msteams";
import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js";
import { resolveMSTeamsGroupToolPolicy } from "./policy.js";
import type { ProbeMSTeamsResult } from "./probe.js";
import {
@ -56,9 +57,12 @@ const TEAMS_GRAPH_PERMISSION_HINTS: Record<string, string> = {
"Files.Read.All": "files (OneDrive)",
};
async function loadMSTeamsChannelRuntime() {
return await import("./channel.runtime.js");
}
type MSTeamsChannelRuntime = typeof import("./channel.runtime.js").msTeamsChannelRuntime;
const loadMSTeamsChannelRuntime = createLazyRuntimeSurface(
() => import("./channel.runtime.js"),
({ msTeamsChannelRuntime }) => msTeamsChannelRuntime,
);
export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
id: "msteams",

View File

@ -1,5 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { uploadToOneDrive, uploadToSharePoint } from "./graph-upload.js";
describe("graph upload helpers", () => {

View File

@ -3,7 +3,7 @@ import os from "node:os";
import path from "node:path";
import { SILENT_REPLY_TOKEN, type PluginRuntime } from "openclaw/plugin-sdk/msteams";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import type { StoredConversationReference } from "./conversation-store.js";
const graphUploadMockState = vi.hoisted(() => ({
uploadAndShareOneDrive: vi.fn(),

View File

@ -1,9 +1,9 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { createStartAccountContext } from "../../test-utils/start-account-context.js";
import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js";
import {
expectStopPendingUntilAbort,
startAccountAndTrackLifecycle,
} from "../../test-utils/start-account-lifecycle.js";
} from "../../../test/helpers/extensions/start-account-lifecycle.js";
import type { ResolvedNextcloudTalkAccount } from "./accounts.js";
const hoisted = vi.hoisted(() => ({

View File

@ -3,7 +3,7 @@ import {
createSendCfgThreadingRuntime,
expectProvidedCfgSkipsRuntimeLoad,
expectRuntimeCfgFallback,
} from "../../test-utils/send-config.js";
} from "../../../test/helpers/extensions/send-config.js";
const hoisted = vi.hoisted(() => ({
loadConfig: vi.fn(),

View File

@ -1,6 +1,6 @@
import type { PluginRuntime } from "openclaw/plugin-sdk/nostr";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createStartAccountContext } from "../../test-utils/start-account-context.js";
import { createStartAccountContext } from "../../../test/helpers/extensions/start-account-context.js";
import { nostrPlugin } from "./channel.js";
import { setNostrRuntime } from "./runtime.js";

View File

@ -1,8 +1,11 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/nostr";
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import {
createTestWizardPrompter,
type WizardPrompter,
} from "../../../test/helpers/extensions/setup-wizard.js";
import { nostrPlugin } from "./channel.js";
const nostrConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@ -7,7 +7,7 @@ import type {
PluginCommandContext,
} from "openclaw/plugin-sdk/phone-control";
import { describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../test-utils/plugin-api.js";
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
import registerPhoneControl from "./index.js";
function createApi(params: {

View File

@ -1,6 +1,6 @@
import { resetSystemEventsForTest } from "openclaw/plugin-sdk/infra-runtime";
import { resetInboundDedupe } from "openclaw/plugin-sdk/reply-runtime";
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { beforeEach, vi } from "vitest";
import type { SignalDaemonExitEvent, SignalDaemonHandle } from "./daemon.js";

View File

@ -1,5 +1,7 @@
import {
createPatchedAccountSetupAdapter,
formatCliCommand,
formatDocsLink,
normalizeE164,
parseSetupEntriesAllowingWildcard,
promptParsedAllowFromForScopedChannel,
@ -14,8 +16,6 @@ import type {
ChannelSetupWizard,
ChannelSetupWizardTextInput,
} from "openclaw/plugin-sdk/setup";
import { formatCliCommand } from "../../../src/cli/command-format.js";
import { formatDocsLink } from "../../../src/terminal/links.js";
import {
listSignalAccountIds,
resolveDefaultSignalAccountId,

View File

@ -1,6 +1,9 @@
import { setSetupChannelEnabled, type ChannelSetupWizard } from "openclaw/plugin-sdk/setup";
import { detectBinary } from "../../../src/plugins/setup-binary.js";
import { installSignalCli } from "../../../src/plugins/signal-cli-install.js";
import {
detectBinary,
installSignalCli,
setSetupChannelEnabled,
type ChannelSetupWizard,
} from "openclaw/plugin-sdk/setup";
import { listSignalAccountIds, resolveSignalAccount } from "./accounts.js";
import {
createSignalCliPathTextInput,

View File

@ -4,15 +4,15 @@ import {
collectAllowlistProviderRestrictSendersWarnings,
} from "openclaw/plugin-sdk/channel-policy";
import {
buildChannelConfigSchema,
DEFAULT_ACCOUNT_ID,
deleteAccountFromConfigSection,
getChatChannelMeta,
normalizeE164,
setAccountEnabledInConfigSection,
} from "../../../src/channels/plugins/config-helpers.js";
import { buildChannelConfigSchema } from "../../../src/channels/plugins/config-schema.js";
import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js";
import { getChatChannelMeta } from "../../../src/channels/registry.js";
import { SignalConfigSchema } from "../../../src/config/zod-schema.providers-core.js";
import { DEFAULT_ACCOUNT_ID } from "../../../src/routing/session-key.js";
import { normalizeE164 } from "../../../src/utils.js";
SignalConfigSchema,
type ChannelPlugin,
} from "openclaw/plugin-sdk/signal";
import {
listSignalAccountIds,
resolveDefaultSignalAccountId,

View File

@ -22,11 +22,11 @@ import {
resolveConfiguredFromRequiredCredentialStatuses,
resolveSlackGroupRequireMention,
resolveSlackGroupToolPolicy,
createSlackActions,
type ChannelPlugin,
type OpenClawConfig,
type SlackActionContext,
} from "openclaw/plugin-sdk/slack";
import type { SlackActionContext } from "../../../src/agents/tools/slack-actions.js";
import { createSlackActions } from "../../../src/channels/plugins/slack.actions.js";
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
import {
listEnabledSlackAccounts,

View File

@ -4,7 +4,10 @@ import * as mediaFetch from "../../../../src/media/fetch.js";
import type { SavedMedia } from "../../../../src/media/store.js";
import * as mediaStore from "../../../../src/media/store.js";
import { mockPinnedHostnameResolution } from "../../../../src/test-helpers/ssrf.js";
import { type FetchMock, withFetchPreconnect } from "../../../test-utils/fetch-mock.js";
import {
type FetchMock,
withFetchPreconnect,
} from "../../../../test/helpers/extensions/fetch-mock.js";
import {
fetchWithSlackAuth,
resolveSlackAttachmentContent,

View File

@ -2,6 +2,7 @@ import {
createAllowlistSetupWizardProxy,
DEFAULT_ACCOUNT_ID,
createEnvPatchedAccountSetupAdapter,
formatDocsLink,
hasConfiguredSecretInput,
type OpenClawConfig,
noteChannelLookupFailure,
@ -18,7 +19,6 @@ import {
type ChannelSetupWizard,
type ChannelSetupWizardAllowFromEntry,
} from "openclaw/plugin-sdk/setup";
import { formatDocsLink } from "../../../src/terminal/links.js";
import { inspectSlackAccount } from "./account-inspect.js";
import { listSlackAccountIds, resolveSlackAccount, type ResolvedSlackAccount } from "./accounts.js";
import {

View File

@ -1,4 +1,5 @@
import {
formatDocsLink,
noteChannelLookupFailure,
noteChannelLookupSummary,
type OpenClawConfig,
@ -11,7 +12,6 @@ import type {
ChannelSetupWizard,
ChannelSetupWizardAllowFromEntry,
} from "openclaw/plugin-sdk/setup";
import { formatDocsLink } from "../../../src/terminal/links.js";
import { resolveDefaultSlackAccountId, resolveSlackAccount } from "./accounts.js";
import { resolveSlackChannelAllowlist } from "./resolve-channels.js";
import { resolveSlackUserAllowlist } from "./resolve-users.js";

View File

@ -3,14 +3,18 @@ import {
createScopedAccountConfigAccessors,
createScopedChannelConfigBase,
} from "openclaw/plugin-sdk/channel-config-helpers";
import { buildChannelConfigSchema } from "../../../src/channels/plugins/config-schema.js";
import { patchChannelConfigForAccount } from "../../../src/channels/plugins/setup-wizard-helpers.js";
import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js";
import { getChatChannelMeta } from "../../../src/channels/registry.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { hasConfiguredSecretInput } from "../../../src/config/types.secrets.js";
import { SlackConfigSchema } from "../../../src/config/zod-schema.providers-core.js";
import { formatDocsLink } from "../../../src/terminal/links.js";
import {
formatDocsLink,
hasConfiguredSecretInput,
patchChannelConfigForAccount,
} from "openclaw/plugin-sdk/setup";
import {
buildChannelConfigSchema,
getChatChannelMeta,
SlackConfigSchema,
type ChannelPlugin,
type OpenClawConfig,
} from "openclaw/plugin-sdk/slack";
import { inspectSlackAccount } from "./account-inspect.js";
import {
listSlackAccountIds,

View File

@ -1,8 +1,11 @@
import { describe, expect, it, vi } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createTestWizardPrompter, type WizardPrompter } from "../../test-utils/setup-wizard.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import {
createTestWizardPrompter,
type WizardPrompter,
} from "../../../test/helpers/extensions/setup-wizard.js";
import { synologyChatPlugin } from "./channel.js";
import { synologyChatSetupWizard } from "./setup-surface.js";

View File

@ -1,6 +1,6 @@
import { describe, expect, it, vi } from "vitest";
import type { OpenClawPluginCommandDefinition } from "../test-utils/plugin-command.js";
import { createPluginRuntimeMock } from "../test-utils/plugin-runtime-mock.js";
import type { OpenClawPluginCommandDefinition } from "../../test/helpers/extensions/plugin-command.js";
import { createPluginRuntimeMock } from "../../test/helpers/extensions/plugin-runtime-mock.js";
import register from "./index.js";
function createHarness(config: Record<string, unknown>) {

View File

@ -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-utils/env.js";
import { withEnv } from "../../../test/helpers/extensions/env.js";
import { inspectTelegramAccount } from "./account-inspect.js";
describe("inspectTelegramAccount SecretRef resolution", () => {

View File

@ -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-utils/env.js";
import { withEnv } from "../../../test/helpers/extensions/env.js";
import {
listTelegramAccountIds,
resetMissingDefaultWarnFlag,

View File

@ -2,7 +2,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import type { ChannelGroupPolicy } from "openclaw/plugin-sdk/config-runtime";
import type { TelegramAccountConfig } from "openclaw/plugin-sdk/config-runtime";
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { vi } from "vitest";
import {
createNativeCommandTestParams,

View File

@ -2,7 +2,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { resetInboundDedupe } from "openclaw/plugin-sdk/reply-runtime";
import type { MsgContext } from "openclaw/plugin-sdk/reply-runtime";
import type { GetReplyOptions, ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { beforeEach, vi } from "vitest";
type AnyMock = MockFn<(...args: unknown[]) => unknown>;

View File

@ -3,8 +3,8 @@ import os from "node:os";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js";
import { withEnvAsync } from "../../test-utils/env.js";
import { useFrozenTime, useRealTime } from "../../test-utils/frozen-time.js";
import { withEnvAsync } from "../../../test/helpers/extensions/env.js";
import { useFrozenTime, useRealTime } from "../../../test/helpers/extensions/frozen-time.js";
import {
answerCallbackQuerySpy,
botCtorSpy,

View File

@ -5,7 +5,7 @@ import type {
PluginRuntime,
} from "openclaw/plugin-sdk/telegram";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import type { ResolvedTelegramAccount } from "./accounts.js";
import * as auditModule from "./audit.js";
import { telegramPlugin } from "./channel.js";

View File

@ -1,5 +1,5 @@
import { afterEach, type Mock, describe, expect, it, vi } from "vitest";
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
import { withFetchPreconnect } from "../../../test/helpers/extensions/fetch-mock.js";
import { probeTelegram, resetTelegramProbeFetcherCacheForTests } from "./probe.js";
const resolveTelegramFetch = vi.hoisted(() => vi.fn());

View File

@ -1,4 +1,4 @@
import type { MockFn } from "openclaw/plugin-sdk/test-utils";
import type { MockFn } from "openclaw/plugin-sdk/testing";
import { beforeEach, vi } from "vitest";
const { botApi, botCtorSpy } = vi.hoisted(() => ({

View File

@ -1,6 +1,8 @@
import {
createEnvPatchedAccountSetupAdapter,
DEFAULT_ACCOUNT_ID,
formatCliCommand,
formatDocsLink,
patchChannelConfigForAccount,
promptResolvedAllowFrom,
splitSetupEntries,
@ -8,8 +10,6 @@ import {
type WizardPrompter,
} from "openclaw/plugin-sdk/setup";
import type { ChannelSetupAdapter, ChannelSetupDmPolicy } from "openclaw/plugin-sdk/setup";
import { formatCliCommand } from "../../../src/cli/command-format.js";
import { formatDocsLink } from "../../../src/terminal/links.js";
import { resolveDefaultTelegramAccountId, resolveTelegramAccount } from "./accounts.js";
import { fetchTelegramChatId } from "./api-fetch.js";

View File

@ -3,12 +3,14 @@ import {
createScopedAccountConfigAccessors,
createScopedChannelConfigBase,
} from "openclaw/plugin-sdk/channel-config-helpers";
import { buildChannelConfigSchema } from "../../../src/channels/plugins/config-schema.js";
import type { ChannelPlugin } from "../../../src/channels/plugins/types.plugin.js";
import { getChatChannelMeta } from "../../../src/channels/registry.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { TelegramConfigSchema } from "../../../src/config/zod-schema.providers-core.js";
import { normalizeAccountId } from "../../../src/routing/session-key.js";
import {
buildChannelConfigSchema,
getChatChannelMeta,
normalizeAccountId,
TelegramConfigSchema,
type ChannelPlugin,
type OpenClawConfig,
} from "openclaw/plugin-sdk/telegram";
import { inspectTelegramAccount } from "./account-inspect.js";
import {
listTelegramAccountIds,

View File

@ -1 +0,0 @@
export { countLines, hasBalancedFences } from "../../src/test-utils/chunk-test-helpers.js";

View File

@ -1 +0,0 @@
export { captureEnv, withEnv, withEnvAsync } from "../../src/test-utils/env.js";

View File

@ -1 +0,0 @@
export { withFetchPreconnect, type FetchMock } from "../../src/test-utils/fetch-mock.js";

View File

@ -1 +0,0 @@
export { useFrozenTime, useRealTime } from "../../src/test-utils/frozen-time.js";

View File

@ -1 +0,0 @@
export { createMockServerResponse } from "../../src/test-utils/mock-http-response.js";

View File

@ -1 +0,0 @@
export { registerSingleProviderPlugin } from "../../src/test-utils/plugin-registration.js";

View File

@ -1,4 +0,0 @@
export {
createProviderUsageFetch,
makeResponse,
} from "../../src/test-utils/provider-usage-fetch.js";

Some files were not shown because too many files have changed in this diff Show More