@chat-adapter/github 4.8.0 → 4.9.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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { extractCard, ValidationError } from \"@chat-adapter/shared\";\nimport { createAppAuth } from \"@octokit/auth-app\";\nimport { Octokit } from \"@octokit/rest\";\nimport type {\n Adapter,\n AdapterPostableMessage,\n Author,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FormattedContent,\n Logger,\n RawMessage,\n ThreadInfo,\n WebhookOptions,\n} from \"chat\";\nimport { convertEmojiPlaceholders, Message } from \"chat\";\nimport { cardToGitHubMarkdown } from \"./cards\";\nimport { GitHubFormatConverter } from \"./markdown\";\nimport type {\n GitHubAdapterConfig,\n GitHubIssueComment,\n GitHubRawMessage,\n GitHubReactionContent,\n GitHubReviewComment,\n GitHubThreadId,\n GitHubUser,\n IssueCommentWebhookPayload,\n PullRequestReviewCommentWebhookPayload,\n} from \"./types\";\n\n// Re-export types\nexport type {\n GitHubAdapterAppConfig,\n GitHubAdapterConfig,\n GitHubAdapterMultiTenantAppConfig,\n GitHubAdapterPATConfig,\n GitHubRawMessage,\n GitHubThreadId,\n} from \"./types\";\n\n/**\n * GitHub adapter for chat SDK.\n *\n * Supports both PR-level comments (Conversation tab) and review comment threads\n * (Files Changed tab - line-specific).\n *\n * @example Single-tenant (your own org)\n * ```typescript\n * import { Chat } from \"chat\";\n * import { GitHubAdapter } from \"@chat-adapter/github\";\n * import { MemoryState } from \"@chat-adapter/state-memory\";\n *\n * const chat = new Chat({\n * userName: \"my-bot\",\n * adapters: {\n * github: new GitHubAdapter({\n * token: process.env.GITHUB_TOKEN!,\n * webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,\n * userName: \"my-bot\",\n * logger: console,\n * }),\n * },\n * state: new MemoryState(),\n * logger: \"info\",\n * });\n * ```\n *\n * @example Multi-tenant (public app anyone can install)\n * ```typescript\n * const chat = new Chat({\n * userName: \"my-bot[bot]\",\n * adapters: {\n * github: new GitHubAdapter({\n * appId: process.env.GITHUB_APP_ID!,\n * privateKey: process.env.GITHUB_PRIVATE_KEY!,\n * // No installationId - automatically extracted from webhooks\n * webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,\n * userName: \"my-bot[bot]\",\n * logger: console,\n * }),\n * },\n * state: new MemoryState(),\n * logger: \"info\",\n * });\n * ```\n */\nexport class GitHubAdapter\n implements Adapter<GitHubThreadId, GitHubRawMessage>\n{\n readonly name = \"github\";\n readonly userName: string;\n\n // Single Octokit instance for PAT or single-tenant app mode\n private octokit: Octokit | null = null;\n // App credentials for multi-tenant mode\n private appCredentials: { appId: string; privateKey: string } | null = null;\n // Cache of Octokit instances per installation (for multi-tenant)\n private installationClients = new Map<number, Octokit>();\n\n private webhookSecret: string;\n private chat: ChatInstance | null = null;\n private logger: Logger;\n private _botUserId: number | null = null;\n private formatConverter = new GitHubFormatConverter();\n\n /** Bot user ID (numeric) used for self-message detection */\n get botUserId(): string | undefined {\n return this._botUserId?.toString();\n }\n\n /** Whether this adapter is in multi-tenant mode (no fixed installation ID) */\n get isMultiTenant(): boolean {\n return this.appCredentials !== null && this.octokit === null;\n }\n\n constructor(config: GitHubAdapterConfig) {\n this.webhookSecret = config.webhookSecret;\n this.logger = config.logger;\n this.userName = config.userName;\n this._botUserId = config.botUserId ?? null;\n\n // Create Octokit instance based on auth method\n if (\"token\" in config && config.token) {\n // PAT mode - single Octokit instance\n this.octokit = new Octokit({ auth: config.token });\n } else if (\"appId\" in config && config.appId) {\n if (\"installationId\" in config && config.installationId) {\n // Single-tenant app mode - fixed installation\n this.octokit = new Octokit({\n authStrategy: createAppAuth,\n auth: {\n appId: config.appId,\n privateKey: config.privateKey,\n installationId: config.installationId,\n },\n });\n } else {\n // Multi-tenant app mode - create clients per installation\n this.appCredentials = {\n appId: config.appId,\n privateKey: config.privateKey,\n };\n this.logger.info(\n \"GitHub adapter initialized in multi-tenant mode (installation ID will be extracted from webhooks)\",\n );\n }\n } else {\n throw new Error(\n \"GitHubAdapter requires either token or appId/privateKey\",\n );\n }\n }\n\n /**\n * Get or create an Octokit instance for a specific installation.\n * For single-tenant mode, returns the single instance.\n * For multi-tenant mode, creates/caches instances per installation.\n */\n private getOctokit(installationId?: number): Octokit {\n // Single-tenant mode - return the single instance\n if (this.octokit) {\n return this.octokit;\n }\n\n // Multi-tenant mode - need an installation ID\n if (!this.appCredentials) {\n throw new Error(\"Adapter not properly configured\");\n }\n\n if (!installationId) {\n throw new Error(\n \"Installation ID required for multi-tenant mode. \" +\n \"This usually means you're trying to make an API call outside of a webhook context. \" +\n \"For proactive messages, use thread IDs from previous webhook interactions.\",\n );\n }\n\n // Check cache\n let client = this.installationClients.get(installationId);\n if (!client) {\n // Create new client for this installation\n client = new Octokit({\n authStrategy: createAppAuth,\n auth: {\n appId: this.appCredentials.appId,\n privateKey: this.appCredentials.privateKey,\n installationId,\n },\n });\n this.installationClients.set(installationId, client);\n this.logger.debug(\"Created Octokit client for installation\", {\n installationId,\n });\n }\n\n return client;\n }\n\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n\n // Fetch bot user ID if not provided (only works for single-tenant or PAT mode)\n if (!this._botUserId && this.octokit) {\n try {\n const { data: user } = await this.octokit.users.getAuthenticated();\n this._botUserId = user.id;\n this.logger.info(\"GitHub auth completed\", {\n botUserId: this._botUserId,\n login: user.login,\n });\n } catch (error) {\n this.logger.warn(\"Could not fetch bot user ID\", { error });\n }\n }\n }\n\n /**\n * Get the state key for storing installation ID for a repository.\n */\n private getInstallationKey(owner: string, repo: string): string {\n return `github:install:${owner}/${repo}`;\n }\n\n /**\n * Store the installation ID for a repository (for multi-tenant mode).\n */\n private async storeInstallationId(\n owner: string,\n repo: string,\n installationId: number,\n ): Promise<void> {\n if (!this.chat || !this.isMultiTenant) return;\n\n const key = this.getInstallationKey(owner, repo);\n await this.chat.getState().set(key, installationId);\n this.logger.debug(\"Stored installation ID\", {\n owner,\n repo,\n installationId,\n });\n }\n\n /**\n * Get the installation ID for a repository (for multi-tenant mode).\n */\n private async getInstallationId(\n owner: string,\n repo: string,\n ): Promise<number | undefined> {\n if (!this.chat || !this.isMultiTenant) return undefined;\n\n const key = this.getInstallationKey(owner, repo);\n return (await this.chat.getState().get<number>(key)) ?? undefined;\n }\n\n /**\n * Handle incoming webhook from GitHub.\n */\n async handleWebhook(\n request: Request,\n options?: WebhookOptions,\n ): Promise<Response> {\n const body = await request.text();\n this.logger.debug(\"GitHub webhook raw body\", {\n body: body.substring(0, 500),\n });\n\n // Verify request signature\n const signature = request.headers.get(\"x-hub-signature-256\");\n if (!this.verifySignature(body, signature)) {\n return new Response(\"Invalid signature\", { status: 401 });\n }\n\n // Get event type from header\n const eventType = request.headers.get(\"x-github-event\");\n this.logger.debug(\"GitHub webhook event type\", { eventType });\n\n // Handle ping event (webhook verification)\n if (eventType === \"ping\") {\n this.logger.info(\"GitHub webhook ping received\");\n return new Response(\"pong\", { status: 200 });\n }\n\n // Parse the JSON payload\n let payload:\n | IssueCommentWebhookPayload\n | PullRequestReviewCommentWebhookPayload;\n try {\n payload = JSON.parse(body);\n } catch {\n this.logger.error(\"GitHub webhook invalid JSON\", {\n contentType: request.headers.get(\"content-type\"),\n bodyPreview: body.substring(0, 200),\n });\n return new Response(\n \"Invalid JSON. Make sure webhook Content-Type is set to application/json\",\n { status: 400 },\n );\n }\n\n // Extract and store installation ID for multi-tenant mode\n const installationId = (payload as { installation?: { id: number } })\n .installation?.id;\n if (installationId && this.isMultiTenant) {\n const repo = payload.repository;\n await this.storeInstallationId(\n repo.owner.login,\n repo.name,\n installationId,\n );\n }\n\n // Handle events\n if (eventType === \"issue_comment\") {\n const issuePayload = payload as IssueCommentWebhookPayload;\n // Only process comments on PRs (they have a pull_request field)\n if (\n issuePayload.action === \"created\" &&\n issuePayload.issue.pull_request\n ) {\n this.handleIssueComment(issuePayload, installationId, options);\n }\n } else if (eventType === \"pull_request_review_comment\") {\n const reviewPayload = payload as PullRequestReviewCommentWebhookPayload;\n if (reviewPayload.action === \"created\") {\n this.handleReviewComment(reviewPayload, installationId, options);\n }\n }\n\n return new Response(\"ok\", { status: 200 });\n }\n\n /**\n * Verify GitHub webhook signature using HMAC-SHA256.\n */\n private verifySignature(body: string, signature: string | null): boolean {\n if (!signature) {\n return false;\n }\n\n // GitHub signature format: sha256=<hex>\n const expected = `sha256=${createHmac(\"sha256\", this.webhookSecret)\n .update(body)\n .digest(\"hex\")}`;\n\n // Use timing-safe comparison\n try {\n return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n\n /**\n * Handle issue_comment webhook (PR-level comments in Conversation tab).\n */\n private handleIssueComment(\n payload: IssueCommentWebhookPayload,\n _installationId: number | undefined,\n options?: WebhookOptions,\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring comment\");\n return;\n }\n\n const { comment, issue, repository, sender } = payload;\n\n // Build thread ID (PR-level)\n const threadId = this.encodeThreadId({\n owner: repository.owner.login,\n repo: repository.name,\n prNumber: issue.number,\n });\n\n // Build message\n const message = this.parseIssueComment(\n comment,\n repository,\n issue.number,\n threadId,\n );\n\n // Check if this is from the bot itself\n if (sender.id === this._botUserId) {\n this.logger.debug(\"Ignoring message from self\", {\n messageId: comment.id,\n });\n return;\n }\n\n this.chat.processMessage(this, threadId, message, options);\n }\n\n /**\n * Handle pull_request_review_comment webhook (line-specific comments).\n */\n private handleReviewComment(\n payload: PullRequestReviewCommentWebhookPayload,\n _installationId: number | undefined,\n options?: WebhookOptions,\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring comment\");\n return;\n }\n\n const { comment, pull_request, repository, sender } = payload;\n\n // Determine root comment ID for thread\n // If in_reply_to_id exists, use that (this is a reply in existing thread)\n // Otherwise, this comment is the root of a new thread\n const rootCommentId = comment.in_reply_to_id ?? comment.id;\n\n // Build thread ID (review comment thread)\n const threadId = this.encodeThreadId({\n owner: repository.owner.login,\n repo: repository.name,\n prNumber: pull_request.number,\n reviewCommentId: rootCommentId,\n });\n\n // Build message\n const message = this.parseReviewComment(\n comment,\n repository,\n pull_request.number,\n threadId,\n );\n\n // Check if this is from the bot itself\n if (sender.id === this._botUserId) {\n this.logger.debug(\"Ignoring message from self\", {\n messageId: comment.id,\n });\n return;\n }\n\n this.chat.processMessage(this, threadId, message, options);\n }\n\n /**\n * Parse an issue comment into a normalized Message.\n */\n private parseIssueComment(\n comment: GitHubIssueComment,\n repository: { owner: GitHubUser; name: string },\n prNumber: number,\n threadId: string,\n ): Message<GitHubRawMessage> {\n const author = this.parseAuthor(comment.user);\n\n return new Message({\n id: comment.id.toString(),\n threadId,\n text: this.formatConverter.extractPlainText(comment.body),\n formatted: this.formatConverter.toAst(comment.body),\n raw: {\n type: \"issue_comment\",\n comment,\n repository: {\n id: 0, // Not needed for raw storage\n name: repository.name,\n full_name: `${repository.owner.login}/${repository.name}`,\n owner: repository.owner,\n },\n prNumber,\n },\n author,\n metadata: {\n dateSent: new Date(comment.created_at),\n edited: comment.created_at !== comment.updated_at,\n editedAt:\n comment.created_at !== comment.updated_at\n ? new Date(comment.updated_at)\n : undefined,\n },\n attachments: [],\n });\n }\n\n /**\n * Parse a review comment into a normalized Message.\n */\n private parseReviewComment(\n comment: GitHubReviewComment,\n repository: { owner: GitHubUser; name: string },\n prNumber: number,\n threadId: string,\n ): Message<GitHubRawMessage> {\n const author = this.parseAuthor(comment.user);\n\n return new Message({\n id: comment.id.toString(),\n threadId,\n text: this.formatConverter.extractPlainText(comment.body),\n formatted: this.formatConverter.toAst(comment.body),\n raw: {\n type: \"review_comment\",\n comment,\n repository: {\n id: 0,\n name: repository.name,\n full_name: `${repository.owner.login}/${repository.name}`,\n owner: repository.owner,\n },\n prNumber,\n },\n author,\n metadata: {\n dateSent: new Date(comment.created_at),\n edited: comment.created_at !== comment.updated_at,\n editedAt:\n comment.created_at !== comment.updated_at\n ? new Date(comment.updated_at)\n : undefined,\n },\n attachments: [],\n });\n }\n\n /**\n * Parse a GitHub user into an Author.\n */\n private parseAuthor(user: GitHubUser): Author {\n return {\n userId: user.id.toString(),\n userName: user.login,\n fullName: user.login, // GitHub doesn't always expose real names\n isBot: user.type === \"Bot\",\n isMe: user.id === this._botUserId,\n };\n }\n\n /**\n * Get the Octokit client for a specific thread.\n * In multi-tenant mode, looks up the installation ID from state.\n */\n private async getOctokitForThread(\n owner: string,\n repo: string,\n ): Promise<Octokit> {\n const installationId = await this.getInstallationId(owner, repo);\n return this.getOctokit(installationId);\n }\n\n /**\n * Post a message to a thread.\n */\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<GitHubRawMessage>> {\n const { owner, repo, prNumber, reviewCommentId } =\n this.decodeThreadId(threadId);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n // Render message to GitHub markdown\n let body: string;\n const card = extractCard(message);\n if (card) {\n body = cardToGitHubMarkdown(card);\n } else {\n body = this.formatConverter.renderPostable(message);\n }\n\n // Convert emoji placeholders to unicode\n body = convertEmojiPlaceholders(body, \"github\");\n\n if (reviewCommentId) {\n // Review comment thread - reply with in_reply_to\n const { data: comment } = await octokit.pulls.createReplyForReviewComment(\n {\n owner,\n repo,\n pull_number: prNumber,\n comment_id: reviewCommentId,\n body,\n },\n );\n\n return {\n id: comment.id.toString(),\n threadId,\n raw: {\n type: \"review_comment\",\n comment: comment as GitHubReviewComment,\n repository: {\n id: 0,\n name: repo,\n full_name: `${owner}/${repo}`,\n owner: { id: 0, login: owner, type: \"User\" },\n },\n prNumber,\n },\n };\n } else {\n // PR-level thread - issue comment\n const { data: comment } = await octokit.issues.createComment({\n owner,\n repo,\n issue_number: prNumber,\n body,\n });\n\n return {\n id: comment.id.toString(),\n threadId,\n raw: {\n type: \"issue_comment\",\n comment: comment as GitHubIssueComment,\n repository: {\n id: 0,\n name: repo,\n full_name: `${owner}/${repo}`,\n owner: { id: 0, login: owner, type: \"User\" },\n },\n prNumber,\n },\n };\n }\n }\n\n /**\n * Edit an existing message.\n */\n async editMessage(\n threadId: string,\n messageId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<GitHubRawMessage>> {\n const { owner, repo, prNumber, reviewCommentId } =\n this.decodeThreadId(threadId);\n const commentId = parseInt(messageId, 10);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n // Render message to GitHub markdown\n let body: string;\n const card = extractCard(message);\n if (card) {\n body = cardToGitHubMarkdown(card);\n } else {\n body = this.formatConverter.renderPostable(message);\n }\n\n // Convert emoji placeholders to unicode\n body = convertEmojiPlaceholders(body, \"github\");\n\n if (reviewCommentId) {\n // Review comment\n const { data: comment } = await octokit.pulls.updateReviewComment({\n owner,\n repo,\n comment_id: commentId,\n body,\n });\n\n return {\n id: comment.id.toString(),\n threadId,\n raw: {\n type: \"review_comment\",\n comment: comment as GitHubReviewComment,\n repository: {\n id: 0,\n name: repo,\n full_name: `${owner}/${repo}`,\n owner: { id: 0, login: owner, type: \"User\" },\n },\n prNumber,\n },\n };\n } else {\n // Issue comment\n const { data: comment } = await octokit.issues.updateComment({\n owner,\n repo,\n comment_id: commentId,\n body,\n });\n\n return {\n id: comment.id.toString(),\n threadId,\n raw: {\n type: \"issue_comment\",\n comment: comment as GitHubIssueComment,\n repository: {\n id: 0,\n name: repo,\n full_name: `${owner}/${repo}`,\n owner: { id: 0, login: owner, type: \"User\" },\n },\n prNumber,\n },\n };\n }\n }\n\n /**\n * Delete a message.\n */\n async deleteMessage(threadId: string, messageId: string): Promise<void> {\n const { owner, repo, reviewCommentId } = this.decodeThreadId(threadId);\n const commentId = parseInt(messageId, 10);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n if (reviewCommentId) {\n await octokit.pulls.deleteReviewComment({\n owner,\n repo,\n comment_id: commentId,\n });\n } else {\n await octokit.issues.deleteComment({\n owner,\n repo,\n comment_id: commentId,\n });\n }\n }\n\n /**\n * Add a reaction to a message.\n */\n async addReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string,\n ): Promise<void> {\n const { owner, repo, reviewCommentId } = this.decodeThreadId(threadId);\n const commentId = parseInt(messageId, 10);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n // Convert emoji to GitHub reaction content\n const content = this.emojiToGitHubReaction(emoji);\n\n if (reviewCommentId) {\n await octokit.reactions.createForPullRequestReviewComment({\n owner,\n repo,\n comment_id: commentId,\n content,\n });\n } else {\n await octokit.reactions.createForIssueComment({\n owner,\n repo,\n comment_id: commentId,\n content,\n });\n }\n }\n\n /**\n * Remove a reaction from a message.\n */\n async removeReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string,\n ): Promise<void> {\n const { owner, repo, reviewCommentId } = this.decodeThreadId(threadId);\n const commentId = parseInt(messageId, 10);\n const content = this.emojiToGitHubReaction(emoji);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n // List reactions to find the one to delete\n const reactions = reviewCommentId\n ? (\n await octokit.reactions.listForPullRequestReviewComment({\n owner,\n repo,\n comment_id: commentId,\n })\n ).data\n : (\n await octokit.reactions.listForIssueComment({\n owner,\n repo,\n comment_id: commentId,\n })\n ).data;\n\n // Find the bot's reaction with matching content\n const reaction = reactions.find(\n (r) => r.content === content && r.user?.id === this._botUserId,\n );\n\n if (reaction) {\n if (reviewCommentId) {\n await octokit.reactions.deleteForPullRequestComment({\n owner,\n repo,\n comment_id: commentId,\n reaction_id: reaction.id,\n });\n } else {\n await octokit.reactions.deleteForIssueComment({\n owner,\n repo,\n comment_id: commentId,\n reaction_id: reaction.id,\n });\n }\n }\n }\n\n /**\n * Convert SDK emoji to GitHub reaction content.\n */\n private emojiToGitHubReaction(\n emoji: EmojiValue | string,\n ): GitHubReactionContent {\n const emojiName = typeof emoji === \"string\" ? emoji : emoji.name;\n\n // Map common emoji names to GitHub reactions\n const mapping: Record<string, GitHubReactionContent> = {\n thumbs_up: \"+1\",\n \"+1\": \"+1\",\n thumbs_down: \"-1\",\n \"-1\": \"-1\",\n laugh: \"laugh\",\n smile: \"laugh\",\n confused: \"confused\",\n thinking: \"confused\",\n heart: \"heart\",\n love_eyes: \"heart\",\n hooray: \"hooray\",\n party: \"hooray\",\n confetti: \"hooray\",\n rocket: \"rocket\",\n eyes: \"eyes\",\n };\n\n return mapping[emojiName] || \"+1\";\n }\n\n /**\n * Show typing indicator (no-op for GitHub).\n */\n async startTyping(_threadId: string): Promise<void> {\n // GitHub doesn't support typing indicators\n }\n\n /**\n * Fetch messages from a thread.\n */\n async fetchMessages(\n threadId: string,\n options?: FetchOptions,\n ): Promise<FetchResult<GitHubRawMessage>> {\n const { owner, repo, prNumber, reviewCommentId } =\n this.decodeThreadId(threadId);\n const limit = options?.limit ?? 100;\n const direction = options?.direction ?? \"backward\";\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n let messages: Message<GitHubRawMessage>[];\n\n if (reviewCommentId) {\n // Fetch review comments for the PR and filter by thread\n const { data: allComments } = await octokit.pulls.listReviewComments({\n owner,\n repo,\n pull_number: prNumber,\n per_page: 100, // Fetch more to filter\n });\n\n // Filter to comments in this thread (same in_reply_to_id or the root itself)\n const threadComments = allComments.filter(\n (c) => c.id === reviewCommentId || c.in_reply_to_id === reviewCommentId,\n );\n\n messages = threadComments.map((comment) =>\n this.parseReviewComment(\n comment as GitHubReviewComment,\n {\n owner: { id: 0, login: owner, type: \"User\", avatar_url: \"\" },\n name: repo,\n },\n prNumber,\n threadId,\n ),\n );\n } else {\n // Fetch issue comments\n const { data: comments } = await octokit.issues.listComments({\n owner,\n repo,\n issue_number: prNumber,\n per_page: limit,\n });\n\n messages = comments.map((comment) =>\n this.parseIssueComment(\n comment as GitHubIssueComment,\n {\n owner: { id: 0, login: owner, type: \"User\", avatar_url: \"\" },\n name: repo,\n },\n prNumber,\n threadId,\n ),\n );\n }\n\n // Sort chronologically (oldest first)\n messages.sort(\n (a, b) => a.metadata.dateSent.getTime() - b.metadata.dateSent.getTime(),\n );\n\n // For backward direction, take the last N messages\n if (direction === \"backward\" && messages.length > limit) {\n messages = messages.slice(-limit);\n } else if (direction === \"forward\" && messages.length > limit) {\n messages = messages.slice(0, limit);\n }\n\n return {\n messages,\n nextCursor: undefined, // Simplified pagination for now\n };\n }\n\n /**\n * Fetch thread metadata.\n */\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { owner, repo, prNumber, reviewCommentId } =\n this.decodeThreadId(threadId);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n const { data: pr } = await octokit.pulls.get({\n owner,\n repo,\n pull_number: prNumber,\n });\n\n return {\n id: threadId,\n channelId: `${owner}/${repo}`,\n channelName: `${repo} #${prNumber}`,\n isDM: false,\n metadata: {\n owner,\n repo,\n prNumber,\n prTitle: pr.title,\n prState: pr.state,\n reviewCommentId,\n },\n };\n }\n\n /**\n * Encode platform data into a thread ID string.\n *\n * Thread ID formats:\n * - PR-level: `github:{owner}/{repo}:{prNumber}`\n * - Review comment: `github:{owner}/{repo}:{prNumber}:rc:{reviewCommentId}`\n */\n encodeThreadId(platformData: GitHubThreadId): string {\n const { owner, repo, prNumber, reviewCommentId } = platformData;\n\n if (reviewCommentId) {\n return `github:${owner}/${repo}:${prNumber}:rc:${reviewCommentId}`;\n }\n return `github:${owner}/${repo}:${prNumber}`;\n }\n\n /**\n * Decode thread ID string back to platform data.\n */\n decodeThreadId(threadId: string): GitHubThreadId {\n if (!threadId.startsWith(\"github:\")) {\n throw new ValidationError(\n \"github\",\n `Invalid GitHub thread ID: ${threadId}`,\n );\n }\n\n const withoutPrefix = threadId.slice(7); // Remove \"github:\"\n\n // Check for review comment thread format\n const rcMatch = withoutPrefix.match(/^([^/]+)\\/([^:]+):(\\d+):rc:(\\d+)$/);\n if (rcMatch) {\n return {\n owner: rcMatch[1],\n repo: rcMatch[2],\n prNumber: parseInt(rcMatch[3], 10),\n reviewCommentId: parseInt(rcMatch[4], 10),\n };\n }\n\n // PR-level thread format\n const prMatch = withoutPrefix.match(/^([^/]+)\\/([^:]+):(\\d+)$/);\n if (prMatch) {\n return {\n owner: prMatch[1],\n repo: prMatch[2],\n prNumber: parseInt(prMatch[3], 10),\n };\n }\n\n throw new ValidationError(\n \"github\",\n `Invalid GitHub thread ID format: ${threadId}`,\n );\n }\n\n /**\n * Parse a raw message into normalized format.\n */\n parseMessage(raw: GitHubRawMessage): Message<GitHubRawMessage> {\n if (raw.type === \"issue_comment\") {\n const threadId = this.encodeThreadId({\n owner: raw.repository.owner.login,\n repo: raw.repository.name,\n prNumber: raw.prNumber,\n });\n return this.parseIssueComment(\n raw.comment,\n { owner: raw.repository.owner, name: raw.repository.name },\n raw.prNumber,\n threadId,\n );\n } else {\n const rootCommentId = raw.comment.in_reply_to_id ?? raw.comment.id;\n const threadId = this.encodeThreadId({\n owner: raw.repository.owner.login,\n repo: raw.repository.name,\n prNumber: raw.prNumber,\n reviewCommentId: rootCommentId,\n });\n return this.parseReviewComment(\n raw.comment,\n { owner: raw.repository.owner, name: raw.repository.name },\n raw.prNumber,\n threadId,\n );\n }\n }\n\n /**\n * Render formatted content to GitHub markdown.\n */\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n}\n\n/**\n * Create a new GitHub adapter instance.\n *\n * @example\n * ```typescript\n * const chat = new Chat({\n * adapters: {\n * github: createGitHubAdapter({\n * token: process.env.GITHUB_TOKEN!,\n * webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,\n * userName: \"my-bot\",\n * logger: console,\n * }),\n * },\n * });\n * ```\n */\nexport function createGitHubAdapter(\n config: GitHubAdapterConfig,\n): GitHubAdapter {\n return new GitHubAdapter(config);\n}\n","/**\n * Convert CardElement to GitHub-flavored markdown.\n *\n * Since GitHub doesn't support rich cards natively, we render cards\n * as formatted markdown with bold text, dividers, and links.\n */\n\nimport type {\n ActionsElement,\n CardChild,\n CardElement,\n FieldsElement,\n TextElement,\n} from \"chat\";\n\n/**\n * Convert a CardElement to GitHub-flavored markdown.\n *\n * Cards are rendered as clean markdown with:\n * - Bold title and subtitle\n * - Text content\n * - Fields as key-value pairs\n * - Buttons as markdown links (action buttons become bold text since GitHub has no interactivity)\n *\n * @example\n * ```typescript\n * const card = Card({\n * title: \"Order #1234\",\n * subtitle: \"Status update\",\n * children: [\n * Text(\"Your order has been shipped!\"),\n * Fields([\n * Field({ label: \"Tracking\", value: \"ABC123\" }),\n * ]),\n * Actions([\n * LinkButton({ url: \"https://track.example.com\", label: \"Track Order\" }),\n * ]),\n * ],\n * });\n *\n * // Output:\n * // **Order #1234**\n * // Status update\n * //\n * // Your order has been shipped!\n * //\n * // **Tracking:** ABC123\n * //\n * // [Track Order](https://track.example.com)\n * ```\n */\nexport function cardToGitHubMarkdown(card: CardElement): string {\n const lines: string[] = [];\n\n // Title (bold)\n if (card.title) {\n lines.push(`**${escapeMarkdown(card.title)}**`);\n }\n\n // Subtitle\n if (card.subtitle) {\n lines.push(escapeMarkdown(card.subtitle));\n }\n\n // Add spacing after header if there are children\n if ((card.title || card.subtitle) && card.children.length > 0) {\n lines.push(\"\");\n }\n\n // Header image\n if (card.imageUrl) {\n lines.push(`![](${card.imageUrl})`);\n lines.push(\"\");\n }\n\n // Children\n for (let i = 0; i < card.children.length; i++) {\n const child = card.children[i];\n const childLines = renderChild(child);\n\n if (childLines.length > 0) {\n lines.push(...childLines);\n\n // Add spacing between children (except last)\n if (i < card.children.length - 1) {\n lines.push(\"\");\n }\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Render a card child element to markdown lines.\n */\nfunction renderChild(child: CardChild): string[] {\n switch (child.type) {\n case \"text\":\n return renderText(child);\n\n case \"fields\":\n return renderFields(child);\n\n case \"actions\":\n return renderActions(child);\n\n case \"section\":\n // Flatten section children\n return child.children.flatMap(renderChild);\n\n case \"image\":\n if (child.alt) {\n return [`![${escapeMarkdown(child.alt)}](${child.url})`];\n }\n return [`![](${child.url})`];\n\n case \"divider\":\n return [\"---\"];\n\n default:\n return [];\n }\n}\n\n/**\n * Render text element.\n */\nfunction renderText(text: TextElement): string[] {\n const content = text.content;\n\n switch (text.style) {\n case \"bold\":\n return [`**${content}**`];\n case \"muted\":\n // Use italic for muted text\n return [`_${content}_`];\n default:\n return [content];\n }\n}\n\n/**\n * Render fields as key-value pairs.\n */\nfunction renderFields(fields: FieldsElement): string[] {\n return fields.children.map(\n (field) =>\n `**${escapeMarkdown(field.label)}:** ${escapeMarkdown(field.value)}`,\n );\n}\n\n/**\n * Render actions (buttons) as markdown links or bold text.\n */\nfunction renderActions(actions: ActionsElement): string[] {\n const buttonTexts = actions.children.map((button) => {\n if (button.type === \"link-button\") {\n // Link buttons become markdown links\n return `[${escapeMarkdown(button.label)}](${button.url})`;\n }\n // Action buttons become bold text (no interactivity in GitHub comments)\n // We could potentially use a special format that the bot recognizes\n return `**[${escapeMarkdown(button.label)}]**`;\n });\n\n // Join buttons with separator\n return [buttonTexts.join(\" • \")];\n}\n\n/**\n * Escape special markdown characters in text.\n */\nfunction escapeMarkdown(text: string): string {\n // Only escape characters that could break the formatting\n // We're deliberately light-handed to preserve intentional markdown\n return text\n .replace(/\\*/g, \"\\\\*\")\n .replace(/_/g, \"\\\\_\")\n .replace(/\\[/g, \"\\\\[\")\n .replace(/\\]/g, \"\\\\]\");\n}\n\n/**\n * Generate plain text fallback from a card (no markdown).\n * Used for alt text or plain text contexts.\n */\nexport function cardToPlainText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(card.title);\n }\n\n if (card.subtitle) {\n parts.push(card.subtitle);\n }\n\n for (const child of card.children) {\n const text = childToPlainText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Convert card child to plain text.\n */\nfunction childToPlainText(child: CardChild): string | null {\n switch (child.type) {\n case \"text\":\n return child.content;\n case \"fields\":\n return child.children.map((f) => `${f.label}: ${f.value}`).join(\"\\n\");\n case \"actions\":\n return child.children.map((b) => `[${b.label}]`).join(\" \");\n case \"section\":\n return child.children.map(childToPlainText).filter(Boolean).join(\"\\n\");\n default:\n return null;\n }\n}\n","/**\n * GitHub-specific format conversion using AST-based parsing.\n *\n * GitHub uses GitHub Flavored Markdown (GFM) which is very close to standard markdown.\n * This converter primarily passes through standard markdown, with special handling for:\n * - @mentions (user references)\n * - #refs (issue/PR references)\n * - SHA references (commit links)\n */\n\nimport {\n type AdapterPostableMessage,\n BaseFormatConverter,\n parseMarkdown,\n type Root,\n stringifyMarkdown,\n} from \"chat\";\n\nexport class GitHubFormatConverter extends BaseFormatConverter {\n /**\n * GitHub uses standard GFM, so we can use remark-stringify directly.\n * We just need to ensure @mentions are preserved.\n */\n fromAst(ast: Root): string {\n // Use standard markdown stringification\n // remark-stringify handles GFM well\n return stringifyMarkdown(ast).trim();\n }\n\n /**\n * Parse GitHub markdown into an AST.\n * GitHub uses standard GFM, so we use the standard parser.\n */\n toAst(markdown: string): Root {\n return parseMarkdown(markdown);\n }\n\n /**\n * Override renderPostable to handle @mentions in plain strings.\n * GitHub @mentions are already in the correct format (@username).\n */\n override renderPostable(message: AdapterPostableMessage): string {\n if (typeof message === \"string\") {\n return message;\n }\n if (\"raw\" in message) {\n return message.raw;\n }\n if (\"markdown\" in message) {\n return this.fromMarkdown(message.markdown);\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n // Handle cards via base class\n return super.renderPostable(message);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { extractCard, ValidationError } from \"@chat-adapter/shared\";\nimport { createAppAuth } from \"@octokit/auth-app\";\nimport { Octokit } from \"@octokit/rest\";\nimport type {\n Adapter,\n AdapterPostableMessage,\n Author,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FormattedContent,\n Logger,\n RawMessage,\n ThreadInfo,\n WebhookOptions,\n} from \"chat\";\nimport { convertEmojiPlaceholders, Message } from \"chat\";\nimport { cardToGitHubMarkdown } from \"./cards\";\nimport { GitHubFormatConverter } from \"./markdown\";\nimport type {\n GitHubAdapterConfig,\n GitHubIssueComment,\n GitHubRawMessage,\n GitHubReactionContent,\n GitHubReviewComment,\n GitHubThreadId,\n GitHubUser,\n IssueCommentWebhookPayload,\n PullRequestReviewCommentWebhookPayload,\n} from \"./types\";\n\n// Re-export types\nexport type {\n GitHubAdapterAppConfig,\n GitHubAdapterConfig,\n GitHubAdapterMultiTenantAppConfig,\n GitHubAdapterPATConfig,\n GitHubRawMessage,\n GitHubThreadId,\n} from \"./types\";\n\n/**\n * GitHub adapter for chat SDK.\n *\n * Supports both PR-level comments (Conversation tab) and review comment threads\n * (Files Changed tab - line-specific).\n *\n * @example Single-tenant (your own org)\n * ```typescript\n * import { Chat } from \"chat\";\n * import { GitHubAdapter } from \"@chat-adapter/github\";\n * import { MemoryState } from \"@chat-adapter/state-memory\";\n *\n * const chat = new Chat({\n * userName: \"my-bot\",\n * adapters: {\n * github: new GitHubAdapter({\n * token: process.env.GITHUB_TOKEN!,\n * webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,\n * userName: \"my-bot\",\n * logger: console,\n * }),\n * },\n * state: new MemoryState(),\n * logger: \"info\",\n * });\n * ```\n *\n * @example Multi-tenant (public app anyone can install)\n * ```typescript\n * const chat = new Chat({\n * userName: \"my-bot[bot]\",\n * adapters: {\n * github: new GitHubAdapter({\n * appId: process.env.GITHUB_APP_ID!,\n * privateKey: process.env.GITHUB_PRIVATE_KEY!,\n * // No installationId - automatically extracted from webhooks\n * webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,\n * userName: \"my-bot[bot]\",\n * logger: console,\n * }),\n * },\n * state: new MemoryState(),\n * logger: \"info\",\n * });\n * ```\n */\nexport class GitHubAdapter\n implements Adapter<GitHubThreadId, GitHubRawMessage>\n{\n readonly name = \"github\";\n readonly userName: string;\n\n // Single Octokit instance for PAT or single-tenant app mode\n private octokit: Octokit | null = null;\n // App credentials for multi-tenant mode\n private appCredentials: { appId: string; privateKey: string } | null = null;\n // Cache of Octokit instances per installation (for multi-tenant)\n private installationClients = new Map<number, Octokit>();\n\n private webhookSecret: string;\n private chat: ChatInstance | null = null;\n private logger: Logger;\n private _botUserId: number | null = null;\n private formatConverter = new GitHubFormatConverter();\n\n /** Bot user ID (numeric) used for self-message detection */\n get botUserId(): string | undefined {\n return this._botUserId?.toString();\n }\n\n /** Whether this adapter is in multi-tenant mode (no fixed installation ID) */\n get isMultiTenant(): boolean {\n return this.appCredentials !== null && this.octokit === null;\n }\n\n constructor(config: GitHubAdapterConfig) {\n this.webhookSecret = config.webhookSecret;\n this.logger = config.logger;\n this.userName = config.userName;\n this._botUserId = config.botUserId ?? null;\n\n // Create Octokit instance based on auth method\n if (\"token\" in config && config.token) {\n // PAT mode - single Octokit instance\n this.octokit = new Octokit({ auth: config.token });\n } else if (\"appId\" in config && config.appId) {\n if (\"installationId\" in config && config.installationId) {\n // Single-tenant app mode - fixed installation\n this.octokit = new Octokit({\n authStrategy: createAppAuth,\n auth: {\n appId: config.appId,\n privateKey: config.privateKey,\n installationId: config.installationId,\n },\n });\n } else {\n // Multi-tenant app mode - create clients per installation\n this.appCredentials = {\n appId: config.appId,\n privateKey: config.privateKey,\n };\n this.logger.info(\n \"GitHub adapter initialized in multi-tenant mode (installation ID will be extracted from webhooks)\",\n );\n }\n } else {\n throw new Error(\n \"GitHubAdapter requires either token or appId/privateKey\",\n );\n }\n }\n\n /**\n * Get or create an Octokit instance for a specific installation.\n * For single-tenant mode, returns the single instance.\n * For multi-tenant mode, creates/caches instances per installation.\n */\n private getOctokit(installationId?: number): Octokit {\n // Single-tenant mode - return the single instance\n if (this.octokit) {\n return this.octokit;\n }\n\n // Multi-tenant mode - need an installation ID\n if (!this.appCredentials) {\n throw new Error(\"Adapter not properly configured\");\n }\n\n if (!installationId) {\n throw new Error(\n \"Installation ID required for multi-tenant mode. \" +\n \"This usually means you're trying to make an API call outside of a webhook context. \" +\n \"For proactive messages, use thread IDs from previous webhook interactions.\",\n );\n }\n\n // Check cache\n let client = this.installationClients.get(installationId);\n if (!client) {\n // Create new client for this installation\n client = new Octokit({\n authStrategy: createAppAuth,\n auth: {\n appId: this.appCredentials.appId,\n privateKey: this.appCredentials.privateKey,\n installationId,\n },\n });\n this.installationClients.set(installationId, client);\n this.logger.debug(\"Created Octokit client for installation\", {\n installationId,\n });\n }\n\n return client;\n }\n\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n\n // Fetch bot user ID if not provided (only works for single-tenant or PAT mode)\n if (!this._botUserId && this.octokit) {\n try {\n const { data: user } = await this.octokit.users.getAuthenticated();\n this._botUserId = user.id;\n this.logger.info(\"GitHub auth completed\", {\n botUserId: this._botUserId,\n login: user.login,\n });\n } catch (error) {\n this.logger.warn(\"Could not fetch bot user ID\", { error });\n }\n }\n }\n\n /**\n * Get the state key for storing installation ID for a repository.\n */\n private getInstallationKey(owner: string, repo: string): string {\n return `github:install:${owner}/${repo}`;\n }\n\n /**\n * Store the installation ID for a repository (for multi-tenant mode).\n */\n private async storeInstallationId(\n owner: string,\n repo: string,\n installationId: number,\n ): Promise<void> {\n if (!this.chat || !this.isMultiTenant) return;\n\n const key = this.getInstallationKey(owner, repo);\n await this.chat.getState().set(key, installationId);\n this.logger.debug(\"Stored installation ID\", {\n owner,\n repo,\n installationId,\n });\n }\n\n /**\n * Get the installation ID for a repository (for multi-tenant mode).\n */\n private async getInstallationId(\n owner: string,\n repo: string,\n ): Promise<number | undefined> {\n if (!this.chat || !this.isMultiTenant) return undefined;\n\n const key = this.getInstallationKey(owner, repo);\n return (await this.chat.getState().get<number>(key)) ?? undefined;\n }\n\n /**\n * Handle incoming webhook from GitHub.\n */\n async handleWebhook(\n request: Request,\n options?: WebhookOptions,\n ): Promise<Response> {\n const body = await request.text();\n this.logger.debug(\"GitHub webhook raw body\", {\n body: body.substring(0, 500),\n });\n\n // Verify request signature\n const signature = request.headers.get(\"x-hub-signature-256\");\n if (!this.verifySignature(body, signature)) {\n return new Response(\"Invalid signature\", { status: 401 });\n }\n\n // Get event type from header\n const eventType = request.headers.get(\"x-github-event\");\n this.logger.debug(\"GitHub webhook event type\", { eventType });\n\n // Handle ping event (webhook verification)\n if (eventType === \"ping\") {\n this.logger.info(\"GitHub webhook ping received\");\n return new Response(\"pong\", { status: 200 });\n }\n\n // Parse the JSON payload\n let payload:\n | IssueCommentWebhookPayload\n | PullRequestReviewCommentWebhookPayload;\n try {\n payload = JSON.parse(body);\n } catch {\n this.logger.error(\"GitHub webhook invalid JSON\", {\n contentType: request.headers.get(\"content-type\"),\n bodyPreview: body.substring(0, 200),\n });\n return new Response(\n \"Invalid JSON. Make sure webhook Content-Type is set to application/json\",\n { status: 400 },\n );\n }\n\n // Extract and store installation ID for multi-tenant mode\n const installationId = (payload as { installation?: { id: number } })\n .installation?.id;\n if (installationId && this.isMultiTenant) {\n const repo = payload.repository;\n await this.storeInstallationId(\n repo.owner.login,\n repo.name,\n installationId,\n );\n }\n\n // Handle events\n if (eventType === \"issue_comment\") {\n const issuePayload = payload as IssueCommentWebhookPayload;\n // Only process comments on PRs (they have a pull_request field)\n if (\n issuePayload.action === \"created\" &&\n issuePayload.issue.pull_request\n ) {\n this.handleIssueComment(issuePayload, installationId, options);\n }\n } else if (eventType === \"pull_request_review_comment\") {\n const reviewPayload = payload as PullRequestReviewCommentWebhookPayload;\n if (reviewPayload.action === \"created\") {\n this.handleReviewComment(reviewPayload, installationId, options);\n }\n }\n\n return new Response(\"ok\", { status: 200 });\n }\n\n /**\n * Verify GitHub webhook signature using HMAC-SHA256.\n */\n private verifySignature(body: string, signature: string | null): boolean {\n if (!signature) {\n return false;\n }\n\n // GitHub signature format: sha256=<hex>\n const expected = `sha256=${createHmac(\"sha256\", this.webhookSecret)\n .update(body)\n .digest(\"hex\")}`;\n\n // Use timing-safe comparison\n try {\n return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n\n /**\n * Handle issue_comment webhook (PR-level comments in Conversation tab).\n */\n private handleIssueComment(\n payload: IssueCommentWebhookPayload,\n _installationId: number | undefined,\n options?: WebhookOptions,\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring comment\");\n return;\n }\n\n const { comment, issue, repository, sender } = payload;\n\n // Build thread ID (PR-level)\n const threadId = this.encodeThreadId({\n owner: repository.owner.login,\n repo: repository.name,\n prNumber: issue.number,\n });\n\n // Build message\n const message = this.parseIssueComment(\n comment,\n repository,\n issue.number,\n threadId,\n );\n\n // Check if this is from the bot itself\n if (sender.id === this._botUserId) {\n this.logger.debug(\"Ignoring message from self\", {\n messageId: comment.id,\n });\n return;\n }\n\n this.chat.processMessage(this, threadId, message, options);\n }\n\n /**\n * Handle pull_request_review_comment webhook (line-specific comments).\n */\n private handleReviewComment(\n payload: PullRequestReviewCommentWebhookPayload,\n _installationId: number | undefined,\n options?: WebhookOptions,\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring comment\");\n return;\n }\n\n const { comment, pull_request, repository, sender } = payload;\n\n // Determine root comment ID for thread\n // If in_reply_to_id exists, use that (this is a reply in existing thread)\n // Otherwise, this comment is the root of a new thread\n const rootCommentId = comment.in_reply_to_id ?? comment.id;\n\n // Build thread ID (review comment thread)\n const threadId = this.encodeThreadId({\n owner: repository.owner.login,\n repo: repository.name,\n prNumber: pull_request.number,\n reviewCommentId: rootCommentId,\n });\n\n // Build message\n const message = this.parseReviewComment(\n comment,\n repository,\n pull_request.number,\n threadId,\n );\n\n // Check if this is from the bot itself\n if (sender.id === this._botUserId) {\n this.logger.debug(\"Ignoring message from self\", {\n messageId: comment.id,\n });\n return;\n }\n\n this.chat.processMessage(this, threadId, message, options);\n }\n\n /**\n * Parse an issue comment into a normalized Message.\n */\n private parseIssueComment(\n comment: GitHubIssueComment,\n repository: { owner: GitHubUser; name: string },\n prNumber: number,\n threadId: string,\n ): Message<GitHubRawMessage> {\n const author = this.parseAuthor(comment.user);\n\n return new Message({\n id: comment.id.toString(),\n threadId,\n text: this.formatConverter.extractPlainText(comment.body),\n formatted: this.formatConverter.toAst(comment.body),\n raw: {\n type: \"issue_comment\",\n comment,\n repository: {\n id: 0, // Not needed for raw storage\n name: repository.name,\n full_name: `${repository.owner.login}/${repository.name}`,\n owner: repository.owner,\n },\n prNumber,\n },\n author,\n metadata: {\n dateSent: new Date(comment.created_at),\n edited: comment.created_at !== comment.updated_at,\n editedAt:\n comment.created_at !== comment.updated_at\n ? new Date(comment.updated_at)\n : undefined,\n },\n attachments: [],\n });\n }\n\n /**\n * Parse a review comment into a normalized Message.\n */\n private parseReviewComment(\n comment: GitHubReviewComment,\n repository: { owner: GitHubUser; name: string },\n prNumber: number,\n threadId: string,\n ): Message<GitHubRawMessage> {\n const author = this.parseAuthor(comment.user);\n\n return new Message({\n id: comment.id.toString(),\n threadId,\n text: this.formatConverter.extractPlainText(comment.body),\n formatted: this.formatConverter.toAst(comment.body),\n raw: {\n type: \"review_comment\",\n comment,\n repository: {\n id: 0,\n name: repository.name,\n full_name: `${repository.owner.login}/${repository.name}`,\n owner: repository.owner,\n },\n prNumber,\n },\n author,\n metadata: {\n dateSent: new Date(comment.created_at),\n edited: comment.created_at !== comment.updated_at,\n editedAt:\n comment.created_at !== comment.updated_at\n ? new Date(comment.updated_at)\n : undefined,\n },\n attachments: [],\n });\n }\n\n /**\n * Parse a GitHub user into an Author.\n */\n private parseAuthor(user: GitHubUser): Author {\n return {\n userId: user.id.toString(),\n userName: user.login,\n fullName: user.login, // GitHub doesn't always expose real names\n isBot: user.type === \"Bot\",\n isMe: user.id === this._botUserId,\n };\n }\n\n /**\n * Get the Octokit client for a specific thread.\n * In multi-tenant mode, looks up the installation ID from state.\n */\n private async getOctokitForThread(\n owner: string,\n repo: string,\n ): Promise<Octokit> {\n const installationId = await this.getInstallationId(owner, repo);\n return this.getOctokit(installationId);\n }\n\n /**\n * Post a message to a thread.\n */\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<GitHubRawMessage>> {\n const { owner, repo, prNumber, reviewCommentId } =\n this.decodeThreadId(threadId);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n // Render message to GitHub markdown\n let body: string;\n const card = extractCard(message);\n if (card) {\n body = cardToGitHubMarkdown(card);\n } else {\n body = this.formatConverter.renderPostable(message);\n }\n\n // Convert emoji placeholders to unicode\n body = convertEmojiPlaceholders(body, \"github\");\n\n if (reviewCommentId) {\n // Review comment thread - reply with in_reply_to\n const { data: comment } = await octokit.pulls.createReplyForReviewComment(\n {\n owner,\n repo,\n pull_number: prNumber,\n comment_id: reviewCommentId,\n body,\n },\n );\n\n return {\n id: comment.id.toString(),\n threadId,\n raw: {\n type: \"review_comment\",\n comment: comment as GitHubReviewComment,\n repository: {\n id: 0,\n name: repo,\n full_name: `${owner}/${repo}`,\n owner: { id: 0, login: owner, type: \"User\" },\n },\n prNumber,\n },\n };\n } else {\n // PR-level thread - issue comment\n const { data: comment } = await octokit.issues.createComment({\n owner,\n repo,\n issue_number: prNumber,\n body,\n });\n\n return {\n id: comment.id.toString(),\n threadId,\n raw: {\n type: \"issue_comment\",\n comment: comment as GitHubIssueComment,\n repository: {\n id: 0,\n name: repo,\n full_name: `${owner}/${repo}`,\n owner: { id: 0, login: owner, type: \"User\" },\n },\n prNumber,\n },\n };\n }\n }\n\n /**\n * Edit an existing message.\n */\n async editMessage(\n threadId: string,\n messageId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<GitHubRawMessage>> {\n const { owner, repo, prNumber, reviewCommentId } =\n this.decodeThreadId(threadId);\n const commentId = parseInt(messageId, 10);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n // Render message to GitHub markdown\n let body: string;\n const card = extractCard(message);\n if (card) {\n body = cardToGitHubMarkdown(card);\n } else {\n body = this.formatConverter.renderPostable(message);\n }\n\n // Convert emoji placeholders to unicode\n body = convertEmojiPlaceholders(body, \"github\");\n\n if (reviewCommentId) {\n // Review comment\n const { data: comment } = await octokit.pulls.updateReviewComment({\n owner,\n repo,\n comment_id: commentId,\n body,\n });\n\n return {\n id: comment.id.toString(),\n threadId,\n raw: {\n type: \"review_comment\",\n comment: comment as GitHubReviewComment,\n repository: {\n id: 0,\n name: repo,\n full_name: `${owner}/${repo}`,\n owner: { id: 0, login: owner, type: \"User\" },\n },\n prNumber,\n },\n };\n } else {\n // Issue comment\n const { data: comment } = await octokit.issues.updateComment({\n owner,\n repo,\n comment_id: commentId,\n body,\n });\n\n return {\n id: comment.id.toString(),\n threadId,\n raw: {\n type: \"issue_comment\",\n comment: comment as GitHubIssueComment,\n repository: {\n id: 0,\n name: repo,\n full_name: `${owner}/${repo}`,\n owner: { id: 0, login: owner, type: \"User\" },\n },\n prNumber,\n },\n };\n }\n }\n\n /**\n * Delete a message.\n */\n async deleteMessage(threadId: string, messageId: string): Promise<void> {\n const { owner, repo, reviewCommentId } = this.decodeThreadId(threadId);\n const commentId = parseInt(messageId, 10);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n if (reviewCommentId) {\n await octokit.pulls.deleteReviewComment({\n owner,\n repo,\n comment_id: commentId,\n });\n } else {\n await octokit.issues.deleteComment({\n owner,\n repo,\n comment_id: commentId,\n });\n }\n }\n\n /**\n * Add a reaction to a message.\n */\n async addReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string,\n ): Promise<void> {\n const { owner, repo, reviewCommentId } = this.decodeThreadId(threadId);\n const commentId = parseInt(messageId, 10);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n // Convert emoji to GitHub reaction content\n const content = this.emojiToGitHubReaction(emoji);\n\n if (reviewCommentId) {\n await octokit.reactions.createForPullRequestReviewComment({\n owner,\n repo,\n comment_id: commentId,\n content,\n });\n } else {\n await octokit.reactions.createForIssueComment({\n owner,\n repo,\n comment_id: commentId,\n content,\n });\n }\n }\n\n /**\n * Remove a reaction from a message.\n */\n async removeReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string,\n ): Promise<void> {\n const { owner, repo, reviewCommentId } = this.decodeThreadId(threadId);\n const commentId = parseInt(messageId, 10);\n const content = this.emojiToGitHubReaction(emoji);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n // List reactions to find the one to delete\n const reactions = reviewCommentId\n ? (\n await octokit.reactions.listForPullRequestReviewComment({\n owner,\n repo,\n comment_id: commentId,\n })\n ).data\n : (\n await octokit.reactions.listForIssueComment({\n owner,\n repo,\n comment_id: commentId,\n })\n ).data;\n\n // Find the bot's reaction with matching content\n const reaction = reactions.find(\n (r) => r.content === content && r.user?.id === this._botUserId,\n );\n\n if (reaction) {\n if (reviewCommentId) {\n await octokit.reactions.deleteForPullRequestComment({\n owner,\n repo,\n comment_id: commentId,\n reaction_id: reaction.id,\n });\n } else {\n await octokit.reactions.deleteForIssueComment({\n owner,\n repo,\n comment_id: commentId,\n reaction_id: reaction.id,\n });\n }\n }\n }\n\n /**\n * Convert SDK emoji to GitHub reaction content.\n */\n private emojiToGitHubReaction(\n emoji: EmojiValue | string,\n ): GitHubReactionContent {\n const emojiName = typeof emoji === \"string\" ? emoji : emoji.name;\n\n // Map common emoji names to GitHub reactions\n const mapping: Record<string, GitHubReactionContent> = {\n thumbs_up: \"+1\",\n \"+1\": \"+1\",\n thumbs_down: \"-1\",\n \"-1\": \"-1\",\n laugh: \"laugh\",\n smile: \"laugh\",\n confused: \"confused\",\n thinking: \"confused\",\n heart: \"heart\",\n love_eyes: \"heart\",\n hooray: \"hooray\",\n party: \"hooray\",\n confetti: \"hooray\",\n rocket: \"rocket\",\n eyes: \"eyes\",\n };\n\n return mapping[emojiName] || \"+1\";\n }\n\n /**\n * Show typing indicator (no-op for GitHub).\n */\n async startTyping(_threadId: string): Promise<void> {\n // GitHub doesn't support typing indicators\n }\n\n /**\n * Fetch messages from a thread.\n */\n async fetchMessages(\n threadId: string,\n options?: FetchOptions,\n ): Promise<FetchResult<GitHubRawMessage>> {\n const { owner, repo, prNumber, reviewCommentId } =\n this.decodeThreadId(threadId);\n const limit = options?.limit ?? 100;\n const direction = options?.direction ?? \"backward\";\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n let messages: Message<GitHubRawMessage>[];\n\n if (reviewCommentId) {\n // Fetch review comments for the PR and filter by thread\n const { data: allComments } = await octokit.pulls.listReviewComments({\n owner,\n repo,\n pull_number: prNumber,\n per_page: 100, // Fetch more to filter\n });\n\n // Filter to comments in this thread (same in_reply_to_id or the root itself)\n const threadComments = allComments.filter(\n (c) => c.id === reviewCommentId || c.in_reply_to_id === reviewCommentId,\n );\n\n messages = threadComments.map((comment) =>\n this.parseReviewComment(\n comment as GitHubReviewComment,\n {\n owner: { id: 0, login: owner, type: \"User\", avatar_url: \"\" },\n name: repo,\n },\n prNumber,\n threadId,\n ),\n );\n } else {\n // Fetch issue comments\n const { data: comments } = await octokit.issues.listComments({\n owner,\n repo,\n issue_number: prNumber,\n per_page: limit,\n });\n\n messages = comments.map((comment) =>\n this.parseIssueComment(\n comment as GitHubIssueComment,\n {\n owner: { id: 0, login: owner, type: \"User\", avatar_url: \"\" },\n name: repo,\n },\n prNumber,\n threadId,\n ),\n );\n }\n\n // Sort chronologically (oldest first)\n messages.sort(\n (a, b) => a.metadata.dateSent.getTime() - b.metadata.dateSent.getTime(),\n );\n\n // For backward direction, take the last N messages\n if (direction === \"backward\" && messages.length > limit) {\n messages = messages.slice(-limit);\n } else if (direction === \"forward\" && messages.length > limit) {\n messages = messages.slice(0, limit);\n }\n\n return {\n messages,\n nextCursor: undefined, // Simplified pagination for now\n };\n }\n\n /**\n * Fetch thread metadata.\n */\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { owner, repo, prNumber, reviewCommentId } =\n this.decodeThreadId(threadId);\n\n const octokit = await this.getOctokitForThread(owner, repo);\n\n const { data: pr } = await octokit.pulls.get({\n owner,\n repo,\n pull_number: prNumber,\n });\n\n return {\n id: threadId,\n channelId: `${owner}/${repo}`,\n channelName: `${repo} #${prNumber}`,\n isDM: false,\n metadata: {\n owner,\n repo,\n prNumber,\n prTitle: pr.title,\n prState: pr.state,\n reviewCommentId,\n },\n };\n }\n\n /**\n * Encode platform data into a thread ID string.\n *\n * Thread ID formats:\n * - PR-level: `github:{owner}/{repo}:{prNumber}`\n * - Review comment: `github:{owner}/{repo}:{prNumber}:rc:{reviewCommentId}`\n */\n encodeThreadId(platformData: GitHubThreadId): string {\n const { owner, repo, prNumber, reviewCommentId } = platformData;\n\n if (reviewCommentId) {\n return `github:${owner}/${repo}:${prNumber}:rc:${reviewCommentId}`;\n }\n return `github:${owner}/${repo}:${prNumber}`;\n }\n\n /**\n * Decode thread ID string back to platform data.\n */\n decodeThreadId(threadId: string): GitHubThreadId {\n if (!threadId.startsWith(\"github:\")) {\n throw new ValidationError(\n \"github\",\n `Invalid GitHub thread ID: ${threadId}`,\n );\n }\n\n const withoutPrefix = threadId.slice(7); // Remove \"github:\"\n\n // Check for review comment thread format\n const rcMatch = withoutPrefix.match(/^([^/]+)\\/([^:]+):(\\d+):rc:(\\d+)$/);\n if (rcMatch) {\n return {\n owner: rcMatch[1],\n repo: rcMatch[2],\n prNumber: parseInt(rcMatch[3], 10),\n reviewCommentId: parseInt(rcMatch[4], 10),\n };\n }\n\n // PR-level thread format\n const prMatch = withoutPrefix.match(/^([^/]+)\\/([^:]+):(\\d+)$/);\n if (prMatch) {\n return {\n owner: prMatch[1],\n repo: prMatch[2],\n prNumber: parseInt(prMatch[3], 10),\n };\n }\n\n throw new ValidationError(\n \"github\",\n `Invalid GitHub thread ID format: ${threadId}`,\n );\n }\n\n /**\n * Parse a raw message into normalized format.\n */\n parseMessage(raw: GitHubRawMessage): Message<GitHubRawMessage> {\n if (raw.type === \"issue_comment\") {\n const threadId = this.encodeThreadId({\n owner: raw.repository.owner.login,\n repo: raw.repository.name,\n prNumber: raw.prNumber,\n });\n return this.parseIssueComment(\n raw.comment,\n { owner: raw.repository.owner, name: raw.repository.name },\n raw.prNumber,\n threadId,\n );\n } else {\n const rootCommentId = raw.comment.in_reply_to_id ?? raw.comment.id;\n const threadId = this.encodeThreadId({\n owner: raw.repository.owner.login,\n repo: raw.repository.name,\n prNumber: raw.prNumber,\n reviewCommentId: rootCommentId,\n });\n return this.parseReviewComment(\n raw.comment,\n { owner: raw.repository.owner, name: raw.repository.name },\n raw.prNumber,\n threadId,\n );\n }\n }\n\n /**\n * Render formatted content to GitHub markdown.\n */\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n}\n\n/**\n * Create a new GitHub adapter instance.\n *\n * @example\n * ```typescript\n * const chat = new Chat({\n * adapters: {\n * github: createGitHubAdapter({\n * token: process.env.GITHUB_TOKEN!,\n * webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,\n * userName: \"my-bot\",\n * logger: console,\n * }),\n * },\n * });\n * ```\n */\nexport function createGitHubAdapter(\n config: GitHubAdapterConfig,\n): GitHubAdapter {\n return new GitHubAdapter(config);\n}\n","/**\n * Convert CardElement to GitHub-flavored markdown.\n *\n * Since GitHub doesn't support rich cards natively, we render cards\n * as formatted markdown with bold text, dividers, and links.\n */\n\nimport type {\n ActionsElement,\n CardChild,\n CardElement,\n FieldsElement,\n TextElement,\n} from \"chat\";\n\n/**\n * Convert a CardElement to GitHub-flavored markdown.\n *\n * Cards are rendered as clean markdown with:\n * - Bold title and subtitle\n * - Text content\n * - Fields as key-value pairs\n * - Buttons as markdown links (action buttons become bold text since GitHub has no interactivity)\n *\n * @example\n * ```typescript\n * const card = Card({\n * title: \"Order #1234\",\n * subtitle: \"Status update\",\n * children: [\n * Text(\"Your order has been shipped!\"),\n * Fields([\n * Field({ label: \"Tracking\", value: \"ABC123\" }),\n * ]),\n * Actions([\n * LinkButton({ url: \"https://track.example.com\", label: \"Track Order\" }),\n * ]),\n * ],\n * });\n *\n * // Output:\n * // **Order #1234**\n * // Status update\n * //\n * // Your order has been shipped!\n * //\n * // **Tracking:** ABC123\n * //\n * // [Track Order](https://track.example.com)\n * ```\n */\nexport function cardToGitHubMarkdown(card: CardElement): string {\n const lines: string[] = [];\n\n // Title (bold)\n if (card.title) {\n lines.push(`**${escapeMarkdown(card.title)}**`);\n }\n\n // Subtitle\n if (card.subtitle) {\n lines.push(escapeMarkdown(card.subtitle));\n }\n\n // Add spacing after header if there are children\n if ((card.title || card.subtitle) && card.children.length > 0) {\n lines.push(\"\");\n }\n\n // Header image\n if (card.imageUrl) {\n lines.push(`![](${card.imageUrl})`);\n lines.push(\"\");\n }\n\n // Children\n for (let i = 0; i < card.children.length; i++) {\n const child = card.children[i];\n const childLines = renderChild(child);\n\n if (childLines.length > 0) {\n lines.push(...childLines);\n\n // Add spacing between children (except last)\n if (i < card.children.length - 1) {\n lines.push(\"\");\n }\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Render a card child element to markdown lines.\n */\nfunction renderChild(child: CardChild): string[] {\n switch (child.type) {\n case \"text\":\n return renderText(child);\n\n case \"fields\":\n return renderFields(child);\n\n case \"actions\":\n return renderActions(child);\n\n case \"section\":\n // Flatten section children\n return child.children.flatMap(renderChild);\n\n case \"image\":\n if (child.alt) {\n return [`![${escapeMarkdown(child.alt)}](${child.url})`];\n }\n return [`![](${child.url})`];\n\n case \"divider\":\n return [\"---\"];\n\n default:\n return [];\n }\n}\n\n/**\n * Render text element.\n */\nfunction renderText(text: TextElement): string[] {\n const content = text.content;\n\n switch (text.style) {\n case \"bold\":\n return [`**${content}**`];\n case \"muted\":\n // Use italic for muted text\n return [`_${content}_`];\n default:\n return [content];\n }\n}\n\n/**\n * Render fields as key-value pairs.\n */\nfunction renderFields(fields: FieldsElement): string[] {\n return fields.children.map(\n (field) =>\n `**${escapeMarkdown(field.label)}:** ${escapeMarkdown(field.value)}`,\n );\n}\n\n/**\n * Render actions (buttons) as markdown links or bold text.\n */\nfunction renderActions(actions: ActionsElement): string[] {\n const buttonTexts = actions.children.map((button) => {\n if (button.type === \"link-button\") {\n // Link buttons become markdown links\n return `[${escapeMarkdown(button.label)}](${button.url})`;\n }\n // Action buttons become bold text (no interactivity in GitHub comments)\n // We could potentially use a special format that the bot recognizes\n return `**[${escapeMarkdown(button.label)}]**`;\n });\n\n // Join buttons with separator\n return [buttonTexts.join(\" • \")];\n}\n\n/**\n * Escape special markdown characters in text.\n */\nfunction escapeMarkdown(text: string): string {\n // Only escape characters that could break the formatting\n // We're deliberately light-handed to preserve intentional markdown\n return text\n .replace(/\\*/g, \"\\\\*\")\n .replace(/_/g, \"\\\\_\")\n .replace(/\\[/g, \"\\\\[\")\n .replace(/\\]/g, \"\\\\]\");\n}\n\n/**\n * Generate plain text fallback from a card (no markdown).\n * Used for alt text or plain text contexts.\n */\nexport function cardToPlainText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(card.title);\n }\n\n if (card.subtitle) {\n parts.push(card.subtitle);\n }\n\n for (const child of card.children) {\n const text = childToPlainText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Convert card child to plain text.\n */\nfunction childToPlainText(child: CardChild): string | null {\n switch (child.type) {\n case \"text\":\n return child.content;\n case \"fields\":\n return child.children.map((f) => `${f.label}: ${f.value}`).join(\"\\n\");\n case \"actions\":\n // Actions are interactive-only — exclude from fallback text.\n // See: https://docs.slack.dev/reference/methods/chat.postMessage\n return null;\n case \"section\":\n return child.children.map(childToPlainText).filter(Boolean).join(\"\\n\");\n default:\n return null;\n }\n}\n","/**\n * GitHub-specific format conversion using AST-based parsing.\n *\n * GitHub uses GitHub Flavored Markdown (GFM) which is very close to standard markdown.\n * This converter primarily passes through standard markdown, with special handling for:\n * - @mentions (user references)\n * - #refs (issue/PR references)\n * - SHA references (commit links)\n */\n\nimport {\n type AdapterPostableMessage,\n BaseFormatConverter,\n parseMarkdown,\n type Root,\n stringifyMarkdown,\n} from \"chat\";\n\nexport class GitHubFormatConverter extends BaseFormatConverter {\n /**\n * GitHub uses standard GFM, so we can use remark-stringify directly.\n * We just need to ensure @mentions are preserved.\n */\n fromAst(ast: Root): string {\n // Use standard markdown stringification\n // remark-stringify handles GFM well\n return stringifyMarkdown(ast).trim();\n }\n\n /**\n * Parse GitHub markdown into an AST.\n * GitHub uses standard GFM, so we use the standard parser.\n */\n toAst(markdown: string): Root {\n return parseMarkdown(markdown);\n }\n\n /**\n * Override renderPostable to handle @mentions in plain strings.\n * GitHub @mentions are already in the correct format (@username).\n */\n override renderPostable(message: AdapterPostableMessage): string {\n if (typeof message === \"string\") {\n return message;\n }\n if (\"raw\" in message) {\n return message.raw;\n }\n if (\"markdown\" in message) {\n return this.fromMarkdown(message.markdown);\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n // Handle cards via base class\n return super.renderPostable(message);\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chat-adapter/github",
3
- "version": "4.8.0",
3
+ "version": "4.9.1",
4
4
  "description": "GitHub adapter for chat - PR comment threads",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,8 +18,8 @@
18
18
  "dependencies": {
19
19
  "@octokit/auth-app": "^7.1.1",
20
20
  "@octokit/rest": "^21.0.2",
21
- "@chat-adapter/shared": "4.8.0",
22
- "chat": "4.8.0"
21
+ "@chat-adapter/shared": "4.9.1",
22
+ "chat": "4.9.1"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.10.2",