perf: speed up telegram channel registration (#69786)

Merged via squash.

Prepared head SHA: ac03f96e0d
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:
Gustavo Madeira Santana
2026-04-21 13:24:28 -04:00
committed by GitHub
parent 5e72e39c18
commit ddc1d9aa54
8 changed files with 80 additions and 48 deletions

View File

@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
- Ollama/onboard: populate the cloud-only model list from `ollama.com/api/tags` so `openclaw onboard` reflects the live cloud catalog instead of a static three-model seed; cap the discovered list at 500 and fall back to the previous hardcoded suggestions when ollama.com is unreachable or returns no models. (#68463) Thanks @BruceMacD.
- Matrix/startup: narrow Matrix runtime registration and defer setup/doctor surfaces so cold plugin registration spends about 1.8s less in `setChannelRuntime`. (#69782) Thanks @gumadeiras.
- QQBot: extract a self-contained `engine/` architecture with QR-code onboarding, native approval handling via `/bot-approve`, per-account isolated resource stacks and multi-account logger, credential backup/restore, shared `~/.openclaw/media` payload root, and unified API/bridge/gateway modules. (#67960) Thanks @cxyhhhhh.
- Telegram/plugin startup: load Telegram's bundled runtime setter through a narrow sidecar and let built sidecars use native loading before falling back to jiti, cutting the measured setup-runtime registration path by about 14s while preserving runtime API compatibility. (#69786) thanks @gumadeiras.
### Fixes

View File

@@ -14,7 +14,7 @@ export default defineBundledChannelEntry({
exportName: "channelSecrets",
},
runtime: {
specifier: "./runtime-api.js",
specifier: "./runtime-setter-api.js",
exportName: "setTelegramRuntime",
},
accountInspect: {

View File

@@ -0,0 +1,3 @@
// Keep bundled registration fast: the runtime setter is needed during plugin
// bootstrap, but the broad runtime-api barrel is only for compatibility callers.
export { setTelegramRuntime } from "./src/runtime.js";

View File

@@ -15,6 +15,7 @@
"dist/extensions/lobster/runtime-api.js",
"dist/extensions/matrix/helper-api.js",
"dist/extensions/matrix/runtime-api.js",
"dist/extensions/matrix/runtime-setter-api.js",
"dist/extensions/matrix/thread-bindings-runtime.js",
"dist/extensions/mattermost/runtime-api.js",
"dist/extensions/memory-core/runtime-api.js",
@@ -27,6 +28,7 @@
"dist/extensions/signal/runtime-api.js",
"dist/extensions/slack/runtime-api.js",
"dist/extensions/telegram/runtime-api.js",
"dist/extensions/telegram/runtime-setter-api.js",
"dist/extensions/tlon/runtime-api.js",
"dist/extensions/twitch/runtime-api.js",
"dist/extensions/voice-call/runtime-api.js",

View File

@@ -17,6 +17,50 @@ afterEach(() => {
vi.unstubAllEnvs();
});
async function expectBuiltArtifactNodeRequireFastPath(
scope: string,
artifactRoot = "dist",
): Promise<void> {
vi.stubEnv("OPENCLAW_PLUGIN_LOAD_PROFILE", "1");
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
try {
const channelEntryContract = await importFreshModule<
typeof import("./channel-entry-contract.js")
>(import.meta.url, `./channel-entry-contract.js?scope=${scope}`);
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-entry-contract-"));
tempDirs.push(tempRoot);
const pluginRoot = path.join(tempRoot, artifactRoot, "extensions", "telegram");
fs.mkdirSync(pluginRoot, { recursive: true });
const importerPath = path.join(pluginRoot, "index.js");
const sidecarPath = path.join(pluginRoot, "fast-path-sidecar.js");
fs.writeFileSync(importerPath, "export default {};\n", "utf8");
// CommonJS so `nodeRequire` succeeds without falling back to jiti.
fs.writeFileSync(sidecarPath, "module.exports = { sentinel: 7 };\n", "utf8");
expect(
channelEntryContract.loadBundledEntryExportSync<number>(pathToFileURL(importerPath).href, {
specifier: "./fast-path-sidecar.js",
exportName: "sentinel",
}),
).toBe(7);
const profileLine = errorSpy.mock.calls
.map((args) => String(args[0] ?? ""))
.find((line) => line.startsWith("[plugin-load-profile] phase=bundled-entry-module-load"));
expect(profileLine, "expected a bundled-entry-module-load profile line").toBeDefined();
expect(profileLine).toContain("getJitiMs=0.0");
expect(profileLine).toContain("jitiCallMs=0.0");
expect(profileLine).not.toMatch(/getJitiMs=-/);
expect(profileLine).not.toMatch(/jitiCallMs=-/);
} finally {
errorSpy.mockRestore();
}
}
describe("loadBundledEntryExportSync", () => {
it("includes importer and resolved path context when a bundled sidecar is missing", () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-entry-contract-"));
@@ -133,51 +177,16 @@ describe("loadBundledEntryExportSync", () => {
});
});
it("emits zero jiti sub-step timings on the Win32 nodeRequire fast-path", async () => {
// The Win32 fast-path goes through `nodeRequire` directly and never
it("emits zero jiti sub-step timings on the built-artifact nodeRequire fast-path", async () => {
// The built-artifact fast-path goes through `nodeRequire` directly and never
// touches jiti. The plugin-load-profile line must reflect that with
// `getJitiMs=0.0 jitiCallMs=0.0` rather than negative or full-elapsed
// values that would mis-attribute nodeRequire time to jiti sub-steps.
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
vi.stubEnv("OPENCLAW_PLUGIN_LOAD_PROFILE", "1");
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
await expectBuiltArtifactNodeRequireFastPath("built-artifact-profile-fast-path");
});
try {
const channelEntryContract = await importFreshModule<
typeof import("./channel-entry-contract.js")
>(import.meta.url, "./channel-entry-contract.js?scope=win32-profile-fast-path");
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-entry-contract-"));
tempDirs.push(tempRoot);
const pluginRoot = path.join(tempRoot, "dist", "extensions", "telegram");
fs.mkdirSync(pluginRoot, { recursive: true });
const importerPath = path.join(pluginRoot, "index.js");
const sidecarPath = path.join(pluginRoot, "fast-path-sidecar.js");
fs.writeFileSync(importerPath, "export default {};\n", "utf8");
// CommonJS so `nodeRequire` succeeds without falling back to jiti.
fs.writeFileSync(sidecarPath, "module.exports = { sentinel: 7 };\n", "utf8");
expect(
channelEntryContract.loadBundledEntryExportSync<number>(pathToFileURL(importerPath).href, {
specifier: "./fast-path-sidecar.js",
exportName: "sentinel",
}),
).toBe(7);
const profileLine = errorSpy.mock.calls
.map((args) => String(args[0] ?? ""))
.find((line) => line.startsWith("[plugin-load-profile] phase=bundled-entry-module-load"));
expect(profileLine, "expected a bundled-entry-module-load profile line").toBeDefined();
expect(profileLine).toContain("getJitiMs=0.0");
expect(profileLine).toContain("jitiCallMs=0.0");
expect(profileLine).not.toMatch(/getJitiMs=-/);
expect(profileLine).not.toMatch(/jitiCallMs=-/);
} finally {
errorSpy.mockRestore();
platformSpy.mockRestore();
}
it("keeps dist-runtime built sidecar loads on the nodeRequire fast-path", async () => {
await expectBuiltArtifactNodeRequireFastPath("dist-runtime-profile-fast-path", "dist-runtime");
});
it("can disable source-tree fallback for dist bundled entry checks", () => {

View File

@@ -316,6 +316,16 @@ function getJiti(modulePath: string) {
});
}
function canTryNodeRequireBuiltModule(modulePath: string): boolean {
const isBuiltBundledArtifact =
modulePath.includes(`${path.sep}dist${path.sep}`) ||
modulePath.includes(`${path.sep}dist-runtime${path.sep}`);
return (
isBuiltBundledArtifact &&
[".js", ".mjs", ".cjs"].includes(normalizeLowercaseStringOrEmpty(path.extname(modulePath)))
);
}
function loadBundledEntryModuleSync(importMetaUrl: string, specifier: string): unknown {
const modulePath = resolveBundledEntryModulePath(importMetaUrl, specifier);
const cached = loadedModuleExports.get(modulePath);
@@ -326,11 +336,7 @@ function loadBundledEntryModuleSync(importMetaUrl: string, specifier: string): u
const profile = shouldProfilePluginLoader();
const loadStartMs = profile ? performance.now() : 0;
let getJitiEndMs = 0;
if (
process.platform === "win32" &&
modulePath.includes(`${path.sep}dist${path.sep}`) &&
[".js", ".mjs", ".cjs"].includes(normalizeLowercaseStringOrEmpty(path.extname(modulePath)))
) {
if (canTryNodeRequireBuiltModule(modulePath)) {
try {
loaded = nodeRequire(modulePath);
} catch {
@@ -355,7 +361,7 @@ function loadBundledEntryModuleSync(importMetaUrl: string, specifier: string): u
pluginId: "(bundled-entry)",
source: modulePath,
elapsedMs: endMs - loadStartMs,
// When the Win32 fast-path resolves the module via `nodeRequire`,
// When the built-artifact fast-path resolves the module via `nodeRequire`,
// `getJitiEndMs` stays `0` because the `catch` block (the only place
// it gets stamped) never runs. Reporting `getJitiMs` /
// `jitiCallMs` as `0` for that path keeps the breakdown honest:

View File

@@ -165,6 +165,16 @@ describe("bundled plugin metadata", () => {
});
});
it("keeps Telegram's narrow runtime setter on the bundled runtime sidecar surface", () => {
const telegram = listRepoBundledPluginMetadata().find((entry) => entry.dirName === "telegram");
expectArtifactPresence(telegram?.publicSurfaceArtifacts, {
contains: ["runtime-setter-api.js"],
});
expectArtifactPresence(telegram?.runtimeSidecarArtifacts, {
contains: ["runtime-setter-api.js"],
});
});
it("loads tlon channel config metadata from the lightweight schema surface", () => {
expect(collectRepoBundledChannelConfigsForTest("tlon")?.tlon).toEqual(
expect.objectContaining({

View File

@@ -8,6 +8,7 @@ const RUNTIME_SIDECAR_ARTIFACTS = new Set([
"helper-api.js",
"light-runtime-api.js",
"runtime-api.js",
"runtime-setter-api.js",
"thread-bindings-runtime.js",
]);