feat: prepare Zhinian desktop pilot
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-05-07 21:49:20 +08:00
parent cddaf37016
commit 0abc48189c
103 changed files with 10975 additions and 2049 deletions

View File

@@ -32,6 +32,17 @@ const AUTH_STORE_VERSION = 1;
const AUTH_PROFILE_FILENAME = 'auth-profiles.json';
const LEGACY_MINIMAX_OAUTH_PLUGIN_ID = 'minimax-portal-auth';
const MERGED_MINIMAX_PLUGIN_ID = 'minimax';
const YINIAN_DESKTOP_TOOLS_PROFILE = 'coding';
const YINIAN_INTERNAL_MODEL_REF = 'minimax/MiniMax-M2.7';
const YINIAN_CORE_PLUGIN_IDS = new Set([
'minimax',
'cloud-sync',
'openclaw-weixin',
'agentbus',
]);
const YINIAN_BASE_PLUGIN_IDS = new Set(['minimax', 'cloud-sync']);
const YINIAN_CORE_CHANNEL_IDS = new Set(['openclaw-weixin', 'agentbus']);
const YINIAN_DISABLED_CHANNEL_IDS = new Set(['feishu', 'dingtalk', 'wecom']);
interface BundledPluginManifest {
id: string;
@@ -1031,6 +1042,87 @@ function isPlainRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function isYinianManagedConfig(config: Record<string, unknown>): boolean {
const models = isPlainRecord(config.models) ? config.models : null;
const providers = models && isPlainRecord(models.providers) ? models.providers : null;
if (providers && isPlainRecord(providers[OPENCLAW_PROVIDER_KEY_MINIMAX])) return true;
const agents = isPlainRecord(config.agents) ? config.agents : null;
const defaults = agents && isPlainRecord(agents.defaults) ? agents.defaults : null;
const model = defaults && isPlainRecord(defaults.model) ? defaults.model : null;
return model?.primary === YINIAN_INTERNAL_MODEL_REF;
}
function trimYinianPluginSurface(config: Record<string, unknown>): boolean {
if (!isYinianManagedConfig(config)) return false;
const plugins = isPlainRecord(config.plugins) ? config.plugins : null;
let modified = false;
if (plugins) {
if (Array.isArray(plugins.allow)) {
const channels = isPlainRecord(config.channels) ? config.channels : null;
const nextAllow = (plugins.allow as unknown[])
.filter((pluginId): pluginId is string => {
if (typeof pluginId !== 'string') return false;
if (YINIAN_BASE_PLUGIN_IDS.has(pluginId)) return true;
return YINIAN_CORE_CHANNEL_IDS.has(pluginId) && Boolean(channels?.[pluginId]);
});
for (const pluginId of YINIAN_BASE_PLUGIN_IDS) {
if (!nextAllow.includes(pluginId)) nextAllow.push(pluginId);
}
for (const pluginId of YINIAN_CORE_CHANNEL_IDS) {
if (channels?.[pluginId] && !nextAllow.includes(pluginId)) nextAllow.push(pluginId);
}
if (JSON.stringify(nextAllow) !== JSON.stringify(plugins.allow)) {
plugins.allow = nextAllow;
modified = true;
console.log(`[sanitize] Trimmed plugins.allow to Yinian core plugins: ${nextAllow.join(', ')}`);
}
}
if (isPlainRecord(plugins.entries)) {
for (const pluginId of Object.keys(plugins.entries)) {
if (YINIAN_CORE_PLUGIN_IDS.has(pluginId)) continue;
delete plugins.entries[pluginId];
modified = true;
console.log(`[sanitize] Removed non-core plugin entry "${pluginId}" from Yinian OpenClaw config`);
}
}
}
const channels = isPlainRecord(config.channels) ? config.channels : null;
if (channels) {
for (const channelId of Object.keys(channels)) {
if (YINIAN_CORE_CHANNEL_IDS.has(channelId)) continue;
delete channels[channelId];
modified = true;
console.log(`[sanitize] Removed disabled channel "${channelId}" from Yinian OpenClaw config`);
}
}
const agents = isPlainRecord(config.agents) ? config.agents : null;
if (Array.isArray(agents?.list)) {
const nextAgents = agents.list.filter((entry) => {
if (!isPlainRecord(entry)) return true;
const id = typeof entry.id === 'string' ? entry.id : '';
const channelType = typeof entry.channelType === 'string'
? entry.channelType
: typeof entry.channel === 'string'
? entry.channel
: '';
if (YINIAN_DISABLED_CHANNEL_IDS.has(channelType)) return false;
return !Array.from(YINIAN_DISABLED_CHANNEL_IDS).some((disabledId) => id.startsWith(`channel-${disabledId}-`));
});
if (nextAgents.length !== agents.list.length) {
agents.list = nextAgents;
modified = true;
console.log('[sanitize] Removed disabled channel agents from Yinian OpenClaw config');
}
}
return modified;
}
function removeLegacyMoonshotKimiSearchConfig(config: Record<string, unknown>): boolean {
const tools = isPlainRecord(config.tools) ? config.tools : null;
const web = tools && isPlainRecord(tools.web) ? tools.web : null;
@@ -1687,6 +1779,15 @@ export async function sanitizeOpenClawConfig(): Promise<void> {
if (modified) config.plugins = validPlugins;
} else if (typeof plugins === 'object') {
const pluginsObj = plugins as Record<string, unknown>;
const KNOWN_INVALID_PLUGINS_ROOT_KEYS = ['bundledDiscovery'];
for (const key of KNOWN_INVALID_PLUGINS_ROOT_KEYS) {
if (key in pluginsObj) {
console.log(`[sanitize] Removing deprecated key "plugins.${key}" from openclaw.json`);
delete pluginsObj[key];
modified = true;
}
}
if (Array.isArray(pluginsObj.load)) {
const validLoad: unknown[] = [];
for (const p of pluginsObj.load) {
@@ -1784,14 +1885,16 @@ export async function sanitizeOpenClawConfig(): Promise<void> {
}
// ── tools.profile & sessions.visibility ───────────────────────
// OpenClaw 3.8+ requires tools.profile = 'full' and tools.sessions.visibility = 'all'
// for ClawX to properly integrate with its updated tool system.
const toolsConfig = (config.tools as Record<string, unknown> | undefined) || {};
// 智念助手需要保留多轮上下文、文件生成与能力包调用。
// OpenClaw 的 messaging/minimal 档会进入 raw model run导致每轮不 replay 历史。
// 因此桌面端使用 coding 档UI 层再隐藏普通用户不需要理解的开发者概念。
const toolsConfig = isPlainRecord(config.tools) ? config.tools : {};
let toolsModified = false;
if (toolsConfig.profile !== 'full') {
toolsConfig.profile = 'full';
if (toolsConfig.profile !== YINIAN_DESKTOP_TOOLS_PROFILE) {
toolsConfig.profile = YINIAN_DESKTOP_TOOLS_PROFILE;
toolsModified = true;
console.log(`[sanitize] Set tools.profile="${YINIAN_DESKTOP_TOOLS_PROFILE}" for Yinian desktop chat latency`);
}
const sessions = (toolsConfig.sessions as Record<string, unknown> | undefined) || {};
@@ -1821,6 +1924,56 @@ export async function sanitizeOpenClawConfig(): Promise<void> {
modified = true;
}
// ── agents.defaults desktop defaults ──────────────────────────
// OpenClaw 会把可见 skills 汇总进系统上下文。对智念助手的普通对话,
// 默认不展开全量 skill 目录;后续由“快捷任务/能力包”按需注入。
// OpenClaw 默认每 30 分钟在 main session 跑一次 heartbeat。桌面端不
// 使用这类后台心跳任务,避免它变成普通用户可见的历史会话。
if (isYinianManagedConfig(config)) {
const agentsConfig = isPlainRecord(config.agents) ? config.agents : {};
const defaults = isPlainRecord(agentsConfig.defaults) ? agentsConfig.defaults : {};
if (!Array.isArray(defaults.skills)) {
defaults.skills = [];
agentsConfig.defaults = defaults;
config.agents = agentsConfig;
modified = true;
console.log('[sanitize] Set agents.defaults.skills=[] for Yinian desktop lightweight chat');
}
const heartbeat = isPlainRecord(defaults.heartbeat) ? defaults.heartbeat : {};
if (heartbeat.every !== '0m') {
heartbeat.every = '0m';
defaults.heartbeat = heartbeat;
agentsConfig.defaults = defaults;
config.agents = agentsConfig;
modified = true;
console.log('[sanitize] Disabled OpenClaw agent heartbeat for Yinian desktop');
}
if (Array.isArray(agentsConfig.list)) {
const nextList = agentsConfig.list.map((entry) => {
if (!isPlainRecord(entry) || entry.id !== 'main') return entry;
const tools = isPlainRecord(entry.tools) ? entry.tools : {};
if (tools.profile === YINIAN_DESKTOP_TOOLS_PROFILE) return entry;
return {
...entry,
tools: {
...tools,
profile: YINIAN_DESKTOP_TOOLS_PROFILE,
},
};
});
if (JSON.stringify(nextList) !== JSON.stringify(agentsConfig.list)) {
agentsConfig.list = nextList;
config.agents = agentsConfig;
modified = true;
console.log(`[sanitize] Set main agent tools.profile="${YINIAN_DESKTOP_TOOLS_PROFILE}" for Yinian desktop context replay`);
}
}
}
if (trimYinianPluginSurface(config)) {
modified = true;
}
// ── plugins.entries.feishu cleanup ──────────────────────────────
// Normalize feishu plugin ids dynamically based on installed manifest.
// Different environments may report either "openclaw-lark" or
@@ -2224,6 +2377,10 @@ export async function sanitizeOpenClawConfig(): Promise<void> {
}
}
if (trimYinianPluginSurface(config)) {
modified = true;
}
if (modified) {
await writeOpenClawJson(config);
console.log('[sanitize] openclaw.json sanitized successfully');