fix: openclaw module loading (#993)
Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Haze <hazeone@users.noreply.github.com>
This commit is contained in:
104
tests/unit/openclaw-self-import-patch.test.ts
Normal file
104
tests/unit/openclaw-self-import-patch.test.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
// @vitest-environment node
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import {
|
||||
patchExtensionOpenClawSelfImports,
|
||||
rewriteOpenClawPluginSdkSpecifiers,
|
||||
toImportSpecifier,
|
||||
} from '../../scripts/openclaw-self-import-patch.mjs';
|
||||
|
||||
const tempRoots: string[] = [];
|
||||
|
||||
async function createTempOpenClawBundle(): Promise<string> {
|
||||
const root = await mkdtemp(path.join(tmpdir(), 'clawx-openclaw-self-import-'));
|
||||
tempRoots.push(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
||||
});
|
||||
|
||||
describe('openclaw self-import bundle patch', () => {
|
||||
it('converts OpenClaw plugin-sdk package specifiers to bundled relative paths', async () => {
|
||||
const root = await createTempOpenClawBundle();
|
||||
const distDir = path.join(root, 'dist');
|
||||
const pluginSdkDir = path.join(distDir, 'plugin-sdk');
|
||||
const extensionDir = path.join(distDir, 'extensions', 'codex');
|
||||
await mkdir(pluginSdkDir, { recursive: true });
|
||||
await mkdir(extensionDir, { recursive: true });
|
||||
await writeFile(path.join(pluginSdkDir, 'provider-model-shared.js'), 'export const ok = true;\n');
|
||||
|
||||
const promptOverlayPath = path.join(extensionDir, 'prompt-overlay.js');
|
||||
await writeFile(
|
||||
promptOverlayPath,
|
||||
[
|
||||
'import { ok } from "openclaw/plugin-sdk/provider-model-shared";',
|
||||
"export { ok };",
|
||||
'',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
const result = patchExtensionOpenClawSelfImports(root);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
filesPatched: 1,
|
||||
specifiersPatched: 1,
|
||||
});
|
||||
await expect(readFile(promptOverlayPath, 'utf8')).resolves.toContain(
|
||||
'from "../../plugin-sdk/provider-model-shared.js"',
|
||||
);
|
||||
});
|
||||
|
||||
it('leaves extension files without OpenClaw self-imports untouched', async () => {
|
||||
const root = await createTempOpenClawBundle();
|
||||
const extensionDir = path.join(root, 'dist', 'extensions', 'telegram');
|
||||
await mkdir(extensionDir, { recursive: true });
|
||||
|
||||
const filePath = path.join(extensionDir, 'runtime.js');
|
||||
const source = 'export const runtime = true;\n';
|
||||
await writeFile(filePath, source);
|
||||
|
||||
const result = patchExtensionOpenClawSelfImports(root);
|
||||
|
||||
expect(result.filesScanned).toBe(1);
|
||||
expect(result.filesPatched).toBe(0);
|
||||
expect(result.specifiersPatched).toBe(0);
|
||||
await expect(readFile(filePath, 'utf8')).resolves.toBe(source);
|
||||
});
|
||||
|
||||
it('throws when the bundled plugin-sdk target is missing', () => {
|
||||
const root = path.join(tmpdir(), 'missing-target-openclaw-bundle');
|
||||
const distDir = path.join(root, 'dist');
|
||||
const filePath = path.join(distDir, 'extensions', 'codex', 'prompt-overlay.js');
|
||||
|
||||
expect(() => rewriteOpenClawPluginSdkSpecifiers(
|
||||
'import "openclaw/plugin-sdk/provider-model-shared";',
|
||||
{ filePath, distDir },
|
||||
)).toThrow(/missing bundled SDK target/);
|
||||
});
|
||||
|
||||
it('formats same-directory import paths with an explicit relative prefix', () => {
|
||||
expect(toImportSpecifier('provider-model-shared.js')).toBe('./provider-model-shared.js');
|
||||
expect(toImportSpecifier('../plugin-sdk/provider-model-shared.js')).toBe(
|
||||
'../plugin-sdk/provider-model-shared.js',
|
||||
);
|
||||
});
|
||||
|
||||
it('returns an empty patch summary when the extensions directory is absent', async () => {
|
||||
const root = await createTempOpenClawBundle();
|
||||
await mkdir(path.join(root, 'dist', 'plugin-sdk'), { recursive: true });
|
||||
|
||||
const result = patchExtensionOpenClawSelfImports(root);
|
||||
|
||||
expect(existsSync(path.join(root, 'dist', 'extensions'))).toBe(false);
|
||||
expect(result).toEqual({
|
||||
filesScanned: 0,
|
||||
filesPatched: 0,
|
||||
specifiersPatched: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user