make webhook

This commit is contained in:
2026-07-13 18:50:38 +02:00
parent 2f128550fc
commit 40c822d3bf
67 changed files with 6286 additions and 2053 deletions
+105
View File
@@ -0,0 +1,105 @@
import {
type IssueSnapshot,
type Marker,
marker,
parseMarker,
protocolVersion,
sha256,
} from "../../core/contracts.js";
import { parseAgentCommand } from "../../core/webhook.js";
import type { GiteaComment, GiteaIssue } from "./types.js";
export function createIssueSnapshot(
issue: GiteaIssue,
comments: GiteaComment[],
botLogin: string,
): IssueSnapshot {
const humanComments = comments
.filter(
(comment) =>
comment.user.login.toLowerCase() !== botLogin.toLowerCase(),
)
.filter((comment) => !parseAgentCommand(comment.body))
.map((comment) => ({
id: comment.id,
author: comment.user.login,
createdAt: comment.created_at,
body: comment.body,
}));
const canonical = JSON.stringify({
v: protocolVersion,
number: issue.number,
state: issue.state,
title: issue.title,
body: issue.body,
comments: humanComments.map(({ author, createdAt, body }) => ({
author,
createdAt,
body,
})),
});
return {
digest: sha256(canonical),
title: issue.title,
body: issue.body,
comments: humanComments,
};
}
export function findAcceptedPlan(
comments: GiteaComment[],
botLogin: string,
issueNumber: number,
):
| {
marker: Marker;
markdown: string;
comment: GiteaComment;
}
| undefined {
const candidates = comments
.filter(
(comment) =>
comment.user.login.toLowerCase() === botLogin.toLowerCase(),
)
.map((comment) => ({
comment,
found: parseMarker(comment.body, "plan"),
}))
.filter((value): value is { comment: GiteaComment; found: Marker } =>
Boolean(value.found),
)
.filter(
(value) =>
value.found.issue === issueNumber &&
value.found.status === "accepted",
)
.sort((a, b) =>
b.comment.updated_at.localeCompare(a.comment.updated_at),
);
const selected = candidates[0];
if (!selected) return undefined;
const header = "## Accepted implementation plan\n\n";
const start = selected.comment.body.indexOf(header);
const end = selected.comment.body.lastIndexOf(
"\n\n<!-- olixero-ci-agent:plan-footer -->",
);
if (start < 0) return undefined;
const markdown = selected.comment.body
.slice(start + header.length, end < 0 ? undefined : end)
.trim();
if (
!selected.found.planDigest ||
sha256(markdown) !== selected.found.planDigest
)
return undefined;
return { marker: selected.found, markdown, comment: selected.comment };
}
export function renderStatus(input: {
marker: Marker;
heading: string;
detail: string;
}): string {
return `${marker(input.marker)}\n## ${input.heading}\n\n${input.detail}`;
}