mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix(plugins): preserve tokenjuice runtime rule data
Preserve tokenjuice runtime rule JSON under dist/rules/tests during bundled plugin runtime dependency staging while continuing to prune unrelated tests directories.
This commit is contained in:
@@ -231,6 +231,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/CLI: keep `--agent` plus `--session-id` lookup scoped to the requested agent store, so explicit agent resumes cannot select another agent's session. (#70985) Thanks @frankekn.
|
||||
- Plugins/Comfy: read workflow and cloud auth configuration from `plugins.entries.comfy.config` while preserving legacy Comfy config fallback, so image, video, and music workflows pass config validation. Fixes #61915. (#63058) Thanks @547895019.
|
||||
- Gateway/secrets: restart secret-backed channels such as Slack and Zalo during `secrets.reload` so rotated webhook secrets take effect immediately, with the reload serialized and per-channel restart errors isolated. (#70720) Thanks @drobison00.
|
||||
- Plugins/tokenjuice: preserve `node_modules/tokenjuice/dist/rules/tests/*.json` during bundled plugin runtime staging so the plugin stops failing to load with `Cannot find module '../rules/tests/bun-test.json'`. The global basename prune treats any `tests/` directory as test cargo, but tokenjuice's `dist/rules/tests/` is runtime-loaded rule data consumed by `dist/core/builtin-rules.generated.js`. Adds an opt-in `keepDirectories` field to the per-package prune rule so packages with asset directories that collide with pruned basenames can stage cleanly.
|
||||
|
||||
## 2026.4.22
|
||||
|
||||
|
||||
@@ -209,8 +209,15 @@ const defaultStagedRuntimeDepPruneRules = new Map([
|
||||
["@jimp/plugin-print", { paths: ["src/__image_snapshots__"] }],
|
||||
["@jimp/plugin-quantize", { paths: ["src/__image_snapshots__"] }],
|
||||
["@jimp/plugin-threshold", { paths: ["src/__image_snapshots__"] }],
|
||||
// tokenjuice ships built-in rules as JSON data under `dist/rules/tests/*.json`
|
||||
// (e.g. `bun-test.json`, `jest.json`, `pytest.json`). These are NOT test
|
||||
// fixtures — they are the runtime-loaded rule definitions consumed by
|
||||
// `dist/core/builtin-rules.generated.js`. The global `tests` basename prune
|
||||
// would strip them, and the plugin then fails to load with
|
||||
// `Cannot find module '../rules/tests/bun-test.json'`. Keep them staged.
|
||||
["tokenjuice", { keepDirectories: ["dist/rules/tests"] }],
|
||||
]);
|
||||
const runtimeDepsStagingVersion = 6;
|
||||
const runtimeDepsStagingVersion = 7;
|
||||
const exactVersionSpecRe = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
|
||||
|
||||
function resolveRuntimeDepPruneConfig(params = {}) {
|
||||
@@ -547,7 +554,7 @@ function isNodeModulesPackageRoot(segments, index) {
|
||||
return parent?.startsWith("@") === true && segments[index - 2] === "node_modules";
|
||||
}
|
||||
|
||||
function pruneDependencyDirectoriesByBasename(depRoot, basenames) {
|
||||
function pruneDependencyDirectoriesByBasename(depRoot, basenames, keepDirs = new Set()) {
|
||||
if (!basenames || basenames.length === 0 || !fs.existsSync(depRoot)) {
|
||||
return;
|
||||
}
|
||||
@@ -562,6 +569,15 @@ function pruneDependencyDirectoriesByBasename(depRoot, basenames) {
|
||||
const fullPath = path.join(currentDir, entry.name);
|
||||
const segments = relativePathSegments(depRoot, fullPath);
|
||||
if (basenameSet.has(entry.name) && !isNodeModulesPackageRoot(segments, segments.length - 1)) {
|
||||
// Per-package opt-out: a pruneRule may keep specific directories that
|
||||
// would otherwise match a global basename prune (e.g. a data/asset
|
||||
// directory named `tests/` that is NOT test code). Descend into kept
|
||||
// directories so their contents are still subject to suffix/pattern
|
||||
// pruning, but do not remove the directory itself.
|
||||
if (keepDirs.has(fullPath)) {
|
||||
queue.push(fullPath);
|
||||
continue;
|
||||
}
|
||||
removePathIfExists(fullPath);
|
||||
continue;
|
||||
}
|
||||
@@ -591,7 +607,12 @@ function pruneStagedInstalledDependencyCargo(nodeModulesDir, depName, pruneConfi
|
||||
for (const relativePath of pruneRule?.paths ?? []) {
|
||||
removePathIfExists(path.join(depRoot, relativePath));
|
||||
}
|
||||
pruneDependencyDirectoriesByBasename(depRoot, pruneConfig.globalPruneDirectories);
|
||||
// Resolve per-package keepDirectories (opt-out of global basename prune)
|
||||
// against depRoot up front so the walk can skip them cheaply.
|
||||
const keepDirs = new Set(
|
||||
(pruneRule?.keepDirectories ?? []).map((relativePath) => path.resolve(depRoot, relativePath)),
|
||||
);
|
||||
pruneDependencyDirectoriesByBasename(depRoot, pruneConfig.globalPruneDirectories, keepDirs);
|
||||
pruneDependencyFilesByPatterns(depRoot, pruneConfig.globalPruneFilePatterns);
|
||||
pruneDependencyFilesBySuffixes(depRoot, pruneConfig.globalPruneSuffixes);
|
||||
pruneDependencyFilesBySuffixes(depRoot, pruneRule?.suffixes ?? []);
|
||||
|
||||
@@ -951,6 +951,66 @@ describe("stageBundledPluginRuntimeDeps", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("honors keepDirectories to opt a subtree out of global basename prune", () => {
|
||||
// Regression: tokenjuice ships runtime-loaded rule data under
|
||||
// `dist/rules/tests/*.json`. Without keepDirectories the global `tests`
|
||||
// basename prune would strip that subtree and the plugin would fail to
|
||||
// load with `Cannot find module '../rules/tests/bun-test.json'`.
|
||||
const { pluginDir, repoRoot } = createBundledPluginFixture({
|
||||
packageJson: {
|
||||
name: "@openclaw/fixture-plugin",
|
||||
version: "1.0.0",
|
||||
dependencies: { "keep-target": "1.0.0" },
|
||||
openclaw: { bundle: { stageRuntimeDependencies: true } },
|
||||
},
|
||||
});
|
||||
const depDir = path.join(repoRoot, "node_modules", "keep-target");
|
||||
fs.mkdirSync(path.join(depDir, "dist", "rules", "tests"), { recursive: true });
|
||||
fs.mkdirSync(path.join(depDir, "src", "tests"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(depDir, "package.json"),
|
||||
'{ "name": "keep-target", "version": "1.0.0" }\n',
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(depDir, "dist", "rules", "tests", "bun-test.json"),
|
||||
'{"rule":"bun"}\n',
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(depDir, "src", "tests", "legit-test.spec.ts"),
|
||||
"describe('x', () => {});\n",
|
||||
"utf8",
|
||||
);
|
||||
|
||||
stageBundledPluginRuntimeDeps({
|
||||
cwd: repoRoot,
|
||||
stagedRuntimeDepPruneRules: new Map([
|
||||
["keep-target", { keepDirectories: ["dist/rules/tests"] }],
|
||||
]),
|
||||
});
|
||||
|
||||
// Opt-in path: preserved intact.
|
||||
expect(
|
||||
fs.existsSync(
|
||||
path.join(
|
||||
pluginDir,
|
||||
"node_modules",
|
||||
"keep-target",
|
||||
"dist",
|
||||
"rules",
|
||||
"tests",
|
||||
"bun-test.json",
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// Unlisted `tests/` directories still get pruned.
|
||||
expect(fs.existsSync(path.join(pluginDir, "node_modules", "keep-target", "src", "tests"))).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("applies default prune rules for known heavy non-runtime package cargo", () => {
|
||||
const { pluginDir, repoRoot } = createBundledPluginFixture({
|
||||
packageJson: {
|
||||
|
||||
Reference in New Issue
Block a user