Refine desktop setup and remove bundled app center apps
This commit is contained in:
@@ -5,19 +5,17 @@ import { dirname, join } from 'node:path';
|
||||
import { getOpenClawConfigDir } from './paths';
|
||||
import { readOpenClawConfig, writeOpenClawConfig } from './channel-config';
|
||||
import { logger } from './logger';
|
||||
import {
|
||||
YINIAN_LEGACY_MODEL_PROVIDER_KEYS,
|
||||
YINIAN_MODEL_AUTH_PROFILE_ID,
|
||||
YINIAN_MODEL_DEFAULT_BASE_URL,
|
||||
YINIAN_MODEL_PROVIDER_KEY,
|
||||
} from '../../shared/yinian-model';
|
||||
|
||||
const YINIAN_MODEL_PROVIDER_KEY = 'minimax';
|
||||
const YINIAN_MODEL_ID = 'MiniMax-M2.7';
|
||||
const YINIAN_MODEL_REF = `${YINIAN_MODEL_PROVIDER_KEY}/${YINIAN_MODEL_ID}`;
|
||||
const YINIAN_INTERNAL_PROVIDER_KEYS = ['minimax', 'minimax-portal'];
|
||||
const YINIAN_MODEL_AUTH_ALIAS_PROFILE_IDS = [
|
||||
'minimax:cn',
|
||||
'minimax-cn:default',
|
||||
'minimax-portal-cn:default',
|
||||
'minimax-portal:default',
|
||||
];
|
||||
const YINIAN_MODEL_AUTH_TARGET_PROFILE_ID = 'minimax:default';
|
||||
const YINIAN_MODEL_AUTH_TARGET_PROVIDER = 'minimax';
|
||||
const YINIAN_INTERNAL_PROVIDER_KEYS = [
|
||||
YINIAN_MODEL_PROVIDER_KEY,
|
||||
...YINIAN_LEGACY_MODEL_PROVIDER_KEYS,
|
||||
] as const;
|
||||
const YINIAN_FALLBACK_SKILL_IDS = ['docx', 'pdf', 'pptx', 'xlsx', 'design', 'image-search', 'web-search'];
|
||||
|
||||
type JsonObject = Record<string, unknown>;
|
||||
@@ -162,7 +160,10 @@ function splitModelRef(modelRef: string | null): { providerKey: string | null; m
|
||||
};
|
||||
}
|
||||
|
||||
function groupAuthProfiles(store: AuthProfilesStore): YinianModelConfigDiagnostics['authProfiles']['providers'] {
|
||||
function groupAuthProfiles(
|
||||
store: AuthProfilesStore,
|
||||
referencedProviderKeys?: Set<string>,
|
||||
): YinianModelConfigDiagnostics['authProfiles']['providers'] {
|
||||
const profiles = isPlainRecord(store.profiles) ? store.profiles : {};
|
||||
const groups = new Map<string, { ids: string[]; types: Set<string> }>();
|
||||
|
||||
@@ -171,6 +172,7 @@ function groupAuthProfiles(store: AuthProfilesStore): YinianModelConfigDiagnosti
|
||||
const provider = typeof profile.provider === 'string' && profile.provider.trim()
|
||||
? profile.provider
|
||||
: profileId.split(':')[0] || 'unknown';
|
||||
if (referencedProviderKeys && !referencedProviderKeys.has(provider)) continue;
|
||||
const type = typeof profile.type === 'string' && profile.type.trim() ? profile.type : 'unknown';
|
||||
const group = groups.get(provider) ?? { ids: [], types: new Set<string>() };
|
||||
group.ids.push(profileId);
|
||||
@@ -205,15 +207,32 @@ function hasConfiguredProvider(config: JsonObject, providerKey: string | null):
|
||||
return isPlainRecord(providers[providerKey]);
|
||||
}
|
||||
|
||||
function buildProviderDiagnostics(config: JsonObject, primaryProviderKey: string | null): YinianModelConfigDiagnostics['providers'] {
|
||||
function getProviderEntry(config: JsonObject, providerKey: string | null): JsonObject | null {
|
||||
if (!providerKey) return null;
|
||||
const models = isPlainRecord(config.models) ? config.models : {};
|
||||
const providers = isPlainRecord(models.providers) ? models.providers : {};
|
||||
const providerKeys = Array.from(new Set([
|
||||
...(primaryProviderKey ? [primaryProviderKey] : []),
|
||||
...YINIAN_INTERNAL_PROVIDER_KEYS,
|
||||
]));
|
||||
return isPlainRecord(providers[providerKey]) ? providers[providerKey] : null;
|
||||
}
|
||||
|
||||
return providerKeys.map((key) => {
|
||||
function isPlaceholderModelApiProvider(provider: JsonObject | null): boolean {
|
||||
if (!provider) return false;
|
||||
return provider.baseUrl === YINIAN_MODEL_DEFAULT_BASE_URL;
|
||||
}
|
||||
|
||||
function collectModelProviderKeys(primary: string | null, fallbacks: string[]): string[] {
|
||||
const providerKeys = [
|
||||
splitModelRef(primary).providerKey,
|
||||
...fallbacks.map((modelRef) => splitModelRef(modelRef).providerKey),
|
||||
].filter((providerKey): providerKey is string => typeof providerKey === 'string' && providerKey.trim().length > 0);
|
||||
|
||||
return [...new Set(providerKeys)];
|
||||
}
|
||||
|
||||
function buildProviderDiagnostics(config: JsonObject, referencedProviderKeys: string[]): YinianModelConfigDiagnostics['providers'] {
|
||||
const models = isPlainRecord(config.models) ? config.models : {};
|
||||
const providers = isPlainRecord(models.providers) ? models.providers : {};
|
||||
|
||||
return referencedProviderKeys.map((key) => {
|
||||
const entry = isPlainRecord(providers[key]) ? providers[key] : {};
|
||||
const modelsList = Array.isArray(entry.models) ? entry.models : [];
|
||||
return {
|
||||
@@ -246,47 +265,6 @@ export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
const currentYinianProvider = isPlainRecord(providers[YINIAN_MODEL_PROVIDER_KEY])
|
||||
? { ...providers[YINIAN_MODEL_PROVIDER_KEY] }
|
||||
: {};
|
||||
if ('timeoutSeconds' in currentYinianProvider) {
|
||||
delete currentYinianProvider.timeoutSeconds;
|
||||
changed = true;
|
||||
}
|
||||
const currentModels = Array.isArray(currentYinianProvider.models)
|
||||
? currentYinianProvider.models.filter(isPlainRecord)
|
||||
: [];
|
||||
const hasYinianModel = currentModels.some((item) => item.id === YINIAN_MODEL_ID);
|
||||
const nextYinianProvider = {
|
||||
...currentYinianProvider,
|
||||
baseUrl: typeof currentYinianProvider.baseUrl === 'string' && currentYinianProvider.baseUrl.trim()
|
||||
? currentYinianProvider.baseUrl
|
||||
: 'https://api.minimaxi.com/anthropic',
|
||||
api: typeof currentYinianProvider.api === 'string' && currentYinianProvider.api.trim()
|
||||
? currentYinianProvider.api
|
||||
: 'anthropic-messages',
|
||||
authHeader: typeof currentYinianProvider.authHeader === 'boolean'
|
||||
? currentYinianProvider.authHeader
|
||||
: true,
|
||||
models: hasYinianModel
|
||||
? currentModels
|
||||
: [
|
||||
...currentModels,
|
||||
{
|
||||
id: YINIAN_MODEL_ID,
|
||||
name: 'MiniMax M2.7',
|
||||
reasoning: true,
|
||||
input: ['text', 'image'],
|
||||
contextWindow: 204800,
|
||||
maxTokens: 131072,
|
||||
},
|
||||
],
|
||||
};
|
||||
if (JSON.stringify(providers[YINIAN_MODEL_PROVIDER_KEY]) !== JSON.stringify(nextYinianProvider)) {
|
||||
providers[YINIAN_MODEL_PROVIDER_KEY] = nextYinianProvider;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
for (const providerKey of YINIAN_INTERNAL_PROVIDER_KEYS) {
|
||||
const currentProvider = providers[providerKey];
|
||||
if (!isPlainRecord(currentProvider)) continue;
|
||||
@@ -307,12 +285,26 @@ export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
|
||||
const agents = isPlainRecord(config.agents) ? { ...config.agents } : {};
|
||||
const defaults = isPlainRecord(agents.defaults) ? { ...agents.defaults } : {};
|
||||
const defaultModel = isPlainRecord(defaults.model) ? { ...defaults.model } : {};
|
||||
if (defaultModel.primary !== YINIAN_MODEL_REF) {
|
||||
defaultModel.primary = YINIAN_MODEL_REF;
|
||||
const currentPrimary = typeof defaultModel.primary === 'string' && defaultModel.primary.trim()
|
||||
? defaultModel.primary.trim()
|
||||
: '';
|
||||
const { providerKey: currentPrimaryProvider } = splitModelRef(currentPrimary || null);
|
||||
const currentYinianProvider = isPlainRecord(providers[YINIAN_MODEL_PROVIDER_KEY])
|
||||
? providers[YINIAN_MODEL_PROVIDER_KEY]
|
||||
: null;
|
||||
const hasPlaceholderYinianProvider = isPlaceholderModelApiProvider(currentYinianProvider);
|
||||
const shouldCleanYinianModelAuth = !currentYinianProvider || hasPlaceholderYinianProvider;
|
||||
|
||||
if (hasPlaceholderYinianProvider) {
|
||||
delete providers[YINIAN_MODEL_PROVIDER_KEY];
|
||||
if (currentPrimaryProvider === YINIAN_MODEL_PROVIDER_KEY) {
|
||||
delete defaultModel.primary;
|
||||
defaultModel.fallbacks = [];
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
if (!Array.isArray(defaultModel.fallbacks)) {
|
||||
defaultModel.fallbacks = ['minimax/MiniMax-M2.5'];
|
||||
defaultModel.fallbacks = [];
|
||||
changed = true;
|
||||
}
|
||||
defaults.model = defaultModel;
|
||||
@@ -385,59 +377,46 @@ export async function ensureYinianModelRuntimeConfigured(): Promise<void> {
|
||||
logger.info('[provider-sync] Applied Yinian model runtime defaults');
|
||||
}
|
||||
|
||||
await ensureYinianModelAuthProfileAliases();
|
||||
if (shouldCleanYinianModelAuth) {
|
||||
await removeYinianModelAuthProfileLeftovers();
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureYinianModelAuthProfileAliases(agentId = 'main'): Promise<void> {
|
||||
export async function removeYinianModelAuthProfileLeftovers(agentId = 'main'): Promise<void> {
|
||||
const authPath = getAuthProfilesPath(agentId);
|
||||
const store = await readAuthProfilesStore(authPath);
|
||||
store.version = typeof store.version === 'number' ? store.version : 1;
|
||||
store.profiles = isPlainRecord(store.profiles) ? store.profiles : {};
|
||||
|
||||
const target = store.profiles[YINIAN_MODEL_AUTH_TARGET_PROFILE_ID];
|
||||
const sourceId = YINIAN_MODEL_AUTH_ALIAS_PROFILE_IDS.find((profileId) => isPlainRecord(store.profiles?.[profileId]));
|
||||
const source = sourceId ? store.profiles[sourceId] : null;
|
||||
let changed = false;
|
||||
|
||||
if (!isPlainRecord(target) && isPlainRecord(source)) {
|
||||
store.profiles[YINIAN_MODEL_AUTH_TARGET_PROFILE_ID] = {
|
||||
...source,
|
||||
provider: YINIAN_MODEL_AUTH_TARGET_PROVIDER,
|
||||
};
|
||||
changed = true;
|
||||
} else if (isPlainRecord(target) && target.provider !== YINIAN_MODEL_AUTH_TARGET_PROVIDER) {
|
||||
store.profiles[YINIAN_MODEL_AUTH_TARGET_PROFILE_ID] = {
|
||||
...target,
|
||||
provider: YINIAN_MODEL_AUTH_TARGET_PROVIDER,
|
||||
};
|
||||
for (const [profileId, profile] of Object.entries(store.profiles)) {
|
||||
const isYinianProfileId = profileId === YINIAN_MODEL_AUTH_PROFILE_ID
|
||||
|| profileId.startsWith(`${YINIAN_MODEL_PROVIDER_KEY}:`);
|
||||
const isYinianProvider = isPlainRecord(profile) && profile.provider === YINIAN_MODEL_PROVIDER_KEY;
|
||||
if (isYinianProfileId || isYinianProvider) {
|
||||
delete store.profiles[profileId];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPlainRecord(store.order) && YINIAN_MODEL_PROVIDER_KEY in store.order) {
|
||||
const order = { ...store.order } as Record<string, string[]>;
|
||||
delete order[YINIAN_MODEL_PROVIDER_KEY];
|
||||
store.order = order;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (isPlainRecord(store.profiles[YINIAN_MODEL_AUTH_TARGET_PROFILE_ID])) {
|
||||
const order = isPlainRecord(store.order) ? { ...store.order } as Record<string, string[]> : {};
|
||||
const currentOrder = Array.isArray(order[YINIAN_MODEL_AUTH_TARGET_PROVIDER])
|
||||
? order[YINIAN_MODEL_AUTH_TARGET_PROVIDER]
|
||||
: [];
|
||||
if (!currentOrder.includes(YINIAN_MODEL_AUTH_TARGET_PROFILE_ID)) {
|
||||
order[YINIAN_MODEL_AUTH_TARGET_PROVIDER] = [
|
||||
YINIAN_MODEL_AUTH_TARGET_PROFILE_ID,
|
||||
...currentOrder.filter((item) => item !== YINIAN_MODEL_AUTH_TARGET_PROFILE_ID),
|
||||
];
|
||||
store.order = order;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
const lastGood = isPlainRecord(store.lastGood) ? { ...store.lastGood } as Record<string, string> : {};
|
||||
if (!lastGood[YINIAN_MODEL_AUTH_TARGET_PROVIDER]) {
|
||||
lastGood[YINIAN_MODEL_AUTH_TARGET_PROVIDER] = YINIAN_MODEL_AUTH_TARGET_PROFILE_ID;
|
||||
store.lastGood = lastGood;
|
||||
changed = true;
|
||||
}
|
||||
if (isPlainRecord(store.lastGood) && YINIAN_MODEL_PROVIDER_KEY in store.lastGood) {
|
||||
const lastGood = { ...store.lastGood } as Record<string, string>;
|
||||
delete lastGood[YINIAN_MODEL_PROVIDER_KEY];
|
||||
store.lastGood = lastGood;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
await writeJsonFile(authPath, store);
|
||||
logger.info('[provider-sync] Normalized Yinian model auth profile aliases');
|
||||
logger.info('[provider-sync] Removed legacy Yinian model auth profile leftovers');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,11 +427,15 @@ export async function buildYinianModelConfigDiagnostics(): Promise<YinianModelCo
|
||||
const authStore = await readAuthProfilesStore(authProfilesPath);
|
||||
const primary = getPrimaryModel(config);
|
||||
const fallbacks = getFallbackModels(config);
|
||||
const referencedProviderKeys = collectModelProviderKeys(primary, fallbacks);
|
||||
const referencedProviderKeySet = new Set(referencedProviderKeys);
|
||||
const { providerKey, modelId } = splitModelRef(primary);
|
||||
const heartbeatEvery = getHeartbeatEvery(config);
|
||||
const providerConfigured = hasConfiguredProvider(config, providerKey);
|
||||
const providerEntry = getProviderEntry(config, providerKey);
|
||||
const placeholderProvider = isPlaceholderModelApiProvider(providerEntry);
|
||||
const authProfileConfigured = hasMatchingAuthProfile(authStore, providerKey);
|
||||
const providers = buildProviderDiagnostics(config, providerKey);
|
||||
const providers = buildProviderDiagnostics(config, referencedProviderKeys);
|
||||
const checks: YinianModelConfigCheck[] = [
|
||||
{
|
||||
id: 'primary-model',
|
||||
@@ -463,9 +446,15 @@ export async function buildYinianModelConfigDiagnostics(): Promise<YinianModelCo
|
||||
{
|
||||
id: 'provider-entry',
|
||||
label: '模型服务',
|
||||
status: providerConfigured ? 'ok' : 'error',
|
||||
status: providerConfigured && !placeholderProvider ? 'ok' : 'error',
|
||||
detail: providerKey
|
||||
? (providerConfigured ? `已配置 ${providerKey}` : `缺少 ${providerKey} 服务配置`)
|
||||
? (
|
||||
!providerConfigured
|
||||
? `缺少 ${providerKey} 服务配置`
|
||||
: placeholderProvider
|
||||
? `${providerKey} 仍使用示例 API 地址,请在设置中重新保存默认模型服务`
|
||||
: `已配置 ${providerKey}`
|
||||
)
|
||||
: '无法从默认模型识别服务',
|
||||
},
|
||||
{
|
||||
@@ -503,7 +492,7 @@ export async function buildYinianModelConfigDiagnostics(): Promise<YinianModelCo
|
||||
authProfiles: {
|
||||
path: authProfilesPath,
|
||||
exists: existsSync(authProfilesPath),
|
||||
providers: groupAuthProfiles(authStore),
|
||||
providers: groupAuthProfiles(authStore, referencedProviderKeySet),
|
||||
},
|
||||
checks,
|
||||
paths: {
|
||||
|
||||
Reference in New Issue
Block a user