fix(plugins): normalize bundled sidecar jiti imports

This commit is contained in:
Peter Steinberger
2026-04-27 12:35:02 +01:00
parent da8576c0bf
commit 48ebed3ed3
3 changed files with 48 additions and 2 deletions

View File

@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- CLI/doctor: run bundled plugin runtime-dependency repairs through the async npm installer with spinner/line progress and heartbeat updates, so long `openclaw doctor --fix` installs no longer look hung in TTY or piped output. Fixes #72775. Thanks @dfpalhano.
- Feishu/Windows: normalize bundled channel sidecar loads before Jiti evaluates them, so Feishu outbound sends no longer fail with raw `C:` ESM loader errors on Windows. Fixes #72783. Thanks @jackychen-png.
- Agents/tools: ignore volatile `exec` runtime metadata when comparing tool-loop outcomes, so enabled loop detection can stop repeated identical shell-command results instead of resetting on duration, PID, session, or cwd changes. Fixes #34574; supersedes #41502. Thanks @gucasbrg and @Zcg2021.
- Agents/fallback: classify internal live-session model switch conflicts as unknown fallback failures instead of provider overloads, preventing local vLLM endpoints from receiving misleading overloaded cooldowns. Refs #63229. Thanks @clawdia-lobster.
- Control UI: keep session-specific assistant identity loads authoritative after WebSocket connect, so non-main agent chat sessions do not show the main agent name in the header after bootstrap refreshes. Fixes #72776. Thanks @rockytian-top.

View File

@@ -265,6 +265,50 @@ describe("loadBundledEntryExportSync", () => {
}
});
it("normalizes Windows absolute sidecar paths before Jiti loads them", async () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-entry-contract-"));
tempDirs.push(tempRoot);
const openedFdPath = path.join(tempRoot, "opened");
fs.writeFileSync(openedFdPath, "opened\n", "utf8");
const jitiLoad = vi.fn(() => ({ load: 42 }));
const createJiti = vi.fn(() => jitiLoad);
vi.doMock("jiti", () => ({
createJiti,
}));
vi.doMock("../infra/boundary-file-read.js", () => ({
openBoundaryFileSync: () => ({
ok: true,
path: "C:\\Users\\alice\\openclaw\\dist\\extensions\\feishu\\helper.ts",
fd: fs.openSync(openedFdPath, "r"),
}),
}));
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
try {
const channelEntryContract = await importFreshModule<
typeof import("./channel-entry-contract.js")
>(import.meta.url, "./channel-entry-contract.js?scope=windows-safe-jiti-path");
expect(
channelEntryContract.loadBundledEntryExportSync<number>(
"file:///C:/Users/alice/openclaw/dist/extensions/feishu/index.js",
{
specifier: "./helper.ts",
exportName: "load",
},
{ installRuntimeDeps: false },
),
).toBe(42);
expect(jitiLoad).toHaveBeenCalledWith(
"file:///C:/Users/alice/openclaw/dist/extensions/feishu/helper.ts",
);
} finally {
platformSpy.mockRestore();
vi.doUnmock("../infra/boundary-file-read.js");
vi.doUnmock("jiti");
}
});
it("loads packaged telegram setup sidecars from dist-facing api modules", () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-entry-contract-"));
tempDirs.push(tempRoot);

View File

@@ -24,6 +24,7 @@ import {
import type { PluginRuntime } from "../plugins/runtime/types.js";
import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js";
import type { AnyAgentTool, OpenClawPluginApi, PluginCommandContext } from "../plugins/types.js";
import { toSafeImportPath } from "../shared/import-specifier.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
export type { AnyAgentTool, OpenClawPluginApi, PluginCommandContext };
@@ -370,12 +371,12 @@ function loadBundledEntryModuleSync(
} catch {
const jiti = getJiti(modulePath);
getJitiEndMs = profile ? performance.now() : 0;
loaded = jiti(modulePath);
loaded = jiti(toSafeImportPath(modulePath));
}
} else {
const jiti = getJiti(modulePath);
getJitiEndMs = profile ? performance.now() : 0;
loaded = jiti(modulePath);
loaded = jiti(toSafeImportPath(modulePath));
}
if (profile) {
const endMs = performance.now();