Gateway: lazily resolve channel runtime

This commit is contained in:
Vincent Koc 2026-03-16 01:41:14 -07:00
parent 77b1f240fd
commit 776e5d8a08
2 changed files with 24 additions and 3 deletions

View File

@ -105,6 +105,14 @@ type ChannelManagerOptions = {
* @see {@link ChannelGatewayContext.channelRuntime}
*/
channelRuntime?: PluginRuntime["channel"];
/**
* Lazily resolves optional channel runtime helpers for external channel plugins.
*
* Use this when the caller wants to avoid instantiating the full plugin channel
* runtime during gateway startup. The manager only needs the runtime surface once
* a channel account actually starts.
*/
resolveChannelRuntime?: () => PluginRuntime["channel"];
};
type StartChannelOptions = {
@ -125,7 +133,8 @@ export type ChannelManager = {
// Channel docking: lifecycle hooks (`plugin.gateway`) flow through this manager.
export function createChannelManager(opts: ChannelManagerOptions): ChannelManager {
const { loadConfig, channelLogs, channelRuntimeEnvs, channelRuntime } = opts;
const { loadConfig, channelLogs, channelRuntimeEnvs, channelRuntime, resolveChannelRuntime } =
opts;
const channelStores = new Map<ChannelId, ChannelRuntimeStore>();
// Tracks restart attempts per channel:account. Reset on successful start.
@ -219,6 +228,10 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
return next;
};
const getChannelRuntime = (): PluginRuntime["channel"] | undefined => {
return channelRuntime ?? resolveChannelRuntime?.();
};
const startChannelInternal = async (
channelId: ChannelId,
accountId?: string,
@ -297,6 +310,7 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
});
const log = channelLogs[channelId];
const resolvedChannelRuntime = getChannelRuntime();
const task = startAccount({
cfg,
accountId: id,
@ -306,7 +320,7 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
log,
getStatus: () => getRuntime(channelId, id),
setStatus: (next) => setRuntime(channelId, id, next),
...(channelRuntime ? { channelRuntime } : {}),
...(resolvedChannelRuntime ? { channelRuntime: resolvedChannelRuntime } : {}),
});
const trackedPromise = Promise.resolve(task)
.catch((err) => {

View File

@ -138,6 +138,13 @@ const logDiscovery = log.child("discovery");
const logTailscale = log.child("tailscale");
const logChannels = log.child("channels");
const logBrowser = log.child("browser");
let cachedChannelRuntime: ReturnType<typeof createPluginRuntime>["channel"] | null = null;
function getChannelRuntime() {
cachedChannelRuntime ??= createPluginRuntime().channel;
return cachedChannelRuntime;
}
const logHealth = log.child("health");
const logCron = log.child("cron");
const logReload = log.child("reload");
@ -575,7 +582,7 @@ export async function startGatewayServer(
loadConfig,
channelLogs,
channelRuntimeEnvs,
channelRuntime: createPluginRuntime().channel,
resolveChannelRuntime: getChannelRuntime,
});
const getReadiness = createReadinessChecker({
channelManager,