mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-30 09:11:20 +00:00
Plugins: clarify registerHttpHandler migration errors (#36794)
* Changelog: note plugin HTTP route migration diagnostics * Tests: cover registerHttpHandler migration diagnostics * Plugins: clarify registerHttpHandler migration errors * Tests: cover registerHttpHandler diagnostic edge cases * Plugins: tighten registerHttpHandler migration hint
This commit is contained in:
@@ -164,12 +164,11 @@ Docs: https://docs.openclaw.ai
|
||||
- LINE cleanup/test follow-ups: fold cleanup/test learnings into the synthesis review path while keeping runtime changes focused on regression fixes. (from #17630, #17289) Thanks @Clawborn and @davidahmann.
|
||||
- Mattermost/interactive buttons: add interactive button send/callback support with directory-based channel/user target resolution, and harden callbacks via account-scoped HMAC verification plus sender-scoped DM routing. (#19957) thanks @tonydehnke.
|
||||
- Feishu/groupPolicy legacy alias compatibility: treat legacy `groupPolicy: "allowall"` as `open` in both schema parsing and runtime policy checks so intended open-group configs no longer silently drop group messages when `groupAllowFrom` is empty. (from #36358) Thanks @Sid-Qin.
|
||||
|
||||
- Mattermost/plugin SDK import policy: replace remaining monolithic `openclaw/plugin-sdk` imports in Mattermost mention-gating paths/tests with scoped subpaths (`openclaw/plugin-sdk/compat` and `openclaw/plugin-sdk/mattermost`) so `pnpm check` passes `lint:plugins:no-monolithic-plugin-sdk-entry-imports` on baseline. (#36480) Thanks @Takhoffman.
|
||||
- Telegram/polls: add Telegram poll action support to channel action discovery and tool/CLI poll flows, with multi-account discoverability gated to accounts that can actually execute polls (`sendMessage` + `poll`). (#36547) thanks @gumadeiras.
|
||||
|
||||
- Agents/failover cooldown classification: stop treating generic `cooling down` text as provider `rate_limit` so healthy models no longer show false global cooldown/rate-limit warnings while explicit `model_cooldown` markers still trigger failover. (#32972) thanks @stakeswky.
|
||||
- Agents/failover service-unavailable handling: stop treating bare proxy/CDN `service unavailable` errors as provider overload while keeping them retryable via the timeout/failover path, so transient outages no longer show false rate-limit warnings or block fallback. (#36646) thanks @jnMetaCode.
|
||||
- Plugins/HTTP route migration diagnostics: rewrite legacy `api.registerHttpHandler(...)` loader failures into actionable migration guidance so doctor/plugin diagnostics point operators to `api.registerHttpRoute(...)` or `registerPluginHttpRoute(...)`. (#36794) Thanks @vincentkoc
|
||||
- Doctor/Heartbeat upgrade diagnostics: warn when heartbeat delivery is configured with an implicit `directPolicy` so upgrades pin direct/DM behavior explicitly instead of relying on the current default. (#36789) Thanks @vincentkoc.
|
||||
- Agents/current-time UTC anchor: append a machine-readable UTC suffix alongside local `Current time:` lines in shared cron-style prompt contexts so agents can compare UTC-stamped workspace timestamps without doing timezone math. (#32423) thanks @jriff.
|
||||
- TUI/webchat command-owner scope alignment: treat internal-channel gateway sessions with `operator.admin` as owner-authorized in command auth, restoring cron/gateway/connector tool access for affected TUI/webchat sessions while keeping external channels on identity-based owner checks. (from #35666, #35673, #35704) Thanks @Naylenv, @Octane0411, and @Sid-Qin.
|
||||
|
||||
@@ -191,6 +191,15 @@ function createWarningLogger(warnings: string[]) {
|
||||
};
|
||||
}
|
||||
|
||||
function createErrorLogger(errors: string[]) {
|
||||
return {
|
||||
info: () => {},
|
||||
warn: () => {},
|
||||
error: (msg: string) => errors.push(msg),
|
||||
debug: () => {},
|
||||
};
|
||||
}
|
||||
|
||||
function createEscapingEntryFixture(params: { id: string; sourceBody: string }) {
|
||||
const pluginDir = makeTempDir();
|
||||
const outsideDir = makeTempDir();
|
||||
@@ -574,6 +583,65 @@ describe("loadOpenClawPlugins", () => {
|
||||
expect(httpPlugin?.httpRoutes).toBe(1);
|
||||
});
|
||||
|
||||
it("rewrites removed registerHttpHandler failures into migration diagnostics", () => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
id: "http-handler-legacy",
|
||||
filename: "http-handler-legacy.cjs",
|
||||
body: `module.exports = { id: "http-handler-legacy", register(api) {
|
||||
api.registerHttpHandler({ path: "/legacy", handler: async () => true });
|
||||
} };`,
|
||||
});
|
||||
|
||||
const errors: string[] = [];
|
||||
const registry = loadRegistryFromSinglePlugin({
|
||||
plugin,
|
||||
pluginConfig: {
|
||||
allow: ["http-handler-legacy"],
|
||||
},
|
||||
options: {
|
||||
logger: createErrorLogger(errors),
|
||||
},
|
||||
});
|
||||
|
||||
const loaded = registry.plugins.find((entry) => entry.id === "http-handler-legacy");
|
||||
expect(loaded?.status).toBe("error");
|
||||
expect(loaded?.error).toContain("api.registerHttpHandler(...) was removed");
|
||||
expect(loaded?.error).toContain("api.registerHttpRoute(...)");
|
||||
expect(loaded?.error).toContain("registerPluginHttpRoute(...)");
|
||||
expect(
|
||||
registry.diagnostics.some((diag) =>
|
||||
String(diag.message).includes("api.registerHttpHandler(...) was removed"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(errors.some((entry) => entry.includes("api.registerHttpHandler(...) was removed"))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not rewrite unrelated registerHttpHandler helper failures", () => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
id: "http-handler-local-helper",
|
||||
filename: "http-handler-local-helper.cjs",
|
||||
body: `module.exports = { id: "http-handler-local-helper", register() {
|
||||
const registerHttpHandler = undefined;
|
||||
registerHttpHandler();
|
||||
} };`,
|
||||
});
|
||||
|
||||
const registry = loadRegistryFromSinglePlugin({
|
||||
plugin,
|
||||
pluginConfig: {
|
||||
allow: ["http-handler-local-helper"],
|
||||
},
|
||||
});
|
||||
|
||||
const loaded = registry.plugins.find((entry) => entry.id === "http-handler-local-helper");
|
||||
expect(loaded?.status).toBe("error");
|
||||
expect(loaded?.error).not.toContain("api.registerHttpHandler(...) was removed");
|
||||
});
|
||||
|
||||
it("rejects plugin http routes missing explicit auth", () => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
|
||||
@@ -297,16 +297,21 @@ function recordPluginError(params: {
|
||||
diagnosticMessagePrefix: string;
|
||||
}) {
|
||||
const errorText = String(params.error);
|
||||
params.logger.error(`${params.logPrefix}${errorText}`);
|
||||
const deprecatedApiHint =
|
||||
errorText.includes("api.registerHttpHandler") && errorText.includes("is not a function")
|
||||
? "deprecated api.registerHttpHandler(...) was removed; use api.registerHttpRoute(...) for plugin-owned routes or registerPluginHttpRoute(...) for dynamic lifecycle routes"
|
||||
: null;
|
||||
const displayError = deprecatedApiHint ? `${deprecatedApiHint} (${errorText})` : errorText;
|
||||
params.logger.error(`${params.logPrefix}${displayError}`);
|
||||
params.record.status = "error";
|
||||
params.record.error = errorText;
|
||||
params.record.error = displayError;
|
||||
params.registry.plugins.push(params.record);
|
||||
params.seenIds.set(params.pluginId, params.origin);
|
||||
params.registry.diagnostics.push({
|
||||
level: "error",
|
||||
pluginId: params.record.id,
|
||||
source: params.record.source,
|
||||
message: `${params.diagnosticMessagePrefix}${errorText}`,
|
||||
message: `${params.diagnosticMessagePrefix}${displayError}`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user