@openclaw/slack 2026.5.12-beta.7
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/account-inspect-D7AZNs8C.js +77 -0
- package/dist/account-inspect-api.js +10 -0
- package/dist/accounts-ClAPP5ry.js +139 -0
- package/dist/accounts.runtime-DDVcLJUI.js +2 -0
- package/dist/action-runtime-e2UhRsNx.js +350 -0
- package/dist/action-runtime.runtime-BFcqMbOm.js +2 -0
- package/dist/actions-CYLFK-Zy.js +292 -0
- package/dist/actions.runtime-CO3OaTLb.js +2 -0
- package/dist/allow-list-BPnnlRPL.js +82 -0
- package/dist/api.js +21 -0
- package/dist/approval-handler.runtime-CmeRr9qA.js +256 -0
- package/dist/blocks-input-CwTFVImV.js +29 -0
- package/dist/blocks-render-BIDw-Pom.js +161 -0
- package/dist/channel-DRjHBTDB.js +1020 -0
- package/dist/channel-api-B_nZwosg.js +20 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-entry.js +22 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.setup-Cayn7afd.js +73 -0
- package/dist/client-CPe4GmDR.js +103 -0
- package/dist/config-api-B_jq4NJW.js +2 -0
- package/dist/config-schema-D9B5LB_L.js +167 -0
- package/dist/configured-state.js +11 -0
- package/dist/contract-api.js +5 -0
- package/dist/directory-config-B3JiHeB7.js +54 -0
- package/dist/directory-contract-api.js +2 -0
- package/dist/directory-live-Bf16GwDh.js +133 -0
- package/dist/doctor-contract-KUjHnkQm.js +147 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/errors-BYFHR24f.js +109 -0
- package/dist/exec-approvals-7xUNgLi9.js +58 -0
- package/dist/group-policy-CyLUK6My.js +41 -0
- package/dist/http-routes-api.js +2 -0
- package/dist/inbound-contract-test-api.js +3 -0
- package/dist/index.js +33 -0
- package/dist/interactive-replies-api.js +2 -0
- package/dist/interactive-replies-qAIfuBor.js +173 -0
- package/dist/magic-string.es-BMaGRRZ1.js +1011 -0
- package/dist/media-D1XCd1uP.js +469 -0
- package/dist/message-tool-api-6lowf9zE.js +104 -0
- package/dist/message-tool-api.js +2 -0
- package/dist/monitor-a97o17G6.js +13 -0
- package/dist/mrkdwn-Cax-eSfK.js +6 -0
- package/dist/outbound-adapter-B_5sEhCg.js +174 -0
- package/dist/outbound-payload-test-api.js +2 -0
- package/dist/outbound-payload.test-harness-CVCamg1x.js +13558 -0
- package/dist/pipeline.runtime-DT0hLnq2.js +1379 -0
- package/dist/plugin-routes-DtTPmga1.js +20 -0
- package/dist/prepare-D3YqV8jB.js +1482 -0
- package/dist/prepare.test-helpers-DVcjRhfG.js +49 -0
- package/dist/probe-3eZf1FjI.js +42 -0
- package/dist/provider-D7uAN3Fq.js +3235 -0
- package/dist/registry-CeaoNfoP.js +39 -0
- package/dist/replies-Xe_jMR6o.js +139 -0
- package/dist/reply-blocks-Z5l6_R6H.js +14 -0
- package/dist/resolve-allowlist-common-Bk3clYPK.js +43 -0
- package/dist/resolve-channels-BRYqyNVJ.js +81 -0
- package/dist/resolve-users-Bd_SdP8j.js +113 -0
- package/dist/rolldown-runtime-CiIaOW0V.js +13 -0
- package/dist/room-context-0vovmZPU.js +787 -0
- package/dist/runtime-Bo-KHM-F.js +8 -0
- package/dist/runtime-api-Dd1xIV5v.js +9 -0
- package/dist/runtime-api.js +14 -0
- package/dist/runtime-setter-api.js +2 -0
- package/dist/scopes-CDevO8jg.js +74 -0
- package/dist/secret-contract-Bo6lbSkh.js +141 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/security-audit-BtHGnD3d.js +51 -0
- package/dist/security-contract-api.js +2 -0
- package/dist/send-D_A9kL-C.js +721 -0
- package/dist/send.runtime-BRE_ncCU.js +2 -0
- package/dist/send.runtime-_l76lUuL.js +2 -0
- package/dist/setup-core-B9NetDkM.js +320 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-plugin-api.js +2 -0
- package/dist/setup-surface-D88QBVOW.js +128 -0
- package/dist/shared-D8U42xFL.js +208 -0
- package/dist/slash-commands.runtime-22kgyst2.js +19 -0
- package/dist/slash-dispatch.runtime-BJgT0jwV.js +32 -0
- package/dist/slash-plugin-commands.runtime-CF-n3MeP.js +2 -0
- package/dist/slash-skill-commands.runtime-BMs0VjTe.js +7 -0
- package/dist/streaming-compat-RkZgTmQ2.js +43 -0
- package/dist/target-parsing-CQmv-iSm.js +55 -0
- package/dist/targets-B1tYCAr6.js +2 -0
- package/dist/test-api.js +8 -0
- package/dist/thread-ts-C2x7c5PP.js +24 -0
- package/openclaw.plugin.json +2405 -0
- package/package.json +84 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode } from "./streaming-compat-RkZgTmQ2.js";
|
|
2
|
+
import { asObjectRecord, hasLegacyAccountStreamingAliases, hasLegacyStreamingAliases, normalizeLegacyChannelAliases } from "openclaw/plugin-sdk/runtime-doctor";
|
|
3
|
+
//#region extensions/slack/src/doctor-contract.ts
|
|
4
|
+
function hasLegacySlackStreamingAliases(value) {
|
|
5
|
+
return hasLegacyStreamingAliases(value, { includeNativeTransport: true });
|
|
6
|
+
}
|
|
7
|
+
function hasLegacySlackChannelAllowAlias(value) {
|
|
8
|
+
const channels = asObjectRecord(asObjectRecord(value)?.channels);
|
|
9
|
+
if (!channels) return false;
|
|
10
|
+
return Object.values(channels).some((channel) => Object.prototype.hasOwnProperty.call(asObjectRecord(channel) ?? {}, "allow"));
|
|
11
|
+
}
|
|
12
|
+
function normalizeSlackChannelAllowAliases(params) {
|
|
13
|
+
let changed = false;
|
|
14
|
+
const nextChannels = { ...params.channels };
|
|
15
|
+
for (const [channelId, channelValue] of Object.entries(params.channels)) {
|
|
16
|
+
const channel = asObjectRecord(channelValue);
|
|
17
|
+
if (!channel || !Object.prototype.hasOwnProperty.call(channel, "allow")) continue;
|
|
18
|
+
const nextChannel = { ...channel };
|
|
19
|
+
if (nextChannel.enabled === void 0) {
|
|
20
|
+
nextChannel.enabled = channel.allow;
|
|
21
|
+
params.changes.push(`Moved ${params.pathPrefix}.${channelId}.allow → ${params.pathPrefix}.${channelId}.enabled.`);
|
|
22
|
+
} else params.changes.push(`Removed ${params.pathPrefix}.${channelId}.allow (${params.pathPrefix}.${channelId}.enabled already set).`);
|
|
23
|
+
delete nextChannel.allow;
|
|
24
|
+
nextChannels[channelId] = nextChannel;
|
|
25
|
+
changed = true;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
channels: nextChannels,
|
|
29
|
+
changed
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const legacyConfigRules = [
|
|
33
|
+
{
|
|
34
|
+
path: ["channels", "slack"],
|
|
35
|
+
message: "channels.slack.streamMode, channels.slack.streaming (scalar), chunkMode, blockStreaming, blockStreamingCoalesce, and nativeStreaming are legacy; use channels.slack.streaming.{mode,chunkMode,block.enabled,block.coalesce,nativeTransport}.",
|
|
36
|
+
match: hasLegacySlackStreamingAliases
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
path: [
|
|
40
|
+
"channels",
|
|
41
|
+
"slack",
|
|
42
|
+
"accounts"
|
|
43
|
+
],
|
|
44
|
+
message: "channels.slack.accounts.<id>.streamMode, streaming (scalar), chunkMode, blockStreaming, blockStreamingCoalesce, and nativeStreaming are legacy; use channels.slack.accounts.<id>.streaming.{mode,chunkMode,block.enabled,block.coalesce,nativeTransport}.",
|
|
45
|
+
match: (value) => hasLegacyAccountStreamingAliases(value, hasLegacySlackStreamingAliases)
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
path: ["channels", "slack"],
|
|
49
|
+
message: "channels.slack.channels.<id>.allow is legacy; use channels.slack.channels.<id>.enabled instead. Run \"openclaw doctor --fix\".",
|
|
50
|
+
match: hasLegacySlackChannelAllowAlias
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
path: [
|
|
54
|
+
"channels",
|
|
55
|
+
"slack",
|
|
56
|
+
"accounts"
|
|
57
|
+
],
|
|
58
|
+
message: "channels.slack.accounts.<id>.channels.<id>.allow is legacy; use channels.slack.accounts.<id>.channels.<id>.enabled instead. Run \"openclaw doctor --fix\".",
|
|
59
|
+
match: (value) => {
|
|
60
|
+
const accounts = asObjectRecord(value);
|
|
61
|
+
if (!accounts) return false;
|
|
62
|
+
return Object.values(accounts).some((account) => hasLegacySlackChannelAllowAlias(account));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
function normalizeCompatibilityConfig({ cfg }) {
|
|
67
|
+
const rawEntry = asObjectRecord(cfg.channels?.slack);
|
|
68
|
+
if (!rawEntry) return {
|
|
69
|
+
config: cfg,
|
|
70
|
+
changes: []
|
|
71
|
+
};
|
|
72
|
+
const changes = [];
|
|
73
|
+
let updated = rawEntry;
|
|
74
|
+
let changed = false;
|
|
75
|
+
const aliases = normalizeLegacyChannelAliases({
|
|
76
|
+
entry: rawEntry,
|
|
77
|
+
pathPrefix: "channels.slack",
|
|
78
|
+
changes,
|
|
79
|
+
normalizeDm: true,
|
|
80
|
+
normalizeAccountDm: true,
|
|
81
|
+
resolveStreamingOptions: (entry) => ({
|
|
82
|
+
resolvedMode: resolveSlackStreamingMode(entry),
|
|
83
|
+
resolvedNativeTransport: resolveSlackNativeStreaming(entry)
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
updated = aliases.entry;
|
|
87
|
+
changed = aliases.changed;
|
|
88
|
+
const channels = asObjectRecord(updated.channels);
|
|
89
|
+
if (channels) {
|
|
90
|
+
const normalized = normalizeSlackChannelAllowAliases({
|
|
91
|
+
channels,
|
|
92
|
+
pathPrefix: "channels.slack.channels",
|
|
93
|
+
changes
|
|
94
|
+
});
|
|
95
|
+
if (normalized.changed) {
|
|
96
|
+
updated = {
|
|
97
|
+
...updated,
|
|
98
|
+
channels: normalized.channels
|
|
99
|
+
};
|
|
100
|
+
changed = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const accounts = asObjectRecord(updated.accounts);
|
|
104
|
+
if (accounts) {
|
|
105
|
+
let accountsChanged = false;
|
|
106
|
+
const nextAccounts = { ...accounts };
|
|
107
|
+
for (const [accountId, accountValue] of Object.entries(accounts)) {
|
|
108
|
+
const account = asObjectRecord(accountValue);
|
|
109
|
+
const channelEntries = asObjectRecord(account?.channels);
|
|
110
|
+
if (!account || !channelEntries) continue;
|
|
111
|
+
const normalized = normalizeSlackChannelAllowAliases({
|
|
112
|
+
channels: channelEntries,
|
|
113
|
+
pathPrefix: `channels.slack.accounts.${accountId}.channels`,
|
|
114
|
+
changes
|
|
115
|
+
});
|
|
116
|
+
if (!normalized.changed) continue;
|
|
117
|
+
nextAccounts[accountId] = {
|
|
118
|
+
...account,
|
|
119
|
+
channels: normalized.channels
|
|
120
|
+
};
|
|
121
|
+
accountsChanged = true;
|
|
122
|
+
}
|
|
123
|
+
if (accountsChanged) {
|
|
124
|
+
updated = {
|
|
125
|
+
...updated,
|
|
126
|
+
accounts: nextAccounts
|
|
127
|
+
};
|
|
128
|
+
changed = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (!changed) return {
|
|
132
|
+
config: cfg,
|
|
133
|
+
changes: []
|
|
134
|
+
};
|
|
135
|
+
return {
|
|
136
|
+
config: {
|
|
137
|
+
...cfg,
|
|
138
|
+
channels: {
|
|
139
|
+
...cfg.channels,
|
|
140
|
+
slack: updated
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
changes
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { normalizeCompatibilityConfig as n, legacyConfigRules as t };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { redactSensitiveText } from "openclaw/plugin-sdk/logging-core";
|
|
2
|
+
//#region extensions/slack/src/errors.ts
|
|
3
|
+
const NO_ERROR_DETAIL = "no error detail";
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function redact(value) {
|
|
8
|
+
return redactSensitiveText(value);
|
|
9
|
+
}
|
|
10
|
+
function addStringDetail(details, label, value) {
|
|
11
|
+
if (typeof value !== "string") return;
|
|
12
|
+
const trimmed = redact(value.trim());
|
|
13
|
+
if (trimmed) details.push(label ? `${label}: ${trimmed}` : trimmed);
|
|
14
|
+
}
|
|
15
|
+
function addScalarDetail(details, label, value) {
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
addStringDetail(details, label, value);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === "number" || typeof value === "boolean") details.push(`${label}: ${String(value)}`);
|
|
21
|
+
}
|
|
22
|
+
function addStringListDetail(details, label, value) {
|
|
23
|
+
if (!Array.isArray(value)) return;
|
|
24
|
+
const entries = value.flatMap((entry) => {
|
|
25
|
+
if (typeof entry !== "string") return [];
|
|
26
|
+
const trimmed = redact(entry.trim());
|
|
27
|
+
return trimmed ? [trimmed] : [];
|
|
28
|
+
});
|
|
29
|
+
if (entries.length) details.push(`${label}: ${entries.join(", ")}`);
|
|
30
|
+
}
|
|
31
|
+
function safeStringify(value) {
|
|
32
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
33
|
+
try {
|
|
34
|
+
const result = JSON.stringify(value, (_key, nested) => {
|
|
35
|
+
if (typeof nested !== "object" || nested === null) return nested;
|
|
36
|
+
if (seen.has(nested)) return "[Circular]";
|
|
37
|
+
seen.add(nested);
|
|
38
|
+
return nested;
|
|
39
|
+
});
|
|
40
|
+
return result ? redact(result) : void 0;
|
|
41
|
+
} catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function addSlackResponseMetadata(details, value) {
|
|
46
|
+
if (!isRecord(value)) return;
|
|
47
|
+
addStringListDetail(details, "scopes", value.scopes);
|
|
48
|
+
addStringListDetail(details, "accepted", value.acceptedScopes);
|
|
49
|
+
const messages = value.messages;
|
|
50
|
+
if (Array.isArray(messages)) for (const message of messages) addStringDetail(details, "slack message", message);
|
|
51
|
+
const warnings = value.warnings;
|
|
52
|
+
if (Array.isArray(warnings)) for (const warning of warnings) addStringDetail(details, "slack warning", warning);
|
|
53
|
+
}
|
|
54
|
+
function addSlackDataDetails(details, value) {
|
|
55
|
+
if (!isRecord(value)) return;
|
|
56
|
+
addScalarDetail(details, "slack error", value.error);
|
|
57
|
+
addScalarDetail(details, "needed", value.needed);
|
|
58
|
+
addScalarDetail(details, "provided", value.provided);
|
|
59
|
+
addSlackResponseMetadata(details, value.response_metadata);
|
|
60
|
+
}
|
|
61
|
+
function addRecordDetails(details, value) {
|
|
62
|
+
addScalarDetail(details, "code", value.code);
|
|
63
|
+
addScalarDetail(details, "status", value.status);
|
|
64
|
+
addScalarDetail(details, "statusCode", value.statusCode);
|
|
65
|
+
addScalarDetail(details, "statusMessage", value.statusMessage);
|
|
66
|
+
addScalarDetail(details, "retryAfter", value.retryAfter);
|
|
67
|
+
addScalarDetail(details, "errno", value.errno);
|
|
68
|
+
addScalarDetail(details, "syscall", value.syscall);
|
|
69
|
+
addScalarDetail(details, "hostname", value.hostname);
|
|
70
|
+
addScalarDetail(details, "type", value.type);
|
|
71
|
+
addStringDetail(details, "statusText", value.statusText);
|
|
72
|
+
addStringDetail(details, "body", value.body);
|
|
73
|
+
addSlackDataDetails(details, value.data);
|
|
74
|
+
if (isRecord(value.response)) {
|
|
75
|
+
addScalarDetail(details, "response status", value.response.status);
|
|
76
|
+
addStringDetail(details, "response statusText", value.response.statusText);
|
|
77
|
+
addSlackDataDetails(details, value.response.data);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function collectSlackErrorDetails(error) {
|
|
81
|
+
const details = [];
|
|
82
|
+
if (error === void 0 || error === null) return details;
|
|
83
|
+
if (typeof error === "string") {
|
|
84
|
+
addStringDetail(details, "", error);
|
|
85
|
+
return details;
|
|
86
|
+
}
|
|
87
|
+
if (error instanceof Error) {
|
|
88
|
+
addStringDetail(details, "", error.message || error.name);
|
|
89
|
+
if (error.cause !== void 0) {
|
|
90
|
+
const cause = formatSlackError(error.cause, "");
|
|
91
|
+
if (cause) details.push(`cause: ${cause}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (isRecord(error)) {
|
|
95
|
+
addRecordDetails(details, error);
|
|
96
|
+
const fallback = safeStringify(error);
|
|
97
|
+
if (details.length === 0 && fallback && fallback !== "{}") details.push(fallback);
|
|
98
|
+
}
|
|
99
|
+
return details;
|
|
100
|
+
}
|
|
101
|
+
function formatSlackError(error, fallback = NO_ERROR_DETAIL) {
|
|
102
|
+
const details = collectSlackErrorDetails(error);
|
|
103
|
+
if (details.length > 0) return details.join("; ");
|
|
104
|
+
if (error === void 0 || error === null) return fallback;
|
|
105
|
+
if (typeof error === "string" && !error.trim()) return fallback;
|
|
106
|
+
return safeStringify(error) ?? fallback;
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { formatSlackError as t };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { a as resolveSlackAccount } from "./accounts-ClAPP5ry.js";
|
|
2
|
+
import { normalizeStringifiedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
import { doesApprovalRequestMatchChannelAccount } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
4
|
+
import { resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
5
|
+
import { createChannelExecApprovalProfile, isChannelExecApprovalTargetRecipient } from "openclaw/plugin-sdk/approval-client-runtime";
|
|
6
|
+
//#region extensions/slack/src/exec-approvals.ts
|
|
7
|
+
function normalizeSlackApproverId(value) {
|
|
8
|
+
const trimmed = normalizeStringifiedOptionalString(value);
|
|
9
|
+
if (!trimmed) return;
|
|
10
|
+
const prefixed = trimmed.match(/^(?:slack|user):([A-Z0-9]+)$/i);
|
|
11
|
+
if (prefixed?.[1]) return prefixed[1];
|
|
12
|
+
const mention = trimmed.match(/^<@([A-Z0-9]+)>$/i);
|
|
13
|
+
if (mention?.[1]) return mention[1];
|
|
14
|
+
return /^[UW][A-Z0-9]+$/i.test(trimmed) ? trimmed : void 0;
|
|
15
|
+
}
|
|
16
|
+
function resolveSlackOwnerApprovers(cfg) {
|
|
17
|
+
const ownerAllowFrom = cfg.commands?.ownerAllowFrom;
|
|
18
|
+
if (!Array.isArray(ownerAllowFrom) || ownerAllowFrom.length === 0) return [];
|
|
19
|
+
return resolveApprovalApprovers({
|
|
20
|
+
explicit: ownerAllowFrom,
|
|
21
|
+
normalizeApprover: normalizeSlackApproverId
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function getSlackExecApprovalApprovers(params) {
|
|
25
|
+
const account = resolveSlackAccount(params).config;
|
|
26
|
+
return resolveApprovalApprovers({
|
|
27
|
+
explicit: account.execApprovals?.approvers ?? resolveSlackOwnerApprovers(params.cfg),
|
|
28
|
+
normalizeApprover: normalizeSlackApproverId
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function isSlackExecApprovalTargetRecipient(params) {
|
|
32
|
+
return isChannelExecApprovalTargetRecipient({
|
|
33
|
+
...params,
|
|
34
|
+
channel: "slack",
|
|
35
|
+
normalizeSenderId: normalizeSlackApproverId,
|
|
36
|
+
matchTarget: ({ target, normalizedSenderId }) => normalizeSlackApproverId(target.to) === normalizedSenderId
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const slackExecApprovalProfile = createChannelExecApprovalProfile({
|
|
40
|
+
resolveConfig: (params) => resolveSlackAccount(params).config.execApprovals,
|
|
41
|
+
resolveApprovers: getSlackExecApprovalApprovers,
|
|
42
|
+
normalizeSenderId: normalizeSlackApproverId,
|
|
43
|
+
isTargetRecipient: isSlackExecApprovalTargetRecipient,
|
|
44
|
+
matchesRequestAccount: (params) => doesApprovalRequestMatchChannelAccount({
|
|
45
|
+
cfg: params.cfg,
|
|
46
|
+
request: params.request,
|
|
47
|
+
channel: "slack",
|
|
48
|
+
accountId: params.accountId
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
const isSlackExecApprovalClientEnabled = slackExecApprovalProfile.isClientEnabled;
|
|
52
|
+
const isSlackExecApprovalApprover = slackExecApprovalProfile.isApprover;
|
|
53
|
+
const isSlackExecApprovalAuthorizedSender = slackExecApprovalProfile.isAuthorizedSender;
|
|
54
|
+
const resolveSlackExecApprovalTarget = slackExecApprovalProfile.resolveTarget;
|
|
55
|
+
const shouldHandleSlackExecApprovalRequest = slackExecApprovalProfile.shouldHandleRequest;
|
|
56
|
+
const shouldSuppressLocalSlackExecApprovalPrompt = slackExecApprovalProfile.shouldSuppressLocalPrompt;
|
|
57
|
+
//#endregion
|
|
58
|
+
export { normalizeSlackApproverId as a, shouldSuppressLocalSlackExecApprovalPrompt as c, isSlackExecApprovalClientEnabled as i, isSlackExecApprovalApprover as n, resolveSlackExecApprovalTarget as o, isSlackExecApprovalAuthorizedSender as r, shouldHandleSlackExecApprovalRequest as s, getSlackExecApprovalApprovers as t };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { i as resolveDefaultSlackAccountId, r as mergeSlackAccountConfig } from "./accounts-ClAPP5ry.js";
|
|
2
|
+
import { normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
|
|
3
|
+
import { normalizeHyphenSlug } from "openclaw/plugin-sdk/string-normalization-runtime";
|
|
4
|
+
import { resolveToolsBySender } from "openclaw/plugin-sdk/channel-policy";
|
|
5
|
+
//#region extensions/slack/src/group-policy.ts
|
|
6
|
+
function resolveSlackChannelPolicyEntry(params) {
|
|
7
|
+
const accountId = normalizeAccountId(params.accountId ?? resolveDefaultSlackAccountId(params.cfg));
|
|
8
|
+
const channelMap = mergeSlackAccountConfig(params.cfg, accountId).channels ?? {};
|
|
9
|
+
if (Object.keys(channelMap).length === 0) return;
|
|
10
|
+
const channelId = params.groupId?.trim();
|
|
11
|
+
const channelName = params.groupChannel?.replace(/^#/, "");
|
|
12
|
+
const normalizedName = normalizeHyphenSlug(channelName);
|
|
13
|
+
const candidates = [
|
|
14
|
+
channelId ?? "",
|
|
15
|
+
channelName ? `#${channelName}` : "",
|
|
16
|
+
channelName ?? "",
|
|
17
|
+
normalizedName
|
|
18
|
+
].filter(Boolean);
|
|
19
|
+
for (const candidate of candidates) if (candidate && channelMap[candidate]) return channelMap[candidate];
|
|
20
|
+
return channelMap["*"];
|
|
21
|
+
}
|
|
22
|
+
function resolveSenderToolsEntry(entry, params) {
|
|
23
|
+
if (!entry) return;
|
|
24
|
+
return resolveToolsBySender({
|
|
25
|
+
toolsBySender: entry.toolsBySender,
|
|
26
|
+
senderId: params.senderId,
|
|
27
|
+
senderName: params.senderName,
|
|
28
|
+
senderUsername: params.senderUsername,
|
|
29
|
+
senderE164: params.senderE164
|
|
30
|
+
}) ?? entry.tools;
|
|
31
|
+
}
|
|
32
|
+
function resolveSlackGroupRequireMention(params) {
|
|
33
|
+
const resolved = resolveSlackChannelPolicyEntry(params);
|
|
34
|
+
if (typeof resolved?.requireMention === "boolean") return resolved.requireMention;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
function resolveSlackGroupToolPolicy(params) {
|
|
38
|
+
return resolveSenderToolsEntry(resolveSlackChannelPolicyEntry(params), params);
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { resolveSlackGroupToolPolicy as n, resolveSlackGroupRequireMention as t };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineBundledChannelEntry, loadBundledEntryExportSync } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
|
+
//#region extensions/slack/index.ts
|
|
3
|
+
function registerSlackPluginHttpRoutes(api) {
|
|
4
|
+
loadBundledEntryExportSync(import.meta.url, {
|
|
5
|
+
specifier: "./http-routes-api.js",
|
|
6
|
+
exportName: "registerSlackPluginHttpRoutes"
|
|
7
|
+
})(api);
|
|
8
|
+
}
|
|
9
|
+
var slack_default = defineBundledChannelEntry({
|
|
10
|
+
id: "slack",
|
|
11
|
+
name: "Slack",
|
|
12
|
+
description: "Slack channel plugin",
|
|
13
|
+
importMetaUrl: import.meta.url,
|
|
14
|
+
plugin: {
|
|
15
|
+
specifier: "./channel-plugin-api.js",
|
|
16
|
+
exportName: "slackPlugin"
|
|
17
|
+
},
|
|
18
|
+
secrets: {
|
|
19
|
+
specifier: "./secret-contract-api.js",
|
|
20
|
+
exportName: "channelSecrets"
|
|
21
|
+
},
|
|
22
|
+
runtime: {
|
|
23
|
+
specifier: "./runtime-setter-api.js",
|
|
24
|
+
exportName: "setSlackRuntime"
|
|
25
|
+
},
|
|
26
|
+
accountInspect: {
|
|
27
|
+
specifier: "./account-inspect-api.js",
|
|
28
|
+
exportName: "inspectSlackReadOnlyAccount"
|
|
29
|
+
},
|
|
30
|
+
registerFull: registerSlackPluginHttpRoutes
|
|
31
|
+
});
|
|
32
|
+
//#endregion
|
|
33
|
+
export { slack_default as default };
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId } from "./accounts-ClAPP5ry.js";
|
|
2
|
+
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
//#region extensions/slack/src/interactive-replies.ts
|
|
4
|
+
const SLACK_BUTTON_MAX_ITEMS = 5;
|
|
5
|
+
const SLACK_SELECT_MAX_ITEMS = 100;
|
|
6
|
+
const SLACK_DIRECTIVE_RE = /\[\[(slack_buttons|slack_select):\s*([^\]]+)\]\]/gi;
|
|
7
|
+
const SLACK_OPTIONS_LINE_RE = /^\s*Options:\s*(.+?)\s*\.?\s*$/i;
|
|
8
|
+
const SLACK_AUTO_SELECT_MAX_ITEMS = 12;
|
|
9
|
+
const SLACK_SIMPLE_OPTION_RE = /^[a-z0-9][a-z0-9 _+/-]{0,31}$/i;
|
|
10
|
+
function parseChoice(raw, options) {
|
|
11
|
+
const trimmed = raw.trim();
|
|
12
|
+
if (!trimmed) return null;
|
|
13
|
+
const delimiter = trimmed.indexOf(":");
|
|
14
|
+
if (delimiter === -1) return {
|
|
15
|
+
label: trimmed,
|
|
16
|
+
value: trimmed
|
|
17
|
+
};
|
|
18
|
+
const label = trimmed.slice(0, delimiter).trim();
|
|
19
|
+
let value = trimmed.slice(delimiter + 1).trim();
|
|
20
|
+
if (!label || !value) return null;
|
|
21
|
+
let style;
|
|
22
|
+
if (options?.allowStyle) {
|
|
23
|
+
const styleDelimiter = value.lastIndexOf(":");
|
|
24
|
+
if (styleDelimiter !== -1) {
|
|
25
|
+
const maybeStyle = normalizeLowercaseStringOrEmpty(value.slice(styleDelimiter + 1));
|
|
26
|
+
if (maybeStyle === "primary" || maybeStyle === "secondary" || maybeStyle === "success" || maybeStyle === "danger") {
|
|
27
|
+
const unstyledValue = value.slice(0, styleDelimiter).trim();
|
|
28
|
+
if (unstyledValue) {
|
|
29
|
+
value = unstyledValue;
|
|
30
|
+
style = maybeStyle;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return style ? {
|
|
36
|
+
label,
|
|
37
|
+
value,
|
|
38
|
+
style
|
|
39
|
+
} : {
|
|
40
|
+
label,
|
|
41
|
+
value
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function parseChoices(raw, maxItems, options) {
|
|
45
|
+
return raw.split(",").map((entry) => parseChoice(entry, options)).filter((entry) => Boolean(entry)).slice(0, maxItems);
|
|
46
|
+
}
|
|
47
|
+
function buildTextBlock(text) {
|
|
48
|
+
const trimmed = text.trim();
|
|
49
|
+
if (!trimmed) return null;
|
|
50
|
+
return {
|
|
51
|
+
type: "text",
|
|
52
|
+
text: trimmed
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function buildButtonsBlock(raw) {
|
|
56
|
+
const choices = parseChoices(raw, SLACK_BUTTON_MAX_ITEMS, { allowStyle: true });
|
|
57
|
+
if (choices.length === 0) return null;
|
|
58
|
+
return {
|
|
59
|
+
type: "buttons",
|
|
60
|
+
buttons: choices.map((choice) => Object.assign({
|
|
61
|
+
label: choice.label,
|
|
62
|
+
value: choice.value
|
|
63
|
+
}, choice.style ? { style: choice.style } : {}))
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function buildSelectBlock(raw) {
|
|
67
|
+
const parts = raw.split("|").map((entry) => entry.trim()).filter(Boolean);
|
|
68
|
+
if (parts.length === 0) return null;
|
|
69
|
+
const [first, second] = parts;
|
|
70
|
+
const placeholder = parts.length >= 2 ? first : "Choose an option";
|
|
71
|
+
const choices = parseChoices(parts.length >= 2 ? second : first, SLACK_SELECT_MAX_ITEMS);
|
|
72
|
+
if (choices.length === 0) return null;
|
|
73
|
+
return {
|
|
74
|
+
type: "select",
|
|
75
|
+
placeholder,
|
|
76
|
+
options: choices
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function hasSlackBlocks(payload) {
|
|
80
|
+
const blocks = (payload.channelData?.slack)?.blocks;
|
|
81
|
+
if (typeof blocks === "string") return blocks.trim().length > 0;
|
|
82
|
+
return Array.isArray(blocks) && blocks.length > 0;
|
|
83
|
+
}
|
|
84
|
+
function parseSimpleSlackOptions(raw) {
|
|
85
|
+
const entries = raw.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
86
|
+
if (entries.length < 2 || entries.length > SLACK_AUTO_SELECT_MAX_ITEMS) return null;
|
|
87
|
+
if (!entries.every((entry) => SLACK_SIMPLE_OPTION_RE.test(entry))) return null;
|
|
88
|
+
if (new Set(entries.map((entry) => normalizeLowercaseStringOrEmpty(entry))).size !== entries.length) return null;
|
|
89
|
+
return entries.map((entry) => ({
|
|
90
|
+
label: entry,
|
|
91
|
+
value: entry
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
function resolveInteractiveRepliesFromCapabilities(capabilities) {
|
|
95
|
+
if (!capabilities) return false;
|
|
96
|
+
if (Array.isArray(capabilities)) return capabilities.some((entry) => normalizeLowercaseStringOrEmpty(String(entry)) === "interactivereplies");
|
|
97
|
+
if (typeof capabilities === "object") return capabilities.interactiveReplies === true;
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
function isSlackInteractiveRepliesEnabled(params) {
|
|
101
|
+
return resolveInteractiveRepliesFromCapabilities(resolveSlackAccount({
|
|
102
|
+
cfg: params.cfg,
|
|
103
|
+
accountId: params.accountId ?? resolveDefaultSlackAccountId(params.cfg)
|
|
104
|
+
}).config.capabilities);
|
|
105
|
+
}
|
|
106
|
+
function compileSlackInteractiveReplies(payload) {
|
|
107
|
+
const text = payload.text;
|
|
108
|
+
if (!text) return payload;
|
|
109
|
+
const generatedBlocks = [];
|
|
110
|
+
const visibleTextParts = [];
|
|
111
|
+
let cursor = 0;
|
|
112
|
+
let matchedDirective = false;
|
|
113
|
+
let generatedInteractiveBlock = false;
|
|
114
|
+
SLACK_DIRECTIVE_RE.lastIndex = 0;
|
|
115
|
+
for (const match of text.matchAll(SLACK_DIRECTIVE_RE)) {
|
|
116
|
+
matchedDirective = true;
|
|
117
|
+
const matchText = match[0];
|
|
118
|
+
const directiveType = match[1];
|
|
119
|
+
const body = match[2];
|
|
120
|
+
const index = match.index ?? 0;
|
|
121
|
+
const precedingText = text.slice(cursor, index);
|
|
122
|
+
visibleTextParts.push(precedingText);
|
|
123
|
+
const section = buildTextBlock(precedingText);
|
|
124
|
+
if (section) generatedBlocks.push(section);
|
|
125
|
+
const block = normalizeLowercaseStringOrEmpty(directiveType) === "slack_buttons" ? buildButtonsBlock(body) : buildSelectBlock(body);
|
|
126
|
+
if (block) {
|
|
127
|
+
generatedInteractiveBlock = true;
|
|
128
|
+
generatedBlocks.push(block);
|
|
129
|
+
}
|
|
130
|
+
cursor = index + matchText.length;
|
|
131
|
+
}
|
|
132
|
+
const trailingText = text.slice(cursor);
|
|
133
|
+
visibleTextParts.push(trailingText);
|
|
134
|
+
const trailingSection = buildTextBlock(trailingText);
|
|
135
|
+
if (trailingSection) generatedBlocks.push(trailingSection);
|
|
136
|
+
const cleanedText = visibleTextParts.join("");
|
|
137
|
+
if (!matchedDirective || !generatedInteractiveBlock) return parseSlackOptionsLine(payload);
|
|
138
|
+
return {
|
|
139
|
+
...payload,
|
|
140
|
+
text: cleanedText.trim() || void 0,
|
|
141
|
+
interactive: { blocks: [...payload.interactive?.blocks ?? [], ...generatedBlocks] }
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function parseSlackOptionsLine(payload) {
|
|
145
|
+
const text = payload.text;
|
|
146
|
+
if (!text || payload.interactive?.blocks?.length || hasSlackBlocks(payload)) return payload;
|
|
147
|
+
const lines = text.split("\n");
|
|
148
|
+
const lastNonEmptyIndex = [...lines.keys()].toReversed().find((index) => lines[index]?.trim());
|
|
149
|
+
if (lastNonEmptyIndex == null) return payload;
|
|
150
|
+
const match = (lines[lastNonEmptyIndex] ?? "").match(SLACK_OPTIONS_LINE_RE);
|
|
151
|
+
if (!match) return payload;
|
|
152
|
+
const choices = parseSimpleSlackOptions(match[1] ?? "");
|
|
153
|
+
if (!choices) return payload;
|
|
154
|
+
const bodyText = lines.filter((_, index) => index !== lastNonEmptyIndex).join("\n").trim();
|
|
155
|
+
const generatedBlocks = [];
|
|
156
|
+
const bodyBlock = buildTextBlock(bodyText);
|
|
157
|
+
if (bodyBlock) generatedBlocks.push(bodyBlock);
|
|
158
|
+
generatedBlocks.push(choices.length <= SLACK_BUTTON_MAX_ITEMS ? {
|
|
159
|
+
type: "buttons",
|
|
160
|
+
buttons: choices
|
|
161
|
+
} : {
|
|
162
|
+
type: "select",
|
|
163
|
+
placeholder: "Choose an option",
|
|
164
|
+
options: choices
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
...payload,
|
|
168
|
+
text: bodyText || void 0,
|
|
169
|
+
interactive: { blocks: [...payload.interactive?.blocks ?? [], ...generatedBlocks] }
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
export { isSlackInteractiveRepliesEnabled as n, parseSlackOptionsLine as r, compileSlackInteractiveReplies as t };
|