@jobmatchme/bee-slack 0.1.16 → 0.1.17
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 +6 -0
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +5 -1
- package/dist/gateway.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +10 -1
- package/dist/main.js.map +1 -1
- package/dist/scheduled.d.ts.map +1 -1
- package/dist/scheduled.js +5 -2
- package/dist/scheduled.js.map +1 -1
- package/dist/telemetry.d.ts +10 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +66 -0
- package/dist/telemetry.js.map +1 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -83,6 +83,12 @@ Slack API failures are surfaced to Bee Gate, which owns the per-run fallback
|
|
|
83
83
|
from streaming to legacy/degraded delivery. Generated artifacts remain separate
|
|
84
84
|
`files.uploadV2` uploads after the final response.
|
|
85
85
|
|
|
86
|
+
## OpenTelemetry
|
|
87
|
+
|
|
88
|
+
The gateway emits a `slack.turn` span and injects W3C `traceparent`, `tracestate`, and `baggage` into Bee turn telemetry hints. Configure `OTEL_SERVICE_NAME`, `OTEL_EXPORTER_OTLP_ENDPOINT` (or `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`), and standard `OTEL_RESOURCE_ATTRIBUTES` as needed. Export stays disabled when no OTLP endpoint is configured; `OTEL_SDK_DISABLED=true` disables it explicitly.
|
|
89
|
+
|
|
90
|
+
Message contents, user details, attachments, and Slack payloads are not recorded as span attributes.
|
|
91
|
+
|
|
86
92
|
## Local development
|
|
87
93
|
|
|
88
94
|
For local manual testing, copy `local.config.example.json` to
|
package/dist/gateway.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAiC,kBAAkB,EAAuB,MAAM,YAAY,CAAC;AAazG,qBAAa,YAAY;IAeZ,OAAO,CAAC,MAAM;IAd1B,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,MAAM,CAAyC;IACvD,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,iBAAiB,CAA2B;IACpD,OAAO,CAAC,6BAA6B,CAAS;IAE9C,YAAoB,MAAM,EAAE,kBAAkB,EAa7C;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAsB3B;IAED,OAAO,CAAC,kBAAkB;IAqD1B,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,kBAAkB;YAmEZ,cAAc;IA0C5B,OAAO,CAAC,iBAAiB;IAoCzB,OAAO,CAAC,iBAAiB;YAOX,mBAAmB;YA+BnB,UAAU;YAsBV,aAAa;CA0B3B;AAaD,wBAAsB,mBAAmB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5E","sourcesContent":["import {\n\ttype AttachmentRef,\n\tBeeGatewayEngine,\n\ttype BeeResolvedTurn,\n\ttype BeeWorkerClient,\n\ttype BlobStore,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { SocketModeClient } from \"@slack/socket-mode\";\nimport { WebClient } from \"@slack/web-api\";\nimport { join } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport { createHandoffServer, SlackHandoffController } from \"./handoff.js\";\nimport { HandoffReplyCache } from \"./handoff-reply-cache.js\";\nimport * as log from \"./log.js\";\nimport { resolveRoute, resolveStreamingPreference } from \"./router.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport type { ResolvedSlackRoute, SlackFile, SlackGatewayConfig, SlackInboundMessage } from \"./types.js\";\n\ninterface SlackUser {\n\tid: string;\n\tuserName: string;\n\tdisplayName: string;\n}\n\ninterface SlackChannel {\n\tid: string;\n\tname: string;\n}\n\nexport class SlackGateway {\n\tprivate socketClient: SocketModeClient;\n\tprivate webClient: WebClient;\n\tprivate users = new Map<string, SlackUser>();\n\tprivate channels = new Map<string, SlackChannel>();\n\tprivate botUserId: string | null = null;\n\tprivate teamId: string | null = null;\n\tprivate teamName: string | null = null;\n\tprivate blobStore: BlobStore;\n\tprivate sink: SlackSink;\n\tprivate engine: BeeGatewayEngine<string> | null = null;\n\tprivate workerClient: BeeWorkerClient | null = null;\n\tprivate handoffReplyCache = new HandoffReplyCache();\n\tprivate slackThreadHistoryUnavailable = false;\n\n\tconstructor(private config: SlackGatewayConfig) {\n\t\tthis.socketClient = new SocketModeClient({ appToken: config.appToken });\n\t\tthis.webClient = new WebClient(config.botToken);\n\t\tthis.blobStore = new LocalFileBlobStore(\n\t\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t\t);\n\t\tthis.sink = new SlackSink(this.webClient, this.blobStore, {\n\t\t\tposted: (target, ref, text) => this.handoffReplyCache.recordPosted(target, ref, text, this.botDisplayName()),\n\t\t\tupdated: (target, ref, text) => this.handoffReplyCache.recordUpdated(target, ref, text, this.botDisplayName()),\n\t\t});\n\t}\n\n\tasync start(): Promise<void> {\n\t\tthis.workerClient = await createNatsBeeClient(this.config.nats);\n\t\tthis.engine = new BeeGatewayEngine({\n\t\t\tsink: this.sink,\n\t\t\tworkerClient: this.workerClient,\n\t\t\tlogger: {\n\t\t\t\tinfo: log.logInfo,\n\t\t\t\twarn: log.logWarning,\n\t\t\t\terror: log.logError,\n\t\t\t},\n\t\t});\n\t\tconst auth = await this.webClient.auth.test();\n\t\tthis.botUserId = auth.user_id as string;\n\t\tthis.teamId = auth.team_id as string;\n\t\tthis.teamName = (auth.team as string | undefined) || null;\n\t\tawait Promise.all([this.fetchUsers(), this.fetchChannels()]);\n\t\tthis.startHandoffServer();\n\t\tthis.setupEventHandlers();\n\t\tawait this.socketClient.start();\n\t\tlog.logInfo(\n\t\t\t`Connected as ${this.botUserId} in ${this.teamId}; loaded ${this.channels.size} channels and ${this.users.size} users`,\n\t\t);\n\t}\n\n\tprivate startHandoffServer(): void {\n\t\tconst handoffConfig = this.config.handoff;\n\t\tif (!handoffConfig?.enabled) return;\n\t\tif (!this.teamId || !this.engine) throw new Error(\"Gateway must be initialized before handoff server startup\");\n\t\tconst controller = new SlackHandoffController(handoffConfig, {\n\t\t\tteamId: this.teamId,\n\t\t\tpostRootMessage: async (channelId, text) => this.sink.postMessage({ channelId }, text),\n\t\t\tgetPermalink: async (channelId, messageTs) => {\n\t\t\t\tconst result = await this.webClient.chat.getPermalink({ channel: channelId, message_ts: messageTs });\n\t\t\t\tif (!result.permalink) throw new Error(\"Slack did not return a permalink\");\n\t\t\t\treturn result.permalink;\n\t\t\t},\n\t\t\tdispatch: (input) => this.engine!.dispatch(input),\n\t\t\tgetReplies: async (channelId, threadTs) => {\n\t\t\t\tif (this.slackThreadHistoryUnavailable) return this.handoffReplyCache.replies(channelId, threadTs);\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.webClient.conversations.replies({\n\t\t\t\t\t\tchannel: channelId,\n\t\t\t\t\t\tts: threadTs,\n\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t});\n\t\t\t\t\treturn (result.messages || []).map((message) => {\n\t\t\t\t\t\tconst typed = message as typeof message & {\n\t\t\t\t\t\t\tbot_id?: string;\n\t\t\t\t\t\t\tbot_profile?: { name?: string };\n\t\t\t\t\t\t\tusername?: string;\n\t\t\t\t\t\t\tthread_ts?: string;\n\t\t\t\t\t\t};\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tts: String(typed.ts || \"\"),\n\t\t\t\t\t\t\tthreadTs: typed.thread_ts,\n\t\t\t\t\t\t\ttext: typed.text || \"\",\n\t\t\t\t\t\t\tauthor: typed.user\n\t\t\t\t\t\t\t\t? this.users.get(typed.user)?.displayName || this.users.get(typed.user)?.userName || typed.user\n\t\t\t\t\t\t\t\t: typed.bot_profile?.name || typed.username || \"Bot\",\n\t\t\t\t\t\t\tisBot: Boolean(typed.bot_id),\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst errorCode = slackErrorCode(error);\n\t\t\t\t\tif (!isSlackHistoryPermissionError(errorCode)) throw error;\n\t\t\t\t\tthis.slackThreadHistoryUnavailable = true;\n\t\t\t\t\tlog.logWarning(`Slack thread history unavailable (${errorCode}); using outbound Bee reply cache`);\n\t\t\t\t\treturn this.handoffReplyCache.replies(channelId, threadTs);\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t\tconst server = createHandoffServer(controller);\n\t\tconst host = handoffConfig.host || \"0.0.0.0\";\n\t\tconst port = handoffConfig.port || 8080;\n\t\tserver.listen(port, host, () => log.logInfo(`bee-slack handoff listening on http://${host}:${port}`));\n\t}\n\n\tprivate botDisplayName(): string {\n\t\tif (!this.botUserId) return \"Bee\";\n\t\tconst bot = this.users.get(this.botUserId);\n\t\treturn bot?.displayName || bot?.userName || \"Bee\";\n\t}\n\n\tprivate setupEventHandlers(): void {\n\t\tthis.socketClient.on(\"app_mention\", async ({ event, ack, body }) => {\n\t\t\tawait ack();\n\t\t\tconst e = event as {\n\t\t\t\ttext: string;\n\t\t\t\tchannel: string;\n\t\t\t\tuser: string;\n\t\t\t\tts: string;\n\t\t\t\tthread_ts?: string;\n\t\t\t\tfiles?: SlackFile[];\n\t\t\t};\n\n\t\t\tif (e.channel.startsWith(\"D\")) return;\n\n\t\t\tawait this.enqueueInbound({\n\t\t\t\ttype: \"mention\",\n\t\t\t\tchannelId: e.channel,\n\t\t\t\tchannelName: this.channels.get(e.channel)?.name,\n\t\t\t\tthreadTs: e.thread_ts,\n\t\t\t\tts: e.ts,\n\t\t\t\tuserId: e.user,\n\t\t\t\tteamId: (event as typeof e & { team?: string }).team || (body as { team_id?: string }).team_id,\n\t\t\t\tuserName: this.users.get(e.user)?.userName,\n\t\t\t\tdisplayName: this.users.get(e.user)?.displayName,\n\t\t\t\ttext: e.text.replace(/<@[A-Z0-9]+>/gi, \"\").trim(),\n\t\t\t\tfiles: e.files,\n\t\t\t});\n\t\t});\n\n\t\tthis.socketClient.on(\"message\", async ({ event, ack }) => {\n\t\t\tawait ack();\n\t\t\tconst e = event as {\n\t\t\t\ttext?: string;\n\t\t\t\tchannel: string;\n\t\t\t\tuser?: string;\n\t\t\t\tts: string;\n\t\t\t\tthread_ts?: string;\n\t\t\t\tchannel_type?: string;\n\t\t\t\tsubtype?: string;\n\t\t\t\tbot_id?: string;\n\t\t\t\tfiles?: SlackFile[];\n\t\t\t};\n\n\t\t\tif (e.bot_id || !e.user || e.user === this.botUserId) return;\n\t\t\tif (e.subtype !== undefined && e.subtype !== \"file_share\") return;\n\t\t\tif (!e.text && (!e.files || e.files.length === 0)) return;\n\n\t\t\tconst isDM = e.channel_type === \"im\";\n\t\t\tconst isBotMention = !!this.botUserId && e.text?.includes(`<@${this.botUserId}>`);\n\t\t\tif (!isDM && isBotMention) return;\n\t\t\tif (!isDM) return;\n\n\t\t\tawait this.enqueueInbound({\n\t\t\t\ttype: \"dm\",\n\t\t\t\tchannelId: e.channel,\n\t\t\t\tchannelName: this.channels.get(e.channel)?.name,\n\t\t\t\tthreadTs: e.thread_ts,\n\t\t\t\tts: e.ts,\n\t\t\t\tuserId: e.user,\n\t\t\t\tuserName: this.users.get(e.user)?.userName,\n\t\t\t\tdisplayName: this.users.get(e.user)?.displayName,\n\t\t\t\ttext: (e.text || \"\").replace(/<@[A-Z0-9]+>/gi, \"\").trim(),\n\t\t\t\tfiles: e.files,\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async enqueueInbound(message: SlackInboundMessage): Promise<void> {\n\t\tif (!this.botUserId || !this.teamId) {\n\t\t\tthrow new Error(\"Gateway has not been initialized\");\n\t\t}\n\t\tif (!this.engine) {\n\t\t\tthrow new Error(\"Gateway engine has not been initialized\");\n\t\t}\n\n\t\ttry {\n\t\t\tconst resolved = resolveRoute(this.config.routes, message, {\n\t\t\t\tbotUserId: this.botUserId,\n\t\t\t\tteamId: this.teamId,\n\t\t\t\tteamName: this.teamName || undefined,\n\t\t\t});\n\t\t\tif (!resolved) {\n\t\t\t\tlog.logWarning(`No route for channel ${message.channelId}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst outputTarget = this.buildOutputTarget(message, resolved);\n\t\t\tif (message.text.trim().toLowerCase() === \"stop\") {\n\t\t\t\tconst stopped = await this.engine.stopActiveRun(resolved.sessionId);\n\t\t\t\tawait this.sink.postMessage(outputTarget, stopped ? \"Stopping active run.\" : \"Nothing running.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst attachments = await this.downloadAttachments(resolved, message);\n\t\t\tconst input = this.buildResolvedTurn(message, resolved, attachments, outputTarget);\n\t\t\tthis.engine.dispatch(input);\n\t\t} catch (error) {\n\t\t\tconst messageText = error instanceof Error ? error.message : String(error);\n\t\t\tlog.logError(`Failed to normalize Slack inbound message ${message.ts}`, messageText);\n\t\t\tawait this.sink.postMessage(\n\t\t\t\t{\n\t\t\t\t\tchannelId: message.channelId,\n\t\t\t\t\tthreadId: message.threadTs || (message.type === \"mention\" ? message.ts : undefined),\n\t\t\t\t},\n\t\t\t\t`_Gateway error: ${messageText}_`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate buildResolvedTurn(\n\t\tmessage: SlackInboundMessage,\n\t\tresolved: ResolvedSlackRoute,\n\t\tattachments: AttachmentRef[],\n\t\toutput: TransportOutputTarget,\n\t): BeeResolvedTurn {\n\t\tif (!this.teamId || !this.botUserId) {\n\t\t\tthrow new Error(\"Missing Slack gateway identity\");\n\t\t}\n\n\t\treturn {\n\t\t\tsessionId: resolved.sessionId,\n\t\t\tthreadId: resolved.threadTs,\n\t\t\tworker: resolved.route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId: resolved.conversationId,\n\t\t\t},\n\t\t\tactor: {\n\t\t\t\tuserId: message.userId,\n\t\t\t\tuserName: message.userName,\n\t\t\t\tdisplayName: message.displayName,\n\t\t\t},\n\t\t\tmessage: {\n\t\t\t\ttext: message.text,\n\t\t\t},\n\t\t\tattachments,\n\t\t\tstreaming: resolveStreamingPreference(resolved.route, message, {\n\t\t\t\tbotUserId: this.botUserId,\n\t\t\t\tteamId: this.teamId,\n\t\t\t\tteamName: this.teamName || undefined,\n\t\t\t}),\n\t\t\toutput,\n\t\t};\n\t}\n\n\tprivate buildOutputTarget(message: SlackInboundMessage, resolved: ResolvedSlackRoute): TransportOutputTarget {\n\t\treturn {\n\t\t\tchannelId: message.channelId,\n\t\t\tthreadId: resolved.threadTs || (message.type === \"mention\" ? message.ts : undefined),\n\t\t};\n\t}\n\n\tprivate async downloadAttachments(\n\t\tresolved: ResolvedSlackRoute,\n\t\tmessage: SlackInboundMessage,\n\t): Promise<AttachmentRef[]> {\n\t\tconst files = message.files || [];\n\t\tif (files.length === 0) return [];\n\n\t\tconst attachments: AttachmentRef[] = [];\n\t\tfor (const file of files) {\n\t\t\tconst url = file.url_private_download || file.url_private;\n\t\t\tif (!url || !file.name) continue;\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { authorization: `Bearer ${this.config.botToken}` },\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`Failed to download Slack attachment ${file.name}: ${response.status}`);\n\t\t\t}\n\t\t\tconst bytes = new Uint8Array(await response.arrayBuffer());\n\t\t\tattachments.push(\n\t\t\t\tawait this.blobStore.put({\n\t\t\t\t\tnamespace: `incoming/slack/${resolved.sessionId}`,\n\t\t\t\t\tname: file.name,\n\t\t\t\t\ttitle: file.name,\n\t\t\t\t\tmimeType: file.mimetype,\n\t\t\t\t\tdata: bytes,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn attachments;\n\t}\n\n\tprivate async fetchUsers(): Promise<void> {\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = await this.webClient.users.list({ limit: 200, cursor });\n\t\t\tconst members = result.members as\n\t\t\t\t| Array<{ id?: string; name?: string; real_name?: string; deleted?: boolean }>\n\t\t\t\t| undefined;\n\t\t\tif (members) {\n\t\t\t\tfor (const user of members) {\n\t\t\t\t\tif (user.id && user.name && !user.deleted) {\n\t\t\t\t\t\tthis.users.set(user.id, {\n\t\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\t\tuserName: user.name,\n\t\t\t\t\t\t\tdisplayName: user.real_name || user.name,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor = result.response_metadata?.next_cursor;\n\t\t} while (cursor);\n\t}\n\n\tprivate async fetchChannels(): Promise<void> {\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = await this.webClient.conversations.list({\n\t\t\t\ttypes: \"public_channel,private_channel,im\",\n\t\t\t\texclude_archived: true,\n\t\t\t\tlimit: 200,\n\t\t\t\tcursor,\n\t\t\t});\n\t\t\tconst channels = result.channels as Array<{ id?: string; name?: string; user?: string }> | undefined;\n\t\t\tif (channels) {\n\t\t\t\tfor (const channel of channels) {\n\t\t\t\t\tif (!channel.id) continue;\n\t\t\t\t\tif (channel.name) {\n\t\t\t\t\t\tthis.channels.set(channel.id, { id: channel.id, name: channel.name });\n\t\t\t\t\t} else if (channel.user && this.users.has(channel.user)) {\n\t\t\t\t\t\tthis.channels.set(channel.id, {\n\t\t\t\t\t\t\tid: channel.id,\n\t\t\t\t\t\t\tname: `DM:${this.users.get(channel.user)!.userName}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor = result.response_metadata?.next_cursor;\n\t\t} while (cursor);\n\t}\n}\n\nfunction slackErrorCode(error: unknown): string | undefined {\n\tif (!error || typeof error !== \"object\" || !(\"data\" in error)) return undefined;\n\tconst data = error.data;\n\tif (!data || typeof data !== \"object\" || !(\"error\" in data)) return undefined;\n\treturn typeof data.error === \"string\" ? data.error : undefined;\n}\n\nfunction isSlackHistoryPermissionError(errorCode: string | undefined): boolean {\n\treturn errorCode === \"missing_scope\" || errorCode === \"no_permission\" || errorCode === \"not_allowed_token_type\";\n}\n\nexport async function startGatewayFromEnv(configPath?: string): Promise<void> {\n\tconst config = loadConfig(configPath);\n\tconst gateway = new SlackGateway(config);\n\tawait gateway.start();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAiC,kBAAkB,EAAuB,MAAM,YAAY,CAAC;AAazG,qBAAa,YAAY;IAeZ,OAAO,CAAC,MAAM;IAd1B,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,MAAM,CAAyC;IACvD,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,iBAAiB,CAA2B;IACpD,OAAO,CAAC,6BAA6B,CAAS;IAE9C,YAAoB,MAAM,EAAE,kBAAkB,EAa7C;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAsB3B;IAED,OAAO,CAAC,kBAAkB;IAqD1B,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,kBAAkB;YAmEZ,cAAc;IA6C5B,OAAO,CAAC,iBAAiB;IAoCzB,OAAO,CAAC,iBAAiB;YAOX,mBAAmB;YA+BnB,UAAU;YAsBV,aAAa;CA0B3B;AAaD,wBAAsB,mBAAmB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5E","sourcesContent":["import {\n\ttype AttachmentRef,\n\tBeeGatewayEngine,\n\ttype BeeResolvedTurn,\n\ttype BeeWorkerClient,\n\ttype BlobStore,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { SocketModeClient } from \"@slack/socket-mode\";\nimport { WebClient } from \"@slack/web-api\";\nimport { join } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport { createHandoffServer, SlackHandoffController } from \"./handoff.js\";\nimport { HandoffReplyCache } from \"./handoff-reply-cache.js\";\nimport * as log from \"./log.js\";\nimport { resolveRoute, resolveStreamingPreference } from \"./router.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport { dispatchTracedTurn } from \"./telemetry.js\";\nimport type { ResolvedSlackRoute, SlackFile, SlackGatewayConfig, SlackInboundMessage } from \"./types.js\";\n\ninterface SlackUser {\n\tid: string;\n\tuserName: string;\n\tdisplayName: string;\n}\n\ninterface SlackChannel {\n\tid: string;\n\tname: string;\n}\n\nexport class SlackGateway {\n\tprivate socketClient: SocketModeClient;\n\tprivate webClient: WebClient;\n\tprivate users = new Map<string, SlackUser>();\n\tprivate channels = new Map<string, SlackChannel>();\n\tprivate botUserId: string | null = null;\n\tprivate teamId: string | null = null;\n\tprivate teamName: string | null = null;\n\tprivate blobStore: BlobStore;\n\tprivate sink: SlackSink;\n\tprivate engine: BeeGatewayEngine<string> | null = null;\n\tprivate workerClient: BeeWorkerClient | null = null;\n\tprivate handoffReplyCache = new HandoffReplyCache();\n\tprivate slackThreadHistoryUnavailable = false;\n\n\tconstructor(private config: SlackGatewayConfig) {\n\t\tthis.socketClient = new SocketModeClient({ appToken: config.appToken });\n\t\tthis.webClient = new WebClient(config.botToken);\n\t\tthis.blobStore = new LocalFileBlobStore(\n\t\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t\t);\n\t\tthis.sink = new SlackSink(this.webClient, this.blobStore, {\n\t\t\tposted: (target, ref, text) => this.handoffReplyCache.recordPosted(target, ref, text, this.botDisplayName()),\n\t\t\tupdated: (target, ref, text) => this.handoffReplyCache.recordUpdated(target, ref, text, this.botDisplayName()),\n\t\t});\n\t}\n\n\tasync start(): Promise<void> {\n\t\tthis.workerClient = await createNatsBeeClient(this.config.nats);\n\t\tthis.engine = new BeeGatewayEngine({\n\t\t\tsink: this.sink,\n\t\t\tworkerClient: this.workerClient,\n\t\t\tlogger: {\n\t\t\t\tinfo: log.logInfo,\n\t\t\t\twarn: log.logWarning,\n\t\t\t\terror: log.logError,\n\t\t\t},\n\t\t});\n\t\tconst auth = await this.webClient.auth.test();\n\t\tthis.botUserId = auth.user_id as string;\n\t\tthis.teamId = auth.team_id as string;\n\t\tthis.teamName = (auth.team as string | undefined) || null;\n\t\tawait Promise.all([this.fetchUsers(), this.fetchChannels()]);\n\t\tthis.startHandoffServer();\n\t\tthis.setupEventHandlers();\n\t\tawait this.socketClient.start();\n\t\tlog.logInfo(\n\t\t\t`Connected as ${this.botUserId} in ${this.teamId}; loaded ${this.channels.size} channels and ${this.users.size} users`,\n\t\t);\n\t}\n\n\tprivate startHandoffServer(): void {\n\t\tconst handoffConfig = this.config.handoff;\n\t\tif (!handoffConfig?.enabled) return;\n\t\tif (!this.teamId || !this.engine) throw new Error(\"Gateway must be initialized before handoff server startup\");\n\t\tconst controller = new SlackHandoffController(handoffConfig, {\n\t\t\tteamId: this.teamId,\n\t\t\tpostRootMessage: async (channelId, text) => this.sink.postMessage({ channelId }, text),\n\t\t\tgetPermalink: async (channelId, messageTs) => {\n\t\t\t\tconst result = await this.webClient.chat.getPermalink({ channel: channelId, message_ts: messageTs });\n\t\t\t\tif (!result.permalink) throw new Error(\"Slack did not return a permalink\");\n\t\t\t\treturn result.permalink;\n\t\t\t},\n\t\t\tdispatch: (input) => this.engine!.dispatch(input),\n\t\t\tgetReplies: async (channelId, threadTs) => {\n\t\t\t\tif (this.slackThreadHistoryUnavailable) return this.handoffReplyCache.replies(channelId, threadTs);\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.webClient.conversations.replies({\n\t\t\t\t\t\tchannel: channelId,\n\t\t\t\t\t\tts: threadTs,\n\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t});\n\t\t\t\t\treturn (result.messages || []).map((message) => {\n\t\t\t\t\t\tconst typed = message as typeof message & {\n\t\t\t\t\t\t\tbot_id?: string;\n\t\t\t\t\t\t\tbot_profile?: { name?: string };\n\t\t\t\t\t\t\tusername?: string;\n\t\t\t\t\t\t\tthread_ts?: string;\n\t\t\t\t\t\t};\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tts: String(typed.ts || \"\"),\n\t\t\t\t\t\t\tthreadTs: typed.thread_ts,\n\t\t\t\t\t\t\ttext: typed.text || \"\",\n\t\t\t\t\t\t\tauthor: typed.user\n\t\t\t\t\t\t\t\t? this.users.get(typed.user)?.displayName || this.users.get(typed.user)?.userName || typed.user\n\t\t\t\t\t\t\t\t: typed.bot_profile?.name || typed.username || \"Bot\",\n\t\t\t\t\t\t\tisBot: Boolean(typed.bot_id),\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst errorCode = slackErrorCode(error);\n\t\t\t\t\tif (!isSlackHistoryPermissionError(errorCode)) throw error;\n\t\t\t\t\tthis.slackThreadHistoryUnavailable = true;\n\t\t\t\t\tlog.logWarning(`Slack thread history unavailable (${errorCode}); using outbound Bee reply cache`);\n\t\t\t\t\treturn this.handoffReplyCache.replies(channelId, threadTs);\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t\tconst server = createHandoffServer(controller);\n\t\tconst host = handoffConfig.host || \"0.0.0.0\";\n\t\tconst port = handoffConfig.port || 8080;\n\t\tserver.listen(port, host, () => log.logInfo(`bee-slack handoff listening on http://${host}:${port}`));\n\t}\n\n\tprivate botDisplayName(): string {\n\t\tif (!this.botUserId) return \"Bee\";\n\t\tconst bot = this.users.get(this.botUserId);\n\t\treturn bot?.displayName || bot?.userName || \"Bee\";\n\t}\n\n\tprivate setupEventHandlers(): void {\n\t\tthis.socketClient.on(\"app_mention\", async ({ event, ack, body }) => {\n\t\t\tawait ack();\n\t\t\tconst e = event as {\n\t\t\t\ttext: string;\n\t\t\t\tchannel: string;\n\t\t\t\tuser: string;\n\t\t\t\tts: string;\n\t\t\t\tthread_ts?: string;\n\t\t\t\tfiles?: SlackFile[];\n\t\t\t};\n\n\t\t\tif (e.channel.startsWith(\"D\")) return;\n\n\t\t\tawait this.enqueueInbound({\n\t\t\t\ttype: \"mention\",\n\t\t\t\tchannelId: e.channel,\n\t\t\t\tchannelName: this.channels.get(e.channel)?.name,\n\t\t\t\tthreadTs: e.thread_ts,\n\t\t\t\tts: e.ts,\n\t\t\t\tuserId: e.user,\n\t\t\t\tteamId: (event as typeof e & { team?: string }).team || (body as { team_id?: string }).team_id,\n\t\t\t\tuserName: this.users.get(e.user)?.userName,\n\t\t\t\tdisplayName: this.users.get(e.user)?.displayName,\n\t\t\t\ttext: e.text.replace(/<@[A-Z0-9]+>/gi, \"\").trim(),\n\t\t\t\tfiles: e.files,\n\t\t\t});\n\t\t});\n\n\t\tthis.socketClient.on(\"message\", async ({ event, ack }) => {\n\t\t\tawait ack();\n\t\t\tconst e = event as {\n\t\t\t\ttext?: string;\n\t\t\t\tchannel: string;\n\t\t\t\tuser?: string;\n\t\t\t\tts: string;\n\t\t\t\tthread_ts?: string;\n\t\t\t\tchannel_type?: string;\n\t\t\t\tsubtype?: string;\n\t\t\t\tbot_id?: string;\n\t\t\t\tfiles?: SlackFile[];\n\t\t\t};\n\n\t\t\tif (e.bot_id || !e.user || e.user === this.botUserId) return;\n\t\t\tif (e.subtype !== undefined && e.subtype !== \"file_share\") return;\n\t\t\tif (!e.text && (!e.files || e.files.length === 0)) return;\n\n\t\t\tconst isDM = e.channel_type === \"im\";\n\t\t\tconst isBotMention = !!this.botUserId && e.text?.includes(`<@${this.botUserId}>`);\n\t\t\tif (!isDM && isBotMention) return;\n\t\t\tif (!isDM) return;\n\n\t\t\tawait this.enqueueInbound({\n\t\t\t\ttype: \"dm\",\n\t\t\t\tchannelId: e.channel,\n\t\t\t\tchannelName: this.channels.get(e.channel)?.name,\n\t\t\t\tthreadTs: e.thread_ts,\n\t\t\t\tts: e.ts,\n\t\t\t\tuserId: e.user,\n\t\t\t\tuserName: this.users.get(e.user)?.userName,\n\t\t\t\tdisplayName: this.users.get(e.user)?.displayName,\n\t\t\t\ttext: (e.text || \"\").replace(/<@[A-Z0-9]+>/gi, \"\").trim(),\n\t\t\t\tfiles: e.files,\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async enqueueInbound(message: SlackInboundMessage): Promise<void> {\n\t\tif (!this.botUserId || !this.teamId) {\n\t\t\tthrow new Error(\"Gateway has not been initialized\");\n\t\t}\n\t\tif (!this.engine) {\n\t\t\tthrow new Error(\"Gateway engine has not been initialized\");\n\t\t}\n\t\tconst engine = this.engine;\n\n\t\ttry {\n\t\t\tconst resolved = resolveRoute(this.config.routes, message, {\n\t\t\t\tbotUserId: this.botUserId,\n\t\t\t\tteamId: this.teamId,\n\t\t\t\tteamName: this.teamName || undefined,\n\t\t\t});\n\t\t\tif (!resolved) {\n\t\t\t\tlog.logWarning(`No route for channel ${message.channelId}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst outputTarget = this.buildOutputTarget(message, resolved);\n\t\t\tif (message.text.trim().toLowerCase() === \"stop\") {\n\t\t\t\tconst stopped = await this.engine.stopActiveRun(resolved.sessionId);\n\t\t\t\tawait this.sink.postMessage(outputTarget, stopped ? \"Stopping active run.\" : \"Nothing running.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst attachments = await this.downloadAttachments(resolved, message);\n\t\t\tconst input = this.buildResolvedTurn(message, resolved, attachments, outputTarget);\n\t\t\tdispatchTracedTurn({ \"bee.session.id\": input.sessionId, \"bee.transport\": \"slack\" }, (telemetry) => {\n\t\t\t\tengine.dispatch({ ...input, telemetry });\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst messageText = error instanceof Error ? error.message : String(error);\n\t\t\tlog.logError(`Failed to normalize Slack inbound message ${message.ts}`, messageText);\n\t\t\tawait this.sink.postMessage(\n\t\t\t\t{\n\t\t\t\t\tchannelId: message.channelId,\n\t\t\t\t\tthreadId: message.threadTs || (message.type === \"mention\" ? message.ts : undefined),\n\t\t\t\t},\n\t\t\t\t`_Gateway error: ${messageText}_`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate buildResolvedTurn(\n\t\tmessage: SlackInboundMessage,\n\t\tresolved: ResolvedSlackRoute,\n\t\tattachments: AttachmentRef[],\n\t\toutput: TransportOutputTarget,\n\t): BeeResolvedTurn {\n\t\tif (!this.teamId || !this.botUserId) {\n\t\t\tthrow new Error(\"Missing Slack gateway identity\");\n\t\t}\n\n\t\treturn {\n\t\t\tsessionId: resolved.sessionId,\n\t\t\tthreadId: resolved.threadTs,\n\t\t\tworker: resolved.route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId: resolved.conversationId,\n\t\t\t},\n\t\t\tactor: {\n\t\t\t\tuserId: message.userId,\n\t\t\t\tuserName: message.userName,\n\t\t\t\tdisplayName: message.displayName,\n\t\t\t},\n\t\t\tmessage: {\n\t\t\t\ttext: message.text,\n\t\t\t},\n\t\t\tattachments,\n\t\t\tstreaming: resolveStreamingPreference(resolved.route, message, {\n\t\t\t\tbotUserId: this.botUserId,\n\t\t\t\tteamId: this.teamId,\n\t\t\t\tteamName: this.teamName || undefined,\n\t\t\t}),\n\t\t\toutput,\n\t\t};\n\t}\n\n\tprivate buildOutputTarget(message: SlackInboundMessage, resolved: ResolvedSlackRoute): TransportOutputTarget {\n\t\treturn {\n\t\t\tchannelId: message.channelId,\n\t\t\tthreadId: resolved.threadTs || (message.type === \"mention\" ? message.ts : undefined),\n\t\t};\n\t}\n\n\tprivate async downloadAttachments(\n\t\tresolved: ResolvedSlackRoute,\n\t\tmessage: SlackInboundMessage,\n\t): Promise<AttachmentRef[]> {\n\t\tconst files = message.files || [];\n\t\tif (files.length === 0) return [];\n\n\t\tconst attachments: AttachmentRef[] = [];\n\t\tfor (const file of files) {\n\t\t\tconst url = file.url_private_download || file.url_private;\n\t\t\tif (!url || !file.name) continue;\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { authorization: `Bearer ${this.config.botToken}` },\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`Failed to download Slack attachment ${file.name}: ${response.status}`);\n\t\t\t}\n\t\t\tconst bytes = new Uint8Array(await response.arrayBuffer());\n\t\t\tattachments.push(\n\t\t\t\tawait this.blobStore.put({\n\t\t\t\t\tnamespace: `incoming/slack/${resolved.sessionId}`,\n\t\t\t\t\tname: file.name,\n\t\t\t\t\ttitle: file.name,\n\t\t\t\t\tmimeType: file.mimetype,\n\t\t\t\t\tdata: bytes,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn attachments;\n\t}\n\n\tprivate async fetchUsers(): Promise<void> {\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = await this.webClient.users.list({ limit: 200, cursor });\n\t\t\tconst members = result.members as\n\t\t\t\t| Array<{ id?: string; name?: string; real_name?: string; deleted?: boolean }>\n\t\t\t\t| undefined;\n\t\t\tif (members) {\n\t\t\t\tfor (const user of members) {\n\t\t\t\t\tif (user.id && user.name && !user.deleted) {\n\t\t\t\t\t\tthis.users.set(user.id, {\n\t\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\t\tuserName: user.name,\n\t\t\t\t\t\t\tdisplayName: user.real_name || user.name,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor = result.response_metadata?.next_cursor;\n\t\t} while (cursor);\n\t}\n\n\tprivate async fetchChannels(): Promise<void> {\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = await this.webClient.conversations.list({\n\t\t\t\ttypes: \"public_channel,private_channel,im\",\n\t\t\t\texclude_archived: true,\n\t\t\t\tlimit: 200,\n\t\t\t\tcursor,\n\t\t\t});\n\t\t\tconst channels = result.channels as Array<{ id?: string; name?: string; user?: string }> | undefined;\n\t\t\tif (channels) {\n\t\t\t\tfor (const channel of channels) {\n\t\t\t\t\tif (!channel.id) continue;\n\t\t\t\t\tif (channel.name) {\n\t\t\t\t\t\tthis.channels.set(channel.id, { id: channel.id, name: channel.name });\n\t\t\t\t\t} else if (channel.user && this.users.has(channel.user)) {\n\t\t\t\t\t\tthis.channels.set(channel.id, {\n\t\t\t\t\t\t\tid: channel.id,\n\t\t\t\t\t\t\tname: `DM:${this.users.get(channel.user)!.userName}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor = result.response_metadata?.next_cursor;\n\t\t} while (cursor);\n\t}\n}\n\nfunction slackErrorCode(error: unknown): string | undefined {\n\tif (!error || typeof error !== \"object\" || !(\"data\" in error)) return undefined;\n\tconst data = error.data;\n\tif (!data || typeof data !== \"object\" || !(\"error\" in data)) return undefined;\n\treturn typeof data.error === \"string\" ? data.error : undefined;\n}\n\nfunction isSlackHistoryPermissionError(errorCode: string | undefined): boolean {\n\treturn errorCode === \"missing_scope\" || errorCode === \"no_permission\" || errorCode === \"not_allowed_token_type\";\n}\n\nexport async function startGatewayFromEnv(configPath?: string): Promise<void> {\n\tconst config = loadConfig(configPath);\n\tconst gateway = new SlackGateway(config);\n\tawait gateway.start();\n}\n"]}
|
package/dist/gateway.js
CHANGED
|
@@ -8,6 +8,7 @@ import { HandoffReplyCache } from "./handoff-reply-cache.js";
|
|
|
8
8
|
import * as log from "./log.js";
|
|
9
9
|
import { resolveRoute, resolveStreamingPreference } from "./router.js";
|
|
10
10
|
import { SlackSink } from "./slack-sink.js";
|
|
11
|
+
import { dispatchTracedTurn } from "./telemetry.js";
|
|
11
12
|
export class SlackGateway {
|
|
12
13
|
config;
|
|
13
14
|
socketClient;
|
|
@@ -172,6 +173,7 @@ export class SlackGateway {
|
|
|
172
173
|
if (!this.engine) {
|
|
173
174
|
throw new Error("Gateway engine has not been initialized");
|
|
174
175
|
}
|
|
176
|
+
const engine = this.engine;
|
|
175
177
|
try {
|
|
176
178
|
const resolved = resolveRoute(this.config.routes, message, {
|
|
177
179
|
botUserId: this.botUserId,
|
|
@@ -190,7 +192,9 @@ export class SlackGateway {
|
|
|
190
192
|
}
|
|
191
193
|
const attachments = await this.downloadAttachments(resolved, message);
|
|
192
194
|
const input = this.buildResolvedTurn(message, resolved, attachments, outputTarget);
|
|
193
|
-
|
|
195
|
+
dispatchTracedTurn({ "bee.session.id": input.sessionId, "bee.transport": "slack" }, (telemetry) => {
|
|
196
|
+
engine.dispatch({ ...input, telemetry });
|
|
197
|
+
});
|
|
194
198
|
}
|
|
195
199
|
catch (error) {
|
|
196
200
|
const messageText = error instanceof Error ? error.message : String(error);
|
package/dist/gateway.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype AttachmentRef,\n\tBeeGatewayEngine,\n\ttype BeeResolvedTurn,\n\ttype BeeWorkerClient,\n\ttype BlobStore,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { SocketModeClient } from \"@slack/socket-mode\";\nimport { WebClient } from \"@slack/web-api\";\nimport { join } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport { createHandoffServer, SlackHandoffController } from \"./handoff.js\";\nimport { HandoffReplyCache } from \"./handoff-reply-cache.js\";\nimport * as log from \"./log.js\";\nimport { resolveRoute, resolveStreamingPreference } from \"./router.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport type { ResolvedSlackRoute, SlackFile, SlackGatewayConfig, SlackInboundMessage } from \"./types.js\";\n\ninterface SlackUser {\n\tid: string;\n\tuserName: string;\n\tdisplayName: string;\n}\n\ninterface SlackChannel {\n\tid: string;\n\tname: string;\n}\n\nexport class SlackGateway {\n\tprivate socketClient: SocketModeClient;\n\tprivate webClient: WebClient;\n\tprivate users = new Map<string, SlackUser>();\n\tprivate channels = new Map<string, SlackChannel>();\n\tprivate botUserId: string | null = null;\n\tprivate teamId: string | null = null;\n\tprivate teamName: string | null = null;\n\tprivate blobStore: BlobStore;\n\tprivate sink: SlackSink;\n\tprivate engine: BeeGatewayEngine<string> | null = null;\n\tprivate workerClient: BeeWorkerClient | null = null;\n\tprivate handoffReplyCache = new HandoffReplyCache();\n\tprivate slackThreadHistoryUnavailable = false;\n\n\tconstructor(private config: SlackGatewayConfig) {\n\t\tthis.socketClient = new SocketModeClient({ appToken: config.appToken });\n\t\tthis.webClient = new WebClient(config.botToken);\n\t\tthis.blobStore = new LocalFileBlobStore(\n\t\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t\t);\n\t\tthis.sink = new SlackSink(this.webClient, this.blobStore, {\n\t\t\tposted: (target, ref, text) => this.handoffReplyCache.recordPosted(target, ref, text, this.botDisplayName()),\n\t\t\tupdated: (target, ref, text) => this.handoffReplyCache.recordUpdated(target, ref, text, this.botDisplayName()),\n\t\t});\n\t}\n\n\tasync start(): Promise<void> {\n\t\tthis.workerClient = await createNatsBeeClient(this.config.nats);\n\t\tthis.engine = new BeeGatewayEngine({\n\t\t\tsink: this.sink,\n\t\t\tworkerClient: this.workerClient,\n\t\t\tlogger: {\n\t\t\t\tinfo: log.logInfo,\n\t\t\t\twarn: log.logWarning,\n\t\t\t\terror: log.logError,\n\t\t\t},\n\t\t});\n\t\tconst auth = await this.webClient.auth.test();\n\t\tthis.botUserId = auth.user_id as string;\n\t\tthis.teamId = auth.team_id as string;\n\t\tthis.teamName = (auth.team as string | undefined) || null;\n\t\tawait Promise.all([this.fetchUsers(), this.fetchChannels()]);\n\t\tthis.startHandoffServer();\n\t\tthis.setupEventHandlers();\n\t\tawait this.socketClient.start();\n\t\tlog.logInfo(\n\t\t\t`Connected as ${this.botUserId} in ${this.teamId}; loaded ${this.channels.size} channels and ${this.users.size} users`,\n\t\t);\n\t}\n\n\tprivate startHandoffServer(): void {\n\t\tconst handoffConfig = this.config.handoff;\n\t\tif (!handoffConfig?.enabled) return;\n\t\tif (!this.teamId || !this.engine) throw new Error(\"Gateway must be initialized before handoff server startup\");\n\t\tconst controller = new SlackHandoffController(handoffConfig, {\n\t\t\tteamId: this.teamId,\n\t\t\tpostRootMessage: async (channelId, text) => this.sink.postMessage({ channelId }, text),\n\t\t\tgetPermalink: async (channelId, messageTs) => {\n\t\t\t\tconst result = await this.webClient.chat.getPermalink({ channel: channelId, message_ts: messageTs });\n\t\t\t\tif (!result.permalink) throw new Error(\"Slack did not return a permalink\");\n\t\t\t\treturn result.permalink;\n\t\t\t},\n\t\t\tdispatch: (input) => this.engine!.dispatch(input),\n\t\t\tgetReplies: async (channelId, threadTs) => {\n\t\t\t\tif (this.slackThreadHistoryUnavailable) return this.handoffReplyCache.replies(channelId, threadTs);\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.webClient.conversations.replies({\n\t\t\t\t\t\tchannel: channelId,\n\t\t\t\t\t\tts: threadTs,\n\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t});\n\t\t\t\t\treturn (result.messages || []).map((message) => {\n\t\t\t\t\t\tconst typed = message as typeof message & {\n\t\t\t\t\t\t\tbot_id?: string;\n\t\t\t\t\t\t\tbot_profile?: { name?: string };\n\t\t\t\t\t\t\tusername?: string;\n\t\t\t\t\t\t\tthread_ts?: string;\n\t\t\t\t\t\t};\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tts: String(typed.ts || \"\"),\n\t\t\t\t\t\t\tthreadTs: typed.thread_ts,\n\t\t\t\t\t\t\ttext: typed.text || \"\",\n\t\t\t\t\t\t\tauthor: typed.user\n\t\t\t\t\t\t\t\t? this.users.get(typed.user)?.displayName || this.users.get(typed.user)?.userName || typed.user\n\t\t\t\t\t\t\t\t: typed.bot_profile?.name || typed.username || \"Bot\",\n\t\t\t\t\t\t\tisBot: Boolean(typed.bot_id),\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst errorCode = slackErrorCode(error);\n\t\t\t\t\tif (!isSlackHistoryPermissionError(errorCode)) throw error;\n\t\t\t\t\tthis.slackThreadHistoryUnavailable = true;\n\t\t\t\t\tlog.logWarning(`Slack thread history unavailable (${errorCode}); using outbound Bee reply cache`);\n\t\t\t\t\treturn this.handoffReplyCache.replies(channelId, threadTs);\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t\tconst server = createHandoffServer(controller);\n\t\tconst host = handoffConfig.host || \"0.0.0.0\";\n\t\tconst port = handoffConfig.port || 8080;\n\t\tserver.listen(port, host, () => log.logInfo(`bee-slack handoff listening on http://${host}:${port}`));\n\t}\n\n\tprivate botDisplayName(): string {\n\t\tif (!this.botUserId) return \"Bee\";\n\t\tconst bot = this.users.get(this.botUserId);\n\t\treturn bot?.displayName || bot?.userName || \"Bee\";\n\t}\n\n\tprivate setupEventHandlers(): void {\n\t\tthis.socketClient.on(\"app_mention\", async ({ event, ack, body }) => {\n\t\t\tawait ack();\n\t\t\tconst e = event as {\n\t\t\t\ttext: string;\n\t\t\t\tchannel: string;\n\t\t\t\tuser: string;\n\t\t\t\tts: string;\n\t\t\t\tthread_ts?: string;\n\t\t\t\tfiles?: SlackFile[];\n\t\t\t};\n\n\t\t\tif (e.channel.startsWith(\"D\")) return;\n\n\t\t\tawait this.enqueueInbound({\n\t\t\t\ttype: \"mention\",\n\t\t\t\tchannelId: e.channel,\n\t\t\t\tchannelName: this.channels.get(e.channel)?.name,\n\t\t\t\tthreadTs: e.thread_ts,\n\t\t\t\tts: e.ts,\n\t\t\t\tuserId: e.user,\n\t\t\t\tteamId: (event as typeof e & { team?: string }).team || (body as { team_id?: string }).team_id,\n\t\t\t\tuserName: this.users.get(e.user)?.userName,\n\t\t\t\tdisplayName: this.users.get(e.user)?.displayName,\n\t\t\t\ttext: e.text.replace(/<@[A-Z0-9]+>/gi, \"\").trim(),\n\t\t\t\tfiles: e.files,\n\t\t\t});\n\t\t});\n\n\t\tthis.socketClient.on(\"message\", async ({ event, ack }) => {\n\t\t\tawait ack();\n\t\t\tconst e = event as {\n\t\t\t\ttext?: string;\n\t\t\t\tchannel: string;\n\t\t\t\tuser?: string;\n\t\t\t\tts: string;\n\t\t\t\tthread_ts?: string;\n\t\t\t\tchannel_type?: string;\n\t\t\t\tsubtype?: string;\n\t\t\t\tbot_id?: string;\n\t\t\t\tfiles?: SlackFile[];\n\t\t\t};\n\n\t\t\tif (e.bot_id || !e.user || e.user === this.botUserId) return;\n\t\t\tif (e.subtype !== undefined && e.subtype !== \"file_share\") return;\n\t\t\tif (!e.text && (!e.files || e.files.length === 0)) return;\n\n\t\t\tconst isDM = e.channel_type === \"im\";\n\t\t\tconst isBotMention = !!this.botUserId && e.text?.includes(`<@${this.botUserId}>`);\n\t\t\tif (!isDM && isBotMention) return;\n\t\t\tif (!isDM) return;\n\n\t\t\tawait this.enqueueInbound({\n\t\t\t\ttype: \"dm\",\n\t\t\t\tchannelId: e.channel,\n\t\t\t\tchannelName: this.channels.get(e.channel)?.name,\n\t\t\t\tthreadTs: e.thread_ts,\n\t\t\t\tts: e.ts,\n\t\t\t\tuserId: e.user,\n\t\t\t\tuserName: this.users.get(e.user)?.userName,\n\t\t\t\tdisplayName: this.users.get(e.user)?.displayName,\n\t\t\t\ttext: (e.text || \"\").replace(/<@[A-Z0-9]+>/gi, \"\").trim(),\n\t\t\t\tfiles: e.files,\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async enqueueInbound(message: SlackInboundMessage): Promise<void> {\n\t\tif (!this.botUserId || !this.teamId) {\n\t\t\tthrow new Error(\"Gateway has not been initialized\");\n\t\t}\n\t\tif (!this.engine) {\n\t\t\tthrow new Error(\"Gateway engine has not been initialized\");\n\t\t}\n\n\t\ttry {\n\t\t\tconst resolved = resolveRoute(this.config.routes, message, {\n\t\t\t\tbotUserId: this.botUserId,\n\t\t\t\tteamId: this.teamId,\n\t\t\t\tteamName: this.teamName || undefined,\n\t\t\t});\n\t\t\tif (!resolved) {\n\t\t\t\tlog.logWarning(`No route for channel ${message.channelId}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst outputTarget = this.buildOutputTarget(message, resolved);\n\t\t\tif (message.text.trim().toLowerCase() === \"stop\") {\n\t\t\t\tconst stopped = await this.engine.stopActiveRun(resolved.sessionId);\n\t\t\t\tawait this.sink.postMessage(outputTarget, stopped ? \"Stopping active run.\" : \"Nothing running.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst attachments = await this.downloadAttachments(resolved, message);\n\t\t\tconst input = this.buildResolvedTurn(message, resolved, attachments, outputTarget);\n\t\t\tthis.engine.dispatch(input);\n\t\t} catch (error) {\n\t\t\tconst messageText = error instanceof Error ? error.message : String(error);\n\t\t\tlog.logError(`Failed to normalize Slack inbound message ${message.ts}`, messageText);\n\t\t\tawait this.sink.postMessage(\n\t\t\t\t{\n\t\t\t\t\tchannelId: message.channelId,\n\t\t\t\t\tthreadId: message.threadTs || (message.type === \"mention\" ? message.ts : undefined),\n\t\t\t\t},\n\t\t\t\t`_Gateway error: ${messageText}_`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate buildResolvedTurn(\n\t\tmessage: SlackInboundMessage,\n\t\tresolved: ResolvedSlackRoute,\n\t\tattachments: AttachmentRef[],\n\t\toutput: TransportOutputTarget,\n\t): BeeResolvedTurn {\n\t\tif (!this.teamId || !this.botUserId) {\n\t\t\tthrow new Error(\"Missing Slack gateway identity\");\n\t\t}\n\n\t\treturn {\n\t\t\tsessionId: resolved.sessionId,\n\t\t\tthreadId: resolved.threadTs,\n\t\t\tworker: resolved.route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId: resolved.conversationId,\n\t\t\t},\n\t\t\tactor: {\n\t\t\t\tuserId: message.userId,\n\t\t\t\tuserName: message.userName,\n\t\t\t\tdisplayName: message.displayName,\n\t\t\t},\n\t\t\tmessage: {\n\t\t\t\ttext: message.text,\n\t\t\t},\n\t\t\tattachments,\n\t\t\tstreaming: resolveStreamingPreference(resolved.route, message, {\n\t\t\t\tbotUserId: this.botUserId,\n\t\t\t\tteamId: this.teamId,\n\t\t\t\tteamName: this.teamName || undefined,\n\t\t\t}),\n\t\t\toutput,\n\t\t};\n\t}\n\n\tprivate buildOutputTarget(message: SlackInboundMessage, resolved: ResolvedSlackRoute): TransportOutputTarget {\n\t\treturn {\n\t\t\tchannelId: message.channelId,\n\t\t\tthreadId: resolved.threadTs || (message.type === \"mention\" ? message.ts : undefined),\n\t\t};\n\t}\n\n\tprivate async downloadAttachments(\n\t\tresolved: ResolvedSlackRoute,\n\t\tmessage: SlackInboundMessage,\n\t): Promise<AttachmentRef[]> {\n\t\tconst files = message.files || [];\n\t\tif (files.length === 0) return [];\n\n\t\tconst attachments: AttachmentRef[] = [];\n\t\tfor (const file of files) {\n\t\t\tconst url = file.url_private_download || file.url_private;\n\t\t\tif (!url || !file.name) continue;\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { authorization: `Bearer ${this.config.botToken}` },\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`Failed to download Slack attachment ${file.name}: ${response.status}`);\n\t\t\t}\n\t\t\tconst bytes = new Uint8Array(await response.arrayBuffer());\n\t\t\tattachments.push(\n\t\t\t\tawait this.blobStore.put({\n\t\t\t\t\tnamespace: `incoming/slack/${resolved.sessionId}`,\n\t\t\t\t\tname: file.name,\n\t\t\t\t\ttitle: file.name,\n\t\t\t\t\tmimeType: file.mimetype,\n\t\t\t\t\tdata: bytes,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn attachments;\n\t}\n\n\tprivate async fetchUsers(): Promise<void> {\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = await this.webClient.users.list({ limit: 200, cursor });\n\t\t\tconst members = result.members as\n\t\t\t\t| Array<{ id?: string; name?: string; real_name?: string; deleted?: boolean }>\n\t\t\t\t| undefined;\n\t\t\tif (members) {\n\t\t\t\tfor (const user of members) {\n\t\t\t\t\tif (user.id && user.name && !user.deleted) {\n\t\t\t\t\t\tthis.users.set(user.id, {\n\t\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\t\tuserName: user.name,\n\t\t\t\t\t\t\tdisplayName: user.real_name || user.name,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor = result.response_metadata?.next_cursor;\n\t\t} while (cursor);\n\t}\n\n\tprivate async fetchChannels(): Promise<void> {\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = await this.webClient.conversations.list({\n\t\t\t\ttypes: \"public_channel,private_channel,im\",\n\t\t\t\texclude_archived: true,\n\t\t\t\tlimit: 200,\n\t\t\t\tcursor,\n\t\t\t});\n\t\t\tconst channels = result.channels as Array<{ id?: string; name?: string; user?: string }> | undefined;\n\t\t\tif (channels) {\n\t\t\t\tfor (const channel of channels) {\n\t\t\t\t\tif (!channel.id) continue;\n\t\t\t\t\tif (channel.name) {\n\t\t\t\t\t\tthis.channels.set(channel.id, { id: channel.id, name: channel.name });\n\t\t\t\t\t} else if (channel.user && this.users.has(channel.user)) {\n\t\t\t\t\t\tthis.channels.set(channel.id, {\n\t\t\t\t\t\t\tid: channel.id,\n\t\t\t\t\t\t\tname: `DM:${this.users.get(channel.user)!.userName}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor = result.response_metadata?.next_cursor;\n\t\t} while (cursor);\n\t}\n}\n\nfunction slackErrorCode(error: unknown): string | undefined {\n\tif (!error || typeof error !== \"object\" || !(\"data\" in error)) return undefined;\n\tconst data = error.data;\n\tif (!data || typeof data !== \"object\" || !(\"error\" in data)) return undefined;\n\treturn typeof data.error === \"string\" ? data.error : undefined;\n}\n\nfunction isSlackHistoryPermissionError(errorCode: string | undefined): boolean {\n\treturn errorCode === \"missing_scope\" || errorCode === \"no_permission\" || errorCode === \"not_allowed_token_type\";\n}\n\nexport async function startGatewayFromEnv(configPath?: string): Promise<void> {\n\tconst config = loadConfig(configPath);\n\tconst gateway = new SlackGateway(config);\n\tawait gateway.start();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype AttachmentRef,\n\tBeeGatewayEngine,\n\ttype BeeResolvedTurn,\n\ttype BeeWorkerClient,\n\ttype BlobStore,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { SocketModeClient } from \"@slack/socket-mode\";\nimport { WebClient } from \"@slack/web-api\";\nimport { join } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport { createHandoffServer, SlackHandoffController } from \"./handoff.js\";\nimport { HandoffReplyCache } from \"./handoff-reply-cache.js\";\nimport * as log from \"./log.js\";\nimport { resolveRoute, resolveStreamingPreference } from \"./router.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport { dispatchTracedTurn } from \"./telemetry.js\";\nimport type { ResolvedSlackRoute, SlackFile, SlackGatewayConfig, SlackInboundMessage } from \"./types.js\";\n\ninterface SlackUser {\n\tid: string;\n\tuserName: string;\n\tdisplayName: string;\n}\n\ninterface SlackChannel {\n\tid: string;\n\tname: string;\n}\n\nexport class SlackGateway {\n\tprivate socketClient: SocketModeClient;\n\tprivate webClient: WebClient;\n\tprivate users = new Map<string, SlackUser>();\n\tprivate channels = new Map<string, SlackChannel>();\n\tprivate botUserId: string | null = null;\n\tprivate teamId: string | null = null;\n\tprivate teamName: string | null = null;\n\tprivate blobStore: BlobStore;\n\tprivate sink: SlackSink;\n\tprivate engine: BeeGatewayEngine<string> | null = null;\n\tprivate workerClient: BeeWorkerClient | null = null;\n\tprivate handoffReplyCache = new HandoffReplyCache();\n\tprivate slackThreadHistoryUnavailable = false;\n\n\tconstructor(private config: SlackGatewayConfig) {\n\t\tthis.socketClient = new SocketModeClient({ appToken: config.appToken });\n\t\tthis.webClient = new WebClient(config.botToken);\n\t\tthis.blobStore = new LocalFileBlobStore(\n\t\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t\t);\n\t\tthis.sink = new SlackSink(this.webClient, this.blobStore, {\n\t\t\tposted: (target, ref, text) => this.handoffReplyCache.recordPosted(target, ref, text, this.botDisplayName()),\n\t\t\tupdated: (target, ref, text) => this.handoffReplyCache.recordUpdated(target, ref, text, this.botDisplayName()),\n\t\t});\n\t}\n\n\tasync start(): Promise<void> {\n\t\tthis.workerClient = await createNatsBeeClient(this.config.nats);\n\t\tthis.engine = new BeeGatewayEngine({\n\t\t\tsink: this.sink,\n\t\t\tworkerClient: this.workerClient,\n\t\t\tlogger: {\n\t\t\t\tinfo: log.logInfo,\n\t\t\t\twarn: log.logWarning,\n\t\t\t\terror: log.logError,\n\t\t\t},\n\t\t});\n\t\tconst auth = await this.webClient.auth.test();\n\t\tthis.botUserId = auth.user_id as string;\n\t\tthis.teamId = auth.team_id as string;\n\t\tthis.teamName = (auth.team as string | undefined) || null;\n\t\tawait Promise.all([this.fetchUsers(), this.fetchChannels()]);\n\t\tthis.startHandoffServer();\n\t\tthis.setupEventHandlers();\n\t\tawait this.socketClient.start();\n\t\tlog.logInfo(\n\t\t\t`Connected as ${this.botUserId} in ${this.teamId}; loaded ${this.channels.size} channels and ${this.users.size} users`,\n\t\t);\n\t}\n\n\tprivate startHandoffServer(): void {\n\t\tconst handoffConfig = this.config.handoff;\n\t\tif (!handoffConfig?.enabled) return;\n\t\tif (!this.teamId || !this.engine) throw new Error(\"Gateway must be initialized before handoff server startup\");\n\t\tconst controller = new SlackHandoffController(handoffConfig, {\n\t\t\tteamId: this.teamId,\n\t\t\tpostRootMessage: async (channelId, text) => this.sink.postMessage({ channelId }, text),\n\t\t\tgetPermalink: async (channelId, messageTs) => {\n\t\t\t\tconst result = await this.webClient.chat.getPermalink({ channel: channelId, message_ts: messageTs });\n\t\t\t\tif (!result.permalink) throw new Error(\"Slack did not return a permalink\");\n\t\t\t\treturn result.permalink;\n\t\t\t},\n\t\t\tdispatch: (input) => this.engine!.dispatch(input),\n\t\t\tgetReplies: async (channelId, threadTs) => {\n\t\t\t\tif (this.slackThreadHistoryUnavailable) return this.handoffReplyCache.replies(channelId, threadTs);\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.webClient.conversations.replies({\n\t\t\t\t\t\tchannel: channelId,\n\t\t\t\t\t\tts: threadTs,\n\t\t\t\t\t\tlimit: 100,\n\t\t\t\t\t});\n\t\t\t\t\treturn (result.messages || []).map((message) => {\n\t\t\t\t\t\tconst typed = message as typeof message & {\n\t\t\t\t\t\t\tbot_id?: string;\n\t\t\t\t\t\t\tbot_profile?: { name?: string };\n\t\t\t\t\t\t\tusername?: string;\n\t\t\t\t\t\t\tthread_ts?: string;\n\t\t\t\t\t\t};\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tts: String(typed.ts || \"\"),\n\t\t\t\t\t\t\tthreadTs: typed.thread_ts,\n\t\t\t\t\t\t\ttext: typed.text || \"\",\n\t\t\t\t\t\t\tauthor: typed.user\n\t\t\t\t\t\t\t\t? this.users.get(typed.user)?.displayName || this.users.get(typed.user)?.userName || typed.user\n\t\t\t\t\t\t\t\t: typed.bot_profile?.name || typed.username || \"Bot\",\n\t\t\t\t\t\t\tisBot: Boolean(typed.bot_id),\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst errorCode = slackErrorCode(error);\n\t\t\t\t\tif (!isSlackHistoryPermissionError(errorCode)) throw error;\n\t\t\t\t\tthis.slackThreadHistoryUnavailable = true;\n\t\t\t\t\tlog.logWarning(`Slack thread history unavailable (${errorCode}); using outbound Bee reply cache`);\n\t\t\t\t\treturn this.handoffReplyCache.replies(channelId, threadTs);\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t\tconst server = createHandoffServer(controller);\n\t\tconst host = handoffConfig.host || \"0.0.0.0\";\n\t\tconst port = handoffConfig.port || 8080;\n\t\tserver.listen(port, host, () => log.logInfo(`bee-slack handoff listening on http://${host}:${port}`));\n\t}\n\n\tprivate botDisplayName(): string {\n\t\tif (!this.botUserId) return \"Bee\";\n\t\tconst bot = this.users.get(this.botUserId);\n\t\treturn bot?.displayName || bot?.userName || \"Bee\";\n\t}\n\n\tprivate setupEventHandlers(): void {\n\t\tthis.socketClient.on(\"app_mention\", async ({ event, ack, body }) => {\n\t\t\tawait ack();\n\t\t\tconst e = event as {\n\t\t\t\ttext: string;\n\t\t\t\tchannel: string;\n\t\t\t\tuser: string;\n\t\t\t\tts: string;\n\t\t\t\tthread_ts?: string;\n\t\t\t\tfiles?: SlackFile[];\n\t\t\t};\n\n\t\t\tif (e.channel.startsWith(\"D\")) return;\n\n\t\t\tawait this.enqueueInbound({\n\t\t\t\ttype: \"mention\",\n\t\t\t\tchannelId: e.channel,\n\t\t\t\tchannelName: this.channels.get(e.channel)?.name,\n\t\t\t\tthreadTs: e.thread_ts,\n\t\t\t\tts: e.ts,\n\t\t\t\tuserId: e.user,\n\t\t\t\tteamId: (event as typeof e & { team?: string }).team || (body as { team_id?: string }).team_id,\n\t\t\t\tuserName: this.users.get(e.user)?.userName,\n\t\t\t\tdisplayName: this.users.get(e.user)?.displayName,\n\t\t\t\ttext: e.text.replace(/<@[A-Z0-9]+>/gi, \"\").trim(),\n\t\t\t\tfiles: e.files,\n\t\t\t});\n\t\t});\n\n\t\tthis.socketClient.on(\"message\", async ({ event, ack }) => {\n\t\t\tawait ack();\n\t\t\tconst e = event as {\n\t\t\t\ttext?: string;\n\t\t\t\tchannel: string;\n\t\t\t\tuser?: string;\n\t\t\t\tts: string;\n\t\t\t\tthread_ts?: string;\n\t\t\t\tchannel_type?: string;\n\t\t\t\tsubtype?: string;\n\t\t\t\tbot_id?: string;\n\t\t\t\tfiles?: SlackFile[];\n\t\t\t};\n\n\t\t\tif (e.bot_id || !e.user || e.user === this.botUserId) return;\n\t\t\tif (e.subtype !== undefined && e.subtype !== \"file_share\") return;\n\t\t\tif (!e.text && (!e.files || e.files.length === 0)) return;\n\n\t\t\tconst isDM = e.channel_type === \"im\";\n\t\t\tconst isBotMention = !!this.botUserId && e.text?.includes(`<@${this.botUserId}>`);\n\t\t\tif (!isDM && isBotMention) return;\n\t\t\tif (!isDM) return;\n\n\t\t\tawait this.enqueueInbound({\n\t\t\t\ttype: \"dm\",\n\t\t\t\tchannelId: e.channel,\n\t\t\t\tchannelName: this.channels.get(e.channel)?.name,\n\t\t\t\tthreadTs: e.thread_ts,\n\t\t\t\tts: e.ts,\n\t\t\t\tuserId: e.user,\n\t\t\t\tuserName: this.users.get(e.user)?.userName,\n\t\t\t\tdisplayName: this.users.get(e.user)?.displayName,\n\t\t\t\ttext: (e.text || \"\").replace(/<@[A-Z0-9]+>/gi, \"\").trim(),\n\t\t\t\tfiles: e.files,\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async enqueueInbound(message: SlackInboundMessage): Promise<void> {\n\t\tif (!this.botUserId || !this.teamId) {\n\t\t\tthrow new Error(\"Gateway has not been initialized\");\n\t\t}\n\t\tif (!this.engine) {\n\t\t\tthrow new Error(\"Gateway engine has not been initialized\");\n\t\t}\n\t\tconst engine = this.engine;\n\n\t\ttry {\n\t\t\tconst resolved = resolveRoute(this.config.routes, message, {\n\t\t\t\tbotUserId: this.botUserId,\n\t\t\t\tteamId: this.teamId,\n\t\t\t\tteamName: this.teamName || undefined,\n\t\t\t});\n\t\t\tif (!resolved) {\n\t\t\t\tlog.logWarning(`No route for channel ${message.channelId}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst outputTarget = this.buildOutputTarget(message, resolved);\n\t\t\tif (message.text.trim().toLowerCase() === \"stop\") {\n\t\t\t\tconst stopped = await this.engine.stopActiveRun(resolved.sessionId);\n\t\t\t\tawait this.sink.postMessage(outputTarget, stopped ? \"Stopping active run.\" : \"Nothing running.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst attachments = await this.downloadAttachments(resolved, message);\n\t\t\tconst input = this.buildResolvedTurn(message, resolved, attachments, outputTarget);\n\t\t\tdispatchTracedTurn({ \"bee.session.id\": input.sessionId, \"bee.transport\": \"slack\" }, (telemetry) => {\n\t\t\t\tengine.dispatch({ ...input, telemetry });\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst messageText = error instanceof Error ? error.message : String(error);\n\t\t\tlog.logError(`Failed to normalize Slack inbound message ${message.ts}`, messageText);\n\t\t\tawait this.sink.postMessage(\n\t\t\t\t{\n\t\t\t\t\tchannelId: message.channelId,\n\t\t\t\t\tthreadId: message.threadTs || (message.type === \"mention\" ? message.ts : undefined),\n\t\t\t\t},\n\t\t\t\t`_Gateway error: ${messageText}_`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate buildResolvedTurn(\n\t\tmessage: SlackInboundMessage,\n\t\tresolved: ResolvedSlackRoute,\n\t\tattachments: AttachmentRef[],\n\t\toutput: TransportOutputTarget,\n\t): BeeResolvedTurn {\n\t\tif (!this.teamId || !this.botUserId) {\n\t\t\tthrow new Error(\"Missing Slack gateway identity\");\n\t\t}\n\n\t\treturn {\n\t\t\tsessionId: resolved.sessionId,\n\t\t\tthreadId: resolved.threadTs,\n\t\t\tworker: resolved.route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId: resolved.conversationId,\n\t\t\t},\n\t\t\tactor: {\n\t\t\t\tuserId: message.userId,\n\t\t\t\tuserName: message.userName,\n\t\t\t\tdisplayName: message.displayName,\n\t\t\t},\n\t\t\tmessage: {\n\t\t\t\ttext: message.text,\n\t\t\t},\n\t\t\tattachments,\n\t\t\tstreaming: resolveStreamingPreference(resolved.route, message, {\n\t\t\t\tbotUserId: this.botUserId,\n\t\t\t\tteamId: this.teamId,\n\t\t\t\tteamName: this.teamName || undefined,\n\t\t\t}),\n\t\t\toutput,\n\t\t};\n\t}\n\n\tprivate buildOutputTarget(message: SlackInboundMessage, resolved: ResolvedSlackRoute): TransportOutputTarget {\n\t\treturn {\n\t\t\tchannelId: message.channelId,\n\t\t\tthreadId: resolved.threadTs || (message.type === \"mention\" ? message.ts : undefined),\n\t\t};\n\t}\n\n\tprivate async downloadAttachments(\n\t\tresolved: ResolvedSlackRoute,\n\t\tmessage: SlackInboundMessage,\n\t): Promise<AttachmentRef[]> {\n\t\tconst files = message.files || [];\n\t\tif (files.length === 0) return [];\n\n\t\tconst attachments: AttachmentRef[] = [];\n\t\tfor (const file of files) {\n\t\t\tconst url = file.url_private_download || file.url_private;\n\t\t\tif (!url || !file.name) continue;\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { authorization: `Bearer ${this.config.botToken}` },\n\t\t\t});\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`Failed to download Slack attachment ${file.name}: ${response.status}`);\n\t\t\t}\n\t\t\tconst bytes = new Uint8Array(await response.arrayBuffer());\n\t\t\tattachments.push(\n\t\t\t\tawait this.blobStore.put({\n\t\t\t\t\tnamespace: `incoming/slack/${resolved.sessionId}`,\n\t\t\t\t\tname: file.name,\n\t\t\t\t\ttitle: file.name,\n\t\t\t\t\tmimeType: file.mimetype,\n\t\t\t\t\tdata: bytes,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn attachments;\n\t}\n\n\tprivate async fetchUsers(): Promise<void> {\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = await this.webClient.users.list({ limit: 200, cursor });\n\t\t\tconst members = result.members as\n\t\t\t\t| Array<{ id?: string; name?: string; real_name?: string; deleted?: boolean }>\n\t\t\t\t| undefined;\n\t\t\tif (members) {\n\t\t\t\tfor (const user of members) {\n\t\t\t\t\tif (user.id && user.name && !user.deleted) {\n\t\t\t\t\t\tthis.users.set(user.id, {\n\t\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\t\tuserName: user.name,\n\t\t\t\t\t\t\tdisplayName: user.real_name || user.name,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor = result.response_metadata?.next_cursor;\n\t\t} while (cursor);\n\t}\n\n\tprivate async fetchChannels(): Promise<void> {\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = await this.webClient.conversations.list({\n\t\t\t\ttypes: \"public_channel,private_channel,im\",\n\t\t\t\texclude_archived: true,\n\t\t\t\tlimit: 200,\n\t\t\t\tcursor,\n\t\t\t});\n\t\t\tconst channels = result.channels as Array<{ id?: string; name?: string; user?: string }> | undefined;\n\t\t\tif (channels) {\n\t\t\t\tfor (const channel of channels) {\n\t\t\t\t\tif (!channel.id) continue;\n\t\t\t\t\tif (channel.name) {\n\t\t\t\t\t\tthis.channels.set(channel.id, { id: channel.id, name: channel.name });\n\t\t\t\t\t} else if (channel.user && this.users.has(channel.user)) {\n\t\t\t\t\t\tthis.channels.set(channel.id, {\n\t\t\t\t\t\t\tid: channel.id,\n\t\t\t\t\t\t\tname: `DM:${this.users.get(channel.user)!.userName}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcursor = result.response_metadata?.next_cursor;\n\t\t} while (cursor);\n\t}\n}\n\nfunction slackErrorCode(error: unknown): string | undefined {\n\tif (!error || typeof error !== \"object\" || !(\"data\" in error)) return undefined;\n\tconst data = error.data;\n\tif (!data || typeof data !== \"object\" || !(\"error\" in data)) return undefined;\n\treturn typeof data.error === \"string\" ? data.error : undefined;\n}\n\nfunction isSlackHistoryPermissionError(errorCode: string | undefined): boolean {\n\treturn errorCode === \"missing_scope\" || errorCode === \"no_permission\" || errorCode === \"not_allowed_token_type\";\n}\n\nexport async function startGatewayFromEnv(configPath?: string): Promise<void> {\n\tconst config = loadConfig(configPath);\n\tconst gateway = new SlackGateway(config);\n\tawait gateway.start();\n}\n"]}
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"","sourcesContent":["#!/usr/bin/env node\n\nimport { startGatewayFromEnv } from \"./gateway.js\";\nimport { runScheduledSlackTurnFromFiles } from \"./scheduled.js\";\n\nconst [commandOrConfigPath, maybeConfigPath, maybeJobPath] = process.argv.slice(2);\n\nif (commandOrConfigPath === \"run-scheduled\" || commandOrConfigPath === \"scheduled\") {\n\tawait runScheduledSlackTurnFromFiles(maybeConfigPath, maybeJobPath);\n} else {\n\tawait startGatewayFromEnv(commandOrConfigPath);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"","sourcesContent":["#!/usr/bin/env node\n\nimport { startGatewayFromEnv } from \"./gateway.js\";\nimport { runScheduledSlackTurnFromFiles } from \"./scheduled.js\";\nimport { initializeTelemetry, shutdownTelemetry } from \"./telemetry.js\";\n\nconst [commandOrConfigPath, maybeConfigPath, maybeJobPath] = process.argv.slice(2);\n\ninitializeTelemetry();\n\nif (commandOrConfigPath === \"run-scheduled\" || commandOrConfigPath === \"scheduled\") {\n\ttry {\n\t\tawait runScheduledSlackTurnFromFiles(maybeConfigPath, maybeJobPath);\n\t} finally {\n\t\tawait shutdownTelemetry();\n\t}\n} else {\n\tprocess.once(\"SIGTERM\", () => void shutdownTelemetry().finally(() => process.exit(0)));\n\tprocess.once(\"SIGINT\", () => void shutdownTelemetry().finally(() => process.exit(0)));\n\tawait startGatewayFromEnv(commandOrConfigPath);\n}\n"]}
|
package/dist/main.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { startGatewayFromEnv } from "./gateway.js";
|
|
3
3
|
import { runScheduledSlackTurnFromFiles } from "./scheduled.js";
|
|
4
|
+
import { initializeTelemetry, shutdownTelemetry } from "./telemetry.js";
|
|
4
5
|
const [commandOrConfigPath, maybeConfigPath, maybeJobPath] = process.argv.slice(2);
|
|
6
|
+
initializeTelemetry();
|
|
5
7
|
if (commandOrConfigPath === "run-scheduled" || commandOrConfigPath === "scheduled") {
|
|
6
|
-
|
|
8
|
+
try {
|
|
9
|
+
await runScheduledSlackTurnFromFiles(maybeConfigPath, maybeJobPath);
|
|
10
|
+
}
|
|
11
|
+
finally {
|
|
12
|
+
await shutdownTelemetry();
|
|
13
|
+
}
|
|
7
14
|
}
|
|
8
15
|
else {
|
|
16
|
+
process.once("SIGTERM", () => void shutdownTelemetry().finally(() => process.exit(0)));
|
|
17
|
+
process.once("SIGINT", () => void shutdownTelemetry().finally(() => process.exit(0)));
|
|
9
18
|
await startGatewayFromEnv(commandOrConfigPath);
|
|
10
19
|
}
|
|
11
20
|
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExE,MAAM,CAAC,mBAAmB,EAAE,eAAe,EAAE,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnF,mBAAmB,EAAE,CAAC;AAEtB,IAAI,mBAAmB,KAAK,eAAe,IAAI,mBAAmB,KAAK,WAAW,EAAE,CAAC;IACpF,IAAI,CAAC;QACJ,MAAM,8BAA8B,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;IACrE,CAAC;YAAS,CAAC;QACV,MAAM,iBAAiB,EAAE,CAAC;IAC3B,CAAC;AACF,CAAC;KAAM,CAAC;IACP,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,iBAAiB,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,iBAAiB,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtF,MAAM,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;AAChD,CAAC","sourcesContent":["#!/usr/bin/env node\n\nimport { startGatewayFromEnv } from \"./gateway.js\";\nimport { runScheduledSlackTurnFromFiles } from \"./scheduled.js\";\nimport { initializeTelemetry, shutdownTelemetry } from \"./telemetry.js\";\n\nconst [commandOrConfigPath, maybeConfigPath, maybeJobPath] = process.argv.slice(2);\n\ninitializeTelemetry();\n\nif (commandOrConfigPath === \"run-scheduled\" || commandOrConfigPath === \"scheduled\") {\n\ttry {\n\t\tawait runScheduledSlackTurnFromFiles(maybeConfigPath, maybeJobPath);\n\t} finally {\n\t\tawait shutdownTelemetry();\n\t}\n} else {\n\tprocess.once(\"SIGTERM\", () => void shutdownTelemetry().finally(() => process.exit(0)));\n\tprocess.once(\"SIGINT\", () => void shutdownTelemetry().finally(() => process.exit(0)));\n\tawait startGatewayFromEnv(commandOrConfigPath);\n}\n"]}
|
package/dist/scheduled.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled.d.ts","sourceRoot":"","sources":["../src/scheduled.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,KAAK,CAAC,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA+BD,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,uBAAuB,CAYhF;AAED,wBAAsB,8BAA8B,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzG;AAED,wBAAsB,qBAAqB,CAC1C,aAAa,EAAE,kBAAkB,EACjC,YAAY,EAAE,uBAAuB,GACnC,OAAO,CAAC,IAAI,CAAC,CAoEf;AA8LD,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAUzG","sourcesContent":["import {\n\ttype ArtifactRef,\n\ttype BeeResolvedTurn,\n\ttype BeeRunEvent,\n\ttype BeeWorkerClient,\n\tbuildConversationId,\n\tbuildSessionKey,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\tnewTurnId,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { WebClient } from \"@slack/web-api\";\nimport { readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport * as log from \"./log.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport type { SlackGatewayConfig } from \"./types.js\";\n\nexport interface SlackScheduledRunConfig {\n\tid: string;\n\trouteId: string;\n\ttext: string;\n\ttarget: {\n\t\tslackUserId?: string;\n\t\tchannelId?: string;\n\t\tthreadTs?: string;\n\t};\n\tactor?: {\n\t\tuserId: string;\n\t\tuserName?: string;\n\t\tdisplayName?: string;\n\t};\n\tsessionId?: string;\n\tsessionPrefix?: string;\n\tconversationId?: string;\n}\n\ninterface RenderState {\n\tstatusRef?: string;\n\tlatestText: string;\n\trequestText?: string;\n\titemTexts: Map<string, string>;\n}\n\ntype ItemPartLike = { kind: string; [key: string]: unknown };\n\ntype ItemAppendedPayload = {\n\teventType: \"item.appended\";\n\titem: { id: string; kind: string; role: string; parts: ItemPartLike[] };\n};\n\ntype ItemUpdatedPayload = {\n\teventType: \"item.updated\";\n\titemId: string;\n\tappendParts?: ItemPartLike[];\n};\n\ntype RunFailedPayload = {\n\teventType: \"run.failed\";\n\terror: string;\n};\n\ntype ApprovalRequestedPayload = {\n\tsummary: string;\n};\n\nexport function loadScheduledRunConfig(jobPath?: string): SlackScheduledRunConfig {\n\tconst path = jobPath || process.env.BEE_SLACK_SCHEDULED_RUN_CONFIG;\n\tif (!path) {\n\t\tthrow new Error(\n\t\t\t\"Missing scheduled run config path; pass it as second argument or set BEE_SLACK_SCHEDULED_RUN_CONFIG\",\n\t\t);\n\t}\n\n\tconst fullPath = resolve(path);\n\tconst config = JSON.parse(readFileSync(fullPath, \"utf-8\")) as SlackScheduledRunConfig;\n\tvalidateScheduledRunConfig(config, fullPath);\n\treturn config;\n}\n\nexport async function runScheduledSlackTurnFromFiles(configPath?: string, jobPath?: string): Promise<void> {\n\tconst gatewayConfig = loadConfig(configPath);\n\tconst scheduledRun = loadScheduledRunConfig(jobPath);\n\tawait runScheduledSlackTurn(gatewayConfig, scheduledRun);\n}\n\nexport async function runScheduledSlackTurn(\n\tgatewayConfig: SlackGatewayConfig,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<void> {\n\tvalidateScheduledRunConfig(scheduledRun, \"scheduled run config\");\n\tconst route = gatewayConfig.routes.find((candidate) => candidate.id === scheduledRun.routeId);\n\tif (!route) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} references unknown routeId ${scheduledRun.routeId}`);\n\t}\n\n\tconst webClient = new WebClient(gatewayConfig.botToken);\n\tconst blobStore = new LocalFileBlobStore(\n\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t);\n\tconst sink = new SlackSink(webClient, blobStore);\n\tconst workerClient = await createNatsBeeClient(gatewayConfig.nats);\n\n\ttry {\n\t\tconst auth = await webClient.auth.test();\n\t\tconst teamId = String(auth.team_id || \"unknown-team\");\n\t\tconst output = await resolveScheduledOutputTarget(webClient, scheduledRun);\n\t\tconst actor = await resolveScheduledActor(webClient, scheduledRun);\n\t\tlet statusRef: string | undefined;\n\t\tif (!output.threadId) {\n\t\t\tstatusRef = await sink.postMessage({ channelId: output.channelId }, \"_Working..._\");\n\t\t\toutput.threadId = statusRef;\n\t\t}\n\t\tconst slackConversationId = buildConversationId([\n\t\t\t\"slack\",\n\t\t\tteamId,\n\t\t\toutput.channelId || \"unknown-channel\",\n\t\t\toutput.threadId,\n\t\t]);\n\t\tconst conversationId = scheduledRun.conversationId || slackConversationId;\n\t\tconst sessionBase =\n\t\t\troute.session?.strategy === \"channel\" ? output.channelId || \"unknown-channel\" : output.threadId;\n\t\tconst routeSessionId = buildSessionKey(\n\t\t\tscheduledRun.sessionPrefix || route.session?.prefix || route.id,\n\t\t\tbuildConversationId([\"slack\", teamId, output.channelId || \"unknown-channel\", sessionBase || output.threadId]),\n\t\t);\n\t\tconst sessionId = scheduledRun.sessionId || routeSessionId;\n\n\t\tconst input: BeeResolvedTurn = {\n\t\t\tsessionId,\n\t\t\tthreadId: output.threadId,\n\t\t\tworker: route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId,\n\t\t\t},\n\t\t\tactor,\n\t\t\tmessage: {\n\t\t\t\ttext: scheduledRun.text,\n\t\t\t},\n\t\t\tattachments: [],\n\t\t\toutput,\n\t\t};\n\n\t\tlog.logInfo(`Starting scheduled Slack run ${scheduledRun.id} on route ${route.id}`);\n\t\tawait streamScheduledTurn(workerClient, sink, input, statusRef);\n\t\tlog.logInfo(`Completed scheduled Slack run ${scheduledRun.id}`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog.logError(`Scheduled Slack run ${scheduledRun.id} failed`, message);\n\t\tthrow error;\n\t} finally {\n\t\tawait workerClient.close?.();\n\t}\n}\n\nasync function resolveScheduledOutputTarget(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<TransportOutputTarget> {\n\tif (scheduledRun.target.channelId) {\n\t\treturn {\n\t\t\tchannelId: scheduledRun.target.channelId,\n\t\t\tthreadId: scheduledRun.target.threadTs,\n\t\t};\n\t}\n\n\tif (!scheduledRun.target.slackUserId) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} needs target.slackUserId or target.channelId`);\n\t}\n\n\tconst result = await webClient.conversations.open({ users: scheduledRun.target.slackUserId });\n\tconst channel = result.channel as { id?: string } | undefined;\n\tif (!channel?.id) {\n\t\tthrow new Error(`Slack conversations.open did not return a channel for user ${scheduledRun.target.slackUserId}`);\n\t}\n\treturn {\n\t\tchannelId: channel.id,\n\t\tthreadId: scheduledRun.target.threadTs,\n\t};\n}\n\nasync function resolveScheduledActor(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<BeeResolvedTurn[\"actor\"]> {\n\tif (scheduledRun.actor) return scheduledRun.actor;\n\tif (!scheduledRun.target.slackUserId) {\n\t\treturn {\n\t\t\tuserId: `scheduler:${scheduledRun.id}`,\n\t\t\tdisplayName: \"Bee Scheduler\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst info = await webClient.users.info({ user: scheduledRun.target.slackUserId });\n\t\tconst user = info.user as\n\t\t\t| { id?: string; name?: string; real_name?: string; profile?: { display_name?: string } }\n\t\t\t| undefined;\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tuserName: user?.name,\n\t\t\tdisplayName: user?.profile?.display_name || user?.real_name || user?.name,\n\t\t};\n\t} catch (error) {\n\t\tlog.logWarning(\n\t\t\t`Could not resolve Slack user ${scheduledRun.target.slackUserId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tdisplayName: scheduledRun.target.slackUserId,\n\t\t};\n\t}\n}\n\nasync function streamScheduledTurn(\n\tworkerClient: BeeWorkerClient,\n\tsink: SlackSink,\n\tinput: BeeResolvedTurn,\n\tstatusRef?: string,\n): Promise<void> {\n\tconst state: RenderState = {\n\t\tstatusRef,\n\t\tlatestText: \"_Working..._\",\n\t\trequestText: input.message.text,\n\t\titemTexts: new Map<string, string>(),\n\t};\n\tconst request = {\n\t\tsessionId: input.sessionId,\n\t\tthreadId: input.threadId,\n\t\tturnId: newTurnId(),\n\t\tconversation: input.conversation,\n\t\tactor: input.actor,\n\t\tmessage: input.message,\n\t\tattachments: input.attachments,\n\t};\n\n\ttry {\n\t\tawait workerClient.streamTurn(input.worker, request, async (event) => {\n\t\t\tawait handleScheduledEvent(sink, input.output, event, state);\n\t\t});\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait sink.postMessage(input.output, `_Scheduled gateway error: ${message}_`);\n\t\tthrow error;\n\t}\n}\n\nasync function handleScheduledEvent(\n\tsink: SlackSink,\n\toutput: TransportOutputTarget,\n\tevent: BeeRunEvent,\n\tstate: RenderState,\n): Promise<void> {\n\tif (event.name === \"run.started\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.completed\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.failed\") {\n\t\tconst errorText = `_Error: ${asRunFailedPayload(event).error}_`;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, errorText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, errorText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"approval.requested\") {\n\t\tawait sink.postMessage(output, `Approval requested: ${asApprovalRequestedPayload(event).summary}`);\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.appended\") {\n\t\tconst payload = asItemAppendedPayload(event);\n\t\tconst text = renderParts(payload.item.parts);\n\t\tstate.itemTexts.set(payload.item.id, text);\n\t\tif (payload.item.kind === \"artifact\") {\n\t\t\tconst artifact = firstArtifactRef(payload.item.parts);\n\t\t\tif (artifact && artifactHasPayload(artifact)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait sink.publishArtifact(output, artifact);\n\t\t\t\t\treturn;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tawait sink.postMessage(output, `${text}\\n_Artifact upload failed: ${message}_`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait sink.postMessage(output, text);\n\t\t\treturn;\n\t\t}\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, text);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.updated\") {\n\t\tconst payload = asItemUpdatedPayload(event);\n\t\tconst current = state.itemTexts.get(payload.itemId) || \"\";\n\t\tconst appended = renderParts(payload.appendParts || []);\n\t\tconst next = current ? `${current}${appended}` : appended;\n\t\tstate.itemTexts.set(payload.itemId, next);\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, next);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t}\n}\n\nfunction firstArtifactRef(parts: ItemPartLike[]): ArtifactRef | undefined {\n\tconst part = parts.find((entry) => entry.kind === \"artifactRef\");\n\tif (!part) return undefined;\n\treturn {\n\t\tartifactId: String(part.artifactId || \"artifact\"),\n\t\tblobKey: typeof part.blobKey === \"string\" ? part.blobKey : undefined,\n\t\tname: typeof part.name === \"string\" ? part.name : undefined,\n\t\ttitle: typeof part.title === \"string\" ? part.title : undefined,\n\t\tmimeType: typeof part.mimeType === \"string\" ? part.mimeType : undefined,\n\t\turi: typeof part.uri === \"string\" ? part.uri : undefined,\n\t\tsizeBytes: typeof part.sizeBytes === \"number\" ? part.sizeBytes : undefined,\n\t};\n}\n\nfunction artifactHasPayload(artifact: ArtifactRef): boolean {\n\treturn !!(artifact.uri || artifact.blobKey);\n}\n\nexport function renderScheduledFinalMessage(requestText: string | undefined, responseText: string): string {\n\tconst request = requestText?.trim();\n\tif (!request) return responseText;\n\n\tconst quotedRequest = request\n\t\t.split(/\\r?\\n/)\n\t\t.map((line) => `> ${escapeSlackText(line)}`)\n\t\t.join(\"\\n\");\n\n\treturn [\"*Request:*\", quotedRequest, \"*Antwort:*\", responseText].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction escapeSlackText(text: string): string {\n\treturn text.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction renderParts(parts: ItemPartLike[]): string {\n\treturn parts\n\t\t.map((part) => {\n\t\t\tif (part.kind === \"text\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"status\") return String(part.status || \"\");\n\t\t\tif (part.kind === \"artifactRef\") {\n\t\t\t\treturn `Artifact: ${String(part.title || part.name || part.artifactId || \"artifact\")}`;\n\t\t\t}\n\t\t\tif (part.kind === \"approval\" || part.kind === \"choice\") {\n\t\t\t\treturn `${String(part.title || \"\")}\\n${String(part.summary || \"\")}`.trim();\n\t\t\t}\n\t\t\tif (part.kind === \"form\") return String(part.title || \"\");\n\t\t\tif (part.kind === \"log\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"patch\" || part.kind === \"diff\") {\n\t\t\t\tconst files = Array.isArray(part.files) ? (part.files as Array<{ path?: unknown }>) : [];\n\t\t\t\treturn files.map((entry) => `File: ${String(entry.path || \"\")}`).join(\"\\n\");\n\t\t\t}\n\t\t\treturn JSON.stringify(part);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction validateScheduledRunConfig(config: SlackScheduledRunConfig, source: string): void {\n\tif (!config.id) throw new Error(`Missing id in ${source}`);\n\tif (!config.routeId) throw new Error(`Missing routeId in ${source}`);\n\tif (!config.text) throw new Error(`Missing text in ${source}`);\n\tif (!config.target) throw new Error(`Missing target in ${source}`);\n\tif (!config.target.slackUserId && !config.target.channelId) {\n\t\tthrow new Error(`Missing target.slackUserId or target.channelId in ${source}`);\n\t}\n}\n\nfunction asRunFailedPayload(event: BeeRunEvent): RunFailedPayload {\n\treturn event.payload as RunFailedPayload;\n}\n\nfunction asApprovalRequestedPayload(event: BeeRunEvent): ApprovalRequestedPayload {\n\treturn event.payload as ApprovalRequestedPayload;\n}\n\nfunction asItemAppendedPayload(event: BeeRunEvent): ItemAppendedPayload {\n\treturn event.payload as ItemAppendedPayload;\n}\n\nfunction asItemUpdatedPayload(event: BeeRunEvent): ItemUpdatedPayload {\n\treturn event.payload as ItemUpdatedPayload;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scheduled.d.ts","sourceRoot":"","sources":["../src/scheduled.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,KAAK,CAAC,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA+BD,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,uBAAuB,CAYhF;AAED,wBAAsB,8BAA8B,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzG;AAED,wBAAsB,qBAAqB,CAC1C,aAAa,EAAE,kBAAkB,EACjC,YAAY,EAAE,uBAAuB,GACnC,OAAO,CAAC,IAAI,CAAC,CAoEf;AAoMD,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAUzG","sourcesContent":["import {\n\ttype ArtifactRef,\n\ttype BeeResolvedTurn,\n\ttype BeeRunEvent,\n\ttype BeeWorkerClient,\n\tbuildConversationId,\n\tbuildSessionKey,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\tnewTurnId,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { WebClient } from \"@slack/web-api\";\nimport { readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport * as log from \"./log.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport { withTracedTurn } from \"./telemetry.js\";\nimport type { SlackGatewayConfig } from \"./types.js\";\n\nexport interface SlackScheduledRunConfig {\n\tid: string;\n\trouteId: string;\n\ttext: string;\n\ttarget: {\n\t\tslackUserId?: string;\n\t\tchannelId?: string;\n\t\tthreadTs?: string;\n\t};\n\tactor?: {\n\t\tuserId: string;\n\t\tuserName?: string;\n\t\tdisplayName?: string;\n\t};\n\tsessionId?: string;\n\tsessionPrefix?: string;\n\tconversationId?: string;\n}\n\ninterface RenderState {\n\tstatusRef?: string;\n\tlatestText: string;\n\trequestText?: string;\n\titemTexts: Map<string, string>;\n}\n\ntype ItemPartLike = { kind: string; [key: string]: unknown };\n\ntype ItemAppendedPayload = {\n\teventType: \"item.appended\";\n\titem: { id: string; kind: string; role: string; parts: ItemPartLike[] };\n};\n\ntype ItemUpdatedPayload = {\n\teventType: \"item.updated\";\n\titemId: string;\n\tappendParts?: ItemPartLike[];\n};\n\ntype RunFailedPayload = {\n\teventType: \"run.failed\";\n\terror: string;\n};\n\ntype ApprovalRequestedPayload = {\n\tsummary: string;\n};\n\nexport function loadScheduledRunConfig(jobPath?: string): SlackScheduledRunConfig {\n\tconst path = jobPath || process.env.BEE_SLACK_SCHEDULED_RUN_CONFIG;\n\tif (!path) {\n\t\tthrow new Error(\n\t\t\t\"Missing scheduled run config path; pass it as second argument or set BEE_SLACK_SCHEDULED_RUN_CONFIG\",\n\t\t);\n\t}\n\n\tconst fullPath = resolve(path);\n\tconst config = JSON.parse(readFileSync(fullPath, \"utf-8\")) as SlackScheduledRunConfig;\n\tvalidateScheduledRunConfig(config, fullPath);\n\treturn config;\n}\n\nexport async function runScheduledSlackTurnFromFiles(configPath?: string, jobPath?: string): Promise<void> {\n\tconst gatewayConfig = loadConfig(configPath);\n\tconst scheduledRun = loadScheduledRunConfig(jobPath);\n\tawait runScheduledSlackTurn(gatewayConfig, scheduledRun);\n}\n\nexport async function runScheduledSlackTurn(\n\tgatewayConfig: SlackGatewayConfig,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<void> {\n\tvalidateScheduledRunConfig(scheduledRun, \"scheduled run config\");\n\tconst route = gatewayConfig.routes.find((candidate) => candidate.id === scheduledRun.routeId);\n\tif (!route) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} references unknown routeId ${scheduledRun.routeId}`);\n\t}\n\n\tconst webClient = new WebClient(gatewayConfig.botToken);\n\tconst blobStore = new LocalFileBlobStore(\n\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t);\n\tconst sink = new SlackSink(webClient, blobStore);\n\tconst workerClient = await createNatsBeeClient(gatewayConfig.nats);\n\n\ttry {\n\t\tconst auth = await webClient.auth.test();\n\t\tconst teamId = String(auth.team_id || \"unknown-team\");\n\t\tconst output = await resolveScheduledOutputTarget(webClient, scheduledRun);\n\t\tconst actor = await resolveScheduledActor(webClient, scheduledRun);\n\t\tlet statusRef: string | undefined;\n\t\tif (!output.threadId) {\n\t\t\tstatusRef = await sink.postMessage({ channelId: output.channelId }, \"_Working..._\");\n\t\t\toutput.threadId = statusRef;\n\t\t}\n\t\tconst slackConversationId = buildConversationId([\n\t\t\t\"slack\",\n\t\t\tteamId,\n\t\t\toutput.channelId || \"unknown-channel\",\n\t\t\toutput.threadId,\n\t\t]);\n\t\tconst conversationId = scheduledRun.conversationId || slackConversationId;\n\t\tconst sessionBase =\n\t\t\troute.session?.strategy === \"channel\" ? output.channelId || \"unknown-channel\" : output.threadId;\n\t\tconst routeSessionId = buildSessionKey(\n\t\t\tscheduledRun.sessionPrefix || route.session?.prefix || route.id,\n\t\t\tbuildConversationId([\"slack\", teamId, output.channelId || \"unknown-channel\", sessionBase || output.threadId]),\n\t\t);\n\t\tconst sessionId = scheduledRun.sessionId || routeSessionId;\n\n\t\tconst input: BeeResolvedTurn = {\n\t\t\tsessionId,\n\t\t\tthreadId: output.threadId,\n\t\t\tworker: route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId,\n\t\t\t},\n\t\t\tactor,\n\t\t\tmessage: {\n\t\t\t\ttext: scheduledRun.text,\n\t\t\t},\n\t\t\tattachments: [],\n\t\t\toutput,\n\t\t};\n\n\t\tlog.logInfo(`Starting scheduled Slack run ${scheduledRun.id} on route ${route.id}`);\n\t\tawait streamScheduledTurn(workerClient, sink, input, statusRef);\n\t\tlog.logInfo(`Completed scheduled Slack run ${scheduledRun.id}`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog.logError(`Scheduled Slack run ${scheduledRun.id} failed`, message);\n\t\tthrow error;\n\t} finally {\n\t\tawait workerClient.close?.();\n\t}\n}\n\nasync function resolveScheduledOutputTarget(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<TransportOutputTarget> {\n\tif (scheduledRun.target.channelId) {\n\t\treturn {\n\t\t\tchannelId: scheduledRun.target.channelId,\n\t\t\tthreadId: scheduledRun.target.threadTs,\n\t\t};\n\t}\n\n\tif (!scheduledRun.target.slackUserId) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} needs target.slackUserId or target.channelId`);\n\t}\n\n\tconst result = await webClient.conversations.open({ users: scheduledRun.target.slackUserId });\n\tconst channel = result.channel as { id?: string } | undefined;\n\tif (!channel?.id) {\n\t\tthrow new Error(`Slack conversations.open did not return a channel for user ${scheduledRun.target.slackUserId}`);\n\t}\n\treturn {\n\t\tchannelId: channel.id,\n\t\tthreadId: scheduledRun.target.threadTs,\n\t};\n}\n\nasync function resolveScheduledActor(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<BeeResolvedTurn[\"actor\"]> {\n\tif (scheduledRun.actor) return scheduledRun.actor;\n\tif (!scheduledRun.target.slackUserId) {\n\t\treturn {\n\t\t\tuserId: `scheduler:${scheduledRun.id}`,\n\t\t\tdisplayName: \"Bee Scheduler\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst info = await webClient.users.info({ user: scheduledRun.target.slackUserId });\n\t\tconst user = info.user as\n\t\t\t| { id?: string; name?: string; real_name?: string; profile?: { display_name?: string } }\n\t\t\t| undefined;\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tuserName: user?.name,\n\t\t\tdisplayName: user?.profile?.display_name || user?.real_name || user?.name,\n\t\t};\n\t} catch (error) {\n\t\tlog.logWarning(\n\t\t\t`Could not resolve Slack user ${scheduledRun.target.slackUserId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tdisplayName: scheduledRun.target.slackUserId,\n\t\t};\n\t}\n}\n\nasync function streamScheduledTurn(\n\tworkerClient: BeeWorkerClient,\n\tsink: SlackSink,\n\tinput: BeeResolvedTurn,\n\tstatusRef?: string,\n): Promise<void> {\n\tconst state: RenderState = {\n\t\tstatusRef,\n\t\tlatestText: \"_Working..._\",\n\t\trequestText: input.message.text,\n\t\titemTexts: new Map<string, string>(),\n\t};\n\tconst request = {\n\t\tsessionId: input.sessionId,\n\t\tthreadId: input.threadId,\n\t\tturnId: newTurnId(),\n\t\tconversation: input.conversation,\n\t\tactor: input.actor,\n\t\tmessage: input.message,\n\t\tattachments: input.attachments,\n\t};\n\n\ttry {\n\t\tawait withTracedTurn(\n\t\t\t\"slack.scheduled_turn\",\n\t\t\t{ \"bee.session.id\": input.sessionId, \"bee.transport\": \"slack\" },\n\t\t\tasync (telemetry) => {\n\t\t\t\tawait workerClient.streamTurn(input.worker, { ...request, telemetry }, async (event) => {\n\t\t\t\t\tawait handleScheduledEvent(sink, input.output, event, state);\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait sink.postMessage(input.output, `_Scheduled gateway error: ${message}_`);\n\t\tthrow error;\n\t}\n}\n\nasync function handleScheduledEvent(\n\tsink: SlackSink,\n\toutput: TransportOutputTarget,\n\tevent: BeeRunEvent,\n\tstate: RenderState,\n): Promise<void> {\n\tif (event.name === \"run.started\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.completed\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.failed\") {\n\t\tconst errorText = `_Error: ${asRunFailedPayload(event).error}_`;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, errorText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, errorText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"approval.requested\") {\n\t\tawait sink.postMessage(output, `Approval requested: ${asApprovalRequestedPayload(event).summary}`);\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.appended\") {\n\t\tconst payload = asItemAppendedPayload(event);\n\t\tconst text = renderParts(payload.item.parts);\n\t\tstate.itemTexts.set(payload.item.id, text);\n\t\tif (payload.item.kind === \"artifact\") {\n\t\t\tconst artifact = firstArtifactRef(payload.item.parts);\n\t\t\tif (artifact && artifactHasPayload(artifact)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait sink.publishArtifact(output, artifact);\n\t\t\t\t\treturn;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tawait sink.postMessage(output, `${text}\\n_Artifact upload failed: ${message}_`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait sink.postMessage(output, text);\n\t\t\treturn;\n\t\t}\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, text);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.updated\") {\n\t\tconst payload = asItemUpdatedPayload(event);\n\t\tconst current = state.itemTexts.get(payload.itemId) || \"\";\n\t\tconst appended = renderParts(payload.appendParts || []);\n\t\tconst next = current ? `${current}${appended}` : appended;\n\t\tstate.itemTexts.set(payload.itemId, next);\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, next);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t}\n}\n\nfunction firstArtifactRef(parts: ItemPartLike[]): ArtifactRef | undefined {\n\tconst part = parts.find((entry) => entry.kind === \"artifactRef\");\n\tif (!part) return undefined;\n\treturn {\n\t\tartifactId: String(part.artifactId || \"artifact\"),\n\t\tblobKey: typeof part.blobKey === \"string\" ? part.blobKey : undefined,\n\t\tname: typeof part.name === \"string\" ? part.name : undefined,\n\t\ttitle: typeof part.title === \"string\" ? part.title : undefined,\n\t\tmimeType: typeof part.mimeType === \"string\" ? part.mimeType : undefined,\n\t\turi: typeof part.uri === \"string\" ? part.uri : undefined,\n\t\tsizeBytes: typeof part.sizeBytes === \"number\" ? part.sizeBytes : undefined,\n\t};\n}\n\nfunction artifactHasPayload(artifact: ArtifactRef): boolean {\n\treturn !!(artifact.uri || artifact.blobKey);\n}\n\nexport function renderScheduledFinalMessage(requestText: string | undefined, responseText: string): string {\n\tconst request = requestText?.trim();\n\tif (!request) return responseText;\n\n\tconst quotedRequest = request\n\t\t.split(/\\r?\\n/)\n\t\t.map((line) => `> ${escapeSlackText(line)}`)\n\t\t.join(\"\\n\");\n\n\treturn [\"*Request:*\", quotedRequest, \"*Antwort:*\", responseText].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction escapeSlackText(text: string): string {\n\treturn text.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction renderParts(parts: ItemPartLike[]): string {\n\treturn parts\n\t\t.map((part) => {\n\t\t\tif (part.kind === \"text\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"status\") return String(part.status || \"\");\n\t\t\tif (part.kind === \"artifactRef\") {\n\t\t\t\treturn `Artifact: ${String(part.title || part.name || part.artifactId || \"artifact\")}`;\n\t\t\t}\n\t\t\tif (part.kind === \"approval\" || part.kind === \"choice\") {\n\t\t\t\treturn `${String(part.title || \"\")}\\n${String(part.summary || \"\")}`.trim();\n\t\t\t}\n\t\t\tif (part.kind === \"form\") return String(part.title || \"\");\n\t\t\tif (part.kind === \"log\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"patch\" || part.kind === \"diff\") {\n\t\t\t\tconst files = Array.isArray(part.files) ? (part.files as Array<{ path?: unknown }>) : [];\n\t\t\t\treturn files.map((entry) => `File: ${String(entry.path || \"\")}`).join(\"\\n\");\n\t\t\t}\n\t\t\treturn JSON.stringify(part);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction validateScheduledRunConfig(config: SlackScheduledRunConfig, source: string): void {\n\tif (!config.id) throw new Error(`Missing id in ${source}`);\n\tif (!config.routeId) throw new Error(`Missing routeId in ${source}`);\n\tif (!config.text) throw new Error(`Missing text in ${source}`);\n\tif (!config.target) throw new Error(`Missing target in ${source}`);\n\tif (!config.target.slackUserId && !config.target.channelId) {\n\t\tthrow new Error(`Missing target.slackUserId or target.channelId in ${source}`);\n\t}\n}\n\nfunction asRunFailedPayload(event: BeeRunEvent): RunFailedPayload {\n\treturn event.payload as RunFailedPayload;\n}\n\nfunction asApprovalRequestedPayload(event: BeeRunEvent): ApprovalRequestedPayload {\n\treturn event.payload as ApprovalRequestedPayload;\n}\n\nfunction asItemAppendedPayload(event: BeeRunEvent): ItemAppendedPayload {\n\treturn event.payload as ItemAppendedPayload;\n}\n\nfunction asItemUpdatedPayload(event: BeeRunEvent): ItemUpdatedPayload {\n\treturn event.payload as ItemUpdatedPayload;\n}\n"]}
|
package/dist/scheduled.js
CHANGED
|
@@ -5,6 +5,7 @@ import { join, resolve } from "path";
|
|
|
5
5
|
import { loadConfig } from "./config.js";
|
|
6
6
|
import * as log from "./log.js";
|
|
7
7
|
import { SlackSink } from "./slack-sink.js";
|
|
8
|
+
import { withTracedTurn } from "./telemetry.js";
|
|
8
9
|
export function loadScheduledRunConfig(jobPath) {
|
|
9
10
|
const path = jobPath || process.env.BEE_SLACK_SCHEDULED_RUN_CONFIG;
|
|
10
11
|
if (!path) {
|
|
@@ -144,8 +145,10 @@ async function streamScheduledTurn(workerClient, sink, input, statusRef) {
|
|
|
144
145
|
attachments: input.attachments,
|
|
145
146
|
};
|
|
146
147
|
try {
|
|
147
|
-
await
|
|
148
|
-
await
|
|
148
|
+
await withTracedTurn("slack.scheduled_turn", { "bee.session.id": input.sessionId, "bee.transport": "slack" }, async (telemetry) => {
|
|
149
|
+
await workerClient.streamTurn(input.worker, { ...request, telemetry }, async (event) => {
|
|
150
|
+
await handleScheduledEvent(sink, input.output, event, state);
|
|
151
|
+
});
|
|
149
152
|
});
|
|
150
153
|
}
|
|
151
154
|
catch (error) {
|
package/dist/scheduled.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled.js","sourceRoot":"","sources":["../src/scheduled.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ArtifactRef,\n\ttype BeeResolvedTurn,\n\ttype BeeRunEvent,\n\ttype BeeWorkerClient,\n\tbuildConversationId,\n\tbuildSessionKey,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\tnewTurnId,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { WebClient } from \"@slack/web-api\";\nimport { readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport * as log from \"./log.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport type { SlackGatewayConfig } from \"./types.js\";\n\nexport interface SlackScheduledRunConfig {\n\tid: string;\n\trouteId: string;\n\ttext: string;\n\ttarget: {\n\t\tslackUserId?: string;\n\t\tchannelId?: string;\n\t\tthreadTs?: string;\n\t};\n\tactor?: {\n\t\tuserId: string;\n\t\tuserName?: string;\n\t\tdisplayName?: string;\n\t};\n\tsessionId?: string;\n\tsessionPrefix?: string;\n\tconversationId?: string;\n}\n\ninterface RenderState {\n\tstatusRef?: string;\n\tlatestText: string;\n\trequestText?: string;\n\titemTexts: Map<string, string>;\n}\n\ntype ItemPartLike = { kind: string; [key: string]: unknown };\n\ntype ItemAppendedPayload = {\n\teventType: \"item.appended\";\n\titem: { id: string; kind: string; role: string; parts: ItemPartLike[] };\n};\n\ntype ItemUpdatedPayload = {\n\teventType: \"item.updated\";\n\titemId: string;\n\tappendParts?: ItemPartLike[];\n};\n\ntype RunFailedPayload = {\n\teventType: \"run.failed\";\n\terror: string;\n};\n\ntype ApprovalRequestedPayload = {\n\tsummary: string;\n};\n\nexport function loadScheduledRunConfig(jobPath?: string): SlackScheduledRunConfig {\n\tconst path = jobPath || process.env.BEE_SLACK_SCHEDULED_RUN_CONFIG;\n\tif (!path) {\n\t\tthrow new Error(\n\t\t\t\"Missing scheduled run config path; pass it as second argument or set BEE_SLACK_SCHEDULED_RUN_CONFIG\",\n\t\t);\n\t}\n\n\tconst fullPath = resolve(path);\n\tconst config = JSON.parse(readFileSync(fullPath, \"utf-8\")) as SlackScheduledRunConfig;\n\tvalidateScheduledRunConfig(config, fullPath);\n\treturn config;\n}\n\nexport async function runScheduledSlackTurnFromFiles(configPath?: string, jobPath?: string): Promise<void> {\n\tconst gatewayConfig = loadConfig(configPath);\n\tconst scheduledRun = loadScheduledRunConfig(jobPath);\n\tawait runScheduledSlackTurn(gatewayConfig, scheduledRun);\n}\n\nexport async function runScheduledSlackTurn(\n\tgatewayConfig: SlackGatewayConfig,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<void> {\n\tvalidateScheduledRunConfig(scheduledRun, \"scheduled run config\");\n\tconst route = gatewayConfig.routes.find((candidate) => candidate.id === scheduledRun.routeId);\n\tif (!route) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} references unknown routeId ${scheduledRun.routeId}`);\n\t}\n\n\tconst webClient = new WebClient(gatewayConfig.botToken);\n\tconst blobStore = new LocalFileBlobStore(\n\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t);\n\tconst sink = new SlackSink(webClient, blobStore);\n\tconst workerClient = await createNatsBeeClient(gatewayConfig.nats);\n\n\ttry {\n\t\tconst auth = await webClient.auth.test();\n\t\tconst teamId = String(auth.team_id || \"unknown-team\");\n\t\tconst output = await resolveScheduledOutputTarget(webClient, scheduledRun);\n\t\tconst actor = await resolveScheduledActor(webClient, scheduledRun);\n\t\tlet statusRef: string | undefined;\n\t\tif (!output.threadId) {\n\t\t\tstatusRef = await sink.postMessage({ channelId: output.channelId }, \"_Working..._\");\n\t\t\toutput.threadId = statusRef;\n\t\t}\n\t\tconst slackConversationId = buildConversationId([\n\t\t\t\"slack\",\n\t\t\tteamId,\n\t\t\toutput.channelId || \"unknown-channel\",\n\t\t\toutput.threadId,\n\t\t]);\n\t\tconst conversationId = scheduledRun.conversationId || slackConversationId;\n\t\tconst sessionBase =\n\t\t\troute.session?.strategy === \"channel\" ? output.channelId || \"unknown-channel\" : output.threadId;\n\t\tconst routeSessionId = buildSessionKey(\n\t\t\tscheduledRun.sessionPrefix || route.session?.prefix || route.id,\n\t\t\tbuildConversationId([\"slack\", teamId, output.channelId || \"unknown-channel\", sessionBase || output.threadId]),\n\t\t);\n\t\tconst sessionId = scheduledRun.sessionId || routeSessionId;\n\n\t\tconst input: BeeResolvedTurn = {\n\t\t\tsessionId,\n\t\t\tthreadId: output.threadId,\n\t\t\tworker: route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId,\n\t\t\t},\n\t\t\tactor,\n\t\t\tmessage: {\n\t\t\t\ttext: scheduledRun.text,\n\t\t\t},\n\t\t\tattachments: [],\n\t\t\toutput,\n\t\t};\n\n\t\tlog.logInfo(`Starting scheduled Slack run ${scheduledRun.id} on route ${route.id}`);\n\t\tawait streamScheduledTurn(workerClient, sink, input, statusRef);\n\t\tlog.logInfo(`Completed scheduled Slack run ${scheduledRun.id}`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog.logError(`Scheduled Slack run ${scheduledRun.id} failed`, message);\n\t\tthrow error;\n\t} finally {\n\t\tawait workerClient.close?.();\n\t}\n}\n\nasync function resolveScheduledOutputTarget(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<TransportOutputTarget> {\n\tif (scheduledRun.target.channelId) {\n\t\treturn {\n\t\t\tchannelId: scheduledRun.target.channelId,\n\t\t\tthreadId: scheduledRun.target.threadTs,\n\t\t};\n\t}\n\n\tif (!scheduledRun.target.slackUserId) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} needs target.slackUserId or target.channelId`);\n\t}\n\n\tconst result = await webClient.conversations.open({ users: scheduledRun.target.slackUserId });\n\tconst channel = result.channel as { id?: string } | undefined;\n\tif (!channel?.id) {\n\t\tthrow new Error(`Slack conversations.open did not return a channel for user ${scheduledRun.target.slackUserId}`);\n\t}\n\treturn {\n\t\tchannelId: channel.id,\n\t\tthreadId: scheduledRun.target.threadTs,\n\t};\n}\n\nasync function resolveScheduledActor(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<BeeResolvedTurn[\"actor\"]> {\n\tif (scheduledRun.actor) return scheduledRun.actor;\n\tif (!scheduledRun.target.slackUserId) {\n\t\treturn {\n\t\t\tuserId: `scheduler:${scheduledRun.id}`,\n\t\t\tdisplayName: \"Bee Scheduler\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst info = await webClient.users.info({ user: scheduledRun.target.slackUserId });\n\t\tconst user = info.user as\n\t\t\t| { id?: string; name?: string; real_name?: string; profile?: { display_name?: string } }\n\t\t\t| undefined;\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tuserName: user?.name,\n\t\t\tdisplayName: user?.profile?.display_name || user?.real_name || user?.name,\n\t\t};\n\t} catch (error) {\n\t\tlog.logWarning(\n\t\t\t`Could not resolve Slack user ${scheduledRun.target.slackUserId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tdisplayName: scheduledRun.target.slackUserId,\n\t\t};\n\t}\n}\n\nasync function streamScheduledTurn(\n\tworkerClient: BeeWorkerClient,\n\tsink: SlackSink,\n\tinput: BeeResolvedTurn,\n\tstatusRef?: string,\n): Promise<void> {\n\tconst state: RenderState = {\n\t\tstatusRef,\n\t\tlatestText: \"_Working..._\",\n\t\trequestText: input.message.text,\n\t\titemTexts: new Map<string, string>(),\n\t};\n\tconst request = {\n\t\tsessionId: input.sessionId,\n\t\tthreadId: input.threadId,\n\t\tturnId: newTurnId(),\n\t\tconversation: input.conversation,\n\t\tactor: input.actor,\n\t\tmessage: input.message,\n\t\tattachments: input.attachments,\n\t};\n\n\ttry {\n\t\tawait workerClient.streamTurn(input.worker, request, async (event) => {\n\t\t\tawait handleScheduledEvent(sink, input.output, event, state);\n\t\t});\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait sink.postMessage(input.output, `_Scheduled gateway error: ${message}_`);\n\t\tthrow error;\n\t}\n}\n\nasync function handleScheduledEvent(\n\tsink: SlackSink,\n\toutput: TransportOutputTarget,\n\tevent: BeeRunEvent,\n\tstate: RenderState,\n): Promise<void> {\n\tif (event.name === \"run.started\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.completed\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.failed\") {\n\t\tconst errorText = `_Error: ${asRunFailedPayload(event).error}_`;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, errorText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, errorText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"approval.requested\") {\n\t\tawait sink.postMessage(output, `Approval requested: ${asApprovalRequestedPayload(event).summary}`);\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.appended\") {\n\t\tconst payload = asItemAppendedPayload(event);\n\t\tconst text = renderParts(payload.item.parts);\n\t\tstate.itemTexts.set(payload.item.id, text);\n\t\tif (payload.item.kind === \"artifact\") {\n\t\t\tconst artifact = firstArtifactRef(payload.item.parts);\n\t\t\tif (artifact && artifactHasPayload(artifact)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait sink.publishArtifact(output, artifact);\n\t\t\t\t\treturn;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tawait sink.postMessage(output, `${text}\\n_Artifact upload failed: ${message}_`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait sink.postMessage(output, text);\n\t\t\treturn;\n\t\t}\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, text);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.updated\") {\n\t\tconst payload = asItemUpdatedPayload(event);\n\t\tconst current = state.itemTexts.get(payload.itemId) || \"\";\n\t\tconst appended = renderParts(payload.appendParts || []);\n\t\tconst next = current ? `${current}${appended}` : appended;\n\t\tstate.itemTexts.set(payload.itemId, next);\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, next);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t}\n}\n\nfunction firstArtifactRef(parts: ItemPartLike[]): ArtifactRef | undefined {\n\tconst part = parts.find((entry) => entry.kind === \"artifactRef\");\n\tif (!part) return undefined;\n\treturn {\n\t\tartifactId: String(part.artifactId || \"artifact\"),\n\t\tblobKey: typeof part.blobKey === \"string\" ? part.blobKey : undefined,\n\t\tname: typeof part.name === \"string\" ? part.name : undefined,\n\t\ttitle: typeof part.title === \"string\" ? part.title : undefined,\n\t\tmimeType: typeof part.mimeType === \"string\" ? part.mimeType : undefined,\n\t\turi: typeof part.uri === \"string\" ? part.uri : undefined,\n\t\tsizeBytes: typeof part.sizeBytes === \"number\" ? part.sizeBytes : undefined,\n\t};\n}\n\nfunction artifactHasPayload(artifact: ArtifactRef): boolean {\n\treturn !!(artifact.uri || artifact.blobKey);\n}\n\nexport function renderScheduledFinalMessage(requestText: string | undefined, responseText: string): string {\n\tconst request = requestText?.trim();\n\tif (!request) return responseText;\n\n\tconst quotedRequest = request\n\t\t.split(/\\r?\\n/)\n\t\t.map((line) => `> ${escapeSlackText(line)}`)\n\t\t.join(\"\\n\");\n\n\treturn [\"*Request:*\", quotedRequest, \"*Antwort:*\", responseText].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction escapeSlackText(text: string): string {\n\treturn text.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction renderParts(parts: ItemPartLike[]): string {\n\treturn parts\n\t\t.map((part) => {\n\t\t\tif (part.kind === \"text\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"status\") return String(part.status || \"\");\n\t\t\tif (part.kind === \"artifactRef\") {\n\t\t\t\treturn `Artifact: ${String(part.title || part.name || part.artifactId || \"artifact\")}`;\n\t\t\t}\n\t\t\tif (part.kind === \"approval\" || part.kind === \"choice\") {\n\t\t\t\treturn `${String(part.title || \"\")}\\n${String(part.summary || \"\")}`.trim();\n\t\t\t}\n\t\t\tif (part.kind === \"form\") return String(part.title || \"\");\n\t\t\tif (part.kind === \"log\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"patch\" || part.kind === \"diff\") {\n\t\t\t\tconst files = Array.isArray(part.files) ? (part.files as Array<{ path?: unknown }>) : [];\n\t\t\t\treturn files.map((entry) => `File: ${String(entry.path || \"\")}`).join(\"\\n\");\n\t\t\t}\n\t\t\treturn JSON.stringify(part);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction validateScheduledRunConfig(config: SlackScheduledRunConfig, source: string): void {\n\tif (!config.id) throw new Error(`Missing id in ${source}`);\n\tif (!config.routeId) throw new Error(`Missing routeId in ${source}`);\n\tif (!config.text) throw new Error(`Missing text in ${source}`);\n\tif (!config.target) throw new Error(`Missing target in ${source}`);\n\tif (!config.target.slackUserId && !config.target.channelId) {\n\t\tthrow new Error(`Missing target.slackUserId or target.channelId in ${source}`);\n\t}\n}\n\nfunction asRunFailedPayload(event: BeeRunEvent): RunFailedPayload {\n\treturn event.payload as RunFailedPayload;\n}\n\nfunction asApprovalRequestedPayload(event: BeeRunEvent): ApprovalRequestedPayload {\n\treturn event.payload as ApprovalRequestedPayload;\n}\n\nfunction asItemAppendedPayload(event: BeeRunEvent): ItemAppendedPayload {\n\treturn event.payload as ItemAppendedPayload;\n}\n\nfunction asItemUpdatedPayload(event: BeeRunEvent): ItemUpdatedPayload {\n\treturn event.payload as ItemUpdatedPayload;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scheduled.js","sourceRoot":"","sources":["../src/scheduled.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ArtifactRef,\n\ttype BeeResolvedTurn,\n\ttype BeeRunEvent,\n\ttype BeeWorkerClient,\n\tbuildConversationId,\n\tbuildSessionKey,\n\tcreateNatsBeeClient,\n\tLocalFileBlobStore,\n\tnewTurnId,\n\ttype TransportOutputTarget,\n} from \"@jobmatchme/bee-gate\";\nimport { WebClient } from \"@slack/web-api\";\nimport { readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { loadConfig } from \"./config.js\";\nimport * as log from \"./log.js\";\nimport { SlackSink } from \"./slack-sink.js\";\nimport { withTracedTurn } from \"./telemetry.js\";\nimport type { SlackGatewayConfig } from \"./types.js\";\n\nexport interface SlackScheduledRunConfig {\n\tid: string;\n\trouteId: string;\n\ttext: string;\n\ttarget: {\n\t\tslackUserId?: string;\n\t\tchannelId?: string;\n\t\tthreadTs?: string;\n\t};\n\tactor?: {\n\t\tuserId: string;\n\t\tuserName?: string;\n\t\tdisplayName?: string;\n\t};\n\tsessionId?: string;\n\tsessionPrefix?: string;\n\tconversationId?: string;\n}\n\ninterface RenderState {\n\tstatusRef?: string;\n\tlatestText: string;\n\trequestText?: string;\n\titemTexts: Map<string, string>;\n}\n\ntype ItemPartLike = { kind: string; [key: string]: unknown };\n\ntype ItemAppendedPayload = {\n\teventType: \"item.appended\";\n\titem: { id: string; kind: string; role: string; parts: ItemPartLike[] };\n};\n\ntype ItemUpdatedPayload = {\n\teventType: \"item.updated\";\n\titemId: string;\n\tappendParts?: ItemPartLike[];\n};\n\ntype RunFailedPayload = {\n\teventType: \"run.failed\";\n\terror: string;\n};\n\ntype ApprovalRequestedPayload = {\n\tsummary: string;\n};\n\nexport function loadScheduledRunConfig(jobPath?: string): SlackScheduledRunConfig {\n\tconst path = jobPath || process.env.BEE_SLACK_SCHEDULED_RUN_CONFIG;\n\tif (!path) {\n\t\tthrow new Error(\n\t\t\t\"Missing scheduled run config path; pass it as second argument or set BEE_SLACK_SCHEDULED_RUN_CONFIG\",\n\t\t);\n\t}\n\n\tconst fullPath = resolve(path);\n\tconst config = JSON.parse(readFileSync(fullPath, \"utf-8\")) as SlackScheduledRunConfig;\n\tvalidateScheduledRunConfig(config, fullPath);\n\treturn config;\n}\n\nexport async function runScheduledSlackTurnFromFiles(configPath?: string, jobPath?: string): Promise<void> {\n\tconst gatewayConfig = loadConfig(configPath);\n\tconst scheduledRun = loadScheduledRunConfig(jobPath);\n\tawait runScheduledSlackTurn(gatewayConfig, scheduledRun);\n}\n\nexport async function runScheduledSlackTurn(\n\tgatewayConfig: SlackGatewayConfig,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<void> {\n\tvalidateScheduledRunConfig(scheduledRun, \"scheduled run config\");\n\tconst route = gatewayConfig.routes.find((candidate) => candidate.id === scheduledRun.routeId);\n\tif (!route) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} references unknown routeId ${scheduledRun.routeId}`);\n\t}\n\n\tconst webClient = new WebClient(gatewayConfig.botToken);\n\tconst blobStore = new LocalFileBlobStore(\n\t\tprocess.env.BEE_SLACK_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.BEE_BLOB_STORE_ROOT ||\n\t\t\tprocess.env.HUDAI_BLOB_STORE_ROOT ||\n\t\t\tjoin(process.cwd(), \".bee-blob-store\"),\n\t);\n\tconst sink = new SlackSink(webClient, blobStore);\n\tconst workerClient = await createNatsBeeClient(gatewayConfig.nats);\n\n\ttry {\n\t\tconst auth = await webClient.auth.test();\n\t\tconst teamId = String(auth.team_id || \"unknown-team\");\n\t\tconst output = await resolveScheduledOutputTarget(webClient, scheduledRun);\n\t\tconst actor = await resolveScheduledActor(webClient, scheduledRun);\n\t\tlet statusRef: string | undefined;\n\t\tif (!output.threadId) {\n\t\t\tstatusRef = await sink.postMessage({ channelId: output.channelId }, \"_Working..._\");\n\t\t\toutput.threadId = statusRef;\n\t\t}\n\t\tconst slackConversationId = buildConversationId([\n\t\t\t\"slack\",\n\t\t\tteamId,\n\t\t\toutput.channelId || \"unknown-channel\",\n\t\t\toutput.threadId,\n\t\t]);\n\t\tconst conversationId = scheduledRun.conversationId || slackConversationId;\n\t\tconst sessionBase =\n\t\t\troute.session?.strategy === \"channel\" ? output.channelId || \"unknown-channel\" : output.threadId;\n\t\tconst routeSessionId = buildSessionKey(\n\t\t\tscheduledRun.sessionPrefix || route.session?.prefix || route.id,\n\t\t\tbuildConversationId([\"slack\", teamId, output.channelId || \"unknown-channel\", sessionBase || output.threadId]),\n\t\t);\n\t\tconst sessionId = scheduledRun.sessionId || routeSessionId;\n\n\t\tconst input: BeeResolvedTurn = {\n\t\t\tsessionId,\n\t\t\tthreadId: output.threadId,\n\t\t\tworker: route.worker,\n\t\t\tconversation: {\n\t\t\t\ttransport: \"slack\",\n\t\t\t\tconversationId,\n\t\t\t},\n\t\t\tactor,\n\t\t\tmessage: {\n\t\t\t\ttext: scheduledRun.text,\n\t\t\t},\n\t\t\tattachments: [],\n\t\t\toutput,\n\t\t};\n\n\t\tlog.logInfo(`Starting scheduled Slack run ${scheduledRun.id} on route ${route.id}`);\n\t\tawait streamScheduledTurn(workerClient, sink, input, statusRef);\n\t\tlog.logInfo(`Completed scheduled Slack run ${scheduledRun.id}`);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tlog.logError(`Scheduled Slack run ${scheduledRun.id} failed`, message);\n\t\tthrow error;\n\t} finally {\n\t\tawait workerClient.close?.();\n\t}\n}\n\nasync function resolveScheduledOutputTarget(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<TransportOutputTarget> {\n\tif (scheduledRun.target.channelId) {\n\t\treturn {\n\t\t\tchannelId: scheduledRun.target.channelId,\n\t\t\tthreadId: scheduledRun.target.threadTs,\n\t\t};\n\t}\n\n\tif (!scheduledRun.target.slackUserId) {\n\t\tthrow new Error(`Scheduled run ${scheduledRun.id} needs target.slackUserId or target.channelId`);\n\t}\n\n\tconst result = await webClient.conversations.open({ users: scheduledRun.target.slackUserId });\n\tconst channel = result.channel as { id?: string } | undefined;\n\tif (!channel?.id) {\n\t\tthrow new Error(`Slack conversations.open did not return a channel for user ${scheduledRun.target.slackUserId}`);\n\t}\n\treturn {\n\t\tchannelId: channel.id,\n\t\tthreadId: scheduledRun.target.threadTs,\n\t};\n}\n\nasync function resolveScheduledActor(\n\twebClient: WebClient,\n\tscheduledRun: SlackScheduledRunConfig,\n): Promise<BeeResolvedTurn[\"actor\"]> {\n\tif (scheduledRun.actor) return scheduledRun.actor;\n\tif (!scheduledRun.target.slackUserId) {\n\t\treturn {\n\t\t\tuserId: `scheduler:${scheduledRun.id}`,\n\t\t\tdisplayName: \"Bee Scheduler\",\n\t\t};\n\t}\n\n\ttry {\n\t\tconst info = await webClient.users.info({ user: scheduledRun.target.slackUserId });\n\t\tconst user = info.user as\n\t\t\t| { id?: string; name?: string; real_name?: string; profile?: { display_name?: string } }\n\t\t\t| undefined;\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tuserName: user?.name,\n\t\t\tdisplayName: user?.profile?.display_name || user?.real_name || user?.name,\n\t\t};\n\t} catch (error) {\n\t\tlog.logWarning(\n\t\t\t`Could not resolve Slack user ${scheduledRun.target.slackUserId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t\treturn {\n\t\t\tuserId: scheduledRun.target.slackUserId,\n\t\t\tdisplayName: scheduledRun.target.slackUserId,\n\t\t};\n\t}\n}\n\nasync function streamScheduledTurn(\n\tworkerClient: BeeWorkerClient,\n\tsink: SlackSink,\n\tinput: BeeResolvedTurn,\n\tstatusRef?: string,\n): Promise<void> {\n\tconst state: RenderState = {\n\t\tstatusRef,\n\t\tlatestText: \"_Working..._\",\n\t\trequestText: input.message.text,\n\t\titemTexts: new Map<string, string>(),\n\t};\n\tconst request = {\n\t\tsessionId: input.sessionId,\n\t\tthreadId: input.threadId,\n\t\tturnId: newTurnId(),\n\t\tconversation: input.conversation,\n\t\tactor: input.actor,\n\t\tmessage: input.message,\n\t\tattachments: input.attachments,\n\t};\n\n\ttry {\n\t\tawait withTracedTurn(\n\t\t\t\"slack.scheduled_turn\",\n\t\t\t{ \"bee.session.id\": input.sessionId, \"bee.transport\": \"slack\" },\n\t\t\tasync (telemetry) => {\n\t\t\t\tawait workerClient.streamTurn(input.worker, { ...request, telemetry }, async (event) => {\n\t\t\t\t\tawait handleScheduledEvent(sink, input.output, event, state);\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tawait sink.postMessage(input.output, `_Scheduled gateway error: ${message}_`);\n\t\tthrow error;\n\t}\n}\n\nasync function handleScheduledEvent(\n\tsink: SlackSink,\n\toutput: TransportOutputTarget,\n\tevent: BeeRunEvent,\n\tstate: RenderState,\n): Promise<void> {\n\tif (event.name === \"run.started\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.completed\") {\n\t\tif (!state.statusRef) {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"run.failed\") {\n\t\tconst errorText = `_Error: ${asRunFailedPayload(event).error}_`;\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, errorText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, errorText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"approval.requested\") {\n\t\tawait sink.postMessage(output, `Approval requested: ${asApprovalRequestedPayload(event).summary}`);\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.appended\") {\n\t\tconst payload = asItemAppendedPayload(event);\n\t\tconst text = renderParts(payload.item.parts);\n\t\tstate.itemTexts.set(payload.item.id, text);\n\t\tif (payload.item.kind === \"artifact\") {\n\t\t\tconst artifact = firstArtifactRef(payload.item.parts);\n\t\t\tif (artifact && artifactHasPayload(artifact)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait sink.publishArtifact(output, artifact);\n\t\t\t\t\treturn;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tawait sink.postMessage(output, `${text}\\n_Artifact upload failed: ${message}_`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait sink.postMessage(output, text);\n\t\t\treturn;\n\t\t}\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, text);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (event.name === \"item.updated\") {\n\t\tconst payload = asItemUpdatedPayload(event);\n\t\tconst current = state.itemTexts.get(payload.itemId) || \"\";\n\t\tconst appended = renderParts(payload.appendParts || []);\n\t\tconst next = current ? `${current}${appended}` : appended;\n\t\tstate.itemTexts.set(payload.itemId, next);\n\t\tstate.latestText = renderScheduledFinalMessage(state.requestText, next);\n\t\tif (state.statusRef) {\n\t\t\tawait sink.updateMessage(output, state.statusRef, state.latestText);\n\t\t} else {\n\t\t\tstate.statusRef = await sink.postMessage(output, state.latestText);\n\t\t}\n\t}\n}\n\nfunction firstArtifactRef(parts: ItemPartLike[]): ArtifactRef | undefined {\n\tconst part = parts.find((entry) => entry.kind === \"artifactRef\");\n\tif (!part) return undefined;\n\treturn {\n\t\tartifactId: String(part.artifactId || \"artifact\"),\n\t\tblobKey: typeof part.blobKey === \"string\" ? part.blobKey : undefined,\n\t\tname: typeof part.name === \"string\" ? part.name : undefined,\n\t\ttitle: typeof part.title === \"string\" ? part.title : undefined,\n\t\tmimeType: typeof part.mimeType === \"string\" ? part.mimeType : undefined,\n\t\turi: typeof part.uri === \"string\" ? part.uri : undefined,\n\t\tsizeBytes: typeof part.sizeBytes === \"number\" ? part.sizeBytes : undefined,\n\t};\n}\n\nfunction artifactHasPayload(artifact: ArtifactRef): boolean {\n\treturn !!(artifact.uri || artifact.blobKey);\n}\n\nexport function renderScheduledFinalMessage(requestText: string | undefined, responseText: string): string {\n\tconst request = requestText?.trim();\n\tif (!request) return responseText;\n\n\tconst quotedRequest = request\n\t\t.split(/\\r?\\n/)\n\t\t.map((line) => `> ${escapeSlackText(line)}`)\n\t\t.join(\"\\n\");\n\n\treturn [\"*Request:*\", quotedRequest, \"*Antwort:*\", responseText].filter(Boolean).join(\"\\n\\n\");\n}\n\nfunction escapeSlackText(text: string): string {\n\treturn text.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction renderParts(parts: ItemPartLike[]): string {\n\treturn parts\n\t\t.map((part) => {\n\t\t\tif (part.kind === \"text\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"status\") return String(part.status || \"\");\n\t\t\tif (part.kind === \"artifactRef\") {\n\t\t\t\treturn `Artifact: ${String(part.title || part.name || part.artifactId || \"artifact\")}`;\n\t\t\t}\n\t\t\tif (part.kind === \"approval\" || part.kind === \"choice\") {\n\t\t\t\treturn `${String(part.title || \"\")}\\n${String(part.summary || \"\")}`.trim();\n\t\t\t}\n\t\t\tif (part.kind === \"form\") return String(part.title || \"\");\n\t\t\tif (part.kind === \"log\") return String(part.text || \"\");\n\t\t\tif (part.kind === \"patch\" || part.kind === \"diff\") {\n\t\t\t\tconst files = Array.isArray(part.files) ? (part.files as Array<{ path?: unknown }>) : [];\n\t\t\t\treturn files.map((entry) => `File: ${String(entry.path || \"\")}`).join(\"\\n\");\n\t\t\t}\n\t\t\treturn JSON.stringify(part);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\");\n}\n\nfunction validateScheduledRunConfig(config: SlackScheduledRunConfig, source: string): void {\n\tif (!config.id) throw new Error(`Missing id in ${source}`);\n\tif (!config.routeId) throw new Error(`Missing routeId in ${source}`);\n\tif (!config.text) throw new Error(`Missing text in ${source}`);\n\tif (!config.target) throw new Error(`Missing target in ${source}`);\n\tif (!config.target.slackUserId && !config.target.channelId) {\n\t\tthrow new Error(`Missing target.slackUserId or target.channelId in ${source}`);\n\t}\n}\n\nfunction asRunFailedPayload(event: BeeRunEvent): RunFailedPayload {\n\treturn event.payload as RunFailedPayload;\n}\n\nfunction asApprovalRequestedPayload(event: BeeRunEvent): ApprovalRequestedPayload {\n\treturn event.payload as ApprovalRequestedPayload;\n}\n\nfunction asItemAppendedPayload(event: BeeRunEvent): ItemAppendedPayload {\n\treturn event.payload as ItemAppendedPayload;\n}\n\nfunction asItemUpdatedPayload(event: BeeRunEvent): ItemUpdatedPayload {\n\treturn event.payload as ItemUpdatedPayload;\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface TelemetryCarrier {
|
|
2
|
+
traceparent?: string;
|
|
3
|
+
tracestate?: string;
|
|
4
|
+
baggage?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function initializeTelemetry(): void;
|
|
7
|
+
export declare function shutdownTelemetry(): Promise<void>;
|
|
8
|
+
export declare function dispatchTracedTurn(attributes: Record<string, string | undefined>, dispatch: (carrier: TelemetryCarrier) => void): void;
|
|
9
|
+
export declare function withTracedTurn<T>(name: string, attributes: Record<string, string | undefined>, run: (carrier: TelemetryCarrier) => Promise<T>): Promise<T>;
|
|
10
|
+
//# sourceMappingURL=telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,gBAAgB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAc1C;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAIvD;AAED,wBAAgB,kBAAkB,CACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC9C,QAAQ,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,GAC3C,IAAI,CAeN;AAED,wBAAsB,cAAc,CAAC,CAAC,EACrC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC9C,GAAG,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,GAC5C,OAAO,CAAC,CAAC,CAAC,CAeZ","sourcesContent":["import { context, propagation, SpanStatusCode, trace } from \"@opentelemetry/api\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\n\nconst tracer = trace.getTracer(\"bee-slack\");\nlet sdk: NodeSDK | undefined;\n\nexport interface TelemetryCarrier {\n\ttraceparent?: string;\n\ttracestate?: string;\n\tbaggage?: string;\n}\n\nexport function initializeTelemetry(): void {\n\tif (process.env.OTEL_SDK_DISABLED?.toLowerCase() === \"true\" || sdk) return;\n\tconst baseEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n\tconst configuredTraceEndpoint = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;\n\tif (!baseEndpoint && !configuredTraceEndpoint) return;\n\tconst traceEndpoint = configuredTraceEndpoint || `${baseEndpoint?.replace(/\\/$/, \"\") || \"\"}/v1/traces`;\n\tsdk = new NodeSDK({\n\t\tresource: resourceFromAttributes({\n\t\t\t[ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || \"bee-slack\",\n\t\t\t\"deployment.environment\": process.env.OTEL_DEPLOYMENT_ENVIRONMENT || \"unknown\",\n\t\t}),\n\t\ttraceExporter: new OTLPTraceExporter({ url: traceEndpoint }),\n\t});\n\tsdk.start();\n}\n\nexport async function shutdownTelemetry(): Promise<void> {\n\tconst activeSdk = sdk;\n\tsdk = undefined;\n\tawait activeSdk?.shutdown();\n}\n\nexport function dispatchTracedTurn(\n\tattributes: Record<string, string | undefined>,\n\tdispatch: (carrier: TelemetryCarrier) => void,\n): void {\n\ttracer.startActiveSpan(\"slack.turn\", { attributes }, (span) => {\n\t\ttry {\n\t\t\tconst carrier: TelemetryCarrier = {};\n\t\t\tpropagation.inject(context.active(), carrier);\n\t\t\tdispatch(carrier);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tspan.recordException(error instanceof Error ? error : new Error(message));\n\t\t\tspan.setStatus({ code: SpanStatusCode.ERROR, message });\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tspan.end();\n\t\t}\n\t});\n}\n\nexport async function withTracedTurn<T>(\n\tname: string,\n\tattributes: Record<string, string | undefined>,\n\trun: (carrier: TelemetryCarrier) => Promise<T>,\n): Promise<T> {\n\treturn tracer.startActiveSpan(name, { attributes }, async (span) => {\n\t\ttry {\n\t\t\tconst carrier: TelemetryCarrier = {};\n\t\t\tpropagation.inject(context.active(), carrier);\n\t\t\treturn await run(carrier);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tspan.recordException(error instanceof Error ? error : new Error(message));\n\t\t\tspan.setStatus({ code: SpanStatusCode.ERROR, message });\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tspan.end();\n\t\t}\n\t});\n}\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { context, propagation, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
2
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
3
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
4
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
5
|
+
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
6
|
+
const tracer = trace.getTracer("bee-slack");
|
|
7
|
+
let sdk;
|
|
8
|
+
export function initializeTelemetry() {
|
|
9
|
+
if (process.env.OTEL_SDK_DISABLED?.toLowerCase() === "true" || sdk)
|
|
10
|
+
return;
|
|
11
|
+
const baseEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
12
|
+
const configuredTraceEndpoint = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;
|
|
13
|
+
if (!baseEndpoint && !configuredTraceEndpoint)
|
|
14
|
+
return;
|
|
15
|
+
const traceEndpoint = configuredTraceEndpoint || `${baseEndpoint?.replace(/\/$/, "") || ""}/v1/traces`;
|
|
16
|
+
sdk = new NodeSDK({
|
|
17
|
+
resource: resourceFromAttributes({
|
|
18
|
+
[ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || "bee-slack",
|
|
19
|
+
"deployment.environment": process.env.OTEL_DEPLOYMENT_ENVIRONMENT || "unknown",
|
|
20
|
+
}),
|
|
21
|
+
traceExporter: new OTLPTraceExporter({ url: traceEndpoint }),
|
|
22
|
+
});
|
|
23
|
+
sdk.start();
|
|
24
|
+
}
|
|
25
|
+
export async function shutdownTelemetry() {
|
|
26
|
+
const activeSdk = sdk;
|
|
27
|
+
sdk = undefined;
|
|
28
|
+
await activeSdk?.shutdown();
|
|
29
|
+
}
|
|
30
|
+
export function dispatchTracedTurn(attributes, dispatch) {
|
|
31
|
+
tracer.startActiveSpan("slack.turn", { attributes }, (span) => {
|
|
32
|
+
try {
|
|
33
|
+
const carrier = {};
|
|
34
|
+
propagation.inject(context.active(), carrier);
|
|
35
|
+
dispatch(carrier);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
39
|
+
span.recordException(error instanceof Error ? error : new Error(message));
|
|
40
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
span.end();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export async function withTracedTurn(name, attributes, run) {
|
|
49
|
+
return tracer.startActiveSpan(name, { attributes }, async (span) => {
|
|
50
|
+
try {
|
|
51
|
+
const carrier = {};
|
|
52
|
+
propagation.inject(context.active(), carrier);
|
|
53
|
+
return await run(carrier);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
57
|
+
span.recordException(error instanceof Error ? error : new Error(message));
|
|
58
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
span.end();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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","sourcesContent":["import { context, propagation, SpanStatusCode, trace } from \"@opentelemetry/api\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\n\nconst tracer = trace.getTracer(\"bee-slack\");\nlet sdk: NodeSDK | undefined;\n\nexport interface TelemetryCarrier {\n\ttraceparent?: string;\n\ttracestate?: string;\n\tbaggage?: string;\n}\n\nexport function initializeTelemetry(): void {\n\tif (process.env.OTEL_SDK_DISABLED?.toLowerCase() === \"true\" || sdk) return;\n\tconst baseEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;\n\tconst configuredTraceEndpoint = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;\n\tif (!baseEndpoint && !configuredTraceEndpoint) return;\n\tconst traceEndpoint = configuredTraceEndpoint || `${baseEndpoint?.replace(/\\/$/, \"\") || \"\"}/v1/traces`;\n\tsdk = new NodeSDK({\n\t\tresource: resourceFromAttributes({\n\t\t\t[ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || \"bee-slack\",\n\t\t\t\"deployment.environment\": process.env.OTEL_DEPLOYMENT_ENVIRONMENT || \"unknown\",\n\t\t}),\n\t\ttraceExporter: new OTLPTraceExporter({ url: traceEndpoint }),\n\t});\n\tsdk.start();\n}\n\nexport async function shutdownTelemetry(): Promise<void> {\n\tconst activeSdk = sdk;\n\tsdk = undefined;\n\tawait activeSdk?.shutdown();\n}\n\nexport function dispatchTracedTurn(\n\tattributes: Record<string, string | undefined>,\n\tdispatch: (carrier: TelemetryCarrier) => void,\n): void {\n\ttracer.startActiveSpan(\"slack.turn\", { attributes }, (span) => {\n\t\ttry {\n\t\t\tconst carrier: TelemetryCarrier = {};\n\t\t\tpropagation.inject(context.active(), carrier);\n\t\t\tdispatch(carrier);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tspan.recordException(error instanceof Error ? error : new Error(message));\n\t\t\tspan.setStatus({ code: SpanStatusCode.ERROR, message });\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tspan.end();\n\t\t}\n\t});\n}\n\nexport async function withTracedTurn<T>(\n\tname: string,\n\tattributes: Record<string, string | undefined>,\n\trun: (carrier: TelemetryCarrier) => Promise<T>,\n): Promise<T> {\n\treturn tracer.startActiveSpan(name, { attributes }, async (span) => {\n\t\ttry {\n\t\t\tconst carrier: TelemetryCarrier = {};\n\t\t\tpropagation.inject(context.active(), carrier);\n\t\t\treturn await run(carrier);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tspan.recordException(error instanceof Error ? error : new Error(message));\n\t\t\tspan.setStatus({ code: SpanStatusCode.ERROR, message });\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tspan.end();\n\t\t}\n\t});\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobmatchme/bee-slack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Slack adapter for Bee Gate and the Bee Dance protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -48,15 +48,20 @@
|
|
|
48
48
|
"prepublishOnly": "npm run clean && npm run check"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@jobmatchme/bee-gate": "0.1.
|
|
51
|
+
"@jobmatchme/bee-gate": "^0.1.10",
|
|
52
|
+
"@opentelemetry/api": "^1.9.1",
|
|
53
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.207.0",
|
|
54
|
+
"@opentelemetry/resources": "^2.10.0",
|
|
55
|
+
"@opentelemetry/sdk-node": "^0.207.0",
|
|
56
|
+
"@opentelemetry/semantic-conventions": "^1.43.0",
|
|
52
57
|
"@slack/socket-mode": "^2.0.0",
|
|
53
58
|
"@slack/web-api": "^7.11.0",
|
|
54
59
|
"chalk": "^5.6.2"
|
|
55
60
|
},
|
|
56
61
|
"devDependencies": {
|
|
57
62
|
"@biomejs/biome": "2.3.5",
|
|
58
|
-
"@typescript/native-preview": "7.0.0-dev.20260120.1",
|
|
59
63
|
"@types/node": "^24.3.0",
|
|
64
|
+
"@typescript/native-preview": "7.0.0-dev.20260120.1",
|
|
60
65
|
"@vitest/coverage-v8": "^3.2.4",
|
|
61
66
|
"husky": "^9.1.7",
|
|
62
67
|
"shx": "^0.4.0",
|