import { z } from "zod";
import { first } from "../db";
import { repoDb } from "../scoped-db";
import { forbidden, notFoundError } from "../errors";
import { applyLabels, indexIssue, issueUrl, loadIssue } from "../issues";
import type { IssueRow } from "../issues";
import { addIssueEvent } from "../pulls";
import { limitWrite } from "../limits";
import { assertNotArchived, hasPermission } from "../repos";
import { credentialCoversRepo } from "../token-scope";
import { changeIssueState, commentsWithAuthors, insertComment, insertIssue } from "../mcp/issue-writes";
import { markdownBody, title } from "~/lib/validation";
import type { RepoRow } from "~/lib/types";
import { readJson, requireScopedRepo, unprocessable } from "./http";
import type { ApiContext, ScopedRepo } from "./http";
const LIST_LIMIT = 100;
const MAX_COMMENTS = 100;
/** Read access lets a session write on public repositories; a token must also list the repository. */
async function requireIssueWriteCoverage(ctx: ApiContext, repoId: string): Promise<void> {
if (!(await credentialCoversRepo(ctx.credential, repoId)))
throw forbidden("This token does not cover this repository");
}
/** Opening issues and commenting need only read access (as on the web), but a fine-grained token must opt in to issue writes. */
function requireIssueWriteGrant(ctx: ApiContext): void {
const grant = ctx.credential.grant;
if (grant?.kind === "fine" && grant.permissions.issues !== "write") {
throw forbidden("This token cannot write issues");
}
if (grant?.kind === "deploy") throw forbidden("Deploy keys cannot write issues");
}
function canMaintain(scoped: ScopedRepo): boolean {
return hasPermission(scoped.permission, "write");
}
async function loadPlainIssue(repo: RepoRow, number: number): Promise<IssueRow> {
const issue = await loadIssue(repo.id, number);