fix(ci): patch main regression surfaces

This commit is contained in:
Peter Steinberger 2026-04-06 04:16:18 +01:00
parent 82ad0f6b24
commit 2a5c355688
No known key found for this signature in database
17 changed files with 127 additions and 46 deletions

View File

@ -46,6 +46,7 @@ jobs:
run_check_additional: ${{ steps.manifest.outputs.run_check_additional }}
run_build_smoke: ${{ steps.manifest.outputs.run_build_smoke }}
run_check_docs: ${{ steps.manifest.outputs.run_check_docs }}
run_control_ui_i18n: ${{ steps.manifest.outputs.run_control_ui_i18n }}
run_checks_windows: ${{ steps.manifest.outputs.run_checks_windows }}
checks_windows_matrix: ${{ steps.manifest.outputs.checks_windows_matrix }}
run_macos_node: ${{ steps.manifest.outputs.run_macos_node }}
@ -128,6 +129,7 @@ jobs:
OPENCLAW_CI_RUN_ANDROID: ${{ steps.changed_scope.outputs.run_android || 'false' }}
OPENCLAW_CI_RUN_WINDOWS: ${{ steps.changed_scope.outputs.run_windows || 'false' }}
OPENCLAW_CI_RUN_SKILLS_PYTHON: ${{ steps.changed_scope.outputs.run_skills_python || 'false' }}
OPENCLAW_CI_RUN_CONTROL_UI_I18N: ${{ steps.changed_scope.outputs.run_control_ui_i18n || 'false' }}
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: ${{ steps.changed_extensions.outputs.has_changed_extensions || 'false' }}
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: ${{ steps.changed_extensions.outputs.changed_extensions_matrix || '{"include":[]}' }}
run: |
@ -165,6 +167,8 @@ jobs:
const runAndroid = parseBoolean(process.env.OPENCLAW_CI_RUN_ANDROID) && !docsOnly;
const runWindows = parseBoolean(process.env.OPENCLAW_CI_RUN_WINDOWS) && !docsOnly;
const runSkillsPython = parseBoolean(process.env.OPENCLAW_CI_RUN_SKILLS_PYTHON) && !docsOnly;
const runControlUiI18n =
parseBoolean(process.env.OPENCLAW_CI_RUN_CONTROL_UI_I18N) && !docsOnly;
const hasChangedExtensions =
parseBoolean(process.env.OPENCLAW_CI_HAS_CHANGED_EXTENSIONS) && !docsOnly;
const changedExtensionsMatrix = hasChangedExtensions
@ -241,6 +245,7 @@ jobs:
run_check_additional: runNode,
run_build_smoke: runNode,
run_check_docs: docsChanged,
run_control_ui_i18n: runControlUiI18n,
run_skills_python_job: runSkillsPython,
run_checks_windows: runWindows,
checks_windows_matrix: createMatrix(
@ -745,6 +750,7 @@ jobs:
- name: Check control UI locale sync
id: control_ui_i18n
if: needs.preflight.outputs.run_control_ui_i18n == 'true'
continue-on-error: true
run: pnpm ui:i18n:check
@ -780,7 +786,7 @@ jobs:
EXTENSION_PLUGIN_SDK_INTERNAL_BOUNDARY_OUTCOME: ${{ steps.extension_plugin_sdk_internal_boundary.outcome }}
EXTENSION_RELATIVE_OUTSIDE_PACKAGE_BOUNDARY_OUTCOME: ${{ steps.extension_relative_outside_package_boundary.outcome }}
NO_RAW_WINDOW_OPEN_OUTCOME: ${{ steps.no_raw_window_open.outcome }}
CONTROL_UI_I18N_OUTCOME: ${{ steps.control_ui_i18n.outcome }}
CONTROL_UI_I18N_OUTCOME: ${{ steps.control_ui_i18n.outcome == 'skipped' && 'success' || steps.control_ui_i18n.outcome }}
GATEWAY_WATCH_REGRESSION_OUTCOME: ${{ steps.gateway_watch_regression.outcome }}
run: |
failures=0

View File

@ -255,6 +255,7 @@ Current bundled provider examples:
| `plugin-sdk/provider-stream` | Provider stream wrapper helpers | `ProviderStreamFamily`, `buildProviderStreamFamilyHooks`, `composeProviderStreamWrappers`, stream wrapper types, and shared Anthropic/Bedrock/Google/Kilocode/Moonshot/OpenAI/OpenRouter/Z.A.I/MiniMax/Copilot wrapper helpers |
| `plugin-sdk/keyed-async-queue` | Ordered async queue | `KeyedAsyncQueue` |
| `plugin-sdk/media-runtime` | Shared media helpers | Media fetch/transform/store helpers plus media payload builders |
| `plugin-sdk/media-generation-runtime` | Shared media-generation helpers | Shared failover helpers, candidate selection, and missing-model messaging for image/video/music generation |
| `plugin-sdk/media-understanding` | Media-understanding helpers | Media understanding provider types plus provider-facing image/audio helper exports |
| `plugin-sdk/text-runtime` | Shared text helpers | Assistant-visible-text stripping, markdown render/chunking/table helpers, redaction helpers, directive-tag helpers, safe-text utilities, and related text/logging helpers |
| `plugin-sdk/text-chunking` | Text chunking helpers | Outbound text chunking helper |

View File

@ -223,6 +223,7 @@ explicitly promotes one as public.
| Subpath | Key exports |
| --- | --- |
| `plugin-sdk/media-runtime` | Shared media fetch/transform/store helpers plus media payload builders |
| `plugin-sdk/media-generation-runtime` | Shared media-generation failover helpers, candidate selection, and missing-model messaging |
| `plugin-sdk/media-understanding` | Media understanding provider types plus provider-facing image/audio helper exports |
| `plugin-sdk/text-runtime` | Shared text/markdown/logging helpers such as assistant-visible-text stripping, markdown render/chunking/table helpers, redaction helpers, directive-tag helpers, and safe-text utilities |
| `plugin-sdk/text-chunking` | Outbound text chunking helper |

View File

@ -124,13 +124,13 @@ MiniMax image generation is available through both bundled MiniMax auth paths:
## Provider capabilities
| Capability | OpenAI | Google | fal | MiniMax | ComfyUI | Vydra |
| --------------------- | -------------------- | -------------------- | ------------------- | -------------------------- | ---------------------------------- | ----- |
| Capability | OpenAI | Google | fal | MiniMax | ComfyUI | Vydra |
| --------------------- | -------------------- | -------------------- | ------------------- | -------------------------- | ---------------------------------- | ------- |
| Generate | Yes (up to 4) | Yes (up to 4) | Yes (up to 4) | Yes (up to 9) | Yes (workflow-defined outputs) | Yes (1) |
| Edit/reference | Yes (up to 5 images) | Yes (up to 5 images) | Yes (1 image) | Yes (1 image, subject ref) | Yes (1 image, workflow-configured) | No |
| Size control | Yes | Yes | Yes | No | No | No |
| Aspect ratio | No | Yes | Yes (generate only) | Yes | No | No |
| Resolution (1K/2K/4K) | No | Yes | Yes | No | No | No |
| Edit/reference | Yes (up to 5 images) | Yes (up to 5 images) | Yes (1 image) | Yes (1 image, subject ref) | Yes (1 image, workflow-configured) | No |
| Size control | Yes | Yes | Yes | No | No | No |
| Aspect ratio | No | Yes | Yes (generate only) | Yes | No | No |
| Resolution (1K/2K/4K) | No | Yes | Yes | No | No | No |
## Related

View File

@ -50,20 +50,20 @@ Outside of session-backed agent runs (for example, direct tool invocations), the
## Supported providers
| Provider | Default model | Text | Image ref | Video ref | API key |
| -------- | ------------------------------- | ---- | ---------------- | ---------------- | ---------------------------------------- |
| Alibaba | `wan2.6-t2v` | Yes | Yes (remote URL) | Yes (remote URL) | `MODELSTUDIO_API_KEY` |
| BytePlus | `seedance-1-0-lite-t2v-250428` | Yes | 1 image | No | `BYTEPLUS_API_KEY` |
| ComfyUI | `workflow` | Yes | 1 image | No | `COMFY_API_KEY` or `COMFY_CLOUD_API_KEY` |
| fal | `fal-ai/minimax/video-01-live` | Yes | 1 image | No | `FAL_KEY` |
| Google | `veo-3.1-fast-generate-preview` | Yes | 1 image | 1 video | `GEMINI_API_KEY` |
| MiniMax | `MiniMax-Hailuo-2.3` | Yes | 1 image | No | `MINIMAX_API_KEY` |
| OpenAI | `sora-2` | Yes | 1 image | 1 video | `OPENAI_API_KEY` |
| Qwen | `wan2.6-t2v` | Yes | Yes (remote URL) | Yes (remote URL) | `QWEN_API_KEY` |
| Runway | `gen4.5` | Yes | 1 image | 1 video | `RUNWAYML_API_SECRET` |
| Together | `Wan-AI/Wan2.2-T2V-A14B` | Yes | 1 image | No | `TOGETHER_API_KEY` |
| Vydra | `veo3` | Yes | 1 image (`kling`) | No | `VYDRA_API_KEY` |
| xAI | `grok-imagine-video` | Yes | 1 image | 1 video | `XAI_API_KEY` |
| Provider | Default model | Text | Image ref | Video ref | API key |
| -------- | ------------------------------- | ---- | ----------------- | ---------------- | ---------------------------------------- |
| Alibaba | `wan2.6-t2v` | Yes | Yes (remote URL) | Yes (remote URL) | `MODELSTUDIO_API_KEY` |
| BytePlus | `seedance-1-0-lite-t2v-250428` | Yes | 1 image | No | `BYTEPLUS_API_KEY` |
| ComfyUI | `workflow` | Yes | 1 image | No | `COMFY_API_KEY` or `COMFY_CLOUD_API_KEY` |
| fal | `fal-ai/minimax/video-01-live` | Yes | 1 image | No | `FAL_KEY` |
| Google | `veo-3.1-fast-generate-preview` | Yes | 1 image | 1 video | `GEMINI_API_KEY` |
| MiniMax | `MiniMax-Hailuo-2.3` | Yes | 1 image | No | `MINIMAX_API_KEY` |
| OpenAI | `sora-2` | Yes | 1 image | 1 video | `OPENAI_API_KEY` |
| Qwen | `wan2.6-t2v` | Yes | Yes (remote URL) | Yes (remote URL) | `QWEN_API_KEY` |
| Runway | `gen4.5` | Yes | 1 image | 1 video | `RUNWAYML_API_SECRET` |
| Together | `Wan-AI/Wan2.2-T2V-A14B` | Yes | 1 image | No | `TOGETHER_API_KEY` |
| Vydra | `veo3` | Yes | 1 image (`kling`) | No | `VYDRA_API_KEY` |
| xAI | `grok-imagine-video` | Yes | 1 image | 1 video | `XAI_API_KEY` |
Some providers accept additional or alternate API key env vars. See individual [provider pages](#related) for details.
@ -139,20 +139,20 @@ If a provider fails, the next candidate is tried automatically. If all candidate
## Provider notes
| Provider | Notes |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Alibaba | Uses DashScope/Model Studio async endpoint. Reference images and videos must be remote `http(s)` URLs. |
| BytePlus | Single image reference only. |
| ComfyUI | Workflow-driven local or cloud execution. Supports text-to-video and image-to-video through the configured graph. |
| fal | Uses queue-backed flow for long-running jobs. Single image reference only. |
| Google | Uses Gemini/Veo. Supports one image or one video reference. |
| MiniMax | Single image reference only. |
| OpenAI | Only `size` override is forwarded. Other style overrides (`aspectRatio`, `resolution`, `audio`, `watermark`) are ignored with a warning. |
| Qwen | Same DashScope backend as Alibaba. Reference inputs must be remote `http(s)` URLs; local files are rejected upfront. |
| Runway | Supports local files via data URIs. Video-to-video requires `runway/gen4_aleph`. Text-only runs expose `16:9` and `9:16` aspect ratios. |
| Together | Single image reference only. |
| Provider | Notes |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Alibaba | Uses DashScope/Model Studio async endpoint. Reference images and videos must be remote `http(s)` URLs. |
| BytePlus | Single image reference only. |
| ComfyUI | Workflow-driven local or cloud execution. Supports text-to-video and image-to-video through the configured graph. |
| fal | Uses queue-backed flow for long-running jobs. Single image reference only. |
| Google | Uses Gemini/Veo. Supports one image or one video reference. |
| MiniMax | Single image reference only. |
| OpenAI | Only `size` override is forwarded. Other style overrides (`aspectRatio`, `resolution`, `audio`, `watermark`) are ignored with a warning. |
| Qwen | Same DashScope backend as Alibaba. Reference inputs must be remote `http(s)` URLs; local files are rejected upfront. |
| Runway | Supports local files via data URIs. Video-to-video requires `runway/gen4_aleph`. Text-only runs expose `16:9` and `9:16` aspect ratios. |
| Together | Single image reference only. |
| Vydra | Uses `https://www.vydra.ai/api/v1` directly to avoid auth-dropping redirects. `veo3` is bundled as text-to-video only; `kling` requires a remote image URL. |
| xAI | Supports text-to-video, image-to-video, and remote video edit/extend flows. |
| xAI | Supports text-to-video, image-to-video, and remote video edit/extend flows. |
## Configuration
@ -182,7 +182,7 @@ openclaw config set agents.defaults.videoGenerationModel.primary "qwen/wan2.6-t2
- [Tools Overview](/tools)
- [Background Tasks](/automation/tasks) -- task tracking for async video generation
- [Alibaba Model Studio](/providers/alibaba)
- [BytePlus](/providers/byteplus)
- [BytePlus](/concepts/model-providers#byteplus-international)
- [ComfyUI](/providers/comfy)
- [fal](/providers/fal)
- [Google (Gemini)](/providers/google)

View File

@ -137,6 +137,8 @@ describe("minimax provider hooks", () => {
registerProvider() {},
registerMediaUnderstandingProvider() {},
registerImageGenerationProvider() {},
registerMusicGenerationProvider() {},
registerVideoGenerationProvider() {},
registerSpeechProvider() {},
registerWebSearchProvider(provider: unknown) {
webSearchProviders.push(provider);

View File

@ -1 +1,2 @@
export { whatsappOutbound } from "./src/outbound-adapter.js";
export { resolveWhatsAppRuntimeGroupPolicy } from "./src/runtime-group-policy.js";

View File

@ -207,6 +207,10 @@
"types": "./dist/plugin-sdk/media-runtime.d.ts",
"default": "./dist/plugin-sdk/media-runtime.js"
},
"./plugin-sdk/media-generation-runtime": {
"types": "./dist/plugin-sdk/media-generation-runtime.d.ts",
"default": "./dist/plugin-sdk/media-generation-runtime.js"
},
"./plugin-sdk/conversation-runtime": {
"types": "./dist/plugin-sdk/conversation-runtime.d.ts",
"default": "./dist/plugin-sdk/conversation-runtime.js"

View File

@ -6,6 +6,7 @@ import net from "node:net";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { writeBuildStamp } from "./build-stamp.mjs";
import { resolveBuildRequirement } from "./run-node.mjs";
const DEFAULTS = {
@ -454,6 +455,10 @@ async function main() {
ensureDir(options.outputDir);
if (!options.skipBuild) {
runCheckedCommand("pnpm", ["build"]);
// The watch harness must start from a completed-build baseline. Refresh
// the build stamp after the full build pipeline finishes so run-node does
// not spuriously rebuild inside the bounded watch window.
writeBuildStamp({ cwd: process.cwd() });
}
const preflightBuildRequirement = resolveBuildRequirement(buildRunNodeDeps(process.env));

View File

@ -5,6 +5,7 @@ export type ChangedScope = {
runWindows: boolean;
runSkillsPython: boolean;
runChangedSmoke: boolean;
runControlUiI18n: boolean;
};
export function detectChangedScope(changedPaths: string[]): ChangedScope;

View File

@ -1,7 +1,7 @@
import { execFileSync } from "node:child_process";
import { appendFileSync } from "node:fs";
/** @typedef {{ runNode: boolean; runMacos: boolean; runAndroid: boolean; runWindows: boolean; runSkillsPython: boolean; runChangedSmoke: boolean }} ChangedScope */
/** @typedef {{ runNode: boolean; runMacos: boolean; runAndroid: boolean; runWindows: boolean; runSkillsPython: boolean; runChangedSmoke: boolean; runControlUiI18n: boolean }} ChangedScope */
const DOCS_PATH_RE = /^(docs\/|.*\.mdx?$)/;
const SKILLS_PYTHON_SCOPE_RE = /^(skills\/|pyproject\.toml$)/;
@ -15,6 +15,8 @@ const NODE_SCOPE_RE =
/^(src\/|test\/|extensions\/|packages\/|scripts\/|ui\/|\.github\/|openclaw\.mjs$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|tsconfig.*\.json$|vitest.*\.ts$|tsdown\.config\.ts$|\.oxlintrc\.json$|\.oxfmtrc\.jsonc$)/;
const WINDOWS_SCOPE_RE =
/^(src\/|test\/|extensions\/|packages\/|scripts\/|ui\/|openclaw\.mjs$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|tsconfig.*\.json$|vitest.*\.ts$|tsdown\.config\.ts$|\.github\/workflows\/ci\.yml$|\.github\/actions\/setup-node-env\/action\.yml$|\.github\/actions\/setup-pnpm-store-cache\/action\.yml$)/;
const CONTROL_UI_I18N_SCOPE_RE =
/^(ui\/src\/i18n\/|scripts\/control-ui-i18n\.ts$|\.github\/workflows\/control-ui-locale-refresh\.yml$)/;
const NATIVE_ONLY_RE =
/^(apps\/android\/|apps\/ios\/|apps\/macos\/|apps\/shared\/|Swabble\/|appcast\.xml$)/;
const CHANGED_SMOKE_SCOPE_RE =
@ -33,6 +35,7 @@ export function detectChangedScope(changedPaths) {
runWindows: true,
runSkillsPython: true,
runChangedSmoke: true,
runControlUiI18n: true,
};
}
@ -42,6 +45,7 @@ export function detectChangedScope(changedPaths) {
let runWindows = false;
let runSkillsPython = false;
let runChangedSmoke = false;
let runControlUiI18n = false;
let hasNonDocs = false;
let hasNonNativeNonDocs = false;
@ -91,6 +95,10 @@ export function detectChangedScope(changedPaths) {
runChangedSmoke = true;
}
if (CONTROL_UI_I18N_SCOPE_RE.test(path)) {
runControlUiI18n = true;
}
if (!NATIVE_ONLY_RE.test(path)) {
hasNonNativeNonDocs = true;
}
@ -100,7 +108,15 @@ export function detectChangedScope(changedPaths) {
runNode = true;
}
return { runNode, runMacos, runAndroid, runWindows, runSkillsPython, runChangedSmoke };
return {
runNode,
runMacos,
runAndroid,
runWindows,
runSkillsPython,
runChangedSmoke,
runControlUiI18n,
};
}
/**
@ -136,6 +152,7 @@ export function writeGitHubOutput(scope, outputPath = process.env.GITHUB_OUTPUT)
appendFileSync(outputPath, `run_windows=${scope.runWindows}\n`, "utf8");
appendFileSync(outputPath, `run_skills_python=${scope.runSkillsPython}\n`, "utf8");
appendFileSync(outputPath, `run_changed_smoke=${scope.runChangedSmoke}\n`, "utf8");
appendFileSync(outputPath, `run_control_ui_i18n=${scope.runControlUiI18n}\n`, "utf8");
}
function isDirectRun() {
@ -172,6 +189,7 @@ if (isDirectRun()) {
runWindows: true,
runSkillsPython: true,
runChangedSmoke: true,
runControlUiI18n: true,
});
process.exit(0);
}
@ -184,6 +202,7 @@ if (isDirectRun()) {
runWindows: true,
runSkillsPython: true,
runChangedSmoke: true,
runControlUiI18n: true,
});
}
}

View File

@ -41,6 +41,7 @@
"ssrf-policy",
"ssrf-runtime",
"media-runtime",
"media-generation-runtime",
"conversation-runtime",
"matrix-runtime-heavy",
"matrix-runtime-shared",

View File

@ -3,10 +3,7 @@ import type { OpenClawConfig } from "../../../config/config.js";
import { listBundledChannelPlugins, setBundledChannelRuntime } from "../bundled.js";
import type { ChannelPlugin } from "../types.js";
import { channelPluginSurfaceKeys, type ChannelPluginSurface } from "./manifest.js";
import {
importBundledChannelContractArtifact,
resolveBundledChannelContractArtifactUrl,
} from "./runtime-artifacts.js";
import { importBundledChannelContractArtifact } from "./runtime-artifacts.js";
type SurfaceContractEntry = {
id: string;
@ -44,6 +41,10 @@ const sendMessageMatrixMock = vi.hoisted(() =>
roomId: to.replace(/^room:/, ""),
})),
);
const matrixRuntimeApiModuleId = new URL(
"../../../../extensions/matrix/runtime-api.js",
import.meta.url,
).href;
const lineContractApi = await importBundledChannelContractArtifact<{
listLineAccountIds: () => string[];
@ -62,11 +63,7 @@ setBundledChannelRuntime("line", {
},
} as never);
vi.mock(resolveBundledChannelContractArtifactUrl("matrix", "runtime-api"), async () => {
const matrixRuntimeApiModuleId = resolveBundledChannelContractArtifactUrl(
"matrix",
"runtime-api",
);
vi.mock(matrixRuntimeApiModuleId, async () => {
const actual = await vi.importActual(matrixRuntimeApiModuleId);
return {
...actual,

View File

@ -25,5 +25,6 @@ declare module "../../scripts/ci-changed-scope.mjs" {
runWindows: boolean;
runSkillsPython: boolean;
runChangedSmoke: boolean;
runControlUiI18n: boolean;
};
}

View File

@ -0,0 +1,3 @@
// Narrow shared media-generation runtime helpers for bundled and third-party plugins.
export * from "../media-generation/runtime-shared.js";

View File

@ -13,6 +13,7 @@ const { detectChangedScope, listChangedPaths } =
runWindows: boolean;
runSkillsPython: boolean;
runChangedSmoke: boolean;
runControlUiI18n: boolean;
};
listChangedPaths: (base: string, head?: string) => string[];
};
@ -37,6 +38,7 @@ describe("detectChangedScope", () => {
runWindows: true,
runSkillsPython: true,
runChangedSmoke: true,
runControlUiI18n: true,
});
});
@ -48,6 +50,7 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
@ -59,6 +62,7 @@ describe("detectChangedScope", () => {
runWindows: true,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
@ -70,6 +74,7 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
expect(detectChangedScope(["apps/shared/OpenClawKit/Sources/Foo.swift"])).toEqual({
runNode: false,
@ -78,6 +83,7 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
@ -90,6 +96,7 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
},
);
});
@ -102,6 +109,7 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
expect(detectChangedScope(["assets/icon.png"])).toEqual({
@ -111,6 +119,7 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
@ -122,6 +131,7 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
@ -133,6 +143,7 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: true,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
@ -144,6 +155,7 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: true,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
@ -155,6 +167,7 @@ describe("detectChangedScope", () => {
runWindows: true,
runSkillsPython: true,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
@ -166,6 +179,7 @@ describe("detectChangedScope", () => {
runWindows: true,
runSkillsPython: false,
runChangedSmoke: true,
runControlUiI18n: false,
});
expect(detectChangedScope([bundledPluginFile("matrix", "package.json")])).toEqual({
runNode: true,
@ -174,6 +188,7 @@ describe("detectChangedScope", () => {
runWindows: true,
runSkillsPython: false,
runChangedSmoke: true,
runControlUiI18n: false,
});
expect(detectChangedScope([".github/workflows/install-smoke.yml"])).toEqual({
runNode: true,
@ -182,6 +197,29 @@ describe("detectChangedScope", () => {
runWindows: false,
runSkillsPython: false,
runChangedSmoke: true,
runControlUiI18n: false,
});
});
it("runs control-ui locale check only for control-ui i18n surfaces", () => {
expect(detectChangedScope(["ui/src/i18n/locales/en.ts"])).toEqual({
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: true,
});
expect(detectChangedScope(["scripts/control-ui-i18n.ts"])).toEqual({
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: true,
});
});

View File

@ -70,6 +70,7 @@ export function describeBundledMetadataOnlyChannelCatalogContract(params: {
}),
"utf8",
);
fs.writeFileSync(path.join(bundledDir, "index.js"), "export default {};\n", "utf8");
fs.writeFileSync(
path.join(bundledDir, "openclaw.plugin.json"),
JSON.stringify({ id: params.pluginId, channels: [params.meta.id], configSchema: {} }),