@cuylabs/channel-slack 0.5.1 → 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.
Files changed (62) hide show
  1. package/README.md +25 -136
  2. package/dist/app-home.d.ts +23 -0
  3. package/dist/app-home.js +35 -0
  4. package/dist/artifacts/index.d.ts +135 -0
  5. package/dist/artifacts/index.js +299 -0
  6. package/dist/{assistant.d.ts → assistant/index.d.ts} +1 -1
  7. package/dist/{assistant.js → assistant/index.js} +2 -2
  8. package/dist/auth/index.d.ts +56 -0
  9. package/dist/auth/index.js +168 -0
  10. package/dist/{chunk-IDVDMJ5U.js → chunk-6JSGIVQH.js} +110 -3
  11. package/dist/chunk-6WHFQUYQ.js +54 -0
  12. package/dist/{bolt.js → chunk-73QXT7MA.js} +25 -320
  13. package/dist/{chunk-CMR6B76C.js → chunk-DNVSH7H5.js} +407 -1
  14. package/dist/chunk-QJYCHWN6.js +76 -0
  15. package/dist/chunk-S3SWPYXJ.js +81 -0
  16. package/dist/{chunk-JZG4IETE.js → chunk-X4WBBBYM.js} +0 -52
  17. package/dist/core.js +5 -3
  18. package/dist/diagnostics/index.d.ts +71 -0
  19. package/dist/{diagnostics.js → diagnostics/index.js} +5 -1
  20. package/dist/entrypoints/index.d.ts +120 -0
  21. package/dist/entrypoints/index.js +132 -0
  22. package/dist/{history.d.ts → history/index.d.ts} +2 -2
  23. package/dist/{history.js → history/index.js} +1 -1
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.js +28 -15
  26. package/dist/{policy.d.ts → policy/index.d.ts} +103 -2
  27. package/dist/{policy.js → policy/index.js} +13 -1
  28. package/dist/runtime-BNBHOZSQ.d.ts +53 -0
  29. package/dist/{setup.d.ts → setup/index.d.ts} +30 -3
  30. package/dist/{setup.js → setup/index.js} +134 -3
  31. package/dist/transports/http/index.d.ts +68 -0
  32. package/dist/transports/http/index.js +8 -0
  33. package/dist/transports/index.d.ts +8 -0
  34. package/dist/transports/index.js +24 -0
  35. package/dist/transports/socket/index.d.ts +94 -0
  36. package/dist/transports/socket/index.js +19 -0
  37. package/dist/types-B9NfCVrk.d.ts +141 -0
  38. package/docs/README.md +31 -0
  39. package/docs/concepts/activity.md +3 -3
  40. package/docs/concepts/artifacts.md +56 -0
  41. package/docs/concepts/entrypoints.md +73 -0
  42. package/docs/concepts/setup-requirements.md +23 -0
  43. package/docs/concepts/{bolt-runtime.md → transport-runtime.md} +9 -4
  44. package/docs/recipes/generate-slack-manifest.md +16 -0
  45. package/docs/recipes/publish-artifact.md +45 -0
  46. package/docs/recipes/slash-command-and-shortcut.md +51 -0
  47. package/docs/recipes/socket-mode-app.md +1 -1
  48. package/docs/reference/channel-slack-boundary.md +10 -6
  49. package/docs/reference/exports.md +7 -2
  50. package/docs/reference/source-layout.md +35 -0
  51. package/package.json +63 -39
  52. package/dist/bolt.d.ts +0 -364
  53. package/dist/chunk-NE57BLLU.js +0 -0
  54. package/dist/diagnostics.d.ts +0 -22
  55. package/dist/shared.d.ts +0 -2
  56. package/dist/shared.js +0 -43
  57. /package/dist/{feedback.d.ts → feedback/index.d.ts} +0 -0
  58. /package/dist/{feedback.js → feedback/index.js} +0 -0
  59. /package/dist/{targets.d.ts → targets/index.d.ts} +0 -0
  60. /package/dist/{targets.js → targets/index.js} +0 -0
  61. /package/dist/{users.d.ts → users/index.d.ts} +0 -0
  62. /package/dist/{users.js → users/index.js} +0 -0
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  extractSlackActionToken
3
- } from "./chunk-TWJGVDA2.js";
3
+ } from "../chunk-TWJGVDA2.js";
4
4
  import {
5
5
  resolveSlackChannelType,
6
6
  stripLeadingMentions
7
- } from "./chunk-FPCE5V5Y.js";
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 WebClient(token);
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
+ };