@openclaw/irc 2026.7.2-beta.3 → 2026.7.2-beta.4
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-BC0q2_Tg.js → channel-BuxKUEja.js} +83 -8
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-runtime-DBNGrI_Y.js → channel-runtime-BrNJqIB_.js} +264 -68
- package/npm-shrinkwrap.json +2 -2
- package/openclaw.plugin.json +0 -14
- package/package.json +91 -11
package/dist/api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as resolveIrcAccount, g as resolveDefaultIrcAccountId, h as listIrcAccountIds, m as listEnabledIrcAccounts, n as ircSetupWizard, r as ircSetupAdapter, t as ircPlugin } from "./channel-BuxKUEja.js";
|
|
2
2
|
import { n as setIrcRuntime } from "./runtime-xy-FcjJC.js";
|
|
3
3
|
export { ircPlugin, ircSetupAdapter, ircSetupWizard, listEnabledIrcAccounts, listIrcAccountIds, resolveDefaultIrcAccountId, resolveIrcAccount, setIrcRuntime };
|
|
@@ -15,16 +15,16 @@ import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
|
15
15
|
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
|
|
16
16
|
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
17
17
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, normalizeStringifiedOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
18
|
-
import { createAccountStatusSink, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter, sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
|
|
18
|
+
import { createAccountStatusSink, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter, runPassiveAccountLifecycle, sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
|
|
19
19
|
import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
|
|
20
20
|
import { chunkTextForOutbound, convertMarkdownTables, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
21
|
-
import { runStoppablePassiveMonitor } from "openclaw/plugin-sdk/extension-shared";
|
|
22
21
|
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
23
22
|
import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
24
23
|
import net from "node:net";
|
|
25
24
|
import tls from "node:tls";
|
|
26
25
|
import { withTimeout } from "openclaw/plugin-sdk/security-runtime";
|
|
27
26
|
import { randomUUID } from "node:crypto";
|
|
27
|
+
import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
|
|
28
28
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/routing";
|
|
29
29
|
import { applyAccountNameToChannelSection, createAllowFromSection, createPromptParsedAllowFromForAccount, createSetupInputPresenceValidator, createSetupTranslator, createStandardChannelSetupStatus, createTopLevelChannelAllowFromSetter, createTopLevelChannelDmPolicySetter, formatDocsLink, patchScopedAccountConfig, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
|
|
30
30
|
//#region extensions/irc/src/accounts.ts
|
|
@@ -202,7 +202,7 @@ const collectIrcMutableAllowlistWarnings = createDangerousNameMatchingMutableAll
|
|
|
202
202
|
});
|
|
203
203
|
//#endregion
|
|
204
204
|
//#region extensions/irc/src/gateway.ts
|
|
205
|
-
const loadIrcChannelRuntime$1 = createLazyRuntimeModule(() => import("./channel-runtime-
|
|
205
|
+
const loadIrcChannelRuntime$1 = createLazyRuntimeModule(() => import("./channel-runtime-BrNJqIB_.js"));
|
|
206
206
|
async function startIrcGatewayAccount(ctx) {
|
|
207
207
|
const account = ctx.account;
|
|
208
208
|
const statusSink = createAccountStatusSink({
|
|
@@ -212,7 +212,7 @@ async function startIrcGatewayAccount(ctx) {
|
|
|
212
212
|
if (!account.configured) throw new Error(`IRC is not configured for account "${account.accountId}" (need host and nick in channels.irc).`);
|
|
213
213
|
ctx.log?.info?.(`[${account.accountId}] starting IRC provider (${account.host}:${account.port}${account.tls ? " tls" : ""})`);
|
|
214
214
|
const { monitorIrcProvider } = await loadIrcChannelRuntime$1();
|
|
215
|
-
await
|
|
215
|
+
await runPassiveAccountLifecycle({
|
|
216
216
|
abortSignal: ctx.abortSignal,
|
|
217
217
|
start: async () => await monitorIrcProvider({
|
|
218
218
|
accountId: account.accountId,
|
|
@@ -220,7 +220,10 @@ async function startIrcGatewayAccount(ctx) {
|
|
|
220
220
|
runtime: ctx.runtime,
|
|
221
221
|
abortSignal: ctx.abortSignal,
|
|
222
222
|
statusSink
|
|
223
|
-
})
|
|
223
|
+
}),
|
|
224
|
+
stop: async (monitor) => {
|
|
225
|
+
await monitor.stop();
|
|
226
|
+
}
|
|
224
227
|
});
|
|
225
228
|
}
|
|
226
229
|
//#endregion
|
|
@@ -554,7 +557,7 @@ async function connectIrcClient(options) {
|
|
|
554
557
|
if (line.command === "PRIVMSG") {
|
|
555
558
|
const targetParam = line.params[0];
|
|
556
559
|
const target = targetParam ? targetParam.trim() : "";
|
|
557
|
-
const text = line.trailing
|
|
560
|
+
const text = line.trailing ?? line.params[1] ?? "";
|
|
558
561
|
const prefix = parseIrcPrefix(line.prefix);
|
|
559
562
|
const senderNick = prefix.nick ? prefix.nick.trim() : "";
|
|
560
563
|
if (!target || !senderNick || !text.trim()) continue;
|
|
@@ -562,6 +565,7 @@ async function connectIrcClient(options) {
|
|
|
562
565
|
senderNick,
|
|
563
566
|
senderUser: prefix.user ? prefix.user.trim() : void 0,
|
|
564
567
|
senderHost: prefix.host ? prefix.host.trim() : void 0,
|
|
568
|
+
connectedNick: currentNick,
|
|
565
569
|
target,
|
|
566
570
|
text,
|
|
567
571
|
rawLine
|
|
@@ -939,6 +943,7 @@ function setIrcGroupAccess(cfg, accountId, policy, entries, normalizeGroupEntry)
|
|
|
939
943
|
});
|
|
940
944
|
}
|
|
941
945
|
const ircSetupAdapter = {
|
|
946
|
+
singleAccountKeysToMove: ["password"],
|
|
942
947
|
resolveAccountId: ({ accountId }) => normalizeAccountId$1(accountId),
|
|
943
948
|
applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
|
|
944
949
|
cfg,
|
|
@@ -975,6 +980,75 @@ const ircSetupAdapter = {
|
|
|
975
980
|
});
|
|
976
981
|
}
|
|
977
982
|
};
|
|
983
|
+
const ircSetupContract = defineChannelSetupContract({
|
|
984
|
+
fields: {
|
|
985
|
+
host: {
|
|
986
|
+
kind: "string",
|
|
987
|
+
cli: {
|
|
988
|
+
flags: "--host <host>",
|
|
989
|
+
description: "IRC server host"
|
|
990
|
+
}
|
|
991
|
+
},
|
|
992
|
+
port: {
|
|
993
|
+
kind: "string",
|
|
994
|
+
cli: {
|
|
995
|
+
flags: "--port <port>",
|
|
996
|
+
description: "IRC server port"
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
tls: {
|
|
1000
|
+
kind: "boolean",
|
|
1001
|
+
cli: {
|
|
1002
|
+
flags: "--tls",
|
|
1003
|
+
description: "Use TLS for IRC"
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
nick: {
|
|
1007
|
+
kind: "string",
|
|
1008
|
+
cli: {
|
|
1009
|
+
flags: "--nick <nick>",
|
|
1010
|
+
description: "IRC nickname"
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
username: {
|
|
1014
|
+
kind: "string",
|
|
1015
|
+
cli: {
|
|
1016
|
+
flags: "--username <name>",
|
|
1017
|
+
description: "IRC username"
|
|
1018
|
+
}
|
|
1019
|
+
},
|
|
1020
|
+
realname: {
|
|
1021
|
+
kind: "string",
|
|
1022
|
+
cli: {
|
|
1023
|
+
flags: "--realname <name>",
|
|
1024
|
+
description: "IRC real name"
|
|
1025
|
+
}
|
|
1026
|
+
},
|
|
1027
|
+
channels: {
|
|
1028
|
+
kind: "string-list",
|
|
1029
|
+
cli: {
|
|
1030
|
+
flags: "--channels <names>",
|
|
1031
|
+
description: "IRC channels"
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
password: {
|
|
1035
|
+
kind: "string",
|
|
1036
|
+
sensitive: true,
|
|
1037
|
+
cli: {
|
|
1038
|
+
flags: "--password <password>",
|
|
1039
|
+
description: "IRC server password"
|
|
1040
|
+
}
|
|
1041
|
+
},
|
|
1042
|
+
useEnv: {
|
|
1043
|
+
kind: "boolean",
|
|
1044
|
+
cli: {
|
|
1045
|
+
flags: "--use-env",
|
|
1046
|
+
description: "Use IRC environment configuration"
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
},
|
|
1050
|
+
legacyAdapter: ircSetupAdapter
|
|
1051
|
+
});
|
|
978
1052
|
//#endregion
|
|
979
1053
|
//#region extensions/irc/src/setup-surface.ts
|
|
980
1054
|
const t = createSetupTranslator();
|
|
@@ -1340,7 +1414,7 @@ const meta = {
|
|
|
1340
1414
|
systemImage: "number",
|
|
1341
1415
|
markdownCapable: true
|
|
1342
1416
|
};
|
|
1343
|
-
const loadIrcChannelRuntime = createLazyRuntimeModule(() => import("./channel-runtime-
|
|
1417
|
+
const loadIrcChannelRuntime = createLazyRuntimeModule(() => import("./channel-runtime-BrNJqIB_.js"));
|
|
1344
1418
|
function normalizePairingTarget(raw) {
|
|
1345
1419
|
const normalized = normalizeIrcAllowEntry(raw);
|
|
1346
1420
|
if (!normalized) return "";
|
|
@@ -1413,6 +1487,7 @@ const ircPlugin = createChatChannelPlugin({
|
|
|
1413
1487
|
quickstartAllowFrom: true
|
|
1414
1488
|
},
|
|
1415
1489
|
setup: ircSetupAdapter,
|
|
1490
|
+
setupContract: ircSetupContract,
|
|
1416
1491
|
setupWizard: ircSetupWizard,
|
|
1417
1492
|
capabilities: {
|
|
1418
1493
|
chatTypes: ["direct", "group"],
|
|
@@ -1590,4 +1665,4 @@ const ircPlugin = createChatChannelPlugin({
|
|
|
1590
1665
|
}
|
|
1591
1666
|
});
|
|
1592
1667
|
//#endregion
|
|
1593
|
-
export { resolveIrcGroupRequireMention as a, isChannelTarget as c, connectIrcClient as d,
|
|
1668
|
+
export { resolveIrcAccount as _, resolveIrcGroupRequireMention as a, isChannelTarget as c, connectIrcClient as d, parseIrcLine as f, resolveDefaultIrcAccountId as g, listIrcAccountIds as h, resolveIrcGroupMatch as i, normalizeIrcAllowEntry as l, listEnabledIrcAccounts as m, ircSetupWizard as n, sendMessageIrc as o, parseIrcPrefix as p, ircSetupAdapter as r, buildIrcAllowlistCandidates as s, ircPlugin as t, buildIrcConnectOptions as u };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as ircPlugin } from "./channel-
|
|
1
|
+
import { t as ircPlugin } from "./channel-BuxKUEja.js";
|
|
2
2
|
export { ircPlugin };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { a as resolveIrcGroupRequireMention, c as isChannelTarget, d as connectIrcClient, f as
|
|
1
|
+
import { _ as resolveIrcAccount, a as resolveIrcGroupRequireMention, c as isChannelTarget, d as connectIrcClient, f as parseIrcLine, i as resolveIrcGroupMatch, l as normalizeIrcAllowEntry, o as sendMessageIrc, p as parseIrcPrefix, s as buildIrcAllowlistCandidates, u as buildIrcConnectOptions } from "./channel-BuxKUEja.js";
|
|
2
2
|
import { t as getIrcRuntime } from "./runtime-xy-FcjJC.js";
|
|
3
3
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
-
import { resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
4
|
+
import { bindIngressLifecycleToReplyOptions, createChannelIngressMonitor, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
5
6
|
import { resolveLoggerBackedRuntime } from "openclaw/plugin-sdk/extension-shared";
|
|
6
7
|
import { buildChannelInboundEventContext, logInboundDrop, resolveChannelInboundRouteEnvelope } from "openclaw/plugin-sdk/channel-inbound";
|
|
7
8
|
import { channelIngressRoutes, createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
@@ -120,7 +121,7 @@ async function deliverIrcReply(params) {
|
|
|
120
121
|
});
|
|
121
122
|
}
|
|
122
123
|
async function handleIrcInbound(params) {
|
|
123
|
-
const { message, account, config, runtime, connectedNick, statusSink } = params;
|
|
124
|
+
const { message, account, config, runtime, connectedNick, statusSink, turnAdoptionLifecycle } = params;
|
|
124
125
|
const core = getIrcRuntime();
|
|
125
126
|
const pairing = createChannelPairingController({
|
|
126
127
|
core,
|
|
@@ -128,7 +129,11 @@ async function handleIrcInbound(params) {
|
|
|
128
129
|
accountId: account.accountId
|
|
129
130
|
});
|
|
130
131
|
const rawBody = message.text?.trim() ?? "";
|
|
131
|
-
if (!rawBody) return;
|
|
132
|
+
if (!rawBody) return { kind: "completed" };
|
|
133
|
+
if (turnAdoptionLifecycle?.abortSignal.aborted) return {
|
|
134
|
+
kind: "failed-retryable",
|
|
135
|
+
error: turnAdoptionLifecycle.abortSignal.reason
|
|
136
|
+
};
|
|
132
137
|
statusSink?.({ lastInboundAt: message.timestamp });
|
|
133
138
|
const senderDisplay = message.senderHost ? `${message.senderNick}!${message.senderUser ?? "?"}@${message.senderHost}` : message.senderNick;
|
|
134
139
|
const allowNameMatching = isDangerousNameMatchingEnabled(account.config);
|
|
@@ -228,11 +233,11 @@ async function handleIrcInbound(params) {
|
|
|
228
233
|
}
|
|
229
234
|
});
|
|
230
235
|
runtime.log?.(`irc: drop DM sender ${senderDisplay} (dmPolicy=${dmPolicy})`);
|
|
231
|
-
return;
|
|
236
|
+
return { kind: "completed" };
|
|
232
237
|
}
|
|
233
238
|
if (access.ingress.admission === "skip") {
|
|
234
239
|
runtime.log?.(`irc: drop channel ${message.target} (missing-mention)`);
|
|
235
|
-
return;
|
|
240
|
+
return { kind: "completed" };
|
|
236
241
|
}
|
|
237
242
|
if (access.ingress.admission !== "dispatch") {
|
|
238
243
|
if (message.isGroup && access.ingress.decisiveGateId === "command" && access.commandAccess.shouldBlockControlCommand) {
|
|
@@ -242,14 +247,18 @@ async function handleIrcInbound(params) {
|
|
|
242
247
|
reason: "control command (unauthorized)",
|
|
243
248
|
target: senderDisplay
|
|
244
249
|
});
|
|
245
|
-
return;
|
|
250
|
+
return { kind: "completed" };
|
|
246
251
|
}
|
|
247
252
|
if (message.isGroup) if (access.routeAccess.reason === "channel_not_allowlisted") runtime.log?.(`irc: drop channel ${message.target} (not allowlisted)`);
|
|
248
253
|
else if (access.routeAccess.reason === "channel_disabled") runtime.log?.(`irc: drop channel ${message.target} (disabled)`);
|
|
249
254
|
else runtime.log?.(`irc: drop group sender ${senderDisplay} (policy=${groupPolicy})`);
|
|
250
255
|
else runtime.log?.(`irc: drop DM sender ${senderDisplay} (dmPolicy=${dmPolicy})`);
|
|
251
|
-
return;
|
|
256
|
+
return { kind: "completed" };
|
|
252
257
|
}
|
|
258
|
+
if (turnAdoptionLifecycle?.abortSignal.aborted) return {
|
|
259
|
+
kind: "failed-retryable",
|
|
260
|
+
error: turnAdoptionLifecycle.abortSignal.reason
|
|
261
|
+
};
|
|
253
262
|
const channelTarget = message.target.startsWith("#") || message.target.startsWith("&") ? message.target : `#${message.target}`;
|
|
254
263
|
const peerId = message.isGroup ? channelTarget : message.senderNick;
|
|
255
264
|
const { route, buildEnvelope } = resolveChannelInboundRouteEnvelope({
|
|
@@ -313,6 +322,22 @@ async function handleIrcInbound(params) {
|
|
|
313
322
|
GroupSystemPrompt: message.isGroup ? groupSystemPrompt : void 0
|
|
314
323
|
}
|
|
315
324
|
});
|
|
325
|
+
const ingressState = { handoff: "none" };
|
|
326
|
+
const trackedIngressLifecycle = turnAdoptionLifecycle ? {
|
|
327
|
+
...turnAdoptionLifecycle,
|
|
328
|
+
onAdopted: async () => {
|
|
329
|
+
ingressState.handoff = "adopted";
|
|
330
|
+
await turnAdoptionLifecycle.onAdopted();
|
|
331
|
+
},
|
|
332
|
+
onDeferred: () => {
|
|
333
|
+
ingressState.handoff = "deferred";
|
|
334
|
+
turnAdoptionLifecycle.onDeferred();
|
|
335
|
+
},
|
|
336
|
+
onAbandoned: async () => {
|
|
337
|
+
ingressState.handoff = "abandoned";
|
|
338
|
+
await turnAdoptionLifecycle.onAbandoned();
|
|
339
|
+
}
|
|
340
|
+
} : void 0;
|
|
316
341
|
await core.channel.inbound.dispatch({
|
|
317
342
|
cfg: config,
|
|
318
343
|
channel: CHANNEL_ID,
|
|
@@ -339,6 +364,7 @@ async function handleIrcInbound(params) {
|
|
|
339
364
|
},
|
|
340
365
|
replyPipeline: {},
|
|
341
366
|
replyOptions: {
|
|
367
|
+
...trackedIngressLifecycle ? bindIngressLifecycleToReplyOptions(trackedIngressLifecycle) : {},
|
|
342
368
|
skillFilter: groupMatch.groupConfig?.skills,
|
|
343
369
|
disableBlockStreaming: typeof blockStreamingEnabled === "boolean" ? !blockStreamingEnabled : void 0
|
|
344
370
|
},
|
|
@@ -346,23 +372,162 @@ async function handleIrcInbound(params) {
|
|
|
346
372
|
runtime.error?.(`irc: failed updating session meta: ${String(err)}`);
|
|
347
373
|
} }
|
|
348
374
|
});
|
|
375
|
+
if (turnAdoptionLifecycle?.abortSignal.aborted && ingressState.handoff === "none") return {
|
|
376
|
+
kind: "failed-retryable",
|
|
377
|
+
error: turnAdoptionLifecycle.abortSignal.reason
|
|
378
|
+
};
|
|
379
|
+
if (turnAdoptionLifecycle && ingressState.handoff === "none") {
|
|
380
|
+
await turnAdoptionLifecycle.onAdopted();
|
|
381
|
+
ingressState.handoff = "adopted";
|
|
382
|
+
}
|
|
383
|
+
return ingressState.handoff === "deferred" || ingressState.handoff === "abandoned" ? { kind: "deferred" } : { kind: "completed" };
|
|
349
384
|
}
|
|
350
385
|
//#endregion
|
|
351
|
-
//#region extensions/irc/src/
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
386
|
+
//#region extensions/irc/src/irc-ingress.ts
|
|
387
|
+
const IRC_INGRESS_PAYLOAD_VERSION = 1;
|
|
388
|
+
const IRC_INGRESS_POLL_INTERVAL_MS = 1e3;
|
|
389
|
+
const IRC_INGRESS_PRUNE_INTERVAL_MS = 3600 * 1e3;
|
|
390
|
+
const IRC_INGRESS_TOMBSTONE_TTL_MS = 720 * 60 * 60 * 1e3;
|
|
391
|
+
const IRC_INGRESS_TOMBSTONE_MAX_ENTRIES = 1e3;
|
|
392
|
+
var IrcIngressPayloadError = class extends Error {
|
|
393
|
+
constructor(message) {
|
|
394
|
+
super(message);
|
|
395
|
+
this.name = "IrcIngressPayloadError";
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
function inspectRawPrivmsg(rawLine) {
|
|
399
|
+
const line = parseIrcLine(rawLine);
|
|
400
|
+
if (!line || line.command !== "PRIVMSG") throw new IrcIngressPayloadError("IRC ingress row is not a PRIVMSG line.");
|
|
401
|
+
const rawTarget = line.params[0]?.trim() ?? "";
|
|
402
|
+
const text = line.trailing ?? line.params[1] ?? "";
|
|
403
|
+
const prefix = parseIrcPrefix(line.prefix);
|
|
404
|
+
const senderNick = prefix.nick?.trim() ?? "";
|
|
405
|
+
if (!rawTarget || !senderNick || !text.trim()) throw new IrcIngressPayloadError("IRC PRIVMSG line is missing target, sender, or text.");
|
|
406
|
+
const isGroup = isChannelTarget(rawTarget);
|
|
407
|
+
const target = isGroup ? rawTarget : senderNick;
|
|
408
|
+
const lanePeer = normalizeLowercaseStringOrEmpty(target);
|
|
409
|
+
return {
|
|
410
|
+
laneKey: `${isGroup ? "channel" : "direct"}:${lanePeer}`,
|
|
411
|
+
message: {
|
|
412
|
+
target,
|
|
413
|
+
rawTarget,
|
|
414
|
+
senderNick,
|
|
415
|
+
senderUser: prefix.user?.trim() || void 0,
|
|
416
|
+
senderHost: prefix.host?.trim() || void 0,
|
|
417
|
+
text,
|
|
418
|
+
isGroup
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function decodeIrcIngressPayload(payload, claimedId) {
|
|
423
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload) || typeof payload.eventId !== "string" || !Number.isSafeInteger(payload.receivedAt) || (payload.receivedAt ?? 0) <= 0 || typeof payload.connectionEpoch !== "string" || !payload.connectionEpoch?.trim() || typeof payload.connectedNick !== "string" || !payload.connectedNick?.trim() || typeof payload.rawLine !== "string") throw new IrcIngressPayloadError(`IRC ingress row ${claimedId} has invalid metadata.`);
|
|
424
|
+
const validPayload = payload;
|
|
425
|
+
return {
|
|
426
|
+
version: validPayload.version,
|
|
427
|
+
body: {
|
|
428
|
+
eventId: validPayload.eventId,
|
|
429
|
+
receivedAt: validPayload.receivedAt,
|
|
430
|
+
connectionEpoch: validPayload.connectionEpoch,
|
|
431
|
+
connectedNick: validPayload.connectedNick,
|
|
432
|
+
rawLine: validPayload.rawLine
|
|
433
|
+
}
|
|
359
434
|
};
|
|
435
|
+
}
|
|
436
|
+
function inspectIrcIngress(raw, phase) {
|
|
437
|
+
try {
|
|
438
|
+
return {
|
|
439
|
+
eventId: raw.eventId,
|
|
440
|
+
laneKey: inspectRawPrivmsg(raw.rawLine).laneKey
|
|
441
|
+
};
|
|
442
|
+
} catch (error) {
|
|
443
|
+
if (phase === "admission" && error instanceof IrcIngressPayloadError) return {
|
|
444
|
+
eventId: raw.eventId,
|
|
445
|
+
laneKey: `invalid:${raw.eventId}`
|
|
446
|
+
};
|
|
447
|
+
throw error;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function resolveIrcIngressNonRetryableFailure(error) {
|
|
451
|
+
return error instanceof IrcIngressPayloadError ? {
|
|
452
|
+
reason: "invalid-event",
|
|
453
|
+
message: error.message
|
|
454
|
+
} : null;
|
|
455
|
+
}
|
|
456
|
+
function createIrcIngressMonitor(options) {
|
|
457
|
+
const monitor = createChannelIngressMonitor({
|
|
458
|
+
queue: options.queue ?? (() => getIrcRuntime().state.openChannelIngressQueue({ accountId: options.accountId })),
|
|
459
|
+
inspect: (raw, context) => inspectIrcIngress(raw, context.phase),
|
|
460
|
+
payload: {
|
|
461
|
+
version: IRC_INGRESS_PAYLOAD_VERSION,
|
|
462
|
+
serialize: (raw) => raw,
|
|
463
|
+
deserialize: (body) => body,
|
|
464
|
+
encode: ({ body }) => ({
|
|
465
|
+
version: IRC_INGRESS_PAYLOAD_VERSION,
|
|
466
|
+
...body
|
|
467
|
+
}),
|
|
468
|
+
decode: (payload, { claim }) => decodeIrcIngressPayload(payload, claim.id),
|
|
469
|
+
createClaimError: (_kind, claim) => new IrcIngressPayloadError(`IRC ingress row ${claim.id} has invalid metadata.`)
|
|
470
|
+
},
|
|
471
|
+
deliver: (raw, lifecycle, claim) => {
|
|
472
|
+
const message = {
|
|
473
|
+
...inspectRawPrivmsg(raw.rawLine).message,
|
|
474
|
+
messageId: claim.id,
|
|
475
|
+
timestamp: raw.receivedAt
|
|
476
|
+
};
|
|
477
|
+
return options.dispatch(message, lifecycle, {
|
|
478
|
+
connectedNick: raw.connectedNick.trim(),
|
|
479
|
+
connectionEpoch: raw.connectionEpoch.trim()
|
|
480
|
+
});
|
|
481
|
+
},
|
|
482
|
+
pollIntervalMs: options.pollIntervalMs ?? IRC_INGRESS_POLL_INTERVAL_MS,
|
|
483
|
+
retention: {
|
|
484
|
+
pruneIntervalMs: IRC_INGRESS_PRUNE_INTERVAL_MS,
|
|
485
|
+
completedTtlMs: IRC_INGRESS_TOMBSTONE_TTL_MS,
|
|
486
|
+
completedMaxEntries: IRC_INGRESS_TOMBSTONE_MAX_ENTRIES,
|
|
487
|
+
failedTtlMs: IRC_INGRESS_TOMBSTONE_TTL_MS,
|
|
488
|
+
failedMaxEntries: IRC_INGRESS_TOMBSTONE_MAX_ENTRIES
|
|
489
|
+
},
|
|
490
|
+
drain: {
|
|
491
|
+
resolveNonRetryableFailure: resolveIrcIngressNonRetryableFailure,
|
|
492
|
+
...options.adoptionStallTimeoutMs === void 0 ? {} : { adoptionStallTimeoutMs: options.adoptionStallTimeoutMs },
|
|
493
|
+
onLog: (message) => options.runtime.log?.(`irc ${message}`)
|
|
494
|
+
},
|
|
495
|
+
createStoppedError: () => /* @__PURE__ */ new Error("IRC ingress is stopped."),
|
|
496
|
+
onError: (error) => options.runtime.error?.(`irc ingress drain failed: ${String(error)}`)
|
|
497
|
+
});
|
|
360
498
|
return {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
499
|
+
openConnection: (connectionEpoch = randomUUID()) => {
|
|
500
|
+
const epoch = connectionEpoch.trim();
|
|
501
|
+
if (!epoch) throw new Error("IRC ingress connection epoch is required.");
|
|
502
|
+
let sequence = 0;
|
|
503
|
+
return {
|
|
504
|
+
connectionEpoch: epoch,
|
|
505
|
+
accept: (rawLine, connectedNick) => {
|
|
506
|
+
if (monitor.isStopped()) return Promise.reject(/* @__PURE__ */ new Error("IRC ingress is stopped."));
|
|
507
|
+
sequence += 1;
|
|
508
|
+
const eventId = `local:${epoch}:${String(sequence).padStart(12, "0")}`;
|
|
509
|
+
const receivedAt = Date.now();
|
|
510
|
+
const normalizedNick = connectedNick.trim();
|
|
511
|
+
if (!normalizedNick) return Promise.reject(/* @__PURE__ */ new Error("IRC ingress connected nickname is required."));
|
|
512
|
+
return monitor.admit({
|
|
513
|
+
eventId,
|
|
514
|
+
rawLine,
|
|
515
|
+
receivedAt,
|
|
516
|
+
connectionEpoch: epoch,
|
|
517
|
+
connectedNick: normalizedNick
|
|
518
|
+
}, { receivedAt }).then(() => void 0);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
},
|
|
522
|
+
start: monitor.start,
|
|
523
|
+
pause: monitor.pause,
|
|
524
|
+
stop: monitor.stop,
|
|
525
|
+
waitForIdle: monitor.waitForIdle
|
|
364
526
|
};
|
|
365
527
|
}
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region extensions/irc/src/monitor.ts
|
|
530
|
+
const IRC_MONITOR_RECONNECT_DELAY_MS = 1e3;
|
|
366
531
|
async function monitorIrcProvider(opts) {
|
|
367
532
|
const core = getIrcRuntime();
|
|
368
533
|
const cfg = opts.config ?? core.config.current();
|
|
@@ -377,6 +542,8 @@ async function monitorIrcProvider(opts) {
|
|
|
377
542
|
accountId: account.accountId
|
|
378
543
|
});
|
|
379
544
|
let client = null;
|
|
545
|
+
let activeConnectionEpoch = null;
|
|
546
|
+
let ingressPause = Promise.resolve();
|
|
380
547
|
let reconnectTimer = null;
|
|
381
548
|
let stopped = false;
|
|
382
549
|
const monitorAbort = new AbortController();
|
|
@@ -389,6 +556,48 @@ async function monitorIrcProvider(opts) {
|
|
|
389
556
|
removeAbortListener = () => opts.abortSignal?.removeEventListener("abort", forwardAbort);
|
|
390
557
|
}
|
|
391
558
|
}
|
|
559
|
+
const ingress = createIrcIngressMonitor({
|
|
560
|
+
accountId: account.accountId,
|
|
561
|
+
runtime,
|
|
562
|
+
...opts.ingressQueue ? { queue: opts.ingressQueue } : {},
|
|
563
|
+
dispatch: async (message, turnAdoptionLifecycle, context) => {
|
|
564
|
+
const activeClient = client;
|
|
565
|
+
if (!activeClient || stopped || monitorAbort.signal.aborted) return {
|
|
566
|
+
kind: "failed-retryable",
|
|
567
|
+
error: /* @__PURE__ */ new Error("IRC transport disconnected before ingress dispatch.")
|
|
568
|
+
};
|
|
569
|
+
if (normalizeLowercaseStringOrEmpty(message.senderNick) === normalizeLowercaseStringOrEmpty(context.connectedNick)) return { kind: "completed" };
|
|
570
|
+
if (!message.isGroup && context.connectionEpoch !== activeConnectionEpoch) {
|
|
571
|
+
logger.warn?.(`[${account.accountId}] dropping replayed IRC DM after the connection changed`);
|
|
572
|
+
return { kind: "completed" };
|
|
573
|
+
}
|
|
574
|
+
if (opts.onMessage) {
|
|
575
|
+
await opts.onMessage(message, activeClient);
|
|
576
|
+
return { kind: "completed" };
|
|
577
|
+
}
|
|
578
|
+
return await handleIrcInbound({
|
|
579
|
+
message,
|
|
580
|
+
account,
|
|
581
|
+
config: cfg,
|
|
582
|
+
runtime,
|
|
583
|
+
connectedNick: context.connectedNick,
|
|
584
|
+
turnAdoptionLifecycle,
|
|
585
|
+
sendReply: async (target, text) => {
|
|
586
|
+
const replyClient = client;
|
|
587
|
+
if (!replyClient || !replyClient.isReady() || stopped || monitorAbort.signal.aborted) throw new Error("IRC transport disconnected before reply send.");
|
|
588
|
+
if (!message.isGroup && context.connectionEpoch !== activeConnectionEpoch) throw new Error("IRC connection changed before private reply send.");
|
|
589
|
+
replyClient.sendPrivmsg(target, text);
|
|
590
|
+
opts.statusSink?.({ lastOutboundAt: Date.now() });
|
|
591
|
+
core.channel.activity.record({
|
|
592
|
+
channel: "irc",
|
|
593
|
+
accountId: account.accountId,
|
|
594
|
+
direction: "outbound"
|
|
595
|
+
});
|
|
596
|
+
},
|
|
597
|
+
statusSink: opts.statusSink
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
});
|
|
392
601
|
function scheduleReconnect() {
|
|
393
602
|
if (stopped || monitorAbort.signal.aborted || reconnectTimer) return;
|
|
394
603
|
reconnectTimer = setTimeout(() => {
|
|
@@ -403,6 +612,7 @@ async function monitorIrcProvider(opts) {
|
|
|
403
612
|
}
|
|
404
613
|
async function connect() {
|
|
405
614
|
if (stopped || monitorAbort.signal.aborted) return;
|
|
615
|
+
const ingressConnection = ingress.openConnection();
|
|
406
616
|
const nextClient = await connectIrcClient(buildIrcConnectOptions(account, {
|
|
407
617
|
channels: account.config.channels,
|
|
408
618
|
abortSignal: monitorAbort.signal,
|
|
@@ -417,54 +627,20 @@ async function monitorIrcProvider(opts) {
|
|
|
417
627
|
},
|
|
418
628
|
onDisconnect: () => {
|
|
419
629
|
if (stopped || monitorAbort.signal.aborted) return;
|
|
630
|
+
ingressPause = ingress.pause();
|
|
631
|
+
if (activeConnectionEpoch === ingressConnection.connectionEpoch) activeConnectionEpoch = null;
|
|
420
632
|
client = null;
|
|
421
633
|
logger.warn?.(`[${account.accountId}] IRC connection closed; reconnecting in ${IRC_MONITOR_RECONNECT_DELAY_MS}ms`);
|
|
422
634
|
scheduleReconnect();
|
|
423
635
|
},
|
|
424
636
|
onPrivmsg: async (event) => {
|
|
425
|
-
|
|
426
|
-
if (normalizeLowercaseStringOrEmpty(event.senderNick) === normalizeLowercaseStringOrEmpty(
|
|
427
|
-
const inboundTarget = resolveIrcInboundTarget({
|
|
428
|
-
target: event.target,
|
|
429
|
-
senderNick: event.senderNick
|
|
430
|
-
});
|
|
431
|
-
const message = {
|
|
432
|
-
messageId: makeIrcMessageId(),
|
|
433
|
-
target: inboundTarget.target,
|
|
434
|
-
rawTarget: inboundTarget.rawTarget,
|
|
435
|
-
senderNick: event.senderNick,
|
|
436
|
-
senderUser: event.senderUser,
|
|
437
|
-
senderHost: event.senderHost,
|
|
438
|
-
text: event.text,
|
|
439
|
-
timestamp: Date.now(),
|
|
440
|
-
isGroup: inboundTarget.isGroup
|
|
441
|
-
};
|
|
637
|
+
await ingressConnection.accept(event.rawLine, event.connectedNick);
|
|
638
|
+
if (normalizeLowercaseStringOrEmpty(event.senderNick) === normalizeLowercaseStringOrEmpty(event.connectedNick)) return;
|
|
442
639
|
core.channel.activity.record({
|
|
443
640
|
channel: "irc",
|
|
444
641
|
accountId: account.accountId,
|
|
445
642
|
direction: "inbound",
|
|
446
|
-
at:
|
|
447
|
-
});
|
|
448
|
-
if (opts.onMessage) {
|
|
449
|
-
await opts.onMessage(message, client);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
await handleIrcInbound({
|
|
453
|
-
message,
|
|
454
|
-
account,
|
|
455
|
-
config: cfg,
|
|
456
|
-
runtime,
|
|
457
|
-
connectedNick: client.nick,
|
|
458
|
-
sendReply: async (target, text) => {
|
|
459
|
-
client?.sendPrivmsg(target, text);
|
|
460
|
-
opts.statusSink?.({ lastOutboundAt: Date.now() });
|
|
461
|
-
core.channel.activity.record({
|
|
462
|
-
channel: "irc",
|
|
463
|
-
accountId: account.accountId,
|
|
464
|
-
direction: "outbound"
|
|
465
|
-
});
|
|
466
|
-
},
|
|
467
|
-
statusSink: opts.statusSink
|
|
643
|
+
at: Date.now()
|
|
468
644
|
});
|
|
469
645
|
}
|
|
470
646
|
}));
|
|
@@ -473,20 +649,40 @@ async function monitorIrcProvider(opts) {
|
|
|
473
649
|
return;
|
|
474
650
|
}
|
|
475
651
|
client = nextClient;
|
|
652
|
+
activeConnectionEpoch = ingressConnection.connectionEpoch;
|
|
653
|
+
await ingressPause;
|
|
654
|
+
if (client !== nextClient || !nextClient.isReady()) {
|
|
655
|
+
if (activeConnectionEpoch === ingressConnection.connectionEpoch) activeConnectionEpoch = null;
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
ingress.start();
|
|
476
659
|
logger.info(`[${account.accountId}] connected to ${account.host}:${account.port}${account.tls ? " (tls)" : ""} as ${nextClient.nick}`);
|
|
477
660
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
661
|
+
try {
|
|
662
|
+
await connect();
|
|
663
|
+
} catch (error) {
|
|
481
664
|
removeAbortListener?.();
|
|
482
665
|
removeAbortListener = null;
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
666
|
+
await ingress.stop();
|
|
667
|
+
throw error;
|
|
668
|
+
}
|
|
669
|
+
let stopTask;
|
|
670
|
+
return { stop: () => {
|
|
671
|
+
stopTask ??= (async () => {
|
|
672
|
+
stopped = true;
|
|
673
|
+
removeAbortListener?.();
|
|
674
|
+
removeAbortListener = null;
|
|
675
|
+
if (!monitorAbort.signal.aborted) monitorAbort.abort();
|
|
676
|
+
if (reconnectTimer) {
|
|
677
|
+
clearTimeout(reconnectTimer);
|
|
678
|
+
reconnectTimer = null;
|
|
679
|
+
}
|
|
680
|
+
client?.quit("shutdown");
|
|
681
|
+
client = null;
|
|
682
|
+
activeConnectionEpoch = null;
|
|
683
|
+
await ingress.stop();
|
|
684
|
+
})();
|
|
685
|
+
return stopTask;
|
|
490
686
|
} };
|
|
491
687
|
}
|
|
492
688
|
//#endregion
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/irc",
|
|
3
|
-
"version": "2026.7.2-beta.
|
|
3
|
+
"version": "2026.7.2-beta.4",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/irc",
|
|
9
|
-
"version": "2026.7.2-beta.
|
|
9
|
+
"version": "2026.7.2-beta.4",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"zod": "4.4.3"
|
|
12
12
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -6,20 +6,6 @@
|
|
|
6
6
|
"channels": [
|
|
7
7
|
"irc"
|
|
8
8
|
],
|
|
9
|
-
"channelEnvVars": {
|
|
10
|
-
"irc": [
|
|
11
|
-
"IRC_HOST",
|
|
12
|
-
"IRC_PORT",
|
|
13
|
-
"IRC_TLS",
|
|
14
|
-
"IRC_NICK",
|
|
15
|
-
"IRC_USERNAME",
|
|
16
|
-
"IRC_REALNAME",
|
|
17
|
-
"IRC_PASSWORD",
|
|
18
|
-
"IRC_CHANNELS",
|
|
19
|
-
"IRC_NICKSERV_PASSWORD",
|
|
20
|
-
"IRC_NICKSERV_REGISTER_EMAIL"
|
|
21
|
-
]
|
|
22
|
-
},
|
|
23
9
|
"configSchema": {
|
|
24
10
|
"type": "object",
|
|
25
11
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/irc",
|
|
3
|
-
"version": "2026.7.2-beta.
|
|
3
|
+
"version": "2026.7.2-beta.4",
|
|
4
4
|
"description": "OpenClaw IRC channel plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"openclaw": {
|
|
@@ -15,8 +15,19 @@
|
|
|
15
15
|
"allowInvalidConfigRecovery": true
|
|
16
16
|
},
|
|
17
17
|
"setupEntry": "./dist/setup-entry.js",
|
|
18
|
+
"setupFeatures": {
|
|
19
|
+
"configPromotion": true
|
|
20
|
+
},
|
|
18
21
|
"channel": {
|
|
19
22
|
"id": "irc",
|
|
23
|
+
"configuredState": {
|
|
24
|
+
"env": {
|
|
25
|
+
"allOf": [
|
|
26
|
+
"IRC_HOST",
|
|
27
|
+
"IRC_NICK"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
20
31
|
"label": "IRC",
|
|
21
32
|
"selectionLabel": "IRC (Server + Nick)",
|
|
22
33
|
"detailLabel": "IRC",
|
|
@@ -27,20 +38,89 @@
|
|
|
27
38
|
"internet-relay-chat"
|
|
28
39
|
],
|
|
29
40
|
"systemImage": "network",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
"setup": {
|
|
42
|
+
"fields": [
|
|
43
|
+
{
|
|
44
|
+
"key": "host",
|
|
45
|
+
"kind": "string",
|
|
46
|
+
"cli": {
|
|
47
|
+
"flags": "--host <host>",
|
|
48
|
+
"description": "IRC server host"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"key": "port",
|
|
53
|
+
"kind": "string",
|
|
54
|
+
"cli": {
|
|
55
|
+
"flags": "--port <port>",
|
|
56
|
+
"description": "IRC server port"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"key": "tls",
|
|
61
|
+
"kind": "boolean",
|
|
62
|
+
"cli": {
|
|
63
|
+
"flags": "--tls",
|
|
64
|
+
"description": "Use TLS for IRC"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"key": "nick",
|
|
69
|
+
"kind": "string",
|
|
70
|
+
"cli": {
|
|
71
|
+
"flags": "--nick <nick>",
|
|
72
|
+
"description": "IRC nickname"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"key": "username",
|
|
77
|
+
"kind": "string",
|
|
78
|
+
"cli": {
|
|
79
|
+
"flags": "--username <name>",
|
|
80
|
+
"description": "IRC username"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"key": "realname",
|
|
85
|
+
"kind": "string",
|
|
86
|
+
"cli": {
|
|
87
|
+
"flags": "--realname <name>",
|
|
88
|
+
"description": "IRC real name"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"key": "channels",
|
|
93
|
+
"kind": "string-list",
|
|
94
|
+
"cli": {
|
|
95
|
+
"flags": "--channels <names>",
|
|
96
|
+
"description": "IRC channels"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"key": "password",
|
|
101
|
+
"kind": "string",
|
|
102
|
+
"sensitive": true,
|
|
103
|
+
"cli": {
|
|
104
|
+
"flags": "--password <password>",
|
|
105
|
+
"description": "IRC server password"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"key": "useEnv",
|
|
110
|
+
"kind": "boolean",
|
|
111
|
+
"cli": {
|
|
112
|
+
"flags": "--use-env",
|
|
113
|
+
"description": "Use IRC environment configuration"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
]
|
|
37
117
|
}
|
|
38
118
|
},
|
|
39
119
|
"compat": {
|
|
40
|
-
"pluginApi": ">=2026.7.2-beta.
|
|
120
|
+
"pluginApi": ">=2026.7.2-beta.4"
|
|
41
121
|
},
|
|
42
122
|
"build": {
|
|
43
|
-
"openclawVersion": "2026.7.2-beta.
|
|
123
|
+
"openclawVersion": "2026.7.2-beta.4",
|
|
44
124
|
"bundledDist": false
|
|
45
125
|
},
|
|
46
126
|
"release": {
|
|
@@ -66,7 +146,7 @@
|
|
|
66
146
|
"README.md"
|
|
67
147
|
],
|
|
68
148
|
"peerDependencies": {
|
|
69
|
-
"openclaw": ">=2026.7.2-beta.
|
|
149
|
+
"openclaw": ">=2026.7.2-beta.4"
|
|
70
150
|
},
|
|
71
151
|
"peerDependenciesMeta": {
|
|
72
152
|
"openclaw": {
|