Refine desktop setup and remove bundled app center apps
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user