071e885d63
* Add utils for r2 upload * Add the tool and new routes * fix auth issue * sign headers * add comment * use our own API key to auth signed uploads * Restructure things slightly * tweak * tweak * add comments * tweak * revert a thing * twaek * drop mime type filtering * new incarnation of mime type filtering * jsut allow all octet-streams * simplify further * tweak * update lockfile --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
146 lines
4.1 KiB
TypeScript
146 lines
4.1 KiB
TypeScript
import TurndownService from "turndown";
|
|
import type { PayloadEvent } from "../external.ts";
|
|
import { log } from "./cli.ts";
|
|
import type { OctokitWithPlugins } from "./github.ts";
|
|
import type { RunContextData } from "./runContextData.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: RunContextData["repo"];
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
log.debug(`[resolveBody] fetching body_html for ${ctx.event.trigger}`);
|
|
const bodyHtml = await fetchBodyHtml(ctx);
|
|
log.debug(`[resolveBody] bodyHtml: ${bodyHtml?.substring(0, 300)}`);
|
|
if (!bodyHtml) return body ?? null;
|
|
|
|
const resolved = turndown.turndown(bodyHtml);
|
|
log.debug(`[resolveBody] resolved: ${resolved.substring(0, 300)}`);
|
|
return resolved;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|