mirror of https://github.com/openclaw/openclaw.git
573 lines
20 KiB
TypeScript
573 lines
20 KiB
TypeScript
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { afterAll, describe, expect, it, vi } from "vitest";
|
|
import { withEnv } from "../test-utils/env.js";
|
|
import {
|
|
buildPluginLoaderAliasMap,
|
|
buildPluginLoaderJitiOptions,
|
|
listPluginSdkAliasCandidates,
|
|
listPluginSdkExportedSubpaths,
|
|
resolveExtensionApiAlias,
|
|
resolvePluginRuntimeModulePath,
|
|
resolvePluginSdkAliasFile,
|
|
shouldPreferNativeJiti,
|
|
} from "./sdk-alias.js";
|
|
|
|
type CreateJiti = typeof import("jiti").createJiti;
|
|
|
|
let createJitiPromise: Promise<CreateJiti> | undefined;
|
|
|
|
async function getCreateJiti() {
|
|
createJitiPromise ??= import("jiti").then(({ createJiti }) => createJiti);
|
|
return createJitiPromise;
|
|
}
|
|
|
|
function chmodSafeDir(dir: string) {
|
|
if (process.platform === "win32") {
|
|
return;
|
|
}
|
|
fs.chmodSync(dir, 0o755);
|
|
}
|
|
|
|
function mkdtempSafe(prefix: string) {
|
|
const dir = fs.mkdtempSync(prefix);
|
|
chmodSafeDir(dir);
|
|
return dir;
|
|
}
|
|
|
|
function mkdirSafe(dir: string) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
chmodSafeDir(dir);
|
|
}
|
|
|
|
const fixtureRoot = mkdtempSafe(path.join(os.tmpdir(), "openclaw-sdk-alias-"));
|
|
let tempDirIndex = 0;
|
|
|
|
function makeTempDir() {
|
|
const dir = path.join(fixtureRoot, `case-${tempDirIndex++}`);
|
|
mkdirSafe(dir);
|
|
return dir;
|
|
}
|
|
|
|
function withCwd<T>(cwd: string, run: () => T): T {
|
|
const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(cwd);
|
|
try {
|
|
return run();
|
|
} finally {
|
|
cwdSpy.mockRestore();
|
|
}
|
|
}
|
|
|
|
function createPluginSdkAliasFixture(params?: {
|
|
srcFile?: string;
|
|
distFile?: string;
|
|
srcBody?: string;
|
|
distBody?: string;
|
|
packageName?: string;
|
|
packageExports?: Record<string, unknown>;
|
|
trustedRootIndicators?: boolean;
|
|
trustedRootIndicatorMode?: "bin+marker" | "cli-entry-only" | "none";
|
|
}) {
|
|
const root = makeTempDir();
|
|
const srcFile = path.join(root, "src", "plugin-sdk", params?.srcFile ?? "index.ts");
|
|
const distFile = path.join(root, "dist", "plugin-sdk", params?.distFile ?? "index.js");
|
|
mkdirSafe(path.dirname(srcFile));
|
|
mkdirSafe(path.dirname(distFile));
|
|
const trustedRootIndicatorMode =
|
|
params?.trustedRootIndicatorMode ??
|
|
(params?.trustedRootIndicators === false ? "none" : "bin+marker");
|
|
const packageJson: Record<string, unknown> = {
|
|
name: params?.packageName ?? "openclaw",
|
|
type: "module",
|
|
};
|
|
if (trustedRootIndicatorMode === "bin+marker") {
|
|
packageJson.bin = {
|
|
openclaw: "openclaw.mjs",
|
|
};
|
|
}
|
|
if (params?.packageExports || trustedRootIndicatorMode === "cli-entry-only") {
|
|
const trustedExports: Record<string, unknown> =
|
|
trustedRootIndicatorMode === "cli-entry-only"
|
|
? { "./cli-entry": { default: "./dist/cli-entry.js" } }
|
|
: {};
|
|
packageJson.exports = {
|
|
"./plugin-sdk": { default: "./dist/plugin-sdk/index.js" },
|
|
...trustedExports,
|
|
...params?.packageExports,
|
|
};
|
|
}
|
|
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify(packageJson, null, 2), "utf-8");
|
|
if (trustedRootIndicatorMode === "bin+marker") {
|
|
fs.writeFileSync(path.join(root, "openclaw.mjs"), "export {};\n", "utf-8");
|
|
}
|
|
fs.writeFileSync(srcFile, params?.srcBody ?? "export {};\n", "utf-8");
|
|
fs.writeFileSync(distFile, params?.distBody ?? "export {};\n", "utf-8");
|
|
return { root, srcFile, distFile };
|
|
}
|
|
|
|
function createExtensionApiAliasFixture(params?: { srcBody?: string; distBody?: string }) {
|
|
const root = makeTempDir();
|
|
const srcFile = path.join(root, "src", "extensionAPI.ts");
|
|
const distFile = path.join(root, "dist", "extensionAPI.js");
|
|
mkdirSafe(path.dirname(srcFile));
|
|
mkdirSafe(path.dirname(distFile));
|
|
fs.writeFileSync(
|
|
path.join(root, "package.json"),
|
|
JSON.stringify({ name: "openclaw", type: "module" }, null, 2),
|
|
"utf-8",
|
|
);
|
|
fs.writeFileSync(path.join(root, "openclaw.mjs"), "export {};\n", "utf-8");
|
|
fs.writeFileSync(srcFile, params?.srcBody ?? "export {};\n", "utf-8");
|
|
fs.writeFileSync(distFile, params?.distBody ?? "export {};\n", "utf-8");
|
|
return { root, srcFile, distFile };
|
|
}
|
|
|
|
function createPluginRuntimeAliasFixture(params?: { srcBody?: string; distBody?: string }) {
|
|
const root = makeTempDir();
|
|
const srcFile = path.join(root, "src", "plugins", "runtime", "index.ts");
|
|
const distFile = path.join(root, "dist", "plugins", "runtime", "index.js");
|
|
mkdirSafe(path.dirname(srcFile));
|
|
mkdirSafe(path.dirname(distFile));
|
|
fs.writeFileSync(
|
|
path.join(root, "package.json"),
|
|
JSON.stringify({ name: "openclaw", type: "module" }, null, 2),
|
|
"utf-8",
|
|
);
|
|
fs.writeFileSync(
|
|
srcFile,
|
|
params?.srcBody ?? "export const createPluginRuntime = () => ({});\n",
|
|
"utf-8",
|
|
);
|
|
fs.writeFileSync(
|
|
distFile,
|
|
params?.distBody ?? "export const createPluginRuntime = () => ({});\n",
|
|
"utf-8",
|
|
);
|
|
return { root, srcFile, distFile };
|
|
}
|
|
|
|
function resolvePluginSdkAlias(params: {
|
|
srcFile: string;
|
|
distFile: string;
|
|
modulePath: string;
|
|
argv1?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
}) {
|
|
const run = () =>
|
|
resolvePluginSdkAliasFile({
|
|
srcFile: params.srcFile,
|
|
distFile: params.distFile,
|
|
modulePath: params.modulePath,
|
|
argv1: params.argv1,
|
|
});
|
|
return params.env ? withEnv(params.env, run) : run();
|
|
}
|
|
|
|
function resolvePluginRuntimeModule(params: {
|
|
modulePath: string;
|
|
argv1?: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
}) {
|
|
const run = () =>
|
|
resolvePluginRuntimeModulePath({
|
|
modulePath: params.modulePath,
|
|
argv1: params.argv1,
|
|
});
|
|
return params.env ? withEnv(params.env, run) : run();
|
|
}
|
|
|
|
afterAll(() => {
|
|
fs.rmSync(fixtureRoot, { recursive: true, force: true });
|
|
});
|
|
|
|
describe("plugin sdk alias helpers", () => {
|
|
it.each([
|
|
{
|
|
name: "prefers dist plugin-sdk alias when loader runs from dist",
|
|
buildFixture: () => createPluginSdkAliasFixture(),
|
|
modulePath: (root: string) => path.join(root, "dist", "plugins", "loader.js"),
|
|
srcFile: "index.ts",
|
|
distFile: "index.js",
|
|
expected: "dist" as const,
|
|
},
|
|
{
|
|
name: "prefers src plugin-sdk alias when loader runs from src in non-production",
|
|
buildFixture: () => createPluginSdkAliasFixture(),
|
|
modulePath: (root: string) => path.join(root, "src", "plugins", "loader.ts"),
|
|
srcFile: "index.ts",
|
|
distFile: "index.js",
|
|
env: { NODE_ENV: undefined },
|
|
expected: "src" as const,
|
|
},
|
|
{
|
|
name: "falls back to src plugin-sdk alias when dist is missing in production",
|
|
buildFixture: () => {
|
|
const fixture = createPluginSdkAliasFixture();
|
|
fs.rmSync(fixture.distFile);
|
|
return fixture;
|
|
},
|
|
modulePath: (root: string) => path.join(root, "src", "plugins", "loader.ts"),
|
|
srcFile: "index.ts",
|
|
distFile: "index.js",
|
|
env: { NODE_ENV: "production", VITEST: undefined },
|
|
expected: "src" as const,
|
|
},
|
|
{
|
|
name: "prefers dist root-alias shim when loader runs from dist",
|
|
buildFixture: () =>
|
|
createPluginSdkAliasFixture({
|
|
srcFile: "root-alias.cjs",
|
|
distFile: "root-alias.cjs",
|
|
srcBody: "module.exports = {};\n",
|
|
distBody: "module.exports = {};\n",
|
|
}),
|
|
modulePath: (root: string) => path.join(root, "dist", "plugins", "loader.js"),
|
|
srcFile: "root-alias.cjs",
|
|
distFile: "root-alias.cjs",
|
|
expected: "dist" as const,
|
|
},
|
|
{
|
|
name: "prefers src root-alias shim when loader runs from src in non-production",
|
|
buildFixture: () =>
|
|
createPluginSdkAliasFixture({
|
|
srcFile: "root-alias.cjs",
|
|
distFile: "root-alias.cjs",
|
|
srcBody: "module.exports = {};\n",
|
|
distBody: "module.exports = {};\n",
|
|
}),
|
|
modulePath: (root: string) => path.join(root, "src", "plugins", "loader.ts"),
|
|
srcFile: "root-alias.cjs",
|
|
distFile: "root-alias.cjs",
|
|
env: { NODE_ENV: undefined },
|
|
expected: "src" as const,
|
|
},
|
|
{
|
|
name: "resolves plugin-sdk alias from package root when loader runs from transpiler cache path",
|
|
buildFixture: () => createPluginSdkAliasFixture(),
|
|
modulePath: () => "/tmp/tsx-cache/openclaw-loader.js",
|
|
argv1: (root: string) => path.join(root, "openclaw.mjs"),
|
|
srcFile: "index.ts",
|
|
distFile: "index.js",
|
|
env: { NODE_ENV: undefined },
|
|
expected: "src" as const,
|
|
},
|
|
])("$name", ({ buildFixture, modulePath, argv1, srcFile, distFile, env, expected }) => {
|
|
const fixture = buildFixture();
|
|
const resolved = resolvePluginSdkAlias({
|
|
srcFile,
|
|
distFile,
|
|
modulePath: modulePath(fixture.root),
|
|
argv1: argv1?.(fixture.root),
|
|
env,
|
|
});
|
|
expect(resolved).toBe(expected === "dist" ? fixture.distFile : fixture.srcFile);
|
|
});
|
|
|
|
it.each([
|
|
{
|
|
name: "prefers dist extension-api alias when loader runs from dist",
|
|
modulePath: (root: string) => path.join(root, "dist", "plugins", "loader.js"),
|
|
expected: "dist" as const,
|
|
},
|
|
{
|
|
name: "prefers src extension-api alias when loader runs from src in non-production",
|
|
modulePath: (root: string) => path.join(root, "src", "plugins", "loader.ts"),
|
|
env: { NODE_ENV: undefined },
|
|
expected: "src" as const,
|
|
},
|
|
{
|
|
name: "resolves extension-api alias from package root when loader runs from transpiler cache path",
|
|
modulePath: () => "/tmp/tsx-cache/openclaw-loader.js",
|
|
argv1: (root: string) => path.join(root, "openclaw.mjs"),
|
|
env: { NODE_ENV: undefined },
|
|
expected: "src" as const,
|
|
},
|
|
])("$name", ({ modulePath, argv1, env, expected }) => {
|
|
const fixture = createExtensionApiAliasFixture();
|
|
const resolved = withEnv(env ?? {}, () =>
|
|
resolveExtensionApiAlias({
|
|
modulePath: modulePath(fixture.root),
|
|
argv1: argv1?.(fixture.root),
|
|
}),
|
|
);
|
|
expect(resolved).toBe(expected === "dist" ? fixture.distFile : fixture.srcFile);
|
|
});
|
|
|
|
it.each([
|
|
{
|
|
name: "prefers dist candidates first for production src runtime",
|
|
env: { NODE_ENV: "production", VITEST: undefined },
|
|
expectedFirst: "dist" as const,
|
|
},
|
|
{
|
|
name: "prefers src candidates first for non-production src runtime",
|
|
env: { NODE_ENV: undefined },
|
|
expectedFirst: "src" as const,
|
|
},
|
|
])("$name", ({ env, expectedFirst }) => {
|
|
const fixture = createPluginSdkAliasFixture();
|
|
const candidates = withEnv(env ?? {}, () =>
|
|
listPluginSdkAliasCandidates({
|
|
srcFile: "index.ts",
|
|
distFile: "index.js",
|
|
modulePath: path.join(fixture.root, "src", "plugins", "loader.ts"),
|
|
}),
|
|
);
|
|
const first = expectedFirst === "dist" ? fixture.distFile : fixture.srcFile;
|
|
const second = expectedFirst === "dist" ? fixture.srcFile : fixture.distFile;
|
|
expect(candidates.indexOf(first)).toBeLessThan(candidates.indexOf(second));
|
|
});
|
|
|
|
it("derives plugin-sdk subpaths from package exports", () => {
|
|
const fixture = createPluginSdkAliasFixture({
|
|
packageExports: {
|
|
"./plugin-sdk/compat": { default: "./dist/plugin-sdk/compat.js" },
|
|
"./plugin-sdk/telegram": { default: "./dist/plugin-sdk/telegram.js" },
|
|
"./plugin-sdk/nested/value": { default: "./dist/plugin-sdk/nested/value.js" },
|
|
},
|
|
});
|
|
const subpaths = listPluginSdkExportedSubpaths({
|
|
modulePath: path.join(fixture.root, "src", "plugins", "loader.ts"),
|
|
});
|
|
expect(subpaths).toEqual(["compat", "telegram"]);
|
|
});
|
|
|
|
it("derives plugin-sdk subpaths from nearest package exports even when package name is renamed", () => {
|
|
const fixture = createPluginSdkAliasFixture({
|
|
packageName: "moltbot",
|
|
packageExports: {
|
|
"./plugin-sdk/core": { default: "./dist/plugin-sdk/core.js" },
|
|
"./plugin-sdk/channel-runtime": { default: "./dist/plugin-sdk/channel-runtime.js" },
|
|
"./plugin-sdk/compat": { default: "./dist/plugin-sdk/compat.js" },
|
|
},
|
|
});
|
|
const subpaths = listPluginSdkExportedSubpaths({
|
|
modulePath: path.join(fixture.root, "src", "plugins", "loader.ts"),
|
|
});
|
|
expect(subpaths).toEqual(["channel-runtime", "compat", "core"]);
|
|
});
|
|
|
|
it("derives plugin-sdk subpaths via cwd fallback when module path is a transpiler cache and package is renamed", () => {
|
|
const fixture = createPluginSdkAliasFixture({
|
|
packageName: "moltbot",
|
|
packageExports: {
|
|
"./plugin-sdk/core": { default: "./dist/plugin-sdk/core.js" },
|
|
"./plugin-sdk/channel-runtime": { default: "./dist/plugin-sdk/channel-runtime.js" },
|
|
},
|
|
});
|
|
const subpaths = withCwd(fixture.root, () =>
|
|
listPluginSdkExportedSubpaths({
|
|
modulePath: "/tmp/tsx-cache/openclaw-loader.js",
|
|
}),
|
|
);
|
|
expect(subpaths).toEqual(["channel-runtime", "core"]);
|
|
});
|
|
|
|
it("resolves plugin-sdk alias files via cwd fallback when module path is a transpiler cache and package is renamed", () => {
|
|
const fixture = createPluginSdkAliasFixture({
|
|
srcFile: "channel-runtime.ts",
|
|
distFile: "channel-runtime.js",
|
|
packageName: "moltbot",
|
|
packageExports: {
|
|
"./plugin-sdk/channel-runtime": { default: "./dist/plugin-sdk/channel-runtime.js" },
|
|
},
|
|
});
|
|
const resolved = withCwd(fixture.root, () =>
|
|
resolvePluginSdkAlias({
|
|
srcFile: "channel-runtime.ts",
|
|
distFile: "channel-runtime.js",
|
|
modulePath: "/tmp/tsx-cache/openclaw-loader.js",
|
|
env: { NODE_ENV: undefined },
|
|
}),
|
|
);
|
|
expect(resolved).not.toBeNull();
|
|
expect(fs.realpathSync(resolved ?? "")).toBe(fs.realpathSync(fixture.srcFile));
|
|
});
|
|
|
|
it("does not derive plugin-sdk subpaths from cwd fallback when package root is not an OpenClaw root", () => {
|
|
const fixture = createPluginSdkAliasFixture({
|
|
packageName: "moltbot",
|
|
trustedRootIndicators: false,
|
|
packageExports: {
|
|
"./plugin-sdk/core": { default: "./dist/plugin-sdk/core.js" },
|
|
"./plugin-sdk/channel-runtime": { default: "./dist/plugin-sdk/channel-runtime.js" },
|
|
},
|
|
});
|
|
const subpaths = withCwd(fixture.root, () =>
|
|
listPluginSdkExportedSubpaths({
|
|
modulePath: "/tmp/tsx-cache/openclaw-loader.js",
|
|
}),
|
|
);
|
|
expect(subpaths).toEqual([]);
|
|
});
|
|
|
|
it("derives plugin-sdk subpaths via cwd fallback when trusted root indicator is cli-entry export", () => {
|
|
const fixture = createPluginSdkAliasFixture({
|
|
packageName: "moltbot",
|
|
trustedRootIndicatorMode: "cli-entry-only",
|
|
packageExports: {
|
|
"./plugin-sdk/core": { default: "./dist/plugin-sdk/core.js" },
|
|
"./plugin-sdk/channel-runtime": { default: "./dist/plugin-sdk/channel-runtime.js" },
|
|
},
|
|
});
|
|
const subpaths = withCwd(fixture.root, () =>
|
|
listPluginSdkExportedSubpaths({
|
|
modulePath: "/tmp/tsx-cache/openclaw-loader.js",
|
|
}),
|
|
);
|
|
expect(subpaths).toEqual(["channel-runtime", "core"]);
|
|
});
|
|
|
|
it("builds plugin-sdk aliases from the module being loaded, not the loader location", () => {
|
|
const fixture = createPluginSdkAliasFixture({
|
|
srcFile: "channel-runtime.ts",
|
|
distFile: "channel-runtime.js",
|
|
packageExports: {
|
|
"./plugin-sdk/channel-runtime": { default: "./dist/plugin-sdk/channel-runtime.js" },
|
|
},
|
|
});
|
|
const sourceRootAlias = path.join(fixture.root, "src", "plugin-sdk", "root-alias.cjs");
|
|
const distRootAlias = path.join(fixture.root, "dist", "plugin-sdk", "root-alias.cjs");
|
|
fs.writeFileSync(sourceRootAlias, "module.exports = {};\n", "utf-8");
|
|
fs.writeFileSync(distRootAlias, "module.exports = {};\n", "utf-8");
|
|
const sourcePluginEntry = path.join(fixture.root, "extensions", "demo", "src", "index.ts");
|
|
fs.mkdirSync(path.dirname(sourcePluginEntry), { recursive: true });
|
|
fs.writeFileSync(sourcePluginEntry, 'export const plugin = "demo";\n', "utf-8");
|
|
|
|
const sourceAliases = withEnv({ NODE_ENV: undefined }, () =>
|
|
buildPluginLoaderAliasMap(sourcePluginEntry),
|
|
);
|
|
expect(fs.realpathSync(sourceAliases["openclaw/plugin-sdk"] ?? "")).toBe(
|
|
fs.realpathSync(sourceRootAlias),
|
|
);
|
|
expect(fs.realpathSync(sourceAliases["openclaw/plugin-sdk/channel-runtime"] ?? "")).toBe(
|
|
fs.realpathSync(path.join(fixture.root, "src", "plugin-sdk", "channel-runtime.ts")),
|
|
);
|
|
|
|
const distPluginEntry = path.join(fixture.root, "dist", "extensions", "demo", "index.js");
|
|
fs.mkdirSync(path.dirname(distPluginEntry), { recursive: true });
|
|
fs.writeFileSync(distPluginEntry, 'export const plugin = "demo";\n', "utf-8");
|
|
|
|
const distAliases = withEnv({ NODE_ENV: undefined }, () =>
|
|
buildPluginLoaderAliasMap(distPluginEntry),
|
|
);
|
|
expect(fs.realpathSync(distAliases["openclaw/plugin-sdk"] ?? "")).toBe(
|
|
fs.realpathSync(distRootAlias),
|
|
);
|
|
expect(fs.realpathSync(distAliases["openclaw/plugin-sdk/channel-runtime"] ?? "")).toBe(
|
|
fs.realpathSync(path.join(fixture.root, "dist", "plugin-sdk", "channel-runtime.js")),
|
|
);
|
|
});
|
|
|
|
it("does not resolve plugin-sdk alias files from cwd fallback when package root is not an OpenClaw root", () => {
|
|
const fixture = createPluginSdkAliasFixture({
|
|
srcFile: "channel-runtime.ts",
|
|
distFile: "channel-runtime.js",
|
|
packageName: "moltbot",
|
|
trustedRootIndicators: false,
|
|
packageExports: {
|
|
"./plugin-sdk/channel-runtime": { default: "./dist/plugin-sdk/channel-runtime.js" },
|
|
},
|
|
});
|
|
const resolved = withCwd(fixture.root, () =>
|
|
resolvePluginSdkAlias({
|
|
srcFile: "channel-runtime.ts",
|
|
distFile: "channel-runtime.js",
|
|
modulePath: "/tmp/tsx-cache/openclaw-loader.js",
|
|
env: { NODE_ENV: undefined },
|
|
}),
|
|
);
|
|
expect(resolved).toBeNull();
|
|
});
|
|
|
|
it("configures the plugin loader jiti boundary to prefer native dist modules", () => {
|
|
const options = buildPluginLoaderJitiOptions({});
|
|
|
|
expect(options.tryNative).toBe(true);
|
|
expect(options.interopDefault).toBe(true);
|
|
expect(options.extensions).toContain(".js");
|
|
expect(options.extensions).toContain(".ts");
|
|
expect("alias" in options).toBe(false);
|
|
});
|
|
|
|
it("uses transpiled Jiti loads for source TypeScript plugin entries", () => {
|
|
expect(shouldPreferNativeJiti("/repo/dist/plugins/runtime/index.js")).toBe(true);
|
|
expect(shouldPreferNativeJiti("/repo/extensions/discord/src/channel.runtime.ts")).toBe(false);
|
|
});
|
|
|
|
it("loads source runtime shims through the non-native Jiti boundary", async () => {
|
|
const copiedExtensionRoot = path.join(makeTempDir(), "extensions", "discord");
|
|
const copiedSourceDir = path.join(copiedExtensionRoot, "src");
|
|
const copiedPluginSdkDir = path.join(copiedExtensionRoot, "plugin-sdk");
|
|
mkdirSafe(copiedSourceDir);
|
|
mkdirSafe(copiedPluginSdkDir);
|
|
const jitiBaseFile = path.join(copiedSourceDir, "__jiti-base__.mjs");
|
|
fs.writeFileSync(jitiBaseFile, "export {};\n", "utf-8");
|
|
fs.writeFileSync(
|
|
path.join(copiedSourceDir, "channel.runtime.ts"),
|
|
`import { resolveOutboundSendDep } from "openclaw/plugin-sdk/infra-runtime";
|
|
|
|
export const syntheticRuntimeMarker = {
|
|
resolveOutboundSendDep,
|
|
};
|
|
`,
|
|
"utf-8",
|
|
);
|
|
const copiedChannelRuntimeShim = path.join(copiedPluginSdkDir, "infra-runtime.ts");
|
|
fs.writeFileSync(
|
|
copiedChannelRuntimeShim,
|
|
`export function resolveOutboundSendDep() {
|
|
return "shimmed";
|
|
}
|
|
`,
|
|
"utf-8",
|
|
);
|
|
const copiedChannelRuntime = path.join(copiedExtensionRoot, "src", "channel.runtime.ts");
|
|
const jitiBaseUrl = pathToFileURL(jitiBaseFile).href;
|
|
|
|
const createJiti = await getCreateJiti();
|
|
const withoutAlias = createJiti(jitiBaseUrl, {
|
|
...buildPluginLoaderJitiOptions({}),
|
|
tryNative: false,
|
|
});
|
|
expect(() => withoutAlias(copiedChannelRuntime)).toThrow();
|
|
|
|
const withAlias = createJiti(jitiBaseUrl, {
|
|
...buildPluginLoaderJitiOptions({
|
|
"openclaw/plugin-sdk/infra-runtime": copiedChannelRuntimeShim,
|
|
}),
|
|
tryNative: false,
|
|
});
|
|
expect(withAlias(copiedChannelRuntime)).toMatchObject({
|
|
syntheticRuntimeMarker: {
|
|
resolveOutboundSendDep: expect.any(Function),
|
|
},
|
|
});
|
|
}, 240_000);
|
|
|
|
it.each([
|
|
{
|
|
name: "prefers dist plugin runtime module when loader runs from dist",
|
|
modulePath: (root: string) => path.join(root, "dist", "plugins", "loader.js"),
|
|
expected: "dist" as const,
|
|
},
|
|
{
|
|
name: "resolves plugin runtime module from package root when loader runs from transpiler cache path",
|
|
modulePath: () => "/tmp/tsx-cache/openclaw-loader.js",
|
|
argv1: (root: string) => path.join(root, "openclaw.mjs"),
|
|
env: { NODE_ENV: undefined },
|
|
expected: "src" as const,
|
|
},
|
|
])("$name", ({ modulePath, argv1, env, expected }) => {
|
|
const fixture = createPluginRuntimeAliasFixture();
|
|
const resolved = resolvePluginRuntimeModule({
|
|
modulePath: modulePath(fixture.root),
|
|
argv1: argv1?.(fixture.root),
|
|
env,
|
|
});
|
|
expect(resolved).toBe(expected === "dist" ? fixture.distFile : fixture.srcFile);
|
|
});
|
|
});
|