opensentinel 2.1.1
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/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/bot-KJ26BG56.js +15 -0
- package/dist/bot-KJ26BG56.js.map +1 -0
- package/dist/charts-MMXM6BWW.js +241 -0
- package/dist/charts-MMXM6BWW.js.map +1 -0
- package/dist/chunk-4LVWXUNC.js +1079 -0
- package/dist/chunk-4LVWXUNC.js.map +1 -0
- package/dist/chunk-4TG2IG5K.js +5249 -0
- package/dist/chunk-4TG2IG5K.js.map +1 -0
- package/dist/chunk-6DRDKB45.js +251 -0
- package/dist/chunk-6DRDKB45.js.map +1 -0
- package/dist/chunk-6SNHU3CY.js +123 -0
- package/dist/chunk-6SNHU3CY.js.map +1 -0
- package/dist/chunk-CI6Q63MM.js +1613 -0
- package/dist/chunk-CI6Q63MM.js.map +1 -0
- package/dist/chunk-CQ4JURG7.js +57 -0
- package/dist/chunk-CQ4JURG7.js.map +1 -0
- package/dist/chunk-F6QUZQGI.js +51 -0
- package/dist/chunk-F6QUZQGI.js.map +1 -0
- package/dist/chunk-GK3E2I7A.js +216 -0
- package/dist/chunk-GK3E2I7A.js.map +1 -0
- package/dist/chunk-GUBEEYDW.js +211 -0
- package/dist/chunk-GUBEEYDW.js.map +1 -0
- package/dist/chunk-GVJVEWHI.js +29 -0
- package/dist/chunk-GVJVEWHI.js.map +1 -0
- package/dist/chunk-HH2HBTQM.js +806 -0
- package/dist/chunk-HH2HBTQM.js.map +1 -0
- package/dist/chunk-JXUP2X7V.js +129 -0
- package/dist/chunk-JXUP2X7V.js.map +1 -0
- package/dist/chunk-KHNYJY2Z.js +178 -0
- package/dist/chunk-KHNYJY2Z.js.map +1 -0
- package/dist/chunk-L3F43VPB.js +652 -0
- package/dist/chunk-L3F43VPB.js.map +1 -0
- package/dist/chunk-L3PDU3XN.js +803 -0
- package/dist/chunk-L3PDU3XN.js.map +1 -0
- package/dist/chunk-NSBPE2FW.js +17 -0
- package/dist/chunk-NSBPE2FW.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +52 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/setup.d.ts +9 -0
- package/dist/commands/setup.js +374 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/start.d.ts +8 -0
- package/dist/commands/start.js +27 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.js +57 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +8 -0
- package/dist/commands/stop.js +37 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/utils.d.ts +50 -0
- package/dist/commands/utils.js +36 -0
- package/dist/commands/utils.js.map +1 -0
- package/dist/discord-ZOJFTVTB.js +49 -0
- package/dist/discord-ZOJFTVTB.js.map +1 -0
- package/dist/imessage-JFRB6EJ7.js +14 -0
- package/dist/imessage-JFRB6EJ7.js.map +1 -0
- package/dist/lib.d.ts +855 -0
- package/dist/lib.js +274 -0
- package/dist/lib.js.map +1 -0
- package/dist/mcp-LS7Q3Z5W.js +30 -0
- package/dist/mcp-LS7Q3Z5W.js.map +1 -0
- package/dist/scheduler-EZ7CZMCS.js +42 -0
- package/dist/scheduler-EZ7CZMCS.js.map +1 -0
- package/dist/signal-T3MCSULM.js +14 -0
- package/dist/signal-T3MCSULM.js.map +1 -0
- package/dist/slack-N2M4FHAJ.js +54 -0
- package/dist/slack-N2M4FHAJ.js.map +1 -0
- package/dist/src-K7GASHRH.js +430 -0
- package/dist/src-K7GASHRH.js.map +1 -0
- package/dist/tools-24GZHYRF.js +16 -0
- package/dist/tools-24GZHYRF.js.map +1 -0
- package/dist/whatsapp-VCRUPAO5.js +14 -0
- package/dist/whatsapp-VCRUPAO5.js.map +1 -0
- package/drizzle/0000_chilly_shinobi_shaw.sql +75 -0
- package/drizzle/0001_freezing_shape.sql +274 -0
- package/drizzle/meta/0000_snapshot.json +529 -0
- package/drizzle/meta/0001_snapshot.json +2576 -0
- package/drizzle/meta/_journal.json +20 -0
- package/package.json +98 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/inputs/slack/index.ts","../src/inputs/slack/commands.ts"],"sourcesContent":["// Use require-style for CJS/ESM compat\nimport { createRequire } from \"node:module\";\nconst require = createRequire(import.meta.url);\nconst { App, ExpressReceiver } = require(\"@slack/bolt\") as {\n App: typeof import(\"@slack/bolt\").App;\n ExpressReceiver: typeof import(\"@slack/bolt\").ExpressReceiver;\n};\nconst { WebClient } = require(\"@slack/web-api\") as {\n WebClient: typeof import(\"@slack/web-api\").WebClient;\n};\ntype SlackEventMiddlewareArgs<T extends string> = any;\ntype AllMiddlewareArgs = any;\ntype MessageEvent = any;\ntype AppMentionEvent = any;\ntype GenericMessageEvent = any;\ntype FileSharedEvent = any;\ntype ChatPostMessageResponse = any;\nimport { chatWithTools, type Message } from \"../../core/brain\";\nimport { transcribeAudio } from \"../../outputs/stt\";\nimport {\n slashCommands,\n getSession,\n addToSession,\n splitMessage,\n formatAsBlocks,\n} from \"./commands\";\n\n/**\n * Slack bot configuration\n */\nexport interface SlackBotConfig {\n token: string; // Bot OAuth token (xoxb-...)\n signingSecret: string; // Slack signing secret\n appToken?: string; // App-level token for Socket Mode (xapp-...)\n socketMode?: boolean; // Use Socket Mode instead of HTTP\n port?: number; // Port for HTTP receiver (default: 3000)\n allowedUserIds?: string[]; // Optional: restrict to specific users\n allowedChannelIds?: string[]; // Optional: restrict to specific channels\n allowDMs?: boolean; // Allow direct messages (default: true)\n allowMentions?: boolean; // Allow @mentions (default: true)\n allowThreadReplies?: boolean; // Allow thread replies (default: true)\n}\n\n/**\n * Slack bot session data\n */\nexport interface SlackSessionData {\n messages: Message[];\n lastActivity: Date;\n threadTs?: string; // Track thread timestamp for threaded conversations\n}\n\n/**\n * Extended message type for thread support\n */\ninterface ThreadedMessage extends GenericMessageEvent {\n thread_ts?: string;\n parent_user_id?: string;\n}\n\n/**\n * Slack bot class\n */\nexport class SlackBot {\n private app: InstanceType<typeof App>;\n private config: SlackBotConfig;\n private client: InstanceType<typeof WebClient>;\n private receiver: InstanceType<typeof ExpressReceiver> | undefined;\n private isRunning: boolean = false;\n\n constructor(config: SlackBotConfig) {\n this.config = {\n allowDMs: true,\n allowMentions: true,\n allowThreadReplies: true,\n ...config,\n };\n\n // Create receiver for HTTP mode\n if (!config.socketMode) {\n this.receiver = new ExpressReceiver({\n signingSecret: config.signingSecret,\n });\n }\n\n // Initialize Slack app\n this.app = new App({\n token: config.token,\n signingSecret: config.signingSecret,\n socketMode: config.socketMode,\n appToken: config.appToken,\n receiver: this.receiver,\n });\n\n this.client = new WebClient(config.token);\n\n this.setupEventHandlers();\n this.setupSlashCommands();\n }\n\n /**\n * Set up Slack event handlers\n */\n private setupEventHandlers(): void {\n // App mention handler (when someone @mentions the bot)\n if (this.config.allowMentions) {\n this.app.event(\"app_mention\", async (args: any) => {\n await this.handleAppMention(args);\n });\n }\n\n // Direct message handler\n if (this.config.allowDMs) {\n this.app.message(async (args: any) => {\n await this.handleMessage(args);\n });\n }\n\n // File shared event handler\n this.app.event(\"file_shared\", async (args: any) => {\n await this.handleFileShared(args);\n });\n\n // Error handler\n this.app.error(async (error: any) => {\n console.error(\"[Slack] App error:\", error);\n });\n }\n\n /**\n * Set up slash command handlers\n */\n private setupSlashCommands(): void {\n for (const cmd of slashCommands) {\n this.app.command(cmd.command, async (args: any) => {\n // Check authorization\n if (!this.isUserAuthorized(args.command.user_id, args.command.channel_id)) {\n await args.ack();\n await args.respond({\n response_type: \"ephemeral\",\n text: \"You are not authorized to use this bot.\",\n });\n return;\n }\n\n await cmd.handler(args);\n });\n }\n }\n\n /**\n * Handle app mentions\n */\n private async handleAppMention(\n args: SlackEventMiddlewareArgs<\"app_mention\"> & AllMiddlewareArgs\n ): Promise<void> {\n const { event, say, client } = args;\n\n // Check authorization\n if (!this.isUserAuthorized(event.user as string, event.channel as string)) {\n return;\n }\n\n // Remove the bot mention from the text\n const text = (event.text as string).replace(/<@[A-Z0-9]+>/gi, \"\").trim();\n if (!text) {\n await say({\n text: \"Hi! How can I help you? Mention me with a question or use `/sentinel help` for available commands.\",\n thread_ts: event.thread_ts || event.ts,\n });\n return;\n }\n\n const userId = event.user as string;\n const sessionKey = event.thread_ts\n ? `${userId}:thread:${event.thread_ts}`\n : userId;\n\n try {\n // Show typing indicator\n // Note: Slack doesn't have a native typing indicator API like Discord\n // We use a reaction to show we're processing\n await client.reactions.add({\n channel: event.channel,\n timestamp: event.ts,\n name: \"hourglass_flowing_sand\",\n });\n\n addToSession(sessionKey, { role: \"user\", content: text });\n\n const response = await chatWithTools(\n getSession(sessionKey),\n `slack:${userId}`\n );\n\n addToSession(sessionKey, { role: \"assistant\", content: response.content });\n\n // Build response with tool usage info\n let finalResponse = response.content;\n if (response.toolsUsed && response.toolsUsed.length > 0) {\n const toolList = [...new Set(response.toolsUsed)].join(\", \");\n finalResponse = `_Used: ${toolList}_\\n\\n${response.content}`;\n }\n\n // Remove processing indicator\n try {\n await client.reactions.remove({\n channel: event.channel,\n timestamp: event.ts,\n name: \"hourglass_flowing_sand\",\n });\n } catch {\n // Ignore if reaction was already removed\n }\n\n // Send response in thread\n if (finalResponse.length > 3000) {\n const chunks = splitMessage(finalResponse, 3000);\n for (const chunk of chunks) {\n await say({\n text: chunk,\n thread_ts: event.thread_ts || event.ts,\n mrkdwn: true,\n });\n }\n } else {\n await say({\n text: finalResponse,\n thread_ts: event.thread_ts || event.ts,\n mrkdwn: true,\n });\n }\n\n console.log(\n `[Slack] Processed mention from ${userId}. Tokens: ${response.inputTokens}/${response.outputTokens}` +\n (response.toolsUsed ? ` Tools: ${response.toolsUsed.join(\", \")}` : \"\")\n );\n } catch (error) {\n console.error(\"[Slack] Error processing mention:\", error);\n\n // Remove processing indicator on error\n try {\n await client.reactions.remove({\n channel: event.channel,\n timestamp: event.ts,\n name: \"hourglass_flowing_sand\",\n });\n await client.reactions.add({\n channel: event.channel,\n timestamp: event.ts,\n name: \"x\",\n });\n } catch {\n // Ignore reaction errors\n }\n\n await say({\n text: \"Sorry, I encountered an error processing your message. Please try again.\",\n thread_ts: event.thread_ts || event.ts,\n });\n }\n }\n\n /**\n * Handle direct messages\n */\n private async handleMessage(\n args: SlackEventMiddlewareArgs<\"message\"> & AllMiddlewareArgs\n ): Promise<void> {\n const { event, say, client, message } = args;\n\n // Skip bot messages and subtypes (edits, deletes, etc.)\n const genericMessage = message as GenericMessageEvent;\n if (genericMessage.subtype) return;\n if (\"bot_id\" in event) return;\n\n // Only handle DMs (im) and group DMs (mpim)\n const channelType = (event as any).channel_type;\n if (channelType !== \"im\" && channelType !== \"mpim\") return;\n\n const userId = (message as GenericMessageEvent).user;\n if (!userId) return;\n\n // Check authorization\n if (!this.isUserAuthorized(userId, event.channel as string)) {\n return;\n }\n\n const text = (message as GenericMessageEvent).text || \"\";\n if (!text.trim()) return;\n\n const threadedMessage = message as ThreadedMessage;\n const sessionKey = threadedMessage.thread_ts\n ? `${userId}:thread:${threadedMessage.thread_ts}`\n : userId;\n\n try {\n // Show processing reaction\n await client.reactions.add({\n channel: event.channel,\n timestamp: (message as GenericMessageEvent).ts,\n name: \"hourglass_flowing_sand\",\n });\n\n addToSession(sessionKey, { role: \"user\", content: text });\n\n const response = await chatWithTools(\n getSession(sessionKey),\n `slack:${userId}`\n );\n\n addToSession(sessionKey, { role: \"assistant\", content: response.content });\n\n let finalResponse = response.content;\n if (response.toolsUsed && response.toolsUsed.length > 0) {\n const toolList = [...new Set(response.toolsUsed)].join(\", \");\n finalResponse = `_Used: ${toolList}_\\n\\n${response.content}`;\n }\n\n // Remove processing indicator\n try {\n await client.reactions.remove({\n channel: event.channel,\n timestamp: (message as GenericMessageEvent).ts,\n name: \"hourglass_flowing_sand\",\n });\n } catch {\n // Ignore if reaction was already removed\n }\n\n // Send response (in thread if it's a threaded message)\n const replyTs =\n this.config.allowThreadReplies && threadedMessage.thread_ts\n ? threadedMessage.thread_ts\n : undefined;\n\n if (finalResponse.length > 3000) {\n const chunks = splitMessage(finalResponse, 3000);\n for (const chunk of chunks) {\n await say({\n text: chunk,\n thread_ts: replyTs,\n mrkdwn: true,\n });\n }\n } else {\n await say({\n text: finalResponse,\n thread_ts: replyTs,\n mrkdwn: true,\n });\n }\n\n console.log(\n `[Slack] Processed DM from ${userId}. Tokens: ${response.inputTokens}/${response.outputTokens}` +\n (response.toolsUsed ? ` Tools: ${response.toolsUsed.join(\", \")}` : \"\")\n );\n } catch (error) {\n console.error(\"[Slack] Error processing message:\", error);\n\n try {\n await client.reactions.remove({\n channel: event.channel,\n timestamp: (message as GenericMessageEvent).ts,\n name: \"hourglass_flowing_sand\",\n });\n await client.reactions.add({\n channel: event.channel,\n timestamp: (message as GenericMessageEvent).ts,\n name: \"x\",\n });\n } catch {\n // Ignore reaction errors\n }\n\n await say({\n text: \"Sorry, I encountered an error processing your message. Please try again.\",\n });\n }\n }\n\n /**\n * Handle file shared events\n */\n private async handleFileShared(\n args: SlackEventMiddlewareArgs<\"file_shared\"> & AllMiddlewareArgs\n ): Promise<void> {\n const { event, client } = args;\n\n try {\n // Get file info\n const fileInfo = await client.files.info({ file: event.file_id });\n const file = fileInfo.file;\n\n if (!file) return;\n\n // Check if it's an audio file\n const mimetype = file.mimetype || \"\";\n if (mimetype.startsWith(\"audio/\")) {\n // Download and transcribe\n if (file.url_private_download) {\n const response = await fetch(file.url_private_download, {\n headers: {\n Authorization: `Bearer ${this.config.token}`,\n },\n });\n\n if (response.ok) {\n const audioBuffer = await response.arrayBuffer();\n const transcription = await transcribeAudio(Buffer.from(audioBuffer));\n\n if (transcription) {\n // Post transcription as a reply\n const channelId = (file.channels && file.channels[0]) || (file.ims && file.ims[0]);\n if (channelId) {\n await client.chat.postMessage({\n channel: channelId,\n text: `Audio transcription: \"${transcription}\"`,\n mrkdwn: true,\n });\n\n console.log(\n `[Slack] Transcribed audio file: ${file.name}`\n );\n }\n }\n }\n }\n }\n } catch (error) {\n console.error(\"[Slack] Error processing file:\", error);\n }\n }\n\n /**\n * Check if a user is authorized to use the bot\n */\n private isUserAuthorized(userId: string, channelId: string): boolean {\n // If no restrictions, allow everyone\n if (\n !this.config.allowedUserIds?.length &&\n !this.config.allowedChannelIds?.length\n ) {\n return true;\n }\n\n // Check user ID allowlist\n if (this.config.allowedUserIds?.includes(userId)) {\n return true;\n }\n\n // Check channel ID allowlist\n if (this.config.allowedChannelIds?.includes(channelId)) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Start the Slack bot\n */\n async start(): Promise<void> {\n const port = this.config.port || 3000;\n\n console.log(\"[Slack] Starting bot...\");\n\n await this.app.start(port);\n this.isRunning = true;\n\n if (this.config.socketMode) {\n console.log(\"[Slack] Bot started in Socket Mode\");\n } else {\n console.log(`[Slack] Bot started on port ${port}`);\n }\n }\n\n /**\n * Stop the Slack bot\n */\n async stop(): Promise<void> {\n console.log(\"[Slack] Stopping bot...\");\n\n await this.app.stop();\n this.isRunning = false;\n\n console.log(\"[Slack] Bot stopped\");\n }\n\n /**\n * Get the Slack app instance\n */\n getApp(): InstanceType<typeof App> {\n return this.app;\n }\n\n /**\n * Get the Slack Web API client\n */\n getClient(): InstanceType<typeof WebClient> {\n return this.client;\n }\n\n /**\n * Check if bot is running\n */\n running(): boolean {\n return this.isRunning;\n }\n\n /**\n * Send a message to a channel\n */\n async sendMessage(\n channelId: string,\n text: string,\n options?: {\n threadTs?: string;\n mrkdwn?: boolean;\n blocks?: object[];\n }\n ): Promise<ChatPostMessageResponse> {\n return this.client.chat.postMessage({\n channel: channelId,\n text,\n thread_ts: options?.threadTs,\n mrkdwn: options?.mrkdwn ?? true,\n blocks: options?.blocks as any,\n });\n }\n\n /**\n * Send a message with blocks\n */\n async sendBlocks(\n channelId: string,\n blocks: object[],\n text?: string,\n threadTs?: string\n ): Promise<ChatPostMessageResponse> {\n return this.client.chat.postMessage({\n channel: channelId,\n text: text || \"Message\",\n blocks: blocks as any,\n thread_ts: threadTs,\n });\n }\n\n /**\n * Send a file to a channel\n */\n async sendFile(\n channelId: string,\n content: Buffer | string,\n filename: string,\n options?: {\n title?: string;\n initialComment?: string;\n threadTs?: string;\n }\n ): Promise<void> {\n await this.client.files.uploadV2({\n channel_id: channelId,\n file: content,\n filename,\n title: options?.title,\n initial_comment: options?.initialComment,\n thread_ts: options?.threadTs,\n } as any);\n }\n\n /**\n * Reply to a thread\n */\n async replyToThread(\n channelId: string,\n threadTs: string,\n text: string,\n mrkdwn?: boolean\n ): Promise<ChatPostMessageResponse> {\n return this.client.chat.postMessage({\n channel: channelId,\n text,\n thread_ts: threadTs,\n mrkdwn: mrkdwn ?? true,\n });\n }\n\n /**\n * Add a reaction to a message\n */\n async addReaction(\n channelId: string,\n timestamp: string,\n emoji: string\n ): Promise<void> {\n await this.client.reactions.add({\n channel: channelId,\n timestamp,\n name: emoji,\n });\n }\n\n /**\n * Remove a reaction from a message\n */\n async removeReaction(\n channelId: string,\n timestamp: string,\n emoji: string\n ): Promise<void> {\n await this.client.reactions.remove({\n channel: channelId,\n timestamp,\n name: emoji,\n });\n }\n\n /**\n * Get user info\n */\n async getUserInfo(userId: string): Promise<object | undefined> {\n const result = await this.client.users.info({ user: userId });\n return result.user;\n }\n\n /**\n * Get channel info\n */\n async getChannelInfo(channelId: string): Promise<object | undefined> {\n const result = await this.client.conversations.info({ channel: channelId });\n return result.channel;\n }\n\n /**\n * Get the Express receiver (for adding custom routes)\n */\n getReceiver(): InstanceType<typeof ExpressReceiver> | undefined {\n return this.receiver;\n }\n}\n\n/**\n * Create and configure Slack bot\n */\nexport function createSlackBot(config: SlackBotConfig): SlackBot {\n return new SlackBot(config);\n}\n\n/**\n * Export commands module\n */\nexport * from \"./commands\";\n\n/**\n * Default export\n */\nexport default {\n createSlackBot,\n SlackBot,\n slashCommands,\n};\n","import type {\n SlashCommand as BoltSlashCommand,\n AllMiddlewareArgs,\n SlackCommandMiddlewareArgs,\n} from \"@slack/bolt\";\nimport { chatWithTools, type Message } from \"../../core/brain\";\nimport { scheduleReminder } from \"../../core/scheduler\";\n\n/**\n * Slack slash command definition\n */\nexport interface SlackSlashCommand {\n command: string;\n description: string;\n usage: string;\n handler: (\n args: SlackCommandMiddlewareArgs & AllMiddlewareArgs\n ) => Promise<void>;\n}\n\n// Session storage for conversation history\nconst sessions = new Map<string, Message[]>();\nconst MAX_HISTORY = 20;\n\n/**\n * Get session for a user\n */\nexport function getSession(userId: string): Message[] {\n if (!sessions.has(userId)) {\n sessions.set(userId, []);\n }\n return sessions.get(userId)!;\n}\n\n/**\n * Add message to session\n */\nexport function addToSession(userId: string, message: Message): void {\n const session = getSession(userId);\n session.push(message);\n if (session.length > MAX_HISTORY) {\n sessions.set(userId, session.slice(-MAX_HISTORY));\n }\n}\n\n/**\n * Clear session for a user\n */\nexport function clearSession(userId: string): void {\n sessions.set(userId, []);\n}\n\n/**\n * Export sessions map for external use\n */\nexport { sessions };\n\n/**\n * /opensentinel ask - Ask OpenSentinel a question\n */\nexport const askCommand: SlackSlashCommand = {\n command: \"/opensentinel-ask\",\n description: \"Ask OpenSentinel a question\",\n usage: \"/opensentinel-ask <question>\",\n\n async handler({ command, ack, respond, client }) {\n await ack();\n\n const question = command.text.trim();\n const userId = command.user_id;\n\n if (!question) {\n await respond({\n response_type: \"ephemeral\",\n text: \"Please provide a question. Usage: `/opensentinel-ask <question>`\",\n });\n return;\n }\n\n try {\n addToSession(userId, { role: \"user\", content: question });\n\n const response = await chatWithTools(\n getSession(userId),\n `slack:${userId}`\n );\n\n addToSession(userId, { role: \"assistant\", content: response.content });\n\n // Build response with tool usage info\n let finalResponse = response.content;\n if (response.toolsUsed && response.toolsUsed.length > 0) {\n const toolList = [...new Set(response.toolsUsed)].join(\", \");\n finalResponse = `_Used: ${toolList}_\\n\\n${response.content}`;\n }\n\n await respond({\n response_type: \"in_channel\",\n text: finalResponse,\n mrkdwn: true,\n });\n\n console.log(\n `[Slack] Processed /opensentinel-ask from ${userId}. Tokens: ${response.inputTokens}/${response.outputTokens}`\n );\n } catch (error) {\n console.error(\"[Slack] Error processing /opensentinel-ask:\", error);\n await respond({\n response_type: \"ephemeral\",\n text: \"Sorry, I encountered an error processing your question. Please try again.\",\n });\n }\n },\n};\n\n/**\n * /sentinel chat - Continue a conversation\n */\nexport const chatCommand: SlackSlashCommand = {\n command: \"/opensentinel-chat\",\n description: \"Continue a conversation with OpenSentinel\",\n usage: \"/opensentinel-chat <message>\",\n\n async handler({ command, ack, respond }) {\n await ack();\n\n const message = command.text.trim();\n const userId = command.user_id;\n\n if (!message) {\n await respond({\n response_type: \"ephemeral\",\n text: \"Please provide a message. Usage: `/opensentinel-chat <message>`\",\n });\n return;\n }\n\n try {\n addToSession(userId, { role: \"user\", content: message });\n\n const response = await chatWithTools(\n getSession(userId),\n `slack:${userId}`\n );\n\n addToSession(userId, { role: \"assistant\", content: response.content });\n\n let finalResponse = response.content;\n if (response.toolsUsed && response.toolsUsed.length > 0) {\n const toolList = [...new Set(response.toolsUsed)].join(\", \");\n finalResponse = `_Used: ${toolList}_\\n\\n${response.content}`;\n }\n\n await respond({\n response_type: \"in_channel\",\n text: finalResponse,\n mrkdwn: true,\n });\n\n console.log(\n `[Slack] Processed /opensentinel-chat from ${userId}. Tokens: ${response.inputTokens}/${response.outputTokens}`\n );\n } catch (error) {\n console.error(\"[Slack] Error processing /opensentinel-chat:\", error);\n await respond({\n response_type: \"ephemeral\",\n text: \"Sorry, I encountered an error processing your message. Please try again.\",\n });\n }\n },\n};\n\n/**\n * /sentinel clear - Clear conversation history\n */\nexport const clearCommand: SlackSlashCommand = {\n command: \"/opensentinel-clear\",\n description: \"Clear your conversation history with OpenSentinel\",\n usage: \"/opensentinel-clear\",\n\n async handler({ command, ack, respond }) {\n await ack();\n\n clearSession(command.user_id);\n\n await respond({\n response_type: \"ephemeral\",\n text: \"Conversation history cleared.\",\n });\n\n console.log(`[Slack] Cleared history for ${command.user_id}`);\n },\n};\n\n/**\n * /sentinel remind - Set a reminder\n */\nexport const remindCommand: SlackSlashCommand = {\n command: \"/opensentinel-remind\",\n description: \"Set a reminder\",\n usage: \"/opensentinel-remind <time><s/m/h> <message>\",\n\n async handler({ command, ack, respond }) {\n await ack();\n\n const text = command.text.trim();\n const match = text.match(/^(\\d+)(s|m|h)\\s+(.+)$/i);\n\n if (!match) {\n await respond({\n response_type: \"ephemeral\",\n text:\n \"Invalid format. Use: `/opensentinel-remind <number><s/m/h> <message>`\\n\\n\" +\n \"Examples:\\n\" +\n \"• `/opensentinel-remind 5m Check the oven`\\n\" +\n \"• `/opensentinel-remind 1h Call mom`\\n\" +\n \"• `/opensentinel-remind 30s Test reminder`\",\n });\n return;\n }\n\n const [, amount, unit, message] = match;\n const multipliers: Record<string, number> = {\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n };\n const delayMs = parseInt(amount) * multipliers[unit.toLowerCase()];\n\n try {\n await scheduleReminder(\n message,\n delayMs,\n `slack:${command.channel_id}:${command.user_id}`\n );\n\n const timeStr =\n unit === \"s\" ? \"seconds\" : unit === \"m\" ? \"minutes\" : \"hours\";\n\n await respond({\n response_type: \"ephemeral\",\n text: `Reminder set for ${amount} ${timeStr}: \"${message}\"`,\n });\n\n console.log(\n `[Slack] Reminder set by ${command.user_id}: ${amount}${unit} - \"${message}\"`\n );\n } catch (error) {\n console.error(\"[Slack] Error setting reminder:\", error);\n await respond({\n response_type: \"ephemeral\",\n text: \"Sorry, I couldn't set the reminder. Please try again.\",\n });\n }\n },\n};\n\n/**\n * /opensentinel status - Check OpenSentinel status\n */\nexport const statusCommand: SlackSlashCommand = {\n command: \"/opensentinel-status\",\n description: \"Check OpenSentinel status and capabilities\",\n usage: \"/opensentinel-status\",\n\n async handler({ command, ack, respond }) {\n await ack();\n\n const session = getSession(command.user_id);\n const historyCount = session.length;\n\n await respond({\n response_type: \"ephemeral\",\n text:\n \"*OpenSentinel Status*\\n\\n\" +\n `Bot: Online\\n` +\n `Your conversation history: ${historyCount} messages\\n\\n` +\n \"*Capabilities:*\\n\" +\n \"• Chat and answer questions using Claude AI\\n\" +\n \"• Execute shell commands\\n\" +\n \"• Read and write files\\n\" +\n \"• Search the web\\n\" +\n \"• Remember important information\\n\" +\n \"• Set reminders\\n\\n\" +\n \"Use `/opensentinel-help` for available commands.\",\n });\n },\n};\n\n/**\n * /opensentinel help - Show available commands\n */\nexport const helpCommand: SlackSlashCommand = {\n command: \"/opensentinel-help\",\n description: \"Show available OpenSentinel commands\",\n usage: \"/opensentinel-help\",\n\n async handler({ ack, respond }) {\n await ack();\n\n await respond({\n response_type: \"ephemeral\",\n text:\n \"*OpenSentinel Commands*\\n\\n\" +\n \"`/opensentinel-ask <question>` - Ask a single question\\n\" +\n \"`/opensentinel-chat <message>` - Continue a conversation\\n\" +\n \"`/opensentinel-clear` - Clear your conversation history\\n\" +\n \"`/opensentinel-remind <time> <message>` - Set a reminder\\n\" +\n \"`/opensentinel-status` - Check bot status\\n\" +\n \"`/opensentinel-help` - Show this help message\\n\\n\" +\n \"*Tips:*\\n\" +\n \"• Use `/opensentinel-chat` for multi-turn conversations with context\\n\" +\n \"• Use `/opensentinel-ask` for quick one-off questions\\n\" +\n \"• Mention @OpenSentinel in any channel to chat directly\\n\" +\n \"• DM the bot for private conversations\",\n });\n },\n};\n\n/**\n * /opensentinel - Main command with subcommands\n */\nexport const mainCommand: SlackSlashCommand = {\n command: \"/opensentinel\",\n description: \"OpenSentinel AI assistant\",\n usage: \"/opensentinel <ask|chat|clear|remind|status|help> [args]\",\n\n async handler({ command, ack, respond }) {\n await ack();\n\n const [subcommand, ...args] = command.text.trim().split(/\\s+/);\n const argsText = args.join(\" \");\n\n // Route to appropriate handler\n switch (subcommand?.toLowerCase()) {\n case \"ask\":\n if (!argsText) {\n await respond({\n response_type: \"ephemeral\",\n text: \"Please provide a question. Usage: `/opensentinel ask <question>`\",\n });\n return;\n }\n command.text = argsText;\n await askCommand.handler({ command, ack, respond } as any);\n break;\n\n case \"chat\":\n if (!argsText) {\n await respond({\n response_type: \"ephemeral\",\n text: \"Please provide a message. Usage: `/opensentinel chat <message>`\",\n });\n return;\n }\n command.text = argsText;\n await chatCommand.handler({ command, ack, respond } as any);\n break;\n\n case \"clear\":\n await clearCommand.handler({ command, ack, respond } as any);\n break;\n\n case \"remind\":\n command.text = argsText;\n await remindCommand.handler({ command, ack, respond } as any);\n break;\n\n case \"status\":\n await statusCommand.handler({ command, ack, respond } as any);\n break;\n\n case \"help\":\n default:\n await respond({\n response_type: \"ephemeral\",\n text:\n \"*OpenSentinel Commands*\\n\\n\" +\n \"`/opensentinel ask <question>` - Ask a single question\\n\" +\n \"`/opensentinel chat <message>` - Continue a conversation\\n\" +\n \"`/opensentinel clear` - Clear your conversation history\\n\" +\n \"`/opensentinel remind <time> <message>` - Set a reminder\\n\" +\n \"`/opensentinel status` - Check bot status\\n\" +\n \"`/opensentinel help` - Show this help message\\n\\n\" +\n \"*Or use individual commands:*\\n\" +\n \"`/opensentinel-ask`, `/opensentinel-chat`, `/opensentinel-clear`, `/opensentinel-remind`, `/opensentinel-status`, `/opensentinel-help`\",\n });\n break;\n }\n },\n};\n\n/**\n * All available slash commands\n */\nexport const slashCommands: SlackSlashCommand[] = [\n mainCommand,\n askCommand,\n chatCommand,\n clearCommand,\n remindCommand,\n statusCommand,\n helpCommand,\n];\n\n/**\n * Get command by name\n */\nexport function getCommand(name: string): SlackSlashCommand | undefined {\n return slashCommands.find((cmd) => cmd.command === name);\n}\n\n/**\n * Get all command names\n */\nexport function getCommandNames(): string[] {\n return slashCommands.map((cmd) => cmd.command);\n}\n\n/**\n * Helper function to split long messages for Slack\n */\nexport function splitMessage(text: string, maxLength: number = 3000): string[] {\n const chunks: string[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n\n // Find a good break point (newline or space)\n let breakPoint = remaining.lastIndexOf(\"\\n\", maxLength);\n if (breakPoint === -1 || breakPoint < maxLength / 2) {\n breakPoint = remaining.lastIndexOf(\" \", maxLength);\n }\n if (breakPoint === -1 || breakPoint < maxLength / 2) {\n breakPoint = maxLength;\n }\n\n chunks.push(remaining.slice(0, breakPoint));\n remaining = remaining.slice(breakPoint).trim();\n }\n\n return chunks;\n}\n\n/**\n * Format a message as a Slack Block Kit message\n */\nexport function formatAsBlocks(text: string): object[] {\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text,\n },\n },\n ];\n}\n\n/**\n * Create an error block message\n */\nexport function createErrorBlocks(message: string): object[] {\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `:warning: *Error*\\n${message}`,\n },\n },\n ];\n}\n\n/**\n * Create a success block message\n */\nexport function createSuccessBlocks(message: string): object[] {\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `:white_check_mark: ${message}`,\n },\n },\n ];\n}\n"],"mappings":"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","names":["require"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
__require,
|
|
15
|
+
__export
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=chunk-NSBPE2FW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
var command = process.argv[2];
|
|
5
|
+
switch (command) {
|
|
6
|
+
case "setup":
|
|
7
|
+
import("./commands/setup.js").then((m) => m.default());
|
|
8
|
+
break;
|
|
9
|
+
case "start":
|
|
10
|
+
import("./commands/start.js").then((m) => m.default());
|
|
11
|
+
break;
|
|
12
|
+
case "stop":
|
|
13
|
+
import("./commands/stop.js").then((m) => m.default());
|
|
14
|
+
break;
|
|
15
|
+
case "status":
|
|
16
|
+
import("./commands/status.js").then((m) => m.default());
|
|
17
|
+
break;
|
|
18
|
+
case "version":
|
|
19
|
+
case "--version":
|
|
20
|
+
case "-v":
|
|
21
|
+
console.log("opensentinel v2.0.0");
|
|
22
|
+
break;
|
|
23
|
+
case "help":
|
|
24
|
+
case "--help":
|
|
25
|
+
case "-h":
|
|
26
|
+
console.log(`
|
|
27
|
+
OpenSentinel - Your Personal AI Assistant
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
opensentinel [command]
|
|
31
|
+
|
|
32
|
+
Commands:
|
|
33
|
+
start Start the server (default)
|
|
34
|
+
setup Interactive setup wizard
|
|
35
|
+
stop Stop the system service
|
|
36
|
+
status Show service status
|
|
37
|
+
version Show version
|
|
38
|
+
help Show this help
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
opensentinel Start with default config
|
|
42
|
+
opensentinel setup Run the setup wizard
|
|
43
|
+
opensentinel status Check service health
|
|
44
|
+
|
|
45
|
+
Documentation: https://docs.opensentinel.ai
|
|
46
|
+
`);
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
import("./commands/start.js").then((m) => m.default());
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env bun\n\n/**\n * OpenSentinel CLI\n *\n * Usage:\n * opensentinel Start the server (default)\n * opensentinel start Start the server\n * opensentinel setup Interactive setup wizard\n * opensentinel stop Stop the system service\n * opensentinel status Show service status\n * opensentinel version Show version\n * opensentinel help Show this help\n */\n\nconst command = process.argv[2];\n\nswitch (command) {\n case \"setup\":\n import(\"./commands/setup\").then((m) => m.default());\n break;\n\n case \"start\":\n import(\"./commands/start\").then((m) => m.default());\n break;\n\n case \"stop\":\n import(\"./commands/stop\").then((m) => m.default());\n break;\n\n case \"status\":\n import(\"./commands/status\").then((m) => m.default());\n break;\n\n case \"version\":\n case \"--version\":\n case \"-v\":\n console.log(\"opensentinel v2.0.0\");\n break;\n\n case \"help\":\n case \"--help\":\n case \"-h\":\n console.log(`\nOpenSentinel - Your Personal AI Assistant\n\nUsage:\n opensentinel [command]\n\nCommands:\n start Start the server (default)\n setup Interactive setup wizard\n stop Stop the system service\n status Show service status\n version Show version\n help Show this help\n\nExamples:\n opensentinel Start with default config\n opensentinel setup Run the setup wizard\n opensentinel status Check service health\n\nDocumentation: https://docs.opensentinel.ai\n`);\n break;\n\n default:\n // Default: start the server (backward compatibility)\n import(\"./commands/start\").then((m) => m.default());\n break;\n}\n"],"mappings":";;;AAeA,IAAM,UAAU,QAAQ,KAAK,CAAC;AAE9B,QAAQ,SAAS;AAAA,EACf,KAAK;AACH,WAAO,qBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;AAClD;AAAA,EAEF,KAAK;AACH,WAAO,qBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;AAClD;AAAA,EAEF,KAAK;AACH,WAAO,oBAAiB,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;AACjD;AAAA,EAEF,KAAK;AACH,WAAO,sBAAmB,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;AACnD;AAAA,EAEF,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACH,YAAQ,IAAI,qBAAqB;AACjC;AAAA,EAEF,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACH,YAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAoBf;AACG;AAAA,EAEF;AAEE,WAAO,qBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;AAClD;AACJ;","names":[]}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkPostgres,
|
|
3
|
+
checkRedis,
|
|
4
|
+
colors,
|
|
5
|
+
confirm,
|
|
6
|
+
detectPlatform,
|
|
7
|
+
exec,
|
|
8
|
+
getConfigDir,
|
|
9
|
+
getMigrationsDir,
|
|
10
|
+
getPackageRoot,
|
|
11
|
+
printBanner,
|
|
12
|
+
prompt,
|
|
13
|
+
which
|
|
14
|
+
} from "../chunk-GUBEEYDW.js";
|
|
15
|
+
import "../chunk-NSBPE2FW.js";
|
|
16
|
+
|
|
17
|
+
// src/commands/setup.ts
|
|
18
|
+
import { writeFileSync, chmodSync, existsSync } from "fs";
|
|
19
|
+
import { join } from "path";
|
|
20
|
+
import { homedir } from "os";
|
|
21
|
+
async function setup() {
|
|
22
|
+
printBanner();
|
|
23
|
+
console.log(`${colors.bold}Setup Wizard${colors.reset}
|
|
24
|
+
`);
|
|
25
|
+
const platform = detectPlatform();
|
|
26
|
+
if (platform.os === "other") {
|
|
27
|
+
console.log(`${colors.yellow}Warning: Only Linux and macOS are fully supported.`);
|
|
28
|
+
console.log(`On Windows, use WSL or Docker.${colors.reset}
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
const configDir = getConfigDir();
|
|
32
|
+
console.log(`${colors.dim}Config directory: ${configDir}${colors.reset}
|
|
33
|
+
`);
|
|
34
|
+
console.log(`${colors.cyan}${colors.bold}[1/5] API Keys${colors.reset}
|
|
35
|
+
`);
|
|
36
|
+
let claudeKey = process.env.CLAUDE_API_KEY || "";
|
|
37
|
+
if (!claudeKey) {
|
|
38
|
+
claudeKey = await prompt(` Claude API Key ${colors.red}(required)${colors.reset}: `);
|
|
39
|
+
if (!claudeKey) {
|
|
40
|
+
console.error(`
|
|
41
|
+
${colors.red}Claude API Key is required. Get one at https://console.anthropic.com${colors.reset}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
console.log(` Claude API Key: ${colors.green}configured${colors.reset} (***${claudeKey.slice(-4)})`);
|
|
46
|
+
}
|
|
47
|
+
const telegramToken = process.env.TELEGRAM_BOT_TOKEN || await prompt(` Telegram Bot Token ${colors.dim}(optional, Enter to skip)${colors.reset}: `);
|
|
48
|
+
const telegramChatId = telegramToken ? process.env.TELEGRAM_CHAT_ID || await prompt(` Telegram Chat ID: `) : "";
|
|
49
|
+
const discordToken = process.env.DISCORD_BOT_TOKEN || await prompt(` Discord Bot Token ${colors.dim}(optional, Enter to skip)${colors.reset}: `);
|
|
50
|
+
const discordClientId = discordToken ? process.env.DISCORD_CLIENT_ID || await prompt(` Discord Client ID: `) : "";
|
|
51
|
+
const discordGuildId = discordToken ? process.env.DISCORD_GUILD_ID || await prompt(` Discord Guild ID: `) : "";
|
|
52
|
+
const discordAllowedUsers = discordToken ? process.env.DISCORD_ALLOWED_USER_IDS || await prompt(` Discord Allowed User IDs ${colors.dim}(comma-separated)${colors.reset}: `) : "";
|
|
53
|
+
const openaiKey = process.env.OPENAI_API_KEY || await prompt(` OpenAI API Key ${colors.dim}(optional, for voice/images)${colors.reset}: `);
|
|
54
|
+
const elevenlabsKey = process.env.ELEVENLABS_API_KEY || await prompt(` ElevenLabs API Key ${colors.dim}(optional, for TTS)${colors.reset}: `);
|
|
55
|
+
const elevenlabsVoice = elevenlabsKey ? process.env.ELEVENLABS_VOICE_ID || await prompt(` ElevenLabs Voice ID: `) : "";
|
|
56
|
+
console.log(`
|
|
57
|
+
${colors.green} API keys collected.${colors.reset}
|
|
58
|
+
`);
|
|
59
|
+
console.log(`${colors.cyan}${colors.bold}[2/5] Infrastructure${colors.reset}
|
|
60
|
+
`);
|
|
61
|
+
const { databaseUrl } = await setupPostgres(platform);
|
|
62
|
+
const { redisUrl } = await setupRedis(platform);
|
|
63
|
+
console.log(`
|
|
64
|
+
${colors.cyan}${colors.bold}[3/5] Database${colors.reset}
|
|
65
|
+
`);
|
|
66
|
+
await runMigrations(databaseUrl);
|
|
67
|
+
console.log(`
|
|
68
|
+
${colors.cyan}${colors.bold}[4/5] Configuration${colors.reset}
|
|
69
|
+
`);
|
|
70
|
+
const port = process.env.PORT || "8030";
|
|
71
|
+
const envContent = `# OpenSentinel Configuration
|
|
72
|
+
# Generated by opensentinel setup on ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
73
|
+
|
|
74
|
+
# Core AI
|
|
75
|
+
CLAUDE_API_KEY=${claudeKey}
|
|
76
|
+
${openaiKey ? `OPENAI_API_KEY=${openaiKey}` : "# OPENAI_API_KEY="}
|
|
77
|
+
${elevenlabsKey ? `ELEVENLABS_API_KEY=${elevenlabsKey}` : "# ELEVENLABS_API_KEY="}
|
|
78
|
+
${elevenlabsVoice ? `ELEVENLABS_VOICE_ID=${elevenlabsVoice}` : "# ELEVENLABS_VOICE_ID="}
|
|
79
|
+
|
|
80
|
+
# Telegram
|
|
81
|
+
${telegramToken ? `TELEGRAM_BOT_TOKEN=${telegramToken}` : "# TELEGRAM_BOT_TOKEN="}
|
|
82
|
+
${telegramChatId ? `TELEGRAM_CHAT_ID=${telegramChatId}` : "# TELEGRAM_CHAT_ID="}
|
|
83
|
+
|
|
84
|
+
# Discord
|
|
85
|
+
${discordToken ? `DISCORD_BOT_TOKEN=${discordToken}` : "# DISCORD_BOT_TOKEN="}
|
|
86
|
+
${discordClientId ? `DISCORD_CLIENT_ID=${discordClientId}` : "# DISCORD_CLIENT_ID="}
|
|
87
|
+
${discordGuildId ? `DISCORD_GUILD_ID=${discordGuildId}` : "# DISCORD_GUILD_ID="}
|
|
88
|
+
${discordAllowedUsers ? `DISCORD_ALLOWED_USER_IDS=${discordAllowedUsers}` : "# DISCORD_ALLOWED_USER_IDS="}
|
|
89
|
+
|
|
90
|
+
# Database & Cache
|
|
91
|
+
DATABASE_URL=${databaseUrl}
|
|
92
|
+
REDIS_URL=${redisUrl}
|
|
93
|
+
|
|
94
|
+
# Server
|
|
95
|
+
PORT=${port}
|
|
96
|
+
NODE_ENV=production
|
|
97
|
+
`;
|
|
98
|
+
const envPath = join(configDir, ".env");
|
|
99
|
+
writeFileSync(envPath, envContent);
|
|
100
|
+
chmodSync(envPath, 384);
|
|
101
|
+
console.log(` Config written to ${colors.green}${envPath}${colors.reset}`);
|
|
102
|
+
console.log(`
|
|
103
|
+
${colors.cyan}${colors.bold}[5/5] System Service${colors.reset}
|
|
104
|
+
`);
|
|
105
|
+
await setupDaemon(platform, configDir);
|
|
106
|
+
console.log(`
|
|
107
|
+
${colors.green}${colors.bold}Setup complete!${colors.reset}
|
|
108
|
+
|
|
109
|
+
${colors.bold}Quick reference:${colors.reset}
|
|
110
|
+
opensentinel start Start the server
|
|
111
|
+
opensentinel stop Stop the service
|
|
112
|
+
opensentinel status Check health
|
|
113
|
+
opensentinel help Show all commands
|
|
114
|
+
|
|
115
|
+
${colors.bold}Config:${colors.reset} ${envPath}
|
|
116
|
+
${colors.bold}Docs:${colors.reset} https://docs.opensentinel.ai
|
|
117
|
+
`);
|
|
118
|
+
}
|
|
119
|
+
async function setupPostgres(platform) {
|
|
120
|
+
const pg = await checkPostgres();
|
|
121
|
+
if (pg.running) {
|
|
122
|
+
console.log(` PostgreSQL: ${colors.green}running${colors.reset} (port ${pg.port})`);
|
|
123
|
+
const useExisting = await confirm(" Use existing PostgreSQL?");
|
|
124
|
+
if (useExisting) {
|
|
125
|
+
return await configureExistingPostgres(pg.port);
|
|
126
|
+
}
|
|
127
|
+
} else if (pg.installed) {
|
|
128
|
+
console.log(` PostgreSQL: ${colors.yellow}installed but not running${colors.reset}`);
|
|
129
|
+
const startIt = await confirm(" Start PostgreSQL?");
|
|
130
|
+
if (startIt) {
|
|
131
|
+
await startPostgres(platform);
|
|
132
|
+
return await configureExistingPostgres(5432);
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
console.log(` PostgreSQL: ${colors.red}not found${colors.reset}`);
|
|
136
|
+
}
|
|
137
|
+
const install = await confirm(" Install PostgreSQL 16 + pgvector?");
|
|
138
|
+
if (!install) {
|
|
139
|
+
console.log(`
|
|
140
|
+
${colors.yellow} Skipping PostgreSQL. The app will start but memory and history won't persist.${colors.reset}`);
|
|
141
|
+
return { databaseUrl: "postgresql://localhost:5432/opensentinel" };
|
|
142
|
+
}
|
|
143
|
+
return await installPostgres(platform);
|
|
144
|
+
}
|
|
145
|
+
async function configureExistingPostgres(port) {
|
|
146
|
+
const dbUser = "opensentinel";
|
|
147
|
+
const dbPass = "opensentinel";
|
|
148
|
+
const dbName = "opensentinel";
|
|
149
|
+
console.log(` Creating database '${dbName}'...`);
|
|
150
|
+
try {
|
|
151
|
+
await exec(`sudo -u postgres psql -c "SELECT 1 FROM pg_roles WHERE rolname='${dbUser}'" 2>/dev/null | grep -q 1 || sudo -u postgres psql -c "CREATE USER ${dbUser} WITH PASSWORD '${dbPass}'"`, { throws: false });
|
|
152
|
+
await exec(`sudo -u postgres psql -c "SELECT 1 FROM pg_database WHERE datname='${dbName}'" 2>/dev/null | grep -q 1 || sudo -u postgres psql -c "CREATE DATABASE ${dbName} OWNER ${dbUser}"`, { throws: false });
|
|
153
|
+
await exec(`sudo -u postgres psql -d ${dbName} -c "CREATE EXTENSION IF NOT EXISTS vector" 2>/dev/null`, { throws: false });
|
|
154
|
+
console.log(` ${colors.green}Database ready.${colors.reset}`);
|
|
155
|
+
} catch {
|
|
156
|
+
try {
|
|
157
|
+
await exec(`psql -c "SELECT 1 FROM pg_roles WHERE rolname='${dbUser}'" postgres 2>/dev/null | grep -q 1 || createuser ${dbUser} 2>/dev/null`, { throws: false });
|
|
158
|
+
await exec(`psql -c "SELECT 1 FROM pg_database WHERE datname='${dbName}'" postgres 2>/dev/null | grep -q 1 || createdb -O ${dbUser} ${dbName} 2>/dev/null`, { throws: false });
|
|
159
|
+
await exec(`psql -d ${dbName} -c "CREATE EXTENSION IF NOT EXISTS vector" 2>/dev/null`, { throws: false });
|
|
160
|
+
console.log(` ${colors.green}Database ready.${colors.reset}`);
|
|
161
|
+
} catch {
|
|
162
|
+
console.log(` ${colors.yellow}Could not auto-configure database. You may need to create it manually.${colors.reset}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { databaseUrl: `postgresql://${dbUser}:${dbPass}@localhost:${port}/${dbName}` };
|
|
166
|
+
}
|
|
167
|
+
async function startPostgres(platform) {
|
|
168
|
+
if (platform.os === "linux") {
|
|
169
|
+
await exec("sudo systemctl start postgresql", { throws: false });
|
|
170
|
+
} else if (platform.os === "darwin") {
|
|
171
|
+
await exec("brew services start postgresql@16", { throws: false });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function installPostgres(platform) {
|
|
175
|
+
console.log(`
|
|
176
|
+
Installing PostgreSQL 16 + pgvector...`);
|
|
177
|
+
if (platform.packageManager === "apt") {
|
|
178
|
+
const hasRepo = await exec("apt-cache show postgresql-16 2>/dev/null", { throws: false });
|
|
179
|
+
if (hasRepo.exitCode !== 0) {
|
|
180
|
+
console.log(` Adding PostgreSQL APT repository...`);
|
|
181
|
+
await exec(`sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'`);
|
|
182
|
+
await exec("curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg");
|
|
183
|
+
}
|
|
184
|
+
await exec("sudo apt-get update -qq");
|
|
185
|
+
await exec("sudo apt-get install -y postgresql-16 postgresql-16-pgvector");
|
|
186
|
+
await exec("sudo systemctl enable postgresql");
|
|
187
|
+
await exec("sudo systemctl start postgresql");
|
|
188
|
+
} else if (platform.packageManager === "brew") {
|
|
189
|
+
await exec("brew install postgresql@16 pgvector");
|
|
190
|
+
await exec("brew services start postgresql@16");
|
|
191
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
192
|
+
} else if (platform.packageManager === "dnf") {
|
|
193
|
+
await exec("sudo dnf install -y postgresql16-server postgresql16-pgvector");
|
|
194
|
+
await exec("sudo postgresql-setup --initdb 2>/dev/null || true");
|
|
195
|
+
await exec("sudo systemctl enable postgresql");
|
|
196
|
+
await exec("sudo systemctl start postgresql");
|
|
197
|
+
} else {
|
|
198
|
+
console.log(` ${colors.yellow}Unsupported package manager. Please install PostgreSQL 16 + pgvector manually.${colors.reset}`);
|
|
199
|
+
return { databaseUrl: "postgresql://opensentinel:opensentinel@localhost:5432/opensentinel" };
|
|
200
|
+
}
|
|
201
|
+
console.log(` ${colors.green}PostgreSQL installed.${colors.reset}`);
|
|
202
|
+
return await configureExistingPostgres(5432);
|
|
203
|
+
}
|
|
204
|
+
async function setupRedis(platform) {
|
|
205
|
+
const redis = await checkRedis();
|
|
206
|
+
if (redis.running) {
|
|
207
|
+
console.log(` Redis: ${colors.green}running${colors.reset} (port ${redis.port})`);
|
|
208
|
+
return { redisUrl: `redis://localhost:${redis.port}` };
|
|
209
|
+
} else if (redis.installed) {
|
|
210
|
+
console.log(` Redis: ${colors.yellow}installed but not running${colors.reset}`);
|
|
211
|
+
const startIt = await confirm(" Start Redis?");
|
|
212
|
+
if (startIt) {
|
|
213
|
+
await startRedis(platform);
|
|
214
|
+
return { redisUrl: "redis://localhost:6379" };
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
console.log(` Redis: ${colors.red}not found${colors.reset}`);
|
|
218
|
+
}
|
|
219
|
+
const install = await confirm(" Install Redis?");
|
|
220
|
+
if (!install) {
|
|
221
|
+
console.log(`
|
|
222
|
+
${colors.yellow} Skipping Redis. Scheduled tasks and job queues will be unavailable.${colors.reset}`);
|
|
223
|
+
return { redisUrl: "redis://localhost:6379" };
|
|
224
|
+
}
|
|
225
|
+
return await installRedis(platform);
|
|
226
|
+
}
|
|
227
|
+
async function startRedis(platform) {
|
|
228
|
+
if (platform.os === "linux") {
|
|
229
|
+
await exec("sudo systemctl start redis-server 2>/dev/null || sudo systemctl start redis 2>/dev/null", { throws: false });
|
|
230
|
+
} else if (platform.os === "darwin") {
|
|
231
|
+
await exec("brew services start redis", { throws: false });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function installRedis(platform) {
|
|
235
|
+
console.log(`
|
|
236
|
+
Installing Redis...`);
|
|
237
|
+
if (platform.packageManager === "apt") {
|
|
238
|
+
await exec("sudo apt-get install -y redis-server");
|
|
239
|
+
await exec("sudo systemctl enable redis-server");
|
|
240
|
+
await exec("sudo systemctl start redis-server");
|
|
241
|
+
} else if (platform.packageManager === "brew") {
|
|
242
|
+
await exec("brew install redis");
|
|
243
|
+
await exec("brew services start redis");
|
|
244
|
+
} else if (platform.packageManager === "dnf") {
|
|
245
|
+
await exec("sudo dnf install -y redis");
|
|
246
|
+
await exec("sudo systemctl enable redis");
|
|
247
|
+
await exec("sudo systemctl start redis");
|
|
248
|
+
} else {
|
|
249
|
+
console.log(` ${colors.yellow}Unsupported package manager. Please install Redis manually.${colors.reset}`);
|
|
250
|
+
return { redisUrl: "redis://localhost:6379" };
|
|
251
|
+
}
|
|
252
|
+
await exec("redis-cli CONFIG SET maxmemory-policy noeviction 2>/dev/null", { throws: false });
|
|
253
|
+
console.log(` ${colors.green}Redis installed.${colors.reset}`);
|
|
254
|
+
return { redisUrl: "redis://localhost:6379" };
|
|
255
|
+
}
|
|
256
|
+
async function runMigrations(databaseUrl) {
|
|
257
|
+
console.log(` Running database migrations...`);
|
|
258
|
+
try {
|
|
259
|
+
const migrationsDir = getMigrationsDir();
|
|
260
|
+
if (!existsSync(migrationsDir)) {
|
|
261
|
+
console.log(` ${colors.yellow}Migrations folder not found at ${migrationsDir}. Skipping.${colors.reset}`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const { default: postgres } = await import("postgres");
|
|
265
|
+
const { drizzle } = await import("drizzle-orm/postgres-js");
|
|
266
|
+
const { migrate } = await import("drizzle-orm/postgres-js/migrator");
|
|
267
|
+
const client = postgres(databaseUrl, { max: 1 });
|
|
268
|
+
const db = drizzle(client);
|
|
269
|
+
await client`CREATE EXTENSION IF NOT EXISTS vector`;
|
|
270
|
+
await migrate(db, { migrationsFolder: migrationsDir });
|
|
271
|
+
await client.end();
|
|
272
|
+
console.log(` ${colors.green}Migrations complete.${colors.reset}`);
|
|
273
|
+
} catch (err) {
|
|
274
|
+
console.log(` ${colors.yellow}Migration warning: ${err.message}${colors.reset}`);
|
|
275
|
+
console.log(` ${colors.dim}You can run migrations later with: opensentinel start${colors.reset}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
async function setupDaemon(platform, configDir) {
|
|
279
|
+
const installDaemon = await confirm(" Install as system service (auto-start on boot)?");
|
|
280
|
+
if (!installDaemon) {
|
|
281
|
+
console.log(` ${colors.dim}Skipped. Run manually with: opensentinel start${colors.reset}`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (platform.os === "linux") {
|
|
285
|
+
await setupSystemd(configDir);
|
|
286
|
+
} else if (platform.os === "darwin") {
|
|
287
|
+
await setupLaunchd(configDir);
|
|
288
|
+
} else {
|
|
289
|
+
console.log(` ${colors.yellow}Daemon setup not supported on this platform. Run manually with: opensentinel start${colors.reset}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
async function setupSystemd(configDir) {
|
|
293
|
+
const bunPath = await which("bun") || "/usr/local/bin/bun";
|
|
294
|
+
const opensentinelPath = await which("opensentinel") || `${bunPath} ${join(getPackageRoot(), "dist", "cli.js")}`;
|
|
295
|
+
const user = process.env.USER || "root";
|
|
296
|
+
const unit = `[Unit]
|
|
297
|
+
Description=OpenSentinel AI Assistant
|
|
298
|
+
After=network.target postgresql.service redis-server.service
|
|
299
|
+
Wants=postgresql.service redis-server.service
|
|
300
|
+
|
|
301
|
+
[Service]
|
|
302
|
+
Type=simple
|
|
303
|
+
User=${user}
|
|
304
|
+
WorkingDirectory=${configDir}
|
|
305
|
+
EnvironmentFile=${configDir}/.env
|
|
306
|
+
ExecStart=${opensentinelPath} start
|
|
307
|
+
Restart=on-failure
|
|
308
|
+
RestartSec=5
|
|
309
|
+
StandardOutput=journal
|
|
310
|
+
StandardError=journal
|
|
311
|
+
SyslogIdentifier=opensentinel
|
|
312
|
+
|
|
313
|
+
[Install]
|
|
314
|
+
WantedBy=multi-user.target
|
|
315
|
+
`;
|
|
316
|
+
const unitPath = "/etc/systemd/system/opensentinel.service";
|
|
317
|
+
writeFileSync("/tmp/opensentinel.service", unit);
|
|
318
|
+
await exec(`sudo cp /tmp/opensentinel.service ${unitPath}`);
|
|
319
|
+
await exec("sudo systemctl daemon-reload");
|
|
320
|
+
await exec("sudo systemctl enable opensentinel");
|
|
321
|
+
console.log(` ${colors.green}Systemd service installed.${colors.reset}`);
|
|
322
|
+
console.log(` ${colors.dim}Unit file: ${unitPath}${colors.reset}`);
|
|
323
|
+
const startNow = await confirm(" Start OpenSentinel now?");
|
|
324
|
+
if (startNow) {
|
|
325
|
+
await exec("sudo systemctl start opensentinel");
|
|
326
|
+
console.log(` ${colors.green}Service started!${colors.reset}`);
|
|
327
|
+
console.log(` ${colors.dim}Logs: journalctl -u opensentinel -f${colors.reset}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function setupLaunchd(configDir) {
|
|
331
|
+
const opensentinelPath = await which("opensentinel") || join(getPackageRoot(), "dist", "cli.js");
|
|
332
|
+
const logsDir = join(configDir, "logs");
|
|
333
|
+
if (!existsSync(logsDir)) {
|
|
334
|
+
const { mkdirSync } = await import("fs");
|
|
335
|
+
mkdirSync(logsDir, { recursive: true });
|
|
336
|
+
}
|
|
337
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
338
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
339
|
+
<plist version="1.0">
|
|
340
|
+
<dict>
|
|
341
|
+
<key>Label</key>
|
|
342
|
+
<string>ai.opensentinel.daemon</string>
|
|
343
|
+
<key>ProgramArguments</key>
|
|
344
|
+
<array>
|
|
345
|
+
<string>${opensentinelPath}</string>
|
|
346
|
+
<string>start</string>
|
|
347
|
+
</array>
|
|
348
|
+
<key>WorkingDirectory</key>
|
|
349
|
+
<string>${configDir}</string>
|
|
350
|
+
<key>RunAtLoad</key>
|
|
351
|
+
<true/>
|
|
352
|
+
<key>KeepAlive</key>
|
|
353
|
+
<true/>
|
|
354
|
+
<key>StandardOutPath</key>
|
|
355
|
+
<string>${logsDir}/opensentinel.log</string>
|
|
356
|
+
<key>StandardErrorPath</key>
|
|
357
|
+
<string>${logsDir}/opensentinel-error.log</string>
|
|
358
|
+
</dict>
|
|
359
|
+
</plist>`;
|
|
360
|
+
const plistPath = join(homedir(), "Library", "LaunchAgents", "ai.opensentinel.daemon.plist");
|
|
361
|
+
writeFileSync(plistPath, plist);
|
|
362
|
+
console.log(` ${colors.green}LaunchAgent installed.${colors.reset}`);
|
|
363
|
+
console.log(` ${colors.dim}Plist: ${plistPath}${colors.reset}`);
|
|
364
|
+
const startNow = await confirm(" Start OpenSentinel now?");
|
|
365
|
+
if (startNow) {
|
|
366
|
+
await exec(`launchctl load ${plistPath}`);
|
|
367
|
+
console.log(` ${colors.green}Service started!${colors.reset}`);
|
|
368
|
+
console.log(` ${colors.dim}Logs: tail -f ${logsDir}/opensentinel.log${colors.reset}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
export {
|
|
372
|
+
setup as default
|
|
373
|
+
};
|
|
374
|
+
//# sourceMappingURL=setup.js.map
|