Update lock

This commit is contained in:
Colin McDonnell
2026-01-22 06:15:49 +00:00
committed by pullfrog[bot]
parent c15049446f
commit 57895ae342
5 changed files with 17677 additions and 2 deletions
+17497 -1
View File
File diff suppressed because one or more lines are too long
+13
View File
@@ -3,6 +3,7 @@ import { initToolState, startMcpHttpServer } from "./mcp/server.ts";
import { computeModes } from "./modes.ts";
import { resolveAgent } from "./utils/agent.ts";
import { validateApiKey } from "./utils/apiKeys.ts";
import { resolveBody } from "./utils/body.ts";
import { log, writeSummary } from "./utils/cli.ts";
import { reportErrorToComment } from "./utils/errorReport.ts";
import { createOctokit } from "./utils/github.ts";
@@ -50,6 +51,18 @@ export async function main(): Promise<MainResult> {
process.chdir(payload.cwd);
}
// resolve body - fetches body_html and converts to markdown if images present
// this ensures agents receive markdown with working signed image URLs
const originalBody = payload.event.body;
const resolvedBody = await resolveBody({ event: payload.event, octokit, repo });
if (resolvedBody !== originalBody) {
payload.event.body = resolvedBody;
// also update prompt if original body was included there
if (originalBody && payload.prompt.includes(originalBody)) {
payload.prompt = payload.prompt.replace(originalBody, resolvedBody ?? "");
}
}
const tmpdir = createTempDirectory();
const agent = resolveAgent({ payload, repoSettings: repo.repoSettings });
+4 -1
View File
@@ -43,11 +43,14 @@
"fastmcp": "^3.26.8",
"package-manager-detector": "^1.6.0",
"semver": "^7.7.3",
"table": "^6.9.0"
"table": "^6.9.0",
"turndown": "^7.2.0"
},
"devDependencies": {
"@types/node": "^24.7.2",
"@types/turndown": "^5.0.5",
"@types/semver": "^7.7.1",
"@types/turndown": "^5.0.5",
"arg": "^5.0.2",
"esbuild": "^0.25.9",
"husky": "^9.0.0",
+23
View File
@@ -65,6 +65,9 @@ importers:
table:
specifier: ^6.9.0
version: 6.9.0
turndown:
specifier: ^7.2.0
version: 7.2.2
devDependencies:
'@types/node':
specifier: ^24.7.2
@@ -72,6 +75,9 @@ importers:
'@types/semver':
specifier: ^7.7.1
version: 7.7.1
'@types/turndown':
specifier: ^5.0.5
version: 5.0.6
arg:
specifier: ^5.0.2
version: 5.0.2
@@ -534,6 +540,9 @@ packages:
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@mixmark-io/domino@2.2.0':
resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
'@modelcontextprotocol/sdk@1.25.2':
resolution: {integrity: sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==}
engines: {node: '>=18'}
@@ -826,6 +835,9 @@ packages:
'@types/semver@7.7.1':
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
'@types/turndown@5.0.6':
resolution: {integrity: sha512-ru00MoyeeouE5BX4gRL+6m/BsDfbRayOskWqUvh7CLGW+UXxHQItqALa38kKnOiZPqJrtzJUgAC2+F0rL1S4Pg==}
'@vitest/expect@4.0.17':
resolution: {integrity: sha512-mEoqP3RqhKlbmUmntNDDCJeTDavDR+fVYkSOw8qRwJFaW/0/5zA9zFeTrHqNtcmwh6j26yMmwx2PqUDPzt5ZAQ==}
@@ -1500,6 +1512,9 @@ packages:
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
turndown@7.2.2:
resolution: {integrity: sha512-1F7db8BiExOKxjSMU2b7if62D/XOyQyZbPKq/nUwopfgnHlqXHqQ0lvfUTeUIr1lZJzOPFn43dODyMSIfvWRKQ==}
type-is@2.0.1:
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
engines: {node: '>= 0.6'}
@@ -1959,6 +1974,8 @@ snapshots:
'@jridgewell/sourcemap-codec@1.5.5': {}
'@mixmark-io/domino@2.2.0': {}
'@modelcontextprotocol/sdk@1.25.2(hono@4.11.3)(zod@4.3.5)':
dependencies:
'@hono/node-server': 1.19.7(hono@4.11.3)
@@ -2226,6 +2243,8 @@ snapshots:
'@types/semver@7.7.1': {}
'@types/turndown@5.0.6': {}
'@vitest/expect@4.0.17':
dependencies:
'@standard-schema/spec': 1.0.0
@@ -2980,6 +2999,10 @@ snapshots:
tunnel@0.0.6: {}
turndown@7.2.2:
dependencies:
'@mixmark-io/domino': 2.2.0
type-is@2.0.1:
dependencies:
content-type: 1.0.5
+140
View File
@@ -0,0 +1,140 @@
import TurndownService from "turndown";
import type { PayloadEvent } from "../external.ts";
import type { OctokitWithPlugins } from "./github.ts";
import type { RepoData } from "./repoData.ts";
const turndown = new TurndownService();
function hasImages(body: string | null | undefined): boolean {
if (!body) return false;
return body.includes("<img") || body.includes("![");
}
interface ResolveBodyContext {
event: PayloadEvent;
octokit: OctokitWithPlugins;
repo: RepoData;
}
/**
* resolves the body of an event by fetching body_html and converting to markdown.
* only fetches body_html if the body contains images (to avoid unnecessary API calls).
* this ensures agents receive markdown with working signed image URLs instead of
* broken user-attachments URLs.
*/
export async function resolveBody(ctx: ResolveBodyContext): Promise<string | null> {
const body = ctx.event.body;
// pass through if no images - no API call needed
if (!hasImages(body)) return body ?? null;
const bodyHtml = await fetchBodyHtml(ctx);
if (!bodyHtml) return body ?? null;
return turndown.turndown(bodyHtml);
}
async function fetchBodyHtml(ctx: ResolveBodyContext): Promise<string | undefined> {
const event = ctx.event;
const headers = { accept: "application/vnd.github.full+json" };
const owner = ctx.repo.owner;
const repo = ctx.repo.name;
switch (event.trigger) {
case "issue_comment_created":
if (!event.comment_id) return;
return (
await ctx.octokit.rest.issues.getComment({
owner,
repo,
comment_id: event.comment_id,
headers,
})
).data.body_html;
case "issues_opened":
case "issues_assigned":
case "issues_labeled":
if (!event.issue_number) return;
return (
await ctx.octokit.rest.issues.get({
owner,
repo,
issue_number: event.issue_number,
headers,
})
).data.body_html;
case "pull_request_opened":
case "pull_request_ready_for_review":
case "pull_request_review_requested":
// PRs are also issues - use issues.get which returns body_html
if (!event.issue_number) return;
return (
await ctx.octokit.rest.issues.get({
owner,
repo,
issue_number: event.issue_number,
headers,
})
).data.body_html;
case "pull_request_review_submitted":
if (!event.issue_number || !event.review_id) return;
return (
await ctx.octokit.rest.pulls.getReview({
owner,
repo,
pull_number: event.issue_number,
review_id: event.review_id,
headers,
})
).data.body_html;
case "pull_request_review_comment_created":
if (!event.comment_id) return;
return (
await ctx.octokit.rest.pulls.getReviewComment({
owner,
repo,
comment_id: event.comment_id,
headers,
})
).data.body_html;
case "check_suite_completed":
// body is the PR body
if (!event.issue_number) return;
return (
await ctx.octokit.rest.issues.get({
owner,
repo,
issue_number: event.issue_number,
headers,
})
).data.body_html;
case "implement_plan":
// body is the plan content from an issue comment
if (!event.plan_comment_id) return;
return (
await ctx.octokit.rest.issues.getComment({
owner,
repo,
comment_id: event.plan_comment_id,
headers,
})
).data.body_html;
// triggers without a body field that needs resolution
case "workflow_dispatch":
case "fix_review":
case "unknown":
return undefined;
default:
// exhaustiveness check - TypeScript will error if a trigger is missing
event satisfies never;
return undefined;
}
}