@openclaw/irc 2026.8.1-beta.2 → 2026.9.1-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-Bfg9kJRw.js → channel-W4DLS5ip.js} +178 -73
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-runtime-kWMtRD4g.js → channel-runtime-CA62Mbrh.js} +15 -13
- package/dist/{config-schema-DdB_h5mL.js → config-schema-BDXGjxOr.js} +2 -1
- package/openclaw.plugin.json +40 -0
- package/package.json +4 -4
package/dist/api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { _ as resolveDefaultIrcAccountId, g as listIrcAccountIds, h as listEnabledIrcAccounts, n as ircSetupWizard, r as ircSetupAdapter, t as ircPlugin, v as resolveIrcAccount } from "./channel-
|
|
1
|
+
import { _ as resolveDefaultIrcAccountId, g as listIrcAccountIds, h as listEnabledIrcAccounts, n as ircSetupWizard, r as ircSetupAdapter, t as ircPlugin, v as resolveIrcAccount } from "./channel-W4DLS5ip.js";
|
|
2
2
|
import { n as setIrcRuntime } from "./runtime-xy-FcjJC.js";
|
|
3
3
|
export { ircPlugin, ircSetupAdapter, ircSetupWizard, listEnabledIrcAccounts, listIrcAccountIds, resolveDefaultIrcAccountId, resolveIrcAccount, setIrcRuntime };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { t as IrcChannelConfigSchema } from "./config-schema-
|
|
1
|
+
import { t as IrcChannelConfigSchema } from "./config-schema-BDXGjxOr.js";
|
|
2
2
|
import { t as getIrcRuntime } from "./runtime-xy-FcjJC.js";
|
|
3
3
|
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-D_DC3YWj.js";
|
|
4
4
|
import { createAccountListHelpers, describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
5
5
|
import { formatNormalizedAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
|
|
6
6
|
import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
7
7
|
import { buildChannelOutboundSessionRoute, createChatChannelPlugin, parseOptionalDelimitedEntries } from "openclaw/plugin-sdk/channel-core";
|
|
8
|
-
import { buildMutableAllowEntryDetector, collectStandardAllowlistLists,
|
|
8
|
+
import { buildMutableAllowEntryDetector, collectStandardAllowlistLists, createAllowlistProviderOpenWarningCollector, createConditionalWarningCollector, createDangerousNameMatchingMutableAllowlistWarningCollector, resolveScopeKeyCaseInsensitive, resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
|
|
9
9
|
import { createChannelDirectoryAdapter, createResolvedDirectoryEntriesLister } from "openclaw/plugin-sdk/directory-runtime";
|
|
10
10
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
11
11
|
import { buildBaseChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
@@ -14,17 +14,19 @@ import { DEFAULT_ACCOUNT_ID, DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2, normali
|
|
|
14
14
|
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
15
15
|
import { isTruthyEnvValue } from "openclaw/plugin-sdk/runtime-env";
|
|
16
16
|
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
|
|
17
|
-
import {
|
|
17
|
+
import { resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
18
18
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, normalizeStringifiedOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
19
|
-
import { createAccountStatusSink, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter, runPassiveAccountLifecycle, sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
|
|
19
|
+
import { createAccountStatusSink, createMessageReceiptFromOutboundResults, createReplyToFanout, defineChannelMessageAdapter, runPassiveAccountLifecycle, sanitizeForPlainText } from "openclaw/plugin-sdk/channel-outbound";
|
|
20
20
|
import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
|
|
21
21
|
import { chunkTextForOutbound, convertMarkdownTables, sanitizeAssistantVisibleText, stripMarkdown } from "openclaw/plugin-sdk/text-chunking";
|
|
22
|
+
import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
|
|
22
23
|
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
23
24
|
import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
24
25
|
import net from "node:net";
|
|
25
26
|
import tls from "node:tls";
|
|
26
27
|
import { withTimeout } from "openclaw/plugin-sdk/security-runtime";
|
|
27
28
|
import { randomUUID } from "node:crypto";
|
|
29
|
+
import { assertSecretOwnerAvailable } from "openclaw/plugin-sdk/channel-secret-owner-runtime";
|
|
28
30
|
import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
|
|
29
31
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/routing";
|
|
30
32
|
import { applyAccountNameToChannelSection, createAllowFromSection, createPromptParsedAllowFromForAccount, createSetupInputPresenceValidator, createSetupTranslator, createStandardChannelSetupStatus, createTopLevelChannelAllowFromSetter, createTopLevelChannelDmPolicySetter, formatDocsLink, patchScopedAccountConfig, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
|
|
@@ -42,6 +44,16 @@ const { listAccountIds: listIrcAccountIds, resolveDefaultAccountId: resolveDefau
|
|
|
42
44
|
hasImplicitDefaultAccount: (cfg) => Boolean((cfg.channels?.irc?.host?.trim() || process.env.IRC_HOST?.trim()) && (cfg.channels?.irc?.nick?.trim() || process.env.IRC_NICK?.trim()))
|
|
43
45
|
});
|
|
44
46
|
function resolvePassword(accountId, merged) {
|
|
47
|
+
const configPassword = resolveSecretInputString({
|
|
48
|
+
value: merged.password,
|
|
49
|
+
path: `channels.irc.accounts.${accountId}.password`,
|
|
50
|
+
mode: "inspect"
|
|
51
|
+
});
|
|
52
|
+
if (configPassword.status === "configured_unavailable") return {
|
|
53
|
+
password: "",
|
|
54
|
+
source: "config",
|
|
55
|
+
unavailable: true
|
|
56
|
+
};
|
|
45
57
|
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
46
58
|
const envPassword = process.env.IRC_PASSWORD?.trim();
|
|
47
59
|
if (envPassword) return {
|
|
@@ -51,7 +63,7 @@ function resolvePassword(accountId, merged) {
|
|
|
51
63
|
}
|
|
52
64
|
if (merged.passwordFile?.trim()) {
|
|
53
65
|
let diagnostic;
|
|
54
|
-
const
|
|
66
|
+
const password = tryReadSecretFileSync(merged.passwordFile, "IRC password file", {
|
|
55
67
|
rejectSymlink: true,
|
|
56
68
|
credentialDiagnostic: {
|
|
57
69
|
configPath: `channels.irc.accounts.${accountId}.passwordFile`,
|
|
@@ -60,8 +72,8 @@ function resolvePassword(accountId, merged) {
|
|
|
60
72
|
}
|
|
61
73
|
}
|
|
62
74
|
});
|
|
63
|
-
if (
|
|
64
|
-
password
|
|
75
|
+
if (password) return {
|
|
76
|
+
password,
|
|
65
77
|
source: "passwordFile"
|
|
66
78
|
};
|
|
67
79
|
return {
|
|
@@ -70,12 +82,8 @@ function resolvePassword(accountId, merged) {
|
|
|
70
82
|
diagnostic
|
|
71
83
|
};
|
|
72
84
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
path: `channels.irc.accounts.${accountId}.password`
|
|
76
|
-
});
|
|
77
|
-
if (configPassword) return {
|
|
78
|
-
password: configPassword,
|
|
85
|
+
if (configPassword.status === "available") return {
|
|
86
|
+
password: configPassword.value,
|
|
79
87
|
source: "config"
|
|
80
88
|
};
|
|
81
89
|
return {
|
|
@@ -85,23 +93,29 @@ function resolvePassword(accountId, merged) {
|
|
|
85
93
|
}
|
|
86
94
|
function resolveNickServConfig(accountId, nickserv) {
|
|
87
95
|
const base = nickserv ?? {};
|
|
96
|
+
const configPassword = resolveSecretInputString({
|
|
97
|
+
value: base.password,
|
|
98
|
+
path: `channels.irc.accounts.${accountId}.nickserv.password`,
|
|
99
|
+
mode: "inspect"
|
|
100
|
+
});
|
|
101
|
+
const unavailable = Boolean(configPassword.ref);
|
|
88
102
|
const envPassword = accountId === DEFAULT_ACCOUNT_ID ? process.env.IRC_NICKSERV_PASSWORD?.trim() : void 0;
|
|
89
103
|
const envRegisterEmail = accountId === DEFAULT_ACCOUNT_ID ? process.env.IRC_NICKSERV_REGISTER_EMAIL?.trim() : void 0;
|
|
90
104
|
const passwordFile = base.passwordFile?.trim();
|
|
91
|
-
let resolvedPassword
|
|
92
|
-
value: base.password,
|
|
93
|
-
path: `channels.irc.accounts.${accountId}.nickserv.password`
|
|
94
|
-
}) || envPassword || "";
|
|
105
|
+
let resolvedPassword;
|
|
95
106
|
let diagnostic;
|
|
96
|
-
if (!
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
if (!unavailable) {
|
|
108
|
+
resolvedPassword = configPassword.value || envPassword;
|
|
109
|
+
if (!resolvedPassword && passwordFile) resolvedPassword = tryReadSecretFileSync(passwordFile, "IRC NickServ password file", {
|
|
110
|
+
rejectSymlink: true,
|
|
111
|
+
credentialDiagnostic: {
|
|
112
|
+
configPath: `channels.irc.accounts.${accountId}.nickserv.passwordFile`,
|
|
113
|
+
report: (value) => {
|
|
114
|
+
diagnostic = value;
|
|
115
|
+
}
|
|
102
116
|
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
105
119
|
return {
|
|
106
120
|
config: {
|
|
107
121
|
...base,
|
|
@@ -110,7 +124,8 @@ function resolveNickServConfig(accountId, nickserv) {
|
|
|
110
124
|
password: resolvedPassword || void 0,
|
|
111
125
|
registerEmail: base.registerEmail?.trim() || envRegisterEmail || void 0
|
|
112
126
|
},
|
|
113
|
-
diagnostic
|
|
127
|
+
diagnostic,
|
|
128
|
+
unavailable: base.enabled !== false && unavailable
|
|
114
129
|
};
|
|
115
130
|
}
|
|
116
131
|
function resolveIrcAccount(params) {
|
|
@@ -154,7 +169,7 @@ function resolveIrcAccount(params) {
|
|
|
154
169
|
realname,
|
|
155
170
|
password: passwordResolution.password,
|
|
156
171
|
passwordSource: passwordResolution.source,
|
|
157
|
-
tokenStatus: diagnostics.length > 0 ? "configured_unavailable" : passwordResolution.password || nickservResolution.config.password ? "available" : "missing",
|
|
172
|
+
tokenStatus: diagnostics.length > 0 || passwordResolution.unavailable || nickservResolution.unavailable ? "configured_unavailable" : passwordResolution.password || nickservResolution.config.password ? "available" : "missing",
|
|
158
173
|
...diagnostics.length > 0 ? { credentialDiagnostics: diagnostics } : {},
|
|
159
174
|
config
|
|
160
175
|
};
|
|
@@ -183,7 +198,7 @@ const collectIrcMutableAllowlistWarnings = createDangerousNameMatchingMutableAll
|
|
|
183
198
|
});
|
|
184
199
|
//#endregion
|
|
185
200
|
//#region extensions/irc/src/gateway.ts
|
|
186
|
-
const loadIrcChannelRuntime$1 = createLazyRuntimeModule(() => import("./channel-runtime-
|
|
201
|
+
const loadIrcChannelRuntime$1 = createLazyRuntimeModule(() => import("./channel-runtime-CA62Mbrh.js"));
|
|
187
202
|
async function startIrcGatewayAccount(ctx) {
|
|
188
203
|
const account = ctx.account;
|
|
189
204
|
const statusSink = createAccountStatusSink({
|
|
@@ -208,6 +223,18 @@ async function startIrcGatewayAccount(ctx) {
|
|
|
208
223
|
});
|
|
209
224
|
}
|
|
210
225
|
//#endregion
|
|
226
|
+
//#region extensions/irc/src/outbound-base.ts
|
|
227
|
+
function sanitizeIrcAssistantText(text) {
|
|
228
|
+
return sanitizeForPlainText(sanitizeAssistantVisibleText(text));
|
|
229
|
+
}
|
|
230
|
+
const ircOutboundBaseAdapter = {
|
|
231
|
+
deliveryMode: "direct",
|
|
232
|
+
chunker: chunkTextForOutbound,
|
|
233
|
+
chunkerMode: "markdown",
|
|
234
|
+
textChunkLimit: 350,
|
|
235
|
+
sanitizeText: ({ text }) => sanitizeIrcAssistantText(text)
|
|
236
|
+
};
|
|
237
|
+
//#endregion
|
|
211
238
|
//#region extensions/irc/src/control-chars.ts
|
|
212
239
|
function isIrcControlChar(charCode) {
|
|
213
240
|
return charCode <= 31 || charCode === 127;
|
|
@@ -613,6 +640,8 @@ async function connectIrcClient(options) {
|
|
|
613
640
|
//#endregion
|
|
614
641
|
//#region extensions/irc/src/connect-options.ts
|
|
615
642
|
function buildIrcConnectOptions(account, overrides = {}) {
|
|
643
|
+
assertSecretOwnerAvailable("account", `irc:${account.accountId}`);
|
|
644
|
+
if (account.tokenStatus === "configured_unavailable") throw new Error(`IRC credentials for account "${account.accountId}" are configured but unavailable.`);
|
|
616
645
|
return {
|
|
617
646
|
host: account.host,
|
|
618
647
|
port: account.port,
|
|
@@ -713,7 +742,7 @@ function resolveTarget(to, opts) {
|
|
|
713
742
|
if (fromOpt) return fromOpt;
|
|
714
743
|
throw new Error(`Invalid IRC target: ${to}`);
|
|
715
744
|
}
|
|
716
|
-
async function
|
|
745
|
+
async function sendIrcMessages(to, messages, opts, onDeliveryResult) {
|
|
717
746
|
const cfg = requireRuntimeConfig(opts.cfg, "IRC send");
|
|
718
747
|
const account = resolveIrcAccount({
|
|
719
748
|
cfg,
|
|
@@ -726,35 +755,108 @@ async function sendMessageIrc(to, text, opts) {
|
|
|
726
755
|
channel: "irc",
|
|
727
756
|
accountId: account.accountId
|
|
728
757
|
});
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
758
|
+
const preparedMessages = messages.map((message) => {
|
|
759
|
+
const prepared = stripMarkdown(convertMarkdownTables(message.text.trim(), tableMode));
|
|
760
|
+
if (!prepared.trim()) throw new Error("Message must be non-empty for IRC sends");
|
|
761
|
+
return {
|
|
762
|
+
payload: message.replyTo ? `${prepared}\n\n[reply:${message.replyTo}]` : prepared,
|
|
763
|
+
replyTo: message.replyTo
|
|
764
|
+
};
|
|
765
|
+
});
|
|
766
|
+
if (preparedMessages.length === 0) return [];
|
|
767
|
+
opts.abortSignal?.throwIfAborted();
|
|
768
|
+
let transient;
|
|
769
|
+
const client = opts.client?.isReady() ? opts.client : transient = await connectIrcClient(buildIrcConnectOptions(account, {
|
|
770
|
+
connectTimeoutMs: 12e3,
|
|
771
|
+
abortSignal: opts.abortSignal
|
|
772
|
+
}));
|
|
773
|
+
const results = [];
|
|
774
|
+
try {
|
|
775
|
+
opts.abortSignal?.throwIfAborted();
|
|
776
|
+
if (transient && (target.startsWith("#") || target.startsWith("&"))) client.join(target);
|
|
777
|
+
for (const message of preparedMessages) {
|
|
778
|
+
opts.abortSignal?.throwIfAborted();
|
|
779
|
+
if (!client.isReady()) throw new Error("IRC connection closed before send");
|
|
780
|
+
await opts.onPlatformSendDispatch?.();
|
|
781
|
+
opts.abortSignal?.throwIfAborted();
|
|
782
|
+
if (!client.isReady()) throw new Error("IRC connection closed before send");
|
|
783
|
+
client.sendPrivmsg(target, message.payload);
|
|
784
|
+
recordIrcOutboundActivity(account.accountId);
|
|
785
|
+
const messageId = makeIrcMessageId();
|
|
786
|
+
const result = {
|
|
748
787
|
messageId,
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
788
|
+
target,
|
|
789
|
+
receipt: createMessageReceiptFromOutboundResults({
|
|
790
|
+
results: [{
|
|
791
|
+
channel: "irc",
|
|
792
|
+
messageId,
|
|
793
|
+
conversationId: target
|
|
794
|
+
}],
|
|
795
|
+
kind: "text",
|
|
796
|
+
...message.replyTo ? { replyToId: message.replyTo } : {}
|
|
797
|
+
})
|
|
798
|
+
};
|
|
799
|
+
results.push(result);
|
|
800
|
+
await onDeliveryResult?.(result);
|
|
801
|
+
}
|
|
802
|
+
return results;
|
|
803
|
+
} finally {
|
|
804
|
+
transient?.quit("sent");
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async function sendMessageIrc(to, text, opts) {
|
|
808
|
+
const result = (await sendIrcMessages(to, [{
|
|
809
|
+
text,
|
|
810
|
+
...opts.replyTo ? { replyTo: opts.replyTo } : {}
|
|
811
|
+
}], opts))[0];
|
|
812
|
+
if (!result) throw new Error("Message must be non-empty for IRC sends");
|
|
813
|
+
return result;
|
|
755
814
|
}
|
|
756
815
|
//#endregion
|
|
757
816
|
//#region extensions/irc/src/message-adapter.ts
|
|
817
|
+
function toIrcMessageResult({ target, ...result }) {
|
|
818
|
+
return {
|
|
819
|
+
...result,
|
|
820
|
+
target: {
|
|
821
|
+
kind: "conversation",
|
|
822
|
+
id: target
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
async function sendIrcMessage(...args) {
|
|
827
|
+
return toIrcMessageResult(await sendMessageIrc(...args));
|
|
828
|
+
}
|
|
829
|
+
const sendFormattedIrcText = async (ctx) => {
|
|
830
|
+
const { chunkMarkdownTextWithMode, resolveChunkMode, resolveTextChunkLimit } = await import("openclaw/plugin-sdk/reply-chunking");
|
|
831
|
+
const accountId = ctx.accountId ?? void 0;
|
|
832
|
+
const textLimit = ctx.formatting?.textLimit ?? resolveTextChunkLimit(ctx.cfg, "irc", accountId, { fallbackLimit: ircOutboundBaseAdapter.textChunkLimit });
|
|
833
|
+
const chunkMode = ctx.formatting?.chunkMode ?? resolveChunkMode(ctx.cfg, "irc", accountId);
|
|
834
|
+
const chunkText = (text) => ctx.formatting ? ircOutboundBaseAdapter.chunker(text, textLimit, { formatting: ctx.formatting }) : ircOutboundBaseAdapter.chunker(text, textLimit);
|
|
835
|
+
let chunks;
|
|
836
|
+
if (chunkMode === "newline") {
|
|
837
|
+
const blocks = chunkMarkdownTextWithMode(ctx.text, textLimit, chunkMode);
|
|
838
|
+
if (blocks.length === 0 && ctx.text) blocks.push(ctx.text);
|
|
839
|
+
chunks = blocks.flatMap((block) => {
|
|
840
|
+
const blockChunks = chunkText(block);
|
|
841
|
+
return blockChunks.length === 0 && block ? [block] : blockChunks;
|
|
842
|
+
});
|
|
843
|
+
} else chunks = chunkText(ctx.text);
|
|
844
|
+
const nextReplyToId = createReplyToFanout(ctx);
|
|
845
|
+
return (await sendIrcMessages(ctx.to, chunks.map((text) => {
|
|
846
|
+
const replyTo = nextReplyToId();
|
|
847
|
+
return replyTo ? {
|
|
848
|
+
text,
|
|
849
|
+
replyTo
|
|
850
|
+
} : { text };
|
|
851
|
+
}), {
|
|
852
|
+
cfg: ctx.cfg,
|
|
853
|
+
accountId,
|
|
854
|
+
abortSignal: ctx.abortSignal,
|
|
855
|
+
onPlatformSendDispatch: ctx.onPlatformSendDispatch
|
|
856
|
+
}, async (result) => {
|
|
857
|
+
await ctx.onDeliveryResult?.(attachChannelToResult("irc", toIrcMessageResult(result)));
|
|
858
|
+
})).map((result) => attachChannelToResult("irc", toIrcMessageResult(result)));
|
|
859
|
+
};
|
|
758
860
|
const ircMessageAdapter = defineChannelMessageAdapter({
|
|
759
861
|
id: "irc",
|
|
760
862
|
durableFinal: { capabilities: {
|
|
@@ -763,12 +865,12 @@ const ircMessageAdapter = defineChannelMessageAdapter({
|
|
|
763
865
|
replyTo: true
|
|
764
866
|
} },
|
|
765
867
|
send: {
|
|
766
|
-
text: async ({ cfg, to, text, accountId, replyToId }) => await
|
|
868
|
+
text: async ({ cfg, to, text, accountId, replyToId }) => await sendIrcMessage(to, text, {
|
|
767
869
|
cfg,
|
|
768
870
|
accountId: accountId ?? void 0,
|
|
769
871
|
replyTo: replyToId ?? void 0
|
|
770
872
|
}),
|
|
771
|
-
media: async ({ cfg, to, text, mediaUrl, accountId, replyToId }) => await
|
|
873
|
+
media: async ({ cfg, to, text, mediaUrl, accountId, replyToId }) => await sendIrcMessage(to, mediaUrl ? `${text}\n\nAttachment: ${mediaUrl}` : text, {
|
|
772
874
|
cfg,
|
|
773
875
|
accountId: accountId ?? void 0,
|
|
774
876
|
replyTo: replyToId ?? void 0
|
|
@@ -776,18 +878,6 @@ const ircMessageAdapter = defineChannelMessageAdapter({
|
|
|
776
878
|
}
|
|
777
879
|
});
|
|
778
880
|
//#endregion
|
|
779
|
-
//#region extensions/irc/src/outbound-base.ts
|
|
780
|
-
function sanitizeIrcAssistantText(text) {
|
|
781
|
-
return sanitizeForPlainText(sanitizeAssistantVisibleText(text));
|
|
782
|
-
}
|
|
783
|
-
const ircOutboundBaseAdapter = {
|
|
784
|
-
deliveryMode: "direct",
|
|
785
|
-
chunker: chunkTextForOutbound,
|
|
786
|
-
chunkerMode: "markdown",
|
|
787
|
-
textChunkLimit: 350,
|
|
788
|
-
sanitizeText: ({ text }) => sanitizeIrcAssistantText(text)
|
|
789
|
-
};
|
|
790
|
-
//#endregion
|
|
791
881
|
//#region extensions/irc/src/policy.ts
|
|
792
882
|
function resolveIrcGroupScope(params) {
|
|
793
883
|
const { "*": wildcard, ...groups } = params.groups ?? {};
|
|
@@ -1399,7 +1489,7 @@ const meta = {
|
|
|
1399
1489
|
systemImage: "number",
|
|
1400
1490
|
markdownCapable: true
|
|
1401
1491
|
};
|
|
1402
|
-
const loadIrcChannelRuntime = createLazyRuntimeModule(() => import("./channel-runtime-
|
|
1492
|
+
const loadIrcChannelRuntime = createLazyRuntimeModule(() => import("./channel-runtime-CA62Mbrh.js"));
|
|
1403
1493
|
function normalizePairingTarget(raw) {
|
|
1404
1494
|
const normalized = normalizeIrcAllowEntry(raw);
|
|
1405
1495
|
if (!normalized) return "";
|
|
@@ -1455,7 +1545,7 @@ const resolveIrcDmPolicy = createScopedDmSecurityResolver({
|
|
|
1455
1545
|
policyPathSuffix: "dmPolicy",
|
|
1456
1546
|
normalizeEntry: (raw) => normalizeIrcAllowEntry(raw)
|
|
1457
1547
|
});
|
|
1458
|
-
const
|
|
1548
|
+
const collectIrcGroupPolicyWarnings = createAllowlistProviderOpenWarningCollector({
|
|
1459
1549
|
providerConfigPresent: (cfg) => cfg.channels?.irc !== void 0,
|
|
1460
1550
|
resolveGroupPolicy: (account) => account.config.groupPolicy,
|
|
1461
1551
|
buildOpenWarning: {
|
|
@@ -1463,7 +1553,19 @@ const collectIrcSecurityWarnings = composeAccountWarningCollectors(createAllowli
|
|
|
1463
1553
|
openBehavior: "allows all channels and senders (mention-gated)",
|
|
1464
1554
|
remediation: "Prefer channels.irc.groupPolicy=\"allowlist\" with channels.irc.groups"
|
|
1465
1555
|
}
|
|
1466
|
-
})
|
|
1556
|
+
});
|
|
1557
|
+
const collectIrcOpenGroupFindings = createConditionalWarningCollector.findings({
|
|
1558
|
+
collectWarnings: collectIrcGroupPolicyWarnings,
|
|
1559
|
+
checkId: "channels.irc.groups.open",
|
|
1560
|
+
severity: "critical",
|
|
1561
|
+
title: "IRC security warning"
|
|
1562
|
+
});
|
|
1563
|
+
const collectIrcSecurityWarnings = (params) => [
|
|
1564
|
+
...collectIrcOpenGroupFindings(params),
|
|
1565
|
+
...!params.account.config.tls ? ["- IRC TLS is disabled (channels.irc.tls=false); traffic and credentials are plaintext."] : [],
|
|
1566
|
+
...params.account.config.nickserv?.register ? ["- IRC NickServ registration is enabled (channels.irc.nickserv.register=true); this sends \"REGISTER\" on every connect. Disable after first successful registration."] : [],
|
|
1567
|
+
...params.account.config.nickserv?.register && !params.account.config.nickserv.password?.trim() ? ["- IRC NickServ registration is enabled but no NickServ password is resolved; set channels.irc.nickserv.password, channels.irc.nickserv.passwordFile, or IRC_NICKSERV_PASSWORD."] : []
|
|
1568
|
+
];
|
|
1467
1569
|
const ircPlugin = createChatChannelPlugin({
|
|
1468
1570
|
base: {
|
|
1469
1571
|
id: "irc",
|
|
@@ -1630,7 +1732,10 @@ const ircPlugin = createChatChannelPlugin({
|
|
|
1630
1732
|
collectWarnings: collectIrcSecurityWarnings
|
|
1631
1733
|
},
|
|
1632
1734
|
outbound: {
|
|
1633
|
-
base:
|
|
1735
|
+
base: {
|
|
1736
|
+
...ircOutboundBaseAdapter,
|
|
1737
|
+
sendFormattedText: sendFormattedIrcText
|
|
1738
|
+
},
|
|
1634
1739
|
attachedResults: {
|
|
1635
1740
|
channel: "irc",
|
|
1636
1741
|
sendText: ({ onDeliveryResult: _onDeliveryResult, ...ctx }) => ircMessageAdapter.send.text(ctx),
|
|
@@ -1642,4 +1747,4 @@ const ircPlugin = createChatChannelPlugin({
|
|
|
1642
1747
|
}
|
|
1643
1748
|
});
|
|
1644
1749
|
//#endregion
|
|
1645
|
-
export { resolveDefaultIrcAccountId as _, resolveIrcGroupRequireMention as a,
|
|
1750
|
+
export { resolveDefaultIrcAccountId as _, resolveIrcGroupRequireMention as a, isChannelTarget as c, connectIrcClient as d, parseIrcLine as f, listIrcAccountIds as g, listEnabledIrcAccounts as h, resolveIrcGroupMatch as i, normalizeIrcAllowEntry as l, sanitizeIrcAssistantText as m, ircSetupWizard as n, sendMessageIrc as o, parseIrcPrefix as p, ircSetupAdapter as r, buildIrcAllowlistCandidates as s, ircPlugin as t, buildIrcConnectOptions as u, resolveIrcAccount as v };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as IrcChannelConfigSchema } from "./config-schema-
|
|
1
|
+
import { t as IrcChannelConfigSchema } from "./config-schema-BDXGjxOr.js";
|
|
2
2
|
export { IrcChannelConfigSchema };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as ircPlugin } from "./channel-
|
|
1
|
+
import { t as ircPlugin } from "./channel-W4DLS5ip.js";
|
|
2
2
|
export { ircPlugin };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as resolveIrcGroupRequireMention, c as
|
|
1
|
+
import { a as resolveIrcGroupRequireMention, c as isChannelTarget, d as connectIrcClient, f as parseIrcLine, i as resolveIrcGroupMatch, l as normalizeIrcAllowEntry, m as sanitizeIrcAssistantText, o as sendMessageIrc, p as parseIrcPrefix, s as buildIrcAllowlistCandidates, u as buildIrcConnectOptions, v as resolveIrcAccount } from "./channel-W4DLS5ip.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
4
|
import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
@@ -455,14 +455,16 @@ function decodeIrcIngressPayload(payload, claimedId) {
|
|
|
455
455
|
}
|
|
456
456
|
function inspectIrcIngress(raw, phase) {
|
|
457
457
|
try {
|
|
458
|
+
const projection = inspectRawPrivmsg(raw.body.rawLine);
|
|
459
|
+
if (phase === "claim") raw.claimedProjection = projection;
|
|
458
460
|
return {
|
|
459
|
-
eventId: raw.eventId,
|
|
460
|
-
laneKey:
|
|
461
|
+
eventId: raw.body.eventId,
|
|
462
|
+
laneKey: projection.laneKey
|
|
461
463
|
};
|
|
462
464
|
} catch (error) {
|
|
463
465
|
if (phase === "admission" && error instanceof IrcIngressPayloadError) return {
|
|
464
|
-
eventId: raw.eventId,
|
|
465
|
-
laneKey: `invalid:${raw.eventId}`
|
|
466
|
+
eventId: raw.body.eventId,
|
|
467
|
+
laneKey: `invalid:${raw.body.eventId}`
|
|
466
468
|
};
|
|
467
469
|
throw error;
|
|
468
470
|
}
|
|
@@ -479,8 +481,8 @@ function createIrcIngressMonitor(options) {
|
|
|
479
481
|
inspect: (raw, context) => inspectIrcIngress(raw, context.phase),
|
|
480
482
|
payload: {
|
|
481
483
|
version: IRC_INGRESS_PAYLOAD_VERSION,
|
|
482
|
-
serialize: (raw) => raw,
|
|
483
|
-
deserialize: (body) => body,
|
|
484
|
+
serialize: (raw) => raw.body,
|
|
485
|
+
deserialize: (body) => ({ body }),
|
|
484
486
|
encode: ({ body }) => ({
|
|
485
487
|
version: IRC_INGRESS_PAYLOAD_VERSION,
|
|
486
488
|
...body
|
|
@@ -490,13 +492,13 @@ function createIrcIngressMonitor(options) {
|
|
|
490
492
|
},
|
|
491
493
|
deliver: (raw, lifecycle, claim) => {
|
|
492
494
|
const message = {
|
|
493
|
-
...
|
|
495
|
+
...raw.claimedProjection.message,
|
|
494
496
|
messageId: claim.id,
|
|
495
|
-
timestamp: raw.receivedAt
|
|
497
|
+
timestamp: raw.body.receivedAt
|
|
496
498
|
};
|
|
497
499
|
return options.dispatch(message, lifecycle, {
|
|
498
|
-
connectedNick: raw.connectedNick.trim(),
|
|
499
|
-
connectionEpoch: raw.connectionEpoch.trim()
|
|
500
|
+
connectedNick: raw.body.connectedNick.trim(),
|
|
501
|
+
connectionEpoch: raw.body.connectionEpoch.trim()
|
|
500
502
|
});
|
|
501
503
|
},
|
|
502
504
|
pollIntervalMs: options.pollIntervalMs ?? IRC_INGRESS_POLL_INTERVAL_MS,
|
|
@@ -526,13 +528,13 @@ function createIrcIngressMonitor(options) {
|
|
|
526
528
|
const receivedAt = Date.now();
|
|
527
529
|
const normalizedNick = connectedNick.trim();
|
|
528
530
|
if (!normalizedNick) return Promise.reject(/* @__PURE__ */ new Error("IRC ingress connected nickname is required."));
|
|
529
|
-
return monitor.admit({
|
|
531
|
+
return monitor.admit({ body: {
|
|
530
532
|
eventId,
|
|
531
533
|
rawLine,
|
|
532
534
|
receivedAt,
|
|
533
535
|
connectionEpoch: epoch,
|
|
534
536
|
connectedNick: normalizedNick
|
|
535
|
-
}, { receivedAt }).then(() => void 0);
|
|
537
|
+
} }, { receivedAt }).then(() => void 0);
|
|
536
538
|
}
|
|
537
539
|
};
|
|
538
540
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createChannelConfigUiHints } from "openclaw/plugin-sdk/channel-core";
|
|
2
|
-
import { ChannelGroupEntrySchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, ReplyRuntimeConfigSchemaShape, buildChannelConfigSchema, buildMultiAccountChannelSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
|
|
2
|
+
import { ChannelGroupEntrySchema, DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, ReplyRuntimeConfigSchemaShape, ReplyToModeSchema, buildChannelConfigSchema, buildMultiAccountChannelSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region extensions/irc/src/config-ui-hints.ts
|
|
5
5
|
const ircChannelConfigUiHints = {
|
|
@@ -60,6 +60,7 @@ const IrcChannelConfigSchema = buildChannelConfigSchema(buildMultiAccountChannel
|
|
|
60
60
|
name: z.string().optional(),
|
|
61
61
|
enabled: z.boolean().optional(),
|
|
62
62
|
configWrites: z.boolean().optional(),
|
|
63
|
+
replyToMode: ReplyToModeSchema.optional(),
|
|
63
64
|
dangerouslyAllowNameMatching: z.boolean().optional(),
|
|
64
65
|
host: z.string().optional(),
|
|
65
66
|
port: z.number().int().min(1).max(65535).optional(),
|
package/openclaw.plugin.json
CHANGED
|
@@ -29,6 +29,26 @@
|
|
|
29
29
|
"configWrites": {
|
|
30
30
|
"type": "boolean"
|
|
31
31
|
},
|
|
32
|
+
"replyToMode": {
|
|
33
|
+
"anyOf": [
|
|
34
|
+
{
|
|
35
|
+
"type": "string",
|
|
36
|
+
"const": "off"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"type": "string",
|
|
40
|
+
"const": "first"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"type": "string",
|
|
44
|
+
"const": "all"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"type": "string",
|
|
48
|
+
"const": "batched"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
32
52
|
"dangerouslyAllowNameMatching": {
|
|
33
53
|
"type": "boolean"
|
|
34
54
|
},
|
|
@@ -355,6 +375,26 @@
|
|
|
355
375
|
"configWrites": {
|
|
356
376
|
"type": "boolean"
|
|
357
377
|
},
|
|
378
|
+
"replyToMode": {
|
|
379
|
+
"anyOf": [
|
|
380
|
+
{
|
|
381
|
+
"type": "string",
|
|
382
|
+
"const": "off"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"type": "string",
|
|
386
|
+
"const": "first"
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
"type": "string",
|
|
390
|
+
"const": "all"
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"type": "string",
|
|
394
|
+
"const": "batched"
|
|
395
|
+
}
|
|
396
|
+
]
|
|
397
|
+
},
|
|
358
398
|
"dangerouslyAllowNameMatching": {
|
|
359
399
|
"type": "boolean"
|
|
360
400
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/irc",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.9.1-beta.1",
|
|
4
4
|
"description": "OpenClaw IRC channel plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"openclaw": {
|
|
@@ -121,10 +121,10 @@
|
|
|
121
121
|
}
|
|
122
122
|
},
|
|
123
123
|
"compat": {
|
|
124
|
-
"pluginApi": ">=2026.
|
|
124
|
+
"pluginApi": ">=2026.9.1-beta.1"
|
|
125
125
|
},
|
|
126
126
|
"build": {
|
|
127
|
-
"openclawVersion": "2026.
|
|
127
|
+
"openclawVersion": "2026.9.1-beta.1",
|
|
128
128
|
"bundledDist": false
|
|
129
129
|
},
|
|
130
130
|
"release": {
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
"README.md"
|
|
150
150
|
],
|
|
151
151
|
"peerDependencies": {
|
|
152
|
-
"openclaw": ">=2026.
|
|
152
|
+
"openclaw": ">=2026.9.1-beta.1"
|
|
153
153
|
},
|
|
154
154
|
"peerDependenciesMeta": {
|
|
155
155
|
"openclaw": {
|