Refine desktop setup and remove bundled app center apps

This commit is contained in:
inman
2026-06-04 09:58:58 +08:00
parent 6153579b90
commit 84128dbe23
73 changed files with 3888 additions and 2024 deletions

View File

@@ -129,13 +129,26 @@ export function fixupPluginManifest(targetDir: string): void {
try {
const raw = readFileSync(fsPath(manifestPath), 'utf-8');
const manifest = JSON.parse(raw);
let modified = false;
const oldId = manifest.id as string | undefined;
if (oldId && MANIFEST_ID_FIXES[oldId]) {
const newId = MANIFEST_ID_FIXES[oldId];
manifest.id = newId;
writeFileSync(fsPath(manifestPath), JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
modified = true;
logger.info(`[plugin] Fixed manifest ID: ${oldId}${newId}`);
}
if (!manifest.channelConfigs && Array.isArray(manifest.channels)) {
const schema = { type: 'object' };
manifest.channelConfigs = Object.fromEntries(
manifest.channels
.filter((channelId: unknown): channelId is string => typeof channelId === 'string' && channelId.trim().length > 0)
.map((channelId: string) => [channelId, { schema }]),
);
modified = true;
}
if (modified) {
writeFileSync(fsPath(manifestPath), JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
}
} catch {
// manifest may not exist yet — ignore
}
@@ -166,6 +179,32 @@ export function fixupPluginManifest(targetDir: string): void {
}
}
const runtimeEntry = findExistingRuntimeEntry(targetDir, pkg);
if (runtimeEntry) {
if (typeof pkg.main !== 'string' || !runtimeEntryExists(targetDir, pkg.main)) {
pkg.main = toPackageEntry(runtimeEntry);
modified = true;
}
if (typeof pkg.module === 'string' && !runtimeEntryExists(targetDir, pkg.module)) {
pkg.module = toPackageEntry(runtimeEntry);
modified = true;
}
const openclaw = pkg.openclaw as { extensions?: unknown } | undefined;
if (Array.isArray(openclaw?.extensions)) {
const patchedExtensions = openclaw.extensions.map((entry) => {
const normalized = normalizeRuntimeEntry(entry);
if (!normalized?.endsWith('.ts')) return entry;
const jsEntry = `dist/${normalized.replace(/\.ts$/i, '.js')}`;
return existsSync(fsPath(join(targetDir, jsEntry))) ? toPackageEntry(jsEntry) : entry;
});
if (JSON.stringify(patchedExtensions) !== JSON.stringify(openclaw.extensions)) {
openclaw.extensions = patchedExtensions;
modified = true;
}
}
}
if (modified) {
writeFileSync(fsPath(pkgPath), JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
logger.info(`[plugin] Fixed package.json entry hints in ${targetDir}`);
@@ -193,7 +232,9 @@ function patchPluginEntryIds(targetDir: string): void {
return;
}
const entryFiles = [pkg.main, pkg.module].filter(Boolean) as string[];
const openclaw = pkg.openclaw as { extensions?: unknown } | undefined;
const extensionEntries = Array.isArray(openclaw?.extensions) ? openclaw.extensions : [];
const entryFiles = [...new Set([pkg.main, pkg.module, ...extensionEntries].filter(Boolean))] as string[];
for (const entry of entryFiles) {
const entryPath = join(targetDir, entry);
@@ -229,6 +270,7 @@ function patchPluginEntryIds(targetDir: string): void {
const PLUGIN_NPM_NAMES: Record<string, string> = {
'openclaw-weixin': '@tencent-weixin/openclaw-weixin',
'openclaw-lark': '@larksuite/openclaw-lark',
};
// ── Version helper ───────────────────────────────────────────────────────────
@@ -243,6 +285,54 @@ function readPluginVersion(pkgJsonPath: string): string | null {
}
}
function normalizeRuntimeEntry(entry: unknown): string | null {
if (typeof entry !== 'string') return null;
const trimmed = entry.trim();
if (!trimmed || path.isAbsolute(trimmed)) return null;
return trimmed.replace(/^\.\//, '');
}
function toPackageEntry(entry: string): string {
return entry.startsWith('.') ? entry : `./${entry}`;
}
function isJavaScriptRuntimeEntry(entry: string): boolean {
return /\.(?:cjs|mjs|js)$/i.test(entry);
}
function runtimeEntryExists(pluginDir: string, entry: unknown): boolean {
const normalized = normalizeRuntimeEntry(entry);
return Boolean(normalized) && existsSync(fsPath(join(pluginDir, normalized!)));
}
function collectRuntimeEntryHints(pkg: Record<string, unknown>): string[] {
const hints: unknown[] = [];
const openclaw = pkg.openclaw as { extensions?: unknown } | undefined;
if (Array.isArray(openclaw?.extensions)) {
hints.push(...openclaw.extensions);
}
hints.push(pkg.main, pkg.module, './dist/index.js', './index.js');
return [...new Set(hints.map(normalizeRuntimeEntry).filter((entry): entry is string => Boolean(entry)))];
}
function findExistingRuntimeEntry(pluginDir: string, pkg: Record<string, unknown>): string | null {
for (const entry of collectRuntimeEntryHints(pkg)) {
if (isJavaScriptRuntimeEntry(entry) && existsSync(fsPath(join(pluginDir, entry)))) {
return entry;
}
}
return null;
}
export function hasPluginRuntimeEntry(pluginDir: string): boolean {
try {
const pkg = JSON.parse(readFileSync(fsPath(join(pluginDir, 'package.json')), 'utf-8')) as Record<string, unknown>;
return Boolean(findExistingRuntimeEntry(pluginDir, pkg));
} catch {
return existsSync(fsPath(join(pluginDir, 'dist', 'index.js'))) || existsSync(fsPath(join(pluginDir, 'index.js')));
}
}
// ── pnpm-aware node_modules copy helpers ─────────────────────────────────────
/** Walk up from a path until we find a parent named node_modules. */
@@ -369,17 +459,33 @@ export function ensurePluginInstalled(
if (!sourceDir) return { installed: true }; // no bundled source to compare, keep existing
const installedVersion = readPluginVersion(targetPkgJson);
const sourceVersion = readPluginVersion(join(sourceDir, 'package.json'));
const installedRuntimeReady = hasPluginRuntimeEntry(targetDir);
const sourceRuntimeReady = hasPluginRuntimeEntry(sourceDir);
if (!sourceVersion || !installedVersion || sourceVersion === installedVersion) {
return { installed: true }; // same version or unable to compare
if (!installedRuntimeReady && sourceRuntimeReady) {
logger.info(
`[plugin] Reinstalling ${pluginLabel} plugin: installed copy is missing a loadable runtime entry`,
);
} else {
fixupPluginManifest(targetDir);
return { installed: true }; // same version or unable to compare
}
} else {
// Version differs — fall through to overwrite install
logger.info(
`[plugin] Upgrading ${pluginLabel} plugin: ${installedVersion}${sourceVersion}`,
);
}
// Version differs — fall through to overwrite install
logger.info(
`[plugin] Upgrading ${pluginLabel} plugin: ${installedVersion}${sourceVersion}`,
);
}
// Fresh install or upgrade — try bundled/build sources first
if (sourceDir) {
if (!hasPluginRuntimeEntry(sourceDir)) {
return {
installed: false,
warning: `Bundled ${pluginLabel} plugin mirror is missing a loadable runtime entry. Rebuild bundled plugins.`,
};
}
const extensionsRoot = join(homedir(), '.openclaw', 'extensions');
const attempts: Array<{ attempt: number; code?: string; name?: string; message: string }> = [];
const maxAttempts = process.platform === 'win32' ? 2 : 1;
@@ -393,6 +499,12 @@ export function ensurePluginInstalled(
return { installed: false, warning: `Failed to install ${pluginLabel} plugin mirror (manifest missing).` };
}
fixupPluginManifest(targetDir);
if (!hasPluginRuntimeEntry(targetDir)) {
return {
installed: false,
warning: `Installed ${pluginLabel} plugin mirror is missing a loadable runtime entry.`,
};
}
logger.info(`Installed ${pluginLabel} plugin from bundled mirror: ${sourceDir}`);
return { installed: true };
} catch (error) {
@@ -431,7 +543,9 @@ export function ensurePluginInstalled(
if (existsSync(fsPath(join(npmPkgPath, 'openclaw.plugin.json')))) {
const installedVersion = existsSync(fsPath(targetPkgJson)) ? readPluginVersion(targetPkgJson) : null;
const sourceVersion = readPluginVersion(join(npmPkgPath, 'package.json'));
if (sourceVersion && (!installedVersion || sourceVersion !== installedVersion)) {
const installedRuntimeReady = existsSync(fsPath(targetManifest)) ? hasPluginRuntimeEntry(targetDir) : false;
const sourceRuntimeReady = hasPluginRuntimeEntry(npmPkgPath);
if (sourceVersion && (!installedVersion || sourceVersion !== installedVersion || (!installedRuntimeReady && sourceRuntimeReady))) {
logger.info(
`[plugin] ${installedVersion ? 'Upgrading' : 'Installing'} ${pluginLabel} plugin` +
`${installedVersion ? `: ${installedVersion}${sourceVersion}` : `: ${sourceVersion}`} (dev/node_modules)`,
@@ -440,7 +554,7 @@ export function ensurePluginInstalled(
mkdirSync(fsPath(join(homedir(), '.openclaw', 'extensions')), { recursive: true });
copyPluginFromNodeModules(npmPkgPath, targetDir, npmName);
fixupPluginManifest(targetDir);
if (existsSync(fsPath(join(targetDir, 'openclaw.plugin.json')))) {
if (existsSync(fsPath(join(targetDir, 'openclaw.plugin.json'))) && hasPluginRuntimeEntry(targetDir)) {
return { installed: true };
}
} catch (err) {
@@ -458,6 +572,7 @@ export function ensurePluginInstalled(
);
}
} else if (existsSync(fsPath(targetManifest))) {
fixupPluginManifest(targetDir);
return { installed: true }; // same version, already installed
}
}
@@ -495,6 +610,10 @@ export function ensureWeChatPluginInstalled(): { installed: boolean; warning?: s
return ensurePluginInstalled('openclaw-weixin', buildCandidateSources('openclaw-weixin'), 'WeChat');
}
export function ensureFeishuPluginInstalled(): { installed: boolean; warning?: string } {
return ensurePluginInstalled('openclaw-lark', buildCandidateSources('openclaw-lark'), 'Feishu');
}
export function ensureCloudSyncPluginInstalled(): { installed: boolean; warning?: string } {
return ensurePluginInstalled('cloud-sync', buildCandidateSources('cloud-sync'), 'Cloud Sync');
}
@@ -506,6 +625,7 @@ export function ensureCloudSyncPluginInstalled(): { installed: boolean; warning?
*/
const ALL_BUNDLED_PLUGINS = [
{ fn: ensureWeChatPluginInstalled, label: 'WeChat' },
{ fn: ensureFeishuPluginInstalled, label: 'Feishu' },
{ fn: ensureCloudSyncPluginInstalled, label: 'Cloud Sync' },
] as const;