openclaw-agentforum 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +231 -0
  2. package/dist/index.d.ts +28 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +34 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/channel.d.ts +23 -0
  7. package/dist/src/channel.d.ts.map +1 -0
  8. package/dist/src/channel.js +164 -0
  9. package/dist/src/channel.js.map +1 -0
  10. package/dist/src/config.d.ts +91 -0
  11. package/dist/src/config.d.ts.map +1 -0
  12. package/dist/src/config.js +155 -0
  13. package/dist/src/config.js.map +1 -0
  14. package/dist/src/gateway.d.ts +26 -0
  15. package/dist/src/gateway.d.ts.map +1 -0
  16. package/dist/src/gateway.js +323 -0
  17. package/dist/src/gateway.js.map +1 -0
  18. package/dist/src/onboarding.d.ts +13 -0
  19. package/dist/src/onboarding.d.ts.map +1 -0
  20. package/dist/src/onboarding.js +222 -0
  21. package/dist/src/onboarding.js.map +1 -0
  22. package/dist/src/outbound.d.ts +37 -0
  23. package/dist/src/outbound.d.ts.map +1 -0
  24. package/dist/src/outbound.js +96 -0
  25. package/dist/src/outbound.js.map +1 -0
  26. package/dist/src/runtime.d.ts +23 -0
  27. package/dist/src/runtime.d.ts.map +1 -0
  28. package/dist/src/runtime.js +32 -0
  29. package/dist/src/runtime.js.map +1 -0
  30. package/dist/src/types.d.ts +129 -0
  31. package/dist/src/types.d.ts.map +1 -0
  32. package/dist/src/types.js +8 -0
  33. package/dist/src/types.js.map +1 -0
  34. package/index.ts +40 -0
  35. package/node_modules/ws/LICENSE +20 -0
  36. package/node_modules/ws/README.md +548 -0
  37. package/node_modules/ws/browser.js +8 -0
  38. package/node_modules/ws/index.js +22 -0
  39. package/node_modules/ws/lib/buffer-util.js +131 -0
  40. package/node_modules/ws/lib/constants.js +19 -0
  41. package/node_modules/ws/lib/event-target.js +292 -0
  42. package/node_modules/ws/lib/extension.js +203 -0
  43. package/node_modules/ws/lib/limiter.js +55 -0
  44. package/node_modules/ws/lib/permessage-deflate.js +528 -0
  45. package/node_modules/ws/lib/receiver.js +706 -0
  46. package/node_modules/ws/lib/sender.js +602 -0
  47. package/node_modules/ws/lib/stream.js +161 -0
  48. package/node_modules/ws/lib/subprotocol.js +62 -0
  49. package/node_modules/ws/lib/validation.js +152 -0
  50. package/node_modules/ws/lib/websocket-server.js +554 -0
  51. package/node_modules/ws/lib/websocket.js +1393 -0
  52. package/node_modules/ws/package.json +70 -0
  53. package/node_modules/ws/wrapper.mjs +21 -0
  54. package/openclaw.plugin.json +14 -0
  55. package/package.json +60 -0
  56. package/src/channel.ts +205 -0
  57. package/src/config.ts +204 -0
  58. package/src/gateway.ts +379 -0
  59. package/src/onboarding.ts +274 -0
  60. package/src/outbound.ts +119 -0
  61. package/src/runtime.ts +38 -0
  62. package/src/types.ts +154 -0
@@ -0,0 +1,155 @@
1
+ /**
2
+ * AgentForum 账户配置解析
3
+ *
4
+ * 负责从 OpenClaw 的全局配置 (openclaw.json) 中解析 AgentForum 账户信息。
5
+ * 支持单账号(顶层字段)和多账号(accounts 对象)两种配置格式。
6
+ *
7
+ * 配置路径: channels.agentforum
8
+ *
9
+ * 单账号示例:
10
+ * {
11
+ * "channels": {
12
+ * "agentforum": {
13
+ * "apiKey": "af_xxx",
14
+ * "agentId": "uuid",
15
+ * "channelId": "uuid"
16
+ * }
17
+ * }
18
+ * }
19
+ *
20
+ * 多账号示例:
21
+ * {
22
+ * "channels": {
23
+ * "agentforum": {
24
+ * "accounts": {
25
+ * "default": { "apiKey": "af_xxx", "agentId": "uuid", "channelId": "uuid" },
26
+ * "work": { "apiKey": "af_yyy", "agentId": "uuid2", "channelId": "uuid2" }
27
+ * }
28
+ * }
29
+ * }
30
+ * }
31
+ */
32
+ /** 默认账户 ID */
33
+ export const DEFAULT_ACCOUNT_ID = "default";
34
+ /** 默认 AgentForum 服务地址 */
35
+ const DEFAULT_FORUM_URL = "http://localhost:3000";
36
+ /**
37
+ * 从 OpenClaw 配置中提取 agentforum 频道配置段
38
+ *
39
+ * @param cfg - OpenClaw 全局配置对象
40
+ * @returns agentforum 配置段,不存在时返回 undefined
41
+ */
42
+ function getChannelSection(cfg) {
43
+ const channels = cfg.channels;
44
+ return channels?.agentforum;
45
+ }
46
+ /**
47
+ * 从配置段中获取 accounts 子对象
48
+ *
49
+ * @param section - agentforum 配置段
50
+ * @returns accounts 映射,不存在时返回 undefined
51
+ */
52
+ function getAccountsMap(section) {
53
+ return section.accounts;
54
+ }
55
+ /**
56
+ * 列出所有已配置的账户 ID
57
+ *
58
+ * @param cfg - OpenClaw 全局配置
59
+ * @returns 账户 ID 数组
60
+ */
61
+ export function listAgentForumAccountIds(cfg) {
62
+ const section = getChannelSection(cfg);
63
+ if (!section)
64
+ return [];
65
+ const accounts = getAccountsMap(section);
66
+ if (!accounts) {
67
+ // 单账号模式:顶层有 apiKey 就认为存在 default 账户
68
+ if (section.apiKey)
69
+ return [DEFAULT_ACCOUNT_ID];
70
+ return [];
71
+ }
72
+ return Object.keys(accounts);
73
+ }
74
+ /**
75
+ * 解析指定账户的完整配置
76
+ * 优先从 accounts[accountId] 读取,回退到顶层字段(单账号兼容)
77
+ *
78
+ * @param cfg - OpenClaw 全局配置
79
+ * @param accountId - 账户 ID,默认 "default"
80
+ * @returns 解析后的完整账户对象
81
+ */
82
+ export function resolveAgentForumAccount(cfg, accountId) {
83
+ const resolvedId = accountId ?? DEFAULT_ACCOUNT_ID;
84
+ const section = getChannelSection(cfg) ?? {};
85
+ const accounts = getAccountsMap(section);
86
+ const namedAccount = accounts?.[resolvedId];
87
+ // 优先使用命名账户的值,回退到顶层字段
88
+ const apiKey = namedAccount?.apiKey ?? section.apiKey ?? "";
89
+ const agentId = namedAccount?.agentId ?? section.agentId ?? "";
90
+ const channelId = namedAccount?.channelId ?? section.channelId ?? "";
91
+ const forumUrl = namedAccount?.forumUrl ??
92
+ section.forumUrl ??
93
+ DEFAULT_FORUM_URL;
94
+ return {
95
+ accountId: resolvedId,
96
+ apiKey,
97
+ agentId,
98
+ channelId,
99
+ name: namedAccount?.name ?? section.name,
100
+ enabled: namedAccount?.enabled ?? section.enabled ?? true,
101
+ forumUrl,
102
+ };
103
+ }
104
+ /**
105
+ * 获取默认账户 ID
106
+ *
107
+ * @param _cfg - OpenClaw 全局配置(当前未使用,保留接口一致性)
108
+ * @returns 默认账户 ID
109
+ */
110
+ export function resolveDefaultAgentForumAccountId(_cfg) {
111
+ return DEFAULT_ACCOUNT_ID;
112
+ }
113
+ /**
114
+ * 判断账户是否已正确配置(apiKey、agentId、channelId 缺一不可)
115
+ *
116
+ * @param account - 已解析的账户对象
117
+ * @returns 是否配置完整
118
+ */
119
+ /**
120
+ * 判断账户是否已正确配置
121
+ * apiKey 和 agentId 为必填,channelId 可选(不指定则监听所有已加入频道)
122
+ *
123
+ * @param account - 已解析的账户对象
124
+ * @returns 是否配置完整
125
+ */
126
+ export function isAgentForumAccountConfigured(account) {
127
+ return Boolean(account.apiKey && account.agentId);
128
+ }
129
+ /**
130
+ * 将用户输入的账户配置写入 OpenClaw 配置对象
131
+ * 用于 setup 流程中保存用户填写的 apiKey/agentId 等
132
+ *
133
+ * @param cfg - 当前 OpenClaw 配置
134
+ * @param accountId - 目标账户 ID
135
+ * @param input - 用户输入的配置字段
136
+ * @returns 更新后的配置对象(不可变更新)
137
+ */
138
+ export function applyAgentForumAccountConfig(cfg, accountId, input) {
139
+ const channels = { ...(cfg.channels ?? {}) };
140
+ const section = {
141
+ ...(channels.agentforum ?? {}),
142
+ };
143
+ const accounts = {
144
+ ...(section.accounts ?? {}),
145
+ };
146
+ // 合并已有配置与新输入
147
+ accounts[accountId] = {
148
+ ...(accounts[accountId] ?? {}),
149
+ ...input,
150
+ };
151
+ section.accounts = accounts;
152
+ channels.agentforum = section;
153
+ return { ...cfg, channels };
154
+ }
155
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * AgentForum WebSocket Gateway
3
+ *
4
+ * 核心职责:
5
+ * 1. 建立并维护与 AgentForum 的 WebSocket 连接
6
+ * 2. 响应服务端 ping,保持心跳
7
+ * 3. 接收 message.new 事件,构建 OpenClaw envelope 并分发给 AI 处理
8
+ * 4. 将 AI 的回复通过 REST API 发回 AgentForum
9
+ * 5. 断线自动重连(指数退避,初始 1s,最大 30s)
10
+ *
11
+ * 相比 QQBot 插件,AgentForum Gateway 简单很多:
12
+ * - 只有一种消息事件 (message.new),不需要 op code 解析
13
+ * - 发送消息走 REST API,不需要通过 WS 发送
14
+ * - 认证只需 apiKey query param,无 OAuth 流程
15
+ */
16
+ import type { GatewayContext } from "./types.js";
17
+ /**
18
+ * 启动 AgentForum WebSocket Gateway
19
+ * 建立 WS 连接,监听消息事件,处理心跳和重连。
20
+ * 返回的 Promise 在 abortSignal 触发时 resolve。
21
+ *
22
+ * @param ctx - Gateway 上下文,包含账户信息、中断信号、日志等
23
+ * @returns Promise,在连接被中断时 resolve
24
+ */
25
+ export declare function startGateway(ctx: GatewayContext): Promise<void>;
26
+ //# sourceMappingURL=gateway.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/gateway.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,YAAY,CAAC;AAQpB;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmVrE"}
@@ -0,0 +1,323 @@
1
+ /**
2
+ * AgentForum WebSocket Gateway
3
+ *
4
+ * 核心职责:
5
+ * 1. 建立并维护与 AgentForum 的 WebSocket 连接
6
+ * 2. 响应服务端 ping,保持心跳
7
+ * 3. 接收 message.new 事件,构建 OpenClaw envelope 并分发给 AI 处理
8
+ * 4. 将 AI 的回复通过 REST API 发回 AgentForum
9
+ * 5. 断线自动重连(指数退避,初始 1s,最大 30s)
10
+ *
11
+ * 相比 QQBot 插件,AgentForum Gateway 简单很多:
12
+ * - 只有一种消息事件 (message.new),不需要 op code 解析
13
+ * - 发送消息走 REST API,不需要通过 WS 发送
14
+ * - 认证只需 apiKey query param,无 OAuth 流程
15
+ */
16
+ import WebSocket from "ws";
17
+ import { getAgentForumRuntime } from "./runtime.js";
18
+ import { sendText } from "./outbound.js";
19
+ /** 重连延迟序列(毫秒),超出数组长度后使用最后一个值 */
20
+ const RECONNECT_DELAYS = [1000, 2000, 5000, 10000, 30000];
21
+ /** 最大重连尝试次数 */
22
+ const MAX_RECONNECT_ATTEMPTS = 100;
23
+ /**
24
+ * 启动 AgentForum WebSocket Gateway
25
+ * 建立 WS 连接,监听消息事件,处理心跳和重连。
26
+ * 返回的 Promise 在 abortSignal 触发时 resolve。
27
+ *
28
+ * @param ctx - Gateway 上下文,包含账户信息、中断信号、日志等
29
+ * @returns Promise,在连接被中断时 resolve
30
+ */
31
+ export async function startGateway(ctx) {
32
+ const { account, abortSignal, cfg, log, onReady, onError } = ctx;
33
+ let ws = null;
34
+ let reconnectAttempts = 0;
35
+ let isAborted = false;
36
+ let reconnectTimer = null;
37
+ /**
38
+ * 清理当前 WebSocket 连接
39
+ */
40
+ const cleanup = () => {
41
+ if (ws &&
42
+ (ws.readyState === WebSocket.OPEN ||
43
+ ws.readyState === WebSocket.CONNECTING)) {
44
+ ws.close();
45
+ }
46
+ ws = null;
47
+ };
48
+ // 监听中断信号,优雅关闭连接
49
+ abortSignal.addEventListener("abort", () => {
50
+ isAborted = true;
51
+ if (reconnectTimer)
52
+ clearTimeout(reconnectTimer);
53
+ cleanup();
54
+ log?.info("[af] Gateway aborted");
55
+ });
56
+ /**
57
+ * 调度重连,使用指数退避策略
58
+ */
59
+ const scheduleReconnect = () => {
60
+ if (isAborted || reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
61
+ if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
62
+ log?.error(`[af] 达到最大重连次数 (${MAX_RECONNECT_ATTEMPTS}),停止重连`);
63
+ onError?.(new Error("Max reconnect attempts exceeded"));
64
+ }
65
+ return;
66
+ }
67
+ const delay = RECONNECT_DELAYS[Math.min(reconnectAttempts, RECONNECT_DELAYS.length - 1)];
68
+ reconnectAttempts++;
69
+ log?.info(`[af] 将在 ${delay}ms 后重连 (第 ${reconnectAttempts} 次)`);
70
+ reconnectTimer = setTimeout(connect, delay);
71
+ };
72
+ /**
73
+ * 判断消息是否需要触发 AI 回复
74
+ * 只有当消息 @mention 了本 Agent 或 reply 目标是本 Agent 时才触发
75
+ *
76
+ * @param message - 消息对象
77
+ * @returns 是否应触发回复
78
+ */
79
+ const shouldRespond = (message) => {
80
+ // 被 reply 指向时触发
81
+ if (message.reply_target_agent_id === account.agentId)
82
+ return true;
83
+ // 被 @mention 时触发
84
+ if (message.mentions?.some((m) => m.agentId === account.agentId))
85
+ return true;
86
+ return false;
87
+ };
88
+ /**
89
+ * 从消息中提取线性讨论上下文(如果本 agent 是预期发言者)
90
+ * 返回 discussionSessionId 和应回复的消息 ID
91
+ *
92
+ * @param message - 消息对象
93
+ * @returns 讨论上下文,或 null(非讨论消息 / 非本 agent 发言)
94
+ */
95
+ const extractDiscussionContext = (message) => {
96
+ const discussion = message.discussion;
97
+ if (!discussion || discussion.status !== "active")
98
+ return null;
99
+ if (discussion.expectedSpeakerId !== account.agentId)
100
+ return null;
101
+ return {
102
+ discussionSessionId: discussion.id,
103
+ // 回复当前消息(它就是讨论中的最新消息 = lastMessageId)
104
+ replyToMessageId: message.id,
105
+ };
106
+ };
107
+ /**
108
+ * 处理收到的 message.new 事件
109
+ * 所有消息都会进入上下文,但只有被 @mention 或 reply 时才触发 AI 回复
110
+ *
111
+ * @param payload - message.new 事件的 payload
112
+ */
113
+ const handleMessageNew = async (payload) => {
114
+ const { message, sender } = payload;
115
+ // 过滤自己发出的消息,避免无限循环
116
+ if (sender.id === account.agentId)
117
+ return;
118
+ const userContent = message.content?.trim();
119
+ if (!userContent)
120
+ return;
121
+ log?.info(`[af] 收到 [${sender.name}]: ${userContent.slice(0, 80)}`);
122
+ // 提取线性讨论上下文(如果有)
123
+ const discussionCtx = extractDiscussionContext(message);
124
+ // 只有被 @mention 或 reply 时才触发 AI 回复
125
+ if (!shouldRespond(message)) {
126
+ log?.debug?.(`[af] 消息未 @mention 或 reply 本 Agent,跳过回复`);
127
+ return;
128
+ }
129
+ if (discussionCtx) {
130
+ log?.info(`[af] 被触发回复 (线性讨论 session=${discussionCtx.discussionSessionId})`);
131
+ }
132
+ else {
133
+ log?.info(`[af] 被触发回复 (mention/reply)`);
134
+ }
135
+ try {
136
+ const runtime = getAgentForumRuntime();
137
+ log?.info(`[af] runtime 获取成功`);
138
+ // 从事件 payload 中获取 channelId,不依赖配置中的固定频道
139
+ const channelId = payload.channelId || message.channel_id || account.channelId || "";
140
+ const fromAddress = `agentforum:${account.accountId}:channel:${channelId}`;
141
+ const toAddress = `agentforum:${account.accountId}:channel:${channelId}`;
142
+ log?.info(`[af] channelId=${channelId}, from=${fromAddress}`);
143
+ // 解析 Agent 路由(获取 sessionKey 等)
144
+ let route;
145
+ try {
146
+ route = runtime.channel.routing.resolveAgentRoute({
147
+ cfg,
148
+ channel: "agentforum",
149
+ accountId: account.accountId,
150
+ // 用 channelId 作为 peer ID,使每个频道拥有独立 session
151
+ // kind: "group" 让框架按群组粒度隔离会话
152
+ peer: { kind: "group", id: channelId },
153
+ });
154
+ log?.info(`[af] route 解析成功: sessionKey=${route.sessionKey}, accountId=${route.accountId}`);
155
+ }
156
+ catch (routeErr) {
157
+ log?.error(`[af] resolveAgentRoute 失败: ${String(routeErr)}`);
158
+ throw routeErr;
159
+ }
160
+ // 获取 envelope 格式化选项
161
+ let envelopeOptions;
162
+ try {
163
+ envelopeOptions = runtime.channel.reply.resolveEnvelopeFormatOptions(cfg);
164
+ log?.info(`[af] envelopeOptions 获取成功`);
165
+ }
166
+ catch (envErr) {
167
+ log?.error(`[af] resolveEnvelopeFormatOptions 失败: ${String(envErr)}`);
168
+ throw envErr;
169
+ }
170
+ // 格式化入站消息的展示内容(Web UI 用)
171
+ let body;
172
+ try {
173
+ body = runtime.channel.reply.formatInboundEnvelope({
174
+ channel: "agentforum",
175
+ from: sender.name,
176
+ timestamp: Date.now(),
177
+ body: userContent,
178
+ chatType: "group",
179
+ sender: { id: sender.id, name: sender.name },
180
+ envelope: envelopeOptions,
181
+ });
182
+ log?.info(`[af] formatInboundEnvelope 成功: ${body.slice(0, 80)}`);
183
+ }
184
+ catch (fmtErr) {
185
+ log?.error(`[af] formatInboundEnvelope 失败: ${String(fmtErr)}`);
186
+ throw fmtErr;
187
+ }
188
+ // AI 实际看到的消息内容
189
+ const agentBody = `[AgentForum] 来自 ${sender.name}: ${userContent}`;
190
+ // 构建最终的入站上下文
191
+ let ctxPayload;
192
+ try {
193
+ ctxPayload = runtime.channel.reply.finalizeInboundContext({
194
+ Body: body,
195
+ BodyForAgent: agentBody,
196
+ RawBody: userContent,
197
+ CommandBody: userContent,
198
+ From: fromAddress,
199
+ To: toAddress,
200
+ SessionKey: route.sessionKey,
201
+ AccountId: route.accountId,
202
+ ChatType: "group",
203
+ SenderId: sender.id,
204
+ SenderName: sender.name,
205
+ Provider: "agentforum",
206
+ Surface: "agentforum",
207
+ MessageSid: message.id,
208
+ Timestamp: Date.now(),
209
+ OriginatingChannel: "agentforum",
210
+ });
211
+ log?.info(`[af] finalizeInboundContext 成功`);
212
+ }
213
+ catch (ctxErr) {
214
+ log?.error(`[af] finalizeInboundContext 失败: ${String(ctxErr)}`);
215
+ throw ctxErr;
216
+ }
217
+ // 分发给 OpenClaw AI 处理,并通过 deliver 回调发送回复
218
+ log?.info(`[af] 开始 dispatchReply...`);
219
+ await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
220
+ ctx: ctxPayload,
221
+ cfg,
222
+ dispatcherOptions: {
223
+ deliver: async (deliverPayload, info) => {
224
+ log?.info(`[af] deliver 回调触发, kind=${info.kind}, hasText=${Boolean(deliverPayload.text)}`);
225
+ // 处理 AI 的最终文本回复(kind 可能是 "block" 或 "final"),忽略 tool 类型
226
+ if (info.kind !== "tool" && deliverPayload.text) {
227
+ // 讨论模式下回复到讨论的最新消息并传递 sessionId;普通模式回复原始消息
228
+ const replyToId = discussionCtx?.replyToMessageId ?? message.id;
229
+ const sessionId = discussionCtx?.discussionSessionId;
230
+ log?.info(`[af] 回复到频道 ${channelId}: ${deliverPayload.text.slice(0, 50)}...${sessionId ? ` (discussion=${sessionId})` : ""}`);
231
+ const result = await sendText(account.forumUrl, channelId, deliverPayload.text, account.apiKey, replyToId, sessionId);
232
+ if (result.error) {
233
+ log?.error(`[af] 发送失败: ${result.error}`);
234
+ }
235
+ else {
236
+ log?.info(`[af] 发送成功: messageId=${result.id}`);
237
+ }
238
+ }
239
+ },
240
+ onError: (err) => {
241
+ log?.error(`[af] dispatcherOptions.onError: ${String(err)}`);
242
+ },
243
+ },
244
+ // 禁用流式块合并:收集完所有块后一次性 deliver
245
+ // AgentForum 走 REST API 发消息,没有流式推送能力
246
+ replyOptions: { disableBlockStreaming: true },
247
+ });
248
+ log?.info(`[af] dispatchReply 完成`);
249
+ }
250
+ catch (err) {
251
+ log?.error(`[af] 处理消息失败: ${String(err)}`);
252
+ // 打印完整堆栈
253
+ if (err instanceof Error && err.stack) {
254
+ log?.error(`[af] 堆栈: ${err.stack}`);
255
+ }
256
+ }
257
+ };
258
+ /**
259
+ * 建立 WebSocket 连接
260
+ */
261
+ const connect = async () => {
262
+ if (isAborted)
263
+ return;
264
+ try {
265
+ cleanup();
266
+ const wsUrl = account.forumUrl.replace(/^http/, "ws");
267
+ log?.info(`[af] 连接到 ${wsUrl}/ws`);
268
+ ws = new WebSocket(`${wsUrl}/ws?apiKey=${account.apiKey}`);
269
+ ws.on("open", () => {
270
+ log?.info("[af] WebSocket 已连接");
271
+ reconnectAttempts = 0;
272
+ onReady?.({});
273
+ });
274
+ ws.on("message", async (raw) => {
275
+ try {
276
+ const event = JSON.parse(raw.toString());
277
+ // 响应服务端心跳
278
+ if (event.type === "ping") {
279
+ ws?.send(JSON.stringify({
280
+ type: "pong",
281
+ payload: {},
282
+ timestamp: new Date().toISOString(),
283
+ }));
284
+ return;
285
+ }
286
+ // 处理新消息事件
287
+ if (event.type === "message.new") {
288
+ await handleMessageNew(event.payload);
289
+ return;
290
+ }
291
+ // 其他事件类型可按需扩展
292
+ log?.debug?.(`[af] 收到事件: ${event.type}`);
293
+ }
294
+ catch (parseErr) {
295
+ log?.error(`[af] 解析 WS 消息失败: ${String(parseErr)}`);
296
+ }
297
+ });
298
+ ws.on("close", (code, reason) => {
299
+ log?.info(`[af] 连接关闭: code=${code} reason=${reason.toString()}`);
300
+ cleanup();
301
+ if (!isAborted)
302
+ scheduleReconnect();
303
+ });
304
+ ws.on("error", (err) => {
305
+ log?.error(`[af] WebSocket 错误: ${err.message}`);
306
+ onError?.(err);
307
+ });
308
+ }
309
+ catch (err) {
310
+ log?.error(`[af] 连接失败: ${String(err)}`);
311
+ cleanup();
312
+ if (!isAborted)
313
+ scheduleReconnect();
314
+ }
315
+ };
316
+ // 发起首次连接
317
+ await connect();
318
+ // 保持 gateway 运行直到 abortSignal 触发
319
+ return new Promise((resolve) => {
320
+ abortSignal.addEventListener("abort", () => resolve());
321
+ });
322
+ }
323
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * AgentForum CLI Onboarding Adapter
3
+ *
4
+ * 对齐 openclaw-qqbot/src/onboarding.ts,
5
+ * 实现 ChannelOnboardingAdapter 接口,供 `openclaw onboard` 命令使用。
6
+ * 交互式引导用户完成 AgentForum 账户配置。
7
+ */
8
+ import type { ChannelOnboardingAdapter } from "openclaw/plugin-sdk";
9
+ /**
10
+ * AgentForum Onboarding Adapter
11
+ */
12
+ export declare const agentforumOnboardingAdapter: ChannelOnboardingAdapter;
13
+ //# sourceMappingURL=onboarding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../src/onboarding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,wBAAwB,EAEzB,MAAM,qBAAqB,CAAC;AAsC7B;;GAEG;AACH,eAAO,MAAM,2BAA2B,EAAE,wBA6NzC,CAAC"}