mirror of https://github.com/openclaw/openclaw.git
fix(matrix): package verification bootstrap runtime (#59249)
Merged via squash.
Prepared head SHA: df5891b663
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
parent
c87c8e66bf
commit
a204f790ce
|
|
@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Agents/compaction: add `agents.defaults.compaction.notifyUser` so the `🧹 Compacting context...` start notice is opt-in instead of always being shown. (#54251) Thanks @oguricap0327.
|
||||
- Plugins/hooks: add `before_agent_reply` so plugins can short-circuit the LLM with synthetic replies after inline actions. (#20067) thanks @JoshuaLelon
|
||||
- Providers/runtime: add provider-owned replay hook surfaces for transcript policy, replay cleanup, and reasoning-mode dispatch. (#59143) Thanks @jalehman.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Matrix/multi-account: keep room-level `account` scoping, inherited room overrides, and implicit account selection consistent across top-level default auth, named accounts, and cached-credential env setups. (#58449) thanks @Daanvdplas and @gumadeiras.
|
||||
|
|
@ -24,6 +25,7 @@ Docs: https://docs.openclaw.ai
|
|||
- BlueBubbles/config: accept `enrichGroupParticipantsFromContacts` in the core strict config schema so gateways no longer fail validation or startup when the BlueBubbles plugin writes that field. (#56889) Thanks @zqchris.
|
||||
- Agents/failover: classify AbortError and stream-abort messages as timeout so Ollama NDJSON stream aborts stop showing `reason=unknown` in model fallback logs. (#58324) Thanks @yelog
|
||||
- Exec approvals: route Slack, Discord, and Telegram approvals through the shared channel approval-capability path so native approval auth, delivery, and `/approve` handling stay aligned across channels while preserving Telegram session-key agent filtering. (#58634) thanks @gumadeiras
|
||||
- Matrix/runtime: resolve the verification/bootstrap runtime from a distinct packaged Matrix entry so global npm installs stop failing on crypto bootstrap with missing-module or recursive runtime alias errors. (#59249) Thanks @gumadeiras.
|
||||
|
||||
## 2026.4.2
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export * from "./src/plugin-entry.runtime.ts";
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// Thin ESM wrapper so native dynamic import() resolves in source-checkout mode
|
||||
// where jiti loads index.ts but import("./src/plugin-entry.runtime.js") uses
|
||||
// Node's native ESM loader which cannot resolve .ts files directly.
|
||||
// while packaged dist builds resolve a distinct runtime entry that cannot loop
|
||||
// back into this wrapper through the stable root runtime alias.
|
||||
import fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
|
|
@ -9,9 +9,11 @@ import { fileURLToPath } from "node:url";
|
|||
const require = createRequire(import.meta.url);
|
||||
const { createJiti } = require("jiti");
|
||||
|
||||
const PLUGIN_ID = "matrix";
|
||||
const OPENCLAW_PLUGIN_SDK_PREFIX = ["openclaw", "plugin-sdk"].join("/");
|
||||
const PLUGIN_SDK_EXPORT_PREFIX = "./plugin-sdk/";
|
||||
const PLUGIN_SDK_SOURCE_EXTENSIONS = [".ts", ".mts", ".js", ".mjs", ".cts", ".cjs"];
|
||||
const PLUGIN_ENTRY_RUNTIME_BASENAME = "plugin-entry.handlers.runtime";
|
||||
const JITI_EXTENSIONS = [
|
||||
".ts",
|
||||
".tsx",
|
||||
|
|
@ -102,6 +104,42 @@ function buildPluginSdkAliasMap(moduleUrl) {
|
|||
return aliasMap;
|
||||
}
|
||||
|
||||
function resolveBundledPluginRuntimeModulePath(moduleUrl, params) {
|
||||
const modulePath = fileURLToPath(moduleUrl);
|
||||
const moduleDir = path.dirname(modulePath);
|
||||
const localCandidates = [
|
||||
path.join(moduleDir, "..", params.runtimeBasename),
|
||||
path.join(moduleDir, "extensions", params.pluginId, params.runtimeBasename),
|
||||
];
|
||||
|
||||
for (const candidate of localCandidates) {
|
||||
const resolved = resolveExistingFile(candidate, PLUGIN_SDK_SOURCE_EXTENSIONS);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
|
||||
const location = findOpenClawPackageRoot(moduleDir);
|
||||
if (location) {
|
||||
const { packageRoot } = location;
|
||||
const packageCandidates = [
|
||||
path.join(packageRoot, "extensions", params.pluginId, params.runtimeBasename),
|
||||
path.join(packageRoot, "dist", "extensions", params.pluginId, params.runtimeBasename),
|
||||
];
|
||||
|
||||
for (const candidate of packageCandidates) {
|
||||
const resolved = resolveExistingFile(candidate, PLUGIN_SDK_SOURCE_EXTENSIONS);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Cannot resolve ${params.pluginId} plugin runtime module ${params.runtimeBasename} from ${modulePath}`,
|
||||
);
|
||||
}
|
||||
|
||||
const jiti = createJiti(import.meta.url, {
|
||||
alias: buildPluginSdkAliasMap(import.meta.url),
|
||||
interopDefault: true,
|
||||
|
|
@ -109,7 +147,12 @@ const jiti = createJiti(import.meta.url, {
|
|||
extensions: JITI_EXTENSIONS,
|
||||
});
|
||||
|
||||
const mod = jiti("./plugin-entry.runtime.ts");
|
||||
const mod = jiti(
|
||||
resolveBundledPluginRuntimeModulePath(import.meta.url, {
|
||||
pluginId: PLUGIN_ID,
|
||||
runtimeBasename: PLUGIN_ENTRY_RUNTIME_BASENAME,
|
||||
}),
|
||||
);
|
||||
export const ensureMatrixCryptoRuntime = mod.ensureMatrixCryptoRuntime;
|
||||
export const handleVerifyRecoveryKey = mod.handleVerifyRecoveryKey;
|
||||
export const handleVerificationBootstrap = mod.handleVerificationBootstrap;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,39 @@
|
|||
import fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { expect, it } from "vitest";
|
||||
import { afterEach, expect, it } from "vitest";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
const REPO_ROOT = process.cwd();
|
||||
const require = createRequire(import.meta.url);
|
||||
const JITI_ENTRY_PATH = require.resolve("jiti");
|
||||
const PACKAGED_RUNTIME_STUB = [
|
||||
"export async function ensureMatrixCryptoRuntime() {}",
|
||||
"export async function handleVerifyRecoveryKey() {}",
|
||||
"export async function handleVerificationBootstrap() {}",
|
||||
"export async function handleVerificationStatus() {}",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
function makeFixtureRoot(prefix: string) {
|
||||
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
tempDirs.push(fixtureRoot);
|
||||
return fixtureRoot;
|
||||
}
|
||||
|
||||
function writeFixtureFile(fixtureRoot: string, relativePath: string, value: string) {
|
||||
const fullPath = path.join(fixtureRoot, relativePath);
|
||||
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
||||
fs.writeFileSync(fullPath, value, "utf8");
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs.splice(0, tempDirs.length)) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("loads the plugin-entry runtime wrapper through native ESM import", async () => {
|
||||
const wrapperPath = path.join(
|
||||
|
|
@ -20,3 +53,56 @@ it("loads the plugin-entry runtime wrapper through native ESM import", async ()
|
|||
handleVerificationStatus: expect.any(Function),
|
||||
});
|
||||
}, 240_000);
|
||||
|
||||
it("loads the packaged runtime wrapper without recursing through the stable root alias", async () => {
|
||||
const fixtureRoot = makeFixtureRoot(".tmp-matrix-runtime-");
|
||||
const wrapperSource = fs.readFileSync(
|
||||
path.join(REPO_ROOT, "extensions", "matrix", "src", "plugin-entry.runtime.js"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
writeFixtureFile(
|
||||
fixtureRoot,
|
||||
"package.json",
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "openclaw",
|
||||
type: "module",
|
||||
exports: {
|
||||
"./plugin-sdk": "./dist/plugin-sdk/index.js",
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + "\n",
|
||||
);
|
||||
writeFixtureFile(fixtureRoot, "dist/plugin-sdk/index.js", "export {};\n");
|
||||
writeFixtureFile(
|
||||
fixtureRoot,
|
||||
"node_modules/jiti/index.js",
|
||||
`module.exports = require(${JSON.stringify(JITI_ENTRY_PATH)});\n`,
|
||||
);
|
||||
writeFixtureFile(fixtureRoot, "dist/plugin-entry.runtime-C88YIa_v.js", wrapperSource);
|
||||
writeFixtureFile(
|
||||
fixtureRoot,
|
||||
"dist/plugin-entry.runtime.js",
|
||||
'export * from "./plugin-entry.runtime-C88YIa_v.js";\n',
|
||||
);
|
||||
writeFixtureFile(
|
||||
fixtureRoot,
|
||||
"dist/extensions/matrix/plugin-entry.handlers.runtime.js",
|
||||
PACKAGED_RUNTIME_STUB,
|
||||
);
|
||||
|
||||
const wrapperUrl = pathToFileURL(
|
||||
path.join(fixtureRoot, "dist", "plugin-entry.runtime-C88YIa_v.js"),
|
||||
);
|
||||
const mod = await import(`${wrapperUrl.href}?t=${Date.now()}`);
|
||||
|
||||
expect(mod).toMatchObject({
|
||||
ensureMatrixCryptoRuntime: expect.any(Function),
|
||||
handleVerifyRecoveryKey: expect.any(Function),
|
||||
handleVerificationBootstrap: expect.any(Function),
|
||||
handleVerificationStatus: expect.any(Function),
|
||||
});
|
||||
}, 240_000);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,15 @@ describe("bundled plugin build entries", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("keeps the Matrix packaged runtime shim in bundled plugin build entries", () => {
|
||||
const entries = listBundledPluginBuildEntries();
|
||||
|
||||
expect(entries).toMatchObject({
|
||||
"extensions/matrix/plugin-entry.handlers.runtime":
|
||||
"extensions/matrix/plugin-entry.handlers.runtime.ts",
|
||||
});
|
||||
});
|
||||
|
||||
it("packs runtime core support packages without requiring plugin manifests", () => {
|
||||
const artifacts = listBundledPluginPackArtifacts();
|
||||
|
||||
|
|
@ -32,4 +41,10 @@ describe("bundled plugin build entries", () => {
|
|||
expect(artifacts).toContain("dist/extensions/speech-core/runtime-api.js");
|
||||
expect(artifacts).not.toContain("dist/extensions/speech-core/openclaw.plugin.json");
|
||||
});
|
||||
|
||||
it("packs the Matrix packaged runtime shim", () => {
|
||||
const artifacts = listBundledPluginPackArtifacts();
|
||||
|
||||
expect(artifacts).toContain("dist/extensions/matrix/plugin-entry.handlers.runtime.js");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue