feat: prepare Zhinian desktop pilot
This commit is contained in:
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user