adp-openclaw 0.0.13 → 0.0.14
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/package.json +5 -5
- package/src/channel.ts +39 -105
- package/src/config-schema.ts +2 -3
- package/src/monitor.ts +31 -29
- package/src/runtime.ts +1 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adp-openclaw",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "OpenClaw demo channel plugin (Go WebSocket backend)",
|
|
3
|
+
"version": "0.0.14",
|
|
4
|
+
"description": "ADP-OpenClaw demo channel plugin (Go WebSocket backend)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"ws": "^8.16.0",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
],
|
|
17
17
|
"channel": {
|
|
18
18
|
"id": "adp-openclaw",
|
|
19
|
-
"label": "ADP
|
|
20
|
-
"selectionLabel": "ADP
|
|
19
|
+
"label": "ADP-OpenClaw",
|
|
20
|
+
"selectionLabel": "ADP-OpenClaw",
|
|
21
21
|
"docsPath": "/channels/adp-openclaw",
|
|
22
|
-
"blurb": "ADP channel backed by a Go WebSocket server.",
|
|
22
|
+
"blurb": "ADP-OpenClaw channel backed by a Go WebSocket server.",
|
|
23
23
|
"order": 999
|
|
24
24
|
}
|
|
25
25
|
}
|
package/src/channel.ts
CHANGED
|
@@ -7,14 +7,15 @@ import {
|
|
|
7
7
|
DEFAULT_ACCOUNT_ID,
|
|
8
8
|
} from "openclaw/plugin-sdk";
|
|
9
9
|
|
|
10
|
+
// Default WebSocket URL for ADP OpenClaw
|
|
11
|
+
const DEFAULT_WS_URL = "wss://wss.lke.cloud.tencent.com/bot/gateway/ws/";
|
|
12
|
+
|
|
10
13
|
// Channel-level config type (from channels["adp-openclaw"])
|
|
11
14
|
export type AdpOpenclawChannelConfig = {
|
|
12
15
|
enabled?: boolean;
|
|
13
|
-
|
|
14
|
-
wsUrl?: string;
|
|
16
|
+
wsUrl?: string; // WebSocket URL (optional, has default)
|
|
15
17
|
clientToken?: string;
|
|
16
|
-
|
|
17
|
-
pollIntervalMs?: number;
|
|
18
|
+
signKey?: string; // HMAC key for signature generation
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
export type ResolvedAdpOpenclawAccount = {
|
|
@@ -22,29 +23,20 @@ export type ResolvedAdpOpenclawAccount = {
|
|
|
22
23
|
name: string;
|
|
23
24
|
enabled: boolean;
|
|
24
25
|
configured: boolean;
|
|
25
|
-
|
|
26
|
+
wsUrl: string; // WebSocket URL
|
|
26
27
|
clientToken: string;
|
|
27
|
-
|
|
28
|
-
pollIntervalMs: number;
|
|
28
|
+
signKey: string; // HMAC key for signature generation
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
function resolveAdpOpenclawCredentials(channelCfg?: AdpOpenclawChannelConfig): {
|
|
32
|
-
|
|
32
|
+
wsUrl: string;
|
|
33
33
|
clientToken: string;
|
|
34
|
-
|
|
35
|
-
pollIntervalMs: number;
|
|
34
|
+
signKey: string;
|
|
36
35
|
} | null {
|
|
37
|
-
// Get
|
|
38
|
-
let
|
|
39
|
-
if (!
|
|
40
|
-
|
|
41
|
-
serverUrl = channelCfg.wsUrl
|
|
42
|
-
.replace(/^wss:\/\//, "https://")
|
|
43
|
-
.replace(/^ws:\/\//, "http://")
|
|
44
|
-
.replace(/\/ws\/?$/, "");
|
|
45
|
-
}
|
|
46
|
-
if (!serverUrl) {
|
|
47
|
-
serverUrl = process.env.ADP_OPENCLAW_SERVER_URL || "";
|
|
36
|
+
// Get wsUrl from config or env (has default value)
|
|
37
|
+
let wsUrl = channelCfg?.wsUrl?.trim();
|
|
38
|
+
if (!wsUrl) {
|
|
39
|
+
wsUrl = process.env.ADP_OPENCLAW_WS_URL || DEFAULT_WS_URL;
|
|
48
40
|
}
|
|
49
41
|
|
|
50
42
|
// Get clientToken from config or env
|
|
@@ -53,20 +45,18 @@ function resolveAdpOpenclawCredentials(channelCfg?: AdpOpenclawChannelConfig): {
|
|
|
53
45
|
clientToken = process.env.ADP_OPENCLAW_CLIENT_TOKEN || "";
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
// Get
|
|
57
|
-
let
|
|
58
|
-
if (!
|
|
59
|
-
|
|
48
|
+
// Get signKey from config or env (default: ADPOpenClaw)
|
|
49
|
+
let signKey = channelCfg?.signKey?.trim();
|
|
50
|
+
if (!signKey) {
|
|
51
|
+
signKey = process.env.ADP_OPENCLAW_SIGN_KEY || "ADPOpenClaw";
|
|
60
52
|
}
|
|
61
53
|
|
|
62
|
-
//
|
|
63
|
-
if (!
|
|
54
|
+
// clientToken is required for configured status (wsUrl has default)
|
|
55
|
+
if (!clientToken) {
|
|
64
56
|
return null;
|
|
65
57
|
}
|
|
66
58
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return { serverUrl, clientToken, hmacKey, pollIntervalMs };
|
|
59
|
+
return { wsUrl, clientToken, signKey };
|
|
70
60
|
}
|
|
71
61
|
|
|
72
62
|
function resolveAccount(cfg: ClawdbotConfig, accountId?: string): ResolvedAdpOpenclawAccount {
|
|
@@ -79,10 +69,9 @@ function resolveAccount(cfg: ClawdbotConfig, accountId?: string): ResolvedAdpOpe
|
|
|
79
69
|
name: "ADP OpenClaw",
|
|
80
70
|
enabled,
|
|
81
71
|
configured: Boolean(creds),
|
|
82
|
-
|
|
72
|
+
wsUrl: creds?.wsUrl || DEFAULT_WS_URL,
|
|
83
73
|
clientToken: creds?.clientToken || "",
|
|
84
|
-
|
|
85
|
-
pollIntervalMs: creds?.pollIntervalMs || 1000,
|
|
74
|
+
signKey: creds?.signKey || "ADPOpenClaw",
|
|
86
75
|
};
|
|
87
76
|
}
|
|
88
77
|
|
|
@@ -115,11 +104,9 @@ export const adpOpenclawPlugin: ChannelPlugin<ResolvedAdpOpenclawAccount> = {
|
|
|
115
104
|
additionalProperties: false,
|
|
116
105
|
properties: {
|
|
117
106
|
enabled: { type: "boolean" },
|
|
118
|
-
|
|
119
|
-
wsUrl: { type: "string" },
|
|
107
|
+
wsUrl: { type: "string" }, // WebSocket URL (optional, default: wss://wss.lke.cloud.tencent.com/bot/gateway/ws/)
|
|
120
108
|
clientToken: { type: "string" },
|
|
121
|
-
|
|
122
|
-
pollIntervalMs: { type: "integer", minimum: 100 },
|
|
109
|
+
signKey: { type: "string" },
|
|
123
110
|
},
|
|
124
111
|
},
|
|
125
112
|
},
|
|
@@ -155,7 +142,7 @@ export const adpOpenclawPlugin: ChannelPlugin<ResolvedAdpOpenclawAccount> = {
|
|
|
155
142
|
name: account.name,
|
|
156
143
|
enabled: account.enabled,
|
|
157
144
|
configured: account.configured,
|
|
158
|
-
|
|
145
|
+
wsUrl: account.wsUrl,
|
|
159
146
|
}),
|
|
160
147
|
resolveAllowFrom: () => [],
|
|
161
148
|
formatAllowFrom: ({ allowFrom }) => allowFrom,
|
|
@@ -184,39 +171,27 @@ export const adpOpenclawPlugin: ChannelPlugin<ResolvedAdpOpenclawAccount> = {
|
|
|
184
171
|
collectStatusIssues: () => [],
|
|
185
172
|
buildChannelSummary: ({ snapshot }) => ({
|
|
186
173
|
configured: snapshot.configured ?? false,
|
|
187
|
-
|
|
174
|
+
wsUrl: snapshot.wsUrl ?? null,
|
|
188
175
|
running: snapshot.running ?? false,
|
|
189
176
|
lastStartAt: snapshot.lastStartAt ?? null,
|
|
190
177
|
lastStopAt: snapshot.lastStopAt ?? null,
|
|
191
178
|
lastError: snapshot.lastError ?? null,
|
|
192
179
|
}),
|
|
193
|
-
probeAccount: async ({ cfg
|
|
180
|
+
probeAccount: async ({ cfg }) => {
|
|
181
|
+
// For WebSocket-only architecture, we just check if config is valid
|
|
194
182
|
const account = resolveAccount(cfg);
|
|
195
183
|
const start = Date.now();
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
clearTimeout(timeout);
|
|
201
|
-
const data = (await res.json()) as { ok?: boolean };
|
|
202
|
-
return {
|
|
203
|
-
ok: data.ok === true,
|
|
204
|
-
elapsedMs: Date.now() - start,
|
|
205
|
-
};
|
|
206
|
-
} catch (err) {
|
|
207
|
-
return {
|
|
208
|
-
ok: false,
|
|
209
|
-
error: err instanceof Error ? err.message : String(err),
|
|
210
|
-
elapsedMs: Date.now() - start,
|
|
211
|
-
};
|
|
212
|
-
}
|
|
184
|
+
return {
|
|
185
|
+
ok: account.configured && Boolean(account.clientToken),
|
|
186
|
+
elapsedMs: Date.now() - start,
|
|
187
|
+
};
|
|
213
188
|
},
|
|
214
189
|
buildAccountSnapshot: ({ account, runtime, probe }) => ({
|
|
215
190
|
accountId: account.accountId,
|
|
216
191
|
name: account.name,
|
|
217
192
|
enabled: account.enabled,
|
|
218
193
|
configured: account.configured,
|
|
219
|
-
|
|
194
|
+
wsUrl: account.wsUrl,
|
|
220
195
|
running: runtime?.running ?? false,
|
|
221
196
|
lastStartAt: runtime?.lastStartAt ?? null,
|
|
222
197
|
lastStopAt: runtime?.lastStopAt ?? null,
|
|
@@ -227,61 +202,20 @@ export const adpOpenclawPlugin: ChannelPlugin<ResolvedAdpOpenclawAccount> = {
|
|
|
227
202
|
gateway: {
|
|
228
203
|
startAccount: async (ctx) => {
|
|
229
204
|
const account = ctx.account;
|
|
230
|
-
ctx.setStatus({ accountId: account.accountId,
|
|
231
|
-
ctx.log?.info(`[adp-openclaw] starting
|
|
205
|
+
ctx.setStatus({ accountId: account.accountId, wsUrl: account.wsUrl });
|
|
206
|
+
ctx.log?.info(`[adp-openclaw] starting WebSocket connection → ${account.wsUrl}`);
|
|
232
207
|
|
|
233
208
|
const { monitorAdpOpenclaw } = await import("./monitor.js");
|
|
234
209
|
return monitorAdpOpenclaw({
|
|
235
|
-
|
|
210
|
+
wsUrl: account.wsUrl,
|
|
236
211
|
clientToken: account.clientToken,
|
|
237
|
-
|
|
238
|
-
pollIntervalMs: account.pollIntervalMs,
|
|
212
|
+
signKey: account.signKey,
|
|
239
213
|
abortSignal: ctx.abortSignal,
|
|
240
214
|
log: ctx.log,
|
|
241
215
|
cfg: ctx.cfg,
|
|
242
216
|
});
|
|
243
217
|
},
|
|
244
218
|
},
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
target: string;
|
|
248
|
-
message: string;
|
|
249
|
-
cfg: unknown;
|
|
250
|
-
context?: {
|
|
251
|
-
conversationId?: string;
|
|
252
|
-
userId?: string;
|
|
253
|
-
username?: string;
|
|
254
|
-
tenantId?: string;
|
|
255
|
-
};
|
|
256
|
-
}) => {
|
|
257
|
-
const account = resolveAccount(cfg as ClawdbotConfig);
|
|
258
|
-
const conversationId = context?.conversationId || "";
|
|
259
|
-
|
|
260
|
-
// Extract user info from context for response tracing
|
|
261
|
-
const userInfo = context?.userId ? {
|
|
262
|
-
userId: context.userId,
|
|
263
|
-
username: context.username,
|
|
264
|
-
tenantId: context.tenantId,
|
|
265
|
-
} : undefined;
|
|
266
|
-
|
|
267
|
-
const res = await fetch(`${account.serverUrl}/send`, {
|
|
268
|
-
method: "POST",
|
|
269
|
-
headers: {
|
|
270
|
-
"Content-Type": "application/json",
|
|
271
|
-
Authorization: `Bearer ${account.clientToken}`,
|
|
272
|
-
},
|
|
273
|
-
body: JSON.stringify({
|
|
274
|
-
to: target,
|
|
275
|
-
text: message,
|
|
276
|
-
conversationId,
|
|
277
|
-
user: userInfo,
|
|
278
|
-
}),
|
|
279
|
-
});
|
|
280
|
-
const data = (await res.json()) as { ok?: boolean; message?: { id?: string } };
|
|
281
|
-
return {
|
|
282
|
-
ok: data.ok === true,
|
|
283
|
-
messageId: data.message?.id,
|
|
284
|
-
};
|
|
285
|
-
},
|
|
286
|
-
},
|
|
219
|
+
// Note: outbound.send is not available in WebSocket-only architecture
|
|
220
|
+
// All message sending is done through the WebSocket connection in monitor.ts
|
|
287
221
|
};
|
package/src/config-schema.ts
CHANGED
|
@@ -2,8 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
|
|
3
3
|
export const AdpOpenclawConfigSchema = z.object({
|
|
4
4
|
enabled: z.boolean().optional(),
|
|
5
|
-
|
|
5
|
+
wsUrl: z.string().optional(), // WebSocket URL (optional, default: wss://wss.lke.cloud.tencent.com/bot/gateway/ws/)
|
|
6
6
|
clientToken: z.string().optional(),
|
|
7
|
-
|
|
8
|
-
pollIntervalMs: z.number().optional(),
|
|
7
|
+
signKey: z.string().optional(),
|
|
9
8
|
});
|
package/src/monitor.ts
CHANGED
|
@@ -5,11 +5,13 @@ import type { PluginLogger, ClawdbotConfig } from "openclaw/plugin-sdk";
|
|
|
5
5
|
import { getAdpOpenclawRuntime } from "./runtime.js";
|
|
6
6
|
import crypto from "crypto";
|
|
7
7
|
|
|
8
|
+
// WebSocket reconnect delay (fixed at 1 second)
|
|
9
|
+
const RECONNECT_DELAY_MS = 1000;
|
|
10
|
+
|
|
8
11
|
export type MonitorParams = {
|
|
9
|
-
|
|
12
|
+
wsUrl: string; // WebSocket URL (direct, no conversion needed)
|
|
10
13
|
clientToken: string;
|
|
11
|
-
|
|
12
|
-
pollIntervalMs: number; // Used as reconnect delay
|
|
14
|
+
signKey?: string; // HMAC key for signature generation
|
|
13
15
|
abortSignal?: AbortSignal;
|
|
14
16
|
log?: PluginLogger;
|
|
15
17
|
cfg?: ClawdbotConfig; // OpenClaw config for model settings
|
|
@@ -67,10 +69,10 @@ type AuthResultPayload = {
|
|
|
67
69
|
};
|
|
68
70
|
|
|
69
71
|
// Generate HMAC-SHA256 signature for authentication (includes timestamp for anti-replay)
|
|
70
|
-
// Uses
|
|
71
|
-
function generateSignature(
|
|
72
|
-
// Use HMAC-SHA256 with
|
|
73
|
-
return crypto.createHmac("sha256",
|
|
72
|
+
// Uses signKey as the HMAC key, and "token:nonce:timestamp" as the message
|
|
73
|
+
function generateSignature(signKey: string, token: string, nonce: string, timestamp: number): string {
|
|
74
|
+
// Use HMAC-SHA256 with signKey as the key, and "token:nonce:timestamp" as the message
|
|
75
|
+
return crypto.createHmac("sha256", signKey).update(`${token}:${nonce}:${timestamp}`).digest("hex");
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
// Generate random nonce
|
|
@@ -84,12 +86,9 @@ function generateRequestId(): string {
|
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
export async function monitorAdpOpenclaw(params: MonitorParams): Promise<void> {
|
|
87
|
-
const {
|
|
89
|
+
const { wsUrl, clientToken, signKey, abortSignal, log, cfg } = params;
|
|
88
90
|
const runtime = getAdpOpenclawRuntime();
|
|
89
91
|
|
|
90
|
-
// Convert HTTP URL to WebSocket URL
|
|
91
|
-
const wsUrl = serverUrl.replace(/^http/, "ws") + "/ws";
|
|
92
|
-
|
|
93
92
|
log?.info(`[adp-openclaw] WebSocket monitor started, connecting to ${wsUrl}`);
|
|
94
93
|
|
|
95
94
|
while (!abortSignal?.aborted) {
|
|
@@ -97,8 +96,7 @@ export async function monitorAdpOpenclaw(params: MonitorParams): Promise<void> {
|
|
|
97
96
|
await connectAndHandle({
|
|
98
97
|
wsUrl,
|
|
99
98
|
clientToken,
|
|
100
|
-
|
|
101
|
-
serverUrl,
|
|
99
|
+
signKey,
|
|
102
100
|
abortSignal,
|
|
103
101
|
log,
|
|
104
102
|
runtime,
|
|
@@ -111,8 +109,8 @@ export async function monitorAdpOpenclaw(params: MonitorParams): Promise<void> {
|
|
|
111
109
|
|
|
112
110
|
// Wait before reconnecting
|
|
113
111
|
if (!abortSignal?.aborted) {
|
|
114
|
-
log?.info(`[adp-openclaw] Reconnecting in ${
|
|
115
|
-
await sleep(
|
|
112
|
+
log?.info(`[adp-openclaw] Reconnecting in ${RECONNECT_DELAY_MS}ms...`);
|
|
113
|
+
await sleep(RECONNECT_DELAY_MS, abortSignal);
|
|
116
114
|
}
|
|
117
115
|
}
|
|
118
116
|
|
|
@@ -122,8 +120,7 @@ export async function monitorAdpOpenclaw(params: MonitorParams): Promise<void> {
|
|
|
122
120
|
type ConnectParams = {
|
|
123
121
|
wsUrl: string;
|
|
124
122
|
clientToken: string;
|
|
125
|
-
|
|
126
|
-
serverUrl: string;
|
|
123
|
+
signKey?: string;
|
|
127
124
|
abortSignal?: AbortSignal;
|
|
128
125
|
log?: PluginLogger;
|
|
129
126
|
runtime: ReturnType<typeof getAdpOpenclawRuntime>;
|
|
@@ -131,7 +128,7 @@ type ConnectParams = {
|
|
|
131
128
|
};
|
|
132
129
|
|
|
133
130
|
async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
134
|
-
const { wsUrl, clientToken,
|
|
131
|
+
const { wsUrl, clientToken, signKey, abortSignal, log, runtime, cfg } = params;
|
|
135
132
|
|
|
136
133
|
// Dynamic import for WebSocket (works in both Node.js and browser)
|
|
137
134
|
const WebSocket = (await import("ws")).default;
|
|
@@ -154,17 +151,17 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
154
151
|
// Send authentication message with signature (includes timestamp for anti-replay)
|
|
155
152
|
const nonce = generateNonce();
|
|
156
153
|
const timestamp = Date.now();
|
|
157
|
-
// Generate signature only if
|
|
158
|
-
const signature =
|
|
154
|
+
// Generate signature only if signKey is provided
|
|
155
|
+
const signature = signKey ? generateSignature(signKey, clientToken, nonce, timestamp) : "";
|
|
159
156
|
|
|
160
157
|
const authMsg: WSMessage = {
|
|
161
158
|
type: MsgType.Auth,
|
|
162
159
|
requestId: generateRequestId(),
|
|
163
160
|
payload: {
|
|
164
161
|
token: clientToken,
|
|
165
|
-
nonce:
|
|
166
|
-
signature:
|
|
167
|
-
timestamp:
|
|
162
|
+
nonce: signKey ? nonce : undefined,
|
|
163
|
+
signature: signKey ? signature : undefined,
|
|
164
|
+
timestamp: signKey ? timestamp : undefined, // Include timestamp in payload for server verification
|
|
168
165
|
},
|
|
169
166
|
timestamp: Date.now(),
|
|
170
167
|
};
|
|
@@ -313,6 +310,7 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
313
310
|
}
|
|
314
311
|
};
|
|
315
312
|
|
|
313
|
+
log?.info(`[adp-openclaw] Starting dispatchReplyWithBufferedBlockDispatcher for ${displayName}`);
|
|
316
314
|
await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
317
315
|
ctx,
|
|
318
316
|
cfg: cfg ?? {},
|
|
@@ -366,10 +364,10 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
366
364
|
// SDK calls this with info.kind = "block" for streaming chunks, "final" for complete response
|
|
367
365
|
deliver: async (payload: { text?: string }, info?: { kind?: string }) => {
|
|
368
366
|
const text = payload.text || "";
|
|
369
|
-
const kind = info?.kind
|
|
367
|
+
const kind = info?.kind;
|
|
370
368
|
|
|
371
|
-
// Debug log for all deliver calls
|
|
372
|
-
log?.
|
|
369
|
+
// Debug log for all deliver calls - log the actual info object
|
|
370
|
+
log?.info(`[adp-openclaw] deliver called: kind=${kind}, text.length=${text.length}, info=${JSON.stringify(info)}`);
|
|
373
371
|
|
|
374
372
|
// Handle streaming block - send chunk via WebSocket
|
|
375
373
|
if (kind === "block" && text) {
|
|
@@ -400,9 +398,11 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
400
398
|
return;
|
|
401
399
|
}
|
|
402
400
|
|
|
403
|
-
// Handle final reply - send outbound_end
|
|
404
|
-
|
|
405
|
-
|
|
401
|
+
// Handle final reply or undefined kind - send outbound_end
|
|
402
|
+
// SDK may call deliver without kind when streaming ends
|
|
403
|
+
if (kind === "final" || kind === undefined) {
|
|
404
|
+
log?.info(`[adp-openclaw] deliver triggering sendOutboundEnd (kind=${kind})`);
|
|
405
|
+
sendOutboundEnd(text || lastPartialText);
|
|
406
406
|
}
|
|
407
407
|
},
|
|
408
408
|
onError: (err: Error) => {
|
|
@@ -411,6 +411,8 @@ async function connectAndHandle(params: ConnectParams): Promise<void> {
|
|
|
411
411
|
},
|
|
412
412
|
});
|
|
413
413
|
|
|
414
|
+
log?.info(`[adp-openclaw] dispatchReplyWithBufferedBlockDispatcher returned (finalSent=${finalSent}, chunkIndex=${chunkIndex})`);
|
|
415
|
+
|
|
414
416
|
// IMPORTANT: After dispatchReplyWithBufferedBlockDispatcher completes,
|
|
415
417
|
// ensure outbound_end is sent even if "final" deliver was not called.
|
|
416
418
|
// This handles cases where the SDK only sends blocks without a final callback.
|
package/src/runtime.ts
CHANGED
|
@@ -7,9 +7,7 @@ let adpOpenclawRuntime: PluginRuntime | null = null;
|
|
|
7
7
|
export type PluginConfig = {
|
|
8
8
|
wsUrl?: string;
|
|
9
9
|
clientToken?: string;
|
|
10
|
-
|
|
11
|
-
serverUrl?: string;
|
|
12
|
-
pollIntervalMs?: number;
|
|
10
|
+
signKey?: string;
|
|
13
11
|
};
|
|
14
12
|
|
|
15
13
|
let pluginConfig: PluginConfig = {};
|