@vercel/factory 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{approval-MoiwGpkh.d.mts → approval-CDiBco2V.d.mts} +14 -6
- package/dist/{task-DMHuvBI0.mjs → change-BPYhWKuL.mjs} +119 -99
- package/dist/change-BPYhWKuL.mjs.map +1 -0
- package/dist/connectors/slack.d.mts +12 -4
- package/dist/connectors/slack.mjs +19 -3
- package/dist/connectors/slack.mjs.map +1 -1
- package/dist/eve/index.d.mts +82 -12
- package/dist/eve/index.mjs +84 -4
- package/dist/eve/index.mjs.map +1 -1
- package/dist/{github-DMOypeIu.d.mts → github-wThBfbm-.d.mts} +99 -2
- package/dist/github.d.mts +2 -2
- package/dist/github.mjs +195 -1
- package/dist/github.mjs.map +1 -1
- package/dist/index.d.mts +103 -2
- package/dist/index.mjs +271 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/task-DMHuvBI0.mjs.map +0 -1
package/dist/github.mjs
CHANGED
|
@@ -19,6 +19,41 @@ const reviewCommentSchema = z.object({
|
|
|
19
19
|
user: z.object({ login: z.string().min(1) }),
|
|
20
20
|
in_reply_to_id: z.number().int().positive().nullish()
|
|
21
21
|
});
|
|
22
|
+
const submittedReviewSchema = z.object({
|
|
23
|
+
id: z.number().int().positive(),
|
|
24
|
+
html_url: z.url(),
|
|
25
|
+
state: z.string().min(1)
|
|
26
|
+
});
|
|
27
|
+
const checkRunSchema = z.object({
|
|
28
|
+
id: z.number().int().positive(),
|
|
29
|
+
name: z.string().min(1),
|
|
30
|
+
head_sha: z.string().regex(/^[0-9a-f]{40}$/),
|
|
31
|
+
status: z.enum([
|
|
32
|
+
"queued",
|
|
33
|
+
"in_progress",
|
|
34
|
+
"completed",
|
|
35
|
+
"waiting",
|
|
36
|
+
"requested",
|
|
37
|
+
"pending"
|
|
38
|
+
]),
|
|
39
|
+
conclusion: z.enum([
|
|
40
|
+
"action_required",
|
|
41
|
+
"cancelled",
|
|
42
|
+
"failure",
|
|
43
|
+
"neutral",
|
|
44
|
+
"skipped",
|
|
45
|
+
"success",
|
|
46
|
+
"timed_out",
|
|
47
|
+
"stale",
|
|
48
|
+
"startup_failure"
|
|
49
|
+
]).nullable(),
|
|
50
|
+
html_url: z.url().nullable(),
|
|
51
|
+
details_url: z.url().nullable(),
|
|
52
|
+
external_id: z.string().nullable(),
|
|
53
|
+
started_at: z.string().nullable(),
|
|
54
|
+
completed_at: z.string().nullable()
|
|
55
|
+
});
|
|
56
|
+
const checkRunsSchema = z.object({ check_runs: z.array(checkRunSchema) });
|
|
22
57
|
const labelsSchema = z.array(z.object({ name: z.string().min(1) }));
|
|
23
58
|
const pullRequestCandidateSchema = z.object({
|
|
24
59
|
number: z.number().int().positive(),
|
|
@@ -77,6 +112,36 @@ async function createPullRequest(input) {
|
|
|
77
112
|
prUrl: pull.html_url
|
|
78
113
|
};
|
|
79
114
|
}
|
|
115
|
+
/** Create a check run for one commit. Requires a GitHub App token with checks:write. */
|
|
116
|
+
async function createCheckRun(input) {
|
|
117
|
+
if (input.status === "completed" && input.conclusion === void 0) throw new Error("completed check runs require a conclusion");
|
|
118
|
+
if (input.status !== "completed" && input.conclusion !== void 0) throw new Error("creating a check run with a conclusion requires completed status");
|
|
119
|
+
return parseCheckRun(await gitHubRequest(input, "/check-runs", {
|
|
120
|
+
method: "POST",
|
|
121
|
+
body: JSON.stringify({
|
|
122
|
+
name: input.name,
|
|
123
|
+
head_sha: commitSha(input.headSha),
|
|
124
|
+
...checkRunFields(input)
|
|
125
|
+
})
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
/** Update a check run previously created by the authenticated GitHub App. */
|
|
129
|
+
async function updateCheckRun(input) {
|
|
130
|
+
if (input.status !== void 0 && input.status !== "completed" && input.conclusion !== void 0) throw new Error("a check run conclusion conflicts with an incomplete status");
|
|
131
|
+
return parseCheckRun(await gitHubRequest(input, `/check-runs/${positiveInteger(input.checkRunId, "checkRunId")}`, {
|
|
132
|
+
method: "PATCH",
|
|
133
|
+
body: JSON.stringify({
|
|
134
|
+
...input.name === void 0 ? {} : { name: input.name },
|
|
135
|
+
...checkRunFields(input)
|
|
136
|
+
})
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
/** List check runs reported for a commit, branch, or tag. */
|
|
140
|
+
async function listCheckRunsForRef(input) {
|
|
141
|
+
const query = new URLSearchParams({ per_page: "100" });
|
|
142
|
+
if (input.checkName !== void 0) query.set("check_name", input.checkName);
|
|
143
|
+
return checkRunsSchema.parse(await gitHubRequest(input, `/commits/${encodeURIComponent(input.ref)}/check-runs?${query.toString()}`)).check_runs.map(parseCheckRun);
|
|
144
|
+
}
|
|
80
145
|
/** Read one inline review comment after applying the author policy, which requires write access by default. */
|
|
81
146
|
async function readReviewComment(input) {
|
|
82
147
|
const comment = parseReviewComment(await gitHubRequest(input, `/pulls/comments/${positiveInteger(input.commentId, "commentId")}`));
|
|
@@ -91,6 +156,31 @@ async function answerReviewComment(input) {
|
|
|
91
156
|
body: JSON.stringify({ body: input.body })
|
|
92
157
|
}));
|
|
93
158
|
}
|
|
159
|
+
/** Submit a summary and optional inline comments against a reviewed pull request commit. */
|
|
160
|
+
async function submitPullRequestReview(input) {
|
|
161
|
+
const commitId = z.string().regex(/^[0-9a-f]{40}$/).parse(input.commitId);
|
|
162
|
+
const head = await readPullRequestHead(input);
|
|
163
|
+
if (head.sha !== commitId) throw new Error(`pull request changed after review: expected ${commitId}, found ${head.sha}`);
|
|
164
|
+
const review = submittedReviewSchema.parse(await gitHubRequest(input, `/pulls/${positiveInteger(input.pullRequestNumber, "pullRequestNumber")}/reviews`, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
body: JSON.stringify({
|
|
167
|
+
commit_id: commitId,
|
|
168
|
+
event: input.event,
|
|
169
|
+
body: input.body,
|
|
170
|
+
comments: (input.comments ?? []).map((comment) => ({
|
|
171
|
+
path: comment.path,
|
|
172
|
+
line: positiveInteger(comment.line, "comment line"),
|
|
173
|
+
side: "RIGHT",
|
|
174
|
+
body: comment.body
|
|
175
|
+
}))
|
|
176
|
+
})
|
|
177
|
+
}));
|
|
178
|
+
return {
|
|
179
|
+
id: review.id,
|
|
180
|
+
url: review.html_url,
|
|
181
|
+
state: review.state
|
|
182
|
+
};
|
|
183
|
+
}
|
|
94
184
|
/** Read the current head branch and commit of a pull request. */
|
|
95
185
|
async function readPullRequestHead(input) {
|
|
96
186
|
const pull = pullRequestHeadSchema.parse(await gitHubRequest(input, `/pulls/${positiveInteger(input.pullRequestNumber, "pullRequestNumber")}`));
|
|
@@ -160,6 +250,50 @@ function parseReviewComment(value) {
|
|
|
160
250
|
function parseLabels(value) {
|
|
161
251
|
return labelsSchema.parse(value).map(({ name }) => name);
|
|
162
252
|
}
|
|
253
|
+
function parseCheckRun(value) {
|
|
254
|
+
const checkRun = checkRunSchema.parse(value);
|
|
255
|
+
return {
|
|
256
|
+
id: checkRun.id,
|
|
257
|
+
name: checkRun.name,
|
|
258
|
+
headSha: checkRun.head_sha,
|
|
259
|
+
status: checkRun.status,
|
|
260
|
+
conclusion: checkRun.conclusion,
|
|
261
|
+
url: checkRun.html_url,
|
|
262
|
+
detailsUrl: checkRun.details_url,
|
|
263
|
+
externalId: checkRun.external_id,
|
|
264
|
+
startedAt: checkRun.started_at,
|
|
265
|
+
completedAt: checkRun.completed_at
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function checkRunFields(input) {
|
|
269
|
+
return {
|
|
270
|
+
...input.status === void 0 ? {} : { status: input.status },
|
|
271
|
+
...input.conclusion === void 0 ? {} : { conclusion: input.conclusion },
|
|
272
|
+
...input.detailsUrl === void 0 ? {} : { details_url: input.detailsUrl },
|
|
273
|
+
...input.externalId === void 0 ? {} : { external_id: input.externalId },
|
|
274
|
+
...input.startedAt === void 0 ? {} : { started_at: input.startedAt },
|
|
275
|
+
...input.completedAt === void 0 ? {} : { completed_at: input.completedAt },
|
|
276
|
+
...input.output === void 0 ? {} : { output: checkRunOutput(input.output) }
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function checkRunOutput(output) {
|
|
280
|
+
return {
|
|
281
|
+
title: output.title,
|
|
282
|
+
summary: output.summary,
|
|
283
|
+
...output.text === void 0 ? {} : { text: output.text },
|
|
284
|
+
...output.annotations === void 0 ? {} : { annotations: output.annotations.map((annotation) => ({
|
|
285
|
+
path: annotation.path,
|
|
286
|
+
start_line: positiveInteger(annotation.startLine, "annotation startLine"),
|
|
287
|
+
end_line: positiveInteger(annotation.endLine ?? annotation.startLine, "annotation endLine"),
|
|
288
|
+
...annotation.startColumn === void 0 ? {} : { start_column: positiveInteger(annotation.startColumn, "annotation startColumn") },
|
|
289
|
+
...annotation.endColumn === void 0 ? {} : { end_column: positiveInteger(annotation.endColumn, "annotation endColumn") },
|
|
290
|
+
annotation_level: annotation.annotationLevel,
|
|
291
|
+
message: annotation.message,
|
|
292
|
+
...annotation.title === void 0 ? {} : { title: annotation.title },
|
|
293
|
+
...annotation.rawDetails === void 0 ? {} : { raw_details: annotation.rawDetails }
|
|
294
|
+
})) }
|
|
295
|
+
};
|
|
296
|
+
}
|
|
163
297
|
async function assertReviewCommentAuthor(input, author) {
|
|
164
298
|
if (input.authorPolicy === "any") return;
|
|
165
299
|
const { permission } = collaboratorPermissionSchema.parse(await gitHubRequest(input, `/collaborators/${encodeURIComponent(author)}/permission`));
|
|
@@ -169,6 +303,9 @@ function positiveInteger(value, name) {
|
|
|
169
303
|
if (!Number.isInteger(value) || value < 1) throw new Error(`${name} must be a positive integer`);
|
|
170
304
|
return value;
|
|
171
305
|
}
|
|
306
|
+
function commitSha(value) {
|
|
307
|
+
return z.string().regex(/^[0-9a-f]{40}$/).parse(value);
|
|
308
|
+
}
|
|
172
309
|
/**
|
|
173
310
|
* Verify a GitHub webhook HMAC signature (the x-hub-signature-256 header)
|
|
174
311
|
* against the raw request body. Returns false for missing or malformed
|
|
@@ -201,6 +338,63 @@ function normalizeGitHubDefaultBranchPush(options) {
|
|
|
201
338
|
defaultBranch: parsed.data.repository.default_branch
|
|
202
339
|
};
|
|
203
340
|
}
|
|
341
|
+
const pullRequestEventSchema = z.looseObject({
|
|
342
|
+
action: z.string(),
|
|
343
|
+
number: z.number().int().positive(),
|
|
344
|
+
pull_request: z.looseObject({
|
|
345
|
+
html_url: z.url(),
|
|
346
|
+
draft: z.boolean(),
|
|
347
|
+
user: z.looseObject({ login: z.string().min(1) }),
|
|
348
|
+
head: z.looseObject({
|
|
349
|
+
sha: z.string().regex(/^[0-9a-f]{40}$/),
|
|
350
|
+
ref: z.string().min(1),
|
|
351
|
+
repo: z.looseObject({ full_name: z.string().min(1) })
|
|
352
|
+
}),
|
|
353
|
+
base: z.looseObject({
|
|
354
|
+
sha: z.string().regex(/^[0-9a-f]{40}$/),
|
|
355
|
+
ref: z.string().min(1)
|
|
356
|
+
})
|
|
357
|
+
}),
|
|
358
|
+
repository: z.looseObject({ full_name: z.string().min(1) }),
|
|
359
|
+
sender: z.looseObject({ login: z.string().min(1) })
|
|
360
|
+
});
|
|
361
|
+
const reviewablePullRequestActions = /* @__PURE__ */ new Set([
|
|
362
|
+
"opened",
|
|
363
|
+
"reopened",
|
|
364
|
+
"ready_for_review",
|
|
365
|
+
"synchronize"
|
|
366
|
+
]);
|
|
367
|
+
/** Normalize a newly reviewable or updated pull request into a factory signal. */
|
|
368
|
+
function normalizeGitHubPullRequest(options) {
|
|
369
|
+
if (options.event !== "pull_request") return null;
|
|
370
|
+
const parsed = pullRequestEventSchema.safeParse(options.payload);
|
|
371
|
+
if (!parsed.success || !reviewablePullRequestActions.has(parsed.data.action)) return null;
|
|
372
|
+
if (parsed.data.pull_request.draft && parsed.data.action !== "ready_for_review") return null;
|
|
373
|
+
const slug = parsed.data.repository.full_name.toLowerCase();
|
|
374
|
+
const pull = parsed.data.pull_request;
|
|
375
|
+
return {
|
|
376
|
+
source: "github_pr",
|
|
377
|
+
deliveryId: options.deliveryId,
|
|
378
|
+
repositorySlug: slug,
|
|
379
|
+
replyTo: {
|
|
380
|
+
channel: "github",
|
|
381
|
+
address: `${slug}/pull/${parsed.data.number}`
|
|
382
|
+
},
|
|
383
|
+
pullRequest: {
|
|
384
|
+
number: parsed.data.number,
|
|
385
|
+
url: pull.html_url,
|
|
386
|
+
author: pull.user.login,
|
|
387
|
+
draft: pull.draft,
|
|
388
|
+
headSha: pull.head.sha,
|
|
389
|
+
headBranch: pull.head.ref,
|
|
390
|
+
headRepositorySlug: pull.head.repo.full_name.toLowerCase(),
|
|
391
|
+
baseSha: pull.base.sha,
|
|
392
|
+
baseBranch: pull.base.ref,
|
|
393
|
+
sender: parsed.data.sender.login
|
|
394
|
+
},
|
|
395
|
+
payload: options.payload
|
|
396
|
+
};
|
|
397
|
+
}
|
|
204
398
|
const issueEventSchema = z.looseObject({
|
|
205
399
|
action: z.string(),
|
|
206
400
|
issue: z.looseObject({
|
|
@@ -329,6 +523,6 @@ function normalizeGitHubWorkflowRunFailure(options) {
|
|
|
329
523
|
};
|
|
330
524
|
}
|
|
331
525
|
//#endregion
|
|
332
|
-
export { GitHubReviewCommentAuthorError, addLabel, answerReviewComment, createPullRequest, listConflictedPullRequests, normalizeGitHubDefaultBranchPush, normalizeGitHubIssueOpened, normalizeGitHubPullRequestClosed, normalizeGitHubWorkflowRunFailure, readPullRequestHead, readReviewComment, removeLabel, verifyGitHubSignature };
|
|
526
|
+
export { GitHubReviewCommentAuthorError, addLabel, answerReviewComment, createCheckRun, createPullRequest, listCheckRunsForRef, listConflictedPullRequests, normalizeGitHubDefaultBranchPush, normalizeGitHubIssueOpened, normalizeGitHubPullRequest, normalizeGitHubPullRequestClosed, normalizeGitHubWorkflowRunFailure, readPullRequestHead, readReviewComment, removeLabel, submitPullRequestReview, updateCheckRun, verifyGitHubSignature };
|
|
333
527
|
|
|
334
528
|
//# sourceMappingURL=github.mjs.map
|
package/dist/github.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.mjs","names":[],"sources":["../src/github.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\nimport type { NormalizedSignal, WebhookEvent } from \"./connectors/index\";\n\nconst gitHubApiUrl = \"https://api.github.com\";\nconst repositorySlugPattern = /^[^/\\s]+\\/[^/\\s]+$/;\n\nexport interface GitHubRepositoryRequest {\n repository: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n}\n\nexport interface CreatePullRequestInput extends GitHubRepositoryRequest {\n title: string;\n head: string;\n base: string;\n body?: string;\n draft?: boolean;\n}\n\nexport interface CreatedPullRequest {\n prNumber: number;\n prUrl: string;\n}\n\nexport type ReviewCommentAuthorPolicy = \"write\" | \"any\";\n\ninterface ReviewCommentTrustInput {\n /** Defaults to `write`. Use `any` only when untrusted review text is handled as data. */\n authorPolicy?: ReviewCommentAuthorPolicy;\n}\n\nexport interface ReadReviewCommentInput extends GitHubRepositoryRequest, ReviewCommentTrustInput {\n commentId: number;\n}\n\nexport interface GitHubReviewComment {\n id: number;\n body: string;\n url: string;\n author: string;\n inReplyToId: number | null;\n}\n\nexport interface AnswerReviewCommentInput\n extends GitHubRepositoryRequest,\n ReviewCommentTrustInput {\n pullRequestNumber: number;\n commentId: number;\n body: string;\n}\n\nexport interface ReadPullRequestHeadInput extends GitHubRepositoryRequest {\n pullRequestNumber: number;\n}\n\nexport interface GitHubPullRequestHead {\n branch: string;\n sha: string;\n repository: string;\n}\n\nexport interface PullRequestLabelInput extends GitHubRepositoryRequest {\n pullRequestNumber: number;\n label: string;\n}\n\nexport interface ConflictedPullRequest {\n prNumber: number;\n prUrl: string;\n headBranch: string;\n headSha: string;\n baseBranch: string;\n baseSha: string;\n}\n\nexport type ListConflictedPullRequestsInput = GitHubRepositoryRequest;\n\nconst pullRequestSchema = z.object({\n number: z.number().int().positive(),\n html_url: z.url(),\n});\n\nconst pullRequestHeadSchema = z.object({\n head: z.object({\n ref: z\n .string()\n .regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/)\n .refine((value) => !value.includes(\"..\")),\n sha: z.string().regex(/^[0-9a-f]{40}$/),\n repo: z.object({ full_name: z.string().min(1) }),\n }),\n});\n\nconst reviewCommentSchema = z.object({\n id: z.number().int().positive(),\n body: z.string(),\n html_url: z.url(),\n user: z.object({ login: z.string().min(1) }),\n in_reply_to_id: z.number().int().positive().nullish(),\n});\n\nconst labelsSchema = z.array(z.object({ name: z.string().min(1) }));\nconst pullRequestCandidateSchema = z.object({\n number: z.number().int().positive(),\n user: z.object({ type: z.string().min(1) }),\n head: z.object({ ref: z.string().min(1), repo: z.object({ full_name: z.string().min(1) }) }),\n});\nconst openPullRequestsSchema = z.array(pullRequestCandidateSchema);\nconst pullRequestDetailSchema = pullRequestCandidateSchema.extend({\n html_url: z.url(),\n mergeable: z.boolean().nullable(),\n mergeable_state: z.string().min(1),\n head: pullRequestCandidateSchema.shape.head.extend({ sha: z.string().min(1) }),\n base: z.object({ ref: z.string().min(1), sha: z.string().min(1) }),\n});\nconst collaboratorPermissionSchema = z.object({\n permission: z.enum([\"admin\", \"maintain\", \"write\", \"triage\", \"read\", \"none\"]),\n});\nconst writePermissions = new Set([\"admin\", \"maintain\", \"write\"]);\n\nexport class GitHubReviewCommentAuthorError extends Error {\n readonly author: string;\n\n constructor(author: string) {\n super(`GitHub user ${author} does not have write access to the repository`);\n this.name = \"GitHubReviewCommentAuthorError\";\n this.author = author;\n }\n}\n\n/** Create a pull request from an existing head branch. */\nexport async function createPullRequest(input: CreatePullRequestInput): Promise<CreatedPullRequest> {\n const pull = pullRequestSchema.parse(\n await gitHubRequest(input, \"/pulls\", {\n method: \"POST\",\n body: JSON.stringify({\n title: input.title,\n head: input.head,\n base: input.base,\n ...(input.body === undefined ? {} : { body: input.body }),\n ...(input.draft === undefined ? {} : { draft: input.draft }),\n }),\n }),\n );\n return { prNumber: pull.number, prUrl: pull.html_url };\n}\n\n/** Read one inline review comment after applying the author policy, which requires write access by default. */\nexport async function readReviewComment(\n input: ReadReviewCommentInput,\n): Promise<GitHubReviewComment> {\n const comment = parseReviewComment(\n await gitHubRequest(input, `/pulls/comments/${positiveInteger(input.commentId, \"commentId\")}`),\n );\n await assertReviewCommentAuthor(input, comment.author);\n return comment;\n}\n\n/** Reply to an inline review comment after applying the author policy, which requires write access by default. */\nexport async function answerReviewComment(\n input: AnswerReviewCommentInput,\n): Promise<GitHubReviewComment> {\n await readReviewComment(input);\n return parseReviewComment(\n await gitHubRequest(\n input,\n `/pulls/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/comments/${positiveInteger(input.commentId, \"commentId\")}/replies`,\n { method: \"POST\", body: JSON.stringify({ body: input.body }) },\n ),\n );\n}\n\n/** Read the current head branch and commit of a pull request. */\nexport async function readPullRequestHead(\n input: ReadPullRequestHeadInput,\n): Promise<GitHubPullRequestHead> {\n const pull = pullRequestHeadSchema.parse(\n await gitHubRequest(input, `/pulls/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}`),\n );\n return {\n branch: pull.head.ref,\n sha: pull.head.sha,\n repository: pull.head.repo.full_name.toLowerCase(),\n };\n}\n\n/** List open, same-repository pull requests by one author that GitHub reports as conflicting. */\nexport async function listConflictedPullRequests(\n input: ListConflictedPullRequestsInput,\n): Promise<ConflictedPullRequest[]> {\n const pulls = openPullRequestsSchema.parse(\n await gitHubRequest(input, `/pulls?state=open&per_page=100`),\n );\n const candidates = pulls.filter(\n (pull) =>\n pull.user.type === \"Bot\" &&\n pull.head.ref.startsWith(\"factory/\") &&\n pull.head.repo.full_name.toLowerCase() === input.repository.toLowerCase(),\n );\n const conflicted: ConflictedPullRequest[] = [];\n for (const candidate of candidates) {\n const pull = pullRequestDetailSchema.parse(\n await gitHubRequest(input, `/pulls/${candidate.number}`),\n );\n if (pull.mergeable === false || pull.mergeable_state === \"dirty\") {\n conflicted.push({\n prNumber: pull.number,\n prUrl: pull.html_url,\n headBranch: pull.head.ref,\n headSha: pull.head.sha,\n baseBranch: pull.base.ref,\n baseSha: pull.base.sha,\n });\n }\n }\n return conflicted;\n}\n\n/** Add one label to a pull request. */\nexport async function addLabel(input: PullRequestLabelInput): Promise<string[]> {\n return parseLabels(\n await gitHubRequest(\n input,\n `/issues/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/labels`,\n { method: \"POST\", body: JSON.stringify({ labels: [input.label] }) },\n ),\n );\n}\n\n/** Remove one label from a pull request. */\nexport async function removeLabel(input: PullRequestLabelInput): Promise<string[]> {\n return parseLabels(\n await gitHubRequest(\n input,\n `/issues/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/labels/${encodeURIComponent(input.label)}`,\n { method: \"DELETE\" },\n ),\n );\n}\n\nasync function gitHubRequest(\n input: GitHubRepositoryRequest,\n path: string,\n init: RequestInit = {},\n): Promise<unknown> {\n if (!repositorySlugPattern.test(input.repository)) {\n throw new Error(`invalid GitHub repository slug: ${input.repository}`);\n }\n const response = await (input.fetch ?? globalThis.fetch)(\n `${gitHubApiUrl}/repos/${input.repository}${path}`,\n {\n ...init,\n headers: {\n Accept: \"application/vnd.github+json\",\n Authorization: `Bearer ${input.token}`,\n \"Content-Type\": \"application/json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n ...init.headers,\n },\n },\n );\n const body: unknown = await response.json();\n if (!response.ok) {\n const message = z.object({ message: z.string() }).safeParse(body);\n throw new Error(\n `GitHub API request failed (${response.status}): ${message.success ? message.data.message : response.statusText}`,\n );\n }\n return body;\n}\n\nfunction parseReviewComment(value: unknown): GitHubReviewComment {\n const comment = reviewCommentSchema.parse(value);\n return {\n id: comment.id,\n body: comment.body,\n url: comment.html_url,\n author: comment.user.login,\n inReplyToId: comment.in_reply_to_id ?? null,\n };\n}\n\nfunction parseLabels(value: unknown): string[] {\n return labelsSchema.parse(value).map(({ name }) => name);\n}\n\nasync function assertReviewCommentAuthor(\n input: GitHubRepositoryRequest & ReviewCommentTrustInput,\n author: string,\n): Promise<void> {\n if (input.authorPolicy === \"any\") {\n return;\n }\n const { permission } = collaboratorPermissionSchema.parse(\n await gitHubRequest(input, `/collaborators/${encodeURIComponent(author)}/permission`),\n );\n if (!writePermissions.has(permission)) {\n throw new GitHubReviewCommentAuthorError(author);\n }\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new Error(`${name} must be a positive integer`);\n }\n return value;\n}\n\n/**\n * Verify a GitHub webhook HMAC signature (the x-hub-signature-256 header)\n * against the raw request body. Returns false for missing or malformed\n * signatures instead of throwing.\n */\nexport function verifyGitHubSignature(options: {\n secret: string;\n payload: string;\n signature: string | null;\n}): boolean {\n const { secret, payload, signature } = options;\n if (signature === null || !signature.startsWith(\"sha256=\")) {\n return false;\n }\n const expected = createHmac(\"sha256\", secret).update(payload).digest(\"hex\");\n const provided = signature.slice(\"sha256=\".length);\n if (provided.length !== expected.length) {\n return false;\n }\n return timingSafeEqual(Buffer.from(provided, \"hex\"), Buffer.from(expected, \"hex\"));\n}\n\nconst pushEventSchema = z.looseObject({\n ref: z.string().min(1),\n repository: z.looseObject({\n default_branch: z.string().min(1),\n full_name: z.string().min(1),\n }),\n});\n\nexport interface GitHubDefaultBranchPush {\n source: \"github_default_branch_push\";\n deliveryId: string;\n repositorySlug: string;\n defaultBranch: string;\n}\n\n/** Normalize a push to a repository's default branch for deterministic reconciliation. */\nexport function normalizeGitHubDefaultBranchPush(options: {\n deliveryId: string;\n event: string;\n payload: unknown;\n}): GitHubDefaultBranchPush | null {\n if (options.event !== \"push\") return null;\n const parsed = pushEventSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.ref !== `refs/heads/${parsed.data.repository.default_branch}`) {\n return null;\n }\n return {\n source: \"github_default_branch_push\",\n deliveryId: options.deliveryId,\n repositorySlug: parsed.data.repository.full_name.toLowerCase(),\n defaultBranch: parsed.data.repository.default_branch,\n };\n}\n\nconst issueEventSchema = z.looseObject({\n action: z.string(),\n issue: z.looseObject({\n number: z.number().int().min(1),\n title: z.string(),\n body: z.string().nullable(),\n }),\n repository: z.looseObject({\n full_name: z.string().min(1),\n }),\n});\n\nexport interface GitHubIssueSignal extends NormalizedSignal<\"github_issue\"> {\n repositorySlug: string;\n issue: { number: number; title: string; body: string | null };\n}\n\nconst failedWorkflowConclusions = new Set([\n \"action_required\",\n \"failure\",\n \"stale\",\n \"startup_failure\",\n \"timed_out\",\n]);\n\nconst workflowRunEventSchema = z.looseObject({\n action: z.string(),\n repository: z.looseObject({\n default_branch: z.string().min(1),\n full_name: z.string().min(1),\n }),\n workflow_run: z.looseObject({\n id: z.number().int().min(1),\n name: z.string().min(1),\n event: z.string().min(1),\n head_branch: z.string().min(1).nullable(),\n head_repository: z\n .looseObject({\n full_name: z.string().min(1),\n })\n .nullable(),\n head_sha: z.string().min(1),\n html_url: z.url(),\n conclusion: z.string().nullable(),\n run_attempt: z.number().int().min(1),\n }),\n});\n\nexport interface GitHubCIFailureSignal extends NormalizedSignal<\"github_ci_failure\"> {\n repositoryDefaultBranch: string;\n repositorySlug: string;\n workflowRun: {\n id: number;\n name: string;\n trigger: string;\n branch: string | null;\n headRepositorySlug: string | null;\n headSha: string;\n url: string;\n conclusion: string;\n attempt: number;\n };\n}\n\n/**\n * Normalize a GitHub issues webhook into the factory's signal shape. Returns\n * null for payloads that are not an opened issue. The caller supplies the\n * repository id; the payload stays untrusted data.\n */\nexport function normalizeGitHubIssueOpened(options: WebhookEvent): GitHubIssueSignal | null {\n if (options.event !== \"issues\") {\n return null;\n }\n const parsed = issueEventSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.action !== \"opened\") {\n return null;\n }\n const slug = parsed.data.repository.full_name.toLowerCase();\n const { number, title, body } = parsed.data.issue;\n return {\n source: \"github_issue\",\n deliveryId: options.deliveryId,\n repositorySlug: slug,\n replyTo: { channel: \"github\", address: `${slug}#${number}` },\n issue: { number, title, body },\n payload: options.payload,\n };\n}\n\nconst pullRequestClosedSchema = z.looseObject({\n action: z.string(),\n pull_request: z.looseObject({\n number: z.number().int().min(1),\n html_url: z.url(),\n merged: z.boolean(),\n merged_by: z.looseObject({ login: z.string().min(1) }).nullable().optional(),\n merge_commit_sha: z\n .string()\n .regex(/^[0-9a-f]{40}$/)\n .nullable()\n .optional(),\n }),\n repository: z.looseObject({\n full_name: z.string().min(1),\n }),\n sender: z.looseObject({ login: z.string().min(1) }),\n});\n\nexport interface GitHubPullRequestClosed {\n source: \"github_pull_request_closed\";\n deliveryId: string;\n repositorySlug: string;\n prNumber: number;\n prUrl: string;\n merged: boolean;\n /** The merge commit on the base branch; null when closed without merging. */\n mergeCommitSha: string | null;\n /** The GitHub login that merged the pull request, or closed it without merging. */\n decidedBy: string;\n}\n\n/**\n * Normalize a closed pull_request webhook so the factory can observe what\n * happened to its change: merged records merged_by (the sender when GitHub\n * omits it), a close without merge records the sender.\n */\nexport function normalizeGitHubPullRequestClosed(\n options: WebhookEvent,\n): GitHubPullRequestClosed | null {\n if (options.event !== \"pull_request\") {\n return null;\n }\n const parsed = pullRequestClosedSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.action !== \"closed\") {\n return null;\n }\n const pr = parsed.data.pull_request;\n const sender = parsed.data.sender.login;\n return {\n source: \"github_pull_request_closed\",\n deliveryId: options.deliveryId,\n repositorySlug: parsed.data.repository.full_name.toLowerCase(),\n prNumber: pr.number,\n prUrl: pr.html_url,\n merged: pr.merged,\n mergeCommitSha: pr.merged ? (pr.merge_commit_sha ?? null) : null,\n decidedBy: pr.merged ? (pr.merged_by?.login ?? sender) : sender,\n };\n}\n\n/** Normalize a completed, unsuccessful GitHub Actions run into a CI failure signal. */\nexport function normalizeGitHubWorkflowRunFailure(\n options: WebhookEvent,\n): GitHubCIFailureSignal | null {\n if (options.event !== \"workflow_run\") {\n return null;\n }\n const parsed = workflowRunEventSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.action !== \"completed\") {\n return null;\n }\n\n const run = parsed.data.workflow_run;\n const conclusion = run.conclusion;\n if (conclusion === null || !failedWorkflowConclusions.has(conclusion)) {\n return null;\n }\n const slug = parsed.data.repository.full_name.toLowerCase();\n return {\n source: \"github_ci_failure\",\n deliveryId: options.deliveryId,\n repositoryDefaultBranch: parsed.data.repository.default_branch,\n repositorySlug: slug,\n replyTo: { channel: \"github\", address: `${slug}/actions/runs/${run.id}` },\n workflowRun: {\n id: run.id,\n name: run.name,\n trigger: run.event,\n branch: run.head_branch,\n headRepositorySlug: run.head_repository?.full_name.toLowerCase() ?? null,\n headSha: run.head_sha,\n url: run.html_url,\n conclusion,\n attempt: run.run_attempt,\n },\n payload: options.payload,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"github.mjs","names":[],"sources":["../src/github.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\nimport type { NormalizedSignal, WebhookEvent } from \"./connectors/index\";\n\nconst gitHubApiUrl = \"https://api.github.com\";\nconst repositorySlugPattern = /^[^/\\s]+\\/[^/\\s]+$/;\n\nexport interface GitHubRepositoryRequest {\n repository: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n}\n\nexport interface CreatePullRequestInput extends GitHubRepositoryRequest {\n title: string;\n head: string;\n base: string;\n body?: string;\n draft?: boolean;\n}\n\nexport interface CreatedPullRequest {\n prNumber: number;\n prUrl: string;\n}\n\nexport type ReviewCommentAuthorPolicy = \"write\" | \"any\";\n\ninterface ReviewCommentTrustInput {\n /** Defaults to `write`. Use `any` only when untrusted review text is handled as data. */\n authorPolicy?: ReviewCommentAuthorPolicy;\n}\n\nexport interface ReadReviewCommentInput extends GitHubRepositoryRequest, ReviewCommentTrustInput {\n commentId: number;\n}\n\nexport interface GitHubReviewComment {\n id: number;\n body: string;\n url: string;\n author: string;\n inReplyToId: number | null;\n}\n\nexport interface AnswerReviewCommentInput\n extends GitHubRepositoryRequest,\n ReviewCommentTrustInput {\n pullRequestNumber: number;\n commentId: number;\n body: string;\n}\n\nexport interface ReadPullRequestHeadInput extends GitHubRepositoryRequest {\n pullRequestNumber: number;\n}\n\nexport interface GitHubPullRequestHead {\n branch: string;\n sha: string;\n repository: string;\n}\n\nexport type PullRequestReviewEvent = \"APPROVE\" | \"REQUEST_CHANGES\" | \"COMMENT\";\n\nexport interface PullRequestReviewComment {\n path: string;\n line: number;\n body: string;\n}\n\nexport interface SubmitPullRequestReviewInput extends GitHubRepositoryRequest {\n pullRequestNumber: number;\n commitId: string;\n event: PullRequestReviewEvent;\n body: string;\n comments?: readonly PullRequestReviewComment[];\n}\n\nexport interface SubmittedPullRequestReview {\n id: number;\n url: string;\n state: string;\n}\n\nexport interface PullRequestLabelInput extends GitHubRepositoryRequest {\n pullRequestNumber: number;\n label: string;\n}\n\nexport interface ConflictedPullRequest {\n prNumber: number;\n prUrl: string;\n headBranch: string;\n headSha: string;\n baseBranch: string;\n baseSha: string;\n}\n\nexport type ListConflictedPullRequestsInput = GitHubRepositoryRequest;\n\nexport type CheckRunStatus = \"queued\" | \"in_progress\" | \"completed\";\nexport type GitHubCheckRunStatus = CheckRunStatus | \"waiting\" | \"requested\" | \"pending\";\nexport type CheckRunConclusion =\n | \"action_required\"\n | \"cancelled\"\n | \"failure\"\n | \"neutral\"\n | \"skipped\"\n | \"success\"\n | \"timed_out\";\nexport type CheckRunAnnotationLevel = \"notice\" | \"warning\" | \"failure\";\n\nexport interface CheckRunAnnotation {\n path: string;\n startLine: number;\n endLine?: number;\n startColumn?: number;\n endColumn?: number;\n annotationLevel: CheckRunAnnotationLevel;\n message: string;\n title?: string;\n rawDetails?: string;\n}\n\nexport interface CheckRunOutput {\n title: string;\n summary: string;\n text?: string;\n annotations?: readonly CheckRunAnnotation[];\n}\n\ninterface CheckRunFields {\n status?: CheckRunStatus;\n conclusion?: CheckRunConclusion;\n detailsUrl?: string;\n externalId?: string;\n startedAt?: string;\n completedAt?: string;\n output?: CheckRunOutput;\n}\n\nexport interface CreateCheckRunInput extends GitHubRepositoryRequest, CheckRunFields {\n name: string;\n headSha: string;\n}\n\nexport interface UpdateCheckRunInput extends GitHubRepositoryRequest, CheckRunFields {\n checkRunId: number;\n name?: string;\n}\n\nexport interface ListCheckRunsForRefInput extends GitHubRepositoryRequest {\n ref: string;\n checkName?: string;\n}\n\nexport interface GitHubCheckRun {\n id: number;\n name: string;\n headSha: string;\n status: GitHubCheckRunStatus;\n conclusion: CheckRunConclusion | \"stale\" | \"startup_failure\" | null;\n url: string | null;\n detailsUrl: string | null;\n externalId: string | null;\n startedAt: string | null;\n completedAt: string | null;\n}\n\nconst pullRequestSchema = z.object({\n number: z.number().int().positive(),\n html_url: z.url(),\n});\n\nconst pullRequestHeadSchema = z.object({\n head: z.object({\n ref: z\n .string()\n .regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/)\n .refine((value) => !value.includes(\"..\")),\n sha: z.string().regex(/^[0-9a-f]{40}$/),\n repo: z.object({ full_name: z.string().min(1) }),\n }),\n});\n\nconst reviewCommentSchema = z.object({\n id: z.number().int().positive(),\n body: z.string(),\n html_url: z.url(),\n user: z.object({ login: z.string().min(1) }),\n in_reply_to_id: z.number().int().positive().nullish(),\n});\n\nconst submittedReviewSchema = z.object({\n id: z.number().int().positive(),\n html_url: z.url(),\n state: z.string().min(1),\n});\n\nconst checkRunSchema = z.object({\n id: z.number().int().positive(),\n name: z.string().min(1),\n head_sha: z.string().regex(/^[0-9a-f]{40}$/),\n status: z.enum([\"queued\", \"in_progress\", \"completed\", \"waiting\", \"requested\", \"pending\"]),\n conclusion: z\n .enum([\n \"action_required\",\n \"cancelled\",\n \"failure\",\n \"neutral\",\n \"skipped\",\n \"success\",\n \"timed_out\",\n \"stale\",\n \"startup_failure\",\n ])\n .nullable(),\n html_url: z.url().nullable(),\n details_url: z.url().nullable(),\n external_id: z.string().nullable(),\n started_at: z.string().nullable(),\n completed_at: z.string().nullable(),\n});\n\nconst checkRunsSchema = z.object({ check_runs: z.array(checkRunSchema) });\nconst labelsSchema = z.array(z.object({ name: z.string().min(1) }));\nconst pullRequestCandidateSchema = z.object({\n number: z.number().int().positive(),\n user: z.object({ type: z.string().min(1) }),\n head: z.object({ ref: z.string().min(1), repo: z.object({ full_name: z.string().min(1) }) }),\n});\nconst openPullRequestsSchema = z.array(pullRequestCandidateSchema);\nconst pullRequestDetailSchema = pullRequestCandidateSchema.extend({\n html_url: z.url(),\n mergeable: z.boolean().nullable(),\n mergeable_state: z.string().min(1),\n head: pullRequestCandidateSchema.shape.head.extend({ sha: z.string().min(1) }),\n base: z.object({ ref: z.string().min(1), sha: z.string().min(1) }),\n});\nconst collaboratorPermissionSchema = z.object({\n permission: z.enum([\"admin\", \"maintain\", \"write\", \"triage\", \"read\", \"none\"]),\n});\nconst writePermissions = new Set([\"admin\", \"maintain\", \"write\"]);\n\nexport class GitHubReviewCommentAuthorError extends Error {\n readonly author: string;\n\n constructor(author: string) {\n super(`GitHub user ${author} does not have write access to the repository`);\n this.name = \"GitHubReviewCommentAuthorError\";\n this.author = author;\n }\n}\n\n/** Create a pull request from an existing head branch. */\nexport async function createPullRequest(input: CreatePullRequestInput): Promise<CreatedPullRequest> {\n const pull = pullRequestSchema.parse(\n await gitHubRequest(input, \"/pulls\", {\n method: \"POST\",\n body: JSON.stringify({\n title: input.title,\n head: input.head,\n base: input.base,\n ...(input.body === undefined ? {} : { body: input.body }),\n ...(input.draft === undefined ? {} : { draft: input.draft }),\n }),\n }),\n );\n return { prNumber: pull.number, prUrl: pull.html_url };\n}\n\n/** Create a check run for one commit. Requires a GitHub App token with checks:write. */\nexport async function createCheckRun(input: CreateCheckRunInput): Promise<GitHubCheckRun> {\n if (input.status === \"completed\" && input.conclusion === undefined) {\n throw new Error(\"completed check runs require a conclusion\");\n }\n if (input.status !== \"completed\" && input.conclusion !== undefined) {\n throw new Error(\"creating a check run with a conclusion requires completed status\");\n }\n return parseCheckRun(\n await gitHubRequest(input, \"/check-runs\", {\n method: \"POST\",\n body: JSON.stringify({\n name: input.name,\n head_sha: commitSha(input.headSha),\n ...checkRunFields(input),\n }),\n }),\n );\n}\n\n/** Update a check run previously created by the authenticated GitHub App. */\nexport async function updateCheckRun(input: UpdateCheckRunInput): Promise<GitHubCheckRun> {\n if (input.status !== undefined && input.status !== \"completed\" && input.conclusion !== undefined) {\n throw new Error(\"a check run conclusion conflicts with an incomplete status\");\n }\n return parseCheckRun(\n await gitHubRequest(input, `/check-runs/${positiveInteger(input.checkRunId, \"checkRunId\")}`, {\n method: \"PATCH\",\n body: JSON.stringify({\n ...(input.name === undefined ? {} : { name: input.name }),\n ...checkRunFields(input),\n }),\n }),\n );\n}\n\n/** List check runs reported for a commit, branch, or tag. */\nexport async function listCheckRunsForRef(input: ListCheckRunsForRefInput): Promise<GitHubCheckRun[]> {\n const query = new URLSearchParams({ per_page: \"100\" });\n if (input.checkName !== undefined) query.set(\"check_name\", input.checkName);\n const response = checkRunsSchema.parse(\n await gitHubRequest(input, `/commits/${encodeURIComponent(input.ref)}/check-runs?${query.toString()}`),\n );\n return response.check_runs.map(parseCheckRun);\n}\n\n/** Read one inline review comment after applying the author policy, which requires write access by default. */\nexport async function readReviewComment(\n input: ReadReviewCommentInput,\n): Promise<GitHubReviewComment> {\n const comment = parseReviewComment(\n await gitHubRequest(input, `/pulls/comments/${positiveInteger(input.commentId, \"commentId\")}`),\n );\n await assertReviewCommentAuthor(input, comment.author);\n return comment;\n}\n\n/** Reply to an inline review comment after applying the author policy, which requires write access by default. */\nexport async function answerReviewComment(\n input: AnswerReviewCommentInput,\n): Promise<GitHubReviewComment> {\n await readReviewComment(input);\n return parseReviewComment(\n await gitHubRequest(\n input,\n `/pulls/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/comments/${positiveInteger(input.commentId, \"commentId\")}/replies`,\n { method: \"POST\", body: JSON.stringify({ body: input.body }) },\n ),\n );\n}\n\n/** Submit a summary and optional inline comments against a reviewed pull request commit. */\nexport async function submitPullRequestReview(\n input: SubmitPullRequestReviewInput,\n): Promise<SubmittedPullRequestReview> {\n const commitId = z.string().regex(/^[0-9a-f]{40}$/).parse(input.commitId);\n const head = await readPullRequestHead(input);\n if (head.sha !== commitId) {\n throw new Error(`pull request changed after review: expected ${commitId}, found ${head.sha}`);\n }\n const review = submittedReviewSchema.parse(\n await gitHubRequest(\n input,\n `/pulls/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/reviews`,\n {\n method: \"POST\",\n body: JSON.stringify({\n commit_id: commitId,\n event: input.event,\n body: input.body,\n comments: (input.comments ?? []).map((comment) => ({\n path: comment.path,\n line: positiveInteger(comment.line, \"comment line\"),\n side: \"RIGHT\",\n body: comment.body,\n })),\n }),\n },\n ),\n );\n return { id: review.id, url: review.html_url, state: review.state };\n}\n\n/** Read the current head branch and commit of a pull request. */\nexport async function readPullRequestHead(\n input: ReadPullRequestHeadInput,\n): Promise<GitHubPullRequestHead> {\n const pull = pullRequestHeadSchema.parse(\n await gitHubRequest(input, `/pulls/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}`),\n );\n return {\n branch: pull.head.ref,\n sha: pull.head.sha,\n repository: pull.head.repo.full_name.toLowerCase(),\n };\n}\n\n/** List open, same-repository pull requests by one author that GitHub reports as conflicting. */\nexport async function listConflictedPullRequests(\n input: ListConflictedPullRequestsInput,\n): Promise<ConflictedPullRequest[]> {\n const pulls = openPullRequestsSchema.parse(\n await gitHubRequest(input, `/pulls?state=open&per_page=100`),\n );\n const candidates = pulls.filter(\n (pull) =>\n pull.user.type === \"Bot\" &&\n pull.head.ref.startsWith(\"factory/\") &&\n pull.head.repo.full_name.toLowerCase() === input.repository.toLowerCase(),\n );\n const conflicted: ConflictedPullRequest[] = [];\n for (const candidate of candidates) {\n const pull = pullRequestDetailSchema.parse(\n await gitHubRequest(input, `/pulls/${candidate.number}`),\n );\n if (pull.mergeable === false || pull.mergeable_state === \"dirty\") {\n conflicted.push({\n prNumber: pull.number,\n prUrl: pull.html_url,\n headBranch: pull.head.ref,\n headSha: pull.head.sha,\n baseBranch: pull.base.ref,\n baseSha: pull.base.sha,\n });\n }\n }\n return conflicted;\n}\n\n/** Add one label to a pull request. */\nexport async function addLabel(input: PullRequestLabelInput): Promise<string[]> {\n return parseLabels(\n await gitHubRequest(\n input,\n `/issues/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/labels`,\n { method: \"POST\", body: JSON.stringify({ labels: [input.label] }) },\n ),\n );\n}\n\n/** Remove one label from a pull request. */\nexport async function removeLabel(input: PullRequestLabelInput): Promise<string[]> {\n return parseLabels(\n await gitHubRequest(\n input,\n `/issues/${positiveInteger(input.pullRequestNumber, \"pullRequestNumber\")}/labels/${encodeURIComponent(input.label)}`,\n { method: \"DELETE\" },\n ),\n );\n}\n\nasync function gitHubRequest(\n input: GitHubRepositoryRequest,\n path: string,\n init: RequestInit = {},\n): Promise<unknown> {\n if (!repositorySlugPattern.test(input.repository)) {\n throw new Error(`invalid GitHub repository slug: ${input.repository}`);\n }\n const response = await (input.fetch ?? globalThis.fetch)(\n `${gitHubApiUrl}/repos/${input.repository}${path}`,\n {\n ...init,\n headers: {\n Accept: \"application/vnd.github+json\",\n Authorization: `Bearer ${input.token}`,\n \"Content-Type\": \"application/json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n ...init.headers,\n },\n },\n );\n const body: unknown = await response.json();\n if (!response.ok) {\n const message = z.object({ message: z.string() }).safeParse(body);\n throw new Error(\n `GitHub API request failed (${response.status}): ${message.success ? message.data.message : response.statusText}`,\n );\n }\n return body;\n}\n\nfunction parseReviewComment(value: unknown): GitHubReviewComment {\n const comment = reviewCommentSchema.parse(value);\n return {\n id: comment.id,\n body: comment.body,\n url: comment.html_url,\n author: comment.user.login,\n inReplyToId: comment.in_reply_to_id ?? null,\n };\n}\n\nfunction parseLabels(value: unknown): string[] {\n return labelsSchema.parse(value).map(({ name }) => name);\n}\n\nfunction parseCheckRun(value: unknown): GitHubCheckRun {\n const checkRun = checkRunSchema.parse(value);\n return {\n id: checkRun.id,\n name: checkRun.name,\n headSha: checkRun.head_sha,\n status: checkRun.status,\n conclusion: checkRun.conclusion,\n url: checkRun.html_url,\n detailsUrl: checkRun.details_url,\n externalId: checkRun.external_id,\n startedAt: checkRun.started_at,\n completedAt: checkRun.completed_at,\n };\n}\n\nfunction checkRunFields(input: CheckRunFields): Record<string, unknown> {\n return {\n ...(input.status === undefined ? {} : { status: input.status }),\n ...(input.conclusion === undefined ? {} : { conclusion: input.conclusion }),\n ...(input.detailsUrl === undefined ? {} : { details_url: input.detailsUrl }),\n ...(input.externalId === undefined ? {} : { external_id: input.externalId }),\n ...(input.startedAt === undefined ? {} : { started_at: input.startedAt }),\n ...(input.completedAt === undefined ? {} : { completed_at: input.completedAt }),\n ...(input.output === undefined ? {} : { output: checkRunOutput(input.output) }),\n };\n}\n\nfunction checkRunOutput(output: CheckRunOutput): Record<string, unknown> {\n return {\n title: output.title,\n summary: output.summary,\n ...(output.text === undefined ? {} : { text: output.text }),\n ...(output.annotations === undefined\n ? {}\n : {\n annotations: output.annotations.map((annotation) => ({\n path: annotation.path,\n start_line: positiveInteger(annotation.startLine, \"annotation startLine\"),\n end_line: positiveInteger(annotation.endLine ?? annotation.startLine, \"annotation endLine\"),\n ...(annotation.startColumn === undefined\n ? {}\n : { start_column: positiveInteger(annotation.startColumn, \"annotation startColumn\") }),\n ...(annotation.endColumn === undefined\n ? {}\n : { end_column: positiveInteger(annotation.endColumn, \"annotation endColumn\") }),\n annotation_level: annotation.annotationLevel,\n message: annotation.message,\n ...(annotation.title === undefined ? {} : { title: annotation.title }),\n ...(annotation.rawDetails === undefined ? {} : { raw_details: annotation.rawDetails }),\n })),\n }),\n };\n}\n\nasync function assertReviewCommentAuthor(\n input: GitHubRepositoryRequest & ReviewCommentTrustInput,\n author: string,\n): Promise<void> {\n if (input.authorPolicy === \"any\") {\n return;\n }\n const { permission } = collaboratorPermissionSchema.parse(\n await gitHubRequest(input, `/collaborators/${encodeURIComponent(author)}/permission`),\n );\n if (!writePermissions.has(permission)) {\n throw new GitHubReviewCommentAuthorError(author);\n }\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new Error(`${name} must be a positive integer`);\n }\n return value;\n}\n\nfunction commitSha(value: string): string {\n return z.string().regex(/^[0-9a-f]{40}$/).parse(value);\n}\n\n/**\n * Verify a GitHub webhook HMAC signature (the x-hub-signature-256 header)\n * against the raw request body. Returns false for missing or malformed\n * signatures instead of throwing.\n */\nexport function verifyGitHubSignature(options: {\n secret: string;\n payload: string;\n signature: string | null;\n}): boolean {\n const { secret, payload, signature } = options;\n if (signature === null || !signature.startsWith(\"sha256=\")) {\n return false;\n }\n const expected = createHmac(\"sha256\", secret).update(payload).digest(\"hex\");\n const provided = signature.slice(\"sha256=\".length);\n if (provided.length !== expected.length) {\n return false;\n }\n return timingSafeEqual(Buffer.from(provided, \"hex\"), Buffer.from(expected, \"hex\"));\n}\n\nconst pushEventSchema = z.looseObject({\n ref: z.string().min(1),\n repository: z.looseObject({\n default_branch: z.string().min(1),\n full_name: z.string().min(1),\n }),\n});\n\nexport interface GitHubDefaultBranchPush {\n source: \"github_default_branch_push\";\n deliveryId: string;\n repositorySlug: string;\n defaultBranch: string;\n}\n\n/** Normalize a push to a repository's default branch for deterministic reconciliation. */\nexport function normalizeGitHubDefaultBranchPush(options: {\n deliveryId: string;\n event: string;\n payload: unknown;\n}): GitHubDefaultBranchPush | null {\n if (options.event !== \"push\") return null;\n const parsed = pushEventSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.ref !== `refs/heads/${parsed.data.repository.default_branch}`) {\n return null;\n }\n return {\n source: \"github_default_branch_push\",\n deliveryId: options.deliveryId,\n repositorySlug: parsed.data.repository.full_name.toLowerCase(),\n defaultBranch: parsed.data.repository.default_branch,\n };\n}\n\nconst pullRequestEventSchema = z.looseObject({\n action: z.string(),\n number: z.number().int().positive(),\n pull_request: z.looseObject({\n html_url: z.url(),\n draft: z.boolean(),\n user: z.looseObject({ login: z.string().min(1) }),\n head: z.looseObject({\n sha: z.string().regex(/^[0-9a-f]{40}$/),\n ref: z.string().min(1),\n repo: z.looseObject({ full_name: z.string().min(1) }),\n }),\n base: z.looseObject({\n sha: z.string().regex(/^[0-9a-f]{40}$/),\n ref: z.string().min(1),\n }),\n }),\n repository: z.looseObject({ full_name: z.string().min(1) }),\n sender: z.looseObject({ login: z.string().min(1) }),\n});\n\nconst reviewablePullRequestActions = new Set([\"opened\", \"reopened\", \"ready_for_review\", \"synchronize\"]);\n\nexport interface GitHubPullRequestSignal extends NormalizedSignal<\"github_pr\"> {\n repositorySlug: string;\n pullRequest: {\n number: number;\n url: string;\n author: string;\n draft: boolean;\n headSha: string;\n headBranch: string;\n headRepositorySlug: string;\n baseSha: string;\n baseBranch: string;\n sender: string;\n };\n}\n\n/** Normalize a newly reviewable or updated pull request into a factory signal. */\nexport function normalizeGitHubPullRequest(options: WebhookEvent): GitHubPullRequestSignal | null {\n if (options.event !== \"pull_request\") return null;\n const parsed = pullRequestEventSchema.safeParse(options.payload);\n if (!parsed.success || !reviewablePullRequestActions.has(parsed.data.action)) return null;\n if (parsed.data.pull_request.draft && parsed.data.action !== \"ready_for_review\") return null;\n\n const slug = parsed.data.repository.full_name.toLowerCase();\n const pull = parsed.data.pull_request;\n return {\n source: \"github_pr\",\n deliveryId: options.deliveryId,\n repositorySlug: slug,\n replyTo: { channel: \"github\", address: `${slug}/pull/${parsed.data.number}` },\n pullRequest: {\n number: parsed.data.number,\n url: pull.html_url,\n author: pull.user.login,\n draft: pull.draft,\n headSha: pull.head.sha,\n headBranch: pull.head.ref,\n headRepositorySlug: pull.head.repo.full_name.toLowerCase(),\n baseSha: pull.base.sha,\n baseBranch: pull.base.ref,\n sender: parsed.data.sender.login,\n },\n payload: options.payload,\n };\n}\n\nconst issueEventSchema = z.looseObject({\n action: z.string(),\n issue: z.looseObject({\n number: z.number().int().min(1),\n title: z.string(),\n body: z.string().nullable(),\n }),\n repository: z.looseObject({\n full_name: z.string().min(1),\n }),\n});\n\nexport interface GitHubIssueSignal extends NormalizedSignal<\"github_issue\"> {\n repositorySlug: string;\n issue: { number: number; title: string; body: string | null };\n}\n\nconst failedWorkflowConclusions = new Set([\n \"action_required\",\n \"failure\",\n \"stale\",\n \"startup_failure\",\n \"timed_out\",\n]);\n\nconst workflowRunEventSchema = z.looseObject({\n action: z.string(),\n repository: z.looseObject({\n default_branch: z.string().min(1),\n full_name: z.string().min(1),\n }),\n workflow_run: z.looseObject({\n id: z.number().int().min(1),\n name: z.string().min(1),\n event: z.string().min(1),\n head_branch: z.string().min(1).nullable(),\n head_repository: z\n .looseObject({\n full_name: z.string().min(1),\n })\n .nullable(),\n head_sha: z.string().min(1),\n html_url: z.url(),\n conclusion: z.string().nullable(),\n run_attempt: z.number().int().min(1),\n }),\n});\n\nexport interface GitHubCIFailureSignal extends NormalizedSignal<\"github_ci_failure\"> {\n repositoryDefaultBranch: string;\n repositorySlug: string;\n workflowRun: {\n id: number;\n name: string;\n trigger: string;\n branch: string | null;\n headRepositorySlug: string | null;\n headSha: string;\n url: string;\n conclusion: string;\n attempt: number;\n };\n}\n\n/**\n * Normalize a GitHub issues webhook into the factory's signal shape. Returns\n * null for payloads that are not an opened issue. The caller supplies the\n * repository id; the payload stays untrusted data.\n */\nexport function normalizeGitHubIssueOpened(options: WebhookEvent): GitHubIssueSignal | null {\n if (options.event !== \"issues\") {\n return null;\n }\n const parsed = issueEventSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.action !== \"opened\") {\n return null;\n }\n const slug = parsed.data.repository.full_name.toLowerCase();\n const { number, title, body } = parsed.data.issue;\n return {\n source: \"github_issue\",\n deliveryId: options.deliveryId,\n repositorySlug: slug,\n replyTo: { channel: \"github\", address: `${slug}#${number}` },\n issue: { number, title, body },\n payload: options.payload,\n };\n}\n\nconst pullRequestClosedSchema = z.looseObject({\n action: z.string(),\n pull_request: z.looseObject({\n number: z.number().int().min(1),\n html_url: z.url(),\n merged: z.boolean(),\n merged_by: z.looseObject({ login: z.string().min(1) }).nullable().optional(),\n merge_commit_sha: z\n .string()\n .regex(/^[0-9a-f]{40}$/)\n .nullable()\n .optional(),\n }),\n repository: z.looseObject({\n full_name: z.string().min(1),\n }),\n sender: z.looseObject({ login: z.string().min(1) }),\n});\n\nexport interface GitHubPullRequestClosed {\n source: \"github_pull_request_closed\";\n deliveryId: string;\n repositorySlug: string;\n prNumber: number;\n prUrl: string;\n merged: boolean;\n /** The merge commit on the base branch; null when closed without merging. */\n mergeCommitSha: string | null;\n /** The GitHub login that merged the pull request, or closed it without merging. */\n decidedBy: string;\n}\n\n/**\n * Normalize a closed pull_request webhook so the factory can observe what\n * happened to its change: merged records merged_by (the sender when GitHub\n * omits it), a close without merge records the sender.\n */\nexport function normalizeGitHubPullRequestClosed(\n options: WebhookEvent,\n): GitHubPullRequestClosed | null {\n if (options.event !== \"pull_request\") {\n return null;\n }\n const parsed = pullRequestClosedSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.action !== \"closed\") {\n return null;\n }\n const pr = parsed.data.pull_request;\n const sender = parsed.data.sender.login;\n return {\n source: \"github_pull_request_closed\",\n deliveryId: options.deliveryId,\n repositorySlug: parsed.data.repository.full_name.toLowerCase(),\n prNumber: pr.number,\n prUrl: pr.html_url,\n merged: pr.merged,\n mergeCommitSha: pr.merged ? (pr.merge_commit_sha ?? null) : null,\n decidedBy: pr.merged ? (pr.merged_by?.login ?? sender) : sender,\n };\n}\n\n/** Normalize a completed, unsuccessful GitHub Actions run into a CI failure signal. */\nexport function normalizeGitHubWorkflowRunFailure(\n options: WebhookEvent,\n): GitHubCIFailureSignal | null {\n if (options.event !== \"workflow_run\") {\n return null;\n }\n const parsed = workflowRunEventSchema.safeParse(options.payload);\n if (!parsed.success || parsed.data.action !== \"completed\") {\n return null;\n }\n\n const run = parsed.data.workflow_run;\n const conclusion = run.conclusion;\n if (conclusion === null || !failedWorkflowConclusions.has(conclusion)) {\n return null;\n }\n const slug = parsed.data.repository.full_name.toLowerCase();\n return {\n source: \"github_ci_failure\",\n deliveryId: options.deliveryId,\n repositoryDefaultBranch: parsed.data.repository.default_branch,\n repositorySlug: slug,\n replyTo: { channel: \"github\", address: `${slug}/actions/runs/${run.id}` },\n workflowRun: {\n id: run.id,\n name: run.name,\n trigger: run.event,\n branch: run.head_branch,\n headRepositorySlug: run.head_repository?.full_name.toLowerCase() ?? null,\n headSha: run.head_sha,\n url: run.html_url,\n conclusion,\n attempt: run.run_attempt,\n },\n payload: options.payload,\n };\n}\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as StoredEntry, i as StoreDriver, n as RecordExistsError, o as StoredRecord, r as RecordNotFoundError, s as VersionConflictError, t as CollectionName } from "./driver-CrZsy7sm.mjs";
|
|
2
2
|
import { a as factoryConfigSchema, i as factoryConfigRepositorySchema, n as FactoryConfigInput, r as defineFactory, t as FactoryConfig } from "./factory-config-Bu4mklm9.mjs";
|
|
3
3
|
import { a as signalSchema, c as ReplyTo, d as replyToSchema, f as schemaVersionSchema, i as signalOutcomeSchema, l as SCHEMA_VERSION, m as vocabularySchema, n as SignalOutcome, o as signalSourceSchema, p as usdSchema, r as SignalSource, s as standardSignalSources, t as Signal, u as isoDateTimeSchema } from "./signal-CHVEmzvI.mjs";
|
|
4
|
-
import { $ as
|
|
4
|
+
import { $ as Cost, A as FactoryStores, At as repositoryIdsSchema, B as standardTaskKinds, C as ChangePatch, Ct as RepositoryId, D as CreateStoresOptions, Dt as factoryIdSchema, E as CreateSignalInput, Et as changeIdSchema, F as TaskApproval, G as taskOriginSchema, H as taskDevboxSchema, I as TaskKind, J as taskStateSchema, K as taskQuestionSchema, L as TaskState, M as TaskTransitionOptions, Mt as taskIdSchema, N as createStores, O as CreateTaskInput, Ot as receiptIdSchema, P as Task, Q as repositorySlugSchema, R as coordinationRepositoryIdsSchema, S as ChangeAssociationError, St as ReceiptId, T as CreateChangeInput, Tt as TaskId, U as taskExecutionRepositoryIdsSchema, V as taskApprovalSchema, W as taskKindSchema, X as RepositorySlug, Y as Repository, Z as repositorySchema, _ as findingVerificationMethodSchema, _t as changeSchema, a as approvalRuleSchema, at as receiptKindSchema, b as ApprovalDecision, bt as ChangeId, c as Finding, ct as receiptStateSchema, d as FindingVerification, dt as standardReceiptStates, et as Receipt, f as findingCategorySchema, ft as Change, g as findingVerificationLevelSchema, gt as changeEvidenceSchema, h as findingSchema, ht as Level, i as approvalPolicySchema, it as costSchema, j as TaskPatch, jt as signalIdSchema, k as DecideSignalOptions, kt as repositoryIdSchema, l as FindingCategory, lt as standardReceiptKinds, m as findingPrioritySchema, mt as ChangeState, n as ApprovalRule, nt as ReceiptPhase, o as defaultApprovalPolicy, ot as receiptPhaseSchema, p as findingFingerprint, pt as ChangeEvidence, q as taskSchema, r as ApprovalSubject, rt as ReceiptState, s as requiresApproval, st as receiptSchema, t as ApprovalPolicy, tt as ReceiptKind, u as FindingPriority, ut as standardReceiptPhases, v as findingVerificationSchema, vt as changeStateSchema, w as ChangeTransitionOptions, wt as SignalId, x as ApprovalError, xt as FactoryId, y as AppendReceiptInput, yt as levelSchema, z as harnessSchema } from "./approval-CDiBco2V.mjs";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/briefs.d.ts
|
|
7
7
|
/** The finding behind a task, when its signal carries one. */
|
|
@@ -23,6 +23,38 @@ interface AdoptPullRequestInput {
|
|
|
23
23
|
}
|
|
24
24
|
declare function adoptPullRequest(stores: Pick<FactoryStores, "changes">, input: AdoptPullRequestInput): Promise<Change>;
|
|
25
25
|
//#endregion
|
|
26
|
+
//#region src/coordination.d.ts
|
|
27
|
+
interface CreateCoordinationInput {
|
|
28
|
+
repositoryIds: readonly [RepositoryId, RepositoryId, ...RepositoryId[]];
|
|
29
|
+
signalId: SignalId;
|
|
30
|
+
replyTo: ReplyTo;
|
|
31
|
+
}
|
|
32
|
+
interface CoordinationPlan {
|
|
33
|
+
parent: Task;
|
|
34
|
+
children: readonly Task[];
|
|
35
|
+
}
|
|
36
|
+
type CoordinationReconciliation = {
|
|
37
|
+
status: "pending";
|
|
38
|
+
parent: Task;
|
|
39
|
+
children: readonly Task[];
|
|
40
|
+
} | {
|
|
41
|
+
status: "succeeded" | "failed";
|
|
42
|
+
transitioned: boolean;
|
|
43
|
+
parent: Task;
|
|
44
|
+
children: readonly Task[];
|
|
45
|
+
};
|
|
46
|
+
interface ReconcileCoordinationOptions {
|
|
47
|
+
present(input: {
|
|
48
|
+
status: "succeeded" | "failed";
|
|
49
|
+
parent: Task;
|
|
50
|
+
children: readonly Task[];
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
/** Create one non-executable parent and one independently executable child per repository. */
|
|
54
|
+
declare function createCoordination(stores: FactoryStores, input: CreateCoordinationInput): Promise<CoordinationPlan>;
|
|
55
|
+
/** Reconcile a coordination parent from its direct children without ever dispatching the parent. */
|
|
56
|
+
declare function reconcileCoordination(stores: FactoryStores, parentTaskId: TaskId, options: ReconcileCoordinationOptions): Promise<CoordinationReconciliation>;
|
|
57
|
+
//#endregion
|
|
26
58
|
//#region src/dispatch.d.ts
|
|
27
59
|
declare class DispatchRefusedError extends Error {
|
|
28
60
|
readonly taskId: TaskId;
|
|
@@ -50,6 +82,75 @@ declare const DEFAULT_MAX_LAUNCH_ATTEMPTS = 45;
|
|
|
50
82
|
*/
|
|
51
83
|
declare function dispatchTask(stores: FactoryStores, taskId: TaskId, options: DispatchOptions): Promise<Task>;
|
|
52
84
|
//#endregion
|
|
85
|
+
//#region src/metrics.d.ts
|
|
86
|
+
/**
|
|
87
|
+
* The derived review bucket from the default risk x confidence table. Derived
|
|
88
|
+
* at read time, never stored; "blocked" is a bucket, not a change state.
|
|
89
|
+
*/
|
|
90
|
+
type ReviewBucket = "stamp" | "review" | "blocked";
|
|
91
|
+
declare function reviewBucket(risk: Level, confidence: Level): ReviewBucket;
|
|
92
|
+
interface LedgerRows {
|
|
93
|
+
changes: readonly Change[];
|
|
94
|
+
signals: readonly Signal[];
|
|
95
|
+
receipts: readonly Receipt[];
|
|
96
|
+
tasks: readonly Task[];
|
|
97
|
+
}
|
|
98
|
+
interface MetricsOptions {
|
|
99
|
+
/** Evaluation time; defaults to the current time. */
|
|
100
|
+
now?: Date;
|
|
101
|
+
/** Days a landed change must hold without a rollback. Default 7. */
|
|
102
|
+
holdDays?: number;
|
|
103
|
+
}
|
|
104
|
+
interface LedgerMetrics {
|
|
105
|
+
/**
|
|
106
|
+
* Changes a human merged that reached deployed (rolled_out for high risk)
|
|
107
|
+
* and did not roll back within the hold window. Includes provisional ones.
|
|
108
|
+
*/
|
|
109
|
+
landed: number;
|
|
110
|
+
/** Landed changes still inside the hold window; the number may drop. */
|
|
111
|
+
provisional: number;
|
|
112
|
+
/** landed / (landed + rolled back + closed after ready). Null when nothing decided. */
|
|
113
|
+
stickRate: number | null;
|
|
114
|
+
/**
|
|
115
|
+
* Not computable from ledger rows: needs a record of human commits pushed
|
|
116
|
+
* to the pull request after ready, which nothing writes today.
|
|
117
|
+
*/
|
|
118
|
+
stampedClean: null;
|
|
119
|
+
/** Median ms from the ready receipt to the merged receipt, per review bucket. */
|
|
120
|
+
humanTimeMs: {
|
|
121
|
+
stamp: number | null;
|
|
122
|
+
review: number | null;
|
|
123
|
+
};
|
|
124
|
+
/** Sum of settled task cost over landed changes / landed. Null until budgets settle. */
|
|
125
|
+
costPerLandedUsd: number | null;
|
|
126
|
+
/** Signals triage turned away plus changes closed in verification. */
|
|
127
|
+
filtered: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The six product metrics from ledger rows alone. One rule governs them: a
|
|
131
|
+
* change counts only after a human merged it and it stayed. Activity that
|
|
132
|
+
* never landed is not a result. Compute over rows already filtered to the
|
|
133
|
+
* period and repositories you are reporting on.
|
|
134
|
+
*/
|
|
135
|
+
declare function computeLedgerMetrics(rows: LedgerRows, options?: MetricsOptions): LedgerMetrics;
|
|
136
|
+
type HealthColor = "green" | "yellow" | "red";
|
|
137
|
+
interface HealthOptions {
|
|
138
|
+
now?: Date;
|
|
139
|
+
/** Open-change cap from factory configuration; omitted disables that check. */
|
|
140
|
+
maxOpenChanges?: number;
|
|
141
|
+
}
|
|
142
|
+
interface RepositoryHealth {
|
|
143
|
+
color: HealthColor;
|
|
144
|
+
/** Every rule that fired, so the color is explainable from rows. */
|
|
145
|
+
reasons: string[];
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Green, yellow, or red from rows, never from a model. Budget and onboarding
|
|
149
|
+
* signals are not evaluated yet: budgets do not settle and onboarding
|
|
150
|
+
* investigations are not recorded, so those spec rules have no rows to read.
|
|
151
|
+
*/
|
|
152
|
+
declare function computeRepositoryHealth(rows: LedgerRows, options?: HealthOptions): RepositoryHealth;
|
|
153
|
+
//#endregion
|
|
53
154
|
//#region src/schema/budget.d.ts
|
|
54
155
|
declare const budgetMonthSchema: z.ZodString;
|
|
55
156
|
declare const budgetSchema: z.ZodObject<{
|
|
@@ -139,5 +240,5 @@ interface SweepResult {
|
|
|
139
240
|
*/
|
|
140
241
|
declare function sweepQueued(stores: FactoryStores, options: SweepOptions): Promise<SweepResult>;
|
|
141
242
|
//#endregion
|
|
142
|
-
export { AdoptPullRequestInput, AppendReceiptInput, ApprovalDecision, ApprovalError, ApprovalPolicy, ApprovalRule, ApprovalSubject, Budget, Change, ChangeEvidence, ChangeId, ChangePatch, ChangeState, ChangeTransitionOptions, CollectionName, Cost, CreateChangeInput, CreateSignalInput, CreateStoresOptions, CreateTaskInput, DEFAULT_MAX_LAUNCH_ATTEMPTS, DecideSignalOptions, DispatchOptions, DispatchRefusedError, FactoryConfig, FactoryConfigInput, FactoryId, FactoryStores, Finding, FindingCategory, FindingPriority, FindingVerification, InvalidTransitionError, Level, Receipt, ReceiptId, ReceiptKind, ReceiptPhase, ReceiptState, RecordExistsError, RecordNotFoundError, ReplyTo, Repository, RepositoryId, RepositorySlug, SCHEMA_VERSION, Signal, SignalCreator, SignalId, SignalOutcome, SignalSource, StoreDriver, StoredEntry, StoredRecord, SweepOptions, SweepResult, Task, TaskApproval, TaskId, TaskKind, TaskLauncher, TaskPatch, TaskState, TaskTransitionOptions, Transition, TransitionActor, VersionConflictError, adoptPullRequest, approvalPolicySchema, approvalRuleSchema, assertTransition, budgetMonthSchema, budgetSchema, changeEvidenceSchema, changeIdSchema, changeInitialState, changeSchema, changeStateSchema, changeTransitions, costSchema, createInMemoryDriver, createSignalCreator, createStores, defaultApprovalPolicy, defineFactory, dispatchTask, factoryConfigRepositorySchema, factoryConfigSchema, factoryIdSchema, findingCategorySchema, findingFingerprint, findingForTask, findingPrioritySchema, findingSchema, findingVerificationLevelSchema, findingVerificationMethodSchema, findingVerificationSchema, fixBrief, harnessSchema, isTransitionAllowed, isoDateTimeSchema, legalNextStates, levelSchema, receiptIdSchema, receiptKindSchema, receiptPhaseSchema, receiptSchema, receiptStateSchema, replyToSchema, repositoryIdSchema, repositoryIdsSchema, repositorySchema, repositorySlugSchema, requiresApproval, scanBrief, schemaVersionSchema, signalIdSchema, signalInitialOutcome, signalOutcomeSchema, signalOutcomeTransitions, signalSchema, signalSourceSchema, standardReceiptKinds, standardReceiptPhases, standardReceiptStates, standardSignalSources, standardTaskKinds, sweepQueued, taskApprovalSchema, taskDevboxSchema, taskIdSchema, taskInitialState, taskKindSchema, taskOriginSchema, taskQuestionSchema, taskSchema, taskStateSchema, taskTransitions, terminalStates, usdSchema, vocabularySchema };
|
|
243
|
+
export { AdoptPullRequestInput, AppendReceiptInput, ApprovalDecision, ApprovalError, ApprovalPolicy, ApprovalRule, ApprovalSubject, Budget, Change, ChangeAssociationError, ChangeEvidence, ChangeId, ChangePatch, ChangeState, ChangeTransitionOptions, CollectionName, CoordinationPlan, CoordinationReconciliation, Cost, CreateChangeInput, CreateCoordinationInput, CreateSignalInput, CreateStoresOptions, CreateTaskInput, DEFAULT_MAX_LAUNCH_ATTEMPTS, DecideSignalOptions, DispatchOptions, DispatchRefusedError, FactoryConfig, FactoryConfigInput, FactoryId, FactoryStores, Finding, FindingCategory, FindingPriority, FindingVerification, HealthColor, HealthOptions, InvalidTransitionError, LedgerMetrics, LedgerRows, Level, MetricsOptions, Receipt, ReceiptId, ReceiptKind, ReceiptPhase, ReceiptState, ReconcileCoordinationOptions, RecordExistsError, RecordNotFoundError, ReplyTo, Repository, RepositoryHealth, RepositoryId, RepositorySlug, ReviewBucket, SCHEMA_VERSION, Signal, SignalCreator, SignalId, SignalOutcome, SignalSource, StoreDriver, StoredEntry, StoredRecord, SweepOptions, SweepResult, Task, TaskApproval, TaskId, TaskKind, TaskLauncher, TaskPatch, TaskState, TaskTransitionOptions, Transition, TransitionActor, VersionConflictError, adoptPullRequest, approvalPolicySchema, approvalRuleSchema, assertTransition, budgetMonthSchema, budgetSchema, changeEvidenceSchema, changeIdSchema, changeInitialState, changeSchema, changeStateSchema, changeTransitions, computeLedgerMetrics, computeRepositoryHealth, coordinationRepositoryIdsSchema, costSchema, createCoordination, createInMemoryDriver, createSignalCreator, createStores, defaultApprovalPolicy, defineFactory, dispatchTask, factoryConfigRepositorySchema, factoryConfigSchema, factoryIdSchema, findingCategorySchema, findingFingerprint, findingForTask, findingPrioritySchema, findingSchema, findingVerificationLevelSchema, findingVerificationMethodSchema, findingVerificationSchema, fixBrief, harnessSchema, isTransitionAllowed, isoDateTimeSchema, legalNextStates, levelSchema, receiptIdSchema, receiptKindSchema, receiptPhaseSchema, receiptSchema, receiptStateSchema, reconcileCoordination, replyToSchema, repositoryIdSchema, repositoryIdsSchema, repositorySchema, repositorySlugSchema, requiresApproval, reviewBucket, scanBrief, schemaVersionSchema, signalIdSchema, signalInitialOutcome, signalOutcomeSchema, signalOutcomeTransitions, signalSchema, signalSourceSchema, standardReceiptKinds, standardReceiptPhases, standardReceiptStates, standardSignalSources, standardTaskKinds, sweepQueued, taskApprovalSchema, taskDevboxSchema, taskExecutionRepositoryIdsSchema, taskIdSchema, taskInitialState, taskKindSchema, taskOriginSchema, taskQuestionSchema, taskSchema, taskStateSchema, taskTransitions, terminalStates, usdSchema, vocabularySchema };
|
|
143
244
|
//# sourceMappingURL=index.d.mts.map
|