fix: bundle QR dependencies for channel login
This commit is contained in:
@@ -23,13 +23,15 @@ type OpenClawRuntimeResolution = {
|
|||||||
|
|
||||||
let cachedOpenClawRuntime: OpenClawRuntimeResolution | null = null;
|
let cachedOpenClawRuntime: OpenClawRuntimeResolution | null = null;
|
||||||
|
|
||||||
// Packages that Zhinian loads from the OpenClaw package context at main-process
|
// Modules that Zhinian loads from the OpenClaw package context at main-process
|
||||||
// module initialization time. Some user-installed OpenClaw packages are valid
|
// module initialization time. Some user-installed or previously managed
|
||||||
// CLIs but do not include these app-side integration dependencies; selecting
|
// OpenClaw packages are valid CLIs but do not include these app-side
|
||||||
// them would crash before the UI opens.
|
// integration dependencies; selecting them would crash before the UI opens.
|
||||||
const REQUIRED_OPENCLAW_CONTEXT_PACKAGES = [
|
const REQUIRED_OPENCLAW_CONTEXT_MODULES = [
|
||||||
'@whiskeysockets/baileys',
|
'@whiskeysockets/baileys/package.json',
|
||||||
'qrcode-terminal',
|
'qrcode-terminal/package.json',
|
||||||
|
'qrcode-terminal/vendor/QRCode/index.js',
|
||||||
|
'qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -148,20 +150,20 @@ function isValidOpenClawPackageDir(dir: string): boolean {
|
|||||||
&& existsSync(fsPath(join(dir, 'openclaw.mjs')));
|
&& existsSync(fsPath(join(dir, 'openclaw.mjs')));
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasRequiredOpenClawContextPackages(dir: string): boolean {
|
function hasRequiredOpenClawContextModules(dir: string): boolean {
|
||||||
if (!isValidOpenClawPackageDir(dir)) return false;
|
if (!isValidOpenClawPackageDir(dir)) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const runtimeRequire = createRequire(join(realpathSync(fsPath(dir)), 'package.json'));
|
const runtimeRequire = createRequire(join(realpathSync(fsPath(dir)), 'package.json'));
|
||||||
for (const packageName of REQUIRED_OPENCLAW_CONTEXT_PACKAGES) {
|
for (const specifier of REQUIRED_OPENCLAW_CONTEXT_MODULES) {
|
||||||
runtimeRequire.resolve(`${packageName}/package.json`);
|
runtimeRequire.resolve(specifier);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
try {
|
try {
|
||||||
const runtimeRequire = createRequire(join(dir, 'package.json'));
|
const runtimeRequire = createRequire(join(dir, 'package.json'));
|
||||||
for (const packageName of REQUIRED_OPENCLAW_CONTEXT_PACKAGES) {
|
for (const specifier of REQUIRED_OPENCLAW_CONTEXT_MODULES) {
|
||||||
runtimeRequire.resolve(`${packageName}/package.json`);
|
runtimeRequire.resolve(specifier);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
@@ -251,10 +253,10 @@ function findExternalOpenClawDir(excludedDirs: string[]): string | null {
|
|||||||
seen.add(candidate);
|
seen.add(candidate);
|
||||||
if (excludedDirs.some((excluded) => samePath(candidate, excluded))) continue;
|
if (excludedDirs.some((excluded) => samePath(candidate, excluded))) continue;
|
||||||
if (!isValidOpenClawPackageDir(candidate)) continue;
|
if (!isValidOpenClawPackageDir(candidate)) continue;
|
||||||
if (hasRequiredOpenClawContextPackages(candidate)) return candidate;
|
if (hasRequiredOpenClawContextModules(candidate)) return candidate;
|
||||||
logOpenClawRuntime('[openclaw-runtime] Ignoring external OpenClaw installation because required app dependencies are missing', {
|
logOpenClawRuntime('[openclaw-runtime] Ignoring external OpenClaw installation because required app dependencies are missing', {
|
||||||
candidate,
|
candidate,
|
||||||
requiredPackages: REQUIRED_OPENCLAW_CONTEXT_PACKAGES,
|
requiredModules: REQUIRED_OPENCLAW_CONTEXT_MODULES,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +271,7 @@ function installBundledOpenClawToManagedRuntime(bundledDir: string, managedDir:
|
|||||||
? readOpenClawVersion(managedDir)
|
? readOpenClawVersion(managedDir)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (managedVersion && bundledVersion && managedVersion === bundledVersion) {
|
if (managedVersion && bundledVersion && managedVersion === bundledVersion && hasRequiredOpenClawContextModules(managedDir)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +325,7 @@ function resolveOpenClawRuntime(): OpenClawRuntimeResolution {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isValidOpenClawPackageDir(managedDir)) {
|
if (hasRequiredOpenClawContextModules(managedDir)) {
|
||||||
cachedOpenClawRuntime = {
|
cachedOpenClawRuntime = {
|
||||||
dir: managedDir,
|
dir: managedDir,
|
||||||
source: 'managed',
|
source: 'managed',
|
||||||
@@ -341,6 +343,13 @@ function resolveOpenClawRuntime(): OpenClawRuntimeResolution {
|
|||||||
return cachedOpenClawRuntime;
|
return cachedOpenClawRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isValidOpenClawPackageDir(managedDir)) {
|
||||||
|
logOpenClawRuntime('[openclaw-runtime] Ignoring managed OpenClaw runtime because required app dependencies are missing', {
|
||||||
|
managedDir,
|
||||||
|
requiredModules: REQUIRED_OPENCLAW_CONTEXT_MODULES,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
cachedOpenClawRuntime = {
|
cachedOpenClawRuntime = {
|
||||||
dir: bundledDir,
|
dir: bundledDir,
|
||||||
source: isValidOpenClawPackageDir(bundledDir) ? 'bundled' : 'missing',
|
source: isValidOpenClawPackageDir(bundledDir) ? 'bundled' : 'missing',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { homedir } from 'node:os';
|
|||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { deflateSync } from 'node:zlib';
|
import { deflateSync } from 'node:zlib';
|
||||||
import { normalizeOpenClawAccountId } from './channel-alias';
|
import { normalizeOpenClawAccountId } from './channel-alias';
|
||||||
import { getOpenClawResolvedDir } from './paths';
|
import { getOpenClawDir, getOpenClawResolvedDir } from './paths';
|
||||||
|
|
||||||
export const DEFAULT_WECHAT_BASE_URL = 'https://ilinkai.weixin.qq.com';
|
export const DEFAULT_WECHAT_BASE_URL = 'https://ilinkai.weixin.qq.com';
|
||||||
const DEFAULT_ILINK_BOT_TYPE = '3';
|
const DEFAULT_ILINK_BOT_TYPE = '3';
|
||||||
@@ -44,8 +44,16 @@ function getQrRenderDeps(): QrRenderDeps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const openclawRequire = createRequire(join(getOpenClawResolvedDir(), 'package.json'));
|
const openclawRequire = createRequire(join(getOpenClawResolvedDir(), 'package.json'));
|
||||||
const qrCodeModulePath = openclawRequire.resolve('qrcode-terminal/vendor/QRCode/index.js');
|
const fallbackRequire = createRequire(join(getOpenClawDir(), 'package.json'));
|
||||||
const qrErrorCorrectLevelPath = openclawRequire.resolve('qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js');
|
const resolveOpenClawModule = (specifier: string) => {
|
||||||
|
try {
|
||||||
|
return openclawRequire.resolve(specifier);
|
||||||
|
} catch {
|
||||||
|
return fallbackRequire.resolve(specifier);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const qrCodeModulePath = resolveOpenClawModule('qrcode-terminal/vendor/QRCode/index.js');
|
||||||
|
const qrErrorCorrectLevelPath = resolveOpenClawModule('qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js');
|
||||||
qrRenderDeps = {
|
qrRenderDeps = {
|
||||||
QRCode: require(qrCodeModulePath),
|
QRCode: require(qrCodeModulePath),
|
||||||
QRErrorCorrectLevel: require(qrErrorCorrectLevelPath),
|
QRErrorCorrectLevel: require(qrErrorCorrectLevelPath),
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ const openclawRequire = createRequire(join(openclawResolvedPath, 'package.json')
|
|||||||
const projectRequire = createRequire(join(openclawPath, 'package.json'));
|
const projectRequire = createRequire(join(openclawPath, 'package.json'));
|
||||||
|
|
||||||
function resolveOpenClawPackageJson(packageName: string): string {
|
function resolveOpenClawPackageJson(packageName: string): string {
|
||||||
const specifier = `${packageName}/package.json`;
|
return resolveOpenClawModule(`${packageName}/package.json`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveOpenClawModule(specifier: string): string {
|
||||||
// 1. Try openclaw's own deps (works in packaged mode + openclaw transitive deps)
|
// 1. Try openclaw's own deps (works in packaged mode + openclaw transitive deps)
|
||||||
try {
|
try {
|
||||||
return openclawRequire.resolve(specifier);
|
return openclawRequire.resolve(specifier);
|
||||||
@@ -32,7 +35,7 @@ function resolveOpenClawPackageJson(packageName: string): string {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
const reason = err instanceof Error ? err.message : String(err);
|
const reason = err instanceof Error ? err.message : String(err);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to resolve "${packageName}" from OpenClaw context. ` +
|
`Failed to resolve "${specifier}" from OpenClaw context. ` +
|
||||||
`openclawPath=${openclawPath}, resolvedPath=${openclawResolvedPath}. ${reason}`,
|
`openclawPath=${openclawPath}, resolvedPath=${openclawResolvedPath}. ${reason}`,
|
||||||
{ cause: err }
|
{ cause: err }
|
||||||
);
|
);
|
||||||
@@ -40,8 +43,8 @@ function resolveOpenClawPackageJson(packageName: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const baileysPath = dirname(resolveOpenClawPackageJson('@whiskeysockets/baileys'));
|
const baileysPath = dirname(resolveOpenClawPackageJson('@whiskeysockets/baileys'));
|
||||||
const qrCodeModulePath = openclawRequire.resolve('qrcode-terminal/vendor/QRCode/index.js');
|
const qrCodeModulePath = resolveOpenClawModule('qrcode-terminal/vendor/QRCode/index.js');
|
||||||
const qrErrorCorrectLevelPath = openclawRequire.resolve('qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js');
|
const qrErrorCorrectLevelPath = resolveOpenClawModule('qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js');
|
||||||
|
|
||||||
// Load Baileys dependencies dynamically
|
// Load Baileys dependencies dynamically
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ echo` Skipped ${skippedDevCount} dev-only package references`;
|
|||||||
// then BFS its transitive deps exactly like we did for openclaw above.
|
// then BFS its transitive deps exactly like we did for openclaw above.
|
||||||
const EXTRA_BUNDLED_PACKAGES = [
|
const EXTRA_BUNDLED_PACKAGES = [
|
||||||
'@whiskeysockets/baileys', // WhatsApp channel (was a dep of old clawdbot, not openclaw)
|
'@whiskeysockets/baileys', // WhatsApp channel (was a dep of old clawdbot, not openclaw)
|
||||||
|
'qrcode-terminal', // QR rendering is loaded from OpenClaw context by channel login flows
|
||||||
];
|
];
|
||||||
|
|
||||||
let extraCount = 0;
|
let extraCount = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user