@cuylabs/channel-slack 0.5.0 → 0.6.0
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/README.md +25 -136
- package/dist/app-home.d.ts +23 -0
- package/dist/app-home.js +35 -0
- package/dist/artifacts/index.d.ts +135 -0
- package/dist/artifacts/index.js +299 -0
- package/dist/{assistant.d.ts → assistant/index.d.ts} +1 -1
- package/dist/{assistant.js → assistant/index.js} +2 -2
- package/dist/auth/index.d.ts +56 -0
- package/dist/auth/index.js +168 -0
- package/dist/{chunk-IDVDMJ5U.js → chunk-6JSGIVQH.js} +110 -3
- package/dist/chunk-6WHFQUYQ.js +54 -0
- package/dist/{bolt.js → chunk-73QXT7MA.js} +29 -322
- package/dist/{chunk-CMR6B76C.js → chunk-DNVSH7H5.js} +407 -1
- package/dist/chunk-QJYCHWN6.js +76 -0
- package/dist/chunk-S3SWPYXJ.js +81 -0
- package/dist/{chunk-JZG4IETE.js → chunk-X4WBBBYM.js} +0 -52
- package/dist/core.js +5 -3
- package/dist/diagnostics/index.d.ts +71 -0
- package/dist/{diagnostics.js → diagnostics/index.js} +5 -1
- package/dist/entrypoints/index.d.ts +120 -0
- package/dist/entrypoints/index.js +132 -0
- package/dist/{history.d.ts → history/index.d.ts} +2 -2
- package/dist/{history.js → history/index.js} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +28 -15
- package/dist/{policy.d.ts → policy/index.d.ts} +103 -2
- package/dist/{policy.js → policy/index.js} +13 -1
- package/dist/runtime-BNBHOZSQ.d.ts +53 -0
- package/dist/{setup.d.ts → setup/index.d.ts} +30 -3
- package/dist/{setup.js → setup/index.js} +134 -3
- package/dist/transports/http/index.d.ts +68 -0
- package/dist/transports/http/index.js +8 -0
- package/dist/transports/index.d.ts +8 -0
- package/dist/transports/index.js +24 -0
- package/dist/transports/socket/index.d.ts +94 -0
- package/dist/transports/socket/index.js +19 -0
- package/dist/types-B9NfCVrk.d.ts +141 -0
- package/docs/README.md +31 -0
- package/docs/concepts/activity.md +3 -3
- package/docs/concepts/artifacts.md +56 -0
- package/docs/concepts/entrypoints.md +73 -0
- package/docs/concepts/message-policy.md +13 -0
- package/docs/concepts/setup-requirements.md +23 -0
- package/docs/concepts/{bolt-runtime.md → transport-runtime.md} +14 -4
- package/docs/recipes/app-mention-handler.md +5 -0
- package/docs/recipes/generate-slack-manifest.md +16 -0
- package/docs/recipes/publish-artifact.md +45 -0
- package/docs/recipes/slash-command-and-shortcut.md +51 -0
- package/docs/recipes/socket-mode-app.md +1 -1
- package/docs/reference/channel-slack-boundary.md +10 -6
- package/docs/reference/exports.md +7 -2
- package/docs/reference/source-layout.md +35 -0
- package/package.json +63 -39
- package/dist/bolt.d.ts +0 -364
- package/dist/chunk-NE57BLLU.js +0 -0
- package/dist/diagnostics.d.ts +0 -22
- package/dist/shared.d.ts +0 -2
- package/dist/shared.js +0 -43
- /package/dist/{feedback.d.ts → feedback/index.d.ts} +0 -0
- /package/dist/{feedback.js → feedback/index.js} +0 -0
- /package/dist/{targets.d.ts → targets/index.d.ts} +0 -0
- /package/dist/{targets.js → targets/index.js} +0 -0
- /package/dist/{users.d.ts → users/index.d.ts} +0 -0
- /package/dist/{users.js → users/index.js} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
extractSlackActionToken
|
|
3
|
-
} from "
|
|
3
|
+
} from "../chunk-TWJGVDA2.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveSlackChannelType,
|
|
6
6
|
stripLeadingMentions
|
|
7
|
-
} from "
|
|
7
|
+
} from "../chunk-FPCE5V5Y.js";
|
|
8
8
|
|
|
9
9
|
// src/assistant/thread-context-store.ts
|
|
10
10
|
var ASSISTANT_THREAD_CONTEXT_EVENT_TYPE = "assistant_thread_context";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { e as SlackDirectAuthOptions, i as SlackOAuthAuthOptions, c as SlackCustomAuthorizeAuthOptions, h as SlackInstallationStore, f as SlackInstallation, g as SlackInstallationQuery, S as SlackAuthorizeFn } from '../types-B9NfCVrk.js';
|
|
2
|
+
export { a as SlackAuthorizeResult, b as SlackAuthorizeSource, d as SlackDirectAuthMode, j as SlackOAuthCallbackOptions, k as SlackOAuthError, l as SlackOAuthInstallPathOptions, m as SlackSingleWorkspaceAuthOptions, n as SlackStateStore } from '../types-B9NfCVrk.js';
|
|
3
|
+
import 'node:http';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Auth-mode resolver for direct Slack Bolt helpers.
|
|
7
|
+
*
|
|
8
|
+
* Picks between single-workspace, OAuth, and custom-authorize modes from the
|
|
9
|
+
* caller's auth options, validating env-var fallbacks and
|
|
10
|
+
* returning a normalized shape consumable by the Bolt `ExpressReceiver` /
|
|
11
|
+
* `App` constructors.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface ResolveDirectAuthOptions {
|
|
15
|
+
auth?: SlackDirectAuthOptions;
|
|
16
|
+
botToken?: string;
|
|
17
|
+
}
|
|
18
|
+
type ResolvedSingleWorkspaceAuth = {
|
|
19
|
+
mode: "single-workspace";
|
|
20
|
+
botToken: string;
|
|
21
|
+
botId?: string;
|
|
22
|
+
botUserId?: string;
|
|
23
|
+
};
|
|
24
|
+
type ResolvedOAuthAuth = SlackOAuthAuthOptions & {
|
|
25
|
+
mode: "oauth";
|
|
26
|
+
clientId: string;
|
|
27
|
+
clientSecret: string;
|
|
28
|
+
};
|
|
29
|
+
type ResolvedDirectAuth = ResolvedSingleWorkspaceAuth | ResolvedOAuthAuth | SlackCustomAuthorizeAuthOptions;
|
|
30
|
+
declare function resolveDirectAuth(options: ResolveDirectAuthOptions): ResolvedDirectAuth;
|
|
31
|
+
declare function normalizeSlackEventsPath(path: string): string;
|
|
32
|
+
declare function trimToUndefined(value: string | undefined): string | undefined;
|
|
33
|
+
|
|
34
|
+
declare class InMemorySlackInstallationStore implements SlackInstallationStore {
|
|
35
|
+
private devDB;
|
|
36
|
+
storeInstallation(installation: SlackInstallation): Promise<void>;
|
|
37
|
+
fetchInstallation(query: SlackInstallationQuery): Promise<SlackInstallation>;
|
|
38
|
+
deleteInstallation(query: SlackInstallationQuery): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
declare function createInMemorySlackInstallationStore(): SlackInstallationStore;
|
|
41
|
+
declare class JsonFileSlackInstallationStore implements SlackInstallationStore {
|
|
42
|
+
private readonly filePath;
|
|
43
|
+
private pending;
|
|
44
|
+
constructor(filePath: string);
|
|
45
|
+
storeInstallation(installation: SlackInstallation): Promise<void>;
|
|
46
|
+
fetchInstallation(query: SlackInstallationQuery): Promise<SlackInstallation>;
|
|
47
|
+
deleteInstallation(query: SlackInstallationQuery): Promise<void>;
|
|
48
|
+
private withLock;
|
|
49
|
+
private readInstallations;
|
|
50
|
+
private writeInstallations;
|
|
51
|
+
}
|
|
52
|
+
declare function createJsonFileSlackInstallationStore(filePath: string): SlackInstallationStore;
|
|
53
|
+
declare function createSlackInstallationStoreAuthorize(store: SlackInstallationStore): SlackAuthorizeFn;
|
|
54
|
+
declare function getSlackInstallationKey(input: SlackInstallation | SlackInstallationQuery): string;
|
|
55
|
+
|
|
56
|
+
export { InMemorySlackInstallationStore, JsonFileSlackInstallationStore, type ResolveDirectAuthOptions, type ResolvedDirectAuth, type ResolvedOAuthAuth, type ResolvedSingleWorkspaceAuth, SlackAuthorizeFn, SlackCustomAuthorizeAuthOptions, SlackDirectAuthOptions, SlackInstallation, SlackInstallationQuery, SlackInstallationStore, SlackOAuthAuthOptions, createInMemorySlackInstallationStore, createJsonFileSlackInstallationStore, createSlackInstallationStoreAuthorize, getSlackInstallationKey, normalizeSlackEventsPath, resolveDirectAuth, trimToUndefined };
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeSlackEventsPath,
|
|
3
|
+
resolveDirectAuth,
|
|
4
|
+
trimToUndefined
|
|
5
|
+
} from "../chunk-S3SWPYXJ.js";
|
|
6
|
+
|
|
7
|
+
// src/auth/installation-store.ts
|
|
8
|
+
import fs from "fs/promises";
|
|
9
|
+
import { dirname } from "path";
|
|
10
|
+
var InMemorySlackInstallationStore = class {
|
|
11
|
+
devDB = {};
|
|
12
|
+
async storeInstallation(installation) {
|
|
13
|
+
const key = getSlackInstallationKey(installation);
|
|
14
|
+
this.devDB[key] = installation;
|
|
15
|
+
}
|
|
16
|
+
async fetchInstallation(query) {
|
|
17
|
+
const key = getSlackInstallationKey(query);
|
|
18
|
+
const installation = this.devDB[key];
|
|
19
|
+
if (!installation) {
|
|
20
|
+
throw new Error(`No Slack installation found for key: ${key}`);
|
|
21
|
+
}
|
|
22
|
+
return installation;
|
|
23
|
+
}
|
|
24
|
+
async deleteInstallation(query) {
|
|
25
|
+
const key = getSlackInstallationKey(query);
|
|
26
|
+
delete this.devDB[key];
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
function createInMemorySlackInstallationStore() {
|
|
30
|
+
return new InMemorySlackInstallationStore();
|
|
31
|
+
}
|
|
32
|
+
var JsonFileSlackInstallationStore = class {
|
|
33
|
+
constructor(filePath) {
|
|
34
|
+
this.filePath = filePath;
|
|
35
|
+
}
|
|
36
|
+
filePath;
|
|
37
|
+
pending = Promise.resolve();
|
|
38
|
+
async storeInstallation(installation) {
|
|
39
|
+
await this.withLock(async () => {
|
|
40
|
+
const installations = await this.readInstallations();
|
|
41
|
+
installations[getSlackInstallationKey(installation)] = installation;
|
|
42
|
+
await this.writeInstallations(installations);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async fetchInstallation(query) {
|
|
46
|
+
return await this.withLock(async () => {
|
|
47
|
+
const key = getSlackInstallationKey(query);
|
|
48
|
+
const installations = await this.readInstallations();
|
|
49
|
+
const installation = installations[key];
|
|
50
|
+
if (!installation) {
|
|
51
|
+
throw new Error(`No Slack installation found for key: ${key}`);
|
|
52
|
+
}
|
|
53
|
+
return installation;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async deleteInstallation(query) {
|
|
57
|
+
await this.withLock(async () => {
|
|
58
|
+
const key = getSlackInstallationKey(query);
|
|
59
|
+
const installations = await this.readInstallations();
|
|
60
|
+
delete installations[key];
|
|
61
|
+
await this.writeInstallations(installations);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async withLock(operation) {
|
|
65
|
+
const run = this.pending.then(operation, operation);
|
|
66
|
+
this.pending = run.then(
|
|
67
|
+
() => void 0,
|
|
68
|
+
() => void 0
|
|
69
|
+
);
|
|
70
|
+
return await run;
|
|
71
|
+
}
|
|
72
|
+
async readInstallations() {
|
|
73
|
+
let contents;
|
|
74
|
+
try {
|
|
75
|
+
contents = await fs.readFile(this.filePath, "utf8");
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (isNodeErrorCode(error, "ENOENT")) {
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
if (!contents.trim()) {
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
const parsed = JSON.parse(contents);
|
|
86
|
+
const record = asRecord(parsed);
|
|
87
|
+
const installations = asRecord(record?.installations) ?? record;
|
|
88
|
+
if (!installations) {
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
const output = {};
|
|
92
|
+
for (const [key, value] of Object.entries(installations)) {
|
|
93
|
+
if (asRecord(value)) {
|
|
94
|
+
output[key] = value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return output;
|
|
98
|
+
}
|
|
99
|
+
async writeInstallations(installations) {
|
|
100
|
+
await fs.mkdir(dirname(this.filePath), { recursive: true });
|
|
101
|
+
const tempPath = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
102
|
+
const payload = JSON.stringify({ version: 1, installations }, null, 2);
|
|
103
|
+
await fs.writeFile(tempPath, `${payload}
|
|
104
|
+
`, "utf8");
|
|
105
|
+
await fs.rename(tempPath, this.filePath);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
function createJsonFileSlackInstallationStore(filePath) {
|
|
109
|
+
return new JsonFileSlackInstallationStore(filePath);
|
|
110
|
+
}
|
|
111
|
+
function createSlackInstallationStoreAuthorize(store) {
|
|
112
|
+
return async (source) => {
|
|
113
|
+
const installation = await store.fetchInstallation({
|
|
114
|
+
teamId: source.teamId,
|
|
115
|
+
enterpriseId: source.enterpriseId,
|
|
116
|
+
userId: source.userId,
|
|
117
|
+
conversationId: source.conversationId,
|
|
118
|
+
isEnterpriseInstall: source.isEnterpriseInstall
|
|
119
|
+
});
|
|
120
|
+
const botToken = installation.bot?.token;
|
|
121
|
+
if (!botToken) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
"Slack OAuth installation is missing a bot token. Reinstall the Slack app with bot scopes."
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
botToken,
|
|
128
|
+
botId: installation.bot?.id,
|
|
129
|
+
botUserId: installation.bot?.userId,
|
|
130
|
+
teamId: installation.team?.id ?? source.teamId,
|
|
131
|
+
enterpriseId: installation.enterprise?.id ?? source.enterpriseId,
|
|
132
|
+
userId: installation.user.id,
|
|
133
|
+
userToken: installation.user.token
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function getSlackInstallationKey(input) {
|
|
138
|
+
if (input.isEnterpriseInstall && "enterprise" in input && input.enterprise?.id) {
|
|
139
|
+
return `enterprise:${input.enterprise.id}`;
|
|
140
|
+
}
|
|
141
|
+
if (input.isEnterpriseInstall && "enterpriseId" in input && input.enterpriseId) {
|
|
142
|
+
return `enterprise:${input.enterpriseId}`;
|
|
143
|
+
}
|
|
144
|
+
if ("team" in input && input.team?.id) {
|
|
145
|
+
return `team:${input.team.id}`;
|
|
146
|
+
}
|
|
147
|
+
if ("teamId" in input && input.teamId) {
|
|
148
|
+
return `team:${input.teamId}`;
|
|
149
|
+
}
|
|
150
|
+
throw new Error("Slack installation is missing a team or enterprise id.");
|
|
151
|
+
}
|
|
152
|
+
function asRecord(value) {
|
|
153
|
+
return value && typeof value === "object" ? value : void 0;
|
|
154
|
+
}
|
|
155
|
+
function isNodeErrorCode(error, code) {
|
|
156
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
157
|
+
}
|
|
158
|
+
export {
|
|
159
|
+
InMemorySlackInstallationStore,
|
|
160
|
+
JsonFileSlackInstallationStore,
|
|
161
|
+
createInMemorySlackInstallationStore,
|
|
162
|
+
createJsonFileSlackInstallationStore,
|
|
163
|
+
createSlackInstallationStoreAuthorize,
|
|
164
|
+
getSlackInstallationKey,
|
|
165
|
+
normalizeSlackEventsPath,
|
|
166
|
+
resolveDirectAuth,
|
|
167
|
+
trimToUndefined
|
|
168
|
+
};
|
|
@@ -269,8 +269,8 @@ async function resolveDiagnosticsClient(options) {
|
|
|
269
269
|
return { tokenSource: "none" };
|
|
270
270
|
}
|
|
271
271
|
async function createSlackWebClient(token) {
|
|
272
|
-
const { WebClient } = await import("@slack/web-api");
|
|
273
|
-
return new
|
|
272
|
+
const { WebClient: WebClient2 } = await import("@slack/web-api");
|
|
273
|
+
return new WebClient2(token);
|
|
274
274
|
}
|
|
275
275
|
async function withRequestTimeout(operation, timeoutMs, method) {
|
|
276
276
|
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
@@ -651,6 +651,111 @@ function collectJsonKeyPathsInternal(value, {
|
|
|
651
651
|
return paths;
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
+
// src/diagnostics/readiness.ts
|
|
655
|
+
import { WebClient } from "@slack/web-api";
|
|
656
|
+
var SlackStartupReadinessError = class extends Error {
|
|
657
|
+
issues;
|
|
658
|
+
constructor(issues) {
|
|
659
|
+
super(`Slack startup readiness failed: ${issues.join("; ")}`);
|
|
660
|
+
this.name = "SlackStartupReadinessError";
|
|
661
|
+
this.issues = issues;
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
async function runSlackStartupReadiness({
|
|
665
|
+
clientFactory = createSlackAuthTestClient,
|
|
666
|
+
config,
|
|
667
|
+
logger
|
|
668
|
+
}) {
|
|
669
|
+
if (!config.enabled) {
|
|
670
|
+
logger?.debug?.("Slack startup readiness skipped", {
|
|
671
|
+
reason: "disabled"
|
|
672
|
+
});
|
|
673
|
+
return {
|
|
674
|
+
authMode: config.authMode,
|
|
675
|
+
identityCheck: "disabled",
|
|
676
|
+
transport: config.transport
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
const socketModeRuntimePolicy = config.transport === "socket" ? config.socketModeRuntimePolicy : void 0;
|
|
680
|
+
if (socketModeRuntimePolicy) {
|
|
681
|
+
logger?.info?.("Slack Socket Mode runtime policy selected", {
|
|
682
|
+
policy: socketModeRuntimePolicy
|
|
683
|
+
});
|
|
684
|
+
if (socketModeRuntimePolicy === "external-coordination") {
|
|
685
|
+
logger?.warn?.(
|
|
686
|
+
"Slack Socket Mode depends on external runtime coordination",
|
|
687
|
+
{ policy: socketModeRuntimePolicy }
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
if (config.authMode !== "single-workspace") {
|
|
692
|
+
logger?.info?.("Slack identity validation skipped", {
|
|
693
|
+
reason: "install-based-auth",
|
|
694
|
+
transport: config.transport
|
|
695
|
+
});
|
|
696
|
+
return {
|
|
697
|
+
authMode: config.authMode,
|
|
698
|
+
identityCheck: "skipped",
|
|
699
|
+
...socketModeRuntimePolicy ? { socketModeRuntimePolicy } : {},
|
|
700
|
+
transport: config.transport
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
if (!config.botToken?.trim()) {
|
|
704
|
+
throw new SlackStartupReadinessError([
|
|
705
|
+
"Slack single-workspace identity validation requires a bot token"
|
|
706
|
+
]);
|
|
707
|
+
}
|
|
708
|
+
const identity = await resolveSlackIdentity({
|
|
709
|
+
botToken: config.botToken,
|
|
710
|
+
clientFactory
|
|
711
|
+
});
|
|
712
|
+
const issues = formatSlackIdentityMismatches(
|
|
713
|
+
compareSlackIdentity(identity, config)
|
|
714
|
+
);
|
|
715
|
+
if (issues.length > 0) {
|
|
716
|
+
throw new SlackStartupReadinessError(issues);
|
|
717
|
+
}
|
|
718
|
+
logger?.info?.("Slack identity validation passed", {
|
|
719
|
+
botIdResolved: Boolean(identity.botId),
|
|
720
|
+
botUserIdResolved: Boolean(identity.botUserId),
|
|
721
|
+
expectedBotIdConfigured: Boolean(config.botId),
|
|
722
|
+
expectedBotUserIdConfigured: Boolean(config.botUserId),
|
|
723
|
+
expectedTeamIdConfigured: Boolean(config.teamId),
|
|
724
|
+
teamIdResolved: Boolean(identity.teamId),
|
|
725
|
+
teamNameResolved: Boolean(identity.team),
|
|
726
|
+
transport: config.transport
|
|
727
|
+
});
|
|
728
|
+
return {
|
|
729
|
+
authMode: config.authMode,
|
|
730
|
+
identity,
|
|
731
|
+
identityCheck: "passed",
|
|
732
|
+
...socketModeRuntimePolicy ? { socketModeRuntimePolicy } : {},
|
|
733
|
+
transport: config.transport
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
async function resolveSlackIdentity({
|
|
737
|
+
botToken,
|
|
738
|
+
clientFactory
|
|
739
|
+
}) {
|
|
740
|
+
try {
|
|
741
|
+
const response = await clientFactory(botToken).auth.test();
|
|
742
|
+
if (response.ok === false) {
|
|
743
|
+
throw new Error(response.error ?? "Slack auth.test returned ok=false");
|
|
744
|
+
}
|
|
745
|
+
return normalizeSlackAuthTestIdentity(response);
|
|
746
|
+
} catch (error) {
|
|
747
|
+
throw new SlackStartupReadinessError([
|
|
748
|
+
`Slack bot identity lookup failed: ${formatReadinessError(error)}`
|
|
749
|
+
]);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function createSlackAuthTestClient(botToken) {
|
|
753
|
+
return new WebClient(botToken);
|
|
754
|
+
}
|
|
755
|
+
function formatReadinessError(error) {
|
|
756
|
+
return error instanceof Error ? error.message : String(error);
|
|
757
|
+
}
|
|
758
|
+
|
|
654
759
|
export {
|
|
655
760
|
inspectSlackConnection,
|
|
656
761
|
inspectSlackTokenScopes,
|
|
@@ -662,5 +767,7 @@ export {
|
|
|
662
767
|
selectSlackBodyForDebug,
|
|
663
768
|
redactSlackDebugValue,
|
|
664
769
|
collectJsonKeyPaths,
|
|
665
|
-
findJsonKeyPaths
|
|
770
|
+
findJsonKeyPaths,
|
|
771
|
+
SlackStartupReadinessError,
|
|
772
|
+
runSlackStartupReadiness
|
|
666
773
|
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/shared/formatting.ts
|
|
2
|
+
function resolveSlackMessageFormatter(options) {
|
|
3
|
+
if (options.formatMessageText) {
|
|
4
|
+
return options.formatMessageText;
|
|
5
|
+
}
|
|
6
|
+
if (options.formatChatMarkdown === false) {
|
|
7
|
+
return identity;
|
|
8
|
+
}
|
|
9
|
+
return markdownToSlackMrkdwn;
|
|
10
|
+
}
|
|
11
|
+
function markdownToSlackMrkdwn(markdown) {
|
|
12
|
+
if (!markdown) {
|
|
13
|
+
return markdown;
|
|
14
|
+
}
|
|
15
|
+
const fences = [];
|
|
16
|
+
const withoutFences = markdown.replace(
|
|
17
|
+
/```[^\n`]*\n([\s\S]*?)```/g,
|
|
18
|
+
(_match, code) => {
|
|
19
|
+
const index = fences.push(`\`\`\`
|
|
20
|
+
${code}\`\`\``) - 1;
|
|
21
|
+
return `@@SLACK_FENCE_${index}@@`;
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
const boldSegments = [];
|
|
25
|
+
const withBoldPlaceholders = withoutFences.split("\n").map(formatMarkdownLine).join("\n").replace(/\[([^\]\n]+)\]\((https?:\/\/[^)\s]+)\)/g, "<$2|$1>").replace(/\*\*([^*\n]+)\*\*/g, (_match, text) => {
|
|
26
|
+
const index = boldSegments.push(`*${text}*`) - 1;
|
|
27
|
+
return `@@SLACK_BOLD_${index}@@`;
|
|
28
|
+
}).replace(/__([^_\n]+)__/g, (_match, text) => {
|
|
29
|
+
const index = boldSegments.push(`*${text}*`) - 1;
|
|
30
|
+
return `@@SLACK_BOLD_${index}@@`;
|
|
31
|
+
});
|
|
32
|
+
const converted = withBoldPlaceholders.replace(
|
|
33
|
+
/(^|[\s([{"'])\*([^*\n][^*\n]*?)\*(?=[\s.,;:!?)}\]'"]|$)/g,
|
|
34
|
+
"$1_$2_"
|
|
35
|
+
);
|
|
36
|
+
return converted.replace(/@@SLACK_BOLD_(\d+)@@/g, (_match, rawIndex) => {
|
|
37
|
+
const index = Number(rawIndex);
|
|
38
|
+
return boldSegments[index] ?? "";
|
|
39
|
+
}).replace(/@@SLACK_FENCE_(\d+)@@/g, (_match, rawIndex) => {
|
|
40
|
+
const index = Number(rawIndex);
|
|
41
|
+
return fences[index] ?? "";
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function formatMarkdownLine(line) {
|
|
45
|
+
return line.replace(/^(\s*)#{1,6}\s+(.+?)\s*#*\s*$/, "$1**$2**");
|
|
46
|
+
}
|
|
47
|
+
function identity(text) {
|
|
48
|
+
return text;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
resolveSlackMessageFormatter,
|
|
53
|
+
markdownToSlackMrkdwn
|
|
54
|
+
};
|