import { forbidden, notFoundError } from "../errors";
import { limitHeavy, limitWrite } from "../limits";
import { latestRuns } from "../mergefix/store";
import { startMergeFix } from "../mergefix/service";
import type { FixPlan } from "../mergefix/plan";
import { loadPull } from "../pulls";
import { hasPermission, loadRepoById } from "../repos";
import { scopedRepoPermission } from "../token-scope";
import { requireScopedRepo } from "./http";
import type { ApiContext } from "./http";
const HTTP_ACCEPTED = 202;
async function loadScopedPull(ctx: ApiContext, owner: string, name: string, number: number, needed: "read" | "write") {
const { repo } = await requireScopedRepo(ctx, owner, name, "pulls", needed);
const pull = await loadPull(repo.id, number);
if (!pull) throw notFoundError("Pull request not found");
return { repo, pull };
}
/** Starts an AI conflict fix; like update-branch, the token must also be able to push to the head repo. */
export async function startPullAiFix(ctx: ApiContext, owner: string, name: string, number: number) {
await limitWrite(ctx.viewer);
const { repo, pull } = await loadScopedPull(ctx, owner, name, number, "write");
const headRepo = pull.head_repo_id === repo.id ? repo : await loadRepoById(pull.head_repo_id);
if (!headRepo) throw notFoundError("Head repository was deleted");
if (!hasPermission(await scopedRepoPermission(headRepo, ctx.credential, "contents"), "write")) {
throw forbidden("This token cannot push to the head branch");
}
const id = await startMergeFix(pull, ctx.viewer, "button");
return Response.json({ id }, { status: HTTP_ACCEPTED });
}
export async function getPullAiFix(ctx: ApiContext, owner: string, name: string, number: number) {
await limitHeavy(ctx.viewer);
const { pull } = await loadScopedPull(ctx, owner, name, number, "read");
const [latest] = await latestRuns(pull.issue_id, 1);
if (!latest) throw notFoundError("No AI fix has run for this pull request");
const plan = latest.result_json ? (JSON.parse(latest.result_json) as FixPlan) : null;
return Response.json({