@openacp/cli 0.6.10 → 2026.41.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.
Files changed (133) hide show
  1. package/README.md +34 -16
  2. package/dist/cli.d.ts +11 -0
  3. package/dist/cli.js +27787 -467
  4. package/dist/cli.js.map +1 -1
  5. package/dist/data/registry-snapshot.json +1 -1
  6. package/dist/index.d.ts +1944 -463
  7. package/dist/index.js +17365 -102
  8. package/dist/index.js.map +1 -1
  9. package/package.json +13 -7
  10. package/dist/action-detect-P7ZE4NEM.js +0 -16
  11. package/dist/action-detect-P7ZE4NEM.js.map +0 -1
  12. package/dist/adapter-ZOANORGM.js +0 -799
  13. package/dist/adapter-ZOANORGM.js.map +0 -1
  14. package/dist/admin-6SYB6XCZ.js +0 -23
  15. package/dist/admin-6SYB6XCZ.js.map +0 -1
  16. package/dist/agent-catalog-FC3HGDEQ.js +0 -11
  17. package/dist/agent-catalog-FC3HGDEQ.js.map +0 -1
  18. package/dist/agent-dependencies-4OWBMZWZ.js +0 -24
  19. package/dist/agent-dependencies-4OWBMZWZ.js.map +0 -1
  20. package/dist/agent-registry-WT4NXPYG.js +0 -9
  21. package/dist/agent-registry-WT4NXPYG.js.map +0 -1
  22. package/dist/agent-store-VZLFPTZU.js +0 -9
  23. package/dist/agent-store-VZLFPTZU.js.map +0 -1
  24. package/dist/agents-QO7DKARJ.js +0 -15
  25. package/dist/agents-QO7DKARJ.js.map +0 -1
  26. package/dist/api-client-CFQT5U7D.js +0 -14
  27. package/dist/api-client-CFQT5U7D.js.map +0 -1
  28. package/dist/autostart-X33OGMX6.js +0 -23
  29. package/dist/autostart-X33OGMX6.js.map +0 -1
  30. package/dist/chunk-2CJ46J3C.js +0 -154
  31. package/dist/chunk-2CJ46J3C.js.map +0 -1
  32. package/dist/chunk-2HMQOC7N.js +0 -134
  33. package/dist/chunk-2HMQOC7N.js.map +0 -1
  34. package/dist/chunk-33RP6K2O.js +0 -435
  35. package/dist/chunk-33RP6K2O.js.map +0 -1
  36. package/dist/chunk-34M4OS5P.js +0 -83
  37. package/dist/chunk-34M4OS5P.js.map +0 -1
  38. package/dist/chunk-4CTX774K.js +0 -265
  39. package/dist/chunk-4CTX774K.js.map +0 -1
  40. package/dist/chunk-7QJS2XBD.js +0 -92
  41. package/dist/chunk-7QJS2XBD.js.map +0 -1
  42. package/dist/chunk-BNLGTZ34.js +0 -122
  43. package/dist/chunk-BNLGTZ34.js.map +0 -1
  44. package/dist/chunk-CS3KCJ5D.js +0 -4788
  45. package/dist/chunk-CS3KCJ5D.js.map +0 -1
  46. package/dist/chunk-GAK6PIBW.js +0 -224
  47. package/dist/chunk-GAK6PIBW.js.map +0 -1
  48. package/dist/chunk-I7WC6E5S.js +0 -71
  49. package/dist/chunk-I7WC6E5S.js.map +0 -1
  50. package/dist/chunk-J4SJTKIK.js +0 -203
  51. package/dist/chunk-J4SJTKIK.js.map +0 -1
  52. package/dist/chunk-JHYXKVV2.js +0 -183
  53. package/dist/chunk-JHYXKVV2.js.map +0 -1
  54. package/dist/chunk-JKBFUAJK.js +0 -282
  55. package/dist/chunk-JKBFUAJK.js.map +0 -1
  56. package/dist/chunk-KIRH7TUJ.js +0 -219
  57. package/dist/chunk-KIRH7TUJ.js.map +0 -1
  58. package/dist/chunk-LBIKITQT.js +0 -22
  59. package/dist/chunk-LBIKITQT.js.map +0 -1
  60. package/dist/chunk-LCRLAV4G.js +0 -1085
  61. package/dist/chunk-LCRLAV4G.js.map +0 -1
  62. package/dist/chunk-LGP2YGRL.js +0 -4880
  63. package/dist/chunk-LGP2YGRL.js.map +0 -1
  64. package/dist/chunk-MKHUZLII.js +0 -738
  65. package/dist/chunk-MKHUZLII.js.map +0 -1
  66. package/dist/chunk-NAMYZIS5.js +0 -1
  67. package/dist/chunk-NAMYZIS5.js.map +0 -1
  68. package/dist/chunk-NVPG6JCL.js +0 -724
  69. package/dist/chunk-NVPG6JCL.js.map +0 -1
  70. package/dist/chunk-O7CPGUAI.js +0 -298
  71. package/dist/chunk-O7CPGUAI.js.map +0 -1
  72. package/dist/chunk-OWP7RZ62.js +0 -697
  73. package/dist/chunk-OWP7RZ62.js.map +0 -1
  74. package/dist/chunk-S64CB6J3.js +0 -98
  75. package/dist/chunk-S64CB6J3.js.map +0 -1
  76. package/dist/chunk-UKT3G5IA.js +0 -484
  77. package/dist/chunk-UKT3G5IA.js.map +0 -1
  78. package/dist/chunk-V5GZQEIY.js +0 -101
  79. package/dist/chunk-V5GZQEIY.js.map +0 -1
  80. package/dist/chunk-VOIJ6OY4.js +0 -63
  81. package/dist/chunk-VOIJ6OY4.js.map +0 -1
  82. package/dist/chunk-VUNV25KB.js +0 -16
  83. package/dist/chunk-VUNV25KB.js.map +0 -1
  84. package/dist/chunk-W3EYKZNQ.js +0 -45
  85. package/dist/chunk-W3EYKZNQ.js.map +0 -1
  86. package/dist/chunk-WTZDAYZX.js +0 -172
  87. package/dist/chunk-WTZDAYZX.js.map +0 -1
  88. package/dist/chunk-XANPHG7W.js +0 -145
  89. package/dist/chunk-XANPHG7W.js.map +0 -1
  90. package/dist/config-6S355X75.js +0 -15
  91. package/dist/config-6S355X75.js.map +0 -1
  92. package/dist/config-editor-QQTZMWGD.js +0 -13
  93. package/dist/config-editor-QQTZMWGD.js.map +0 -1
  94. package/dist/config-registry-AHYI4MYL.js +0 -18
  95. package/dist/config-registry-AHYI4MYL.js.map +0 -1
  96. package/dist/daemon-4CS6HMB5.js +0 -30
  97. package/dist/daemon-4CS6HMB5.js.map +0 -1
  98. package/dist/discord-OMC52Y54.js +0 -2239
  99. package/dist/discord-OMC52Y54.js.map +0 -1
  100. package/dist/dist-UHQK5CXN.js +0 -21151
  101. package/dist/dist-UHQK5CXN.js.map +0 -1
  102. package/dist/doctor-HZZ5BSHB.js +0 -10
  103. package/dist/doctor-HZZ5BSHB.js.map +0 -1
  104. package/dist/doctor-OLYBO3V3.js +0 -15
  105. package/dist/doctor-OLYBO3V3.js.map +0 -1
  106. package/dist/install-cloudflared-Z7VCGOVG.js +0 -33
  107. package/dist/install-cloudflared-Z7VCGOVG.js.map +0 -1
  108. package/dist/install-jq-HUYSQWKR.js +0 -32
  109. package/dist/install-jq-HUYSQWKR.js.map +0 -1
  110. package/dist/integrate-PNEHRY2I.js +0 -373
  111. package/dist/integrate-PNEHRY2I.js.map +0 -1
  112. package/dist/log-NXABYJTT.js +0 -24
  113. package/dist/log-NXABYJTT.js.map +0 -1
  114. package/dist/main-XOZCLFUK.js +0 -238
  115. package/dist/main-XOZCLFUK.js.map +0 -1
  116. package/dist/menu-YY5MKHEK.js +0 -16
  117. package/dist/menu-YY5MKHEK.js.map +0 -1
  118. package/dist/new-session-FEO4J4VU.js +0 -17
  119. package/dist/new-session-FEO4J4VU.js.map +0 -1
  120. package/dist/post-upgrade-CJG5I7M2.js +0 -80
  121. package/dist/post-upgrade-CJG5I7M2.js.map +0 -1
  122. package/dist/session-IUSI7P5S.js +0 -20
  123. package/dist/session-IUSI7P5S.js.map +0 -1
  124. package/dist/settings-RQPAM4KC.js +0 -14
  125. package/dist/settings-RQPAM4KC.js.map +0 -1
  126. package/dist/setup-XHS4OMPM.js +0 -37
  127. package/dist/setup-XHS4OMPM.js.map +0 -1
  128. package/dist/suggest-7D6B542M.js +0 -38
  129. package/dist/suggest-7D6B542M.js.map +0 -1
  130. package/dist/tunnel-service-CJLUH6SZ.js +0 -1174
  131. package/dist/tunnel-service-CJLUH6SZ.js.map +0 -1
  132. package/dist/version-NQZBM5M7.js +0 -16
  133. package/dist/version-NQZBM5M7.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/adapters/slack/adapter.ts","../../src/adapters/slack/send-queue.ts","../../src/adapters/slack/utils.ts","../../src/adapters/slack/formatter.ts","../../src/adapters/slack/slug.ts","../../src/adapters/slack/channel-manager.ts","../../src/adapters/slack/permission-handler.ts","../../src/adapters/slack/event-router.ts","../../src/adapters/slack/text-buffer.ts"],"sourcesContent":["// src/adapters/slack/adapter.ts\nimport fs from \"node:fs\";\nimport { App } from \"@slack/bolt\";\nimport { WebClient } from \"@slack/web-api\";\nimport {\n ChannelAdapter,\n type ChannelConfig,\n type OpenACPCore,\n type OutgoingMessage,\n type PermissionRequest,\n type NotificationMessage,\n} from \"../../core/index.js\";\nimport type { Attachment } from \"../../core/types.js\";\nimport type { FileService } from \"../../core/file-service.js\";\nimport { createChildLogger } from \"../../core/log.js\";\nconst log = createChildLogger({ module: \"slack\" });\n\nimport type { SlackChannelConfig } from \"./types.js\";\nimport type { SlackSessionMeta, SlackFileInfo } from \"./types.js\";\nimport { SlackSendQueue } from \"./send-queue.js\";\nimport { SlackFormatter } from \"./formatter.js\";\nimport { SlackChannelManager } from \"./channel-manager.js\";\nimport { SlackPermissionHandler } from \"./permission-handler.js\";\nimport { SlackEventRouter } from \"./event-router.js\";\nimport { SlackTextBuffer } from \"./text-buffer.js\";\nimport { toSlug } from \"./slug.js\";\nimport { isAudioClip } from \"./utils.js\";\n\nexport class SlackAdapter extends ChannelAdapter<OpenACPCore> {\n private app!: App;\n private webClient!: WebClient;\n private queue!: SlackSendQueue;\n private formatter: SlackFormatter;\n private channelManager!: SlackChannelManager;\n private permissionHandler!: SlackPermissionHandler;\n private eventRouter!: SlackEventRouter;\n private sessions = new Map<string, SlackSessionMeta>();\n private textBuffers = new Map<string, SlackTextBuffer>();\n private botUserId = \"\";\n private slackConfig: SlackChannelConfig;\n private fileService!: FileService;\n\n constructor(core: OpenACPCore, config: SlackChannelConfig) {\n super(core, config as unknown as ChannelConfig);\n this.slackConfig = config;\n this.formatter = new SlackFormatter();\n }\n\n async start(): Promise<void> {\n const { botToken, appToken, signingSecret } = this.slackConfig;\n\n if (!botToken || !appToken || !signingSecret) {\n throw new Error(\"Slack adapter requires botToken, appToken, and signingSecret\");\n }\n\n this.app = new App({\n token: botToken,\n appToken,\n signingSecret,\n socketMode: true,\n });\n\n this.webClient = new WebClient(botToken);\n this.queue = new SlackSendQueue(this.webClient);\n this.fileService = this.core.fileService;\n\n // Resolve bot user ID — required to filter bot's own messages (prevent infinite loop)\n const authResult = await this.webClient.auth.test();\n if (!authResult.user_id) {\n throw new Error(\"Slack auth.test() did not return user_id — verify botToken is valid\");\n }\n this.botUserId = authResult.user_id as string;\n log.info({ botUserId: this.botUserId }, \"Slack bot authenticated\");\n\n this.channelManager = new SlackChannelManager(this.queue, this.slackConfig);\n\n // Permission handler — resolve permission gate when user clicks a button\n this.permissionHandler = new SlackPermissionHandler(\n this.queue,\n (requestId, optionId) => {\n for (const [sessionId, _meta] of this.sessions) {\n const session = this.core.sessionManager.getSession(sessionId);\n if (session && session.permissionGate.requestId === requestId) {\n session.permissionGate.resolve(optionId);\n log.info({ sessionId, requestId, optionId }, \"Permission resolved\");\n return;\n }\n }\n log.warn({ requestId, optionId }, \"No matching session found for permission response\");\n },\n );\n this.permissionHandler.register(this.app);\n\n // Event router — dispatch incoming messages from session channels to core\n this.eventRouter = new SlackEventRouter(\n (slackChannelId) => {\n for (const meta of this.sessions.values()) {\n if (meta.channelId === slackChannelId) return meta;\n }\n return undefined;\n },\n (sessionChannelSlug, text, userId, files) => {\n const processFiles = async (): Promise<Attachment[] | undefined> => {\n if (!files?.length) return undefined;\n const audioFiles = files.filter((f) => isAudioClip(f));\n if (!audioFiles.length) return undefined;\n\n const attachments: Attachment[] = [];\n for (const file of audioFiles) {\n const buffer = await this.downloadSlackFile(file.url_private);\n if (!buffer) continue;\n const mimeType = file.mimetype === \"video/mp4\" ? \"audio/mp4\" : file.mimetype;\n const sessionId = this.core.sessionManager.getSessionByThread(\"slack\", sessionChannelSlug)?.id;\n if (!sessionId) continue;\n const att = await this.fileService.saveFile(sessionId, file.name, buffer, mimeType);\n attachments.push(att);\n }\n return attachments.length > 0 ? attachments : undefined;\n };\n\n processFiles()\n .then((attachments) => {\n this.core\n .handleMessage({\n channelId: \"slack\",\n threadId: sessionChannelSlug,\n userId,\n text,\n attachments,\n })\n .catch((err) => log.error({ err }, \"handleMessage error\"));\n })\n .catch((err) => log.error({ err }, \"Failed to process audio files\"));\n },\n this.botUserId,\n this.slackConfig.notificationChannelId,\n // onNewSession: reply with guidance when user messages the notification channel\n async (_text, _userId) => {\n if (this.slackConfig.notificationChannelId) {\n await this.queue.enqueue(\"chat.postMessage\", {\n channel: this.slackConfig.notificationChannelId,\n text: \"💬 To start a new session, use the `/openacp-new` slash command in any channel.\",\n }).catch((err: unknown) => log.warn({ err }, \"Failed to send onNewSession reply\"));\n }\n },\n this.slackConfig,\n this.core.configManager.get().security.allowedUserIds,\n );\n this.eventRouter.register(this.app);\n\n // Start Bolt (Socket Mode)\n await this.app.start();\n log.info(\"Slack adapter started (Socket Mode)\");\n\n // Create startup session + channel (configurable — set autoCreateSession: false to skip)\n if (this.slackConfig.autoCreateSession !== false) {\n await this._createStartupSession();\n }\n }\n\n private async downloadSlackFile(url: string): Promise<Buffer | null> {\n try {\n const resp = await fetch(url, {\n headers: { Authorization: `Bearer ${this.slackConfig.botToken}` },\n });\n if (!resp.ok) {\n log.warn({ status: resp.status }, \"Failed to download Slack file\");\n return null;\n }\n // Slack returns 200 with HTML login page if bot lacks files:read scope\n const contentType = resp.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"text/html\")) {\n log.warn(\"Slack file download returned HTML instead of binary — bot likely missing files:read scope. Reinstall the Slack app with files:read scope.\");\n return null;\n }\n return Buffer.from(await resp.arrayBuffer());\n } catch (err) {\n log.error({ err }, \"Error downloading Slack file\");\n return null;\n }\n }\n\n private async uploadAudioFile(channelId: string, att: Attachment): Promise<void> {\n const fileBuffer = await fs.promises.readFile(att.filePath);\n await this.webClient.files.uploadV2({\n channel_id: channelId,\n file: fileBuffer,\n filename: att.fileName,\n });\n }\n\n private async _createStartupSession(): Promise<void> {\n try {\n let reuseChannelId = this.slackConfig.startupChannelId;\n\n // Try to reuse existing startup channel (Telegram ensureTopics pattern)\n if (reuseChannelId) {\n try {\n const info = await this.queue.enqueue<Record<string, unknown>>(\n \"conversations.info\", { channel: reuseChannelId },\n );\n const channel = (info as Record<string, unknown>)?.channel as Record<string, unknown> | undefined;\n if (!channel || typeof channel.is_archived !== \"boolean\") {\n log.warn({ reuseChannelId }, \"Unexpected conversations.info response shape, creating new channel\");\n reuseChannelId = undefined;\n } else if (channel.is_archived) {\n await this.queue.enqueue(\"conversations.unarchive\", { channel: reuseChannelId });\n log.info({ channelId: reuseChannelId }, \"Unarchived startup channel for reuse\");\n }\n } catch {\n // Channel deleted or inaccessible — will create new\n reuseChannelId = undefined;\n }\n }\n\n if (reuseChannelId) {\n // Reuse existing channel — create session pointing to it\n let hasSession = false;\n for (const m of this.sessions.values()) {\n if (m.channelId === reuseChannelId) { hasSession = true; break; }\n }\n if (!hasSession) {\n const session = await this.core.handleNewSession(\"slack\", undefined, undefined, { createThread: false });\n const slug = `startup-${session.id.slice(0, 8)}`;\n this.sessions.set(session.id, { channelId: reuseChannelId, channelSlug: slug });\n session.threadId = slug;\n // Persist slug to session store so session resume after restart can find it\n await this.core.sessionManager.patchRecord(session.id, {\n platform: { topicId: slug },\n });\n log.info({ sessionId: session.id, channelId: reuseChannelId }, \"Reused startup channel\");\n }\n } else {\n // Create new channel + session\n const session = await this.core.handleNewSession(\"slack\", undefined, undefined, { createThread: true });\n if (!session.threadId) {\n log.error({ sessionId: session.id }, \"Startup session created without threadId\");\n return;\n }\n\n // Persist channel ID to config for reuse on next restart\n const meta = this.sessions.get(session.id);\n if (meta) {\n await this.core.configManager.save(\n { channels: { slack: { startupChannelId: meta.channelId } } },\n );\n log.info({ sessionId: session.id, channelId: meta.channelId }, \"Saved startup channel to config\");\n }\n }\n\n // Notify\n if (this.slackConfig.notificationChannelId) {\n const startupMeta = [...this.sessions.values()].find(m =>\n m.channelId === (reuseChannelId ?? this.slackConfig.startupChannelId)\n );\n if (startupMeta) {\n await this.queue.enqueue(\"chat.postMessage\", {\n channel: this.slackConfig.notificationChannelId,\n text: `✅ OpenACP ready — chat with the agent in <#${startupMeta.channelId}>`,\n });\n }\n }\n } catch (err) {\n log.error({ err }, \"Failed to create/reuse Slack startup session\");\n }\n }\n\n async stop(): Promise<void> {\n // Flush all active text buffers before stopping to prevent data loss\n for (const [sessionId, buf] of this.textBuffers) {\n try {\n await buf.flush();\n } catch (err) {\n log.warn({ err, sessionId }, \"Flush failed during stop\");\n }\n buf.destroy();\n }\n this.textBuffers.clear();\n await this.app.stop();\n log.info(\"Slack adapter stopped\");\n }\n\n // --- ChannelAdapter implementations ---\n\n async createSessionThread(sessionId: string, name: string): Promise<string> {\n const meta = await this.channelManager.createChannel(sessionId, name);\n this.sessions.set(sessionId, meta);\n log.info({ sessionId, channelId: meta.channelId, slug: meta.channelSlug }, \"Session channel created\");\n // Return the slug as the threadId so that lookups via getSessionByThread work\n return meta.channelSlug;\n }\n\n async renameSessionThread(sessionId: string, newName: string): Promise<void> {\n const meta = this.sessions.get(sessionId);\n if (!meta) return;\n\n const newSlug = toSlug(newName, this.slackConfig.channelPrefix ?? \"openacp\");\n\n try {\n await this.queue.enqueue(\"conversations.rename\", {\n channel: meta.channelId,\n name: newSlug,\n });\n meta.channelSlug = newSlug;\n // Update session.threadId so getSessionByThread() keeps working after rename\n const session = this.core.sessionManager.getSession(sessionId);\n if (session) session.threadId = newSlug;\n const existingRecord = this.core.sessionManager.getSessionRecord(sessionId);\n await this.core.sessionManager.patchRecord(sessionId, {\n name: newName,\n platform: { ...(existingRecord?.platform ?? {}), topicId: newSlug },\n });\n log.info({ sessionId, newSlug }, \"Session channel renamed\");\n } catch (err) {\n log.warn({ err, sessionId }, \"Failed to rename Slack channel\");\n }\n }\n\n async deleteSessionThread(sessionId: string): Promise<void> {\n const meta = this.sessions.get(sessionId);\n if (!meta) return;\n\n try {\n await this.permissionHandler.cleanupSession(meta.channelId);\n } catch (err) {\n log.warn({ err, sessionId }, \"Failed to clean up permission buttons\");\n }\n\n try {\n await this.channelManager.archiveChannel(meta.channelId);\n log.info({ sessionId, channelId: meta.channelId }, \"Session channel archived\");\n } catch (err) {\n log.warn({ err, sessionId }, \"Failed to archive Slack channel\");\n }\n this.sessions.delete(sessionId);\n const buf = this.textBuffers.get(sessionId);\n if (buf) { buf.destroy(); this.textBuffers.delete(sessionId); }\n }\n\n private getTextBuffer(sessionId: string, channelId: string): SlackTextBuffer {\n let buf = this.textBuffers.get(sessionId);\n if (!buf) {\n buf = new SlackTextBuffer(channelId, sessionId, this.queue);\n this.textBuffers.set(sessionId, buf);\n }\n return buf;\n }\n\n async sendMessage(sessionId: string, content: OutgoingMessage): Promise<void> {\n const meta = this.sessions.get(sessionId);\n if (!meta) {\n log.warn({ sessionId }, \"No Slack channel for session, skipping message\");\n return;\n }\n\n // Text chunks are buffered and flushed as a single message after idle timeout\n if (content.type === \"text\") {\n const buf = this.getTextBuffer(sessionId, meta.channelId);\n buf.append(content.text ?? \"\");\n return;\n }\n\n // For session_end / error: flush any pending text first, then send the event\n if (content.type === \"session_end\" || content.type === \"error\") {\n const buf = this.textBuffers.get(sessionId);\n if (buf) {\n try {\n await buf.flush();\n } catch (err) {\n log.warn({ err, sessionId }, \"Flush failed on session_end\");\n }\n buf.destroy();\n this.textBuffers.delete(sessionId);\n }\n }\n\n if (content.type === \"attachment\" && content.attachment) {\n if (content.attachment.type === \"audio\") {\n try {\n await this.uploadAudioFile(meta.channelId, content.attachment);\n const buf = this.textBuffers.get(sessionId);\n if (buf) await buf.stripTtsBlock();\n } catch (err) {\n log.error({ err, sessionId }, \"Failed to upload audio to Slack\");\n }\n }\n return;\n }\n\n const blocks = this.formatter.formatOutgoing(content);\n if (blocks.length === 0) return;\n\n try {\n await this.queue.enqueue(\"chat.postMessage\", {\n channel: meta.channelId,\n text: content.text ?? content.type,\n blocks,\n });\n } catch (err) {\n log.error({ err, sessionId, type: content.type }, \"Failed to post Slack message\");\n }\n }\n\n // NOTE: Async flow — different from Telegram adapter.\n // Telegram: sendPermissionRequest awaits user response inline.\n // Slack: posts interactive buttons and returns immediately.\n // Resolution happens asynchronously via the Bolt action handler in\n // SlackPermissionHandler, which calls the PermissionResponseCallback\n // passed during construction. The callback iterates sessions to find\n // the matching permissionGate and resolves it.\n async sendPermissionRequest(\n sessionId: string,\n request: PermissionRequest,\n ): Promise<void> {\n const meta = this.sessions.get(sessionId);\n if (!meta) return;\n\n log.info({ sessionId, requestId: request.id }, \"Sending Slack permission request\");\n const blocks = this.formatter.formatPermissionRequest(request);\n\n try {\n const result = await this.queue.enqueue(\"chat.postMessage\", {\n channel: meta.channelId,\n text: `Permission request: ${request.description}`,\n blocks,\n });\n const ts = (result as { ts?: string })?.ts;\n if (ts) {\n this.permissionHandler.trackPendingMessage(request.id, meta.channelId, ts);\n }\n } catch (err) {\n log.error({ err, sessionId }, \"Failed to post Slack permission request\");\n }\n }\n\n async sendNotification(notification: NotificationMessage): Promise<void> {\n if (!this.slackConfig.notificationChannelId) return;\n\n const emoji: Record<string, string> = {\n completed: \"✅\",\n error: \"❌\",\n permission: \"🔐\",\n input_required: \"💬\",\n };\n const icon = emoji[notification.type] ?? \"ℹ️\";\n const text = `${icon} *${notification.sessionName ?? \"Session\"}*\\n${notification.summary}`;\n const blocks = this.formatter.formatNotification(text);\n\n try {\n await this.queue.enqueue(\"chat.postMessage\", {\n channel: this.slackConfig.notificationChannelId,\n text,\n blocks,\n });\n } catch (err) {\n log.warn({ err, sessionId: notification.sessionId }, \"Failed to send Slack notification\");\n }\n }\n}\n\nexport type { SlackChannelConfig } from \"./types.js\";\n","import PQueue from \"p-queue\";\nimport type { WebClient } from \"@slack/web-api\";\n\nexport type SlackMethod =\n | \"chat.postMessage\"\n | \"chat.update\"\n | \"conversations.create\"\n | \"conversations.rename\"\n | \"conversations.archive\"\n | \"conversations.invite\"\n | \"conversations.join\"\n | \"conversations.unarchive\"\n | \"conversations.info\";\n\n// Requests per minute per method (Slack Tier definitions)\nconst METHOD_RPM: Record<SlackMethod, number> = {\n \"chat.postMessage\": 50, // Tier 3\n \"chat.update\": 50, // Tier 3\n \"conversations.create\": 20, // Tier 2\n \"conversations.rename\": 20, // Tier 2\n \"conversations.archive\": 20, // Tier 2\n \"conversations.invite\": 20, // Tier 2\n \"conversations.join\": 20, // Tier 2\n \"conversations.unarchive\": 20, // Tier 2\n \"conversations.info\": 50, // Tier 3\n};\n\nexport interface ISlackSendQueue {\n enqueue<T = unknown>(method: SlackMethod, params: Record<string, unknown>): Promise<T>;\n}\n\nexport class SlackSendQueue implements ISlackSendQueue {\n private queues = new Map<SlackMethod, PQueue>();\n\n constructor(private client: WebClient) {\n for (const [method, rpm] of Object.entries(METHOD_RPM) as [SlackMethod, number][]) {\n // Spread requests evenly across the minute\n this.queues.set(method, new PQueue({\n interval: Math.ceil(60_000 / rpm),\n intervalCap: 1,\n carryoverConcurrencyCount: true,\n }));\n }\n }\n\n async enqueue<T = unknown>(method: SlackMethod, params: Record<string, unknown>): Promise<T> {\n const queue = this.queues.get(method);\n if (!queue) throw new Error(`Unknown Slack method: ${method}`);\n return queue.add(() => this.client.apiCall(method, params) as Promise<T>);\n }\n}\n","// src/adapters/slack/utils.ts\n// Shared utilities for Slack adapter modules.\n\nimport type { SlackFileInfo } from \"./types.js\";\n\n/** Detect Slack audio clips — MIME type or filename pattern */\nexport function isAudioClip(file: SlackFileInfo): boolean {\n return (file.mimetype === \"video/mp4\" && file.name?.startsWith(\"audio_message\")) ||\n file.mimetype?.startsWith(\"audio/\");\n}\n\nconst SECTION_LIMIT = 3000;\n\n/**\n * Split text at nearest newline boundary before `limit`.\n * Does NOT track code fence state — a triple-backtick block straddling\n * the boundary will be split mid-block.\n * Used by SlackFormatter and SlackTextBuffer to avoid exceeding Slack's\n * 3000-char section limit.\n */\nexport function splitSafe(text: string, limit = SECTION_LIMIT): string[] {\n if (text.length <= limit) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n if (remaining.length <= limit) { chunks.push(remaining); break; }\n let cut = remaining.lastIndexOf(\"\\n\", limit);\n if (cut <= 0) cut = limit;\n chunks.push(remaining.slice(0, cut));\n remaining = remaining.slice(cut).trimStart();\n }\n return chunks;\n}\n","// src/adapters/slack/formatter.ts\nimport type { types } from \"@slack/bolt\";\nimport type { OutgoingMessage, PermissionRequest } from \"../../core/types.js\";\nimport { splitSafe } from \"./utils.js\";\n\ntype KnownBlock = types.KnownBlock;\n\nexport interface ISlackFormatter {\n formatOutgoing(message: OutgoingMessage): KnownBlock[];\n formatPermissionRequest(req: PermissionRequest): KnownBlock[];\n formatNotification(text: string): KnownBlock[];\n formatSessionEnd(reason?: string): KnownBlock[];\n}\n\n/**\n * Convert a markdown string to Slack mrkdwn format.\n * Handles the most common patterns from AI responses.\n */\nexport function markdownToMrkdwn(text: string): string {\n return text\n // Fenced code blocks — preserve as-is (Slack supports ``` natively)\n // Headers: # H1 → placeholder (protected from italic regex)\n .replace(/^#{1,6}\\s+(.+)$/gm, \"\\x00BOLD\\x00$1\\x00BOLD\\x00\")\n // Bold: **text** → placeholder\n .replace(/\\*\\*(.+?)\\*\\*/g, \"\\x00BOLD\\x00$1\\x00BOLD\\x00\")\n // Italic: *text* → _text_ (won't match placeholder tokens)\n .replace(/(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)/g, \"_$1_\")\n // Restore bold/header placeholders → *text*\n .replace(/\\x00BOLD\\x00(.+?)\\x00BOLD\\x00/g, \"*$1*\")\n // Inline code: `code` — kept as-is (Slack supports backtick)\n // Strikethrough: ~~text~~ → ~text~\n .replace(/~~(.+?)~~/g, \"~$1~\")\n // Links: [text](url) → <url|text>\n .replace(/\\[([^\\]]+)\\]\\((https?:\\/\\/[^)]+)\\)/g, \"<$2|$1>\")\n // Unordered lists: \"- item\" or \"* item\" → \"• item\"\n .replace(/^[ \\t]*[-*]\\s+/gm, \"• \")\n // Ordered lists: \"1. item\" → \"1. item\" (already fine in mrkdwn)\n .trim();\n}\n\n// Slack mrkdwn text block, max 3000 chars per section\nconst SECTION_LIMIT = 3000;\n\nfunction section(text: string): KnownBlock {\n return { type: \"section\", text: { type: \"mrkdwn\", text: text.slice(0, SECTION_LIMIT) } };\n}\n\nfunction context(text: string): KnownBlock {\n return { type: \"context\", elements: [{ type: \"mrkdwn\", text }] };\n}\n\nexport class SlackFormatter implements ISlackFormatter {\n formatOutgoing(message: OutgoingMessage): KnownBlock[] {\n switch (message.type) {\n case \"text\": {\n const text = message.text ?? \"\";\n if (!text.trim()) return [];\n const converted = markdownToMrkdwn(text);\n return splitSafe(converted).map(chunk => section(chunk));\n }\n\n case \"thought\":\n return [context(`💭 _${(message.text ?? \"\").slice(0, 500)}_`)];\n\n case \"tool_call\": {\n const name = (message as OutgoingMessage & { metadata?: { name?: string; input?: unknown } }).metadata?.name ?? \"tool\";\n const input = (message as OutgoingMessage & { metadata?: { input?: unknown } }).metadata?.input;\n const inputStr = input ? `\\n\\`\\`\\`\\n${JSON.stringify(input, null, 2).slice(0, 500)}\\n\\`\\`\\`` : \"\";\n return [context(`🔧 \\`${name}\\`${inputStr}`)];\n }\n\n case \"tool_update\": {\n const name = (message as OutgoingMessage & { metadata?: { name?: string; status?: string } }).metadata?.name ?? \"tool\";\n const status = (message as OutgoingMessage & { metadata?: { status?: string } }).metadata?.status ?? \"done\";\n const icon = status === \"error\" ? \"❌\" : \"✅\";\n return [context(`${icon} \\`${name}\\` — ${status}`)];\n }\n\n case \"plan\":\n return [\n { type: \"divider\" },\n section(`📋 *Plan*\\n${message.text ?? \"\"}`),\n ];\n\n case \"usage\": {\n const meta = (message as OutgoingMessage & { metadata?: { input_tokens?: number; output_tokens?: number; cost_usd?: number } }).metadata ?? {};\n const parts = [\n meta.input_tokens != null ? `in: ${meta.input_tokens}` : null,\n meta.output_tokens != null ? `out: ${meta.output_tokens}` : null,\n meta.cost_usd != null ? `$${Number(meta.cost_usd).toFixed(4)}` : null,\n ].filter((p): p is string => p !== null);\n return parts.length ? [context(`📊 ${parts.join(\" · \")}`)] : [];\n }\n\n case \"session_end\":\n return this.formatSessionEnd(message.text);\n\n case \"error\":\n return [section(`⚠️ *Error:* ${message.text ?? \"Unknown error\"}`)];\n\n default:\n return [];\n }\n }\n\n formatPermissionRequest(req: PermissionRequest): KnownBlock[] {\n return [\n section(`🔐 *Permission Request*\\n${req.description}`),\n {\n type: \"actions\",\n block_id: `perm_${req.id}`,\n elements: req.options.map(opt => ({\n type: \"button\" as const,\n text: { type: \"plain_text\" as const, text: opt.label, emoji: true },\n value: `${req.id}:${opt.id}`,\n action_id: `perm_action_${opt.id}_${req.id}`,\n style: (opt.isAllow ? \"primary\" : \"danger\") as \"primary\" | \"danger\",\n })),\n } as KnownBlock,\n ];\n }\n\n formatNotification(text: string): KnownBlock[] {\n return [section(text)];\n }\n\n formatSessionEnd(reason?: string): KnownBlock[] {\n return [\n { type: \"divider\" },\n context(`✅ Session ended${reason ? ` — ${reason}` : \"\"}`),\n ];\n }\n}\n","// src/adapters/slack/slug.ts\nimport { customAlphabet } from \"nanoid\";\n\nconst nanoidAlpha = customAlphabet(\"abcdefghijklmnopqrstuvwxyz0123456789\", 4);\n\n/**\n * Convert a human-readable session name to a valid Slack channel name.\n * Rules: lowercase, ≤80 chars, only [a-z0-9-], unique suffix appended.\n *\n * Examples:\n * \"Fix authentication bug\" → \"openacp-fix-authentication-bug-a3k9\"\n * \"New Session\" → \"openacp-new-session-x7p2\"\n * \"Implement OAuth 2.0 & JWT refresh\" → \"openacp-implement-oauth-20-jwt-refresh-b8qr\"\n */\nexport function toSlug(name: string, prefix = \"openacp\"): string {\n const base = name\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, \"\") // strip special chars\n .trim()\n .replace(/\\s+/g, \"-\") // spaces → dashes\n .replace(/-+/g, \"-\") // collapse consecutive dashes\n .slice(0, 60); // leave room for prefix and suffix\n\n const suffix = nanoidAlpha();\n return `${prefix}-${base}-${suffix}`.replace(/-+/g, \"-\");\n}\n","// src/adapters/slack/channel-manager.ts\nimport type { ISlackSendQueue } from \"./send-queue.js\";\nimport { toSlug } from \"./slug.js\";\nimport type { SlackSessionMeta } from \"./types.js\";\nimport type { SlackChannelConfig } from \"./types.js\";\n\nexport interface ISlackChannelManager {\n createChannel(sessionId: string, sessionName: string): Promise<SlackSessionMeta>;\n archiveChannel(channelId: string): Promise<void>;\n notifyChannel(text: string): Promise<void>;\n}\n\nexport class SlackChannelManager implements ISlackChannelManager {\n constructor(\n private queue: ISlackSendQueue,\n private config: SlackChannelConfig,\n ) {}\n\n async createChannel(sessionId: string, sessionName: string): Promise<SlackSessionMeta> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n const finalSlug = toSlug(sessionName, this.config.channelPrefix ?? \"openacp\");\n\n try {\n const res = await this.queue.enqueue<{ channel: { id: string } }>(\n \"conversations.create\",\n { name: finalSlug, is_private: true },\n );\n const channelId = res.channel.id;\n\n // Bot is automatically a member of private channels it creates — no join/invite needed.\n // Invite configured users so they can access the channel.\n const userIds = this.config.allowedUserIds ?? [];\n if (userIds.length > 0) {\n await this.queue.enqueue(\"conversations.invite\", {\n channel: channelId,\n users: userIds.join(\",\"),\n });\n }\n\n return { channelId, channelSlug: finalSlug };\n } catch (err: any) {\n if (err?.data?.error === \"name_taken\" && attempt < 2) {\n lastError = err;\n continue;\n }\n throw err;\n }\n }\n\n throw lastError;\n }\n\n async archiveChannel(channelId: string): Promise<void> {\n await this.queue.enqueue(\"conversations.archive\", { channel: channelId });\n }\n\n async notifyChannel(text: string): Promise<void> {\n if (this.config.notificationChannelId) {\n await this.queue.enqueue(\"chat.postMessage\", {\n channel: this.config.notificationChannelId,\n text,\n });\n }\n }\n}\n","import type { App, BlockAction, ButtonAction } from \"@slack/bolt\";\nimport type { ISlackSendQueue } from \"./send-queue.js\";\n\nexport type PermissionResponseCallback = (requestId: string, optionId: string) => void;\n\nexport interface ISlackPermissionHandler {\n register(app: App): void;\n trackPendingMessage(requestId: string, channelId: string, messageTs: string): void;\n cleanupSession(channelId: string): Promise<void>;\n}\n\nexport class SlackPermissionHandler implements ISlackPermissionHandler {\n private pendingMessages = new Map<string, { channelId: string; messageTs: string }>();\n\n constructor(\n private queue: ISlackSendQueue,\n private onResponse: PermissionResponseCallback,\n ) {}\n\n trackPendingMessage(requestId: string, channelId: string, messageTs: string): void {\n this.pendingMessages.set(requestId, { channelId, messageTs });\n }\n\n async cleanupSession(channelId: string): Promise<void> {\n for (const [requestId, info] of this.pendingMessages) {\n if (info.channelId !== channelId) continue;\n await this.queue.enqueue(\"chat.update\", {\n channel: info.channelId,\n ts: info.messageTs,\n blocks: [],\n });\n this.pendingMessages.delete(requestId);\n }\n }\n\n register(app: App): void {\n // Match any action starting with \"perm_action_\"\n app.action<BlockAction<ButtonAction>>(\n /^perm_action_/,\n async ({ ack, body, action }) => {\n await ack();\n\n const value: string = action.value ?? \"\";\n const colonIdx = value.indexOf(\":\");\n if (colonIdx === -1) return;\n\n const requestId = value.slice(0, colonIdx);\n const optionId = value.slice(colonIdx + 1);\n\n this.onResponse(requestId, optionId);\n\n // Remove from pending tracking since the user has responded\n this.pendingMessages.delete(requestId);\n\n // Update message to remove action buttons and show confirmation\n const message = body.message;\n if (message) {\n await this.queue.enqueue(\"chat.update\", {\n channel: body.channel?.id ?? \"\",\n ts: message.ts,\n text: `✅ Permission response: *${optionId}*`,\n blocks: [],\n });\n }\n }\n );\n }\n}\n","// src/adapters/slack/event-router.ts\nimport type { App } from \"@slack/bolt\";\nimport type { SlackSessionMeta, SlackFileInfo } from \"./types.js\";\nimport type { SlackChannelConfig } from \"./types.js\";\nimport { createChildLogger } from \"../../core/log.js\";\nconst log = createChildLogger({ module: \"slack-event-router\" });\n\n/** Subset of Bolt's message event fields used by the router */\ninterface SlackMessageEvent {\n bot_id?: string;\n subtype?: string;\n channel: string;\n text?: string;\n user?: string;\n files?: Array<{\n id: string;\n name: string;\n mimetype: string;\n size: number;\n url_private: string;\n }>;\n}\n\n// Callback to look up which session (if any) owns a Slack channelId\nexport type SessionLookup = (channelId: string) => SlackSessionMeta | undefined;\n\n// Callback to dispatch an incoming message to core\nexport type IncomingMessageCallback = (sessionId: string, text: string, userId: string, files?: SlackFileInfo[]) => void;\n\n// Callback to create a new session when user messages the notification channel\nexport type NewSessionCallback = (text: string, userId: string) => void;\n\nexport interface ISlackEventRouter {\n register(app: App): void;\n}\n\nexport class SlackEventRouter implements ISlackEventRouter {\n constructor(\n private sessionLookup: SessionLookup,\n private onIncoming: IncomingMessageCallback,\n private botUserId: string,\n private notificationChannelId: string | undefined,\n private onNewSession: NewSessionCallback,\n private config: SlackChannelConfig,\n private globalAllowedUserIds: string[] = [],\n ) {}\n\n private isAllowedUser(userId: string): boolean {\n const slackAllowed = this.config.allowedUserIds ?? [];\n const allowed = slackAllowed.length > 0 ? slackAllowed : this.globalAllowedUserIds;\n if (allowed.length === 0) return true;\n return allowed.includes(userId);\n }\n\n register(app: App): void {\n app.message(async ({ message }) => {\n log.debug({ message }, \"Slack raw message event\");\n\n const msg = message as unknown as SlackMessageEvent;\n\n if (msg.bot_id) return;\n const subtype = msg.subtype;\n if (subtype && subtype !== \"file_share\") return; // edited, deleted, etc.\n\n const channelId = msg.channel;\n const text: string = msg.text ?? \"\";\n const userId: string = msg.user ?? \"\";\n\n const files: SlackFileInfo[] | undefined = msg.files?.map((f) => ({\n id: f.id,\n name: f.name,\n mimetype: f.mimetype,\n size: f.size,\n url_private: f.url_private,\n }));\n\n log.debug({ channelId, userId, text }, \"Slack message received\");\n\n // Ignore messages from the bot itself\n if (userId === this.botUserId) return;\n\n // Enforce allowedUserIds\n if (!this.isAllowedUser(userId)) {\n log.warn({ userId }, \"slack: message from non-allowed user rejected\");\n return;\n }\n\n const session = this.sessionLookup(channelId);\n if (session) {\n // Message to an existing session channel\n log.debug({ channelId, sessionSlug: session.channelSlug }, \"Routing to session\");\n this.onIncoming(session.channelSlug, text, userId, files);\n return;\n }\n\n log.debug({ channelId, notificationChannelId: this.notificationChannelId }, \"No session found for channel\");\n\n // Message to the notification channel → create new session\n if (this.notificationChannelId && channelId === this.notificationChannelId) {\n this.onNewSession(text, userId);\n return;\n }\n });\n }\n}\n","// src/adapters/slack/text-buffer.ts\n// Buffers streamed text chunks per session and flushes as a single Slack message.\n// This prevents the \"many tiny messages\" problem from streaming AI responses.\n\nimport type { ISlackSendQueue } from \"./send-queue.js\";\nimport { markdownToMrkdwn } from \"./formatter.js\";\nimport { splitSafe } from \"./utils.js\";\nimport { createChildLogger } from \"../../core/log.js\";\n\nconst log = createChildLogger({ module: \"slack-text-buffer\" });\n\nconst FLUSH_IDLE_MS = 2000; // flush after 2s of no new chunks\n\nexport class SlackTextBuffer {\n private buffer = \"\";\n private timer: ReturnType<typeof setTimeout> | undefined;\n private flushPromise: Promise<void> | undefined;\n private lastMessageTs: string | undefined;\n private lastPostedText: string | undefined;\n\n constructor(\n private channelId: string,\n private sessionId: string,\n private queue: ISlackSendQueue,\n ) {}\n\n append(text: string): void {\n if (!text) return;\n this.buffer += text;\n this.resetTimer();\n }\n\n private resetTimer(): void {\n if (this.timer) clearTimeout(this.timer);\n this.timer = setTimeout(() => {\n this.timer = undefined;\n this.flush().catch((err) => log.error({ err, sessionId: this.sessionId }, \"Text buffer flush error\"));\n }, FLUSH_IDLE_MS);\n }\n\n async flush(): Promise<void> {\n if (this.flushPromise) return this.flushPromise;\n const text = this.buffer.trim();\n if (!text) return;\n this.buffer = \"\";\n if (this.timer) { clearTimeout(this.timer); this.timer = undefined; }\n\n this.flushPromise = (async () => {\n try {\n const converted = markdownToMrkdwn(text);\n const chunks = splitSafe(converted);\n for (const chunk of chunks) {\n if (!chunk.trim()) continue;\n const result = await this.queue.enqueue(\"chat.postMessage\", {\n channel: this.channelId,\n text: chunk,\n blocks: [{ type: \"section\", text: { type: \"mrkdwn\", text: chunk } }],\n });\n // Track last posted message for potential TTS block editing\n this.lastMessageTs = (result as { ts?: string } | undefined)?.ts;\n this.lastPostedText = chunk;\n }\n } finally {\n this.flushPromise = undefined;\n // Re-flush if content arrived while we were flushing\n if (this.buffer.trim()) {\n await this.flush();\n }\n }\n })();\n\n return this.flushPromise;\n }\n\n destroy(): void {\n if (this.timer) { clearTimeout(this.timer); this.timer = undefined; }\n this.buffer = \"\";\n }\n\n /** Remove [TTS]...[/TTS] blocks — from buffer if unflushed, or edit posted message */\n async stripTtsBlock(): Promise<void> {\n // Case 1: TTS block still in unflushed buffer\n if (/\\[TTS\\][\\s\\S]*?\\[\\/TTS\\]/.test(this.buffer)) {\n this.buffer = this.buffer.replace(/\\[TTS\\][\\s\\S]*?\\[\\/TTS\\]/g, \"\").replace(/\\s{2,}/g, \" \").trim();\n return;\n }\n\n // Case 2: Already flushed — edit the posted message via chat.update\n if (this.lastMessageTs && this.lastPostedText && /\\[TTS\\][\\s\\S]*?\\[\\/TTS\\]/.test(this.lastPostedText)) {\n const cleaned = this.lastPostedText.replace(/\\[TTS\\][\\s\\S]*?\\[\\/TTS\\]/g, \"\").replace(/\\s{2,}/g, \" \").trim();\n if (cleaned) {\n await this.queue.enqueue(\"chat.update\", {\n channel: this.channelId,\n ts: this.lastMessageTs,\n text: cleaned,\n blocks: [{ type: \"section\", text: { type: \"mrkdwn\", text: cleaned } }],\n });\n }\n this.lastPostedText = cleaned;\n }\n }\n}\n"],"mappings":"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","names":["SECTION_LIMIT","log","log"]}
@@ -1,23 +0,0 @@
1
- import {
2
- buildSessionControlKeyboard,
3
- handleDangerous,
4
- handleDangerousButton,
5
- handleRestart,
6
- handleTTS,
7
- handleTTSButton,
8
- handleUpdate,
9
- handleVerbosity
10
- } from "./chunk-KIRH7TUJ.js";
11
- import "./chunk-GAK6PIBW.js";
12
- import "./chunk-VUNV25KB.js";
13
- export {
14
- buildSessionControlKeyboard,
15
- handleDangerous,
16
- handleDangerousButton,
17
- handleRestart,
18
- handleTTS,
19
- handleTTSButton,
20
- handleUpdate,
21
- handleVerbosity
22
- };
23
- //# sourceMappingURL=admin-6SYB6XCZ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,11 +0,0 @@
1
- import {
2
- AgentCatalog
3
- } from "./chunk-UKT3G5IA.js";
4
- import "./chunk-34M4OS5P.js";
5
- import "./chunk-JKBFUAJK.js";
6
- import "./chunk-GAK6PIBW.js";
7
- import "./chunk-VUNV25KB.js";
8
- export {
9
- AgentCatalog
10
- };
11
- //# sourceMappingURL=agent-catalog-FC3HGDEQ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,24 +0,0 @@
1
- import {
2
- REGISTRY_AGENT_ALIASES,
3
- checkDependencies,
4
- checkRuntimeAvailable,
5
- commandExists,
6
- getAgentAlias,
7
- getAgentCapabilities,
8
- getAgentDependencies,
9
- getAgentSetup,
10
- listAgentsWithIntegration
11
- } from "./chunk-JKBFUAJK.js";
12
- import "./chunk-VUNV25KB.js";
13
- export {
14
- REGISTRY_AGENT_ALIASES,
15
- checkDependencies,
16
- checkRuntimeAvailable,
17
- commandExists,
18
- getAgentAlias,
19
- getAgentCapabilities,
20
- getAgentDependencies,
21
- getAgentSetup,
22
- listAgentsWithIntegration
23
- };
24
- //# sourceMappingURL=agent-dependencies-4OWBMZWZ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,9 +0,0 @@
1
- import "./chunk-NAMYZIS5.js";
2
- import {
3
- getAgentCapabilities
4
- } from "./chunk-JKBFUAJK.js";
5
- import "./chunk-VUNV25KB.js";
6
- export {
7
- getAgentCapabilities
8
- };
9
- //# sourceMappingURL=agent-registry-WT4NXPYG.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,9 +0,0 @@
1
- import {
2
- AgentStore
3
- } from "./chunk-34M4OS5P.js";
4
- import "./chunk-GAK6PIBW.js";
5
- import "./chunk-VUNV25KB.js";
6
- export {
7
- AgentStore
8
- };
9
- //# sourceMappingURL=agent-store-VZLFPTZU.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,15 +0,0 @@
1
- import {
2
- handleAgentButton,
3
- handleAgents,
4
- handleInstall,
5
- showAgentsList
6
- } from "./chunk-J4SJTKIK.js";
7
- import "./chunk-GAK6PIBW.js";
8
- import "./chunk-VUNV25KB.js";
9
- export {
10
- handleAgentButton,
11
- handleAgents,
12
- handleInstall,
13
- showAgentsList
14
- };
15
- //# sourceMappingURL=agents-QO7DKARJ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,14 +0,0 @@
1
- import {
2
- apiCall,
3
- readApiPort,
4
- readApiSecret,
5
- removeStalePortFile
6
- } from "./chunk-W3EYKZNQ.js";
7
- import "./chunk-VUNV25KB.js";
8
- export {
9
- apiCall,
10
- readApiPort,
11
- readApiSecret,
12
- removeStalePortFile
13
- };
14
- //# sourceMappingURL=api-client-CFQT5U7D.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,23 +0,0 @@
1
- import {
2
- escapeSystemdValue,
3
- escapeXml,
4
- generateLaunchdPlist,
5
- generateSystemdUnit,
6
- installAutoStart,
7
- isAutoStartInstalled,
8
- isAutoStartSupported,
9
- uninstallAutoStart
10
- } from "./chunk-2CJ46J3C.js";
11
- import "./chunk-GAK6PIBW.js";
12
- import "./chunk-VUNV25KB.js";
13
- export {
14
- escapeSystemdValue,
15
- escapeXml,
16
- generateLaunchdPlist,
17
- generateSystemdUnit,
18
- installAutoStart,
19
- isAutoStartInstalled,
20
- isAutoStartSupported,
21
- uninstallAutoStart
22
- };
23
- //# sourceMappingURL=autostart-X33OGMX6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,154 +0,0 @@
1
- import {
2
- createChildLogger
3
- } from "./chunk-GAK6PIBW.js";
4
-
5
- // src/core/autostart.ts
6
- import { execFileSync } from "child_process";
7
- import * as fs from "fs";
8
- import * as path from "path";
9
- import * as os from "os";
10
- var log = createChildLogger({ module: "autostart" });
11
- var LAUNCHD_LABEL = "com.openacp.daemon";
12
- var LAUNCHD_PLIST_PATH = path.join(os.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
13
- var SYSTEMD_SERVICE_PATH = path.join(os.homedir(), ".config", "systemd", "user", "openacp.service");
14
- function isAutoStartSupported() {
15
- return process.platform === "darwin" || process.platform === "linux";
16
- }
17
- function escapeXml(str) {
18
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
19
- }
20
- function escapeSystemdValue(str) {
21
- const escaped = str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\$/g, "$$$$").replace(/%/g, "%%");
22
- return `"${escaped}"`;
23
- }
24
- function generateLaunchdPlist(nodePath, cliPath, logDir) {
25
- const logFile = path.join(logDir, "openacp.log");
26
- return `<?xml version="1.0" encoding="UTF-8"?>
27
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
28
- <plist version="1.0">
29
- <dict>
30
- <key>Label</key>
31
- <string>${LAUNCHD_LABEL}</string>
32
- <key>ProgramArguments</key>
33
- <array>
34
- <string>${escapeXml(nodePath)}</string>
35
- <string>${escapeXml(cliPath)}</string>
36
- <string>--daemon-child</string>
37
- </array>
38
- <key>RunAtLoad</key>
39
- <true/>
40
- <key>KeepAlive</key>
41
- <dict>
42
- <key>SuccessfulExit</key>
43
- <false/>
44
- </dict>
45
- <key>StandardOutPath</key>
46
- <string>${escapeXml(logFile)}</string>
47
- <key>StandardErrorPath</key>
48
- <string>${escapeXml(logFile)}</string>
49
- </dict>
50
- </plist>
51
- `;
52
- }
53
- function generateSystemdUnit(nodePath, cliPath) {
54
- return `[Unit]
55
- Description=OpenACP Daemon
56
-
57
- [Service]
58
- ExecStart=${escapeSystemdValue(nodePath)} ${escapeSystemdValue(cliPath)} --daemon-child
59
- Restart=on-failure
60
-
61
- [Install]
62
- WantedBy=default.target
63
- `;
64
- }
65
- function installAutoStart(logDir) {
66
- if (!isAutoStartSupported()) {
67
- return { success: false, error: "Auto-start not supported on this platform" };
68
- }
69
- const nodePath = process.execPath;
70
- const cliPath = path.resolve(process.argv[1]);
71
- const resolvedLogDir = logDir.startsWith("~") ? path.join(os.homedir(), logDir.slice(1)) : logDir;
72
- try {
73
- if (process.platform === "darwin") {
74
- const plist = generateLaunchdPlist(nodePath, cliPath, resolvedLogDir);
75
- const dir = path.dirname(LAUNCHD_PLIST_PATH);
76
- fs.mkdirSync(dir, { recursive: true });
77
- fs.writeFileSync(LAUNCHD_PLIST_PATH, plist);
78
- execFileSync("launchctl", ["load", LAUNCHD_PLIST_PATH], { stdio: "pipe" });
79
- log.info("LaunchAgent installed");
80
- return { success: true };
81
- }
82
- if (process.platform === "linux") {
83
- const unit = generateSystemdUnit(nodePath, cliPath);
84
- const dir = path.dirname(SYSTEMD_SERVICE_PATH);
85
- fs.mkdirSync(dir, { recursive: true });
86
- fs.writeFileSync(SYSTEMD_SERVICE_PATH, unit);
87
- execFileSync("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
88
- execFileSync("systemctl", ["--user", "enable", "openacp"], { stdio: "pipe" });
89
- log.info("systemd user service installed");
90
- return { success: true };
91
- }
92
- return { success: false, error: "Unsupported platform" };
93
- } catch (e) {
94
- const msg = e.message;
95
- log.error({ err: msg }, "Failed to install auto-start");
96
- return { success: false, error: msg };
97
- }
98
- }
99
- function uninstallAutoStart() {
100
- if (!isAutoStartSupported()) {
101
- return { success: false, error: "Auto-start not supported on this platform" };
102
- }
103
- try {
104
- if (process.platform === "darwin") {
105
- if (fs.existsSync(LAUNCHD_PLIST_PATH)) {
106
- try {
107
- execFileSync("launchctl", ["unload", LAUNCHD_PLIST_PATH], { stdio: "pipe" });
108
- } catch {
109
- }
110
- fs.unlinkSync(LAUNCHD_PLIST_PATH);
111
- log.info("LaunchAgent removed");
112
- }
113
- return { success: true };
114
- }
115
- if (process.platform === "linux") {
116
- if (fs.existsSync(SYSTEMD_SERVICE_PATH)) {
117
- try {
118
- execFileSync("systemctl", ["--user", "disable", "openacp"], { stdio: "pipe" });
119
- } catch {
120
- }
121
- fs.unlinkSync(SYSTEMD_SERVICE_PATH);
122
- execFileSync("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
123
- log.info("systemd user service removed");
124
- }
125
- return { success: true };
126
- }
127
- return { success: false, error: "Unsupported platform" };
128
- } catch (e) {
129
- const msg = e.message;
130
- log.error({ err: msg }, "Failed to uninstall auto-start");
131
- return { success: false, error: msg };
132
- }
133
- }
134
- function isAutoStartInstalled() {
135
- if (process.platform === "darwin") {
136
- return fs.existsSync(LAUNCHD_PLIST_PATH);
137
- }
138
- if (process.platform === "linux") {
139
- return fs.existsSync(SYSTEMD_SERVICE_PATH);
140
- }
141
- return false;
142
- }
143
-
144
- export {
145
- isAutoStartSupported,
146
- escapeXml,
147
- escapeSystemdValue,
148
- generateLaunchdPlist,
149
- generateSystemdUnit,
150
- installAutoStart,
151
- uninstallAutoStart,
152
- isAutoStartInstalled
153
- };
154
- //# sourceMappingURL=chunk-2CJ46J3C.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/core/autostart.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport { createChildLogger } from './log.js'\n\nconst log = createChildLogger({ module: 'autostart' })\n\nconst LAUNCHD_LABEL = 'com.openacp.daemon'\nconst LAUNCHD_PLIST_PATH = path.join(os.homedir(), 'Library', 'LaunchAgents', `${LAUNCHD_LABEL}.plist`)\nconst SYSTEMD_SERVICE_PATH = path.join(os.homedir(), '.config', 'systemd', 'user', 'openacp.service')\n\nexport function isAutoStartSupported(): boolean {\n return process.platform === 'darwin' || process.platform === 'linux'\n}\n\nexport function escapeXml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;')\n}\n\nexport function escapeSystemdValue(str: string): string {\n const escaped = str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\$/g, '$$$$')\n .replace(/%/g, '%%')\n return `\"${escaped}\"`\n}\n\nexport function generateLaunchdPlist(nodePath: string, cliPath: string, logDir: string): string {\n const logFile = path.join(logDir, 'openacp.log')\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${LAUNCHD_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(nodePath)}</string>\n <string>${escapeXml(cliPath)}</string>\n <string>--daemon-child</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n <key>StandardOutPath</key>\n <string>${escapeXml(logFile)}</string>\n <key>StandardErrorPath</key>\n <string>${escapeXml(logFile)}</string>\n</dict>\n</plist>\n`\n}\n\nexport function generateSystemdUnit(nodePath: string, cliPath: string): string {\n return `[Unit]\nDescription=OpenACP Daemon\n\n[Service]\nExecStart=${escapeSystemdValue(nodePath)} ${escapeSystemdValue(cliPath)} --daemon-child\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n`\n}\n\nexport function installAutoStart(logDir: string): { success: boolean; error?: string } {\n if (!isAutoStartSupported()) {\n return { success: false, error: 'Auto-start not supported on this platform' }\n }\n\n const nodePath = process.execPath\n const cliPath = path.resolve(process.argv[1])\n const resolvedLogDir = logDir.startsWith('~')\n ? path.join(os.homedir(), logDir.slice(1))\n : logDir\n\n try {\n if (process.platform === 'darwin') {\n const plist = generateLaunchdPlist(nodePath, cliPath, resolvedLogDir)\n const dir = path.dirname(LAUNCHD_PLIST_PATH)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(LAUNCHD_PLIST_PATH, plist)\n execFileSync('launchctl', ['load', LAUNCHD_PLIST_PATH], { stdio: 'pipe' })\n log.info('LaunchAgent installed')\n return { success: true }\n }\n\n if (process.platform === 'linux') {\n const unit = generateSystemdUnit(nodePath, cliPath)\n const dir = path.dirname(SYSTEMD_SERVICE_PATH)\n fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(SYSTEMD_SERVICE_PATH, unit)\n execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' })\n execFileSync('systemctl', ['--user', 'enable', 'openacp'], { stdio: 'pipe' })\n log.info('systemd user service installed')\n return { success: true }\n }\n\n return { success: false, error: 'Unsupported platform' }\n } catch (e) {\n const msg = (e as Error).message\n log.error({ err: msg }, 'Failed to install auto-start')\n return { success: false, error: msg }\n }\n}\n\nexport function uninstallAutoStart(): { success: boolean; error?: string } {\n if (!isAutoStartSupported()) {\n return { success: false, error: 'Auto-start not supported on this platform' }\n }\n\n try {\n if (process.platform === 'darwin') {\n if (fs.existsSync(LAUNCHD_PLIST_PATH)) {\n try {\n execFileSync('launchctl', ['unload', LAUNCHD_PLIST_PATH], { stdio: 'pipe' })\n } catch {\n // may already be unloaded\n }\n fs.unlinkSync(LAUNCHD_PLIST_PATH)\n log.info('LaunchAgent removed')\n }\n return { success: true }\n }\n\n if (process.platform === 'linux') {\n if (fs.existsSync(SYSTEMD_SERVICE_PATH)) {\n try {\n execFileSync('systemctl', ['--user', 'disable', 'openacp'], { stdio: 'pipe' })\n } catch {\n // may already be disabled\n }\n fs.unlinkSync(SYSTEMD_SERVICE_PATH)\n execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'pipe' })\n log.info('systemd user service removed')\n }\n return { success: true }\n }\n\n return { success: false, error: 'Unsupported platform' }\n } catch (e) {\n const msg = (e as Error).message\n log.error({ err: msg }, 'Failed to uninstall auto-start')\n return { success: false, error: msg }\n }\n}\n\nexport function isAutoStartInstalled(): boolean {\n if (process.platform === 'darwin') {\n return fs.existsSync(LAUNCHD_PLIST_PATH)\n }\n if (process.platform === 'linux') {\n return fs.existsSync(SYSTEMD_SERVICE_PATH)\n }\n return false\n}\n"],"mappings":"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","names":[]}
@@ -1,134 +0,0 @@
1
- import {
2
- DoctorEngine
3
- } from "./chunk-NVPG6JCL.js";
4
- import {
5
- log
6
- } from "./chunk-GAK6PIBW.js";
7
-
8
- // src/adapters/discord/commands/doctor.ts
9
- import {
10
- ActionRowBuilder,
11
- ButtonBuilder,
12
- ButtonStyle
13
- } from "discord.js";
14
- var pendingFixesStore = /* @__PURE__ */ new Map();
15
- function renderReport(report) {
16
- const icons = { pass: "\u2705", warn: "\u26A0\uFE0F", fail: "\u274C" };
17
- const lines = ["\u{1FA7A} **OpenACP Doctor**\n"];
18
- for (const category of report.categories) {
19
- lines.push(`**${category.name}**`);
20
- for (const result of category.results) {
21
- lines.push(` ${icons[result.status]} ${result.message}`);
22
- }
23
- lines.push("");
24
- }
25
- const { passed, warnings, failed, fixed } = report.summary;
26
- const fixedStr = fixed > 0 ? `, ${fixed} fixed` : "";
27
- lines.push(`**Result:** ${passed} passed, ${warnings} warnings, ${failed} failed${fixedStr}`);
28
- const components = [];
29
- if (report.pendingFixes.length > 0) {
30
- const row = new ActionRowBuilder();
31
- for (let i = 0; i < Math.min(report.pendingFixes.length, 5); i++) {
32
- row.addComponents(
33
- new ButtonBuilder().setCustomId(`m:doctor:fix:${i}`).setLabel(`\u{1F527} Fix: ${report.pendingFixes[i].message.slice(0, 30)}`).setStyle(ButtonStyle.Primary)
34
- );
35
- }
36
- components.push(row);
37
- }
38
- return { content: lines.join("\n"), components };
39
- }
40
- async function handleDoctor(interaction, _adapter) {
41
- await interaction.deferReply({ ephemeral: true });
42
- try {
43
- const engine = new DoctorEngine();
44
- const report = await engine.runAll();
45
- const { content, components } = renderReport(report);
46
- const storeKey = `${interaction.guildId}:${interaction.channelId}`;
47
- if (report.pendingFixes.length > 0) {
48
- pendingFixesStore.set(storeKey, report.pendingFixes);
49
- }
50
- await interaction.editReply({ content, components });
51
- } catch (err) {
52
- log.error({ err }, "[discord-doctor] Doctor command failed");
53
- await interaction.editReply(
54
- `\u274C Doctor failed: ${err instanceof Error ? err.message : String(err)}`
55
- );
56
- }
57
- }
58
- async function runDoctorInline(interaction, _adapter) {
59
- try {
60
- const engine = new DoctorEngine();
61
- const report = await engine.runAll();
62
- const { content, components } = renderReport(report);
63
- const storeKey = `${interaction.guildId}:${interaction.channelId}`;
64
- if (report.pendingFixes.length > 0) {
65
- pendingFixesStore.set(storeKey, report.pendingFixes);
66
- }
67
- await interaction.followUp({ content, components, ephemeral: true });
68
- } catch (err) {
69
- log.error({ err }, "[discord-doctor] Doctor inline failed");
70
- await interaction.followUp({
71
- content: `\u274C Doctor failed: ${err instanceof Error ? err.message : String(err)}`,
72
- ephemeral: true
73
- });
74
- }
75
- }
76
- async function handleDoctorButton(interaction, _adapter) {
77
- const { customId } = interaction;
78
- if (customId === "m:doctor") {
79
- try {
80
- await interaction.deferUpdate();
81
- } catch {
82
- }
83
- await runDoctorInline(interaction, _adapter);
84
- return;
85
- }
86
- if (customId.startsWith("m:doctor:fix:")) {
87
- const index = parseInt(customId.replace("m:doctor:fix:", ""), 10);
88
- const storeKey = `${interaction.guildId}:${interaction.channelId}`;
89
- const fixes = pendingFixesStore.get(storeKey);
90
- try {
91
- await interaction.deferUpdate();
92
- } catch {
93
- }
94
- if (!fixes || index < 0 || index >= fixes.length) {
95
- try {
96
- await interaction.followUp({ content: "\u26A0\uFE0F Fix no longer available.", ephemeral: true });
97
- } catch {
98
- }
99
- return;
100
- }
101
- const pending = fixes[index];
102
- try {
103
- const result = await pending.fix();
104
- if (result.success) {
105
- const engine = new DoctorEngine();
106
- const report = await engine.runAll();
107
- const { content, components } = renderReport(report);
108
- if (report.pendingFixes.length > 0) {
109
- pendingFixesStore.set(storeKey, report.pendingFixes);
110
- } else {
111
- pendingFixesStore.delete(storeKey);
112
- }
113
- try {
114
- await interaction.followUp({ content, components, ephemeral: true });
115
- } catch {
116
- }
117
- } else {
118
- try {
119
- await interaction.followUp({ content: `\u274C Fix failed: ${result.message}`, ephemeral: true });
120
- } catch {
121
- }
122
- }
123
- } catch (err) {
124
- log.error({ err, index }, "[discord-doctor] Doctor fix callback failed");
125
- }
126
- }
127
- }
128
-
129
- export {
130
- handleDoctor,
131
- runDoctorInline,
132
- handleDoctorButton
133
- };
134
- //# sourceMappingURL=chunk-2HMQOC7N.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/adapters/discord/commands/doctor.ts"],"sourcesContent":["import {\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n} from 'discord.js'\nimport type { ChatInputCommandInteraction, ButtonInteraction } from 'discord.js'\nimport { DoctorEngine } from '../../../core/doctor/index.js'\nimport type { DoctorReport, PendingFix } from '../../../core/doctor/types.js'\nimport { log } from '../../../core/log.js'\nimport type { DiscordAdapter } from '../adapter.js'\n\n// In-memory store of pending fixes keyed by \"guildId:channelId:messageId\"\nconst pendingFixesStore = new Map<string, PendingFix[]>()\n\nfunction renderReport(report: DoctorReport): {\n content: string\n components: ActionRowBuilder<ButtonBuilder>[]\n} {\n const icons = { pass: '✅', warn: '⚠️', fail: '❌' }\n const lines: string[] = ['🩺 **OpenACP Doctor**\\n']\n\n for (const category of report.categories) {\n lines.push(`**${category.name}**`)\n for (const result of category.results) {\n lines.push(` ${icons[result.status]} ${result.message}`)\n }\n lines.push('')\n }\n\n const { passed, warnings, failed, fixed } = report.summary\n const fixedStr = fixed > 0 ? `, ${fixed} fixed` : ''\n lines.push(`**Result:** ${passed} passed, ${warnings} warnings, ${failed} failed${fixedStr}`)\n\n const components: ActionRowBuilder<ButtonBuilder>[] = []\n\n if (report.pendingFixes.length > 0) {\n const row = new ActionRowBuilder<ButtonBuilder>()\n for (let i = 0; i < Math.min(report.pendingFixes.length, 5); i++) {\n row.addComponents(\n new ButtonBuilder()\n .setCustomId(`m:doctor:fix:${i}`)\n .setLabel(`🔧 Fix: ${report.pendingFixes[i].message.slice(0, 30)}`)\n .setStyle(ButtonStyle.Primary),\n )\n }\n components.push(row)\n }\n\n return { content: lines.join('\\n'), components }\n}\n\nexport async function handleDoctor(\n interaction: ChatInputCommandInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n try {\n const engine = new DoctorEngine()\n const report = await engine.runAll()\n const { content, components } = renderReport(report)\n\n // Store pending fixes for button callbacks\n const storeKey = `${interaction.guildId}:${interaction.channelId}`\n if (report.pendingFixes.length > 0) {\n pendingFixesStore.set(storeKey, report.pendingFixes)\n }\n\n await interaction.editReply({ content, components })\n } catch (err) {\n log.error({ err }, '[discord-doctor] Doctor command failed')\n await interaction.editReply(\n `❌ Doctor failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\nexport async function runDoctorInline(\n interaction: ButtonInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n try {\n const engine = new DoctorEngine()\n const report = await engine.runAll()\n const { content, components } = renderReport(report)\n\n const storeKey = `${interaction.guildId}:${interaction.channelId}`\n if (report.pendingFixes.length > 0) {\n pendingFixesStore.set(storeKey, report.pendingFixes)\n }\n\n await interaction.followUp({ content, components, ephemeral: true })\n } catch (err) {\n log.error({ err }, '[discord-doctor] Doctor inline failed')\n await interaction.followUp({\n content: `❌ Doctor failed: ${err instanceof Error ? err.message : String(err)}`,\n ephemeral: true,\n })\n }\n}\n\nexport async function handleDoctorButton(\n interaction: ButtonInteraction,\n _adapter: DiscordAdapter,\n): Promise<void> {\n const { customId } = interaction\n\n if (customId === 'm:doctor') {\n try { await interaction.deferUpdate() } catch { /* ignore */ }\n await runDoctorInline(interaction, _adapter)\n return\n }\n\n if (customId.startsWith('m:doctor:fix:')) {\n const index = parseInt(customId.replace('m:doctor:fix:', ''), 10)\n const storeKey = `${interaction.guildId}:${interaction.channelId}`\n const fixes = pendingFixesStore.get(storeKey)\n\n try { await interaction.deferUpdate() } catch { /* ignore */ }\n\n if (!fixes || index < 0 || index >= fixes.length) {\n try { await interaction.followUp({ content: '⚠️ Fix no longer available.', ephemeral: true }) } catch { /* */ }\n return\n }\n\n const pending = fixes[index]\n try {\n const result = await pending.fix()\n if (result.success) {\n // Re-run doctor to show updated status\n const engine = new DoctorEngine()\n const report = await engine.runAll()\n const { content, components } = renderReport(report)\n\n if (report.pendingFixes.length > 0) {\n pendingFixesStore.set(storeKey, report.pendingFixes)\n } else {\n pendingFixesStore.delete(storeKey)\n }\n\n try { await interaction.followUp({ content, components, ephemeral: true }) } catch { /* ignore */ }\n } else {\n try { await interaction.followUp({ content: `❌ Fix failed: ${result.message}`, ephemeral: true }) } catch { /* */ }\n }\n } catch (err) {\n log.error({ err, index }, '[discord-doctor] Doctor fix callback failed')\n }\n }\n}\n"],"mappings":"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","names":[]}