This commit is contained in:
committed by
GitHub
Unverified
parent
c866205eac
commit
0cdd12cd40
@@ -1,6 +1,6 @@
|
||||
import { app } from 'electron';
|
||||
import path from 'path';
|
||||
import { existsSync, readFileSync, mkdirSync, rmSync } from 'fs';
|
||||
import { existsSync, readFileSync, mkdirSync, readdirSync, rmSync, symlinkSync } from 'fs';
|
||||
import { homedir } from 'os';
|
||||
import { join } from 'path';
|
||||
|
||||
@@ -164,6 +164,73 @@ function ensureConfiguredPluginsUpgraded(configuredChannels: string[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure extension-specific packages are resolvable from shared dist/ chunks.
|
||||
*
|
||||
* OpenClaw's Rollup bundler creates shared chunks in dist/ (e.g.
|
||||
* sticker-cache-*.js) that eagerly `import "grammy"`. ESM bare specifier
|
||||
* resolution walks from the importing file's directory upward:
|
||||
* dist/node_modules/ → openclaw/node_modules/ → …
|
||||
* It does NOT search `dist/extensions/telegram/node_modules/`.
|
||||
*
|
||||
* NODE_PATH only works for CJS require(), NOT for ESM import statements.
|
||||
*
|
||||
* Fix: create symlinks in openclaw/node_modules/ pointing to packages in
|
||||
* dist/extensions/<ext>/node_modules/. This makes the standard ESM
|
||||
* resolution algorithm find them. Skip-if-exists avoids overwriting
|
||||
* openclaw's own deps (they take priority).
|
||||
*/
|
||||
function ensureExtensionDepsResolvable(openclawDir: string): void {
|
||||
const extDir = join(openclawDir, 'dist', 'extensions');
|
||||
const topNM = join(openclawDir, 'node_modules');
|
||||
let linkedCount = 0;
|
||||
|
||||
try {
|
||||
if (!existsSync(extDir)) return;
|
||||
|
||||
for (const ext of readdirSync(extDir, { withFileTypes: true })) {
|
||||
if (!ext.isDirectory()) continue;
|
||||
const extNM = join(extDir, ext.name, 'node_modules');
|
||||
if (!existsSync(extNM)) continue;
|
||||
|
||||
for (const pkg of readdirSync(extNM, { withFileTypes: true })) {
|
||||
if (pkg.name === '.bin') continue;
|
||||
|
||||
if (pkg.name.startsWith('@')) {
|
||||
// Scoped package — iterate sub-entries
|
||||
const scopeDir = join(extNM, pkg.name);
|
||||
let scopeEntries;
|
||||
try { scopeEntries = readdirSync(scopeDir, { withFileTypes: true }); } catch { continue; }
|
||||
for (const sub of scopeEntries) {
|
||||
if (!sub.isDirectory()) continue;
|
||||
const dest = join(topNM, pkg.name, sub.name);
|
||||
if (existsSync(dest)) continue;
|
||||
try {
|
||||
mkdirSync(join(topNM, pkg.name), { recursive: true });
|
||||
symlinkSync(join(scopeDir, sub.name), dest);
|
||||
linkedCount++;
|
||||
} catch { /* skip on error — non-fatal */ }
|
||||
}
|
||||
} else {
|
||||
const dest = join(topNM, pkg.name);
|
||||
if (existsSync(dest)) continue;
|
||||
try {
|
||||
mkdirSync(topNM, { recursive: true });
|
||||
symlinkSync(join(extNM, pkg.name), dest);
|
||||
linkedCount++;
|
||||
} catch { /* skip on error — non-fatal */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// extensions dir may not exist or be unreadable — non-fatal
|
||||
}
|
||||
|
||||
if (linkedCount > 0) {
|
||||
logger.info(`[extension-deps] Linked ${linkedCount} extension packages into ${topNM}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Pre-launch sync ──────────────────────────────────────────────
|
||||
|
||||
export async function syncGatewayConfigBeforeLaunch(
|
||||
@@ -365,6 +432,11 @@ export async function prepareGatewayLaunchContext(port: number): Promise<Gateway
|
||||
OPENCLAW_NO_RESPAWN: '1',
|
||||
};
|
||||
|
||||
// Ensure extension-specific packages (e.g. grammy from the telegram
|
||||
// extension) are resolvable by shared dist/ chunks via symlinks in
|
||||
// openclaw/node_modules/. NODE_PATH does NOT work for ESM imports.
|
||||
ensureExtensionDepsResolvable(openclawDir);
|
||||
|
||||
return {
|
||||
appSettings,
|
||||
openclawDir,
|
||||
|
||||
@@ -266,6 +266,65 @@ for (const [realPath, pkgName] of collected) {
|
||||
}
|
||||
}
|
||||
|
||||
// 5b. Merge built-in extension node_modules into top-level node_modules
|
||||
//
|
||||
// OpenClaw 3.31+ ships built-in extensions (telegram, discord, etc.) under
|
||||
// dist/extensions/<ext>/node_modules/. The Rollup bundler creates shared
|
||||
// chunks at dist/ root (e.g. sticker-cache-*.js) that eagerly import
|
||||
// extension-specific packages like "grammy". Node.js resolves bare
|
||||
// specifiers from the importing file's directory upward:
|
||||
// dist/ → openclaw/ → openclaw/node_modules/
|
||||
// It does NOT search dist/extensions/telegram/node_modules/.
|
||||
//
|
||||
// Fix: copy extension deps into the top-level node_modules/ so they are
|
||||
// resolvable from shared chunks. Skip-if-exists preserves version priority
|
||||
// (openclaw's own deps take precedence over extension deps).
|
||||
const extensionsDir = path.join(OUTPUT, 'dist', 'extensions');
|
||||
let mergedExtCount = 0;
|
||||
if (fs.existsSync(extensionsDir)) {
|
||||
for (const extEntry of fs.readdirSync(extensionsDir, { withFileTypes: true })) {
|
||||
if (!extEntry.isDirectory()) continue;
|
||||
const extNM = path.join(extensionsDir, extEntry.name, 'node_modules');
|
||||
if (!fs.existsSync(extNM)) continue;
|
||||
|
||||
for (const pkgEntry of fs.readdirSync(extNM, { withFileTypes: true })) {
|
||||
if (!pkgEntry.isDirectory() || pkgEntry.name === '.bin') continue;
|
||||
const srcPkg = path.join(extNM, pkgEntry.name);
|
||||
|
||||
if (pkgEntry.name.startsWith('@')) {
|
||||
// Scoped package — iterate sub-entries
|
||||
let scopeEntries;
|
||||
try { scopeEntries = fs.readdirSync(srcPkg, { withFileTypes: true }); } catch { continue; }
|
||||
for (const scopeEntry of scopeEntries) {
|
||||
if (!scopeEntry.isDirectory()) continue;
|
||||
const scopedName = `${pkgEntry.name}/${scopeEntry.name}`;
|
||||
if (copiedNames.has(scopedName)) continue;
|
||||
const srcScoped = path.join(srcPkg, scopeEntry.name);
|
||||
const destScoped = path.join(outputNodeModules, pkgEntry.name, scopeEntry.name);
|
||||
try {
|
||||
fs.mkdirSync(normWin(path.dirname(destScoped)), { recursive: true });
|
||||
fs.cpSync(normWin(srcScoped), normWin(destScoped), { recursive: true, dereference: true });
|
||||
copiedNames.add(scopedName);
|
||||
mergedExtCount++;
|
||||
} catch { /* skip on copy error */ }
|
||||
}
|
||||
} else {
|
||||
if (copiedNames.has(pkgEntry.name)) continue;
|
||||
const destPkg = path.join(outputNodeModules, pkgEntry.name);
|
||||
try {
|
||||
fs.cpSync(normWin(srcPkg), normWin(destPkg), { recursive: true, dereference: true });
|
||||
copiedNames.add(pkgEntry.name);
|
||||
mergedExtCount++;
|
||||
} catch { /* skip on copy error */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mergedExtCount > 0) {
|
||||
echo` Merged ${mergedExtCount} extension packages into top-level node_modules`;
|
||||
}
|
||||
|
||||
// 6. Clean up the bundle to reduce package size
|
||||
//
|
||||
// This removes platform-agnostic waste: dev artifacts, docs, source maps,
|
||||
|
||||
Reference in New Issue
Block a user