@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
package/README.md
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
# @cuylabs/channel-slack
|
|
2
2
|
|
|
3
|
-
Slack channel primitives
|
|
3
|
+
Agent-runtime-agnostic Slack channel primitives.
|
|
4
4
|
|
|
5
|
-
This package
|
|
6
|
-
formatting,
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
This package owns reusable Slack mechanics: event parsing, message admission,
|
|
6
|
+
history loading, formatting, setup inspection, auth helpers, entrypoint
|
|
7
|
+
normalization, artifact publishing, and HTTP or Socket Mode transport helpers.
|
|
8
|
+
It does not create or run an agent, and it does not depend on an agent SDK.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
[Channel Slack Boundary](docs/reference/channel-slack-boundary.md) for the
|
|
13
|
-
package boundary.
|
|
10
|
+
Runtime-specific adapters should compose these primitives with their own turn
|
|
11
|
+
types, event streams, tools, prompts, and deployment policy.
|
|
14
12
|
|
|
15
13
|
## Install
|
|
16
14
|
|
|
@@ -18,48 +16,18 @@ package boundary.
|
|
|
18
16
|
npm install @cuylabs/channel-slack
|
|
19
17
|
```
|
|
20
18
|
|
|
21
|
-
Slack SDK packages are optional peers. Install the peers for the
|
|
19
|
+
Slack SDK packages are optional peers. Install the peers for the features you
|
|
22
20
|
use:
|
|
23
21
|
|
|
24
22
|
```bash
|
|
25
23
|
npm install @slack/bolt @slack/web-api @slack/types express
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
Postgres-backed helpers lazy-load `pg` when you pass a connection string.
|
|
29
|
-
`pg`
|
|
30
|
-
or pool.
|
|
26
|
+
Postgres-backed helpers lazy-load `pg` when you pass a connection string.
|
|
27
|
+
Install `pg` only when your application uses those helpers without injecting its
|
|
28
|
+
own client or pool.
|
|
31
29
|
|
|
32
|
-
##
|
|
33
|
-
|
|
34
|
-
Prefer feature-specific imports so applications only couple to the Slack
|
|
35
|
-
surface they need.
|
|
36
|
-
|
|
37
|
-
| Import | Use for |
|
|
38
|
-
| ------------------------------------ | ---------------------------------------------------------------------------------- |
|
|
39
|
-
| `@cuylabs/channel-slack/core` | Activity parsing, formatting, sessions, turn context, shared types |
|
|
40
|
-
| `@cuylabs/channel-slack/policy` | Message admission, duplicate suppression, mentioned-thread state |
|
|
41
|
-
| `@cuylabs/channel-slack/history` | Slack history reading, prompt shaping, supplemental-history visibility |
|
|
42
|
-
| `@cuylabs/channel-slack/bolt` | Bolt app factories, auth options, Socket Mode runtime helpers, installation stores |
|
|
43
|
-
| `@cuylabs/channel-slack/setup` | Required scopes/events/settings, generated manifests, setup inspection |
|
|
44
|
-
| `@cuylabs/channel-slack/diagnostics` | Slack token and scope checks |
|
|
45
|
-
| `@cuylabs/channel-slack/users` | User profile lookup and mention enrichment |
|
|
46
|
-
| `@cuylabs/channel-slack/targets` | Human-friendly Slack channel/user target parsing and resolution |
|
|
47
|
-
| `@cuylabs/channel-slack/feedback` | Feedback Block Kit and action helpers |
|
|
48
|
-
|
|
49
|
-
The package root re-exports `core` and `policy` as a lightweight convenience.
|
|
50
|
-
Use feature-specific imports for peer-backed helpers such as `bolt`,
|
|
51
|
-
`diagnostics`, `setup`, and `users`. `@cuylabs/channel-slack/shared` remains as
|
|
52
|
-
an alias of `core`.
|
|
53
|
-
|
|
54
|
-
## Core Flow
|
|
55
|
-
|
|
56
|
-
Most Slack agent adapters follow this flow:
|
|
57
|
-
|
|
58
|
-
1. Parse a Slack event into `SlackActivityInfo`.
|
|
59
|
-
2. Resolve whether the message should start or continue an agent turn.
|
|
60
|
-
3. Optionally load model-visible Slack history for the turn.
|
|
61
|
-
4. Run the product-owned agent.
|
|
62
|
-
5. Format the response back to Slack.
|
|
30
|
+
## Quick Start
|
|
63
31
|
|
|
64
32
|
```typescript
|
|
65
33
|
import {
|
|
@@ -77,100 +45,21 @@ const policy = createSlackMessagePolicyResolver({
|
|
|
77
45
|
const decision = policy.resolve(activity);
|
|
78
46
|
if (!decision.accepted) return;
|
|
79
47
|
|
|
80
|
-
const
|
|
48
|
+
const modelInput = decision.text;
|
|
49
|
+
const slackText = markdownToSlackMrkdwn("**Ready**");
|
|
81
50
|
```
|
|
82
51
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
### Core
|
|
86
|
-
|
|
87
|
-
`core` is transport-neutral. It parses raw Slack message and app mention
|
|
88
|
-
payloads, extracts model-facing text from Slack text, Block Kit, rich text, and
|
|
89
|
-
attachments, formats Markdown to Slack mrkdwn, derives thread-aware session IDs,
|
|
90
|
-
and carries request context.
|
|
91
|
-
|
|
92
|
-
See [Activity](docs/concepts/activity.md).
|
|
93
|
-
|
|
94
|
-
### Policy
|
|
95
|
-
|
|
96
|
-
`policy` decides which Slack messages an adapter should process. It accepts DMs
|
|
97
|
-
and direct mentions by default, can allow passive channel messages by channel or
|
|
98
|
-
install scope, tracks mentioned threads, and suppresses duplicate Slack events.
|
|
99
|
-
Use `createPostgresSlackMessagePolicyStateStore` when duplicate suppression and
|
|
100
|
-
mentioned-thread state must survive restarts or coordinate across workers.
|
|
101
|
-
|
|
102
|
-
See [Message Policy](docs/concepts/message-policy.md).
|
|
103
|
-
|
|
104
|
-
### History
|
|
105
|
-
|
|
106
|
-
`history` reads Slack thread/channel history, normalizes messages, formats prompt
|
|
107
|
-
sections, reports expected Slack history access failures, and filters
|
|
108
|
-
supplemental context before it reaches a model.
|
|
109
|
-
|
|
110
|
-
See [Supplemental History](docs/concepts/supplemental-history.md).
|
|
111
|
-
|
|
112
|
-
### Bolt
|
|
52
|
+
Most adapters follow the same shape:
|
|
113
53
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
54
|
+
1. Normalize the Slack payload.
|
|
55
|
+
2. Decide whether it should become a turn.
|
|
56
|
+
3. Optionally load model-visible Slack context.
|
|
57
|
+
4. Run the application-owned runtime.
|
|
58
|
+
5. Format the response for Slack.
|
|
119
59
|
|
|
120
|
-
|
|
60
|
+
## Documentation
|
|
121
61
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
requirements.
|
|
127
|
-
|
|
128
|
-
See [Setup Requirements](docs/concepts/setup-requirements.md).
|
|
129
|
-
|
|
130
|
-
## Recipes
|
|
131
|
-
|
|
132
|
-
- [App mention handler](docs/recipes/app-mention-handler.md)
|
|
133
|
-
- [Assistant thread handler](docs/recipes/assistant-thread-handler.md)
|
|
134
|
-
- [Socket Mode app](docs/recipes/socket-mode-app.md)
|
|
135
|
-
- [Generate a Slack manifest](docs/recipes/generate-slack-manifest.md)
|
|
136
|
-
- [History visibility filter](docs/recipes/history-visibility.md)
|
|
137
|
-
|
|
138
|
-
## Source Layout
|
|
139
|
-
|
|
140
|
-
```text
|
|
141
|
-
src/
|
|
142
|
-
core.ts public core entrypoint
|
|
143
|
-
shared/ types, parsing, formatting, turn helpers
|
|
144
|
-
assistant/ Slack Assistant API helpers
|
|
145
|
-
bolt/ Bolt app/auth/runtime helpers
|
|
146
|
-
diagnostics/ token and scope inspection
|
|
147
|
-
feedback/ feedback blocks and action parsing
|
|
148
|
-
history/
|
|
149
|
-
context/ turn-history loading component
|
|
150
|
-
reader.ts Slack history API reader and prompt formatter
|
|
151
|
-
visibility-policy.ts model-visible history filters
|
|
152
|
-
inclusion-policy.ts direct-message supplemental-history inclusion policy
|
|
153
|
-
policy/
|
|
154
|
-
message/ message admission and state-store component
|
|
155
|
-
setup/ scopes, events, manifests, setup inspection
|
|
156
|
-
targets/ channel/user target parsing and resolution
|
|
157
|
-
users/ profile lookup and mention enrichment
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Development
|
|
161
|
-
|
|
162
|
-
```bash
|
|
163
|
-
pnpm --filter @cuylabs/channel-slack typecheck
|
|
164
|
-
pnpm --filter @cuylabs/channel-slack test
|
|
165
|
-
pnpm --filter @cuylabs/channel-slack build
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Run workspace checks before publishing changes:
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
pnpm lint
|
|
172
|
-
pnpm typecheck
|
|
173
|
-
pnpm build
|
|
174
|
-
pnpm test
|
|
175
|
-
pnpm format:check
|
|
176
|
-
```
|
|
62
|
+
- [Package boundary](docs/reference/channel-slack-boundary.md)
|
|
63
|
+
- [Exports and peer expectations](docs/reference/exports.md)
|
|
64
|
+
- [Source layout](docs/reference/source-layout.md)
|
|
65
|
+
- [Docs index](docs/README.md)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { App } from '@slack/bolt';
|
|
2
|
+
import { HomeView } from '@slack/types';
|
|
3
|
+
import { L as Logger } from './logging-Bl3HfcC8.js';
|
|
4
|
+
|
|
5
|
+
interface SlackAppHomeOpenedEvent {
|
|
6
|
+
tab?: string;
|
|
7
|
+
user: string;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
interface SlackAppHomeOpenContext {
|
|
11
|
+
event: SlackAppHomeOpenedEvent;
|
|
12
|
+
}
|
|
13
|
+
interface InstallSlackAppHomeOptions {
|
|
14
|
+
boltApp: App;
|
|
15
|
+
buildView(context: SlackAppHomeOpenContext): HomeView | Promise<HomeView>;
|
|
16
|
+
logger?: Logger;
|
|
17
|
+
}
|
|
18
|
+
interface SlackAppHomeInstallation {
|
|
19
|
+
cancelAll(reason?: string): void;
|
|
20
|
+
}
|
|
21
|
+
declare function installSlackAppHome({ boltApp, buildView, logger, }: InstallSlackAppHomeOptions): SlackAppHomeInstallation;
|
|
22
|
+
|
|
23
|
+
export { type InstallSlackAppHomeOptions, type SlackAppHomeInstallation, type SlackAppHomeOpenContext, type SlackAppHomeOpenedEvent, installSlackAppHome };
|
package/dist/app-home.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/app-home.ts
|
|
2
|
+
function installSlackAppHome({
|
|
3
|
+
boltApp,
|
|
4
|
+
buildView,
|
|
5
|
+
logger
|
|
6
|
+
}) {
|
|
7
|
+
boltApp.event("app_home_opened", async ({ event, client }) => {
|
|
8
|
+
const homeEvent = event;
|
|
9
|
+
try {
|
|
10
|
+
await client.views.publish({
|
|
11
|
+
user_id: homeEvent.user,
|
|
12
|
+
view: await buildView({ event: homeEvent })
|
|
13
|
+
});
|
|
14
|
+
logger?.debug?.("Slack App Home published", {
|
|
15
|
+
tab: homeEvent.tab,
|
|
16
|
+
userId: homeEvent.user
|
|
17
|
+
});
|
|
18
|
+
} catch (error) {
|
|
19
|
+
logger?.warn?.("Failed to publish Slack App Home", {
|
|
20
|
+
error: formatAppHomeError(error),
|
|
21
|
+
userId: homeEvent.user
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
cancelAll() {
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function formatAppHomeError(error) {
|
|
31
|
+
return error instanceof Error ? error.stack ?? error.message : String(error);
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
installSlackAppHome
|
|
35
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
type SlackArtifactPublishMethod = "message" | "file" | "canvas";
|
|
2
|
+
type SlackArtifactBinaryData = string | Uint8Array | ArrayBuffer;
|
|
3
|
+
interface SlackArtifactBase {
|
|
4
|
+
kind: string;
|
|
5
|
+
title: string;
|
|
6
|
+
summary?: string;
|
|
7
|
+
metadata?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
interface SlackTextArtifact extends SlackArtifactBase {
|
|
10
|
+
kind: "text";
|
|
11
|
+
text: string;
|
|
12
|
+
filename?: string;
|
|
13
|
+
mimeType?: string;
|
|
14
|
+
publishAs?: "message" | "file";
|
|
15
|
+
}
|
|
16
|
+
interface SlackFileArtifact extends SlackArtifactBase {
|
|
17
|
+
kind: "file";
|
|
18
|
+
filename: string;
|
|
19
|
+
data?: SlackArtifactBinaryData;
|
|
20
|
+
filePath?: string;
|
|
21
|
+
mimeType?: string;
|
|
22
|
+
initialComment?: string;
|
|
23
|
+
}
|
|
24
|
+
interface SlackImageArtifact extends SlackArtifactBase {
|
|
25
|
+
kind: "image";
|
|
26
|
+
filename: string;
|
|
27
|
+
altText: string;
|
|
28
|
+
data?: SlackArtifactBinaryData;
|
|
29
|
+
filePath?: string;
|
|
30
|
+
initialComment?: string;
|
|
31
|
+
}
|
|
32
|
+
interface SlackLinkArtifact extends SlackArtifactBase {
|
|
33
|
+
kind: "link";
|
|
34
|
+
url: string;
|
|
35
|
+
}
|
|
36
|
+
interface SlackCanvasArtifact extends SlackArtifactBase {
|
|
37
|
+
kind: "canvas";
|
|
38
|
+
markdown: string;
|
|
39
|
+
/**
|
|
40
|
+
* When set, updates an existing canvas instead of creating one.
|
|
41
|
+
*/
|
|
42
|
+
canvasId?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Create a channel canvas when a channel is available.
|
|
45
|
+
*
|
|
46
|
+
* @default true
|
|
47
|
+
*/
|
|
48
|
+
channelCanvas?: boolean;
|
|
49
|
+
}
|
|
50
|
+
type SlackArtifact = SlackTextArtifact | SlackFileArtifact | SlackImageArtifact | SlackLinkArtifact | SlackCanvasArtifact;
|
|
51
|
+
interface SlackArtifactPostMessageResponse {
|
|
52
|
+
ok?: boolean;
|
|
53
|
+
channel?: string;
|
|
54
|
+
ts?: string;
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
interface SlackArtifactFileUploadResponse {
|
|
58
|
+
ok?: boolean;
|
|
59
|
+
file?: {
|
|
60
|
+
id?: string;
|
|
61
|
+
[key: string]: unknown;
|
|
62
|
+
};
|
|
63
|
+
files?: Array<{
|
|
64
|
+
id?: string;
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
}>;
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
}
|
|
69
|
+
interface SlackArtifactCanvasResponse {
|
|
70
|
+
ok?: boolean;
|
|
71
|
+
canvas_id?: string;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
interface SlackArtifactClient {
|
|
75
|
+
chat?: {
|
|
76
|
+
postMessage(args: Record<string, unknown>): Promise<SlackArtifactPostMessageResponse>;
|
|
77
|
+
};
|
|
78
|
+
files?: {
|
|
79
|
+
uploadV2(args: Record<string, unknown>): Promise<SlackArtifactFileUploadResponse>;
|
|
80
|
+
};
|
|
81
|
+
canvases?: {
|
|
82
|
+
create(args?: Record<string, unknown>): Promise<SlackArtifactCanvasResponse>;
|
|
83
|
+
edit?(args: Record<string, unknown>): Promise<SlackArtifactCanvasResponse>;
|
|
84
|
+
};
|
|
85
|
+
conversations?: {
|
|
86
|
+
canvases?: {
|
|
87
|
+
create(args: Record<string, unknown>): Promise<SlackArtifactCanvasResponse>;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
interface PublishSlackArtifactOptions {
|
|
92
|
+
client: SlackArtifactClient;
|
|
93
|
+
artifact: SlackArtifact;
|
|
94
|
+
channelId?: string;
|
|
95
|
+
threadTs?: string;
|
|
96
|
+
token?: string;
|
|
97
|
+
unfurlLinks?: boolean;
|
|
98
|
+
unfurlMedia?: boolean;
|
|
99
|
+
}
|
|
100
|
+
interface SlackArtifactPublication {
|
|
101
|
+
artifact: SlackArtifact;
|
|
102
|
+
method: SlackArtifactPublishMethod;
|
|
103
|
+
response: unknown;
|
|
104
|
+
channelId?: string;
|
|
105
|
+
threadTs?: string;
|
|
106
|
+
messageTs?: string;
|
|
107
|
+
fileId?: string;
|
|
108
|
+
canvasId?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface SlackArtifactBlockKitMessage {
|
|
112
|
+
text: string;
|
|
113
|
+
blocks: Array<Record<string, unknown>>;
|
|
114
|
+
}
|
|
115
|
+
declare function createSlackArtifactBlocks(artifact: SlackArtifact): SlackArtifactBlockKitMessage;
|
|
116
|
+
declare function createSlackLinkArtifactBlocks(artifact: SlackLinkArtifact): SlackArtifactBlockKitMessage;
|
|
117
|
+
|
|
118
|
+
declare function publishSlackArtifact(options: PublishSlackArtifactOptions): Promise<SlackArtifactPublication>;
|
|
119
|
+
declare function publishSlackTextArtifact(options: PublishSlackArtifactOptions & {
|
|
120
|
+
artifact: SlackTextArtifact;
|
|
121
|
+
}): Promise<SlackArtifactPublication>;
|
|
122
|
+
declare function publishSlackFileArtifact(options: PublishSlackArtifactOptions & {
|
|
123
|
+
artifact: SlackFileArtifact;
|
|
124
|
+
}): Promise<SlackArtifactPublication>;
|
|
125
|
+
declare function publishSlackImageArtifact(options: PublishSlackArtifactOptions & {
|
|
126
|
+
artifact: SlackImageArtifact;
|
|
127
|
+
}): Promise<SlackArtifactPublication>;
|
|
128
|
+
declare function publishSlackLinkArtifact(options: PublishSlackArtifactOptions & {
|
|
129
|
+
artifact: SlackLinkArtifact;
|
|
130
|
+
}): Promise<SlackArtifactPublication>;
|
|
131
|
+
declare function publishSlackCanvasArtifact(options: PublishSlackArtifactOptions & {
|
|
132
|
+
artifact: SlackCanvasArtifact;
|
|
133
|
+
}): Promise<SlackArtifactPublication>;
|
|
134
|
+
|
|
135
|
+
export { type PublishSlackArtifactOptions, type SlackArtifact, type SlackArtifactBase, type SlackArtifactBinaryData, type SlackArtifactBlockKitMessage, type SlackArtifactCanvasResponse, type SlackArtifactClient, type SlackArtifactFileUploadResponse, type SlackArtifactPostMessageResponse, type SlackArtifactPublication, type SlackArtifactPublishMethod, type SlackCanvasArtifact, type SlackFileArtifact, type SlackImageArtifact, type SlackLinkArtifact, type SlackTextArtifact, createSlackArtifactBlocks, createSlackLinkArtifactBlocks, publishSlackArtifact, publishSlackCanvasArtifact, publishSlackFileArtifact, publishSlackImageArtifact, publishSlackLinkArtifact, publishSlackTextArtifact };
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import {
|
|
2
|
+
markdownToSlackMrkdwn
|
|
3
|
+
} from "../chunk-6WHFQUYQ.js";
|
|
4
|
+
|
|
5
|
+
// src/artifacts/blocks.ts
|
|
6
|
+
function createSlackArtifactBlocks(artifact) {
|
|
7
|
+
switch (artifact.kind) {
|
|
8
|
+
case "link":
|
|
9
|
+
return createSlackLinkArtifactBlocks(artifact);
|
|
10
|
+
case "canvas":
|
|
11
|
+
return createGenericArtifactBlocks({
|
|
12
|
+
title: artifact.title,
|
|
13
|
+
summary: artifact.summary ?? "Canvas created."
|
|
14
|
+
});
|
|
15
|
+
case "text":
|
|
16
|
+
case "file":
|
|
17
|
+
case "image":
|
|
18
|
+
return createGenericArtifactBlocks({
|
|
19
|
+
title: artifact.title,
|
|
20
|
+
summary: artifact.summary
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function createSlackLinkArtifactBlocks(artifact) {
|
|
25
|
+
const title = normalizeText(artifact.title);
|
|
26
|
+
const summary = normalizeText(artifact.summary);
|
|
27
|
+
const link = `<${escapeSlackLinkUrl(artifact.url)}|Open link>`;
|
|
28
|
+
const body = [summary ? markdownToSlackMrkdwn(summary) : void 0, link].filter(Boolean).join("\n");
|
|
29
|
+
return {
|
|
30
|
+
text: [title, artifact.url].filter(Boolean).join(": "),
|
|
31
|
+
blocks: [
|
|
32
|
+
{
|
|
33
|
+
type: "section",
|
|
34
|
+
text: {
|
|
35
|
+
type: "mrkdwn",
|
|
36
|
+
text: [`*${title}*`, body].filter(Boolean).join("\n")
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function createGenericArtifactBlocks({
|
|
43
|
+
title,
|
|
44
|
+
summary
|
|
45
|
+
}) {
|
|
46
|
+
const normalizedTitle = normalizeText(title);
|
|
47
|
+
const normalizedSummary = normalizeText(summary);
|
|
48
|
+
return {
|
|
49
|
+
text: normalizedSummary ? `${normalizedTitle}: ${normalizedSummary}` : normalizedTitle,
|
|
50
|
+
blocks: [
|
|
51
|
+
{
|
|
52
|
+
type: "section",
|
|
53
|
+
text: {
|
|
54
|
+
type: "mrkdwn",
|
|
55
|
+
text: [
|
|
56
|
+
`*${normalizedTitle}*`,
|
|
57
|
+
markdownToSlackMrkdwn(normalizedSummary)
|
|
58
|
+
].filter(Boolean).join("\n")
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function normalizeText(value) {
|
|
65
|
+
return value?.trim() || "Untitled artifact";
|
|
66
|
+
}
|
|
67
|
+
function escapeSlackLinkUrl(url) {
|
|
68
|
+
return url.trim().replace(/[>|]/g, encodeURIComponent);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/artifacts/publish.ts
|
|
72
|
+
async function publishSlackArtifact(options) {
|
|
73
|
+
switch (options.artifact.kind) {
|
|
74
|
+
case "link":
|
|
75
|
+
return publishSlackLinkArtifact({
|
|
76
|
+
...options,
|
|
77
|
+
artifact: options.artifact
|
|
78
|
+
});
|
|
79
|
+
case "text":
|
|
80
|
+
return publishSlackTextArtifact({
|
|
81
|
+
...options,
|
|
82
|
+
artifact: options.artifact
|
|
83
|
+
});
|
|
84
|
+
case "file":
|
|
85
|
+
return publishSlackFileArtifact({
|
|
86
|
+
...options,
|
|
87
|
+
artifact: options.artifact
|
|
88
|
+
});
|
|
89
|
+
case "image":
|
|
90
|
+
return publishSlackImageArtifact({
|
|
91
|
+
...options,
|
|
92
|
+
artifact: options.artifact
|
|
93
|
+
});
|
|
94
|
+
case "canvas":
|
|
95
|
+
return publishSlackCanvasArtifact({
|
|
96
|
+
...options,
|
|
97
|
+
artifact: options.artifact
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function publishSlackTextArtifact(options) {
|
|
102
|
+
if (options.artifact.publishAs === "message") {
|
|
103
|
+
return publishArtifactMessage(
|
|
104
|
+
options,
|
|
105
|
+
createSlackArtifactBlocks({
|
|
106
|
+
...options.artifact,
|
|
107
|
+
summary: options.artifact.summary ?? options.artifact.text
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return uploadArtifactFile(options, {
|
|
112
|
+
filename: options.artifact.filename ?? filenameFromTitle(options.artifact.title, "txt"),
|
|
113
|
+
title: options.artifact.title,
|
|
114
|
+
content: options.artifact.text,
|
|
115
|
+
initialComment: options.artifact.summary
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async function publishSlackFileArtifact(options) {
|
|
119
|
+
return uploadArtifactFile(options, {
|
|
120
|
+
filename: options.artifact.filename,
|
|
121
|
+
title: options.artifact.title,
|
|
122
|
+
data: options.artifact.data,
|
|
123
|
+
filePath: options.artifact.filePath,
|
|
124
|
+
initialComment: options.artifact.initialComment ?? options.artifact.summary
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
async function publishSlackImageArtifact(options) {
|
|
128
|
+
return uploadArtifactFile(options, {
|
|
129
|
+
filename: options.artifact.filename,
|
|
130
|
+
title: options.artifact.title,
|
|
131
|
+
data: options.artifact.data,
|
|
132
|
+
filePath: options.artifact.filePath,
|
|
133
|
+
initialComment: options.artifact.initialComment ?? options.artifact.summary,
|
|
134
|
+
altText: options.artifact.altText
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async function publishSlackLinkArtifact(options) {
|
|
138
|
+
return publishArtifactMessage(
|
|
139
|
+
options,
|
|
140
|
+
createSlackArtifactBlocks(options.artifact)
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
async function publishSlackCanvasArtifact(options) {
|
|
144
|
+
const documentContent = {
|
|
145
|
+
type: "markdown",
|
|
146
|
+
markdown: options.artifact.markdown
|
|
147
|
+
};
|
|
148
|
+
const tokenArgs = options.token ? { token: options.token } : {};
|
|
149
|
+
let response;
|
|
150
|
+
if (options.artifact.canvasId) {
|
|
151
|
+
const edit = options.client.canvases?.edit;
|
|
152
|
+
if (typeof edit !== "function") {
|
|
153
|
+
throw new Error("Slack client does not expose canvases.edit.");
|
|
154
|
+
}
|
|
155
|
+
response = await edit({
|
|
156
|
+
...tokenArgs,
|
|
157
|
+
canvas_id: options.artifact.canvasId,
|
|
158
|
+
changes: [
|
|
159
|
+
{
|
|
160
|
+
operation: "replace",
|
|
161
|
+
document_content: documentContent
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
});
|
|
165
|
+
} else if (options.channelId && options.artifact.channelCanvas !== false && typeof options.client.conversations?.canvases?.create === "function") {
|
|
166
|
+
response = await options.client.conversations.canvases.create({
|
|
167
|
+
...tokenArgs,
|
|
168
|
+
channel_id: options.channelId,
|
|
169
|
+
title: options.artifact.title,
|
|
170
|
+
document_content: documentContent
|
|
171
|
+
});
|
|
172
|
+
} else {
|
|
173
|
+
const create = options.client.canvases?.create;
|
|
174
|
+
if (typeof create !== "function") {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"Slack client does not expose canvases.create or conversations.canvases.create."
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
response = await create({
|
|
180
|
+
...tokenArgs,
|
|
181
|
+
...options.channelId && options.artifact.channelCanvas !== false ? { channel_id: options.channelId } : {},
|
|
182
|
+
title: options.artifact.title,
|
|
183
|
+
document_content: documentContent
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
artifact: options.artifact,
|
|
188
|
+
method: "canvas",
|
|
189
|
+
response,
|
|
190
|
+
...options.channelId ? { channelId: options.channelId } : {},
|
|
191
|
+
...options.threadTs ? { threadTs: options.threadTs } : {},
|
|
192
|
+
...readString(response.canvas_id) ?? options.artifact.canvasId ? {
|
|
193
|
+
canvasId: readString(response.canvas_id) ?? options.artifact.canvasId
|
|
194
|
+
} : {}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async function publishArtifactMessage(options, message) {
|
|
198
|
+
const channelId = requireChannelId(options.channelId, options.artifact.kind);
|
|
199
|
+
const postMessage = options.client.chat?.postMessage;
|
|
200
|
+
if (typeof postMessage !== "function") {
|
|
201
|
+
throw new Error("Slack client does not expose chat.postMessage.");
|
|
202
|
+
}
|
|
203
|
+
const response = await postMessage({
|
|
204
|
+
...options.token ? { token: options.token } : {},
|
|
205
|
+
channel: channelId,
|
|
206
|
+
text: message.text,
|
|
207
|
+
blocks: message.blocks,
|
|
208
|
+
...options.threadTs ? { thread_ts: options.threadTs } : {},
|
|
209
|
+
...options.unfurlLinks !== void 0 ? { unfurl_links: options.unfurlLinks } : {},
|
|
210
|
+
...options.unfurlMedia !== void 0 ? { unfurl_media: options.unfurlMedia } : {}
|
|
211
|
+
});
|
|
212
|
+
return {
|
|
213
|
+
artifact: options.artifact,
|
|
214
|
+
method: "message",
|
|
215
|
+
response,
|
|
216
|
+
channelId,
|
|
217
|
+
...options.threadTs ? { threadTs: options.threadTs } : {},
|
|
218
|
+
...readString(response.ts) ? { messageTs: readString(response.ts) } : {}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
async function uploadArtifactFile(options, file) {
|
|
222
|
+
const channelId = requireChannelId(options.channelId, options.artifact.kind);
|
|
223
|
+
const uploadV2 = options.client.files?.uploadV2;
|
|
224
|
+
if (typeof uploadV2 !== "function") {
|
|
225
|
+
throw new Error("Slack client does not expose files.uploadV2.");
|
|
226
|
+
}
|
|
227
|
+
const uploadArgs = {
|
|
228
|
+
...options.token ? { token: options.token } : {},
|
|
229
|
+
channel_id: channelId,
|
|
230
|
+
...options.threadTs ? { thread_ts: options.threadTs } : {},
|
|
231
|
+
filename: requireNonEmpty(file.filename, "artifact filename"),
|
|
232
|
+
title: requireNonEmpty(file.title, "artifact title"),
|
|
233
|
+
...file.initialComment ? { initial_comment: file.initialComment } : {},
|
|
234
|
+
...file.altText ? { alt_text: file.altText } : {},
|
|
235
|
+
...resolveFilePayload(file)
|
|
236
|
+
};
|
|
237
|
+
const response = await uploadV2(uploadArgs);
|
|
238
|
+
return {
|
|
239
|
+
artifact: options.artifact,
|
|
240
|
+
method: "file",
|
|
241
|
+
response,
|
|
242
|
+
channelId,
|
|
243
|
+
...options.threadTs ? { threadTs: options.threadTs } : {},
|
|
244
|
+
...readUploadedFileId(response) ? { fileId: readUploadedFileId(response) } : {}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function resolveFilePayload(file) {
|
|
248
|
+
if (file.content !== void 0) {
|
|
249
|
+
return { content: file.content };
|
|
250
|
+
}
|
|
251
|
+
if (file.filePath) {
|
|
252
|
+
return { file: file.filePath };
|
|
253
|
+
}
|
|
254
|
+
if (file.data !== void 0) {
|
|
255
|
+
return typeof file.data === "string" ? { content: file.data } : { file: toBuffer(file.data) };
|
|
256
|
+
}
|
|
257
|
+
throw new Error(
|
|
258
|
+
"Slack file artifact requires `data`, `filePath`, or `content`."
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
function toBuffer(data) {
|
|
262
|
+
return data instanceof ArrayBuffer ? Buffer.from(new Uint8Array(data)) : Buffer.from(data);
|
|
263
|
+
}
|
|
264
|
+
function requireChannelId(channelId, artifactKind) {
|
|
265
|
+
const trimmed = channelId?.trim();
|
|
266
|
+
if (!trimmed) {
|
|
267
|
+
throw new Error(
|
|
268
|
+
`Slack ${artifactKind} artifact publication requires a channelId.`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return trimmed;
|
|
272
|
+
}
|
|
273
|
+
function requireNonEmpty(value, label) {
|
|
274
|
+
const trimmed = value.trim();
|
|
275
|
+
if (!trimmed) {
|
|
276
|
+
throw new Error(`Slack ${label} must not be empty.`);
|
|
277
|
+
}
|
|
278
|
+
return trimmed;
|
|
279
|
+
}
|
|
280
|
+
function filenameFromTitle(title, extension) {
|
|
281
|
+
const slug = title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
282
|
+
return `${slug || "artifact"}.${extension}`;
|
|
283
|
+
}
|
|
284
|
+
function readUploadedFileId(response) {
|
|
285
|
+
return readString(response.file?.id) ?? readString(response.files?.[0]?.id);
|
|
286
|
+
}
|
|
287
|
+
function readString(value) {
|
|
288
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
289
|
+
}
|
|
290
|
+
export {
|
|
291
|
+
createSlackArtifactBlocks,
|
|
292
|
+
createSlackLinkArtifactBlocks,
|
|
293
|
+
publishSlackArtifact,
|
|
294
|
+
publishSlackCanvasArtifact,
|
|
295
|
+
publishSlackFileArtifact,
|
|
296
|
+
publishSlackImageArtifact,
|
|
297
|
+
publishSlackLinkArtifact,
|
|
298
|
+
publishSlackTextArtifact
|
|
299
|
+
};
|