feat(gateway): enhance gateway readiness handling and batch sync configuration (#851)

Co-authored-by: paisley <8197966+su8su@users.noreply.github.com>
This commit is contained in:
Haze
2026-04-14 15:42:37 +08:00
committed by GitHub
Unverified
parent 758a8f8c94
commit 30bd8c08f9
14 changed files with 626 additions and 69 deletions

View File

@@ -20,8 +20,8 @@ import { getApiKey, getDefaultProvider, getProvider } from '../utils/secure-stor
import { getProviderEnvVar, getKeyableProviderTypes } from '../utils/provider-registry';
import { getOpenClawDir, getOpenClawEntryPath, isOpenClawPresent } from '../utils/paths';
import { getUvMirrorEnv } from '../utils/uv-env';
import { cleanupDanglingWeChatPluginState, listConfiguredChannels, readOpenClawConfig } from '../utils/channel-config';
import { syncGatewayTokenToConfig, syncBrowserConfigToOpenClaw, syncSessionIdleMinutesToOpenClaw, sanitizeOpenClawConfig } from '../utils/openclaw-auth';
import { cleanupDanglingWeChatPluginState, listConfiguredChannelsFromConfig, readOpenClawConfig } from '../utils/channel-config';
import { sanitizeOpenClawConfig, batchSyncConfigFields } from '../utils/openclaw-auth';
import { buildProxyEnv, resolveProxySettings } from '../utils/proxy';
import { syncProxyConfigToOpenClaw } from '../utils/openclaw-proxy';
import { logger } from '../utils/logger';
@@ -180,7 +180,20 @@ function ensureConfiguredPluginsUpgraded(configuredChannels: string[]): void {
* resolution algorithm find them. Skip-if-exists avoids overwriting
* openclaw's own deps (they take priority).
*/
let _extensionDepsLinked = false;
/**
* Reset the extension-deps-linked cache so the next
* ensureExtensionDepsResolvable() call re-scans and links.
* Called before each Gateway launch to pick up newly installed extensions.
*/
export function resetExtensionDepsLinked(): void {
_extensionDepsLinked = false;
}
function ensureExtensionDepsResolvable(openclawDir: string): void {
if (_extensionDepsLinked) return;
const extDir = join(openclawDir, 'dist', 'extensions');
const topNM = join(openclawDir, 'node_modules');
let linkedCount = 0;
@@ -229,6 +242,8 @@ function ensureExtensionDepsResolvable(openclawDir: string): void {
if (linkedCount > 0) {
logger.info(`[extension-deps] Linked ${linkedCount} extension packages into ${topNM}`);
}
_extensionDepsLinked = true;
}
// ── Pre-launch sync ──────────────────────────────────────────────
@@ -236,6 +251,11 @@ function ensureExtensionDepsResolvable(openclawDir: string): void {
export async function syncGatewayConfigBeforeLaunch(
appSettings: Awaited<ReturnType<typeof getAllSettings>>,
): Promise<void> {
// Reset the extension-deps cache so that newly installed extensions
// (e.g. user added a channel while the app was running) get their
// node_modules linked on the next Gateway spawn.
resetExtensionDepsLinked();
await syncProxyConfigToOpenClaw(appSettings, { preserveExistingWhenDisabled: true });
try {
@@ -260,21 +280,20 @@ export async function syncGatewayConfigBeforeLaunch(
// Auto-upgrade installed plugins before Gateway starts so that
// the plugin manifest ID matches what sanitize wrote to the config.
// Read config once and reuse for both listConfiguredChannels and plugins.allow.
try {
const configuredChannels = await listConfiguredChannels();
const rawCfg = await readOpenClawConfig();
const configuredChannels = await listConfiguredChannelsFromConfig(rawCfg);
// Also ensure plugins referenced in plugins.allow are installed even if
// they have no channels.X section yet (e.g. qqbot added via plugins.allow
// but never fully saved through ClawX UI).
try {
const rawCfg = await readOpenClawConfig();
const allowList = Array.isArray(rawCfg.plugins?.allow) ? (rawCfg.plugins!.allow as string[]) : [];
// Build reverse maps: dirName → channelType AND known manifest IDs → channelType
const pluginIdToChannel: Record<string, string> = {};
for (const [channelType, info] of Object.entries(CHANNEL_PLUGIN_MAP)) {
pluginIdToChannel[info.dirName] = channelType;
}
// Known manifest IDs that differ from their dirName/channelType
pluginIdToChannel['openclaw-lark'] = 'feishu';
pluginIdToChannel['feishu-openclaw-plugin'] = 'feishu';
@@ -295,22 +314,11 @@ export async function syncGatewayConfigBeforeLaunch(
logger.warn('Failed to auto-upgrade plugins:', err);
}
// Batch gateway token, browser config, and session idle into one read+write cycle.
try {
await syncGatewayTokenToConfig(appSettings.gatewayToken);
await batchSyncConfigFields(appSettings.gatewayToken);
} catch (err) {
logger.warn('Failed to sync gateway token to openclaw.json:', err);
}
try {
await syncBrowserConfigToOpenClaw();
} catch (err) {
logger.warn('Failed to sync browser config to openclaw.json:', err);
}
try {
await syncSessionIdleMinutesToOpenClaw();
} catch (err) {
logger.warn('Failed to sync session idle minutes to openclaw.json:', err);
logger.warn('Failed to batch-sync config fields to openclaw.json:', err);
}
}
@@ -360,7 +368,8 @@ async function resolveChannelStartupPolicy(): Promise<{
channelStartupSummary: string;
}> {
try {
const configuredChannels = await listConfiguredChannels();
const rawCfg = await readOpenClawConfig();
const configuredChannels = await listConfiguredChannelsFromConfig(rawCfg);
if (configuredChannels.length === 0) {
return {
skipChannels: true,