@sumeai/sumeclaw 1.1.20 → 1.2.1
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.
- package/dist/channel-CAf407pr.d.ts +7 -0
- package/dist/chunk-RIUKFLMY.js +746 -0
- package/dist/chunk-RIUKFLMY.js.map +1 -0
- package/dist/index.d.ts +14 -3
- package/dist/index.js +83 -66
- package/dist/index.js.map +1 -1
- package/dist/setup-entry.d.ts +8 -3
- package/dist/setup-entry.js +10 -11
- package/dist/setup-entry.js.map +1 -1
- package/openclaw.plugin.json +72 -39
- package/package.json +37 -28
- package/readme.md +61 -0
- package/HARNESS_EXPERIENCE.md +0 -457
- package/dist/index.d.ts.map +0 -1
- package/dist/setup-entry.d.ts.map +0 -1
- package/dist/src/channel.d.ts +0 -7
- package/dist/src/channel.d.ts.map +0 -1
- package/dist/src/channel.js +0 -162
- package/dist/src/channel.js.map +0 -1
- package/dist/src/gateway.d.ts +0 -46
- package/dist/src/gateway.d.ts.map +0 -1
- package/dist/src/gateway.js +0 -621
- package/dist/src/gateway.js.map +0 -1
- package/dist/src/runtime.d.ts +0 -3
- package/dist/src/runtime.d.ts.map +0 -1
- package/dist/src/runtime.js +0 -12
- package/dist/src/runtime.js.map +0 -1
package/dist/src/gateway.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 友虾名片 OpenClaw Channel 插件 — Gateway 层
|
|
3
|
-
*
|
|
4
|
-
* WebSocket 连接管理:
|
|
5
|
-
* - 出站连接到 wss://api.gixin.cc/api/channel/{registrationToken}
|
|
6
|
-
* - 心跳保活(30s 间隔)
|
|
7
|
-
* - 断线自动重连(指数退避,最大 60s)
|
|
8
|
-
* - 平台下发的消息注入 OpenClaw Agent
|
|
9
|
-
* - Agent 回复通过 WS 推回平台
|
|
10
|
-
*/
|
|
11
|
-
import WebSocket from "ws";
|
|
12
|
-
export declare function getWebSocket(accountId: string): WebSocket | undefined;
|
|
13
|
-
export declare function rememberPendingReplyContext(params: {
|
|
14
|
-
accountId: string;
|
|
15
|
-
userId: string;
|
|
16
|
-
requestId?: string;
|
|
17
|
-
sessionKey: string;
|
|
18
|
-
stream?: boolean;
|
|
19
|
-
}): void;
|
|
20
|
-
export declare function getPendingReplyContext(accountId: string, userId: string): {
|
|
21
|
-
requestId?: string;
|
|
22
|
-
sessionKey: string;
|
|
23
|
-
stream?: boolean;
|
|
24
|
-
accumulatedText: string;
|
|
25
|
-
} | undefined;
|
|
26
|
-
export declare function consumePendingReplyContext(accountId: string, userId: string): {
|
|
27
|
-
requestId?: string;
|
|
28
|
-
sessionKey: string;
|
|
29
|
-
stream?: boolean;
|
|
30
|
-
accumulatedText: string;
|
|
31
|
-
} | undefined;
|
|
32
|
-
export declare function appendPendingReplyText(accountId: string, userId: string, text: string): string;
|
|
33
|
-
export interface MinicardAccount {
|
|
34
|
-
registrationToken?: string;
|
|
35
|
-
platformUrl?: string;
|
|
36
|
-
enabled?: boolean;
|
|
37
|
-
}
|
|
38
|
-
type ConnectOptions = {
|
|
39
|
-
accountId: string;
|
|
40
|
-
platformUrl?: string;
|
|
41
|
-
registrationToken: string;
|
|
42
|
-
api: any;
|
|
43
|
-
};
|
|
44
|
-
export declare function connectGateway(opts: ConnectOptions): void;
|
|
45
|
-
export {};
|
|
46
|
-
//# sourceMappingURL=gateway.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/gateway.ts"],"names":[],"mappings":"AACA;;;;;;;;;GASG;AAEH,OAAO,SAAS,MAAM,IAAI,CAAC;AA4C3B,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAErE;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,QAQA;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;gBA/BxD,MAAM;gBACN,MAAM;aACT,OAAO;qBACC,MAAM;cA8B1B;AAED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;gBAnC5D,MAAM;gBACN,MAAM;aACT,OAAO;qBACC,MAAM;cAqC1B;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAarF;AAID,MAAM,WAAW,eAAe;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAKD,KAAK,cAAc,GAAG;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,GAAG,CAAC;CACV,CAAC;AAEF,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,QAooBlD"}
|
package/dist/src/gateway.js
DELETED
|
@@ -1,621 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* 友虾名片 OpenClaw Channel 插件 — Gateway 层
|
|
4
|
-
*
|
|
5
|
-
* WebSocket 连接管理:
|
|
6
|
-
* - 出站连接到 wss://api.gixin.cc/api/channel/{registrationToken}
|
|
7
|
-
* - 心跳保活(30s 间隔)
|
|
8
|
-
* - 断线自动重连(指数退避,最大 60s)
|
|
9
|
-
* - 平台下发的消息注入 OpenClaw Agent
|
|
10
|
-
* - Agent 回复通过 WS 推回平台
|
|
11
|
-
*/
|
|
12
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
-
};
|
|
15
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.getWebSocket = getWebSocket;
|
|
17
|
-
exports.rememberPendingReplyContext = rememberPendingReplyContext;
|
|
18
|
-
exports.getPendingReplyContext = getPendingReplyContext;
|
|
19
|
-
exports.consumePendingReplyContext = consumePendingReplyContext;
|
|
20
|
-
exports.appendPendingReplyText = appendPendingReplyText;
|
|
21
|
-
exports.connectGateway = connectGateway;
|
|
22
|
-
const ws_1 = __importDefault(require("ws"));
|
|
23
|
-
const direct_dm_1 = require("openclaw/plugin-sdk/direct-dm");
|
|
24
|
-
const runtime_js_1 = require("./runtime.js");
|
|
25
|
-
/**
|
|
26
|
-
* 全局 WS 管理
|
|
27
|
-
*/
|
|
28
|
-
const wsMap = {};
|
|
29
|
-
// const __filename = fileURLToPath(import.meta.url);
|
|
30
|
-
// const __dirname = dirname(__filename);
|
|
31
|
-
// const pkg = JSON.parse(readFileSync(join(__dirname, "..", "..", "package.json"), "utf-8"));
|
|
32
|
-
// ─── 配置常量 ────────────────────────────────────────────────────────────────
|
|
33
|
-
const DEFAULT_PLATFORM_URL = "wss://api.gixin.cc";
|
|
34
|
-
const HEARTBEAT_MS = 30_000;
|
|
35
|
-
const MAX_RECONNECT_MS = 60_000;
|
|
36
|
-
const BASE_BACKOFF_MS = 1_000;
|
|
37
|
-
// ─── 连接池 ──────────────────────────────────────────────────────────────────
|
|
38
|
-
/** key: accountId → WebSocket */
|
|
39
|
-
const connections = new Map();
|
|
40
|
-
const connectingAccounts = new Set();
|
|
41
|
-
const pendingReplyContexts = new Map();
|
|
42
|
-
function replyContextKey(accountId, userId) {
|
|
43
|
-
return `${accountId}:${userId}`;
|
|
44
|
-
}
|
|
45
|
-
function getWebSocket(accountId) {
|
|
46
|
-
return connections.get(accountId);
|
|
47
|
-
}
|
|
48
|
-
function rememberPendingReplyContext(params) {
|
|
49
|
-
const key = replyContextKey(params.accountId, params.userId);
|
|
50
|
-
pendingReplyContexts.set(replyContextKey(params.accountId, params.userId), {
|
|
51
|
-
requestId: params.requestId,
|
|
52
|
-
sessionKey: params.sessionKey,
|
|
53
|
-
stream: params.stream,
|
|
54
|
-
accumulatedText: "",
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
function getPendingReplyContext(accountId, userId) {
|
|
58
|
-
return pendingReplyContexts.get(replyContextKey(accountId, userId));
|
|
59
|
-
}
|
|
60
|
-
function consumePendingReplyContext(accountId, userId) {
|
|
61
|
-
const key = replyContextKey(accountId, userId);
|
|
62
|
-
const ctx = pendingReplyContexts.get(key);
|
|
63
|
-
pendingReplyContexts.delete(key);
|
|
64
|
-
return ctx;
|
|
65
|
-
}
|
|
66
|
-
function appendPendingReplyText(accountId, userId, text) {
|
|
67
|
-
const ctx = getPendingReplyContext(accountId, userId);
|
|
68
|
-
if (!ctx)
|
|
69
|
-
return "";
|
|
70
|
-
if (!ctx.accumulatedText) {
|
|
71
|
-
ctx.accumulatedText = text;
|
|
72
|
-
}
|
|
73
|
-
else if (text.startsWith(ctx.accumulatedText)) {
|
|
74
|
-
ctx.accumulatedText = text;
|
|
75
|
-
}
|
|
76
|
-
else if (!ctx.accumulatedText.endsWith(text)) {
|
|
77
|
-
ctx.accumulatedText += text;
|
|
78
|
-
}
|
|
79
|
-
return ctx.accumulatedText;
|
|
80
|
-
}
|
|
81
|
-
function connectGateway(opts) {
|
|
82
|
-
const { accountId, registrationToken, api } = opts;
|
|
83
|
-
const platformUrl = opts.platformUrl || DEFAULT_PLATFORM_URL;
|
|
84
|
-
const existing = connections.get(accountId);
|
|
85
|
-
if (existing &&
|
|
86
|
-
(existing.readyState === ws_1.default.OPEN ||
|
|
87
|
-
existing.readyState === ws_1.default.CONNECTING)) {
|
|
88
|
-
console.log(`[sumeclaw] ♻️ reuse existing connection: ${accountId}`);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (connectingAccounts.has(accountId)) {
|
|
92
|
-
console.log(`[sumeclaw] ♻️ connection already starting: ${accountId}`);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
let ws = null;
|
|
96
|
-
let heartbeatTimer = null;
|
|
97
|
-
let reconnectTimer = null;
|
|
98
|
-
const HEARTBEAT_INTERVAL = 30_000;
|
|
99
|
-
const RECONNECT_DELAY = 5_000;
|
|
100
|
-
function start() {
|
|
101
|
-
const url = `${platformUrl.replace(/\/$/, "")}/api/channel/${registrationToken}`;
|
|
102
|
-
console.log(`[sumeclaw] 🌐 connecting -> ${url} (${accountId})`);
|
|
103
|
-
connectingAccounts.add(accountId);
|
|
104
|
-
ws = new ws_1.default(url);
|
|
105
|
-
connections.set(accountId, ws);
|
|
106
|
-
ws.on("open", () => {
|
|
107
|
-
console.log(`[sumeclaw] ✅ connected: ${accountId}`);
|
|
108
|
-
connectingAccounts.delete(accountId);
|
|
109
|
-
connections.set(accountId, ws);
|
|
110
|
-
// 🔐 上报本地插件/Agent 信息。平台端会据此刷新在线状态。
|
|
111
|
-
ws?.send(JSON.stringify({
|
|
112
|
-
type: "register_info",
|
|
113
|
-
token: registrationToken,
|
|
114
|
-
agentId: "main",
|
|
115
|
-
version: "1.1.19",
|
|
116
|
-
}));
|
|
117
|
-
// ❤️ 心跳
|
|
118
|
-
heartbeatTimer = setInterval(() => {
|
|
119
|
-
if (ws?.readyState === ws_1.default.OPEN) {
|
|
120
|
-
ws.send(JSON.stringify({ type: "heartbeat" }));
|
|
121
|
-
}
|
|
122
|
-
}, HEARTBEAT_INTERVAL);
|
|
123
|
-
});
|
|
124
|
-
ws.on("message", (data) => {
|
|
125
|
-
try {
|
|
126
|
-
const msg = JSON.parse(data.toString());
|
|
127
|
-
// 🔍 调试
|
|
128
|
-
console.log("[sumeclaw] 📩 message:", msg);
|
|
129
|
-
handleMessage(msg).catch((err) => {
|
|
130
|
-
console.error("[sumeclaw] ❌ handle message error", err);
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
catch (err) {
|
|
134
|
-
console.error("[sumeclaw] ❌ parse message error", err);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
ws.on("close", (code, reasonBuffer) => {
|
|
138
|
-
const reason = reasonBuffer.toString();
|
|
139
|
-
console.warn(`[sumeclaw] ⚠️ disconnected: ${accountId}`, { code, reason });
|
|
140
|
-
cleanup();
|
|
141
|
-
if (code === 4000 || reason.includes("replaced by a newer OpenClaw plugin connection")) {
|
|
142
|
-
console.warn(`[sumeclaw] 🛑 connection replaced, stop reconnecting: ${accountId}`);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
scheduleReconnect();
|
|
146
|
-
});
|
|
147
|
-
ws.on("error", (err) => {
|
|
148
|
-
console.error(`[sumeclaw] ❌ ws error: ${accountId}`, err);
|
|
149
|
-
connectingAccounts.delete(accountId);
|
|
150
|
-
ws?.close();
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
async function handleMessage(msg) {
|
|
154
|
-
switch (msg.type) {
|
|
155
|
-
case "heartbeat_ack":
|
|
156
|
-
// 心跳回应
|
|
157
|
-
break;
|
|
158
|
-
case "hooks_agent":
|
|
159
|
-
await handleHooksAgent(msg.payload ?? msg);
|
|
160
|
-
break;
|
|
161
|
-
case "message":
|
|
162
|
-
// 👉 这里接入 OpenClaw
|
|
163
|
-
console.log("[sumeclaw] 💬 收到消息:", msg);
|
|
164
|
-
await dispatchToOpenClaw({
|
|
165
|
-
agentId: msg.agentId || "main",
|
|
166
|
-
message: msg.text || msg.message || "",
|
|
167
|
-
messageId: msg.id || msg.messageId,
|
|
168
|
-
sessionKey: msg.sessionKey ||
|
|
169
|
-
`agent:${msg.agentId || "main"}:user:${msg.from || msg.userId || "unknown"}`,
|
|
170
|
-
userId: msg.from || msg.userId || msg.senderId || "unknown",
|
|
171
|
-
raw: msg,
|
|
172
|
-
requestId: msg.requestId,
|
|
173
|
-
stream: Boolean(msg.stream),
|
|
174
|
-
});
|
|
175
|
-
break;
|
|
176
|
-
case "registered":
|
|
177
|
-
console.log(`[sumeclaw] 🔐 注册成功: ${accountId}`);
|
|
178
|
-
break;
|
|
179
|
-
default:
|
|
180
|
-
console.log("[sumeclaw] ❓ 未处理消息:", msg);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
async function handleHooksAgent(payload) {
|
|
184
|
-
const meta = payload?.meta ?? {};
|
|
185
|
-
const userId = String(meta.user_id || payload.userId || payload.from || "unknown");
|
|
186
|
-
const agentId = String(payload.agentId || meta.agent_id || "main");
|
|
187
|
-
const text = String(payload.message || payload.text || "");
|
|
188
|
-
const sessionKey = payload.sessionKey || `agent:${agentId}:user:${userId}`;
|
|
189
|
-
console.log("[sumeclaw] 🧠 hooks_agent -> OpenClaw", {
|
|
190
|
-
accountId,
|
|
191
|
-
agentId,
|
|
192
|
-
sessionKey,
|
|
193
|
-
userId,
|
|
194
|
-
text: text.slice(0, 100),
|
|
195
|
-
});
|
|
196
|
-
rememberPendingReplyContext({
|
|
197
|
-
accountId,
|
|
198
|
-
userId,
|
|
199
|
-
requestId: payload.requestId,
|
|
200
|
-
sessionKey,
|
|
201
|
-
stream: Boolean(payload.stream),
|
|
202
|
-
});
|
|
203
|
-
await dispatchToOpenClaw({
|
|
204
|
-
agentId,
|
|
205
|
-
message: text,
|
|
206
|
-
messageId: payload.messageId || `${sessionKey}:${Date.now()}`,
|
|
207
|
-
sessionKey,
|
|
208
|
-
userId,
|
|
209
|
-
raw: payload,
|
|
210
|
-
requestId: payload.requestId,
|
|
211
|
-
stream: Boolean(payload.stream),
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
function sendAgentReply(input) {
|
|
215
|
-
const activeWs = connections.get(accountId) ?? ws;
|
|
216
|
-
if (!activeWs || activeWs.readyState !== ws_1.default.OPEN) {
|
|
217
|
-
console.error("[sumeclaw] ❌ cannot send agent_reply, websocket is not open", {
|
|
218
|
-
accountId,
|
|
219
|
-
requestId: input.requestId,
|
|
220
|
-
readyState: activeWs?.readyState,
|
|
221
|
-
});
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
activeWs.send(JSON.stringify({
|
|
225
|
-
type: "agent_reply",
|
|
226
|
-
requestId: input.requestId,
|
|
227
|
-
userId: input.userId,
|
|
228
|
-
sessionKey: input.sessionKey,
|
|
229
|
-
text: input.text,
|
|
230
|
-
messageId: input.messageId ?? `sumeclaw-${Date.now()}`,
|
|
231
|
-
ok: input.ok ?? true,
|
|
232
|
-
}));
|
|
233
|
-
console.log("[sumeclaw] 📤 agent_reply sent", {
|
|
234
|
-
accountId,
|
|
235
|
-
requestId: input.requestId,
|
|
236
|
-
userId: input.userId,
|
|
237
|
-
textLength: input.text.length,
|
|
238
|
-
});
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
function sendAgentDelta(input) {
|
|
242
|
-
const activeWs = connections.get(accountId) ?? ws;
|
|
243
|
-
if (!activeWs || activeWs.readyState !== ws_1.default.OPEN) {
|
|
244
|
-
console.error("[sumeclaw] ❌ cannot send agent_delta, websocket is not open", {
|
|
245
|
-
accountId,
|
|
246
|
-
requestId: input.requestId,
|
|
247
|
-
readyState: activeWs?.readyState,
|
|
248
|
-
});
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
activeWs.send(JSON.stringify({
|
|
252
|
-
type: "agent_delta",
|
|
253
|
-
requestId: input.requestId,
|
|
254
|
-
userId: input.userId,
|
|
255
|
-
sessionKey: input.sessionKey,
|
|
256
|
-
text: input.text,
|
|
257
|
-
messageId: input.messageId ?? `sumeclaw-${Date.now()}`,
|
|
258
|
-
}));
|
|
259
|
-
console.log("[sumeclaw] 📤 agent_delta sent", {
|
|
260
|
-
accountId,
|
|
261
|
-
requestId: input.requestId,
|
|
262
|
-
userId: input.userId,
|
|
263
|
-
textLength: input.text.length,
|
|
264
|
-
});
|
|
265
|
-
return true;
|
|
266
|
-
}
|
|
267
|
-
function sendAgentDone(input) {
|
|
268
|
-
const activeWs = connections.get(accountId) ?? ws;
|
|
269
|
-
if (!activeWs || activeWs.readyState !== ws_1.default.OPEN) {
|
|
270
|
-
console.error("[sumeclaw] ❌ cannot send agent_done, websocket is not open", {
|
|
271
|
-
accountId,
|
|
272
|
-
requestId: input.requestId,
|
|
273
|
-
readyState: activeWs?.readyState,
|
|
274
|
-
});
|
|
275
|
-
return false;
|
|
276
|
-
}
|
|
277
|
-
activeWs.send(JSON.stringify({
|
|
278
|
-
type: "agent_done",
|
|
279
|
-
requestId: input.requestId,
|
|
280
|
-
userId: input.userId,
|
|
281
|
-
sessionKey: input.sessionKey,
|
|
282
|
-
text: input.text,
|
|
283
|
-
messageId: input.messageId ?? `sumeclaw-${Date.now()}`,
|
|
284
|
-
ok: input.ok ?? true,
|
|
285
|
-
}));
|
|
286
|
-
console.log("[sumeclaw] 📤 agent_done sent", {
|
|
287
|
-
accountId,
|
|
288
|
-
requestId: input.requestId,
|
|
289
|
-
userId: input.userId,
|
|
290
|
-
textLength: input.text.length,
|
|
291
|
-
});
|
|
292
|
-
return true;
|
|
293
|
-
}
|
|
294
|
-
function finishStreamReply(input) {
|
|
295
|
-
const latest = consumePendingReplyContext(accountId, input.userId);
|
|
296
|
-
if (!latest) {
|
|
297
|
-
console.warn("[sumeclaw] ⚠️ no pending stream context to finish", {
|
|
298
|
-
accountId,
|
|
299
|
-
requestId: input.requestId,
|
|
300
|
-
userId: input.userId,
|
|
301
|
-
});
|
|
302
|
-
return false;
|
|
303
|
-
}
|
|
304
|
-
return sendAgentDone({
|
|
305
|
-
requestId: latest.requestId ?? input.requestId,
|
|
306
|
-
userId: input.userId,
|
|
307
|
-
sessionKey: latest.sessionKey ?? input.sessionKey,
|
|
308
|
-
text: latest.accumulatedText,
|
|
309
|
-
ok: input.ok,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
function finishBufferedReply(input) {
|
|
313
|
-
const latest = consumePendingReplyContext(accountId, input.userId);
|
|
314
|
-
if (!latest) {
|
|
315
|
-
console.warn("[sumeclaw] ⚠️ no pending reply context to finish", {
|
|
316
|
-
accountId,
|
|
317
|
-
requestId: input.requestId,
|
|
318
|
-
userId: input.userId,
|
|
319
|
-
});
|
|
320
|
-
return false;
|
|
321
|
-
}
|
|
322
|
-
return sendAgentReply({
|
|
323
|
-
requestId: latest.requestId ?? input.requestId,
|
|
324
|
-
userId: input.userId,
|
|
325
|
-
sessionKey: latest.sessionKey ?? input.sessionKey,
|
|
326
|
-
text: latest.accumulatedText,
|
|
327
|
-
ok: input.ok,
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
async function dispatchToOpenClaw(input) {
|
|
331
|
-
const runtime = (0, runtime_js_1.tryGetSumeclawRuntime)() ?? api?.runtime;
|
|
332
|
-
const turn = runtime?.channel?.turn;
|
|
333
|
-
if (!turn?.run) {
|
|
334
|
-
const directReplyDispatcher = runtime?.channel?.reply?.dispatchReplyWithBufferedBlockDispatcher;
|
|
335
|
-
if (directReplyDispatcher) {
|
|
336
|
-
await dispatchViaDirectDmRuntime(runtime, input);
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
console.error("[sumeclaw] ❌ 当前 OpenClaw runtime 不提供可用的 channel 入站分发能力");
|
|
340
|
-
console.error("[sumeclaw] runtime debug:", {
|
|
341
|
-
hasStoredRuntime: Boolean((0, runtime_js_1.tryGetSumeclawRuntime)()),
|
|
342
|
-
apiKeys: api ? Object.keys(api) : [],
|
|
343
|
-
runtimeKeys: runtime ? Object.keys(runtime) : [],
|
|
344
|
-
channelKeys: runtime?.channel ? Object.keys(runtime.channel) : [],
|
|
345
|
-
});
|
|
346
|
-
sendAgentReply({
|
|
347
|
-
requestId: input.requestId,
|
|
348
|
-
userId: input.userId,
|
|
349
|
-
sessionKey: input.sessionKey,
|
|
350
|
-
text: "OpenClaw 插件未获得运行时分发能力,请升级插件入口后重启 OpenClaw Gateway。",
|
|
351
|
-
ok: false,
|
|
352
|
-
});
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
const now = Date.now();
|
|
356
|
-
const messageId = String(input.messageId || `${input.sessionKey}:${now}`);
|
|
357
|
-
const senderLabel = `友虾用户 ${input.userId}`;
|
|
358
|
-
try {
|
|
359
|
-
const result = await turn.run({
|
|
360
|
-
channel: "sumeclaw",
|
|
361
|
-
accountId,
|
|
362
|
-
raw: input.raw,
|
|
363
|
-
adapter: {
|
|
364
|
-
ingest() {
|
|
365
|
-
return {
|
|
366
|
-
id: messageId,
|
|
367
|
-
timestamp: now,
|
|
368
|
-
rawText: input.message,
|
|
369
|
-
textForAgent: input.message,
|
|
370
|
-
textForCommands: input.message,
|
|
371
|
-
raw: input.raw,
|
|
372
|
-
};
|
|
373
|
-
},
|
|
374
|
-
classify(turnInput) {
|
|
375
|
-
return {
|
|
376
|
-
kind: "message",
|
|
377
|
-
canStartAgentTurn: Boolean(turnInput.rawText),
|
|
378
|
-
};
|
|
379
|
-
},
|
|
380
|
-
resolveTurn(turnInput) {
|
|
381
|
-
return {
|
|
382
|
-
sender: {
|
|
383
|
-
id: input.userId,
|
|
384
|
-
name: senderLabel,
|
|
385
|
-
displayLabel: senderLabel,
|
|
386
|
-
isBot: false,
|
|
387
|
-
isSelf: false,
|
|
388
|
-
},
|
|
389
|
-
conversation: {
|
|
390
|
-
kind: "direct",
|
|
391
|
-
id: input.userId,
|
|
392
|
-
label: senderLabel,
|
|
393
|
-
routePeer: input.userId,
|
|
394
|
-
},
|
|
395
|
-
route: {
|
|
396
|
-
agentId: input.agentId,
|
|
397
|
-
accountId,
|
|
398
|
-
routeSessionKey: input.sessionKey,
|
|
399
|
-
dispatchSessionKey: input.sessionKey,
|
|
400
|
-
persistedSessionKey: input.sessionKey,
|
|
401
|
-
mainSessionKey: input.sessionKey,
|
|
402
|
-
createIfMissing: true,
|
|
403
|
-
},
|
|
404
|
-
reply: {
|
|
405
|
-
to: input.userId,
|
|
406
|
-
originatingTo: input.userId,
|
|
407
|
-
replyTarget: input.userId,
|
|
408
|
-
sourceReplyDeliveryMode: "direct",
|
|
409
|
-
},
|
|
410
|
-
access: {
|
|
411
|
-
dm: {
|
|
412
|
-
policy: "open",
|
|
413
|
-
allowed: true,
|
|
414
|
-
allowFrom: [],
|
|
415
|
-
},
|
|
416
|
-
group: {
|
|
417
|
-
isGroup: false,
|
|
418
|
-
routeAllowed: true,
|
|
419
|
-
senderAllowed: true,
|
|
420
|
-
requireMention: false,
|
|
421
|
-
},
|
|
422
|
-
commands: {
|
|
423
|
-
authorized: true,
|
|
424
|
-
},
|
|
425
|
-
mentions: {
|
|
426
|
-
canDetectMention: false,
|
|
427
|
-
wasMentioned: true,
|
|
428
|
-
},
|
|
429
|
-
},
|
|
430
|
-
message: {
|
|
431
|
-
body: turnInput.rawText,
|
|
432
|
-
rawBody: turnInput.rawText,
|
|
433
|
-
bodyForAgent: turnInput.textForAgent,
|
|
434
|
-
commandBody: turnInput.textForCommands,
|
|
435
|
-
envelopeFrom: senderLabel,
|
|
436
|
-
senderLabel,
|
|
437
|
-
preview: turnInput.rawText.slice(0, 120),
|
|
438
|
-
},
|
|
439
|
-
delivery: {
|
|
440
|
-
deliver: async (payload) => {
|
|
441
|
-
const text = String(payload?.text || payload?.body || "");
|
|
442
|
-
if (!text)
|
|
443
|
-
return { visibleReplySent: false };
|
|
444
|
-
const replyId = `sumeclaw-${Date.now()}`;
|
|
445
|
-
if (input.stream) {
|
|
446
|
-
appendPendingReplyText(accountId, input.userId, text);
|
|
447
|
-
sendAgentDelta({
|
|
448
|
-
requestId: input.requestId,
|
|
449
|
-
userId: input.userId,
|
|
450
|
-
sessionKey: input.sessionKey,
|
|
451
|
-
text,
|
|
452
|
-
messageId: replyId,
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
appendPendingReplyText(accountId, input.userId, text);
|
|
457
|
-
}
|
|
458
|
-
return {
|
|
459
|
-
messageIds: [replyId],
|
|
460
|
-
visibleReplySent: true,
|
|
461
|
-
};
|
|
462
|
-
},
|
|
463
|
-
onError(err) {
|
|
464
|
-
console.error("[sumeclaw] ❌ deliver error", err);
|
|
465
|
-
},
|
|
466
|
-
},
|
|
467
|
-
};
|
|
468
|
-
},
|
|
469
|
-
},
|
|
470
|
-
});
|
|
471
|
-
console.log("[sumeclaw] ✅ OpenClaw turn dispatched", result?.admission ?? "");
|
|
472
|
-
if (input.stream) {
|
|
473
|
-
finishStreamReply(input);
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
finishBufferedReply(input);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
catch (err) {
|
|
480
|
-
console.error("[sumeclaw] ❌ OpenClaw turn dispatch failed", err);
|
|
481
|
-
sendAgentReply({
|
|
482
|
-
requestId: input.requestId,
|
|
483
|
-
userId: input.userId,
|
|
484
|
-
sessionKey: input.sessionKey,
|
|
485
|
-
text: "OpenClaw 本地处理消息失败,请稍后再试。",
|
|
486
|
-
ok: false,
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
async function dispatchViaDirectDmRuntime(runtime, input) {
|
|
491
|
-
const now = Date.now();
|
|
492
|
-
const messageId = String(input.messageId || `${input.sessionKey}:${now}`);
|
|
493
|
-
const senderLabel = `友虾用户 ${input.userId}`;
|
|
494
|
-
const baseCfg = api?.getConfig?.() ?? api?.config ?? runtime?.config ?? {};
|
|
495
|
-
const cfg = input.stream
|
|
496
|
-
? {
|
|
497
|
-
...baseCfg,
|
|
498
|
-
agents: {
|
|
499
|
-
...(baseCfg.agents ?? {}),
|
|
500
|
-
defaults: {
|
|
501
|
-
...(baseCfg.agents?.defaults ?? {}),
|
|
502
|
-
blockStreamingDefault: "on",
|
|
503
|
-
blockStreamingBreak: "text_end",
|
|
504
|
-
blockStreamingChunk: {
|
|
505
|
-
minChars: 12,
|
|
506
|
-
maxChars: 80,
|
|
507
|
-
breakPreference: "sentence",
|
|
508
|
-
...(baseCfg.agents?.defaults?.blockStreamingChunk ?? {}),
|
|
509
|
-
},
|
|
510
|
-
},
|
|
511
|
-
},
|
|
512
|
-
channels: {
|
|
513
|
-
...(baseCfg.channels ?? {}),
|
|
514
|
-
sumeclaw: {
|
|
515
|
-
...(baseCfg.channels?.sumeclaw ?? {}),
|
|
516
|
-
blockStreaming: true,
|
|
517
|
-
},
|
|
518
|
-
},
|
|
519
|
-
}
|
|
520
|
-
: baseCfg;
|
|
521
|
-
try {
|
|
522
|
-
await (0, direct_dm_1.dispatchInboundDirectDmWithRuntime)({
|
|
523
|
-
cfg,
|
|
524
|
-
runtime,
|
|
525
|
-
channel: "sumeclaw",
|
|
526
|
-
channelLabel: "友虾名片",
|
|
527
|
-
accountId,
|
|
528
|
-
peer: {
|
|
529
|
-
kind: "direct",
|
|
530
|
-
id: input.userId,
|
|
531
|
-
},
|
|
532
|
-
senderId: input.userId,
|
|
533
|
-
senderAddress: input.userId,
|
|
534
|
-
recipientAddress: "sumeclaw",
|
|
535
|
-
conversationLabel: senderLabel,
|
|
536
|
-
rawBody: input.message,
|
|
537
|
-
messageId,
|
|
538
|
-
timestamp: now,
|
|
539
|
-
bodyForAgent: input.message,
|
|
540
|
-
commandBody: input.message,
|
|
541
|
-
commandAuthorized: true,
|
|
542
|
-
provider: "sumeclaw",
|
|
543
|
-
surface: "sumeclaw",
|
|
544
|
-
originatingChannel: "sumeclaw",
|
|
545
|
-
originatingTo: input.userId,
|
|
546
|
-
extraContext: {
|
|
547
|
-
AccountId: accountId,
|
|
548
|
-
RouteAgentId: input.agentId,
|
|
549
|
-
RequestId: input.requestId,
|
|
550
|
-
},
|
|
551
|
-
deliver: async (payload) => {
|
|
552
|
-
const text = String(payload?.text || payload?.body || "");
|
|
553
|
-
if (!text)
|
|
554
|
-
return { visibleReplySent: false };
|
|
555
|
-
const replyId = `sumeclaw-${Date.now()}`;
|
|
556
|
-
if (input.stream) {
|
|
557
|
-
appendPendingReplyText(accountId, input.userId, text);
|
|
558
|
-
sendAgentDelta({
|
|
559
|
-
requestId: input.requestId,
|
|
560
|
-
userId: input.userId,
|
|
561
|
-
sessionKey: input.sessionKey,
|
|
562
|
-
text,
|
|
563
|
-
messageId: replyId,
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
appendPendingReplyText(accountId, input.userId, text);
|
|
568
|
-
}
|
|
569
|
-
return {
|
|
570
|
-
messageIds: [replyId],
|
|
571
|
-
visibleReplySent: true,
|
|
572
|
-
};
|
|
573
|
-
},
|
|
574
|
-
onDispatchError: (err) => {
|
|
575
|
-
console.error("[sumeclaw] ❌ Direct DM dispatch error", err);
|
|
576
|
-
},
|
|
577
|
-
onRecordError: (err) => {
|
|
578
|
-
console.error("[sumeclaw] ❌ Direct DM record error", err);
|
|
579
|
-
},
|
|
580
|
-
});
|
|
581
|
-
console.log("[sumeclaw] ✅ OpenClaw Direct DM dispatched");
|
|
582
|
-
if (input.stream) {
|
|
583
|
-
finishStreamReply(input);
|
|
584
|
-
}
|
|
585
|
-
else {
|
|
586
|
-
finishBufferedReply(input);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
catch (err) {
|
|
590
|
-
console.error("[sumeclaw] ❌ Direct DM dispatch failed", err);
|
|
591
|
-
sendAgentReply({
|
|
592
|
-
requestId: input.requestId,
|
|
593
|
-
userId: input.userId,
|
|
594
|
-
sessionKey: input.sessionKey,
|
|
595
|
-
text: "OpenClaw 本地处理消息失败,请稍后再试。",
|
|
596
|
-
ok: false,
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
function cleanup() {
|
|
601
|
-
connectingAccounts.delete(accountId);
|
|
602
|
-
if (heartbeatTimer) {
|
|
603
|
-
clearInterval(heartbeatTimer);
|
|
604
|
-
heartbeatTimer = null;
|
|
605
|
-
}
|
|
606
|
-
if (connections.get(accountId) === ws) {
|
|
607
|
-
connections.delete(accountId);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
function scheduleReconnect() {
|
|
611
|
-
if (reconnectTimer)
|
|
612
|
-
return;
|
|
613
|
-
console.log(`[sumeclaw] 🔁 reconnecting in ${RECONNECT_DELAY / 1000}s`);
|
|
614
|
-
reconnectTimer = setTimeout(() => {
|
|
615
|
-
reconnectTimer = null;
|
|
616
|
-
start();
|
|
617
|
-
}, RECONNECT_DELAY);
|
|
618
|
-
}
|
|
619
|
-
start();
|
|
620
|
-
}
|
|
621
|
-
//# sourceMappingURL=gateway.js.map
|