


























@@ -1,78 +1,41 @@
11/**
2- * Gateway entry point — thin shell that passes the PluginRuntime to
3- * core/gateway/gateway.ts.
2+ * Gateway entry point — thin bridge shell that constructs
3+ * {@link EngineAdapters} and passes them to the engine's
4+ * `startGateway`.
45 *
5- * All module dependencies are imported directly by the core gateway.
6- * This file only provides the runtime object (which is dynamically
7- * injected by the framework at startup).
6+ * All adapter dependencies are assembled here in one place.
87 */
98109import { resolveRuntimeServiceVersion } from "openclaw/plugin-sdk/cli-runtime";
1110import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
12-import {
13-registerVersionResolver,
14-registerPluginVersion,
15-registerApproveRuntimeGetter,
16-} from "../engine/commands/slash-commands-impl.js";
11+import type { EngineAdapters } from "../engine/adapter/index.js";
1712import {
1813startGateway as coreStartGateway,
1914type CoreGatewayContext,
2015} from "../engine/gateway/gateway.js";
21-import type { GatewayAccount } from "../engine/gateway/types.js";
22-import { registerOutboundAudioAdapterFactory } from "../engine/messaging/outbound.js";
16+import type { GatewayPluginRuntime } from "../engine/gateway/types.js";
2317import { initSender, registerAccount } from "../engine/messaging/sender.js";
2418import type { EngineLogger } from "../engine/types.js";
2519import * as _audioModule from "../engine/utils/audio.js";
20+import { formatDuration } from "../engine/utils/format.js";
2621import { debugLog, debugError } from "../engine/utils/log.js";
27-import { registerTextChunker } from "../engine/utils/text-chunk.js";
2822import type { ResolvedQQBotAccount } from "../types.js";
2923import { ensurePlatformAdapter } from "./bootstrap.js";
3024import { setBridgeLogger } from "./logger.js";
25+import { toGatewayAccount } from "./narrowing.js";
3126import { resolveQQBotPluginVersion } from "./plugin-version.js";
3227import { getQQBotRuntime, getQQBotRuntimeForEngine } from "./runtime.js";
28+import { createSdkHistoryAdapter, createSdkMentionGateAdapter } from "./sdk-adapter.js";
332934-// Register framework SDK version resolver for core/ slash commands.
35-registerVersionResolver(resolveRuntimeServiceVersion);
30+// ---- One-time startup initialization (module-level) ----
363137-// Inject plugin + framework versions into sender and into the slash
38-// command registry. The plugin version is read from this plugin's own
39-// `package.json` by walking up from this file's URL, which is robust
40-// against source-vs-dist layout differences.
4132const _pluginVersion = resolveQQBotPluginVersion(import.meta.url);
4233initSender({
4334pluginVersion: _pluginVersion,
4435openclawVersion: resolveRuntimeServiceVersion(),
4536});
46-registerPluginVersion(_pluginVersion);
473748-// Register runtime getter for /bot-approve config management.
49-registerApproveRuntimeGetter(() => {
50-const rt = getQQBotRuntime();
51-return {
52-config: rt.config as {
53-current: () => Record<string, unknown>;
54-replaceConfigFile: (params: {
55-nextConfig: Record<string, unknown>;
56-afterWrite: { mode: "auto" };
57-}) => Promise<unknown>;
58-},
59-};
60-});
61-62-// Register audio adapter factory so outbound.sendMedia can lazy-init even
63-// when startGateway() hasn't run yet (bundler chunk-splitting scenario).
64-registerOutboundAudioAdapterFactory(() => {
65-// Use a synchronous require-like approach: the audio module should already
66-// be loaded by the time the factory is invoked (gateway has started).
67-// We import it at the top and reference it here.
68-return {
69-audioFileToSilkBase64: async (p: string, f?: string[]) =>
70-(await _audioModule.audioFileToSilkBase64(p, f)) ?? undefined,
71-isAudioFile: (p: string, m?: string) => _audioModule.isAudioFile(p, m),
72-shouldTranscodeVoice: (p: string) => _audioModule.shouldTranscodeVoice(p),
73-waitForFile: (p: string, ms?: number) => _audioModule.waitForFile(p, ms),
74-};
75-});
38+// ============ Public types ============
76397740export interface GatewayContext {
7841account: ResolvedQQBotAccount;
@@ -99,32 +62,62 @@ export interface GatewayContext {
9962};
10063}
1016465+// ============ Adapter factory ============
66+67+/**
68+ * Create the full set of engine adapters from the bridge layer.
69+ *
70+ * This is the **single assembly point** — all SDK → engine binding
71+ * happens here. The engine receives a fully-populated
72+ * {@link EngineAdapters} object with zero global singletons.
73+ */
74+function createEngineAdapters(_runtime: GatewayPluginRuntime): EngineAdapters {
75+return {
76+history: createSdkHistoryAdapter(),
77+mentionGate: createSdkMentionGateAdapter(),
78+audioConvert: {
79+convertSilkToWav: _audioModule.convertSilkToWav,
80+isVoiceAttachment: _audioModule.isVoiceAttachment,
81+ formatDuration,
82+},
83+outboundAudio: {
84+audioFileToSilkBase64: async (p: string, f?: string[]) =>
85+(await _audioModule.audioFileToSilkBase64(p, f)) ?? undefined,
86+isAudioFile: (p: string, m?: string) => _audioModule.isAudioFile(p, m),
87+shouldTranscodeVoice: (p: string) => _audioModule.shouldTranscodeVoice(p),
88+waitForFile: (p: string, ms?: number) => _audioModule.waitForFile(p, ms),
89+},
90+commands: {
91+resolveVersion: resolveRuntimeServiceVersion,
92+pluginVersion: _pluginVersion,
93+approveRuntimeGetter: () => {
94+const rt = getQQBotRuntime();
95+return { config: rt.config };
96+},
97+},
98+};
99+}
100+101+// ============ startGateway ============
102+102103/**
103104 * Start the Gateway WebSocket connection.
104105 *
105- * Passes the PluginRuntime to core/gateway/gateway.ts.
106- * All other dependencies are imported directly by the core module.
106+ * Assembles all adapters and passes them to the engine's core gateway.
107107 */
108108export async function startGateway(ctx: GatewayContext): Promise<void> {
109-// Ensure the PlatformAdapter is registered before any engine code runs.
110-// When the bundler splits code into separate chunks, bootstrap.ts's
111-// side-effect registration may not have executed yet at this point.
112109ensurePlatformAdapter();
113110114111const runtime = getQQBotRuntimeForEngine();
115-116-// Create per-account logger with auto [qqbot:{accountId}] prefix.
117112const accountLogger = createAccountLogger(ctx.log, ctx.account.accountId);
118113119-// Register into engine sender (per-appId logger + API config) and bridge layer.
114+// Per-account registration (still global — sender is a leaf utility).
120115registerAccount(ctx.account.appId, {
121116logger: accountLogger,
122117markdownSupport: ctx.account.markdownSupport,
123118});
124119setBridgeLogger(accountLogger);
125120126-registerTextChunker((text, limit) => runtime.channel.text.chunkMarkdownText(text, limit));
127-128121if (ctx.channelRuntime) {
129122accountLogger.info("Registering approval.native runtime context");
130123const lease = ctx.channelRuntime.runtimeContexts.register({
@@ -140,44 +133,42 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
140133}
141134142135const coreCtx: CoreGatewayContext = {
143-account: ctx.account as unknown as GatewayAccount,
136+account: toGatewayAccount(ctx.account),
144137abortSignal: ctx.abortSignal,
145138cfg: ctx.cfg,
146139onReady: ctx.onReady,
147140onResumed: ctx.onResumed,
148141onError: ctx.onError,
149142log: accountLogger,
150143 runtime,
144+adapters: createEngineAdapters(runtime),
151145};
152146153147return coreStartGateway(coreCtx);
154148}
155149156150// ============ Per-account logger factory ============
157151158-/**
159- * Create an EngineLogger that auto-prefixes all messages with `[qqbot:{accountId}]`.
160- *
161- * Follows the WhatsApp pattern of per-connection loggers — each account gets
162- * its own logger instance so multi-account logs are automatically attributed.
163- */
164152function createAccountLogger(
165153raw: GatewayContext["log"] | undefined,
166154accountId: string,
167155): EngineLogger {
168156const prefix = `[${accountId}]`;
157+const withMeta = (msg: string, meta?: Record<string, unknown>) =>
158+meta && Object.keys(meta).length > 0 ? `${msg} ${JSON.stringify(meta)}` : msg;
159+169160if (!raw) {
170161return {
171-info: (msg) => debugLog(`${prefix} ${msg}`),
172-error: (msg) => debugError(`${prefix} ${msg}`),
173-warn: (msg) => debugError(`${prefix} ${msg}`),
174-debug: (msg) => debugLog(`${prefix} ${msg}`),
162+info: (msg, meta) => debugLog(`${prefix} ${withMeta(msg, meta)}`),
163+error: (msg, meta) => debugError(`${prefix} ${withMeta(msg, meta)}`),
164+warn: (msg, meta) => debugError(`${prefix} ${withMeta(msg, meta)}`),
165+debug: (msg, meta) => debugLog(`${prefix} ${withMeta(msg, meta)}`),
175166};
176167}
177168return {
178-info: (msg) => raw.info(`${prefix} ${msg}`),
179-error: (msg) => raw.error(`${prefix} ${msg}`),
180-warn: (msg) => raw.error(`${prefix} ${msg}`),
181-debug: (msg) => raw.debug?.(`${prefix} ${msg}`),
169+info: (msg, meta) => raw.info(`${prefix} ${withMeta(msg, meta)}`),
170+error: (msg, meta) => raw.error(`${prefix} ${withMeta(msg, meta)}`),
171+warn: (msg, meta) => raw.error(`${prefix} ${withMeta(msg, meta)}`),
172+debug: (msg, meta) => raw.debug?.(`${prefix} ${withMeta(msg, meta)}`),
182173};
183174}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。