@openclaw/twitch 2026.5.7-beta.1 → 2026.5.9-beta.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/api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{markdown-MRdI1sR7.js → markdown-CLNO0I2v.js} +1 -1
- package/dist/{monitor-DS0YTAPB.js → monitor-DmMYPZvJ.js} +13 -23
- package/dist/{plugin-BQX9GiIn.js → plugin-CoIt3s7P.js} +117 -14
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-yArVgckI.js → setup-surface-CDhELE-4.js} +1 -1
- package/dist/{twitch-CklAMZL5.js → twitch-D0A1Bjui.js} +1 -3
- package/package.json +4 -4
package/dist/api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as twitchPlugin } from "./plugin-
|
|
1
|
+
import { t as twitchPlugin } from "./plugin-CoIt3s7P.js";
|
|
2
2
|
export { twitchPlugin };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as normalizeToken, o as resolveTwitchToken } from "./twitch-
|
|
1
|
+
import { i as normalizeToken, o as resolveTwitchToken } from "./twitch-D0A1Bjui.js";
|
|
2
2
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
3
3
|
import { RefreshingAuthProvider, StaticAuthProvider } from "@twurple/auth";
|
|
4
4
|
import { ChatClient, LogLevel } from "@twurple/chat";
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { i as getOrCreateClientManager, n as stripMarkdownForTwitch } from "./markdown-
|
|
1
|
+
import { i as getOrCreateClientManager, n as stripMarkdownForTwitch } from "./markdown-CLNO0I2v.js";
|
|
2
2
|
import { t as getTwitchRuntime } from "./runtime-QZ5I3GlI.js";
|
|
3
3
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
4
4
|
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
5
|
-
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
|
6
5
|
//#region extensions/twitch/src/access-control.ts
|
|
7
6
|
/**
|
|
8
7
|
* Check if a Twitch message should be allowed based on account configuration
|
|
@@ -117,12 +116,6 @@ function extractMentions(message) {
|
|
|
117
116
|
//#endregion
|
|
118
117
|
//#region extensions/twitch/src/monitor.ts
|
|
119
118
|
/**
|
|
120
|
-
* Twitch message monitor - processes incoming messages and routes to agents.
|
|
121
|
-
*
|
|
122
|
-
* This monitor connects to the Twitch client manager, processes incoming messages,
|
|
123
|
-
* resolves agent routes, and handles replies.
|
|
124
|
-
*/
|
|
125
|
-
/**
|
|
126
119
|
* Process an incoming Twitch message and dispatch to agent.
|
|
127
120
|
*/
|
|
128
121
|
async function processTwitchMessage(params) {
|
|
@@ -203,12 +196,6 @@ async function processTwitchMessage(params) {
|
|
|
203
196
|
channel: "twitch",
|
|
204
197
|
accountId
|
|
205
198
|
});
|
|
206
|
-
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
|
|
207
|
-
cfg,
|
|
208
|
-
agentId: route.agentId,
|
|
209
|
-
channel: "twitch",
|
|
210
|
-
accountId
|
|
211
|
-
});
|
|
212
199
|
return {
|
|
213
200
|
cfg,
|
|
214
201
|
channel: "twitch",
|
|
@@ -220,24 +207,26 @@ async function processTwitchMessage(params) {
|
|
|
220
207
|
recordInboundSession: core.channel.session.recordInboundSession,
|
|
221
208
|
dispatchReplyWithBufferedBlockDispatcher: core.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
|
|
222
209
|
delivery: {
|
|
210
|
+
durable: () => ({ to: `twitch:channel:${message.channel}` }),
|
|
223
211
|
deliver: async (payload) => {
|
|
224
|
-
await deliverTwitchReply({
|
|
212
|
+
return await deliverTwitchReply({
|
|
225
213
|
payload,
|
|
226
214
|
channel: message.channel,
|
|
227
215
|
account,
|
|
228
216
|
accountId,
|
|
229
217
|
config,
|
|
230
218
|
tableMode,
|
|
231
|
-
runtime
|
|
232
|
-
statusSink
|
|
219
|
+
runtime
|
|
233
220
|
});
|
|
234
221
|
},
|
|
222
|
+
onDelivered: (_payload, _info, result) => {
|
|
223
|
+
if (result?.visibleReplySent !== false) statusSink?.({ lastOutboundAt: Date.now() });
|
|
224
|
+
},
|
|
235
225
|
onError: (err, info) => {
|
|
236
226
|
runtime.error?.(`Twitch ${info.kind} reply failed: ${String(err)}`);
|
|
237
227
|
}
|
|
238
228
|
},
|
|
239
|
-
|
|
240
|
-
replyOptions: { onModelSelected },
|
|
229
|
+
replyPipeline: {},
|
|
241
230
|
record: { onRecordError: (err) => {
|
|
242
231
|
runtime.error?.(`Failed updating session meta: ${String(err)}`);
|
|
243
232
|
} }
|
|
@@ -250,7 +239,7 @@ async function processTwitchMessage(params) {
|
|
|
250
239
|
* Deliver a reply to Twitch chat.
|
|
251
240
|
*/
|
|
252
241
|
async function deliverTwitchReply(params) {
|
|
253
|
-
const { payload, channel, account, accountId, config, runtime
|
|
242
|
+
const { payload, channel, account, accountId, config, runtime } = params;
|
|
254
243
|
try {
|
|
255
244
|
const client = await getOrCreateClientManager(accountId, {
|
|
256
245
|
info: (msg) => runtime.log?.(msg),
|
|
@@ -260,17 +249,18 @@ async function deliverTwitchReply(params) {
|
|
|
260
249
|
}).getClient(account, config, accountId);
|
|
261
250
|
if (!client) {
|
|
262
251
|
runtime.error?.(`No client available for sending reply`);
|
|
263
|
-
return;
|
|
252
|
+
return { visibleReplySent: false };
|
|
264
253
|
}
|
|
265
254
|
if (!payload.text) {
|
|
266
255
|
runtime.error?.(`No text to send in reply payload`);
|
|
267
|
-
return;
|
|
256
|
+
return { visibleReplySent: false };
|
|
268
257
|
}
|
|
269
258
|
const textToSend = stripMarkdownForTwitch(payload.text);
|
|
270
259
|
await client.say(channel, textToSend);
|
|
271
|
-
|
|
260
|
+
return { visibleReplySent: true };
|
|
272
261
|
} catch (err) {
|
|
273
262
|
runtime.error?.(`Failed to send reply: ${String(err)}`);
|
|
263
|
+
return { visibleReplySent: false };
|
|
274
264
|
}
|
|
275
265
|
}
|
|
276
266
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as normalizeTwitchChannel, i as normalizeToken, n as isAccountConfigured, o as resolveTwitchToken, r as missingTargetError, t as generateMessageId } from "./twitch-
|
|
2
|
-
import { a as getAccountConfig, c as resolveTwitchAccountContext, i as DEFAULT_ACCOUNT_ID, l as resolveTwitchSnapshotAccountId, o as listAccountIds, r as twitchSetupWizard, s as resolveDefaultTwitchAccountId, t as twitchSetupAdapter } from "./setup-surface-
|
|
3
|
-
import { a as removeClientManager, n as stripMarkdownForTwitch, r as getClientManager, t as chunkTextForTwitch } from "./markdown-
|
|
1
|
+
import { a as normalizeTwitchChannel, i as normalizeToken, n as isAccountConfigured, o as resolveTwitchToken, r as missingTargetError, t as generateMessageId } from "./twitch-D0A1Bjui.js";
|
|
2
|
+
import { a as getAccountConfig, c as resolveTwitchAccountContext, i as DEFAULT_ACCOUNT_ID, l as resolveTwitchSnapshotAccountId, o as listAccountIds, r as twitchSetupWizard, s as resolveDefaultTwitchAccountId, t as twitchSetupAdapter } from "./setup-surface-CDhELE-4.js";
|
|
3
|
+
import { a as removeClientManager, n as stripMarkdownForTwitch, r as getClientManager, t as chunkTextForTwitch } from "./markdown-CLNO0I2v.js";
|
|
4
4
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
5
5
|
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
6
6
|
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
@@ -8,14 +8,33 @@ import { createLoggedPairingApprovalNotifier, createPairingPrefixStripper } from
|
|
|
8
8
|
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
|
9
9
|
import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
10
10
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
11
|
+
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
12
|
+
import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-message";
|
|
11
13
|
import { StaticAuthProvider } from "@twurple/auth";
|
|
12
14
|
import { ChatClient } from "@twurple/chat";
|
|
13
15
|
import { MarkdownConfigSchema } from "openclaw/plugin-sdk/channel-config-primitives";
|
|
14
16
|
import { z } from "openclaw/plugin-sdk/zod";
|
|
15
17
|
import { ApiClient } from "@twurple/api";
|
|
16
|
-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
17
18
|
//#region extensions/twitch/src/send.ts
|
|
18
19
|
/**
|
|
20
|
+
* Twitch message sending functions with dependency injection support.
|
|
21
|
+
*
|
|
22
|
+
* These functions are the primary interface for sending messages to Twitch.
|
|
23
|
+
* They support dependency injection via the `deps` parameter for testability.
|
|
24
|
+
*/
|
|
25
|
+
function createTwitchSendReceipt(params) {
|
|
26
|
+
const messageId = params.messageId.trim();
|
|
27
|
+
const conversationId = params.channel?.trim();
|
|
28
|
+
return createMessageReceiptFromOutboundResults({
|
|
29
|
+
results: params.visible === true && messageId && messageId !== "skipped" ? [{
|
|
30
|
+
channel: "twitch",
|
|
31
|
+
messageId,
|
|
32
|
+
...conversationId ? { conversationId } : {}
|
|
33
|
+
}] : [],
|
|
34
|
+
kind: "text"
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
19
38
|
* Internal send function used by the outbound adapter.
|
|
20
39
|
*
|
|
21
40
|
* This function has access to the full OpenClaw config and handles
|
|
@@ -44,47 +63,93 @@ async function sendMessageTwitchInternal(channel, text, cfg, accountId, stripMar
|
|
|
44
63
|
if (!account) return {
|
|
45
64
|
ok: false,
|
|
46
65
|
messageId: generateMessageId(),
|
|
66
|
+
receipt: createTwitchSendReceipt({
|
|
67
|
+
messageId: "",
|
|
68
|
+
channel,
|
|
69
|
+
visible: false
|
|
70
|
+
}),
|
|
47
71
|
error: `Account not found: ${accountId ?? "(default)"}. Available accounts: ${availableAccountIds.join(", ") || "none"}`
|
|
48
72
|
};
|
|
49
73
|
if (!configured) return {
|
|
50
74
|
ok: false,
|
|
51
75
|
messageId: generateMessageId(),
|
|
76
|
+
receipt: createTwitchSendReceipt({
|
|
77
|
+
messageId: "",
|
|
78
|
+
channel,
|
|
79
|
+
visible: false
|
|
80
|
+
}),
|
|
52
81
|
error: `Account ${resolvedAccountId} is not properly configured. Required: username, clientId, and token (config or env for default account).`
|
|
53
82
|
};
|
|
54
83
|
const normalizedChannel = channel || account.channel;
|
|
55
84
|
if (!normalizedChannel) return {
|
|
56
85
|
ok: false,
|
|
57
86
|
messageId: generateMessageId(),
|
|
87
|
+
receipt: createTwitchSendReceipt({
|
|
88
|
+
messageId: "",
|
|
89
|
+
channel: normalizedChannel,
|
|
90
|
+
visible: false
|
|
91
|
+
}),
|
|
58
92
|
error: "No channel specified and no default channel in account config"
|
|
59
93
|
};
|
|
94
|
+
const deliveryChannel = normalizeTwitchChannel(normalizedChannel);
|
|
60
95
|
const cleanedText = stripMarkdown ? stripMarkdownForTwitch(text) : text;
|
|
61
96
|
if (!cleanedText) return {
|
|
62
97
|
ok: true,
|
|
63
|
-
messageId: "skipped"
|
|
98
|
+
messageId: "skipped",
|
|
99
|
+
receipt: createTwitchSendReceipt({
|
|
100
|
+
messageId: "skipped",
|
|
101
|
+
channel: deliveryChannel,
|
|
102
|
+
visible: false
|
|
103
|
+
})
|
|
64
104
|
};
|
|
65
105
|
const clientManager = getClientManager(resolvedAccountId);
|
|
66
106
|
if (!clientManager) return {
|
|
67
107
|
ok: false,
|
|
68
108
|
messageId: generateMessageId(),
|
|
109
|
+
receipt: createTwitchSendReceipt({
|
|
110
|
+
messageId: "",
|
|
111
|
+
channel: deliveryChannel,
|
|
112
|
+
visible: false
|
|
113
|
+
}),
|
|
69
114
|
error: `Client manager not found for account: ${resolvedAccountId}. Please start the Twitch gateway first.`
|
|
70
115
|
};
|
|
71
116
|
try {
|
|
72
|
-
const result = await clientManager.sendMessage(account,
|
|
73
|
-
if (!result.ok)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
117
|
+
const result = await clientManager.sendMessage(account, deliveryChannel, cleanedText, cfg, resolvedAccountId);
|
|
118
|
+
if (!result.ok) {
|
|
119
|
+
const messageId = result.messageId ?? generateMessageId();
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
messageId,
|
|
123
|
+
receipt: createTwitchSendReceipt({
|
|
124
|
+
messageId,
|
|
125
|
+
channel: deliveryChannel,
|
|
126
|
+
visible: false
|
|
127
|
+
}),
|
|
128
|
+
error: result.error ?? "Send failed"
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const messageId = result.messageId ?? generateMessageId();
|
|
78
132
|
return {
|
|
79
133
|
ok: true,
|
|
80
|
-
messageId
|
|
134
|
+
messageId,
|
|
135
|
+
receipt: createTwitchSendReceipt({
|
|
136
|
+
messageId,
|
|
137
|
+
channel: deliveryChannel,
|
|
138
|
+
visible: true
|
|
139
|
+
})
|
|
81
140
|
};
|
|
82
141
|
} catch (error) {
|
|
83
142
|
const errorMsg = formatErrorMessage(error);
|
|
143
|
+
const messageId = generateMessageId();
|
|
84
144
|
logger.error(`Failed to send message: ${errorMsg}`);
|
|
85
145
|
return {
|
|
86
146
|
ok: false,
|
|
87
|
-
messageId
|
|
147
|
+
messageId,
|
|
148
|
+
receipt: createTwitchSendReceipt({
|
|
149
|
+
messageId,
|
|
150
|
+
channel: deliveryChannel,
|
|
151
|
+
visible: false
|
|
152
|
+
}),
|
|
88
153
|
error: errorMsg
|
|
89
154
|
};
|
|
90
155
|
}
|
|
@@ -106,6 +171,11 @@ async function sendMessageTwitchInternal(channel, text, cfg, accountId, stripMar
|
|
|
106
171
|
const twitchOutbound = {
|
|
107
172
|
/** Direct delivery mode - messages are sent immediately */
|
|
108
173
|
deliveryMode: "direct",
|
|
174
|
+
deliveryCapabilities: { durableFinal: {
|
|
175
|
+
text: true,
|
|
176
|
+
media: true,
|
|
177
|
+
messageSendingHooks: true
|
|
178
|
+
} },
|
|
109
179
|
/** Twitch chat message limit is 500 characters */
|
|
110
180
|
textChunkLimit: 500,
|
|
111
181
|
/** Word-boundary chunker with markdown stripping */
|
|
@@ -184,6 +254,7 @@ const twitchOutbound = {
|
|
|
184
254
|
return {
|
|
185
255
|
channel: "twitch",
|
|
186
256
|
messageId: result.messageId,
|
|
257
|
+
receipt: result.receipt,
|
|
187
258
|
timestamp: Date.now()
|
|
188
259
|
};
|
|
189
260
|
},
|
|
@@ -216,6 +287,37 @@ const twitchOutbound = {
|
|
|
216
287
|
});
|
|
217
288
|
}
|
|
218
289
|
};
|
|
290
|
+
function toTwitchMessageSendResult(result, kind) {
|
|
291
|
+
const receipt = result.receipt ?? createMessageReceiptFromOutboundResults({
|
|
292
|
+
results: result.messageId ? [{
|
|
293
|
+
channel: "twitch",
|
|
294
|
+
messageId: result.messageId
|
|
295
|
+
}] : [],
|
|
296
|
+
kind
|
|
297
|
+
});
|
|
298
|
+
return {
|
|
299
|
+
messageId: result.messageId || receipt.primaryPlatformMessageId,
|
|
300
|
+
receipt
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const twitchMessageAdapter = defineChannelMessageAdapter({
|
|
304
|
+
id: "twitch",
|
|
305
|
+
durableFinal: { capabilities: {
|
|
306
|
+
text: true,
|
|
307
|
+
media: true,
|
|
308
|
+
messageSendingHooks: true
|
|
309
|
+
} },
|
|
310
|
+
send: {
|
|
311
|
+
text: async (ctx) => {
|
|
312
|
+
if (!twitchOutbound.sendText) throw new Error("Twitch text sending is not available.");
|
|
313
|
+
return toTwitchMessageSendResult(await twitchOutbound.sendText(ctx), "text");
|
|
314
|
+
},
|
|
315
|
+
media: async (ctx) => {
|
|
316
|
+
if (!twitchOutbound.sendMedia) throw new Error("Twitch media sending is not available.");
|
|
317
|
+
return toTwitchMessageSendResult(await twitchOutbound.sendMedia(ctx), "media");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
});
|
|
219
321
|
//#endregion
|
|
220
322
|
//#region extensions/twitch/src/actions.ts
|
|
221
323
|
/**
|
|
@@ -779,6 +881,7 @@ const twitchPlugin = createChatChannelPlugin({
|
|
|
779
881
|
setup: twitchSetupAdapter,
|
|
780
882
|
setupWizard: twitchSetupWizard,
|
|
781
883
|
capabilities: { chatTypes: ["group"] },
|
|
884
|
+
message: twitchMessageAdapter,
|
|
782
885
|
configSchema: buildChannelConfigSchema(TwitchConfigSchema),
|
|
783
886
|
config: {
|
|
784
887
|
listAccountIds: (cfg) => listAccountIds(cfg),
|
|
@@ -851,7 +954,7 @@ const twitchPlugin = createChatChannelPlugin({
|
|
|
851
954
|
lastError: null
|
|
852
955
|
});
|
|
853
956
|
ctx.log?.info(`Starting Twitch connection for ${account.username}`);
|
|
854
|
-
const { monitorTwitchProvider } = await import("./monitor-
|
|
957
|
+
const { monitorTwitchProvider } = await import("./monitor-DmMYPZvJ.js");
|
|
855
958
|
await monitorTwitchProvider({
|
|
856
959
|
account,
|
|
857
960
|
accountId,
|
package/dist/setup-plugin-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as twitchSetupPlugin } from "./setup-surface-
|
|
1
|
+
import { n as twitchSetupPlugin } from "./setup-surface-CDhELE-4.js";
|
|
2
2
|
export { twitchSetupPlugin };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as isAccountConfigured, o as resolveTwitchToken } from "./twitch-
|
|
1
|
+
import { n as isAccountConfigured, o as resolveTwitchToken } from "./twitch-D0A1Bjui.js";
|
|
2
2
|
import { listCombinedAccountIds, normalizeAccountId, resolveNormalizedAccountEntry } from "openclaw/plugin-sdk/account-resolution";
|
|
3
3
|
import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-id";
|
|
4
4
|
import { getChatChannelMeta } from "openclaw/plugin-sdk/core";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId, resolveNormalizedAccountEntry } from "openclaw/plugin-sdk/account-resolution";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
3
4
|
//#region extensions/twitch/src/token.ts
|
|
4
5
|
/**
|
|
5
6
|
* Twitch access token resolution with environment variable support.
|
|
@@ -61,9 +62,6 @@ function resolveTwitchToken(cfg, opts = {}) {
|
|
|
61
62
|
/**
|
|
62
63
|
* Twitch-specific utility functions
|
|
63
64
|
*/
|
|
64
|
-
function normalizeLowercaseStringOrEmpty(value) {
|
|
65
|
-
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
66
|
-
}
|
|
67
65
|
/**
|
|
68
66
|
* Normalize Twitch channel names.
|
|
69
67
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/twitch",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.9-beta.1",
|
|
4
4
|
"description": "OpenClaw Twitch channel plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"minHostVersion": ">=2026.4.10"
|
|
27
27
|
},
|
|
28
28
|
"compat": {
|
|
29
|
-
"pluginApi": ">=2026.5.
|
|
29
|
+
"pluginApi": ">=2026.5.9-beta.1"
|
|
30
30
|
},
|
|
31
31
|
"build": {
|
|
32
|
-
"openclawVersion": "2026.5.
|
|
32
|
+
"openclawVersion": "2026.5.9-beta.1"
|
|
33
33
|
},
|
|
34
34
|
"channel": {
|
|
35
35
|
"id": "twitch",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"README.md"
|
|
57
57
|
],
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"openclaw": ">=2026.5.
|
|
59
|
+
"openclaw": ">=2026.5.9-beta.1"
|
|
60
60
|
},
|
|
61
61
|
"peerDependenciesMeta": {
|
|
62
62
|
"openclaw": {
|