@jonsoc/slack 1.1.34

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/.env.example ADDED
@@ -0,0 +1,3 @@
1
+ SLACK_BOT_TOKEN=xoxb-your-bot-token
2
+ SLACK_SIGNING_SECRET=your-signing-secret
3
+ SLACK_APP_TOKEN=xapp-your-app-token
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # @jonsoc/slack
2
+
3
+ Slack bot integration for jonsoc that creates threaded conversations.
4
+
5
+ ## Setup
6
+
7
+ 1. Create a Slack app at https://api.slack.com/apps
8
+ 2. Enable Socket Mode
9
+ 3. Add the following OAuth scopes:
10
+ - `chat:write`
11
+ - `app_mentions:read`
12
+ - `channels:history`
13
+ - `groups:history`
14
+ 4. Install the app to your workspace
15
+ 5. Set environment variables in `.env`:
16
+ - `SLACK_BOT_TOKEN` - Bot User OAuth Token
17
+ - `SLACK_SIGNING_SECRET` - Signing Secret from Basic Information
18
+ - `SLACK_APP_TOKEN` - App-Level Token from Basic Information
19
+
20
+ ## Usage
21
+
22
+ ```bash
23
+ # Edit .env with your Slack app credentials
24
+ bun dev
25
+ ```
26
+
27
+ The bot will respond to messages in channels where it's added, creating separate jonsoc sessions for each thread.
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@jonsoc/slack",
3
+ "version": "1.1.34",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "scripts": {
7
+ "dev": "bun run src/index.ts",
8
+ "typecheck": "tsgo --noEmit"
9
+ },
10
+ "dependencies": {
11
+ "@jonsoc/sdk": "workspace:*",
12
+ "@slack/bolt": "^3.17.1"
13
+ },
14
+ "devDependencies": {
15
+ "@types/node": "catalog:",
16
+ "typescript": "catalog:",
17
+ "@typescript/native-preview": "catalog:"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ }
22
+ }
package/src/index.ts ADDED
@@ -0,0 +1,145 @@
1
+ import { App } from "@slack/bolt"
2
+ import { createOpencode, type ToolPart } from "@jonsoc/sdk"
3
+
4
+ const app = new App({
5
+ token: process.env.SLACK_BOT_TOKEN,
6
+ signingSecret: process.env.SLACK_SIGNING_SECRET,
7
+ socketMode: true,
8
+ appToken: process.env.SLACK_APP_TOKEN,
9
+ })
10
+
11
+ console.log("๐Ÿ”ง Bot configuration:")
12
+ console.log("- Bot token present:", !!process.env.SLACK_BOT_TOKEN)
13
+ console.log("- Signing secret present:", !!process.env.SLACK_SIGNING_SECRET)
14
+ console.log("- App token present:", !!process.env.SLACK_APP_TOKEN)
15
+
16
+ console.log("๐Ÿš€ Starting jonsoc server...")
17
+ const jonsoc = await createOpencode({
18
+ port: 0,
19
+ })
20
+ console.log("โœ… Opencode server ready")
21
+
22
+ const sessions = new Map<string, { client: any; server: any; sessionId: string; channel: string; thread: string }>()
23
+ ;(async () => {
24
+ const events = await jonsoc.client.event.subscribe()
25
+ for await (const event of events.stream) {
26
+ if (event.type === "message.part.updated") {
27
+ const part = event.properties.part
28
+ if (part.type === "tool") {
29
+ // Find the session for this tool update
30
+ for (const [sessionKey, session] of sessions.entries()) {
31
+ if (session.sessionId === part.sessionID) {
32
+ handleToolUpdate(part, session.channel, session.thread)
33
+ break
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ })()
40
+
41
+ async function handleToolUpdate(part: ToolPart, channel: string, thread: string) {
42
+ if (part.state.status !== "completed") return
43
+ const toolMessage = `*${part.tool}* - ${part.state.title}`
44
+ await app.client.chat
45
+ .postMessage({
46
+ channel,
47
+ thread_ts: thread,
48
+ text: toolMessage,
49
+ })
50
+ .catch(() => {})
51
+ }
52
+
53
+ app.use(async ({ next, context }) => {
54
+ console.log("๐Ÿ“ก Raw Slack event:", JSON.stringify(context, null, 2))
55
+ await next()
56
+ })
57
+
58
+ app.message(async ({ message, say }) => {
59
+ console.log("๐Ÿ“จ Received message event:", JSON.stringify(message, null, 2))
60
+
61
+ if (message.subtype || !("text" in message) || !message.text) {
62
+ console.log("โญ๏ธ Skipping message - no text or has subtype")
63
+ return
64
+ }
65
+
66
+ console.log("โœ… Processing message:", message.text)
67
+
68
+ const channel = message.channel
69
+ const thread = (message as any).thread_ts || message.ts
70
+ const sessionKey = `${channel}-${thread}`
71
+
72
+ let session = sessions.get(sessionKey)
73
+
74
+ if (!session) {
75
+ console.log("๐Ÿ†• Creating new jonsoc session...")
76
+ const { client, server } = jonsoc
77
+
78
+ const createResult = await client.session.create({
79
+ body: { title: `Slack thread ${thread}` },
80
+ })
81
+
82
+ if (createResult.error) {
83
+ console.error("โŒ Failed to create session:", createResult.error)
84
+ await say({
85
+ text: "Sorry, I had trouble creating a session. Please try again.",
86
+ thread_ts: thread,
87
+ })
88
+ return
89
+ }
90
+
91
+ console.log("โœ… Created jonsoc session:", createResult.data.id)
92
+
93
+ session = { client, server, sessionId: createResult.data.id, channel, thread }
94
+ sessions.set(sessionKey, session)
95
+
96
+ const shareResult = await client.session.share({ path: { id: createResult.data.id } })
97
+ if (!shareResult.error && shareResult.data) {
98
+ const sessionUrl = shareResult.data.share?.url!
99
+ console.log("๐Ÿ”— Session shared:", sessionUrl)
100
+ await app.client.chat.postMessage({ channel, thread_ts: thread, text: sessionUrl })
101
+ }
102
+ }
103
+
104
+ console.log("๐Ÿ“ Sending to jonsoc:", message.text)
105
+ const result = await session.client.session.prompt({
106
+ path: { id: session.sessionId },
107
+ body: { parts: [{ type: "text", text: message.text }] },
108
+ })
109
+
110
+ console.log("๐Ÿ“ค Opencode response:", JSON.stringify(result, null, 2))
111
+
112
+ if (result.error) {
113
+ console.error("โŒ Failed to send message:", result.error)
114
+ await say({
115
+ text: "Sorry, I had trouble processing your message. Please try again.",
116
+ thread_ts: thread,
117
+ })
118
+ return
119
+ }
120
+
121
+ const response = result.data
122
+
123
+ // Build response text
124
+ const responseText =
125
+ response.info?.content ||
126
+ response.parts
127
+ ?.filter((p: any) => p.type === "text")
128
+ .map((p: any) => p.text)
129
+ .join("\n") ||
130
+ "I received your message but didn't have a response."
131
+
132
+ console.log("๐Ÿ’ฌ Sending response:", responseText)
133
+
134
+ // Send main response (tool updates will come via live events)
135
+ await say({ text: responseText, thread_ts: thread })
136
+ })
137
+
138
+ app.command("/test", async ({ command, ack, say }) => {
139
+ await ack()
140
+ console.log("๐Ÿงช Test command received:", JSON.stringify(command, null, 2))
141
+ await say("๐Ÿค– Bot is working! I can hear you loud and clear.")
142
+ })
143
+
144
+ await app.start()
145
+ console.log("โšก๏ธ Slack bot is running!")
package/sst-env.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /* This file is auto-generated by SST. Do not edit. */
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ /* deno-fmt-ignore-file */
5
+
6
+ /// <reference path="../../sst-env.d.ts" />
7
+
8
+ import "sst"
9
+ export {}
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "@tsconfig/bun/tsconfig.json",
4
+ "compilerOptions": {
5
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
6
+ "noUncheckedIndexedAccess": false
7
+ }
8
+ }