mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
fix(plugins): harden inspector runtime capture
This commit is contained in:
@@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Plugins/inspector: keep bundled plugin runtime capture quiet and config-tolerant for Codex, memory-lancedb, Feishu, QQBot, and Tlon so plugin-inspector JSON checks can validate the full bundled set. Thanks @vincentkoc.
|
||||
- Gateway/startup: start chat channels without waiting for primary model prewarm, keeping model warmup bounded in the background so Slack and other channels come online promptly when provider discovery is slow. Supersedes #73420. Thanks @dorukardahan.
|
||||
- Gateway/install: carry env-backed config SecretRefs such as `channels.discord.token` into generated service environments when they are present only in the installing shell, while keeping gateway auth SecretRefs non-persisted. Fixes #67817; supersedes #73426. Thanks @wdimaculangan and @ztexydt-cqh.
|
||||
- Auto-reply/commands: stop bare `/reset` and `/new` after reset hooks acknowledge the command, so non-ACP channels no longer fall through into empty provider calls while `/reset <message>` and `/new <message>` still seed the next model turn. Fixes #73367 and #73412. Thanks @hoyanhan, @wenxu007, and @amdhelper.
|
||||
|
||||
@@ -59,6 +59,27 @@ describe("codex plugin", () => {
|
||||
expect(onConversationBindingResolved).toHaveBeenCalledWith(expect.any(Function));
|
||||
});
|
||||
|
||||
it("registers with capture APIs that do not expose conversation binding hooks yet", () => {
|
||||
const api = createTestPluginApi({
|
||||
id: "codex",
|
||||
name: "Codex",
|
||||
source: "test",
|
||||
config: {},
|
||||
pluginConfig: {},
|
||||
runtime: {} as never,
|
||||
registerAgentHarness: vi.fn(),
|
||||
registerCommand: vi.fn(),
|
||||
registerMediaUnderstandingProvider: vi.fn(),
|
||||
registerProvider: vi.fn(),
|
||||
on: vi.fn(),
|
||||
}) as ReturnType<typeof createTestPluginApi> & {
|
||||
onConversationBindingResolved?: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
delete api.onConversationBindingResolved;
|
||||
|
||||
expect(() => plugin.register(api)).not.toThrow();
|
||||
});
|
||||
|
||||
it("only claims the codex provider by default", () => {
|
||||
const harness = createCodexAppServerAgentHarness();
|
||||
|
||||
|
||||
@@ -34,6 +34,6 @@ export default definePluginEntry({
|
||||
pluginConfig: resolveCurrentPluginConfig(),
|
||||
}),
|
||||
);
|
||||
api.onConversationBindingResolved(handleCodexConversationBindingResolved);
|
||||
api.onConversationBindingResolved?.(handleCodexConversationBindingResolved);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -543,13 +543,11 @@ const UpdateRecordSchema = Type.Object({
|
||||
|
||||
export function registerFeishuBitableTools(api: OpenClawPluginApi) {
|
||||
if (!api.config) {
|
||||
api.logger.debug?.("feishu_bitable: No config available, skipping bitable tools");
|
||||
return;
|
||||
}
|
||||
|
||||
const accounts = listEnabledFeishuAccounts(api.config);
|
||||
if (accounts.length === 0) {
|
||||
api.logger.debug?.("feishu_bitable: No Feishu accounts configured, skipping bitable tools");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -732,6 +730,4 @@ export function registerFeishuBitableTools(api: OpenClawPluginApi) {
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
api.logger.debug?.("feishu_bitable: Registered bitable tools");
|
||||
}
|
||||
|
||||
@@ -123,20 +123,17 @@ export async function getFeishuMemberInfo(
|
||||
|
||||
export function registerFeishuChatTools(api: OpenClawPluginApi) {
|
||||
if (!api.config) {
|
||||
api.logger.debug?.("feishu_chat: No config available, skipping chat tools");
|
||||
return;
|
||||
}
|
||||
|
||||
const accounts = listEnabledFeishuAccounts(api.config);
|
||||
if (accounts.length === 0) {
|
||||
api.logger.debug?.("feishu_chat: No Feishu accounts configured, skipping chat tools");
|
||||
return;
|
||||
}
|
||||
|
||||
const firstAccount = accounts[0];
|
||||
const toolsCfg = resolveToolsConfig(firstAccount.config.tools);
|
||||
if (!toolsCfg.chat) {
|
||||
api.logger.debug?.("feishu_chat: chat tool disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -188,6 +185,4 @@ export function registerFeishuChatTools(api: OpenClawPluginApi) {
|
||||
},
|
||||
{ name: "feishu_chat" },
|
||||
);
|
||||
|
||||
api.logger.debug?.("feishu_chat: Registered feishu_chat tool");
|
||||
}
|
||||
|
||||
@@ -1386,14 +1386,12 @@ async function listAppScopes(client: Lark.Client) {
|
||||
|
||||
export function registerFeishuDocTools(api: OpenClawPluginApi) {
|
||||
if (!api.config) {
|
||||
api.logger.debug?.("feishu_doc: No config available, skipping doc tools");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if any account is configured
|
||||
const accounts = listEnabledFeishuAccounts(api.config);
|
||||
if (accounts.length === 0) {
|
||||
api.logger.debug?.("feishu_doc: No Feishu accounts configured, skipping doc tools");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1615,8 +1613,4 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
|
||||
);
|
||||
registered.push("feishu_app_scopes");
|
||||
}
|
||||
|
||||
if (registered.length > 0) {
|
||||
api.logger.debug?.(`feishu_doc: Registered ${registered.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -733,19 +733,16 @@ export async function deliverCommentThreadText(
|
||||
|
||||
export function registerFeishuDriveTools(api: OpenClawPluginApi) {
|
||||
if (!api.config) {
|
||||
api.logger.debug?.("feishu_drive: No config available, skipping drive tools");
|
||||
return;
|
||||
}
|
||||
|
||||
const accounts = listEnabledFeishuAccounts(api.config);
|
||||
if (accounts.length === 0) {
|
||||
api.logger.debug?.("feishu_drive: No Feishu accounts configured, skipping drive tools");
|
||||
return;
|
||||
}
|
||||
|
||||
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
|
||||
if (!toolsCfg.drive) {
|
||||
api.logger.debug?.("feishu_drive: drive tool disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -829,6 +826,4 @@ export function registerFeishuDriveTools(api: OpenClawPluginApi) {
|
||||
},
|
||||
{ name: "feishu_drive" },
|
||||
);
|
||||
|
||||
api.logger.debug?.(`feishu_drive: Registered feishu_drive tool`);
|
||||
}
|
||||
|
||||
@@ -114,19 +114,16 @@ async function removeMember(
|
||||
|
||||
export function registerFeishuPermTools(api: OpenClawPluginApi) {
|
||||
if (!api.config) {
|
||||
api.logger.debug?.("feishu_perm: No config available, skipping perm tools");
|
||||
return;
|
||||
}
|
||||
|
||||
const accounts = listEnabledFeishuAccounts(api.config);
|
||||
if (accounts.length === 0) {
|
||||
api.logger.debug?.("feishu_perm: No Feishu accounts configured, skipping perm tools");
|
||||
return;
|
||||
}
|
||||
|
||||
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
|
||||
if (!toolsCfg.perm) {
|
||||
api.logger.debug?.("feishu_perm: perm tool disabled in config (default: false)");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,6 +167,4 @@ export function registerFeishuPermTools(api: OpenClawPluginApi) {
|
||||
},
|
||||
{ name: "feishu_perm" },
|
||||
);
|
||||
|
||||
api.logger.debug?.(`feishu_perm: Registered feishu_perm tool`);
|
||||
}
|
||||
|
||||
@@ -153,19 +153,16 @@ async function renameNode(client: Lark.Client, spaceId: string, nodeToken: strin
|
||||
|
||||
export function registerFeishuWikiTools(api: OpenClawPluginApi) {
|
||||
if (!api.config) {
|
||||
api.logger.debug?.("feishu_wiki: No config available, skipping wiki tools");
|
||||
return;
|
||||
}
|
||||
|
||||
const accounts = listEnabledFeishuAccounts(api.config);
|
||||
if (accounts.length === 0) {
|
||||
api.logger.debug?.("feishu_wiki: No Feishu accounts configured, skipping wiki tools");
|
||||
return;
|
||||
}
|
||||
|
||||
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
|
||||
if (!toolsCfg.wiki) {
|
||||
api.logger.debug?.("feishu_wiki: wiki tool disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -227,6 +224,4 @@ export function registerFeishuWikiTools(api: OpenClawPluginApi) {
|
||||
},
|
||||
{ name: "feishu_wiki" },
|
||||
);
|
||||
|
||||
api.logger.debug?.(`feishu_wiki: Registered feishu_wiki tool`);
|
||||
}
|
||||
|
||||
@@ -202,6 +202,43 @@ describe("memory plugin e2e", () => {
|
||||
expect(config?.autoRecall).toBe(true);
|
||||
});
|
||||
|
||||
test("registers as disabled instead of throwing when inspected without config", async () => {
|
||||
const registerService = vi.fn();
|
||||
const logger = {
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
};
|
||||
const mockApi = {
|
||||
id: "memory-lancedb",
|
||||
name: "Memory (LanceDB)",
|
||||
source: "test",
|
||||
config: {},
|
||||
pluginConfig: {},
|
||||
runtime: {},
|
||||
logger,
|
||||
registerTool: vi.fn(),
|
||||
registerCli: vi.fn(),
|
||||
registerService,
|
||||
on: vi.fn(),
|
||||
resolvePath: (filePath: string) => filePath,
|
||||
};
|
||||
|
||||
expect(() => memoryPlugin.register(mockApi as any)).not.toThrow();
|
||||
expect(registerService).toHaveBeenCalledWith({
|
||||
id: "memory-lancedb",
|
||||
start: expect.any(Function),
|
||||
});
|
||||
expect(mockApi.registerTool).not.toHaveBeenCalled();
|
||||
expect(mockApi.on).not.toHaveBeenCalled();
|
||||
|
||||
registerService.mock.calls[0]?.[0].start({});
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
"memory-lancedb: disabled until configured (embedding config required)",
|
||||
);
|
||||
});
|
||||
|
||||
test("registers auto-recall on before_prompt_build instead of the legacy hook", async () => {
|
||||
const on = vi.fn();
|
||||
const mockApi = {
|
||||
|
||||
@@ -503,7 +503,19 @@ export default definePluginEntry({
|
||||
configSchema: memoryConfigSchema,
|
||||
|
||||
register(api: OpenClawPluginApi) {
|
||||
const cfg = memoryConfigSchema.parse(api.pluginConfig);
|
||||
let cfg: MemoryConfig;
|
||||
try {
|
||||
cfg = memoryConfigSchema.parse(api.pluginConfig);
|
||||
} catch (error) {
|
||||
api.registerService({
|
||||
id: "memory-lancedb",
|
||||
start: () => {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
api.logger.warn(`memory-lancedb: disabled until configured (${message})`);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
const dbPath = cfg.dbPath!;
|
||||
const resolvedDbPath = dbPath.includes("://") ? dbPath : api.resolvePath(dbPath);
|
||||
const { model, dimensions } = cfg.embedding;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { getAccessToken } from "../../engine/messaging/sender.js";
|
||||
import { ChannelApiSchema, executeChannelApi } from "../../engine/tools/channel-api.js";
|
||||
import type { ChannelApiParams } from "../../engine/tools/channel-api.js";
|
||||
import { listQQBotAccountIds, resolveQQBotAccount } from "../config.js";
|
||||
import { getBridgeLogger } from "../logger.js";
|
||||
|
||||
/**
|
||||
* Register the QQ channel API proxy tool.
|
||||
@@ -15,13 +14,11 @@ import { getBridgeLogger } from "../logger.js";
|
||||
export function registerChannelTool(api: OpenClawPluginApi): void {
|
||||
const cfg = api.config;
|
||||
if (!cfg) {
|
||||
getBridgeLogger().debug?.("[qqbot-channel-api] No config available, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
const accountIds = listQQBotAccountIds(cfg);
|
||||
if (accountIds.length === 0) {
|
||||
getBridgeLogger().debug?.("[qqbot-channel-api] No QQBot accounts configured, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,7 +26,6 @@ export function registerChannelTool(api: OpenClawPluginApi): void {
|
||||
const account = resolveQQBotAccount(cfg, firstAccountId);
|
||||
|
||||
if (!account.appId || !account.clientSecret) {
|
||||
getBridgeLogger().debug?.("[qqbot-channel-api] Account not fully configured, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,6 @@ export default defineBundledChannelEntry({
|
||||
exportName: "setTlonRuntime",
|
||||
},
|
||||
registerFull(api) {
|
||||
api.logger.debug?.("[tlon] Registering tlon tool");
|
||||
api.registerTool({
|
||||
name: "tlon",
|
||||
label: "Tlon CLI",
|
||||
|
||||
Reference in New Issue
Block a user