upgrade openclaw to 3.23 (#652)
Co-authored-by: Felix <24791380+vcfgv@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
b786b773f1
commit
ba5947e2cb
@@ -158,6 +158,54 @@ describe('WeCom plugin configuration', () => {
|
||||
expect(plugins.allow).toContain('wecom');
|
||||
expect(plugins.entries['wecom'].enabled).toBe(true);
|
||||
});
|
||||
|
||||
it('saves whatsapp as a built-in channel instead of a plugin', async () => {
|
||||
const { saveChannelConfig } = await import('@electron/utils/channel-config');
|
||||
|
||||
await saveChannelConfig('whatsapp', { enabled: true }, 'default');
|
||||
|
||||
const config = await readOpenClawJson();
|
||||
const channels = config.channels as Record<string, { enabled?: boolean; defaultAccount?: string; accounts?: Record<string, { enabled?: boolean }> }>;
|
||||
|
||||
expect(channels.whatsapp.enabled).toBe(true);
|
||||
expect(channels.whatsapp.defaultAccount).toBe('default');
|
||||
expect(channels.whatsapp.accounts?.default?.enabled).toBe(true);
|
||||
expect(config.plugins).toBeUndefined();
|
||||
});
|
||||
|
||||
it('cleans up stale whatsapp plugin registration when saving built-in config', async () => {
|
||||
const { saveChannelConfig, writeOpenClawConfig } = await import('@electron/utils/channel-config');
|
||||
|
||||
await writeOpenClawConfig({
|
||||
plugins: {
|
||||
enabled: true,
|
||||
allow: ['whatsapp'],
|
||||
entries: {
|
||||
whatsapp: { enabled: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await saveChannelConfig('whatsapp', { enabled: true }, 'default');
|
||||
|
||||
const config = await readOpenClawJson();
|
||||
expect(config.plugins).toBeUndefined();
|
||||
const channels = config.channels as Record<string, { enabled?: boolean }>;
|
||||
expect(channels.whatsapp.enabled).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps configured built-in channels in plugins.allow when a plugin-backed channel is enabled', async () => {
|
||||
const { saveChannelConfig } = await import('@electron/utils/channel-config');
|
||||
|
||||
await saveChannelConfig('discord', { token: 'discord-token' }, 'default');
|
||||
await saveChannelConfig('whatsapp', { enabled: true }, 'default');
|
||||
await saveChannelConfig('qqbot', { appId: 'qq-app', token: 'qq-token', appSecret: 'qq-secret' }, 'default');
|
||||
|
||||
const config = await readOpenClawJson();
|
||||
const plugins = config.plugins as { allow: string[] };
|
||||
|
||||
expect(plugins.allow).toEqual(expect.arrayContaining(['qqbot', 'discord', 'whatsapp']));
|
||||
});
|
||||
});
|
||||
|
||||
describe('WeChat dangling plugin cleanup', () => {
|
||||
|
||||
@@ -63,12 +63,29 @@ vi.mock('@electron/api/route-utils', () => ({
|
||||
|
||||
vi.mock('@electron/utils/paths', () => ({
|
||||
getOpenClawConfigDir: () => testOpenClawConfigDir,
|
||||
getOpenClawDir: () => testOpenClawConfigDir,
|
||||
getOpenClawResolvedDir: () => testOpenClawConfigDir,
|
||||
}));
|
||||
|
||||
vi.mock('@electron/utils/proxy-fetch', () => ({
|
||||
proxyAwareFetch: (...args: unknown[]) => proxyAwareFetchMock(...args),
|
||||
}));
|
||||
|
||||
// Stub openclaw SDK functions that are dynamically loaded via createRequire
|
||||
// in the real code — the extracted utility module is easy to mock.
|
||||
vi.mock('@electron/utils/openclaw-sdk', () => ({
|
||||
listDiscordDirectoryGroupsFromConfig: vi.fn().mockResolvedValue([]),
|
||||
listDiscordDirectoryPeersFromConfig: vi.fn().mockResolvedValue([]),
|
||||
normalizeDiscordMessagingTarget: vi.fn().mockReturnValue(undefined),
|
||||
listTelegramDirectoryGroupsFromConfig: vi.fn().mockResolvedValue([]),
|
||||
listTelegramDirectoryPeersFromConfig: vi.fn().mockResolvedValue([]),
|
||||
normalizeTelegramMessagingTarget: vi.fn().mockReturnValue(undefined),
|
||||
listSlackDirectoryGroupsFromConfig: vi.fn().mockResolvedValue([]),
|
||||
listSlackDirectoryPeersFromConfig: vi.fn().mockResolvedValue([]),
|
||||
normalizeSlackMessagingTarget: vi.fn().mockReturnValue(undefined),
|
||||
normalizeWhatsAppMessagingTarget: vi.fn().mockReturnValue(undefined),
|
||||
}));
|
||||
|
||||
describe('handleChannelRoutes', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
|
||||
@@ -40,6 +40,19 @@ async function sanitizeConfig(filePath: string): Promise<boolean> {
|
||||
|
||||
const config = JSON.parse(raw) as Record<string, unknown>;
|
||||
let modified = false;
|
||||
const BUILTIN_CHANNEL_IDS = new Set([
|
||||
'discord',
|
||||
'telegram',
|
||||
'whatsapp',
|
||||
'slack',
|
||||
'signal',
|
||||
'imessage',
|
||||
'matrix',
|
||||
'line',
|
||||
'msteams',
|
||||
'googlechat',
|
||||
'mattermost',
|
||||
]);
|
||||
|
||||
/** Non-throwing async existence check. */
|
||||
async function fileExists(p: string): Promise<boolean> {
|
||||
@@ -104,6 +117,68 @@ async function sanitizeConfig(filePath: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const allow = Array.isArray(pluginsObj.allow) ? [...pluginsObj.allow as string[]] : [];
|
||||
const entries = (
|
||||
pluginsObj.entries && typeof pluginsObj.entries === 'object' && !Array.isArray(pluginsObj.entries)
|
||||
? { ...(pluginsObj.entries as Record<string, unknown>) }
|
||||
: {}
|
||||
) as Record<string, unknown>;
|
||||
|
||||
if ('whatsapp' in entries) {
|
||||
delete entries.whatsapp;
|
||||
pluginsObj.entries = entries;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
const configuredBuiltIns = new Set<string>();
|
||||
const channels = config.channels;
|
||||
if (channels && typeof channels === 'object' && !Array.isArray(channels)) {
|
||||
for (const [channelId, section] of Object.entries(channels as Record<string, Record<string, unknown>>)) {
|
||||
if (!BUILTIN_CHANNEL_IDS.has(channelId)) continue;
|
||||
if (!section || section.enabled === false) continue;
|
||||
if (Object.keys(section).length > 0) {
|
||||
configuredBuiltIns.add(channelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const externalPluginIds = allow.filter((id) => !BUILTIN_CHANNEL_IDS.has(id));
|
||||
const nextAllow = [...externalPluginIds];
|
||||
if (externalPluginIds.length > 0) {
|
||||
for (const channelId of configuredBuiltIns) {
|
||||
if (!nextAllow.includes(channelId)) {
|
||||
nextAllow.push(channelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(nextAllow) !== JSON.stringify(allow)) {
|
||||
if (nextAllow.length > 0) {
|
||||
pluginsObj.allow = nextAllow;
|
||||
} else {
|
||||
delete pluginsObj.allow;
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (Array.isArray(pluginsObj.allow) && pluginsObj.allow.length === 0) {
|
||||
delete pluginsObj.allow;
|
||||
modified = true;
|
||||
}
|
||||
if (pluginsObj.entries && Object.keys(entries).length === 0) {
|
||||
delete pluginsObj.entries;
|
||||
modified = true;
|
||||
}
|
||||
const pluginKeysExcludingEnabled = Object.keys(pluginsObj).filter((key) => key !== 'enabled');
|
||||
if (pluginsObj.enabled === true && pluginKeysExcludingEnabled.length === 0) {
|
||||
delete pluginsObj.enabled;
|
||||
modified = true;
|
||||
}
|
||||
if (Object.keys(pluginsObj).length === 0) {
|
||||
delete config.plugins;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Mirror: remove stale tools.web.search.kimi.apiKey when moonshot provider exists.
|
||||
@@ -275,7 +350,7 @@ describe('sanitizeOpenClawConfig (blocklist approach)', () => {
|
||||
await writeConfig({
|
||||
skills: { enabled: true, entries: {} },
|
||||
channels: { discord: { token: 'abc', enabled: true } },
|
||||
plugins: { entries: { whatsapp: { enabled: true } } },
|
||||
plugins: { entries: { customPlugin: { enabled: true } } },
|
||||
gateway: { mode: 'local', auth: { token: 'xyz' } },
|
||||
agents: { defaults: { model: { primary: 'gpt-4' } } },
|
||||
models: { providers: { openai: { baseUrl: 'https://api.openai.com' } } },
|
||||
@@ -289,7 +364,7 @@ describe('sanitizeOpenClawConfig (blocklist approach)', () => {
|
||||
expect(result.skills).not.toHaveProperty('enabled');
|
||||
// All other sections unchanged
|
||||
expect(result.channels).toEqual({ discord: { token: 'abc', enabled: true } });
|
||||
expect(result.plugins).toEqual({ entries: { whatsapp: { enabled: true } } });
|
||||
expect(result.plugins).toEqual({ entries: { customPlugin: { enabled: true } } });
|
||||
expect(result.gateway).toEqual({ mode: 'local', auth: { token: 'xyz' } });
|
||||
expect(result.agents).toEqual({ defaults: { model: { primary: 'gpt-4' } } });
|
||||
});
|
||||
@@ -359,7 +434,7 @@ describe('sanitizeOpenClawConfig (blocklist approach)', () => {
|
||||
'/another/missing/plugin/dir',
|
||||
],
|
||||
},
|
||||
entries: { whatsapp: { enabled: true } },
|
||||
entries: { customPlugin: { enabled: true } },
|
||||
},
|
||||
gateway: { mode: 'local' },
|
||||
});
|
||||
@@ -372,11 +447,40 @@ describe('sanitizeOpenClawConfig (blocklist approach)', () => {
|
||||
const load = plugins.load as Record<string, unknown>;
|
||||
expect(load.paths).toEqual([]);
|
||||
// Other plugin config is preserved
|
||||
expect(plugins.entries).toEqual({ whatsapp: { enabled: true } });
|
||||
expect(plugins.entries).toEqual({ customPlugin: { enabled: true } });
|
||||
// Other top-level sections untouched
|
||||
expect(result.gateway).toEqual({ mode: 'local' });
|
||||
});
|
||||
|
||||
it('keeps configured built-in channels in plugins.allow when external plugins are enabled', async () => {
|
||||
await writeConfig({
|
||||
plugins: {
|
||||
enabled: true,
|
||||
allow: ['whatsapp', 'customPlugin'],
|
||||
entries: {
|
||||
whatsapp: { enabled: true },
|
||||
customPlugin: { enabled: true },
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
discord: { enabled: true, token: 'abc' },
|
||||
},
|
||||
});
|
||||
|
||||
const modified = await sanitizeConfig(configPath);
|
||||
expect(modified).toBe(true);
|
||||
|
||||
const result = await readConfig();
|
||||
expect(result.channels).toEqual({ discord: { enabled: true, token: 'abc' } });
|
||||
expect(result.plugins).toEqual({
|
||||
enabled: true,
|
||||
allow: ['customPlugin', 'discord'],
|
||||
entries: {
|
||||
customPlugin: { enabled: true },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('removes bundled node_modules paths from plugins.load.paths', async () => {
|
||||
await writeConfig({
|
||||
plugins: {
|
||||
|
||||
Reference in New Issue
Block a user